topograph-1.0.0.2/0000755000000000000000000000000007346545000012044 5ustar0000000000000000topograph-1.0.0.2/LICENSE0000644000000000000000000000276607346545000013064 0ustar0000000000000000Copyright (c) 2018-2019 Oleg Grenrus All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Oleg Grenrus nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. topograph-1.0.0.2/dag-closure.png0000644000000000000000000002004307346545000014756 0ustar0000000000000000PNG  IHDRsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATxyTTGڇ;/,q=&1"'РaL/c4n0DIDLLԘE5YGYdf MD9[mxoݺuxhhh@FF^yCaN5#"1118uap OQXX/P8J8qBCCv(@%ƍS]A@%͛W *AѣXr%`ܸqlj@ee%Z[[ ---cjHLLDTT%%%>}:aiirt˃u`jj ~p탯/> HJJDYY[!r1b$Q555W:Ǵ *dlAݻ'f0JKKMeHg,n:3336$$%%Ν;C 2VVZ.6^$СCPS-**Ydd$|}}:&$Ղ$&&"??b3ZAq]zo}yz<""~~~^Q)HBB CBPP?.xxx8F$N"99Y!95TrJ|B$)UP)cRӌ\`I2jٷorH*H Aۇ{Q.2ӌ( "ͼ C !!!XjըDGQQ-W'n|MV%aE;vcyʠ+H_W,_mmm# -riի xw$ }v<~xTy7CW# GL ۷oH$B||Ϩ$}vtvv:94IkǸ$ m6tvv"..X燍72& l۶ ]]]V L IB ۶mCwwH$~ mݺ݈zk׮1ڮDn(kA6m???twwc֭3rH`:HŻ'ݻF]DAڈ5%SL!6mRJpss\XLIOO 3g[[[bjjJHff&Պ|hnnFGGB!rssGrLf먩Acc#k.j*VT3f}?dΝT8\A!$##lذhυ x}KKKRWWh"z*%jjjښ .(k(*iDEE &mmmȖ-[V1AfϞMImmBƍɕ+W(F>:::_|A&NHU~Ab1Y\z_hy_~.]RB/^WҺCtԐ~w"<&MNx}Dguuuhii2e Q^^X ۢm(//裏IeAPUU8~@OOO?.\O}}=QWWW_}HN4TA퟽K555]]]?B";v O|,==sRZ[[РJ3Àl2(Xl9ؿ?~m (; b16m>zep95kpicٲeܠAMM ǏW_}e˖}޶*-r<􄙙akk+Ihhh}V\\LON LCUD»K)*RRR/L-Z$>˗1g}fkk'/ WL~~>B!>SdeePj<z7.((ԩS)v44SYYT꫘;w.,XÇK-?@=T ǃ3gfjjj7x5kѣ}dO7cǎ͛vՅ~ VVVHKKb#GŅo}WTT %%† ;W, x3NNNHOO:uښ-pÐ 4mDHz͛qfZ5//oȿLA~w4iz<;;*Inbt!߳gjkke)kڥ3^}~Ũ#ciСCHKKW_}p]2ѳCG֭/dJ3BJJ 9 }FLAt-udҥ*Id`ooO{СCterC bMRI24Ut>LPX颽JziTȆ4sAR.0TZoD#,,L%S,\:|uڵPI2" 99iii9fȑT-#-t6wkS%SPfÇ:? FӍR"W寽Jí[}$''#++ GЂX>A$"w㏑/ld j^BCCU@4#//19ab>n(22 <n߾=,9:+C3^yN"IXXؘui-9aQiΖ J^fX fϛ!F###3͂ L3IIIskrE3QpDD|}}Ǵ$nnnHKKXbb"Y VVZCyc]0 &&&ݻ8x k k|V>ZGjx ŷ~ MMMZ"sܹs}N`!])I>?f$PPPdN@K'KTgTHHH|>!hk$$$Sr# WdʕcJ ߨՕFHH@ رc֦M6ٷoݻI9\Axj<L1@KKHHVZ5$98I90{tҏQ-2TI^`F`J"Φ'Qt:u j|jQ!I||?e'˨ea͚5$66F2s=Τf&+2 $111FRR˖{xx %%CnAttȳHȪ$111hnnFRRBp)(4;+bGU, p$FKKrys"($ w1?&2@```XI+D"}nnnzN}}}x).H3p+)Hj(ĉߟ l[[100 DSS}}}%-[3g&sNqFʿC^^'Yr%y"K!S.r?N&L@ ߥk3&666DMM7| !899HD[Dgڦ0cLm2 !'ODcc#=zb޽Roݺo5]]]ZDKK b1둒zfyxx_aC bE*G}}~W+++. ښ YNchhH֮]K $"" a sMу";& Ž{cҤIEmm-:$[BMM Rw(..Faa!`mm 9sFf0{lf&FRUeD"7bdd$\WW"EE;NNNDOO|pM~Zf$}O*L2OMM%bdRPP@i#aw*)eѢE27Ey+~Ξ=EEEJNvo~+ 1jhnnFNN*ijjb޼y033c1?ą Xٳ) w-DN}J!DCCLMMىm۶!**klrA,^@gs'nR]ӧO@ <==qTUUQ̚5 %%% =sia>!'N`>AŋoALf@Zy#e%TVr;fh{.rAzf]]:؝;w`eeBTa+&&e2~~&(!==ӦM̀cXt) Q nIh}1ty(d[0ǵkPUUk2H3 =ޚMg3 1X͛7#>>649_-r!LDPWW;wD"[pww=Wf4г S%8e!9y${??455Q[[*Ņ%88w+H{t-t1o=/%DCc̜9x1ttthoKlClZTl0f]|/m?ߐ,ҳVH0*x<9dmbYhiiA@@̜9eee0@m-;QS9M+'6mڄ˗ѣG++9w۴-M'^^^x$LɊ Xrp _OxRvM9s&iO38n8L~eRM.z///l޼|>5I|||p9Z`ug~Y]H<==yf֮$L4c}WϜqhC:t鉝;wbŊKbkkN op92:_@\]]c/Acc#mӝfX 1XJb`׮]`TZErBw&WEn$,_1IM gٯ͆x{P|/_N[OCg  PtPwF@@#9ӌs,9E1i,Y1IMPHK ԵqsQ^v8rä$>>>8{,rR2҂O޸}s0% ]iܿwǵИ,Y~!L6 <>>8s ur^0dJ2uT<*TшYx’ 1: /}s[!T ы/FRR咄 ͣZ2ւadk=~F-щl2TWlmI&V4307v@庚aXbň%Q4͌A'^?xp!~uʱ< $NSx9Xzd)rw"olV7'HE &llptJ0l`>I7'N/[#k.5 yx! 7nGGG[577ÇE===b``@fΜw=딝G{D.D..f$y&﷍듻k5gI{qcÑInnn?Ib1%}CٳgINN UDe'CNsW$d͚5Ʀ| 111!Hxx88q"166&ZZZȈKzU%Ȑ )?UNN"d*۪ɍ7100 &&&ܹsDMM%}Wɓ'U 2lĄXFN"W7\TE,իWo8;;(3k,믓beeE -j Pu\ H-?G݌rEbddDttt?~<~r$//BH`` @,,,H]]U(Bӽ&r+)r7.5X$&&[[[bbbBx<ZDCC˪V%D$|r~T]b5t@H$]ZSSSrYYUx%?T~k@ᗅx6^6d +Btׯ_*:U!V㥨] n`̞,r zD=X)ZJ[`|'Ǔ%uycc'**Afעtҫm `:n߾~صkx<:::7nɍ ݋-[HR2^ݎUߛV{6ц.Jwғ=?..0n4h}GELL >bbb HmmmIQ Lgs',G2{mԂqܾ}{GhΒxVq1Ǝ~~~>"""JDVƍ 27n6n܈7/ZFqq1222 O㖖x-&&7 I ZJbOX~DEE^ttHbT*ܼysRr=]6 4xI RLZ \'Oj}?** - [n!--M'9k(,, R2 ]]]:폏 tSL˵ Ehh( P( N$99V9(Ƌ C INN۷i|||ߎ3( BɑJ??nPbƍ#^ RFFD BMJ T*QYYɹj*M*f(r^IkAJ%r.{PrDFF$$!!uuuPTBטdz! /IHH@KK j5b&77WIx'HBBZ[[5w X WWW\r. Gkk+T*ץ ]1C!LN^w3aDGGC.J^6777\|~( ۷mmmHIIẔ AwPlذ111SAۇv<<QgyfHI@$yyy\2*3gĺup1#ڎ*Hvv~رRAAATm`9f 22_~%n:̙3gv X'Oȑ#Xn\]]ww+Vʕ+fBoo/7:Ąܿ_ڝ;wŋIII 9tP$77>"++^Xbf#A[<3ռ|'l5fQ]]Çnv Ï?ņKJJpBڋ5dx3555/e˖G~ 999#y뭷`oou 7D̽{-[h^5d̤^|'Nq%?쐓S2) \cǎˋꐒ> ׫*a۶m/~1d[ỘA]hxxx 77SLo1ٹ.\ຌI ׯ__lfI˗/#&&Fd\]]w߱sJMMw)~6: OOOC;k19rDt>իIƁ@VV;LZRccc/ +l8U ==ϟѣGimAa*fӑMw$xh?aR?l޼k֬$j:HKKCNNal&u͐H$$à+fҐG2:T+"H2 |w:)%Ʋe˰lٲ!T*B@TTRю$)))())AZZ/x"B*B&rX'%%n"f0B'Nܜ؁r<0>Iqm^P`@HdPrR@HHIAǂ+IRRܹ4?LHH! 솒#55uغu+G B` 'N‚&Gbb"FT4YBP "" b^a7+Ibb"ǔ3"bP( 4?LǍRDEE>qH{9ܼy033c±ѫB.cӦMI&"/bF//RDee3z1弌R:|/Čގ 2 IPWWZ=I5k ++& _LƩ$ hiiZ֩>Ō^G`d2L&>>>Yxg?"f bdL&ӬEAEEmP^^rtvvjҢu!SrT*>/?bX&33v"ˉֶdڴi֖[[[beeE noK}۷HLL &,,ܽ{w&gNn;͛DGG37!F1555d׮]dҒbhmIb_ZZJNJb1'^^^/X ]]]oqtt$3fڮRm<<<ԩS;C(w?#} #??8s̘ggg~Hߏ#L=+V}D2tX<(--l̨\zdкJxg1k,+?gϞdb<͝TUUA" 666ݻNjgr݌ bmmӧcP(pqqD"Ajj*jkkiYRCWz)qrU '0}Hff&D"sX߯ F_+bI (.. ƍ㠪p3F/Hnn.-ZGGegg_䠪ѡfvq%b.b;w")) s妰Qb*ZܸqcGWW^uY/]՘1ڙӧO/ٳGmm-BCCu㥗^beF+T*T*庌 tRѣG\m3 mmm\Bٌ hmmEpp0,]LD;v`x!̙3GdO93,F8}4f̘z K.Eee%g32IsNHRrR?F!;wDpp0'q1:"H`ff 6ԩSƍ~'tvvҒ}# x{{c޽o~fVtЄ~m*Ihh(E %YdѢE<nAh ֯_]a2fA Gpp0+0y CZ5I-ZjFbFAؔ_5 0 [03 ,j*fTEݻnj Kxzz2.?+Za466HHf ,ä$ .Duu5iS<==qF$ uL#<<BSA= ^Aj|駸|2/_t yyyprr“O>9b;6[-}>И 8IENDB`topograph-1.0.0.2/dag-reduction.png0000644000000000000000000001473007346545000015304 0ustar0000000000000000PNG  IHDRsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<UIDATxy\T?ȢzB 4d2 Kn^uf2S.RUUwovV xd@DA30(f_/_֜<;py466x.mLخoe N rv)222&K ²e0qD~ ^PT|2yA Dxx8`ĉ|)p۷o#++ JP]]6tuǔEσrqqq?~< 3f̀Rձ ?RPP`kk yDR!)) (--|^UUBسg****uĨODMLL`mm F[[C=cDzR2jAxq t顲$c4( ܹs6sW=//F%QP(pʕ r?V\ ???\Um ^BBdd_zA" F!A "QXXKz=䶘`z 4._r{LL  ^$99EEECE凵nFppAKbp$'':>>Ⱦ SEDx\ĠR#j5aaaT*58I F$JPsQc III())\X'kN3j QD-GJJ r}W3s&͹ǫ ʕ+ F$11ׯ_eσƌPDoILLDii)RRR h=njPDDD@"$z)r}13w\\uqDIRDFF$z'HBBrį/ݻwi^J1.x;xԈbFZTw 2 ̼pօQ#^"PQQO>9Ԍ6fHRz% dqr"E8<=Q#HJN "ɠT*.{̨H$ I8+H||5T4441c /{{{w^]2PqBH̳<ڤ}6$G!M6oLm:9Hnn.ֆF>fl2$X|98عs'x j; {{{n:D"S(|wHLL1u! )HNNLLLpaݻ˗/;K=mUN]u"*KPI&: ԔܺuKYii)1c)**3Ys5}R'Udee%K l9ɓ'cɚ\]]> Laa!{n磸XkA{-6\TTӧS^!`Luu5K/a٘?>߯ Arrr Tk N+q5S[[TkϢpA$ٽhfRϝ;C8{,_n/GNNƍG00$.~ެՠT*/+++Ja͚5x'/ޏ%\s~=s1F0iƸfg O{ORbQ /_h kAc}Ouܹs%SSO^ͤw)?@]Gݰmt$..^^^x饗<3;b&==999ػw}|zj,Y>03`j:vCIի w\Lk̤ĉPl./ɚ'N`P6/3iiiΦ\W^ ___DFF3SjN9^j*,]d9Aɡ^hI]jB!/=P31f }SN+$6 ?5y"??Uw ̇~||駌<^y!""?0X,[ FMv ?(fr3+?WKi8pXp%ILLDii)RSS9&D"1)))jgfF*$ : + (++$8w¥KXLr~Q#J T:EzprYbI'tAoW+wEG*B @"ÜL |'=!}zӸt]]031cѫDD"/ Dd$ruT %dqƈ3z1H$LR?xOM| EMEAԈbDEEqf$RB$RǥȪ΢ѡK"Y$>>P(:åX,xlݺ:ON|ךq"f bQ#XY t:QRrtthY> hn}Z\Nw}ؗ1cP@HHbbb Hpj1 QaN,</a᱅x:i\}M>IIIX`iNL'$$̜9DII f͚58qHy [3`!VT8x n޼3f@*bϞ=Č @_=zMMMhmmŭ[hmaL[jr%4OZ~7hiiAoo/Fʿw5b|o5W4?~\늝3fi3"cǎˊVWWc>\X$""%%%_ gggXZZZ0oL&p8ӡW(--Eqq1LLLooo?~|~u1pv_̰k_Lgg'NvݪnbnF7_ӓ7l۶444Q ^Jw54xAG۵na&T5,EME$D4-}? y ]$v<<-Y1*A.\鷮رcObҤI,V;BG!22kIH BEWW6n܈8NSͫ&LLzA("%%555,+ZЩb~zOEFF Id z`5PPVbGaaW(W\= U 1zArss''A۲ϳPȬdF/gϢVb(-3F/P_c!P]sU rʀN=WX UŌΤ=z_5LcǎaرCMM vC헶G_`xF+H$H$b3z[qWuc1ۨ#F_wg,fxAF@~~>.RW)cW3 #K2z*Z+B֭Ê+j-ڏ 2B?Js3F{ ~~~`=zVVV0znޠjAF֯_Hy iz ^ X~=Y;P333ȑ#ƍ+~m=4B>>>ؼy3^|E4551zlgooo{fT00dw[$/%YbcX=C?w+ m۶aŊ=/t / x{{cfD:4E1& ګh^aRg|_= S3 h"oU+lIy0;xqJa444Pk(2*4a:%>a:ڪNY ,;v"I#A رcD"P}C3/xzzߧt$>a:n޹IYz$RG}%5f |ePyC3/GHR__S_'L򎒒jID"Β>>i 1D"dҌxxx@&$ɴ (^s,I#AtT#gM>5#Vk[zA ya׮]7o96df7|3aL4z w{KՅL g!1!裏ގ6ܾ}۶mf̴665ؚLMLhV±cǠR4]v @!Sob L47xS{L$\N\]] $&&Fk~&uk\K ďtvvjmg-oi ǏםT.~ykkk>W1F}]rë:-?!sAMM 3h>Bmm퀶bTVVL͛7w〶 FHNN\]]5ڵ }avm1BaeeL .] /xDFF}ֶ|X,|G#3gj?ztt ~&?'N0z;wzjjϏ FBUUۇsaܹHKKCww7*++qiG2lWYT$P\IENDB`topograph-1.0.0.2/dag-transpose.png0000644000000000000000000001442507346545000015327 0ustar0000000000000000PNG  IHDRsBIT|d pHYs  tEXtSoftwarewww.inkscape.org<IDATx{TuÝp6EɬMPClϒՔ=QfyR܊B-oXr@:3| vץAa`՘6m0A8s A!r>iӦ s ׯ_GII T*Pհ:n A,︄9---dɉ<kkkr)m֬YCT*%?#f+HMM $nnnʊڒO?`+++eMKK }Ύ曤D"RYY׶L>]o@@uo+nA3}QL6 xW /Px ;;;DEEN6$$...ܹs++1煲\ԩSظq#|Iڶ Ǐ၎=zO?H$_?Ξ=$$$@Ѱك1nrrrׯ׶!!!$NNN">>>͛BZֵO3O?4B{{{8::bطoVXa}\\@.СC7>`#HNN~gŋ_T*\]]j*X[[9$&?Prz6rJݿ,XӧOA$Hb#Hvv6h9FC&>xgb$1YAqeB$1˗o߱HHHL&3iILR6SƵ\.GbbIKbrdee*xcb$nb2f1)AP__Ϻov\1CAI"MNDTʕ+ … QSS3Mu$1 AJ%^s"DcB.cݺu&% Q*hhh\Xl*++'32̤$ J* \dHJJ2 Ix+BJB^^/06f(RIHKA +90*f(LA P(ӣv߰{Z$d۶mA^^ץ ]1C!J T/Fm۶ƍ̽AJJ d2$ ۶mCooI ŋQ]]Mk|sA233ۋ\Kt ELL RSSy# dff@/B" 55$ ~l% 6n\Ω$7o"''S1C! /`ڵcl?c "03}#1C!?ќHª [nŭ[B׳bB,#-- zklݺoFvv6[dcB,_D"aUVٺu+j ɓ'H%a\tjdee1+NDɓ'Y_dd$6mĚ$ FcrP3I˜ jf/`KFyסjT*wD",^'N`u$L.믿혡ʘ$ e Zj p$M-[]vեIA [g3w3\7n/-X\ %D"Mӟ$q.cnIDPQ"K,alnñi&ߙ1S൶9>DHZZ  f9tss3P3cҥ8y$1CAge;wNobXHOOOCsG$ ⺜tttG5tRϜ9SDۈDբk׮;vի9Glf,Z[[///|0VNdd$;тX[[v5B1! K'_NN!Dѐ rڵ~ RQQ{L/Rz{{%} JKɓ'#** ={=L2!$$Dxpp7oD"X,fj31III/(<UrXYY>???ݷʒ%K2oaj2$666_~=we2gR[[d4r] *O())!+V K,co=f̘{ \k\pMMM(((@UU.]dA>ŬYh/֜Yx1beeex0|,Z~#)//!d5kD"xs6ֆ"⋺璒G򫻒Z]]C̙38qhpY<̐d(..Fpp0g5T*?F6l/\o[aiaX{7C,giv. ķ~ub‚BP(Ig=f g3j;wD[[6;v*ƜK/]XLjjj}d8D2??F\\\ȤI  dcug/^$zkٳ-g0)͛IYY#}GFD""w6͑#G y,X0b-7xn9IAK/HǏ'zwwwkpzJܹs/'|B\\\bkkKRSS3B/L4I'Hbbd̙>/gggjvkP2A{{;|||3f ==`]v]F bkk[+Ws='H2 L8peee(--EAA1o޼Q۪j|WxOOOh49rd{$66h4:g #c}">>GGGׯGhh(."蠨(..ͯ0Ʈ_aaa$wAW~! ܅/~~EEEBqq10Y H'3wFUU}F`úO?4I0ٽ{7Ν;ǚ󤦦bHHH֞%q\Y(9{=Xi$11%Y|} Wr.H2L~~>grpE3fqy8\/&%%-IHHG}-//.\T,]QŋqNFmRRR ?\Cmm-/x!$''C"XHBZB'@\\$ Ro5Crssq%^ b8t9*3A˓$''?3/x(0$H$2{I(9 y)SA&5WILAǂ+Ivv6._"_u@-솒PO&qXz>:t8hbdee~Ch4XB.#11r\Wa,9(BBB~LFt%•+WƔf&1ÑD&FTիؿ=H|I\xjvvv,U86&5Pd2[2#xXjb$/RDCCø3&1ÑdR J}:o[&fLvJHJJHP(R?`())aJ"J9DPFç1T*}af۶mqrO<~1c#T*EJJ R)+cQrUx3f%04QJJ d2+0!1NLL _tΗ1L.1"&&$MǏ3`D"Ajj*$gT`:c,#f8 ܼy9994Uhyq3f=Pb"55uvwyj9(_7ǞiLP[[K雵YMgggF_gy桮j}p r/QSS2l]%[[[<>}:JXX;ɚ5! CWWN Ztlذsqy6cL2Θ3gr9|}}B\G}o }ra fA0ZcXNb |?nnnT5bٳCx +W䠪ѡf1tq be.b^{ ٘9s&73lViтtttzoK/UVqV^jXԿO#GhmmE\\.qTbcc[oa͚5bH$H$\1nqܺu-:bLpbFdlٲgϞ rfAA[[.\ /'ux{{իA&+Tcǎ^Ctt4zzzX;]h4dppPnkYlж] acc79ˑgy׮]ccF&o ::UI-RF(I֮]˚$gFcc#cS]Lpp0|M]]]]ɘa`[fE&4ae˖&ٳH0? 0 [03 ,l2یJ2{l3 ,ĸ$ꫯhSEKI{_4a&%5kG[ ;w2"X, GbΝH$JB ͂ ;v:̚5 ---Ō P# b. @ڵ6I輡Y'$0YfAR3 Iy' U0, d9N@e2K/Ht"1B@mm-DG!!V <f+3g...y`ذa3g믿s=jI_#Vo}YVYYiqdb7o|ϸqFٌ3؎;B&5#!!&L@nn.{1b޽9+V@}}(:u xgQYYiD$ˀ#*>>>19997xc5`DGG?3%%.]B]]]Vt {՟҂\_ڐVkԄџڂ\_ڐVs{2ӂ\ڐV;Ԅ}5ۂ\ڐVCLqiAn mQ+jGtfϞ=ڂ@ڐڊ)))Pbc9FqiAn0mQ+5ì~`[LrԊ2&z kgцPm?Z-Y 9jE dق5ېVn6-ڏv rlCZQD/ReqhAmQ+ 1Ж٪9[!G(e@ m~-[erԊq&~IqnAΖmQ+JH2`+]ݻR 'Er,::Z6 n1c9)ڐӧmE2`M"ڏ9)ېx"u9Nh?.77QQQ 'erԊ&z,ljhAO?ek׮wS+Z}7D |IېV@ȡ8-ȉlCZq ~D!G8SqrhANmQ+}4ڏK rrhCZD/"ԂڐVNmB9'Ԇb^&؂ېV쑼Pɵ99!G c~[srԊfڏ{ rrnCZ QoOC rІoE1MXPP`7K rІܐoE)G/;;>/,449991lժU# oc,==]t,kȴΝ;999#F8`nnnlٲeStilldӦMcNNNl NcaaaLP0lѬFt#̝;UUUh4(((380;z>T*qơĦMrJržvJ^RHL zVp>|j… L2EP*ٱcŋh4cuɓ'mVZŦOnqIޝ:u3dؾ}& <==Zݻl…Rݻم ̷F# b۷oBҥK_ϣEEE_|an݊?xmmmdž 0uT ѣ@[[N</wAFFv):?}p 暷F#~_ L A!DZ+WHCud:%%%o2PTT$a=r_urP{l˖-tRL&\ߗ0Up8v|c\\\l2l߾]d=!$bn.]C oCBPۑE}cJ%^|EmHCHJ_[CџІ4a9mHCH@Z݆4! 9mHCH`Zن4 9QmHCH5Zmc+$;Bb׬ՂҥKqIې-k ' iݲv r/mHCH-Z i]U rR! !;lAN6!$v-IՆ4ĮHтTmHCHT-Iц4nHقmHCH֭[%mAmHCH jR g6!$va֭կ~%y rlCB"{Eomن4DD g6!$&lՆ4D҂-ڐȖZEْ[ rnCB"KrlAmHCHdI-Y iȹ9RKbΝY4Dvނܲeː56!$b-Y iK rhC!looGyy9ˡnooTXjiizц͛7/亶acc#V^;wÇQF1RFņ ݻCBQQ` 111LJUVo,00#F<=Ӣ ǎ'x @CCbbbdCV"##St!CBբwƔ)SP\\,:V]SNE}}=&pT*[niel͚5с1c 44T`KJJ:;;zMMM=abb"FAll ////@PP N4 .Ǒ#G*8U̞=gΜ P(ӄ ˗/^z%( +WWWիW1}tf,^֭CRRW);|0 {uRA^^M;/~nW ;wPVVfϟGMMGA{EVVƌ? 555h4X~="##y9s 233=rrroh ;`0p=L&dee9܅,dff"00puuEcc#nܸ#Gĉϡώ"V5B{+|ш|1dC[H6mCVph h h h h h h h h h h h h h h h h h  ZeeeV&NoooiQUU466ɼ_EFFBT N'1ƺnAhh(\]]OOO ܾ}Fعcǎ?)1l0xzzݻP(㉔eq8hhZFhZAIJJ¨QNW_}u ^ 7l؀#GFK8B_puu~b !ň#* QQQ"W^/`̙tzuW^Bk&e|||0yd୷G6,.pCcc#<<l۶M899A"11x7qIhBBbŊ?~1c<ooo-&VEHHtb؄jƍ3͛7&L,q,yyy5`!yyy0ư}v@DD~J8Uss3~фu[ZZ"..^^^gggX򝚛qbݺuƓO> pssN,Aii)>3 ,,lHR:Ξ= J͛7CP`0`֬YcɂEA~>zhDGG6o,m:0(?;w.jG N'!>>|駟;?)VW>?!<}t,ƍ1w\rhW_YW/^ׇ/Dff&:::P\\;wB֭[8uV\I_$va:t8qΟ?VoOƞ={DG>;JXY.NAVETIENDB`topograph-1.0.0.2/src/0000755000000000000000000000000007346545000012633 5ustar0000000000000000topograph-1.0.0.2/src/Topograph.hs0000644000000000000000000004614307346545000015142 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -- | Copyright: (c) 2018, Oleg Grenrus -- SPDX-License-Identifier: BSD-3-Clause -- -- Tools to work with Directed Acyclic Graphs, -- by taking advantage of topological sorting. -- module Topograph ( -- * Graph -- $setup G (..), runG, runG', -- * Transpose transpose, -- * Transitive reduction reduction, -- * Transitive closure closure, -- * DFS dfs, dfsTree, -- * All paths allPaths, allPaths', allPathsTree, -- * Path lengths shortestPathLengths, longestPathLengths, -- * Query edgesSet, adjacencyMap, adjacencyList, -- * Utilities pairs, treePairs, ) where import Data.Orphans () import Prelude () import Prelude.Compat import Control.Monad.ST (ST, runST) import Data.Foldable (for_) import Data.List (sort) import Data.Map (Map) import Data.Maybe (catMaybes, mapMaybe) import Data.Monoid (First (..)) import Data.Ord (Down (..)) import Data.Set (Set) import qualified Data.Graph as G import qualified Data.Map as Map import qualified Data.Set as Set import qualified Data.Tree as T import qualified Data.Vector as V import qualified Data.Vector.Unboxed as U import qualified Data.Vector.Unboxed.Mutable as MU ------------------------------------------------------------------------------- -- Setup ------------------------------------------------------------------------------- -- $setup -- -- Initial setup and imports: -- -- >>> :set -XRecordWildCards -- >>> import Data.Monoid (All (..)) -- >>> import Data.Foldable (traverse_) -- >>> import Data.List (elemIndex, sort) -- >>> import Data.Tree (Tree (..)) -- >>> import Data.Map (Map) -- >>> import Data.Set (Set) -- >>> import qualified Data.Tree as T -- >>> import qualified Data.Map as Map -- >>> import qualified Data.Set as Set -- -- Some compatibility imports -- -- >>> import Control.Applicative -- >>> import Data.Foldable (traverse_, foldMap) -- -- Graph used in examples: -- -- <> -- -- >>> let example :: Map Char (Set Char); example = Map.map Set.fromList $ Map.fromList [('a', "bxde"), ('b', "d"), ('x', "de"), ('d', "e"), ('e', "")] -- -- == Few functions to be used in examples -- -- To make examples slightly shorter: -- -- >>> let fmap2 f = fmap (fmap f) -- >>> let fmap3 f = fmap (fmap2 f) -- >>> let traverse2_ f = traverse_ (traverse_ f) -- >>> let traverse3_ f = traverse_ (traverse2_ f) -- -- To display trees: -- -- >>> let dispTree :: Show a => Tree a -> IO (); dispTree = go 0 where go i (T.Node x xs) = putStrLn (replicate (i * 2) ' ' ++ show x) >> traverse_ (go (succ i)) xs -- -- And fold them (this function is available in recent @containers@): -- -- >>> let foldTree f = go where go (T.Node x ts) = f x (map go ts) -- ------------------------------------------------------------------------------- -- Graph ------------------------------------------------------------------------------- -- | Graph representation. -- -- The 'runG' creates a @'G' v i@ structure. Note, that @i@ is kept free, -- so you cannot construct `i` which isn't in the `gVertices`. -- Therefore operations, like `gFromVertex` are total (and fast). -- -- === __Properties__ -- -- @'gVerticeCount' g = 'length' ('gVertices' g)@ -- -- >>> runG example $ \G {..} -> (length gVertices, gVerticeCount) -- Right (5,5) -- -- @'Just' ('gVertexIndex' g x) = 'elemIndex' x ('gVertices' g)@ -- -- >>> runG example $ \G {..} -> map (`elemIndex` gVertices) gVertices -- Right [Just 0,Just 1,Just 2,Just 3,Just 4] -- -- >>> runG example $ \G {..} -> map gVertexIndex gVertices -- Right [0,1,2,3,4] -- data G v i = G { gVertices :: [i] -- ^ all vertices, in topological order. , gFromVertex :: i -> v -- ^ /O(1)/. retrieve original vertex data , gToVertex :: v -> Maybe i -- ^ /O(log n)/. , gEdges :: i -> [i] -- ^ /O(1)/. Outgoing edges. Note: target indices are larger than source index. , gDiff :: i -> i -> Int -- ^ /O(1)/. Upper bound of the path length. Negative means there aren't path. , gVerticeCount :: Int -- ^ /O(1)/. @'gVerticeCount' g = 'length' ('gVertices' g)@ , gVertexIndex :: i -> Int -- ^ /O(1)/. @'Just' ('verticeIndex' g x) = 'elemIndex' x ('gVertices' g)@. Note, there are no efficient way to convert 'Int' into 'i', conversion back and forth is discouraged on purpose. } -- | Run action on topologically sorted representation of the graph. -- -- === __Examples__ -- -- ==== Topological sorting -- -- >>> runG example $ \G {..} -> map gFromVertex gVertices -- Right "axbde" -- -- Vertices are sorted -- -- >>> runG example $ \G {..} -> map gFromVertex $ sort gVertices -- Right "axbde" -- -- ==== Outgoing edges -- -- >>> runG example $ \G {..} -> map (map gFromVertex . gEdges) gVertices -- Right ["xbde","de","d","e",""] -- -- Note: target indices are always larger than source vertex' index: -- -- >>> runG example $ \G {..} -> getAll $ foldMap (\a -> foldMap (\b -> All (a < b)) (gEdges a)) gVertices -- Right True -- -- ==== Not DAG -- -- >>> let loop = Map.map Set.fromList $ Map.fromList [('a', "bx"), ('b', "cx"), ('c', "ax"), ('x', "")] -- >>> runG loop $ \G {..} -> map gFromVertex gVertices -- Left "abc" -- -- >>> runG (Map.singleton 'a' (Set.singleton 'a')) $ \G {..} -> map gFromVertex gVertices -- Left "aa" -- runG :: forall v r. Ord v => Map v (Set v) -- ^ Adjacency Map -> (forall i. Ord i => G v i -> r) -- ^ function on linear indices -> Either [v] r -- ^ Return the result or a cycle in the graph. runG m f | Just l <- loop = Left (map (indices V.!) l) | otherwise = Right (f g) where gr :: G.Graph r :: G.Vertex -> ((), v, [v]) _t :: v -> Maybe G.Vertex (gr, r, _t) = G.graphFromEdges [ ((), v, Set.toAscList us) | (v, us) <- Map.toAscList m ] r' :: G.Vertex -> v r' i = case r i of (_, v, _) -> v topo :: [G.Vertex] topo = G.topSort gr indices :: V.Vector v indices = V.fromList (map r' topo) revIndices :: Map v Int revIndices = Map.fromList $ zip (map r' topo) [0..] edges :: V.Vector [Int] edges = V.map (\v -> maybe [] (\sv -> sort $ mapMaybe (\v' -> Map.lookup v' revIndices) $ Set.toList sv) (Map.lookup v m)) indices -- TODO: let's see if this check is too expensive loop :: Maybe [Int] loop = getFirst $ foldMap (\a -> foldMap (check a) (gEdges g a)) (gVertices g) where check a b | a < b = First Nothing -- TODO: here we could use shortest path | otherwise = First $ case allPaths g b a of [] -> Nothing (p : _) -> Just p g :: G v Int g = G { gVertices = [0 .. V.length indices - 1] , gFromVertex = (indices V.!) , gToVertex = (`Map.lookup` revIndices) , gDiff = \a b -> b - a , gEdges = (edges V.!) , gVerticeCount = V.length indices , gVertexIndex = id } -- | Like 'runG' but returns 'Maybe' runG' :: forall v r. Ord v => Map v (Set v) -- ^ Adjacency Map -> (forall i. Ord i => G v i -> r) -- ^ function on linear indices -> Maybe r -- ^ Return the result or 'Nothing' if there is a cycle. runG' m f = either (const Nothing) Just (runG m f) ------------------------------------------------------------------------------- -- All paths ------------------------------------------------------------------------------- -- | All paths from @a@ to @b@. Note that every path has at least 2 elements, start and end. -- Use 'allPaths'' for the intermediate steps only. -- -- See 'dfs', which returns all paths starting at some vertice. -- This function returns paths with specified start and end vertices. -- -- >>> runG example $ \g@G{..} -> fmap3 gFromVertex $ allPaths g <$> gToVertex 'a' <*> gToVertex 'e' -- Right (Just ["axde","axe","abde","ade","ae"]) -- -- There are no paths from element to itself: -- -- >>> runG example $ \g@G{..} -> fmap3 gFromVertex $ allPaths g <$> gToVertex 'a' <*> gToVertex 'a' -- Right (Just []) -- allPaths :: forall v i. Ord i => G v i -> i -> i -> [[i]] allPaths g a b = map (\p -> a : p) (allPaths' g a b [b]) -- | 'allPaths' without begin and end elements. -- -- >>> runG example $ \g@G{..} -> fmap3 gFromVertex $ allPaths' g <$> gToVertex 'a' <*> gToVertex 'e' <*> pure [] -- Right (Just ["xd","x","bd","d",""]) -- allPaths' :: forall v i. Ord i => G v i -> i -> i -> [i] -> [[i]] allPaths' G {..} a b end = concatMap go (gEdges a) where go :: i -> [[i]] go i | i == b = [end] | otherwise = let js :: [i] js = filter (<= b) $ gEdges i js2b :: [[i]] js2b = concatMap go js in map (i:) js2b -- | Like 'allPaths' but return a 'T.Tree'. -- All paths from @a@ to @b@. Note that every path has at least 2 elements, start and end, -- -- Unfortunately, this is the same as @'dfs' g \<$> 'gToVertex' \'a\'@, -- as in our example graph, all paths from @\'a\'@ end up in @\'e\'@. -- -- <> -- -- >>> let t = runG example $ \g@G{..} -> fmap3 gFromVertex $ allPathsTree g <$> gToVertex 'a' <*> gToVertex 'e' -- >>> fmap3 (foldTree $ \a bs -> if null bs then [[a]] else concatMap (map (a:)) bs) t -- Right (Just (Just ["axde","axe","abde","ade","ae"])) -- -- >>> fmap3 (Set.fromList . treePairs) t -- Right (Just (Just (fromList [('a','b'),('a','d'),('a','e'),('a','x'),('b','d'),('d','e'),('x','d'),('x','e')]))) -- -- >>> let ls = runG example $ \g@G{..} -> fmap3 gFromVertex $ allPaths g <$> gToVertex 'a' <*> gToVertex 'e' -- >>> fmap2 (Set.fromList . concatMap pairs) ls -- Right (Just (fromList [('a','b'),('a','d'),('a','e'),('a','x'),('b','d'),('d','e'),('x','d'),('x','e')])) -- -- 'Tree' paths show how one can explore the paths. -- -- >>> traverse3_ dispTree t -- 'a' -- 'x' -- 'd' -- 'e' -- 'e' -- 'b' -- 'd' -- 'e' -- 'd' -- 'e' -- 'e' -- -- >>> traverse3_ (putStrLn . T.drawTree . fmap show) t -- 'a' -- | -- +- 'x' -- | | -- | +- 'd' -- | | | -- | | `- 'e' -- | | -- | `- 'e' -- ... -- -- There are no paths from element to itself, but we'll return a -- single root node, as 'Tree' cannot be empty. -- -- >>> runG example $ \g@G{..} -> fmap3 gFromVertex $ allPathsTree g <$> gToVertex 'a' <*> gToVertex 'a' -- Right (Just (Just (Node {rootLabel = 'a', subForest = []}))) -- allPathsTree :: forall v i. Ord i => G v i -> i -> i -> Maybe (T.Tree i) allPathsTree G {..} a b = go a where go :: i -> Maybe (T.Tree i) go i | i == b = Just (T.Node b []) | otherwise = case mapMaybe go $ filter (<= b) $ gEdges i of [] -> Nothing js -> Just (T.Node i js) ------------------------------------------------------------------------------- -- DFS ------------------------------------------------------------------------------- -- | Depth-first paths starting at a vertex. -- -- >>> runG example $ \g@G{..} -> fmap3 gFromVertex $ dfs g <$> gToVertex 'x' -- Right (Just ["xde","xe"]) -- dfs :: forall v i. Ord i => G v i -> i -> [[i]] dfs G {..} = go where go :: i -> [[i]] go a = case gEdges a of [] -> [[a]] bs -> concatMap (\b -> map (a :) (go b)) bs -- | like 'dfs' but returns a 'T.Tree'. -- -- >>> traverse2_ dispTree $ runG example $ \g@G{..} -> fmap2 gFromVertex $ dfsTree g <$> gToVertex 'x' -- 'x' -- 'd' -- 'e' -- 'e' -- dfsTree :: forall v i. Ord i => G v i -> i -> T.Tree i dfsTree G {..} = go where go :: i -> T.Tree i go a = case gEdges a of [] -> T.Node a [] bs -> T.Node a $ map go bs ------------------------------------------------------------------------------- -- Longest / shortest path ------------------------------------------------------------------------------- -- | Shortest paths lengths starting from a vertex. -- The resulting list is of the same length as 'gVertices'. -- It's quite efficient to compute all shortest (or longest) paths' lengths -- at once. Zero means that there are no path. -- -- >>> runG example $ \g@G{..} -> shortestPathLengths g <$> gToVertex 'a' -- Right (Just [0,1,1,1,1]) -- -- >>> runG example $ \g@G{..} -> shortestPathLengths g <$> gToVertex 'b' -- Right (Just [0,0,0,1,2]) -- shortestPathLengths :: Ord i => G v i -> i -> [Int] shortestPathLengths = pathLenghtsImpl min' where min' 0 y = y min' x y = min x y -- | Longest paths lengths starting from a vertex. -- The resulting list is of the same length as 'gVertices'. -- -- >>> runG example $ \g@G{..} -> longestPathLengths g <$> gToVertex 'a' -- Right (Just [0,1,1,2,3]) -- -- >>> runG example $ \G {..} -> map gFromVertex gVertices -- Right "axbde" -- -- >>> runG example $ \g@G{..} -> longestPathLengths g <$> gToVertex 'b' -- Right (Just [0,0,0,1,2]) -- longestPathLengths :: Ord i => G v i -> i -> [Int] longestPathLengths = pathLenghtsImpl max pathLenghtsImpl :: forall v i. Ord i => (Int -> Int -> Int) -> G v i -> i -> [Int] pathLenghtsImpl merge G {..} a = runST $ do v <- MU.replicate (length gVertices) (0 :: Int) go v (Set.singleton a) v' <- U.freeze v pure (U.toList v') where go :: MU.MVector s Int -> Set i -> ST s () go v xs = do case Set.minView xs of Nothing -> pure () Just (x, xs') -> do c <- MU.unsafeRead v (gVertexIndex x) let ys = Set.fromList $ gEdges x for_ ys $ \y -> flip (MU.unsafeModify v) (gVertexIndex y) $ \d -> merge d (c + 1) go v (xs' `Set.union` ys) ------------------------------------------------------------------------------- -- Transpose ------------------------------------------------------------------------------- -- | Graph with all edges reversed. -- -- <> -- -- >>> runG example $ adjacencyList . transpose -- Right [('a',""),('b',"a"),('d',"abx"),('e',"adx"),('x',"a")] -- -- === __Properties__ -- -- Commutes with 'closure' -- -- >>> runG example $ adjacencyList . closure . transpose -- Right [('a',""),('b',"a"),('d',"abx"),('e',"abdx"),('x',"a")] -- -- >>> runG example $ adjacencyList . transpose . closure -- Right [('a',""),('b',"a"),('d',"abx"),('e',"abdx"),('x',"a")] -- -- Commutes with 'reduction' -- -- >>> runG example $ adjacencyList . reduction . transpose -- Right [('a',""),('b',"a"),('d',"bx"),('e',"d"),('x',"a")] -- -- >>> runG example $ adjacencyList . transpose . reduction -- Right [('a',""),('b',"a"),('d',"bx"),('e',"d"),('x',"a")] -- transpose :: forall v i. Ord i => G v i -> G v (Down i) transpose G {..} = G { gVertices = map Down $ reverse gVertices , gFromVertex = gFromVertex . getDown , gToVertex = fmap Down . gToVertex , gEdges = gEdges' , gDiff = \(Down a) (Down b) -> gDiff b a , gVerticeCount = gVerticeCount , gVertexIndex = \(Down a) -> gVerticeCount - gVertexIndex a - 1 } where gEdges' :: Down i -> [Down i] gEdges' (Down a) = es V.! gVertexIndex a -- Note: in original order! es :: V.Vector [Down i] es = V.fromList $ map (map Down . revEdges) gVertices revEdges :: i -> [i] revEdges x = concatMap (\y -> [y | x `elem` gEdges y ]) gVertices ------------------------------------------------------------------------------- -- Reduction ------------------------------------------------------------------------------- -- | Transitive reduction. -- -- Smallest graph, -- such that if there is a path from /u/ to /v/ in the original graph, -- then there is also such a path in the reduction. -- -- The green edges are not in the transitive reduction: -- -- <> -- -- >>> runG example $ \g -> adjacencyList $ reduction g -- Right [('a',"bx"),('b',"d"),('d',"e"),('e',""),('x',"d")] -- -- Taking closure first doesn't matter: -- -- >>> runG example $ \g -> adjacencyList $ reduction $ closure g -- Right [('a',"bx"),('b',"d"),('d',"e"),('e',""),('x',"d")] -- reduction :: Ord i => G v i -> G v i reduction = transitiveImpl (== 1) ------------------------------------------------------------------------------- -- Closure ------------------------------------------------------------------------------- -- | Transitive closure. -- -- A graph, -- such that if there is a path from /u/ to /v/ in the original graph, -- then there is an edge from /u/ to /v/ in the closure. -- -- The purple edge is added in a closure: -- -- <> -- -- >>> runG example $ \g -> adjacencyList $ closure g -- Right [('a',"bdex"),('b',"de"),('d',"e"),('e',""),('x',"de")] -- -- Taking reduction first, doesn't matter: -- -- >>> runG example $ \g -> adjacencyList $ closure $ reduction g -- Right [('a',"bdex"),('b',"de"),('d',"e"),('e',""),('x',"de")] -- closure :: Ord i => G v i -> G v i closure = transitiveImpl (/= 0) transitiveImpl :: forall v i. Ord i => (Int -> Bool) -> G v i -> G v i transitiveImpl pre g@G {..} = g { gEdges = gEdges' } where gEdges' :: i -> [i] gEdges' a = es V.! gVertexIndex a es :: V.Vector [i] es = V.fromList $ map f gVertices where f :: i -> [i] f x = catMaybes $ zipWith edge gVertices (longestPathLengths g x) edge y i | pre i = Just y | otherwise = Nothing ------------------------------------------------------------------------------- -- Display ------------------------------------------------------------------------------- -- | Recover adjacency map representation from the 'G'. -- -- >>> runG example adjacencyMap -- Right (fromList [('a',fromList "bdex"),('b',fromList "d"),('d',fromList "e"),('e',fromList ""),('x',fromList "de")]) -- adjacencyMap :: Ord v => G v i -> Map v (Set v) adjacencyMap G {..} = Map.fromList $ map f gVertices where f x = (gFromVertex x, Set.fromList $ map gFromVertex $ gEdges x) -- | Adjacency list representation of 'G'. -- -- >>> runG example adjacencyList -- Right [('a',"bdex"),('b',"d"),('d',"e"),('e',""),('x',"de")] -- adjacencyList :: Ord v => G v i -> [(v, [v])] adjacencyList = flattenAM . adjacencyMap flattenAM :: Map a (Set a) -> [(a, [a])] flattenAM = map (fmap Set.toList) . Map.toList -- | Edges set. -- -- >>> runG example $ \g@G{..} -> map (\(a,b) -> [gFromVertex a, gFromVertex b]) $ Set.toList $ edgesSet g -- Right ["ax","ab","ad","ae","xd","xe","bd","de"] -- edgesSet :: Ord i => G v i -> Set (i, i) edgesSet G {..} = Set.fromList [ (x, y) | x <- gVertices , y <- gEdges x ] ------------------------------------------------------------------------------- -- Utilities ------------------------------------------------------------------------------- #if !(MIN_VERSION_base(4,14,0)) -- | Unwrap 'Down'. getDown :: Down a -> a getDown (Down a) = a #endif -- | Like 'pairs' but for 'T.Tree'. treePairs :: T.Tree a -> [(a,a)] treePairs (T.Node i js) = [ (i, j) | T.Node j _ <- js ] ++ concatMap treePairs js -- | Consecutive pairs. -- -- >>> pairs [1..10] -- [(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10)] -- -- >>> pairs [] -- [] -- pairs :: [a] -> [(a, a)] pairs [] = [] pairs xs = zip xs (tail xs) topograph-1.0.0.2/topograph.cabal0000644000000000000000000000320207346545000015030 0ustar0000000000000000cabal-version: 2.2 name: topograph version: 1.0.0.2 synopsis: Directed acyclic graphs. category: Data, Graph description: Directed acyclic graphs can be sorted topographically. Existence of topographic ordering allows writing many graph algorithms efficiently. And many graphs, e.g. most dependency graphs are acyclic! . There are some algorithms built-in: dfs, transpose, transitive closure, transitive reduction... Some algorithms even become not-so-hard to implement, like a longest path! homepage: https://github.com/phadej/topograph bug-reports: https://github.com/phadej/topograph/issues license: BSD-3-Clause license-file: LICENSE author: Oleg Grenrus maintainer: Oleg.Grenrus copyright: (c) 2018-2019 Oleg Grenrus build-type: Simple extra-doc-files: dag-original.png dag-closure.png dag-reduction.png dag-transpose.png dag-tree.png tested-with: GHC ==7.6.3 || ==7.8.4 || ==7.10.3 || ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.2 || ==9.2.4 || ==9.4.1 source-repository head type: git location: https://github.com/phadej/topograph.git library exposed-modules: Topograph build-depends: , base >=4.6 && <4.18 , base-compat ^>=0.10.5 || ^>=0.11.0 || ^>=0.12.0 , base-orphans ^>=0.8 , containers ^>=0.5.0.0 || ^>=0.6.0.1 , vector ^>=0.12 || ^>=0.13 other-extensions: RankNTypes RecordWildCards ScopedTypeVariables hs-source-dirs: src default-language: Haskell2010