PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/proc/self/root/opt/cloudlinux/venv/lib/python3.11/site-packages/guppy/heapy/
Upload File :
Current File : //proc/self/root/opt/cloudlinux/venv/lib/python3.11/site-packages/guppy/heapy/Part.py
class Format(object):
    __slots__ = 'impl', 'mod'

    def __init__(self, impl):
        self.impl = impl
        self.mod = impl.mod

    def get_formatted_row(self, row):
        fr = self.get_stat_data(row)
        rows = []
        rs = row.name.split('\n')
        subsequent_indent = len(fr)*' '
        rows.extend(self.mod.wrap(
            fr+rs[0],
            width=self.mod.line_length,
            subsequent_indent=subsequent_indent))

        for r in rs[1:]:
            rows.extend(self.mod.wrap(
                r,
                width=self.mod.line_length,
                initial_indent=subsequent_indent,
                subsequent_indent=subsequent_indent))
        return '\n'.join(rows)

    def load_statrow_csk(self, r):
        impl = self.impl
        count, size, kind = r.split(' ', 2)
        count = int(count)
        size = int(size)
        impl.cum_size += size
        return StatRow(count, size, kind, impl.cur_index, impl.cum_size)

    def load_statrow_sk(self, r):
        impl = self.impl
        size, kind = r.split(' ', 1)
        size = int(size)
        impl.cum_size += size
        return StatRow(1, size, kind, impl.cur_index, impl.cum_size)

    def _oh_get_num_lines(self):
        return self.impl.numrows

    def _oh_get_label(self):
        return self.get_label()

    def _oh_get_row_header(self):
        impl = self.impl
        if not (impl.count or impl.size):
            return ''
        sh = self.get_stat_header()
        return self.mod.fill(
            sh + self.impl.kindheader,
            width=self.mod.line_length,
            subsequent_indent=' '*len(sh))

    def _oh_get_more_msg(self, start_lineno, end_lineno):
        nummore = self.impl.numrows-(end_lineno+1)
        return "<%d more rows. Type e.g. '_.more' to view.>" % nummore

    def _oh_get_line_iter(self):
        for numrows, row in enumerate(self.impl.get_rows()):
            yield self.get_formatted_row(row)


class SetFormat(Format):
    __slots__ = ()

    def get_label(self):
        impl = self.impl
        if impl.count != 1:
            s = 's'
        else:
            s = ''
        return 'Partition of a set of %d object%s. Total size = %d bytes.' % (
            impl.count, s, impl.size)

    def get_rowdata(self, row):
        return '%d %d %s' % (row.count, row.size, row.name)

    def get_stat_header(self):
        return (
            ' Index  Count   %     Size   % Cumulative  % ')

    def get_stat_data(self, row):
        format = '%6d %6d %3d %8d %3d %9d %3d '
        impl = self.impl
        fr = format % (
            row.index,
            row.count, int('%.0f' % (row.count * 100.0/impl.count)),
            row.size, int('%.0f' % (row.size * 100.0/impl.size)),
            row.cumulsize, int('%.0f' % (row.cumulsize * 100.0/impl.size)),
        )
        return fr

    def load_statrow(self, r):
        return self.load_statrow_csk(r)


class IdFormat(Format):
    __slots__ = ()

    def get_label(self):
        impl = self.impl
        if impl.count != 1:
            s = 's'
        else:
            s = ''
        return (
            'Set of %d %s object%s. Total size = %d bytes.' % (
                impl.count, impl.kindname, s, impl.size))
        return part

    def get_rowdata(self, row):
        return '%d %s' % (row.size, row.name)

    def get_stat_header(self):
        return (
            ' Index     Size   %   Cumulative  %   ')

    def get_stat_data(self, row):
        impl = self.impl
        format = '%6d %8d %5.1f %9d %5.1f '
        fr = format % (
            row.index,
            row.size, (row.size * 100.0/impl.size),
            row.cumulsize, row.cumulsize * 100.0/impl.size,
        )
        return fr

    def load_statrow(self, r):
        return self.load_statrow_sk(r)


class DiffFormat(Format):
    __slots__ = ()

    def _percent_of_b(self, size):
        if self.impl.b_size != 0:
            return '%9.3g' % (size*100.0/self.impl.b_size,)
        else:
            return '   (n.a.)'

    def get_label(self):
        impl = self.impl
        x = (
            'Summary of difference operation (A-B).\n' +
            '        Count     Size\n' +
            '  A    %6d %8d\n' % (impl.count+impl.b_count, impl.size+impl.b_size) +
            '  B    %6d %8d\n' % (impl.b_count, impl.b_size) +
            '  A-B  %6d %8d  = %s %% of B\n' % (impl.count, impl.size, self._percent_of_b(impl.size)))

        if impl.count or impl.size:
            x += '\nDifferences by kind, largest absolute size diffs first.'
        return x

    def get_rowdata(self, row):
        return '%d %d %s' % (row.count, row.size, row.name)

    def get_stat_header(self):
        return (
            ' Index  Count     Size  Cumulative  % of B ')

    def get_stat_data(self, row):
        impl = self.impl
        format = '%6d %6d %8d %9d %s '
        fr = format % (
            row.index,
            row.count,
            row.size,
            row.cumulsize,
            self._percent_of_b(row.cumulsize),
        )
        return fr

    def load_statrow(self, r):
        return self.load_statrow_csk(r)


class StatRow(object):
    __slots__ = 'count', 'size', 'name', 'index', 'cumulsize'

    def __init__(self, count, size, name, index=None, cumulsize=None):
        self.count = count
        self.size = size
        self.name = name
        self.index = index
        self.cumulsize = cumulsize


class PartRow(StatRow):
    __slots__ = 'set', 'kind'

    def __init__(self, count, size, name, index, cumulsize, set, kind):
        self.count = count
        self.size = size
        self.name = name
        self.index = index
        self.cumulsize = cumulsize
        self.set = set
        self.kind = kind


class Stat:
    def __init__(self, mod, get_trows, firstheader=''):
        self.mod = mod
        self._hiding_tag_ = mod._hiding_tag_
        self.get_trows = get_trows
        self.firstheader = firstheader

        self.it = iter(get_trows())

        self.cur_index = 0
        self.cum_size = 0
        self.rows = []

        r = self.get_next()
        while r and not r.startswith('.r:'):
            name = r[1:r.index(':')]
            value = r[r.index(':')+1:].strip()
            try:
                value = int(value)
            except ValueError:
                pass
            setattr(self, name, value)
            r = self.get_next()

        self.format_name = self.format
        self.format_class = getattr(self.mod, self.format)
        self.format = self.format_class(self)

        self.timemade = float(self.timemade)

        self.mod.OutputHandling.setup_printing(self, self.format)

    def __getitem__(self, idx):
        if isinstance(idx, int):
            if idx < 0:
                idx = self.numrows + idx
            if not (0 <= idx < self.numrows):
                raise IndexError('Stat index out of range.')
            rows = [self.get_row(idx)]
        elif isinstance(idx, slice):
            start, stop, step = idx.indices(self.numrows)
            rows = [self.get_row(idx) for idx in range(start, stop, step)]
        else:
            raise IndexError('Stat indices must be integers or slices.')

        count = 0
        size = 0
        for r in rows:
            count += r.count
            size += r.size

        trows = [
            '.loader: _load_stat',
            '.format: %s' % self.format_name,
            '.timemade: %f' % self.timemade,
            '.count: %d' % count,
            '.size: %d' % size,
            '.kindheader: %s' % self.kindheader,
            '.kindname: %s' % self.kindname,
            '.numrows: %d' % len(rows),
        ]
        if getattr(self, 'b_count', None) is not None:
            trows.append('.b_count: %d' % self.b_count)
            trows.append('.b_size: %d' % self.b_size)
        for r in rows:
            trows.append('.r: %s' % self.format.get_rowdata(r))
        return self.mod.load(trows)

    def __len__(self):
        return self.numrows

    def __sub__(self, other):
        if not isinstance(other, Stat):
            raise TypeError(
                'Can only take difference with other Stat instance.')
        if self.kindheader != other.kindheader:
            raise ValueError('Mismatching table kind header, %r vs %r.' % (
                self.kindheader, other.kindheader))
        rows = []
        otab = {}
        stab = {}
        for r in other.get_rows():
            o = otab.get(r.name)
            if o:
                otab[r.name] = StatRow(
                    r.count+o.count, r.size+o.size, r.name, o.index,  None)
            else:
                otab[r.name] = r
        for r in self.get_rows():
            o = otab.get(r.name)
            if o:
                del otab[r.name]
                count = r.count - o.count
                size = r.size - o.size
            else:
                count = r.count
                size = r.size
            if count == 0 and size == 0:
                continue
            sr = stab.get(r.name)
            if sr:
                sr.count += count
                sr.size += size
            else:
                sr = StatRow(count, size, r.name)
                stab[sr.name] = sr
                rows.append(sr)
        rs = list(otab.values())
        rs.sort(key=lambda x: x.index)  # Preserve orig. order
        for r in rs:
            sr = StatRow(-r.count, -r.size, r.name)
            assert sr.name not in stab
            rows.append(sr)
        rows.sort(key=lambda x: abs(x.size), reverse=True)
        cumulcount = 0
        cumulsize = 0
        for r in rows:
            cumulcount += r.count
            cumulsize += r.size
            r.cumulsize = cumulsize
        trows = [
            '.loader: _load_stat',
            '.format: DiffFormat',
            '.timemade: %f' % self.mod.time.time(),
            '.b_count: %d' % other.count,
            '.b_size: %d' % other.size,
            '.count: %d' % cumulcount,
            '.size: %d' % cumulsize,
            '.kindheader: %s' % self.kindheader,
            '.kindname: %s' % self.kindname,
            '.numrows: %d' % len(rows),
        ]
        for r in rows:
            trows.append('.r: %d %d %s' % (r.count, r.size, r.name))
        return self.mod.load(trows)

    def dump(self, fn, mode='a'):
        if not hasattr(fn, 'write'):
            f = open(fn, mode)
        else:
            f = fn
        try:
            for r in self.get_trows():
                if not r[-1:] == '\n':
                    r += '\n'
                f.write(r)
            end = '.end: .loader: %s\n' % self.loader
            if r != end:
                f.write(end)
        finally:
            if f is not fn:
                f.close()

    def get_next(self):
        try:
            r = next(self.it)
        except StopIteration:
            r = None
        else:
            r = r.rstrip('\n')
        self.last = r
        return r

    def get_row(self, idx):
        while idx >= len(self.rows):
            self.parse_next_row()
        return self.rows[idx]

    def get_rows(self, idx=0):
        while idx < self.numrows:
            try:
                row = self.get_row(idx)
            except IndexError:
                return
            else:
                yield row
            idx += 1

    def get_rows_of_kinds(self, kinds):
        # Return the rows with names in sequence kinds of unique names
        # in that order. None if no such kind.

        kindtab = {}
        N = len(kinds)
        res = [None] * len(kinds)
        for i, kind in enumerate(kinds):
            kindtab[kind] = i
        assert len(kindtab) == N

        n = 0
        for row in self.get_rows():
            idx = kindtab.get(row.name)
            if idx is not None:
                res[idx] = row
            n += 1
            if n >= N:
                break
        return res

    def get_rows_n_and_other(self, N, sortby='Size'):
        # Get N rows, the largest first
        # mix in an '<Other>' row at a sorted position
        # Size is either size if sortby = 'Size',
        #    or count if sortby = 'Count'.
        # Returns a NEW LIST (caller may modify/sort it)

        if sortby not in ('Size', 'Count'):
            raise ValueError("Argument 'sortby' must be 'Size' or 'Count'.")

        # Rows are already sorted by Size, largest first.
        # If they want by Count, we need to resort them.

        rows = self.get_rows()

        if sortby == 'Count':
            rows = list(rows)
            rows.sort(key=lambda x: x.count, reverse=True)

        retrows = []
        cumulcount = 0
        cumulsize = 0

        for (i, r) in enumerate(rows):
            if i >= N:
                othercount = self.count - cumulcount
                othersize = self.size - cumulsize
                other = StatRow(othercount,
                                othersize,
                                '<Other>')

                if sortby == 'Size':
                    for (i, r) in enumerate(retrows):
                        if r.size < othersize:
                            retrows[i:i] = [other]
                            break
                    else:
                        retrows.append(other)
                elif sortby == 'Count':
                    for (i, r) in enumerate(retrows):
                        if r.count < othercount:
                            retrows[i:i] = [other]
                            break
                    else:
                        retrows.append(other)
                else:
                    assert 0
                break

            cumulcount += r.count
            cumulsize += r.size
            retrows.append(r)
        else:
            assert cumulcount == self.count
            assert cumulsize == self.size

        return retrows

    def parse_next_row(self):
        r = self.last
        if not r:
            raise IndexError('Row index out of range.')
        if r.startswith('.r: '):
            r = r[4:]
            sr = self.format.load_statrow(r)

            self.cur_index += 1
            self.rows.append(sr)
            self.get_next()
            return

        elif r.startswith('.end'):
            raise IndexError('Row index out of range.')
        else:
            raise SyntaxError


class Partition:
    def __init__(self, mod, set, er):
        self.mod = mod
        self.set = set
        self.er = er
        self._hiding_tag_ = mod._hiding_tag_
        self.timemade = mod.time.time()

    def __iter__(self):
        # The default iteration is over the sets
        # To iterate over rows (if more info is needed), get_rows() is available.
        return self.get_sets()

    def get_rows(self, rowindex=None):
        # Iterator over rows
        if rowindex is None:
            rowindex = 0
        while 1:
            try:
                row = self.get_row(rowindex)
            except IndexError:
                return
            else:
                yield row
            rowindex += 1

    def get_set(self, index):
        if isinstance(index, slice):
            start, stop, step = index.indices(self.numrows)
            ns = self.get_nodeset(start, stop, step)
            return self.mod.idset(ns, er=self.er)
        else:
            if index < 0:
                index += self.numrows
            return self.get_rowset(index)

    def get_sets(self, index=None):
        for idx in range(self.numrows):
            yield self.get_rowset(idx)

    def get_stat(self):
        # Avoid any references into the set!
        trows = list(self.get_trows())

        def get_trows():
            return trows
        return self.mod._load_stat(get_trows)

    def get_trows(self):
        yield '.loader: _load_stat'
        yield '.format: %s' % self.format.__class__.__name__
        yield '.timemade: %f' % self.timemade
        yield '.count: %d' % self.count
        yield '.size: %d' % self.size
        yield '.kindname: %s' % self.kindname
        yield '.kindheader: %s' % self.kindheader
        yield '.numrows: %d' % self.numrows
        for row in self.get_rows():
            yield '.r: %s' % self.format.get_rowdata(row)

    def init_format(self, FormatClass):
        self.format = FormatClass(self)

        self.mod.OutputHandling.setup_printing(self, self.format)


class IdentityPartitionCluster(object):
    # Contains objects of same size.
    # to speed up management of identity partition
    # - since otherwise we'd have to sort all the objects,
    #   on their string representation in worst case.
    __slots__ = 'objects', 'locount', 'hicount', 'losize', 'obsize', 'issorted'

    def __init__(self, objects, locount, count, losize, obsize):
        self.objects = objects          # tuple of objects in this segment
        self.locount = locount          # count BEFORE objects in this cluster
        self.hicount = locount+count    # count AFTER these objects
        self.losize = losize            # size BEFORE  objects in this cluster
        self.obsize = obsize            # size of EACH object in this segment
        self.issorted = False           # indicates if .objects is sorted


class IdentityPartition(Partition):
    def __init__(self, mod, set, er):
        Partition.__init__(self, mod, set, er)

        clusters = []
        sizeclasses = mod.Size.classifier.partition_cli(set.nodes)
        sizeclasses.sort()
        sizeclasses.reverse()
        totcount = 0
        totsize = 0
        for size, v in sizeclasses:
            count = len(v)
            clusters.append(IdentityPartitionCluster(
                self.mod.observation_list(v), totcount, count, totsize, size))
            totsize += size * count
            totcount += count
        assert totcount == set.count

        self.cluidx = 0
        self.clusters = clusters
        self.count = totcount
        self.kind = kind = set.byclodo.kind
        self.kindheader = kind.fam.c_get_idpart_header(kind)
        self.kindname = kind.fam.c_get_idpart_label(kind)
        self.numrows = totcount
        self.render = kind.fam.c_get_idpart_render(kind)
        self.size = totsize
        self.sortrender = kind.fam.c_get_idpart_sortrender(kind)

        self.init_format(IdFormat)

    def get_nodeset(self, start, stop, step):
        return self.get_nodeset_cluster(start, stop, step)[0]

    def get_nodeset_cluster(self, start, stop, step):
        if step <= 0:
            raise ValueError('Step must be positive.')
        ns = self.mod.mutnodeset()
        if start >= stop:
            return (ns, None)
        clusters = self.clusters
        lo = 0
        hi = len(clusters)
        cluidx = self.cluidx

        while lo < hi:
            clu = clusters[cluidx]
            if clu.locount <= start:
                if start < clu.hicount:
                    break
                else:
                    lo = cluidx + 1
            else:
                hi = cluidx
            cluidx = (lo + hi) // 2
        else:
            return (ns, None)
        clu_to_return = clu

        while 1:
            objects = clu.objects
            if start != clu.locount or stop < clu.hicount or step != 1:
                if not clu.issorted:
                    sortrender = self.sortrender
                    if sortrender == 'IDENTITY':
                        ks = objects
                    else:
                        ks = [sortrender(x) for x in objects]
                    ks = [(kind, i) for i, kind in enumerate(ks)]
                    ks.sort()
                    clu.objects = objects = self.mod.observation_list(
                        [objects[i] for (kind, i) in ks])
                    clu.issorted = True
                objects = objects[start-clu.locount:stop-clu.locount:step]

            ns |= objects
            self.cluidx = cluidx  # memo till next call
            start += len(objects)*step
            if start >= stop:
                break
            for cluidx in range(cluidx + 1, len(clusters)):
                clu = clusters[cluidx]
                if clu.locount <= start < clu.hicount:
                    break
            else:
                break
        return (ns, clu_to_return)

    def get_row(self, rowidx):
        ns, clu = self.get_nodeset_cluster(rowidx, rowidx+1, 1)
        if not ns:
            raise IndexError('Partition index out of range.')
        vi = self.mod.idset(ns, er=self.er)
        row = PartRow(1, clu.obsize, self.render(vi.theone),
                      rowidx, (rowidx+1-clu.locount)*clu.obsize + clu.losize,
                      vi, vi.kind)
        return row

    def get_rowset(self, rowidx):
        ns = self.get_nodeset(rowidx, rowidx+1, 1)
        if not ns:
            raise IndexError('Partition index out of range.')
        return self.mod.idset(ns, er=self.er)


class SetPartition(Partition):
    def __init__(self, mod, set, er):
        Partition.__init__(self, mod, set, er)

        classifier = er.classifier
        tosort = [(-part.size, classifier.get_tabrendering(kind, ''), kind, part)
                  for (kind, part) in classifier.partition(set.nodes)]
        tosort.sort()
        cumulsize = 0
        rows = []
        for (minusize, name, kind, part) in tosort:
            size = -minusize
            cumulsize += size
            rows.append(PartRow(
                part.count, size, name,
                len(rows), cumulsize,
                part, kind))

        self.count = set.count
        self.kindheader = classifier.get_tabheader('')
        self.kindname = ''
        self.numrows = len(rows)
        self.rows = rows
        self.size = cumulsize

        self.init_format(SetFormat)

    def get_nodeset(self, start, stop, step):
        if step <= 0:
            raise ValueError('Step must be positive.')
        ns = self.mod.mutnodeset()
        while start < stop:
            ns |= self.rows[start].set.nodes
            start += step
        return ns

    def get_row(self, idx):
        try:
            return self.rows[idx]
        except IndexError:
            raise IndexError('Partition index out of range.')

    def get_rowset(self, idx):
        row = self.get_row(idx)
        set = row.set
        try:
            set._partition
        except AttributeError:
            set._partition = self.mod._fast_partition(self, row)
        return set


class FastPartition(SetPartition):
    def __init__(self, mod, parent, row):
        Partition.__init__(self, mod, parent.set, parent.er)

        self.count = row.count
        self.kindheader = parent.kindheader
        self.kindname = ''
        self.numrows = 1
        self.rows = [row]
        self.size = row.size

        self.init_format(SetFormat)


class _GLUECLAMP_:
    _preload_ = ('_hiding_tag_',)
    _chgable_ = ('line_length', 'backup_suffix')
    _imports_ = (
        '_parent.ImpSet:mutnodeset',
        '_parent:OutputHandling',
        '_parent.Use:Id',
        '_parent.Use:Size',
        '_parent.Use:idset',
        '_parent.Use:load',
        '_parent.View:_hiding_tag_',
        '_parent.View:observation_list',
        '_root.os:rename',
        '_root.textwrap:fill',
        '_root.textwrap:wrap',
        '_root.textwrap:wrap',
        '_root:time',
    )

    # 'Config'

    line_length = 100
    backup_suffix = '.old'

    # Factory method

    def partition(self, set, er):
        if er.classifier is self.Id.classifier:
            return IdentityPartition(self, set, er)
        else:
            return SetPartition(self, set, er)

    # Private - Use.load is intended to be used directly.
    def _load_stat(self, get_trows):
        return Stat(self, get_trows)

    def _fast_partition(self, parent, row):
        return FastPartition(self, parent, row)