PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/share/perl5/vendor_perl/GD/Graph
Viewing File: /usr/share/perl5/vendor_perl/GD/Graph/Data.pm
#========================================================================== # Copyright (c) 1995-2000 Martien Verbruggen #-------------------------------------------------------------------------- # # Name: # GD::Graph::Data.pm # # $Id: Data.pm,v 1.22 2007/04/26 03:16:09 ben Exp $ # #========================================================================== package GD::Graph::Data; ($GD::Graph::Data::VERSION) = '$Revision: 1.22 $' =~ /\s([\d.]+)/; use strict; use GD::Graph::Error; @GD::Graph::Data::ISA = qw( GD::Graph::Error ); =head1 NAME GD::Graph::Data - Data set encapsulation for GD::Graph =head1 SYNOPSIS use GD::Graph::Data; =head1 DESCRIPTION This module encapsulates the data structure that is needed for GD::Graph and friends. An object of this class contains a list of X values, and a number of lists of corresponding Y values. This only really makes sense if the Y values are numerical, but you can basically store anything. Undefined values have a special meaning to GD::Graph, so they are treated with care when stored. Many of the methods of this module are intended for internal use by GD::Graph and the module itself, and will most likely not be useful to you. Many won't even I<seem> useful to you... =head1 EXAMPLES use GD::Graph::Data; use GD::Graph::bars; my $data = GD::Graph::Data->new(); $data->read(file => '/data/sales.dat', delimiter => ','); $data = $data->copy(wanted => [2, 4, 5]); # Add the newer figures from the database use DBI; # do DBI things, like connecting to the database, statement # preparation and execution while (@row = $sth->fetchrow_array) { $data->add_point(@row); } my $chart = GD::Graph::bars->new(); my $gd = $chart->plot($data); or for quick changes to legacy code # Legacy code builds array like this @data = ( [qw(Jan Feb Mar)], [1, 2, 3], [5, 4, 3], [6, 3, 7] ); # And we quickly need to do some manipulations on that my $data = GD::Graph::Data->new(); $data->copy_from(\@data); # And now do all the new stuff that's wanted. while (@foo = bar_baz()) { $data->add_point(@foo); } =head1 METHODS =head2 $data = GD::Graph::Data->new() Create a new GD::Graph::Data object. =cut # Error constants use constant ERR_ILL_DATASET => 'Illegal dataset number'; use constant ERR_ILL_POINT => 'Illegal point number'; use constant ERR_NO_DATASET => 'No data sets set'; use constant ERR_ARGS_NO_HASH => 'Arguments must be given as a hash list'; sub new { my $proto = shift; my $class = ref($proto) || $proto; my $self = []; bless $self => $class; $self->copy_from(@_) or return $self->_move_errors if (@_); return $self; } sub DESTROY { my $self = shift; $self->clear_errors(); } sub _set_value { my $self = shift; my ($nd, $np, $val) = @_; # Make sure we have empty arrays in between if ($nd > $self->num_sets) { # XXX maybe do this with splice for ($self->num_sets .. $nd - 1) { push @{$self}, []; } } $self->[$nd][$np] = $val; return $self; } =head2 $data->set_x($np, $value); Set the X value of point I<$np> to I<$value>. Points are numbered starting with 0. You probably will never need this. Returns undef on failure. =cut sub set_x { my $self = shift; $self->_set_value(0, @_); } =head2 $data->get_x($np) Get the X value of point I<$np>. See L<"set_x">. =cut sub get_x { my $self = shift; my $np = shift; return $self->_set_error(ERR_ILL_POINT) unless defined $np && $np >= 0; $self->[0][$np]; } =head2 $data->set_y($nd, $np, $value); Set the Y value of point I<$np> in data set I<$nd> to I<$value>. Points are numbered starting with 0, data sets are numbered starting with 1. You probably will never need this. Returns undef on failure. =cut sub set_y { my $self = shift; return $self->_set_error(ERR_ILL_DATASET) unless defined $_[0] && $_[0] >= 1; $self->_set_value(@_); } =head2 $data->get_y($nd, $np) Get the Y value of point I<$np> in data set I<$nd>. See L<"set_y">. This will return undef on an error, but the fact that it returns undef does not mean there was an error (since undefined values can be stored, and therefore returned). =cut sub get_y { my $self = shift; my ($nd, $np) = @_; return $self->_set_error(ERR_ILL_DATASET) unless defined $nd && $nd >= 1 && $nd <= $self->num_sets; return $self->_set_error(ERR_ILL_POINT) unless defined $np && $np >= 0; $self->[$nd][$np]; } =head2 $data->get_y_cumulative($nd, $np) Get the cumulative value of point I<$np> in data set<$nd>. The cumulative value is obtained by adding all the values of the points I<$np> in the data sets 1 to I<$nd>. =cut sub get_y_cumulative { my $self = shift; my ($nd, $np, $incl_vec) = @_; return $self->_set_error(ERR_ILL_DATASET) unless defined $nd && $nd >= 1 && $nd <= $self->num_sets; return $self->_set_error(ERR_ILL_POINT) unless defined $np && $np >= 0; my $value; my @indices = $incl_vec ? grep($_ <= $nd, @$incl_vec) : 1 .. $nd; for my $i ( @indices ) { $value += $self->[$i][$np] || 0; } return $value; } sub _get_min_max { my $self = shift; my $nd = shift; my ($min, $max); for my $val (@{$self->[$nd]}) { next unless defined $val; $min = $val if !defined $min || $val < $min; $max = $val if !defined $max || $val > $max; } return $self->_set_error("No (defined) values in " . ($nd == 0 ? "X list" : "dataset $nd")) unless defined $min && defined $max; return ($min, $max); } =head2 $data->get_min_max_x Returns a list of the minimum and maximum x value or the empty list on failure. =cut sub get_min_max_x { my $self = shift; $self->_get_min_max(0); } =head2 $data->get_min_max_y($nd) Returns a list of the minimum and maximum y value in data set $nd or the empty list on failure. =cut sub get_min_max_y { my $self = shift; my $nd = shift; return $self->_set_error(ERR_ILL_DATASET) unless defined $nd && $nd >= 1 && $nd <= $self->num_sets; $self->_get_min_max($nd); } =head2 $data->get_min_max_y_all() Returns a list of the minimum and maximum y value in all data sets or the empty list on failure. =cut sub get_min_max_y_all { my $self = shift; my ($min, $max); for (my $ds = 1; $ds <= $self->num_sets; $ds++) { my ($ds_min, $ds_max) = $self->get_min_max_y($ds); next unless defined $ds_min; $min = $ds_min if !defined $min || $ds_min < $min; $max = $ds_max if !defined $max || $ds_max > $max; } return $self->_set_error('No (defined) values in any data set') unless defined $min && defined $max; return ($min, $max); } # Undocumented, not part of interface right now. Might expose at later # point in time. sub set_point { my $self = shift; my $np = shift; return $self->_set_error(ERR_ILL_POINT) unless defined $np && $np >= 0; for (my $ds = 0; $ds < @_; $ds++) { $self->_set_value($ds, $np, $_[$ds]); } return $self; } =head2 $data->add_point($X, $Y1, $Y2 ...) Adds a point to the data set. The base for the addition is the current number of X values. This means that if you have a data set with the contents (X1, X2) (Y11, Y12) (Y21) (Y31, Y32, Y33, Y34) a $data->add_point(Xx, Y1x, Y2x, Y3x, Y4x) will result in (X1, X2, Xx ) (Y11, Y12, Y1x) (Y21, undef, Y2x) (Y31, Y32, Y3x, Y34) (undef, undef, Y4x) In other words: beware how you use this. As long as you make sure that all data sets are of equal length, this method is safe to use. =cut sub add_point { my $self = shift; $self->set_point(scalar $self->num_points, @_); } =head2 $data->num_sets() Returns the number of data sets. =cut sub num_sets { my $self = shift; @{$self} - 1; } =head2 $data->num_points() In list context, returns a list with its first element the number of X values, and the subsequent elements the number of respective Y values for each data set. In scalar context returns the number of points that have an X value set, i.e. the number of data sets that would result from a call to C<make_strict>. =cut sub num_points { my $self = shift; return (0) unless @{$self}; wantarray ? map { scalar @{$_} } @{$self} : scalar @{$self->[0]} } =head2 $data->x_values() Return a list of all the X values. =cut sub x_values { my $self = shift; return $self->_set_error(ERR_NO_DATASET) unless @{$self}; @{$self->[0]}; } =head2 $data->y_values($nd) Return a list of the Y values for data set I<$nd>. Data sets are numbered from 1. Returns the empty list if $nd is out of range, or if the data set at $nd is empty. =cut sub y_values { my $self = shift; my $nd = shift; return $self->_set_error(ERR_ILL_DATASET) unless defined $nd && $nd >= 1 && $nd <= $self->num_sets; return $self->_set_error(ERR_NO_DATASET) unless @{$self}; @{$self->[$nd]}; } =head2 $data->reset() OR GD::Graph::Data->reset() As an object method: Reset the data container, get rid of all data and error messages. As a class method: get rid of accumulated error messages and possible other crud. =cut sub reset { my $self = shift; @{$self} = () if ref($self); $self->clear_errors(); return $self; } =head2 $data->make_strict() Make all data set lists the same length as the X list by truncating data sets that are too long, and filling data sets that are too short with undef values. always returns a true value. =cut sub make_strict { my $self = shift; for my $ds (1 .. $self->num_sets) { my $data_set = $self->[$ds]; my $short = $self->num_points - @{$data_set}; next if $short == 0; if ($short > 0) { my @fill = (undef) x $short; push @{$data_set}, @fill; } else { splice @{$data_set}, $short; } } return $self; } =head2 $data->cumulate(preserve_undef => boolean) The B<cumulate> parameter will summarise the Y value sets as follows: the first Y value list will be unchanged, the second will contain a sum of the first and second, the third will contain the sum of first, second and third, and so on. Returns undef on failure. if the argument I<preserve_undef> is set to a true value, then the sum of exclusively undefined values will be preserved as an undefined value. If it is not present or a false value, undef will be treated as zero. Note that this still will leave undefined values in the first data set alone. Note: Any non-numerical defined Y values will be treated as 0, but you really shouldn't be using this to store that sort of Y data. =cut sub cumulate { my $self = shift; return $self->_set_error(ERR_ARGS_NO_HASH) if (@_ && @_ % 2); my %args = @_; # For all the sets, starting at the last one, ending just # before the first for (my $ds = $self->num_sets; $ds > 1; $ds--) { # For each point in the set for my $point (0 .. $#{$self->[$ds]}) { # Add the value for each point in lower sets to this one for my $i (1 .. $ds - 1) { # If neither are defined, we want to preserve the # undefinedness of this point. If we don't do this, then # the mathematical operation will force undef to be a 0. next if $args{preserve_undef} && ! defined $self->[$ds][$point] && ! defined $self->[$i][$point]; $self->[$ds][$point] += $self->[$i][$point] || 0; } } } return $self; } =head2 $data->wanted(indexes) Removes all data sets except the ones in the argument list. It will also reorder the data sets in the order given. Returns undef on failure. To remove all data sets except the first, sixth and second, in that order: $data->wanted(1, 6, 2) or die $data->error; =cut sub wanted { my $self = shift; for my $wanted (@_) { return $self->_set_error("Wanted index $wanted out of range 1-" . $self->num_sets) if $wanted < 1 || $wanted > $self->num_sets; } @{$self} = @{$self}[0, @_]; return $self; } =head2 $data->reverse Reverse the order of the data sets. =cut sub reverse { my $self = shift; @{$self} = ($self->[0], reverse @{$self}[1..$#{$self}]); return $self; } =head2 $data->copy_from($data_ref) Copy an 'old' style GD::Graph data structure or another GD::Graph::Data object into this object. This will remove the current data. Returns undef on failure. =cut sub copy_from { my $self = shift; my $data = shift; return $self->_set_error('Not a valid source data structure') unless defined $data && ( ref($data) eq 'ARRAY' || ref($data) eq __PACKAGE__); $self->reset; my $i = 0; for my $data_set (@{$data}) { return $self->_set_error("Invalid data set: $i") unless ref($data_set) eq 'ARRAY'; push @{$self}, [@{$data_set}]; $i++; } return $self; } =head2 $data->copy() Returns a copy of the object, or undef on failure. =cut sub copy { my $self = shift; my $new = $self->new(); $new->copy_from($self); return $new; } =head2 $data->read(I<arguments>) Read a data set from a file. This will remove the current data. returns undef on failure. This method uses the standard module Text::ParseWords to parse lines. If you don't have this for some odd reason, don't use this method, or your program will die. B<Data file format>: The default data file format is tab separated data (which can be changed with the delimiter argument). Comment lines are any lines that start with a #. In the following example I have replaced literal tabs with <tab> for clarity # This is a comment, and will be ignored Jan<tab>12<tab>24 Feb<tab>13<tab>37 # March is missing Mar<tab><tab> Apr<tab>9<tab>18 Valid arguments are: I<file>, mandatory. The file name of the file to read from, or a reference to a file handle or glob. $data->read(file => '/data/foo.dat') or die $data->error; $data->read(file => \*DATA) or die $data->error; $data->read(file => $file_handle) or die $data->error; I<no_comment>, optional. Give this a true value if you don't want lines with an initial # to be skipped. $data->read(file => '/data/foo.dat', no_comment => 1); I<delimiter>, optional. A regular expression that will become the delimiter instead of a single tab. $data->read(file => '/data/foo.dat', delimiter => '\s+'); $data->read(file => '/data/foo.dat', delimiter => qr/\s+/); =cut sub read { my $self = shift; return $self->_set_error(ERR_ARGS_NO_HASH) if (@_ && @_ % 2); my %args = @_; return $self->_set_error('Missing required argument: file') unless $args{file}; my $delim = $args{delimiter} || "\t"; $self->reset(); # The following will die if these modules are not present, as # documented. require Text::ParseWords; my $fh; local *FH; if (UNIVERSAL::isa($args{file}, "GLOB")) { $fh = $args{file}; } else { # $fh = \do{ local *FH }; # Odd... This dumps core, sometimes in 5.005 $fh = \*FH; # XXX Need this for perl 5.005 open($fh, $args{file}) or return $self->_set_error("open ($args{file}): $!"); } while (my $line = <$fh>) { chomp $line; next if $line =~ /^#/ && !$args{no_comment}; my @fields = Text::ParseWords::parse_line($delim, 1, $line); next unless @fields; $self->add_point(@fields); } return $self; } =head2 $data->error() OR GD::Graph::Data->error() Returns a list of all the errors that the current object has accumulated. In scalar context, returns the last error. If called as a class method it works at a class level. This method is inherited, see L<GD::Graph::Error> for more information. =cut =head2 $data->has_error() OR GD::Graph::Data->has_error() Returns true if the object (or class) has errors pending, false if not. In some cases (see L<"copy">) this is the best way to check for errors. This method is inherited, see L<GD::Graph::Error> for more information. =cut =head1 NOTES As with all Modules for Perl: Please stick to using the interface. If you try to fiddle too much with knowledge of the internals of this module, you could get burned. I may change them at any time. Specifically, I probably won't always keep this implemented as an array reference. =head1 AUTHOR Martien Verbruggen E<lt>mgjv@tradingpost.com.auE<gt> =head2 Copyright (c) Martien Verbruggen. All rights reserved. This package is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L<GD::Graph>, L<GD::Graph::Error> =cut "Just another true value";