CombBLAS_beta_16_2/000755 000765 000024 00000000000 13271516016 015426 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/ms_sys/000755 000765 000024 00000000000 13212627400 016736 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/._release-notes.html000644 000765 000024 00000000273 13271435245 021305 0ustar00aydinbulucstaff000000 000000 Mac OS X  2‰»TEXTMSIEATTR»˜#˜#com.apple.quarantineq/0082;5ae63aa5;Microsoft\x20Word;CombBLAS_beta_16_2/release-notes.html000644 000765 000024 00000555234 13271435245 021104 0ustar00aydinbulucstaff000000 000000 ÿþ<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns:mv="http://macVmlSchemaUri" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta name=Title content="The Combinatorial BLAS Release Notes"> <meta name=Keywords content=""> <meta http-equiv=Content-Type content="text/html; charset=unicode"> <meta name=ProgId content=Word.Document> <meta name=Generator content="Microsoft Word 15"> <meta name=Originator content="Microsoft Word 15"> <link rel=File-List href="release-notes.fld/filelist.xml"> <title>The Combinatorial BLAS Release Notes</title> <!--[if gte mso 9]><xml> <o:DocumentProperties> <o:Author>Aydin Buluc</o:Author> <o:LastAuthor>Aydin Buluc</o:LastAuthor> <o:Revision>8</o:Revision> <o:TotalTime>18</o:TotalTime> <o:Created>2014-01-17T00:22:00Z</o:Created> <o:LastSaved>2018-04-29T21:35:00Z</o:LastSaved> <o:Pages>1</o:Pages> <o:Words>728</o:Words> <o:Characters>4151</o:Characters> <o:Company>Berkeley Lab</o:Company> <o:Lines>34</o:Lines> <o:Paragraphs>9</o:Paragraphs> <o:CharactersWithSpaces>4870</o:CharactersWithSpaces> <o:Version>15.0</o:Version> </o:DocumentProperties> <o:OfficeDocumentSettings> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]--> <link rel=themeData href="release-notes.fld/themedata.thmx"> <!--[if gte mso 9]><xml> <w:WordDocument> <w:Zoom>150</w:Zoom> <w:SpellingState>Clean</w:SpellingState> <w:GrammarState>Clean</w:GrammarState> <w:TrackMoves>false</w:TrackMoves> <w:TrackFormatting/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>EN-US</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:SplitPgBreakAndParaMark/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="&#45;-"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="false" DefSemiHidden="false" DefQFormat="false" DefPriority="99" LatentStyleCount="382"> <w:LsdException Locked="false" Priority="0" QFormat="true" Name="Normal"/> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 1"/> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 2"/> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 3"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 4"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 5"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 6"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 7"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 8"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 9"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 1"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 2"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 3"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 4"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 5"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 6"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 7"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 8"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="header"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footer"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index heading"/> <w:LsdException Locked="false" Priority="35" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="caption"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="table of figures"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="envelope address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="envelope return"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="line number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="page number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="endnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="endnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="table of authorities"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="macro"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="toa heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 5"/> <w:LsdException Locked="false" Priority="10" QFormat="true" Name="Title"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Closing"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Signature"/> <w:LsdException Locked="false" Priority="1" SemiHidden="true" UnhideWhenUsed="true" Name="Default Paragraph Font"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Message Header"/> <w:LsdException Locked="false" Priority="11" QFormat="true" Name="Subtitle"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Salutation"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Date"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text First Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text First Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Block Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Hyperlink"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="FollowedHyperlink"/> <w:LsdException Locked="false" Priority="22" QFormat="true" Name="Strong"/> <w:LsdException Locked="false" Priority="20" QFormat="true" Name="Emphasis"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Document Map"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Plain Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="E-mail Signature"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Top of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Bottom of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal (Web)"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Acronym"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Cite"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Code"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Definition"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Keyboard"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Preformatted"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Sample"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Typewriter"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Variable"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal Table"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation subject"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="No List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Contemporary"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Elegant"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Professional"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Subtle 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Subtle 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Balloon Text"/> <w:LsdException Locked="false" Priority="59" Name="Table Grid"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Theme"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 9"/> <w:LsdException Locked="false" SemiHidden="true" Name="Placeholder Text"/> <w:LsdException Locked="false" Priority="1" QFormat="true" Name="No Spacing"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading"/> <w:LsdException Locked="false" Priority="61" Name="Light List"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 1"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 1"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 1"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 1"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 1"/> <w:LsdException Locked="false" SemiHidden="true" Name="Revision"/> <w:LsdException Locked="false" Priority="34" QFormat="true" Name="List Paragraph"/> <w:LsdException Locked="false" Priority="29" QFormat="true" Name="Quote"/> <w:LsdException Locked="false" Priority="30" QFormat="true" Name="Intense Quote"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 1"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 1"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 1"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 1"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 1"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 1"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 1"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 2"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 2"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 2"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 2"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 2"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 2"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 2"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 2"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 2"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 2"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 2"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 3"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 3"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 3"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 3"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 3"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 3"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 3"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 3"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 3"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 3"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 3"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 3"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 3"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 4"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 4"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 4"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 4"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 4"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 4"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 4"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 4"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 4"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 4"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 4"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 4"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 4"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 4"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 5"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 5"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 5"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 5"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 5"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 5"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 5"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 5"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 5"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 5"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 5"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 5"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 5"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 5"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 6"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 6"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 6"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 6"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 6"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 6"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 6"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 6"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 6"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 6"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 6"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 6"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 6"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 6"/> <w:LsdException Locked="false" Priority="19" QFormat="true" Name="Subtle Emphasis"/> <w:LsdException Locked="false" Priority="21" QFormat="true" Name="Intense Emphasis"/> <w:LsdException Locked="false" Priority="31" QFormat="true" Name="Subtle Reference"/> <w:LsdException Locked="false" Priority="32" QFormat="true" Name="Intense Reference"/> <w:LsdException Locked="false" Priority="33" QFormat="true" Name="Book Title"/> <w:LsdException Locked="false" Priority="37" SemiHidden="true" UnhideWhenUsed="true" Name="Bibliography"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="TOC Heading"/> <w:LsdException Locked="false" Priority="41" Name="Plain Table 1"/> <w:LsdException Locked="false" Priority="42" Name="Plain Table 2"/> <w:LsdException Locked="false" Priority="43" Name="Plain Table 3"/> <w:LsdException Locked="false" Priority="44" Name="Plain Table 4"/> <w:LsdException Locked="false" Priority="45" Name="Plain Table 5"/> <w:LsdException Locked="false" Priority="40" Name="Grid Table Light"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Mention"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Smart Hyperlink"/> </w:LatentStyles> </xml><![endif]--> <style> <!--p.P3 {min-height: 14.0px;} p.P5 {min-height: 18.0px;} p.P9 {min-height: 14.0px;} /* Font Definitions */ @font-face {font-family:"Courier New"; panose-1:2 7 3 9 2 2 5 2 4 4; mso-font-charset:0; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:-536859905 -1073711037 9 0 511 0;} @font-face {font-family:Times; panose-1:2 0 5 0 0 0 0 0 0 0; mso-font-charset:0; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} @font-face {font-family:Wingdings; panose-1:5 0 0 0 0 0 0 0 0 0; mso-font-charset:2; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:0 268435456 0 0 -2147483648 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:-536870145 1107305727 0 0 415 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} h1 {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"Heading 1 Char"; mso-margin-top-alt:auto; margin-right:0in; mso-margin-bottom-alt:auto; margin-left:0in; mso-pagination:widow-orphan; mso-outline-level:1; font-size:24.0pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} h2 {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"Heading 2 Char"; mso-margin-top-alt:auto; margin-right:0in; mso-margin-bottom-alt:auto; margin-left:0in; mso-pagination:widow-orphan; mso-outline-level:2; font-size:18.0pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} h3 {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"Heading 3 Char"; mso-margin-top-alt:auto; margin-right:0in; mso-margin-bottom-alt:auto; margin-left:0in; mso-pagination:widow-orphan; mso-outline-level:3; font-size:13.5pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} a:link, span.MsoHyperlink {mso-style-noshow:yes; mso-style-priority:99; color:blue; text-decoration:underline; text-underline:single;} a:visited, span.MsoHyperlinkFollowed {mso-style-noshow:yes; mso-style-priority:99; color:purple; text-decoration:underline; text-underline:single;} span.Heading1Char {mso-style-name:"Heading 1 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"Heading 1"; mso-ansi-font-size:16.0pt; mso-bidi-font-size:16.0pt; font-family:"Calibri Light"; mso-ascii-font-family:"Calibri Light"; mso-ascii-theme-font:major-latin; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:major-fareast; mso-hansi-font-family:"Calibri Light"; mso-hansi-theme-font:major-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:major-bidi; color:#2D4F8E; mso-themecolor:accent1; mso-themeshade:181; font-weight:bold;} span.Heading2Char {mso-style-name:"Heading 2 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"Heading 2"; mso-ansi-font-size:13.0pt; mso-bidi-font-size:13.0pt; font-family:"Calibri Light"; mso-ascii-font-family:"Calibri Light"; mso-ascii-theme-font:major-latin; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:major-fareast; mso-hansi-font-family:"Calibri Light"; mso-hansi-theme-font:major-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:major-bidi; color:#4472C4; mso-themecolor:accent1; font-weight:bold;} span.Heading3Char {mso-style-name:"Heading 3 Char"; mso-style-noshow:yes; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"Heading 3"; font-family:"Calibri Light"; mso-ascii-font-family:"Calibri Light"; mso-ascii-theme-font:major-latin; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:major-fareast; mso-hansi-font-family:"Calibri Light"; mso-hansi-theme-font:major-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:major-bidi; color:#4472C4; mso-themecolor:accent1; font-weight:bold;} p.p2, li.p2, div.p2 {mso-style-name:p2; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:9.0pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} p.p3, li.p3, div.p3 {mso-style-name:p3; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:9.0pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} p.p4, li.p4, div.p4 {mso-style-name:p4; mso-style-unhide:no; margin-top:0in; margin-right:0in; margin-bottom:10.5pt; margin-left:0in; mso-pagination:widow-orphan; font-size:8.5pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} p.p5, li.p5, div.p5 {mso-style-name:p5; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} p.p9, li.p9, div.p9 {mso-style-name:p9; mso-style-unhide:no; margin-top:0in; margin-right:0in; margin-bottom:4.5pt; margin-left:0in; mso-pagination:widow-orphan; font-size:9.0pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} span.s1 {mso-style-name:s1; mso-style-unhide:no; mso-ansi-font-size:13.5pt; mso-bidi-font-size:13.5pt; font-family:Times; mso-ascii-font-family:Times; mso-hansi-font-family:Times;} span.s2 {mso-style-name:s2; mso-style-unhide:no; mso-ansi-font-size:8.5pt; mso-bidi-font-size:8.5pt; font-family:Times; mso-ascii-font-family:Times; mso-hansi-font-family:Times;} span.s3 {mso-style-name:s3; mso-style-unhide:no; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; font-family:Times; mso-ascii-font-family:Times; mso-hansi-font-family:Times;} span.s4 {mso-style-name:s4; mso-style-unhide:no; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; font-family:Courier; mso-ascii-font-family:Courier; mso-hansi-font-family:Courier;} span.s5 {mso-style-name:s5; mso-style-unhide:no; color:#0000EE; text-decoration:underline; text-underline:single;} span.SpellE {mso-style-name:""; mso-spl-e:yes;} span.GramE {mso-style-name:""; mso-gram-e:yes;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:10.0pt; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt;} @page WordSection1 {size:8.5in 11.0in; margin:1.0in 1.25in 1.0in 1.25in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} /* List Definitions */ @list l0 {mso-list-id:120000762; mso-list-template-ids:190749096;} @list l0:level1 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l0:level2 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l0:level3 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l0:level4 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l0:level5 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l0:level6 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l0:level7 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l0:level8 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l0:level9 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l1 {mso-list-id:448167702; mso-list-template-ids:-1604408110;} @list l1:level1 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l1:level2 {mso-level-number-format:bullet; mso-level-text:o; mso-level-tab-stop:1.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:"Courier New"; mso-bidi-font-family:"Times New Roman";} @list l1:level3 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l1:level4 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l1:level5 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l1:level6 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l1:level7 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l1:level8 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l1:level9 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2 {mso-list-id:941037056; mso-list-template-ids:2109484434;} @list l2:level1 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2:level2 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2:level3 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2:level4 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2:level5 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2:level6 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2:level7 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2:level8 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l2:level9 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3 {mso-list-id:1210150872; mso-list-template-ids:235977046;} @list l3:level1 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3:level2 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3:level3 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3:level4 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3:level5 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3:level6 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3:level7 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3:level8 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l3:level9 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4 {mso-list-id:1361663422; mso-list-template-ids:1111251678;} @list l4:level1 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4:level2 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4:level3 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4:level4 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4:level5 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4:level6 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4:level7 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4:level8 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l4:level9 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l5 {mso-list-id:1484589693; mso-list-type:hybrid; mso-list-template-ids:151650356 67698689 196130222 67698693 67698689 67698691 67698693 67698689 67698691 67698693;} @list l5:level1 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:Symbol;} @list l5:level2 {mso-level-start-at:0; mso-level-number-format:bullet; mso-level-text:-; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman";} @list l5:level3 {mso-level-number-format:bullet; mso-level-text:§ð; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:Wingdings;} @list l5:level4 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:Symbol;} @list l5:level5 {mso-level-number-format:bullet; mso-level-text:o; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:"Courier New";} @list l5:level6 {mso-level-number-format:bullet; mso-level-text:§ð; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:Wingdings;} @list l5:level7 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:Symbol;} @list l5:level8 {mso-level-number-format:bullet; mso-level-text:o; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:"Courier New";} @list l5:level9 {mso-level-number-format:bullet; mso-level-text:§ð; mso-level-tab-stop:none; mso-level-number-position:left; text-indent:-.25in; font-family:Wingdings;} @list l6 {mso-list-id:1948613391; mso-list-template-ids:1581035490;} @list l6:level1 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l6:level2 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l6:level3 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:1.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l6:level4 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l6:level5 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:2.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l6:level6 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l6:level7 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:3.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l6:level8 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.0in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} @list l6:level9 {mso-level-number-format:bullet; mso-level-text:·ð; mso-level-tab-stop:4.5in; mso-level-number-position:left; text-indent:-.25in; mso-ansi-font-size:10.0pt; font-family:Symbol;} ol {margin-bottom:0in;} ul {margin-bottom:0in;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:"Table Normal"; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-parent:""; mso-padding-alt:0in 5.4pt 0in 5.4pt; mso-para-margin:0in; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman";} </style> <![endif]--> <meta http-equiv=Content-Style-Type content="text/css"> <meta name=CocoaVersion content=1187.34> <!--[if gte mso 9]><xml> <o:shapedefaults v:ext="edit" spidmax="1026"/> </xml><![endif]--><!--[if gte mso 9]><xml> <o:shapelayout v:ext="edit"> <o:idmap v:ext="edit" data="1"/> </o:shapelayout></xml><![endif]--> </head> <body bgcolor=white lang=EN-US link=blue vlink=purple style='tab-interval:.5in'> <div class=WordSection1> <h1 style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt;margin-left: 0in'><span style='font-size:18.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>The Combinatorial BLAS Release Notes<o:p></o:p></span></h1> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'>This document describes the new features and changes in each release of the Combinatorial BLAS. The numbering is of the form Z.Y.X.<o:p></o:p></span></p> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'>X denotes bug fixes and/or minor updates for existing functionality.<o:p></o:p></span></p> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'>Y denotes new major functionality.<o:p></o:p></span></p> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'>Z denotes an overall paradigm shift in terms of abstractions.<o:p></o:p></span></p> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <p class=p4><span class=SpellE><span class=s1><b><span style='font-size:13.5pt; mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></b></span></span><span class=s1><b><span style='font-size:13.5pt;mso-bidi-font-family:"Times New Roman"'> 1.6.2 </span></b></span><span style='mso-bidi-font-family:"Times New Roman"'>(April 2018)<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Several bug fixes related to AWPM usage and its <span class=SpellE>SuperLU</span> integration<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Modernized <span class=SpellE>cmake</span> usage thanks to Tristan<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in'><span class=s1><span style='mso-ansi-font-size: 9.0pt;mso-bidi-font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></span></p> <p class=p4><span class=SpellE><span class=s1><b><span style='font-size:13.5pt; mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></b></span></span><span class=s1><b><span style='font-size:13.5pt;mso-bidi-font-family:"Times New Roman"'> 1.6.1 </span></b></span><span style='mso-bidi-font-family:"Times New Roman"'>(January 2018)<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Several bug fixes<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Modernizing some calls to <span class=SpellE>ParallelReadMM</span> as opposed to <span class=SpellE>ReadDistribute</span><o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Introducing <span class=SpellE>combblas</span>:: namespace, thanks to Tristan.<br style='mso-special-character:line-break'> <![if !supportLineBreakNewLine]><br style='mso-special-character:line-break'> <![endif]><o:p></o:p></span></p> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <p class=p4><span class=SpellE><span class=s1><b><span style='font-size:13.5pt; mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></b></span></span><span class=s1><b><span style='font-size:13.5pt;mso-bidi-font-family:"Times New Roman"'> 1.6 </span></b></span><span style='mso-bidi-font-family:"Times New Roman"'>(September 2017)<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Fully parallel text-file reader for vectors (<span class=SpellE><span class=GramE>FullyDistSpVec</span></span><span class=GramE>::</span><span class=SpellE>ParallelReadMM</span>() and <span class=SpellE>FullyDistVec</span>::<span class=SpellE>ParallelReadMM</span>())<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Fully parallel text-file writer for vectors (<span class=SpellE><span class=GramE>FullyDistSpVec</span></span><span class=GramE>::</span><span class=SpellE>ParallelWrite</span> () and <span class=SpellE>FullyDistVec</span>::<span class=SpellE>ParallelWrite</span>())<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Reverse <span class=SpellE>Cuthill</span>-McKee (RCM) ordering implementation<o:p></o:p></span></p> <p class=p2 style='margin-left:1.0in;text-indent:-.25in;mso-list:l5 level2 lfo1'><![if !supportLists]><span style='mso-fareast-font-family:Times;mso-bidi-font-family:Times'><span style='mso-list:Ignore'>-<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Please cite [12] if you use this implementation<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Novel multithreaded <span class=SpellE>SpGEMM</span> and <span class=SpellE>SpMV</span> (with sparse vectors) algorithms are integrated with the rest of <span class=SpellE>CombBLAS</span>.<o:p></o:p></span></p> <p class=p2 style='margin-left:1.0in;text-indent:-.25in;mso-list:l5 level2 lfo1'><![if !supportLists]><span style='mso-fareast-font-family:Times;mso-bidi-font-family:Times'><span style='mso-list:Ignore'>-<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>For <span class=GramE>benchmarking</span> multithreaded <span class=SpellE>SpMV</span> with sparse vectors, go to Applications/SpMSpV-IPDPS2017 directory and use the code there.<o:p></o:p></span></p> <p class=p2 style='margin-left:1.0in;text-indent:-.25in;mso-list:l5 level2 lfo1'><![if !supportLists]><span style='mso-fareast-font-family:Times;mso-bidi-font-family:Times'><span style='mso-list:Ignore'>-<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Please cite [13] if you use the new multithreaded <span class=SpellE>SpMV</span> with sparse vectors.<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Extended CSC support <o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Previously deprecated <span class=SpellE>SpParVec</span> and <span class=SpellE>DenseParVec</span> (that were distributed to diagonal processors only) classes are removed.<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span class=s1><span style='mso-ansi-font-size:9.0pt;mso-bidi-font-size:9.0pt; font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family:Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span></span><![endif]><span style='mso-fareast-font-family: "Times New Roman";mso-bidi-font-family:"Times New Roman"'>Lots of more bug fixes</span><span class=s1><span style='mso-ansi-font-size:9.0pt;mso-bidi-font-size: 9.0pt'><o:p></o:p></span></span></p> <p class=p4><span class=s1><b><span style='font-size:13.5pt;mso-bidi-font-family: "Times New Roman"'><o:p>&nbsp;</o:p></span></b></span></p> <p class=p4><span class=SpellE><span class=s1><b><span style='font-size:13.5pt; mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></b></span></span><span class=s1><b><span style='font-size:13.5pt;mso-bidi-font-family:"Times New Roman"'> 1.5 </span></b></span><span style='mso-bidi-font-family:"Times New Roman"'>(January 2016)</span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Fully parallel matrix market format reader (<span class=SpellE><span class=GramE>SpParMat</span></span><span class=GramE>::</span><span class=SpellE>ParallelReadMM</span>())<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Complete multithreading support, including <span class=SpellE>SpGEMM</span> (previously it was solely <span class=SpellE>SpMV</span>), enabled by -DTHREADED during compilation<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Experimental 3D <span class=SpellE>SpGEMM</span> (the ability to switch processor grids from 2D to 3D will have to wait for version 1.6)<o:p></o:p></span></p> <p class=p2 style='margin-left:1.0in;text-indent:-.25in;mso-list:l5 level2 lfo1'><![if !supportLists]><span style='mso-fareast-font-family:Times;mso-bidi-font-family:Times'><span style='mso-list:Ignore'>-<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Please cite [9] if you use this implementation<o:p></o:p></span></p> <p class=p2 style='margin-left:1.0in;text-indent:-.25in;mso-list:l5 level2 lfo1'><![if !supportLists]><span style='mso-fareast-font-family:Times;mso-bidi-font-family:Times'><span style='mso-list:Ignore'>-<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>cd 3DSpGEMM/, make <span class=SpellE>test_mpipspgemm</span>, and call the executable with correct parameters<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Maximal and Maximum cardinality matching algorithms on bipartite graphs<o:p></o:p></span></p> <p class=p2 style='margin-left:1.0in;text-indent:-.25in;mso-list:l5 level2 lfo1'><![if !supportLists]><span style='mso-fareast-font-family:Times;mso-bidi-font-family:Times'><span style='mso-list:Ignore'>-<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Please cite [10] for maximal cardinality and [11] for maximum cardinality matching<o:p></o:p></span></p> <p class=p2 style='margin-left:1.0in;text-indent:-.25in;mso-list:l5 level2 lfo1'><![if !supportLists]><span style='mso-fareast-font-family:Times;mso-bidi-font-family:Times'><span style='mso-list:Ignore'>-<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>cd <span class=SpellE>MaximumMatching</span>, make <span class=SpellE>bpmm</span>, and call the executable with correct parameters<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Automated <span class=SpellE>MPI_Op</span> creation from simple C++ function objects (simplifies <span class=SpellE>semiring</span> descriptions and <span class=GramE>Reduce(</span>) functions) <o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span class=SpellE><span class=GramE><span style='mso-fareast-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman"'>FullyDistSpVec</span></span></span><span class=GramE><span style='mso-fareast-font-family:"Times New Roman";mso-bidi-font-family: "Times New Roman"'>::</span></span><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Invert() to map from/to (integer) values to/from indices<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Many more helper functions<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Experimental CSC support for low concurrencies<o:p></o:p></span></p> <p class=p2 style='margin-left:.5in;text-indent:-.25in;mso-list:l5 level1 lfo1'><![if !supportLists]><span style='font-family:Symbol;mso-fareast-font-family:Symbol;mso-bidi-font-family: Symbol'><span style='mso-list:Ignore'>·<span style='font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><![endif]><span style='mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Lots of bug fixes</span><span style='mso-bidi-font-family:"Times New Roman"'><o:p></o:p></span></p> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <p class=p4><span class=SpellE><span class=s1><b><span style='font-size:13.5pt; mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></b></span></span><span class=s1><b><span style='font-size:13.5pt;mso-bidi-font-family:"Times New Roman"'> 1.4 </span></b></span><span style='mso-bidi-font-family:"Times New Roman"'>(January 2014)<o:p></o:p></span></p> <ul style='margin-top:0in' type=disc> <li class=MsoNormal style='mso-list:l6 level1 lfo2;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Direction optimizing breadth-first search is available</span><span style='font-size:9.0pt;mso-bidi-font-family: "Times New Roman"'><o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l6 level1 lfo2;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Several performance enhancements and <span class=SpellE>bugfixes</span></span><span style='font-size:9.0pt; mso-bidi-font-family:"Times New Roman"'><o:p></o:p></span></li> </ul> <p class=MsoNormal style='margin-left:.5in'><span style='mso-bidi-font-family: "Times New Roman"'><o:p>&nbsp;</o:p></span></p> <p class=p4><span class=SpellE><span class=s1><b><span style='font-size:13.5pt; mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></b></span></span><span class=s1><b><span style='font-size:13.5pt;mso-bidi-font-family:"Times New Roman"'> 1.3 </span></b></span><span style='mso-bidi-font-family:"Times New Roman"'>(February 2013)<o:p></o:p></span></p> <ul style='margin-top:0in' type=disc> <li class=MsoNormal style='mso-list:l6 level1 lfo2;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>MPI C++ bindings are removed since they are no longer supported by the MPI-3 standard. All MPI calls are C-style.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l6 level1 lfo2;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>On-the-fly filtering support illustrated through examples.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l6 level1 lfo2;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Frequently asked questions (FAQ) added.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l6 level1 lfo2;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Maximal Independent Set (MIS) application implemented in the language of linear algebra.<o:p></o:p></span></li> </ul> <p class=p5><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <h3 style='margin-top:0in;margin-right:0in;margin-bottom:10.5pt;margin-left: 0in'><span class=SpellE><span style='font-size:10.5pt;mso-fareast-font-family: "Times New Roman";mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></span><span style='font-size:10.5pt;mso-fareast-font-family:"Times New Roman";mso-bidi-font-family: "Times New Roman"'> 1.2.1 </span><span class=s2><span style='font-size:8.5pt; mso-fareast-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman"'>(April 2012)</span></span><span style='font-size:10.5pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'><o:p></o:p></span></h3> <ul style='margin-top:0in' type=disc> <li class=MsoNormal style='mso-list:l0 level1 lfo3;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Sparse matrix assignment is improved to cover cases where the additive inverses are not available.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l0 level1 lfo3;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Prune with row/column vector indices are supported.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l0 level1 lfo3;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>New test/timing scripts for <span class=SpellE>galerkin</span> restriction.<o:p></o:p></span></li> </ul> <h2 style='margin-top:0in;margin-right:0in;margin-bottom:10.5pt;margin-left: 0in'><span class=SpellE><span style='font-size:13.5pt;mso-fareast-font-family: "Times New Roman";mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></span><span style='font-size:13.5pt;mso-fareast-font-family:"Times New Roman";mso-bidi-font-family: "Times New Roman"'> 1.2 </span><span class=s2><span style='font-size:8.5pt; mso-fareast-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman"'>(March 2012)</span></span><span style='font-size:13.5pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'><o:p></o:p></span></h2> <ul style='margin-top:0in' type=disc> <li class=MsoNormal style='mso-list:l1 level1 lfo4;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>It is made possible to create matrices with globally 64-bit, locally 32-bit indices.<o:p></o:p></span></li> <ul style='margin-top:0in' type=circle> <li class=MsoNormal style='mso-list:l1 level2 lfo4;tab-stops:list 1.0in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>This is very handy because the index range for submatrices are typically <span class=SpellE>addressible</span> with 32-bit indices but the global semantics require 64-bit computation.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l1 level2 lfo4;tab-stops:list 1.0in'><span class=s3><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Example: </span></span><span class=SpellE><span style='font-size:9.0pt;font-family:Courier;mso-fareast-font-family: "Times New Roman";mso-bidi-font-family:"Times New Roman"'>SpParMat</span></span><span style='font-size:9.0pt;font-family:Courier;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'> &lt;int64_t, float, <span class=SpellE>SpDCCols</span>&lt;int32_<span class=GramE>t,float</span>&gt; &gt; A;<o:p></o:p></span></li> </ul> <li class=MsoNormal style='mso-list:l1 level1 lfo4;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Some primitives (Sparse <span class=SpellE>SpMV</span>, <span class=SpellE>EWiseMult</span>, Apply, Set) are hybrid multithreaded (using <span class=SpellE>OpenMP</span>) within a socket. To enable, <a href="http://gauss.cs.ucsb.edu/~aydin/CombBLAS_FILES/CombBLASv1.2_threading.txt">follow instructions here</a><o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l1 level1 lfo4;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>A new binary format is introduced that enables parallel I/O.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l1 level1 lfo4;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Filtering (in the context of semantic graphs) is supported in Sparse <span class=SpellE>SpMV</span> and <span class=SpellE>SpGEMM</span> by checking against the <span class=SpellE>semiring</span> additive identity (SAID) that is returned by the scalar <span class=SpellE>semiring</span> multiply if the edge is filtered.<o:p></o:p></span></li> </ul> <h3 style='margin-top:0in;margin-right:0in;margin-bottom:10.5pt;margin-left: 0in'><span class=SpellE><span style='font-size:10.5pt;mso-fareast-font-family: "Times New Roman";mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></span><span style='font-size:10.5pt;mso-fareast-font-family:"Times New Roman";mso-bidi-font-family: "Times New Roman"'> 1.1.1 </span><span class=s2><span style='font-size:8.5pt; mso-fareast-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman"'>(August 2011)</span></span><span style='font-size:10.5pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'><o:p></o:p></span></h3> <ul style='margin-top:0in' type=disc> <li class=MsoNormal style='mso-list:l4 level1 lfo5;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Bug fixes for the Graph500 sample script and some sample input files.<o:p></o:p></span></li> </ul> <p class=p3><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <h2 style='margin-top:0in;margin-right:0in;margin-bottom:10.5pt;margin-left: 0in'><span class=SpellE><span style='font-size:13.5pt;mso-fareast-font-family: "Times New Roman";mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></span><span style='font-size:13.5pt;mso-fareast-font-family:"Times New Roman";mso-bidi-font-family: "Times New Roman"'> 1.1 </span><span class=s2><span style='font-size:8.5pt; mso-fareast-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman"'>(May 2011)</span></span><span style='font-size:13.5pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'><o:p></o:p></span></h2> <ul style='margin-top:0in' type=disc> <li class=MsoNormal style='mso-list:l2 level1 lfo6;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Fully distributed vectors are supported for better load balance; diagonally distributed vectors are deprecated.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l2 level1 lfo6;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Scalable Sparse <span class=SpellE>SpMV</span> (that enables breadth-first search) is exposed.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l2 level1 lfo6;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Many new functions such as <span class=SpellE>Matlab</span> equivalent </span><span class=GramE><span class=s4><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Sparse(</span></span></span><span class=s4><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>)</span></span><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>-like constructor and </span><span class=s4><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Find()</span></span><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'><o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l2 level1 lfo6;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>An exact splitter is used recursively until the range is covered by at most 512 core, before <span class=SpellE>PSort</span> is called. This is because <span class=SpellE>PSort</span> needed local memory that was quadratic in the number of processors.<o:p></o:p></span></li> <li class=MsoNormal style='mso-list:l2 level1 lfo6;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Sparse matrix to dense vector reduction along rows is made memory efficient for large number of cores.<o:p></o:p></span></li> </ul> <p class=p3><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <h2 style='margin-top:0in;margin-right:0in;margin-bottom:10.5pt;margin-left: 0in'><span class=SpellE><span style='font-size:13.5pt;mso-fareast-font-family: "Times New Roman";mso-bidi-font-family:"Times New Roman"'>CombBLAS</span></span><span style='font-size:13.5pt;mso-fareast-font-family:"Times New Roman";mso-bidi-font-family: "Times New Roman"'> 1.0 </span><span class=s2><span style='font-size:8.5pt; mso-fareast-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman"'>(May 2011)</span></span><span style='font-size:13.5pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'><o:p></o:p></span></h2> <ul style='margin-top:0in' type=disc> <li class=MsoNormal style='mso-list:l3 level1 lfo7;tab-stops:list .5in'><span style='font-size:9.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Initial release of the Combinatorial BLAS.<o:p></o:p></span></li> </ul> <p class=p9><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <p class=p2><span style='mso-bidi-font-family:"Times New Roman"'>Go <a href="../../../SVNLOCAL/kdt/trunk/CombBLAS/index.html"><span class=s5>back</span></a> to the <span class=SpellE>the</span> Combinatorial BLAS home page.<o:p></o:p></span></p> </div> </body> </html> CombBLAS_beta_16_2/ReleaseTests/000755 000765 000024 00000000000 13271513272 020032 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/CMakeLists.txt000644 000765 000024 00000007421 13271404146 020172 0ustar00aydinbulucstaff000000 000000 cmake_minimum_required(VERSION 3.3) set(CombBLAS_VERSION 1.16.0) project(CombBLAS VERSION ${CombBLAS_VERSION} LANGUAGES C CXX) # Main CombBLAS library add_library(CombBLAS src/CommGrid.cpp src/mmio.c src/MPIType.cpp src/MPIOp.cpp src/MemoryPool.cpp src/hash.cpp) # require c++14 if("cxx_std_14" IN_LIST CMAKE_CXX_COMPILE_FEATURES) # check if cmake knows about c++14 target_compile_features(CombBLAS PUBLIC cxx_std_14) else() target_compile_options(CombBLAS PUBLIC $<$:-std=c++14>) endif() # set include directories target_include_directories(CombBLAS PUBLIC $ $) target_include_directories(CombBLAS PUBLIC $ $) target_include_directories(CombBLAS PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/CombBLAS) # MPI and OpenMP dependencies find_package(MPI REQUIRED) find_package(OpenMP REQUIRED) if(TARGET MPI::MPI_CXX) # Use target if available (cmake >= 3.9) target_link_libraries(CombBLAS PUBLIC MPI::MPI_CXX) elseif() target_compile_options(CombBLAS PUBLIC "${MPI_CXX_COMPILE_FLAGS}") target_link_libraries(CombBLAS PUBLIC MPI_CXX_LINK_LIBRARIES "${MPI_CXX_LINKFLAGS}") target_include_directories(CombBLAS PUBLIC "${MPI_CXX_INCLUDE_PATH}") endif() if(OPENMP_FOUND OR OpenMP_CXX_FOUND) # Set THREADED if OpenMP is found target_compile_definitions(CombBLAS PUBLIC THREADED) if(TARGET OpenMP::OpenMP_CXX) target_link_libraries(CombBLAS PUBLIC OpenMP::OpenMP_CXX) else() target_compile_options(CombBLAS PUBLIC "${OpenMP_CXX_FLAGS}") target_link_libraries(CombBLAS PUBLIC "${OpenMP_CXX_FLAGS}") endif() endif() ADD_SUBDIRECTORY( usort ) target_link_libraries(CombBLAS PUBLIC Usortlib) ADD_SUBDIRECTORY( graph500-1.2/generator ) target_link_libraries(CombBLAS PUBLIC GraphGenlib) # Set up exported configuration # This allows CombBLAS to be installed in two ways: # 1. In /usr/local (or whatever prefix is specified) # 2. Exporting the current build directory. This allows a user to make # modifications to CombBLAS and have the changes automatically recompiled for # dependent projects. # Either way, we need to create a CombBLASConfig.cmake. set(ConfigPackageLocation lib/cmake/CombBLAS) # Generate version number header include(GenerateExportHeader) generate_export_header(CombBLAS) set_property(TARGET CombBLAS PROPERTY VERSION ${CombBLAS_VERSION}) # installation install(DIRECTORY include/ DESTINATION include) install(DIRECTORY psort-1.0/include/ DESTINATION include) install(TARGETS CombBLAS EXPORT CombBLASTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin INCLUDES DESTINATION include ) include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/CombBLAS/CombBLASConfigVersion.cmake" VERSION ${CombBLAS_VERSION} COMPATIBILITY AnyNewerVersion ) # The following commands allow for option 2. export(EXPORT CombBLASTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/CombBLAS/CombBLASTargets.cmake" NAMESPACE CombBLAS:: ) configure_file(cmake/CombBLASConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/CombBLAS/CombBLASConfig.cmake" COPYONLY ) # Allow for option 2 install(EXPORT CombBLASTargets FILE CombBLASTargets.cmake NAMESPACE CombBLAS:: DESTINATION ${ConfigPackageLocation} ) export(PACKAGE CombBLAS) install( FILES cmake/CombBLASConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/CombBLAS/CombBLASConfigVersion.cmake" DESTINATION ${ConfigPackageLocation} COMPONENT Devel ) # Testing ENABLE_TESTING() INCLUDE(CTest) ADD_SUBDIRECTORY( ReleaseTests ) ADD_SUBDIRECTORY( Applications ) ADD_SUBDIRECTORY( Ordering ) ADD_SUBDIRECTORY( Applications/SpMSpV-IPDPS2017 ) ADD_SUBDIRECTORY( 3DSpGEMM ) CombBLAS_beta_16_2/._.DS_Store000644 000765 000024 00000000170 13271513772 017332 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/.DS_Store000644 000765 000024 00000054004 13271513772 017122 0ustar00aydinbulucstaff000000 000000 Bud1PP H 0 9plicat  @€ @€ @€ @OrderingvSrnlong psort-1.0Ilocblob;Xÿÿÿÿÿÿ psort-1.0bwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™š psort-1.0lsvCblobmbplist00Ø E G_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDatesXiconSize « #(-27;@Ô   WvisibleUwidthYascendingZidentifier , TnameÔ #XubiquityÔ   µ\dateModifiedÔ "[dateCreatedÔ $% Tsizea Ô )* Tkinds Ô ./ Ulabeld Ô 34 WversionK Ô 8 Xcomments Ô =?È^dateLastOpenedÔ AYdateAdded#@( #@0.@H\epƒŒŽ›¤¬²¼ÇÈËÌÑÚÛÝÞçðñóô   !&()*38:;<EKMNOX`bcdmvwx‚„…”§¨©ª³´H½ psort-1.0lsvpblobRbplist00Ø D F_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDatesXiconSize Ù #'+05:?Xcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameÔ WvisibleUwidthYascendingUindex, Ô ÈÔ $ µ Ô($Ô -/ aÔ2 4d Ô 7 9 s Ô< >K Ô@  #@( #@0.@H\epƒŒŽ¢«ºÇÓØÞãëðù')+,-689:CEFGPQSTV_`bcenoqrt}~€ƒŒŽ‘š›G¤ psort-1.0vSrnlongREADME_DEVELOPERSIlocblobXÿÿÿÿÿÿrelease-notes.htmlIlocblob…Xÿÿÿÿÿÿ ReleaseTestsIlocblobóXÿÿÿÿÿÿ ReleaseTestsbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™š ReleaseTestslsvCblob¯bplist00Ú GHI KXiconSize_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 «!%*/49=BÔ  WvisibleUwidthYascendingZidentifier , TnameÔ#XubiquityÔ \dateModifiedµ Ô"[dateCreatedÔ&' Tsizea Ô+, Tkinds Ô01 Ulabeld Ô56 WversionK Ô: Xcomments Ô>?^dateLastOpenedÈÔEYdateAdded#@S@#@(# &8@TfoŒŸ´½¾ÊÓÛáëö÷úû    ,./09EFGPUWXYbgijktz|}~‡‘’“œ¥¦§°¿ÁÂÃÌÍÎØÙâëôõL÷ ReleaseTestslsvpblob”bplist00Ú GHI )XiconSize_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 Ù %*.38=BXcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameÔ WvisibleUwidthYascendingUindex, Ô"$ÈÔ ') µÔ'-Ô 02 aÔ5 7d Ô : < s Ô? AK ÔC  #@S@#@(# &8@TfoŒŸ´½¾ÑÚéö (06@FGJKMVWYZ\efhiktuvx‚„…‡‘“”–Ÿ ¢£¥®¯±²´½¿ÀÁÂËÔÝKÞ ReleaseTestsvSrnlongsrcIlocblob©ÈÿÿÿÿÿÿsrcbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šsrcvSrnlongTestCXXAcceptsFlag.cmakeIlocblob©8ÿÿÿÿÿÿTODO.txtIlocblob8ÿÿÿÿÿÿusortIlocblob©¨ÿÿÿÿÿÿ 3DSpGEMMIlocblob(ÿÿÿÿÿÿ3DSpGEMMbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™š3DSpGEMMlsvCblobÉbplist00Ú JKJLM _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates «$(-27<@EÔ  WvisibleUwidthYascendingZidentifier , TnameÔUwidthYascendingWvisibleXubiquity#Ô ! \dateModifiedµ Ô%![dateCreatedÔ *, aTsizeÔ / 1 s TkindÔ4 6d UlabelÔ9 ;K WversionÔ ? XcommentsÔAB^dateLastOpenedÈÔ!HYdateAdded##@(Tname#@0 2DL`r{˜¡´¶·ÃÌÔÚäïðóôù#%&'0=?@AJVWXabdejstvw|…†ˆ‰˜™›œ¤­®¯¸ÁÐÒÓÔÝÞßéêóü O 3DSpGEMMlsvpblobbplist00Ú FGFHI _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates Ù %*/49>BXcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ UindexUwidthYascendingWvisible, Ô"$ÈÔ')µÔ+, a Ô01 d Ô56 s Ô:; K Ô?  Ô ' ##@(Tname#@0 2DL`r{˜¡´¶·ÊÓâîóùþ !'-7?ADEFOPRSU^_abdmoqrs|~€‚‹‘šœžŸ ©«¬­¶·¸¹ÂËÐÙKÚ3DSpGEMMvSrnlong ApplicationsIlocblob…(ÿÿÿÿÿÿ ApplicationsbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™š Applicationsicvpblob¾bplist00ß _backgroundColorBlue[gridSpacingXtextSize_backgroundColorRed^backgroundType_backgroundColorGreen[gridOffsetX[gridOffsetY_scrollPositionY\showItemInfo_viewOptionsVersion_scrollPositionXYarrangeBy]labelOnBottom_showIconPreviewXiconSize#?ð#@K#@(##@}@Tnone #@P+AMVkz‘©»ÈÝïù"+4=?HQRTYZ[d ApplicationslsvCblob¨bplist00Ú HIHJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates «!%*/49>CÔ ZidentifierUwidthYascendingWvisibleTname¸ Ô#XubiquityÔ  µ\dateModifiedÔ$[dateCreatedÔ&' Tsizea Ô+, Tkinds Ô01 Ulabeld Ô56 WversionK Ô:; Xcomments, Ô@BÈ^dateLastOpenedÔDYdateAdded##@(#@0 2DL`r{˜¡´¶·ÃÌ×Ýçïôö÷ø(123?HMOPQZ_abclrtuv‡‰Š‹” ¡¢«¬®¯¾ÇÑÒÓÔÝæïLð Applicationslsvpblobbplist00Ú GHGI _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates Ù %)-27 @K Ô C E ¸ ##@(#@0 2DL`r{˜¡´¶·ÊÓâïû !)/9?@CDFOQSTU^`abkmnoxy{|~‡ˆŠ‹–—™šœ¥¦¨©«´µ·¸º»ÄÍÖK×usortIlocblob©¨ÿÿÿÿÿÿBipartiteMatchingsIlocblob©˜ÿÿÿÿÿÿBipartiteMatchingsbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šBipartiteMatchingslsvCblobmbplist00Ø E GXiconSize_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDates_viewOptionsVersion#@0 « #(-27;@Ô   WvisibleUwidthYascendingZidentifier , TnameÔ Xubiquity#Ô   µ\dateModifiedÔ "[dateCreatedÔ  %' aTsizeÔ  * , s TkindÔ / 1d UlabelÔ 4 6K WversionÔ  : XcommentsÔ =?È^dateLastOpenedÔ AYdateAdded#@( "4<PYdwŒ•–¢«³¹ÃÎÏÒÓØáêìíî÷øúû()+,1:;=>CLMOPV_`bcktuvˆ‰‹Œ›¤®¯°±º»H½BipartiteMatchingslsvpblobRbplist00Ø E $XiconSize_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDates_viewOptionsVersion#@0 Ù #(,16;@Xcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameÔ UindexUwidthYascendingWvisible, Ô ÈÔ$% µ Ô)%Ô-. a Ô23 d Ô78 s Ô<= K ÔA  #@( "4<PYdwŒ•–©²ÁÎÚßåêò÷  #$%.0234=?ABCLNOPY[]^_hjlmnwy{|}†ˆŠ‹Œ•—˜™š£G¤BipartiteMatchingsvSrnlongCMakeLists.txtIlocblob…˜ÿÿÿÿÿÿDoxyfileIlocblob©ÿÿÿÿÿÿFAQ-combblas.htmlIlocblobÿÿÿÿÿÿ graph500-1.2Ilocblob…ÿÿÿÿÿÿ graph500-1.2bwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™š graph500-1.2lsvCblobmbplist00Ø E G_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDatesXiconSize « #(-27;@Ô   WvisibleUwidthYascendingZidentifier , TnameÔ #XubiquityÔ   µ\dateModifiedÔ "[dateCreatedÔ $% Tsizea Ô )* Tkinds Ô ./ Ulabeld Ô 34 WversionK Ô 8 Xcomments Ô =?È^dateLastOpenedÔ AYdateAdded#@( #@0.@H\epƒŒŽ›¤¬²¼ÇÈËÌÑÚÛÝÞçðñóô   !&()*38:;<EKMNOX`bcdmvwx‚„…”§¨©ª³´H½ graph500-1.2lsvpblobRbplist00Ø D F_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDatesXiconSize Ù #'+05:?Xcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameÔ WvisibleUwidthYascendingUindex, Ô ÈÔ $ µ Ô($Ô -/ aÔ2 4d Ô 7 9 s Ô< >K Ô@  #@( #@0.@H\epƒŒŽ¢«ºÇÓØÞãëðù')+,-689:CEFGPQSTV_`bcenoqrt}~€ƒŒŽ‘š›G¤ graph500-1.2vSrnlongincludeIlocblob;xÿÿÿÿÿÿincludebwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™š{˜¡´¶·ÊÓâïû !)/9?@CDFOQSTU^`abkmnoxy{|~‡ˆŠ‹–—™šœ¥¦¨©«´µ·¸º»ÄÍÖK×usortIlocblob©¨ÿÿÿÿÿÿ includelsvpblobRbplist00Ø D F_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDatesXiconSize Ù #'+05:?Xcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameÔ WvisibleUwidthYascendingUindex, Ô ÈÔ $ µ Ô($Ô -/ aÔ2 4d Ô 7 9 s Ô< >K Ô@  #@( #@0.@H\epƒŒŽ¢«ºÇÓØÞãëðù')+,-689:CEFGPQSTV_`bcenoqrt}~€ƒŒŽ‘š›G¤includevSrnlongLICENSEIlocblob…xÿÿÿÿÿÿ MainPage.hIlocblob¨ÿÿÿÿÿÿ ms_inttypesIlocblob©èÿÿÿÿÿÿms_sysIlocblobèÿÿÿÿÿÿNERSC_INSTALL.htmlIlocblob…èÿÿÿÿÿÿOrderingIlocblobóèÿÿÿÿÿÿOrderingbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šOrderinglsvCblobmbplist00Ø E GXiconSize_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDates_viewOptionsVersion#@0 « #(-27;@Ô   WvisibleUwidthYascendingZidentifier , TnameÔ Xubiquity#Ô   µ\dateModifiedÔ "[dateCreatedÔ  %' aTsizeÔ  * , s TkindÔ / 1d UlabelÔ 4 6K WversionÔ  : XcommentsÔ =?È^dateLastOpenedÔ AYdateAdded#@( "4<PYdwŒ•–¢«³¹ÃÎÏÒÓØáêìíî÷øúû()+,1:;=>CLMOPV_`bcktuvˆ‰‹Œ›¤®¯°±º»H½ÿÿÿÿÿÿ graph500-1.2Ilocblob…ÿÿÿÿÿÿ graph500-1.2bwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_Co ApplicationsvSrnlongincludelsvCblobmbplist00Ø E G_viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDatesXiconSize « #(-27;@Ô   WvisibleUwidthYascendingZidentifier , TnameÔ #XubiquityÔ   µ\dateModifiedÔ "[dateCreatedÔ $% Tsizea Ô )* Tkinds Ô ./ Ulabeld Ô 34 WversionK Ô 8 Xcomments Ô =?È^dateLastOpenedÔ AYdateAdded#@( #@0.@H\epƒŒŽ›¤¬²¼ÇÈËÌÑÚÛÝÞçðñóô   !&()*38:;<EKMNOX`bcdmvwx‚„…”§¨©ª³´H½OrderinglsvpblobRbplist00Ø E $XiconSize_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumn_useRelativeDates_viewOptionsVersion#@0 Ù #(,16;@Xcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameÔ UindexUwidthYascendingWvisible, Ô ÈÔ$% µ Ô)%Ô-. a Ô23 d Ô78 s Ô<= K ÔA  #@( "4<PYdwŒ•–©²ÁÎÚßåêò÷  #$%.0234=?ABCLNOPY[]^_hjlmnwy{|}†ˆŠ‹Œ•—˜™š£G¤mn_useRelativeDates_viewOptionsVersion#@0 « #(-27;@Ô   WvisibleUwidthYascendingZidentifier , TnameÔ Xubiquity#Ô   µ\dateModifiedÔ "[dateCreatedÔ  %' aTsizeÔ  * , s TkindÔ / 1d UlabelÔ 4 6K WversionÔ  : XcommentsÔ =?È^dateLastOpenedÔ AYdateAdded#@( "4<PYdwŒ•–¢«³¹ÃÎÏÒÓØáêìíî÷øúû()+,1:;=>CLMOPV_`bcktuvˆ‰‹Œ›¤®¯°±º»H½ÿÿÿÿÿÿ graph500-1.2Ilocblob…ÿÿÿÿÿÿ graph500-1.2bwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_CoP E H 0 @ DSDB `€X`€ @€ @wy{|}†ˆŠ‹Œ•—˜™š£G¤mn_useRelativeDates_viewOptionsVersion#@0 « #(-27;@Ô   WvisibleUwidthYascendingZidentifier , TnameÔ Xubiquity#Ô   µ\dateModifiedÔ "[dateCreatedÔ  %' aTsizeÔ  * , s TkindÔ / 1d UlabelÔ 4 6K WversionÔ  : XcommentsÔ =?È^dateLastOpenedÔ AYdateAdded#@( "4<PYdwŒ•–¢«³¹ÃÎÏÒÓØáêìíî÷øúû()+,1:;=>CLMOPV_`bcktuvˆ‰‹Œ›¤®¯°±º»H½ÿÿÿÿÿÿ graph500-1.2Ilocblob…ÿÿÿÿÿÿ graph500-1.2bwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_CoCombBLAS_beta_16_2/BipartiteMatchings/000755 000765 000024 00000000000 13271513706 021212 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/._TODO.txt000644 000765 000024 00000000260 13212627400 017142 0ustar00aydinbulucstaff000000 000000 Mac OS X  2~°ATTR°  com.apple.lastuseddate#PSÑ•æZ’µ¿CombBLAS_beta_16_2/TODO.txt000644 000765 000024 00000000421 13212627400 016724 0ustar00aydinbulucstaff000000 000000 - Implement move constructors for FullyDistSpVec and FullyDistVec (like the old stealFrom functions) - Name Sparse SpMV "SpMSpV" - Name BFSFriends versions to SpMV_NoSR(...) and SpMSpV_NoSR(...) - Implement SpMM and SpMV using CSB - Provide conversion to/from CSB and DCSC CombBLAS_beta_16_2/._LICENSE000644 000765 000024 00000000600 13271410421 016635 0ustar00aydinbulucstaff000000 000000 Mac OS X  2N€ATTR€xcom.apple.TextEncodingcom.apple.lastuseddate#PS'Y7com.apple.metadata:kMDLabel_6jssn2uemdvwrbg7n2jkvibysuutf-8;134217984–æZbaÅòj Åqë)UfÁîЉ[§EÛÓðó°9öÀ λ£Ð`~ö·ˆµÄŠSÝBÉÙ]ŽºCombBLAS_beta_16_2/LICENSE000644 000765 000024 00000005121 13271410421 016423 0ustar00aydinbulucstaff000000 000000 **************************** *** License Agreement *** Combinatorial BLAS, Copyright (c) 2018, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy) and University of California, Santa Barbara. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: (1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. (2) 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. (3) Neither the name of the University of California, Lawrence Berkeley National Laboratory, University of California, Santa Barbara, U.S. Dept. of Energy nor the names of its 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. You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to Lawrence Berkeley National Laboratory, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a non-exclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such enhancements or derivative works thereof, in binary and source code form. ---------------------------------------------------------------CombBLAS_beta_16_2/cmake/000755 000765 000024 00000000000 13271515107 016506 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/psort-1.0/000755 000765 000024 00000000000 13271513305 017067 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/3DSpGEMM/000755 000765 000024 00000000000 13271513665 016654 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/include/000755 000765 000024 00000000000 13271405603 017050 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/README_DEVELOPERS000644 000765 000024 00000003103 13212627400 020026 0ustar00aydinbulucstaff000000 000000 To Developers: Whenever you introduce a new instantiation of the existing types, such as SpParMat< NEWINDEXTYPE, NEWVALUETYPE, NEWLOCALMATRIX>, you should create three traits: 1) Inside promote.h, tell the compiler how to determine the return type of binop(bool,NEWVALUETYPE) where binop is usually a user defined multiplication. 2) You should do a similar promotion for your NEWLOCALMATRIX, as done at the end of SpDCCols.h because the compiler can not deduce your NEWLOCALMATRIX's promotion from the promotion information of its template parameters. At this point, you should declare promotions for each possible instantiation of your NEWLOCALMATRIX object. Here is an example: template <> struct promote_trait< NEWLOCALMATRIX , NEWLOCALMATRIX > { typedef NEWLOCALMATRIX T_promote; }; 3) You should add a mechanism for the compiler to infer the types of template parameters in your NEWLOCALMATRIX. This allows the compiler to instantiate a concrete NEWLOCALMATRIX<> object with the promoted type parameters. template struct create_trait< NEWLOCALMATRIX , NIT, NNT > { typedef NEWLOCALMATRIX T_inferred; typedef SpTuples TUP_inferred; // required for internals }; For all three, you can use the existing traits as examples. To Users: Please avoid declaring vectors (FullyDistVec, FullyDistSpVec, etc) with NT=bool. Parallel vectors uses vector internally, which breaks with bool. You are more than welcome (and encouraged) to declare matrices (SpParMat) with NT=bool. CombBLAS_beta_16_2/ms_inttypes/000755 000765 000024 00000000000 13212627400 017777 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/._NERSC_INSTALL.html000644 000765 000024 00000000273 13271533754 020603 0ustar00aydinbulucstaff000000 000000 Mac OS X  2‰»TEXTMSIEATTR»˜#˜#com.apple.quarantineq/0082;5ae6b7cb;Microsoft\x20Word;CombBLAS_beta_16_2/NERSC_INSTALL.html000644 000765 000024 00000260564 13271533754 020401 0ustar00aydinbulucstaff000000 000000 ÿþ<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns:mv="http://macVmlSchemaUri" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta name=Title content=""> <meta name=Keywords content=""> <meta http-equiv=Content-Type content="text/html; charset=unicode"> <meta name=ProgId content=Word.Document> <meta name=Generator content="Microsoft Word 15"> <meta name=Originator content="Microsoft Word 15"> <link rel=File-List href="NERSC_INSTALL.fld/filelist.xml"> <!--[if gte mso 9]><xml> <o:DocumentProperties> <o:Author>Aydin Buluc</o:Author> <o:LastAuthor>Aydin Buluc</o:LastAuthor> <o:Revision>6</o:Revision> <o:TotalTime>10</o:TotalTime> <o:Created>2017-12-26T04:13:00Z</o:Created> <o:LastSaved>2018-04-30T06:29:00Z</o:LastSaved> <o:Pages>1</o:Pages> <o:Words>147</o:Words> <o:Characters>841</o:Characters> <o:Lines>7</o:Lines> <o:Paragraphs>1</o:Paragraphs> <o:CharactersWithSpaces>987</o:CharactersWithSpaces> <o:Version>15.0</o:Version> </o:DocumentProperties> <o:OfficeDocumentSettings> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]--> <link rel=themeData href="NERSC_INSTALL.fld/themedata.thmx"> <!--[if gte mso 9]><xml> <w:WordDocument> <w:Zoom>150</w:Zoom> <w:SpellingState>Clean</w:SpellingState> <w:GrammarState>Clean</w:GrammarState> <w:TrackMoves>false</w:TrackMoves> <w:TrackFormatting/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>EN-US</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:SplitPgBreakAndParaMark/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="&#45;-"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="false" DefSemiHidden="false" DefQFormat="false" DefPriority="99" LatentStyleCount="382"> <w:LsdException Locked="false" Priority="0" QFormat="true" Name="Normal"/> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 1"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 2"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 3"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 4"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 5"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 6"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 7"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 8"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 9"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 1"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 2"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 3"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 4"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 5"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 6"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 7"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 8"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="header"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footer"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index heading"/> <w:LsdException Locked="false" Priority="35" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="caption"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="table of figures"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="envelope address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="envelope return"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="line number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="page number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="endnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="endnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="table of authorities"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="macro"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="toa heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 5"/> <w:LsdException Locked="false" Priority="10" QFormat="true" Name="Title"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Closing"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Signature"/> <w:LsdException Locked="false" Priority="1" SemiHidden="true" UnhideWhenUsed="true" Name="Default Paragraph Font"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Message Header"/> <w:LsdException Locked="false" Priority="11" QFormat="true" Name="Subtitle"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Salutation"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Date"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text First Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text First Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Block Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Hyperlink"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="FollowedHyperlink"/> <w:LsdException Locked="false" Priority="22" QFormat="true" Name="Strong"/> <w:LsdException Locked="false" Priority="20" QFormat="true" Name="Emphasis"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Document Map"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Plain Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="E-mail Signature"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Top of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Bottom of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal (Web)"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Acronym"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Cite"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Code"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Definition"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Keyboard"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Preformatted"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Sample"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Typewriter"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Variable"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal Table"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation subject"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="No List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Contemporary"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Elegant"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Professional"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Subtle 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Subtle 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Balloon Text"/> <w:LsdException Locked="false" Priority="39" Name="Table Grid"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Theme"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 9"/> <w:LsdException Locked="false" SemiHidden="true" Name="Placeholder Text"/> <w:LsdException Locked="false" Priority="1" QFormat="true" Name="No Spacing"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading"/> <w:LsdException Locked="false" Priority="61" Name="Light List"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 1"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 1"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 1"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 1"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 1"/> <w:LsdException Locked="false" SemiHidden="true" Name="Revision"/> <w:LsdException Locked="false" Priority="34" QFormat="true" Name="List Paragraph"/> <w:LsdException Locked="false" Priority="29" QFormat="true" Name="Quote"/> <w:LsdException Locked="false" Priority="30" QFormat="true" Name="Intense Quote"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 1"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 1"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 1"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 1"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 1"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 1"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 1"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 2"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 2"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 2"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 2"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 2"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 2"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 2"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 2"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 2"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 2"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 2"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 3"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 3"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 3"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 3"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 3"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 3"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 3"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 3"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 3"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 3"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 3"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 3"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 3"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 4"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 4"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 4"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 4"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 4"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 4"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 4"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 4"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 4"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 4"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 4"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 4"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 4"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 4"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 5"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 5"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 5"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 5"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 5"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 5"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 5"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 5"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 5"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 5"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 5"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 5"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 5"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 5"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 6"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 6"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 6"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 6"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 6"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 6"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 6"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 6"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 6"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 6"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 6"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 6"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 6"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 6"/> <w:LsdException Locked="false" Priority="19" QFormat="true" Name="Subtle Emphasis"/> <w:LsdException Locked="false" Priority="21" QFormat="true" Name="Intense Emphasis"/> <w:LsdException Locked="false" Priority="31" QFormat="true" Name="Subtle Reference"/> <w:LsdException Locked="false" Priority="32" QFormat="true" Name="Intense Reference"/> <w:LsdException Locked="false" Priority="33" QFormat="true" Name="Book Title"/> <w:LsdException Locked="false" Priority="37" SemiHidden="true" UnhideWhenUsed="true" Name="Bibliography"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="TOC Heading"/> <w:LsdException Locked="false" Priority="41" Name="Plain Table 1"/> <w:LsdException Locked="false" Priority="42" Name="Plain Table 2"/> <w:LsdException Locked="false" Priority="43" Name="Plain Table 3"/> <w:LsdException Locked="false" Priority="44" Name="Plain Table 4"/> <w:LsdException Locked="false" Priority="45" Name="Plain Table 5"/> <w:LsdException Locked="false" Priority="40" Name="Grid Table Light"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Mention"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Smart Hyperlink"/> </w:LatentStyles> </xml><![endif]--> <style> <!--p.P1 {-webkit-text-stroke: #000000;} p.P2 {-webkit-text-stroke: #000000; min-height: 18.0px;} span.S1 {font-kerning: none;} span.S2 {font-kerning: none; -webkit-text-stroke: 0px #0000ee;} /* Font Definitions */ @font-face {font-family:Times; panose-1:2 0 5 0 0 0 0 0 0 0; mso-font-charset:0; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:0; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:-536870145 1107305727 0 0 415 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast;} a:link, span.MsoHyperlink {mso-style-noshow:yes; mso-style-priority:99; color:blue; text-decoration:underline; text-underline:single;} a:visited, span.MsoHyperlinkFollowed {mso-style-noshow:yes; mso-style-priority:99; color:purple; text-decoration:underline; text-underline:single;} p.p1, li.p1, div.p1 {mso-style-name:p1; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; color:black;} p.p2, li.p2, div.p2 {mso-style-name:p2; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; color:black;} span.s2 {mso-style-name:s2; mso-style-unhide:no; color:#0000EE; text-decoration:underline; text-underline:single;} span.SpellE {mso-style-name:""; mso-spl-e:yes;} span.GramE {mso-style-name:""; mso-gram-e:yes;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:10.0pt; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt;} @page WordSection1 {size:8.5in 11.0in; margin:1.0in 1.0in 1.0in 1.0in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:"Table Normal"; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-parent:""; mso-padding-alt:0in 5.4pt 0in 5.4pt; mso-para-margin:0in; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman";} </style> <![endif]--> <meta http-equiv=Content-Style-Type content="text/css"> <meta name=CocoaVersion content=1348.17> <!--[if gte mso 9]><xml> <o:shapedefaults v:ext="edit" spidmax="1026"/> </xml><![endif]--><!--[if gte mso 9]><xml> <o:shapelayout v:ext="edit"> <o:idmap v:ext="edit" data="1"/> </o:shapelayout></xml><![endif]--> </head> <body bgcolor=white lang=EN-US link=blue vlink=purple style='tab-interval:.5in'> <div class=WordSection1> <p class=p1><span class=s1><span style='font-family:Times'>Combinatorial BLAS compilation instructions for <a href="http://www.nersc.gov/"><span class=s2>NERSC</span></a> resources.</span></span><span class=apple-converted-space><span style='font-family:Times'>&nbsp;</span></span></p> <p class=p2><o:p>&nbsp;</o:p></p> <p class=p1><span class=s1><span style='font-family:Times'>1- First and foremost, make <span class=SpellE>cmake</span> active by running: <i>module load <span class=SpellE>cmake</span></i></span></span><span class=apple-converted-space><i><span style='font-family:Times'>&nbsp;</span></i></span></p> <p class=p1><span class=s1><span style='font-family:Times'>2- module swap <span class=SpellE>PrgEnv</span>-intel <span class=SpellE>PrgEnv</span>-gnu</span></span><span class=apple-converted-space><span style='font-family:Times'>&nbsp;<o:p></o:p></span></span></p> <p class=p1><span class=apple-converted-space><span style='font-family:Times'>3- <span class=SpellE>mkdir</span> _build<o:p></o:p></span></span></p> <p class=p1><span class=apple-converted-space><span style='font-family:Times'>4- <span class=SpellE>mkdir</span> _install<o:p></o:p></span></span></p> <p class=p1><span class=apple-converted-space><span style='font-family:Times'>5- cd _build<o:p></o:p></span></span></p> <p class=p1><span class=apple-converted-space><span style='font-family:Times'>6- <span class=SpellE>cmake</span><span class=GramE> ..</span> -DCMAKE_INSTALL_PREFIX<span class=GramE>=..</span>/_install -DMPIEXEC_NUMPROC_FLAG=-n<o:p></o:p></span></span></p> <p class=p1>7- make</p> <p class=p1>8- make install</p> <p class=p2><o:p>&nbsp;</o:p></p> <p class=p1><span class=s1><span style='font-family:Times'>For tests, you might want to run them through the interactive batch system. But first download the <span class=SpellE>testdata</span> <a href="http://eecs.berkeley.edu/~aydin/CombBLAS_FILES/testdata_combblas1.6.1.tgz">here</a> and extract it inside the _build directory. Then execute:</span></span></p> <p class=p1><span class=s1><span style='font-family:Times'>&gt; &quot;<span class=SpellE>salloc</span> -N 4 -p debug -t 00:30:00&quot; //</span></span><span class=apple-converted-space><span style='font-family:Times'>&nbsp;reserve 4 nodes, 96 cores of Edison<o:p></o:p></span></span></p> <p class=p1><span class=apple-converted-space><span style='font-family:Times'>&gt; </span></span><span class=s1><span style='font-family:Times'>&quot;</span></span><span class=apple-converted-space><span style='font-family:Times;mso-fareast-font-family: Helvetica;mso-bidi-font-family:Helvetica'>export OMP_</span></span><span class=apple-converted-space><span style='font-family:Times'>PROC_BIND=true</span></span><span class=s1><span style='font-family:Times'>&quot;</span></span><span class=apple-converted-space><span style='font-family:Times'><o:p></o:p></span></span></p> <p class=p1><span class=apple-converted-space><span style='font-family:Times'>&gt; </span></span><span class=s1><span style='font-family:Times'>&quot;</span></span><span class=apple-converted-space><span style='font-family:Times;mso-fareast-font-family: Helvetica;mso-bidi-font-family:Helvetica'>export OMP_PLACES=threads</span></span><span class=s1><span style='font-family:Times'>&quot;</span></span><span class=apple-converted-space><span style='font-family:Times'><o:p></o:p></span></span></p> <p class=p1><span class=apple-converted-space><span style='font-family:Times'>&gt; </span></span><span class=s1><span style='font-family:Times'>&quot;</span></span><span class=apple-converted-space><span style='font-family:Times;mso-fareast-font-family: Helvetica;mso-bidi-font-family:Helvetica'>export OMP_NUM_THREADS=12</span></span><span class=s1><span style='font-family:Times'>&quot;</span></span></p> <p class=p1><span class=s1><span style='font-family:Times'>&gt; &quot;<span class=SpellE>ctest</span> -V&quot;</span></span><span class=apple-converted-space><span style='font-family:Times'>&nbsp;</span></span></p> <p class=p1><span class=s1><span style='font-family:Times'>&gt; or just run a single test: &quot;cd Applications; <span class=SpellE>srun</span> -n 16 -c <span class=GramE>12 .</span>/<span class=SpellE>dobfs</span> 23&quot;</span></span><span class=apple-converted-space><span style='font-family:Times'>&nbsp; // run using 4 MPI tasks for node, 12 threads per MPI task<o:p></o:p></span></span></p> <p class=p1><o:p>&nbsp;</o:p></p> <p class=p1><span class=s1><span style='font-family:Times'>Contact abuluc@lbl.gov for questions/problems.</span></span><span class=apple-converted-space><span style='font-family:Times'>&nbsp;</span></span></p> </div> </body> </html> CombBLAS_beta_16_2/Ordering/000755 000765 000024 00000000000 13271513341 017175 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/._MainPage.h000644 000765 000024 00000000515 13271443130 017472 0ustar00aydinbulucstaff000000 000000 Mac OS X  2MATTRMäiäcom.apple.lastuseddate#PSôY7com.apple.metadata:kMDLabel_6jssn2uemdvwrbg7n2jkvibysuFæZÿÝ›!ò?ÐR•"Ãe3Ö]3å½zZÄieÿ>¨ "Cå 9]‹#?Œgz§-:¯æÎ%¤°gÿ8ùüBt¤Œ1é”Ù\GZ¯2­ ”ºKüÃÒ h UCombBLAS_beta_16_2/MainPage.h000644 000765 000024 00000044300 13271443130 017255 0ustar00aydinbulucstaff000000 000000 /** @mainpage The Combinatorial BLAS Library * * @authors Ariful Azad , Aydın Buluç , and John R. Gilbert (with contributions from Adam Lugowski, Scott Beamer and Tristan Konolige). * * @copyright * Combinatorial BLAS, Copyright (c) 2018, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy) and University of California, Santa Barbara. All rights reserved. If you have questions about your rights to use or distribute this software, please contact Berkeley Lab's Innovation & Partnerships Office at IPO@lbl.gov. NOTICE. This Software was developed under funding from the U.S. Department of Energy and the U.S. Government consequently retains certain rights. As such, the U.S. Government has been granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, distribute copies to the public, prepare derivative works, and perform publicly and display publicly, and to permit other to do so. This material is based upon work supported by the National Science Foundation under Grant No. 0709385 and by the Department of Energy, Office of Science, ASCR Contract No. DE-AC02-05CH11231. Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation (NSF) and the Department of Energy (DOE). This software is released under the following license. * * * @section intro Introduction * The Combinatorial BLAS (CombBLAS) is an extensible distributed-memory parallel graph library offering a small but powerful set of linear * algebra primitives specifically targeting graph analytics. * - The Combinatorial BLAS development influences the Graph BLAS standardization process. * - It achieves scalability via its two dimensional distribution and coarse-grained parallelism. * - For an illustrative overview, check out these slides. * - CombBLAS powers HipMCL, a highly-scalable parallel implementation of the Markov Cluster Algorithm (MCL). * - Operations among sparse matrices and vectors use arbitrary user defined semirings. Here is a semiring primer * - Check out the Frequently asked questions about CombBLAS. * * Download * - Read release notes. * - The latest CMake'd tarball (version 1.6.2, April 2018) here. (NERSC users read this). The previous version (version 1.6.1, Jan 2018) is also available here for backwards compatibility and benchmarking. * - Installation and testing can be done by executing these commands within the CombBLAS directory: * - mkdir _build * - mkdir _install * - cd _build * - cmake .. -DCMAKE_INSTALL_PREFIX=../_install * - make * - make install * - ctest -V (you need the testinputs, see below) * - Test inputs are separately downloadable here. Extract them inside the _build directory you've just created with the command "tar -xzvf testdata_combblas1.6.1.tgz" * - Alternatively (if cmake fails, or you just don't want to install it), you can just imitate the sample makefiles inside the ReleaseTests and Applications * directories. Those sample makefiles have the following format: makefile-machine. (example: makefile-macair) * - The CMake now automatically compiles for hybrid MPI+OpenMP mode because almost all expensive primitives are now multithreaded. Example makefiles are also multithreaded for many cases. You just need to make sure that your OMP_NUM_THREADS environmental variable is set to the right value for the configuration you are running and you are not oversubscribing or undersubscribing cores. * - At this point, you can incorporate CombBLAS into your own code by linking against the contents of the _install/lib directory and including the header _install/include/CombBLAS/CombBLAS.h. If you need an example, SuperLU_Dist does that * * - While we do not recommend using the code from our development repository, you can certainly issue pull requests there * * Requirements: You need a recent * C++ compiler (gcc version 4.8+, Intel version 15.0+ and compatible), a compliant MPI implementation, and C++11 Standard library (libstdc++ that comes with g++ * has them). The recommended tarball uses the CMake build system, but only to build the documentation and unit-tests, and to automate installation. The chances are that you're not going to use any of our sample applications "as-is", so you can just modify them or imitate their structure to write your own application by just using the header files. There are very few binary libraries to link to, and no configured header files. Like many high-performance C++ libraries, the Combinatorial BLAS is mostly templated. * * Documentation: * This is a beta implementation of the Combinatorial BLAS Library in written in C++ with MPI and OpenMP for parallelism. * It is purposefully designed for distributed memory platforms though it also runs in uniprocessor and shared-memory (such as multicores) platforms. * It contains efficient implementations of novel data structures/algorithms * as well as reimplementations of some previously known data structures/algorithms for convenience. More details can be found in the accompanying paper [1]. One of * the distinguishing features of the Combinatorial BLAS is its decoupling of parallel logic from the * sequential parts of the computation, making it possible to implement new formats and plug them in * without changing the rest of the library. * * For I/O purposes, the implementation supports both a tuples format very similar to the Matrix Market and the Matrix Market format itself. We recommend using the Matrix Market version and associated ParallelReadMM() functions. * We encourage in-memory generators for faster benchmarking. * * The main data structure is a distributed sparse matrix ( SpParMat ) which HAS-A sequential sparse matrix ( SpMat ) that * can be implemented in various ways as long as it supports the interface of the base class (currently: SpTuples, SpCCols, SpDCCols). * * For example, the standard way to declare a parallel sparse matrix A that uses 32-bit integers for indices, floats for numerical values (nonzeros), * SpDCCols for the underlying sequential matrix operations is: * - SpParMat > A; * * * Sparse and dense vectors are distributed along all processors. This is very space efficient and provides * good load balance for SpMSV (sparse matrix-sparse vector multiplication). * * New in version 1.6: * - In-node multithreading enabled for many expensive operations. * - Fully parallel text-file reader for vectors (FullyDistSpVec::ParallelReadMM() and FullyDistVec::ParallelReadMM()) * - Fully parallel text-file writer for vectors (FullyDistSpVec::ParallelWrite () and FullyDistVec::ParallelWrite()) * - Reverse Cuthill-McKee (RCM) ordering implementation. Please cite [12] if you use this implementation * - Novel multithreaded SpGEMM and SpMV (with sparse vectors) algorithms are integrated with the rest of CombBLAS. * - For benchmarking multithreaded SpMV with sparse vectors, go to Applications/SpMSpV-IPDPS2017 directory and use the code there. * - Please cite [13] if you use the new multithreaded SpMV with sparse vectors. * - Extended CSC support * - Previously deprecated SpParVec and DenseParVec (that were distributed to diagonal processors only) classes are removed. * - Lots of more bug fixes * * New in version 1.5: * - Fully parallel matrix market format reader (SpParMat::ParallelReadMM()) * - Complete multithreading support, including SpGEMM (previously it was solely SpMV), enabled by -DTHREADED during compilation * - Experimental 3D SpGEMM (the ability to switch processor grids from 2D to 3D will have to wait for version 1.6) * - Cite [9] if you use this implementation * - cd 3DSpGEMM/, make test_mpipspgemm, and call the executable with correct parameters * - Maximal and Maximum cardinality matching algorithms on bipartite graphs * - Cite [10] for maximal cardinality and [11] for maximum cardinality matching * - cd MaximumMatching, make bpmm, and call the executable with correct parameters * - Automated MPI_Op creation from simple C++ function objects (simplifies semiring descriptions and Reduce() functions) * - FullyDistSpVec::Invert() to map from/to (integer) values to/from indices * - Many more helper functions * - Experimental CSC support for low concurrencies * - Lots of bug fixes * * New in version 1.4: * - Direction optimizing breadth-first search in distributed memory (in collaboration with Scott Beamer and GAP). Please cite [8] if you use this code in your research or benchmarks (DirOptBFS.cpp). * * * The supported operations (a growing list) are: * - Sparse matrix-matrix multiplication on a semiring SR: PSpGEMM() * - Elementwise multiplication of sparse matrices (A .* B and A .* not(B) in Matlab): EWiseMult() * - Unary operations on nonzeros: SpParMat::Apply() * - Matrix-matrix and matrix-vector scaling (the latter scales each row/column with the same scalar of the vector) * - Reductions along row/column: SpParMat::Reduce() * - Sparse matrix-dense vector multiplication on a semiring, SpMV() * - Sparse matrix-sparse vector multiplication on a semiring, SpMV() * - Generalized matrix indexing: SpParMat::operator(const FullyDistVec & ri, const FullyDistVec & ci) * - Generalized sparse matrix assignment: SpParMat::SpAsgn (const FullyDistVec & ri, const FullyDistVec &ci, SpParMat & B) * - Numeric type conversion through conversion operators * - Elementwise operations between sparse and dense matrices: SpParMat::EWiseScale() and operator+=() * - BFS specific optimizations inside BFSFriends.h * * All the binary operations can be performed on matrices with different numerical value representations. * The type-traits mechanism will take care of the automatic type promotion, and automatic MPI data type determination. * * Some features it uses: * - templates (for generic types, and for metaprogramming through "curiously recurring template pattern") * - operator overloading * - compositors (to avoid intermediate copying) * - standard library whenever possible * - Reference counting using shared_ptr for IITO (implemented in terms of) relationships * - As external code, it utilizes * - sequence heaps of Peter Sanders . * - a modified (memory efficient) version of the Viral Shah's PSort . * - a modified version of the R-MAT generator from Graph 500 reference implementation * * Important Sequential classes: * - SpTuples : uses triples format to store matrices, mostly used for input/output and intermediate tasks (such as sorting) * - SpDCCols : implements Alg 1B and Alg 2 [2], holds DCSC. * - SpCCols : implements CSC * Important Parallel classes: * - SpParMat : distributed memory MPI implementation \n Each processor locally stores its submatrix (block) as a sequential SpDCCols object \n Uses a polyalgorithm for SpGEMM: For most systems this boils down to a BSP like Sparse SUMMA [3] algorithm. * - FullyDistVec : dense vector distributed to all processors * - FullyDistSpVec: : sparse vector distributed to all processors * * * Applications implemented using Combinatorial BLAS: * - BetwCent.cpp : Betweenness centrality computation on directed, unweighted graphs. Download sample input here . * - TopDownBFS.cpp: A conformant implementation of the Graph 500 benchmark using the traditional top-down BFS. * - DirOptBFS.cpp: A conformant implementation of the Graph 500 benchmark using the faster direction-optimizing BFS. * - FilteredMIS.cpp: Filtered maximal independent set calculation on ER graphs using Luby's algorithm. * - FilteredBFS.cpp: Filtered breadth-first search on a twitter-like data set. * - BipartiteMatchings/BPMaximalMatching.cpp: Maximal matching algorithms on bipartite graphs [10] * - BipartiteMatchings/BPMaximumMatching.cpp: Maximum matching algorithm on bipartite graphs [11] * - Ordering/RCM.cpp: Reverse Cuthill-McKee ordering on distributed memory [12] * * Performance results of the first two applications can be found in the design paper [1]; Graph 500 results are in a recent BFS paper [4]. The most recent sparse matrix indexing, assignment, and multiplication results can be found in [5]. Performance of filtered graph algorithms (BFS and MIS) are reported in [7]. Performance of the 3D SpGEMM algorithm can be found in [9] * * A subset of test programs demonstrating how to use the library (under ReleaseTests): * - TransposeTest.cpp : File I/O and parallel transpose tests * - MultTiming.cpp : Parallel SpGEMM tests * - IndexingTest.cpp: Various sparse matrix indexing usages * - SpAsgnTiming.cpp: Sparse matrix assignment usage and timing. * - FindSparse.cpp : Parallel find/sparse routines akin to Matlab's. * - GalerkinNew.cpp : Graph contraction or restriction operator (used in Algebraic Multigrid). * * Citation: Please cite the design paper [1] if you end up using the Combinatorial BLAS in your research. * * - [1] Aydın Buluç and John R. Gilbert, The Combinatorial BLAS: Design, implementation, and applications . International Journal of High Performance Computing Applications (IJHPCA), 2011. Preprint , Link * - [2] Aydın Buluç and John R. Gilbert, On the Representation and Multiplication of Hypersparse Matrices . The 22nd IEEE International Parallel and Distributed Processing Symposium (IPDPS 2008), Miami, FL, April 14-18, 2008 * - [3] Aydın Buluç and John R. Gilbert, Challenges and Advances in Parallel Sparse Matrix-Matrix Multiplication . The 37th International Conference on Parallel Processing (ICPP 2008), Portland, Oregon, USA, 2008 * - [4] Aydın Buluç and Kamesh Madduri, Parallel Breadth-First Search on Distributed-Memory Systems . Supercomputing (SC'11), Seattle, USA. Extended preprint PDF * - [5] Aydın Buluç and John R. Gilbert. Parallel Sparse Matrix-Matrix Multiplication and Indexing: Implementation and Experiments . SIAM Journal of Scientific Computing, 2012. Preprint PDF * - [6] Aydın Buluç. Linear Algebraic Primitives for Computation on Large Graphs . PhD thesis, University of California, Santa Barbara, 2010. PDF * - [7] Aydın Buluç, Erika Duriakova, Armando Fox, John Gilbert, Shoaib Kamil, Adam Lugowski, Leonid Oliker, Samuel Williams. High-Productivity and High-Performance Analysis of Filtered Semantic Graphs , International Parallel and Distributed Processing Symposium (IPDPS), 2013. PDF * - [8] Scott Beamer, Aydin Buluç, Krste Asanović, and David Patterson. Distributed memory breadth-first search revisited: Enabling bottom-up search. In Workshop on Multithreaded Architectures and Applications (MTAAP), in conjunction with IPDPS. IEEE Computer Society, 2013. PDF * - [9] Ariful Azad, Grey Ballard, Aydin Buluç, James Demmel, Laura Grigori, Oded Schwartz, Sivan Toledo, and Samuel Williams. Exploiting multiple levels of parallelism in sparse matrix-matrix multiplication. SIAM Journal on Scientific Computing (SISC), 38(6):C624--C651, 2016. PDF * - [10] Ariful Azad and Aydin Buluç. Distributed-memory algorithms for maximal cardinality matching using matrix algebra. In IEEE International Conference on Cluster Computing (CLUSTER), 2015. PDF * - [11] Ariful Azad and Aydin Buluc. Distributed-memory algorithms for maximum cardinality matching in bipartite graphs. In Proceedings of the IPDPS, 2016. PDF * - [12] Ariful Azad, Mathias Jacquelin, Aydin Buluç, and Esmond G. Ng. The reverse Cuthill-McKee algorithm in distributed-memory. In Proceedings of the IPDPS, 2017. PDF * - [13] Ariful Azad and Aydin Buluç. A work-efficient parallel sparse matrix-sparse vector multiplication algorithm. In Proceedings of the IPDPS, 2017. PDF */ CombBLAS_beta_16_2/graph500-1.2/000755 000765 000024 00000000000 13271513354 017254 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/usort/000755 000765 000024 00000000000 13271404146 016602 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/Applications/000755 000765 000024 00000000000 13271515277 020064 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/._FAQ-combblas.html000644 000765 000024 00000000273 13212627400 020715 0ustar00aydinbulucstaff000000 000000 Mac OS X  2‰»ATTR»˜#˜#com.apple.quarantineq/0082;5a2b3ff4;Microsoft\x20Word;CombBLAS_beta_16_2/FAQ-combblas.html000644 000765 000024 00000371070 13212627400 020506 0ustar00aydinbulucstaff000000 000000 ÿþ<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns:mv="http://macVmlSchemaUri" xmlns="http://www.w3.org/TR/REC-html40"> <head> <meta name=Title content="The Combinatorial BLAS Release Notes"> <meta name=Keywords content=""> <meta http-equiv=Content-Type content="text/html; charset=unicode"> <meta name=ProgId content=Word.Document> <meta name=Generator content="Microsoft Word 15"> <meta name=Originator content="Microsoft Word 15"> <link rel=File-List href="FAQ-combblas.fld/filelist.xml"> <title>The Combinatorial BLAS Release Notes</title> <!--[if gte mso 9]><xml> <o:DocumentProperties> <o:Author>Aydin Buluc</o:Author> <o:LastAuthor>Aydin Buluc</o:LastAuthor> <o:Revision>6</o:Revision> <o:TotalTime>8</o:TotalTime> <o:Created>2013-11-27T00:49:00Z</o:Created> <o:LastSaved>2017-10-04T06:29:00Z</o:LastSaved> <o:Pages>1</o:Pages> <o:Words>1905</o:Words> <o:Characters>10861</o:Characters> <o:Lines>90</o:Lines> <o:Paragraphs>25</o:Paragraphs> <o:CharactersWithSpaces>12741</o:CharactersWithSpaces> <o:Version>15.0</o:Version> </o:DocumentProperties> <o:OfficeDocumentSettings> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]--> <link rel=themeData href="FAQ-combblas.fld/themedata.thmx"> <!--[if gte mso 9]><xml> <w:WordDocument> <w:TrackMoves>false</w:TrackMoves> <w:TrackFormatting/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>EN-US</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:SplitPgBreakAndParaMark/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="&#45;-"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]--><!--[if gte mso 9]><xml> <w:LatentStyles DefLockedState="false" DefUnhideWhenUsed="false" DefSemiHidden="false" DefQFormat="false" DefPriority="99" LatentStyleCount="382"> <w:LsdException Locked="false" Priority="0" QFormat="true" Name="Normal"/> <w:LsdException Locked="false" Priority="9" QFormat="true" Name="heading 1"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 2"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 3"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 4"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 5"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 6"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 7"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 8"/> <w:LsdException Locked="false" Priority="9" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="heading 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index 9"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 1"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 2"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 3"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 4"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 5"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 6"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 7"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 8"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" Name="toc 9"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="header"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footer"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="index heading"/> <w:LsdException Locked="false" Priority="35" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="caption"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="table of figures"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="envelope address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="envelope return"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="footnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="line number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="page number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="endnote reference"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="endnote text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="table of authorities"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="macro"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="toa heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Bullet 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Number 5"/> <w:LsdException Locked="false" Priority="10" QFormat="true" Name="Title"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Closing"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Signature"/> <w:LsdException Locked="false" Priority="1" SemiHidden="true" UnhideWhenUsed="true" Name="Default Paragraph Font"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="List Continue 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Message Header"/> <w:LsdException Locked="false" Priority="11" QFormat="true" Name="Subtitle"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Salutation"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Date"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text First Indent"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text First Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Heading"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Body Text Indent 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Block Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Hyperlink"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="FollowedHyperlink"/> <w:LsdException Locked="false" Priority="22" QFormat="true" Name="Strong"/> <w:LsdException Locked="false" Priority="20" QFormat="true" Name="Emphasis"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Document Map"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Plain Text"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="E-mail Signature"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Top of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Bottom of Form"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal (Web)"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Acronym"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Address"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Cite"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Code"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Definition"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Keyboard"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Preformatted"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Sample"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Typewriter"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="HTML Variable"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Normal Table"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="annotation subject"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="No List"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Outline List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Simple 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Classic 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Colorful 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Columns 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Grid 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table List 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table 3D effects 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Contemporary"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Elegant"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Professional"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Subtle 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Subtle 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Web 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Balloon Text"/> <w:LsdException Locked="false" Priority="59" Name="Table Grid"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Table Theme"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 1"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 2"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 3"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 4"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 5"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 7"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 8"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Note Level 9"/> <w:LsdException Locked="false" SemiHidden="true" Name="Placeholder Text"/> <w:LsdException Locked="false" Priority="1" QFormat="true" Name="No Spacing"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading"/> <w:LsdException Locked="false" Priority="61" Name="Light List"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 1"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 1"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 1"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 1"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 1"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 1"/> <w:LsdException Locked="false" SemiHidden="true" Name="Revision"/> <w:LsdException Locked="false" Priority="34" QFormat="true" Name="List Paragraph"/> <w:LsdException Locked="false" Priority="29" QFormat="true" Name="Quote"/> <w:LsdException Locked="false" Priority="30" QFormat="true" Name="Intense Quote"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 1"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 1"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 1"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 1"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 1"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 1"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 1"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 1"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 2"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 2"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 2"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 2"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 2"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 2"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 2"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 2"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 2"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 2"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 2"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 2"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 2"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 2"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 3"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 3"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 3"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 3"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 3"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 3"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 3"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 3"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 3"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 3"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 3"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 3"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 3"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 3"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 4"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 4"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 4"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 4"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 4"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 4"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 4"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 4"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 4"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 4"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 4"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 4"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 4"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 4"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 5"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 5"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 5"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 5"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 5"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 5"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 5"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 5"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 5"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 5"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 5"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 5"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 5"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 5"/> <w:LsdException Locked="false" Priority="60" Name="Light Shading Accent 6"/> <w:LsdException Locked="false" Priority="61" Name="Light List Accent 6"/> <w:LsdException Locked="false" Priority="62" Name="Light Grid Accent 6"/> <w:LsdException Locked="false" Priority="63" Name="Medium Shading 1 Accent 6"/> <w:LsdException Locked="false" Priority="64" Name="Medium Shading 2 Accent 6"/> <w:LsdException Locked="false" Priority="65" Name="Medium List 1 Accent 6"/> <w:LsdException Locked="false" Priority="66" Name="Medium List 2 Accent 6"/> <w:LsdException Locked="false" Priority="67" Name="Medium Grid 1 Accent 6"/> <w:LsdException Locked="false" Priority="68" Name="Medium Grid 2 Accent 6"/> <w:LsdException Locked="false" Priority="69" Name="Medium Grid 3 Accent 6"/> <w:LsdException Locked="false" Priority="70" Name="Dark List Accent 6"/> <w:LsdException Locked="false" Priority="71" Name="Colorful Shading Accent 6"/> <w:LsdException Locked="false" Priority="72" Name="Colorful List Accent 6"/> <w:LsdException Locked="false" Priority="73" Name="Colorful Grid Accent 6"/> <w:LsdException Locked="false" Priority="19" QFormat="true" Name="Subtle Emphasis"/> <w:LsdException Locked="false" Priority="21" QFormat="true" Name="Intense Emphasis"/> <w:LsdException Locked="false" Priority="31" QFormat="true" Name="Subtle Reference"/> <w:LsdException Locked="false" Priority="32" QFormat="true" Name="Intense Reference"/> <w:LsdException Locked="false" Priority="33" QFormat="true" Name="Book Title"/> <w:LsdException Locked="false" Priority="37" SemiHidden="true" UnhideWhenUsed="true" Name="Bibliography"/> <w:LsdException Locked="false" Priority="39" SemiHidden="true" UnhideWhenUsed="true" QFormat="true" Name="TOC Heading"/> <w:LsdException Locked="false" Priority="41" Name="Plain Table 1"/> <w:LsdException Locked="false" Priority="42" Name="Plain Table 2"/> <w:LsdException Locked="false" Priority="43" Name="Plain Table 3"/> <w:LsdException Locked="false" Priority="44" Name="Plain Table 4"/> <w:LsdException Locked="false" Priority="45" Name="Plain Table 5"/> <w:LsdException Locked="false" Priority="40" Name="Grid Table Light"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46" Name="Grid Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="Grid Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="Grid Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="Grid Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="Grid Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51" Name="Grid Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52" Name="Grid Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 1"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 1"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 1"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 1"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 1"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 1"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 2"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 2"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 2"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 2"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 2"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 2"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 3"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 3"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 3"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 3"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 3"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 3"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 4"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 4"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 4"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 4"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 4"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 4"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 5"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 5"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 5"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 5"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 5"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 5"/> <w:LsdException Locked="false" Priority="46" Name="List Table 1 Light Accent 6"/> <w:LsdException Locked="false" Priority="47" Name="List Table 2 Accent 6"/> <w:LsdException Locked="false" Priority="48" Name="List Table 3 Accent 6"/> <w:LsdException Locked="false" Priority="49" Name="List Table 4 Accent 6"/> <w:LsdException Locked="false" Priority="50" Name="List Table 5 Dark Accent 6"/> <w:LsdException Locked="false" Priority="51" Name="List Table 6 Colorful Accent 6"/> <w:LsdException Locked="false" Priority="52" Name="List Table 7 Colorful Accent 6"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Mention"/> <w:LsdException Locked="false" SemiHidden="true" UnhideWhenUsed="true" Name="Smart Hyperlink"/> </w:LatentStyles> </xml><![endif]--> <style> <!--p.P3 {min-height: 14.0px;} p.P4 {min-height: 14.0px;} p.P6 {min-height: 15.0px;} p.P8 {min-height: 18.0px;} /* Font Definitions */ @font-face {font-family:Arial; panose-1:2 11 6 4 2 2 2 2 2 4; mso-font-charset:0; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:-536859905 -1073711037 9 0 511 0;} @font-face {font-family:Times; panose-1:2 0 5 0 0 0 0 0 0 0; mso-font-charset:0; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 0 0 0 1 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:1; mso-generic-font-family:roman; mso-font-format:other; mso-font-pitch:variable; mso-font-signature:0 0 0 0 0 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} h1 {mso-style-priority:9; mso-style-unhide:no; mso-style-qformat:yes; mso-style-link:"Heading 1 Char"; mso-margin-top-alt:auto; margin-right:0in; mso-margin-bottom-alt:auto; margin-left:0in; mso-pagination:widow-orphan; mso-outline-level:1; font-size:24.0pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} a:link, span.MsoHyperlink {mso-style-noshow:yes; mso-style-priority:99; color:blue; text-decoration:underline; text-underline:single;} a:visited, span.MsoHyperlinkFollowed {mso-style-noshow:yes; mso-style-priority:99; color:purple; text-decoration:underline; text-underline:single;} span.Heading1Char {mso-style-name:"Heading 1 Char"; mso-style-priority:9; mso-style-unhide:no; mso-style-locked:yes; mso-style-link:"Heading 1"; mso-ansi-font-size:16.0pt; mso-bidi-font-size:16.0pt; font-family:"Calibri Light"; mso-ascii-font-family:"Calibri Light"; mso-ascii-theme-font:major-latin; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:major-fareast; mso-hansi-font-family:"Calibri Light"; mso-hansi-theme-font:major-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:major-bidi; color:#2D4F8E; mso-themecolor:accent1; mso-themeshade:181; font-weight:bold;} p.p2, li.p2, div.p2 {mso-style-name:p2; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:9.0pt; font-family:Arial; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast;} p.p3, li.p3, div.p3 {mso-style-name:p3; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:9.0pt; font-family:Arial; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast;} p.p4, li.p4, div.p4 {mso-style-name:p4; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:9.0pt; font-family:Arial; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; color:#232323;} p.p5, li.p5, div.p5 {mso-style-name:p5; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:9.0pt; font-family:Arial; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; color:#232323;} p.p6, li.p6, div.p6 {mso-style-name:p6; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:Arial; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; color:#232323;} p.p7, li.p7, div.p7 {mso-style-name:p7; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:Arial; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; color:#232323;} p.p8, li.p8, div.p8 {mso-style-name:p8; mso-style-unhide:no; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; font-family:Times; mso-fareast-font-family:"Times New Roman"; mso-fareast-theme-font:minor-fareast; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} span.s1 {mso-style-name:s1; mso-style-unhide:no; color:#042EEE; text-decoration:underline; text-underline:single;} span.s2 {mso-style-name:s2; mso-style-unhide:no; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; font-family:Courier; mso-ascii-font-family:Courier; mso-hansi-font-family:Courier;} span.s3 {mso-style-name:s3; mso-style-unhide:no; color:black;} span.s4 {mso-style-name:s4; mso-style-unhide:no; color:#232323;} span.s5 {mso-style-name:s5; mso-style-unhide:no; color:#3D578C;} span.s6 {mso-style-name:s6; mso-style-unhide:no; color:#0000EE; text-decoration:underline; text-underline:single;} span.s7 {mso-style-name:s7; mso-style-unhide:no; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt; font-family:Arial; mso-ascii-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial;} span.s8 {mso-style-name:s8; mso-style-unhide:no; color:black; background:#FBFCFD;} span.s9 {mso-style-name:s9; mso-style-unhide:no; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; font-family:Courier; mso-ascii-font-family:Courier; mso-hansi-font-family:Courier; color:black; background:#FBFCFD;} span.s10 {mso-style-name:s10; mso-style-unhide:no; mso-ansi-font-size:9.0pt; mso-bidi-font-size:9.0pt; font-family:Courier; mso-ascii-font-family:Courier; mso-hansi-font-family:Courier; color:#4665A2;} span.s11 {mso-style-name:s11; mso-style-unhide:no; background:#FFFECC;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:10.0pt; mso-ansi-font-size:10.0pt; mso-bidi-font-size:10.0pt;} @page WordSection1 {size:8.5in 11.0in; margin:1.0in 1.25in 1.0in 1.25in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} --> </style> <!--[if gte mso 10]> <style> /* Style Definitions */ table.MsoNormalTable {mso-style-name:"Table Normal"; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-parent:""; mso-padding-alt:0in 5.4pt 0in 5.4pt; mso-para-margin:0in; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman";} </style> <![endif]--> <meta http-equiv=Content-Style-Type content="text/css"> <meta name=CocoaVersion content=1187.34> <!--[if gte mso 9]><xml> <o:shapedefaults v:ext="edit" spidmax="1026"/> </xml><![endif]--><!--[if gte mso 9]><xml> <o:shapelayout v:ext="edit"> <o:idmap v:ext="edit" data="1"/> </o:shapelayout></xml><![endif]--> </head> <body bgcolor=white lang=EN-US link=blue vlink=purple style='tab-interval:.5in'> <div class=WordSection1> <h1 style='margin-top:0in;margin-right:0in;margin-bottom:12.0pt;margin-left: 0in'><span style='font-size:18.0pt;mso-fareast-font-family:"Times New Roman"; mso-bidi-font-family:"Times New Roman"'>Frequently Asked Questions about Combinatorial BLAS<o:p></o:p></span></h1> <p class=p2>Go <a href="http://gauss.cs.ucsb.edu/~aydin/CombBLAS/html/index.html"><span class=s1>back</span></a> to the the Combinatorial BLAS home page.</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>Q1: </b>I would like to use your Combinatorial BLAS code for some of my experiments which involve sparse matrix multiplication. However, it is not clear how to write an output of <span class=s2>PSpGEMM(& )</span> function to a file. I've tried to use &quot;put&quot; function of SpParMat, but it outputs part of the matrix that corresponds to particular process and not the whole matrix. Is there a way to do it using your code?</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p2><span class=s3><b>A1:</b> Yes, </span><span class=s2>SpParMat::SaveGathered(& )</span> will create a single file output (albeit slow) sorted with increasing row id's when called like<span class=s2> A.SaveGathered(&quot;product.txt&quot;)</span>. The caveat is that<span class=apple-converted-space><span style='font-family: Arial'>&nbsp; </span></span>&quot;gathered&quot; I/O in human readable form is quite slow due to serialization on large processor counts. It should only be used for debugging, ideally.<span class=apple-converted-space><span style='font-family:Arial'>&nbsp;For vectors, </span></span>we have a much much faster version:<span class=apple-converted-space><span style='font-family:Arial'>&nbsp; </span></span><span class=s2>FullyDistVec::ParallelWrite (& ), </span>which should be used instead. SpParMat will also get a ParallelWrite soon.</p> <p class=p7>----</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q2</b>: Does Combinatorial BLAS support in-node multithreading?<span class=apple-converted-space><span style='font-family:Arial'>&nbsp;</span></span></p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p2><span class=s4><b>A2:</b> </span>Almost all expensive primitives (SpGEMM, SpMV with sparse vectors, SpMV with dense vectors, EWiseMult, Apply, Set) are hybrid multithreaded within a socket. Read this <a href="http://gauss.cs.ucsb.edu/~aydin/CombBLAS_FILES/CombBLASv1.2_threading.txt"><span class=s5>example</span><span class=s6>.</span></a> </p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q3:</b> Reading/writing text files is really slow for my purposes, what can I do?</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p2><span class=s4><b>A3:</b> </span>Starting from version 1.6, we now have extremely fast matrix market (text) file reading, check out SpParMat::ParallelReadMM() and <span class=s2>FullyDistVec::ParallelReadMM()</span></p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q4:</b> Is there a preferred way to prune elements from a SpParMat according to a predicate?</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A4:</b> Yes,<span class=s7><span style='font-size:10.0pt'> </span></span><span class=s2>SpParMat::Prune(& )</span> will do it according to a predicate. An overloaded version of the same function, <span class=s2>SpParMat::Prune(ri,ci)</span> will <span class=s8>prune all entries whose row indices are in </span><span class=s9>ri </span><span class=s8>and column indices are in </span><span class=s9>ci</span></p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q5:</b> I am trying to run CombBLAS on Windows but the MS MPI does not seem to support MPI C++ bindings.</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p2><b>A5: </b>Combinatorial BLAS recently (version 1.3.0) switched to C-API for all its internal MPI calls. After that, we've also compiled CombLAS on a windows machine without problems. However, we recommend using an open source MPI for windows too, such as MPICH-2.</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p2>---</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p5><b>Q6:</b> I would like to use Combinatorial BLAS for some parallel sparse matrix-matrix multiplications. This works quite well, however when I try to assign a m x 1 sparse matrix (actually a vector) to the first column of an existing sparse matrix with SpAsgn I get an error saying: &quot;Matrix is too small to be splitted&quot;. Is this because it's not possible to use SpAsgn on vector-like matrices?</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A6: </b>SpAsgn internally uses a memory efficient <span class=s2>Mult_AnXBn_DoubleBuff</span> as opposed to&nbsp;<span class=s2>Mult_AnXBn_Synch</span>). You might probably go into&nbsp;<a href="http://gauss.cs.ucsb.edu/~aydin/CombBLAS/html/class_sp_par_mat.html#ae510b0084843dc7d7c7d5c6572f5ef12"><span class=s10>SpParMat&lt;IT,NT,DER&gt;::SpAsgn</span></a><span class=s2>(...)</span> and change occuranges of&nbsp;<span class=s2>Mult_AnXBn_DoubleBuff</span> to&nbsp;<span class=s2>Mult_AnXBn_Synch</span>.&nbsp;However, this will likely only solve your problem for the serial case. because ComBBLAS can not effectively 2D decompose an m x 1 matrix: each dimension should ideally be at least sqrt(p).&nbsp;It is much better to represent that vector as a <span class=s2>FullyDistSpVec</span>.</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q7: </b>Starting from a general sparse matrix Z, I want to construct the symmetric matrix M: [[X'X; X'Z];[Z'X; Z'Z]], where X is a vector of 1's. Thus the element at position (1,1) is simply the number of columns of Z, and the vector Z'X contains the sums per column of Z. For now, I have a working code, but it is quite sloppy because I do not find a function for which I can easily increase the dimension of a sparse matrix or even set an element to a specific value. Is there any function in Combinatorial BLAS which can do this?<span class=apple-converted-space><span style='font-family:Arial'>&nbsp;</span></span></p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A7:</b> Not out of the box. You don't want an SpAsgn or any variant of it because it can't grow the matrix. You want some sort of matrix append. How about using Find(& ) and Sparse(& )?&nbsp; The Matlab care of what you want to do is:</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5>X = ones(size(Z,2),1)&nbsp;</p> <p class=p5>M = [X' * X, X' * Z; Z'* X, Z' * Z]</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5>Supporting such a general concatenation efficiently might be hard to add at this point. Instead,<span class=apple-converted-space><span style='font-family:Arial'>&nbsp; </span></span>there is a Concatenate(& ) function for vectors. Armed with Concatenate(& ), find(), and the sparse-like constructor, one can solve your problem. &nbsp;Check out the working example in ReleaseTests/FindSparse.cpp</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q8:</b> Does CombBLAS include the API to perform a symmetric permutation on a matrix, as explained in your <a href="http://gauss.cs.ucsb.edu/~aydin/spgemm_sisc12.pdf"><span class=s6>SISC paper</span></a>?&nbsp;</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A8:</b> Yes it does. Check out the ReleaseTests/IndexingTiming.cpp for an example.</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---<span class=apple-converted-space><span style='font-family:Arial'>&nbsp;</span></span></p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q9:</b> How can I use small test case to see whether the operation on matrix is correct? In other words, how do I print all the information of a matrix with each value in matrix?&nbsp;</p> <p class=p5>I can use PrintInfo to print basic information, but it only gives me number of rows and columns and nnz</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p2><span class=s4><b>A9:</b> Our recommendation is </span>to use <span class=s2>SaveGathered(& ) </span>to dump the whole matrix into a file in triples (matrix market) format. For vectors, we have a much much faster version:<span class=apple-converted-space><span style='font-family:Arial'>&nbsp; </span></span><span class=s2>FullyDistVec::ParallelWrite (& )</span></p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><span class=s3><b>Q10:</b> </span>Does CombBLAS code run on any graph size or there is some limitation on the dimension of the matrix A. I mean should it be a multiple of sqrt(p) where p is total number of processors.&nbsp;</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A10:</b> No, the matrix dimension does not have to be a multiple of sqrt(p) but it should be bigger than sqrt(p). In other words you can have a 5x5 matrix on 4 processors but not on 36 processors. We don't really see the point of using more than |V|^2 processors.</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5>---</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>Q11:</b> My comparison results on real graph inputs revealed something weird. In input loc-gowalla, how can 16 processors time(called time_16) and&nbsp;</p> <p class=p5>64 processors time(called time_64) which time_64*4&lt;time_16 &nbsp;which is more than linear scale?&nbsp;</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A11:</b> The complexity of the parallel algorithm drops as sub-matrices owned by each processor gets sparser. In particular, it is proportional to O(flops x log(ni)) where ni is the size of the intersection of the set of nonzero columns of Aik and nonzero rows of Bkj for A*B. What might happen as p increases is that there is a phase transition that makes ni drop significantly for your input (for p=64, each sub-matrix will have only ~1.2 nonzeros per row or column). More details are in the <a href="http://gauss.cs.ucsb.edu/~aydin/spgemm_sisc12.pdf"><span class=s6>SISC paper</span></a> and the references therein. Hope this makes sense. This is why I don't suggest people use CombBLAS for small p (&lt; 40) because it is not on the top of its game for small number of processors.&nbsp;</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5>---<span class=apple-converted-space><span style='font-family:Arial'>&nbsp;</span></span></p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>Q12:</b> Should the input file have nodes numbered from 1 or it is fine if the nodes are numbered from 0?</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A12:</b> If you're using the human readable matrix market format as your input, then it should be 1-indexed.<span class=apple-converted-space><span style='font-family:Arial'>&nbsp;</span></span></p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q13: </b>I'm wondering for breadth-first-search, under the hood does the matrix-vector multiplication method change based on the sparsity of the frontier vector, or does the underlying matrix-vector multiplication assume the frontier is always sparse?</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A13:</b> Depending on your definition of sparseness, the frontier is almost always sparse. We use the pragmatic definition of &quot;sparse&quot; in the sense that a vector is sparse if it is worth taking advantage of the sparsity in there. I'd guess, for a dense vector assumption to be competitive, it would have to have at least 1/3 of its potential locations nonzero. However, I might be wrong (and you're welcome to prove me wrong). To answer your question more directly, CombBLAS supports both dense and sparse right hand side vectors, but the specific BFS implementation does not adapt.&nbsp;</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>Q14: </b>Could you briefly explain the difference in your implementations of matrix-sparse vector and matrix-dense vector multiply? For example, is the sparse vector case a write-based approach: Every element updates all of its neighbors (from a graph-theoretic standpoint) locations in the output vector; and the dense vector case a read-based approach: Every element reads some value from each of its neighbors and updates its own entry in the resulting vector?</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A14:</b> Sparse matrix-sparse vector is &quot;right hand side vector structure&quot; driven. In y = A*x, for each nonzero x_i, we scale the column A(:,i) with that and merge the scaled sparse columns results into y. The computation boils down into merging sparse columns into one. <span class=s11>Combinatorial</span> <span class=s11>BLAS</span> is a matrix-vector based library, so thinking in terms of updates on single entries is probably not the right abstraction.</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5>Sparse matrix-dense vector is slightly different in the sense that it is driven by the matrix structure; you basically stream the matrix. The correctness of both operations are handled by a SPA-like or heap-like data structure that merges multiple intermediate values contributing to the same output location; no atomics are used.</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---<span class=apple-converted-space><span style='font-family:Arial'>&nbsp;</span></span></p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p5><span class=s3><b>Q15: </b>I </span>would like to get your opinion on how sparse-matrix based implementations compare with more native implementations</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><span class=s3><b>A15: </b></span>Sparse matrix abstraction, like any abstraction, will leave some performance on the table. In particular it is prone to performing extra passes over data or creating extra temporaries (if you've ever programmed in Matlab; this is similar). On the other hand, sparse matrix abstraction gives you &quot;primitives&quot; to implement graph &quot;algorithms&quot; as opposed to the algorithms themselves. For instance, CombBLAS has sparse matrix x sparse vector over a semiring as opposed to BFS, because now using the same primitive one can implement MIS (maximal independent set) too, only by changing the&nbsp;semiring. Or one can perform run time filtering on edges based on the attributes, similarly by changing the semiring functions (therefore extending functionality to semantic graphs). Indeed this is what we've done in our upcoming IPDPS'13 paper.</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p2>---</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p2><b>Q16: </b>Is there an effort to incorporate the bottom-up BFS of Scott Beamer into CombBLAS?</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p2><b>A16: </b>Yes, it is already done. Just use the dobfs executable (made from DirOptBFS.cpp).</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p7>---</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p5><b>Q17: </b>My serial code is faster than CombBLAS on a single core.</p> <p class=p4><o:p>&nbsp;</o:p></p> <p class=p5><b>A17: </b>I believe that. CombBLAS targets &quot;scalability&quot;, not optimizing the single core performance.</p> <p class=p5>&nbsp;</p> <p class=p5>Examples:</p> <p class=p5>- think about the 2D BFS. CombBLAS does not use a CSR like data structure because that is not memory scalable due to problems of <a href="http://gauss.cs.ucsb.edu/publication/hypersparse-ipdps08.pdf"><span class=s6>hypersparsity</span></a> in large concurrencies. Instead CombBLAS&nbsp;opts to use a slower (about 2x around 1000 cores) but memory scalable format called DCSC. &nbsp;</p> <p class=p5>- think about betweenness centrality which uses sparse matrix-matrix multiply. CombBLAS doesn't use the fastest serial algorithm as its subroutine because it doesn't &nbsp;scale to thousands of cores. Instead it uses a outer-product algorithm that is significantly slower for p=1, but scales indefinitely.</p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p7>---<span class=apple-converted-space><span style='font-family:Arial'>&nbsp;</span></span></p> <p class=p6><o:p>&nbsp;</o:p></p> <p class=p2><b>Q18:</b> Looking at the output of your Graph500 application, I noticed a large number of self-edges removed. That s very interesting.</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p2><b>A18: </b>The duplicate edges problem is inherent to the R-MAT generator on large scale, unless some special kind of noise is added. Check here for a great analysis of this phenomenon: <a href="http://arxiv.org/abs/1102.5046"><span class=s6>http://arxiv.org/abs/1102.5046</span></a></p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p2>---</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p2><b>Q19:</b> How are you counting the number of edges traversed in Graph500? Is this still using the original verify.c file provided with the reference version of the Graph500 benchmark and passing in the parent tree?</p> <p class=p3><o:p>&nbsp;</o:p></p> <p class=p5><span class=s3><b>A19: </b></span>It is calculated by summing the degrees of the discovered vertices using <span class=s2>EWiseMult(& )</span> followed by a <span class=s2>Reduce(& )</span>. Degrees are pre-symmetrization (original edges), so we're not over-counting. However, we count self-loops and duplicates as mentioned in the benchmark specs.</p> <p class=p5><o:p>&nbsp;</o:p></p> <p class=p5>---</p> <p class=p5><o:p>&nbsp;</o:p></p> <p class=p5><b style='mso-bidi-font-weight:normal'>Q20:</b> My computations finishes fine but I get an  Attempting to use an MPI routine after finalizing MPICH afterwards.</p> <p class=p5><o:p>&nbsp;</o:p></p> <p class=p5><b style='mso-bidi-font-weight:normal'>A20:</b> To avoid the finalization error, please imitate an example such as MultTest.cpp: <a href="http://gauss.cs.ucsb.edu/~aydin/CombBLAS/html/_mult_test_8cpp_source.html">http://gauss.cs.ucsb.edu/~aydin/CombBLAS/html/_mult_test_8cpp_source.html</a></p> <p class=p5>The curly brackets around the code are intentional. Since distributed objects have MPI related pointers in them, those pointers are released once the destructors are called. In C++ (at least until C++11) there isn t a good way to call the destructor manually, so the destructor is called immediately before the program exists, which is after the MPI_Finalize. Since the MPI related objects are destructed after MPI_Finalize, you see this error. Try the curly brackets approach.</p> <p class=p8><span style='mso-bidi-font-family:"Times New Roman"'><o:p>&nbsp;</o:p></span></p> <p class=p2>Go <a href="http://gauss.cs.ucsb.edu/~aydin/CombBLAS/html/index.html"><span class=s6>back</span></a> to the the Combinatorial BLAS home page.</p> </div> </body> </html> CombBLAS_beta_16_2/Doxyfile000644 000765 000024 00000001227 13271437351 017142 0ustar00aydinbulucstaff000000 000000 PROJECT_NAME = COMBINATORIAL_BLAS PROJECT_NUMBER = 1.6 OUTPUT_DIRECTORY = doc OUTPUT_LANGUAGE = English EXTRACT_ALL = YES FILE_PATTERNS = *.cpp *.h RECURSIVE = YES PDF_HYPERLINKS = YES USE_PDFLATEX = YES GENERATE_XML=YES SOURCE_BROWSER = YES DISABLE_INDEX = YES GENERATE_TREEVIEW = YES EXCLUDE_PATTERNS = */Test/* \ */BranchTrial/* \ */SequenceHeaps/* \ */pspectralclustering/* \ */ms_inttypes/* \ */ms_sys/* \ */mfiles/* \ */Serialization/* \ */CMakeFiles/* \ */KameshCode/* \ */TDSP/* \ */MD/* \ */Temporary/* \ */TESTDATA/* \ */Applications/hyperthread_finder.cpp \ */Applications/Driver.cpp \ */Applications/SpMMError.cpp \ CombBLAS_beta_16_2/TestCXXAcceptsFlag.cmake000644 000765 000024 00000002407 13212627400 022025 0ustar00aydinbulucstaff000000 000000 # - Test CXX compiler for a flag # Check if the CXX compiler accepts a flag # # Macro CHECK_CXX_ACCEPTS_FLAG(FLAGS VARIABLE) - # checks if the function exists # FLAGS - the flags to try # VARIABLE - variable to store the result # MACRO(CHECK_CXX_ACCEPTS_FLAG FLAGS VARIABLE) IF(NOT DEFINED ${VARIABLE}) MESSAGE(STATUS "Checking to see if CXX compiler accepts flag ${FLAGS}") TRY_COMPILE(${VARIABLE} ${CMAKE_BINARY_DIR} ${CMAKE_ROOT}/Modules/DummyCXXFile.cxx CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${FLAGS} OUTPUT_VARIABLE OUTPUT) IF(${VARIABLE}) MESSAGE(STATUS "Checking to see if CXX compiler accepts flag ${FLAGS} - yes") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log "Determining if the CXX compiler accepts the flag ${FLAGS} passed with " "the following output:\n${OUTPUT}\n\n") ELSE(${VARIABLE}) MESSAGE(STATUS "Checking to see if CXX compiler accepts flag ${FLAGS} - no") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if the CXX compiler accepts the flag ${FLAGS} failed with " "the following output:\n${OUTPUT}\n\n") ENDIF(${VARIABLE}) ENDIF(NOT DEFINED ${VARIABLE}) ENDMACRO(CHECK_CXX_ACCEPTS_FLAG) CombBLAS_beta_16_2/src/000755 000765 000024 00000000000 13271404146 016215 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/src/MPIOp.cpp000644 000765 000024 00000000156 13271404146 017647 0ustar00aydinbulucstaff000000 000000 #include #include #include "CombBLAS/MPIOp.h" namespace combblas { MPIOpCache mpioc; } CombBLAS_beta_16_2/src/CommGrid.cpp000644 000765 000024 00000013642 13271404146 020430 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.5 -------------------------------------------------*/ /* date: 10/09/2015 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2015, The Regents of the University of California 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. */ #include #include "CombBLAS/CommGrid.h" #include "CombBLAS/SpDefs.h" using namespace std; namespace combblas { CommGrid::CommGrid(MPI_Comm world, int nrowproc, int ncolproc): grrows(nrowproc), grcols(ncolproc) { MPI_Comm_dup(world, &commWorld); MPI_Comm_rank(commWorld, &myrank); int nproc; MPI_Comm_size(commWorld,&nproc); if(grrows == 0 && grcols == 0) { grrows = (int)std::sqrt((float)nproc); grcols = grrows; if(grcols * grrows != nproc) { cerr << "This version of the Combinatorial BLAS only works on a square logical processor grid" << endl; MPI_Abort(MPI_COMM_WORLD,NOTSQUARE); } } assert((nproc == (grrows*grcols))); myproccol = (int) (myrank % grcols); myprocrow = (int) (myrank / grcols); /** * Create row and column communicators (must be collectively called) * C syntax: int MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm) * C++ syntax: MPI::Intercomm MPI::Intercomm::Split(int color, int key) consts * Semantics: Processes with the same color are in the same new communicator */ MPI_Comm_split(commWorld,myprocrow, myrank,&rowWorld); MPI_Comm_split(commWorld,myproccol, myrank,&colWorld); CreateDiagWorld(); int rowRank, colRank; MPI_Comm_rank(rowWorld,&rowRank); MPI_Comm_rank(colWorld,&colRank); assert( (rowRank == myproccol) ); assert( (colRank == myprocrow) ); } void CommGrid::CreateDiagWorld() { if(grrows != grcols) { cout << "The grid is not square... !" << endl; cout << "Returning diagworld to everyone instead of the diagonal" << endl; diagWorld = commWorld; return; } int * process_ranks = new int[grcols]; for(int i=0; i < grcols; ++i) { process_ranks[i] = i*grcols + i; } MPI_Group group; MPI_Comm_group(commWorld,&group); MPI_Group diag_group; MPI_Group_incl(group,grcols, process_ranks, &diag_group); // int MPI_Group_incl(MPI_Group group, int n, int *ranks, MPI_Group *newgroup) MPI_Group_free(&group); delete [] process_ranks; // The Create() function returns MPI_COMM_NULL to processes that are NOT in group MPI_Comm_create(commWorld,diag_group,&diagWorld); MPI_Group_free(&diag_group); } bool CommGrid::OnSameProcCol( int rhsrank) { return ( myproccol == ((int) (rhsrank % grcols)) ); } bool CommGrid::OnSameProcRow( int rhsrank) { return ( myprocrow == ((int) (rhsrank / grcols)) ); } //! Return rank in the column world int CommGrid::GetRankInProcCol( int wholerank) { return ((int) (wholerank / grcols)); } //! Return rank in the row world int CommGrid::GetRankInProcRow( int wholerank) { return ((int) (wholerank % grcols)); } //! Get the rank of the diagonal processor in that particular row //! In the ith processor row, the diagonal processor is the ith processor within that row int CommGrid::GetDiagOfProcRow( ) { return myprocrow; } //! Get the rank of the diagonal processor in that particular col //! In the ith processor col, the diagonal processor is the ith processor within that col int CommGrid::GetDiagOfProcCol( ) { return myproccol; } bool CommGrid::operator== (const CommGrid & rhs) const { int result; MPI_Comm_compare(commWorld, rhs.commWorld, &result); if ((result != MPI_IDENT) && (result != MPI_CONGRUENT)) { // A call to MPI::Comm::Compare after MPI::Comm::Dup returns MPI_CONGRUENT // MPI::CONGRUENT means the communicators have the same group members, in the same order return false; } return ( (grrows == rhs.grrows) && (grcols == rhs.grcols) && (myprocrow == rhs.myprocrow) && (myproccol == rhs.myproccol)); } void CommGrid::OpenDebugFile(string prefix, ofstream & output) const { stringstream ss; string rank; ss << myrank; ss >> rank; string ofilename = prefix; ofilename += rank; output.open(ofilename.c_str(), ios_base::app ); } shared_ptr ProductGrid(CommGrid * gridA, CommGrid * gridB, int & innerdim, int & Aoffset, int & Boffset) { if(*gridA != *gridB) { cout << "Grids don't confirm for multiplication" << endl; MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } // AA: these parameters are kept for backward compatibility // they should not be used innerdim = gridA->grcols; Aoffset = (gridA->myprocrow + gridA->myproccol) % gridA->grcols; // get sequences that avoids contention Boffset = (gridB->myprocrow + gridB->myproccol) % gridB->grrows; //MPI_Comm world = MPI_COMM_WORLD; //return shared_ptr( new CommGrid(world, gridA->grrows, gridB->grcols) ); return shared_ptr( new CommGrid(*gridA) ); } } CombBLAS_beta_16_2/src/mmio.c000644 000765 000024 00000022377 13271404146 017335 0ustar00aydinbulucstaff000000 000000 /* * Matrix Market I/O library for ANSI C * * See http://math.nist.gov/MatrixMarket for details. * * */ #include #include #include #include #include "CombBLAS/mmio.h" int mm_is_valid(MM_typecode matcode) { if (!mm_is_matrix(matcode)) return 0; if (mm_is_dense(matcode) && mm_is_pattern(matcode)) return 0; if (mm_is_real(matcode) && mm_is_hermitian(matcode)) return 0; if (mm_is_pattern(matcode) && (mm_is_hermitian(matcode) || mm_is_skew(matcode))) return 0; return 1; } int mm_read_banner(FILE *f, MM_typecode *matcode) { char line[MM_MAX_LINE_LENGTH]; char banner[MM_MAX_TOKEN_LENGTH]; char mtx[MM_MAX_TOKEN_LENGTH]; char crd[MM_MAX_TOKEN_LENGTH]; char data_type[MM_MAX_TOKEN_LENGTH]; char storage_scheme[MM_MAX_TOKEN_LENGTH]; char *p; mm_clear_typecode(matcode); if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL) return MM_PREMATURE_EOF; if (sscanf(line, "%s %s %s %s %s", banner, mtx, crd, data_type, storage_scheme) != 5) return MM_PREMATURE_EOF; for (p=mtx; *p!='\0'; *p=tolower(*p),p++); /* convert to lower case */ for (p=crd; *p!='\0'; *p=tolower(*p),p++); for (p=data_type; *p!='\0'; *p=tolower(*p),p++); for (p=storage_scheme; *p!='\0'; *p=tolower(*p),p++); /* check for banner */ if (strncmp(banner, MatrixMarketBanner, strlen(MatrixMarketBanner)) != 0) return MM_NO_HEADER; /* first field should be "mtx" */ if (strcmp(mtx, MM_MTX_STR) != 0) return MM_UNSUPPORTED_TYPE; mm_set_matrix(matcode); /* second field describes whether this is a sparse matrix (in coordinate storgae) or a dense array */ if (strcmp(crd, MM_SPARSE_STR) == 0) mm_set_sparse(matcode); else if (strcmp(crd, MM_DENSE_STR) == 0) mm_set_dense(matcode); else return MM_UNSUPPORTED_TYPE; /* third field */ if (strcmp(data_type, MM_REAL_STR) == 0) mm_set_real(matcode); else if (strcmp(data_type, MM_COMPLEX_STR) == 0) mm_set_complex(matcode); else if (strcmp(data_type, MM_PATTERN_STR) == 0) mm_set_pattern(matcode); else if (strcmp(data_type, MM_INT_STR) == 0) mm_set_integer(matcode); else return MM_UNSUPPORTED_TYPE; /* fourth field */ if (strcmp(storage_scheme, MM_GENERAL_STR) == 0) mm_set_general(matcode); else if (strcmp(storage_scheme, MM_SYMM_STR) == 0) mm_set_symmetric(matcode); else if (strcmp(storage_scheme, MM_HERM_STR) == 0) mm_set_hermitian(matcode); else if (strcmp(storage_scheme, MM_SKEW_STR) == 0) mm_set_skew(matcode); else return MM_UNSUPPORTED_TYPE; return 0; } int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz) { if (fprintf(f, "%d %d %d\n", M, N, nz) != 3) return MM_COULD_NOT_WRITE_FILE; else return 0; } /* ABAB: fixed to support 64-bit values */ int mm_read_mtx_crd_size(FILE *f, int64_t *M, int64_t *N, int64_t *nz, int64_t * numlinesread ) { char line[MM_MAX_LINE_LENGTH]; int64_t num_items_read; /* set return null parameter values, in case we exit with errors */ *M = *N = *nz = 0; /* now continue scanning until you reach the end-of-comments */ do { (*numlinesread) = (*numlinesread)+1; if (fgets(line,MM_MAX_LINE_LENGTH,f) == NULL) return MM_PREMATURE_EOF; }while (line[0] == '%'); /* line[] is either blank or has M,N, nz */ if (sscanf(line, "%lld %lld %lld", M, N, nz) == 3) return 0; else do { num_items_read = fscanf(f, "%lld %lld %lld", M, N, nz); if (num_items_read == EOF) return MM_PREMATURE_EOF; } while (num_items_read != 3); return 0; } int mm_read_mtx_array_size(FILE *f, int *M, int *N) { char line[MM_MAX_LINE_LENGTH]; int num_items_read; /* set return null parameter values, in case we exit with errors */ *M = *N = 0; /* now continue scanning until you reach the end-of-comments */ do { if (fgets(line,MM_MAX_LINE_LENGTH,f) == NULL) return MM_PREMATURE_EOF; }while (line[0] == '%'); /* line[] is either blank or has M,N, nz */ if (sscanf(line, "%d %d", M, N) == 2) return 0; else /* we have a blank line */ do { num_items_read = fscanf(f, "%d %d", M, N); if (num_items_read == EOF) return MM_PREMATURE_EOF; } while (num_items_read != 2); return 0; } int mm_write_mtx_array_size(FILE *f, int M, int N) { if (fprintf(f, "%d %d\n", M, N) != 2) return MM_COULD_NOT_WRITE_FILE; else return 0; } /*-------------------------------------------------------------------------*/ /******************************************************************/ /* use when I[], J[], and val[]J, and val[] are already allocated */ /******************************************************************/ int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode) { int i; if (mm_is_complex(matcode)) { for (i=0; i #include #include "CombBLAS/hash.hpp" namespace combblas { uint64_t inline _rotl64(uint64_t value, int8_t amount) { return ((value) << (amount)) | ((value) >> (64 - (amount))); } uint32_t SuperFastHash (const char * data, int len) { uint32_t hash = len, tmp; int rem; if (len <= 0 || data == NULL) return 0; rem = len & 3; len >>= 2; /* Main loop */ for (;len > 0; len--) { hash += get16bits (data); tmp = (get16bits (data+2) << 11) ^ hash; hash = (hash << 16) ^ tmp; data += 2*sizeof (uint16_t); hash += hash >> 11; } /* Handle end cases */ switch (rem) { case 3: hash += get16bits (data); hash ^= hash << 16; hash ^= data[sizeof (uint16_t)] << 18; hash += hash >> 11; break; case 2: hash += get16bits (data); hash ^= hash << 11; hash += hash >> 17; break; case 1: hash += *data; hash ^= hash << 10; hash += hash >> 1; } /* Force "avalanching" of final 127 bits */ hash ^= hash << 3; hash += hash >> 5; hash ^= hash << 4; hash += hash >> 17; hash ^= hash << 25; hash += hash >> 6; return hash; } //----------------------------------------------------------------------------- // Block read - if your platform needs to do endian-swapping or can only // handle aligned reads, do the conversion here inline uint64_t getblock ( const uint64_t * p, int i ) { return p[i]; } //---------- // Block mix - combine the key bits with the hash bits and scramble everything inline void bmix64 ( uint64_t & h1, uint64_t & h2, uint64_t & k1, uint64_t & k2, uint64_t & c1, uint64_t & c2 ) { k1 *= c1; k1 = _rotl64(k1,23); k1 *= c2; h1 ^= k1; h1 += h2; h2 = _rotl64(h2,41); k2 *= c2; k2 = _rotl64(k2,23); k2 *= c1; h2 ^= k2; h2 += h1; h1 = h1*3+0x52dce729; h2 = h2*3+0x38495ab5; c1 = c1*5+0x7b7d159c; c2 = c2*5+0x6bce6396; } //---------- // Finalization mix - avalanches all bits to within 0.05% bias inline uint64_t fmix64 ( uint64_t k ) { k ^= k >> 33; k *= 0xff51afd7ed558ccd; k ^= k >> 33; k *= 0xc4ceb9fe1a85ec53; k ^= k >> 33; return k; } void MurmurHash3_x64_128 ( const void * key, const int len, const uint32_t seed, void * out ) { const uint8_t * data = (const uint8_t*)key; const int nblocks = len / 16; uint64_t h1 = 0x9368e53c2f6af274 ^ seed; uint64_t h2 = 0x586dcd208f7cd3fd ^ seed; uint64_t c1 = 0x87c37b91114253d5; uint64_t c2 = 0x4cf5ad432745937f; //---------- // body const uint64_t * blocks = (const uint64_t *)(data); for(int i = 0; i < nblocks; i++) { uint64_t k1 = getblock(blocks,i*2+0); uint64_t k2 = getblock(blocks,i*2+1); bmix64(h1,h2,k1,k2,c1,c2); } //---------- // tail const uint8_t * tail = (const uint8_t*)(data + nblocks*16); uint64_t k1 = 0; uint64_t k2 = 0; switch(len & 15) { case 15: k2 ^= uint64_t(tail[14]) << 48; case 14: k2 ^= uint64_t(tail[13]) << 40; case 13: k2 ^= uint64_t(tail[12]) << 32; case 12: k2 ^= uint64_t(tail[11]) << 24; case 11: k2 ^= uint64_t(tail[10]) << 16; case 10: k2 ^= uint64_t(tail[ 9]) << 8; case 9: k2 ^= uint64_t(tail[ 8]) << 0; case 8: k1 ^= uint64_t(tail[ 7]) << 56; case 7: k1 ^= uint64_t(tail[ 6]) << 48; case 6: k1 ^= uint64_t(tail[ 5]) << 40; case 5: k1 ^= uint64_t(tail[ 4]) << 32; case 4: k1 ^= uint64_t(tail[ 3]) << 24; case 3: k1 ^= uint64_t(tail[ 2]) << 16; case 2: k1 ^= uint64_t(tail[ 1]) << 8; case 1: k1 ^= uint64_t(tail[ 0]) << 0; bmix64(h1,h2,k1,k2,c1,c2); }; //---------- // finalization h2 ^= len; h1 += h2; h2 += h1; h1 = fmix64(h1); h2 = fmix64(h2); h1 += h2; h2 += h1; ((uint64_t*)out)[0] = h1; ((uint64_t*)out)[1] = h2; } //----------------------------------------------------------------------------- // If we need a smaller hash value, it's faster to just use a portion of the // 128-bit hash void MurmurHash3_x64_32 ( const void * key, int len, uint32_t seed, void * out ) { uint32_t temp[4]; MurmurHash3_x64_128(key,len,seed,temp); *(uint32_t*)out = temp[0]; } //---------- void MurmurHash3_x64_64 ( const void * key, int len, uint32_t seed, void * out ) { uint64_t temp[2]; MurmurHash3_x64_128(key,len,seed,temp); *(uint64_t*)out = temp[0]; } //----------------------------------------------------------------------------- } CombBLAS_beta_16_2/src/MemoryPool.cpp000644 000765 000024 00000011431 13271404146 021023 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include "CombBLAS/MemoryPool.h" using namespace std; namespace combblas { MemoryPool::MemoryPool(void * m_beg, size_t m_size):initbeg((char*)m_beg), initend(((char*)m_beg)+m_size) { Memory m((char*) m_beg, m_size); freelist.push_back(m); } void * MemoryPool::alloc(size_t size) { for(list::iterator iter = freelist.begin(); iter != freelist.end(); ++iter) { if ((*iter).size > size) // return the first 'big enough' chunk of memory { char * free = (*iter).begin; (*iter).begin += size; // modify the beginning of the remaining chunk (*iter).size -= size; // modify the size of the remaning chunk return (void *) free; // return the memory } } cout << "No pinned memory available" << endl; return NULL; } void MemoryPool::dealloc(void * base, size_t size) { if( ((char*) base) >= initbeg && (((char*)base) + size) < initend) { list::iterator titr = freelist.begin(); // trailing iterator list::iterator litr = freelist.begin(); // leading iterator ++litr; if( (char*)base < titr->begaddr()) // if we're inserting to the front of the list { if(titr->begaddr() == ((char*)base) + size) { titr->begin = (char*)base; titr->size += size; } else { Memory m((char*) base, size); freelist.insert(titr, m); } } else if( litr == freelist.end() ) // if we're inserting to the end of a 'single element list' { if(titr->endaddr() == (char*)base) { titr->size += size; } else { Memory m((char*) base, size); freelist.insert(litr, m); } } else // not a single element list, nor we're inserting to the front { // loop until you find the right spot while( litr != freelist.end() && litr->begaddr() < (char*)base) { ++litr; ++titr; } //! defragment on the fly //! by attaching to the previous chunk if prevchunk.endaddr equals newitem.beginaddr, or //! by attaching to the next available chunk if newitem.endaddr equals nextchunk.begaddr if(titr->endaddr() == (char*)base) { //! check the next chunk to see if we perfectly fill the hole if( litr == freelist.end() || litr->begaddr() != ((char*)base) + size) { titr->size += size; } else { titr->size += (size + litr->size); // modify the chunk pointed by trailing iterator freelist.erase(litr); // delete the chunk pointed by the leading iterator } } else { if( litr == freelist.end() || litr->begaddr() != ((char*)base) + size) { Memory m((char*) base, size); //! Insert x before pos: 'iterator insert(iterator pos, const T& x)' freelist.insert(litr, m); } else { litr->begin = (char*)base; litr->size += size; } } } } else { cerr << "Memory starting at " << base << " and ending at " << (void*) ((char*) base + size) << " is out of pool bounds, cannot dealloc()" << endl; } } //! Dump the contents of the pinned memory ofstream& operator<< (ofstream& outfile, const MemoryPool & mpool) { int i = 0; for(list::const_iterator iter = mpool.freelist.begin(); iter != mpool.freelist.end(); ++iter, ++i) { outfile << "Chunk " << i << " of size: " << (*iter).size << " starts:" << (void*)(*iter).begin << " and ends: " << (void*) ((*iter).begin + (*iter).size) << endl ; } return outfile; } } CombBLAS_beta_16_2/src/MPIType.cpp000644 000765 000024 00000005215 13271404146 020213 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include #include #include "CombBLAS/MPIType.h" using namespace std; namespace combblas { MPIDataTypeCache mpidtc; // global variable template<> MPI_Datatype MPIType< signed char >( void ) { return MPI_CHAR; }; template<> MPI_Datatype MPIType< unsigned char >( void ) { return MPI_UNSIGNED_CHAR; }; template<> MPI_Datatype MPIType< signed short int >( void ) { return MPI_SHORT; }; template<> MPI_Datatype MPIType< unsigned short int >( void ) { return MPI_UNSIGNED_SHORT; }; template<> MPI_Datatype MPIType< int32_t >( void ) { return MPI_INT; }; template<> MPI_Datatype MPIType< uint32_t >( void ) { return MPI_UNSIGNED; }; template<> MPI_Datatype MPIType(void) { return MPI_LONG_LONG; }; template<> MPI_Datatype MPIType< uint64_t>(void) { return MPI_UNSIGNED_LONG_LONG; }; template<> MPI_Datatype MPIType< float >( void ) { return MPI_FLOAT; }; template<> MPI_Datatype MPIType< double >( void ) { return MPI_DOUBLE; }; template<> MPI_Datatype MPIType< long double >( void ) { return MPI_LONG_DOUBLE; }; template<> MPI_Datatype MPIType< bool >( void ) { return MPI_BYTE; // usually #define MPI_BOOL MPI_BYTE anyway }; } CombBLAS_beta_16_2/Applications/CC.cpp000644 000765 000024 00000016233 13271515277 021062 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" #include "CC.h" using namespace std; using namespace combblas; /** ** Connected components based on Awerbuch-Shiloach algorithm **/ class Dist { public: typedef SpDCCols < int64_t, double > DCCols; typedef SpParMat < int64_t, double, DCCols > MPI_DCCols; }; int main(int argc, char* argv[]) { int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nthreads = 1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank == 0) { cout << "Process Grid (p x p x t): " << sqrt(nprocs) << " x " << sqrt(nprocs) << " x " << nthreads << endl; } if(argc < 3) { if(myrank == 0) { cout << "Usage: ./cc -I -M (required)\n"; cout << "-I (mm: matrix market, triples: (vtx1, vtx2, edge_weight) triples. default:mm)\n"; cout << "-base (default:1)\n"; cout << "-rand (default:0)\n"; cout << "Example (0-indexed mtx with random permutation): ./cc -M input.mtx -base 0 -rand 1" << endl; cout << "Example (triples format): ./cc -I triples -M input.txt" << endl; } MPI_Finalize(); return -1; } { string ifilename = ""; int base = 1; int randpermute = 0; bool isMatrixMarket = true; for (int i = 1; i < argc; i++) { if (strcmp(argv[i],"-I")==0) { string ifiletype = string(argv[i+1]); if(ifiletype == "triples") isMatrixMarket = false; } if (strcmp(argv[i],"-M")==0) { ifilename = string(argv[i+1]); if(myrank == 0) printf("filename: %s",ifilename.c_str()); } else if (strcmp(argv[i],"-base")==0) { base = atoi(argv[i + 1]); if(myrank == 0) printf("\nBase of MM (1 or 0):%d",base); } else if (strcmp(argv[i],"-rand")==0) { randpermute = atoi(argv[i + 1]); if(myrank == 0) printf("\nRandomly permute the matrix? (1 or 0):%d",randpermute); } } double tIO = MPI_Wtime(); Dist::MPI_DCCols A; // construct object if(isMatrixMarket) A.ParallelReadMM(ifilename, base, maximum()); else A.ReadGeneralizedTuples(ifilename, maximum()); A.PrintInfo(); Dist::MPI_DCCols AT = A; AT.Transpose(); if(!(AT == A)) { SpParHelper::Print("Symmatricizing an unsymmetric input matrix.\n"); A += AT; } A.PrintInfo(); ostringstream outs; outs << "File Read time: " << MPI_Wtime() - tIO << endl; SpParHelper::Print(outs.str()); if(randpermute && isMatrixMarket) // no need for GeneralizedTuples { // randomly permute for load balance if(A.getnrow() == A.getncol()) { FullyDistVec p( A.getcommgrid()); p.iota(A.getnrow(), 0); p.RandPerm(); (A)(p,p,true);// in-place permute to save memory SpParHelper::Print("Applied symmetric permutation.\n"); } else { SpParHelper::Print("Rectangular matrix: Can not apply symmetric permutation.\n"); } } FullyDistVec ColSums = A.Reduce(Column, plus(), 0.0); FullyDistVec isov = ColSums.FindInds(bind2nd(equal_to(), 0)); outs.str(""); outs.clear(); outs << "isolated vertice: " << isov.TotalLength() << endl; SpParHelper::Print(outs.str()); float balance = A.LoadImbalance(); int64_t nnz = A.getnnz(); outs.str(""); outs.clear(); outs << "Load balance: " << balance << endl; outs << "Nonzeros: " << nnz << endl; SpParHelper::Print(outs.str()); double t1 = MPI_Wtime(); int64_t nCC = 0; FullyDistVec cclabels = CC(A, nCC); double t2 = MPI_Wtime(); //outs.str(""); //outs.clear(); //outs << "Total time: " << t2 - t1 << endl; //SpParHelper::Print(outs.str()); //HistCC(cclabels, nCC); int64_t nclusters = cclabels.Reduce(maximum(), (int64_t) 0 ) ; nclusters ++; // because of zero based indexing for clusters double tend = MPI_Wtime(); stringstream s2; s2 << "Number of clusters: " << nclusters << endl; s2 << "Total time: " << (t2-t1) << endl; s2 << "=================================================\n" << endl ; SpParHelper::Print(s2.str()); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/CMakeLists.txt000644 000765 000024 00000002035 13271404146 022614 0ustar00aydinbulucstaff000000 000000 # Top level directory has the include files ADD_EXECUTABLE( tdbfs TopDownBFS.cpp ) ADD_EXECUTABLE( dobfs DirOptBFS.cpp ) ADD_EXECUTABLE( fbfs FilteredBFS.cpp ) ADD_EXECUTABLE( fmis FilteredMIS.cpp ) ADD_EXECUTABLE( mcl MCL.cpp ) ADD_EXECUTABLE( betwcent BetwCent.cpp ) TARGET_LINK_LIBRARIES( tdbfs CombBLAS) TARGET_LINK_LIBRARIES( dobfs CombBLAS) TARGET_LINK_LIBRARIES( fbfs CombBLAS) TARGET_LINK_LIBRARIES( fmis CombBLAS) TARGET_LINK_LIBRARIES( mcl CombBLAS) TARGET_LINK_LIBRARIES( betwcent CombBLAS) ADD_TEST(NAME BetwCent_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA/SCALE16BTW-TRANSBOOL/ 10 96 ) ADD_TEST(NAME TopDownBFS_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ Force 17 FastGen) ADD_TEST(NAME DirOptBFS_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ 17 ) ADD_TEST(NAME FBFS_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ Gen 16 ) ADD_TEST(NAME FMIS_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ 17 ) CombBLAS_beta_16_2/Applications/._.DS_Store000644 000765 000024 00000000170 13271513625 021755 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/Applications/.DS_Store000644 000765 000024 00000020004 13271513625 021536 0ustar00aydinbulucstaff000000 000000 Bud1 pV-IPD  @€ @€ @€ @SpMSpV-IPDPS2017bwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šSpMSpV-IPDPS2017lsvCblob’bplist00Ø HIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates « "&+05:>CÔ   WvisibleUwidthYascendingZidentifier , TnameÔWvisibleUwidthYascending#XubiquityÔ  ! µ\dateModifiedÔ %[dateCreatedÔ '( Tsizea Ô ,- Tkinds Ô 12 Ulabeld Ô 67 WversionK Ô ; Xcomments Ô @BÈ^dateLastOpenedÔDYdateAdded#@(Tname#@0 .@H\epyŒŽ›¤¬²¼ÇÈËÌÑÚâèòóõöÿ   "#$09>@ABKPRST]cefgpxz{|…Ž™šœ¬µ¿ÀÁÂËÐÙLÚSpMSpV-IPDPS2017lsvpblobYbplist00Ø DEF _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates Ù #(-27<@Xcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ WvisibleUwidthYascendingUindex, Ô ÈÔ$%µÔ *, aÔ/ 1d Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©SpMSpV-IPDPS2017vSrnlong E DSDB `€ @€ @€ @hYascendingUindex, Ô ÈÔ$%µÔ *, aÔ/ 1d Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©SpMSpV-IPDPS2017vSrnlongCombBLAS_beta_16_2/Applications/WriteMCLClusters.h000644 000765 000024 00000017054 13271404146 023407 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ #include #include #include #include #include #include #include // Required for stringstreams #include #include #include "CombBLAS/CombBLAS.h" namespace combblas { class HipMCLClusterSaveHandler { public: // no reader template void save(std::basic_ostream& os, std::vector & strvec, int64_t index) { for (auto it = strvec.begin() ; it != strvec.end(); ++it) os << *it << " "; } }; /** * Write clusters to file: vertices belonging to a cluster are written in a single line separated by space. * TODO: sort clusters by their sizes * @param[in] ofName {output file name} * @param[in] clustIdForVtx {the ith entry stores the cluster id of the ith vertex} * @param[in] vtxLabels {labels of vertices} */ template void WriteMCLClusters(std::string ofName, FullyDistVec clustIdForVtx, FullyDistVec > vtxLabels) { auto commGrid = clustIdForVtx.getcommgrid(); MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); // find the number of clusters IT nclusters = clustIdForVtx.Reduce(maximum(), (IT) 0 ) ; nclusters ++; // because of zero based indexing for clusters std::vector rdispls(nprocs+1); std::vector recvcnt(nprocs); std::vector sendcnt(nprocs,0); std::vector sdispls(nprocs+1); IT ploclen = clustIdForVtx.LocArrSize(); const IT* larr = clustIdForVtx.GetLocArr(); // local part of cluster ids for vertices //just to get the destination processor FullyDistVec temp(commGrid, nclusters,0); for(IT i=0; i < ploclen; ++i) { IT locind; int owner = temp.Owner(larr[i], locind); sendcnt[owner]++; } MPI_Alltoall(sendcnt.data(), 1, MPI_INT, recvcnt.data(), 1, MPI_INT, World); sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i STRASARRAY; typedef std::pair< IT, STRASARRAY> TYPE2SEND; const STRASARRAY* lVtxLabels = vtxLabels.GetLocArr(); std::vector senddata(ploclen); // Pack cluster and vertex information to send std::vector count(nprocs, 0); for(IT i=0; i < ploclen; ++i) { IT locind; int owner = temp.Owner(larr[i], locind); int idx = sdispls[owner] + count[owner]; count[owner]++; senddata[idx] = TYPE2SEND(locind, lVtxLabels[i]); // sending local cluster ids for the destination processor } MPI_Datatype MPI_CLUST; MPI_Type_contiguous(sizeof(TYPE2SEND), MPI_CHAR, &MPI_CLUST); MPI_Type_commit(&MPI_CLUST); IT totrecv = rdispls[nprocs]; std::vector recvdata(totrecv); MPI_Alltoallv(senddata.data(), sendcnt.data(), sdispls.data(), MPI_CLUST, recvdata.data(), recvcnt.data(), rdispls.data(), MPI_CLUST, World); // Receiver groups vertices by cluster ids std::vector< std::vector > vtxGroupbyCC(temp.LocArrSize()); for(int i=0; i > clusters(commGrid, nclusters, std::vector{}); for(int i=0; i void WriteMCLClusters(std::string ofName, FullyDistVec clustIdForVtx, int base) { auto commGrid = clustIdForVtx.getcommgrid(); MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); IT lenuntil = clustIdForVtx.LengthUntil(); // find the number of clusters IT nclusters = clustIdForVtx.Reduce(maximum(), (IT) 0 ) ; nclusters ++; // because of zero based indexing for clusters std::vector rdispls(nprocs+1); std::vector recvcnt(nprocs); std::vector sendcnt(nprocs,0); std::vector sdispls(nprocs+1); IT ploclen = clustIdForVtx.LocArrSize(); const IT* larr = clustIdForVtx.GetLocArr(); // local part of cluster ids for vertices //just to get the destination processor FullyDistVec temp(commGrid, nclusters,0); for(IT i=0; i < ploclen; ++i) { IT locind; int owner = temp.Owner(larr[i], locind); sendcnt[owner]++; } MPI_Alltoall(sendcnt.data(), 1, MPI_INT, recvcnt.data(), 1, MPI_INT, World); sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i> senddata(ploclen); // Pack cluster and vertex information to send std::vector count(nprocs, 0); for(IT i=0; i < ploclen; ++i) { IT locind; int owner = temp.Owner(larr[i], locind); int idx = sdispls[owner] + count[owner]; count[owner]++; senddata[idx] = std::make_pair(locind, i+lenuntil+base); // sending local cluster ids for the destination processor } MPI_Datatype MPI_CLUST; MPI_Type_contiguous(sizeof(std::pair), MPI_CHAR, &MPI_CLUST); MPI_Type_commit(&MPI_CLUST); IT totrecv = rdispls[nprocs]; std::vector> recvdata(totrecv); MPI_Alltoallv(senddata.data(), sendcnt.data(), sdispls.data(), MPI_CLUST, recvdata.data(), recvcnt.data(), rdispls.data(), MPI_CLUST, World); // Receiver groups vertices by cluster ids std::vector< std::vector > vtxGroupbyCC(temp.LocArrSize()); for(int i=0; i > clusters(commGrid, nclusters, std::vector{}); for(int i=0; i #include #include #include #include #include #include #include #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #endif #ifdef _OPENMP #include #endif double cblas_alltoalltime; double cblas_allgathertime; double cblas_mergeconttime; double cblas_transvectime; double cblas_localspmvtime; #ifdef _OPENMP int cblas_splits = omp_get_max_threads(); #else int cblas_splits = 1; #endif #define ITERS 16 #define EDGEFACTOR 16 using namespace std; using namespace combblas; MTRand GlobalMT(123); // for reproducable result template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } struct ParentType { public: ParentType(){parent=-1; p = 0;}; ParentType(int64_t x){parent=(x); p = 0;}; friend ostream& operator<<(ostream& os, const ParentType & vertex ){os << "Parent=" << vertex.parent << " p=" << vertex.p; return os;}; //private: int64_t parent; float p; }; struct Edge_randomizer : public std::unary_function, std::pair> { const std::pair operator()(const std::pair & x) const { float edgeRand = static_cast(rand()); // random range(0,1) return std::pair(x.first, edgeRand); } }; static void MPI_randuniq(void * invec, void * inoutvec, int * len, MPI_Datatype *datatype) { RandReduce RR; int64_t * inveccast = (int64_t *) invec; int64_t * inoutveccast = (int64_t *) inoutvec; for (int i=0; i<*len; i++ ) inoutveccast[i] = RR(inveccast[i], inoutveccast[i]); } struct SelectRandSRing { static MPI_Op MPI_BFSRAND; typedef int64_t T_promote; static ParentType id(){ return ParentType(); }; static bool returnedSAID() { return false; } //static MPI_Op mpi_op() { return MPI_MAX; }; // do we need this? static ParentType add(const ParentType & arg1, const ParentType & arg2) { //cout << arg1 << " ;;; " << arg2 << endl; if(arg1.p < arg2.p) return arg1; else return arg2; } static ParentType multiply(const T_promote & arg1, const ParentType & arg2) { ParentType temp; temp.parent = arg2.parent; temp.p = GlobalMT.rand(); return temp; } static void axpy(T_promote a, const ParentType & x, ParentType & y) { y = add(y, multiply(a, x)); } }; // This one is used for BFS iteration struct SelectMinSRing1 { typedef int64_t T_promote; static T_promote id(){ return -1; }; static bool returnedSAID() { return false; } //static MPI_Op mpi_op() { return MPI_MAX; }; static T_promote add(const T_promote & arg1, const T_promote & arg2) { cout << arg1 << " a " << arg2 << endl; return std::max(arg1, arg2); } static T_promote multiply(const bool & arg1, const T_promote & arg2) { cout << arg1 << " m " << arg2 << endl; return arg2; } /* static void axpy(bool a, const T_promote & x, T_promote & y) { y = std::max(y, x); }*/ }; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_s32p64; typedef SpParMat < int64_t, int64_t, SpDCCols > PSpMat_Int64; void RandomParentBFS(PSpMat_Bool & Aeff) { int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); FullyDistSpVec fringe(Aeff.getcommgrid(), Aeff.getncol()); fringe.SetElement(0, ParentType(0)); fringe.SetElement(1, ParentType(1)); fringe.SetElement(5, ParentType(5)); fringe.SetElement(6, ParentType(6)); fringe.SetElement(7, ParentType(7)); PSpMat_Int64 A = Aeff; //A.PrintInfo(); SpMV(A, fringe, fringe, false); fringe.DebugPrint(); } int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 2) { if(myrank == 0) { cout << "Usage: ./rpbfs " << endl; cout << "Example: mpirun -np 4 ./spbfs 20" << endl; } MPI_Finalize(); return -1; } { // Declare objects PSpMat_Bool A; FullyDistVec nonisov; // id's of non-isolated (connected) vertices unsigned scale; scale = static_cast(atoi(argv[1])); double initiator[4] = {.57, .19, .19, .05}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true ); MPI_Barrier(MPI_COMM_WORLD); PSpMat_Bool * ABool = new PSpMat_Bool(*DEL, false); delete DEL; int64_t removed = ABool->RemoveLoops(); ABool->PrintInfo(); FullyDistVec * ColSums = new FullyDistVec(ABool->getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(ABool->getcommgrid()); ABool->Reduce(*ColSums, Column, plus(), static_cast(0)); ABool->Reduce(*RowSums, Row, plus(), static_cast(0)); ColSums->EWiseApply(*RowSums, plus()); delete RowSums; nonisov = ColSums->FindInds(bind2nd(greater(), 0)); delete ColSums; nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) #ifndef NOPERMUTE ABool->operator()(nonisov, nonisov, true); // in-place permute to save memory #endif RandomParentBFS(*ABool); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/SpMSpV-IPDPS2017/000755 000765 000024 00000000000 13271404146 022373 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/Applications/CC.h000644 000765 000024 00000037412 13271515277 020531 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" /** ** Connected components based on Awerbuch-Shiloach algorithm **/ namespace combblas { template struct Select2ndMinSR { typedef typename promote_trait::T_promote T_promote; static T_promote id(){ return std::numeric_limits::max(); }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MIN; }; static T_promote add(const T_promote & arg1, const T_promote & arg2) { return std::min(arg1, arg2); } static T_promote multiply(const T1 & arg1, const T2 & arg2) { return static_cast (arg2); } static void axpy(const T1 a, const T2 & x, T_promote & y) { y = add(y, multiply(a, x)); } }; template FullyDistVec StarCheck(const SpParMat & A, FullyDistVec & father) { // FullyDistVec doesn't support "bool" due to crippled vector semantics //TODO: change the value of star entries to grandfathers so it will be IT as well. FullyDistVec star(A.getcommgrid(), A.getnrow(), 1); // all initialized to true FullyDistVec grandfather = father(father); // find grandparents grandfather.EWiseOut(father, [](IT pv, IT gpv) -> short { return static_cast(pv == gpv); }, star); // remove some stars // nostars FullyDistSpVec nonstar = star.Find([](short isStar){return isStar==0;}); // grandfathers of nonstars FullyDistSpVec nonstarGF = EWiseApply(nonstar, grandfather, [](short isStar, IT gf){return gf;}, [](short isStar, IT gf){return true;}, false, static_cast(0)); // grandfather pointing to a grandchild FullyDistSpVec gfNonstar = nonstarGF.Invert(nonstarGF.TotalLength()); // for duplicates, keep the first one // star(GF) = 0 star.EWiseApply(gfNonstar, [](short isStar, IT x){return static_cast(0);}, false, static_cast(0)); // at this point vertices at level 1 (children of the root) can still be stars FullyDistVec starFather = star(father); star.EWiseApply(starFather, std::multiplies()); /* alternative approach (used in the Matlab code) // fathers of nonstars FullyDistSpVec nonstarF = EWiseApply(nonstar, father,[](short isStar, IT f){return f;}, [](short isStar, IT f){return true;},false, static_cast(0)); // father pointing to a child FullyDistSpVec fNonstar = nonstarF.Invert(nonstarF.TotalLength()); // star(F) = 0 star.EWiseApply(fNonstar, [](short isStar, IT x){return static_cast(0);},false, static_cast(0)); star = star(father); */ return star; } template void ConditionalHook(const SpParMat & A, FullyDistVec & father) { FullyDistVec stars = StarCheck(A, father); FullyDistVec minNeighborFather ( A.getcommgrid()); minNeighborFather = SpMV>(A, father); // value is the minimum of all neighbors' fathers FullyDistSpVec> hooks(A.getcommgrid(), A.getnrow()); // create entries belonging to stars hooks = EWiseApply>(hooks, stars, [](std::pair x, short isStar){return std::make_pair(0,0);}, [](std::pair x, short isStar){return isStar==1;}, true, {0,0}); // include father information hooks = EWiseApply>(hooks, father, [](std::pair x, IT f){return std::make_pair(f,0);}, [](std::pair x, IT f){return true;}, false, {0,0}); //keep entries with father>minNeighborFather and insert minNeighborFather information hooks = EWiseApply>(hooks, minNeighborFather, [](std::pair x, IT mnf){return std::make_pair(std::get<0>(x), mnf);}, [](std::pair x, IT mnf){return std::get<0>(x) > mnf;}, false, {0,0}); //Invert FullyDistSpVec> starhooks= hooks.Invert(hooks.TotalLength(), [](std::pair val, IT ind){return std::get<0>(val);}, [](std::pair val, IT ind){return std::make_pair(ind, std::get<1>(val));}, [](std::pair val1, std::pair val2){return val2;} ); // allowing the last vertex to pick the parent of the root of a star gives the correct output!! // [](pair val1, pair val2){return val1;} does not give the correct output. why? // drop the index informaiton FullyDistSpVec finalhooks = EWiseApply(starhooks, father, [](std::pair x, IT f){return std::get<1>(x);}, [](std::pair x, IT f){return true;}, false, {0,0}); father.Set(finalhooks); } template void UnconditionalHook(const SpParMat & A, FullyDistVec & father) { FullyDistVec stars = StarCheck(A, father); FullyDistVec minNeighborFather ( A.getcommgrid()); minNeighborFather = SpMV>(A, father); // value is the minimum of all neighbors' fathers FullyDistSpVec> hooks(A.getcommgrid(), A.getnrow()); // create entries belonging to stars hooks = EWiseApply>(hooks, stars, [](std::pair x, short isStar){return std::make_pair(0,0);}, [](std::pair x, short isStar){return isStar==1;}, true, {0,0}); // include father information hooks = EWiseApply>(hooks, father, [](std::pair x, IT f){return std::make_pair(f,0);}, [](std::pair x, IT f){return true;}, false, {0,0}); //keep entries with father!minNeighborFather and insert minNeighborFather information hooks = EWiseApply>(hooks, minNeighborFather, [](std::pair x, IT mnf){return std::make_pair(std::get<0>(x), mnf);}, [](std::pair x, IT mnf){return std::get<0>(x) != mnf;}, false, {0,0}); //Invert FullyDistSpVec> starhooks= hooks.Invert(hooks.TotalLength(), [](std::pair val, IT ind){return std::get<0>(val);}, [](std::pair val, IT ind){return std::make_pair(ind, std::get<1>(val));}, [](std::pair val1, std::pair val2){return val1;} ); // drop the index informaiton FullyDistSpVec finalhooks = EWiseApply(starhooks, father, [](std::pair x, IT f){return std::get<1>(x);}, [](std::pair x, IT f){return true;}, false, {0,0}); father.Set(finalhooks); } template void Shortcut(FullyDistVec & father) { FullyDistVec grandfather = father(father); father = grandfather; // we can do it unconditionally because it is trivially true for stars } template bool neigborsInSameCC(const SpParMat & A, FullyDistVec & cclabel) { FullyDistVec minNeighborCCLabel ( A.getcommgrid()); minNeighborCCLabel = SpMV>(A, cclabel); return minNeighborCCLabel==cclabel; } // works only on P=1 template void Correctness(const SpParMat & A, FullyDistVec & cclabel, IT nCC, FullyDistVec father) { DER* spSeq = A.seqptr(); // local submatrix for(auto colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { IT j = colit.colid(); // local numbering for(auto nzit = spSeq->begnz(colit); nzit < spSeq->endnz(colit); ++nzit) { IT i = nzit.rowid(); if( cclabel[i] != cclabel[j]) { std::cout << i << " (" << father[i] << ", "<< cclabel[i] << ") & "<< j << "("<< father[j] << ", " << cclabel[j] << ")\n"; } } } } // Input: // father: father of each vertex. Father is essentilly the root of the star which a vertex belongs to. // father of the root is itself // Output: // cclabel: connected components are incrementally labeled // returns the number of connected components // Example: input = [0, 0, 2, 3, 0, 2], output = (0, 0, 1, 2, 0, 1), return 3 template IT LabelCC(FullyDistVec & father, FullyDistVec & cclabel) { cclabel = father; cclabel.ApplyInd([](IT val, IT ind){return val==ind ? -1 : val;}); FullyDistSpVec roots (cclabel, bind2nd(std::equal_to(), -1)); roots.nziota(0); cclabel.Set(roots); cclabel = cclabel(father); return roots.getnnz(); } // Compute strongly connected components // If you need weakly connected components, symmetricize the matrix beforehand template FullyDistVec CC(SpParMat & A, IT & nCC) { A.AddLoops(1); // needed for isolated vertices FullyDistVec father(A.getcommgrid()); father.iota(A.getnrow(), 0); // father(i)=i initially IT nonstars = 1; int iteration = 0; std::ostringstream outs; while (true) { #ifdef TIMING double t1 = MPI_Wtime(); #endif ConditionalHook(A, father); UnconditionalHook(A, father); Shortcut(father); FullyDistVec stars = StarCheck(A, father); nonstars = stars.Reduce(std::plus(), static_cast(0), [](short isStar){return static_cast(isStar==0);}); if(nonstars==0) { if(neigborsInSameCC(A, father)) break; } iteration++; #ifdef CC_TIMING double t2 = MPI_Wtime(); outs.str(""); outs.clear(); outs << "Iteration: " << iteration << " Non stars: " << nonstars; outs << " Time: " << t2 - t1; outs<< endl; SpParHelper::Print(outs.str()); #endif } FullyDistVec cc(father.getcommgrid()); nCC = LabelCC(father, cc); // TODO: Print to file //PrintCC(cc, nCC); //Correctness(A, cc, nCC, father); return cc; } template void PrintCC(FullyDistVec CC, IT nCC) { for(IT i=0; i< nCC; i++) { FullyDistVec ith = CC.FindInds(bind2nd(std::equal_to(), i)); ith.DebugPrint(); } } // Print the size of the first 4 clusters template void First4Clust(FullyDistVec& cc) { FullyDistSpVec cc1 = cc.Find([](IT label){return label==0;}); FullyDistSpVec cc2 = cc.Find([](IT label){return label==1;}); FullyDistSpVec cc3 = cc.Find([](IT label){return label==2;}); FullyDistSpVec cc4 = cc.Find([](IT label){return label==3;}); std::ostringstream outs; outs.str(""); outs.clear(); outs << "Size of the first component: " << cc1.getnnz() << std::endl; outs << "Size of the second component: " << cc2.getnnz() << std::endl; outs << "Size of the third component: " << cc3.getnnz() << std::endl; outs << "Size of the fourth component: " << cc4.getnnz() << std::endl; } template void HistCC(FullyDistVec CC, IT nCC) { FullyDistVec ccSizes(CC.getcommgrid(), nCC, 0); for(IT i=0; i< nCC; i++) { FullyDistSpVec ith = CC.Find(bind2nd(std::equal_to(), i)); ccSizes.SetElement(i, ith.getnnz()); } IT largestCCSise = ccSizes.Reduce(maximum(), static_cast(0)); const IT * locCCSizes = ccSizes.GetLocArr(); int numBins = 200; std::vector localHist(numBins,0); for(IT i=0; i< ccSizes.LocArrSize(); i++) { IT bin = (locCCSizes[i]*(numBins-1))/largestCCSise; localHist[bin]++; } std::vector globalHist(numBins,0); MPI_Comm world = CC.getcommgrid()->GetWorld(); MPI_Reduce(localHist.data(), globalHist.data(), numBins, MPIType(), MPI_SUM, 0, world); int myrank; MPI_Comm_rank(world,&myrank); if(myrank==0) { std::cout << "The largest component size: " << largestCCSise << std::endl; std::ofstream output; output.open("hist.txt", std::ios_base::app ); std::copy(globalHist.begin(), globalHist.end(), std::ostream_iterator (output, " ")); output << std::endl; output.close(); } //ccSizes.PrintToFile("histCC.txt"); } } CombBLAS_beta_16_2/Applications/MCL.cpp000644 000765 000024 00000055303 13271404146 021201 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #include #include #include #include #include // Required for stringstreams #include #include #include "CombBLAS/CombBLAS.h" #include "CC.h" #include "WriteMCLClusters.h" using namespace std; using namespace combblas; #define EPS 0.0001 double mcl_Abcasttime; double mcl_Bbcasttime; double mcl_localspgemmtime; double mcl_multiwaymergetime; double mcl_kselecttime; double mcl_prunecolumntime; double cblas_allgathertime; // for compilation (TODO: fix this dependency) int64_t mcl_memory; double tIO; class Dist { public: typedef SpDCCols < int64_t, double > DCCols; typedef SpParMat < int64_t, double, DCCols > MPI_DCCols; typedef FullyDistVec < int64_t, double> MPI_DenseVec; }; typedef struct { //Input/Output file string ifilename; bool isInputMM; int base; // only usefule for matrix market files string ofilename; //Preprocessing int randpermute; bool remove_isolated; //inflation double inflation; //pruning double prunelimit; int64_t select; int64_t recover_num; double recover_pct; int kselectVersion; // 0: adapt based on k, 1: kselect1, 2: kselect2 //HipMCL optimization int phases; int perProcessMem; bool isDoublePrecision; // true: double, false: float bool is64bInt; // true: int64_t, false: int32_t (currently for both local and global indexing) //debugging bool show; }HipMCLParam; void InitParam(HipMCLParam & param) { //Input/Output file param.ifilename = ""; param.isInputMM = false; param.ofilename = ""; param.base = 1; //Preprocessing // mcl removes isolated vertices by default, // we don't do this because it will create different ordering of vertices! param.remove_isolated = false; param.randpermute = 0; //inflation param.inflation = 0.0; //pruning param.prunelimit = 1.0/10000.0; param.select = 1100; param.recover_num = 1400; param.recover_pct = .9; // we allow both 90 or .9 as input. Internally, we keep it 0.9 param.kselectVersion = 1; //HipMCL optimization param.phases = 1; param.perProcessMem = 0; param.isDoublePrecision = true; param.is64bInt = true; //debugging param.show = false; } void ShowParam(HipMCLParam & param) { ostringstream runinfo; runinfo << "\n======================================" << endl; runinfo << "Running HipMCL with the parameters: " << endl; runinfo << "======================================" << endl; runinfo << "Input/Output file" << endl; runinfo << " input filename: " << param.ifilename << endl; runinfo << " input file type: " ; if(param.isInputMM) { runinfo << " Matrix Market" << endl; runinfo << " Base of the input matrix: " << param.base << endl; } else runinfo << " Labeled Triples format" << endl; runinfo << " Output filename: " << param.ofilename << endl; runinfo << "Preprocessing" << endl; runinfo << " Remove isolated vertices? : "; if (param.remove_isolated) runinfo << "yes"; else runinfo << "no" << endl; runinfo << " Randomly permute vertices? : "; if (param.randpermute) runinfo << "yes"; else runinfo << "no" << endl; runinfo << "Inflation: " << param.inflation << endl; runinfo << "Pruning" << endl; runinfo << " Prunelimit: " << param.prunelimit << endl; runinfo << " Recover number: " << param.recover_num << endl; runinfo << " Recover percent: " << ceil(param.recover_pct*100) << endl; runinfo << " Selection number: " << param.select << endl; // do not expose selection option at this moment //runinfo << "Selection algorithm: "; //if(kselectVersion==1) runinfo << "tournament select" << endl; //else if(kselectVersion==2) runinfo << "quickselect" << endl; //else runinfo << "adaptive based on k" << endl; runinfo << "HipMCL optimization" << endl; runinfo << " Number of phases: " << param.phases << endl; runinfo << " Memory avilable per process: "; if(param.perProcessMem>0) runinfo << param.perProcessMem << "GB" << endl; else runinfo << "not provided" << endl; if(param.isDoublePrecision) runinfo << "Using double precision floating point" << endl; else runinfo << "Using single precision floating point" << endl; if(param.is64bInt ) runinfo << "Using 64 bit indexing" << endl; else runinfo << "Using 32 bit indexing" << endl; runinfo << "Debugging" << endl; runinfo << " Show matrices after major steps? : "; if (param.show) runinfo << "yes"; else runinfo << "no" << endl; runinfo << "======================================" << endl; SpParHelper::Print(runinfo.str()); } void ProcessParam(int argc, char* argv[], HipMCLParam & param) { for (int i = 1; i < argc; i++) { if (strcmp(argv[i],"-M")==0){ param.ifilename = string(argv[i+1]); } else if (strcmp(argv[i],"--matrix-market")==0){ param.isInputMM = true; } else if (strcmp(argv[i],"-o")==0){ param.ofilename = string(argv[i+1]); } else if (strcmp(argv[i],"--show")==0){ param.show = true; } else if (strcmp(argv[i],"--remove-isolated")==0){ param.remove_isolated = true; } else if (strcmp(argv[i],"--tournament-select")==0){ param.kselectVersion = 1; } else if (strcmp(argv[i],"--quick-select")==0){ param.kselectVersion = 2; } else if (strcmp(argv[i],"-I")==0){ param.inflation = atof(argv[i + 1]); } else if (strcmp(argv[i],"-p")==0) { param.prunelimit = atof(argv[i + 1]); } else if (strcmp(argv[i],"-S")==0) { param.select = atoi(argv[i + 1]); } else if (strcmp(argv[i],"-R")==0) { param.recover_num = atoi(argv[i + 1]); } else if (strcmp(argv[i],"-pct")==0) { param.recover_pct = atof(argv[i + 1]); if(param.recover_pct>1) param.recover_pct/=100.00; } else if (strcmp(argv[i],"-base")==0) { param.base = atoi(argv[i + 1]); } else if (strcmp(argv[i],"-rand")==0) { param.randpermute = atoi(argv[i + 1]); } else if (strcmp(argv[i],"-phases")==0) { param.phases = atoi(argv[i + 1]); } else if (strcmp(argv[i],"-per-process-mem")==0) { param.perProcessMem = atoi(argv[i + 1]); } else if (strcmp(argv[i],"--single-precision")==0) { param.isDoublePrecision = false; } else if (strcmp(argv[i],"--32bit-index")==0) { param.is64bInt = false; } } if(param.ofilename=="") // construct output file name if it is not provided { param.ofilename = param.ifilename + ".hipmcl"; } } void ShowOptions() { ostringstream runinfo; runinfo << "Usage: ./hipmcl -M -I (required)" << endl; runinfo << "======================================" << endl; runinfo << " Detail parameter options " << endl; runinfo << "======================================" << endl; runinfo << "Input/Output file" << endl; runinfo << " -M (mandatory)" << endl; runinfo << " --matrix-market : if provided, the input file is in the matrix market format (default: the file is in labeled triples format)" << endl; runinfo << " -base (default: 1) " << endl; runinfo << " -o (default: input_file_name.hipmcl )" << endl; runinfo << "Inflation" << endl; runinfo << "-I (mandatory)\n"; runinfo << "Preprocessing" << endl; runinfo << " -rand (default:0)\n"; runinfo << " --remove-isolated : if provided, remove isolated vertices (default: don't remove isolated vertices)\n"; runinfo << "Pruning" << endl; runinfo << " -p (default: 1/10000)\n"; runinfo << " -R (default: 1400)\n"; runinfo << " -pct (default: 90)\n"; runinfo << " -S (default: 1100)\n"; runinfo << "HipMCL optimization" << endl; runinfo << " -phases (default:1)\n"; runinfo << " -per-process-mem (default:0, number of phases is not estimated)\n"; runinfo << " --single-precision (if not provided, use double precision floating point numbers)\n" << endl; runinfo << " --32bit-index (if not provided, use 64 bit indexing for vertex ids)\n" << endl; runinfo << "Debugging" << endl; runinfo << " --show: show information about matrices after major steps (default: do not show matrices)" << endl; runinfo << "======================================" << endl; runinfo << " Few examples " << endl; runinfo << "======================================" << endl; runinfo << "Example with with a graph in labeled triples format on a laptop with 8GB memory and 8 cores:\nexport OMP_NUM_THREADS=8\nbin/hipmcl -M data/sevenvertexgraph.txt -I 2 -per-process-mem 8" << endl; runinfo << "Same as above with 4 processes and 2 theaded per process cores:\nexport OMP_NUM_THREADS=2\nmpirun -np 4 bin/hipmcl -M data/sevenvertexgraph.txt -I 2 -per-process-mem 2" << endl; runinfo << "Example with a graph in matrix market format:\nbin/hipmcl -M data/sevenvertex.mtx --matrix-market -base 1 -I 2 -per-process-mem 8" << endl; runinfo << "Example on the NERSC/Edison system with 16 nodes and 24 threads per node: \nsrun -N 16 -n 16 -c 24 bin/hipmcl -M data/hep-th.mtx --matrix-market -base 1 -per-process-mem 64 -o hep-th.hipmcl" << endl; SpParHelper::Print(runinfo.str()); } // base: base of items // clusters are always numbered 0-based template FullyDistVec Interpret(SpParMat & A) { IT nCC; // A is a directed graph // symmetricize A SpParMat AT = A; AT.Transpose(); A += AT; FullyDistVec cclabels = CC(A, nCC); return cclabels; } template void MakeColStochastic(SpParMat & A) { FullyDistVec colsums = A.Reduce(Column, plus(), 0.0); colsums.Apply(safemultinv()); A.DimApply(Column, colsums, multiplies()); // scale each "Column" with the given vector } template NT Chaos(SpParMat & A) { // sums of squares of columns FullyDistVec colssqs = A.Reduce(Column, plus(), 0.0, bind2nd(exponentiate(), 2)); // Matrix entries are non-negative, so max() can use zero as identity FullyDistVec colmaxs = A.Reduce(Column, maximum(), 0.0); colmaxs -= colssqs; // multiplu by number of nonzeros in each column FullyDistVec nnzPerColumn = A.Reduce(Column, plus(), 0.0, [](NT val){return 1.0;}); colmaxs.EWiseApply(nnzPerColumn, multiplies()); return colmaxs.Reduce(maximum(), 0.0); } template void Inflate(SpParMat & A, double power) { A.Apply(bind2nd(exponentiate(), power)); } // default adjustloop setting // 1. Remove loops // 2. set loops to max of all arc weights template void AdjustLoops(SpParMat & A) { A.RemoveLoops(); FullyDistVec colmaxs = A.Reduce(Column, maximum(), numeric_limits::min()); A.Apply([](NT val){return val==numeric_limits::min() ? 1.0 : val;}); // for isolated vertices A.AddLoops(colmaxs); ostringstream outs; outs << "Adjusting loops" << endl; SpParHelper::Print(outs.str()); } template void RemoveIsolated(SpParMat & A, HipMCLParam & param) { ostringstream outs; FullyDistVec ColSums = A.Reduce(Column, plus(), 0.0); FullyDistVec nonisov = ColSums.FindInds(bind2nd(greater(), 0)); IT numIsolated = A.getnrow() - nonisov.TotalLength(); outs << "Number of isolated vertices: " << numIsolated << endl; SpParHelper::Print(outs.str()); A(nonisov, nonisov, true); SpParHelper::Print("Removed isolated vertices.\n"); if(param.show) { A.PrintInfo(); } } //TODO: handle reordered cluster ids template void RandPermute(SpParMat & A, HipMCLParam & param) { // randomly permute for load balance if(A.getnrow() == A.getncol()) { FullyDistVec p( A.getcommgrid()); p.iota(A.getnrow(), 0); p.RandPerm(); (A)(p,p,true);// in-place permute to save memory SpParHelper::Print("Applied symmetric permutation.\n"); } else { SpParHelper::Print("Rectangular matrix: Can not apply symmetric permutation.\n"); } } template FullyDistVec HipMCL(SpParMat & A, HipMCLParam & param) { if(param.remove_isolated) RemoveIsolated(A, param); if(param.randpermute) RandPermute(A, param); // Adjust self loops AdjustLoops(A); // Make stochastic MakeColStochastic(A); SpParHelper::Print("Made stochastic\n"); if(param.show) { A.PrintInfo(); } // chaos doesn't make sense for non-stochastic matrices // it is in the range {0,1} for stochastic matrices NT chaos = 1; int it=1; double tInflate = 0; double tExpand = 0; typedef PlusTimesSRing PTFF; // while there is an epsilon improvement while( chaos > EPS) { double t1 = MPI_Wtime(); //A.Square() ; // expand A = MemEfficientSpGEMM(A, A, param.phases, param.prunelimit, (IT)param.select, (IT)param.recover_num, param.recover_pct, param.kselectVersion, param.perProcessMem); MakeColStochastic(A); tExpand += (MPI_Wtime() - t1); if(param.show) { SpParHelper::Print("After expansion\n"); A.PrintInfo(); } chaos = Chaos(A); double tInflate1 = MPI_Wtime(); Inflate(A, param.inflation); MakeColStochastic(A); tInflate += (MPI_Wtime() - tInflate1); if(param.show) { SpParHelper::Print("After inflation\n"); A.PrintInfo(); } double newbalance = A.LoadImbalance(); double t3=MPI_Wtime(); stringstream s; s << "Iteration# " << setw(3) << it << " : " << " chaos: " << setprecision(3) << chaos << " load-balance: "<< newbalance << " Time: " << (t3-t1) << endl; SpParHelper::Print(s.str()); it++; } double tcc1 = MPI_Wtime(); // bool does not work because A.AddLoops(1) can not create a fullydist vector with Bool? // write a promote train for float SpParMat> ADouble = A; FullyDistVec cclabels = Interpret(ADouble); double tcc = MPI_Wtime() - tcc1; #ifdef TIMING int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank==0) { cout << "================detailed timing==================" << endl; cout << "Expansion: " << mcl_Abcasttime + mcl_Bbcasttime + mcl_localspgemmtime + mcl_multiwaymergetime << endl; cout << " Abcast= " << mcl_Abcasttime << endl; cout << " Bbcast= " << mcl_Bbcasttime << endl; cout << " localspgemm= " << mcl_localspgemmtime << endl; cout << " multiwaymergetime= "<< mcl_multiwaymergetime << endl; cout << "Prune: " << mcl_kselecttime + mcl_prunecolumntime << endl; cout << " kselect= " << mcl_kselecttime << endl; cout << " prunecolumn= " << mcl_prunecolumntime << endl; cout << "Inflation " << tInflate << endl; cout << "Component: " << tcc << endl; cout << "File I/O: " << tIO << endl; cout << "=================================================" << endl; } #endif return cclabels; } template void Symmetricize(SpParMat & A) { SpParMat AT = A; AT.Transpose(); if(!(AT == A)) { SpParHelper::Print("Symmatricizing an unsymmetric input matrix.\n"); A += AT; } } template void MainBody(HipMCLParam & param) { SpParMat> A(MPI_COMM_WORLD); // construct object FullyDistVec > vtxLabels(A.getcommgrid()); // read file SpParHelper::Print("Reading input file......\n"); double tIO1 = MPI_Wtime(); if(param.isInputMM) A.ParallelReadMM(param.ifilename, param.base, maximum()); // if base=0, then it is implicitly converted to Boolean false else // default labeled triples format vtxLabels = A.ReadGeneralizedTuples(param.ifilename, maximum()); tIO = MPI_Wtime() - tIO1; ostringstream outs; outs << " : took " << tIO << " seconds" << endl; SpParHelper::Print(outs.str()); // Symmetricize the matrix only if needed Symmetricize(A); double balance = A.LoadImbalance(); outs.str(""); outs.clear(); if(!param.is64bInt) // don't compute nnz because it may overflow { GIT nnz = A.getnnz(); GIT nv = A.getnrow(); outs << "Number of vertices: " << nv << " number of edges: "<< nnz << endl; } outs << "Load balance: " << balance << endl; SpParHelper::Print(outs.str()); if(param.show) { A.PrintInfo(); } #ifdef TIMING mcl_Abcasttime = 0; mcl_Bbcasttime = 0; mcl_localspgemmtime = 0; mcl_multiwaymergetime = 0; mcl_kselecttime = 0; mcl_prunecolumntime = 0; #endif double tstart = MPI_Wtime(); // Run HipMCL FullyDistVec culstLabels = HipMCL(A, param); //culstLabels.ParallelWrite(param.ofilename, param.base); // clusters are always numbered 0-based if(param.isInputMM) WriteMCLClusters(param.ofilename, culstLabels, param.base); else WriteMCLClusters(param.ofilename, culstLabels, vtxLabels); GIT nclusters = culstLabels.Reduce(maximum(), (GIT) 0 ) ; nclusters ++; // because of zero based indexing for clusters double tend = MPI_Wtime(); stringstream s2; s2 << "Number of clusters: " << nclusters << endl; s2 << "Total time: " << (tend-tstart) << endl; s2 << "=================================================\n" << endl ; SpParHelper::Print(s2.str()); } int main(int argc, char* argv[]) { int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nthreads = 1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); HipMCLParam param; // initialize parameters to default values InitParam(param); // Populate parameters from command line options ProcessParam(argc, argv, param); // check if mandatory arguments are provided if(param.ifilename=="" || param.inflation == 0.0) { SpParHelper::Print("Required options are missing.\n"); ShowOptions(); MPI_Finalize(); return -1; } if(myrank == 0) { cout << "\nProcess Grid used (pr x pc x threads): " << sqrt(nprocs) << " x " << sqrt(nprocs) << " x " << nthreads << endl; } // show parameters used to run HipMCL ShowParam(param); if(param.perProcessMem==0) { if(myrank == 0) { cout << "******** Number of phases will not be estimated as -per-process-mem option is supplied. It is highly recommended that you provide -per-process-mem option for large-scale runs. *********** " << endl; } } { if(param.isDoublePrecision) if(param.is64bInt) // default case MainBody(param); else MainBody(param); else if(param.is64bInt) MainBody(param); else MainBody(param); // memory effecient case } // make sure the destructors for all objects are called before MPI::Finalize() MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/DirOptBFS.cpp000644 000765 000024 00000063360 13271404146 022324 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #define DETERMINISTIC #define BOTTOMUPTIME #include #include #include #include #include #include #include #include #include #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #include #endif // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include double cblas_alltoalltime; double cblas_allgathertime; double cblas_mergeconttime; double cblas_transvectime; double cblas_localspmvtime; double cblas_ewisemulttime; double bottomup_sendrecv; double bottomup_allgather; double bottomup_total; double bottomup_convert; double bu_local; double bu_update; double bu_rotate; int cblas_splits; #include "CombBLAS/CombBLAS.h" using namespace combblas; using namespace std; #define ITERS 64 #define EDGEFACTOR 16 // 64-bit floor(log2(x)) function // note: least significant bit is the "zeroth" bit // pre: v > 0 unsigned int highestbitset(uint64_t v) { // b in binary is {10,1100, 11110000, 1111111100000000 ...} const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; const unsigned int S[] = {1, 2, 4, 8, 16, 32}; int i; unsigned int r = 0; // result of log2(v) will go here for (i = 5; i >= 0; i--) { if (v & b[i]) // highestbitset is on the left half (i.e. v > S[i] for sure) { v >>= S[i]; r |= S[i]; } } return r; } template bool from_string(T & t, const string& s, ios_base& (*f)(ios_base&)) { istringstream iss(s); return !(iss >> f >> t).fail(); } template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } /** * Binary function to prune the previously discovered vertices from the current frontier * When used with EWiseApply(SparseVec V, DenseVec W,...) we get the 'exclude = false' effect of EWiseMult **/ struct prunediscovered: public binary_function { int64_t operator()(int64_t x, const int64_t & y) const { return ( y == -1 ) ? x: -1; } }; int main(int argc, char* argv[]) { #ifdef THREADED cblas_splits = omp_get_max_threads(); #else cblas_splits = 1; #endif int nprocs, myrank; #ifdef _OPENMP int provided, flag, claimed; MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided ); MPI_Is_thread_main( &flag ); if (!flag) SpParHelper::Print("This thread called init_thread but Is_thread_main gave false\n"); MPI_Query_thread( &claimed ); if (claimed != provided) SpParHelper::Print("Query thread gave different thread level than requested\n"); #else MPI_Init(&argc, &argv); #endif MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 2) { if(myrank == 0) { cout << "Usage: ./dobfs " << endl; cout << "Example: ./dobfs 25" << endl; } MPI_Finalize(); return -1; } { typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_s32p64; // sequentially use 32-bits for local matrices, but parallel semantics are 64-bits typedef SpParMat < int64_t, int, SpDCCols > PSpMat_s32p64_Int; // similarly mixed, but holds integers as upposed to booleans // Declare objects PSpMat_Bool A; PSpMat_s32p64 Aeff; PSpMat_s32p64 ALocalT; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); FullyDistVec degrees(fullWorld); // degrees of vertices (including multi-edges and self-loops) FullyDistVec nonisov(fullWorld); // id's of non-isolated (connected) vertices unsigned scale; OptBuf optbuf; // let indices be 32-bits scale = static_cast(atoi(argv[1])); ostringstream outs; outs << "Forcing scale to : " << scale << endl; SpParHelper::Print(outs.str()); SpParHelper::Print("Using fast vertex permutations; skipping edge permutations (like v2.1)\n"); // this is an undirected graph, so A*x does indeed BFS double initiator[4] = {.57, .19, .19, .05}; double t01 = MPI_Wtime(); double t02; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true ); // generate packed edges SpParHelper::Print("Generated renamed edge lists\n"); t02 = MPI_Wtime(); ostringstream tinfo; tinfo << "Generation took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); // Start Kernel #1 MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); // conversion from distributed edge list, keeps self-loops, sums duplicates PSpMat_s32p64_Int * G = new PSpMat_s32p64_Int(*DEL, false); delete DEL; // free memory before symmetricizing SpParHelper::Print("Created Sparse Matrix (with int32 local indices and values)\n"); MPI_Barrier(MPI_COMM_WORLD); double redts = MPI_Wtime(); G->Reduce(degrees, Row, plus(), static_cast(0)); // Identity is 0 MPI_Barrier(MPI_COMM_WORLD); double redtf = MPI_Wtime(); ostringstream redtimeinfo; redtimeinfo << "Calculated degrees in " << redtf-redts << " seconds" << endl; SpParHelper::Print(redtimeinfo.str()); A = PSpMat_Bool(*G); // Convert to Boolean delete G; int64_t removed = A.RemoveLoops(); ostringstream loopinfo; loopinfo << "Converted to Boolean and removed " << removed << " loops" << endl; SpParHelper::Print(loopinfo.str()); A.PrintInfo(); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(A.getcommgrid()); A.Reduce(*ColSums, Column, plus(), static_cast(0)); A.Reduce(*RowSums, Row, plus(), static_cast(0)); SpParHelper::Print("Reductions done\n"); ColSums->EWiseApply(*RowSums, plus()); SpParHelper::Print("Intersection of colsums and rowsums found\n"); delete RowSums; nonisov = ColSums->FindInds(bind2nd(greater(), 0)); // only the indices of non-isolated vertices delete ColSums; nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) SpParHelper::Print("Found non-isolated vertices\n"); A.PrintInfo(); #ifndef NOPERMUTE A(nonisov, nonisov, true); // in-place permute to save memory SpParHelper::Print("Dropped isolated vertices from input\n"); A.PrintInfo(); #endif Aeff = PSpMat_s32p64(A); // Convert to 32-bit local integers A.FreeMemory(); SpParHelper::Print("Converted to 32-bit integers\n"); Symmetricize(Aeff); // A += A'; SpParHelper::Print("Symmetricized\n"); Aeff.OptimizeForGraph500(optbuf); // Should be called before threading is activated ALocalT = PSpMat_s32p64(Aeff.seq().TransposeConstPtr(), Aeff.getcommgrid()); // this should be copied before the threading is activated #ifdef THREADED tinfo << "Threading activated with " << cblas_splits << " threads" << endl; SpParHelper::Print(tinfo.str()); Aeff.ActivateThreading(cblas_splits); #endif Aeff.PrintInfo(); MPI_Barrier(MPI_COMM_WORLD); double t2=MPI_Wtime(); ostringstream k1timeinfo; k1timeinfo << (t2-t1) - (redtf-redts) << " seconds elapsed for Kernel #1" << endl; SpParHelper::Print(k1timeinfo.str()); Aeff.PrintInfo(); float balance = Aeff.LoadImbalance(); ostringstream lbout; lbout << "Load balance: " << balance << endl; SpParHelper::Print(lbout.str()); MPI_Barrier(MPI_COMM_WORLD); t1 = MPI_Wtime(); // Now that every remaining vertex is non-isolated, randomly pick ITERS many of them as starting vertices #ifndef NOPERMUTE degrees = degrees(nonisov); // fix the degrees array too degrees.PrintInfo("Degrees array"); #endif // degrees.DebugPrint(); FullyDistVec Cands(A.getcommgrid(), ITERS, 0); double nver = (double) degrees.TotalLength(); #ifdef DETERMINISTIC uint64_t seed = 1383098845; #else uint64_t seed= time(NULL); #endif MTRand M(seed); // generate random numbers with Mersenne Twister vector loccands(ITERS); vector loccandints(ITERS); if(myrank == 0) { for(int i=0; i(cout," ")); cout << endl; transform(loccands.begin(), loccands.end(), loccands.begin(), bind2nd( multiplies(), nver )); for(int i=0; i(loccands[i]); copy(loccandints.begin(), loccandints.end(), ostream_iterator(cout," ")); cout << endl; } MPI_Bcast(&(loccandints[0]), ITERS, MPIType(),0,MPI_COMM_WORLD); for(int i=0; i 1 { cblas_allgathertime = 0; cblas_alltoalltime = 0; cblas_mergeconttime = 0; cblas_transvectime = 0; cblas_localspmvtime = 0; cblas_ewisemulttime = 0; bottomup_sendrecv = 0; bottomup_allgather = 0; bottomup_total = 0; bottomup_convert = 0; bu_local = 0; bu_update = 0; bu_rotate = 0; MPI_Pcontrol(1,"BFS"); double MTEPS[ITERS]; double INVMTEPS[ITERS]; double TIMES[ITERS]; double EDGES[ITERS]; for(int i=0; i grid, IT globallen, NT initval); FullyDistVec parents ( Aeff.getcommgrid(), Aeff.getncol(), (int64_t) -1); // identity is -1 // FullyDistSpVec ( shared_ptr grid, IT glen); FullyDistSpVec fringe(Aeff.getcommgrid(), Aeff.getncol()); // numerical values are stored 0-based ostringstream devout; devout.setf(ios::fixed); MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); int64_t num_edges = Aeff.getnnz(); int64_t num_nodes = Aeff.getncol(); int64_t up_cutoff = num_edges / 20; int64_t down_cutoff = (((double) num_nodes) * ((double)num_nodes)) / ((double) num_edges * 12.0); devout << "param " << num_nodes << " vertices with " << num_edges << " edges" << endl; devout << up_cutoff << " up and " << down_cutoff << " down" << endl; fringe.SetElement(Cands[i], Cands[i]); parents.SetElement(Cands[i], Cands[i]); int iterations = 0; BitMapFringe bm_fringe(fringe.getcommgrid(), fringe); BitMapCarousel done(Aeff.getcommgrid(), parents.TotalLength(), bm_fringe.GetSubWordDisp()); SpDCCols::SpColIter *starts = CalcSubStarts(ALocalT, fringe, done); int64_t fringe_size = fringe.getnnz(); int64_t last_fringe_size = 0; double pred_start = MPI_Wtime(); fringe.Apply(myset(1)); int64_t pred = EWiseMult(fringe, degrees, false, (int64_t) 0).Reduce(plus(), (int64_t) 0); double pred_end = MPI_Wtime(); devout << " s" << setw(15) << pred << setw(15) << setprecision(5) << (pred_end - pred_start) << endl; cblas_ewisemulttime += (pred_end - pred_start); while(fringe_size > 0) { if ((pred > up_cutoff) && (last_fringe_size < fringe_size)) { // Bottom-up MPI_Barrier(MPI_COMM_WORLD); double conv_start = MPI_Wtime(); done.LoadVec(parents); bm_fringe.LoadFromSpVec(fringe); double conv_end = MPI_Wtime(); devout << " c" << setw(30) << setprecision(5) << (conv_end - conv_start) << endl; bottomup_convert += (conv_end - conv_start); while (fringe_size > 0) { double step_start = MPI_Wtime(); BottomUpStep(ALocalT, fringe, bm_fringe, parents, done, starts); double step_end = MPI_Wtime(); devout << setw(2) << iterations << "u" << setw(15) << fringe_size << setprecision(5) << setw(15) << (step_end-step_start) << endl; bottomup_total += (step_end-step_start); iterations++; last_fringe_size = fringe_size; fringe_size = bm_fringe.GetNumSet(); if ((fringe_size < down_cutoff) && (last_fringe_size > fringe_size)) { conv_start = MPI_Wtime(); bm_fringe.UpdateSpVec(fringe); conv_end = MPI_Wtime(); devout << " c" << setw(30) << setprecision(5) << (conv_end - conv_start) << endl; bottomup_convert += (conv_end - conv_start); break; } } } else { // Top-down double step_start = MPI_Wtime(); fringe.setNumToInd(); fringe = SpMV(Aeff, fringe,optbuf); double ewise_start = MPI_Wtime(); fringe = EWiseMult(fringe, parents, true, (int64_t) -1); parents.Set(fringe); double step_end = MPI_Wtime(); devout << setw(2) << iterations << "d" << setw(15) << fringe.getnnz() << setw(15) << setprecision(5) << (step_end-step_start) << endl; cblas_ewisemulttime += (step_end - ewise_start); pred_start = MPI_Wtime(); fringe.Apply(myset(1)); pred = EWiseMult(fringe, degrees, false, (int64_t) 0).Reduce(plus(), (int64_t) 0); pred_end = MPI_Wtime(); devout << " s" << setw(15) << pred << setw(15) << setprecision(5) << (pred_end - pred_start) << endl; cblas_ewisemulttime += (pred_end - pred_start); iterations++; last_fringe_size = fringe_size; fringe_size = fringe.getnnz(); } } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); delete[] starts; SpParHelper::Print(devout.str()); FullyDistSpVec parentsp = parents.Find(bind2nd(greater(), -1)); parentsp.Apply(myset(1)); // we use degrees on the directed graph, so that we don't count the reverse edges in the teps score int64_t nedges = EWiseMult(parentsp, degrees, false, (int64_t) 0).Reduce(plus(), (int64_t) 0); int64_t nverts = parentsp.Reduce(plus(), (int64_t) 0); ostringstream outnew; outnew << i << "th starting vertex was " << Cands[i] << endl; outnew << "Number iterations: " << iterations << endl; outnew << "Number of vertices found: " << nverts << endl; outnew << "Number of edges traversed: " << nedges << endl; outnew << "BFS time: " << t2-t1 << " seconds" << endl; outnew << "MTEPS: " << static_cast(nedges) / (t2-t1) / 1000000.0 << endl; outnew << "Total communication (average so far): " << (cblas_allgathertime + cblas_alltoalltime) / (i+1) << endl; TIMES[i] = t2-t1; EDGES[i] = nedges; MTEPS[i] = static_cast(nedges) / (t2-t1) / 1000000.0; SpParHelper::Print(outnew.str()); } MPI_Pcontrol(-1,"BFS"); SpParHelper::Print("Finished\n"); #ifdef TIMING double * bu_total, *bu_ag_all, *bu_sr_all, *bu_convert, *td_ag_all, *td_a2a_all, *td_tv_all, *td_mc_all, *td_spmv_all, *td_ewm_all; if(myrank == 0) { bu_total = new double[nprocs]; bu_ag_all = new double[nprocs]; bu_sr_all = new double[nprocs]; bu_convert = new double[nprocs]; td_ag_all = new double[nprocs]; td_a2a_all = new double[nprocs]; td_tv_all = new double[nprocs]; td_mc_all = new double[nprocs]; td_spmv_all = new double[nprocs]; td_ewm_all = new double[nprocs]; } bottomup_allgather /= static_cast(ITERS); bottomup_sendrecv /= static_cast(ITERS); bottomup_total /= static_cast(ITERS); bottomup_convert /= static_cast(ITERS); // conversion not included in total time cblas_allgathertime /= static_cast(ITERS); cblas_alltoalltime /= static_cast(ITERS); cblas_transvectime /= static_cast(ITERS); cblas_mergeconttime /= static_cast(ITERS); cblas_localspmvtime /= static_cast(ITERS); cblas_ewisemulttime /= static_cast(ITERS); MPI_Gather(&bottomup_convert, 1, MPI_DOUBLE, bu_convert, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&bottomup_total, 1, MPI_DOUBLE, bu_total, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&bottomup_allgather, 1, MPI_DOUBLE, bu_ag_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&bottomup_sendrecv, 1, MPI_DOUBLE, bu_sr_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_allgathertime, 1, MPI_DOUBLE, td_ag_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_alltoalltime, 1, MPI_DOUBLE, td_a2a_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_transvectime, 1, MPI_DOUBLE, td_tv_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_mergeconttime, 1, MPI_DOUBLE, td_mc_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_localspmvtime, 1, MPI_DOUBLE, td_spmv_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_ewisemulttime, 1, MPI_DOUBLE, td_ewm_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); double bu_local_total = 0; double bu_update_total = 0; double bu_rotate_total = 0; MPI_Allreduce(&bu_local, &bu_local_total, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce(&bu_update, &bu_update_total, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce(&bu_rotate, &bu_rotate_total, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); if(myrank == 0) { cout << "BU Local: " << bu_local_total/nprocs << endl; cout << "BU Update: " << bu_update_total/nprocs << endl; cout << "BU Rotate: " << bu_rotate_total/nprocs << endl; vector total_time(nprocs, 0); for(int i=0; i< nprocs; ++i) // find the mean performing guy total_time[i] += bu_total[i] + bu_convert[i] + td_ag_all[i] + td_a2a_all[i] + td_tv_all[i] + td_mc_all[i] + td_spmv_all[i] + td_ewm_all[i]; vector permutation = SpHelper::find_order(total_time); size_t smallest = permutation[0]; size_t largest = permutation[nprocs-1]; size_t median = permutation[nprocs/2]; cout << "TOTAL (accounted) MEAN: " << accumulate( total_time.begin(), total_time.end(), 0.0 )/ static_cast (nprocs) << endl; cout << "TOTAL (accounted) MAX: " << total_time[0] << endl; cout << "TOTAL (accounted) MIN: " << total_time[nprocs-1] << endl; cout << "TOTAL (accounted) MEDIAN: " << total_time[nprocs/2] << endl; cout << "-------------------------------" << endl; cout << "Convert median: " << bu_convert[median] << endl; cout << "Bottom-up allgather median: " << bu_ag_all[median] << endl; cout << "Bottom-up send-recv median: " << bu_sr_all[median] << endl; cout << "Bottom-up compute median: " << bu_total[median] - (bu_ag_all[median] + bu_sr_all[median]) << endl; cout << "Top-down allgather median: " << td_ag_all[median] << endl; cout << "Top-down all2all median: " << td_a2a_all[median] << endl; cout << "Top-down transposevector median: " << td_tv_all[median] << endl; cout << "Top-down mergecontributions median: " << td_mc_all[median] << endl; cout << "Top-down spmsv median: " << td_spmv_all[median] << endl; cout << "-------------------------------" << endl; cout << "Convert MEAN: " << accumulate( bu_convert, bu_convert+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "Bottom-up total MEAN: " << accumulate( bu_total, bu_total+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "Bottom-up allgather MEAN: " << accumulate( bu_ag_all, bu_ag_all+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "Bottom-up send-recv MEAN: " << accumulate( bu_sr_all, bu_sr_all+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "Top-down allgather MEAN: " << accumulate( td_ag_all, td_ag_all+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "Top-down all2all MEAN: " << accumulate( td_a2a_all, td_a2a_all+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "Top-down transposevector MEAN: " << accumulate( td_tv_all, td_tv_all+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "Top-down mergecontributions MEAN: " << accumulate( td_mc_all, td_mc_all+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "Top-down spmsv MEAN: " << accumulate( td_spmv_all, td_spmv_all+nprocs, 0.0 )/ static_cast (nprocs) << endl; cout << "-------------------------------" << endl; cout << "Bottom-up allgather fastest: " << bu_ag_all[smallest] << endl; cout << "Bottom-up send-recv fastest: " << bu_sr_all[smallest] << endl; cout << "Bottom-up compute fastest: " << bu_total[smallest] - (bu_ag_all[smallest] + bu_sr_all[smallest]) << endl; cout << "Top-down allgather fastest: " << td_ag_all[smallest] << endl; cout << "Top-down all2all fastest: " << td_a2a_all[smallest] << endl; cout << "Top-down transposevector fastest: " << td_tv_all[smallest] << endl; cout << "Top-down mergecontributions fastest: " << td_mc_all[smallest] << endl; cout << "Top-down spmsv fastest: " << td_spmv_all[smallest] << endl; cout << "-------------------------------" << endl; cout << "Bottom-up allgather slowest: " << bu_ag_all[largest] << endl; cout << "Bottom-up send-recv slowest: " << bu_sr_all[largest] << endl; cout << "Bottom-up compute slowest: " << bu_total[largest] - (bu_ag_all[largest] + bu_sr_all[largest]) << endl; cout << "Top-down allgather slowest: " << td_ag_all[largest] << endl; cout << "Top-down all2all slowest: " << td_a2a_all[largest] << endl; cout << "Top-down transposevector slowest: " << td_tv_all[largest] << endl; cout << "Top-down mergecontributions slowest: " << td_mc_all[largest] << endl; cout << "Top-down spmsv slowest: " << td_spmv_all[largest] << endl; } #endif ostringstream os; sort(EDGES, EDGES+ITERS); os << "--------------------------" << endl; os << "Min nedges: " << EDGES[0] << endl; os << "First Quartile nedges: " << (EDGES[(ITERS/4)-1] + EDGES[ITERS/4])/2 << endl; os << "Median nedges: " << (EDGES[(ITERS/2)-1] + EDGES[ITERS/2])/2 << endl; os << "Third Quartile nedges: " << (EDGES[(3*ITERS/4) -1 ] + EDGES[3*ITERS/4])/2 << endl; os << "Max nedges: " << EDGES[ITERS-1] << endl; double mean = accumulate( EDGES, EDGES+ITERS, 0.0 )/ ITERS; vector zero_mean(ITERS); // find distances to the mean transform(EDGES, EDGES+ITERS, zero_mean.begin(), bind2nd( minus(), mean )); // self inner-product is sum of sum of squares double deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ); os << "Mean nedges: " << mean << endl; os << "STDDEV nedges: " << deviation << endl; os << "--------------------------" << endl; sort(TIMES,TIMES+ITERS); os << "Min time: " << TIMES[0] << " seconds" << endl; os << "First Quartile time: " << (TIMES[(ITERS/4)-1] + TIMES[ITERS/4])/2 << " seconds" << endl; os << "Median time: " << (TIMES[(ITERS/2)-1] + TIMES[ITERS/2])/2 << " seconds" << endl; os << "Third Quartile time: " << (TIMES[(3*ITERS/4)-1] + TIMES[3*ITERS/4])/2 << " seconds" << endl; os << "Max time: " << TIMES[ITERS-1] << " seconds" << endl; mean = accumulate( TIMES, TIMES+ITERS, 0.0 )/ ITERS; transform(TIMES, TIMES+ITERS, zero_mean.begin(), bind2nd( minus(), mean )); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ); os << "Mean time: " << mean << " seconds" << endl; os << "STDDEV time: " << deviation << " seconds" << endl; os << "--------------------------" << endl; sort(MTEPS, MTEPS+ITERS); os << "Min MTEPS: " << MTEPS[0] << endl; os << "First Quartile MTEPS: " << (MTEPS[(ITERS/4)-1] + MTEPS[ITERS/4])/2 << endl; os << "Median MTEPS: " << (MTEPS[(ITERS/2)-1] + MTEPS[ITERS/2])/2 << endl; os << "Third Quartile MTEPS: " << (MTEPS[(3*ITERS/4)-1] + MTEPS[3*ITERS/4])/2 << endl; os << "Max MTEPS: " << MTEPS[ITERS-1] << endl; transform(MTEPS, MTEPS+ITERS, INVMTEPS, safemultinv()); // returns inf for zero teps double hteps = static_cast(ITERS) / accumulate(INVMTEPS, INVMTEPS+ITERS, 0.0); os << "Harmonic mean of MTEPS: " << hteps << endl; transform(INVMTEPS, INVMTEPS+ITERS, zero_mean.begin(), bind2nd(minus(), 1/hteps)); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ) * (hteps*hteps); // harmonic_std_dev os << "Harmonic standard deviation of MTEPS: " << deviation << endl; SpParHelper::Print(os.str()); } } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/FilteredBFS.cpp000644 000765 000024 00000061316 13271404146 022660 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #define DETERMINISTIC #include "CombBLAS/CombBLAS.h" #include #include #include #include #include #include #include #include #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #include #endif #ifdef USE_PAPI #include #include "papi_combblas_global.h" #endif /* Global variables for timing */ double cblas_alltoalltime; double cblas_allgathertime; int cblas_splits; /* End global variables */ #include "TwitterEdge.h" #define MAX_ITERS 20000 #define EDGEFACTOR 16 #define ITERS 16 #define CC_LIMIT 100 #define PERCENTS 4 // testing with 4 different percentiles #define MINRUNS 4 //#define ONLYTIME // don't calculate TEPS using namespace std; using namespace combblas; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } #ifdef DETERMINISTIC MTRand GlobalMT(1); #else MTRand GlobalMT; // generate random numbers with Mersenne Twister #endif struct Twitter_obj_randomizer : public std::unary_function { const TwitterEdge operator()(const TwitterEdge & x) const { short mycount = 1; bool myfollow = 0; time_t mylatest = static_cast(GlobalMT.rand() * 10000); // random.randrange(0,10000) return TwitterEdge(mycount, myfollow, mylatest); } }; struct Twitter_materialize: public std::binary_function { bool operator()(const TwitterEdge & x, time_t sincedate) const { if(x.isRetwitter() && x.LastTweetBy(sincedate)) return false; // false if the edge is going to be kept else return true; // true if the edge is to be pruned } }; #ifdef USE_PAPI void CheckPAPI(int errorcode, char [] errorstring) { if (errorcode != PAPI_OK) { PAPI_perror(errorcode, errorstring, PAPI_MAX_STR_LEN); fprintf(stderr, "PAPI error (%d): %s\n", errorcode, errorstring); } } #endif int main(int argc, char* argv[]) { int nprocs, myrank; #ifdef _OPENMP int cblas_splits = omp_get_max_threads(); int provided, flag, claimed; MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided ); MPI_Is_thread_main( &flag ); if (!flag) SpParHelper::Print("This thread called init_thread but Is_thread_main gave false\n"); MPI_Query_thread( &claimed ); if (claimed != provided) SpParHelper::Print("Query thread gave different thread level than requested\n"); #else MPI_Init(&argc, &argv); int cblas_splits = 1; #endif MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); int MAXTRIALS; int retval; #ifdef USE_PAPI /* Initialize the PAPI library */ retval = PAPI_library_init(PAPI_VER_CURRENT); if (retval != PAPI_VER_CURRENT && retval > 0) { fprintf(stderr,"PAPI library version mismatch!\en"); exit(1); } retval = PAPI_is_initialized(); if (retval != PAPI_LOW_LEVEL_INITED) cout << "Not initialized" << endl; #endif if(argc < 3) { if(myrank == 0) { cout << "Usage: ./FilteredBFS (Optional: Double)" << endl; cout << "Example: ./FilteredBFS File twitter_small.txt Double" << endl; } MPI_Finalize(); return -1; } { typedef SpParMat < int64_t, TwitterEdge, SpDCCols > PSpMat_Twitter; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); // Declare objects PSpMat_Twitter A(fullWorld); FullyDistVec indegrees(fullWorld); // in-degrees of vertices (including multi-edges and self-loops) FullyDistVec oudegrees(fullWorld); // out-degrees of vertices (including multi-edges and self-loops) FullyDistVec degrees(fullWorld); // combined degrees of vertices (including multi-edges and self-loops) PSpMat_Bool * ABool; double t01 = MPI_Wtime(); if(string(argv[1]) == string("File")) // text|binary input option { SpParHelper::Print("Using real data, which we NEVER permute for load balance, also leaving isolated vertices as-is, if any\n"); SpParHelper::Print("This is because the input is assumed to be load balanced\n"); SpParHelper::Print("BFS is run on DIRECTED graph, hence hitting SCCs, and TEPS is unidirectional\n"); // ReadDistribute (const string & filename, int master, bool nonum, HANDLER handler, bool transpose, bool pario) // if nonum is true, then numerics are not supplied and they are assumed to be all 1's A.ReadDistribute(string(argv[2]), 0, false, TwitterReadSaveHandler(), true, true); // read it from file (and transpose on the fly) A.PrintInfo(); SpParHelper::Print("Read input\n"); ABool = new PSpMat_Bool(A); if(argc == 4 && string(argv[3]) == string("Double")) { MAXTRIALS = 2; } else { MAXTRIALS = 1; } } else if(string(argv[1]) == string("Gen")) { SpParHelper::Print("Using synthetic data, which we ALWAYS permute for load balance\n"); SpParHelper::Print("We only balance the original input, we don't repermute after each filter change\n"); SpParHelper::Print("BFS is run on UNDIRECTED graph, hence hitting CCs, and TEPS is bidirectional\n"); double initiator[4] = {.57, .19, .19, .05}; double t01 = MPI_Wtime(); double t02; DistEdgeList * DEL = new DistEdgeList(); unsigned scale = static_cast(atoi(argv[2])); ostringstream outs; outs << "Forcing scale to : " << scale << endl; SpParHelper::Print(outs.str()); // parameters: (double initiator[4], int log_numverts, int edgefactor, bool scramble, bool packed) DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true ); // generate packed edges SpParHelper::Print("Generated renamed edge lists\n"); ABool = new PSpMat_Bool(*DEL, false); int64_t removed = ABool->RemoveLoops(); ostringstream loopinfo; loopinfo << "Converted to Boolean and removed " << removed << " loops" << endl; SpParHelper::Print(loopinfo.str()); ABool->PrintInfo(); delete DEL; // free memory A = PSpMat_Twitter(*ABool); // any upcasting generates the default object MAXTRIALS = PERCENTS; // benchmarking } else { SpParHelper::Print("Not supported yet\n"); return 0; } double t02 = MPI_Wtime(); ostringstream tinfo; tinfo << "I/O (or generation) took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); // indegrees is sum along rows because A is loaded as "tranposed", similarly oudegrees is sum along columns ABool->PrintInfo(); ABool->Reduce(oudegrees, Column, plus(), static_cast(0)); ABool->Reduce(indegrees, Row, plus(), static_cast(0)); // indegrees_filt and oudegrees_filt is used for the real data FullyDistVec indegrees_filt(fullWorld); FullyDistVec oudegrees_filt(fullWorld); typedef FullyDistVec IntVec; // used for the synthetic data (symmetricized before randomization) FullyDistVec degrees_filt[4] = {IntVec(fullWorld), IntVec(fullWorld), IntVec(fullWorld), IntVec(fullWorld)}; int64_t keep[PERCENTS] = {100, 1000, 2500, 10000}; // ratio of edges kept in range (0, 10000) if(string(argv[1]) == string("File")) // if using synthetic data, no notion of out/in degrees after randomization exist { struct tm timeinfo; memset(&timeinfo, 0, sizeof(struct tm)); int year, month, day, hour, min, sec; year = 2009; month = 7; day = 1; hour = 0; min = 0; sec = 0; timeinfo.tm_year = year - 1900; // year is "years since 1900" timeinfo.tm_mon = month - 1 ; // month is in range 0...11 timeinfo.tm_mday = day; // range 1...31 timeinfo.tm_hour = hour; // range 0...23 timeinfo.tm_min = min; // range 0...59 timeinfo.tm_sec = sec; // range 0. time_t mysincedate = timegm(&timeinfo); PSpMat_Twitter B = A; B.Prune(bind2nd(Twitter_materialize(), mysincedate)); PSpMat_Bool BBool = B; BBool.PrintInfo(); BBool.Reduce(oudegrees_filt, Column, plus(), static_cast(0)); BBool.Reduce(indegrees_filt, Row, plus(), static_cast(0)); } degrees = indegrees; degrees.EWiseApply(oudegrees, plus()); SpParHelper::Print("All degrees calculated\n"); delete ABool; float balance = A.LoadImbalance(); ostringstream outs; outs << "Load balance: " << balance << endl; SpParHelper::Print(outs.str()); if(string(argv[1]) == string("Gen")) { // We symmetricize before we apply the random generator // Otherwise += will naturally add the random numbers together // hence will create artificially high-permeable filters Symmetricize(A); // A += A'; SpParHelper::Print("Symmetricized\n"); A.Apply(Twitter_obj_randomizer()); A.PrintInfo(); FullyDistVec * nonisov = new FullyDistVec(degrees.FindInds(bind2nd(greater(), 0))); SpParHelper::Print("Found (and permuted) non-isolated vertices\n"); nonisov->RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A(*nonisov, *nonisov, true); // in-place permute to save memory SpParHelper::Print("Dropped isolated vertices from input\n"); indegrees = indegrees(*nonisov); // fix the degrees arrays too oudegrees = oudegrees(*nonisov); degrees = degrees(*nonisov); delete nonisov; for (int i=0; i < PERCENTS; i++) { PSpMat_Twitter B = A; B.Prune(bind2nd(Twitter_materialize(), keep[i])); PSpMat_Bool BBool = B; BBool.PrintInfo(); float balance = B.LoadImbalance(); ostringstream outs; outs << "Load balance of " << static_cast(keep[i])/100 << "% filtered case: " << balance << endl; SpParHelper::Print(outs.str()); // degrees_filt[i] is by-default generated as permuted BBool.Reduce(degrees_filt[i], Column, plus(), static_cast(0)); // Column=Row since BBool is symmetric } } // real data is pre-balanced, because otherwise even the load balancing routine crushes due to load imbalance float balance_former = A.LoadImbalance(); ostringstream outs_former; outs_former << "Load balance: " << balance_former << endl; SpParHelper::Print(outs_former.str()); MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); FullyDistVec Cands(MAX_ITERS, 0); double nver = (double) degrees.TotalLength(); Cands.SelectCandidates(nver); for(int trials =0; trials < MAXTRIALS; trials++) { if(string(argv[1]) == string("Gen")) { LatestRetwitterBFS::sincedate = keep[trials]; ostringstream outs; outs << "Initializing since date (only once) to " << LatestRetwitterBFS::sincedate << endl; SpParHelper::Print(outs.str()); } cblas_allgathertime = 0; cblas_alltoalltime = 0; double MTEPS[ITERS]; double INVMTEPS[ITERS]; double TIMES[ITERS]; double EDGES[ITERS]; double MPEPS[ITERS]; double INVMPEPS[ITERS]; int sruns = 0; // successful runs for(int i=0; i grid, IT globallen, NT initval); FullyDistVec parents ( A.getcommgrid(), A.getncol(), ParentType()); // FullyDistSpVec ( shared_ptr grid, IT glen); FullyDistSpVec fringe(A.getcommgrid(), A.getncol()); MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); #ifdef USE_PAPI char errorstring[PAPI_MAX_STR_LEN+1]; long long ptr2values[combblas_papi_num_events]; #endif fringe.SetElement(Cands[i], Cands[i]); parents.SetElement(Cands[i], ParentType(Cands[i])); // make root discovered int iterations = 0; while(fringe.getnnz() > 0) { #ifdef USE_PAPI vector< vector > papi_this_iterate(num_bfs_papi_labels); #endif fringe.ApplyInd(NumSetter); #ifdef USE_PAPI int errorcode = PAPI_start_counters(combblas_papi_events, combblas_papi_num_events); CheckPAPI(errorcode, errorstring); long_long papi_t_sta = PAPI_get_real_usec(); #endif // SpMV with sparse vector, optimizations disabled for generality SpMV(A, fringe, fringe, false); #ifdef USE_PAPI lond_long papi_t_end = PAPI_get_real_usec(); errorcode = PAPI_read_counters(ptr2values, combblas_papi_num_events); CheckPAPI(errorcode, errorstring); errorcode = PAPI_stop_counters(ptr2values, combblas_papi_num_events); papi_this_iterate[bfs_papi_enum.SpMV].resize(combblas_papi_num_events+1); // +1 for the time for(int k=0; k & V, const FullyDistVec & W, // _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, NU1 Vzero) fringe = EWiseApply(fringe, parents, getfringe(), keepinfrontier_f(), true, ParentType()); #ifdef USE_PAPI papi_t_end = PAPI_get_real_usec(); errorcode = PAPI_read_counters(ptr2values, combblas_papi_num_events); CheckPAPI(errorcode, errorstring); errorcode = PAPI_stop_counters(ptr2values, combblas_papi_num_events); papi_this_iterate[bfs_papi_enum.fringe_updt].resize(combblas_papi_num_events+1); // +1 for the time for(int k=0; k parentsp = parents.Find(isparentset()); parentsp.Apply(myset(ParentType(1))); #ifndef ONLYTIME FullyDistSpVec intraversed(fullWorld); FullyDistSpVec inprocessed(fullWorld); FullyDistSpVec outraversed(fullWorld); FullyDistSpVec ouprocessed(fullWorld); inprocessed = EWiseApply(parentsp, indegrees, seldegree(), passifthere(), true, ParentType()); ouprocessed = EWiseApply(parentsp, oudegrees, seldegree(), passifthere(), true, ParentType()); int64_t nedges, in_nedges, ou_nedges; if(string(argv[1]) == string("Gen")) { FullyDistSpVec traversed = EWiseApply(parentsp, degrees_filt[trials], seldegree(), passifthere(), true, ParentType()); nedges = traversed.Reduce(plus(), (int64_t) 0); } else { intraversed = EWiseApply(parentsp, indegrees_filt, seldegree(), passifthere(), true, ParentType()); outraversed = EWiseApply(parentsp, oudegrees_filt, seldegree(), passifthere(), true, ParentType()); in_nedges = intraversed.Reduce(plus(), (int64_t) 0); ou_nedges = outraversed.Reduce(plus(), (int64_t) 0); nedges = in_nedges + ou_nedges; // count birectional edges twice } int64_t in_nedges_processed = inprocessed.Reduce(plus(), (int64_t) 0); int64_t ou_nedges_processed = ouprocessed.Reduce(plus(), (int64_t) 0); int64_t nedges_processed = in_nedges_processed + ou_nedges_processed; // count birectional edges twice #else int64_t in_nedges, ou_nedges, nedges, in_nedges_processed, ou_nedges_processed, nedges_processed = 0; #endif if(parentsp.getnnz() > CC_LIMIT) { // intraversed.PrintInfo("Incoming edges traversed per vertex"); // intraversed.DebugPrint(); // outraversed.PrintInfo("Outgoing edges traversed per vertex"); // outraversed.DebugPrint(); #ifdef DEBUG parents.PrintInfo("Final parents array"); parents.DebugPrint(); #endif ostringstream outnew; outnew << i << "th starting vertex was " << Cands[i] << endl; outnew << "Number iterations: " << iterations << endl; outnew << "Number of vertices found: " << parentsp.getnnz() << endl; outnew << "Number of edges traversed in both directions: " << nedges << endl; if(string(argv[1]) == string("File")) outnew << "Number of edges traversed in one direction: " << ou_nedges << endl; outnew << "Number of edges processed in both directions: " << nedges_processed << endl; outnew << "Number of edges processed in one direction: " << ou_nedges_processed << endl; outnew << "BFS time: " << t2-t1 << " seconds" << endl; outnew << "MTEPS (bidirectional): " << static_cast(nedges) / (t2-t1) / 1000000.0 << endl; if(string(argv[1]) == string("File")) outnew << "MTEPS (unidirectional): " << static_cast(ou_nedges) / (t2-t1) / 1000000.0 << endl; outnew << "MPEPS (bidirectional): " << static_cast(nedges_processed) / (t2-t1) / 1000000.0 << endl; outnew << "MPEPS (unidirectional): " << static_cast(ou_nedges_processed) / (t2-t1) / 1000000.0 << endl; outnew << "Total communication (average so far): " << (cblas_allgathertime + cblas_alltoalltime) / (i+1) << endl; /* Write to PAPI */ #ifdef USE_PAPI ostringstream papiout; papiout << i << "th starting vertex was " << Cands[i] << endl; papiout << "Threshold is " << LatestRetwitterBFS::sincedate << endl; for(int i=0; i < iterations; i++) // over all spmv iterations in this BFS { papiout << "Iteration : " << i << endl; for(int j=0; j < bfs_papi_labels; ++j) { papiout << "Function : " << bfs_papi_labels[j] << endl; for(int k=0; k < combblas_papi_num_events; ++k) { papiout << combblas_event_names[k] << ":\t" << bfs_counters[i][j][k] << endl; } papiout << "Time (usec)" << ":\t" << bfs_counters[i][j][combblas_papi_num_events] << endl; } } SpParHelper::PrintFile(papiout.str(), "PAPIRES.txt"); #endif /* (End) Write to PAPI */ TIMES[sruns] = t2-t1; if(string(argv[1]) == string("Gen")) EDGES[sruns] = static_cast(nedges); else EDGES[sruns] = static_cast(ou_nedges); MTEPS[sruns] = EDGES[sruns] / (t2-t1) / 1000000.0; MPEPS[sruns++] = static_cast(nedges_processed) / (t2-t1) / 1000000.0; SpParHelper::Print(outnew.str()); } } if (sruns < MINRUNS) { SpParHelper::Print("Not enough valid runs done\n"); MPI_Finalize(); return 0; } ostringstream os; os << sruns << " valid runs done" << endl; os << "Connected component lower limite was " << CC_LIMIT << endl; os << "Per iteration communication times: " << endl; os << "AllGatherv: " << cblas_allgathertime / sruns << endl; os << "AlltoAllv: " << cblas_alltoalltime / sruns << endl; sort(EDGES, EDGES+sruns); os << "--------------------------" << endl; os << "Min nedges: " << EDGES[0] << endl; os << "Median nedges: " << (EDGES[(sruns/2)-1] + EDGES[sruns/2])/2 << endl; os << "Max nedges: " << EDGES[sruns-1] << endl; double mean = accumulate( EDGES, EDGES+sruns, 0.0 )/ sruns; vector zero_mean(sruns); // find distances to the mean transform(EDGES, EDGES+sruns, zero_mean.begin(), bind2nd( minus(), mean )); // self inner-product is sum of sum of squares double deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (sruns-1) ); os << "Mean nedges: " << mean << endl; os << "STDDEV nedges: " << deviation << endl; os << "--------------------------" << endl; sort(TIMES,TIMES+sruns); os << "Filter keeps " << static_cast(keep[trials])/100.0 << " percentage of edges" << endl; os << "Min time: " << TIMES[0] << " seconds" << endl; os << "Median time: " << (TIMES[(sruns/2)-1] + TIMES[sruns/2])/2 << " seconds" << endl; os << "Max time: " << TIMES[sruns-1] << " seconds" << endl; mean = accumulate( TIMES, TIMES+sruns, 0.0 )/ sruns; transform(TIMES, TIMES+sruns, zero_mean.begin(), bind2nd( minus(), mean )); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (sruns-1) ); os << "Mean time: " << mean << " seconds" << endl; os << "STDDEV time: " << deviation << " seconds" << endl; os << "--------------------------" << endl; sort(MTEPS, MTEPS+sruns); os << "Min MTEPS: " << MTEPS[0] << endl; os << "Median MTEPS: " << (MTEPS[(sruns/2)-1] + MTEPS[sruns/2])/2 << endl; os << "Max MTEPS: " << MTEPS[sruns-1] << endl; transform(MTEPS, MTEPS+sruns, INVMTEPS, safemultinv()); // returns inf for zero teps double hteps = static_cast(sruns) / accumulate(INVMTEPS, INVMTEPS+sruns, 0.0); os << "Harmonic mean of MTEPS: " << hteps << endl; transform(INVMTEPS, INVMTEPS+sruns, zero_mean.begin(), bind2nd(minus(), 1/hteps)); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (sruns-1) ) * (hteps*hteps); // harmonic_std_dev os << "Harmonic standard deviation of MTEPS: " << deviation << endl; sort(MPEPS, MPEPS+sruns); os << "Bidirectional Processed Edges per second (to estimate sustained BW)"<< endl; os << "Min MPEPS: " << MPEPS[0] << endl; os << "Median MPEPS: " << (MPEPS[(sruns/2)-1] + MPEPS[sruns/2])/2 << endl; os << "Max MPEPS: " << MPEPS[sruns-1] << endl; transform(MPEPS, MPEPS+sruns, INVMPEPS, safemultinv()); // returns inf for zero teps double hpeps = static_cast(sruns) / accumulate(INVMPEPS, INVMPEPS+sruns, 0.0); os << "Harmonic mean of MPEPS: " << hpeps << endl; transform(INVMPEPS, INVMPEPS+sruns, zero_mean.begin(), bind2nd(minus(), 1/hpeps)); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (sruns-1) ) * (hpeps*hpeps); // harmonic_std_dev os << "Harmonic standard deviation of MPEPS: " << deviation << endl; SpParHelper::Print(os.str()); } } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/BetwCent.cpp000644 000765 000024 00000020254 13271404146 022276 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #include #include #include #include #include // Required for stringstreams #include #include #include "CombBLAS/CombBLAS.h" using namespace combblas; using namespace std; // Simple helper class for declarations: Just the numerical type is templated // The index type and the sequential matrix type stays the same for the whole code // In this case, they are "int" and "SpDCCols" template class Dist { public: typedef SpDCCols < int, NT > DCCols; typedef SpParMat < int, NT, DCCols > MPI_DCCols; }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); typedef PlusTimesSRing PTBOOLINT; typedef PlusTimesSRing PTBOOLDOUBLE; if(argc < 4) { if(myrank == 0) { cout << "Usage: ./betwcent " << endl; cout << "Example: ./betwcent Data/ 15 128" << endl; cout << "Input file input.mtx should be under in matrix market format" << endl; cout << " should be a multiple of sqrt(p)" << endl; cout << "Because is for the overall matrix (similarly, is global as well) " << endl; } MPI_Finalize(); return -1; } { int K4Approx = atoi(argv[2]); int batchSize = atoi(argv[3]); string directory(argv[1]); string ifilename = "input.mtx"; ifilename = directory+"/"+ifilename; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); Dist::MPI_DCCols A(fullWorld); Dist::MPI_DCCols AT(fullWorld); // construct object AT.ParallelReadMM(ifilename, true, maximum()); // read it from file, note that we use the transpose of "input" data A = AT; A.Transpose(); int nPasses = (int) pow(2.0, K4Approx); int numBatches = (int) ceil( static_cast(nPasses)/ static_cast(batchSize)); // get the number of batch vertices for submatrix int subBatchSize = batchSize / (AT.getcommgrid())->GetGridCols(); int nBatchSize = subBatchSize * (AT.getcommgrid())->GetGridCols(); nPasses = numBatches * nBatchSize; // update the number of starting vertices if(batchSize % (AT.getcommgrid())->GetGridCols() > 0 && myrank == 0) { cout << "*** Batchsize is not evenly divisible by the grid dimension ***" << endl; cout << "*** Processing "<< nPasses <<" vertices instead"<< endl; } A.PrintInfo(); ostringstream tinfo; tinfo << "Batch processing will occur " << numBatches << " times, each processing " << nBatchSize << " vertices (overall)" << endl; SpParHelper::Print(tinfo.str()); vector candidates; // Only consider non-isolated vertices int vertices = 0; int vrtxid = 0; int nlocpass = numBatches * subBatchSize; while(vertices < nlocpass) { vector single; vector empty; single.push_back(vrtxid); // will return ERROR if vrtxid > N (the column dimension) int locnnz = ((AT.seq())(empty,single)).getnnz(); int totnnz; MPI_Allreduce( &locnnz, &totnnz, 1, MPI_INT, MPI_SUM, (AT.getcommgrid())->GetColWorld()); if(totnnz > 0) { candidates.push_back(vrtxid); ++vertices; } ++vrtxid; } SpParHelper::Print("Candidates chosen, precomputation finished\n"); double t1 = MPI_Wtime(); vector batch(subBatchSize); FullyDistVec bc(AT.getcommgrid(), A.getnrow(), 0.0); for(int i=0; i< numBatches; ++i) { for(int j=0; j< subBatchSize; ++j) { batch[j] = candidates[i*subBatchSize + j]; } Dist::MPI_DCCols fringe = AT.SubsRefCol(batch); // Create nsp by setting (r,i)=1 for the ith root vertex with label r // Inially only the diagonal processors have any nonzeros (because we chose roots so) Dist::DCCols * nsploc = new Dist::DCCols(); tuple * mytuples = NULL; if(AT.getcommgrid()->GetRankInProcRow() == AT.getcommgrid()->GetRankInProcCol()) { mytuples = new tuple[subBatchSize]; for(int k =0; kCreate( subBatchSize, AT.getlocalrows(), subBatchSize, mytuples); } else { nsploc->Create( 0, AT.getlocalrows(), subBatchSize, mytuples); } Dist::MPI_DCCols nsp(nsploc, AT.getcommgrid()); vector < Dist::MPI_DCCols * > bfs; // internally keeps track of depth SpParHelper::Print("Exploring via BFS...\n"); while( fringe.getnnz() > 0 ) { nsp += fringe; Dist::MPI_DCCols * level = new Dist::MPI_DCCols( fringe ); bfs.push_back(level); fringe = PSpGEMM(AT, fringe); fringe = EWiseMult(fringe, nsp, true); } // Apply the unary function 1/x to every element in the matrix // 1/x works because no explicit zeros are stored in the sparse matrix nsp Dist::MPI_DCCols nspInv = nsp; nspInv.Apply(bind1st(divides(), 1)); // create a dense matrix with all 1's DenseParMat bcu(1.0, AT.getcommgrid(), fringe.getlocalrows(), fringe.getlocalcols() ); SpParHelper::Print("Tallying...\n"); // BC update for all vertices except the sources for(int j = bfs.size()-1; j > 0; --j) { Dist::MPI_DCCols w = EWiseMult( *bfs[j], nspInv, false); w.EWiseScale(bcu); Dist::MPI_DCCols product = PSpGEMM(A,w); product = EWiseMult(product, *bfs[j-1], false); product = EWiseMult(product, nsp, false); bcu += product; } for(int j=0; j < bfs.size(); ++j) { delete bfs[j]; } SpParHelper::Print("Adding bc contributions...\n"); bc += FullyDistVec(bcu.Reduce(Row, plus(), 0.0)); // pack along rows } bc.Apply(bind2nd(minus(), nPasses)); // Subtrack nPasses from all the bc scores (because bcu was initialized to all 1's) double t2=MPI_Wtime(); double TEPS = (nPasses * static_cast(A.getnnz())) / (t2-t1); if( myrank == 0) { cout<<"Computation finished"< #include #include #include #include #include #include #include #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #endif #ifdef _OPENMP #include #endif double cblas_alltoalltime; double cblas_allgathertime; double cblas_mergeconttime; double cblas_transvectime; double cblas_localspmvtime; #ifdef _OPENMP int cblas_splits = omp_get_max_threads(); #else int cblas_splits = 1; #endif #define ITERS 16 #define EDGEFACTOR 16 using namespace std; using namespace combblas; // 64-bit floor(log2(x)) function // note: least significant bit is the "zeroth" bit // pre: v > 0 unsigned int highestbitset(uint64_t v) { // b in binary is {10,1100, 11110000, 1111111100000000 ...} const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; const unsigned int S[] = {1, 2, 4, 8, 16, 32}; int i; unsigned int r = 0; // result of log2(v) will go here for (i = 5; i >= 0; i--) { if (v & b[i]) // highestbitset is on the left half (i.e. v > S[i] for sure) { v >>= S[i]; r |= S[i]; } } return r; } template bool from_string(T & t, const string& s, std::ios_base& (*f)(std::ios_base&)) { istringstream iss(s); return !(iss >> f >> t).fail(); } template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } /** * Binary function to prune the previously discovered vertices from the current frontier * When used with EWiseApply(SparseVec V, DenseVec W,...) we get the 'exclude = false' effect of EWiseMult **/ struct prunediscovered: public std::binary_function { int64_t operator()(int64_t x, const int64_t & y) const { return ( y == -1 ) ? x: -1; } }; static void MPI_randuniq(void * invec, void * inoutvec, int * len, MPI_Datatype *datatype) { RandReduce RR; int64_t * inveccast = (int64_t *) invec; int64_t * inoutveccast = (int64_t *) inoutvec; for (int i=0; i<*len; i++ ) inoutveccast[i] = RR(inveccast[i], inoutveccast[i]); } int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 2) { if(myrank == 0) { cout << "Usage: ./scbfs " << endl; cout << "Example: mpirun -np 4 ./scbfs 20" << endl; } MPI_Finalize(); return -1; } { typedef SelectMaxSRing SR; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_s32p64; // sequentially use 32-bits for local matrices, but parallel semantics are 64-bits typedef SpParMat < int64_t, int, SpDCCols > PSpMat_s32p64_Int; // similarly mixed, but holds integers as upposed to booleans typedef SpParMat < int64_t, int64_t, SpDCCols > PSpMat_Int64; // Declare objects PSpMat_Bool A; PSpMat_s32p64 Aeff; FullyDistVec degrees; // degrees of vertices (including multi-edges and self-loops) FullyDistVec nonisov; // id's of non-isolated (connected) vertices unsigned scale; OptBuf optbuf; // let indices be 32-bits scale = static_cast(atoi(argv[1])); ostringstream outs; outs << "Forcing scale to : " << scale << endl; // this is an undirected graph, so A*x does indeed BFS double initiator[4] = {.57, .19, .19, .05}; double t01 = MPI_Wtime(); double t02; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true ); // generate packed edges SpParHelper::Print("Generated renamed edge lists\n"); t02 = MPI_Wtime(); ostringstream tinfo; tinfo << "Generation took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); // Start Kernel #1 MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); // conversion from distributed edge list, keeps self-loops, sums duplicates PSpMat_s32p64_Int * G = new PSpMat_s32p64_Int(*DEL, false); delete DEL; // free memory before symmetricizing SpParHelper::Print("Created Sparse Matrix (with int32 local indices and values)\n"); MPI_Barrier(MPI_COMM_WORLD); double redts = MPI_Wtime(); G->Reduce(degrees, Row, plus(), static_cast(0)); // Identity is 0 MPI_Barrier(MPI_COMM_WORLD); double redtf = MPI_Wtime(); ostringstream redtimeinfo; redtimeinfo << "Calculated degrees in " << redtf-redts << " seconds" << endl; SpParHelper::Print(redtimeinfo.str()); A = PSpMat_Bool(*G); // Convert to Boolean delete G; int64_t removed = A.RemoveLoops(); ostringstream loopinfo; loopinfo << "Converted to Boolean and removed " << removed << " loops" << endl; SpParHelper::Print(loopinfo.str()); A.PrintInfo(); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(A.getcommgrid()); A.Reduce(*ColSums, Column, plus(), static_cast(0)); A.Reduce(*RowSums, Row, plus(), static_cast(0)); SpParHelper::Print("Reductions done\n"); ColSums->EWiseApply(*RowSums, plus()); delete RowSums; SpParHelper::Print("Intersection of colsums and rowsums found\n"); nonisov = ColSums->FindInds(bind2nd(greater(), 0)); // only the indices of non-isolated vertices delete ColSums; SpParHelper::Print("Found (and permuted) non-isolated vertices\n"); nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A.PrintInfo(); #ifndef NOPERMUTE A(nonisov, nonisov, true); // in-place permute to save memory SpParHelper::Print("Dropped isolated vertices from input\n"); A.PrintInfo(); #endif Aeff = PSpMat_s32p64(A); // Convert to 32-bit local integers A.FreeMemory(); Symmetricize(Aeff); // A += A'; SpParHelper::Print("Symmetricized\n"); Aeff.OptimizeForGraph500(optbuf); // Should be called before threading is activated #ifdef THREADED ostringstream tinfo; tinfo << "Threading activated with " << cblas_splits << " threads" << endl; SpParHelper::Print(tinfo.str()); Aeff.ActivateThreading(cblas_splits); #endif Aeff.PrintInfo(); MPI_Barrier(MPI_COMM_WORLD); double t2=MPI_Wtime(); ostringstream k1timeinfo; k1timeinfo << (t2-t1) - (redtf-redts) << " seconds elapsed for Kernel #1" << endl; SpParHelper::Print(k1timeinfo.str()); Aeff.PrintInfo(); float balance = Aeff.LoadImbalance(); ostringstream outs2; outs2 << "Load balance: " << balance << endl; SpParHelper::Print(outs2.str()); MPI_Barrier(MPI_COMM_WORLD); // Now that every remaining vertex is non-isolated, randomly pick ITERS many of them as starting vertices #ifndef NOPERMUTE degrees = degrees(nonisov); // fix the degrees array too degrees.PrintInfo("Degrees array"); #endif // degrees.DebugPrint(); FullyDistVec Cands(ITERS, 0); double nver = (double) degrees.TotalLength(); #ifdef DETERMINISTIC MTRand M(1); #else MTRand M; // generate random numbers with Mersenne Twister #endif vector loccands(ITERS); vector loccandints(ITERS); if(myrank == 0) { for(int i=0; i(cout," ")); cout << endl; transform(loccands.begin(), loccands.end(), loccands.begin(), bind2nd( multiplies(), nver )); for(int i=0; i(loccands[i]); copy(loccandints.begin(), loccandints.end(), ostream_iterator(cout," ")); cout << endl; } MPI_Bcast(&(loccandints[0]), ITERS, MPIType(),0,MPI_COMM_WORLD); for(int i=0; i grid, IT globallen, NT initval); FullyDistVec parents ( Aeff.getcommgrid(), Aeff.getncol(), (int64_t) -1); // identity is -1 // FullyDistSpVec ( shared_ptr grid, IT glen); FullyDistSpVec fringe(Aeff.getcommgrid(), Aeff.getncol()); // numerical values are stored 0-based MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); fringe.SetElement(Cands[i], Cands[i]); int iterations = 0; MPI_Op randreducempiop; MPI_Op_create(MPI_randuniq, true, &randreducempiop); while(fringe.getnnz() > 0) { fringe.setNumToInd(); fringe = SpMV(Aeff, fringe,optbuf); // SpMV with sparse vector (with indexisvalue flag preset), optimization enabled fringe = EWiseMult(fringe, parents, true, (int64_t) -1); // clean-up vertices that already has parents fringe.PrintInfo("Frontier"); auto singlechild = fringe.Uniq(RandReduce(), randreducempiop); singlechild.PrintInfo("Single child frontier"); parents.Set(fringe); iterations++; } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); FullyDistSpVec parentsp = parents.Find(bind2nd(greater(), -1)); parentsp.Apply(myset(1)); // we use degrees on the directed graph, so that we don't count the reverse edges in the teps score int64_t nedges = EWiseMult(parentsp, degrees, false, (int64_t) 0).Reduce(plus(), (int64_t) 0); ostringstream outnew; outnew << i << "th starting vertex was " << Cands[i] << endl; outnew << "Number iterations: " << iterations << endl; outnew << "Number of vertices found: " << parentsp.Reduce(plus(), (int64_t) 0) << endl; outnew << "Number of edges traversed: " << nedges << endl; outnew << "BFS time: " << t2-t1 << " seconds" << endl; outnew << "MTEPS: " << static_cast(nedges) / (t2-t1) / 1000000.0 << endl; outnew << "Total communication (average so far): " << (cblas_allgathertime + cblas_alltoalltime) / (i+1) << endl; TIMES[i] = t2-t1; EDGES[i] = nedges; MTEPS[i] = static_cast(nedges) / (t2-t1) / 1000000.0; SpParHelper::Print(outnew.str()); } SpParHelper::Print("Finished\n"); ostringstream os; sort(EDGES, EDGES+ITERS); os << "--------------------------" << endl; os << "Min nedges: " << EDGES[0] << endl; os << "First Quartile nedges: " << (EDGES[(ITERS/4)-1] + EDGES[ITERS/4])/2 << endl; os << "Median nedges: " << (EDGES[(ITERS/2)-1] + EDGES[ITERS/2])/2 << endl; os << "Third Quartile nedges: " << (EDGES[(3*ITERS/4) -1 ] + EDGES[3*ITERS/4])/2 << endl; os << "Max nedges: " << EDGES[ITERS-1] << endl; double mean = accumulate( EDGES, EDGES+ITERS, 0.0 )/ ITERS; vector zero_mean(ITERS); // find distances to the mean transform(EDGES, EDGES+ITERS, zero_mean.begin(), bind2nd( minus(), mean )); // self inner-product is sum of sum of squares double deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ); os << "Mean nedges: " << mean << endl; os << "STDDEV nedges: " << deviation << endl; os << "--------------------------" << endl; sort(TIMES,TIMES+ITERS); os << "Min time: " << TIMES[0] << " seconds" << endl; os << "First Quartile time: " << (TIMES[(ITERS/4)-1] + TIMES[ITERS/4])/2 << " seconds" << endl; os << "Median time: " << (TIMES[(ITERS/2)-1] + TIMES[ITERS/2])/2 << " seconds" << endl; os << "Third Quartile time: " << (TIMES[(3*ITERS/4)-1] + TIMES[3*ITERS/4])/2 << " seconds" << endl; os << "Max time: " << TIMES[ITERS-1] << " seconds" << endl; mean = accumulate( TIMES, TIMES+ITERS, 0.0 )/ ITERS; transform(TIMES, TIMES+ITERS, zero_mean.begin(), bind2nd( minus(), mean )); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ); os << "Mean time: " << mean << " seconds" << endl; os << "STDDEV time: " << deviation << " seconds" << endl; os << "--------------------------" << endl; sort(MTEPS, MTEPS+ITERS); os << "Min MTEPS: " << MTEPS[0] << endl; os << "First Quartile MTEPS: " << (MTEPS[(ITERS/4)-1] + MTEPS[ITERS/4])/2 << endl; os << "Median MTEPS: " << (MTEPS[(ITERS/2)-1] + MTEPS[ITERS/2])/2 << endl; os << "Third Quartile MTEPS: " << (MTEPS[(3*ITERS/4)-1] + MTEPS[3*ITERS/4])/2 << endl; os << "Max MTEPS: " << MTEPS[ITERS-1] << endl; transform(MTEPS, MTEPS+ITERS, INVMTEPS, safemultinv()); // returns inf for zero teps double hteps = static_cast(ITERS) / accumulate(INVMTEPS, INVMTEPS+ITERS, 0.0); os << "Harmonic mean of MTEPS: " << hteps << endl; transform(INVMTEPS, INVMTEPS+ITERS, zero_mean.begin(), bind2nd(minus(), 1/hteps)); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ) * (hteps*hteps); // harmonic_std_dev os << "Harmonic standard deviation of MTEPS: " << deviation << endl; SpParHelper::Print(os.str()); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/makefile-mac000644 000765 000024 00000013640 13271404146 022316 0ustar00aydinbulucstaff000000 000000 INCDIR = $(HOME) INCADD = -I$(INCDIR) # notes for configure: # -fno-exceptions does not work with MPICH2 # -fno-rtti does not work with tr1:tuples OPT = -O2 -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ -Wreturn-type #-DNDEBUG (disables important assertions) DEB = -g -O0 -fno-inline -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ -Wreturn-type -DDEBUG PROF = -pg -O2 -fno-inline -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ -Wreturn-type COMPILER = mpicxx -std=c++11 -std=c++14 -DTHREADED -fopenmp -fpermissive #-DRAND_PERMUTE #ABAB:please define this per class as opposed to globally for each compilation FLAGS = $(OPT) -Wall -Wno-maybe-uninitialized #-DTIMING #-DCOMBBLAS_DEBUG COMBBLAS = .. # # build Graph500 generator # $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator CommGrid.o: ../CommGrid.cpp ../CommGrid.h $(COMPILER) $(INCADD) $(FLAGS) -c -o CommGrid.o ../CommGrid.cpp mmio.o: ../mmio.c mpicc $(INCADD) $(FLAGS) -c -o mmio.o ../mmio.c MPIType.o: ../MPIType.cpp ../MPIType.h $(COMPILER) $(INCADD) $(FLAGS) -c -o MPIType.o ../MPIType.cpp MemoryPool.o: ../MemoryPool.cpp ../SpDefs.h $(COMPILER) $(INCADD) $(FLAGS) -c -o MemoryPool.o ../MemoryPool.cpp hash.o: ../hash.cpp ../hash.hpp $(COMPILER) $(FLAGS) $(INCADD) -c -o hash.o ../hash.cpp BetwCent.o: BetwCent.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp $(COMPILER) $(INCADD) $(FLAGS) -c -o BetwCent.o BetwCent.cpp MCL.o: MCL.cpp CC.h WriteMCLClusters.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp $(COMPILER) $(INCADD) $(FLAGS) -c -o MCL.o MCL.cpp CC.o: CC.cpp CC.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp $(COMPILER) $(INCADD) $(FLAGS) -c -o CC.o CC.cpp TopDownBFS.o: TopDownBFS.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../DenseParVec.h ../DenseParVec.cpp ../SpImpl.h $(COMPILER) $(INCADD) $(FLAGS) -c -o TopDownBFS.o TopDownBFS.cpp DirOptBFS.o: DirOptBFS.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../DenseParVec.h ../DenseParVec.cpp ../SpImpl.h $(COMPILER) $(INCADD) $(FLAGS) -c -o DirOptBFS.o DirOptBFS.cpp FilteredBFS.o: FilteredBFS.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../DenseParVec.h ../DenseParVec.cpp ../SpImpl.h ../SpParHelper.cpp ../Friends.h TwitterEdge.h ../MPIType.h $(COMPILER) $(INCADD) $(FLAGS) -c -o FilteredBFS.o FilteredBFS.cpp FilteredMIS.o: FilteredMIS.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../DenseParVec.h ../DenseParVec.cpp ../SpImpl.h ../SpParHelper.cpp ../Friends.h TwitterEdge.h ../MPIType.h ../FullyDistVec.cpp $(COMPILER) $(INCADD) $(FLAGS) -c -o FilteredMIS.o FilteredMIS.cpp mcl: MemoryPool.o CommGrid.o MPIType.o MCL.o mmio.o hash.o $(COMPILER) $(INCADD) $(FLAGS) -o mcl MCL.o MemoryPool.o mmio.o CommGrid.o MPIType.o hash.o cc: MemoryPool.o CommGrid.o MPIType.o CC.o mmio.o hash.o $(COMPILER) $(INCADD) $(FLAGS) -o cc CC.o MemoryPool.o mmio.o CommGrid.o MPIType.o hash.o tdbfs: MemoryPool.o CommGrid.o MPIType.o TopDownBFS.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o tdbfs TopDownBFS.o MemoryPool.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq dobfs: MemoryPool.o CommGrid.o MPIType.o DirOptBFS.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o dobfs DirOptBFS.o MemoryPool.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq betwcent: MemoryPool.o CommGrid.o MPIType.o BetwCent.o $(COMPILER) $(INCADD) $(FLAGS) -o betwcent BetwCent.o MemoryPool.o CommGrid.o MPIType.o fbfs: MemoryPool.o CommGrid.o MPIType.o FilteredBFS.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o fbfs FilteredBFS.o MemoryPool.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq fmis: MemoryPool.o CommGrid.o MPIType.o FilteredMIS.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o fmis FilteredMIS.o MemoryPool.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq BPMaximalMatching.o: BPMaximalMatching.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../DenseParVec.h ../DenseParVec.cpp ../SpImpl.h $(COMPILER) $(FLAGS) -c -o BPMaximalMatching.o BPMaximalMatching.cpp BPMaximalMatching: MemoryPool.o CommGrid.o MPIType.o BPMaximalMatching.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(FLAGS) -o BPMaximalMatching BPMaximalMatching.o MemoryPool.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq BPMaximumMatching.o: BPMaximumMatching.cpp BPMaximalMatching.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../DenseParVec.h ../DenseParVec.cpp ../SpImpl.h $(COMPILER) $(INCADD) $(FLAGS) -c -o BPMaximumMatching.o BPMaximumMatching.cpp bpmm: MemoryPool.o CommGrid.o MPIType.o BPMaximumMatching.o mmio.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o bpmm BPMaximumMatching.o MemoryPool.o CommGrid.o mmio.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq clean: rm -f betwcent rm -f driver rm -f mcl rm -f fbfs rm -f cc rm -f dobfs rm -f tdbfs rm -f *.o rm -f BPMaximumMatching rm -f ../graph500-1.2/generator/*.o rm -f ../graph500-1.2/generator/libgraph_generator_seq.a cleanout: rm out.* rm err.* CombBLAS_beta_16_2/Applications/FilteredMIS.cpp000644 000765 000024 00000041255 13271404146 022676 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #define DETERMINISTIC #include "CombBLAS/CombBLAS.h" #include #include #include #include #include #include #include #include #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #include #endif double cblas_alltoalltime; double cblas_allgathertime; int cblas_splits; #include "TwitterEdge.h" #define EDGEFACTOR 5 // For MIS #define ITERS 16 #define PERCENTS 4 // testing with 4 different percentiles using namespace std; using namespace combblas; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } struct DetSymmetricize: public std::binary_function { // have to deterministically choose between one of the two values. // cannot just add them because that will change the distribution (small values are unlikely to survive) TwitterEdge operator()(const TwitterEdge & g, const TwitterEdge & t) { TwitterEdge toret = g; if(((g.latest + t.latest) & 1) == 1) { toret.latest = std::min(g.latest, t.latest); } else { toret.latest = std::max(g.latest, t.latest); } return toret; } }; typedef SpParMat < int64_t, TwitterEdge, SpDCCols > PSpMat_Twitter; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; void SymmetricizeRands(PSpMat_Twitter & A) { PSpMat_Twitter AT = A; AT.Transpose(); // SpParMat EWiseApply (const SpParMat & A, // const SpParMat & B, _BinaryOperation __binary_op, bool notB, const NU2& defaultBVal) // Default B value is irrelevant since the structures of the matrices are A = EWiseApply >(A, AT, DetSymmetricize(), false, TwitterEdge()); } #ifdef DETERMINISTIC MTRand GlobalMT(1); #else MTRand GlobalMT; // generate random numbers with Mersenne Twister #endif struct Twitter_obj_randomizer : public std::unary_function { const TwitterEdge operator()(const TwitterEdge & x) const { short mycount = 1; bool myfollow = 0; time_t mylatest = static_cast(GlobalMT.rand() * 10000); // random.randrange(0,10000) return TwitterEdge(mycount, myfollow, mylatest); } }; struct Twitter_materialize: public std::binary_function { bool operator()(const TwitterEdge & x, time_t sincedate) const { if(x.isRetwitter() && x.LastTweetBy(sincedate)) return false; // false if the edge is going to be kept else return true; // true if the edge is to be pruned } }; // def rand( verc ): // import random // return random.random() struct randGen : public std::unary_function { const double operator()(const double & ignore) { return GlobalMT.rand(); } }; int main(int argc, char* argv[]) { int nprocs, myrank; #ifdef _OPENMP int cblas_splits = omp_get_max_threads(); int provided, flag, claimed; MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided ); MPI_Is_thread_main( &flag ); if (!flag) SpParHelper::Print("This thread called init_thread but Is_thread_main gave false\n"); MPI_Query_thread( &claimed ); if (claimed != provided) SpParHelper::Print("Query thread gave different thread level than requested\n"); #else MPI_Init(&argc, &argv); int cblas_splits = 1; #endif MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 2) { if(myrank == 0) { cout << "Usage: ./FilteredMIS " << endl; } MPI_Finalize(); return -1; } { // Declare objects PSpMat_Twitter A; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); FullyDistVec indegrees(fullWorld); // in-degrees of vertices (including multi-edges and self-loops) FullyDistVec oudegrees(fullWorld); // out-degrees of vertices (including multi-edges and self-loops) FullyDistVec degrees(fullWorld); // combined degrees of vertices (including multi-edges and self-loops) PSpMat_Bool * ABool; SpParHelper::Print("Using synthetic data, which we ALWAYS permute for load balance\n"); SpParHelper::Print("We only balance the original input, we don't repermute after each filter change\n"); SpParHelper::Print("BFS is run on UNDIRECTED graph, hence hitting CCs, and TEPS is bidirectional\n"); double initiator[4] = {.25, .25, .25, .25}; // creating erdos-renyi double t01 = MPI_Wtime(); DistEdgeList * DEL = new DistEdgeList(); unsigned scale = static_cast(atoi(argv[1])); ostringstream outs; outs << "Forcing scale to : " << scale << endl; SpParHelper::Print(outs.str()); // parameters: (double initiator[4], int log_numverts, int edgefactor, bool scramble, bool packed) DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true ); // generate packed edges SpParHelper::Print("Generated renamed edge lists\n"); ABool = new PSpMat_Bool(*DEL, false); int64_t removed = ABool->RemoveLoops(); ostringstream loopinfo; loopinfo << "Converted to Boolean and removed " << removed << " loops" << endl; SpParHelper::Print(loopinfo.str()); ABool->PrintInfo(); delete DEL; // free memory A = PSpMat_Twitter(*ABool); // any upcasting generates the default object double t02 = MPI_Wtime(); ostringstream tinfo; tinfo << "Generation took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); // indegrees is sum along rows because A is loaded as "tranposed", similarly oudegrees is sum along columns ABool->PrintInfo(); ABool->Reduce(oudegrees, Column, plus(), static_cast(0)); ABool->Reduce(indegrees, Row, plus(), static_cast(0)); // indegrees_filt and oudegrees_filt is used for the real data FullyDistVec indegrees_filt(fullWorld); FullyDistVec oudegrees_filt(fullWorld); typedef FullyDistVec IntVec; // used for the synthetic data (symmetricized before randomization) FullyDistVec degrees_filt[4] = {IntVec(fullWorld), IntVec(fullWorld), IntVec(fullWorld), IntVec(fullWorld)}; int64_t keep[PERCENTS] = {100, 1000, 2500, 10000}; // ratio of edges kept in range (0, 10000) degrees = indegrees; degrees.EWiseApply(oudegrees, plus()); SpParHelper::Print("All degrees calculated\n"); delete ABool; float balance = A.LoadImbalance(); ostringstream outlb; outlb << "Load balance: " << balance << endl; SpParHelper::Print(outlb.str()); // We symmetricize before we apply the random generator // Otherwise += will naturally add the random numbers together // hence will create artificially high-permeable filters Symmetricize(A); // A += A'; SpParHelper::Print("Symmetricized\n"); A.Apply(Twitter_obj_randomizer()); A.PrintInfo(); SymmetricizeRands(A); SpParHelper::Print("Symmetricized Rands\n"); A.PrintInfo(); FullyDistVec * nonisov = new FullyDistVec(degrees.FindInds(bind2nd(greater(), 0))); SpParHelper::Print("Found (and permuted) non-isolated vertices\n"); nonisov->RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A(*nonisov, *nonisov, true); // in-place permute to save memory SpParHelper::Print("Dropped isolated vertices from input\n"); indegrees = indegrees(*nonisov); // fix the degrees arrays too oudegrees = oudegrees(*nonisov); degrees = degrees(*nonisov); delete nonisov; for (int i=0; i < PERCENTS; i++) { PSpMat_Twitter B = A; B.Prune(bind2nd(Twitter_materialize(), keep[i])); PSpMat_Bool BBool = B; BBool.PrintInfo(); float balance = B.LoadImbalance(); ostringstream outs; outs << "Load balance of " << static_cast(keep[i])/100 << "% filtered case: " << balance << endl; SpParHelper::Print(outs.str()); // degrees_filt[i] is by-default generated as permuted BBool.Reduce(degrees_filt[i], Column, plus(), static_cast(0)); // Column=Row since BBool is symmetric } float balance_former = A.LoadImbalance(); ostringstream outs_former; outs_former << "Load balance: " << balance_former << endl; SpParHelper::Print(outs_former.str()); for(int trials =0; trials < PERCENTS; trials++) { cblas_allgathertime = 0; cblas_alltoalltime = 0; double MISVS[ITERS]; // numbers of vertices in each MIS double TIMES[ITERS]; LatestRetwitterMIS::sincedate = keep[trials]; LatestRetwitterSelect2nd::sincedate = keep[trials]; ostringstream outs; outs << "Initializing since date (only once) to " << LatestRetwitterMIS::sincedate << endl; SpParHelper::Print(outs.str()); for(int sruns = 0; sruns < ITERS; ++sruns) { double t1 = MPI_Wtime(); int64_t nvert = A.getncol(); //# the final result set. S[i] exists and is 1 if vertex i is in the MIS //S = Vec(nvert, sparse=True) FullyDistSpVec S ( A.getcommgrid(), nvert); //# the candidate set. initially all vertices are candidates. //# this vector doubles as 'r', the random value vector. //# i.e. if C[i] exists, then i is a candidate. The value C[i] is i's r for this iteration. //C = Vec.ones(nvert, sparse=True) //FullyDistSpVec's length is not the same as its nnz //Since FullyDistSpVec::Apply only affects nonzeros, nnz should be forced to glen // FullyDistVec ( shared_ptr grid, IT globallen, NT initval); FullyDistVec * denseC = new FullyDistVec( A.getcommgrid(), nvert, 1.0); FullyDistSpVec C ( *denseC); delete denseC; FullyDistSpVec min_neighbor_r ( A.getcommgrid(), nvert); FullyDistSpVec new_S_members ( A.getcommgrid(), nvert); FullyDistSpVec new_S_neighbors ( A.getcommgrid(), nvert); while (C.getnnz() > 0) { //# label each vertex in C with a random value C.Apply(randGen()); //# find the smallest random value among a vertex's neighbors //# In other words: min_neighbor_r[i] = min(C[j] for all neighbors j of vertex i) //min_neighbor_r = Gmatrix.SpMV(C, sr(myMin,select2nd)) # could use "min" directly SpMV(A, C, min_neighbor_r, false); // min_neighbor_r empty OK? #ifdef PRINTITERS min_neighbor_r.PrintInfo("Neighbors"); #endif #ifdef DEBUG min_neighbor_r.DebugPrint(); C.DebugPrint(); #endif //# The vertices to be added to S this iteration are those whose random value is //# smaller than those of all its neighbors: //# new_S_members[i] exists if C[i] < min_neighbor_r[i] //# If C[i] exists and min_neighbor_r[i] doesn't, still a value is returned with bin_op(NULL,C[i]) //new_S_members = min_neighbor_r.eWiseApply(C, return1, doOp=is2ndSmaller, allowANulls=True, allowBNulls=False, inPlace=False, ANull=2) new_S_members = EWiseApply(min_neighbor_r, C, return1_uint8(), is2ndSmaller(), true, false, (double) 2.0, (double) 2.0, true); //// template //// FullyDistSpVec EWiseApply (const FullyDistSpVec & V, const FullyDistSpVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, //// bool allowVNulls, bool allowWNulls, NU1 Vzero, NU2 Wzero, const bool allowIntersect = true); #ifdef PRINTITERS new_S_members.PrintInfo("New members of the MIS"); #endif #ifdef DEBUG new_S_members.DebugPrint(); #endif //# new_S_members are no longer candidates, so remove them from C //C.eWiseApply(new_S_members, return1, allowANulls=False, allowIntersect=False, allowBNulls=True, inPlace=True) C = EWiseApply(C, new_S_members, return1_uint8(), return1_uint8(), false, true, (double) 0.0, (uint8_t) 0, false); #ifdef PRINTITERS C.PrintInfo("Entries to be removed from the Candidates set"); #endif //# find neighbors of new_S_members //new_S_neighbors = Gmatrix.SpMV(new_S_members, sr(select2nd,select2nd)) SpMV(A, new_S_members, new_S_neighbors, false); //# remove neighbors of new_S_members from C, because they cannot be part of the MIS anymore //# If C[i] exists and new_S_neighbors[i] doesn't, still a value is returned with bin_op(NULL,C[i]) //C.eWiseApply(new_S_neighbors, return1, allowANulls=False, allowIntersect=False, allowBNulls=True, inPlace=True) C = EWiseApply(C, new_S_neighbors, return1_uint8(), return1_uint8(), false, true, (double) 0.0, (uint8_t) 0, false); #ifdef PRINTITERS C.PrintInfo("Candidates set after neighbors of MIS removed"); #endif //# add new_S_members to S //S.eWiseApply(new_S_members, return1, allowANulls=True, allowBNulls=True, inPlace=True) S = EWiseApply(S, new_S_members, return1_uint8(), return1_uint8(), true, true, (uint8_t) 1, (uint8_t) 1, true); S.PrintInfo("The current MIS:"); } double t2 = MPI_Wtime(); ostringstream ositr; ositr << "MIS has " << S.getnnz() << " vertices" << endl; SpParHelper::Print(ositr.str()); ostringstream ositr2; ositr << "MIS time: " << t2-t1 << " seconds" << endl; SpParHelper::Print(ositr.str()); TIMES[sruns] = t2-t1; MISVS[sruns] = S.getnnz(); } // end for(int sruns = 0; sruns < ITERS; ++sruns) ostringstream os; os << "Per iteration communication times: " << endl; os << "AllGatherv: " << cblas_allgathertime / ITERS << endl; os << "AlltoAllv: " << cblas_alltoalltime / ITERS << endl; sort(MISVS, MISVS+ITERS); os << "--------------------------" << endl; os << "Min MIS vertices: " << MISVS[0] << endl; os << "Median MIS vertices: " << (MISVS[(ITERS/2)-1] + MISVS[ITERS/2])/2 << endl; os << "Max MIS vertices: " << MISVS[ITERS-1] << endl; double mean = accumulate( MISVS, MISVS+ITERS, 0.0 )/ ITERS; vector zero_mean(ITERS); // find distances to the mean transform(MISVS, MISVS+ITERS, zero_mean.begin(), bind2nd( minus(), mean )); // self inner-product is sum of sum of squares double deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ); os << "Mean MIS vertices: " << mean << endl; os << "STDDEV MIS vertices: " << deviation << endl; os << "--------------------------" << endl; sort(TIMES,TIMES+ITERS); os << "Filter keeps " << static_cast(keep[trials])/100.0 << " percentage of edges" << endl; os << "Min time: " << TIMES[0] << " seconds" << endl; os << "Median time: " << (TIMES[(ITERS/2)-1] + TIMES[ITERS/2])/2 << " seconds" << endl; os << "Max time: " << TIMES[ITERS-1] << " seconds" << endl; mean = accumulate( TIMES, TIMES+ITERS, 0.0 )/ ITERS; transform(TIMES, TIMES+ITERS, zero_mean.begin(), bind2nd( minus(), mean )); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ); os << "Mean time: " << mean << " seconds" << endl; os << "STDDEV time: " << deviation << " seconds" << endl; os << "--------------------------" << endl; SpParHelper::Print(os.str()); } // end for(int trials =0; trials < PERCENTS; trials++) } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/TopDownBFS.cpp000644 000765 000024 00000050366 13271404146 022517 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #define DETERMINISTIC #include "CombBLAS/CombBLAS.h" #include #include #include #include #include #include #include #include int cblas_splits; double cblas_alltoalltime; double cblas_allgathertime; double cblas_mergeconttime; double cblas_transvectime; double cblas_localspmvtime; #define ITERS 16 #define EDGEFACTOR 16 using namespace std; using namespace combblas; // 64-bit floor(log2(x)) function // note: least significant bit is the "zeroth" bit // pre: v > 0 unsigned int highestbitset(uint64_t v) { // b in binary is {10,1100, 11110000, 1111111100000000 ...} const uint64_t b[] = {0x2ULL, 0xCULL, 0xF0ULL, 0xFF00ULL, 0xFFFF0000ULL, 0xFFFFFFFF00000000ULL}; const unsigned int S[] = {1, 2, 4, 8, 16, 32}; int i; unsigned int r = 0; // result of log2(v) will go here for (i = 5; i >= 0; i--) { if (v & b[i]) // highestbitset is on the left half (i.e. v > S[i] for sure) { v >>= S[i]; r |= S[i]; } } return r; } template bool from_string(T & t, const string& s, std::ios_base& (*f)(std::ios_base&)) { istringstream iss(s); return !(iss >> f >> t).fail(); } template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } /** * Binary function to prune the previously discovered vertices from the current frontier * When used with EWiseApply(SparseVec V, DenseVec W,...) we get the 'exclude = false' effect of EWiseMult **/ struct prunediscovered: public std::binary_function { int64_t operator()(int64_t x, const int64_t & y) const { return ( y == -1 ) ? x: -1; } }; int main(int argc, char* argv[]) { int nprocs, myrank; #ifdef _OPENMP int cblas_splits = omp_get_max_threads(); int provided, flag, claimed; MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided ); MPI_Is_thread_main( &flag ); if (!flag) SpParHelper::Print("This thread called init_thread but Is_thread_main gave false\n"); MPI_Query_thread( &claimed ); if (claimed != provided) SpParHelper::Print("Query thread gave different thread level than requested\n"); #else MPI_Init(&argc, &argv); int cblas_splits = 1; #endif MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./tdbfs {FastGen}" << endl; cout << "Example: ./tdbfs Force 25 FastGen" << endl; } MPI_Finalize(); return -1; } { typedef SelectMaxSRing SR; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_s32p64; // sequentially use 32-bits for local matrices, but parallel semantics are 64-bits typedef SpParMat < int64_t, int, SpDCCols > PSpMat_s32p64_Int; // similarly mixed, but holds integers as upposed to booleans typedef SpParMat < int64_t, int64_t, SpDCCols > PSpMat_Int64; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); // Declare objects PSpMat_Bool A(fullWorld); PSpMat_s32p64 Aeff(fullWorld); FullyDistVec degrees(fullWorld); // degrees of vertices (including multi-edges and self-loops) FullyDistVec nonisov(fullWorld); // id's of non-isolated (connected) vertices unsigned scale; OptBuf optbuf; // let indices be 32-bits bool scramble = false; if(string(argv[1]) == string("Input")) // input option { A.ReadDistribute(string(argv[2]), 0); // read it from file SpParHelper::Print("Read input\n"); PSpMat_Int64 * G = new PSpMat_Int64(A); G->Reduce(degrees, Row, plus(), static_cast(0)); // identity is 0 delete G; Symmetricize(A); // A += A'; FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); A.Reduce(*ColSums, Column, plus(), static_cast(0)); // plus matches the type of the output vector nonisov = ColSums->FindInds(bind2nd(greater(), 0)); // only the indices of non-isolated vertices delete ColSums; A = A(nonisov, nonisov); Aeff = PSpMat_s32p64(A); A.FreeMemory(); SpParHelper::Print("Symmetricized and pruned\n"); Aeff.OptimizeForGraph500(optbuf); // Should be called before threading is activated #ifdef THREADED ostringstream tinfo; tinfo << "Threading activated with " << cblas_splits << " threads" << endl; SpParHelper::Print(tinfo.str()); Aeff.ActivateThreading(cblas_splits); #endif } else if(string(argv[1]) == string("Binary")) { uint64_t n, m; from_string(n,string(argv[3]),std::dec); from_string(m,string(argv[4]),std::dec); ostringstream outs; outs << "Reading " << argv[2] << " with " << n << " vertices and " << m << " edges" << endl; SpParHelper::Print(outs.str()); DistEdgeList * DEL = new DistEdgeList(argv[2], n, m); SpParHelper::Print("Read binary input to distributed edge list\n"); PermEdges(*DEL); SpParHelper::Print("Permuted Edges\n"); RenameVertices(*DEL); //DEL->Dump32bit("graph_permuted"); SpParHelper::Print("Renamed Vertices\n"); // conversion from distributed edge list, keeps self-loops, sums duplicates PSpMat_Int64 * G = new PSpMat_Int64(*DEL, false); delete DEL; // free memory before symmetricizing SpParHelper::Print("Created Int64 Sparse Matrix\n"); G->Reduce(degrees, Row, plus(), static_cast(0)); // Identity is 0 A = PSpMat_Bool(*G); // Convert to Boolean delete G; int64_t removed = A.RemoveLoops(); ostringstream loopinfo; loopinfo << "Converted to Boolean and removed " << removed << " loops" << endl; SpParHelper::Print(loopinfo.str()); A.PrintInfo(); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(A.getcommgrid()); A.Reduce(*ColSums, Column, plus(), static_cast(0)); A.Reduce(*RowSums, Row, plus(), static_cast(0)); ColSums->EWiseApply(*RowSums, plus()); delete RowSums; nonisov = ColSums->FindInds(bind2nd(greater(), 0)); // only the indices of non-isolated vertices delete ColSums; SpParHelper::Print("Found (and permuted) non-isolated vertices\n"); nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A.PrintInfo(); #ifndef NOPERMUTE A(nonisov, nonisov, true); // in-place permute to save memory SpParHelper::Print("Dropped isolated vertices from input\n"); A.PrintInfo(); #endif Aeff = PSpMat_s32p64(A); // Convert to 32-bit local integers A.FreeMemory(); Symmetricize(Aeff); // A += A'; SpParHelper::Print("Symmetricized\n"); //A.Dump("graph_symmetric"); Aeff.OptimizeForGraph500(optbuf); // Should be called before threading is activated #ifdef THREADED ostringstream tinfo; tinfo << "Threading activated with " << cblas_splits << " threads" << endl; SpParHelper::Print(tinfo.str()); Aeff.ActivateThreading(cblas_splits); #endif } else { if(string(argv[1]) == string("Force")) { scale = static_cast(atoi(argv[2])); ostringstream outs; outs << "Forcing scale to : " << scale << endl; SpParHelper::Print(outs.str()); if(argc > 3 && string(argv[3]) == string("FastGen")) { SpParHelper::Print("Using fast vertex permutations; skipping edge permutations (like v2.1)\n"); scramble = true; } } else { SpParHelper::Print("Unknown option\n"); MPI_Finalize(); return -1; } // this is an undirected graph, so A*x does indeed BFS double initiator[4] = {.57, .19, .19, .05}; double t01 = MPI_Wtime(); double t02; DistEdgeList * DEL = new DistEdgeList(); if(!scramble) { DEL->GenGraph500Data(initiator, scale, EDGEFACTOR); SpParHelper::Print("Generated edge lists\n"); t02 = MPI_Wtime(); ostringstream tinfo; tinfo << "Generation took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); PermEdges(*DEL); SpParHelper::Print("Permuted Edges\n"); //DEL->Dump64bit("edges_permuted"); //SpParHelper::Print("Dumped\n"); RenameVertices(*DEL); // intermediate: generates RandPerm vector, using MemoryEfficientPSort SpParHelper::Print("Renamed Vertices\n"); } else // fast generation { DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true ); // generate packed edges SpParHelper::Print("Generated renamed edge lists\n"); t02 = MPI_Wtime(); ostringstream tinfo; tinfo << "Generation took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); } // Start Kernel #1 MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); // conversion from distributed edge list, keeps self-loops, sums duplicates PSpMat_s32p64_Int * G = new PSpMat_s32p64_Int(*DEL, false); delete DEL; // free memory before symmetricizing SpParHelper::Print("Created Sparse Matrix (with int32 local indices and values)\n"); MPI_Barrier(MPI_COMM_WORLD); double redts = MPI_Wtime(); G->Reduce(degrees, Row, plus(), static_cast(0)); // Identity is 0 MPI_Barrier(MPI_COMM_WORLD); double redtf = MPI_Wtime(); ostringstream redtimeinfo; redtimeinfo << "Calculated degrees in " << redtf-redts << " seconds" << endl; SpParHelper::Print(redtimeinfo.str()); A = PSpMat_Bool(*G); // Convert to Boolean delete G; int64_t removed = A.RemoveLoops(); ostringstream loopinfo; loopinfo << "Converted to Boolean and removed " << removed << " loops" << endl; SpParHelper::Print(loopinfo.str()); A.PrintInfo(); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(A.getcommgrid()); A.Reduce(*ColSums, Column, plus(), static_cast(0)); A.Reduce(*RowSums, Row, plus(), static_cast(0)); SpParHelper::Print("Reductions done\n"); ColSums->EWiseApply(*RowSums, plus()); delete RowSums; SpParHelper::Print("Intersection of colsums and rowsums found\n"); nonisov = ColSums->FindInds(bind2nd(greater(), 0)); // only the indices of non-isolated vertices delete ColSums; SpParHelper::Print("Found (and permuted) non-isolated vertices\n"); nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A.PrintInfo(); #ifndef NOPERMUTE A(nonisov, nonisov, true); // in-place permute to save memory SpParHelper::Print("Dropped isolated vertices from input\n"); A.PrintInfo(); #endif Aeff = PSpMat_s32p64(A); // Convert to 32-bit local integers A.FreeMemory(); Symmetricize(Aeff); // A += A'; SpParHelper::Print("Symmetricized\n"); Aeff.OptimizeForGraph500(optbuf); // Should be called before threading is activated #ifdef THREADED ostringstream tinfo; tinfo << "Threading activated with " << cblas_splits << " threads" << endl; SpParHelper::Print(tinfo.str()); Aeff.ActivateThreading(cblas_splits); #endif Aeff.PrintInfo(); MPI_Barrier(MPI_COMM_WORLD); double t2=MPI_Wtime(); ostringstream k1timeinfo; k1timeinfo << (t2-t1) - (redtf-redts) << " seconds elapsed for Kernel #1" << endl; SpParHelper::Print(k1timeinfo.str()); } Aeff.PrintInfo(); float balance = Aeff.LoadImbalance(); ostringstream outs; outs << "Load balance: " << balance << endl; SpParHelper::Print(outs.str()); MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); // Now that every remaining vertex is non-isolated, randomly pick ITERS many of them as starting vertices #ifndef NOPERMUTE degrees = degrees(nonisov); // fix the degrees array too degrees.PrintInfo("Degrees array"); #endif // degrees.DebugPrint(); FullyDistVec Cands(ITERS, 0); double nver = (double) degrees.TotalLength(); #ifdef DETERMINISTIC MTRand M(1); #else MTRand M; // generate random numbers with Mersenne Twister #endif vector loccands(ITERS); vector loccandints(ITERS); if(myrank == 0) { for(int i=0; i(cout," ")); cout << endl; transform(loccands.begin(), loccands.end(), loccands.begin(), bind2nd( multiplies(), nver )); for(int i=0; i(loccands[i]); copy(loccandints.begin(), loccandints.end(), ostream_iterator(cout," ")); cout << endl; } MPI_Bcast(&(loccandints[0]), ITERS, MPIType(),0,MPI_COMM_WORLD); for(int i=0; i grid, IT globallen, NT initval); FullyDistVec parents ( Aeff.getcommgrid(), Aeff.getncol(), (int64_t) -1); // identity is -1 // FullyDistSpVec ( shared_ptr grid, IT glen); FullyDistSpVec fringe(Aeff.getcommgrid(), Aeff.getncol()); // numerical values are stored 0-based MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); fringe.SetElement(Cands[i], Cands[i]); int iterations = 0; while(fringe.getnnz() > 0) { fringe.setNumToInd(); fringe = SpMV(Aeff, fringe,optbuf); // SpMV with sparse vector (with indexisvalue flag preset), optimization enabled fringe = EWiseMult(fringe, parents, true, (int64_t) -1); // clean-up vertices that already has parents parents.Set(fringe); iterations++; } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); FullyDistSpVec parentsp = parents.Find(bind2nd(greater(), -1)); parentsp.Apply(myset(1)); // we use degrees on the directed graph, so that we don't count the reverse edges in the teps score int64_t nedges = EWiseMult(parentsp, degrees, false, (int64_t) 0).Reduce(plus(), (int64_t) 0); ostringstream outnew; outnew << i << "th starting vertex was " << Cands[i] << endl; outnew << "Number iterations: " << iterations << endl; outnew << "Number of vertices found: " << parentsp.Reduce(plus(), (int64_t) 0) << endl; outnew << "Number of edges traversed: " << nedges << endl; outnew << "BFS time: " << t2-t1 << " seconds" << endl; outnew << "MTEPS: " << static_cast(nedges) / (t2-t1) / 1000000.0 << endl; outnew << "Total communication (average so far): " << (cblas_allgathertime + cblas_alltoalltime) / (i+1) << endl; TIMES[i] = t2-t1; EDGES[i] = nedges; MTEPS[i] = static_cast(nedges) / (t2-t1) / 1000000.0; SpParHelper::Print(outnew.str()); } SpParHelper::Print("Finished\n"); ostringstream os; MPI_Pcontrol(-1,"BFS"); os << "Per iteration communication times: " << endl; os << "AllGatherv: " << cblas_allgathertime / ITERS << endl; os << "AlltoAllv: " << cblas_alltoalltime / ITERS << endl; os << "Transvec: " << cblas_transvectime / ITERS << endl; os << "Per iteration computation times: " << endl; os << "MergeCont: " << cblas_mergeconttime / ITERS << endl; os << "LocalSpmv: " << cblas_localspmvtime / ITERS << endl; sort(EDGES, EDGES+ITERS); os << "--------------------------" << endl; os << "Min nedges: " << EDGES[0] << endl; os << "First Quartile nedges: " << (EDGES[(ITERS/4)-1] + EDGES[ITERS/4])/2 << endl; os << "Median nedges: " << (EDGES[(ITERS/2)-1] + EDGES[ITERS/2])/2 << endl; os << "Third Quartile nedges: " << (EDGES[(3*ITERS/4) -1 ] + EDGES[3*ITERS/4])/2 << endl; os << "Max nedges: " << EDGES[ITERS-1] << endl; double mean = accumulate( EDGES, EDGES+ITERS, 0.0 )/ ITERS; vector zero_mean(ITERS); // find distances to the mean transform(EDGES, EDGES+ITERS, zero_mean.begin(), bind2nd( minus(), mean )); // self inner-product is sum of sum of squares double deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ); os << "Mean nedges: " << mean << endl; os << "STDDEV nedges: " << deviation << endl; os << "--------------------------" << endl; sort(TIMES,TIMES+ITERS); os << "Min time: " << TIMES[0] << " seconds" << endl; os << "First Quartile time: " << (TIMES[(ITERS/4)-1] + TIMES[ITERS/4])/2 << " seconds" << endl; os << "Median time: " << (TIMES[(ITERS/2)-1] + TIMES[ITERS/2])/2 << " seconds" << endl; os << "Third Quartile time: " << (TIMES[(3*ITERS/4)-1] + TIMES[3*ITERS/4])/2 << " seconds" << endl; os << "Max time: " << TIMES[ITERS-1] << " seconds" << endl; mean = accumulate( TIMES, TIMES+ITERS, 0.0 )/ ITERS; transform(TIMES, TIMES+ITERS, zero_mean.begin(), bind2nd( minus(), mean )); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ); os << "Mean time: " << mean << " seconds" << endl; os << "STDDEV time: " << deviation << " seconds" << endl; os << "--------------------------" << endl; sort(MTEPS, MTEPS+ITERS); os << "Min MTEPS: " << MTEPS[0] << endl; os << "First Quartile MTEPS: " << (MTEPS[(ITERS/4)-1] + MTEPS[ITERS/4])/2 << endl; os << "Median MTEPS: " << (MTEPS[(ITERS/2)-1] + MTEPS[ITERS/2])/2 << endl; os << "Third Quartile MTEPS: " << (MTEPS[(3*ITERS/4)-1] + MTEPS[3*ITERS/4])/2 << endl; os << "Max MTEPS: " << MTEPS[ITERS-1] << endl; transform(MTEPS, MTEPS+ITERS, INVMTEPS, safemultinv()); // returns inf for zero teps double hteps = static_cast(ITERS) / accumulate(INVMTEPS, INVMTEPS+ITERS, 0.0); os << "Harmonic mean of MTEPS: " << hteps << endl; transform(INVMTEPS, INVMTEPS+ITERS, zero_mean.begin(), bind2nd(minus(), 1/hteps)); deviation = inner_product( zero_mean.begin(),zero_mean.end(), zero_mean.begin(), 0.0 ); deviation = sqrt( deviation / (ITERS-1) ) * (hteps*hteps); // harmonic_std_dev os << "Harmonic standard deviation of MTEPS: " << deviation << endl; SpParHelper::Print(os.str()); } } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/TwitterEdge.h000644 000765 000024 00000031731 13271404146 022461 0ustar00aydinbulucstaff000000 000000 #ifndef _TWITTER_EDGE_ #define _TWITTER_EDGE_ #include #include #include "CombBLAS/CombBLAS.h" struct DetSymmetricize; /** * This is not part of the Combinatorial BLAS library, it's just an example. * A nonzero A(i,j) is present if at least one edge (of any type) * from vertex i to vertex j exists. Multiple types of edges are supported: follower/retweeter (or both) **/ class TwitterEdge { public: TwitterEdge(): count(0), follower(0), latest(0) {}; template TwitterEdge(X x):count(0), follower(0), latest(0) {}; // any upcasting constructs the default object too TwitterEdge(short mycount, bool myfollow, time_t mylatest):count(mycount), follower(myfollow), latest(mylatest) {}; bool isFollower() const { return follower; }; bool isRetwitter() const { return (count > 0); }; bool TweetWithinInterval (time_t begin, time_t end) const { return ((count > 0) && (begin <= latest && latest <= end)); }; bool TweetSince (time_t begin) const { return ((count > 0) && (begin <= latest)); }; bool LastTweetBy (time_t end) const { return ((count > 0) && (latest <= end)); }; operator bool () const { return true; } ; // Type conversion operator (ABAB: Shoots in the foot by implicitly converting many things) TwitterEdge & operator+=(const TwitterEdge & rhs) { std::cout << "Error: TwitterEdge::operator+=() shouldn't be executed" << std::endl; count += rhs.count; follower |= rhs.follower; if(rhs.count > 0) // ensure that addition with additive identity doesn't change "latest" latest = std::max(latest, rhs.latest); return *this; } bool operator ==(const TwitterEdge & b) const { return ((follower == b.follower) && (latest == b.latest) && (count == b.count)); } friend std::ostream& operator<<( std::ostream& os, const TwitterEdge & twe); friend TwitterEdge operator*( const TwitterEdge & a, const TwitterEdge & b); private: bool follower; // default constructor sets all to zero time_t latest; // not assigned if no retweets happened short count; template friend class TwitterReadSaveHandler; friend struct DetSymmetricize; }; std::ostream& operator<<(std::ostream& os, const TwitterEdge & twe ) { if( twe.follower == 0 && twe.latest == 0 && twe.count == 0) os << 0; else os << 1; return os; }; TwitterEdge operator*( const TwitterEdge & a, const TwitterEdge & b) { // One of the parameters is an upcast from bool (used in Indexing), so return the other one if(a == TwitterEdge()) return b; else return a; } template class TwitterReadSaveHandler { public: TwitterReadSaveHandler() {}; TwitterEdge getNoNum(IT row, IT col) { return TwitterEdge(); } MPI_Datatype getMPIType() { return combblas::MPIType(); // utilize the MPI type cache } void binaryfill(FILE * rFile, IT & row, IT & col, TwitterEdge & val) { TwitterInteraction twi; size_t entryLength = fread (&twi,sizeof(TwitterInteraction),1,rFile); row = twi.from - 1 ; col = twi.to - 1; val = TwitterEdge(twi.retweets, twi.follow, twi.twtime); if(entryLength != 1) std::cout << "Not enough bytes read in binaryfill " << std::endl; } size_t entrylength() { return sizeof(TwitterInteraction); } template TwitterEdge read(std::basic_istream& is, IT row, IT col) { TwitterEdge tw; is >> tw.follower; is >> tw.count; if(tw.count > 0) { std::string date; std::string time; is >> date; is >> time; struct tm timeinfo; int year, month, day, hour, min, sec; sscanf (date.c_str(),"%d-%d-%d",&year, &month, &day); sscanf (time.c_str(),"%d:%d:%d",&hour, &min, &sec); memset(&timeinfo, 0, sizeof(struct tm)); timeinfo.tm_year = year - 1900; // year is "years since 1900" timeinfo.tm_mon = month - 1 ; // month is in range 0...11 timeinfo.tm_mday = day; // range 1...31 timeinfo.tm_hour = hour; // range 0...23 timeinfo.tm_min = min; // range 0...59 timeinfo.tm_sec = sec; // range 0. tw.latest = timegm(&timeinfo); if(tw.latest == -1) { std::cout << "Can not parse time date" << std::endl; exit(-1);} } else { tw.latest = 0; // initialized to dummy } //cout << row << " follows " << col << "? : " << tw.follower << " and the retweet count is " << tw.count << endl; return tw; } template void save(std::basic_ostream& os, const TwitterEdge & tw, IT row, IT col) // save is NOT compatible with read { os << row << "\t" << col << "\t"; os << tw.follower << "\t"; os << tw.count << "\t"; os << tw.latest << std::endl; } private: struct TwitterInteraction { int32_t from; int32_t to; bool follow; int16_t retweets; time_t twtime; }; }; struct ParentType { ParentType():id(-1) { }; ParentType(int64_t myid):id(myid) { }; int64_t id; bool operator ==(const ParentType & rhs) const { return (id == rhs.id); } bool operator !=(const ParentType & rhs) const { return (id != rhs.id); } ParentType & operator+=(const ParentType & rhs) { std::cout << "Adding parent with id: " << rhs.id << " to this one with id " << id << std::endl; return *this; } const ParentType operator++(int) // for iota { ParentType temp(*this); // post-fix requirement ++id; return temp; } friend std::ostream& operator<<(std::ostream& os, const ParentType & twe ); template friend ParentType operator+( const IT & left, const ParentType & right); }; std::ostream& operator<<(std::ostream& os, const ParentType & twe ) { os << "Parent=" << twe.id; return os; }; ParentType NumSetter(ParentType & num, int64_t index) { return ParentType(index); } template ParentType operator+( const IT & left, const ParentType & right) { return ParentType(left+right.id); } // forward declaration template void select2nd(void * invec, void * inoutvec, int * len, MPI_Datatype *datatype); template static VECTYPE filtered_select2nd(const TwitterEdge & arg1, const VECTYPE & arg2, time_t & sincedate) { if(sincedate == -1) // uninitialized { struct tm timeinfo; memset(&timeinfo, 0, sizeof(struct tm)); int year, month, day, hour, min, sec; year = 2009; month = 7; day = 1; hour = 0; min = 0; sec = 0; timeinfo.tm_year = year - 1900; // year is "years since 1900" timeinfo.tm_mon = month - 1 ; // month is in range 0...11 timeinfo.tm_mday = day; // range 1...31 timeinfo.tm_hour = hour; // range 0...23 timeinfo.tm_min = min; // range 0...59 timeinfo.tm_sec = sec; // range 0. sincedate = timegm(&timeinfo); std::ostringstream outs; outs << "Initializing since date (only once) to " << sincedate << std::endl; combblas::SpParHelper::Print(outs.str()); } if(arg1.isRetwitter() && arg1.LastTweetBy(sincedate)) // T1 is of type edges for BFS { return arg2; } else { SR::returnedSAID(true); return VECTYPE(); // return null-type parent id (for BFS) or // double() for MIS - POD objects are zero initilied } } //! Filters are only indirectly supported in Combinatorial BLAS //! KDT generates them by embedding their filter stack and pushing those //! predicates to the SR::multiply() function, conceptually like //! if(predicate(maxrix_val)) { bin_op(xxx) } //! Here we emulate this filtered traversal approach. struct LatestRetwitterBFS { static MPI_Op MPI_BFSADD; static ParentType id() { return ParentType(); } // additive identity // the default argument means that this function can be used like this: // if (returnedSAID()) {...} // which is how it is called inside CombBLAS routines. That call conveniently clears the flag for us. static bool returnedSAID(bool setFlagTo = false) { static bool flag = false; bool temp = flag; // save the current flag value to be returned later. Saves an if statement. flag = setFlagTo; // set/clear the flag. return temp; } static ParentType add(const ParentType & arg1, const ParentType & arg2) { return ((arg2 == ParentType()) ? arg1: arg2); } static MPI_Op mpi_op() { MPI_Op_create(select2nd, false, &MPI_BFSADD); // \todo {do this once only, by greating a MPI_Op buffer} return MPI_BFSADD; } static time_t sincedate; static ParentType multiply(const TwitterEdge & arg1, const ParentType & arg2) { return filtered_select2nd(arg1, arg2, sincedate); } static void axpy(TwitterEdge a, const ParentType & x, ParentType & y) { y = add(y, multiply(a, x)); } }; time_t LatestRetwitterBFS::sincedate = -1; // select2nd for doubles template void select2nd(void * invec, void * inoutvec, int * len, MPI_Datatype *datatype) { T * pinvec = static_cast(invec); T * pinoutvec = static_cast(inoutvec); for (int i = 0; i < *len; i++) { pinoutvec[i] = SR::add(pinvec[i], pinoutvec[i]); } } MPI_Op LatestRetwitterBFS::MPI_BFSADD; struct getfringe: public std::binary_function { ParentType operator()(ParentType x, const ParentType & y) const { return x; } }; // x: Parent type (always 1 if exits, sparse) // y: degree (dense) struct seldegree: public std::binary_function { int64_t operator()(ParentType x, const int64_t & y) const { return y; } }; // This is like an "isparentset" with the extra parameter that we don't care struct passifthere: public std::binary_function { bool operator()(ParentType x, const int64_t & y) const { return (x != ParentType()); } }; // DoOp for MIS's EWiseApply struct is2ndSmaller: public std::binary_function { bool operator()(double m, double c) const { return (c < m); } }; // BinOp for MIS's EWiseApply struct return1_uint8: public std::binary_function { uint8_t operator() (double t1, double t2) { return (uint8_t) 1; } }; // x: elements from fringe (sparse), y: elements from parents (dense) // return true for edges that are not filtered out, and not previously discovered // if the edge was filtered out, then x would be ParentType() // if y was already discovered its parent would NOT be ParentType() struct keepinfrontier_f: public std::binary_function { bool operator()(ParentType x, const ParentType & y) const { return ( x != ParentType() && y == ParentType()) ; } }; struct isparentset: public std::unary_function { bool operator()(const ParentType & x) const { return ( x != ParentType() ) ; } }; // Matrix type: TwitterEdge // Vector type: double struct LatestRetwitterMIS { static double id() { return 0.0; } // additive identity // the default argument means that this function can be used like this: // if (returnedSAID()) {...} // which is how it is called inside CombBLAS routines. That call conveniently clears the flag for us. static bool returnedSAID(bool setFlagTo = false) { static bool flag = false; bool temp = flag; // save the current flag value to be returned later. Saves an if statement. flag = setFlagTo; // set/clear the flag. return temp; } static double add(const double & arg1, const double & arg2) { return std::min(arg1, arg2); } static MPI_Op mpi_op() { return MPI_MIN; } static time_t sincedate; static double multiply(const TwitterEdge & arg1, const double & arg2) // filtered select2nd { return filtered_select2nd(arg1, arg2, sincedate); } static void axpy(TwitterEdge a, const double & x, double & y) { y = add(y, multiply(a, x)); } }; // Matrix type: TwitterEdge // Vector type: double struct LatestRetwitterSelect2nd // also used for finding neighbors of the candidate set in MIS { static MPI_Op MPI_SEL2NDADD; static double id() { return 0.0; } // additive identity // the default argument means that this function can be used like this: // if (returnedSAID()) {...} // which is how it is called inside CombBLAS routines. That call conveniently clears the flag for us. static bool returnedSAID(bool setFlagTo = false) { static bool flag = false; bool temp = flag; // save the current flag value to be returned later. Saves an if statement. flag = setFlagTo; // set/clear the flag. return temp; } static double add(const double & arg1, const double & arg2) { return arg2; } static MPI_Op mpi_op() { MPI_Op_create(select2nd, false, &MPI_SEL2NDADD); // \todo {do this once only, by greating a MPI_Op buffer} return MPI_SEL2NDADD; } static time_t sincedate; static double multiply(const TwitterEdge & arg1, const double & arg2) // filtered select2nd { return filtered_select2nd(arg1, arg2, sincedate); } static void axpy(TwitterEdge a, const double & x, double & y) { y = add(y, multiply(a, x)); } }; time_t LatestRetwitterMIS::sincedate = -1; time_t LatestRetwitterSelect2nd::sincedate = -1; MPI_Op LatestRetwitterSelect2nd::MPI_SEL2NDADD; #endif CombBLAS_beta_16_2/Applications/makefile-nersc000644 000765 000024 00000015246 13271404146 022674 0ustar00aydinbulucstaff000000 000000 #INCADD = -I/global/homes/a/abuluc \ # -I/opt/cray/pmi/1.0-1.0000.7901.22.1.ss/include \ # -I/opt/cray/mpt/5.1.0/xt/seastar/mpich2-gnu/include \ # -I/opt/cray/mpt/5.1.0/xt/seastar/sma/include \ # -I/opt/xt-libsci/10.4.8/gnu/include \ # -I/usr/include/alps \ # -I/opt/gcc/4.5.1/snos/include/g++ -I/opt/gcc/4.5.1/snos/include/g++/x86_64-suse-linux -I/opt/gcc/4.5.1/snos/include/g++/backward \ # -I/usr/local/include \ # -I/opt/gcc/4.5.1/snos/include \ # -I/opt/gcc/4.5.1/snos/lib/gcc/x86_64-suse-linux/4.5.1/include \ # -I/opt/gcc/4.5.1/snos/lib/gcc/x86_64-suse-linux/4.5.1/include-fixed \ # -I/usr/include # LIBADD = -L/opt/gcc/4.5.1/snos/lib/gcc/x86_64-suse-linux/4.5.1/../../../ BOOST = $(BOOST_DIR) INCADD = -I$(BOOST)/include # notes for configure: # -fno-exceptions does not work with MPICH2 # -fno-rtti does not work with tr1:tuples GCCOPT = -O2 -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ #-DNDEBUG (disables important assertions) GCCDEB = -g -fno-inline -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ #-DDEBUG OPTPGI = -fast -Mipa=fast,inline -Msmartalloc --zc_eh -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ COMPILER = CC GCCFLAGS = $(GCCOPT) -DTIMING PGIFLAGS = $(INCADD) $(OPTPGI) -DCOMBBLAS_BOOST CRAYFLAGS = $(INCADD) -DCOMBBLAS_BOOST -DCRAYCOMP -h msglevel_4 FLAGS = $(GCCFLAGS) -std=c++11 -std=gnu++14 -DTHREADED -fopenmp #-DCOMBBLAS_DEBUG #-DKSELECTLIMIT=40 COMBBLAS = .. # # build Graph500 generator # $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator mmio.o: ../mmio.c cc $(INCADD) $(FLAGS) -c -o mmio.o ../mmio.c CommGrid.o: ../CommGrid.cpp ../CommGrid.h $(COMPILER) $(FLAGS) -c -o CommGrid.o ../CommGrid.cpp MPIType.o: ../MPIType.cpp ../MPIType.h $(COMPILER) $(FLAGS) -c -o MPIType.o ../MPIType.cpp MemoryPool.o: ../MemoryPool.cpp ../SpDefs.h $(COMPILER) $(FLAGS) -c -o MemoryPool.o ../MemoryPool.cpp hash.o: ../hash.cpp ../hash.hpp $(COMPILER) $(FLAGS) $(INCADD) -c -o hash.o ../hash.cpp BetwCent.o: BetwCent.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp $(COMPILER) $(FLAGS) -c -o BetwCent.o BetwCent.cpp MCL.o: MCL.cpp CC.h WriteMCLClusters.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp $(COMPILER) $(FLAGS) -c -o MCL.o MCL.cpp CC.o: CC.cpp CC.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp $(COMPILER) $(INCADD) $(FLAGS) -c -o CC.o CC.cpp APowers.o: APowers.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp $(COMPILER) $(FLAGS) -c -o APowers.o APowers.cpp TopDownBFS.o: TopDownBFS.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(COMPILER) $(INCADD) $(FLAGS) -c -o TopDownBFS.o TopDownBFS.cpp DirOptBFS.o: DirOptBFS.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(COMPILER) $(INCADD) $(FLAGS) -c -o DirOptBFS.o DirOptBFS.cpp FilteredBFS.o: FilteredBFS.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpParHelper.cpp ../Friends.h TwitterEdge.h ../MPIType.h $(COMPILER) $(INCADD) $(FLAGS) -c -o FilteredBFS.o FilteredBFS.cpp FilteredMIS.o: FilteredMIS.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpParHelper.cpp ../Friends.h TwitterEdge.h ../MPIType.h ../FullyDistVec.cpp $(COMPILER) $(INCADD) $(FLAGS) -c -o FilteredMIS.o FilteredMIS.cpp nqp.o: nqp.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp $(COMPILER) $(FLAGS) -c -o nqp.o nqp.cpp mcl: CommGrid.o MPIType.o MCL.o mmio.o hash.o $(COMPILER) $(FLAGS) -o mcl MCL.o mmio.o CommGrid.o MPIType.o hash.o cc: MemoryPool.o CommGrid.o MPIType.o CC.o mmio.o hash.o $(COMPILER) $(FLAGS) -o cc CC.o mmio.o CommGrid.o MPIType.o hash.o nqp: CommGrid.o MPIType.o nqp.o $(COMPILER) $(FLAGS) -o nqp nqp.o CommGrid.o MPIType.o apowers: CommGrid.o MPIType.o APowers.o $(COMPILER) $(FLAGS) -o apowers APowers.o CommGrid.o MPIType.o tdbfs: CommGrid.o MPIType.o TopDownBFS.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o tdbfs TopDownBFS.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq dobfs: CommGrid.o MPIType.o DirOptBFS.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o dobfs DirOptBFS.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq betwcent: CommGrid.o MPIType.o BetwCent.o $(COMPILER) $(INCADD) $(FLAGS) -o betwcent BetwCent.o CommGrid.o MPIType.o fbfs: CommGrid.o MPIType.o FilteredBFS.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o fbfs FilteredBFS.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq fmis: CommGrid.o MPIType.o FilteredMIS.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o fmis FilteredMIS.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq BPMaximalMatching.o: BPMaximalMatching.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(COMPILER) $(FLAGS) -c -o BPMaximalMatching.o BPMaximalMatching.cpp BPMaximalMatching: CommGrid.o MPIType.o BPMaximalMatching.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(FLAGS) -o BPMaximalMatching BPMaximalMatching.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq BPMaximumMatching.o: BPMaximumMatching.cpp BPMaximalMatching.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(COMPILER) $(INCADD) $(FLAGS) -c -o BPMaximumMatching.o BPMaximumMatching.cpp bpmm: CommGrid.o MPIType.o BPMaximumMatching.o mmio.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(INCADD) $(FLAGS) -o bpmm BPMaximumMatching.o CommGrid.o mmio.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq clean: rm -f betwcent rm -f driver rm -f mcl rm -f fbfs rm -f fmis rm -f apowers rm -f dobfs rm -f tdbfs rm -f BPMaximumMatching rm -f *.o rm -f ../graph500-1.2/generator/*.o rm -f ../graph500-1.2/generator/libgraph_generator_seq.a cleanout: rm out.* rm err.* CombBLAS_beta_16_2/Applications/gridtransposer.py000644 000765 000024 00000000632 13212627377 023505 0ustar00aydinbulucstaff000000 000000 import sys filename = "MPICH_RANK_ORDER" file = open(filename, "w") print "Printing --transposed-- virtual mapping to MPI ranks to MPICH_RANK_ORDER" print "The processor grid dimensions are ", sys.argv[1], "-by-", sys.argv[1] dim = int(sys.argv[1]); gen = range(dim); for i in range(dim): line = [x * dim + i for x in gen] for item in line: file.write(str(item)+ ',') file.write('\n'); file.close(); CombBLAS_beta_16_2/Applications/SpMMError.cpp000644 000765 000024 00000005436 13271404146 022416 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace combblas; double cblas_alltoalltime; double cblas_allgathertime; typedef SelectMaxSRing SR; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; typedef SpParMat < int64_t, int, SpDCCols > PSpMat_Int; #define ValueType int64_t typedef SpDCCols DCColsType; typedef SpParMat < int64_t, ValueType, DCColsType > PSpMat_Int64; typedef PSpMat_Int64 MatType; DECLARE_PROMOTE(MatType, MatType, MatType) DECLARE_PROMOTE(DCColsType, DCColsType, DCColsType) int main(int argc, char* argv[]) { int torusi[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; int torusj[] = {3,0,1,2,7,4,5,6,11,8,9,10,15,12,13,14,1,2,3,0,5,6,7,4,9,10,11,8,13,14,15,12,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3}; int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); { SpParHelper::Print("Usage: SpMMError [arg]\nIf arg is present then matrix will be read from torus.mtx (no error).\nIf arg is absent, matrix will be created from vectors (error present)\n\n"); // Declare objects MatType G1; MatType G2; if (argc > 1) { SpParHelper::Print("Reading torus.mtx\n"); std::ifstream input("torus.mtx"); G1.ReadDistribute(input, 0); // read it from file ifstream input2("torus.mtx"); G2.ReadDistribute(input2, 0); SpParHelper::Print("Read input\n"); } else { SpParHelper::Print("Creating matrices from built-in vectors\n"); FullyDistVec dpvi(64, 0, 0); FullyDistVec dpvj(64, 0, 0); FullyDistVec dpvv(64, 1, 0); for (int i = 0; i <64; i++) { dpvi.SetElement(i, torusi[i]); dpvj.SetElement(i, torusj[i]); } dpvi.DebugPrint(); dpvj.DebugPrint(); G1 = MatType(16, 16, dpvi, dpvj, dpvv); G2 = MatType(16, 16, dpvi, dpvj, dpvv); ofstream out1("G1.txt"); ofstream out2("G2.txt"); out1 << G1; out2 << G2; } MatType G3(G1); G1.PrintInfo(); G2.PrintInfo(); G3.PrintInfo(); SpParHelper::Print("The nnz values should be 112, 112, 112:\n"); MatType G12 = Mult_AnXBn_Synch >(G1, G2); G12.PrintInfo(); ofstream out12("G12.txt"); out12 << G12; MatType G13 = Mult_AnXBn_Synch >(G1, G3); G13.PrintInfo(); MatType G23 = Mult_AnXBn_Synch >(G2, G3); G23.PrintInfo(); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/SpMSpV-IPDPS2017/SpMSpVBench.cpp000644 000765 000024 00000046010 13271404146 025170 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.5 -------------------------------------------------*/ /* date: 10/09/2015 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2015, The Regents of the University of California 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. */ #include "CombBLAS/CombBLAS.h" #include #include #include #include #include #include #include #include using namespace combblas; #ifdef _OPENMP int cblas_splits = omp_get_max_threads(); #else int cblas_splits = 1; #endif double cblas_alltoalltime; double cblas_allgathertime; double cblas_mergeconttime; double cblas_transvectime; double cblas_localspmvtime; #define EDGEFACTOR 16 int ITERS; using namespace std; typedef SelectMaxSRing SR; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_s32p64; // sequentially use 32-bits for local matrices, but parallel semantics are 64-bits typedef SpParMat < int64_t, int, SpDCCols > PSpMat_s32p64_Int; // similarly mixed, but holds integers as upposed to booleans typedef SpParMat < int64_t, int64_t, SpDCCols > PSpMat_Int64; typedef SpParMat < int64_t, bool, SpCCols > Par_CSC_Bool; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } struct SelectMinSR { typedef int64_t T_promote; static T_promote id(){ return -1; }; static bool returnedSAID() { return false; } //static MPI_Op mpi_op() { return MPI_MIN; }; static T_promote add(const T_promote & arg1, const T_promote & arg2) { return std::min(arg1, arg2); } static T_promote multiply(const bool & arg1, const T_promote & arg2) { return arg2; } static void axpy(bool a, const T_promote & x, T_promote & y) { y = std::min(y, x); } }; void BFS_CSC(PSpMat_s32p64 Aeff, int64_t source, FullyDistVec degrees) { int nthreads=1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif Par_CSC_Bool ABoolCSC (Aeff); PreAllocatedSPA SPA(ABoolCSC.seq(), nthreads*4); double tspmvall=0, tall=0; int iterall = 0; int visitedE = 0; int visitedV = 0; for(int i=0; i grid, IT globallen, NT initval); FullyDistVec parents ( Aeff.getcommgrid(), Aeff.getncol(), (int64_t) -1); // identity is -1 // FullyDistSpVec ( shared_ptr grid, IT glen); FullyDistSpVec fringe(Aeff.getcommgrid(), Aeff.getncol()); // numerical values are stored 0-based MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); fringe.SetElement(source, source); int iterations = 0; while(fringe.getnnz() > 0) { int64_t xnnz = fringe.getnnz(); fringe.setNumToInd(); double tstart = MPI_Wtime(); SpMV(ABoolCSC, fringe, fringe, false, SPA); double tspmv = MPI_Wtime()-tstart; tspmvall += tspmv; int64_t ynnz = fringe.getnnz(); fringe = EWiseMult(fringe, parents, true, (int64_t) -1); // clean-up vertices that already has parents ostringstream outs1; outs1 << "iteration: " << iterations << " xnnz: "<< xnnz << " ynnz: " << ynnz << " SpMSpV time: " << tspmv << endl; SpParHelper::Print(outs1.str()); parents.Set(fringe); iterations++; } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); tall += (t2 - t1); FullyDistSpVec parentsp = parents.Find(bind2nd(greater(), -1)); parentsp.Apply(myset(1)); // we use degrees on the directed graph, so that we don't count the reverse edges in the teps score int64_t nedges = EWiseMult(parentsp, degrees, false, (int64_t) 0).Reduce(plus(), (int64_t) 0); visitedE += nedges; visitedV += parentsp.Reduce(plus(), (int64_t) 0); iterall += iterations; } int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank == 0) { cout << "\nOverall stats:" << endl; cout << " starting vertex: " << source << endl; cout << " Avg number iterations: " << iterall/ITERS << endl; cout << " Avg number of vertices found: " << visitedV/ITERS << endl; cout << " Avg Number of edges traversed: " << visitedE/ITERS << endl; cout << " Avg SpMSpV time: " << tspmvall/ITERS << endl; cout << " Avg Total time: " << tall/ITERS << endl; } } void BFS_DCSC(PSpMat_s32p64 Aeff1, int64_t source, FullyDistVec degrees) { PSpMat_Bool Aeff = Aeff1; OptBuf optbuf; // let indices be 32-bits //Aeff.OptimizeForGraph500(optbuf); int nthreads=1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); cblas_splits = nthreads*4; } Aeff.ActivateThreading(cblas_splits); #endif double tspmvall=0, tall=0; int iterall = 0; int visitedE = 0; int visitedV = 0; for(int i=0; i grid, IT globallen, NT initval); FullyDistVec parents ( Aeff.getcommgrid(), Aeff.getncol(), (int64_t) -1); // identity is -1 // FullyDistSpVec ( shared_ptr grid, IT glen); FullyDistSpVec fringe(Aeff.getcommgrid(), Aeff.getncol()); // numerical values are stored 0-based MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); fringe.SetElement(source, source); int iterations = 0; while(fringe.getnnz() > 0) { int64_t xnnz = fringe.getnnz(); fringe.setNumToInd(); double tstart = MPI_Wtime(); SpMV(Aeff, fringe, fringe, false); double tspmv = MPI_Wtime()-tstart; tspmvall += tspmv; int64_t ynnz = fringe.getnnz(); fringe = EWiseMult(fringe, parents, true, (int64_t) -1); // clean-up vertices that already has parents ostringstream outs1; outs1 << "iteration: " << iterations << " xnnz: "<< xnnz << " ynnz: " << ynnz << " SpMSpV time: " << tspmv << endl; SpParHelper::Print(outs1.str()); parents.Set(fringe); iterations++; } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); tall += (t2 - t1); FullyDistSpVec parentsp = parents.Find(bind2nd(greater(), -1)); parentsp.Apply(myset(1)); // we use degrees on the directed graph, so that we don't count the reverse edges in the teps score int64_t nedges = EWiseMult(parentsp, degrees, false, (int64_t) 0).Reduce(plus(), (int64_t) 0); visitedE += nedges; visitedV += parentsp.Reduce(plus(), (int64_t) 0); iterall += iterations; } int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank == 0) { cout << "\nOverall stats:" << endl; cout << " starting vertex: " << source << endl; cout << " Avg number iterations: " << iterall/ITERS << endl; cout << " Avg number of vertices found: " << visitedV/ITERS << endl; cout << " Avg Number of edges traversed: " << visitedE/ITERS << endl; cout << " Avg SpMSpV time: " << tspmvall/ITERS << endl; cout << " Avg Total time: " << tall/ITERS << endl; } } void BFS_CSC_Split(PSpMat_s32p64 Aeff, int64_t source, FullyDistVec degrees) { int nthreads=1; Par_CSC_Bool ABoolCSC(Aeff); #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); cblas_splits = nthreads*4; } ABoolCSC.ActivateThreading(cblas_splits); #endif double tspmvall=0, tall=0; int iterall = 0; int visitedE = 0; int visitedV = 0; for(int i=0; i grid, IT globallen, NT initval); FullyDistVec parents ( Aeff.getcommgrid(), Aeff.getncol(), (int64_t) -1); // identity is -1 // FullyDistSpVec ( shared_ptr grid, IT glen); FullyDistSpVec fringe(Aeff.getcommgrid(), Aeff.getncol()); // numerical values are stored 0-based MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); fringe.SetElement(source, source); int iterations = 0; while(fringe.getnnz() > 0) { int64_t xnnz = fringe.getnnz(); fringe.setNumToInd(); double tstart = MPI_Wtime(); SpMV(ABoolCSC, fringe, fringe, false); double tspmv = MPI_Wtime()-tstart; tspmvall += tspmv; int64_t ynnz = fringe.getnnz(); fringe = EWiseMult(fringe, parents, true, (int64_t) -1); // clean-up vertices that already has parents ostringstream outs1; outs1 << "iteration: " << iterations << " xnnz: "<< xnnz << " ynnz: " << ynnz << " SpMSpV time: " << tspmv << endl; SpParHelper::Print(outs1.str()); parents.Set(fringe); iterations++; } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); tall += (t2-t1); FullyDistSpVec parentsp = parents.Find(bind2nd(greater(), -1)); parentsp.Apply(myset(1)); // we use degrees on the directed graph, so that we don't count the reverse edges in the teps score int64_t nedges = EWiseMult(parentsp, degrees, false, (int64_t) 0).Reduce(plus(), (int64_t) 0); visitedE += nedges; visitedV += parentsp.Reduce(plus(), (int64_t) 0); iterall += iterations; } int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank == 0) { cout << "\nOverall stats:" << endl; cout << " starting vertex: " << source << endl; cout << " Avg number iterations: " << iterall/ITERS << endl; cout << " Avg number of vertices found: " << visitedV/ITERS << endl; cout << " Avg Number of edges traversed: " << visitedE/ITERS << endl; cout << " Avg SpMSpV time: " << tspmvall/ITERS << endl; cout << " Avg Total time: " << tall/ITERS << endl; } } int main(int argc, char* argv[]) { int nprocs, myrank; int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./SpMSpVBench <-input|-rmat|-er> " << endl; cout << " optional parameters:" << endl; cout << " -source \"source of BFS\" (default: 0) " << endl; cout << " -iter \"number of BFS iterations\" (default: 1)" << endl; cout << "Example with a user supplied matrix:" << endl; cout << " mpirun -np 4 ./SpMSpVBench -input a.mtx -source 2" << endl; cout << "Example with a user supplied matrix (pre-permute the input matrix for load balance):" << endl; cout << " mpirun -np 4 ./SpMSpVBench -input a.mtx -permute" << endl; cout << "Example with RMAT matrix: mpirun -np 4 ./SpMSpVBench -rmat 18" << endl; cout << "Example with an Erdos-Renyi matrix: mpirun -np 4 ./SpMSpVBench -er 18" << endl; } MPI_Finalize(); return -1; } { shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); int nthreads=1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif // Declare objects PSpMat_Bool A(fullWorld); PSpMat_s32p64 Aeff(fullWorld); FullyDistVec degrees(fullWorld); // degrees of vertices (including multi-edges and self-loops) FullyDistVec nonisov(fullWorld); // id's of non-isolated (connected) vertices unsigned scale; bool scramble = false; int source = 0; ITERS = 1; bool randpermute = false; bool symm = false; int maxthreads = nthreads; int minthreads = nthreads; string filename(argv[2]); for (int i = 1; i < argc; i++) { if (strcmp(argv[i],"-permute")==0) { if(myrank == 0) cout << "Randomly permute the matrix " << endl; randpermute = true; } if (strcmp(argv[i],"-source")==0) { source = atoi(argv[i + 1]); if(myrank == 0) cout << "Source vertex: " << source << endl; } if (strcmp(argv[i],"-iter")==0) { ITERS = atoi(argv[i + 1]); if(myrank == 0) cout << "Number of iterations: " << ITERS << endl; } } if(string(argv[1]) == string("-input")) // input option { //A.ReadDistribute(string(argv[2]), 0); // read it from file A.ParallelReadMM(filename, true, maximum()); SpParHelper::Print("Read input\n"); PSpMat_Int64 * G = new PSpMat_Int64(A); G->Reduce(degrees, Row, plus(), static_cast(0)); // identity is 0 delete G; Symmetricize(A); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); A.Reduce(*ColSums, Column, plus(), static_cast(0)); // plus matches the type of the output vector nonisov = ColSums->FindInds(bind2nd(greater(), 0)); // only the indices of non-isolated vertices delete ColSums; if(randpermute) nonisov.RandPerm(); A(nonisov, nonisov, true); // in-place permute to save memory degrees = degrees(nonisov); // fix the degrees array too FullyDistVec newsource = nonisov.FindInds(bind2nd(equal_to(), source)); source = newsource.GetElement(0); degrees = degrees(nonisov); // fix the source vertex too Aeff = PSpMat_s32p64(A); A.FreeMemory(); } else if(string(argv[1]) == string("-rmat")) { unsigned scale; scale = static_cast(atoi(argv[2])); double initiator[4] = {.57, .19, .19, .05}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); MPI_Barrier(MPI_COMM_WORLD); A = PSpMat_Bool(*DEL, false); delete DEL; Symmetricize(A); Aeff = PSpMat_s32p64(A); Aeff.Reduce(degrees, Row, plus(), static_cast(0)); // identity is 0 A.FreeMemory(); } else if(string(argv[1]) == string("-er")) { unsigned scale; scale = static_cast(atoi(argv[2])); double initiator[4] = {.25, .25, .25, .25}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); MPI_Barrier(MPI_COMM_WORLD); A = PSpMat_Bool(*DEL, false); delete DEL; Symmetricize(A); Aeff = PSpMat_s32p64(A); Aeff.Reduce(degrees, Row, plus(), static_cast(0)); // identity is 0 A.FreeMemory(); } else { SpParHelper::Print("Unknown input option\n"); MPI_Finalize(); return -1; } Aeff.PrintInfo(); float balance = Aeff.LoadImbalance(); ostringstream outs; outs << "Load balance: " << balance << endl; SpParHelper::Print(outs.str()); MPI_Barrier(MPI_COMM_WORLD); SpParHelper::Print("-------------------------------------------------\n"); SpParHelper::Print ("BFS With CSC matrix and SpMSpV-bucket algorithm \n"); SpParHelper::Print("-------------------------------------------------\n"); BFS_CSC(Aeff, source, degrees); SpParHelper::Print("-------------------------------------------------\n"); SpParHelper::Print ("BFS With Split CSC matrix and SpMSpV-heapsort algorithm \n"); SpParHelper::Print("-------------------------------------------------\n"); BFS_CSC_Split(Aeff, source, degrees); SpParHelper::Print("-------------------------------------------------\n"); SpParHelper::Print ("BFS With DCSC matric and SpMSpV-SPA algorithm \n"); SpParHelper::Print("-------------------------------------------------\n"); BFS_DCSC(Aeff, source, degrees); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Applications/SpMSpV-IPDPS2017/CMakeLists.txt000644 000765 000024 00000000376 13271404146 025141 0ustar00aydinbulucstaff000000 000000 # Top level directory has the include files ADD_EXECUTABLE( SpMSpVBench SpMSpVBench.cpp ) TARGET_LINK_LIBRARIES( SpMSpVBench CombBLAS) ADD_TEST(NAME SpMSpVBench_test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ -rmat 18 ) CombBLAS_beta_16_2/Applications/SpMSpV-IPDPS2017/makefile-mac000644 000765 000024 00000002651 13212627377 024645 0ustar00aydinbulucstaff000000 000000 CXX := mpicxx CXXFLAGS := -std=c++11 -std=gnu++14 -O3 -fopenmp -fpermissive -DTHREADED #-DDETERMINISTIC -DTIMING #-DBENCHMARK_SPMSPV COMBBLAS = ../.. $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator %.o : %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/Tommy/%.o : $(COMBBLAS)/Tommy/%.c $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/usort/%.o : $(COMBBLAS)/usort/src/%.cpp $(CXX) -I$(COMBBLAS)/usort/include $(CXXFLAGS) -o $@ -c $< clean: rm -rf ../../*.o rm -f SpMSpVBench rm -f *.o TOMMYS = $(COMBBLAS)/Tommy/tommyhashdyn.o $(COMBBLAS)/Tommy/tommyhash.o $(COMBBLAS)/Tommy/tommylist.o USORT = $(COMBBLAS)/usort/binUtils.o $(COMBBLAS)/usort/parUtils.o SpMSpVBench.o: SpMSpVBench.cpp $(COMBBLAS)/SpDCCols.cpp $(COMBBLAS)/dcsc.cpp $(COMBBLAS)/SpHelper.h $(COMBBLAS)/SpParMat.h $(COMBBLAS)/ParFriends.h $(COMBBLAS)/SpParMat.cpp $(COMBBLAS)/SpDefs.h $(COMBBLAS)/SpTuples.cpp $(COMBBLAS)/SpImpl.h $(COMBBLAS)/SpCCols.h $(COMBBLAS)/SpCCols.cpp $(COMBBLAS)/csc.cpp $(COMBBLAS)/SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< SpMSpVBench: SpMSpVBench.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq CombBLAS_beta_16_2/Applications/SpMSpV-IPDPS2017/makefile-nersc000644 000765 000024 00000002571 13212627377 025220 0ustar00aydinbulucstaff000000 000000 CXX := CC CXXFLAGS := -std=c++11 -std=gnu++14 -O3 -fopenmp -fpermissive -DTHREADED #-DDETERMINISTIC -DTIMING COMBBLAS = ../.. $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator %.o : %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/Tommy/%.o : $(COMBBLAS)/Tommy/%.c $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/usort/%.o : $(COMBBLAS)/usort/src/%.cpp $(CXX) -I$(COMBBLAS)/usort/include $(CXXFLAGS) -o $@ -c $< clean: rm -rf ../../*.o rm -f tdbfs rm -f *.o TOMMYS = $(COMBBLAS)/Tommy/tommyhashdyn.o $(COMBBLAS)/Tommy/tommyhash.o $(COMBBLAS)/Tommy/tommylist.o USORT = $(COMBBLAS)/usort/binUtils.o $(COMBBLAS)/usort/parUtils.o tdbfs.o: TopDownBFS.cpp $(COMBBLAS)/SpDCCols.cpp $(COMBBLAS)/dcsc.cpp $(COMBBLAS)/SpHelper.h $(COMBBLAS)/SpParMat.h $(COMBBLAS)/ParFriends.h $(COMBBLAS)/SpParMat.cpp $(COMBBLAS)/SpDefs.h $(COMBBLAS)/SpTuples.cpp $(COMBBLAS)/SpImpl.h $(COMBBLAS)/SpCCols.h $(COMBBLAS)/SpCCols.cpp $(COMBBLAS)/csc.cpp $(COMBBLAS)/SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< tdbfs: tdbfs.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq CombBLAS_beta_16_2/usort/CMakeLists.txt000755 000765 000024 00000000763 13271404146 021353 0ustar00aydinbulucstaff000000 000000 # Top level directory has the include files ADD_LIBRARY( Usortlib src/parUtils.cpp src/binUtils.cpp ) target_include_directories(Usortlib PUBLIC $ $) target_include_directories(Usortlib PRIVATE include/usort) install(DIRECTORY include/ DESTINATION include) install(TARGETS Usortlib EXPORT CombBLASTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin INCLUDES DESTINATION include ) CombBLAS_beta_16_2/usort/LICENSE000755 000765 000024 00000002066 13212627401 017612 0ustar00aydinbulucstaff000000 000000 The MIT License (MIT) Copyright (c) 2016 Hari Sundar 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. CombBLAS_beta_16_2/usort/makefile000644 000765 000024 00000015745 13220066202 020305 0ustar00aydinbulucstaff000000 000000 # CMAKE generated file: DO NOT EDIT! # Generated by "Unix Makefiles" Generator, CMake Version 3.9 # Default target executed when no arguments are given to make. default_target: all .PHONY : default_target # Allow only one "make -f Makefile2" at a time, but pass parallelism. .NOTPARALLEL: #============================================================================= # Special targets provided by cmake. # Disable implicit rules so canonical targets will work. .SUFFIXES: # Remove some rules from gmake that .SUFFIXES does not remove. SUFFIXES = .SUFFIXES: .hpux_make_needs_suffix_list # Suppress display of executed commands. $(VERBOSE).SILENT: # A target that is always out of date. cmake_force: .PHONY : cmake_force #============================================================================= # Set environment variables for the build. # The shell in which to execute make rules. SHELL = /bin/sh # The CMake executable. CMAKE_COMMAND = /usr/local/Cellar/cmake/3.9.1/bin/cmake # The command to remove a file. RM = /usr/local/Cellar/cmake/3.9.1/bin/cmake -E remove -f # Escaping for special characters. EQUALS = = # The top-level source directory on which CMake was run. CMAKE_SOURCE_DIR = /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS # The top-level build directory on which CMake was run. CMAKE_BINARY_DIR = /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS #============================================================================= # Targets provided globally by CMake. # Special rule for the target rebuild_cache rebuild_cache: @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." /usr/local/Cellar/cmake/3.9.1/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) .PHONY : rebuild_cache # Special rule for the target rebuild_cache rebuild_cache/fast: rebuild_cache .PHONY : rebuild_cache/fast # Special rule for the target edit_cache edit_cache: @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..." /usr/local/Cellar/cmake/3.9.1/bin/ccmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) .PHONY : edit_cache # Special rule for the target edit_cache edit_cache/fast: edit_cache .PHONY : edit_cache/fast # Special rule for the target test test: @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running tests..." /usr/local/Cellar/cmake/3.9.1/bin/ctest --force-new-ctest-process $(ARGS) .PHONY : test # Special rule for the target test test/fast: test .PHONY : test/fast # The main all target all: cmake_check_build_system cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(CMAKE_COMMAND) -E cmake_progress_start /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS/CMakeFiles /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS/usort/CMakeFiles/progress.marks cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f CMakeFiles/Makefile2 usort/all $(CMAKE_COMMAND) -E cmake_progress_start /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS/CMakeFiles 0 .PHONY : all # The main clean target clean: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f CMakeFiles/Makefile2 usort/clean .PHONY : clean # The main clean target clean/fast: clean .PHONY : clean/fast # Prepare targets for installation. preinstall: all cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f CMakeFiles/Makefile2 usort/preinstall .PHONY : preinstall # Prepare targets for installation. preinstall/fast: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f CMakeFiles/Makefile2 usort/preinstall .PHONY : preinstall/fast # clear depends depend: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 .PHONY : depend # Convenience name for target. usort/CMakeFiles/Usortlib.dir/rule: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f CMakeFiles/Makefile2 usort/CMakeFiles/Usortlib.dir/rule .PHONY : usort/CMakeFiles/Usortlib.dir/rule # Convenience name for target. Usortlib: usort/CMakeFiles/Usortlib.dir/rule .PHONY : Usortlib # fast build rule for target. Usortlib/fast: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f usort/CMakeFiles/Usortlib.dir/build.make usort/CMakeFiles/Usortlib.dir/build .PHONY : Usortlib/fast # target to build an object file src/binUtils.o: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f usort/CMakeFiles/Usortlib.dir/build.make usort/CMakeFiles/Usortlib.dir/src/binUtils.o .PHONY : src/binUtils.o # target to preprocess a source file src/binUtils.i: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f usort/CMakeFiles/Usortlib.dir/build.make usort/CMakeFiles/Usortlib.dir/src/binUtils.i .PHONY : src/binUtils.i # target to generate assembly for a file src/binUtils.s: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f usort/CMakeFiles/Usortlib.dir/build.make usort/CMakeFiles/Usortlib.dir/src/binUtils.s .PHONY : src/binUtils.s # target to build an object file src/parUtils.o: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f usort/CMakeFiles/Usortlib.dir/build.make usort/CMakeFiles/Usortlib.dir/src/parUtils.o .PHONY : src/parUtils.o # target to preprocess a source file src/parUtils.i: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f usort/CMakeFiles/Usortlib.dir/build.make usort/CMakeFiles/Usortlib.dir/src/parUtils.i .PHONY : src/parUtils.i # target to generate assembly for a file src/parUtils.s: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(MAKE) -f usort/CMakeFiles/Usortlib.dir/build.make usort/CMakeFiles/Usortlib.dir/src/parUtils.s .PHONY : src/parUtils.s # Help Target help: @echo "The following are some of the valid targets for this Makefile:" @echo "... all (the default if no target is provided)" @echo "... clean" @echo "... depend" @echo "... rebuild_cache" @echo "... edit_cache" @echo "... test" @echo "... Usortlib" @echo "... src/binUtils.o" @echo "... src/binUtils.i" @echo "... src/binUtils.s" @echo "... src/parUtils.o" @echo "... src/parUtils.i" @echo "... src/parUtils.s" .PHONY : help #============================================================================= # Special targets to cleanup operation of make. # Special rule to run CMake to check the build system integrity. # No rule that depends on this can have commands that come from listfiles # because they might be regenerated. cmake_check_build_system: cd /Users/arifulazad/Documents/Projects/combinatorial-blas-2.0/CombBLAS && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 .PHONY : cmake_check_build_system CombBLAS_beta_16_2/usort/include/000755 000765 000024 00000000000 13271404146 020225 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/usort/README.md000755 000765 000024 00000001246 13212627401 020063 0ustar00aydinbulucstaff000000 000000 # Utah Sorting Librrary Fast distributed sorting routines using MPI and OpenMP. **References** 2. Hari Sundar, Dhairya Malhotra, Karl W Schulz, [Algorithms for high-throughput disk-to-disk sorting](http://dx.doi.org/10.1145/2503210.2503259), Proceedings of the ACM/IEEE International Conference for High Performance Computing, Networking, Storage and Analysis (**SC13**), 2013. 3. Hari Sundar, Dhairya Malhotra, George Biros, [HykSort: a new variant of hypercube quicksort on distributed memory architectures](http://dx.doi.org/10.1145/2464996.2465442), Proceedings of the 27th international ACM conference on international conference on supercomputing (**ICS13**), 2013. CombBLAS_beta_16_2/usort/.gitignore000755 000765 000024 00000000362 13212627401 020572 0ustar00aydinbulucstaff000000 000000 # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app CombBLAS_beta_16_2/usort/main000755 000765 000024 00001037514 13212627401 017463 0ustar00aydinbulucstaff000000 000000 Ïúíþ€˜€!H__PAGEZEROx__TEXTàà__text__TEXTð k7ð €__text_startup__TEXT`A.`A€__stubs__TEXTŽA¢ ŽA€__stub_helper__TEXT0N,0N€__const__TEXT`PH`P__cstring__TEXT¨Pª¨P__eh_frame__TEXTXR¨XRx__DATAà à __got__DATAà8à__nl_symbol_ptr__DATA8à8à"__la_symbol_ptr__DATAHàØHà$__mod_init_func__DATA ñ ñ __static_data__DATA0ñ0ñ__gcc_except_tab__DATA8ñè8ñ__data__DATA ö öH__LINKEDIT@L?"€0Ðà…à…˜xŠhfôLeÚ Pkkøc¡PT? /usr/lib/dyld?ÛZ†y1I•Ε} ¿Î?$ *(€ð H/usr/local/opt/mpich/lib/libmpicxx.12.dylib H/usr/local/opt/mpich/lib/libmpi.12.dylib H/usr/local/opt/mpich/lib/libpmpi.12.dylib H/usr/local/opt/gcc/lib/gcc/5/libstdc++.6.dylib H/usr/local/opt/gcc/lib/gcc/5/libgomp.1.dylib 8­/usr/lib/libSystem.B.dylib @/usr/local/lib/gcc/5/libgcc_s.1.dylib&àðÀ) ó+ ópUH‰åSHƒìX‰}¬H‰u ÇEì‹Eì‰ÇèõCHU HE¬H‰ÖH‰Çè"CHEÐH‰Æ¿DèóBHE×H‰Çè=;HU×HE°¾H‰ÇèV=HE×H‰Çè(;‹EÐ…Àu?HE°¾H‰Çè,=HE°¾H‰Çè3=ÇHE°¾H‰Çè=ÇèSCfH~ÀH‰EàHE°¾DH‰Çèý6è4CfH~ÀH‰EØòEØò\EàfH~ËEЉÆH‹ ÕH‰Çè¢BH5©EH‰Çè«BfHnÃH‰Çè€BH‰ÂH‹úÔH‰ÆH‰×èeBèB»HE°H‰Çèy<‰Øë4H‰ÃHE×H‰Çè8:H‰ØH‰Çè¯BH‰ÃHE°H‰ÇèL<H‰ØH‰Çè•BHƒÄX[]ÃUH‰åHƒì‰}ü‰uøƒ}üu2}øÿÿu)H=ŽåèùAHRôÿÿH5{åH‹LÔH‰ÇèTBÉÃUH‰å¾ÿÿ¿è¤ÿÿÿ]ÃUH‰åATSHƒì H‰}ØH‹EØH‹‹èûAA‰ÄèùA‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|:¯ñ‰òÐ9Ð}6‰Eì‹EìH˜H …H‹EØH‹@HÈǃEì9Uì|Ùë ¸ƒÁë¼HƒÄ [A\]ÃUH‰åAWAVAUATSHƒì8H‰}¨H‹E¨D‹`<ènA‰ÃèmA‰ÇAt$ÿ‰ð™÷û‰Á‰ð™÷û‰Ð9ÇŒŽ¯ù‰úÐ9ЉƒÀ‰EÌDjH‹E¨H‹@‹H‹E¨‹@8¯ÂH‹U¨H‹R‹™÷ûHcÈ‹EÌHcÐH‹E¨‹@8H˜H¯ÂH‹U¨‹RA‰Äè>‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¾¯ñ‰òÐ9ع‰EÜÇEØ‹EÜH˜H…H‹E¨H‹@0HЋ‰EÔ‹EÜH˜H…H‹E¨H‹@8HЋ‰EЋEÜH˜HƒÀH…H‹E¨H‹@8HЋ;EÐ3ƒ}؃EÜ9]Ü|é>‹EÔH˜H…H‹E¨H‹@(H‹E؉ëÓ‹EÔHcÐH‹E¨H‹@H‰ÖH‰Çèî6I‰Å‹EÐHcÐH‹E¨H‹@L$‹EÐHcÐH‹E¨H‹H‰ÖH‰Çè97‹HEÀL‰â‰ÎH‰Çèü0HEÀL‰îH‰Çè½3A‰ÄHEÀH‰Çèê0E„ä…‰ë ƒEÐéÿÿÿƒ}ØRÇEØ‹EÐHcÐH‹E¨H‹@L$‹EÐHcÐH‹E¨H‹H‰ÖH‰Çè¹6‹HE°L‰â‰ÎH‰Çè|0‹EÔƒÀ‰ÆH‹E¨H‹@‹ƒè‰ÇH‹E¨H‹@ HU°H‰Ñ‰òH‰ÆèÜ0‰EÔHE°H‰ÇèI0H‹E¨H‹@‹ƒè;EÔ„§H‹E¨H‹@‹ƒè;EÔžÀ¶ÀH…Àum‹EÔHcÐH‹E¨H‹@H‰ÖH‰Çè¥5D‹`‹EÐHcÐH‹E¨H‹H‰ÖH‰Çè6‹A9ÄœÀ¶ÀH…Àu ƒEØéëþÿÿH K@ºóH5Ù>H=x>è<H I@ºòH5º>H=Y>èþ;H‹E¨‹@@+EЉEØéæýÿÿ‹EÔH˜H…H‹E¨H‹@(H‹E؉éŒþÿÿƒEØépþÿÿ¸ƒÁé5ýÿÿHƒÄH[A\A]]ÃUH‰åATSHƒì H‰}ØÇEèH‹EØ‹Xèi;A‰Äèg;‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ‘¯ñ‰òÐ9Ø}s‰Eì‹EìH˜H…H‹EØH‹@L$‹EìHcÐH‹EØH‹H‰ÖH‰ÇèÏ4H‰ÂH‹EØH‹@H‰×ÿÐA‰$‹EìH˜H…H‹EØH‹@HЋ‹EèЉEèƒEì9]ì|‹UèH‹EØHƒÀðë ¸ƒÁébÿÿÿHƒÄ [A\]ÃUH‰åATSHƒì H‰}ØH‹EØ‹Xè…:A‰Äèƒ:‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|[¯ñ‰òÐ9Ð}W‰Eì‹EìH˜H …H‹EØH‹@HÁ‹EìH˜H4…H‹EØH‹@Hð‹0H‹EØH‹‹ð‰ƒEì9Uì|¸ë ¸ƒÁë›HƒÄ [A\]ÃUH‰åATSHƒì H‰}ØH‹EØH‹‹èÜ9A‰ÄèÚ9‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|:¯ñ‰òÐ9Ð}6‰Eì‹EìH˜H …H‹EØH‹@HÈǃEì9Uì|Ùë ¸ƒÁë¼HƒÄ [A\]ÃUH‰åATSHƒì H‰}ØÇEìH‹EØH‹‹èM9A‰ÄèK9‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|G¯ñ‰òÐ9Ð})‰Eè‹EèH˜H …H‹EØH‹@HÈ‹EìƒEè9Uè|ÚH‹EØHP‹Eìðë ¸ƒÁë¯HƒÄ [A\]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹@@HcØH…Û„Úè¬8Lcàèª8HcðH‰ØºI÷ôH‰ÁH‰ØºI÷ôH‰ÐH9Æ‚—H¯ñH‰òHÐHH9؃ŽH‰EØH‹EÈH‹@(H‹UØH‰ÖH‰Çè2I‰ÅH‹EÈH‹@ ‹HcÐH‹EÈH‹H‰ÖH‰Çèò1I‰ÄH‹EÈH‹¾H‰ÇèÛ1L‰êL‰æH‰Çè‡2I‰ÄH‹EÈH‹¾H‰Çè¶1I)ÄL‰àHÁø‰ÂH‹EȉPDH‹EÈH‹@(H‹UØH‰ÖH‰ÇèŒ1I‰ÅH‹EÈH‹@ ‹HcÐH‹EÈH‹H‰ÖH‰Çèj1I‰ÄH‹EÈH‹¾H‰ÇèS1L‰êL‰æH‰Çè2I‰ÄH‹EÈH‹¾H‰Çè.1I)ÄL‰àHÁø‰ÂH‹EȉPHH‹EÈ‹PDH‹EÈ‹@H)Ѓø;ëHƒEØH9]Ø‚ÊþÿÿéOH‹EÈH‹@8H‹UØH‰ÖH‰ÇèÙ0H‰ÂH‹EÈ‹@D‰ëÇH‹EÈH‹@0H‹UØH‰ÖH‰Çè´0‹H‹EÈH‹@‹¯Â‰ÂÁêÐÑø‰ÁH‹EÈH‹@‹8‰È™÷ÿ‰EÔH‹EÈH‹@‹9EÔŒžH‹EÈH‹@‹9EÔgH‹EÈH‹@8H‹UØH‰ÖH‰ÇèO0I‰ÄH‹EÈH‹@0H‹UØH‰ÖH‰Çè50‹H‹EÈH‹@‹H‹EÈH‹@‹¯ÂH‹UÈH‹R‹:™÷ÿ)Á‰ÈA‰$éûþÿÿH‹EÈH‹@8H‹UØH‰ÖH‰Çèè/H‰ÂH‹EÈ‹@H‰éÓþÿÿH‹EÈH‹@8H‹UØH‰ÖH‰ÇèÀ/H‰ÂH‹EÈ‹@D‰é«þÿÿ¸HƒÁé[ýÿÿHƒÄ([A\A]]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹X0…Û„ëè©5A‰Äè§5‰Æ‰ØºA÷ô‰Á‰ØºA÷ô‰Ð9Æ‚¯¯ñ‰òÐ9؃ª‰EìH‹EÈH‹@ ¾H‰Çè/H‰Eà‹EìPH‹EÈH‹@‹¯ÂH‹UÈ‹R0zº÷÷‰EÔHÇEØH‹EÈ‹@4H˜H;E؇ÈL‹eàH‹EÈH‹@ ¾H‰ÇèÀ.I)ÄL‰àHÁøH‰ÂH‹EÈH‹@H‰ÖH‰Çè .‹H‹EÈH‹@(‰L‹eàH‹EÈH‹@ ¾H‰Çè{.I)ÄL‰àHÁøH‰ÂH‹EÈH‹@H‰ÖH‰Çè[.‹H‹EÈH‹@(‰P‹UìH‹EÈH‹H‰ÖH‰ÇèÙ-H‰ÂH‹EÈH‹@(H‰ÆH‰×è-ƒEì9]ì‚âþÿÿé„H‹EÈH‹@ H‹UØH‰ÖH‰Çèú-‹+EÔH˜H‰ÁHÁù?H1ÈH‰ÂH)ÊH‹Eà‹+EÔH˜H‰ÁHÁù?H1ÈH)ÈH9œÀ„Àu HƒEØéËþÿÿH‹EÈH‹@ H‹UØH‰ÖH‰Çè -H‰EàëÙ¸ƒÁéDþÿÿHƒÄ0[A\]ÃUH‰åATSHƒì H‰}ØH‹EØ‹XèŸ3A‰Äè3‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|W¯ñ‰òÐÁ9È}T‰Eì‹EìH˜HÅH‹EØH‹@H4‹EìHcÐH‹EØH‹H¯ÂH‹UØ‹RHcúH™H÷ÿH‰ƒEì9Mì|»ë ¸ƒÁëŸHƒÄ [A\]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹Xèú2A‰Äèø2‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ‘¯ñ‰òÐ9ØŒ‰EÜ‹EÜH˜HƒÀHÅH‹EÈH‹@HÐH‹HÁàH‰ÂH‹EÈH‹H‹EÜH˜H ÅH‹EÈH‹@HÈH‹HÁàH‰ÁH‹EÈH‹HÈHƒìAUH‰ÖH‰Çèþ/HƒÄƒEÜ9]Ü|†ë ¸ƒÁébÿÿÿHeè[A\A]]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹@H˜HÅH‹EÈH‹HÐH‹‰ÃH‹EÈ‹@H˜HÅH‹EÈH‹HÐH‹A‰ÅèØ1A‰ÄèÖ1‰ÇD‰î)Þ‰ð™A÷ü‰Á‰ð™A÷ü‰Ð9Ç|Y¯ù‰úÐ9Ð}U؉EÜÓ‹EÜH˜HÁàH‰ÂH‹EÈH‹@H‹EÜH˜HÁàH‰ÁH‹EÈH‹@HÈH‰ÖH‰Çè%ƒEÜ9]Ü|¾ë ¸ƒÁëHƒÄ([A\A]]ÃUH‰åATSHƒì H‰}ØH‹EØH‹@‰Ãè)1A‰Äè'1‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|T¯ñ‰òÐ9Ø}P‰Eì‹EìH˜HÁàH‰ÂH‹EØH‹@H‹EìH˜HÁàH‰ÁH‹EØH‹HÈH‰ÖH‰Çèt$ƒEì9]ì|¿ë ¸ƒÁë¢HƒÄ [A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹Xè‰0A‰Äè‡0‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¶¯ñ‰òÐ9ر‰Eì‹EìHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰Eà‹EìƒÀHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰EØH‹EØH+EàHÁàH‰ÂH‹EàHÁàH‰ÁH‹EÈH‹@HÁH‹EàHÁàH‰ÆH‹EÈH‹HðH‰ÎH‰Çèê/ƒEì9]ìŒaÿÿÿë ¸ƒÁé=ÿÿÿHƒÄ0[A\]ÃUH‰åAVAUATSHƒì0H‰}¸H‹E¸‹X@è|/A‰Äèz/‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ#¯ñ‰òÐ9؉EÜÇEØH‹E¸‹@D9EØ|ƒEÜ9]Ü|äéúH‹E¸‹@D¯E܉‹EØЉEÔ‹EÔHcÐH‹E¸H‹@ H¯ÂH‹U¸‹JDH‹U¸‹R@¯ÑHcúH™H÷ÿH‰EÈH‹EÈHÁàH‰ÂH‹E¸H‹H‹EÔH˜HÁàH‰ÁH‹E¸H‹@0HÈH‰ÖH‰Çè["‹EÔH˜HÅH‹E¸H‹@8L$‹EÔH˜HÁàH‰ÂH‹E¸H‹@0HÂH‹E¸H‹HH‹E¸H‹@HƒìAUH‰ÎH‰ÇèÊ(HƒÄH‰ÂH‹E¸H‹@H)ÂH‰ÐHÁøH‰ÂH‹EÈHÐI‰$‹EÔHcÐH‹E¸H‹@(H¯ÂH‹U¸‹JDH‹U¸‹R@¯ÑHcúH™H÷ÿH‰EÈH‹E¸‹PDH‹E¸‹@@¯ÂEÔH‹EÈHÁàH‰ÂH‹E¸H‹@H‹EÔH˜HÁàH‰ÁH‹E¸H‹@0HÈH‰ÖH‰Çèh!‹EÔH˜HÅH‹E¸H‹@8L$‹EÔH˜HÁàH‰ÂH‹E¸H‹@0HÂH‹E¸H‹HH‹E¸H‹HƒìAVH‰ÎH‰ÇèØ'HƒÄH‰ÂH‹E¸H‹H)ÂH‰ÐHÁøH‰ÂH‹EÈHÐI‰$ƒEØéùýÿÿ¸ƒÁéÐýÿÿHeà[A\A]A^]ÃUH‰åAWAVAUATSHƒìHH‰}˜H‹E˜D‹`Pèû,‰Ãèú,‰ÇAt$ÿ‰ð™÷û‰Á‰ð™÷û‰Ð9ÇŒ¯ù‰úÐ9Ð|ƒÀ‰EÌZ‹EÌHcÐH‹E˜H‹H(H‹E˜H‹@ HÈH¯ÂH‹U˜‹RPHcòH™H÷þH‰E°H‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8H H‹E˜H‹@8HU°HƒìAUH‰ÎH‰ÇèØ&HƒÄH‰ÂH‹E˜H‹@8H)ÂH‰ÐHÁø‰EÈH‹E˜‹PTH‹E˜‹@P¯Â;EÈŽ ‹EÈH˜HÁàH‰ÂH‹E˜H‹@0HÂHE H‰ÖH‰Ç膋EÈH˜HÅH‹E˜H‹@8HÐH‹H‰EÀH‹E˜‹PTH‹E˜‹@P¯ÂH˜HÁàH‰ÂH‹E˜H‹@8H4H‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8H HE°HƒìAVH‰ÂH‰Ïèû%HƒÄH‰ÁH‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8HÐH)ÁH‰ÈHÁø‰ÁH‹E˜‹PTH‹E˜‹@P¯ÂȉEÈH‹E˜‹@PH‹E˜‹@T¯Â;EÈŽn‹EÈH˜HÅH‹E˜H‹@8HÐH‹‰ÂH‹E°)‰Љ‰ÐÁø1Â)ÂH‹EÀ‰ÁH‹E°)Á‰È‰ÁÁù1È)È9ŒՋEÌH˜HÅH‹E˜H‹@@L$H‹E˜H‹HH‹E˜H‹HU HƒìAWH‰ÎH‰Çèå$HƒÄH‰ÂH‹E˜H‹H)ÂH‰ÐHÁøI‰$‹EÌH˜HÅH‹E˜H‹@HL$H‹E˜H‹HH‹E˜H‹@HU Hƒì¶u—VH‰ÎH‰Çèˆ$HƒÄH‰ÂH‹E˜H‹@H)ÂH‰ÐHÁøI‰$HE H‰Ç虃EÌ9]ÌŒ$ýÿÿé’‹EÈH˜HÁàH‰ÂH‹E˜H‹@0HÂHE H‰ÖH‰Çèg‹EÈH˜HÅH‹E˜H‹@8HÐH‹H‰EÀéáþÿÿH‹E˜‹@PH‹E˜‹@T¯Âƒè‰EÈésþÿÿH‹E˜‹PTH‹E˜‹@P¯Âƒè‰EÈéDýÿÿ¸ƒÁérüÿÿHeØ[A\A]A^A_]ÃUH‰åATSHƒì H‰}ØH‹EØ‹X(è)A‰Äè)‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ@¯ñ‰òÐ9Ø;‰Eì‹EìH˜HÅH‹EØH‹@HÐH‹H‰Á‹EìH˜HÅH‹EØH‹@ HÐH‹HÈHÁàH‰ÂH‹EØH‹@HÐH‰Eà‹EìH˜HƒÀHÅH‹EØH‹@ HÐH‹HÁàH‰ÂH‹EØH‹@H ‹EìH˜HÅH‹EØH‹@ HÐH‹HÁàH‰ÂH‹EØH‹@H‹EìH˜HƒÀH4ÅH‹EØH‹@HðH‹HÁàH‰ÆH‹EØH‹HÆ‹EìH˜H<ÅH‹EØH‹@HøH‹HÁàH‰ÇH‹EØH‹HøH‹}àI‰øH‰Çè°%ƒEì9]ìŒ×þÿÿë ¸ƒÁé³þÿÿHƒÄ [A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹X èŒ'A‰ÄèŠ'‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒÿ¯ñ‰òÐ9Øú‰Eì‹EìHcÐH‹EÈH‹@H¯ÂH‹UÈ‹R HcúH™H÷ÿH‰EØ‹EìƒÀHcÐH‹EÈH‹@H¯ÂH‹UÈ‹R HcúH™H÷ÿH‰EÐH‹EÐH+EØHÁàH‰ÂH‹EØHÁàH‰ÁH‹EÈH‹HÁH‹EØHÁàH‰ÆH‹EÈH‹@HðH‰ÎH‰Çèí&H‹EØH‰EàH‹EàH;EÐ|ƒEì9]ìŒOÿÿÿëDH‹EàHÅH‹EÈH‹@HÂH‹EàHÁàH‰ÁH‹EÈH‹@HÈH‰HƒEàë°¸ƒÁéôþÿÿHƒÄ0[A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹Xè:&A‰Äè8&‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒÀ¯ñ‰òÐ9Ø»‰Eì‹EìHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰EØ‹EìƒÀHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰EÐH‹EØH‰EàH‹EÐH9Eàr ƒEì9]ì|˜ëNH‹EàHÅH‹EÈH‹@HÐH‹H‹UàH‰ÑHÁáH‹UÈH‹HÊH‰ÆH‰×èHƒEà몸ƒÁé3ÿÿÿHƒÄ0[A\]ÃUH‰åATSHƒì H‰}ØH‹EØ‹Xè'%A‰Äè%%‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|W¯ñ‰òÐÁ9È}T‰Eì‹EìH˜HÅH‹EØH‹@H4‹EìHcÐH‹EØH‹H¯ÂH‹UØ‹RHcúH™H÷ÿH‰ƒEì9Mì|»ë ¸ƒÁëŸHƒÄ [A\]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹Xè‚$A‰Äè€$‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ“¯ñ‰òÐ9ØމEÜ‹EÜH˜HƒÀHÅH‹EÈH‹@HÐH‹HÅH‹EÈH‹H‹EÜH˜H ÅH‹EÈH‹@HÈH‹H ÅH‹EÈH‹HÈHƒìAUH‰ÖH‰ÇèŠ!HƒÄƒEÜ9]Ü|„ë ¸ƒÁé`ÿÿÿHeè[A\A]]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹@H˜HÅH‹EÈH‹HÐH‹‰ÃH‹EÈ‹@H˜HÅH‹EÈH‹HÐH‹A‰Åè^#A‰Äè\#‰ÇD‰î)Þ‰ð™A÷ü‰Á‰ð™A÷ü‰Ð9Ç|V¯ù‰úÐ9Ð}R؉EÜÚ‹EÜH˜H ÅH‹EÈH‹@HÁ‹EÜH˜H4ÅH‹EÈH‹@HðH‹H‰ƒEÜ9UÜ|Áë ¸ƒÁë HƒÄ([A\A]]ÃUH‰åATSHƒì H‰}ØH‹EØH‹@‰Ãè²"A‰Äè°"‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|Q¯ñ‰òÐ9Ð}M‰Eì‹EìH˜H ÅH‹EØH‹HÁ‹EìH˜H4ÅH‹EØH‹@HðH‹H‰ƒEì9Uì|Âë ¸ƒÁë¥HƒÄ [A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹Xè"A‰Äè"‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¹¯ñ‰òÐ9Ø´‰Eì‹EìHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰Eà‹EìƒÀHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰EØH‹EØH+EàHÅH‹EàH ÅH‹EÈH‹@HÁH‹EàH4ÅH‹EÈH‹HðH‰ÎH‰Çès!ƒEì9]ìŒ^ÿÿÿë ¸ƒÁé:ÿÿÿHƒÄ0[A\]ÃUH‰åAVAUATSHƒì0H‰}¸H‹E¸‹X@è!A‰Äè!‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¯ñ‰òÐ9؉EÜÇEØH‹E¸‹@D9EØ|ƒEÜ9]Ü|äéöH‹E¸‹@D¯E܉‹EØЉEÔ‹EÔHcÐH‹E¸H‹@ H¯ÂH‹U¸‹JDH‹U¸‹R@¯ÑHcúH™H÷ÿH‰EÈ‹EÔH˜HÅH‹E¸H‹@0HÂH‹EÈH ÅH‹E¸H‹HÈH‹H‰‹EÔH˜HÅH‹E¸H‹@8L$‹EÔH˜HÅH‹E¸H‹@0HÂH‹E¸H‹HH‹E¸H‹@HƒìAUH‰ÎH‰Çè[HƒÄH‰ÂH‹E¸H‹@H)ÂH‰ÐHÁøH‰ÂH‹EÈHÐI‰$‹EÔHcÐH‹E¸H‹@(H¯ÂH‹U¸‹JDH‹U¸‹R@¯ÑHcúH™H÷ÿH‰EÈH‹E¸‹PDH‹E¸‹@@¯ÂEÔ‹EÔH˜HÅH‹E¸H‹@0HÂH‹EÈH ÅH‹E¸H‹@HÈH‹H‰‹EÔH˜HÅH‹E¸H‹@8L$‹EÔH˜HÅH‹E¸H‹@0HÂH‹E¸H‹HH‹E¸H‹HƒìAVH‰ÎH‰ÇèkHƒÄH‰ÂH‹E¸H‹H)ÂH‰ÐHÁøH‰ÂH‹EÈHÐI‰$ƒEØéýýÿÿ¸ƒÁéÔýÿÿHeà[A\A]A^]ÃUH‰åAWAVAUATSHƒìHH‰}˜H‹E˜D‹`P舉Ã臉ÇAt$ÿ‰ð™÷û‰Á‰ð™÷û‰Ð9ÇŒg¯ù‰úÐ9ÐbƒÀ‰EÌZ‹EÌHcÐH‹E˜H‹H(H‹E˜H‹@ HÈH¯ÂH‹U˜‹RPHcòH™H÷þH‰E°H‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8H H‹E˜H‹@8HU°HƒìAUH‰ÎH‰ÇèeHƒÄH‰ÂH‹E˜H‹@8H)ÂH‰ÐHÁø‰EÈH‹E˜‹PTH‹E˜‹@P¯Â;EÈކ‹EÈH˜HÅH‹E˜H‹@0HÐH‹H‰E ‹EÈH˜HÅH‹E˜H‹@8HÐH‹H‰EÀH‹E˜‹PTH‹E˜‹@P¯ÂH˜HÁàH‰ÂH‹E˜H‹@8H4H‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8H HE°HƒìAVH‰ÂH‰ÏèHƒÄH‰ÁH‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8HÐH)ÁH‰ÈHÁø‰ÁH‹E˜‹PTH‹E˜‹@P¯ÂȉEÈH‹E˜‹@PH‹E˜‹@T¯Â;EÈŽ[‹EÈH˜HÅH‹E˜H‹@8HÐH‹‰ÂH‹E°)‰Љ‰ÐÁø1Â)ÂH‹EÀ‰ÁH‹E°)Á‰È‰ÁÁù1È)È9ŒɋEÌH˜HÅH‹E˜H‹@@L$H‹E˜H‹HH‹E˜H‹HU HƒìAWH‰ÎH‰ÇèHƒÄH‰ÂH‹E˜H‹H)ÂH‰ÐHÁøI‰$‹EÌH˜HÅH‹E˜H‹@HL$H‹E˜H‹HH‹E˜H‹@HU Hƒì¶u—VH‰ÎH‰Çè"HƒÄH‰ÂH‹E˜H‹@H)ÂH‰ÐHÁøI‰$ƒEÌ9]ÌŒ7ýÿÿé‹‹EÈH˜HÅH‹E˜H‹@0HÐH‹H‰E ‹EÈH˜HÅH‹E˜H‹@8HÐH‹H‰EÀéôþÿÿH‹E˜‹@PH‹E˜‹@T¯Âƒè‰EÈé†þÿÿH‹E˜‹PTH‹E˜‹@P¯Âƒè‰EÈé^ýÿÿ¸ƒÁéŒüÿÿHeØ[A\A]A^A_]ÃUH‰åATSHƒì H‰}ØH‹EØ‹X(èÆA‰ÄèĉƉؙA÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒE¯ñ‰òÐ9Ø@‰Eì‹EìH˜HÅH‹EØH‹@HÐH‹H‰Á‹EìH˜HÅH‹EØH‹@ HÐH‹HÈHÅH‹EØH‹@HÐH‰Eà‹EìH˜HƒÀHÅH‹EØH‹@ HÐH‹HÅH‹EØH‹@H ‹EìH˜HÅH‹EØH‹@ HÐH‹HÅH‹EØH‹@H‹EìH˜HƒÀH4ÅH‹EØH‹@HðH‹H4ÅH‹EØH‹HÆ‹EìH˜H<ÅH‹EØH‹@HøH‹H<ÅH‹EØH‹HøH‹}àI‰øH‰ÇèXƒEì9]ìŒÒþÿÿë ¸ƒÁé®þÿÿHƒÄ [A\]ÃUH‰åATSHƒì H‰}ØH‹EØ‹Xè.A‰Äè,‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒó¯ñ‰òÐ9Ðî‰EìH‹EØ‹@‹Mì¯Á‰EàH‹EØ‹H‹EàȉEèH‹EØ‹@ƒè;E섃}ìut‹EàƒÀ‰Eä‹Eä;Eè|ƒEì9Uì|®é”‹EäH˜H …H‹EØH‹@HÁ‹EäH˜HÁàHpüH‹EØH‹@Hð‹0‹EäH˜HÁàHxüH‹EØH‹Hø‹ð‰ƒEäë•‹EàH˜H …H‹EØH‹@HÈÇéiÿÿÿH‹EØ‹@‰EèéTÿÿÿ¸ƒÁéÿÿÿHƒÄ [A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈD‹`èç‰Ãèæ‰ÇAt$ÿ‰ð™÷û‰Á‰ð™÷û‰Ð9njȯù‰úÐ9ÐÃÀ‰EìƒÂH‹EÈ‹@‹Mì¯Á‰EàH‹EÈ‹H‹EàȉEèH‹EÈ‹@ƒè;Eìts‹EìH˜H …H‹EÈH‹@HÈ‹‰EÜ‹Eà‰Eä‹Eä;Eè| ƒEì9Uì|žëV‹EäH˜H …H‹EÈH‹HÁ‹EäH˜H4…H‹EÈH‹Hð‹0‹EÜð‰ƒEäë°H‹EÈ‹@‰Eè븃Áé+ÿÿÿHƒÄ0[A\]ÃUH‰åATSHƒì H‰}ØH‹EØ‹XèÊA‰ÄèȉƉؙA÷ü‰Á‰Ø™A÷ü‰Ð9Æ|W¯ñ‰òÐÁ9È}T‰Eì‹EìH˜HÅH‹EØH‹@H4‹EìHcÐH‹EØH‹H¯ÂH‹UØ‹RHcúH™H÷ÿH‰ƒEì9Mì|»ë ¸ƒÁëŸHƒÄ [A\]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹Xè%A‰Äè#‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ“¯ñ‰òÐ9ØމEÜ‹EÜH˜HƒÀHÅH‹EÈH‹@HÐH‹H…H‹EÈH‹H‹EÜH˜H ÅH‹EÈH‹@HÈH‹H …H‹EÈH‹HÈHƒìAUH‰ÖH‰Çè9HƒÄƒEÜ9]Ü|„ë ¸ƒÁé`ÿÿÿHeè[A\A]]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹@H˜HÅH‹EÈH‹HÐH‹‰ÃH‹EÈ‹@H˜HÅH‹EÈH‹HÐH‹A‰ÅèA‰Äèÿ‰ÇD‰î)Þ‰ð™A÷ü‰Á‰ð™A÷ü‰Ð9Ç|T¯ù‰úÐ9Ð}P؉EÜÚ‹EÜH˜H …H‹EÈH‹@HÁ‹EÜH˜H4…H‹EÈH‹@Hð‹‰ƒEÜ9UÜ|Ãë ¸ƒÁë¢HƒÄ([A\A]]ÃUH‰åATSHƒì H‰}ØH‹EØH‹@‰ÃèWA‰ÄèU‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|O¯ñ‰òÐ9Ð}K‰Eì‹EìH˜H …H‹EØH‹HÁ‹EìH˜H4…H‹EØH‹@Hð‹‰ƒEì9Uì|Äë ¸ƒÁë§HƒÄ [A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹Xè¼A‰Ä躉ƉؙA÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¹¯ñ‰òÐ9Ø´‰Eì‹EìHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰Eà‹EìƒÀHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰EØH‹EØH+EàH…H‹EàH …H‹EÈH‹@HÁH‹EàH4…H‹EÈH‹HðH‰ÎH‰ÇèƒEì9]ìŒ^ÿÿÿë ¸ƒÁé:ÿÿÿHƒÄ0[A\]ÃUH‰åAVAUATSHƒì0H‰}¸H‹E¸‹X@è¬A‰Ä誉ƉؙA÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¯ñ‰òÐ9؉EÜÇEØH‹E¸‹@D9EØ|ƒEÜ9]Ü|äéòH‹E¸‹@D¯E܉‹EØЉEÔ‹EÔHcÐH‹E¸H‹@ H¯ÂH‹U¸‹JDH‹U¸‹R@¯ÑHcúH™H÷ÿH‰EÈ‹EÔH˜H…H‹E¸H‹@0HÂH‹EÈH …H‹E¸H‹HÈ‹‰‹EÔH˜HÅH‹E¸H‹@8L$‹EÔH˜H…H‹E¸H‹@0HÂH‹E¸H‹HH‹E¸H‹@HƒìAUH‰ÎH‰Çè HƒÄH‰ÂH‹E¸H‹@H)ÂH‰ÐHÁøH‰ÂH‹EÈHÐI‰$‹EÔHcÐH‹E¸H‹@(H¯ÂH‹U¸‹JDH‹U¸‹R@¯ÑHcúH™H÷ÿH‰EÈH‹E¸‹PDH‹E¸‹@@¯ÂEÔ‹EÔH˜H…H‹E¸H‹@0HÂH‹EÈH …H‹E¸H‹@HÈ‹‰‹EÔH˜HÅH‹E¸H‹@8L$‹EÔH˜H…H‹E¸H‹@0HÂH‹E¸H‹HH‹E¸H‹HƒìAVH‰ÎH‰Çè( HƒÄH‰ÂH‹E¸H‹H)ÂH‰ÐHÁøH‰ÂH‹EÈHÐI‰$ƒEØéþÿÿ¸ƒÁéØýÿÿHeà[A\A]A^]ÃUH‰åAWAVAUATSHƒìHH‰}˜H‹E˜D‹`Pè3‰Ãè2‰ÇAt$ÿ‰ð™÷û‰Á‰ð™÷û‰Ð9ÇŒc¯ù‰úÐ9Ð^ƒÀ‰EÌZ‹EÌHcÐH‹E˜H‹H(H‹E˜H‹@ HÈH¯ÂH‹U˜‹RPHcòH™H÷þH‰E°H‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8H H‹E˜H‹@8HU°HƒìAUH‰ÎH‰Çè HƒÄH‰ÂH‹E˜H‹@8H)ÂH‰ÐHÁø‰EÈH‹E˜‹PTH‹E˜‹@P¯Â;EÈŽ‚‹EÈH˜H…H‹E˜H‹@0HЋ‰E¬‹EÈH˜HÅH‹E˜H‹@8HÐH‹H‰EÀH‹E˜‹PTH‹E˜‹@P¯ÂH˜HÁàH‰ÂH‹E˜H‹@8H4H‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8H HE°HƒìAVH‰ÂH‰Ïè< HƒÄH‰ÁH‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8HÐH)ÁH‰ÈHÁø‰ÁH‹E˜‹PTH‹E˜‹@P¯ÂȉEÈH‹E˜‹@PH‹E˜‹@T¯Â;EÈŽY‹EÈH˜HÅH‹E˜H‹@8HÐH‹‰ÂH‹E°)‰Љ‰ÐÁø1Â)ÂH‹EÀ‰ÁH‹E°)Á‰È‰ÁÁù1È)È9ŒɋEÌH˜HÅH‹E˜H‹@@L$H‹E˜H‹HH‹E˜H‹HU¬HƒìAWH‰ÎH‰Çè>HƒÄH‰ÂH‹E˜H‹H)ÂH‰ÐHÁøI‰$‹EÌH˜HÅH‹E˜H‹@HL$H‹E˜H‹HH‹E˜H‹@HU¬Hƒì¶u—VH‰ÎH‰ÇèáHƒÄH‰ÂH‹E˜H‹@H)ÂH‰ÐHÁøI‰$ƒEÌ9]ÌŒ9ýÿÿ鉋EÈH˜H…H‹E˜H‹@0HЋ‰E¬‹EÈH˜HÅH‹E˜H‹@8HÐH‹H‰EÀéöþÿÿH‹E˜‹@PH‹E˜‹@T¯Âƒè‰EÈéˆþÿÿH‹E˜‹PTH‹E˜‹@P¯Âƒè‰EÈébýÿÿ¸ƒÁéüÿÿHeØ[A\A]A^A_]ÃUH‰åATSHƒì H‰}ØH‹EØ‹X(èu A‰Äès ‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒE¯ñ‰òÐ9Ø@‰Eì‹EìH˜HÅH‹EØH‹@HÐH‹H‰Á‹EìH˜HÅH‹EØH‹@ HÐH‹HÈH…H‹EØH‹@HÐH‰Eà‹EìH˜HƒÀHÅH‹EØH‹@ HÐH‹H…H‹EØH‹@H ‹EìH˜HÅH‹EØH‹@ HÐH‹H…H‹EØH‹@H‹EìH˜HƒÀH4ÅH‹EØH‹@HðH‹H4…H‹EØH‹HÆ‹EìH˜H<ÅH‹EØH‹@HøH‹H<…H‹EØH‹HøH‹}àI‰øH‰Çè ƒEì9]ìŒÒþÿÿë ¸ƒÁé®þÿÿHƒÄ [A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹X èÝ A‰ÄèÛ ‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¯ñ‰òÐ9Øþ‰Eì‹EìHcÐH‹EÈH‹@H¯ÂH‹UÈ‹R HcúH™H÷ÿH‰EØ‹EìƒÀHcÐH‹EÈH‹@H¯ÂH‹UÈ‹R HcúH™H÷ÿH‰EÐH‹EÐH+EØH…H‹EØH …H‹EÈH‹HÁH‹EØH4…H‹EÈH‹@HðH‰ÎH‰Çè; H‹EØH‰EàH‹EàH;EÐ|ƒEì9]ìŒLÿÿÿëEH‹EàHÅH‹EÈH‹@HÂH‹EàH …H‹EÈH‹@HÈH‰HƒEà미ƒÁéðþÿÿHƒÄ0[A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹Xè‡ A‰Äè… ‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¹¯ñ‰òÐÁ9ȵ‰Eì‹EìHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰EØ‹EìƒÀHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰EÐH‹EØH‰EàH‹EÐH9Eàr ƒEì9Mì|˜ëHH‹EàH…H‹EÈH‹HÂH‹EàH4ÅH‹EÈH‹@HðH‹‹‰HƒEàë°¸ƒÁé:ÿÿÿHƒÄ0[A\]ÃUH‰åATSHƒì H‰}ØH‹EØ‹Xè{A‰Äèy‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|W¯ñ‰òÐÁ9È}T‰Eì‹EìH˜HÅH‹EØH‹@H4‹EìHcÐH‹EØH‹H¯ÂH‹UØ‹RHcúH™H÷ÿH‰ƒEì9Mì|»ë ¸ƒÁëŸHƒÄ [A\]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹XèÖA‰ÄèԉƉؙA÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ“¯ñ‰òÐ9ØމEÜ‹EÜH˜HƒÀHÅH‹EÈH‹@HÐH‹HÅH‹EÈH‹H‹EÜH˜H ÅH‹EÈH‹@HÈH‹H ÅH‹EÈH‹HÈHƒìAUH‰ÖH‰ÇèäHƒÄƒEÜ9]Ü|„ë ¸ƒÁé`ÿÿÿHeè[A\A]]ÃUH‰åAUATSHƒì(H‰}ÈH‹EÈ‹@H˜HÅH‹EÈH‹HÐH‹‰ÃH‹EÈ‹@H˜HÅH‹EÈH‹HÐH‹A‰Åè²A‰Äè°‰ÇD‰î)Þ‰ð™A÷ü‰Á‰ð™A÷ü‰Ð9Ç|V¯ù‰úÐ9Ð}R؉EÜÚ‹EÜH˜H ÅH‹EÈH‹@HÁ‹EÜH˜H4ÅH‹EÈH‹@HðH‹H‰ƒEÜ9UÜ|Áë ¸ƒÁë HƒÄ([A\A]]ÃUH‰åATSHƒì H‰}ØH‹EØH‹@‰ÃèA‰Äè‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9Æ|Q¯ñ‰òÐ9Ð}M‰Eì‹EìH˜H ÅH‹EØH‹HÁ‹EìH˜H4ÅH‹EØH‹@HðH‹H‰ƒEì9Uì|Âë ¸ƒÁë¥HƒÄ [A\]ÃUH‰åATSHƒì0H‰}ÈH‹EÈ‹XèiA‰Äèg‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¹¯ñ‰òÐ9Ø´‰Eì‹EìHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰Eà‹EìƒÀHcÐH‹EÈH‹@H¯ÂH‹UÈ‹RHcúH™H÷ÿH‰EØH‹EØH+EàHÅH‹EàH ÅH‹EÈH‹@HÁH‹EàH4ÅH‹EÈH‹HðH‰ÎH‰ÇèǃEì9]ìŒ^ÿÿÿë ¸ƒÁé:ÿÿÿHƒÄ0[A\]ÃUH‰åAVAUATSHƒì0H‰}¸H‹E¸‹X@èYA‰ÄèW‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒ¯ñ‰òÐ9؉EÜÇEØH‹E¸‹@D9EØ|ƒEÜ9]Ü|äéöH‹E¸‹@D¯E܉‹EØЉEÔ‹EÔHcÐH‹E¸H‹@ H¯ÂH‹U¸‹JDH‹U¸‹R@¯ÑHcúH™H÷ÿH‰EÈ‹EÔH˜HÅH‹E¸H‹@0HÂH‹EÈH ÅH‹E¸H‹HÈH‹H‰‹EÔH˜HÅH‹E¸H‹@8L$‹EÔH˜HÅH‹E¸H‹@0HÂH‹E¸H‹HH‹E¸H‹@HƒìAUH‰ÎH‰ÇèµýHƒÄH‰ÂH‹E¸H‹@H)ÂH‰ÐHÁøH‰ÂH‹EÈHÐI‰$‹EÔHcÐH‹E¸H‹@(H¯ÂH‹U¸‹JDH‹U¸‹R@¯ÑHcúH™H÷ÿH‰EÈH‹E¸‹PDH‹E¸‹@@¯ÂEÔ‹EÔH˜HÅH‹E¸H‹@0HÂH‹EÈH ÅH‹E¸H‹@HÈH‹H‰‹EÔH˜HÅH‹E¸H‹@8L$‹EÔH˜HÅH‹E¸H‹@0HÂH‹E¸H‹HH‹E¸H‹HƒìAVH‰ÎH‰ÇèÅüHƒÄH‰ÂH‹E¸H‹H)ÂH‰ÐHÁøH‰ÂH‹EÈHÐI‰$ƒEØéýýÿÿ¸ƒÁéÔýÿÿHeà[A\A]A^]ÃUH‰åAWAVAUATSHƒìHH‰}˜H‹E˜D‹`Pè܉ÃèÛ‰ÇAt$ÿ‰ð™÷û‰Á‰ð™÷û‰Ð9ÇŒg¯ù‰úÐ9ÐbƒÀ‰EÌZ‹EÌHcÐH‹E˜H‹H(H‹E˜H‹@ HÈH¯ÂH‹U˜‹RPHcòH™H÷þH‰E°H‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8H H‹E˜H‹@8HU°HƒìAUH‰ÎH‰Çè¹ûHƒÄH‰ÂH‹E˜H‹@8H)ÂH‰ÐHÁø‰EÈH‹E˜‹PTH‹E˜‹@P¯Â;EÈކ‹EÈH˜HÅH‹E˜H‹@0HÐH‹H‰E ‹EÈH˜HÅH‹E˜H‹@8HÐH‹H‰EÀH‹E˜‹PTH‹E˜‹@P¯ÂH˜HÁàH‰ÂH‹E˜H‹@8H4H‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8H HE°HƒìAVH‰ÂH‰ÏèãúHƒÄH‰ÁH‹E˜‹PTH‹E˜‹@P¯ÂH˜HÅH‹E˜H‹@8HÐH)ÁH‰ÈHÁø‰ÁH‹E˜‹PTH‹E˜‹@P¯ÂȉEÈH‹E˜‹@PH‹E˜‹@T¯Â;EÈŽ[‹EÈH˜HÅH‹E˜H‹@8HÐH‹‰ÂH‹E°)‰Љ‰ÐÁø1Â)ÂH‹EÀ‰ÁH‹E°)Á‰È‰ÁÁù1È)È9ŒɋEÌH˜HÅH‹E˜H‹@@L$H‹E˜H‹HH‹E˜H‹HU HƒìAWH‰ÎH‰ÇèÙùHƒÄH‰ÂH‹E˜H‹H)ÂH‰ÐHÁøI‰$‹EÌH˜HÅH‹E˜H‹@HL$H‹E˜H‹HH‹E˜H‹@HU Hƒì¶u—VH‰ÎH‰Çè|ùHƒÄH‰ÂH‹E˜H‹@H)ÂH‰ÐHÁøI‰$ƒEÌ9]ÌŒ7ýÿÿé‹‹EÈH˜HÅH‹E˜H‹@0HÐH‹H‰E ‹EÈH˜HÅH‹E˜H‹@8HÐH‹H‰EÀéôþÿÿH‹E˜‹@PH‹E˜‹@T¯Âƒè‰EÈé†þÿÿH‹E˜‹PTH‹E˜‹@P¯Âƒè‰EÈé^ýÿÿ¸ƒÁéŒüÿÿHeØ[A\A]A^A_]ÃUH‰åATSHƒì H‰}ØH‹EØ‹X(èþA‰Äèþ‰Æ‰Ø™A÷ü‰Á‰Ø™A÷ü‰Ð9ÆŒE¯ñ‰òÐ9Ø@‰Eì‹EìH˜HÅH‹EØH‹@HÐH‹H‰Á‹EìH˜HÅH‹EØH‹@ HÐH‹HÈHÅH‹EØH‹@HÐH‰Eà‹EìH˜HƒÀHÅH‹EØH‹@ HÐH‹HÅH‹EØH‹@H ‹EìH˜HÅH‹EØH‹@ HÐH‹HÅH‹EØH‹@H‹EìH˜HƒÀH4ÅH‹EØH‹@HðH‹H4ÅH‹EØH‹HÆ‹EìH˜H<ÅH‹EØH‹@HøH‹H<ÅH‹EØH‹HøH‹}àI‰øH‰Çè²úƒEì9]ìŒÒþÿÿë ¸ƒÁé®þÿÿHƒÄ [A\]ÃUH‰å]ÃUH‰å]ÃUH‰å]ÃUH‰åH‰}øº@H‹EøH½ÀHƒð?H˜H)ÂH‰ÐHƒè]ÃUH‰åH‰}øH‰uðH‹Eð]ÃUH‰å¸L]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèñÉÃUH‰åHƒìH‰}øH‹EøH‰ÇèððÉÃUH‰åHƒìH‰}øH‹EøH‰ÇèÔðÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EèH‹UØH‹MàH‰ÎH‰ÇèFôH‹UàH‹EèH‰ÖH‰ÇèAõëH‰ÃH‹EèH‰Çè(ôH‰ØH‰Çè¯ûHƒÄ([]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèòóH‰ÂH‹EøH‹HH‹EøH‹H‰ÎH‰ÇèáùH‹EøH‰ÇèÕóÉÃUH‰åHƒìH‰}øH‰uðH‹EøH‰ÇèxòH;Eð’À„Àt*H‹EøH‰ÇèaòH‰ÂH‹EðH)ÐH‰ÂH‹EøH‰ÖH‰Çèyôë8H‹EøH‰Çè7òH;Eð—À„Àt!H‹EøH‹H‹UðHÁâHÂH‹EøH‰ÖH‰Çè3ôÉÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÐ]ÃUH‰åATSHìH‰½èýÿÿ‰µäýÿÿH•lþÿÿ‹…äýÿÿH‰Ö‰Çè¨ùH‹…èýÿÿH‰Çè§ñH…À”À¶ÀH…ÀtH ýºGH5 ýH=«üèPú‹…lþÿÿƒøuFH‹…èýÿÿH‰Çè`ñH‰ÂH‹…èýÿÿH‰ÖH‰ÇèÍóH‰ÃH‹…èýÿÿ¾H‰Çè¶óH‰ÞH‰Çè/îH•hþÿÿ‹…äýÿÿH‰Ö‰ÇèþøH‹…èýÿÿH‰Çèñ‰EÌ‹Ẻ…dþÿÿ‹•äýÿÿHµ`þÿÿH…dþÿÿA‰Ð¹XºH‰Çèzí‹…lþÿÿ‰EÈÇEÄ‹UȉÐÁàЯEȉ‹…`þÿÿ9ÂŽ3‹…hþÿÿ…Àu\‹lþÿÿD‹¥`þÿÿH5&üH‹O‹H‰ÇèéøD‰æH‰ÇèÆøH5AüH‰ÇèÏø‰ÞH‰Çè­øH‰ÂH‹!‹H‰ÆH‰×èŒø‹…`þÿÿ;EÈ…‹…hþÿÿ…Àu\‹`þÿÿD‹¥lþÿÿH5ùûH‹ÚŠH‰ÇètøD‰æH‰ÇèQøH5üH‰ÇèZø‰ÞH‰Çè8øH‰ÂH‹¬ŠH‰ÆH‰×èø‹…`þÿÿ‹•äýÿÿHüýÿÿH‰Î‰Çè<ãë ‹…äýÿÿ‰…üýÿÿH‹…èýÿÿH‰Çè—ïƒð„Àt‹•üýÿÿH‹…èýÿÿ‰ÖH‰Çèûë‹•äýÿÿH‹…èýÿÿ¾H‰ÇèÕëH‹…èýÿÿH‰ÇèDï‰EÌH‹…èýÿÿH‰Çè2ïH‰ÂH‹…èýÿÿH‰ÖH‰ÇèŸñH‰ÃH‹…èýÿÿ¾H‰ÇèˆñH‰ÞH‰ÇèìH…@þÿÿH‰Çèêð‹…lþÿÿƒè‰ÇH… þÿÿ‹•äýÿÿH‹µèýÿÿ‰Ñ‰úH‰Çè—ëÇEì‹EìHcØH… þÿÿH‰Çè“îH9Ã’À„Àt}‹EìHcÐH… þÿÿH‰ÖH‰Çè¥ð‹@HcØ‹EìHcÐH… þÿÿH‰ÖH‰Çè‡ð‹H…ÿÿÿH‰Ú‰ÎH‰Çè§êH•ÿÿÿH…@þÿÿH‰ÖH‰Çè.ðH…ÿÿÿH‰Çè‹êƒEìédÿÿÿÇEÀ‹…lþÿÿXÿè©ê‰Æ‹•äýÿÿH…þÿÿA‰Ð‰Ù‰ò¾H‰ÇèÀõ‹EÀHcЋþÿÿH… ÿÿÿ‰ÎH‰Çè!êH• ÿÿÿH…@þÿÿH‰ÖH‰Çè¨ïH… ÿÿÿH‰ÇèêH…@þÿÿH‰ÇèhíH‰ÂH…@þÿÿH‰ÖH‰Çè‡ïH‰ÃH…@þÿÿ¾H‰ÇèpïH‰ÞH‰ÇèIêHÇEàH…@þÿÿH‰Çè&íƒð„Àt)H…@þÿÿH‰Çè ïH‰…0ÿÿÿH…0ÿÿÿH‰ÇèjìH‰Eà‹…lþÿÿH˜HºÀH9ÐwHÁàH‰ÇèyõëèõH‰E¸Hƒ}¸”À¶ÀH…ÀtH éøº»H5IøH=è÷èõ‹…lþÿÿH˜HºÀH9ÐwHÁàH‰Çèõëè3õH‰E°Hƒ}°”À¶ÀH…ÀtH •øº¾H5ì÷H=‹÷è0õ‹…lþÿÿH˜HºÀH9ÐwHÁàH‰Çè¿ôëèÖôH‰E¨Hƒ}¨”À¶ÀH…ÀtH AøºÁH5÷H=.÷èÓô‹…lþÿÿH˜HºÀH9ÐwHÁàH‰ÇèbôëèyôH‰E Hƒ} ”À¶ÀH…ÀtH ì÷ºÄH52÷H=ÑöèvôH…lþÿÿH‰…pþÿÿH‹E¸H‰…xþÿÿH…pþÿÿ¹ºH‰ÆH= ²ÿÿèôH‹…xþÿÿH‰E¸è ô‰Eœ‹EœƒÀH˜HºÀH9ÐwHÁàH‰Çè¹óëèÐóH‰E‹EœƒÀH˜HºÀH9ÐwHÁàH‰Çè‹óëè¢óH‰EˆH‹EÇH‹EˆÇ‹EœH˜H…H‹EˆH‹ẺH‹…èýÿÿH‰…€þÿÿH…lþÿÿH‰…ˆþÿÿH…hþÿÿH‰…þÿÿH‹E€H‰…˜þÿÿH‹EàH‰… þÿÿH‹EH‰…¨þÿÿH‹EˆH‰…°þÿÿ‹Ẻ…¸þÿÿ‹Eœ‰…¼þÿÿH…€þÿÿ¹ºH‰ÆH=p±ÿÿèïòH‹…˜þÿÿH‰E€H‹… þÿÿH‰EàH‹…¨þÿÿH‰EH‹…°þÿÿH‰Eˆ‹…¸þÿÿ‰EÌ‹…¼þÿÿ‰Eœ‹…hþÿÿÀ¯EÌ‹lþÿÿ™÷ùH˜H‰E€H‹…èýÿÿH‰…ÀþÿÿH…lþÿÿH‰…ÈþÿÿH‹E€H‰…ÐþÿÿH…@þÿÿH‰…ØþÿÿH‹EàH‰…àþÿÿH‹E¸H‰…èþÿÿH‹EH‰…ðþÿÿH‹EˆH‰…øþÿÿ‹Ẻ…ÿÿÿ‹Eœ‰…ÿÿÿH…Àþÿÿ¹ºH‰ÆH=t³ÿÿèòH‹…ÐþÿÿH‰E€H‹…àþÿÿH‰EàH‹…èþÿÿH‰E¸H‹…ðþÿÿH‰EH‹…øþÿÿH‰Eˆ‹…ÿÿÿ‰EÌ‹…ÿÿÿ‰EœHƒ}ˆt H‹EˆH‰ÇèwñHƒ}t H‹EH‰Çèdñ‹•äýÿÿH‹u°H‹E¸‰ÑºH‰Çè_åH‹E¨ÇH‹E Ç‹•lþÿÿH‹M¨H‹E¸H‰ÎH‰Çè¼å‹•lþÿÿH‹M H‹E°H‰ÎH‰Çè£å‹…lþÿÿH˜HÁàHPüH‹E HЋ‹…lþÿÿH˜HÁàHHüH‹E°HÈ‹Љ…|ÿÿÿH…?ÿÿÿH‰Çè~è‹…|ÿÿÿHcÈH•?ÿÿÿH…þÿÿH‰ÎH‰ÇèŠêH…?ÿÿÿH‰ÇèYèHÇEØHÇEÐH‹…èýÿÿH‰Çèøçƒð„Àt)H‹…èýÿÿH‰Çè.êH‰…@ÿÿÿH…@ÿÿÿH‰Çè*çH‰EØH…þÿÿH‰Çè¹çƒð„Àt)H…þÿÿH‰ÇèïéH‰…PÿÿÿH…PÿÿÿH‰ÇèëæH‰EÐL‹M L‹E°H‹MÐH‹U¨H‹u¸H‹EØHƒì‹½äýÿÿWH‰ÇèäHƒÄH•þÿÿH‹…èýÿÿH‰ÖH‰Çè‡éH…þÿÿH‰Çè„éHƒ}¸t H‹E¸H‰ÇèïHÇE¸Hƒ}°t H‹E°H‰ÇètïHÇE°Hƒ}¨t H‹E¨H‰ÇèYïHÇE¨Hƒ} t H‹E H‰Çè>ïHÇE ‹…|ÿÿÿHcÐH‹…èýÿÿH‰ÖH‰Çè!éH‰ÃH‹…èýÿÿ¾H‰Çè éH‰ÞH‰ÇèƒãH…þÿÿH‰ÇèêèH… þÿÿH‰Çè{èH…@þÿÿH‰ÇèTèéH‰ÃH…ÿÿÿH‰Çèâë?H‰ÃH… ÿÿÿH‰Çè‰âë+H‰ÃH…?ÿÿÿH‰Çè_æëH‰ÃH…þÿÿH‰ÇèyèëH‰ÃH… þÿÿH‰ÇèèëH‰ÃH…@þÿÿH‰ÇèÙçH‰ØH‰ÇèšîHeð[A\]ÃUH‰åH‰}ø]ÃUH‰åH‰}ø]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèäåÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EèH‹UØH‰ÖH‰Çè~æH‹UàH‹EèH‰ÖH‰Çè}æëH‰ÃH‹EèH‰Çè`æH‰ØH‰ÇèîHƒÄ([]ÃUH‰åHƒìH‰}øH‹EøH‹@H‰ÂH‹EøH‹H)ÂH‰ÐHÁøH‰ÂH‹EøH‹H‹EøH‰ÎH‰ÇèæH‹EøH‰ÇèæÉÃUH‰åHƒìH‰}øH‰uðH‹EøH‰ÇèôåH‰ÂH‹EøH‹H‹MðH‰ÎH‰Çè—êH‰ÂH‹EøH‰PÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UðH‹EøH‰ÖH‰ÇèëÉÃUH‰åH‰}øH‹EøH‹@H‰ÂH‹EøH‹H)ÂH‰ÐHÁø]ÃUH‰åSHƒì8H‰}ÈH‰uÀHƒ}À„åH‹EÈH‹@H‰ÂH‹EÈH‹@H)ÂH‰ÐHÁøH;EÀr6H‹EÈH‰Çè#åH‰ÂH‹EÈH‹@H‹MÀH‰ÎH‰ÇèÅéH‰ÂH‹EÈH‰PéŒH‹MÀH‹EÈHôïH‰ÎH‰Çè¡ãH‰EàH‹EÈH‰ÇèãH‰EØH‹EÈH‹UàH‰ÖH‰Çè”äH‰EÐH‹EÐH‰EèH‹EÈH‰Çè äH‰ÁH‹EÈH‹pH‹EÈH‹H‹UÐH‰ÇèVéH‰EèH‹EÈH‰ÇèräH‰ÂH‹MÀH‹EèH‰ÎH‰ÇèéH‰EèH‹EÈH‰ÇèLäH‰ÂH‹EÈH‹HH‹EÈH‹H‰ÎH‰Çè;êH‹EÈH‹@H‰ÂH‹EÈH‹H)ÂH‰ÐHÁøH‰ÂH‹EÈH‹H‹EÈH‰ÎH‰ÇèîãH‹EÈH‹UÐH‰H‹EÈH‹UèH‰PH‹EàH…H‹EÐHÂH‹EÈH‰PëYH‰Çè ëH‹EÈH‰Çè²ãH‰ÂH‹MèH‹EÐH‰ÎH‰Çè¨éH‹EÈH‹UàH‹MÐH‰ÎH‰ÇèyãèîêH‰ÃèàêH‰ØH‰Çè ëHƒÄ8[]ÃUH‰åHƒìH‰}øH‰uðH‹EøH‰ÇèHãH‰ÂH‹EøH‹HH‹EðH‰ÎH‰Çè:éH‹EøH‹UðH‰PÉÃUH‰åHƒì H‰}èH‰uà¸@HƒørH‹UàH‹EèHƒìQH‰ÖH‰ÇèÆÞHƒÄëH‹UàH‹EèH‰ÖH‰Çè¹ÞÉÃUH‰åHƒì H‰}øH‰uð‰Uì‰MèD‰EäèÞA‰Â‹}ä‹Mè‹UìH‹uðH‹EøA‰ùA‰ÈD‰ÑH‰ÇèéÉÃUH‰åHƒì0H‰}ØH‹EØH‰Çè8áH‰EàH‹EØH‰Çè4áH‰EðHUàHEðH‰ÖH‰Çè'àÉÃUH‰åSHƒì8H‰}ȉuÄHUà‹EÄH‰Ö‰ÇèæèHUä‹EÄH‰Ö‰ÇèÏèH‹EÈH‰Çèãà¶ÀH…ÀtH íºøH5BìH=ñëè†éH‹EÈH‰Çè¤àH‰ÂH‹EÈH‰ÖH‰ÇèãH‰ÃH‹EȾH‰ÇèãH‰ÞH‰ÇèyÝ‹EĉÇè=è‹EàƒøŽÌ‹EàPÿ‹Eà!Ð…À”ÀˆEï€}ït‹UÄH‹EȉÖH‰ÇèëÜéHUÜ‹EÄH‰Ö‰Çè‘ωEè‹Eä;Eès‹UÜH‹EȉÖH‰Çè¹Üë‹UÜH‹EȉÖH‰ÇèÙþÿÿ‹Eà‰Çè¨Î‰Á‹UÄH‹EȉÎH‰Çè¡ÜHUÜ‹EÄH‰Ö‰ÇèDЉEè‹Eä;Eès‹UÜH‹EȉÖH‰Çè\Üë‹UÜH‹EȉÖH‰Çè|þÿÿHƒÄ8[]ÃUH‰åSHìˆH‰½ˆþÿÿH‰µ€þÿÿ‰•|þÿÿH•üþÿÿ‹…|þÿÿH‰Ö‰ÇèDçHƒ½€þÿÿuH‹zH‰…€þÿÿH•øþÿÿ‹…|þÿÿH‰Ö‰ÇèçH‹…ˆþÿÿH‰Çè ߈EÇÇ…ÜþÿÿÇ…ØþÿÿÇ…ÔþÿÿÇ…ÐþÿÿHÇEèHÇEàH‹…ˆþÿÿH‰ÇèÊÞ‰EÀƒ}À„´‹EÀH˜HºÀH9Ðw"HÁàH‰Çè%çH‰EèHƒ}è”À¶ÀH…Àt&ëè(çH ßêº7H5õéH=Äéè9ç‹EÀH˜HºÀH9Ðw"HÁàH‰ÇèËæH‰EàHƒ}à”À¶ÀH…Àt&ëèÎæH ‰êº:H5›éH=jéèßæ‹…Ôþÿÿ‰…ÿÿÿH‹…ˆþÿÿH‰…ÿÿÿH‹…€þÿÿH‰…ÿÿÿH‹EèH‰…ÿÿÿ‹EÀ‰…ÿÿÿH…ÿÿÿ¹ºH‰ÆH=ߪÿÿèYæ‹…ÿÿÿ‰…ÔþÿÿH‹…ÿÿÿH‰…€þÿÿH‹…ÿÿÿH‰Eè‹…ÿÿÿ‰EÀ‹•|þÿÿHµÐþÿÿH…ÔþÿÿA‰Ð¹XºH‰ÇèÚÇ…Ìþÿÿ¶Eǃð„Àt_H‹Eè‹H‹Eà‰H‹EèHH‹UÀH‹EàH‰ÆH‰Ïè9Ú‹EÀH˜HÁàHPüH‹EàH<‹•|þÿÿH…ÜþÿÿA‰Ð¹XºH‰ÆèÆÙë)‹•|þÿÿHµÜþÿÿH…ÌþÿÿA‰Ð¹XºH‰Çè›Ù‹…üþÿÿPÿ‹…øþÿÿ9Â~5‹…øþÿÿPHµôþÿÿ‹|þÿÿH…ÜþÿÿI‰ñA‰È¹¾H‰ÇèíØ‹…øþÿÿ…Àt7‹…øþÿÿPÿHµàþÿÿ‹|þÿÿH…ØþÿÿI‰ñA‰È¹¾H‰ÇèÙë Ç…ØþÿÿH…ØþÿÿH‰… ÿÿÿH‹EàH‰…(ÿÿÿ‹EÀ‰…0ÿÿÿH… ÿÿÿ¹ºH‰ÆH=ªÿÿèäH‹…(ÿÿÿH‰Eà‹…0ÿÿÿ‰EÀ‹…üþÿÿH˜HºÀH9Ðw"HÁàH‰Çè5äH‰E¸Hƒ}¸”À¶ÀH…Àt&ëè8äH øçºwH5çH=ÔæèIä‹…üþÿÿH˜HºÀH9Ðw"HÁàH‰ÇèØãH‰E°Hƒ}°”À¶ÀH…Àt&ëèÛãH ¢çºzH5¨æH=wæèìã‹…üþÿÿH˜HºÀH9Ðw"HÁàH‰Çè{ãH‰E¨Hƒ}¨”À¶ÀH…Àt&ëè~ãH Lçº}H5KæH=æèãH‹E¨Ç‹…üþÿÿH˜HºÀH9Ðw"HÁàH‰ÇèãH‰E Hƒ} ”À¶ÀH…Àt&ëèãH íæºH5äåH=³åè(ãH‹E ÇH…üþÿÿH‰…@ÿÿÿH‹E¸H‰…HÿÿÿH…@ÿÿÿ¹ºH‰ÆH=Ó¨ÿÿè»âH‹…HÿÿÿH‰E¸‹…üþÿÿ‰Eœ‹…Ðþÿÿ™÷}œ‰E˜‹…Ðþÿÿ™÷}œ‰U”ƒ}˜ŽÇEÜ‹EÜ;EÀ ‹EÜH˜H…H‹EàHЋ…ÀuH‹E¸‹PH‹E¸‰éÅÇEØ‹EÜH˜H…H‹EàHЋ‹E˜ƒÀ¯E”9Â'‹EÜH˜H…H‹EàHЋƒè‹U˜Z™÷û‰EØë%‹EÜH˜H…H‹EàHЋ‹U”ƒÂ)Й÷}˜‰EØ‹…üþÿÿ9EØÀ¶ÀH…ÀtH 庞H5~äH=MäèÂá‹EØH˜H…H‹E¸HЋƒÂ‰ƒEÜéøþÿÿH‹E¸‹‹EÀÂH‹E¸‰‹…üþÿÿPÿ‹…øþÿÿ9Â~H•þÿÿH…ôþÿÿH‰ÖH‰ÇèÅà‹•|þÿÿH‹u°H‹E¸‰ÑºH‰ÇèÕÇE‹E‰…`ÿÿÿH…üþÿÿH‰…PÿÿÿH‹E°H‰…XÿÿÿH…Pÿÿÿ¹ºH‰ÆH=q§ÿÿèÑà‹…`ÿÿÿ‰EH‹…XÿÿÿH‰E°‹•üþÿÿH‹M¨H‹E¸H‰ÎH‰ÇèÕ‹•üþÿÿH‹M H‹E°H‰ÎH‰ÇèýÔH…oÿÿÿH‰ÇèØ‹EHcÈH•oÿÿÿH…°þÿÿH‰ÎH‰Çè!ÚH…oÿÿÿH‰Çèð×HÇEÐHÇEÈH‹…ˆþÿÿH‰Çè׃ð„Àt)H‹…ˆþÿÿH‰ÇèÅÙH‰…pÿÿÿH…pÿÿÿH‰ÇèÁÖH‰EÐH…°þÿÿH‰ÇèP׃ð„Àt#H…°þÿÿH‰Çè†ÙH‰E€HE€H‰ÇèˆÖH‰EÈL‹M L‹E°H‹MÈH‹U¨H‹u¸H‹EÐHƒì‹½|þÿÿWH‰Çè­ÓHƒÄH•°þÿÿH‹…ˆþÿÿH‰ÖH‰ÇèhÝH…°þÿÿH‰Çè!Ù¶Eǃð„Àt&Hƒ}àt H‹EàH‰Çè!ßHƒ}èt H‹EèH‰ÇèßHƒ}¸t H‹E¸H‰ÇèûÞHÇE¸Hƒ}¨t H‹E¨H‰ÇèàÞHÇE¨Hƒ}°t H‹E°H‰ÇèÅÞHÇE°Hƒ} t H‹E H‰ÇèªÞHÇE H…°þÿÿH‰Çè“Øë:H‰ÃH…oÿÿÿH‰ÇèQÖH‰ØH‰ÇèÈÞH‰ÃH…°þÿÿH‰ÇèbØH‰ØH‰Çè«ÞH‹]øÉÃUH‰åHƒìH‰}øH‹EøH‰ÇèŽÖÉÃUH‰åHƒìH‰}øH‹EøH‰ÇèlÖH‰ÂH‹EøH‹HH‹EøH‹H‰ÎH‰Çè©ÜH‹EøH‰ÇèOÖÉÃUH‰åH‰}øH‹EøÇH‹EøÇ@]ÃUH‰åAWAVAUATSHìH‰½èýÿÿH‰µàýÿÿ‰•Üýÿÿ‰ØýÿÿH•ÿÿÿ‹…ØýÿÿH‰Ö‰ÇèÝH• ÿÿÿ‹…ØýÿÿH‰Ö‰ÇèäÜH‹…àýÿÿH‰ÇèéÔ‰…ÿÿÿ‹•ØýÿÿHµÿÿÿH…ÿÿÿA‰Ð¹XºH‰ÇèfÑ‹…ÿÿÿ¯…ÜýÿÿiÀè‹•ÿÿÿ‰Ñº÷ñ‰…üþÿÿ‹…ÜýÿÿiÀè‹•ÿÿÿ9ЃƒèaÝfïÀó*Àó Ëßó^Á(È‹…ÿÿÿfïÀó*ÀóYÈ‹…ÿÿÿ¯…ÜýÿÿiÀè‰ÀH…Àx fïÀóH*ÀëH‰ÂHÑêƒàH ÂfïÀóH*ÂóXÀ.Áv¸ë¸‰…üþÿÿ‹•üþÿÿ‹…ÿÿÿ9Â~ ‹…ÿÿÿ‰…üþÿÿHE H‰ÇèÔ‹…üþÿÿHcÈHU H…àþÿÿH‰ÎH‰ÇèÖHE H‰ÇèðÓHE¡H‰ÇèØÓ‹…üþÿÿHcÈHU¡H…ÀþÿÿH‰ÎH‰ÇèçÕHE¡H‰Çè¹ÓHÇEÈ‹…üþÿÿH˜H;EȆžH‹UÈH…ÀþÿÿH‰ÖH‰Çè½ÕH‰Ã‹…ÿÿÿH˜H¯EÈHÀ‹•ÿÿÿHcʺH÷ñA‰ÄèýÛ‹ÿÿÿ™÷ù‰ÐDà‰H‹UÈH…àþÿÿH‰ÖH‰ÇèkÕH‰ÃèÏÛ‹ÿÿÿ™÷ù‰ÐHcÐH‹…àýÿÿH‰ÖH‰ÇèCÕ‹‰HƒEÈéPÿÿÿHE¢H‰ÇèéÒ‹…ÿÿÿHcÈHU¢H… þÿÿH‰ÎH‰ÇèøÔHE¢H‰ÇèÊÒHE£H‰Çè²ÒÇE¤‹…ÿÿÿHcðHM£HU¤H…€þÿÿH‰Çè¿ÔHE£H‰Çè‹ÒH… þÿÿ¾H‰Çè«ÔH‰Æ‹•ØýÿÿH…üþÿÿ‰ÑºH‰Çè°Î‹ÿÿÿH…€þÿÿ¾H‰ÇèrÔI‰ÄH… þÿÿ¾H‰Çè[Ô‰ÚL‰æH‰Çèê΋…ÿÿÿƒèHcÐH… þÿÿH‰ÖH‰Çè0Ô‹‹…ÿÿÿƒèHcÐH…€þÿÿH‰ÖH‰ÇèÔ‹؉EÄHE©H‰Çè½Ñ‹EÄHcÈHU©H…`þÿÿH‰ÎH‰ÇèÏÓHE©H‰Çè¡ÑHEªH‰Çè‰Ñ‹EÄHcÈHUªH…@þÿÿH‰ÎH‰Çè›ÓHEªH‰ÇèmÑèºÍA‰ÄH…€þÿÿ¾H‰Çè…ÓH‰…ÐýÿÿH… þÿÿ¾H‰ÇèjÓI‰ÇH…@þÿÿ¾H‰ÇèSÓI‰ÆèiÍA‰Å‹üþÿÿH…Àþÿÿ¾H‰Çè.ÓH‰Ç‹…ØýÿÿPATL‹ÐýÿÿM‰øL‰ñD‰ê‰Þè?ØHƒÄè ÍA‰ÄH…€þÿÿ¾H‰ÇèëÒH‰…ÐýÿÿH… þÿÿ¾H‰ÇèÐÒI‰ÇH…`þÿÿ¾H‰Çè¹ÒI‰ÆèÏÌA‰Å‹üþÿÿH…àþÿÿ¾H‰Çè”ÒH‰Ç‹…ØýÿÿPATL‹ÐýÿÿM‰øL‰ñD‰ê‰Þè¥×HƒÄHE«H‰Çè!ÐÇE¬‹EÄHcðHM«HU¬H… þÿÿH‰Çè1ÒHE«H‰ÇèýÏ‹…ÿÿÿ…ÀŽÆH‹…àýÿÿH‰…ÿÿÿH… ÿÿÿH‰…ÿÿÿH…ÿÿÿH‰… ÿÿÿH…ÿÿÿH‰…(ÿÿÿH…ÿÿÿH‰…0ÿÿÿH…`þÿÿH‰…8ÿÿÿH…@þÿÿH‰…@ÿÿÿH… þÿÿH‰…Hÿÿÿ‹Eĉ…Pÿÿÿ‹EÀ‰…Tÿÿÿ‹E¼‰…XÿÿÿH…ÿÿÿ¹ºH‰ÆH=Þžÿÿè¢×‹…Pÿÿÿ‰EÄ‹…Tÿÿÿ‰EÀ‹…Xÿÿÿ‰E¼HE³H‰ÇèÏÇE´‹EÄHcðHM³HU´H…þÿÿH‰Çè!ÑHE³H‰ÇèíÎè:ËA‰ÄH…þÿÿ¾H‰ÇèÑH‰ÃH… þÿÿ¾H‰ÇèîÐH‰Ç‹•Øýÿÿ‹EÄA‰ÑA¸XD‰á‰ÂH‰Þè ÖHE»H‰Çèh΋ÜýÿÿHU»H‹…èýÿÿH‰ÎH‰Çè8ÐHE»H‰ÇèLÎÇ…ðýÿÿÇ…ôýÿÿH‹…èýÿÿH‰…`ÿÿÿH…ÿÿÿH‰…hÿÿÿH…`þÿÿH‰…pÿÿÿH…@þÿÿH‰…xÿÿÿH…þÿÿH‰E€H…ðýÿÿH‰Eˆ‹…Üýÿÿ‰E‹EĉE”H…`ÿÿÿ¹ºH‰ÆH=v ÿÿè1Ö‹E‰…Üýÿÿ‹E”‰EÄH…þÿÿH‰ÇèâÏH… þÿÿH‰ÇèÓÏH…@þÿÿH‰ÇèÄÏH…`þÿÿH‰ÇèµÏH…€þÿÿH‰Çè¦ÏH… þÿÿH‰Çè—ÏH…ÀþÿÿH‰ÇèˆÏH…àþÿÿH‰ÇèyÏé>H‰ÃHE H‰Çè7ÍH‰ØH‰Çè®ÕH‰ÃHE¡H‰ÇèÍéöH‰ÃHE¢H‰Çè ÍéÓH‰ÃHE£H‰ÇèõÌé°H‰ÃHE©H‰ÇèáÌéH‰ÃHEªH‰ÇèÍÌëhH‰ÃHE«H‰Çè¼ÌëHH‰ÃHE³H‰Çè«Ìë#H‰ÃHE»H‰Çè‚ÌëH‰ÃH…þÿÿH‰Çè´ÎH… þÿÿH‰Çè¥ÎëH‰ÃH…@þÿÿH‰Çè‘ÎH…`þÿÿH‰Çè‚ÎëH‰ÃH…€þÿÿH‰ÇènÎH… þÿÿH‰Çè_ÎH…ÀþÿÿH‰ÇèPÎH…àþÿÿH‰ÇèAÎH‰ØH‰ÇèŠÔH‹…èýÿÿHeØ[A\A]A^A_]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèˆÌH‰ÂH‹EøH‹HH‹EøH‹H‰ÎH‰Çè¡ÒH‹EøH‰ÇèkÌÉÃUH‰åH‰}øH‹EøH‹@H‰ÂH‹EøH‹H)ÂH‰ÐHÁø]ÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÐ]ÃUH‰åH‰}ø‰uôH‰UèH‹Eø‹Uô‰PH‹EøH‹UèH‰]ÃUH‰åH‰}ø]ÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒìH‰}øH‰uðH‹EðH‰ÇèþÐH‰ÂH‹EøH‰ÖH‰ÇèÌÉÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÐ]ÃUH‰åH‰}øH‹EøH‹@H‰ÂH‹EøH‹H)ÂH‰ÐHÁø]ÃUH‰åHƒì H‰}èH‰uà¸@HƒørH‹UàH‹EèHƒìQH‰ÖH‰Çè!ÇHƒÄëH‹UàH‹EèH‰ÖH‰Çè&ÇÉÃUH‰åHƒì0H‰}ØH‹EØH‰ÇèÈÉH‰EàH‹EØH‰ÇèÄÉH‰EðHUàHEðH‰ÖH‰ÇèÛÈÉÃUH‰åHƒì H‰}èH‹UèHEðH‰ÖH‰ÇèQÇH‹EðÉÃUH‰åH‰}øH‹EøH‹]ÃUH‰åSHƒì(H‰}èH‰uàH‰UØHƒìPè@ÈHƒÄH‹UØH‹MàH‹EèHƒìSH‰ÎH‰ÇèlÍHƒÄH‹]øÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØHƒìPèÈHƒÄH‹UØH‹MàH‹EèHƒìSH‰ÎH‰Çè)ÍHƒÄH‹]øÉÃUH‰åH‰}øH‰uðH‹Eø‹PH‹Eð‹@9ÂuH‹EøH‹H‹EðH‹H9–ÀëH‹Eø‹PH‹Eð‹@9žÀ]ÃUH‰åHƒì ‰}üH‰uð‰UøH‰Mè‹Eø;Eür‹Eüë:‹Eø;Eüs/‹EøHÁàH‰ÂH‹EðHÂH‹EèH‰ÖH‰Çè˜Ç„Àt‹Eøë ƒEøëÉ‹EøÉÃUH‰åSHƒì(H‰}èH‰uà‰U܉MØèàĉÃèÙÄA‰Â‹}ÜH‹Uà‹uÜH‹EèHƒì‹MØQA‰ÙA‰øH‰ÑD‰ÒH‰ÇèÔÏHƒÄH‹]øÉÃUH‰åHƒÄ€H‰}˜H‰u‰UŒè›Ð‰Eô‹EôkÀd;EŒ~_ÇEü‹Eü;EŒÛ‹EüH˜H…H‹EHЋUüHcÒHÁâHJüH‹UHÊ‹ ‹UüHcÒHÁâHrüH‹U˜Hò‹ʉƒEü먋EŒ™÷}ô‰EðH‹E˜H‰E H‹EH‰E¨‹EŒ‰E°‹Eô‰E´‹Eð‰E¸HE ¹ºH‰ÆH=§¶ÿÿèßÏH‹E H‰E˜H‹E¨H‰E‹E°‰EŒ‹E´‰Eô‹E¸‰Eð‹EôH˜HºÀH9Ðw#HÁàH‰ÇèyÏH‰EèH‹EèÇÇEøëè{Ï‹Eø;Eô}j‹EøH˜H…H‹EèH‹EøH˜HÁàHHüH‹EèHÈ‹‹Eø¯EðH˜HÁàHpüH‹EHð‹Á‹Eø¯EðH˜HÁàHpüH‹E˜Hð‹ȉƒEøëŽH‹EH‰EÀH‹EèH‰EÈ‹EŒ‰EЋEô‰EÔ‹Eð‰EØHEÀ¹ºH‰ÆH=Þ¶ÿÿèÐÎH‹EÀH‰EH‹EÈH‰Eè‹EЉEŒ‹EÔ‰Eô‹E؉EðHƒ}ètH‹EèH‰ÇèkÎëÉÃUH‰åHƒì H‰}èH‹UèHEðH‰ÖH‰Çè…ÃH‹EðÉÃUH‰åH‰}øH‹EøH‹]ÃUH‰åSHƒì8H‰}èH‰uàH‰UØH‰MÐL‰EÈL‰MÀè-‰Ãè&ÂA‰ÂL‹EÈH‹}ÐH‹UØH‹uàH‹EèHƒì‹MQSÿuÀM‰ÁI‰øD‰ÑH‰Çè ÍHƒÄ ¸H‹]øÉÃUH‰åSHƒìH‰}èH‰uàH‹UàH‹EèH‰ÖH‰Çè2ÆH‹EàH‰ÇèDÆH‰ÃH‹EèH‰Çè5ÆH‰ÞH‰Çè”ÂHƒÄ[]ÃUH‰åHƒìH‰}øH‹EøH‹H‹EøH‰ÖH‰ÇèöÆÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèïÄH‹EøHÇH‹EøHÇ@H‹EøHÇ@ÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèkÅH‰ÂH‹EøH‰H‹EøH‹H‹EøH‰PH‹EøH‹H‹UðHÁâHÂH‹EøH‰PÉÃUH‰åHƒì H‰}øH‰uðH‰UèHƒ}ðtH‹EøH‹UèH‹MðH‰ÎH‰ÇèzÅÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UðH‹EøH‰ÖH‰Çè|ÉÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèSÄÉÃUH‰åSHƒì8H‰}ØH‰uÐH‰UÈH‹EØH‰Çè™ÃH‰ÃH‹EØH‰ÇèrÃH)ÃH‰ÚH‹EÐH9Â’À„Àt H‹EÈH‰ÇèÂËH‹EØH‰ÇèFÃH‰ÃH‹EØH‰Çè7ÃH‰EàHUÐHEàH‰ÖH‰Çè,ÉH‹HØH‰EèH‹EØH‰Çè ÃH;EèwH‹EØH‰ÇèÃH;EèsH‹EØH‰ÇèþÂëH‹EèHƒÄ8[]ÃUH‰åHƒìH‰}øH‰uðHƒ}ðtH‹EøH‹UðH‰ÖH‰Çè@Äë¸ÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‰MÐH‹EàH‰ÇèzÈH‰ÃH‹EèH‰ÇèkÈH‰ÇH‹UÐH‹EØH‰ÑH‰ÂH‰ÞèÂÇHƒÄ([]ÃUH‰åATSHìÐH‰½(ÿÿÿH‰µ ÿÿÿèâʉEìH‹• ÿÿÿH‹…(ÿÿÿH)ÂH‰ÐHÁøH‰EÀ‹EìÀH˜H;EÀƒ}ìu(H‹• ÿÿÿH‹…(ÿÿÿHƒìATH‰ÖH‰ÇèQÈHƒÄé‹EìƒÀH˜HºàH9ЇHÁàH‰Çè7ÊH‰Eà‹EìH˜HÅH‹EàHÂH‹EÀH‰H‹EÀH‰…0ÿÿÿH‹EàH‰…8ÿÿÿ‹E쉅@ÿÿÿH…0ÿÿÿ¹ºH‰ÆH=)³ÿÿèýÉH‹…0ÿÿÿH‰EÀH‹…8ÿÿÿH‰Eà‹…@ÿÿÿ‰EìH‹…(ÿÿÿH‰…PÿÿÿH‹EàH‰…Xÿÿÿ‹E쉅`ÿÿÿH…Pÿÿÿ¹ºH‰ÆH=k³ÿÿèœÉH‹…PÿÿÿH‰…(ÿÿÿH‹…XÿÿÿH‰Eà‹…`ÿÿÿ‰EìH‹EÀHºÀH9Ðw3ëèZÉHÁàH‰Çè0ÉH‰E¸H‹…(ÿÿÿH‰EØH‹E¸H‰EÐÇEÌëè)É‹EÌ;Eì¸ÇEÈ‹EÈ;Eì…‹UÈ‹EÌÐ;Eìó‹EÈH˜HÅH‹EàHÐH‹H…H‹EÐL‹EÌ‹EÈ‹Eì9ÂNÂH˜HÅH‹EàHÐH‹H…H‹EØH ‹UÈ‹EÌÐH˜HÅH‹EàHÐH‹H…H‹EØH‹uÈ‹EÌðH˜H4ÅH‹EàHðH‹H4…H‹EØHÆ‹EÈH˜H<ÅH‹EàHøH‹H<…H‹EØHø‹}ìHƒìSA‰ùH‰Ç蜼HƒÄëtH‹EàH‰…pÿÿÿH‹EØH‰…xÿÿÿH‹EÐH‰E€‹Eì‰Eˆ‹EȉEŒH…pÿÿÿ¹ºH‰ÆH={²ÿÿèÂÇH‹…pÿÿÿH‰EàH‹…xÿÿÿH‰EØH‹E€H‰EЋEˆ‰Eì‹EŒ‰EÈ‹EÌÀEÈéoþÿÿH‹EØH‰E°H‹EÐH‰EØH‹E°H‰EÐÑeÌé<þÿÿH‹EØH;…(ÿÿÿtSH‹…(ÿÿÿH‰EH‹EÀH‰E˜H‹EØH‰E HE¹ºH‰ÆH=²ÿÿè&ÇH‹EH‰…(ÿÿÿH‹E˜H‰EÀH‹E H‰EØHƒ}àt H‹EàH‰ÇèÈÆHƒ}¸t H‹E¸H‰ÇèµÆHeð[A\]ÃUH‰åSHì˜H‰½hÿÿÿH‰µ`ÿÿÿèÃÆ‰EìH‹•`ÿÿÿH‹…hÿÿÿH)ÂH‰ÐHÁøH‰EàH‹EàHºàH9Ðw)HÁàH‰Çè[ÆH‰EØH‹EàHºÀH9Ї(ëèWÆHÁàH‰Çè-ÆH‰EÐH‹…hÿÿÿH‰…pÿÿÿH‹EàH‰…xÿÿÿH‹EØH‰E€H‹EÐH‰Eˆ‹Eì‰EH…pÿÿÿ¹ºH‰ÆH=»ÿÿèþÅH‹…pÿÿÿH‰…hÿÿÿH‹…xÿÿÿH‰EàH‹E€H‰EØH‹EˆH‰EЋE‰EìH‹EàHÅH‹EØHÂH‹EØHƒìSH‰ÖH‰Çèü¹HƒÄH‹…hÿÿÿH‰E H‹EàH‰E¨H‹EØH‰E°‹Eì‰E¸HE ¹ºH‰ÆH=Õ»ÿÿèfÅH‹E H‰…hÿÿÿH‹E¨H‰EàH‹E°H‰EØ‹E¸‰EìHƒ}Øtëè1ÅH‹EØH‰ÇèûÄHƒ}Ðt H‹EÐH‰ÇèèÄH‹]øÉÃUH‰åHƒì H‰}èH‹EèH‹H‰EøHUøHEðH‰ÖH‰Çèè¹H‹EðÉÃUH‰åHƒì H‰}èH‹EèH‹@H‰EøHUøHEðH‰ÖH‰Çè·¹H‹EðÉÃUH‰åSHƒìH‰}èH‰uàH‹EèH‰ÇèD»H‹H‹EàH‰Çè5»H‹H9ÔÀHƒÄ[]ÃUH‰åHƒì H‰}è‰uäHUð‹EäH‰Ö‰Çè™ÃHUô‹EäH‰Ö‰Çè‚ÃÇEüÇEø‹Eð9Eü<‹Eô#Eø…Àu‹Uä‹MüH‹Eè‰ÎH‰Çè,¸ë‹Uä‹MüH‹Eè‰ÎH‰Çè¸ÑeüÑeøë¼ÉÃUH‰åHƒì0H‰}؉uÔ‰UÐHUð‹EÐH‰Ö‰Çè ÃHUì‹EÐH‰Ö‰Çèÿ‹Eì‰Çè÷©‰Eü‹Eì+Eü‰Eø‹Eð;Eüs3‹Eü+Eø‹Uð9Ðw&‹Eð‰Â‹EøЉEô‹MЋUôH‹EؾeH‰Çè4·ë(‹Eð;Eür ‹Eð+Eø‰Eô‹MЋUôH‹EؾdH‰Çè ·ÉÃUH‰åH‰}ø¸]ÃUH‰åHƒì H‰}øH‰uð‰Uì‰MèD‰Eäè·A‰Â‹}ä‹Mè‹UìH‹uðH‹EøA‰ùA‰ÈD‰ÑH‰Çè`ÂÉÃUH‰åHƒì H‰}ø‰uô‰Uð‰MìD‰EèL‰Màè¹¶A‰Â‹}è‹Mì‹Uð‹uôH‹EøHƒìÿuàA‰ùA‰È‰ÑD‰ÒH‰ÇèüÁHƒÄ¸ÉÃUH‰åHƒì H‰}ø‰uô‰Uð‰MìD‰EèL‰Màè_¶A‰Â‹}è‹Mì‹Uð‹uôH‹EøHƒìÿuàA‰ùA‰È‰ÑD‰ÒH‰Çè®ÁHƒÄ¸ÉÃUH‰åHƒì0H‰}øH‰uðH‰UèH‰MàL‰EØL‰MÐL‹MÐL‹EØH‹MàH‹UèH‹uðH‹EøHƒì‹}WH‰ÇèñµHƒÄÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çèg»ÉÃUH‰åHƒìH‰}øH‹EøH‰Çè¹ÉÃUH‰åHƒìH‰}øH‹EøH‰Ç蘹ÉÃUH‰åHƒìH‰}øH‹EøH‹@H‰ÂH‹EøH‹H)ÂH‰ÐHÁøH‰ÂH‹EøH‹H‹EøH‰ÎH‰Çè_¹H‹EøH‰ÇèM¹ÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UðH‹EøH‰ÖH‰Çèu¿ÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‰MÐH‹EèH‹UÐH‹MàH‰ÎH‰ÇèZ¹H‹UØH‹MàH‹EèH‰ÎH‰ÇèEºëH‰ÃH‹EèH‰Çè8¹H‰ØH‰Çè¿ÀHƒÄ([]ÃUH‰åSHƒì(H‰}èH‰uà‰U܉MØèl´‰Ãèe´A‰Â‹}ÜH‹Uà‹uÜH‹EèHƒì‹MØQA‰ÙA‰øH‰ÑD‰ÒH‰ÇèH¿HƒÄH‹]øÉÃUH‰åSHƒì8H‰}ØH‰uÐH‰UÈè/¶H‹UÈH‹MÐH‹EØHƒìSH‰ÎH‰Çè_»HƒÄH‹]øÉÃUH‰åSHƒì8H‰}ØH‰uÐH‰UÈè¶H‹UÈH‹MÐH‹EØHƒìSH‰ÎH‰Çè7»HƒÄH‹]øÉÃUH‰åHƒìH‰}øH‹EøH‰Çèf´ÉÃUH‰åHƒìH‰}øH‹EøH‰ÇèP´ÉÃUH‰åHƒìH‰}øH‹EøH‰Çè4´ÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EèH‹UØH‹MàH‰ÎH‰Ç获H‹UàH‹EèH‰ÖH‰Ç艸ëH‰ÃH‹EèH‰Çèp·H‰ØH‰Çè-¿HƒÄ([]ÃUH‰åH‰}øH‰uðH‹Eð‹H‹Eø‰H‹Eð‹PH‹Eø‰PH‹Eø]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèN¶ÉÃUH‰åHƒìH‰}øH‹EøH‹@H‰ÂH‹EøH‹H)ÂH‰ÐHÁøH‰ÂH‹EøH‹H‹EøH‰ÎH‰Çè½¶H‹EøH‰Çè«¶ÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UðH‹EøH‰ÖH‰Çèµ¼ÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒìH‰}øH‰uðH‹EøH‹PH‹EøH‹@H9Ât¼H‹…@ÿÿÿH‰…ÿÿÿH‹…HÿÿÿH‰EЋ…Pÿÿÿ‰EÜH‹]°H¸ðH9Ãwëèü»H‰ØHÁàHƒÀëHÇÀÿÿÿÿH‰Çè»I‰ÄI‰$ID$HSÿH‰ÓI‰ÅHƒûÿtL‰ïèt¯IƒÅHƒëëèID$H‰E¨H‹…ÿÿÿH‰EÈH‹E¨H‰EÀÇE¼‹E¼;EÜÉÇE¸‹E¸;EÜ–‹U¸‹E¼Ð;EÜò‹E¸H˜HÅH‹EÐHÐH‹HÁàH‰ÂH‹EÀL‹E¼‹E¸‹EÜ9ÂNÂH˜HÅH‹EÐHÐH‹HÁàH‰ÂH‹EÈH ‹U¸‹E¼ÐH˜HÅH‹EÐHÐH‹HÁàH‰ÂH‹EÈH‹u¸‹E¼ðH˜H4ÅH‹EÐHðH‹HÁàH‰ÆH‹EÈHÆ‹E¸H˜H<ÅH‹EÐHøH‹HÁàH‰ÇH‹EÈHø‹}ÜHƒìAVA‰ùH‰Çèô®HƒÄé†H‹EÐH‰…`ÿÿÿH‹EÈH‰…hÿÿÿH‹EÀH‰…pÿÿÿ‹E܉…xÿÿÿ‹E¸‰…|ÿÿÿH…`ÿÿÿ¹ºH‰ÆH=ˆÿÿè ºH‹…`ÿÿÿH‰EÐH‹…hÿÿÿH‰EÈH‹…pÿÿÿH‰EÀ‹…xÿÿÿ‰EÜ‹…|ÿÿÿ‰E¸‹E¼ÀE¸é^þÿÿH‹EÈH‰E H‹EÀH‰EÈH‹E H‰EÀÑe¼é+þÿÿH‹EÈH;…ÿÿÿtSH‹…ÿÿÿH‰E€H‹E°H‰EˆH‹EÈH‰EHE€¹ºH‰ÆH=Eˆÿÿè{¹H‹E€H‰…ÿÿÿH‹EˆH‰E°H‹EH‰EÈHƒ}Ðt H‹EÐH‰Çè¹Hƒ}¨t>H‹E¨HƒèH‹HÁàH‰ÂH‹E¨HH;]¨tHƒëH‰ßèÒ¬ëìH‹E¨HƒèH‰ÇèØ¸Heà[A\A]A^]ÃUH‰åAVAUATSHìH‰½XÿÿÿH‰µPÿÿÿèܸ‰EÜH‹•PÿÿÿH‹…XÿÿÿH)ÂH‰ÐHÁøH‰EÐH‹EÐHºàH9Ðw%HÁàH‰Çèt¸H‰EÈH‹]ÐH¸ðH9Ãwëèt¸H‰ØHÁàHƒÀëHÇÀÿÿÿÿH‰Çè:¸I‰ÄI‰$ID$HSÿH‰ÓI‰ÅHƒûÿtL‰ïèì«IƒÅHƒëëèID$H‰EÀH‹…XÿÿÿH‰…`ÿÿÿH‹EÐH‰…hÿÿÿH‹EÈH‰…pÿÿÿH‹EÀH‰…xÿÿÿ‹E܉E€H…`ÿÿÿ¹ºH‰ÆH=<ÿÿèÒ·H‹…`ÿÿÿH‰…XÿÿÿH‹…hÿÿÿH‰EÐH‹…pÿÿÿH‰EÈH‹…xÿÿÿH‰EÀ‹E€‰EÜH‹EÐHÅH‹EÈHÂH‹EÈHƒìAVH‰ÖH‰ÇèëHƒÄH‹…XÿÿÿH‰EH‹EÐH‰E˜H‹EÈH‰E ‹E܉E¨HE¹ºH‰ÆH=ïÿÿè3·H‹EH‰…XÿÿÿH‹E˜H‰EÐH‹E H‰EÈ‹E¨‰EÜHƒ}Èt H‹EÈH‰Çè϶Hƒ}Àt>H‹EÀHƒèH‹HÁàH‰ÂH‹EÀHH;]ÀtHƒëH‰ß脪ëìH‹EÀHƒèH‰Ç芶Heà[A\A]A^]ÃUH‰åHƒì H‰}èH‹EèH‹H‰EøHUøHEðH‰ÖH‰Çè~«H‹EðÉÃUH‰åHƒì H‰}èH‹EèH‹@H‰EøHUøHEðH‰ÖH‰ÇèM«H‹EðÉÃUH‰åSHƒìH‰}èH‰uàH‹EèH‰ÇèÚ¬H‹H‹EàH‰ÇèˬH‹H9ÔÀHƒÄ[]ÃUH‰åH‰}øH‰uðH‹EðH‹H‹EøH‰]ÃUH‰åSHƒìHEïHƒìRH‰ÇèE«HƒÄ‰ØH‹]øÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰ÇèB´H‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰Çè·³H‹UèH‹EÈH‰ÆH}èÓª„Àt H‹EðH‰Eøë±H‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰Eøë’H‹EØÉÃUH‰åSHƒìHEïHƒìRH‰Ç說HƒÄ‰ØH‹]øÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰Çè³H‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰Çè³H‹UèH‹EÈH‰ÆH}è8ª„Àt H‹EðH‰Eøë±H‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰Eøë’H‹EØÉÃUH‰åH‰}øH‰uðH‹EðH‹H‹EøH‰]ÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çè²H‹EðHPH‹EøHƒÀH‰ÖH‰Çèè±H‹EðHPH‹EøHƒÀH‰ÖH‰ÇèͱÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çè¯ÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çè¨ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹MðH‹EøH‰ÎH‰ÇèG¨ÉÃUH‰åHƒì H‰}èH‰uàÆEÿH‹UàH‹EèH‰ÖH‰Çès¬ÉÃUH‰åH‰}øH‰uð]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèªH‰Çè¬ÉÃUH‰åH‰}øH‰uðH‹EøH‹H‹EðH‹H9ÂsH‹EðëH‹Eø]ÃUH‰åHƒìH‰}øH‰uðH‹MðH‹EøºH‰ÎH‰Çè§ÉÃUH‰åHƒì H‰}èH‹UèHEðH‰ÖH‰Çè<«H‹EðÉÃUH‰åHƒì H‰}ðH‰uàH‰UèH‰MøH‹UèH‹MàH‹EðH‰ÎH‰Çè‘®ÉÃUH‰åSHƒìH‰}èH‰uàHƒìP褨HƒÄH‹UàH‹EèHƒìSH‰ÖH‰ÇèP°HƒÄH‹]øÉÃUH‰åHìH‰½˜þÿÿH‰µþÿÿH‰•ˆþÿÿH‰€þÿÿL‰…xþÿÿD‰tþÿÿH‹•þÿÿH‹…˜þÿÿH)ÂH‰ÐHÁøH‰EøH‹•€þÿÿH‹…ˆþÿÿH)ÂH‰ÐHÁøH‰EðHƒ}øu Hƒ}ð„0Hƒ}øt Hƒ}ð…µHƒ}øu H‹…ˆþÿÿëH‹…˜þÿÿH‰EÀHƒ}øuH‹EðëH‹EøH‰E¸H‹…xþÿÿH‰… þÿÿH‹EÀH‰…¨þÿÿH‹E¸H‰…°þÿÿ‹…tþÿÿ‰…¸þÿÿH… þÿÿ¹ºH‰ÆH=Tÿÿè±H‹… þÿÿH‰…xþÿÿH‹…¨þÿÿH‰EÀH‹…°þÿÿH‰E¸‹…¸þÿÿ‰…tþÿÿéjÇEì ‹…tþÿÿ¯EìÀH˜HºÀH9Ðw3HÁàH‰Çè‘°H‰Eà‹…tþÿÿ¯EìÀH˜HºàH9ЇEë胰HÁàH‰ÇèY°H‰EØH‹…˜þÿÿH‰…ÀþÿÿH‹…þÿÿH‰…ÈþÿÿH‹…ˆþÿÿH‰…ÐþÿÿH‹…€þÿÿH‰…ØþÿÿH‹EøH‰…àþÿÿH‹EðH‰…èþÿÿH‹EàH‰…ðþÿÿH‹EØH‰…øþÿÿ‹…tþÿÿ‰…ÿÿÿ‹E쉅ÿÿÿH…Àþÿÿ¹ºH‰ÆH=&ÿÿèà¯H‹…ÀþÿÿH‰…˜þÿÿH‹…ÈþÿÿH‰…þÿÿH‹…ÐþÿÿH‰…ˆþÿÿH‹…ØþÿÿH‰…€þÿÿH‹…àþÿÿH‰EøH‹…èþÿÿH‰EðH‹…ðþÿÿH‰EàH‹…øþÿÿH‰EØ‹…ÿÿÿ‰…tþÿÿ‹…ÿÿÿ‰Eì‹…tþÿÿƒÀH˜HºàH9Ðw7ëè@¯HÁàH‰Çè¯H‰EЋ…tþÿÿƒÀH˜HºàH9Ї°ëè ¯HÁàH‰Çèá®H‰EÈH‹EÐHÇH‹EÈHÇ‹…tþÿÿH˜HÅH‹EÐHÂH‹EøH‰‹…tþÿÿH˜HÅH‹EÈHÂH‹EðH‰H‹…˜þÿÿH‰…ÿÿÿH‹…þÿÿH‰…ÿÿÿH‹…ˆþÿÿH‰… ÿÿÿH‹…€þÿÿH‰…(ÿÿÿH‹EøH‰…0ÿÿÿH‹EðH‰…8ÿÿÿH‹EàH‰…@ÿÿÿH‹EØH‰…HÿÿÿH‹EÐH‰…PÿÿÿH‹EÈH‰…Xÿÿÿ‹…tþÿÿ‰…`ÿÿÿ‹E쉅dÿÿÿH…ÿÿÿ¹ºH‰ÆH=¼ÿÿè®H‹…ÿÿÿH‰…˜þÿÿH‹…ÿÿÿH‰…þÿÿH‹… ÿÿÿH‰…ˆþÿÿH‹…(ÿÿÿH‰…€þÿÿH‹…0ÿÿÿH‰EøH‹…8ÿÿÿH‰EðH‹…@ÿÿÿH‰EàH‹…HÿÿÿH‰EØH‹…PÿÿÿH‰EÐH‹…XÿÿÿH‰EÈ‹…`ÿÿÿ‰…tþÿÿ‹…dÿÿÿ‰EìHƒ}àtëè]­H‹EàH‰Çè'­Hƒ}Øt H‹EØH‰Çè­H‹…˜þÿÿH‰…pÿÿÿH‹…ˆþÿÿH‰…xÿÿÿH‹…xþÿÿH‰E€H‹EÐH‰EˆH‹EÈH‰E‹…tþÿÿ‰E˜H…pÿÿÿ¹ºH‰ÆH=e ÿÿèä¬H‹…pÿÿÿH‰…˜þÿÿH‹…xÿÿÿH‰…ˆþÿÿH‹E€H‰…xþÿÿH‹EˆH‰EÐH‹EH‰EÈ‹E˜‰…tþÿÿHƒ}Ðt H‹EÐH‰Çèa¬Hƒ}ÈtH‹EÈH‰ÇèN¬ëÉÃUH‰åATSHìÐH‰½(ÿÿÿH‰µ ÿÿÿè^¬‰EìH‹• ÿÿÿH‹…(ÿÿÿH)ÂH‰ÐHÁøH‰EÀ‹EìÀH˜H;EÀƒ}ìu(H‹• ÿÿÿH‹…(ÿÿÿHƒìATH‰ÖH‰ÇèÇ©HƒÄé‹EìƒÀH˜HºàH9ЇHÁàH‰Ç賫H‰Eà‹EìH˜HÅH‹EàHÂH‹EÀH‰H‹EÀH‰…0ÿÿÿH‹EàH‰…8ÿÿÿ‹E쉅@ÿÿÿH…0ÿÿÿ¹ºH‰ÆH=ô¢ÿÿèy«H‹…0ÿÿÿH‰EÀH‹…8ÿÿÿH‰Eà‹…@ÿÿÿ‰EìH‹…(ÿÿÿH‰…PÿÿÿH‹EàH‰…Xÿÿÿ‹E쉅`ÿÿÿH…Pÿÿÿ¹ºH‰ÆH=6£ÿÿè«H‹…PÿÿÿH‰…(ÿÿÿH‹…XÿÿÿH‰Eà‹…`ÿÿÿ‰EìH‹EÀHºàH9Ðw3ëèÖªHÁàH‰Ç謪H‰E¸H‹…(ÿÿÿH‰EØH‹E¸H‰EÐÇEÌë襪‹EÌ;Eì¸ÇEÈ‹EÈ;Eì…‹UÈ‹EÌÐ;Eìó‹EÈH˜HÅH‹EàHÐH‹HÅH‹EÐL‹EÌ‹EÈ‹Eì9ÂNÂH˜HÅH‹EàHÐH‹HÅH‹EØH ‹UÈ‹EÌÐH˜HÅH‹EàHÐH‹HÅH‹EØH‹uÈ‹EÌðH˜H4ÅH‹EàHðH‹H4ÅH‹EØHÆ‹EÈH˜H<ÅH‹EàHøH‹H<ÅH‹EØHø‹}ìHƒìSA‰ùH‰ÇèžHƒÄëtH‹EàH‰…pÿÿÿH‹EØH‰…xÿÿÿH‹EÐH‰E€‹Eì‰Eˆ‹EȉEŒH…pÿÿÿ¹ºH‰ÆH=F¢ÿÿè>©H‹…pÿÿÿH‰EàH‹…xÿÿÿH‰EØH‹E€H‰EЋEˆ‰Eì‹EŒ‰EÈ‹EÌÀEÈéoþÿÿH‹EØH‰E°H‹EÐH‰EØH‹E°H‰EÐÑeÌé<þÿÿH‹EØH;…(ÿÿÿtSH‹…(ÿÿÿH‰EH‹EÀH‰E˜H‹EØH‰E HE¹ºH‰ÆH=¢ÿÿ袨H‹EH‰…(ÿÿÿH‹E˜H‰EÀH‹E H‰EØHƒ}àt H‹EàH‰ÇèD¨Hƒ}¸t H‹E¸H‰Çè1¨Heð[A\]ÃUH‰åH‰}øH‰uðH‹EðH‹H‹EøH‰]ÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì0H‰}؉uÔ‰UÐHUè‹EÐH‰Ö‰ÇèI§ÇEø‹EÔ‰Eðƒ}ð~ Ñ}ðƒEøëñ‹Eøƒèº‰ÁÓâ‰Ð‰EüÇEô‹Eô;Eø}J‹Eè3Eü‰Eì‹Eè;Eì}‹MЋUìH‹EؾeH‰Çèl›ë‹MЋUìH‹EؾdH‰ÇèS›Ñ}üƒEôë®ÉÃUH‰åHƒì0H‰}؉uÔ‰UÐHUè‹EÐH‰Ö‰Ç蚦ÇEø‹EÔ‰Eðƒ}ð~ Ñ}ðƒEøëñ‹Eøƒèº‰ÁÓâ‰Ð‰EüÇEô‹Eô;Eø}J‹Eè3Eü‰Eì‹Eè;Eì~‹MЋUìH‹EؾeH‰Ç轚ë‹MЋUìH‹EؾdH‰Ç褚Ñ}üƒEôë®ÉÃUH‰åSHì¨H‰½hÿÿÿ‰µdÿÿÿ‰•`ÿÿÿ‰\ÿÿÿH‹…hÿÿÿH‰Çèî‰EœÇE˜HU”‹…\ÿÿÿH‰Ö‰Çè¼¥Hu˜‹•`ÿÿÿHEœHM Q‹\ÿÿÿQj‹`ÿÿÿQA¹I‰ð¹¾H‰Çè,šHƒÄ HE¿H‰ÇèÄH‹…hÿÿÿ¾H‰ÇèðŸH‰Ç‹E˜HcðHU¿H…pÿÿÿH‰ÑH‰úH‰ÇèŸHE¿H‰ÇèŽHÇEèHÇEàH‹…hÿÿÿH‰Çè-ƒð„Àt#H‹…hÿÿÿH‰ÇècŸH‰EÀHEÀH‰ÇèeœH‰EèH…pÿÿÿH‰Çèôœƒð„Àt#H…pÿÿÿH‰Çè*ŸH‰EÐHEÐH‰Çè,œH‰EàD‹E˜‹uœH‹}à‹•`ÿÿÿH‹EèHM Q‹\ÿÿÿQj‹`ÿÿÿQE‰ÁI‰ø¹H‰Çè™HƒÄ ‹•dÿÿÿHpÿÿÿH‹…hÿÿÿH‰ÎH‰ÇèΘH…pÿÿÿH‰Çè­žH…pÿÿÿH‰Ç輞ë7H‰ÃHE¿H‰Çè}œH‰ØH‰Çèô¤H‰ÃH…pÿÿÿH‰Ç莞H‰ØH‰ÇèפH‹]øÉÃUH‰åHƒìH‰}øH‹EøH‰ÇèœH‹EøHÇH‹EøHÇ@H‹EøHÇ@ÉÃUH‰åHƒìH‰}øH‹EøH‰Çè™ÉÃUH‰åHƒì H‰}øH‰uðH‰UèHƒ}ðtH‹EøH‹UèH‹MðH‰ÎH‰ÇèÄœÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çèð›ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‰Çè\œH‰ÁH‹EøH‹H‹UèH‹uðH‰Çèž H‰ÂH‹EøH‰PÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰Çè7¢H‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰Ç謡H‹EèH‹UÈH‰ÆH}è š„ÀtH‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰EøëœH‹EðH‰Eøë’H‹EØÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰Ç蘡H‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰Çè ¡H‹UèH‹EÈH‰ÆH}ès™„Àt H‹EðH‰Eøë±H‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰Eøë’H‹EØÉÃUH‰åH‰}ø]ÃUH‰åH‰}ø]ÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EèH‹UØH‰ÖH‰ÇèhšH‹UàH‹EèH‰ÖH‰ÇègšëH‰ÃH‹EèH‰ÇèJšH‰ØH‰Çè%¢HƒÄ([]ÃUH‰åHƒìH‰}øH‰uðH‹EøH‰Çè.šH‰ÂH‹EøH‹H‹MðH‰ÎH‰ÇèŸH‰ÂH‹EøH‰PÉÃUH‰åHƒì H‰}øH‰uðH‰UèHƒ}ðtH‹EøH‹UèH‹MðH‰ÎH‰ÇèRšÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèZ™ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EèH‰ÇèŸH‰ÂH‹MðH‹EøH‰ÎH‰ÇèÕ™ÉÃUH‰åSHƒì8H‰}ÈH‰uÀH‹EÈHð¤¾H‰Çè˜H‰EàH‹EÈH‹UàH‰ÖH‰Çèê˜H‰EØH‹EØH‰EèH‹EÀH‰Çè,ŸH‰ÃH‹EÈH‰Çèå—HÁàH‰ÂH‹EØH H‹EÈH‰ÚH‰ÎH‰Çèb™HÇEèH‹EÈH‰Ç覘H‰ÁH‹EÈH‹pH‹EÈH‹H‹UØH‰Çè¼H‰EèHƒEèH‹EÈH‰Çès˜H‰ÂH‹EÈH‹HH‹EÈH‹H‰ÎH‰Çè°žH‹EÈH‹@H‰ÂH‹EÈH‹H)ÂH‰ÐHÁøH‰ÂH‹EÈH‹H‹EÈH‰ÎH‰Çè˜H‹EÈH‹UØH‰H‹EÈH‹UèH‰PH‹EàHÁàH‰ÂH‹EØHÂH‹EÈH‰Pé‹H‰Ç諟Hƒ}èu+H‹EÈH‰ÇèÚ–HÁàH‰ÂH‹EØHÂH‹EÈH‰ÖH‰ÇèI˜ë"H‹EÈH‰Ç襗H‰ÂH‹MèH‹EØH‰ÎH‰ÇèéH‹EÈH‹UàH‹MØH‰ÎH‰Çèr—èGŸH‰Ãè9ŸH‰ØH‰ÇèdŸHƒÄ8[]ÃUH‰åSHƒìH‰}èH‰uàHƒìPè^•HƒÄH‹UàH‹EèHƒìSH‰ÖH‰Çè HƒÄH‹]øÉÃUH‰åH‰}øH‹EøÇ@H‹EøHÇ]ÃUH‰åAUATSHì¨H‰½hþÿÿH‰µ`þÿÿH‰•XþÿÿH‰PþÿÿL‰…HþÿÿD‰DþÿÿH‹•`þÿÿH‹…hþÿÿH)ÂH‰ÐHÁøH‰EØH‹•PþÿÿH‹…XþÿÿH)ÂH‰ÐHÁøH‰EÐHƒ}Øu Hƒ}Є·Hƒ}Øt Hƒ}Ð…µHƒ}Øu H‹…XþÿÿëH‹…hþÿÿH‰E Hƒ}ØuH‹EÐëH‹EØH‰E˜H‹…HþÿÿH‰…pþÿÿH‹E H‰…xþÿÿH‹E˜H‰…€þÿÿ‹…Dþÿÿ‰…ˆþÿÿH…pþÿÿ¹ºH‰ÆH=,mÿÿè¿H‹…pþÿÿH‰…HþÿÿH‹…xþÿÿH‰E H‹…€þÿÿH‰E˜‹…ˆþÿÿ‰…DþÿÿéñÇEÌ ‹…Dþÿÿ¯EÌÀHcØH¸ðH9Ãw H‰ØHÁàHƒÀëHÇÀÿÿÿÿH‰Çè%I‰ÄI‰$ID$HSÿH‰ÓI‰ÅHƒûÿtL‰ïè×IƒÅHƒëëèID$H‰EÀ‹…Dþÿÿ¯EÌÀH˜HºàH9Ї>HÁàH‰ÇèÁœH‰E¸H‹…hþÿÿH‰…þÿÿH‹…`þÿÿH‰…˜þÿÿH‹…XþÿÿH‰… þÿÿH‹…PþÿÿH‰…¨þÿÿH‹EØH‰…°þÿÿH‹EÐH‰…¸þÿÿH‹EÀH‰…ÀþÿÿH‹E¸H‰…Èþÿÿ‹…Dþÿÿ‰…Ðþÿÿ‹Ẻ…ÔþÿÿH…þÿÿ¹ºH‰ÆH=¾lÿÿèHœH‹…þÿÿH‰…hþÿÿH‹…˜þÿÿH‰…`þÿÿH‹… þÿÿH‰…XþÿÿH‹…¨þÿÿH‰…PþÿÿH‹…°þÿÿH‰EØH‹…¸þÿÿH‰EÐH‹…ÀþÿÿH‰EÀH‹…ÈþÿÿH‰E¸‹…Ðþÿÿ‰…Dþÿÿ‹…Ôþÿÿ‰EÌ‹…DþÿÿƒÀH˜HºàH9Ðw7ë訛HÁàH‰Çè~›H‰E°‹…DþÿÿƒÀH˜HºàH9Ї°ëès›HÁàH‰ÇèI›H‰E¨H‹E°HÇH‹E¨HÇ‹…DþÿÿH˜HÅH‹E°HÂH‹EØH‰‹…DþÿÿH˜HÅH‹E¨HÂH‹EÐH‰H‹…hþÿÿH‰…àþÿÿH‹…`þÿÿH‰…èþÿÿH‹…XþÿÿH‰…ðþÿÿH‹…PþÿÿH‰…øþÿÿH‹EØH‰…ÿÿÿH‹EÐH‰…ÿÿÿH‹EÀH‰…ÿÿÿH‹E¸H‰…ÿÿÿH‹E°H‰… ÿÿÿH‹E¨H‰…(ÿÿÿ‹…Dþÿÿ‰…0ÿÿÿ‹Ẻ…4ÿÿÿH…àþÿÿ¹ºH‰ÆH=\mÿÿèhšH‹…àþÿÿH‰…hþÿÿH‹…èþÿÿH‰…`þÿÿH‹…ðþÿÿH‰…XþÿÿH‹…øþÿÿH‰…PþÿÿH‹…ÿÿÿH‰EØH‹…ÿÿÿH‰EÐH‹…ÿÿÿH‰EÀH‹…ÿÿÿH‰E¸H‹… ÿÿÿH‰E°H‹…(ÿÿÿH‰E¨‹…0ÿÿÿ‰…Dþÿÿ‹…4ÿÿÿ‰EÌHƒ}ÀtEëèÅ™H‹EÀHƒèH‹HÁàH‰ÂH‹EÀHH;]ÀtHƒëH‰ßèWëìH‹EÀHƒèH‰Çè]™Hƒ}¸t H‹E¸H‰ÇèJ™H‹…hþÿÿH‰…@ÿÿÿH‹…XþÿÿH‰…HÿÿÿH‹…HþÿÿH‰…PÿÿÿH‹E°H‰…XÿÿÿH‹E¨H‰…`ÿÿÿ‹…Dþÿÿ‰…hÿÿÿH…@ÿÿÿ¹ºH‰ÆH=åoÿÿè™H‹…@ÿÿÿH‰…hþÿÿH‹…HÿÿÿH‰…XþÿÿH‹…PÿÿÿH‰…HþÿÿH‹…XÿÿÿH‰E°H‹…`ÿÿÿH‰E¨‹…hÿÿÿ‰…DþÿÿHƒ}°t H‹E°H‰Çè˜Hƒ}¨tH‹E¨H‰Çèl˜ëHĨ[A\A]]ÃUH‰åH‰}øH‰uðH‹EøH;EðtH‹Eð‹PH‹Eø‰PH‹EðH‹H‹EøH‰H‹Eø]ÃUH‰åATSHìÐH‰½(ÿÿÿH‰µ ÿÿÿè7˜‰EìH‹• ÿÿÿH‹…(ÿÿÿH)ÂH‰ÐHÁøH‰EÀ‹EìÀH˜H;EÀƒ}ìu(H‹• ÿÿÿH‹…(ÿÿÿHƒìATH‰ÖH‰Çèš•HƒÄé‹EìƒÀH˜HºàH9ЇHÁàH‰Ç茗H‰Eà‹EìH˜HÅH‹EàHÂH‹EÀH‰H‹EÀH‰…0ÿÿÿH‹EàH‰…8ÿÿÿ‹E쉅@ÿÿÿH…0ÿÿÿ¹ºH‰ÆH=!rÿÿèR—H‹…0ÿÿÿH‰EÀH‹…8ÿÿÿH‰Eà‹…@ÿÿÿ‰EìH‹…(ÿÿÿH‰…PÿÿÿH‹EàH‰…Xÿÿÿ‹E쉅`ÿÿÿH…Pÿÿÿ¹ºH‰ÆH=crÿÿèñ–H‹…PÿÿÿH‰…(ÿÿÿH‹…XÿÿÿH‰Eà‹…`ÿÿÿ‰EìH‹EÀHºàH9Ðw3ë诖HÁàH‰Çè…–H‰E¸H‹…(ÿÿÿH‰EØH‹E¸H‰EÐÇEÌëè~–‹EÌ;Eì¸ÇEÈ‹EÈ;Eì…‹UÈ‹EÌÐ;Eìó‹EÈH˜HÅH‹EàHÐH‹HÅH‹EÐL‹EÌ‹EÈ‹Eì9ÂNÂH˜HÅH‹EàHÐH‹HÅH‹EØH ‹UÈ‹EÌÐH˜HÅH‹EàHÐH‹HÅH‹EØH‹uÈ‹EÌðH˜H4ÅH‹EàHðH‹H4ÅH‹EØHÆ‹EÈH˜H<ÅH‹EàHøH‹H<ÅH‹EØHø‹}ìHƒìSA‰ùH‰Çèå‰HƒÄëtH‹EàH‰…pÿÿÿH‹EØH‰…xÿÿÿH‹EÐH‰E€‹Eì‰Eˆ‹EȉEŒH…pÿÿÿ¹ºH‰ÆH=sqÿÿè•H‹…pÿÿÿH‰EàH‹…xÿÿÿH‰EØH‹E€H‰EЋEˆ‰Eì‹EŒ‰EÈ‹EÌÀEÈéoþÿÿH‹EØH‰E°H‹EÐH‰EØH‹E°H‰EÐÑeÌé<þÿÿH‹EØH;…(ÿÿÿtSH‹…(ÿÿÿH‰EH‹EÀH‰E˜H‹EØH‰E HE¹ºH‰ÆH=¼qÿÿè{”H‹EH‰…(ÿÿÿH‹E˜H‰EÀH‹E H‰EØHƒ}àt H‹EàH‰Çè”Hƒ}¸t H‹E¸H‰Çè ”Heð[A\]ÃUH‰åH‰}øH‰uðH‹EðH‹H‹EøH‰]ÃUH‰åH‰}øH‹Eø]ÃUH‰åH‰}ø]ÃUH‰åSHƒì(H‰}ØH‰uÐHEØH‰Çè H‹EØH‹UÐHƒìSH‰ÖH‰Çè²HƒÄH‹]øÉÃUH‰åSHƒì(H‰}ØH‰uÐH‹EÐH‰EèH‹EØH‰ÇèÃH‹UèH‹EØHƒìSH‰ÖH‰Çè?’HƒÄH‹]øÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰ÇèGŠÉÃUH‰åH‰}ø]ÃUH‰åSHƒì(H‰}ØH‰uÐHEØH‰Çè\H‹EØH‹UÐHƒìSH‰ÖH‰ÇèHƒÄH‹]øÉÃUH‰åSHƒì(H‰}ØH‰uÐH‹EÐH‰EèH‹EØH‰ÇèH‹UèH‹EØHƒìSH‰ÖH‰Çè‘‘HƒÄH‹]øÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Ç虉ÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì H‰}èH‰uàH‹EèH‰ÇèH‹H‰EøH‹EàH‰ÇèðH‹H‹EèH‰HEøH‰ÇèÚH‹H‹EàH‰ÉÃUH‰åHƒì H‰}èH‰uàH‹UàH‹EèHƒìQH‰ÖH‰ÇèŽHƒÄÉÃUH‰åH‰}øH‰uð]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EðH‰Ç蜑ÉÃUH‰åHƒì H‰}èH‰uàÇEüHUüH‹MàH‹EèH‰ÎH‰ÇèµÉÃUH‰åHƒìH‰}øH‹Eø¾H‰ÇèVŠÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‰Çèæ‡H;Eð’À„ÀtèôH‹EðHÁàH‰Çè‘ÉÃUH‰åH‰}øH‰uðH‹EøH‹UðH‰]ÃUH‰åHƒì0H‰}àH‰uÐH‰UØÆEÿH‹UØH‹MÐH‹EàH‰ÎH‰Çèñ‰ÉÃUH‰åSHƒìHEïHƒìRH‰Çè†HƒÄ‰ØH‹]øÉÃUH‰åHƒìH‰}øH‰uðH‹EøH;EðtaH‹UðH‹EøH)ÂH‰ÐHÁøH‰ÇèåH4H‹MðH‹EøHƒì¶URH‰òH‰ÎH‰Çè`ŒHƒÄH‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèÐŒHƒÄÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØHƒìPè†HƒÄH‹UØH‹MàH‹EèHƒìSH‰ÎH‰ÇèL‹HƒÄH‹]øÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØHƒìPè×…HƒÄH‹UØH‹MàH‹EèHƒìSH‰ÎH‰Çè‹HƒÄH‹]øÉÃUH‰åSHƒìHH‰}ØH‰uÐH‰UÈH‰MÀL‰E¸èÙ…H‹}¸H‹MÀH‹UÈH‹uÐH‹EØHƒìSI‰øH‰Çè•HƒÄH‹]øÉÃUH‰åSHƒìH‰}èH‰uàHƒìPè„…HƒÄH‹UàH‹EèHƒìSH‰ÖH‰Çè0HƒÄH‹]øÉÃUH‰åHìH‰½˜þÿÿH‰µþÿÿH‰•ˆþÿÿH‰€þÿÿL‰…xþÿÿD‰tþÿÿH‹•þÿÿH‹…˜þÿÿH)ÂH‰ÐHÁøH‰EøH‹•€þÿÿH‹…ˆþÿÿH)ÂH‰ÐHÁøH‰EðHƒ}øu Hƒ}ð„0Hƒ}øt Hƒ}ð…µHƒ}øu H‹…ˆþÿÿëH‹…˜þÿÿH‰EÀHƒ}øuH‹EðëH‹EøH‰E¸H‹…xþÿÿH‰… þÿÿH‹EÀH‰…¨þÿÿH‹E¸H‰…°þÿÿ‹…tþÿÿ‰…¸þÿÿH… þÿÿ¹ºH‰ÆH=ˆÿÿèŽH‹… þÿÿH‰…xþÿÿH‹…¨þÿÿH‰EÀH‹…°þÿÿH‰E¸‹…¸þÿÿ‰…tþÿÿéjÇEì ‹…tþÿÿ¯EìÀH˜HºàH9Ðw3HÁàH‰ÇèwH‰Eà‹…tþÿÿ¯EìÀH˜HºàH9ЇEëèiHÁàH‰Çè?H‰EØH‹…˜þÿÿH‰…ÀþÿÿH‹…þÿÿH‰…ÈþÿÿH‹…ˆþÿÿH‰…ÐþÿÿH‹…€þÿÿH‰…ØþÿÿH‹EøH‰…àþÿÿH‹EðH‰…èþÿÿH‹EàH‰…ðþÿÿH‹EØH‰…øþÿÿ‹…tþÿÿ‰…ÿÿÿ‹E쉅ÿÿÿH…Àþÿÿ¹ºH‰ÆH=_ˆÿÿèÆŒH‹…ÀþÿÿH‰…˜þÿÿH‹…ÈþÿÿH‰…þÿÿH‹…ÐþÿÿH‰…ˆþÿÿH‹…ØþÿÿH‰…€þÿÿH‹…àþÿÿH‰EøH‹…èþÿÿH‰EðH‹…ðþÿÿH‰EàH‹…øþÿÿH‰EØ‹…ÿÿÿ‰…tþÿÿ‹…ÿÿÿ‰Eì‹…tþÿÿƒÀH˜HºàH9Ðw7ëè&ŒHÁàH‰Çèü‹H‰EЋ…tþÿÿƒÀH˜HºàH9Ї°ëèñ‹HÁàH‰ÇèÇ‹H‰EÈH‹EÐHÇH‹EÈHÇ‹…tþÿÿH˜HÅH‹EÐHÂH‹EøH‰‹…tþÿÿH˜HÅH‹EÈHÂH‹EðH‰H‹…˜þÿÿH‰…ÿÿÿH‹…þÿÿH‰…ÿÿÿH‹…ˆþÿÿH‰… ÿÿÿH‹…€þÿÿH‰…(ÿÿÿH‹EøH‰…0ÿÿÿH‹EðH‰…8ÿÿÿH‹EàH‰…@ÿÿÿH‹EØH‰…HÿÿÿH‹EÐH‰…PÿÿÿH‹EÈH‰…Xÿÿÿ‹…tþÿÿ‰…`ÿÿÿ‹E쉅dÿÿÿH…ÿÿÿ¹ºH‰ÆH=ùˆÿÿèæŠH‹…ÿÿÿH‰…˜þÿÿH‹…ÿÿÿH‰…þÿÿH‹… ÿÿÿH‰…ˆþÿÿH‹…(ÿÿÿH‰…€þÿÿH‹…0ÿÿÿH‰EøH‹…8ÿÿÿH‰EðH‹…@ÿÿÿH‰EàH‹…HÿÿÿH‰EØH‹…PÿÿÿH‰EÐH‹…XÿÿÿH‰EÈ‹…`ÿÿÿ‰…tþÿÿ‹…dÿÿÿ‰EìHƒ}àtëèCŠH‹EàH‰Çè ŠHƒ}Øt H‹EØH‰Çèú‰H‹…˜þÿÿH‰…pÿÿÿH‹…ˆþÿÿH‰…xÿÿÿH‹…xþÿÿH‰E€H‹EÐH‰EˆH‹EÈH‰E‹…tþÿÿ‰E˜H…pÿÿÿ¹ºH‰ÆH=¦‹ÿÿèʉH‹…pÿÿÿH‰…˜þÿÿH‹…xÿÿÿH‰…ˆþÿÿH‹E€H‰…xþÿÿH‹EˆH‰EÐH‹EH‰EÈ‹E˜‰…tþÿÿHƒ}Ðt H‹EÐH‰ÇèG‰Hƒ}ÈtH‹EÈH‰Çè4‰ëÉÃUH‰åSHƒì(H‰}è‰uä‰Uà‰MÜL‰EÐD‰MØè0}‰Ãè)}A‰ÂL‹EЋ}Ü‹Uà‹uäH‹Eèÿu(‹M Q‹MQ‹MQS‹MØQM‰ÁA‰ø‰ÑD‰ÒH‰ÇèvˆHƒÄ0H‹]øÉÃUH‰åATSHìÐH‰½8ÿÿÿH‰µ0ÿÿÿ‰•,ÿÿÿH‹…8ÿÿÿH‰Ç耶ÀH…ÀtH µŒº( H5}‹H=<‹èÁˆH‹…0ÿÿÿH‰Çèè¶ÀH…ÀtH Œº) H5G‹H=‹è‹ˆH‹…8ÿÿÿ¾H‰Çè#‚‹H‹…0ÿÿÿ¾H‰Çè ‚‹9Ã~H‹…8ÿÿÿ¾H‰Çèó‹ëH‹…0ÿÿÿ¾H‰ÇèÛ‹‰EÔH‹…8ÿÿÿH‰ÇèEHPÿH‹…8ÿÿÿH‰ÖH‰Ç豋H‹…0ÿÿÿH‰ÇèHPÿH‹…0ÿÿÿH‰ÖH‰Ç芋9Ã})H‹…8ÿÿÿH‰Çèó~HPÿH‹…8ÿÿÿH‰ÖH‰Çè_‹ë'H‹…0ÿÿÿH‰ÇèÊ~HPÿH‹…0ÿÿÿH‰ÖH‰Çè6‹‰EÐH‹…8ÿÿÿH‰Çè ~H‰ÃH‹…0ÿÿÿH‰ÇèŽ~HØH‰EÈH…_ÿÿÿH‰Çèº~H•_ÿÿÿH‹MÈH…@ÿÿÿH‰ÎH‰ÇèË€H…_ÿÿÿH‰Çèš~ÇEìÇEèHÇEàH‹EàH;Eȃè‹]ìH‹…8ÿÿÿH‰Çè~H9ÃsP‹]èH‹…0ÿÿÿH‰Çèÿ}H9Ãs2‹UìH‹…8ÿÿÿH‰ÖH‰Çèg€‹‹UèH‹…0ÿÿÿH‰ÖH‰ÇèP€‹9ø븄Àt8H‹UàH…@ÿÿÿH‰ÖH‰Çè$€H‰Ã‹UìH‹…8ÿÿÿH‰ÖH‰Çè €‹‰ƒEìë6H‹UàH…@ÿÿÿH‰ÖH‰ÇèìH‰Ã‹UèH‹…0ÿÿÿH‰ÖH‰ÇèÔ‹‰ƒEèHƒEàé ÿÿÿH‹…8ÿÿÿH‰ÇèH‹…0ÿÿÿH‰Ç考½,ÿÿÿe…ÇEÜ‹EÜHcÐH…@ÿÿÿH‰ÖH‰Çèx‹;EÔ|‹EÜH˜H‹UÈHÑêH9Ðs&‹EÜHcÐH…@ÿÿÿH‰ÖH‰ÇèH‹;Eи븄ÀtƒEÜ뛃}Ü„¨‹EÜHcØH…@ÿÿÿH‰Çèâ~H‰…`ÿÿÿH…`ÿÿÿH‰ÞH‰Çèá{I‰ÄH…@ÿÿÿH‰Çè·~H‰ÃH‹…8ÿÿÿH‰Çè™~H‰E€HU€H…pÿÿÿH‰ÖH‰ÇèçyH‹µpÿÿÿH‹…8ÿÿÿL‰áH‰ÚH‰Çè{~éH‹Eȃè‰EØ‹EØH˜H‹UÈHÑêH9Ðr‹EØHcÐH…@ÿÿÿH‰ÖH‰Çèa~‹;EÔ}‹EØHcÐH…@ÿÿÿH‰ÖH‰ÇèB~‹;EÐ~¸ë¸„ÀtƒmØ뛋EØH˜H‹UÈHƒêH9Ѓ–H‹]ÈH…@ÿÿÿH‰ÇèÒ}H‰EHEH‰ÞH‰Çè×zI‰Ä‹E؃ÀHcØH…@ÿÿÿH‰Çè¤}H‰E HE H‰ÞH‰Çè©zH‰ÃH‹…8ÿÿÿH‰Çè}H‰EÀHUÀHE°H‰ÖH‰ÇèÄxH‹u°H‹…8ÿÿÿL‰áH‰ÚH‰Çè[}H…@ÿÿÿH‰ÇèF}H…@ÿÿÿH‰ÇèU}ë:H‰ÃH…_ÿÿÿH‰Çè{H‰ØH‰Ç芃H‰ÃH…@ÿÿÿH‰Çè$}H‰ØH‰ÇèmƒHÄÐ[A\]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèÌwÉÃUH‰åH‰}ø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹MðH‹EøH‰ÎH‰ÇèzwÉÃUH‰åHƒìH‰}øH‰uðH‹EøH;EðtH‹EøH‰Çèë|H‰Çè'HƒEøëÛÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‰MàH‹UèH‹MðH‹EøH‰ÎH‰ÇèÌ~ÉÃUH‰åH‰}øH‰uðH‰UèH‹Eð‹H‹Eè‹9œÀ]ÃUH‰åH‰}øH‰uðH‰UèH‹Eð‹H‹Eè‹9œÀ]ÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çè—yH‹EøHÇH‹EøHÇ@H‹EøHÇ@ÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèûyH‰ÂH‹EøH‰H‹EøH‹H‹EøH‰PH‹EøH‹H‹UðHÁâHÂH‹EøH‰PÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UðH‹EøH‰ÖH‰Çèl~ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹MðH‹EøH‰ÎH‰ÇèóuÉÃUH‰åH‰}øH‰uð]ÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèÉuÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EèH‰ÇèHH‰ÂH‹MðH‹EøH‰ÎH‰ÇèruÉÃUH‰åSHƒì8H‰}ØH‰uÐH‰UÈH‹EØH‰ÇèçwH‰ÃH‹EØH‰ÇèÆwH)ÃH‰ÚH‹EÐH9Â’À„Àt H‹EÈH‰Çè@€H‹EØH‰ÇèšwH‰ÃH‹EØH‰Çè‹wH‰EàHUÐHEàH‰ÖH‰Çèª}H‹HØH‰EèH‹EØH‰Çè^wH;EèwH‹EØH‰Çè^wH;EèsH‹EØH‰ÇèLwëH‹EèHƒÄ8[]ÃUH‰åHƒìH‰}øH‰uðHƒ}ðtH‹EøH‹UðH‰ÖH‰Çè”xë¸ÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‰MÐH‹EàH‰Çèò|H‰ÃH‹EèH‰Çèã|H‰ÇH‹UÐH‹EØH‰ÑH‰ÂH‰Þè:|HƒÄ([]ÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèöwÉÃUH‰åSHƒìHEïHƒìRH‰ÇèåtHƒÄ‰ØH‹]øÉÃUH‰åHƒìH‰}øH‰uðH‹EøH;EðtaH‹UðH‹EøH)ÂH‰ÐHÁøH‰Çèa|H4H‹MðH‹EøHƒì¶URH‰òH‰ÎH‰ÇèÊzHƒÄH‹UðH‹EøHƒì¶MQH‰ÖH‰Çè:{HƒÄÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØHƒìPèrtHƒÄH‹UØH‹MàH‹EèHƒìSH‰ÎH‰Çè¶yHƒÄH‹]øÉÃUH‰åH‰}øH‰uðH‹Eð‹PH‹Eø‰PH‹EðH‹H‹EøH‰]ÃUH‰åSHƒìHH‰}ØH‰uÐH‰UÈH‰MÀL‰E¸èstH‹}¸H‹MÀH‹UÈH‹uÐH‹EØHƒìSI‰øH‰Çè|HƒÄH‹]øÉÃUH‰åSHƒìH‰}èH‰uàHƒìPètHƒÄH‹UàH‹EèHƒìSH‰ÖH‰ÇèÄ{HƒÄH‹]øÉÃUH‰åHìH‰½˜þÿÿH‰µþÿÿH‰•ˆþÿÿH‰€þÿÿL‰…xþÿÿD‰tþÿÿH‹•þÿÿH‹…˜þÿÿH)ÂH‰ÐHÁøH‰EøH‹•€þÿÿH‹…ˆþÿÿH)ÂH‰ÐHÁøH‰EðHƒ}øu Hƒ}ð„0Hƒ}øt Hƒ}ð…µHƒ}øu H‹…ˆþÿÿëH‹…˜þÿÿH‰EÀHƒ}øuH‹EðëH‹EøH‰E¸H‹…xþÿÿH‰… þÿÿH‹EÀH‰…¨þÿÿH‹E¸H‰…°þÿÿ‹…tþÿÿ‰…¸þÿÿH… þÿÿ¹ºH‰ÆH={Zÿÿèš|H‹… þÿÿH‰…xþÿÿH‹…¨þÿÿH‰EÀH‹…°þÿÿH‰E¸‹…¸þÿÿ‰…tþÿÿéjÇEì ‹…tþÿÿ¯EìÀH˜HºàH9Ðw3HÁàH‰Çè|H‰Eà‹…tþÿÿ¯EìÀH˜HºàH9ЇEëè|HÁàH‰ÇèÙ{H‰EØH‹…˜þÿÿH‰…ÀþÿÿH‹…þÿÿH‰…ÈþÿÿH‹…ˆþÿÿH‰…ÐþÿÿH‹…€þÿÿH‰…ØþÿÿH‹EøH‰…àþÿÿH‹EðH‰…èþÿÿH‹EàH‰…ðþÿÿH‹EØH‰…øþÿÿ‹…tþÿÿ‰…ÿÿÿ‹E쉅ÿÿÿH…Àþÿÿ¹ºH‰ÆH=MZÿÿè`{H‹…ÀþÿÿH‰…˜þÿÿH‹…ÈþÿÿH‰…þÿÿH‹…ÐþÿÿH‰…ˆþÿÿH‹…ØþÿÿH‰…€þÿÿH‹…àþÿÿH‰EøH‹…èþÿÿH‰EðH‹…ðþÿÿH‰EàH‹…øþÿÿH‰EØ‹…ÿÿÿ‰…tþÿÿ‹…ÿÿÿ‰Eì‹…tþÿÿƒÀH˜HºàH9Ðw7ëèÀzHÁàH‰Çè–zH‰EЋ…tþÿÿƒÀH˜HºàH9Ї°ëè‹zHÁàH‰ÇèazH‰EÈH‹EÐHÇH‹EÈHÇ‹…tþÿÿH˜HÅH‹EÐHÂH‹EøH‰‹…tþÿÿH˜HÅH‹EÈHÂH‹EðH‰H‹…˜þÿÿH‰…ÿÿÿH‹…þÿÿH‰…ÿÿÿH‹…ˆþÿÿH‰… ÿÿÿH‹…€þÿÿH‰…(ÿÿÿH‹EøH‰…0ÿÿÿH‹EðH‰…8ÿÿÿH‹EàH‰…@ÿÿÿH‹EØH‰…HÿÿÿH‹EÐH‰…PÿÿÿH‹EÈH‰…Xÿÿÿ‹…tþÿÿ‰…`ÿÿÿ‹E쉅dÿÿÿH…ÿÿÿ¹ºH‰ÆH=çZÿÿè€yH‹…ÿÿÿH‰…˜þÿÿH‹…ÿÿÿH‰…þÿÿH‹… ÿÿÿH‰…ˆþÿÿH‹…(ÿÿÿH‰…€þÿÿH‹…0ÿÿÿH‰EøH‹…8ÿÿÿH‰EðH‹…@ÿÿÿH‰EàH‹…HÿÿÿH‰EØH‹…PÿÿÿH‰EÐH‹…XÿÿÿH‰EÈ‹…`ÿÿÿ‰…tþÿÿ‹…dÿÿÿ‰EìHƒ}àtëèÝxH‹EàH‰Çè§xHƒ}Øt H‹EØH‰Çè”xH‹…˜þÿÿH‰…pÿÿÿH‹…ˆþÿÿH‰…xÿÿÿH‹…xþÿÿH‰E€H‹EÐH‰EˆH‹EÈH‰E‹…tþÿÿ‰E˜H…pÿÿÿ¹ºH‰ÆH=”]ÿÿèdxH‹…pÿÿÿH‰…˜þÿÿH‹…xÿÿÿH‰…ˆþÿÿH‹E€H‰…xþÿÿH‹EˆH‰EÐH‹EH‰EÈ‹E˜‰…tþÿÿHƒ}Ðt H‹EÐH‰ÇèáwHƒ}ÈtH‹EÈH‰ÇèÎwëÉÃUH‰åH‰}ø]ÃUH‰åH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHÁø]ÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÂH‹EøH‰]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹EðH‰ÖH‰ÇènÉÃUH‰åH‰}ø]ÃUH‰åH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHÁø]ÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÂH‹EøH‰]ÃUH‰åH‰}øH‰uðH‰UèH‹Eð‹H‹Eè‹9œÀ]ÃUH‰åH‰}øH‰uð]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‰ÇèÈqH‰ÁH‹UèH‹EðH‰ÆH‰ÏèÎpÉÃUH‰åHƒìH‰}ø‰uôH‹EøH‰ÇèEmÉÃUH‰åH‰}øH¸ÿÿÿÿÿÿÿ?]ÃUH‰åHƒì H‰}ðH‰uàH‰UèH‹UèH‹MàH‹EðH‰ÎH‰ÇèósÉÃUH‰åH‰}ø]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHƒøCއHƒ}Øu&H‹UàH‹MàH‹EèHƒì¶uVH‰ÎH‰ÇèÐqHƒÄëZHƒmØH‹UàH‹EèHƒì¶MQH‰ÖH‰ÇèsHƒÄH‰EøH‹UØH‹MàH‹EøHƒì¶uVH‰ÎH‰Çè^ÿÿÿHƒÄH‹EøH‰EàéaÿÿÿÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHƒøC~JH‹EøHH@H‹EøHƒì¶URH‰ÎH‰ÇèHqHƒÄH‹EøHH@H‹EðHƒì¶URH‰ÆH‰ÏèVrHƒÄë H‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèqHƒÄÉÃUH‰åSHƒìHEïHƒìRH‰ÇèEjHƒÄ‰ØH‹]øÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰ÇèrsH‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰ÇèçrH‹EèH‹UÈH‰ÆH}èÓi„ÀtH‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰EøëœH‹EðH‰Eøë’H‹EØÉÃUH‰åSHƒìHEïHƒìRH‰Çè’iHƒÄ‰ØH‹]øÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰Çè³rH‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰Çè(rH‹EèH‹UÈH‰ÆH}è i„ÀtH‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰EøëœH‹EðH‰Eøë’H‹EØÉÃUH‰åHƒì0H‰}øH‰uðH‰UèH‰MàL‰EØH‹EøH;EðtMH‹EèH;EàtCH‹UøH‹EèH‰ÆH}è)j„ÀtH‹Eè‹H‹E؉HƒEèëH‹Eø‹H‹E؉HƒEøHƒEØë©H‹UØH‹MðH‹EøH‰ÎH‰Çè…pH‰ÂH‹MàH‹EèH‰ÎH‰ÇèopÉÃUH‰åSHƒìHEïHƒìRH‰Çè‹hHƒÄ‰ØH‹]øÉÃUH‰åHƒìH‰}øH‰uðH‹EøH;EðtaH‹UðH‹EøH)ÂH‰ÐHÁøH‰ÇèïoH4H‹MðH‹EøHƒì¶URH‰òH‰ÎH‰ÇèdnHƒÄH‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèÔnHƒÄÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØHƒìPèhHƒÄH‹UØH‹MàH‹EèHƒìSH‰ÎH‰ÇèPmHƒÄH‹]øÉÃUH‰åSHƒìHH‰}ØH‰uÐH‰UÈH‰MÀL‰E¸è,hH‹}¸H‹MÀH‹UÈH‹uÐH‹EØHƒìSI‰øH‰ÇèâoHƒÄH‹]øÉÃUH‰åHƒì H‰}èH‹EèHPHEðH‰ÖH‰Çè‘fH‹EðÉÃUH‰åHƒì H‰}èH‰uàH‹EèH‹H‹UàHÁâHÐH‰EøHUøHEðH‰ÖH‰ÇèQfH‹EðÉÃUH‰åHƒìH‰}øH‰uðH‹EðH‰ÇèØgH‹H‹EøH‰ÉÃUH‰åSHƒìhH‰}¸H‰u°H‰U H‰MH‹E¸H‰ÇèShH‰EÀHUÀHE°H‰ÖH‰Çè@gH‰EèH‹E¸H‰ÇèrjH‰EÐH‹UèHEÐH‰ÖH‰ÇèsgH‰ÆH‹MH‹U H‹E¸HƒìSH‰Çè'jHƒÄH‹E¸H‰Çè/jH‰EàH‹UèHEàH‰ÖH‰Çè0gH‹]øÉÃUH‰åH‰}ø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EðH‰ÇèpÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèÉcÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰ÇèûhÉÃUH‰åHƒìH‰}øH‰uðHƒ}ðtH‹EøH‹UðH‰ÖH‰Çè†hë¸ÉÃUH‰åHƒì H‰}èH‰uàÆEÿH‹UàH‹EèH‰ÖH‰Çè¤hÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EðH‰Çè6oÉÃUH‰åH‰}øH‰uð]ÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EØH‰ÇèmH‰ÃH‹EàH‰Æ¿ènH…Àt H‰ÞH‰Çè­bHƒÄ([]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèÔeH‰Çè gÉÃUH‰åHƒìH‰}øH‰uðH‹MðH‹EøºH‰ÎH‰ÇèPcÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‰MàH‹UèH‹MðH‹EøH‰ÎH‰Çè‘jÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèæbÉÃUH‰åH‰}ø]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐH=އHƒ}Øu&H‹UàH‹MàH‹EèHƒì¶uVH‰ÎH‰Çè¨iHƒÄëZHƒmØH‹UàH‹EèHƒì¶MQH‰ÖH‰ÇèéjHƒÄH‰EøH‹UØH‹MàH‹EøHƒì¶uVH‰ÎH‰Çè\ÿÿÿHƒÄH‹EøH‰Eàé_ÿÿÿÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐH=~PH‹EøHˆH‹EøHƒì¶URH‰ÎH‰ÇèiHƒÄH‹EøHˆH‹EðHƒì¶URH‰ÆH‰Ïè&jHƒÄë H‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèÒhHƒÄÉÃUH‰åSHƒìHEïHƒìRH‰ÇèñaHƒÄ‰ØH‹]øÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰ÇèBkH‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰Çè·jH‹EèH‹UÈH‰ÆH}èa„ÀtH‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰EøëœH‹EðH‰Eøë’H‹EØÉÃUH‰åHƒì0H‰}øH‰uðH‰UèH‰MàL‰EØH‹EøH;Eðt[H‹EèH;EàtQH‹UøH‹EèH‰ÆH}è¾b„ÀtH‹UèH‹EØH‰ÖH‰Çè_HƒEèëH‹UøH‹EØH‰ÖH‰Çèu_HƒEøHƒEØë›H‹UØH‹MðH‹EøH‰ÎH‰Çè iH‰ÂH‹MàH‹EèH‰ÎH‰ÇèöhÉÃUH‰åSHƒìHEïHƒìRH‰ÇèaHƒÄ‰ØH‹]øÉÃUH‰åHƒìH‰}øH‰uðH‹EøH;EðtaH‹UðH‹EøH)ÂH‰ÐHÁøH‰ÇèˆhH4H‹MðH‹EøHƒì¶URH‰òH‰ÎH‰Çè÷fHƒÄH‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèggHƒÄÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØHƒìPè¥`HƒÄH‹UØH‹MàH‹EèHƒìSH‰ÎH‰ÇèãeHƒÄH‹]øÉÃUH‰åSHƒìHH‰}ØH‰uÐH‰UÈH‰MÀL‰E¸èÅ`H‹}¸H‹MÀH‹UÈH‹uÐH‹EØHƒìSI‰øH‰ÇèuhHƒÄH‹]øÉÃUH‰åH‰}øH‰uðH‹Eø‹PH‹Eð‹@9ÂuH‹EøH‹H‹EðH‹H9Â’ÀëH‹Eø‹PH‹Eð‹@9œÀ]ÃUH‰åHƒìH‰}øH‹EøH‰Çè©aÉÃUH‰åH‰}èH‰uàH‰UØH‹EØ‹‰EôH‹EàH‰EøHƒ}øtH‹Eè‹Uô‰HƒmøHƒEèëäH‹Eè]ÃUH‰åSHƒì(H‰}àH‰uÐH‰UØH‹EÐH‰ÇèIdH‰ÃH‹EàH‰Çè:dH‰ÁH‹EØH‰ÂH‰ÞH‰Ïè eHƒÄ([]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹MðH‹EøHƒì¶uVH‰ÎH‰ÇèVdHƒÄH‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèpcHƒÄÉÃUH‰åHƒì H‰}èH‰uàH‹UàH‹EèH)ÂH‰ÐHÁøH‰ÂHÁê?HÐHÑøH…H‹EèHÐH‰EøH‹EàHxüH‹EèHpH‹UøH‹EèHƒì¶MQH‰ùH‰Çè/eHƒÄH‹EèHxH‹UèH‹EàHƒì¶MQH‰ÆèÂdHƒÄÉÃUH‰åH‰}øH‹Eø]ÃUH‰åSHƒì(H‰}ØH‰uÐH‹EØH;EЄ¥H‹EØHƒÀH‰EèH‹EèH;EЄŒH‹UØH‹EèH‰ÆH}èÑ]„ÀtBH‹EèH‰Çè›e‹‰EäH‹EèHPH‹MèH‹EØH‰ÎH‰ÇèYcHEäH‰Çèoe‹H‹E؉ë'Hƒì¶EPè×]HƒÄH‹EèHƒìSH‰ÇèŽdHƒÄHƒEèégÿÿÿH‹]øÉÃUH‰åSHƒì(H‰}ØH‰uÐH‹EØH‰EèH‹EèH;EÐt.Hƒì¶EPè|]HƒÄH‹EèHƒìSH‰Çè3dHƒÄHƒEèëÈH‹]øÉÃUH‰åH‰}ø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Çè^ÉÃUH‰åH‰}ø]ÃUH‰åSHƒì(H‰}ØH‰uÐHEØH‰Çè cH‹EØH‹UÐHƒìSH‰ÖH‰Çè²`HƒÄH‹]øÉÃUH‰åSHƒì(H‰}ØH‰uÐH‹EÐH‰EèH‹EØH‰ÇèÃbH‹UèH‹EØHƒìSH‰ÖH‰Çè?eHƒÄH‹]øÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰ÇèG]ÉÃUH‰åH‰}øH‰uðH‰UèH‹Eð‹H‹Eè‹9œÀ]ÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EàH‰Çè´`H‰ÃH‹EèH‰Çè¥`H‰ÁH‹EØH‰ÂH‰ÞH‰ÏètaHƒÄ([]ÃUH‰åH‰}ø]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐH=‡އHƒ}Øu&H‹UàH‹MàH‹EèHƒì¶uVH‰ÎH‰Çè$aHƒÄëZHƒmØH‹UàH‹EèHƒì¶MQH‰ÖH‰ÇèebHƒÄH‰EøH‹UØH‹MàH‹EøHƒì¶uVH‰ÎH‰Çè\ÿÿÿHƒÄH‹EøH‰Eàé_ÿÿÿÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐH=‡~PH‹EøHˆ€H‹EøHƒì¶URH‰ÎH‰Çè—`HƒÄH‹EøHˆ€H‹EðHƒì¶URH‰ÆH‰Ïè¢aHƒÄë H‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèN`HƒÄÉÃUH‰åSHƒìHEïHƒìRH‰Çè…YHƒÄ‰ØH‹]øÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰Çè¾bH‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰Çè3bH‹EèH‹UÈH‰ÆH}èY„ÀtH‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰EøëœH‹EðH‰Eøë’H‹EØÉÃUH‰åHƒì0H‰}øH‰uðH‰UèH‰MàL‰EØH‹EøH;EðtQH‹EèH;EàtGH‹UøH‹EèH‰ÆH}è:Z„ÀtH‹EØH‹UèH‹H‰HƒEèëH‹EØH‹UøH‹H‰HƒEøHƒEØë¥H‹UØH‹MðH‹EøH‰ÎH‰Çè’`H‰ÂH‹MàH‹EèH‰ÎH‰Çè|`ÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì H‰}èH‹EèH‹H‰EøHUøHEðH‰ÖH‰ÇèÀWH‹EðÉÃUH‰åSHƒìH‰}èH‰uàH‹EèH‰ÇèMYH‹H‰ÃH‹EàH‰Çè;YH‹H)ÃH‰ØHÁøHƒÄ[]ÃUH‰åHƒì@H‰}èH‰uàH‰UÐH‰MÀH‹MÀH‹UÐH‹uàH‹EèHƒìAPH‰ÇèÀ[HƒÄÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹MðH‹EøH‰ÎH‰Çè,`ÉÃUH‰åHƒìH‰}øH‰uðH‹MðH‹EøºH‰ÎH‰ÇèœVÉÃUH‰åHƒì H‰}èH‰uàH‹EèH‰EøHƒ}àt H‹EøH‰Çèå[H‰Çè[HƒmàHƒEøëÙH‹EøÉÃUH‰åHƒìH‰}øH‹Eø¾H‰Çè(ZÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‰ÇèâWH;Eð’À„Àtèü`H‹EðHÁàH‰ÇèaÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰ÇèZÉÃUH‰åHƒìH‰}øH‰uðH‹EðH‰Çè–TÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹MðH‹EøHƒì¶uVH‰ÎH‰ÇèÓ[HƒÄH‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèíZHƒÄÉÃUH‰åHƒì H‰}èH‰uàH‹UàH‹EèH)ÂH‰ÐHÁøH‰ÂHÁê?HÐHÑøHÁàH‰ÂH‹EèHÐH‰EøH‹EàHxðH‹EèHpH‹UøH‹EèHƒì¶MQH‰ùH‰Çè­\HƒÄH‹EèHxH‹UèH‹EàHƒì¶MQH‰Æè@\HƒÄÉÃUH‰åSHƒì8H‰}ÈH‰uÀH‹EÈH;EÀ„âH‹EÈHƒÀH‰EèH‹EèH;EÀ„ÉH‹UÈH‹EèH‰ÆH}èKU„ÀteH‹EèH‰Çè!]H‰ÂHEÐH‰ÖH‰ÇèSH‹EèHPH‹MèH‹EÈH‰ÎH‰ÇèØZHEÐH‰Çèè\H‰ÂH‹EÈH‰ÖH‰ÇèþRHEÐH‰ÇèìRë'Hƒì¶EPè:UHƒÄH‹EèHƒìSH‰Çè÷[HƒÄHƒEèéDÿÿÿH‰ÃHEÐH‰ÇèªRH‰ØH‰Çè _H‹]øÉÃUH‰åSHƒì(H‰}ØH‰uÐH‹EØH‰EèH‹EèH;EÐt.Hƒì¶EPèÅTHƒÄH‹EèHƒìSH‰Çè‚[HƒÄHƒEèëÈH‹]øÉÃUH‰åH‰}ø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰ÇèQUÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹EðH‰ÖH‰Çè©TÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EàH‰Çè¾XH‰ÃH‹EèH‰Çè¯XH‰ÁH‹EØH‰ÂH‰ÞH‰Ïè~YHƒÄ([]ÃUH‰åH‰}ø]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐH=‡އHƒ}Øu&H‹UàH‹MàH‹EèHƒì¶uVH‰ÎH‰Çè:YHƒÄëZHƒmØH‹UàH‹EèHƒì¶MQH‰ÖH‰Çè{ZHƒÄH‰EøH‹UØH‹MàH‹EøHƒì¶uVH‰ÎH‰Çè\ÿÿÿHƒÄH‹EøH‰Eàé_ÿÿÿÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐH=‡~PH‹EøHˆ€H‹EøHƒì¶URH‰ÎH‰Çè­XHƒÄH‹EøHˆ€H‹EðHƒì¶URH‰ÆH‰Ïè¸YHƒÄë H‹UðH‹EøHƒì¶MQH‰ÖH‰ÇèdXHƒÄÉÃUH‰åSHƒìHEïHƒìRH‰ÇèQHƒÄ‰ØH‹]øÉÃUH‰åHƒì@H‰}ØH‰uÐH‰UÈH‹UÐH‹EØH‰ÖH‰ÇèÔZH‰EøHƒ}ø~gH‹EøHÑøH‰EðH‹EØH‰EèH‹UðHEèH‰ÖH‰ÇèIZH‹EèH‹UÈH‰ÆH}èQ„ÀtH‹EèH‰EØHƒEØH‹EøH+EðHƒèH‰EøëœH‹EðH‰Eøë’H‹EØÉÃUH‰åHƒì0H‰}øH‰uðH‰UèH‰MàL‰EØH‹EøH;EðtQH‹EèH;EàtGH‹UøH‹EèH‰ÆH}èPR„ÀtH‹EØH‹UèH‹H‰HƒEèëH‹EØH‹UøH‹H‰HƒEøHƒEØë¥H‹UØH‹MðH‹EøH‰ÎH‰Çè¨XH‰ÂH‹MàH‹EèH‰ÎH‰Çè’XÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒìH‰}ðH‹EðH‰Çè¾RÉÃUH‰åATSHƒì H‰}èH‰uàH‰UØH‹EØH‰Çè·UI‰ÄH‹EàH‰Çè¨UH‰ÃH‹EèH‰Çè™UL‰âH‰ÞH‰ÇèÁUHƒÄ [A\]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèHƒì¶MQH‰ÖH‰ÇèªTHƒÄH‹EàH‰EøH‹EøH;EØsCH‹UèH‹EøH‰ÆH}è P„Àt$H‹UøH‹MàH‹EèHƒì¶uVH‰ÎH‰Çè8THƒÄHƒEøë³ÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHƒø~+HƒmðH‹UðH‹MðH‹EøHƒì¶uVH‰ÎH‰ÇèáSHƒÄëÁÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‰MàH‹UèH‹EðH‰ÆH}è\O„ÀtuH‹UàH‹EèH‰ÆH}èDO„ÀtH‹UèH‹EøH‰ÖH‰ÇèKXé²H‹UàH‹EðH‰ÆH}èO„ÀtH‹UàH‹EøH‰ÖH‰ÇèXé‚H‹UðH‹EøH‰ÖH‰ÇèXëmH‹UàH‹EðH‰ÆH}èÏN„ÀtH‹UðH‹EøH‰ÖH‰ÇèÖWë@H‹UàH‹EèH‰ÆH}è¢N„ÀtH‹UàH‹EøH‰ÖH‰Çè©WëH‹UèH‹EøH‰ÖH‰Çè”WÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹EøH‰ÆH}èKN„ÀtHƒEøëáHƒmðH‹UðH‹EèH‰ÆH}è'N„ÀtHƒmðëáH‹EøH;EðrH‹EøëH‹UðH‹EøH‰ÖH‰ÇèWHƒEøë“ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰ÇèOÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EàH‰ÇèšRH‰ÃH‹EèH‰Çè‹RH‰ÁH‹EØH‰ÂH‰ÞH‰Ïè†THƒÄ([]ÃUH‰åSHƒìHEïHƒìRH‰Çè!MHƒÄ‰ØH‹]øÉÃUH‰åHƒì H‰}èH‹EèH‰ÇèU‹‰EôH‹EèH‰EøHƒmøH‹UøHEôH‰ÆH}èãL„Àt#H‹EøH‰ÇèÝT‹H‹Eè‰H‹EøH‰EèHƒmøëÅHEôH‰ÇèºT‹H‹Eè‰ÉÃUH‰åH‰}ø]ÃUH‰åH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHÁø]ÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÂH‹EøH‰]ÃUH‰åH‰}øH‰uðH‰UèH‹EðH‹H‹EèH‹H9œÀ]ÃUH‰åHƒìH‰}øH‹EøH‰ÇèH‹EÈH‰EèHƒmèH‹UèHEÐH‰ÆH}è¹?„Àt-H‹EèH‰ÇèÅGH‰ÂH‹EÈH‰ÖH‰ÇèÛ=H‹EèH‰EÈHƒmèë»HEÐH‰Çè˜GH‰ÂH‹EÈH‰ÖH‰Çè®=HEÐH‰Çèœ=ëH‰ÃHEÐH‰Çè‹=H‰ØH‰ÇèìIHƒÄ8[]ÃUH‰åHƒìH‰}øH‹EøH‰ÇècAÉÃUH‰åATSHƒì H‰}èH‰uàH‰UØH‹EØH‰ÇèbDI‰ÄH‹EàH‰ÇèSDH‰ÃH‹EèH‰ÇèDDL‰âH‰ÞH‰ÇèfDHƒÄ [A\]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹MðH‹EøHƒì¶uVH‰ÎH‰ÇèMDHƒÄH‹UðH‹EøHƒì¶MQH‰ÖH‰ÇègCHƒÄÉÃUH‰åHƒì H‰}èH‰uàH‹UàH‹EèH)ÂH‰ÐHÁøH‰ÂHÁê?HÐHÑøHÅH‹EèHÐH‰EøH‹EàHxøH‹EèHpH‹UøH‹EèHƒì¶MQH‰ùH‰Çè&EHƒÄH‹EèHxH‹UèH‹EàHƒì¶MQH‰Æè¹DHƒÄÉÃUH‰åH‰}øH‹Eø]ÃUH‰åSHƒì(H‰}ØH‰uÐH‹EØH;EЄ¬H‹EØHƒÀH‰EèH‹EèH;EЄ“H‹UØH‹EèH‰ÆH}è¼=„ÀtIH‹EèH‰ÇèŒEH‹H‰EàH‹EèHPH‹MèH‹EØH‰ÎH‰ÇèNCHEàH‰Çè^EH‰ÂH‹EØH‹H‰ë'Hƒì¶EPèÇ=HƒÄH‹EèHƒìSH‰Çè~DHƒÄHƒEèé`ÿÿÿH‹]øÉÃUH‰åSHƒì(H‰}ØH‰uÐH‹EØH‰EèH‹EèH;EÐt.Hƒì¶EPèl=HƒÄH‹EèHƒìSH‰Çè#DHƒÄHƒEèëÈH‹]øÉÃUH‰åH‰}ø]ÃUH‰åSHƒì(H‰}ØH‰uÐHEØH‰Çè.CH‹EØH‹UÐHƒìSH‰ÖH‰ÇèÖ@HƒÄH‹]øÉÃUH‰åSHƒì(H‰}ØH‰uÐH‹EÐH‰EèH‹EØH‰ÇèçBH‹UèH‹EØHƒìSH‰ÖH‰ÇècEHƒÄH‹]øÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Çèk=ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹EðH‰ÖH‰ÇèÃ<ÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EàH‰ÇèØ@H‰ÃH‹EèH‰ÇèÉ@H‰ÁH‹EØH‰ÂH‰ÞH‰Ïè˜AHƒÄ([]ÃUH‰åHƒìH‰}ðHEðH‰ÇèÏ<ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰Çè”=ÉÃUH‰åHƒì0H‰}ØH‰uÐH‹UÐH‹EØH)ÂH‰ÐHƒøŽŒH‹UÐH‹EØH)ÂH‰ÐHÁøH‰EðH‹EðHƒèH‰ÂHÁê?HÐHÑøH‰EøH‹EøH…H‹EØHÐH‰ÇèÞB‹‰EìHEìH‰ÇèÍB‹H‹UðH‹uøH‹EØHƒì¶}WH‰Çè@HƒÄHƒ}øt Hƒmøë£ëÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹EØH‰ÇèvB‹‰EüH‹EèH‰ÇèeB‹H‹E؉HEüH‰ÇèQB‹H‹MàH‹EèH)ÁH‰ÈHÁøH‰ÆH‹EèHƒì¶MQ‰ÑH‰ò¾H‰Çèu?HƒÄÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çè'BÉÃUH‰åATSHƒì H‰}èH‰uàH‰UØH‹EØH‰Çè ?I‰ÄH‹EàH‰Çèþ>H‰ÃH‹EèH‰Çèï>L‰âH‰ÞH‰Çèm@HƒÄ [A\]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰ÇèÏ:ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰Çèv;ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèHƒì¶MQH‰ÖH‰Çèœ=HƒÄH‹EàH‰EøH‹EøH;EØsCH‹UèH‹EøH‰ÆH}èø8„Àt$H‹UøH‹MàH‹EèHƒì¶uVH‰ÎH‰Çè*=HƒÄHƒEøë³ÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHƒø~+HƒmðH‹UðH‹MðH‹EøHƒì¶uVH‰ÎH‰ÇèÓ<HƒÄëÁÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‰MàH‹UèH‹EðH‰ÆH}èH8„ÀtuH‹UàH‹EèH‰ÆH}è08„ÀtH‹UèH‹EøH‰ÖH‰Çè=Aé²H‹UàH‹EðH‰ÆH}è8„ÀtH‹UàH‹EøH‰ÖH‰Çè Aé‚H‹UðH‹EøH‰ÖH‰Çèõ@ëmH‹UàH‹EðH‰ÆH}è»7„ÀtH‹UðH‹EøH‰ÖH‰ÇèÈ@ë@H‹UàH‹EèH‰ÆH}èŽ7„ÀtH‹UàH‹EøH‰ÖH‰Çè›@ëH‹UèH‹EøH‰ÖH‰Çè†@ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹EøH‰ÆH}è77„ÀtHƒEøëáHƒmðH‹UðH‹EèH‰ÆH}è7„ÀtHƒmðëáH‹EøH;EðrH‹EøëH‹UðH‹EøH‰ÖH‰Çè @HƒEøë“ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Çèõ7ÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EàH‰ÇèŒ;H‰ÃH‹EèH‰Çè};H‰ÁH‹EØH‰ÂH‰ÞH‰Ïèx=HƒÄ([]ÃUH‰åSHƒìHEïHƒìRH‰Çè 6HƒÄ‰ØH‹]øÉÃUH‰åHƒì H‰}èH‹EèH‰Çèÿ=H‹H‰EðH‹EèH‰EøHƒmøH‹UøHEðH‰ÆH}èÇ5„Àt(H‹EøH‰ÇèÇ=H‰ÂH‹EèH‹H‰H‹EøH‰EèHƒmøëÀHEðH‰ÇèŸ=H‰ÂH‹EèH‹H‰ÉÃUH‰åH‰}ø]ÃUH‰åH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHÁø]ÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÂH‹EøH‰]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹EðH‰ÖH‰Çè6ÉÃUH‰åH‰}øH‰uðH‹EøH‹‹H‹EðH‹‹9œÀ]ÃUH‰åHƒìH‰}øH‹EøH‰Çèù6ÉÃUH‰åATSHƒì H‰}èH‰uàH‰UØH‹EØH‰Çèø9I‰ÄH‹EàH‰Çèé9H‰ÃH‹EèH‰ÇèÚ9L‰âH‰ÞH‰Çèü9HƒÄ [A\]ÃUH‰åSHƒìH‰}èH‰uàH‹EèH‰Çèt5H‹H‹EàH‰Çèe5H‹H9ÕÀHƒÄ[]ÃUH‰åSHƒì8H‰}ÐH‰uÀHEÐH‰Çè¨:H‹UÀH‹EÐHƒìSH‰ÖH‰ÇèP8HƒÄH‹]øÉÃUH‰åSHƒìH‰}èH‰uàH‹EèH‰Çèù4H‹H‰ÃH‹EàH‰Çèç4H‹H)ÃH‰ØHÁøHƒÄ[]ÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‰MÐH‹EàH‰Çè:H‰ÃH‹EèH‰Çè÷9H‰ÇH‹UÐH‹EØH‰ÑH‰ÂH‰ÞèŒ:HƒÄ([]ÃUH‰åSHƒì8H‰}àH‰uÐH‰UÀH‹EÐH‰ÇèZ8H‰ÃH‹EàH‰ÇèK8H‰ÁH‹EÀH‰ÂH‰ÞH‰Ïè9HƒÄ8[]ÃUH‰åSHƒì(H‰}ØH‰uÐH‹EÐH‰EèH‹EØH‰Çè~9H‹UèH‹EØHƒìSH‰ÖH‰Çèú;HƒÄH‹]øÉÃUH‰åHƒì H‰}ðH‰uàH‰UèH‰MøH‹UèH‹MàH‹EðH‰ÎH‰Çè9ÉÃUH‰åH‰}øH¸ÿÿÿÿÿÿÿ]ÃUH‰åH‰}øH‹Eø]ÃUH‰åSHƒìH‰}èH‰uàH‹EàH‰Çè+;H‰ÃH‹EèH‰Æ¿è§;H…Àt H‰ÞH‰ÇèE0HƒÄ[]ÃUH‰åSHƒìHH‰}¸H‰u°H‹U°H‹E¸H)ÂH‰ÐHƒøŽÿH‹U°H‹E¸H)ÂH‰ÐHÁøH‰EàH‹EàHƒèH‰ÂHÁê?HÐHÑøH‰EèH‹EèHÁàH‰ÂH‹E¸HÐH‰Çè»9H‰ÂHEÀH‰ÖH‰Çè¹/HEÀH‰Çè9H‰ÂHEÐH‰ÖH‰Çè›/HMÐH‹UàH‹uèH‹E¸Hƒì¶}WH‰ÇèÊ6HƒÄHEÐH‰Çè|/Hƒ}èu»ë Hƒmè»HEÀH‰ÇèX/ƒûu-éVÿÿÿH‰ÃHEÐH‰Çè>/HEÀH‰Çè2/H‰ØH‰Çè“;H‹]øÉÃUH‰åSHƒìHH‰}ÈH‰uÀH‰U¸H‹E¸H‰ÇèÝ8H‰ÂHEÐH‰ÖH‰ÇèÛ.H‹EÈH‰Çè¿8H‰ÂH‹E¸H‰ÖH‰ÇèÕ.HEÐH‰Çè¡8H‰ÂHEàH‰ÖH‰ÇèŸ.H‹UÀH‹EÈH)ÂH‰ÐHÁøH‰ÆHUàH‹EÈHƒì¶MQH‰ÑH‰ò¾H‰Çè¶5HƒÄHEàH‰Çèh.HEÐH‰Çè\.ë&H‰ÃHEàH‰ÇèK.HEÐH‰Çè?.H‰ØH‰Çè :H‹]øÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çè8ÉÃUH‰åATSHƒì H‰}èH‰uàH‰UØH‹EØH‰Çè 5I‰ÄH‹EàH‰Çèü4H‰ÃH‹EèH‰Çèí4L‰âH‰ÞH‰Çèk6HƒÄ [A\]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰ÇèÍ0ÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒìH‰}øH‹EøH‰Çè_1ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰ÇèL1ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèHƒì¶MQH‰ÖH‰Çè~3HƒÄH‹EàH‰EøH‹EøH;EØsCH‹UèH‹EøH‰ÆH}èÔ.„Àt$H‹UøH‹MàH‹EèHƒì¶uVH‰ÎH‰Çè 3HƒÄHƒEøë³ÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHƒø~+HƒmðH‹UðH‹MðH‹EøHƒì¶uVH‰ÎH‰Çèµ2HƒÄëÁÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‰MàH‹UèH‹EðH‰ÆH}è$.„ÀtuH‹UàH‹EèH‰ÆH}è .„ÀtH‹UèH‹EøH‰ÖH‰Çè7é²H‹UàH‹EðH‰ÆH}èÜ-„ÀtH‹UàH‹EøH‰ÖH‰Çèï6é‚H‹UðH‹EøH‰ÖH‰Çè×6ëmH‹UàH‹EðH‰ÆH}è—-„ÀtH‹UðH‹EøH‰ÖH‰Çèª6ë@H‹UàH‹EèH‰ÆH}èj-„ÀtH‹UàH‹EøH‰ÖH‰Çè}6ëH‹UèH‹EøH‰ÖH‰Çèh6ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹EøH‰ÆH}è-„ÀtHƒEøëáHƒmðH‹UðH‹EèH‰ÆH}èï,„ÀtHƒmðëáH‹EøH;EðrH‹EøëH‹UðH‹EøH‰ÖH‰Çèë5HƒEøë“ÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Çè×-ÉÃUH‰åSHƒì(H‰}èH‰uàH‰UØH‹EàH‰Çèn1H‰ÃH‹EèH‰Çè_1H‰ÁH‹EØH‰ÂH‰ÞH‰ÏèZ3HƒÄ([]ÃUH‰åSHƒìHEïHƒìRH‰Çèé+HƒÄ‰ØH‹]øÉÃUH‰åHƒì H‰}èH‹EèH‰Çèá3H‹H‰EðH‹EèH‰EøHƒmøH‹UøHEðH‰ÆH}è£+„Àt(H‹EøH‰Çè©3H‰ÂH‹EèH‹H‰H‹EøH‰EèHƒmøëÀHEðH‰Çè3H‰ÂH‹EèH‹H‰ÉÃUH‰åH‰}ø]ÃUH‰åH‰}øH‰uðH‹UðH‹EøH)ÂH‰ÐHÁø]ÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÂH‹EøH‰]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹UèH‹EðH‰ÖH‰Çèã+ÉÃUH‰åHƒìH‰}øH‰uðH‹EðH‹H‹EøH‹H‰ÖH‰Çè±+ÉÃUH‰åHƒìH‰}øH‹EøH‰ÇèÕ,ÉÃUH‰åATSHƒì H‰}èH‰uàH‰UØH‹EØH‰ÇèÔ/I‰ÄH‹EàH‰ÇèÅ/H‰ÃH‹EèH‰Çè¶/L‰âH‰ÞH‰ÇèØ/HƒÄ [A\]ÃUH‰åH‰}øH‹EøH‹]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}øtH‹EøH…H‹MèH‹EØH‰ÎH‰Çè•4H‹EøH…H‹EØHÐÉÃUH‰åATSHƒì0H‰}ØH‰uÐH‰UȉMÄH‹EÐH‰EàH‹EÐH‰EèH‹EÈHƒèH‰ÂHÁê?HÐHÑøH;E莊H‹EèHƒÀHÀH‰EèH‹EèHÁàHPüH‹EØHÂH‹EèH …H‹EØHÈH‰ÆH}èŽ)„ÀtHƒmèH‹EÐH…H‹EØHH‹EèH…H‹EØHÐH‰Çè01‹‰H‹EèH‰EÐéWÿÿÿH‹EȃàH…ÀuiH‹EÈHƒèH‰ÂHÁê?HÐHÑøH;EèuNH‹EèHƒÀHÀH‰EèH‹EÐH…H‹EØHH‹EèHÁàHPüH‹EØHÐH‰Çèº0‹‰H‹EèHƒèH‰EÐHƒì¶EPèì(HƒÄHEÄH‰ÇèŒ0‹H‹UàH‹uÐH‹EØHƒìATH‰Çè"-HƒÄHeð[A\]ÃUH‰åHƒì H‰}èH‰uàH‹EèH‰ÇèF0‹‰EüH‹EàH‰Çè50‹H‹Eè‰HEüH‰Çè!0‹H‹Eà‰ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰Çè=+ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}øtH‹EøH…H‹MèH‹EØH‰ÎH‰Çè#2H‹EøH…H‹EØHÐÉÃUH‰åHƒì0H‰}ØH‰uÐH‹UÐH‹EØH)ÂH‰ÐHƒøŽ’H‹UÐH‹EØH)ÂH‰ÐHÁøH‰EðH‹EðHƒèH‰ÂHÁê?HÐHÑøH‰EøH‹EøHÅH‹EØHÐH‰Çè/H‹H‰EàHEàH‰Çèï.H‰ÇH‹UðH‹uøH‹EØHƒì¶MQH‹H‰Çè,,HƒÄHƒ}øt HƒmøëëÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹EØH‰Çè”.H‹H‰EðH‹EèH‰Çè.H‰ÂH‹EØH‹H‰HEðH‰Çèh.H‰ÁH‹UàH‹EèH)ÂH‰ÐHÁøH‰ÆH‹EèHƒì¶URH‹ H‰ò¾H‰Çè+HƒÄÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çè<.ÉÃUH‰åATSHƒì H‰}èH‰uàH‰UØH‹EØH‰Çè(+I‰ÄH‹EàH‰Çè+H‰ÃH‹EèH‰Çè +L‰âH‰ÞH‰Çèˆ,HƒÄ [A\]ÃUH‰åH‰}ø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰ÇèÝ&ÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒìH‰}øH‹EøH‰Çèo'ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰Çè\'ÉÃUH‰åH‰}ø]ÃUH‰åHƒì H‰}ðH‰uàHUðHEàH‰ÖH‰Çè¹%ÉÃUH‰åHƒì H‰}èH‹UèHEðH‰ÖH‰ÇèÀ'H‹EðÉÃUH‰åHƒìH‰}ðH‹EðH‰Çè¸&ÉÃUH‰åATSHƒì@H‰}ÐH‰uÀH‰U°H‹E°H‰Çè½)I‰ÄH‹EÀH‰Çè®)H‰ÃH‹EÐH‰ÇèŸ)L‰âH‰ÞH‰ÇèÙ)H‰EèHUèHEàH‰ÖH‰Çè°#H‹EàHƒÄ@[A\]ÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰Çè†#ÉÃUH‰åHƒì0H‰}àH‰uÐH‰UØÆEÿH‹UØH‹MÐH‹EàH‰ÎH‰ÇèP'ÉÃUH‰åSHƒìHH‰}ÈH‰uÀH‰U¸H‰M°H‹EÀH‰EàH‹EÀH‰EèH‹E¸HƒèH‰ÂHÁê?HÐHÑøH;EèŽH‹EèHƒÀHÀH‰EèH‹EèHÁàHPðH‹EÈHÂH‹EèHÁàH‰ÁH‹EÈHÈH‰ÆH}èf#„ÀtHƒmèH‹EèHÁàH‰ÂH‹EÈHÐH‰Çè)+H‰ÁH‹EÀHÁàH‰ÂH‹EÈHÐH‰ÎH‰Çè1!H‹EèH‰EÀéQÿÿÿH‹E¸ƒàH…ÀuqH‹E¸HƒèH‰ÂHÁê?HÐHÑøH;EèuVH‹EèHƒÀHÀH‰EèH‹EèHÁàHPðH‹EÈHÐH‰Çè«*H‰ÁH‹EÀHÁàH‰ÂH‹EÈHÐH‰ÎH‰Çè³ H‹EèHƒèH‰EÀHƒì¶EPèµ"HƒÄH‹E°H‰Çèa*H‰ÂHEÐH‰ÖH‰Çè_ HMÐH‹UàH‹uÀH‹EÈHƒìSH‰Çèê&HƒÄHEÐH‰ÇèD ëH‰ÃHEÐH‰Çè3 H‰ØH‰Çè”,H‹]øÉÃUH‰åHƒì H‰}èH‰uàH‹EèH‰Çèä)H‰ÂHEðH‰ÖH‰ÇèâH‹EàH‰ÇèÆ)H‰ÂH‹EèH‰ÖH‰ÇèÜHEðH‰Çè¨)H‰ÂH‹EàH‰ÖH‰Çè¾HEðH‰Çè¬ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰Çè´$ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}ø~$H‹UèH‹EØH‰ÖH‰Çè:HƒEèHƒEØHƒmøëÕH‹EØÉÃUH‰åHƒì0H‰}ØH‰uÐH‹UÐH‹EØH)ÂH‰ÐHƒøŽ’H‹UÐH‹EØH)ÂH‰ÐHÁøH‰EðH‹EðHƒèH‰ÂHÁê?HÐHÑøH‰EøH‹EøHÅH‹EØHÐH‰Çè(H‹H‰EàHEàH‰Çè|(H‰ÇH‹UðH‹uøH‹EØHƒì¶MQH‹H‰Çè¹%HƒÄHƒ}øt HƒmøëëÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹EØH‰Çè!(H‹H‰EðH‹EèH‰Çè(H‰ÂH‹EØH‹H‰HEðH‰Çèõ'H‰ÁH‹UàH‹EèH)ÂH‰ÐHÁøH‰ÆH‹EèHƒì¶URH‹ H‰ò¾H‰Çè%HƒÄÉÃUH‰åHƒìH‰}øH‰uðH‹UðH‹EøH‰ÖH‰ÇèÉ'ÉÃUH‰åATSHƒì H‰}èH‰uàH‰UØH‹EØH‰Çèµ$I‰ÄH‹EàH‰Çè¦$H‰ÃH‹EèH‰Çè—$L‰âH‰ÞH‰Çè&HƒÄ [A\]ÃUH‰åH‰}ø]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Çèk ÉÃUH‰åH‰}øH‹Eø]ÃUH‰åHƒìH‰}øH‹EøH‰Çèý ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰Çèê ÉÃUH‰åSHƒìHEïHƒìRH‰Çè@HƒÄ‰ØH‹]øÉÃUH‰åSHƒì8H‰}ØH‰uÐH‰UȉMÄH‹EÐHƒèH‰ÂHÁê?HÐHÑøH‰EèH‹EÐH;EÈ~2H‹EèH…H‹EØH HEÄH‰ÂH‰ÎH}èÞ„Àt¸ë¸„ÀtYH‹EÐH…H‹EØHH‹EèH…H‹EØHÐH‰Çèí%‹‰H‹EèH‰EÐH‹EÐHƒèH‰ÂHÁê?HÐHÑøH‰EèébÿÿÿH‹EÐH…H‹EØHHEÄH‰Çè£%‹‰HƒÄ8[]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}øt0H‹EøH…H‹EøHÁàH÷ØH‰ÁH‹EØHÁH‹EèH‰ÆH‰ÏèÄ'H‹EøHÁàH÷ØH‰ÂH‹EØHÐÉÃUH‰åATSHƒì0H‰}ØH‰uÐH‰UÈH‰MÀH‹EÐH‰EàH‹EÐH‰EèH‹EÈHƒèH‰ÂHÁê?HÐHÑøH;E莌H‹EèHƒÀHÀH‰EèH‹EèHÁàHPøH‹EØHÂH‹EèH ÅH‹EØHÈH‰ÆH}讄ÀtHƒmèH‹EÐHÅH‹EØHH‹EèHÅH‹EØHÐH‰ÇèP$H‹H‰H‹EèH‰EÐéUÿÿÿH‹EȃàH…ÀukH‹EÈHƒèH‰ÂHÁê?HÐHÑøH;EèuPH‹EèHƒÀHÀH‰EèH‹EÐHÅH‹EØHH‹EèHÁàHPøH‹EØHÐH‰ÇèØ#H‹H‰H‹EèHƒèH‰EÐHƒì¶EPèHƒÄHEÀH‰Çè¨#H‰ÁH‹UàH‹uÐH‹EØHƒìATH‹ H‰Çè@ HƒÄHeð[A\]ÃUH‰åHƒì H‰}èH‰uàH‹EèH‰Çè^#H‹H‰EðH‹EàH‰ÇèK#H‰ÂH‹EèH‹H‰HEðH‰Çè2#H‰ÂH‹EàH‹H‰ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰ÇèOÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}øtH‹EøHÅH‹MèH‹EØH‰ÎH‰Çè;%H‹EøHÅH‹EØHÐÉÃUH‰åH‰}ðH‹Eð]ÃUH‰åHƒìH‰}ðH‹EðH‰ÇèxÉÃUH‰åH‰}øH‰uðH‹EøH‹H‹UðHÁâHÂH‹EøH‰H‹Eø]ÃUH‰åHƒì H‰}ðH‰uàH‰UèH‹UèH‹MàH‹EðH‰ÎH‰ÇèØ!ÉÃUH‰åSHƒìHEïHƒìRH‰ÇèjHƒÄ‰ØH‹]øÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‰MÐH‹EàHƒèH‰ÂHÁê?HÐHÑøH‰EøH‹EàH;EØ~1H‹EøHÁàH‰ÂH‹EèH H‹EÐH‰ÂH‰ÎH}è „Àt¸ë¸„Àt`H‹EøHÁàH‰ÂH‹EèHÐH‰ÇèK!H‰ÁH‹EàHÁàH‰ÂH‹EèHÐH‰ÎH‰ÇèSH‹EøH‰EàH‹EàHƒèH‰ÂHÁê?HÐHÑøH‰Eøé\ÿÿÿH‹EÐH‰Çèù H‰ÁH‹EàHÁàH‰ÂH‹EèHÐH‰ÎH‰ÇèÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}ø~/HƒmàH‹EàH‰Çè” H‰ÂHƒmØH‹EØH‰ÖH‰Çè¥HƒmøëÊH‹EØÉÃUH‰åATSHƒì0H‰}ØH‰uÐH‰UÈH‰MÀH‹EÐH‰EàH‹EÐH‰EèH‹EÈHƒèH‰ÂHÁê?HÐHÑøH;E莌H‹EèHƒÀHÀH‰EèH‹EèHÁàHPøH‹EØHÂH‹EèH ÅH‹EØHÈH‰ÆH}è „ÀtHƒmèH‹EÐHÅH‹EØHH‹EèHÅH‹EØHÐH‰Çè³H‹H‰H‹EèH‰EÐéUÿÿÿH‹EȃàH…ÀukH‹EÈHƒèH‰ÂHÁê?HÐHÑøH;EèuPH‹EèHƒÀHÀH‰EèH‹EÐHÅH‹EØHH‹EèHÁàHPøH‹EØHÐH‰Çè;H‹H‰H‹EèHƒèH‰EÐHƒì¶EPèeHƒÄHEÀH‰Çè H‰ÁH‹UàH‹uÐH‹EØHƒìATH‹ H‰Çè£HƒÄHeð[A\]ÃUH‰åHƒì H‰}èH‰uàH‹EèH‰ÇèÁH‹H‰EðH‹EàH‰Çè®H‰ÂH‹EèH‹H‰HEðH‰Çè•H‰ÂH‹EàH‹H‰ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØÆEÿH‹UØH‹MàH‹EèH‰ÎH‰Çè²ÉÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}øtH‹EøHÅH‹MèH‹EØH‰ÎH‰Çè¤ H‹EøHÅH‹EØHÐÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Çè9ÉÃUH‰åSHƒìHEïHƒìRH‰ÇèIHƒÄ‰ØH‹]øÉÃUH‰åSHƒì8H‰}ØH‰uÐH‰UÈH‰MÀH‹EÐHƒèH‰ÂHÁê?HÐHÑøH‰EèH‹EÐH;EÈ~2H‹EèHÅH‹EØH HEÀH‰ÂH‰ÎH}èæ„Àt¸ë¸„Àt[H‹EÐHÅH‹EØHH‹EèHÅH‹EØHÐH‰ÇèûH‹H‰H‹EèH‰EÐH‹EÐHƒèH‰ÂHÁê?HÐHÑøH‰Eèé`ÿÿÿH‹EÐHÅH‹EØHHEÀH‰Çè¯H‹H‰HƒÄ8[]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}øt0H‹EøHÅH‹EøHÁàH÷ØH‰ÁH‹EØHÁH‹EèH‰ÆH‰ÏèÚH‹EøHÁàH÷ØH‰ÂH‹EØHÐÉÃUH‰åHƒìH‰}ðHEðH‰Çè'H‹ÉÃUH‰åSHƒì(H‰}àH‰uÐH‰UØH‹EÐH‰ÇèýH‰ÃH‹EàH‰ÇèîH‰ÁH‹EØH‰ÂH‰ÞH‰Ïè·HƒÄ([]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰ÇèóÉÃUH‰åSHƒìHEïHƒìRH‰ÇèHƒÄ‰ØH‹]øÉÃUH‰åSHƒì8H‰}ØH‰uÐH‰UÈH‰MÀH‹EÐHƒèH‰ÂHÁê?HÐHÑøH‰EèH‹EÐH;EÈ~2H‹EèHÅH‹EØH HEÀH‰ÂH‰ÎH}è „Àt¸ë¸„Àt[H‹EÐHÅH‹EØHH‹EèHÅH‹EØHÐH‰ÇèÁH‹H‰H‹EèH‰EÐH‹EÐHƒèH‰ÂHÁê?HÐHÑøH‰Eèé`ÿÿÿH‹EÐHÅH‹EØHHEÀH‰ÇèuH‹H‰HƒÄ8[]ÃUH‰åHƒì0H‰}èH‰uàH‰UØH‹UàH‹EèH)ÂH‰ÐHÁøH‰EøHƒ}øt0H‹EøHÅH‹EøHÁàH÷ØH‰ÁH‹EØHÁH‹EèH‰ÆH‰Ïè¦H‹EøHÁàH÷ØH‰ÂH‹EØHÐÉÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Çè3ÉÃUH‰åATSHƒì H‰}àH‰uÐH‰UØH‹EØH‰ÇèòI‰ÄH‹EÐH‰ÇèËH‰ÃH‹EàH‰Çè¼L‰âH‰ÞH‰ÇèöHƒÄ [A\]ÃUH‰åHƒì H‰}øH‰uðH‰UèH‹EøH‹UèH‹MðH‰ÎH‰Çè§ÉÃ…ÿ¸ÿÿÿÿuÃfDƒÿv¸ë@‰ÐPÑïƒÿuôÃ1ÀÃÿ¸vfDÑïƒÀƒÿuöÃÃ@AWI‰×AVA‰öAUATM‰ôU‰ýSI^?IÁþHÁëHƒìHÁãH‰ßèùN4ðI‰ÅHÃD‰àƒà?I9݉D$ tL‰éHÇHƒÁH9ËuðI‹?H…ÿt豋D$ 1ÉE…äM‰/AÇG¾M‰wI‰_ A‰GtH‰ÊH‰ðHÓàHÁêHƒÁH÷ÐI!DÕA9ÌwâAL$ÿ…í¾uë>€€H ƒéÑít&H‰ÈH‰òHÁèHÓâ%øÿÿLè@öÅuÛH÷ÒH!ëÖHƒÄ¸[]A\A]A^A_ÃfDfD…ötFÿHL‡1Àf‹HƒÇH9ÏBuòÃ1ÀÀ€1À…ÿtGÿ…Ç”ÀÃGÿ‰ÂÑê ЉÂÁê ЉÂÁê ЉÂÁê ЉÂÁê ЃÀÀGÿ‰ÂÑê ЉÂÁê ЉÂÁê ЉÂÁê ЉÂÁê ЃÀÑèÃDiÇï¾­Þ‰ÂÁê1ÐiÀgE#-«™ˆw‰ÂÁê1ЉÂÁê 1ÐiÀcs‚‘‰ÂÁê1ЉÂÁê 1ЉÂÁê1ÐiÀ33wwÃAVAUATI‰ôU‰ýSHƒìH‰æèeHt$‰ïèS‹<$èSÿÿÿ‰ÇI‰ýHÁçèÙ‹<$H‰ÃD)ïHÁçèÇ‹4$1ÉI‰Æ1ÀVÿD)ê…öë3fD€HcùƒÁ‰»ƒÀ9ÆtD9èrëHcúƒêA‰¾ƒÀ9ÆuêHt$‰ïèÐD;l$vG‹|$HL$ H‰ÚD‰îèÓ‹t$ L‰â‰ïè¡H‰ßè5L‰÷è-HƒÄD‰è[]A\A]A^ÃD‹4$HL$ L‰ò‹|$D)îè‰ë´€AVAUATI‰ôU‰ýSHƒìH‰æèUHt$‰ïèC‹<$èCþÿÿ‰ÇI‰ýHÁçèÉ‹<$H‰ÃD)ïHÁçè·‹4$1É1ÒI‰Æ1À…öë'HcúƒÂ‰»ƒÀ9ðtD9èrëHcùƒÁA‰¾ƒÀ9ðuêHt$‰ïèÐD;l$vG‹|$HL$ H‰ÚD‰îèÓ‹t$ L‰â‰ïè¡H‰ßè5L‰÷è-HƒÄD‰è[]A\A]A^ÃD‹4$HL$ L‰ò‹|$D)îè‰ë´€AWAVAUATA‰ÔUSHƒì(H‰4$Ht$@ˆ|$ ‰×èHHc|$èÔH‹)©I‰Ç€;…MH‹©D‹D‰ÊH|$ HƒìL‰ùATA¸¾è¾‹T$,XY…ÒŽyƒêL‰ø1ÿIL1ÒëHƒÀƒÂH9Èt€8uïHƒÀƒÇH9ÈuïHcÿHcÒH•HÁçè@H‰ßI‰Æè5‹T$I‰Å…ÒŽ1À1Û1íëf.„Hc˃ÃA‰DHƒÀ9Â~A€<uæHc̓ÅA‰ŽHƒÀ9ÂçL‰ÿèÔHt$D‰çè1€|$ HL$uC‹|$L‰ò‰îè5H‹$D‰ç‹t$èL‰÷è•L‰ïèHƒÄ(1À[]A\A]A^A_Ã@‹|$L‰ê‰Þèòë»H‹-ѧ¾ L¿ÆH‰êèH‰ïèö€;D‹MtAH‰ê¾ L¿ÆD‰L$èÙH‰ïèË‹UD‹L$édþÿÿ1Û1íé'ÿÿÿ1Û1ÿéÁþÿÿD‰ÊéJþÿÿf.„AVAUA‰ý‰×ATI‰ôUH½ÀS‰ÓHƒìHt$ è.Ht$‰ßè(IcýH9ï‡×HÁçè©‹|$I‰ÆD)ïHcÿH9HÁçèŠH‰Å1ÀE…í~fDfDA‰†HƒÀA9Åó‹L$H‰êD‰èA9Í}@‰ƒÀHƒÂ9ÈuóH‰æ‰ßè›D;l$ ~B‹<$HL$L‰òD‰î蟋t$L‰â‰ßèmL‰÷èH‰ïèùHƒÄ[]A\A]A^Ã@‹t$HL$H‰ê‹<$D)îèYë¸èôfAWI‰ÿ‰×AVAUATA‰ÔUSHƒì(H‰t$Ht$èHt$D‰çè‹T$…ÒŽƒêL‰ø1ÿIL1Òë HƒÀƒÂH9Èt€8uïHƒÀƒÇH9ÈuïHcÿHcÒH•HÁçèPH‰ßI‰ÆèE‹T$I‰Å…ÒŽ»1À1Û1íëf.„Hc˃ÃA‰DHƒÀ9Â~A€<uæHc̓ÅA‰ŽHƒÀ9ÂçHt$D‰çèIHcD$HL$A€<uF‹|$L‰ò‰îèHH‹T$D‰ç‹t$èL‰÷è§L‰ïèŸHƒÄ(1À[]A\A]A^A_ÃfD‹|$L‰ê‰Þèë¸1Û1ÿé&ÿÿÿ1Û1íézÿÿÿ€€UH‰åAWE‰ÇAVI‰þD‰ÇAUATSHƒìXH‰u¨Hu¼H‰M˜H‰U è™Hu¸D‰ÿè“I‹M‹nI)ÍIÁýD‰ëHH‰E€HÁèHÁàH)ÄI‰äH)ÄI‰ãH)Ä1ÀM…íI‰ât€‹A‰„HƒÀL9èuð¹LL‰ÖE‰ùL‰]ˆA¸XD‰êL‰çL‰UèJ‹E¸L‹UL‹]ˆHÿ;M¼„…E‰øL‰ßºLL‰UˆD‰îL‰]èÐH‹E€L‹]L‹UˆHÁèHÁàH)ÄH…ÛI‰à„¢Hc}¸1ö1É€„H‰ð1ÒHþH÷óA‰ˆHƒÁH9ÙrèH‹M¨AEÿ¾L‰uI‰ÅL‹ ë&€„™A÷þÈH9ÞA‰D±üHFsfH‰ÆHFÿA‹L°üL9è„§A‹<°)ÏA‹D³üE‹|²ü™÷ÿ‰ÇA‹D´üA‰ÆAÁîDðDwÑøA)ÇD‰øA‰×E¯þD9ø|›)Й÷ÿÈH9ÞA‰D±üHFrš‹E¼A9L‹u„Ö1öHƒû¸wëC€A;Tü„5‰ÆHƒÀH9Øt&A‹H …;U¼uÙI‹‰ÆÇ‚HƒÀH9ØuÚHc}¸HUȾHÁçèFD‹E¸E…À„™H‹}È1ÀDfDLJHcu¸HƒÀH9ÆwìH]À¹ºI‰ÙA¸DE1äèy‹uÀ¿ èrI‹NI‹L‹m¨H‰ÈH)ÐHÁøH…Àué‘H‰ÈIƒÄH)ÐHÁøI9Äs}J<¢‹7…ötãI‹EºL¾A¹LcE¼B‹  ‹EÀPhLèÛZI‹YI‹N뮀€‹}¸)ÏéUþÿÿfDI‹>HcÖE‹„D—Çé³þÿÿfD‹uÀ¿HèµH‹M H‹U˜‹}¸H‹…ÿH‰AH‹H‰B„’LuÄE1äI‰ÏL‰u¨I‰Ö뀄HcE¸IƒÄL9àvcN,¥L‰îHuÈ‹…ÀtÞI‹FD‰eÄI;F„üH…ÀtD‰ HƒÀI‰FI‹GI;G„ÎH…À‹t‰HƒÀIƒÄI‰GHcE¸L9àwH‰ßèH‹}Èè© Heظ[A\A]A^A_]ÃDfDH…Û„ª1ÀDA‹‚A‰ƒHƒÀH9ØuïE‰øL‰ßºLL‰UˆD‰îL‰]è* H‹E€L‹]L‹UˆHÁèHÁàH)ÄI‰àé^üÿÿH‹E¨L‹A‹9E¼…~ýÿÿfI‹ÇéýÿÿfL‰ÿè® éÓþÿÿH‹u¨L‰÷èš L‰îHuÈéüþÿÿH‹}È1öéýÿÿE‰øºLD‰îL‰ßè  H‹E¨L‹‹E¼A9…ýÿÿë•AVAUATI‰ôUSH‹GH‰ûH+HÁøH…ÀtHH9ÐvzIÇÅüÿÿÿëA½L‰ïèü H‰ÅL‹3H‹SA‹ $I‰ìL)òH‰ÐHÁøIÔtA‰ $H…Àu)IƒÄM…ötL‰÷è¶ H‰+LíL‰cH‰k[]A\A]A^ÃL‰öH‰ïIƒÄèõ ëÏH¹ÿÿÿÿÿÿÿ?H9ʇsÿÿÿE1í1íH…ÒtƒL,ÅékÿÿÿH=ʯHƒìè0 H‹=‘žH‚¾þÿHƒÄH5¨¯éŠ ÿ%´žÿ%¶žÿ%¸žÿ%ºžÿ%¼žÿ%¾žÿ%Àžÿ%žÿ%Äžÿ%Æžÿ%Èžÿ%Êžÿ%Ìžÿ%Ξÿ%Оÿ%Òžÿ%Ôžÿ%Öžÿ%Øžÿ%Úžÿ%Üžÿ%Þžÿ%àžÿ%âžÿ%äžÿ%æžÿ%èžÿ%êžÿ%ìžÿ%îžÿ%ðžÿ%òžÿ%ôžÿ%öžÿ%øžÿ%úžÿ%üžÿ%þžÿ%Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ% Ÿÿ% Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ%Ÿÿ% Ÿÿ%"Ÿÿ%$Ÿÿ%&Ÿÿ%(Ÿÿ%*Ÿÿ%,Ÿÿ%.Ÿÿ%0Ÿÿ%2Ÿÿ%4Ÿÿ%6Ÿÿ%8Ÿÿ%:Ÿÿ%<Ÿÿ%>Ÿÿ%@Ÿÿ%BŸÿ%DŸÿ%FŸÿ%HŸÿ%JŸÿ%LŸÿ%NŸÿ%PŸÿ%RŸÿ%TŸÿ%VŸÿ%XŸÿ%ZŸÿ%\Ÿÿ%^Ÿÿ%`Ÿÿ%bŸÿ%dŸÿ%fŸÿ%hŸÿ%jŸÿ%lŸÿ%nŸÿ%pŸÿ%rŸÿ%tŸÿ%vŸÿ%xŸÿ%zŸÿ%|Ÿÿ%~Ÿÿ%€Ÿÿ%‚Ÿÿ%„Ÿÿ%†Ÿÿ%ˆŸÿ%ŠŸÿ%ŒŸÿ%ŽŸÿ%Ÿÿ%’Ÿÿ%”Ÿÿ%–Ÿÿ%˜Ÿÿ%šŸÿ%œŸÿ%žŸÿ% Ÿÿ%¢Ÿÿ%¤Ÿÿ%¦Ÿÿ%¨Ÿÿ%ªŸÿ%¬Ÿÿ%®Ÿÿ%°Ÿÿ%²Ÿÿ%´Ÿÿ%¶Ÿÿ%¸Ÿÿ%ºŸÿ%¼Ÿÿ%¾Ÿÿ%ÀŸÿ%Ÿÿ%ÄŸÿ%ÆŸÿ%ÈŸÿ%ÊŸÿ%ÌŸÿ%Οÿ%Пÿ%ÒŸÿ%ÔŸÿ%ÖŸÿ%ØŸÿ%ÚŸÿ%ÜŸÿ%ÞŸÿ%àŸÿ%âŸÿ%äŸÿ%æŸÿ%èŸÿ%êŸÿ%ìŸÿ%îŸÿ%ðŸÿ%òŸÿ%ôŸÿ%öŸÿ%øŸÿ%úŸÿ%üŸÿ%þŸÿ% ÿ% ÿ% ÿ% ÿ% ÿ%  ÿ%  ÿ% ÿ% ÿ% ÿ% ÿ% ÿ% ÿ% ÿ% ÿ% ÿ%  ÿ%" ÿ%$ ÿ%& ÿ%( ÿ%* ÿ%, ÿ%. ÿ%0 ÿ%2 ÿ%4 ÿ%6 ÿ%8 ÿ%: ÿ%< ÿ%> ÿ%@ ÿ%B ÿ%D ÿ%F ÿ%H ÿ%J ÿ%L ÿ%N ÿ%P ÿ%R ÿ%T ÿ%V ÿ%X ÿ%Z ÿ%\ ÿ%^ ÿ%` ÿ%b ÿ%d ÿ%f ÿ%h ÿ%j ÿ%l ÿ%n ÿ%p ÿ%r ÿ%t ÿ%v ÿ%x ÿ%z ÿ%| ÿ%~ ÿ%€ ÿ%‚ ÿ%„ ÿ%† ÿ%ˆ ÿ%Š ÿ%Œ ÿ%Ž ÿ% ÿ%’ ÿ%” ÿ%– ÿ%˜ ÿ%š ÿ%œ ÿ%ž ÿ%  ÿ%¢ ÿ%¤ ÿ%¦ ÿ%¨ ÿ%ª ÿ%¬ ÿ%® ÿ%° ÿ%² ÿ%´ ÿ%¶ ÿ%¸ ÿ%º ÿ%¼ ÿ%¾ ÿ%À ÿ% ÿ%Ä ÿ%Æ ÿ%È ÿ%Ê ÿ%Ì ÿ%Πÿ%Рÿ%Ò ÿ%Ô ÿ%Ö ÿ%Ø ÿ%Ú ÿ%Ü ÿ%Þ ÿ%à ÿ%â ÿ%ä ÿ%æ ÿ%è ÿ%ê ÿ%ì ÿ%î ÿ%ð ÿ%ò ÿ%ô ÿ%ö ÿ%ø ÿ%ú ÿ%ü ÿ%þ ÿ%¡ÿ%¡ÿ%¡ÿ%¡ÿ%¡ÿ% ¡ÿ% ¡ÿ%¡ÿ%¡ÿ%¡ÿ%¡ÿ%¡ÿ%¡ÿ%¡ÿ%¡ÿ%¡ÿ% ¡ÿ%"¡ÿ%$¡ÿ%&¡ÿ%(¡ÿ%*¡ÿ%,¡ÿ%.¡ÿ%0¡ÿ%2¡ÿ%4¡ÿ%6¡ÿ%8¡ÿ%:¡ÿ%<¡ÿ%>¡ÿ%@¡ÿ%B¡ÿ%D¡ÿ%F¡ÿ%H¡ÿ%J¡ÿ%L¡ÿ%N¡ÿ%P¡ÿ%R¡ÿ%T¡ÿ%V¡ÿ%X¡ÿ%Z¡ÿ%\¡ÿ%^¡ÿ%`¡ÿ%b¡ÿ%d¡ÿ%f¡ÿ%h¡ÿ%j¡ÿ%l¡ÿ%n¡ÿ%p¡ÿ%r¡ÿ%t¡ÿ%v¡ÿ%x¡ÿ%z¡ÿ%|¡ÿ%~¡ÿ%€¡ÿ%‚¡ÿ%„¡ÿ%†¡ÿ%ˆ¡ÿ%Š¡ÿ%Œ¡ÿ%Ž¡ÿ%¡ÿ%’¡ÿ%”¡ÿ%–¡ÿ%˜¡ÿ%š¡ÿ%œ¡ÿ%ž¡ÿ% ¡ÿ%¢¡ÿ%¤¡ÿ%¦¡ÿ%¨¡ÿ%ª¡ÿ%¬¡ÿ%®¡ÿ%°¡ÿ%²¡ÿ%´¡ÿ%¶¡ÿ%¸¡ÿ%º¡ÿ%¼¡ÿ%¾¡ÿ%À¡ÿ%¡ÿ%Ä¡ÿ%Æ¡ÿ%È¡ÿ%Ê¡ÿ%Ì¡ÿ%Ρÿ%Сÿ%Ò¡ÿ%Ô¡ÿ%Ö¡ÿ%Ø¡ÿ%Ú¡ÿ%Ü¡ÿ%Þ¡ÿ%à¡ÿ%â¡ÿ%ä¡ÿ%æ¡ÿ%è¡ÿ%ê¡ÿ%ì¡ÿ%î¡ÿ%ð¡ÿ%ò¡ÿ%ô¡ÿ%ö¡ÿ%ø¡ÿ%ú¡ÿ%ü¡ÿ%þ¡ÿ%¢ÿ%¢ÿ%¢ÿ%¢ÿ%¢ÿ% ¢ÿ% ¢ÿ%¢ÿ%¢ÿ%¢ÿ%¢ÿ%¢ÿ%¢ÿ%¢ÿ%¢ÿ%¢ÿ% ¢ÿ%"¢ÿ%$¢ÿ%&¢ÿ%(¢ÿ%*¢ÿ%,¢ÿ%.¢ÿ%0¢ÿ%2¢ÿ%4¢ÿ%6¢ÿ%8¢ÿ%:¢ÿ%<¢ÿ%>¢ÿ%@¢ÿ%B¢ÿ%D¢ÿ%F¢ÿ%H¢ÿ%J¢ÿ%L¢ÿ%N¢ÿ%P¢ÿ%R¢ÿ%T¢ÿ%V¢ÿ%X¢ÿ%Z¢ÿ%\¢ÿ%^¢ÿ%`¢ÿ%b¢ÿ%d¢ÿ%f¢ÿ%h¢ÿ%j¢ÿ%l¢ÿ%n¢ÿ%p¢ÿ%r¢ÿ%t¢ÿ%v¢ÿ%x¢ÿ%z¢ÿ%|¢ÿ%~¢ÿ%€¢ÿ%‚¢ÿ%„¢ÿ%†¢ÿ%ˆ¢ÿ%Š¢ÿ%Œ¢ÿ%Ž¢ÿ%¢ÿ%’¢ÿ%”¢ÿ%–¢ÿ%˜¢ÿ%š¢ÿ%œ¢ÿ%ž¢ÿ% ¢ÿ%¢¢ÿ%¤¢ÿ%¦¢ÿ%¨¢ÿ%ª¢ÿ%¬¢ÿ%®¢ÿ%°¢ÿ%²¢ÿ%´¢ÿ%¶¢ÿ%¸¢ÿ%º¢ÿ%¼¢ÿ%¾¢ÿ%À¢ÿ%¢ÿ%Ä¢ÿ%Æ¢ÿ%È¢ÿ%Ê¢ÿ%Ì¢ÿ%΢ÿ%Тÿ%Ò¢ÿ%Ô¢ÿ%Ö¢ÿ%Ø¢ÿ%Ú¢ÿ%Ü¢ÿ%Þ¢ÿ%à¢ÿ%â¢ÿ%ä¢ÿ%æ¢ÿ%è¢L ’ASÿ%ù‘héæÿÿÿhéÜÿÿÿh-éÒÿÿÿhCéÈÿÿÿhYé¾ÿÿÿhné´ÿÿÿh„éªÿÿÿh˜é ÿÿÿhªé–ÿÿÿhÂéŒÿÿÿhÙé‚ÿÿÿhïéxÿÿÿhénÿÿÿhédÿÿÿh/éZÿÿÿhFéPÿÿÿhWéFÿÿÿhjé<ÿÿÿhzé2ÿÿÿh‹é(ÿÿÿhœéÿÿÿh±éÿÿÿhÉé ÿÿÿhåéÿÿÿhöéöþÿÿh éìþÿÿh#éâþÿÿh8éØþÿÿhQéÎþÿÿhcéÄþÿÿhuéºþÿÿh•é°þÿÿh¶é¦þÿÿhÜéœþÿÿhé’þÿÿhéˆþÿÿhé~þÿÿhétþÿÿhéjþÿÿh6é`þÿÿhNéVþÿÿhdéLþÿÿhéBþÿÿh£é8þÿÿh¿é.þÿÿhÛé$þÿÿhöéþÿÿh éþÿÿh(éþÿÿh?éüýÿÿhTéòýÿÿhiéèýÿÿhxéÞýÿÿhˆéÔýÿÿsampleSortbitonicSortMergeListspartitionWO: all done in arr.size()./include/parUtils.tcc Using bitonic sort since totSize < (5*(npes^2)). totSize: npes: Input to sort is small. splittingComm: -> sendcntsrecvcntssdisplsrdisplsvector::_M_default_append!(in.empty())wtslscnsendSzrecvSzsendOffrecvOffind < npesvector::_M_emplace_back_aux!(listA.empty())!(listB.empty())vector::_M_range_insertsplitters[k].value >= arr[j]k < (npes-1)zPLRx›Å <$Øþþÿÿÿÿÿ†  <džþþÿÿÿÿÿ†  <¤dþþÿÿÿÿÿ†  <ä*þþÿÿÿÿÿ'† " <$þþÿÿÿÿÿ†  <dãýþÿÿÿÿÿ †  <¤ðµþÿÿÿÿÿŽ'† ƒ„ <änýþÿÿÿÿÿ†  <$Jýþÿÿÿÿÿ†  <d&ýþÿÿÿÿÿ†  <¤ýþÿÿÿÿÿb_œ† ƒX <ä$ýþÿÿÿÿÿDJœ† ? <$(ýþÿÿÿÿÿŒ† ‡ <dtýþÿÿÿÿÿ †  L¤Týþÿÿÿÿÿ' Ž›†  Œƒƒ .Õ.À <ô, ÿÿÿÿÿÿ †  <4øÿÿÿÿÿÿ †  <tÄÿÿÿÿÿÿ†  <´ ÿÿÿÿÿÿ^÷š† ƒT <ô¾ÿÿÿÿÿÿPÕš† K <4ÎÿÿÿÿÿÿC† > <tÒÿÿÿÿÿÿ†  <´ ÿÿÿÿÿÿ*† % <ôŠÿÿÿÿÿÿ&† ! <4pÿÿÿÿÿÿ Ÿ™† ƒÿ <t: ÿÿÿÿÿÿE¯™† @ <´? ÿÿÿÿÿÿO† J <ôN ÿÿÿÿÿÿG† B <4V ÿÿÿÿÿÿA† < <tW ÿÿÿÿÿÿŒ† ƒ‚ D´£ ÿÿÿÿÿÿ´ s˜† ƒ‰. <üÿÿÿÿÿÿc˜†  <<ìÿÿÿÿÿÿD'˜† ? <|ðÿÿÿÿÿÿ †  T¼Ðÿÿÿÿÿÿ° «—† ŽŒƒ².Î. < (ÿÿÿÿÿÿD˜† ? <T ,ÿÿÿÿÿÿ&† ! <” ÿÿÿÿÿÿ †  <Ô òÿÿÿÿÿÿ'† " < Úÿÿÿÿÿÿ †  <T ¥ÿÿÿÿÿÿ†  <” tÿÿÿÿÿÿ1† , <Ô fÿÿÿÿÿÿ †  < Fÿÿÿÿÿÿ&† ! <T ,ÿÿÿÿÿÿO† J <” <ÿÿÿÿÿÿA† < <Ô >ÿÿÿÿÿÿ%†  < $ÿÿÿÿÿÿ†  <T õÿÿÿÿÿÿI† ƒ? <” þÿÿÿÿÿÿI† ƒ? <Ô ÿÿÿÿÿÿI† D < ÿÿÿÿÿÿ_† Z <T 0ÿÿÿÿÿÿ[† ƒQ <” Kÿÿÿÿÿÿ†  <Ô "ÿÿÿÿÿÿ%†  <ÿÿÿÿÿÿ†  <TÙÿÿÿÿÿÿp† ƒf <” ÿÿÿÿÿÿR’’† ƒH <Ôÿÿÿÿÿÿ%†  <ÿÿÿÿÿÿI† D <T ÿÿÿÿÿÿY† T <”&ÿÿÿÿÿÿ5† 0 <Ôÿÿÿÿÿÿ)† $ <ÿÿÿÿÿÿ&† ! <TêÿÿÿÿÿÿɆ ƒ¿ <”tÿÿÿÿÿÿ3† . <ÔgÿÿÿÿÿÿT† ƒJ D{ÿÿÿÿÿÿ!†  Œƒ <\T#ÿÿÿÿÿÿˆ ƒ¾ <œà$ÿÿÿÿÿÿ0† + <ÜÐ$ÿÿÿÿÿÿ1† , <Á$ÿÿÿÿÿÿ<† ƒ2 <\½$ÿÿÿÿÿÿ††  <œ%ÿÿÿÿÿÿ°† « <Üs%ÿÿÿÿÿÿ†  <B%ÿÿÿÿÿÿG† B <\I%ÿÿÿÿÿÿZ† U <œc%ÿÿÿÿÿÿZ† U <Ü}%ÿÿÿÿÿÿO† J <Œ%ÿÿÿÿÿÿ&† ! <\r%ÿÿÿÿÿÿ†  <œN%ÿÿÿÿÿÿ†  <Ü*%ÿÿÿÿÿÿPNŒ† K <:%ÿÿÿÿÿÿ†  <\%ÿÿÿÿÿÿ*† % <œò$ÿÿÿÿÿÿj’‹† ƒ` <Ü%ÿÿÿÿÿÿ[† ƒQ <7%ÿÿÿÿÿÿ@† ƒ6 <\7%ÿÿÿÿÿÿ@† ƒ6 <œ8%ÿÿÿÿÿÿ†  <Ü%ÿÿÿÿÿÿ†  <ð$ÿÿÿÿÿÿ†  <\Ì$ÿÿÿÿÿÿbý‰† ƒX <œî$ÿÿÿÿÿÿ,† ' <ÜÚ$ÿÿÿÿÿÿ†  <¶$ÿÿÿÿÿÿPh‰† K <\Æ$ÿÿÿÿÿÿ†  <œ”$ÿÿÿÿÿÿ*† % <Ü~$ÿÿÿÿÿÿ†  <L$ÿÿÿÿÿÿ‚† } D\Ž$ÿÿÿÿÿÿ§† ŽŒƒ” D¤í(ÿÿÿÿÿÿO† ŽŒƒ< <ìô*ÿÿÿÿÿÿ0† + <,ä*ÿÿÿÿÿÿ1† , <lÕ*ÿÿÿÿÿÿ<† ƒ2 <¬Ò*ÿÿÿÿÿÿ†  <ì¯*ÿÿÿÿÿÿ&† ƒ <,•*ÿÿÿÿÿÿŸ† š <lô*ÿÿÿÿÿÿ&† ƒ <¬Ú*ÿÿÿÿÿÿŸ† š <ì:+ÿÿÿÿÿÿ†  <,+ÿÿÿÿÿÿ\† W <l4+ÿÿÿÿÿÿ&† ! <¬+ÿÿÿÿÿÿ&† ! <ì+ÿÿÿÿÿÿ.† ) <,î*ÿÿÿÿÿÿ)† $ <l×*ÿÿÿÿÿÿ†  <¬¦*ÿÿÿÿÿÿ"†  <ìˆ*ÿÿÿÿÿÿ+† & <,s*ÿÿÿÿÿÿ*† % <l]*ÿÿÿÿÿÿ%†  <¬B*ÿÿÿÿÿÿ1† , <ì3*ÿÿÿÿÿÿB† ƒ8 <,5*ÿÿÿÿÿÿ²† ­ Dl§/ÿÿÿÿÿÿ!†  Œƒ <´€3ÿÿÿÿÿÿ†  <ô^3ÿÿÿÿÿÿ†  <4 ,3ÿÿÿÿÿÿ¯† ª <t ›3ÿÿÿÿÿÿ¯† ª L´ 4ÿÿÿÿÿÿ!Ô€† ƒ. #.a <!Ü5ÿÿÿÿÿÿ>† 9 <D!Ú5ÿÿÿÿÿÿ†  <„!¶5ÿÿÿÿÿÿ5† 0 <Ä!«5ÿÿÿÿÿÿ&† ! <"’5ÿÿÿÿÿÿH† C <D"š5ÿÿÿÿÿÿŸ† š <„"ù5ÿÿÿÿÿÿŸ† š <Ä"X6ÿÿÿÿÿÿ †  <#$6ÿÿÿÿÿÿ †  <D#ð5ÿÿÿÿÿÿ^|~† ƒT <„#6ÿÿÿÿÿÿC† > <Ä#6ÿÿÿÿÿÿ5† 0 <$6ÿÿÿÿÿÿ&† ! <D$í5ÿÿÿÿÿÿ9† 4 <„$æ5ÿÿÿÿÿÿé_}† ƒß <Ä$7ÿÿÿÿÿÿB† ƒ8 <%’7ÿÿÿÿÿÿ!†  DD%s7ÿÿÿÿÿÿJ†  Œƒ9 <Œ%v=ÿÿÿÿÿÿ8† 3 DÌ%n=ÿÿÿÿÿÿ!†  Œƒ <&HAÿÿÿÿÿÿ†  <T&&Aÿÿÿÿÿÿ†  <”&ô@ÿÿÿÿÿÿ †  <Ô&¿@ÿÿÿÿÿÿ?† ƒ5 <'¾@ÿÿÿÿÿÿH† ƒ> <T'Æ@ÿÿÿÿÿÿ-† ( <”'´@ÿÿÿÿÿÿ †  <Ô'@ÿÿÿÿÿÿ?† ƒ5 <(~@ÿÿÿÿÿÿH† ƒ> <T(†@ÿÿÿÿÿÿ-† ( <”(s@ÿÿÿÿÿÿ†  <Ô(A@ÿÿÿÿÿÿR† M <)S@ÿÿÿÿÿÿ/† * <T)B@ÿÿÿÿÿÿ†  <”)@ÿÿÿÿÿÿ#†  <Ô)õ?ÿÿÿÿÿÿ0† + <*å?ÿÿÿÿÿÿ†  <T*Ä?ÿÿÿÿÿÿ†  <”*’?ÿÿÿÿÿÿB† = <Ô*”?ÿÿÿÿÿÿ†  <+n?ÿÿÿÿÿÿ1† , <T+_?ÿÿÿÿÿÿ&† ƒ <”+E?ÿÿÿÿÿÿ~† y <Ô+ƒ?ÿÿÿÿÿÿI† ƒ? <,Œ?ÿÿÿÿÿÿI† ƒ? <T,•?ÿÿÿÿÿÿP† ƒF <”,¥?ÿÿÿÿÿÿB† ƒ8 <Ô,§?ÿÿÿÿÿÿ²† ­ <-Eÿÿÿÿÿÿp† ƒf DT-IEÿÿÿÿÿÿ²ßt†  Œƒ£ <œ-´Jÿÿÿÿÿÿ†  <Ü-Jÿÿÿÿÿÿ †  <.[Jÿÿÿÿÿÿ.† ) <\.IJÿÿÿÿÿÿ8† 3 <œ.AJÿÿÿÿÿÿ1† , <Ü.2Jÿÿÿÿÿÿ#†  </Jÿÿÿÿÿÿ#†  <\/úIÿÿÿÿÿÿI† D <œ/JÿÿÿÿÿÿY† T <Ü/Jÿÿÿÿÿÿ)† $ <0Jÿÿÿÿÿÿ.† ) <\0ôIÿÿÿÿÿÿ†  <œ0ÄIÿÿÿÿÿÿ&† ! <Ü0ªIÿÿÿÿÿÿ9† 4 <1¤IÿÿÿÿÿÿɆ ƒ¿ <\1.Jÿÿÿÿÿÿ3† . <œ1!JÿÿÿÿÿÿT† ƒJ <Ü15Jÿÿÿÿÿÿ&† ! <2Jÿÿÿÿÿÿ&† ƒ <\2Jÿÿÿÿÿÿ~† y <œ2?JÿÿÿÿÿÿI† ƒ? <Ü2HJÿÿÿÿÿÿ+† & <33JÿÿÿÿÿÿP† ƒF <\3CJÿÿÿÿÿÿB† ƒ8 <œ3EJÿÿÿÿÿÿ²† ­ <Ü3·Oÿÿÿÿÿÿ †  <4Oÿÿÿÿÿÿ †  <\4aOÿÿÿÿÿÿ(† # <œ4JOÿÿÿÿÿÿ)† $ <Ü43Oÿÿÿÿÿÿ †  <5ýNÿÿÿÿÿÿ †  <\5ÝNÿÿÿÿÿÿ(† # <œ5ÆNÿÿÿÿÿÿ#†  <Ü5©Nÿÿÿÿÿÿ†  <6xNÿÿÿÿÿÿ8† 3 <\6pNÿÿÿÿÿÿ†  <œ6NNÿÿÿÿÿÿ†  <Ü6"Nÿÿÿÿÿÿ-† ( <7Nÿÿÿÿÿÿ †  <\7ÛMÿÿÿÿÿÿµ† ° <œ7PNÿÿÿÿÿÿ‘† Œ <Ü7¡Nÿÿÿÿÿÿ&† ƒ <8‡NÿÿÿÿÿÿŸ† š <\8æNÿÿÿÿÿÿ&† ƒ <œ8ÌNÿÿÿÿÿÿŸ† š <Ü8+Oÿÿÿÿÿÿ¢†  <9Oÿÿÿÿÿÿ&† ƒ <\9sOÿÿÿÿÿÿ~† y <œ9±OÿÿÿÿÿÿI† ƒ? <Ü9ºOÿÿÿÿÿÿP† ƒF <:ÊOÿÿÿÿÿÿ)† $ <\:´Oÿÿÿÿÿÿ?† : <œ:´Oÿÿÿÿÿÿ)† $ <Ü:žOÿÿÿÿÿÿ¬† ƒ¢ <; Pÿÿÿÿÿÿ †  <\;ÖOÿÿÿÿÿÿ#†  <œ;¹Oÿÿÿÿÿÿ†  <Ü;‡Oÿÿÿÿÿÿ†  <<bOÿÿÿÿÿÿ1† , <\<TOÿÿÿÿÿÿ3† . <œ<GOÿÿÿÿÿÿ)† $ <Ü<0Oÿÿÿÿÿÿ#†  <=Oÿÿÿÿÿÿ†  <\=äNÿÿÿÿÿÿM† ƒC <œ=òNÿÿÿÿÿÿ"†  <Ü=ÔNÿÿÿÿÿÿ*† % <>¾Nÿÿÿÿÿÿ†  <\>ŒNÿÿÿÿÿÿ1† , <œ>}Nÿÿÿÿÿÿ&† ! <Ü>dNÿÿÿÿÿÿ †  <?/Nÿÿÿÿÿÿ·† ² <\?¦Nÿÿÿÿÿÿ™† ” <œ?ÿNÿÿÿÿÿÿ&† ƒ <Ü?åNÿÿÿÿÿÿŸ† š <@DOÿÿÿÿÿÿ°† « <\@´Oÿÿÿÿÿÿ&† ƒ <œ@šOÿÿÿÿÿÿ~† y <Ü@ØOÿÿÿÿÿÿI† ƒ? <AáOÿÿÿÿÿÿP† ƒF <\AòOÿÿÿÿÿÿI† D <œAûOÿÿÿÿÿÿ†  <ÜAÕOÿÿÿÿÿÿC† > <BØOÿÿÿÿÿÿL† ƒB <\BäOÿÿÿÿÿÿ[† V <œBÿOÿÿÿÿÿÿ™† ” <ÜBXPÿÿÿÿÿÿ†  <C&Pÿÿÿÿÿÿˆ ƒÁ <\C±PÿÿÿÿÿÿX† ƒN <œCÊPÿÿÿÿÿÿ †  <ÜC–Pÿÿÿÿÿÿ-† ( <D„Pÿÿÿÿÿÿ †  <\DOPÿÿÿÿÿÿ?† ƒ5 <œDNPÿÿÿÿÿÿH† ƒ> <ÜDVPÿÿÿÿÿÿ-† ( <EDPÿÿÿÿÿÿ#†  <\E'PÿÿÿÿÿÿL† ƒB <œE4Pÿÿÿÿÿÿ †  <ÜEÿOÿÿÿÿÿÿ·† ² <FvPÿÿÿÿÿÿ™† ” <\FÏPÿÿÿÿÿÿ&† ƒ <œFµPÿÿÿÿÿÿŸ† š <ÜFQÿÿÿÿÿÿ¦† ¡ <GzQÿÿÿÿÿÿ†  <\GHQÿÿÿÿÿÿ0† + <œG8QÿÿÿÿÿÿC† ƒ9 <ÜG <ìSHTÿÿÿÿÿÿ-† ( <,T6Tÿÿÿÿÿÿ)† $ <lTTÿÿÿÿÿÿL† ƒB D¬T,TÿÿÿÿÿÿïM† Œƒ <ôTôWÿÿÿÿÿÿB† = <4UöWÿÿÿÿÿÿ†  <tUÄWÿÿÿÿÿÿ-† ( <´U±Wÿÿÿÿÿÿ†  <ôUŽWÿÿÿÿÿÿ†  <4VbWÿÿÿÿÿÿV† Q <tVxWÿÿÿÿÿÿŒ† ‡ <´VÄWÿÿÿÿÿÿR† M <ôVÖWÿÿÿÿÿÿ†  <4W«Xÿÿÿÿÿÿƒ† ~ <tWîXÿÿÿÿÿÿ-† ( <´WÛXÿÿÿÿÿÿL† ƒB <ôWçXÿÿÿÿÿÿ&† ƒ <4XÍXÿÿÿÿÿÿÊ·J† ƒÀ <tXWYÿÿÿÿÿÿ†  D´X1YÿÿÿÿÿÿX† ŒƒL <üXAYÿÿÿÿÿÿ[† V < <üZZÿÿÿÿÿÿ-† ( <<[îYÿÿÿÿÿÿ)† $ <|[×YÿÿÿÿÿÿL† ƒB <¼[ãYÿÿÿÿÿÿ†  <ü[½Yÿÿÿÿÿÿ1† , <<\®Yÿÿÿÿÿÿº† µ <|\(Zÿÿÿÿÿÿ‚† } <¼\jZÿÿÿÿÿÿ&† ! Dü\PZÿÿÿÿÿÿX† ŒƒL <D]`Zÿÿÿÿÿÿ-† ( <„]MZÿÿÿÿÿÿ1† , <Ä]>ZÿÿÿÿÿÿŒ† ‡ <^ŠZÿÿÿÿÿÿR† M <D^œZÿÿÿÿÿÿ†  <„^q[ÿÿÿÿÿÿƒ† ~ <Ä^´[ÿÿÿÿÿÿ-† ( <_¡[ÿÿÿÿÿÿL† ƒB <D_­[ÿÿÿÿÿÿ&† ƒ <„_“[ÿÿÿÿÿÿˆ† ƒ <Ä_Û[ÿÿÿÿÿÿ †  <`¥[ÿÿÿÿÿÿ †  <D`…[ÿÿÿÿÿÿ(† # <„`n[ÿÿÿÿÿÿ)† $ <Ä`X[ÿÿÿÿÿÿ%†  <a=[ÿÿÿÿÿÿ†  DDa[ÿÿÿÿÿÿX† ŒƒL <Œa'[ÿÿÿÿÿÿ<† ƒ2 <Ìa#[ÿÿÿÿÿÿ?† ƒ5 < b"[ÿÿÿÿÿÿC† ƒ9 <Lb%[ÿÿÿÿÿÿT† ƒJ <Œb9[ÿÿÿÿÿÿL† ƒB <ÌbE[ÿÿÿÿÿÿH† ƒ> < cM[ÿÿÿÿÿÿ1† , <Lc>[ÿÿÿÿÿÿ†  <Œc[ÿÿÿÿÿÿ†  <ÌcàZÿÿÿÿÿÿI† ƒ? D déZÿÿÿÿÿÿ/ý>† ƒµ.p DTdÐ[ÿÿÿÿÿÿòÓ>† ƒš.N <œdz\ÿÿÿÿÿÿ&† ! DÜd`\ÿÿÿÿÿÿX† ŒƒL <$ep\ÿÿÿÿÿÿ-† ( <de]\ÿÿÿÿÿÿ†  <¤e+\ÿÿÿÿÿÿ†  <äe\ÿÿÿÿÿÿ1† , <$fö[ÿÿÿÿÿÿŒ† ‡ <dfB\ÿÿÿÿÿÿR† M <¤fT\ÿÿÿÿÿÿ†  <äf)]ÿÿÿÿÿÿƒ† ~ <$gl]ÿÿÿÿÿÿ-† ( <dgY]ÿÿÿÿÿÿL† ƒB <¤ge]ÿÿÿÿÿÿ&† ƒ <ägK]ÿÿÿÿÿÿˆ† ƒ <$h“]ÿÿÿÿÿÿ †  <dh]]ÿÿÿÿÿÿ †  <¤h=]ÿÿÿÿÿÿ(† # <äh&]ÿÿÿÿÿÿ)† $ <$i]ÿÿÿÿÿÿ+† & <diû\ÿÿÿÿÿÿ†  D¤iÕ\ÿÿÿÿÿÿX† ŒƒL <ìiæ\ÿÿÿÿÿÿ†  <,j·\ÿÿÿÿÿÿe† ` DljÜ\ÿÿÿÿÿÿ† Œƒ„ <´j$^ÿÿÿÿÿÿL† G <ôj0^ÿÿÿÿÿÿ1† , <4k!^ÿÿÿÿÿÿe† ` <tkF^ÿÿÿÿÿÿÀ† » <´kÆ^ÿÿÿÿÿÿ‹† † <ôk_ÿÿÿÿÿÿ&† ! D4l÷^ÿÿÿÿÿÿX† ŒƒL <|l_ÿÿÿÿÿÿ †  <¼lÔ^ÿÿÿÿÿÿ-† ( <ülÁ^ÿÿÿÿÿÿ†  <þÿÿÿÿÿI† D <ìz>þÿÿÿÿÿ†  D,{T>þÿÿÿÿÿˆ† Œƒ| Dt{”>þÿÿÿÿÿðï'†  ŽŒƒÞ D¼{PHPRPÇ `Aÿÿ4/aS…¹mhÿÿ'':AVÿÿÿÿu-À" ­ ýí 3± ˆÛí É Å ‘ ÚÙ  ÿÿ6=RÿÿÿMA`Vç+¨::Í!îüÿÿÿÿ42xÓt •‘ Œ "ÿÿÿÿÿÿ¶@Z¡\ØvÇŠžECP ð²$ÆÆŸ- ”פè -ùq)ÿÿÿÿÿÿÿÿ'+BI^ÿÿ'':AVÿÿÿÿ4MCÑäœ(þù"ÿÿ6=RÿMA$ƒ7Wç:ŠEÏÝÿÿ4S;ãl'%‰„"ÿÿ'H…çÔ-ÿMAM$¬k­?:Ò!óÿÿG©¾ÿÿ¾#ÿÿ£Æçÿÿ´Éÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ"pRC`>@__ZNSt8ios_base4InitD1EvQr@__ZSt4cout@__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@__ZdaPv€à @__ZdlPv@__Znam@__Znwm@___gxx_personality_v0€ßÿÿÿÿÿÿÿ@dyld_stub_binder@__ZN11IndexHolderIiEC1ERKS0_QrH@__ZN11IndexHolderIiEC1Eim@__ZN11IndexHolderIiEC1Ev@__ZN11IndexHolderIiED1Ev@__ZN11IndexHolderIiEaSERKS0_@__ZN3par10MergeListsIiEEvRSt6vectorIT_SaIS2_EES5_i@__ZN3par10MergeSplitIiEEvRSt6vectorIT_SaIS2_EEiii@__ZN3par10Mpi_IssendIiEEiPT_iiiiPi@__ZN3par10partitionWIiEEiRSt6vectorIT_SaIS2_EEPFjPKS2_Ei@__ZN3par10sampleSortIiEEiRSt6vectorIT_SaIS2_EEi@__ZN3par11bitonicSortIiEEvRSt6vectorIT_SaIS2_EEi@__ZN3par12Mpi_AlltoallIiEEiPT_S2_ii@__ZN3par12Mpi_SendrecvIiiEEiPT_iiiPT0_iiiiP10MPI_Status@__ZN3par12Mpi_datatypeIiE5valueEv@__ZN3par13Mpi_AllgatherIiEEiPT_S2_ii@__ZN3par13Mpi_AllreduceIiEEiPT_S2_iii@__ZN3par13Mpi_AlltoallvIiEEiPT_PiS3_S2_S3_S3_i@__ZN3par13defaultWeightIiEEjPKT_€°þÿÿÿÿÿÿÿ@__ZN3par18bitonicSort_binaryIiEEvRSt6vectorIT_SaIS2_EEi€È@__ZN3par20Mpi_Alltoallv_sparseIiEEiPT_PiS3_S2_S3_S3_i@__ZN3par21Par_bitonic_sort_decrIiEEvRSt6vectorIT_SaIS2_EEii@__ZN3par21Par_bitonic_sort_incrIiEEvRSt6vectorIT_SaIS2_EEii@__ZN3par22Par_bitonic_merge_incrIiEEvRSt6vectorIT_SaIS2_EEii@__ZN3par27Sorted_approx_Select_skewedIiEESt6vectorISt4pairIT_iESaIS4_EERS1_IS3_SaIS3_EEji@__ZN3par8Mpi_RecvIiEEiPT_iiiiP10MPI_Status@__ZN3par8Mpi_ScanIiEEiPT_S2_iii@__ZN3seq10UpperBoundI11IndexHolderIiEEEijPKT_jRS4_@__ZN7omp_par10merge_sortIP11IndexHolderIiEEEvT_S4_@__ZN7omp_par10merge_sortIP11IndexHolderIiESt4lessIS2_EEEvT_S6_T0_@__ZN7omp_par10merge_sortIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_T0_@__ZN7omp_par10merge_sortIP7DataPtrIiESt4lessIS2_EEEvT_S6_T0_@__ZN7omp_par10merge_sortIPiEEvT_S2_@__ZN7omp_par10merge_sortIPiSt4lessIiEEEvT_S4_T0_@__ZN7omp_par15merge_sort_ptrsIP11IndexHolderIiEEEvT_S4_@__ZN7omp_par15merge_sort_ptrsIPiEEvT_S2_@__ZN7omp_par4scanIiiEEvPT_S2_T0_@__ZN7omp_par5mergeIP11IndexHolderIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_@__ZN7omp_par5mergeIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_S8_S8_S8_iT0_@__ZN7omp_par5mergeIP7DataPtrIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_@__ZN7omp_par5mergeIPiSt4lessIiEEEvT_S4_S4_S4_S4_iT0_@__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEE10deallocateEPS2_m@__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEE7destroyIS2_EEvPT_@__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEE8allocateEmPKv@__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEE9constructIS2_JS2_EEEvPT_DpOT0_@__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEEC2Ev@__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEED2Ev@__ZN9__gnu_cxx13new_allocatorISt4pairIiiEE10deallocateEPS2_m@__ZN9__gnu_cxx13new_allocatorISt4pairIiiEE8allocateEmPKv@__ZN9__gnu_cxx13new_allocatorISt4pairIiiEEC2ERKS3_@__ZN9__gnu_cxx13new_allocatorISt4pairIiiEEC2Ev@__ZN9__gnu_cxx13new_allocatorISt4pairIiiEED2Ev@__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim@__ZN9__gnu_cxx13new_allocatorIiE8allocateEmPKv@__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1_@__ZN9__gnu_cxx13new_allocatorIiEC2Ev@__ZN9__gnu_cxx13new_allocatorIiED2Ev@__ZN9__gnu_cxx14__alloc_traitsISaIiEE10_S_on_swapERS1_S3_@__ZN9__gnu_cxx17__normal_iteratorIP11IndexHolderIiESt6vectorIS2_SaIS2_EEEC1ERKS3_@__ZN9__gnu_cxx17__normal_iteratorIPK11IndexHolderIiESt6vectorIS2_SaIS2_EEEC1ERKS4_@__ZN9__gnu_cxx17__normal_iteratorIPKiSt6vectorIiSaIiEEEC1ERKS2_@__ZN9__gnu_cxx17__normal_iteratorIPKiSt6vectorIiSaIiEEEC1IPiEERKNS0_IT_NS_11__enable_ifIXsrSt10__are_sameIS9_S8_E7__valueES5_E6__typeEEE@__ZN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEC1ERKS1_@__ZN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEpLEl@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI11IndexHolderIiEEEC1ES5_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI11IndexHolderIiEEEclIPS4_KS4_EEbT_RT0_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI11IndexHolderIiEEEclIPS4_S4_EEbT_RT0_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEC1ES7_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEclIPS6_KS6_EEbT_RT0_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEclIPS6_S6_EEbT_RT0_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrIiEEEC1ES5_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrIiEEEclIPS4_KS4_EEbT_RT0_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrIiEEEclIPS4_S4_EEbT_RT0_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEC1ES3_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEclIPiKiEEbT_RT0_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEclIPiiEEbT_RT0_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIlEEC1ES3_@__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIlEEclIPlKlEEbT_RT0_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI11IndexHolderIiEEEC1ES5_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI11IndexHolderIiEEEclIKS4_PS4_EEbRT_T0_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI11IndexHolderIiEEEclIS4_PS4_EEbRT_T0_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEC1ES7_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEclIS6_PS6_EEbRT_T0_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI7DataPtrIiEEEC1ES5_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI7DataPtrIiEEEclIS4_PS4_EEbRT_T0_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEC1ES3_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEclIKiPiEEbRT_T0_@__ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEclIiPiEEbRT_T0_@__ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI11IndexHolderIiEEEC1ES5_@__ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI11IndexHolderIiEEEclIPS4_S8_EEbT_T0_@__ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEC1ES7_@__ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEclIPS6_SA_EEbT_T0_@__ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI7DataPtrIiEEEC1ES5_@__ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI7DataPtrIiEEEclIPS4_S8_EEbT_T0_@__ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEC1ES3_@__ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEclIPiS6_EEbT_T0_@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI11IndexHolderIiEEEENS0_14_Iter_comp_valIT_EENS0_15_Iter_comp_iterIS7_EE@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI11IndexHolderIiEEEENS0_14_Iter_comp_valIT_EES7_@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEENS0_14_Iter_comp_valIT_EENS0_15_Iter_comp_iterIS9_EE@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEENS0_14_Iter_comp_valIT_EES9_@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI7DataPtrIiEEEENS0_14_Iter_comp_valIT_EENS0_15_Iter_comp_iterIS7_EE@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI7DataPtrIiEEEENS0_14_Iter_comp_valIT_EES7_@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessIiEEENS0_14_Iter_comp_valIT_EENS0_15_Iter_comp_iterIS5_EE@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessIiEEENS0_14_Iter_comp_valIT_EES5_@__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessIlEEENS0_14_Iter_comp_valIT_EES5_@__ZN9__gnu_cxx5__ops15__iter_less_valEv@__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessI11IndexHolderIiEEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS7_EE@__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessI11IndexHolderIiEEEENS0_14_Val_comp_iterIT_EES7_@__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS9_EE@__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessI7DataPtrIiEEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS7_EE@__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessIiEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS5_EE@__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessIiEEENS0_14_Val_comp_iterIT_EES5_@__ZN9__gnu_cxx5__ops15__val_less_iterEv@__ZN9__gnu_cxx5__ops16__iter_comp_iterISt4lessI11IndexHolderIiEEEENS0_15_Iter_comp_iterIT_EES7_@__ZN9__gnu_cxx5__ops16__iter_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEENS0_15_Iter_comp_iterIT_EES9_@__ZN9__gnu_cxx5__ops16__iter_comp_iterISt4lessI7DataPtrIiEEEENS0_15_Iter_comp_iterIT_EES7_@__ZN9__gnu_cxx5__ops16__iter_comp_iterISt4lessIiEEENS0_15_Iter_comp_iterIT_EES5_@__ZN9__gnu_cxx5__ops16__iter_less_iterEv@__ZN9__gnu_cxxeqIPK11IndexHolderIiESt6vectorIS2_SaIS2_EEEEbRKNS_17__normal_iteratorIT_T0_EESD_@__ZN9__gnu_cxxeqIPKiSt6vectorIiSaIiEEEEbRKNS_17__normal_iteratorIT_T0_EESB_@__ZN9__gnu_cxxmiIPKiSt6vectorIiSaIiEEEENS_17__normal_iteratorIT_T0_E15difference_typeERKS9_SC_@__ZN9__gnu_cxxmiIPiSt6vectorIiSaIiEEEENS_17__normal_iteratorIT_T0_E15difference_typeERKS8_SB_@__ZN9__gnu_cxxneIPiSt6vectorIiSaIiEEEEbRKNS_17__normal_iteratorIT_T0_EESA_@__ZNK11IndexHolderIiEleERKS0_@__ZNK11IndexHolderIiEltERKS0_@__ZNK7DataPtrI11IndexHolderIiEEltERKS2_@__ZNK7DataPtrIiEltERKS0_@__ZNK9__gnu_cxx13new_allocatorI11IndexHolderIiEE8max_sizeEv@__ZNK9__gnu_cxx13new_allocatorISt4pairIiiEE8max_sizeEv@__ZNK9__gnu_cxx13new_allocatorIiE8max_sizeEv@__ZNK9__gnu_cxx17__normal_iteratorIP11IndexHolderIiESt6vectorIS2_SaIS2_EEEdeEv@__ZNK9__gnu_cxx17__normal_iteratorIPK11IndexHolderIiESt6vectorIS2_SaIS2_EEE4baseEv@__ZNK9__gnu_cxx17__normal_iteratorIPKiSt6vectorIiSaIiEEE4baseEv@__ZNK9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEE4baseEv@__ZNK9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEdeEv@__ZNK9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEplEl@__ZNK9__gnu_cxx5__ops14_Iter_less_valclIPiKiEEbT_RT0_@__ZNK9__gnu_cxx5__ops14_Val_less_iterclIKiPiEEbRT_T0_@__ZNK9__gnu_cxx5__ops15_Iter_less_iterclIP11IndexHolderIiES5_EEbT_T0_@__ZNK9__gnu_cxx5__ops15_Iter_less_iterclIP7DataPtrI11IndexHolderIiEES7_EEbT_T0_@__ZNK9__gnu_cxx5__ops15_Iter_less_iterclIP7DataPtrIiES5_EEbT_T0_@__ZNK9__gnu_cxx5__ops15_Iter_less_iterclIPiS3_EEbT_T0_@__ZNKSt12_Vector_baseI11IndexHolderIiESaIS1_EE19_M_get_Tp_allocatorEv@__ZNKSt12_Vector_baseIiSaIiEE19_M_get_Tp_allocatorEv@__ZNKSt13move_iteratorIPiE4baseEv@__ZNKSt4lessI11IndexHolderIiEEclERKS1_S4_@__ZNKSt4lessI7DataPtrI11IndexHolderIiEEEclERKS3_S6_@__ZNKSt4lessI7DataPtrIiEEclERKS1_S4_@__ZNKSt4lessIiEclERKiS2_@__ZNKSt4lessIlEclERKlS2_@__ZNKSt6vectorI11IndexHolderIiESaIS1_EE12_M_check_lenEmPKc@__ZNKSt6vectorI11IndexHolderIiESaIS1_EE3endEv@__ZNKSt6vectorI11IndexHolderIiESaIS1_EE4sizeEv@__ZNKSt6vectorI11IndexHolderIiESaIS1_EE5beginEv@__ZNKSt6vectorI11IndexHolderIiESaIS1_EE5emptyEv@__ZNKSt6vectorI11IndexHolderIiESaIS1_EE8max_sizeEv@__ZNKSt6vectorISt4pairIiiESaIS1_EE4sizeEv@__ZNKSt6vectorIiSaIiEE12_M_check_lenEmPKc@__ZNKSt6vectorIiSaIiEE3endEv@__ZNKSt6vectorIiSaIiEE4sizeEv@__ZNKSt6vectorIiSaIiEE5beginEv@__ZNKSt6vectorIiSaIiEE5emptyEv@__ZNKSt6vectorIiSaIiEE6cbeginEv@__ZNKSt6vectorIiSaIiEE8max_sizeEv@__ZNSaI11IndexHolderIiEEC2Ev@__ZNSaI11IndexHolderIiEED2Ev@__ZNSaISt4pairIiiEEC1Ev@__ZNSaISt4pairIiiEEC2ERKS1_@__ZNSaISt4pairIiiEED1Ev@__ZNSaISt4pairIiiEED2Ev@__ZNSaIiEC1Ev@__ZNSaIiEC2ERKS_@__ZNSaIiED1Ev@__ZNSaIiED2Ev@__ZNSt10_Iter_baseIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEELb0EE7_S_baseES6_@__ZNSt10_Iter_baseIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEELb1EE7_S_baseES6_@__ZNSt10_Iter_baseIP11IndexHolderIiELb0EE7_S_baseES2_@__ZNSt10_Iter_baseIP7DataPtrI11IndexHolderIiEELb0EE7_S_baseES4_@__ZNSt10_Iter_baseIP7DataPtrIiELb0EE7_S_baseES2_@__ZNSt10_Iter_baseIPiLb0EE7_S_baseES0_@__ZNSt10_Iter_baseISt13move_iteratorIPiELb1EE7_S_baseES2_@__ZNSt11__copy_moveILb0ELb0ESt26random_access_iterator_tagE8__copy_mIP11IndexHolderIiES5_EET0_T_S7_S6_@__ZNSt11__copy_moveILb0ELb1ESt26random_access_iterator_tagE8__copy_mI7DataPtrI11IndexHolderIiEEEEPT_PKS7_SA_S8_@__ZNSt11__copy_moveILb0ELb1ESt26random_access_iterator_tagE8__copy_mI7DataPtrIiEEEPT_PKS5_S8_S6_@__ZNSt11__copy_moveILb0ELb1ESt26random_access_iterator_tagE8__copy_mIiEEPT_PKS3_S6_S4_@__ZNSt11__copy_moveILb1ELb1ESt26random_access_iterator_tagE8__copy_mIiEEPT_PKS3_S6_S4_@__ZNSt12_Destroy_auxILb0EE9__destroyIP11IndexHolderIiEEEvT_S5_@__ZNSt12_Destroy_auxILb1EE9__destroyIPSt4pairIiiEEEvT_S5_@__ZNSt12_Destroy_auxILb1EE9__destroyIPiEEvT_S3_@__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE11_M_allocateEm@__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE12_Vector_implC1Ev@__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE12_Vector_implD1Ev@__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE13_M_deallocateEPS1_m@__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE19_M_get_Tp_allocatorEv@__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EEC2Ev@__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EED2Ev@__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE11_M_allocateEm@__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE12_Vector_implC1ERKS2_@__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE12_Vector_implD1Ev@__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE13_M_deallocateEPS1_m@__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE17_M_create_storageEm@__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE19_M_get_Tp_allocatorEv@__ZNSt12_Vector_baseISt4pairIiiESaIS1_EEC2EmRKS2_@__ZNSt12_Vector_baseISt4pairIiiESaIS1_EED2Ev@__ZNSt12_Vector_baseIiSaIiEE11_M_allocateEm@__ZNSt12_Vector_baseIiSaIiEE12_Vector_impl12_M_swap_dataERS2_@__ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0_@__ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev@__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim@__ZNSt12_Vector_baseIiSaIiEE17_M_create_storageEm@__ZNSt12_Vector_baseIiSaIiEE19_M_get_Tp_allocatorEv@__ZNSt12_Vector_baseIiSaIiEEC2EmRKS0_@__ZNSt12_Vector_baseIiSaIiEED2Ev@__ZNSt13move_iteratorIPiEC1ES0_@__ZNSt16allocator_traitsISaI11IndexHolderIiEEE10_S_destroyIS1_EENSt9enable_ifIXsrSt6__and_IJNS3_16__destroy_helperIT_E4typeEEE5valueEvE4typeERS2_PS8_@__ZNSt16allocator_traitsISaI11IndexHolderIiEEE10deallocateERS2_PS1_m@__ZNSt16allocator_traitsISaI11IndexHolderIiEEE11_S_max_sizeIKS2_vEEmRT_i@__ZNSt16allocator_traitsISaI11IndexHolderIiEEE12_S_constructIS1_JS1_EEENSt9enable_ifIXsrSt6__and_IJNS3_18__construct_helperIT_JDpT0_EE4typeEEE5valueEvE4typeERS2_PS8_DpOS9_@__ZNSt16allocator_traitsISaI11IndexHolderIiEEE7destroyIS1_EEvRS2_PT_@__ZNSt16allocator_traitsISaI11IndexHolderIiEEE8allocateERS2_m@__ZNSt16allocator_traitsISaI11IndexHolderIiEEE8max_sizeERKS2_@__ZNSt16allocator_traitsISaI11IndexHolderIiEEE9constructIS1_JS1_EEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS2_PT_DpOS5_@__ZNSt16allocator_traitsISaISt4pairIiiEEE10deallocateERS2_PS1_m@__ZNSt16allocator_traitsISaISt4pairIiiEEE8allocateERS2_m@__ZNSt16allocator_traitsISaIiEE10deallocateERS0_Pim@__ZNSt16allocator_traitsISaIiEE11_S_max_sizeIKS0_vEEmRT_i@__ZNSt16allocator_traitsISaIiEE8allocateERS0_m@__ZNSt16allocator_traitsISaIiEE8max_sizeERKS0_@__ZNSt20__copy_move_backwardILb1ELb0ESt26random_access_iterator_tagE13__copy_move_bIP11IndexHolderIiES5_EET0_T_S7_S6_@__ZNSt20__copy_move_backwardILb1ELb1ESt26random_access_iterator_tagE13__copy_move_bI7DataPtrI11IndexHolderIiEEEEPT_PKS7_SA_S8_@__ZNSt20__copy_move_backwardILb1ELb1ESt26random_access_iterator_tagE13__copy_move_bI7DataPtrIiEEEPT_PKS5_S8_S6_@__ZNSt20__copy_move_backwardILb1ELb1ESt26random_access_iterator_tagE13__copy_move_bIiEEPT_PKS3_S6_S4_@__ZNSt20__uninitialized_copyILb0EE13__uninit_copyIP11IndexHolderIiES4_EET0_T_S6_S5_@__ZNSt20__uninitialized_copyILb1EE13__uninit_copyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES4_EET0_T_SA_S9_@__ZNSt20__uninitialized_copyILb1EE13__uninit_copyISt13move_iteratorIPiES3_EET0_T_S6_S5_@__ZNSt22__uninitialized_fill_nILb1EE15__uninit_fill_nIPimiEET_S3_T0_RKT1_@__ZNSt27__uninitialized_default_n_1ILb0EE18__uninit_default_nIPSt4pairIiiEmEET_S5_T0_@__ZNSt27__uninitialized_default_n_1ILb1EE18__uninit_default_nIPimEET_S3_T0_@__ZNSt4pairIiiEC1Ev@__ZNSt4pairIiiEaSERKS0_@__ZNSt6vectorI11IndexHolderIiESaIS1_EE12emplace_backIJS1_EEEvDpOT_@__ZNSt6vectorI11IndexHolderIiESaIS1_EE19_M_emplace_back_auxIJS1_EEEvDpOT_@__ZNSt6vectorI11IndexHolderIiESaIS1_EE5beginEv@__ZNSt6vectorI11IndexHolderIiESaIS1_EE9push_backEOS1_@__ZNSt6vectorI11IndexHolderIiESaIS1_EEC1Ev@__ZNSt6vectorI11IndexHolderIiESaIS1_EED1Ev@__ZNSt6vectorI11IndexHolderIiESaIS1_EEixEm@__ZNSt6vectorISt4pairIiiESaIS1_EE21_M_default_initializeEm@__ZNSt6vectorISt4pairIiiESaIS1_EEC1EmRKS2_@__ZNSt6vectorISt4pairIiiESaIS1_EED1Ev@__ZNSt6vectorISt4pairIiiESaIS1_EEixEm@__ZNSt6vectorIiSaIiEE15_M_erase_at_endEPi@__ZNSt6vectorIiSaIiEE15_M_range_insertIN9__gnu_cxx17__normal_iteratorIPiS1_EEEEvS6_T_S7_St20forward_iterator_tag@__ZNSt6vectorIiSaIiEE17_M_default_appendEm@__ZNSt6vectorIiSaIiEE18_M_fill_initializeEmRKi@__ZNSt6vectorIiSaIiEE18_M_insert_dispatchIN9__gnu_cxx17__normal_iteratorIPiS1_EEEEvS6_T_S7_St12__false_type@__ZNSt6vectorIiSaIiEE19_M_emplace_back_auxIJiEEEvDpOT_€°@__ZNSt6vectorIiSaIiEE21_M_default_initializeEm€Èñÿÿÿÿÿÿÿ@__ZNSt6vectorIiSaIiEE3endEv@__ZNSt6vectorIiSaIiEE4swapERS1_@__ZNSt6vectorIiSaIiEE5beginEv@__ZNSt6vectorIiSaIiEE5clearEv@__ZNSt6vectorIiSaIiEE6insertIN9__gnu_cxx17__normal_iteratorIPiS1_EEvEES6_NS4_IPKiS1_EET_SA_@__ZNSt6vectorIiSaIiEE6resizeEm@__ZNSt6vectorIiSaIiEEC1EmRKS0_@__ZNSt6vectorIiSaIiEEC1EmRKiRKS0_@__ZNSt6vectorIiSaIiEED1Ev@__ZNSt6vectorIiSaIiEEixEm@__ZSt10_ConstructI11IndexHolderIiEJRS1_EEvPT_DpOT0_@__ZSt10_ConstructISt4pairIiiEJEEvPT_DpOT0_@__ZSt10__distanceIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E15difference_typeES8_S8_St26random_access_iterator_tag@__ZSt10__distanceIP11IndexHolderIiEENSt15iterator_traitsIT_E15difference_typeES4_S4_St26random_access_iterator_tag@__ZSt10__distanceIP7DataPtrI11IndexHolderIiEEENSt15iterator_traitsIT_E15difference_typeES6_S6_St26random_access_iterator_tag@__ZSt10__distanceIP7DataPtrIiEENSt15iterator_traitsIT_E15difference_typeES4_S4_St26random_access_iterator_tag@__ZSt10__distanceIPiENSt15iterator_traitsIT_E15difference_typeES2_S2_St26random_access_iterator_tag@__ZSt10__distanceIPlENSt15iterator_traitsIT_E15difference_typeES2_S2_St26random_access_iterator_tag@__ZSt10__fill_n_aIPimiEN9__gnu_cxx11__enable_ifIXsrSt11__is_scalarIT1_E7__valueET_E6__typeES6_T0_RKS4_@__ZSt10__pop_heapIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_@__ZSt10__pop_heapIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0_@__ZSt10__pop_heapIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_@__ZSt10__pop_heapIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0_@__ZSt11__addressofI11IndexHolderIiEEPT_RS2_@__ZSt11__addressofISt4pairIiiEEPT_RS2_@__ZSt11__make_heapIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt11__make_heapIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_@__ZSt11__make_heapIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt11__make_heapIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_@__ZSt11__push_heapIP11IndexHolderIiElS1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEEvT_T0_SA_T1_T2_@__ZSt11__push_heapIP7DataPtrI11IndexHolderIiEElS3_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS3_EEEEvT_T0_SC_T1_T2_@__ZSt11__push_heapIP7DataPtrIiElS1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEEvT_T0_SA_T1_T2_@__ZSt11__push_heapIPiliN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEEEvT_T0_S8_T1_T2_@__ZSt11__sort_heapIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt11__sort_heapIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_@__ZSt11__sort_heapIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt11__sort_heapIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_@__ZSt11lower_boundIP11IndexHolderIiES1_St4lessIS1_EET_S5_S5_RKT0_T1_@__ZSt11lower_boundIP7DataPtrI11IndexHolderIiEES3_St4lessIS3_EET_S7_S7_RKT0_T1_@__ZSt11lower_boundIP7DataPtrIiES1_St4lessIS1_EET_S5_S5_RKT0_T1_@__ZSt11lower_boundIPiiET_S1_S1_RKT0_@__ZSt11lower_boundIPiiSt4lessIiEET_S3_S3_RKT0_T1_@__ZSt11lower_boundIPllSt4lessIlEET_S3_S3_RKT0_T1_@__ZSt11upper_boundIP11IndexHolderIiES1_St4lessIS1_EET_S5_S5_RKT0_T1_@__ZSt11upper_boundIPiiET_S1_S1_RKT0_@__ZSt11upper_boundIPiiSt4lessIiEET_S3_S3_RKT0_T1_@__ZSt12__miter_baseIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt11_Miter_baseIT_E13iterator_typeES8_@__ZSt12__miter_baseIP11IndexHolderIiEENSt11_Miter_baseIT_E13iterator_typeES4_@__ZSt12__miter_baseIP7DataPtrI11IndexHolderIiEEENSt11_Miter_baseIT_E13iterator_typeES6_@__ZSt12__miter_baseIP7DataPtrIiEENSt11_Miter_baseIT_E13iterator_typeES4_@__ZSt12__miter_baseIPiENSt11_Miter_baseIT_E13iterator_typeES2_@__ZSt12__miter_baseISt13move_iteratorIPiEENSt11_Miter_baseIT_E13iterator_typeES4_@__ZSt12__niter_baseIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt11_Niter_baseIT_E13iterator_typeES8_@__ZSt12__niter_baseIP11IndexHolderIiEENSt11_Niter_baseIT_E13iterator_typeES4_@__ZSt12__niter_baseIP7DataPtrI11IndexHolderIiEEENSt11_Niter_baseIT_E13iterator_typeES6_@__ZSt12__niter_baseIP7DataPtrIiEENSt11_Niter_baseIT_E13iterator_typeES4_@__ZSt12__niter_baseIPiENSt11_Niter_baseIT_E13iterator_typeES2_@__ZSt13__adjust_heapIP11IndexHolderIiElS1_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_T0_SA_T1_T2_@__ZSt13__adjust_heapIP7DataPtrI11IndexHolderIiEElS3_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_T0_SC_T1_T2_@__ZSt13__adjust_heapIP7DataPtrIiElS1_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_T0_SA_T1_T2_@__ZSt13__adjust_heapIPiliN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_T0_S8_T1_T2_@__ZSt13__copy_move_aILb0EP11IndexHolderIiES2_ET1_T0_S4_S3_@__ZSt13__copy_move_aILb0EP7DataPtrI11IndexHolderIiEES4_ET1_T0_S6_S5_@__ZSt13__copy_move_aILb0EP7DataPtrIiES2_ET1_T0_S4_S3_@__ZSt13__copy_move_aILb0EPiS0_ET1_T0_S2_S1_@__ZSt13__copy_move_aILb1EPiS0_ET1_T0_S2_S1_@__ZSt13__heap_selectIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_@__ZSt13__heap_selectIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0_@__ZSt13__heap_selectIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_@__ZSt13__heap_selectIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0_@__ZSt13__lower_boundIP11IndexHolderIiES1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEET_S9_S9_RKT0_T1_@__ZSt13__lower_boundIP7DataPtrI11IndexHolderIiEES3_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS3_EEEET_SB_SB_RKT0_T1_@__ZSt13__lower_boundIP7DataPtrIiES1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEET_S9_S9_RKT0_T1_@__ZSt13__lower_boundIPiiN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEEET_S7_S7_RKT0_T1_@__ZSt13__lower_boundIPiiN9__gnu_cxx5__ops14_Iter_less_valEET_S4_S4_RKT0_T1_@__ZSt13__lower_boundIPllN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIlEEEET_S7_S7_RKT0_T1_@__ZSt13__upper_boundIP11IndexHolderIiES1_N9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEET_S9_S9_RKT0_T1_@__ZSt13__upper_boundIPiiN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEEET_S7_S7_RKT0_T1_@__ZSt13__upper_boundIPiiN9__gnu_cxx5__ops14_Val_less_iterEET_S4_S4_RKT0_T1_@__ZSt13move_backwardIP11IndexHolderIiES2_ET0_T_S4_S3_@__ZSt13move_backwardIP7DataPtrI11IndexHolderIiEES4_ET0_T_S6_S5_@__ZSt13move_backwardIP7DataPtrIiES2_ET0_T_S4_S3_@__ZSt13move_backwardIPiS0_ET0_T_S2_S1_@__ZSt14__copy_move_a2ILb0EN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_ET1_T0_S8_S7_@__ZSt14__copy_move_a2ILb0EN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES6_ET1_T0_S8_S7_@__ZSt14__copy_move_a2ILb0EP11IndexHolderIiES2_ET1_T0_S4_S3_@__ZSt14__copy_move_a2ILb0EP7DataPtrI11IndexHolderIiEES4_ET1_T0_S6_S5_@__ZSt14__copy_move_a2ILb0EP7DataPtrIiES2_ET1_T0_S4_S3_@__ZSt14__copy_move_a2ILb0EPiS0_ET1_T0_S2_S1_@__ZSt14__copy_move_a2ILb1EPiS0_ET1_T0_S2_S1_@__ZSt14__partial_sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_@__ZSt14__partial_sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0_@__ZSt14__partial_sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_@__ZSt14__partial_sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0_@__ZSt15__alloc_on_swapISaIiEEvRT_S2_@__ZSt16__insertion_sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt16__insertion_sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_@__ZSt16__insertion_sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt16__insertion_sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_@__ZSt16__introsort_loopIP11IndexHolderIiElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_T1_@__ZSt16__introsort_loopIP7DataPtrI11IndexHolderIiEElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_T1_@__ZSt16__introsort_loopIP7DataPtrIiElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_T1_@__ZSt16__introsort_loopIPilN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_T1_@__ZSt18__do_alloc_on_swapISaIiEEvRT_S2_St17integral_constantIbLb0EE@__ZSt18make_move_iteratorIPiESt13move_iteratorIT_ES2_@__ZSt18uninitialized_copyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_ET0_T_S8_S7_@__ZSt18uninitialized_copyIP11IndexHolderIiES2_ET0_T_S4_S3_@__ZSt18uninitialized_copyISt13move_iteratorIPiES1_ET0_T_S4_S3_@__ZSt19__iterator_categoryIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E17iterator_categoryERKS8_@__ZSt19__iterator_categoryIP11IndexHolderIiEENSt15iterator_traitsIT_E17iterator_categoryERKS4_@__ZSt19__iterator_categoryIP7DataPtrI11IndexHolderIiEEENSt15iterator_traitsIT_E17iterator_categoryERKS6_@__ZSt19__iterator_categoryIP7DataPtrIiEENSt15iterator_traitsIT_E17iterator_categoryERKS4_@__ZSt19__iterator_categoryIPiENSt15iterator_traitsIT_E17iterator_categoryERKS2_@__ZSt19__iterator_categoryIPlENSt15iterator_traitsIT_E17iterator_categoryERKS2_@__ZSt20uninitialized_fill_nIPimiET_S1_T0_RKT1_@__ZSt21__unguarded_partitionIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_S9_T0_@__ZSt21__unguarded_partitionIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEET_SB_SB_SB_T0_@__ZSt21__unguarded_partitionIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_S9_T0_@__ZSt21__unguarded_partitionIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEET_S7_S7_S7_T0_@__ZSt22__copy_move_backward_aILb1EP11IndexHolderIiES2_ET1_T0_S4_S3_@__ZSt22__copy_move_backward_aILb1EP7DataPtrI11IndexHolderIiEES4_ET1_T0_S6_S5_@__ZSt22__copy_move_backward_aILb1EP7DataPtrIiES2_ET1_T0_S4_S3_@__ZSt22__copy_move_backward_aILb1EPiS0_ET1_T0_S2_S1_@__ZSt22__final_insertion_sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt22__final_insertion_sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_@__ZSt22__final_insertion_sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt22__final_insertion_sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_@__ZSt22__move_median_to_firstIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_S9_T0_@__ZSt22__move_median_to_firstIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_SB_T0_@__ZSt22__move_median_to_firstIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_S9_T0_@__ZSt22__move_median_to_firstIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_S7_T0_@__ZSt22__uninitialized_copy_aIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_iET0_T_S8_S7_RSaIT1_E@__ZSt22__uninitialized_copy_aIP11IndexHolderIiES2_S1_ET0_T_S4_S3_RSaIT1_E@__ZSt22__uninitialized_copy_aISt13move_iteratorIPiES1_iET0_T_S4_S3_RSaIT1_E@__ZSt22__uninitialized_move_aIPiS0_SaIiEET0_T_S3_S2_RT1_@__ZSt23__copy_move_backward_a2ILb1EP11IndexHolderIiES2_ET1_T0_S4_S3_@__ZSt23__copy_move_backward_a2ILb1EP7DataPtrI11IndexHolderIiEES4_ET1_T0_S6_S5_@__ZSt23__copy_move_backward_a2ILb1EP7DataPtrIiES2_ET1_T0_S4_S3_@__ZSt23__copy_move_backward_a2ILb1EPiS0_ET1_T0_S2_S1_@__ZSt24__uninitialized_fill_n_aIPimiiET_S1_T0_RKT1_RSaIT2_E@__ZSt25__unguarded_linear_insertIP11IndexHolderIiEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEEvT_T0_@__ZSt25__unguarded_linear_insertIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS3_EEEEvT_T0_@__ZSt25__unguarded_linear_insertIP7DataPtrIiEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEEvT_T0_@__ZSt25__unguarded_linear_insertIPiN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEEEvT_T0_@__ZSt25__uninitialized_default_nIPSt4pairIiiEmET_S3_T0_@__ZSt25__uninitialized_default_nIPimET_S1_T0_@__ZSt26__unguarded_insertion_sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt26__unguarded_insertion_sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_@__ZSt26__unguarded_insertion_sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt26__unguarded_insertion_sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_@__ZSt27__unguarded_partition_pivotIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_T0_@__ZSt27__unguarded_partition_pivotIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEET_SB_SB_T0_@__ZSt27__unguarded_partition_pivotIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_T0_@__ZSt27__unguarded_partition_pivotIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEET_S7_S7_T0_@__ZSt27__uninitialized_default_n_aIPSt4pairIiiEmS1_ET_S3_T0_RSaIT1_E@__ZSt27__uninitialized_default_n_aIPimiET_S1_T0_RSaIT1_E@__ZSt32__make_move_if_noexcept_iteratorIP11IndexHolderIiES2_ET0_T_@__ZSt32__make_move_if_noexcept_iteratorIPiSt13move_iteratorIS0_EET0_T_@__ZSt34__uninitialized_move_if_noexcept_aIP11IndexHolderIiES2_SaIS1_EET0_T_S5_S4_RT1_@__ZSt34__uninitialized_move_if_noexcept_aIPiS0_SaIiEET0_T_S3_S2_RT1_@__ZSt3maxImERKT_S2_S2_@__ZSt4__lgl@__ZSt4copyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_ET0_T_S8_S7_@__ZSt4copyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES6_ET0_T_S8_S7_@__ZSt4copyIP11IndexHolderIiES2_ET0_T_S4_S3_@__ZSt4copyIP7DataPtrI11IndexHolderIiEES4_ET0_T_S6_S5_@__ZSt4copyIP7DataPtrIiES2_ET0_T_S4_S3_@__ZSt4copyIPiS0_ET0_T_S2_S1_@__ZSt4copyISt13move_iteratorIPiES1_ET0_T_S4_S3_@__ZSt4moveIR11IndexHolderIiEEONSt16remove_referenceIT_E4typeEOS4_@__ZSt4moveIR7DataPtrI11IndexHolderIiEEEONSt16remove_referenceIT_E4typeEOS6_@__ZSt4moveIR7DataPtrIiEEONSt16remove_referenceIT_E4typeEOS4_@__ZSt4moveIRPiEONSt16remove_referenceIT_E4typeEOS3_@__ZSt4moveIRiEONSt16remove_referenceIT_E4typeEOS2_@__ZSt4sortIP11IndexHolderIiESt4lessIS1_EEvT_S5_T0_@__ZSt4sortIP7DataPtrI11IndexHolderIiEESt4lessIS3_EEvT_S7_T0_@__ZSt4sortIP7DataPtrIiESt4lessIS1_EEvT_S5_T0_@__ZSt4sortIPiSt4lessIiEEvT_S3_T0_@__ZSt4swapI11IndexHolderIiEEvRT_S3_@__ZSt4swapI7DataPtrI11IndexHolderIiEEEvRT_S5_@__ZSt4swapI7DataPtrIiEEvRT_S3_@__ZSt4swapIPiEvRT_S2_@__ZSt4swapIiEvRT_S1_@__ZSt4swapIiSaIiEEvRSt6vectorIT_T0_ES5_@__ZSt5mergeIP11IndexHolderIiES2_S2_ET1_T_S4_T0_S5_S3_@__ZSt5mergeIP7DataPtrI11IndexHolderIiEES4_S4_ET1_T_S6_T0_S7_S5_@__ZSt5mergeIP7DataPtrIiES2_S2_ET1_T_S4_T0_S5_S3_@__ZSt5mergeIPiS0_S0_ET1_T_S2_T0_S3_S1_@__ZSt6__sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt6__sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_@__ZSt6__sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_@__ZSt6__sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_@__ZSt6fill_nIPimiET_S1_T0_RKT1_@__ZSt7__mergeIP11IndexHolderIiES2_S2_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S7_T0_S8_S6_T2_@__ZSt7__mergeIP7DataPtrI11IndexHolderIiEES4_S4_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S9_T0_SA_S8_T2_@__ZSt7__mergeIP7DataPtrIiES2_S2_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S7_T0_S8_S6_T2_@__ZSt7__mergeIPiS0_S0_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S5_T0_S6_S4_T2_@__ZSt7advanceIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEmEvRT_T0_@__ZSt7advanceIP11IndexHolderIiElEvRT_T0_@__ZSt7advanceIP7DataPtrI11IndexHolderIiEElEvRT_T0_@__ZSt7advanceIP7DataPtrIiElEvRT_T0_@__ZSt7advanceIPilEvRT_T0_@__ZSt7advanceIPllEvRT_T0_@__ZSt7forwardI11IndexHolderIiEEOT_RNSt16remove_referenceIS2_E4typeE@__ZSt7forwardIR11IndexHolderIiEEOT_RNSt16remove_referenceIS3_E4typeE@__ZSt8_DestroyI11IndexHolderIiEEvPT_@__ZSt8_DestroyIP11IndexHolderIiEEvT_S3_@__ZSt8_DestroyIP11IndexHolderIiES1_EvT_S3_RSaIT0_E@__ZSt8_DestroyIPSt4pairIiiEEvT_S3_@__ZSt8_DestroyIPSt4pairIiiES1_EvT_S3_RSaIT0_E@__ZSt8_DestroyIPiEvT_S1_@__ZSt8_DestroyIPiiEvT_S1_RSaIT0_E@__ZSt8distanceIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E15difference_typeES8_S8_@__ZSt8distanceIP11IndexHolderIiEENSt15iterator_traitsIT_E15difference_typeES4_S4_@__ZSt8distanceIP7DataPtrI11IndexHolderIiEEENSt15iterator_traitsIT_E15difference_typeES6_S6_@__ZSt8distanceIP7DataPtrIiEENSt15iterator_traitsIT_E15difference_typeES4_S4_@__ZSt8distanceIPiENSt15iterator_traitsIT_E15difference_typeES2_S2_@__ZSt8distanceIPlENSt15iterator_traitsIT_E15difference_typeES2_S2_@__ZSt9__advanceIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEElEvRT_T0_St26random_access_iterator_tag@__ZSt9__advanceIP11IndexHolderIiElEvRT_T0_St26random_access_iterator_tag@__ZSt9__advanceIP7DataPtrI11IndexHolderIiEElEvRT_T0_St26random_access_iterator_tag@__ZSt9__advanceIP7DataPtrIiElEvRT_T0_St26random_access_iterator_tag@__ZSt9__advanceIPilEvRT_T0_St26random_access_iterator_tag@__ZSt9__advanceIPllEvRT_T0_St26random_access_iterator_tag@__ZSt9iter_swapIP11IndexHolderIiES2_EvT_T0_@__ZSt9iter_swapIP7DataPtrI11IndexHolderIiEES4_EvT_T0_@__ZSt9iter_swapIP7DataPtrIiES2_EvT_T0_@__ZSt9iter_swapIPiS0_EvT_T0_@__ZZN3par12Mpi_datatypeIbE5valueEvE5first€Àáÿÿÿÿÿÿÿ@__ZZN3par12Mpi_datatypeIbE5valueEvE8datatype@__ZdaPv€Ð @__ZdlPv@__Znam@__Znwm@__ZnwmPv€Àýÿÿÿÿÿÿÿrð>@_MPI_Allgatherrø>@_MPI_Allgathervr€>@_MPI_Alloc_memrˆ>@_MPI_Allreducer>@_MPI_Alltoallr˜>@_MPI_Alltoallvr >@_MPI_Barrierr¨>@_MPI_Bcastr°>@_MPI_Comm_creater¸>@_MPI_Comm_grouprÀ>@_MPI_Comm_rankrÈ>@_MPI_Comm_sizerÐ>@_MPI_FinalizerØ>@_MPI_Free_memrà>@_MPI_Group_inclrè>@_MPI_Initrð>@_MPI_Issendrø>@_MPI_Putr€ >@_MPI_Recvrˆ >@_MPI_Scanr >@_MPI_Sendrecvr˜ >@_MPI_Type_commitr  >@_MPI_Type_contiguousr¨ >@_MPI_Waitr° >@_MPI_Win_creater¸ >@_MPI_Win_fencerÀ >@_MPI_Win_freerÈ >@__ZNSolsEPFRSoS_ErÐ >@__ZNSolsEdrØ >@__ZNSolsEirà >@__ZNSt8ios_base4InitC1Evrè >@__ZSt17__throw_bad_allocvrð >@__ZSt20__throw_length_errorPKcrø >@__ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKcr !>@___cxa_begin_catchr¨!>@___cxa_end_catchr°!>@___cxa_rethrowr¸!>@___cxa_throw_bad_array_new_lengthrÀ!>@_GOMP_parallelrÈ!>@_omp_get_max_threadsrÐ!>@_omp_get_num_threadsrØ!>@_omp_get_thread_numrà!>@_omp_get_wtimerè!>@_omp_set_num_threadsrð!>@__Unwind_Resumerø!>@___assert_rtnr€">@___cxa_atexitrˆ">@_memcpyr">@_memmover˜">@_rand__main,mh_execute_header(Z1ðNgStÑnwmPv£ZN3par12Mpi_datatypeIbE5valueEvEÂÌ9__gnu_cxxª3©SÚKº7omp_parâ11IndexHolderIiE†5binOpŽÊ5__ops1Š1â eqIPKð%miIP¬†neIPiSt6vectorIiSaIiEEEEbRKNS_17__normal_iteratorIT_T0_EESA_æ³6__iter_¡5_Ì4_ºFless_iterEvÆcomp_iterISt4lessI QØ¢_ìIter_comp_iterISt4lessI”hiter_ýval_¦less_valEv comp_valISt4lessIÎkÞ¢less_iterEvËcomp_iterISt4lessIÛ0ä¢ 4þ8¶1‡2”!3û#7.6‹R5mergeIPßS9žb__lglmoveIR¿s…(copyIùvꢑ£paráseq10UpperBoundI11IndexHolderIiEEEijPKT_jRS4_¡ 1ó2“8Mpi_“'2Mpi_ø0ý 31bitonicSortIiEEvRSt6vectorIT_SaIS2_EEi©8bitonicSort_binaryIiEEvRSt6vectorIT_SaIS2_EEi'5splitCommBinaryEiPi„ÌdatatypeIiE5valueEvÔAlltoallIiEEiPT_S2_ii§ SendrecvIiiEEiPT_iiiPT0_iiiiP10MPI_StatusêU££aIåtÊiESt4pairIiiEEé+11IndexHolderIiEEñ@C™D°1Evª2ERKS_ö4®£2Ev¾1EvÄÊ£æ£6vectorIë1± 4pairIiiEù2ŒLiSaIiEE¨11IndexHolderIiESaIS1_EEµSt4pairIiiESaIS1_EE¼ C1EmRKÿD1Ev– 6œ ixEm÷ 21_M_default_initializeEmª15³ 4swapERS1_ö 3endEvàqS0_ iRKS0_º*‚¤ä¤resizeEmñ insertIN9__gnu_cxx17__normal_iteratorIPiS1_EEvEES6_NS4_IPKiS1_EET_SA_òq¨¥´¦sampleSortIiEEiRSt6vectorIT_SaIS2_EEiÜ partitionWIiEEiRSt6vectorIT_SaIS2_EEPFjPKS2_Ei¯MÐ'Ô¦3new_allocatorIº 7__normal_iteratorIPˆ04__alloc_traitsISaIiEE10_S_on_swapERS1_S3_ð4iEä St4pairIiiEE£D11IndexHolderIiEEüUC2E— D2Ev« 10deallocateEPim†L8allocateEmPKvñNv¥ RKS1_€Lü¾ˆ¿2_Ž 6allocator_traitsISaIü43move_iteratorIPiEC1ES0_÷N0_Iter_baseIá‰1__copy_moveILbصVector_baseI° Destroy_auxILbÒ6iSaIiEEí 11IndexHolderIiESaIS1_EE„)St4pairIiiESaIS1_EE±,1†C2EmRKS0_žD2Ev¤2_Vector_implî9_M_get_Tp_allocatorEv°7_M_create_storageEmˆ!3_M_deallocateEPimŽ!1_M_allocateEmõ#D1Ev˜C1ERKS0_‚!12_M_swap_dataERS2_ê4”¿°¿ŽÀÞÀ¢Á_DestroyIÐdistanceI¯GPî11IndexHolderIiEEvPT_úri•11IndexHolderIiE“*St4pairIiiEÚ-iEvT_S1_RSaIT0_E´EvT_S1_é#°ÁStï9__gnu_cxxý11IndexHolderIiEl… 7DataPtrIý±6vectorI‰1åM4lessIÇdiSaIiEEÍSt4pairIiiESaIS1_EE4sizeEvú11IndexHolderIiESaIS1_EEë4sizeEv—512_M_check_lenEmPKcï#3endEvê%8max_sizeEvº76cbeginEv¦†ÚÁ7_M_default_appendEmè5_M_î8_M_“A9_M_emplace_back_auxIJiEEEvDpOT_¼Ì€Âerase_at_endEPiÜrange_insertIN9__gnu_cxx17__normal_iteratorIPiS1_EEEEvS6_T_S7_St20forward_iterator_tag ŸŠÆ1Š4scanIiiEEvPT_S2_T0_­ 5mergeIPÔ;0merge_sortIP±5merge_sort_ptrsIPµ%iÖ11IndexHolderIiE®7DataPtrI¿<EEvT_S2_ûSt4lessIiEEEvT_S4_T0_¯%ÏÆMpi_All¸defaultWeightIiEEjPKT_'splitComm2wayEÌreduceIiEEiPT_S2_iii‡toallvIiEEiPT_PiS3_S2_S3_S3_ið gatherIiEEiPT_S2_iiÀ*žÇemptyEv£beginEvä%æÇ§È³ËC1EvíD1Evó9push_backEOS1_ßixEmå5beginEv÷1Ÿ/èÞ„ßC1EvaSERKS0_«,Èß7¾2Par_bitonic_merge_incrIiEEvRSt6vectorIT_SaIS2_EEii‡'0Mpi_Alltoallv_sparseIiEEiPT_PiS3_S2_S3_S3_iÿ'1‘?6AdjustCommunicationPatternERSt6vectorIiSaIiEES3_S3_S3_i¶ÌSorted_approx_Select_skewedIiEESt6vectorISt4pairIT_iESaIS4_EERS1_IS3_SaIS3_EEji¶splitCommUsingSplittingRankEiPiiªÌèßD1EvôixEm€C1EmRKS2_¥,21_M_default_initializeEmùD˜óÜó‚ôC1E D1Ev¹aSERKS0_¢Fim³v–FRKS0_”[¢ôÊô11IndexHolderIiEEONSt16remove_referenceIT_E4typeEOS4_ÙPiEONSt16remove_referenceIT_E4typeEOS3_îKiEONSt16remove_referenceIT_E4typeEOS2_×z7DataPtrIæ˜Õôäô–õ4sizeEv¨5Û3endEvü/12_M_check_lenEmPKcêZ8max_sizeEv¤s¶õEEvT_S4_ÕSt4lessIS2_EEEvT_S6_T0_ê/ÜõemptyEvñbeginEvö/¬öîö15__ops1¦Y7__normal_iteratorIP¸3new_allocatorIÂe11IndexHolderIiESt6vectorIS2_SaIS2_EEEdeEviSt6vectorIiSaIiEEEÏ K¼>”÷ 1ç3æ15__alloc_on_swapISaIiEEvRT_S2_úK8ýN9__iterator_categoryI¬[0_ã]6__inâh2__ñt4__åwupper_boundIPŽlower_boundIPÆ*__„r11IndexHolderIiES1_St4lessIS1_EET_S5_S5_RKT0_T1_ÈiiÎ¥÷St4lessIiEET_S3_S3_RKT0_T1_ÿET_S1_S1_RKT0_ã+î÷eERKS0_› tERKS0_ët¸øùàù»úbeginEvÉ clearEvü ÒþdeEvê plElæq4baseEv †øþ‰ÿúÿÌ€ò€¼–‚7__uní"5__uní52__´84__uninitialized_fill_n_aIPimiiET_S1_T0_RKT1_RSaIT2_E Y0uninitialized_fill_nIPimiET_S1_T0_RKT1_€s6__unguarded_insertion_sortIPþ{1__unguarded_partitionIPè3__copy_move_backward_a2ILb1EPþ¤initialized_default_n_aIP§#guarded_partition_pivotIP²yimiET_S1_T0_RSaIT1_Eã#St4pairIiiEmS1_ET_S3_T0_RSaIT1_EžZË‚ô‚šƒä„4__uninitialized_move_if_noexcept_aIPÞ$maxImERKT_S2_S2_À72__make_move_if_noexcept_iteratorIPð7iS0_SaIiEET0_T_S3_S2_RT1_©%11IndexHolderIiES2_SaIS1_EET0_T_S5_S4_RT1_öZ—…ë…iEEvT_S2_Þ%11IndexHolderIiEEEvT_S4_ð/ŒŽØ‘ˆ’iSt6vectorIiSaIiEEEEbRKNS_17__normal_iteratorIT_T0_EESB_û&11IndexHolderIiESt6vectorIS2_SaIS2_EEEEbRKNS_17__normal_iteratorIT_T0_EESD_‚0¹’õ’û“«•ScanIiEEiPT_S2_iiiÊ'RecvIiEEiPT_iiiiP10MPI_Statusù'º•pi_IssendIiEEiPT_iiiiPió'erge˜@–Û–µ—wapI–(ortIPÿ:iÒ(PiEvRT_S2_ôK11IndexHolderIiEEvRT_S3_´À7DataPtrI»ÅSaIiEEvRSt6vectorIT_T0_ES5_þ(EvRT_S1_à·„˜1˜)C2Ev*D2Ev‡*2_Vector_implë)9_M_get_Tp_allocatorEv*3_M_deallocateEPS1_m‡A1_M_allocateEmðZD1Evû)C1Evë@ª˜Ƙ☲™S1_EvT_S3_RSaIT0_E´*EvT_S3_AÀ™ê™Ôšii¬+llSt4lessIlEET_S3_S3_RKT0_T1_ÙS11IndexHolderIiES1_St4lessIS1_EET_S5_S5_RKT0_T1_Ž[7DataPtrI®pET_S1_S1_RKT0_Ý+St4lessIiEET_S3_S3_RKT0_T1_ÓS¯›ï›Có+D‹,1Ev…,2ERKS1_ÞZ°œ2Ev™,1EvŸ,̜蜄æ1Ê,D2EvÎ-C2EmRKS2_óD2_Vector_impl´-9_M_get_Tp_allocatorEvÔ-3_M_deallocateEPS1_mÿD7_M_create_storageEm˜Z1_M_allocateEm†sD1EvÈ-C1ERKS2_’Z’ž®žþžS1_EvT_S3_RSaIT0_Eû-EvT_S3_…EŒŸforwardI¥.advanceIáI__mergeIPŽm11IndexHolderIiEEOT_RNSt16remove_referenceIS2_E4typeE™/R11IndexHolderIiEEOT_RNSt16remove_referenceIS3_E4typeE¼´¶Ÿ2emplace_backIJS1_EEEvDpOT_ä/9_M_emplace_back_auxIJS1_EEEvDpOT_ŠFÄŸÆ í©¼®ì®¯11IndexHolderIiESt6vectorIS2_SaIS2_EEEC1ERKS3_Õ0iSt6vectorIiSaIiEEEÏ4KŽ=Ú¯11IndexHolderIiEEEENS0_14_Val_comp_iterIT_EE¹1iEEENS0_14_Val_comp_iterIT_EEÒ37DataPtrIç«S7_à1NS0_15_Iter_comp_iterIS7_EE± ÷¯__€2move_backwardIPš’upper_boundIPÖ2lower_boundIP‡Bheap_selectIPðŒcopy_move_aILbË¡adjust_heapIP¬¶11IndexHolderIiES1_N9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEET_S9_S9_RKT0_T1_Ì3iiN9__gnu_cxx5__ops14_Val_ÿ3°S5_ù3NS0_15_Iter_comp_iterIS5_EEæ’¼±comp_iterISt4lessIiEEEET_S7_S7_RKT0_T1_É4less_iterEET_S4_S4_RKT0_T1_Dâ±C1ERKS1_ä4pLEl¨Ç‚³ ³ü³¢´iEE©511IndexHolderIiEEE‹ESt4pairIiiEEE¤Z1³58Æ70deallocateERS0_Pimç51_S_max_sizeIKS0_vEEmRT_i¼eÈ´initialized_default_nIP¤6guarded_linear_insertIPì’imET_S1_T0_Ì6St4pairIiiEmET_S3_T0_Œsö´1EE9__destroyIP70EE9__destroyIP11IndexHolderIiEEEvT_S5_šYiEEvT_S3_´7St4pairIiiEEEvT_S5_ØZŸµ®µеallocateERS0_mê7max_sizeERKS0_ßMûµiSt13move_iteratorIS0_EET0_T_®811IndexHolderIiES2_ET0_T_Õs¥¶uninitialized_˜9final_insertion_sortIP®jmove_median_to_firstIPºcopy_move_backward_aILb1EPæ·copy_aIÉ9move_aIPiS0_SaIiEET0_T_S3_S2_RT1_ø³St13move_iteratorIPiES1_iET0_T_S4_S3_RSaIT1_Eù:P11IndexHolderIiES2_S1_ET0_T_S4_S3_RSaIT1_EÛsN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_iET0_T_S8_S7_RSaIT1_E°´ʶiSt4lessIiEEvT_S3_T0_Î;11IndexHolderIiESt4lessIS1_EEvT_S5_T0_F7DataPtrIºTû¶iSt4lessIiEEEvT_S4_S4_S4_S4_iT0_¹<11IndexHolderIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_œF7DataPtrI‡U½·iESt4lessIS2_EEEvT_S6_T0_ˆ=11IndexHolderIiEESt4lessIS4_EEEvT_S8_T0_¨FïÂiSt6vectorIiSaIiEEEC1Ù=11IndexHolderIiESt6vectorIS2_SaIS2_EEEC1ERKS4_®FERKS2_¶>IPiEERKNS0_IT_NS_11__enable_ifIXsrSt10__are_sameIS9_S8_E7__valueES5_E6__typeEEEìqËiSt6vectorIiSaIiEEE4baseEv‹?11IndexHolderIiESt6vectorIS2_SaIS2_EEE4baseEv´F®ËPar_bitonic_sort_Ä?splitCommBinaryNoFlipEiPiŠÌincrIiEEvRSt6vectorIT_SaIS2_EEiiŒ@decrIiEEvRSt6vectorIT_SaIS2_EEii’@¼ËëÌSplitIiEEvRSt6vectorIT_SaIS2_EEiiiå@ListsIiEEvRSt6vectorIT_SaIS2_EES5_iðUšÎ¼ÒD2EvAC2EvöUúÒ–ÓËÓfill_initializeEmRKiBinsert_dispatchIN9__gnu_cxx17__normal_iteratorIPiS1_EEEEvS6_T_S7_St12__false_typeÓ‡òÓiiN9__gnu_cxx5__ops14_Iter_ÏCllN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIlEEEET_S7_S7_RKT0_T1_ˆm11IndexHolderIiES1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEET_S9_S9_RKT0_T1_Çt7DataPtrIÖƒless_valEET_S4_S4_RKT0_T1_—Dcomp_valISt4lessIiEEEET_S7_S7_RKT0_T1_ülºÔÙÕC2EÙDD2EvíD10deallocateEPS2_m’s8allocateEmPKv¦ŸvçDRKS3_˜søÖ„××îײØçØ9constructIS1_JS1_EEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS2_PT_DpOS5_„F1ñV7destroyIS1_EEvRS2_PT_üZ8ªsÙÆÙ¯ÝòÝ“ÞÞê–ë¸óÖóVal_comp_iterISt4lessIîFIter_comp_valISt4lessIŸ}11IndexHolderIiEEE˜GiEE£K7DataPtrI‘½C1ES5_©GclIóJäóPžHN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E15difference_typeES8_S8_ì³11IndexHolderIiEENSt15iterator_traitsIT_E15difference_typeES4_S4_ÛIiENSt15iterator_traitsIT_E15difference_typeES2_S2_ºKlENSt15iterator_traitsIT_E15difference_typeES2_S2_±~7DataPtrIÜïóP¨JN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEmEvRT_T0_ª´11IndexHolderIiElEvRT_T0_íJilEvRT_T0_ÀKllEvRT_T0_·~7DataPtrIÈ®ôKS4_PS4_EEbRT_T0_KS4_PS4_EEbRT_T0_à´öôC1ES3_´KclIÆK¤õ¯õîõKiPiEEbRT_T0_èKiPiEEbRT_T0_Ì¥¶öãöñöÃ÷ò÷‚ø7__uninitialized_default_n_1ILbüL0__•f2__uninitialized_fill_nILb1EE15__uninit_fill_nIPimiEET_S3_T0_RKT1_Ù‡1EE18__uninit_default_nIPimEET_S3_T0_ÙM0EE18__uninit_default_nIPSt4pairIiiEmEET_S5_T0_凥øÕø2_Vector_baseI•N3move_iteratorIPiE4baseEvÒµiSaIiEE19_M_get_Tp_allocatorEvëN11IndexHolderIiESaIS1_EE19_M_get_Tp_allocatorEvñ‡ôø‚ùÄùuninitialized_copyI†P__do_alloc_on_swapISaIiEEvRT_S2_St17integral_constantIbLb0EE°emake_move_iteratorIPiESt13move_iteratorIT_ES2_è¿St13move_iteratorIPiES1_ET0_T_S4_S3_šQP11IndexHolderIiES2_ET0_T_S4_S3_ý‡N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_ET0_T_S8_S7_¨ÀÞùiEEENS0_15_Iter_comp_iterIT_EES5_…R11IndexHolderIiEEEENS0_15_Iter_comp_iterIT_EES7_‚[7DataPtrI¶nú__sortIP´Rfill_nIPimiET_S1_T0_RKT1_¶eiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_ÍS11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_ˆ[7DataPtrI—oµú³ûüûiS0_S0_ET1_T_S2_T0_S3_S1_´T11IndexHolderIiES2_S2_ET1_T_S4_T0_S5_S3_š[7DataPtrIqÅüiESt4lessIS1_EEvT_S5_T0_U11IndexHolderIiEESt4lessIS3_EEvT_S7_T0_ [•ýiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_äU11IndexHolderIiEESt4lessIS4_EEEvT_S8_S8_S8_S8_iT0_¦[×ý‰‰ù‰¬•D2EvëVC2Evøq10deallocateEPS2_mþq9constructIS2_JS2_EEEvPT_DpOT0_žs8allocateEmPKv÷‡7destroyIS2_EEvPT_ƒˆÈ•0“X2_S_constructIS1_JS1_EEENSt9enable_ifIXsrSt6__and_IJNS3_18__construct_helperIT_JDpT0_EE4typeEEE5valueEvE4typeERS2_PS8_DpOS9_äZ1_S_max_sizeIKS2_vEEmRT_iûŸdeallocateERS2_PS1_m”Y_S_destroyIS1_EENSt9enable_ifIXsrSt6__and_IJNS3_16__destroy_helperIT_E4typeEEE5valueEvE4typeERS2_PS8_ásÓ•–¹–4_ÄY5_Iter_less_iterclIPÃ~Iter_less_valclIPiKiEEbT_RT0_†ZVal_less_iterclIKiPiEEbRT_T0_ŒZê–Ž—²—ü—Õ˜10deallocateERS2_PS1_mÒZ8allocateERS2_m߇þ˜¬™¼™â™œšæ›™œíœ“¹·ž€Ÿ«ŸûŸ½ Pœ\N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E17iterator_categoryERKS8_Ü¿11IndexHolderIiEENSt15iterator_traitsIT_E17iterator_categoryERKS4_Ý]iENSt15iterator_traitsIT_E17iterator_categoryERKS2_˜elENSt15iterator_traitsIT_E17iterator_categoryERKS2_†”7DataPtrI‰®ï«_÷]ConstructI²ŸdistanceIó^fill_n_aIPimiEN9__gnu_cxx11__enable_ifIXsrSt11__is_scalarIT1_E7__valueET_E6__typeES6_T0_RKS4_óvpop_heapIP”£P€`N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E15difference_typeES8_S8_St26random_access_iterator_tagâ¿11IndexHolderIiEENSt15iterator_traitsIT_E15difference_typeES4_S4_St26random_access_iterator_tag˜biENSt15iterator_traitsIT_E15difference_typeES2_S2_St26random_access_iterator_tagželENSt15iterator_traitsIT_E15difference_typeES2_S2_St26random_access_iterator_tagŒ”7DataPtrI¯ù«__advanceI¼biter_swapIP¼¤P¡cN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEElEvRT_T0_St26random_access_iterator_tag¢À11IndexHolderIiElEvRT_T0_St26random_access_iterator_tagÁdilEvRT_T0_St26random_access_iterator_tag¤ellEvRT_T0_St26random_access_iterator_tag’”7DataPtrIѰ™¬11IndexHolderIiEEclERKS1_S4_’eiEclERKiS2_ªelEclERKlS2_˜”7DataPtrI±¬ë¬õ¬•­¾­á­ð­¨®iE8max_sizeEvf11IndexHolderIiEE8max_sizeEv St4pairIiiEE8max_sizeEv¶´Æ®uninitialized_copyILbÌfcopy_move_backwardILb1ELb×Â1EE13__uninit_copyIœg0EE13__uninit_copyIP11IndexHolderIiES4_EET0_T_S6_S5_‡ St13move_iteratorIPiES3_EET0_T_S6_S5_ŽhN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES4_EET0_T_SA_S9_®ÇÚ®iEE½h11IndexHolderIiEEEçs7DataPtrIÑC1ES3_ÜhclIPiS6_EEbT_T0_”’ˆ¯trosort_loopIP†isertion_sortIPÝzilN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_T1_¨j11IndexHolderIiElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_T1_Žt7DataPtrI™€“¯iN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_Èk11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_”t7DataPtrIºȰiEEENS0_14_Iter_comp_valIT_EEÏllEEENS0_14_Iter_comp_valIT_EES5_‚m11IndexHolderIiEEEENS0_14_Iter_comp_valIT_EEšt7DataPtrIÓ‚S5_ölNS0_15_Iter_comp_iterIS5_EEŸÁÙ±ÿ±ž³ijiS0_S0_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S5_T0_S6_S4_T2_°n11IndexHolderIiES2_S2_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S7_T0_S8_S6_T2_Ít7DataPtrI…ã´iEEEENS0_15_Iter_comp_iterIT_EES7_‘o11IndexHolderIiEEEEENS0_15_Iter_comp_iterIT_EES9_Ót…¶iEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_¨p11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_Ùt«¶iES1_St4lessIS1_EET_S5_S5_RKT0_T1_‰q11IndexHolderIiEES3_St4lessIS3_EET_S7_S7_RKT0_T1_ßt©·iES2_S2_ET1_T_S4_T0_S5_S3_Úq11IndexHolderIiEES4_S4_ET1_T_S6_T0_S7_S5_åtò·¸ì¸¬¹Ö¹‚»Ž»addressofIÀrsort_heapIP˜Žmake_heapIPò¡push_heapIP¥Á11IndexHolderIiEEPT_RS2_ôrSt4pairIiiEEPT_RS2_¬Ÿ±»¿»Ú»Œ¼¿¼è¼Œ½œ½ê½allocateERS2_mÏsmax_sizeERKS2_뇌¾¶¾ľõ¾C1ES5_ˆtclIPS4_S8_EEbT_T0_¥ œ¿§¿ÞÀS7_ÁtNS0_15_Iter_comp_iterIS7_EE´Ç÷Á¼ÃìÄ’ÅÆÙÆªÇniter_baseIumiter_baseIœ‹PöuN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt11_Niter_baseIT_E13iterator_typeES8_¢ÇiENSt11_Niter_baseIT_E13iterator_typeES2_ív11IndexHolderIiEENSt11_Niter_baseIT_E13iterator_typeES4_ì´7DataPtrI¦¾óÇÈSt13move_iteratorIPiES1_ET0_T_S4_S3_ßwP†N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEESþ³ÐÈpartial_sortIP‹xcopy_move_a2ILbÊŒiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0_¬y11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_‰ˆ7DataPtrIª–œÉiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEET_S7_S7_T0_Ñz11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_T0_ˆ7DataPtrIÉ—÷ÉËiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_ø{11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_•ˆ7DataPtrIÓ™žËiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_™}11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_›ˆ7DataPtrIìšéÌiEEÐ}lEE~11IndexHolderIiEEE¡ˆ7DataPtrI…œC1ES3_ã}clIPié}ÂÍKiEEbT_RT0_‡~iEEbT_RT0_ÞÇÎÍC1ES3_«~clIPlKlEEbT_RT0_½~üÍ‡ÎÆÎŽÏiS3_EEbT_T0_€11IndexHolderIiES5_EEbT_T0_çˆ7DataPtrI¦ž¼ÏiS0_ET0_T_S2_S1_Ë11IndexHolderIiES2_ET0_T_S4_S3_íˆ7DataPtrIßžßÏiEEEñ11IndexHolderIiEEEEóˆC1ES5_“€clIPS4_S8_EEbT_T0_ «¬ÐiElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_T1_´11IndexHolderIiEElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_T1_›‰·ÐiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_Í‚11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_¡‰îÑiEEEENS0_14_Iter_comp_valIT_EE¨ƒ11IndexHolderIiEEEEENS0_14_Iter_comp_valIT_EE§‰S7_ЃNS0_15_Iter_comp_iterIS7_EEäLJÓiES1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEET_S9_S9_RKT0_T1_û„11IndexHolderIiEES3_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS3_EEEET_SB_SB_RKT0_T1_Õ‰­ÓiES2_S2_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S7_T0_S8_S6_T2_š†11IndexHolderIiEES4_S4_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S9_T0_SA_S8_T2_Û‰ÌÔòÕ€ÖKiSt6vectorIiSaIiEEEENS_17__normal_iteratorIT_T0_E15difference_typeERKS9_SC_͇iSt6vectorIiSaIiEEEENS_17__normal_iteratorIT_T0_E15difference_typeERKS8_SB_ò³°ÖôÖ±×Þ׈ØÍØìØúؼÙîÙÚèڀ܈ÞC1ES5_¸ˆclIPS4_¾ˆàÞKS4_EEbT_RT0_áˆS4_EEbT_RT0_äÉìÞšßÃßC1ES7_•‰clIPS6_SA_EEbT_T0_µà›àÒáS9_ωNS0_15_Iter_comp_iterIS9_EEêÉëâ‘ã°äPÌŠSt13move_iteratorIPiELb1EE7_S_baseES2_Å¡N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEELbôÆiLb0EE7_S_baseES0_–‹11IndexHolderIiELb0EE7_S_baseES2_æ´7DataPtrIá½ÖåSt13move_iteratorIPiEENSt11_Miter_baseIT_E13iterator_typeES4_ÄŒPž”N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt11_Miter_baseIT_E13iterator_typeES8_î¿äå1EPiS0_ET1_T0_S2_S1_êŒ0Eœ•þåiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0_’Ž11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_ 7DataPtrI ¦ÖæiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_´11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_“ 7DataPtrI¿§âçiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_S7_T0_â11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_S9_T0_™ 7DataPtrIب´èiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEET_S7_S7_S7_T0_Ž’11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_S9_T0_Ÿ 7DataPtrIý©ÉêÌëiS0_ET0_T_S2_S1_à’11IndexHolderIiES2_ET0_T_S4_S3_« 7DataPtrI¦«ùëÅìiN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEEEvT_T0_€”11IndexHolderIiEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEEvT_T0_· 7DataPtrIø¬ëìçíñí‘îºîiENSt11_Miter_baseIT_E13iterator_typeES2_–•11IndexHolderIiEENSt11_Miter_baseIT_E13iterator_typeES4_½ 7DataPtrI°²àîPÜ•N9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEESô¿iS0_ET1_T0_S2_S1_¤–11IndexHolderIiES2_ET1_T0_S4_S3_à7DataPtrI£³úîiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_×11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0_É ÒïiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_T0_à˜11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEET_SB_SB_T0_Ï ­ðiEEONSt16remove_referenceIT_E4typeEOS4_Í™11IndexHolderIiEEEONSt16remove_referenceIT_E4typeEOS6_Õ ÆñiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_æš11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_Û ÔñiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_ÿ›11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_á ¦óiEEE¦œ11IndexHolderIiEEEEç C1ES5_½œclIPS4_ýþóiEENSt15iterator_traitsIT_E15difference_typeES4_S4_Â11IndexHolderIiEEENSt15iterator_traitsIT_E15difference_typeES6_S6_„¡‰ôiElEvRT_T0_÷11IndexHolderIiEElEvRT_T0_Š¡ÈôKS4_EEbT_RT0_ žS4_EEbT_RT0_üÉõiES5_EEbT_T0_Ùž11IndexHolderIiEES7_EEbT_T0_¹¡¾õiES2_ET0_T_S4_S3_šŸ11IndexHolderIiEES4_ET0_T_S6_S5_¿¡çõ´öÄþ†ÿSt4pairIiiEJEEvPT_DpOT0_õŸ11IndexHolderIiEJRS1_EEvPT_DpOT0_´”ÿÁÿÞÿòÿÈ€Ô¦‚»„¾…ë…·†݆§ˆÁˆ™‰ô‰‹›‹íŒC1ES7_þ clIPS6_¡ÆÑŽKS6_EEbT_RT0_³¡S6_EEbT_RT0_ˆÊØŽ†¯û1EPiS0_ET1_T0_S2_S1_ì¡0EPÒ¥•iN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_Ž£11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_È´7DataPtrIß¹ÆiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0_¶¤11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_δ7DataPtrIøº€’iS0_EvT_T0_ø¤11IndexHolderIiES2_EvT_T0_Ô´7DataPtrI—¼‚“iS0_ET1_T0_S2_S1_Æ¥11IndexHolderIiES2_ET1_T0_S4_S3_Ú´7DataPtrI쨓€”iS0_ET1_T0_S2_S1_š¦11IndexHolderIiES2_ET1_T0_S4_S3_ò´7DataPtrI™¿­”iEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_¹§11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0_ø´Þ”iEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_Ò¨11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_þ´ê•iEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_S9_T0_÷©11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_SB_T0_„µ¼–iEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_S9_T0_š«11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEET_SB_SB_SB_T0_ејÔ™iES2_ET0_T_S4_S3_á«11IndexHolderIiEES4_ET0_T_S6_S5_–µšiEEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS7_EEò¬11IndexHolderIiEEEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS9_EEœµÍšiEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEEvT_T0_ƒ®11IndexHolderIiEEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS3_EEEEvT_T0_¢µóšiEENSt15iterator_traitsIT_E17iterator_categoryERKS4_Н11IndexHolderIiEEENSt15iterator_traitsIT_E17iterator_categoryERKS6_¨µû›iEENSt15iterator_traitsIT_E15difference_typeES4_S4_St26random_access_iterator_tag˰11IndexHolderIiEEENSt15iterator_traitsIT_E15difference_typeES6_S6_St26random_access_iterator_tag®µ…œiElEvRT_T0_St26random_access_iterator_tag¼±11IndexHolderIiEElEvRT_T0_St26random_access_iterator_tag´µ¥œiEEclERKS1_S4_÷±11IndexHolderIiEEEclERKS3_S6_ºµΜiEltERKS0_ª²11IndexHolderIiEEltERKS2_ÀµøœiEENSt11_Miter_baseIT_E13iterator_typeES4_³11IndexHolderIiEEENSt11_Miter_baseIT_E13iterator_typeES6_ƵiES2_ET1_T0_S4_S3_à³11IndexHolderIiEES4_ET1_T0_S6_S5_̵·žËžŠŸÍŸ6_ET0_T_S8_S7_¤´2_ET0_T_S8_S7_ÞÉ¡ í µ¡æ¡ú¡ˆ¢Ñ¢€¥ò¦˜§ð§¨«¨Ũö¨‚ªÔªé¬ì­™®å®‹¯“°°½°æ°±»±Õ±®²1ELb1ESt26random_access_iterator_tagE8__copy_mIiEEPT_PKS3_S6_S4_¦¶0ELb´¸¿²iliN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_T0_S8_T1_T2_Ú·11IndexHolderIiElS1_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_T0_SA_T1_T2_®À7DataPtrIŽÄ¤³´¶iS0_ET1_T0_S2_S1_®¸11IndexHolderIiES2_ET1_T0_S4_S3_ºÀ7DataPtrIîÅ€·1ESt26random_access_iterator_tagE8__copy_mIµ¹0ESt26random_access_iterator_tagE8__copy_mIP11IndexHolderIiES5_EET0_T_S7_S6_ÀÀiEEPT_PKS3_S6_S4_Ù¹7DataPtrI±Æ±·iEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_òº11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_ÆÀ–¸iEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0_‘¼11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0_ÌÀÖ¹iES2_EvT_T0_ȼ11IndexHolderIiEES4_EvT_T0_ÒÀáºiES2_ET1_T0_S4_S3_‹½11IndexHolderIiEES4_ET1_T0_S6_S5_ØÀ‡»iEEE²½11IndexHolderIiEEEEÞÀC1ES5_Õ½clIS4_PS4_EEbRT_T0_Û½à»ì»iELb0EE7_S_baseES2_ ¾11IndexHolderIiEELb0EE7_S_baseES4_Á™¼iEENSt11_Niter_baseIT_E13iterator_typeES4_“¿11IndexHolderIiEEENSt11_Niter_baseIT_E13iterator_typeES6_“Á§¼iES2_ET1_T0_S4_S3_Ö¿11IndexHolderIiEES4_ET1_T0_S6_S5_™ÁÁ¼ò¼ü¼¡½ƽ6_ET1_T0_S8_S7_œÀ2_ET1_T0_S8_S7_‚Êà½Ó¾ù¾ª¿þÂ÷èăÅÃÆÎÇôÇC1ES7_ÁclIS6_PS6_EEbRT_T0_‡ÁÌÈØÈ…É“É­ÉÞÉiliN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEEEvT_T0_S8_T1_T2_ÑÂ11IndexHolderIiElS1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEEvT_T0_SA_T1_T2_ºÇ7DataPtrIêÇ„Ê1ESt26random_access_iterator_tagE13__copy_move_bIäÃ0ESt26random_access_iterator_tagE13__copy_move_bIP11IndexHolderIiES5_EET0_T_S7_S6_ÀÇiEEPT_PKS3_S6_S4_ˆÄ7DataPtrI•ÉÿËiElS1_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_T0_SA_T1_T2_µÅ11IndexHolderIiEElS3_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_T0_SC_T1_T2_ÆÇ÷ÌiEEvRT_S3_èÅ11IndexHolderIiEEEvRT_S5_ÌÇÐiES2_ET1_T0_S4_S3_«Æ11IndexHolderIiEES4_ET1_T0_S6_S5_ÒÇèÐiEEEPT_PKS5_S8_S6_îÆ11IndexHolderIiEEEEPT_PKS7_SA_S8_ØÇ™Ñ0EE7_S_baseES6_œÇ1EE7_S_baseES6_ØÉþьҦÒÑÒþÒ¤Ó¨ÕŽÖ§ÙÿÙ°Ú–ÛÃÛiElS1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEEvT_T0_SA_T1_T2_É11IndexHolderIiEElS3_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS3_EEEEvT_T0_SC_T1_T2_ðÉéÛiEEEPT_PKS5_S8_S6_ÒÉ11IndexHolderIiEEEEPT_PKS7_SA_S8_öÉéÝáÞþÞÊß÷ßàâ–ãÃãœä8ÒÊ9binLengthEjøÊ5toBinEjjRSt6vectorIbSaIbEEþÊ1ŠË24getºËfastLog2EjòÊbinToDecEPjj„ËÐä€å åÀç2isPowerOfTwoEj´Ë4reversibleHashEjþËðçNextHighestPowerOfTwoEjòËPrevHighestPowerOfTwoEjøË€è°èàè°éÀëbPii¤ÌPKbPii°ÌÀíÐñðóàö8datatypeÛÌ5firstáÌ ì¤ìðŽIˆð•마œ‰’£è裉þã“Ò“£êå Œúɘƞ£ê㞌öŘ֌£êå ŒúÉ™' bDŒ ¨ ^PD*&ŠEOHAŒµD °D& ( 2 &PB&IJI_[—&qR&JZ5)&Ê3T¡Ì01<†°GZZO&P*j[@Ab,P*‚§ Ï01=&Ÿ& \&&.)"+*%1B² ¡¯¯¢>5'HŸŸ ^D5&9éC!Ë 8¢ ?H. ?H-R/#0B1&~IIPB² p³  .81$$JY).&:Ê3T&&~I+PB² )) )#8. µ‘&Ÿ&Ÿ¢&~IP*@*¬ #23)$N"*1' ·™&Ÿ°&~IQICL[™ËY . ?H.#M ·™&Ÿ¦0D=-*EB2[˜ˆX .)M ·™&Ÿ¦XŒR•ƒ-L&| )&X[™ÒX ?H.)MB-VŒR•ƒ-L&ÊX[™ÒY ?H.)L1º‚&X-1ŒR•ƒ-L&ˆ )*%X€L>€Öòs>€û­>€("?€0-„?€™(å?€±<@€?“@€ËÒ@€¬Ì A€Ÿš¢P¢f¢~¢˜¢¯¢âÖ¢ç¢ú¢ ££9£U£u£Š£¬£ãá£ó£ ¤¤3¤I¤_¤q¤ˆ¤ ¤°¤Ò¤ê¤¥¥8¥T¥o¥ƒ¥¡¥¶¥Ò¥ñ¥ ¦#¦9¦N¦k¦„¦£¦¼¦Ò¦í¦ §(§B§Z§l§’§²§ƧÙ§ñ§¨$¨9¨L¨d¨z¨¨¡¨º¨̨â¨õ¨©©9©H©g©ƒ©•©¨©½©שô©ªª=ªXªrªª£ª˪èªýª!«;«Z«x«‘«­«ëÙ«ï« ¬)¬E¬Y¬z¬‘¬ª¬À¬׬í¬­­.­B­T­f­y­Œ­ ­¿­ß­ý­®;®O®c®}®˜®µ®É®á®ó®¯*¯=¯^¯y¯Нž¯³¯ʯٯ篰$°A°_°}°˜°©°¾°ΰã°ñ° ±*±G±Y±q±‘±«±±Ò±å±ù±²0²Q²v²–²¶²Õ²ì²³³*³?³S³r³³§³óÛ³ü³´3´J´Z´j´}´Ž´§´º´Í´ß´ñ´µµ4µZµµµ¯µĵáµüµ¶9¶[¶s¶‹¶¤¶·¶ζå¶ü¶··1·C·T·l··”·©·¹·Ì·Ý·î· ¸¸)¸G¸^¸t¸…¸•¸£¸¯¸»¸̸á¸ú¸¹<¹K¹X¹g¹w¹‘¹œ¹¬¹Á¹Õ¹ë¹ú¹ ºº(º6ºEºTºaºlºwº‡ºšº­º¹ºʺÛºéº÷º»»$»5»E»V»p»‚»“»¯»Ç»Ý»ò»¼¼"¼;¼N¼c¼s¼‚¼‘¼¨¼¾¼Ѽê¼ý¼ ½½1½B½W½h½z½†½›½©½ýӽセ¾"¾:¾T¾m¾¾”¾«¾ɾܾö¾ ¿¿-¿@¿W¿n¿¿”¿¨¿À¿Ø¿ç¿ý¿ ÀÀ8ÀOÀaÀwÀ“À­ÀÄÀáÀüÀÁ!Á7ÁOÁhÁ{ÁŽÁ¡Á°ÁÀÁÔÁîÁÂÂ0ÂMÂh€žºÂÑÂßÂîÂüÂÃÃÃ/Ã>ÃPÃhÃyËÚéþÃÙÃìÃÄÄ'Ä7ÄGÄ]ÄsăēğİĿÄÍÄáÄöÄ ÅÅ%Å6ÅGÅ]ÅrÅ~ʼnŕũŴÅÀÅÑÅåÅôÅÆÆÆ-ÆEÆTÆbÆmÆÆˆÆšÆ¤Æ³Æ¿ÆÑÆåÆÿÆÇ'Ç?ÇJÇZÇdÇqÇljǘǦǼÇÇÇ×ÇâÇüÇÈ-È7ÈKÈYÈhÈyȎȦÈÀÈÙÈøÈÉ)ÉCÉYÉhÉ{ɒɩɾÉÑÉëÉýÉÊ#Ê2ÊCÊTÊdÊpʅʙʣʼÊÎÊÞÊùÊË%Ë4ËBËTËlËˑ˟˭˿Ë×ËéËøËÌÌ$Ì/Ì:ÌYÌx̡̯̿̕ÌÏÌÞÌîÌýÌ ÍÍ&ÍAÍPÍ`ÍqÍ͑ͧͺÍÔÍáÍïÍÎ Î5ÎGÎYÎhÎx·ΜβÎÊÎÙÎëÎ÷ÎÏÏ)Ï9ÏGÏRÏ]ÏhσώϜϮÏÄÏÖÏæÏþÏÐ1ÐEÐXÐfÐzЌОЫжÐÄÐÒÐÞÐòÐÑÑÑ*ÑCÑ\ÑvѕѠÑÜÑÒ<ÒfÒ€nÒ€vÒ€}Ò€„Ò’Ò¬ÒºÒÍÒÞÒòÒÓÓ0Ó9ÓOÓ[ÓiÓxӂӌӖӥӰӻÓÃÓÎÓØÓäÓñÓüÓÔÔÔÔ Ô+Ô6Ô=ÔFÔQÔ]ÔcÔkÔqÔyÔ€ÔˆÔÔ•ÔžÔ¥Ô¬Ô²Ô¹ÔÄÔÌÔÛÔèÔðÔýÔ ÕÕÕ$Õ,Õ3ÕBÕVÕjÕrÕzÕ‚Õ‹Õ”ÕšÕ§Õ¯ÕÄÕÙÕíÕüÕÖÖÖ%Ö,Ö4ÖJÖ]ÖzÖ’Ö¨Ö»ÖÐÖãÖóÖ××%×9×M×d×x׌ע׽×Õ×óרØ1Ø:ØBØHØTØ`ØhØoØuØ{؅،ؕ؛بخضØÁØÍØÖØàØèØòØúØÙ ÙÙ!Ù)Ù1Ù:ÙBÙLÙXÙeÙnÙwـوÙٚ٢٨ٯٷٿÙÉÙÒÙÝÙäÙìÙklmnopqrstuvwxyz{€‚ƒ…‡‰Š‹”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]`x.9;A‡‰œ©³ÀÂÍÒÞßùüVWXY[\_bcdeijlnv¯°±²³Ufh©ªØ|Z]^^_p@klmnopqrstuvwxyz{€‚ƒ…‡‰Š‹”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]`x.9;A‡‰œ©³ÀÂÍÒÞßùüVWXY[\_bcdeijlnv¯°±²³Ufh©ªØ __Z41__static_initialization_and_destruction_0ii__GLOBAL__sub_I_main.cpp__ZN3par10sampleSortIiEEiRSt6vectorIT_SaIS2_EEi._omp_fn.0__ZN3par10sampleSortIiEEiRSt6vectorIT_SaIS2_EEi._omp_fn.1__ZN3par10sampleSortIiEEiRSt6vectorIT_SaIS2_EEi._omp_fn.2__ZN3par10partitionWIiEEiRSt6vectorIT_SaIS2_EEPFjPKS2_Ei._omp_fn.3__ZN3par10partitionWIiEEiRSt6vectorIT_SaIS2_EEPFjPKS2_Ei._omp_fn.4__ZN3par10partitionWIiEEiRSt6vectorIT_SaIS2_EEPFjPKS2_Ei._omp_fn.5__ZN3par10partitionWIiEEiRSt6vectorIT_SaIS2_EEPFjPKS2_Ei._omp_fn.6__ZN3par27Sorted_approx_Select_skewedIiEESt6vectorISt4pairIT_iESaIS4_EERS1_IS3_SaIS3_EEji._omp_fn.7__ZN3par27Sorted_approx_Select_skewedIiEESt6vectorISt4pairIT_iESaIS4_EERS1_IS3_SaIS3_EEji._omp_fn.8__ZN7omp_par10merge_sortIP11IndexHolderIiESt4lessIS2_EEEvT_S6_T0_._omp_fn.9__ZN7omp_par10merge_sortIP11IndexHolderIiESt4lessIS2_EEEvT_S6_T0_._omp_fn.10__ZN7omp_par10merge_sortIP11IndexHolderIiESt4lessIS2_EEEvT_S6_T0_._omp_fn.11__ZN7omp_par10merge_sortIP11IndexHolderIiESt4lessIS2_EEEvT_S6_T0_._omp_fn.12__ZN7omp_par5mergeIP11IndexHolderIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_._omp_fn.13__ZN7omp_par5mergeIP11IndexHolderIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_._omp_fn.14__ZN7omp_par5mergeIP11IndexHolderIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_._omp_fn.15__ZN7omp_par5mergeIP11IndexHolderIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_._omp_fn.16__ZN7omp_par15merge_sort_ptrsIP11IndexHolderIiEEEvT_S4_._omp_fn.17__ZN7omp_par15merge_sort_ptrsIP11IndexHolderIiEEEvT_S4_._omp_fn.18__ZN7omp_par10merge_sortIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_T0_._omp_fn.19__ZN7omp_par10merge_sortIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_T0_._omp_fn.20__ZN7omp_par10merge_sortIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_T0_._omp_fn.21__ZN7omp_par10merge_sortIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_T0_._omp_fn.22__ZN7omp_par5mergeIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_S8_S8_S8_iT0_._omp_fn.23__ZN7omp_par5mergeIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_S8_S8_S8_iT0_._omp_fn.24__ZN7omp_par5mergeIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_S8_S8_S8_iT0_._omp_fn.25__ZN7omp_par5mergeIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_S8_S8_S8_iT0_._omp_fn.26__ZN7omp_par4scanIiiEEvPT_S2_T0_._omp_fn.27__ZN7omp_par4scanIiiEEvPT_S2_T0_._omp_fn.28__ZN7omp_par10merge_sortIPiSt4lessIiEEEvT_S4_T0_._omp_fn.29__ZN7omp_par10merge_sortIPiSt4lessIiEEEvT_S4_T0_._omp_fn.30__ZN7omp_par10merge_sortIPiSt4lessIiEEEvT_S4_T0_._omp_fn.31__ZN7omp_par10merge_sortIPiSt4lessIiEEEvT_S4_T0_._omp_fn.32__ZN7omp_par5mergeIPiSt4lessIiEEEvT_S4_S4_S4_S4_iT0_._omp_fn.33__ZN7omp_par5mergeIPiSt4lessIiEEEvT_S4_S4_S4_S4_iT0_._omp_fn.34__ZN7omp_par5mergeIPiSt4lessIiEEEvT_S4_S4_S4_S4_iT0_._omp_fn.35__ZN7omp_par5mergeIPiSt4lessIiEEEvT_S4_S4_S4_S4_iT0_._omp_fn.36__ZN7omp_par15merge_sort_ptrsIPiEEvT_S2_._omp_fn.37__ZN7omp_par15merge_sort_ptrsIPiEEvT_S2_._omp_fn.38__ZN7omp_par10merge_sortIP7DataPtrIiESt4lessIS2_EEEvT_S6_T0_._omp_fn.39__ZN7omp_par10merge_sortIP7DataPtrIiESt4lessIS2_EEEvT_S6_T0_._omp_fn.40__ZN7omp_par10merge_sortIP7DataPtrIiESt4lessIS2_EEEvT_S6_T0_._omp_fn.41__ZN7omp_par10merge_sortIP7DataPtrIiESt4lessIS2_EEEvT_S6_T0_._omp_fn.42__ZN7omp_par5mergeIP7DataPtrIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_._omp_fn.43__ZN7omp_par5mergeIP7DataPtrIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_._omp_fn.44__ZN7omp_par5mergeIP7DataPtrIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_._omp_fn.45__ZN7omp_par5mergeIP7DataPtrIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0_._omp_fn.46__GLOBAL__sub_I_parUtils.cpp__ZStL19piecewise_construct__ZStL13allocator_arg__ZStL6ignore__ZZN3par10sampleSortIiEEiRSt6vectorIT_SaIS2_EEiE8__func____ZZN3par11bitonicSortIiEEvRSt6vectorIT_SaIS2_EEiE8__func____ZZN3par10MergeListsIiEEvRSt6vectorIT_SaIS2_EES5_iE8__func____ZZN3par10partitionWIiEEiRSt6vectorIT_SaIS2_EEPFjPKS2_EiE8__func____ZStL8__ioinit__ZStL8__ioinitGCC_except_table0GCC_except_table1GCC_except_table2GCC_except_table3GCC_except_table4GCC_except_table5GCC_except_table6GCC_except_table7GCC_except_table8GCC_except_table9GCC_except_table10GCC_except_table11GCC_except_table12GCC_except_table13GCC_except_table14GCC_except_table15GCC_except_table16GCC_except_table17GCC_except_table18GCC_except_table19GCC_except_table20GCC_except_table21GCC_except_table22GCC_except_table23GCC_except_table24GCC_except_table25GCC_except_table26GCC_except_table27GCC_except_table28GCC_except_table29GCC_except_table30GCC_except_table31GCC_except_table32GCC_except_table33GCC_except_table34GCC_except_table35GCC_except_table36GCC_except_table37GCC_except_table38GCC_except_table39GCC_except_table40GCC_except_table41GCC_except_table42GCC_except_table43GCC_except_table44GCC_except_table45GCC_except_table46GCC_except_table47__ZN11IndexHolderIiEC1ERKS0___ZN11IndexHolderIiEC1Eim__ZN11IndexHolderIiEC1Ev__ZN11IndexHolderIiED1Ev__ZN11IndexHolderIiEaSERKS0___ZN3par10MergeListsIiEEvRSt6vectorIT_SaIS2_EES5_i__ZN3par10MergeSplitIiEEvRSt6vectorIT_SaIS2_EEiii__ZN3par10Mpi_IssendIiEEiPT_iiiiPi__ZN3par10partitionWIiEEiRSt6vectorIT_SaIS2_EEPFjPKS2_Ei__ZN3par10sampleSortIiEEiRSt6vectorIT_SaIS2_EEi__ZN3par11bitonicSortIiEEvRSt6vectorIT_SaIS2_EEi__ZN3par12Mpi_AlltoallIiEEiPT_S2_ii__ZN3par12Mpi_SendrecvIiiEEiPT_iiiPT0_iiiiP10MPI_Status__ZN3par12Mpi_datatypeIiE5valueEv__ZN3par13Mpi_AllgatherIiEEiPT_S2_ii__ZN3par13Mpi_AllreduceIiEEiPT_S2_iii__ZN3par13Mpi_AlltoallvIiEEiPT_PiS3_S2_S3_S3_i__ZN3par13defaultWeightIiEEjPKT___ZN3par13splitComm2wayEPKbPii__ZN3par13splitComm2wayEbPii__ZN3par15splitCommBinaryEiPi__ZN3par18bitonicSort_binaryIiEEvRSt6vectorIT_SaIS2_EEi__ZN3par20Mpi_Alltoallv_sparseIiEEiPT_PiS3_S2_S3_S3_i__ZN3par21Par_bitonic_sort_decrIiEEvRSt6vectorIT_SaIS2_EEii__ZN3par21Par_bitonic_sort_incrIiEEvRSt6vectorIT_SaIS2_EEii__ZN3par21splitCommBinaryNoFlipEiPi__ZN3par22Par_bitonic_merge_incrIiEEvRSt6vectorIT_SaIS2_EEii__ZN3par26AdjustCommunicationPatternERSt6vectorIiSaIiEES3_S3_S3_i__ZN3par27Sorted_approx_Select_skewedIiEESt6vectorISt4pairIT_iESaIS4_EERS1_IS3_SaIS3_EEji__ZN3par27splitCommUsingSplittingRankEiPii__ZN3par8Mpi_RecvIiEEiPT_iiiiP10MPI_Status__ZN3par8Mpi_ScanIiEEiPT_S2_iii__ZN3seq10UpperBoundI11IndexHolderIiEEEijPKT_jRS4___ZN5binOp12isPowerOfTwoEj__ZN5binOp14reversibleHashEj__ZN5binOp24getNextHighestPowerOfTwoEj__ZN5binOp24getPrevHighestPowerOfTwoEj__ZN5binOp5toBinEjjRSt6vectorIbSaIbEE__ZN5binOp8binToDecEPjj__ZN5binOp8fastLog2Ej__ZN5binOp9binLengthEj__ZN7omp_par10merge_sortIP11IndexHolderIiEEEvT_S4___ZN7omp_par10merge_sortIP11IndexHolderIiESt4lessIS2_EEEvT_S6_T0___ZN7omp_par10merge_sortIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_T0___ZN7omp_par10merge_sortIP7DataPtrIiESt4lessIS2_EEEvT_S6_T0___ZN7omp_par10merge_sortIPiEEvT_S2___ZN7omp_par10merge_sortIPiSt4lessIiEEEvT_S4_T0___ZN7omp_par15merge_sort_ptrsIP11IndexHolderIiEEEvT_S4___ZN7omp_par15merge_sort_ptrsIPiEEvT_S2___ZN7omp_par4scanIiiEEvPT_S2_T0___ZN7omp_par5mergeIP11IndexHolderIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0___ZN7omp_par5mergeIP7DataPtrI11IndexHolderIiEESt4lessIS4_EEEvT_S8_S8_S8_S8_iT0___ZN7omp_par5mergeIP7DataPtrIiESt4lessIS2_EEEvT_S6_S6_S6_S6_iT0___ZN7omp_par5mergeIPiSt4lessIiEEEvT_S4_S4_S4_S4_iT0___ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEE10deallocateEPS2_m__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEE7destroyIS2_EEvPT___ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEE8allocateEmPKv__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEE9constructIS2_JS2_EEEvPT_DpOT0___ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEEC2Ev__ZN9__gnu_cxx13new_allocatorI11IndexHolderIiEED2Ev__ZN9__gnu_cxx13new_allocatorISt4pairIiiEE10deallocateEPS2_m__ZN9__gnu_cxx13new_allocatorISt4pairIiiEE8allocateEmPKv__ZN9__gnu_cxx13new_allocatorISt4pairIiiEEC2ERKS3___ZN9__gnu_cxx13new_allocatorISt4pairIiiEEC2Ev__ZN9__gnu_cxx13new_allocatorISt4pairIiiEED2Ev__ZN9__gnu_cxx13new_allocatorIiE10deallocateEPim__ZN9__gnu_cxx13new_allocatorIiE8allocateEmPKv__ZN9__gnu_cxx13new_allocatorIiEC2ERKS1___ZN9__gnu_cxx13new_allocatorIiEC2Ev__ZN9__gnu_cxx13new_allocatorIiED2Ev__ZN9__gnu_cxx14__alloc_traitsISaIiEE10_S_on_swapERS1_S3___ZN9__gnu_cxx17__normal_iteratorIP11IndexHolderIiESt6vectorIS2_SaIS2_EEEC1ERKS3___ZN9__gnu_cxx17__normal_iteratorIPK11IndexHolderIiESt6vectorIS2_SaIS2_EEEC1ERKS4___ZN9__gnu_cxx17__normal_iteratorIPKiSt6vectorIiSaIiEEEC1ERKS2___ZN9__gnu_cxx17__normal_iteratorIPKiSt6vectorIiSaIiEEEC1IPiEERKNS0_IT_NS_11__enable_ifIXsrSt10__are_sameIS9_S8_E7__valueES5_E6__typeEEE__ZN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEC1ERKS1___ZN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEpLEl__ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI11IndexHolderIiEEEC1ES5___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI11IndexHolderIiEEEclIPS4_KS4_EEbT_RT0___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI11IndexHolderIiEEEclIPS4_S4_EEbT_RT0___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEC1ES7___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEclIPS6_KS6_EEbT_RT0___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEclIPS6_S6_EEbT_RT0___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrIiEEEC1ES5___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrIiEEEclIPS4_KS4_EEbT_RT0___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessI7DataPtrIiEEEclIPS4_S4_EEbT_RT0___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEC1ES3___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEclIPiKiEEbT_RT0___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEclIPiiEEbT_RT0___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIlEEC1ES3___ZN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIlEEclIPlKlEEbT_RT0___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI11IndexHolderIiEEEC1ES5___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI11IndexHolderIiEEEclIKS4_PS4_EEbRT_T0___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI11IndexHolderIiEEEclIS4_PS4_EEbRT_T0___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEC1ES7___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEclIS6_PS6_EEbRT_T0___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI7DataPtrIiEEEC1ES5___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessI7DataPtrIiEEEclIS4_PS4_EEbRT_T0___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEC1ES3___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEclIKiPiEEbRT_T0___ZN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEclIiPiEEbRT_T0___ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI11IndexHolderIiEEEC1ES5___ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI11IndexHolderIiEEEclIPS4_S8_EEbT_T0___ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEC1ES7___ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEclIPS6_SA_EEbT_T0___ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI7DataPtrIiEEEC1ES5___ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessI7DataPtrIiEEEclIPS4_S8_EEbT_T0___ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEC1ES3___ZN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEclIPiS6_EEbT_T0___ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI11IndexHolderIiEEEENS0_14_Iter_comp_valIT_EENS0_15_Iter_comp_iterIS7_EE__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI11IndexHolderIiEEEENS0_14_Iter_comp_valIT_EES7___ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEENS0_14_Iter_comp_valIT_EENS0_15_Iter_comp_iterIS9_EE__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI7DataPtrI11IndexHolderIiEEEEENS0_14_Iter_comp_valIT_EES9___ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI7DataPtrIiEEEENS0_14_Iter_comp_valIT_EENS0_15_Iter_comp_iterIS7_EE__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessI7DataPtrIiEEEENS0_14_Iter_comp_valIT_EES7___ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessIiEEENS0_14_Iter_comp_valIT_EENS0_15_Iter_comp_iterIS5_EE__ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessIiEEENS0_14_Iter_comp_valIT_EES5___ZN9__gnu_cxx5__ops15__iter_comp_valISt4lessIlEEENS0_14_Iter_comp_valIT_EES5___ZN9__gnu_cxx5__ops15__iter_less_valEv__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessI11IndexHolderIiEEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS7_EE__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessI11IndexHolderIiEEEENS0_14_Val_comp_iterIT_EES7___ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS9_EE__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessI7DataPtrIiEEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS7_EE__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessIiEEENS0_14_Val_comp_iterIT_EENS0_15_Iter_comp_iterIS5_EE__ZN9__gnu_cxx5__ops15__val_comp_iterISt4lessIiEEENS0_14_Val_comp_iterIT_EES5___ZN9__gnu_cxx5__ops15__val_less_iterEv__ZN9__gnu_cxx5__ops16__iter_comp_iterISt4lessI11IndexHolderIiEEEENS0_15_Iter_comp_iterIT_EES7___ZN9__gnu_cxx5__ops16__iter_comp_iterISt4lessI7DataPtrI11IndexHolderIiEEEEENS0_15_Iter_comp_iterIT_EES9___ZN9__gnu_cxx5__ops16__iter_comp_iterISt4lessI7DataPtrIiEEEENS0_15_Iter_comp_iterIT_EES7___ZN9__gnu_cxx5__ops16__iter_comp_iterISt4lessIiEEENS0_15_Iter_comp_iterIT_EES5___ZN9__gnu_cxx5__ops16__iter_less_iterEv__ZN9__gnu_cxxeqIPK11IndexHolderIiESt6vectorIS2_SaIS2_EEEEbRKNS_17__normal_iteratorIT_T0_EESD___ZN9__gnu_cxxeqIPKiSt6vectorIiSaIiEEEEbRKNS_17__normal_iteratorIT_T0_EESB___ZN9__gnu_cxxmiIPKiSt6vectorIiSaIiEEEENS_17__normal_iteratorIT_T0_E15difference_typeERKS9_SC___ZN9__gnu_cxxmiIPiSt6vectorIiSaIiEEEENS_17__normal_iteratorIT_T0_E15difference_typeERKS8_SB___ZN9__gnu_cxxneIPiSt6vectorIiSaIiEEEEbRKNS_17__normal_iteratorIT_T0_EESA___ZNK11IndexHolderIiEleERKS0___ZNK11IndexHolderIiEltERKS0___ZNK7DataPtrI11IndexHolderIiEEltERKS2___ZNK7DataPtrIiEltERKS0___ZNK9__gnu_cxx13new_allocatorI11IndexHolderIiEE8max_sizeEv__ZNK9__gnu_cxx13new_allocatorISt4pairIiiEE8max_sizeEv__ZNK9__gnu_cxx13new_allocatorIiE8max_sizeEv__ZNK9__gnu_cxx17__normal_iteratorIP11IndexHolderIiESt6vectorIS2_SaIS2_EEEdeEv__ZNK9__gnu_cxx17__normal_iteratorIPK11IndexHolderIiESt6vectorIS2_SaIS2_EEE4baseEv__ZNK9__gnu_cxx17__normal_iteratorIPKiSt6vectorIiSaIiEEE4baseEv__ZNK9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEE4baseEv__ZNK9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEdeEv__ZNK9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEplEl__ZNK9__gnu_cxx5__ops14_Iter_less_valclIPiKiEEbT_RT0___ZNK9__gnu_cxx5__ops14_Val_less_iterclIKiPiEEbRT_T0___ZNK9__gnu_cxx5__ops15_Iter_less_iterclIP11IndexHolderIiES5_EEbT_T0___ZNK9__gnu_cxx5__ops15_Iter_less_iterclIP7DataPtrI11IndexHolderIiEES7_EEbT_T0___ZNK9__gnu_cxx5__ops15_Iter_less_iterclIP7DataPtrIiES5_EEbT_T0___ZNK9__gnu_cxx5__ops15_Iter_less_iterclIPiS3_EEbT_T0___ZNKSt12_Vector_baseI11IndexHolderIiESaIS1_EE19_M_get_Tp_allocatorEv__ZNKSt12_Vector_baseIiSaIiEE19_M_get_Tp_allocatorEv__ZNKSt13move_iteratorIPiE4baseEv__ZNKSt4lessI11IndexHolderIiEEclERKS1_S4___ZNKSt4lessI7DataPtrI11IndexHolderIiEEEclERKS3_S6___ZNKSt4lessI7DataPtrIiEEclERKS1_S4___ZNKSt4lessIiEclERKiS2___ZNKSt4lessIlEclERKlS2___ZNKSt6vectorI11IndexHolderIiESaIS1_EE12_M_check_lenEmPKc__ZNKSt6vectorI11IndexHolderIiESaIS1_EE3endEv__ZNKSt6vectorI11IndexHolderIiESaIS1_EE4sizeEv__ZNKSt6vectorI11IndexHolderIiESaIS1_EE5beginEv__ZNKSt6vectorI11IndexHolderIiESaIS1_EE5emptyEv__ZNKSt6vectorI11IndexHolderIiESaIS1_EE8max_sizeEv__ZNKSt6vectorISt4pairIiiESaIS1_EE4sizeEv__ZNKSt6vectorIiSaIiEE12_M_check_lenEmPKc__ZNKSt6vectorIiSaIiEE3endEv__ZNKSt6vectorIiSaIiEE4sizeEv__ZNKSt6vectorIiSaIiEE5beginEv__ZNKSt6vectorIiSaIiEE5emptyEv__ZNKSt6vectorIiSaIiEE6cbeginEv__ZNKSt6vectorIiSaIiEE8max_sizeEv__ZNSaI11IndexHolderIiEEC2Ev__ZNSaI11IndexHolderIiEED2Ev__ZNSaISt4pairIiiEEC1Ev__ZNSaISt4pairIiiEEC2ERKS1___ZNSaISt4pairIiiEED1Ev__ZNSaISt4pairIiiEED2Ev__ZNSaIiEC1Ev__ZNSaIiEC2ERKS___ZNSaIiED1Ev__ZNSaIiED2Ev__ZNSt10_Iter_baseIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEELb0EE7_S_baseES6___ZNSt10_Iter_baseIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEELb1EE7_S_baseES6___ZNSt10_Iter_baseIP11IndexHolderIiELb0EE7_S_baseES2___ZNSt10_Iter_baseIP7DataPtrI11IndexHolderIiEELb0EE7_S_baseES4___ZNSt10_Iter_baseIP7DataPtrIiELb0EE7_S_baseES2___ZNSt10_Iter_baseIPiLb0EE7_S_baseES0___ZNSt10_Iter_baseISt13move_iteratorIPiELb1EE7_S_baseES2___ZNSt11__copy_moveILb0ELb0ESt26random_access_iterator_tagE8__copy_mIP11IndexHolderIiES5_EET0_T_S7_S6___ZNSt11__copy_moveILb0ELb1ESt26random_access_iterator_tagE8__copy_mI7DataPtrI11IndexHolderIiEEEEPT_PKS7_SA_S8___ZNSt11__copy_moveILb0ELb1ESt26random_access_iterator_tagE8__copy_mI7DataPtrIiEEEPT_PKS5_S8_S6___ZNSt11__copy_moveILb0ELb1ESt26random_access_iterator_tagE8__copy_mIiEEPT_PKS3_S6_S4___ZNSt11__copy_moveILb1ELb1ESt26random_access_iterator_tagE8__copy_mIiEEPT_PKS3_S6_S4___ZNSt12_Destroy_auxILb0EE9__destroyIP11IndexHolderIiEEEvT_S5___ZNSt12_Destroy_auxILb1EE9__destroyIPSt4pairIiiEEEvT_S5___ZNSt12_Destroy_auxILb1EE9__destroyIPiEEvT_S3___ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE11_M_allocateEm__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE12_Vector_implC1Ev__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE12_Vector_implD1Ev__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE13_M_deallocateEPS1_m__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EE19_M_get_Tp_allocatorEv__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EEC2Ev__ZNSt12_Vector_baseI11IndexHolderIiESaIS1_EED2Ev__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE11_M_allocateEm__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE12_Vector_implC1ERKS2___ZNSt12_Vector_baseISt4pairIiiESaIS1_EE12_Vector_implD1Ev__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE13_M_deallocateEPS1_m__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE17_M_create_storageEm__ZNSt12_Vector_baseISt4pairIiiESaIS1_EE19_M_get_Tp_allocatorEv__ZNSt12_Vector_baseISt4pairIiiESaIS1_EEC2EmRKS2___ZNSt12_Vector_baseISt4pairIiiESaIS1_EED2Ev__ZNSt12_Vector_baseIiSaIiEE11_M_allocateEm__ZNSt12_Vector_baseIiSaIiEE12_Vector_impl12_M_swap_dataERS2___ZNSt12_Vector_baseIiSaIiEE12_Vector_implC1ERKS0___ZNSt12_Vector_baseIiSaIiEE12_Vector_implD1Ev__ZNSt12_Vector_baseIiSaIiEE13_M_deallocateEPim__ZNSt12_Vector_baseIiSaIiEE17_M_create_storageEm__ZNSt12_Vector_baseIiSaIiEE19_M_get_Tp_allocatorEv__ZNSt12_Vector_baseIiSaIiEEC2EmRKS0___ZNSt12_Vector_baseIiSaIiEED2Ev__ZNSt13move_iteratorIPiEC1ES0___ZNSt16allocator_traitsISaI11IndexHolderIiEEE10_S_destroyIS1_EENSt9enable_ifIXsrSt6__and_IJNS3_16__destroy_helperIT_E4typeEEE5valueEvE4typeERS2_PS8___ZNSt16allocator_traitsISaI11IndexHolderIiEEE10deallocateERS2_PS1_m__ZNSt16allocator_traitsISaI11IndexHolderIiEEE11_S_max_sizeIKS2_vEEmRT_i__ZNSt16allocator_traitsISaI11IndexHolderIiEEE12_S_constructIS1_JS1_EEENSt9enable_ifIXsrSt6__and_IJNS3_18__construct_helperIT_JDpT0_EE4typeEEE5valueEvE4typeERS2_PS8_DpOS9___ZNSt16allocator_traitsISaI11IndexHolderIiEEE7destroyIS1_EEvRS2_PT___ZNSt16allocator_traitsISaI11IndexHolderIiEEE8allocateERS2_m__ZNSt16allocator_traitsISaI11IndexHolderIiEEE8max_sizeERKS2___ZNSt16allocator_traitsISaI11IndexHolderIiEEE9constructIS1_JS1_EEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS2_PT_DpOS5___ZNSt16allocator_traitsISaISt4pairIiiEEE10deallocateERS2_PS1_m__ZNSt16allocator_traitsISaISt4pairIiiEEE8allocateERS2_m__ZNSt16allocator_traitsISaIiEE10deallocateERS0_Pim__ZNSt16allocator_traitsISaIiEE11_S_max_sizeIKS0_vEEmRT_i__ZNSt16allocator_traitsISaIiEE8allocateERS0_m__ZNSt16allocator_traitsISaIiEE8max_sizeERKS0___ZNSt20__copy_move_backwardILb1ELb0ESt26random_access_iterator_tagE13__copy_move_bIP11IndexHolderIiES5_EET0_T_S7_S6___ZNSt20__copy_move_backwardILb1ELb1ESt26random_access_iterator_tagE13__copy_move_bI7DataPtrI11IndexHolderIiEEEEPT_PKS7_SA_S8___ZNSt20__copy_move_backwardILb1ELb1ESt26random_access_iterator_tagE13__copy_move_bI7DataPtrIiEEEPT_PKS5_S8_S6___ZNSt20__copy_move_backwardILb1ELb1ESt26random_access_iterator_tagE13__copy_move_bIiEEPT_PKS3_S6_S4___ZNSt20__uninitialized_copyILb0EE13__uninit_copyIP11IndexHolderIiES4_EET0_T_S6_S5___ZNSt20__uninitialized_copyILb1EE13__uninit_copyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES4_EET0_T_SA_S9___ZNSt20__uninitialized_copyILb1EE13__uninit_copyISt13move_iteratorIPiES3_EET0_T_S6_S5___ZNSt22__uninitialized_fill_nILb1EE15__uninit_fill_nIPimiEET_S3_T0_RKT1___ZNSt27__uninitialized_default_n_1ILb0EE18__uninit_default_nIPSt4pairIiiEmEET_S5_T0___ZNSt27__uninitialized_default_n_1ILb1EE18__uninit_default_nIPimEET_S3_T0___ZNSt4pairIiiEC1Ev__ZNSt4pairIiiEaSERKS0___ZNSt6vectorI11IndexHolderIiESaIS1_EE12emplace_backIJS1_EEEvDpOT___ZNSt6vectorI11IndexHolderIiESaIS1_EE19_M_emplace_back_auxIJS1_EEEvDpOT___ZNSt6vectorI11IndexHolderIiESaIS1_EE5beginEv__ZNSt6vectorI11IndexHolderIiESaIS1_EE9push_backEOS1___ZNSt6vectorI11IndexHolderIiESaIS1_EEC1Ev__ZNSt6vectorI11IndexHolderIiESaIS1_EED1Ev__ZNSt6vectorI11IndexHolderIiESaIS1_EEixEm__ZNSt6vectorISt4pairIiiESaIS1_EE21_M_default_initializeEm__ZNSt6vectorISt4pairIiiESaIS1_EEC1EmRKS2___ZNSt6vectorISt4pairIiiESaIS1_EED1Ev__ZNSt6vectorISt4pairIiiESaIS1_EEixEm__ZNSt6vectorIiSaIiEE15_M_erase_at_endEPi__ZNSt6vectorIiSaIiEE15_M_range_insertIN9__gnu_cxx17__normal_iteratorIPiS1_EEEEvS6_T_S7_St20forward_iterator_tag__ZNSt6vectorIiSaIiEE17_M_default_appendEm__ZNSt6vectorIiSaIiEE18_M_fill_initializeEmRKi__ZNSt6vectorIiSaIiEE18_M_insert_dispatchIN9__gnu_cxx17__normal_iteratorIPiS1_EEEEvS6_T_S7_St12__false_type__ZNSt6vectorIiSaIiEE19_M_emplace_back_auxIJiEEEvDpOT___ZNSt6vectorIiSaIiEE21_M_default_initializeEm__ZNSt6vectorIiSaIiEE3endEv__ZNSt6vectorIiSaIiEE4swapERS1___ZNSt6vectorIiSaIiEE5beginEv__ZNSt6vectorIiSaIiEE5clearEv__ZNSt6vectorIiSaIiEE6insertIN9__gnu_cxx17__normal_iteratorIPiS1_EEvEES6_NS4_IPKiS1_EET_SA___ZNSt6vectorIiSaIiEE6resizeEm__ZNSt6vectorIiSaIiEEC1EmRKS0___ZNSt6vectorIiSaIiEEC1EmRKiRKS0___ZNSt6vectorIiSaIiEED1Ev__ZNSt6vectorIiSaIiEEixEm__ZSt10_ConstructI11IndexHolderIiEJRS1_EEvPT_DpOT0___ZSt10_ConstructISt4pairIiiEJEEvPT_DpOT0___ZSt10__distanceIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E15difference_typeES8_S8_St26random_access_iterator_tag__ZSt10__distanceIP11IndexHolderIiEENSt15iterator_traitsIT_E15difference_typeES4_S4_St26random_access_iterator_tag__ZSt10__distanceIP7DataPtrI11IndexHolderIiEEENSt15iterator_traitsIT_E15difference_typeES6_S6_St26random_access_iterator_tag__ZSt10__distanceIP7DataPtrIiEENSt15iterator_traitsIT_E15difference_typeES4_S4_St26random_access_iterator_tag__ZSt10__distanceIPiENSt15iterator_traitsIT_E15difference_typeES2_S2_St26random_access_iterator_tag__ZSt10__distanceIPlENSt15iterator_traitsIT_E15difference_typeES2_S2_St26random_access_iterator_tag__ZSt10__fill_n_aIPimiEN9__gnu_cxx11__enable_ifIXsrSt11__is_scalarIT1_E7__valueET_E6__typeES6_T0_RKS4___ZSt10__pop_heapIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0___ZSt10__pop_heapIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0___ZSt10__pop_heapIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0___ZSt10__pop_heapIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0___ZSt11__addressofI11IndexHolderIiEEPT_RS2___ZSt11__addressofISt4pairIiiEEPT_RS2___ZSt11__make_heapIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt11__make_heapIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0___ZSt11__make_heapIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt11__make_heapIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0___ZSt11__push_heapIP11IndexHolderIiElS1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEEvT_T0_SA_T1_T2___ZSt11__push_heapIP7DataPtrI11IndexHolderIiEElS3_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS3_EEEEvT_T0_SC_T1_T2___ZSt11__push_heapIP7DataPtrIiElS1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEEvT_T0_SA_T1_T2___ZSt11__push_heapIPiliN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEEEvT_T0_S8_T1_T2___ZSt11__sort_heapIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt11__sort_heapIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0___ZSt11__sort_heapIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt11__sort_heapIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0___ZSt11lower_boundIP11IndexHolderIiES1_St4lessIS1_EET_S5_S5_RKT0_T1___ZSt11lower_boundIP7DataPtrI11IndexHolderIiEES3_St4lessIS3_EET_S7_S7_RKT0_T1___ZSt11lower_boundIP7DataPtrIiES1_St4lessIS1_EET_S5_S5_RKT0_T1___ZSt11lower_boundIPiiET_S1_S1_RKT0___ZSt11lower_boundIPiiSt4lessIiEET_S3_S3_RKT0_T1___ZSt11lower_boundIPllSt4lessIlEET_S3_S3_RKT0_T1___ZSt11upper_boundIP11IndexHolderIiES1_St4lessIS1_EET_S5_S5_RKT0_T1___ZSt11upper_boundIPiiET_S1_S1_RKT0___ZSt11upper_boundIPiiSt4lessIiEET_S3_S3_RKT0_T1___ZSt12__miter_baseIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt11_Miter_baseIT_E13iterator_typeES8___ZSt12__miter_baseIP11IndexHolderIiEENSt11_Miter_baseIT_E13iterator_typeES4___ZSt12__miter_baseIP7DataPtrI11IndexHolderIiEEENSt11_Miter_baseIT_E13iterator_typeES6___ZSt12__miter_baseIP7DataPtrIiEENSt11_Miter_baseIT_E13iterator_typeES4___ZSt12__miter_baseIPiENSt11_Miter_baseIT_E13iterator_typeES2___ZSt12__miter_baseISt13move_iteratorIPiEENSt11_Miter_baseIT_E13iterator_typeES4___ZSt12__niter_baseIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt11_Niter_baseIT_E13iterator_typeES8___ZSt12__niter_baseIP11IndexHolderIiEENSt11_Niter_baseIT_E13iterator_typeES4___ZSt12__niter_baseIP7DataPtrI11IndexHolderIiEEENSt11_Niter_baseIT_E13iterator_typeES6___ZSt12__niter_baseIP7DataPtrIiEENSt11_Niter_baseIT_E13iterator_typeES4___ZSt12__niter_baseIPiENSt11_Niter_baseIT_E13iterator_typeES2___ZSt13__adjust_heapIP11IndexHolderIiElS1_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_T0_SA_T1_T2___ZSt13__adjust_heapIP7DataPtrI11IndexHolderIiEElS3_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_T0_SC_T1_T2___ZSt13__adjust_heapIP7DataPtrIiElS1_N9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_T0_SA_T1_T2___ZSt13__adjust_heapIPiliN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_T0_S8_T1_T2___ZSt13__copy_move_aILb0EP11IndexHolderIiES2_ET1_T0_S4_S3___ZSt13__copy_move_aILb0EP7DataPtrI11IndexHolderIiEES4_ET1_T0_S6_S5___ZSt13__copy_move_aILb0EP7DataPtrIiES2_ET1_T0_S4_S3___ZSt13__copy_move_aILb0EPiS0_ET1_T0_S2_S1___ZSt13__copy_move_aILb1EPiS0_ET1_T0_S2_S1___ZSt13__heap_selectIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0___ZSt13__heap_selectIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0___ZSt13__heap_selectIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0___ZSt13__heap_selectIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0___ZSt13__lower_boundIP11IndexHolderIiES1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEET_S9_S9_RKT0_T1___ZSt13__lower_boundIP7DataPtrI11IndexHolderIiEES3_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS3_EEEET_SB_SB_RKT0_T1___ZSt13__lower_boundIP7DataPtrIiES1_N9__gnu_cxx5__ops14_Iter_comp_valISt4lessIS1_EEEET_S9_S9_RKT0_T1___ZSt13__lower_boundIPiiN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIiEEEET_S7_S7_RKT0_T1___ZSt13__lower_boundIPiiN9__gnu_cxx5__ops14_Iter_less_valEET_S4_S4_RKT0_T1___ZSt13__lower_boundIPllN9__gnu_cxx5__ops14_Iter_comp_valISt4lessIlEEEET_S7_S7_RKT0_T1___ZSt13__upper_boundIP11IndexHolderIiES1_N9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEET_S9_S9_RKT0_T1___ZSt13__upper_boundIPiiN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEEET_S7_S7_RKT0_T1___ZSt13__upper_boundIPiiN9__gnu_cxx5__ops14_Val_less_iterEET_S4_S4_RKT0_T1___ZSt13move_backwardIP11IndexHolderIiES2_ET0_T_S4_S3___ZSt13move_backwardIP7DataPtrI11IndexHolderIiEES4_ET0_T_S6_S5___ZSt13move_backwardIP7DataPtrIiES2_ET0_T_S4_S3___ZSt13move_backwardIPiS0_ET0_T_S2_S1___ZSt14__copy_move_a2ILb0EN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_ET1_T0_S8_S7___ZSt14__copy_move_a2ILb0EN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES6_ET1_T0_S8_S7___ZSt14__copy_move_a2ILb0EP11IndexHolderIiES2_ET1_T0_S4_S3___ZSt14__copy_move_a2ILb0EP7DataPtrI11IndexHolderIiEES4_ET1_T0_S6_S5___ZSt14__copy_move_a2ILb0EP7DataPtrIiES2_ET1_T0_S4_S3___ZSt14__copy_move_a2ILb0EPiS0_ET1_T0_S2_S1___ZSt14__copy_move_a2ILb1EPiS0_ET1_T0_S2_S1___ZSt14__partial_sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0___ZSt14__partial_sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_T0___ZSt14__partial_sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_T0___ZSt14__partial_sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_T0___ZSt15__alloc_on_swapISaIiEEvRT_S2___ZSt16__insertion_sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt16__insertion_sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0___ZSt16__insertion_sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt16__insertion_sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0___ZSt16__introsort_loopIP11IndexHolderIiElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_T1___ZSt16__introsort_loopIP7DataPtrI11IndexHolderIiEElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0_T1___ZSt16__introsort_loopIP7DataPtrIiElN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0_T1___ZSt16__introsort_loopIPilN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0_T1___ZSt18__do_alloc_on_swapISaIiEEvRT_S2_St17integral_constantIbLb0EE__ZSt18make_move_iteratorIPiESt13move_iteratorIT_ES2___ZSt18uninitialized_copyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_ET0_T_S8_S7___ZSt18uninitialized_copyIP11IndexHolderIiES2_ET0_T_S4_S3___ZSt18uninitialized_copyISt13move_iteratorIPiES1_ET0_T_S4_S3___ZSt19__iterator_categoryIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E17iterator_categoryERKS8___ZSt19__iterator_categoryIP11IndexHolderIiEENSt15iterator_traitsIT_E17iterator_categoryERKS4___ZSt19__iterator_categoryIP7DataPtrI11IndexHolderIiEEENSt15iterator_traitsIT_E17iterator_categoryERKS6___ZSt19__iterator_categoryIP7DataPtrIiEENSt15iterator_traitsIT_E17iterator_categoryERKS4___ZSt19__iterator_categoryIPiENSt15iterator_traitsIT_E17iterator_categoryERKS2___ZSt19__iterator_categoryIPlENSt15iterator_traitsIT_E17iterator_categoryERKS2___ZSt20uninitialized_fill_nIPimiET_S1_T0_RKT1___ZSt21__unguarded_partitionIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_S9_T0___ZSt21__unguarded_partitionIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEET_SB_SB_SB_T0___ZSt21__unguarded_partitionIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_S9_T0___ZSt21__unguarded_partitionIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEET_S7_S7_S7_T0___ZSt22__copy_move_backward_aILb1EP11IndexHolderIiES2_ET1_T0_S4_S3___ZSt22__copy_move_backward_aILb1EP7DataPtrI11IndexHolderIiEES4_ET1_T0_S6_S5___ZSt22__copy_move_backward_aILb1EP7DataPtrIiES2_ET1_T0_S4_S3___ZSt22__copy_move_backward_aILb1EPiS0_ET1_T0_S2_S1___ZSt22__final_insertion_sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt22__final_insertion_sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0___ZSt22__final_insertion_sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt22__final_insertion_sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0___ZSt22__move_median_to_firstIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_S9_T0___ZSt22__move_median_to_firstIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_SB_SB_T0___ZSt22__move_median_to_firstIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_S9_S9_T0___ZSt22__move_median_to_firstIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_S7_S7_T0___ZSt22__uninitialized_copy_aIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_iET0_T_S8_S7_RSaIT1_E__ZSt22__uninitialized_copy_aIP11IndexHolderIiES2_S1_ET0_T_S4_S3_RSaIT1_E__ZSt22__uninitialized_copy_aISt13move_iteratorIPiES1_iET0_T_S4_S3_RSaIT1_E__ZSt22__uninitialized_move_aIPiS0_SaIiEET0_T_S3_S2_RT1___ZSt23__copy_move_backward_a2ILb1EP11IndexHolderIiES2_ET1_T0_S4_S3___ZSt23__copy_move_backward_a2ILb1EP7DataPtrI11IndexHolderIiEES4_ET1_T0_S6_S5___ZSt23__copy_move_backward_a2ILb1EP7DataPtrIiES2_ET1_T0_S4_S3___ZSt23__copy_move_backward_a2ILb1EPiS0_ET1_T0_S2_S1___ZSt24__uninitialized_fill_n_aIPimiiET_S1_T0_RKT1_RSaIT2_E__ZSt25__unguarded_linear_insertIP11IndexHolderIiEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEEvT_T0___ZSt25__unguarded_linear_insertIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS3_EEEEvT_T0___ZSt25__unguarded_linear_insertIP7DataPtrIiEN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIS1_EEEEvT_T0___ZSt25__unguarded_linear_insertIPiN9__gnu_cxx5__ops14_Val_comp_iterISt4lessIiEEEEvT_T0___ZSt25__uninitialized_default_nIPSt4pairIiiEmET_S3_T0___ZSt25__uninitialized_default_nIPimET_S1_T0___ZSt26__unguarded_insertion_sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt26__unguarded_insertion_sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0___ZSt26__unguarded_insertion_sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt26__unguarded_insertion_sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0___ZSt27__unguarded_partition_pivotIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_T0___ZSt27__unguarded_partition_pivotIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEET_SB_SB_T0___ZSt27__unguarded_partition_pivotIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEET_S9_S9_T0___ZSt27__unguarded_partition_pivotIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEET_S7_S7_T0___ZSt27__uninitialized_default_n_aIPSt4pairIiiEmS1_ET_S3_T0_RSaIT1_E__ZSt27__uninitialized_default_n_aIPimiET_S1_T0_RSaIT1_E__ZSt32__make_move_if_noexcept_iteratorIP11IndexHolderIiES2_ET0_T___ZSt32__make_move_if_noexcept_iteratorIPiSt13move_iteratorIS0_EET0_T___ZSt34__uninitialized_move_if_noexcept_aIP11IndexHolderIiES2_SaIS1_EET0_T_S5_S4_RT1___ZSt34__uninitialized_move_if_noexcept_aIPiS0_SaIiEET0_T_S3_S2_RT1___ZSt3maxImERKT_S2_S2___ZSt4__lgl__ZSt4copyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES2_ET0_T_S8_S7___ZSt4copyIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEES6_ET0_T_S8_S7___ZSt4copyIP11IndexHolderIiES2_ET0_T_S4_S3___ZSt4copyIP7DataPtrI11IndexHolderIiEES4_ET0_T_S6_S5___ZSt4copyIP7DataPtrIiES2_ET0_T_S4_S3___ZSt4copyIPiS0_ET0_T_S2_S1___ZSt4copyISt13move_iteratorIPiES1_ET0_T_S4_S3___ZSt4moveIR11IndexHolderIiEEONSt16remove_referenceIT_E4typeEOS4___ZSt4moveIR7DataPtrI11IndexHolderIiEEEONSt16remove_referenceIT_E4typeEOS6___ZSt4moveIR7DataPtrIiEEONSt16remove_referenceIT_E4typeEOS4___ZSt4moveIRPiEONSt16remove_referenceIT_E4typeEOS3___ZSt4moveIRiEONSt16remove_referenceIT_E4typeEOS2___ZSt4sortIP11IndexHolderIiESt4lessIS1_EEvT_S5_T0___ZSt4sortIP7DataPtrI11IndexHolderIiEESt4lessIS3_EEvT_S7_T0___ZSt4sortIP7DataPtrIiESt4lessIS1_EEvT_S5_T0___ZSt4sortIPiSt4lessIiEEvT_S3_T0___ZSt4swapI11IndexHolderIiEEvRT_S3___ZSt4swapI7DataPtrI11IndexHolderIiEEEvRT_S5___ZSt4swapI7DataPtrIiEEvRT_S3___ZSt4swapIPiEvRT_S2___ZSt4swapIiEvRT_S1___ZSt4swapIiSaIiEEvRSt6vectorIT_T0_ES5___ZSt5mergeIP11IndexHolderIiES2_S2_ET1_T_S4_T0_S5_S3___ZSt5mergeIP7DataPtrI11IndexHolderIiEES4_S4_ET1_T_S6_T0_S7_S5___ZSt5mergeIP7DataPtrIiES2_S2_ET1_T_S4_T0_S5_S3___ZSt5mergeIPiS0_S0_ET1_T_S2_T0_S3_S1___ZSt6__sortIP11IndexHolderIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt6__sortIP7DataPtrI11IndexHolderIiEEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS3_EEEEvT_SB_T0___ZSt6__sortIP7DataPtrIiEN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIS1_EEEEvT_S9_T0___ZSt6__sortIPiN9__gnu_cxx5__ops15_Iter_comp_iterISt4lessIiEEEEvT_S7_T0___ZSt6fill_nIPimiET_S1_T0_RKT1___ZSt7__mergeIP11IndexHolderIiES2_S2_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S7_T0_S8_S6_T2___ZSt7__mergeIP7DataPtrI11IndexHolderIiEES4_S4_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S9_T0_SA_S8_T2___ZSt7__mergeIP7DataPtrIiES2_S2_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S7_T0_S8_S6_T2___ZSt7__mergeIPiS0_S0_N9__gnu_cxx5__ops15_Iter_less_iterEET1_T_S5_T0_S6_S4_T2___ZSt7advanceIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEmEvRT_T0___ZSt7advanceIP11IndexHolderIiElEvRT_T0___ZSt7advanceIP7DataPtrI11IndexHolderIiEElEvRT_T0___ZSt7advanceIP7DataPtrIiElEvRT_T0___ZSt7advanceIPilEvRT_T0___ZSt7advanceIPllEvRT_T0___ZSt7forwardI11IndexHolderIiEEOT_RNSt16remove_referenceIS2_E4typeE__ZSt7forwardIR11IndexHolderIiEEOT_RNSt16remove_referenceIS3_E4typeE__ZSt8_DestroyI11IndexHolderIiEEvPT___ZSt8_DestroyIP11IndexHolderIiEEvT_S3___ZSt8_DestroyIP11IndexHolderIiES1_EvT_S3_RSaIT0_E__ZSt8_DestroyIPSt4pairIiiEEvT_S3___ZSt8_DestroyIPSt4pairIiiES1_EvT_S3_RSaIT0_E__ZSt8_DestroyIPiEvT_S1___ZSt8_DestroyIPiiEvT_S1_RSaIT0_E__ZSt8distanceIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEENSt15iterator_traitsIT_E15difference_typeES8_S8___ZSt8distanceIP11IndexHolderIiEENSt15iterator_traitsIT_E15difference_typeES4_S4___ZSt8distanceIP7DataPtrI11IndexHolderIiEEENSt15iterator_traitsIT_E15difference_typeES6_S6___ZSt8distanceIP7DataPtrIiEENSt15iterator_traitsIT_E15difference_typeES4_S4___ZSt8distanceIPiENSt15iterator_traitsIT_E15difference_typeES2_S2___ZSt8distanceIPlENSt15iterator_traitsIT_E15difference_typeES2_S2___ZSt9__advanceIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEElEvRT_T0_St26random_access_iterator_tag__ZSt9__advanceIP11IndexHolderIiElEvRT_T0_St26random_access_iterator_tag__ZSt9__advanceIP7DataPtrI11IndexHolderIiEElEvRT_T0_St26random_access_iterator_tag__ZSt9__advanceIP7DataPtrIiElEvRT_T0_St26random_access_iterator_tag__ZSt9__advanceIPilEvRT_T0_St26random_access_iterator_tag__ZSt9__advanceIPllEvRT_T0_St26random_access_iterator_tag__ZSt9iter_swapIP11IndexHolderIiES2_EvT_T0___ZSt9iter_swapIP7DataPtrI11IndexHolderIiEES4_EvT_T0___ZSt9iter_swapIP7DataPtrIiES2_EvT_T0___ZSt9iter_swapIPiS0_EvT_T0___ZZN3par12Mpi_datatypeIbE5valueEvE5first__ZZN3par12Mpi_datatypeIbE5valueEvE8datatype__ZnwmPv__mh_execute_header_main_ADIOI_Datarep_head_ADIOI_Datatype_iscontig_ADIOI_Free_fn_ADIOI_Get_byte_offset_ADIOI_Get_eof_offset_ADIOI_Get_position_ADIOI_Malloc_fn_ADIOI_Set_lock_ADIOI_Shfp_fname_ADIOI_Strdup_ADIOI_Strncpy_ADIO_Close_ADIO_Get_shared_fp_ADIO_ImmediateOpen_ADIO_Open_ADIO_ResolveFileType_ADIO_Set_shared_fp_ADIO_Set_view_ADIO_same_amode_GOMP_parallel_MPIC_Isend_MPIC_Probe_MPIC_Recv_MPIC_Waitall_MPIDI_CH3I_Progress_MPIDI_CH3I_progress_completion_count_MPID_Abort_MPID_Accumulate_MPID_Aint_add_MPID_Aint_diff_MPID_Alloc_mem_MPID_Attr_free_MPID_Bsend_init_MPID_Cancel_recv_MPID_Cancel_send_MPID_Comm_AS_enabled_MPID_Comm_builtin_MPID_Comm_direct_MPID_Comm_disconnect_MPID_Comm_failure_ack_MPID_Comm_failure_get_acked_MPID_Comm_get_all_failed_procs_MPID_Comm_mem_MPID_Comm_revoke_MPID_Comm_spawn_multiple_MPID_Compare_and_swap_MPID_Datatype_builtin_MPID_Datatype_direct_MPID_Datatype_free_MPID_Datatype_mem_MPID_Datatype_set_contents_MPID_Datatype_size_external32_MPID_Errhandler_builtin_MPID_Errhandler_direct_MPID_Errhandler_free_MPID_Errhandler_mem_MPID_Fetch_and_op_MPID_Finalize_MPID_Free_mem_MPID_Get_MPID_Get_accumulate_MPID_Get_processor_name_MPID_Grequest_class_direct_MPID_Grequest_class_mem_MPID_Group_builtin_MPID_Group_direct_MPID_Group_empty_MPID_Group_mem_MPID_Improbe_MPID_Imrecv_MPID_Info_builtin_MPID_Info_direct_MPID_Info_mem_MPID_Iprobe_MPID_Irecv_MPID_Irsend_MPID_Isend_MPID_Issend_MPID_Keyval_direct_MPID_Keyval_mem_MPID_Mprobe_MPID_Mrecv_MPID_NS_Create_MPID_NS_Free_MPID_NS_Lookup_MPID_NS_Publish_MPID_NS_Unpublish_MPID_Op_builtin_MPID_Op_direct_MPID_Op_mem_MPID_Probe_MPID_Put_MPID_Raccumulate_MPID_Recv_MPID_Recv_init_MPID_Request_direct_MPID_Request_is_anysource_MPID_Request_mem_MPID_Request_release_MPID_Rget_MPID_Rget_accumulate_MPID_Rput_MPID_Rsend_MPID_Rsend_init_MPID_Segment_alloc_MPID_Segment_free_MPID_Segment_init_MPID_Segment_pack_external32_MPID_Segment_unpack_external32_MPID_Send_MPID_Send_init_MPID_Ssend_MPID_Ssend_init_MPID_Startall_MPID_Type_blockindexed_MPID_Type_commit_MPID_Type_contiguous_MPID_Type_create_resized_MPID_Type_dup_MPID_Type_get_contents_MPID_Type_indexed_MPID_Type_vector_MPID_Win_allocate_MPID_Win_allocate_shared_MPID_Win_attach_MPID_Win_complete_MPID_Win_create_MPID_Win_create_dynamic_MPID_Win_detach_MPID_Win_direct_MPID_Win_fence_MPID_Win_flush_MPID_Win_flush_all_MPID_Win_flush_local_MPID_Win_flush_local_all_MPID_Win_free_MPID_Win_get_info_MPID_Win_lock_MPID_Win_lock_all_MPID_Win_mem_MPID_Win_post_MPID_Win_set_info_MPID_Win_shared_query_MPID_Win_start_MPID_Win_sync_MPID_Win_test_MPID_Win_unlock_MPID_Win_unlock_all_MPID_Win_wait_MPIOI_File_iread_MPIOI_File_iread_all_MPIOI_File_iwrite_MPIOI_File_iwrite_all_MPIOI_File_read_MPIOI_File_read_all_MPIOI_File_read_all_begin_MPIOI_File_read_all_end_MPIOI_File_write_MPIOI_File_write_all_MPIOI_File_write_all_begin_MPIOI_File_write_all_end_MPIO_Completed_request_create_MPIO_Err_create_code_MPIO_Err_return_file_MPIO_File_c2f_MPIO_File_f2c_MPIO_File_free_MPIO_File_resolve_MPIR_Add_finalize_MPIR_Allgather_impl_MPIR_Allgatherv_impl_MPIR_Allreduce_group_MPIR_Allreduce_impl_MPIR_Alltoall_impl_MPIR_Alltoallv_impl_MPIR_Alltoallw_impl_MPIR_Assert_fail_MPIR_Assert_fail_fmt_MPIR_Attr_copy_c_proxy_MPIR_Attr_delete_c_proxy_MPIR_Attr_delete_list_MPIR_Attr_dup_list_MPIR_Barrier_impl_MPIR_Bcast_impl_MPIR_Bsend_attach_MPIR_Bsend_detach_MPIR_Bsend_isend_MPIR_CVAR_ASYNC_PROGRESS_MPIR_CVAR_COLL_ALIAS_CHECK_MPIR_CVAR_DEFAULT_THREAD_LEVEL_MPIR_CVAR_ENABLE_FT_MPIR_CVAR_SUPPRESS_ABORT_MESSAGE_MPIR_Call_attr_delete_MPIR_Call_finalize_callbacks_MPIR_Cancel_impl_MPIR_Cart_create_impl_MPIR_Cart_map_impl_MPIR_Cart_rank_impl_MPIR_Cart_shift_impl_MPIR_Close_port_impl_MPIR_CommGetAttr_MPIR_Comm_accept_impl_MPIR_Comm_connect_impl_MPIR_Comm_copy_MPIR_Comm_create_errhandler_impl_MPIR_Comm_create_group_MPIR_Comm_create_inter_MPIR_Comm_create_intra_MPIR_Comm_create_keyval_impl_MPIR_Comm_delete_attr_impl_MPIR_Comm_delete_internal_MPIR_Comm_dup_impl_MPIR_Comm_dup_with_info_impl_MPIR_Comm_free_impl_MPIR_Comm_free_keyval_impl_MPIR_Comm_get_errhandler_impl_MPIR_Comm_get_info_impl_MPIR_Comm_get_name_impl_MPIR_Comm_group_impl_MPIR_Comm_idup_impl_MPIR_Comm_remote_group_impl_MPIR_Comm_set_attr_impl_MPIR_Comm_set_errhandler_impl_MPIR_Comm_set_info_impl_MPIR_Comm_split_impl_MPIR_Comm_split_type_impl_MPIR_Create_unnamed_predefined_MPIR_DatatypeAttrFinalize_MPIR_Datatype_init_names_MPIR_Datatype_iscontig_MPIR_Dims_create_MPIR_Dist_graph_neighbors_count_impl_MPIR_Dist_graph_neighbors_impl_MPIR_Err_add_class_MPIR_Err_add_code_MPIR_Err_combine_codes_MPIR_Err_create_code_MPIR_Err_create_code_valist_MPIR_Err_get_string_MPIR_Err_is_fatal_MPIR_Err_preOrPostInit_MPIR_Err_return_comm_MPIR_Err_return_win_MPIR_Err_set_msg_MPIR_Errhandler_set_cxx_MPIR_Exscan_impl_MPIR_Ext_assert_fail_MPIR_Ext_cs_enter_MPIR_Ext_cs_exit_MPIR_Ext_cs_yield_MPIR_Ext_datatype_iscommitted_MPIR_Ext_init_MPIR_File_call_cxx_errhandler_MPIR_Finalize_async_thread_MPIR_Gather_impl_MPIR_Gatherv_impl_MPIR_Get_count_impl_MPIR_Get_elements_x_impl_MPIR_Get_file_error_routine_MPIR_Graph_create_MPIR_Graph_map_impl_MPIR_Graph_neighbors_count_impl_MPIR_Graph_neighbors_impl_MPIR_Grequest_class_list_MPIR_Grequest_complete_impl_MPIR_Grequest_free_MPIR_Grequest_free_classes_on_finalize_MPIR_Grequest_progress_poke_MPIR_Grequest_query_MPIR_Grequest_registered_finalizer_MPIR_Grequest_start_impl_MPIR_Group_check_valid_ranges_MPIR_Group_check_valid_ranks_MPIR_Group_compare_impl_MPIR_Group_difference_impl_MPIR_Group_excl_impl_MPIR_Group_free_impl_MPIR_Group_incl_impl_MPIR_Group_intersection_impl_MPIR_Group_range_excl_impl_MPIR_Group_range_incl_impl_MPIR_Group_release_MPIR_Group_translate_ranks_impl_MPIR_Group_union_impl_MPIR_Handle_fatal_error_MPIR_Iallgather_impl_MPIR_Iallgatherv_impl_MPIR_Iallreduce_impl_MPIR_Ialltoall_impl_MPIR_Ialltoallv_impl_MPIR_Ialltoallw_impl_MPIR_Ibarrier_impl_MPIR_Ibcast_impl_MPIR_Ibsend_impl_MPIR_Iexscan_impl_MPIR_Igather_impl_MPIR_Igatherv_impl_MPIR_Ineighbor_allgather_impl_MPIR_Ineighbor_allgatherv_impl_MPIR_Ineighbor_alltoall_impl_MPIR_Ineighbor_alltoallv_impl_MPIR_Ineighbor_alltoallw_impl_MPIR_Info_dup_impl_MPIR_Info_get_impl_MPIR_Info_get_nkeys_impl_MPIR_Info_get_nthkey_impl_MPIR_Info_get_valuelen_impl_MPIR_Info_set_impl_MPIR_Init_async_thread_MPIR_Init_thread_MPIR_Intercomm_create_impl_MPIR_Intercomm_merge_impl_MPIR_Ireduce_impl_MPIR_Ireduce_scatter_block_impl_MPIR_Ireduce_scatter_impl_MPIR_Iscan_impl_MPIR_Iscatter_impl_MPIR_Iscatterv_impl_MPIR_Keyval_set_proxy_MPIR_MPIOInit_MPIR_Namepub_MPIR_Neighbor_allgather_impl_MPIR_Neighbor_allgatherv_impl_MPIR_Neighbor_alltoall_impl_MPIR_Neighbor_alltoallv_impl_MPIR_Neighbor_alltoallw_impl_MPIR_Op_check_dtype_table_MPIR_Op_set_cxx_MPIR_Open_port_impl_MPIR_Pack_impl_MPIR_Pack_size_impl_MPIR_Process_MPIR_Progress_wait_request_MPIR_ROMIO_Get_file_errhand_MPIR_ROMIO_Set_file_errhand_MPIR_Reduce_impl_MPIR_Reduce_local_impl_MPIR_Reduce_scatter_block_impl_MPIR_Reduce_scatter_impl_MPIR_Request_complete_MPIR_Scan_impl_MPIR_Scatter_impl_MPIR_Scatterv_impl_MPIR_Setup_intercomm_localcomm_MPIR_Status_set_bytes_MPIR_Status_set_elements_x_impl_MPIR_T_category_get_categories_impl_MPIR_T_category_get_cvars_impl_MPIR_T_category_get_pvars_impl_MPIR_T_cvar_handle_alloc_impl_MPIR_T_cvar_read_impl_MPIR_T_cvar_write_impl_MPIR_T_env_finalize_MPIR_T_env_init_MPIR_T_init_balance_MPIR_T_is_threaded_MPIR_T_pvar_handle_alloc_impl_MPIR_T_pvar_handle_free_impl_MPIR_T_pvar_read_impl_MPIR_T_pvar_readreset_impl_MPIR_T_pvar_reset_impl_MPIR_T_pvar_session_create_impl_MPIR_T_pvar_session_free_impl_MPIR_T_pvar_start_impl_MPIR_T_pvar_stop_impl_MPIR_T_strncpy_MPIR_Test_impl_MPIR_Testall_impl_MPIR_ThreadInfo_MPIR_Thread_CS_Finalize_MPIR_Topology_get_MPIR_Topology_put_MPIR_TypeGetAttr_MPIR_TypeSetAttr_MPIR_Type_block_MPIR_Type_commit_impl_MPIR_Type_contiguous_impl_MPIR_Type_create_hindexed_block_impl_MPIR_Type_create_indexed_block_impl_MPIR_Type_create_struct_impl_MPIR_Type_cyclic_MPIR_Type_free_impl_MPIR_Type_get_envelope_impl_MPIR_Type_get_extent_impl_MPIR_Type_get_extent_x_impl_MPIR_Type_get_true_extent_impl_MPIR_Type_get_true_extent_x_impl_MPIR_Type_hvector_impl_MPIR_Type_indexed_impl_MPIR_Type_is_rma_atomic_MPIR_Type_lb_impl_MPIR_Type_size_x_impl_MPIR_Type_struct_impl_MPIR_Type_vector_impl_MPIR_Unpack_impl_MPIR_Version_CC_MPIR_Version_CXX_MPIR_Version_F77_MPIR_Version_FC_MPIR_Version_configure_MPIR_Version_date_MPIR_Version_device_MPIR_Version_string_MPIR_Wait_impl_MPIR_Waitall_impl_MPIR_WinGetAttr_MPIR_WinSetAttr_MPIR_async_thread_initialized_MPIR_fd_recv_MPIR_fd_send_MPIU_Handle_get_ptr_indirect_MPIU_Handle_obj_alloc_MPIU_Handle_obj_free_MPIU_Info_alloc_MPIU_Info_free_MPIU_Strncpy_MPIU_Wtick_MPIU_Wtime_MPIU_Wtime_init_MPIU_Wtime_todouble_MPIU_datatype_full_size_MPIU_external32_buffer_setup_MPIU_read_external32_conversion_fn_MPIU_trcalloc_MPIU_trfree_MPIU_trmalloc_MPIU_trrealloc_MPIX_Grequest_start_impl_MPI_Abort_MPI_Accumulate_MPI_Add_error_class_MPI_Add_error_code_MPI_Add_error_string_MPI_Allgather_MPI_Allgatherv_MPI_Alloc_mem_MPI_Allreduce_MPI_Alltoall_MPI_Alltoallv_MPI_Alltoallw_MPI_Barrier_MPI_Bcast_MPI_Bsend_MPI_Bsend_init_MPI_Buffer_attach_MPI_Buffer_detach_MPI_Cancel_MPI_Cart_coords_MPI_Cart_create_MPI_Cart_get_MPI_Cart_map_MPI_Cart_rank_MPI_Cart_shift_MPI_Cart_sub_MPI_Cartdim_get_MPI_Close_port_MPI_Comm_accept_MPI_Comm_call_errhandler_MPI_Comm_connect_MPI_Comm_create_MPI_Comm_create_errhandler_MPI_Comm_create_keyval_MPI_Comm_delete_attr_MPI_Comm_disconnect_MPI_Comm_dup_MPI_Comm_free_MPI_Comm_get_attr_MPI_Comm_get_errhandler_MPI_Comm_get_name_MPI_Comm_get_parent_MPI_Comm_group_MPI_Comm_join_MPI_Comm_rank_MPI_Comm_remote_group_MPI_Comm_remote_size_MPI_Comm_set_attr_MPI_Comm_set_errhandler_MPI_Comm_set_name_MPI_Comm_size_MPI_Comm_split_MPI_Comm_test_inter_MPI_Dims_create_MPI_Errhandler_free_MPI_Error_class_MPI_Error_string_MPI_Exscan_MPI_F_STATUS_IGNORE_MPI_File_c2f_MPI_File_call_errhandler_MPI_File_close_MPI_File_create_errhandler_MPI_File_delete_MPI_File_f2c_MPI_File_get_amode_MPI_File_get_atomicity_MPI_File_get_byte_offset_MPI_File_get_errhandler_MPI_File_get_group_MPI_File_get_info_MPI_File_get_position_MPI_File_get_position_shared_MPI_File_get_size_MPI_File_get_type_extent_MPI_File_get_view_MPI_File_iread_MPI_File_iread_all_MPI_File_iread_at_MPI_File_iread_at_all_MPI_File_iread_shared_MPI_File_iwrite_MPI_File_iwrite_all_MPI_File_iwrite_at_MPI_File_iwrite_at_all_MPI_File_iwrite_shared_MPI_File_open_MPI_File_preallocate_MPI_File_read_MPI_File_read_all_MPI_File_read_all_begin_MPI_File_read_all_end_MPI_File_read_at_MPI_File_read_at_all_MPI_File_read_at_all_begin_MPI_File_read_at_all_end_MPI_File_read_ordered_MPI_File_read_ordered_begin_MPI_File_read_ordered_end_MPI_File_read_shared_MPI_File_seek_MPI_File_seek_shared_MPI_File_set_atomicity_MPI_File_set_errhandler_MPI_File_set_info_MPI_File_set_size_MPI_File_set_view_MPI_File_sync_MPI_File_write_MPI_File_write_all_MPI_File_write_all_begin_MPI_File_write_all_end_MPI_File_write_at_MPI_File_write_at_all_MPI_File_write_at_all_begin_MPI_File_write_at_all_end_MPI_File_write_ordered_MPI_File_write_ordered_begin_MPI_File_write_ordered_end_MPI_File_write_shared_MPI_Finalize_MPI_Finalized_MPI_Free_mem_MPI_Gather_MPI_Gatherv_MPI_Get_MPI_Get_address_MPI_Get_count_MPI_Get_elements_MPI_Get_processor_name_MPI_Get_version_MPI_Graph_create_MPI_Graph_get_MPI_Graph_map_MPI_Graph_neighbors_MPI_Graph_neighbors_count_MPI_Graphdims_get_MPI_Grequest_complete_MPI_Grequest_start_MPI_Group_excl_MPI_Group_free_MPI_Group_incl_MPI_Group_range_excl_MPI_Group_range_incl_MPI_Group_rank_MPI_Group_size_MPI_Ibsend_MPI_Info_delete_MPI_Info_free_MPI_Info_get_MPI_Info_get_nkeys_MPI_Info_get_nthkey_MPI_Info_get_valuelen_MPI_Info_set_MPI_Init_MPI_Init_thread_MPI_Initialized_MPI_Intercomm_create_MPI_Intercomm_merge_MPI_Iprobe_MPI_Irecv_MPI_Irsend_MPI_Is_thread_main_MPI_Isend_MPI_Issend_MPI_Lookup_name_MPI_Op_commutative_MPI_Op_create_MPI_Op_free_MPI_Open_port_MPI_Pack_MPI_Pack_external_MPI_Pack_external_size_MPI_Pack_size_MPI_Pcontrol_MPI_Probe_MPI_Publish_name_MPI_Put_MPI_Query_thread_MPI_Recv_MPI_Recv_init_MPI_Reduce_MPI_Reduce_local_MPI_Reduce_scatter_MPI_Reduce_scatter_block_MPI_Register_datarep_MPI_Request_free_MPI_Request_get_status_MPI_Rsend_MPI_Rsend_init_MPI_Scan_MPI_Scatter_MPI_Scatterv_MPI_Send_MPI_Send_init_MPI_Sendrecv_MPI_Sendrecv_replace_MPI_Ssend_MPI_Ssend_init_MPI_Start_MPI_Status_set_cancelled_MPI_Status_set_elements_MPI_T_PVAR_ALL_HANDLES_MPI_Test_MPI_Test_cancelled_MPI_Testsome_MPI_Topo_test_MPI_Type_commit_MPI_Type_contiguous_MPI_Type_create_darray_MPI_Type_create_hindexed_MPI_Type_create_hvector_MPI_Type_create_indexed_block_MPI_Type_create_keyval_MPI_Type_create_resized_MPI_Type_create_subarray_MPI_Type_delete_attr_MPI_Type_free_MPI_Type_get_attr_MPI_Type_get_contents_MPI_Type_get_envelope_MPI_Type_get_extent_MPI_Type_get_name_MPI_Type_get_true_extent_MPI_Type_indexed_MPI_Type_set_attr_MPI_Type_set_name_MPI_Type_size_MPI_Type_size_x_MPI_Type_vector_MPI_UNWEIGHTED_MPI_Unpack_MPI_Unpack_external_MPI_Unpublish_name_MPI_Wait_MPI_Win_call_errhandler_MPI_Win_complete_MPI_Win_create_MPI_Win_create_errhandler_MPI_Win_create_keyval_MPI_Win_delete_attr_MPI_Win_fence_MPI_Win_free_MPI_Win_get_attr_MPI_Win_get_errhandler_MPI_Win_get_group_MPI_Win_get_name_MPI_Win_lock_MPI_Win_post_MPI_Win_set_attr_MPI_Win_set_errhandler_MPI_Win_set_name_MPI_Win_start_MPI_Win_test_MPI_Win_unlock_MPI_Win_wait_MPI_Wtick_MPI_Wtime_MPL_internal_sys_error_printf_PMPIX_Grequest_class_allocate_PMPIX_Grequest_class_create_PMPI_Abort_PMPI_Address_PMPI_Allgather_PMPI_Allreduce_PMPI_Alltoall_PMPI_Alltoallw_PMPI_Attr_get_PMPI_Attr_put_PMPI_Barrier_PMPI_Bcast_PMPI_Comm_call_errhandler_PMPI_Comm_dup_PMPI_Comm_free_PMPI_Comm_group_PMPI_Comm_rank_PMPI_Comm_size_PMPI_Comm_test_inter_PMPI_Error_string_PMPI_File_set_errhandler_PMPI_Gather_PMPI_Gatherv_PMPI_Get_processor_name_PMPI_Grequest_complete_PMPI_Grequest_start_PMPI_Info_create_PMPI_Info_delete_PMPI_Info_dup_PMPI_Info_free_PMPI_Info_get_PMPI_Info_get_nkeys_PMPI_Info_get_nthkey_PMPI_Info_get_valuelen_PMPI_Info_set_PMPI_Initialized_PMPI_Irecv_PMPI_Isend_PMPI_Keyval_create_PMPI_Keyval_free_PMPI_Op_create_PMPI_Op_free_PMPI_Pack_PMPI_Recv_PMPI_Send_PMPI_Status_set_cancelled_PMPI_Test_PMPI_Testall_PMPI_Type_commit_PMPI_Type_contiguous_PMPI_Type_extent_PMPI_Type_free_PMPI_Type_get_contents_PMPI_Type_get_envelope_PMPI_Type_get_true_extent_PMPI_Type_hindexed_PMPI_Type_hvector_PMPI_Type_lb_PMPI_Type_set_name_PMPI_Type_struct_PMPI_Type_vector_PMPI_Unpack_PMPI_Wait_PMPI_Waitall_PMPI_Waitany_PMPI_Wtime__DefaultRuneLocale__Unwind_Resume__ZNSolsEPFRSoS_E__ZNSolsEd__ZNSolsEi__ZNSt8ios_base4InitC1Ev__ZNSt8ios_base4InitD1Ev__ZSt17__throw_bad_allocv__ZSt20__throw_length_errorPKc__ZSt4cout__ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6___ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc__ZTVN10__cxxabiv117__class_type_infoE__ZTVN10__cxxabiv120__si_class_type_infoE__ZdaPv__ZdlPv__Znam__Znwm___assert_rtn___cxa_allocate_exception___cxa_atexit___cxa_begin_catch___cxa_end_catch___cxa_pure_virtual___cxa_rethrow___cxa_throw___cxa_throw_bad_array_new_length___error___gxx_personality_v0___maskrune___memcpy_chk___memmove_chk___muldc3___mulsc3___mulxc3___sprintf_chk___stderrp___stdoutp_accept_aio_error_aio_read_aio_return_aio_suspend_aio_write_atoi_atol_bind_calloc_cat_hash_cat_stamp_cat_table_close_connect_cvar_hash_cvar_table_dup2_execvp_exit_fclose_fcntl_fflush_fopen_fork_fprintf_fputc_fputs_free_fsync_ftruncate_getenv_gethostbyname_gethostname_getpid_getsockname_getsockopt_h_errno_inet_pton_ioctl_listen_lseek_lstat$INODE64_mach_absolute_time_mach_timebase_info_malloc_memcmp_memcpy_memmove_mkstemp_mmap_mpi_t_mutex_munmap_omp_get_max_threads_omp_get_num_threads_omp_get_thread_num_omp_get_wtime_omp_set_num_threads_open_perror_poll_pread_printf_pthread_attr_destroy_pthread_attr_init_pthread_attr_setdetachstate_pthread_cond_broadcast_pthread_cond_destroy_pthread_cond_init_pthread_cond_signal_pthread_cond_wait_pthread_create_pthread_equal_pthread_exit_pthread_getspecific_pthread_key_create_pthread_key_delete_pthread_mutex_destroy_pthread_mutex_init_pthread_mutex_lock_pthread_mutex_unlock_pthread_mutexattr_destroy_pthread_mutexattr_init_pthread_mutexattr_setpshared_pthread_mutexattr_settype_pthread_self_pthread_setspecific_putchar_putenv_puts_pvar_hashs_pvar_table_pwrite_qsort_rand_read_readlink_readv_realloc_recv_sched_yield_send_setbuf_setitimer_setsockopt_setvbuf_shutdown_signal_snprintf_socket_srand_sscanf_statfs$INODE64_strchr_strcmp_strcpy_strcspn_strdup_strerror_strerror_r_strncasecmp_strncmp_strncpy_strrchr_strstr_strtod_strtok_r_strtol_time_umask_unlink_usleep_vfprintf_vprintf_vsnprintf_write_writevdyld_stub_binderCombBLAS_beta_16_2/usort/src/000755 000765 000024 00000000000 13212627401 017365 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/usort/src/parUtils.cpp000755 000765 000024 00000023046 13212627401 021704 0ustar00aydinbulucstaff000000 000000 /** @file parUtils.C @author Rahul S. Sampath, rahul.sampath@gmail.com @author Hari Sundar, hsundar@gmail.com */ #include "mpi.h" #include "binUtils.h" #include "dtypes.h" #include "parUtils.h" #ifdef __DEBUG__ #ifndef __DEBUG_PAR__ #define __DEBUG_PAR__ #endif #endif namespace par { unsigned int splitCommBinary( MPI_Comm orig_comm, MPI_Comm *new_comm) { int npes, rank; MPI_Group orig_group, new_group; MPI_Comm_size(orig_comm, &npes); MPI_Comm_rank(orig_comm, &rank); unsigned int splitterRank = binOp::getPrevHighestPowerOfTwo(npes); int *ranksAsc, *ranksDesc; //Determine sizes for the 2 groups ranksAsc = new int[splitterRank]; ranksDesc = new int[( npes - splitterRank)]; int numAsc = 0; int numDesc = ( npes - splitterRank - 1); //This is the main mapping between old ranks and new ranks. for(int i=0; i(i) < splitterRank) { ranksAsc[numAsc] = i; numAsc++; }else { ranksDesc[numDesc] = i; numDesc--; } }//end for i MPI_Comm_group(orig_comm, &orig_group); /* Divide tasks into two distinct groups based upon rank */ if (static_cast(rank) < splitterRank) { MPI_Group_incl(orig_group, splitterRank, ranksAsc, &new_group); }else { MPI_Group_incl(orig_group, (npes-splitterRank), ranksDesc, &new_group); } MPI_Comm_create(orig_comm, new_group, new_comm); delete [] ranksAsc; ranksAsc = NULL; delete [] ranksDesc; ranksDesc = NULL; return splitterRank; }//end function unsigned int splitCommBinaryNoFlip( MPI_Comm orig_comm, MPI_Comm *new_comm) { int npes, rank; MPI_Group orig_group, new_group; MPI_Comm_size(orig_comm, &npes); MPI_Comm_rank(orig_comm, &rank); unsigned int splitterRank = binOp::getPrevHighestPowerOfTwo(npes); int *ranksAsc, *ranksDesc; //Determine sizes for the 2 groups ranksAsc = new int[splitterRank]; ranksDesc = new int[( npes - splitterRank)]; int numAsc = 0; int numDesc = 0; //( npes - splitterRank - 1); //This is the main mapping between old ranks and new ranks. for(int i = 0; i < npes; i++) { if(static_cast(i) < splitterRank) { ranksAsc[numAsc] = i; numAsc++; }else { ranksDesc[numDesc] = i; numDesc++; } }//end for i MPI_Comm_group(orig_comm, &orig_group); /* Divide tasks into two distinct groups based upon rank */ if (static_cast(rank) < splitterRank) { MPI_Group_incl(orig_group, splitterRank, ranksAsc, &new_group); }else { MPI_Group_incl(orig_group, (npes-splitterRank), ranksDesc, &new_group); } MPI_Comm_create(orig_comm, new_group, new_comm); delete [] ranksAsc; ranksAsc = NULL; delete [] ranksDesc; ranksDesc = NULL; return splitterRank; }//end function //create Comm groups and remove empty processors... int splitComm2way(bool iAmEmpty, MPI_Comm * new_comm, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif MPI_Group orig_group, new_group; int size; MPI_Comm_size(comm, &size); bool* isEmptyList = new bool[size]; par::Mpi_Allgather(&iAmEmpty, isEmptyList, 1, comm); int numActive=0, numIdle=0; for(int i = 0; i < size; i++) { if(isEmptyList[i]) { numIdle++; }else { numActive++; } }//end for i int* ranksActive = new int[numActive]; int* ranksIdle = new int[numIdle]; numActive=0; numIdle=0; for(int i = 0; i < size; i++) { if(isEmptyList[i]) { ranksIdle[numIdle] = i; numIdle++; }else { ranksActive[numActive] = i; numActive++; } }//end for i delete [] isEmptyList; isEmptyList = NULL; /* Extract the original group handle */ MPI_Comm_group(comm, &orig_group); /* Divide tasks into two distinct groups based upon rank */ if (!iAmEmpty) { MPI_Group_incl(orig_group, numActive, ranksActive, &new_group); }else { MPI_Group_incl(orig_group, numIdle, ranksIdle, &new_group); } /* Create new communicator */ MPI_Comm_create(comm, new_group, new_comm); delete [] ranksActive; ranksActive = NULL; delete [] ranksIdle; ranksIdle = NULL; }//end function int splitCommUsingSplittingRank(int splittingRank, MPI_Comm* new_comm, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif MPI_Group orig_group, new_group; int size; int rank; MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &size); int* ranksActive = new int[splittingRank]; int* ranksIdle = new int[size - splittingRank]; for(int i = 0; i < splittingRank; i++) { ranksActive[i] = i; } for(int i = splittingRank; i < size; i++) { ranksIdle[i - splittingRank] = i; } /* Extract the original group handle */ MPI_Comm_group(comm, &orig_group); /* Divide tasks into two distinct groups based upon rank */ if (rank < splittingRank) { MPI_Group_incl(orig_group, splittingRank, ranksActive, &new_group); }else { MPI_Group_incl(orig_group, (size - splittingRank), ranksIdle, &new_group); } /* Create new communicator */ MPI_Comm_create(comm, new_group, new_comm); delete [] ranksActive; ranksActive = NULL; delete [] ranksIdle; ranksIdle = NULL; }//end function //create Comm groups and remove empty processors... int splitComm2way(const bool* isEmptyList, MPI_Comm * new_comm, MPI_Comm comm) { MPI_Group orig_group, new_group; int size, rank; MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank); int numActive=0, numIdle=0; for(int i = 0; i < size; i++) { if(isEmptyList[i]) { numIdle++; }else { numActive++; } }//end for i int* ranksActive = new int[numActive]; int* ranksIdle = new int[numIdle]; numActive=0; numIdle=0; for(int i = 0; i < size; i++) { if(isEmptyList[i]) { ranksIdle[numIdle] = i; numIdle++; }else { ranksActive[numActive] = i; numActive++; } }//end for i /* Extract the original group handle */ MPI_Comm_group(comm, &orig_group); /* Divide tasks into two distinct groups based upon rank */ if (!isEmptyList[rank]) { MPI_Group_incl(orig_group, numActive, ranksActive, &new_group); }else { MPI_Group_incl(orig_group, numIdle, ranksIdle, &new_group); } /* Create new communicator */ MPI_Comm_create(comm, new_group, new_comm); delete [] ranksActive; ranksActive = NULL; delete [] ranksIdle; ranksIdle = NULL; return 0; }//end function int AdjustCommunicationPattern(std::vector& send_sizes, std::vector& send_partners, std::vector& recv_sizes, std::vector& recv_partners, MPI_Comm comm) { int npes; int rank; MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &npes); unsigned int k = send_sizes.size(); // do scans ... DendroIntL lsz[k]; DendroIntL gsz[k], gscan[k]; for(size_t i = 0; i < send_sizes.size(); ++i) { lsz[i] = send_sizes[i]; } par::Mpi_Scan( lsz, gscan, k, MPI_SUM, comm); if (rank == npes-1) { for(size_t i = 0; i < k; ++i) { gsz[i] = gscan[i]; } } // broadcast from last proc to get total counts, per segment ... par::Mpi_Bcast( gsz, k, npes-1, comm); DendroIntL segment_p0[k]; for(size_t i = 0; i < k; ++i) { segment_p0[i] = (i*npes)/k; } /* * -- Dividing into k segments, so each segment will have npes/k procs. * -- Each proc will have gsz[i]/(npes/k) elements. * -- rank of proc which will get i-th send_buff is, * -- segment_p0[i] + gscan[i] */ // figure out send_partners for k sends // send_partners.clear(); for(size_t i = 0; i < k; ++i) { int new_part; int seg_npes = ( (i == k-1) ? npes - segment_p0[i] : segment_p0[i+1]-segment_p0[i] ); int overhang = gsz[i] % seg_npes; DendroIntL rank_mid = gscan[i] - lsz[i]/2; if ( rank_mid < overhang*(gsz[i]/seg_npes + 1)) { new_part = segment_p0[i] + rank_mid/(gsz[i]/seg_npes + 1); } else { new_part = segment_p0[i] + (rank_mid - overhang)/(gsz[i]/seg_npes); } send_partners[i] = new_part; } int idx=0; if (send_partners[0] == rank) { send_sizes[0] = 0; } for(size_t i = 1; i < k; ++i) { if (send_partners[i] == rank) { send_sizes[i] = 0; idx = i; continue; } if (send_partners[i] == send_partners[i-1]) { send_sizes[idx] += lsz[i]; send_sizes[i]=0; } else { idx = i; } } // let procs know you will be sending to them ... // try MPI one sided comm MPI_Win win; int *rcv; MPI_Alloc_mem(sizeof(int)*npes, MPI_INFO_NULL, &rcv); for(size_t i = 0; i < npes; ++i) rcv[i] = 0; MPI_Win_create(rcv, npes, sizeof(int), MPI_INFO_NULL, MPI_COMM_WORLD, &win); MPI_Win_fence(MPI_MODE_NOPRECEDE, win); for (size_t i = 0; i < send_sizes.size(); i++) { if (send_sizes[i]) { MPI_Put(&(send_sizes[i]), 1, MPI_INT, send_partners[i], rank, 1, MPI_INT, win); } } MPI_Win_fence((MPI_MODE_NOSTORE | MPI_MODE_NOSUCCEED), win); // figure out recv partners and sizes ... recv_sizes.clear(); recv_partners.clear(); for(size_t i = 0; i < npes; ++i) { if (rcv[i]) { recv_partners.push_back(i); recv_sizes.push_back(rcv[i]); } } MPI_Win_free(&win); MPI_Free_mem(rcv); return 1; } }// end namespace CombBLAS_beta_16_2/usort/src/sort_profiler.cpp000755 000765 000024 00000002601 13212627401 022764 0ustar00aydinbulucstaff000000 000000 #include "sort_profiler.h" #ifdef HAVE_PAPI #include #endif #include sort_profiler_t total_sort; sort_profiler_t sample_get_splitters; sort_profiler_t sample_sort_splitters; sort_profiler_t sample_prepare_scatter; sort_profiler_t sample_do_all2all; sort_profiler_t hyper_compute_splitters; sort_profiler_t hyper_communicate; sort_profiler_t hyper_merge; sort_profiler_t hyper_comm_split; sort_profiler_t seq_sort; sort_profiler_t sort_partitionw; long total_bytes; sort_profiler_t::sort_profiler_t () { seconds = 0.0; // openmp wall time p_flpops = 0; // papi floating point operations _pri_seconds = 0.0; _pri_p_flpops = 0; } sort_profiler_t::~sort_profiler_t () { } void sort_profiler_t::start() { _pri_seconds = omp_get_wtime(); flops_papi(); } void sort_profiler_t::stop() { seconds -= _pri_seconds; p_flpops -= _pri_p_flpops; _pri_seconds = omp_get_wtime(); flops_papi(); seconds += _pri_seconds; p_flpops += _pri_p_flpops; } void sort_profiler_t::clear() { seconds = 0.0; p_flpops = 0; _pri_seconds = 0.0; _pri_p_flpops = 0; } void sort_profiler_t::flops_papi() { #ifdef HAVE_PAPI int retval; float rtime, ptime, mflops; retval = PAPI_flops(&rtime, &ptime, &_pri_p_flpops, &mflops); // assert (retval == PAPI_OK); #else _pri_p_flpops = 0; #endif } CombBLAS_beta_16_2/usort/src/main-short.cpp000755 000765 000024 00000001471 13212627401 022160 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include #include #ifdef _PROFILE_SORT #include "sort_profiler.h" #endif #include #include #include int main(int argc, char **argv){ int num_threads = 1; omp_set_num_threads(num_threads); // Initialize MPI MPI_Init(&argc, &argv); int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); std::vector in; if(myrank==0) { in.resize(2); in[0] = 1; in[1] = 2; } double t0 = omp_get_wtime(); par::sampleSort(in, MPI_COMM_WORLD); double t1 = omp_get_wtime(); std::cout << myrank << ": all done in " << t1-t0 << std::endl; MPI_Finalize(); return 0; } CombBLAS_beta_16_2/usort/src/binUtils.cpp000755 000765 000024 00000004074 13212627401 021672 0ustar00aydinbulucstaff000000 000000 /** @file binUtils.C @brief A set of functions for fast binary operations @author Rahul S. Sampath, rahul.sampath@gmail.com @author Hari Sundar, hsundar@gmail.com */ #include #include #include "binUtils.h" namespace binOp { unsigned int fastLog2(unsigned int num) { if(num) { return (binLength(num) - 1); } else { assert(false); return -1; } }//end function unsigned int binLength(unsigned int num) { unsigned int len = 1; while(num > 1) { num = (num >> 1); len++; } return len; }//end function int toBin(unsigned int num, unsigned int binLen, std::vector& numBin) { numBin = std::vector(binLen); for(unsigned int i = 0; i < binLen; i++) { numBin[i]=0; }//end for unsigned int pos = binLen -1; while(num > 0) { numBin[pos] = (num%2); num = num/2; pos--; } //end while return 1; }//end function unsigned int binToDec(unsigned int* numBin, unsigned int binLen) { unsigned int res = 0; for(unsigned int i = 0; i< binLen; i++) { res = (2*res) + numBin[i]; } return res; }//end function bool isPowerOfTwo(unsigned int n) { return (n && (!(n & (n - 1)))); } // compute the next highest power of 2 of 32-bit v int getNextHighestPowerOfTwo(unsigned int n) { unsigned int v = n; assert(v > 0); v--; v |= (v >> 1); v |= (v >> 2); v |= (v >> 4); v |= (v >> 8); v |= (v >> 16); v++; return v; } // compute the prev highest power of 2 of 32-bit v int getPrevHighestPowerOfTwo(unsigned int n) { unsigned int v = n; assert(v > 0); v--; v |= (v >> 1); v |= (v >> 2); v |= (v >> 4); v |= (v >> 8); v |= (v >> 16); v++; return (v >> 1); } unsigned int reversibleHash(unsigned int x) { x*=0xDEADBEEF; x=x^(x>>17); x*=0x01234567; x+=0x88776655; x=x^(x>>4); x=x^(x>>9); x*=0x91827363; x=x^(x>>7); x=x^(x>>11); x=x^(x>>20); x*=0x77773333; return x; } }//end namespace CombBLAS_beta_16_2/usort/src/main.cpp000755 000765 000024 00000045036 13212627401 021030 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include #include #ifdef _PROFILE_SORT #include "sort_profiler.h" #endif #include #include #include #ifdef KWICK #if KWAY > 2 #define SORT_FUNCTION par::HyperQuickSort_kway #else #ifdef SWAPRANKS #define SORT_FUNCTION par::RankSwapSort #else #define SORT_FUNCTION par::HyperQuickSort #endif #endif #else #define SORT_FUNCTION par::sampleSort #endif // #define __VERIFY__ enum DistribType{ UNIF_DISTRIB, GAUSS_DISTRIB, }; void printResults(int num_threads, MPI_Comm comm); void getStats(double val, double *meanV, double *minV, double *maxV, MPI_Comm comm) { int p; double d, din; din = val; MPI_Comm_size(comm, &p); MPI_Reduce(&din, &d, 1, MPI_DOUBLE, MPI_SUM, 0, comm); *meanV = d/p; MPI_Reduce(&din, &d, 1, MPI_DOUBLE, MPI_MIN, 0, comm); *minV = d; MPI_Reduce(&din, &d, 1, MPI_DOUBLE, MPI_MAX, 0, comm); *maxV = d; } DistribType getDistType(char* code) { if(!strcmp(code,"GAUSS\0")){ return GAUSS_DISTRIB; }else{ return UNIF_DISTRIB; } } long getNumElements(char* code) { unsigned int slen = strlen(code); char dtype = code[0]; char tmp[128]; strncpy(tmp, code+1, slen-3); tmp[slen-3] = '\0'; long numBytes = atol(tmp); switch(code[slen-2]) { case 'g': case 'G': numBytes *= 1024*1024*1024; break; case 'k': case 'K': numBytes *= 1024; break; case 'm': case 'M': numBytes *= 1024*1024; break; default: std::cout << "unknown code " << code[slen-2] << std::endl; return 0; }; switch (dtype) { case 'd': // double array return numBytes/sizeof(double); break; case 'f': // float array return numBytes/sizeof(float); break; case 'i': // int array return numBytes/sizeof(int); break; case 'l': // long array return numBytes/sizeof(long); break; default: std::cout << "unknown dtype: " << dtype << std::endl; return 0; }; } template bool verify (std::vector& in_, std::vector &out_, MPI_Comm comm){ // Find out my identity in the default communicator int myrank, p; MPI_Comm_rank(comm, &myrank); MPI_Comm_size(comm,&p); if (!myrank) std::cout << "Verifying sort" << std::endl; std::vector in; { int N_local=in_.size()*sizeof(T); std::vector r_size(p, 0); std::vector r_disp(p, 0); MPI_Gather(&N_local , 1, MPI_INT, &r_size[0], 1, MPI_INT, 0, comm); omp_par::scan(&r_size[0], &r_disp[0], p); if(!myrank) in.resize((r_size[p-1]+r_disp[p-1])/sizeof(T)); MPI_Gatherv((char*)&in_[0], N_local, MPI_BYTE, (char*)&in [0], &r_size[0], &r_disp[0], MPI_BYTE, 0, comm); } std::vector out; { int N_local=out_.size()*sizeof(T); std::vector r_size(p, 0); std::vector r_disp(p, 0); MPI_Gather(&N_local , 1, MPI_INT, &r_size[0], 1, MPI_INT, 0, comm); omp_par::scan(&r_size[0], &r_disp[0], p); if(!myrank) out.resize((r_size[p-1]+r_disp[p-1])/sizeof(T)); MPI_Gatherv((char*)&out_[0], N_local, MPI_BYTE, (char*)&out [0], &r_size[0], &r_disp[0], MPI_BYTE, 0, comm); } if(in.size()!=out.size()){ std::cout<<"Wrong size: in="< qq(nl), out; // printf("%d: read size is %ld\n", myrank, lSize); / * srand(myrank*1713); for (int i=0; i > sortBins = par::Sorted_approx_Select_skewed(qq, 9, MPI_COMM_WORLD); if (!myrank) std::cout << myrank << ": finished approx Select" << std::endl; / * if (!myrank) { std::cout << "splitters = [" << std::endl; for (int i=0; i<9; ++i) std::cout << sortBins[i] << " " << std::endl;; std::cout << "]" << std::endl; } * / / * double t0 = omp_get_wtime(); par::bucketDataAndWriteSkewed(qq, sortBins, "/tmp/foo", MPI_COMM_WORLD); double t1 = omp_get_wtime(); * / double t0 = omp_get_wtime(); // par::HyperQuickSort(qq, out, MPI_COMM_WORLD); par::sampleSort(qq, MPI_COMM_WORLD); double t1 = omp_get_wtime(); std::cout << myrank << ": all done in " << t1-t0 << std::endl; MPI_Finalize(); return 0; //!---------------------------------- */ int proc_group=0; int min_np=1; MPI_Comm comm; std::vector tt(10000,0); int k = 0; // in case size based runs are needed char dtype = argv[2][0]; long N = getNumElements(argv[2]); DistribType dist_type=getDistType(argv[3]); if (!N) { std::cerr << "illegal typeSize code provided: " << argv[2] << std::endl; return 2; } std::string num; std::stringstream mystream; mystream << N*p; num = mystream.str(); int insertPosition = num.length() - 3; while (insertPosition > 0) { num.insert(insertPosition, ","); insertPosition-=3; } if (!myrank) std::cout << "sorting array of size " << num << " keys of type " << dtype << std::endl; // check if arguments are ok ... { // -- full size run double ttt; switch(dtype) { case 'd': ttt = time_sort(N, MPI_COMM_WORLD,dist_type); break; case 'f': ttt = time_sort(N, MPI_COMM_WORLD,dist_type); break; case 'i': ttt = time_sort(N, MPI_COMM_WORLD,dist_type); break; case 'l': ttt = time_sort(N, MPI_COMM_WORLD,dist_type); break; }; #ifdef _PROFILE_SORT if (!myrank) { std::cout << "---------------------------------------------------------------------------" << std::endl; #ifndef KWICK std::cout << "\tSample Sort with " << KWAY << "-way all2all" << "\t\tMean\tMin\tMax" << std::endl; #else #ifdef SWAPRANKS std::cout << "\t" << KWAY << "-way SwapRankSort " << "\t\tMean\tMin\tMax" << std::endl; #else std::cout << "\t" << KWAY << "-way HyperQuickSort" << "\t\tMean\tMin\tMax" << std::endl; #endif #endif std::cout << "---------------------------------------------------------------------------" << std::endl; } printResults(num_threads, MPI_COMM_WORLD); #endif if(!myrank){ tt[100*k+0]=ttt; } } // MPI_Finalize(); // return 0; for(int i=p; myrank=min_np; i=i>>1) proc_group++; MPI_Comm_split(MPI_COMM_WORLD, proc_group, myrank, &comm); { // smaller /2^k runs int myrank_; MPI_Comm_rank(comm, &myrank_); double ttt; switch(dtype) { case 'd': ttt = time_sort(N, comm,dist_type); break; case 'f': ttt = time_sort(N, comm,dist_type); break; case 'i': ttt = time_sort(N, comm,dist_type); break; case 'l': ttt = time_sort(N, comm,dist_type); break; }; if(!myrank_){ tt[100*k+proc_group]=ttt; } } int num_groups=0; if (!myrank) num_groups = proc_group; MPI_Bcast(&num_groups, 1, MPI_INT, 0, MPI_COMM_WORLD); for(size_t i = 0; i < num_groups; ++i) { MPI_Barrier(MPI_COMM_WORLD); if (proc_group == i) { MPI_Barrier(comm); printResults(num_threads, comm); MPI_Barrier(comm); } } MPI_Barrier(MPI_COMM_WORLD); std::vector tt_glb(10000); MPI_Reduce(&tt[0], &tt_glb[0], 10000, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); if(!myrank){ std::cout<<"\nSort - Weak Scaling:\n"; for(int i=0;i0) np=(p>>(i-1))-(p>>i); std::cout<<"\t\t\tP = "< 0) { num.insert(insertPosition, ","); insertPosition-=3; } std::cout << "total comm \t\t\t" << num << " bytes" << std::endl; } #endif #endif if (!myrank) { // std::cout << "---------------------------------------------------------------------------" << std::endl; std::cout << "" << std::endl; } } #define FALSE 0 // Boolean false #define TRUE 1 // Boolean true //=========================================================================== //= Function to generate Zipf (power law) distributed random variables = //= - Input: alpha and N = //= - Output: Returns with Zipf distributed random variable = //=========================================================================== int zipf(double alpha, int n, unsigned int *seedp) { static int first = TRUE; // Static first time flag static double c = 0; // Normalization constant double z; // Uniform random number (0 < z < 1) double sum_prob; // Sum of probabilities double zipf_value; // Computed exponential value to be returned int i; // Loop counter // Compute normalization constant on first call only if (first == TRUE) { for (i=1; i<=n; i++) c = c + (1.0 / pow((double) i, alpha)); c = 1.0 / c; first = FALSE; } // Pull a uniform random number (0 < z < 1) do { z = rand_r(seedp)*(1.0/RAND_MAX); } while ((z == 0) || (z == 1)); static std::vector oopia; #pragma omp critical if(oopia.size()!=n){ oopia.resize(n); for(int i=0;i= z) { zipf_value = i; break; } } // Assert that zipf_value is between 1 and N assert((zipf_value >=1) && (zipf_value <= n)); return(zipf_value); } CombBLAS_beta_16_2/usort/include/usort/000755 000765 000024 00000000000 13271404146 021401 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/usort/include/usort/sort_profiler.h000755 000765 000024 00000002211 13271404146 024442 0ustar00aydinbulucstaff000000 000000 #ifndef SORT_PROFILER_H_1CK8Y26 #define SORT_PROFILER_H_1CK8Y26 class sort_profiler_t { public: sort_profiler_t (); virtual ~sort_profiler_t (); void start(); void stop(); void clear(); public: double seconds; // openmp wall time long long p_flpops; // papi floating point operations private: void flops_papi(); protected: double _pri_seconds; // openmp wall time long long _pri_p_flpops; // papi floating point operations }; extern sort_profiler_t total_sort; extern sort_profiler_t sample_get_splitters; extern sort_profiler_t sample_sort_splitters; extern sort_profiler_t sample_prepare_scatter; extern sort_profiler_t sample_do_all2all; extern sort_profiler_t hyper_compute_splitters; extern sort_profiler_t hyper_communicate; extern sort_profiler_t hyper_merge; extern sort_profiler_t hyper_comm_split; extern sort_profiler_t seq_sort; extern sort_profiler_t sort_partitionw; extern long total_bytes; #endif /* end of include guard: SORT_PROFILER_H_1CK8Y26 */ CombBLAS_beta_16_2/usort/include/usort/dtypes.h000755 000765 000024 00000013651 13271404146 023073 0ustar00aydinbulucstaff000000 000000 #ifndef __DTYPES_H_ #define __DTYPES_H_ #include #include /** @file dtypes.h @brief Traits to determine MPI_DATATYPE from a C++ datatype @author Hari Sundar, hsundar@gmail.com Traits to determine MPI_DATATYPE from a C++ datatype. For non standard C++ datatypes (like classes), we will need to define additional classes. An example is given for the case of the std. complex variable. Additional classes can be added as required. **/ namespace par { /** @class Mpi_datatype @brief An abstract class used for communicating messages using user-defined datatypes. The user must implement the static member function "value()" that returns the MPI_Datatype corresponding to this user-defined datatype. @author Hari Sundar, hsundar@gmail.com @see Mpi_datatype */ template class Mpi_datatype; template class Mpi_pairtype; #define HS_MPIDATATYPE(CTYPE, MPITYPE) \ template <> \ class Mpi_datatype \ { \ public: \ static MPI_Datatype value() {\ return MPITYPE;\ } \ }; HS_MPIDATATYPE(short, MPI_SHORT) HS_MPIDATATYPE(int, MPI_INT) HS_MPIDATATYPE(long, MPI_LONG) HS_MPIDATATYPE(unsigned short, MPI_UNSIGNED_SHORT) HS_MPIDATATYPE(unsigned int, MPI_UNSIGNED) HS_MPIDATATYPE(unsigned long, MPI_UNSIGNED_LONG) HS_MPIDATATYPE(float, MPI_FLOAT) HS_MPIDATATYPE(double, MPI_DOUBLE) HS_MPIDATATYPE(long double, MPI_LONG_DOUBLE) HS_MPIDATATYPE(long long, MPI_LONG_LONG_INT) HS_MPIDATATYPE(char, MPI_CHAR) HS_MPIDATATYPE(unsigned char, MPI_UNSIGNED_CHAR) //PetscScalar is simply a typedef for double. Hence no need to explicitly //define an mpi_datatype for it. #undef HS_MPIDATATYPE #define HS_MPIPAIRDATATYPE(CTYPE1, CTYPE2, MPITYPE) \ template <> \ class Mpi_pairtype \ { \ public: \ static MPI_Datatype value() {\ return MPITYPE;\ } \ }; HS_MPIPAIRDATATYPE(float, int, MPI_FLOAT_INT) HS_MPIPAIRDATATYPE(double, int, MPI_DOUBLE_INT) HS_MPIPAIRDATATYPE(long, int, MPI_LONG_INT) HS_MPIPAIRDATATYPE(int, int, MPI_2INT) HS_MPIPAIRDATATYPE(short, int, MPI_SHORT_INT) HS_MPIPAIRDATATYPE(long double, int, MPI_LONG_DOUBLE_INT) #undef HS_MPIPAIRDATATYPE template class Mpi_datatype > { public: static MPI_Datatype value() { static bool first = true; static MPI_Datatype datatype; if (first) { first = false; MPI_Type_contiguous(2, Mpi_datatype::value(), &datatype); MPI_Type_commit(&datatype); } return datatype; } }; template class Mpi_pairtype { public: static MPI_Datatype value() { static bool first = true; static MPI_Datatype datatype; if (first) { first = false; int block[2]; MPI_Aint disp[2]; MPI_Datatype type[2]; block[0] = 1; block[0] = 1; type[0] = Mpi_datatype::value(); type[1] = Mpi_datatype::value(); disp[0] = 0; disp[1] = sizeof(T1); MPI_Type_create_struct(2, block, disp, type, &datatype); MPI_Type_commit(&datatype); } return datatype; } }; /** @brief A template specialization of the abstract class Mpi_datatype. This can be used for communicating messages of type "bool" @author Rahul Sampath, rahul.sampath@gmail.com */ template <> class Mpi_datatype { static void bool_LOR(void *in, void *inout, int* len, MPI_Datatype * dptr) { for (int i = 0; i < (*len); i++) { ((static_cast(inout))[i]) = ( ((static_cast(in))[i]) || ((static_cast(inout))[i]) ); }//end for }//end function static void bool_LAND(void *in, void *inout, int* len, MPI_Datatype * dptr) { for (int i = 0; i < (*len); i++) { ((static_cast(inout))[i]) = ( ((static_cast(in))[i]) && ((static_cast(inout))[i])); }//end for }//end function public: /** @brief User defined MPI_Operation to perform: second[i] = (first[i] && second[i]), @remark first and second are 2 arrays of type bool and '&&' represents the 'Logical AND' operation. **/ static MPI_Op LAND() { static bool first = true; static MPI_Op land; if (first) { first = false; MPI_Op_create(Mpi_datatype::bool_LAND ,true ,&land); } return land; } /** @brief User defined MPI_Operation to perform: second[i] = (first[i] || second[i]), @remark first and second are 2 arrays of type bool and '||' represents the 'Logical OR' operation. **/ static MPI_Op LOR() { static bool first = true; static MPI_Op lor; if (first) { first = false; MPI_Op_create(Mpi_datatype::bool_LOR ,true ,&lor); } return lor; } /** @return the MPI_Datatype for the C++ datatype "bool" **/ static MPI_Datatype value() { static bool first = true; static MPI_Datatype datatype; if (first) { first = false; MPI_Type_contiguous(sizeof(bool), MPI_BYTE, &datatype); MPI_Type_commit(&datatype); } return datatype; } }; } //end namespace #endif CombBLAS_beta_16_2/usort/include/usort/parUtils.tcc000755 000765 000024 00000256564 13271404146 023724 0ustar00aydinbulucstaff000000 000000 /** @file parUtils.txx @brief Definitions of the templated functions in the par module. @author Rahul S. Sampath, rahul.sampath@gmail.com @author Hari Sundar, hsundar@gmail.com @author Shravan Veerapaneni, shravan@seas.upenn.edu @author Santi Swaroop Adavani, santis@gmail.com */ #include "binUtils.h" #include "seqUtils.h" #include "dtypes.h" #include #include #include #include #include "indexHolder.h" #ifdef _PROFILE_SORT #include "sort_profiler.h" #endif #include "ompUtils.h" #include #ifdef __DEBUG__ #ifndef __DEBUG_PAR__ #define __DEBUG_PAR__ #endif #endif #ifndef KWAY #define KWAY 64 #endif namespace par { template inline int Mpi_Isend(T* buf, int count, int dest, int tag, MPI_Comm comm, MPI_Request* request) { MPI_Isend(buf, count, par::Mpi_datatype::value(), dest, tag, comm, request); return 1; } template inline int Mpi_Issend(T* buf, int count, int dest, int tag, MPI_Comm comm, MPI_Request* request) { MPI_Issend(buf, count, par::Mpi_datatype::value(), dest, tag, comm, request); return 1; } template inline int Mpi_Recv(T* buf, int count, int source, int tag, MPI_Comm comm, MPI_Status* status) { MPI_Recv(buf, count, par::Mpi_datatype::value(), source, tag, comm, status); return 1; } template inline int Mpi_Irecv(T* buf, int count, int source, int tag, MPI_Comm comm, MPI_Request* request) { MPI_Irecv(buf, count, par::Mpi_datatype::value(), source, tag, comm, request); return 1; } template inline int Mpi_Sendrecv( T* sendBuf, int sendCount, int dest, int sendTag, S* recvBuf, int recvCount, int source, int recvTag, MPI_Comm comm, MPI_Status* status) { return MPI_Sendrecv(sendBuf, sendCount, par::Mpi_datatype::value(), dest, sendTag, recvBuf, recvCount, par::Mpi_datatype::value(), source, recvTag, comm, status); } template inline int Mpi_Scan( T* sendbuf, T* recvbuf, int count, MPI_Op op, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif return MPI_Scan(sendbuf, recvbuf, count, par::Mpi_datatype::value(), op, comm); } template inline int Mpi_Allreduce(T* sendbuf, T* recvbuf, int count, MPI_Op op, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif return MPI_Allreduce(sendbuf, recvbuf, count, par::Mpi_datatype::value(), op, comm); } template inline int Mpi_Alltoall(T* sendbuf, T* recvbuf, int count, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif return MPI_Alltoall(sendbuf, count, par::Mpi_datatype::value(), recvbuf, count, par::Mpi_datatype::value(), comm); } template inline int Mpi_Alltoallv (T* sendbuf, int* sendcnts, int* sdispls, T* recvbuf, int* recvcnts, int* rdispls, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif return MPI_Alltoallv( sendbuf, sendcnts, sdispls, par::Mpi_datatype::value(), recvbuf, recvcnts, rdispls, par::Mpi_datatype::value(), comm); return 0; } template inline int Mpi_Gather( T* sendBuffer, T* recvBuffer, int count, int root, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif return MPI_Gather(sendBuffer, count, par::Mpi_datatype::value(), recvBuffer, count, par::Mpi_datatype::value(), root, comm); } template inline int Mpi_Bcast(T* buffer, int count, int root, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif return MPI_Bcast(buffer, count, par::Mpi_datatype::value(), root, comm); } template inline int Mpi_Reduce(T* sendbuf, T* recvbuf, int count, MPI_Op op, int root, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif return MPI_Reduce(sendbuf, recvbuf, count, par::Mpi_datatype::value(), op, root, comm); } template int Mpi_Allgatherv(T* sendBuf, int sendCount, T* recvBuf, int* recvCounts, int* displs, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif #ifdef __USE_A2A_FOR_MPI_ALLGATHER__ int maxSendCount; int npes, rank; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &rank); par::Mpi_Allreduce(&sendCount, &maxSendCount, 1, MPI_MAX, comm); T* dummySendBuf = new T[maxSendCount*npes]; assert(dummySendBuf); #pragma omp parallel for for(int i = 0; i < npes; i++) { for(int j = 0; j < sendCount; j++) { dummySendBuf[(i*maxSendCount) + j] = sendBuf[j]; } } T* dummyRecvBuf = new T[maxSendCount*npes]; assert(dummyRecvBuf); par::Mpi_Alltoall(dummySendBuf, dummyRecvBuf, maxSendCount, comm); #pragma omp parallel for for(int i = 0; i < npes; i++) { for(int j = 0; j < recvCounts[i]; j++) { recvBuf[displs[i] + j] = dummyRecvBuf[(i*maxSendCount) + j]; } } delete [] dummySendBuf; delete [] dummyRecvBuf; return 1; #else return MPI_Allgatherv(sendBuf, sendCount, par::Mpi_datatype::value(), recvBuf, recvCounts, displs, par::Mpi_datatype::value(), comm); #endif } template int Mpi_Allgather(T* sendBuf, T* recvBuf, int count, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif #ifdef __USE_A2A_FOR_MPI_ALLGATHER__ int npes; MPI_Comm_size(comm, &npes); T* dummySendBuf = new T[count*npes]; assert(dummySendBuf); #pragma omp parallel for for(int i = 0; i < npes; i++) { for(int j = 0; j < count; j++) { dummySendBuf[(i*count) + j] = sendBuf[j]; } } par::Mpi_Alltoall(dummySendBuf, recvBuf, count, comm); delete [] dummySendBuf; return 1; #else return MPI_Allgather(sendBuf, count, par::Mpi_datatype::value(), recvBuf, count, par::Mpi_datatype::value(), comm); #endif } template int Mpi_Alltoallv_sparse(T* sendbuf, int* sendcnts, int* sdispls, T* recvbuf, int* recvcnts, int* rdispls, MPI_Comm comm) { #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif #ifndef ALLTOALLV_FIX return Mpi_Alltoallv (sendbuf, sendcnts, sdispls, recvbuf, recvcnts, rdispls, comm); #else int npes, rank; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &rank); int commCnt = 0; #pragma omp parallel for reduction(+:commCnt) for(int i = 0; i < rank; i++) { if(sendcnts[i] > 0) { commCnt++; } if(recvcnts[i] > 0) { commCnt++; } } #pragma omp parallel for reduction(+:commCnt) for(int i = (rank+1); i < npes; i++) { if(sendcnts[i] > 0) { commCnt++; } if(recvcnts[i] > 0) { commCnt++; } } MPI_Request* requests = new MPI_Request[commCnt]; assert(requests); MPI_Status* statuses = new MPI_Status[commCnt]; assert(statuses); commCnt = 0; //First place all recv requests. Do not recv from self. for(int i = 0; i < rank; i++) { if(recvcnts[i] > 0) { par::Mpi_Irecv( &(recvbuf[rdispls[i]]) , recvcnts[i], i, 1, comm, &(requests[commCnt]) ); commCnt++; } } for(int i = (rank + 1); i < npes; i++) { if(recvcnts[i] > 0) { par::Mpi_Irecv( &(recvbuf[rdispls[i]]) , recvcnts[i], i, 1, comm, &(requests[commCnt]) ); commCnt++; } } //Next send the messages. Do not send to self. for(int i = 0; i < rank; i++) { if(sendcnts[i] > 0) { par::Mpi_Issend( &(sendbuf[sdispls[i]]), sendcnts[i], i, 1, comm, &(requests[commCnt]) ); commCnt++; } } for(int i = (rank + 1); i < npes; i++) { if(sendcnts[i] > 0) { par::Mpi_Issend( &(sendbuf[sdispls[i]]), sendcnts[i], i, 1, comm, &(requests[commCnt]) ); commCnt++; } } //Now copy local portion. #ifdef __DEBUG_PAR__ assert(sendcnts[rank] == recvcnts[rank]); #endif #pragma omp parallel for for(int i = 0; i < sendcnts[rank]; i++) { recvbuf[rdispls[rank] + i] = sendbuf[sdispls[rank] + i]; } MPI_Waitall(commCnt, requests, statuses); delete [] requests; delete [] statuses; return 1; #endif } //* template int Mpi_Alltoallv_dense(T* sbuff_, int* s_cnt_, int* sdisp_, T* rbuff_, int* r_cnt_, int* rdisp_, MPI_Comm c){ #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif #ifndef ALLTOALLV_FIX return Mpi_Alltoallv (sbuff_, s_cnt_, sdisp_, rbuff_, r_cnt_, rdisp_, c); #else int kway = KWAY; int np, pid; MPI_Comm_size(c, &np); MPI_Comm_rank(c, &pid); int range[2]={0,np}; int split_id, partner; std::vector s_cnt(np); #pragma omp parallel for for(int i=0;i sdisp(np); sdisp[0]=0; omp_par::scan(&s_cnt[0],&sdisp[0],np); char* sbuff=new char[sdisp[np-1]+s_cnt[np-1]]; #pragma omp parallel for for(int i=0;i1){ iter_cnt++; if(kway>range[1]-range[0]) kway=range[1]-range[0]; std::vector new_range(kway+1); for(int i=0;i<=kway;i++) new_range[i]=(range[0]*(kway-i)+range[1]*i)/kway; int p_class=(std::upper_bound(&new_range[0],&new_range[kway],pid)-&new_range[0]-1); int new_np=new_range[p_class+1]-new_range[p_class]; int new_pid=pid-new_range[p_class]; //Communication. { std::vector r_cnt (new_np*kway, 0); std::vector r_cnt_ext(new_np*kway, 0); //Exchange send sizes. for(int i=0;inew_np){ int partner=new_range[i+1]-1; std::vector s_cnt_ext(cmp_np, 0); MPI_Sendrecv(&s_cnt_ext[ 0], cmp_np, MPI_INT, partner, 0, &r_cnt_ext[new_np*i], new_np, MPI_INT, partner, 0, c, &status); } } //Allocate receive buffer. std::vector rdisp (new_np*kway, 0); std::vector rdisp_ext(new_np*kway, 0); int rbuff_size, rbuff_size_ext; char *rbuff, *rbuff_ext; { omp_par::scan(&r_cnt [0], &rdisp [0],new_np*kway); omp_par::scan(&r_cnt_ext[0], &rdisp_ext[0],new_np*kway); rbuff_size = rdisp [new_np*kway-1] + r_cnt [new_np*kway-1]; rbuff_size_ext = rdisp_ext[new_np*kway-1] + r_cnt_ext[new_np*kway-1]; rbuff = new char[rbuff_size ]; rbuff_ext = new char[rbuff_size_ext]; } //Sendrecv data. //* int my_block=kway; while(pidnew_np){ int partner=new_range[i+1]-1; std::vector s_cnt_ext(cmp_np, 0); MPI_Sendrecv( NULL, 0, MPI_BYTE, partner, 0, &rbuff[rdisp_ext[new_np*i]], r_cnt_ext[new_np*(i+1)-1]+rdisp_ext[new_np*(i+1)-1]-rdisp_ext[new_np*i], MPI_BYTE, partner, 0, c, &status); } } } //Rearrange received data. { if(sbuff!=NULL) delete[] sbuff; sbuff=new char[rbuff_size+rbuff_size_ext]; std::vector cnt_new(2*new_np*kway, 0); std::vector disp_new(2*new_np*kway, 0); for(int i=0;i buff_ptr(np); char* tmp_ptr=sbuff; for(int i=0;i unsigned int defaultWeight(const T *a){ return 1; } template int partitionW(std::vector& nodeList, unsigned int (*getWeight)(const T *), MPI_Comm comm){ #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif int npes; MPI_Comm_size(comm, &npes); if(getWeight == NULL) { getWeight = par::defaultWeight; } int rank; MPI_Comm_rank(comm, &rank); MPI_Request request; MPI_Status status; const bool nEmpty = nodeList.empty(); DendroIntL off1= 0, off2= 0, localWt= 0, totalWt = 0; DendroIntL* wts = NULL; DendroIntL* lscn = NULL; DendroIntL nlSize = nodeList.size(); if(nlSize) { wts = new DendroIntL[nlSize]; assert(wts); lscn= new DendroIntL[nlSize]; assert(lscn); } // First construct arrays of id and wts. #pragma omp parallel for reduction(+:localWt) for (DendroIntL i = 0; i < nlSize; i++){ wts[i] = (*getWeight)( &(nodeList[i]) ); localWt+=wts[i]; } #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!rank) { std::cout<<"Partition: Stage-1 passed."<(&localWt, &totalWt, 1, MPI_SUM, comm); // perform a local scan on the weights first ... DendroIntL zero = 0; if(!nEmpty) { lscn[0]=wts[0]; // for (DendroIntL i = 1; i < nlSize; i++) { // lscn[i] = wts[i] + lscn[i-1]; // }//end for omp_par::scan(&wts[1],lscn,nlSize); // now scan with the final members of par::Mpi_Scan(lscn+nlSize-1, &off1, 1, MPI_SUM, comm ); } else{ par::Mpi_Scan(&zero, &off1, 1, MPI_SUM, comm ); } // communicate the offsets ... if (rank < (npes-1)){ par::Mpi_Issend( &off1, 1, rank+1, 0, comm, &request ); } if (rank){ par::Mpi_Recv( &off2, 1, rank-1, 0, comm, &status ); } else{ off2 = 0; } // add offset to local array #pragma omp parallel for for (DendroIntL i = 0; i < nlSize; i++) { lscn[i] = lscn[i] + off2; // This has the global scan results now ... }//end for #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!rank) { std::cout<<"Partition: Stage-2 passed."< 0) { for (DendroIntL i = 0; i < nlSize; i++) { if(lscn[i] == 0) { sendSz[0]++; }else { int ind=0; if ( lscn[i] <= (extra*(avgLoad + 1)) ) { ind = ((lscn[i] - 1)/(avgLoad + 1)); }else { ind = ((lscn[i] - (1 + extra))/avgLoad); } assert(ind < npes); sendSz[ind]++; }//end if-else }//end for */ /* //This is more effecient and parallelizable than the above. //This has a bug trying a simpler approach below. int ind_min,ind_max; ind_min=(lscn[0]*npesLong)/totalWt-1; ind_max=(lscn[nlSize-1]*npesLong)/totalWt+2; if(ind_min< 0 )ind_min=0; if(ind_max>=npesLong)ind_max=npesLong; #pragma omp parallel for for(int i=ind_min;i())-&lscn[0]; int start = std::upper_bound(&lscn[0], &lscn[nlSize], wt1, std::less())-&lscn[0]; if(i==npesLong-1)end =nlSize; if(i== 0)start=0 ; sendSz[i]=end-start; }// */ #ifdef __DEBUG_PAR__ int tmp_sum=0; for(int i=0;i(sendSz, recvSz, 1, comm); #ifdef __DEBUG_PAR__ DendroIntL totSendToOthers = 0; DendroIntL totRecvFromOthers = 0; for (int i = 0; i < npes; i++) { if(rank != i) { totSendToOthers += sendSz[i]; totRecvFromOthers += recvSz[i]; } } #endif DendroIntL nn=0; // new value of nlSize, ie the local nodes. #pragma omp parallel for reduction(+:nn) for (int i = 0; i < npes; i++) { nn += recvSz[i]; } // compute offsets ... omp_par::scan(sendSz,sendOff,npes); omp_par::scan(recvSz,recvOff,npes); #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!rank) { std::cout<<"Partition: Stage-4 passed."< newNodes(nn); #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!rank) { std::cout<<"Partition: Final alloc successful."<(nodeListPtr, sendSz, sendOff, newNodesPtr, recvSz, recvOff, comm); #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!rank) { std::cout<<"Partition: Stage-5 passed."< container for rbuff. the space required can be * reserved before doing MPI_SendRecv * 3. alternatively, keep a send buffer and recv into original buffer. * */ template int HyperQuickSort(std::vector& arr, MPI_Comm comm_){ // O( ((N/p)+log(p))*(log(N/p)+log(p)) ) #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif #ifdef _PROFILE_SORT long bytes_comm=0; total_sort.start(); #endif // Copy communicator. MPI_Comm comm=comm_; // Get comm size and rank. int npes, myrank, rank_; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &myrank); rank_ = myrank; if(npes==1){ #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0],&arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); total_sort.stop(); #endif } // buffers ... keeping all allocations together std::vector commBuff; std::vector mergeBuff; std::vector glb_splt_cnts(npes); std::vector glb_splt_disp(npes,0); int omp_p=omp_get_max_threads(); srand(myrank); // Local and global sizes. O(log p) long totSize, nelem = arr.size(); assert(nelem); par::Mpi_Allreduce(&nelem, &totSize, 1, MPI_SUM, comm); long nelem_ = nelem; // Local sort. #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0], &arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif // Binary split and merge in each iteration. while(npes>1 && totSize>0){ // O(log p) iterations. //Determine splitters. O( log(N/p) + log(p) ) #ifdef _PROFILE_SORT hyper_compute_splitters.start(); #endif T split_key; long totSize_new; //while(true) { // Take random splitters. O( 1 ) -- Let p * splt_count = glb_splt_count = const = 100~1000 int splt_count = (1000*nelem)/totSize; if (npes>1000) splt_count = ( ((float)rand()/(float)RAND_MAX)*totSize < (1000*nelem) ? 1 : 0 ); if ( splt_count > nelem ) splt_count = nelem; std::vector splitters(splt_count); for(size_t i=0;i(&splt_count, &glb_splt_cnts[0], 1, comm); omp_par::scan(&glb_splt_cnts[0],&glb_splt_disp[0],npes); glb_splt_count = glb_splt_cnts[npes-1] + glb_splt_disp[npes-1]; std::vector glb_splitters(glb_splt_count); MPI_Allgatherv(&splitters[0], splt_count, par::Mpi_datatype::value(), &glb_splitters[0], &glb_splt_cnts[0], &glb_splt_disp[0], par::Mpi_datatype::value(), comm); // Determine split key. O( log(N/p) + log(p) ) std::vector disp(glb_splt_count,0); if(nelem>0){ #pragma omp parallel for for(size_t i=0;i glb_disp(glb_splt_count,0); MPI_Allreduce(&disp[0], &glb_disp[0], glb_splt_count, par::Mpi_datatype::value(), MPI_SUM, comm); long* split_disp = &glb_disp[0]; for(size_t i=0; i split_id?0:split_id+1); int new_np = (myrank<=split_id? split_id+1: npes-split_id-1); int cmp_np = (myrank> split_id? split_id+1: npes-split_id-1); int partner = myrank+cmp_p0-new_p0; if(partner>=npes) partner=npes-1; assert(partner>=0); bool extra_partner=( npes%2==1 && npes-1==myrank ); // Exchange send sizes. char *sbuff, *lbuff; int rsize=0, ssize=0, lsize=0; int ext_rsize=0, ext_ssize=0; size_t split_indx=(nelem>0?std::lower_bound(&arr[0], &arr[nelem], split_key)-&arr[0]:0); ssize= (myrank> split_id? split_indx: nelem-split_indx )*sizeof(T); sbuff=(char*)(myrank> split_id? &arr[0] : &arr[split_indx]); lsize= (myrank<=split_id? split_indx: nelem-split_indx )*sizeof(T); lbuff=(char*)(myrank<=split_id? &arr[0] : &arr[split_indx]); MPI_Status status; MPI_Sendrecv (& ssize,1,MPI_INT, partner,0, & rsize,1,MPI_INT, partner, 0,comm,&status); if(extra_partner) MPI_Sendrecv(&ext_ssize,1,MPI_INT,split_id,0, &ext_rsize,1,MPI_INT,split_id, 0,comm,&status); // Exchange data. commBuff.reserve(rsize/sizeof(T)); char* rbuff = (char *)(&commBuff[0]); char* ext_rbuff=(ext_rsize>0? new char[ext_rsize]: NULL); MPI_Sendrecv (sbuff,ssize,MPI_BYTE, partner,0, rbuff, rsize,MPI_BYTE, partner, 0,comm,&status); if(extra_partner) MPI_Sendrecv( NULL, 0,MPI_BYTE,split_id,0, ext_rbuff,ext_rsize,MPI_BYTE,split_id, 0,comm,&status); #ifdef _PROFILE_SORT bytes_comm += ssize; hyper_communicate.stop(); hyper_merge.start(); #endif int nbuff_size=lsize+rsize+ext_rsize; mergeBuff.reserve(nbuff_size/sizeof(T)); char* nbuff= (char *)(&mergeBuff[0]); // new char[nbuff_size]; omp_par::merge((T*)lbuff, (T*)&lbuff[lsize], (T*)rbuff, (T*)&rbuff[rsize], (T*)nbuff, omp_p, std::less()); if(ext_rsize>0 && nbuff!=NULL){ // XXX case not handled char* nbuff1= new char[nbuff_size]; omp_par::merge((T*)nbuff, (T*)&nbuff[lsize+rsize], (T*)ext_rbuff, (T*)&ext_rbuff[ext_rsize], (T*)nbuff1, omp_p, std::less()); if(nbuff!=NULL) delete[] nbuff; nbuff=nbuff1; } // Copy new data. totSize=totSize_new; nelem = nbuff_size/sizeof(T); /* if(arr_!=NULL) delete[] arr_; arr_=(T*) nbuff; nbuff=NULL; */ mergeBuff.swap(arr); //Free memory. // if( rbuff!=NULL) delete[] rbuff; if(ext_rbuff!=NULL) delete[] ext_rbuff; #ifdef _PROFILE_SORT hyper_merge.stop(); #endif } {// Split comm. O( log(p) ) ?? #ifdef _PROFILE_SORT hyper_comm_split.start(); #endif MPI_Comm scomm; MPI_Comm_split(comm, myrank<=split_id, myrank, &scomm ); comm=scomm; npes =(myrank<=split_id? split_id+1: npes -split_id-1); myrank=(myrank<=split_id? myrank : myrank-split_id-1); #ifdef _PROFILE_SORT hyper_comm_split.stop(); #endif } } // par::partitionW(SortedElem, NULL , comm_); // par::partitionW(arr, NULL , comm_); #ifdef _PROFILE_SORT total_sort.stop(); par::Mpi_Allreduce(&bytes_comm, &total_bytes, 1, MPI_SUM, comm_); // if(!rank_) printf("Total comm is %ld bytes\n", total_comm); #endif return 1; }//end function //-------------------------------------------------------------------------------- template int HyperQuickSort(std::vector& arr, std::vector & SortedElem, MPI_Comm comm_){ // O( ((N/p)+log(p))*(log(N/p)+log(p)) ) #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif #ifdef _PROFILE_SORT total_sort.start(); #endif // Copy communicator. MPI_Comm comm=comm_; // Get comm size and rank. int npes, myrank, myrank_; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &myrank); myrank_=myrank; if(npes==1){ // @dhairya isn't this wrong for the !sort-in-place case ... #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0],&arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif SortedElem = arr; #ifdef _PROFILE_SORT total_sort.stop(); #endif } int omp_p=omp_get_max_threads(); srand(myrank); // Local and global sizes. O(log p) DendroIntL totSize, nelem = arr.size(); assert(nelem); par::Mpi_Allreduce(&nelem, &totSize, 1, MPI_SUM, comm); DendroIntL nelem_ = nelem; // Local sort. #ifdef _PROFILE_SORT seq_sort.start(); #endif T* arr_=new T[nelem]; memcpy (&arr_[0], &arr[0], nelem*sizeof(T)); omp_par::merge_sort(&arr_[0], &arr_[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif // Binary split and merge in each iteration. while(npes>1 && totSize>0){ // O(log p) iterations. //Determine splitters. O( log(N/p) + log(p) ) #ifdef _PROFILE_SORT hyper_compute_splitters.start(); #endif T split_key; DendroIntL totSize_new; //while(true) { // Take random splitters. O( 1 ) -- Let p * splt_count = glb_splt_count = const = 100~1000 int splt_count=(1000*nelem)/totSize; if(npes>1000) splt_count=(((float)rand()/(float)RAND_MAX)*totSize<(1000*nelem)?1:0); if(splt_count>nelem) splt_count=nelem; std::vector splitters(splt_count); for(size_t i=0;i glb_splt_cnts(npes); std::vector glb_splt_disp(npes,0); par::Mpi_Allgather(&splt_count, &glb_splt_cnts[0], 1, comm); omp_par::scan(&glb_splt_cnts[0],&glb_splt_disp[0],npes); glb_splt_count=glb_splt_cnts[npes-1]+glb_splt_disp[npes-1]; std::vector glb_splitters(glb_splt_count); MPI_Allgatherv(& splitters[0], splt_count, par::Mpi_datatype::value(), &glb_splitters[0], &glb_splt_cnts[0], &glb_splt_disp[0], par::Mpi_datatype::value(), comm); // Determine split key. O( log(N/p) + log(p) ) std::vector disp(glb_splt_count,0); if(nelem>0){ #pragma omp parallel for for(size_t i=0;i glb_disp(glb_splt_count,0); MPI_Allreduce(&disp[0], &glb_disp[0], glb_splt_count, par::Mpi_datatype::value(), MPI_SUM, comm); DendroIntL* split_disp=&glb_disp[0]; for(size_t i=0;i split_id?0:split_id+1); int new_np=(myrank<=split_id? split_id+1: npes-split_id-1); int cmp_np=(myrank> split_id? split_id+1: npes-split_id-1); int partner = myrank+cmp_p0-new_p0; if(partner>=npes) partner=npes-1; assert(partner>=0); bool extra_partner=( npes%2==1 && npes-1==myrank ); // Exchange send sizes. char *sbuff, *lbuff; int rsize=0, ssize=0, lsize=0; int ext_rsize=0, ext_ssize=0; size_t split_indx=(nelem>0?std::lower_bound(&arr_[0], &arr_[nelem], split_key)-&arr_[0]:0); ssize= (myrank> split_id? split_indx: nelem-split_indx )*sizeof(T); sbuff=(char*)(myrank> split_id? &arr_[0] : &arr_[split_indx]); lsize= (myrank<=split_id? split_indx: nelem-split_indx )*sizeof(T); lbuff=(char*)(myrank<=split_id? &arr_[0] : &arr_[split_indx]); MPI_Status status; MPI_Sendrecv (& ssize,1,MPI_INT, partner,0, & rsize,1,MPI_INT, partner, 0,comm,&status); if(extra_partner) MPI_Sendrecv(&ext_ssize,1,MPI_INT,split_id,0, &ext_rsize,1,MPI_INT,split_id, 0,comm,&status); // Exchange data. char* rbuff= new char[ rsize] ; char* ext_rbuff=(ext_rsize>0? new char[ext_rsize]: NULL); MPI_Sendrecv (sbuff,ssize,MPI_BYTE, partner,0, rbuff, rsize,MPI_BYTE, partner, 0,comm,&status); if(extra_partner) MPI_Sendrecv( NULL, 0,MPI_BYTE,split_id,0, ext_rbuff,ext_rsize,MPI_BYTE,split_id, 0,comm,&status); #ifdef _PROFILE_SORT hyper_communicate.stop(); hyper_merge.start(); #endif int nbuff_size=lsize+rsize+ext_rsize; char* nbuff= new char[nbuff_size]; omp_par::merge((T*)lbuff, (T*)&lbuff[lsize], (T*)rbuff, (T*)&rbuff[rsize], (T*)nbuff, omp_p, std::less()); if(ext_rsize>0 && nbuff!=NULL){ char* nbuff1= new char[nbuff_size]; omp_par::merge((T*)nbuff, (T*)&nbuff[lsize+rsize], (T*)ext_rbuff, (T*)&ext_rbuff[ext_rsize], (T*)nbuff1, omp_p, std::less()); if(nbuff!=NULL) delete[] nbuff; nbuff=nbuff1; } // Copy new data. totSize=totSize_new; nelem = nbuff_size/sizeof(T); if(arr_!=NULL) delete[] arr_; arr_=(T*) nbuff; nbuff=NULL; //Free memory. if( rbuff!=NULL) delete[] rbuff; if(ext_rbuff!=NULL) delete[] ext_rbuff; #ifdef _PROFILE_SORT hyper_merge.stop(); #endif } #ifdef _PROFILE_SORT hyper_comm_split.start(); #endif {// Split comm. O( log(p) ) ?? MPI_Comm scomm; MPI_Comm_split(comm, myrank<=split_id, myrank, &scomm ); comm=scomm; npes =(myrank<=split_id? split_id+1: npes -split_id-1); myrank=(myrank<=split_id? myrank : myrank-split_id-1); } #ifdef _PROFILE_SORT hyper_comm_split.stop(); #endif } SortedElem.resize(nelem); SortedElem.assign(arr_, &arr_[nelem]); if(arr_!=NULL) delete[] arr_; #ifdef _PROFILE_SORT sort_partitionw.start(); #endif // par::partitionW(SortedElem, NULL , comm_); #ifdef _PROFILE_SORT sort_partitionw.stop(); #endif #ifdef _PROFILE_SORT total_sort.stop(); #endif return 1; }//end function // */ template int HyperQuickSort_kway(std::vector& arr, std::vector & SortedElem, MPI_Comm comm_) { #ifdef _PROFILE_SORT total_sort.clear(); seq_sort.clear(); hyper_compute_splitters.clear(); hyper_communicate.clear(); hyper_merge.clear(); hyper_comm_split.clear(); sort_partitionw.clear(); MPI_Barrier(comm_); total_sort.start(); #endif unsigned int kway = KWAY; int omp_p=omp_get_max_threads(); // Copy communicator. MPI_Comm comm=comm_; // Get comm size and rank. int npes, myrank; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &myrank); srand(myrank); // Local and global sizes. O(log p) size_t totSize, nelem = arr.size(); assert(nelem); par::Mpi_Allreduce(&nelem, &totSize, 1, MPI_SUM, comm); std::vector arr_(nelem*2); //Extra buffer. std::vector arr__(nelem*2); //Extra buffer. // Local sort. #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0], &arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif while(npes>1 && totSize>0){ if(kway>npes) kway = npes; int blk_size=npes/kway; assert(blk_size*kway==npes); int blk_id=myrank/blk_size, new_pid=myrank%blk_size; // Determine splitters. #ifdef _PROFILE_SORT hyper_compute_splitters.start(); #endif std::vector split_key = par::Sorted_approx_Select(arr, kway-1, comm); #ifdef _PROFILE_SORT hyper_compute_splitters.stop(); #endif {// Communication #ifdef _PROFILE_SORT hyper_communicate.start(); #endif // Determine send_size. std::vector send_size(kway), send_disp(kway+1); send_disp[0]=0; send_disp[kway]=arr.size(); for(int i=1;i recv_ptr(kway); std::vector recv_cnt(kway); std::vector recv_size(kway), recv_disp(kway+1,0); for(int i_=0;i_<=kway/2;i_++){ int i1=(blk_id+i_)%kway; int i2=(blk_id+kway-i_)%kway; MPI_Status status; for(int j=0;j<(i_==0 || i_==kway/2?1:2);j++){ int i=(i_==0?i1:((j+blk_id/i_)%2?i1:i2)); int partner=blk_size*i+new_pid; MPI_Sendrecv(&send_size[ i ], 1, MPI_INT, partner, 0, &recv_size[recv_iter], 1, MPI_INT, partner, 0, comm, &status); recv_disp[recv_iter+1]=recv_disp[recv_iter]+recv_size[recv_iter]; recv_ptr[recv_iter]=&arr_[recv_disp[recv_iter]]; recv_cnt[recv_iter]=recv_size[recv_iter]; recv_iter++; } } // Communicate data. int asynch_count=2; recv_iter=0; int merg_indx=2; std::vector reqst(kway*2); std::vector status(kway*2); arr_ .resize(recv_disp[kway]); arr__.resize(recv_disp[kway]); for(int i_=0;i_<=kway/2;i_++){ int i1=(blk_id+i_)%kway; int i2=(blk_id+kway-i_)%kway; for(int j=0;j<(i_==0 || i_==kway/2?1:2);j++){ int i=(i_==0?i1:((j+blk_id/i_)%2?i1:i2)); int partner=blk_size*i+new_pid; if(recv_iter-asynch_count-1>=0) MPI_Waitall(2, &reqst[(recv_iter-asynch_count-1)*2], &status[(recv_iter-asynch_count-1)*2]); par::Mpi_Irecv (&arr_[recv_disp[recv_iter]], recv_size[recv_iter], partner, 1, comm, &reqst[recv_iter*2+0]); par::Mpi_Issend(&arr [send_disp[ i ]], send_size[ i ], partner, 1, comm, &reqst[recv_iter*2+1]); recv_iter++; int flag[2]={0,0}; if(recv_iter>merg_indx) MPI_Test(&reqst[(merg_indx-1)*2],&flag[0],&status[(merg_indx-1)*2]); if(recv_iter>merg_indx) MPI_Test(&reqst[(merg_indx-2)*2],&flag[1],&status[(merg_indx-2)*2]); if(flag[0] && flag[1]){ T* A=&arr_[0]; T* B=&arr__[0]; for(int s=2;merg_indx%s==0;s*=2){ //std ::merge(&A[recv_disp[merg_indx-s/2]],&A[recv_disp[merg_indx ]], // &A[recv_disp[merg_indx-s ]],&A[recv_disp[merg_indx-s/2]], &B[recv_disp[merg_indx-s]]); omp_par::merge(&A[recv_disp[merg_indx-s/2]],&A[recv_disp[merg_indx ]], &A[recv_disp[merg_indx-s ]],&A[recv_disp[merg_indx-s/2]], &B[recv_disp[merg_indx-s]],omp_p,std::less()); T* C=A; A=B; B=C; // Swap } merg_indx+=2; } } } #ifdef _PROFILE_SORT hyper_communicate.stop(); hyper_merge.start(); #endif // Merge remaining parts. while(merg_indx<=(int)kway){ MPI_Waitall(1, &reqst[(merg_indx-1)*2], &status[(merg_indx-1)*2]); MPI_Waitall(1, &reqst[(merg_indx-2)*2], &status[(merg_indx-2)*2]); { T* A=&arr_[0]; T* B=&arr__[0]; for(int s=2;merg_indx%s==0;s*=2){ //std ::merge(&A[recv_disp[merg_indx-s/2]],&A[recv_disp[merg_indx ]], // &A[recv_disp[merg_indx-s ]],&A[recv_disp[merg_indx-s/2]], &B[recv_disp[merg_indx-s]]); omp_par::merge(&A[recv_disp[merg_indx-s/2]],&A[recv_disp[merg_indx ]], &A[recv_disp[merg_indx-s ]],&A[recv_disp[merg_indx-s/2]], &B[recv_disp[merg_indx-s]],omp_p,std::less()); T* C=A; A=B; B=C; // Swap } merg_indx+=2; } } {// Swap buffers. int swap_cond=0; for(int s=2;kway%s==0;s*=2) swap_cond++; if(swap_cond%2==0) swap(arr,arr_); else swap(arr,arr__); } } #ifdef _PROFILE_SORT hyper_merge.stop(); hyper_comm_split.start(); #endif {// Split comm. kway O( log(p) ) ?? MPI_Comm scomm; MPI_Comm_split(comm, blk_id, myrank, &scomm ); if(comm!=comm_) MPI_Comm_free(&comm); comm = scomm; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &myrank); } #ifdef _PROFILE_SORT hyper_comm_split.stop(); #endif } #ifdef _PROFILE_SORT total_sort.stop(); #endif SortedElem=arr; return 1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // for sc13 -- mem effecient HykSort template int HyperQuickSort_kway(std::vector& arr, MPI_Comm comm_) { #ifdef _PROFILE_SORT total_sort.clear(); seq_sort.clear(); hyper_compute_splitters.clear(); hyper_communicate.clear(); hyper_merge.clear(); hyper_comm_split.clear(); sort_partitionw.clear(); MPI_Barrier(comm_); total_sort.start(); #endif unsigned int kway = KWAY; int omp_p = omp_get_max_threads(); // Copy communicator. MPI_Comm comm = comm_; // Get comm size and rank. int npes, myrank; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &myrank); srand(myrank); // Local and global sizes. O(log p) size_t totSize, nelem = arr.size(); assert(nelem); par::Mpi_Allreduce(&nelem, &totSize, 1, MPI_SUM, comm); // dummy array for now ... std::vector arr_(128); // (nelem*2); //Extra buffer. std::vector arr__; // (nelem*2); //Extra buffer. // Local sort. #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0], &arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif while(npes>1 && totSize>0){ if(kway>npes) kway = npes; int blk_size=npes/kway; assert(blk_size*kway==npes); int blk_id=myrank/blk_size, new_pid=myrank%blk_size; // Determine splitters. #ifdef _PROFILE_SORT hyper_compute_splitters.start(); #endif std::vector split_key = par::Sorted_approx_Select(arr, kway-1, comm); #ifdef _PROFILE_SORT hyper_compute_splitters.stop(); #endif {// Communication #ifdef _PROFILE_SORT hyper_communicate.start(); #endif // Determine send_size. std::vector send_size(kway), send_disp(kway+1); send_disp[0] = 0; send_disp[kway] = arr.size(); for(int i=1;i recv_ptr(kway); std::vector recv_cnt(kway); std::vector recv_size(kway), recv_disp(kway+1,0); for(int i_=0;i_<=kway/2;i_++){ int i1=(blk_id+i_)%kway; int i2=(blk_id+kway-i_)%kway; MPI_Status status; for(int j=0;j<(i_==0 || i_==kway/2?1:2);j++){ int i=(i_==0?i1:((j+blk_id/i_)%2?i1:i2)); int partner=blk_size*i+new_pid; MPI_Sendrecv(&send_size[ i ], 1, MPI_INT, partner, 0, &recv_size[recv_iter], 1, MPI_INT, partner, 0, comm, &status); recv_disp[recv_iter+1] = recv_disp[recv_iter]+recv_size[recv_iter]; recv_ptr[recv_iter]=&arr_[0] + recv_disp[recv_iter]; //! @hari - only setting address, doesnt need to be allocated yet (except for 0) recv_cnt[recv_iter]=recv_size[recv_iter]; recv_iter++; } } // Communicate data. int asynch_count=2; recv_iter=0; int merg_indx=2; std::vector reqst(kway*2); std::vector status(kway*2); arr_ .resize(recv_disp[kway]); arr__.resize(recv_disp[kway]); for(int i_=0;i_<=kway/2;i_++){ int i1=(blk_id+i_)%kway; int i2=(blk_id+kway-i_)%kway; for(int j=0;j<(i_==0 || i_==kway/2?1:2);j++){ int i=(i_==0?i1:((j+blk_id/i_)%2?i1:i2)); int partner=blk_size*i+new_pid; if(recv_iter-asynch_count-1>=0) MPI_Waitall(2, &reqst[(recv_iter-asynch_count-1)*2], &status[(recv_iter-asynch_count-1)*2]); par::Mpi_Irecv (&arr_[recv_disp[recv_iter]], recv_size[recv_iter], partner, 1, comm, &reqst[recv_iter*2+0]); par::Mpi_Issend(&arr [send_disp[ i ]], send_size[ i ], partner, 1, comm, &reqst[recv_iter*2+1]); recv_iter++; int flag[2]={0,0}; if ( recv_iter > merg_indx ) MPI_Test(&reqst[(merg_indx-1)*2], &flag[0], &status[(merg_indx-1)*2]); if ( recv_iter > merg_indx ) MPI_Test(&reqst[(merg_indx-2)*2], &flag[1], &status[(merg_indx-2)*2]); if (flag[0] && flag[1]){ T* A=&arr_[0]; T* B=&arr__[0]; for(int s=2; merg_indx%s==0; s*=2){ //std ::merge(&A[recv_disp[merg_indx-s/2]],&A[recv_disp[merg_indx ]], // &A[recv_disp[merg_indx-s ]],&A[recv_disp[merg_indx-s/2]], &B[recv_disp[merg_indx-s]]); omp_par::merge(&A[recv_disp[merg_indx-s/2]],&A[recv_disp[merg_indx ]], &A[recv_disp[merg_indx-s ]],&A[recv_disp[merg_indx-s/2]], &B[recv_disp[merg_indx-s]], omp_p,std::less()); T* C=A; A=B; B=C; // Swap } merg_indx+=2; } } } #ifdef _PROFILE_SORT hyper_communicate.stop(); hyper_merge.start(); #endif // Merge remaining parts. while(merg_indx<=(int)kway){ MPI_Waitall(1, &reqst[(merg_indx-1)*2], &status[(merg_indx-1)*2]); MPI_Waitall(1, &reqst[(merg_indx-2)*2], &status[(merg_indx-2)*2]); { T* A=&arr_[0]; T* B=&arr__[0]; for(int s=2;merg_indx%s==0;s*=2){ //std ::merge(&A[recv_disp[merg_indx-s/2]],&A[recv_disp[merg_indx ]], // &A[recv_disp[merg_indx-s ]],&A[recv_disp[merg_indx-s/2]], &B[recv_disp[merg_indx-s]]); omp_par::merge(&A[recv_disp[merg_indx-s/2]],&A[recv_disp[merg_indx ]], &A[recv_disp[merg_indx-s ]],&A[recv_disp[merg_indx-s/2]], &B[recv_disp[merg_indx-s]],omp_p,std::less()); T* C=A; A=B; B=C; // Swap } merg_indx+=2; } } {// Swap buffers. int swap_cond=0; for(int s=2;kway%s==0;s*=2) swap_cond++; if(swap_cond%2==0) swap(arr,arr_); else swap(arr,arr__); } } #ifdef _PROFILE_SORT hyper_merge.stop(); hyper_comm_split.start(); #endif {// Split comm. kway O( log(p) ) ?? MPI_Comm scomm; MPI_Comm_split(comm, blk_id, myrank, &scomm ); if(comm!=comm_) MPI_Comm_free(&comm); comm = scomm; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &myrank); } #ifdef _PROFILE_SORT hyper_comm_split.stop(); #endif } #ifdef _PROFILE_SORT total_sort.stop(); #endif return 1; } // mem effecient HykSort /// ----------- low mem verison - sc13 ----------------------------------- template int sampleSort(std::vector& arr, MPI_Comm comm){ #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif #ifdef _PROFILE_SORT total_sort.start(); #endif int npes; MPI_Comm_size(comm, &npes); assert(arr.size()); if (npes == 1) { #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0],&arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); total_sort.stop(); #endif } int myrank; MPI_Comm_rank(comm, &myrank); DendroIntL nelem = arr.size(); DendroIntL nelemCopy = nelem; DendroIntL totSize; par::Mpi_Allreduce(&nelemCopy, &totSize, 1, MPI_SUM, comm); DendroIntL npesLong = npes; const DendroIntL FIVE = 5; if(totSize < (FIVE*npesLong*npesLong)) { #ifdef __DEBUG_PAR__ if(!myrank) { std::cout <<" Using bitonic sort since totSize < (5*(npes^2)). totSize: " < "<< totSize<(totSize), &new_comm, comm); } else { new_comm = comm; } #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!myrank) { std::cout<<"SampleSort (small n): Stage-2 passed."<(arr, new_comm); } if(comm!=new_comm)MPI_Comm_free(&new_comm); #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!myrank) { std::cout<<"SampleSort (small n): Stage-3 passed."<(arr, NULL, comm); #ifdef _PROFILE_SORT sort_partitionw.stop(); #endif nelem = arr.size(); #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0],&arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif unsigned long idx; // std::vector > sendSplits(npes-1); std::vector< IndexHolder > splitters; std::vector< std::pair > splitters_pair = par::Sorted_approx_Select_skewed( arr, npes-1, comm); for (int i=0; i ( splitters_pair[i].first, splitters_pair[i].second ) ); } T key_last; DendroIntL zero = 0; MPI_Bcast (&key_last, 1, par::Mpi_datatype::value(), npes-1, comm); splitters.push_back( IndexHolder(key_last, zero) ); omp_par::merge_sort(&splitters[0], &splitters[splitters.size()]); IndexHolder *splittersPtr = NULL; if(!splitters.empty()) { splittersPtr = &(*(splitters.begin())); } int *sendcnts = new int[npes]; assert(sendcnts); int * recvcnts = new int[npes]; assert(recvcnts); int * sdispls = new int[npes]; assert(sdispls); int * rdispls = new int[npes]; assert(rdispls); #pragma omp parallel for for(int k = 0; k < npes; k++){ sendcnts[k] = 0; } { int omp_p=omp_get_max_threads(); int* proc_split = new int[omp_p+1]; DendroIntL* lst_split_indx = new DendroIntL[omp_p+1]; proc_split[0]=0; lst_split_indx[0]=0; lst_split_indx[omp_p]=nelem; #pragma omp parallel for for(int i=1;i()); idx = 2*myrank*nelem/npes + i*(size_t)nelem/omp_p; IndexHolder key( arr[i*(size_t)nelem/omp_p], idx); proc_split[i] = std::upper_bound( &splittersPtr[0], &splittersPtr[npes-1], key, std::less >()) - &splittersPtr[0]; if(proc_split[i]()); lst_split_indx[i] = std::upper_bound(&arr[0], &arr[nelem], splittersPtr[proc_split[i]].value , std::less()) - &arr[0]; }else{ proc_split[i] = npes-1; lst_split_indx[i] = nelem; } } idx = 2*myrank*nelem/npes; #pragma omp parallel for for (int i=0;i(arr[j],idx+j) <= splitters[k]) { sendcnts_++; } else{ if(sendcnts_>0) sendcnts[k]=sendcnts_; sendcnts_=0; k = seq::UpperBound< IndexHolder >(npes-1, splittersPtr, k+1, IndexHolder(arr[j],idx+j) ); if (k == (npes-1) ){ //could not find any splitter >= arr[j] sendcnts_ = (nelem - j); break; } else { assert(k < (npes-1)); assert(splitters[k].value >= arr[j]); sendcnts_++; } }//end if-else }//end for j if(sendcnts_>0) sendcnts[k]=sendcnts_; } delete [] lst_split_indx; delete [] proc_split; } par::Mpi_Alltoall(sendcnts, recvcnts, 1, comm); sdispls[0] = 0; rdispls[0] = 0; omp_par::scan(sendcnts,sdispls,npes); omp_par::scan(recvcnts,rdispls,npes); DendroIntL nsorted = rdispls[npes-1] + recvcnts[npes-1]; std::vector SortedElem(nsorted); T* arrPtr = NULL; T* SortedElemPtr = NULL; if(!arr.empty()) { arrPtr = &(*(arr.begin())); } if(!SortedElem.empty()) { SortedElemPtr = &(*(SortedElem.begin())); } #ifdef _PROFILE_SORT sample_prepare_scatter.stop(); #endif #ifdef _PROFILE_SORT sample_do_all2all.start(); #endif // par::Mpi_Alltoallv_dense(arrPtr, sendcnts, sdispls, SortedElemPtr, recvcnts, rdispls, comm); Mpi_Alltoallv(arrPtr, sendcnts, sdispls, SortedElemPtr, recvcnts, rdispls, comm); #ifdef _PROFILE_SORT sample_do_all2all.stop(); #endif arr.swap(SortedElem); SortedElem.clear(); delete [] sendcnts; sendcnts = NULL; delete [] recvcnts; recvcnts = NULL; delete [] sdispls; sdispls = NULL; delete [] rdispls; rdispls = NULL; #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0], &arr[nsorted]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif #ifdef _PROFILE_SORT total_sort.stop(); #endif }//end function //------------------------------------------------------------------------ template int sampleSort(std::vector& arr, std::vector & SortedElem, MPI_Comm comm){ #ifdef __PROFILE_WITH_BARRIER__ MPI_Barrier(comm); #endif #ifdef _PROFILE_SORT total_sort.start(); #endif int npes; MPI_Comm_size(comm, &npes); assert(arr.size()); if (npes == 1) { #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0],&arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif SortedElem = arr; #ifdef _PROFILE_SORT total_sort.stop(); #endif } std::vector splitters; std::vector allsplitters; int myrank; MPI_Comm_rank(comm, &myrank); DendroIntL nelem = arr.size(); DendroIntL nelemCopy = nelem; DendroIntL totSize; par::Mpi_Allreduce(&nelemCopy, &totSize, 1, MPI_SUM, comm); DendroIntL npesLong = npes; const DendroIntL FIVE = 5; if(totSize < (FIVE*npesLong*npesLong)) { if(!myrank) { std::cout <<" Using bitonic sort since totSize < (5*(npes^2)). totSize: " <(arr, NULL, comm); #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!myrank) { std::cout<<"SampleSort (small n): Stage-1 passed."< "<< totSize<(totSize), &new_comm, comm); } else { new_comm = comm; } #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!myrank) { std::cout<<"SampleSort (small n): Stage-2 passed."<(SortedElem, new_comm); } if(comm!=new_comm) MPI_Comm_free(&new_comm); #ifdef __DEBUG_PAR__ MPI_Barrier(comm); if(!myrank) { std::cout<<"SampleSort (small n): Stage-3 passed."<(arr, NULL, comm); #ifdef _PROFILE_SORT sort_partitionw.stop(); #endif nelem = arr.size(); #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&arr[0],&arr[arr.size()]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif std::vector sendSplits(npes-1); splitters.resize(npes); #pragma omp parallel for for(int i = 1; i < npes; i++) { sendSplits[i-1] = arr[i*nelem/npes]; }//end for i #ifdef _PROFILE_SORT sample_sort_splitters.start(); #endif // sort sendSplits using bitonic ... par::bitonicSort(sendSplits,comm); #ifdef _PROFILE_SORT sample_sort_splitters.stop(); #endif #ifdef _PROFILE_SORT sample_prepare_scatter.start(); #endif // All gather with last element of splitters. T* sendSplitsPtr = NULL; T* splittersPtr = NULL; if(sendSplits.size() > static_cast(npes-2)) { sendSplitsPtr = &(*(sendSplits.begin() + (npes -2))); } if(!splitters.empty()) { splittersPtr = &(*(splitters.begin())); } par::Mpi_Allgather(sendSplitsPtr, splittersPtr, 1, comm); sendSplits.clear(); int *sendcnts = new int[npes]; assert(sendcnts); int * recvcnts = new int[npes]; assert(recvcnts); int * sdispls = new int[npes]; assert(sdispls); int * rdispls = new int[npes]; assert(rdispls); #pragma omp parallel for for(int k = 0; k < npes; k++){ sendcnts[k] = 0; } { int omp_p=omp_get_max_threads(); int* proc_split = new int[omp_p+1]; DendroIntL* lst_split_indx = new DendroIntL[omp_p+1]; proc_split[0]=0; lst_split_indx[0]=0; lst_split_indx[omp_p]=nelem; #pragma omp parallel for for(int i=1;i()); proc_split[i] = std::upper_bound(&splittersPtr[0],&splittersPtr[npes-1],arr[i*(size_t)nelem/omp_p],std::less())-&splittersPtr[0]; if(proc_split[i]()); lst_split_indx[i]=std::upper_bound(&arr[0],&arr[nelem],splittersPtr[proc_split[i]],std::less())-&arr[0]; }else{ proc_split[i]=npes-1; lst_split_indx[i]=nelem; } } #pragma omp parallel for for (int i=0;i0) sendcnts[k]=sendcnts_; sendcnts_=0; k = seq::UpperBound(npes-1, splittersPtr, k+1, arr[j]); if (k == (npes-1) ){ //could not find any splitter >= arr[j] sendcnts_ = (nelem - j); break; } else { assert(k < (npes-1)); assert(splitters[k] >= arr[j]); sendcnts_++; } }//end if-else }//end for j if(sendcnts_>0) sendcnts[k]=sendcnts_; } delete [] lst_split_indx; delete [] proc_split; } par::Mpi_Alltoall(sendcnts, recvcnts, 1, comm); sdispls[0] = 0; rdispls[0] = 0; omp_par::scan(sendcnts,sdispls,npes); omp_par::scan(recvcnts,rdispls,npes); DendroIntL nsorted = rdispls[npes-1] + recvcnts[npes-1]; SortedElem.resize(nsorted); T* arrPtr = NULL; T* SortedElemPtr = NULL; if(!arr.empty()) { arrPtr = &(*(arr.begin())); } if(!SortedElem.empty()) { SortedElemPtr = &(*(SortedElem.begin())); } #ifdef _PROFILE_SORT sample_prepare_scatter.stop(); #endif #ifdef _PROFILE_SORT sample_do_all2all.start(); #endif par::Mpi_Alltoallv_dense(arrPtr, sendcnts, sdispls, SortedElemPtr, recvcnts, rdispls, comm); #ifdef _PROFILE_SORT sample_do_all2all.stop(); #endif arr.clear(); delete [] sendcnts; sendcnts = NULL; delete [] recvcnts; recvcnts = NULL; delete [] sdispls; sdispls = NULL; delete [] rdispls; rdispls = NULL; #ifdef _PROFILE_SORT seq_sort.start(); #endif omp_par::merge_sort(&SortedElem[0], &SortedElem[nsorted]); #ifdef _PROFILE_SORT seq_sort.stop(); #endif #ifdef _PROFILE_SORT total_sort.stop(); #endif }//end function /********************************************************************/ /* * which_keys is one of KEEP_HIGH or KEEP_LOW * partner is the processor with which to Merge and Split. * */ template void MergeSplit( std::vector &local_list, int which_keys, int partner, MPI_Comm comm) { MPI_Status status; int send_size = local_list.size(); int recv_size = 0; // first communicate how many you will send and how many you will receive ... int my_rank; MPI_Comm_rank(comm, &my_rank); par::Mpi_Sendrecv( &send_size , 1, partner, 0, &recv_size, 1, partner, 0, comm, &status); // if (!my_rank || my_rank==2) // std::cout << my_rank << " <--> " << partner << " -> " << send_size << " <- " << recv_size << std::endl; std::vector temp_list( recv_size, local_list[0] ); T* local_listPtr = NULL; T* temp_listPtr = NULL; if(!local_list.empty()) { local_listPtr = &(*(local_list.begin())); } if(!temp_list.empty()) { temp_listPtr = &(*(temp_list.begin())); } par::Mpi_Sendrecv( local_listPtr, send_size, partner, 1, temp_listPtr, recv_size, partner, 1, comm, &status); MergeLists(local_list, temp_list, which_keys); temp_list.clear(); } // Merge_split template void Par_bitonic_sort_incr( std::vector &local_list, int proc_set_size, MPI_Comm comm ) { int eor_bit; int proc_set_dim; int stage; int partner; int my_rank; MPI_Comm_rank(comm, &my_rank); proc_set_dim = 0; int x = proc_set_size; while (x > 1) { x = x >> 1; proc_set_dim++; } eor_bit = (1 << (proc_set_dim - 1) ); for (stage = 0; stage < proc_set_dim; stage++) { partner = (my_rank ^ eor_bit); if (my_rank < partner) { MergeSplit ( local_list, KEEP_LOW, partner, comm); } else { MergeSplit ( local_list, KEEP_HIGH, partner, comm); } eor_bit = (eor_bit >> 1); } } // Par_bitonic_sort_incr template void Par_bitonic_sort_decr( std::vector &local_list, int proc_set_size, MPI_Comm comm) { int eor_bit; int proc_set_dim; int stage; int partner; int my_rank; MPI_Comm_rank(comm, &my_rank); proc_set_dim = 0; int x = proc_set_size; while (x > 1) { x = x >> 1; proc_set_dim++; } eor_bit = (1 << (proc_set_dim - 1)); for (stage = 0; stage < proc_set_dim; stage++) { partner = my_rank ^ eor_bit; if (my_rank > partner) { MergeSplit ( local_list, KEEP_LOW, partner, comm); } else { MergeSplit ( local_list, KEEP_HIGH, partner, comm); } eor_bit = (eor_bit >> 1); } } // Par_bitonic_sort_decr template void Par_bitonic_merge_incr( std::vector &local_list, int proc_set_size, MPI_Comm comm ) { int partner; int rank, npes; MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &npes); unsigned int num_left = binOp::getPrevHighestPowerOfTwo(npes); unsigned int num_right = npes - num_left; // 1, Do merge between the k right procs and the highest k left procs. if ( (static_cast(rank) < num_left) && (static_cast(rank) >= (num_left - num_right)) ) { partner = static_cast(rank) + num_right; MergeSplit ( local_list, KEEP_LOW, partner, comm); } else if (static_cast(rank) >= num_left) { partner = static_cast(rank) - num_right; MergeSplit ( local_list, KEEP_HIGH, partner, comm); } } template void bitonicSort_binary(std::vector & in, MPI_Comm comm) { int proc_set_size; unsigned int and_bit; int rank; int npes; MPI_Comm_size(comm, &npes); #ifdef __DEBUG_PAR__ assert(npes > 1); assert(!(npes & (npes-1))); assert(!(in.empty())); #endif MPI_Comm_rank(comm, &rank); for (proc_set_size = 2, and_bit = 2; proc_set_size <= npes; proc_set_size = proc_set_size*2, and_bit = and_bit << 1) { if ((rank & and_bit) == 0) { Par_bitonic_sort_incr( in, proc_set_size, comm); } else { Par_bitonic_sort_decr( in, proc_set_size, comm); } }//end for } template void bitonicSort(std::vector & in, MPI_Comm comm) { int rank; int npes; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &rank); assert(!(in.empty())); omp_par::merge_sort(&in[0],&in[in.size()]); MPI_Barrier(comm); if(npes > 1) { // check if npes is a power of two ... bool isPower = (!(npes & (npes - 1))); if ( isPower ) { bitonicSort_binary(in, comm); } else { MPI_Comm new_comm; // Since npes is not a power of two, we shall split the problem in two ... // // 1. Create 2 comm groups ... one for the 2^d portion and one for the // remainder. unsigned int splitter = splitCommBinary(comm, &new_comm); if ( static_cast(rank) < splitter) { bitonicSort_binary(in, new_comm); } else { bitonicSort(in, new_comm); } MPI_Comm_free(&new_comm); // 3. Do a special merge of the two segments. (original comm). Par_bitonic_merge_incr( in, binOp::getNextHighestPowerOfTwo(npes), comm ); splitter = splitCommBinaryNoFlip(comm, &new_comm); // 4. Now a final sort on the segments. if (static_cast(rank) < splitter) { bitonicSort_binary(in, new_comm); } else { bitonicSort(in, new_comm); } MPI_Comm_free(&new_comm); }//end if isPower of 2 }//end if single processor }//end function template void MergeLists( std::vector &listA, std::vector &listB, int KEEP_WHAT) { T _low, _high; assert(!(listA.empty())); assert(!(listB.empty())); _low = ( (listA[0] > listB[0]) ? listA[0] : listB[0]); _high = ( (listA[listA.size()-1] < listB[listB.size()-1]) ? listA[listA.size()-1] : listB[listB.size()-1]); // We will do a full merge first ... size_t list_size = listA.size() + listB.size(); std::vector scratch_list(list_size); unsigned int index1 = 0; unsigned int index2 = 0; for (size_t i = 0; i < list_size; i++) { //The order of (A || B) is important here, //so that index2 remains within bounds if ( (index1 < listA.size()) && ( (index2 >= listB.size()) || (listA[index1] <= listB[index2]) ) ) { scratch_list[i] = listA[index1]; index1++; } else { scratch_list[i] = listB[index2]; index2++; } } //Scratch list is sorted at this point. listA.clear(); listB.clear(); if ( KEEP_WHAT == KEEP_LOW ) { int ii=0; while ( ( (scratch_list[ii] < _low) || (ii < (list_size/2)) ) && (scratch_list[ii] <= _high) ) { ii++; } if(ii) { listA.insert(listA.end(), scratch_list.begin(), (scratch_list.begin() + ii)); } } else { int ii = (list_size - 1); while ( ( (ii >= (list_size/2)) && (scratch_list[ii] >= _low) ) || (scratch_list[ii] > _high) ) { ii--; } if(ii < (list_size - 1) ) { listA.insert(listA.begin(), (scratch_list.begin() + (ii + 1)), (scratch_list.begin() + list_size)); } } scratch_list.clear(); }//end function template std::vector Sorted_Sample_Select(std::vector& arr, unsigned int kway, std::vector& min_idx, std::vector& max_idx, std::vector& splitter_ranks, MPI_Comm comm) { int rank, npes; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &rank); //------------------------------------------- DendroIntL totSize, nelem = arr.size(); par::Mpi_Allreduce(&nelem, &totSize, 1, MPI_SUM, comm); //Determine splitters. O( log(N/p) + log(p) ) int splt_count = (1000*kway*nelem)/totSize; if (npes>1000*kway) splt_count = (((float)rand()/(float)RAND_MAX)*totSize<(1000*kway*nelem)?1:0); if (splt_count>nelem) splt_count=nelem; std::vector splitters(splt_count); for(size_t i=0;i glb_splt_cnts(npes); std::vector glb_splt_disp(npes,0); par::Mpi_Allgather(&splt_count, &glb_splt_cnts[0], 1, comm); omp_par::scan(&glb_splt_cnts[0],&glb_splt_disp[0],npes); glb_splt_count = glb_splt_cnts[npes-1] + glb_splt_disp[npes-1]; std::vector glb_splitters(glb_splt_count); MPI_Allgatherv(& splitters[0], splt_count, par::Mpi_datatype::value(), &glb_splitters[0], &glb_splt_cnts[0], &glb_splt_disp[0], par::Mpi_datatype::value(), comm); // rank splitters. O( log(N/p) + log(p) ) std::vector disp(glb_splt_count,0); if(nelem>0){ #pragma omp parallel for for(size_t i=0; i glb_disp(glb_splt_count, 0); MPI_Allreduce(&disp[0], &glb_disp[0], glb_splt_count, par::Mpi_datatype::value(), MPI_SUM, comm); splitter_ranks.clear(); splitter_ranks.resize(kway); min_idx.clear(); min_idx.resize(kway); max_idx.clear(); max_idx.resize(kway); std::vector split_keys(kway); #pragma omp parallel for for (unsigned int qq=0; qq optSplitter) && ( labs(glb_disp[i] - optSplitter) < labs(*_maxd - optSplitter)) ) { _maxd = &glb_disp[i]; } if( (glb_disp[i] < optSplitter) && ( labs(optSplitter - glb_disp[i]) < labs(optSplitter - *_mind)) ) { _mind = &glb_disp[i]; } } split_keys[qq] = glb_splitters[_disp - &glb_disp[0]]; min_idx[qq] = std::lower_bound(&arr[0], &arr[nelem], glb_splitters[_mind - &glb_disp[0]]) - &arr[0]; max_idx[qq] = std::upper_bound(&arr[0], &arr[nelem], glb_splitters[_maxd - &glb_disp[0]]) - &arr[0]; splitter_ranks[qq] = optSplitter - *_mind; } return split_keys; } template void Sorted_approx_Select_helper(std::vector& arr, std::vector& exp_rank, std::vector& splt_key, int beta, std::vector& start, std::vector& end, size_t& max_err, MPI_Comm comm) { int rank, npes; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &rank); size_t nelem=arr.size(); int kway=exp_rank.size(); std::vector locSize(kway), totSize(kway); for(int i=0;i(&locSize[0], &totSize[0], kway, MPI_SUM, comm); //------------------------------------------- std::vector loc_splt; for(int i=0;ibeta) splt_count = (((float)rand()/(float)RAND_MAX)*totSize[i]<(beta*locSize[i])?1:0); for(int j=0;j glb_splt_cnts(npes); std::vector glb_splt_disp(npes,0); par::Mpi_Allgather(&splt_count, &glb_splt_cnts[0], 1, comm); omp_par::scan(&glb_splt_cnts[0],&glb_splt_disp[0],npes); glb_splt_count = glb_splt_cnts[npes-1] + glb_splt_disp[npes-1]; std::vector glb_splt(glb_splt_count); MPI_Allgatherv(&loc_splt[0], splt_count, par::Mpi_datatype::value(), &glb_splt[0], &glb_splt_cnts[0], &glb_splt_disp[0], par::Mpi_datatype::value(), comm); //MPI_Barrier(comm); tt[dbg_cnt]+=omp_get_wtime(); dbg_cnt++; ////////////////////////////////////////////////////////////////////// std::sort(&glb_splt[0],&glb_splt[glb_splt_count]); //MPI_Barrier(comm); tt[dbg_cnt]+=omp_get_wtime(); dbg_cnt++; ////////////////////////////////////////////////////////////////////// // rank splitters. O( log(N/p) + log(p) ) std::vector loc_rank(glb_splt_count,0); if(nelem>0){ #pragma omp parallel for for(size_t i=0; i glb_rank(glb_splt_count, 0); MPI_Allreduce(&loc_rank[0], &glb_rank[0], glb_splt_count, par::Mpi_datatype::value(), MPI_SUM, comm); //MPI_Barrier(comm); tt[dbg_cnt]+=omp_get_wtime(); dbg_cnt++; ////////////////////////////////////////////////////////////////////// size_t new_max_err=0; std::vector split_keys(kway); // #pragma omp parallel for for (int i=0; iexp_rank[i]) start[i]=0; else start[i] = loc_rank[lb_indx]; if(ub_indx==glb_splt_count) end[i]=nelem; else end[i] = loc_rank[ub_indx]; splt_key[i]=glb_splt[lb_indx]; if(new_max_err std::vector Sorted_approx_Select_recursive(std::vector& arr, unsigned int kway, MPI_Comm comm) { int rank, npes; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &rank); //------------------------------------------- DendroIntL totSize, nelem = arr.size(); par::Mpi_Allreduce(&nelem, &totSize, 1, MPI_SUM, comm); double tol=1e-2/kway; int beta=pow(1.0/tol,1.0/3.0)*3.0; std::vector splt_key(kway); std::vector start(kway,0); std::vector end(kway,nelem); std::vector exp_rank(kway); for(int i=0;itotSize*tol){ Sorted_approx_Select_helper(arr, exp_rank, splt_key, beta, start, end, max_error, comm); } return splt_key; } template std::vector Sorted_approx_Select(std::vector& arr, unsigned int kway, MPI_Comm comm) { int rank, npes; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &rank); //------------------------------------------- DendroIntL totSize, nelem = arr.size(); par::Mpi_Allreduce(&nelem, &totSize, 1, MPI_SUM, comm); //Determine splitters. O( log(N/p) + log(p) ) int splt_count = (1000*kway*nelem)/totSize; if (npes>1000*kway) splt_count = (((float)rand()/(float)RAND_MAX)*totSize<(1000*kway*nelem)?1:0); if (splt_count>nelem) splt_count=nelem; std::vector splitters(splt_count); for(size_t i=0;i glb_splt_cnts(npes); std::vector glb_splt_disp(npes,0); par::Mpi_Allgather(&splt_count, &glb_splt_cnts[0], 1, comm); omp_par::scan(&glb_splt_cnts[0],&glb_splt_disp[0],npes); glb_splt_count = glb_splt_cnts[npes-1] + glb_splt_disp[npes-1]; std::vector glb_splitters(glb_splt_count); MPI_Allgatherv(& splitters[0], splt_count, par::Mpi_datatype::value(), &glb_splitters[0], &glb_splt_cnts[0], &glb_splt_disp[0], par::Mpi_datatype::value(), comm); // rank splitters. O( log(N/p) + log(p) ) std::vector disp(glb_splt_count,0); if(nelem>0){ #pragma omp parallel for for(size_t i=0; i glb_disp(glb_splt_count, 0); MPI_Allreduce(&disp[0], &glb_disp[0], glb_splt_count, par::Mpi_datatype::value(), MPI_SUM, comm); std::vector split_keys(kway); #pragma omp parallel for for (unsigned int qq=0; qq std::vector > Sorted_approx_Select_skewed (std::vector& arr, unsigned int kway, MPI_Comm comm) { int rank, npes; MPI_Comm_size(comm, &npes); MPI_Comm_rank(comm, &rank); //------------------------------------------- DendroIntL totSize, nelem = arr.size(); par::Mpi_Allreduce(&nelem, &totSize, 1, MPI_SUM, comm); //Determine splitters. O( log(N/p) + log(p) ) int splt_count = (1000*kway*nelem)/totSize; if (npes>1000*kway) splt_count = (((float)rand()/(float)RAND_MAX)*totSize<(1000*kway*nelem)?1:0); if (splt_count>nelem) splt_count=nelem; //! this changes to a pair ? // long should be sufficient for some time at least // 1<<63 <- 9,223,372,036,854,775,808 (9 Quintillion ) std::vector splitters(splt_count); std::vector dup_ranks(splt_count); for(size_t i=0;i glb_splt_cnts(npes); std::vector glb_splt_disp(npes,0); par::Mpi_Allgather(&splt_count, &glb_splt_cnts[0], 1, comm); omp_par::scan(&glb_splt_cnts[0],&glb_splt_disp[0],npes); glb_splt_count = glb_splt_cnts[npes-1] + glb_splt_disp[npes-1]; std::vector glb_splitters (glb_splt_count); std::vector glb_dup_ranks (glb_splt_count); MPI_Allgatherv(& dup_ranks[0], splt_count, par::Mpi_datatype::value(), &glb_dup_ranks[0], &glb_splt_cnts[0], &glb_splt_disp[0], par::Mpi_datatype::value(), comm); MPI_Allgatherv(& splitters[0], splt_count, par::Mpi_datatype::value(), &glb_splitters[0], &glb_splt_cnts[0], &glb_splt_disp[0], par::Mpi_datatype::value(), comm); // std::cout << rank << ": ranking splitters " << std::endl; // rank splitters. O( log(N/p) + log(p) ) std::vector disp(glb_splt_count, 0); DendroIntL dLow, dHigh; if(nelem>0){ #pragma omp parallel for for(size_t i=0; i 1 ) { DendroIntL sRank = glb_dup_ranks[i]*npes/2/totSize; if (sRank < rank ) { disp[i] = dLow; } else if (sRank > rank) { disp[i] = dHigh; } else { disp[i] = glb_dup_ranks[i] - (2*rank*totSize/npes); } } else { disp[i] = dLow; } } } std::vector glb_disp(glb_splt_count, 0); // std::cout << rank << ": all reduce " << std::endl; MPI_Allreduce(&disp[0], &glb_disp[0], glb_splt_count, par::Mpi_datatype::value(), MPI_SUM, comm); std::vector< std::pair > split_keys(kway); std::pair key_pair; #pragma omp parallel for for (unsigned int qq=0; qq /** @brief A small helper class used while sorting a pair of value and its index in an array/vector. */ template class IndexHolder { public: unsigned long index; T value; /** @name Constructors and Destructor */ //@{ IndexHolder() { value = T(); index = 0; }; IndexHolder(T v, unsigned long i) { value = v; index = i;}; IndexHolder(const IndexHolder& rhs) { value = rhs.value; index = rhs.index; }; ~IndexHolder() {}; //@} /** @return a < b */ static bool lessThan (const IndexHolder& a, const IndexHolder& b) { return a < b; } /** @name Overload Operators */ //@{ IndexHolder& operator= (const IndexHolder& other) { if ( this != &other ) { value = other.value; index = other.index; } return *this; } friend std::ostream& operator<<(std::ostream& os, const IndexHolder& dt) { os << "idx-hldr: " << dt.value << ":" << dt.index << std::endl; return os; } bool operator < ( IndexHolder const &other) const { if ( value == other.value ) return (index < other.index ); else return ( value < other.value ); } bool operator > ( IndexHolder const &other) const { if ( value == other.value ) return (index > other.index ); else return ( value > other.value ); } bool operator <= ( IndexHolder const &other) const { if ( value == other.value ) return (index <= other.index ); else return ( value <= other.value ); } bool operator >= ( IndexHolder const &other) const { if ( value == other.value ) return (index >= other.index ); else return ( value >= other.value ); } bool operator == ( IndexHolder const &other) const { return ( ( value == other.value ) && (index == other.index) ); } bool operator != ( IndexHolder const &other) const { return ( ( value != other.value) || (index != other.index) ); } //@} }; namespace par { //Forward Declaration template class Mpi_datatype; template class Mpi_datatype > { public: static MPI_Datatype value() { static bool first = true; static MPI_Datatype datatype; if (first) { /* IndexHolder test; first = false; int block[2]; MPI_Aint disp[2]; MPI_Datatype type[2]; block[0] = 1; block[0] = 1; type[0] = MPI_LONG; type[1] = Mpi_datatype::value(); disp[0] = 0; disp[1] = sizeof(long); // sizeof(unsigned long); */ MPI_Type_contiguous(sizeof(IndexHolder), MPI_BYTE, &datatype); // MPI_Type_create_struct(2, block, disp, type, &datatype); MPI_Type_commit(&datatype); } return datatype; } }; }//end namespace par #endif CombBLAS_beta_16_2/usort/include/usort/seqUtils.h000755 000765 000024 00000007162 13271404146 023374 0ustar00aydinbulucstaff000000 000000 /** @file seqUtils.h @brief A set of sequential utilities. @author Rahul S. Sampath, rahul.sampath@gmail.com */ #ifndef __SEQ_UTILS_H_ #define __SEQ_UTILS_H_ #include /** @namespace seq @author Rahul Sampath @brief Collection of Generic Sequential Functions. */ namespace seq { /** * @brief Flash sort algo to sort an array in O(n). * * @param a The array to be sorted * @param n The number of elements in a * @param m Size of index vector. typically m = 0.1*n * @param ctr The number of times flashsort was called. * * Sorts array a with n elements by use of the index vector l of * dimension m (with m about 0.1 n). * The routine runs fastest with a uniform distribution of elements. * The vector l is declare dynamically using the calloc function. * The variable ctr counts the number of times that flashsort is called. * THRESHOLD is a very important constant. It is the minimum number of * elements required in a subclass before recursion is used. * * Templated version of flashsort based on original C code by * Karl-Dietrich Neubert. */ template void flashsort(T* a, int n, int m, int *ctr); /** @brief Removes duplicates from the vector. @author Rahul Sampath @param vec The vector to be made free of duplicates. @param isSorted Pass 'true' if the input is sorted. If the vector is not sorted, it is first sorted before removing duplicates. */ template void makeVectorUnique(std::vector& vec, bool isSorted) ; /** @brief A binary search implementation. @author Rahul Sampath @param arr A sorted array @param nelem The length of the array @param key The search key @param idx 0-based index of the position of the key in the array @return 'true' if the key exists in the array and 'false' otherwise */ template bool BinarySearch(const T* arr, unsigned int nelem, const T & key, unsigned int *idx) ; /** @brief Finds the index of the smallest upper bound of the search key in the array. @author Santi Swaroop Adavani @param arr A sorted array @param nelem The length of the array @param startIdx The starting location to search from @param key The search key @return the index of the first element in the array >= key */ template int UpperBound (unsigned int nelem,const T * arr,unsigned int startIdx, const T & key); /** @brief Finds the index of the greatest lower bound of the search key in the array. The implementation uses a simple modification of the binary search algorithm. @author Rahul Sampath @param arr A sorted array @param key The search key @param retIdx The index of the position of the last element in the array <= key @param leftIdx If this is not NULL, then the search will be limited to elements at positions >= *leftIdx @param rightIdx if this is not NULL, then the search will be limited to elements at positions <= *rightIdx @return 'true' if the search was successful */ template bool maxLowerBound(const std::vector & arr,const T & key, unsigned int & retIdx, unsigned int* leftIdx, unsigned int* rightIdx); /* template typename std::iterator_traits::difference_type BinSearch(T A,T A_last, const typename std::iterator_traits::value_type& val,StrictWeakOrdering comp); template void Merge(T A_,T A_last,T B_,T B_last,T C_,StrictWeakOrdering comp); */ }//end namespace #include "seqUtils.tcc" #endif CombBLAS_beta_16_2/usort/include/usort/ompUtils.tcc000755 000765 000024 00000015053 13271404146 023717 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include // #include template void omp_par::merge(T A_,T A_last,T B_,T B_last,T C_,int p,StrictWeakOrdering comp){ typedef typename std::iterator_traits::difference_type _DiffType; typedef typename std::iterator_traits::value_type _ValType; _DiffType N1=A_last-A_; _DiffType N2=B_last-B_; if(N1==0 && N2==0) return; if(N1==0 || N2==0){ _ValType* A=(N1==0? &B_[0]: &A_[0]); _DiffType N=(N1==0? N2 : N1 ); #pragma omp parallel for for(int i=0;i())-&split_size[0]; if(j>=p*n) j=p*n-1; _ValType split1 =split [j]; _DiffType split_size1=split_size[j]; j=(std::lower_bound(&split_size[p*n],&split_size[p*n*2],req_size,std::less<_DiffType>())-&split_size[p*n])+p*n; if(j>=2*p*n) j=2*p*n-1; if(abs(split_size[j]-req_size)::merge(A_+split_indx_A[i],A_+split_indx_A[i+1],B_+split_indx_B[i],B_+split_indx_B[i+1],C); std::merge(A_+split_indx_A[i],A_+split_indx_A[i+1],B_+split_indx_B[i],B_+split_indx_B[i+1],C); } delete[] split_indx_A; delete[] split_indx_B; } template void omp_par::merge_sort(T A,T A_last,StrictWeakOrdering comp){ typedef typename std::iterator_traits::difference_type _DiffType; typedef typename std::iterator_traits::value_type _ValType; int p=omp_get_max_threads(); _DiffType N=A_last-A; if(N<2*p || p==1){ std::sort(A,A_last,comp); return; } //Split the array A into p equal parts. _DiffType* split=new _DiffType[p+1]; split[p]=N; #pragma omp parallel for for(int id=0;id void omp_par::merge_sort(T A,T A_last){ typedef typename std::iterator_traits::value_type _ValType; if(sizeof(_ValType)<=8*sizeof(_ValType*)) omp_par::merge_sort(A,A_last,std::less<_ValType>()); else omp_par::merge_sort_ptrs(A,A_last); } template struct DataPtr{ public: T* elem; inline bool operator < ( DataPtr const &other) const { return ((*elem)<(*(other.elem))); } }; template void omp_par::merge_sort_ptrs(T A,T A_last){ //std::cout<<"Using Pointer sort.\n"; int p=omp_get_max_threads(); typedef typename std::iterator_traits::difference_type _DiffType; typedef typename std::iterator_traits::value_type _ValType; _DiffType N=A_last-A; // Make copy and init pointer array to be sorted. DataPtr<_ValType>* B=new DataPtr<_ValType>[N]; _ValType* C=new _ValType[N]; #pragma omp parallel for for(int i=0;i >()); // Copy data to its sorted position. #pragma omp parallel for for(int i=0;i T omp_par::reduce(T* A, I cnt){ T sum=0; #pragma omp parallel for reduction(+:sum) for(I i = 0; i < cnt; i++) sum+=A[i]; return sum; } template void omp_par::scan(T* A, T* B,I cnt){ int p=omp_get_max_threads(); if(cnt<100*p){ for(I i=1;i void merge(T A_,T A_last,T B_,T B_last,T C_,int p,StrictWeakOrdering comp); template void merge_sort(T A,T A_last,StrictWeakOrdering comp); template void merge_sort(T A,T A_last); template void merge_sort_ptrs(T A,T A_last); template T reduce(T* A, I cnt); template void scan(T* A, T* B,I cnt); } #include "ompUtils.tcc" #endif CombBLAS_beta_16_2/usort/include/usort/binUtils.h000755 000765 000024 00000003105 13271404146 023345 0ustar00aydinbulucstaff000000 000000 /** @file binUtils.h @brief A set of efficient functions that use binary operations to perform some small computations. @author Hari Sundar, hsundar@gmail.com */ #ifndef __BIN_UTILS_H_ #define __BIN_UTILS_H_ #include /** @namespace binOp @brief A set of functions for fast binary operations. @author Hari Sundar */ namespace binOp{ /** @return true if n is a power of 2. */ bool isPowerOfTwo(unsigned int n); /** @return the minimum number of digits required to represent num in binary */ unsigned int binLength(unsigned int num) ; /** return log to base 2 of num */ unsigned int fastLog2(unsigned int num) ; /** @brief Converts a decimal number to binary @param dec the decimal number @param binLen the number of digits required in the binary representation @param result the binary representation @return error flag */ int toBin(unsigned int dec, unsigned int binLen, std::vector& result); /** @param numBin binary representation of the number @param binLen length of numBin @return the decimal representation of the binary number */ unsigned int binToDec(unsigned int* numBin, unsigned int binLen) ; /** @return compute the next highest power of 2 of 32-bit v */ int getNextHighestPowerOfTwo(unsigned int n); /** @return compute the prev highest power of 2 of 32-bit v */ int getPrevHighestPowerOfTwo(unsigned int n); /** * psuedo random generator ... kind of ... */ unsigned int reversibleHash(unsigned int x); }//end namespace #endif CombBLAS_beta_16_2/usort/include/usort/parUtils.h000755 000765 000024 00000031314 13271404146 023362 0ustar00aydinbulucstaff000000 000000 /** @file parUtils.h @brief A set of parallel utilities. @author Hari Sundar, hsundar@gmail.com */ #ifndef __PAR_UTILS_H_ #define __PAR_UTILS_H_ #define KEEP_HIGH 100 #define KEEP_LOW 101 #ifdef __DEBUG__ #ifndef __DEBUG_PAR__ #define __DEBUG_PAR__ #endif #endif #include "mpi.h" #include #ifdef __USE_64_BIT_INT__ #define DendroIntL long long #define DendroIntLSpecifier %lld #define DendroUIntLSpecifier %llu #else #define DendroIntL int #define DendroIntLSpecifier %d #define DendroUIntLSpecifier %u #endif /** @namespace par @author Hari Sundar hsundar@gmail.com @brief Collection of Generic Parallel Functions: Sorting, Partitioning, Searching,... */ namespace par { template int Mpi_Isend(T* buf, int count, int dest, int tag, MPI_Comm comm, MPI_Request* request); template int Mpi_Issend(T* buf, int count, int dest, int tag, MPI_Comm comm, MPI_Request* request); template int Mpi_Recv(T* buf, int count, int source, int tag, MPI_Comm comm, MPI_Status* status); template int Mpi_Irecv(T* buf, int count, int source, int tag, MPI_Comm comm, MPI_Request* request); template int Mpi_Gather( T* sendBuffer, T* recvBuffer, int count, int root, MPI_Comm comm); template int Mpi_Sendrecv( T* sendBuf, int sendCount, int dest, int sendTag, S* recvBuf, int recvCount, int source, int recvTag, MPI_Comm comm, MPI_Status* status); template int Mpi_Bcast( T* buffer, int count, int root, MPI_Comm comm); template int Mpi_Scan( T* sendbuf, T* recvbuf, int count, MPI_Op op, MPI_Comm comm); template int Mpi_Reduce( T* sendbuf, T* recvbuf, int count, MPI_Op op, int root, MPI_Comm comm); template int Mpi_Allreduce( T* sendbuf, T* recvbuf, int count, MPI_Op op, MPI_Comm comm); template int Mpi_Alltoall(T* sendbuf, T* recvbuf, int count, MPI_Comm comm); template int Mpi_Allgatherv(T* sendbuf, int sendcount, T* recvbuf, int* recvcounts, int* displs, MPI_Comm comm); template int Mpi_Allgather(T* sendbuf, T* recvbuf, int count, MPI_Comm comm); template int Mpi_Alltoallv_sparse(T* sendbuf, int* sendcnts, int* sdispls, T* recvbuf, int* recvcnts, int* rdispls, MPI_Comm comm); template int Mpi_Alltoallv_dense(T* sendbuf, int* sendcnts, int* sdispls, T* recvbuf, int* recvcnts, int* rdispls, MPI_Comm comm); template unsigned int defaultWeight(const T *a); /** @brief A parallel weighted partitioning function. In our implementation, we do not pose any restriction on the input or the number of processors. This function can be used with an odd number of processors as well. Some processors can pass an empty vector as input. The relative ordering of the elements is preserved. @author Hari Sundar @author Rahul Sampath @param vec the input vector @param getWeight function pointer to compute the weight of each element. If you pass NULL, then every element will get a weight equal to 1. @param comm the communicator */ template int partitionW(std::vector& vec, unsigned int (*getWeight)(const T *), MPI_Comm comm); template void rankSamples(std::vector& arr, std::vector samples, MPI_Comm comm); template std::vector Sorted_Sample_Select(std::vector& arr, unsigned int kway, std::vector& min_idx, std::vector& max_idx, std::vector& splitter_ranks, MPI_Comm comm); template std::vector Sorted_approx_Select(std::vector& arr, unsigned int k, MPI_Comm comm); //! new one to handle skewed distributions ... template std::vector > Sorted_approx_Select_skewed(std::vector& arr, unsigned int k, MPI_Comm comm); template std::vector GetRangeMean(std::vector& arr, std::vector range_min, std::vector range_max, MPI_Comm comm); template std::vector GuessRangeMedian(std::vector& arr, std::vector range_min, std::vector range_max, MPI_Comm comm); /** @brief A parallel k-selection algorithms @author Hari Sundar @date 2013-01-10 @param arr arr from which samples are to be selected @param k number of samples @param isSorted if false, arr is locally sorted first. @return list of k keys. **/ template std::vector Sorted_k_Select(std::vector& arr, std::vector min_idx, std::vector max_idx, std::vector K, std::vector guess, MPI_Comm comm); /** @brief A parallel hyper quick sort implementation. @author Dhairya Malhotra @param in the input vector @param out the output vector @param comm the communicator */ template int HyperQuickSort(std::vector& in, std::vector & out, MPI_Comm comm); template int HyperQuickSort_kway(std::vector& in, std::vector & out, MPI_Comm comm); template int HyperQuickSort_cav(std::vector& in, std::vector & out, MPI_Comm comm); /* mem-efficient version */ template int HyperQuickSort(std::vector& arr, MPI_Comm comm); template int HyperQuickSort_kway(std::vector& in, MPI_Comm comm); template int HyperQuickSort_cav(std::vector& in, MPI_Comm comm); /** @brief A parallel sample sort implementation. In our implementation, we do not pose any restriction on the input or the number of processors. This function can be used with an odd number of processors as well. Some processors can pass an empty vector as input. If the total number of elements in the vector (globally) is fewer than 10*p^2, where p is the number of processors, then we will use bitonic sort instead of sample sort to sort the vector. We use a paralle bitonic sort to sort the samples in the sample sort algorithm. Hence, the complexity of the algorithm is O(n/p log n/p) + O(p log p). Here, n is the global length of the vector and p is the number of processors. @author Hari Sundar @param in the input vector @param out the output vector @param comm the communicator */ template int sampleSort(std::vector& in, std::vector & out, MPI_Comm comm); template int sampleSort(std::vector& in, MPI_Comm comm); /** @brief Splits a communication group into two, one containing processors that passed a value of 'false' for the parameter 'iAmEmpty' and the another containing processors that passed a value of 'true' for the parameter. Both the groups are sorted in the ascending order of their ranks in the old comm. @author Rahul Sampath @param iAmEmpty Some flag to determine which group the calling processor will be combined into. @param orig_comm The comm group that needs to be split. @param new_comm The new comm group. */ int splitComm2way(bool iAmEmpty, MPI_Comm* new_comm, MPI_Comm orig_comm); /** @brief Splits a communication group into two depending on the values in isEmptyList. Both the groups are sorted in the ascending order of their ranks in the old comm. All processors must call this function with the same 'isEmptyList' array. @author Rahul Sampath @param isEmptyList flags (of length equal to the number of processors) to determine whether each processor is active or not. @param orig_comm The comm group that needs to be split. @param new_comm The new comm group. */ int splitComm2way(const bool* isEmptyList, MPI_Comm* new_comm, MPI_Comm orig_comm); /* @author Rahul Sampath @brief Splits a communication group into two, processors with ranks less than splittingRank form one group and the other processors form the second group. Both the groups are sorted in the ascending order of their ranks in the old comm. @param splittingRank The rank used for splitting the communicator @param orig_comm The comm group that needs to be split. @param new_comm The new comm group. */ int splitCommUsingSplittingRank(int splittingRank, MPI_Comm* new_comm, MPI_Comm orig_comm); /** * @brief Splits a communication group into two, the first having a power of 2 * number of processors and the other having the remainder. The first group * is sorted in the ascending order of their ranks in the old comm and the second group * is sorted in the descending order of their ranks in the old comm * @author Hari Sundar * @param orig_comm The comm group that needs to be split. * @param new_comm The new comm group. */ unsigned int splitCommBinary( MPI_Comm orig_comm, MPI_Comm* new_comm); /** * @brief Splits a communication group into two, the first having a power of 2 * number of processors and the other having the remainder. Both the groups * are sorted in the ascending order of their ranks in the old comm. * @author Hari Sundar * @param orig_comm The comm group that needs to be split. * @param new_comm The new comm group. */ unsigned int splitCommBinaryNoFlip( MPI_Comm orig_comm, MPI_Comm* new_comm); /** @author Hari Sundar * @brief Merges lists A, and B, retaining either the low or the High in list A. * * @param listA Input list, and where the output is stored. * @param listB Second input list. * @param KEEP_WHAT determines whether to retain the High or the low values * from A and B. One of KEEP_HIGH or KEEP_LOW. * * Merging the two lists when their sizes are not the same is a bit involved. * The major condition that needs to be used is that all elements that are less * than max(min(A), min(B)) are retained by the KEEP_LOW processor, and * similarly all elements that are larger larger than min(max(A), max(B)) are * retained by the KEEP_HIGH processor. * * The reason for this is that, on the Keep_Low side, * * max(min(A), min(B)) > min(A) > max(A-) * * and similarly on the Keep_high side, * * min(max(A), max(B)) < max(A) < min(A+) * * which guarantees that the merged lists remain bitonic. */ template void MergeLists( std::vector &listA, std::vector &listB, int KEEP_WHAT) ; /** @author Hari Sundar @brief The main operation in the parallel bitonic sort algorithm. This implements the compare-split operation. * @param which_keys is one of KEEP_HIGH or KEEP_LOW * @param partner is the processor with which to Merge and Split. @param local_list the input vector @param comm the communicator */ template void MergeSplit( std::vector &local_list, int which_keys, int partner, MPI_Comm comm); /** @author Hari Sundar */ template void Par_bitonic_sort_incr( std::vector &local_list, int proc_set_size, MPI_Comm comm ); /** @author Hari Sundar */ template void Par_bitonic_sort_decr( std::vector &local_list, int proc_set_size, MPI_Comm comm); /** @author Hari Sundar */ template void Par_bitonic_merge_incr( std::vector &local_list, int proc_set_size, MPI_Comm comm ); /** @brief An implementation of parallel bitonic sort that expects the number of processors to be a power of 2. However, unlike most implementations, we do not expect the length of the vector (neither locally nor globally) to be a power of 2 or even. Moreover, each processor can call this with a different number of elements. However, we do expect that 'in' atleast has 1 element on each processor. @param in the vector to be sorted @author Hari Sundar */ template void bitonicSort_binary(std::vector & in, MPI_Comm comm) ; /** @brief An implementation of parallel bitonic sort that does not expect the number of processors to be a power of 2. In fact, the number of processors can even be odd. Moreover, we do not even expect the length of the vector (neither locally nor globally) to be a power of 2 or even. Moreover, each processor can call this with a different number of elements. However, we do expect that 'in' atleast has 1 element on each processor. This recursively calls the function bitonicSort_binary, followed by a special parallel merge. @param in the vector to be sorted @author Hari Sundar @see bitonicSort_binary */ template void bitonicSort(std::vector & in, MPI_Comm comm) ; }//end namespace #include "parUtils.tcc" #endif CombBLAS_beta_16_2/usort/include/usort/seqUtils.tcc000755 000765 000024 00000016101 13271404146 023707 0ustar00aydinbulucstaff000000 000000 /** @file seqUtils.txx @brief Definitions of the templated functions in the seq module. @author Rahul S. Sampath, rahul.sampath@gmail.com */ #include #include #include #include namespace seq { template bool BinarySearch(const T* arr, unsigned int nelem, const T & key, unsigned int *ret_idx) { if(!nelem) {*ret_idx = nelem; return false;} unsigned int left = 0; unsigned int right = (nelem -1); while (left <= right) { unsigned int mid = (unsigned int)( left + (unsigned int)(floor((double)(right-left)/2.0)) ); if (key > arr[mid]) { left = mid+1; } else if (key < arr[mid]) { if(mid>0) { right = mid-1; } else { right = 0; break;} } else { *ret_idx = mid; return true; }//end if-else-if }//end while *ret_idx = nelem; return false; }//end function template int UpperBound (unsigned int p,const T * splitt,unsigned int itr, const T & elem) { if (itr >= p) { return p; } while (itr < p){ if (elem <= splitt[itr]) { return itr; } else { itr = itr + 1; } }//end while return itr; }//end function template bool maxLowerBound(const std::vector& arr, const T & key, unsigned int &ret_idx, unsigned int* leftIdx, unsigned int* rightIdx ) { unsigned int nelem = static_cast(arr.size()); ret_idx = 0; if(!nelem) { return false;} if(arr[0] > key) { return false; } if(arr[nelem-1] < key) { ret_idx = (nelem-1); return true; }//end if //binary search unsigned int left = 0; unsigned int right = (nelem -1); unsigned int mid = 0; if(leftIdx) { left = (*leftIdx); } if(rightIdx) { right = (*rightIdx); } while (left <= right) { mid = (unsigned int)( left + (unsigned int)(floor((double)(right-left)/2.0)) ); if (key > arr[mid]) { left = mid + (1u); } else if (key < arr[mid]){ if(mid>0) { right = mid-1; }else { right=0; break; } } else { ret_idx = mid; return true; }//end if-else-if }//end while //If binary search did not find an exact match, it would have //stopped one element after or one element before. if( (arr[mid] > key) && (mid > 0) ){ mid--; } if(arr[mid] <= key ) { ret_idx = mid; return true; } else { ret_idx = 0; return false;} }//end function template void makeVectorUnique(std::vector& vecT, bool isSorted) { if(vecT.size() < 2) { return;} if(!isSorted) { sort(vecT.begin(),vecT.end()); } std::vector tmp(vecT.size()); //Using the array [] is faster than the vector version T* tmpPtr = (&(*(tmp.begin()))); T* vecTptr = (&(*(vecT.begin()))); tmpPtr[0] = vecTptr[0]; unsigned int tmpSize=1; unsigned int vecTsz = static_cast(vecT.size()); for(unsigned int i = 1; i < vecTsz; i++) { if(tmpPtr[tmpSize-1] != vecTptr[i]) { tmpPtr[tmpSize] = vecTptr[i]; tmpSize++; } }//end for vecT.clear(); vecT.insert(vecT.begin(), tmp.begin(), (tmp.begin() + tmpSize)); tmp.clear(); }//end function template void flashsort(T* a, int n, int m, int *ctr) { const int THRESHOLD1 = 75; const int CLASS_SIZE = 75; /* minimum value for m */ /* declare variables */ int *l, nmin, nmax, i, j, k, nmove, nx, mx; T c1,c2,flash,hold; /* allocate space for the l vector */ l=(int*)calloc(m,sizeof(int)); /***** CLASS FORMATION ****/ nmin=nmax=0; for (i=0 ; i a[nmax]) nmax = i; } if ( (a[nmax]==a[nmin]) && (ctr==0) ) { printf("All the numbers are identical, the list is sorted\n"); return; } c1=(m-1.0)/(a[nmax]-a[nmin]) ; c2=a[nmin]; l[0]=-1; /* since the base of the "a" (data) array is 0 */ for (k=1; k l[k] ) { j++; k=floor(c1*(a[j]-c2) ) ; } flash=a[ j ] ; while ( j <= l[k] ) { k=floor(c1*(flash-c2)); hold=a[ l[k] ]; a[ l[k] ] = flash; l[k]--; flash=hold; nmove++; } } /**** Choice of RECURSION or STRAIGHT INSERTION *****/ for (k=0;k<(m-1);k++) if ( (nx = l[k+1]-l[k]) > THRESHOLD1 ) /* then use recursion */ { flashsort(&a[l[k]+1],nx,CLASS_SIZE,ctr); (*ctr)++; } else /* use insertion sort */ for (i=l[k+1]-1; i > l[k] ; i--) if (a[i] > a[i+1]) { hold=a[i]; j=i; while (hold > a[j+1] ) a[j++]=a[j+1] ; a[j]=hold; } free(l); /* need to free the memory we grabbed for the l vector */ } }//end namespace /* //Same as std::upper_limit template typename std::iterator_traits::difference_type seq::BinSearch(T A,T A_last, const typename std::iterator_traits::value_type& val,StrictWeakOrdering comp){ typedef typename std::iterator_traits::difference_type _DiffType; _DiffType a=0; _DiffType b=A_last-A-1; _DiffType c; if(!comp(val,*(A+b))) return b+1; if(comp(val,*(A+a))) return a; while(b-a>1){ c=(a+b)/2; if(comp(val,*(A+c))) b=c; else a=c; } return b; } //Same as std::merge template void seq::Merge(T A_,T A_last,T B_,T B_last,T C_,StrictWeakOrdering comp){ typedef typename std::iterator_traits::difference_type _DiffType; _DiffType N1=A_last-A_; _DiffType N2=B_last-B_; T A=A_; T B=B_; T C=C_; if(N1+N2==0)return; for(_DiffType i=0;i #include #include #include #include #include #include #include "xalloc.h" #include "prng.h" #include "generator/splittable_mrg.h" #if defined(_OPENMP) || defined(__MTA__) static int64_t take_i64 (volatile int64_t* p); static void release_i64 (volatile int64_t* p, int64_t val); #endif /* Recursively divide a grid of N x N by four to a single point, (i, j). Choose between the four quadrants with probability a, b, c, and d. Create an edge between node i and j. */ MTA("mta expect parallel context") static void rmat_edge (int64_t *iout, int64_t *jout, int SCALE, double A, double B, double C, double D, const double *rn) { size_t rni = 0; int64_t i = 0, j = 0; int64_t bit = ((int64_t)1) << (SCALE-1); while (1) { const double r = rn[rni++]; if (r > A) { /* outside quadrant 1 */ if (r <= A + B) /* in quadrant 2 */ j |= bit; else if (r <= A + B + C) /* in quadrant 3 */ i |= bit; else { /* in quadrant 4 */ j |= bit; i |= bit; } } if (1 == bit) break; /* Assuming R is in (0, 1), 0.95 + 0.1 * R is in (0.95, 1.05). So the new probabilities are *not* the old +/- 10% but instead the old +/- 5%. */ A *= 0.95 + rn[rni++]/10; B *= 0.95 + rn[rni++]/10; C *= 0.95 + rn[rni++]/10; D *= 0.95 + rn[rni++]/10; /* Used 5 random numbers. */ { const double norm = 1.0 / (A + B + C + D); A *= norm; B *= norm; C *= norm; } /* So long as +/- are monotonic, ensure a+b+c+d <= 1.0 */ D = 1.0 - (A + B + C); bit >>= 1; } /* Iterates SCALE times. */ *iout = i; *jout = j; } #define I(k) (IJ[2*(k)]) #define J(k) (IJ[2*(k)+1]) #if defined(_OPENMP)||defined(__MTA__) static void randpermute (int64_t *A_in, int64_t nelem, int nper, mrg_state * restrict st) { int64_t * restrict A = A_in; int64_t k; assert (nper <= 2); OMP("omp for") MTA("mta assert nodep") for (k = 0; k < nelem; ++k) { int k2; int64_t place; double Rk, Rplace; int64_t Ak, Aplace; Ak = take_i64 (&A[k*nper]); assert (Ak >= 0); mrg_skip (st, 1, k, 0); place = k + (int64_t)floor (mrg_get_double_orig (st) * (nelem - k)); if (k != place) { assert (place > k); assert (place < nelem); Aplace = take_i64 (&A[place*nper]); assert (Aplace >= 0); for (k2 = 1; k2 < nper; ++k2) { int64_t t; t = A[place*nper + k2]; A[place*nper + k2] = A[k*nper + k2]; A[k*nper + k2] = t; } { int64_t t; t = Aplace; Aplace = Ak; Ak = t; } release_i64 (&A[place*nper], Aplace); } release_i64 (&A[k*nper], Ak); } } #else static void randpermute (int64_t *A_in, int64_t nelem, int nper, mrg_state * restrict st) { int64_t * restrict A = A_in; int64_t k; assert (nper <= 2); for (k = 0; k < nelem; ++k) { int k2; int64_t place; place = k + (int64_t)floor (mrg_get_double_orig (st) * (nelem - k)); if (k != place) for (k2 = 0; k2 < nper; ++k2) { int64_t t; t = A[place*nper + k2]; A[place*nper + k2] = A[k*nper + k2]; A[k*nper + k2] = t; } } } #endif void permute_vertex_labels (int64_t * restrict IJ, int64_t nedge, int64_t max_nvtx, mrg_state * restrict st, int64_t * restrict newlabel) { int64_t k; OMP("omp for") for (k = 0; k < max_nvtx; ++k) newlabel[k] = k; randpermute (newlabel, max_nvtx, 1, st); OMP("omp for") for (k = 0; k < 2*nedge; ++k) IJ[k] = newlabel[IJ[k]]; } void permute_edgelist (int64_t * restrict IJ, int64_t nedge, mrg_state *st) { randpermute (IJ, nedge, 2, st); } #define NRAND(ne) (5 * SCALE * (ne)) void rmat_edgelist (int64_t *IJ_in, int64_t nedge, int SCALE, double A, double B, double C) { int64_t * restrict IJ = IJ_in, * restrict iwork; double D = 1.0 - (A + B + C); iwork = xmalloc_large_ext ((1L<= 0 && __sync_bool_compare_and_swap (p, oldval, -1))); return oldval; } void release_i64 (volatile int64_t *p, int64_t val) { assert (*p == -1); *p = val; } #else /* XXX: These suffice for the above uses. */ int64_t take_i64 (volatile int64_t *p) { int64_t out; do { OMP("omp critical (TAKE)") { out = *p; if (out >= 0) *p = -1; } } while (out < 0); OMP("omp flush (p)"); return out; } void release_i64 (volatile int64_t *p, int64_t val) { assert (*p == -1); OMP("omp critical (TAKE)") { *p = val; OMP("omp flush (p)"); } return; } #endif #elif defined(__MTA__) int64_t take_i64 (volatile int64_t *p) { return readfe (p); } void release_i64 (volatile int64_t *p, int64_t val) { writeef (p, val); } #else /* double_cas isn't used sequentially. */ #endif CombBLAS_beta_16_2/graph500-1.2/timer.c000644 000765 000024 00000002653 13212627400 020537 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #include "compat.h" #include #include #include #include #include #if defined(__MTA__) #include long tic_ts; #elif defined(__MacOSX__) static AbsoluteTime tic_ts; #else #if defined(CLOCK_MONOTONIC) #define TICTOC_CLOCK CLOCK_MONOTONIC #define TICTOC_CLOCK_NAME "CLOCK_MONOTONIC" #elif defined(CLOCK_REALTIME) #define TICTOC_CLOCK CLOCK_REALTIME #define TICTOC_CLOCK_NAME "CLOCK_REALTIME" #else #error "Failed to find a timing clock." #endif static struct timespec tic_ts; #endif void tic (void) { #if defined(__MTA__) MTA("mta fence") tic_ts = mta_get_clock (0); #elif defined(__MacOSX__) tic_ts = UpTime (); #else clock_gettime (TICTOC_CLOCK, &tic_ts); #endif } double toc (void) { double out; #if defined(__MTA__) long ts; MTA("mta fence") ts = mta_get_clock (tic_ts); out = ((double)ts) * mta_clock_period (); /*fprintf (stderr, "%ld %g %g %g\n", ts, out, mta_clock_period(), mta_clock_freq());*/ #elif defined(__MacOSX__) AbsoluteTime ts; ts = UpTime (); out = 1.0e-9 * AbsoluteDeltaToNanoseconds (ts, tic_ts); #else struct timespec ts; clock_gettime (TICTOC_CLOCK, &ts); out = (ts.tv_nsec - (double)tic_ts.tv_nsec) * 1.0e-9; out += (ts.tv_sec - (double)tic_ts.tv_sec); #endif return out; } CombBLAS_beta_16_2/graph500-1.2/._.DS_Store000644 000765 000024 00000000170 13271513366 021155 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/graph500-1.2/.DS_Store000644 000765 000024 00000024004 13271513366 020742 0ustar00aydinbulucstaff000000 000000 Bud1   ratorbw  @€ @€ @€ @ generatorbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™š generatorlsvCblob¯bplist00Ú GHI KXiconSize_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 «!%*/49=BÔ  WvisibleUwidthYascendingZidentifier , TnameÔ#XubiquityÔ \dateModifiedµ Ô"[dateCreatedÔ&' Tsizea Ô+, Tkinds Ô01 Ulabeld Ô56 WversionK Ô: Xcomments Ô>?^dateLastOpenedÈÔEYdateAdded#@X#@(# &8@TfoŒŸ´½¾ÊÓÛáëö÷úû    ,./09EFGPUWXYbgijktz|}~‡‘’“œ¥¦§°¿ÁÂÃÌÍÎØÙâëôõL÷ generatorlsvpblob”bplist00Ú GHI )XiconSize_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDates_viewOptionsVersion#@0 Ù %*.38=BXcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameÔ WvisibleUwidthYascendingUindex, Ô"$ÈÔ ') µÔ'-Ô 02 aÔ5 7d Ô : < s Ô? AK ÔC  #@X#@(# &8@TfoŒŸ´½¾ÑÚéö (06@FGJKMVWYZ\efhiktuvx‚„…‡‘“”–Ÿ ¢£¥®¯±²´½¿ÀÁÂËÔÝKÞ generatorvSrnlongmpibwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šmpilsvCblob’bplist00Ø HIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates « "&+05:>CÔ   WvisibleUwidthYascendingZidentifier , TnameÔWvisibleUwidthYascending#XubiquityÔ  ! µ\dateModifiedÔ %[dateCreatedÔ '( Tsizea Ô ,- Tkinds Ô 12 Ulabeld Ô 67 WversionK Ô ; Xcomments Ô @BÈ^dateLastOpenedÔDYdateAdded#@(Tname#@0 .@H\epyŒŽ›¤¬²¼ÇÈËÌÑÚâèòóõöÿ   "#$09>@ABKPRST]cefgpxz{|…Ž™šœ¬µ¿ÀÁÂËÐÙLÚmpilsvpblobYbplist00Ø DEF _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates Ù #(-27<@Xcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ WvisibleUwidthYascendingUindex, Ô ÈÔ$%µÔ *, aÔ/ 1d Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©mpivSrnlong E DSDB `€(0@€ @€ @ionTnameÔ WvisibleUwidthYascendingUindex, Ô"$ÈÔ ') µÔ'-Ô 02 aÔ5 7d Ô : < s Ô? AK ÔC  #@X#@(# &8@TfoŒŸ´½¾ÑÚéö (06@FGJKMVWYZ\efhiktuvx‚„…‡‘“”–Ÿ ¢£¥®¯±²´½¿ÀÁÂËÔÝKÞ generatorvSrnlongmpibwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šmpilsvCblob’bplist00Ø HIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumCombBLAS_beta_16_2/graph500-1.2/xalloc.c000644 000765 000024 00000011760 13212627400 020700 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #include "compat.h" #include #include #include #include #include #include #include #include #include #include #include #if !defined(MAP_HUGETLB) #define MAP_HUGETLB 0 #endif #if !defined(MAP_POPULATE) #define MAP_POPULATE 0 #endif #if !defined(MAP_NOSYNC) #define MAP_NOSYNC 0 #endif #if 0 /* Included in the generator. */ void * xmalloc (size_t sz) { void *out; if (!(out = malloc (sz))) { perror ("malloc failed"); abort (); } return out; } #endif #if defined(__MTA__)||defined(USE_MMAP_LARGE)||defined(USE_MMAP_LARGE_EXT) #define MAX_LARGE 32 static int n_large_alloc = 0; static struct { void * p; size_t sz; int fd; } large_alloc[MAX_LARGE]; static int installed_handler = 0; static void (*old_abort_handler)(int); static void exit_handler (void) { int k; for (k = 0; k < n_large_alloc; ++k) { if (large_alloc[k].p) munmap (large_alloc[k].p, large_alloc[k].sz); if (large_alloc[k].fd >= 0) close (large_alloc[k].fd); large_alloc[k].p = NULL; large_alloc[k].fd = -1; } } static void abort_handler (int passthrough) { exit_handler (); if (old_abort_handler) old_abort_handler (passthrough); } #endif #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) #define MAP_ANONYMOUS MAP_ANON #endif void * xmalloc_large (size_t sz) { #if defined(__MTA__)||defined(USE_MMAP_LARGE) void *out; int which = n_large_alloc++; if (n_large_alloc > MAX_LARGE) { fprintf (stderr, "Too many large allocations. %d %d\n", n_large_alloc, MAX_LARGE); --n_large_alloc; abort (); } large_alloc[which].p = NULL; large_alloc[which].fd = -1; out = mmap (NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|MAP_POPULATE, 0, 0); if (out == MAP_FAILED || !out) { perror ("mmap failed"); abort (); } large_alloc[which].p = out; large_alloc[which].sz = sz; return out; #else return xmalloc (sz); #endif } void xfree_large (void *p) { #if defined(__MTA__)||defined(USE_MMAP_LARGE)||defined(USE_MMAP_LARGE_EXT) int k, found = 0; for (k = 0; k < n_large_alloc; ++k) { if (p == large_alloc[k].p) { munmap (p, large_alloc[k].sz); large_alloc[k].p = NULL; if (large_alloc[k].fd >= 0) { close (large_alloc[k].fd); large_alloc[k].fd = -1; } found = 1; break; } } if (found) { --n_large_alloc; for (; k < n_large_alloc; ++k) large_alloc[k] = large_alloc[k+1]; } else free (p); #else free (p); #endif } void * xmalloc_large_ext (size_t sz) { #if !defined(__MTA__)&&defined(USE_MMAP_LARGE_EXT) char extname[PATH_MAX+1]; char *tmppath; void *out; int fd, which; if (getenv ("TMPDIR")) tmppath = getenv ("TMPDIR"); else if (getenv ("TEMPDIR")) tmppath = getenv ("TEMPDIR"); else tmppath = "/tmp"; sprintf (extname, "%s/graph500-ext-XXXXXX", tmppath); which = n_large_alloc++; if (n_large_alloc > MAX_LARGE) { fprintf (stderr, "Out of large allocations.\n"); abort (); } large_alloc[which].p = 0; large_alloc[which].fd = -1; fd = mkstemp (extname); if (fd < 0) { perror ("xmalloc_large_ext failed to make a file"); abort (); } if (unlink (extname)) { perror ("UNLINK FAILED!"); goto errout; } #if _XOPEN_SOURCE >= 500 if (pwrite (fd, &fd, sizeof (fd), sz - sizeof(fd)) != sizeof (fd)) { perror ("resizing pwrite failed"); goto errout; } #else if (lseek (fd, sz - sizeof(fd), SEEK_SET) < 0) { perror ("lseek failed"); goto errout; } if (write (fd, &fd, sizeof(fd)) != sizeof (fd)) { perror ("resizing write failed"); goto errout; } #endif fcntl (fd, F_SETFD, O_ASYNC); out = mmap (NULL, sz, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE|MAP_NOSYNC, fd, 0); if (MAP_FAILED == out || !out) { perror ("mmap ext failed"); goto errout; } if (!installed_handler) { installed_handler = 1; if (atexit (exit_handler)) { perror ("failed to install exit handler"); goto errout; } old_abort_handler = signal (SIGABRT, abort_handler); if (SIG_ERR == old_abort_handler) { perror ("failed to install cleanup handler"); goto errout; } } large_alloc[which].p = out; large_alloc[which].sz = sz; large_alloc[which].fd = fd; return out; errout: if (fd >= 0) close (fd); abort (); #else return xmalloc_large (sz); #endif } /* void mark_large_unused (void *p) { #if !defined(__MTA__) int k; for (k = 0; k < n_large_alloc; ++k) if (p == large_alloc[k].p) posix_madvise (large_alloc[k].p, large_alloc[k].sz, POSIX_MADV_DONTNEED); #endif } void mark_large_willuse (void *p) { #if !defined(__MTA__) int k; for (k = 0; k < n_large_alloc; ++k) if (p == large_alloc[k].p) posix_madvise (large_alloc[k].p, large_alloc[k].sz, POSIX_MADV_WILLNEED); #endif } */ CombBLAS_beta_16_2/graph500-1.2/mpi/000755 000765 000024 00000000000 13212627400 020032 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/xmt-csr-local/000755 000765 000024 00000000000 13212627400 021732 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/xmt-csr/000755 000765 000024 00000000000 13212627400 020642 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/Makefile000644 000765 000024 00000004370 13212627400 020711 0ustar00aydinbulucstaff000000 000000 # -*- Makefile -*- # Copyright 2010, Georgia Institute of Technology, USA. # See COPYING for license. BUILD_OPENMP = No BUILD_XMT = No include make.inc GRAPH500_SOURCES=graph500.c options.c rmat.c kronecker.c verify.c prng.c \ xalloc.c timer.c BIN=seq-list/seq-list seq-csr/seq-csr ifeq ($(BUILD_OPENMP), Yes) BIN += omp-csr/omp-csr endif ifeq ($(BUILD_MPI), Yes) BIN += mpi/graph500_mpi_simple endif ifeq ($(BUILD_XMT), Yes) BIN = xmt-csr/xmt-csr xmt-csr-local/xmt-csr-local endif GENERATOR_OBJS_SEQ=btrd_binomial_distribution.o splittable_mrg.o \ mrg_transitions.o graph_generator.o permutation_gen.o \ make_graph.o scramble_edges.o utils.o .PHONY: all all: $(BIN) seq-list/seq-list: seq-list/seq-list.c $(GRAPH500_SOURCES) libgenerator-seq.a seq-csr/seq-csr: seq-csr/seq-csr.c $(GRAPH500_SOURCES) libgenerator-seq.a omp-csr/omp-csr: CFLAGS:=$(CFLAGS) $(CFLAGS_OPENMP) omp-csr/omp-csr: omp-csr/omp-csr.c $(GRAPH500_SOURCES) libgenerator-omp.a xmt-csr/xmt-csr: CFLAGS:=$(CFLAGS) -pl xmt-csr/xmt-csr.pl xmt-csr/xmt-csr: xmt-csr/xmt-csr.c $(GRAPH500_SOURCES) \ $(addprefix generator/,$(patsubst %.o,%.c,$(GENERATOR_OBJS_SEQ))) xmt-csr-local/xmt-csr-local: CFLAGS:=$(CFLAGS) -pl xmt-csr-local/xmt-csr-local.pl xmt-csr-local/xmt-csr-local: xmt-csr-local/xmt-csr-local.c $(GRAPH500_SOURCES) \ $(addprefix generator/,$(patsubst %.o,%.c,$(GENERATOR_OBJS_SEQ))) generator/generator_test_seq: generator/generator_test_seq.c libgenerator-seq.a generator/generator_test_omp: generator/generator_test_omp.c libgenerator-omp.a libgenerator-seq.a: libgenerator-seq.a($(addprefix generator/,$(GENERATOR_OBJS_SEQ))) ranlib libgenerator-seq.a libgenerator-seq.a($(addprefix generator/,$(GENERATOR_OBJS_SEQ))): CFLAGS:=$(CFLAGS) $(CFLAGS_OPENMP) libgenerator-seq.a($(addprefix generator/,$(GENERATOR_OBJS_SEQ))): CPPFLAGS=-DGRAPH_GENERATOR_SEQ libgenerator-omp.a: libgenerator-omp.a($(addprefix generator/,$(GENERATOR_OBJS_SEQ))) ranlib libgenerator-omp.a libgenerator-omp.a($(addprefix generator/,$(GENERATOR_OBJS_SEQ))): CPPFLAGS=-DGRAPH_GENERATOR_OMP mpi/graph500_mpi_simple mpi/graph500_mpi_one_sided: $(MAKE) -C mpi .PHONY: clean clean: rm -f libgenerator-omp.a libgenerator-seq.a \ generator/generator_test_seq generator/generator_test_omp \ $(BIN) -$(MAKE) -C mpi clean CombBLAS_beta_16_2/graph500-1.2/graph500.c000644 000765 000024 00000020324 13212627400 020740 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #include "compat.h" #include #include #include #include #include #include #include /* Portable enough... */ #if !defined(__MTA__) #include #endif #include "graph500.h" #include "rmat.h" #include "kronecker.h" #include "verify.h" #include "prng.h" #include "timer.h" #include "xalloc.h" #include "options.h" #include "generator/splittable_mrg.h" static int64_t nvtx_scale; static int64_t bfs_root[NBFS_max]; static double generation_time; static double construction_time; static double bfs_time[NBFS_max]; static int64_t bfs_nedge[NBFS_max]; static int64_t * restrict IJ; static int64_t nedge; static void run_bfs (void); static void output_results (const int64_t SCALE, int64_t nvtx_scale, int64_t edgefactor, const double A, const double B, const double C, const double D, const double generation_time, const double construction_time, const int NBFS, const double *bfs_time, const int64_t *bfs_nedge); int main (int argc, char **argv) { if (sizeof (int64_t) < 8) { fprintf (stderr, "No 64-bit support.\n"); return EXIT_FAILURE; } if (argc > 1) get_options (argc, argv); nvtx_scale = 1L<= nvtx_scale); assert (nedge >= edgefactor); IJ = xmalloc_large_ext (2 * nedge * sizeof (*IJ)); if (VERBOSE) fprintf (stderr, "Generating edge list..."); if (use_RMAT) TIME(generation_time, rmat_edgelist (IJ, nedge, SCALE, A, B, C)); else TIME(generation_time, kronecker_edgelist (IJ, nedge, SCALE, A, B, C)); if (VERBOSE) fprintf (stderr, " done.\n"); if (getenv ("DUMPGRAPH")) { int k; FILE *g = fopen (getenv ("DUMPGRAPH"), "w"); if (g) { fprintf (g, "%" PRId64 "\n", nedge); for (k = 0; k < 2*nedge; k+=2) { const int64_t i = IJ[k]; const int64_t j = IJ[k+1]; fprintf (g, "%" PRId64 " %" PRId64 "\n", i, j); } fclose (g); } else fprintf (stderr, "Failed to open \"%s\" for dumping.\n", getenv ("DUMPGRAPH")); } run_bfs (); xfree_large (IJ); output_results (SCALE, nvtx_scale, edgefactor, A, B, C, D, generation_time, construction_time, NBFS, bfs_time, bfs_nedge); return EXIT_SUCCESS; } void run_bfs (void) { int * restrict has_adj; int k, m, err; int64_t t; double R[2*NBFS]; if (VERBOSE) fprintf (stderr, "Creating graph..."); TIME(construction_time, err = create_graph_from_edgelist (IJ, nedge)); if (VERBOSE) fprintf (stderr, "done.\n"); if (err) { fprintf (stderr, "Failure creating graph.\n"); exit (EXIT_FAILURE); } has_adj = xmalloc_large (nvtx_scale * sizeof (*has_adj)); OMP("omp parallel") { OMP("omp for") for (k = 0; k < nvtx_scale; ++k) has_adj[k] = 0; MTA("mta assert nodep") OMP("omp for") for (k = 0; k < 2*nedge; k+=2) { const int64_t i = IJ[k]; const int64_t j = IJ[k+1]; if (i != j) has_adj[i] = has_adj[j] = 1; } } /* Sample from {0, ..., nvtx_scale-1} without replacement. */ m = 0; t = 0; while (m < NBFS && t < nvtx_scale) { double R = mrg_get_double_orig (prng_state); if (!has_adj[t] || (nvtx_scale - t)*R > NBFS - m) ++t; else bfs_root[m++] = t++; } if (t >= nvtx_scale && m < NBFS) { if (m > 0) { fprintf (stderr, "Cannot find %d sample roots of non-self degree > 0, using %d.\n", NBFS, m); NBFS = m; } else { fprintf (stderr, "Cannot find any sample roots of non-self degree > 0.\n"); exit (EXIT_FAILURE); } } xfree_large (has_adj); for (k = 0; k < NBFS; ++k) { int64_t *bfs_tree, max_bfsvtx; /* Re-allocate. Some systems may randomize the addres... */ bfs_tree = xmalloc_large (nvtx_scale * sizeof (*bfs_tree)); assert (bfs_root[k] < nvtx_scale); if (VERBOSE) fprintf (stderr, "Running bfs %d...", k); TIME(bfs_time[k], err = make_bfs_tree (bfs_tree, &max_bfsvtx, bfs_root[k])); if (VERBOSE) fprintf (stderr, "done\n"); if (err) { perror ("make_bfs_tree failed"); abort (); } if (VERBOSE) fprintf (stderr, "Verifying bfs %d...", k); bfs_nedge[k] = verify_bfs_tree (bfs_tree, max_bfsvtx, bfs_root[k], IJ, nedge); if (VERBOSE) fprintf (stderr, "done\n"); if (bfs_nedge[k] < 0) { fprintf (stderr, "bfs %d from %" PRId64 " failed verification (%" PRId64 ")\n", k, bfs_root[k], bfs_nedge[k]); abort (); } xfree_large (bfs_tree); } destroy_graph (); } #define NSTAT 9 #define PRINT_STATS(lbl, israte) \ do { \ printf ("min_%s: %20.17e\n", lbl, stats[0]); \ printf ("firstquartile_%s: %20.17e\n", lbl, stats[1]); \ printf ("median_%s: %20.17e\n", lbl, stats[2]); \ printf ("thirdquartile_%s: %20.17e\n", lbl, stats[3]); \ printf ("max_%s: %20.17e\n", lbl, stats[4]); \ if (!israte) { \ printf ("mean_%s: %20.17e\n", lbl, stats[5]); \ printf ("stddev_%s: %20.17e\n", lbl, stats[6]); \ } else { \ printf ("harmonic_mean_%s: %20.17e\n", lbl, stats[7]); \ printf ("harmonic_stddev_%s: %20.17e\n", lbl, stats[8]); \ } \ } while (0) static int dcmp (const void *a, const void *b) { const double da = *(const double*)a; const double db = *(const double*)b; if (da > db) return 1; if (db > da) return -1; if (da == db) return 0; fprintf (stderr, "No NaNs permitted in output.\n"); abort (); return 0; } void statistics (double *out, double *data, int64_t n) { long double s, mean; double t; int k; /* Quartiles */ qsort (data, n, sizeof (*data), dcmp); out[0] = data[0]; t = (n+1) / 4.0; k = (int) t; if (t == k) out[1] = data[k]; else out[1] = 3*(data[k]/4.0) + data[k+1]/4.0; t = (n+1) / 2.0; k = (int) t; if (t == k) out[2] = data[k]; else out[2] = data[k]/2.0 + data[k+1]/2.0; t = 3*((n+1) / 4.0); k = (int) t; if (t == k) out[3] = data[k]; else out[3] = data[k]/4.0 + 3*(data[k+1]/4.0); out[4] = data[n-1]; s = data[n-1]; for (k = n-1; k > 0; --k) s += data[k-1]; mean = s/n; out[5] = mean; s = data[n-1] - mean; s *= s; for (k = n-1; k > 0; --k) { long double tmp = data[k-1] - mean; s += tmp * tmp; } out[6] = sqrt (s/(n-1)); s = (data[0]? 1.0L/data[0] : 0); for (k = 1; k < n; ++k) s += (data[k]? 1.0L/data[k] : 0); out[7] = n/s; mean = s/n; /* Nilan Norris, The Standard Errors of the Geometric and Harmonic Means and Their Application to Index Numbers, 1940. http://www.jstor.org/stable/2235723 */ s = (data[0]? 1.0L/data[0] : 0) - mean; s *= s; for (k = 1; k < n; ++k) { long double tmp = (data[k]? 1.0L/data[k] : 0) - mean; s += tmp * tmp; } s = (sqrt (s)/(n-1)) * out[7] * out[7]; out[8] = s; } void output_results (const int64_t SCALE, int64_t nvtx_scale, int64_t edgefactor, const double A, const double B, const double C, const double D, const double generation_time, const double construction_time, const int NBFS, const double *bfs_time, const int64_t *bfs_nedge) { int k; int64_t sz; double *tm; double *stats; tm = alloca (NBFS * sizeof (*tm)); stats = alloca (NSTAT * sizeof (*stats)); if (!tm || !stats) { perror ("Error allocating within final statistics calculation."); abort (); } sz = (1L << SCALE) * edgefactor * 2 * sizeof (int64_t); printf ("SCALE: %" PRId64 "\nnvtx: %" PRId64 "\nedgefactor: %" PRId64 "\n" "terasize: %20.17e\n", SCALE, nvtx_scale, edgefactor, sz/1.0e12); printf ("A: %20.17e\nB: %20.17e\nC: %20.17e\nD: %20.17e\n", A, B, C, D); printf ("generation_time: %20.17e\n", generation_time); printf ("construction_time: %20.17e\n", construction_time); printf ("nbfs: %d\n", NBFS); memcpy (tm, bfs_time, NBFS*sizeof(tm[0])); statistics (stats, tm, NBFS); PRINT_STATS("time", 0); for (k = 0; k < NBFS; ++k) tm[k] = bfs_nedge[k]; statistics (stats, tm, NBFS); PRINT_STATS("nedge", 0); for (k = 0; k < NBFS; ++k) tm[k] = bfs_nedge[k] / bfs_time[k]; statistics (stats, tm, NBFS); PRINT_STATS("TEPS", 1); } CombBLAS_beta_16_2/graph500-1.2/Graph500.html000644 000765 000024 00000106151 13212627400 021425 0ustar00aydinbulucstaff000000 000000 Graph 500 Benchmark 1 ("Search")

Graph 500 Benchmark 1 ("Search")

Contributors: David A. Bader (Georgia Institute of Technology), Jonathan Berry (Sandia National Laboratories), Simon Kahan (Pacific Northwest National Laboratory and University of Washington), Richard Murphy (Sandia National Laboratories), E. Jason Riedy (Georgia Institute of Technology), and Jeremiah Willcock (Indiana University).

Version History:

V0.1
Draft, created 28 July 2010
V0.2
Draft, created 29 September 2010
V0.3
Draft, created 30 September 2010
V1.0
Created 1 October 2010
V1.1
Created 3 October 2010

Version 0.1 of this document was part of the Graph 500 community benchmark effort, led by Richard Murphy (Sandia National Laboratories). The intent is that there will be at least three variants of implementations, on shared memory and threaded systems, on distributed memory clusters, and on external memory map-reduce clouds. This specification is for the first of potentially several benchmark problems.

References: "Introducing the Graph 500," Richard C. Murphy, Kyle B. Wheeler, Brian W. Barrett, James A. Ang, Cray User’s Group (CUG), May 5, 2010.

"DFS: A Simple to Write Yet Difficult to Execute Benchmark," Richard C. Murphy, Jonathan Berry, William McLendon, Bruce Hendrickson, Douglas Gregor, Andrew Lumsdaine, IEEE International Symposium on Workload Characterizations 2006 (IISWC06), San Jose, CA, 25-27 October 2006.

1 Brief Description of the Graph 500 Benchmark

Data-intensive supercomputer applications are an increasingly important workload, but are ill-suited for platforms designed for 3D physics simulations. Application performance cannot be improved without a meaningful benchmark. Graphs are a core part of most analytics workloads. Backed by a steering committee of 30 international HPC experts from academia, industry, and national laboratories, this specification establishes a large-scale benchmark for these applications. It will offer a forum for the community and provide a rallying point for data-intensive supercomputing problems. This is the first serious approach to augment the Top 500 with data-intensive applications.

The intent of this benchmark problem ("Search") is to develop a compact application that has multiple analysis techniques (multiple kernels) accessing a single data structure representing a weighted, undirected graph. In addition to a kernel to construct the graph from the input tuple list, there is one additional computational kernel to operate on the graph.

This benchmark includes a scalable data generator which produces edge tuples containing the start vertex and end vertex for each edge. The first kernel constructs an undirected graph in a format usable by all subsequent kernels. No subsequent modifications are permitted to benefit specific kernels. The second kernel performs a breadth-first search of the graph. Both kernels are timed.

There are five problem classes defined by their input size:

toy
17GB or around 1010 bytes, which we also call level 10,
mini
140GB (1011 bytes, level 11),
small
1TB (1012 bytes, level 12),
medium
17TB (1013 bytes, level 13),
large
140TB (1014 bytes, level 14), and
huge
1.1PB (1015 bytes, level 15).

Table classes provides the parameters used by the graph generator specified below.

1.1 References

D.A. Bader, J. Feo, J. Gilbert, J. Kepner, D. Koester, E. Loh, K. Madduri, W. Mann, Theresa Meuse, HPCS Scalable Synthetic Compact Applications #2 Graph Analysis (SSCA#2 v2.2 Specification), 5 September 2007.

2 Overall benchmark

The benchmark performs the following steps:

  1. Generate the edge list.
  2. Construct a graph from the edge list (timed, kernel 1).
  3. Randomly sample 64 unique search keys with degree at least one, not counting self-loops.
  4. For each search key:
    1. Compute the parent array (timed, kernel 2).
    2. Validate that the parent array is a correct BFS search tree for the given search tree.
  5. Compute and output performance information.

Only the sections marked as timed are included in the performance information. Note that all uses of "random" permit pseudorandom number generation.

3 Generating the edge list

3.1 Brief Description

The scalable data generator will construct a list of edge tuples containing vertex identifiers. Each edge is undirected with its endpoints given in the tuple as StartVertex and EndVertex.

The intent of the first kernel below is to convert a list with no locality into a more optimized form. The generated list of input tuples must not exhibit any locality that can be exploited by the computational kernels. Thus, the vertex numbers must be randomized and a random ordering of tuples must be presented to kernel 1. The data generator may be parallelized, but the vertex names must be globally consistent and care must be taken to minimize effects of data locality at the processor level.

3.2 Detailed Text Description

The edge tuples will have the form <StartVertex, EndVertex> where StartVertex is one endpoint vertex label and EndVertex is the other endpoint vertex label. The space of labels is the set of integers beginning with zero up to but not including the number of vertices N (defined below). The kernels are not provided the size N explicitly but must discover it.

The input values required to describe the graph are:

SCALE
The logarithm base two of the number of vertices.
edgefactor
The ratio of the graph's edge count to its vertex count (i.e., half the average degree of a vertex in the graph).

These inputs determine the graph's size:

N
the total number of vertices, 2SCALE. An implementation may use any set of N distinct integers to number the vertices, but at least 48 bits must be allocated per vertex number. Other parameters may be assumed to fit within the natural word of the machine. N is derived from the problem’s scaling parameter.
M
the number of edges. M = edgefactor * N.

The graph generator is a Kronecker generator similar to the Recursive MATrix (R-MAT) scale-free graph generation algorithm [Chakrabarti, et al., 2004]. For ease of discussion, the description of this R-MAT generator uses an adjacency matrix data structure; however, implementations may use any alternate approach that outputs the equivalent list of edge tuples. This model recursively sub-divides the adjacency matrix of the graph into four equal-sized partitions and distributes edges within these partitions with unequal probabilities. Initially, the adjacency matrix is empty, and edges are added one at a time. Each edge chooses one of the four partitions with probabilities A, B, C, and D, respectively. These probabilities, the initiator parameters, are provided in Table initiator.

Initiator parameters for the Kronecker graph generator
A = 0.57B = 0.19
C = 0.19D = 1-(A+B+C) = 0.05

The next section details a high-level implementation for this generator. High-performance, parallel implementations are included in the reference implementation.

The graph generator creates a small number of multiple edges between two vertices as well as self-loops. Multiple edges, self-loops, and isolated vertices may be ignored in the subsequent kernels but must be included in the edge list provided to the first kernel. The algorithm also generates the data tuples with high degrees of locality. Thus, as a final step, vertex numbers must be randomly permuted, and then the edge tuples randomly shuffled.

It is permissible to run the data generator in parallel. In this case, it is necessary to ensure that the vertices are named globally, and that the generated data does not possess any locality, either in local memory or globally across processors.

The scalable data generator should be run before starting kernel 1, storing its results to either RAM or disk. If stored to disk, the data may be retrieved before starting kernel 1. The data generator and retrieval operations need not be timed.

3.3 Sample high-level implementation of the Kronecker generator

The GNU Octave routine in Algorithm generator is an attractive implementation in that it is embarrassingly parallel and does not require the explicit formation of the adjacency matrix.

function ij = kronecker_generator (SCALE, edgefactor)
%% Generate an edgelist according to the Graph500
%% parameters.  In this sample, the edge list is
%% returned in an array with two rows, where StartVertex
%% is first row and EndVertex is the second.  The vertex
%% labels start at zero.
%%
%% Example, creating a sparse matrix for viewing:
%%   ij = kronecker_generator (10, 16);
%%   G = sparse (ij(1,:)+1, ij(2,:)+1, ones (1, size (ij, 2)));
%%   spy (G);
%% The spy plot should appear fairly dense. Any locality
%% is removed by the final permutations.

  %% Set number of vertices.
  N = 2^SCALE;

  %% Set number of edges.
  M = edgefactor * N;

  %% Set initiator probabilities.
  [A, B, C] = deal (0.57, 0.19, 0.19);

  %% Create index arrays.
  ij = ones (2, M);
  %% Loop over each order of bit.
  ab = A + B;
  c_norm = C/(1 - (A + B));
  a_norm = A/(A + B);

  for ib = 1:SCALE,
    %% Compare with probabilities and set bits of indices.
    ii_bit = rand (1, M) > ab;
    jj_bit = rand (1, M) > ( c_norm * ii_bit + a_norm * not (ii_bit) );
    ij = ij + 2^(ib-1) * [ii_bit; jj_bit];
  end

  %% Permute vertex labels
  p = randperm (N);
  ij = p(ij);

  %% Permute the edge list
  p = randperm (M);
  ij = ij(:, p);

  %% Adjust to zero-based labels.
  ij = ij - 1;

3.4 Parameter settings

The input parameter settings for each class are given in Table classes.

High-level generator code Problem class definitions and required storage for the edge list assuming 64-bit integers.
Problem classSCALEedge factorApprox. storage size in TB
Toy (level 10)26160.0172
Mini (level 11)29160.1374
Small (level 12)32161.0995
Medium (level 13)361617.5922
Large (level 14)3916140.7375
Huge (level 15)42161125.8999

3.5 References

D. Chakrabarti, Y. Zhan, and C. Faloutsos, R-MAT: A recursive model for graph mining, SIAM Data Mining 2004.

Section 17.6, Algorithms in C (third edition). Part 5 Graph Algorithms, Robert Sedgewick (Programs 17.7 and 17.8)

P. Sanders, Random Permutations on Distributed, External and Hierarchical Memory, Information Processing Letters 67 (1988) pp 305-309.

4 Kernel 1 – Graph Construction

4.1 Description

The first kernel may transform the edge list to any data structures (held in internal or external memory) that are used for the remaining kernels. For instance, kernel 1 may construct a (sparse) graph from a list of tuples; each tuple contains endpoint vertex identifiers for an edge, and a weight that represents data assigned to the edge.

The graph may be represented in any manner, but it may not be modified by or between subsequent kernels. Space may be reserved in the data structure for marking or locking. Only one copy of a kernel will be run at a time; that kernel has exclusive access to any such marking or locking space and is permitted to modify that space (only).

There are various internal memory representations for sparse graphs, including (but not limited to) sparse matrices and (multi-level) linked lists. For the purposes of this application, the kernel is provided only the edge list and the edge list's size. Further information such as the number of vertices must be computed within this kernel. Algorithm kernel1 provides a high-level sample implementation of kernel 1.

The process of constructing the graph data structure (in internal or external memory) from the set of tuples must be timed.

function G = kernel_1 (ij)
%% Compute a sparse adjacency matrix representation
%% of the graph with edges from ij.

  %% Remove self-edges.
  ij(:, ij(1,:) == ij(2,:)) = [];
  %% Adjust away from zero labels.
  ij = ij + 1;
  %% Find the maximum label for sizing.
  N = max (max (ij));
  %% Create the matrix, ensuring it is square.
  G = sparse (ij(1,:), ij(2,:), ones (1, size (ij, 2)), N, N);
  %% Symmetrize to model an undirected graph.
  G = spones (G + G.');

4.2 References

Section 17.6 Algorithms in C third edition Part 5 Graph Algorithms, Robert Sedgewick (Program 17.9)

5 Sampling 64 search keys

The search keys must be randomly sampled from the vertices in the graph. To avoid trivial searches, sample only from vertices that are connected to some other vertex. Their degrees, not counting self-loops, must be at least one. If there are fewer than 64 such vertices, run fewer than 64 searches. This should never occur with the graph sizes in this benchmark, but there is a non-zero probability of producing a trivial or nearly trivial graph. The number of search keys used is included in the output, but this step is untimed.

6 Kernel 2 – Breadth-First Search

6.1 Description

A Breadth-First Search (BFS) of a graph starts with a single source vertex, then, in phases, finds and labels its neighbors, then the neighbors of its neighbors, etc. This is a fundamental method on which many graph algorithms are based. A formal description of BFS can be found in Cormen, Leiserson, and Rivest. Below, we specify the input and output for a BFS benchmark, and we impose some constraints on the computation. However, we do not constrain the choice of BFS algorithm itself, as long as it produces a correct BFS tree as output.

This benchmark's memory access pattern (internal or external) is data-dependent with small average prefetch depth. As in a simple concurrent linked-list traversal benchmark, performance reflects an architecture's throughput when executing concurrent threads, each of low memory concurrency and high memory reference density. Unlike such a benchmark, this one also measures resilience to hot-spotting when many of the memory references are to the same location; efficiency when every thread's execution path depends on the asynchronous side-effects of others; and the ability to dynamically load balance unpredictably sized work units. Measuring synchronization performance is not a primary goal here.

You may not search from multiple search keys concurrently.

ALGORITHM NOTE We allow a benign race condition when vertices at BFS level k are discovering vertices at level k+1. Specifically, we do not require synchronization to ensure that the first visitor must become the parent while locking out subsequent visitors. As long as the discovered BFS tree is correct at the end, the algorithm is considered to be correct.

6.2 Kernel 2 Output

For each search key, the routine must return an array containing valid breadth-first search parent information (per vertex). The parent of the searchkey is itself, and the parent of any vertex not included in the tree is -1. Algorithm kernel2 provides a sample (and inefficient) high-level implementation of kernel two.

function parent = kernel_2 (G, root)
%% Compute a sparse adjacency matrix representation
%% of the graph with edges from ij.

  N = size (G, 1);
  %% Adjust from zero labels.
  root = root + 1;
  parent = zeros (N, 1);
  parent (root) = root;

  vlist = zeros (N, 1);
  vlist(1) = root;
  lastk = 1;
  for k = 1:N,
    v = vlist(k);
    if v == 0, break; end
    [I,J,V] = find (G(:, v));
    nxt = I(parent(I) == 0);
    parent(nxt) = v;
    vlist(lastk + (1:length (nxt))) = nxt;
    lastk = lastk + length (nxt);
  end

  %% Adjust to zero labels.
  parent = parent - 1;

7 Validation

It is not intended that the results of full-scale runs of this benchmark can be validated by exact comparison to a standard reference result. At full scale, the data set is enormous, and its exact details depend on the pseudo-random number generator and BFS algorithm used. Therefore, the validation of an implementation of the benchmark uses soft checking of the results.

We emphasize that the intent of this benchmark is to exercise these algorithms on the largest data sets that will fit on machines being evaluated. However, for debugging purposes it may be desirable to run on small data sets, and it may be desirable to verify parallel results against serial results, or even against results from the executable specification.

The executable specification verifies its results by comparing them with results computed directly from the tuple list.

Kernel 2 validation: after each search, run (but do not time) a function that ensures that the discovered breadth-first tree is correct by ensuring that:

  1. the BFS tree is a tree and does not contain cycles,
  2. each tree edge connects vertices whose BFS levels differ by exactly one,
  3. every edge in the input list has vertices with levels that differ by at most one or that both are not in the BFS tree,
  4. the BFS tree spans an entire connected component's vertices, and
  5. a node and its parent are joined by an edge of the original graph.

Algorithm validate shows a sample validation routine.

function out = validate (parent, ij, search_key)
  out = 1;
  parent = parent + 1;
  search_key = search_key + 1;

  if parent (search_key) != search_key,
    out = 0;
    return;
  end

  ij = ij + 1;
  N = max (max (ij));
  slice = find (parent > 0);

  level = zeros (size (parent));
  level (slice) = 1;
  P = parent (slice);
  mask = P != search_key;
  k = 0;
  while any (mask),
    level(slice(mask)) = level(slice(mask)) + 1;
    P = parent (P);
    mask = P != search_key;
    k = k + 1;
    if k > N,
      %% There must be a cycle in the tree.
      out = -3;
      return;
    end
  end

  lij = level (ij);
  neither_in = lij(1,:) == 0 & lij(2,:) == 0;
  both_in = lij(1,:) > 0 & lij(2,:) > 0;
  if any (not (neither_in | both_in)),
    out = -4;
    return
  end
  respects_tree_level = abs (lij(1,:) - lij(2,:)) <= 1;
  if any (not (neither_in | respects_tree_level)),
    out = -5;
    return
  end

8 Computing and outputting performance information

8.1 Timing

Start the time for a search immediately prior to visiting the search root. Stop the time for that search when the output has been written to memory. Do not time any I/O outside of the search routine. If your algorithm relies on problem-specific data structures (by our definition, these are informed by vertex degree), you must include the setup time for such structures in each search. The spirit of the benchmark is to gauge the performance of a single search. We run many searches in order to compute means and variances, not to amortize data structure setup time.

8.2 Performance Metric (TEPS)

In order to compare the performance of Graph 500 "Search" implementations across a variety of architectures, programming models, and productivity languages and frameworks, we adopt a new performance metric described in this section. In the spirit of well-known computing rates floating-point operations per second (flops) measured by the LINPACK benchmark and global updates per second (GUPs) measured by the HPCC RandomAccess benchmark, we define a new rate called traversed edges per second (TEPS). We measure TEPS through the benchmarking of kernel 2 as follows. Let timeK2(n) be the measured execution time for kernel 2. Let m be the number of input edge tuples within the component traversed by the search, counting any multiple edges and self-loops. We define the normalized performance rate (number of edge traversals per second) as:

TEPS(n) = m / timeK2(n)

8.3 Output

The output must contain the following information:

SCALE
Graph generation parameter
edgefactor
Graph generation parameter
NBFS
Number of BFS searches run, 64 for non-trivial graphs
construction_time
The single kernel 1 time
min_time, firstquartile_time, median_time, thirdquartile_time, max_time
Quartiles for the kernel 2 times
mean_time, stddev_time
Mean and standard deviation of the kernel 2 times
min_nedge, firstquartile_nedge, median_nedge, thirdquartile_nedge, max_nedge
Quartiles for the number of input edges visited by kernel 2, see TEPS section above.
mean_nedge, stddev_nedge
Mean and standard deviation of the number of input edges visited by kernel 2, see TEPS section above.
min_TEPS, firstquartile_TEPS, median_TEPS, thirdquartile_TEPS, max_TEPS
Quartiles for the kernel 2 TEPS
harmonic_mean_TEPS, harmonic_stddev_TEPS
Mean and standard deviation of the kernel 2 TEPS. Note: Because TEPS is a rate, the rates are compared using harmonic means.

Additional fields are permitted. Algorithm output provides a high-level sample.

function output (SCALE, edgefactor, NBFS, kernel_1_time, kernel_2_time, kernel_2_nedge)
  printf ("SCALE: %d\n", SCALE);
  printf ("edgefactor: %d\n", edgefactor);
  printf ("NBFS: %d\n", NBFS);
  printf ("construction_time: %20.17e\n", kernel_1_time);

  S = statistics (kernel_2_time);
  printf ("min_time: %20.17e\n", S(1));
  printf ("firstquartile_time: %20.17e\n", S(2));
  printf ("median_time: %20.17e\n", S(3));
  printf ("thirdquartile_time: %20.17e\n", S(4));
  printf ("max_time: %20.17e\n", S(5));
  printf ("mean_time: %20.17e\n", S(6));
  printf ("stddev_time: %20.17e\n", S(7));

  S = statistics (kernel_2_nedge);
  printf ("min_nedge: %20.17e\n", S(1));
  printf ("firstquartile_nedge: %20.17e\n", S(2));
  printf ("median_nedge: %20.17e\n", S(3));
  printf ("thirdquartile_nedge: %20.17e\n", S(4));
  printf ("max_nedge: %20.17e\n", S(5));
  printf ("mean_nedge: %20.17e\n", S(6));
  printf ("stddev_nedge: %20.17e\n", S(7));

  TEPS = kernel_2_nedge ./ kernel_2_time;
  N = length (TEPS);
  S = statistics (TEPS);
  S(6) = mean (TEPS, 'h');
  %% Harmonic standard deviation from:
  %% Nilan Norris, The Standard Errors of the Geometric and Harmonic
  %% Means and Their Application to Index Numbers, 1940.
  %% http://www.jstor.org/stable/2235723
  tmp = zeros (N, 1);
  tmp(TEPS > 0) = 1./TEPS(TEPS > 0);
  tmp = tmp - 1/S(6);
  S(7) = (sqrt (sum (tmp.^2)) / (N-1)) * S(6)^2;

  printf ("min_TEPS: %20.17e\n", S(1));
  printf ("firstquartile_TEPS: %20.17e\n", S(2));
  printf ("median_TEPS: %20.17e\n", S(3));
  printf ("thirdquartile_TEPS: %20.17e\n", S(4));
  printf ("max_TEPS: %20.17e\n", S(5));
  printf ("harmonic_mean_TEPS: %20.17e\n", S(6));
  printf ("harmonic_stddev_TEPS: %20.17e\n", S(7));

8.4 References

Nilan Norris, The Standard Errors of the Geometric and Harmonic Means and Their Application to Index Numbers, The Annals of Mathematical Statistics, vol. 11, num. 4, 1940. http://www.jstor.org/stable/2235723

9 Sample driver

A high-level sample driver for the above routines is given in Algorithm driver.

SCALE = 10;
edgefactor = 16;
NBFS = 64;

rand ("seed", 103);

ij = kronecker_generator (SCALE, edgefactor);

tic;
G = kernel_1 (ij);
kernel_1_time = toc;

N = size (G, 1);
coldeg = full (spstats (G));
search_key = randperm (N);
search_key(coldeg(search_key) == 0) = [];
if length (search_key) > NBFS,
  search_key = search_key(1:NBFS);
else
  NBFS = length (search_key);
end
search_key = search_key - 1;  

kernel_2_time = Inf * ones (NBFS, 1);
kernel_2_nedge = zeros (NBFS, 1);

indeg = histc (ij(:), 1:N); % For computing the number of edges

for k = 1:NBFS,
  tic;
  parent = kernel_2 (G, search_key(k));
  kernel_2_time(k) = toc;
  err = validate (parent, ij, search_key (k));
  if err <= 0,
    error (sprintf ("BFS %d from search key %d failed to validate: %d",
                    k, search_key(k), err));
  end
  kernel_2_nedge(k) = sum (indeg(parent >= 0))/2; % Volume/2
end

output (SCALE, edgefactor, NBFS, kernel_1_time, kernel_2_time, kernel_2_nedge);

10 Evaluation Criteria

In approximate order of importance, the goals of this benchmark are:

  • Fair adherence to the intent of the benchmark specification
  • Maximum problem size for a given machine
  • Minimum execution time for a given problem size

Less important goals:

  • Minimum code size (not including validation code)
  • Minimal development time
  • Maximal maintainability
  • Maximal extensibility

Author: Graph 500 Steering Committee

Date: 2010-10-05 10:35:40 EDT

HTML generated by org-mode 7.01trans in emacs 24

CombBLAS_beta_16_2/graph500-1.2/verify.c000644 000765 000024 00000010742 13212627400 020721 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #include "compat.h" #include #include #include #include #include #include #include #include "xalloc.h" static int compute_levels (int64_t * level, int64_t nv, const int64_t * restrict bfs_tree, int64_t root) { int err = 0; OMP("omp parallel shared(err)") { int terr; int64_t k; OMP("omp for") for (k = 0; k < nv; ++k) level[k] = (k == root? 0 : -1); OMP("omp for") MTA("mta assert parallel") MTA("mta use 100 streams") for (k = 0; k < nv; ++k) { if (level[k] >= 0) continue; terr = err; if (!terr && bfs_tree[k] >= 0 && k != root) { int64_t parent = k; int64_t nhop = 0; /* Run up the tree until we encounter an already-leveled vertex. */ while (parent >= 0 && level[parent] < 0 && nhop < nv) { assert (parent != bfs_tree[parent]); parent = bfs_tree[parent]; ++nhop; } if (nhop >= nv) terr = -1; /* Cycle. */ if (parent < 0) terr = -2; /* Ran off the end. */ if (!terr) { /* Now assign levels until we meet an already-leveled vertex */ /* NOTE: This permits benign races if parallelized. */ nhop += level[parent]; parent = k; while (level[parent] < 0) { assert (nhop > 0); level[parent] = nhop--; parent = bfs_tree[parent]; } assert (nhop == level[parent]); /* Internal check to catch mistakes in races... */ #if !defined(NDEBUG) nhop = 0; parent = k; int64_t lastlvl = level[k]+1; while (level[parent] > 0) { assert (lastlvl == 1 + level[parent]); lastlvl = level[parent]; parent = bfs_tree[parent]; ++nhop; } #endif } } if (terr) { err = terr; OMP("omp flush (err)"); } } } return err; } int64_t verify_bfs_tree (int64_t *bfs_tree_in, int64_t max_bfsvtx, int64_t root, const int64_t *IJ_in, int64_t nedge) { int64_t * restrict bfs_tree = bfs_tree_in; const int64_t * restrict IJ = IJ_in; int err, nedge_traversed; int64_t * restrict seen_edge, * restrict level; const int64_t nv = max_bfsvtx+1; /* This code is horrifically contorted because many compilers complain about continue, return, etc. in parallel sections. */ if (root > max_bfsvtx || bfs_tree[root] != root) return -999; err = 0; nedge_traversed = 0; seen_edge = xmalloc_large (2 * (nv) * sizeof (*seen_edge)); level = &seen_edge[nv]; err = compute_levels (level, nv, bfs_tree, root); if (err) goto done; OMP("omp parallel shared(err)") { int64_t k; int terr = 0; OMP("omp for") for (k = 0; k < nv; ++k) seen_edge[k] = 0; OMP("omp for") MTA("mta assert parallel") MTA("mta use 100 streams") for (k = 0; k < 2*nedge; k+=2) { const int64_t i = IJ[k]; const int64_t j = IJ[k+1]; int64_t lvldiff; terr = err; if (i < 0 || j < 0) continue; if (i > max_bfsvtx && j <= max_bfsvtx) terr = -10; if (j > max_bfsvtx && i <= max_bfsvtx) terr = -11; if (terr) { err = terr; OMP("omp flush(err)"); } if (terr || i > max_bfsvtx /* both i & j are on the same side of max_bfsvtx */) continue; /* All neighbors must be in the tree. */ if (bfs_tree[i] >= 0 && bfs_tree[j] < 0) terr = -12; if (bfs_tree[j] >= 0 && bfs_tree[i] < 0) terr = -13; if (terr) { err = terr; OMP("omp flush(err)"); } if (terr || bfs_tree[i] < 0 /* both i & j have the same sign */) continue; /* Both i and j are in the tree, count as a traversed edge. NOTE: This counts self-edges and repeated edges. They're part of the input data. */ ++nedge_traversed; /* Mark seen tree edges. */ if (i != j) { if (bfs_tree[i] == j) seen_edge[i] = 1; if (bfs_tree[j] == i) seen_edge[j] = 1; } lvldiff = level[i] - level[j]; /* Check that the levels differ by no more than one. */ if (lvldiff > 1 || lvldiff < -1) terr = -14; if (terr) { err = terr; OMP("omp flush(err)"); } } if (!terr) { /* Check that every BFS edge was seen and that there's only one root. */ OMP("omp for") MTA("mta assert parallel") MTA("mta use 100 streams") for (k = 0; k < nv; ++k) { terr = err; if (!terr && k != root) { if (bfs_tree[k] >= 0 && !seen_edge[k]) terr = -15; if (bfs_tree[k] == k) terr = -16; if (terr) { err = terr; OMP("omp flush(err)"); } } } } } done: xfree_large (seen_edge); if (err) return err; return nedge_traversed; } CombBLAS_beta_16_2/graph500-1.2/kronecker.h000644 000765 000024 00000000302 13212627400 021374 0ustar00aydinbulucstaff000000 000000 #if !defined(KRONECKER_HEADER_) #define KRONECKER_HEADER_ void kronecker_edgelist (int64_t *IJ, int64_t nedge, int64_t SCALE, double A, double B, double C); #endif /* KRONECKER_HEADER_ */ CombBLAS_beta_16_2/graph500-1.2/options.h000644 000765 000024 00000001123 13212627400 021106 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #if !defined(OPTIONS_HEADER_) #define OPTIONS_HEADER_ extern int VERBOSE; extern int use_RMAT; #define A_PARAM 0.57 #define B_PARAM 0.19 #define C_PARAM 0.19 /* Hence D = 0.05. */ extern double A, B, C, D; #define NBFS_max 64 extern int NBFS; #define default_SCALE ((int64_t)14) #define default_edgefactor ((int64_t)16) extern int64_t SCALE; extern int64_t edgefactor; void get_options (int argc, char **argv); #endif /* OPTIONS_HEADER_ */ CombBLAS_beta_16_2/graph500-1.2/README000644 000765 000024 00000004666 13212627400 020141 0ustar00aydinbulucstaff000000 000000 Graph500 sequentual and shared-memory reference implementation See COPYING for the general license. Portions copyright 2010 by the Georgia Institute of Technology, and portions copyright 2009-2010 by he Trustees of Indiana University. The specification is included both in plain text format (Graph500.org, with Emacs Org mode annotations) and exported HTML (Graph500.html). Included implementations: octave/Graph500.m : GNU Octave (may be Matlab(TM) compatible) from the specification. seq-list/seq-list : Sequential list-based implementation seq-csr/seq-csr : Sequential compressed-sparse-row implementation omp-csr/omp-csr : OpenMP compressed-sparse-row implementation xmt-csr/xmt-csr : Cray XMT compressed-sparse-row implementation xmt-csr-local/xmt-csr-local : Cray XMT compressed-sparse-row implementation accumulating vertices into a small buffer before storing globally BUILDING INSTRUCTIONS The Makefile includes make.inc to define compiler and flag variables. Some sample make.inc files are in the make-incs directory. The Makefile relies heavily on implicit rules and is known to work using GNU Make. To build the MPI executables, set BUILD_MPI=Yes in your make.inc, and optionally set MPICC if needed. To build the OpenMP executables, set BUILD_OPENMP=Yes in your make.inc, and define CFLAGS_OPENMP appropriately. For the XMT, ensure the correct modules are loaded and set BUILD_XMT=Yes. This disables all other builds, including the sequential versions. RUNNING INSTRUCTIONS Options: v : version h|? : this message R : use R-MAT from SSCA2 (default: use Kronecker generator) s : R-MAT scale (default 14) e : R-MAT edge factor (default 16) A|a : R-MAT A (default 0.57) >= 0 B|b : R-MAT B (default 0.19) >= 0 C|c : R-MAT C (default 0.19) >= 0 D|d : R-MAT D (default 0.05) >= 0 Note: Setting 3 of A,B,C,D requires the arguments to sum to at most 1. Otherwise, the parameters are added and normalized so that the sum is 1. V : Enable extra (Verbose) output Outputs take the form of "key: value", with keys: SCALE edgefactor construction_time min_time firstquartile_time median_time thirdquartile_time max_time mean_time stddev_time min_nedge firstquartile_nedge median_nedge thirdquartile_nedge max_nedge mean_nedge stddev_nedge min_TEPS firstquartile_TEPS median_TEPS thirdquartile_TEPS max_TEPS harmonic_mean_TEPS harmonic_stddev_TEPS CombBLAS_beta_16_2/graph500-1.2/timer.h000644 000765 000024 00000000655 13212627400 020544 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #if !defined(TIMER_HEADER_) #define TIMER_HEADER_ /** Start timing. */ void tic (void); /** Return seconds since last tic. */ double toc (void); /** Macro to time a block. */ #define TIME(timevar, what) do { tic (); what; timevar = toc(); } while (0) #endif /* TIMER_HEADER_ */ CombBLAS_beta_16_2/graph500-1.2/rmat.h000644 000765 000024 00000001063 13212627400 020361 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #if !defined(RMAT_HEADER_) #define RMAT_HEADER_ /** Fill IJ with a randomly permuted R-MAT generated edge list. */ void rmat_edgelist (int64_t *IJ, int64_t nedge, int SCALE, double A, double B, double C); void permute_vertex_labels (int64_t * IJ, int64_t nedge, int64_t max_nvtx, void * st, int64_t * newlabel); void permute_edgelist (int64_t * IJ, int64_t nedge, void *st); #endif /* RMAT_HEADER_ */ CombBLAS_beta_16_2/graph500-1.2/generator/000755 000765 000024 00000000000 13271513361 021240 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/COPYING000644 000765 000024 00000003647 13212627400 020312 0ustar00aydinbulucstaff000000 000000 Copyright 2010, Georgia Institute of Technology, USA 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 listed in this license in the documentation and/or other materials provided with the distribution. - Neither the name of the copyright holders nor the names of its 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. ==== Included Kronecker graph generator: /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ CombBLAS_beta_16_2/graph500-1.2/seq-list/000755 000765 000024 00000000000 13212627400 021006 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/prng.c000644 000765 000024 00000002021 13212627400 020352 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #include "compat.h" #include #include #include #include "generator/splittable_mrg.h" uint_fast32_t prng_seed[5]; static mrg_state prng_state_store; void *prng_state = &prng_state_store; /* Spread the two 64-bit numbers into five nonzero values in the correct * range. */ static void make_mrg_seed(uint64_t userseed, uint_fast32_t* seed) { seed[0] = (userseed & 0x3FFFFFFF) + 1; seed[1] = ((userseed >> 30) & 0x3FFFFFFF) + 1; seed[2] = (userseed & 0x3FFFFFFF) + 1; seed[3] = ((userseed >> 30) & 0x3FFFFFFF) + 1; seed[4] = ((userseed >> 60) << 4) + (userseed >> 60) + 1; } void init_random (void) { long seed = -1; if (getenv ("SEED")) { errno = 0; seed = strtol (getenv ("SEED"), NULL, 10); if (errno) seed = -1; } if (seed < 0) seed = 0xDECAFBAD; make_mrg_seed (seed, prng_seed); mrg_seed(&prng_state_store, prng_seed); } CombBLAS_beta_16_2/graph500-1.2/octave/000755 000765 000024 00000000000 13212627400 020526 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/make-incs/000755 000765 000024 00000000000 13212627400 021114 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/compat.h000644 000765 000024 00000001674 13212627400 020711 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #if !defined(COMPAT_HEADER_) #define COMPAT_HEADER_ #define _FILE_OFFSET_BITS 64 #define _THREAD_SAFE #define _XOPEN_SOURCE 600 #define _XOPEN_SOURCE_EXTENDED #define _SVID_SOURCE #if __STDC_VERSION__ >= 199901L #include #elif defined(__MTA__) #include #define PRId64 "d" #define SCNd64 "d" #else #warning "Defining long as int64_t." typedef long int64_t; typedef unsigned uint32_fast_t; #define PRId64 "ld" #define SCNd64 "ld" #if !defined(restrict) #define restrict #endif #endif #if defined(_OPENMP) #define OMP(x) _Pragma(x) #include #else #define OMP(x) static int omp_get_thread_num (void) { return 0; } static int omp_get_num_threads (void) { return 1; } #endif #if defined(__MTA__) #define MTA(x) _Pragma(x) #else #define MTA(x) #endif #endif /* COMPAT_HEADER_ */ CombBLAS_beta_16_2/graph500-1.2/xalloc.h000644 000765 000024 00000000645 13212627400 020705 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #if !defined(XALLOC_HEADER_) #define XALLOC_HEADER_ void * xmalloc (size_t); void * xmalloc_large (size_t); void xfree_large (void *); void * xmalloc_large_ext (size_t); /* void mark_large_unused (void *); void mark_large_willuse (void *); */ #endif /* XALLOC_HEADER_ */ CombBLAS_beta_16_2/graph500-1.2/Graph500.org000644 000765 000024 00000062553 13212627400 021257 0ustar00aydinbulucstaff000000 000000 #+TITLE: Graph 500 Benchmark 1 ("Search") #+AUTHOR: Graph 500 Steering Committee #+EMAIL: sc@graph500.org #+LANGUAGE: en #+OPTIONS: H:3 num:t toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t #+OPTIONS: TeX:t LaTeX:t skip:nil d:nil todo:t pri:nil tags:not-in-toc #+STYLE: Contributors: David A. Bader (Georgia Institute of Technology), Jonathan Berry (Sandia National Laboratories), Simon Kahan (Pacific Northwest National Laboratory and University of Washington), Richard Murphy (Sandia National Laboratories), E. Jason Riedy (Georgia Institute of Technology), and Jeremiah Willcock (Indiana University). Version History: - V0.1 :: Draft, created 28 July 2010 - V0.2 :: Draft, created 29 September 2010 - V0.3 :: Draft, created 30 September 2010 - V1.0 :: Created 1 October 2010 - V1.1 :: Created 3 October 2010 Version 0.1 of this document was part of the Graph 500 community benchmark effort, led by Richard Murphy (Sandia National Laboratories). The intent is that there will be at least three variants of implementations, on shared memory and threaded systems, on distributed memory clusters, and on external memory map-reduce clouds. This specification is for the first of potentially several benchmark problems. References: "Introducing the Graph 500," Richard C. Murphy, Kyle B. Wheeler, Brian W. Barrett, James A. Ang, Cray User’s Group (CUG), May 5, 2010. "DFS: A Simple to Write Yet Difficult to Execute Benchmark," Richard C. Murphy, Jonathan Berry, William McLendon, Bruce Hendrickson, Douglas Gregor, Andrew Lumsdaine, IEEE International Symposium on Workload Characterizations 2006 (IISWC06), San Jose, CA, 25-27 October 2006. * Brief Description of the Graph 500 Benchmark Data-intensive supercomputer applications are an increasingly important workload, but are ill-suited for platforms designed for 3D physics simulations. Application performance cannot be improved without a meaningful benchmark. Graphs are a core part of most analytics workloads. Backed by a steering committee of 30 international HPC experts from academia, industry, and national laboratories, this specification establishes a large-scale benchmark for these applications. It will offer a forum for the community and provide a rallying point for data-intensive supercomputing problems. This is the first serious approach to augment the Top 500 with data-intensive applications. The intent of this benchmark problem ("Search") is to develop a compact application that has multiple analysis techniques (multiple kernels) accessing a single data structure representing a weighted, undirected graph. In addition to a kernel to construct the graph from the input tuple list, there is one additional computational kernel to operate on the graph. This benchmark includes a scalable data generator which produces edge tuples containing the start vertex and end vertex for each edge. The first kernel constructs an /undirected/ graph in a format usable by all subsequent kernels. No subsequent modifications are permitted to benefit specific kernels. The second kernel performs a breadth-first search of the graph. Both kernels are timed. There are five problem classes defined by their input size: - toy :: 17GB or around 10^10 bytes, which we also call level 10, - mini :: 140GB (10^11 bytes, level 11), - small :: 1TB (10^12 bytes, level 12), - medium :: 17TB (10^13 bytes, level 13), - large :: 140TB (10^14 bytes, level 14), and - huge :: 1.1PB (10^15 bytes, level 15). Table \ref{tbl:classes} provides the parameters used by the graph generator specified below. ** References D.A. Bader, J. Feo, J. Gilbert, J. Kepner, D. Koester, E. Loh, K. Madduri, W. Mann, Theresa Meuse, HPCS Scalable Synthetic Compact Applications #2 Graph Analysis (SSCA#2 v2.2 Specification), 5 September 2007. * Overall benchmark The benchmark performs the following steps: 1. Generate the edge list. 2. Construct a graph from the edge list (*timed*, kernel 1). 3. Randomly sample 64 unique search keys with degree at least one, not counting self-loops. 4. For each search key: 1. Compute the parent array (*timed*, kernel 2). 2. Validate that the parent array is a correct BFS search tree for the given search tree. 5. Compute and output performance information. Only the sections marked as *timed* are included in the performance information. Note that all uses of "random" permit pseudorandom number generation. * Generating the edge list ** Brief Description The scalable data generator will construct a list of edge tuples containing vertex identifiers. Each edge is undirected with its endpoints given in the tuple as StartVertex and EndVertex. The intent of the first kernel below is to convert a list with no locality into a more optimized form. The generated list of input tuples must not exhibit any locality that can be exploited by the computational kernels. Thus, the vertex numbers must be randomized and a random ordering of tuples must be presented to kernel 1. The data generator may be parallelized, but the vertex names must be globally consistent and care must be taken to minimize effects of data locality at the processor level. ** Detailed Text Description The edge tuples will have the form where StartVertex is one endpoint vertex label and EndVertex is the other endpoint vertex label. The space of labels is the set of integers beginning with *zero* up to but not including the number of vertices N (defined below). The kernels are not provided the size N explicitly but must discover it. The input values required to describe the graph are: - SCALE :: The logarithm base two of the number of vertices. - edgefactor :: The ratio of the graph's edge count to its vertex count (i.e., half the average degree of a vertex in the graph). These inputs determine the graph's size: - N :: the total number of vertices, 2^SCALE. An implementation may use any set of N distinct integers to number the vertices, but at least 48 bits must be allocated per vertex number. Other parameters may be assumed to fit within the natural word of the machine. N is derived from the problem’s scaling parameter. - M :: the number of edges. M = edgefactor * N. The graph generator is a Kronecker generator similar to the Recursive MATrix (R-MAT) scale-free graph generation algorithm [Chakrabarti, et al., 2004]. For ease of discussion, the description of this R-MAT generator uses an adjacency matrix data structure; however, implementations may use any alternate approach that outputs the equivalent list of edge tuples. This model recursively sub-divides the adjacency matrix of the graph into four equal-sized partitions and distributes edges within these partitions with unequal probabilities. Initially, the adjacency matrix is empty, and edges are added one at a time. Each edge chooses one of the four partitions with probabilities A, B, C, and D, respectively. These probabilities, the initiator parameters, are provided in Table \ref{tbl:initiator}. #+CAPTION: Initiator parameters for the Kronecker graph generator #+LABEL: tbl:initiator | A = 0.57 | B = 0.19 | | C = 0.19 | D = 1-(A+B+C) = 0.05 | The next section details a high-level implementation for this generator. High-performance, parallel implementations are included in the reference implementation. The graph generator creates a small number of multiple edges between two vertices as well as self-loops. Multiple edges, self-loops, and isolated vertices may be ignored in the subsequent kernels but must be included in the edge list provided to the first kernel. The algorithm also generates the data tuples with high degrees of locality. Thus, as a final step, vertex numbers must be randomly permuted, and then the edge tuples randomly shuffled. It is permissible to run the data generator in parallel. In this case, it is necessary to ensure that the vertices are named globally, and that the generated data does not possess any locality, either in local memory or globally across processors. The scalable data generator should be run before starting kernel 1, storing its results to either RAM or disk. If stored to disk, the data may be retrieved before starting kernel 1. The data generator and retrieval operations need not be timed. ** Sample high-level implementation of the Kronecker generator The GNU Octave routine in Algorithm \ref{alg:generator} is an attractive implementation in that it is embarrassingly parallel and does not require the explicit formation of the adjacency matrix. #+CAPTION: High-level generator code #+LABEL: alg:generator #+BEGIN_SRC Octave function ij = kronecker_generator (SCALE, edgefactor) %% Generate an edgelist according to the Graph500 %% parameters. In this sample, the edge list is %% returned in an array with two rows, where StartVertex %% is first row and EndVertex is the second. The vertex %% labels start at zero. %% %% Example, creating a sparse matrix for viewing: %% ij = kronecker_generator (10, 16); %% G = sparse (ij(1,:)+1, ij(2,:)+1, ones (1, size (ij, 2))); %% spy (G); %% The spy plot should appear fairly dense. Any locality %% is removed by the final permutations. %% Set number of vertices. N = 2^SCALE; %% Set number of edges. M = edgefactor * N; %% Set initiator probabilities. [A, B, C] = deal (0.57, 0.19, 0.19); %% Create index arrays. ij = ones (2, M); %% Loop over each order of bit. ab = A + B; c_norm = C/(1 - (A + B)); a_norm = A/(A + B); for ib = 1:SCALE, %% Compare with probabilities and set bits of indices. ii_bit = rand (1, M) > ab; jj_bit = rand (1, M) > ( c_norm * ii_bit + a_norm * not (ii_bit) ); ij = ij + 2^(ib-1) * [ii_bit; jj_bit]; end %% Permute vertex labels p = randperm (N); ij = p(ij); %% Permute the edge list p = randperm (M); ij = ij(:, p); %% Adjust to zero-based labels. ij = ij - 1; #+END_SRC ** Parameter settings The input parameter settings for each class are given in Table \ref{tbl:classes}. #+CAPTION: Problem class definitions and required storage for the edge list assuming 64-bit integers. #+LABEL: tbl:classes | Problem class | SCALE | edge factor | Approx. storage size in TB | |-------------------+-------+-------------+----------------------------| | Toy (level 10) | 26 | 16 | 0.0172 | | Mini (level 11) | 29 | 16 | 0.1374 | | Small (level 12) | 32 | 16 | 1.0995 | | Medium (level 13) | 36 | 16 | 17.5922 | | Large (level 14) | 39 | 16 | 140.7375 | | Huge (level 15) | 42 | 16 | 1125.8999 | #+TBLFM: $4=2^$2*$3*2*8/1e12;%.4f ** References D. Chakrabarti, Y. Zhan, and C. Faloutsos, R-MAT: A recursive model for graph mining, SIAM Data Mining 2004. Section 17.6, Algorithms in C (third edition). Part 5 Graph Algorithms, Robert Sedgewick (Programs 17.7 and 17.8) P. Sanders, Random Permutations on Distributed, External and Hierarchical Memory, Information Processing Letters 67 (1988) pp 305-309. * Kernel 1 – Graph Construction ** Description The first kernel may transform the edge list to any data structures (held in internal or external memory) that are used for the remaining kernels. For instance, kernel 1 may construct a (sparse) graph from a list of tuples; each tuple contains endpoint vertex identifiers for an edge, and a weight that represents data assigned to the edge. #+ XXX: Unsure about this part. I'd rather permit structural #+ optimizations here so long as they're used for *all* kernels. #+ - However, any layout or arrangement computations intended to #+ increase locality, improve load-balance, or reduce hot-spotting #+ must be counted in the benchmark execution time. Therefore, /no #+ comparison of or binning by vertex degrees is permitted during this #+ kernel 1 (graph construction)/. The graph may be represented in any manner, but it may not be modified by or between subsequent kernels. Space may be reserved in the data structure for marking or locking. Only one copy of a kernel will be run at a time; that kernel has exclusive access to any such marking or locking space and is permitted to modify that space (only). There are various internal memory representations for sparse graphs, including (but not limited to) sparse matrices and (multi-level) linked lists. For the purposes of this application, the kernel is provided only the edge list and the edge list's size. Further information such as the number of vertices must be computed within this kernel. Algorithm \ref{alg:kernel1} provides a high-level sample implementation of kernel 1. The process of constructing the graph data structure (in internal or external memory) from the set of tuples must be timed. #+CAPTION: High-level implementation of kernel 1 #+LABEL: alg:kernel1 #+BEGIN_SRC Octave function G = kernel_1 (ij) %% Compute a sparse adjacency matrix representation %% of the graph with edges from ij. %% Remove self-edges. ij(:, ij(1,:) == ij(2,:)) = []; %% Adjust away from zero labels. ij = ij + 1; %% Find the maximum label for sizing. N = max (max (ij)); %% Create the matrix, ensuring it is square. G = sparse (ij(1,:), ij(2,:), ones (1, size (ij, 2)), N, N); %% Symmetrize to model an undirected graph. G = spones (G + G.'); #+END_SRC ** References Section 17.6 Algorithms in C third edition Part 5 Graph Algorithms, Robert Sedgewick (Program 17.9) * Sampling 64 search keys The search keys must be randomly sampled from the vertices in the graph. To avoid trivial searches, sample only from vertices that are connected to some other vertex. Their degrees, not counting self-loops, must be at least one. If there are fewer than 64 such vertices, run fewer than 64 searches. This should never occur with the graph sizes in this benchmark, but there is a non-zero probability of producing a trivial or nearly trivial graph. The number of search keys used is included in the output, but this step is untimed. * Kernel 2 – Breadth-First Search ** Description A Breadth-First Search (BFS) of a graph starts with a single source vertex, then, in phases, finds and labels its neighbors, then the neighbors of its neighbors, etc. This is a fundamental method on which many graph algorithms are based. A formal description of BFS can be found in Cormen, Leiserson, and Rivest. Below, we specify the input and output for a BFS benchmark, and we impose some constraints on the computation. However, we do not constrain the choice of BFS algorithm itself, as long as it produces a correct BFS tree as output. This benchmark's memory access pattern (internal or external) is data-dependent with small average prefetch depth. As in a simple concurrent linked-list traversal benchmark, performance reflects an architecture's throughput when executing concurrent threads, each of low memory concurrency and high memory reference density. Unlike such a benchmark, this one also measures resilience to hot-spotting when many of the memory references are to the same location; efficiency when every thread's execution path depends on the asynchronous side-effects of others; and the ability to dynamically load balance unpredictably sized work units. Measuring synchronization performance is not a primary goal here. You may not search from multiple search keys concurrently. *ALGORITHM NOTE* We allow a benign race condition when vertices at BFS level k are discovering vertices at level k+1. Specifically, we do not require synchronization to ensure that the first visitor must become the parent while locking out subsequent visitors. As long as the discovered BFS tree is correct at the end, the algorithm is considered to be correct. ** Kernel 2 Output For each search key, the routine must return an array containing valid breadth-first search parent information (per vertex). The parent of the search_key is itself, and the parent of any vertex not included in the tree is -1. Algorithm \ref{alg:kernel2} provides a sample (and inefficient) high-level implementation of kernel two. #+CAPTION: High-level implementation of kernel 2 #+LABEL: alg:kernel2 #+BEGIN_SRC Octave function parent = kernel_2 (G, root) %% Compute a sparse adjacency matrix representation %% of the graph with edges from ij. N = size (G, 1); %% Adjust from zero labels. root = root + 1; parent = zeros (N, 1); parent (root) = root; vlist = zeros (N, 1); vlist(1) = root; lastk = 1; for k = 1:N, v = vlist(k); if v == 0, break; end [I,J,V] = find (G(:, v)); nxt = I(parent(I) == 0); parent(nxt) = v; vlist(lastk + (1:length (nxt))) = nxt; lastk = lastk + length (nxt); end %% Adjust to zero labels. parent = parent - 1; #+END_SRC * Validation It is not intended that the results of full-scale runs of this benchmark can be validated by exact comparison to a standard reference result. At full scale, the data set is enormous, and its exact details depend on the pseudo-random number generator and BFS algorithm used. Therefore, the validation of an implementation of the benchmark uses soft checking of the results. We emphasize that the intent of this benchmark is to exercise these algorithms on the largest data sets that will fit on machines being evaluated. However, for debugging purposes it may be desirable to run on small data sets, and it may be desirable to verify parallel results against serial results, or even against results from the executable specification. The executable specification verifies its results by comparing them with results computed directly from the tuple list. Kernel 2 validation: after each search, run (but do not time) a function that ensures that the discovered breadth-first tree is correct by ensuring that: 1) the BFS tree is a tree and does not contain cycles, 2) each tree edge connects vertices whose BFS levels differ by exactly one, 3) every edge in the input list has vertices with levels that differ by at most one or that both are not in the BFS tree, 4) the BFS tree spans an entire connected component's vertices, and 5) a node and its parent are joined by an edge of the original graph. Algorithm \ref{alg:validate} shows a sample validation routine. #+CAPTION: High-level implementation of kernel 2 validation #+LABEL: alg:validate #+BEGIN_SRC Octave function out = validate (parent, ij, search_key) out = 1; parent = parent + 1; search_key = search_key + 1; if parent (search_key) != search_key, out = 0; return; end ij = ij + 1; N = max (max (ij)); slice = find (parent > 0); level = zeros (size (parent)); level (slice) = 1; P = parent (slice); mask = P != search_key; k = 0; while any (mask), level(slice(mask)) = level(slice(mask)) + 1; P = parent (P); mask = P != search_key; k = k + 1; if k > N, %% There must be a cycle in the tree. out = -3; return; end end lij = level (ij); neither_in = lij(1,:) == 0 & lij(2,:) == 0; both_in = lij(1,:) > 0 & lij(2,:) > 0; if any (not (neither_in | both_in)), out = -4; return end respects_tree_level = abs (lij(1,:) - lij(2,:)) <= 1; if any (not (neither_in | respects_tree_level)), out = -5; return end #+END_SRC * Computing and outputting performance information ** Timing Start the time for a search immediately prior to visiting the search root. Stop the time for that search when the output has been written to memory. Do not time any I/O outside of the search routine. If your algorithm relies on problem-specific data structures (by our definition, these are informed by vertex degree), you must include the setup time for such structures in /each search/. The spirit of the benchmark is to gauge the performance of a single search. We run many searches in order to compute means and variances, not to amortize data structure setup time. ** Performance Metric (TEPS) In order to compare the performance of Graph 500 "Search" implementations across a variety of architectures, programming models, and productivity languages and frameworks, we adopt a new performance metric described in this section. In the spirit of well-known computing rates floating-point operations per second (flops) measured by the LINPACK benchmark and global updates per second (GUPs) measured by the HPCC RandomAccess benchmark, we define a new rate called traversed edges per second (TEPS). We measure TEPS through the benchmarking of kernel 2 as follows. Let time_K2(n) be the measured execution time for kernel 2. Let m be the number of input edge tuples within the component traversed by the search, counting any multiple edges and self-loops. We define the normalized performance rate (number of edge traversals per second) as: #+BEGIN_CENTER TEPS(n) = m / time_K2(n) #+END_CENTER ** Output The output must contain the following information: - SCALE :: Graph generation parameter - edgefactor :: Graph generation parameter - NBFS :: Number of BFS searches run, 64 for non-trivial graphs - construction\_time :: The single kernel 1 time - min\_time, firstquartile\_time, median\_time, thirdquartile\_time, max\_time :: Quartiles for the kernel 2 times - mean\_time, stddev\_time :: Mean and standard deviation of the kernel 2 times - min\_nedge, firstquartile\_nedge, median\_nedge, thirdquartile\_nedge, max\_nedge :: Quartiles for the number of input edges visited by kernel 2, see TEPS section above. - mean\_nedge, stddev\_nedge :: Mean and standard deviation of the number of input edges visited by kernel 2, see TEPS section above. - min\_TEPS, firstquartile\_TEPS, median\_TEPS, thirdquartile\_TEPS, max\_TEPS :: Quartiles for the kernel 2 TEPS - harmonic\_mean\_TEPS, harmonic\_stddev\_TEPS :: Mean and standard deviation of the kernel 2 TEPS. *Note*: Because TEPS is a rate, the rates are compared using *harmonic* means. Additional fields are permitted. Algorithm \ref{alg:output} provides a high-level sample. #+CAPTION: High-level implementation of the output routine #+LABEL: alg:output #+BEGIN_SRC Octave function output (SCALE, edgefactor, NBFS, kernel_1_time, kernel_2_time, kernel_2_nedge) printf ("SCALE: %d\n", SCALE); printf ("edgefactor: %d\n", edgefactor); printf ("NBFS: %d\n", NBFS); printf ("construction_time: %20.17e\n", kernel_1_time); S = statistics (kernel_2_time); printf ("min_time: %20.17e\n", S(1)); printf ("firstquartile_time: %20.17e\n", S(2)); printf ("median_time: %20.17e\n", S(3)); printf ("thirdquartile_time: %20.17e\n", S(4)); printf ("max_time: %20.17e\n", S(5)); printf ("mean_time: %20.17e\n", S(6)); printf ("stddev_time: %20.17e\n", S(7)); S = statistics (kernel_2_nedge); printf ("min_nedge: %20.17e\n", S(1)); printf ("firstquartile_nedge: %20.17e\n", S(2)); printf ("median_nedge: %20.17e\n", S(3)); printf ("thirdquartile_nedge: %20.17e\n", S(4)); printf ("max_nedge: %20.17e\n", S(5)); printf ("mean_nedge: %20.17e\n", S(6)); printf ("stddev_nedge: %20.17e\n", S(7)); TEPS = kernel_2_nedge ./ kernel_2_time; N = length (TEPS); S = statistics (TEPS); S(6) = mean (TEPS, 'h'); %% Harmonic standard deviation from: %% Nilan Norris, The Standard Errors of the Geometric and Harmonic %% Means and Their Application to Index Numbers, 1940. %% http://www.jstor.org/stable/2235723 tmp = zeros (N, 1); tmp(TEPS > 0) = 1./TEPS(TEPS > 0); tmp = tmp - 1/S(6); S(7) = (sqrt (sum (tmp.^2)) / (N-1)) * S(6)^2; printf ("min_TEPS: %20.17e\n", S(1)); printf ("firstquartile_TEPS: %20.17e\n", S(2)); printf ("median_TEPS: %20.17e\n", S(3)); printf ("thirdquartile_TEPS: %20.17e\n", S(4)); printf ("max_TEPS: %20.17e\n", S(5)); printf ("harmonic_mean_TEPS: %20.17e\n", S(6)); printf ("harmonic_stddev_TEPS: %20.17e\n", S(7)); #+END_SRC ** References Nilan Norris, The Standard Errors of the Geometric and Harmonic Means and Their Application to Index Numbers, The Annals of Mathematical Statistics, vol. 11, num. 4, 1940. http://www.jstor.org/stable/2235723 * Sample driver A high-level sample driver for the above routines is given in Algorithm \ref{alg:driver}. #+CAPTION: High-level sample driver #+LABEL: alg:driver #+BEGIN_SRC Octave SCALE = 10; edgefactor = 16; NBFS = 64; rand ("seed", 103); ij = kronecker_generator (SCALE, edgefactor); tic; G = kernel_1 (ij); kernel_1_time = toc; N = size (G, 1); coldeg = full (spstats (G)); search_key = randperm (N); search_key(coldeg(search_key) == 0) = []; if length (search_key) > NBFS, search_key = search_key(1:NBFS); else NBFS = length (search_key); end search_key = search_key - 1; kernel_2_time = Inf * ones (NBFS, 1); kernel_2_nedge = zeros (NBFS, 1); indeg = histc (ij(:), 1:N); % For computing the number of edges for k = 1:NBFS, tic; parent = kernel_2 (G, search_key(k)); kernel_2_time(k) = toc; err = validate (parent, ij, search_key (k)); if err <= 0, error (sprintf ("BFS %d from search key %d failed to validate: %d", k, search_key(k), err)); end kernel_2_nedge(k) = sum (indeg(parent >= 0))/2; % Volume/2 end output (SCALE, edgefactor, NBFS, kernel_1_time, kernel_2_time, kernel_2_nedge); #+END_SRC * Evaluation Criteria In approximate order of importance, the goals of this benchmark are: - Fair adherence to the intent of the benchmark specification - Maximum problem size for a given machine - Minimum execution time for a given problem size Less important goals: - Minimum code size (not including validation code) - Minimal development time - Maximal maintainability - Maximal extensibility CombBLAS_beta_16_2/graph500-1.2/graph500.h000644 000765 000024 00000001137 13212627400 020746 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #if !defined(GRAPH500_HEADER_) #define GRAPH500_HEADER_ #define NAME "Graph500 sequential list" #define VERSION 0 /** Pass the edge list to an external graph creation routine. */ int create_graph_from_edgelist (int64_t *IJ, int64_t nedge); /** Create the BFS tree from a given source vertex. */ int make_bfs_tree (int64_t *bfs_tree_out, int64_t *max_vtx_out, int64_t srcvtx); /** Clean up. */ void destroy_graph (void); #endif /* GRAPH500_HEADER_ */ CombBLAS_beta_16_2/graph500-1.2/options.c000644 000765 000024 00000013117 13212627400 021107 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #include "compat.h" #include #include #include #if !defined(__MTA__) #include #endif #include "graph500.h" #include "options.h" int VERBOSE = 0; int use_RMAT = 0; double A = A_PARAM; double B = B_PARAM; double C = C_PARAM; double D = 1.0 - (A_PARAM + B_PARAM + C_PARAM); int NBFS = NBFS_max; int64_t SCALE = default_SCALE; int64_t edgefactor = default_edgefactor; void get_options (int argc, char **argv) { extern int opterr; extern int optopt; extern int optind; extern char *optarg; int c, err = 0; int nset = 0; int whichset = 0; if (getenv ("VERBOSE")) VERBOSE = 1; while ((c = getopt (argc, argv, "v?hRs:e:A:a:B:b:C:c:D:d:V")) != -1) switch (c) { case 'v': printf ("%s version %d\n", NAME, VERSION); exit (EXIT_SUCCESS); break; case 'h': case '?': printf ("Options:\n" " v : version\n" " h|? : this message\n" " R : use R-MAT from SSCA2 (default: use Kronecker generator)\n" " s : R-MAT scale (default %" PRId64 ")\n" " e : R-MAT edge factor (default %" PRId64 ")\n" " A|a : R-MAT A (default %lg) >= 0\n" " B|b : R-MAT B (default %lg) >= 0\n" " C|c : R-MAT C (default %lg) >= 0\n" " D|d : R-MAT D (default %lg) >= 0\n" " Note: Setting 3 of A,B,C,D requires the arguments to sum to\n" " at most 1. Otherwise, the parameters are added and normalized\n" " so that the sum is 1.\n" " V : Enable extra (Verbose) output\n" "\n" "Outputs take the form of \"key: value\", with keys:\n" " SCALE\n" " edgefactor\n" " construction_time\n" " min_time\n" " firstquartile_time\n" " median_time\n" " thirdquartile_time\n" " max_time\n" " mean_time\n" " stddev_time\n" " min_nedge\n" " firstquartile_nedge\n" " median_nedge\n" " thirdquartile_nedge\n" " max_nedge\n" " mean_nedge\n" " stddev_nedge\n" " min_TEPS\n" " firstquartile_TEPS\n" " median_TEPS\n" " thirdquartile_TEPS\n" " max_TEPS\n" " harmonic_mean_TEPS\n" " harmonic_stddev_TEPS\n" , default_SCALE, default_edgefactor, A_PARAM, B_PARAM, C_PARAM, (1.0 - (A_PARAM + B_PARAM + C_PARAM)) ); exit (EXIT_SUCCESS); break; case 'V': VERBOSE = 1; break; case 'R': use_RMAT = 1; break; case 's': errno = 0; SCALE = strtol (optarg, NULL, 10); if (errno) { fprintf (stderr, "Error parsing scale %s\n", optarg); err = -1; } if (SCALE <= 0) { fprintf (stderr, "Scale must be non-negative.\n"); err = -1; } break; case 'e': errno = 0; edgefactor = strtol (optarg, NULL, 10); if (errno) { fprintf (stderr, "Error parsing edge factor %s\n", optarg); err = -1; } if (edgefactor <= 0) { fprintf (stderr, "Edge factor must be non-negative.\n"); err = -1; } break; case 'A': case 'a': errno = 0; A = strtod (optarg, NULL); if (whichset & 1) { fprintf (stderr, "A already set\n"); err = -1; } if (errno) { fprintf (stderr, "Error parsing A %s\n", optarg); err = -1; } if (A < 0) { fprintf (stderr, "A must be non-negative\n"); err = -1; } whichset |= 1; ++nset; break; case 'B': case 'b': errno = 0; B = strtod (optarg, NULL); if (whichset & 2) { fprintf (stderr, "B already set\n"); err = -1; } if (errno) { fprintf (stderr, "Error parsing B %s\n", optarg); err = -1; } if (B < 0) { fprintf (stderr, "B must be non-negative\n"); err = -1; } whichset |= 2; ++nset; break; case 'C': case 'c': errno = 0; C = strtod (optarg, NULL); if (whichset & 4) { fprintf (stderr, "C already set\n"); err = -1; } if (errno) { fprintf (stderr, "Error parsing C %s\n", optarg); err = -1; } if (C < 0) { fprintf (stderr, "C must be non-negative\n"); err = -1; } whichset |= 4; ++nset; break; case 'D': case 'd': errno = 0; D = strtod (optarg, NULL); if (whichset & 8) { fprintf (stderr, "D already set\n"); err = -1; } if (errno) { fprintf (stderr, "Error parsing D %s\n", optarg); err = -1; } if (D < 0) { fprintf (stderr, "D must be non-negative\n"); err = -1; } whichset |= 8; ++nset; break; default: fprintf (stderr, "Unrecognized option\n"); err = -1; } if (err) exit (EXIT_FAILURE); if (nset == 3) { switch (whichset) { case (4+2+1) : D = 1.0 - (A + B + C); break; case (8+2+1) : C = 1.0 - (A + B + D); break; case (8+4+1) : B = 1.0 - (A + C + D); break; case (8+4+2) : A = 1.0 - (B + C + D); break; default: fprintf (stderr, "Impossible combination of three bits...\n"); abort (); } if (A < 0 || B < 0 || C < 0 || D < 0) { fprintf (stderr, "When setting three R-MAT parameters, all must be < 1.\n" " A = %lg\n B = %lg\n C = %lg\n D = %lg\n", A, B, C, D); exit (EXIT_FAILURE); } } else if (nset > 0) { long double sum = A; sum += B; sum += C; sum += D; A /= sum; B /= sum; C /= sum; D = 1.0 - (A + B + C); } } CombBLAS_beta_16_2/graph500-1.2/kronecker.c000644 000765 000024 00000002505 13212627400 021376 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #include "compat.h" #include #include "xalloc.h" #include "prng.h" #include "rmat.h" #include "generator/splittable_mrg.h" #include "generator/permutation_gen.h" #include "generator/graph_generator.h" void kronecker_edgelist (int64_t *IJ_in, int64_t nedge, int64_t SCALE, double A, double B, double C) { const int64_t nvtx = 1L< #include #include #include #include #include static int64_t int64_fetch_add (int64_t* p, int64_t incr); static int64_t int64_casval(int64_t* p, int64_t oldval, int64_t newval); static int int64_cas(int64_t* p, int64_t oldval, int64_t newval); #include "../graph500.h" #include "../xalloc.h" #define MINVECT_SIZE 2 static int64_t maxvtx, nv, sz; static int64_t * restrict xoff; /* Length 2*nv+2 */ static int64_t * restrict xadjstore; /* Length MINVECT_SIZE + (xoff[nv] == nedge) */ static int64_t * restrict xadj; static void find_nv (const int64_t * restrict IJ, const int64_t nedge) { maxvtx = -1; OMP("omp parallel") { int64_t k, gmaxvtx, tmaxvtx = -1; OMP("omp for") for (k = 0; k < 2*nedge; ++k) if (IJ[k] > tmaxvtx) tmaxvtx = IJ[k]; gmaxvtx = maxvtx; while (tmaxvtx > gmaxvtx) gmaxvtx = int64_casval (&maxvtx, gmaxvtx, tmaxvtx); } nv = 1+maxvtx; } static int alloc_graph (int64_t nedge) { sz = (2*nv+2) * sizeof (*xoff); xoff = xmalloc_large_ext (sz); if (!xoff) return -1; return 0; } static void free_graph (void) { xfree_large (xadjstore); xfree_large (xoff); } #define XOFF(k) (xoff[2*(k)]) #define XENDOFF(k) (xoff[1+2*(k)]) static int64_t prefix_sum (int64_t *buf) { int nt, tid; int64_t slice_begin, slice_end, t1, t2, k; nt = omp_get_num_threads (); tid = omp_get_thread_num (); t1 = nv / nt; t2 = nv % nt; slice_begin = t1 * tid + (tid < t2? tid : t2); slice_end = t1 * (tid+1) + ((tid+1) < t2? (tid+1) : t2); buf[tid] = 0; for (k = slice_begin; k < slice_end; ++k) buf[tid] += XOFF(k); OMP("omp barrier"); OMP("omp single") for (k = 1; k < nt; ++k) buf[k] += buf[k-1]; if (tid) t1 = buf[tid-1]; else t1 = 0; for (k = slice_begin; k < slice_end; ++k) { int64_t tmp = XOFF(k); XOFF(k) = t1; t1 += tmp; } OMP("omp flush (xoff)"); OMP("omp barrier"); return buf[nt-1]; } static int setup_deg_off (const int64_t * restrict IJ, int64_t nedge) { int err = 0; int64_t *buf = NULL; xadj = NULL; OMP("omp parallel") { int64_t k, accum; OMP("omp for") for (k = 0; k < 2*nv+2; ++k) xoff[k] = 0; OMP("omp for") for (k = 0; k < 2*nedge; k+=2) if (IJ[k] != IJ[k+1]) { /* Skip self-edges. */ if (IJ[k] >= 0) OMP("omp atomic") ++XOFF(IJ[k]); if (IJ[k+1] >= 0) OMP("omp atomic") ++XOFF(IJ[k+1]); } OMP("omp single") { buf = alloca (omp_get_num_threads () * sizeof (*buf)); if (!buf) { perror ("alloca for prefix-sum hosed"); abort (); } } OMP("omp for") for (k = 0; k < nv; ++k) if (XOFF(k) < MINVECT_SIZE) XOFF(k) = MINVECT_SIZE; accum = prefix_sum (buf); OMP("omp for") for (k = 0; k < nv; ++k) XENDOFF(k) = XOFF(k); OMP("omp single") { XOFF(nv) = accum; if (!(xadjstore = xmalloc_large_ext ((XOFF(nv) + MINVECT_SIZE) * sizeof (*xadjstore)))) err = -1; if (!err) { xadj = &xadjstore[MINVECT_SIZE]; /* Cheat and permit xadj[-1] to work. */ for (k = 0; k < XOFF(nv) + MINVECT_SIZE; ++k) xadjstore[k] = -1; } } } return !xadj; } static void scatter_edge (const int64_t i, const int64_t j) { int64_t where; where = int64_fetch_add (&XENDOFF(i), 1); xadj[where] = j; } static int i64cmp (const void *a, const void *b) { const int64_t ia = *(const int64_t*)a; const int64_t ib = *(const int64_t*)b; if (ia < ib) return -1; if (ia > ib) return 1; return 0; } static void pack_vtx_edges (const int64_t i) { int64_t kcur, k; if (XOFF(i)+1 <= XENDOFF(i)) return; qsort (&xadj[XOFF(i)], XENDOFF(i)-XOFF(i), sizeof(*xadj), i64cmp); kcur = XOFF(i); for (k = XOFF(i)+1; k < XENDOFF(i); ++k) if (xadj[k] != xadj[kcur]) xadj[++kcur] = xadj[k]; ++kcur; for (k = kcur; k < XENDOFF(i); ++k) xadj[k] = -1; XENDOFF(i) = kcur; } static void pack_edges (void) { int64_t v; OMP("omp for") for (v = 0; v < nv; ++v) pack_vtx_edges (v); } static void gather_edges (const int64_t * restrict IJ, int64_t nedge) { OMP("omp parallel") { int64_t k; OMP("omp for") for (k = 0; k < 2*nedge; k += 2) if (IJ[k] >= 0 && IJ[k+1] >= 0 && IJ[k] != IJ[k+1]) { scatter_edge (IJ[k], IJ[k+1]); scatter_edge (IJ[k+1], IJ[k]); } pack_edges (); } } int create_graph_from_edgelist (int64_t *IJ, int64_t nedge) { find_nv (IJ, nedge); if (alloc_graph (nedge)) return -1; if (setup_deg_off (IJ, nedge)) { xfree_large (xoff); return -1; } gather_edges (IJ, nedge); return 0; } int make_bfs_tree (int64_t *bfs_tree_out, int64_t *max_vtx_out, int64_t srcvtx) { int64_t * restrict bfs_tree = bfs_tree_out; int err = 0; int64_t * restrict vlist = NULL; int64_t k1, k2; *max_vtx_out = maxvtx; vlist = xmalloc_large (nv * sizeof (vlist)); if (!vlist) return -1; vlist[0] = srcvtx; k1 = 0; k2 = 1; bfs_tree[srcvtx] = srcvtx; #define THREAD_BUF_LEN 16384 OMP("omp parallel shared(k1, k2)") { int64_t k; int64_t nbuf[THREAD_BUF_LEN]; OMP("omp for") for (k = 0; k < srcvtx; ++k) bfs_tree[k] = -1; OMP("omp for") for (k = srcvtx+1; k < nv; ++k) bfs_tree[k] = -1; while (k1 != k2) { const int64_t oldk2 = k2; int64_t kbuf = 0; OMP("omp barrier"); OMP("omp for") for (k = k1; k < oldk2; ++k) { const int64_t v = vlist[k]; const int64_t veo = XENDOFF(v); int64_t vo; for (vo = XOFF(v); vo < veo; ++vo) { const int64_t j = xadj[vo]; if (bfs_tree[j] == -1) { if (int64_cas (&bfs_tree[j], -1, v)) { if (kbuf < THREAD_BUF_LEN) { nbuf[kbuf++] = j; } else { int64_t voff = int64_fetch_add (&k2, THREAD_BUF_LEN), vk; assert (voff + THREAD_BUF_LEN <= nv); for (vk = 0; vk < THREAD_BUF_LEN; ++vk) vlist[voff + vk] = nbuf[vk]; nbuf[0] = j; kbuf = 1; } } } } } if (kbuf) { int64_t voff = int64_fetch_add (&k2, kbuf), vk; assert (voff + kbuf <= nv); for (vk = 0; vk < kbuf; ++vk) vlist[voff + vk] = nbuf[vk]; } OMP("omp single") k1 = oldk2; OMP("omp barrier"); } } xfree_large (vlist); return err; } void destroy_graph (void) { free_graph (); } #if defined(_OPENMP) #if defined(__GNUC__)||defined(__INTEL_COMPILER) int64_t int64_fetch_add (int64_t* p, int64_t incr) { return __sync_fetch_and_add (p, incr); } int64_t int64_casval(int64_t* p, int64_t oldval, int64_t newval) { return __sync_val_compare_and_swap (p, oldval, newval); } int int64_cas(int64_t* p, int64_t oldval, int64_t newval) { return __sync_bool_compare_and_swap (p, oldval, newval); } #else /* XXX: These are not correct, but suffice for the above uses. */ int64_t int64_fetch_add (int64_t* p, int64_t incr) { OMP("omp critical") { int64_t t = *p; *p += incr; } OMP("omp flush (p)"); return t; } int64_t int64_casval(int64_t* p, int64_t oldval, int64_t newval) { int64_t v; OMP("omp critical (CAS)") { v = *p; if (v == oldval) *p = newval; } OMP("omp flush (p)"); return v; } int int64_cas(int64_t* p, int64_t oldval, int64_t newval) { int out = 0; OMP("omp critical (CAS)") { int64_t v = *p; if (v == oldval) { *p = newval; out = 1; } } OMP("omp flush (p)"); return out; } #endif #else int64_t int64_fetch_add (int64_t* p, int64_t incr) { int64_t t = *p; *p += incr; return t; } int64_t int64_casval(int64_t* p, int64_t oldval, int64_t newval) { int64_t v = *p; if (v == oldval) *p = newval; return v; } int int64_cas(int64_t* p, int64_t oldval, int64_t newval) { int64_t v = *p; int out = 0; if (v == oldval) { *p = newval; out = 1; } return out; } #endif CombBLAS_beta_16_2/graph500-1.2/make-incs/make.inc-xmt000644 000765 000024 00000000474 13212627400 023337 0ustar00aydinbulucstaff000000 000000 # -*- Makefile -*- # Copyright 2010, Georgia Institute of Technology, USA. # See COPYING for license. # Modules and Emacs+tramp don't cooperate at PNNL. #PATH:=/opt/mta-pe/6.5.0/bin:${PATH} CFLAGS = LDLIBS = -lprand -lm -lrt CPPFLAGS = -DUSE_MMAP_LARGE -DNDEBUG BUILD_OPENMP = No CFLAGS_OPENMP = BUILD_XMT = Yes CombBLAS_beta_16_2/graph500-1.2/make-incs/make.inc-gcc000644 000765 000024 00000000567 13212627400 023266 0ustar00aydinbulucstaff000000 000000 # -*- Makefile -*- # Copyright 2010, Georgia Institute of Technology, USA. # See COPYING for license. CFLAGS = -g -std=c99 #CFLAGS = -g -std=c99 -O3 -march=native -fgcse-sm -fgcse-las -fgcse-after-reload -floop-strip-mine -ftree-loop-im -fivopts -funswitch-loops LDLIBS = -lm -lrt CPPFLAGS = -DUSE_MMAP_LARGE -DUSE_MMAP_LARGE_EXT BUILD_OPENMP = Yes CFLAGS_OPENMP = -fopenmp CombBLAS_beta_16_2/graph500-1.2/octave/validate.m000644 000765 000024 00000001623 13212627400 022477 0ustar00aydinbulucstaff000000 000000 function out = validate (parent, ij, search_key) out = 1; parent = parent + 1; search_key = search_key + 1; if parent (search_key) != search_key, out = 0; return; end ij = ij + 1; N = max (max (ij)); slice = find (parent > 0); level = zeros (size (parent)); level (slice) = 1; P = parent (slice); mask = P != search_key; k = 0; while any (mask), level(slice(mask)) = level(slice(mask)) + 1; P = parent (P); mask = P != search_key; k = k + 1; if k > N, %% There must be a cycle in the tree. out = -3; return; end end lij = level (ij); neither_in = lij(1,:) == 0 & lij(2,:) == 0; both_in = lij(1,:) > 0 & lij(2,:) > 0; if any (not (neither_in | both_in)), out = -4; return end respects_tree_level = abs (lij(1,:) - lij(2,:)) <= 1; if any (not (neither_in | respects_tree_level)), out = -5; return end CombBLAS_beta_16_2/graph500-1.2/octave/kronecker_generator.m000644 000765 000024 00000002403 13212627400 024734 0ustar00aydinbulucstaff000000 000000 function ij = kronecker_generator (SCALE, edgefactor) %% Generate an edgelist according to the Graph500 %% parameters. In this sample, the edge list is %% returned in an array with two rows, where StartVertex %% is first row and EndVertex is the second. The vertex %% labels start at zero. %% %% Example, creating a sparse matrix for viewing: %% ij = kronecker_generator (10, 16); %% G = sparse (ij(1,:)+1, ij(2,:)+1, ones (1, size (ij, 2))); %% spy (G); %% The spy plot should appear fairly dense. Any locality %% is removed by the final permutations. %% Set number of vertices. N = 2^SCALE; %% Set number of edges. M = edgefactor * N; %% Set initiator probabilities. [A, B, C] = deal (0.57, 0.19, 0.19); %% Create index arrays. ij = ones (2, M); %% Loop over each order of bit. ab = A + B; c_norm = C/(1 - (A + B)); a_norm = A/(A + B); for ib = 1:SCALE, %% Compare with probabilities and set bits of indices. ii_bit = rand (1, M) > ab; jj_bit = rand (1, M) > ( c_norm * ii_bit + a_norm * not (ii_bit) ); ij = ij + 2^(ib-1) * [ii_bit; jj_bit]; end %% Permute vertex labels p = randperm (N); ij = p(ij); %% Permute the edge list p = randperm (M); ij = ij(:, p); %% Adjust to zero-based labels. ij = ij - 1; CombBLAS_beta_16_2/graph500-1.2/octave/Graph500.m000644 000765 000024 00000001667 13212627400 022204 0ustar00aydinbulucstaff000000 000000 SCALE = 10; edgefactor = 16; NBFS = 64; rand ("seed", 103); ij = kronecker_generator (SCALE, edgefactor); tic; G = kernel_1 (ij); kernel_1_time = toc; N = size (G, 1); coldeg = full (spstats (G)); search_key = randperm (N); search_key(coldeg(search_key) == 0) = []; if length (search_key) > NBFS, search_key = search_key(1:NBFS); else NBFS = length (search_key); end search_key = search_key - 1; kernel_2_time = Inf * ones (NBFS, 1); kernel_2_nedge = zeros (NBFS, 1); indeg = histc (ij(:), 1:N); % For computing the number of edges for k = 1:NBFS, tic; parent = kernel_2 (G, search_key(k)); kernel_2_time(k) = toc; err = validate (parent, ij, search_key (k)); if err <= 0, error (sprintf ("BFS %d from search key %d failed to validate: %d", k, search_key(k), err)); end kernel_2_nedge(k) = sum (indeg(parent >= 0))/2; % Volume/2 end output (SCALE, edgefactor, NBFS, kernel_1_time, kernel_2_time, kernel_2_nedge); CombBLAS_beta_16_2/graph500-1.2/octave/kernel_2.m000644 000765 000024 00000001077 13212627400 022412 0ustar00aydinbulucstaff000000 000000 function parent = kernel_2 (G, root) %% Compute a sparse adjacency matrix representation %% of the graph with edges from ij. N = size (G, 1); %% Adjust from zero labels. root = root + 1; parent = zeros (N, 1); parent (root) = root; vlist = zeros (N, 1); vlist(1) = root; lastk = 1; for k = 1:N, v = vlist(k); if v == 0, break; end [I,J,V] = find (G(:, v)); nxt = I(parent(I) == 0); parent(nxt) = v; vlist(lastk + (1:length (nxt))) = nxt; lastk = lastk + length (nxt); end %% Adjust to zero labels. parent = parent - 1; CombBLAS_beta_16_2/graph500-1.2/octave/kernel_1.m000644 000765 000024 00000000722 13212627400 022405 0ustar00aydinbulucstaff000000 000000 function G = kernel_1 (ij) %% Compute a sparse adjacency matrix representation %% of the graph with edges from ij. %% Remove self-edges. ij(:, ij(1,:) == ij(2,:)) = []; %% Adjust away from zero labels. ij = ij + 1; %% Find the maximum label for sizing. N = max (max (ij)); %% Create the matrix, ensuring it is square. G = sparse (ij(1,:), ij(2,:), ones (1, size (ij, 2)), N, N); %% Symmetrize to model an undirected graph. G = spones (G + G.'); CombBLAS_beta_16_2/graph500-1.2/octave/output.m000644 000765 000024 00000003275 13212627400 022253 0ustar00aydinbulucstaff000000 000000 function output (SCALE, edgefactor, NBFS, kernel_1_time, kernel_2_time, kernel_2_nedge) printf ("SCALE: %d\n", SCALE); printf ("edgefactor: %d\n", edgefactor); printf ("NBFS: %d\n", NBFS); printf ("construction_time: %20.17e\n", kernel_1_time); S = statistics (kernel_2_time); printf ("min_time: %20.17e\n", S(1)); printf ("firstquartile_time: %20.17e\n", S(2)); printf ("median_time: %20.17e\n", S(3)); printf ("thirdquartile_time: %20.17e\n", S(4)); printf ("max_time: %20.17e\n", S(5)); printf ("mean_time: %20.17e\n", S(6)); printf ("stddev_time: %20.17e\n", S(7)); S = statistics (kernel_2_nedge); printf ("min_nedge: %20.17e\n", S(1)); printf ("firstquartile_nedge: %20.17e\n", S(2)); printf ("median_nedge: %20.17e\n", S(3)); printf ("thirdquartile_nedge: %20.17e\n", S(4)); printf ("max_nedge: %20.17e\n", S(5)); printf ("mean_nedge: %20.17e\n", S(6)); printf ("stddev_nedge: %20.17e\n", S(7)); TEPS = kernel_2_nedge ./ kernel_2_time; N = length (TEPS); S = statistics (TEPS); S(6) = mean (TEPS, 'h'); %% Harmonic standard deviation from: %% Nilan Norris, The Standard Errors of the Geometric and Harmonic %% Means and Their Application to Index Numbers, 1940. %% http://www.jstor.org/stable/2235723 tmp = zeros (N, 1); tmp(TEPS > 0) = 1./TEPS(TEPS > 0); tmp = tmp - 1/S(6); S(7) = (sqrt (sum (tmp.^2)) / (N-1)) * S(6)^2; printf ("min_TEPS: %20.17e\n", S(1)); printf ("firstquartile_TEPS: %20.17e\n", S(2)); printf ("median_TEPS: %20.17e\n", S(3)); printf ("thirdquartile_TEPS: %20.17e\n", S(4)); printf ("max_TEPS: %20.17e\n", S(5)); printf ("harmonic_mean_TEPS: %20.17e\n", S(6)); printf ("harmonic_stddev_TEPS: %20.17e\n", S(7)); CombBLAS_beta_16_2/graph500-1.2/seq-list/seq-list.c000644 000765 000024 00000004713 13212627400 022720 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #define _FILE_OFFSET_BITS 64 #define _THREAD_SAFE #include #include #include #include #include "../compat.h" #include "../graph500.h" static int64_t maxvtx, maxdeg, nIJ; static const int64_t * restrict IJ; static int64_t * restrict head, * restrict deg, * restrict next; int create_graph_from_edgelist (int64_t *IJ_in, int64_t nedge) { int err = 0; IJ = IJ_in; nIJ = nedge; maxvtx = -1; maxdeg = -1; int64_t k; for (k = 0; k < 2*nedge; ++k) if (IJ[k] > maxvtx) maxvtx = IJ[k]; head = malloc ((2*(maxvtx+1) + 2*nIJ) * sizeof (int64_t)); if (!head) return -1; deg = &head[maxvtx+1]; next = °[maxvtx+1]; for (k = 0; k <= maxvtx; ++k) { head[k] = -1; deg[k] = 0; } for (k = 0; k < nedge; ++k) { const int64_t i = IJ[2*k]; const int64_t j = IJ[2*k+1]; int64_t t_head, t; if (i >= 0 && j >= 0 && i != j) { next[2*k] = -1; next[1+2*k] = -1; t = 2*k+1; /* Point at the *other* end. */ t_head = head[i]; head[i] = t; assert (t_head < 2*nIJ); next[t] = t_head; ++deg[i]; --t; t_head = head[j]; head[j] = t; assert (t_head < 2*nIJ); next[t] = t_head; ++deg[j]; } } for (int64_t kg = 0; kg <= maxvtx; ++kg) if (deg[kg] > maxdeg) maxdeg = deg[kg]; return err; } int make_bfs_tree (int64_t *bfs_tree_out, int64_t *max_vtx_out, int64_t srcvtx) { int64_t * restrict bfs_tree = bfs_tree_out; int err = 0; const int64_t nv = maxvtx+1; int64_t k, k1, k2, newk2; int64_t * restrict vlist; *max_vtx_out = maxvtx; bfs_tree[srcvtx] = srcvtx; newk2 = 1; vlist = malloc (nv * sizeof (*vlist)); if (!vlist) return -1; bfs_tree[srcvtx] = srcvtx; k1 = 0; k2 = 1; vlist[0] = srcvtx; for (k = 0; k < srcvtx; ++k) bfs_tree[k] = -1; for (k = srcvtx+1; k < nv; ++k) bfs_tree[k] = -1; while (k1 != k2) { int64_t k, newk2 = k2; for (k = k1; k < k2; ++k) { const int64_t parent = vlist[k]; int64_t p = head[parent]; while (p >= 0) { const int64_t newv = IJ[p]; if (bfs_tree[newv] < 0) { bfs_tree[newv] = parent; vlist[newk2++] = newv; } p = next[p]; } k1 = k2; k2 = newk2; } } free (vlist); return err; } void destroy_graph (void) { free (head); } CombBLAS_beta_16_2/graph500-1.2/generator/CMakeLists.txt000644 000765 000024 00000001335 13271404146 024002 0ustar00aydinbulucstaff000000 000000 # Top level directory has the include files ADD_LIBRARY( GraphGenlib btrd_binomial_distribution.c splittable_mrg.c mrg_transitions.c graph_generator.c permutation_gen.c make_graph.c utils.c scramble_edges.c) target_include_directories(GraphGenlib PUBLIC $ $) target_include_directories(GraphGenlib PRIVATE include/graph500/generator) if(CMAKE_C_COMPILER_ID STREQUAL "Intel") target_compile_options(GraphGenlib PRIVATE "-restrict") endif() install(DIRECTORY include/ DESTINATION include) install(TARGETS GraphGenlib EXPORT CombBLASTargets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin INCLUDES DESTINATION include ) CombBLAS_beta_16_2/graph500-1.2/generator/._.DS_Store000644 000765 000024 00000000170 13271513361 023136 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/graph500-1.2/generator/.DS_Store000644 000765 000024 00000020004 13271513361 022717 0ustar00aydinbulucstaff000000 000000 Bud1 udebwspbl  @€ @€ @€ @includebwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šincludelsvCblob’bplist00Ø HIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates « "&+05:>CÔ   WvisibleUwidthYascendingZidentifier , TnameÔWvisibleUwidthYascending#XubiquityÔ  ! µ\dateModifiedÔ %[dateCreatedÔ '( Tsizea Ô ,- Tkinds Ô 12 Ulabeld Ô 67 WversionK Ô ; Xcomments Ô @BÈ^dateLastOpenedÔDYdateAdded#@(Tname#@0 .@H\epyŒŽ›¤¬²¼ÇÈËÌÑÚâèòóõöÿ   "#$09>@ABKPRST]cefgpxz{|…Ž™šœ¬µ¿ÀÁÂËÐÙLÚincludelsvpblobYbplist00Ø DEF _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates Ù #(-27<@Xcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ WvisibleUwidthYascendingUindex, Ô ÈÔ$%µÔ *, aÔ/ 1d Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©includevSrnlong E DSDB `€ @€ @€ @Ô *, aÔ/ 1d Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©includevSrnlongCombBLAS_beta_16_2/graph500-1.2/generator/Makefile.omp000644 000765 000024 00000001537 13212627400 023473 0ustar00aydinbulucstaff000000 000000 AR = ar RANLIB = ranlib CC = cc -fopenmp CFLAGS = -g -Wall -Drestrict=__restrict__ -O3 -DNDEBUG -ffast-math -DGRAPH_GENERATOR_OMP # -g -pg # CFLAGS = -g -Wall -Drestrict=__restrict__ LDFLAGS = -g # -g -pg all: libgraph_generator_omp.a generator_test_omp generator_test_omp: generator_test_omp.c libgraph_generator_omp.a $(CC) $(CFLAGS) $(LDFLAGS) -o generator_test_omp generator_test_omp.c -L. -lgraph_generator_omp -lm libgraph_generator_omp.a: btrd_binomial_distribution.o splittable_mrg.o mrg_transitions.o graph_generator.o permutation_gen.o make_graph.o utils.o scramble_edges.o $(AR) cruv libgraph_generator_omp.a btrd_binomial_distribution.o splittable_mrg.o mrg_transitions.o graph_generator.o permutation_gen.o make_graph.o utils.o scramble_edges.o $(RANLIB) libgraph_generator_omp.a clean: -rm -f generator_test_omp libgraph_generator_omp.a *.o CombBLAS_beta_16_2/graph500-1.2/generator/btrd_binomial_distribution.c000644 000765 000024 00000012272 13212627400 027007 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include "btrd_binomial_distribution.h" #include #include #include "splittable_mrg.h" static double f_c(unsigned int k) { static const double one_over_12 = 1./12; static const double one_over_360 = 1./360; static const double one_over_1260 = 1./1260; static const double values[] = { /* Constants for f_c(0) to f_c(9) */ .08106146679532726, .04134069595540929, .02767792568499834, .02079067210376509, .01664469118982119, .01387612882307075, .01189670994589177, .01041126526197209, .009255462182712733, .008330563433362871 }; if (k <= 9) { return values[k]; } else { double recip_k_plus_1 = 1. / (k + 1); return (one_over_12 - (one_over_360 - one_over_1260 * recip_k_plus_1 * recip_k_plus_1) * recip_k_plus_1 * recip_k_plus_1) * recip_k_plus_1; } } #ifdef __MTA__ #pragma mta expect parallel context #pragma mta serial #endif size_t btrd_binomial_distribution(size_t n_orig, double p, mrg_state* state) { /* BTRD algorithm from pages 6--7 of "The Generation of Binomial Random * Variates" (Wolfgang Hoermann) -- * http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.47.8407 */ if (p == 0.) return 0; if (p > .5) return n_orig - btrd_binomial_distribution(n_orig, 1. - p, state); if (n_orig * p < 10) { /* First waiting time algorithm from page 525 of * http://cg.scs.carleton.ca/~luc/chapter_ten.pdf */ int x = 0; int sum = 0; double recip_log_1_minus_p; /* Approximation to ln(1-p) from second sequence in exercise 1 of * http://cg.scs.carleton.ca/~luc/chapter_ten.pdf page 500; done to avoid * use of C99 log1p function. */ { double r = 1. - 2. / p; double recip_r = 1. / r; double recip_r2 = recip_r * recip_r; double recip_r4 = recip_r2 * recip_r2; double recip_r6 = recip_r4 * recip_r2; double log_1_minus_p = 2. * recip_r * (1 + recip_r2 / 3. + recip_r4 / 5. + recip_r6 / 7.); recip_log_1_minus_p = 1. / log_1_minus_p; } #if 0 /* Old version using C99 log1p: */ recip_log_1_minus_p = 1. / log1p(-p); /* 1 / ln(1 - p) */ #endif do { /* From page 500 of http://cg.scs.carleton.ca/~luc/chapter_ten.pdf * (geometric variate generator). */ sum += (int)ceil(log(mrg_get_double_orig(state)) * recip_log_1_minus_p); ++x; } while (sum <= n_orig); return x - 1; } if (n_orig > 1000000000) { return btrd_binomial_distribution(1000000000, p, state) + btrd_binomial_distribution(n_orig - 1000000000, p, state); } { int n = (int)n_orig; /* Setup */ int m = (int)floor((n + 1) * p); double r = p / (1. - p); double nr = (n + 1) * r; double npq = n * p * (1. - p); double sqrt_npq = sqrt(npq); double b = 1.15 + 2.53 * sqrt_npq; double a = -.0873 + .0248 * b + .01 * p; double c = n * p + .5; double alpha = (2.83 + 5.1 / b) * sqrt_npq; double v_r = .92 - 4.2 / b; double u_rv_r = .86 * v_r; while (1) { /* 1 */ double v = mrg_get_double_orig(state); double u, us, temp_random_num; int k, km; if (v <= u_rv_r) { u = v / v_r - .43; return (int)floor((2 * a / (.5 + fabs(u)) + b) * u + c); } /* 2 */ temp_random_num = mrg_get_double_orig(state); if (v >= v_r) { u = temp_random_num - .5; } else { u = v / v_r - .93; u = (u > 0. ? 1. : u < 0. ? -1. : 0.) * .5 - u; v = v_r * temp_random_num; } /* 3.0 */ us = .5 - fabs(u); k = (int)floor((2 * a / us + b) * u + c); if (k < 0 || k > n) continue; v *= alpha / (a / (us * us) + b); km = (k >= m ? k - m : m - k); if (km > 15) { /* 3.2 */ double rho = (km / npq) * (((km / 3 + .625) * km + 1. / 6) / npq + .5); double t = -km * km / (2 * npq); int nm, nk; double h, threshold; v = log(v); if (v < t - rho) return k; if (v > t + rho) continue; /* 3.3 */ nm = n - m + 1; h = (m + .5) * log((m + 1) / (r * nm)) + f_c(m) + f_c(n - m); /* 3.4 */ nk = n - k + 1; threshold = h + (n + 1) * log((double)nm / nk) + (k + .5) * log(nk * r / (k + 1)) - f_c(k) - f_c(n - k); if (v <= threshold) return k; } else { /* 3.1 */ double f = 1.; int i; if (m < k) { for (i = m; i != k; ++i) f *= nr / i - r; } else if (m > k) { for (i = k; i != m; ++i) v *= nr / i + r; } if (v <= f) return k; } } } } CombBLAS_beta_16_2/graph500-1.2/generator/Makefile000755 000765 000024 00000000000 13220066202 025107 2Makefile.sequstar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/generator/permutation_gen.c000644 000765 000024 00000037124 13212627400 024606 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #include "splittable_mrg.h" #include "graph_generator.h" #include "permutation_gen.h" #include "utils.h" #include #include #include #include #include #ifdef __MTA__ #include #endif #ifdef GRAPH_GENERATOR_MPI #include #endif #ifdef GRAPH_GENERATOR_OMP #include #endif typedef struct slot_data { int64_t index, value; } slot_data; /* This code defines a simple closed-indexing hash table. It is used to speed * up the rand_sort algorithm given below. Elements with -1 as index are * unused; others are used. */ #ifdef __MTA__ #pragma mta inline #endif static inline void hashtable_insert(slot_data* ht, int64_t ht_size, int64_t index, int64_t value, int64_t hashval) { int64_t i; for (i = hashval; i < ht_size; ++i) { if (int64_t_cas(&ht[i].index, (int64_t)(-1), index)) { ht[i].value = value; return; } } for (i = 0; i < hashval; ++i) { if (int64_t_cas(&ht[i].index, (int64_t)(-1), index)) { ht[i].value = value; return; } } assert (!"Should not happen: overflow in hash table"); } #ifdef __MTA__ #pragma mta inline #endif static inline int hashtable_count_key(const slot_data* ht, int64_t ht_size, int64_t index, int64_t hashval) { int c = 0; int64_t i; for (i = hashval; i < ht_size && ht[i].index != (int64_t)(-1); ++i) { if (ht[i].index == index) ++c; } if (i == ht_size) { for (i = 0; i < hashval && ht[i].index != (int64_t)(-1); ++i) { if (ht[i].index == index) ++c; } } return c; } /* Return all values with the given index value into result array; return value * of function is element count. */ #ifdef __MTA__ #pragma mta inline #endif static inline int hashtable_get_values(const slot_data* ht, int64_t ht_size, int64_t index, int64_t hashval, int64_t* result) { int x = 0; int64_t i; for (i = hashval; i < ht_size && ht[i].index != (int64_t)(-1); ++i) { if (ht[i].index == index) { result[x++] = ht[i].value; } } if (i == ht_size) { for (i = 0; i < hashval && ht[i].index != (int64_t)(-1); ++i) { if (ht[i].index == index) { result[x++] = ht[i].value; } } } return x; } #ifdef __MTA__ #pragma mta inline #endif static inline void selection_sort(int64_t* a, int64_t n) { int64_t i, j; if (n <= 1) return; for (i = 0; i + 1 < n; ++i) { int64_t minpos = i; for (j = i + 1; j < n; ++j) { if (a[j] < a[minpos]) minpos = j; } if (minpos != i) { int64_t t = a[minpos]; a[minpos] = a[i]; a[i] = t; } } } /* Fisher-Yates shuffle */ #ifdef __MTA__ #pragma mta inline #endif static inline void randomly_permute(int64_t* a, int64_t n, mrg_state* st) { int64_t i, j; if (n <= 1) return; for (i = n - 1; i > 0; --i) { j = random_up_to(st, i + 1); if (i != j) { int64_t t = a[i]; a[i] = a[j]; a[j] = t; } } } /* Exclusive prefix sum on ints; returns sum of overall input array */ static inline int int_prefix_sum(int* out, const int* in, size_t n) { size_t i; if (n == 0) return 0; out[0] = 0; for (i = 1; i < n; ++i) out[i] = out[i - 1] + in[i - 1]; return out[n - 1] + in[n - 1]; } /* A variant of the rand_sort algorithm from Cong and Bader ("An Empirical * Analysis of Parallel Random Permutation Algorithms on SMPs", Georgia Tech TR * GT-CSE-06-06.pdf, * ). * Sorting here is done using a hash table to effectively act as a bucket sort. * The rand_sort algorithm was chosen instead of the other algorithms in order * to get reproducibility across architectures and processor counts. That is * also the reason for the extra sort immediately before scrambling all * elements with the same key, as well as the expensive PRNG operations. */ /* This version is for sequential machines, OpenMP, and the XMT. */ void rand_sort_shared(mrg_state* st, int64_t n, int64_t* result /* Array of size n */) { int64_t hash_table_size = 2 * n + 128; /* Must be >n, preferably larger for performance */ slot_data* ht = (slot_data*)xmalloc(hash_table_size * sizeof(slot_data)); int64_t i; int64_t index; int64_t* bucket_counts; int64_t* bucket_starts_in_result; int64_t running_sum; int64_t old_running_sum; int64_t result_start_idx; int64_t* temp; int64_t bi; mrg_state new_st; #ifdef __MTA__ #pragma mta block schedule #endif #ifdef GRAPH_GENERATOR_OMP #pragma omp parallel for #endif for (i = 0; i < hash_table_size; ++i) ht[i].index = (int64_t)(-1); /* Unused */ #ifdef __MTA__ #pragma mta assert parallel #pragma mta block schedule #endif #ifdef GRAPH_GENERATOR_OMP #pragma omp parallel for #endif /* Put elements into the hash table with random keys. */ for (i = 0; i < n; ++i) { mrg_state new_st = *st; mrg_skip(&new_st, 1, i, 0); index = (int64_t)random_up_to(&new_st, hash_table_size); hashtable_insert(ht, hash_table_size, index, i, index); } /* Count elements with each key in order to sort them by key. */ bucket_counts = (int64_t*)xcalloc(hash_table_size, sizeof(int64_t)); /* Uses zero-initialization */ #ifdef __MTA__ #pragma mta assert parallel #pragma mta block schedule #endif #ifdef GRAPH_GENERATOR_OMP #pragma omp parallel for #endif for (i = 0; i < hash_table_size; ++i) { /* Count all elements with same index. */ bucket_counts[i] = hashtable_count_key(ht, hash_table_size, i, i); } /* bucket_counts replaced by its prefix sum (start of each bucket in output array) */ bucket_starts_in_result = bucket_counts; running_sum = 0; #ifdef __MTA__ #pragma mta block schedule #endif /* FIXME: parallelize this on OpenMP */ for (i = 0; i < hash_table_size; ++i) { old_running_sum = running_sum; running_sum += bucket_counts[i]; bucket_counts[i] = old_running_sum; } assert (running_sum == n); bucket_counts = NULL; #ifdef __MTA__ #pragma mta assert parallel #pragma mta block schedule #endif #ifdef GRAPH_GENERATOR_OMP #pragma omp parallel for #endif for (i = 0; i < hash_table_size; ++i) { result_start_idx = bucket_starts_in_result[i]; temp = result + result_start_idx; /* Gather up all elements with same key. */ bi = (int64_t)hashtable_get_values(ht, hash_table_size, i, i, temp); if (bi > 1) { /* Selection sort them (for consistency in parallel implementations). */ selection_sort(temp, bi); /* Randomly permute them. */ new_st = *st; mrg_skip(&new_st, 1, i, 100); randomly_permute(temp, bi, &new_st); } } free(ht); ht = NULL; free(bucket_starts_in_result); bucket_starts_in_result = NULL; } #ifdef GRAPH_GENERATOR_MPI void rand_sort_mpi(MPI_Comm comm, mrg_state* st, int64_t n, int64_t* result_size_ptr, int64_t** result_ptr /* Allocated using xmalloc() by rand_sort_mpi */) { int size, rank; MPI_Comm_size(comm, &size); MPI_Comm_rank(comm, &rank); /* Make MPI data type for slot_data. */ MPI_Datatype slot_data_type; { int blocklens[] = {1, 1}; MPI_Aint temp_base, indices[2]; slot_data temp; MPI_Get_address(&temp, &temp_base); MPI_Get_address(&temp.index, &indices[0]); MPI_Get_address(&temp.value, &indices[1]); indices[0] -= temp_base; indices[1] -= temp_base; MPI_Datatype old_types[] = {INT64_T_MPI_TYPE, INT64_T_MPI_TYPE}; MPI_Type_struct(2, blocklens, indices, old_types, &slot_data_type); MPI_Type_commit(&slot_data_type); } int64_t total_hash_table_size = 2 * n + 128; /* Must be >n, preferably larger for performance */ /* Hash table is distributed by blocks: first (total_hash_table_size % size) * are of size (total_hash_table_size / size + 1), rest are of size * (total_hash_table_size / size). This distribution is necessary so that * the permutation can easily be assembled at the end of the function. */ int64_t ht_base_block_size = total_hash_table_size / size; int ht_block_size_cutoff_rank = total_hash_table_size % size; int64_t ht_block_size_cutoff_index = ht_block_size_cutoff_rank * (ht_base_block_size + 1); int64_t ht_my_size = ht_base_block_size + (rank < ht_block_size_cutoff_rank); int64_t ht_my_start = (rank < ht_block_size_cutoff_rank) ? rank * (ht_base_block_size + 1) : ht_block_size_cutoff_index + (rank - ht_block_size_cutoff_rank) * ht_base_block_size; int64_t ht_my_end = ht_my_start + ht_my_size; #define HT_OWNER(e) \ (((e) < ht_block_size_cutoff_index) ? \ (e) / (ht_base_block_size + 1) : \ ht_block_size_cutoff_rank + ((e) - ht_block_size_cutoff_index) / ht_base_block_size) #define HT_LOCAL(e) ((e) - ht_my_start) /* Input elements to scramble are distributed cyclically for simplicity; * their distribution does not matter. */ int64_t elt_my_size = (n / size) + (rank < n % size); int64_t i; /* Cache the key-value pairs to avoid PRNG skip operations. Count the number * of pairs going to each destination processor. */ slot_data* kv_pairs = (slot_data*)xmalloc(elt_my_size * sizeof(slot_data)); int* outcounts = (int*)xcalloc(size, sizeof(int)); /* Relies on zero-init */ for (i = 0; i < elt_my_size; ++i) { mrg_state new_st = *st; mrg_skip(&new_st, 1, i * size + rank, 0); int64_t index = (int64_t)random_up_to(&new_st, total_hash_table_size); int64_t owner = HT_OWNER(index); assert (owner >= 0 && owner < size); ++outcounts[owner]; kv_pairs[i].index = index; kv_pairs[i].value = i * size + rank; } int* outdispls = (int*)xmalloc(size * sizeof(int)); int total_outcount = int_prefix_sum(outdispls, outcounts, size); slot_data* outdata = (slot_data*)xmalloc(total_outcount * sizeof(slot_data)); int* outoffsets = (int*)xmalloc(size * sizeof(int)); memcpy(outoffsets, outdispls, size * sizeof(int)); /* Put the key-value pairs into the output buffer, sorted by destination, to * get ready for MPI_Alltoallv. */ for (i = 0; i < elt_my_size; ++i) { int64_t index = kv_pairs[i].index; int64_t owner = HT_OWNER(index); outdata[outoffsets[owner]] = kv_pairs[i]; ++outoffsets[owner]; } free(kv_pairs); kv_pairs = NULL; for (i = 0; i < size; ++i) { assert (outoffsets[i] == outdispls[i] + outcounts[i]); } free(outoffsets); outoffsets = NULL; int* incounts = (int*)xmalloc(size * sizeof(int)); /* Send data counts. */ MPI_Alltoall(outcounts, 1, MPI_INT, incounts, 1, MPI_INT, comm); int* indispls = (int*)xmalloc(size * sizeof(int)); int total_incount = int_prefix_sum(indispls, incounts, size); slot_data* indata = (slot_data*)xmalloc(total_incount * sizeof(slot_data)); /* Send data to put into hash table. */ MPI_Alltoallv(outdata, outcounts, outdispls, slot_data_type, indata, incounts, indispls, slot_data_type, comm); free(outdata); outdata = NULL; free(outcounts); outcounts = NULL; free(outdispls); outdispls = NULL; free(incounts); incounts = NULL; free(indispls); indispls = NULL; MPI_Type_free(&slot_data_type); /* Create the local part of the hash table. */ slot_data* ht = (slot_data*)xmalloc(ht_my_size * sizeof(slot_data)); for (i = ht_my_start; i < ht_my_end; ++i) { ht[HT_LOCAL(i)].index = (int64_t)(-1); /* Unused */ } for (i = 0; i < total_incount; ++i) { int64_t index = indata[i].index, value = indata[i].value; assert (HT_OWNER(index) == rank); hashtable_insert(ht, ht_my_size, index, value, HT_LOCAL(index)); } free(indata); indata = NULL; /* Make the local part of the result. Most of the rest of this code is * similar to the shared-memory/XMT version above. */ int64_t* result = (int64_t*)xmalloc(total_incount * sizeof(int64_t)); *result_ptr = result; *result_size_ptr = total_incount; int64_t* bucket_counts = (int64_t*)xmalloc(ht_my_size * sizeof(int64_t)); for (i = ht_my_start; i < ht_my_end; ++i) { /* Count all elements with same index. */ bucket_counts[HT_LOCAL(i)] = hashtable_count_key(ht, ht_my_size, i, HT_LOCAL(i)); } /* bucket_counts replaced by its prefix sum (start of each bucket in output array) */ int64_t* bucket_starts_in_result = bucket_counts; int64_t running_sum = 0; for (i = 0; i < ht_my_size; ++i) { int64_t old_running_sum = running_sum; running_sum += bucket_counts[i]; bucket_counts[i] = old_running_sum; } assert (running_sum == total_incount); bucket_counts = NULL; for (i = ht_my_start; i < ht_my_end; ++i) { int64_t result_start_idx = bucket_starts_in_result[HT_LOCAL(i)]; int64_t* temp = result + result_start_idx; /* Gather up all elements with same key. */ int64_t bi = (int64_t)hashtable_get_values(ht, ht_my_size, i, HT_LOCAL(i), temp); if (bi > 1) { /* Selection sort them (for consistency in parallel implementations). */ selection_sort(temp, bi); /* Randomly permute them. */ mrg_state new_st = *st; mrg_skip(&new_st, 1, i, 100); randomly_permute(temp, bi, &new_st); } } free(ht); ht = NULL; free(bucket_starts_in_result); bucket_starts_in_result = NULL; } #undef HT_OWNER #undef HT_LOCAL #endif /* GRAPH_GENERATOR_MPI */ /* Code below this is used for testing the permutation generators. */ #if 0 int main(int argc, char** argv) { MPI_Init(&argc, &argv); const int64_t n = 200000; int64_t* result = NULL; int64_t result_size; mrg_state st; uint_fast32_t seed[5] = {1, 2, 3, 4, 5}; mrg_seed(&st, seed); MPI_Barrier(MPI_COMM_WORLD); double start = MPI_Wtime(); rand_sort_mpi(MPI_COMM_WORLD, &st, n, &result_size, &result); MPI_Barrier(MPI_COMM_WORLD); double time = MPI_Wtime() - start; #if 0 int64_t i; printf("My count = %" PRId64 "\n", result_size); for (i = 0; i < result_size; ++i) printf("%" PRId64 "\n", result[i]); #endif int rank; MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) { printf("Shuffle of %" PRId64 " element(s) took %f second(s).\n", n, time); } free(result); result = NULL; MPI_Finalize(); return 0; } #endif #if 0 int main(int argc, char** argv) { const int64_t n = 5000000; int64_t* result = (int64_t*)xmalloc(n * sizeof(int64_t)); mrg_state st; uint_fast32_t seed[5] = {1, 2, 3, 4, 5}; mrg_seed(&st, seed); unsigned long time; #pragma mta fence time = mta_get_clock(0); rand_sort_shared(&st, n, result); #pragma mta fence time = mta_get_clock(time); #if 0 int64_t i; for (i = 0; i < n; ++i) printf("%" PRId64 "\n", result[i]); #endif printf("Shuffle of %" PRId64 " element(s) took %f second(s).\n", n, time * mta_clock_period()); free(result); result = NULL; return 0; } #endif #if 0 int main(int argc, char** argv) { const int64_t n = 5000000; int64_t* result = (int64_t*)xmalloc(n * sizeof(int64_t)); mrg_state st; uint_fast32_t seed[5] = {1, 2, 3, 4, 5}; mrg_seed(&st, seed); double time; time = omp_get_wtime(); rand_sort_shared(&st, n, result); time = omp_get_wtime() - time; #if 0 int64_t i; for (i = 0; i < n; ++i) printf("%" PRId64 "\n", result[i]); #endif printf("Shuffle of %" PRId64 " element(s) took %f second(s).\n", n, time); free(result); result = NULL; return 0; } #endif CombBLAS_beta_16_2/graph500-1.2/generator/include/000755 000765 000024 00000000000 13271404146 022663 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/generator/splittable_mrg.c000644 000765 000024 00000036150 13212627400 024414 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include #include #include #define __STDC_FORMAT_MACROS #include #include "mod_arith.h" #include "splittable_mrg.h" /* Multiple recursive generator from L'Ecuyer, P., Blouin, F., and */ /* Couture, R. 1993. A search for good multiple recursive random number */ /* generators. ACM Trans. Model. Comput. Simul. 3, 2 (Apr. 1993), 87-98. */ /* DOI= http://doi.acm.org/10.1145/169702.169698 -- particular generator */ /* used is from table 3, entry for m = 2^31 - 1, k = 5 (same generator */ /* is used in GNU Scientific Library). */ /* */ /* MRG state is 5 numbers mod 2^31 - 1, and there is a transition matrix */ /* A from one state to the next: */ /* */ /* A = [x 0 0 0 y] */ /* [1 0 0 0 0] */ /* [0 1 0 0 0] */ /* [0 0 1 0 0] */ /* [0 0 0 1 0] */ /* where x = 107374182 and y = 104480 */ /* */ /* To do leapfrogging (applying multiple steps at once so that we can */ /* create a tree of generators), we need powers of A. These (from an */ /* analysis with Maple) all look like: */ /* */ /* let a = x * s + t */ /* b = x * a + u */ /* c = x * b + v */ /* d = x * c + w in */ /* A^n = [d s*y a*y b*y c*y] */ /* [c w s*y a*y b*y] */ /* [b v w s*y a*y] */ /* [a u v w s*y] */ /* [s t u v w ] */ /* for some values of s, t, u, v, and w */ /* Note that A^n is determined by its bottom row (and x and y, which are */ /* fixed), and that it has a large part that is a Toeplitz matrix. You */ /* can multiply two A-like matrices by: */ /* (defining a..d1 and a..d2 for the two matrices) */ /* s3 = s1 d2 + t1 c2 + u1 b2 + v1 a2 + w1 s2, */ /* t3 = s1 s2 y + t1 w2 + u1 v2 + v1 u2 + w1 t2, */ /* u3 = s1 a2 y + t1 s2 y + u1 w2 + v1 v2 + w1 u2, */ /* v3 = s1 b2 y + t1 a2 y + u1 s2 y + v1 w2 + w1 v2, */ /* w3 = s1 c2 y + t1 b2 y + u1 a2 y + v1 s2 y + w1 w2 */ #ifdef _MSC_VER static void mrg_update_cache(mrg_transition_matrix* __restrict p) { /* Set a, b, c, and d */ #else static void mrg_update_cache(mrg_transition_matrix* restrict p) { /* Set a, b, c, and d */ #endif p->a = mod_add(mod_mul_x(p->s), p->t); p->b = mod_add(mod_mul_x(p->a), p->u); p->c = mod_add(mod_mul_x(p->b), p->v); p->d = mod_add(mod_mul_x(p->c), p->w); } static void mrg_make_identity(mrg_transition_matrix* result) { result->s = result->t = result->u = result->v = 0; result->w = 1; mrg_update_cache(result); } static void mrg_make_A(mrg_transition_matrix* result) { /* Initial RNG transition matrix */ result->s = result->t = result->u = result->w = 0; result->v = 1; mrg_update_cache(result); } /* Multiply two transition matrices; result may alias either/both inputs. */ #ifdef _MSC_VER static void mrg_multiply(const mrg_transition_matrix* __restrict m, const mrg_transition_matrix* __restrict n, mrg_transition_matrix* result) { #else static void mrg_multiply(const mrg_transition_matrix* restrict m, const mrg_transition_matrix* restrict n, mrg_transition_matrix* result) { #endif uint_least32_t rs = mod_mac(mod_mac(mod_mac(mod_mac(mod_mul(m->s, n->d), m->t, n->c), m->u, n->b), m->v, n->a), m->w, n->s); uint_least32_t rt = mod_mac(mod_mac(mod_mac(mod_mac(mod_mul_y(mod_mul(m->s, n->s)), m->t, n->w), m->u, n->v), m->v, n->u), m->w, n->t); uint_least32_t ru = mod_mac(mod_mac(mod_mac(mod_mul_y(mod_mac(mod_mul(m->s, n->a), m->t, n->s)), m->u, n->w), m->v, n->v), m->w, n->u); uint_least32_t rv = mod_mac(mod_mac(mod_mul_y(mod_mac(mod_mac(mod_mul(m->s, n->b), m->t, n->a), m->u, n->s)), m->v, n->w), m->w, n->v); uint_least32_t rw = mod_mac(mod_mul_y(mod_mac(mod_mac(mod_mac(mod_mul(m->s, n->c), m->t, n->b), m->u, n->a), m->v, n->s)), m->w, n->w); result->s = rs; result->t = rt; result->u = ru; result->v = rv; result->w = rw; mrg_update_cache(result); } /* No aliasing allowed */ #ifdef _MSC_VER static void mrg_power(const mrg_transition_matrix* __restrict m, unsigned int exponent, mrg_transition_matrix* __restrict result) { #else static void mrg_power(const mrg_transition_matrix* restrict m, unsigned int exponent, mrg_transition_matrix* restrict result) { #endif mrg_transition_matrix current_power_of_2 = *m; mrg_make_identity(result); while (exponent > 0) { if (exponent % 2 == 1) mrg_multiply(result, ¤t_power_of_2, result); mrg_multiply(¤t_power_of_2, ¤t_power_of_2, ¤t_power_of_2); exponent /= 2; } } /* r may alias st */ #ifdef __MTA__ #pragma mta inline #endif #ifdef _MSC_VER static void mrg_apply_transition(const mrg_transition_matrix* __restrict mat, const mrg_state* __restrict st, mrg_state* r) { #else static void mrg_apply_transition(const mrg_transition_matrix* restrict mat, const mrg_state* restrict st, mrg_state* r) { #endif #ifdef __MTA__ uint_fast64_t s = mat->s; uint_fast64_t t = mat->t; uint_fast64_t u = mat->u; uint_fast64_t v = mat->v; uint_fast64_t w = mat->w; uint_fast64_t z1 = st->z1; uint_fast64_t z2 = st->z2; uint_fast64_t z3 = st->z3; uint_fast64_t z4 = st->z4; uint_fast64_t z5 = st->z5; uint_fast64_t temp = s * z1 + t * z2 + u * z3 + v * z4; r->z5 = mod_down(mod_down_fast(temp) + w * z5); uint_fast64_t a = mod_down(107374182 * s + t); uint_fast64_t sy = mod_down(104480 * s); r->z4 = mod_down(mod_down_fast(a * z1 + u * z2 + v * z3) + w * z4 + sy * z5); uint_fast64_t b = mod_down(107374182 * a + u); uint_fast64_t ay = mod_down(104480 * a); r->z3 = mod_down(mod_down_fast(b * z1 + v * z2 + w * z3) + sy * z4 + ay * z5); uint_fast64_t c = mod_down(107374182 * b + v); uint_fast64_t by = mod_down(104480 * b); r->z2 = mod_down(mod_down_fast(c * z1 + w * z2 + sy * z3) + ay * z4 + by * z5); uint_fast64_t d = mod_down(107374182 * c + w); uint_fast64_t cy = mod_down(104480 * c); r->z1 = mod_down(mod_down_fast(d * z1 + sy * z2 + ay * z3) + by * z4 + cy * z5); /* A^n = [d s*y a*y b*y c*y] */ /* [c w s*y a*y b*y] */ /* [b v w s*y a*y] */ /* [a u v w s*y] */ /* [s t u v w ] */ #else uint_fast32_t o1 = mod_mac_y(mod_mul(mat->d, st->z1), mod_mac4(0, mat->s, st->z2, mat->a, st->z3, mat->b, st->z4, mat->c, st->z5)); uint_fast32_t o2 = mod_mac_y(mod_mac2(0, mat->c, st->z1, mat->w, st->z2), mod_mac3(0, mat->s, st->z3, mat->a, st->z4, mat->b, st->z5)); uint_fast32_t o3 = mod_mac_y(mod_mac3(0, mat->b, st->z1, mat->v, st->z2, mat->w, st->z3), mod_mac2(0, mat->s, st->z4, mat->a, st->z5)); uint_fast32_t o4 = mod_mac_y(mod_mac4(0, mat->a, st->z1, mat->u, st->z2, mat->v, st->z3, mat->w, st->z4), mod_mul(mat->s, st->z5)); uint_fast32_t o5 = mod_mac2(mod_mac3(0, mat->s, st->z1, mat->t, st->z2, mat->u, st->z3), mat->v, st->z4, mat->w, st->z5); r->z1 = o1; r->z2 = o2; r->z3 = o3; r->z4 = o4; r->z5 = o5; #endif } #ifdef __MTA__ #pragma mta inline #endif static void mrg_step(const mrg_transition_matrix* mat, mrg_state* state) { mrg_apply_transition(mat, state, state); } #ifdef __MTA__ #pragma mta inline #endif static void mrg_orig_step(mrg_state* state) { /* Use original A, not fully optimized yet */ uint_fast32_t new_elt = mod_mac2(0, 107374182, state->z1, 104480, state->z5); state->z5 = state->z4; state->z4 = state->z3; state->z3 = state->z2; state->z2 = state->z1; state->z1 = new_elt; } extern const mrg_transition_matrix mrg_skip_matrices[][256]; /* In generated mrg_transitions.c */ void mrg_skip(mrg_state* state, uint_least64_t exponent_high, uint_least64_t exponent_middle, uint_least64_t exponent_low) { /* fprintf(stderr, "skip(%016" PRIXLEAST64 "%016" PRIXLEAST64 "%016" PRIXLEAST64 ")\n", exponent_high, exponent_middle, exponent_low); */ int byte_index; for (byte_index = 0; exponent_low; ++byte_index, exponent_low >>= 8) { uint_least8_t val = (uint_least8_t)(exponent_low & 0xFF); if (val != 0) mrg_step(&mrg_skip_matrices[byte_index][val], state); } for (byte_index = 8; exponent_middle; ++byte_index, exponent_middle >>= 8) { uint_least8_t val = (uint_least8_t)(exponent_middle & 0xFF); if (val != 0) mrg_step(&mrg_skip_matrices[byte_index][val], state); } for (byte_index = 16; exponent_high; ++byte_index, exponent_high >>= 8) { uint_least8_t val = (uint_least8_t)(exponent_high & 0xFF); if (val != 0) mrg_step(&mrg_skip_matrices[byte_index][val], state); } } #ifdef DUMP_TRANSITION_TABLE const mrg_transition_matrix mrg_skip_matrices[][256] = {}; /* Dummy version */ void dump_mrg(FILE* out, const mrg_transition_matrix* m) { /* This is used as an initializer for the mrg_transition_matrix struct, so * the order of the fields here needs to match the struct * mrg_transition_matrix definition in splittable_mrg.h */ fprintf(out, "{%" PRIuFAST32 ", %" PRIuFAST32 ", %" PRIuFAST32 ", %" PRIuFAST32 ", %" PRIuFAST32 ", %" PRIuFAST32 ", %" PRIuFAST32 ", %" PRIuFAST32 ", %" PRIuFAST32 "}\n", m->s, m->t, m->u, m->v, m->w, m->a, m->b, m->c, m->d); } void dump_mrg_powers(void) { /* transitions contains A^(256^n) for n in 0 .. 192/8 */ int i, j; mrg_transition_matrix transitions[192 / 8]; FILE* out = fopen("mrg_transitions.c", "w"); if (!out) { fprintf(stderr, "dump_mrg_powers: could not open mrg_transitions.c for output\n"); exit (1); } fprintf(out, "/* Copyright (C) 2010 The Trustees of Indiana University. */\n"); fprintf(out, "/* */\n"); fprintf(out, "/* Use, modification and distribution is subject to the Boost Software */\n"); fprintf(out, "/* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */\n"); fprintf(out, "/* http://www.boost.org/LICENSE_1_0.txt) */\n"); fprintf(out, "/* */\n"); fprintf(out, "/* Authors: Jeremiah Willcock */\n"); fprintf(out, "/* Andrew Lumsdaine */\n"); fprintf(out, "\n"); fprintf(out, "/* This code was generated by dump_mrg_powers() in splittable_mrg.c;\n * look there for how to rebuild the table. */\n\n"); fprintf(out, "#include \"splittable_mrg.h\"\n"); fprintf(out, "const mrg_transition_matrix mrg_skip_matrices[][256] = {\n"); for (i = 0; i < 192 / 8; ++i) { if (i != 0) fprintf(out, ","); fprintf(out, "/* Byte %d */ {\n", i); mrg_transition_matrix m; mrg_make_identity(&m); dump_mrg(out, &m); if (i == 0) { mrg_make_A(&transitions[i]); } else { mrg_power(&transitions[i - 1], 256, &transitions[i]); } fprintf(out, ","); dump_mrg(out, &transitions[i]); for (j = 2; j < 256; ++j) { fprintf(out, ","); mrg_power(&transitions[i], j, &m); dump_mrg(out, &m); } fprintf(out, "} /* End of byte %d */\n", i); } fprintf(out, "};\n"); fclose(out); } /* Build this file with -DDUMP_TRANSITION_TABLE on the host system, then build * the output mrg_transitions.c */ int main(int argc, char** argv) { dump_mrg_powers(); return 0; } #endif /* Returns integer value in [0, 2^31-1) */ uint_fast32_t mrg_get_uint(const mrg_transition_matrix* mat, mrg_state* state) { mrg_step(mat, state); return state->z1; } /* Returns real value in [0, 1) */ double mrg_get_double(const mrg_transition_matrix* mat, mrg_state* state) { return (double)mrg_get_uint(mat, state) * .000000000465661287524579692 /* (2^31 - 1)^(-1) */ + (double)mrg_get_uint(mat, state) * .0000000000000000002168404346990492787 /* (2^31 - 1)^(-2) */ ; } /* Returns integer value in [0, 2^31-1) using original transition matrix. */ uint_fast32_t mrg_get_uint_orig(mrg_state* state) { mrg_orig_step(state); return state->z1; } /* Returns real value in [0, 1) using original transition matrix. */ double mrg_get_double_orig(mrg_state* state) { return (double)mrg_get_uint_orig(state) * .000000000465661287524579692 /* (2^31 - 1)^(-1) */ + (double)mrg_get_uint_orig(state) * .0000000000000000002168404346990492787 /* (2^31 - 1)^(-2) */ ; } void mrg_init(mrg_transition_matrix* tm, mrg_state* st) { uint_fast32_t seed[5] = {1, 1, 1, 1, 1}; mrg_make_A(tm); mrg_seed(st, seed); } void mrg_seed(mrg_state* st, const uint_fast32_t seed[5]) { st->z1 = seed[0]; st->z2 = seed[1]; st->z3 = seed[2]; st->z4 = seed[3]; st->z5 = seed[4]; } /* Split a transition matrix; the result of this function is pre-cached so it * does not need to be called for individual splits of the PRNG state. */ void mrg_split_matrix(const mrg_transition_matrix* tm_in, mrg_transition_matrix* tm_out, unsigned int n) { if (n == 0) return; mrg_power(tm_in, n, tm_out); } /* The variable st_out should be an array of length n; all other parameters are * single elements. * * The state st_in should not be used for random number generation after this * function is called. */ void mrg_split_state(const mrg_transition_matrix* tm_in, const mrg_state* st_in, mrg_state* st_out, unsigned int n) { unsigned int i; if (n == 0) return; st_out[0] = *st_in; for (i = 1; i < n; ++i) { st_out[i] = st_out[i - 1]; mrg_step(tm_in, &st_out[i]); } } CombBLAS_beta_16_2/graph500-1.2/generator/Makefile.xmt000644 000765 000024 00000001517 13212627400 023506 0ustar00aydinbulucstaff000000 000000 CC = cc CFLAGS = -DNDEBUG LDFLAGS = $(CFLAGS) # -g -pg all: libgraph_generator_xmt.a generator_test_xmt libgraph_generator_xmt.a: btrd_binomial_distribution.c splittable_mrg.c mrg_transitions.c graph_generator.c permutation_gen.c make_graph.c utils.c scramble_edges.c btrd_binomial_distribution.h splittable_mrg.h graph_generator.h mod_arith_xmt.h permutation_gen.h make_graph.h utils.h scramble_edges.h $(CC) $(CFLAGS) -R -pl libgraph_generator_xmt.a btrd_binomial_distribution.c splittable_mrg.c mrg_transitions.c graph_generator.c permutation_gen.c make_graph.c utils.c scramble_edges.c -lm ranlib libgraph_generator_xmt.a generator_test_xmt: generator_test_xmt.c libgraph_generator_xmt.a $(CC) $(CFLAGS) -o generator_test_xmt generator_test_xmt.c -L. -lgraph_generator_xmt -lm clean: -rm -f generator_test_xmt *.pl *.o *.a *.ap2 *.out CombBLAS_beta_16_2/graph500-1.2/generator/Makefile-backup000644 000765 000024 00000002233 13212627400 024136 0ustar00aydinbulucstaff000000 000000 AR = ar RANLIB = ranlib # CC is the C++ compiler, while cc is the C compiler COMPILER = CC CFLAGS = -Drestrict=__restrict__ -O2 -DNDEBUG -DGRAPH_GENERATOR_SEQ -fPIC # -g # -pg # CFLAGS = -Drestrict=__restrict__ LDFLAGS = #-g # -pg all: libgraph_generator_seq.a generator_test_seq: generator_test_seq.c libgraph_generator_seq.a CC $(CFLAGS) $(LDFLAGS) -o generator_test_seq generator_test_seq.c -L. -lgraph_generator_seq -lm # mrg_transitions.c only contains one global array. mpicxx will ignore it, needs a C compiler for it to even exist in the object file. # If mpicxx is used to compile this then you'll get an "undefined mrg_skip_matrices" linker error. mrg_transitions.o: cc $(CFLAGS) -c -o mrg_transitions.o mrg_transitions.c libgraph_generator_seq.a: btrd_binomial_distribution.o splittable_mrg.o mrg_transitions.o graph_generator.o permutation_gen.o make_graph.o utils.o scramble_edges.o $(AR) cruv libgraph_generator_seq.a btrd_binomial_distribution.o splittable_mrg.o mrg_transitions.o graph_generator.o permutation_gen.o make_graph.o utils.o scramble_edges.o $(RANLIB) libgraph_generator_seq.a clean: -rm -f generator_test_seq libgraph_generator_seq.a *.o CombBLAS_beta_16_2/graph500-1.2/generator/mrg_transitions.c000644 000765 000024 00002357425 13212627400 024643 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ /* This code was generated by dump_mrg_powers() in splittable_mrg.c; * look there for how to rebuild the table. */ #include "splittable_mrg.h" mrg_transition_matrix mrg_skip_matrices[][256] = { /* Byte 0 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{0, 0, 0, 1, 0, 0, 0, 1, 107374182} ,{0, 0, 1, 0, 0, 0, 1, 107374182, 177167401} ,{0, 1, 0, 0, 0, 1, 107374182, 177167401, 45365592} ,{1, 0, 0, 0, 0, 107374182, 177167401, 45365592, 1272612231} ,{107374182, 0, 0, 0, 104480, 177167401, 45365592, 1272612231, 735806205} ,{177167401, 0, 0, 104480, 2147447079, 45365592, 1272612231, 735806205, 279302172} ,{45365592, 0, 104480, 2147447079, 1288502987, 1272612231, 735806205, 279302172, 331753768} ,{1272612231, 104480, 2147447079, 1288502987, 300643231, 735806205, 279302172, 331753768, 1043522871} ,{735806205, 2147447079, 1288502987, 300643231, 1075890875, 279302172, 331753768, 1043522871, 1891773876} ,{279302172, 1288502987, 300643231, 1075890875, 1412703094, 331753768, 1043522871, 1891773876, 321085508} ,{331753768, 300643231, 1075890875, 1412703094, 1483135124, 1043522871, 1891773876, 321085508, 82265008} ,{1043522871, 1075890875, 1412703094, 1483135124, 1247618060, 1891773876, 321085508, 82265008, 2077818766} ,{1891773876, 1412703094, 1483135124, 1247618060, 1672287537, 321085508, 82265008, 2077818766, 1589296063} ,{321085508, 1483135124, 1247618060, 1672287537, 287178247, 82265008, 2077818766, 1589296063, 53047172} ,{82265008, 1247618060, 1672287537, 287178247, 1171826053, 2077818766, 1589296063, 53047172, 294266084} ,{2077818766, 1672287537, 287178247, 1171826053, 818480546, 1589296063, 53047172, 294266084, 1144984146} ,{1589296063, 287178247, 1171826053, 818480546, 1382796450, 53047172, 294266084, 1144984146, 1626297093} ,{53047172, 1171826053, 818480546, 1382796450, 1922108906, 294266084, 1144984146, 1626297093, 601285647} ,{294266084, 818480546, 1382796450, 1922108906, 1860721300, 1144984146, 1626297093, 601285647, 254406953} ,{1144984146, 1382796450, 1922108906, 1860721300, 1544565868, 1626297093, 601285647, 254406953, 703904158} ,{1626297093, 1922108906, 1860721300, 1544565868, 219534298, 601285647, 254406953, 703904158, 1905903125} ,{601285647, 1860721300, 1544565868, 219534298, 171675059, 254406953, 703904158, 1905903125, 41479877} ,{254406953, 1544565868, 219534298, 171675059, 1985272869, 703904158, 1905903125, 41479877, 1648632365} ,{703904158, 219534298, 171675059, 1985272869, 1033350521, 1905903125, 41479877, 1648632365, 993200105} ,{1905903125, 171675059, 1985272869, 1033350521, 1181452678, 41479877, 1648632365, 993200105, 1370703553} ,{41479877, 1985272869, 1033350521, 1181452678, 1189848278, 1648632365, 993200105, 1370703553, 2105966405} ,{1648632365, 1033350521, 1181452678, 1189848278, 195549314, 993200105, 1370703553, 2105966405, 2142815631} ,{993200105, 1181452678, 1189848278, 195549314, 1593652977, 1370703553, 2105966405, 2142815631, 2024783512} ,{1370703553, 1189848278, 195549314, 1593652977, 989663713, 2105966405, 2142815631, 2024783512, 1569479672} ,{2105966405, 195549314, 1593652977, 989663713, 1865249951, 2142815631, 2024783512, 1569479672, 456938607} ,{2142815631, 1593652977, 989663713, 1865249951, 195522780, 2024783512, 1569479672, 456938607, 787213544} ,{2024783512, 989663713, 1865249951, 195522780, 1911959836, 1569479672, 456938607, 787213544, 2065931825} ,{1569479672, 1865249951, 195522780, 1911959836, 767267790, 456938607, 787213544, 2065931825, 581062563} ,{456938607, 195522780, 1911959836, 767267790, 1679812934, 787213544, 2065931825, 581062563, 1798563584} ,{787213544, 1911959836, 767267790, 1679812934, 236702903, 2065931825, 581062563, 1798563584, 36702378} ,{2065931825, 767267790, 1679812934, 236702903, 1594880667, 581062563, 1798563584, 36702378, 1367286470} ,{581062563, 1679812934, 236702903, 1594880667, 680748736, 1798563584, 36702378, 1367286470, 1275940295} ,{1798563584, 236702903, 1594880667, 680748736, 53881550, 36702378, 1367286470, 1275940295, 1217915182} ,{36702378, 1594880667, 680748736, 53881550, 514209232, 1367286470, 1275940295, 1217915182, 302687283} ,{1367286470, 680748736, 53881550, 514209232, 1406143545, 1275940295, 1217915182, 302687283, 1622325543} ,{1275940295, 53881550, 514209232, 1406143545, 1330703513, 1217915182, 302687283, 1622325543, 1085012120} ,{1217915182, 514209232, 1406143545, 1330703513, 899666781, 302687283, 1622325543, 1085012120, 519912539} ,{302687283, 1406143545, 1330703513, 899666781, 782196022, 1622325543, 1085012120, 519912539, 492852451} ,{1622325543, 1330703513, 899666781, 782196022, 923142118, 1085012120, 519912539, 492852451, 1931759766} ,{1085012120, 899666781, 782196022, 923142118, 1835958577, 519912539, 492852451, 1931759766, 1804087753} ,{519912539, 782196022, 923142118, 1835958577, 699539764, 492852451, 1931759766, 1804087753, 1463973421} ,{492852451, 923142118, 1835958577, 699539764, 2010707502, 1931759766, 1804087753, 1463973421, 1605690987} ,{1931759766, 1835958577, 699539764, 2010707502, 861192714, 1804087753, 1463973421, 1605690987, 1050820145} ,{1804087753, 699539764, 2010707502, 861192714, 1157272032, 1463973421, 1605690987, 1050820145, 1326355893} ,{1463973421, 2010707502, 861192714, 1157272032, 6285309, 1605690987, 1050820145, 1326355893, 937925117} ,{1605690987, 861192714, 1157272032, 6285309, 1420268505, 1050820145, 1326355893, 937925117, 769872167} ,{1050820145, 1157272032, 6285309, 1420268505, 1171818120, 1326355893, 937925117, 769872167, 1653982138} ,{1326355893, 6285309, 1420268505, 1171818120, 1734780372, 937925117, 769872167, 1653982138, 941138259} ,{937925117, 1420268505, 1171818120, 1734780372, 543959730, 769872167, 1653982138, 941138259, 107187157} ,{769872167, 1171818120, 1734780372, 543959730, 442444256, 1653982138, 941138259, 107187157, 82806204} ,{1653982138, 1734780372, 543959730, 442444256, 96526128, 941138259, 107187157, 82806204, 497040686} ,{941138259, 543959730, 442444256, 96526128, 44704150, 107187157, 82806204, 497040686, 514985004} ,{107187157, 442444256, 96526128, 44704150, 1144071484, 82806204, 497040686, 514985004, 1393323462} ,{82806204, 96526128, 44704150, 1144071484, 1934427902, 497040686, 514985004, 1393323462, 1661513055} ,{497040686, 44704150, 1144071484, 1934427902, 1528063804, 514985004, 1393323462, 1661513055, 409663323} ,{514985004, 1144071484, 1934427902, 1528063804, 361321526, 1393323462, 1661513055, 409663323, 540061910} ,{1393323462, 1934427902, 1528063804, 361321526, 430442335, 1661513055, 409663323, 540061910, 1315162490} ,{1661513055, 1528063804, 361321526, 430442335, 813846924, 409663323, 540061910, 1315162490, 1427281876} ,{409663323, 361321526, 430442335, 813846924, 895897508, 540061910, 1315162490, 1427281876, 2114335769} ,{540061910, 430442335, 813846924, 895897508, 127418683, 1315162490, 1427281876, 2114335769, 353768805} ,{1315162490, 813846924, 895897508, 127418683, 535531875, 1427281876, 2114335769, 353768805, 948583705} ,{1427281876, 895897508, 127418683, 535531875, 1435801905, 2114335769, 353768805, 948583705, 1640668520} ,{2114335769, 127418683, 535531875, 1435801905, 1145956800, 353768805, 948583705, 1640668520, 571722818} ,{353768805, 535531875, 1435801905, 1145956800, 600829171, 948583705, 1640668520, 571722818, 185977820} ,{948583705, 1435801905, 1145956800, 600829171, 1423697883, 1640668520, 571722818, 185977820, 1358605646} ,{1640668520, 1145956800, 600829171, 1423697883, 1655189350, 571722818, 185977820, 1358605646, 1823922468} ,{571722818, 600829171, 1423697883, 1655189350, 607298766, 185977820, 1358605646, 1823922468, 827919361} ,{185977820, 1423697883, 1655189350, 607298766, 1342383335, 1358605646, 1823922468, 827919361, 1159985741} ,{1358605646, 1655189350, 607298766, 1342383335, 530595544, 1823922468, 827919361, 1159985741, 231974717} ,{1823922468, 607298766, 1342383335, 530595544, 596311027, 827919361, 1159985741, 231974717, 192997329} ,{827919361, 1342383335, 530595544, 596311027, 15589154, 1159985741, 231974717, 192997329, 914407730} ,{1159985741, 530595544, 596311027, 15589154, 373536120, 231974717, 192997329, 914407730, 1127235238} ,{231974717, 596311027, 15589154, 373536120, 2070601235, 192997329, 914407730, 1127235238, 1461320537} ,{192997329, 15589154, 373536120, 2070601235, 217992118, 914407730, 1127235238, 1461320537, 1531891030} ,{914407730, 373536120, 2070601235, 217992118, 1636972237, 1127235238, 1461320537, 1531891030, 27068553} ,{1127235238, 2070601235, 217992118, 1636972237, 67142664, 1461320537, 1531891030, 27068553, 1453533041} ,{1461320537, 217992118, 1636972237, 67142664, 1239497466, 1531891030, 27068553, 1453533041, 838135084} ,{1531891030, 1636972237, 67142664, 1239497466, 1272338648, 27068553, 1453533041, 838135084, 1408488098} ,{27068553, 67142664, 1239497466, 1272338648, 18603490, 1453533041, 838135084, 1408488098, 1458367938} ,{1453533041, 1239497466, 1272338648, 18603490, 2033937988, 838135084, 1408488098, 1458367938, 1308760845} ,{838135084, 1272338648, 18603490, 2033937988, 1531058781, 1408488098, 1458367938, 1308760845, 1609863397} ,{1408488098, 18603490, 2033937988, 1531058781, 412902601, 1458367938, 1308760845, 1609863397, 1674811512} ,{1458367938, 2033937988, 1531058781, 412902601, 372084718, 1308760845, 1609863397, 1674811512, 1074390877} ,{1308760845, 1531058781, 412902601, 372084718, 2022440296, 1609863397, 1674811512, 1074390877, 1324280942} ,{1609863397, 412902601, 372084718, 2022440296, 459346522, 1674811512, 1074390877, 1324280942, 210596557} ,{1674811512, 372084718, 2022440296, 459346522, 1166034579, 1074390877, 1324280942, 210596557, 770203237} ,{1074390877, 2022440296, 459346522, 1166034579, 896765259, 1324280942, 210596557, 770203237, 305071579} ,{1324280942, 459346522, 1166034579, 896765259, 1241116623, 210596557, 770203237, 305071579, 1026967388} ,{210596557, 1166034579, 896765259, 1241116623, 648927597, 770203237, 305071579, 1026967388, 1148482470} ,{770203237, 896765259, 1241116623, 648927597, 10828198, 305071579, 1026967388, 1148482470, 682601157} ,{305071579, 1241116623, 648927597, 10828198, 326981376, 1026967388, 1148482470, 682601157, 1913432071} ,{1026967388, 648927597, 10828198, 326981376, 926285146, 1148482470, 682601157, 1913432071, 1437699927} ,{1148482470, 10828198, 326981376, 926285146, 679759532, 682601157, 1913432071, 1437699927, 928183834} ,{682601157, 326981376, 926285146, 679759532, 652205828, 1913432071, 1437699927, 928183834, 1830580039} ,{1913432071, 926285146, 679759532, 652205828, 236966490, 1437699927, 928183834, 1830580039, 1636372941} ,{1437699927, 679759532, 652205828, 236966490, 1835111556, 928183834, 1830580039, 1636372941, 1369755209} ,{928183834, 652205828, 236966490, 1835111556, 849716251, 1830580039, 1636372941, 1369755209, 1336669569} ,{1830580039, 236966490, 1835111556, 849716251, 580445094, 1636372941, 1369755209, 1336669569, 1078978386} ,{1636372941, 1835111556, 849716251, 580445094, 1961389253, 1369755209, 1336669569, 1078978386, 80508265} ,{1369755209, 849716251, 580445094, 1961389253, 629287069, 1336669569, 1078978386, 80508265, 1137980088} ,{1336669569, 580445094, 1961389253, 629287069, 1566516593, 1078978386, 80508265, 1137980088, 2027217021} ,{1078978386, 1961389253, 629287069, 1566516593, 80037416, 80508265, 1137980088, 2027217021, 1625369288} ,{80508265, 629287069, 1566516593, 80037416, 1655203662, 1137980088, 2027217021, 1625369288, 1945317870} ,{1137980088, 1566516593, 80037416, 1655203662, 1957565548, 2027217021, 1625369288, 1945317870, 202962470} ,{2027217021, 80037416, 1655203662, 1957565548, 727478085, 1625369288, 1945317870, 202962470, 1730183044} ,{1625369288, 1655203662, 1957565548, 727478085, 1617217764, 1945317870, 202962470, 1730183044, 1441150428} ,{1945317870, 1957565548, 727478085, 1617217764, 2018856421, 202962470, 1730183044, 1441150428, 225963583} ,{202962470, 727478085, 1617217764, 2018856421, 368770932, 1730183044, 1441150428, 225963583, 611806225} ,{1730183044, 1617217764, 2018856421, 368770932, 1265335122, 1441150428, 225963583, 611806225, 1588073855} ,{1441150428, 2018856421, 368770932, 1265335122, 793483601, 225963583, 611806225, 1588073855, 1848270487} ,{225963583, 368770932, 1265335122, 793483601, 580808035, 611806225, 1588073855, 1848270487, 685532641} ,{611806225, 1265335122, 793483601, 580808035, 1387420369, 1588073855, 1848270487, 685532641, 1254858127} ,{1588073855, 793483601, 580808035, 1387420369, 1663635045, 1848270487, 685532641, 1254858127, 1976053977} ,{1848270487, 580808035, 1387420369, 1663635045, 927352239, 685532641, 1254858127, 1976053977, 2061094447} ,{685532641, 1387420369, 1663635045, 927352239, 1275976226, 1254858127, 1976053977, 2061094447, 1306212446} ,{1254858127, 1663635045, 927352239, 1275976226, 1575736936, 1976053977, 2061094447, 1306212446, 1762807674} ,{1976053977, 927352239, 1275976226, 1575736936, 1552975963, 2061094447, 1306212446, 1762807674, 291748183} ,{2061094447, 1275976226, 1575736936, 1552975963, 1189178027, 1306212446, 1762807674, 291748183, 1409188710} ,{1306212446, 1575736936, 1552975963, 1189178027, 2077635988, 1762807674, 291748183, 1409188710, 510678116} ,{1762807674, 1552975963, 1189178027, 2077635988, 490591230, 291748183, 1409188710, 510678116, 2029840807} ,{291748183, 1189178027, 2077635988, 490591230, 1358278212, 1409188710, 510678116, 2029840807, 1399453206} ,{1409188710, 2077635988, 490591230, 1358278212, 467274322, 510678116, 2029840807, 1399453206, 621710794} ,{510678116, 490591230, 1358278212, 467274322, 557582480, 2029840807, 1399453206, 621710794, 1843222255} ,{2029840807, 1358278212, 467274322, 557582480, 1418349965, 1399453206, 621710794, 1843222255, 236351264} ,{1399453206, 467274322, 557582480, 1418349965, 872472228, 621710794, 1843222255, 236351264, 1219246015} ,{621710794, 557582480, 1418349965, 872472228, 1299373238, 1843222255, 236351264, 1219246015, 335766221} ,{1843222255, 1418349965, 872472228, 1299373238, 1405886311, 236351264, 1219246015, 335766221, 1395742316} ,{236351264, 872472228, 1299373238, 1405886311, 2117674028, 1219246015, 335766221, 1395742316, 1199667488} ,{1219246015, 1299373238, 1405886311, 2117674028, 65605867, 335766221, 1395742316, 1199667488, 504715705} ,{335766221, 1405886311, 2117674028, 65605867, 241190807, 1395742316, 1199667488, 504715705, 601411222} ,{1395742316, 2117674028, 65605867, 241190807, 1709396335, 1199667488, 504715705, 601411222, 1713650772} ,{1199667488, 65605867, 241190807, 1709396335, 132642498, 504715705, 601411222, 1713650772, 821354916} ,{504715705, 241190807, 1709396335, 132642498, 1228605438, 601411222, 1713650772, 821354916, 511634488} ,{601411222, 1709396335, 132642498, 1228605438, 1235906315, 1713650772, 821354916, 511634488, 1915827703} ,{1713650772, 132642498, 1228605438, 1235906315, 72963340, 821354916, 511634488, 1915827703, 1872029838} ,{821354916, 1228605438, 1235906315, 72963340, 78557229, 511634488, 1915827703, 1872029838, 1356082068} ,{511634488, 1235906315, 72963340, 78557229, 1715089560, 1915827703, 1872029838, 1356082068, 2099454295} ,{1915827703, 72963340, 78557229, 1715089560, 408365116, 1872029838, 1356082068, 2099454295, 1284168848} ,{1872029838, 78557229, 1715089560, 408365116, 875156217, 1356082068, 2099454295, 1284168848, 1284690579} ,{1356082068, 1715089560, 408365116, 875156217, 1161872774, 2099454295, 1284168848, 1284690579, 604856889} ,{2099454295, 408365116, 875156217, 1161872774, 1073370168, 1284168848, 1284690579, 604856889, 1828037898} ,{1284168848, 875156217, 1161872774, 1073370168, 562586079, 1284690579, 604856889, 1828037898, 1855508097} ,{1284690579, 1161872774, 1073370168, 562586079, 1625425421, 604856889, 1828037898, 1855508097, 653875040} ,{604856889, 1073370168, 562586079, 1625425421, 301305479, 1828037898, 1855508097, 653875040, 72449215} ,{1828037898, 562586079, 1625425421, 301305479, 1446482451, 1855508097, 653875040, 72449215, 884254314} ,{1855508097, 1625425421, 301305479, 1446482451, 498986154, 653875040, 72449215, 884254314, 1692735697} ,{653875040, 301305479, 1446482451, 498986154, 1547225282, 72449215, 884254314, 1692735697, 632645241} ,{72449215, 1446482451, 498986154, 1547225282, 1114400836, 884254314, 1692735697, 632645241, 1000349184} ,{884254314, 498986154, 1547225282, 1114400836, 1761611172, 1692735697, 632645241, 1000349184, 1840985687} ,{1692735697, 1547225282, 1114400836, 1761611172, 2144232780, 632645241, 1000349184, 1840985687, 104023419} ,{632645241, 1114400836, 1761611172, 2144232780, 1009873875, 1000349184, 1840985687, 104023419, 866091496} ,{1000349184, 1761611172, 2144232780, 1009873875, 1375608667, 1840985687, 104023419, 866091496, 642979914} ,{1840985687, 2144232780, 1009873875, 1375608667, 601128477, 104023419, 866091496, 642979914, 1879324060} ,{104023419, 1009873875, 1375608667, 601128477, 369283264, 866091496, 642979914, 1879324060, 1859003490} ,{866091496, 1375608667, 601128477, 369283264, 2099563300, 642979914, 1879324060, 1859003490, 375170255} ,{642979914, 601128477, 369283264, 2099563300, 721068441, 1879324060, 1859003490, 375170255, 52887940} ,{1879324060, 369283264, 2099563300, 721068441, 957969266, 1859003490, 375170255, 52887940, 939458487} ,{1859003490, 2099563300, 721068441, 957969266, 905492649, 375170255, 52887940, 939458487, 1328301455} ,{375170255, 721068441, 957969266, 905492649, 1673665932, 52887940, 939458487, 1328301455, 671889511} ,{52887940, 957969266, 905492649, 1673665932, 1916717356, 939458487, 1328301455, 671889511, 715188386} ,{939458487, 905492649, 1673665932, 1916717356, 256547469, 1328301455, 671889511, 715188386, 650476628} ,{1328301455, 1673665932, 1916717356, 256547469, 1735151978, 671889511, 715188386, 650476628, 218994970} ,{671889511, 1916717356, 256547469, 1735151978, 1952814672, 715188386, 650476628, 218994970, 802424609} ,{715188386, 256547469, 1735151978, 1952814672, 2070656144, 650476628, 218994970, 802424609, 608691525} ,{650476628, 1735151978, 1952814672, 2070656144, 1189071915, 218994970, 802424609, 608691525, 1512900793} ,{218994970, 1952814672, 2070656144, 1189071915, 383116831, 802424609, 608691525, 1512900793, 1249465924} ,{802424609, 2070656144, 1189071915, 383116831, 1303690462, 608691525, 1512900793, 1249465924, 1295874118} ,{608691525, 1189071915, 383116831, 1303690462, 1709053087, 1512900793, 1249465924, 1295874118, 1040748781} ,{1512900793, 383116831, 1303690462, 1709053087, 509809742, 1249465924, 1295874118, 1040748781, 252921851} ,{1249465924, 1303690462, 1709053087, 509809742, 193531558, 1295874118, 1040748781, 252921851, 1286124916} ,{1295874118, 1709053087, 509809742, 193531558, 816322037, 1040748781, 252921851, 1286124916, 2084165234} ,{1040748781, 509809742, 193531558, 816322037, 526356231, 252921851, 1286124916, 2084165234, 1300136952} ,{252921851, 193531558, 816322037, 526356231, 1745656682, 1286124916, 2084165234, 1300136952, 431615290} ,{1286124916, 816322037, 526356231, 1745656682, 488716145, 2084165234, 1300136952, 431615290, 1411392617} ,{2084165234, 526356231, 1745656682, 488716145, 1984463596, 1300136952, 431615290, 1411392617, 1168353633} ,{1300136952, 1745656682, 488716145, 1984463596, 889326167, 431615290, 1411392617, 1168353633, 1876266766} ,{431615290, 488716145, 1984463596, 889326167, 1378137622, 1411392617, 1168353633, 1876266766, 1365689348} ,{1411392617, 1984463596, 889326167, 1378137622, 156395847, 1168353633, 1876266766, 1365689348, 537398034} ,{1168353633, 889326167, 1378137622, 156395847, 1041035611, 1876266766, 1365689348, 537398034, 208701205} ,{1876266766, 1378137622, 156395847, 1041035611, 174629419, 1365689348, 537398034, 208701205, 638454909} ,{1365689348, 156395847, 1041035611, 174629419, 1454478932, 537398034, 208701205, 638454909, 49903708} ,{537398034, 1041035611, 174629419, 1454478932, 1967121419, 208701205, 638454909, 49903708, 661164933} ,{208701205, 174629419, 1454478932, 1967121419, 1386641505, 638454909, 49903708, 661164933, 403614502} ,{638454909, 1454478932, 1967121419, 1386641505, 1700430409, 49903708, 661164933, 403614502, 1773913698} ,{49903708, 1967121419, 1386641505, 1700430409, 631849206, 661164933, 403614502, 1773913698, 1943714694} ,{661164933, 1386641505, 1700430409, 631849206, 1996600571, 403614502, 1773913698, 1943714694, 672055334} ,{403614502, 1700430409, 631849206, 1996600571, 405726791, 1773913698, 1943714694, 672055334, 1673745977} ,{1773913698, 631849206, 1996600571, 405726791, 1654276468, 1943714694, 672055334, 1673745977, 746342829} ,{1943714694, 1996600571, 405726791, 1654276468, 2074496352, 672055334, 1673745977, 746342829, 632160356} ,{672055334, 405726791, 1654276468, 2074496352, 372666918, 1673745977, 746342829, 632160356, 1869397711} ,{1673745977, 1654276468, 2074496352, 372666918, 68490361, 746342829, 632160356, 1869397711, 595317168} ,{746342829, 2074496352, 372666918, 68490361, 1238818103, 632160356, 1869397711, 595317168, 1889450553} ,{632160356, 372666918, 68490361, 1238818103, 620067703, 1869397711, 595317168, 1889450553, 1354624380} ,{1869397711, 68490361, 1238818103, 620067703, 106947748, 595317168, 1889450553, 1354624380, 1780312862} ,{595317168, 1238818103, 620067703, 106947748, 1035150630, 1889450553, 1354624380, 1780312862, 626789493} ,{1889450553, 620067703, 106947748, 1035150630, 1168844579, 1354624380, 1780312862, 626789493, 197848980} ,{1354624380, 106947748, 1035150630, 1168844579, 212043318, 1780312862, 626789493, 197848980, 142796175} ,{1780312862, 1035150630, 1168844579, 212043318, 1245466865, 626789493, 197848980, 142796175, 658617292} ,{626789493, 1168844579, 212043318, 1245466865, 644253208, 197848980, 142796175, 658617292, 1702227344} ,{197848980, 212043318, 1245466865, 644253208, 1599897022, 142796175, 658617292, 1702227344, 1433614181} ,{142796175, 1245466865, 644253208, 1599897022, 1731328025, 658617292, 1702227344, 1433614181, 1336937244} ,{658617292, 644253208, 1599897022, 1731328025, 775468291, 1702227344, 1433614181, 1336937244, 737036985} ,{1702227344, 1599897022, 1731328025, 775468291, 516167339, 1433614181, 1336937244, 737036985, 795075306} ,{1433614181, 1731328025, 775468291, 516167339, 559707521, 1336937244, 737036985, 795075306, 925676258} ,{1336937244, 775468291, 516167339, 559707521, 1320219924, 737036985, 795075306, 925676258, 781484869} ,{737036985, 516167339, 559707521, 1320219924, 129434005, 795075306, 925676258, 781484869, 822281942} ,{795075306, 559707521, 1320219924, 129434005, 1155578674, 925676258, 781484869, 822281942, 1082528359} ,{925676258, 1320219924, 129434005, 1155578674, 505537626, 781484869, 822281942, 1082528359, 19278518} ,{781484869, 129434005, 1155578674, 505537626, 581909548, 822281942, 1082528359, 19278518, 360413702} ,{822281942, 1155578674, 505537626, 581909548, 63370533, 1082528359, 19278518, 360413702, 151974102} ,{1082528359, 505537626, 581909548, 63370533, 1934001925, 19278518, 360413702, 151974102, 2095559354} ,{19278518, 581909548, 63370533, 1934001925, 1041711771, 360413702, 151974102, 2095559354, 1811504550} ,{360413702, 63370533, 1934001925, 1041711771, 2027383401, 151974102, 2095559354, 1811504550, 319614985} ,{151974102, 1934001925, 1041711771, 2027383401, 2045318462, 2095559354, 1811504550, 319614985, 322840482} ,{2095559354, 1041711771, 2027383401, 2045318462, 1907574689, 1811504550, 319614985, 322840482, 2009328885} ,{1811504550, 2027383401, 2045318462, 1907574689, 1641043329, 319614985, 322840482, 2009328885, 1474649131} ,{319614985, 2045318462, 1907574689, 1641043329, 1819122949, 322840482, 2009328885, 1474649131, 336628112} ,{322840482, 1907574689, 1641043329, 1819122949, 2921950, 2009328885, 1474649131, 336628112, 1173592299} ,{2009328885, 1641043329, 1819122949, 2921950, 1995399578, 1474649131, 336628112, 1173592299, 1477268091} ,{1474649131, 1819122949, 2921950, 1995399578, 975541374, 336628112, 1173592299, 1477268091, 1639613548} ,{336628112, 2921950, 1995399578, 975541374, 126952865, 1173592299, 1477268091, 1639613548, 412081582} ,{1173592299, 1995399578, 975541374, 126952865, 1565454841, 1477268091, 1639613548, 412081582, 1635974652} ,{1477268091, 975541374, 126952865, 1565454841, 2049606761, 1639613548, 412081582, 1635974652, 618022174} ,{1639613548, 126952865, 1565454841, 2049606761, 1025470496, 412081582, 1635974652, 618022174, 164917641} ,{412081582, 1565454841, 2049606761, 1025470496, 2052973850, 1635974652, 618022174, 164917641, 2102626858} ,{1635974652, 2049606761, 1025470496, 2052973850, 1531532304, 618022174, 164917641, 2102626858, 580864539} ,{618022174, 1025470496, 2052973850, 1531532304, 1965725289, 164917641, 2102626858, 580864539, 1655048518} ,{164917641, 2052973850, 1531532304, 1965725289, 418441524, 2102626858, 580864539, 1655048518, 1771909825} ,{2102626858, 1531532304, 1965725289, 418441524, 1333831799, 580864539, 1655048518, 1771909825, 1250534272} ,{580864539, 1965725289, 418441524, 1333831799, 1319486681, 1655048518, 1771909825, 1250534272, 22806227} ,{1655048518, 418441524, 1333831799, 1319486681, 839170500, 1771909825, 1250534272, 22806227, 1582807597} ,{1771909825, 1333831799, 1319486681, 839170500, 1938420553, 1250534272, 22806227, 1582807597, 1062315347} ,{1250534272, 1319486681, 839170500, 1938420553, 1015759071, 22806227, 1582807597, 1062315347, 1395567976} ,{22806227, 839170500, 1938420553, 1015759071, 768171433, 1582807597, 1062315347, 1395567976, 1997709559} ,{1582807597, 1938420553, 1015759071, 768171433, 1235232437, 1062315347, 1395567976, 1997709559, 428659909} ,{1062315347, 1015759071, 768171433, 1235232437, 464530031, 1395567976, 1997709559, 428659909, 1280866704} ,{1395567976, 768171433, 1235232437, 464530031, 162643012, 1997709559, 428659909, 1280866704, 143836395} ,{1997709559, 1235232437, 464530031, 162643012, 1244952121, 428659909, 1280866704, 143836395, 657738471} ,{428659909, 464530031, 162643012, 1244952121, 316621449, 1280866704, 143836395, 657738471, 1267528990} ,{1280866704, 162643012, 1244952121, 316621449, 615834135, 143836395, 657738471, 1267528990, 1245940812} ,{143836395, 1244952121, 316621449, 615834135, 214803821, 657738471, 1267528990, 1245940812, 1067214725} ,{657738471, 316621449, 615834135, 214803821, 2083471541, 1267528990, 1245940812, 1067214725, 99333652} ,{1267528990, 615834135, 214803821, 2083471541, 1038746080, 1245940812, 1067214725, 99333652, 144985843} ,{1245940812, 214803821, 2083471541, 1038746080, 407332004, 1067214725, 99333652, 144985843, 678709506} ,{1067214725, 2083471541, 1038746080, 407332004, 1879807561, 99333652, 144985843, 678709506, 139020681} ,{99333652, 1038746080, 407332004, 1879807561, 948548466, 144985843, 678709506, 139020681, 1007265410} ,{144985843, 407332004, 1879807561, 948548466, 1738978656, 678709506, 139020681, 1007265410, 312693939} ,{678709506, 1879807561, 948548466, 1738978656, 1918714349, 139020681, 1007265410, 312693939, 1701897288} ,{139020681, 948548466, 1738978656, 1918714349, 1659162940, 1007265410, 312693939, 1701897288, 1922492348} ,{1007265410, 1738978656, 1918714349, 1659162940, 1448846219, 312693939, 1701897288, 1922492348, 1634967356} } /* End of byte 0 */ ,/* Byte 1 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{312693939, 1918714349, 1659162940, 1448846219, 1653915565, 1701897288, 1922492348, 1634967356, 652180261} ,{1490540692, 1016212937, 1288169097, 1445404775, 1735087838, 1783013883, 986236785, 1637092812, 303111895} ,{2000977377, 1001943231, 1311130587, 2040446836, 1344782180, 2126962249, 1533061441, 1611249514, 136599756} ,{351698113, 1701095545, 1429876927, 176189899, 972386072, 826381929, 2107010893, 834600457, 358153365} ,{858255002, 1164423131, 2047535417, 789854044, 1911175580, 1078782245, 59348896, 339585201, 1899694942} ,{1108948050, 1703136668, 1797164158, 423624305, 751050497, 241263027, 316857728, 1171717559, 233575169} ,{78744670, 595706641, 1402720996, 1244172727, 179208595, 1641887830, 1901802079, 471167817, 1839660959} ,{1921475429, 1682175673, 511721276, 803426084, 843045387, 1976026914, 1323350409, 1306621082, 600476373} ,{1560719607, 555888348, 1089888607, 2105962025, 953112022, 761255762, 1038197455, 1205722004, 960606050} ,{2066909054, 1409919306, 256485846, 68185987, 315761891, 42256043, 563818778, 1803584697, 1509868347} ,{1667955123, 548863247, 928966273, 1854233440, 109245176, 287201501, 935819930, 452954641, 58085234} ,{1958192493, 28265551, 1102144206, 653596019, 669122304, 738762549, 1809944955, 1630728020, 98367497} ,{928248412, 335515911, 467999208, 844704075, 1943855929, 1299119155, 1623920239, 168957809, 703604690} ,{1612892272, 114555911, 1021719716, 160242693, 1438155293, 838533804, 1157729614, 1258275881, 1105132917} ,{719229692, 163875986, 1599070253, 1413505520, 103522844, 1200635782, 1393596094, 281501793, 1400861587} ,{1893946831, 905799957, 1645941880, 823563257, 1128134089, 1424034572, 288536321, 829949727, 1589270961} ,{1427756481, 1952124018, 1604365958, 422382890, 360730196, 1559783432, 199448298, 137827621, 419864711} ,{580930584, 1072420559, 1312381756, 775303010, 1936734135, 1298591584, 1287371431, 1505839015, 872819568} ,{1629722972, 419909105, 1238524829, 319314954, 2048002667, 1137996253, 88606864, 717799281, 1904147101} ,{2048441213, 2091969478, 332303260, 332789117, 1254585379, 623395777, 1939475838, 1586707856, 269740900} ,{1395265528, 1952533090, 1358904250, 1329497098, 1668725802, 175699967, 2049028538, 397588745, 2066440653} ,{1255606735, 525291061, 394601392, 685447314, 2015074721, 1696441439, 1840956353, 1436976961, 1619506967} ,{88380627, 460711077, 1534451506, 967994117, 1590409373, 1181397134, 476717415, 264272110, 424172311} ,{1775365434, 645037836, 1252994209, 1040286208, 139759725, 1526898487, 1470199015, 2136329288, 251037933} ,{1996081361, 1750819522, 532901925, 60937824, 324274482, 1159565228, 986047554, 1219059733, 1293467946} ,{2075639444, 1985456167, 64413007, 258351591, 929109183, 1688479091, 654561331, 1210371131, 1686595293} ,{1123798223, 654091347, 409721012, 63880789, 1216396668, 582884516, 1923698349, 356954008, 1950456224} ,{609643813, 1681257991, 1218560561, 551973857, 375742993, 716263380, 967868378, 2145955207, 376277947} ,{696675109, 1400346484, 897755710, 1265413298, 934000773, 2122877837, 1980109567, 1323994226, 1114847888} ,{1908138516, 1131042087, 380074813, 1268850074, 63179262, 33696877, 46158359, 1145320466, 306562193} ,{1832630869, 2139557784, 1820227562, 104868816, 555670797, 317020974, 1065025127, 483729298, 171617178} ,{262388536, 1964109240, 223560501, 1326432190, 1952044537, 1442776523, 40711265, 1849054159, 1197501399} ,{940308882, 482531245, 474202125, 864968428, 611251194, 368171501, 452716282, 921266094, 1792046614} ,{2145065540, 1312128493, 823648467, 2062051006, 152779774, 561355554, 2130412576, 886909875, 1452974053} ,{876997513, 570144107, 1948465114, 1735738748, 1928390330, 1659059348, 79304154, 1063737200, 1556082310} ,{377290071, 110400282, 786541597, 165730193, 1723845622, 1159464763, 702851477, 1745093276, 683566246} ,{1130632317, 1219481583, 1675052276, 2107846701, 1651725478, 501637725, 2036349984, 1824620936, 583611421} ,{1709270644, 382607852, 1551576329, 291562165, 119540335, 213859856, 1047228650, 998773961, 2024827278} ,{1130276897, 1006505571, 53045463, 1736217076, 1744205243, 288786110, 1025712148, 88727636, 1283653841} ,{1074854969, 191545506, 482976500, 1945051261, 98799751, 781713908, 1068370091, 604754088, 746129279} ,{836262229, 1979075486, 1534821121, 1945119835, 1462621252, 505267700, 1357977426, 2114072830, 1796437585} ,{1152236488, 2079349374, 1912330975, 1029956825, 489893955, 387576415, 1239808318, 381275549, 1322815154} ,{337360508, 1950200542, 770376895, 137163297, 939274159, 543634176, 150608204, 513947155, 222521743} ,{742258146, 59680736, 1001366041, 211054277, 1526669618, 444135479, 738544441, 59937905, 2042562263} ,{1387290610, 1204378697, 11633966, 43137784, 1159618202, 1792568807, 135854160, 2143072475, 2020155571} ,{992527729, 1683269963, 65914427, 336589020, 1585349762, 154769252, 1300235377, 1706867738, 773197689} ,{1886133274, 982075485, 948582810, 148074131, 470024557, 1825167392, 1598264411, 769797593, 1596459770} ,{95964553, 1769517349, 2070677133, 527208819, 33250257, 984310479, 1618794283, 282753367, 685905855} ,{773199615, 412138333, 2066066849, 566385851, 1735919232, 1752131203, 1774943475, 1555768370, 117658479} ,{495003552, 1014813534, 413248271, 1911261048, 467113202, 2130052479, 1707839368, 25027081, 565727906} ,{1647541329, 1039630555, 1434824947, 1039660811, 308504423, 1429358731, 2115665397, 2124539022, 1927147777} ,{1028824985, 1562152217, 225932211, 686320315, 455672065, 1738934384, 46801906, 1314184742, 210455770} ,{239583419, 1584543509, 2124006269, 1248258715, 694111164, 1393315130, 562604150, 2125089086, 594575078} ,{1699479898, 1410386868, 1248673807, 108412326, 1026309001, 600820539, 931012436, 1500544891, 1682234295} ,{403880611, 2059238209, 983142497, 177958951, 1976423837, 951512354, 5868079, 68530941, 2059812190} ,{550989945, 881637524, 858449133, 936020052, 1817611943, 1225661955, 2040080184, 651488717, 1267468345} ,{221354924, 1575546824, 1924999433, 1140317556, 1917207470, 1927569330, 176608344, 1508001365, 1926277904} ,{1458300975, 1265387810, 701351464, 1110984424, 1620711167, 218111557, 302889872, 145979510, 495876515} ,{1358077121, 1742041707, 1426391412, 666380672, 1402386436, 1374088897, 623337751, 1629328465, 1368992385} ,{1460983968, 1664050967, 769570418, 2056801789, 1153237441, 2011700037, 1890836505, 1931879924, 906576197} ,{1672876350, 109035968, 1588955367, 177076390, 776105674, 597271069, 198794487, 859117596, 45917786} ,{1356943266, 1937936793, 1537774779, 247805435, 1440145299, 2107251744, 1229733398, 1750134028, 1686591848} ,{1134213795, 608214076, 1266884020, 398654246, 1487400489, 1821851983, 951358373, 1461543186, 1620105468} ,{671147444, 401027466, 946034606, 803370771, 1119823610, 595622590, 1811308523, 491535335, 410915331} ,{930148275, 183165135, 1657257514, 1608004959, 1387771271, 1468225974, 499133329, 252192288, 11013782} ,{1030641991, 948983858, 881949496, 1514386093, 673478871, 1769375167, 1014287464, 1588882210, 1191111921} ,{514493809, 1787307606, 1617857528, 1491228406, 695722998, 426118767, 72851589, 284614344, 1025604707} ,{711580541, 194844871, 1707225951, 2143421818, 1116141830, 53165864, 2118114628, 113591510, 2642978} ,{1532855080, 369863741, 1388369654, 53615650, 1214673023, 1980848110, 1768814639, 1474639991, 1879665032} ,{274025079, 795098418, 989471546, 1025787262, 1582267737, 591815458, 567587771, 2008247548, 1738374554} ,{2060526370, 513281218, 2101533691, 220146973, 928428082, 865838812, 939496648, 750316605, 1202688182} ,{1898947129, 712745136, 1975109045, 155874042, 2041089309, 1014481282, 1834788961, 1768555735, 885223890} ,{196702740, 740332729, 1840726461, 402625160, 80771333, 671486770, 531964268, 1075431125, 241241351} ,{661533327, 294154760, 2020570571, 1917719932, 1460759763, 814237372, 876594032, 751918562, 1412336631} ,{524499350, 945062465, 227464427, 788307628, 546698725, 1835229516, 1303121014, 1835453826, 548534980} ,{1629598130, 854082649, 1145040055, 707546695, 2084133060, 1357465127, 1421546537, 2035366507, 2123374059} ,{1343279220, 1284929790, 28931231, 2142686446, 860497593, 814782063, 65880056, 1690131697, 2094312599} ,{402484807, 1456581759, 260990005, 586582470, 1190833971, 2067331353, 933288402, 474679894, 380450914} ,{253355173, 1383273585, 791411776, 1926284647, 1293151823, 542979998, 386620412, 931974044, 1396457637} ,{51001, 1178578801, 2056838356, 1989641596, 1157710648, 1285935133, 855141783, 2012464519, 345973884} ,{1535005288, 588271937, 552939128, 1327371100, 1951201118, 910013545, 771305299, 950040063, 1940809643} ,{1537483069, 1252260005, 23376490, 189891780, 2011698199, 1680508572, 723688678, 1869336025, 1894301502} ,{1419818886, 354791592, 1647360014, 371273744, 184451241, 502100076, 1042128258, 1939264136, 1223695711} ,{1878618447, 786553337, 1837327085, 41866207, 1960723521, 880656157, 1206974883, 2089031192, 370569145} ,{905292614, 1342542487, 482661775, 456292632, 1685042686, 381444978, 134407668, 1268243407, 1992776770} ,{2002254530, 145802132, 269592759, 1924641782, 1997304700, 518754870, 1161770378, 1303273785, 2078029787} ,{2057315234, 1162041245, 1845101087, 248790476, 1537739959, 1945219466, 1808519368, 474802156, 942062475} ,{1316832064, 1464112652, 153525729, 696472602, 30271024, 1432718159, 1692183838, 2036943541, 1572198614} ,{1499678535, 678863065, 84125397, 1157675314, 1434710829, 1764588313, 862383858, 641092599, 1102954237} ,{1576355733, 1677528850, 1303851517, 41342994, 594716829, 374185067, 1924506020, 1515249534, 1567618045} ,{500361084, 2115928600, 710623780, 1407182306, 1002645185, 222815303, 954760971, 106648325, 1502189183} ,{554574380, 689477521, 511862719, 1370811853, 369812270, 495376488, 1197474407, 1703315087, 525271266} ,{680058010, 77427193, 1170398184, 1553378582, 28387068, 913148713, 99176858, 1303918317, 1397376757} ,{1578801438, 418136807, 1363796831, 743530455, 1421904699, 1798291586, 1378639870, 1334748324, 1384239515} ,{1548711230, 1763191646, 1134013060, 273912643, 611728566, 147400892, 223429289, 1162080033, 1600864925} ,{595469324, 568551835, 1702363589, 1665919013, 1324923467, 789634301, 1533365766, 1773486089, 1670570977} ,{459293309, 1216962610, 1567386836, 818805576, 710131202, 2022577593, 107865402, 995801050, 1435342658} ,{1050467969, 2037323329, 1559251236, 1919458385, 1015680602, 488543534, 744015905, 48440083, 1320849120} ,{1582697253, 1296792921, 1582631153, 387873051, 2017661340, 2138713253, 82462238, 144262903, 141808224} ,{608975647, 1818941310, 84291694, 1296133838, 471909377, 209935463, 332936829, 2145973589, 687186262} ,{454141115, 822279448, 1068794301, 1206867695, 126907893, 126459146, 1668778694, 2126033705, 2067150655} ,{2102660197, 1189202215, 825451022, 1809039965, 703859457, 131148599, 672174830, 500036951, 1709962530} ,{2076085680, 159946747, 1975622410, 2124517694, 703790261, 1580800406, 2066587362, 1615960482, 352952457} ,{515733287, 1811911077, 1896465084, 324617601, 218344167, 235540056, 1384529335, 1450645069, 676986034} ,{524792024, 1054724607, 986662722, 1378014102, 1269288891, 1300544128, 1390465736, 461854365, 1644510775} ,{1613844544, 706989387, 1498082765, 608483708, 622885217, 571640526, 1942253675, 1539307657, 1909488637} ,{1932716273, 1100765401, 420745730, 195054538, 1280416522, 1820179076, 1501669971, 850586054, 338466309} ,{1420232715, 1950849869, 1599004806, 2047259074, 756754638, 916897507, 2029709955, 799989678, 262009886} ,{1625813766, 48122134, 727136944, 1744869703, 2066738031, 123332410, 1757712424, 1559167084, 1950526281} ,{1408965261, 72427661, 571859098, 750199078, 1279463630, 1834147649, 896275062, 651251171, 85158079} ,{1953890346, 1534325413, 1068284177, 506014694, 1110077440, 1494708886, 1189381161, 197105470, 2114832349} ,{1556580339, 1394178781, 1318846118, 1802699011, 643114279, 742001480, 1059145600, 1431998051, 1323030967} ,{95065998, 1864271677, 1885954880, 31378801, 983624314, 1616250213, 568648029, 798719632, 1992562631} ,{1767511345, 256457456, 516406163, 647955004, 1183609041, 174699397, 133138827, 1352975691, 1891183555} ,{1826282846, 1226882812, 1932562034, 715452833, 608997079, 1231928910, 427645092, 1854267239, 2000113010} ,{715094734, 51250840, 998921628, 442889990, 933231540, 1304206236, 112952716, 2121343457, 2016122430} ,{1583281283, 2004290796, 2030968974, 594139231, 1316110854, 1772264894, 766431167, 1077507599, 831609012} ,{186242185, 831489454, 231180584, 511507729, 463569452, 1303175601, 2029926953, 1196897666, 688900363} ,{232910, 481244912, 1843531872, 637374459, 1000842954, 1554905217, 977192499, 187982902, 1149797303} ,{1209135230, 1378555283, 1310829061, 19637814, 356256120, 2029099776, 171147410, 1033478044, 424035534} ,{425066314, 1223944921, 1467767169, 1207640135, 551217215, 430926617, 994820306, 1503698122, 239671237} ,{1894745532, 98514332, 1921950691, 1378411817, 615508098, 723843584, 2098102166, 1288321153, 1560460065} ,{1314430366, 1499301458, 1583808938, 845519359, 84181640, 1683495924, 1424082094, 1850329179, 1476675892} ,{855469330, 1539515635, 548355650, 2046577385, 2112615144, 166359546, 1134374903, 1971668716, 993034364} ,{1052507536, 920441642, 2084309885, 1640687860, 842274460, 122567275, 1504540427, 1865717987, 940892441} ,{876242430, 1790705070, 683693034, 918507775, 1438495216, 410278396, 110598866, 1524043266, 1549325167} ,{425734971, 653635638, 885650677, 196956304, 2111915301, 1685744404, 725136865, 480029313, 1192285765} ,{941446626, 764417580, 1019861092, 2146303402, 1389650141, 1079156355, 105285456, 1679956763, 1123787821} ,{546655753, 439701231, 1473411677, 1115002163, 185129032, 1644236088, 1756922505, 1036950198, 1754931745} ,{1682345727, 1323396471, 1617331350, 267434695, 311281296, 1486194743, 1419285737, 1596045787, 504284547} ,{1130103543, 265348005, 995097999, 233924448, 1183085432, 191934312, 68927531, 1390915818, 481516531} ,{309784146, 218738568, 1544255781, 1427870415, 1485918726, 754559211, 313792416, 888546340, 1174927507} ,{1251963111, 1993608809, 1776088951, 1658927159, 149566068, 589054079, 1462545841, 1254410297, 1535883564} ,{601203539, 1501932956, 460692137, 385485330, 1271649986, 1184137535, 1656856735, 1416198208, 1634974072} ,{560138621, 195180069, 1712480444, 599611846, 1783373799, 106505734, 1030958343, 560898973, 835439882} ,{146904381, 419009549, 1533394219, 1433749044, 1550350457, 474967198, 1152407335, 493535565, 1914483921} ,{356823776, 2000967606, 1127239285, 981359937, 1248634026, 1446582555, 84064479, 844563187, 1704656187} ,{754131443, 1067756091, 513495673, 894325019, 1279393566, 1125932633, 1515283622, 578724116, 647343396} ,{1161349442, 1036686413, 1828673623, 519768625, 1778710959, 844962473, 781317481, 353681689, 473806362} ,{1683923445, 1612788585, 834083809, 440791568, 1621558475, 1560286291, 1469099613, 1322471074, 514448505} ,{187785570, 346018255, 623763138, 1807869008, 527129693, 1354035129, 1116218484, 1846689268, 739781908} ,{641463442, 635644009, 1122554399, 1639705172, 1664775554, 625880169, 1869863981, 1092626961, 1389730300} ,{1633581327, 187930031, 998165356, 1277772739, 743430462, 367795843, 1191559358, 645978599, 409963770} ,{2072649482, 795917967, 911185786, 1301307402, 1146766782, 285239013, 59732855, 743529991, 2067647291} ,{1097221525, 1821517360, 1277987257, 1568995859, 882661632, 1974360738, 372212634, 794476343, 926717459} ,{2019448501, 1976646003, 1226972522, 1002303269, 2009983317, 1377213210, 1818689722, 580510231, 840437095} ,{248579610, 1413415727, 1259661761, 836492051, 1378898473, 252671040, 1171226897, 104440090, 268602618} ,{897030869, 131747595, 1774308605, 399094650, 1392137342, 784154432, 640861095, 1785406002, 981993606} ,{2075679591, 2099014574, 955975504, 213688007, 1677343613, 406159076, 384323098, 2011910205, 1510045953} ,{1961225979, 576569876, 1835862313, 1868983862, 489441017, 1930250248, 2019268185, 1699110909, 861119840} ,{1780107625, 1309894392, 1394507387, 1499975875, 1538805884, 1223727635, 429331803, 1671832291, 2134780588} ,{1879022346, 1529028241, 2078045806, 1859823492, 1552505234, 1515615514, 903335282, 1758404508, 1796057115} ,{1009274784, 1460222641, 1647151780, 1891766649, 1685509748, 1536473196, 679889432, 794811889, 226209581} ,{671807548, 748677521, 1840864770, 1987535397, 277205192, 1372538338, 1145727987, 190666231, 1391588017} ,{1026373216, 1076206496, 1866237363, 1658481065, 1565537885, 287479141, 1872993846, 1647178313, 237406199} ,{1797514595, 924373710, 1320281115, 151630056, 1618131451, 1905856337, 331108850, 1109483782, 1444560492} ,{926093270, 724775786, 823507753, 805406870, 1976194267, 1474384965, 844343927, 1261505772, 675673788} ,{1712489973, 369543503, 841112737, 537437655, 1624232950, 1166036383, 755122550, 1346886586, 1797067739} ,{2123168138, 1753706609, 1293216114, 1403149197, 320859278, 795849396, 585172096, 768842234, 1555003049} ,{469019000, 1583455415, 1013248623, 732358762, 1098297022, 1419298765, 1053364967, 1115300300, 707941917} ,{1724988571, 259295416, 982366943, 821766987, 1209960647, 836665422, 904282410, 1579009967, 1408926435} ,{491768362, 708122139, 2096995799, 168385697, 1604313766, 750751577, 1512110200, 1786630774, 334747901} ,{2138102227, 1820669156, 577427028, 1060752054, 80825747, 1823952653, 1334907970, 1667276088, 356272575} ,{18434956, 1412510318, 353355676, 1765115143, 1474389860, 976561354, 1514797755, 698065017, 907944557} ,{1485500093, 65198820, 900551025, 1232190274, 216988986, 941138158, 356404305, 1644319679, 1681586563} ,{749933075, 64453639, 2142497891, 827621988, 233271894, 1412589798, 1433343097, 3829357, 2057292719} ,{938425462, 811326751, 513002227, 2107102498, 598766701, 697626204, 698329785, 252074338, 295792318} ,{881643107, 1927684630, 1276852272, 1556357086, 1377025659, 223245172, 339723003, 1759576582, 975922220} ,{350397182, 1305663945, 1793073041, 1809483488, 1139835994, 1397773296, 874355658, 1288710643, 1010909816} ,{1903658225, 29395794, 1090036730, 126092634, 727206787, 2047469974, 1876660792, 757751545, 998864658} ,{285913622, 778839807, 1754459990, 1675849889, 1205894621, 893518404, 1871225278, 806172677, 601611637} ,{119380511, 1197452534, 1522035490, 549969138, 871131852, 189301714, 811534796, 1983918877, 2002121345} ,{1578345651, 678077965, 2044622844, 2057412934, 1552072462, 1306772993, 835633020, 1764941377, 612220433} ,{2114030128, 2014596192, 918274653, 655730609, 306183418, 2133679106, 815732060, 370224388, 1035598341} ,{77780503, 836624126, 832731568, 1899868393, 175889748, 1131523497, 114575797, 1537644317, 1463075337} ,{250411113, 836919135, 1198769713, 763446614, 1258347275, 2145139616, 18474118, 542232308, 1927559426} ,{1145481733, 671629055, 1555928102, 1393609174, 1954510991, 1666574819, 865252733, 339151441, 1943182169} ,{2078909763, 896220392, 663807074, 590782226, 1773867169, 490724522, 706801856, 2061388494, 408136102} ,{1280515433, 936032095, 726726163, 348546661, 627054786, 1883716064, 496922270, 1248365690, 1263868618} ,{1219392870, 1874529349, 80965756, 194569366, 653540568, 374000021, 57439931, 1355581396, 1897073997} ,{556387109, 1489964626, 936034307, 582542358, 136535128, 114113132, 37101252, 1858047108, 345212099} ,{1278475410, 73197859, 892784666, 931195654, 8963113, 699473289, 1614336656, 2084164742, 1641737465} ,{252991860, 579282841, 547847420, 1190294297, 701813306, 490735690, 1449831752, 1971343372, 1300333314} ,{237806756, 119127330, 1022559994, 538266023, 1750701242, 1753881883, 730823882, 497226029, 395556126} ,{1541032249, 2005375005, 340450955, 1723664456, 233670443, 284897712, 1529226944, 1617931755, 1278007064} ,{491127597, 247645527, 742399393, 1472127805, 898667456, 1901111968, 936003663, 1466649070, 1459082105} ,{1895153230, 576925191, 790708849, 724986580, 1936658671, 987363384, 874628394, 1922105195, 727050941} ,{1942588252, 801624442, 1417476499, 108173759, 1116379769, 1410208742, 1138651804, 139142357, 745557397} ,{1148007632, 2055170127, 1996286251, 1924574048, 1226775713, 794373997, 1396132805, 1972798478, 321547881} ,{798508685, 655449803, 1407449460, 124540662, 263368978, 912842675, 551083612, 1220151586, 480561017} ,{1323189424, 718135641, 1999363857, 2066595110, 1865058350, 684516072, 900789773, 999699413, 763544279} ,{936024010, 136387117, 370706377, 1799190012, 517899554, 882520537, 1887185289, 2105042802, 2143366585} ,{128363447, 933155033, 423047013, 1664212100, 1090798439, 1639847103, 171223074, 960038930, 1828526637} ,{823172988, 1957447619, 1434223383, 936694720, 343410092, 380846885, 1837797885, 830336372, 1341282550} ,{2131161113, 310921412, 458949405, 1310640153, 1944983135, 960879393, 1518505988, 1638156516, 942131625} ,{1946817707, 634516752, 1445408899, 1231151996, 1250640067, 704749831, 232378817, 827696863, 1283068712} ,{681052549, 171433214, 2075742841, 1506844588, 1728501639, 899432463, 2083064026, 1422017273, 479176317} ,{2078895116, 484684663, 2007287530, 1019892736, 1610259715, 1475058290, 417275305, 1410717291, 150141022} ,{574885214, 556213525, 1912194687, 62731099, 1082849632, 1858242253, 510190622, 98912746, 1692475265} ,{715817941, 302550299, 2063742387, 770317850, 2095644482, 159388202, 75221234, 99745324, 342746701} ,{1147089970, 2021495608, 282770556, 292446768, 2022577141, 546272295, 1702187988, 555674431, 861723449} ,{371161603, 1496973592, 128162583, 411153437, 1816326751, 1689189578, 1469681513, 1292629278, 1149158139} ,{863880651, 1408342094, 589239118, 684025361, 1530641413, 139616225, 1077244351, 1488105844, 1439301097} ,{374705027, 261365181, 56783564, 1856991354, 177649954, 881837698, 1680875652, 409691417, 1859619058} ,{564060272, 1004771536, 1536280075, 945393681, 2002371182, 2095840629, 1769103496, 2044194375, 750032239} ,{2023412844, 1701548188, 292764532, 118919087, 2064141960, 1422850422, 9515249, 1081956391, 719089582} ,{1005033287, 1697265731, 1234585841, 985613245, 135240568, 2097123357, 178470119, 815774521, 2104577315} ,{508350056, 146311113, 530707854, 903832735, 1190217424, 1686375511, 1121592431, 1692391390, 1671622261} ,{201837623, 2019367897, 759691813, 354740523, 630753048, 123363629, 1682882184, 195228488, 1421416536} ,{53466201, 1379537234, 149404493, 1277445041, 1463518148, 1468198246, 279780201, 1286896153, 261485218} ,{1797388533, 459057386, 953803746, 1024238778, 413936309, 1225835770, 1598503050, 1538504534, 1378698275} ,{977653318, 1981021395, 1864225647, 27906252, 133500217, 1424094369, 184676612, 1251759626, 339629442} ,{1021605017, 1563269396, 381073616, 42617072, 1399306170, 883585093, 1467683204, 2105908327, 1413857532} ,{803705661, 1715574018, 936307134, 970836790, 200375145, 1541651219, 289355025, 1406433443, 30245987} ,{1988271820, 67429518, 1761493527, 254648368, 1417499769, 1519018028, 2088830676, 1241544549, 1949326818} ,{2078190531, 1087139080, 1753973826, 473345296, 836702915, 1540888400, 1214662886, 692458380, 594342482} ,{1614617948, 1717692804, 548535033, 1986727678, 1452851408, 2011569981, 2099343369, 70841493, 676437609} ,{913143544, 1186365326, 13891550, 63623220, 1651854358, 1296261815, 1170812650, 727580616, 967704413} ,{1608321929, 1998927234, 1877629788, 1669121864, 1383925143, 254898553, 1036796018, 1091494893, 250282654} ,{972687533, 328316780, 836940557, 427329099, 1522900111, 1383740514, 1855869930, 851516447, 1976488631} ,{1656558384, 1655938582, 957516312, 730320389, 1464840090, 1505639877, 108419808, 1551366915, 384990758} ,{729946080, 1117553488, 59998674, 213171452, 1327385012, 862072360, 1905756995, 1156769239, 815141596} ,{472746710, 39987169, 1936201832, 1608853023, 805829423, 948267644, 2033804886, 1541266407, 1018005457} ,{1367733300, 441772064, 500040521, 884817994, 203157290, 2110549056, 1479335269, 1333418291, 917576894} ,{555674102, 458054739, 1876728100, 436052410, 1138713024, 478317168, 420826903, 610885541, 1032277267} ,{1106270735, 1957197264, 1013382828, 327385743, 1726145331, 1033131595, 114915858, 72416828, 412309253} ,{205426304, 1802551393, 295611946, 1643158357, 29907339, 12665269, 1257546743, 1525139544, 2073088875} ,{86118563, 1756613235, 1163077075, 1853126634, 489161415, 2048594285, 982939987, 113233268, 1308523230} ,{1525217444, 1799277390, 115668246, 1200982500, 835873731, 1694948014, 1025674994, 197751158, 551912461} ,{400787236, 231066659, 1474385013, 1315185431, 2048800950, 1808778044, 1270809427, 1622021408, 192603269} ,{1656018907, 20444189, 1014974017, 2004538483, 701880557, 192456848, 1806607579, 1264851648, 1118175939} ,{47740694, 2131914178, 1671343220, 1316423662, 395631557, 1470959841, 1263881458, 659316787, 916489958} ,{203274205, 1873604315, 2117222029, 1901592453, 1056376261, 191845608, 761585878, 1420289031, 1740391106} ,{446223657, 70145202, 1346143033, 145889738, 1469473743, 1739328022, 952126590, 886387255, 622367292} ,{985915762, 1477948858, 2043544189, 1742194409, 1698482616, 1347626706, 68636289, 537055702, 1725261485} ,{1884351556, 301644298, 1108428368, 1499186247, 1300626254, 1360108171, 1813506514, 220213873, 471932122} ,{1691079806, 84974232, 804378893, 28985994, 2141454419, 137341394, 112064311, 1170879491, 765278956} ,{833970317, 1656414714, 1881788398, 276657115, 869798074, 1042402556, 1087450774, 1399287897, 57924763} ,{1681702018, 1501192648, 1894979791, 232693144, 644838785, 697848577, 1328610242, 2129911571, 1080485741} ,{627698545, 1283607364, 1733685630, 1911855924, 637920446, 1600783785, 1710282217, 991134601, 398397518} ,{2121356651, 1398674318, 619321425, 467452537, 489160863, 1837315496, 1694247919, 1914575230, 892801356} ,{969943396, 1830370542, 583573841, 1517182490, 666663683, 1061393624, 641582802, 1507376874, 1642320330} ,{1620932285, 1332573943, 396595440, 932059820, 1687446792, 1302118555, 1551466681, 496420664, 1943196289} ,{1209613320, 1044387226, 1984468763, 171547595, 152552431, 621022564, 49123948, 1013347672, 1086370934} ,{1157843205, 1388739227, 1203597480, 1199989920, 1898031004, 1520365017, 349347177, 755595861, 1740946635} ,{2098111121, 1466863394, 1871036769, 338004646, 1431877302, 839898684, 2006568959, 1675814975, 308471149} ,{1098612978, 995897770, 118213194, 1321261782, 6748148, 396634863, 301513539, 1108357861, 1873680726} ,{1918039191, 1042136620, 1004623393, 1362332275, 1486958283, 1551938909, 1427812416, 433101200, 1335372863} ,{1706736313, 606834521, 576157191, 1683375779, 135133728, 1405341182, 299036142, 1793461494, 1010660758} ,{1115429275, 1709702319, 1056158010, 950153845, 1956033284, 782431161, 889681286, 1283010489, 325863607} ,{1416716424, 1546822756, 676541530, 1890273073, 90931706, 1480468737, 1983738572, 336971114, 1476230369} ,{775757276, 962600597, 1886998271, 104997002, 1457665031, 261588821, 1902816366, 83256368, 140035114} ,{314253602, 1953163559, 1879073620, 692225200, 2022506351, 2057923163, 1480923060, 173902129, 780524600} ,{971452087, 2119529524, 479718582, 628383116, 1402372015, 383656923, 667561206, 1038981788, 1897721848} ,{833832024, 961803108, 754816731, 1597341125, 412031720, 1099458629, 1336373852, 270616818, 102567469} } /* End of byte 1 */ ,/* Byte 2 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{1841195089, 1488605044, 70958207, 42299766, 358764160, 1810554404, 2014244542, 1699546188, 622916453} ,{1492876117, 632513910, 622638443, 758371028, 1744353458, 1935368369, 911627155, 2049914259, 919509285} ,{746814505, 1549606791, 367684326, 417383095, 1855988473, 1825092626, 373147001, 394155827, 322169563} ,{324543768, 349086733, 1476201052, 991873218, 2059583029, 1094489873, 341510320, 872344606, 251023864} ,{1442047988, 1610722385, 529817400, 1118058889, 911667037, 1964999048, 701061192, 13694013, 155254856} ,{645176773, 1438037227, 1569378510, 1588035248, 1915118326, 460606080, 1408166382, 1309925379, 1349270261} ,{539845751, 1636173371, 1659721947, 474005036, 1213620331, 480859717, 1169298499, 2104860026, 1121164416} ,{1305529819, 874978240, 592466250, 2026365239, 1890475564, 310668621, 591106415, 1282607082, 1656311450} ,{1834769364, 995490617, 1272115049, 1805509723, 458070657, 782818069, 1964496366, 1762181089, 807674917} ,{1073525991, 439750824, 339986086, 1769424051, 1506475496, 1245132733, 1300054000, 1314405151, 80066052} ,{485553444, 638335632, 76219088, 1842662032, 1618638231, 897888656, 1479944976, 895184561, 1412697817} ,{740858403, 224058730, 415653557, 847981676, 395051016, 286880836, 2033232182, 351098777, 2097527544} ,{632040916, 359858413, 1117674985, 573608425, 240752102, 1856631010, 1541595955, 1644662576, 1383107118} ,{1975673105, 798581198, 1558633908, 859185964, 607092008, 643966523, 1655368172, 1568297292, 1346678144} ,{525760283, 353253881, 454151010, 233517642, 369269384, 491360329, 1248542536, 1514514672, 1127679437} ,{1389695502, 563722919, 488978747, 186097697, 1446458569, 292077858, 172003132, 1414386789, 1917790834} ,{1903581582, 1143657322, 194107071, 961839963, 558339732, 692152133, 1347718195, 2100751330, 896818590} ,{451363390, 1446595021, 1891400395, 1566938630, 199138278, 214876011, 849826150, 195757654, 1633861652} ,{1977696358, 1939041108, 2008019528, 166446940, 1832229020, 1032099018, 1432036507, 416853439, 1578956134} ,{1879262282, 151808584, 73624403, 174928238, 368627535, 1856298797, 1249280924, 167176644, 739612439} ,{1563781965, 168922865, 1976224973, 1324664547, 1626193075, 158470089, 739644436, 636292265, 1940361694} ,{1424280874, 142479859, 959931056, 304178388, 837158781, 1147220106, 1202649113, 1279115569, 1355835973} ,{1562538882, 693321316, 513353543, 1514756137, 1406313158, 361181072, 1675430356, 498858783, 1553835131} ,{582076310, 1065498298, 525362734, 1883042636, 1169812722, 1935513413, 1243797410, 373971719, 931548438} ,{751733178, 1101374736, 365007970, 79098941, 1280219555, 623519759, 39401872, 1353798474, 162144995} ,{877785889, 1823359403, 1431882674, 222696380, 2030557112, 335018336, 885129527, 664520322, 2012723364} ,{455199638, 977719931, 172415300, 671505125, 1099944846, 603651693, 1357001578, 2129289855, 1965306132} ,{392538003, 877479765, 445581704, 846465215, 314338622, 1062214011, 1254922806, 1051487327, 697937334} ,{500317088, 2144337988, 110031159, 777954021, 1045498034, 680736819, 1911882737, 1934156163, 690665924} ,{549414891, 1670622007, 2027850200, 1021293350, 1291119561, 511959154, 1204419402, 814494924, 1435543067} ,{1605693737, 1006417876, 1738651896, 79390692, 1262596208, 122302521, 1803220196, 1166250541, 961782701} ,{1812517847, 754062902, 2135988828, 331784467, 1737239903, 871300932, 972040043, 313692999, 1520073171} ,{1448007831, 1131261544, 133175049, 1524734127, 1632517556, 1805574809, 467591507, 2112696376, 463577095} ,{782524778, 812448509, 285448085, 1557059652, 304216489, 323816472, 1460602508, 1904842233, 1033386078} ,{1588441027, 923462523, 1631113353, 819228749, 2012617470, 1119127440, 1239418749, 1351799828, 250997342} ,{342480107, 1634867284, 531893750, 1631111684, 1292843532, 119134876, 60699814, 965621655, 418005041} ,{1220821423, 1590211822, 452934149, 1486950818, 1104703395, 1485046871, 1114283750, 23209682, 1311328371} ,{1740471691, 1221993589, 568939489, 1577707101, 424060623, 1793944503, 263181460, 1485593590, 977844690} ,{293866215, 1117248586, 179366407, 1584662553, 1832817430, 477524499, 2052342297, 544220202, 1857088724} ,{1284138581, 1667559303, 876329549, 22190656, 1554370381, 1325484982, 627158170, 876427120, 1247620889} ,{1686630382, 1873988732, 1110640557, 1175496248, 774470078, 1498416463, 908317342, 1072333543, 721275885} ,{2076042331, 990839792, 1249625533, 1426217254, 480932128, 1445340982, 958504554, 446495566, 968903774} ,{1686878895, 1110833275, 656168947, 151618172, 754837880, 2131038397, 1735666608, 403128318, 398994604} ,{1320790320, 2003025039, 1921313831, 922170587, 481985270, 1540748427, 2133671158, 2108120964, 173639662} ,{805783706, 729520412, 116351572, 621287264, 322156780, 1091741209, 700609790, 1449815661, 2069579128} ,{93285413, 1128783683, 259506273, 1080900733, 706702247, 344514512, 1427416382, 796053364, 857580299} ,{979303749, 126874296, 773222332, 1348489216, 1602833254, 750485625, 1047423275, 445020158, 1232327834} ,{1739892849, 1799552983, 2128356499, 1463078657, 594453625, 9474480, 2125040431, 1900430512, 1217793134} ,{1872599771, 236021484, 337679156, 568258554, 464769298, 761727570, 1144816330, 1241314662, 245057531} ,{1476987404, 1721461484, 652234785, 228105294, 1297130961, 1634012622, 295078732, 1413317926, 1446714781} ,{342858906, 284881741, 1365430574, 1211297317, 1375247513, 809126218, 867488033, 156057229, 139511477} ,{1240025165, 1245287783, 1897423078, 1197289782, 139102074, 1348149887, 29706247, 1938511872, 749113107} ,{1179435316, 355217794, 1627640064, 1755202373, 1853625195, 1660402351, 80131600, 1727156313, 497501209} ,{983197421, 249953478, 1660132631, 313299437, 1962637872, 13208563, 1977632181, 1875986003, 1628165318} ,{906865151, 604062766, 112919691, 220615884, 422856470, 1467775969, 565565743, 344790421, 409554005} ,{266060520, 394834030, 1624867746, 532800402, 1290474441, 301712848, 230778061, 559402263, 1416806196} ,{557963095, 2136316551, 1952273170, 764093179, 1476578086, 1404158556, 1031320946, 1047375942, 1324744871} ,{1216951903, 2082575172, 1560974073, 656296158, 1264168173, 1978764553, 116787203, 937543184, 1365524788} ,{1719177888, 1076011395, 747260717, 776444301, 307920850, 1333292593, 1676472680, 189678863, 563655795} ,{1887247074, 1650133450, 1424944414, 1876460642, 1115786199, 345351880, 1304071256, 990538973, 17478282} ,{1730135031, 1980738826, 1944901245, 656702812, 414750729, 408823924, 83825954, 2130602281, 1923897760} ,{668916699, 478536060, 1258895922, 1489660770, 2139620432, 137041033, 459312284, 1758398200, 1524181062} ,{1011527388, 1895678486, 1389246755, 1960133989, 1746955757, 253153712, 441649497, 1483434118, 1013005451} ,{867081930, 1775641679, 824132775, 1432974174, 743051653, 398421180, 684685362, 1408082662, 464971086} ,{147733520, 1973160383, 362923842, 1234315349, 1583098090, 1921453651, 871531070, 2003021298, 667292271} ,{1203921189, 147835735, 1899496731, 1364878550, 1746348857, 692830960, 1657005895, 248055575, 1122658494} ,{2082649937, 1972830532, 1348580990, 52169871, 1117985211, 921780507, 1777577089, 396385531, 12882634} ,{751905443, 36484442, 817135492, 927447636, 1164455887, 95440084, 1213228192, 1791307957, 215375555} ,{1178878744, 1818062452, 151784350, 1012998094, 515964936, 1834951621, 1764409112, 1683945093, 1322448524} ,{1322183253, 243744077, 1338085685, 2133527251, 42601299, 1176844309, 1892557818, 1256383650, 676608845} ,{138632274, 1956766080, 1594663912, 1979238278, 2037499459, 1263999690, 78522197, 1629632962, 1681876287} ,{556870760, 638893680, 1655244131, 933184387, 1969439507, 443988914, 855602917, 311600819, 1753005038} ,{1468461893, 711632964, 929757561, 833768594, 41945989, 1593535672, 1660510264, 682086731, 984331639} ,{1581852863, 803431249, 1339746509, 1678641562, 1606383197, 571905294, 495334562, 1720022830, 2078117030} ,{676175681, 1081948722, 2047283343, 1970041717, 1786268618, 952661416, 1284355118, 1305769061, 1436623629} ,{152029652, 1827655445, 1364390301, 2005111179, 1789037635, 915451608, 1902975697, 1016947138, 1218357772} ,{856345469, 1805764693, 1779780645, 860782167, 1861920983, 324927773, 914436648, 1399722799, 1264643821} ,{1927279076, 1929341655, 137735671, 691365220, 2021585694, 825297249, 815249275, 2016640709, 134645440} ,{30716072, 1007026531, 1810865950, 482474258, 999861455, 137282447, 366952723, 676163352, 2051694470} ,{454239280, 1481001298, 251909000, 763795810, 1304138521, 1322017550, 862944681, 569139354, 460694653} ,{914712528, 1688824623, 2122622574, 1021216209, 603355638, 80185050, 1020815983, 986053162, 472985396} ,{37970339, 1314625165, 978362774, 489821368, 26775807, 1193961364, 989973026, 787575903, 73246788} ,{970461646, 1595274922, 701361307, 1875634658, 603242810, 1899858440, 36410853, 1111271583, 536420303} ,{135401189, 1718820340, 2000965898, 279971586, 1930979424, 490313918, 1614607662, 2077090916, 774500874} ,{160801739, 197390763, 155435185, 993584385, 1417856956, 33735972, 1432117783, 814465708, 1991787417} ,{1461338992, 1194482472, 134276916, 1932435944, 1459571335, 1971504013, 840114882, 1853144100, 810970900} ,{2069130977, 904710628, 605018748, 1335019134, 1957906491, 2005875886, 547207282, 1358244950, 408778935} ,{1268432390, 1567439653, 253608082, 986690816, 142140895, 49746493, 1632061180, 415469403, 318849151} ,{1509332824, 996726384, 76881964, 355427235, 1460638433, 897956625, 299468057, 2075974515, 197176441} ,{917188435, 265008294, 20518140, 1704710779, 407835830, 1554605077, 1301767463, 1571214714, 1361149233} ,{1511846443, 1826918069, 2102036298, 1954709944, 559966670, 1619894361, 1642447454, 735608241, 409877968} ,{23807830, 1142103193, 953440212, 49207096, 335209239, 60028629, 1898797833, 780492225, 598907872} ,{940931994, 1579924795, 1734939035, 1453985671, 216452538, 606353503, 1844837856, 378795692, 1372364234} ,{787049764, 533949095, 253356031, 296070825, 361375779, 687978407, 764182865, 565477734, 1666697125} ,{306179896, 250555060, 2145215424, 2087832416, 2097398993, 1861379014, 849487675, 1253640818, 1443876342} ,{1328518626, 552285828, 2067258785, 1460331229, 1306741140, 731549403, 2133339041, 821036747, 1770997555} ,{1823247615, 496645825, 387909514, 2000323671, 1056023761, 1469121895, 1484329586, 2125053410, 1385996891} ,{876359590, 174350937, 1161546871, 1074198513, 125747627, 941366904, 1261565184, 1062147428, 612989486} ,{1635961441, 1670355854, 985019829, 507510802, 2103853521, 1205143532, 1851709781, 2114270208, 75368760} ,{1519270373, 1836270955, 1399997774, 514951442, 128948400, 552907048, 2065473766, 436280718, 1908985431} ,{1993338614, 800033158, 518941063, 1388978512, 1340116023, 1605603196, 1674966862, 1017488475, 447124145} ,{828521457, 668951900, 561730311, 713378444, 2004660142, 56846843, 863956463, 733116229, 566953456} ,{2103148147, 1424471835, 875243471, 1083834052, 2130587624, 1439989260, 371247230, 2027639345, 1957784765} ,{1162401808, 107935631, 449283122, 1026979813, 1910153835, 560088457, 2078613262, 514213536, 1300682368} ,{980153351, 203399679, 258102351, 1648075689, 1363984, 1041462012, 1182080835, 697476485, 294118126} ,{1711888320, 1476282766, 2102565422, 217541558, 2077835353, 877121854, 1151327679, 1854686335, 891824224} ,{853743333, 866374259, 1929662594, 1268668187, 1788171707, 1963428463, 1564585179, 613689192, 714387031} ,{446589943, 1410431868, 1817633832, 1798024153, 1312877773, 1576247935, 729076143, 1864970050, 1733880079} ,{2060169803, 1309161064, 1123339029, 694960459, 1776409314, 910224180, 804760566, 1057539355, 869399628} ,{529820140, 1986814834, 868211731, 364376587, 2077256966, 1801377785, 774600418, 2026001723, 1690278910} ,{1930207021, 1137894750, 1850583184, 1376020870, 105358096, 569696475, 1114318506, 1630254487, 286388302} ,{1473503410, 1383491993, 1533168560, 382468184, 2093436240, 1941507623, 1175763439, 2011060445, 1926435996} ,{718332808, 1413880585, 1751143915, 1606998801, 539115183, 2021457561, 1151007951, 237778377, 133770204} ,{751978999, 208233834, 1707126069, 84335917, 1964603588, 1985150649, 1978690983, 1861400267, 2064732771} ,{1495236380, 359595642, 1859802499, 185954440, 164732207, 1983746556, 735994475, 1538969109, 592460660} ,{158370092, 525727336, 1203485279, 1192554958, 2133256718, 1758787992, 1876399670, 1609556897, 1247789257} ,{1560865692, 1405699866, 545478900, 1560629947, 904417784, 403415, 8466793, 806047293, 2018165602} ,{563789743, 338837688, 659793634, 2036661662, 908748424, 463633825, 1034392707, 278759844, 1240679208} ,{2008495944, 330036915, 196883614, 226661074, 1523010446, 56560064, 606584321, 121730744, 1909901415} ,{1510754894, 1202744135, 585447182, 1491239263, 350224335, 29734828, 1434033451, 22959914, 1845426918} ,{1884689066, 297271460, 476874964, 1547143234, 1981804740, 281875381, 485592763, 1699308314, 742801736} ,{238589965, 265350022, 1852171214, 1794589718, 329267702, 718714446, 97382605, 149893071, 1457921133} ,{1776894547, 755091066, 908562437, 683060349, 91502324, 884797251, 1779999405, 596931469, 848943951} ,{739282285, 452032584, 122625147, 15610449, 1911753770, 730154696, 1585057921, 1715698006, 1955504562} ,{1546322520, 1141225323, 1962092997, 292375657, 1780237179, 600012441, 1859462825, 178434580, 1717785076} ,{2100580412, 984283576, 1302446131, 699204888, 1894328464, 1537570620, 764296414, 1934939696, 787602841} ,{153447303, 1986647467, 247535385, 954426617, 1339513336, 107579811, 1390998457, 145454610, 214862399} ,{563065758, 759975544, 611258691, 1741373803, 429994382, 348154164, 918901463, 1741880838, 1753071371} ,{502349238, 1098612146, 305115496, 731843443, 121641219, 708041548, 916294413, 1807004769, 455557191} ,{1062437260, 1380696098, 884917196, 121968139, 1159455889, 1008843057, 209699579, 2088682751, 1609532932} ,{1534052441, 221620131, 1189554162, 1529267231, 1476887031, 1939559606, 1154953394, 480788449, 127495068} ,{1779532773, 381764752, 1727837803, 1925537472, 134916088, 1154792652, 464666916, 1333407322, 2030455537} ,{161396139, 25249205, 615995692, 2124728390, 1320591329, 2008870021, 20265367, 721771141, 1175345612} ,{1442292008, 1650580482, 644020039, 1530496257, 386999483, 2004771738, 1875085213, 122597156, 2062077396} ,{703880074, 931390865, 1145843531, 1473918377, 2001388672, 40787745, 1668438732, 30971362, 57813413} ,{1429580271, 1820947110, 1168518302, 792026407, 1019543206, 354226374, 400293977, 329800968, 1763106326} ,{924318276, 783007922, 1100352505, 1349364251, 1109844353, 29999796, 660355847, 1869858981, 562767892} ,{56986627, 191193126, 443172431, 634627920, 11215130, 922867083, 442291499, 372451713, 1276721401} ,{839623712, 1572782749, 1483623098, 2024557832, 1305865176, 419920991, 370283110, 821216920, 1018439254} ,{772746857, 2004699612, 1619471940, 1918460883, 1342091509, 1412115665, 1662102369, 155609048, 2146621801} ,{478534481, 1065797931, 33433980, 535970305, 322925324, 1005685045, 218315126, 1103805105, 473464449} ,{553400443, 1389710483, 1881928365, 2000229417, 503131984, 1518142875, 813707447, 319567440, 391283380} ,{1026385271, 827347753, 1909917345, 1796988189, 747006415, 1649228914, 688442131, 589665802, 755371749} ,{1635453806, 1845266000, 1354112253, 72016742, 640580788, 1917102262, 897874826, 402005647, 1251498088} ,{495379617, 2100398979, 697518525, 1046208605, 282288728, 1604893566, 780050871, 1954306806, 242526440} ,{1847866473, 715464133, 1780210397, 871638771, 1195188203, 1464575238, 1052860699, 395763344, 1486167762} ,{221160328, 1578636281, 7111192, 730176523, 1179389498, 212739978, 1865387482, 292039269, 2043543395} ,{2054880983, 1223236109, 1665795018, 234123767, 1463750824, 826150312, 517648950, 1126688458, 854661499} ,{799159333, 1189019838, 534771341, 2141246748, 917029486, 157694795, 2090190898, 1194931569, 1465171078} ,{804290582, 51904006, 1138156647, 694072575, 1816224782, 2132634314, 1894973190, 1104573782, 1644372323} ,{777581920, 1906992840, 622861937, 808530898, 1698549172, 1634839168, 909661687, 1241768584, 1693426897} ,{534547955, 743116108, 121498013, 1218370981, 643050580, 19153412, 1403284507, 1478840680, 125456342} ,{2126250901, 881058343, 145579477, 718078036, 709066802, 244244710, 1133835652, 1609725746, 789907885} ,{660960487, 1733744867, 1455664722, 1451993915, 1601249940, 106544326, 2062619302, 944825524, 1700057736} ,{238804718, 1092810976, 94446494, 135322235, 2119351350, 794480960, 1963861805, 2132325162, 1587785908} ,{587542815, 1884460516, 941310619, 311645860, 1939387927, 1141949619, 434254070, 1233398759, 1400324179} ,{170942760, 1250046262, 465600353, 778353873, 1302694058, 1190216296, 1767011567, 911519101, 1091036555} ,{1446526800, 748704866, 1211121267, 1918792343, 269471641, 242420486, 1770519191, 332742985, 689882508} ,{1856195390, 904397949, 268446889, 894450941, 593834102, 1328471386, 447726998, 522998127, 1162404034} ,{1053705993, 374811456, 122108528, 744484116, 2080862601, 1401878729, 597818614, 2038486154, 723147353} ,{1779319604, 1933882239, 524952913, 1900084851, 1837803166, 1740617107, 667356202, 1881258545, 1716233587} ,{166846334, 495011515, 2035263688, 2074589765, 849524506, 1939853851, 389947199, 1830734063, 530890131} ,{957381893, 307330183, 1911492522, 1643575850, 1260600266, 1368110891, 466286069, 299259720, 1155859364} ,{237933605, 1532327678, 1454353559, 2085223733, 480560506, 1985921828, 1618274378, 1304079336, 1742119656} ,{1622939996, 970136319, 491075762, 1447528308, 941997311, 2120094238, 1681778061, 966280169, 1570166893} ,{1091937897, 1962544578, 1076663563, 1756819218, 54767773, 1258243767, 1387897521, 1378429268, 431310988} ,{1285142711, 894764556, 9189720, 797384461, 960511603, 1626080613, 835925876, 75313675, 397280905} ,{1509225026, 338174255, 294150781, 2040369534, 2126673574, 454190590, 1208925898, 1402497105, 25186852} ,{170689542, 115472479, 1076745825, 264629872, 560075346, 270479504, 1411574728, 629572176, 2057712002} ,{1319175593, 173782795, 1976747188, 1555151851, 106001477, 1107935708, 300479502, 1664732390, 597086964} ,{983674610, 1836797113, 1067661676, 1983569788, 1951388645, 418769176, 491595735, 1274640369, 324148510} ,{1685327376, 1024196651, 736006173, 754083683, 844266509, 4835340, 734313804, 926570581, 627340988} ,{1567023633, 1922188627, 1458449358, 292171537, 300543453, 622111079, 1133336298, 1828239115, 1271272498} ,{1775617307, 280890643, 2088229886, 252405253, 9068178, 411043862, 11629252, 1536825203, 1940785551} ,{1032954543, 1542764846, 1986907277, 2097922718, 282400785, 1503353303, 1782856168, 185432871, 1398615286} ,{1505670470, 2136946339, 1681275606, 1421604988, 511446568, 536219851, 527231017, 914951585, 728084425} ,{1194766024, 703550080, 174584973, 41035303, 117080296, 714878701, 31751610, 1103664063, 52920421} ,{1338677424, 1816126915, 766169402, 866572872, 1406421157, 1777086546, 788434205, 1127491812, 152805564} ,{1582417159, 1221168182, 391271617, 722842207, 2115615974, 559947994, 1698528372, 1416847465, 9106626} ,{1062220436, 145155895, 113613836, 2136392059, 1074402763, 1491365660, 1739119502, 1742448598, 249797389} ,{1144232945, 66935242, 2122366861, 1125317187, 1919769631, 203324623, 225842143, 1368394984, 1870328116} ,{980675056, 1589488519, 155502110, 475170953, 1530179907, 816755520, 2017121325, 306049401, 1530436799} ,{1889184277, 1305008335, 286581012, 285517757, 326920054, 321671291, 1355112066, 455473628, 1026497743} ,{1935279877, 1084973944, 1619212658, 1514992165, 1301576301, 85503440, 1589286454, 314496812, 332508958} ,{74167153, 82830705, 1171666650, 1152308050, 1956634626, 1452736572, 1951699038, 254465022, 2082320233} ,{1142498431, 155945186, 2090109209, 2099606270, 2116360213, 937186741, 1869468032, 586299000, 1911155563} ,{1022273152, 432678325, 1077313921, 1424920362, 47944993, 1363372910, 1673875226, 1483309127, 280406075} ,{355021005, 2030254560, 1834387765, 1678289267, 1832880130, 295384473, 979383923, 1657627441, 1360084708} ,{347408671, 621218114, 1745996542, 2064460428, 1567014174, 1680741085, 1694608074, 827102508, 2136521755} ,{1257016779, 1854245595, 2107220657, 1904312837, 1195712187, 1306915540, 1649800218, 1112134396, 376968419} ,{1768785775, 1092532705, 1769087158, 112964778, 1556421611, 2084070419, 932288329, 753031504, 1722357314} ,{658161389, 1503837147, 2050509639, 1317970985, 1633643402, 92364655, 1481311098, 584763736, 999479365} ,{1269434329, 1711869176, 1858920728, 1271355682, 677201235, 86451155, 1291791912, 2107718701, 46873872} ,{1704274889, 568991656, 1308684190, 274966581, 1016683436, 938863086, 1624327204, 135948789, 1935469001} ,{1277875188, 534012130, 1689848462, 286022244, 891309647, 945749273, 607216940, 73496315, 328715025} ,{2011874447, 207409355, 2060574340, 498795029, 1088366910, 254872575, 1434498027, 748339996, 396951182} ,{1643050204, 2124664152, 570342723, 1760418584, 2019141619, 1979093310, 951401888, 138937735, 1433642500} ,{548422770, 1138816154, 1893239673, 1436416811, 763631205, 2020610008, 2045019629, 1687027582, 387919916} ,{153105827, 1449639829, 2069872333, 1729124786, 954526160, 188419, 1962432204, 1471770244, 868903304} ,{591577190, 1779678838, 557937569, 862595645, 227417900, 498884998, 168579455, 266721924, 563561956} ,{132021276, 2069726937, 1181282741, 1663346862, 57590357, 1594022761, 730748957, 1085462180, 1825162241} ,{549795487, 122242455, 209279321, 1281499185, 1267365894, 681433311, 1151893668, 1737329860, 659300443} ,{292769924, 373995874, 951810436, 1169424274, 664303011, 701023130, 1780194164, 975853046, 966999539} ,{2035757612, 1547413162, 1826121020, 1806236848, 288627897, 2123388186, 1727180249, 20607755, 1892027918} ,{268524954, 523829708, 972400910, 1955056584, 960788122, 1933084527, 1047440602, 1803200738, 114919499} ,{1745464768, 816395960, 1236767844, 1258291356, 1982354365, 1064476750, 1937942805, 1116882286, 88207012} ,{426146367, 2112614410, 1137255932, 1818453051, 536403522, 567598811, 2119712354, 432308633, 1780959871} ,{130439740, 352375589, 354446776, 393878257, 586466118, 306721680, 247094188, 1166388750, 1251971879} ,{1513710315, 1207655689, 68166486, 1274847043, 1308761000, 140986167, 770440604, 1434689561, 913993836} ,{1184303845, 1205126870, 1352385373, 1683783166, 1740111402, 1327491436, 458266641, 1630764024, 1598840723} ,{2115094671, 729585700, 2030832568, 1350103189, 1097449856, 1170418571, 654818427, 1872536016, 12565521} ,{1991654077, 607155134, 913119183, 1805809782, 90501714, 1735437307, 1057335402, 1650490756, 1230816867} ,{636645626, 203895418, 1429610531, 539084314, 1787396119, 625314543, 1532872988, 861572227, 89981469} ,{656406810, 1301096965, 1859175605, 612034218, 1544223338, 2145096405, 1645262775, 1646804982, 1182589959} ,{1074062055, 68558680, 484813967, 1932330853, 1698689305, 1303249696, 1746663491, 354630990, 500826635} ,{67704871, 1866851541, 1731958483, 1221308147, 356789052, 876787195, 888212053, 158814652, 1589694112} ,{2089975909, 70686675, 322053773, 1963312759, 1684049776, 305562748, 1074100270, 513635841, 1611651414} ,{586413818, 669005964, 274737799, 1902235547, 1702160706, 249012763, 509705879, 1616464307, 1888017475} ,{815845038, 1669050013, 284356522, 835328645, 1118304782, 1168755885, 412162874, 46826545, 1638786403} ,{1081047557, 1185722470, 122018601, 1004005526, 1997469531, 485233278, 1884922236, 2062269661, 1383049332} ,{752924987, 1456426753, 1320500395, 64674997, 1479535882, 1944522284, 1069414325, 227250895, 863127157} ,{1947995851, 1482658762, 313304726, 1564051543, 1936521888, 1981976220, 1767096696, 516070970, 682155225} ,{1656478333, 938367637, 963468552, 579355364, 1545939228, 1754464591, 1530521951, 1224788687, 1868882464} ,{854309253, 1016981493, 1397679075, 1860509229, 1709149955, 2113837625, 1194706818, 1227613478, 1064736873} ,{403502657, 571592508, 1660265130, 916773041, 781964530, 108244031, 656012078, 472420449, 1582985014} ,{1786587529, 1969262123, 76232485, 411195143, 743324637, 162840482, 233986681, 436673987, 1342108018} ,{257593948, 296358305, 2138313523, 47167270, 1067411648, 1065193882, 1980244029, 320449501, 1062628505} ,{1162619597, 1392769274, 1685134533, 1210180229, 736897977, 663729868, 164338891, 186293976, 242198356} ,{1242943523, 1643322426, 2126792827, 781892554, 1095945640, 1530414740, 1591147668, 1083984329, 1682918766} ,{1984571918, 965085189, 1052495235, 436208962, 41632156, 55736653, 281368130, 1411471940, 1695100624} ,{1644349700, 766931040, 475492806, 884232419, 1083784872, 191408645, 945370692, 1841842865, 976010781} ,{206936455, 2140092514, 1539024672, 1358156901, 498646637, 1530793843, 1325369374, 250032526, 1055380347} ,{980875179, 814382915, 1450045674, 822743461, 1703689877, 363702420, 1322749827, 1111400298, 1099951408} ,{1808601811, 974357842, 2132812671, 839229540, 1202809978, 1522463214, 955705452, 1793222820, 575181991} ,{1923396953, 2015177087, 1726477436, 872091206, 2103387400, 590368877, 1197725782, 667635547, 473850588} ,{1424122035, 1738656968, 1694703567, 565424182, 129617386, 703343344, 1878030126, 552358732, 1224782018} ,{1208788459, 1634298789, 1366067376, 1139729807, 635332550, 1103848646, 1623965444, 1000838631, 1466155035} ,{2142176669, 1730570839, 24090258, 68783503, 1409144156, 1947176646, 2134307173, 717640363, 1480092576} ,{1272516130, 265166903, 1548004215, 1821210346, 1577465430, 893528081, 1342643569, 170169091, 551538607} ,{962730392, 532463400, 605113504, 1679747543, 241821752, 1483997951, 1266830227, 1987976240, 1693513715} ,{273527163, 1333082763, 1388888427, 1142494874, 962255772, 1559470803, 1165196193, 2130540577, 2041927670} ,{606954392, 136640759, 1813320939, 1842501640, 1581437742, 1212696910, 315135197, 1410081774, 443664027} ,{1351910748, 1838322085, 1261381362, 448513022, 447944140, 76663135, 697678353, 1600189969, 854245292} ,{806008417, 842939634, 1134622161, 334753166, 217837324, 238714141, 1158446394, 1432535481, 1971307735} ,{1124641897, 1423632689, 1276669320, 449118283, 258274760, 707885478, 814161038, 2096897202, 1886592751} ,{1500808344, 57401737, 483002986, 1061671257, 273799836, 2109099193, 1140682639, 555058151, 1260645489} ,{1233251419, 1859138784, 987402314, 2124395307, 974583289, 1320126605, 1062228914, 1108370093, 1982518127} ,{2085761726, 298150413, 500689843, 1583578945, 335253507, 212378903, 748479774, 677365930, 1171917255} ,{1520101139, 128925241, 1285468319, 1138507044, 1764629093, 1636999307, 1464137838, 411310436, 1191173711} ,{1181727236, 320402870, 1998412667, 901930693, 851105791, 1624785255, 892866916, 159930543, 1117252648} ,{612437270, 813852641, 1674160834, 607340654, 106179468, 1673241420, 1088526337, 2051717536, 1106065248} ,{259299661, 396225387, 1383674226, 431194328, 1660563502, 412844688, 2098172044, 126330842, 1831096072} ,{140667663, 1981982315, 411913397, 1198119490, 502805728, 107387533, 1770192131, 1759668250, 960663664} ,{1914060477, 2079098059, 1046429582, 1868127660, 1328382868, 1087054345, 1202831473, 695517368, 1943945248} ,{1878991998, 1289747012, 216805625, 2094455703, 1271904144, 417351448, 929726077, 1446929029, 1731846625} } /* End of byte 2 */ ,/* Byte 3 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{1508012513, 2017431394, 364496407, 468402026, 1594355142, 738007738, 2038928981, 2009634712, 31989534} ,{598133793, 1796514457, 1581669433, 335560125, 1089786967, 835548353, 537608233, 1543261614, 2052883955} ,{553642099, 555313442, 1833568133, 2030403321, 365626859, 254164525, 133997814, 1339258992, 1185376400} ,{1186955238, 719854390, 2131409688, 206683826, 178478510, 89671692, 1241031137, 1597684028, 478282559} ,{1929548276, 1508588234, 1917917787, 949267285, 1522736265, 403749608, 488115236, 348930223, 1722733234} ,{2040820581, 306610133, 248429574, 1885326886, 1801077327, 1847180759, 1642025773, 558998589, 424311815} ,{1104561090, 1302420874, 1876321703, 1951393463, 935145465, 1989566316, 750476763, 2010849143, 553470812} ,{1799823648, 126849880, 580491968, 1825467117, 362792278, 355905062, 670673561, 1698105553, 1164319705} ,{1623871533, 1010078678, 1228793193, 613051372, 281165259, 1837588012, 1874127577, 1782467820, 1804785169} ,{834976593, 596587738, 1398503430, 1136946059, 101805234, 1700210301, 910804007, 1569783933, 948245228} ,{296465561, 458030599, 994586473, 303722641, 916851446, 461641835, 296140919, 92699137, 562284201} ,{140393748, 255646822, 25850420, 1683382737, 1456409330, 1065502469, 619292197, 1144507921, 1163205740} ,{732729554, 1012892054, 735339772, 490003985, 1092976204, 112191616, 266575977, 74579846, 1711118352} ,{1410834007, 278969045, 1587020442, 542501577, 1355323411, 536796419, 1291767513, 1486247318, 620388485} ,{1889406890, 652996057, 1118800996, 2051195787, 370301760, 1065445469, 1712262723, 1774026381, 2004250356} ,{423681515, 206237420, 28633995, 670066325, 1317505971, 1668561625, 2128991985, 461790042, 1370627821} ,{1126603873, 1677269140, 1162425614, 656931474, 1776025067, 531338508, 1835450595, 1625136501, 1314601474} ,{284857799, 1206025074, 1055398737, 716918519, 712087226, 998950662, 920514370, 1468480313, 1593983487} ,{293748584, 1683693168, 1215223307, 111163627, 49661888, 2010377893, 1907455415, 1054166967, 432322726} ,{338789418, 288746733, 1774405266, 964196526, 425994225, 2102905719, 931014082, 853089962, 342161103} ,{42615987, 1469476379, 21554181, 357747255, 767108514, 58696413, 1396874807, 620460349, 1516315033} ,{2006543520, 544758069, 773887134, 326882602, 2053622741, 1989951484, 506900844, 578964036, 1421488599} ,{953216565, 129440452, 1933276544, 289819843, 1872965183, 332685566, 313598043, 502183075, 1160330195} ,{770498223, 1605057535, 518964655, 2046355876, 940264637, 1657505704, 368334388, 628948652, 2008622797} ,{1026052687, 1032660109, 967854055, 585582437, 433338563, 1425160945, 1005918636, 1951497832, 1038804510} ,{445121449, 53504038, 144389967, 775421534, 1725205494, 864079172, 1130452445, 916634090, 330641739} ,{1959964493, 1062537791, 230587153, 14141235, 2135732873, 1772414589, 576609688, 671321303, 75409317} ,{471161438, 1078191166, 1569848781, 1944042241, 1519822739, 698536298, 1110612712, 696334333, 524486446} ,{1557262229, 1239288581, 509619013, 1169055489, 190546694, 1660614442, 143152323, 1441074723, 8293088} ,{733522654, 563940157, 1586180799, 1421658749, 1248294110, 1810445781, 1059898958, 835945749, 1922080739} ,{2042333112, 1121522760, 894104384, 475607264, 825377670, 1695196359, 193411476, 2125900165, 618183524} ,{857461129, 1288056933, 347872062, 1253270629, 922086808, 1954313179, 1703971914, 12635365, 1454535342} ,{187131489, 192762230, 712390497, 1755556693, 715080663, 1093633850, 1403360473, 512761251, 1716730231} ,{2123850889, 1072400251, 1241410826, 1603037952, 2037432735, 1295420081, 895387980, 1289652159, 1478680297} ,{880126030, 252146281, 143165443, 218194251, 1073273876, 1017843994, 1290158598, 1699374024, 907989697} ,{625313978, 1095727766, 1358507687, 1824663138, 1189571897, 662119509, 2093133500, 1092066413, 55729376} ,{982415943, 1020675533, 512606198, 1157974139, 606064527, 998952500, 162972823, 1423056198, 2040730140} ,{2116377503, 1497129330, 1759107941, 636700496, 1978619864, 1078519751, 415258387, 1242979337, 1221454549} ,{1355380784, 538107060, 29344912, 1746714846, 384865425, 493220515, 1467330467, 1984768459, 1730305929} ,{488728680, 1297885483, 69444926, 1025843808, 819610319, 1126830445, 211925182, 1166418359, 303989711} ,{708725502, 2068823443, 490137380, 25260631, 989634317, 2035517882, 2139938133, 672146655, 217512076} ,{1179181232, 643541765, 1503711004, 191429836, 1880157623, 1519318522, 1186697886, 420330670, 659300065} ,{1653445860, 1322409571, 1458865960, 1084703557, 875413955, 743703520, 1198569728, 1524197611, 1523060797} ,{1186818631, 54990040, 236855204, 1640277521, 1350249017, 820719525, 486474282, 1684759887, 1512202333} ,{68413368, 1208000715, 1926315293, 1267171806, 2130513509, 2043049495, 674377058, 816391471, 878408853} ,{1763045311, 208106143, 480288451, 61747738, 1740861350, 772156290, 1283775573, 1008290658, 1173211255} ,{1167081324, 821033012, 1273588105, 2005698546, 497788225, 842051278, 764121793, 986636642, 367213765} ,{1050916699, 979722033, 923946137, 1581220652, 829510151, 504527006, 1391606779, 986784097, 162013170} ,{2047357415, 484062844, 1603755456, 467008733, 520192708, 1378100484, 1550917016, 1642174695, 1556044300} ,{669855080, 1012699297, 644309784, 1680920569, 1472886925, 778250019, 264548095, 1051457824, 1534373416} ,{2073246366, 733742807, 1028224097, 1803878572, 540698844, 652351673, 48281735, 1250109053, 1499025046} ,{1241215680, 877973808, 1249644245, 1442885226, 438250115, 443548320, 1094402333, 308225133, 1726235689} ,{1279784079, 877861792, 713246437, 517791822, 706467772, 322563182, 815097688, 1091501090, 1398184214} ,{1305695356, 987385976, 1925127739, 685388205, 461672137, 100895872, 1030820725, 861471863, 482279532} ,{1854936624, 696131437, 1282781060, 247960455, 655027726, 476400348, 1975034397, 1382059516, 1889293813} ,{1949393078, 2042737963, 1347705107, 1365396267, 1256182872, 1145702021, 1054083582, 1211215378, 617509125} ,{1816941325, 225062648, 487759523, 379977651, 1453637747, 126004096, 14161360, 375021175, 785509424} ,{1501956370, 1989708259, 1964408679, 1815047216, 310004659, 390281706, 324571529, 520331175, 1738501483} ,{1396612712, 152893057, 461862991, 1159606488, 565285496, 952568796, 1846450830, 1587090521, 117177996} ,{1386200424, 124770259, 436744253, 381616129, 936554301, 69096840, 412560359, 129845821, 998482446} ,{1084989172, 1950179577, 1828723708, 107219927, 93702804, 711439908, 291229552, 1293779772, 929370072} ,{40833562, 1597957745, 1034472750, 673816629, 893202668, 1798414363, 727150270, 1493055858, 155884753} ,{995938568, 870294468, 1292320087, 760302943, 308571874, 1380709428, 1668065246, 820725201, 128692236} ,{1556417358, 1130429024, 958921915, 1080036356, 1516479239, 370934584, 1258591540, 639529317, 970521431} ,{1901767270, 439334930, 382902460, 576154466, 1914928151, 847458209, 1052659728, 1066717020, 1541577194} ,{222741713, 1774704466, 1719611005, 1936684748, 1647477707, 945125590, 315075225, 215795684, 2001445947} ,{238503438, 582970284, 319062699, 1737262353, 1940428241, 284745716, 1937388616, 629679608, 431550190} ,{909904009, 654478934, 2098781166, 1213981788, 1948069550, 1302380172, 783954647, 1691216938, 1141395257} ,{79652426, 742918734, 2066714035, 2097665234, 1055475194, 1359285479, 1483589935, 1041537845, 1227807860} ,{250877579, 1242999246, 44825127, 1610612474, 711356473, 1047817911, 859204864, 1739387501, 209945030} ,{818860503, 578086143, 837394010, 1751274560, 1970648634, 613607514, 2125869933, 255600807, 485323981} ,{2014339809, 514736920, 1309116625, 1055197657, 1244419, 776085628, 1896480114, 1894668170, 411852383} ,{1553791375, 1765313574, 588546148, 735827793, 859680417, 684615681, 456304842, 790869463, 904998652} ,{1764814146, 1091596015, 2013889650, 1429743861, 1734788119, 1118156158, 1407786630, 2010760364, 1460518721} ,{1974064705, 1788625484, 186719417, 230191784, 1769905321, 1634573749, 580986246, 671091692, 676029770} ,{832022621, 367164518, 2054894210, 2016759397, 1478704765, 183330783, 165367336, 1529384100, 943420330} ,{69893121, 1163842894, 39048632, 1832964604, 1902137207, 1246754484, 32181292, 962707693, 813570238} ,{1403482538, 432458233, 1629249677, 1467035925, 1528164584, 1873974627, 1724977834, 219048589, 270381572} ,{1031637859, 1419504601, 373786862, 133506481, 146429337, 951057168, 899910312, 1107028060, 1906453163} ,{983613807, 1296758057, 1595794782, 1209644490, 609506090, 1704112501, 1106729589, 1788656775, 1594088954} ,{1465832961, 543606247, 1781973916, 166629437, 3225815, 137938893, 982076027, 574522104, 231639808} ,{1504201775, 101379373, 733884340, 2076386514, 1505474502, 1185521487, 1070571096, 1272189901, 1167582219} ,{1528987338, 1956501387, 329312909, 1023066267, 1743790892, 1206607454, 1410238853, 1925347039, 962545246} ,{1116563350, 1249104498, 1596278866, 1699929146, 721323881, 1932049149, 1886429305, 1576549801, 276905633} ,{555104553, 578897528, 1266695731, 2072297537, 690157276, 1780475305, 1180400286, 155918884, 1065082396} ,{245088049, 402967616, 1889416457, 746097022, 113859496, 1283554440, 1440172403, 564159228, 775397225} ,{452378721, 1011967142, 1178694102, 8165194, 748931462, 961008772, 2130831220, 1409857914, 1758719745} ,{1360189021, 155847922, 1250088436, 1527391930, 323563952, 1934639594, 2076203131, 1981836840, 1777404705} ,{1121244026, 1658173336, 1304812492, 567959803, 1303875490, 1909983021, 743692617, 2133028487, 1308934796} ,{1257748109, 493422611, 309341189, 1122451738, 1632948835, 1019578414, 1455727297, 290824637, 1209037665} ,{959440536, 1247234430, 1772923732, 1870040742, 1122614486, 481933513, 852627726, 68382485, 1635551528} ,{2019193531, 647333922, 1510267480, 350411833, 231263990, 1121732192, 258667754, 1763116672, 902663343} ,{1985231014, 47449135, 1290457419, 966300248, 1999953508, 855856833, 239288251, 2063665366, 1921915724} ,{49104666, 925933005, 1267853788, 1079223113, 1860454490, 1552991466, 1368551869, 1566597600, 1312145330} ,{679677099, 1923206612, 1803112678, 2004128986, 1907407075, 1577945445, 1787702684, 1807929776, 845134924} ,{2035713883, 1193330332, 2035387356, 261707423, 14051361, 802953020, 1754353799, 1687793058, 1356059073} ,{2146009395, 2003639128, 1938516805, 1437818919, 760793052, 715664928, 399543892, 438985098, 392399903} ,{1982879095, 295436112, 1416465543, 691522414, 648583599, 1212041164, 1421747865, 730781573, 1788674419} ,{2023584684, 1781976636, 2000158466, 852281829, 1991601011, 1503218726, 2118277006, 755129971, 760937880} ,{447623595, 1487319019, 2140118521, 762066851, 115664450, 793779849, 681179568, 1382647461, 1886595668} ,{363297372, 1746875824, 327716568, 1317092179, 450430765, 760728285, 598332580, 1107675776, 1780731161} ,{925922418, 332422227, 867108032, 256939825, 322905963, 1941084663, 509850947, 830111270, 1106108842} ,{1042988326, 1163168642, 16979591, 1635335579, 704726315, 1442367822, 1874382865, 1516172488, 1033059403} ,{1555504378, 908892372, 1626468383, 1562995765, 2076178296, 149717475, 1037196355, 663106129, 662975145} ,{1586277845, 597074179, 677537342, 505339596, 583827478, 578747845, 1011846508, 1010186777, 2055623206} ,{1724680834, 71071699, 925007399, 101425960, 580328257, 970671960, 585272213, 1292445056, 1845959405} ,{1390907991, 1437730933, 287398809, 1609319939, 2038028953, 2132029142, 1903420621, 1050496904, 2099851766} ,{1771630422, 1335178929, 1272985708, 970594903, 2089031380, 929856646, 1591780976, 2131458479, 1235646730} ,{843790178, 1131243261, 144918539, 1385157973, 1321348024, 621168334, 1430748175, 347525200, 1199714204} ,{1043855188, 1909513271, 1194603947, 1125567580, 1192335098, 255673767, 1856737405, 1012580400, 837931958} ,{646125729, 2077101035, 146314571, 120562807, 2067429992, 669841024, 341366942, 215832742, 59153250} ,{1785246708, 1293241268, 1410735885, 620468351, 690803651, 1527398379, 768772270, 1425139880, 192004693} ,{729848103, 1247646684, 1745437355, 555285595, 2119221916, 1314322395, 748553605, 830162745, 218052220} ,{961931046, 1607355355, 1724297609, 591724895, 1162507213, 1914924583, 1376196552, 1398546290, 1746757835} ,{2074055904, 1920866919, 1138328161, 971590554, 1413228586, 1624444082, 784521097, 374885623, 1604141165} ,{149739801, 1169816725, 1671855956, 556765055, 1097553080, 1224781977, 921059717, 2059755254, 1879877294} ,{1406847028, 894226509, 1033879055, 137096324, 1556147953, 1260823508, 1451584286, 273286918, 1245749167} ,{1001818526, 1019599561, 1117515580, 1928786223, 57657777, 1313208171, 1839008726, 1929378263, 1851981579} ,{154643227, 1715826200, 1409752642, 1976055500, 88369956, 265836700, 1316709797, 1193084524, 100287102} ,{757852454, 183894370, 152773190, 483234222, 778526005, 1421884564, 84610322, 668368974, 2047835417} ,{1060647298, 816110676, 347441401, 140510155, 2078319833, 230135757, 2092254986, 52466004, 341969814} ,{1530765360, 1885553672, 1829135438, 1610463062, 1059960714, 1349785796, 927213680, 1285938274, 2113120871} ,{1507285736, 1103221203, 1299517768, 1827098779, 990862420, 146174466, 1892601799, 1057313967, 1372421808} ,{1213138218, 451695445, 1065285334, 1616255466, 1746553260, 1959832351, 1560460017, 747971913, 733143814} ,{69580334, 1976930360, 1115754973, 663980085, 797783505, 1308332149, 1624206362, 310256223, 1011316374} ,{1977975585, 1577416503, 300618796, 113300864, 1392294261, 1421995960, 1950403857, 1256020614, 308441952} ,{2083764402, 70131269, 855991557, 1100692926, 656140236, 1703045740, 259925548, 1868712443, 324213428} ,{243069951, 1507484579, 534513639, 1556325013, 1290522170, 456042455, 1985511515, 324525071, 210570754} ,{1063773589, 1067112412, 763717142, 1958476290, 667838647, 1661159297, 2007672488, 2114784378, 1860399397} ,{1371471461, 1921613555, 754433536, 712396962, 1098430008, 1548972726, 856538176, 2130595518, 137973212} ,{1854740785, 1098596671, 1776140662, 46741987, 941711622, 986308308, 142442566, 641132183, 1039437905} ,{784949080, 1948252595, 1233191459, 1245437642, 974009985, 1673520417, 325336766, 1775814868, 1211468240} ,{959286487, 2101239328, 2113680692, 210447972, 548059106, 369624687, 588447681, 111865466, 1153151287} ,{1701831810, 2068166090, 967654836, 379643417, 316543144, 398783133, 76461463, 675004452, 1368781774} ,{1662604856, 1711707640, 108817062, 1233011079, 1860174652, 700299211, 1044828344, 1296817888, 117798203} ,{871273720, 1414224741, 258744138, 1172054008, 631763561, 1109278939, 1910605974, 2006580470, 1003202220} ,{1776623249, 672925278, 1241909462, 2133223200, 1169941474, 1017474782, 1100541653, 996414345, 1358067365} ,{840964405, 1209694558, 100376239, 1842880845, 1281500384, 1452227928, 451089923, 2007121919, 471633530} ,{863799992, 255302404, 1771084823, 1857640009, 1474434473, 1241462595, 799702003, 1899866855, 272610162} ,{1599751277, 950849911, 78883161, 391483457, 2034250646, 68814417, 1880159215, 1344040467, 167972112} ,{458887656, 1260682967, 1994358033, 1712748741, 402741008, 670575558, 1544908223, 1494153410, 953529138} ,{1131225800, 1637311845, 1414176653, 1687556353, 478483194, 1241382815, 442821756, 1103072009, 1058775632} ,{2050496240, 1874603642, 727957592, 1960634440, 562992515, 1156929958, 108283742, 2137483495, 1425486027} ,{1577990908, 930164774, 1627588075, 1956603540, 1539519426, 1236861415, 657815668, 437877868, 97771984} ,{1760913265, 512133529, 1772205630, 1755647298, 521464007, 432684798, 1406017586, 1907786237, 1679099924} ,{2079469003, 254514925, 998480411, 573373244, 1739638420, 1996306968, 1158766431, 1348920999, 1160141888} ,{628574269, 49582278, 230905030, 1508193114, 1290009079, 795948925, 489193818, 1122226913, 145610383} ,{486037026, 1385548144, 232676547, 1681290107, 1986698868, 1859680279, 1621897914, 469380743, 2144538155} ,{1090448160, 1856526419, 1612748693, 211214348, 1121420386, 1474869563, 1418666893, 1110545306, 1376974623} ,{1478306805, 1597739196, 595926766, 2103010338, 853658283, 1617202726, 674150906, 363818968, 1585315103} ,{1120219166, 129122958, 1833006106, 1587642712, 305520143, 381291344, 2129050865, 1379345821, 2077606935} ,{871111006, 503215372, 2006585089, 1955119213, 346792528, 842571614, 1067439930, 507773414, 1672310386} ,{664438365, 168675054, 348077682, 627928691, 1164436829, 472992538, 2115265576, 1605572657, 280363852} ,{849854585, 758856712, 738250221, 1526046963, 625951454, 998278519, 281478557, 1105406921, 346433214} ,{936259196, 2065620391, 1779733659, 1524064583, 48358768, 1308432943, 1643904676, 519201217, 1691999442} ,{1200956416, 16093358, 1117318033, 2046692097, 729195383, 1313745530, 1731248921, 1548129157, 2012711278} ,{603786854, 1660946243, 591744902, 1253208034, 277599055, 805375750, 1383605213, 17326933, 1667398999} ,{1543180169, 1138158466, 730462859, 528245946, 1438333190, 1564413048, 1041911751, 1344692839, 860316514} ,{1277147832, 1267922964, 1927321833, 2091280456, 2035348593, 2109411411, 222660198, 1798601022, 1620586600} ,{599785944, 1399731896, 1703883698, 2011385173, 643159873, 1619303545, 1673998369, 244369738, 342882100} ,{1537603832, 2008510733, 161391348, 1163219270, 1960985641, 611355933, 1343281142, 907819235, 1106377997} ,{133162806, 126783223, 556187282, 1238681006, 245756348, 724421335, 1913252550, 1642784437, 1496142895} ,{184282810, 2120227687, 1221316859, 745299887, 1557653755, 981986880, 877621451, 1619248385, 1527787732} ,{1908901370, 1100214634, 1052314665, 1353164819, 256619194, 1505840978, 310521958, 1029733769, 862580016} ,{1573139274, 1026327941, 1821528216, 254982509, 700497258, 1978967748, 1987882963, 2028829666, 634651969} ,{966177875, 24718251, 1958456261, 1888496204, 1292059383, 1297168730, 430705382, 1952497685, 1145556105} ,{89254944, 1713499512, 202149886, 1221104079, 1449950897, 2111757011, 644150938, 780902886, 1820879981} ,{286690905, 2072548395, 1248347559, 791657663, 1717385570, 361593843, 1443912261, 393662554, 935358582} ,{1595826258, 2054081925, 1770210233, 513253344, 2052277171, 1280794370, 248190380, 426386711, 936674181} ,{901394767, 218582191, 126030575, 1203304657, 505548618, 654713299, 1936990385, 1062228934, 1637007044} ,{1215983659, 1515307232, 1924176392, 622105340, 247919290, 982338769, 399241817, 160248157, 2017193535} ,{369998399, 810485751, 1497823693, 1540074916, 439993534, 573612129, 115943442, 1714243076, 1557995375} ,{3341087, 1123127777, 1034669052, 503682976, 1354883694, 1873577673, 1774781237, 1707870643, 1079251516} ,{192464878, 1105641041, 219912432, 1755392505, 877057202, 823529969, 898044584, 1870573630, 1296098255} ,{1704657370, 502331548, 5876156, 697619803, 1160016497, 979443292, 951561192, 1653063574, 2084682799} ,{1655302196, 1526668562, 513873720, 414717180, 1235521042, 517816064, 762134827, 899589267, 1672284075} ,{344439600, 1629785064, 2143114628, 622255617, 2024650726, 1509231204, 2044380436, 1624709382, 1670750807} ,{2116685005, 689087100, 1304433295, 2061368175, 1523910070, 485118260, 1134641904, 2093740238, 576352622} ,{1965967044, 1764805926, 1085518421, 1898311603, 1100852171, 1506214190, 1632085278, 1112333391, 1892651490} ,{1665523749, 714261032, 1885002820, 1660858254, 1052104144, 1097695361, 1608183626, 1742239079, 334946284} ,{1307680765, 300213111, 231071281, 48978340, 1196410600, 379395755, 1708895502, 1813096926, 1206071770} ,{2125811071, 1309588810, 923891322, 1302258369, 2115149043, 1746670941, 419930675, 618411721, 2006079123} ,{273270858, 1863533602, 1863367840, 879224307, 349978969, 1553140437, 997646140, 530048158, 2097197396} ,{451276764, 1203825115, 427396988, 488257918, 1387202146, 1475374977, 1736376846, 524771116, 774035526} ,{556080046, 1853164386, 2073277328, 590875040, 836331545, 155297817, 1696800545, 533865761, 756852711} ,{529616136, 496690091, 1732850368, 1770273995, 206184545, 2029311361, 1129965574, 730540950, 1024237036} ,{1691561466, 271540482, 1024851371, 1408453126, 888116902, 323739063, 1233665246, 1620915384, 750293247} ,{2020035689, 647066965, 2056303706, 125411428, 1412853678, 906422115, 1202185054, 1207885212, 131100395} ,{1474275320, 1136871715, 1165400596, 775282527, 1608939273, 620875353, 196474946, 1350761390, 62430963} ,{2090409561, 2013491889, 1589742963, 191812888, 1778873135, 1389222725, 1640385921, 1872535645, 1660356571} ,{1213516651, 189842843, 847811218, 1739155025, 263443419, 946228021, 624005593, 769133791, 1175362598} ,{1746284579, 906872655, 441127152, 35116706, 1610510454, 188298870, 1448964371, 709095182, 1577075505} ,{887654762, 1807658057, 282052655, 367403652, 48453469, 1711727255, 1293560851, 1095773360, 1812416440} ,{1269400835, 1507783276, 775511620, 1592473923, 3146370, 526622072, 1879684083, 1256707041, 1818156735} ,{1366425196, 1423718275, 1991268559, 1100649815, 2093850412, 515972727, 414813734, 311219914, 1340678348} ,{1349236011, 1587405511, 1941183801, 2002695801, 1908716473, 148805266, 385863405, 257030874, 1174510573} ,{678855630, 1306412544, 1404973248, 2126119767, 827324224, 2142554897, 332956487, 613720626, 1256767099} ,{105961956, 1790077187, 1783828282, 513654377, 1731022264, 1323493773, 568986185, 851380124, 1862535950} ,{1156494375, 149195865, 411302678, 1337352234, 1058721303, 1355035569, 903407870, 2094901303, 647628394} ,{1940096492, 1895638126, 2011389157, 1498578134, 379086120, 357610895, 1349354432, 167310624, 750024131} ,{1569088760, 622764809, 1070182384, 2109613759, 1510620885, 73583743, 1366550621, 1738695224, 1331574286} ,{1988052620, 121670433, 957250362, 454697897, 2102146410, 1573335663, 728705427, 951270274, 1124956720} ,{1659136074, 789453639, 1156241047, 1749928394, 1159887375, 1711994566, 1201288043, 1651600126, 1226072425} ,{1679926512, 765368708, 1242717033, 322562451, 1928735036, 1465884617, 407534870, 1253667070, 416209738} ,{1975205727, 637902831, 143536581, 1845765078, 1124436491, 698200103, 221289092, 909320437, 484051791} ,{2119787075, 1174005348, 277459124, 407729102, 1734185630, 2042692607, 314135988, 1156774965, 1866185304} ,{129807727, 1459285211, 1917995149, 1324433446, 363285745, 17988136, 1482202572, 2094152734, 1133570841} ,{1378382226, 941370906, 1344885063, 1879556449, 363236628, 1103182221, 1066145468, 217915347, 1038585533} ,{668080986, 232448554, 521607540, 736353556, 1376548352, 642865303, 618727231, 1700915031, 1962344097} ,{84209338, 1959343104, 75342863, 2016852873, 2015052669, 1715121471, 656166354, 1142949555, 1078149413} ,{1871221330, 288557878, 2078839980, 1817066839, 595028856, 707372236, 1401762968, 37959612, 1870233180} ,{1931059499, 1003828083, 1005505696, 808089770, 963798221, 220583076, 498804890, 1707249882, 581009127} ,{561446413, 1251028442, 273961443, 1310347855, 1928308350, 302902921, 275319603, 1536108541, 1498044543} ,{1543164597, 2065148331, 821152089, 1502395982, 1469299448, 1202918175, 2010743463, 1120758317, 754911490} ,{237193077, 2061482860, 972214705, 153694266, 658300022, 1656342736, 2110481665, 2099380242, 138265302} ,{2054404431, 136682643, 1006726387, 1813256337, 996111722, 598757098, 582413038, 1394663409, 1474347170} ,{1921617460, 1284274852, 1964160301, 1899597819, 98722628, 611708741, 1857436424, 1678991800, 1658559145} ,{2085453120, 1835073069, 971971959, 1430634802, 915403745, 1105164477, 263041845, 1875441068, 1117992830} ,{1102000079, 2052472050, 852130887, 1841972927, 11395122, 1559397840, 306341643, 2056875899, 1331598022} ,{56172055, 1237408468, 1433225114, 69319009, 649180259, 680877337, 872795499, 1803950049, 984165383} ,{311328604, 1452428348, 1346660675, 1914318707, 1691488713, 1772960066, 1370369746, 2078934390, 2037603500} ,{2145152017, 1918093050, 1590203816, 1426722499, 1292600963, 845167297, 972272715, 549556137, 778133768} ,{1334020641, 1330490167, 415074363, 825884872, 143218189, 970957125, 612110281, 719020456, 1609547947} ,{1658115575, 1134592835, 880102981, 450637620, 195855087, 17381472, 15026007, 1196997794, 1280144412} ,{663174881, 265833856, 1680935819, 2130190184, 610642085, 141096830, 557810105, 324343912, 1785611904} ,{2018279184, 207692704, 644976104, 1180445315, 1566570653, 2078275366, 561824820, 983806628, 2081231792} ,{1087173406, 1399083458, 1966874287, 1328315327, 134597087, 1662817860, 1384888036, 414107785, 526530274} ,{1209453076, 201187741, 1996479007, 1114271947, 1559148513, 1495866082, 1687674243, 845708509, 82034529} ,{876273439, 1836493496, 1214217259, 2057159457, 1852710599, 1422423610, 1790110819, 1323246488, 101084140} ,{1741002523, 556150740, 379603784, 1564895738, 1745211096, 268922404, 714977672, 455660094, 941484969} ,{1610151946, 678934752, 108564166, 1297239682, 659178430, 759626665, 379565745, 1701262583, 385859073} ,{474314203, 151699629, 940081728, 1346442429, 37284057, 307812205, 1369218368, 1726209459, 1473220211} ,{700164003, 2051690675, 1442752759, 1656387584, 1804566337, 2128755821, 805062404, 1804112472, 314133513} ,{1122439277, 1923881463, 2051624782, 1945566603, 1363456642, 1208905169, 447391967, 393115044, 1655363106} ,{555441368, 511948529, 596367128, 544057753, 925481081, 1176537509, 1150946641, 248600611, 2019586873} ,{929728346, 854958682, 1053751746, 301342747, 297658467, 1173798855, 106051235, 1874837550, 715207148} ,{237582742, 197913325, 1624811193, 966273430, 501509356, 329507730, 435741664, 1243260577, 1891729254} ,{1441495293, 1384307839, 650484464, 671189984, 1970858864, 128165210, 1679368464, 512907751, 824973510} ,{1338603333, 929700192, 2012865622, 1587786631, 2084408982, 1857053396, 933400204, 1690593289, 311585325} ,{1517334878, 385836630, 1917138419, 661284533, 557301042, 1787504705, 1828382684, 450847323, 721627026} ,{390814732, 1232627239, 1059164892, 1296139599, 56305491, 236848624, 1405764603, 1126244535, 1272732639} ,{573587485, 1218772158, 428119056, 2077251222, 602254042, 1554887450, 957650272, 883080168, 1152169442} ,{620705002, 1252810540, 1940374418, 1072867472, 838560314, 1250312154, 858520070, 1846127271, 1373531775} ,{1805857878, 1181420116, 1521954774, 346314702, 376169742, 334621494, 760592157, 1905468547, 460875027} ,{873268978, 1571389378, 1572710893, 564957283, 1914884672, 1050996871, 238494347, 1233103538, 1268550069} ,{1270076131, 2100831169, 718515931, 1797301714, 11496774, 689936882, 691786387, 159312108, 814730995} ,{609744906, 1449304161, 1222895585, 413672386, 1484574608, 1880138538, 350098732, 1579628018, 716956437} ,{1723013584, 1848126772, 1759816143, 1616430190, 1587957625, 1674568747, 1925336358, 727814100, 1333222690} ,{1498995164, 298821597, 1495333560, 306176852, 77415961, 203670019, 1316674871, 1026456653, 1114020503} ,{300609700, 1237688330, 1588082200, 2049695466, 2023302541, 1132474935, 654845061, 1927873877, 1026424137} ,{1666388662, 1155697745, 1110173717, 1968591836, 799390960, 787210078, 619901825, 141013462, 964784613} ,{390754859, 523132428, 1633013679, 711798044, 558506716, 278994045, 2072236675, 1597127943, 321634483} ,{815424507, 123147612, 1489253669, 1543092371, 1411638139, 589368311, 316607119, 1324905697, 625798598} ,{2100725480, 1328906949, 1841550107, 839164007, 861880831, 593653031, 667403905, 1142443552, 1750515776} ,{882937771, 2142829562, 100914816, 1273535120, 556372921, 867433701, 2052170850, 1629017146, 630462014} } /* End of byte 3 */ ,/* Byte 4 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{2061690300, 280993994, 670311578, 1353395404, 775658222, 1706886036, 1790888383, 1048707017, 86488219} ,{1235905778, 1040255579, 242126504, 1619428425, 400690665, 392940192, 1393087625, 1668718668, 675632590} ,{975027115, 336106362, 1978250608, 2097875933, 2014765134, 1605459607, 20475375, 1553838640, 1470921610} ,{640840791, 644193035, 1718519095, 153740126, 1105836682, 1601014764, 1587660657, 1423419996, 178142954} ,{1579174447, 1112724687, 1137961733, 1398396852, 405143173, 1311632907, 1430509492, 38725071, 1572705404} ,{1594998117, 1997359956, 1942605217, 727257602, 252532250, 1116988068, 263169205, 1172019292, 1130815686} ,{1627139614, 1601167746, 1628398942, 2070007943, 205276521, 387423787, 96936246, 532841704, 448278654} ,{645596785, 836970474, 148251583, 565594342, 300207973, 1147882511, 927608710, 1314673117, 1665433482} ,{366822546, 82226669, 320165737, 1828912066, 1683698853, 598083872, 1399326570, 265405943, 1912929320} ,{1183118387, 2032522878, 1725052555, 982926438, 717138309, 222567072, 788160621, 814444403, 754205315} ,{1544488323, 1967270047, 1399656563, 812362074, 252797269, 1748821681, 894943157, 177009422, 405592336} ,{1713467194, 740840201, 1366021790, 403154430, 591914699, 1644365236, 360997228, 1135798859, 87010916} ,{1884560141, 556522894, 1885895938, 1314525354, 206036040, 4301027, 488526208, 2002534640, 1652632563} ,{1508096767, 2040870844, 1468445250, 592385397, 1070854878, 117172605, 1964305750, 978620208, 1587331264} ,{1278812801, 490494666, 1713299585, 96825831, 1290998836, 150284368, 372209868, 825545836, 572561064} ,{2024261390, 2142663074, 1483349074, 359724509, 279394285, 360429764, 1786695386, 378626218, 2079610391} ,{459714067, 790773344, 934551019, 1293589595, 817247398, 1381492697, 128906028, 2107465944, 509131047} ,{1016976246, 467770521, 79676065, 2101069837, 1694515293, 756073929, 781417831, 861205955, 856222297} ,{1320489300, 1296871414, 1834648994, 1486985809, 809301263, 834700159, 1435129756, 555193665, 1151854392} ,{1843481507, 1888843195, 1985798490, 156825006, 1820267619, 1995243944, 1716959839, 1595998527, 2013287411} ,{1488776010, 979244079, 1458673238, 560045478, 209882153, 1531914299, 815129051, 1455866316, 1418315860} ,{1313136200, 678345836, 461082824, 17559613, 371562032, 218748166, 1028766060, 1804975139, 1779930198} ,{1347264513, 2102767812, 1339656398, 958408081, 257673335, 879605956, 602297584, 1177100656, 1563675023} ,{1131074689, 1940998864, 79627382, 916728999, 2072029077, 364006717, 1777586131, 1475689859, 1448163444} ,{28698099, 1990498204, 1558148218, 109490938, 1471740917, 1873079687, 1654189604, 2107504953, 2129978554} ,{1180032600, 1817121533, 769605394, 1865564006, 596478276, 1404110123, 600289398, 1440714352, 1380718441} ,{184784247, 393920919, 931737212, 1321796828, 1851917312, 1080865709, 1519801855, 252995267, 367504598} ,{212155313, 889311085, 1126496771, 313685358, 1254522856, 63437449, 2070661305, 125824813, 458864895} ,{1155171517, 1259183249, 1711322342, 1368953554, 604579048, 532750671, 558491966, 1817726460, 2115858434} ,{1155135017, 855449260, 1122526881, 1602074447, 78633805, 129029457, 755244024, 1767235768, 319094745} ,{487172741, 129503444, 1202928190, 1808805572, 820697784, 66367167, 1931318958, 918095572, 1787854522} ,{2049214683, 414535663, 1454731976, 296213515, 1398123474, 19433071, 481562760, 127666549, 172324176} ,{1612995154, 784060240, 1751870334, 1792311206, 950323973, 1722750489, 2115275304, 1481461579, 324438238} ,{156920605, 110806737, 1769427884, 147227969, 32907640, 592755437, 1239840934, 1216522195, 1217737607} ,{605652804, 455120060, 1958689268, 1643719438, 2051508090, 672638308, 434775672, 632554494, 1185868923} ,{1548574457, 646334283, 1656917554, 2105058580, 603425119, 1929694323, 1303647088, 360291911, 1658438956} ,{1842771022, 148391234, 22668844, 955476198, 1564867304, 1865653388, 228683617, 553314385, 1908078181} ,{521643094, 430774797, 1865378146, 1059231350, 870853454, 1751438267, 2003994029, 1324201081, 514757258} ,{213528470, 1466568156, 259184982, 463902254, 1874789101, 318091368, 1006846462, 326254357, 1438477529} ,{396323802, 540178107, 122745173, 1003464217, 634220937, 616213141, 14444756, 568911823, 757224346} ,{185974220, 1682473366, 67684966, 944547051, 781253676, 1617382389, 467968771, 1961873987, 846217057} ,{1297425995, 1896232027, 917574866, 384577405, 324452809, 905262017, 278610613, 1682928061, 1990285817} ,{867472432, 62390819, 1862429337, 1587578643, 1782264244, 1047265656, 1066389628, 2073335732, 197603279} ,{1024464944, 1983831435, 588502204, 1182126472, 1625143802, 2054765434, 1372572855, 164855061, 1674818713} ,{1473507435, 1252480237, 453907400, 1751690694, 1425908263, 199881723, 706071344, 1934062453, 2144850775} ,{220702332, 1969266411, 1944664178, 769366245, 1300006268, 1033027136, 1153607951, 1546719468, 1617647913} ,{1257509405, 233086725, 1506710854, 705126839, 587500376, 329829345, 1928141495, 1640890051, 1194304864} ,{1401754326, 1207973950, 1208116912, 867586474, 599186118, 1361605030, 1805296975, 1846345268, 811958733} ,{5988062, 342857813, 1235838394, 1404990389, 2090953597, 555510356, 611913040, 1190820825, 63553573} ,{639421261, 1457399762, 147483181, 231201964, 907778976, 1340976503, 263952, 1519599769, 1342286698} ,{1020789843, 820388082, 844674179, 744074467, 1802037787, 785234184, 999338944, 823802566, 10468336} ,{981579068, 1595176336, 287790840, 1463865472, 426674735, 2110617121, 1803932677, 510366488, 1107039923} ,{722868043, 632586491, 1947571353, 1791969219, 1717118309, 701705223, 2024097072, 224541785, 27915949} ,{1409628975, 2075637494, 746423628, 674855423, 1367113118, 1045396441, 487909056, 74590524, 1770503164} ,{1550373143, 1625866570, 55650567, 1920589897, 1888755448, 1405358517, 1389136186, 2078637326, 1805477478} ,{1892449238, 448139364, 1113275502, 1651820997, 1214645833, 1718517413, 1907658778, 769392060, 945358612} ,{290398284, 1127835, 1266419457, 1879352938, 13284784, 328985165, 1688145561, 1395876174, 1027966676} ,{1528796395, 174842846, 1723217717, 1098721516, 383167836, 1250376843, 1607708369, 1502391228, 716324365} ,{70987099, 1799809506, 272441177, 184843076, 2086948208, 1667589839, 1728894198, 1512465389, 376469316} ,{1555381260, 940133819, 2111542308, 850697218, 847410717, 395750378, 1758281311, 1416414765, 888536461} ,{2101132684, 776887113, 266902660, 1570112674, 217370230, 470987403, 424179616, 992153079, 1910226117} ,{1103834175, 1677190638, 931920150, 1013519088, 1019828431, 753977765, 1204898844, 1021301222, 877121368} ,{1426481987, 1637410412, 1090599832, 1203075555, 545902807, 1889760993, 1825047855, 27437894, 2039538097} ,{531442429, 658403735, 53388633, 13882852, 1437364271, 1438766526, 194065443, 268082494, 699290304} ,{693644520, 1711037799, 84955968, 1734725574, 898088098, 1468262217, 1396425292, 386983263, 1084766503} ,{1872555447, 1333189674, 1754046934, 700581142, 1547643545, 1429414544, 1683248573, 1507308512, 161092107} ,{1525430335, 1022211943, 1768485046, 238476604, 61989714, 2098924061, 1141235807, 590663348, 714251001} ,{2062233841, 1382792228, 357771100, 450822459, 515599029, 768384566, 733081596, 1912230818, 1779053525} ,{2015071407, 185311598, 166627066, 140273984, 1182733716, 231655882, 300295872, 1323660617, 397329953} ,{1445216150, 1083894088, 1356003939, 495873418, 578873394, 1651810259, 670496166, 905444854, 1765206248} ,{928226387, 971274830, 34201928, 285424722, 650401600, 1398014871, 726012729, 997687908, 1160204291} ,{1227892263, 1483522768, 461587055, 2115020532, 1349367546, 1375883023, 302150544, 291280924, 1676915952} ,{1490156810, 775721408, 1437361508, 1041457343, 1508296422, 1327908348, 1831587045, 937272789, 2146618587} ,{647027700, 1021780673, 191999265, 4026252, 1727339256, 795320978, 1846372205, 2042150539, 905212385} ,{373142969, 1142239024, 2076392222, 1061586736, 519159519, 1978006626, 2028334997, 29546940, 508818090} ,{1237792726, 792767360, 2108893608, 1587998284, 37748953, 1003785000, 1757568858, 758100819, 1812523131} ,{1185480689, 24952725, 1038946971, 224263878, 1325046172, 576402125, 1374077139, 1783446344, 1130336681} ,{1756866049, 1023470384, 339364724, 824658491, 377355184, 1374934908, 717130965, 1110533565, 525539348} ,{1540027315, 1378169068, 1312030283, 1540398694, 2102665944, 302288596, 776732545, 1805413215, 933900407} ,{422976194, 1283669508, 1192381232, 913897898, 1797210094, 491382746, 1664642365, 868143982, 1708108065} ,{2142051263, 1755999889, 808804712, 945311133, 564609774, 1328404494, 1847101692, 1587315729, 975416910} ,{1563012016, 683607648, 29560783, 1333485697, 2112298932, 1854540360, 1527955304, 1228198070, 608687784} ,{1189677889, 1827203011, 326655487, 1130648628, 1785601530, 229699744, 675757306, 1538378665, 1784039909} ,{663586606, 1290699694, 62711311, 1543656645, 2094989336, 1702689476, 1184756912, 269998267, 604625572} ,{615333616, 703546175, 260915428, 529831076, 2121459057, 58682680, 240376490, 1519441128, 301164474} ,{1985975850, 1416334086, 2054251613, 69496461, 450403117, 1794984362, 1640755451, 676348059, 106307114} ,{192207518, 1543954104, 47395354, 1937695306, 1309893884, 1261933108, 464712225, 164433292, 393348773} ,{199704471, 242288073, 281858547, 1195914638, 663479612, 1353507514, 1311369470, 1810677147, 781361887} ,{1990851256, 813796171, 360724711, 1612521497, 2093338315, 1834985149, 684847550, 299083031, 1022291613} ,{83669455, 994725875, 1308407170, 1641688699, 1611446088, 428570654, 514162347, 65867507, 192528090} ,{906822817, 1451550805, 1390422734, 2043755777, 1931593941, 812040272, 247215180, 1957230464, 1676060008} ,{1087834299, 412464835, 1007975900, 655676231, 1919467821, 2071832295, 1893447332, 1281459853, 719337596} ,{1947569347, 2109015573, 26788886, 342114230, 1971582543, 31501931, 1196879216, 1641193422, 1611913210} ,{624788443, 480232609, 102333143, 293178401, 726081866, 583679201, 5419605, 828152451, 1617344514} ,{2020009836, 536787749, 131924718, 1111548123, 1546310610, 1547771224, 19701519, 997278409, 16147161} ,{246617937, 517213068, 784302764, 1716680405, 1404780253, 108774243, 1068354326, 1987001485, 1246200645} ,{375068471, 1290481129, 1918863582, 182621304, 656998662, 192839523, 26008649, 1139885918, 43290226} ,{283451116, 2138578625, 1107810505, 610839367, 177162264, 1609874005, 1081225515, 1843023172, 820594342} ,{130434103, 1780060614, 818378623, 33833798, 1419400397, 2056531225, 635463606, 455666630, 186175253} ,{2031320447, 1492515620, 508748592, 1115206383, 851412591, 1533172740, 2119621780, 373338760, 720744025} ,{168646215, 1723763804, 517120047, 507306039, 806489709, 1127866717, 1947727796, 1543588228, 1125227288} ,{1416334216, 130112976, 154171998, 1980571024, 1994082170, 1352382918, 1613573259, 1308446201, 1643500182} ,{1035834924, 545369028, 1491878594, 869830798, 82629208, 612323534, 633320263, 970291253, 1138891640} ,{1046477687, 2113823843, 1172308176, 1468679894, 207320561, 351692282, 1263964242, 1241040774, 1276194843} ,{532828784, 2057911955, 421355086, 1667572574, 525336297, 153434963, 689775396, 996654456, 1894494155} ,{1891701712, 1453762808, 829602151, 1915754170, 698610378, 2080157397, 1926908162, 1456084678, 2121716023} ,{56888719, 1353686615, 1637013251, 112684669, 2132824940, 1226401381, 1315146950, 726125060, 1878681169} ,{412323128, 753823342, 1028288029, 649641162, 607818356, 1468503706, 1158556826, 888391367, 1048500654} ,{1903978426, 1689284343, 716820529, 1364010446, 1936541948, 1667136988, 992316042, 1231448196, 1076038350} ,{1753735781, 1443826417, 217194941, 372577781, 1711306579, 937393076, 1607094282, 24843147, 306747107} ,{104065759, 1127076274, 49795597, 1815906775, 1230225952, 983279076, 1423634838, 1102886217, 522093229} ,{659010867, 300224996, 1764841841, 261713377, 1333996801, 821190469, 296309171, 1339121173, 113685114} ,{174924460, 937651655, 1808321746, 384985936, 1188044719, 876428094, 857326819, 2125031014, 1947522417} ,{1750024654, 1367652089, 426280387, 1676008496, 210516656, 110898366, 1031711053, 563290351, 1194481039} ,{330722267, 139658060, 1053490284, 410753733, 1572706027, 775524543, 1104179241, 131665181, 1633997396} ,{1665773641, 1887041722, 1810373680, 1989135783, 364274549, 1411395130, 242643561, 2011584719, 1700329362} ,{333893580, 920965973, 1384314129, 2107397818, 96095849, 804103220, 1102878002, 1936138882, 1780679252} ,{734963434, 1289115770, 1912846454, 150864956, 1793827755, 387633474, 1132929644, 183836310, 655743223} ,{1107266839, 596159112, 698315035, 572747669, 247992157, 101241536, 233383768, 1350056809, 741839915} ,{1049385420, 395292491, 1887234754, 1577167290, 1706034131, 28007594, 1233187002, 1360300204, 1659425789} ,{491922951, 721234370, 1553444405, 41128608, 944755034, 1730177343, 1270004882, 1958858911, 1440270421} ,{123363626, 1220514461, 699746011, 1981790825, 1938895615, 1821582286, 706437305, 123925033, 1143902577} ,{1162393950, 511196425, 554672623, 105679782, 638825267, 1178100366, 786582589, 796743517, 37842489} ,{1856458044, 1307917390, 1863904889, 1255426271, 547809507, 1087653804, 1912722787, 1337592572, 1368142295} ,{1557317545, 1379062586, 1126819808, 2038817671, 698136058, 1370872357, 324891936, 1495608764, 604169720} ,{664388853, 716225139, 1075321830, 1908295670, 1299070502, 1879553411, 1598594142, 1563536085, 1288703784} ,{2370101, 1642019685, 1297654633, 638951009, 1419043024, 1748564332, 1974147305, 484870364, 1678835126} ,{1545934198, 627492844, 1784671994, 1428716086, 780902175, 2019151157, 755846542, 1378918161, 405655001} ,{369724374, 700918065, 1726759097, 1143091679, 1985189377, 2074753087, 1752214793, 1925680872, 452207613} ,{8421513, 1195173687, 312027615, 2098769957, 1735617782, 440606881, 265189389, 824837665, 1983795511} ,{1135498696, 157267825, 712985332, 58467755, 1490118966, 1477830199, 88370580, 27538052, 621487189} ,{1172543222, 71344201, 1880544671, 351642226, 1515020678, 2023186085, 1709300453, 1149251438, 898034310} ,{1820895720, 1721295491, 150768989, 1291865727, 2138259437, 1083981989, 737742934, 389410606, 498727172} ,{1981556797, 364161094, 499157633, 276276918, 657627259, 1495977315, 1586178308, 580107969, 1420957111} ,{1678295688, 154385693, 1140072110, 907887562, 1058682100, 425975661, 1098354811, 1704579384, 891576045} ,{560861067, 886974568, 380259744, 1729390439, 436288804, 1442292471, 1056573385, 1896460666, 416772665} ,{1241217315, 774036353, 1151706421, 1644852436, 646384814, 1950223028, 1328121820, 1180009799, 126007202} ,{270971825, 1261170296, 316997104, 2041753739, 836167969, 1703201069, 687244371, 834850568, 1402963729} ,{1506355785, 1746296001, 610161575, 1347271099, 1039792595, 1755942388, 854575198, 833421415, 211224188} ,{1551166562, 1912242621, 1886197392, 307472554, 2071094723, 1584082689, 150652445, 791615110, 720287611} ,{978577797, 798567906, 606361976, 669241221, 1739058019, 133943130, 1633223704, 527109654, 910324546} ,{1494896111, 467436488, 1561525181, 1204829814, 1740556960, 1125338855, 630785670, 2057796653, 268708855} ,{1690494290, 1321037958, 1261743293, 601178102, 765972612, 1803106780, 630655920, 380448530, 1706557450} ,{12913515, 1488999640, 677681125, 317020476, 595983873, 947608998, 131269611, 1452192118, 2020451914} ,{702746958, 600570617, 602426591, 1655783903, 1503842631, 139860817, 231352758, 1360062073, 276201629} ,{1934186028, 727745499, 973112665, 1862193022, 2007388927, 909773848, 1513685277, 1010280628, 365300519} ,{501189508, 830982887, 439411383, 182856027, 1436118571, 1514560018, 1842050659, 1578247761, 991106037} ,{1006249666, 521429696, 836072416, 1359363613, 712332653, 813487407, 1302971100, 903323728, 1255162807} ,{1052639393, 926494244, 1048303243, 1079680735, 165652953, 1953934827, 1116045330, 1762806693, 944534981} ,{98877687, 1444708663, 625277929, 775459851, 151635414, 14237102, 835043308, 1342188152, 970359749} ,{2067333498, 586359672, 291246366, 285262126, 509684503, 1795528230, 736553309, 993836109, 1128209506} ,{1615516334, 1429262163, 2010916135, 679676108, 825986099, 219586352, 1075067453, 1699266870, 1304984518} ,{1266805735, 1320101410, 1908804145, 114885181, 998348294, 339848491, 823489532, 1115154033, 2003908753} ,{1553325072, 1219938383, 2059761544, 1531000854, 177923653, 1964764796, 942597136, 771595127, 659484635} ,{712380347, 1818472282, 1901958305, 1211494815, 922317240, 173274790, 767570305, 1479716120, 404416598} ,{1936674824, 133839966, 848457955, 1803140234, 865372009, 2032984154, 1640152054, 584841921, 768051519} ,{1933798956, 155852471, 1683848271, 676802130, 345136426, 1197009754, 620649763, 781697260, 71542385} ,{1175428503, 1762397548, 223283090, 1020184713, 236483384, 1673120119, 1677800513, 1828818904, 25893497} ,{492741100, 2059294479, 1413652415, 1792376281, 1070369257, 1886835094, 109015038, 1539472653, 1927418199} ,{1604721976, 1014192009, 572442527, 1325926075, 142450315, 23042588, 1423371080, 827746197, 1678100246} ,{1619098046, 643038361, 1833147233, 1816140309, 967412485, 720599139, 1473563352, 441399677, 490800051} ,{1699394601, 1735990164, 911767864, 1863484996, 228158300, 1248576236, 45269452, 988647229, 848499411} ,{1740064762, 1944836297, 1719883533, 1318761011, 1550515031, 1550561995, 640315923, 1416772985, 1591515398} ,{2095628741, 1853574760, 1206966031, 1969587057, 828144634, 1227478883, 1099470969, 403656212, 1975355148} ,{1208534203, 1048821278, 1488600083, 1320600781, 1212240610, 947956854, 512570090, 67459426, 1832874905} ,{1217590947, 1761611381, 1510599648, 2003246978, 1795505632, 2087073826, 1424368903, 1826840409, 2122479130} ,{659880907, 1526310252, 1799206344, 2123986603, 1589811980, 2046971211, 116398779, 1975872848, 1757249942} ,{1316015039, 1450125448, 1225815274, 114705330, 1303960902, 882146002, 1131812538, 1651306224, 1155500453} ,{696272615, 1729816472, 323720957, 629937770, 1968766430, 949250145, 528354318, 230265394, 1243928448} ,{879028985, 1937235529, 995728156, 1202633148, 474492273, 18962649, 1955458870, 1591964367, 668924021} ,{1199713736, 1178519216, 393956197, 810359599, 808264310, 329122679, 171389077, 428250875, 121505592} ,{615058128, 19424265, 809727416, 283903587, 1121671204, 663147379, 470251651, 1300431515, 129649262} ,{404403477, 1570900726, 1028927607, 33770086, 1940713383, 1107236962, 856143035, 1344732759, 1362682735} ,{2120084216, 2003529189, 739417198, 824884343, 623066474, 832002984, 877712883, 839807381, 436508073} ,{712600355, 608829645, 60434287, 1715777697, 1299092922, 1970032256, 1088909915, 797788315, 482996100} ,{1959443963, 133854588, 1915637931, 1744429013, 2066915272, 1917655395, 707587631, 530405701, 1988647459} ,{1984450809, 339689518, 1540716585, 950861364, 1769900968, 611499376, 897195074, 2140081641, 1128246576} ,{1117045222, 1036778207, 1565771821, 944736320, 496278852, 860560744, 1694072290, 1425552842, 212083722} ,{561332331, 799406715, 1233003407, 1012140902, 1445422665, 1784056405, 1145454577, 289109253, 592615150} ,{1258674914, 1377903475, 1694622609, 2055873321, 230524029, 293122161, 1699404035, 924210997, 1732411280} ,{2003133657, 1063930855, 1921472566, 900291749, 1246681805, 40711528, 618733343, 1005857626, 1538876730} ,{81299760, 2114064618, 1254192325, 1985021040, 368027012, 2085609702, 738977294, 1082133893, 1385144520} ,{1590885911, 440460876, 1727593452, 1792190481, 692662914, 1064766813, 603305791, 614665813, 1873394250} ,{1666703626, 1915632985, 219237626, 1638534252, 1955755830, 1976531810, 601193316, 998619862, 1820987243} ,{1835019487, 938332979, 774367200, 732644141, 172785047, 1047695435, 2018286533, 1422108225, 211918080} ,{457798036, 702889476, 2006959071, 1545894606, 1115126219, 113163434, 1323106775, 545936323, 1246171053} ,{1325940988, 1351343595, 1485410691, 1922139281, 482165630, 1746257708, 1733213952, 456520939, 215009119} ,{265388844, 285220947, 308733896, 970086531, 1368034053, 621831581, 198467025, 1437493984, 1294407888} ,{1669441967, 1647953414, 549738042, 1414510970, 509063991, 1815268002, 129142606, 2013556152, 1092809526} ,{1264941278, 494541898, 621151921, 1895019655, 682562588, 1984547733, 1322424585, 1969041962, 208146266} ,{1238212219, 654818421, 2018681568, 544458446, 530911647, 114069962, 46021799, 420976634, 1886808378} ,{1450485845, 1271042374, 1395492015, 144344964, 899254326, 1300243240, 940406881, 2070060385, 711604103} ,{478040554, 1583501697, 1638976475, 386309866, 1368338130, 771942409, 187680626, 964866741, 1138008953} ,{1942752968, 1672385861, 1354881855, 456494068, 1745824244, 1851415781, 814260514, 1674741441, 1267038922} ,{1317099136, 1795097815, 1834161789, 1527229937, 279178345, 904616388, 229055865, 1983931296, 1302789309} ,{760201411, 1768207467, 453768846, 996566607, 752061222, 535769332, 1554739768, 1311401147, 1044690097} ,{1834127769, 1533410582, 320777307, 124610754, 1909586200, 1857833504, 100032310, 1163341269, 321300750} ,{2109871860, 1185639874, 2048710830, 1189484069, 2142003409, 447184723, 66835077, 843969245, 236001438} ,{1911528981, 484188239, 794970143, 1697497884, 1427176444, 2070010925, 607337231, 518562212, 386686211} ,{1615097785, 527174324, 1386056910, 1362347727, 36609625, 498761011, 245122915, 739683795, 1388333032} ,{296218728, 1369944029, 491418052, 84752666, 527979213, 2125260933, 1143441096, 1402535200, 37091893} ,{104925333, 743813826, 1020044014, 1347161526, 1032326358, 2102954330, 1357751822, 1086696753, 2047846865} ,{767907268, 1337477903, 1593049761, 703475774, 1860907777, 1927703818, 703605060, 457214003, 2023005423} ,{5462413, 246901888, 1926530552, 516490675, 1068196234, 1640854414, 707986413, 1664559801, 592974486} ,{1916717137, 825286288, 180974819, 64340968, 137934292, 1979796390, 561787906, 511960295, 1569360924} ,{445540132, 955415994, 1210890916, 785347291, 1332250164, 2087967136, 50605689, 1734002941, 832723317} ,{996587498, 1720596685, 1982058486, 1825195560, 159731384, 1157042696, 1147596813, 671917399, 1964669759} ,{1844369318, 348163946, 1842535315, 444076729, 1823620815, 1635369967, 2021775103, 58577990, 729376695} ,{1080327704, 1337076529, 742705159, 194225647, 507776238, 1388458562, 471493027, 780822364, 663985140} ,{23854824, 162709277, 1644042517, 780558957, 208143141, 583856818, 1224944266, 996073558, 1792252678} ,{1337631426, 2126633736, 557403136, 1393376232, 139535475, 155224184, 932571401, 1174350424, 158009556} ,{1946116410, 1397525486, 1948248032, 1767117994, 125968566, 1790126566, 1965948828, 1938029363, 1917264483} ,{961713903, 2103861859, 1236039130, 1862654981, 777299735, 2089384540, 504754541, 1793365074, 1652860512} ,{1864361266, 1064599595, 1765940124, 954622877, 2108496233, 1056318246, 2040473832, 1528947224, 2002861434} ,{1318865856, 1287700224, 477210780, 1811464139, 738377573, 396600445, 875271536, 1075622372, 1650399931} ,{561312488, 1872678682, 1146454844, 1281621445, 1368743140, 387729123, 1332872198, 600367811, 192246765} ,{1955758014, 1980527414, 788387521, 1322376397, 1803529754, 651767015, 23398154, 669941949, 387934066} ,{1536125114, 1576083642, 893225462, 342138472, 1595128196, 394194758, 540508932, 1441450534, 446375415} ,{75282883, 536444993, 368352262, 321896687, 979307461, 832218531, 1258191782, 96277928, 1804603645} ,{866602872, 313331818, 296460635, 1977806542, 1103943272, 1298511001, 2096839614, 599667583, 1216182165} ,{1028561425, 394428740, 1296174163, 400023434, 957707758, 571303153, 344598783, 601536407, 1498789292} ,{1297835604, 761749126, 1830969583, 982066839, 582597738, 737003394, 928773301, 764370366, 959313204} ,{553194747, 1882698301, 224480277, 1966126293, 390078957, 293215769, 1088222399, 1477874271, 1053938968} ,{1404000864, 1758041069, 2066476716, 628063417, 1060918593, 1696137496, 1043331863, 585019812, 2144651847} ,{2120435410, 559746162, 1096429410, 836907001, 818538899, 891335592, 2072952141, 218747934, 97732028} ,{1183517321, 1374688487, 338990865, 1710417870, 635359184, 1067831607, 716869079, 1352139510, 1235852179} ,{1428198108, 1677996514, 271536524, 1993039114, 1339308101, 2037120635, 1169157037, 1261711604, 1327205769} ,{227663486, 1672785867, 2020201558, 1053697068, 392046373, 89865094, 1344503681, 690494962, 365121501} ,{61088030, 355555224, 597349370, 1308656904, 683728318, 1407916237, 1929939787, 1384797255, 1809662014} ,{991853711, 2008770887, 1321492014, 282295880, 1263546194, 695254447, 1829772234, 1145114151, 2043872247} ,{1659913490, 1555937290, 164832896, 394375352, 749163717, 2048709392, 736274797, 1962040273, 1458313992} ,{828919616, 768967248, 1737718414, 1219429107, 774886537, 49348653, 968827109, 1846707260, 128538996} ,{783474287, 1140774341, 577775628, 1605718086, 966614447, 1618177617, 1836774562, 1177595354, 2057694626} ,{2070633564, 1580709027, 928385020, 51640565, 1694929569, 1285484009, 1444833258, 1478684207, 1929009373} ,{575185968, 1237438262, 620667743, 2006453720, 1628395629, 1895116632, 1245867110, 496658408, 166074998} ,{1021878582, 1387679490, 15559515, 1251430066, 1965663954, 1244770351, 761005898, 770329637, 1373926034} ,{296045799, 219369643, 1577736849, 972138091, 2138222186, 8379431, 608436407, 1510804625, 2146311479} ,{1407334337, 1010666319, 1418521156, 703819533, 140235551, 195976754, 705684198, 242081699, 2095616421} ,{49857864, 958003674, 1760692703, 1384711819, 283360211, 1370050151, 314807509, 93413185, 787536508} ,{1426981770, 503179940, 1651029287, 2064821974, 43411605, 1077478144, 1703408666, 2112874035, 914518428} ,{802974180, 2142642804, 313363830, 873499284, 9156469, 1861601841, 1916661015, 1813280664, 1951488613} ,{1338537954, 30581606, 1459349875, 861066722, 1906395533, 1065331875, 549612807, 1420321516, 979786273} ,{1035621127, 1988572912, 489250410, 1702335350, 744313135, 230241147, 1160285285, 1833106412, 1391216079} ,{1498162797, 875686788, 2006685910, 374550473, 1692368019, 29207262, 63728086, 996490737, 1021473714} ,{1421918064, 1934277676, 1527009493, 1048181773, 446301533, 1866103083, 1195995961, 736957369, 1154734095} ,{307468311, 506688445, 2123653038, 1844940258, 755058939, 1580190542, 1785334713, 468453832, 1879590286} ,{705429712, 839826987, 309982071, 1253030431, 1811464028, 1881416776, 1369473117, 451592293, 901787449} ,{334837132, 506708772, 1833608729, 482869485, 376354013, 1678005964, 1675803371, 1077454311, 1180361010} ,{1059923350, 1992166580, 439721350, 244591221, 1459292437, 547451584, 677610025, 544298624, 1698284648} ,{240908944, 2039763651, 644910036, 701721810, 712634113, 237458603, 883922072, 1680839273, 1520204738} ,{1406973489, 643495749, 209312283, 1494081167, 1608998194, 1117422669, 784581990, 145735647, 162126347} ,{1642148701, 661872590, 508419059, 105563962, 1809511949, 194494727, 1191965181, 1943233978, 914631692} ,{2137123750, 1443441634, 125370197, 994751645, 1347486606, 1769190145, 43024558, 764944685, 1616626878} ,{1388490883, 231279612, 1610287747, 201162236, 711572161, 67430350, 512945301, 129005563, 988542761} ,{1375992771, 1819284599, 651280309, 1485838607, 625092018, 371319488, 1380311947, 1754348702, 225818337} } /* End of byte 4 */ ,/* Byte 5 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{515769131, 142138076, 1097629934, 1998204213, 98420724, 1142734886, 1341917818, 1313784612, 927086298} ,{1958301840, 1774108231, 889807719, 744516909, 1800418879, 1088702587, 1260381090, 1377125351, 352057365} ,{1823070941, 786550849, 1774542449, 1723220314, 222914731, 255850202, 1899743243, 1380432726, 384008371} ,{702516624, 606136791, 1035328649, 75277229, 26957848, 789752702, 973663568, 593488439, 1859346359} ,{593686007, 31441429, 1037101635, 6393153, 944834055, 575270603, 1157879471, 782251344, 1100542814} ,{1974179051, 393568237, 526206447, 1410242438, 1987879418, 883721575, 1827516631, 1951727623, 1626897297} ,{807390973, 890620431, 1062264610, 1476725278, 257885154, 2003897961, 468274506, 1957074295, 1183521886} ,{519245959, 1354305672, 634244421, 193274965, 1625138757, 1065195404, 690922759, 1991561464, 1357588974} ,{97470679, 1403797279, 865828124, 335889734, 726655531, 1262308359, 316646016, 1943050546, 690832934} ,{1610369097, 585726307, 1024400316, 2006532441, 1100238104, 1847458223, 699912485, 150950336, 617908757} ,{640380450, 1195812513, 875471832, 1939926379, 1270791219, 2045421179, 52200237, 1599533749, 1677322048} ,{827241867, 74436339, 1171428768, 1089377876, 2019763623, 536520962, 1198394796, 240442968, 647118396} ,{213791281, 1045195015, 231492018, 644505827, 1439212060, 1077742249, 820649872, 1645768560, 863193064} ,{712403641, 1875204994, 1231093484, 1096398631, 1236945238, 1733237902, 839208583, 1124798174, 199020783} ,{596898797, 888493813, 342236256, 227840601, 112850916, 357456687, 968745692, 1177269797, 1526167587} ,{1802857540, 1545633839, 326696530, 1821029747, 2078088446, 914633700, 6574735, 1281857678, 1414689894} ,{216145493, 739258127, 1338160559, 331417122, 2009523977, 2059471575, 80474596, 2021237931, 335723060} ,{1964494, 723713228, 1374735601, 795607489, 893978852, 78780561, 1454536587, 1038138960, 530630216} ,{531588279, 1864729830, 378141817, 764789525, 689604753, 1571299750, 901928728, 1308107929, 1198134619} ,{494943097, 2005802782, 557397232, 935902171, 1338754797, 1510450151, 1209855685, 1049323593, 219872263} ,{1257174950, 167328308, 307602352, 754255562, 15211504, 801058899, 2067341202, 245434506, 573554521} ,{1883374863, 832895091, 619298778, 1067185076, 1323425999, 495836436, 16259296, 631997593, 350607565} ,{1118041314, 797421585, 879983969, 725416048, 911225170, 1909345678, 2144448264, 404355885, 1306571522} ,{378241905, 1651014496, 711466803, 648031186, 12446920, 2055500741, 99415726, 1257480776, 1290315566} ,{864301876, 743947483, 463766285, 847705514, 1854059251, 11945097, 137462954, 155348386, 296448763} ,{761113936, 621793647, 1087517816, 352369901, 2088245136, 2073390687, 1113450352, 1251152466, 147103220} ,{1656957112, 1458020452, 827812922, 610459623, 1971897350, 19092004, 1250627450, 1246481839, 1428254524} ,{1178301318, 472012953, 916790028, 1139893279, 1037189665, 1992342774, 1722708610, 1610687089, 1439816825} ,{412796679, 1590825075, 790175784, 1048270033, 2131874570, 1338972055, 1932148300, 372018128, 713178037} ,{1238162541, 1247773808, 1387172776, 829089813, 265863321, 921791101, 1171920073, 1814782158, 1563424848} ,{375839348, 196958610, 1386764955, 1970933752, 242412215, 924408297, 741099504, 2141045655, 1103658971} ,{920069559, 22484092, 446044854, 896692077, 1765200063, 1740569211, 1017961636, 110908775, 1189511080} ,{225116938, 1175239772, 265318250, 27559398, 1357798878, 881700479, 1996832547, 80287283, 1651820876} ,{946575309, 1295952742, 1344826749, 1488845474, 1405362944, 1931019025, 1205841002, 1281549488, 1815814082} ,{584256994, 1446270005, 316379209, 1228515342, 911409134, 597534963, 429364519, 970863578, 356858517} ,{1627061835, 1743576030, 1086558276, 553465887, 916064229, 637233476, 434029830, 1475297270, 1473452008} ,{1769799548, 1477432748, 126674369, 2094790385, 1233451671, 1716996365, 62596553, 1321262315, 234138949} ,{1452751343, 101456389, 1548925694, 1750352914, 800069644, 2062599613, 75396553, 972344844, 889245678} ,{41288870, 1814869251, 1176544839, 376323424, 733880520, 726676323, 1244330673, 1336672059, 158671117} ,{242602203, 691203944, 659415329, 244392405, 1932364913, 928415720, 334469827, 878947242, 1839481743} ,{1530867026, 1606971730, 821023646, 586972799, 1988119487, 1715413365, 757499880, 321847841, 1982846925} ,{338302162, 47643598, 1278014333, 520266418, 829264611, 143986206, 1871864255, 1475726664, 742257008} ,{822914522, 137754382, 122873188, 743761397, 179344319, 64482664, 529800985, 1095201964, 225520361} ,{1737507222, 2122576617, 1741446454, 211621639, 119010374, 1729197454, 491982251, 1220543857, 1517181124} ,{984740139, 346487933, 87974107, 1254042002, 382521492, 2041938349, 339663326, 1779404932, 1048219954} ,{1798217298, 46475621, 1645608055, 695929364, 243191806, 1349834849, 2139533499, 1987202104, 2124651446} ,{1188180731, 726697795, 195607013, 256884162, 1889102901, 1491950545, 210295234, 1686519383, 1620943664} ,{278862120, 215713201, 874960371, 846119506, 2076483696, 118111459, 726247178, 377184629, 763353070} ,{1009636359, 747852160, 759666060, 1857688546, 876452674, 287105252, 1947669410, 102262429, 1807028465} ,{1904810952, 1700743253, 1055982819, 1354869209, 1397218670, 175065961, 1102083915, 432268927, 1997543822} ,{546745406, 758281161, 2084218699, 719479370, 259109418, 1211165363, 1982433369, 991995332, 1200401240} ,{404668322, 1408961536, 376469857, 78378137, 2060569284, 1482075988, 716736720, 1975003932, 510324449} ,{1950019430, 391011286, 1119675077, 193894832, 656174189, 782246309, 1812256510, 633346877, 112380235} ,{1044576417, 192319852, 1662027516, 1432939780, 1324847420, 1652079206, 1728044888, 1687117528, 1593349744} ,{1918407926, 667389315, 32649833, 1654963442, 533290201, 640191635, 1419195496, 728748289, 1244595941} ,{275703173, 1637452625, 1686746888, 1037998544, 168838366, 789337238, 1195730490, 1693234696, 1294193140} ,{1016855007, 847384026, 24485870, 603014919, 565774630, 1243104050, 663141276, 2088902390, 908400617} ,{642774878, 1168784672, 60726582, 230824238, 2063053243, 729065100, 1953037444, 2124241509, 138452709} ,{2121245, 1710782947, 1763633072, 1644994108, 1817878140, 99427776, 1299336621, 1297600473, 612098698} ,{40512603, 1938027419, 545076562, 1908452280, 774305418, 98486908, 1369599603, 1751214966, 805625274} ,{1510642849, 2135735685, 2111563020, 1174158290, 45942450, 425894682, 29764599, 1056366498, 1608949458} ,{2073791654, 977301168, 1337462367, 2009348477, 1736545412, 1754712642, 938061307, 285162649, 455622479} ,{797073400, 975784015, 1604621280, 1822308587, 1721476333, 696808325, 1897609278, 943396975, 854416480} ,{1527019952, 818851597, 1733670399, 2018382919, 52917387, 1572884802, 1397909083, 1851237287, 156603613} ,{929642414, 85833114, 1033356699, 1094563235, 1343623769, 1263696822, 805811176, 383032594, 565317267} ,{888215836, 439742985, 1384865599, 1385791276, 327436147, 1846854360, 738466573, 375708699, 88563920} ,{137027262, 871498931, 332321553, 828983832, 253138612, 1038287754, 1472159392, 1602218233, 1088226601} ,{985655190, 275194479, 1835442891, 2121559317, 791017394, 1003956986, 2128303040, 1376653253, 1705053126} ,{129963494, 1328227957, 481610153, 1852352578, 226802179, 638495640, 258136679, 1654630558, 1580416766} ,{1281815691, 1441348019, 350224880, 1141749918, 1322742180, 26344886, 985249264, 1226409405, 1430369800} ,{825652127, 734464994, 596786811, 307817971, 1853329029, 1197106026, 822044796, 1738089210, 171255982} ,{2015246779, 1861137250, 1878382694, 1443996805, 524431449, 1048426695, 974562439, 995525769, 1142365071} ,{998216866, 1665058287, 998921918, 406699795, 1131346544, 1959927478, 98198936, 2090317085, 936606476} ,{2038056788, 2094672319, 93263297, 1659498008, 297688812, 92862255, 1671374243, 1396639570, 882606786} ,{602055180, 1739773242, 738925458, 2025587000, 962186980, 1529053929, 1170124224, 2045540251, 1427363898} ,{1673437224, 223638208, 1556107233, 1480412948, 571914072, 67431909, 351390059, 1250052245, 671266698} ,{161902983, 1272870607, 1764726019, 717886458, 1286697009, 1538327110, 152569707, 1416106337, 468937244} ,{756625944, 742239012, 677823511, 988850250, 1984297806, 906916661, 467776862, 1039876713, 868721680} ,{648620439, 599840271, 841284404, 543528129, 1237493868, 265448935, 211506365, 1006371813, 133644457} ,{1754073974, 1334933764, 502600037, 1253997242, 251972055, 76762779, 368358882, 1339819998, 1715770338} ,{1241712275, 703177851, 1054459011, 1827615807, 2124936837, 1879191290, 1470483883, 1635068995, 1015791777} ,{1116340789, 1554772269, 1201988928, 1530564022, 2080058113, 2130420634, 1959580259, 737336749, 640874245} ,{1486726862, 1824691088, 1958370388, 703591436, 1428037722, 1519085051, 460322979, 435104211, 309383607} ,{754724552, 2045662252, 2090439347, 298466512, 607772801, 922515200, 1767559027, 431440129, 1423136397} ,{1121604336, 851315076, 834995891, 1352658421, 1086564989, 29256829, 1791123642, 940513511, 1938501266} ,{602969847, 839248073, 1578927883, 331047369, 79103849, 1379827903, 1418110664, 264205366, 630877065} ,{368192427, 783861679, 899596833, 1623735551, 125600974, 1406613606, 1051527165, 1792571955, 1108813525} ,{624768090, 1917443175, 655095013, 74862595, 1835705879, 625032520, 436333631, 1103261830, 375822415} ,{932538123, 420435898, 4858408, 1424765388, 302319143, 416170102, 73947237, 1076761308, 784446144} ,{1477989521, 1060497984, 107824065, 46030912, 1718769240, 650575834, 1383361076, 1279841453, 519205455} ,{418017327, 347587950, 1428602953, 97276771, 1210086294, 952901162, 1309835911, 819950208, 1782097180} ,{1793180031, 1766001809, 1448139659, 313299535, 1869941810, 172021157, 1065809707, 691885414, 983536821} ,{2013330655, 1905562204, 1654841975, 373596080, 46212625, 664025563, 1744555575, 1373614364, 2142427974} ,{47670911, 490436915, 1751481182, 1021192055, 388598290, 1654868102, 1387025711, 1716849062, 2449483} ,{529609421, 2143759483, 621077523, 809611534, 130470346, 2065770368, 757051353, 1940507931, 632408576} ,{651806519, 50112340, 684080285, 669351082, 1491313411, 1862089523, 354471499, 437911875, 801173343} ,{164527585, 1772972135, 841249754, 369792140, 1825265240, 104774745, 1341449505, 437155725, 61648001} ,{906530855, 1174171624, 1981550144, 991391269, 1833486700, 320014913, 1117925648, 1459110751, 356430296} ,{1218402099, 510051808, 429402473, 2143281846, 1820987207, 2123720538, 1618835567, 180825027, 361834077} ,{1923130579, 1321713301, 220917713, 1556882244, 1631630126, 541243416, 1749469435, 407697030, 415194342} ,{2179469, 609690059, 69613436, 1894342395, 1710512254, 1575294886, 162505320, 1837465533, 315780041} ,{36270692, 1037825391, 1998032402, 1282226484, 717757443, 166137190, 866142562, 1193824952, 1588408898} ,{562225255, 88570766, 532698182, 525047005, 337338268, 1502404662, 221604915, 2058098020, 1764487608} ,{1798244763, 1437971598, 414223605, 1134803490, 1320552903, 1130708478, 1951210920, 451879668, 2021388478} ,{222276152, 1457147625, 271947355, 907167255, 1298931937, 520357513, 1485686596, 2105163864, 991621314} ,{319302235, 1718070221, 1553134545, 1018198166, 1955750847, 1069443527, 1930448587, 1094160437, 1250672147} ,{1192924806, 941963917, 332010011, 1245045417, 1570862580, 1168685329, 889337787, 1685396468, 1839967275} ,{851973517, 1191615839, 387111526, 2115184860, 601276423, 571302561, 294529812, 1153105967, 949308611} ,{1669472740, 965059136, 2041734579, 60632687, 1989659026, 380743677, 1586351745, 42280488, 686370667} ,{670012590, 1832332715, 1806010137, 465862148, 508807690, 524086485, 11967132, 1750163840, 2043733993} ,{1476719120, 1433951589, 63057995, 230857336, 1731979729, 917099897, 1567434131, 863371396, 1000303011} ,{671598677, 1941461362, 1872136957, 553030040, 1661746848, 1384279278, 1172890845, 679389156, 994463914} ,{770152526, 2037751099, 667539323, 1014659458, 1053203541, 264959162, 789551981, 845690447, 1508831161} ,{427390684, 1796540086, 932014214, 330366587, 270478044, 2076450076, 1923243605, 194102237, 2027903361} ,{1565358186, 421767127, 1312094073, 1768148393, 1990248514, 518136856, 701249444, 1952207817, 984853231} ,{135366806, 88497843, 72143218, 1047379946, 2009851574, 685364555, 1442878359, 434998338, 1642853791} ,{960195480, 1196445698, 1888948915, 444947348, 1615317736, 860377280, 1587816867, 640830721, 1498401166} ,{1400518169, 201495279, 1162478420, 1953380864, 1647989457, 677681561, 1032664056, 1162451715, 704260445} ,{605608716, 1505051866, 1671156369, 400822662, 201344857, 863592086, 2013144233, 1092086551, 1000230570} ,{1257221066, 888818719, 667661742, 444173846, 1634252662, 1093036440, 285098988, 1203382659, 1105694549} ,{1357906605, 1696829774, 1847292277, 1987419851, 941700662, 1758433374, 587595502, 1996509790, 1316664059} ,{48081825, 1221953328, 424623000, 1391781279, 442994757, 1741995601, 2069782369, 1633725091, 1052306981} ,{220922475, 1417844445, 769180329, 577266683, 556813079, 803650667, 1239521872, 1431924216, 1773626521} ,{669596266, 1145306788, 602023746, 896717528, 550011199, 1555193189, 1024073771, 1719407714, 1451457052} ,{92541201, 819608346, 1611048619, 588473291, 994787443, 894593108, 9450843, 907288043, 999359175} ,{938426202, 1959734835, 618295775, 1591017949, 2034940986, 1846034029, 938551506, 1906770016, 938074751} ,{1651825671, 233662946, 247207981, 735797840, 338169322, 836639967, 706003269, 1455064337, 1654257904} ,{847775871, 1030920856, 686139737, 54648433, 1850192628, 1915315307, 767398656, 1504045821, 1431150773} ,{547236211, 570430078, 1554439201, 1015892524, 51280102, 1560013410, 2082176331, 1468246814, 1040632270} ,{215819797, 1939999512, 1678246799, 402647228, 1485227031, 1542340036, 708931057, 1979882458, 577519806} ,{754684385, 999858413, 1950733658, 1432981083, 420999622, 1272589790, 431585408, 2140919649, 638045386} ,{279810211, 917279069, 326356540, 1625329142, 1008283354, 2000461501, 1881052844, 1396457376, 90026543} ,{202074414, 568939790, 845926137, 449327186, 1011893245, 2001452298, 2078153115, 1332586331, 1726604035} ,{1566443720, 1695942895, 1028698528, 2097635216, 994963914, 1147687593, 2022872241, 1497004114, 1974251027} ,{1588999718, 1452361367, 264411434, 118648413, 1076121796, 681463101, 133273531, 1253118683, 959652804} ,{615974733, 390122875, 685406961, 222156034, 721608183, 1570396089, 1102135971, 1017524450, 1439216449} ,{1309501738, 1103269163, 460857759, 1707921716, 885867181, 430195190, 1384031266, 1867755867, 983771904} ,{1578659, 1664947586, 1152142551, 1225624941, 1852377629, 1557020873, 2003049616, 95060846, 315867780} ,{1627251316, 2115532838, 1122376130, 1496622099, 1377217405, 1116498148, 1590595237, 617791219, 1053616296} ,{798330724, 812623105, 107154245, 152375121, 1186075883, 962704081, 2025065646, 87847239, 1047955167} ,{1755962128, 2112648150, 2110676572, 1932455462, 912077876, 209571217, 1715204099, 1224759845, 1020282842} ,{218768794, 1171670608, 2032643776, 263018288, 1462457205, 450856436, 1445347294, 1260385288, 1880315813} ,{1877036859, 532393091, 1317793050, 1833696551, 1134825567, 1915539655, 110483259, 1687653228, 1403140396} ,{820513614, 1579150613, 684892224, 1881719163, 2145812802, 647725754, 1961426763, 1517342343, 1936865529} ,{1269119670, 1383370508, 1943328022, 1758099144, 897768486, 2012920447, 1990425142, 1276198709, 1417466579} ,{1934402521, 1817405860, 194644103, 496382229, 1617505244, 1247739160, 1905419044, 258982293, 775242165} ,{497062536, 90190940, 1975250654, 1815743134, 295780172, 1634205970, 329536741, 1807779457, 1488418462} ,{1126445422, 677982322, 1446581135, 614215882, 652537801, 498474789, 90998953, 1978230619, 2000266549} ,{125637385, 1636981303, 616565604, 623353262, 1420606666, 2129879130, 944849732, 1581146044, 1296702280} ,{1520390064, 206438961, 2050997595, 1907667719, 561802469, 103799168, 726177698, 1438757160, 58237463} ,{1884953913, 1481306577, 1952003472, 860374289, 1949900895, 69953431, 961152130, 1597712867, 2142320668} ,{229221580, 1663906507, 2086654832, 811876776, 1519542770, 1583678954, 888122104, 930530769, 12740995} ,{260556374, 1806427120, 791068281, 824428445, 1777109270, 1070987295, 2026835463, 437158580, 1624103767} ,{403499910, 1654853844, 1396814783, 502676618, 1952799022, 439887052, 383860856, 2086312236, 793093010} ,{47476392, 1906511950, 568831918, 2064654628, 838716573, 1030901754, 1711254857, 1143592881, 545833247} ,{1919072165, 873968822, 818085471, 1983958792, 290043311, 739164476, 129881175, 1401629469, 765840638} ,{1008589249, 306709215, 1707972791, 1427541697, 1333727566, 920070619, 1278573892, 121047376, 861864255} ,{361325413, 1434882317, 949229505, 331957589, 1820362412, 556799146, 1398594898, 1775184657, 876925235} ,{1852503122, 809151165, 808966323, 743560131, 1971219295, 375523437, 355410573, 2015030801, 1373332697} ,{223661742, 1407103163, 1928218616, 783983459, 358017460, 1543569918, 1173220780, 373356186, 871587889} ,{1978961952, 1201446014, 1815366200, 1449678741, 1812129998, 1797299519, 1078937186, 1716295820, 1211426461} ,{894572329, 224333821, 623717064, 1307033516, 1578036467, 877601147, 1068175939, 825797755, 752136341} ,{1562670898, 977610152, 1130139584, 1381535310, 947426100, 215926973, 302945867, 2027123533, 1633797234} ,{1421313228, 1104975564, 829653995, 1349782146, 1544332043, 1466509393, 1712240078, 535749754, 712574535} ,{2141844514, 1879543531, 250335425, 482607034, 356371656, 485652857, 1905718025, 352476637, 2058365933} ,{1289651815, 92445594, 647563420, 962527084, 1816285061, 1251680194, 1712713905, 899948129, 320187210} ,{729927277, 257945213, 1697347149, 976353862, 757543929, 1827831766, 1701851125, 917576880, 436392021} ,{361515370, 170239125, 1157617617, 1905706591, 723702366, 1117450569, 1732877559, 1191825263, 628686071} ,{1216076034, 606476712, 2023799909, 463847822, 1351160113, 1684088653, 682749604, 654382190, 48384523} ,{1291128721, 776585929, 1689292607, 376588777, 2029726806, 432065059, 1430695654, 1379083851, 580679817} ,{1828207737, 1553960567, 1305274399, 1247902618, 681898033, 591965312, 239093081, 1271594222, 451588420} ,{1062908771, 1301120901, 1571606454, 289573198, 1008916616, 2110218837, 510907314, 1613994191, 1625134655} ,{495808174, 1198673789, 1902638495, 507209953, 1453416488, 380895834, 1125079859, 6057820, 1451296251} ,{926775469, 1694930691, 1481235642, 378696744, 1573269498, 189443271, 448562856, 1939686662, 1109127531} ,{1110218712, 530117828, 1695158391, 1658154479, 273288823, 1430031467, 1946266654, 332716056, 1874825121} ,{967757115, 1273718139, 1843110943, 585744765, 733394918, 398132237, 1381642113, 1498034396, 1927069797} ,{1995530754, 1280260673, 974062690, 729049423, 1146305118, 2085063462, 459038843, 890508375, 297756275} ,{319350350, 1077192240, 1910327595, 1806517105, 1479036444, 2039161441, 1303995273, 598499483, 1591684172} ,{1892014568, 621834484, 1439725775, 81690329, 1356014130, 818622844, 1582704509, 494111392, 324081684} ,{413492036, 1884057184, 209696323, 568346019, 662284859, 1309838242, 2113484950, 902368110, 1420197844} ,{1753917772, 2059139364, 1964501144, 1206692879, 77676971, 586274685, 148692269, 2121018226, 2127049333} ,{33938924, 138818548, 1345698759, 1772136659, 221403911, 556436654, 506700836, 1165294637, 1638911888} ,{385409272, 125992990, 1945512779, 63528797, 1315115114, 1279589933, 746037026, 446660932, 299790329} ,{841342651, 1000089353, 2072016732, 2023681888, 2064917843, 1886735431, 445291690, 794087973, 1035367776} ,{141045629, 1581050638, 1162352612, 1237861732, 140611341, 350568662, 1254401945, 1335691963, 2142725348} ,{450278960, 1456614801, 309105943, 1667973816, 1649590473, 1299017165, 391320847, 135147149, 421172965} ,{1165316584, 1750897556, 2055628944, 1808267138, 1740921876, 1772533481, 1542616408, 2127344854, 352106083} ,{394543716, 1862152511, 1787270994, 1274896022, 790812510, 1294565481, 1441547258, 555606117, 274227822} ,{982982717, 1889520909, 1179966927, 333641025, 311788664, 1223354411, 1932908889, 623490555, 1704179705} ,{1516248862, 403211973, 453694848, 474468131, 14028452, 87273236, 2141136133, 1120934855, 1232313988} ,{924178515, 202592853, 1202277513, 848451657, 204373390, 1489743108, 1539860884, 738997077, 1771085513} ,{182337220, 195429464, 509950967, 1073876731, 471922239, 131611437, 141764417, 702136638, 11426051} ,{1023801411, 1832325720, 1273670033, 2060851617, 1894422702, 507627585, 1632871290, 415604842, 1963709372} ,{226034514, 850325955, 1545680774, 971129977, 1625285548, 126968781, 1608615883, 730236965, 1906573522} ,{747155155, 570488806, 1821436846, 1996064636, 144020266, 1919597237, 827455266, 203216740, 72894407} ,{1975350561, 876678435, 249400763, 383923575, 926675840, 292679921, 254336973, 1690770005, 871777250} ,{917905313, 929893082, 516017221, 1759864350, 1655623308, 2004490593, 1210309884, 1765752620, 1037609891} ,{543993721, 2001775432, 322279260, 717620696, 1783225709, 1918751812, 939206314, 1892137039, 1013603563} ,{186436467, 671457816, 1693225740, 1166392421, 737923596, 1357824329, 36871219, 1046113312, 1660274125} ,{1488529632, 477941971, 335281994, 665823556, 2031411124, 1245446788, 758369077, 78271832, 1145022524} ,{662064504, 1002313423, 705626374, 2060483791, 607064717, 1200087576, 2003582640, 1359229867, 882953540} ,{1160475030, 1307547777, 1554940538, 1318758211, 414607606, 1975123340, 863647369, 1982849273, 1116474731} ,{1569356090, 50348008, 938418331, 630542835, 1234165364, 574815200, 737233011, 1553627287, 1442015090} ,{177222733, 1466718577, 2030522915, 1369297038, 1751436145, 653071344, 83961027, 2091529955, 482529749} ,{1331412249, 574263205, 1284069591, 365232225, 1270557298, 1074636559, 800572613, 1480896181, 859617817} ,{1013758102, 331582107, 129436902, 1036033196, 2106683595, 191515136, 1780393522, 627643828, 598518067} ,{217726531, 1018298656, 793316383, 1812503834, 1262328712, 2123210376, 1768179669, 12524944, 1687441711} ,{1205301124, 609750432, 1082254582, 1402457235, 1073415867, 617391768, 1725160922, 1013399277, 396603573} ,{1308716800, 406267518, 23878212, 41101612, 788618463, 2095700285, 1974737671, 531059433, 1998612032} ,{1154016204, 1952633736, 750715555, 232798290, 2126262548, 1978224794, 1561575430, 759988713, 1108647222} ,{1291225007, 1008966579, 1981430894, 961702379, 1644001580, 1308657103, 1845523455, 1926381905, 1506638825} ,{148396952, 783917433, 182306651, 1664631949, 57467914, 2020468688, 334136069, 366568319, 1969278467} ,{657018952, 1447289791, 740583510, 1556801876, 1183668090, 358339699, 507790433, 627455948, 1823051967} ,{86983103, 162680032, 776516423, 265704861, 57321662, 454358493, 2013355321, 1815888328, 280754206} ,{775425532, 605704380, 1617239729, 167980740, 1419199813, 1622795632, 190267799, 2141496475, 132805135} ,{773257834, 1715846509, 1928506754, 1558448517, 1561198091, 800961173, 896551067, 1996274920, 862501869} ,{998616948, 248102626, 129012380, 2064178262, 1598704363, 757580153, 1259723697, 1301152421, 1250675198} ,{880343655, 1863373321, 2105035384, 1857159911, 1368714148, 1018382130, 674859815, 1084088064, 1418780055} ,{112602122, 1037404637, 74294504, 782928852, 142802223, 1212742259, 1689944178, 2124183672, 687828126} ,{1091210826, 34030865, 276601208, 1625099308, 760824267, 296352170, 1246619772, 329788929, 1611765783} ,{1856055020, 878072724, 1687970149, 645657301, 987344413, 228453467, 212147065, 1108276740, 599447554} ,{1176760179, 689306696, 1903526300, 106656819, 1929453400, 170066451, 877635401, 2054342258, 995685245} ,{1175550145, 1891557468, 621756590, 1461166639, 887764311, 2016985829, 882179191, 186036281, 930025795} ,{909540393, 1060708465, 98402807, 102147449, 501965717, 2138233698, 1282756295, 1263795481, 167011481} ,{1632029803, 1540865281, 1390654591, 543623286, 529437793, 1291777397, 616409955, 1938492537, 1676326505} ,{1563854753, 437277942, 142170779, 78785716, 410035650, 1285793149, 658510818, 1781042212, 1075161064} ,{718562211, 1268698251, 1424219845, 721223157, 916061315, 50833836, 976931273, 1775161582, 509503126} ,{21223153, 773505485, 1105855199, 812834413, 1717330029, 14458105, 1637665774, 1742889945, 1644189460} ,{1541459338, 1540433296, 2119300515, 878919434, 816910857, 786174163, 18778458, 657598609, 1553118985} ,{578040008, 366442929, 1761954929, 910551342, 1047324287, 1023122385, 1940733006, 875539884, 1170382057} ,{1908336557, 417357461, 633982328, 326626346, 1109520819, 1574800766, 727047154, 1575398395, 21260469} ,{1987084434, 51467765, 1667938435, 524159680, 664697122, 859226766, 2011454161, 2075008553, 1334308499} ,{2046164982, 648932190, 630398854, 37034695, 2076494092, 147522811, 1759881876, 1139062956, 1248325328} ,{289792054, 1407924991, 1580157097, 667929756, 842876142, 662252678, 1133620295, 1881775388, 1043248215} ,{1227599214, 492406667, 1361043840, 442534881, 374253430, 1565985495, 276078005, 882778491, 1246396964} ,{1569635301, 1967918803, 324362964, 487138452, 1831637403, 1525920630, 864032567, 936346330, 430174364} ,{551099401, 2009569442, 1985914516, 1502298049, 478785362, 1924058834, 668248830, 194669135, 2021263900} ,{1621901692, 146999733, 281750777, 574100780, 95682345, 867824329, 944379903, 565690361, 5064901} ,{1408739784, 950044490, 364786612, 2126350301, 31845545, 886482295, 1665130544, 1973051340, 1488761223} ,{1337200839, 1575285233, 70363330, 1540331222, 1862042888, 999890757, 1545762665, 1536185201, 1431752250} ,{865129884, 413898425, 1698251676, 1907289030, 2095282054, 540599695, 972170871, 600661584, 167063582} ,{1845812061, 1171622091, 946728208, 1652686902, 348778754, 632962052, 2013681678, 733149950, 1165918095} ,{1733777947, 2124763533, 465682686, 1313979999, 1286144105, 122076881, 530329960, 1128364513, 139597249} ,{1690063729, 862021189, 524051942, 1049212532, 1146452025, 1236866525, 628019570, 1903147506, 1124595492} ,{907069566, 1008625243, 114070191, 737040565, 1458969473, 1335395989, 613049236, 92976603, 1748550209} ,{2135124612, 1062079847, 1500528407, 1086191315, 822573535, 1603276421, 1046755842, 934575135, 2106084973} ,{1047627501, 257365979, 1973509842, 1292968809, 773996951, 2145554183, 1544688425, 1289198772, 1611267569} ,{164822113, 1672625393, 98994658, 1013619941, 1619336593, 863318377, 1622194326, 1090097021, 1345176818} ,{1974079094, 662816549, 901315725, 1404954277, 1273194618, 1475127419, 277646946, 1952022940, 589986589} ,{1221928966, 2053067688, 1348774124, 1015662022, 1895743936, 122153997, 983897678, 456549470, 662209798} ,{395887936, 1449987956, 755598791, 66651997, 123059575, 881930449, 1413290775, 1182612961, 1964002868} ,{754765805, 1074999961, 270866990, 233463125, 2081150829, 1347702841, 2054028825, 51423948, 774662259} ,{478040520, 1770281840, 1860050135, 1342039192, 222363887, 1602967658, 1084263090, 2036288934, 1012901313} ,{1985398838, 669804933, 1690551760, 1296620093, 420788029, 1907650622, 1237622407, 1615071527, 607132271} ,{1084921550, 722343758, 778045294, 2028913134, 741134510, 1416363039, 174944048, 679192529, 1469784766} } /* End of byte 5 */ ,/* Byte 6 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{1010845446, 1185423608, 974952887, 195798204, 965458505, 1475872796, 28900679, 78308784, 1367547160} ,{828093520, 1013625014, 896623716, 1597555669, 1511507719, 723792282, 858044782, 1511988360, 982311793} ,{606719862, 1575050371, 1434853565, 75277686, 1646024060, 1577446784, 1312243920, 1763475961, 1136181656} ,{1950229895, 722176570, 409670134, 864750680, 875145109, 1650208842, 46845404, 1277851518, 213148713} ,{1710662475, 777357780, 478529824, 416549721, 833494699, 1789238649, 818663938, 2062752625, 648402192} ,{55502294, 461865017, 1430680971, 901367456, 807489348, 1945677767, 1501313029, 1342275537, 15570363} ,{1717803522, 882607222, 1121757679, 1449874744, 1508206323, 496124354, 303869061, 1450894755, 463522247} ,{361689396, 783372337, 534639621, 855889947, 1416274984, 227284319, 347715927, 1485808649, 1862609598} ,{2044039206, 211339401, 1659135387, 2038311486, 1101016176, 140170773, 858456340, 1737851767, 1244387334} ,{526563173, 325237807, 1120876135, 272741475, 791298515, 1536805067, 1334613638, 1738361984, 612368550} ,{1159281593, 277260141, 1956808148, 1606202289, 145864340, 1267375954, 868981470, 228316951, 1247069413} ,{2094176893, 1943935479, 450852161, 1956585935, 625506981, 459354290, 1363819983, 1801371488, 854020419} ,{1165308637, 1184546107, 1354767667, 710810987, 1504234539, 454565537, 873547182, 619817838, 1072549931} ,{719472700, 1974265941, 803529679, 342444041, 402662035, 1722450496, 1918658923, 2140519612, 941970359} ,{470528619, 1294051152, 956937760, 56193556, 1218572465, 1021991953, 1995104947, 109526101, 1287612512} ,{52477752, 1549883615, 1746505134, 359921126, 1131246672, 672522943, 1833244651, 899401504, 1245952875} ,{525376659, 49874804, 277863398, 1570446196, 2022440287, 1906102438, 1543462827, 1781853483, 1720914115} ,{1724729931, 854099051, 1462999730, 737555328, 1270392188, 1431559581, 1069328059, 255916325, 1717692386} ,{614853198, 637782648, 1019750612, 101090320, 560987930, 207835664, 1376504859, 1659423084, 409686580} ,{575374891, 1329809298, 955397260, 1529414017, 571881912, 162060445, 1435547016, 597475832, 1651255559} ,{2004278544, 1166179260, 348435514, 1090688254, 1295265830, 894178499, 2075582504, 793731107, 1769079219} ,{368310082, 1380266357, 1565439336, 934082844, 281763984, 1466106193, 300682892, 2117334020, 1688180724} ,{1993165711, 116954790, 587647901, 346894244, 249534534, 600462797, 55363375, 1938129798, 1503924387} ,{1152693577, 1858223392, 1790528771, 353006094, 1538981208, 1132658093, 642479162, 342886752, 559977386} ,{532509331, 1417902496, 1385748873, 577583374, 409658593, 265156589, 111828061, 645817735, 1794235121} ,{106203131, 293680559, 1932393013, 2041452929, 1188479665, 1437625469, 248108093, 1202995820, 767431128} ,{1094414788, 1376456798, 1870212587, 1785475904, 1316671204, 1852405081, 1329244991, 353872516, 763319094} ,{1746992353, 1924720836, 1875978055, 844470466, 1600615262, 561654236, 1249902343, 729127193, 593801468} ,{918093300, 1541539892, 1546694822, 1806315871, 1552718003, 1220207237, 797499742, 1741939326, 1587284333} ,{1194747700, 493036158, 14781403, 379304211, 1628098847, 74874463, 310697888, 1129553409, 51639148} ,{908180984, 246197903, 280984950, 377923822, 1480322790, 357831288, 1014737458, 1955500994, 151652348} ,{860790103, 1419580720, 575506682, 776247381, 652000714, 1440426731, 1252473332, 1626371903, 404893095} ,{1095997185, 410359166, 1692924060, 1276460712, 402897431, 563631063, 1817775735, 103368293, 1762582899} ,{700788643, 373695532, 270740455, 216351396, 1671787384, 450542054, 1616289289, 617017786, 2100076253} ,{295209045, 144860673, 793138207, 970948928, 1291134404, 578408419, 483321078, 587038186, 1729916133} ,{160721774, 558865370, 34735709, 347523080, 1067829521, 2005851302, 1694919765, 291172074, 321674201} ,{987286429, 334815601, 585053518, 1384535913, 397784349, 955632992, 1539072159, 738486475, 1749926818} ,{433989762, 1826566807, 864164120, 1995303451, 708363326, 1889418755, 1813480291, 394217708, 1429380587} ,{495658462, 440182804, 1136235181, 819702880, 1863058362, 481450707, 1719346710, 1291673355, 874101776} ,{1523336587, 92797828, 1127202006, 706923666, 178724560, 311249299, 910890569, 1354479608, 563650156} ,{116840234, 1861664299, 465940088, 1666216813, 487147898, 1176525123, 376278842, 1749267583, 197026791} ,{934192639, 1676945721, 634487788, 468984439, 1356119833, 1242604115, 1810189083, 157540807, 2052599827} ,{1032313233, 1187764914, 478906207, 1464469376, 1049064497, 74836006, 1096958699, 973159649, 1674826261} ,{383880664, 384255930, 614654692, 728784140, 524624967, 679394427, 1128485919, 226439886, 1089616101} ,{1013951152, 978141347, 631255539, 1505843583, 731232498, 1911748632, 1250633706, 1712366880, 131904090} ,{1326859209, 953030753, 856638458, 1262237166, 2072207490, 1454997671, 1528505279, 619886136, 1425750613} ,{952621074, 39719954, 304987824, 1269739364, 1760181396, 1209541131, 1062764434, 253526718, 1456698680} ,{155845806, 1881411401, 1891472622, 1505591519, 1989851787, 323626816, 1348706507, 1785163518, 1150296191} ,{976412265, 888267828, 25044846, 1954418857, 2129542266, 1083394447, 397476066, 312063681, 2127694160} ,{1969368663, 1422688259, 1975063752, 1394555613, 758537854, 1055531774, 961382537, 735949178, 286207277} ,{165338260, 353685534, 1993994414, 908647905, 478817096, 295817143, 65097314, 241618751, 1575366539} ,{1055590865, 1636192854, 2055075841, 1889480820, 566744125, 1803606963, 1745935951, 312035596, 28034937} ,{1654678319, 720050555, 97658948, 1485260551, 1600209313, 33538961, 193294494, 773362384, 1759029208} ,{1377364732, 1972978827, 1567042576, 967841335, 935732769, 631907712, 486881418, 582684474, 87548109} ,{1190991179, 63425356, 1922068486, 1194518606, 135393121, 1686687908, 43237530, 105643647, 850037121} ,{358186177, 1567743821, 1072977755, 965443735, 1366375392, 1120256112, 1969378304, 705658058, 904646707} ,{830857982, 2022444985, 1682604904, 1753794190, 39995287, 1946393056, 571870605, 2090510390, 382058474} ,{2090688986, 772760841, 296024782, 1479792825, 1503637810, 685264790, 1129923929, 2050687091, 1967013334} ,{1965362624, 900668696, 1469804182, 1494934601, 1560439068, 642288507, 1996622481, 903490915, 707346336} ,{597301364, 925705327, 1952190270, 579191448, 1255044571, 1146146579, 1443664785, 610779685, 1578142593} ,{2004805839, 1940194343, 295537613, 1485421396, 988765874, 1131138117, 1725000372, 22677807, 1732447918} ,{1682179737, 1174549864, 1975962520, 566532675, 522782980, 263664409, 702563971, 1501751291, 1178286034} ,{1760275605, 600942796, 656286365, 2027028135, 1363349910, 521717246, 1117930423, 1957875034, 33848554} ,{394152262, 1033695231, 1526975574, 1730773736, 1926374746, 1110490304, 1567800697, 859920945, 14789680} ,{674887435, 447722514, 1637932825, 1668104124, 617943494, 1822124647, 1751808475, 518100246, 1080853502} ,{1275180795, 1521726288, 579655008, 490405482, 1006731037, 538542098, 176416909, 1395027205, 1055342427} ,{528017271, 1263741913, 1116718073, 1547155479, 462645228, 112568227, 1828938470, 1980768838, 1702111417} ,{2146131034, 1006033138, 1745886263, 1284508627, 577428485, 1758125829, 2096909864, 980086904, 663894798} ,{1327300304, 691852480, 1731280405, 1603820906, 446452113, 656794103, 1823525016, 536090421, 366194648} ,{185624074, 1421804447, 114947535, 697837090, 1873211202, 712590927, 617159987, 1233450371, 475135931} ,{1886262113, 2090975260, 229139864, 469123268, 484951768, 679164244, 420929108, 1180791539, 2111784194} ,{666314209, 2031249400, 1608192009, 1430195362, 1956791285, 616923421, 1499642994, 261075220, 1865414958} ,{446720126, 652938829, 244877894, 1898982593, 811012452, 1140831879, 1885696201, 1346363105, 876656277} ,{197388827, 1560289682, 1440707254, 1896293631, 918982494, 95339222, 1622086891, 362195578, 577465677} ,{1431327477, 570026073, 1861019631, 1121525940, 531898347, 1894422556, 768475007, 1604178964, 399932439} ,{1537642109, 294115567, 1786261383, 241706527, 1811459693, 722308470, 459711595, 1691420204, 1648959351} ,{879869690, 1483433393, 1242245767, 1540715016, 1369398001, 101737178, 991889390, 119811906, 1971708928} ,{951995167, 1113918890, 1448773123, 83424507, 1157047979, 1532339858, 697705808, 698220933, 161051376} ,{1265192764, 964797552, 235909758, 98891367, 2120873526, 951476814, 1406131426, 250990462, 100291582} ,{807388021, 1176912172, 612357473, 644080045, 797375845, 1001700547, 1013381558, 74648135, 234378086} ,{1704083175, 743851036, 1929369502, 595467590, 187789086, 1758034660, 1314057371, 1316663516, 1444943773} ,{637282926, 984196669, 2072191940, 1622017991, 1948649752, 1405392739, 1472930299, 999118204, 2028455110} ,{1438729674, 2057176357, 650544771, 173748494, 1006983618, 909375877, 10140667, 921818537, 362224583} ,{1223335986, 13454721, 1950141114, 1227290268, 1108684251, 229532220, 1869804837, 250736028, 1879920100} ,{20678459, 1961419548, 959974339, 595228733, 577450390, 1846807905, 850462484, 727063593, 1718842503} ,{1471171607, 923702165, 1867738696, 370599106, 772471217, 1160411379, 1354220531, 1077737926, 1039508037} ,{341034651, 1365402302, 964874569, 596705068, 1038814725, 279672533, 115369906, 1200570695, 81744070} ,{767929018, 178105345, 213994769, 354693950, 2076771569, 1842065471, 750387860, 92058199, 1937177017} ,{1869471583, 740620837, 1741872220, 1864535522, 673778026, 408428330, 525180481, 1788096536, 1765931156} ,{2010363455, 516804780, 1939951973, 2061420823, 527713027, 1423790306, 2085870460, 1331366162, 276483235} ,{18710932, 797409125, 535553951, 2067035857, 1202354299, 2079350487, 559400557, 1549123115, 123290297} ,{1549170331, 1118081741, 1319124961, 1388243350, 14562507, 1756988131, 1885295121, 835764240, 1869528670} ,{208131581, 669703155, 1384192103, 1123970426, 1984726690, 704231284, 1567207883, 897570214, 1026332021} ,{557649935, 1947454842, 885745216, 1962098832, 259401207, 1215406453, 1856217328, 23932579, 143650622} ,{437088638, 2032397051, 1138071816, 1739523969, 145066102, 1664667663, 877560681, 1539751913, 1002017303} ,{1714860078, 185379908, 1018700536, 1606935508, 891255932, 1517914163, 809553126, 1967837008, 1061506438} ,{221982098, 976419092, 46159185, 972685256, 765860331, 683976993, 1202631608, 1410757652, 1560585341} ,{642022817, 880466959, 2117288053, 596480403, 1533485489, 333636426, 497276751, 1603549546, 1616488242} ,{775370766, 65398679, 2033003269, 523119767, 1779188765, 438264005, 268998132, 1717460609, 2144445193} ,{1518809974, 834229170, 77683172, 67483959, 1902386099, 1805884232, 734113879, 1850653566, 1898902445} ,{133723487, 500539717, 1707467560, 2044892668, 1825367778, 1205355773, 533973763, 32640751, 847575874} ,{2092542799, 1779245601, 452253966, 1076101823, 1886229719, 939481439, 16061280, 1070480375, 974690676} ,{1216422181, 1164500429, 129975712, 1534551617, 1681264021, 846126848, 692824774, 647817852, 595534314} ,{513942070, 1918596498, 436206463, 819641979, 1191256727, 664974950, 1277207054, 1875858063, 856828952} ,{1945568126, 1235149504, 739787128, 1816496219, 1053282634, 1198445754, 1823569667, 1929866112, 1666319683} ,{1503507739, 1080982572, 1142653727, 1081092992, 466037640, 447380681, 1093444671, 1879503363, 130334010} ,{15623656, 957656189, 1272094645, 2145774783, 1185860528, 522691180, 1089152732, 905577868, 1727901733} ,{1233210498, 571730394, 914360178, 247809634, 975190414, 2072842002, 403613842, 321293154, 218492716} ,{1190979421, 501863013, 242806040, 1413351682, 1545822076, 192394398, 2108203283, 997603080, 1196660998} ,{901151912, 937927887, 1845292475, 136158271, 282712870, 1911014906, 1820682352, 787409636, 1725106415} ,{1469868505, 1160745141, 444196245, 1029213162, 1758989177, 1183162076, 1748076436, 2135373327, 1763227789} ,{1849756642, 1101130617, 723499187, 1868021180, 1516163668, 668464157, 167414185, 198813480, 1446578950} ,{1423402603, 2103058676, 556619033, 1712799159, 519814337, 1926990312, 1170662612, 444073786, 1008633606} ,{1203060718, 1788231823, 1631196970, 1503707168, 1175972270, 1152412207, 1979471974, 166646883, 1439768408} ,{505505566, 1840858588, 447371272, 1727928219, 928483086, 160693087, 1142747968, 39476242, 1129414766} ,{1060171767, 1994218857, 715422151, 806386480, 1696381076, 227294368, 1494862581, 390558759, 1452311328} ,{359544634, 637557112, 306131092, 523536355, 1935734286, 2014955043, 2070503021, 2053718127, 1968552218} ,{1813369212, 1560688442, 1450456514, 2026906495, 1387818294, 67015759, 1319626816, 1135540380, 990379161} ,{1838539962, 394368814, 2037599288, 1781561788, 1960359044, 2113111839, 1190635962, 1579587566, 2051748490} ,{489950253, 417652337, 738560623, 2098031883, 1174823902, 1642034119, 56474499, 1970891626, 1129256927} ,{2024409181, 49281830, 1550541871, 2031251228, 168300050, 1595596446, 1636328209, 277420349, 1037570569} ,{524454163, 1224624913, 1528144952, 897756704, 453230916, 1363188503, 1373151523, 739276218, 2127219522} ,{16928798, 1730108553, 151068350, 1524958826, 1780625884, 1509435109, 589133703, 1640884577, 884193735} ,{515563831, 113683729, 192273427, 1870013995, 1156226813, 1114352394, 1305488642, 1627841335, 49611434} ,{1666587842, 1269013217, 895168829, 333754400, 1298488173, 900455837, 257886739, 136119859, 1143472040} ,{153913395, 1182360144, 1938762806, 1611071696, 544951898, 591619544, 13709048, 317783341, 541101911} ,{135872676, 6531323, 365419477, 1434299482, 1980913177, 1676962804, 207979225, 1898377665, 1853351906} ,{292247445, 760282231, 1697539989, 1620593212, 1589010583, 1194866537, 957214154, 641323164, 1794044205} ,{1192713369, 714058468, 993841424, 924806375, 1266380047, 1262976430, 1625541497, 33744304, 1684066270} ,{227215546, 3982018, 99019110, 1555163464, 412004042, 568701671, 1081089531, 210414487, 1089978248} ,{1658185801, 168776531, 1567522392, 464603135, 91815065, 1843269330, 1996119950, 839702976, 1515905941} ,{2074595067, 1716105794, 1427141797, 71890982, 283545152, 1741616797, 495453371, 1079598308, 764679203} ,{1089372847, 2055094822, 901775051, 1810018705, 915668182, 277949955, 267621655, 1179480214, 2006088660} ,{187307701, 6814060, 719892900, 399039918, 138435376, 48630547, 1454491485, 426838810, 1062783616} ,{1973276954, 2098716011, 219461837, 264859058, 1801763605, 763823983, 274245990, 1242614785, 1903719342} ,{1719201297, 1871797522, 601993606, 309069010, 1783404395, 947954521, 377583706, 821159807, 100134092} ,{250761804, 1472462855, 234232391, 1788008917, 778666221, 1814192953, 995129228, 151223499, 618363814} ,{1145860259, 992413067, 176779537, 1666064051, 554948081, 483987794, 1510622362, 1352094589, 1048082616} ,{1779656535, 2145736294, 594667502, 315635018, 592112017, 985985595, 1860185279, 1704679635, 1606086880} ,{1472091638, 1672825750, 868945278, 1153865862, 798184853, 942845312, 1827439607, 1265881276, 2073113324} ,{1831226355, 96460974, 38265473, 2029198463, 1038994939, 1066144485, 201985815, 1421632516, 111926829} ,{1484413106, 877750416, 18690160, 283305159, 1403516219, 1002450923, 2137438531, 716317679, 1045430849} ,{199531952, 1325510499, 280562207, 788167776, 1364709130, 396680857, 1967085007, 851307300, 1066751575} ,{1699009518, 2053245018, 1340802145, 1986009214, 263092834, 1243843322, 1120205347, 198072972, 1482257482} ,{951993205, 703213814, 78898933, 401800135, 1024508440, 906887104, 190985176, 2052942241, 413352838} ,{1864468185, 617518341, 1649854390, 867597031, 520637674, 501825388, 185725316, 373096441, 497428102} ,{1351412907, 786315894, 1796398470, 1575336614, 265035492, 1064940653, 672049965, 1876990038, 1540824261} ,{1308134179, 2099888993, 376689495, 1556853536, 478511498, 1534667848, 698549207, 2063980590, 829860115} ,{847949649, 1932160971, 1800848372, 1458030065, 1849671803, 454262588, 353366278, 1119603503, 1779933124} ,{1517876731, 966179126, 31597992, 1384667406, 2106266160, 1616038276, 1183971513, 218658100, 2029735825} ,{1218161151, 1593427372, 82697157, 593702576, 1893319750, 200703328, 871444451, 1469813024, 1808381921} ,{2078870898, 1037924789, 1911997961, 1245739058, 205110430, 95571610, 804806074, 319811838, 2025911569} ,{965606428, 1992018918, 805966094, 781619926, 1866295335, 365566480, 678017826, 1188558781, 1557673944} ,{563692835, 841613112, 447210399, 1476113984, 230081497, 107449708, 1268596460, 1032105223, 190967216} ,{1692050376, 2103857078, 229057603, 1449771261, 858565626, 1082142717, 1675668752, 4293739, 749688635} ,{363040556, 1267496355, 869053167, 735837050, 1566618318, 710935431, 1801341772, 1393857618, 864019787} ,{1279896421, 1715966533, 2099755248, 291470816, 1192825488, 1375376968, 329883121, 283385906, 1737885515} ,{1672293953, 996982005, 2123851963, 1617038268, 1491008512, 1807543492, 632218282, 1610510234, 283084836} ,{1524551684, 1174059667, 351951849, 1541360882, 153862258, 1069963307, 729083968, 2145174952, 691541213} ,{1131618044, 189976460, 1054910698, 1265288868, 230024170, 223406874, 332473198, 934174884, 332559690} ,{1534702716, 1779253658, 641828153, 1559158331, 1235499978, 812610978, 142665946, 5986697, 911282087} ,{830446513, 586179402, 1790480180, 1653349894, 928387598, 1691387493, 446875281, 1604317728, 1225869852} ,{1226284093, 1144531320, 1604771978, 91250203, 601011083, 2111196258, 651104923, 185486027, 1287710250} ,{733008632, 831962616, 2028627776, 207088981, 1082231389, 1863899783, 1698385399, 1652763556, 74267415} ,{718589286, 157768537, 764748732, 368890814, 1239501057, 550507381, 679445331, 1312200954, 135985629} ,{1850837269, 336063224, 1482091126, 1162879153, 665354075, 654637821, 1360342071, 1867875434, 1514836226} ,{1235104549, 1807386172, 68790908, 434259384, 975275445, 193983574, 1504135210, 981553884, 1061228315} ,{314680622, 2089164201, 809852909, 1222808411, 1570565000, 46290701, 901025346, 1551694634, 383226784} ,{647403625, 1914316497, 1891670322, 1818706052, 716943720, 77112493, 1113061673, 677515190, 1553555227} ,{1728251894, 1572230442, 540569141, 1383311386, 1204201265, 323097185, 964356038, 831038408, 1772331281} ,{1181495294, 2048079723, 80646134, 439671274, 1329480148, 990311276, 1452024105, 468333749, 2131930977} ,{443550534, 18561496, 988670910, 1852465969, 99589213, 1366557362, 725124198, 1383924135, 1225828501} ,{651157109, 1166605760, 1075620610, 1640617495, 585405304, 1905068413, 1804711036, 579471903, 704712685} ,{1235958473, 251825312, 2085045576, 1475020588, 1171110748, 1215104217, 1337636553, 255228518, 867032402} ,{1981430227, 1089229747, 1975112651, 1190752420, 155160904, 1147348444, 2003037425, 1026560233, 1191729193} ,{431221025, 800454120, 1251775514, 1577814661, 595004276, 1186397673, 84917052, 689100234, 1857057747} ,{1436353394, 38819173, 1409652767, 1592278556, 611342388, 1039334038, 831137489, 120264429, 1535617479} ,{1224417350, 1266215705, 1270573229, 333129666, 1196758208, 1911411456, 172082490, 1346642618, 510684927} ,{1915837864, 488774328, 1456331758, 931445061, 797530669, 247727805, 1906497938, 49422418, 565484458} ,{157042262, 305805368, 885147765, 571124343, 136621128, 465588941, 829565818, 66027942, 328259713} ,{447844894, 2108887356, 635963100, 194225258, 1189269498, 1307896549, 1144566949, 759994467, 1674890711} ,{235921430, 217073732, 1975847267, 674388414, 733171080, 1208243055, 1016091286, 963001558, 181372170} ,{1618858950, 952088189, 2015255392, 409012218, 718158412, 1459229380, 1504525109, 848796071, 1602195793} ,{855134801, 403232160, 124414343, 982607281, 1536141395, 211309162, 265204501, 997159888, 2046128893} ,{594613820, 229903003, 1299331526, 1127040798, 610053584, 21788166, 1935950762, 664206396, 2095568263} ,{220087134, 1855874516, 2018547371, 124912172, 1800157864, 1134598925, 10825012, 1409613606, 1951038196} ,{1817722740, 203775416, 1553712757, 409854119, 742467003, 1715056104, 1382939850, 999566995, 2003231290} ,{970153623, 371226168, 547091829, 1342675502, 763886749, 353794947, 1174882874, 287221402, 878107623} ,{1697812109, 99921527, 755864798, 959261293, 477780636, 472054930, 1664387396, 2094712622, 2106863230} ,{1402959742, 1197151372, 1609212108, 227064894, 621993330, 920863827, 2038529045, 50450640, 604335606} ,{1387406191, 751516967, 1493786423, 579807091, 925269663, 1447040806, 1631567235, 1619371294, 1861728263} ,{1985472893, 2121652471, 1693982143, 2134702283, 666536199, 675117682, 1672439319, 1441974339, 54470998} ,{1476035136, 113235308, 328289661, 979114124, 1701948096, 1314609928, 727169645, 1261475660, 1260431615} ,{706485061, 799248330, 1405811264, 676178922, 769388347, 659352741, 1282411987, 978954003, 748876993} ,{2124546010, 756291060, 453283426, 653194229, 1898517096, 1086441780, 73028803, 949756695, 1029231341} ,{712379820, 1988809669, 2022765582, 522852556, 2098181671, 1739476732, 554955267, 1080237489, 538982544} ,{1320395105, 1581326962, 150040327, 3505938, 867895321, 1656059587, 322038748, 749785835, 68599367} ,{1360295476, 1493619893, 589269453, 172649957, 239771678, 588019747, 1135081818, 1708106603, 2111540561} ,{462812243, 978145801, 61650069, 1594699991, 52341322, 1138284063, 2132856841, 955574279, 1757999789} ,{1382732896, 319464357, 1424345265, 921495102, 1887618662, 1553494761, 987996281, 683070586, 145305404} ,{1368067642, 167164317, 1477778839, 1233709611, 435730739, 2050572654, 115833316, 763671221, 275819994} ,{134349804, 1498862611, 1879177119, 631145665, 1542693692, 1881336909, 39593195, 80417135, 977676783} ,{661069036, 1302979431, 369735587, 1346776540, 549222091, 642108539, 37623416, 904111615, 1843395761} ,{147750189, 1045429758, 1682017745, 839744234, 1098758052, 1960084833, 244368777, 432092615, 410654725} ,{1767102877, 1170715631, 74783703, 532235913, 1959539218, 230107077, 1819607326, 539618443, 2092795310} ,{1680377928, 696191098, 1989714369, 712178422, 1191914775, 967052282, 1865994435, 1669693105, 1144393100} ,{1454217230, 1686779397, 1388321145, 305229996, 137889119, 104061543, 1674022152, 1007812431, 966270774} ,{389270859, 1413430316, 700124209, 194339174, 1763344432, 1169811333, 1686554613, 999909430, 339634308} ,{861758454, 770349644, 320860354, 126875728, 868870218, 1971972738, 1563405178, 1512419198, 124775134} ,{333108050, 1681124953, 2070611986, 1253979125, 1127551672, 490795312, 1039840168, 1749028525, 1052262600} ,{1412535219, 356999255, 1831275468, 635521716, 1062536982, 1902721393, 413703704, 920222149, 1706826871} ,{1419813009, 1324372170, 1293393077, 327074140, 617747368, 1793805258, 450812872, 1457779823, 429646977} ,{914873828, 1069956859, 87051997, 753649970, 539450382, 1608744478, 1456726712, 1532285809, 969517990} ,{1352814085, 1738101383, 1158010126, 1532000311, 2044785652, 1801487365, 1064360460, 1159474150, 565227876} ,{414126159, 1352825814, 419485056, 1493384831, 876534663, 1100507476, 1752294357, 557959259, 573874740} ,{817331101, 521188922, 1985185985, 1541348736, 2066038716, 342497219, 1757937776, 496573785, 281625156} ,{1465038212, 1375477334, 1426233888, 1273061535, 1213063515, 3720501, 1532305895, 199883560, 1143104269} ,{640629292, 1746851030, 324715836, 727850830, 1384158595, 663637319, 2132552239, 2021567011, 1857726147} ,{1006240350, 2092955691, 175348654, 246783985, 2120763656, 667029745, 478759155, 1689831016, 1099826071} ,{735547620, 1052371432, 293551558, 1984674999, 1540412286, 794929765, 552197052, 932412572, 355074427} ,{1569238996, 1080514366, 1157852736, 1528964891, 1107658914, 101783988, 1981221799, 728163079, 745427654} ,{1569905993, 508688512, 524479433, 743317116, 538878721, 1355085785, 587070320, 537842504, 780130574} ,{459189544, 941577011, 1944266543, 1050304180, 1182200761, 1210357400, 1520641453, 1913944042, 727068711} ,{358573535, 1742250773, 1328282583, 1456948849, 317621401, 1079879124, 1379821619, 866637100, 14298416} ,{1692281929, 443545632, 1542969037, 1712807743, 81617219, 817614598, 1042055563, 1670210843, 1966649618} ,{1159563979, 1346689841, 207402004, 1354375916, 1799747049, 833468266, 559933205, 1695270206, 1850647571} ,{77583628, 1410634957, 1550810274, 1576293861, 1692175586, 94990499, 1410189417, 760605018, 1211215465} ,{1081712333, 691647348, 1836557614, 211872894, 183880968, 1708912402, 1453186638, 1635992853, 1007147840} ,{42985943, 1473289322, 398595697, 852100772, 1682339335, 1780366789, 741834962, 807206900, 1399816920} ,{545752163, 805272411, 502464095, 638009632, 2102420325, 936381701, 282104682, 754021358, 1623764485} ,{90000082, 2081493328, 979976239, 628640820, 118181986, 117258017, 616813386, 1057001229, 714599197} ,{53654646, 915260015, 863679821, 1688144697, 1179918901, 1540725983, 646548274, 817607707, 1645375480} ,{601945656, 1895990800, 2005381556, 599549580, 1907843711, 1255813091, 599479333, 1785596184, 1712381776} ,{84177897, 1532216229, 524197958, 1726488769, 1600831470, 1180631418, 2043712244, 1440686213, 344972019} ,{725116824, 1988964952, 553537649, 945408061, 1918507708, 17187146, 1191767242, 743037891, 692076805} ,{2099513153, 898978318, 1728976928, 2002894257, 2039558964, 1560013085, 1719843260, 1400949116, 1119730044} ,{814698344, 1094584584, 410622880, 1990273374, 1139331086, 1238936893, 1372859338, 1295024241, 793446784} ,{1325173476, 1254408483, 1076854655, 563665107, 53368546, 361101037, 628346745, 880614658, 1677888698} ,{1893726577, 1740341913, 547765510, 2124011856, 501205878, 755415064, 712866967, 478644047, 1085299738} ,{396063144, 801833287, 536987950, 1671298939, 55624080, 1092707916, 1872527097, 693791908, 671790371} ,{232882802, 421555040, 1902771075, 2074665671, 1850593817, 554794424, 2138089756, 896837527, 140836312} ,{2069131491, 1835264649, 1876893688, 152435881, 2009740700, 144700986, 323009790, 1113124278, 1405398838} ,{2052675473, 312136643, 1425632334, 330183317, 81986941, 989564598, 864536360, 27595591, 1253444490} ,{1321432901, 589325433, 2071807871, 847307776, 1417233122, 234198100, 1989838536, 1868851206, 1407380294} ,{1275284619, 1104066245, 1062072368, 253477458, 114707620, 550342446, 1513697606, 367928390, 1059674507} ,{1025267939, 835876814, 1906456079, 1512682204, 985675854, 369658853, 1025456204, 1583269262, 646279977} ,{210087529, 757150429, 1035237871, 964267497, 1613355547, 1649987435, 2068355004, 669839975, 842040644} ,{525567614, 2024050845, 623372761, 2133979232, 998849717, 1195857086, 849067875, 1299934564, 973369349} ,{1996671228, 35347112, 1425075323, 179965782, 2075848024, 195505641, 1464022531, 848673902, 1993560523} ,{1029272821, 742788717, 1647480326, 2128398998, 1351030436, 489917412, 617015773, 1160824201, 1052116148} ,{226233016, 1666440786, 1118066567, 2060000179, 1193699960, 1157762501, 820223874, 1128676729, 1765030746} ,{164012664, 136322314, 2044043022, 615148455, 1466870401, 508414611, 899730267, 1051862138, 883970288} ,{1427074205, 1298389145, 1630221688, 890255115, 435322111, 1335784085, 1699568170, 1369148079, 1996229748} ,{1885655985, 285217693, 933012032, 1961377935, 530210647, 162109010, 1950015702, 1493620804, 436940095} ,{999543306, 729294811, 700230177, 1976303071, 343604427, 1023699748, 1200928724, 1985474747, 400307542} } /* End of byte 6 */ ,/* Byte 7 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{123134746, 357661612, 104256492, 1229661191, 1116680535, 958809545, 305544063, 1444843316, 181488645} ,{396136447, 1687430198, 748674123, 1484021485, 717422, 152918071, 1876268804, 1256824133, 956693346} ,{154523851, 419495223, 827453217, 549929154, 1566708865, 1546527881, 393542641, 519563412, 525868212} ,{1169955142, 367211989, 338284947, 449836392, 1277278137, 172476054, 1781156881, 2081289313, 1944691248} ,{1002152021, 1015271219, 2125823378, 1401121081, 1928802436, 771892194, 1211416016, 547628746, 233893822} ,{1510960642, 519025684, 955990378, 1599515246, 17870639, 204937824, 1313758869, 2106067283, 1750353284} ,{43477722, 648488344, 305585371, 2079516671, 714179246, 848019506, 653023638, 1636210033, 1537370105} ,{1955197330, 809764655, 31651894, 1874633754, 1514230371, 1199187413, 1007800670, 448161696, 927877048} ,{267445455, 1035225757, 1608763456, 943679023, 23373955, 404748936, 1037604599, 473143231, 1038889830} ,{178320416, 1487081255, 1530185881, 1474067942, 722750938, 995172380, 1181875548, 1919404959, 2091068667} ,{1079149199, 107629429, 1047615057, 394548648, 1815054404, 1770036674, 1931340774, 1221817930, 313676305} ,{1787481863, 1327168105, 519699733, 586585656, 789698988, 1023672000, 161414533, 1925954940, 115614759} ,{558447235, 1567665171, 626980659, 1928454829, 22596000, 835337727, 1086231731, 581906082, 33677236} ,{1987519732, 1333468186, 772294508, 1235628653, 1028062522, 1926326468, 957073703, 1222775404, 1029587860} ,{1485289135, 676513693, 650039679, 2012721656, 1501415537, 1767275231, 1212609354, 944063288, 2029986845} ,{1757528319, 2123349701, 1516538111, 1399043737, 2079081112, 1400840607, 1777863175, 239920714, 1350863768} ,{819998593, 1638441127, 212773985, 1129804444, 210736184, 599822343, 324958712, 157075436, 1873746699} ,{103362981, 757523307, 49138205, 1894481598, 1293240942, 828720446, 403331143, 2075438245, 1103708468} ,{216707348, 1862116660, 2049932366, 657733570, 338824767, 497778900, 1875709751, 1182351163, 247124407} ,{1317946100, 2098850140, 357562854, 802614329, 50741583, 1637569005, 321284614, 45919620, 34669716} ,{593530710, 952801018, 1719059649, 727401232, 2118065055, 1818807093, 330857890, 1685342794, 883949983} ,{1194618149, 1297022630, 1616552053, 1289189142, 205441071, 1845273919, 863331999, 879648760, 2045047652} ,{217374305, 1709626221, 1950758475, 454630320, 1184721817, 22932479, 1835357925, 349125958, 847779367} ,{1029482705, 1843267316, 927900013, 244399144, 1924214964, 2019819281, 328337447, 881100314, 971584760} ,{1933172375, 534634968, 221159979, 62461391, 291658017, 1468637372, 995627087, 465611187, 880313378} ,{458867925, 1768609119, 1927693457, 1640085685, 1260336071, 2144876257, 854864220, 1340883208, 1650020407} ,{1257464469, 1334507532, 910242415, 435285966, 1938172564, 1860762609, 1225343143, 328538413, 1071564843} ,{944344329, 445820188, 876346820, 2139741678, 745611109, 1081667314, 2001001813, 687771767, 1256510267} ,{909742161, 991489917, 946917446, 624982845, 1816586512, 780454343, 995880973, 1672288875, 694414494} ,{1638654592, 38392454, 290288079, 2138029754, 972984468, 753353535, 1637227077, 1242877730, 1611719086} ,{649505098, 1503823967, 1769665279, 840350357, 617397356, 1061748818, 1183304828, 1285187126, 811826956} ,{365975818, 1100112637, 178870811, 1006959507, 1107187687, 757272736, 1631812271, 1616941218, 326509896} ,{835461178, 1529712136, 1071701251, 420241968, 696719054, 1022552359, 606433743, 530112705, 1048050519} ,{859342526, 2083997480, 1509895055, 255081811, 1925761030, 279989043, 1734021437, 1473535408, 121533449} ,{1684291005, 1076150484, 1319813325, 282677286, 486233308, 1023519544, 1391078214, 1299038464, 461066575} ,{479148728, 20191624, 1738039251, 1605678091, 998092391, 711483028, 200530003, 1857615137, 25804546} ,{1955745423, 1342681585, 200839647, 183529143, 1704460602, 980293234, 1360975568, 566181153, 754677922} ,{1929398051, 1500307579, 568397962, 722035651, 818242665, 2006134267, 617870245, 1042651977, 131191926} ,{1132056049, 1033461125, 1598046432, 734894765, 113016256, 1603609149, 2003150871, 1214907966, 332043562} ,{1122716082, 844309105, 471629067, 1711793203, 1606520227, 666106841, 345865855, 1053869242, 1452414357} ,{1380319497, 1303238881, 376628073, 1037858515, 1537936653, 498004510, 1276068318, 376486239, 1298792287} ,{645053139, 419604338, 1205927636, 1351452207, 641964721, 86461557, 853543544, 1482208696, 1841178595} ,{1773102801, 1347411361, 448860392, 1350316826, 1694429841, 834199563, 479013092, 323668785, 2118016678} ,{493611063, 1242524537, 811937553, 321221767, 1196674846, 1391883212, 1613268617, 1581938851, 1824112254} ,{1776077069, 161162207, 575359695, 173959434, 1190549670, 505902874, 1901532242, 1870655161, 643194546} ,{752523824, 1852226620, 901805717, 1656680817, 438854656, 2018340011, 1376502719, 1067530683, 387341464} ,{610109021, 1340189714, 1535247701, 1177371906, 802013885, 1234025739, 995964510, 1902526151, 1317245738} ,{231337308, 1768528506, 1971206979, 927585015, 2076395669, 399070260, 1831532388, 1145542138, 1460707556} ,{1934301123, 521769116, 2102348847, 1878197934, 218459429, 166886270, 970196829, 357513038, 2026065148} ,{691443805, 35091355, 581482149, 98352104, 275996119, 329956935, 2076609957, 1196899719, 1897190682} ,{1950441314, 1624110634, 1978505919, 637428475, 932290083, 297211080, 1874482041, 88733943, 1223355750} ,{604246333, 1800024561, 1314380476, 655253889, 16171487, 836919068, 1880452261, 104469780, 2127090711} ,{693453318, 1665601489, 1082256815, 1630104304, 846528218, 1208144463, 981528800, 1286569224, 825725719} ,{1122078057, 533842925, 717613666, 157780433, 1270514852, 1966476705, 566217731, 1140720233, 119643494} ,{940969191, 859636130, 2054240760, 1183057747, 1250592019, 1711412919, 1347872056, 281805798, 937211625} ,{1304989420, 260931082, 2089357219, 1473551039, 121439495, 1951668432, 547279809, 100887100, 86129010} ,{817032242, 968882242, 1417430239, 158449770, 573157175, 897669322, 1317994341, 1952009580, 2037437469} ,{495787503, 1531979357, 526578849, 986091431, 1576038241, 1680576278, 1871112434, 1834440632, 74990561} ,{2030173550, 1823828103, 718730298, 907530048, 1481671682, 39525537, 382773813, 21939937, 1151870157} ,{804145889, 1986813482, 365393209, 2107777028, 670136273, 524246415, 1792519699, 1373020951, 1370694946} ,{114116205, 390256109, 327726188, 1632011914, 998945310, 887186349, 983578607, 2039378678, 70414408} ,{1929517813, 1193096158, 754543938, 1956577998, 1822326094, 1913629294, 1588012238, 1186025350, 333475398} ,{1403902969, 1046173105, 923226369, 783700865, 814678490, 1521174707, 1142434498, 169100426, 1399738435} ,{2002562923, 631740123, 1657669595, 1651095749, 23188448, 252965647, 173267248, 301962024, 346998469} ,{1779071840, 1712321011, 35389996, 871382132, 323274507, 1089645867, 405633219, 622036323, 427684341} ,{759770986, 628853263, 1649119937, 27237910, 1868282764, 1007178512, 437613999, 1914182475, 661447986} ,{1493664113, 2129087710, 1589601093, 1351757055, 1645288278, 854685994, 646215901, 1232955672, 354760334} ,{1656655012, 343558513, 920556065, 666868010, 632059893, 1052219447, 1303898535, 1821116258, 1927404485} ,{1317059597, 71798954, 1091813165, 1075147026, 804367135, 1436189195, 52276035, 519979502, 837122674} ,{409071251, 1726720190, 1998798300, 1202035050, 1408285204, 617177611, 816418495, 379417665, 1812359933} ,{1050602744, 1161995021, 1295671741, 347700798, 455831377, 1223780790, 1941090288, 527312656, 1989258865} ,{2051952214, 748096979, 469039155, 604853365, 567580534, 1533152257, 1757796965, 526495339, 275932983} ,{1880707453, 772702515, 2055513506, 1867908902, 886073699, 1510319277, 1204779212, 587242719, 573164565} ,{1553511729, 777033341, 1247316133, 1762832694, 1217669029, 1199671877, 505308429, 404858738, 861220106} ,{332196869, 1881029640, 1963531674, 785714231, 1889932985, 583644730, 685514195, 8913351, 920445671} ,{1537111571, 1198629333, 522311975, 674948217, 1028924418, 1841756289, 844064915, 1990138232, 1620866225} ,{1691351454, 1329512344, 192125752, 1713168834, 634471733, 93294241, 266846950, 546030578, 228612666} ,{178708143, 411134228, 1434547076, 1205479772, 1204360462, 670708925, 1736669864, 1027142049, 1811228386} ,{988047804, 57134554, 2143507112, 1674361385, 469439577, 140814552, 1235228560, 1242031389, 1001096232} ,{577071902, 154408480, 1971573581, 2105557368, 2043767443, 167181679, 1805685811, 507199693, 1114628274} ,{661869431, 366751650, 1941249141, 295777014, 1020007702, 1316213355, 943703555, 1576093505, 1005245887} ,{1024775032, 325769993, 112634957, 65488660, 107781310, 1255588920, 1820662482, 1790488803, 1950716423} ,{95552488, 11076767, 752912906, 1502592946, 712124365, 836626855, 2070706242, 992594126, 1008961515} ,{1733831738, 1626943, 1857558877, 84614427, 398175990, 1327521117, 1070803939, 1749942513, 1181560481} ,{1510109028, 1709884404, 1947975832, 281944970, 1077398845, 2040339703, 1555979483, 59474698, 841834336} ,{816353512, 1931931372, 435983369, 2098719274, 1035665361, 787214184, 589955134, 1247989883, 920991449} ,{1357326146, 750147160, 1795789312, 1636816166, 952014113, 919328103, 1796147023, 1330287255, 2097026309} ,{699457710, 384881319, 1474981216, 74678464, 971872898, 1213812944, 1479643415, 1167416004, 992774026} ,{630775737, 30839447, 939486355, 1379800231, 939021574, 1635429039, 259712009, 107785022, 1116045181} ,{1667420549, 376431534, 732886380, 1459005163, 974079481, 759201983, 789288233, 431135005, 1360053141} ,{2026629903, 769576791, 174822861, 727950213, 306341689, 382378872, 1329480444, 692128787, 815715890} ,{819828940, 1311923591, 1082926478, 432080517, 528007191, 1024983462, 938930631, 1284570802, 293155775} ,{323261864, 646436355, 1408563606, 181772233, 894950685, 962791432, 212593146, 751609726, 1276132375} ,{937221796, 1887701596, 339903937, 1717912592, 939441620, 1130177238, 1877077186, 1705180671, 1523744391} ,{1041966618, 740543037, 548069840, 1016252284, 200468253, 161106356, 62185886, 1638732318, 1559647224} ,{121360316, 475954507, 433816074, 1438038775, 796866572, 3981667, 1184041767, 1775243433, 1571395741} ,{634487867, 77226572, 2089955548, 57867695, 2042270417, 606775095, 1340713353, 984482392, 838708121} ,{2036759659, 771936113, 870520753, 1359252287, 537835482, 2099179697, 1961168959, 565468969, 1306288984} ,{692086715, 1476506908, 1349448544, 739224196, 522039413, 697405646, 1749601662, 341611979, 295101038} ,{1260596640, 164417415, 76138139, 1383714992, 807514810, 1870692238, 1354131138, 695020729, 1530625196} ,{563665301, 1210692238, 1641390259, 639054915, 1611449251, 1120783565, 1785986923, 336082039, 1386446355} ,{1081329544, 1903523976, 70561138, 59844272, 1429776581, 1954555365, 2070821319, 1375166275, 411597473} ,{1405482938, 224805511, 1731065530, 445715423, 605125223, 1665621765, 1684968824, 285473064, 934706380} ,{635845828, 1488439816, 1943954105, 863892961, 1950542096, 2124887235, 663372661, 739086712, 832868288} ,{1043733062, 2130346874, 1647693633, 1640839919, 613941008, 1979788667, 1706386876, 614107783, 721125831} ,{1135953436, 1598547089, 1641911001, 1193420635, 121448708, 771466657, 1049775124, 1255496071, 863141089} ,{1271817329, 2017624995, 2120898483, 1337599982, 1631733733, 391372924, 265931042, 1459272482, 1335736729} ,{303909679, 782026594, 1067049764, 1935697426, 114719847, 568284024, 1297647085, 2018391858, 1341017979} ,{631921674, 1344870702, 629039269, 1189947616, 1699609196, 479453022, 675979076, 523858210, 442516999} ,{671899298, 1041163702, 2029076586, 286469869, 1688989452, 591250583, 2144261429, 502346010, 439426525} ,{389947344, 1322600064, 1185426437, 1135741891, 1544485958, 1615615223, 942083656, 376515882, 1627453764} ,{1493241911, 1131065694, 1497541459, 925671571, 2098520627, 1789547031, 2052316004, 636857699, 1768246250} ,{502019142, 2006891164, 924514382, 1720664081, 1097861091, 2045932829, 1174805533, 557862868, 1761602546} ,{392410312, 1071061492, 596170142, 1670806910, 525511786, 74724424, 999513323, 1643099794, 1453665411} ,{200378196, 1814881671, 1225770751, 877908432, 663664934, 1315252573, 13813074, 228828762, 798323232} ,{697800751, 283121484, 1422963239, 1062614850, 859986881, 1220007227, 1747579986, 1095206949, 1443032090} ,{2134742808, 917467863, 1773156848, 338528244, 112009984, 1029301339, 1305527197, 1706954825, 51446707} ,{1564363505, 86233387, 1789560606, 921988010, 760483746, 75577072, 904115172, 1894037888, 956563944} ,{605191584, 371341586, 648008069, 1231375969, 1726555430, 589021261, 549224810, 2112889109, 1953411883} ,{23563888, 621743753, 764645338, 1007236932, 2117680029, 1472489851, 1430389896, 77103739, 1983319538} ,{2139775670, 489301961, 867602896, 52274494, 324723115, 814122300, 582660091, 1029459468, 823405760} ,{34279338, 1176889038, 1532384765, 493992006, 1298232698, 950142905, 1736705660, 2033628672, 1874952851} ,{650635776, 1191802153, 203687531, 1832619713, 1924312855, 534582902, 231331880, 1751653555, 774363199} ,{27312923, 132021286, 592782563, 335793474, 530411242, 444584310, 1510919878, 1739706799, 1961623327} ,{586931563, 1684006599, 926827683, 1082371839, 223001368, 1800703099, 189207416, 586652514, 1520911541} ,{1706074879, 1749266577, 1329270427, 2023005645, 1774060637, 1044766187, 1715221538, 1207929742, 1566033592} ,{1589280989, 1161574129, 370099529, 1228118057, 295810105, 1571693424, 249503560, 1140791811, 1077648977} ,{416683779, 2037897807, 327145726, 1674706772, 112599622, 1784684302, 2064738232, 93054932, 1368520584} ,{707110167, 1592584761, 492081654, 350209577, 1051147310, 2096715479, 1798340701, 1975648161, 467044636} ,{975680033, 1303900372, 1227215093, 1657470587, 291551410, 210793084, 1582934243, 1425566149, 758970899} ,{1044706751, 1692367477, 1215413767, 1012336701, 1259099765, 360352473, 337671125, 1431022719, 650867631} ,{1626642685, 118459609, 1400881852, 1921294095, 1260875235, 86005581, 1478154081, 1511314349, 1698282854} ,{522113737, 1610806790, 301386820, 764188161, 1063479382, 1105944435, 1524919003, 552589057, 547950665} ,{1528118171, 394722147, 699099607, 1832333535, 273460244, 1040996793, 1730615100, 1226618250, 917885680} ,{666529869, 533233538, 554114830, 1459377997, 1514433941, 1266315725, 647775238, 1017908299, 1050791854} ,{1617098627, 1067131130, 850098477, 1475935864, 906629179, 1252765887, 1163249693, 317179195, 258745549} ,{1287416163, 1610557058, 467539778, 274827082, 808761689, 1482083948, 807803855, 1602708468, 1106807184} ,{2106658785, 468992784, 898152617, 643563985, 561066476, 268533121, 911540207, 1076144189, 1150783651} ,{1981415293, 107023716, 1255663417, 1715047036, 2017553133, 809392734, 328130866, 96962680, 1983616195} ,{825165805, 416799555, 1679242008, 1788251545, 1972124848, 664862435, 909669244, 1899364039, 1199973252} ,{570235118, 1940872107, 1490323632, 638337803, 1905403432, 1526541451, 2137150130, 964077081, 1675350636} ,{1826477593, 509382515, 1983219945, 985940997, 1018265977, 1265979728, 251636852, 38874640, 1004659853} ,{952832179, 1185683709, 1416599613, 1088319909, 1282664564, 744818264, 1585409950, 1607168250, 1793897500} ,{508100929, 213299825, 777921460, 1038831226, 518244818, 1001832141, 534654393, 100082912, 1771705987} ,{1804776856, 573627549, 1278475606, 966620189, 305811934, 1659942567, 1449114984, 888926674, 1497926151} ,{986033980, 21959762, 1381272619, 1892648977, 334562413, 1824331516, 313259859, 1675633844, 177587297} ,{2138161540, 857395021, 2014270199, 1822395140, 304176638, 109038482, 43371448, 518724945, 659493819} ,{347166499, 470293046, 404946407, 1213677482, 422482546, 241410589, 1286820342, 978038727, 831788268} ,{1454416945, 634675858, 180057489, 1754449243, 1786315817, 662500839, 1988291660, 1058547162, 1630572675} ,{1023084990, 1743817964, 1482367706, 891485539, 901081232, 311996394, 728923874, 2139600736, 1870207892} ,{80830690, 1887569385, 1345174175, 440203870, 1485866436, 785536820, 1070236288, 924614628, 2021244775} ,{2002566453, 1272864159, 2042774670, 79859483, 1354525580, 1967830271, 387666434, 1447414784, 1277427135} ,{126793264, 1909304344, 1111167498, 448588251, 1996250095, 146939784, 1489235303, 249478442, 2123681005} ,{1779187615, 1265743709, 1966094990, 1245005098, 2026429008, 106157132, 1069946535, 333652899, 1802276311} ,{482229776, 2046191179, 1459987118, 553726434, 625477061, 1447914028, 1812210667, 671071977, 68479322} ,{1400152298, 1915950803, 89676141, 1092538758, 1234429687, 1211149134, 1169012497, 361261837, 785865497} ,{1705822023, 1041238721, 763467420, 1279788270, 1200460927, 766323560, 495254174, 462204215, 501818540} ,{657548185, 337639282, 465603425, 1521379574, 591019205, 644368329, 1206442151, 132757180, 544554192} ,{631722502, 152799278, 240926631, 1488912181, 900495960, 146444767, 941290239, 1052086415, 2142878450} ,{1081524237, 868454550, 1694173090, 123993174, 1282005108, 167798520, 1635443608, 410581370, 64559805} ,{1958540286, 1778152416, 1639615404, 2136836413, 1947979151, 1736908410, 2105439284, 1829429393, 556059587} ,{419228093, 530290295, 426996526, 604783678, 991482555, 1779424833, 1200062205, 721632818, 524162704} ,{547972303, 33302796, 1020792009, 656756445, 1108483354, 163635037, 641397199, 324893243, 1316893266} ,{1360034525, 209449447, 121216774, 2010374057, 1293637621, 270308275, 1637221613, 685727216, 624136366} ,{421007573, 263630173, 247666046, 577379037, 1742084999, 1512141893, 1114280754, 1690619326, 1794613329} ,{1971153196, 1287829474, 607394838, 1404328298, 1241189860, 168429126, 1192689738, 772138525, 1507812288} ,{159323736, 561809003, 52385519, 549888458, 2135337747, 76548966, 669838475, 1926057727, 65353172} ,{375580376, 997372137, 1654625350, 194395592, 381798504, 436422276, 1072380824, 248559033, 1690667213} ,{408657643, 993084734, 1173508354, 682277175, 1029573958, 1172177106, 1407491461, 297029346, 1569858781} ,{1435421825, 1806233662, 1361475589, 1431319397, 116856681, 1840706935, 180357250, 294452536, 1731785211} ,{1460272808, 192375858, 799804066, 503774577, 217039805, 540273834, 2113946777, 1589254305, 197671710} ,{1299561582, 1311919316, 1068882818, 930079977, 241652930, 1071821127, 1445364700, 424202332, 1381672302} ,{167975528, 1586375271, 736763443, 1649013098, 1369337536, 239093648, 1512074125, 1656658066, 1433752307} ,{1547403888, 1316377072, 595246299, 1604747524, 1839991505, 1633779170, 1097165413, 469120353, 924180105} ,{954010695, 499771938, 1393140576, 622280482, 327283122, 1776480930, 1845114074, 1479729109, 775745575} ,{710705746, 863410918, 1107113286, 559271581, 925452623, 1258909001, 773869318, 73668955, 362797577} ,{1919506287, 542113216, 1183098460, 1949229541, 516764831, 621905292, 106438149, 730860183, 583086314} ,{794666577, 1314023302, 755512100, 306455518, 1305496179, 713767453, 1901557862, 2003142278, 389648017} ,{1370649598, 490196192, 1461078878, 527145439, 1672195730, 1943204115, 244086526, 1085960249, 110993637} ,{1251669300, 2049999685, 1808711693, 1484101042, 156002329, 1611915430, 170799469, 243205222, 285628866} ,{546103090, 22343531, 1700606925, 2036508542, 1938652515, 904949273, 632255403, 2137341698, 975834556} ,{432642854, 74930292, 1437508945, 566739640, 1557011946, 1426743846, 1582393693, 1408766218, 849195405} ,{1117003444, 1126718264, 39920988, 301777603, 956309094, 1165263788, 491072121, 237276543, 1195384851} ,{829848438, 424230249, 1249312539, 477771150, 140624154, 2066518578, 311282672, 1657312403, 2030171007} ,{1252391119, 1467440502, 1689705601, 514002627, 296287326, 921729428, 78610113, 1882353458, 1570198898} ,{600241506, 1551919600, 2146407760, 1357350347, 300553923, 1986080167, 55415331, 371587340, 170498354} ,{491947227, 1537432000, 1953627465, 727747509, 1329433839, 2116869747, 1964342330, 1113969517, 617421961} ,{426625672, 153135678, 1803281235, 766329205, 1309771571, 1292306881, 1458348009, 1222275043, 1204097853} ,{401823302, 1744444954, 517990039, 1485277314, 1252561419, 1818555163, 203618279, 1306636734, 150993468} ,{1228959857, 1743209716, 1948773068, 1792254917, 1944199093, 990951219, 1494565959, 1161782649, 356459160} ,{1515519086, 382614115, 583389502, 1649026398, 1353709817, 496427529, 1376007508, 2026417229, 1610831428} ,{1105873820, 960161994, 1955873397, 39549277, 533690949, 573106157, 1433163695, 1148554719, 24322615} ,{975552536, 1926664193, 1370127013, 1867356970, 919716210, 1155724076, 536126857, 1357590023, 766682249} ,{870720384, 10257571, 1363847051, 1582406984, 1754364131, 135002166, 1960841387, 1647731775, 640787098} ,{1868029546, 848473623, 1309532558, 198063721, 305315076, 838908376, 586417897, 1818178557, 1494313681} ,{1240823908, 1215153967, 557370092, 1042641414, 1008677279, 1639859058, 1916154704, 801483997, 406035333} ,{1853487671, 1812708103, 987298390, 1322920810, 96104876, 197619777, 596008921, 1221691870, 742254545} ,{703001678, 337460094, 1387139305, 197727397, 1682640344, 2024144789, 1645056270, 695699526, 2083390604} ,{1649204552, 1934835339, 998658715, 1574959228, 2007701836, 498620287, 1575760891, 57075275, 1450854578} ,{171044680, 921346714, 1379276801, 1159898289, 1651790843, 861481076, 648261695, 396135784, 1942640048} ,{509896901, 220474739, 128852592, 1563042098, 524158439, 149385006, 720812934, 666512477, 2116240172} ,{1300746182, 243491147, 1764039695, 1021673822, 276679002, 2978348, 474507085, 1392467254, 1292554016} ,{718563359, 1800794504, 218206276, 7028600, 1653282223, 1441923146, 357778269, 848173847, 2108040653} ,{198650791, 982191848, 520051063, 947094626, 1962369317, 2093780077, 1612589136, 2100675346, 1871378040} ,{436060618, 434390753, 796442150, 1570972297, 1972390201, 67021172, 2061474928, 1708449531, 408065224} ,{936984831, 311955462, 1775964356, 1213524479, 1143398689, 1165126777, 1046047437, 525285329, 1925916465} ,{1843726429, 1277456401, 214992177, 2087319888, 702553828, 1598519792, 944000438, 1542171370, 1236535672} ,{1521162238, 31208061, 1281370113, 1357450632, 676036525, 1431536560, 780332317, 762211774, 1912500957} ,{1888683805, 375453125, 952886477, 2077923630, 1242188126, 251284705, 1401807742, 1802039285, 1148345288} ,{779676629, 1332920956, 489429353, 761613459, 1218205787, 2026401777, 1605549831, 1380787024, 1164427058} ,{1231186529, 1565023901, 1875535355, 1512820274, 1607619257, 2100476257, 818246118, 1011685768, 2112522697} ,{811628379, 1705610307, 16874460, 1798372723, 2147340465, 1314166192, 845406481, 1609854637, 1261768795} ,{1810342776, 1552035946, 687657744, 1990194976, 700723248, 488919245, 1053406920, 1621502554, 1636435907} ,{1504258710, 134977415, 833102749, 778784557, 1219879966, 682228690, 1668064531, 1376077977, 416130127} ,{839100178, 1749243200, 1701101882, 271969123, 210687910, 1240809773, 515199185, 628520320, 2138189445} ,{1470366633, 2141011593, 1266203254, 666286714, 1842064823, 874763995, 423164944, 947675713, 758759047} ,{1678015270, 116859260, 1247010065, 215287851, 1591300239, 603295739, 928482374, 1393557573, 351935812} ,{411315658, 1064405443, 949149267, 1917473011, 36221335, 705696598, 487407093, 995261252, 976370085} ,{2081921739, 559198170, 1300558149, 1961823939, 1609758527, 1870635026, 1290080984, 1939792324, 1360327943} ,{915831794, 1142556041, 1144918594, 1044745561, 813535973, 177769819, 975324975, 166510908, 1614250614} ,{1077798838, 1494331585, 1385766398, 713737839, 1154290893, 902353627, 1821561905, 613062084, 1369215893} ,{789990038, 1601012174, 1776491438, 264167406, 787146142, 1109767296, 958576155, 1539278487, 1000017948} ,{498987354, 230568280, 1758899452, 588999776, 1550898805, 1559161259, 1105818829, 1168330827, 1893602292} ,{1668335203, 109656423, 1994170127, 1970675775, 575726905, 1995345296, 866302544, 2096966614, 1345027143} ,{2008871224, 170627974, 548657407, 1678084000, 954578382, 2044503422, 47829574, 1017098555, 61722976} ,{1559053548, 1158156737, 185813084, 1269770054, 238481041, 1471481454, 1174033128, 1717851918, 1569968152} ,{90603665, 2146146157, 1960137669, 2110273685, 418479584, 503822139, 1676425738, 1308776312, 1248898063} ,{1126644690, 1854586881, 431787306, 1594060896, 1048001961, 386519416, 2014492428, 1747982005, 973079171} ,{1073202696, 376374321, 1792228590, 1552289949, 197978067, 1718740295, 653798575, 786589536, 1640658647} ,{550632988, 1626443699, 1456830509, 1050914362, 484698716, 145231965, 1942870233, 1766774151, 1047443769} ,{867845328, 382011243, 1081917251, 1395520813, 1495998871, 937258837, 431754111, 278039233, 647065863} ,{42576710, 797838980, 1429053173, 1968287534, 495024721, 1856678955, 242344627, 487602544, 753860560} ,{1886852976, 1609614086, 1078478039, 699036233, 477365725, 519718815, 359705542, 787887658, 2134340327} ,{645308600, 619586273, 1140884945, 308241351, 1090448024, 393728263, 1325202600, 1991904088, 1252275052} ,{1705545768, 1340633113, 146667783, 233111376, 1835938049, 1602685553, 981592210, 963295926, 2143029569} ,{2049570472, 345754663, 1721819552, 1917163652, 1348046060, 916895186, 2045151331, 234993045, 1802669406} ,{387841506, 1893940453, 834168143, 1765397009, 948747989, 254957373, 2140797433, 264498631, 2037289474} ,{633709227, 911024675, 1332942235, 536113350, 1947923889, 1440845722, 1043394597, 1996286341, 1356597852} ,{1360410282, 1984049154, 84043418, 2056371927, 1337268247, 1722653920, 1628598193, 734743283, 1402230645} ,{1861581132, 1138995265, 113032201, 1163089445, 433497632, 1775932057, 1316817081, 809577649, 1116513096} ,{192647327, 999506358, 14160254, 1822981100, 896958741, 1683699070, 498607403, 1970591056, 1925238789} ,{552338480, 762004938, 714912894, 447600555, 212530724, 568686470, 1589614453, 1287099867, 513665047} ,{103408329, 1686018862, 1832062621, 1739962585, 111901374, 468709941, 1775388324, 1548073401, 1824933513} ,{956301795, 1275507926, 5541941, 58465823, 1877820004, 403931386, 508411050, 954263779, 1436453499} ,{829228985, 523673283, 1189093, 927624862, 888061490, 770314050, 805320999, 538388330, 1773367398} ,{188032623, 49926186, 2131902940, 2072065411, 675689834, 306237315, 1487848968, 262828084, 1013196734} ,{1603184062, 1995888420, 1626879601, 1495278515, 117147775, 1649522363, 1371669321, 1122568435, 1334861558} ,{2012239862, 1972575343, 300838702, 960303325, 1605688593, 1483039756, 1499761705, 972257640, 1265398419} ,{1044625126, 1792101421, 631212518, 816950621, 290303225, 2070727721, 13831998, 597361057, 1906587955} ,{2119676837, 1023493679, 1760373509, 908642252, 477091563, 2106967886, 1667179843, 647251854, 1753791967} ,{30867481, 1326301794, 2114042830, 962582583, 505982928, 1422872358, 1401289140, 472131384, 770233673} ,{1599030147, 541256280, 1003418393, 574008529, 1588276517, 733215005, 1283664053, 1520590481, 1163444031} ,{346690764, 533096000, 1618628798, 439565889, 1133375816, 841250962, 1538939326, 545182219, 835187857} ,{418800739, 178967331, 592929537, 517342289, 1050804147, 2072496537, 1692916849, 891189033, 2134752356} ,{734435472, 672358097, 1890019402, 1995473104, 493825606, 1703795870, 219949024, 200504028, 1282642655} } /* End of byte 7 */ ,/* Byte 8 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{76622690, 1262953926, 691591548, 1420879705, 677234486, 162394161, 742127774, 516889890, 1570064848} ,{1257138893, 1010943131, 1220316968, 1008098602, 122134259, 1966808889, 1498301498, 268944713, 1423867980} ,{1947600612, 1041288870, 1738585834, 608105213, 1406191251, 1648118844, 1591240968, 910164333, 336014458} ,{813149256, 1165547001, 1660636699, 478433473, 1231168130, 451448032, 643636429, 1219528364, 1233829932} ,{427442644, 1358489726, 31693233, 1491214197, 1661919698, 1638381530, 532001521, 1412387847, 1919203228} ,{1163746257, 1132790788, 2053379590, 175913168, 1403171617, 403357051, 945836981, 2099728054, 24021704} ,{574253195, 391526437, 382595941, 989715773, 1066814412, 1801150554, 1255431800, 550314643, 1196326834} ,{1197325563, 1936861601, 359364252, 1558261395, 56435017, 1839920201, 1970250011, 2049789897, 1164369653} ,{2056689770, 163475901, 2132214923, 1183217709, 1185731435, 517376305, 340520481, 1171409723, 1097860579} ,{1493122068, 1961058629, 262062212, 1351600809, 275021794, 149975717, 2034931811, 1820490681, 1892707885} ,{2080765645, 1908139595, 1822918331, 599078489, 2068537662, 1716742531, 255690804, 939083437, 1417735912} ,{621206210, 2028496068, 729225541, 1705293592, 129144965, 737332071, 1652275322, 1341745594, 1162772560} ,{979563241, 1207442495, 1752960832, 1754692403, 949854765, 971969543, 1734894039, 1040105307, 1337437184} ,{89730113, 28766505, 1544434888, 966869319, 1076742123, 1393225336, 627309291, 1928427073, 1797657018} ,{716446997, 1910834844, 1876012270, 1501750750, 879660094, 1337955848, 119237535, 923146701, 663932931} ,{678464481, 2031647132, 401431770, 1900286467, 1174960925, 1901558746, 380131303, 2089363058, 228935490} ,{645674026, 180105544, 1098940951, 415401971, 121048253, 598364729, 1855880937, 1591204743, 2033732787} ,{475187477, 1945199003, 435833310, 873673585, 1591154940, 1456760839, 1966076481, 292920999, 1381258408} ,{1189883719, 1011070741, 2121478862, 696398371, 1926739222, 487237257, 1628823275, 1736922960, 1318816186} ,{1703463820, 837794420, 747918282, 46128496, 44731188, 241582083, 985487100, 1848691658, 1330424390} ,{1808483547, 368341134, 1748503884, 1497101198, 910036106, 486991169, 396940969, 177055853, 96447281} ,{14564532, 583490611, 128180657, 384270063, 1897990052, 1866883213, 870635903, 401670044, 39418619} ,{1500472772, 1471888659, 1046258346, 635156539, 832705137, 87729730, 2089294764, 333400101, 823389284} ,{1965748022, 2100525572, 279807828, 1936935320, 314329830, 1627262129, 676633724, 2129610246, 213211338} ,{2091338394, 2006973819, 1457183804, 945375171, 1041497112, 630760287, 1988036980, 249562228, 1813143791} ,{715722623, 1623512518, 942303185, 399591742, 1831674063, 1695132147, 1100626210, 1088114392, 591840567} ,{1162880479, 1269408864, 637918569, 2097406683, 1609898517, 755026514, 1876897842, 1655240803, 1352686783} ,{1413049094, 1580755517, 572641518, 377441342, 53165472, 441943240, 417961384, 660651587, 573556693} ,{986060811, 1811426223, 1448247938, 546195151, 1363421232, 499937298, 1058521519, 68338437, 1017380232} ,{1570670963, 647253849, 1530207798, 941943291, 1991313601, 419641559, 1275959070, 1569099440, 1442128797} ,{1986756839, 2114635029, 494098906, 2137919979, 1792079974, 1311895953, 1430799693, 885520810, 408405867} ,{2038070407, 1028939678, 375880731, 1942535494, 1552485169, 1067234312, 1290838910, 417000052, 547541692} ,{1113380133, 16079759, 127071570, 860443876, 469982324, 1022261083, 91402738, 613704553, 1651050101} ,{1762361769, 895098348, 598978188, 217240839, 305754146, 1244639370, 1237096232, 1072747346, 574537669} ,{1891469025, 977657187, 562824439, 121341251, 224995080, 852513940, 264444560, 28785655, 1825532836} ,{654084285, 691063054, 1674600029, 1939279051, 1117940662, 999004466, 1969193560, 1250061305, 1217290117} ,{1387131850, 1703047808, 1505530887, 849829612, 1701330401, 143809837, 1133074897, 131130851, 689066962} ,{909561441, 1249312750, 1347883518, 1059848733, 629862792, 1038340428, 1843457827, 1166257770, 1295414396} ,{1157970020, 1432871617, 954756221, 103525986, 802188179, 1027582110, 1668844306, 163675573, 2140766099} ,{1685591041, 415121478, 1608126475, 1255221690, 1744804928, 2080022443, 1202241167, 1586056558, 974936768} ,{1103280009, 491074650, 13717077, 2087822327, 1362230110, 1071294288, 497757535, 1376736278, 665624048} ,{76009830, 321484326, 1224712704, 1390050165, 1126127369, 1368622709, 1712062397, 468705779, 854706164} ,{1693002609, 540731630, 1038608133, 1514516719, 682011332, 914548358, 503767843, 1660320521, 208273332} ,{1422310183, 1306692390, 394131953, 1725590381, 1278907386, 1131006373, 1394144093, 486020672, 249806692} ,{854667557, 1918759853, 563281889, 1590420603, 1327422264, 1297503661, 216529790, 440893353, 421490314} ,{2131004642, 755546158, 906029187, 684318008, 556797013, 224442898, 612725808, 1328857434, 1594935464} ,{1287557758, 497776306, 1665647943, 1348088376, 1543915337, 1979866373, 221075436, 841215244, 1678986731} ,{2008254558, 551297217, 250613028, 506148742, 2065939892, 1781143404, 56709566, 1130545488, 381758783} ,{877399111, 1364796933, 938733822, 1578427605, 478444459, 91339603, 1228887508, 2007310436, 1493872724} ,{1467339773, 1637848991, 1059214289, 1018989716, 1485901581, 372660794, 284537917, 597278898, 1062105602} ,{1027766325, 1389499643, 1025134613, 1061576237, 2025315149, 1566652341, 584180476, 427616341, 1983023612} ,{334831320, 998745135, 2106171212, 1657230965, 1557186988, 881554173, 1046007975, 754257262, 1507945311} ,{1353036720, 801630760, 590527646, 1362650625, 72198842, 328067908, 1334697337, 573384010, 945256262} ,{1509329766, 791448999, 625284690, 1465382211, 1108839132, 907428675, 1918297389, 1760345766, 1136963208} ,{797885489, 351082445, 1524897010, 183563858, 1815482816, 1038190165, 1698401364, 18620110, 735223954} ,{1779042405, 2123711584, 176502105, 985246364, 342530778, 2037917654, 966469479, 539607864, 583164755} ,{1542413066, 899242265, 142853818, 502692345, 1240208000, 1003642786, 435823937, 28031420, 1230397003} ,{2053245527, 1814360540, 90714760, 356166217, 1149464752, 1847343882, 1806376413, 1119798843, 1079657704} ,{1167764503, 1169346327, 384920818, 16550880, 262965154, 1082751298, 1938693146, 2129737020, 1665040844} ,{267014935, 1168545153, 2011752009, 1489353788, 1880555270, 538219014, 1179130260, 1076658197, 1181602354} ,{653425090, 567076855, 1565470095, 766282773, 407019736, 1412119897, 749105584, 933592548, 939255803} ,{1567102436, 1951062138, 672879107, 518637563, 47276468, 973079556, 2050288180, 1948520347, 116913623} ,{1871955034, 408411623, 1093466043, 1372178599, 1428574208, 1256465914, 9457879, 1261494159, 879677070} ,{364777060, 738090725, 138117353, 1050215045, 1219762192, 610418754, 1427709342, 765265140, 951919393} ,{787976571, 188759461, 1991471290, 540823390, 1440696140, 1094083667, 212677636, 36889488, 139294631} ,{142862968, 2117002626, 1367181895, 963505358, 1590870375, 778510399, 987329073, 2013804553, 134419505} ,{1799816503, 860356369, 2144690669, 389401004, 1548767606, 552543140, 1951300570, 780187628, 2134695395} ,{477361174, 364590810, 831133347, 839226119, 1087314929, 1700752952, 1524360002, 520448483, 1227280507} ,{437179178, 1031102247, 1678475747, 35554807, 1884060921, 663341170, 372564514, 1408395780, 1391122398} ,{1746486361, 502612195, 629728558, 852077070, 1943695453, 2146199798, 1811293911, 1399240207, 58097010} ,{2035505335, 671150566, 1142291810, 217584534, 787988775, 1569336434, 2096262611, 665008626, 1199480850} ,{650325563, 936508716, 843235054, 1434503120, 1132320060, 1031017316, 52882264, 1845491057, 164275643} ,{197795555, 1851576518, 1327292195, 1922313655, 1194817669, 1245477162, 1106123553, 783551135, 383703860} ,{2008346843, 1004350694, 639156616, 483580544, 115595160, 623551846, 1065158564, 540271776, 1644486956} ,{192172067, 1542314238, 878032154, 1426232400, 766411126, 79189644, 1279812508, 1837291481, 230733290} ,{708687977, 187069245, 2088334681, 1992506442, 1485597575, 1764389553, 719179061, 1848167953, 87119515} ,{1482919068, 802476107, 1112201519, 340109734, 1270225255, 1142447892, 2000834945, 176688415, 671513398} ,{366743957, 1870688754, 632061393, 708736410, 606032499, 1420205822, 349737720, 586328208, 1259811085} ,{1514589758, 394019090, 1824553642, 57842068, 2065705255, 1796647957, 873604310, 825822383, 2098789968} ,{348923018, 164006386, 148293217, 1097201928, 40052803, 1974618612, 745666891, 2017334522, 1696217732} ,{1377026540, 350265539, 810774812, 2023225621, 148932849, 2015789897, 1930609448, 59022126, 772520199} ,{1919023920, 573371149, 434282342, 1926994982, 1170916982, 2049196424, 146560323, 50337769, 2119666404} ,{752582528, 1078431089, 1724222442, 2024232693, 9602876, 1674020663, 1460437757, 1190956931, 773883956} ,{1498708907, 2011383693, 1619995664, 6073662, 1505439642, 90971205, 2125026654, 765552886, 1881741226} ,{1697362287, 1181738133, 1843440199, 16445477, 168687820, 1339280609, 193575980, 2096177531, 616141690} ,{1377406477, 1869719511, 248894690, 724339349, 1335957702, 1065504697, 1701329146, 773119242, 1280114332} ,{659025791, 1423813806, 631385842, 1166857595, 816463961, 226787138, 337261979, 941441720, 486959359} ,{1651918891, 1314019757, 1205225684, 630538717, 300123628, 1916964151, 1715404237, 1855508334, 1153934264} ,{2104395145, 980344374, 530997496, 1344684534, 154983898, 780676985, 794631463, 1388686069, 635311415} ,{729823239, 1127673457, 746887363, 648689552, 838331803, 764861141, 586560146, 1087638595, 2068271030} ,{156742361, 642857268, 1920541390, 1724202320, 1083897654, 695371624, 2106658051, 20504361, 1184095310} ,{1312733772, 986567525, 693137512, 237556632, 961643492, 1815600893, 1453541570, 802558906, 1324992969} ,{720395646, 39264999, 1087631797, 2026545086, 35092485, 431371617, 614529184, 93472954, 1505615504} ,{2143574953, 1511966714, 1194201662, 609847937, 1150986034, 10096204, 1620164720, 42790285, 1672880346} ,{1056896006, 1291764852, 2051099216, 1555197607, 451313423, 1566096344, 1932462225, 1415706740, 2103299711} ,{175599240, 510462923, 634527425, 725173209, 502851158, 449003189, 1443743950, 1293604650, 1123831354} ,{1407804906, 1154113091, 847671257, 1727842106, 1229036245, 1305626468, 1249695452, 431455239, 970652729} ,{215572271, 751692117, 1289358161, 2014746603, 1250085940, 1857357828, 1498276380, 1490349870, 1802205309} ,{1236212959, 1537457145, 650123605, 1203995736, 1698713151, 997408427, 1052649932, 2124058448, 1814286153} ,{1210363915, 1266097501, 990370558, 1104748946, 1049290724, 305599219, 776036649, 1799503760, 419464408} ,{1774609902, 1454986272, 216085727, 59122713, 177727448, 1048621171, 1030184323, 20680747, 922108463} ,{395394116, 1032670363, 1433756084, 1613557095, 398315265, 464785693, 519461815, 894874548, 944102632} ,{563544074, 220456072, 1807064453, 824065534, 1504023351, 1526454199, 1165431301, 523538761, 1428158967} ,{1512601276, 164759392, 1444473589, 1158444432, 585070223, 1353335863, 1292928584, 1135416157, 2013035668} ,{550896338, 171330187, 1794661369, 1947689592, 656039038, 1911251751, 159355615, 1355044215, 1792386298} ,{1144509162, 2061849034, 1084276375, 921275270, 1839361447, 1876019192, 1716159846, 964864418, 1286910536} ,{260035459, 1768641377, 95702419, 602287064, 545522111, 1570254784, 2123093621, 2114062126, 449845461} ,{680361991, 235126955, 1588773805, 419209116, 1580896409, 1178116264, 1605929842, 71882036, 1126240967} ,{1588533012, 1996079322, 2035880127, 1447059634, 457875808, 581099309, 651379363, 1541199404, 347952746} ,{1838651461, 1820274846, 1591845261, 1038123452, 1872164872, 1284121017, 820280358, 536276962, 1899216300} ,{343665224, 1091335493, 701048704, 1282811599, 640212553, 1400549394, 1714094969, 1649246001, 170350635} ,{1467553402, 371792987, 1243202082, 1414139671, 1176239882, 72897661, 1325062083, 1272490489, 1697235852} ,{414570402, 139779557, 1697344058, 510093324, 986700768, 209428281, 1731418342, 118845269, 1911472565} ,{1768747022, 953547813, 8700415, 2142883260, 133404610, 549234720, 1963951910, 381758268, 858782675} ,{530917265, 888943867, 1295573302, 1065619695, 744472631, 1239993736, 432078765, 1451263039, 129156385} ,{885246945, 694838988, 502242470, 572927460, 750190704, 921873469, 1145954397, 1997204521, 158543304} ,{1624850116, 907322948, 332044423, 1091497064, 1290242743, 2056612325, 149101021, 1146685889, 1855270323} ,{614868838, 271888387, 2022838106, 1657005186, 1514636273, 1989419576, 897044525, 1879910514, 212422499} ,{1264872439, 1543900995, 634817235, 1488893100, 400125529, 993821459, 179605542, 1640779525, 362723607} ,{1519741919, 2061686307, 828346472, 1265988049, 1404625513, 1422402453, 1726369984, 1091255284, 1452182893} ,{1069500052, 482075726, 585402511, 190050655, 1615378034, 1396240896, 1814705115, 1165516600, 1207447224} ,{1247169724, 2017869777, 1065461747, 1177956684, 2085051005, 2010857103, 683784308, 1797625635, 919011121} ,{905228797, 1698877070, 1157753191, 226594385, 539958548, 1059924444, 1216276365, 337768569, 1388107190} ,{721186231, 1016782857, 2008726936, 861739302, 180238390, 1945483682, 1542556012, 1610334886, 260866274} ,{916983402, 222413436, 66540691, 504440587, 2129129703, 116217610, 1099606351, 1300694370, 600144850} ,{1888751317, 1524694410, 1350614352, 743407049, 2002718784, 541508902, 1375834601, 369239121, 1980859274} ,{904836252, 563107399, 696338202, 1939166045, 9619299, 1534904899, 51747305, 310441753, 1296829056} ,{573968466, 475684055, 861645203, 1103492599, 426249193, 919040186, 1184226232, 1977503606, 378368025} ,{2131879849, 1731971207, 840060391, 1209390921, 211779408, 1952180901, 264171258, 902182616, 1614002410} ,{769119323, 122432434, 360007654, 159554515, 500338214, 175363218, 83882163, 452318305, 878897719} ,{1074914909, 341531336, 4980063, 836221399, 1335741444, 931678759, 1719001962, 449319077, 856357220} ,{854798137, 2142645505, 564425890, 1825052627, 819136669, 1521343610, 1105697450, 364316696, 262129096} ,{387926344, 2058165008, 785961897, 1071909447, 116726172, 204403870, 1788162366, 1090297713, 1130986343} ,{2004682437, 823863587, 1560143180, 283283465, 859354830, 1947585834, 234243044, 630795129, 1604944176} ,{756445727, 1162582200, 2038285584, 875617336, 1985188478, 1649445472, 601986210, 1738663986, 2020901177} ,{1184023761, 1207928575, 1647296032, 1658452862, 1336978353, 900894441, 1439357160, 1154677856, 503344374} ,{805482877, 1228253740, 235157337, 471340878, 169258403, 624212186, 660928166, 884261114, 1363005566} ,{396969836, 709364036, 877065720, 851734490, 1428728058, 140927864, 1257237697, 89578749, 216259490} ,{1012511944, 807057442, 461913695, 1447340927, 1089918427, 882174991, 1334268454, 336101874, 328037677} ,{1634214520, 679744277, 280583742, 1660467541, 1328258552, 107769195, 1853477259, 904376318, 796978476} ,{931340262, 339262378, 1745756429, 222084070, 1360311669, 228041651, 699574210, 1050974920, 992470447} ,{632218414, 540313674, 1732554837, 1167278053, 872862473, 1822275782, 1309506678, 494202351, 1881007656} ,{802168940, 1037385437, 1909841389, 830796228, 1682969135, 756626308, 356531993, 2101874401, 1054687277} ,{1582808524, 1147926766, 1465190782, 741343677, 1466203711, 1023440512, 247993144, 1084042806, 1731033823} ,{1943364045, 1261270946, 621881715, 1384450670, 135359718, 1117964442, 445342525, 1765451698, 1450186906} ,{1012431234, 1735181310, 1173228553, 243386076, 1799788841, 736585284, 1344920433, 1168528295, 853933026} ,{1541890707, 1567814651, 505417621, 534032953, 1621307421, 1779772180, 2029981005, 360410513, 743544465} ,{1593779600, 23070247, 147658197, 553240314, 471126225, 1612731034, 1086440888, 1031979462, 324681778} ,{1235978710, 1132892313, 698445613, 657947630, 79629679, 1774041588, 936524516, 2048150967, 114396117} ,{2037599088, 2077057749, 842471915, 1920656333, 1226252596, 75407880, 816079157, 1312906081, 874109650} ,{1293060814, 36626528, 481479626, 990568455, 2015732453, 1087293796, 1818913715, 1964561390, 254394143} ,{33498745, 1765531133, 1416715888, 1387108602, 1084857880, 143193837, 1044475498, 806793813, 50860769} ,{1071162689, 1890489523, 1398531315, 1738344809, 1118813031, 334466576, 851971284, 1869651589, 1430802616} ,{1090751444, 1977427728, 374750646, 573993547, 2129387263, 2025161452, 954434326, 884186627, 424057573} ,{235047904, 939083508, 933606250, 1180807098, 1335791362, 1286313471, 1664512541, 705601891, 122463059} ,{677385223, 1753791217, 472166664, 2041947872, 1200464059, 1838828936, 1546563454, 856405569, 1867089751} ,{1911417413, 2008827129, 64376264, 138205143, 1207704233, 588211758, 1791237431, 692388048, 1824361875} ,{1808051153, 706475871, 338398083, 2108129905, 1127000384, 1469522338, 1756800547, 97385343, 1415038061} ,{25039702, 1123887658, 490251548, 879000575, 1817434551, 1329872127, 776415580, 607255122, 1819643623} ,{1644067016, 1738850054, 1164711686, 1369425656, 1407476585, 733929869, 1874203873, 2109318671, 1850331056} ,{644817122, 2093400822, 1583883978, 718725935, 1832292357, 2082463194, 210776766, 1289199161, 1488446833} ,{1639123879, 1303062005, 662956178, 737739192, 1047565658, 621994465, 982129027, 1145613309, 1612968641} ,{396050851, 2084485094, 1155936110, 1350713767, 1179135276, 979499655, 276240319, 1146655473, 26186584} ,{1531507068, 204293137, 1896592370, 1131696804, 1418596466, 527259122, 1926800042, 672065154, 539128568} ,{1614412765, 1723831398, 773220625, 991963142, 1483839855, 1695657842, 394488745, 1390762993, 245453531} ,{1918053851, 1367641519, 502362525, 1204796354, 977914133, 1877438677, 1670620088, 1479072782, 674987024} ,{1802059851, 565675178, 1110610202, 728701659, 872868375, 1116070236, 290488890, 1700772371, 1458714051} ,{1983574300, 1604224142, 1200637877, 1429262930, 469046515, 909973137, 560024732, 374260815, 1948667965} ,{446196927, 1461964750, 1804496164, 1780785396, 127261834, 2057415102, 1299149243, 1648205708, 409383295} ,{1024705237, 866058754, 2012999889, 1308847138, 1775589388, 185289374, 1303903514, 208235814, 1058461759} ,{201069683, 2071189364, 357252992, 1051847452, 489469441, 175453875, 1906456871, 1565703553, 1337337568} ,{1927513207, 54842564, 2011192543, 1898121088, 15154534, 131832218, 1750302902, 1500263437, 1315423431} ,{52029874, 564327192, 2113726104, 531817667, 733453102, 2049355289, 215335747, 1208069432, 1599118989} ,{270725124, 1802722816, 358115325, 1203312587, 2076653487, 2137465752, 898492500, 888840212, 906565954} ,{608506474, 1417904998, 376586948, 93458120, 1623664036, 560682638, 2113083307, 105498239, 1479365470} ,{1965898957, 699426219, 1093758275, 1868652118, 711891305, 1836722684, 880402065, 2097382307, 729426774} ,{1197261809, 1331589893, 1236685621, 947176858, 884050337, 1878915901, 686439238, 492174760, 711789171} ,{1549011024, 1451677727, 257568526, 1622859760, 470880993, 1339020598, 1721646599, 912909268, 1010356208} ,{1394787119, 1123674924, 762238967, 285744223, 1238395632, 528125250, 1651136953, 1103710660, 852096901} ,{617088316, 1261533699, 2019814535, 1049821454, 2127501273, 616056059, 1696820732, 1744424386, 13714185} ,{1229814399, 464992500, 457275784, 703159643, 1247124630, 2074666925, 268013272, 1897845186, 1227123909} ,{244856929, 2012257089, 690212598, 1704390369, 1409568697, 745441158, 214559828, 340804241, 1397661395} ,{801210459, 23497828, 47638656, 991743481, 1578209794, 1783183632, 712014573, 2138402751, 2010884837} ,{1910386415, 1639338897, 844655844, 1883763804, 172886006, 433832740, 692814385, 30666034, 1665391447} ,{2015828548, 1493264004, 2062462853, 1629713792, 1009344130, 1646717471, 519744097, 1125680811, 1796471852} ,{1835501664, 1999987315, 1298877304, 1262956799, 995381157, 1787058462, 888155207, 1703721753, 1794942914} ,{928471896, 1786303435, 739441163, 1888245603, 1350376693, 1031841542, 593044988, 392189669, 31994303} ,{215153529, 1360422757, 1314678234, 1781806305, 2028357744, 104003016, 848780449, 303617142, 2136840109} ,{1686905425, 1856136307, 1636165546, 458937486, 2008354159, 1802590320, 1005258934, 1610335412, 585743306} ,{53836281, 433162304, 1550303847, 1177981514, 1177118676, 521693788, 79220833, 398634946, 1681841539} ,{1636598258, 776854993, 715220357, 1483834825, 11402748, 2136780885, 504217959, 1199984357, 1416769323} ,{571125825, 736194084, 1551573831, 1727471185, 679811713, 1073170957, 853841449, 247510672, 1881673166} ,{694808063, 1345663596, 2008714432, 1861381481, 1853926713, 1424603321, 1617477452, 436270914, 1056986799} ,{588188738, 782925145, 918504070, 1544068016, 386109458, 362310722, 1006443682, 1406561092, 1182303264} ,{1043196944, 1028615001, 1797263598, 1623567452, 467628146, 1092992800, 1414716118, 913668446, 792089284} ,{1372262391, 277285817, 2052757405, 1416532854, 7020127, 978109986, 207180357, 1021897182, 2011588125} ,{614999392, 1542665441, 1154299651, 620655532, 1993114045, 468422195, 453480971, 1643053198, 1203297061} ,{558214976, 1221144205, 633775227, 562178849, 1655590538, 596272234, 1928318498, 1820002657, 696467061} ,{1121078507, 2094285302, 743547366, 341201636, 1340852240, 306043454, 2139670710, 666058711, 141364050} ,{2017116964, 103178154, 888849439, 1886052392, 2008893210, 1974167593, 1593755152, 469244630, 770915766} ,{316586562, 1704556691, 40721257, 1245016998, 714550901, 1808499759, 1447855806, 1382512560, 230671505} ,{883209699, 824121272, 629643645, 999462239, 1553698092, 407623695, 2097588087, 1016925685, 1734645014} ,{1512741604, 1512842949, 1404484163, 1365256393, 744131015, 1412880117, 587853575, 622636730, 1599949983} ,{435846361, 1036163119, 1899301616, 1043124403, 1762919260, 990991075, 1015583828, 1546663522, 1436335392} ,{1528768065, 857235054, 1949998735, 1523557410, 741230885, 859037143, 1971458282, 1048295376, 2092314421} ,{364968367, 530845146, 1683933107, 1859028684, 1760789583, 1154725494, 635534090, 562849929, 382676102} ,{1341685924, 1770587180, 1899993552, 2023366919, 893915110, 1730493836, 864823980, 1720678526, 935922720} ,{1430119060, 823481242, 658408711, 700985412, 1979004497, 322939571, 1726495867, 848331135, 1145217688} ,{878909366, 433368474, 1210967251, 781760528, 1280202402, 769995290, 2015210723, 398559322, 1355455004} ,{2145405193, 451131243, 252625853, 1880454465, 951581873, 1096103796, 1586976442, 1539761075, 2023278232} ,{417539093, 1989662889, 1512864108, 40395813, 660031914, 1091904930, 56955559, 2060570832, 1227322311} ,{299134768, 1026768403, 1095597217, 445792330, 1077509777, 1781064693, 1868088945, 328832111, 2143534544} ,{2087337103, 892235551, 972778733, 1124765233, 2009571003, 483790112, 2091942382, 607333764, 79017268} ,{1502160616, 1289580144, 481978745, 5234339, 1265809165, 334327199, 257590043, 237200371, 216421394} ,{1962095531, 1522013735, 842419499, 1238031348, 1080954013, 2016396305, 673551704, 1431784981, 687203452} ,{37009199, 1891568535, 1330787177, 1231066769, 1662220422, 1771241133, 2106717151, 1674831772, 217035843} ,{1886041860, 1805586674, 1872553089, 141960585, 1662769191, 1145472023, 1793760428, 373137894, 887925834} ,{589810594, 1277907571, 2140501088, 1540007896, 1731753430, 427228769, 809855013, 504939365, 2091895564} ,{1499163126, 1586770542, 379703924, 945232235, 1412536799, 1706308542, 2144727946, 838822548, 1977942366} ,{1637912452, 2088818089, 360037143, 81243363, 1174608112, 656555272, 1418732986, 228931912, 235488484} ,{974469497, 1699601358, 1652401203, 90424132, 1910758610, 1036414487, 2041275409, 342345380, 1790937727} ,{1962390262, 2102192646, 1386167105, 617926013, 1945893959, 1630104419, 708256376, 2088023199, 1107711657} ,{818014600, 313950028, 277787949, 1254979295, 2145333972, 27644918, 53363863, 1558424490, 526143577} ,{285150557, 589339836, 2082702549, 1763176836, 1212435689, 167414594, 1379862347, 2031844291, 1682406193} ,{92533581, 2120317487, 1634222377, 466163232, 1603000602, 47821269, 436368927, 1065053384, 1659728647} ,{609391999, 842388673, 1118641564, 520713858, 312764066, 521727291, 2117153018, 1712445584, 142904841} ,{1087658662, 287766399, 545667014, 2061796934, 1509625936, 121834232, 1791515221, 1542140789, 1936244301} ,{1455725792, 24130898, 903326540, 394186216, 345857338, 803117059, 514861387, 965604007, 759515212} ,{510301073, 1877725462, 468132402, 1856922743, 960992364, 947500810, 1210248942, 1648083978, 169414607} ,{971143029, 1364552559, 1524652922, 72829406, 311117465, 1991020140, 827795873, 1178965221, 5853820} ,{465547966, 609118875, 1584320989, 1296827725, 1859954601, 1090422181, 1310047408, 1697304591, 299530353} ,{287548021, 125969087, 227144787, 1562477472, 859324093, 132701462, 395447640, 1424070798, 146150949} ,{1760678542, 2038931003, 1958188044, 1569455282, 1970021227, 1637441878, 1170335022, 1374586389, 307799985} ,{1775296214, 1424461092, 1020391546, 1105476547, 186640389, 158862323, 1286912280, 655057249, 923737993} ,{235084588, 1218433973, 1259894274, 2144733371, 751102764, 1995147826, 1205837629, 541574195, 24680884} ,{1117275594, 1883224087, 2121531192, 1539479964, 362313695, 847932535, 1287883893, 337101325, 781199143} ,{494124716, 1124071275, 1855910221, 408214753, 2006746944, 521630895, 1136468496, 1728437697, 1079671203} ,{1659640455, 493320848, 2067674484, 647013421, 928353718, 1523059424, 1964100415, 1570191011, 1559902870} ,{192631179, 1678231275, 986428868, 255779710, 1186868151, 1503436180, 460226205, 631571450, 2039559967} ,{497332128, 1840254579, 964092223, 233590012, 1589018354, 377698146, 1476142966, 361185068, 174113392} ,{1685059342, 1618611735, 381289364, 1324947485, 1703892832, 1243589330, 1019774922, 1182774627, 2041540989} ,{2051240620, 399175179, 69434493, 1721567317, 1917312866, 1828724609, 395748521, 1690429517, 1003539988} ,{435644048, 2093024491, 97686384, 1094732879, 517107310, 652058886, 513710868, 1773927534, 1399471226} ,{731352777, 1939752627, 435051989, 1686496879, 309064953, 1361656608, 817465635, 863512995, 1617448140} ,{1607632578, 2083517797, 779586430, 542353915, 404599709, 1306098030, 1396193943, 375808582, 487815070} ,{2012468723, 1769359693, 2095941966, 166022319, 1075511277, 1387118187, 214586230, 1164658962, 882629005} ,{1968383356, 1217792162, 1335522000, 119324315, 1044806267, 99361258, 1085997195, 1349838032, 1860853144} ,{108710118, 775000491, 1794522139, 1494630124, 1854758839, 522203585, 1138149, 313115766, 241929768} ,{2143733020, 634849364, 354331799, 728758894, 1468512080, 2032026454, 1146361093, 1723396882, 1080071536} ,{305276790, 1776002992, 1342485250, 522733922, 401869566, 595414292, 275096789, 1392817687, 666002652} ,{57975234, 1959146628, 1994647938, 2072652597, 991380182, 1294610202, 1756282732, 598960182, 996492483} ,{887998331, 119496924, 357856902, 1547597297, 1251531885, 989813514, 1514660725, 1554336955, 170643039} ,{1855889478, 1565314082, 445574055, 2143232286, 1145935616, 701004400, 200222515, 1536283494, 2111474946} ,{364207608, 1838082137, 213036909, 385389658, 1135561827, 422119286, 709540253, 1532914940, 599041598} ,{56798945, 1529571902, 1815351490, 1330486399, 1171312503, 2046563183, 1421176923, 1155197023, 1089116092} } /* End of byte 8 */ ,/* Byte 9 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{619049912, 1079722514, 1354807971, 149667838, 1552977993, 4061586, 1997631510, 524238633, 617875195} ,{535516095, 218819892, 1803453533, 806567535, 733030258, 1642001994, 584507741, 709364008, 1343746314} ,{1974506565, 1271326081, 1095774688, 1443001672, 1382192579, 1117119695, 167911883, 1706355060, 784968308} ,{1904573868, 644902499, 48552544, 965945966, 611689662, 837295104, 184995987, 1652816647, 784823112} ,{1863020615, 217684939, 1250054277, 949936848, 2137033600, 1176240459, 730995934, 49843177, 1797465941} ,{142994373, 801421864, 157686382, 812649715, 1268776227, 2147238204, 1983133387, 870172306, 1608461014} ,{2072136048, 2096251095, 1897143067, 878046842, 655472403, 82513290, 794521592, 1888454473, 1390377708} ,{6461960, 699114421, 1874375723, 1020466481, 489501780, 696852735, 1093606354, 2140942810, 813913620} ,{1635608418, 1558581574, 1377684977, 1734235561, 332236814, 771370263, 1429827932, 374802326, 845301094} ,{357748305, 2095175263, 1762713580, 1778264428, 1695858347, 359350621, 1744315045, 1704625074, 454994477} ,{151399900, 1590309679, 2058517990, 1559390799, 185298695, 1537319714, 876210996, 823220221, 4545800} ,{1394832398, 546484721, 2060952283, 898294513, 1124410352, 1991028664, 1793588980, 270538370, 2103463746} ,{90931320, 927287731, 625526632, 334578869, 399623455, 895461769, 1278482654, 1390348493, 1308865853} ,{38279199, 1423388043, 498137195, 849770688, 1258220986, 1302616141, 149595728, 1656405642, 893227376} ,{451391601, 52549319, 1765710159, 972021851, 2106230937, 1936441, 1872406587, 1068298822, 1947074714} ,{1062547187, 1665870345, 1375864241, 429355074, 1027307948, 2045598106, 1304149998, 1905637857, 38212151} ,{1001576382, 772256397, 875033500, 2000436370, 1915668879, 636453028, 1511268399, 1364118248, 149737304} ,{1644015529, 595404386, 400422380, 448997642, 455803349, 986366592, 1343684261, 86082333, 1821538903} ,{1350136621, 57403831, 1543436134, 1856879695, 314740216, 1839713843, 1221658836, 999802373, 1360673756} ,{2105336211, 1807611667, 1234099556, 2070925050, 403287763, 104376352, 338574374, 1308178925, 482296051} ,{393316110, 1360338099, 977026399, 1465102986, 133869482, 148935637, 602776379, 1146757071, 913620513} ,{1230497636, 1562221818, 1384021246, 1191407244, 1250908662, 702050916, 708806696, 513828171, 104701161} ,{88277466, 709143456, 1724818603, 1475288839, 459370051, 1322491437, 939824053, 394731144, 750710880} ,{677935013, 140364546, 483812281, 252444054, 1710605949, 1298951662, 243927564, 596566136, 1072311072} ,{78939106, 1186136771, 602572284, 817467367, 1770008375, 1802753178, 1904343954, 1654185536, 761546708} ,{429434322, 1205705214, 1007057269, 1554608428, 791363309, 1270151566, 1206749315, 595375256, 153485240} ,{978931300, 1320254265, 540214532, 1665202339, 755305851, 977628310, 1271786447, 1971696359, 2105321590} ,{1864548026, 743097659, 1339066245, 700723693, 330573693, 734750944, 1511400144, 601230372, 1408633251} ,{287651679, 1237537606, 499631393, 1113542764, 111451801, 1029485336, 1857298443, 785610856, 1554474919} ,{2093636885, 1349622704, 69079890, 839870784, 1382173384, 1153720706, 309522737, 409415279, 1131503854} ,{2104734668, 317095917, 1114941443, 1054662176, 93930203, 439432242, 1175888523, 965223740, 1903585541} ,{628554766, 1630151487, 1507185511, 930631393, 1013163798, 2054402413, 36525390, 1991589330, 1389849356} ,{758762081, 1600760383, 581144288, 522917653, 1478955063, 1442567837, 1901606645, 394226239, 1233601697} ,{523260636, 2105403486, 745814256, 1502381784, 1868292349, 1492765534, 1726584872, 39083620, 1854613082} ,{1488887086, 1129030464, 2075424761, 1088604653, 1839205978, 1252165078, 1422418619, 483383954, 1025776500} ,{1667428830, 61481922, 3488054, 1995749386, 790395665, 551623655, 1421032510, 424646184, 1071266230} ,{2138168344, 980713428, 1237089983, 1093686229, 684462007, 661851237, 683319503, 1176646950, 1346377398} ,{1792554299, 772948721, 781982845, 1254311261, 447767026, 38180534, 124374564, 1640276893, 1269534484} ,{1080038599, 2132773768, 1051228472, 2126522347, 1867664802, 1647386076, 45146616, 1681224302, 1493984661} ,{1173059173, 1290929837, 241328073, 254507121, 612986054, 128739850, 1270010949, 776370930, 1414998052} ,{416331673, 1466644764, 1530598417, 1108629674, 976898083, 569309402, 1546088491, 1748614708, 1223876394} ,{1158093271, 2135212453, 1800640037, 1132091666, 1740986325, 763512167, 137546408, 1942943882, 1275704331} ,{964198910, 796077917, 154965414, 689787887, 1958502200, 1532350122, 1980874883, 318604225, 236377986} ,{1356589332, 831882403, 2127838631, 1591000185, 1406147889, 1645566325, 2088761329, 1826301361, 874316595} ,{1089372067, 422654497, 1022472868, 427155708, 1540381111, 792993550, 1818666949, 756989917, 953312093} ,{1639559898, 1339547579, 2094179613, 1767564509, 498235731, 550953250, 827604152, 618909597, 2106978472} ,{167503462, 38951599, 1900173882, 1958753685, 449394992, 195073752, 972904610, 544495248, 1117815114} ,{524746468, 1920573749, 1153626273, 651059922, 854377130, 448422297, 674555922, 629713714, 2137215883} ,{416965971, 918319397, 1756114414, 245637043, 343430591, 1953497313, 320771078, 2066102448, 479288193} ,{263390522, 1671110345, 1988347723, 314212821, 1420378877, 1793672027, 2112181790, 648691018, 978588656} ,{998893422, 2056054170, 660163555, 2088618751, 702155549, 1921189837, 1813108212, 595037418, 279144088} ,{1539346154, 816228068, 1615584931, 103930926, 684541693, 1780695467, 1743960794, 996783201, 443041755} ,{150510525, 1452172420, 1576996487, 2024976832, 1270920475, 1936364648, 1758262319, 1302210838, 600398317} ,{1585750596, 2123791680, 1244579270, 1151556803, 1488518691, 1139282242, 1060578850, 1854096029, 1805952722} ,{2041753275, 1166253998, 1892270849, 1226528131, 1682466410, 2062253087, 1922101545, 1090663502, 1515482549} ,{2047844705, 1751937589, 1814392436, 196114955, 1589587318, 1572062854, 619925343, 301263632, 625151588} ,{651472542, 1006945562, 425812063, 1602240689, 1730929540, 993678537, 1903385675, 399184791, 624847222} ,{2133292870, 1571485875, 1407349081, 861463532, 1579821175, 1898575194, 98602669, 1793320239, 844784909} ,{730789890, 2023683424, 1870038081, 460077793, 106912621, 694165139, 1519706100, 2075664305, 2064784673} ,{1366682236, 1115529983, 395571054, 1721054062, 500723368, 207694471, 1503993995, 657785252, 1558988718} ,{1076362069, 1798837596, 1942883948, 961910767, 559288357, 240994866, 355297192, 2126046938, 1747907211} ,{628299032, 1568637984, 1727002228, 467992943, 582594107, 489739864, 1985090005, 310082353, 1869929654} ,{94760650, 583305894, 2074673943, 1991469711, 304647757, 1623881490, 432573598, 1625320587, 487404828} ,{2066465019, 2090178646, 1594328122, 2078969693, 1154649322, 1259541707, 1905107801, 1519556145, 1159675583} ,{895085680, 1963782608, 2104952676, 1808317680, 529158016, 1650502620, 1527276759, 1166396632, 1409409383} ,{1729345243, 1306402272, 482577818, 737116431, 1447421425, 1023253984, 553935653, 1939103323, 1090857809} ,{2016037859, 1545143026, 1172767164, 613561883, 1457402766, 732155593, 164893430, 1629591006, 1531291008} ,{283847497, 1801444704, 1293134334, 691826450, 1193627701, 1379975533, 58523621, 778717365, 1457947535} ,{821183652, 902587135, 1879027841, 1028735675, 1676122846, 1903663045, 1749616687, 1167989111, 300959016} ,{1472129492, 841121110, 908649993, 970421216, 1208938670, 1614365976, 2061608819, 141483947, 1911038565} ,{269452246, 686772962, 1902901736, 1213480377, 1771316637, 1236709770, 396311493, 323152078, 1443465045} ,{1029485271, 731203554, 953219060, 273126457, 751845459, 1551999715, 2020631895, 1176518029, 1306431790} ,{1419965073, 643245435, 1779965963, 1636982683, 184609028, 1542122030, 166481429, 397598177, 1870810766} ,{1247861429, 1806275313, 308074826, 619986808, 924882933, 188407807, 993751370, 1345915652, 1742302643} ,{599647232, 2000369910, 2083347069, 1213240746, 562505679, 931499920, 1757322097, 276055465, 1002757178} ,{1932636252, 2099931020, 125537017, 501738785, 1697895693, 564514873, 1323821182, 253149736, 1179796556} ,{260075885, 2040349319, 811858437, 729147395, 1551280132, 338710024, 1122806658, 121416700, 1508784287} ,{1305552928, 1598771188, 1037722356, 62473197, 1046699198, 2000821122, 552183328, 728202491, 1972944332} ,{509201895, 1408587283, 1134964849, 2041640515, 582300407, 693495708, 1751234810, 354966508, 1317055588} ,{1303147228, 961406249, 1173247523, 1737978189, 1314254776, 1364298178, 480994796, 1140133281, 1022582310} ,{1214726185, 1937767947, 2118578232, 1961567074, 1717423596, 2049484694, 757013495, 1159741439, 1204139910} ,{510264271, 1827402698, 1413529477, 1918018869, 1932026946, 682442562, 1389422945, 1968591750, 169278010} ,{1056171287, 24138713, 572456414, 2132999492, 1764613686, 406098039, 322947918, 1805219356, 703290182} ,{1837245566, 1821568270, 1642711518, 1504952911, 154329757, 1822777416, 575242693, 551998692, 1249620403} ,{1436769833, 529861437, 169192894, 1676740967, 1968499337, 1422856366, 315438260, 1566337576, 990784456} ,{496330537, 1677185697, 774381754, 1384679993, 1421260484, 1181347462, 575658507, 1934818792, 2032564095} ,{1150609023, 1645537618, 1476042798, 1556290112, 183406315, 1564947007, 1679930622, 1183062759, 1809443814} ,{107679818, 935328944, 305182780, 297007152, 1316894110, 682892643, 388292902, 375853001, 1292719742} ,{1159525526, 1777607226, 823155609, 1622622319, 2016975448, 2016018386, 761794268, 67504137, 1671226453} ,{2055981591, 1509963515, 1409020949, 1963490739, 1586675444, 1971485964, 1148497591, 595148941, 1485747497} ,{1352231775, 59852365, 2116842465, 1381470563, 594880555, 1197183979, 1590453890, 1898553525, 467257733} ,{1981981690, 892044988, 1600409512, 1864608602, 1960436332, 1272093220, 1155176885, 1997167604, 1690924400} ,{1343019630, 2042352255, 104541280, 1865154188, 365899493, 498553561, 37421716, 1422559858, 1800738825} ,{556658466, 1618255052, 1868683177, 627613784, 439658035, 2067669683, 1467121335, 1724734052, 1124491305} ,{593813029, 61437219, 959676791, 1595779669, 428073552, 819970300, 672687186, 2004584248, 585462524} ,{589506868, 715770711, 217679424, 1269352674, 534561062, 1368436766, 382971650, 61570773, 1908875662} ,{1140812398, 1681209913, 1901036319, 367970173, 91591647, 1067177209, 346408290, 1320469095, 1240040199} ,{1418471223, 1857089000, 1475679116, 582520281, 205799578, 1682746619, 779343617, 2135111115, 1069123423} ,{630870701, 327211068, 380522142, 11063386, 1446026407, 213780505, 842569877, 1541525029, 1872860288} ,{569073780, 656699519, 1694230899, 782947865, 580892177, 457523696, 1104600876, 2114324476, 1558865528} ,{554799857, 521632850, 680652494, 300995519, 2009144614, 5330353, 2074651241, 1829725414, 724495625} ,{942219264, 1648722318, 1457570136, 2076132118, 138652350, 1748442305, 1382486241, 1699636116, 1261766627} ,{815365015, 1988084037, 1877673235, 615075910, 590534597, 1165835370, 395889032, 1765004937, 1798143969} ,{1648265420, 953347173, 1114567507, 862843432, 56829526, 376454276, 553311781, 776558491, 966150060} ,{2082576863, 930072806, 621825541, 1594753343, 176492615, 523293451, 1619788839, 920453067, 605953318} ,{701034094, 2030074050, 1462994527, 1058216445, 122407161, 1140467023, 1385953616, 143635950, 1145876402} ,{538222559, 1923577402, 2110569094, 674110487, 477628269, 1627825324, 1970326960, 2131979698, 1664170657} ,{1700725382, 119647178, 1550892453, 1366958948, 2084566890, 1886625306, 1534818690, 1903514230, 344595086} ,{1385797973, 60433709, 1650118736, 33384039, 957644357, 971268789, 129058654, 1491452063, 757758682} ,{2102416586, 65112053, 2083551662, 1470724268, 235001699, 2120994989, 160087410, 340951851, 1296784557} ,{300760221, 1014582586, 1026821242, 1553025580, 2121475489, 1016690691, 1852095506, 1549037247, 183448082} ,{1111421159, 2126681308, 472687233, 741417662, 1222517844, 1630309720, 2049562478, 1956806077, 215513170} ,{1170523436, 1457723623, 1578689433, 8472112, 573522605, 618543691, 395831500, 2017414734, 1370666001} ,{1280461345, 1067618791, 350478701, 2144875656, 1156159905, 1156328232, 1234254008, 424396565, 1544492019} ,{1519498198, 1599946245, 1591842551, 1989044134, 729476589, 853373511, 326794181, 1982040353, 1431626836} ,{95586490, 157854048, 460248005, 1475852801, 2017151587, 1198140600, 40898795, 924667311, 727150387} ,{253475368, 303741073, 61520603, 1827914903, 717658381, 1074018153, 1081478620, 1449397386, 854614390} ,{412799901, 1587050682, 434458848, 1317586860, 510189272, 1549944899, 1932087598, 426607836, 2078863447} ,{1277521508, 468262381, 1831512806, 959838628, 1570309460, 880123312, 664476188, 1586265421, 1122490745} ,{252496774, 1705995803, 2100008057, 1781210528, 610186719, 973376838, 1544577799, 1133234116, 1931541696} ,{1893321191, 1305644235, 502385748, 1392237567, 298069941, 1824097824, 293448239, 1182156501, 2139172995} ,{399740898, 239063798, 643445332, 1503214092, 284010284, 2031889766, 576529008, 12938751, 1460597727} ,{1033545183, 822700471, 1192169599, 1578513548, 491506220, 783082204, 1347587557, 784735356, 1934835763} ,{603668926, 1443927199, 1790770295, 2021240672, 1085889182, 1876888169, 2100227077, 964038648, 1607469114} ,{589816837, 1216622654, 790477698, 303154437, 1366019987, 688064214, 2052893776, 1302628533, 158480724} ,{71258286, 652521893, 391939259, 687778500, 1103228172, 1271826587, 698419230, 1517073593, 1968116785} ,{1763273811, 134511335, 2024715531, 1733513426, 1489570240, 698481507, 384382633, 847360228, 2051987619} ,{1373627429, 2098903205, 483600411, 483141867, 1310375215, 437017599, 223270069, 1371364984, 1259894200} ,{1188913832, 1687990983, 1564595371, 56912222, 839757312, 412877683, 1742210729, 413506108, 1554023633} ,{333878157, 1591161215, 367113531, 352351632, 443942553, 1152181313, 1359714442, 91199942, 626770938} ,{1536434973, 901681514, 1751302415, 1924063993, 694754263, 1759793644, 1564871369, 195243008, 1485412669} ,{1548219053, 1979062825, 1843053559, 566075770, 2119350760, 685566880, 1603105151, 1186104973, 952594743} ,{1998628166, 884895768, 436444206, 605134758, 1808086276, 829621004, 575573584, 833180733, 764853743} ,{1905107722, 1909116090, 1953400399, 915112734, 1003466302, 1457076752, 584430077, 388439660, 867512421} ,{429258245, 1844867947, 1685159135, 455922488, 1193860205, 84014826, 152515393, 1798406471, 1745533946} ,{1459586820, 1798056886, 1603326090, 661070764, 1439992998, 1287201499, 1045431383, 617292327, 1975559960} ,{413675905, 1966037892, 1425614349, 510384558, 152332911, 210638590, 278149019, 305658219, 2085461999} ,{1535470393, 1383000287, 588219292, 488046713, 153228518, 93966373, 1951195432, 1093618500, 1917945690} ,{1644068647, 31251009, 1456867641, 1956305524, 164175860, 207446259, 1276887268, 220904792, 1375349371} ,{958856143, 369867509, 1047392691, 1986103047, 864586796, 356390406, 1566901143, 1759810194, 1751891781} ,{234276797, 640047063, 1915085077, 654355000, 1464907629, 235927637, 1510387857, 1951080350, 1855771330} ,{211780435, 2021921089, 647892463, 1384322200, 1354523515, 1410927025, 690938916, 712996850, 31232794} ,{1751944637, 1272104129, 910106382, 1488801641, 1816271898, 336800959, 684851864, 1678600218, 1014013457} ,{284336500, 283655605, 1591997379, 478004065, 1083146124, 184137830, 453807315, 1929784240, 407721640} ,{2048520758, 368041967, 1495191520, 235287456, 101384086, 1583794984, 1370360005, 292532366, 643242852} ,{2103342379, 1312753710, 689930560, 841299291, 773033125, 469209695, 2136319902, 308335690, 1738857457} ,{413655679, 882020609, 1764721498, 1173126636, 1280407774, 629866939, 1436893887, 1421833052, 2071256394} ,{845286788, 551250710, 1578652747, 1739753299, 1493217213, 1114393793, 436995643, 1908927371, 2006208639} ,{1662892811, 1356084002, 1676740334, 84173488, 1411820974, 1955187524, 1421921430, 660242811, 214368349} ,{576287284, 1354478177, 1478181193, 1031549141, 1920320303, 1582274357, 602262621, 928131406, 92235758} ,{45571754, 1627089989, 1233442969, 497888031, 1969599846, 966894781, 1002403978, 2079781921, 1349050356} ,{1164043895, 1714916448, 245886657, 1608124340, 1709150384, 770630173, 1372030467, 1879532953, 299694574} ,{1912549594, 1821178164, 1143210541, 166648689, 1308214400, 507540712, 106577833, 1525210818, 559642249} ,{2096016015, 163735636, 842455649, 1934104581, 1611709380, 1040742766, 1122440775, 1004379398, 1045428226} ,{365864355, 1492174029, 1844601305, 1558897888, 679198928, 827250593, 803444321, 1385066558, 2127160915} ,{1175408101, 524942709, 1622803019, 264034504, 87649688, 220924056, 1115982870, 947182323, 78258422} ,{792746515, 1426721958, 1710512169, 638254992, 1633081624, 612389766, 2140420845, 425978608, 195498923} ,{1547722614, 1120841582, 621268885, 1976123762, 469534678, 2082377220, 2039920505, 1799022497, 1665237904} ,{383526381, 80209027, 1813798528, 1098959813, 1165956383, 53348976, 1365629657, 298866886, 1705598067} ,{1559500958, 1566104373, 783540322, 1313667560, 292816618, 805530673, 1897468957, 327430878, 2110951093} ,{164244733, 1323463668, 303532759, 1228414524, 1101007457, 514358735, 1734119937, 299349999, 888860775} ,{1761764813, 791895146, 425206129, 1362894226, 843541425, 1571141832, 1163796676, 526068660, 659417394} ,{955424070, 1203069699, 1622331422, 949671905, 1584693230, 1942413098, 727738473, 2090827810, 1926645320} ,{289176373, 414080424, 82525580, 144887340, 1299169713, 1708733064, 2061449384, 2000360432, 1887533750} ,{35495351, 627100120, 803600014, 2101626142, 1948597897, 1795792753, 1570936921, 1659172402, 1582635921} ,{195766052, 213219612, 1290935459, 356961276, 1966225506, 1433191682, 1004066735, 1616150654, 756327683} ,{795705912, 1209933896, 702626070, 1334234787, 1322414671, 72443368, 1536264350, 1870284088, 1526808699} ,{1766563988, 1850259116, 1792822364, 2020343494, 366957525, 2090955179, 953613869, 505462634, 1693284156} ,{1110848268, 530073383, 1571657865, 977003158, 1988568670, 1000269948, 2080556842, 463556628, 537833662} ,{1185022118, 1844987974, 399206135, 659549632, 1567107440, 1215481868, 832780940, 368076303, 1760403281} ,{474820529, 506402005, 352844971, 246477497, 1941876447, 1306582461, 2915292, 1533947333, 653375604} ,{1813957660, 1826862239, 930142102, 1889004368, 35102199, 1191977058, 298201767, 388769379, 1939142381} ,{2109216097, 1110283343, 541158351, 1556960907, 1944022368, 49935162, 738429409, 117394608, 614444067} ,{28341930, 1635381459, 1756412386, 782302569, 372994497, 551719960, 1563310400, 235143929, 1257061763} ,{2074424817, 396887096, 1751919495, 1289902099, 2073972760, 1496199510, 154507843, 1557946901, 1636065527} ,{1622156049, 509394218, 122150654, 661615637, 1571900394, 908007242, 19096484, 1084428597, 870227838} ,{1733312990, 1715006435, 373870324, 1758647678, 2070691479, 34605065, 898629463, 1766249913, 700884733} ,{562052802, 570688881, 532211183, 328368052, 2039555316, 588718765, 863030527, 777926644, 49294073} ,{2044553845, 198926504, 1089919052, 27148878, 605670658, 20203570, 9105979, 2064071250, 956987544} ,{1102189186, 1216791771, 658820823, 486962387, 1026949328, 1475270650, 1216217919, 2101395580, 291460875} ,{1516059967, 413984823, 628524249, 492886937, 1768151242, 634983111, 1587396166, 581543373, 812991785} ,{1995710601, 184975226, 379537105, 1039855070, 846863493, 1741334345, 306940996, 502928992, 1959328534} ,{1449022295, 370510123, 145221576, 1907226171, 1853072678, 1473965055, 1239946542, 1687993246, 1906520136} ,{962612479, 1023219462, 1706029418, 983866383, 395009028, 578930912, 644410140, 758322834, 1632834589} ,{1863394742, 1018259219, 548238544, 1255559579, 1270488087, 580819424, 774448475, 447631701, 1221191174} ,{979361776, 1461834244, 1275511934, 1408726113, 1662106378, 689560893, 282546345, 1846705804, 1445256076} ,{1445700481, 341867744, 1634790720, 1632186225, 870177569, 2090730405, 1439905990, 54477305, 1387981424} ,{987170439, 498299959, 1814006099, 1310969200, 886680585, 45416123, 2120233003, 891010196, 145330287} ,{1020411739, 1988871904, 1460860449, 1841334321, 1138980170, 1524353613, 175717408, 491343040, 967010106} ,{656248936, 1279890651, 99237757, 1655926002, 769584756, 620706794, 1385228932, 312102417, 338226363} ,{874185807, 1764756575, 74015647, 188244065, 1495459754, 62927172, 1340481325, 255946513, 654259198} ,{1893940156, 1251292355, 369904389, 2016432059, 102055195, 158916571, 1495399595, 956171289, 733762885} ,{272129840, 1089479965, 116580089, 1204425718, 387846216, 994234521, 2023455836, 66719446, 1008739504} ,{326683314, 752645832, 109538896, 1982985350, 1756675287, 2141545225, 2044352626, 1911707025, 1624448740} ,{2018835270, 784821895, 1039957470, 390317070, 1248581246, 1151971374, 2140006042, 2003546967, 1298959084} ,{834613073, 1798380942, 892742648, 2005701017, 1631003476, 754647090, 1702357990, 336133897, 1191234065} ,{920381808, 200184647, 2106499752, 1883650044, 322691943, 737044473, 1096914910, 425988002, 388344507} ,{1529620847, 2120428000, 359488086, 796557186, 1000989815, 189196333, 1689133740, 205360377, 606991136} ,{1337539041, 512884878, 1727530062, 286305870, 80686088, 152120396, 1244791194, 1353867505, 143703373} ,{1060861514, 1948054679, 1473471678, 1263155918, 1244198757, 932508055, 610222947, 1801197163, 935902297} ,{1832056837, 1494275533, 607741975, 1855117362, 1306832105, 530933093, 1817779763, 1541016992, 2055966346} ,{1527891094, 1351181283, 1739978358, 1669880929, 1913381948, 172174306, 176478798, 1393364985, 1962575115} ,{301897737, 874063706, 868577897, 2133183136, 144578053, 446276951, 1893496970, 396717373, 1401591343} ,{470327144, 1016249492, 1599630548, 1411539783, 509370854, 1281131721, 1258608628, 1830020222, 83612141} ,{445488022, 1403373031, 2004209575, 331216254, 849610401, 1462200588, 203949181, 367208223, 1043210070} ,{738749595, 507307114, 1077645325, 1798206599, 474449, 1859357491, 1607986209, 54295420, 2128954699} ,{823700950, 1979499476, 605037409, 1988642335, 1814909088, 617462320, 388925597, 1530395829, 98154542} ,{2131729581, 1913763198, 1623776273, 1851993761, 929432605, 1275032027, 1929134340, 1176796742, 732302110} ,{1685905000, 250438788, 1303558336, 604323397, 812123545, 1807855685, 1207679758, 2114370764, 501590507} ,{750628464, 410716948, 152562100, 602884605, 784698536, 577493715, 1561052035, 1667129128, 1060196800} ,{1774769935, 1982770514, 1945095517, 1494603464, 1379281507, 824730125, 45827238, 1263815566, 1581191153} ,{806365178, 1694371318, 497803947, 1715093281, 1635552374, 1197395141, 186089830, 576220017, 1111752821} ,{1611155275, 122493254, 704568411, 2126053535, 1788180551, 1169201643, 617470383, 84577801, 1865952503} ,{580435932, 1942471493, 445889325, 1742946007, 11564829, 880325458, 2070510697, 696144716, 1485901096} ,{1412326130, 1330154838, 736700667, 325763702, 1293602454, 1909582516, 1786333704, 130043635, 711216270} ,{1908919849, 1858318850, 987386879, 1252264394, 2038716858, 9080897, 662086018, 805785923, 2078814332} ,{1280995392, 31082115, 1147166018, 1562791995, 1188745831, 871223916, 412740918, 1203584309, 1733858964} ,{1426433499, 872397622, 1275080167, 110231023, 2080732618, 265771715, 645189155, 1495027554, 913227880} ,{1976094693, 709216796, 982171127, 697979231, 155169301, 1413448024, 916961048, 1236036323, 44679135} ,{1131649220, 183951913, 2121524699, 277982179, 693785924, 1935358333, 692530006, 679841771, 1636957310} ,{759893394, 935272515, 97269181, 984753698, 1998312192, 25064733, 1484360895, 2075840120, 1271768150} ,{352583401, 2133494686, 1458740482, 596074212, 2084921840, 2117464678, 502879480, 420066394, 1293653508} ,{180914388, 1834029436, 743159419, 34010776, 1307056237, 482219212, 1862872883, 1851611461, 766366408} ,{1857289423, 747301768, 180767786, 963886375, 922967276, 419373017, 1859348330, 1386856283, 759690124} ,{1986036610, 1771092373, 487089987, 635018477, 1964886837, 2237736, 56810050, 1688876783, 1695902510} ,{953434726, 603009946, 2053142980, 583090698, 1972079152, 913552886, 230160917, 180411830, 835193188} ,{1779941981, 1531086100, 2107592763, 2127905544, 1111734829, 1015480589, 571058551, 961667410, 1848893059} ,{1196004493, 824695397, 1910962071, 1204802994, 218759982, 1801958195, 743405791, 2125726973, 870619912} ,{1561792146, 1146560213, 1116979035, 1023401749, 1752798107, 1244178056, 252019986, 1579439848, 2058987619} ,{955619211, 1850138070, 1388524789, 1941952926, 1625159278, 549303705, 1733139404, 1764850864, 1436958205} ,{1917545445, 1964647516, 1628862150, 1267102734, 170252932, 1830377522, 1202978382, 1060808665, 335840811} ,{526640473, 1697108947, 1999356295, 1148566770, 693813567, 761165505, 122335633, 354130022, 784616424} ,{102166420, 1834993073, 1888459495, 2128345819, 1762631008, 1799234826, 1902972400, 1462305479, 1143449908} ,{1702327287, 134798764, 390784740, 576756936, 310436941, 290603490, 1362815342, 314519931, 1381470971} ,{1492764118, 264896999, 1671654000, 64427508, 192586244, 1675164840, 1085346306, 328801395, 1688118491} ,{647443981, 1518164365, 1259358753, 446476736, 1787311236, 1398933154, 125487055, 2013169002, 1297450450} ,{532400382, 425012594, 517904683, 687478723, 157724634, 453420825, 896078306, 1018096410, 875132714} ,{198125759, 1940244571, 1529796604, 451127533, 1491976925, 1763526373, 160943097, 72674902, 1681289074} ,{286009394, 305564506, 2098970265, 201449614, 582720674, 1708699771, 534557704, 443851147, 1178992049} ,{736531850, 426457222, 1733024379, 1570021293, 4323121, 1242412898, 1083431500, 1190820268, 446529486} ,{1166868620, 728564399, 1749331893, 1138025718, 1397648936, 320160382, 1852024124, 919314004, 1505385764} ,{2069885317, 378906045, 1431778579, 1116677941, 451732501, 1479807284, 1343342759, 539133793, 1658900044} ,{526011906, 1539188071, 841387578, 1968831814, 854980508, 1999328998, 2074357711, 276438974, 113981773} ,{824114253, 674132339, 1285552937, 356106614, 339747230, 1781556721, 769382267, 838442097, 1871653596} ,{733771500, 570433661, 1306910156, 1084171615, 284114145, 313613636, 767648654, 171249492, 1512667011} ,{825953456, 281563589, 40392852, 742891150, 1191502364, 1710466797, 1267090573, 1695273820, 598156527} ,{626120451, 1537610885, 1412538906, 901936978, 1680679897, 352101086, 1933548620, 225194961, 1709235843} ,{915236941, 178535964, 1157856922, 1515820876, 681277227, 2113060864, 847782349, 37981048, 1526977319} ,{1062081674, 1882876453, 1838360455, 2127291908, 1008597142, 866902773, 783325208, 564637897, 488851331} ,{646974196, 593993124, 1761675798, 1386867461, 1497836451, 2085539073, 280117846, 1933071309, 1787629134} ,{1822701570, 903957294, 1518607466, 1027977426, 1643609851, 1339753568, 1908687176, 2077923832, 57343051} ,{493126885, 1085523956, 1072175602, 1055697808, 179105744, 1449800458, 349997077, 611076284, 394725774} ,{1908853474, 1753577499, 241786756, 1818028627, 261180613, 441233689, 1053722606, 2093470809, 494833471} ,{1100098615, 927200885, 1195301905, 1257810136, 1445970666, 5295458, 978700130, 1989006914, 105573152} ,{2060783462, 806547530, 1801018024, 878834595, 680972, 300021683, 2018132982, 387236416, 1583135144} } /* End of byte 9 */ ,/* Byte 10 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{1115622606, 1978542745, 616051321, 579961529, 1895225671, 84836280, 586358623, 696858558, 1436576811} ,{151580019, 1112607596, 161884554, 1192504207, 2062974756, 952180407, 580240688, 1848413425, 1952900969} ,{1620981068, 710698754, 761463283, 75099060, 1551062641, 1002348839, 303267007, 720574884, 1728358161} ,{759678910, 436202962, 1201666798, 788357464, 784936449, 1244057167, 1517866066, 901349435, 2080076882} ,{1079977865, 7266769, 231822328, 1079894138, 370046019, 166145428, 1032664887, 1470080704, 285014502} ,{1476440166, 210889257, 854172855, 1859687511, 388006817, 338380293, 2131604123, 1435748615, 1496107537} ,{1369850342, 1421392223, 896662151, 1224468068, 620331437, 1156692968, 1350813071, 1932799499, 1983961077} ,{357567338, 1171360660, 747095363, 756290193, 763937574, 831463727, 1207702335, 1944207111, 1264581091} ,{775390255, 1462871463, 187811404, 362836467, 332651994, 654613962, 173444882, 516879123, 473866848} ,{1014898841, 615827470, 776759122, 502574339, 1128557394, 367987058, 433215287, 1102568265, 1279529413} ,{1605231064, 1971282747, 638537751, 444802507, 1553980007, 1838948604, 424402469, 1262629284, 1541556487} ,{820532047, 1866156818, 1512537023, 870797251, 361428266, 183106231, 482082201, 809442663, 400245881} ,{1670234377, 1960237195, 150043193, 679559468, 1209434186, 1053532616, 1499293695, 1765419410, 1665279216} ,{1463440276, 1164497131, 1910620188, 1230917488, 1098058750, 222796305, 222028746, 1797452521, 576324550} ,{1743945991, 2008458073, 45172850, 1637040466, 890187606, 431709335, 1504687318, 895651540, 576709567} ,{1442124680, 1297067824, 2081839850, 1113800896, 1404161966, 792324186, 301287832, 149356696, 922390393} ,{922143195, 431308486, 887840701, 247253854, 480015806, 1719171103, 608253362, 249113542, 607574431} ,{1798558963, 1527741054, 126698358, 225473714, 673858118, 1220367964, 129066300, 180300509, 1577120581} ,{1833184250, 1022997499, 2117212902, 1598928832, 782931696, 1455124835, 1071048298, 1009313563, 751794496} ,{1141106355, 690628417, 1738585224, 1119724273, 1476549344, 1901853928, 1931929808, 1302542299, 913285357} ,{68068751, 1722379127, 503281758, 841913047, 87929490, 732187423, 569138707, 1394333776, 1317899586} ,{926924138, 1220159558, 1553643087, 1990791985, 1399824047, 680987745, 1852168288, 54042896, 951412304} ,{139149177, 923584350, 152589370, 1604734634, 1520728123, 552759591, 1140239519, 1098276620, 1136331306} ,{2094062270, 147290024, 1366306450, 317865143, 1315249260, 488110053, 443848655, 1773130849, 1661021104} ,{540054927, 166366321, 89240199, 1356217744, 2016505093, 728966373, 1229966339, 818355343, 2052203270} ,{1946791858, 1864716342, 471624000, 1277700420, 1910410683, 968590827, 884236487, 1719836926, 1952712853} ,{1693622788, 548835355, 1550093041, 510324829, 1702172004, 815060838, 1050073383, 464921692, 680455953} ,{934627241, 1542026898, 115061964, 401290651, 395706439, 1322281546, 296508517, 2122873770, 726442443} ,{1786447349, 1567925915, 1519690400, 923879158, 534170806, 1909036984, 1281024185, 1012391605, 716704656} ,{271111811, 1452427271, 617792436, 11759060, 62001389, 391170496, 51386033, 1389638319, 1615737442} ,{1701121303, 644444149, 2089287962, 1927993610, 1450595070, 371174240, 1959376978, 1027463303, 1413105461} ,{259303102, 148719470, 570626178, 1132840679, 1187597410, 272711749, 1441544707, 1379919308, 1563619111} ,{356506464, 931294393, 742299243, 1048969583, 1881254075, 1236013860, 309694392, 81583087, 456835624} ,{1347823559, 1863520331, 1431495774, 38911188, 826007633, 1284407903, 1304075555, 1193097479, 301049333} ,{1337230614, 1161496431, 657115545, 518576901, 788688954, 49220622, 854636692, 1507944247, 1012527744} ,{267743091, 1348974753, 1736413143, 592293023, 1364780740, 288897030, 561557359, 288373765, 1800720834} ,{322016920, 1607196099, 1771338872, 257482990, 1116990137, 1494490177, 926144763, 255454870, 2101322756} ,{1836871675, 1221501374, 44717493, 154234530, 1700240015, 41725376, 1748100529, 508766986, 18933017} ,{1170432103, 342562255, 1330435463, 434077055, 1279830178, 255033566, 1885418809, 740548113, 269019062} ,{1719509046, 1755965282, 143368536, 1787221104, 1666678645, 1798382210, 587676586, 78295746, 136036581} ,{941305224, 1228415013, 2124631486, 1547583459, 69042372, 1328454914, 1015427172, 333190490, 1026167524} ,{1896084146, 1155364248, 1947410895, 317451070, 2065560473, 1135979891, 583450292, 1401733656, 1145456964} ,{493028548, 1032464922, 1969102309, 330481130, 882307419, 1718898389, 186371867, 1016870253, 1922267201} ,{1404908039, 722535249, 1673142511, 1215768056, 1885919498, 123443253, 878318096, 478859993, 966699224} ,{1038377815, 2084957036, 1864229854, 10899898, 1044460445, 1184653889, 268484987, 668549429, 1776835786} ,{1170964318, 1154316418, 1644908794, 1803788788, 1249324437, 529730542, 1674251469, 36684768, 2095478227} ,{31978486, 1853236218, 1259669161, 1392374329, 714262439, 338805195, 604216431, 214530937, 317054064} ,{939530890, 1098686485, 1117009624, 502172982, 995480783, 1843592497, 149629703, 771925133, 2121171357} ,{1885130272, 1099133364, 1848250487, 1157526288, 1169795275, 1727827957, 921388155, 298169522, 1280184307} ,{1639683347, 1258366451, 233282562, 847451396, 661152176, 1436096556, 1448635685, 877299818, 139348875} ,{801770721, 228110820, 735873004, 459852815, 6907620, 54865250, 1790411990, 906950442, 2051706977} ,{1772493477, 904471684, 328248334, 689099203, 1543011679, 2109460067, 341556587, 1321173674, 436355799} ,{163152531, 1145316536, 800315970, 2081716764, 1435717936, 121845509, 1724037683, 1800426122, 1020317158} ,{2145876543, 1437485487, 1296670655, 48803041, 1843046276, 1008551244, 1373174449, 534559625, 45337672} ,{1944054762, 858547569, 1568783102, 1537815491, 8687636, 392876767, 35411863, 1847543886, 6292370} ,{1381420593, 1216991173, 2038363999, 1156839376, 1370698993, 2129358336, 863591852, 2143072416, 191126918} ,{94923200, 84410579, 2135551948, 166564759, 1875593550, 51187459, 2010262155, 1073585740, 1499838541} ,{1285850109, 1901214835, 611699982, 622013180, 1919164431, 270051291, 1698298036, 1745595785, 1845076818} ,{757102377, 102552614, 547033968, 1398725636, 581749169, 1662927882, 179757574, 691565391, 1520817288} ,{1914379236, 487869687, 824079150, 1820250725, 760262296, 1535823872, 1575030983, 1591112428, 1062366405} ,{873936607, 1958734364, 97915700, 1589009979, 1886188420, 256992181, 115342619, 1441265880, 1381745362} ,{224167644, 42349563, 2144266732, 1328140664, 1572609565, 393387617, 1684458519, 631206000, 1351687465} ,{811091423, 568118368, 358364107, 2001124696, 1682431668, 606358917, 1971499586, 1955344935, 461190029} ,{1579449245, 909182853, 55690973, 1295612330, 754803084, 893246529, 709422329, 2013682156, 1768001247} ,{1720706009, 31910836, 1698656272, 1488822599, 1900928727, 396031374, 915800197, 846169983, 1926891780} ,{499372402, 443494014, 2135628790, 746840415, 1882639121, 483462038, 1751668712, 1422246554, 740607733} ,{206974589, 1635974734, 526100768, 665580402, 1116294369, 382417622, 607002965, 990000276, 340297543} ,{1821623027, 503982490, 217533762, 2129977979, 741838856, 618033707, 752841241, 1973857727, 802607928} ,{151678199, 433214405, 737279372, 590734579, 1744919991, 272752853, 2037680244, 307043223, 1959577410} ,{851393118, 311728004, 630907460, 1503488830, 431537331, 1946475695, 1560253702, 1172148399, 2061394856} ,{1318341960, 942085324, 1162452016, 686792609, 1887121036, 480665638, 779470678, 199229507, 421526338} ,{1420886710, 71052633, 1423511525, 223674986, 433289308, 647484108, 2055885546, 148360139, 273989077} ,{1479603325, 1532955918, 483955395, 2090315129, 1701096945, 1551965666, 585012506, 382322199, 1459909993} ,{1545397516, 1981875536, 1321626107, 561807443, 603287603, 1011489676, 538107991, 1554585652, 1347672813} ,{828345582, 821303793, 705899285, 197874007, 1480800678, 746131204, 874250093, 1287750845, 1566958794} ,{257490169, 7528585, 1327669283, 1904387460, 521639513, 883774667, 1769967426, 1929143955, 1457051864} ,{530906104, 1456387897, 2095374903, 1344303514, 1204089606, 1700067490, 426609458, 980241839, 753630780} ,{1470976507, 389154296, 225620009, 1724092466, 64988395, 625931795, 1617156616, 728590921, 2064839402} ,{1619669481, 2120634051, 1833929716, 2103378281, 184980004, 1661123915, 715665434, 1208650285, 298823316} ,{419884952, 1251264873, 1243347151, 431881814, 1866990599, 245311681, 1264862245, 526050940, 1682872770} ,{1546619983, 2097527949, 2120341569, 1937005333, 309345790, 1878333502, 1677673208, 61329522, 502628822} ,{655277273, 1913578384, 173238620, 1861626965, 1968286726, 932612062, 61572763, 14715398, 1748387972} ,{1332829807, 690370390, 393250083, 819302645, 1068681583, 975499234, 1555063904, 704527008, 1681090589} ,{1723649555, 1276322140, 438922991, 13621944, 2037152468, 136173884, 820758861, 1981214172, 484734049} ,{933672374, 1197794196, 367777674, 1319769356, 1629087696, 226763771, 1469526360, 805435130, 273443577} ,{841806566, 626653036, 334756486, 1040379614, 1237549554, 976265832, 1281553633, 1987700213, 1937718850} ,{145820932, 1661852351, 2147354183, 890605719, 181120360, 751821566, 380978082, 972011755, 1451528981} ,{22852396, 2046145685, 1323576237, 718173636, 2019711520, 1608650617, 438425974, 2067963098, 1081176071} ,{2026113239, 2139232759, 941003531, 1443168292, 1882315761, 1322718943, 800174448, 2022100694, 530335424} ,{784538248, 1628961006, 2064986390, 1705595734, 1170706704, 65882431, 1075559898, 1114401405, 1317537124} ,{604522103, 1097586923, 905644032, 826649928, 1732418514, 1208126734, 1986038228, 990530007, 2137352288} ,{163621726, 923783438, 470854386, 1515245106, 140405941, 1510760928, 801081520, 1234866574, 1211441193} ,{521711829, 1937339834, 101883479, 139815728, 867078539, 573624688, 760108297, 1699138924, 701876645} ,{638767152, 114259548, 652702180, 1245229484, 1146907247, 1179181233, 1635853119, 565306710, 2022791722} ,{204606349, 34480233, 1058164842, 1315934289, 405772822, 929235652, 2021422552, 1896926584, 171345247} ,{495868311, 2104107863, 550085901, 1353660799, 1527567548, 964186313, 1608485062, 1005439392, 316670302} ,{1057728103, 1545122164, 259093828, 983831014, 1874374280, 1497039875, 1345742607, 1264440378, 1217071783} ,{2016450557, 737658419, 2070728720, 967976405, 2046325155, 1857261824, 1850183811, 1501528077, 1198667781} ,{1770377353, 1709151423, 978199447, 625822938, 1928514846, 337900073, 108315145, 1124783549, 353724598} ,{2014417356, 1816594190, 73728109, 1207850845, 1254859381, 682051386, 479255218, 825363154, 321737183} ,{1909114387, 424987994, 1669197738, 698302689, 956397302, 508417235, 954380794, 1867507964, 732266244} ,{576412311, 219927542, 1425640154, 940154247, 692914386, 1199299239, 898511238, 410926949, 1515457595} ,{255981135, 656245923, 774817965, 267641124, 836962697, 29781614, 120149306, 869833961, 639894993} ,{1442626197, 63076665, 548350804, 1161216354, 861055426, 1383518596, 1782106213, 1933343550, 1258127007} ,{144116608, 1814871471, 600673525, 1164167562, 484642972, 475940470, 1507836184, 1065921627, 863189679} ,{54510256, 1122912860, 628920450, 1337322685, 1574147549, 674337541, 500276493, 410606636, 1000938497} ,{646138325, 356472363, 1397813097, 1029213772, 79774947, 667194861, 1271669078, 369381230, 1024233340} ,{547167270, 534905118, 195847366, 1979663669, 1571517745, 1417138397, 1525210027, 49975789, 372910213} ,{206733469, 1835873626, 771974421, 322961460, 1631785153, 582400906, 1212379198, 1831364023, 1312930292} ,{1147595243, 249991232, 826741450, 436190674, 1432493013, 170455444, 1196578774, 1520626656, 470776954} ,{963334165, 39029485, 94137689, 1258405370, 1656552290, 238733439, 2050690450, 1614405536, 662013623} ,{1140643742, 1218403391, 963574537, 1480914423, 919450747, 1033926446, 1245945375, 507962630, 1815405650} ,{48770020, 1629787889, 362971306, 1931310393, 106269455, 1612718382, 13268237, 1604543963, 2014285262} ,{1464228294, 2039300166, 128038599, 1900722177, 420136034, 882575169, 785504931, 659427810, 1263078124} ,{344057837, 1000409157, 460169725, 510643480, 260048328, 557866367, 1016535773, 1550720330, 791038036} ,{707913174, 155718270, 1411410570, 1898341444, 980954978, 1411187212, 58501587, 482001518, 597506082} ,{1620130347, 1863626128, 905681255, 874598209, 157032335, 2048199783, 510933878, 481022987, 740293566} ,{820242424, 2068478811, 1028999721, 1920457775, 1791727640, 63407045, 1543678167, 2131789693, 293981971} ,{460566619, 1789059341, 1484333766, 1740617200, 318386908, 1520486842, 1166911736, 902701363, 324563978} ,{1864355730, 2096773990, 1979940075, 536386802, 2007045721, 370507661, 1957636576, 1569200918, 1243077035} ,{987367181, 5509380, 1825434016, 1011229761, 1418315637, 1914788696, 725761243, 1079335873, 288928805} ,{1647327177, 1797098175, 275691252, 1856571431, 730565470, 898411116, 1679234279, 1161465251, 1505168638} ,{1159376498, 979490874, 1793167982, 148361637, 1446749437, 358960735, 1130660813, 1148494723, 1366898831} ,{422518277, 505732092, 1419804573, 6424712, 904451782, 35728148, 118809533, 1360705746, 1072449865} ,{1959029835, 2061956739, 1943385840, 1564149752, 1026756846, 839425385, 38974220, 1550508775, 2094691510} ,{1666165114, 146030278, 226542697, 772997895, 45694936, 1066111041, 2108261662, 249854678, 1890981081} ,{327764024, 55035392, 667944601, 2011073768, 1468520511, 369814713, 1934373822, 1548791295, 389572646} ,{889601868, 1614838066, 1194056782, 416114758, 10879375, 14987224, 1618307983, 171829511, 1131855052} ,{1176735665, 1821681407, 811979138, 984519051, 792506286, 1946694836, 1848622863, 659623596, 132141298} ,{1364542423, 1842094370, 2045288075, 1129011506, 2073951336, 1686627069, 273852595, 496292186, 397010518} ,{243156371, 126697123, 1351247443, 1756031478, 115103809, 1222708399, 815925321, 1577831798, 1495597962} ,{190942247, 1362461681, 1584499177, 358633101, 1852525757, 2047251171, 2049077273, 1037320426, 2133708702} ,{276627501, 1234694793, 1542649613, 725113501, 446496957, 1245249350, 33070517, 391416273, 1705365632} ,{160043193, 2071572972, 243239920, 531138586, 891398727, 1263938578, 1733596700, 2071863388, 1025240000} ,{1248745368, 489670002, 335412578, 1666988852, 1149620277, 911602582, 231100039, 1478729656, 202568168} ,{80714118, 142616504, 707060412, 386642566, 999827812, 2047101845, 527445678, 2134771861, 360031843} ,{163551188, 1238355367, 761362153, 960197483, 541124498, 2040105910, 1121066908, 1426817524, 471235094} ,{981239367, 1165002384, 461579209, 1484657965, 1570732762, 1573187882, 125711815, 903787918, 1039658626} ,{89733954, 123299567, 1969796909, 1905741619, 1270193991, 1595131236, 982004247, 166175762, 1426780839} ,{987729496, 1013948018, 1997181327, 1116787215, 586344194, 238745965, 303007504, 1440231318, 2014998515} ,{1802940439, 1660652007, 1905320836, 1520515011, 1238077279, 922248671, 616166160, 1304856855, 244506468} ,{32535900, 1953538696, 1203837758, 507397744, 898721185, 1942151131, 1705200868, 769570899, 521997188} ,{44767772, 1136562942, 1107145374, 125786588, 267806233, 261900763, 1337602654, 1160864212, 1149993947} ,{82864898, 1028141737, 1987569020, 1056123595, 930521038, 784390658, 1498283925, 1068595133, 1952377112} ,{1522662843, 341850962, 1013339296, 259524274, 1197886144, 131041514, 323229672, 1434884077, 373554170} ,{546268464, 997632984, 398233311, 802567360, 2127244423, 1235935751, 1146771804, 830693958, 1621753173} ,{1657791963, 2010198565, 630315656, 1837999933, 358909430, 1752093925, 553953694, 999871046, 653199658} ,{340392989, 1419581027, 1167007387, 448017688, 2062595132, 119327475, 588371859, 134713355, 1478574546} ,{138636791, 962533551, 284109243, 337608659, 2111756342, 2095126680, 1698298552, 1031694354, 1106418224} ,{12184784, 488517983, 570127615, 289486866, 1679382424, 913750038, 35566737, 2102399608, 1802536020} ,{771767823, 925025364, 554603625, 1500344637, 1639428642, 977029173, 1608507785, 1474237824, 1552942133} ,{549959742, 92139596, 2028115079, 286871444, 1139656501, 114402051, 1021706720, 2076757739, 305417110} ,{1550537829, 1298226421, 1614974388, 1106517541, 1251138718, 1721905822, 1227055715, 140177129, 20960717} ,{733561477, 1712181235, 1800865954, 2030136277, 338121864, 1133312171, 437839053, 1125273332, 1232766386} ,{1905256815, 1724677537, 821807800, 776141243, 1912253717, 520966740, 639469441, 659701121, 1788732507} ,{282438130, 951347118, 591828844, 1463923887, 2037241234, 1926235596, 1635633303, 1213574778, 1397741697} ,{1816104121, 1075397972, 1839187854, 466720346, 1559775044, 547135712, 788696896, 1908663350, 1965484695} ,{840083507, 96315009, 1522105073, 891540178, 2040138114, 553905058, 1113489938, 287070335, 1402792585} ,{1444904840, 1512757937, 2140010749, 337647430, 1001788013, 1007041243, 2109668861, 1854121158, 138097243} ,{218069171, 1421594957, 1854906254, 1056714891, 604052252, 378903106, 219051614, 335801732, 1775011834} ,{1832938990, 109712779, 1770032502, 342347, 1607139057, 541925956, 1150861688, 456534215, 910481170} ,{1077201134, 2024128056, 1413104182, 1466617554, 1347564167, 1002862565, 1598973196, 477480206, 1824691189} ,{348053798, 224796151, 293615104, 1163481009, 184385846, 2035712604, 10612422, 1374515026, 347550681} ,{1896210715, 1326842730, 1781559021, 892399537, 1760890739, 126298068, 448864509, 1701664600, 1165308129} ,{1939846194, 416407866, 530262278, 335025240, 814375482, 1240700251, 1277133196, 1606015539, 144895861} ,{260342029, 379665630, 784727972, 803788969, 1590773297, 1254913561, 452882408, 1504273585, 1601148454} ,{1278569570, 1046463331, 992378703, 559895436, 499040251, 1672705805, 943802583, 551687079, 198575591} ,{712106462, 1900153302, 1599340648, 1387554985, 1758385230, 1865664405, 1483229018, 653676464, 1959095197} ,{412027077, 1998140932, 2038733800, 701963515, 693693843, 1531808908, 214110494, 2130263395, 1558714390} ,{341277336, 1773546478, 674738364, 2061390153, 736423839, 1224602681, 353501608, 649174402, 723961163} ,{1122983961, 1980662834, 817757868, 1013688345, 880122508, 1694992630, 1298252271, 1740416056, 1988963806} ,{1114367025, 1240747636, 492352899, 2093198960, 2091879489, 1387590089, 973064009, 571510551, 925483155} ,{1175322773, 1035702455, 197160247, 1827652401, 268784348, 2020203855, 1100701633, 690787553, 1422873075} ,{766351858, 1541521018, 865540375, 72750882, 1139039545, 1058549503, 817170596, 1504728091, 1793500719} ,{1804813249, 1911265205, 917515280, 313159939, 1462398200, 98464562, 1097801048, 787923031, 220257498} ,{185377792, 946649973, 1737587161, 2024943741, 1299711694, 22774287, 333751790, 834388791, 41307976} ,{397189658, 1819657163, 75928634, 2121949548, 793305578, 1465892418, 1495601570, 524747175, 72773155} ,{1997064142, 900588397, 762018234, 1250579723, 373656114, 416364312, 1904780913, 1979770774, 1183974896} ,{593388561, 880910186, 740006676, 523712564, 61404018, 780598372, 1755287434, 1412600515, 1177606573} ,{1243076446, 879185585, 1605017025, 92164323, 1258980101, 1088353923, 1546215699, 1591098293, 2097960069} ,{232131122, 1807371954, 1934503388, 252613919, 871882912, 1940874426, 1899442433, 983673438, 312848844} ,{203817725, 1698115598, 1295651812, 404733665, 1939295853, 16166659, 1182619299, 2030926375, 691600710} ,{1095723731, 1556463305, 819889564, 1113967557, 1166010755, 206592358, 532833874, 283230607, 1818499319} ,{1278292636, 13971865, 173584524, 1725982835, 1926283157, 1284556360, 1871473445, 1607838041, 1470914025} ,{102518441, 178309629, 79677151, 1477607793, 1395290506, 249802357, 1817607426, 1485690288, 1734292364} ,{2009330786, 138469177, 340759465, 428422718, 255216940, 79448496, 2030939409, 683961566, 660075486} ,{1678009980, 1279506472, 2091190058, 293086069, 1340909336, 692202979, 1741544833, 1079409748, 1822109383} ,{571931886, 790111488, 1052389013, 1154224424, 1742408664, 1234180422, 835174230, 1935655267, 1816548597} ,{177544502, 1851228128, 1017892680, 1379299315, 1151714330, 2003835917, 2141911209, 1595998033, 1988979389} ,{1533537005, 1167814356, 222344053, 1588915770, 1963691803, 1167947316, 1531549410, 2126615300, 1219376448} ,{1859170717, 1989166438, 1939412972, 1947651433, 2112657142, 1016334140, 1583696023, 1715480372, 653245553} ,{1511765931, 1776302319, 545218278, 669446942, 981922422, 280816602, 661680832, 1726348839, 270326146} ,{1272929122, 329005756, 464952629, 1052393446, 82204533, 98228928, 1289565963, 923167906, 403340860} ,{2062939520, 1516647048, 1123858748, 651247204, 151812040, 794618216, 416245643, 827683776, 1580109636} ,{1884658517, 632506609, 1795105702, 818437073, 493213182, 1798237228, 2024716131, 1290902433, 1437261701} ,{286490285, 1760518919, 787651810, 1786953163, 2026458494, 49634584, 1199776435, 830160499, 1628528137} ,{749746560, 1909788773, 174421604, 190538826, 1425486508, 1647377477, 1423200587, 444037897, 947950697} ,{517463045, 831530797, 2110074057, 1308071601, 1229441494, 1187289643, 2016645229, 1568613412, 1968916988} ,{1024534555, 824116955, 2095459470, 1550207614, 1204834010, 2076142596, 939312832, 362454664, 1507471607} ,{1487520632, 362149663, 2102321907, 1246823760, 1063334418, 1130007630, 633077413, 273627389, 1933932473} ,{1115777766, 448041557, 626849556, 2110915188, 1287059109, 701764433, 1777096375, 952060545, 1490708830} ,{1900271690, 1445799995, 848827235, 1255733047, 1058469493, 1854446727, 951390157, 600623945, 1385122024} ,{760466954, 2055913628, 646385187, 582100360, 1914142333, 1145505100, 245458402, 710938284, 2094810663} ,{274165849, 1605200162, 1231750586, 1030348217, 1651725097, 328126109, 2083274089, 1267569927, 1959694899} ,{100610353, 298086438, 1792260128, 1214829524, 1515861137, 1658737185, 1748573025, 1139699877, 794843633} ,{2121638824, 709721394, 1133760478, 1241043347, 1935472060, 396644535, 458063979, 973346772, 735807231} ,{1992824777, 50443952, 785277964, 491045862, 678347562, 1178316380, 372867231, 1541658337, 1964128244} ,{549465528, 1324182423, 1445285205, 1388439814, 1462869755, 1990862947, 1500102450, 1937145780, 784868732} ,{1499967310, 1013885506, 1846492900, 374330608, 1136447204, 1562638771, 333201689, 1224077658, 493271659} ,{88446787, 654635818, 399880228, 479202066, 483446946, 1375298719, 1958635141, 2048537596, 1484445705} ,{1921820872, 1313369631, 1643340652, 714157672, 645510970, 1929222514, 323867678, 386055620, 510391503} ,{1569025389, 1209073784, 352359465, 1160442175, 1321371561, 1626282539, 1823270041, 629671843, 1423108963} ,{2026158758, 772226556, 1377419250, 1228708449, 1813098425, 1995806273, 2074751425, 1039416362, 1664051063} ,{511227281, 1514809527, 651192479, 621884217, 1817153263, 1443254161, 253427705, 1070055432, 583640403} ,{1012398261, 1755309848, 1153189349, 1976258963, 536815837, 1508344639, 517894543, 2117118420, 1943308037} ,{596893637, 1756165695, 1903977777, 703804397, 1332035405, 1225130375, 938311234, 1878634018, 459765134} ,{1109012173, 143558188, 1906449633, 2063248404, 1057028171, 1151268298, 1288757364, 2041680056, 2060427069} ,{595916967, 1312497388, 1674516186, 1031290583, 354010913, 1855545726, 1669320276, 17531757, 25752251} ,{395474375, 49247700, 1263985950, 1339891414, 1252456359, 1521444404, 1160977138, 718801051, 34508350} ,{429426897, 1107807577, 1357011262, 1150929879, 51420477, 635385616, 705129567, 1655753807, 223525921} ,{1870965032, 250209369, 2012137451, 548395232, 1677337746, 883861796, 1273289093, 1498608420, 1152824799} ,{1910740483, 803378452, 812273707, 1424306854, 1497669463, 456741830, 1726155890, 1893894116, 405309793} ,{481948419, 1735822947, 806739071, 2037132684, 847865682, 1459766818, 81072320, 2008757372, 1433290790} ,{731834790, 2039299469, 1238364359, 1191836254, 1708180278, 709415469, 1956436586, 1151328543, 1627337835} ,{984168298, 980319853, 978650835, 1482919141, 442245949, 421112584, 1260758160, 1041653785, 614538036} ,{1821332254, 493176687, 280171187, 1601561433, 152927328, 1358948951, 985655060, 1256582162, 2075355583} ,{1731879317, 993796697, 1217534272, 308475081, 1321182924, 65516389, 13487530, 1377496269, 1805426871} ,{1867049024, 517604640, 14989477, 2012253021, 575975896, 293634211, 1093333509, 448470287, 1170630572} ,{338146380, 811031324, 1188910006, 452879004, 329458479, 692680091, 2127587980, 1855706858, 1612696361} ,{2012760707, 1220935057, 2050221669, 345764409, 799131253, 1268088086, 103152286, 953906203, 787386629} ,{1623267747, 1700808888, 449869672, 1344993217, 397806278, 1884284453, 1186234484, 1359307877, 1747409621} ,{1077262645, 1866842312, 1026954680, 110303154, 343667633, 2026671298, 102871361, 181672360, 280082307} ,{414759672, 556049480, 293498540, 127015232, 1712454395, 1699373783, 20840263, 441843687, 161944734} ,{2075251923, 1705470122, 2142749719, 685776374, 1032619188, 1301254496, 1257813916, 1963528421, 452758423} ,{1882414248, 686704358, 1547797076, 1531423983, 1346027543, 886852830, 163656762, 1688892481, 862289357} ,{160900642, 155255099, 1603455380, 1608172054, 678449739, 313688239, 1386290314, 478725350, 1584637690} ,{1487360936, 440482681, 1629717709, 1817886501, 308811788, 1637893271, 90087423, 2108478450, 644586154} ,{424528339, 813759685, 663284638, 2006452686, 1491509356, 557800584, 897551163, 2014432326, 1430703136} ,{1179883528, 964094313, 111044685, 2133899138, 1366877270, 1410128537, 1442860797, 1306775312, 50512452} ,{1424975290, 1603981378, 936424296, 1567927018, 1262794435, 31498203, 1247522472, 272300694, 523244098} ,{841341110, 756553751, 442857375, 309667938, 1700110625, 1535826186, 549563304, 546817511, 542356855} ,{707200829, 1770424361, 12036385, 2036326168, 1002028686, 341788065, 429281474, 1241832558, 352638926} ,{737115088, 1512608027, 1706194688, 157513316, 780307142, 2113611205, 1503301678, 1564093011, 1413990594} ,{691967740, 1108243686, 1318766332, 1825679850, 1400048168, 866054977, 693524543, 1905068807, 1484893362} ,{439434699, 981714438, 1123343454, 48924456, 1762163573, 720538111, 2052271121, 1585487393, 455623709} ,{1719340601, 394216615, 396759482, 1262421452, 1758001182, 2047305234, 1183441203, 1170339578, 1133633965} ,{1657704297, 1519841087, 447956352, 555279071, 2031569708, 617522036, 1949810557, 1698206476, 1007700712} ,{1394654741, 190454398, 1543081188, 2034809177, 1568716114, 1957183068, 1717060573, 682218700, 1329939569} ,{377633373, 1358004561, 16805736, 374110707, 1345739906, 474213604, 280327704, 705492740, 1098817447} ,{1775703557, 1677381430, 1576185713, 135246499, 1926398768, 733762638, 1104620425, 285500262, 2041222041} ,{1505165824, 167884955, 1871268983, 771124469, 1346205699, 70573646, 343329654, 6713996, 914359071} ,{721468982, 1782869723, 2081255132, 132193438, 2100069381, 1745103944, 1899965481, 1722063349, 316231203} ,{650154027, 529909041, 1576487469, 1682198877, 1505900929, 1053974408, 2066589885, 1495763329, 1948751405} ,{456448660, 2113339297, 1642483726, 589032793, 212298879, 1953582266, 1602975027, 779610810, 1013176919} ,{431312435, 667011605, 413934499, 1260379269, 613117992, 2126664988, 528595212, 216377486, 1181630966} } /* End of byte 10 */ ,/* Byte 11 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{1398966622, 1157247437, 916238461, 182928118, 1809268605, 882357484, 1036910071, 1001125599, 1351500463} ,{525501314, 1126053257, 1649640023, 538705899, 497139502, 297882703, 1867503624, 314576360, 387037776} ,{493227047, 1039551408, 1976422311, 2101408042, 1633135117, 1618541218, 1195184520, 1683093460, 1044052406} ,{2053804527, 415076613, 1989231709, 622589464, 1635041103, 447864305, 221866467, 1296555477, 859124139} ,{1226114955, 1879448501, 1827132510, 90592997, 599958262, 913437355, 970558524, 180394243, 858942824} ,{650681148, 1038466928, 1545372726, 1872646231, 1254857590, 1669721985, 1497840943, 1670524448, 1529167492} ,{2063004186, 336500128, 1084298421, 1932134653, 217347117, 258693757, 671633059, 1589688900, 1808439649} ,{1895731585, 1867327307, 2069915815, 1512868696, 1404866392, 1740692164, 1890170287, 1602928372, 2132331650} ,{353702917, 1636569458, 1999775804, 1236663178, 589528637, 1190650890, 509306169, 2024773660, 2028341503} ,{300551735, 1362211986, 1235079945, 1826801450, 1232815891, 720147967, 1734647433, 468055572, 210002982} ,{1766323542, 1703660757, 714693843, 2055191616, 855316045, 1300195882, 474373649, 708044833, 2003364724} ,{803448149, 927508544, 49655274, 1783395331, 1998593708, 1612669333, 881085378, 1260267084, 1986996958} ,{1776753597, 1244028694, 457382947, 1196935646, 363579328, 300042388, 1211361570, 1846700920, 1864717653} ,{1512392443, 1110028809, 43216871, 1945417866, 1013642605, 902814001, 1982089800, 1251686436, 146055623} ,{1445629082, 1700361515, 1289428643, 2102772721, 484607451, 1409139701, 903603930, 712769522, 449886483} ,{1103145403, 876725196, 1237695176, 1764973820, 590485398, 812746852, 94240319, 1624615526, 666115058} ,{935174504, 693368170, 322338346, 1752551188, 1611662084, 795553823, 366017055, 1087574307, 1982630353} ,{1220464825, 763320766, 732784283, 1896441275, 1415687376, 873028989, 1393591778, 1193935788, 1856803309} ,{1086212718, 525164634, 1058970802, 1134627238, 1214114044, 2077725465, 868637801, 937978190, 1959563501} ,{350010232, 1344503653, 1122547667, 724702066, 317915906, 363006613, 243876076, 209848710, 1318210681} ,{1633007739, 1552358037, 364535782, 1880176872, 1791760903, 873431146, 703079975, 1097227969, 226615108} ,{1572168048, 591933441, 1126961958, 1965234018, 1929670103, 900668083, 1133850676, 1138889552, 672065301} ,{604237682, 796882047, 606251523, 1508551869, 467589029, 800147223, 648322542, 1496387344, 373350188} ,{710593110, 409721883, 209617143, 1799717565, 620470793, 1234756118, 1710187784, 1630648570, 1123485617} ,{1023684369, 1405643873, 875128499, 1540151227, 1456382037, 2013721985, 707196716, 863135647, 1905903837} ,{934200240, 1254957147, 352087696, 1375644216, 579195974, 927987063, 349414771, 286981405, 1015623394} ,{1316860033, 1462526538, 479005768, 1742784992, 1375278607, 250006250, 1465245404, 1659445830, 1868214390} ,{1654454245, 1943344786, 828449314, 765684550, 2004057438, 1901156712, 1451534653, 1653511792, 566334852} ,{1880166422, 1979612943, 1781322291, 1501424526, 568938724, 1536303060, 1243616220, 1066158849, 1162150768} ,{553817157, 1728223845, 1591118988, 1685103103, 618489551, 1212265293, 415206859, 1432406520, 117147269} ,{1945326466, 533146905, 1791765405, 1517300882, 378356281, 496527736, 1188483968, 1960324952, 980732736} ,{881265815, 574418292, 1592380547, 1089008700, 475651698, 1876587992, 76581291, 95837607, 1193727812} ,{1211260224, 1769730150, 987136614, 2060036996, 743654488, 1775285801, 473160766, 391192175, 69866315} ,{1797616851, 318387576, 155609251, 199975957, 789739457, 870337684, 280487791, 1282921236, 2058703942} ,{2046050227, 2093091409, 648507465, 1305788038, 298935178, 2128593106, 547744972, 255083839, 102281652} ,{150514484, 933027114, 508943220, 1808840634, 1963306924, 1309843774, 1553736452, 406039417, 1499070581} ,{80611167, 1215338990, 1899410516, 1638862879, 202454882, 1938744358, 1006101626, 1930972404, 2103594917} ,{2073933312, 830946976, 701551158, 1353273841, 1532352338, 1393560505, 750675893, 338918002, 1628479402} ,{1590221295, 1128987600, 884564734, 1273403442, 278139764, 35539235, 335255090, 82322337, 2074688046} ,{1646297184, 3170881, 677273052, 285901470, 1285241028, 2003947243, 298014064, 611093277, 749235834} ,{2061876278, 397752882, 994261848, 1442725166, 1870965041, 1608831467, 1182790111, 62380986, 345893143} ,{1144631239, 1410710179, 586451439, 820401755, 1741051621, 902715063, 592623714, 2116222008, 1859367377} ,{1314894197, 150650687, 875768148, 1222620245, 1354377182, 1515798818, 130490197, 854826129, 2021555678} ,{249954181, 1961716985, 346432504, 1493174693, 6468776, 1981607204, 82366712, 605352885, 331466178} ,{1178215816, 1442452572, 470358948, 404027829, 919930000, 600580307, 1011775117, 1875267638, 48837962} ,{923457391, 1748741401, 208473305, 1457119565, 337368406, 459163673, 1443630390, 2025590752, 916901831} ,{238956149, 1067870626, 2110891226, 539956745, 422368628, 1950603615, 891309049, 1194366219, 2044449916} ,{237321276, 51718718, 716493265, 1775987367, 521878257, 1686643189, 1092535790, 319858017, 87805404} ,{1898798259, 1835481426, 1577814431, 439796891, 801798149, 1063527853, 453960406, 925155843, 800116151} ,{1033770189, 232965581, 1036827019, 951526395, 786405488, 837513656, 314200510, 1915298040, 116051174} ,{1351516112, 1834227575, 307253181, 1258920240, 612003897, 502203477, 1956843064, 1003521897, 2086132333} ,{1316584083, 1096099166, 107645669, 385932763, 717963673, 957417284, 202046349, 1281584182, 484157574} ,{1527381230, 1885301061, 2021681525, 2036597986, 1770456602, 276975807, 528875622, 2066239883, 1369395190} ,{779288329, 171399346, 805403216, 1257123997, 392992180, 865016072, 1791137779, 522851592, 1498484311} ,{931873234, 1644821283, 1418500644, 600622377, 196700765, 674420557, 860330902, 514254926, 660956635} ,{1826899106, 326344627, 481403760, 1089419963, 609544591, 331175034, 1868731051, 1616480101, 151150738} ,{315308470, 952397108, 2014400664, 1990710692, 671880614, 1915780967, 2095496602, 1472035246, 800913372} ,{620046641, 1737302677, 1984312927, 986410687, 2132131390, 1627660535, 877760828, 1538187856, 1164268911} ,{366175867, 951040144, 874255873, 588101475, 1267246635, 1574497867, 1074800896, 1929908079, 484404625} ,{1977908008, 1014461719, 1686246067, 2064669103, 1688443171, 1181187375, 735959574, 1162838158, 1066701451} ,{1479746864, 789710107, 1421892090, 513457306, 1437483384, 701295434, 532193594, 1830428101, 904207731} ,{5646606, 1161699467, 1961355606, 256464513, 1460009999, 1803968249, 148850713, 1600231134, 255684008} ,{872026952, 2059839395, 1350474237, 1622004364, 889421903, 895636503, 1359124008, 2005304420, 187565356} ,{1290710085, 746644977, 476082424, 226668047, 229293297, 831767359, 77589666, 843756758, 1866713714} ,{191936953, 637490372, 1037147976, 483966510, 1616649924, 1966176809, 1315353734, 1526831256, 652762255} ,{784768, 887625370, 391938724, 561722228, 1339358989, 1746344160, 1928201915, 1497464293, 63627210} ,{1385007571, 104960698, 2045726066, 380149916, 1890864345, 801324054, 1121017553, 1383658143, 1728706542} ,{1135397719, 2042057729, 2001441130, 737968413, 35965892, 1537294345, 2000259021, 145251938, 1917862996} ,{722286047, 1517234701, 1655010889, 435499663, 27148972, 2016053861, 1056766220, 65631486, 648423046} ,{800593399, 1704628110, 486994437, 1646677009, 588243494, 1317046238, 1958763536, 531613042, 616927294} ,{279126711, 808701803, 232647509, 831823426, 1533243993, 1892123460, 1717887945, 767433557, 942519701} ,{389056541, 1655211056, 1618029676, 1947512444, 518572996, 1626415449, 2015151910, 168467452, 1748099576} ,{826234399, 389486318, 1677112894, 2038078681, 1605912724, 2140413743, 1250090631, 634179319, 1276575780} ,{1632698028, 4179484, 269352576, 2055000285, 881121608, 291728633, 1563111925, 2044782023, 487570447} ,{388083739, 1429933510, 1732831862, 851772701, 258816472, 1186730019, 1210102173, 1824101311, 801497019} ,{369934423, 1383781045, 1880965013, 225583381, 1592311702, 1576426544, 1758712452, 898524211, 311460587} ,{207907950, 795734125, 1441877758, 465209994, 181098168, 1796708166, 1457274994, 1458402299, 1710766828} ,{849543287, 1215429045, 1860029455, 687438150, 73868729, 1669708171, 309263954, 2082434319, 1385126182} ,{380393887, 278358008, 1436743469, 1801092715, 804332240, 896839424, 1552346400, 1257771475, 1974724959} ,{302626196, 382875442, 1095874096, 1573891303, 220339406, 1994943191, 1578759985, 1558196220, 1822454376} ,{1646446487, 692924404, 701688456, 1100965572, 1195125918, 868287410, 1471529686, 1230175276, 335067842} ,{978640786, 981542559, 1508789559, 749214057, 1491109287, 1283263378, 844899012, 1741989591, 2062528936} ,{487521159, 287171863, 1906893312, 1194735938, 1500061430, 9165275, 1366814554, 72105750, 401082594} ,{1073474840, 1927559542, 2030890439, 264122121, 1577205144, 1551843348, 199255079, 87008661, 1654126295} ,{1341867453, 1189322900, 2060320144, 2044937683, 272617850, 2115533662, 1534631727, 111952208, 1092428036} ,{1515649155, 1195600715, 1655659861, 586405996, 407386576, 128252599, 1503397269, 1026584593, 1443946339} ,{1531393699, 1965827206, 903716797, 1713503832, 1531433103, 1322465229, 1407221608, 2079969728, 1662437157} ,{1100482959, 953180950, 846621591, 1259118780, 1764673701, 460637732, 1973888573, 1964122150, 3489125} ,{644452889, 1835123183, 245411025, 602321527, 45951756, 428448666, 739699086, 987671941, 1955124406} ,{20871227, 1799550471, 1656573689, 199586595, 53582163, 396381171, 551472638, 1939306454, 878063457} ,{1284088338, 1696296376, 412212447, 1775819728, 463617907, 1032117093, 1446835835, 732556274, 1710461764} ,{2097983531, 1520590171, 1530168582, 2144810923, 1582558553, 1967411941, 948948585, 202066183, 1833957936} ,{739783402, 671048317, 534006165, 7800097, 185436254, 626872491, 1495716799, 1524408682, 2014125227} ,{533644009, 467947796, 149860343, 1995241936, 786508843, 1247540034, 1216459884, 1998977706, 731111740} ,{390827225, 1331639005, 1325705763, 836074675, 806283665, 1731720388, 1578597086, 927810789, 1447917530} ,{1578191690, 367895804, 438901722, 233173001, 1531747770, 889270536, 1845643952, 875687806, 1869502132} ,{838185586, 2009141490, 982334759, 949263284, 1806552469, 212537982, 1122694830, 1630061917, 913908251} ,{1303712982, 1642705585, 1125284460, 734189632, 770643948, 1401154406, 1279125512, 1574985891, 1400514892} ,{283142805, 1938140395, 759461633, 45607138, 1995849163, 228427678, 464763581, 2137797714, 603374869} ,{1548874425, 1081375161, 1781755827, 337231233, 1729271761, 1076140024, 1834603548, 554113450, 461590230} ,{965561883, 1921023010, 629865647, 1520721480, 816437220, 1905198898, 1895781315, 320327108, 1563316191} ,{28935517, 974071289, 37707730, 2029279155, 233041268, 641821311, 994186277, 1359191411, 938440280} ,{1400257877, 386690611, 1408185414, 478546333, 145230459, 1721961454, 161253811, 1603223505, 120973144} ,{9613938, 444958614, 648131738, 780206330, 1325516704, 226845371, 1749851864, 597254907, 1868096763} ,{1558768028, 510958594, 170049698, 1889666137, 943273394, 824383243, 203638110, 744650975, 145774641} ,{749949171, 1413440725, 1478648567, 1753106910, 1239698577, 184590874, 769796667, 87813706, 1853208874} ,{873361886, 1634629031, 1052409966, 293082117, 2117670097, 1973197465, 898661765, 515421411, 970904962} ,{383537470, 2130801820, 1209917876, 1808721534, 1320668354, 922821882, 1101678582, 1637882395, 210538604} ,{1039273898, 1053143454, 114250876, 1433875592, 1805917767, 474649225, 484994559, 1156753314, 756809013} ,{1351134457, 896570522, 2077348590, 1405656529, 352326440, 101550915, 1504934858, 664180964, 549359832} ,{396451192, 2078577712, 673268637, 744730663, 1875155134, 1080826336, 2012966337, 1865553545, 1759082305} ,{1051263218, 1235079520, 597513880, 9430000, 423100072, 652389029, 1335545361, 1796846953, 1190068009} ,{958843346, 1289040609, 1350757214, 1083521370, 687083312, 1597690532, 2080055716, 2073488787, 712981513} ,{1964651803, 1561787912, 398144876, 557420958, 1297921561, 1196282328, 838439520, 263967126, 1849778161} ,{88744084, 1710781196, 215280907, 739504014, 864450253, 2109217496, 1195041701, 428613601, 821809675} ,{107532094, 1892664662, 1462597834, 567911525, 1219636678, 1210783335, 501952755, 2002840796, 89145670} ,{1326033436, 1246307966, 411367957, 667641698, 2068940898, 352699534, 1791161673, 1436599483, 1888253626} ,{1987819942, 2088045840, 808033482, 1723578164, 10099047, 1607057225, 782434365, 1986597048, 173783539} ,{992685984, 983487522, 1607004178, 2080761883, 2009171717, 1065544157, 911941176, 1332085742, 1757690072} ,{1788385200, 752472939, 749461243, 2107730903, 934659263, 126538119, 597798719, 1791127169, 1274132395} ,{1202770716, 314792571, 66214205, 1386639545, 2093858130, 1611809738, 1434816079, 777079735, 1285009311} ,{236235983, 69868183, 1483971075, 220770660, 261389106, 309308136, 946216498, 1822330168, 482567006} ,{1252037449, 2039225569, 2013386770, 1160797436, 1652502349, 419896456, 1436926281, 765247420, 1384665752} ,{559071297, 1264820636, 1645922792, 743623316, 2103325467, 747023135, 847593783, 769088039, 1726770471} ,{676878856, 511842652, 2139518390, 645365874, 2053603049, 1992921970, 368253877, 194354470, 911837161} ,{932328291, 326508576, 881653155, 1596653325, 520587264, 1181309680, 468194767, 36920786, 1151910083} ,{24457320, 446371652, 89173169, 950930200, 1771783645, 437811590, 1009680936, 168045143, 2035090392} ,{164039459, 783183683, 1532654219, 1119463022, 622229906, 618395690, 242473904, 1464093885, 646667958} ,{412713245, 1210139988, 238439355, 1061814555, 1847752582, 1602561264, 107039642, 1239099045, 1950938828} ,{1368313186, 1723698050, 172475083, 1378365823, 1055339874, 1889033529, 477680989, 30061471, 78450718} ,{1407761424, 1351323152, 1986641438, 2132546091, 1956780743, 1288103383, 1857927801, 1589645543, 1722527350} ,{1978818034, 1013793500, 64243355, 354544229, 629073325, 1824445741, 1680545175, 1376966153, 1542999542} ,{536280389, 1101586710, 2080902842, 1808305416, 293175122, 1880256215, 885942255, 961354715, 1567313707} ,{1375226062, 887637307, 833013759, 996299239, 117669533, 621056550, 1689385790, 1478756036, 1318091838} ,{27072304, 1271849690, 1429860353, 1172723656, 1204757870, 1691871113, 86086187, 1894212767, 1293402678} ,{468976026, 1394270241, 2143359724, 945849613, 936572377, 1874373726, 2131574014, 1703037261, 447883518} ,{172520270, 855491355, 1189813652, 1964428641, 1413578986, 1868851084, 965212502, 1841352630, 1842847389} ,{1181644909, 825392281, 2093980163, 750596481, 319354269, 1378184204, 2041112421, 143581316, 1987087726} ,{2085350436, 1595044029, 8912517, 1185191263, 1557174120, 435674647, 608045667, 1723994556, 524279296} ,{271061812, 232983259, 806857674, 1758694132, 1144695020, 1426601813, 1703411410, 88758315, 576758698} ,{1247512532, 2021692240, 999832613, 252416836, 1881593123, 726069395, 208837413, 1575188112, 471283825} ,{942301406, 296686383, 25721265, 686521775, 2120949355, 611125985, 348698082, 779225811, 881852680} ,{1356583800, 445029220, 1113476523, 400142121, 681049849, 2117708537, 50155988, 1241580984, 675993234} ,{42812365, 142013956, 2060362139, 405074473, 577042943, 663900540, 1827996950, 839017364, 712883595} ,{617926067, 1155725249, 1861714877, 360111199, 101719588, 1691070402, 1484588601, 2095363018, 1301077814} ,{199340948, 1557572766, 1595724788, 1446086899, 91043777, 199313246, 22726599, 1330758407, 376897611} ,{2105779642, 309295565, 982619772, 1693347530, 499631027, 1934504702, 520291491, 544877867, 1060543050} ,{329738186, 547447549, 312672196, 917060948, 173820334, 1076284278, 1868707981, 370387337, 1869545866} ,{22370610, 924919242, 1305035442, 1383698402, 594754831, 1990831352, 1896734657, 397718725, 992424189} ,{606965924, 947586090, 1589130488, 1943099727, 1595611000, 1164644746, 1825749921, 1411461437, 779476950} ,{343975177, 1961593522, 748711625, 178738513, 860246367, 1519079663, 539156290, 1063775635, 2098537630} ,{1167172959, 1049708067, 2063374046, 209866104, 1394181480, 533823349, 695419868, 825462609, 2071637208} ,{2019269694, 2014322171, 1285689615, 1043783283, 2138205362, 663332684, 1483019905, 1061597228, 478156144} ,{696263274, 283716399, 151531196, 942468966, 1001006519, 1543262806, 255634308, 1711990417, 79687326} ,{181774090, 1215071315, 467692519, 1054104184, 2000343845, 77708560, 440494523, 1222053648, 284134880} ,{1969335059, 906078307, 427856956, 475264194, 1774737732, 109436854, 1892792610, 886528604, 1893949450} ,{1335450325, 372635843, 1978600630, 1907224879, 1556408677, 442099141, 1931240113, 479671563, 1710646177} ,{498702562, 1828991973, 284107074, 698720885, 1612941585, 1869194441, 1884746849, 1005427129, 79926084} ,{1826563951, 466642607, 1346552446, 2036510613, 828939995, 1008461230, 2067332839, 1205569937, 84867970} ,{2026585161, 1176102193, 624589227, 2106443992, 1556761331, 574171569, 1389996819, 1512570923, 1349484055} ,{1972038738, 2128004701, 212679751, 568902647, 1753254486, 1223042778, 1717350061, 75204308, 438442790} ,{1530442143, 725590996, 1155006135, 8364550, 2055643168, 512058793, 224166281, 37280534, 1398349887} ,{909771006, 144337620, 1257163396, 1744714766, 1942383312, 470162862, 1307354759, 1179766418, 1314716701} ,{550010533, 1898560929, 468179505, 136472692, 1350791268, 954437966, 778371311, 1045158739, 877611527} ,{433837868, 1540684921, 1118499370, 1300284149, 996411380, 100351479, 976002170, 2032425213, 1680926926} ,{914651626, 1989015580, 1624695372, 863230255, 2092264129, 165648958, 1351969872, 1678530988, 216288095} ,{1996222421, 498854076, 1675863870, 1891136863, 213147483, 2055034058, 741853585, 20875373, 1601705473} ,{1240883427, 1387902716, 127081727, 438101018, 1087626032, 1705212793, 926121620, 113958451, 81372933} ,{781859265, 949103203, 424632019, 658555113, 192884729, 1212323372, 1288809027, 959091230, 930944622} ,{2026970611, 2016988139, 2011477135, 855045868, 1095487378, 341180784, 174076943, 1116241485, 1241673770} ,{591055610, 1060335675, 1258734546, 1028330541, 993560331, 1927208035, 47340822, 1226509618, 349533600} ,{474397193, 2077815902, 580698020, 851149327, 553539540, 1160157608, 1033636316, 59879887, 1284200856} ,{656792993, 1614920546, 891958362, 684540967, 1223128806, 633423722, 885008424, 804284748, 1800622603} ,{1374046595, 1159092038, 183426431, 816904277, 1149755410, 141304818, 2066705027, 845176794, 209698438} ,{208935577, 1964287894, 1290689124, 1668862826, 1236660903, 1569037895, 204654949, 416117588, 1950013206} ,{1951400851, 191927669, 620759198, 2016849123, 1199643056, 690053377, 57117969, 815741828, 1773126875} ,{567580201, 1947398309, 750034191, 1466814569, 172879173, 1856119421, 207766576, 964599538, 1768004617} ,{1703976435, 403956971, 1674973120, 1495867951, 567581048, 1418177954, 534365742, 1523588306, 678570235} ,{240430468, 909861065, 449399312, 838193191, 1902401191, 1684703860, 2007236608, 994653837, 1232149801} ,{382429979, 971253008, 2015881252, 841613755, 670171029, 730028333, 1008752059, 381176352, 1825249494} ,{5638970, 236071928, 1175674997, 869835242, 1256331244, 1307840112, 2006421146, 811832935, 435318805} ,{600110568, 1732388788, 7845031, 1917843508, 1325839414, 233859901, 33368248, 617674433, 358034086} ,{860899387, 446173476, 448040869, 1357735675, 892521554, 896477967, 885892857, 725550628, 1497572293} ,{599091214, 1896833313, 1659528594, 1844763470, 1062881770, 1042906294, 650266297, 1295047719, 502240886} ,{107988928, 513683492, 2035721322, 1121627813, 71157390, 1334880826, 65274480, 1098781745, 223454691} ,{1168435403, 2046536723, 494623867, 297125515, 651292397, 1959706879, 1848835924, 79529671, 1804573018} ,{1272983110, 1535603581, 635133898, 1798850220, 1451389407, 16317669, 1595790355, 703452684, 1634677697} ,{149233730, 1549813033, 118155068, 237659065, 993994568, 423839404, 399308006, 742146357, 412120796} ,{1726725868, 676415651, 1436326095, 2051165787, 744152047, 931055056, 680960096, 1383333024, 689482218} ,{320786719, 964875123, 1867230531, 1858909197, 97983634, 745225589, 425285569, 528943242, 127601864} ,{639205105, 1112842691, 2126553470, 1267867502, 207233447, 1425991816, 1197959605, 1385452552, 1010815242} ,{646490098, 378658310, 1277695546, 1048970500, 33685061, 2085122058, 333154461, 1039740621, 1924633673} ,{547927038, 868128903, 663679640, 1263476106, 768368143, 461606075, 2112730249, 1490388160, 246732287} ,{982469487, 1505254108, 2115476551, 950566098, 2024339439, 1913009064, 1875420108, 1153162519, 1513358375} ,{1351072732, 891651361, 1847742183, 1188223826, 1336315121, 1707266093, 498579774, 369475811, 240630946} ,{560158578, 1555620771, 413374959, 57754366, 1326328035, 1144816904, 442185772, 1191479534, 265065104} ,{320431665, 1604740872, 448628547, 1371355354, 1580880504, 2029460701, 1993175131, 1854860064, 1361176211} ,{89955220, 1090682105, 468820915, 1285301718, 489822174, 1059197778, 2030836975, 37637865, 1013519833} ,{888474915, 787136394, 389533682, 778617186, 589750374, 2086782909, 625527305, 1096553541, 313330817} ,{257128385, 261988351, 387317728, 1058215602, 902906418, 708864328, 998208672, 1997332755, 1814452689} ,{2128119878, 1335660855, 430886682, 1243796524, 1464148395, 376070533, 1695126366, 1294747390, 2084728632} ,{814846573, 1234664956, 371824798, 119779890, 1742632266, 197849379, 195203333, 1447323094, 591824089} ,{1614146356, 518076024, 1975373331, 335230768, 929980564, 1671111717, 1068361683, 283426726, 1475026304} ,{1689759980, 352732307, 2042418416, 22966012, 1153147927, 1908799961, 1481712612, 792856786, 1519893146} ,{1409537474, 80163292, 1758867761, 2081118454, 1667104871, 1090063729, 196229450, 938696323, 1660683705} ,{334429515, 223267306, 1645985444, 1695618668, 612152182, 1716829711, 78727404, 2097560806, 522250994} ,{224487197, 1738359031, 158296853, 1266353104, 1329775676, 1337665965, 226984677, 864785920, 1027100604} ,{956259928, 1640031682, 580272501, 1964931942, 1914966694, 16850519, 467000637, 1479359172, 538197525} ,{1860278891, 1967541887, 1280421864, 911263141, 1939278279, 350076634, 513649948, 1590479118, 1167862223} ,{2095115804, 1150870013, 800823993, 819668689, 1865239155, 847076211, 1685463325, 766627437, 1274797005} ,{1112133265, 1999045760, 1714571576, 385917284, 806116174, 2146670029, 1929604707, 462174913, 2040219325} ,{810986818, 92710449, 1417597035, 779109582, 223079664, 1741600345, 1344907826, 952636937, 1715017836} ,{2042480124, 1834870321, 651277110, 1906639661, 480414167, 1549499007, 860571734, 961194460, 143996106} ,{1917657618, 730596839, 293272443, 193642663, 718609911, 1992151955, 1206631994, 1274560018, 57765540} ,{1798026075, 802657864, 1223836436, 1492411252, 125048294, 1783961473, 1995314291, 1975167256, 1151726672} ,{358328779, 647096695, 266970721, 2049334699, 1211082137, 414307440, 121963117, 1684525061, 728872548} ,{1067232274, 1301740398, 773861151, 2094757157, 1497082220, 283964008, 1533467207, 162179264, 1869816207} ,{1070834770, 778777015, 685967291, 986206148, 1543454294, 1477726669, 1135130598, 374162074, 768252474} ,{106398292, 1771627323, 2110428034, 1756025633, 1540235722, 875394462, 2018788337, 727327168, 2144664672} ,{93216548, 1256027787, 1476298383, 142821831, 1697849176, 2082395454, 103214880, 106696623, 1982627905} ,{1181027311, 1308050654, 1614194062, 1792717619, 558910731, 2075807101, 995035759, 1337080921, 198306591} ,{1173854424, 61555314, 1158174443, 1006179413, 703728358, 80202995, 593232483, 1120670591, 1492609657} ,{765608620, 423640093, 106108863, 337555623, 971541500, 155677076, 1769608804, 147689271, 2100966261} ,{713128857, 1353942237, 312140353, 396517172, 796750658, 782224590, 1112103570, 1081022746, 1062637791} ,{844268523, 765060988, 1685438868, 1687945123, 888179633, 791689552, 549354066, 2139916294, 1642447483} ,{1029225728, 719684707, 1046914110, 1519526926, 1966185603, 1218449161, 727831086, 1909031140, 1298024704} ,{1617530560, 1172242034, 1624600017, 858581666, 147156156, 606106338, 1197714434, 1942620167, 218858374} ,{1921287956, 1478627779, 441763051, 434085192, 438011450, 376680265, 846795870, 1211448461, 121378671} ,{1676868090, 307766929, 1047594428, 848503335, 1779377811, 794604921, 876856888, 1400596883, 1611291449} ,{1372264271, 556373014, 984181270, 1312389175, 1839585931, 1257196525, 1081033398, 719279121, 1695212421} ,{279142459, 1559972279, 1323839805, 188997188, 1202453217, 1354898236, 420128693, 1437816516, 269720707} ,{779465324, 716247735, 1858343928, 1487792903, 830128981, 872931601, 1660192050, 1980467509, 1103332994} ,{159413008, 954437726, 421019494, 624473512, 291046085, 1757636632, 1094336861, 348829793, 1564820028} ,{800535499, 152520527, 975091273, 134052032, 2009114448, 1912442567, 1057355651, 945093560, 1678331702} ,{1582927517, 699328408, 1378228817, 475685979, 1465378021, 1970664877, 366373563, 669577779, 1123651616} ,{1026272972, 1802623481, 1489725074, 583103450, 669163628, 584434482, 1499921370, 1131872794, 1776246703} ,{370583497, 1029652728, 399576315, 1458497119, 1787454212, 577825957, 2022698330, 1824294527, 1900570404} ,{1938524356, 929860676, 356133569, 2100482268, 1781439055, 1969364069, 633223786, 375615390, 576231845} ,{1088336740, 1136563399, 896595337, 847576010, 507219524, 755645540, 632119398, 411585856, 2081151392} ,{61937080, 311891788, 1564106624, 1101631185, 1847738402, 290213810, 388789967, 1717173973, 495108235} ,{827690718, 871075368, 1488528461, 2123454305, 229458949, 366635252, 501212664, 230042955, 1759556650} ,{1270433679, 568296364, 237201804, 658485673, 194782623, 16270394, 1734745719, 2091434136, 1180767593} ,{197143922, 706242083, 331429217, 174590238, 276032628, 851990075, 1643845426, 243489433, 1586675697} ,{1292245194, 1686278886, 810038937, 543010209, 73702457, 589747974, 2106865699, 1845716679, 1467811084} ,{2044645147, 1567449295, 782276006, 1678964530, 242250023, 1603442770, 1294812860, 1225780029, 779594654} ,{1105082783, 2033988813, 1394157411, 1775411816, 848429025, 1969332386, 1349136170, 229472333, 16494432} ,{561411245, 79344421, 503289950, 710521079, 680979672, 419721397, 34264914, 54283265, 1198851441} ,{926528978, 1081022522, 1177205209, 1321610334, 1364585233, 541989015, 450638142, 1378635349, 1848430502} ,{1216030079, 1081540767, 1213112952, 1281660405, 748553241, 548556057, 698995785, 1573882792, 1486184452} ,{768179062, 803429057, 88395736, 1593946551, 2096157017, 749314750, 899877397, 956866915, 1224382685} ,{370094193, 1476057517, 332802090, 303682957, 1225542588, 594905273, 1520449615, 1382138327, 1493413450} ,{1567015798, 1286863034, 935299387, 149706418, 1617173205, 523659140, 752018688, 745493336, 926753808} ,{674221735, 27315207, 1412953567, 766416766, 1551752809, 1401950335, 385400038, 416778388, 117390185} ,{1844080660, 112143243, 8230936, 1034352967, 1442392337, 1614198659, 1483370870, 1588914986, 1530517186} ,{1344821152, 1900718879, 603842137, 102519437, 1899842950, 571038017, 81856284, 503366467, 327800316} } /* End of byte 11 */ ,/* Byte 12 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{696788222, 1812096040, 882428843, 1609134457, 1078616037, 1782968527, 1010009135, 718760348, 1686043374} ,{1202180744, 42874873, 2062234348, 161658249, 1423167426, 51608342, 111436146, 766900692, 295758725} ,{1146186515, 58536875, 446231569, 913842564, 417848192, 1267984330, 1076178877, 215057410, 1416319922} ,{783395902, 1549319768, 1597739531, 285561366, 321855346, 1489879567, 1827900959, 1685905495, 1342401158} ,{1644664828, 1464835735, 497174019, 2048306693, 1557558248, 1748196504, 314801972, 1079132544, 1609358587} ,{1395101234, 1195914052, 1021136119, 317029561, 184672441, 63383526, 1643196979, 1782020083, 2030571606} ,{419946285, 942800688, 1228070151, 1387434373, 2104593841, 1332690400, 761628511, 154496753, 1298900701} ,{2132263651, 1018867167, 1178466566, 1734643453, 647703741, 1453690895, 132803841, 1795536291, 1200382045} ,{1395839771, 1427915431, 1505127076, 1938604544, 1485266408, 2120487517, 440833898, 1569564315, 399047986} ,{398926468, 1783326693, 1500398855, 104821855, 987238773, 355212241, 1483448753, 981479162, 858469431} ,{1350872487, 117981453, 681553802, 616542729, 1803328278, 396795359, 435301244, 893684023, 1812661417} ,{2090571687, 147935214, 1391667443, 188701832, 1127409435, 167854400, 1332918403, 44302938, 897155042} ,{574690785, 2117237780, 1100582759, 207233149, 1933138402, 305483270, 2067405438, 1416376528, 148916429} ,{2147169391, 1075960581, 738666450, 2031938273, 652590330, 1505567300, 211717895, 1420966098, 2087987478} ,{1998335582, 902731590, 868146862, 1879195961, 1570165653, 418062501, 829199169, 407860246, 2071659661} ,{522007123, 279919223, 83499994, 762310327, 1561280008, 419339277, 1762092347, 897197282, 1462009324} ,{881585730, 31318601, 1893457268, 121783159, 347579961, 796505419, 1507306189, 560593634, 1654610742} ,{1270474204, 1403419848, 2069160783, 990287640, 1987433222, 1388250606, 80034518, 747527194, 1081553610} ,{1054055266, 1886420203, 901325693, 2144257583, 811873509, 14262307, 1647953162, 1782222341, 295469872} ,{2135127386, 748167985, 1342423435, 667705086, 1210803764, 645118494, 472386868, 1361363141, 841700847} ,{1037626816, 1344010387, 15086114, 1921074444, 929462577, 551344272, 1110605807, 136498041, 989062445} ,{214283764, 657559181, 141797254, 476941659, 1202755375, 1012056593, 1183441817, 1888098123, 864043579} ,{1779056755, 506495601, 1840527005, 1413621132, 1217834657, 1494438472, 458480081, 1360527286, 1385895201} ,{1258907145, 779470512, 1718159072, 1927446600, 1631923073, 875723923, 1733778246, 1964869308, 1803212274} ,{2030189366, 316269033, 1324350818, 328555118, 803802916, 249947849, 55753065, 845912457, 185611009} ,{2009180327, 368501835, 143500705, 1018775192, 1410557240, 416907997, 1822944006, 1024989884, 1481307510} ,{365378227, 1405712768, 1831417885, 1610168826, 877064681, 2029449665, 1657981414, 385630237, 419971551} ,{1939603071, 443822591, 361014742, 347336555, 909018255, 946077522, 244635974, 1764952517, 2116645974} ,{1226368805, 1558231512, 1947516869, 185668397, 1142679143, 1665873342, 1579209564, 62441779, 1013450338} ,{28079442, 1376073886, 1132410012, 1835086761, 701163152, 1580994446, 1223307050, 333187470, 1658289361} ,{127887597, 865542840, 2070864585, 1666319089, 1853316403, 498659634, 1252088619, 1120713890, 387324718} ,{30077069, 1599850421, 1571211200, 1538362238, 398560681, 408207441, 1535712778, 786114401, 230794823} ,{351427891, 132700222, 542526223, 2112293290, 1313491376, 1190816466, 769985554, 1198553252, 35004279} ,{1948300955, 1359108464, 1738305054, 834245276, 439352440, 140332218, 1474440413, 1714055502, 54181379} ,{48522004, 764918241, 1944784217, 1441792898, 1658103647, 1177432269, 351566917, 996621930, 235544148} ,{1681474937, 872028403, 589307132, 516653903, 1747866657, 2108873275, 1461814221, 112393108, 420038881} ,{1336550160, 1276029054, 194713084, 692560261, 60979972, 808236498, 1844565592, 1335452492, 882061788} ,{1659330136, 592482791, 278939816, 432317971, 1123046790, 1729704161, 1928401189, 723745196, 440239242} ,{1002421666, 982984946, 1046480286, 710863573, 723785617, 1276382457, 277623879, 506321033, 1942437626} ,{1037444329, 786435165, 1473943156, 2045064968, 1684881956, 1389697291, 21181463, 212290356, 1181083602} ,{1452982732, 770828505, 1827540207, 1152149652, 486079777, 1550774737, 962646502, 1029971741, 232963850} ,{1564004223, 1143247447, 1686414711, 1165190918, 319861074, 917968516, 935629001, 945094950, 1062819665} ,{1265122571, 945025972, 1806109020, 1352435549, 228708949, 1683349078, 1002188478, 786921217, 1778647623} ,{1542028138, 300215744, 1085348765, 1985513885, 1025744370, 1693241178, 277965988, 599735601, 923211092} ,{415459310, 1729088186, 2142495310, 1931560600, 1166869954, 509935604, 246030931, 879082133, 107571931} ,{931816679, 1482769272, 2081017449, 2708358, 724310367, 1049259252, 854783252, 992024408, 1236095283} ,{51393308, 126475116, 1091805853, 1030810527, 2049416357, 967480917, 431064985, 1416808694, 909288220} ,{1953142785, 1223314758, 1198217253, 1924082724, 1500891632, 1076585695, 284541348, 536003064, 1742787289} ,{49606365, 273298902, 1032584285, 1024310220, 559323094, 792807586, 1399346724, 964035596, 1939897553} ,{2054617134, 896076399, 476983398, 190302648, 376867992, 1680198955, 1499526499, 1705577838, 1712651031} ,{1838162657, 1648090226, 185078703, 614717173, 1606849275, 682610749, 912532582, 510079134, 784076484} ,{546857375, 2077720603, 944407365, 732612671, 995884328, 1349449610, 1545841825, 728438944, 1170427427} ,{31300942, 745346390, 1387708508, 1727091326, 1592783484, 949139425, 1592380621, 1277132291, 179419541} ,{1552595670, 1278629417, 32396315, 810091727, 495786371, 1808962756, 1117246268, 1278048992, 1336959412} ,{2104184015, 816620296, 1292947428, 922786918, 463393421, 1690768626, 1345423503, 774011239, 85115305} ,{1450852587, 701336827, 2040315511, 1847273555, 56231133, 945157698, 1494761952, 465113413, 1289305809} ,{2037115888, 1906993108, 2010734827, 1499184005, 53512755, 2052996006, 1936431319, 714058861, 2058449983} ,{1048861364, 879958584, 1289388345, 876951673, 846522117, 942353836, 530067773, 2087292323, 438092351} ,{1014365917, 607803662, 1894664988, 1985546610, 1181586190, 2078136691, 200949505, 304601548, 1933969107} ,{1786591549, 749684577, 439846277, 1146768873, 676843154, 1090745176, 1776072383, 847266086, 1024545118} ,{415302310, 2009236305, 1298126179, 945621240, 1537145333, 790138673, 269958367, 1602755088, 1835174511} ,{762461665, 1122392429, 737913527, 1759694907, 315417675, 1392401758, 35824547, 351291945, 729336406} ,{291166125, 1084528304, 1678341182, 1865772381, 1493781319, 1519491072, 287525848, 476648146, 1971199562} ,{587493162, 435182392, 737577629, 780611083, 1550649134, 444308150, 1655811600, 201077023, 1802394723} ,{1389300081, 685661134, 2093686594, 624189349, 2145957161, 306780288, 697823305, 916822104, 107082507} ,{1500724170, 1071042272, 438693193, 324778690, 1420572696, 1619530636, 1589844388, 627326613, 449389105} ,{120301961, 979833722, 983911173, 2016199557, 1279754511, 1045102218, 403377032, 1016024137, 602023516} ,{1804585813, 2146457583, 1369617381, 1529147241, 1042512084, 763233272, 243492277, 1121802397, 327758698} ,{1434626505, 388673847, 2018368120, 2133513954, 1932461723, 423425482, 2084917566, 2048037900, 1215648458} ,{1414594852, 403253272, 1315959015, 758448768, 996107119, 1196635262, 1111885038, 154540640, 942017895} ,{1666701570, 1409601197, 456044066, 1350163658, 1488657786, 1899997471, 972160957, 687784776, 818436385} ,{1892325122, 746885764, 1025488056, 416686884, 1543381686, 299320336, 491229209, 1211124302, 1334236545} ,{1046689848, 1052727161, 1905338108, 302710278, 1325250656, 1545379173, 612836121, 195591818, 1042045155} ,{1544894979, 1421822564, 1538791798, 222963445, 733724705, 773735139, 1160610317, 1642110934, 1662224431} ,{247204486, 1106757602, 1785062233, 479203957, 295937421, 1664481126, 1846738933, 1228709701, 2120746855} ,{1820458569, 110287386, 225358918, 492358696, 195124016, 439494528, 930529292, 1455163632, 974306933} ,{1901830698, 1221666851, 1720971757, 281460102, 1953786761, 341277742, 1816272912, 934254771, 660429950} ,{1441123476, 18547493, 1356114318, 1664337095, 1924168244, 1232141194, 280619806, 62881610, 828417857} ,{925085040, 482021842, 269799975, 151514061, 1442498876, 158242078, 2147150530, 473753199, 1169311074} ,{853610942, 68340728, 383862005, 1986891527, 527064322, 2131808910, 711470710, 664134955, 1905229823} ,{638050382, 1007591661, 593817482, 1538963983, 2027026200, 999022392, 1532649833, 250917265, 328592422} ,{2021784628, 1618178822, 972471659, 218022355, 1171553244, 1769547661, 460504160, 56845899, 1044282997} ,{836882401, 144379572, 1828573148, 759581331, 293387515, 2106328561, 1198732334, 1843263567, 399864543} ,{731505939, 554733719, 1433139095, 26827131, 1299504536, 191332458, 1151424370, 697570425, 1592225799} ,{448792000, 1647205168, 1835685795, 1783293045, 1449645019, 1490127968, 25650818, 1559566894, 259551512} ,{449890930, 706985545, 2016134869, 761481833, 409013262, 1623265543, 1770114476, 1859928684, 187534952} ,{555128875, 1812492930, 331660447, 519087781, 475596323, 1081326912, 1241686216, 1802484523, 166849287} ,{75741010, 517573344, 963073667, 949879509, 2003888055, 1564805814, 1918630185, 815229856, 1289060876} ,{1786005116, 121217745, 454428508, 228074382, 1791720059, 1214102872, 1317982691, 947896446, 2104201397} ,{2102458707, 1467845711, 2125122627, 1977273521, 255816182, 1483604440, 1605861073, 663602869, 989922819} ,{205920560, 1551603012, 485867102, 1067071525, 1957876812, 1479530816, 1686018234, 1980203696, 835308789} ,{2044908115, 1006954080, 256571765, 524684772, 1632046441, 1901848975, 1201537359, 2144256161, 988930967} ,{1840636971, 395990234, 116702147, 1231394391, 209194094, 932883300, 1937676639, 445833385, 590023321} ,{1069347642, 399186329, 363828558, 1149069710, 2034857074, 239663019, 172572319, 981295216, 1261907019} ,{958606121, 1396423171, 1528354469, 12270032, 73550778, 1168285211, 153087004, 388186310, 1011427393} ,{395509781, 646543780, 713701261, 1092693123, 1600818053, 615489539, 390905740, 955876114, 622016319} ,{1944295328, 629222673, 1668045255, 113291825, 577523319, 807712767, 2136965063, 1834960247, 686906509} ,{1972699124, 218060274, 2035282067, 415271547, 1687466260, 2104595957, 976550935, 1684091455, 561163339} ,{340538948, 2026629881, 183755191, 1023474272, 797374276, 618951061, 74496502, 1212148861, 480496357} ,{2105203046, 1752112332, 649657783, 1310173700, 487338346, 1659536360, 68820057, 963964133, 1545815270} ,{968299374, 1338555242, 425783176, 436699059, 1155789547, 355405367, 1053010574, 1571383911, 1786921184} ,{671888013, 95571442, 788652228, 706822491, 461065524, 1256275008, 1207949434, 1787278742, 50266329} ,{253642787, 730384959, 325337830, 127775180, 253606110, 1393229260, 1985191236, 1150945165, 387646214} ,{573100363, 73794066, 839188301, 1836971337, 146654513, 195331486, 1415067375, 804826844, 294461847} ,{1281143408, 916743304, 213924520, 27997364, 315504262, 1327336570, 823098544, 169409603, 578333448} ,{482152762, 1671922619, 324240683, 1640564451, 989228669, 1717917517, 1548330652, 239655264, 1334846056} ,{1523922440, 335809492, 1025971443, 894102921, 828255639, 1949920285, 880370255, 49102420, 811069792} ,{1392298759, 2050121255, 830746957, 630925269, 1393934553, 1455442507, 1072961356, 1973375712, 1991743242} ,{546028185, 1963482915, 1564828127, 1792921819, 1231602611, 161760315, 971341105, 1989823344, 964661170} ,{1182180557, 961156366, 2107103538, 620948757, 603423133, 225270624, 310271902, 727101956, 2066924366} ,{553119176, 1069448143, 806964722, 1366047384, 285566068, 446359702, 865487191, 96759226, 895945433} ,{627049804, 449241608, 1862282327, 1005046689, 1312018061, 659270906, 128298957, 638019507, 1840330510} ,{728114793, 1732350219, 299746032, 283623426, 1008315513, 725890765, 582555176, 1797716032, 1667605090} ,{1550224238, 1436927234, 662334548, 203451952, 1790341883, 679600386, 1068719507, 581019401, 1694359275} ,{2108463850, 1105836020, 913243453, 1836682248, 955701007, 1441615496, 2126664947, 1843968793, 1706176300} ,{2053051036, 1012355475, 1646918120, 1745995758, 1796301755, 2011774530, 2016538858, 825458793, 755771901} ,{929777915, 366920783, 723042476, 535694139, 341131414, 1652111248, 1003796998, 2117100472, 888636437} ,{13354760, 27598147, 1781244068, 1015080648, 2068444336, 22923981, 1880594857, 34749901, 16172406} ,{1229428993, 723828729, 1298165108, 1743379978, 1717119021, 1689392952, 1995367763, 1367123808, 2097619147} ,{1691212527, 1539433081, 1018411804, 1745496835, 1735214882, 1699127973, 1819581384, 1538140080, 1196865854} ,{1001857750, 151587285, 1192588884, 902674961, 1182850483, 874678896, 456954541, 850115054, 241065120} ,{287013602, 1477604294, 1560168054, 408500365, 684022791, 1591897898, 788255425, 669481878, 234955769} ,{2047790000, 1430362833, 418729393, 289858902, 53780468, 713636333, 1564821047, 493790812, 1169443872} ,{905708307, 1827109157, 2026175826, 416559650, 733216574, 114246879, 1878815236, 1476961235, 1826892877} ,{1043177264, 1741638820, 824182052, 2033778361, 1804576145, 1806023507, 943693101, 1810859958, 956026795} ,{1132337194, 1086281928, 1138322978, 129085198, 626141548, 45718816, 692824663, 208719113, 1948954229} ,{558899408, 61835412, 1662035134, 809229155, 588245303, 725214078, 1193461842, 606265875, 1986664982} ,{1845660459, 1258918258, 1108130310, 3685475, 961772168, 505562915, 394312378, 1798411425, 869199081} ,{8771549, 1235129498, 1308240580, 1005606907, 1371747760, 50943450, 216668549, 1896140556, 278601836} ,{1110412500, 522246569, 1969562154, 1130297090, 930540277, 133602194, 1278556292, 1971292576, 1958574793} ,{1651977991, 1146069666, 420222023, 1982647481, 1021390414, 1748993375, 1418687077, 1163984457, 291873307} ,{729579956, 631524691, 755842124, 1513530044, 1262140470, 2094158624, 452383335, 818324965, 1512597644} ,{155997300, 4047260, 1476717626, 206028942, 1119876262, 2096931852, 2031281666, 139325453, 319493077} ,{324522066, 74496233, 1161095448, 1374777760, 1690989543, 605158604, 1378786666, 1536447521, 1260607093} ,{1846555793, 410977853, 734181595, 1066811449, 611025804, 1160547696, 2045976819, 243345380, 525854921} ,{2064823991, 1101176654, 465628525, 1857472460, 38379719, 1559604263, 241889580, 1772811107, 169515108} ,{1278198678, 967648382, 1338594563, 601596619, 1880622149, 305530480, 1231658895, 1781128741, 1364601272} ,{1622863454, 1884557007, 1889705453, 1711805492, 151994631, 672309704, 2083893786, 1626687761, 1837511744} ,{975323767, 726579709, 1865176392, 336502747, 1619504058, 1136835667, 71419538, 96757544, 2015135647} ,{1798693059, 1387560779, 469871958, 596973526, 584989782, 650644026, 886391643, 608858998, 157140768} ,{1387517840, 524792, 917152549, 1322161764, 1740339215, 1662377195, 1945933266, 1285330215, 753602728} ,{1073040623, 1872186690, 1498627978, 1796943597, 587348573, 1818745019, 754693039, 1425426851, 1269565181} ,{221610131, 276969024, 914982218, 1982617779, 842130865, 1380521484, 861296428, 392673841, 812069203} ,{147052064, 130382886, 272096342, 241929617, 1173659237, 508411393, 1490016725, 257294675, 546735189} ,{1598627007, 1268653679, 1019793701, 1210204436, 717214709, 1460753503, 830652522, 1134224418, 105487798} ,{1175257192, 1350496773, 1602943181, 275343708, 120535901, 80163297, 1252763480, 1984360137, 1251370953} ,{6597741, 1249166044, 378512865, 1404637827, 1647341250, 1354231017, 1729893109, 1765542880, 1029401242} ,{1013526118, 1141594131, 150475186, 630143300, 1520341387, 572111625, 487107029, 1426023481, 1128607351} ,{501598278, 144746945, 1251041669, 409645552, 1228371958, 1901922830, 1659110502, 43705241, 1320449306} ,{1435765802, 392422697, 261168661, 1835173071, 2091789298, 104653031, 1405656106, 1987438528, 107695625} ,{2129917877, 476344252, 1906443348, 184387879, 168038009, 1556234095, 824890503, 217798750, 1165550270} ,{2061439204, 657050468, 184072254, 1234299438, 1806304528, 365043476, 1774293955, 76425642, 1994303918} ,{1838262640, 2006866746, 761911230, 1957642998, 314142049, 1363474822, 499443407, 386973435, 1789314082} ,{2134484684, 69066333, 684517797, 1123470945, 1328198284, 1898977070, 1093617646, 1384949863, 1165588379} ,{1847910306, 350190757, 1288825907, 111403543, 1336869928, 347667244, 1596639101, 1807437687, 1455886014} ,{585534035, 1197353841, 12595098, 2038436052, 1479739686, 455546017, 1678515092, 591962311, 306185236} ,{2140676101, 982955376, 1251591459, 1956120095, 1422954564, 341092923, 1454331483, 1769226623, 1125847793} ,{65293872, 478535934, 1253610918, 821602263, 719404596, 1744173267, 1394769551, 1514548926, 833557566} ,{1326606507, 171294718, 1191756737, 308502311, 19912814, 458601717, 709123589, 1026676696, 1378562888} ,{222816397, 1344741635, 191193932, 1471751179, 1580572134, 944633349, 826939901, 1289696396, 699681666} ,{1095103315, 247200989, 650343019, 238798154, 1744595689, 1474527564, 563755101, 148858051, 726127730} ,{1124712629, 1620973619, 1670348416, 1202065686, 1402995443, 46208193, 902556272, 27177532, 534489848} ,{1835869542, 1115078988, 1424389501, 623854089, 1460256078, 687273013, 432224670, 1546317278, 704296666} ,{871365504, 1841720215, 756160302, 338844457, 1119787051, 1966239018, 2000711928, 497588741, 1053005174} ,{918206403, 744425403, 1560489118, 318639901, 1476822892, 745175709, 118561614, 1780381889, 1820056872} ,{1483520887, 425718214, 1379410810, 83188852, 1548857702, 658105180, 1149073997, 1506374053, 270007507} ,{1874646608, 1555830941, 1649965936, 1845479994, 1980014333, 1758698087, 1786040882, 1435114050, 403982592} ,{1208650843, 947245001, 1011165527, 2101693009, 679989620, 846339753, 2110810984, 1792405894, 1555886110} ,{1361675884, 352609680, 2063732929, 1047114553, 1245371311, 305519850, 883059158, 523295483, 1384340439} ,{157719104, 493633815, 1223946279, 1370764522, 265302211, 867928858, 705422814, 479621443, 419557253} ,{147638000, 1229817733, 886843234, 829375599, 1902133389, 1178144433, 1870357053, 1570615001, 1459792321} ,{339217759, 591460430, 239168744, 702608318, 1073604734, 365360032, 1399782921, 320058478, 746835902} ,{2052469638, 1528591651, 103934190, 295477979, 410454757, 595478913, 1291380941, 2098352479, 1716140854} ,{948747824, 1729495846, 1729509126, 1339145436, 626921507, 1826930837, 767960786, 1714604255, 1637422753} ,{1298276400, 1304918110, 680860584, 31921917, 505241966, 850521370, 1456919928, 380993401, 479268458} ,{1230974009, 2091543540, 160511127, 1949759252, 1942889836, 479586631, 1173771812, 679945659, 1597534673} ,{506135064, 1223410262, 1013317395, 1169662475, 653920677, 1475759719, 389427311, 66995275, 93601419} ,{318194125, 407558162, 1161285772, 410518563, 1105733729, 833061130, 1943456200, 1877792540, 448506340} ,{591097016, 1175855096, 1648642051, 275070646, 120374848, 539474411, 493458366, 746605312, 1147553177} ,{661237332, 1527139150, 1341806185, 837083850, 1847621303, 436712625, 1725827678, 18295798, 1626469409} ,{972099054, 240476436, 1043137113, 1496481321, 2018796444, 1403480320, 551919001, 1410683853, 773437819} ,{1998055339, 1140953050, 505877128, 405598481, 1003285403, 334259499, 281512121, 414443421, 965604388} ,{1260407927, 1074793653, 108461932, 447796430, 1668659135, 1385270155, 1234230113, 1411680261, 1281945226} ,{995817444, 1198267350, 1581071724, 752105905, 1492189802, 1279227974, 489096839, 473547829, 145332056} ,{90702157, 21869220, 1019967850, 1126792586, 2074769133, 1815484565, 921419164, 1233792608, 354451532} ,{61278289, 631049944, 587298053, 1550312929, 359062690, 1575970184, 465205218, 1172742738, 1881338014} ,{888865347, 402034957, 1791626081, 767171606, 1700589769, 842551362, 1711481469, 1134520733, 551888236} ,{801841760, 1523290685, 1635886633, 1947481072, 1973991604, 1242646069, 19844503, 115174396, 1504183836} ,{1721704680, 1512647346, 2100282548, 1110260145, 1265759801, 910050708, 493274612, 78620572, 379249142} ,{164332003, 946379600, 284263445, 425200547, 1951180690, 1210985946, 504663458, 33819972, 1080350241} ,{1150352555, 1255134892, 681186957, 495525640, 171084746, 315640586, 1214957846, 714535488, 779990784} ,{954760160, 919613551, 203209638, 1027385423, 739870926, 585447495, 1608915750, 1538006734, 1704807122} ,{1785569161, 455467717, 18294489, 1505086501, 1382968614, 2085376340, 1435896417, 680400208, 2003822000} ,{386647044, 212133711, 2000150346, 1914587750, 1229602846, 506303975, 1286073043, 1786584732, 1892788378} ,{1282674225, 1732342480, 244199426, 163717237, 862670482, 1820277413, 1002966702, 27427256, 423574213} ,{755302978, 1686730540, 39211503, 453005932, 2101506579, 1207626133, 1012406727, 850282854, 1159662486} ,{964890346, 2062039726, 24603183, 1993669660, 1325755486, 221089552, 1235712028, 272680262, 1445065759} ,{474626455, 303001875, 822116707, 591310811, 1320745734, 1747495351, 1391609340, 104247542, 1499007459} ,{2022541955, 1829030478, 552467041, 1077636989, 1515635702, 584269882, 562720947, 1632303934, 300084231} ,{1337493503, 1875387439, 900862910, 474980592, 300077288, 1729387260, 295577369, 1337896154, 1335052187} ,{2131795839, 2044863927, 582905584, 901459040, 278299712, 1191361201, 273303346, 1450047963, 92905472} ,{844194869, 2145563853, 1163097478, 844934303, 168125103, 668979643, 1251077150, 1480799124, 79342139} ,{25086849, 1091341245, 1093923717, 645461960, 1811007336, 2048928489, 1343166387, 926973001, 1593940968} ,{590062510, 1283385293, 266595916, 1519456159, 1131257506, 3121591, 1446619365, 1550010293, 1984618274} ,{2033076038, 875977001, 884708989, 795393142, 1752559997, 2097135670, 1224453328, 1225827936, 894023490} ,{945867825, 1888941826, 299111810, 650601352, 38845642, 2094758999, 1606055625, 625352795, 1430584899} ,{2027781802, 137997246, 996080096, 172599027, 1348766502, 1790505627, 1121022403, 102363733, 561319919} ,{715089489, 1843597359, 92465245, 592478453, 819287880, 412200032, 1236685422, 374386920, 688252458} ,{1410186106, 179038791, 678757759, 1450206307, 2014253721, 329718748, 1422349656, 522887198, 1616494837} ,{1054226190, 1290839992, 280661107, 1811857784, 330458628, 1995602649, 548567821, 1727233229, 692294639} ,{713996323, 1775050425, 281559288, 706705728, 848198573, 1847274259, 1675122762, 335161126, 1375137273} ,{1115753396, 1491370958, 1407286590, 1441257675, 1571935353, 671360540, 1172310401, 1138323217, 851399680} ,{1557152208, 391297455, 872811925, 2010817287, 1272198611, 705287641, 733335433, 1002530609, 1887680539} ,{2106991708, 1693183140, 480396029, 1612359890, 844691734, 1814729501, 2100098533, 125706127, 1552313866} ,{470800543, 431955346, 2025966244, 1096486387, 873066888, 589297703, 2141834595, 1957457014, 1691195486} ,{1666812569, 1027442270, 1155964587, 838067873, 967175614, 1410425512, 1950805846, 799530921, 794713974} ,{1049910717, 376133398, 2103659341, 571757418, 731895055, 1834025747, 65885959, 441323150, 1651173776} ,{810003034, 992054280, 1060501291, 1853578111, 655339090, 64308124, 1467490177, 1017834002, 513845554} ,{50606696, 1926166554, 1999980735, 399160995, 1068332047, 1478957481, 1589719799, 1882868530, 1483069885} ,{454779155, 618794267, 1910106057, 1899736819, 1062802157, 2070234298, 970775688, 271475140, 967785858} ,{1181557543, 805411811, 699222251, 578339288, 641222993, 713989218, 234577660, 496237107, 1219159282} ,{1351271487, 1840832311, 2121545276, 2144526868, 1225524613, 2119506567, 2131337254, 754313735, 424643894} ,{348926897, 394945610, 847207322, 1897277336, 2033903429, 2098182296, 1830830436, 826989954, 1100211851} ,{422080834, 1641202589, 746947925, 279659322, 1054397794, 849229203, 771840251, 1190631240, 637676860} ,{40564282, 1142391286, 808418635, 1242464057, 1570058475, 1342942152, 1626879070, 1746798206, 1602924197} ,{1832840882, 487595002, 613929801, 1158955058, 932421459, 60849058, 377884266, 1670940659, 240218046} ,{2095961889, 1205962770, 514462773, 1312767920, 1756222256, 1438743750, 1084644284, 1362639150, 205556730} ,{896879037, 1791419263, 1745660397, 582966730, 2070744500, 1155389053, 589654952, 1665077685, 2024838222} ,{97417345, 1331143602, 377090146, 1517507080, 1137102178, 1833918443, 57341238, 1282689282, 902909294} ,{1246498967, 306674295, 1720575362, 56019314, 1790673416, 622018933, 751249459, 1833191468, 2008049861} ,{1269681750, 1942391216, 1259494508, 1295189125, 1664774633, 424260780, 1111003235, 369467081, 1642835337} ,{639503488, 1601559848, 1475459179, 1068091003, 2139228362, 89243439, 1336849793, 1996057946, 2084853175} ,{26786134, 1015572041, 951585019, 2038451442, 370266984, 361951800, 824901889, 568619775, 1781862798} ,{119245792, 983160623, 2130957913, 1745595182, 1188765594, 82431137, 1779984468, 1981594077, 173085120} ,{154051916, 2023996191, 1158140547, 1049709232, 852286912, 1540581291, 1800053101, 527064829, 1634181863} ,{196355780, 1730671057, 1212414231, 519451105, 1962100068, 1661946534, 2133971497, 1597922181, 1510201487} ,{141716305, 525652719, 1581716406, 1488458455, 2044627377, 1012922924, 1656690112, 49623457, 1705136620} ,{582982868, 1425825983, 749998085, 2073338064, 99785523, 2080775438, 1954461964, 1818773106, 107460030} ,{1338040740, 1365076832, 39837600, 304989506, 1217643191, 896762573, 1121835070, 986089055, 335641110} ,{105856795, 839872147, 1313614731, 1987982521, 429418702, 265951357, 898409209, 492423292, 1545560738} ,{719480449, 313367490, 1198928905, 2054398259, 1035020113, 1027916974, 194912870, 912436931, 1896783193} ,{1307280814, 1784129172, 2059963529, 1969371804, 774958541, 682335793, 1069526725, 2131908362, 243538979} ,{50921912, 904369314, 2060320511, 175142102, 1615173400, 27553186, 547438343, 305661229, 327075964} ,{1092697968, 1356931547, 5820246, 1475102913, 1588793475, 1833480717, 1189463095, 521919918, 1191373139} ,{155905087, 654430674, 1384657821, 595711584, 697754532, 1351483170, 1985380535, 1511441132, 1457240324} ,{1654744217, 1516413902, 1955999751, 200713582, 688074354, 615130879, 1633329761, 1883905995, 1639319991} ,{1624234051, 1703024017, 699219579, 1301476070, 1508611249, 168174458, 425610154, 508267422, 1545466016} ,{909347348, 629879329, 599710544, 341116305, 2046540478, 1170601216, 1907987036, 1391307760, 1559582762} ,{640258876, 2013518187, 679318538, 75620875, 1493165809, 1359930851, 1384458746, 235305408, 122318728} ,{1339470322, 1610477270, 279914798, 373082650, 245358128, 1356411022, 19919305, 902981805, 466185408} ,{542390786, 1970258377, 152863795, 307464004, 1557275013, 277183049, 1022217369, 916055566, 1880900659} ,{1606188939, 941453451, 247939228, 24445220, 1628746418, 271913140, 152769629, 937343491, 334308555} ,{1782413523, 631688668, 419933223, 1642650535, 535179529, 329966482, 519193319, 1353558691, 1242549993} ,{595660046, 1285986882, 560670748, 1711395904, 664321949, 1721750960, 2105541559, 867082176, 2078830105} ,{237907513, 1777881138, 527614332, 53612964, 411334818, 942994232, 1486056539, 1573602640, 2008057541} } /* End of byte 12 */ ,/* Byte 13 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{1200494274, 584421010, 351652233, 1469897113, 258880152, 1667486567, 519651211, 321651548, 1005295569} ,{1452300503, 1157177839, 1656547621, 1864207766, 2086696265, 970995210, 242957474, 1134927556, 1259974891} ,{2036333187, 1555969027, 2138663719, 1149597802, 1589030574, 1594871688, 291968440, 1047408848, 2081430936} ,{162727274, 1933884626, 1696899220, 467782205, 2144814259, 1232684986, 1909704569, 765753247, 480936252} ,{1876780995, 113689996, 1903460192, 1193644695, 427896150, 1067429383, 1851982455, 8579924, 854389906} ,{1407835166, 354794369, 582146892, 1258825201, 2115578683, 506297155, 2015555623, 875503280, 1809152535} ,{1595419120, 580493018, 1569116788, 1645864550, 824667339, 22096326, 58144521, 1732888150, 1291898310} ,{11958790, 1659228481, 1607975730, 1088902327, 1693602900, 581301081, 1511894534, 2062977793, 219941396} ,{1137652916, 637995172, 593005591, 1014191434, 1507428597, 1957803569, 874141983, 1030364287, 1898420373} ,{121167505, 895140703, 612350149, 420552509, 948733743, 1389602988, 984982562, 290556977, 524916254} ,{791382383, 1010140098, 250415111, 1402476991, 730892226, 1055278811, 1062183533, 279093478, 418461144} ,{640301865, 71864221, 1052355564, 794069724, 779163518, 384629480, 917735246, 1117107482, 602924264} ,{1912234702, 1899077200, 478108629, 1356548823, 376308284, 1444543419, 2012627897, 330006512, 1549296193} ,{915856699, 1810768752, 1686747602, 2051177748, 1302770816, 1382844725, 1739622860, 1442309747, 1549581681} ,{1000642938, 222347121, 775485927, 687486704, 1106042705, 1804857375, 1754398581, 180821383, 1364877768} ,{907756212, 829359942, 8841376, 1332931810, 1224856455, 1800135456, 1096780884, 1378555230, 1816103948} ,{1974102172, 294001533, 1275659882, 260776002, 825639729, 891555961, 1070989478, 1818664967, 940726267} ,{1232438541, 1982444825, 1890406577, 1934587829, 178480266, 1658465518, 1095195281, 1658643663, 2067561178} ,{1834843919, 1137834400, 1049171168, 1550235167, 220443556, 388264846, 1557523566, 1649347013, 1039036472} ,{1708546348, 68898157, 72568204, 869262894, 1784898117, 329900394, 1460341619, 250769145, 86516181} ,{1462135747, 11168930, 625895018, 157543601, 1957423934, 251040695, 1159863, 479260196, 1360186136} ,{691830099, 1882869025, 33674931, 1958049368, 794101346, 1533354308, 355994382, 2048199699, 2117340916} ,{1720834166, 265775616, 1892615248, 2054739403, 156670480, 307728752, 925916726, 227429996, 1795056899} ,{118810361, 478656871, 1676579977, 301018929, 2023058228, 544447427, 90159007, 1021082553, 914060058} ,{573120729, 1531355523, 1338313851, 1465338957, 927832885, 149647262, 1500685674, 295853877, 502161481} ,{464392355, 1081702777, 1695494985, 904158953, 1645413246, 382294541, 1669066078, 105237461, 1715954317} ,{987504448, 1820864800, 1445630546, 102778493, 1414140801, 186748055, 843397815, 1418201993, 166150827} ,{1696508696, 1790634655, 1912982232, 292504807, 355623276, 767359882, 1859154638, 1574535966, 448780782} ,{1695269387, 1599618531, 1657026275, 838040402, 1511154470, 1757893522, 1256511907, 1149880511, 142328650} ,{48898484, 1796742323, 1312667075, 726022516, 252810161, 61640936, 861596018, 209715545, 716280632} ,{1374990074, 1635714663, 2124972452, 1384508164, 1646945838, 510223043, 121033287, 2093765790, 1987869635} ,{317677507, 71040132, 618226405, 1930116180, 781950428, 711472281, 476585289, 582195323, 900304612} ,{474500859, 791580949, 236865644, 300374879, 1950066877, 518131466, 699764725, 592328137, 1420629482} ,{1688936591, 1234458625, 1826486521, 1825124938, 1393637579, 1824446824, 1617426862, 1473773901, 985190896} ,{732001600, 2114978186, 1391967087, 223807374, 1937017299, 1858777626, 1385640012, 1027323558, 1362705689} ,{7600868, 693687101, 810654566, 1990596644, 928460628, 1550020256, 1986134394, 651204512, 1989029237} ,{925341374, 2140012711, 676782354, 1759242745, 660563269, 1171898136, 1984604924, 1494127751, 1318734562} ,{209132607, 1460850620, 1378264697, 134621787, 2115550249, 2139273484, 1059015707, 515585566, 431856748} ,{110803827, 796317077, 1116693089, 216954595, 240183008, 1509155014, 2091727387, 236469286, 801663852} ,{472366446, 1625453231, 1818878664, 1173419623, 1459895220, 2104370069, 2048716781, 563742932, 403591735} ,{448315370, 857178361, 2063284167, 864242963, 1511023992, 1774009805, 1979251647, 923124163, 1510053082} ,{938780830, 2092062240, 1500001142, 1672239473, 1594084689, 689747126, 1902834742, 1220995678, 951987837} ,{1024857673, 623966526, 641823724, 288163179, 121007498, 1661130711, 1241543981, 2108480615, 993652018} ,{1089452805, 1002046777, 2139871705, 333278476, 991150785, 1157609207, 338844112, 1503173225, 1001911068} ,{1308190825, 513349361, 433974216, 1722990879, 1089267282, 592353484, 656147226, 2137584444, 770609456} ,{1654538505, 1414230627, 940180136, 760004853, 24702934, 1372013062, 674723929, 1490219119, 1543235707} ,{650358862, 1990393496, 2028633661, 644459009, 1996464630, 1977516259, 1229128788, 1073257392, 761831084} ,{1248224952, 1040376609, 2143393643, 580021059, 1662762940, 1891988064, 1910694550, 985019790, 244264190} ,{221671450, 250171952, 1325273923, 1905395637, 1199253726, 1246328768, 1748052313, 541958051, 43200767} ,{908063864, 1619895175, 2112396371, 137029226, 1604071170, 1731569552, 647353569, 876823118, 1082434714} ,{1001486752, 343177192, 437026514, 269473034, 438762955, 1281147017, 1813986158, 1567313161, 2145061178} ,{1830147107, 1826818466, 17456864, 1369164595, 1700518786, 1937886255, 949809410, 2110473125, 1498724104} ,{647704853, 1301023230, 1928055109, 509894531, 442951309, 322707255, 1278236658, 1995246983, 66737412} ,{324162503, 2071327110, 608882779, 2083303183, 867053541, 132509134, 2065743135, 823422174, 2082094333} ,{1640726872, 1820273650, 2093684357, 1952261733, 1046329030, 387025786, 454986779, 1685642178, 241605903} ,{325248946, 1918784352, 1065476766, 316793311, 1545675190, 301708668, 1818872191, 861304050, 170476949} ,{186896682, 1151731331, 348251395, 337035571, 1288140870, 1301065857, 1718239445, 272522677, 870635386} ,{2104930510, 1028790172, 1804050307, 399568373, 62251819, 1365806317, 1003895549, 1014572572, 995641607} ,{2111293976, 1304422003, 580232915, 961427815, 585773700, 135972382, 747390946, 1344086078, 2048078855} ,{1425605820, 1224611448, 904151474, 1323673809, 1715189198, 725649411, 1831290186, 1326967338, 1036002265} ,{147296806, 1841789989, 1932746956, 1531124273, 1772609220, 286997554, 1188052718, 900557457, 1135291563} ,{614561967, 1218474299, 602544551, 1812188713, 1046756516, 1754996887, 739914917, 1231095945, 1152743847} ,{76377570, 1724942483, 1636035681, 1923642990, 1713059420, 624468510, 343729879, 1695963350, 45730424} ,{3164555, 883386484, 1826015954, 407977612, 1216164586, 345407978, 1490374797, 1711707533, 2012931320} ,{1034586332, 971437532, 921530063, 1103291172, 1326384485, 1897822504, 686788916, 433418322, 1389436437} ,{1235378859, 1784172866, 1395033493, 1972008975, 1020011041, 1244416083, 1281610411, 557077690, 1898775673} ,{564883773, 1434093793, 727012553, 955112533, 1938534465, 484765196, 127848005, 1447236643, 1754124187} ,{81375460, 31013236, 128091603, 837055295, 214793850, 2531825, 664076376, 175131834, 1656736261} ,{1564804695, 721180271, 1246579115, 734282484, 1050836318, 1784111363, 944262685, 940661456, 292108079} ,{2076781938, 1886505374, 750697716, 2065428676, 1973523206, 944883331, 1601104556, 1075545352, 738088874} ,{542862404, 597028730, 472205043, 2008178648, 1447909665, 836523618, 2112157059, 1161549495, 504496430} ,{737937627, 351445703, 1009269148, 639082858, 231416201, 844786810, 1787335588, 872508861, 33412282} ,{805391007, 817860759, 976636865, 1828191616, 365118652, 1287593183, 848101798, 1316607622, 119054349} ,{224545504, 1465146886, 2008849001, 1202086218, 386063323, 1816052689, 192114554, 490601030, 1288094786} ,{824174561, 1677828162, 1653634301, 1391072658, 847053217, 1496741248, 1988768323, 1017126292, 1779549203} ,{1317135571, 384039166, 294045266, 766193355, 1401819286, 1104157722, 122338428, 1582368364, 1277487088} ,{80121714, 1094865988, 1273024180, 334886048, 240153872, 422578294, 480876683, 488701756, 1787095175} ,{2015343785, 1257580839, 1011165184, 1237914717, 1620889781, 1089081426, 1274231779, 684559412, 522300528} ,{695169473, 249612695, 1013033859, 434319635, 311367592, 1402167750, 1596016970, 949455519, 2019167625} ,{563381876, 1239170627, 1167565156, 1028534176, 1972933685, 612490241, 1060567754, 13090368, 679861868} ,{1829854279, 182315647, 2145875145, 293628564, 540457237, 1581976114, 947938411, 1142966126, 784664187} ,{134650171, 1022864782, 545037104, 300201094, 1047240479, 9369581, 649131933, 1468869288, 1392129687} ,{1182936102, 1588466972, 769906060, 1981453063, 1887547697, 1389187701, 391064547, 448716101, 1837871244} ,{426351620, 1075909628, 265138673, 2144153228, 1617646006, 926686561, 48172559, 2019918650, 1984416302} ,{1007668196, 2025847644, 780528298, 528284926, 607395720, 1243667046, 989489926, 826208546, 962467823} ,{1708817194, 533707443, 512948818, 729457272, 871369580, 1438859978, 1942083108, 908721643, 875439552} ,{1819234782, 200649439, 481304264, 264195914, 1498159687, 1926149277, 1632513117, 1518177423, 1288920136} ,{624220070, 1408477337, 864139770, 788973798, 79082705, 116258489, 1789816940, 162537869, 988562092} ,{867717464, 399293359, 283781653, 256021362, 105272681, 525088976, 1817987429, 586093403, 222262537} ,{1703332020, 1362963858, 1451761534, 943351727, 432602273, 766797651, 217014715, 330525665, 853789202} ,{1042743455, 1697449801, 733671017, 1704770469, 1363809729, 795618680, 455204479, 1438074719, 753109395} ,{705444771, 114994483, 1971839240, 738887714, 174258528, 1049204819, 1497243371, 1395968540, 1833153186} ,{1602901149, 535136375, 1233754750, 20950491, 1861575424, 940488614, 260338641, 37206149, 667437266} ,{958994636, 1188616463, 923636105, 1469083805, 478272988, 423471611, 1956537047, 1535915115, 1551315433} ,{1555431675, 1244695555, 996760214, 1340251857, 518946514, 163423557, 617439422, 1338896424, 479829495} ,{1942909900, 133819194, 621503058, 244665776, 1372645489, 1601284376, 1779040444, 51498350, 280879243} ,{224088992, 393718777, 510191561, 1756863715, 105931503, 1603777818, 1881604607, 1849921379, 1498568485} ,{1275265795, 1546768077, 363923722, 1583372023, 1806530027, 563554137, 1992040874, 241912623, 2043983156} ,{1822584124, 1125421266, 851493463, 986058820, 1480995581, 917013552, 1819028908, 1208392161, 1165432507} ,{669114605, 968251757, 1574160834, 1906731497, 57733674, 1270932557, 807211892, 765213876, 1507895735} ,{227115046, 1938638496, 1290747115, 1516386213, 1522490737, 355909677, 844056181, 1328340732, 198578022} ,{1516327729, 945995907, 824319577, 1694338054, 1508709567, 1381648843, 662865029, 281219288, 121792628} ,{1875605087, 677191876, 771831838, 516067486, 1858165119, 772349372, 1789999746, 533812669, 490214679} ,{1189120274, 616745123, 303673130, 311175491, 1933133656, 1703791580, 1854829724, 91481817, 1578992473} ,{1501544567, 325294428, 777860994, 391424392, 957540053, 551373106, 1229125501, 68604649, 1899896067} ,{1760289922, 185135011, 835860730, 1009354337, 1224489565, 1931265550, 1233659611, 1758689479, 501574065} ,{119614813, 789936648, 2016227543, 8160414, 246038497, 2143935834, 621604907, 542217973, 1452126577} ,{1111975228, 1774817775, 1360097125, 1350172274, 875077303, 97136257, 1003976888, 1857773822, 439604830} ,{56665870, 469740501, 942654306, 1031288411, 1849606303, 1523649270, 1483118885, 1049067713, 730813327} ,{1887938580, 1549243545, 275743427, 849889990, 861523536, 888465042, 179529027, 1538674107, 1074606875} ,{750893736, 1485453079, 2073285298, 824761601, 2072883695, 793143542, 2010433423, 443232450, 844010514} ,{127106376, 1408548307, 175071296, 700081482, 1115300727, 934564346, 492218869, 1494172519, 484966163} ,{1714231590, 1180803941, 802424223, 2080996754, 42101827, 1654564708, 1082320034, 1057939648, 530816409} ,{1171014509, 809302302, 173967712, 2101229908, 514735004, 1365814865, 232803421, 2127122893, 1166106362} ,{1448897630, 1367128067, 1165946482, 159422716, 1390723364, 1933755720, 489131980, 2135710170, 1716966628} ,{84099639, 2120610189, 673707980, 1385062115, 1615399153, 1983801133, 1375241954, 259482337, 1202457788} ,{1308704688, 1813758068, 1541697491, 497981314, 1827039119, 67221239, 1410795875, 1614815493, 510234420} ,{741319574, 918589616, 1898765629, 736495973, 1166974280, 14882671, 927189053, 1807844175, 2144841554} ,{1031647804, 2050931653, 367550742, 1235310834, 890087916, 2119351651, 806893670, 2026639873, 1576628331} ,{684549161, 903542497, 547862163, 69720171, 1707395434, 771324473, 1673762968, 342896591, 621013986} ,{1406501108, 695813338, 715702768, 556914216, 1826527015, 1062531409, 1310184416, 1816336588, 2049802668} ,{1472282218, 1872700236, 1599342467, 439667971, 119664141, 1142653095, 662542972, 1496268119, 1636079764} ,{90687164, 1182743333, 1237197461, 904903357, 605936407, 1580499555, 147151705, 1390271172, 1407831685} ,{1504011173, 311973073, 729740456, 549698044, 1728091602, 1181433533, 1712103090, 1024203786, 2013865371} ,{789656121, 1532489500, 1985201651, 334381427, 2039853264, 1363484040, 1507982237, 1631948744, 1898167933} ,{1020659653, 992332641, 1392926910, 201528127, 1002485876, 2030966133, 2077953134, 977483083, 982489344} ,{884748458, 1954692058, 1143105253, 797041199, 1570621621, 1430281733, 2038371017, 1908972443, 1224603813} ,{302551675, 1210275820, 597585105, 749881289, 162375867, 567511822, 613704332, 1823574961, 1778982460} ,{488636826, 115805701, 1834992293, 1879616792, 1248014043, 589027906, 125593973, 1084039625, 1405471086} ,{1857251720, 581788166, 265693550, 1914299648, 2008867304, 2079233711, 719077757, 1340499886, 36453791} ,{2035243529, 135711369, 595565872, 98096583, 1120263474, 389743775, 2069768286, 17922777, 791867955} ,{880247878, 1144191909, 2101315523, 677909956, 1666547319, 621356787, 487976277, 184995712, 742805361} ,{693297400, 1353155473, 321620458, 1722890184, 1334430293, 1110501383, 255067521, 1740990734, 80838442} ,{649369396, 771303660, 544534580, 1410948744, 922831317, 114527642, 719198270, 85487526, 1537155777} ,{1060638312, 1941310016, 1413489040, 1024728074, 1442871675, 711093148, 2023599897, 2141829210, 1766973275} ,{1515412932, 817730869, 1356492600, 787952879, 1768482575, 1575826531, 1986069320, 92828617, 1413870012} ,{1215593759, 450756585, 476932560, 2077312622, 1614685945, 2065408234, 1257278231, 670897600, 1379871785} ,{1494331310, 1177786922, 943086307, 234263971, 1385474020, 1728512787, 1089726108, 711853292, 277331909} ,{1392205185, 1624793300, 1685045080, 626682316, 1313818785, 1674392397, 776885194, 1858011051, 1844630923} ,{1241683810, 1956443584, 1158289587, 1839293468, 1607528848, 448112427, 1753069514, 581474044, 1833509662} ,{806644288, 1199003036, 1051312530, 1936765235, 712705913, 1775670994, 1933066235, 723321141, 566917696} ,{1366890918, 1891202667, 1337256391, 1157838442, 975392537, 1198042481, 1025315705, 1335848857, 185722890} ,{890570477, 1019621018, 68516289, 2004706227, 1914933621, 385798804, 362983437, 1555539477, 1048372257} ,{2020588, 148218098, 234085709, 1616933050, 957195228, 1006504351, 1062925192, 385915774, 177879613} ,{1426044866, 307469715, 1274765643, 528005453, 1999255551, 452599106, 1760601050, 985536909, 473201627} ,{571996431, 1694539803, 1219289987, 253153080, 141564953, 527973411, 68131652, 1517797190, 684077760} ,{4089460, 1267704778, 1927469727, 669078896, 336744600, 1266273467, 88409643, 960258068, 859647735} ,{1454189573, 1765053021, 111440316, 438445857, 1841651344, 504467394, 1438115281, 42479691, 860415811} ,{1385530160, 1554119095, 411682048, 221777406, 1619979578, 1069183539, 2077577274, 997863913, 519107932} ,{1699153120, 1469330044, 787228978, 192118935, 436622732, 874626452, 1769599908, 431752426, 929754477} ,{580915735, 1168639926, 653627232, 1676352531, 17606875, 428448507, 1255289531, 270633554, 1426123684} ,{572406283, 451301028, 1532324219, 1318647501, 889329945, 573081376, 902249008, 1861853807, 989300389} ,{1366928713, 65071747, 445113631, 2081389428, 860189095, 982511068, 960228216, 1315812823, 721777154} ,{855282597, 1008980416, 1693424271, 1974833570, 1053856959, 387508960, 1557796135, 892734011, 1922516061} ,{1627687897, 1249432136, 859726281, 2041354672, 1811546610, 357618825, 1271430604, 2025850690, 28757045} ,{1609750685, 505040316, 1526203397, 2009947720, 354614667, 478498488, 70238738, 1770615797, 1560260238} ,{607490313, 1517498315, 2144639518, 676331277, 1612567047, 553257429, 769883412, 1695362271, 52822611} ,{1633370849, 1864363319, 319651983, 167280003, 596597101, 111567516, 1998590270, 541515232, 1695556958} ,{781466856, 930350097, 1247815388, 262684162, 996365691, 227339968, 2027239858, 1485885494, 1979544321} ,{1597537431, 560420790, 703230222, 1162973169, 1623799131, 1182398695, 1900003414, 2001210527, 1674994723} ,{809432062, 1911558605, 598499478, 1203053715, 761913121, 1843005748, 812440925, 1455570303, 574586062} ,{2003083993, 839052212, 1768621741, 2119027772, 2077319190, 1533837185, 1768649638, 1285252034, 983235884} ,{1733668289, 1733479570, 785512611, 545664515, 579632051, 2093063310, 1126682276, 1869312636, 1643359546} ,{550073486, 1020940328, 326276729, 956344007, 1641686044, 1472659702, 25594198, 732637673, 633643582} ,{1737058099, 832381906, 1839209338, 410862950, 1168589983, 117037389, 617130246, 839112458, 660152258} ,{763143038, 197069718, 978883137, 744390083, 1452490745, 1862704937, 4813862, 957453596, 687885257} ,{1107999863, 1526608667, 1136319420, 38738390, 739734379, 1460931262, 839741843, 66951292, 2004791615} ,{24306653, 233963182, 1869265992, 1797086234, 592452074, 1621320224, 1731300643, 1513253556, 1780800247} ,{1399149137, 1209802428, 491743351, 2137492327, 1296843790, 397977683, 674573709, 720275523, 1366869904} ,{1960404809, 1212262556, 92497241, 2095493679, 1644742015, 1492488514, 1073364814, 1075570900, 1268292200} ,{205106255, 84902564, 1825663685, 1917828095, 1216865686, 1623728110, 183617023, 28201037, 884872776} ,{1355750050, 125342824, 580556021, 2081387029, 1364708640, 724572130, 1400697599, 1483768687, 1597008876} ,{566126314, 1983364089, 844609962, 528140663, 874175064, 1140974785, 982139699, 77017586, 1491464003} ,{491779761, 1788740584, 1750761753, 1341750767, 1902316506, 1723991850, 73622782, 1530731158, 1151812236} ,{1933379580, 906078824, 1289097735, 304753684, 1237845910, 229395971, 242441504, 649395887, 1762176626} ,{2017687826, 1332558434, 184545932, 1729718437, 1329519341, 1270612789, 706199097, 1160426206, 1567615263} ,{1141465822, 1071498298, 779828262, 1310503589, 2051893245, 886733625, 1006342405, 1495154659, 1421214932} ,{2040524274, 473487262, 47917015, 1959535919, 1506315769, 1262542319, 1646136668, 94897897, 1150978958} ,{152574817, 432164437, 1836856849, 1290899048, 848147554, 56640704, 99045685, 1793103970, 1294302988} ,{338152651, 1714101842, 852076758, 1486240212, 1467837863, 629380773, 2027657858, 561811597, 949081257} ,{1555148403, 1581313352, 965981525, 1052945995, 215304072, 1359133958, 275536275, 419637387, 820050263} ,{1695543744, 747178567, 614333712, 1425451715, 1256208416, 583234986, 1054446561, 1163769601, 956263238} ,{564007739, 627436674, 129081978, 1535714391, 667500713, 322659783, 338273601, 1524692813, 1529722599} ,{1249949260, 1177577887, 1308045218, 873474632, 1092145621, 740095646, 1693256836, 1998821657, 70435494} ,{1176144847, 1480021604, 1657854416, 1160942176, 382682068, 1819990184, 1450354581, 760692255, 1727052514} ,{630659393, 1755069452, 1744102163, 25888156, 876513365, 782719388, 181660189, 928674731, 1732593215} ,{538989767, 1591992414, 1728076692, 591056637, 1453674867, 7481625, 114845388, 1409854210, 2033967717} ,{898948673, 1509282426, 346379934, 1878823035, 829168507, 443031114, 1694557597, 963605329, 1458274283} ,{180604162, 1145420961, 1221631393, 1837014566, 1236820011, 1296957869, 1734063780, 1230092243, 1128410273} ,{371783242, 186487574, 751764346, 566606963, 898290441, 271111804, 1086371944, 615873512, 1971224900} ,{1651207110, 1002815378, 695388996, 1926366286, 1310490902, 1498634713, 1566731217, 1055887813, 189310891} ,{65591999, 1626100372, 1538992347, 586281105, 1121457004, 1495768990, 2089215024, 284552576, 592366873} ,{1452008625, 1742746599, 386325086, 795055151, 981908199, 1771414492, 1054820202, 640616445, 1294563355} ,{1395648846, 1331719094, 337451308, 1438873994, 412754592, 1487487092, 1105321014, 407766545, 806907213} ,{890576393, 257808890, 1464386445, 242721044, 2035891216, 1341971523, 1316818959, 1821943873, 646591584} ,{1860228638, 297451314, 451804933, 1988386832, 1404892172, 1579106573, 1294982003, 1857265678, 540100820} ,{1569432360, 1995157523, 378402504, 182662787, 1086570997, 1445856197, 1697713935, 1199075645, 1203765433} ,{1375257496, 709498455, 787922130, 2091665799, 184178031, 1946145249, 1073138934, 1071822078, 1741775586} ,{71998578, 1846098692, 460798388, 1710095048, 1236778289, 1606150825, 435516511, 591296628, 1888817928} ,{864249650, 159357303, 1538417668, 497993832, 1287916962, 930611749, 31587550, 1560680013, 2137543328} ,{226979604, 185518397, 986172090, 1951202289, 628386628, 535572265, 1335592709, 302628835, 2133079271} ,{2035646982, 328700600, 1035287026, 1727927912, 421390127, 1978456168, 1201820826, 1951535717, 1563713726} ,{118520498, 306187848, 1205467161, 980708163, 1698061499, 49957309, 6866097, 656182482, 1683145995} ,{737426782, 697983945, 1349516951, 1493720637, 2016841640, 654632936, 690898694, 607661000, 1804160290} ,{1569249813, 1743836086, 1322687688, 2006330418, 1985828068, 442979375, 630773995, 1248688608, 260296867} ,{112789397, 379037483, 431231167, 914325130, 1243635451, 17438647, 1176746917, 180341162, 1395264409} ,{557219963, 1327973817, 1067038003, 924643499, 33637077, 1455069377, 235641174, 197923994, 1467602232} ,{390318551, 36955168, 29154131, 622214316, 567017270, 1081459681, 1905501072, 1243779129, 1098062216} ,{1763420152, 1921459938, 1043940644, 601534021, 1118356122, 445269426, 1532341439, 2105323982, 596241093} ,{19534197, 331316872, 315221016, 519240802, 1405111101, 2357356, 2032382859, 1848016266, 1402550502} ,{728499354, 975793248, 1931831282, 174867479, 1241751571, 76573380, 1905030599, 1548216234, 55630795} ,{1026544882, 1466461003, 338415224, 1277894676, 665869890, 1321918659, 1915853158, 392597706, 1172705787} ,{1419809276, 184750538, 623976327, 1485990276, 1958296602, 1405804209, 1098312495, 564709991, 794280464} ,{24677126, 1132929577, 1959865013, 156892604, 336568774, 1768537677, 1018754279, 1840438071, 873531455} ,{1731923676, 1012776137, 629151427, 149141968, 547214809, 2124589768, 744538467, 640172781, 430528518} ,{2136415121, 1698711388, 861317833, 230544824, 241953998, 1058340278, 276150371, 1315008200, 1929184775} ,{316102222, 1395077279, 262250682, 1841882836, 1672503168, 1499189866, 381779323, 2030382620, 961869251} ,{1383857382, 1956502665, 455404718, 871424074, 1223196027, 1686900946, 509234481, 800566188, 1801991320} ,{792784484, 356053313, 684956279, 1122199497, 1632604420, 508075473, 1902994234, 1959390068, 1805811355} ,{701224791, 500555698, 781621049, 923639142, 634438473, 1436243027, 1030555266, 1207189893, 1607786381} ,{1428130620, 675308854, 710803313, 746640754, 81062387, 175463137, 327268668, 1491090179, 1599290289} ,{1759153113, 1323637604, 534563244, 757110797, 958362747, 2103798385, 335104721, 747198327, 1448462609} ,{467839354, 1859641396, 52174687, 641502644, 266763354, 1051652528, 543089761, 558795410, 1144926784} ,{1855063272, 578905768, 1130891034, 1868674055, 1618489654, 1218123811, 1885663706, 1852936852, 110968297} ,{1149161932, 1192820641, 282892703, 17298209, 1398164005, 2079104153, 951070620, 1831907139, 649622324} ,{557962833, 262213297, 1419386018, 1083200236, 701805104, 1462790676, 477912552, 56937384, 1111373749} ,{352101689, 394661457, 1473359864, 1900622860, 924763324, 1237793507, 1791751413, 521890589, 1708469259} ,{1838068504, 1651037690, 916337184, 391937133, 1909585076, 1437210443, 735436076, 1852521424, 1690699307} ,{194374089, 1205128519, 1297143464, 880572513, 523952397, 2103465229, 1527298275, 1956630852, 1127621787} ,{1394062762, 1329184628, 502078330, 1312610413, 1970842339, 1056011026, 776719565, 1577629477, 1096549475} ,{1646693765, 1836011884, 455269114, 1613234647, 33897579, 1796539978, 1759215404, 1427005985, 71316396} ,{1837205461, 933183040, 1453244271, 192356302, 1492045580, 397535311, 347739271, 1251763563, 1376050880} ,{386308051, 1874776915, 47147179, 1512550109, 1877273082, 773201456, 1494513587, 1741089630, 194149888} ,{1110768505, 1017581281, 821786921, 798841270, 458451084, 1165683216, 2131784713, 1448580991, 1132563743} ,{879793454, 724645654, 165929573, 1561344742, 517003146, 1919956498, 1426680081, 1169380896, 1825706750} ,{456107292, 64343412, 490668837, 247118768, 1789635425, 1193196048, 932043679, 1961012945, 1640151806} ,{1260088601, 1104909141, 1471812133, 911714227, 941843690, 771252313, 450254547, 1505744412, 1703323334} ,{1614871920, 1323174442, 515135863, 1939484356, 2077093552, 757969270, 1323588442, 1690976766, 2129496778} ,{1191619289, 1879076389, 1101303957, 912322501, 57427428, 280893632, 143997727, 1613542573, 888551898} ,{1886786406, 1259722451, 520647209, 1500105856, 912908291, 1243592303, 407512450, 283734675, 276730243} ,{950935619, 1672930809, 543155018, 1888854465, 1854435446, 1232729160, 111699812, 990766072, 648673862} ,{571671526, 13769862, 1321616932, 361020681, 143490108, 457929922, 1376089824, 308885972, 1323870206} ,{1262657593, 46632423, 470589183, 251624283, 1044085499, 1000566636, 1838377778, 1540927343, 826883476} ,{632643530, 365112619, 1908201135, 394699464, 1307483759, 1217429207, 86236542, 579265039, 997366813} ,{1100756143, 549154416, 1621128238, 71980241, 1620541401, 486012313, 699404652, 1115678801, 1337428003} ,{1639661790, 1380769514, 1470337468, 724670183, 1792126207, 1880629711, 1993233075, 1637651342, 1433696602} ,{2102597298, 1756320037, 1466233611, 5230675, 1704941746, 805662618, 969503330, 739646333, 694446253} ,{2010416569, 2121030964, 1676841892, 377653063, 217040210, 236269159, 1486773504, 286779066, 760912631} ,{1900535152, 646003116, 1542553876, 422783616, 189257842, 1269306001, 1205670958, 1933534063, 1982127114} ,{1282428388, 5301415, 1269907315, 159543298, 1633765417, 415444938, 909753222, 55878035, 1077337193} ,{1246478006, 1728719196, 1812701560, 754808794, 1604149461, 1936696988, 1993851073, 1452825289, 2062028251} ,{665456592, 190005672, 262300956, 666487720, 1199427434, 1245586053, 1222210208, 1097707606, 1459474866} ,{761045263, 1155289911, 1679447053, 1858860483, 939601513, 1211046616, 826084008, 281240892, 2129657389} ,{65350539, 1079361835, 1088380714, 1781820556, 1495505477, 949114964, 1185687206, 2011075128, 1650622641} ,{236045694, 908347007, 468017637, 1366238845, 607762885, 181485920, 404497565, 1761535609, 957593063} } /* End of byte 13 */ ,/* Byte 14 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{2053962982, 47231560, 707464806, 1440754982, 854145785, 1690576528, 974756480, 1099590214, 1972527763} ,{1179156304, 147214654, 243690464, 1901695259, 893655729, 164006677, 2011649227, 1949237306, 855667766} ,{1255063327, 1720074542, 1806961978, 2147294975, 1610713514, 2032421654, 451369305, 378702983, 1800290017} ,{2100697439, 2016875562, 1254057470, 491610572, 168265475, 1174257276, 413570694, 1850099382, 1882962703} ,{70159011, 730088128, 275408840, 1143481649, 1531102209, 1886648480, 1762565519, 419209535, 847507960} ,{1354067547, 1435854617, 628331216, 1029804677, 603223758, 1713550252, 1317078816, 139330362, 769206496} ,{1969244327, 2026108905, 82377259, 1374504282, 1202213741, 2088492667, 103024102, 1553194211, 1839711773} ,{960872402, 1923446900, 134351781, 1697209305, 1259751052, 1801889924, 2080670684, 1398471295, 233415187} ,{16773300, 2056522622, 206440911, 880601510, 637682863, 2050651967, 240331999, 689111128, 1255487427} ,{763697278, 1094098520, 1644884877, 1656699063, 765656513, 612056108, 142175051, 640570154, 2044695512} ,{1647255860, 558641235, 1630791303, 473895512, 147903142, 2129585331, 2066552443, 72724704, 551946225} ,{678244729, 1056441603, 1875956983, 613326796, 95619232, 1785423589, 69942721, 696221026, 496186967} ,{1344526200, 2075166010, 2101154545, 338441266, 1893050456, 1604581840, 1539550901, 2054456280, 1173990758} ,{116582286, 177050982, 1259972790, 1975282617, 887122679, 780492276, 557303764, 62239382, 1080087260} ,{10618569, 294382313, 832193609, 2098762597, 594695482, 1257033455, 2002844635, 860896063, 615504407} ,{1733355497, 190746464, 1477953713, 1543481307, 944675114, 1409433140, 984652114, 554607973, 2146426694} ,{1803249737, 1500651968, 787752920, 1575602530, 1956968669, 547392013, 1992030086, 1522637094, 779800592} ,{23981132, 2076811417, 770109739, 2087770426, 1651591451, 1209424562, 561559507, 495360228, 189725183} ,{1269851545, 785292728, 1938414361, 457619226, 73505434, 877715599, 1523839719, 1964384789, 352338399} ,{803725389, 1204819141, 1851856953, 895837372, 907139270, 1889882896, 760901210, 1703263772, 1599487138} ,{1104044250, 532249156, 302990234, 805422237, 1618715523, 1219575492, 1164629000, 397802087, 83620422} ,{1206171845, 1885013276, 1688755417, 656088384, 2139364122, 1999724042, 1203600367, 986447532, 935114027} ,{2024629155, 1325772702, 1534725763, 1360938044, 389325471, 80281586, 3388655, 822881103, 423439632} ,{1846892746, 1841079335, 1964554457, 2093440367, 1694104168, 1838911968, 32445080, 2082084589, 1931742203} ,{1023304719, 2026278445, 1538664881, 662236340, 1694058784, 1560747611, 26035576, 223627159, 1508415096} ,{1146960703, 67556472, 303904361, 647506336, 1486122491, 2135726420, 1703883761, 158521202, 1645388435} ,{1295669297, 863231105, 279930498, 2052048054, 2022627408, 87624304, 678758721, 1921856684, 1779474298} ,{1034735955, 749312963, 1005599667, 49348964, 1026850309, 1997768114, 1809619380, 1563465828, 1338630728} ,{393236449, 354728221, 1551392376, 535156303, 1782658956, 1183463105, 1674051201, 56612565, 152231823} ,{577577340, 1951084268, 675457477, 1350773566, 1118050002, 1748932199, 2103440672, 1903059519, 344604988} ,{64074528, 1695399172, 2115211589, 730360282, 51324078, 384482899, 1873268392, 1363206533, 970066162} ,{762801865, 1294636212, 541424563, 1125567460, 1340256317, 1564526471, 1174956304, 1143829483, 1262038545} ,{187634498, 1819467708, 918863567, 1205635013, 1593474755, 1539047269, 1346564664, 1163834110, 112390993} ,{990137207, 484497018, 1779204908, 733830636, 1022839298, 889568272, 608862554, 2023967295, 1925063480} ,{1621145066, 1608087901, 974280980, 1215342784, 209138751, 1684932222, 599303067, 1757205987, 345735932} ,{2073778764, 825086918, 1803323788, 1076149936, 181632759, 528761080, 1618257410, 1583501666, 271652270} ,{1242102071, 1419113446, 1534957299, 1756120101, 1350056261, 18010080, 1528653771, 254723640, 1260902987} ,{1632090499, 787890744, 847094156, 1513556932, 1176334278, 109284887, 1560463722, 1182142994, 118339136} ,{296988503, 1749809655, 1297440506, 1701465398, 2086245339, 1967986226, 1252890421, 1370327933, 855011286} ,{1931547604, 1979228928, 693150619, 877838332, 996131164, 1732683996, 1804698138, 31445619, 877751015} ,{316999311, 721290776, 1738133105, 1192503018, 1334218136, 1791457023, 1433245694, 46621931, 351532819} ,{514998457, 671528123, 1386155042, 467510913, 1251380063, 169156116, 897453672, 1441892316, 317221023} ,{1080628233, 1742181684, 1228924846, 789265377, 2085271149, 612342526, 1658850056, 1926654775, 874071066} ,{1373507477, 1478056851, 66622206, 1160813405, 429083361, 675206687, 581919142, 1171890070, 1092663660} ,{783056016, 2110269458, 1652209881, 352014249, 147024343, 1406703123, 1481986335, 1443931767, 393267501} ,{227138689, 1549368527, 1919461341, 272921185, 2078269363, 288753980, 1818397448, 495475537, 1582730378} ,{424607961, 1072233732, 1574593684, 1814615482, 498169810, 1030995128, 2072738848, 1948150344, 245813919} ,{2003783763, 1476288665, 602578333, 1650598641, 1094836546, 1097086895, 1829210655, 473504000, 929110146} ,{1209509021, 193411236, 1012020328, 1588181357, 1549717702, 2024940908, 1162284469, 265787, 153760306} ,{1976299175, 88910908, 37578481, 97866037, 716031764, 1007818932, 973332043, 79322369, 1654636576} ,{2096960117, 67448134, 2025415660, 754078890, 2043642750, 1158873193, 868190766, 1094457216, 1231085995} ,{679071207, 248804495, 1341031214, 2130783147, 1500020896, 762748849, 2040436758, 1201881917, 757239678} ,{1345971879, 2065655255, 536299142, 1244569367, 1505729467, 1487190915, 1626395057, 353208550, 308364651} ,{1675254622, 913896104, 1402048842, 1669975685, 2038696102, 542305351, 245874328, 295429482, 2560501} ,{147384171, 2047446480, 536068378, 1581952338, 243907531, 1029494379, 68371163, 1880144978, 1518592071} ,{1794571694, 1940008555, 492448905, 177242462, 1125618718, 667663368, 1117760185, 322897309, 1978972301} ,{1393242441, 1674287857, 768358135, 1645877545, 737573797, 1294027185, 852319532, 488572250, 1640315333} ,{553353823, 1765931429, 11660846, 836604860, 1729478866, 1894380138, 1281363080, 388127782, 1808382507} ,{1852327139, 2013908301, 615110662, 1998706563, 657971592, 1258219620, 174733795, 1400678823, 489856551} ,{539908872, 54908812, 2050312360, 645546279, 522457763, 1154430895, 1109390635, 1867872292, 1157192649} ,{758343900, 1273195830, 83663248, 1453129385, 63425343, 1007775465, 267812747, 2111014200, 1472054020} ,{41597918, 1199131232, 1790983531, 1368197654, 1305029859, 969823596, 1022048543, 1332603211, 2019734741} ,{193412841, 1527534113, 1009593424, 2054620765, 736011031, 1567213801, 568442776, 1426169064, 666348588} ,{370901673, 1798109349, 4090233, 1550685228, 1882873788, 916674487, 434873439, 1291105342, 1645735283} ,{1620341197, 1565765830, 394062849, 1345980504, 383884462, 676523864, 586776226, 1784853919, 1799295055} ,{1541093000, 1020222426, 189542826, 1940018468, 301937866, 480839876, 1739235787, 2082905219, 1613030504} ,{1409300456, 315462455, 1905047590, 913825439, 1662176735, 1540194213, 614360339, 591425138, 1240429572} ,{206375885, 1846776778, 1666913767, 1348824321, 1505481593, 163932483, 1931659945, 1209614252, 223123146} ,{540689428, 2042964742, 1742668719, 1198152380, 66988343, 565233254, 900591986, 1527190279, 1572581210} ,{204547661, 481573607, 1502631699, 1539172915, 1051813196, 517356108, 33066873, 775980233, 28600838} ,{1525513632, 1525899195, 966420744, 669382040, 1311631070, 132975965, 1456750068, 1018512975, 418280617} ,{44144578, 1759815490, 2083589271, 1639554073, 1334236932, 1529616523, 1870346035, 448062049, 2143782856} ,{174886359, 176004020, 624114494, 2045407846, 1667490599, 7419612, 1910007818, 1162156745, 1797606650} ,{772177357, 1304954740, 2108908922, 742985966, 1403367613, 712570118, 1644761016, 1885306528, 1602503787} ,{1278435762, 2026910501, 453771861, 137234245, 758024723, 1794206349, 792167280, 2007459344, 484910682} ,{1518441948, 1236478619, 1777238989, 1275143525, 523542038, 1564017396, 800336171, 28658224, 943008389} ,{173213194, 1223586929, 1190987018, 1563114710, 983782451, 518717217, 687313445, 1859425916, 2050970298} ,{1801057250, 384846761, 1156162738, 2050321637, 1342494747, 828218547, 1617905523, 1806177251, 1891448715} ,{1135927065, 762374911, 955135935, 141992480, 498197048, 901671350, 1713292786, 186585099, 325518081} ,{1480501201, 843969448, 198605596, 62650911, 1361623774, 433168210, 1120738546, 314637514, 607255550} ,{1002628957, 1433862149, 2125385750, 306494024, 1657745422, 760819467, 463234566, 788607020, 1381732965} ,{173370981, 628167527, 1886245597, 1369158713, 1592100140, 674861866, 146805391, 351409185, 2005977837} ,{1699185666, 1652452602, 962900537, 1610142186, 979414033, 1701982713, 1763070958, 778318986, 1351247482} ,{1474805014, 351826325, 1033940113, 1383213730, 1262699790, 1338883123, 887453567, 1824224258, 409472935} ,{1322593431, 925396895, 686486841, 1248129027, 1335998258, 1643605200, 111225021, 1316574452, 16203741} ,{1904513346, 916842785, 179558724, 861887735, 1124200955, 894508208, 725474310, 1681713550, 1609343036} ,{1163472308, 251507358, 1839790535, 16860893, 748919994, 703285509, 412524601, 2127335112, 1292842893} ,{853241817, 1275840731, 731259575, 839602516, 1487629538, 655083548, 1360973792, 1651751877, 587393834} ,{164035084, 1920136810, 1071892179, 715750135, 1226850776, 144737613, 269614738, 406636612, 225534503} ,{1883267612, 2062811221, 242074410, 1320073353, 2017900, 544674098, 1984173758, 410864173, 1254079810} ,{1195818278, 1617007866, 788822309, 1860156588, 1907915364, 983723104, 874015952, 695257546, 161336670} ,{1186597515, 232078677, 482591246, 580445430, 1704253714, 1427382282, 197755812, 1799721084, 1503848064} ,{712651504, 2069718651, 1917464945, 1980594557, 2025492889, 102303707, 485794277, 1488444013, 752918208} ,{502774330, 1413166149, 1242510631, 705926431, 1718505148, 163453310, 111560149, 1633248020, 1146868341} ,{1714685004, 1646232591, 1930545537, 1866046705, 117173232, 1475589569, 232973182, 1999254456, 1135421090} ,{93889833, 1793414129, 1629066329, 999854615, 291215224, 1008933411, 309571994, 247259323, 526797008} ,{1274372295, 1461504131, 1929829141, 205028245, 916049561, 478602916, 1332821391, 919656764, 1023666423} ,{317907617, 874911214, 636011034, 699860429, 1875117485, 441521001, 588852866, 1138007020, 1476815028} ,{1896183732, 688382083, 793890418, 1676877227, 1570228588, 1313207965, 871138542, 1586727102, 1229622467} ,{643697366, 2107258225, 1751403144, 1884346345, 451315460, 378725594, 974604092, 684241454, 1715069504} ,{1331697883, 1027851559, 755696240, 912476097, 662105655, 883879847, 1197957570, 1566932771, 1294795191} ,{331944797, 276104181, 712259863, 1162144654, 397907939, 1985284602, 232158617, 758766591, 1313455638} ,{1645549468, 436199825, 1737234898, 888225126, 575582367, 719250970, 411755235, 207239882, 717796773} ,{724409989, 1620785348, 952459641, 822882010, 1419405607, 186125846, 1531560689, 1253203410, 2054526237} ,{1515111844, 1827931010, 2079624515, 475198572, 930193718, 1727138594, 830880913, 1580254623, 699227147} ,{50256343, 327729207, 120603863, 1426198848, 1343700040, 632262034, 1402550704, 1364802831, 2047135055} ,{1784689130, 117851013, 709744244, 867639156, 538943155, 566951641, 618685352, 1939589471, 1041202846} ,{1543397211, 2066224407, 504954488, 1999028493, 1183389141, 559667742, 523819143, 2137814340, 435154122} ,{977423096, 1951358311, 2038231140, 766350131, 1021499048, 1179763498, 1410565551, 1453768194, 2015918733} ,{1524432275, 920771873, 2146994600, 1930453782, 1988146503, 1997833312, 588759482, 1939136328, 20958600} ,{1002867165, 23627334, 1653215375, 1785438808, 1070366154, 209494738, 1365143852, 448645001, 1020714586} ,{127933872, 1041232888, 1382619324, 480668424, 458989687, 137462574, 690262329, 1205444250, 1110826023} ,{411888676, 568421462, 334159125, 2066138632, 434685047, 2142247343, 2053978749, 166130064, 806036254} ,{182999526, 1119107450, 240983699, 1324457052, 548221311, 1699302710, 719969574, 428222607, 1149962675} ,{615902057, 140912590, 12947946, 1084295214, 665886702, 1750707970, 473941980, 918415521, 451815452} ,{974491791, 860025408, 568748980, 787176138, 426239412, 1700069287, 725344006, 1177550830, 1087838445} ,{2082172198, 2000232735, 262961619, 1376726232, 1019424918, 1056724101, 482366, 2020802498, 97395679} ,{759900403, 391138937, 427166286, 1495485084, 34507089, 447296343, 592735113, 536408518, 1779499390} ,{1827684050, 834036989, 1408757823, 911420542, 702145735, 1268089395, 428055623, 1083723621, 430216650} ,{347096371, 434248406, 292701482, 527367301, 1182602995, 1493880682, 2132075255, 1391753697, 373366654} ,{1199678507, 985819999, 1543755749, 1914968654, 981516280, 1317551798, 867864255, 1074345253, 2001359812} ,{324563158, 1856089768, 5889067, 1165536483, 1374122986, 1527744298, 1403913845, 1211037549, 1916627485} ,{1197931554, 135632113, 541241387, 1382839516, 63330846, 1219594622, 329131634, 623398350, 918883247} ,{21470942, 292413785, 1603032893, 121631405, 1301675547, 499647320, 1428156331, 802892695, 483792192} ,{2073718055, 811074407, 45733368, 841495064, 1950335923, 1695885823, 1921779524, 598368960, 1740906787} ,{731734551, 386140465, 244933680, 719226902, 2048195633, 1311149378, 1718766680, 117658564, 289028218} ,{738140197, 304312790, 1227693512, 840438257, 1714602129, 1871324821, 680104007, 1354021131, 274327092} ,{272455637, 1778044437, 1566533755, 205760325, 670017394, 1360562417, 768214362, 151633663, 939068159} ,{2105332978, 1226467401, 1206346184, 708014941, 932702676, 274852494, 465902717, 222826443, 1176835968} ,{1394253901, 862266812, 371359483, 1268698458, 1822766561, 481652129, 1169148879, 752122168, 271033614} ,{1654012533, 804185515, 450012890, 200283445, 1699700409, 1621145499, 1922721430, 601072768, 200834752} ,{2045072895, 2088150306, 1829854283, 1457177283, 1283050836, 835503881, 1644802107, 1633115822, 926208663} ,{2041441355, 1550073679, 1268852756, 1149799326, 1926296205, 298698293, 412689077, 683235602, 1901912109} ,{1452795726, 975977424, 1961887853, 1408655806, 1441894132, 1111744014, 928532354, 439424388, 2147089055} ,{1089255335, 544818158, 139780044, 575464238, 75534701, 1774191526, 163058104, 947890631, 924888986} ,{1480336749, 451080760, 1031947259, 1980702730, 723911996, 899330539, 609807388, 478779956, 126842282} ,{748102470, 1964678939, 744556120, 464901026, 1187823792, 629101251, 1705486688, 726974144, 1362879571} ,{1678533972, 1777881271, 562222158, 1482406230, 587542275, 331400922, 660980200, 1251063160, 149670169} ,{756659785, 1185288134, 548923710, 72087863, 1531091786, 1457328121, 146233050, 1094648119, 1040590762} ,{1158503120, 360897282, 1713137246, 1803834985, 1673922528, 2102904837, 654998006, 71347130, 575209209} ,{1295975507, 1384588003, 1889567694, 371894565, 588233791, 1682615852, 441658687, 968933301, 356481318} ,{1231002761, 1496493816, 162354650, 729443386, 586031580, 1173017032, 1040288877, 43219732, 1859394862} ,{1980980152, 926185984, 1743302126, 1541082007, 1122901127, 1521333119, 1103461352, 295877075, 482473239} ,{955657538, 1236882971, 1149680474, 1974550531, 497063309, 687654468, 1767994869, 174636321, 543314779} ,{917850063, 1059715872, 422485280, 1103126911, 354554697, 1060590897, 1876639566, 1090548157, 1798223942} ,{631089550, 1116952054, 1301914029, 407206436, 940769337, 1969812535, 75608730, 1454485204, 861196245} ,{1452851818, 706523931, 204613424, 372461346, 869614162, 2130761077, 1284208147, 674607771, 1814617448} ,{5121397, 643636981, 1709453638, 711226051, 2042194654, 319721945, 2134421869, 930546038, 1501755176} ,{672867283, 329050863, 1951952345, 276234979, 1326910189, 415669861, 1913842076, 1324377170, 1937120003} ,{202169609, 1330720298, 1094231, 1187523411, 1591549745, 78844929, 939866147, 1610189536, 598486678} ,{1593726013, 1617766748, 600526138, 890382912, 556957455, 308343367, 1244225236, 25407350, 1621806706} ,{2115294126, 1851168725, 363913634, 1537784180, 946784021, 1755060875, 1360255063, 1383817455, 2073060647} ,{1769611580, 1850095943, 981822255, 32981891, 1406391866, 1230731890, 1624807917, 1289660220, 955010789} ,{62898996, 1633809737, 656103315, 259534016, 1785160612, 1182298359, 134924707, 963929645, 1984656148} ,{206550281, 1971307393, 1410582401, 306335212, 510902435, 2006388977, 386223712, 1459647101, 107400132} ,{305444016, 1876260827, 1495609619, 1301552183, 724693976, 1339858692, 167665618, 1028120852, 1653341866} ,{810633683, 917250261, 1945021737, 630955948, 203075248, 955651019, 1503169698, 2037581836, 1207908523} ,{848134437, 1721581625, 619500381, 1948205273, 1465663662, 1102612025, 770457084, 2108042023, 1049971501} ,{767855270, 1389225082, 1639779228, 936272154, 873418163, 46733914, 979177264, 1023056841, 622722451} ,{1622360503, 1644301157, 2077329058, 1001269010, 52537008, 1398597528, 299329735, 359632691, 1107781572} ,{1963692884, 1014682289, 287247515, 628119621, 938670009, 756886509, 988704878, 67324549, 1881474058} ,{1409012777, 2016019416, 809783005, 1062103395, 1467222056, 1200742397, 67400619, 931138996, 711826678} ,{346587872, 1535175520, 1469900206, 1187467399, 1357156973, 554876306, 1919938593, 1911353262, 902931696} ,{860246050, 659257649, 238795460, 470993290, 1098142732, 1431913355, 1348238521, 106483990, 2134615159} ,{1977408905, 2043615476, 2083688656, 421980098, 833284872, 1888393271, 456383370, 1335987742, 580437527} ,{274903284, 10887014, 461664905, 385416229, 1888603403, 344167594, 1844444800, 1887344196, 798536205} ,{1952642234, 5378665, 1351520103, 360596346, 570098842, 825192436, 633206021, 246348421, 591251077} ,{794869814, 1147801987, 1587300784, 702076496, 1639526185, 225352458, 1293679059, 141914643, 1911978607} ,{1350526391, 1464170213, 1864030456, 291714648, 1466921687, 25118335, 1318368127, 581905080, 1263254909} ,{1204439269, 850764449, 260633949, 654937715, 621304619, 1395578346, 416426622, 723936762, 582675117} ,{682606075, 573529377, 1766038076, 48008562, 1403860906, 1945229986, 1729452675, 1053312861, 1142575587} ,{1207125239, 2107972314, 728892976, 1410108311, 27863092, 1578104298, 2109291754, 27611103, 340321753} ,{418311786, 2128783487, 2009061830, 40545021, 1275298114, 479135809, 660248291, 990574125, 1465468082} ,{1032465875, 854079965, 268590853, 1638310829, 254447478, 2103329644, 2109405854, 255773686, 809171782} ,{295434558, 1001525797, 390042397, 1442272585, 1186160207, 683375337, 1976222129, 1716962481, 692597521} ,{2116990624, 220636372, 772681306, 506388011, 1979073578, 2056670030, 1126588619, 4707812, 1118432385} ,{2001649166, 1440728422, 2144235211, 1704970640, 423139100, 1384396308, 371206315, 1038177518, 1992512251} ,{1840466677, 1117302074, 1317254032, 2044195761, 214964464, 151016190, 190656542, 44730689, 1165676364} ,{765760357, 300200860, 1331775680, 262103690, 140428866, 1857545835, 144763726, 855681480, 1988423995} ,{1680990669, 693340820, 1595922263, 1884922248, 1592607987, 1071361727, 1972564935, 657653609, 181313218} ,{329608727, 1149953213, 1750761275, 1236305918, 863386662, 1786209435, 588717061, 1137629129, 1431584108} ,{1166615418, 1443474655, 873561715, 1436555000, 1758669297, 820410894, 2089656455, 168304329, 518646776} ,{1570747727, 775552314, 2020790317, 1108447973, 133220041, 977409886, 175458304, 1476534296, 1334419955} ,{2028973475, 1794501025, 1528099569, 1958623357, 764311526, 547489397, 1014355733, 851979574, 1969357228} ,{1120618770, 1115181062, 1452765926, 206436673, 2090280905, 1796706316, 394421986, 712634072, 981865521} ,{58230883, 1130348426, 1023631904, 179215536, 668817531, 1432090164, 951897076, 1564038477, 1946765164} ,{530312954, 1102621701, 1550286273, 298706408, 1804039486, 272767073, 703198521, 159961108, 459562910} ,{1469504198, 74272864, 625140379, 1701467465, 1768133695, 1492681677, 1928062892, 167651994, 1065210403} ,{448263481, 125174331, 273797479, 575459719, 332625564, 75656295, 1857930511, 1106300046, 589665642} ,{1894279021, 1114166693, 1814724031, 915074715, 1515067755, 558543218, 1404485540, 423504776, 937344354} ,{1820522170, 672348563, 837717573, 1848709318, 424687459, 1108907627, 1201219180, 1428282605, 461659459} ,{579165384, 1466474521, 1843306625, 1932325856, 467864101, 1693263366, 1894909541, 1376481699, 2026204971} ,{1782410963, 388002121, 534383198, 1194473317, 513615808, 86280831, 1685300913, 2000482368, 672440438} ,{334476456, 1865211550, 1287162453, 1092357929, 904897657, 1318648061, 933009814, 121559400, 862351867} ,{1771703003, 350254825, 178556227, 110115503, 1815243588, 52281321, 267631947, 768063598, 1331672964} ,{1444350308, 1842029817, 1189430526, 1975525242, 1420101200, 48017021, 1279998751, 561158038, 1008947522} ,{1617086588, 1915681855, 871122525, 1153726133, 1097803897, 61211361, 957072731, 1999866683, 719973105} ,{1556003280, 1723646726, 1991565772, 419272068, 549987515, 1179045578, 1364151455, 1552431794, 1509874940} ,{1737009974, 769676279, 1496537280, 743916317, 952176121, 1664961341, 1021174993, 1782369440, 328346817} ,{1369848304, 988076129, 761232346, 447512952, 280215449, 938125952, 1721378451, 1026146500, 2068547821} ,{1828564067, 881821386, 3029176, 1275301980, 175024641, 993443239, 1695433507, 1433519529, 639660447} ,{641784965, 1956076027, 1380512917, 1429559172, 184419607, 120838554, 693974329, 5552151, 1363592360} ,{308559391, 1626044904, 1125128759, 698266105, 914409732, 551681476, 502543513, 1918240246, 887270740} ,{211183725, 1800565239, 2063129591, 50758846, 1913060701, 116038200, 2022516221, 1597735998, 1139104737} ,{1758159517, 1687469901, 757006122, 100148532, 164309250, 749991523, 816631636, 1532314377, 1453360318} ,{747001327, 741183270, 1901756588, 465252769, 498483987, 1231352082, 1685531724, 304813395, 2002412034} ,{99769295, 1297203233, 1548359217, 1679837562, 1113363122, 725413068, 5974455, 1140875591, 1895172671} ,{1990136511, 1717110409, 495778638, 937102266, 1420415484, 54194989, 1443178033, 1827854325, 1317537382} ,{424562320, 2064502358, 1104441300, 1296496008, 1487348586, 1915905546, 1078119453, 167534923, 1750833910} ,{2062210997, 1030408348, 1609149743, 1542593231, 813130404, 2133995599, 754877101, 1385760428, 1187107713} ,{628121683, 1994223382, 1233513778, 1933074867, 509576109, 2096503340, 499737609, 577050698, 92860000} ,{180145037, 447698391, 442069751, 1547098357, 34442940, 62525081, 527560155, 825581391, 926605459} ,{651133337, 1453302019, 1014954777, 1092878397, 96719959, 903282804, 1128302525, 1234843425, 201395672} ,{1490707205, 1417063090, 1592453271, 75478142, 1913874827, 1432186480, 1091188003, 15684888, 619894928} ,{1610987700, 843204504, 245855872, 1027136709, 1895907014, 279358809, 1114447930, 1710821757, 974996852} ,{2059598006, 1613595137, 500524940, 1442129920, 1090934327, 1536980929, 928949256, 687500951, 2031425000} ,{225678989, 487952917, 258741365, 548153521, 1025544487, 1375332912, 1065865034, 1678339312, 1726615916} ,{1985137678, 1678015370, 136109382, 256291874, 1718259389, 768468818, 1799880578, 1559068954, 528340161} ,{803104767, 296881645, 226385969, 922413585, 237434617, 767414253, 1353655351, 1629750218, 1599757323} ,{1678380485, 798709815, 360507423, 1598775357, 224888524, 748147557, 1924016878, 710621085, 513042056} ,{1244456524, 295916957, 822522224, 1992594854, 1016404843, 289853903, 1043195905, 16863552, 151509141} ,{937730913, 385537740, 592131110, 128471720, 1221382458, 1453196291, 1264628414, 1189090328, 1664194302} ,{807554425, 619400901, 2077667200, 1290996382, 2144960111, 873627764, 53910565, 1808998596, 1082313873} ,{235980159, 1434784671, 1672127774, 1309763106, 1640099074, 1244817433, 484822396, 710578538, 1176648221} ,{683361108, 1490336408, 115035162, 838513426, 1915160486, 2110153479, 1416590909, 1309074249, 275868493} ,{1192661407, 616207365, 1579588792, 747564412, 109871519, 950395149, 65834484, 1154019072, 994455032} ,{782054332, 2100872555, 978964205, 430912003, 2140713785, 968160080, 640108177, 2032235241, 1536805633} ,{123714077, 779431896, 1691324302, 1705851722, 662272038, 414009422, 1761169369, 2055810084, 372235238} ,{1686163241, 523111513, 780980341, 1167021634, 465702985, 40328561, 874239527, 1612657076, 1619259926} ,{1198350249, 1531721854, 1875088304, 957646898, 1398512749, 2078666908, 2006548345, 792225889, 2087601329} ,{316257426, 1737561844, 57415450, 1734154006, 141261752, 123633192, 1302634021, 1385606281, 1911157383} ,{361118689, 906947544, 184401460, 1466909259, 2015926416, 1746923644, 2474914, 821797945, 117684400} ,{1011777680, 1760542666, 1788080876, 57969949, 864222070, 1406420478, 1081085344, 109086808, 1685035146} ,{1721734165, 2137139572, 1625609137, 1869693478, 631706532, 2071403526, 1544862997, 1006868882, 494050788} ,{945235555, 1734637715, 137292771, 700483455, 115457915, 866934359, 1873975210, 1118333955, 1334653766} ,{521253252, 1511611501, 779068480, 1587597131, 500945708, 470179404, 1044002418, 1007447920, 148338936} ,{1671347839, 1406149181, 655520399, 1336530389, 1298708811, 713803255, 2016301995, 93953779, 1158450806} ,{150414666, 1329681896, 1647557492, 72089669, 574618202, 1921281857, 652986295, 1454157201, 173037364} ,{920908054, 700615417, 780210003, 1101875050, 40843099, 1881536151, 1302788356, 216402396, 1683089178} ,{1434695892, 584295967, 418949303, 1341766752, 1709091913, 1370642593, 1335088766, 1518730778, 962787776} ,{1859812862, 826352051, 646580549, 1698903957, 685280194, 390165914, 2013261032, 135269137, 315813449} ,{563211148, 875255502, 1335762272, 156607178, 2117393224, 1537125059, 690394319, 1955078631, 466748062} ,{1798541855, 539435905, 1083932192, 727956427, 1527006743, 1520558991, 1732852551, 1302574040, 1071105829} ,{1867600906, 1998289056, 203777605, 1725645795, 1252900131, 1988873833, 903536134, 765163054, 340847968} ,{1000609970, 1008277394, 1610684162, 899418403, 443956723, 1731805728, 1863545616, 1965164355, 1366761934} ,{1365641618, 6549635, 1964924299, 840389589, 1582087757, 1461310351, 487098035, 133034365, 2072396641} ,{507344798, 591024749, 827600300, 746635559, 1097154116, 198705705, 1294924215, 1904024819, 323371247} ,{1913842535, 307402358, 763822257, 1905889131, 1867038948, 1248170206, 971207779, 1458592226, 2000776763} ,{908109660, 518900668, 840649297, 1040662779, 928290197, 201062287, 1521896773, 1903863279, 154563867} ,{978414468, 186242967, 1808048982, 207830591, 820204003, 702791362, 1776820370, 659685285, 1126185065} ,{981155418, 1464092875, 817721753, 1960139916, 1149274963, 905940114, 2003881266, 1903026567, 1234834941} ,{1691452101, 1594580609, 574009314, 1858646584, 858114974, 1109946556, 1903514937, 870293809, 1519879782} ,{1793772743, 1923780212, 1923025301, 1825531957, 1761015005, 1618082299, 1249322314, 744024053, 748987310} ,{1955478094, 1144695945, 914943165, 1248257415, 452348481, 1963517165, 764583069, 1947020982, 2133123149} ,{576329442, 639032506, 738589900, 197921141, 212494508, 652065566, 1154612046, 438052019, 2099285766} } /* End of byte 14 */ ,/* Byte 15 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{623694405, 1576373966, 1333266485, 556951101, 1726684937, 1894951836, 240536613, 1868627657, 750542710} ,{932365212, 1941826503, 210053829, 679449911, 444705015, 756505220, 2092760649, 913351325, 661902963} ,{1929178848, 88070402, 1629527298, 1008803811, 545007285, 271851264, 1963876085, 858318093, 1640460323} ,{745646681, 678084980, 1593850193, 801007321, 368953126, 524482824, 1839777934, 1660323597, 1613200967} ,{998261774, 1717761052, 273040053, 202919778, 2009234144, 724124337, 1844957635, 1167797341, 1707879257} ,{539830140, 993919068, 424095015, 1421312114, 808836672, 804978519, 34978351, 442702050, 1727632778} ,{1148563976, 173677147, 931575638, 343124253, 1763757545, 1489666673, 1806056673, 1106868788, 87863281} ,{1952692826, 2119693665, 397427817, 1267453831, 975050416, 2080496270, 742995946, 1651650344, 826469525} ,{1180947376, 472776723, 716540193, 1070696145, 2141884209, 1777432059, 2134548437, 1481645, 530752898} ,{2090866867, 1416369259, 1562271221, 1734971150, 1991419255, 1436185132, 200612966, 161518059, 1827513752} ,{1356721310, 489809203, 1120922108, 868606867, 2130992480, 1088698568, 1598871068, 1167995452, 863200613} ,{1217540477, 1847827289, 1054621814, 1459019899, 2035035504, 1099565575, 132902951, 446136225, 268275090} ,{1267475706, 1780905869, 750049432, 692474695, 530953143, 1981534466, 700757463, 769332130, 1335428721} ,{111318032, 738100576, 1398464447, 461688115, 238454066, 1987629453, 2098658509, 693525278, 1928455501} ,{2023828708, 1242811753, 803293610, 807992545, 2079435593, 1393465164, 745077532, 1835705597, 1114816087} ,{1625876192, 1273875864, 587847281, 2038002420, 963024328, 1993309385, 427059908, 600041264, 1182506615} ,{609697238, 261744904, 573434050, 2017386349, 1558372740, 1981086153, 1275918267, 174950585, 2034010947} ,{1256455938, 694562660, 617843347, 852912486, 1007199265, 40054717, 281701649, 1720684550, 1478701496} ,{818354493, 695840909, 1219808972, 1886965492, 2027334647, 1805281207, 1339579826, 2062357647, 2057128747} ,{655922590, 795408217, 1227883230, 1284627045, 610268199, 1639577134, 9786139, 1173827714, 1702667052} ,{935957010, 77850882, 1804597321, 1134911600, 713885212, 824007752, 657201149, 1871258839, 2099054083} ,{462135060, 729185802, 922878382, 842377736, 1085186960, 567438531, 1905390902, 390239285, 1485474122} ,{1873838146, 1127654877, 1606786168, 248941252, 613026521, 1116056620, 1216166351, 1004399035, 1872099594} ,{1160306136, 1524105440, 560926123, 971643977, 116837155, 688501563, 642073123, 1069040931, 923788835} ,{832611462, 1764365391, 1132150603, 1197177532, 490307881, 1687699744, 970952422, 1072092549, 1081443130} ,{32356163, 949858572, 536991104, 1964869656, 640991935, 1260656462, 310509707, 460326888, 1338870983} ,{683850429, 1637220381, 1224629461, 472049729, 208216590, 216756725, 1685635519, 1922186762, 1897683235} ,{348844362, 2017954787, 1710395809, 1440179156, 377281966, 2110607625, 1508554052, 53191779, 251290661} ,{1883668674, 534425835, 253038320, 382633723, 1373461623, 1378380352, 1059095385, 548821250, 107632362} ,{285579840, 1661979019, 1495663715, 2019225327, 1115843880, 1562026075, 412083677, 1552873493, 1968202528} ,{1770536234, 419721996, 1296154629, 1220908083, 1461850575, 1303272867, 1591628402, 878586507, 1905964574} ,{1970777095, 162253888, 871468365, 1218856069, 472510520, 1083094640, 492385241, 1153895417, 1894008224} ,{197674996, 1521006880, 1306614002, 1944672497, 338774254, 1022323902, 1163549001, 1644804529, 729460310} ,{1240608732, 1777704116, 1859972532, 1144205192, 2019648099, 484497601, 1797772554, 2018223351, 346902285} ,{164843902, 1521309788, 806246401, 367716618, 472861404, 1678362787, 970438702, 242811437, 65754854} ,{1836454440, 398441208, 188238862, 1161318694, 100738657, 1903165801, 1776988661, 646746845, 411248173} ,{1911133039, 315895767, 571452649, 339498607, 1041098698, 1687108668, 839958074, 1548751834, 2002274109} ,{1549925069, 474050656, 1225158911, 66477488, 1135456567, 897944523, 1233000875, 1245539917, 377395049} ,{549090279, 188267360, 1208508391, 350302210, 1453498746, 2036195227, 1247459338, 1846426724, 1236746122} ,{698519150, 2012648994, 373163725, 1386365610, 2063854565, 694425468, 989108270, 2113919539, 1216608544} ,{349638450, 1559632922, 1204006550, 905087157, 269453240, 363517641, 1184149558, 275886447, 924512260} ,{1454425180, 235306075, 1528881164, 1242984821, 374628587, 1873740909, 1839439487, 1350800277, 1727209590} ,{1645454635, 844036361, 771523782, 2114642386, 1862025304, 1878739974, 1617203344, 1978117945, 1706554935} ,{1281262495, 1083839792, 1519819117, 67703478, 958019931, 98527007, 89470294, 1539627428, 1278143790} ,{488814054, 629005870, 1659054576, 1716051073, 1480736089, 1961159504, 1402145479, 1117925973, 337842722} ,{129452215, 1221114015, 1053649477, 452452082, 2042872755, 638934828, 1689015746, 505541665, 255320437} ,{2125640473, 227831340, 1330505958, 455781282, 2003118149, 879721545, 1559474329, 876332908, 407911443} ,{2093544668, 932191912, 483514845, 594079889, 476762545, 1058444737, 1938420287, 667252065, 780095234} ,{1619033686, 26876258, 1322889829, 1887749761, 1147307076, 104459562, 1501077347, 2113991966, 1051654982} ,{651596215, 1218477215, 138444696, 165906641, 1479590873, 453547628, 838696485, 409233783, 1658481596} ,{1957139408, 6820540, 1285371791, 1610426559, 1067084882, 180815206, 1866331563, 1279333059, 511944129} ,{728040149, 415816584, 1098115823, 1666199827, 627772545, 1127370173, 2099400633, 179790329, 1531213571} ,{516698880, 1357728997, 1246064564, 146702961, 1612462082, 1176884389, 1800522669, 482887668, 154961210} ,{189055551, 2045336815, 1401775793, 646333416, 1773336617, 1012799731, 80928246, 1262253624, 1761044578} ,{468078052, 1451385679, 1248266258, 182115252, 2077418927, 428564902, 1313016907, 474178611, 945088772} ,{2011210692, 1326989915, 639889324, 427921474, 531316562, 1911556361, 78218780, 400544901, 498500029} ,{1442592104, 1304632483, 1006579034, 998122390, 1953100449, 1229221976, 146854613, 195103999, 1777439867} ,{1043419359, 1972952867, 104808278, 1249010864, 180676689, 1500381909, 546042251, 91528435, 1759254472} ,{2086720893, 1542710676, 1542027557, 101100800, 73246755, 60739087, 124904506, 701629317, 1653037594} ,{2059384794, 1421233364, 984074729, 1280773371, 1839235874, 56203592, 105410013, 492260590, 593202844} ,{2064769331, 1374313871, 1692141445, 425941322, 1815621091, 1832760611, 84307590, 1470175489, 119943664} ,{1758258613, 1187334475, 1002396145, 1288533007, 540322311, 1967808331, 1494779235, 228489363, 782473581} ,{1870222769, 482265175, 246971803, 1692548824, 2063900854, 794054847, 720671883, 1762436212, 588054721} ,{749855844, 462345846, 1452798263, 111055432, 1081039412, 629393030, 158768879, 2095595789, 1313948527} ,{1688205188, 2115892338, 1740220991, 155126005, 390123985, 236530334, 1013190280, 1947993054, 1211564969} ,{265286467, 1990204023, 1444550849, 26694449, 507495903, 501489389, 87913557, 1821285804, 299542601} ,{140787320, 1832805588, 698573489, 1072132313, 1430962973, 1783530026, 718583074, 176383143, 1691351420} ,{1073186198, 1123839274, 1557814366, 349626413, 114502448, 533475740, 1371097857, 1695103263, 1990822500} ,{115204669, 544761534, 1754266567, 953176599, 755650111, 1470807541, 1346858110, 1555518084, 640715511} ,{1359813466, 2012524319, 1134801535, 905129095, 494129328, 33351053, 371509390, 1848842632, 1135524595} ,{465300593, 1562256642, 376501981, 483020328, 463989419, 647782158, 2082513508, 613134059, 142018316} ,{1903528651, 164271783, 720810015, 1300733133, 1697699815, 679152761, 590480731, 127697236, 1223509053} ,{1312144878, 1026190564, 569029338, 1428478695, 777138676, 352191492, 1734252504, 1250987048, 1198286668} ,{892427493, 278282995, 1727241769, 2072274239, 1449830756, 1361797743, 1572735106, 18578399, 1335954134} ,{440615119, 328962504, 1321536103, 885534010, 362377937, 67373030, 224213719, 699685026, 761733272} ,{1567339104, 2111349449, 602851150, 729326034, 158727986, 1992277492, 1194044216, 2029397476, 1166425787} ,{1498886532, 672892576, 860904337, 1531362629, 196475867, 1436772478, 143285605, 2018083579, 1530256079} ,{772816716, 669601055, 1435788407, 386924668, 1675607051, 2117102122, 909551029, 1034949449, 132258738} ,{1742707174, 1304184770, 2017809202, 752033573, 731439768, 49992165, 389699209, 1582006491, 1358853502} ,{564539503, 1503947770, 888792639, 144001874, 854856607, 1628481491, 1499940123, 2088629025, 660707360} ,{751450407, 511040999, 1501792396, 1851751331, 1204409729, 999652633, 400294698, 1496899822, 895243156} ,{1123671418, 1764230137, 1452143120, 223580702, 1064101988, 1156196776, 617977519, 2047398035, 1958125411} ,{1732891133, 834739840, 72860791, 105178465, 1476709530, 1624092314, 1007667034, 1255733556, 607706056} ,{572054193, 417661196, 341899698, 1624462613, 1434681302, 1613306599, 1817351853, 236770188, 63321548} ,{1485962385, 494574183, 2023271468, 186403683, 1937922796, 511358260, 1844296077, 1366261156, 1030234662} ,{1048176683, 1233443342, 592709344, 1939416325, 1464683559, 1188704050, 1250404750, 428032839, 1207497883} ,{1496175721, 1267876031, 1670830365, 244204076, 1435563283, 851588711, 406406675, 1712574475, 299291305} ,{2137209481, 1655701787, 2048640147, 417050503, 1935463294, 1015052651, 727004078, 2095334358, 987347904} ,{1034287027, 103559609, 740851574, 1964456416, 351021320, 493178426, 1212484219, 1432712757, 1674932955} ,{471862088, 655214154, 1855716137, 960112916, 282999493, 1349055882, 1598294943, 722832233, 1425872582} ,{1532916026, 481591190, 1889341417, 1041774901, 180412427, 589315675, 1146210019, 533227212, 1282273091} ,{1200855927, 1230128190, 678747224, 1703865082, 1045841412, 1561447892, 1420730650, 132867531, 32970135} ,{1096523282, 900544424, 1202994655, 1941296386, 981965211, 731509640, 946966281, 1717232370, 1454675705} ,{696552467, 1316795580, 730931831, 461556851, 199490213, 1824621493, 1488178679, 1980803778, 1438944173} ,{1656938245, 949686795, 1841179400, 65013682, 510269102, 906629321, 1631233320, 1641565667, 687340395} ,{1616179419, 57857682, 1356787093, 981564358, 1808352652, 1532304350, 1894222394, 1821825073, 419094600} ,{1337961661, 194187324, 2144678125, 2068227097, 930461166, 1980758572, 592419166, 357641836, 375789794} ,{999781854, 2005997300, 2071118448, 1503237388, 1462209106, 1011828557, 1394855906, 1659282915, 344589174} ,{1367194909, 1770735920, 2114444232, 2069263141, 1212322531, 111101696, 1646061909, 312025467, 1854732894} ,{1480760338, 1384028653, 102910806, 1990540710, 831986114, 651014170, 948797670, 584719702, 842082583} ,{1215634535, 1380875055, 616436881, 1611408938, 147375600, 418532056, 40453932, 738256603, 211108336} ,{714928015, 725007283, 339265806, 255364029, 570851106, 2085395213, 1005241852, 1192019569, 1120011898} ,{463082219, 220803371, 1795647808, 1014383338, 377358783, 2098834059, 953681705, 1217465653, 1347110175} ,{258635001, 1213319305, 1569834515, 1903644329, 1176629448, 1230171237, 817152035, 1080770205, 1335230788} ,{712681856, 301957019, 2062890200, 2132725152, 602896255, 1770505287, 47348979, 2008778827, 651442942} ,{2099268612, 798357706, 371687376, 1064454809, 744633594, 1352103880, 2045934665, 885248588, 1293790047} ,{1248353038, 900384495, 1571931285, 826524522, 257271361, 248712567, 89017516, 365871662, 343964644} ,{1596504514, 440252985, 1757927432, 757384175, 496282068, 1384714958, 1058528832, 1675389272, 1198386011} ,{734379844, 1590410957, 1473809171, 401511282, 149256487, 1762874741, 964177194, 1567287817, 1426066851} ,{1209426549, 1724147252, 717374954, 1272975570, 113491033, 119731954, 31223676, 832550554, 1325336892} ,{226519560, 584000264, 419012978, 267505657, 1951906006, 504718418, 27613167, 1009460325, 2135465804} ,{1669002361, 632587710, 270350337, 393034161, 1442624405, 155811066, 860061558, 2024747898, 519214276} ,{2006614560, 429030435, 1390412528, 820281603, 711947406, 1874198986, 1378687977, 15618264, 1135977743} ,{1666047384, 1976342206, 1828165825, 2000321746, 1819032721, 1822722351, 223845361, 2029350052, 249766744} ,{563544522, 181379154, 1610342986, 624897122, 444239408, 198886936, 1111235829, 1202332223, 345545677} ,{2114803350, 1914228686, 453276007, 1617646398, 2056283039, 100305690, 1491910839, 988103422, 1925195206} ,{887137523, 29715229, 1156928470, 2100847625, 230233641, 41339643, 1464582142, 1802992240, 1746670004} ,{446841453, 2131229253, 464739903, 201612804, 2103207278, 1223215468, 895607948, 747143481, 1949081242} ,{993626994, 392984121, 1957366046, 759130662, 1304553858, 1548453226, 2059652511, 1219368289, 1844142598} ,{1520270743, 157628288, 1143800224, 1378414201, 676027572, 2095139722, 625249686, 1803821905, 581560817} ,{1895154779, 768137462, 1696798866, 1646646804, 1765567261, 2144942754, 301823808, 252518283, 1999308409} ,{763637327, 36766227, 1889514945, 459141058, 1514547313, 521112439, 1599751409, 865595706, 1855833910} ,{901837104, 144765444, 1996667026, 959216315, 1962773433, 258619187, 510285940, 780616236, 1260061021} ,{1452523776, 479104145, 795757155, 395460956, 1354565931, 1688707741, 312083628, 1145225145, 1490608042} ,{151941225, 678183193, 1504205024, 785169909, 593851764, 1161874676, 668052158, 336603289, 1442408254} ,{943686876, 591044590, 216421918, 297492301, 1134287813, 1978741101, 1778720362, 2037172186, 1065522642} ,{400203457, 2115921421, 1846545327, 862912470, 1317604620, 1653727664, 1697237374, 1772117942, 912111705} ,{1881116928, 588328209, 821817512, 233134312, 945688464, 788930743, 867814299, 1969508772, 1544850582} ,{375394715, 1932445068, 2052145845, 754884286, 2111249226, 1264186006, 106442190, 1791371343, 1806391803} ,{2082158485, 74355558, 1408257582, 1491243827, 12795157, 2029954647, 1449392732, 124962912, 1257548326} ,{310958507, 2105715759, 1149082880, 2118174233, 905547884, 601015911, 2119843317, 1054106525, 1073481512} ,{980285810, 306296941, 1183566074, 1524792948, 178571440, 1036938731, 2001753524, 1253675944, 169281589} ,{1295569796, 446231718, 460558309, 50369727, 875414555, 1710769207, 613408363, 157799347, 1571804060} ,{372054694, 1708810177, 677222408, 1552355259, 703818572, 934345940, 350201329, 248668788, 1475777955} ,{1623619186, 163068185, 215683664, 1077797697, 1521450416, 239046564, 561514096, 451771034, 719085460} ,{455534502, 739894668, 846841248, 1514111265, 613202136, 795205957, 246396616, 998375720, 263770634} ,{201458568, 958949131, 1818146048, 927941975, 1042087222, 1747432091, 240177175, 307009052, 75640595} ,{1463122238, 688361860, 1632933011, 1420921818, 171339668, 2109004359, 787407303, 1467451809, 624099176} ,{1087440718, 516545516, 170815648, 721259762, 172151895, 2068676547, 198398133, 2047684786, 99707314} ,{8447147, 1657097165, 1733312898, 1104155119, 560024497, 258276293, 891296919, 684827015, 1930947777} ,{108335497, 996860340, 2065276253, 185390124, 1764866075, 636820369, 661273118, 1886679815, 567657228} ,{1297689457, 1279956830, 1364876095, 1212102391, 198740737, 503642973, 436981778, 844410404, 332693825} ,{2038986513, 1577838319, 1056158530, 754988416, 896406238, 112573763, 1338880260, 286380325, 1333044036} ,{285797031, 1657771797, 576845867, 1069650196, 151508775, 591375195, 1980477284, 805979876, 1587402736} ,{1721668291, 1291030566, 689604422, 1509928817, 1541850104, 1869562670, 1108999311, 155411417, 1165333561} ,{2092222430, 1027521356, 1523136282, 1041185155, 745865101, 1368985329, 2010359058, 122811120, 702881209} ,{1794166491, 1755726397, 2105731655, 1382697939, 574499062, 161400484, 331254568, 2125752299, 1870595222} ,{515562622, 2146368842, 970677627, 1770759807, 8451017, 33186642, 1173810667, 2111545350, 343151968} ,{561052955, 1672080195, 867236304, 1236341911, 104463623, 938840749, 1505009683, 1031711069, 709732390} ,{1782501796, 828174977, 426721676, 132622590, 1538205487, 1922286266, 398166577, 1818625388, 1760680060} ,{2010735114, 1060842892, 814585764, 233381596, 703067536, 1860324155, 1774085045, 149322742, 865552941} ,{1892409542, 1288499043, 1445910755, 1892168289, 450506037, 840904068, 2010587790, 114720739, 302979596} ,{1045551010, 2043137520, 1784283225, 116548157, 1397019516, 603452843, 1895197277, 1278590210, 2023254766} ,{205283867, 299376678, 623301715, 1505951998, 1513705178, 979146601, 387974587, 2121780169, 1737449760} ,{1677906907, 1262978848, 710060693, 1640768789, 2062106662, 1427330707, 962114222, 1518777176, 1101037921} ,{752618347, 333140253, 1778477211, 1294241475, 1465656208, 821343108, 202516935, 686489636, 795888106} ,{1897955693, 2025872766, 712717465, 54335998, 175687155, 609968997, 177105769, 958716620, 1987619985} ,{999884088, 185799845, 908412653, 155693240, 61431720, 694833873, 2061085168, 293306890, 1032516132} ,{980438216, 1237306951, 1433348619, 165509002, 558681924, 464656846, 1914963817, 1320632766, 740705550} ,{2121921807, 2081744488, 155512665, 1286432194, 1899565744, 2090691132, 712260957, 715018312, 790315876} ,{1826151921, 1801923097, 1240517123, 1079309268, 1635602897, 1270144107, 1547585962, 752402546, 2016507100} ,{2096352584, 1935550214, 471618738, 398823795, 2036662031, 1631323539, 1940764964, 149052787, 588629185} ,{957741628, 50347253, 935693039, 2041333822, 452026898, 574131142, 949495504, 2138507125, 240420316} ,{471997395, 1444926501, 169258861, 211424487, 864627036, 742856501, 16633268, 1064596302, 706766695} ,{1631614011, 1820456318, 817070747, 506000147, 225876955, 283023773, 2113876797, 1591504368, 527843885} ,{384976635, 1632450706, 64163192, 1169498074, 857955675, 960837972, 1016360090, 1887513866, 841570916} ,{782219909, 2071220844, 1729437791, 1218836398, 924099653, 616327870, 439981213, 313223697, 492348812} ,{592319342, 51832262, 1160038315, 1413952052, 1101577479, 59268857, 817171668, 1986935427, 1157769356} ,{490783086, 1642905791, 1238254441, 1959363485, 1029948330, 2115376805, 1034743471, 630835629, 1775523501} ,{2073376685, 1427427467, 1224448751, 316655656, 957597649, 1238616539, 683558780, 77410083, 1252626667} ,{942772140, 1299768876, 379797251, 1476627980, 986489099, 969798627, 791987008, 2058425986, 910285098} ,{1467190183, 2022650563, 1263668680, 2022895381, 2072987240, 1831256546, 1266973983, 1901577034, 763190184} ,{2085525969, 2113967701, 1887632250, 1283817443, 1970911839, 202917606, 313372535, 637266144, 29881771} ,{965899345, 1083664079, 1643177913, 1870512294, 1796306769, 1282470220, 1194313336, 1023005897, 1116132158} ,{116307337, 1811931850, 2021051542, 1202609345, 862388364, 1449101735, 976995023, 1182783634, 1951652645} ,{1056635722, 782140306, 663436797, 1890750346, 1165310023, 627066168, 1302957097, 1112592815, 239031626} ,{927486746, 425758963, 33306712, 502072567, 576918086, 745383696, 1490409336, 1698416217, 1807833510} ,{398617024, 1555078179, 316780458, 1701316316, 780626992, 1845058950, 744751649, 259537233, 2085653331} ,{1995646847, 1499352462, 814104952, 988428003, 1104059416, 1552495342, 485479947, 1570129298, 339765797} ,{169408965, 219447517, 1261649893, 761719166, 578073278, 697025291, 51323400, 743755976, 2035745604} ,{1206829295, 1263714485, 1398599955, 1096974643, 1712746268, 304453320, 1292041293, 2040624561, 1105901854} ,{260127816, 863858753, 1439974113, 508834300, 747485050, 343317288, 31322874, 2001109847, 798715880} ,{625788342, 994331601, 1028708285, 223131862, 1865217440, 990054046, 1326434463, 81002347, 441002248} ,{100320105, 548529987, 447657409, 922449328, 481881197, 1050288862, 294804672, 2107757881, 1999023768} ,{262471644, 2109604964, 1268946977, 2090081055, 712503475, 299752971, 197665796, 1591401297, 1980874121} ,{473030559, 1069794348, 1804789417, 352264191, 1764357903, 796859470, 452146779, 86638636, 1304537651} ,{1291407033, 1062867432, 108584141, 1432594572, 416560577, 2006739341, 1661083201, 958589634, 1584292758} ,{1959447057, 1366388017, 1942199371, 696109580, 1605246997, 358459000, 1816738721, 167625210, 472836350} ,{1970493014, 58270117, 1898184328, 197948401, 1762182949, 871836115, 1056170776, 1546275547, 1972605784} ,{1892065422, 139830892, 1390170557, 425996306, 31108323, 1839840006, 1390471649, 905698870, 787855542} ,{1589381342, 1535423760, 87337534, 107742090, 461111008, 1193888655, 1280089240, 1807194503, 150715479} ,{124663394, 1563172829, 2070485926, 2052600204, 1823369784, 875295547, 368268114, 1279461270, 301816516} ,{1782006211, 1886627679, 1787574850, 1202992854, 1295868003, 296557864, 2113276327, 1214965416, 441133378} ,{2001293427, 1061206822, 911878992, 5510510, 1047789364, 1112373399, 415174120, 2007683215, 1955712974} ,{938823309, 1819118066, 2029134605, 962353112, 2013860923, 309413902, 2135588104, 644394005, 177710286} ,{1651022085, 69923026, 1396641336, 851585293, 992508841, 28936208, 98023475, 280406165, 1431237595} ,{502654990, 615503300, 1333015030, 635865683, 1736051536, 1513315877, 481231926, 1111679603, 1669086222} ,{582509907, 1769050334, 783955896, 316380310, 1235747923, 169307496, 295201543, 535182317, 726311565} ,{940780193, 2138753706, 424940590, 1636359917, 23562583, 1057861362, 269437478, 1327308435, 1169617366} ,{510582115, 520838766, 570657754, 206954713, 1495509458, 1952747761, 2142053867, 208855136, 992913431} ,{1694410629, 585352737, 1493516613, 766938794, 559854941, 958676658, 943231418, 222059433, 1877998510} ,{1636017296, 474515734, 260775329, 1405981214, 876029068, 1619896598, 1626546802, 1051438198, 293277334} ,{1583505222, 1764709193, 833781425, 1855136552, 2131168941, 1425230730, 1408692493, 610474903, 92141625} ,{1431435716, 1786191101, 1409676558, 1259757277, 476861409, 855691871, 143816762, 1424169775, 1589014723} ,{131795085, 115284268, 867215619, 955043597, 668504420, 606026900, 655106204, 1155253155, 1874778551} ,{1981052478, 888978468, 972445438, 1714585678, 70507251, 2128345383, 549647101, 1629583375, 1110765805} ,{990453199, 402920411, 814519100, 30853137, 2006880438, 2096371256, 1798776078, 1334016792, 680981102} ,{1411651856, 773685347, 1421618125, 1641811615, 1286027969, 1997594115, 185589273, 825236093, 245576060} ,{2125313879, 1878366537, 329464400, 1728041440, 1364302836, 1027132497, 1795329126, 1743921340, 753930367} ,{1781183159, 1244334198, 511741551, 1206453918, 527572841, 513545910, 1405742306, 1358689205, 588902531} ,{1945764614, 60317990, 2142549890, 1277741415, 1637614065, 882538928, 545171077, 764808991, 403563277} ,{1822894843, 1360788894, 722599342, 1554645983, 1619603685, 1044898246, 1001130050, 130508642, 1788674025} ,{1630373533, 658152627, 484220365, 1880540622, 726980820, 1483386261, 72409356, 1425700618, 13237239} ,{1194395866, 823295297, 2072419418, 1191616146, 1610714324, 1049501838, 1490345410, 1743737076, 570909618} ,{1771274861, 397903512, 1402093714, 1439231385, 1944346073, 2032815140, 690608415, 660647528, 424629250} ,{2001509132, 1986486920, 129970454, 392289961, 479712397, 426965265, 517403523, 533321275, 1903662686} ,{1146922543, 1841081493, 1253332767, 389793808, 1558050359, 1761781150, 1710451188, 650129351, 364137445} ,{1143249891, 1448175865, 1310090100, 1497480194, 41687787, 81670762, 1496253698, 759043035, 1386635460} ,{1077265125, 1428134126, 951912109, 655156828, 1031841324, 1587962244, 825622053, 1762053480, 415122606} ,{1475209992, 1151813171, 1483573570, 1397711492, 2002127754, 1923979862, 1024928983, 1361108895, 988868729} ,{1233250265, 1318750971, 1977449552, 1123415536, 539520591, 1423984290, 405313227, 1733175183, 255031824} ,{254030986, 283326089, 1354067701, 1829580760, 724587941, 838660338, 845788218, 1318806519, 155631477} ,{1518339302, 1697908896, 1980789963, 1767122131, 305133842, 1381238505, 2034227398, 840394177, 1836356980} ,{1781826181, 444946336, 1177179160, 2095404854, 514641931, 2076165002, 665269774, 1218315339, 2128341027} ,{1081266923, 97834323, 1437063053, 271002834, 1290845103, 41513447, 26668976, 1979655610, 1671707463} ,{2035472916, 2007175649, 750466860, 1389727011, 1659756532, 865263399, 340250488, 2129632799, 807010870} ,{566571562, 2105639074, 1207481038, 1678096413, 1451077935, 2122087392, 1753240639, 957088007, 1867716409} ,{2135371574, 419539827, 1184492645, 419925426, 439862289, 1175398329, 1739470871, 992226627, 844202246} ,{1342942528, 147593141, 999593078, 1003166333, 97803499, 536556715, 274927316, 477445043, 252820281} ,{868033536, 791168657, 1787906707, 1492173736, 941136618, 57860190, 693913817, 927181353, 2012487515} ,{1275923140, 1125912544, 721415145, 1349295854, 1260381799, 679339445, 1020517251, 25747175, 714499376} ,{1413330743, 1848671324, 1658017672, 1912630636, 532811559, 1676128111, 105005192, 1016885360, 176901683} ,{622389139, 740432098, 820219608, 991002146, 643425508, 415221717, 352769460, 867532835, 1950401751} ,{113629885, 521476107, 60051008, 486126745, 1181321155, 1018576559, 1743658677, 1701207308, 1444892056} ,{376891819, 293369610, 2099432044, 1992329508, 232518157, 54083291, 1114135251, 636014529, 976280713} ,{285587548, 1934297529, 1051726845, 13475228, 654912532, 545851699, 753304568, 608812088, 1300821760} ,{2002817761, 1899840931, 1411171970, 751131711, 262067025, 1306228897, 631869309, 1496345094, 1241584795} ,{194262557, 1117782148, 123549240, 907246274, 700714331, 727667706, 513110637, 405535004, 988273809} ,{1964666209, 1240603296, 1300180199, 1502508592, 1324560031, 1519337764, 1197908711, 116872902, 1498402880} ,{620439676, 40723407, 2120842670, 1089137923, 2006576929, 1541556438, 1366549552, 1899335768, 53319222} ,{1883336775, 416420765, 1326194359, 832231417, 2137703310, 1367865629, 1813809030, 1271140080, 1692804282} ,{1235778087, 336095651, 1719843897, 2041324615, 404416544, 655192597, 1168403941, 1739757418, 1728236730} ,{1971823342, 916230683, 563763017, 66313774, 400014474, 440840878, 194720345, 535032565, 749623988} ,{1666464056, 1298083838, 89582648, 431015868, 1222872615, 285324689, 956086648, 955379000, 888489965} ,{1095061960, 633904426, 1470825111, 1446386706, 567235129, 250632740, 1383103652, 103306969, 1497445331} ,{1725568829, 813428019, 1364145848, 627001606, 1731515557, 1175846570, 2026341372, 1206272314, 665075153} ,{1744276977, 1458308952, 626727917, 1138983728, 1383551647, 525689463, 764859152, 12289566, 2023495393} ,{22936752, 1430548439, 1363077388, 1971846698, 226833052, 563527117, 843720350, 602802752, 1304342277} ,{871805714, 426157923, 841176579, 451348723, 514891717, 1624264476, 1990670930, 828355721, 332341397} ,{1180617979, 635801876, 1676295397, 989697280, 183714357, 115211401, 1743345589, 1345893965, 249522381} ,{263067008, 1331066833, 285396031, 1268352935, 1282246891, 2097986839, 1591210102, 926177764, 1387581403} ,{506059315, 1986443466, 1326003536, 867512728, 502189538, 1272451794, 236400314, 140527524, 882501634} ,{1167134528, 1195788996, 1011183380, 1959794859, 713414716, 1646285370, 1508725324, 1861237725, 598852424} ,{1196835563, 96278310, 135062745, 1142110828, 618344014, 2146992057, 1208976625, 1255839921, 286174224} ,{443913490, 501589407, 591048117, 999655871, 1125770220, 1419961509, 1060429230, 1702247464, 959480337} } /* End of byte 15 */ ,/* Byte 16 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{223523295, 1025489125, 1401138388, 1931651083, 397527729, 410385060, 1257503617, 1169402270, 1061978758} ,{853985131, 1580046827, 1667963498, 399395201, 1806173930, 314784390, 484047138, 15230338, 1586094947} ,{1775135080, 1726821872, 445741792, 1403845606, 988874153, 1105524594, 1562046737, 535006701, 908995990} ,{849940231, 1742252521, 922835159, 570951374, 110121404, 478405799, 648018947, 1095764019, 1766713462} ,{255906636, 1326048753, 1272245768, 619359662, 487957782, 806984701, 1097175305, 772219217, 2043042156} ,{1511139589, 1842257344, 1157944880, 613794144, 586330687, 132242482, 1326408376, 1867538130, 1006434165} ,{1771715722, 293277776, 1114254981, 2352789, 1472481703, 2035409285, 938732643, 2143402558, 507542443} ,{1626323495, 2071236246, 1593478516, 1497027233, 547391819, 965152111, 289307636, 966272831, 1390312334} ,{1469218138, 436474883, 1661517479, 1420804030, 640121693, 1854983817, 690150596, 749754592, 1666197774} ,{2105557366, 1241871219, 335127301, 1026308207, 916508955, 1149171235, 1543530104, 915569400, 596059665} ,{95581071, 818662481, 1759851386, 558725781, 161757618, 1966325112, 212644138, 269551968, 926407888} ,{2134033597, 907519465, 1123188558, 1928738071, 1487383156, 1985968806, 1072344570, 479675648, 31006491} ,{1066786875, 1152175584, 1618127045, 1127274289, 1557339695, 241929266, 30213249, 2083067293, 76646866} ,{331278639, 611387685, 1791243267, 1795098470, 2012913826, 388065979, 1548045992, 394288914, 1230667612} ,{2017238759, 1505421010, 747976240, 828271084, 1077265212, 692013262, 720519963, 898211644, 1192387866} ,{919641396, 1622821963, 842588455, 370355330, 1434920135, 871450745, 1074451606, 638542362, 1426178673} ,{470630360, 1208696598, 1761540347, 670177055, 1654102874, 1043975972, 537155298, 267424336, 1131007627} ,{1189942405, 631224685, 524459457, 336963563, 109004615, 751615755, 1872006678, 1614496508, 402924296} ,{1412329321, 433509458, 464771671, 664351785, 866077447, 46568378, 233724374, 2085786807, 887671341} ,{318607616, 2145276252, 2005794679, 1230450067, 1528855217, 1604266857, 1122178732, 2126177699, 677318840} ,{24826754, 1185534070, 643189535, 917054647, 909073401, 532599612, 1745269859, 198836014, 195235702} ,{1677458109, 647466595, 472949102, 542838531, 398863306, 1026723898, 2046331020, 1974106321, 1962783923} ,{1927406602, 2015085482, 1419545307, 393280857, 1226437326, 1555241536, 445714040, 237280943, 1465511543} ,{1658788917, 1919890496, 942058705, 1423687991, 1737126604, 1017191828, 1445035024, 1347422462, 1480277107} ,{453637159, 991779731, 2088517407, 2060655727, 597446066, 725632543, 9184917, 1735318459, 2030194070} ,{273768605, 2110117454, 1931204766, 566642058, 791728196, 403685707, 394050398, 213976054, 72591483} ,{2087133086, 87512000, 1226777445, 1413237485, 1049673967, 1260514, 582091171, 243137934, 320330596} ,{1322618066, 494697427, 1327873259, 199226955, 2001716844, 676026198, 876515725, 429317363, 26094667} ,{682474668, 1403027955, 1591219724, 1251819674, 2135800873, 2023155280, 883115376, 513232563, 130808376} ,{1062368858, 1293190485, 892879353, 505636402, 861660399, 706613020, 645564796, 1997675641, 269848107} ,{440663373, 260353135, 419535188, 102444378, 1634538519, 1501985325, 430711236, 1669682363, 1372272239} ,{1086793760, 1390142628, 2006248685, 203085511, 447618653, 1009764812, 793837542, 139990736, 2116608813} ,{1432210272, 1979585317, 1486034647, 1437044629, 1171224716, 619318263, 1591395802, 1094804463, 1110165701} ,{827669020, 1462960421, 1895910083, 422425510, 1878044592, 1173276264, 1914760120, 1899743115, 676263590} ,{925995387, 433053743, 2025316556, 1312518322, 1747947929, 860574634, 1079870340, 934563703, 1742973180} ,{77686912, 1464025476, 1343792219, 724142573, 1478595663, 577841598, 926799295, 2010375555, 238093307} ,{1348909193, 722357583, 527877578, 250689815, 120973060, 1646103736, 1669728188, 525278408, 796119076} ,{113463842, 1389151526, 1950883251, 1396266240, 32191395, 1564187546, 2047662704, 1109081023, 2113619231} ,{1571509129, 1602097237, 1991085272, 1229505224, 468365530, 2018436683, 1606754980, 667140981, 342240369} ,{1826555841, 1852188682, 1714016753, 547728833, 1845065164, 1320268320, 1251922841, 216930021, 1876513839} ,{297155847, 1233436431, 767115786, 275963981, 213454614, 1881051161, 216122062, 415069624, 497676975} ,{1294247312, 311385881, 1421041612, 1860941747, 1045978356, 1146889510, 2093372107, 1879880786, 1032265175} ,{1775943990, 842042943, 833453079, 29859824, 1257920880, 1294204370, 1454223373, 916746014, 292814681} ,{267693917, 2139648731, 797010463, 2053567896, 539903106, 1723833313, 1589533174, 852986191, 1422473945} ,{1007694134, 410133695, 1536574901, 756186168, 532855232, 1560679301, 1097711328, 1230980662, 316760365} ,{310236447, 1991956740, 862533285, 697713592, 436294701, 487509613, 2087769291, 1148110346, 678701174} ,{1739936348, 1334746171, 2012421120, 1118464739, 540298335, 1584761908, 169264264, 1488718976, 1737233611} ,{829499274, 1222320748, 409207839, 1928094907, 1251035516, 287750908, 1167488480, 1519473939, 611845455} ,{1209913158, 959324959, 532479039, 1888831052, 1951950396, 321106989, 1386459234, 759325226, 182948014} ,{524446385, 395582072, 474308670, 1184969278, 1424905550, 748896749, 1178562449, 1738840062, 1031059893} ,{1273118309, 1803058488, 1565530751, 1979930910, 1135055850, 176351074, 859562781, 1786458119, 402421326} ,{462022989, 71595233, 333130640, 290063693, 541239228, 876254828, 885434909, 946529116, 1927940955} ,{1381887582, 1480278183, 870549195, 88171713, 1742016131, 1211365894, 1949809685, 2090092882, 1225231987} ,{1269980690, 1569376531, 1277539104, 1294872387, 541543676, 51141466, 1903884685, 1165383659, 26285213} ,{547726552, 2020750541, 978527582, 327966382, 676688549, 970052789, 1605376747, 517703797, 173369673} ,{1044604040, 1018486168, 641300807, 695798835, 186712931, 652874754, 1916033196, 1743174134, 1079840537} ,{1292775150, 1843815935, 1647601444, 1865564024, 910683780, 317602809, 355324455, 1204329553, 1885032807} ,{363602495, 828172787, 497356448, 583418635, 219334876, 164041002, 654690462, 569025338, 1952911290} ,{1752583277, 2041023059, 1261381357, 2028682971, 2142406754, 1105496365, 1411328541, 1642092164, 1997171226} ,{779268821, 1700814130, 1085687826, 2139998914, 234046291, 1535444225, 1085153259, 1652821091, 836674915} ,{219525926, 922035042, 507452738, 1296923199, 957318675, 1489446062, 200894981, 1333984138, 275675862} ,{1641936840, 635039627, 1690529194, 1578844886, 436254410, 60361733, 917783311, 291253086, 978560924} ,{378118163, 1584767117, 1223893827, 942946486, 1508552338, 1774548307, 1354421196, 39402338, 1280013155} ,{2049211379, 968276961, 526764154, 1929214317, 389523122, 143678796, 46979846, 409532818, 31438271} ,{337264956, 572686488, 1597144666, 1119086360, 846475351, 25147024, 2017839937, 90719835, 277852497} ,{60010375, 1173990792, 1486959435, 1185435018, 565867568, 616116249, 90202742, 1368612423, 408975767} ,{696301172, 484717305, 112701850, 909227582, 104696212, 1529502083, 2046982315, 1803396507, 225126711} ,{1097563001, 485716605, 1491431895, 394703865, 1000906112, 208943737, 1096179040, 11041201, 1104415874} ,{837822360, 1592728607, 490919115, 881013571, 1544987599, 1299490781, 143471524, 1260295267, 1855503532} ,{1810594670, 1129719408, 1898184786, 397895612, 841814798, 1569753097, 1026648655, 1649181318, 49852972} ,{1148065692, 1399087275, 403842138, 504449579, 349704456, 138270824, 784944079, 122344969, 1273251358} ,{1534233413, 1322862237, 1042355207, 1904877994, 1932118809, 34261266, 1674608858, 1104016529, 364597018} ,{146439224, 80144672, 408071237, 1095235860, 1004094135, 458387673, 1643499922, 734759252, 2035418585} ,{365242051, 1127806849, 1553111822, 1504395508, 831751654, 33604490, 467608427, 2092351835, 1710041247} ,{134200336, 499861679, 782099148, 1656298692, 71821612, 23394832, 2062401145, 1471329203, 2026462585} ,{477926562, 1750652618, 457537578, 1660216645, 1905441001, 1798126686, 472438332, 635869770, 609144758} ,{445539178, 67716279, 1378360629, 402396655, 594792020, 1844512849, 1699148773, 1203558955, 1784159121} ,{519731379, 120928463, 1659286309, 370142272, 361318030, 1979131945, 1503461040, 1991414555, 1274935671} ,{558202640, 810549876, 39125478, 1142785324, 1669255805, 615178952, 1112303033, 1859986, 165366257} ,{577067822, 1653776495, 1868803133, 490297932, 1571845365, 1666551122, 1500258605, 502078332, 537124490} ,{1325285395, 1866146196, 1661241371, 11481549, 112608175, 865425396, 928845753, 1082249906, 378065802} ,{459588744, 949708177, 909646427, 1099137979, 1323168579, 1218348846, 1127469425, 1241394592, 29687013} ,{401971259, 649471418, 1665589777, 322160381, 41001445, 401407295, 988226312, 1264771360, 1745815116} ,{967027595, 502779802, 1445801340, 1224436016, 588057672, 1774932879, 717200650, 2047157612, 1160042696} ,{1481560948, 739364078, 1620632552, 1819198037, 707048498, 1079811205, 1779569542, 1411097062, 427912891} ,{1207550056, 1098410139, 709782626, 204999653, 1778047425, 246270890, 1697329638, 1543669562, 1452511443} ,{642969438, 1575001814, 1554538832, 1849529612, 830399684, 1135214146, 1801458975, 682148059, 484273681} ,{1236753582, 1062730775, 1760956479, 793806717, 802160764, 844615386, 2109586188, 914445010, 1555846834} ,{166547920, 422013447, 1431970870, 1548092177, 1265831018, 363721675, 767797372, 420369638, 903953280} ,{425464415, 1457152098, 128663169, 1333156753, 1503451654, 771368641, 2113541974, 2096655615, 232751277} ,{1062490814, 695388628, 2012720170, 151630697, 326004380, 1826755396, 943859052, 1109770217, 1762945904} ,{2135563997, 709300399, 659452132, 873671779, 556245077, 1787214100, 33927197, 539674713, 1763223298} ,{80787050, 802729382, 1184356895, 2115722390, 196314828, 1848195738, 322740022, 70028100, 171804993} ,{236697177, 109863136, 412056265, 63614693, 597772784, 1852380224, 193219916, 1713974640, 2145365307} ,{1398791973, 1019171607, 1650290676, 310006283, 358382039, 1925458787, 1727999377, 1530567601, 2077541208} ,{416015238, 1815539106, 575510147, 1794310113, 2143451005, 1455185408, 925188713, 718874787, 495980459} ,{932153929, 1525500991, 1560165771, 146442693, 1552419555, 18131110, 480078059, 2018524837, 523813315} ,{936688562, 1799520590, 1107357327, 846356525, 1042453794, 1686427958, 302359177, 418408266, 1540255995} ,{171557034, 410683462, 1907207375, 219381586, 376204941, 1853877053, 506731130, 1115767514, 1488924864} ,{78001352, 633322192, 1037081321, 2031692799, 1147245299, 1894511907, 1125621430, 563983475, 412980171} ,{1355960228, 2102076481, 1207482718, 844346939, 339310211, 339000213, 337213367, 1477941537, 1647391773} ,{299509334, 728979327, 73453302, 1827113258, 409158556, 2127389613, 724731308, 284967112, 1597910255} ,{1748228553, 449350868, 1647701692, 1621495881, 302778226, 1233335245, 1752905268, 1866972496, 1367324770} ,{2145031417, 1988395726, 584368490, 95473289, 396557445, 915512183, 586061773, 1286216039, 1986491296} ,{2048128873, 135238167, 463244662, 481727264, 1616502189, 814257432, 1466744749, 934734243, 1611467751} ,{1672025356, 1552431560, 1269866221, 819830565, 1219462304, 537725956, 652165407, 1343191949, 1715712763} ,{396713163, 2117808252, 1425759638, 1151027638, 1464864027, 153597545, 1908871409, 1449290286, 1601857521} ,{226001914, 1412172121, 342032278, 753566173, 1841880084, 688826357, 1926304153, 1475224090, 251809829} ,{30212810, 617459054, 860804773, 1040392410, 1890581446, 1680626394, 1775824088, 1277847438, 1228586478} ,{652875686, 1241101472, 1815768248, 181502961, 522402409, 1656840076, 806377492, 1187761027, 858305326} ,{853558208, 258737431, 1821966692, 1021694689, 1271528064, 818985517, 1213199214, 2100313517, 214295786} ,{924476164, 1948539335, 1461324675, 573349687, 232478488, 2054469407, 1493879659, 2090601271, 681884049} ,{1065927734, 69066928, 2017552858, 52391754, 1402121196, 1199230774, 953576993, 1114504177, 689922187} ,{2080468886, 525162191, 1708911861, 487478353, 919211453, 441243175, 1017605838, 2064051592, 1485283584} ,{2118800469, 1971178528, 1042559909, 703594553, 1958730154, 48482358, 810842719, 312425419, 1742007075} ,{1481566343, 732137456, 1676176488, 1979597632, 1908453975, 535711783, 1810799911, 379450022, 1990394832} ,{2084346891, 1197829255, 1868594919, 1321214235, 1001788473, 1649423849, 110180566, 1926896131, 1508490833} ,{1469457653, 758145398, 2095248141, 1566705031, 1115450901, 1639699590, 447611461, 1517415202, 799103945} ,{868081575, 1130812981, 1160496838, 201504703, 106820467, 290113518, 844208742, 120780008, 923540923} ,{899218883, 1728795737, 1688519828, 770700100, 1888283309, 1736191675, 543981830, 1654048283, 1631488957} ,{533833139, 572633620, 1492278787, 809612847, 1421109344, 278417839, 1287458361, 466376603, 1580000080} ,{545570547, 231035737, 556853125, 1946432295, 66817305, 791705322, 494504627, 377491305, 471566260} ,{1749451652, 1012373070, 217170871, 817474552, 1271746647, 1688555180, 1773660205, 733564392, 156005651} ,{1221827446, 1485350762, 1546978682, 1611539162, 425465498, 1701956250, 2025035818, 688028261, 292029789} ,{1354255778, 601725722, 171970271, 1946712708, 586109677, 2060471482, 1813037264, 1741646395, 1587146174} ,{138488776, 1007443591, 1553028013, 411031345, 1566033713, 529475790, 293969663, 630264510, 271699311} ,{408421952, 599280178, 321591128, 1257791427, 1326733186, 1744822683, 33025736, 816735690, 2114617518} ,{334766756, 87274498, 1212215085, 291530924, 125057531, 1688093051, 1802498523, 2130262635, 990078344} ,{294897963, 1491888644, 272139730, 660751090, 1072597790, 1710796904, 102857543, 946873497, 419069519} ,{105842703, 2006660239, 1249426153, 1307288776, 848024680, 144254193, 447317909, 2117095149, 1073409019} ,{1726804295, 1802106356, 558991994, 758931625, 332327964, 660853941, 435067297, 284535524, 662237260} ,{487399339, 2050522543, 1407174245, 1895572680, 835492554, 1772558592, 2075268926, 1813473650, 1274518600} ,{1219515172, 638640061, 2073658948, 1343370500, 177224780, 1500299939, 1441179787, 1590576851, 801638888} ,{1751825004, 1990375726, 1990869541, 945208254, 376578560, 1806733704, 1788009474, 1822643491, 919769344} ,{213136811, 1590258726, 163448994, 151190755, 553633534, 549293201, 78570556, 1841677978, 1841781524} ,{1338055127, 1461876497, 2050277175, 37898263, 1997423171, 1745176479, 1332091225, 108537246, 456196582} ,{409291015, 153960984, 1143162188, 1818063991, 1338194019, 1621321864, 1005196265, 2003116210, 1710845169} ,{1914239813, 1193361616, 158438786, 1892909212, 903956322, 1919242052, 775194256, 1192094493, 1882587620} ,{425368906, 1839156397, 891179040, 557166877, 1521492134, 187038727, 1577334762, 219848075, 907674396} ,{206150651, 1057538916, 1689475159, 1289838812, 472196022, 19018547, 286954297, 867282261, 276021413} ,{1342036273, 1129207523, 199329127, 916884194, 1531082643, 2055359198, 1412688690, 1496184976, 577921172} ,{1908049327, 62051406, 2090741658, 1086935780, 813230892, 145853418, 1824944597, 126082624, 1198598703} ,{1038536063, 1779889323, 1761043209, 1504575564, 1439022374, 1738524248, 2011553181, 907906133, 369635951} ,{2114446120, 759803948, 905372158, 245011845, 1618358019, 19747806, 1542705520, 1852548560, 969966023} ,{732329926, 757937125, 2066936269, 1018598386, 844743928, 1145866745, 55270173, 247634549, 1724439477} ,{1482972484, 1535724968, 2052042137, 426321187, 1419128503, 1446181328, 257388484, 765731947, 1902741598} ,{1398330699, 1076966975, 942750550, 1781806978, 490492792, 480177048, 1633682042, 1424766628, 850817931} ,{1763550760, 2062033935, 1099851829, 407801075, 2136125816, 1444791169, 1560542561, 2116469008, 106871475} ,{838417096, 1391594188, 1192657137, 491578305, 1869837505, 668651475, 421758209, 1310330573, 659602528} ,{1085411, 2053027096, 910334900, 971119977, 799074360, 1086279561, 637511236, 318494315, 150730438} ,{1018057676, 155091718, 838459032, 1917911213, 2079584278, 1516758449, 1273961216, 1042528058, 1499951093} ,{1632179241, 214999102, 1643501908, 1935241004, 1353861866, 1898594197, 656871392, 846342558, 842893606} ,{657395709, 1158522828, 61205592, 1090322203, 1541619216, 1894801971, 579140908, 1746616344, 1359800225} ,{657647475, 633022745, 1064980167, 279621675, 736434330, 2013458864, 789766294, 1506442025, 746050533} ,{2063664129, 981616406, 853745909, 792182560, 1921440959, 1225701602, 639498713, 1964222381, 1341337308} ,{161035245, 828825708, 667015830, 5962827, 810379649, 1309334284, 638245560, 1930060528, 993851923} ,{170050672, 1773183209, 1775036452, 1210728658, 1798932405, 854672015, 939030335, 345197129, 496997404} ,{824211981, 1528722463, 479124871, 1043819297, 1592724428, 1347622452, 1295947201, 697611959, 1241186060} ,{1638192236, 1581777915, 1877899994, 1890088154, 716715288, 578913903, 1997402675, 654126306, 1132016175} ,{184748183, 44983240, 774478493, 439281541, 1798226246, 302443923, 990745667, 844139834, 858532210} ,{1619224420, 1846165262, 762302013, 1127059166, 2111796024, 1279436715, 1925111898, 238521637, 1706190904} ,{684016305, 1016936260, 293253039, 1568656614, 691614780, 1314401465, 370083438, 1224379046, 907327208} ,{2008735816, 1274823826, 398198586, 2102893573, 2084227966, 142269561, 455778422, 10635843, 255144321} ,{896512862, 1371130006, 1075683462, 161347179, 1056490306, 1272098869, 1596816499, 1642570869, 1447958143} ,{358700755, 193244922, 882501382, 259791622, 431131737, 1678312393, 1690956415, 1278569612, 1272122561} ,{983877854, 1105564924, 1811547721, 458482734, 1240709155, 116962581, 1877985000, 1948671631, 1739790090} ,{1515247795, 1547987137, 1171584556, 1140774859, 1353659514, 480779497, 681189185, 1439229556, 420432440} ,{389407623, 216755886, 1577014842, 304945845, 287187374, 402585765, 1972980736, 1332389505, 357721959} ,{1457993497, 711595233, 1137138925, 234616447, 319581644, 2026658609, 1394176053, 1142519199, 1959809389} ,{1832412452, 1970859433, 214819669, 307013693, 199714796, 470521616, 1768124021, 1943028115, 1130267691} ,{1159423080, 1700155673, 854435725, 1202436499, 465251655, 1294357595, 2012023302, 712976708, 1074703266} ,{1503971342, 1729005776, 260437798, 230124223, 1612951800, 1417364171, 945476344, 328704232, 638911860} ,{1689707333, 1293019396, 763484704, 1822417118, 2137882100, 2097486200, 29364534, 1167894437, 1406996500} ,{1384409974, 624568535, 351122954, 1572323597, 767942707, 1643263597, 1601341795, 474983057, 279576090} ,{1942336268, 1039971834, 817394467, 1169552181, 106908209, 1219147599, 283318625, 1607261574, 1047605211} ,{1253964126, 1841612748, 1433876001, 2028651582, 880944227, 2046970398, 502687997, 1530588236, 2063225262} ,{1214647686, 846997726, 825471574, 82841715, 711375817, 1066116130, 1526072752, 837206440, 418353563} ,{2013648589, 1460326455, 1971405043, 1379074360, 1332164537, 1721917090, 294992238, 1061078712, 101793529} ,{1908256449, 668460584, 2136150745, 1789902572, 881722849, 966938468, 509232093, 860052063, 902827174} ,{552121189, 1787938648, 1172837317, 1724581555, 1398350261, 413580226, 1672329332, 280272830, 226512947} ,{1479969071, 2139725635, 942440704, 1718049489, 1683153637, 655368819, 605687435, 969187975, 807066934} ,{1557268610, 1832320981, 539761321, 1575334369, 1538468572, 213535144, 894520750, 188510283, 1794612520} ,{2079844769, 554450196, 1609608275, 1959189682, 541579934, 792872168, 43612828, 655435004, 741674412} ,{1121795453, 1849720166, 583260021, 959752358, 1571202418, 705472481, 443718835, 267579854, 833304375} ,{1485547472, 1805916081, 1459194222, 446363093, 2090540124, 426981007, 2061370146, 369128636, 1531848372} ,{1944776229, 994631589, 788946773, 299719373, 1007607257, 1280327550, 1414573954, 1307857042, 764605657} ,{1427140809, 1426854223, 1144428634, 257784898, 402902332, 1893722581, 588999913, 1447499299, 1936387042} ,{764494856, 1984477791, 1017864413, 267572526, 833534348, 1287407862, 782020026, 638110611, 1791311640} ,{1664456003, 866916741, 414783290, 1547034914, 1442051899, 606479687, 954134676, 783591048, 2026788491} ,{1516657960, 693142882, 582422663, 1604504620, 1452789002, 162312596, 96116525, 2107734748, 1574075299} ,{141618298, 279199877, 51456606, 1400615672, 170278318, 14885108, 905240277, 761659028, 762691117} ,{1038327399, 371996024, 1143116903, 757628447, 1962344271, 2048690899, 318700906, 1290328224, 1940226122} ,{905773289, 1202637985, 1697430101, 1041859240, 2103783480, 1851984975, 512364448, 1721525142, 1715998045} ,{1989132506, 1446747261, 1171070210, 393388575, 1596299869, 1394795978, 468143253, 1625402807, 1779028163} ,{790891442, 1243478696, 1347652009, 939158092, 1971277425, 1181415056, 504660010, 1836268912, 469589847} ,{478207456, 1242668667, 354946953, 1957562925, 1317227760, 645799328, 987910647, 215929828, 2100645779} ,{1783701298, 1786946163, 1928418690, 1622375577, 1403131170, 947902344, 2026149599, 805849035, 584213096} ,{2090722057, 807742894, 2033434971, 1017507251, 1968961982, 1901351274, 723716931, 1945322331, 321731525} ,{1156255403, 697066471, 957657724, 986046062, 1604025970, 614499627, 1494202131, 1644191322, 1243307372} ,{24415550, 1912515670, 1620318675, 1947389834, 1293133985, 830228404, 1759235463, 1653779969, 1680678637} ,{873916901, 367105889, 661706073, 1483261173, 1215768936, 168609156, 173196139, 1315268342, 970173381} ,{799313445, 982562897, 1107613956, 2013836073, 776449124, 1239674103, 995850567, 269424004, 1111647452} ,{566204686, 1447551346, 1237270386, 756092603, 1052436672, 1893624800, 574501706, 1199262100, 632694937} ,{887136480, 898139704, 38629669, 1519532409, 331980814, 587641936, 1550941909, 1943070382, 2014138192} ,{1170627715, 1840914862, 743413046, 301653047, 427860334, 894324250, 1504141382, 2137435575, 1290370618} ,{1463320829, 2009463259, 1697123918, 1175228484, 103225561, 316184963, 1908581728, 1366218338, 1557784425} ,{672241311, 779415481, 2030824335, 2048250718, 702053575, 1725247028, 138497687, 603912157, 168561773} ,{1812147094, 584285553, 1595476825, 2009589887, 1978581843, 1453272623, 1408953954, 872210909, 492192019} ,{451449159, 2046327920, 584844818, 1371323150, 1943205526, 1780946532, 1250003720, 933821848, 327877691} ,{1667883701, 1132066908, 1794068152, 2019057017, 2118364838, 655681795, 1027708612, 800365544, 120249980} ,{134397566, 1321796762, 1884440796, 1613555205, 1018071272, 1919002708, 2071783307, 1640050324, 873550388} ,{2098873413, 560425954, 437918978, 1376032210, 963457437, 1221684630, 1084071181, 1103981479, 469689737} ,{862309383, 1725273372, 852043221, 1808081428, 596923494, 1745587635, 1851700284, 1589483058, 1973339706} ,{1070983352, 1066048083, 1772158549, 1971909992, 1369969081, 1979694098, 864517250, 595587131, 195145944} ,{1916371590, 385962573, 994066771, 1291943977, 835352104, 788974340, 717925752, 181676505, 1308636239} ,{1622322282, 676367482, 248750426, 1877105722, 757724400, 323303048, 994587818, 1314251621, 405110515} ,{628637869, 1359462453, 1054824880, 627240855, 2035435458, 2105806840, 317792486, 1160258579, 1521970773} ,{158720130, 692411897, 1689856749, 1578616607, 1392567356, 1710601675, 554275251, 418252628, 2105172395} ,{101055087, 2089239259, 728172972, 789309869, 186978161, 658005608, 1356864468, 1173400764, 205784623} ,{1259610744, 1351152006, 332828347, 366828892, 223779042, 1339784975, 1474516341, 2105606002, 1849048953} ,{396193657, 1807696619, 561265769, 1533694003, 1150980119, 1346906292, 1378338755, 514404527, 1722557811} ,{1306285891, 1019257088, 989688090, 790037912, 330762267, 1743173032, 1668067717, 2031575311, 800826914} ,{2022402336, 1354132757, 1559971733, 85386999, 1055434838, 216795210, 410351586, 586009038, 635583310} ,{1419768867, 435785735, 579421408, 450529151, 1127492244, 690485908, 1196744799, 2071777936, 2120356884} ,{677942381, 1924491943, 655384088, 1805035377, 2016393591, 1794586292, 1315769074, 700271107, 375434333} ,{535020863, 1636247256, 1588887059, 284050765, 922902605, 1771112501, 1076371866, 551565706, 1374099702} ,{1407616408, 344623617, 1697625483, 449354057, 2086433182, 710951333, 697173240, 205343423, 189201884} ,{1462371999, 1703222327, 1207326708, 248655720, 1586526919, 1084017945, 1364791339, 1811088216, 523149314} ,{1827760757, 1279126920, 827596300, 2058016199, 1801688977, 317288108, 1575538921, 1613951759, 1129431679} ,{982619232, 601624326, 1378535521, 695121907, 681801971, 1546197783, 1159488844, 718797541, 537597014} ,{1414595979, 510639857, 531712470, 1524698519, 1871705712, 2055640729, 778605856, 822689740, 1583764303} ,{64063490, 2064213911, 66811450, 30070950, 287786723, 968049866, 372239091, 1080903274, 1412709130} ,{125223970, 1657114878, 1635862367, 304507517, 723300102, 539544665, 1983892646, 254390185, 1171134449} ,{1704424368, 113535336, 2016220467, 1125208064, 603828228, 375980266, 381388821, 1099096159, 111770390} ,{1119913715, 1163455458, 744347299, 263922829, 832135240, 234614746, 1306477232, 1095145986, 1093079239} ,{229933607, 134375554, 739598441, 1701798316, 360896614, 805518068, 1316660576, 811470385, 613752891} ,{884007041, 1328596665, 357672085, 489932172, 1763818813, 1126568383, 285495698, 175260313, 950858427} ,{1937763234, 1833986293, 42663906, 1036060660, 800887878, 511524067, 615249759, 713349062, 765964071} ,{493923492, 155671779, 515202321, 508065838, 167612663, 1271288745, 607122172, 1584063266, 257435614} ,{1453850820, 810776756, 1198911285, 1937619653, 1674840926, 301928969, 2059603787, 1968377604, 1415405494} ,{603550263, 1826190330, 853437143, 1343273899, 1092607449, 1937070285, 712333455, 557086278, 682878887} ,{874285960, 561817353, 1545132470, 1735573306, 392999075, 255817267, 59732056, 1285170357, 1768550550} ,{1817466758, 762739157, 757549889, 13947675, 1613102658, 2059361074, 1540012066, 119188546, 68148114} ,{49441206, 1783893261, 1641237503, 562523782, 1922219888, 263350286, 45826350, 1620226383, 1677263201} ,{2066876865, 1937439456, 467425335, 219110253, 1879416819, 1750903465, 391480034, 1585330794, 680305947} ,{1138462770, 330892636, 954607752, 233301757, 688767833, 1006172490, 1676189204, 76132265, 1198992452} ,{2025665991, 1183210953, 792794777, 1094332631, 658846763, 1655343862, 428172790, 2018213978, 1885207153} ,{447836956, 1522015072, 1284168586, 1036521322, 1982074594, 935775408, 1815640652, 1689537282, 1605484910} ,{955762518, 1946791041, 789709610, 690502262, 2002908196, 1397525795, 1911188317, 1846947451, 390108947} ,{540438691, 917726590, 1635603487, 1254819961, 1195619626, 1909689054, 322967224, 1571278162, 860420634} ,{1875936266, 364129035, 1642238430, 1798313845, 1639714489, 351796436, 1089612948, 128459125, 2131624707} ,{1156178060, 286247000, 1642538317, 285409002, 1315947704, 2029068326, 1576609497, 1558956778, 555564467} ,{2129155606, 923456181, 43356433, 508705578, 1324319964, 822496813, 1151346919, 2145843621, 680648879} ,{1708174815, 1614356757, 526948438, 1360638710, 1377494285, 479624660, 359079807, 1986580054, 37946172} ,{469966405, 367657238, 665578393, 1219104812, 299246280, 740039908, 1265557884, 1205656282, 92014946} } /* End of byte 16 */ ,/* Byte 17 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{455019054, 1550225934, 132415801, 1617396107, 1976346353, 746724171, 1052178347, 2000752962, 1490831181} ,{1082808950, 713628854, 1281218388, 235656116, 464816746, 1408387545, 1325153659, 1811961800, 1978113763} ,{1396268210, 1406025640, 1794948036, 1961802270, 1196856078, 1991073590, 24330456, 1523789881, 770903802} ,{690608210, 1366648766, 739659085, 1902422778, 1283772595, 51194069, 1688108802, 1526333062, 964304388} ,{143554826, 1865044570, 1858738681, 1354692319, 1047214589, 311561828, 461201853, 441652394, 248391157} ,{1842439355, 75490519, 1398351733, 2133264321, 1803112379, 1041249480, 1033914415, 1234523364, 1800525931} ,{1598076393, 1013673947, 611129001, 1617921713, 1703235924, 1850211580, 2111038595, 342187293, 831851095} ,{1344467875, 568830291, 700025871, 1740909111, 1236425344, 1708879270, 1175659950, 255686305, 1683806049} ,{1178440401, 344159862, 498881820, 988717601, 1167576492, 39079904, 914700583, 990694944, 1250329991} ,{1744531456, 2052535526, 16156272, 112810127, 471327566, 1012452787, 413417073, 1363978522, 208683448} ,{870687656, 1722705725, 1420898305, 2017226429, 737270655, 988468316, 645437665, 180710511, 1855137982} ,{966483491, 2062934711, 1704269053, 79770930, 1334977196, 758297848, 150374618, 1959875096, 219524183} ,{2104059284, 1729244554, 1300994771, 1457265318, 2050803807, 1422320534, 158937490, 327895373, 1184421150} ,{897318534, 1313551760, 1092141947, 2040649989, 1127066701, 355245179, 860431952, 880505347, 1570509106} ,{46115545, 1013871755, 1113573323, 1244408497, 737780464, 1534602226, 1220707638, 602412459, 419561921} ,{646648513, 33299793, 1492269104, 1838463661, 1194902393, 1202837184, 1500772819, 1205818992, 2061355934} ,{1803832403, 1353531983, 2047876518, 1533488645, 2097674012, 1044313189, 501250896, 928554102, 1987428441} ,{2026473095, 2120007090, 1361173108, 1427448184, 85248009, 873870595, 518447488, 2104985022, 1710735263} ,{1607062800, 1214092048, 945222454, 972592529, 155577077, 651620068, 1576148889, 1387308059, 1710128721} ,{132931117, 942880140, 1438053719, 350723947, 1683693642, 574231702, 1451820988, 701580060, 1438140621} ,{1643531356, 860338899, 378788589, 438206729, 1097086871, 2003089842, 2039939156, 1442214942, 807060006} ,{648334242, 956713716, 733261650, 1493248999, 1820227431, 944545096, 2120657784, 1180515504, 1836543734} ,{745021447, 1047892810, 1578617517, 1472318866, 460158763, 1538754580, 1040053414, 464055077, 2123100586} ,{605888800, 1033515578, 1460597707, 1970313397, 667708529, 821454498, 958340268, 346404115, 9596177} ,{483882242, 1834695558, 822681119, 1886527752, 1773421849, 1880085138, 2097386603, 1474564988, 2116317562} ,{1194446144, 551690294, 1560025700, 2125754307, 145231743, 563130873, 611310618, 1697047226, 195510308} ,{861905665, 2019903392, 161982712, 671909913, 194760360, 107623674, 1627552979, 2142375835, 1055541553} ,{733870656, 1841414684, 88913700, 923246892, 1478356352, 1155063225, 221512483, 1167840070, 2143354151} ,{516706376, 472722695, 10530779, 1794277853, 287218646, 2009862381, 1561936775, 710729070, 1112205295} ,{166714962, 850425392, 778159155, 409379473, 137057811, 1006823520, 425770923, 582482197, 1758550142} ,{1678269028, 977145221, 1635965833, 1260509466, 770256933, 1248744520, 1198905251, 2022008634, 1565792464} ,{845990576, 270987682, 42915810, 1216492589, 1984441636, 1692877898, 1383143828, 1591385708, 138966450} ,{485873409, 1929244822, 509648955, 1367693233, 2064835539, 578073123, 629445909, 2113754806, 1969266451} ,{1772327659, 361667446, 1556520471, 1994068909, 1675974447, 1781462230, 2006750514, 647461135, 912492138} ,{1984621422, 1786932034, 272489576, 1603157324, 23979877, 1307062901, 2069875390, 1952442761, 1595482740} ,{962502760, 655500371, 563007920, 396188213, 232123468, 318624405, 988360290, 1124003935, 1449334826} ,{1737131451, 682766741, 1341308899, 1743836139, 1030783200, 1255886739, 794374358, 1251056749, 1559280979} ,{1089499105, 2032157061, 139918424, 984439543, 1462176078, 40219639, 18467368, 1836969423, 1141359327} ,{818802518, 102501801, 2104717305, 1409163981, 119606608, 1748656202, 1707435999, 704187199, 1913250553} ,{177365790, 1845081806, 1570857513, 1759652630, 1006579141, 709261956, 893119099, 1339686763, 859811321} ,{1964806367, 2125742743, 1353222502, 2003621744, 1019981742, 42196144, 1767950581, 1492213223, 819829661} ,{781826106, 1604458390, 134841192, 1861222482, 669856812, 1975064347, 195187947, 397042330, 1604633820} ,{1461174872, 96383350, 213651492, 886370250, 793623344, 873462333, 1303804046, 1074283928, 1276617428} ,{1445495600, 368681784, 645024505, 2074001294, 166032313, 2010241971, 1122555821, 1788480939, 1580173449} ,{589216705, 1390296076, 2030663648, 220307377, 1075317131, 1720941141, 1535708431, 863925432, 2061433418} ,{283147884, 392989471, 1651711808, 1721617197, 1537061622, 723384441, 1505901436, 765054965, 1806163296} ,{126437613, 1194727961, 1214016442, 921089, 1643487743, 398855520, 1074417010, 698616959, 1291597625} ,{1331773919, 446623959, 425468067, 1374691945, 542732605, 2020612552, 1006743862, 1237079958, 2042489902} ,{1870605352, 40728126, 1050498832, 1302850231, 560808009, 674506441, 921795760, 980221715, 1828343144} ,{309583315, 484228290, 255663038, 944081024, 1336090052, 1986486865, 97263547, 1661658059, 647135549} ,{1504027387, 1722223816, 1344396267, 379745345, 304573756, 1947433507, 1414413816, 1602687427, 495252433} ,{513860254, 1445078800, 1891885344, 1374383715, 22461234, 620982617, 1352418881, 1008411289, 635884924} ,{162036363, 1519745494, 59707100, 1887944797, 572871529, 1785155314, 938141293, 807976068, 1149073364} ,{1854768505, 1703687840, 50927162, 587003598, 1785513626, 1591389775, 1104553476, 1918396799, 1006700564} ,{1375436327, 1027454009, 1151881733, 1313663428, 1495101553, 1297670571, 1878813039, 548704682, 1517803279} ,{241985100, 2027144224, 1901142041, 903368388, 173092981, 1942449439, 1113910555, 2124112429, 396021272} ,{1926676787, 373665149, 1511979394, 255391382, 1429220972, 450947550, 280405928, 1016242766, 1717781098} ,{2004292277, 1589976465, 2027192470, 1031908472, 1847211815, 566351621, 1936343585, 891059129, 354225114} ,{1067920304, 427745124, 1627177880, 527865749, 1095656609, 483469747, 62099098, 291382700, 993672664} ,{458854315, 33992238, 1020932692, 1950211654, 1376545688, 1484005963, 823653152, 802939592, 236523372} ,{1630439267, 96883147, 1694400781, 419700071, 1483065302, 277848680, 1597153743, 182818808, 130588531} ,{1406937700, 572188395, 1129254942, 2115223808, 1994156880, 79760200, 1101338872, 870761744, 2118886999} ,{791352707, 92096718, 356706501, 1196418124, 992978901, 566742547, 909965886, 1522175158, 245469231} ,{81880653, 499254819, 1410225263, 20844280, 1099698171, 1866460961, 864338109, 684693583, 1182177964} ,{1125891749, 1534186165, 460449506, 43096372, 448442090, 2106491694, 1226415966, 258095878, 143360168} ,{881685480, 1468395237, 2090603072, 486183956, 2123362325, 1159805319, 1577297028, 793123455, 1308898204} ,{1290302516, 970102874, 1755047236, 2116266120, 1842968226, 89000264, 5910226, 610958988, 340642392} ,{1040915829, 2130146201, 1099342462, 1539052872, 1660905819, 584709655, 357823171, 447447121, 1611673509} ,{435379506, 788987806, 109735859, 98602886, 760550315, 1280850073, 1057302704, 158043669, 1671602672} ,{953476682, 1250439407, 849079070, 1270558835, 1881760111, 1131470933, 1848928614, 2126672373, 385805504} ,{934361875, 1453406880, 95235266, 561690873, 691618659, 589509312, 1177397195, 1760214590, 1149285376} ,{2017210424, 1945999386, 927208557, 1464296555, 1565340234, 1669472467, 1094512470, 7475367, 166859485} ,{968528997, 596257840, 103174453, 1500520340, 236281082, 2082633791, 555368632, 447147860, 79779331} ,{1271844547, 1806661056, 1120091294, 1020165374, 1800815282, 2113134741, 487868317, 527288916, 1186767432} ,{1678257484, 807670991, 436524463, 113573980, 451064551, 649777601, 316476485, 539678122, 476925573} ,{262644175, 294912828, 1167381289, 686180404, 510024298, 1813600102, 747369618, 209852673, 1832440233} ,{1666613310, 1780966513, 908017565, 527425294, 1034168918, 123910031, 2045765060, 1958891170, 1422298832} ,{765029539, 499443402, 548444132, 1663153546, 1789789496, 124308881, 612310206, 2093090068, 1916201431} ,{1529639084, 1890054744, 403278512, 458197755, 1761271763, 1784177794, 1282054837, 1834839662, 1333826246} ,{1191859918, 621985017, 1480665512, 1050151020, 1320625124, 2137569328, 1591509706, 1137367717, 600423876} ,{1505072291, 2026893458, 2075384689, 1954150016, 19669540, 533750515, 1351701097, 1158932085, 150914222} ,{693259703, 1263873427, 1247698486, 691868552, 1731101020, 1343355078, 562775844, 924393736, 978066483} ,{1816757012, 948184676, 1851303609, 482011106, 1210525086, 1600809910, 217278317, 83841148, 2040174143} ,{1645047632, 2046753956, 122634913, 269841355, 407475943, 611993826, 552682168, 935396055, 1690700059} ,{725354916, 630528082, 1441968408, 695745993, 1917319421, 2094640779, 601469953, 1881095880, 1258935863} ,{2088216714, 573810266, 270687423, 1120362802, 1913596916, 1346172969, 765894525, 1389170630, 353645372} ,{912332880, 1512376067, 1213073635, 2001023973, 292946116, 1193059559, 688128607, 364314590, 1239177833} ,{820281085, 522347982, 1929295712, 1984947603, 1111727443, 772120514, 1014808438, 1415016285, 1153342655} ,{1102415492, 1711016402, 1905130465, 967903662, 1998776202, 466177521, 1849342515, 1931246517, 1000717374} ,{1425535659, 751413869, 1530117135, 393205393, 1088223971, 145102206, 2123576457, 1475314733, 1967728185} ,{2144191257, 595382973, 375145827, 295096860, 1206728066, 1670277133, 1186413201, 2134710069, 1425947183} ,{1656416688, 453418931, 1333822203, 1469230017, 2023978926, 732666549, 2043756552, 2042405412, 450143573} ,{815409934, 2131042943, 1606261854, 1475473347, 686675466, 1201404372, 326776865, 1897972356, 1740372059} ,{4999581, 482807215, 187487100, 474862493, 1891007852, 588431544, 411032789, 1297368658, 1222180457} ,{854375266, 1524449813, 625918261, 954317526, 1728996114, 1869663564, 401032743, 1136078613, 579749323} ,{1122446409, 2054491729, 936921850, 1338650206, 671389925, 480519480, 768740032, 210597736, 168183988} ,{760077205, 202097296, 1523747143, 419426682, 1183504607, 472941186, 2002462822, 2080796706, 1099470854} ,{789887366, 2012294615, 1931853364, 1543360078, 575488264, 232595484, 132458027, 101135398, 325342510} ,{1284679545, 1031326895, 816644861, 130141487, 1083338748, 1118559966, 1069393967, 507472875, 368852330} ,{1227689013, 513022572, 1982708923, 1531707017, 292645402, 1479195788, 176500209, 288815938, 2124295106} ,{1164655994, 734138350, 817976809, 2026880190, 203430739, 1829747305, 714436164, 58840615, 1793449259} ,{1139411138, 1556376142, 674236251, 155587362, 722356695, 942833879, 236870211, 1253798794, 1786765670} ,{1876217809, 1904631906, 416183386, 222096417, 166052722, 66839667, 1144408779, 1861662809, 480838380} ,{919846125, 288778934, 469736415, 669960992, 1463848020, 503703702, 508188484, 921591752, 282297448} ,{1775459149, 231076394, 227598253, 1685223038, 32672934, 576033333, 1421850957, 865452656, 1447751422} ,{166083160, 646683605, 1503646502, 226004307, 1171144459, 588554499, 1190278245, 346277833, 298327941} ,{1700770044, 1150530321, 171345205, 552422098, 400042385, 984757535, 1437292803, 371492164, 699516857} ,{2133824528, 1219292745, 1179131236, 1458123482, 634623717, 1331447619, 605750387, 1997730123, 257540721} ,{929387190, 1358507774, 743745052, 1480089788, 1292267649, 2106964081, 113681806, 2084546250, 1636418285} ,{1079968138, 774203378, 1808237101, 1379093465, 801480502, 181466165, 134111208, 43664354, 141952884} ,{1948338563, 933225331, 452008393, 514200289, 1568530464, 573429381, 358682292, 1677151675, 444656466} ,{1452741606, 1554738967, 2058372446, 1738300480, 331949773, 1690524499, 1359314689, 81424333, 1699315627} ,{1188439451, 2119882686, 347220175, 681295900, 1380744218, 737561237, 1914434842, 225992070, 227905170} ,{1812311073, 587117151, 1307926284, 496661376, 752316256, 1348672646, 1480135952, 1267103981, 416204045} ,{1548127543, 1147121081, 691475661, 1045331519, 1153406860, 927398988, 1225879474, 2119512256, 2129564488} ,{1832440634, 473371977, 21668977, 1610226426, 830148651, 1335256308, 413322728, 177073283, 1090295549} ,{1831190577, 337963444, 1376151405, 1252599157, 2033599897, 1522407842, 1058057025, 1419150110, 463155535} ,{1700477244, 319748854, 1948230388, 1555208008, 1533629383, 154078548, 605812708, 54683372, 655496744} ,{1047591322, 1897213648, 1109130620, 531520655, 785676904, 1745305050, 1572015676, 1699302086, 835166268} ,{610832282, 1526829436, 1121876232, 243275984, 373883716, 1527786502, 801899321, 69985404, 778885554} ,{1595201545, 796148177, 1961202947, 696601310, 1996491873, 774698548, 401568267, 1307671693, 787187504} ,{111638491, 1891858793, 593080923, 1424076927, 293303774, 886417680, 282834735, 788213858, 1950164206} ,{1354675604, 711341975, 708263905, 1067188837, 105559620, 666702243, 797040667, 1539843880, 1714097909} ,{159571586, 2073823440, 629129170, 1717233859, 691087873, 514734832, 1737462167, 1860741377, 1865189491} ,{1197639407, 930741056, 1340727751, 854221253, 1092443755, 1263186540, 898612462, 754455256, 398887686} ,{2100710244, 1877904796, 463992368, 520793128, 98912985, 1572152940, 2061222486, 443610352, 1232139550} ,{274203357, 1720901803, 342197995, 161220553, 1885925704, 1302808081, 2141072996, 1129831922, 1705232896} ,{1478452635, 495549876, 1836602187, 330268632, 122357989, 1588704189, 99439715, 1906077467, 206850152} ,{541095524, 51594486, 1719338381, 400788586, 1753550412, 291707782, 1831989022, 2121824440, 1010911858} ,{148200136, 1435245873, 914298010, 541536161, 1111674880, 953879096, 150943597, 166583355, 516499794} ,{713735343, 557043401, 363698625, 814584918, 554850779, 629358578, 2076158405, 624800388, 1195164102} ,{1378989887, 901985411, 635377771, 387313430, 901272477, 1170958227, 977161668, 904300305, 1121638282} ,{1677226629, 1907894925, 1309491142, 2093964069, 2078842925, 139749599, 1153204600, 1690342459, 1379848882} ,{1693553067, 927649606, 1637215561, 786967198, 811694213, 1086525309, 75815697, 438309157, 336163461} ,{1474140775, 875431688, 1213183560, 137564676, 1795049360, 1970095152, 1812140445, 40186432, 921990650} ,{168498856, 1494280123, 110590120, 1444635801, 1989997273, 1005808794, 1261795595, 466136431, 860481881} ,{2127395594, 1022425557, 267547083, 669128888, 1466217583, 1781075652, 932660793, 1738561981, 965095072} ,{960563546, 1201875630, 298775168, 941811940, 1790140999, 1509923483, 92424496, 479966637, 1300030129} ,{2103400601, 580614767, 1306864966, 1253206914, 636351683, 2099282386, 1216361225, 1364351397, 1984189794} ,{1336741240, 1237624979, 90871617, 1430792502, 828632619, 769765545, 358324588, 16888708, 1681715030} ,{1470816993, 1586050848, 24069577, 924832922, 715167851, 319645624, 341690338, 590492939, 401121140} ,{1526053920, 2080062954, 1442814557, 1919001453, 718288194, 1545944082, 1116482493, 776613304, 875970267} ,{2029864062, 486953310, 1112545692, 1446288170, 1862235788, 2138732900, 363989177, 996769411, 546998853} ,{1125522384, 1626289394, 989273223, 1212183979, 1278298763, 1661853289, 1373992213, 2127151075, 2144408622} ,{135605959, 466457951, 1961885214, 943253591, 1171237933, 311621683, 27456525, 1470514719, 549183599} ,{238569845, 648109808, 1471460680, 452097022, 938266087, 1101481274, 441697140, 297503023, 1156262576} ,{1650346294, 943541288, 2000135813, 1108708441, 891864793, 1869158638, 1131181925, 1249665679, 347107623} ,{1216392419, 1480293304, 930042839, 1036120115, 56776113, 947181775, 61658306, 1658784802, 1838433444} ,{1518281379, 1290466821, 246587912, 1034689893, 1699116179, 651694156, 1736481875, 2037533972, 126985830} ,{434030463, 37605211, 1859562721, 1477058467, 407820228, 207817096, 1357330008, 1860986423, 78597527} ,{2063098309, 1622990425, 16075399, 941410856, 1414412370, 1867273658, 1295264901, 595442323, 1528130104} ,{1820346898, 1861083307, 556707219, 873349886, 1274001768, 1009213528, 1062475943, 823605853, 234120443} ,{1907321536, 1344515488, 383250684, 738082123, 596724361, 247456221, 404015189, 1563044448, 908652263} ,{132333785, 758250454, 2040214114, 1060510255, 1949918511, 1248804541, 1710506707, 1213452184, 1954706976} ,{736957332, 632801945, 52274138, 75566328, 117535002, 1663357067, 221718441, 105339056, 1798653250} ,{1235405022, 581164043, 1523742143, 104122870, 659920833, 363520650, 322768092, 1279644226, 856290448} ,{550810506, 1407915444, 884775631, 892459133, 1129443824, 1859376861, 341367912, 2061470552, 1696419319} ,{895150316, 1333164521, 142480115, 330572160, 1136504564, 590365181, 43226484, 744939620, 875775697} ,{537215950, 1029417973, 1780216713, 2106304622, 1997729153, 1915134214, 465674644, 225331579, 1811488918} ,{97811947, 1268414865, 397260784, 1559076028, 1765488733, 1985799960, 1849714445, 1448546884, 1687994053} ,{487408230, 246543501, 2113065871, 1560845768, 1842664969, 1149692444, 2140170245, 1348657094, 726389892} ,{1754518217, 1797288875, 758951964, 368439755, 1639593043, 861084952, 1746062419, 1797427373, 258874186} ,{1805123059, 1924867342, 1674774440, 1857729698, 2084378634, 1185700089, 78663403, 4836407, 686821521} ,{696110563, 1453648999, 1299290326, 1979553720, 1309134697, 1532132849, 1729411470, 300517882, 1418701803} ,{635183300, 1797907543, 1254754509, 1171311760, 203885877, 1575593388, 1562290282, 839258526, 554390487} ,{1449833143, 510798627, 1549188418, 46168978, 478309757, 325479574, 791025473, 1165174433, 1466363076} ,{1251363620, 746444116, 29018775, 1853971792, 2040512109, 308466849, 887423019, 1435999553, 786292989} ,{25011648, 1824584871, 830292627, 1028263794, 910636691, 527340606, 1289968509, 1543142457, 48414284} ,{669951086, 1646488566, 464806405, 1968772052, 1940829468, 2056250780, 1892602279, 1198987072, 662190534} ,{973774752, 1072402006, 606820925, 805929175, 1609048493, 2020071031, 1080912070, 1501351774, 439330278} ,{1803475324, 1531521329, 134018229, 321215915, 646323775, 1329801695, 1279200371, 1054611791, 1458325654} ,{198026815, 5259024, 2007218638, 1895796324, 687040846, 1546562374, 821676713, 856590198, 172485912} ,{808167690, 715064347, 2095086494, 791827315, 1696886265, 1505947479, 1460630694, 1783845125, 1609411383} ,{962899958, 1671961540, 371173022, 1773574411, 1312064111, 1120198190, 1052845479, 1297704311, 2038983608} ,{985267819, 964527032, 2111625143, 1321411270, 363563541, 512309113, 1180697677, 586044536, 1876434871} ,{1042746176, 394789794, 2019032508, 1939343349, 587466193, 1747815550, 333555242, 2037347379, 1914504075} ,{591399592, 161561473, 1343200634, 1556466177, 614240970, 1243061804, 1337625732, 229303712, 1822474859} ,{381605808, 1784911299, 1260743297, 203478944, 653455785, 362859078, 918994255, 1492443690, 1204842317} ,{1399834854, 1627255836, 658462493, 819277093, 830633857, 493068543, 808011050, 1610215049, 1233426231} ,{1568531169, 411897839, 904625069, 1561032348, 925896023, 829279571, 1795493225, 1469480631, 1592693808} ,{1944347271, 1889379996, 873964594, 1135749687, 477037862, 242490810, 1862834634, 1986996118, 1714324503} ,{334000181, 853771883, 1787226818, 159970699, 1231176182, 844246002, 1706489082, 1924931532, 1845940334} ,{869048805, 1154054050, 1609699721, 1807235094, 1092053976, 1386757880, 1124334463, 1735840579, 377135591} ,{1043812189, 1945872313, 146758514, 417660095, 1314839594, 399422041, 114334982, 592391216, 678005939} ,{938206637, 1795274716, 2100345445, 1453958225, 1348110638, 1144779846, 196433946, 2029451438, 423054270} ,{1734875971, 1888800351, 867404862, 767645962, 1414913763, 315226120, 757075720, 502669460, 1238979452} ,{2071813165, 730938669, 208653793, 288814710, 1141960803, 542674973, 1414581923, 115833584, 1530915778} ,{1198231836, 607370941, 379501726, 1012274310, 633329469, 1905976716, 1430396793, 1907499803, 287827085} ,{1242687709, 897472054, 1937022714, 887147459, 2098097719, 1428898997, 1114785518, 282224163, 173958162} ,{1620056983, 486056991, 1790488433, 1697590218, 1943609752, 241159594, 1061837481, 1433321282, 1656695668} ,{174586388, 1655348984, 1184370198, 717634027, 1181694676, 305753560, 1077356452, 1629049457, 289404819} ,{1009015472, 1312430815, 1868281143, 1425896281, 1027359044, 100281941, 1940556646, 1390946549, 1506895393} ,{1709143011, 682104333, 1007552461, 1878428229, 1970184381, 1265020285, 1101666273, 741225757, 1388632819} ,{1525107794, 608021222, 1240427623, 1647055363, 1883821058, 1577472047, 1439931683, 1465201821, 1478374603} ,{596703976, 540452550, 1111289739, 407124249, 216277398, 2049593076, 2111919080, 1815436218, 1513610004} ,{841414369, 550154699, 1499140204, 2020636195, 955663676, 1222027311, 105063004, 265877226, 1506851741} ,{236723991, 1625312913, 1126984735, 931259831, 1020652857, 576091875, 388481667, 1546910524, 908730903} ,{1001919838, 60275013, 612504944, 328881702, 1747713617, 1642338352, 1326176709, 831087495, 919962082} ,{17515213, 422838074, 69265712, 1142616168, 1241921074, 1812572120, 1582349117, 266671430, 74844250} ,{1429539879, 988017477, 355796574, 2110095018, 716246163, 380304337, 2048051156, 963780384, 808419758} ,{2122589891, 449701947, 754427624, 892851404, 540770152, 887911491, 1624774608, 1183173750, 1200401163} ,{399127208, 1092988461, 579985049, 2113457630, 725368176, 1812287397, 1771045560, 1493591684, 632107816} ,{1680546656, 1726148208, 1382565887, 850090082, 708256489, 708460149, 2100972476, 1832736633, 1462663038} ,{5922570, 1505574308, 2107815, 266490098, 1750513681, 429759585, 388562872, 1418983281, 1361243715} ,{1797891968, 346333621, 1159992796, 2015277404, 473907355, 576064891, 2139486090, 192715449, 1372824589} ,{906922679, 1281834, 464731868, 1513975692, 1876731540, 1723968361, 2116200771, 1954421428, 2051677499} ,{239846480, 1269880014, 1333282880, 1862523938, 126307625, 1185933746, 1562451163, 1637788578, 1485816905} ,{252463858, 2112937600, 1701686170, 526934851, 806771531, 1809826885, 1605117672, 1253633854, 1871238235} ,{1014419097, 1816908969, 1991389619, 1068281593, 142999685, 1139739738, 1377732346, 1230320366, 356632651} ,{1879180594, 1373620707, 249074870, 1194680652, 1163352411, 71662405, 760863940, 928378273, 86800739} ,{1606650274, 683278714, 545004564, 640792429, 994108840, 1624189671, 1157654185, 772484376, 294242579} ,{1222397828, 1556293343, 585694207, 500145579, 40667624, 1987447562, 104835925, 1000323917, 1515915353} ,{982917335, 1445324331, 1002861103, 1955520248, 1739277283, 564432352, 2093799968, 2081683718, 795939617} ,{1628789111, 251356061, 2129104914, 1074239070, 972553208, 862395878, 1612517992, 1798347961, 450505604} ,{1100562795, 1166394798, 1535107641, 1148587265, 1129586607, 244326908, 161103035, 555330291, 2116337011} ,{2037210034, 712769297, 816044005, 1844321030, 1491324786, 1502984338, 75251122, 2032731502, 994617125} ,{441803471, 1203603604, 1852182679, 821245384, 256093899, 82604748, 534780829, 1600439735, 1306552727} ,{327968970, 677629272, 387979721, 664059401, 1145537290, 1636581956, 1533162954, 1630690920, 574795468} ,{2146641032, 667972048, 1481285509, 271723866, 1564576298, 1205137875, 522616341, 196182329, 314796477} ,{529784385, 497341898, 28267636, 1258447301, 565100001, 848788275, 1341804475, 251944823, 799041860} ,{901304267, 814951740, 676712376, 823479715, 523378227, 1251114523, 560944840, 627149021, 411250252} ,{318025619, 854371230, 1566014656, 562373992, 876940091, 635688081, 1450898010, 1128301512, 1770524750} ,{877054362, 340422831, 26759663, 305412431, 1086994971, 248202169, 906256545, 525093552, 44218769} ,{364773408, 1228275134, 987631528, 1578808327, 2046977139, 1959597900, 301772263, 1795310582, 1633366800} ,{1844353860, 1407063574, 454573134, 389743269, 1017584817, 761539723, 510156778, 2143923679, 159837347} ,{151005266, 40030963, 287262370, 1262028671, 727072134, 631424214, 1569502448, 1571696273, 1572842809} ,{2053756184, 33865073, 1539574757, 2076119070, 76346249, 1892030785, 1414234894, 936891763, 70556679} ,{326190601, 65675367, 295319651, 1456208804, 947088172, 58882839, 167336475, 860770126, 1290063722} ,{1198598768, 90261271, 279710849, 47351371, 2116343219, 529745161, 201674225, 513636304, 218583595} ,{141808684, 537218884, 784640970, 1957710455, 716642647, 917082574, 1966900622, 1484043602, 411975751} ,{263758467, 667046205, 1297477154, 1131952785, 187143743, 1326350018, 618506283, 1237598133, 1149848767} ,{1271921149, 344277931, 1612272674, 2109377717, 1804792980, 865473170, 235615241, 2134286565, 1594663594} ,{1568317571, 1144969552, 1578867066, 135153470, 1787102228, 1777174408, 1815849482, 1861838163, 1457581418} ,{1288455820, 856082873, 1320193479, 554813522, 1704794986, 405123336, 748903582, 507445633, 775569738} ,{758238951, 285208328, 1050743863, 425504871, 337979782, 1200940701, 737788800, 167278791, 1460548211} ,{1837876889, 1060036568, 922741856, 841158161, 1701730645, 1383147298, 223891937, 440673436, 1117998213} ,{594726370, 1013883797, 1341007149, 1262260860, 552465288, 1879471391, 1864308168, 1468746460, 38404027} ,{471766285, 1643881974, 1220571740, 1403617870, 304231006, 2015634686, 1159344694, 353602133, 1576334630} ,{406988149, 613910698, 1195762344, 1991526327, 2003710364, 1437832487, 1444140250, 412335416, 1429896239} ,{491868557, 1186467803, 1953445705, 1146180222, 744777951, 692191261, 1818552946, 1153931785, 877772738} ,{922597176, 108612594, 264354500, 1048876262, 1942269253, 1503690500, 1885546472, 1677425185, 1892041350} ,{1593247654, 382603336, 1240881788, 1552043258, 295931234, 1328205210, 1849751788, 1763623591, 859778983} ,{1609343321, 262208205, 126589786, 663523067, 1177341346, 1953795872, 731251419, 300210888, 1931260994} ,{1520598520, 665228507, 761019462, 333647365, 422685473, 133019025, 1251333715, 1506293300, 2042966465} ,{1097016528, 1623904342, 572864075, 1479807508, 664844565, 2098942016, 1556221287, 1686749334, 1577720851} ,{1269015458, 311202331, 941305246, 1529191619, 1983395326, 1799782203, 633504022, 1522213576, 1021123845} ,{1135535316, 2104985281, 2088047406, 536170909, 989928051, 1278051191, 674361848, 1159137721, 691604031} ,{356887695, 400562126, 1033935278, 1811052431, 1667525575, 1886264168, 1232736278, 1164846369, 78713340} ,{1409901248, 708972449, 943412118, 733485921, 1319677152, 1074500471, 1748452959, 14153203, 1636846078} ,{360301483, 274593691, 698099699, 852056241, 1985043186, 470610719, 426011765, 1239823035, 1014234212} ,{729032433, 375501753, 2079126435, 1161910273, 1416383542, 1516204772, 689461306, 1564843910, 1942429997} ,{147443586, 323604944, 270500844, 1638667279, 777560814, 916244783, 271937717, 1221366531, 1531198534} ,{132134403, 1515727784, 28341638, 1282863331, 1196460773, 1791603290, 475022310, 42863699, 1074084296} ,{1931547208, 1834286149, 1886066663, 1206940598, 1709375143, 2017238085, 1716904245, 1142895024, 1738858614} ,{1231401610, 1165608281, 225535545, 911252005, 1475396422, 1808359541, 1847467535, 1875251103, 1141181083} } /* End of byte 17 */ ,/* Byte 18 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{1753405884, 1769995508, 1751487212, 587978929, 268979410, 1585800178, 981708785, 781251766, 639786386} ,{2044682940, 1925017135, 1167540168, 242764772, 1195986832, 1209378106, 1388502925, 293659660, 1093205951} ,{90174143, 49121981, 1308219488, 590873747, 1198967477, 339683578, 974581871, 1430886098, 483408978} ,{464443298, 109588475, 1270927330, 731151190, 755270269, 1879768603, 935130866, 1048100481, 495809283} ,{1608982539, 1376684911, 870095503, 318512698, 1170221375, 706166840, 622937109, 1066852351, 1977939058} ,{1852660140, 81357140, 863997760, 794207386, 2080494457, 1580409738, 96105987, 1512189567, 155363738} ,{1606806871, 1285548667, 1764575808, 56721127, 1338621801, 1904282268, 1957070473, 767610832, 210964551} ,{457668442, 2089043212, 947195759, 1901595471, 191881782, 2143607622, 411681456, 1328010232, 1015568389} ,{1433955147, 1685894823, 1599250514, 1130008689, 1363112442, 1935629798, 707031720, 882547587, 1805840063} ,{1067937213, 618209737, 1344518697, 409308012, 1688638990, 1640296083, 1092537615, 1637532582, 1330250951} ,{164703860, 147086445, 1596297040, 1933021531, 896854398, 89440094, 920747913, 859140485, 1133026140} ,{1291345133, 1831607270, 920665965, 1866347688, 185108035, 628017197, 378737399, 1626415416, 1333849557} ,{484459995, 1215201814, 1413090899, 1265559751, 136534953, 508769904, 1664518162, 897726759, 1862440052} ,{750831838, 56105442, 117633728, 345487278, 340714242, 1726049581, 1768374204, 156053036, 2004082597} ,{277944505, 768383713, 1993056902, 2103970385, 1313572034, 1207974048, 281775797, 1683226309, 1690810467} ,{531587774, 1671831140, 392208509, 2078044769, 212459430, 841530325, 634543807, 460090066, 695673001} ,{636044649, 495206447, 267171055, 1026884893, 765192511, 1238958461, 2088393423, 618069742, 763616466} ,{382052543, 276947441, 541473254, 1840032781, 1539008437, 465351598, 163851830, 708942817, 968755904} ,{1517176482, 535968028, 975764284, 1639521728, 1062080369, 219704624, 1328364395, 637723278, 624128857} ,{1233585529, 1423255503, 445077056, 101462396, 666254403, 1957868209, 726190824, 276792337, 247254538} ,{1659334594, 1592570870, 1227653720, 183118528, 1722135184, 367558668, 1958001645, 34688864, 2139490811} ,{1857324397, 940642945, 1679506079, 524148561, 183617249, 2115940506, 1583171996, 1688025280, 1740292048} ,{1405601631, 1156848757, 692403527, 977589477, 1550305516, 1846004192, 1334792248, 1369405649, 2037381180} ,{637541051, 1914472393, 2038650752, 391575069, 2036312406, 724965384, 66925950, 1441892810, 457908099} ,{83264522, 1297911687, 597538937, 1855410074, 981647163, 1483517469, 1044675464, 1919270391, 1491018532} ,{1278029851, 1153372097, 156234847, 68468547, 599830840, 1887177655, 1106335403, 3373703, 920772591} ,{1239777941, 413831440, 1712531127, 114303244, 458992927, 87283343, 2004104504, 1989847044, 192043191} ,{1029774917, 1555476109, 194240364, 2007966507, 31415665, 872932341, 2143571874, 613471257, 1642061825} ,{1957027266, 1007092817, 1857775737, 1568047372, 1024287236, 966378368, 231053120, 1487178780, 503774663} ,{847767066, 1270395251, 1934370630, 1982987730, 2045300905, 1617921872, 509104516, 1375304420, 1563944358} ,{626507006, 260574830, 279289282, 951533488, 824232784, 685542472, 1327839605, 1023660538, 251203231} ,{1602810846, 1647703761, 563472650, 73985347, 72773775, 1730965059, 1997744344, 1951755203, 1859265648} ,{2122410562, 2131838485, 216708364, 1437419916, 1271125407, 1603743153, 1051262631, 103110354, 590791689} ,{897490748, 1056766174, 320740086, 1686283694, 1593477934, 1601637871, 941282837, 1034712154, 587083586} ,{927311022, 1155656641, 879939955, 1995673876, 1628337985, 1045846148, 1372887262, 1729911699, 915494708} ,{2058520682, 2014803609, 2098046911, 242233817, 1522311464, 1509069735, 1033001592, 1169173448, 1972094216} ,{1714219000, 1687683951, 1006852688, 319461976, 556687945, 1087707301, 733529315, 1673339451, 1152135143} ,{2129535743, 101004771, 558136913, 531196592, 1514101252, 1825273455, 1529903939, 2035839678, 586809000} ,{1214829751, 2096074784, 1944426662, 1727421090, 342931452, 704516730, 624103983, 1831107243, 24166464} ,{1778587597, 520987524, 1656653721, 2133026491, 989561702, 1723842965, 1590179595, 1039592721, 733078432} ,{806633354, 430084343, 389932910, 407859347, 1357068248, 1651001222, 26830847, 1150087827, 1706156785} ,{345086190, 937224198, 1290285867, 249322220, 81290853, 1890185855, 91849906, 861419847, 531413183} ,{653771688, 1372099649, 461439901, 1065283409, 243725297, 2002273017, 1586005445, 1047052415, 1487869687} ,{100665786, 1622701627, 732314954, 1587851258, 432890606, 84230049, 1669202078, 788882166, 801026942} ,{1318493959, 1913814356, 1762366808, 1715119442, 786282447, 1344967288, 3138069, 532905112, 1888255846} ,{736310788, 2102620438, 1588698673, 1634368997, 2136096799, 556421474, 749706063, 1694094422, 1757912116} ,{1599127011, 46577473, 1048770751, 978710010, 1370020551, 667999025, 1351842004, 935062038, 828000473} ,{807670171, 1094142179, 1608008550, 991990412, 198434374, 1992573625, 1447478693, 1881237240, 1687484987} ,{265464773, 566894555, 1182529601, 421324757, 1979928987, 1869846255, 2138696147, 424400382, 2046137218} ,{79233778, 737326225, 268618117, 1773273213, 1631571786, 494846038, 2028157286, 1707663257, 711767099} ,{404199915, 2059281779, 2081824292, 1341799973, 1811304001, 1380940897, 1276372431, 2076185628, 1943632490} ,{1456215895, 1031054119, 1030118313, 323348484, 238965148, 2131991291, 1465037367, 562204682, 256941874} ,{125600335, 1857101505, 353296118, 1616860542, 562859847, 1276270476, 1624588369, 2014622254, 1360980611} ,{2013080266, 308718285, 1735213640, 879847232, 1118210911, 248385286, 145040237, 506960602, 1155523065} ,{2043468827, 626148519, 1268740648, 687423790, 302865008, 662553706, 1681091945, 635912521, 187669808} ,{1062819161, 1513813348, 106496566, 567683021, 1489807225, 1249200824, 98773007, 1284731745, 1577022026} ,{1709479091, 1074093687, 353796926, 252736033, 511903574, 1656892011, 955000728, 777479237, 2065146941} ,{450445629, 1580422675, 1186512315, 1533752850, 101018809, 241650699, 994560388, 2044650173, 781255619} ,{217364360, 1544027822, 1049323016, 1354192882, 286674213, 1467950296, 106043683, 1639200140, 1860437811} ,{331764363, 814135352, 281829312, 1842620346, 74898550, 1020140372, 1213270370, 344233893, 1350281058} ,{891260147, 1518756119, 1679590822, 1583964072, 1406201744, 1958434344, 1423635531, 119323995, 827567434} ,{332717109, 1828007153, 202550184, 1855816772, 1026388795, 530440159, 2057005593, 384245538, 677154492} ,{1411266440, 1402779861, 372352181, 1299332238, 714993960, 908836607, 805878645, 1554145624, 600539721} ,{810800389, 1719915188, 780376533, 36861089, 1149436640, 255019046, 1335364961, 1824341182, 725665591} ,{1292843760, 635263803, 1797284436, 1387114617, 671079100, 182768487, 337451095, 732135822, 629579927} ,{1839825053, 46782759, 1667676507, 954094846, 257168483, 798708361, 1495502763, 752791426, 637936578} ,{328394033, 1885252162, 765297046, 1032782370, 792397607, 1018694974, 1911992358, 148836680, 740304769} ,{1163416241, 1742410785, 1439526530, 1047124015, 1631672393, 1442589283, 1256742828, 1466257484, 1547979003} ,{738407437, 491790506, 850386453, 776293565, 1929404548, 2058709003, 451960849, 1584474909, 193722324} ,{2060587073, 766875857, 601602823, 1555527104, 897956725, 1441534752, 1385555848, 1929576016, 1940592037} ,{1643546180, 2006713140, 2133290079, 144324825, 1499737465, 1431471977, 1310152340, 1833255153, 106478885} ,{2074763817, 1790344201, 773264172, 641740783, 679621493, 742054318, 298796796, 107665175, 105067770} ,{901365789, 1628630459, 389819717, 1713085921, 512904226, 132036427, 1095226244, 1759253465, 434036425} ,{173041388, 139211702, 479519508, 1950328798, 1445322607, 937640675, 1761958007, 2085262772, 2003970825} ,{1600444188, 1990629013, 17508298, 1550052211, 575493160, 141983359, 2007923587, 1598898232, 1304368967} ,{70430698, 129487528, 1753763480, 1572093111, 886133023, 2037572066, 1684858351, 16025047, 1632143533} ,{805469680, 760770495, 1674988839, 1511661887, 711592772, 478856107, 111524831, 506260555, 2145014313} ,{1730719692, 1689332384, 1913307014, 1223216846, 1699790622, 224587033, 1083082276, 414641320, 1554666160} ,{486985839, 1113958997, 1540178251, 569356404, 1983308391, 836139771, 281161690, 1544691636, 1013169589} ,{2029787859, 690751443, 563140957, 1576744897, 945129625, 2020435157, 1681349752, 129279025, 1436752878} ,{1575460876, 1113356932, 1315858019, 634677551, 698744569, 132448896, 840004176, 2058663007, 729831793} ,{611809301, 27893866, 1384039724, 2140490186, 1014329296, 2068618440, 660023270, 835740218, 507071855} ,{1766877320, 823542173, 1988651737, 932731464, 2126862703, 205135111, 950486807, 1351680358, 1439026213} ,{1837134490, 660637864, 708571879, 1750721362, 1581458742, 1091382616, 2044574881, 1142494336, 752088995} ,{326236199, 436508074, 2144917591, 690878884, 863094585, 214951222, 136949381, 750320783, 922604858} ,{370800030, 1982757427, 1176271055, 864510792, 2030730288, 779235593, 151919321, 918713212, 850187205} ,{1261133616, 564904069, 61509129, 1554133119, 2069294162, 1841494221, 1671843981, 1076361908, 404077306} ,{2074382005, 1583202064, 393743342, 1120629885, 1636169904, 1394039274, 1409068149, 1593823674, 434086524} ,{506143672, 1211782675, 461244034, 222153447, 484578579, 175638931, 1580886414, 1172081755, 1684962700} ,{1148497677, 601540323, 818184713, 341905762, 751127746, 2024927236, 1827447098, 1635034560, 178865650} ,{1096357131, 959996576, 1297673757, 1210549640, 1937530758, 1757387586, 1326833196, 316661292, 967705847} ,{378497362, 1677394894, 1076445271, 568026627, 1663847155, 1759669182, 675309422, 546416694, 828356218} ,{629152162, 2093411549, 1429945971, 1506487220, 1091014080, 2087956657, 377038594, 730278618, 620668199} ,{474840910, 1022916065, 113843446, 1939502976, 692595978, 1930463570, 511923020, 1760329919, 2116589971} ,{975598288, 145289375, 1109777004, 1754127867, 1381488587, 662823433, 126169526, 206729980, 1309133094} ,{744620263, 1063040426, 824291071, 729265736, 2002757533, 1124545881, 538074195, 4068856, 1571836704} ,{2027084896, 503856682, 1567277508, 947866019, 1202971471, 1512363886, 1682195242, 573846049, 1968492995} ,{1616250442, 1185815638, 1333416251, 462479640, 1729003775, 834876348, 1900202988, 656402053, 747643780} ,{661310878, 325390820, 1957939639, 1157043123, 224352768, 2026667295, 711735174, 263690718, 2064796299} ,{939930037, 185908257, 204032938, 842590057, 969318369, 1682293844, 44726822, 1041684034, 2107967510} ,{249227816, 1131389066, 1094558885, 1636454669, 1415300682, 614662601, 986801157, 968951717, 754045034} ,{97914932, 1037128838, 1824564969, 935767272, 33685082, 143865153, 1022592889, 1544227402, 1855437503} ,{1074975749, 290572089, 1438734652, 1132201277, 547083717, 880698218, 915741911, 1992807614, 1352839605} ,{250362038, 1381599928, 347404038, 1760420280, 1457434435, 1079224850, 1043417164, 1824721002, 1033530449} ,{1548929720, 1341348958, 1269963648, 1974608844, 42255324, 799223556, 560738674, 1134105214, 1148557052} ,{1171457408, 268102129, 377246356, 1709284230, 1038372491, 717085495, 1736879168, 1960369980, 352242998} ,{386050500, 626503280, 1365971188, 5133365, 368826076, 491385605, 1730857138, 1332068649, 868969690} ,{332719310, 1869428023, 386800100, 1377044646, 6747529, 679234441, 256442228, 2146283325, 1939902924} ,{614007450, 149410958, 2035349108, 504683433, 466694393, 1008250174, 1038216453, 1537172045, 465555089} ,{1653764691, 1768747111, 866044310, 664423155, 1747549850, 223561828, 1646791129, 1054413901, 1485879167} ,{1347642948, 1601333279, 1618886919, 814104041, 17539847, 1988651706, 1567103916, 1983604588, 182271700} ,{608801499, 1375994953, 611142325, 1515745863, 2141556097, 1055540246, 885948333, 454044670, 908898639} ,{502252022, 1081289833, 572924726, 1119033478, 232877938, 1120249990, 1254579053, 2075795180, 1653833272} ,{1000378956, 1036040998, 1709923213, 3070674, 780594122, 256411634, 975934047, 413113034, 2139243113} ,{1231126399, 987997749, 1152476231, 1947707674, 1643976254, 449729327, 1746690243, 1658488636, 634008502} ,{1939971967, 1015677977, 435519316, 655489574, 899983765, 1088307065, 591482755, 2059083345, 716175506} ,{2114113204, 329358989, 2077255715, 296275380, 1062927488, 18916097, 1748512534, 1187534546, 1291535491} ,{1204040145, 231343630, 370028692, 1931495457, 366496335, 346800491, 1429764526, 2075322967, 391752573} ,{572913749, 272333798, 1480076138, 923365800, 1116904318, 1038181627, 1868331845, 806320566, 1478937214} ,{515599240, 699295032, 1866172858, 2010349623, 531071864, 518835298, 1469832139, 1388534192, 1333575085} ,{1414951818, 1119432163, 280254861, 830050185, 337083195, 409450662, 351695494, 62711668, 1174127570} ,{1896916370, 2078540156, 1494437473, 1224011188, 1516768430, 340877603, 1697252859, 522598505, 1870729865} ,{1280881998, 1772476208, 1093296541, 1910030294, 944487479, 1109419144, 1134496570, 439214671, 1971878350} ,{505666293, 493793530, 718637096, 1328602835, 1753941362, 1712674698, 2051936234, 2113663706, 1658404159} ,{2143963153, 1722368743, 388411820, 1833517377, 171522777, 220362363, 633407540, 1611824738, 1540119401} ,{841368718, 1826356568, 1381227046, 2077013793, 845579160, 1317129152, 61238384, 337593441, 834795638} ,{1958176111, 1362066357, 2007883405, 1532329326, 1051664298, 1857820724, 1787142881, 1014203500, 696693073} ,{327485071, 1752504138, 1750876093, 1648594465, 494684725, 671516722, 1730593605, 1579757615, 1552382295} ,{2108223303, 270373764, 1234479717, 996178777, 1340309645, 2002101802, 748492451, 1915322425, 1206817708} ,{746482448, 2077034009, 1763426695, 1487108773, 507844300, 527274964, 2008377187, 1535796034, 1473554241} ,{488472089, 1941862098, 1417024028, 1244183253, 1037634427, 589780861, 1317974909, 1749259676, 2143380458} ,{406765810, 2071701225, 803903500, 1422935879, 97721901, 855591368, 1363439980, 945731886, 410960835} ,{1212220581, 396925483, 491312421, 1837839334, 813570300, 80022462, 678052924, 2030017540, 103064161} ,{919189322, 1919974499, 1500261053, 901379807, 1335737752, 1813006601, 973082925, 1097671695, 414681747} ,{1473056810, 1303681806, 1552317804, 1244544795, 35234342, 1861853746, 1544914087, 1455444141, 1780686722} ,{486674752, 2099521702, 828358042, 1962862416, 2044725826, 1070192080, 453790814, 1159790537, 1316676591} ,{1001264394, 465769113, 191439495, 276516820, 912494321, 1618565128, 483935159, 2147248979, 53582996} ,{1296893596, 1853165565, 1710364022, 1648285648, 1984652717, 969756077, 1048826848, 2140189710, 161844495} ,{1259382698, 196153504, 789701677, 1417093047, 1410484018, 1688104842, 413613347, 2023947652, 1990592528} ,{114523193, 92395130, 734181548, 935198428, 1797074484, 1448176383, 549442361, 850267784, 1928977489} ,{542857381, 124625111, 444023154, 750773438, 885672672, 41999210, 1503065254, 1727939152, 1569384157} ,{1319806064, 51984722, 1272432375, 1692046166, 1337759610, 19549329, 84474104, 2091976959, 498193492} ,{1662729394, 1954877312, 896117083, 1799680802, 1242801733, 728676930, 1714821981, 1306867291, 1966514187} ,{1639247113, 369454356, 1960386258, 1563192577, 174822087, 1191582237, 1221209928, 1994762561, 1731513020} ,{1920646753, 1962194403, 1786427868, 849801926, 639252726, 538348763, 1920128348, 1036750463, 598512611} ,{1042063854, 710509891, 1257134092, 1195775763, 411825246, 1849026095, 73104047, 1921808623, 61314775} ,{1163706368, 569383794, 1107663301, 1300766957, 298797601, 1021080024, 1179782022, 1102591614, 1416129089} ,{1664025949, 1699040400, 1605851209, 180953605, 2053870503, 2082998959, 769427391, 1092770024, 2100897724} ,{278256194, 700762957, 630170880, 884695403, 1687552755, 2106611842, 107605100, 847033618, 1176342624} ,{861751986, 965680951, 1941715713, 193129131, 1264519660, 1308312850, 410064392, 1338096782, 1010934151} ,{51267305, 2006102034, 998516084, 1149542172, 492793834, 377545742, 1081123439, 663774786, 904717753} ,{1172138283, 472684534, 2084483272, 747213655, 1661796494, 384558682, 17152451, 1922326303, 1311104835} ,{1095490839, 1025857426, 1634489002, 2101933518, 615521323, 535061450, 373475671, 1004849392, 1552314224} ,{68014517, 1168109190, 400426938, 603522928, 2042652431, 822181562, 327411756, 59432084, 303864284} ,{1898685754, 298052896, 1062528567, 2125282109, 544817969, 1136751435, 127794653, 1328934704, 509187552} ,{852270820, 95962237, 1724428165, 1236523939, 752970970, 1945151097, 721502734, 339752888, 1493050918} ,{28476022, 10636551, 2115109178, 416332136, 1264181727, 215418308, 751222582, 368152597, 813205771} ,{839676798, 793980418, 1815056195, 1506215502, 1980200197, 285345174, 1070940290, 57644577, 1637902048} ,{1198792905, 1989118555, 51276092, 1878511014, 97205667, 2106411950, 387773733, 991170931, 931411847} ,{850187531, 140069448, 1384165096, 521771042, 1068278298, 1023619818, 811149795, 1848481349, 1387677467} ,{1636003768, 645902786, 1876830052, 1873334687, 560788500, 932294926, 47288275, 1319912879, 2138928457} ,{1389402584, 1850595603, 1169522231, 781724372, 1865030230, 1793801428, 1400685190, 1365226379, 1279826815} ,{1594879766, 1468907571, 726176036, 805918607, 1215530003, 1554944747, 933564651, 1660286985, 1171300470} ,{192584802, 1949043919, 1593951598, 1547251604, 1439915602, 2096387603, 1182338484, 1562929864, 1322386879} ,{571947755, 359847083, 1312681763, 862880463, 482386975, 1770278104, 1122581156, 40480329, 1434586501} ,{221219697, 736399884, 196439139, 664457906, 62562530, 336850443, 400664031, 1705341501, 1720550834} ,{835876257, 1165507444, 541721440, 828237654, 1449535079, 550828207, 1100550844, 872541588, 2003138982} ,{1682825439, 1846584434, 2113322218, 2085558455, 528949803, 1150221348, 422254558, 1723020995, 1536505190} ,{470878312, 759880669, 663851205, 1434886468, 2134746201, 1883563448, 863597457, 810504811, 884701876} ,{1915746184, 204749535, 2053084661, 346712453, 732003819, 2111218747, 2065777376, 1341677289, 1228784409} ,{198618425, 281145144, 1874533762, 577329005, 1764594191, 748499607, 216694529, 1467853561, 1358219627} ,{1074688741, 652063001, 574999352, 1519928420, 1541359476, 383296124, 870342438, 1000560202, 1405911770} ,{1673185752, 1577335814, 761250701, 2063876977, 1054053528, 132727342, 929544496, 1309039674, 2099128195} ,{255959462, 1770564243, 333664229, 592001419, 709162929, 1895726796, 1388146768, 965143509, 1337730342} ,{1798450381, 1707124006, 1750607295, 2022109959, 1525326641, 1185040555, 798972189, 561353687, 2080472127} ,{2063354890, 602445287, 808259773, 1931593566, 1345639140, 954012899, 366981076, 1373653460, 864860429} ,{939950489, 128089993, 1826844900, 292863019, 1535260207, 765474963, 1881051210, 708236919, 1180003103} ,{1307730048, 1227511927, 1811558968, 703767570, 605175387, 1628799869, 60363008, 1541633976, 1783590413} ,{1970235810, 1001879748, 80970160, 1163419557, 1772646543, 1386039038, 1528591779, 521038252, 731289696} ,{1131168901, 1504125507, 1144184599, 164920781, 1516276491, 1215590574, 74482804, 568348529, 136238500} ,{1492002979, 1057755255, 1936900000, 326384279, 1737251926, 428180030, 713295166, 720976065, 2021781215} ,{359949084, 560723925, 261089093, 1420845913, 2093522714, 864238475, 1569218362, 1086367851, 746926325} ,{178620966, 2112523540, 1685681019, 617625631, 922052442, 546767649, 313196336, 78510184, 1324070607} ,{997662180, 881827819, 2047764294, 1729486786, 1534658897, 532646056, 1431841445, 1765213192, 57840821} ,{639669337, 162369276, 1845688860, 1583595909, 1897033393, 1763846108, 2087336181, 960402428, 272402355} ,{1216284354, 1356102994, 515488120, 1569747819, 104083624, 286158376, 2133319606, 1467331051, 771633762} ,{1060373286, 2118538359, 1147949396, 1443642278, 657898668, 244169156, 632993462, 1436842931, 1336119648} ,{1942773372, 668983248, 253129061, 1891385429, 1058055895, 1277502756, 1523990014, 713743830, 1881987378} ,{1872779044, 1720463451, 627255923, 330483987, 578705786, 1494487515, 1714798028, 589298136, 2090438356} ,{1319255264, 1188941230, 1364483006, 1988281180, 990820277, 1156698617, 637515943, 2087273147, 1011893952} ,{2145320968, 10520887, 1585526616, 1728008632, 2125157160, 118652007, 148134043, 1998284264, 1855254397} ,{315279014, 1428364007, 660131680, 1363643729, 1933425604, 673771258, 209563375, 753425636, 1240229902} ,{284485148, 989205550, 1931675388, 1895016022, 838987663, 1748629207, 2071274442, 1384818332, 1642791435} ,{1435251266, 1323143812, 759018009, 559129503, 1979625847, 1465050963, 568372719, 252824869, 710021137} ,{665679823, 1983670992, 342885111, 753898094, 2123937391, 2072805601, 1872260980, 98606751, 1123057387} ,{867446351, 593403812, 1468556035, 609169679, 2008848259, 1470913595, 416865365, 1000137713, 907180783} ,{223253621, 1348970463, 1637429505, 613854348, 354642186, 1378205878, 940309083, 606868716, 1860225053} ,{1740959866, 1457108073, 1756995992, 182483012, 1002440536, 1492017214, 590544873, 1371656677, 200238152} ,{1305927276, 758336455, 440687916, 569384987, 695226577, 2019248826, 378195921, 544390597, 182567321} ,{385506294, 1916800977, 1628385909, 19132242, 1618460360, 1137628680, 1230215871, 769672693, 597455641} ,{762344379, 340650251, 1671937891, 668414077, 1979369034, 2113939183, 1254181724, 658947203, 2070860060} ,{826834439, 574625490, 1368475816, 843023754, 223531709, 177859254, 661979983, 933453307, 648442328} ,{1763892279, 698310533, 1031072396, 424171116, 1628072207, 2121057700, 288702201, 430499528, 188907184} ,{1343448910, 1317856871, 1020736345, 709961332, 5679526, 1921391576, 2066236211, 1167894664, 26413123} ,{1195594260, 1626778990, 698080527, 1976089702, 1019389317, 1208320999, 167793995, 1380490892, 1824707693} ,{1735514033, 176556155, 2003383295, 1851376908, 1372294781, 964990614, 1021391486, 2138134982, 838695902} ,{335049882, 410295943, 1271019837, 2093631489, 1441864301, 507776849, 2059665581, 1480122718, 709072985} ,{617983549, 827143772, 1171800325, 1077177590, 1190573109, 1577217171, 1800890321, 554240160, 996589053} ,{1708592011, 2043325341, 457660647, 1825884934, 37913942, 478950496, 2008014891, 156712081, 90438896} ,{1527956587, 603604210, 1151194418, 602897841, 913489133, 820438681, 971415062, 477650934, 102066212} ,{1815961807, 1058724759, 2015810963, 1160487444, 1209469129, 1174757403, 1926768419, 378744315, 540037707} ,{999811959, 403262317, 301974079, 202899860, 649319450, 2093437596, 1287257838, 1685094899, 2099645700} ,{1394178014, 931494868, 1780765883, 706839024, 691227860, 1946771116, 669899263, 794496829, 1379521611} ,{1557539345, 918159551, 1234959904, 792432347, 2038313538, 909891692, 57504353, 20686547, 635208876} ,{829153233, 2012390922, 800730700, 1119747596, 2096954506, 970568014, 1964270448, 1291246398, 1430269902} ,{1632705714, 1114369226, 1773962493, 1092075732, 2018750592, 2046160779, 950432038, 544676154, 1183868844} ,{893526567, 1115145836, 1737300617, 2002168648, 2041512493, 1554030814, 549144738, 1595219625, 2020056536} ,{588257836, 856640117, 505597191, 772171983, 1854094074, 221253145, 965029502, 649160022, 1841636431} ,{1294405911, 1239476154, 1524767408, 1288692731, 1662269593, 1967550091, 2017240882, 797406787, 2134796494} ,{2109314548, 2006692866, 873391425, 1431558046, 408825329, 2127426233, 1524656614, 253683137, 2145397331} ,{1526774927, 718897603, 1024634012, 1892507245, 640240568, 936145655, 160112121, 1943842185, 496766715} ,{1117735840, 1767263501, 806750619, 852118579, 1495990295, 1376055957, 3008487, 1602684885, 1471921497} ,{2018329443, 1613177125, 1052895039, 241786167, 1542708512, 1228884367, 1374404787, 512363768, 74891005} ,{1090587196, 1769836808, 269337534, 316450007, 1348847261, 958634560, 2081299085, 124866239, 1197769895} ,{1741101978, 111561175, 1168656972, 240083116, 1936494521, 1434910765, 1203309116, 1536911843, 1720697923} ,{2079513246, 1379661789, 2066472203, 2125574517, 1043469669, 1296077247, 216980796, 1620134509, 1442790232} ,{1748258923, 43373580, 375223862, 1661785543, 1678554676, 1901089151, 890958665, 1886820922, 1232915718} ,{612102430, 1444107624, 1013236673, 1789836195, 624177347, 156129950, 2032333014, 434274546, 1116426350} ,{1248311709, 452406052, 1312079225, 762448458, 417385542, 981864595, 431555705, 1148274873, 1411353707} ,{658199818, 1887449895, 645766590, 879477987, 267071788, 1442331594, 1644189085, 840882719, 2012872301} ,{1188144361, 1480052455, 1944963495, 1771714952, 224700047, 1171576111, 568544215, 1035853565, 399022211} ,{1860794749, 1013901033, 369906048, 1380579908, 561596491, 1328990512, 1193249557, 640820016, 2055296403} ,{509555163, 1524210269, 39151243, 266935549, 2033612612, 1667988509, 421722906, 763577626, 263121890} ,{986181541, 1109702647, 1896964684, 891853196, 834656188, 871913290, 518053209, 1676902214, 1750978966} ,{397395638, 724524678, 811460998, 740432820, 160293368, 370687840, 681720254, 2005069284, 2035499495} ,{2026415083, 569947385, 795390814, 176782719, 697603319, 182824653, 2127266556, 1150226342, 509772464} ,{1390524040, 1999543890, 853452483, 688661341, 1932206410, 1512860476, 2041938234, 1477221512, 556185422} ,{943571039, 1660924794, 1493233529, 576872650, 1375324404, 1223300748, 1924071726, 547692640, 1183631980} ,{1518897251, 1467441261, 1305807262, 903230612, 621468653, 2116943229, 1531244773, 1763159312, 1292853082} ,{1517754389, 2038845972, 255477464, 473403636, 344756459, 326515930, 1214938712, 1336665275, 1487536348} ,{163523837, 1238876949, 1459952181, 1073812193, 1258673800, 859521059, 1051745628, 1564694682, 925779026} ,{1265576315, 1950609457, 2083218582, 1273392951, 1143185166, 970786835, 1206572278, 636344289, 1886832306} ,{1924187863, 651419096, 527850389, 520471711, 135425547, 300075891, 1603939833, 1354957140, 1808674195} ,{387896912, 1075805166, 1858713150, 1226353070, 313329847, 81047788, 541856236, 607206658, 2033542799} ,{913574281, 1650495991, 2077388179, 1733091804, 821265780, 1438119175, 1037175556, 940583630, 1565803333} ,{953410960, 1643848175, 1846607840, 595745872, 1463147358, 1310154339, 1280679639, 40133816, 1019603793} ,{457644923, 865430521, 1489415609, 1545215641, 582174112, 1027377345, 1666704450, 2035610907, 621329571} ,{1061847360, 1614447597, 441289755, 429178553, 2082226095, 1242801021, 113683580, 389389300, 1945939840} ,{1549924761, 1701629981, 1243375820, 1111785892, 24183902, 1266530497, 477967599, 837123050, 804932658} ,{1369318169, 524402505, 2095274523, 1735230598, 1113888714, 1011508787, 345382077, 1292224324, 1091106930} ,{1723003553, 1455654405, 959797375, 1167799649, 1659559597, 100983885, 1461323927, 1407955551, 200407513} ,{136963636, 1661367368, 119729455, 528578519, 134925923, 1183933366, 349597871, 1587335270, 653100402} ,{462289241, 1969005912, 4552981, 1596912064, 389707894, 1914578860, 1481934027, 1829854431, 930374849} ,{639565797, 88467471, 891234721, 5469723, 1817215961, 1689980542, 514489896, 1543385177, 954908602} ,{1853462410, 953432986, 910788562, 909440150, 1961064185, 1378462966, 1072571618, 319291719, 1741937901} } /* End of byte 18 */ ,/* Byte 19 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{224069202, 940167678, 1265804454, 187150741, 625651655, 1076491822, 1103780681, 2055685332, 1194651977} ,{895554552, 717275268, 396264426, 2113216803, 486029730, 1692321363, 126074496, 1639594000, 2059655477} ,{869441263, 2005641267, 1911187837, 572980734, 332280544, 2023459372, 343983598, 237838110, 1322779029} ,{1259175009, 1755939251, 165881407, 1785139761, 155369278, 134111992, 1407432398, 1077790057, 1603503858} ,{755207726, 2061872424, 1362416010, 1660538798, 1641169488, 294311167, 2011026378, 741931201, 1488867750} ,{1020733683, 1871644402, 1573902582, 1571837473, 706027430, 1836510160, 931124026, 1890189158, 1977196507} ,{824281435, 1663202092, 38276168, 920271310, 471406236, 837832678, 1677770013, 1728916176, 1584272492} ,{1577095969, 1036947225, 369643279, 1800149053, 431349669, 1451331277, 1687038432, 350692143, 630729966} ,{1494347735, 6449902, 1870820201, 1871356634, 1503052259, 1094040930, 414164052, 867405757, 877337697} ,{2120574983, 989546032, 1119791626, 1549670970, 1397510158, 569467335, 383607147, 19544098, 1175921359} ,{1204394378, 1026989262, 496189994, 122178885, 1097256354, 390702865, 896314903, 130591216, 622052699} ,{34550696, 681518768, 6976002, 983175239, 960341858, 239929295, 1533613484, 875907249, 1620141962} ,{356174000, 1946292312, 933107752, 1135990737, 1123872970, 1821631412, 1584026946, 1225826400, 694833730} ,{545391482, 1451705608, 723601227, 1069642231, 878672069, 1475566954, 1710391346, 1115250354, 1991572998} ,{1534973459, 196589047, 1991102685, 1165446365, 1340258020, 1699457801, 1503666637, 317040495, 692422935} ,{1494923393, 488224762, 203390741, 1934278450, 1013737600, 1360865945, 263958572, 982899491, 1850838784} ,{1943446761, 538835895, 2127113894, 371680804, 1251575156, 2113487358, 1172644954, 1464493623, 1061124935} ,{330291731, 1391155759, 365837374, 812478357, 928551173, 309186012, 1546112458, 56590632, 49750993} ,{1190660530, 1986365736, 1493979198, 2038010546, 1357862874, 495892727, 2072036020, 1312797939, 791009413} ,{1460237431, 607080324, 1451762638, 616971299, 322144625, 1277113229, 1971140649, 893439713, 1405305096} ,{1667581109, 1636110437, 1230251455, 1175450993, 1757214439, 2018824690, 1597404637, 294236823, 1976354098} ,{786673529, 492453878, 1970523761, 894710268, 349976285, 1183485784, 1985800466, 843925199, 2094711930} ,{118828726, 5104755, 1854130366, 1811051998, 611591885, 607759795, 1104543526, 2068706858, 1820279767} ,{2029920267, 237077554, 1585421196, 1128394448, 1718486641, 278224737, 1165919991, 1901438457, 730860634} ,{1363409318, 1721710592, 2127095214, 1366134806, 692027662, 1029768966, 263437523, 1596054220, 133408685} ,{2010258762, 1566503832, 1867212842, 1408241287, 1664659508, 1077661630, 416289448, 2121533439, 814748622} ,{181870805, 1338077783, 1696867803, 966527488, 1650821445, 1811293913, 311295657, 535451461, 1570787616} ,{2079694704, 1712706616, 135350303, 49526641, 1497915380, 1414310199, 1680451198, 1394104004, 1439475708} ,{198738539, 1074966301, 660431440, 807400713, 1169340878, 898033630, 1419861493, 1706313561, 679505314} ,{876791217, 1518665562, 2049966360, 1063517031, 1476150578, 889666089, 557467223, 1190526050, 2133208284} ,{1195722961, 1263691161, 1430521190, 1300058655, 2068963536, 952562307, 1848743659, 545624192, 1019001610} ,{714406650, 1472939049, 2080385818, 178250362, 2075461961, 149154898, 1813433239, 1583658193, 769562317} ,{1530532545, 796307435, 432548006, 1561636717, 1491551147, 797491956, 1871412739, 799268076, 782310591} ,{292545182, 638856542, 225273344, 227763756, 2061683409, 751214093, 1358212782, 2114621294, 677320862} ,{355768090, 391304215, 1136046257, 1933548441, 1228445455, 1340527207, 1418481011, 470712446, 1707941193} ,{1961957318, 228822903, 2019833410, 638430239, 1853486747, 1474873124, 1933124546, 606081742, 1856106502} ,{1811202200, 543755968, 382087234, 582369678, 526239243, 2057318845, 198896550, 1586497709, 937332686} ,{1433217028, 1447812746, 818498558, 246554672, 1368563658, 1805180245, 723556384, 422806667, 1972200601} ,{1164078400, 636810452, 183226118, 518813840, 868356638, 229383012, 1391432252, 1320302740, 406250679} ,{1833301356, 1800143190, 738073471, 65177407, 1291194840, 728990986, 1127171720, 1818150952, 1943332195} ,{669180920, 1182664238, 1588411346, 1912886069, 377062673, 948450916, 826956796, 1193954461, 66552794} ,{1685316804, 493102090, 1922324412, 65645707, 1878089272, 332737938, 1591117769, 475122129, 530680521} ,{1054619267, 410314736, 1898841497, 1017505021, 2081439923, 792817269, 440239447, 1615040491, 549808110} ,{422266869, 1646891955, 698937896, 1078561488, 870217302, 317982545, 1124514917, 362858720, 743216750} ,{205704044, 1502580408, 1101573537, 1805446118, 782083244, 1860080722, 665293649, 391477335, 108195265} ,{2122161833, 4000972, 2101579122, 1411641860, 1559404706, 657108701, 1978965259, 611629837, 1023211716} ,{136356341, 2054110452, 860250712, 790878436, 1683634460, 2113759915, 1731047477, 2010372919, 872629756} ,{767232435, 1704724234, 980047257, 838640953, 1254408822, 899321970, 1739026391, 1411097722, 975272984} ,{85002857, 480595359, 1549700533, 1513682629, 1799038694, 128721812, 645654440, 1287703575, 811471531} ,{1160401096, 1918594049, 1808717587, 236691048, 729218309, 1082956936, 1000185930, 960367796, 2111076498} ,{1471587058, 884800600, 1679321723, 785928316, 262809455, 154996765, 14460120, 780867274, 1492744462} ,{154965831, 2048621478, 466687441, 1264215266, 1722328900, 1028015796, 1824868830, 1699252999, 1020216168} ,{1691962701, 413921713, 1141820085, 79914720, 1345223090, 2076592597, 92890129, 1013770816, 560906575} ,{112593413, 684871370, 849777808, 1675655277, 1046987083, 2041328046, 779558086, 2047055041, 437892001} ,{1078196359, 869364087, 1095885261, 559672765, 1270515271, 384621179, 853893666, 905055076, 524249265} ,{2003239846, 1961498268, 2139473128, 1415953133, 867408308, 1904609416, 1043363103, 1372898594, 1890132353} ,{258679922, 664779814, 427715572, 1215238184, 725164223, 788990206, 795814094, 292458157, 300681321} ,{1888268386, 1576466037, 2030140778, 1021957144, 643702240, 1559817196, 1054708030, 1726551157, 1864770435} ,{718616949, 1250957074, 1895924748, 938734615, 1267543975, 1965808783, 1530014221, 510603820, 1088832638} ,{1383098212, 10357757, 461132440, 113241699, 1706953814, 814763571, 1357081196, 1356250198, 1017517880} ,{1739156827, 823376654, 909124806, 1643283191, 1130611229, 966291041, 678297124, 1835375927, 1239848931} ,{261956829, 1734954674, 1700032864, 1706120301, 1937830290, 462153778, 1323530677, 920762017, 1293441037} ,{2040317225, 2043060651, 1072159458, 6119115, 2043218294, 1865820534, 1922360824, 1910273203, 1696745220} ,{2003215337, 1479730651, 2139001281, 605100140, 661738824, 456482736, 1549735594, 1565931235, 1724275627} ,{437829660, 838862814, 1275796922, 2091311471, 1618822658, 685622433, 284209794, 1347592949, 2113532767} ,{1428996710, 1680976556, 91386649, 295833663, 1923167957, 107085884, 483403319, 19268319, 1809049863} ,{1934223985, 1920828091, 1307141542, 166844889, 1858546978, 1780720608, 1542882788, 485829372, 829513239} ,{1367295213, 291674595, 766281848, 2128003995, 673612112, 1208985641, 450511056, 1540828396, 1852309091} ,{1465670918, 79259836, 1725766070, 564500849, 648054236, 1499010297, 878989919, 149480195, 58865256} ,{945857338, 1337251472, 1258319967, 1650928553, 2094928199, 791453039, 873937221, 1452424708, 298089363} ,{1151260501, 1625315757, 2089642980, 1870034788, 1694388956, 1329748764, 2053727642, 1365978478, 1001548124} ,{967674494, 673279798, 904823587, 1355648412, 643039396, 1837832278, 46833925, 1876127450, 1060136612} ,{2136579396, 1958919846, 953002606, 776781771, 243123485, 781620328, 1538428950, 1312073462, 2146129785} ,{1530865123, 358288590, 169254006, 532895396, 1116384132, 144608344, 548137815, 1951659896, 3806439} ,{698391991, 426024407, 1880745204, 2063215938, 91564276, 1362703216, 974302349, 541094110, 975923161} ,{1911100755, 160694465, 1819476798, 1331920299, 939331374, 1102421936, 1004132391, 14106321, 1041768344} ,{42247411, 190521427, 973765708, 83623927, 768531850, 1356850839, 391493732, 1235091309, 1302617533} ,{522216350, 950458756, 1399747187, 1299914167, 2129978285, 1841424857, 433125940, 1148320088, 439576066} ,{1448926471, 612224558, 702599138, 168964326, 1759063161, 1286216299, 145049251, 1299313094, 660058484} ,{699869573, 1010191551, 1917788136, 65132973, 393542369, 13617924, 195034945, 533741654, 1709971343} ,{2116601939, 182935938, 2022133029, 1515146831, 2033840076, 1482234724, 1932847605, 1375521081, 1659781880} ,{2024651567, 1059030374, 1473682171, 1866097430, 1606063365, 1102021602, 1302722975, 873273477, 978295101} ,{565179629, 1268420338, 234676979, 932564538, 704807156, 2036975109, 488103332, 2050218560, 2134714307} ,{2105282016, 1303663453, 795778395, 1047362632, 485647794, 137318018, 532968724, 1290320308, 893029145} ,{1804424069, 1656970582, 1844785587, 1685562660, 1079214547, 1991789799, 1040284975, 784592007, 1556226621} ,{325684045, 213928578, 771527952, 147727341, 1475617772, 636810074, 2051882979, 1469677763, 1283353102} ,{281638492, 368465257, 2090553191, 1731423302, 1247632460, 1558381973, 793500224, 1883194953, 1984378597} ,{1390461553, 1295580455, 1284998274, 1844686867, 465430865, 57299635, 728072490, 516119672, 1573279168} ,{461514990, 310664595, 1907328334, 473097619, 1872561763, 1222876172, 620328215, 1866595479, 1111879163} ,{321129994, 436475861, 1706690107, 1136883231, 1431200373, 1827318916, 637631757, 591589569, 43028018} ,{196210911, 646036091, 1161754518, 970314824, 1998971588, 1758478278, 331538756, 424779530, 776556929} ,{1952775001, 1703538743, 719294109, 1763876487, 923009283, 1127441675, 1935302258, 871772332, 1906379155} ,{718708436, 2084968322, 2087092838, 1970839350, 97599228, 1403923640, 1595719564, 1841834232, 741447435} ,{1783675173, 1201249070, 1254498502, 834766100, 475348695, 1972827130, 1637750830, 1335295133, 1403859769} ,{922216225, 462233075, 1598893042, 257115280, 1993242784, 676328308, 73687946, 875569593, 935174150} ,{1799932392, 1753322193, 803527788, 685467344, 1799927719, 264352397, 388881902, 764107043, 1854612801} ,{567267242, 1502859610, 1248844848, 1301053775, 2041137295, 1519064440, 717172294, 405798378, 1684359498} ,{1813761182, 1629745619, 1162619783, 1269349184, 933748927, 1209677570, 1812974457, 312685577, 502186428} ,{1703837190, 390337458, 1956697438, 1633844683, 755171395, 867736265, 42377010, 545270906, 1208571672} ,{1542574244, 259721997, 1952043256, 53631255, 807059082, 149317741, 2007156229, 317494216, 266439377} ,{2076515046, 1085008249, 1471574251, 1967530313, 1648855487, 1002473077, 798586127, 292160798, 1331850843} ,{255514418, 1683364953, 642296202, 1349566250, 1679216691, 1379186542, 374329277, 896428456, 935970002} ,{2116173759, 1671839322, 1640011816, 1117013603, 897717569, 823804324, 1781177032, 1782091830, 1347727252} ,{620805464, 87208436, 918942942, 467991399, 531083590, 299423253, 62525527, 1197726741, 219253413} ,{249898659, 1102217864, 789520220, 1967421481, 443335617, 907379151, 1653053523, 1710975295, 1455106999} ,{1147075214, 1823217421, 1491933134, 1374600863, 1601299999, 777496002, 1434557898, 657757234, 726839873} ,{1881449971, 762851540, 516077206, 613584613, 1441721430, 1285460056, 1784153104, 418627756, 865704986} ,{907748144, 251641790, 1598143069, 1063518104, 173533849, 363426669, 289827729, 1928446040, 1646061382} ,{388331993, 1639240494, 1911804992, 228631846, 1422143913, 751705020, 1648708235, 1262196699, 873000886} ,{1718510837, 1057716932, 919165695, 926122479, 1001843754, 134115592, 13231779, 814117174, 72657649} ,{1771327975, 149120133, 1984872286, 1165129023, 1019766659, 1139768077, 1263830912, 2011278392, 1604309410} ,{1960107747, 343975357, 1787519761, 231148885, 846036017, 409556922, 1858923203, 2050131958, 2061225114} ,{274853686, 1271303463, 1044706786, 1378452102, 724150696, 1819349767, 1159553644, 1402105056, 1951400844} ,{1874051005, 273751227, 2002892693, 1643554723, 1483789954, 154704287, 552881822, 1664794450, 1974853720} ,{194805762, 324804174, 2068832536, 1996854627, 954562650, 471370522, 2118601218, 1040595836, 160857378} ,{426738284, 978071147, 68781324, 2126520391, 731761853, 1258209477, 1453769107, 221836833, 2049983332} ,{1442621441, 369008482, 1064737958, 569544519, 1524187590, 2118948807, 1074725152, 1481880904, 1435026003} ,{1710129767, 118260425, 572123782, 2130450862, 551824850, 271334283, 799279330, 776961273, 1675752775} ,{788589366, 653316318, 1705514043, 2014775479, 1452102801, 1021555134, 703724652, 909478392, 274791905} ,{1708456168, 1014686439, 1983998882, 1829291280, 139603726, 1275720239, 1430122616, 899251635, 1435478389} ,{1938290648, 279957179, 1241831685, 1876220335, 1851846129, 460548911, 114271925, 225612426, 269643227} ,{455803066, 871225532, 1933091128, 762981448, 882933226, 1355939553, 706893008, 1374562354, 1905074955} ,{2012536233, 136273847, 1302655484, 1823683135, 1083963142, 827750536, 583446067, 223612641, 1113072900} ,{1035668578, 2117722644, 2087451978, 1492754485, 813450335, 1540490277, 1226157834, 419354149, 1633044024} ,{1944595159, 849642627, 531507040, 1264233518, 811575622, 61660139, 402551809, 2089708026, 724422907} ,{684538509, 799531575, 1431451633, 1164129117, 1224226057, 1526310738, 682494510, 1998997862, 739325170} ,{696055681, 1428980888, 1949635000, 292860134, 1477553838, 1292735582, 1711925911, 874802071, 205005472} ,{1348636741, 121254949, 1906454706, 568330512, 739654254, 1904089919, 1132649052, 1460393532, 1517006706} ,{1027691990, 65333986, 1704174552, 197474590, 1420199758, 779383613, 679771011, 1140670742, 1235713363} ,{854671999, 773524070, 108305487, 1378291784, 32163761, 367014688, 838843805, 1621567364, 2041595560} ,{2054526705, 162954288, 406335259, 377586337, 168924576, 2128224500, 1808940331, 925573227, 596593223} ,{1793023969, 801851271, 1519484383, 1877574050, 1719865451, 1140660523, 1442375747, 2124361815, 439467904} ,{181352823, 1421818942, 1431348695, 1004459355, 753745420, 1680468001, 950559077, 349641131, 1812487030} ,{638490271, 2101182796, 1808273566, 625038432, 1799638493, 911343560, 1489303320, 103782270, 689572875} ,{184826010, 848430075, 1974634600, 1245620356, 1518944791, 1857482795, 787644710, 2043686531, 1984770511} ,{1363805241, 1808507829, 612623155, 1365940592, 1889817326, 1438550177, 1934491693, 2084732870, 86418998} ,{2085482567, 190670189, 1117407734, 1380343196, 678178128, 212370567, 1794697312, 2040689325, 500807776} ,{1266034815, 431432424, 2100013709, 1335283161, 533962614, 1598932974, 896142074, 377388341, 509250877} ,{629139355, 2138652814, 488551924, 780208230, 883493986, 1381583128, 863991288, 1336804738, 200863963} ,{2146257871, 681189602, 1022698355, 1255157152, 451428377, 1111115353, 2029672352, 1833262017, 1635147771} ,{439703485, 1558020871, 646503317, 585500426, 680162595, 1940995563, 289277417, 162130783, 945539368} ,{2114359551, 1042288381, 1452366489, 211884544, 939801900, 1483378544, 1362680728, 593939748, 1590916447} ,{1763451938, 330189596, 639819329, 1264464861, 2067954854, 1645716700, 63818484, 1671625121, 1590260244} ,{1731012370, 1825727116, 1919650867, 627006079, 1295676644, 146130963, 43143930, 1685647527, 1457319286} ,{1147640766, 2067200818, 1600731719, 58771332, 2116495830, 162287997, 1221808373, 1027002772, 898051401} ,{816737525, 781445571, 90707392, 107847628, 476454642, 1032458349, 695714611, 1045463520, 110542410} ,{1584126686, 1829183198, 1515354242, 1057176252, 2145736620, 1918983952, 2132200047, 1062525512, 914859232} ,{1946609531, 1012071555, 2120900562, 611709295, 404738355, 1511874225, 2128615495, 1477306607, 639300319} ,{605734822, 679755647, 1096573276, 1444380858, 1996346680, 682496824, 1287196117, 671739670, 687495972} ,{1697233631, 81679193, 1851588950, 280737346, 1809993422, 668763428, 329031562, 380324664, 2106376519} ,{1115151700, 1442507147, 1640551129, 1828619277, 1247246418, 1052204052, 413286252, 824975630, 2032246771} ,{1288422081, 41074026, 2054061158, 1462160066, 1435069051, 1844984127, 12452343, 1779924293, 60476272} ,{902755993, 138814887, 1195260442, 970171525, 1423319562, 1218714660, 768710311, 1882238922, 979284304} ,{503182265, 217056555, 1630076982, 612518450, 75002783, 577813674, 783597102, 553007829, 847817684} ,{1022257753, 1915985941, 136752577, 174980491, 1785046173, 806576451, 1035566825, 349403014, 1018510024} ,{54277785, 92754583, 661433769, 1649561797, 760724308, 610628270, 1521455698, 902303938, 230169565} ,{805838011, 559709560, 830244895, 478940590, 1560157802, 1458782262, 534419468, 1150887235, 620476358} ,{2147110783, 121073358, 133413541, 1518683691, 1841875166, 1839190778, 1422432051, 54464832, 963819016} ,{1279158324, 1390067517, 527138231, 709743744, 2104925655, 1371858833, 1442852010, 1278487364, 2086951807} ,{592015050, 685518994, 696010883, 780892235, 711452671, 1552055550, 1226533264, 781102322, 652815223} ,{2062663866, 849502134, 1210777140, 1609503427, 902216545, 771814875, 403771022, 1682931934, 1816428921} ,{1075002654, 1991923761, 791028277, 820450688, 119785824, 971427738, 236280204, 1167249346, 355493647} ,{1683316526, 1663912948, 2062690873, 603317258, 857998882, 1718997258, 1246293468, 1026108003, 820983628} ,{1403960889, 779341290, 21969634, 1002597846, 2001771230, 1254322620, 1730440364, 826440448, 424026885} ,{629935001, 449124645, 1007189913, 793562173, 1801529074, 336021577, 567459814, 2098189791, 100795006} ,{1812263361, 1639185726, 1909848476, 1045417262, 571046673, 1112267732, 661561311, 1994986809, 839168931} ,{1859038612, 848079573, 1555833214, 1643376059, 887304527, 1485906247, 1787385304, 1447287932, 1669243939} ,{2099192700, 172518049, 55754573, 1632348355, 1462884724, 1585284251, 682021091, 427273332, 454345599} ,{1951917886, 1798191840, 1196783936, 1215872756, 1811797413, 1759265674, 2084279503, 808497477, 1206700749} ,{254873674, 2064319586, 264169534, 412900814, 1984949021, 1330868706, 442610581, 365361293, 1105453292} ,{770008860, 1137850787, 350529158, 43145319, 2119903447, 868347686, 690852562, 16095287, 718405726} ,{2108059419, 1195558340, 335137915, 1764727478, 213279863, 350363361, 319884921, 1760141938, 1529965467} ,{1721050611, 1755481036, 1638109591, 1179372387, 52368464, 186745681, 1680122785, 1128200324, 86995080} ,{1108838960, 1554146624, 1603854779, 221736959, 1800157958, 1166052988, 2054729692, 791071755, 986411932} ,{1540582626, 490989857, 1659084141, 1089168907, 2076703574, 596031032, 591479821, 989525152, 871376312} ,{656922648, 1846257828, 1876226202, 884262323, 1411553895, 327844713, 1009861276, 101314147, 2127713220} ,{1738331222, 435662726, 1782517160, 133324626, 301108797, 41995163, 2089941400, 1549328783, 80966270} ,{1188464265, 1173112258, 1529870171, 53260481, 1966876409, 1294020677, 754840387, 540685622, 1992384806} ,{1746129753, 56866372, 1370117130, 173269596, 1865517877, 841585329, 2041929906, 102839223, 4163049} ,{403844792, 759681119, 348071666, 190052854, 97082485, 1906825630, 754424519, 1966113737, 1234303777} ,{878877166, 2026237240, 150657395, 260631675, 1108624424, 215391679, 2115379772, 808738943, 1147688341} ,{1934323763, 2014192616, 1470812648, 925577446, 2004382320, 1659301846, 1534302096, 2106558630, 193344976} ,{2137054764, 2138560395, 1239832814, 886887333, 1294862063, 1820087957, 280679482, 1003397879, 836298623} ,{2086536103, 1960420303, 905164518, 1613730951, 1827887863, 1552255214, 1865113746, 1605186234, 621827587} ,{1899248451, 55235570, 1001979255, 514842378, 1867627358, 571614618, 587165774, 1812572910, 159485016} ,{1900194377, 950459652, 1271111842, 850267831, 793212722, 2110752720, 532348390, 1737687718, 2117757303} ,{177142920, 465064078, 1686352619, 1597975491, 440418453, 403064056, 1115783470, 133709453, 1789484515} ,{266461828, 276406817, 1667525216, 634968984, 296419933, 1042138636, 873279964, 758817726, 675078823} ,{1234560660, 1642089858, 502764970, 99198734, 1067992241, 1209993627, 830886477, 1633749567, 1247799169} ,{1988595382, 2524269, 75854653, 888456184, 1927117775, 1668747897, 1317153989, 1393819929, 258164794} ,{806233933, 104605955, 280043086, 1503944961, 1753128410, 1218288449, 820009770, 143199718, 1488260144} ,{1519623756, 661079024, 1062641559, 1135701936, 147788696, 1847197627, 1167741666, 1371237447, 419474866} ,{1862051480, 1128218083, 744876945, 750958361, 2120558873, 476500065, 1114972834, 1863956422, 1682922490} ,{255594434, 160066322, 1008759389, 1704510493, 1253154834, 1573846823, 780035548, 143007863, 1525224629} ,{722144647, 342517302, 1692949352, 319170105, 34614019, 841385952, 539470810, 1204097145, 150050930} ,{707678599, 2006882559, 545325147, 40748199, 742682443, 1651820867, 718807120, 1936649354, 1568093722} ,{2058430684, 1843046874, 1743277071, 1387383374, 1243961460, 1552092864, 1629541298, 602295555, 496287104} ,{1141897953, 1538803612, 657343709, 1358478414, 1451293759, 387520052, 1810201879, 617533574, 590911914} ,{2073028486, 1707750318, 1648156762, 1975880136, 3829591, 1626435442, 1293652722, 1737850048, 254575533} ,{757970798, 330952186, 999508078, 1495992228, 291684203, 1998397689, 1266436528, 1911732902, 1984809699} ,{1715739345, 1352865252, 1258406525, 683923787, 1319398970, 1289227393, 55557661, 771852788, 1908243953} ,{1000661620, 1682247519, 458572934, 528134842, 87947364, 1332015952, 1280857539, 2119944168, 204960364} ,{736217460, 1395826950, 1372692173, 2001593474, 2011552687, 1138150839, 866965197, 1376033108, 241450911} ,{2101230035, 1960032920, 502470924, 966276483, 1024327206, 687731496, 1979751818, 58614982, 1218560327} ,{614559327, 214620668, 522082565, 1050396628, 1004768593, 751144180, 259182102, 1174431257, 271595106} ,{125278026, 1036667609, 1917348145, 1247457709, 1764861016, 1637065394, 700130163, 1324534699, 1193899689} ,{673858257, 2026131203, 1028939427, 2027900591, 346750891, 1468158266, 1159329128, 333645208, 1088968527} ,{1382291676, 1717144864, 639238488, 1812324097, 2092711566, 803846048, 1216885830, 312672233, 1231657008} ,{1087003426, 1992620714, 123661247, 526758917, 624918221, 108930962, 300283775, 2032272331, 1094738911} ,{800762247, 1130237249, 1211718942, 1264369540, 1214783325, 1601589739, 543788351, 107675976, 747600004} ,{210366924, 849074977, 699635082, 1215797972, 1014941671, 1204943283, 600027480, 1005788354, 18670653} ,{1467365579, 1268414241, 661913986, 1850477808, 553295278, 647462106, 1079547343, 1794758785, 462000615} ,{83239785, 1128118033, 1768481842, 1132910099, 1033893516, 1635855020, 1195932585, 1251204606, 1240216998} ,{1583473088, 1426305959, 522000824, 643675676, 1952270898, 1731083837, 1741482581, 141530955, 1365864152} ,{1273029620, 338299903, 2115772360, 1817936998, 1536223139, 2040223183, 1723816793, 462981844, 1803676223} ,{266641722, 278708955, 968982543, 398710068, 1169311100, 400132717, 506813545, 758196239, 796568234} ,{489403740, 387472058, 2043891459, 1250937394, 1871465795, 216180749, 787112191, 9080486, 365049072} ,{1218438816, 1182445150, 582509383, 1697784885, 1910037976, 326494835, 2078848926, 1614432855, 808115565} ,{2141438715, 877107049, 724114978, 1508004380, 275837403, 1738216234, 1618977849, 1907729774, 1111370535} ,{1461854384, 635158806, 1822479404, 1732768371, 886065765, 553006501, 1736301311, 158695271, 2011638426} ,{480610959, 923473919, 2126113416, 579326734, 945017929, 647885901, 2006727533, 1272836468, 1358518624} ,{647693355, 1392071757, 969230320, 1600066751, 1708617414, 628508171, 1930368466, 1568682882, 1374326770} ,{103419126, 1903864243, 1927593512, 1762191222, 1899789387, 364428996, 1370546634, 638254806, 173161652} ,{1846176344, 1238907708, 36852983, 461040286, 1145863001, 1022242717, 1504429132, 1222980278, 503071539} ,{648546288, 1839258631, 1210759661, 64019053, 911636655, 323777242, 1312185991, 785869962, 851330533} ,{1770031485, 1021748250, 1765923389, 958549231, 2079614051, 939108142, 1651983904, 809851594, 1151920899} ,{857694519, 755971488, 18664023, 1154017072, 1095630392, 348404224, 326219274, 395595232, 98178602} ,{618518704, 2034880871, 941340281, 1540470474, 2021189741, 100412407, 1657815215, 423364237, 1550889711} ,{234341828, 1776143788, 2087945730, 332141440, 1138043713, 405633960, 1945973844, 80547324, 1539348879} ,{53742823, 495088528, 1750071345, 1266312088, 392766969, 798401087, 74766594, 595898686, 828447523} ,{1800388266, 2128687854, 674394947, 1448156380, 257969743, 2142797055, 1535028713, 159277054, 1705461327} ,{629348521, 766844628, 14873287, 1446649637, 1580871502, 653946828, 644985356, 791408033, 552259414} ,{551035313, 1977956196, 637937513, 339428318, 1289282683, 1033474560, 276221417, 2068111922, 780191875} ,{922519739, 1250255857, 1117829134, 2060918877, 1032293149, 819999766, 1475074310, 470901045, 1404348695} ,{15447422, 239907709, 992272540, 700841405, 571670874, 449249476, 405538494, 2062141485, 386792266} ,{236708849, 863912481, 1641506348, 362289303, 1658364631, 1747432025, 1566776051, 995033691, 343735198} ,{970093161, 595062049, 225855619, 1323903212, 2092363776, 362903625, 635710262, 1316152985, 21097496} ,{662134054, 2058295933, 1320053301, 1356955682, 517245962, 1182303920, 906246929, 2006136898, 1747833330} ,{827536188, 2016108696, 1813986196, 9880910, 1629226286, 437980842, 1875441266, 2145205208, 1737397922} ,{920214924, 673207292, 1026441789, 1697577249, 1702801160, 780628798, 538473345, 2045982490, 2060449112} ,{631743781, 2065752937, 2053170836, 84794889, 1739485277, 1952016796, 940468228, 614624468, 235876525} ,{1025177208, 1039388646, 2114696014, 2092693698, 268905792, 1539570082, 1790594850, 392243677, 1956981605} ,{1030276901, 82038243, 46058912, 1276262702, 345997962, 1976299157, 1179715307, 1614981621, 2035612224} ,{285239666, 1292710535, 1718728606, 1288066722, 787269268, 1837121746, 1719981089, 1652440982, 423663289} ,{1503962926, 16337520, 567916968, 1215059225, 1014207967, 134195590, 1594690335, 120046696, 542694894} ,{997610914, 732889404, 689558067, 1697098256, 582527870, 1886964137, 1854481719, 940655472, 1541788643} ,{937918483, 664116158, 1865346423, 424452223, 1590029988, 657967236, 1205561161, 109879999, 1444197806} ,{871215631, 1375953232, 520604750, 1524267767, 2126697116, 104660120, 483973708, 66386781, 63352278} ,{764508237, 876995658, 590207498, 1262634354, 1113985855, 287295228, 1348647627, 1542226961, 681580601} ,{955487631, 477620811, 36668487, 271326909, 1822489881, 1324316146, 217402930, 1268977707, 2129966960} ,{1385861199, 1884536017, 1234729716, 296691035, 1766615667, 1292110415, 245620159, 103349797, 1408320691} ,{509303627, 2032766214, 1382156396, 661894582, 690011421, 458645574, 577385351, 1640925715, 1726300156} ,{1333648783, 1705462589, 758323473, 1366569260, 744105674, 1560808062, 426789016, 787696375, 2079024678} ,{806830011, 1064875755, 180417669, 1799663562, 734380071, 1963601257, 1318518329, 157066141, 786781104} ,{1370351540, 420766473, 1924365831, 1859038734, 74293307, 2088627081, 1300720535, 866915635, 1381485570} } /* End of byte 19 */ ,/* Byte 20 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{2057004684, 1493057145, 1403435589, 293942661, 1174538212, 1202602235, 445653895, 1748576533, 1958400796} ,{326008212, 689616539, 1998408437, 63370352, 1029484943, 1864003853, 594387812, 1143824806, 1273391355} ,{1991460307, 845822920, 1698463356, 12530034, 708098079, 900431089, 202196469, 908128911, 1571368966} ,{480611564, 698258957, 58642830, 1110046136, 889827382, 959541639, 1762912721, 600400866, 1323932173} ,{712899949, 1065416683, 305072726, 1390372915, 1311110036, 1782269342, 2043510468, 1534137710, 1847903661} ,{1706688237, 1256055868, 1106147095, 1142001677, 1797420356, 336592438, 773591377, 549122148, 316737416} ,{766703538, 719114066, 1921605263, 481446117, 2126558390, 236019463, 13637351, 1657789050, 472590399} ,{1367753254, 359163413, 725928939, 990141691, 490259944, 1383688327, 993257301, 749875818, 13055043} ,{1175102122, 934858792, 1322929596, 1359031153, 1119796165, 738321414, 420272007, 1963555227, 1184171112} ,{969205947, 1083680420, 2055462417, 1276984209, 994006866, 1496077615, 994964340, 928746690, 1742687348} ,{664321318, 1988110833, 627277680, 176994333, 803361452, 1540850007, 839599454, 1386373077, 2143491975} ,{347138697, 1886700805, 1233995738, 1488854386, 1139037303, 1443079714, 84672744, 1888715655, 2088599559} ,{480553805, 583484032, 1293153906, 1188439078, 2013920560, 952161112, 100904058, 938374293, 933870281} ,{575473114, 1841922782, 279698223, 530344551, 1325314017, 996262098, 1863741771, 1059150937, 632488642} ,{1940277118, 1727655663, 1975174645, 1208221905, 507608333, 833810307, 287476667, 1859224348, 715873270} ,{616793459, 1893772612, 537768115, 1979763980, 2095739426, 1570520719, 2028195328, 2128889074, 706383156} ,{1738409816, 1876319505, 2117553733, 394433677, 576334867, 838379340, 1824120964, 185488069, 1477781684} ,{1349385451, 1747691947, 863519181, 1884406015, 1465093658, 309039398, 540607027, 299329185, 1897199355} ,{301386811, 1357166917, 1216558054, 605534421, 1291569734, 285313892, 257704733, 1911202135, 85778075} ,{1288693491, 1926031733, 948855490, 269038594, 350253807, 508621370, 1844579834, 1126674205, 492788747} ,{75150727, 1373002184, 132913261, 131710748, 951732606, 2098318706, 42746808, 975742824, 1039719347} ,{1453280185, 2123445014, 2027741438, 33663365, 1294110971, 4184214, 1382031869, 516319852, 254405564} ,{1284250444, 1969851, 1326290493, 265308663, 170056795, 2129462572, 1869468781, 1865852419, 1557117913} ,{1827860233, 417096538, 817753285, 556481419, 172468690, 1173209827, 1158749122, 365667591, 1225601039} ,{1382175323, 1601853364, 211445990, 1954752359, 446050799, 1440214548, 566364357, 1434402287, 695629275} ,{728921357, 1299124112, 1636783535, 1310232837, 1498764388, 721879090, 310384030, 127856603, 1776137124} ,{1722027948, 2022404006, 769842024, 1242438766, 1918816568, 131204036, 294423882, 1354138772, 585874539} ,{56523793, 1178202244, 1279801811, 1629999487, 87069940, 406799640, 1137421937, 909779262, 2130879210} ,{1646469568, 1813726467, 1768631429, 1763016005, 1626690277, 2096455577, 712749430, 439811881, 1580130301} ,{2043143516, 594289127, 1763183781, 427509242, 784244863, 1597175814, 559927152, 1520024927, 1003855415} ,{1641396282, 833612367, 752114710, 1014522539, 1094253068, 473872033, 1982123869, 1287146826, 1287996773} ,{2083010532, 1501616673, 1403337849, 341625420, 1443048607, 2061053175, 145098326, 935086100, 1115768472} ,{430929789, 1480829660, 631763621, 390019347, 1472726769, 148888228, 1438646200, 2033976824, 1190331610} ,{644268113, 1321725811, 1977221359, 1132143998, 53405643, 344612695, 1319736004, 1099733126, 312744143} ,{547255956, 359183077, 144275905, 602785390, 1920908123, 1885630410, 558047085, 944339822, 1805137550} ,{206043346, 2036960365, 1479390315, 2143271149, 1339806661, 461606641, 1425202173, 892831112, 168322313} ,{712465269, 1543059027, 572680534, 1148363014, 1176116939, 112580177, 211154925, 1611329702, 826899908} ,{1465614201, 1471082812, 1805884848, 156731835, 271574675, 1065492024, 1862459369, 471238697, 1932002231} ,{1270709803, 100955866, 1015156851, 863327247, 1096923192, 2125813629, 1237489722, 644954209, 1837556860} ,{727929514, 1948106865, 572209447, 1745743603, 1275198458, 1049086441, 312403375, 1099531510, 1964104253} ,{2142818190, 2108077654, 1507385127, 760826643, 739671474, 284349464, 1837359544, 547247532, 1836625026} ,{1186819277, 1129947851, 1227986454, 1570016853, 1088063414, 392438557, 768510412, 442044750, 2007089575} ,{249335502, 1514656136, 1389742207, 867186605, 279194099, 1642137075, 278123319, 662469261, 154704040} ,{1138412749, 1623600618, 205913796, 1517990223, 1877993034, 44040150, 1264241567, 1827124951, 272131660} ,{663890081, 1764683428, 806037818, 294096034, 1837711895, 1639696082, 446892554, 1640922193, 511769851} ,{1008526460, 230454402, 170022707, 1675639553, 1052959513, 2024953788, 320282340, 1563540734, 2008958809} ,{1660855320, 941987691, 1060879003, 249241510, 1813690913, 360688329, 1901005729, 550257146, 117862359} ,{1899564477, 1832171501, 1400234077, 1547516366, 817894989, 845201387, 1856032868, 1756898321, 310354759} ,{880456370, 619896595, 57205613, 1617732026, 376485306, 1385478689, 538655713, 677583250, 1213072992} ,{2026697930, 1844882896, 579423998, 1782140172, 1006044093, 61796797, 235672572, 840661313, 2107677004} ,{1943018722, 238557294, 1278424050, 2082227745, 689203945, 1920732753, 2002031957, 1059394013, 1714280411} ,{1013094480, 1680723598, 569448587, 1467290270, 1465319305, 1326140530, 1179041225, 1591496753, 156676165} ,{1309927476, 338633984, 118072147, 339059229, 1661193861, 1598146285, 95591859, 198227896, 1162317368} ,{1278685986, 644455209, 1499911703, 2004241805, 1890989696, 841160208, 2064499089, 100551118, 1641048440} ,{138254466, 1649990131, 776736739, 1031896897, 666806794, 98362515, 205438947, 1711612542, 282490769} ,{65609183, 931570160, 2120906090, 1078114125, 176304470, 1230729493, 938531491, 1930744109, 466911673} ,{418439914, 1231832626, 812676509, 955359417, 1920465287, 441133562, 873028127, 1401418849, 248852684} ,{84918408, 575069803, 2074988856, 1605708142, 1705896001, 1404341819, 1476095037, 766952332, 578469226} ,{724412980, 1681762713, 1081799755, 1234150971, 1002177208, 1428218170, 1655665219, 547293962, 1025372686} ,{660596556, 1680691597, 1677014213, 270065565, 2059534794, 1019986073, 568399811, 1252241637, 1299127674} ,{130737665, 2032055621, 1450509013, 1894913407, 218662900, 375684703, 1641141914, 676268643, 304091422} ,{894783980, 1407582811, 355752025, 182139961, 318954145, 1094408418, 1905444361, 1770092264, 128918582} ,{1277048915, 1369428435, 2037345251, 23945992, 2071363564, 385590403, 77027510, 1070728187, 300744328} ,{137115395, 1628275660, 1784018621, 2007697726, 1809746453, 1043414360, 1418823595, 974238556, 1039266229} ,{1087815444, 801813743, 801901824, 1449960409, 1266550873, 850575067, 1255819827, 1762042746, 1294081006} ,{2021558774, 2083202094, 1610611287, 1883727423, 404570486, 731411429, 173501281, 1930376157, 1554299931} ,{634366808, 544704234, 1098709761, 1413488902, 1102900700, 1181669310, 1758867326, 1442130432, 1886645237} ,{190265193, 107584443, 521623222, 1711714456, 1403142458, 1436855996, 1736710541, 1211239949, 1945576117} ,{1818602194, 2092037187, 374865297, 1547772754, 448454786, 811281325, 627787745, 1864917955, 1406346237} ,{2044115671, 2139609086, 115072713, 923540241, 1709597056, 457800960, 2102326024, 617222862, 1708317419} ,{1707750326, 1358883296, 1652818483, 1919943202, 1520306675, 1405415776, 731426232, 804950562, 1453322343} ,{82321616, 1570670509, 1225734244, 1921722893, 740737498, 1112361214, 192162725, 243853204, 1084885606} ,{1800522813, 1681801037, 1600138858, 1955892059, 822737553, 299998776, 1065642557, 1260794617, 59336890} ,{1269136998, 1706926680, 297452019, 1468904992, 552494670, 1047980366, 574903985, 1804559509, 887266483} ,{2028352630, 2122245347, 2137521670, 1929120995, 119527489, 338580103, 193657534, 1217095764, 123040701} ,{36704471, 70787896, 1962599131, 1064489833, 1908465246, 1239057337, 1206806516, 212610823, 8690358} ,{764347042, 1919468321, 478508774, 1189573328, 886333285, 1866695221, 2080023276, 32068452, 16115868} ,{536230677, 1868841066, 852130758, 1663627798, 670337787, 1359037782, 591215899, 1349328051, 1379188975} ,{922671861, 819429522, 1679590862, 515660101, 1658781112, 603868553, 716617592, 1553334132, 256120707} ,{608759690, 1062201155, 195757463, 1169110146, 438667547, 1922877087, 274369759, 965706548, 959663714} ,{1996138415, 88135852, 2080271182, 542415663, 1854431987, 1000100142, 1944984497, 1687032189, 82854715} ,{69289307, 832953555, 41165152, 1421502474, 1196042622, 1560321574, 998291154, 427855476, 616796476} ,{907022725, 1589082094, 929655828, 360232601, 682413225, 1808495052, 1585172748, 664415598, 235119401} ,{922332699, 1955670008, 526617204, 1670254808, 1417796156, 1525479381, 100073603, 1957351594, 88478004} ,{461288356, 48510635, 1275216754, 654297052, 839612329, 1605046628, 1572443893, 1499806060, 314680208} ,{62059565, 1637224251, 1821988584, 845595377, 1238601031, 4890668, 531786662, 874218410, 2006366411} ,{272261086, 10810502, 1405339653, 1663090291, 1536265991, 559764216, 779925448, 101626196, 1071200093} ,{1548002523, 2068820898, 926794170, 69199758, 778258472, 1849142562, 494342638, 1828915117, 1963499281} ,{943944450, 1442100177, 1408001706, 1670660799, 1925505101, 37977796, 965212748, 44346149, 728867943} ,{1833705841, 141856507, 1415403399, 1212071752, 1867933203, 1754917292, 2089672535, 2091299100, 1135978518} ,{1263987074, 170853200, 1902360234, 939073390, 465439292, 1231696277, 1149143990, 1610614817, 1727085206} ,{1349961093, 117282506, 716266506, 1662890979, 2109454225, 1040660494, 1855273886, 1657790213, 777608374} ,{15692750, 914064985, 40220112, 99885715, 593440015, 1982314346, 2138138832, 640027312, 1657920644} ,{2117397139, 1250953331, 686411107, 722256296, 1481257655, 402490150, 1619281378, 2088243096, 320875842} ,{1701834681, 221640701, 712242194, 1276667654, 998202161, 1880856392, 1342432645, 1343687140, 527911662} ,{41024946, 680714362, 2097045829, 1166150095, 685898560, 1310600725, 27722840, 1156447101, 388516257} ,{1353838906, 256739459, 1441465660, 1378538060, 1830574470, 427140936, 862469603, 1398796246, 1985240878} ,{455496733, 778366753, 107886443, 1086551098, 869025462, 2014807267, 154323176, 603041257, 335838475} ,{995779296, 1145738914, 1212548768, 1581579267, 2126078561, 367719431, 117479326, 37222950, 1039308705} ,{1345113064, 1543772084, 1420996703, 598883302, 1700800231, 1502479241, 1002503151, 1429123205, 1737478021} ,{339392649, 1766615626, 928972046, 1862233809, 757469527, 466712193, 14003502, 2072080948, 891234654} ,{2048161926, 752903797, 198118197, 138006366, 1998702660, 680292217, 1785377021, 1767982238, 1165160512} ,{221898995, 1969379135, 1712731882, 727264407, 604208883, 1354843575, 701665719, 374307223, 795323902} ,{1866254603, 403834476, 477538122, 486215991, 1248544165, 72767912, 1740559541, 2131877981, 609761054} ,{211286536, 2089234005, 1090547832, 629559641, 1027457124, 1585786988, 1394515845, 678350007, 1541653898} ,{1369032307, 1889264390, 755153950, 1599554749, 2090824723, 14238712, 2038660589, 1852391184, 1871984538} ,{1893020416, 968868136, 1705921458, 1096561632, 896971375, 2024297908, 1856410649, 1413185546, 1046601528} ,{1828409107, 842565863, 1414626544, 102984408, 282760759, 954241952, 221648402, 240155832, 1487196406} ,{1875937515, 159673286, 1090617554, 404009963, 280719950, 1113707891, 1881935798, 1678067716, 1411383167} ,{1428513094, 1165653877, 1024233153, 50531304, 1892690499, 21429200, 1016732933, 1090539148, 222511609} ,{328608006, 615802672, 994110260, 1977223467, 1102791125, 1145034964, 1022844752, 760234345, 1373580016} ,{1697025031, 907102920, 1860392660, 2038778295, 736675553, 1494260165, 1874272514, 738537821, 585561498} ,{256823182, 1071325612, 1154862252, 1785816641, 1148349916, 1196185863, 1058319747, 19540359, 1034136608} ,{166066923, 1330794871, 469892707, 1167297925, 440177505, 1594793995, 1522327544, 1063980014, 1571023053} ,{2048005445, 120104848, 156657157, 1908445946, 1986298278, 2087657501, 1680834861, 1427527927, 90799133} ,{2046833158, 251671288, 507811883, 937916346, 1369093037, 1468014965, 530877557, 429986654, 574352614} ,{1319348657, 1870795646, 1137486658, 587553504, 275966951, 1086901069, 1723438925, 521220792, 1382029862} ,{1569545426, 1734275187, 138525607, 1137563859, 586585611, 1829179382, 1860544835, 2096985902, 67388910} ,{1629127555, 627987180, 1972450653, 1322654405, 171544828, 1668405271, 422141167, 1926524273, 893125703} ,{457137643, 1692642621, 1602085667, 408254420, 899354357, 1854766993, 201297943, 659922687, 1420000693} ,{166889960, 1215824772, 820848592, 1287191136, 1390483878, 1157413286, 1059999036, 486694744, 1649637447} ,{1454276444, 84012725, 1363748340, 439618185, 40935553, 4512699, 1254794713, 1396304406, 196474105} ,{453585048, 887879589, 2070186427, 842738028, 2103148370, 1588118281, 1621719211, 1456252310, 519718238} ,{980330825, 1147448985, 216597502, 535811394, 573353957, 1341204108, 606169523, 645774608, 1206326303} ,{1862778847, 1677915241, 505799207, 1697118042, 1640898196, 1777561921, 2138510364, 1378136144, 1588047275} ,{966382062, 371634878, 601138122, 120979948, 2073397983, 248149521, 621659972, 1191889146, 152998229} ,{879465693, 1892922280, 1534689256, 53270682, 462938373, 833490011, 276600111, 1137576649, 1031154187} ,{822720115, 1665783426, 1399979741, 1621236283, 535910657, 840960474, 461398481, 1567120997, 1812779408} ,{1407085920, 817122760, 1903203561, 1280041918, 460431220, 324642688, 501088432, 245667508, 1233441051} ,{1021412468, 591689948, 1399765765, 1185604133, 1376125921, 1093189043, 1339272147, 1468478158, 647410201} ,{2077666017, 1019122209, 48528326, 1659407681, 1872532762, 2117300203, 1777079449, 2003797515, 634332720} ,{176928420, 1524874174, 1261972413, 1905304288, 1022970798, 1462949227, 1501559460, 1379758477, 217932784} ,{2144043126, 905849904, 1769953057, 1164530457, 2009019497, 799679904, 1919561820, 492683820, 1836580160} ,{1914967213, 1333759079, 1647977708, 1631439162, 1286323842, 2059384925, 1464063896, 689520069, 2011359459} ,{939108538, 147309699, 39945260, 108571273, 1917978409, 1751356993, 822834683, 142701681, 1975407003} ,{104049758, 1273874899, 228435507, 340873588, 933298442, 1022709119, 1910596780, 1819648362, 511169880} ,{1053544681, 1758026420, 1079175538, 778007214, 2047496311, 1496659964, 984841280, 433312766, 392598290} ,{62607493, 2139471811, 116654020, 243923686, 108388892, 1365939912, 927065239, 1959560317, 1247903881} ,{1985354821, 1225099286, 909071388, 1111633495, 751716544, 637599281, 793285822, 1048731822, 599408771} ,{1820374917, 1451011354, 589710026, 373076879, 1562250613, 491757586, 1061839965, 538303803, 1695966829} ,{657562169, 1313634731, 309894095, 1988969894, 6146398, 2049855613, 988309001, 1750435926, 37738918} ,{258158906, 561494664, 1300685037, 1768149191, 1736924180, 1115384141, 1017674770, 338221198, 1403798396} ,{678289878, 481867072, 570225486, 1717976783, 6196551, 29717250, 1633566272, 287235129, 872031897} ,{1815994883, 202012536, 1907654835, 302965204, 489818965, 2036020521, 1302421835, 1457730297, 1804974461} ,{1276619775, 2124812907, 1631780063, 1411248032, 1850068663, 1141125074, 588141193, 453779338, 1476497530} ,{1210256512, 642345096, 1228226870, 917624692, 1156188033, 1507245505, 1237561855, 2095090778, 208157896} ,{1300275027, 470417926, 1028321440, 477598224, 714822590, 766940943, 1082014657, 1924254194, 1544572175} ,{1770774274, 1152570880, 248933009, 1088003609, 1638136842, 2036038437, 1361680656, 181918650, 500723491} ,{400266524, 1835766434, 1679488049, 289205855, 104772934, 2125169880, 935678591, 1142834354, 1208019463} ,{786315665, 1806420325, 928372064, 2060320050, 1445046500, 2068080754, 1707782353, 710976950, 122462744} ,{696936287, 733295196, 991656552, 1293886922, 907757460, 1240986772, 1845801370, 1721598266, 949443161} ,{1939197430, 27106071, 116953023, 104332173, 1878773052, 422128794, 1472446498, 1521711181, 1453548321} ,{1480896784, 578266830, 1832465514, 1351427601, 1063096624, 489449685, 50545389, 152620709, 1976047017} ,{637654235, 1915246905, 1567480458, 1934386520, 824679151, 1155197011, 196793863, 40147568, 1669620961} ,{1917376833, 126329144, 374249217, 1312083260, 784543139, 851111623, 398482696, 743117587, 1276071260} ,{1465807910, 747783709, 1294202988, 154731936, 1145730119, 1308492764, 1265727250, 785469222, 1085564256} ,{1420930961, 371692148, 892808574, 349525768, 1773394468, 2129224141, 254954307, 1011911037, 1097103058} ,{216188009, 1086480272, 1758022831, 306894384, 1643090979, 1977182110, 2139750916, 1275968481, 1303876193} ,{745338691, 1473530219, 1444931315, 374922516, 950655526, 246294036, 929231673, 1445555801, 552085178} ,{553241649, 971522886, 936634687, 735873458, 529654142, 1744255950, 1399886928, 1104906492, 1431427058} ,{232750703, 785695828, 1513146475, 525212045, 342501429, 1026355629, 2120289646, 427355763, 515049459} ,{616795467, 1880743007, 1382888497, 1649372664, 1530166237, 269000223, 1610860966, 1729816420, 924730490} ,{256900901, 1223521154, 220994492, 2079035778, 1543759636, 1240980021, 2041509314, 720262424, 1721164517} ,{189298541, 781379966, 1758904165, 571206238, 2117094881, 822499659, 1363655102, 308675317, 1686935973} ,{1695002579, 992932681, 1507391678, 311920161, 1323658181, 292307596, 975587290, 1044206433, 206566653} ,{1184378926, 693043162, 2026947558, 1005291478, 1125616763, 922755632, 844989628, 1568538567, 1328247541} ,{1301840932, 1320359322, 1320734712, 561306263, 1387352864, 5721537, 996609627, 964112170, 2123655428} ,{1594327070, 908866805, 158672950, 1726867196, 1998576114, 1424594154, 1163303549, 138594948, 661577694} ,{1309810234, 1939905820, 563090219, 1062726250, 1804311021, 837227144, 699557448, 1676874602, 1432153275} ,{1849006473, 1647753069, 1570474047, 1980062720, 879395550, 248981527, 87466142, 16714288, 1732539008} ,{1835889368, 1062451910, 730358081, 2086389517, 1326886100, 1278884090, 1356490473, 859998575, 489015687} ,{1966340073, 703036270, 334905369, 1273778385, 1543245316, 1410681615, 1451779539, 658281364, 1742343568} ,{1876012182, 400832747, 1991322179, 965684671, 1706706417, 2106460495, 717190094, 70423044, 2111555081} ,{1614736880, 942625395, 382915403, 1810347324, 1855709780, 377467487, 1002421059, 1352125771, 416098119} ,{75595196, 630726092, 1399993586, 1976793080, 327521453, 174771044, 1768320450, 284139099, 120698586} ,{1330439058, 353524112, 44891079, 1836690278, 126625535, 1820605724, 1984659452, 283066011, 1208668437} ,{1361723932, 1378969322, 1054237058, 1169185815, 101736841, 43372487, 1790675964, 971945957, 1586916856} ,{1757542592, 1682923440, 309370064, 1620553368, 1045385908, 208790074, 1739532091, 45349495, 492642673} ,{1734754686, 867989865, 1635404065, 1589545818, 1814478574, 905070819, 1211255096, 736109805, 2093711054} ,{143299662, 1790454966, 1513728122, 1541843050, 1770203935, 1955048449, 1795828806, 1557548062, 1439810478} ,{165232477, 2127677345, 1768771369, 2000361179, 1185536690, 1747723431, 190700527, 537751624, 1426820351} ,{1979567700, 2089075067, 2140009702, 212440451, 469408332, 1396226372, 792337013, 1330986867, 755182205} ,{2104116209, 393809822, 476670078, 132420882, 426046696, 623736790, 1332104025, 203055385, 891848223} ,{555103591, 1294126210, 1216991812, 1114694245, 1295042758, 133472312, 311283044, 1435241909, 1759075731} ,{439125168, 1359154129, 576971756, 755079807, 1601662257, 2064453779, 1894522398, 2024732250, 1966747793} ,{855510106, 1455253641, 728595301, 549991771, 1396043187, 1800070198, 2031306014, 1342273219, 818873378} ,{1859442239, 1673880699, 568807490, 986201072, 1078843585, 915701733, 1644176254, 1913977936, 2126938225} ,{1064891660, 1361192847, 817178800, 2135744164, 2145733028, 988480766, 1115455626, 242096142, 128264096} ,{106883270, 673468143, 528116108, 1451087769, 743011848, 1709800822, 144434185, 1937406716, 1782906415} ,{2145318509, 1491682535, 298272092, 1618831722, 722747330, 1707188698, 1633491330, 2120851580, 2127932924} ,{1374195960, 1562017539, 1445590134, 1074363836, 1704007235, 1081048953, 315603724, 1393399262, 1431065858} ,{403528979, 1098957500, 17742788, 761309221, 420623310, 850348175, 1330733662, 510300804, 671514758} ,{1702409222, 137507493, 2032688955, 100485412, 1325508694, 1903896277, 1044202711, 916130469, 1971230671} ,{216940987, 1305251850, 937626368, 1675424382, 834697759, 1980941781, 351670927, 156475187, 1531550720} ,{122689033, 432005165, 1590026522, 300515980, 467829269, 1784928374, 321056497, 2013507306, 407346806} ,{1377003669, 1406788686, 559015045, 1331447348, 1099051468, 1891205043, 219215827, 2006341085, 933703000} ,{768040764, 2022685739, 1790799745, 1821639325, 118092335, 35884554, 1133995057, 1102618508, 591169316} ,{93739404, 1152657056, 1635761003, 502799350, 2129159844, 1549344994, 449245161, 452937726, 467393087} ,{387320065, 700647489, 534510455, 1870904535, 1345759746, 1101956378, 2081561005, 1679229095, 221158651} ,{301606822, 334271278, 794480381, 493045350, 789587880, 443457255, 102399430, 1530947373, 1649620670} ,{402124027, 1678645082, 2147322363, 277277928, 2040466554, 142037302, 164874025, 756442931, 809343887} ,{1076424509, 259126313, 658987844, 843769049, 1786876319, 848745376, 2079913880, 115799191, 779978961} ,{1553979347, 1976050397, 154181223, 458355687, 280603977, 36293255, 1752091319, 1885233190, 694514184} ,{1708493109, 722745426, 1342252957, 692787191, 1271090363, 1091140479, 852979607, 1145863605, 1406909013} ,{1536272169, 1088816042, 181007615, 784162184, 583789790, 1517488424, 79383396, 326881266, 1113626441} ,{1596019616, 1238944601, 645425291, 407935606, 415769623, 250841006, 1201876033, 1383143365, 468540357} ,{332041095, 1106519810, 1835612312, 1613550299, 1699162283, 453434515, 1140039320, 1214536537, 951951948} ,{1663956313, 1752131066, 1069677304, 2049118868, 473399760, 418127080, 923332826, 222713826, 1039695015} ,{284070660, 1182755765, 1802462338, 1821128803, 2106406221, 1083331034, 779051382, 1763209184, 1918779736} ,{770437803, 1464517849, 1187088010, 453302910, 1147799773, 1516987165, 1193013414, 1538986768, 1468147863} ,{851640141, 571395732, 726520172, 2010787717, 2001315731, 380695865, 1130147531, 648868440, 1774211777} ,{251862914, 1795373475, 1108090510, 1899793922, 133920035, 1062976361, 843422966, 101357331, 1279560975} ,{338872660, 1680889385, 888922396, 1148830503, 1175873298, 1562283954, 1845361565, 1039824867, 1563553871} ,{757373824, 1192954155, 1511139082, 312473714, 923514060, 1357370046, 1680304660, 1871850730, 1342108128} ,{491147954, 1124606586, 203645330, 2012634112, 1335685733, 308459708, 954677891, 712129209, 2052808151} ,{2097126388, 543175164, 1806049620, 1394377251, 1924606110, 668174387, 176324214, 688418682, 1898407936} ,{731556925, 1777499416, 1971218854, 432667385, 1743990349, 2058325404, 1680301692, 1133051981, 1454796338} ,{510998238, 1506451534, 1471876853, 1274060635, 609270729, 1112853786, 1726623122, 884490907, 1051318188} ,{758242898, 1680957232, 1617953001, 1244363555, 611457824, 1200823853, 446045376, 658750944, 810391723} ,{1351434703, 1599723675, 1181382983, 1702810649, 1334196431, 1448844076, 244790827, 221269489, 75636104} ,{1590290128, 1266378229, 96487915, 1405717653, 2135027022, 1568770143, 2017024559, 592384875, 1390821404} ,{233593815, 1224371145, 1698914018, 1836089362, 1640312575, 605742398, 1272155814, 746589733, 627386892} ,{495315470, 43744453, 1057313119, 1366214093, 1024019037, 944125862, 941617432, 177654533, 210220674} ,{609090859, 710632614, 197114081, 1629480982, 799315607, 390076631, 1241703266, 1839129933, 1551484501} ,{1622569665, 958854843, 946811184, 1942694351, 1852918747, 927826372, 1910562142, 1488745966, 1976102753} ,{1487733798, 935442081, 1821552070, 745161312, 1887755466, 199986887, 355692289, 1587036652, 473299179} ,{1020642836, 1686992151, 591932964, 251945569, 1679924084, 900270429, 1243205955, 1427436220, 1180321407} ,{1487199256, 201258517, 1718648221, 1900618663, 329805695, 1398725695, 692223316, 1228843773, 1295574745} ,{274864149, 1807517757, 1122168880, 403968046, 686387110, 530199299, 829224943, 435861863, 855958005} ,{923878429, 1399729361, 1200494271, 1573038502, 2019897952, 2042739552, 1774025616, 522632807, 441112099} ,{600553436, 1924218002, 393842986, 704712586, 1908942896, 1284527570, 1018000160, 348412530, 713256687} ,{447337056, 210195996, 1065144760, 2082025386, 1405207892, 1771614944, 874576259, 1668549513, 69596286} ,{2111671870, 712033947, 365449845, 1827104293, 565368111, 1046690616, 1717095047, 1977740303, 195281552} ,{548617277, 225748296, 485507770, 1446059785, 370283907, 1859093349, 801192739, 1058268144, 429386786} ,{622405982, 53924596, 1431529503, 1006642557, 2106160934, 50830867, 17874329, 1966754183, 1739919517} ,{864877770, 1112549513, 172520829, 670727837, 153335563, 1883584117, 1338627488, 1061201675, 1392527712} ,{1527898985, 68410947, 192325308, 662764602, 975915045, 70517214, 1670882836, 1795942527, 1098954437} ,{1732988893, 613793825, 74809453, 1704851143, 101267460, 1403112083, 2053326418, 771438532, 1119754162} ,{34962299, 1289233833, 315059498, 1746830584, 1368441953, 1169622846, 549936596, 1124856046, 1618987431} ,{1179028329, 1432636593, 721726717, 1272378365, 1727895735, 1986344319, 2066615670, 1622804704, 1589410818} ,{1392043069, 580830734, 1467673564, 1277876437, 784104027, 1059983301, 1204053591, 2037573686, 715198331} ,{1974445695, 640445217, 430296961, 1517930168, 1598396618, 1560001959, 1924405740, 844388159, 1195486580} ,{675628427, 512994496, 1809470881, 753650488, 529292449, 1028143823, 1771743090, 1207282230, 1180485492} ,{258496357, 392026500, 731946532, 1729316455, 1200057515, 2126913875, 1598139411, 203600020, 1128797508} ,{1494736128, 1699206280, 1623494288, 369053223, 430736496, 2035042094, 266984461, 382982844, 726189230} ,{505282018, 1373061092, 1382019723, 1270854314, 1590570459, 981464021, 1145881498, 655047425, 1898174772} ,{1653513238, 1212251443, 1003380939, 515893316, 1250721280, 418773445, 1393681145, 564975827, 1804599017} ,{1669243476, 93272440, 1438109968, 604569456, 277528467, 1227024141, 1116025701, 321334643, 487183889} ,{2046113607, 1738022177, 394593398, 151104319, 2045346901, 1773501691, 954983812, 1105350173, 906855064} ,{1443231914, 2086427426, 1416081885, 1168959682, 251711124, 937051162, 1302862343, 1035080409, 855800622} ,{2025540788, 787744990, 1861918659, 543361818, 1776916410, 937799173, 782069672, 1558127621, 1338945925} ,{1449115513, 849978619, 1490078638, 1222368236, 1932836404, 1738652560, 881550242, 1128574016, 1108338769} ,{1659697305, 1030692677, 657979399, 975464585, 146284724, 986669532, 1601135251, 1596183253, 983484956} ,{365352603, 1445465349, 1969021034, 1110812350, 207575146, 1639714485, 1931991876, 5118464, 635280413} ,{961171775, 1476726629, 1116797452, 246470163, 1357572234, 603445596, 476094764, 509333725, 1716176342} } /* End of byte 20 */ ,/* Byte 21 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{210997660, 1557582898, 1719011835, 1944147003, 409192291, 1483733717, 877582487, 241128762, 539545589} ,{1398635851, 1233273130, 1516291190, 1096519297, 1637945024, 1924866588, 1701581343, 823088374, 705618999} ,{334475908, 893158909, 381493814, 2143924386, 624145901, 1635085800, 1956697431, 492712644, 881193205} ,{1824557261, 1121769354, 241704164, 1443974509, 604523276, 590548495, 1645624926, 1512250879, 2115344933} ,{2104203008, 1988675383, 952426612, 1437121763, 1928972774, 2111197789, 1179875027, 1775784780, 1307448101} ,{702697761, 1550683677, 1641878892, 1424779876, 76914261, 1412113643, 1469761664, 1339860023, 2077569447} ,{858109643, 416471811, 474010628, 1530306080, 1682439836, 438255983, 642743581, 1412720009, 6871827} ,{1342583156, 2141317189, 287858349, 1460527303, 1985477685, 1241916355, 1463800360, 948197177, 1331486126} ,{793884928, 300382619, 1015049017, 1857755295, 1886625305, 881516353, 2102382664, 1551418092, 484635514} ,{904544725, 1645258227, 1157206246, 845060312, 1293352642, 1865538485, 1041138688, 1339655230, 1898215135} ,{1002366471, 1443423510, 1450863893, 525391138, 1583756296, 126227604, 1836180961, 2137585631, 2016717331} ,{1880428105, 1612168162, 994734279, 1185999225, 2058672891, 1490889237, 150800499, 1025844868, 411136999} ,{260740416, 840354793, 1812657414, 1975117321, 2107250941, 319598918, 1486049428, 166509833, 1297353223} ,{1553393401, 2144836428, 1550178122, 882388266, 1422190950, 1708522920, 952195100, 549119981, 1337373139} ,{380959521, 177824475, 2110602948, 547260371, 688857759, 151862825, 446838224, 820363722, 616478821} ,{1628180416, 1204115781, 1774850471, 1919257556, 891496279, 204755906, 199947351, 882908342, 797226724} ,{1757551073, 537597402, 1568984535, 257364020, 961410805, 1318318897, 785450374, 1485694942, 656165940} ,{1447401152, 1725027655, 1780325003, 592877022, 921678356, 359443793, 902900399, 169487700, 862357661} ,{1625244687, 807105989, 209844789, 1024549869, 408149175, 989889625, 400254332, 25467394, 1902474140} ,{1423620752, 1813057048, 1802899441, 482898325, 1116321068, 455796326, 140132174, 1937090617, 116216805} ,{1837831319, 1107964093, 392886813, 196448471, 1920566840, 357348949, 1234182322, 2126716670, 102474182} ,{532800938, 1814228771, 789136266, 252007229, 437760117, 1413000078, 79837874, 1727302526, 477449327} ,{1403437605, 1281356209, 247670274, 374837583, 1652318677, 1327023959, 1823321353, 1132539480, 1255929859} ,{1578482247, 753076267, 1749938687, 1318749904, 1138454152, 952226757, 1094536775, 398791121, 1106251442} ,{445893919, 472356689, 648608555, 2043046116, 1228698044, 208919635, 38615771, 1063162955, 319720098} ,{2141973913, 64398422, 1394024392, 1741603911, 963189299, 710571923, 1467446766, 1872242637, 2133265476} ,{1821408808, 1160946169, 2050114523, 1597038748, 1464790138, 1382446545, 2103129144, 1290440277, 691013494} ,{1256595685, 539855061, 752424525, 819619439, 464742621, 636917483, 851625953, 1917414726, 437892561} ,{1249445131, 1171414028, 1219701891, 1776795727, 525648957, 1915224238, 334625043, 1981799509, 798386770} ,{615203729, 372665610, 74435857, 1993601975, 2102447749, 1123711946, 325381770, 805976532, 961362504} ,{282052697, 2062593966, 411765713, 199370949, 517935070, 1641752975, 1447764907, 444272508, 1221433151} ,{1408532066, 1810372542, 1770892434, 566400572, 158431763, 1961631413, 332702163, 772077362, 102953051} ,{328101964, 412187182, 190161073, 1030108533, 153139458, 726848224, 365260924, 1331763939, 1727131544} ,{1930887892, 806076054, 1247676639, 1896582300, 276912786, 1418755480, 751112221, 1741067205, 204410176} ,{1206074979, 212980922, 1487601372, 1668525234, 143647379, 1830964144, 1276260651, 255466365, 591105063} ,{420093790, 649728261, 1515387150, 506638012, 1342364435, 1576437258, 748885745, 781398913, 317255539} ,{1393906548, 1954995897, 42556172, 808766760, 1603802171, 178638417, 1805393826, 821124015, 779537854} ,{1157598304, 1412243475, 1749154711, 1354676945, 628113512, 1436580798, 1031603067, 1745235148, 876274669} ,{594217230, 1937648894, 751601090, 75877500, 1720305325, 655931040, 522025226, 537413765, 2069081419} ,{189858672, 923273354, 1920399363, 1703136678, 1946930032, 2145313007, 1921159087, 1782350274, 678862342} ,{881848031, 1208062555, 1854685909, 321623030, 1382338284, 2080531750, 52757973, 1699022110, 1861422369} ,{1910134281, 861785629, 1481216727, 194183193, 1694608165, 300612813, 624382966, 619894249, 296529172} ,{721366117, 1564848920, 9209032, 171474064, 173048055, 990248232, 951112339, 1878694210, 589246905} ,{1568588272, 1206635953, 1904004807, 311536730, 1313677793, 1946120246, 1867107815, 1268661730, 1943388011} ,{908437422, 1779937401, 2000587072, 1077223391, 896354426, 1676732668, 125240450, 2107131057, 1984219656} ,{846234951, 1692785478, 1006348240, 347840493, 714003376, 430235604, 1285262508, 756992074, 1952294703} ,{501273529, 1201499268, 1019754623, 197417871, 1937281622, 1992421174, 1825645765, 95312765, 293309419} ,{986684420, 1159875987, 208122617, 1680899520, 338634680, 814536440, 2070518510, 2029959865, 165019639} ,{1722842074, 2099637322, 180001785, 57915308, 87493384, 852397502, 96411024, 453668179, 1968818986} ,{132542035, 215431719, 2044410825, 670166546, 857554540, 1779654742, 1636280030, 1171210359, 340256732} ,{1474287898, 1490400818, 1905123028, 1709888679, 56562069, 759651689, 458128931, 583175912, 1140940688} ,{615948042, 946196426, 1983074291, 1536881655, 919788479, 945362976, 1222700520, 1108936473, 1927525084} ,{405321570, 1525144409, 1888771598, 1601263032, 591013788, 309540036, 1350935856, 698938753, 1742249595} ,{1577309626, 1729900611, 885052451, 463349167, 1626432833, 1822087336, 1965308801, 2030348916, 486313983} ,{1352563301, 679384343, 1334127186, 92521802, 1917643980, 313361370, 150708883, 361896240, 1790980296} ,{1899852190, 1060434910, 1150427336, 451695246, 1817548998, 1469228467, 1387816649, 932327060, 1491234527} ,{495323042, 610197167, 1747174926, 1711249138, 1345078840, 651582467, 123256692, 809115837, 739765750} ,{1357546386, 1162046573, 907671694, 2129309545, 1944036706, 1331150432, 1730259231, 557351173, 997344519} ,{1766268736, 624863522, 1349055709, 268096242, 597782181, 1724656382, 960174340, 2079518870, 943692400} ,{479830461, 1887126981, 334025055, 1243971227, 192112758, 1826560502, 2056960891, 1705150921, 1850167765} ,{17955374, 765731852, 2100283941, 1181167454, 611540981, 115202377, 1737840562, 787671622, 550604278} ,{1330469933, 2008959102, 569049385, 343593369, 522858145, 791675349, 1258330654, 1406416193, 1426476848} ,{1977140793, 1882096587, 168078474, 186377428, 1447846935, 438478033, 1410475533, 1088575362, 1281593923} ,{925416498, 1511402149, 458227598, 791566554, 22979407, 972758010, 1191504118, 159791748, 826045754} ,{2058801684, 50488107, 1481226397, 1726227871, 335194579, 1906887894, 169570540, 1666878182, 2114019227} ,{526383150, 1979518552, 982153409, 1366952802, 921786160, 721542626, 1373858584, 1315599027, 1212945777} ,{1774043700, 399406944, 1500109697, 1756229863, 1104169367, 1925975296, 396521614, 973202204, 1193045325} ,{1791403815, 1595318686, 526240355, 1974432843, 1586788711, 431456439, 267856419, 1773308914, 321885497} ,{778815353, 269522906, 1165328397, 593687504, 1092234629, 1392801903, 999970278, 28949542, 1296850654} ,{19374049, 1925796710, 1675016265, 59614254, 847763241, 737899787, 20886969, 1018671456, 61731502} ,{1671158254, 807455199, 1059615401, 1002463047, 433200328, 1725788363, 777712021, 837638022, 354775385} ,{117370444, 912934941, 1039378723, 1514573891, 120401927, 1301352015, 47034606, 2142356873, 766441392} ,{672447086, 1535687894, 128983578, 723109473, 1838535629, 1944576508, 307375259, 508153950, 586939923} ,{1068722429, 1649112385, 1797233911, 1301933918, 82600534, 93943529, 583237670, 24058910, 1147921739} ,{776063734, 212216088, 335742243, 1693093170, 1928004155, 1443832334, 1333639479, 1118945170, 462631522} ,{298613660, 1369189622, 1990587159, 1161415970, 191327119, 1264674841, 1655325147, 1333671445, 261413025} ,{82367028, 1100457507, 355540998, 1688569278, 54067248, 1930622506, 324068215, 1038274491, 871787182} ,{595111659, 120795042, 603117432, 2083238131, 508149579, 1952615426, 563947127, 489992266, 980897380} ,{1255582371, 266154997, 1356466304, 2037748152, 72325468, 1007817173, 252111017, 1627386749, 469107747} ,{570764003, 1001799993, 173223297, 1313750134, 1664411928, 1124155139, 1819878463, 998915219, 1207417419} ,{295729869, 1741590789, 962448446, 2064042142, 1753498093, 456969329, 1768876822, 1659683619, 1065234644} ,{1378319774, 961753644, 1709957193, 108760917, 1578603667, 1982580276, 586557367, 655085115, 812452965} ,{75869675, 1887690947, 1200068824, 1278245823, 346051186, 1324265649, 1702943488, 1541209061, 2061485844} ,{1550769241, 510524775, 1515614093, 218884463, 532002926, 75129723, 1811441237, 1410241130, 1112160354} ,{210919391, 1469252680, 198807974, 858014697, 1599359608, 429063252, 1337126024, 819517318, 1097780182} ,{238313740, 1634038046, 117216558, 36697102, 1770699708, 1550628237, 1399857775, 1157359616, 936127113} ,{1784960213, 1715289263, 1330130974, 1408805404, 431365435, 338933912, 352510646, 1929671772, 1044470503} ,{1323776252, 902965601, 1077542519, 553630570, 480768161, 1728134101, 580069766, 994851246, 776815319} ,{164907106, 1054903580, 1202416077, 155074847, 243232777, 1641431187, 1379534438, 1604973076, 1399479118} ,{1840507038, 873363526, 1362897687, 1486312807, 890956630, 14437698, 1143096128, 1945222621, 317502895} ,{1410828922, 1291913041, 1298885665, 1970898546, 215152468, 1012871283, 1266503263, 1849744951, 748857741} ,{58537349, 1994926011, 1860029135, 1897586103, 1590500113, 793321933, 830747182, 1821572954, 308704485} ,{744498641, 1673198832, 2073917089, 36334383, 859519648, 1519998490, 468175794, 1375711408, 1237014114} ,{580460862, 1810103150, 516011387, 1832612255, 1409336815, 1821690213, 1274284183, 1708735338, 596531082} ,{3515954, 976629809, 419299284, 977417884, 2021426265, 331154131, 1484511344, 887335643, 2032981337} ,{1848357512, 1132396330, 94527352, 1408903871, 1942274614, 1773961389, 440008507, 2006520170, 166250731} ,{972631984, 1573128395, 225579030, 729472834, 1177847549, 1662203930, 717549478, 263582152, 226600337} ,{2112370750, 1648865392, 1187708692, 451527245, 1690620755, 1983277453, 1889425954, 1293466714, 593662311} ,{853363435, 2128713642, 874745857, 1496616583, 1782311965, 1293165528, 1281131381, 1155594782, 1592602156} ,{439195034, 633584272, 1004889640, 332846781, 157754771, 1983104563, 632925590, 1185064648, 601975603} ,{116661648, 1749077311, 342837153, 2139622971, 519630879, 419755546, 840167806, 342325686, 1044061983} ,{1988330181, 950234246, 301914116, 1919503007, 568301287, 361692865, 712192525, 59622888, 1406426735} ,{278809932, 957385774, 1791302887, 1815409768, 78524626, 808839, 1683645611, 259766163, 309729016} ,{1408096968, 850865283, 1749425351, 191671124, 376218691, 1217024803, 1645589217, 1441075998, 1804577374} ,{1900981529, 561325072, 716222830, 271690689, 1562318510, 862349178, 199652253, 1597676771, 36763999} ,{998491852, 670869177, 1632096619, 1146486805, 1142908165, 1609887217, 746513546, 1529452158, 392851545} ,{431358143, 2082392598, 1763762999, 294259020, 1754887240, 106056148, 438153159, 33531232, 884157850} ,{655826326, 347656814, 711080464, 1812699854, 425459271, 762362694, 1947492074, 486832534, 1758306437} ,{1886844009, 1954801863, 1721027657, 41483945, 1147293787, 113290454, 1037130904, 107984858, 894750722} ,{306707958, 148296918, 38486497, 1574126439, 915308700, 1973684415, 958309687, 1990337325, 755561548} ,{926142560, 630992084, 1008482085, 2069450272, 403511135, 306842188, 1760080778, 1238673635, 1580588098} ,{603065048, 338904226, 1557255459, 848775905, 192830344, 986824918, 997118373, 1895648845, 66224160} ,{314760962, 622418851, 661477319, 1893709542, 1152227534, 727000879, 299652829, 607715046, 1583772362} ,{1305411726, 2134583786, 73776439, 1696105772, 742309579, 174451129, 979086185, 1890296519, 2120815262} ,{831232162, 1087551186, 1093938148, 1552498498, 300855213, 1011368294, 95714151, 552630904, 536931126} ,{938236172, 303862892, 481920272, 1862533360, 1329324232, 1263970420, 39530625, 238084906, 1890239609} ,{683621578, 113936821, 2054287896, 1235223781, 1032734038, 1807404551, 455328662, 1290607114, 2084260101} ,{1876097738, 876615826, 187229324, 1399460119, 1047691751, 5233253, 1581262056, 416521670, 1975650990} ,{1995469003, 98021091, 660297570, 1722237324, 1870222239, 1869213134, 1509311526, 1838223384, 1656340784} ,{967088273, 2040613891, 1381303519, 2019078387, 2023246789, 950513719, 941249535, 1152770138, 1405028876} ,{1664769830, 591075767, 1139926915, 1740709484, 447232264, 1082148150, 1834916886, 1742733668, 696268939} ,{706257206, 773154472, 548159078, 135784622, 337278541, 1170209544, 568082467, 688575035, 1706890014} ,{42702924, 1236324577, 769196410, 1624756658, 19847423, 1650875283, 513512608, 156537057, 1790420553} ,{1426600741, 941477046, 2107948387, 1863633032, 555282405, 549540969, 734493042, 1821308832, 1206314502} ,{1695283043, 916373015, 654303053, 1969781746, 1777319200, 645146497, 106379232, 1073555556, 972078026} ,{14181901, 2022087383, 5209773, 1629777289, 838882843, 2124497900, 1409119155, 599714673, 2024847078} ,{1122177603, 870086878, 281929448, 2094528601, 532842019, 799447264, 431619635, 1406590817, 1865896333} ,{591802549, 1702774888, 1021205910, 1480398903, 692707898, 314527990, 1984862937, 463574328, 1389450342} ,{1222376730, 1249350243, 1889985175, 1823241736, 439164912, 1895260211, 260276460, 1732144975, 1443526906} ,{592299173, 1988284106, 737200206, 646555147, 2125013537, 1029360119, 269549982, 766961018, 1641828816} ,{1027883877, 1786211297, 1870312000, 2112057651, 413274810, 1104329393, 732177436, 1426298819, 1954179688} ,{312460720, 17655692, 994122018, 1508320027, 1172943541, 2055778087, 1026218964, 1578640119, 513045317} ,{410262048, 2064886617, 1475184731, 610987369, 1776850423, 632804712, 394709623, 794961548, 210123693} ,{533619983, 1750627057, 927836449, 777315717, 1543127285, 1885982610, 1341484359, 200422009, 291863576} ,{1351605108, 1737597431, 1692387228, 46112467, 614055860, 2123529102, 1163900407, 390366601, 584801732} ,{2095014343, 496751055, 1723087941, 618610520, 1049612627, 85618582, 1907869802, 165604454, 347405974} ,{1909674430, 1387214188, 1181202352, 1338962644, 1469058623, 1792569961, 661177048, 1966544136, 351271446} ,{2080311393, 1487025744, 440582554, 1070744790, 1526068456, 7297480, 438028436, 487938108, 66799930} ,{1640367415, 2016830100, 1120440373, 459034553, 1752991361, 905830593, 51780389, 1407279058, 1045695326} ,{418778612, 828708250, 1600267404, 294597015, 1601542925, 1970625924, 1340045060, 1973064891, 2092086219} ,{1825292716, 733936359, 1896104310, 208652418, 1248928907, 1813070826, 1905774615, 1152244038, 630895129} ,{1383368756, 78255562, 765896230, 1948641230, 603677737, 1312063415, 1917286770, 203849037, 210208027} ,{754175911, 1719147112, 19466458, 102829279, 932828185, 488817902, 63128557, 1906095384, 695191530} ,{284561158, 632025897, 637228848, 375671231, 197032432, 317681127, 1277659730, 1002232149, 812618821} ,{1523135988, 780395666, 235438018, 1257357815, 2060668737, 1106291529, 814603624, 1401743276, 1140561861} ,{1534299235, 593192429, 1570377687, 104981017, 800033672, 1666800432, 128004077, 1885540690, 1213836254} ,{227695286, 1175373744, 1593077250, 1057255968, 1168992404, 1739925488, 1843096788, 1271165551, 1905200467} ,{1070351438, 2142138932, 1243628788, 338367478, 1520169520, 1552767564, 1129656870, 1016729397, 842191684} ,{2138533734, 1987217277, 528443480, 644031186, 1964199923, 594485376, 2038360516, 1648591923, 1709315297} ,{1878944746, 764933665, 1312693170, 1471159530, 1790833304, 751548098, 834902971, 212575849, 535315751} ,{451556731, 743743310, 1528341250, 625726365, 1207027837, 1766814460, 909956189, 1273609340, 761264568} ,{1637333611, 140199413, 818352951, 1057208213, 2004557817, 748248655, 19595010, 2124091783, 1583248240} ,{1134938492, 130189750, 190635208, 2057632469, 1251704321, 1021451466, 477372289, 709436162, 1218150029} ,{656727302, 543641357, 2013957669, 1748468997, 115715938, 528535166, 325731808, 345972676, 1712612419} ,{1004915674, 1406931403, 203020545, 1107023677, 1644563918, 410965823, 381305054, 329321814, 885056189} ,{2100092036, 818571575, 447546948, 349049466, 2062967719, 1801526280, 1964496397, 1486836827, 146710459} ,{491574884, 1217925302, 1230015548, 1081610411, 968858488, 1475370822, 928384125, 1293546879, 408742898} ,{1172817999, 1559109304, 907083830, 592795155, 540740006, 1041248822, 757395107, 1079326144, 592472585} ,{569663201, 737998692, 2139763696, 1747288378, 729528645, 645990754, 1269421838, 1088242370, 1422385639} ,{790164150, 900693636, 325473658, 855690701, 1024134774, 1697878007, 482835632, 1975188418, 118070463} ,{490203362, 1846822748, 1426528383, 1997450150, 385128480, 1889999936, 335531676, 1450517334, 1380685966} ,{829887857, 1966653468, 1674128126, 744711593, 754947408, 1354070171, 233835925, 1199739931, 1516154438} ,{1531547513, 542162739, 784353031, 1926161286, 1340494860, 1401985480, 293658113, 1071761670, 2039120099} ,{1738478093, 594404261, 1593890729, 951004226, 1707411345, 1381801299, 1002886092, 1888484282, 1261190211} ,{1555684468, 513462646, 1375526423, 1407832914, 1877433224, 827966541, 1193112316, 560746874, 1036926724} ,{869425811, 596604722, 26097264, 465830201, 1065718565, 1473421694, 1013638224, 540553552, 17531363} ,{1252363497, 240906065, 584050870, 1034082861, 129603097, 1627939941, 121646073, 239887459, 2085751951} ,{722097334, 236143771, 568649022, 1795194896, 13554007, 1486648257, 1873683232, 280412306, 559654794} ,{2030687460, 447262003, 2144684984, 733449912, 1019475249, 1884005039, 1377909038, 36433384, 1436220294} ,{1387685949, 1603175816, 318434280, 396085640, 748552276, 2083853375, 1199698334, 1479429776, 1948738772} ,{1682442199, 290984934, 278386337, 1912859630, 1941354040, 1742239629, 634970108, 402129904, 82621656} ,{2068442, 1412375413, 1321600245, 1058312275, 430753217, 1626399823, 1074482854, 37998182, 632202218} ,{386288915, 1008602777, 991218576, 1057305110, 481005634, 336530745, 1410303727, 1315318082, 235392670} ,{1235085781, 26137904, 1585271307, 1299494164, 834995648, 1848715710, 2011962632, 1883797431, 1356782553} ,{30282270, 180255790, 1804188160, 1572562886, 111511747, 1243398819, 1261624391, 164626708, 912885858} ,{2141346344, 1180430663, 1740098668, 136211115, 1016854141, 860456172, 579945549, 899597814, 57749812} ,{1754603513, 1144525465, 1285833696, 2126643303, 1312250992, 1926278606, 1255881278, 1472336491, 1978049226} ,{418370077, 517537276, 2138093699, 252116279, 1553601472, 48985202, 188213596, 1904228438, 672373154} ,{1285053250, 754699600, 1322313818, 393122329, 962132486, 1378672786, 1484023437, 1699075226, 1011701251} ,{497119785, 1359637256, 795694129, 359376777, 211876702, 1722516243, 514935991, 1360265186, 380028981} ,{780878375, 474899254, 1313766992, 530799002, 876522960, 1812204558, 464747032, 1656627729, 1263070896} ,{167474124, 1243191137, 319789473, 1262301708, 992240162, 1614071923, 76986847, 1986975588, 1155792165} ,{986104285, 851822290, 1123881359, 1440779004, 1546165334, 1043556702, 973384878, 885345932, 377300799} ,{1252342863, 1007360516, 790194401, 1782228300, 620927658, 891163061, 585661512, 718253312, 1658029187} ,{531323688, 434129869, 1486302957, 1326032864, 941152736, 1107160037, 776674397, 732074278, 470178374} ,{1316163257, 424072873, 1795062523, 1829427607, 1284252252, 1788776833, 417371355, 1146476721, 990359582} ,{807218705, 943508188, 1401342637, 1772685739, 1001152660, 1197852553, 230474967, 296155130, 1971240188} ,{1982861102, 1872226202, 40037765, 1924611085, 953008995, 1392973181, 1807354981, 1399411024, 892711866} ,{1424073665, 458071724, 1563953364, 177419560, 549503852, 496516853, 638553189, 920293585, 764272009} ,{933117098, 1958868094, 1512511174, 1185048037, 832599301, 1417528745, 1553247025, 1178282490, 1493942253} ,{2013028260, 482507281, 975283648, 1306089580, 321934210, 1925431037, 2126743885, 1098600132, 1225914352} ,{1624078303, 1231311499, 336835077, 1372461148, 1115372058, 985006640, 2139566400, 623612908, 1756100999} ,{375458744, 1460391468, 452606986, 1391314657, 462980024, 1758477637, 1662500913, 57820061, 550117185} ,{974899209, 645704940, 1323834023, 826738602, 1823039817, 1270857858, 664285408, 1453232168, 25918370} ,{44936232, 2019645694, 2109357712, 2075799552, 1617185129, 1144924554, 1064389024, 2132760123, 1192841633} ,{1482136533, 2104974295, 1641826359, 1237715577, 2116373231, 834607232, 490720369, 2032331089, 223941344} ,{865496928, 1379185122, 679089244, 610124547, 783365612, 1935254656, 1719737032, 1296706774, 1832756794} ,{878457627, 1399099243, 1457548233, 1016398808, 1377483558, 1843258350, 1886149634, 1859484989, 1693031453} ,{1424410828, 1492976000, 1751990749, 2036494489, 1362738702, 1853425669, 2069659406, 1956358791, 1859129131} ,{1655929366, 2013211950, 1653519642, 80563324, 67931716, 2077881766, 1570506118, 1463621465, 92535115} ,{679826767, 1598814398, 912289011, 1124453312, 1485967496, 2112494306, 817161098, 623698563, 1589795546} ,{95683842, 986363499, 1408896983, 76627890, 1132427136, 1167622519, 892854919, 1804238133, 1896808160} ,{1500766562, 1413334479, 1386018830, 1188504376, 2107350080, 1102814547, 1751653015, 38554909, 912739856} ,{634650384, 1459447000, 363259374, 550759044, 1573490051, 1666816095, 1390486476, 1782075695, 412892646} ,{1349362761, 1056547762, 849031039, 1010315949, 1574244779, 691644978, 392206932, 14050064, 1998823986} ,{1938246536, 1039405288, 1110174106, 216881308, 2064497847, 2079005918, 167773670, 1231902347, 237467655} ,{1975929887, 391901366, 269641404, 694303564, 1578182836, 451945182, 326208955, 43259518, 1348293640} ,{1146890982, 1067668693, 860587194, 909887140, 1916941916, 881005214, 2055473922, 405219632, 916121586} ,{1728493464, 675501693, 715103321, 51144113, 1305907736, 500025710, 1613836146, 130546556, 830719712} ,{1902584066, 1444402561, 1248531986, 1821518320, 1528074668, 1422743232, 2039062043, 1429969152, 168592006} ,{1612348857, 123157282, 1865393370, 505691183, 687379734, 1384196282, 1595673036, 1665192538, 2037297628} ,{1621753310, 941494368, 34243631, 2038693443, 38985979, 1447622533, 923440115, 1178618491, 807585513} ,{1500273564, 1629184799, 1614001086, 2044495337, 59076075, 1533585781, 1184620245, 19265516, 1770320062} ,{785705290, 1163224718, 1155791328, 479784791, 1828166623, 1961969690, 1542843760, 2087273122, 1312369395} ,{1840477248, 1472784549, 951851026, 1455194333, 723178149, 1687610971, 1542303192, 56394757, 381317437} ,{438762515, 683893563, 152113363, 1348151863, 86089222, 2140939418, 1335519849, 1847087557, 1264969677} ,{1157663106, 1774838122, 1412425749, 145924544, 1471929561, 2013901129, 1673927995, 1170662481, 1169571875} ,{799713976, 255515666, 165430761, 651555562, 1416534893, 1693602692, 861160007, 1101768836, 601419071} ,{1773003692, 178361050, 1780249129, 1980663964, 969094418, 846299946, 2128289242, 1450511094, 1964654088} ,{2031480822, 1626856687, 981386796, 895437040, 678441515, 1130586764, 1015178158, 325376320, 564559803} ,{2099988055, 531242415, 1076848431, 1717373957, 1241508917, 1406859331, 1765563671, 133059031, 228570615} ,{1766548823, 2135509351, 223852433, 816812207, 1191917316, 1839339810, 653825323, 910095891, 2054499760} ,{1561145827, 1866481927, 536796646, 1055885545, 1576908026, 2071700164, 241198318, 756717769, 130940801} ,{1915527009, 157974833, 455903331, 396695582, 1040866708, 453908021, 404409706, 899397279, 618703478} ,{116748050, 1681313444, 1842888348, 366685398, 1573762805, 566709803, 1966662464, 107850265, 2072886124} ,{236858992, 1757542742, 2106078684, 655007348, 1200363599, 815648636, 1391104932, 1456610810, 1764291639} ,{207824018, 185053237, 766903978, 335889893, 1895817367, 2045050113, 1447000809, 795807251, 650917188} ,{1219914349, 2105948856, 736784257, 494849438, 1616691577, 497862828, 1421525726, 641560528, 103655204} ,{1142084744, 54620956, 601089684, 103397527, 2145023230, 84388025, 1108424787, 467068128, 693059197} ,{1188032267, 886832692, 1465072885, 1661600884, 1411451547, 1222640675, 500277737, 1164381129, 1970285793} ,{1140172947, 1090754644, 1666430898, 288232629, 1513200846, 1443313389, 2127638853, 939423401, 1291776838} ,{1604373154, 1106112956, 761791418, 1501956747, 1706466831, 2047820905, 581925013, 546663716, 1085637801} ,{1853782078, 1192851194, 2063954885, 520865268, 2135142182, 329279102, 15971917, 193152550, 993796966} ,{571527497, 735828936, 339819937, 1902193038, 713925628, 213671765, 801905731, 655158391, 1665736197} ,{107984311, 1436288300, 236409412, 938601339, 2130000344, 432126150, 1158907083, 855106407, 434848731} ,{849594010, 70026723, 1465812277, 41451631, 1229255616, 846410643, 1491691099, 1559469211, 1864557398} ,{476601236, 435150740, 527392280, 768776246, 197274718, 1986327225, 369048663, 961731761, 2115526431} ,{459109589, 1695901038, 157999622, 536609232, 551936406, 354096676, 1752052703, 245513333, 1861871110} ,{539337691, 618999768, 1892050151, 918459819, 1598937272, 1611347582, 1542826862, 593218782, 1606059063} ,{1182075734, 1304994973, 264493927, 855695580, 196689728, 247023372, 1466525935, 1953024238, 1445866527} ,{1752619196, 367964629, 282534689, 1048153728, 1983685178, 1472534828, 626140958, 614256028, 480205380} ,{1114619587, 945653598, 1435784, 842563625, 904316852, 1307156019, 1584040642, 502897765, 1265173546} ,{1599483851, 617912038, 1434649377, 1369293689, 447911053, 1239208696, 571429604, 1598790057, 1713695633} ,{185088603, 1978003265, 11282593, 197066674, 445244378, 87861154, 1483769742, 2039979276, 1449238549} ,{534965257, 1317235924, 1012469654, 880529159, 813860933, 807875537, 407590669, 1704240066, 861622004} ,{1803385746, 1606850956, 664186598, 1716771587, 993212991, 1619911039, 2137327199, 861332885, 1228617393} ,{823241476, 1507551028, 1844346897, 2042776322, 196584131, 789919782, 1782623338, 1204109789, 741513346} ,{2037765786, 1932755841, 196958814, 1422573998, 1764113388, 1863782910, 618376619, 1098767999, 1272170406} ,{811175941, 1056848704, 2057388612, 273125696, 957170543, 880311307, 353415284, 578927076, 325049337} ,{1477467625, 446725857, 1042479301, 711868445, 179722401, 466483100, 879210216, 2122131787, 188595552} ,{1276769037, 1306150225, 592356810, 1968222601, 111518872, 537158515, 2014964065, 1799856090, 555311064} ,{1662677927, 2003078838, 1497946111, 908744809, 936099677, 25277193, 737479817, 328504326, 1465368257} ,{944313267, 183443236, 1067084131, 1647776311, 1951040634, 604552869, 1821858268, 1869119376, 867352123} ,{1987657701, 1825000652, 934543979, 1412438502, 1763624317, 1236694639, 394326673, 522804890, 506900782} ,{1280547324, 2145349823, 1122795051, 1315318750, 1726585498, 2126654989, 1344833446, 1488872138, 990731885} } /* End of byte 21 */ ,/* Byte 22 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{830479910, 1794659137, 289793637, 1628702739, 1294382859, 430249345, 676077278, 1177327327, 1633937571} ,{540083923, 527919552, 1119740234, 1719527232, 1989259573, 661012726, 1532630874, 538861332, 941664648} ,{1471566037, 390282643, 1881089938, 1531882762, 474769918, 1700595630, 212139644, 1887130616, 1532261120} ,{951243526, 934367116, 944980615, 118049640, 222315535, 1245676976, 79496944, 519722439, 2080522146} ,{830107389, 713601975, 83741652, 1948337523, 1198914653, 1389432030, 671182265, 102810995, 626059893} ,{549213751, 2048939614, 1849102329, 412342094, 741529438, 890347160, 1537480823, 196346353, 2068672585} ,{1872133274, 1267515442, 1319100510, 811837676, 2051997894, 2115507349, 1545040579, 163699291, 1028335501} ,{688238414, 469254966, 853653465, 555686741, 1935881502, 1731610074, 1750828492, 1231386957, 1182773520} ,{2093115007, 862460879, 732908368, 701585765, 1246735135, 881489903, 746509449, 1406675099, 647024668} ,{355789465, 1883703385, 2138479202, 1073227722, 415876197, 148564337, 1764359137, 133579477, 47000833} ,{1254222562, 971285188, 306363865, 2003146107, 827088961, 747055656, 1762881303, 1708260198, 14449527} ,{1806893704, 1722011289, 1454296078, 278460709, 159580419, 1519095222, 1137361115, 1490997054, 1140970003} ,{394286648, 160240476, 120641989, 838061518, 1997496153, 881233608, 671203685, 1140011140, 1598492254} ,{533807328, 760261689, 1702564310, 1179569014, 1739296838, 1432422583, 1523338953, 2042264751, 58136534} ,{234601043, 937402147, 1990664135, 184045859, 984300354, 1177414329, 397453114, 1548175822, 657187181} ,{926698163, 519333020, 28578128, 1228303993, 144500496, 517111210, 921331028, 1764831592, 815299627} ,{1246052069, 1970776477, 1584607011, 1751792780, 977333426, 353542247, 65002854, 1084796687, 1349273862} ,{2067846377, 1558753257, 400174012, 1177918721, 1373094423, 512884478, 5916080, 1175848093, 209928314} ,{1728576597, 2027104741, 1763426137, 2074717360, 1196214783, 1099980385, 1915303914, 760115896, 500677490} ,{203598501, 1143511129, 22492132, 1435953811, 1599678251, 1179625836, 1327610007, 1722909585, 1533530808} ,{473893174, 818335314, 240208293, 957228643, 1862867769, 8227609, 1203696271, 1717050954, 617654841} ,{1675649207, 67315705, 1990020985, 1283666075, 155606393, 232457759, 1801286587, 1404835046, 308159221} ,{423614230, 182423525, 222519908, 112730058, 14536198, 1107900368, 693748238, 1802653457, 1208968588} ,{433311750, 985226303, 630809646, 1856831059, 2101697261, 1907309014, 1466490044, 1773056273, 729508289} ,{146795205, 1603285531, 494055434, 196836926, 1038842035, 2088778121, 2017840921, 1745450433, 1823798754} ,{1892649493, 1984277645, 1742296585, 2139912558, 1563060490, 570231046, 39477166, 622856997, 1022937994} ,{1272725399, 471178143, 1687882299, 1871022779, 260590717, 2065833718, 750092133, 856871256, 1678672695} ,{97172064, 763608884, 1065046172, 197250769, 2050635722, 1159095391, 1840478791, 734199198, 1578917638} ,{151334233, 1998617687, 843899651, 1114139732, 1457385697, 1194031429, 1392356292, 1915305218, 572280506} ,{609206370, 1669554855, 1255737995, 2081496231, 1701193204, 382590802, 1336579579, 1506319196, 744484756} ,{2008840430, 1341323772, 1800338829, 1906808627, 201649625, 1711971445, 1738019735, 761630808, 794072301} ,{119364410, 1352476599, 696531199, 1047088305, 908002355, 236957232, 1902086356, 2099344998, 2105966888} ,{127564715, 109412361, 1051721303, 2016589246, 591884813, 1675377446, 1109584291, 661867103, 682353874} ,{954468869, 744984898, 54778966, 890308668, 1882341890, 1377288435, 1183340749, 1442507047, 2129083700} ,{1625499109, 647515480, 1589668309, 1848461757, 579753435, 1044958433, 472313581, 1790526186, 597314364} ,{413688924, 710609570, 1081533800, 1124465942, 1590839706, 995315176, 303676759, 910804894, 627812899} ,{2739715, 1174115769, 1725025111, 1713449178, 46729170, 636285957, 1180202479, 1193004128, 488171184} ,{1886905752, 1678999199, 1829477097, 841281027, 1793257772, 159588727, 377756672, 1997556380, 1094113039} ,{658477306, 249866384, 899029767, 1325173001, 997735747, 663644421, 774128402, 1268976425, 1090464910} ,{1395827617, 1083091364, 1611222277, 1314534429, 257680263, 272429151, 549504433, 370588601, 235348435} ,{151298207, 1593011344, 685520671, 1050961967, 1292696313, 144192601, 742427443, 1113234909, 1869431736} ,{1991205298, 87751359, 1614597222, 1372891529, 416534837, 1323564787, 1902968823, 1028974988, 915387050} ,{174775029, 2063181017, 636579845, 678628944, 1913950820, 820893751, 1530383038, 2075730163, 1509567810} ,{1559974870, 1133280966, 460734300, 1571996614, 95838675, 1661031585, 416244157, 1104188612, 997862849} ,{1626315773, 1575052584, 592625487, 735628179, 543341018, 254222787, 1255266788, 1155278262, 353741991} ,{1202275217, 1924221716, 1713508067, 1506701724, 1571148157, 1181302843, 1622174619, 831566425, 1816970820} ,{653308745, 1899336963, 302113335, 1214238862, 438502544, 60066167, 1032709453, 101171277, 80970050} ,{1603157337, 419743205, 1130002512, 1235035434, 1126762454, 1683999237, 218480232, 299573894, 377666497} ,{1795423893, 337065636, 538590492, 2077125000, 17135380, 1104531644, 581501146, 370361046, 531754108} ,{912136132, 1616262699, 1396547787, 1646958236, 561336963, 438021594, 598995135, 900439027, 997802580} ,{502739012, 590557541, 550756926, 1795627020, 189158920, 1703089075, 1565288485, 1784646962, 1926764495} ,{1694903237, 495449421, 940319988, 266835384, 673202244, 1727594388, 1194655411, 1029821996, 2030751463} ,{348483118, 1714285561, 543182328, 1586920940, 399863752, 1377568105, 597904403, 1699776946, 449186915} ,{1592566977, 1735774939, 608732883, 267587144, 111554345, 856253950, 1382785824, 213108835, 1647578988} ,{665440179, 942202384, 741614749, 675692183, 681869792, 601924139, 423567118, 312695327, 1324045704} ,{844191310, 1609227659, 1712198879, 283303099, 843689550, 240018877, 1306069725, 363049607, 1468241464} ,{108420809, 117126116, 1432508087, 800522866, 1879745252, 1045546474, 422321727, 1404329538, 1173481549} ,{927071622, 1577656080, 665938096, 612141990, 610131262, 1467929377, 1977523914, 1423247173, 1507859122} ,{1164144893, 98758678, 573563279, 1812933770, 579949690, 1087172336, 1911039879, 1036695630, 1290848043} ,{1419689520, 1278832722, 138628911, 1018732223, 1129559471, 781941390, 938691248, 1549183745, 1124216072} ,{384031924, 1953628520, 1242047610, 729279779, 2032759976, 101230429, 25500954, 76109351, 1039754062} ,{178897876, 583290460, 1663940582, 1948192131, 1965137330, 91179474, 987782672, 743474737, 1382798625} ,{1415503536, 1413709086, 1265646137, 819967411, 791233566, 488786119, 987196813, 1870312897, 1961985152} ,{1105367358, 1433790011, 354681563, 509628073, 377031513, 832163071, 1244540494, 1577277453, 1220848775} ,{1666027017, 606571375, 61747344, 46326611, 84096564, 1848823019, 1454768752, 825647736, 1513106774} ,{1786876702, 774198831, 1588353698, 148071126, 532193202, 363540350, 387372752, 1300980851, 1257965910} ,{2123304298, 1569421683, 769537194, 658339521, 2110402934, 611516814, 2058744862, 152527184, 339031502} ,{1533994889, 663195024, 579098314, 618659957, 1973960116, 1092664454, 1699904308, 882686908, 376529510} ,{1184888108, 1167878240, 872398056, 1065754656, 355592174, 1612160861, 415515937, 598201531, 1327337644} ,{610120446, 1343851308, 1447107842, 1776569873, 1162956082, 1774554246, 1470258950, 188237417, 774950439} ,{858926677, 1868470831, 995432248, 1762797691, 914742031, 1245723947, 1311048143, 1626053388, 1204616804} ,{1751325573, 726727920, 389258292, 1860118361, 1828348140, 1509628340, 2008372020, 1157188154, 779087192} ,{1415585880, 1969655639, 1503645471, 1989936937, 1918959186, 1474200581, 1095049450, 532927806, 229195901} ,{92258237, 929544120, 2111514120, 1422055004, 185565996, 575131190, 836476380, 1129288271, 971431107} ,{14493679, 1008319722, 274475989, 356879077, 1265070500, 895872752, 1249410714, 1422823880, 767082142} ,{1123184068, 1969274095, 649956655, 768333694, 1879793998, 287669483, 871394883, 785468032, 745886728} ,{457047983, 1429263824, 769437512, 631273986, 1473188251, 1591419577, 2037801760, 2065527017, 428131248} ,{1267615435, 163145016, 1834984144, 124260122, 1236845007, 1330092349, 188335816, 1776329504, 1044626410} ,{851591291, 1224218367, 1092723974, 1299628051, 395935534, 2107277421, 462551059, 1030360998, 1968044467} ,{682822368, 855225039, 355446847, 1296647157, 1297434638, 1475230669, 805483754, 370482749, 2134133317} ,{948448201, 151050427, 2142455967, 137865529, 1349753076, 2073951386, 2060818076, 1134566120, 952654934} ,{1507091096, 69138514, 1312072972, 134118789, 1587353492, 1259643548, 1730191189, 494919514, 769886568} ,{2050992938, 1942722164, 55675332, 539082626, 274934457, 1010126271, 883247143, 552068673, 1477574792} ,{1785521297, 539837085, 1043287252, 810107827, 485111751, 1740265731, 1615310252, 1533239427, 700097228} ,{1403278000, 1322797936, 2110888885, 1396977682, 1107954009, 831650636, 1390314433, 158748354, 408146991} ,{1038993429, 611702005, 952251962, 860615678, 914354468, 1214421946, 1171449375, 2061221132, 1481417260} ,{1861402283, 576671087, 1271644758, 1877481974, 1382549769, 247302835, 648217854, 1006360631, 63955907} ,{1711373545, 13555846, 310515214, 2001084780, 1699571227, 2098929664, 5386561, 2106573666, 1606515538} ,{92010044, 1175282307, 339245895, 1473761777, 929241556, 1572575521, 2043702292, 2046956163, 534929446} ,{1034985785, 1101727169, 858547236, 456714252, 1869253829, 1276353056, 2129810584, 140777277, 1497859235} ,{921082648, 278635969, 1113905648, 70280158, 103774094, 815250501, 935942155, 1353313139, 1670223960} ,{1703198245, 1654789240, 1612628120, 1067887378, 2116410673, 1595540766, 1698433946, 1117680591, 758854825} ,{1561777283, 822737917, 307754131, 861905292, 2095799122, 598238415, 1708983421, 371135277, 1643779228} ,{1150819634, 1360935809, 191926246, 181126879, 1272549820, 313903843, 404182448, 898656481, 1065394234} ,{614711053, 1143460231, 546263694, 1320991662, 1479497290, 176692086, 1128666558, 711210002, 1445322154} ,{1885127262, 2033016511, 640400269, 1998366560, 512060373, 1587970334, 1587849205, 1979490250, 892980609} ,{1002960973, 1090973987, 1426730446, 359951904, 1899563709, 2135802017, 357077193, 1630839257, 1006647422} ,{207627164, 300717896, 1129594457, 1514215034, 855064609, 657545118, 684705301, 1381942361, 478758965} ,{1877837215, 526209521, 1561673863, 1785981862, 1044933148, 1479579231, 77453491, 792505499, 660182041} ,{2006663059, 2115507187, 1094225165, 583146452, 1825561555, 1305800934, 2140433391, 1015110771, 503905144} ,{1421531561, 2064631581, 1620317295, 1420277965, 2033495095, 1674469717, 712130347, 1922651620, 1360567028} ,{1412904878, 530787894, 607248398, 406381994, 1156803080, 1969006469, 884463775, 1707432408, 1418195196} ,{1007407696, 1148326583, 1161196814, 2142717923, 526781056, 366237160, 1033013808, 492672902, 569093905} ,{2006825592, 311438556, 1440727151, 1640426474, 1657355540, 897539787, 1878207502, 1197802213, 486505489} ,{830530912, 1204425871, 1221824687, 83468612, 1692119594, 54746593, 451044103, 247725723, 1927538138} ,{778201839, 1902572069, 1093876763, 1400971458, 1001721613, 1522827243, 883009775, 555047125, 1344326031} ,{103161635, 1688004465, 162374807, 1500536189, 217462495, 1115026981, 2026973193, 39476295, 1814258527} ,{865436769, 1850559756, 883415135, 1125126024, 252289238, 366540881, 862500009, 1789618662, 1988154718} ,{1177514251, 101701883, 284201607, 358641573, 230124138, 870687901, 86835024, 757746044, 394409752} ,{407701908, 689730612, 1913171901, 1926112295, 1683773487, 1406028403, 1743184507, 2067616994, 315862445} ,{323378752, 360887981, 1225572632, 151571104, 1410193449, 1536195606, 1332149264, 114815591, 403640351} ,{171996772, 365209489, 1739215546, 316634754, 2103579737, 1593500807, 1933109540, 1787530062, 1692692580} ,{1631268827, 52146631, 80504886, 1509665161, 300447645, 232821818, 1931752532, 2122041963, 2027339152} ,{955236179, 1755354551, 563438769, 1827894597, 1846135829, 1313647706, 747907166, 62888536, 1394628112} ,{291520790, 582791811, 1659533092, 2122295055, 1308578444, 1554501358, 900709252, 948053358, 762011404} ,{1131153277, 597485562, 1787466697, 54449831, 1553914431, 2026943015, 541165730, 938783649, 44224148} ,{1007373967, 1394549788, 1185339310, 69888589, 1328463343, 1793588176, 128086719, 2065167702, 820403012} ,{935812399, 1563916446, 1820244427, 1382858184, 704943540, 1129007924, 1854588383, 1055874797, 13264814} ,{2124070025, 1868545868, 663950994, 1149141341, 1159361613, 1661992271, 1263369705, 1243832856, 294523384} ,{1847344083, 583684034, 601511842, 1529258120, 1664220857, 259236152, 1799269377, 577391291, 495766264} ,{1117857865, 1456059434, 901611712, 873310541, 2037099180, 1601680093, 1736888050, 1339141547, 172535268} ,{188286737, 431354712, 1777559097, 2140104720, 1711336224, 43331807, 366528594, 1367574618, 1017936743} ,{591636248, 1456948453, 387589203, 1614347911, 416967271, 2108869225, 186355886, 45884798, 186159227} ,{406071277, 1794976176, 899271194, 1047509939, 1178479706, 1330728682, 648264520, 820617357, 569141084} ,{136437532, 1074099337, 1257204668, 1725917362, 982464671, 167352742, 1413379573, 479615235, 277728427} ,{1235649003, 2117584289, 18674078, 896495507, 1258798064, 2007229685, 2000498247, 947940397, 604896378} ,{1720327279, 1421196901, 1069349816, 598038703, 953353738, 711708171, 2001367962, 112308281, 1021420022} ,{334441850, 1422582910, 777315810, 645645681, 684640942, 231786439, 588816374, 1942798503, 326784013} ,{338733442, 1488985052, 1254329816, 811989752, 263634393, 1585176712, 1988008155, 1726799633, 1055118892} ,{146070469, 1944209267, 1817877663, 1291733162, 1169191002, 711968597, 1246566107, 1607054301, 714096179} ,{1493978590, 2140142484, 1856200646, 637179326, 794860502, 543508154, 1021727698, 64826267, 1523790585} ,{4543481, 1736493404, 1246711802, 1368190953, 635413685, 1842277368, 1460908182, 1071621454, 1763584729} ,{219623360, 220663945, 1184234835, 148962359, 620270765, 143795769, 2100273957, 1239227574, 1689779667} ,{1388286736, 459572982, 204387885, 500496776, 602813831, 1691659542, 1974539057, 1634769206, 674889703} ,{2061048776, 1395128411, 1209239070, 523951643, 991078564, 244264610, 50004633, 1902314392, 1613758715} ,{2096500461, 1508763888, 1118869898, 2031893725, 1274128692, 882362909, 1776410521, 1517524225, 1279866125} ,{1531882368, 9788234, 283266980, 581389271, 1243296876, 332622864, 596345707, 1124287550, 1923538057} ,{848700317, 323296748, 2005544336, 254519431, 1363663004, 1851612737, 1035357331, 1073260371, 21654233} ,{114892097, 141467705, 1919133423, 1511742930, 1495359595, 1926616571, 278449982, 1629033801, 1032571947} ,{2027071249, 972643663, 615609688, 1149164757, 437087921, 1229536367, 936891236, 391756095, 1910586023} ,{775059396, 1371226372, 1354472501, 1020103322, 2116029743, 670458854, 475566808, 1712648398, 1301854439} ,{661609872, 697035997, 1008333910, 1186219616, 974973492, 1753962730, 1468188778, 457605179, 707437497} ,{1615443382, 762533388, 1826460516, 512778746, 735188590, 411876569, 501187711, 1518479053, 1599585292} ,{625938588, 1365575644, 1859907694, 2020929969, 545264277, 2005490597, 835863438, 1513629401, 122868169} ,{366521516, 983789778, 755811413, 25548785, 58498973, 426010518, 391959367, 639982283, 156627721} ,{866463066, 555254796, 1836752149, 968575157, 1765089604, 896237817, 1200946366, 1192489023, 1669840993} ,{667359018, 1433907689, 492208916, 599829683, 1614142506, 985583668, 1006248091, 1428758857, 791954359} ,{1946611151, 1168053657, 1611509800, 178538300, 1304275065, 1667855760, 1027760284, 248318930, 143621616} ,{1803066941, 396888298, 124768763, 958612159, 317978988, 2020672698, 1350268601, 593392331, 1291407678} ,{1431970428, 1960118937, 286945424, 46508697, 1193937006, 170439099, 119917557, 1829898652, 1841962666} ,{1534160090, 459482943, 1208941327, 448859428, 1625406261, 996268735, 323376358, 120929338, 1368332628} ,{987184589, 2068378457, 1361446311, 1144394185, 343609366, 541747845, 1708705477, 224224721, 372504896} ,{1021333437, 949815666, 940732137, 1008635773, 380658423, 270226416, 416656162, 1077554481, 110888537} ,{2048439308, 2123294626, 1732841234, 839165398, 1631270631, 117850680, 1691593496, 1965094592, 84494065} ,{689087958, 799963119, 1435019032, 604385516, 731958113, 344033969, 133491137, 235541071, 1830634744} ,{1196425110, 1667090323, 1414617056, 449063799, 429028662, 174599711, 387139516, 2031551886, 362230596} ,{116178628, 1550148079, 1049149684, 1190388150, 744035059, 220995371, 5433663, 1510608915, 1825934674} ,{729239467, 1203142640, 667405974, 1157295213, 991137239, 1699528103, 394693685, 1556023335, 2057141807} ,{338666116, 1889459843, 1616135438, 13518278, 1826633619, 1341429973, 395015671, 1056378799, 1349526857} ,{1367986244, 1211852374, 1693169944, 253203791, 583771287, 1162553918, 1071527708, 737162552, 1614254582} ,{376844919, 1105747048, 1195358271, 323134243, 619648152, 866477144, 1321588000, 2008062090, 990568244} ,{825575175, 341752316, 447597726, 284009899, 381386935, 1663413740, 2012886564, 8996331, 1559354225} ,{1267786832, 930849023, 1685917751, 1870674690, 652023321, 1775613820, 1064452914, 853871076, 2071155362} ,{1966413479, 561792163, 615149423, 1014364710, 242575016, 1913656910, 1019111328, 1516669204, 141237524} ,{1407755186, 1870961708, 1664288276, 1635685429, 1225447518, 2022492487, 1708035182, 1252621480, 787030000} ,{1943664148, 1102949034, 689226279, 922822800, 999920217, 1281660041, 348019447, 1552635270, 1530239696} ,{1202072895, 171965954, 1616285377, 1723293528, 1216360695, 1361853176, 710140036, 1045247786, 1494769064} ,{1601760195, 1698923897, 1812104757, 199895564, 1877820805, 601436917, 1279479289, 718445454, 982119802} ,{1080689250, 159990439, 346456087, 1174234879, 1029692080, 855491025, 583905140, 969868080, 690238252} ,{569646490, 1436551375, 1542491571, 1341486187, 529483043, 163433280, 1485289923, 1143757261, 236542184} ,{1012087154, 366784632, 1299450994, 1233835714, 1289163429, 1515792681, 876297738, 712383141, 1147203512} ,{1430867445, 1320989701, 992386445, 467337283, 1910851804, 1357057007, 1269035769, 989542405, 2101382874} ,{84492138, 1638430421, 1094479344, 1066781652, 196642452, 1394109808, 1465534370, 1627586446, 271232290} ,{1158613679, 438080924, 456753764, 1147557183, 333914710, 2072675601, 1986175133, 1848260257, 1512384720} ,{410649521, 1767867111, 2065987416, 1345643887, 1571011079, 1731513961, 1567331712, 2085567976, 411565558} ,{1625450648, 609198079, 1004434185, 980950739, 2129520162, 899283811, 1870800857, 4047892, 1269109941} ,{978611523, 2040130666, 1005500302, 774327012, 775896910, 2019739180, 298591589, 1636187597, 2028592351} ,{602938933, 1240954846, 1532935432, 1460509605, 711757975, 278306943, 1757650549, 1811699554, 1580901684} ,{1450230319, 2113584776, 994388662, 743920844, 592298277, 1498629982, 684616533, 1900169428, 786232436} ,{529456683, 1520308682, 559847032, 1353083583, 854088030, 1657121390, 1053596369, 1950692495, 1781958392} ,{1036471653, 1761679983, 1439039400, 332557107, 973248084, 647295628, 2071479389, 573906962, 987129012} ,{2111219964, 581071661, 338558280, 1415371499, 1528402998, 271641403, 565606336, 787912552, 393640146} ,{411680946, 70445163, 1992855299, 1527716500, 820879940, 570601926, 289906072, 567255916, 192843640} ,{1688968240, 1266419701, 1882478328, 759186754, 1031195358, 675280817, 1324007495, 1906396866, 1008201549} ,{1535908222, 1882589086, 2021181854, 1458964278, 1012901858, 1559769573, 723643227, 1957308425, 864714821} ,{236739784, 896782404, 903205511, 1331600710, 361973117, 1243420209, 1434376079, 722194900, 109204902} ,{1739721671, 410018027, 147076909, 1248235822, 219490246, 982231448, 662289361, 1123808728, 685150650} ,{85764848, 299562260, 364340767, 986755585, 1384087083, 1128538022, 184100824, 1351817026, 1555196218} ,{164113992, 2019512638, 1391528984, 484936490, 138309881, 1103079282, 1220199600, 57866630, 1191798384} ,{1825461910, 1014851002, 1325432199, 492004062, 466153470, 1449681157, 495921247, 1070050902, 306384019} ,{1937485265, 1231518939, 1687670637, 104298812, 1692795134, 1090270008, 17585946, 742388825, 1969829957} ,{372894604, 377119619, 1592143637, 186653414, 1166221341, 676103237, 1033384957, 1650329779, 481231736} ,{853274347, 2119911166, 1293473317, 1555040403, 1828403164, 425400774, 500337952, 520928661, 1753452315} ,{1724077601, 1856770486, 2098897721, 1843324, 577992776, 1360717508, 334156405, 421759494, 1933615506} ,{7982797, 1229780803, 2124003976, 335663937, 1568901979, 904864277, 1485178932, 1104341499, 1075008272} ,{592424435, 1187401829, 927269505, 1947405388, 430141622, 443182365, 1309026589, 308130076, 2040283013} ,{1045174844, 344260368, 356323779, 1378487667, 879268837, 407945902, 428291078, 1013837425, 1061296650} ,{1329624230, 1097596352, 762976016, 1064529491, 935869885, 1705969695, 1776499358, 228006351, 2037183668} ,{1559721133, 621413895, 1390823575, 423961225, 937415881, 1471375869, 1842209662, 2141419855, 1798531667} ,{958679605, 1022533784, 90386964, 94099148, 2068306837, 1223866834, 1165272125, 223124816, 1560716422} ,{1126945847, 1601215724, 1080865381, 1141636527, 596699167, 1958403954, 1898662550, 1550846458, 1986638189} ,{355839794, 59860888, 317615418, 581206083, 1967756661, 1438555513, 1209985359, 50337025, 339525967} ,{2104547316, 622052528, 886124417, 1499384650, 903512531, 1603447885, 861788569, 16642645, 1434558517} ,{508635576, 920672475, 840119608, 1524002635, 1513994801, 313153294, 86270861, 1601182016, 524084366} ,{1481645114, 482914367, 901899466, 1703276169, 1492301707, 1467577130, 1461989294, 547334822, 1515482884} ,{534973085, 1345667760, 1323465382, 965860477, 1312897079, 1695298092, 2018601238, 44601679, 1189912309} ,{1400173070, 1735939325, 1105259136, 877852332, 4584412, 172136927, 1796630488, 1108025120, 1764259267} ,{1665639677, 2042064294, 223934287, 91664479, 1519985383, 1136967860, 1973479183, 1870552959, 757917665} ,{126715201, 250367910, 47831693, 800350204, 2004534812, 313391772, 1226634761, 478402220, 1837094035} ,{1302977872, 1317907557, 1951517368, 1903679930, 796093997, 2871843, 125151123, 34515937, 461890872} ,{1464537992, 1979444687, 1447341521, 2113186487, 1355682163, 607862931, 268221854, 1375063744, 1303906582} ,{1485840193, 1475890408, 1173702353, 904234729, 1105983041, 204227064, 1531719610, 1441874689, 1567694541} ,{1974965931, 1156870300, 1136643490, 1957566738, 853658558, 1646748230, 1634023433, 634039260, 631744817} ,{1180179895, 1993179254, 1380725500, 33820388, 6708911, 1043245379, 908215435, 1326557721, 1797271538} ,{861765981, 1185478730, 1790843923, 1406998294, 1565844369, 991234819, 1336537554, 294965056, 1033109870} ,{981282970, 1467603974, 537952221, 625097406, 1442251013, 50413111, 1701423638, 1962334415, 218563056} ,{2089015022, 1269298916, 1108004601, 331524876, 1665535774, 752892023, 1166614940, 2070693294, 296548027} ,{1776120468, 1086775605, 175375179, 761119834, 1821851424, 1324126900, 1859414411, 1291440796, 940350416} ,{373886771, 107155443, 702293514, 627324680, 22400152, 1157411079, 189825454, 2064124324, 1876937015} ,{2017966742, 2110893402, 2059568504, 1325318973, 445777158, 1619353407, 96930441, 1398767501, 63582715} ,{698468373, 1047798652, 1580668213, 1263205341, 1832881092, 51715445, 2099438719, 421027607, 289657059} ,{1347576704, 338861215, 551489723, 1809912034, 1951451442, 296706098, 232894224, 10412138, 1733058829} ,{90484426, 911378429, 879139009, 761852382, 1288206979, 1523953974, 1848993671, 1295820603, 1156792315} ,{611530620, 1589477392, 1311482304, 144782464, 632887921, 1375441675, 293206806, 686405176, 2110633027} ,{1962297046, 1877546621, 1290826117, 238259988, 2002598116, 1834987749, 1614948046, 317273266, 388313920} ,{1215471404, 246141506, 1094101248, 246051632, 1294589770, 250223244, 1436019842, 2105676699, 450228743} ,{1842165803, 1859429123, 688847115, 1622599279, 1842304862, 1536793639, 43595159, 1499966791, 350948844} ,{529983345, 1520035700, 2008171575, 1638555726, 78831510, 1871412441, 1460551403, 1449485282, 1933743673} ,{1654436478, 1715019868, 1823466128, 1597511773, 1572237529, 921218736, 1071542841, 1329845961, 1214165625} ,{1732046604, 457339394, 1671127494, 1527116715, 1556033723, 280619812, 713917101, 1384619912, 212423295} ,{1943370323, 1321452720, 402708920, 29080080, 1750031760, 963395654, 1568758994, 983252985, 1942764127} ,{474131943, 53148639, 203211655, 2090539731, 1153452664, 209325006, 774192997, 1497449635, 92474380} ,{367478457, 924867797, 527222464, 1610723125, 376089466, 474127790, 1435019561, 1215840461, 57919487} ,{162788913, 1714548435, 962281087, 267421818, 820671693, 905953039, 537823341, 186557831, 1936492458} ,{1343801416, 1970730414, 1700717717, 1933090570, 1957651046, 1070903189, 144785595, 1345544700, 1486710401} ,{1526316849, 445796063, 475198985, 67281223, 214852626, 877952807, 919534779, 1785553515, 1200521631} ,{2026884378, 2128665958, 1590432330, 1340596187, 2109302952, 1204508061, 1276228691, 2075032151, 416674058} ,{328043878, 293485072, 1709411464, 1853979371, 1664392685, 2111404997, 648297168, 338585174, 901642780} ,{1128678366, 510216607, 1869453990, 1864635551, 1980238505, 759424273, 852036218, 1351674510, 433410603} ,{434119110, 472176415, 1901775936, 1247282641, 1731355489, 1393976550, 340142320, 1128232829, 155357993} ,{516735143, 618396371, 1112354534, 1583323462, 1847013542, 759661618, 631724603, 1684342398, 1042745338} ,{1411170895, 1732775980, 306884712, 724216300, 1271508797, 701995255, 1671799108, 998080071, 2103296778} ,{317758836, 1497425249, 360899174, 193096637, 517600830, 956712927, 777668926, 565157607, 1071414944} ,{681309257, 775246349, 2110814126, 555233250, 674139162, 214665562, 102945897, 197079639, 497787106} ,{960497128, 436742629, 198883878, 1511075894, 1510298322, 959562093, 1258901516, 640963634, 641715956} ,{1006042321, 549151852, 219555950, 726102885, 870890388, 304411222, 327760387, 1363006026, 1038083373} ,{402331789, 1182189242, 1140202379, 1758581032, 290680438, 2007740757, 115370567, 322336963, 499985048} ,{996793298, 1415403507, 214717054, 579238471, 1504214356, 851777488, 775588392, 1596272722, 1160267268} ,{1342569667, 1480339500, 699908198, 1107694609, 493121935, 1762059393, 1479051781, 697400668, 1108025160} ,{1879927132, 251242985, 1864168841, 856314330, 1676369704, 881758677, 1233430757, 102491018, 1425749483} ,{1511087953, 535460625, 836818987, 1771539554, 827163641, 1402444212, 1634453701, 1306854941, 477138594} ,{1838799398, 297249360, 1580537134, 1804756305, 1737920113, 1586404853, 273676159, 1601595467, 1928980976} ,{339897586, 1731634858, 1839951536, 1122613228, 943591825, 109432150, 727908460, 867845267, 1391465258} ,{996573669, 868678533, 222252578, 465445913, 1717762051, 1486245390, 775808515, 1804525668, 1945171526} ,{290341348, 1829182561, 207777404, 1814595121, 116013322, 439072901, 161476071, 791710855, 1449527258} } /* End of byte 22 */ ,/* Byte 23 */ { {0, 0, 0, 0, 1, 0, 0, 0, 1} ,{888704518, 1662553068, 513723693, 942116488, 754406834, 1136758122, 330606715, 289533226, 1297315299} ,{99251579, 1760376624, 1213006780, 807232700, 1644350103, 1618264389, 1612981885, 779559952, 512510661} ,{1179407538, 1335155435, 2145337203, 51213872, 690716235, 707614432, 1038678693, 1083540700, 311476990} ,{1019164940, 2326200, 1799892028, 1316680099, 1510448879, 1793102118, 957557922, 1196283191, 125382121} ,{68056367, 1260493245, 339132442, 1545960707, 2132509156, 1988292793, 1039094335, 645406778, 1691868419} ,{727710984, 719764431, 1610762372, 929537600, 819203442, 894562316, 868168832, 1914168697, 1974605498} ,{681433799, 1807083028, 1889912342, 2069867223, 2103156131, 1461207016, 948993157, 1415597071, 641329515} ,{795084912, 677868040, 1509515536, 616860870, 1435789420, 1688078509, 1885055699, 1997200840, 736769126} ,{507196177, 550578213, 1906346346, 80136086, 1280020212, 50937004, 170531477, 1845811169, 1600353944} ,{1545257346, 227785103, 171063641, 2115159265, 775498923, 331190126, 699392191, 904004357, 136974851} ,{1501639035, 1631166679, 1819408562, 73426789, 1161607070, 568722105, 9743090, 1143758531, 1942407590} ,{1531916142, 990258340, 435599067, 1340798907, 1997907459, 668836055, 1812119183, 1028679740, 1637869550} ,{1955620485, 725695530, 1129185115, 1872130726, 1096168655, 578099272, 67856911, 882013166, 1431709141} ,{658871130, 369295673, 1629307280, 1694660455, 1062242656, 1212432601, 1312330052, 376351478, 715771274} ,{2021438529, 724198535, 556699324, 1737525862, 1521239760, 983062691, 1393743388, 2108709135, 246320651} ,{862518949, 683738620, 1428753937, 558042005, 1674904386, 1348224629, 1923242958, 1817642252, 179736139} ,{1373590688, 1198029740, 647758785, 1710180622, 1134930592, 1576266458, 2028800807, 1751719616, 92331997} ,{2070278674, 576438408, 2107021660, 1820566444, 1998135625, 1355079425, 22131126, 309581997, 1567659379} ,{800584669, 411967974, 318860284, 437301930, 159893627, 1098130981, 41888623, 744763459, 1939335881} ,{1004711627, 1788865944, 1945913686, 1661886119, 1319546103, 41352504, 213453392, 728183973, 313062436} ,{1137760069, 1177255120, 621144254, 1663744059, 1482973137, 1745406737, 1835612996, 591782781, 1383223346} ,{1433046643, 1773183568, 1519403731, 1834997415, 1432432827, 1593739790, 2035336628, 1981623054, 94619664} ,{1380032275, 490239285, 112948071, 2074534975, 391473350, 1617840724, 2123684194, 687000413, 1546887576} ,{1837425764, 1688530854, 452945670, 332746049, 918463587, 1474928566, 580965766, 773653125, 1184555905} ,{1337571166, 298967127, 1095290084, 1892314303, 1649156486, 475062313, 177398998, 1615476289, 2050107426} ,{1758614940, 888983177, 820074147, 2123827483, 1625296055, 273467948, 1583353824, 1999150374, 281348330} ,{872399666, 573851934, 39869569, 1638730917, 1447716844, 912757145, 257275480, 1548684499, 798303087} ,{1101156310, 1219151290, 2019601037, 2144131301, 395661129, 1907488405, 1888851007, 87169078, 150403587} ,{589084380, 239656158, 987437161, 1401922395, 1113709747, 33476625, 1512591254, 228270362, 1248563485} ,{1901677851, 548026240, 1263078896, 785101920, 1209842352, 1063554998, 676086282, 763220086, 1586960416} ,{685527763, 629702361, 49517760, 18670305, 185181791, 711890191, 981472199, 1715264500, 1732322863} ,{61252081, 1782605775, 747086108, 78534797, 2118619971, 1868541729, 1059464144, 137219076, 1641096565} ,{727622793, 364309418, 373434631, 1724287432, 64005849, 1505505811, 1027623603, 1686741718, 1406381530} ,{1217840915, 1959313803, 1784911662, 739034313, 85590296, 996198571, 469874521, 681952413, 1242771322} ,{935037669, 175740507, 843766586, 1182447044, 179281963, 814844964, 988067578, 621875027, 713244980} ,{1186899423, 830749021, 493118664, 948840567, 2104666862, 737456770, 1308750618, 276029486, 504817989} ,{323608743, 1771773550, 1872263378, 2140636126, 742251007, 1980633037, 856919268, 552224194, 2052211092} ,{1042420836, 859927383, 1544676094, 1954804862, 1643291274, 65583361, 1629096100, 1384621227, 1910293121} ,{341817606, 1743525133, 1731121515, 1417257616, 1067339544, 120650418, 1474145504, 1330803419, 494184165} ,{1604092269, 1164415005, 908038262, 1134280904, 1941543002, 1569350352, 1647255827, 1309360641, 1590640960} ,{10114346, 1044206374, 1294574029, 1057267870, 1212214345, 1684911447, 1456474299, 440127683, 1380292203} ,{672745273, 1725476651, 846916208, 652605907, 649003803, 738396529, 1554845064, 537906864, 890233130} ,{1544788813, 157593431, 299889416, 2117510797, 990033014, 1012781717, 1770776915, 960867965, 1190600138} ,{1241329891, 732538749, 1521573556, 342067072, 358987965, 1479189293, 252238027, 1005403039, 2047206366} ,{1937729278, 1697275304, 1455019122, 441564199, 196877847, 804321692, 314513071, 1512600630, 741209450} ,{39813409, 118344595, 2008381535, 1638518941, 182010274, 1070777543, 1955731942, 1168761126, 417188974} ,{443727908, 981304811, 1344343814, 1372501325, 314011796, 1684993502, 969344453, 281611490, 1289189598} ,{1498321159, 1889361365, 1830085196, 1588493995, 1641130000, 1257574777, 1067811477, 892637431, 362339258} ,{537953253, 671514760, 1238073044, 1093423376, 409478492, 1879095492, 1868879810, 1513057266, 524153543} ,{65475981, 60818738, 333872960, 482139416, 584482953, 145276327, 1034645522, 334761848, 1326309765} ,{287485731, 52589998, 857858073, 311110122, 2029249128, 1133085998, 246529609, 1191192400, 1612331788} ,{2038972584, 1588554475, 1518998257, 625430056, 1721060791, 1304410800, 1062454477, 2078932089, 1959802201} ,{1919568766, 435253085, 514289875, 2008018775, 1062621891, 407649111, 1552728692, 605570274, 206427201} ,{885517218, 883254522, 132884505, 932260800, 566099635, 358575131, 1188499215, 2126898810, 895426875} ,{425717066, 1046532688, 1464113345, 930131237, 1526495938, 1541776809, 1890859103, 590453098, 1105088989} ,{1884818974, 3887221, 913111630, 869905800, 476394749, 847439133, 2012372304, 595072223, 590242018} ,{1587850379, 178076963, 1815638392, 2029566478, 1736513992, 1662438795, 696913902, 2000394977, 714253203} ,{778719155, 57533722, 1548078888, 1782676692, 1553449117, 1395594753, 308001448, 386385997, 1096091471} ,{1804045245, 814288996, 1916570413, 822415151, 1271274781, 719744072, 805666529, 1506799507, 1495514230} ,{379543066, 1215645511, 958639764, 419767216, 1194237004, 1727050532, 1642662266, 489080517, 700936276} ,{1838771631, 241764181, 190086080, 2017860485, 151008849, 779310116, 1635314457, 1123377878, 1690561874} ,{699022414, 175910571, 2140959505, 1186143906, 2117447795, 1434491279, 1531513375, 113243313, 1326193359} ,{1241954280, 1040291278, 2081952245, 808230752, 416621597, 605607280, 1869989697, 1979095458, 1656673469} ,{483314018, 1345391885, 1445610917, 339144201, 1440538434, 961483614, 464846558, 2109183188, 1561317777} ,{1777290606, 808336043, 837020496, 1526802907, 2028695092, 830529425, 1083206109, 2114048410, 215036325} ,{46838832, 1877532407, 752313272, 657330189, 1185748914, 1002145357, 79439850, 1703268065, 1126476003} ,{1146108145, 1268458186, 1131752120, 193290655, 1522371847, 1404191247, 1391904460, 1853607741, 980983320} ,{532886526, 1541988453, 1144989174, 669899430, 226983020, 1999723263, 767208579, 294002245, 660953146} ,{867648320, 1173252112, 1336060949, 1810279440, 133074272, 869575200, 1031709629, 268065064, 468748229} ,{1930051930, 685857021, 1920527792, 364196139, 2116568249, 1084080669, 359983552, 1526692084, 2011722749} ,{1672777416, 1339034204, 1209486918, 873072853, 1140473540, 324065379, 988689853, 1922895775, 2078072754} ,{50251230, 810590416, 327403468, 561564090, 2044466530, 1866744309, 640410601, 444794562, 2103536798} ,{987978441, 1127091705, 6888177, 752984718, 1562529700, 888673433, 1091716846, 1015128916, 777737850} ,{1853388008, 632422193, 1544149753, 180835269, 197299700, 842729849, 68078300, 157007864, 571843677} ,{1967903351, 847180231, 1228426920, 722201818, 1077875910, 1339530064, 1189088127, 1057640250, 1781443646} ,{1708902509, 606578314, 1214329340, 1200019704, 307971548, 974830077, 551016266, 1651409105, 266849273} ,{1301392699, 1375280350, 1409206245, 500364225, 997878489, 812418723, 1446982239, 2034029906, 930213116} ,{666952657, 1581000734, 985236509, 353807397, 1398732830, 1025444757, 304208297, 2072695593, 2069153743} ,{422945563, 1143252035, 228728317, 749846767, 380050659, 1317343635, 1378270780, 267451994, 1789681014} ,{1708915195, 1069935285, 1375736390, 1626509552, 2046412075, 2082427702, 861635059, 1217563099, 1512890808} ,{1648843464, 337040439, 2028585282, 298126986, 799728736, 189441956, 1532783868, 620646091, 1763618610} ,{595144079, 1848891437, 222546242, 1808121777, 1245869441, 1533216827, 437539629, 473866901, 1187390208} ,{1517760567, 1455142420, 702340332, 1423755239, 1181915015, 1675545498, 2048634690, 1780474921, 666122975} ,{1058349463, 146727653, 1413188677, 1077956939, 1020933484, 98427888, 90248728, 1905363343, 676178861} ,{880993135, 907681384, 374681005, 496723222, 26896808, 62462875, 1963431734, 1312760668, 426424033} ,{2092905987, 1573506072, 991051186, 180819699, 268183277, 1592608253, 1829502668, 399487224, 557859478} ,{76393674, 1141282790, 1068115541, 285095624, 2038502350, 470299910, 1977252396, 1311044203, 1901759426} ,{732398430, 2041488792, 131307101, 385159733, 1504905663, 711407518, 1815049752, 1038382508, 2000465244} ,{1118698071, 354493713, 1551111275, 1375467369, 193548894, 1144065394, 506443293, 446592940, 37241365} ,{128000008, 905871116, 580544675, 714500527, 1072866910, 1720064572, 1267012263, 593168782, 1080006201} ,{1772787718, 312490087, 113369939, 2110097082, 1343416291, 1624749668, 403701014, 1324556633, 128202193} ,{743891320, 1043447795, 70890599, 294827184, 715449535, 783085833, 1192674928, 736384418, 242966624} ,{899158099, 2054533513, 1057049438, 842531983, 433361979, 1632453996, 56193810, 1896605973, 1165414259} ,{345121506, 1546237762, 1374818642, 2016336311, 1519275276, 2069690329, 1616794668, 161967989, 281470474} ,{1005306205, 422340836, 1937287842, 55168588, 1189763229, 607354576, 1295217011, 782958640, 915727705} ,{1684350207, 1422469912, 1477788124, 1453344409, 1501059142, 1584566616, 493693079, 1173177649, 2056814606} ,{806992707, 230487941, 153041518, 1462221625, 1958026265, 699659770, 981902422, 1333304142, 1706118180} ,{1293118667, 329694629, 272544543, 1313633561, 1648876455, 628722372, 1340981901, 951664078, 1101045663} ,{858813508, 2109305595, 128799819, 1384610517, 8852852, 520230679, 1986828546, 1333465620, 1689623532} ,{1808874614, 1557598351, 1324190156, 542249655, 1731229644, 280247142, 1440852021, 145325630, 606623850} ,{711743270, 1491113328, 1647852939, 1152238279, 1424200532, 168261360, 1588961463, 918224314, 458576928} ,{1910797016, 1369896835, 1939668721, 336199437, 1042287103, 271621150, 770859495, 1677011349, 1421700772} ,{247406034, 2060668687, 687107420, 1200016941, 302283770, 1329831481, 329040584, 1514349466, 416506551} ,{338850640, 21900952, 2017287196, 501962232, 1344975446, 2050786875, 762640878, 20289560, 1337874100} ,{628208965, 131465200, 126538273, 1033536284, 2120785453, 448462974, 1472814785, 1054922021, 1858936928} ,{78444217, 1357128848, 1530972465, 1094503779, 172289190, 1007550825, 1715200588, 1353177032, 987167417} ,{331450875, 1329099548, 1564322941, 1343604297, 1632335489, 676220830, 253903827, 2006357234, 285865363} ,{1007803743, 1020309324, 802359679, 967660714, 498342648, 989700561, 563338665, 1307363093, 1436629936} ,{943403092, 1541112137, 1910423602, 36173705, 881768083, 351927596, 1357752214, 1064198983, 831420986} ,{1649586584, 1547878719, 1605005330, 321596696, 1359900240, 1400020144, 1544495009, 747391084, 1527810090} ,{1875829741, 1167149339, 756532042, 1972712882, 17583753, 617983112, 1828728141, 1440032215, 1124185213} ,{2082111831, 1088646291, 356832754, 850130324, 101979689, 1541023156, 1535461567, 1064338052, 1017951559} ,{729136700, 2086654468, 2043248230, 1039041014, 1506135822, 1831456623, 1724360959, 328140496, 961789919} ,{452970582, 561631939, 1429548534, 2072545721, 1865254223, 617840600, 1213304324, 2077385937, 816046598} ,{105652976, 511684712, 1005027955, 1747229813, 1020634981, 45209441, 1096578833, 611807945, 1343373112} ,{576209740, 1214898698, 562746863, 1718305491, 212717810, 1013225289, 1174485653, 555616236, 1736239045} ,{265449968, 1698826258, 1655467372, 732288424, 1840525450, 317428581, 1651741551, 1335294887, 2124791516} ,{1253216642, 606285883, 1007015386, 151405406, 996191674, 382408423, 1195294985, 269923073, 150099322} ,{113914044, 714453173, 1856291403, 1224065829, 1959756021, 1104079987, 73999037, 876043619, 1545766572} ,{180089365, 31150886, 257171305, 262340553, 1933668258, 504990520, 80424623, 556314482, 1953706554} ,{653314729, 750776501, 2145900897, 1929106169, 1037783521, 1488483987, 229067131, 882565032, 2017375948} ,{552071542, 926979477, 2065543706, 1693569457, 917728958, 948502802, 1948316090, 2085400649, 1154206372} ,{230357708, 256606225, 600760190, 175401219, 1523550118, 1034974486, 882764214, 1369672297, 722042267} ,{1353698821, 1443641354, 1191653066, 98268669, 52696472, 1077220949, 1780993375, 1085533723, 2142365863} ,{1688246430, 167623444, 503700173, 59603534, 1834766095, 650479017, 2101393617, 1149476868, 143959003} ,{788776745, 1149923265, 1744530297, 347134845, 635003041, 1410722316, 821280757, 1885047680, 2122720000} ,{747179417, 1751689709, 93826211, 763454072, 1533240210, 1168054366, 329252277, 326093228, 130617392} ,{902778833, 639315084, 1084168759, 1283804631, 1204490120, 1719206863, 804568904, 1431702244, 1132891064} ,{867801247, 1741150822, 534317634, 1055758761, 214850586, 41556015, 2130385764, 739620473, 1351847791} ,{980782314, 35251984, 1491852334, 1785449067, 1941708822, 1195216727, 1825145756, 717151323, 2012828406} ,{766946307, 1374309895, 1436863565, 1927147077, 1884120600, 1857497964, 1216236007, 105600104, 129173646} ,{1885402416, 739019030, 545283684, 128607592, 94277565, 1797115102, 131041763, 404865522, 167322997} ,{1671865956, 1055655027, 1871588815, 655396639, 2122626080, 41005213, 1105617714, 1771668992, 643548474} ,{506230229, 1109438022, 1085383502, 101609263, 1497751185, 1898625083, 742987270, 915305542, 1392142610} ,{1772250407, 1464366450, 1819931800, 81714976, 1912639850, 1595698084, 1690934200, 1637371653, 587940495} ,{1813957398, 1493627555, 504109943, 118260845, 1578796450, 643994101, 386086190, 1056872502, 1423639439} ,{1485158271, 469861517, 757644866, 1072228752, 121103165, 1131172128, 1220728080, 644973924, 324859021} ,{156486153, 911534875, 169363832, 883054057, 2105728739, 105145445, 669433838, 434003849, 772711386} ,{140441843, 125788989, 953646098, 525110200, 1392662388, 398756891, 1995197192, 1115281371, 35946267} ,{1001501032, 777806188, 2113289614, 1587409798, 1830372311, 1715771015, 975898847, 1997464478, 916511379} ,{1420638284, 47671442, 116720674, 1581673734, 458778533, 2127428419, 1412230192, 228399708, 1237832094} ,{830392375, 400157242, 1308325582, 1074108730, 737803280, 1720132646, 1350524250, 1675167066, 795739901} ,{1636308395, 2115845661, 2103610042, 1854259082, 508865792, 1006266811, 785049017, 1257369379, 2108895974} ,{972869632, 1962435832, 1557825517, 1468103014, 700180905, 762938002, 1505545581, 1048536243, 655315767} ,{17838325, 32750089, 1613963745, 685357051, 824971126, 563377587, 20917219, 570661842, 839987846} ,{2080224261, 1970840956, 11641567, 1603176824, 578710454, 1350136647, 290713017, 1179304721, 273327984} ,{1850522389, 1185539240, 89347544, 1800247218, 288407290, 1504224045, 99740040, 1765338204, 100035648} ,{2027454713, 112299962, 448640290, 359412624, 945774278, 798555183, 491268523, 509591188, 1626410821} ,{370226007, 534903305, 760095605, 1904604692, 1994646390, 1156943479, 247791205, 207265035, 1385232716} ,{10753570, 1180594183, 129261327, 13395651, 1135533406, 103088610, 1166922137, 1430334003, 957039052} ,{283577490, 1019177979, 616320933, 1534437219, 2008372207, 1993667681, 25911427, 129503849, 781929854} ,{1337354204, 876869541, 504990890, 1944353286, 441675107, 838292299, 104214403, 82517145, 949665018} ,{1525301549, 2034983521, 1232179781, 1611560139, 1766337087, 320011973, 368556314, 838320335, 936054058} ,{281066592, 1046052726, 888057209, 1004726110, 281660621, 88685960, 857017123, 1026892664, 351744918} ,{517787712, 2115090911, 1866809182, 1619863442, 255124759, 1074871753, 738984792, 502225306, 723590996} ,{358375818, 499079648, 1614401240, 901262808, 1969979871, 158899747, 162921958, 629491758, 1534909391} ,{1523414038, 146419940, 790959535, 1313912192, 1016832670, 1545960309, 1216241068, 1747221277, 83182676} ,{1506352153, 377898691, 344920568, 1735644633, 384225065, 1246539808, 767625094, 822730756, 1814256218} ,{441190918, 1528363576, 56950817, 1757696656, 33256910, 1159198390, 724973204, 1933452764, 1933528819} ,{718527959, 798951260, 1341042777, 1590884155, 553501097, 440092292, 328017016, 1046581470, 1260939406} ,{1019207266, 1299711548, 1104948162, 1365506296, 243281662, 1587234099, 442042045, 1747662492, 920089978} ,{723760222, 75366178, 1655075156, 1896460446, 40630693, 36798465, 31582958, 1670658046, 100145471} ,{611090211, 955827667, 971137646, 1947383044, 1286234092, 1923062099, 190691729, 699524933, 289781089} ,{1442015672, 1580578427, 62395413, 2091976789, 906647955, 216879483, 308610141, 2091337422, 389428222} ,{1879968236, 1709567395, 840108216, 456780363, 1905188049, 622081783, 944502139, 18830432, 1039603939} ,{1531181384, 1427852057, 360213478, 2020777831, 1977336860, 1321435302, 112459487, 585552640, 1772393436} ,{1672567953, 1130046518, 1866045104, 984902556, 1295977366, 1940512105, 1723736779, 274220501, 1307374373} ,{995818253, 1616337384, 134186405, 970571907, 724381916, 516181719, 1993632268, 1131794072, 1616744179} ,{1986405829, 801342666, 1791849757, 320239713, 129995312, 1072468267, 20621493, 1708886561, 1786742845} ,{1904272510, 1271468622, 1172846292, 413744305, 909021527, 1678715067, 1336915295, 1556436687, 1115887963} ,{1155700101, 1777429807, 1733744535, 2094491132, 958714493, 1480308954, 571391307, 498639804, 1213687291} ,{653774252, 622971674, 16832206, 1878498621, 199520724, 1682640874, 931146453, 800978086, 563423488} ,{834875140, 526993975, 2069754407, 1634085691, 1452773610, 234787676, 1558081991, 122389353, 658318060} ,{808236483, 1955505896, 539850467, 1782825094, 766894245, 1994745674, 1344928034, 667855188, 1392138388} ,{251796301, 832660781, 1547291704, 1835766639, 1129652128, 851906258, 1034376149, 292618981, 1134609667} ,{650189050, 534103921, 1772194399, 1766241714, 1496072957, 1380279577, 966974000, 1427800814, 352097578} ,{1539268390, 1802099038, 1732828711, 2117843991, 1386879090, 189613278, 1451715699, 1502369314, 216804736} ,{128830494, 810912144, 134539106, 261796872, 1130859305, 121576377, 1917348474, 1093963459, 640597912} ,{1800300769, 1418583782, 1014988938, 2104190004, 208339634, 1754846154, 1904031337, 1115656489, 784227504} ,{1601429117, 972023510, 1080594817, 1485110258, 849173338, 89400772, 190311088, 130011189, 1770037063} ,{801989306, 2122670670, 1511343969, 1446823603, 1070945406, 338735860, 1392786418, 744599992, 2098825597} ,{1474121652, 1284090416, 1615559898, 879699354, 402401755, 2056638026, 1539981683, 662828312, 1458902034} ,{920447909, 907256367, 814053439, 471578464, 1979591770, 1551467240, 271039905, 913585409, 478720871} ,{1424961031, 751705093, 1253778648, 1194474639, 870202748, 1434084738, 537100625, 1543360332, 1618516820} ,{1674292368, 1772461085, 306432515, 1165387284, 1920771405, 2045452215, 1201136975, 208118431, 881562313} ,{2021457172, 234151611, 1755542051, 1781486147, 1189764241, 815131789, 289129919, 1572916493, 2035107839} ,{1861107207, 1045793432, 2094992904, 280408762, 1185593830, 1146025186, 190645536, 1931669742, 724257785} ,{667548678, 1215815684, 671044902, 977529525, 1108325336, 767425282, 617194418, 546763114, 272713152} ,{787728674, 712665123, 959902336, 1345679085, 458863280, 1940198640, 280832812, 388394142, 537673695} ,{106121270, 1421349656, 800376057, 192623482, 891789488, 310465388, 1550706630, 723617985, 1175394105} ,{1276552452, 1752754258, 396329688, 1742914497, 911900851, 446967441, 347265266, 118133101, 977928448} ,{674671788, 923257324, 1573499495, 271223647, 633016363, 1546115657, 710236468, 881634342, 539192708} ,{1862905487, 404812794, 2127599282, 269272703, 320036172, 504415150, 877312156, 1680200366, 376211138} ,{2066753331, 136408375, 247833380, 144016533, 1097781771, 594160715, 1650489865, 103215992, 202662715} ,{561664989, 1320123821, 20547721, 1085990622, 265434474, 2089908716, 1007066588, 1592510775, 1318668438} ,{2014571333, 371033380, 1948551601, 501093964, 246771683, 1061797784, 2006419106, 443092371, 1272805359} ,{1379525157, 1131689368, 471342666, 1061919500, 996436135, 326733016, 2074973028, 1194672399, 470926613} ,{126825737, 502347018, 61979871, 2110816565, 1976514458, 135835463, 336560006, 489782010, 731348931} ,{945623733, 2073217663, 2099287976, 1939632323, 1613364417, 990630080, 1752567448, 37743528, 311663994} ,{14818725, 538679347, 635867174, 37583454, 1003661699, 1070363705, 798110789, 724612319, 642673205} ,{1145044236, 977172434, 1812507188, 871669422, 349742333, 146910222, 1975836975, 1790739216, 1440970525} ,{2107161609, 2146525662, 1811386826, 338062963, 997668309, 227903093, 980001467, 746681726, 1380574799} ,{486202949, 1684798545, 1161244629, 218281322, 1461153196, 333511507, 1796134878, 1522369397, 606201360} ,{1770699527, 678996876, 664230875, 346930560, 1662234515, 810871318, 165677549, 1255311059, 1115501462} ,{1883003536, 1252399137, 1559563186, 1466043470, 766217871, 163851170, 428473453, 564458485, 1105528313} ,{1682255259, 958348699, 1772317063, 263258874, 202317483, 262185176, 1251055522, 40137806, 832514345} ,{166234425, 1519004393, 1525141877, 1836294417, 1470918674, 1997693256, 396452508, 409045851, 361384985} ,{1736502975, 1603387171, 873545358, 1005386490, 1584270069, 458740218, 498237917, 508880672, 547168375} ,{408694021, 1464134103, 1630453440, 210964587, 981865791, 1428465378, 915742193, 1286319190, 1605395898} ,{1423250515, 2035496656, 678150920, 1540911197, 1189370216, 1000488064, 757476827, 2027413584, 909272191} ,{618049052, 821853822, 2099383110, 210810420, 1785114773, 1894026842, 1651222080, 1780366339, 1054612372} ,{276659015, 1171381207, 758177633, 145721993, 574568418, 537679640, 569989759, 1986335042, 94099518} ,{1480435469, 1694297772, 454654773, 1360025867, 908035892, 2142512999, 1744884688, 1608309685, 881998414} ,{1968615079, 1656755375, 1993366622, 998344367, 28937154, 860365915, 1155367640, 593965693, 1216913532} ,{1140906854, 237792652, 906433879, 1591468549, 949033143, 1341713806, 1081079141, 1320465032, 1775360570} ,{613364720, 1724206024, 883134537, 252612976, 2132670796, 1509528372, 1643289795, 1288074283, 2003967344} ,{440308260, 950464619, 1286381340, 11837465, 1635313787, 796356728, 1866649944, 1935490361, 1065266343} ,{48434322, 1947443176, 286025632, 898329135, 2055921025, 2145239528, 394185256, 330867566, 436878824} ,{2094553555, 1576281646, 555214550, 880884357, 138298721, 306316990, 1521745427, 1099892734, 1256574817} ,{488778025, 1397109876, 680230558, 898172365, 39413646, 1762908479, 2103322055, 1772622381, 1673853642} ,{1293451821, 59094087, 2005768020, 168479994, 751187423, 1861243779, 1246958515, 1342657249, 1247625027} ,{197676655, 1054970504, 1253670760, 1861827422, 1176633591, 448912763, 1418673840, 1365291578, 484033174} ,{1170201996, 162623306, 1849560204, 151071672, 442546301, 1471039525, 1871567282, 1858255135, 1402769739} ,{306394847, 1954003877, 199684492, 487159794, 2018661236, 450901310, 1115610857, 1922057094, 701696159} ,{417828677, 778092456, 1418686854, 1140367019, 602882650, 309729872, 451287940, 982416240, 259036966} ,{197931806, 1888412940, 1503994486, 518266328, 1571294798, 315898255, 856559185, 755341525, 1843796176} ,{1963027320, 1388958721, 303016177, 1022801991, 1322230057, 701899159, 2097460936, 2006677581, 727267086} ,{2087663868, 368351760, 1789847648, 481900297, 305547629, 496662865, 5402910, 1553751102, 2123966755} ,{1072138688, 1676849683, 218557230, 1256822838, 880034729, 13110954, 1717206949, 1622168047, 1063895189} ,{582332959, 954073947, 730393917, 1329494272, 1362173142, 642883229, 1471752428, 1673374381, 883866291} ,{812516952, 1169885791, 1236462108, 1907745011, 2141412313, 26511399, 1119808936, 1086315154, 1116956915} ,{203051139, 2075086320, 248931142, 1036905681, 1470459045, 1896644239, 1625215123, 790202935, 657017106} ,{391526948, 1166894491, 888493202, 909554027, 192286649, 1888853518, 12646106, 1549372984, 79502834} ,{338210054, 1042552550, 1724435100, 326327395, 1505840460, 279933937, 1304335675, 1480422644, 1417189264} ,{1625241944, 142114324, 374886669, 663196355, 1328351252, 2776373, 1769779309, 1010141238, 760053454} ,{1303473866, 1270914567, 825253458, 1325476945, 31003778, 1458943808, 1173616584, 1344207870, 634272847} ,{1704532647, 781317474, 678900205, 1922726371, 862814454, 936350324, 780674321, 1756864541, 355286047} ,{691828202, 1345625827, 854220906, 1600789169, 1666051590, 1318234321, 500213076, 996217863, 1639497885} ,{1343132366, 808188170, 877696809, 668030531, 213052010, 982336936, 104382152, 1919986966, 185301666} ,{1840737732, 1990316468, 1668774220, 78646902, 931008248, 487064803, 1820424086, 85743566, 1545243094} ,{126865257, 729339235, 1177600601, 1350999620, 255056574, 362813848, 1909609213, 2078500766, 171826400} ,{2086263230, 1437545402, 1173875958, 1316635030, 1615749964, 1781095095, 13621763, 1633989960, 1043853478} ,{460635857, 734408275, 1764807375, 1898333329, 1292536636, 251063178, 1462186898, 1171819550, 1956141617} ,{1431162880, 490029232, 1262503105, 1329807571, 751264109, 2136605871, 1695807056, 306778372, 1932381867} ,{1988937863, 2027480699, 1535004989, 214807941, 2139126656, 1653474994, 312043647, 857211941, 1946476659} ,{818958097, 1388277664, 1397334511, 1660691557, 2048007464, 779519783, 1446625134, 510127666, 366224228} ,{280969743, 1174250323, 2079217420, 951676236, 261379682, 1398033460, 1589905709, 1361576879, 1824937239} ,{1951571268, 271837383, 1355560478, 684631178, 1435288185, 447780898, 984088799, 232825916, 924302385} ,{2046214895, 44243714, 300323637, 765919960, 206501312, 938681236, 1689772122, 389248082, 285012848} ,{1830236273, 1288660329, 1970837636, 1933369000, 460566881, 2043942004, 1684954664, 1773131597, 1665331922} ,{201676699, 1998072415, 533742967, 2134059984, 1557115276, 1820111388, 755697440, 1869565880, 902767218} ,{1723567772, 32608884, 1354576639, 1369109799, 1284454825, 717850352, 244335557, 961469807, 1699559669} ,{1018127524, 506279999, 199983247, 153825334, 104450790, 579432095, 1607794749, 557464813, 1305202476} ,{974706551, 1778115591, 121543928, 420407332, 175662788, 470600657, 1782194798, 1729374435, 1180994471} ,{2126178131, 1227265672, 1799394210, 1089246034, 482572629, 1664219332, 357923985, 1500843551, 1138393392} } /* End of byte 23 */ }; CombBLAS_beta_16_2/graph500-1.2/generator/generator_test_xmt.c000644 000765 000024 00000003431 13212627400 025315 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include #include #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include #include #include "make_graph.h" int main(int argc, char* argv[]) { int log_numverts; unsigned int start, time_taken; size_t i; int64_t nedges, actual_nedges; int64_t* result; log_numverts = 16; /* In base GRAPHGEN_INITIATOR_SIZE */ if (argc >= 2) log_numverts = atoi(argv[1]); /* Start of graph generation timing */ #pragma mta fence start = mta_get_clock(0); double initiator[] = {.57, .19, .19, .05}; make_graph(log_numverts, 8. * pow(2., log_numverts), 1, 2, initiator, &nedges, &result); #pragma mta fence time_taken = mta_get_clock(start); /* End of graph generation timing */ actual_nedges = 0; #pragma mta block schedule for (i = 0; i < nedges; ++i) if (result[i * 2] != (int64_t)(-1)) ++actual_nedges; fprintf(stderr, "%" PRIu64 " edge%s generated and permuted in %fs (%f Medges/s)\n", actual_nedges, (actual_nedges == 1 ? "" : "s"), time_taken * mta_clock_period(), 1. * actual_nedges / time_taken * 1.e-6 / mta_clock_period()); free(result); return 0; } CombBLAS_beta_16_2/graph500-1.2/generator/generator_test_omp.c000644 000765 000024 00000003331 13212627400 025277 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include #include #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include #include #include "make_graph.h" int main(int argc, char* argv[]) { int log_numverts; double start, time_taken; int64_t i; int64_t nedges, actual_nedges; int64_t* result; log_numverts = 16; /* In base GRAPHGEN_INITIATOR_SIZE */ if (argc >= 2) log_numverts = atoi(argv[1]); /* Start of graph generation timing */ start = omp_get_wtime(); double initiator[] = {.57, .19, .19, .05}; make_graph(log_numverts, 8. * pow(2., log_numverts), 1, 2, initiator, &nedges, &result); time_taken = omp_get_wtime() - start; /* End of graph generation timing */ actual_nedges = 0; #pragma omp parallel for reduction(+: actual_nedges) for (i = 0; i < nedges; ++i) if (result[i * 2] != (int64_t)(-1)) ++actual_nedges; fprintf(stderr, "%" PRIu64 " edge%s generated and permuted in %fs (%f Medges/s)\n", actual_nedges, (actual_nedges == 1 ? "" : "s"), time_taken, 1. * actual_nedges / time_taken * 1.e-6); free(result); return 0; } CombBLAS_beta_16_2/graph500-1.2/generator/README000644 000765 000024 00000003603 13212627400 022115 0ustar00aydinbulucstaff000000 000000 Graph500 Benchmark: Scalable Kronecker Graph Generator Jeremiah Willcock and Andrew Lumsdaine This directory contains a parallel Kronecker graph generator, as well as a random permutation generator and code to apply a distributed permutation to the endpoints of the elements of an edge list. The benefits of this approach are two-fold: first, the generated graphs and permutations are consistent for a given graph size, initiator, and seed, regardless of shared vs. distributed memory or processor count; and second, the generator detects parallel edges (including those with the endpoints given in the opposite order for an undirected graph) automatically and without needing to store the edges explicitly. Its main disadvantage is that some performance is lost through the use of a more sophisticated pseudo-random number generator (PRNG); if reproducibility is not required, a cheaper PRNG can be used for more performance while keeping the parallel edge detection property. Four Makefiles are provided: Makefile.seq (sequential), Makefile.xmt (Cray XMT), Makefile.omp (OpenMP), and Makefile.mpi (MPI). There are three corresponding generator_test_* programs that demonstrate how to use the generator. The file make_graph.h declares a simplified interface suitable for benchmark implementations; this interface hides many of the parameters that are fixed in the benchmark specification. Most compile-time settings are at the top of graph_generator.h; users should not need to modify most of them, except for possibly data type sizes. Developers of the benchmark specification itself might benefit from playing around with different initiators and other settings, however. Copyright (C) 2009-2010 The Trustees of Indiana University. Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) CombBLAS_beta_16_2/graph500-1.2/generator/LICENSE_1_0.txt000644 000765 000024 00000002472 13212627400 023522 0ustar00aydinbulucstaff000000 000000 Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CombBLAS_beta_16_2/graph500-1.2/generator/Makefile.mpi000644 000765 000024 00000004746 13212627400 023472 0ustar00aydinbulucstaff000000 000000 AR = ar RANLIB = ranlib CC = cc CFLAGS = -g -Wall -Drestrict=__restrict__ -O3 -DNDEBUG -ffast-math -DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY # -g -pg # CFLAGS = -g -Wall -Drestrict= -DGRAPH_GENERATOR_MPI -DGRAPHGEN_DISTRIBUTED_MEMORY # -g -pg LDFLAGS = -g # -g -pg MPICC = mpicc all: libgraph_generator_mpi.a generator_test_mpi # all: generator_test_xmt generator_test_mpi: generator_test_mpi.c libgraph_generator_mpi.a $(MPICC) $(CFLAGS) $(LDFLAGS) -o generator_test_mpi generator_test_mpi.c -L. -lgraph_generator_mpi -lm libgraph_generator_mpi.a: btrd_binomial_distribution.o splittable_mrg.o mrg_transitions.o graph_generator.o permutation_gen.o apply_permutation_mpi.o make_graph.o utils.o scramble_edges.o $(AR) cruv libgraph_generator_mpi.a btrd_binomial_distribution.o splittable_mrg.o mrg_transitions.o graph_generator.o permutation_gen.o apply_permutation_mpi.o make_graph.o utils.o scramble_edges.o $(RANLIB) libgraph_generator_mpi.a btrd_binomial_distribution.o: btrd_binomial_distribution.c btrd_binomial_distribution.h splittable_mrg.h mod_arith.h $(CC) $(CFLAGS) -c btrd_binomial_distribution.c splittable_mrg.o: splittable_mrg.c splittable_mrg.h mod_arith.h $(CC) $(CFLAGS) -c splittable_mrg.c mrg_transitions.o: mrg_transitions.c splittable_mrg.h $(CC) $(CFLAGS) -c mrg_transitions.c graph_generator.o: graph_generator.c splittable_mrg.h mod_arith.h btrd_binomial_distribution.h graph_generator.h utils.h $(CC) $(CFLAGS) -c graph_generator.c permutation_gen.o: permutation_gen.c splittable_mrg.h mod_arith.h btrd_binomial_distribution.h graph_generator.h permutation_gen.h utils.h $(MPICC) $(CFLAGS) -c permutation_gen.c make_graph.o: make_graph.c splittable_mrg.h mod_arith.h btrd_binomial_distribution.h graph_generator.h make_graph.h permutation_gen.h utils.h scramble_edges.h apply_permutation_mpi.h $(MPICC) $(CFLAGS) -c make_graph.c apply_permutation_mpi.o: apply_permutation_mpi.c splittable_mrg.h mod_arith.h btrd_binomial_distribution.h graph_generator.h make_graph.h permutation_gen.h utils.h apply_permutation_mpi.h $(MPICC) $(CFLAGS) -c apply_permutation_mpi.c utils.o: utils.c splittable_mrg.h mod_arith.h btrd_binomial_distribution.h graph_generator.h make_graph.h permutation_gen.h utils.h $(MPICC) $(CFLAGS) -c utils.c scramble_edges.o: scramble_edges.c splittable_mrg.h mod_arith.h btrd_binomial_distribution.h graph_generator.h make_graph.h permutation_gen.h utils.h scramble_edges.h $(MPICC) $(CFLAGS) -c scramble_edges.c clean: -rm -f generator_test_mpi *.o *.a CombBLAS_beta_16_2/graph500-1.2/generator/make_graph.c000644 000765 000024 00000030555 13212627400 023505 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include #include #include #include #include #include #include #ifdef __MTA__ #include #endif #ifdef GRAPH_GENERATOR_MPI #include #endif #ifdef GRAPH_GENERATOR_OMP #include #endif /* Simplified interface to build graphs with scrambled vertices. */ #include "graph_generator.h" #include "permutation_gen.h" #include "apply_permutation_mpi.h" #include "scramble_edges.h" #include "utils.h" #ifdef GRAPH_GENERATOR_SEQ void make_graph(int log_numverts, int64_t desired_nedges, uint64_t userseed1, uint64_t userseed2, const double initiator[4], int64_t* nedges_ptr, int64_t** result_ptr) { int64_t N, M; /* Spread the two 64-bit numbers into five nonzero values in the correct * range. */ uint_fast32_t seed[5]; #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* edges; #else int64_t* edges; #endif int64_t nedges; int64_t* vertex_perm; int64_t* result; int64_t i; mrg_state state; int64_t v1; int64_t v2; N = (int64_t)pow(GRAPHGEN_INITIATOR_SIZE, log_numverts); M = desired_nedges; make_mrg_seed(userseed1, userseed2, seed); nedges = compute_edge_array_size(0, 1, M); *nedges_ptr = nedges; #ifdef GRAPHGEN_KEEP_MULTIPLICITIES edges = (generated_edge*)xcalloc(nedges, sizeof(generated_edge)); /* multiplicity set to 0 for unused edges */ #else edges = (int64_t*)xmalloc(2 * nedges * sizeof(int64_t)); #endif generate_kronecker(0, 1, seed, log_numverts, M, initiator, edges); vertex_perm = (int64_t*)xmalloc(N * sizeof(int64_t)); /* result; AL: this is a needless warning about unused code. */ #ifdef GRAPHGEN_KEEP_MULTIPLICITIES result = (int64_t*)xmalloc(2 * nedges * sizeof(int64_t)); #else result = edges; #endif *result_ptr = result; mrg_seed(&state, seed); rand_sort_shared(&state, N, vertex_perm); /* Apply vertex permutation to graph, optionally copying into user's result * array. */ #ifdef GRAPHGEN_KEEP_MULTIPLICITIES for (i = 0; i < nedges; ++i) { if (edges[i].multiplicity != 0) { v1 = vertex_perm[edges[i].src]; v2 = vertex_perm[edges[i].tgt]; /* Sort these since otherwise the directions of the permuted edges would * give away the unscrambled vertex order. */ result[i * 2] = (v1 < v2) ? v1 : v2; result[i * 2 + 1] = (v1 < v2) ? v2 : v1; } else { result[i * 2] = result[i * 2 + 1] = (int64_t)(-1); } } free(edges); #else for (i = 0; i < 2 * nedges; i += 2) { if (edges[i] != (int64_t)(-1)) { v1 = vertex_perm[edges[i]]; v2 = vertex_perm[edges[i + 1]]; /* Sort these since otherwise the directions of the permuted edges would * give away the unscrambled vertex order. */ edges[i] = (v1 < v2) ? v1 : v2; edges[i + 1] = (v1 < v2) ? v2 : v1; } } #endif free(vertex_perm); /* Randomly mix up the order of the edges. */ scramble_edges_shared(userseed1, userseed2, nedges, edges); } #endif /* GRAPH_GENERATOR_SEQ */ #ifdef __MTA__ void make_graph(int log_numverts, int64_t desired_nedges, uint64_t userseed1, uint64_t userseed2, const double initiator[4], int64_t* nedges_ptr, int64_t** result_ptr) { int64_t N, M; N = (int64_t)pow(GRAPHGEN_INITIATOR_SIZE, log_numverts); M = (int64_t)desired_nedges; /* Spread the two 64-bit numbers into five nonzero values in the correct * range. */ uint_fast32_t seed[5]; make_mrg_seed(userseed1, userseed2, seed); int64_t nedges = compute_edge_array_size(0, 1, M); *nedges_ptr = nedges; #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* edges = (generated_edge*)xcalloc(nedges, sizeof(generated_edge)); /* multiplicity set to 0 for unused edges */ #else int64_t* edges = (int64_t*)xmalloc(2 * nedges * sizeof(int64_t)); #endif int rank, size; /* The "for all streams" here is in compiler versions >= 6.4 */ #pragma mta use 100 streams #pragma mta for all streams rank of size { double my_initiator[GRAPHGEN_INITIATOR_SIZE * GRAPHGEN_INITIATOR_SIZE]; /* Local copy */ int i; for (i = 0; i < GRAPHGEN_INITIATOR_SIZE * GRAPHGEN_INITIATOR_SIZE; ++i) { my_initiator[i] = initiator[i]; } generate_kronecker(rank, size, seed, log_numverts, M, my_initiator, edges); } int64_t* vertex_perm = (int64_t*)xmalloc(N * sizeof(int64_t)); int64_t* result; #ifdef GRAPHGEN_KEEP_MULTIPLICITIES result = (int64_t*)xmalloc(2 * nedges * sizeof(int64_t)); #else result = edges; #endif *result_ptr = result; mrg_state state; mrg_seed(&state, seed); rand_sort_shared(&state, N, vertex_perm); int64_t i; /* Apply vertex permutation to graph, optionally copying into user's result * array. */ #ifdef GRAPHGEN_KEEP_MULTIPLICITIES #pragma mta assert parallel #pragma mta block schedule for (i = 0; i < nedges; ++i) { if (edges[i].multiplicity != 0) { int64_t v1 = vertex_perm[edges[i].src]; int64_t v2 = vertex_perm[edges[i].tgt]; /* Sort these since otherwise the directions of the permuted edges would * give away the unscrambled vertex order. */ result[i * 2] = MTA_INT_MIN(v1, v2); result[i * 2 + 1] = MTA_INT_MAX(v1, v2); } else { result[i * 2] = result[i * 2 + 1] = (int64_t)(-1); } } free(edges); #else #pragma mta assert parallel #pragma mta block schedule for (i = 0; i < 2 * nedges; i += 2) { if (edges[i] != (int64_t)(-1)) { int64_t v1 = vertex_perm[edges[i]]; int64_t v2 = vertex_perm[edges[i + 1]]; /* Sort these since otherwise the directions of the permuted edges would * give away the unscrambled vertex order. */ edges[i] = MTA_INT_MIN(v1, v2); edges[i + 1] = MTA_INT_MAX(v1, v2); } } #endif free(vertex_perm); /* Randomly mix up the order of the edges. */ scramble_edges_shared(userseed1, userseed2, nedges, edges); } #endif /* __MTA__ */ #ifdef GRAPH_GENERATOR_OMP void make_graph(int log_numverts, int64_t desired_nedges, uint64_t userseed1, uint64_t userseed2, const double initiator[4], int64_t* nedges_ptr, int64_t** result_ptr) { int64_t N, M; N = (int64_t)pow(GRAPHGEN_INITIATOR_SIZE, log_numverts); M = desired_nedges; /* Spread the two 64-bit numbers into five nonzero values in the correct * range. */ uint_fast32_t seed[5]; make_mrg_seed(userseed1, userseed2, seed); int64_t nedges = compute_edge_array_size(0, 1, M); *nedges_ptr = nedges; #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* edges = (generated_edge*)xcalloc(nedges, sizeof(generated_edge)); /* multiplicity set to 0 for unused edges */ #else int64_t* edges = (int64_t*)xmalloc(2 * nedges * sizeof(int64_t)); #endif #pragma omp parallel { int rank = omp_get_thread_num(), size = omp_get_num_threads(); generate_kronecker(rank, size, seed, log_numverts, M, initiator, edges); } int64_t* vertex_perm = (int64_t*)xmalloc(N * sizeof(int64_t)); int64_t* result; #ifdef GRAPHGEN_KEEP_MULTIPLICITIES result = (int64_t*)xmalloc(2 * nedges * sizeof(int64_t)); #else result = edges; #endif *result_ptr = result; mrg_state state; mrg_seed(&state, seed); rand_sort_shared(&state, N, vertex_perm); int64_t i; /* Apply vertex permutation to graph, optionally copying into user's result * array. */ #ifdef GRAPHGEN_KEEP_MULTIPLICITIES #pragma omp parallel for for (i = 0; i < nedges; ++i) { if (edges[i].multiplicity != 0) { int64_t v1 = vertex_perm[edges[i].src]; int64_t v2 = vertex_perm[edges[i].tgt]; /* Sort these since otherwise the directions of the permuted edges would * give away the unscrambled vertex order. */ result[i * 2] = (v1 < v2) ? v1 : v2; result[i * 2 + 1] = (v1 < v2) ? v2 : v1; } else { result[i * 2] = result[i * 2 + 1] = (int64_t)(-1); } } free(edges); #else #pragma omp parallel for for (i = 0; i < 2 * nedges; i += 2) { if (edges[i] != (int64_t)(-1)) { int64_t v1 = vertex_perm[edges[i]]; int64_t v2 = vertex_perm[edges[i + 1]]; /* Sort these since otherwise the directions of the permuted edges would * give away the unscrambled vertex order. */ edges[i] = (v1 < v2) ? v1 : v2; edges[i + 1] = (v1 < v2) ? v2 : v1; } } #endif free(vertex_perm); /* Randomly mix up the order of the edges. */ scramble_edges_shared(userseed1, userseed2, nedges, edges); } #endif /* GRAPH_GENERATOR_OMP */ #ifdef GRAPH_GENERATOR_MPI void make_graph(int log_numverts, int64_t desired_nedges, uint64_t userseed1, uint64_t userseed2, const double initiator[4], int64_t* nedges_ptr, int64_t** result_ptr) { int64_t N, M; int rank, size; N = (int64_t)pow(GRAPHGEN_INITIATOR_SIZE, log_numverts); M = desired_nedges; /* Spread the two 64-bit numbers into five nonzero values in the correct * range. */ uint_fast32_t seed[5]; make_mrg_seed(userseed1, userseed2, seed); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); int64_t nedges = compute_edge_array_size(rank, size, M); #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* local_edges = (generated_edge*)xmalloc(nedges * sizeof(generated_edge)); #else int64_t* local_edges = (int64_t*)xmalloc(2 * nedges * sizeof(int64_t)); #endif double start = MPI_Wtime(); generate_kronecker(rank, size, seed, log_numverts, M, initiator, local_edges); double gen_time = MPI_Wtime() - start; int64_t* local_vertex_perm = NULL; mrg_state state; mrg_seed(&state, seed); start = MPI_Wtime(); int64_t perm_local_size; rand_sort_mpi(MPI_COMM_WORLD, &state, N, &perm_local_size, &local_vertex_perm); double perm_gen_time = MPI_Wtime() - start; /* Copy the edge endpoints into the result array if necessary. */ int64_t* result; #ifdef GRAPHGEN_KEEP_MULTIPLICITIES result = (int64_t*)xmalloc(2 * nedges * sizeof(int64_t)); for (i = 0; i < nedges; ++i) { if (local_edges[i].multiplicity != 0) { result[i * 2] = local_edges[i].src; result[i * 2 + 1] = local_edges[i].tgt; } else { result[i * 2] = result[i * 2 + 1] = (int64_t)(-1); } } free(local_edges); local_edges = NULL; #else result = local_edges; *result_ptr = result; local_edges = NULL; /* Freed by caller */ #endif /* Apply vertex permutation to graph. */ start = MPI_Wtime(); apply_permutation_mpi(MPI_COMM_WORLD, perm_local_size, local_vertex_perm, N, nedges, result); double perm_apply_time = MPI_Wtime() - start; free(local_vertex_perm); local_vertex_perm = NULL; /* Randomly mix up the order of the edges. */ start = MPI_Wtime(); int64_t* new_result; int64_t nedges_out; scramble_edges_mpi(MPI_COMM_WORLD, userseed1, userseed2, nedges, result, &nedges_out, &new_result); double edge_scramble_time = MPI_Wtime() - start; free(result); result = NULL; *result_ptr = new_result; *nedges_ptr = nedges_out; if (rank == 0) { fprintf(stdout, "unpermuted_graph_generation: %f s\n", gen_time); fprintf(stdout, "vertex_permutation_generation: %f s\n", perm_gen_time); fprintf(stdout, "vertex_permutation_application: %f s\n", perm_apply_time); fprintf(stdout, "edge_scrambling: %f s\n", edge_scramble_time); } } #endif /* PRNG interface for implementations; takes seed in same format as given by * users, and creates a vector of doubles in a reproducible (and * random-access) way. */ void make_random_numbers( /* in */ int64_t nvalues /* Number of values to generate */, /* in */ uint64_t userseed1 /* Arbitrary 64-bit seed value */, /* in */ uint64_t userseed2 /* Arbitrary 64-bit seed value */, /* in */ int64_t position /* Start index in random number stream */, /* out */ double* result /* Returned array of values */ ) { int64_t i; uint_fast32_t seed[5]; mrg_state st; make_mrg_seed(userseed1, userseed2, seed); mrg_seed(&st, seed); mrg_skip(&st, 2, 0, 2 * position); /* Each double takes two PRNG outputs */ for (i = 0; i < nvalues; ++i) { result[i] = mrg_get_double_orig(&st); } } CombBLAS_beta_16_2/graph500-1.2/generator/utils.c000644 000765 000024 00000005543 13212627400 022546 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #include "splittable_mrg.h" #include "graph_generator.h" #include "permutation_gen.h" #include #include #include #include #include #ifdef __MTA__ #include #endif #ifdef GRAPH_GENERATOR_MPI #include #endif #ifdef GRAPH_GENERATOR_OMP #include #endif #include "utils.h" void* xmalloc(size_t n) { void* p = malloc(n); if (!p) { fprintf(stderr, "Out of memory trying to allocate %zu byte(s)\n", n); abort(); } return p; } void* xcalloc(size_t n, size_t k) { void* p = calloc(n, k); if (!p) { fprintf(stderr, "Out of memory trying to allocate %zu byte(s)\n", n); abort(); } return p; } /* Get a number in [0, n) in an unbiased way. */ #ifdef __MTA__ #pragma mta inline #endif uint_fast64_t random_up_to(mrg_state* st, uint_fast64_t n) { /* PRNG returns values in [0, 0x7FFFFFFF) */ /* Two iters returns values in [0, 0x3FFFFFFF00000001) */ assert (n > 0 && n <= UINT64_C(0x3FFFFFFF00000001)); if (n == 1) { return 0; } else if (n <= UINT64_C(0x7FFFFFFF)) { uint_fast64_t acc_value_limit = (UINT64_C(0x7FFFFFFF) / n) * n; /* Round down to multiple of n */ while (1) { uint_fast64_t acc = mrg_get_uint_orig(st); if (acc >= acc_value_limit) continue; return acc % n; } } else if (n <= UINT64_C(0x3FFFFFFF00000001)) { uint_fast64_t acc_value_limit = (UINT64_C(0x3FFFFFFF00000001) / n) * n; /* Round down to multiple of n */ while (1) { uint_fast64_t acc = mrg_get_uint_orig(st) * UINT64_C(0x7FFFFFFF); acc += mrg_get_uint_orig(st); /* Do this separately to get fixed ordering. */ if (acc >= acc_value_limit) continue; return acc % n; } } else { /* Should have been caught before */ return 0; } } /* Spread the two 64-bit numbers into five nonzero values in the correct * range. */ void make_mrg_seed(uint64_t userseed1, uint64_t userseed2, uint_fast32_t* seed) { seed[0] = (userseed1 & 0x3FFFFFFF) + 1; seed[1] = ((userseed1 >> 30) & 0x3FFFFFFF) + 1; seed[2] = (userseed2 & 0x3FFFFFFF) + 1; seed[3] = ((userseed2 >> 30) & 0x3FFFFFFF) + 1; seed[4] = ((userseed2 >> 60) << 4) + (userseed1 >> 60) + 1; } CombBLAS_beta_16_2/graph500-1.2/generator/apply_permutation_mpi.c000644 000765 000024 00000033542 13212627400 026027 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ /* MPI code to apply a distributed permutation. The versions for sequential, * XMT, and OpenMP are trivial and so are in the corresponding versions of * make_graph() (make_graph.c), but the MPI one is long and so it is in a * separate file. */ #include #include #include #include #include #include #include #include #include "graph_generator.h" #include "permutation_gen.h" #include "apply_permutation_mpi.h" #include "utils.h" void gather_block_distribution_info(MPI_Comm comm, const int64_t local_perm_size, const int64_t global_perm_size, int64_t* perm_displs /* size = MPI comm size + 1 */, int** perm_owner_table_ptr /* malloc'ed in here */, int64_t** perm_owner_cutoff_ptr /* malloc'ed in here */, int* lg_minpermsize_ptr, int64_t* maxpermsize_ptr) { int rank, size; MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &size); /* Get the size of the permutation fragment on each node. */ int64_t* perm_sizes = (int64_t*)xmalloc(size * sizeof(int64_t)); MPI_Allgather((void*)&local_perm_size, 1, INT64_T_MPI_TYPE, perm_sizes, 1, INT64_T_MPI_TYPE, comm); /* Sanity check the permutation to make sure its distribution is reasonable * (PRNG problems can cause bad distributions) and get some statistics to use * later. */ int minpermsize = local_perm_size, maxpermsize = local_perm_size; int64_t i; for (i = 0; i < size; ++i) { if (perm_sizes[i] < minpermsize) minpermsize = perm_sizes[i]; if (perm_sizes[i] > maxpermsize) maxpermsize = perm_sizes[i]; } *maxpermsize_ptr = maxpermsize; if (minpermsize == 0) { fprintf(stderr, "One node has permutation size 0\n"); abort(); } if (maxpermsize > 2 * minpermsize && rank == 0 && maxpermsize >= 1000) { fprintf(stderr, "Minimum permutation bucket size %d is too small compared to maximum size %d\n", minpermsize, maxpermsize); abort(); } /* Compute floor(log_2(minpermsize)) for fast computation in lookup table. */ int lg_minpermsize = 0; while ((1 << lg_minpermsize) <= minpermsize) ++lg_minpermsize; --lg_minpermsize; int minpermsize_for_table = (1 << lg_minpermsize); *lg_minpermsize_ptr = lg_minpermsize; /* Prefix sum permutation sizes (exclusive) for owner lookups. */ perm_displs[0] = 0; for (i = 1; i < size + 1; ++i) perm_displs[i] = perm_displs[i - 1] + perm_sizes[i - 1]; free(perm_sizes); perm_sizes = NULL; /* Create tables containing permutation_owner(minpermsize_for_table * i) and * perm_displs[permutation_owner(minpermsize_for_table * i) + 1] for i = 0 ... global_perm_size / * minpermsize_for_table. These two tables allow a constant-time owner lookup. */ size_t perm_owner_table_size = (size_t)((global_perm_size + minpermsize_for_table - 1) >> lg_minpermsize); int* perm_owner_table = (int*)xmalloc(perm_owner_table_size * sizeof(int)); *perm_owner_table_ptr = perm_owner_table; int64_t* perm_owner_cutoff = (int64_t*)xmalloc(perm_owner_table_size * sizeof(int64_t)); *perm_owner_cutoff_ptr = perm_owner_cutoff; int cur_owner = 0; for (i = 0; i < perm_owner_table_size; ++i) { assert (cur_owner < size); while ((i << lg_minpermsize) >= perm_displs[cur_owner + 1]) { ++cur_owner; assert (cur_owner < size); } perm_owner_table[i] = cur_owner; perm_owner_cutoff[i] = perm_displs[cur_owner + 1]; assert (perm_displs[cur_owner] <= (i << lg_minpermsize) && perm_displs[cur_owner + 1] > (i << lg_minpermsize)); } } void apply_permutation_mpi(MPI_Comm comm, const int64_t local_perm_size, const int64_t* const local_vertex_perm, const int64_t N, const int64_t nedges, int64_t* result) { int rank, size; MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &size); int64_t* perm_displs = (int64_t*)xmalloc((size + 1) * sizeof(int64_t)); int* perm_owner_table; int64_t* perm_owner_cutoff; int lg_minpermsize; int64_t maxpermsize; gather_block_distribution_info(comm, local_perm_size, N, perm_displs, &perm_owner_table, &perm_owner_cutoff, &lg_minpermsize, &maxpermsize); int64_t my_perm_displ = perm_displs[rank]; #define LOOKUP_PERM_OWNER(v) \ (perm_owner_table[(v) >> lg_minpermsize] + \ ((v) >= perm_owner_cutoff[(v) >> lg_minpermsize])) /* From here on until a cleanup at the end of permutation application, vertex * numbers in edges will be coded as either (unpermuted vertex) or (permuted * vertex - N - 1) [avoiding -1 in the second case because that still means * unused vertex slot]. */ /* Apply any permutation elements that I own. */ int64_t i; for (i = 0; i < 2 * nedges; ++i) { if (result[i] >= perm_displs[rank] && result[i] < perm_displs[rank + 1]) { result[i] = local_vertex_perm[result[i] - perm_displs[rank]] - N - 1; } } /* Create a bitmap (as bytes) for which nodes I will need data from. */ unsigned char* owner_bitmap = (unsigned char*)xcalloc(size, sizeof(unsigned char)); /* Uses zero-init */ for (i = 0; i < 2 * nedges; ++i) { if (result[i] >= 0) { owner_bitmap[LOOKUP_PERM_OWNER(result[i])] = 1; } } /* Send those bitmaps so owners of permutation segments know who they will be * sending data to. */ unsigned char* send_bitmap = (unsigned char*)xmalloc(size * sizeof(unsigned char)); MPI_Alltoall(owner_bitmap, 1, MPI_UNSIGNED_CHAR, send_bitmap, 1, MPI_UNSIGNED_CHAR, comm); /* Go through node offsets in batches of node_chunk_size. Create bitmaps of * which particular permutation elements will be needed from each of those * nodes, and which local vertices in result should be checked when those * elements arrive. */ const int node_chunk_size = 64; const int ulong_bits = sizeof(unsigned long) * CHAR_BIT; const size_t needed_element_bitmap_size = (maxpermsize + ulong_bits - 1) / ulong_bits; /* Per destination */ unsigned long* needed_element_bitmap = (unsigned long*)xmalloc(needed_element_bitmap_size * node_chunk_size * sizeof(unsigned long)); /* Number of needed elements per source */ int64_t* num_needed_elements = (int64_t*)xmalloc(node_chunk_size * sizeof(int64_t)); const size_t rescan_bitmap_size = (2 * nedges + ulong_bits - 1) / ulong_bits; unsigned long* rescan_bitmap = (unsigned long*)xmalloc(rescan_bitmap_size * sizeof(unsigned long)); int64_t* incoming_perm_requests = (int64_t*)xmalloc(maxpermsize * sizeof(int64_t)); int64_t* incoming_perm_replies = (int64_t*)xmalloc(maxpermsize * sizeof(int64_t)); int64_t* outgoing_perm_requests = (int64_t*)xmalloc(maxpermsize * sizeof(int64_t)); int64_t* outgoing_perm_replies = (int64_t*)xmalloc(maxpermsize * sizeof(int64_t)); int64_t* perm_replies_spread = (int64_t*)xmalloc(maxpermsize * sizeof(int64_t)); /* Replies scattered for direct indexing */ int offset; for (offset = 1 /* Already did our own perm elements */; offset < size; offset += node_chunk_size) { /* Chunk of destinations with this offset is [rank+offset, rank+min(size, * offset+node_chunk_size)), all of those modulo size. */ int num_nodes_being_processed = size - offset; if (num_nodes_being_processed > node_chunk_size) { num_nodes_being_processed = node_chunk_size; } /* Clear bitmaps. */ memset(num_needed_elements, 0, node_chunk_size * sizeof(int64_t)); memset(needed_element_bitmap, 0, node_chunk_size * needed_element_bitmap_size * sizeof(unsigned long)); memset(rescan_bitmap, 0, rescan_bitmap_size * sizeof(unsigned long)); #define SET_BIT(bm, idx) do { \ (bm)[(idx) / ulong_bits] |= (1UL << ((idx) % ulong_bits)); \ } while (0) #define TEST_BIT_SCALAR(bm, idx) (((bm) & (1UL << ((idx) % ulong_bits))) != 0) #define TEST_BIT(bm, idx) TEST_BIT_SCALAR((bm)[(idx) / ulong_bits], idx) /* Fill in needed_element_bitmap (and num_needed_elements), as well as * rescan_bitmap, for this set of destinations (permutation owners). */ for (i = 0; i < 2 * nedges; ++i) { int64_t v = result[i]; if (v < 0) continue; int owner = LOOKUP_PERM_OWNER(v); int64_t owner_displ = perm_displs[owner]; int delta = (owner + size - rank) % size; /* Rank difference from mine, modulo size */ if (delta >= offset && delta < offset + node_chunk_size) { unsigned long* bm = needed_element_bitmap + (delta - offset) * needed_element_bitmap_size; if (!TEST_BIT(bm, v - owner_displ)) { SET_BIT(bm, v - owner_displ); ++num_needed_elements[delta - offset]; } SET_BIT(rescan_bitmap, i); } } /* For each delta in the current range, set up the sends and receives if * necessary (as determined by send_bitmap and owner_bitmap). */ int delta; for (delta = offset; delta < offset + num_nodes_being_processed; ++delta) { MPI_Request incomingreq; int src = (rank + size - delta) % size; if (send_bitmap[src]) { /* Will I get any requests from src? */ /* Set up the receive for incoming permutation data requests. */ MPI_Irecv(incoming_perm_requests, maxpermsize, INT64_T_MPI_TYPE, src, 0, comm, &incomingreq); } int dest = (rank + delta) % size; MPI_Request responsereq; int output_offset = 0; int64_t perm_displ_for_dest = perm_displs[dest]; /* First permutation element on dest */ if (owner_bitmap[dest]) { /* Convert the needed_element_bitmap chunk for this destination into an * array of individual vertex indices. */ const unsigned long* bm = needed_element_bitmap + (delta - offset) * needed_element_bitmap_size; int64_t block; for (block = 0; block < needed_element_bitmap_size; ++block) { unsigned long val = bm[block]; if (!val) continue; int bit; for (bit = 0; bit < ulong_bits; ++bit) { if (!TEST_BIT_SCALAR(val, bit)) continue; outgoing_perm_requests[output_offset++] = perm_displ_for_dest + block * ulong_bits + bit; } } /* Do irecv first to allow rsend on destination. */ MPI_Irecv(outgoing_perm_replies, output_offset, INT64_T_MPI_TYPE, dest, 1, comm, &responsereq); /* Send the requests, using ssend for flow control (this can be changed * if your system has good flow control). */ MPI_Ssend(outgoing_perm_requests, output_offset, INT64_T_MPI_TYPE, dest, 0, comm); } if (send_bitmap[src]) { /* Wait for and process an incoming request if send_bitmap says there * will be one from this src. */ MPI_Status st; MPI_Wait(&incomingreq, &st); int count; MPI_Get_count(&st, INT64_T_MPI_TYPE, &count); int i; for (i = 0; i < count; ++i) { incoming_perm_replies[i] = local_vertex_perm[incoming_perm_requests[i] - my_perm_displ]; } /* Rsend the reply for performance; note recv being before send in * request-sending code above. */ MPI_Rsend(incoming_perm_replies, count, INT64_T_MPI_TYPE, src, 1, comm); } if (owner_bitmap[dest]) { MPI_Wait(&responsereq, MPI_STATUS_IGNORE); /* Distribute the (request, reply) pairs into a large array for easy * random access without searching. */ int i; for (i = 0; i < output_offset; ++i) { perm_replies_spread[outgoing_perm_requests[i] - perm_displ_for_dest] = outgoing_perm_replies[i]; } /* For those edges that are marked as needing rescanning for this group * of destinations, update any with endpoints that belong to the * current destination. */ int64_t block; for (block = 0; block < rescan_bitmap_size; ++block) { unsigned long val = rescan_bitmap[block]; if (val == 0) continue; int bit; for (bit = 0; bit < ulong_bits; ++bit) { if (!TEST_BIT_SCALAR(val, bit)) continue; int64_t i = block * ulong_bits + bit; int64_t v = result[i]; if (v >= 0 && LOOKUP_PERM_OWNER(v) == dest) { result[i] = perm_replies_spread[v - perm_displ_for_dest] - N - 1; } } } } } #undef SET_BIT #undef TEST_BIT_SCALAR #undef TEST_BIT #undef LOOKUP_PERM_OWNER } free(perm_owner_table); perm_owner_table = NULL; free(perm_owner_cutoff); perm_owner_cutoff = NULL; free(perm_displs); perm_displs = NULL; free(rescan_bitmap); rescan_bitmap = NULL; free(needed_element_bitmap); needed_element_bitmap = NULL; free(num_needed_elements); num_needed_elements = NULL; free(incoming_perm_requests); incoming_perm_requests = NULL; free(incoming_perm_replies); incoming_perm_replies = NULL; free(outgoing_perm_requests); outgoing_perm_requests = NULL; free(outgoing_perm_replies); outgoing_perm_replies = NULL; free(perm_replies_spread); perm_replies_spread = NULL; /* Undo bias of permuted vertices in result */ for (i = 0; i < 2 * nedges; i += 2) { if (result[i] != -1) { int64_t v1 = result[i] + N + 1; int64_t v2 = result[i + 1] + N + 1; assert (v1 >= 0 && v1 < N); assert (v2 >= 0 && v2 < N); /* Sort these since otherwise the directions of the permuted edges would * give away the unscrambled vertex order. */ result[i] = (v1 < v2) ? v1 : v2; result[i + 1] = (v1 < v2) ? v2 : v1; } } free(send_bitmap); send_bitmap = NULL; free(owner_bitmap); owner_bitmap = NULL; } CombBLAS_beta_16_2/graph500-1.2/generator/generator_test_mpi.c000644 000765 000024 00000004321 13212627400 025271 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include #include #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include #include #include "make_graph.h" int main(int argc, char* argv[]) { int log_numverts; int size, rank; unsigned long my_edges; unsigned long global_edges; double start, stop; size_t i; MPI_Init(&argc, &argv); log_numverts = 16; /* In base GRAPHGEN_INITIATOR_SIZE */ if (argc >= 2) log_numverts = atoi(argv[1]); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_rank(MPI_COMM_WORLD, &rank); if (rank == 0) fprintf(stderr, "Graph size is %" PRIu64 " vertices and %" PRIu64 " edges\n", (uint64_t)pow(2., log_numverts), (uint64_t)(pow(2., log_numverts) * 8.)); /* Start of graph generation timing */ MPI_Barrier(MPI_COMM_WORLD); start = MPI_Wtime(); int64_t nedges; int64_t* result; double initiator[] = {.57, .19, .19, .05}; make_graph(log_numverts, 8. * pow(2., log_numverts), 1, 2, initiator, &nedges, &result); MPI_Barrier(MPI_COMM_WORLD); stop = MPI_Wtime(); /* End of graph generation timing */ my_edges = 0; for (i = 0; i < nedges; ++i) { if (result[2 * i] != (uint64_t)(-1)) { ++my_edges; } } free(result); MPI_Reduce(&my_edges, &global_edges, 1, MPI_UNSIGNED_LONG, MPI_SUM, 0, MPI_COMM_WORLD); if (rank == 0) { fprintf(stderr, "%lu edge%s generated and permuted in %fs (%f Medges/s on %d processor%s)\n", global_edges, (global_edges == 1 ? "" : "s"), (stop - start), global_edges / (stop - start) * 1.e-6, size, (size == 1 ? "" : "s")); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/graph500-1.2/generator/scramble_edges.c000644 000765 000024 00000017133 13212627400 024343 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #include "splittable_mrg.h" #include "graph_generator.h" #include "permutation_gen.h" #include "apply_permutation_mpi.h" #include "scramble_edges.h" #include "utils.h" #include #include #include #include #include #ifdef __MTA__ #include #endif #ifdef GRAPH_GENERATOR_MPI #include #endif #ifdef GRAPH_GENERATOR_OMP #include #endif /* This version is for sequential machines, OpenMP, and the XMT. */ void scramble_edges_shared(uint64_t userseed1, uint64_t userseed2, int64_t nedges, int64_t* result /* Input and output array of edges (size = 2 * nedges) */) { mrg_state st; uint_fast32_t seed[5]; int64_t* new_result; int64_t i; int64_t* perm = (int64_t*)xmalloc(nedges * sizeof(int64_t)); make_mrg_seed(userseed1, userseed2, seed); mrg_seed(&st, seed); mrg_skip(&st, 5, 0, 0); /* To make offset different from other PRNG uses */ rand_sort_shared(&st, nedges, perm); new_result = (int64_t*)xmalloc(nedges * 2 * sizeof(int64_t)); #ifdef __MTA__ #pragma mta assert parallel #pragma mta block schedule #endif #ifdef GRAPH_GENERATOR_OMP #pragma omp parallel for #endif for (i = 0; i < nedges; ++i) { int64_t p = perm[i]; new_result[i * 2 + 0] = result[p * 2 + 0]; new_result[i * 2 + 1] = result[p * 2 + 1]; } free(perm); memcpy(result, new_result, nedges * 2 * sizeof(int64_t)); free(new_result); } #ifdef GRAPH_GENERATOR_MPI /* For MPI distributed memory. */ void scramble_edges_mpi(MPI_Comm comm, const uint64_t userseed1, const uint64_t userseed2, const int64_t local_nedges_in, const int64_t* const local_edges_in, int64_t* const local_nedges_out_ptr, int64_t** const local_edges_out_ptr /* Allocated using xmalloc() by scramble_edges_mpi */) { int rank, size; MPI_Comm_rank(comm, &rank); MPI_Comm_size(comm, &size); mrg_state st; uint_fast32_t seed[5]; make_mrg_seed(userseed1, userseed2, seed); mrg_seed(&st, seed); mrg_skip(&st, 5, 0, 0); /* To make offset different from other PRNG uses */ int64_t total_nedges; MPI_Allreduce((void*)&local_nedges_in, &total_nedges, 1, INT64_T_MPI_TYPE, MPI_SUM, comm); int64_t local_nedges_out; /* = local permutation size */ int64_t* local_perm; rand_sort_mpi(comm, &st, total_nedges, &local_nedges_out, &local_perm); *local_nedges_out_ptr = local_nedges_out; /* Gather permutation information and fast owner lookup cache (code in * apply_permutation_mpi.c). */ int64_t* edge_displs = (int64_t*)xmalloc((size + 1) * sizeof(int64_t)); int* edge_owner_table; int64_t* edge_owner_cutoff; int lg_minedgecount; int64_t maxedgecount; gather_block_distribution_info(comm, local_nedges_in, total_nedges, edge_displs, &edge_owner_table, &edge_owner_cutoff, &lg_minedgecount, &maxedgecount); /* Originally from apply_permutation_mpi.c */ #define LOOKUP_EDGE_OWNER(v) \ (edge_owner_table[(v) >> lg_minedgecount] + \ ((v) >= edge_owner_cutoff[(v) >> lg_minedgecount])) /* Apply permutation. Output distribution is same as distribution of * generated edge permutation. */ /* Count number of requests to send to each destination. */ int* send_counts = (int*)xcalloc(size, sizeof(int)); /* Uses zero-init */ int64_t i; for (i = 0; i < local_nedges_out; ++i) { ++send_counts[LOOKUP_EDGE_OWNER(local_perm[i])]; } /* Prefix sum to get displacements. */ int* send_displs = (int*)xmalloc((size + 1) * sizeof(int)); send_displs[0] = 0; for (i = 0; i < size; ++i) { send_displs[i + 1] = send_displs[i] + send_counts[i]; } assert (send_displs[size] == local_nedges_out); /* Put edges into buffer by destination; also keep around index values for * where to write the result. */ int64_t* sendbuf = (int64_t*)xmalloc(local_nedges_out * sizeof(int64_t)); int64_t* reply_loc_buf = (int64_t*)xmalloc(local_nedges_out * sizeof(int64_t)); int* send_offsets = (int*)xmalloc((size + 1) * sizeof(int)); memcpy(send_offsets, send_displs, (size + 1) * sizeof(int)); for (i = 0; i < local_nedges_out; ++i) { int write_index = send_offsets[LOOKUP_EDGE_OWNER(local_perm[i])]; sendbuf[write_index] = local_perm[i]; reply_loc_buf[write_index] = i; ++send_offsets[LOOKUP_EDGE_OWNER(local_perm[i])]; } for (i = 0; i < size; ++i) assert (send_offsets[i] == send_displs[i + 1]); free(send_offsets); send_offsets = NULL; free(local_perm); local_perm = NULL; #undef LOOKUP_EDGE_OWNER free(edge_owner_table); edge_owner_table = NULL; free(edge_owner_cutoff); edge_owner_cutoff = NULL; /* Find out how many requests I will be receiving. */ int* recv_counts = (int*)xmalloc(size * sizeof(int)); MPI_Alltoall(send_counts, 1, MPI_INT, recv_counts, 1, MPI_INT, comm); /* Compute their displacements. */ int* recv_displs = (int*)xmalloc((size + 1) * sizeof(int)); recv_displs[0] = 0; for (i = 0; i < size; ++i) { recv_displs[i + 1] = recv_displs[i] + recv_counts[i]; } /* Make receive and reply buffers. */ int64_t* recvbuf = (int64_t*)xmalloc(recv_displs[size] * sizeof(int64_t)); int64_t* replybuf = (int64_t*)xmalloc(recv_displs[size] * 2 * sizeof(int64_t)); /* Move requests for edges into receive buffer. */ MPI_Alltoallv(sendbuf, send_counts, send_displs, INT64_T_MPI_TYPE, recvbuf, recv_counts, recv_displs, INT64_T_MPI_TYPE, comm); free(sendbuf); sendbuf = NULL; /* Put requested edges into response buffer. */ int64_t my_edge_offset = edge_displs[rank]; for (i = 0; i < recv_displs[size]; ++i) { replybuf[i * 2 + 0] = local_edges_in[(recvbuf[i] - my_edge_offset) * 2 + 0]; replybuf[i * 2 + 1] = local_edges_in[(recvbuf[i] - my_edge_offset) * 2 + 1]; } free(recvbuf); recvbuf = NULL; free(edge_displs); edge_displs = NULL; /* Send replies back. */ int64_t* reply_edges = (int64_t*)xmalloc(local_nedges_out * 2 * sizeof(int64_t)); for (i = 0; i < size; ++i) { /* Sending back two values for each request */ recv_counts[i] *= 2; recv_displs[i] *= 2; send_counts[i] *= 2; send_displs[i] *= 2; } MPI_Alltoallv(replybuf, recv_counts, recv_displs, INT64_T_MPI_TYPE, reply_edges, send_counts, send_displs, INT64_T_MPI_TYPE, comm); free(replybuf); replybuf = NULL; free(recv_counts); recv_counts = NULL; free(recv_displs); recv_displs = NULL; free(send_counts); send_counts = NULL; free(send_displs); send_displs = NULL; /* Make output array of edges. */ int64_t* local_edges_out = (int64_t*)xmalloc(local_nedges_out * 2 * sizeof(int64_t)); *local_edges_out_ptr = local_edges_out; /* Put edges into output array. */ for (i = 0; i < local_nedges_out; ++i) { local_edges_out[reply_loc_buf[i] * 2 + 0] = reply_edges[2 * i + 0]; local_edges_out[reply_loc_buf[i] * 2 + 1] = reply_edges[2 * i + 1]; } free(reply_loc_buf); reply_loc_buf = NULL; free(reply_edges); reply_edges = NULL; } #endif CombBLAS_beta_16_2/graph500-1.2/generator/graph_generator.c000644 000765 000024 00000026616 13212627400 024561 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include #include #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include "splittable_mrg.h" #include "btrd_binomial_distribution.h" #include "graph_generator.h" #define GRAPHGEN_INITIATOR_SIZE2 (GRAPHGEN_INITIATOR_SIZE * GRAPHGEN_INITIATOR_SIZE) typedef struct generator_settings { /* Internal settings */ double initiator[GRAPHGEN_INITIATOR_SIZE2]; int64_t my_first_edge, my_last_edge; int64_t total_nverts; #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* out; /* Start of local (when GRAPHGEN_DISTRIBUTED_MEMORY is defined) or global (when GRAPHGEN_DISTRIBUTED_MEMORY is undefined) output edge list */ #else int64_t* out; /* Start of local (when GRAPHGEN_DISTRIBUTED_MEMORY is defined) or global (when GRAPHGEN_DISTRIBUTED_MEMORY is undefined) output edge list */ #endif } generator_settings; static int generate_nway_bernoulli(const generator_settings* s, mrg_state* st) { double random_number; int j; random_number = mrg_get_double_orig(st); for (j = 0; j + 1 < GRAPHGEN_INITIATOR_SIZE2; ++j) { double ini = s->initiator[j]; if (random_number < ini) { return j; } random_number -= ini; } return GRAPHGEN_INITIATOR_SIZE2 - 1; } static void make_square_counts(int64_t num_edges, mrg_state* st, const generator_settings* s, int64_t* square_counts) { /* fprintf(stderr, "make_square_counts %zu with (%lf, %lf, %lf, %lf)\n", num_edges, a, b, c, d); */ if (num_edges <= 20) { int i; int64_t ii; for (i = 0; i < GRAPHGEN_INITIATOR_SIZE2; ++i) { square_counts[i] = 0; } for (ii = 0; ii < num_edges; ++ii) { /* ++square_counts[generate_nway_bernoulli(s, st)]; -- replaced by code below */ int j; double random_number = mrg_get_double_orig(st); for (j = 0; j < GRAPHGEN_INITIATOR_SIZE2; ++j) { double ini = s->initiator[j]; if (random_number < ini || j == GRAPHGEN_INITIATOR_SIZE2 - 1) { ++square_counts[j]; break; } random_number -= ini; } } } else { int64_t num_edges_left = num_edges; double divisor = 1.; int i; for (i = 0; i < GRAPHGEN_INITIATOR_SIZE2 - 1; ++i) { square_counts[i] = btrd_binomial_distribution(num_edges_left, s->initiator[i] / divisor, st); num_edges_left -= square_counts[i]; divisor -= s->initiator[i]; } square_counts[GRAPHGEN_INITIATOR_SIZE2 - 1] = num_edges_left; } /* fprintf(stderr, "--> (%zu, %zu, %zu, %zu)\n", square_counts[0], square_counts[1], square_counts[2], square_counts[3]); */ } #ifdef GRAPHGEN_MODIFY_PARAMS_AT_EACH_LEVEL static void alter_params(generator_settings* s, const mrg_transition_matrix* trans, mrg_state* st) { double divisor = 0.; for (int i = 0; i < GRAPHGEN_INITIATOR_SIZE2; ++i) { s->initiator[i] *= .95 + .1 * mrg_get_double(trans, st); divisor += s->initiator[i]; } for (int i = 0; i < GRAPHGEN_INITIATOR_SIZE2; ++i) { s->initiator[i] /= divisor; } } #endif static void make_one_edge(int64_t base_src, int64_t base_tgt, int64_t nverts, mrg_state* st, const generator_settings* s, #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* result #else int64_t* result #endif ) { int is_self_loop_to_skip; #ifdef GRAPHGEN_MODIFY_PARAMS_AT_EACH_LEVEL generator_settings my_settings_data = s; const generator_settings* my_settings = &my_settings_data; #else const generator_settings* my_settings = s; #endif while (nverts > 1) { int square = generate_nway_bernoulli(my_settings, st); int src_offset = square / GRAPHGEN_INITIATOR_SIZE; int tgt_offset = square % GRAPHGEN_INITIATOR_SIZE; #ifdef GRAPHGEN_UNDIRECTED assert (base_src <= base_tgt); if (base_src == base_tgt) { /* Clip-and-flip for undirected graph */ if (src_offset > tgt_offset) { int temp = src_offset; src_offset = tgt_offset; tgt_offset = temp; } } #endif /* GRAPHGEN_UNDIRECTED */ nverts /= GRAPHGEN_INITIATOR_SIZE; base_src += nverts * src_offset; base_tgt += nverts * tgt_offset; #ifdef GRAPHGEN_MODIFY_PARAMS_AT_EACH_LEVEL alter_params(my_settings, trans, st); #endif } #ifdef GRAPHGEN_KEEP_SELF_LOOPS is_self_loop_to_skip = 0; #else is_self_loop_to_skip = (base_src == base_tgt); #endif #ifdef GRAPHGEN_KEEP_MULTIPLICITIES result->src = base_src; result->tgt = base_tgt; result->multiplicity = is_self_loop_to_skip ? 0 : 1; #else result[0] = is_self_loop_to_skip ? -1 : base_src; result[1] = is_self_loop_to_skip ? -1 : base_tgt; #endif } static void generate_kronecker_internal( const mrg_state* orig_state, int64_t first_edge_index, int64_t num_edges, int64_t nverts, const generator_settings* s, int64_t base_src, int64_t base_tgt) { mrg_state state = *orig_state; int64_t my_first_edge; int64_t my_last_edge; int is_self_loop_to_skip; int i; mrg_skip(&state, 0, (base_src + s->total_nverts) / nverts, (base_tgt + s->total_nverts) / nverts); my_first_edge = s->my_first_edge; my_last_edge = s->my_last_edge; #ifdef GRAPHGEN_UNDIRECTED assert (base_src <= base_tgt); #endif /* GRAPHGEN_UNDIRECTED */ if (nverts == 1) { assert (num_edges != 0); #ifdef GRAPHGEN_KEEP_SELF_LOOPS is_self_loop_to_skip = 0; #else is_self_loop_to_skip = (base_src == base_tgt); #endif for (i = 0; i < num_edges; ++i) { /* Write all edges, filling all slots except the first with edges marked * as removed duplicates; the complexity of the loop here is to deal with * cases of overflows between nodes (i.e., some of the copies of an edge * on one node and some on another). Also, if the edge is a self-loop * and those are being removed, write -1 (or a multiplicity of 0) instead * so it will be removed. */ if (first_edge_index + i >= my_first_edge && first_edge_index + i < my_last_edge) { #ifdef GRAPHGEN_DISTRIBUTED_MEMORY int64_t write_offset = first_edge_index + i - my_first_edge; #else int64_t write_offset = first_edge_index + i; #endif #ifdef GRAPHGEN_KEEP_DUPLICATES int is_duplicate_to_skip = 0; #else int is_duplicate_to_skip = (i != 0); #endif #ifdef GRAPHGEN_KEEP_MULTIPLICITIES { #ifdef GRAPHGEN_KEEP_DUPLICATES int64_t multiplicity_to_write = 1; /* Write all edges as 1 */ #else int64_t multiplicity_to_write = num_edges; /* Write first edge as num_edges and rest as 0 */ #endif generated_edge* out_loc = &(s->out[write_offset]); out_loc->src = base_src; out_loc->tgt = base_tgt; out_loc->multiplicity = ((!is_duplicate_to_skip && !is_self_loop_to_skip) ? multiplicity_to_write : 0); } #else { int64_t* out_loc = &(s->out[2 * write_offset]); out_loc[0] = ((!is_duplicate_to_skip && !is_self_loop_to_skip) ? base_src : -1); out_loc[1] = ((!is_duplicate_to_skip && !is_self_loop_to_skip) ? base_tgt : -1); } #endif } } } else if (num_edges == 1) { if (first_edge_index >= my_first_edge && first_edge_index < my_last_edge) { #ifdef GRAPHGEN_DISTRIBUTED_MEMORY int64_t write_offset = first_edge_index - my_first_edge; #else int64_t write_offset = first_edge_index; #endif #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* out_loc = &(s->out[write_offset]); #else int64_t* out_loc = &(s->out[2 * write_offset]); #endif make_one_edge(base_src, base_tgt, nverts, &state, s, out_loc); } } else { int64_t new_nverts; #ifdef GRAPHGEN_MODIFY_PARAMS_AT_EACH_LEVEL generator_settings new_settings_data; const generator_settings* new_settings = &new_settings_data; #else const generator_settings* new_settings = s; #endif int64_t fei; int i; int64_t square_counts[GRAPHGEN_INITIATOR_SIZE2]; make_square_counts(num_edges, &state, s, square_counts); #ifdef GRAPHGEN_UNDIRECTED if (base_src == base_tgt) { /* Clip-and-flip for undirected graph */ int i, j; for (i = 0; i < GRAPHGEN_INITIATOR_SIZE; ++i) { for (j = i + 1; j < GRAPHGEN_INITIATOR_SIZE; ++j) { square_counts[i * GRAPHGEN_INITIATOR_SIZE + j] += square_counts[j * GRAPHGEN_INITIATOR_SIZE + i]; square_counts[j * GRAPHGEN_INITIATOR_SIZE + i] = 0; } } } #endif /* GRAPHGEN_UNDIRECTED */ new_nverts = nverts / GRAPHGEN_INITIATOR_SIZE; fei = first_edge_index; for (i = 0; i < GRAPHGEN_INITIATOR_SIZE2; ++i) { if (square_counts[i] != 0) { int lhs_pos = (fei < my_first_edge) ? -1 : (fei < my_last_edge) ? 0 : 1; int rhs_pos = ((fei + square_counts[i]) < my_first_edge) ? -1 : ((fei + square_counts[i]) < my_last_edge) ? 0 : 1; if (lhs_pos == 0 || rhs_pos == 0 || lhs_pos != rhs_pos) { #ifdef GRAPHGEN_MODIFY_PARAMS_AT_EACH_LEVEL new_settings_data = *s; alter_params(&new_settings_data, trans + 1, &subpart_states[i]); #endif generate_kronecker_internal( orig_state, fei, square_counts[i], new_nverts, new_settings, base_src + new_nverts * (i / GRAPHGEN_INITIATOR_SIZE), base_tgt + new_nverts * (i % GRAPHGEN_INITIATOR_SIZE)); } fei += square_counts[i]; } } } } int64_t compute_edge_array_size( int rank, int size, int64_t M) { int64_t rankc = (int64_t)(rank); int64_t sizec = (int64_t)(size); int64_t my_start_edge = rankc * (M / sizec) + (rankc < (M % sizec) ? rankc : (M % sizec)); int64_t my_end_edge = (rankc + 1) * (M / sizec) + (rankc + 1 < (M % sizec) ? rankc + 1 : (M % sizec)); return my_end_edge - my_start_edge; } void generate_kronecker( int rank, int size, const uint_fast32_t seed[5] /* All values in [0, 2^31 - 1), not all zero */, int logN /* In base GRAPHGEN_INITIATOR_SIZE */, int64_t M, const double initiator[GRAPHGEN_INITIATOR_SIZE2], #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* const edges /* Size >= compute_edge_array_size(rank, size, M), must be zero-initialized */ #else int64_t* const edges /* Size >= 2 * compute_edge_array_size(rank, size, M) */ #endif ) { mrg_state state; int64_t my_start_edge = rank * (M / size) + (rank < (M % size) ? rank : (M % size)); int64_t my_end_edge = (rank + 1) * (M / size) + (rank + 1 < (M % size) ? rank + 1 : (M % size)); unsigned int i; generator_settings settings_data; mrg_seed(&state, seed); for (i = 0; i < GRAPHGEN_INITIATOR_SIZE2; ++i) { settings_data.initiator[i] = initiator[i]; } settings_data.my_first_edge = my_start_edge; settings_data.my_last_edge = my_end_edge; settings_data.total_nverts = (int64_t)pow(GRAPHGEN_INITIATOR_SIZE, logN); settings_data.out = edges; generate_kronecker_internal( &state, 0, M, (int64_t)pow(GRAPHGEN_INITIATOR_SIZE, logN), &settings_data, 0, 0); } CombBLAS_beta_16_2/graph500-1.2/generator/Makefile.seq000644 000765 000024 00000001526 13212627400 023466 0ustar00aydinbulucstaff000000 000000 AR = ar RANLIB = ranlib CC = cc CFLAGS = -g -Wall -Drestrict=__restrict__ -O3 -DNDEBUG -ffast-math -DGRAPH_GENERATOR_SEQ # -g -pg # CFLAGS = -g -Wall -Drestrict=__restrict__ LDFLAGS = -g # -g -pg all: libgraph_generator_seq.a generator_test_seq generator_test_seq: generator_test_seq.c libgraph_generator_seq.a $(CC) $(CFLAGS) $(LDFLAGS) -o generator_test_seq generator_test_seq.c -L. -lgraph_generator_seq -lm libgraph_generator_seq.a: btrd_binomial_distribution.o splittable_mrg.o mrg_transitions.o graph_generator.o permutation_gen.o make_graph.o utils.o scramble_edges.o $(AR) cruv libgraph_generator_seq.a btrd_binomial_distribution.o splittable_mrg.o mrg_transitions.o graph_generator.o permutation_gen.o make_graph.o utils.o scramble_edges.o $(RANLIB) libgraph_generator_seq.a clean: -rm -f generator_test_seq libgraph_generator_seq.a *.o CombBLAS_beta_16_2/graph500-1.2/generator/generator_test_seq.c000644 000765 000024 00000003213 13212627400 025273 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include #include #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #include #include #include #include "make_graph.h" inline double get_time() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + tv.tv_usec * 1.e-6; } int main(int argc, char* argv[]) { int log_numverts; double start, time_taken; int64_t nedges; int64_t* result; log_numverts = 16; /* In base GRAPHGEN_INITIATOR_SIZE */ if (argc >= 2) log_numverts = atoi(argv[1]); /* Start of graph generation timing */ start = get_time(); double initiator[] = {.57, .19, .19, .05}; make_graph(log_numverts, 8. * pow(2., log_numverts), 1, 2, initiator, &nedges, &result); time_taken = get_time() - start; /* End of graph generation timing */ fprintf(stderr, "%" PRIu64 " edge%s generated and permuted in %fs (%f Medges/s)\n", nedges, (nedges == 1 ? "" : "s"), time_taken, 1. * nedges / time_taken * 1.e-6); free(result); return 0; } CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/000755 000765 000024 00000000000 13271404146 024211 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/000755 000765 000024 00000000000 13271404146 026177 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/apply_permutation_mpi.h000644 000765 000024 00000003170 13271404146 032772 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef APPLY_PERMUTATION_MPI_H #define APPLY_PERMUTATION_MPI_H #ifdef GRAPH_GENERATOR_MPI #include #include "splittable_mrg.h" #include "graph_generator.h" #include #ifdef __cplusplus extern "C" { #endif /* Internal function to get various information about an uneven block * distribution of some data. */ void gather_block_distribution_info(MPI_Comm comm, const int64_t local_block_size, const int64_t global_block_size, int64_t* block_displs /* size = MPI comm size + 1 */, int** block_owner_table_ptr /* malloc'ed in here */, int64_t** block_owner_cutoff_ptr /* malloc'ed in here */, int* lg_minblocksize_ptr, int64_t* maxblocksize_ptr); /* Internal function to apply a distributed permutation to a distributed set of * edges. */ void apply_permutation_mpi(MPI_Comm comm, const int64_t local_perm_size, const int64_t* const local_vertex_perm, const int64_t N, const int64_t nedges, int64_t* result); #ifdef __cplusplus } #endif #endif /* GRAPH_GENERATOR_MPI */ #endif /* APPLY_PERMUTATION_MPI_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/make_graph.h000644 000765 000024 00000004516 13271404146 030454 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef MAKE_GRAPH_H #define MAKE_GRAPH_H #include #ifdef __cplusplus extern "C" { #endif /* Simplified interface for users; implemented in different ways on different * platforms. */ void make_graph( /* in */ int log_numverts /* log_2 of vertex count */, /* in */ int64_t desired_nedges /* Target number of edges (actual number * will be slightly smaller) */, /* in */ uint64_t userseed1 /* Arbitrary 64-bit seed value */, /* in */ uint64_t userseed2 /* Arbitrary 64-bit seed value */, /* in */ const double initiator[4] /* Kronecker initiator (i.e., R-MAT a, b, * c, d) */, /* out */ int64_t* nedges /* Number of generated edges */, /* out */ int64_t** result /* Array of edges (each pair of elements * is a single edge); pairs with first * element -1 should be ignored; * allocated by make_graph() but must be * freed using free() by user */ ); /* PRNG interface for implementations; takes seed in same format as given by * users, and creates a vector of doubles in a reproducible (and * random-access) way. */ void make_random_numbers( /* in */ int64_t nvalues /* Number of values to generate */, /* in */ uint64_t userseed1 /* Arbitrary 64-bit seed value */, /* in */ uint64_t userseed2 /* Arbitrary 64-bit seed value */, /* in */ int64_t position /* Start index in random number stream */, /* out */ double* result /* Returned array of values */ ); #ifdef __cplusplus } #endif #endif /* MAKE_GRAPH_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/utils.h000644 000765 000024 00000005055 13271404146 027515 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef UTILS_H #define UTILS_H #include #include #include "splittable_mrg.h" #ifdef __MTA__ #include #endif #ifdef __cplusplus extern "C" { #endif void* xmalloc(size_t n); void* xcalloc(size_t n, size_t k); uint_fast64_t random_up_to(mrg_state* st, uint_fast64_t n); void make_mrg_seed(uint64_t userseed1, uint64_t userseed2, uint_fast32_t* seed); /* Compare-and-swap; return 1 if successful or 0 otherwise. */ #ifdef __MTA__ #pragma mta inline static inline int int64_t_cas(volatile int64_t* p, int64_t oldval, int64_t newval) { int64_t val = readfe(p); if (val == oldval) { writeef(p, newval); return 1; } else { writeef(p, val); return 0; } } #elif defined(GRAPH_GENERATOR_MPI) || defined(GRAPH_GENERATOR_SEQ) /* Sequential */ #ifdef _MSC_VER static _inline int int64_t_cas(int64_t* p, int64_t oldval, int64_t newval){ #else static inline int int64_t_cas(int64_t* p, int64_t oldval, int64_t newval) { #endif if (*p == oldval) { *p = newval; return 1; } else { return 0; } } #elif defined(GRAPH_GENERATOR_OMP) #ifndef _MSC_VER /* GCC intrinsic */ static inline int int64_t_cas(volatile int64_t* p, int64_t oldval, int64_t newval) { return __sync_bool_compare_and_swap(p, oldval, newval); } #else static _inline int int64_t_cas(volatile int64_t* p, int64_t oldval, int64_t newval) { if (*p == oldval) { *p = newval; return 1; } else { return 0; } } #endif #else #ifndef _MSC_VER static inline int int64_t_cas(volatile int64_t* p, int64_t oldval, int64_t newval) { if (*p == oldval) { *p = newval; return 1; } else { return 0; } } #else static _inline int int64_t_cas(volatile int64_t* p, int64_t oldval, int64_t newval) { if (*p == oldval) { *p = newval; return 1; } else { return 0; } } #endif #endif #ifdef __cplusplus } #endif #endif /* UTILS_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/scramble_edges.h000644 000765 000024 00000003112 13271404146 031304 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef SCRAMBLE_EDGES_H #define SCRAMBLE_EDGES_H #include #include "splittable_mrg.h" #include "graph_generator.h" #ifdef GRAPH_GENERATOR_MPI #include #endif #ifdef __cplusplus extern "C" { #endif /* This version is for sequential machines, OpenMP, and the XMT. */ void scramble_edges_shared(uint64_t userseed1, uint64_t userseed2, int64_t nedges, int64_t* result /* Input and output array of edges (size = 2 * nedges) */); #ifdef GRAPH_GENERATOR_MPI /* For MPI distributed memory. */ void scramble_edges_mpi(MPI_Comm comm, const uint64_t userseed1, const uint64_t userseed2, const int64_t local_nedges_in, const int64_t* const local_edges_in, int64_t* const local_nedges_out_ptr, int64_t** const local_edges_out_ptr /* Allocated using xmalloc() by scramble_edges_mpi */); #endif #ifdef __cplusplus } #endif #endif /* SCRAMBLE_EDGES_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/graph_generator.h000644 000765 000024 00000007622 13271404146 031526 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef GRAPH_GENERATOR_H #define GRAPH_GENERATOR_H #include #include #include #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include #ifdef __cplusplus extern "C" { #endif /* Settings for user modification ----------------------------------- */ #ifndef MODIFY_PARAMS_AT_EACH_LEVEL /* Add noise into each subblock's Kronecker parameters (as is done in SSCA #2, * but here the same mutation is used for all edges within a block) */ /* #define MODIFY_PARAMS_AT_EACH_LEVEL */ #endif #define INT64_T_MPI_TYPE MPI_LONG_LONG /* Should be MPI_INT64_T */ /* End of user settings ----------------------------------- */ /* Graph generator #define settings -- set with benchmark configuration, but * should not be changed by individual runners of the benchmark (other than * GRAPHGEN_DISTRIBUTED_MEMORY). */ #ifndef GRAPHGEN_SETTINGS_DEFINED #define GRAPHGEN_SETTINGS_DEFINED /* Define if output array is local to each rank (and the output array pointer * points to the beginning of the local part) instead of being global (i.e., * having the output array pointer pointing to the same place on all ranks); in * either case, the output array is still broken into independent pieces). */ /* #define GRAPHGEN_DISTRIBUTED_MEMORY -- Set this in your Makefile */ /* Define if Kronecker parameters should be modified within each sub-block of * the adjacency matrix (like SSCA #2 does). */ /* #define GRAPHGEN_MODIFY_PARAMS_AT_EACH_LEVEL */ /* Size of Kronecker initiator matrix. */ #define GRAPHGEN_INITIATOR_SIZE 2 /* Define to clip edges to one half of adjacency matrix to create undirected * graphs. */ #define GRAPHGEN_UNDIRECTED /* Define to return graph edges in a struct that contains multiplicities, * rather than just as an array of endpoints with duplicates and self-loops * removed. */ /* #define GRAPHGEN_KEEP_MULTIPLICITIES */ /* Define to keep self-loops in output array rather than marking them as unused * slots. */ #define GRAPHGEN_KEEP_SELF_LOOPS /* Define to keep duplicate edges in output array rather than marking them as * unused slots. */ #define GRAPHGEN_KEEP_DUPLICATES #endif /* GRAPHGEN_SETTINGS_DEFINED */ #ifdef GRAPHGEN_KEEP_MULTIPLICITIES typedef struct generated_edge { int64_t src; int64_t tgt; int64_t multiplicity; } generated_edge; #endif int64_t compute_edge_array_size( int rank, int size, int64_t M); void generate_kronecker( int rank, int size, const uint_fast32_t seed[5] /* All values in [0, 2^31 - 1) */, int logN /* In base initiator_size */, int64_t M, const double initiator[ /* initiator_size * initiator_size */ ], #ifdef GRAPHGEN_KEEP_MULTIPLICITIES generated_edge* const edges /* Size >= compute_edge_array_size(rank, size, M), must be zero-initialized; points to beginning of local chunk when output_array_is_local is 1 and beginning of global array when output_array_is_local is 0 */ #else int64_t* const edges /* Size >= 2 * compute_edge_array_size(rank, size, M); two endpoints per edge (= -1 when slot is unused); points to beginning of local chunk when output_array_is_local is 1 and beginning of global array when output_array_is_local is 0 */ #endif ); #ifdef __cplusplus } #endif #endif /* GRAPH_GENERATOR_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/mod_arith_xmt.h000644 000765 000024 00000011554 13271404146 031214 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef MOD_ARITH_XMT_H #define MOD_ARITH_XMT_H #include #include #include /* Various modular arithmetic operations for modulus 2^31-1 (0x7FFFFFFF). * These may need to be tweaked to get acceptable performance on some platforms * (especially ones without conditional moves). */ /* MTA code is based on description in * http://www.hackersdelight.org/divcMore.pdf and experimentation based on * that. */ #pragma mta inline static inline uint_fast32_t mod_add(uint_fast32_t a, uint_fast32_t b) { uint_fast32_t x; assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); x = a + b; /* x <= 0xFFFFFFFC */ return (x + MTA_UNS_ADD_MUL_UPPER(0x0000000200000003, 0x0000000200000004, x)) & 0x7FFFFFFF; } #pragma mta inline static inline uint_fast32_t mod_mul(uint_fast32_t a, uint_fast32_t b) { uint_fast64_t temp; uint_fast32_t temp2; assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); temp = MTA_INT_ADD_MUL(0, a, b); return (temp + MTA_UNS_ADD_MUL_UPPER(0x0000000200000003, 0x0000000200000004, temp)) & 0x7FFFFFFF; } #pragma mta inline static inline uint_fast32_t mod_mac(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b) { uint_fast64_t temp; uint_fast32_t temp2; assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); temp = MTA_INT_ADD_MUL(sum, a, b); return (temp + MTA_UNS_ADD_MUL_UPPER(0x0000000200000003, 0x0000000200000004, temp)) & 0x7FFFFFFF; } #pragma mta inline static inline uint_fast32_t mod_mac2(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d) { uint_fast64_t temp; assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); temp = MTA_INT_ADD_MUL(MTA_INT_ADD_MUL(sum, a, b), c, d); return (temp + MTA_UNS_ADD_MUL_UPPER(0x0000000200000003, 0x0000000200000004, temp)) & 0x7FFFFFFF; } #pragma mta inline static inline uint_fast32_t mod_mac3(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d, uint_fast32_t e, uint_fast32_t f) { uint_fast64_t temp; assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); assert (e <= 0x7FFFFFFE); assert (f <= 0x7FFFFFFE); temp = MTA_INT_ADD_MUL(MTA_INT_ADD_MUL(MTA_INT_ADD_MUL(sum, a, b), c, d), e, f); return (temp + MTA_UNS_ADD_MUL_UPPER(0x0000000200000003, 0x0000000200000004, temp)) & 0x7FFFFFFF; } #pragma mta inline static inline uint_fast32_t mod_mac4(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d, uint_fast32_t e, uint_fast32_t f, uint_fast32_t g, uint_fast32_t h) { uint_fast64_t temp; assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); assert (e <= 0x7FFFFFFE); assert (f <= 0x7FFFFFFE); assert (g <= 0x7FFFFFFE); assert (h <= 0x7FFFFFFE); temp = MTA_INT_ADD_MUL(MTA_INT_ADD_MUL(MTA_INT_ADD_MUL(MTA_INT_ADD_MUL(sum, a, b), c, d), e, f), g, h); return (temp + MTA_UNS_ADD_MUL_UPPER(0x0000000200000003, 0x0000000200000004, temp)) & 0x7FFFFFFF; } static inline uint_fast64_t mod_down(uint_fast64_t x); #pragma mta inline static inline uint_fast64_t mod_down_fast(uint_fast64_t x) { return (x >> 31) + (x & 0x7FFFFFFF); } #pragma mta inline static inline uint_fast64_t mod_down(uint_fast64_t x) { return ((x + MTA_UNS_ADD_MUL_UPPER(0x0000000200000003, 0x0000000200000004, x)) & 0x7FFFFFFF); // x = mod_down_fast(mod_down_fast(x)); // x = (x + (x >= 0x7FFFFFFF)) & 0x7FFFFFFF; // return x; } /* The two constants x and y are special cases because they are easier to * multiply by on 32-bit systems. They are used as multipliers in the random * number generator. The techniques for fast multiplication by these * particular values are in L'Ecuyer's papers; we don't use them yet. */ #pragma mta inline static inline uint_fast32_t mod_mul_x(uint_fast32_t a) { return mod_mul(a, 107374182); } #pragma mta inline static inline uint_fast32_t mod_mul_y(uint_fast32_t a) { return mod_mul(a, 104480); } #pragma mta inline static inline uint_fast32_t mod_mac_y(uint_fast32_t sum, uint_fast32_t a) { return mod_mac(sum, a, 104480); } #endif /* MOD_ARITH_XMT_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/mod_arith.h000644 000765 000024 00000002356 13271404146 030324 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef MOD_ARITH_H #define MOD_ARITH_H /* Various modular arithmetic operations for modulus 2^31-1 (0x7FFFFFFF). * These may need to be tweaked to get acceptable performance on some platforms * (especially ones without conditional moves). */ /* This code is now just a dispatcher that chooses the right header file to use * per-platform. */ /* FIXME: fill this in automatically */ #ifndef FAST_64BIT_ARITHMETIC #define FAST_64BIT_ARITHMETIC #endif #ifdef __MTA__ #include "mod_arith_xmt.h" #else #ifdef FAST_64BIT_ARITHMETIC #include "mod_arith_64bit.h" #else #include "mod_arith_32bit.h" #endif #endif #endif /* MOD_ARITH_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/mod_arith_64bit.h000644 000765 000024 00000010016 13271404146 031324 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef MOD_ARITH_64BIT_H #define MOD_ARITH_64BIT_H #include #include /* Various modular arithmetic operations for modulus 2^31-1 (0x7FFFFFFF). * These may need to be tweaked to get acceptable performance on some platforms * (especially ones without conditional moves). */ static inline uint_fast32_t mod_add(uint_fast32_t a, uint_fast32_t b) { uint_fast32_t x; assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); #if 0 return (a + b) % 0x7FFFFFFF; #else x = a + b; /* x <= 0xFFFFFFFC */ x = (x >= 0x7FFFFFFF) ? (x - 0x7FFFFFFF) : x; return x; #endif } static inline uint_fast32_t mod_mul(uint_fast32_t a, uint_fast32_t b) { uint_fast64_t temp; uint_fast32_t temp2; assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); #if 0 return (uint_fast32_t)((uint_fast64_t)a * b % 0x7FFFFFFF); #else temp = (uint_fast64_t)a * b; /* temp <= 0x3FFFFFFE00000004 */ temp2 = (uint_fast32_t)(temp & 0x7FFFFFFF) + (uint_fast32_t)(temp >> 31); /* temp2 <= 0xFFFFFFFB */ return (temp2 >= 0x7FFFFFFF) ? (temp2 - 0x7FFFFFFF) : temp2; #endif } static inline uint_fast32_t mod_mac(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b) { uint_fast64_t temp; uint_fast32_t temp2; assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); #if 0 return (uint_fast32_t)(((uint_fast64_t)a * b + sum) % 0x7FFFFFFF); #else temp = (uint_fast64_t)a * b + sum; /* temp <= 0x3FFFFFFE80000002 */ temp2 = (uint_fast32_t)(temp & 0x7FFFFFFF) + (uint_fast32_t)(temp >> 31); /* temp2 <= 0xFFFFFFFC */ return (temp2 >= 0x7FFFFFFF) ? (temp2 - 0x7FFFFFFF) : temp2; #endif } static inline uint_fast32_t mod_mac2(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d) { assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); return mod_mac(mod_mac(sum, a, b), c, d); } static inline uint_fast32_t mod_mac3(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d, uint_fast32_t e, uint_fast32_t f) { assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); assert (e <= 0x7FFFFFFE); assert (f <= 0x7FFFFFFE); return mod_mac2(mod_mac(sum, a, b), c, d, e, f); } static inline uint_fast32_t mod_mac4(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d, uint_fast32_t e, uint_fast32_t f, uint_fast32_t g, uint_fast32_t h) { assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); assert (e <= 0x7FFFFFFE); assert (f <= 0x7FFFFFFE); assert (g <= 0x7FFFFFFE); assert (h <= 0x7FFFFFFE); return mod_mac2(mod_mac2(sum, a, b, c, d), e, f, g, h); } /* The two constants x and y are special cases because they are easier to * multiply by on 32-bit systems. They are used as multipliers in the random * number generator. The techniques for fast multiplication by these * particular values are in L'Ecuyer's papers; we don't use them yet. */ static inline uint_fast32_t mod_mul_x(uint_fast32_t a) { return mod_mul(a, 107374182); } static inline uint_fast32_t mod_mul_y(uint_fast32_t a) { return mod_mul(a, 104480); } static inline uint_fast32_t mod_mac_y(uint_fast32_t sum, uint_fast32_t a) { return mod_mac(sum, a, 104480); } #endif /* MOD_ARITH_64BIT_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/mod_arith_32bit.h000644 000765 000024 00000010072 13271404146 031321 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef MOD_ARITH_32BIT_H #define MOD_ARITH_32BIT_H #include #include /* Various modular arithmetic operations for modulus 2^31-1 (0x7FFFFFFF). * These may need to be tweaked to get acceptable performance on some platforms * (especially ones without conditional moves). */ static inline uint_fast32_t mod_add(uint_fast32_t a, uint_fast32_t b) { uint_fast32_t x; assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); #if 0 return (a + b) % 0x7FFFFFFF; #else x = a + b; /* x <= 0xFFFFFFFC */ x = (x >= 0x7FFFFFFF) ? (x - 0x7FFFFFFF) : x; return x; #endif } static inline uint_fast32_t mod_mul(uint_fast32_t a, uint_fast32_t b) { uint_fast64_t temp; uint_fast32_t temp2; assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); #if 0 return (uint_fast32_t)((uint_fast64_t)a * b % 0x7FFFFFFF); #else temp = (uint_fast64_t)a * b; /* temp <= 0x3FFFFFFE00000004 */ temp2 = (uint_fast32_t)(temp & 0x7FFFFFFF) + (uint_fast32_t)(temp >> 31); /* temp2 <= 0xFFFFFFFB */ return (temp2 >= 0x7FFFFFFF) ? (temp2 - 0x7FFFFFFF) : temp2; #endif } static inline uint_fast32_t mod_mac(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b) { uint_fast64_t temp; uint_fast32_t temp2; assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); #if 0 return (uint_fast32_t)(((uint_fast64_t)a * b + sum) % 0x7FFFFFFF); #else temp = (uint_fast64_t)a * b + sum; /* temp <= 0x3FFFFFFE80000002 */ temp2 = (uint_fast32_t)(temp & 0x7FFFFFFF) + (uint_fast32_t)(temp >> 31); /* temp2 <= 0xFFFFFFFC */ return (temp2 >= 0x7FFFFFFF) ? (temp2 - 0x7FFFFFFF) : temp2; #endif } static inline uint_fast32_t mod_mac2(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d) { assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); return mod_mac(mod_mac(sum, a, b), c, d); } static inline uint_fast32_t mod_mac3(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d, uint_fast32_t e, uint_fast32_t f) { uint_fast64_t temp; assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); assert (e <= 0x7FFFFFFE); assert (f <= 0x7FFFFFFE); return mod_mac2(mod_mac(sum, a, b), c, d, e, f); } static inline uint_fast32_t mod_mac4(uint_fast32_t sum, uint_fast32_t a, uint_fast32_t b, uint_fast32_t c, uint_fast32_t d, uint_fast32_t e, uint_fast32_t f, uint_fast32_t g, uint_fast32_t h) { uint_fast64_t temp; assert (sum <= 0x7FFFFFFE); assert (a <= 0x7FFFFFFE); assert (b <= 0x7FFFFFFE); assert (c <= 0x7FFFFFFE); assert (d <= 0x7FFFFFFE); assert (e <= 0x7FFFFFFE); assert (f <= 0x7FFFFFFE); assert (g <= 0x7FFFFFFE); assert (h <= 0x7FFFFFFE); return mod_mac2(mod_mac2(sum, a, b, c, d), e, f, g, h); } /* The two constants x and y are special cases because they are easier to * multiply by on 32-bit systems. They are used as multipliers in the random * number generator. The techniques for fast multiplication by these * particular values are in L'Ecuyer's papers; we don't use them yet. */ static inline uint_fast32_t mod_mul_x(uint_fast32_t a) { return mod_mul(a, 107374182); } static inline uint_fast32_t mod_mul_y(uint_fast32_t a) { return mod_mul(a, 104480); } static inline uint_fast32_t mod_mac_y(uint_fast32_t sum, uint_fast32_t a) { return mod_mac(sum, a, 104480); } #endif /* MOD_ARITH_32BIT_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/btrd_binomial_distribution.h000644 000765 000024 00000002161 13271404146 033754 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2009-2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef BTRD_BINOMIAL_H #define BTRD_BINOMIAL_H /* BTRD algorithm from pages 6--7 of "The Generation of Binomial Random */ /* Variates" (Wolfgang Hoermann) -- */ /* http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.47.8407 */ #include #include #include "splittable_mrg.h" #ifdef __cplusplus extern "C" { #endif size_t btrd_binomial_distribution(size_t n, double p, mrg_state* state); #ifdef __cplusplus } #endif #endif /* BTRD_BINOMIAL_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/splittable_mrg.h000644 000765 000024 00000006321 13271404146 031362 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef SPLITTABLE_MRG_H #define SPLITTABLE_MRG_H #include /* Multiple recursive generator from L'Ecuyer, P., Blouin, F., and */ /* Couture, R. 1993. A search for good multiple recursive random number */ /* generators. ACM Trans. Model. Comput. Simul. 3, 2 (Apr. 1993), 87-98. */ /* DOI= http://doi.acm.org/10.1145/169702.169698 -- particular generator */ /* used is from table 3, entry for m = 2^31 - 1, k = 5 (same generator */ /* is used in GNU Scientific Library). */ /* See notes at top of splittable_mrg.c for information on this */ /* implementation. */ #ifdef __cplusplus extern "C" { #endif typedef struct mrg_transition_matrix { uint_fast32_t s, t, u, v, w; /* Cache for other parts of matrix (see mrg_update_cache function) */ uint_fast32_t a, b, c, d; } mrg_transition_matrix; typedef struct mrg_state { uint_fast32_t z1, z2, z3, z4, z5; } mrg_state; /* Returns integer value in [0, 2^31-1) */ uint_fast32_t mrg_get_uint(const mrg_transition_matrix* mat, mrg_state* state); /* Returns real value in [0, 1) */ double mrg_get_double(const mrg_transition_matrix* mat, mrg_state* state); /* Returns integer value in [0, 2^31-1) using original transition matrix */ uint_fast32_t mrg_get_uint_orig(mrg_state* state); /* Returns real value in [0, 1) using original transition matrix */ double mrg_get_double_orig(mrg_state* state); void mrg_init(mrg_transition_matrix* tm, mrg_state* st); void mrg_seed(mrg_state* st, const uint_fast32_t seed[5]); /* Split a transition matrix; the result of this function is pre-cached so it * does not need to be called for individual splits of the PRNG state. */ void mrg_split_matrix(const mrg_transition_matrix* tm_in, mrg_transition_matrix* tm_out, unsigned int n); /* The variable st_out should be an array of length n; all other parameters are * single elements. * * The state st_in should not be used for random number generation after this * function is called. */ void mrg_split_state(const mrg_transition_matrix* tm_in, const mrg_state* st_in, mrg_state* st_out, unsigned int n); /* Skip the PRNG ahead _exponent_ steps. This code treats the exponent as a * 192-bit word, even though the PRNG period is less than that. */ void mrg_skip(mrg_state* state, uint_least64_t exponent_high, uint_least64_t exponent_middle, uint_least64_t exponent_low); #ifdef __cplusplus } #endif #endif /* SPLITTABLE_MRG_H */ CombBLAS_beta_16_2/graph500-1.2/generator/include/graph500/generator/permutation_gen.h000644 000765 000024 00000002421 13271404146 031547 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef PERMUTATION_GEN_H #define PERMUTATION_GEN_H #include #include "splittable_mrg.h" #include "graph_generator.h" #ifdef GRAPH_GENERATOR_MPI #include #endif /* This version is for sequential machines and the XMT. */ void rand_sort_shared(mrg_state* st, int64_t n, int64_t* result /* Array of size n */); #ifdef GRAPH_GENERATOR_MPI /* For MPI distributed memory. */ void rand_sort_mpi(MPI_Comm comm, mrg_state* st, int64_t n, int64_t* result_size_ptr, int64_t** result_ptr /* Allocated using malloc() by rand_sort_mpi(), must be free()d by user */); #endif #endif /* PERMUTATION_GEN_H */ CombBLAS_beta_16_2/graph500-1.2/xmt-csr/xmt-csr.c000644 000765 000024 00000011032 13212627400 022400 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #define _FILE_OFFSET_BITS 64 #define _THREAD_SAFE #include #include #include #include #include #include "../compat.h" #include "../graph500.h" #include "../xalloc.h" #define MINVECT_SIZE 2 static int64_t maxvtx, nv, sz; static int64_t * restrict xoff; /* Length 2*nv+2 */ static int64_t * restrict xadjstore; /* Length MINVECT_SIZE + (xoff[nv] == nedge) */ static int64_t * restrict xadj; static void find_nv (const int64_t * restrict IJ, const int64_t nedge) { int64_t k, tmaxvtx = -1; maxvtx = -1; for (k = 0; k < 2*nedge; ++k) if (IJ[k] > tmaxvtx) tmaxvtx = IJ[k]; k = readfe (&maxvtx); if (tmaxvtx > k) k = tmaxvtx; writeef (&maxvtx, k); nv = 1+maxvtx; } static int alloc_graph (int64_t nedge) { sz = (2*nv+2) * sizeof (*xoff); xoff = xmalloc_large (sz); if (!xoff) return -1; return 0; } static void free_graph (void) { xfree_large (xadjstore); xfree_large (xoff); } #define XOFF(k) (xoff[2*(k)]) #define XENDOFF(k) (xoff[1+2*(k)]) static int setup_deg_off (const int64_t * restrict IJ, int64_t nedge) { int64_t k, accum; for (k = 0; k < 2*nv+2; ++k) xoff[k] = 0; MTA("mta assert nodep") MTA("mta use 100 streams") for (k = 0; k < 2*nedge; k+=2) if (IJ[k] != IJ[k+1]) { /* Skip self-edges. */ int_fetch_add (&XOFF(IJ[k]), 1); int_fetch_add (&XOFF(IJ[k+1]), 1); } accum = 0; MTA("mta use 100 streams") for (k = 0; k < nv; ++k) { int64_t tmp = XOFF(k); if (tmp < MINVECT_SIZE) tmp = MINVECT_SIZE; XOFF(k) = accum; accum += tmp; } XOFF(nv) = accum; MTA("mta use 100 streams") for (k = 0; k < nv; ++k) XENDOFF(k) = XOFF(k); if (!(xadjstore = malloc ((accum + MINVECT_SIZE) * sizeof (*xadjstore)))) return -1; xadj = &xadjstore[MINVECT_SIZE]; /* Cheat and permit xadj[-1] to work. */ for (k = 0; k < accum + MINVECT_SIZE; ++k) xadjstore[k] = -1; return 0; } static void scatter_edge (const int64_t i, const int64_t j) { int64_t where; where = int_fetch_add (&XENDOFF(i), 1); xadj[where] = j; } static int i64cmp (const void *a, const void *b) { const int64_t ia = *(const int64_t*)a; const int64_t ib = *(const int64_t*)b; if (ia < ib) return -1; if (ia > ib) return 1; return 0; } MTA("mta no inline") static void pack_edges (void) { int64_t v; MTA("mta assert parallel") MTA("mta use 100 streams") for (v = 0; v < nv; ++v) { int64_t kcur, k; if (XOFF(v)+1 > XENDOFF(v)) { qsort (&xadj[XOFF(v)], XENDOFF(v)-XOFF(v), sizeof(*xadj), i64cmp); kcur = XOFF(v); MTA("mta loop serial") for (k = XOFF(v)+1; k < XENDOFF(v); ++k) if (xadj[k] != xadj[kcur]) xadj[1 + int_fetch_add (&kcur, 1)] = xadj[k]; ++kcur; for (k = kcur; k < XENDOFF(v); ++k) xadj[k] = -1; XENDOFF(v) = kcur; } } } static void gather_edges (const int64_t * restrict IJ, int64_t nedge) { int64_t k; MTA("mta assert nodep") MTA("mta use 100 streams") for (k = 0; k < 2*nedge; k += 2) if (IJ[k] != IJ[k+1]) { scatter_edge (IJ[k], IJ[k+1]); scatter_edge (IJ[k+1], IJ[k]); } pack_edges (); } int create_graph_from_edgelist (int64_t *IJ, int64_t nedge) { find_nv (IJ, nedge); if (alloc_graph (nedge)) return -1; if (setup_deg_off (IJ, nedge)) { xfree_large (xoff); return -1; } gather_edges (IJ, nedge); return 0; } int make_bfs_tree (int64_t *bfs_tree_out, int64_t *max_vtx_out, int64_t srcvtx) { int64_t * restrict bfs_tree = bfs_tree_out; int err = 0; int64_t * restrict vlist = NULL; int64_t k1, k2; *max_vtx_out = maxvtx; vlist = xmalloc_large (nv * sizeof (vlist)); if (!vlist) return -1; for (k1 = 0; k1 < nv; ++k1) bfs_tree[k1] = -1; vlist[0] = srcvtx; bfs_tree[srcvtx] = srcvtx; k1 = 0; k2 = 1; while (k1 != k2) { const int64_t oldk2 = k2; int64_t k; MTA("mta assert nodep") MTA("mta use 100 streams") MTA("mta interleave schedule") for (k = k1; k < oldk2; ++k) { const int64_t v = vlist[k]; const int64_t veo = XENDOFF(v); int64_t vo; MTA("mta loop serial") for (vo = XOFF(v); vo < veo; ++vo) { const int64_t j = xadj[vo]; if (bfs_tree[j] == -1) { int64_t t = readfe (&bfs_tree[j]); if (t == -1) { t = v; vlist[int_fetch_add (&k2, 1)] = j; } writeef (&bfs_tree[j], t); } } } k1 = oldk2; } xfree_large (vlist); return err; } void destroy_graph (void) { free_graph (); } CombBLAS_beta_16_2/graph500-1.2/xmt-csr-local/xmt-csr-local.c000644 000765 000024 00000011602 13212627400 024563 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #define _FILE_OFFSET_BITS 64 #define _THREAD_SAFE #include #include #include #include #include #include "../compat.h" #include "../graph500.h" #include "../xalloc.h" #define MINVECT_SIZE 2 static int64_t maxvtx, nv, sz; static int64_t * restrict xoff; /* Length 2*nv+2 */ static int64_t * restrict xadjstore; /* Length MINVECT_SIZE + (xoff[nv] == nedge) */ static int64_t * restrict xadj; static void find_nv (const int64_t * restrict IJ, const int64_t nedge) { int64_t k, tmaxvtx = -1; maxvtx = -1; for (k = 0; k < 2*nedge; ++k) if (IJ[k] > tmaxvtx) tmaxvtx = IJ[k]; k = readfe (&maxvtx); if (tmaxvtx > k) k = tmaxvtx; writeef (&maxvtx, k); nv = 1+maxvtx; } static int alloc_graph (int64_t nedge) { sz = (2*nv+2) * sizeof (*xoff); xoff = xmalloc_large (sz); if (!xoff) return -1; return 0; } static void free_graph (void) { xfree_large (xadjstore); xfree_large (xoff); } #define XOFF(k) (xoff[2*(k)]) #define XENDOFF(k) (xoff[1+2*(k)]) static int setup_deg_off (const int64_t * restrict IJ, int64_t nedge) { int64_t k, accum; for (k = 0; k < 2*nv+2; ++k) xoff[k] = 0; MTA("mta assert nodep") MTA("mta use 100 streams") for (k = 0; k < 2*nedge; k+=2) if (IJ[k] != IJ[k+1]) { /* Skip self-edges. */ int_fetch_add (&XOFF(IJ[k]), 1); int_fetch_add (&XOFF(IJ[k+1]), 1); } accum = 0; MTA("mta use 100 streams") for (k = 0; k < nv; ++k) { int64_t tmp = XOFF(k); if (tmp < MINVECT_SIZE) tmp = MINVECT_SIZE; XOFF(k) = accum; accum += tmp; } XOFF(nv) = accum; MTA("mta use 100 streams") for (k = 0; k < nv; ++k) XENDOFF(k) = XOFF(k); if (!(xadjstore = malloc ((accum + MINVECT_SIZE) * sizeof (*xadjstore)))) return -1; xadj = &xadjstore[MINVECT_SIZE]; /* Cheat and permit xadj[-1] to work. */ for (k = 0; k < accum + MINVECT_SIZE; ++k) xadjstore[k] = -1; return 0; } static void scatter_edge (const int64_t i, const int64_t j) { int64_t where; where = int_fetch_add (&XENDOFF(i), 1); xadj[where] = j; } static int i64cmp (const void *a, const void *b) { const int64_t ia = *(const int64_t*)a; const int64_t ib = *(const int64_t*)b; if (ia < ib) return -1; if (ia > ib) return 1; return 0; } MTA("mta no inline") static void pack_edges (void) { int64_t v; MTA("mta assert parallel") MTA("mta use 100 streams") for (v = 0; v < nv; ++v) { int64_t kcur, k; if (XOFF(v)+1 > XENDOFF(v)) { qsort (&xadj[XOFF(v)], XENDOFF(v)-XOFF(v), sizeof(*xadj), i64cmp); kcur = XOFF(v); MTA("mta loop serial") for (k = XOFF(v)+1; k < XENDOFF(v); ++k) if (xadj[k] != xadj[kcur]) xadj[1 + int_fetch_add (&kcur, 1)] = xadj[k]; ++kcur; for (k = kcur; k < XENDOFF(v); ++k) xadj[k] = -1; XENDOFF(v) = kcur; } } } static void gather_edges (const int64_t * restrict IJ, int64_t nedge) { int64_t k; MTA("mta assert nodep") MTA("mta use 100 streams") for (k = 0; k < 2*nedge; k += 2) if (IJ[k] != IJ[k+1]) { scatter_edge (IJ[k], IJ[k+1]); scatter_edge (IJ[k+1], IJ[k]); } pack_edges (); } int create_graph_from_edgelist (int64_t *IJ, int64_t nedge) { find_nv (IJ, nedge); if (alloc_graph (nedge)) return -1; if (setup_deg_off (IJ, nedge)) { xfree_large (xoff); return -1; } gather_edges (IJ, nedge); return 0; } int make_bfs_tree (int64_t *bfs_tree_out, int64_t *max_vtx_out, int64_t srcvtx) { int64_t * restrict bfs_tree = bfs_tree_out; int err = 0; int64_t * restrict vlist = NULL; int64_t k1, k2; *max_vtx_out = maxvtx; vlist = xmalloc_large (nv * sizeof (vlist)); if (!vlist) return -1; for (k1 = 0; k1 < nv; ++k1) bfs_tree[k1] = -1; vlist[0] = srcvtx; bfs_tree[srcvtx] = srcvtx; k1 = 0; k2 = 1; while (k1 != k2) { const int64_t oldk2 = k2; int64_t k; MTA("mta assert nodep") MTA("mta use 100 streams") MTA("mta interleave schedule") for (k = k1; k < oldk2; ++k) { const int64_t v = vlist[k]; const int64_t veo = XENDOFF(v); #define NPACK 64 int kpack = 0; int64_t vpack[NPACK]; int64_t vo; MTA("mta loop serial") for (vo = XOFF(v); vo < veo; ++vo) { const int64_t j = xadj[vo]; if (bfs_tree[j] == -1) { int64_t t = readfe (&bfs_tree[j]); if (t == -1) { t = v; vpack[kpack++] = j; if (kpack == NPACK) { int64_t dstk = int_fetch_add (&k2, NPACK), k; for (k = 0; k < NPACK; ++k) vlist[dstk + k] = vpack[k]; kpack = 0; } } writeef (&bfs_tree[j], t); } } if (kpack) { int64_t dstk = int_fetch_add (&k2, kpack); int k; for (k = 0; k < kpack; ++k) vlist[dstk + k] = vpack[k]; } } k1 = oldk2; } xfree_large (vlist); return err; } void destroy_graph (void) { free_graph (); } CombBLAS_beta_16_2/graph500-1.2/mpi/bfs_simple.c000644 000765 000024 00000021162 13212627400 022323 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include "common.h" #include #include #include #include #include #include #include #include /* This version is the traditional level-synchronized BFS using two queues. A * bitmap is used to indicate which vertices have been visited. Messages are * sent and processed asynchronously throughout the code to hopefully overlap * communication with computation. */ void run_mpi_bfs(const csr_graph* const g, int64_t root, int64_t* pred, int64_t* nvisited) { const size_t nlocalverts = g->nlocalverts; int64_t nvisited_local = 0; /* Set up the queues. */ int64_t* oldq = (int64_t*)xmalloc(nlocalverts * sizeof(int64_t)); int64_t* newq = (int64_t*)xmalloc(nlocalverts * sizeof(int64_t)); size_t oldq_count = 0; size_t newq_count = 0; /* Set up the visited bitmap. */ const int ulong_bits = sizeof(unsigned long) * CHAR_BIT; int64_t visited_size = (nlocalverts + ulong_bits - 1) / ulong_bits; unsigned long* visited = (unsigned long*)xcalloc(visited_size, sizeof(unsigned long)); /* Uses zero-init */ #define SET_VISITED(v) do {visited[VERTEX_LOCAL((v)) / ulong_bits] |= (1UL << (VERTEX_LOCAL((v)) % ulong_bits));} while (0) #define TEST_VISITED(v) ((visited[VERTEX_LOCAL((v)) / ulong_bits] & (1UL << (VERTEX_LOCAL((v)) % ulong_bits))) != 0) /* Set up buffers for message coalescing, MPI requests, etc. for * communication. */ const int coalescing_size = 256; int64_t* outgoing = (int64_t*)xMPI_Alloc_mem(coalescing_size * size * 2 * sizeof(int64_t)); size_t* outgoing_counts = (size_t*)xmalloc(size * sizeof(size_t)) /* 2x actual count */; MPI_Request* outgoing_reqs = (MPI_Request*)xmalloc(size * sizeof(MPI_Request)); int* outgoing_reqs_active = (int*)xcalloc(size, sizeof(int)); /* Uses zero-init */ int64_t* recvbuf = (int64_t*)xMPI_Alloc_mem(coalescing_size * 2 * sizeof(int64_t)); MPI_Request recvreq; int recvreq_active = 0; /* Termination counter for each level: this variable counts the number of * ranks that have said that they are done sending to me in the current * level. This rank can stop listening for new messages when it reaches * size. */ int num_ranks_done; /* Set all vertices to "not visited." */ {size_t i; for (i = 0; i < nlocalverts; ++i) pred[i] = -1;} /* Mark the root and put it into the queue. */ if (VERTEX_OWNER(root) == rank) { SET_VISITED(root); pred[VERTEX_LOCAL(root)] = root; oldq[oldq_count++] = root; } #define CHECK_MPI_REQS \ /* Check all MPI requests and handle any that have completed. */ \ do { \ /* Test for incoming vertices to put onto the queue. */ \ while (recvreq_active) { \ int flag; \ MPI_Status st; \ MPI_Test(&recvreq, &flag, &st); \ if (flag) { \ recvreq_active = 0; \ int count; \ MPI_Get_count(&st, INT64_T_MPI_TYPE, &count); \ /* count == 0 is a signal from a rank that it is done sending to me * (using MPI's non-overtaking rules to keep that signal after all * "real" messages. */ \ if (count == 0) { \ ++num_ranks_done; \ } else { \ int j; \ for (j = 0; j < count; j += 2) { \ int64_t tgt = recvbuf[j]; \ int64_t src = recvbuf[j + 1]; \ /* Process one incoming edge. */ \ assert (VERTEX_OWNER(tgt) == rank); \ if (!TEST_VISITED(tgt)) { \ SET_VISITED(tgt); \ pred[VERTEX_LOCAL(tgt)] = src; \ newq[newq_count++] = tgt; \ } \ } \ } \ /* Restart the receive if more messages will be coming. */ \ if (num_ranks_done < size) { \ MPI_Irecv(recvbuf, coalescing_size * 2, INT64_T_MPI_TYPE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &recvreq); \ recvreq_active = 1; \ } \ } else break; \ } \ /* Mark any sends that completed as inactive so their buffers can be * reused. */ \ int c; \ for (c = 0; c < size; ++c) { \ if (outgoing_reqs_active[c]) { \ int flag; \ MPI_Test(&outgoing_reqs[c], &flag, MPI_STATUS_IGNORE); \ if (flag) outgoing_reqs_active[c] = 0; \ } \ } \ } while (0) while (1) { memset(outgoing_counts, 0, size * sizeof(size_t)); num_ranks_done = 1; /* I never send to myself, so I'm always done */ /* Start the initial receive. */ if (num_ranks_done < size) { MPI_Irecv(recvbuf, coalescing_size * 2, INT64_T_MPI_TYPE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &recvreq); recvreq_active = 1; } /* Step through the current level's queue. */ size_t i; for (i = 0; i < oldq_count; ++i) { CHECK_MPI_REQS; assert (VERTEX_OWNER(oldq[i]) == rank); assert (pred[VERTEX_LOCAL(oldq[i])] >= 0 && pred[VERTEX_LOCAL(oldq[i])] < g->nglobalverts); ++nvisited_local; int64_t src = oldq[i]; /* Iterate through its incident edges. */ size_t j, j_end = g->rowstarts[VERTEX_LOCAL(oldq[i]) + 1]; for (j = g->rowstarts[VERTEX_LOCAL(oldq[i])]; j < j_end; ++j) { int64_t tgt = g->column[j]; int owner = VERTEX_OWNER(tgt); /* If the other endpoint is mine, update the visited map, predecessor * map, and next-level queue locally; otherwise, send the target and * the current vertex (its possible predecessor) to the target's owner. * */ if (owner == rank) { if (!TEST_VISITED(tgt)) { SET_VISITED(tgt); pred[VERTEX_LOCAL(tgt)] = src; newq[newq_count++] = tgt; } } else { while (outgoing_reqs_active[owner]) CHECK_MPI_REQS; /* Wait for buffer to be available */ size_t c = outgoing_counts[owner]; outgoing[owner * coalescing_size * 2 + c] = tgt; outgoing[owner * coalescing_size * 2 + c + 1] = src; outgoing_counts[owner] += 2; if (outgoing_counts[owner] == coalescing_size * 2) { MPI_Isend(&outgoing[owner * coalescing_size * 2], coalescing_size * 2, INT64_T_MPI_TYPE, owner, 0, MPI_COMM_WORLD, &outgoing_reqs[owner]); outgoing_reqs_active[owner] = 1; outgoing_counts[owner] = 0; } } } } /* Flush any coalescing buffers that still have messages. */ int offset; for (offset = 1; offset < size; ++offset) { int dest = MOD_SIZE(rank + offset); if (outgoing_counts[dest] != 0) { while (outgoing_reqs_active[dest]) CHECK_MPI_REQS; MPI_Isend(&outgoing[dest * coalescing_size * 2], outgoing_counts[dest], INT64_T_MPI_TYPE, dest, 0, MPI_COMM_WORLD, &outgoing_reqs[dest]); outgoing_reqs_active[dest] = 1; outgoing_counts[dest] = 0; } /* Wait until all sends to this destination are done. */ while (outgoing_reqs_active[dest]) CHECK_MPI_REQS; /* Tell the destination that we are done sending to them. */ MPI_Isend(&outgoing[dest * coalescing_size * 2], 0, INT64_T_MPI_TYPE, dest, 0, MPI_COMM_WORLD, &outgoing_reqs[dest]); /* Signal no more sends */ outgoing_reqs_active[dest] = 1; while (outgoing_reqs_active[dest]) CHECK_MPI_REQS; } /* Wait until everyone else is done (and thus couldn't send us any more * messages). */ while (num_ranks_done < size) CHECK_MPI_REQS; /* Test globally if all queues are empty. */ int64_t global_newq_count; MPI_Allreduce(&newq_count, &global_newq_count, 1, INT64_T_MPI_TYPE, MPI_SUM, MPI_COMM_WORLD); /* Quit if they all are empty. */ if (global_newq_count == 0) break; /* Swap old and new queues; clear new queue for next level. */ {int64_t* temp = oldq; oldq = newq; newq = temp;} oldq_count = newq_count; newq_count = 0; } #undef CHECK_MPI_REQS /* Add up total visited-vertex count. */ MPI_Allreduce(MPI_IN_PLACE, &nvisited_local, 1, INT64_T_MPI_TYPE, MPI_SUM, MPI_COMM_WORLD); *nvisited = nvisited_local; free(oldq); free(newq); MPI_Free_mem(outgoing); free(outgoing_counts); free(outgoing_reqs); free(outgoing_reqs_active); MPI_Free_mem(recvbuf); free(visited); } CombBLAS_beta_16_2/graph500-1.2/mpi/validate.c000644 000765 000024 00000041137 13212627400 021775 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include "common.h" #include #include #include #include #include #include #include #include int validate_bfs_result(const csr_graph* const g, const int64_t root, const int64_t* const pred, const int64_t nvisited) { int validation_passed = 1; int root_is_mine = (VERTEX_OWNER(root) == rank); const size_t nlocalverts = g->nlocalverts; const size_t nlocaledges = g->nlocaledges; const int64_t nglobalverts = g->nglobalverts; /* Check that root is its own parent. */ if (root_is_mine) { if (pred[VERTEX_LOCAL(root)] != root) { fprintf(stderr, "%d: Validation error: parent of root vertex %" PRId64 " is %" PRId64 ", not the root itself.\n", rank, root, pred[VERTEX_LOCAL(root)]); validation_passed = 0; } } /* Check that nothing else is its own parent, and check for in-range * values. */ int any_range_errors = 0; size_t i; for (i = 0; i < nlocalverts; ++i) { int64_t v = VERTEX_TO_GLOBAL(i); assert (VERTEX_OWNER(v) == rank); assert (VERTEX_LOCAL(v) == i); if (v != root && pred[i] == v) { fprintf(stderr, "%d: Validation error: parent of non-root vertex %" PRId64 " is itself.\n", rank, v); validation_passed = 0; } if (pred[i] < -1 || pred[i] >= nglobalverts) { fprintf(stderr, "%d: Validation error: parent of vertex %" PRId64 " is out-of-range value %" PRId64 ".\n", rank, v, pred[i]); validation_passed = 0; any_range_errors = 1; } } MPI_Allreduce(MPI_IN_PLACE, &any_range_errors, 1, MPI_INT, MPI_LOR, MPI_COMM_WORLD); /* Check that nvisited is correct. */ int64_t nvisited_actual = 0; for (i = 0; i < nlocalverts; ++i) { if (pred[i] != -1) ++nvisited_actual; } MPI_Allreduce(MPI_IN_PLACE, &nvisited_actual, 1, INT64_T_MPI_TYPE, MPI_SUM, MPI_COMM_WORLD); if (nvisited_actual != nvisited) { fprintf(stderr, "%d: Validation error: claimed visit count %" PRId64 " is different from actual count %" PRId64 ".\n", rank, nvisited, nvisited_actual); validation_passed = 0; } if (!any_range_errors) { /* Other parts of validation assume in-range values */ /* Check that there is an edge from each vertex to its claimed * predecessor. */ size_t i; for (i = 0; i < nlocalverts; ++i) { int64_t v = VERTEX_TO_GLOBAL(i); int64_t p = pred[i]; if (p == -1) continue; int found_pred_edge = 0; if (v == p) found_pred_edge = 1; /* Root vertex */ size_t ei, ei_end = g->rowstarts[i + 1]; for (ei = g->rowstarts[i]; ei < ei_end; ++ei) { int64_t w = g->column[ei]; if (w == p) { found_pred_edge = 1; break; } } if (!found_pred_edge) { fprintf(stderr, "%d: Validation error: no graph edge from vertex %" PRId64 " to its parent %" PRId64 ".\n", rank, v, p); validation_passed = 0; } } /* Create a vertex depth map to use for later validation. */ int64_t* depth = (int64_t*)xmalloc(nlocalverts * sizeof(int64_t)); { /* Scope some code that has a lot of temporary variables. */ int64_t* pred_depth = (int64_t*)xmalloc(nlocalverts * sizeof(int64_t)); /* Depth of predecessor vertex for each local vertex */ size_t i; for (i = 0; i < nlocalverts; ++i) depth[i] = INT64_MAX; if (root_is_mine) depth[VERTEX_LOCAL(root)] = 0; /* Send each vertex that appears in the local part of the predecessor map * to its owner; record the original locations so we can put the answers * into pred_depth. */ /* Do a histogram sort by owner (this same kind of sort is used other * places as well). First, count the number of vertices going to each * destination. */ int* num_preds_per_owner = (int*)xcalloc(size, sizeof(int)); /* Uses zero-init */ for (i = 0; i < nlocalverts; ++i) { ++num_preds_per_owner[pred[i] == -1 ? size - 1 : VERTEX_OWNER(pred[i])]; } int64_t* preds_per_owner = (int64_t*)xmalloc(nlocalverts * sizeof(int64_t)); /* Predecessors sorted by owner */ int64_t* preds_per_owner_results_offsets = (int64_t*)xmalloc(nlocalverts * sizeof(int64_t)); /* Indices into pred_depth to write */ /* Second, do a prefix sum to get the displacements of the different * owners in the outgoing array. */ int* pred_owner_displs = (int*)xmalloc((size + 1) * sizeof(int)); pred_owner_displs[0] = 0; int r; for (r = 0; r < size; ++r) { pred_owner_displs[r + 1] = pred_owner_displs[r] + num_preds_per_owner[r]; } /* Last, put the vertices into the correct positions in the array, based * on their owners and the counts and displacements computed earlier. */ int* pred_owner_offsets = (int*)xmalloc((size + 1) * sizeof(int)); memcpy(pred_owner_offsets, pred_owner_displs, (size + 1) * sizeof(int)); for (i = 0; i < nlocalverts; ++i) { int* offset_ptr = &pred_owner_offsets[pred[i] == -1 ? size - 1 : VERTEX_OWNER(pred[i])]; preds_per_owner[*offset_ptr] = pred[i]; preds_per_owner_results_offsets[*offset_ptr] = i; ++*offset_ptr; } for (r = 0; r < size; ++r) { assert (pred_owner_offsets[r] == pred_owner_displs[r + 1]); } free(pred_owner_offsets); /* Send around the number of vertices that will be sent to each destination. */ int* num_my_preds_per_sender = (int*)xmalloc(size * sizeof(int)); MPI_Alltoall(num_preds_per_owner, 1, MPI_INT, num_my_preds_per_sender, 1, MPI_INT, MPI_COMM_WORLD); int* my_preds_per_sender_displs = (int*)xmalloc((size + 1) * sizeof(int)); my_preds_per_sender_displs[0] = 0; for (r = 0; r < size; ++r) { my_preds_per_sender_displs[r + 1] = my_preds_per_sender_displs[r] + num_my_preds_per_sender[r]; } /* Send around the actual vertex data (list of depth requests that will * be responded to at each BFS iteration). */ int64_t* my_depth_requests = (int64_t*)xmalloc(my_preds_per_sender_displs[size] * sizeof(int64_t)); int64_t* my_depth_replies = (int64_t*)xmalloc(my_preds_per_sender_displs[size] * sizeof(int64_t)); MPI_Alltoallv(preds_per_owner, num_preds_per_owner, pred_owner_displs, INT64_T_MPI_TYPE, my_depth_requests, num_my_preds_per_sender, my_preds_per_sender_displs, INT64_T_MPI_TYPE, MPI_COMM_WORLD); int64_t* pred_depth_raw = (int64_t*)xmalloc(nlocalverts * sizeof(int64_t)); /* Depth of predecessor vertex for each local vertex, ordered by source proc */ /* Do a mini-BFS (naively) over just the predecessor graph (hopefully a * tree) produced by the real BFS; fill in the depth map. */ while (1) { int any_changed = 0; int i; /* Create and send the depth values requested by other nodes. The list * of requests is sent once, and are stored on the receiver so the * replies can be sent (possibly with updated depth values) at every * iteration. */ for (i = 0; i < my_preds_per_sender_displs[size]; ++i) { my_depth_replies[i] = (my_depth_requests[i] == -1 ? INT64_MAX : depth[VERTEX_LOCAL(my_depth_requests[i])]); } MPI_Alltoallv(my_depth_replies, num_my_preds_per_sender, my_preds_per_sender_displs, INT64_T_MPI_TYPE, pred_depth_raw, num_preds_per_owner, pred_owner_displs, INT64_T_MPI_TYPE, MPI_COMM_WORLD); { size_t i; /* Put the received depths into the local array. */ for (i = 0; i < nlocalverts; ++i) { pred_depth[preds_per_owner_results_offsets[i]] = pred_depth_raw[i]; } /* Check those values to determine if they violate any correctness * conditions. */ for (i = 0; i < nlocalverts; ++i) { int64_t v = VERTEX_TO_GLOBAL(i); if (v == root) { /* The depth and predecessor for this were checked earlier. */ } else if (depth[i] == INT64_MAX && pred_depth[i] == INT64_MAX) { /* OK -- depth should be filled in later. */ } else if (depth[i] == INT64_MAX && pred_depth[i] != INT64_MAX) { depth[i] = pred_depth[i] + 1; any_changed = 1; } else if (depth[i] != pred_depth[i] + 1) { fprintf(stderr, "%d: Validation error: BFS predecessors do not form a tree; see vertices %" PRId64 " (depth %" PRId64 ") and %" PRId64 " (depth %" PRId64 ").\n", rank, v, depth[i], pred[i], pred_depth[i]); validation_passed = 0; } else { /* Vertex already has its correct depth value. */ } } } MPI_Allreduce(MPI_IN_PLACE, &any_changed, 1, MPI_INT, MPI_LOR, MPI_COMM_WORLD); if (!any_changed) break; } free(num_preds_per_owner); free(num_my_preds_per_sender); free(preds_per_owner); free(preds_per_owner_results_offsets); free(my_preds_per_sender_displs); free(my_depth_requests); free(my_depth_replies); free(pred_owner_displs); free(pred_depth); free(pred_depth_raw); } /* Check that all edges connect vertices whose depths differ by at most * one. */ { int64_t maxlocaledges = 0; MPI_Allreduce((void*)&nlocaledges, &maxlocaledges, 1, INT64_T_MPI_TYPE, MPI_MAX, MPI_COMM_WORLD); /* We break the total list of overall edges into chunks to reduce the * amount of data to be sent at a time (since we are using MPI_Alltoallv * to send data collectively). */ const int edge_chunk_size = (1 << 23); /* Reduce memory usage */ int num_edge_groups = (maxlocaledges + edge_chunk_size - 1) / edge_chunk_size; int eg; for (eg = 0; eg < num_edge_groups; ++eg) { size_t first_edge_index = (size_t)(eg * edge_chunk_size); if (first_edge_index > nlocaledges) first_edge_index = nlocaledges; size_t last_edge_index = (size_t)((eg + 1) * edge_chunk_size); if (last_edge_index > nlocaledges) last_edge_index = nlocaledges; /* Sort the edge targets in this chunk by their owners (histogram * sort); see the BFS code above for details of the steps of the * algorithm. */ int* num_edge_targets_by_owner = (int*)xcalloc(size, sizeof(int)); /* Uses zero-init */ size_t ei; for (ei = first_edge_index; ei < last_edge_index; ++ei) { ++num_edge_targets_by_owner[VERTEX_OWNER(g->column[ei])]; } int* edge_targets_by_owner_displs = (int*)xmalloc((size + 1) * sizeof(int)); edge_targets_by_owner_displs[0] = 0; int i; for (i = 0; i < size; ++i) { edge_targets_by_owner_displs[i + 1] = edge_targets_by_owner_displs[i] + num_edge_targets_by_owner[i]; } int64_t* edge_targets_by_owner = (int64_t*)xmalloc(edge_targets_by_owner_displs[size] * sizeof(int64_t)); int64_t* edge_targets_by_owner_indices = (int64_t*)xmalloc(edge_targets_by_owner_displs[size] * sizeof(int64_t)); /* Source indices for where to write the targets */ int* edge_targets_by_owner_offsets = (int*)xmalloc((size + 1) * sizeof(int)); memcpy(edge_targets_by_owner_offsets, edge_targets_by_owner_displs, (size + 1) * sizeof(int)); for (ei = first_edge_index; ei < last_edge_index; ++ei) { edge_targets_by_owner[edge_targets_by_owner_offsets[VERTEX_OWNER(g->column[ei])]] = g->column[ei]; edge_targets_by_owner_indices[edge_targets_by_owner_offsets[VERTEX_OWNER(g->column[ei])]] = ei; ++edge_targets_by_owner_offsets[VERTEX_OWNER(g->column[ei])]; } for (i = 0; i < size; ++i) { assert (edge_targets_by_owner_offsets[i] == edge_targets_by_owner_displs[i + 1]); } free(edge_targets_by_owner_offsets); /* Send around the number of data elements that will be sent later. */ int* num_incoming_targets_by_src = (int*)xmalloc(size * sizeof(int)); MPI_Alltoall(num_edge_targets_by_owner, 1, MPI_INT, num_incoming_targets_by_src, 1, MPI_INT, MPI_COMM_WORLD); int* incoming_targets_by_src_displs = (int*)xmalloc((size + 1) * sizeof(int)); incoming_targets_by_src_displs[0] = 0; for (i = 0; i < size; ++i) { incoming_targets_by_src_displs[i + 1] = incoming_targets_by_src_displs[i] + num_incoming_targets_by_src[i]; } int64_t* target_depth_requests = (int64_t*)xmalloc(incoming_targets_by_src_displs[size] * sizeof(int64_t)); int64_t* target_depth_replies = (int64_t*)xmalloc(incoming_targets_by_src_displs[size] * sizeof(int64_t)); /* Send the actual requests for the depths of edge targets. */ MPI_Alltoallv(edge_targets_by_owner, num_edge_targets_by_owner, edge_targets_by_owner_displs, INT64_T_MPI_TYPE, target_depth_requests, num_incoming_targets_by_src, incoming_targets_by_src_displs, INT64_T_MPI_TYPE, MPI_COMM_WORLD); free(edge_targets_by_owner); /* Fill in the replies for the requests sent to me. */ for (i = 0; i < incoming_targets_by_src_displs[size]; ++i) { assert (VERTEX_OWNER(target_depth_requests[i]) == rank); target_depth_replies[i] = depth[VERTEX_LOCAL(target_depth_requests[i])]; } free(target_depth_requests); int64_t* target_depth_raw = (int64_t*)xmalloc((last_edge_index - first_edge_index) * sizeof(int64_t)); /* Send back the replies. */ MPI_Alltoallv(target_depth_replies, num_incoming_targets_by_src, incoming_targets_by_src_displs, INT64_T_MPI_TYPE, target_depth_raw, num_edge_targets_by_owner, edge_targets_by_owner_displs, INT64_T_MPI_TYPE, MPI_COMM_WORLD); free(target_depth_replies); free(num_incoming_targets_by_src); free(num_edge_targets_by_owner); free(incoming_targets_by_src_displs); free(edge_targets_by_owner_displs); int64_t* target_depth = (int64_t*)xmalloc((last_edge_index - first_edge_index) * sizeof(int64_t)); /* Put the replies into the proper order (original order of the edges). * */ for (ei = 0; ei < last_edge_index - first_edge_index; ++ei) { target_depth[edge_targets_by_owner_indices[ei] - first_edge_index] = target_depth_raw[ei]; } free(target_depth_raw); free(edge_targets_by_owner_indices); /* Check the depth relationship of the endpoints of each edge in the * current chunk. */ size_t src_i = 0; for (ei = first_edge_index; ei < last_edge_index; ++ei) { while (ei >= g->rowstarts[src_i + 1]) { ++src_i; } int64_t src = VERTEX_TO_GLOBAL(src_i); int64_t src_depth = depth[src_i]; int64_t tgt = g->column[ei]; int64_t tgt_depth = target_depth[ei - first_edge_index]; if (src_depth != INT64_MAX && tgt_depth == INT64_MAX) { fprintf(stderr, "%d: Validation error: edge connects vertex %" PRId64 " in the BFS tree (depth %" PRId64 ") to vertex %" PRId64 " outside the tree.\n", rank, src, src_depth, tgt); validation_passed = 0; } else if (src_depth == INT64_MAX && tgt_depth != INT64_MAX) { /* Skip this for now; this problem will be caught when scanning * reversed copy of this edge. Set the failure flag, though, * just in case. */ validation_passed = 0; } else if (src_depth - tgt_depth < -1 || src_depth - tgt_depth > 1) { fprintf(stderr, "%d: Validation error: depths of edge endpoints %" PRId64 " (depth %" PRId64 ") and %" PRId64 " (depth %" PRId64 ") are too far apart (abs. val. > 1).\n", rank, src, src_depth, tgt, tgt_depth); validation_passed = 0; } } free(target_depth); } } free(depth); } /* End of part skipped by range errors */ /* Collect the global validation result. */ MPI_Allreduce(MPI_IN_PLACE, &validation_passed, 1, MPI_INT, MPI_LAND, MPI_COMM_WORLD); return validation_passed; } CombBLAS_beta_16_2/graph500-1.2/mpi/bfs_custom.c000644 000765 000024 00000006053 13212627400 022346 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include "common.h" #include #include #include #include #include #include #include #include /* Add your own BFS code into this file (or a copy of it). */ void run_mpi_bfs(const csr_graph* const g, int64_t root, int64_t* pred, int64_t* nvisited) { /* Predefined entities you can use in your BFS (from common.h): * + rank: global variable containing MPI rank * + size: global variable containing MPI size * + DIV_SIZE: single-parameter macro that divides by size (using a shift * when properly set up) * + MOD_SIZE: single-parameter macro that reduces modulo size (using a * mask when properly set up) * + VERTEX_OWNER: single-parameter macro returning the owner of a global * vertex number * + VERTEX_LOCAL: single-parameter macro returning the local offset of a * global vertex number * + VERTEX_TO_GLOBAL: single-parameter macro converting a local vertex * offset to a global number * + g->nlocalverts: number of vertices stored on the local rank * + g->nglobalverts: total number of vertices in the graph * + g->nlocaledges: number of graph edges stored locally * + g->rowstarts, g->column: zero-based compressed sparse row data * structure for the local part of the graph * * All macros documented above evaluate their arguments exactly once. * * The graph is stored using a 1-D, cyclic distribution: all edges incident * to vertex v are stored on rank (v % size) (aka VERTEX_OWNER(v)). Edges * that are not self-loops are stored twice, once for each endpoint; * duplicates edges are kept. The neighbors of vertex v can be obtained on * rank VERTEX_OWNER(v); they are stored in elements * {g->rowstarts[VERTEX_LOCAL(v)] ... g->rowstarts[VERTEX_LOCAL(v) + 1] - 1} * (inclusive) of g->column. * * Upon exit, your BFS must have filled in: * + pred (an array of size g->nlocalverts): * - The predecessor of vertex v in the BFS tree should go into * pred[VERTEX_LOCAL(v)] on rank VERTEX_OWNER(v) * - The predecessor of root is root * - The predecessor of any unreachable vertex is -1 * + *nvisited * - The number of vertices, including the root, visited during the BFS * - This number should be summed over all ranks and should be consistent * * The validator will check both of these for correctness. */ } CombBLAS_beta_16_2/graph500-1.2/mpi/Makefile000644 000765 000024 00000002116 13212627400 021472 0ustar00aydinbulucstaff000000 000000 include ../make.inc MPICC ?= mpicc BIN = graph500_mpi_simple graph500_mpi_one_sided ifeq ($(BUILD_MPI_CUSTOM), Yes) BIN += graph500_mpi_custom endif all: $(BIN) GENLIB=../generator/libgraph_generator_mpi.a graph500_mpi_simple: main.c bfs_simple.c convert_to_csr.c find_roots.c utils.c validate.c $(GENLIB) $(MPICC) $(CFLAGS) $(LDFLAGS) -o graph500_mpi_simple bfs_simple.c convert_to_csr.c find_roots.c main.c utils.c validate.c $(GENLIB) -lm graph500_mpi_one_sided: main.c bfs_one_sided.c convert_to_csr.c find_roots.c utils.c validate.c $(GENLIB) $(MPICC) $(CFLAGS) $(LDFLAGS) -o graph500_mpi_one_sided bfs_one_sided.c convert_to_csr.c find_roots.c main.c utils.c validate.c $(GENLIB) -lm graph500_mpi_custom: main.c bfs_custom.c convert_to_csr.c find_roots.c utils.c validate.c $(GENLIB) $(MPICC) $(CFLAGS) $(LDFLAGS) -o graph500_mpi_custom bfs_custom.c convert_to_csr.c find_roots.c main.c utils.c validate.c $(GENLIB) -lm $(GENLIB): $(MAKE) -C ../generator -f ../generator/Makefile.mpi clean: -rm -f graph500_mpi_* *.o *.a -$(MAKE) -C ../generator -f ../generator/Makefile.mpi clean CombBLAS_beta_16_2/graph500-1.2/mpi/bfs_one_sided.c000644 000765 000024 00000021270 13212627400 022763 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include "common.h" #include #include #include #include #include #include #include /* This BFS represents its queues as bitmaps and uses some data representation * tricks to fit with the use of MPI one-sided operations. It is not much * faster than the standard version on the machines I have tested it on, but * systems that have good RDMA hardware and good MPI one-sided implementations * might get better performance from it. This code might also be good to * translate to UPC, Co-array Fortran, SHMEM, or GASNet since those systems are * more designed for one-sided remote memory operations. */ void run_mpi_bfs(const csr_graph* const g, int64_t root, int64_t* pred, int64_t* nvisited) { const size_t nlocalverts = g->nlocalverts; const int64_t nglobalverts = g->nglobalverts; int64_t nvisited_local = 0; /* Set up a second predecessor map so we can read from one and modify the * other. */ int64_t* orig_pred = pred; int64_t* pred2 = (int64_t*)xMPI_Alloc_mem(nlocalverts * sizeof(int64_t)); /* The queues (old and new) are represented as bitmaps. Each bit in the * queue bitmap says to check elts_per_queue_bit elements in the predecessor * map for vertices that need to be visited. In other words, the queue * bitmap is an overapproximation of the actual queue; because MPI_Accumulate * does not get any information on the result of the update, sometimes * elements are also added to the bitmap when they were actually already * black. Because of this, the predecessor map needs to be checked to be * sure a given vertex actually needs to be processed. */ const int elts_per_queue_bit = 4; const int ulong_bits = sizeof(unsigned long) * CHAR_BIT; int64_t queue_nbits = (nlocalverts + elts_per_queue_bit - 1) / elts_per_queue_bit; int64_t queue_nwords = (queue_nbits + ulong_bits - 1) / ulong_bits; unsigned long* queue_bitmap1 = (unsigned long*)xMPI_Alloc_mem(queue_nwords * sizeof(unsigned long)); unsigned long* queue_bitmap2 = (unsigned long*)xMPI_Alloc_mem(queue_nwords * sizeof(unsigned long)); memset(queue_bitmap1, 0, queue_nwords * sizeof(unsigned long)); /* List of local vertices (used as sources in MPI_Accumulate). */ int64_t* local_vertices = (int64_t*)xMPI_Alloc_mem(nlocalverts * sizeof(int64_t)); {size_t i; for (i = 0; i < nlocalverts; ++i) local_vertices[i] = VERTEX_TO_GLOBAL(i);} /* List of all bit masks for an unsigned long (used as sources in * MPI_Accumulate). */ unsigned long masks[ulong_bits]; {int i; for (i = 0; i < ulong_bits; ++i) masks[i] = (1UL << i);} /* Coding of predecessor map: */ /* - White (not visited): INT64_MAX */ /* - Grey (in queue): 0 .. nglobalverts-1 */ /* - Black (done): -nglobalverts .. -1 */ /* Set initial predecessor map. */ {size_t i; for (i = 0; i < nlocalverts; ++i) pred[i] = INT64_MAX;} /* Mark root as grey and add it to the queue. */ if (VERTEX_OWNER(root) == rank) { pred[VERTEX_LOCAL(root)] = root; queue_bitmap1[VERTEX_LOCAL(root) / elts_per_queue_bit / ulong_bits] |= (1UL << ((VERTEX_LOCAL(root) / elts_per_queue_bit) % ulong_bits)); } /* Create MPI windows on the two predecessor arrays and the two queues. */ MPI_Win pred_win, pred2_win, queue1_win, queue2_win; MPI_Win_create(pred, nlocalverts * sizeof(int64_t), sizeof(int64_t), MPI_INFO_NULL, MPI_COMM_WORLD, &pred_win); MPI_Win_create(pred2, nlocalverts * sizeof(int64_t), sizeof(int64_t), MPI_INFO_NULL, MPI_COMM_WORLD, &pred2_win); MPI_Win_create(queue_bitmap1, queue_nwords * sizeof(unsigned long), sizeof(unsigned long), MPI_INFO_NULL, MPI_COMM_WORLD, &queue1_win); MPI_Win_create(queue_bitmap2, queue_nwords * sizeof(unsigned long), sizeof(unsigned long), MPI_INFO_NULL, MPI_COMM_WORLD, &queue2_win); while (1) { int64_t i; /* Clear the next-level queue. */ memset(queue_bitmap2, 0, queue_nwords * sizeof(unsigned long)); /* The pred2 array is pred with all grey vertices changed to black. */ memcpy(pred2, pred, nlocalverts * sizeof(int64_t)); for (i = 0; i < (int64_t)nlocalverts; ++i) { if (pred2[i] >= 0 && pred2[i] < nglobalverts) pred2[i] -= nglobalverts; } /* Start one-sided operations for this level. */ MPI_Win_fence(MPI_MODE_NOPRECEDE, pred2_win); MPI_Win_fence(MPI_MODE_NOPRECEDE, queue2_win); /* Step through the words of the queue bitmap. */ for (i = 0; i < queue_nwords; ++i) { unsigned long val = queue_bitmap1[i]; int bitnum; /* Skip any that are all zero. */ if (!val) continue; /* Scan the bits in the word. */ for (bitnum = 0; bitnum < ulong_bits; ++bitnum) { size_t first_v_local = (size_t)((i * ulong_bits + bitnum) * elts_per_queue_bit); if (first_v_local >= nlocalverts) break; int bit = (int)((val >> bitnum) & 1); /* Skip any that are zero. */ if (!bit) continue; /* Scan the queue elements corresponding to this bit. */ int qelem_idx; for (qelem_idx = 0; qelem_idx < elts_per_queue_bit; ++qelem_idx) { size_t v_local = first_v_local + qelem_idx; if (v_local >= nlocalverts) continue; /* Since the queue is an overapproximation, check the predecessor map * to be sure this vertex is grey. */ if (pred[v_local] >= 0 && pred[v_local] < nglobalverts) { ++nvisited_local; size_t ei, ei_end = g->rowstarts[v_local + 1]; /* Walk the incident edges. */ for (ei = g->rowstarts[v_local]; ei < ei_end; ++ei) { int64_t w = g->column[ei]; if (w == VERTEX_TO_GLOBAL(v_local)) continue; /* Self-loop */ /* Set the predecessor of the other edge endpoint (note use of * MPI_MIN and the coding of the predecessor map). */ MPI_Accumulate(&local_vertices[v_local], 1, INT64_T_MPI_TYPE, VERTEX_OWNER(w), VERTEX_LOCAL(w), 1, INT64_T_MPI_TYPE, MPI_MIN, pred2_win); /* Mark the endpoint in the remote queue (note that the min may * not do an update, so the queue is an overapproximation in this * way as well). */ MPI_Accumulate(&masks[((VERTEX_LOCAL(w) / elts_per_queue_bit) % ulong_bits)], 1, MPI_UNSIGNED_LONG, VERTEX_OWNER(w), VERTEX_LOCAL(w) / elts_per_queue_bit / ulong_bits, 1, MPI_UNSIGNED_LONG, MPI_BOR, queue2_win); } } } } } /* End one-sided operations. */ MPI_Win_fence(MPI_MODE_NOSUCCEED, queue2_win); MPI_Win_fence(MPI_MODE_NOSUCCEED, pred2_win); /* Test if there are any elements in the next-level queue (globally); stop * if none. */ int any_set = 0; for (i = 0; i < queue_nwords; ++i) { if (queue_bitmap2[i] != 0) {any_set = 1; break;} } MPI_Allreduce(MPI_IN_PLACE, &any_set, 1, MPI_INT, MPI_LOR, MPI_COMM_WORLD); if (!any_set) break; /* Swap queues and predecessor maps. */ {MPI_Win temp = queue1_win; queue1_win = queue2_win; queue2_win = temp;} {unsigned long* temp = queue_bitmap1; queue_bitmap1 = queue_bitmap2; queue_bitmap2 = temp;} {MPI_Win temp = pred_win; pred_win = pred2_win; pred2_win = temp;} {int64_t* temp = pred; pred = pred2; pred2 = temp;} } MPI_Win_free(&pred_win); MPI_Win_free(&pred2_win); MPI_Win_free(&queue1_win); MPI_Win_free(&queue2_win); MPI_Free_mem(local_vertices); MPI_Free_mem(queue_bitmap1); MPI_Free_mem(queue_bitmap2); /* Clean up the predecessor map swapping since the surrounding code does not * allow the BFS to change the predecessor map pointer. */ if (pred2 != orig_pred) { memcpy(orig_pred, pred2, nlocalverts * sizeof(int64_t)); MPI_Free_mem(pred2); } else { MPI_Free_mem(pred); } /* Change from special coding of predecessor map to the one the benchmark * requires. */ size_t i; for (i = 0; i < nlocalverts; ++i) { if (orig_pred[i] < 0) { orig_pred[i] += nglobalverts; } else if (orig_pred[i] == INT64_MAX) { orig_pred[i] = -1; } } /* Count visited vertices. */ MPI_Allreduce(MPI_IN_PLACE, &nvisited_local, 1, INT64_T_MPI_TYPE, MPI_SUM, MPI_COMM_WORLD); *nvisited = nvisited_local; } CombBLAS_beta_16_2/graph500-1.2/mpi/README000644 000765 000024 00000004275 13212627400 020722 0ustar00aydinbulucstaff000000 000000 Graph500 Benchmark: MPI Reference Implementation Jeremiah Willcock and Andrew Lumsdaine This directory contains an MPI-based reference implementation for the Graph500 benchmark. The Makefile will automatically build the supporting code, along with two reference implementations of breadth-first search and a skeleton version for users to modify. The Makefile may need to be modified with your MPI location and compiler flags. The generated programs, named graph500_mpi_*, take up to two parameters: the scale of the problem and the edge factor. The problem scale is the logarithm, base 2, of the number of vertices in the graph; only graphs with power-of-2 vertex counts are supported without source code modification. The edge factor is the ratio of the number of edges to the number of vertices; i.e., it is half the average vertex degree in the graph. The scale parameter is mandatory; the edge factor is optional and defaults to 16 (the value specified by the benchmark). Running any of the graph500_mpi_* programs without any arguments will produce a usage message. The code is written in C; the code compiles with GCC's default gnu89 language setting, but should be valid C99 and C++ (except for the use of a few C99 headers). The main non-C89 features used are variable declarations after statements in a block and the and headers. A template for writing your own BFS using the reference data structures and infrastructure is in bfs_custom.c. You can either modify that file in place or copy it (adjusting the Makefile) to create your own version. The documentation for what data structures are available and how to use them is in comments in bfs_custom.c. The reference implementation also contains code to convert from a distributed list of graph edges into a distributed compressed sparse row data structure, as well as code for timing the BFS run, validating the correctness of the results, and printing the timings in the Graph500-required format. Copyright (C) 2009-2010 The Trustees of Indiana University. Use, modification and distribution is subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) CombBLAS_beta_16_2/graph500-1.2/mpi/convert_to_csr.c000644 000765 000024 00000025405 13212627400 023235 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include "common.h" #include #include #include #include #include #include #include #include /* STINGER-like group of edges, forming a linked list of pages. */ typedef struct edge_page { int64_t targets[16]; /* Unused elements filled with -1 */ struct edge_page* next; /* NULL if no more */ } edge_page; static inline edge_page* new_edge_page(void) { edge_page* ep = (edge_page*)xmalloc(sizeof(edge_page)); int i; ep->next = NULL; for (i = 0; i < 16; ++i) ep->targets[i] = -1; return ep; } static inline void delete_edge_page(edge_page* ep) { if (!ep) return; delete_edge_page(ep->next); free(ep); } typedef struct adjacency_list { size_t nvertices; /* User-visible number of vertices */ size_t nvertices_allocated; /* Actual allocated size of data */ edge_page** data; /* Indexed by vertex */ } adjacency_list; static void grow_adj_list(adjacency_list* al, size_t min_nvertices) { if (min_nvertices <= al->nvertices) return; while (min_nvertices > al->nvertices_allocated) { al->nvertices_allocated = (al->nvertices_allocated == 0) ? 16 : (al->nvertices_allocated * 2); al->data = (edge_page**)xrealloc(al->data, al->nvertices_allocated * sizeof(edge_page*)); } size_t i; for (i = al->nvertices; i < min_nvertices; ++i) { al->data[i] = NULL; } al->nvertices = min_nvertices; } static void add_adj_list_edge(adjacency_list* al, size_t src, int64_t tgt) { grow_adj_list(al, src + 1); edge_page** p = al->data + src; /* Each page is filled before we allocate another one, so we only need to * check the last one in the chain. */ while (*p && (*p)->next) {p = &((*p)->next);} if (*p) { assert (!(*p)->next); int i; for (i = 0; i < 16; ++i) { if ((*p)->targets[i] == -1) { (*p)->targets[i] = tgt; return; } } p = &((*p)->next); assert (!*p); } assert (!*p); *p = new_edge_page(); (*p)->targets[0] = tgt; } static void clear_adj_list(adjacency_list* al) { size_t i; for (i = 0; i < al->nvertices; ++i) delete_edge_page(al->data[i]); free(al->data); al->data = NULL; al->nvertices = al->nvertices_allocated = 0; } void convert_graph_to_csr(const int64_t nedges, const int64_t* const edges, csr_graph* const g) { adjacency_list adj_list = {0, 0, NULL}; /* Adjacency list being built up with * received data */ { /* Redistribute each input undirected edge (a, b) to create two directed * copies: (a -> b) on VERTEX_OWNER(a) and (b -> a) on VERTEX_OWNER(b) * [except for self-loops, of which only one copy is kept]. */ const size_t edge_buffer_size = (1 << 27) / (2 * sizeof(int64_t)) / size; /* 128 MiB */ /* Note that these buffers are edge pairs (src, tgt), where both elements * are global vertex indexes. */ int64_t* recvbuf = (int64_t*)xmalloc(edge_buffer_size * 2 * sizeof(int64_t)); MPI_Request recvreq; int recvreq_active = 0; int64_t* coalescing_buf = (int64_t*)xmalloc(size * edge_buffer_size * 2 * sizeof(int64_t)); size_t* coalescing_counts = (size_t*)xcalloc(size, sizeof(size_t)); /* Uses zero-init */ MPI_Request* sendreqs = (MPI_Request*)xmalloc(size * sizeof(MPI_Request)); int* sendreqs_active = (int*)xcalloc(size, sizeof(int)); /* Uses zero-init */ int num_sendreqs_active = 0; int num_senders_done = 1; /* Number of ranks that have said that they will * not send to me again; I will never send to * myself at all (see test in SEND). */ /* The heavy use of macros here is to create the equivalent of nested * functions with proper access to variables from the enclosing scope. */ #define PROCESS_EDGE(src, tgt) \ /* Do the handling for one received edge. */ \ do { \ assert (VERTEX_OWNER((src)) == rank); \ add_adj_list_edge(&adj_list, VERTEX_LOCAL((src)), (tgt)); \ } while (0) #define START_IRECV \ /* Start/restart the receive operation to wait for blocks of edges, if * needed. */ \ do { \ if (num_senders_done < size) { \ MPI_Irecv(recvbuf, edge_buffer_size * 2, INT64_T_MPI_TYPE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &recvreq); \ recvreq_active = 1; \ } \ } while (0) #define PROCESS_REQS \ /* Handle all outstanding MPI requests and progress the MPI implementation. * */ \ do { \ int flag; \ /* Test receive request. */ \ while (recvreq_active) { \ MPI_Status st; \ MPI_Test(&recvreq, &flag, &st); \ if (!flag) break; \ /* A message arrived. */ \ recvreq_active = 0; \ int count; \ MPI_Get_count(&st, INT64_T_MPI_TYPE, &count); \ count /= 2; \ if (count == 0 /* This count is used as a flag when each sender is done */ ) { \ ++num_senders_done; \ } else { \ /* Process the edges in the received message. */ \ int c; \ for (c = 0; c < count; ++c) { \ PROCESS_EDGE(recvbuf[c * 2], recvbuf[c * 2 + 1]); \ } \ } \ START_IRECV; \ } \ /* Test send requests to determine when their buffers are available for * reuse. */ \ int c; \ for (c = 0; c < size; ++c) { \ if (sendreqs_active[c]) { \ MPI_Test(&sendreqs[c], &flag, MPI_STATUS_IGNORE); \ if (flag) {sendreqs_active[c] = 0; --num_sendreqs_active;} \ } \ } \ } while (0) #define SEND(src, tgt) \ do { \ int dest = VERTEX_OWNER((src)); \ if (dest == rank) { \ /* Process self-sends locally. */ \ PROCESS_EDGE((src), (tgt)); \ } else { \ while (sendreqs_active[dest]) PROCESS_REQS; /* Wait for send buffer to be available */ \ /* Push onto coalescing buffer. */ \ size_t c = coalescing_counts[dest]; \ coalescing_buf[dest * edge_buffer_size * 2 + c * 2] = (src); \ coalescing_buf[dest * edge_buffer_size * 2 + c * 2 + 1] = (tgt); \ ++coalescing_counts[dest]; \ /* Send if the buffer is full. */ \ if (coalescing_counts[dest] == edge_buffer_size) { \ FLUSH_COALESCING_BUFFER(dest); \ } \ } \ } while (0) #define FLUSH_COALESCING_BUFFER(dest) \ do { \ while (sendreqs_active[(dest)]) PROCESS_REQS; /* Wait for previous sends to finish */ \ /* Ssend plus only having one request to a given destination active at a time should act as flow control. */ \ MPI_Issend(coalescing_buf + (dest) * edge_buffer_size * 2, coalescing_counts[(dest)] * 2, INT64_T_MPI_TYPE, (dest), 0, MPI_COMM_WORLD, &sendreqs[(dest)]); \ sendreqs_active[(dest)] = 1; \ ++num_sendreqs_active; \ /* Clear the buffer for the next user. */ \ coalescing_counts[(dest)] = 0; \ } while (0) START_IRECV; size_t i; for (i = 0; i < (size_t)nedges; ++i) { if ((i % (1 << 16)) == 0) PROCESS_REQS; if (edges[i * 2 + 0] == -1 || edges[i * 2 + 1] == -1) { continue; } SEND(edges[i * 2 + 0], edges[i * 2 + 1]); if (edges[i * 2 + 0] != edges[i * 2 + 1]) { /* Only send reverse for non-self-loops. */ SEND(edges[i * 2 + 1], edges[i * 2 + 0]); } } int offset; for (offset = 1; offset < size; ++offset) { int dest = MOD_SIZE(rank + offset); if (coalescing_counts[dest] != 0) { /* Send actual data, if any. */ FLUSH_COALESCING_BUFFER(dest); } /* Send empty message to indicate that we won't send anything else to * this rank (takes advantage of MPI non-overtaking rules). */ FLUSH_COALESCING_BUFFER(dest); } while (num_senders_done < size || num_sendreqs_active > 0) PROCESS_REQS; free(recvbuf); free(coalescing_buf); free(coalescing_counts); free(sendreqs); free(sendreqs_active); #undef PROCESS_REQS #undef PROCESS_EDGE #undef FLUSH_COALESCING_BUFFER #undef SEND #undef START_IRECV } /* Compute global number of vertices and count the degrees of the local * vertices. */ int64_t nverts_known = 0; /* We only count vertices touched by at least one * edge, and because of edge doubling each vertex * incident to an edge must be the target of some * copy of that edge. */ size_t nlocalverts_orig = adj_list.nvertices; size_t* degrees = (size_t*)xcalloc(nlocalverts_orig, sizeof(size_t)); /* Uses zero-init */ size_t i, j; for (i = 0; i < nlocalverts_orig; ++i) { size_t deg = 0; edge_page* p; for (p = adj_list.data[i]; p; p = p->next) { for (j = 0; j < 16; ++j) { if (p->targets[j] != -1) { ++deg; if (p->targets[j] >= nverts_known) nverts_known = p->targets[j] + 1; } } } degrees[i] = deg; } int64_t nglobalverts = 0; MPI_Allreduce(&nverts_known, &nglobalverts, 1, INT64_T_MPI_TYPE, MPI_MAX, MPI_COMM_WORLD); g->nglobalverts = nglobalverts; /* Compute the final number of local vertices based on the global maximum * vertex number. */ size_t nlocalverts = VERTEX_LOCAL(nglobalverts + size - 1 - rank); g->nlocalverts = nlocalverts; grow_adj_list(&adj_list, nlocalverts); /* Build CSR data structure. */ size_t *rowstarts = (size_t*)xmalloc((nlocalverts + 1) * sizeof(size_t)); g->rowstarts = rowstarts; /* Compute offset to start of each row. */ rowstarts[0] = 0; for (i = 0; i < nlocalverts; ++i) { rowstarts[i + 1] = rowstarts[i] + (i >= nlocalverts_orig ? 0 : degrees[i]); } size_t nlocaledges = rowstarts[nlocalverts]; g->nlocaledges = nlocaledges; int64_t* column = (int64_t*)xmalloc(nlocaledges * sizeof(int64_t)); g->column = column; /* Append outgoing edges for each vertex to the column array, in order. */ for (i = 0; i < nlocalverts; ++i) { edge_page* p; int offset = 0; for (p = adj_list.data[i]; p; p = p->next, offset += 16) { size_t deg = (i >= nlocalverts_orig ? 0 : degrees[i]); size_t nelts = (deg - offset > 16) ? 16 : deg - offset; memcpy(column + rowstarts[i] + offset, p->targets, nelts * sizeof(int64_t)); } } free(degrees); degrees = NULL; clear_adj_list(&adj_list); } CombBLAS_beta_16_2/graph500-1.2/mpi/LICENSE_1_0.txt000644 000765 000024 00000002472 13212627400 022321 0ustar00aydinbulucstaff000000 000000 Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CombBLAS_beta_16_2/graph500-1.2/mpi/utils.c000644 000765 000024 00000004427 13212627400 021345 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include #include #include #include #include #include "common.h" int rank, size; #ifdef SIZE_MUST_BE_A_POWER_OF_TWO int lgsize, size_minus_one; #endif void setup_globals() { MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); #ifdef SIZE_MUST_BE_A_POWER_OF_TWO size_minus_one = size - 1; if (/* Check for power of 2 */ (size & (size - 1)) != 0) { fprintf(stderr, "Number of processes %d is not a power of two, yet SIZE_MUST_BE_A_POWER_OF_TWO is defined in main.cpp.\n", size); MPI_Abort(MPI_COMM_WORLD, 1); } for (lgsize = 0; lgsize < size; ++lgsize) { if ((1 << lgsize) == size) break; } assert (lgsize < size); #endif } void free_csr_graph(csr_graph* const g) { if (g->rowstarts != NULL) {free(g->rowstarts); g->rowstarts = NULL;} if (g->column != NULL) {free(g->column); g->column = NULL;} } /* These are in the graph generator. */ #if 0 void* xmalloc(size_t nbytes) { void* p = malloc(nbytes); if (!p) { fprintf(stderr, "malloc() failed for size %zu\n", nbytes); abort(); } return p; } void* xcalloc(size_t n, size_t unit) { void* p = calloc(n, unit); if (!p) { fprintf(stderr, "calloc() failed for size %zu * %zu\n", n, unit); abort(); } return p; } #endif void* xrealloc(void* p, size_t nbytes) { p = realloc(p, nbytes); if (!p && nbytes != 0) { fprintf(stderr, "realloc() failed for size %zu\n", nbytes); abort(); } return p; } void* xMPI_Alloc_mem(size_t nbytes) { void* p; MPI_Alloc_mem(nbytes, MPI_INFO_NULL, &p); if (!p) { fprintf(stderr, "MPI_Alloc_mem failed for size %zu\n", nbytes); abort(); } return p; } CombBLAS_beta_16_2/graph500-1.2/mpi/common.h000644 000765 000024 00000004630 13212627400 021476 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #ifndef COMMON_H #define COMMON_H #include #include #define INT64_T_MPI_TYPE MPI_LONG_LONG #define SIZE_MUST_BE_A_POWER_OF_TWO extern int rank, size; #ifdef SIZE_MUST_BE_A_POWER_OF_TWO extern int lgsize, size_minus_one; #endif /* Distribute edges by their endpoints (make two directed copies of each input * undirected edge); distribution is 1-d and cyclic. */ #ifdef SIZE_MUST_BE_A_POWER_OF_TWO #define MOD_SIZE(v) ((v) & size_minus_one) #define DIV_SIZE(v) ((v) >> lgsize) #else #define MOD_SIZE(v) ((v) % size) #define DIV_SIZE(v) ((v) / size) #endif #define VERTEX_OWNER(v) ((int)(MOD_SIZE(v))) #define VERTEX_LOCAL(v) ((size_t)(DIV_SIZE(v))) #define VERTEX_TO_GLOBAL(i) ((int64_t)((i) * size + rank)) typedef struct csr_graph { size_t nlocalverts; size_t nlocaledges; int64_t nglobalverts; size_t *rowstarts; int64_t *column; } csr_graph; #ifdef __cplusplus extern "C" { #endif void setup_globals(void); /* In utils.c */ void free_csr_graph(csr_graph* const g); /* In utils.c */ void* xMPI_Alloc_mem(size_t nbytes); /* In utils.c */ void* xmalloc(size_t nbytes); /* In utils.c */ void* xcalloc(size_t n, size_t unit); /* In utils.c */ void* xrealloc(void* p, size_t nbytes); /* In utils.c */ void convert_graph_to_csr(const int64_t nedges, const int64_t* const edges, csr_graph* const g); /* In convert_to_csr.c */ void find_bfs_roots(int *num_bfs_roots, const csr_graph* const g, const uint64_t seed1, const uint64_t seed2, int64_t* const bfs_roots); /* In find_roots.c */ int validate_bfs_result(const csr_graph* const g, const int64_t root, const int64_t* const pred, const int64_t nvisited); /* In validate.c */ void run_mpi_bfs(const csr_graph* const g, int64_t root, int64_t* pred, int64_t* nvisited); /* Provided by user */ #ifdef __cplusplus } #endif #endif /* COMMON_H */ CombBLAS_beta_16_2/graph500-1.2/mpi/main.c000644 000765 000024 00000024500 13212627400 021123 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #define GRAPHGEN_DISTRIBUTED_MEMORY #define GRAPH_GENERATOR_MPI /* These need to be before any possible inclusions of stdint.h or inttypes.h. * */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif #include "../generator/make_graph.h" #include "common.h" #include #include #include #include #include #include #include #include #include #include static int compare_doubles(const void* a, const void* b) { double aa = *(const double*)a; double bb = *(const double*)b; return (aa < bb) ? -1 : (aa == bb) ? 0 : 1; } enum {s_minimum, s_firstquartile, s_median, s_thirdquartile, s_maximum, s_mean, s_std, s_LAST}; static void get_statistics(const double x[], int n, double r[s_LAST]) { double temp; int i; /* Compute mean. */ temp = 0; for (i = 0; i < n; ++i) temp += x[i]; temp /= n; r[s_mean] = temp; /* Compute std. dev. */ temp = 0; for (i = 0; i < n; ++i) temp += (x[i] - r[s_mean]) * (x[i] - r[s_mean]); temp /= n - 1; r[s_std] = sqrt(temp); /* Sort x. */ double* xx = (double*)xmalloc(n * sizeof(double)); memcpy(xx, x, n * sizeof(double)); qsort(xx, n, sizeof(double), compare_doubles); /* Get order statistics. */ r[s_minimum] = xx[0]; r[s_firstquartile] = (xx[(n - 1) / 4] + xx[n / 4]) * .5; r[s_median] = (xx[(n - 1) / 2] + xx[n / 2]) * .5; r[s_thirdquartile] = (xx[n - 1 - (n - 1) / 4] + xx[n - 1 - n / 4]) * .5; r[s_maximum] = xx[n - 1]; /* Clean up. */ free(xx); } int main(int argc, char** argv) { MPI_Init(&argc, &argv); setup_globals(); /* Parse arguments. */ int SCALE = 16; double edgefactor = 16.; /* nedges / nvertices, i.e., 2*avg. degree */ if (argc >= 2) SCALE = atoi(argv[1]); if (argc >= 3) edgefactor = atof(argv[2]); if (argc <= 1 || argc >= 4 || SCALE == 0 || edgefactor == 0) { if (rank == 0) { fprintf(stderr, "Usage: %s SCALE edgefactor\n SCALE = log_2(# vertices) [integer, required]\n edgefactor = (# edges) / (# vertices) = .5 * (average vertex degree) [float, defaults to 16]\n(Random number seed and Kronecker initiator are in main.c)\n", argv[0]); } MPI_Abort(MPI_COMM_WORLD, 1); } uint64_t seed1 = 2, seed2 = 3; const double initiator[4] = {.57, .19, .19, .05}; int64_t nedges; int64_t* edges; /* Make the raw graph edges. */ double make_graph_start = MPI_Wtime(); make_graph(SCALE, (int64_t)(edgefactor * pow(2., SCALE)), seed1, seed2, initiator, &nedges, &edges); double make_graph_stop = MPI_Wtime(); double make_graph_time = make_graph_stop - make_graph_start; /* CSR graph data structure. */ csr_graph g; /* Make CSR data structure, redistributing data using VERTEX_OWNER * distribution. */ double data_struct_start = MPI_Wtime(); convert_graph_to_csr(nedges, edges, &g); double data_struct_stop = MPI_Wtime(); double data_struct_time = data_struct_stop - data_struct_start; free(edges); edges = NULL; /* Get roots for BFS runs. */ int num_bfs_roots = 64; int64_t* bfs_roots = (int64_t*)xmalloc(num_bfs_roots * sizeof(int64_t)); find_bfs_roots(&num_bfs_roots, &g, seed1, seed2, bfs_roots); /* Number of edges visited in each BFS; a double so get_statistics can be * used directly. */ double* edge_counts = (double*)xmalloc(num_bfs_roots * sizeof(double)); /* Run BFS. */ int validation_passed = 1; double* bfs_times = (double*)xmalloc(num_bfs_roots * sizeof(double)); double* validate_times = (double*)xmalloc(num_bfs_roots * sizeof(double)); int64_t* pred = (int64_t*)xMPI_Alloc_mem(g.nlocalverts * sizeof(int64_t)); int bfs_root_idx; for (bfs_root_idx = 0; bfs_root_idx < num_bfs_roots; ++bfs_root_idx) { int64_t root = bfs_roots[bfs_root_idx]; if (rank == 0) fprintf(stderr, "Running BFS %d\n", bfs_root_idx); /* Clear the pred array. */ memset(pred, 0, g.nlocalverts * sizeof(int64_t)); /* Do the actual BFS. */ double bfs_start = MPI_Wtime(); int64_t nvisited = 0; run_mpi_bfs(&g, root, &pred[0], &nvisited); double bfs_stop = MPI_Wtime(); bfs_times[bfs_root_idx] = bfs_stop - bfs_start; /* Validate result. */ if (rank == 0) fprintf(stderr, "Validating BFS %d\n", bfs_root_idx); double validate_start = MPI_Wtime(); int validation_passed_one = validate_bfs_result(&g, root, pred, nvisited); double validate_stop = MPI_Wtime(); validate_times[bfs_root_idx] = validate_stop - validate_start; if (!validation_passed_one) { validation_passed = 0; if (rank == 0) fprintf(stderr, "Validation failed for this BFS root; skipping rest.\n"); break; } /* Calculate number of input edges visited. */ { int64_t edge_visit_count = 0; size_t v_local; for (v_local = 0; v_local < g.nlocalverts; ++v_local) { if (pred[v_local] != -1) { size_t ei, ei_end = g.rowstarts[v_local + 1]; for (ei = g.rowstarts[v_local]; ei < ei_end; ++ei) { /* Test here is so that each input edge is counted exactly once, even * though non-self-loops are duplicated in the CSR data structure. */ if (g.column[ei] <= VERTEX_TO_GLOBAL(v_local)) { ++edge_visit_count; } } } } MPI_Allreduce(MPI_IN_PLACE, &edge_visit_count, 1, INT64_T_MPI_TYPE, MPI_SUM, MPI_COMM_WORLD); edge_counts[bfs_root_idx] = (double)edge_visit_count; } } MPI_Free_mem(pred); free(bfs_roots); free_csr_graph(&g); /* Print results. */ if (rank == 0) { if (!validation_passed) { fprintf(stdout, "No results printed for invalid run.\n"); } else { int i; fprintf(stdout, "SCALE: %d\n", SCALE); fprintf(stdout, "edgefactor: %.2g\n", edgefactor); fprintf(stdout, "NBFS: %d\n", num_bfs_roots); fprintf(stdout, "graph_generation: %g s\n", make_graph_time); fprintf(stdout, "num_mpi_processes: %d\n", size); fprintf(stdout, "construction_time: %g s\n", data_struct_time); double stats[s_LAST]; get_statistics(bfs_times, num_bfs_roots, stats); fprintf(stdout, "min_time: %g s\n", stats[s_minimum]); fprintf(stdout, "firstquartile_time: %g s\n", stats[s_firstquartile]); fprintf(stdout, "median_time: %g s\n", stats[s_median]); fprintf(stdout, "thirdquartile_time: %g s\n", stats[s_thirdquartile]); fprintf(stdout, "max_time: %g s\n", stats[s_maximum]); fprintf(stdout, "mean_time: %g s\n", stats[s_mean]); fprintf(stdout, "stddev_time: %g\n", stats[s_std]); get_statistics(edge_counts, num_bfs_roots, stats); fprintf(stdout, "min_nedge: %.11g\n", stats[s_minimum]); fprintf(stdout, "firstquartile_nedge: %.11g\n", stats[s_firstquartile]); fprintf(stdout, "median_nedge: %.11g\n", stats[s_median]); fprintf(stdout, "thirdquartile_nedge: %.11g\n", stats[s_thirdquartile]); fprintf(stdout, "max_nedge: %.11g\n", stats[s_maximum]); fprintf(stdout, "mean_nedge: %.11g\n", stats[s_mean]); fprintf(stdout, "stddev_nedge: %.11g\n", stats[s_std]); double* secs_per_edge = (double*)xmalloc(num_bfs_roots * sizeof(double)); for (i = 0; i < num_bfs_roots; ++i) secs_per_edge[i] = bfs_times[i] / edge_counts[i]; get_statistics(secs_per_edge, num_bfs_roots, stats); fprintf(stdout, "min_TEPS: %g TEPS\n", 1. / stats[s_maximum]); fprintf(stdout, "firstquartile_TEPS: %g TEPS\n", 1. / stats[s_thirdquartile]); fprintf(stdout, "median_TEPS: %g TEPS\n", 1. / stats[s_median]); fprintf(stdout, "thirdquartile_TEPS: %g TEPS\n", 1. / stats[s_firstquartile]); fprintf(stdout, "max_TEPS: %g TEPS\n", 1. / stats[s_minimum]); fprintf(stdout, "harmonic_mean_TEPS: %g TEPS\n", 1. / stats[s_mean]); /* Formula from: * Title: The Standard Errors of the Geometric and Harmonic Means and * Their Application to Index Numbers * Author(s): Nilan Norris * Source: The Annals of Mathematical Statistics, Vol. 11, No. 4 (Dec., 1940), pp. 445-448 * Publisher(s): Institute of Mathematical Statistics * Stable URL: http://www.jstor.org/stable/2235723 * (same source as in specification). */ fprintf(stdout, "harmonic_stddev_TEPS: %g\n", stats[s_std] / (stats[s_mean] * stats[s_mean] * sqrt(num_bfs_roots - 1))); free(secs_per_edge); secs_per_edge = NULL; free(edge_counts); edge_counts = NULL; get_statistics(validate_times, num_bfs_roots, stats); fprintf(stdout, "min_validate: %g s\n", stats[s_minimum]); fprintf(stdout, "firstquartile_validate: %g s\n", stats[s_firstquartile]); fprintf(stdout, "median_validate: %g s\n", stats[s_median]); fprintf(stdout, "thirdquartile_validate: %g s\n", stats[s_thirdquartile]); fprintf(stdout, "max_validate: %g s\n", stats[s_maximum]); fprintf(stdout, "mean_validate: %g s\n", stats[s_mean]); fprintf(stdout, "stddev_validate: %g\n", stats[s_std]); #if 0 for (i = 0; i < num_bfs_roots; ++i) { fprintf(stdout, "Run %3d: %g s, validation %g s\n", i + 1, bfs_times[i], validate_times[i]); } #endif } } free(bfs_times); free(validate_times); MPI_Finalize(); return 0; } CombBLAS_beta_16_2/graph500-1.2/mpi/find_roots.c000644 000765 000024 00000004413 13212627400 022346 0ustar00aydinbulucstaff000000 000000 /* Copyright (C) 2010 The Trustees of Indiana University. */ /* */ /* Use, modification and distribution is subject to the Boost Software */ /* License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at */ /* http://www.boost.org/LICENSE_1_0.txt) */ /* */ /* Authors: Jeremiah Willcock */ /* Andrew Lumsdaine */ #include "common.h" #include "../generator/make_graph.h" #include #include #include #include #include #include /* Find num_bfs_roots random vertices, each of which has degree >= 1, with the * same answer produced on all nodes. */ void find_bfs_roots(int* num_bfs_roots, const csr_graph* const g, const uint64_t seed1, const uint64_t seed2, int64_t* const bfs_roots) { /* This implementation is slow, but there aren't enough roots being * generated for that to be a big issue. */ uint64_t counter = 0; int bfs_root_idx; for (bfs_root_idx = 0; bfs_root_idx < *num_bfs_roots; ++bfs_root_idx) { int64_t root; while (1) { double d[2]; make_random_numbers(2, seed1, seed2, counter, d); root = (int64_t)((d[0] + d[1]) * g->nglobalverts) % g->nglobalverts; counter += 2; if (counter > 2*g->nglobalverts) break; int is_duplicate = 0; int i; for (i = 0; i < bfs_root_idx; ++i) { if (root == bfs_roots[i]) { is_duplicate = 1; break; } } if (is_duplicate) continue; /* Everyone takes the same path here */ int root_ok; if (VERTEX_OWNER(root) == rank) { root_ok = 0; size_t ei, ei_end = g->rowstarts[VERTEX_LOCAL(root) + 1]; for (ei = g->rowstarts[VERTEX_LOCAL(root)]; ei < ei_end; ++ei) { if (g->column[ei] != root) { root_ok = 1; break; } } } MPI_Bcast(&root_ok, 1, MPI_INT, VERTEX_OWNER(root), MPI_COMM_WORLD); if (root_ok) break; } bfs_roots[bfs_root_idx] = root; } *num_bfs_roots = bfs_root_idx; } CombBLAS_beta_16_2/graph500-1.2/seq-csr/seq-csr.c000644 000765 000024 00000010015 13212627400 022340 0ustar00aydinbulucstaff000000 000000 /* -*- mode: C; mode: folding; fill-column: 70; -*- */ /* Copyright 2010, Georgia Institute of Technology, USA. */ /* See COPYING for license. */ #define _FILE_OFFSET_BITS 64 #define _THREAD_SAFE #include #include #include #include #include #include "../compat.h" #include "../graph500.h" #include "../xalloc.h" #define MINVECT_SIZE 2 static int64_t maxvtx, nv, sz; static int64_t * restrict xoff; /* Length 2*nv+2 */ static int64_t * restrict xadjstore; /* Length MINVECT_SIZE + (xoff[nv] == nedge) */ static int64_t * restrict xadj; static void find_nv (const int64_t * restrict IJ, const int64_t nedge) { int64_t k; maxvtx = -1; for (k = 0; k < 2*nedge; ++k) if (IJ[k] > maxvtx) maxvtx = IJ[k]; nv = 1+maxvtx; } static int alloc_graph (int64_t nedge) { sz = (2*nv+2) * sizeof (*xoff); xoff = xmalloc_large_ext (sz); if (!xoff) return -1; return 0; } static void free_graph (void) { xfree_large (xadjstore); xfree_large (xoff); } #define XOFF(k) (xoff[2*(k)]) #define XENDOFF(k) (xoff[1+2*(k)]) static int setup_deg_off (const int64_t * restrict IJ, int64_t nedge) { int64_t k, accum; for (k = 0; k < 2*nv+2; ++k) xoff[k] = 0; for (k = 0; k < 2*nedge; k+=2) if (IJ[k] != IJ[k+1]) { /* Skip self-edges. */ if (IJ[k] >= 0) ++XOFF(IJ[k]); if (IJ[k+1] >= 0) ++XOFF(IJ[k+1]); } accum = 0; for (k = 0; k < nv; ++k) { int64_t tmp = XOFF(k); if (tmp < MINVECT_SIZE) tmp = MINVECT_SIZE; XOFF(k) = accum; accum += tmp; } XOFF(nv) = accum; for (k = 0; k < nv; ++k) XENDOFF(k) = XOFF(k); if (!(xadjstore = xmalloc_large_ext ((accum + MINVECT_SIZE) * sizeof (*xadjstore)))) return -1; xadj = &xadjstore[MINVECT_SIZE]; /* Cheat and permit xadj[-1] to work. */ for (k = 0; k < accum + MINVECT_SIZE; ++k) xadjstore[k] = -1; return 0; } static void scatter_edge (const int64_t i, const int64_t j) { int64_t where; where = XENDOFF(i)++; xadj[where] = j; } static int i64cmp (const void *a, const void *b) { const int64_t ia = *(const int64_t*)a; const int64_t ib = *(const int64_t*)b; if (ia < ib) return -1; if (ia > ib) return 1; return 0; } static void pack_vtx_edges (const int64_t i) { int64_t kcur, k; if (XOFF(i)+1 <= XENDOFF(i)) return; qsort (&xadj[XOFF(i)], XENDOFF(i)-XOFF(i), sizeof(*xadj), i64cmp); kcur = XOFF(i); for (k = XOFF(i)+1; k < XENDOFF(i); ++k) if (xadj[k] != xadj[kcur]) xadj[++kcur] = xadj[k]; ++kcur; for (k = kcur; k < XENDOFF(i); ++k) xadj[k] = -1; XENDOFF(i) = kcur; } static void pack_edges (void) { int64_t v; for (v = 0; v < nv; ++v) pack_vtx_edges (v); } static void gather_edges (const int64_t * restrict IJ, int64_t nedge) { int64_t k; for (k = 0; k < 2*nedge; k += 2) if (IJ[k] >= 0 && IJ[k+1] >= 0 && IJ[k] != IJ[k+1]) { scatter_edge (IJ[k], IJ[k+1]); scatter_edge (IJ[k+1], IJ[k]); } pack_edges (); } int create_graph_from_edgelist (int64_t *IJ, int64_t nedge) { find_nv (IJ, nedge); if (alloc_graph (nedge)) return -1; if (setup_deg_off (IJ, nedge)) { xfree_large (xoff); return -1; } gather_edges (IJ, nedge); return 0; } int make_bfs_tree (int64_t *bfs_tree_out, int64_t *max_vtx_out, int64_t srcvtx) { int64_t * restrict bfs_tree = bfs_tree_out; int err = 0; int64_t * restrict vlist = NULL; int64_t k1, k2; *max_vtx_out = maxvtx; vlist = xmalloc_large (nv * sizeof (vlist)); if (!vlist) return -1; for (k1 = 0; k1 < nv; ++k1) bfs_tree[k1] = -1; vlist[0] = srcvtx; bfs_tree[srcvtx] = srcvtx; k1 = 0; k2 = 1; while (k1 != k2) { const int64_t oldk2 = k2; int64_t k; for (k = k1; k < oldk2; ++k) { const int64_t v = vlist[k]; const int64_t veo = XENDOFF(v); int64_t vo; for (vo = XOFF(v); vo < veo; ++vo) { const int64_t j = xadj[vo]; if (bfs_tree[j] == -1) { bfs_tree[j] = v; vlist[k2++] = j; } } } k1 = oldk2; } xfree_large (vlist); return err; } void destroy_graph (void) { free_graph (); } CombBLAS_beta_16_2/Ordering/MatPermuteSave.cpp000644 000765 000024 00000005004 13271404146 022604 0ustar00aydinbulucstaff000000 000000 #define DETERMINISTIC 1 #ifdef THREADED #ifndef _OPENMP #define _OPENMP // should be defined before any COMBBLAS header is included #endif #include #endif #include "CombBLAS/CombBLAS.h" #include #include #include #include #include #include #include #include using namespace std; using namespace combblas; typedef SpParMat < int64_t, bool, SpDCCols > Par_DCSC_Bool; typedef SpParMat < int64_t, int64_t, SpDCCols > Par_DCSC_int64_t; typedef SpParMat < int64_t, double, SpDCCols > Par_DCSC_Double; int main(int argc, char* argv[]) { int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 2) { if(myrank == 0) { cout << "Usage: ./rcm " << endl; } MPI_Finalize(); return -1; } { Par_DCSC_Bool * ABool; ostringstream tinfo; ABool = new Par_DCSC_Bool(); string filename(argv[1]); tinfo.str(""); tinfo << "**** Reading input matrix: " << filename << " ******* " << endl; SpParHelper::Print(tinfo.str()); double t01 = MPI_Wtime(); ABool->ParallelReadMM(filename, true, maximum()); double t02 = MPI_Wtime(); tinfo.str(""); tinfo << "Reader took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); if(ABool->getnrow() == ABool->getncol()) { FullyDistVec p( ABool->getcommgrid()); p.iota(ABool->getnrow(), 0); p.RandPerm(); (*ABool)(p,p,true);// in-place permute to save memory SpParHelper::Print("Applied symmetric permutation.\n"); } else { SpParHelper::Print("Rectangular matrix: Can not apply symmetric permutation.\n"); } filename += "_permuted"; ABool->SaveGathered(filename); tinfo.str(""); tinfo << "**** Saved to output matrix: " << filename << " ******* " << endl; SpParHelper::Print(tinfo.str()); delete ABool; } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Ordering/CMakeLists.txt000644 000765 000024 00000000323 13271404146 021735 0ustar00aydinbulucstaff000000 000000 # Top level directory has the include files ADD_EXECUTABLE( rcm RCM.cpp ) TARGET_LINK_LIBRARIES( rcm CombBLAS) ADD_TEST(NAME RCM_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ er 18 ) CombBLAS_beta_16_2/Ordering/._.DS_Store000644 000765 000024 00000000170 13271513341 021073 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/Ordering/.DS_Store000644 000765 000024 00000014004 13271513341 020657 0ustar00aydinbulucstaff000000 000000 Bud1%  @€ @€ @€ @ E%DSDB`€ @€ @€ @CombBLAS_beta_16_2/Ordering/test.mtx000644 000765 000024 00000000166 13212627400 020706 0ustar00aydinbulucstaff000000 000000 %%MatrixMarket matrix coordinate real symmetric 6 6 8 1 2 1.0 1 6 1.0 2 4 1.0 3 5 1.0 3 6 1.0 4 5 1.0 4 6 1.0 5 6 1.0 CombBLAS_beta_16_2/Ordering/gathertest.cpp000644 000765 000024 00000010742 13271404146 022061 0ustar00aydinbulucstaff000000 000000 //#define DETERMINISTIC 1 #ifdef THREADED #ifndef _OPENMP #define _OPENMP // should be defined before any COMBBLAS header is included #endif #include #endif #include "CombBLAS/CombBLAS.h" #include #include #include #include #include #include #include #include #define EDGEFACTOR 16 #define RAND_PERMUTE 1 #ifdef DETERMINISTIC MTRand GlobalMT(1); #else MTRand GlobalMT; // generate random numbers with Mersenne Twister #endif double cblas_alltoalltime; double cblas_allgathertime; double cblas_localspmvtime; double cblas_mergeconttime; double cblas_transvectime; using namespace std; using namespace combblas; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); AT.RemoveLoops(); // not needed for boolean matrices, but no harm in keeping it A += AT; } typedef SpParMat < int64_t, bool, SpDCCols > Par_DCSC_Bool; typedef SpParMat < int64_t, int64_t, SpDCCols > Par_DCSC_int64_t; typedef SpParMat < int64_t, double, SpDCCols > Par_DCSC_Double; typedef SpParMat < int64_t, bool, SpCCols > Par_CSC_Bool; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./rcm " << endl; cout << "Example: mpirun -np 4 ./rcm rmat 20" << endl; cout << "Example: mpirun -np 4 ./rcm er 20" << endl; cout << "Example: mpirun -np 4 ./rcm input a.mtx" << endl; } MPI_Finalize(); return -1; } { Par_DCSC_Bool * ABool; Par_DCSC_Bool AAT; bool unsym=false; ostringstream tinfo; if(string(argv[1]) == string("input")) // input option { ABool = new Par_DCSC_Bool(); string filename(argv[2]); tinfo.str(""); tinfo << "**** Reading input matrix: " << filename << " ******* " << endl; SpParHelper::Print(tinfo.str()); double t01 = MPI_Wtime(); ABool->ParallelReadMM(filename, false, maximum()); double t02 = MPI_Wtime(); int64_t bw = ABool->Bandwidth(); int64_t pf = ABool->Profile(); tinfo.str(""); tinfo << "Reader took " << t02-t01 << " seconds" << endl; tinfo << "Bandwidth before random permutation " << bw << endl; tinfo << "Profile before random permutation " << pf << endl; SpParHelper::Print(tinfo.str()); } else if(string(argv[1]) == string("rmat")) { unsigned scale; scale = static_cast(atoi(argv[2])); double initiator[4] = {.57, .19, .19, .05}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); MPI_Barrier(MPI_COMM_WORLD); ABool = new Par_DCSC_Bool(*DEL, false); Symmetricize(*ABool); delete DEL; } else if(string(argv[1]) == string("er")) { unsigned scale; scale = static_cast(atoi(argv[2])); double initiator[4] = {.25, .25, .25, .25}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); MPI_Barrier(MPI_COMM_WORLD); ABool = new Par_DCSC_Bool(*DEL, false); Symmetricize(*ABool); delete DEL; } else { SpParHelper::Print("Unknown input option\n"); MPI_Finalize(); return -1; } MPI_Comm com = ABool->getcommgrid()->GetWorld(); double gtime = MPI_Wtime(); SpParHelper::GatherMatrix(com, ABool->seq(), (int)0); if(myrank==0) { cout << "gathertime " << MPI_Wtime() - gtime << endl; } delete ABool; } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Ordering/RCM.cpp000644 000765 000024 00000072451 13271404146 020335 0ustar00aydinbulucstaff000000 000000 #ifdef THREADED #ifndef _OPENMP #define _OPENMP // should be defined before any COMBBLAS header is included #endif #include #endif #include "CombBLAS/CombBLAS.h" #include #include #include #include #include #include #include #include #define EDGEFACTOR 16 #ifdef DETERMINISTIC MTRand GlobalMT(1); #else MTRand GlobalMT; // generate random numbers with Mersenne Twister #endif double cblas_alltoalltime; double cblas_allgathertime; double cblas_localspmvtime; double cblas_mergeconttime; double cblas_transvectime; using namespace std; using namespace combblas; int threads, processors; string base_filename; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); AT.RemoveLoops(); // not needed for boolean matrices, but no harm in keeping it A += AT; } struct VertexType { public: VertexType(int64_t ord=-1, int64_t deg=-1){order=ord; degree = deg;}; friend bool operator<(const VertexType & vtx1, const VertexType & vtx2 ) { if(vtx1.order==vtx2.order) return vtx1.degree < vtx2.degree; else return vtx1.order(const VertexType & vtx1, const VertexType & vtx2 ) { if(vtx1.order==vtx2.order) return vtx1.degree > vtx2.degree; else return vtx1.order>vtx2.order; }; friend bool operator>=(const VertexType & vtx1, const VertexType & vtx2 ) { if(vtx1.order==vtx2.order) return vtx1.degree >= vtx2.degree; else return vtx1.order>vtx2.order; //if(vtx1.order==vtx2.order) return vtx1.degree <= vtx2.degree; //else return vtx1.order > Par_DCSC_Bool; typedef SpParMat < int64_t, int64_t, SpDCCols > Par_DCSC_int64_t; typedef SpParMat < int64_t, double, SpDCCols > Par_DCSC_Double; typedef SpParMat < int64_t, bool, SpCCols > Par_CSC_Bool; FullyDistSpVec getOrder(FullyDistSpVec &fringeRow, int64_t startLabel, int64_t endLabel) { int myrank, nprocs; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); vector lind = fringeRow.GetLocalInd (); vector lnum = fringeRow.GetLocalNum (); int64_t ploclen = lind.size(); int64_t nparents = endLabel - startLabel + 1; int64_t perproc = nparents/nprocs; int * rdispls = new int[nprocs+1]; int * recvcnt = new int[nprocs]; int * sendcnt = new int[nprocs](); // initialize to 0 int * sdispls = new int[nprocs+1]; MPI_Barrier(MPI_COMM_WORLD); #ifdef _OPENMP #pragma omp parallel for #endif for(int64_t k=0; k < ploclen; ++k) { int64_t temp = lnum[k].order-startLabel; int owner; if(perproc==0 || temp/perproc > nprocs-1) owner = nprocs-1; else owner = temp/perproc; #ifdef _OPENMP __sync_fetch_and_add(&sendcnt[owner], 1); #else sendcnt[owner]++; #endif } MPI_Barrier(MPI_COMM_WORLD); MPI_Alltoall(sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, MPI_COMM_WORLD); // share the request counts sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i nprocs-1) owner = nprocs-1; else owner = temp/perproc; int id; #ifdef _OPENMP id = sdispls[owner] + __sync_fetch_and_add(&count[owner], 1); #else id = sdispls[owner] + count[owner]; count[owner]++; #endif datbuf1[id] = temp; datbuf2[id] = lnum[i].degree; indbuf[id] = lind[i] + fringeRow.LengthUntil(); } delete [] count; int64_t totrecv = rdispls[nprocs]; int64_t * recvdatbuf1 = new int64_t[totrecv]; int64_t * recvdatbuf2 = new int64_t[totrecv]; MPI_Alltoallv(datbuf1, sendcnt, sdispls, MPIType(), recvdatbuf1, recvcnt, rdispls, MPIType(), MPI_COMM_WORLD); delete [] datbuf1; MPI_Alltoallv(datbuf2, sendcnt, sdispls, MPIType(), recvdatbuf2, recvcnt, rdispls, MPIType(), MPI_COMM_WORLD); delete [] datbuf2; int64_t * recvindbuf = new int64_t[totrecv]; MPI_Alltoallv(indbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), MPI_COMM_WORLD); delete [] indbuf; tuple* tosort = static_cast*> (::operator new (sizeof(tuple)*totrecv)); #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i(tosort[k]), locind); #ifdef _OPENMP __sync_fetch_and_add(&sendcnt1[owner], 1); #else sendcnt1[owner]++; #endif } MPI_Alltoall(sendcnt1, 1, MPI_INT, recvcnt, 1, MPI_INT, MPI_COMM_WORLD); // share the request counts sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i sortperproc (nprocs); sortperproc[myrank] = totrecv; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), sortperproc.data(), 1, MPIType(), MPI_COMM_WORLD); vector disp(nprocs+1); disp[0] = 0; for(int i=0; i(tosort[i]), locind); int id; #ifdef _OPENMP id = sdispls[owner] + __sync_fetch_and_add(&count[owner], 1); #else id = sdispls[owner] + count[owner]; count[owner]++; #endif datbuf[id] = i + disp[myrank] + endLabel + 1; //cout << datbuf[id] << endl; indbuf[id] = locind; } delete [] count; totrecv = rdispls[nprocs]; vector recvdatbuf3 (totrecv); MPI_Alltoallv(datbuf, sendcnt1, sdispls, MPIType(), recvdatbuf3.data(), recvcnt, rdispls, MPIType(), MPI_COMM_WORLD); delete [] datbuf; vector recvindbuf3 (totrecv); MPI_Alltoallv(indbuf, sendcnt1, sdispls, MPIType(), recvindbuf3.data(), recvcnt, rdispls, MPIType(), MPI_COMM_WORLD); delete [] indbuf; FullyDistSpVec order(fringeRow.getcommgrid(), fringeRow.TotalLength(), recvindbuf3, recvdatbuf3); DeleteAll(recvindbuf, recvdatbuf1, recvdatbuf2); DeleteAll(sdispls, rdispls, sendcnt, sendcnt1, recvcnt); ::operator delete(tosort); return order; } double torderSpMV=0, torderSort=0, torderOther=0; // perform ordering from a pseudo peripheral vertex template void RCMOrder(PARMAT & A, int64_t source, FullyDistVec& order, int64_t startOrder, FullyDistVec degrees, PreAllocatedSPA& SPA) { int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); double tSpMV=0, tOrder, tOther, tSpMV1, tsort=0, tsort1; tOrder = MPI_Wtime(); int64_t nv = A.getnrow(); FullyDistSpVec fringe(A.getcommgrid(), nv ); order.SetElement(source, startOrder); fringe.SetElement(source, startOrder); int64_t curOrder = startOrder+1; if(myrank == 0) cout << " Computing the RCM ordering:" << endl; int64_t startLabel = startOrder; int64_t endLabel = startOrder; while(startLabel <= endLabel) // continue until the frontier is empty { fringe = EWiseApply(fringe, order, [](int64_t parent_order, int64_t ord){return ord;}, [](int64_t parent_order, int64_t ord){return true;}, false, (int64_t) -1); tSpMV1 = MPI_Wtime(); SpMV(A, fringe, fringe, false, SPA); tSpMV += MPI_Wtime() - tSpMV1; fringe = EWiseMult(fringe, order, true, (int64_t) -1); FullyDistSpVec fringeRow = EWiseApply(fringe, degrees, [](int64_t parent_order, int64_t degree){return VertexType(parent_order, degree);}, [](int64_t parent_order, int64_t degree){return true;}, false, (int64_t) -1); tsort1 = MPI_Wtime(); FullyDistSpVec levelOrder = getOrder(fringeRow, startLabel, endLabel); tsort += MPI_Wtime()-tsort1; order.Set(levelOrder); startLabel = endLabel + 1; endLabel += fringe.getnnz(); } tOrder = MPI_Wtime() - tOrder; tOther = tOrder - tSpMV - tsort; if(myrank == 0) { cout << " Total time: " << tOrder << " seconds [SpMV: " << tSpMV << ", sorting: " << tsort << ", other: " << tOther << "]" << endl << endl; } torderSpMV+=tSpMV; torderSort+=tsort; torderOther+=tOther; } template int64_t PseudoPeripheralVertex(PARMAT & A, FullyDistSpVec>& unvisitedVertices, FullyDistVec degrees, PreAllocatedSPA& SPA) { int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); double tpvSpMV=0, tpvOther=0; double tstart = MPI_Wtime(); int64_t prevLevel=-1, curLevel=0; // initialized just to make the first iteration going // Select a minimum-degree unvisited vertex as the initial source pair mindegree_vertex = unvisitedVertices.Reduce(minimum >(), make_pair(LLONG_MAX, (int64_t)-1)); int64_t source = mindegree_vertex.second; //level structure in the current BFS tree //we are not using this information. Currently it is serving as visited flag FullyDistVec level ( A.getcommgrid(), A.getnrow(), (int64_t) -1); int iterations = 0; double tSpMV=0, tOther=0, tSpMV1; if(myrank == 0) cout << " Computing a pseudo-peripheral vertex:" << endl; while(curLevel > prevLevel) { double tItr = MPI_Wtime(); prevLevel = curLevel; FullyDistSpVec fringe(A.getcommgrid(), A.getnrow() ); level = (int64_t)-1; // reset level structure in every iteration level.SetElement(source, 1); // place source at level 1 fringe.SetElement(source, 1); // include source to the initial fringe curLevel = 2; while(fringe.getnnz() > 0) // continue until the frontier is empty { tSpMV1 = MPI_Wtime(); SpMV(A, fringe, fringe, false, SPA); tSpMV += MPI_Wtime() - tSpMV1; fringe = EWiseMult(fringe, level, true, (int64_t) -1); // set value to the current level fringe=curLevel; curLevel++; level.Set(fringe); } curLevel = curLevel-2; // last non-empty level (we can avoid this by keeping the last nonempty fringe) fringe = level.Find(curLevel); fringe.setNumToInd(); // find a minimum degree vertex in the last level FullyDistSpVec> fringe_degree = EWiseApply>(fringe, degrees, [](int64_t vtx, int64_t deg){return make_pair(deg, vtx);}, [](int64_t vtx, int64_t deg){return true;}, false, (int64_t) -1); mindegree_vertex = fringe_degree.Reduce(minimum >(), make_pair(LLONG_MAX, (int64_t)-1)); if (curLevel > prevLevel) source = mindegree_vertex.second; iterations++; if(myrank == 0) { cout <<" iteration: "<< iterations << " BFS levels: " << curLevel << " Time: " << MPI_Wtime() - tItr << " seconds." << endl; } } // remove vertices in the current connected component //unvisitedVertices = EWiseMult(unvisitedVertices, level, true, (int64_t) -1); unvisitedVertices = EWiseApply>(unvisitedVertices, level, [](pair vtx, int64_t visited){return vtx;}, [](pair vtx, int64_t visited){return visited==-1;}, false, make_pair((int64_t)-1, (int64_t)0)); tOther = MPI_Wtime() - tstart - tSpMV; tpvSpMV += tSpMV; tpvOther += tOther; if(myrank == 0) { cout << " vertex " << source << " is a pseudo peripheral vertex" << endl; cout << " pseudo diameter: " << curLevel << ", #iterations: "<< iterations << endl; cout << " Total time: " << MPI_Wtime() - tstart << " seconds [SpMV: " << tSpMV << ", other: " << tOther << "]" << endl << endl; } return source; } template FullyDistVec RCM(PARMAT & A, FullyDistVec degrees, PreAllocatedSPA& SPA) { #ifdef TIMING cblas_allgathertime = 0; cblas_alltoalltime = 0; cblas_mergeconttime = 0; cblas_transvectime = 0; cblas_localspmvtime = 0; #endif int myrank, nprocs; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); FullyDistSpVec unvisited ( A.getcommgrid(), A.getnrow()); unvisited.iota(A.getnrow(), (int64_t) 0); // index and values become the same // The list of unvisited vertices. The value is (degree, vertex index) pair FullyDistSpVec> unvisitedVertices = EWiseApply>(unvisited, degrees, [](int64_t vtx, int64_t deg){return make_pair(deg, vtx);}, [](int64_t vtx, int64_t deg){return true;}, false, (int64_t) -1); // The RCM order will be stored here FullyDistVec rcmorder ( A.getcommgrid(), A.getnrow(), (int64_t) -1); int cc = 1; // current connected component int64_t numUnvisited = unvisitedVertices.getnnz(); while(numUnvisited>0) // for each connected component { if(myrank==0) cout << "Connected component: " << cc++ << endl; // Get a pseudo-peripheral vertex to start the RCM algorithm int64_t source = PseudoPeripheralVertex(A, unvisitedVertices, degrees,SPA); // Get the RCM ordering in this connected component int64_t curOrder = A.getnrow() - numUnvisited; RCMOrder(A, source, rcmorder, curOrder, degrees, SPA); numUnvisited = unvisitedVertices.getnnz(); } #ifdef TIMING double *td_ag_all, *td_a2a_all, *td_tv_all, *td_mc_all, *td_spmv_all; if(myrank == 0) { td_ag_all = new double[nprocs]; td_a2a_all = new double[nprocs]; td_tv_all = new double[nprocs]; td_mc_all = new double[nprocs]; td_spmv_all = new double[nprocs]; } MPI_Gather(&cblas_allgathertime, 1, MPI_DOUBLE, td_ag_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_alltoalltime, 1, MPI_DOUBLE, td_a2a_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_transvectime, 1, MPI_DOUBLE, td_tv_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_mergeconttime, 1, MPI_DOUBLE, td_mc_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); MPI_Gather(&cblas_localspmvtime, 1, MPI_DOUBLE, td_spmv_all, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); double td_ag_all1=0, td_a2a_all1=0, td_tv_all1=0, td_mc_all1=0,td_spmv_all1 = 0; if(myrank == 0) { vector total_time(nprocs, 0); for(int i=0; i< nprocs; ++i) // find the mean performing guy total_time[i] += td_ag_all[i] + td_a2a_all[i] + td_tv_all[i] + td_mc_all[i] + td_spmv_all[i]; // order vector> tosort; for(int i=0; i permutation = SpHelper::order(total_time); vector permutation(nprocs); for(int i=0; i (nprocs) << endl; cout << "TOTAL (accounted) MAX: " << total_time[0] << endl; cout << "TOTAL (accounted) MIN: " << total_time[nprocs-1] << endl; cout << "TOTAL (accounted) MEDIAN: " << total_time[nprocs/2] << endl; cout << "-------------------------------" << endl; cout << "allgather median: " << td_ag_all[median] << endl; cout << "all2all median: " << td_a2a_all[median] << endl; cout << "transposevector median: " << td_tv_all[median] << endl; cout << "mergecontributions median: " << td_mc_all[median] << endl; cout << "spmsv median: " << td_spmv_all[median] << endl; cout << "-------------------------------" << endl; td_ag_all1=td_ag_all[median]; td_a2a_all1=td_a2a_all[median]; td_tv_all1=td_tv_all[median]; td_mc_all1=td_mc_all[median]; td_spmv_all1 = td_spmv_all[median]; cout << "allgather fastest: " << td_ag_all[smallest] << endl; cout << "all2all fastest: " << td_a2a_all[smallest] << endl; cout << "transposevector fastest: " << td_tv_all[smallest] << endl; cout << "mergecontributions fastest: " << td_mc_all[smallest] << endl; cout << "spmsv fastest: " << td_spmv_all[smallest] << endl; cout << "-------------------------------" << endl; cout << "allgather slowest: " << td_ag_all[largest] << endl; cout << "all2all slowest: " << td_a2a_all[largest] << endl; cout << "transposevector slowest: " << td_tv_all[largest] << endl; cout << "mergecontributions slowest: " << td_mc_all[largest] << endl; cout << "spmsv slowest: " << td_spmv_all[largest] << endl; } if(myrank == 0) { cout << "summary statistics" << endl; cout << base_filename << " " << processors << " " << threads << " " << processors * threads << " "<< torderSpMV << " "<< torderSort<< " "<< torderOther<< " "<< td_ag_all1 << " "<< td_a2a_all1 << " "<< td_tv_all1 << " "<< td_mc_all1 << " "<< td_spmv_all1 << " "<< endl; } #endif return rcmorder; } int main(int argc, char* argv[]) { int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./rcm " << "-permute" << " -savercm" << endl; cout << "Example with a user supplied matrix:" << endl; cout << " mpirun -np 4 ./rcm input a.mtx" << endl; cout << "Example with a user supplied matrix (pre-permute the input matrix for load balance):" << endl; cout << " mpirun -np 4 ./rcm input a.mtx -permute " << endl; cout << "Example with a user supplied matrix (pre-permute the input matrix for load balance) & save rcm order to input_file_name.rcm.txt file:" << endl; cout << " mpirun -np 4 ./rcm input a.mtx -permute -savercm" << endl; cout << "Example with RMAT matrix: mpirun -np 4 ./rcm rmat 20" << endl; cout << "Example with an Erdos-Renyi matrix: mpirun -np 4 ./rcm er 20" << endl; } MPI_Finalize(); return -1; } { string filename=""; bool randpermute = false; bool savercm = false; for (int i = 1; i < argc; i++) { if (strcmp(argv[i],"-permute")==0) randpermute = true; if (strcmp(argv[i],"-savercm")==0) savercm = true; } Par_DCSC_Bool * ABool; ostringstream tinfo; if(string(argv[1]) == string("input")) // input option { ABool = new Par_DCSC_Bool(); filename = argv[2]; tinfo.str(""); tinfo << "**** Reading input matrix: " << filename << " ******* " << endl; base_filename = filename.substr(filename.find_last_of("/\\") + 1); SpParHelper::Print(tinfo.str()); double t01 = MPI_Wtime(); ABool->ParallelReadMM(filename, true, maximum()); double t02 = MPI_Wtime(); Symmetricize(*ABool); tinfo.str(""); tinfo << "matrix read and symmetricized " << endl; tinfo << "Reader took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); } else if(string(argv[1]) == string("rmat")) { unsigned scale; scale = static_cast(atoi(argv[2])); double initiator[4] = {.57, .19, .19, .05}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); MPI_Barrier(MPI_COMM_WORLD); ABool = new Par_DCSC_Bool(*DEL, false); Symmetricize(*ABool); delete DEL; } else if(string(argv[1]) == string("er")) { unsigned scale; scale = static_cast(atoi(argv[2])); double initiator[4] = {.25, .25, .25, .25}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); MPI_Barrier(MPI_COMM_WORLD); ABool = new Par_DCSC_Bool(*DEL, false); Symmetricize(*ABool); delete DEL; } else { SpParHelper::Print("Unknown input option\n"); MPI_Finalize(); return -1; } Par_DCSC_Bool ABoolOld(ABool->getcommgrid()); // needed for random permutation FullyDistVec randp( ABool->getcommgrid()); if(randpermute && string(argv[1]) == string("input")) // do this only for user provided matrices { if(ABool->getnrow() == ABool->getncol()) { ABoolOld = *ABool; // create a copy for bandwidth computation randp.iota(ABool->getnrow(), 0); randp.RandPerm(); (*ABool)(randp,randp,true);// in-place permute to save memory SpParHelper::Print("Matrix is randomly permuted for load balance.\n"); } else { SpParHelper::Print("Rectangular matrix: Can not apply symmetric permutation.\n"); } } ABool->RemoveLoops(); Par_CSC_Bool * ABoolCSC; FullyDistVec degrees ( ABool->getcommgrid()); float balance; balance = ABool->LoadImbalance(); ABool->Reduce(degrees, Column, plus(), static_cast(0)); ABoolCSC = new Par_CSC_Bool(*ABool); int nthreads = 1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif threads = nthreads; processors = nprocs; ostringstream outs; outs << "--------------------------------------" << endl; outs << "Number of MPI proceses: " << nprocs << endl; outs << "Number of threads per procese: " << nthreads << endl; outs << "Load balance: " << balance << endl; outs << "--------------------------------------" << endl; SpParHelper::Print(outs.str()); // create Pre allocated SPA for SpMSpV PreAllocatedSPA SPA(ABoolCSC->seq(), nthreads*4); // Compute the RCM ordering FullyDistVec rcmorder = RCM(*ABoolCSC, degrees, SPA); FullyDistVec reverseOrder = rcmorder; // comment out the next two lines if you want the Cuthill-McKee ordering reverseOrder= rcmorder.TotalLength(); reverseOrder -= rcmorder; //revert random permutation if applied before if(randpermute==true && randp.TotalLength() >0) { // inverse permutation FullyDistVecinvRandp = randp.sort(); reverseOrder = reverseOrder(invRandp); } // Write the RCM ordering // TODO: should we save the permutation instead? if(savercm && filename!="") { string ofName = filename + ".rcm.txt"; reverseOrder.ParallelWrite(ofName, 1, false); } // get permutation from the ordering // sort returns permutation from ordering // and make the original vector a sequence (like iota) // TODO: Can we use invert() ? FullyDistVecrcmorder1 = reverseOrder.sort(); // Permute the original matrix with the RCM order // this part is not timed as it is needed for sanity check only if(randpermute==true && randp.TotalLength() >0) { int64_t bw_before1 = ABoolOld.Bandwidth(); int64_t bw_before2 = ABool->Bandwidth(); ABoolOld(rcmorder1,rcmorder1,true); int64_t bw_after = ABoolOld.Bandwidth(); ostringstream outs1; outs1 << "Original Bandwidth: " << bw_before1 << endl; outs1 << "Bandwidth after randomly permuting the matrix: " << bw_before2 << endl; outs1 << "Bandwidth after the matrix is permuted by RCM: " << bw_after << endl << endl; SpParHelper::Print(outs1.str()); } else { int64_t bw_before1 = ABool->Bandwidth(); (*ABool)(rcmorder1,rcmorder1,true); int64_t bw_after = ABool->Bandwidth(); ostringstream outs1; outs1 << "Original Bandwidth: " << bw_before1 << endl; outs1 << "Bandwidth after the matrix is permuted by RCM: " << bw_after << endl << endl;; SpParHelper::Print(outs1.str()); } delete ABool; delete ABoolCSC; } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/Ordering/.ipynb_checkpoints/000755 000765 000024 00000000000 13212627400 022763 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/Ordering/makefile-mac000644 000765 000024 00000005617 13212627400 021441 0ustar00aydinbulucstaff000000 000000 CXX := mpicxx CXXFLAGS := -std=c++11 -std=gnu++14 -O3 -fopenmp -fpermissive -DTHREADED #-DTIMING -DDETERMINISTIC COMBBLAS = .. $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator %.o : %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/Tommy/%.o : $(COMBBLAS)/Tommy/%.c $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/usort/%.o : $(COMBBLAS)/usort/src/%.cpp $(CXX) -I$(COMBBLAS)/usort/include $(CXXFLAGS) -o $@ -c $< clean: rm -rf ../*.o rm -f md rm -f rcm rm -f MatPermuteSave rm -f *.o TOMMYS = $(COMBBLAS)/Tommy/tommyhashdyn.o $(COMBBLAS)/Tommy/tommyhash.o $(COMBBLAS)/Tommy/tommylist.o USORT = $(COMBBLAS)/usort/binUtils.o $(COMBBLAS)/usort/parUtils.o md.o: MD.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(CXX) $(CXXFLAGS) -o $@ -c $< md: MD.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq rcm.o: RCM.cpp ../PreAllocatedSPA.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< rcm: rcm.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq MatPermuteSave.o: MatPermuteSave.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< MatPermuteSave: MatPermuteSave.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq gathertest.o: gathertest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< gathertest: gathertest.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq CombBLAS_beta_16_2/Ordering/MD.cpp000644 000765 000024 00000026726 13271404146 020220 0ustar00aydinbulucstaff000000 000000 #define DETERMINISTIC #include "CombBLAS/CombBLAS.h" #include #include #include #include #include #include #include #include #define EDGEFACTOR 16 /// changed to 8 using namespace std; using namespace combblas; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); AT.RemoveLoops(); // not needed for boolean matrices, but no harm in keeping it A += AT; } struct SelectMinSR { typedef int64_t T_promote; static T_promote id(){ return -1; }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MIN; }; static T_promote add(const T_promote & arg1, const T_promote & arg2) { return std::min(arg1, arg2); } static T_promote multiply(const bool & arg1, const T_promote & arg2) { return arg2; } static void axpy(bool a, const T_promote & x, T_promote & y) { y = std::min(y, x); } }; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; typedef SpParMat < int64_t, int64_t, SpDCCols > PSpMat_Int64; void MD(PSpMat_Int64 & A); int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./md " << endl; cout << "Example: mpirun -np 4 ./md rmat 20" << endl; cout << "Example: mpirun -np 4 ./md er 20" << endl; cout << "Example: mpirun -np 4 ./md input a.mtx" << endl; } MPI_Finalize(); return -1; } { PSpMat_Bool * ABool; if(string(argv[1]) == string("input")) // input option { string filename(argv[2]); ifstream inf; inf.open(filename.c_str(), ios::in); string header; getline(inf,header); bool isSymmetric = header.find("symmetric"); bool isUnweighted = header.find("pattern"); inf.close(); ABool = new PSpMat_Bool(); ABool->ReadDistribute(filename, 0, isUnweighted); // unweighted if(isSymmetric) Symmetricize(*ABool); SpParHelper::Print("Read input\n"); } else if(string(argv[1]) == string("rmat")) { unsigned scale; scale = static_cast(atoi(argv[2])); double initiator[4] = {.57, .19, .19, .05}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); MPI_Barrier(MPI_COMM_WORLD); ABool = new PSpMat_Bool(*DEL, false); Symmetricize(*ABool); delete DEL; } else if(string(argv[1]) == string("er")) { unsigned scale; scale = static_cast(atoi(argv[2])); double initiator[4] = {.25, .25, .25, .25}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); MPI_Barrier(MPI_COMM_WORLD); ABool = new PSpMat_Bool(*DEL, false); Symmetricize(*ABool); delete DEL; } else { SpParHelper::Print("Unknown input option\n"); MPI_Finalize(); return -1; } Symmetricize(*ABool); PSpMat_Int64 A = *ABool; MD(A); } MPI_Finalize(); return 0; } // Single source BFS // Find all reachable vertices from surce via enodes FullyDistSpVec getReach(int64_t source, PSpMat_Int64 & A, FullyDistVec& enodes) { FullyDistSpVec x(A.getcommgrid(), A.getncol()); FullyDistSpVec nx(A.getcommgrid(), A.getnrow()); FullyDistVec visited ( A.getcommgrid(), A.getnrow(), (int64_t) 0); x.SetElement(source, 1); visited.SetElement(source, 1); while(x.getnnz() > 0) { SpMV(A, x, nx, false); nx.Select(visited, [](int64_t visit){return visit==0;}); visited.Set(nx); nx.Select(enodes, [](int64_t ev){return ev!=0;}); // newly visited enodes x = nx; } FullyDistSpVec reach(visited, [](int64_t visit){return visit!=0;}); reach.Select(enodes, [](int64_t ev){return ev==0;}); reach.DelElement(source); // remove source return reach; } /*************************************************************************** * Multisource (guided) BFS from all vertices in "sources" with SpGEMM * Only Vertices in "enodes" are used in the traversal * Inputs: * sources: the sources of BFSs. Let nnz(sources) = k * A: nxn adjacency matrix (n is the number of vertices) * enodes: enodes[i]=1 if the ith vertex has already received an order. * let the number of enodes is r ****************************************************************************/ FullyDistSpVec getReachesSPMM(FullyDistSpVec& sources, PSpMat_Int64 & A, FullyDistVec& enodes) { // --------------------------------------------------------------------------- // create an nxk initial frontier from sources // ith column represent the current frontier of BFS tree rooted at the ith source vertex //---------------------------------------------------------------------------- FullyDistVec ri = sources.FindInds([](int64_t x){return true;}); FullyDistVec ci(A.getcommgrid()); ci.iota(sources.getnnz(), 0); PSpMat_Int64 fringe(A.getnrow(), sources.getnnz(), ri, ci, (int64_t) 1, false); PSpMat_Int64 visited(A.getnrow(), sources.getnnz(), ri, ci, (int64_t) 1, false); typedef PlusTimesSRing PTDD; // --------------------------------------------------------------------------- // create an nxn matrix E such that E(i,i) = 1 if i is an enode // Note: we can avoid creating this matrix from scratch // by incrementally adding nonzero entries in this matrix //---------------------------------------------------------------------------- FullyDistVec ri1 = enodes.FindInds([](int64_t x){return x>0;}); PSpMat_Int64 E(A.getnrow(), A.getnrow(), ri1, ri1, 1); while( fringe.getnnz() > 0 ) { // get the next frontier fringe = PSpGEMM(A, fringe); // remove previously visited vertices fringe = EWiseMult(fringe, visited, true); visited += fringe; // keep enodes in the frontier // this can be replaced by EWiseMult if we keep a matrix with repeated enodes (memory requirement can be high) fringe = PSpGEMM(E, fringe); } FullyDistVec degrees(A.getcommgrid(), sources.getnnz(), 0); // create an nxn matrix NE such that E(i,i) = 1 if i is not an enode // Note: NE and E together create a diagonal matrix FullyDistVec ri2 = enodes.FindInds([](int64_t x){return x==0;}); PSpMat_Int64 NE(A.getnrow(), A.getnrow(), ri2, ri2, 1); // keep only visited non enodes visited = PSpGEMM(NE, visited); // count visited non enodes from each sources visited.Reduce(degrees, Column, plus(), static_cast(0)); degrees.Apply([](int64_t val){return (val-1);}); // -1 to remove sources themselves // option 2, using maskedReduce /* FullyDistSpVec nenodes(enodes,[](int64_t x){return x==0;}); visited.MaskedReduce(degrees, nenodes, Column, plus(), static_cast(0)); // or use negative mask //FullyDistSpVec nenodes(enodes,[](int64_t x){return x>0;}); //visited.MaskedReduce(degrees1, nenodes, Column, plus(), static_cast(0), true); degrees.Apply([](int64_t val){return (val-1);}); // -1 to remove sources themselves */ return FullyDistSpVec(sources.TotalLength(), ri, degrees); } // assume that source is an enode FullyDistSpVec getReachesSPMV(FullyDistSpVec& sources, PSpMat_Int64 & A, FullyDistVec& enodes) { int nprocs = sources.getcommgrid()->GetSize(); int myrank = sources.getcommgrid()->GetRank(); FullyDistSpVec degrees = sources; vector locvals = sources.GetLocalInd(); int64_t j = 0; for(int i=0; i(), i, sources.getcommgrid()->GetWorld()); if(s!=-1) { FullyDistSpVec reach = getReach(s, A, enodes); degrees.SetElement(s, reach.getnnz()); } else i++; } return degrees; } void MD(PSpMat_Int64 & A) { FullyDistVec degrees ( A.getcommgrid()); FullyDistVec enodes (A.getcommgrid(), A.getnrow(), (int64_t) 0); FullyDistVec mdOrder (A.getcommgrid(), A.getnrow(), (int64_t) 0); A.Reduce(degrees, Column, plus(), static_cast(0)); degrees.Apply([](int64_t x){return x-1;}); // magic FullyDistVec treach (A.getcommgrid(), A.getnrow(), (double) 0); FullyDistVec treaches (A.getcommgrid(), A.getnrow(), (double) 0); FullyDistVec nreach (A.getcommgrid(), A.getnrow(), (int64_t) 0); int myrank, nprocs; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); double time_beg = MPI_Wtime(); //degrees.DebugPrint(); double time1=0, time2=0, time3=0; for(int64_t i=0; i reach = getReach(s, A, enodes); time2 += MPI_Wtime()-time1; time1 = MPI_Wtime(); FullyDistSpVec updatedDeg( A.getcommgrid()); //updatedDeg = getReachesSPMV(reach, A, enodes); updatedDeg = getReachesSPMM(reach, A, enodes); time3 += MPI_Wtime()-time1; degrees.Set(updatedDeg); degrees.SetElement(s, A.getnrow()); // set degree to infinite //degrees.DebugPrint(); int nnz = reach.getnnz(); if(myrank==0) { if(i%20==0) { cout << i << " .................. " << nnz << " :: " << time2 << " + " << time3 << endl; time2 = 0; time3 = 0; } } } double time_end = MPI_Wtime(); if(myrank==0) cout << " Total time: " << time_end - time_beg << endl; //mdOrder.DebugPrint(); } CombBLAS_beta_16_2/Ordering/makefile-nersc000644 000765 000024 00000005325 13212627400 022007 0ustar00aydinbulucstaff000000 000000 CXX := CC CXXFLAGS := -std=c++11 -std=gnu++14 -O3 -fopenmp -fpermissive -DTHREADED #-DTIMING -DDETERMINISTIC COMBBLAS = .. $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator %.o : %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/Tommy/%.o : $(COMBBLAS)/Tommy/%.c $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/usort/%.o : $(COMBBLAS)/usort/src/%.cpp $(CXX) -I$(COMBBLAS)/usort/include $(CXXFLAGS) -o $@ -c $< clean: rm -rf ../*.o rm -f md rm -f rcm rm -f MatPermuteSave rm -f *.o TOMMYS = $(COMBBLAS)/Tommy/tommyhashdyn.o $(COMBBLAS)/Tommy/tommyhash.o $(COMBBLAS)/Tommy/tommylist.o USORT = $(COMBBLAS)/usort/binUtils.o $(COMBBLAS)/usort/parUtils.o md.o: MD.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(CXX) $(CXXFLAGS) -o $@ -c $< md: MD.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq rcm.o: RCM.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(CXX) $(CXXFLAGS) -o $@ -c $< MatPermuteSave: MatPermuteSave.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq MatPermuteSave.o: MatPermuteSave.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(CXX) $(CXXFLAGS) -o $@ -c $< rcm: rcm.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq gathertest.o: gathertest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h $(CXX) $(CXXFLAGS) -o $@ -c $< gathertest: gathertest.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq CombBLAS_beta_16_2/Ordering/.ipynb_checkpoints/Minimum_Degree-Clean-checkpoint.ipynb000644 000765 000024 00000050273 13212627400 032070 0ustar00aydinbulucstaff000000 000000 { "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "using Compose,Gadfly\n", "\n", "set_default_plot_size(12cm, 12cm)\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "GetReach (generic function with 1 method)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function GetReach(v,Adj,Labels)\n", " \n", " n = size(Adj,1)\n", " reach = spzeros(n,1)\n", " \n", " X = sparsevec([v],[1],n);\n", " marker = spzeros(n,1)\n", "\n", " #iter is just a statistic counter\n", " iter = 0\n", " while( nnz(X)>0)\n", " Xn = *(Adj,X); \n", " rows = find(X)\n", " marker[rows] = 1\n", " \n", " for i in find(Xn)\n", " reach[i] = Xn[i] * (1-max(marker[i],Labels[i])>0)\n", " end\n", " \n", " X=spzeros(n,1)\n", " for i in find(Xn)\n", " X[i]= Xn[i] * (1- max(marker[i],reach[i])>0)\n", " end\n", " \n", " iter+=1\n", " end\n", " \n", " return (reach,iter)\n", "end" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "GetReaches (generic function with 1 method)" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function GetReaches(vs,Adj,Labels)\n", " nv = length(vs)\n", " n = size(Adj,1)\n", " reaches = spzeros(n,nv)\n", " \n", " XS = sparse(vs,[1:nv],vec(ones(1,nv)),n,n)\n", " \n", " markers = spzeros(n,nv)\n", "\n", "\n", " #iter is just a statistic counter\n", " iter = 0\n", " while( nnz(XS)>0)\n", " (rx,cx)=findn(XS)\n", " for ir =1:length(rx)\n", " i = rx[ir]\n", " j = cx[ir]\n", " markers[i,j] = 1\n", " end\n", " \n", " Xns = *(Adj,XS); \n", " \n", " (r,c)=findn(Xns)\n", "\n", " for ir =1:length(r)\n", " i = r[ir]\n", " j = c[ir]\n", " #reaches[i,j] += (Xns[i,j] * (1-max(markers[i,j],Labels[i])>0))\n", " reaches[i,j] += (Xns[i,j] * (1-max(markers[i,j],Labels[i])>0))\n", " end\n", " \n", " \n", " XS=spzeros(n,nv)\n", " for ir =1:length(r)\n", " i = r[ir]\n", " j = c[ir]\n", " XS[i,j]= (Xns[i,j] * (1- max(markers[i,j],reaches[i,j])>0))\n", " end \n", "\n", " iter+=1\n", " end\n", " \n", "\n", " return (reaches,iter)\n", "end\n" ] }, { "cell_type": "code", "execution_count": 54, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "GetReaches1 (generic function with 1 method)" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function GetReaches1(vs,Adj,Labels)\n", " nv = length(vs)\n", " n = size(Adj,1)\n", " reaches = spzeros(n,nv)\n", " \n", " k = 1\n", " iter = -1\n", " for s in vs\n", " (rs,it) = GetReach(s,Adj,Labels)\n", " reaches[:,k] = rs\n", " k = k +1\n", " iter = max(iter, it)\n", " end\n", " return (reaches,iter)\n", "end\n" ] }, { "cell_type": "code", "execution_count": 67, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "MD (generic function with 1 method)" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "function MD(B)\n", "\n", " n = size(B,1)\n", " Labels = vec(zeros(n,1))\n", " #statistics\n", " SPMV = vec(zeros(n,1))\n", " SPMM = vec(zeros(n,1))\n", " SPMM_SIZE = vec(zeros(n,1))\n", "\n", " #initialize degree list\n", " Degrees = sum(B,2)-ones(n,1);\n", "\n", " for s=1:n\n", " #pick node with minimum degree\n", " print(Degrees')\n", " print(\"\\n\")\n", " mind = n+1\n", " v = -1\n", " for i=1:length(Degrees)\n", " if Labels[i]==0\n", " if Degrees[i] 1000 #pragma once #endif #include "stdint.h" // 7.8 Format conversion of integer types typedef struct { intmax_t quot; intmax_t rem; } imaxdiv_t; // 7.8.1 Macros for format specifiers #if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 // The fprintf macros for signed integers are: #define PRId8 "d" #define PRIi8 "i" #define PRIdLEAST8 "d" #define PRIiLEAST8 "i" #define PRIdFAST8 "d" #define PRIiFAST8 "i" #define PRId16 "hd" #define PRIi16 "hi" #define PRIdLEAST16 "hd" #define PRIiLEAST16 "hi" #define PRIdFAST16 "hd" #define PRIiFAST16 "hi" #define PRId32 "I32d" #define PRIi32 "I32i" #define PRIdLEAST32 "I32d" #define PRIiLEAST32 "I32i" #define PRIdFAST32 "I32d" #define PRIiFAST32 "I32i" #define PRId64 "I64d" #define PRIi64 "I64i" #define PRIdLEAST64 "I64d" #define PRIiLEAST64 "I64i" #define PRIdFAST64 "I64d" #define PRIiFAST64 "I64i" #define PRIdMAX "I64d" #define PRIiMAX "I64i" #define PRIdPTR "Id" #define PRIiPTR "Ii" // The fprintf macros for unsigned integers are: #define PRIo8 "o" #define PRIu8 "u" #define PRIx8 "x" #define PRIX8 "X" #define PRIoLEAST8 "o" #define PRIuLEAST8 "u" #define PRIxLEAST8 "x" #define PRIXLEAST8 "X" #define PRIoFAST8 "o" #define PRIuFAST8 "u" #define PRIxFAST8 "x" #define PRIXFAST8 "X" #define PRIo16 "ho" #define PRIu16 "hu" #define PRIx16 "hx" #define PRIX16 "hX" #define PRIoLEAST16 "ho" #define PRIuLEAST16 "hu" #define PRIxLEAST16 "hx" #define PRIXLEAST16 "hX" #define PRIoFAST16 "ho" #define PRIuFAST16 "hu" #define PRIxFAST16 "hx" #define PRIXFAST16 "hX" #define PRIo32 "I32o" #define PRIu32 "I32u" #define PRIx32 "I32x" #define PRIX32 "I32X" #define PRIoLEAST32 "I32o" #define PRIuLEAST32 "I32u" #define PRIxLEAST32 "I32x" #define PRIXLEAST32 "I32X" #define PRIoFAST32 "I32o" #define PRIuFAST32 "I32u" #define PRIxFAST32 "I32x" #define PRIXFAST32 "I32X" #define PRIo64 "I64o" #define PRIu64 "I64u" #define PRIx64 "I64x" #define PRIX64 "I64X" #define PRIoLEAST64 "I64o" #define PRIuLEAST64 "I64u" #define PRIxLEAST64 "I64x" #define PRIXLEAST64 "I64X" #define PRIoFAST64 "I64o" #define PRIuFAST64 "I64u" #define PRIxFAST64 "I64x" #define PRIXFAST64 "I64X" #define PRIoMAX "I64o" #define PRIuMAX "I64u" #define PRIxMAX "I64x" #define PRIXMAX "I64X" #define PRIoPTR "Io" #define PRIuPTR "Iu" #define PRIxPTR "Ix" #define PRIXPTR "IX" // The fscanf macros for signed integers are: #define SCNd8 "d" #define SCNi8 "i" #define SCNdLEAST8 "d" #define SCNiLEAST8 "i" #define SCNdFAST8 "d" #define SCNiFAST8 "i" #define SCNd16 "hd" #define SCNi16 "hi" #define SCNdLEAST16 "hd" #define SCNiLEAST16 "hi" #define SCNdFAST16 "hd" #define SCNiFAST16 "hi" #define SCNd32 "ld" #define SCNi32 "li" #define SCNdLEAST32 "ld" #define SCNiLEAST32 "li" #define SCNdFAST32 "ld" #define SCNiFAST32 "li" #define SCNd64 "I64d" #define SCNi64 "I64i" #define SCNdLEAST64 "I64d" #define SCNiLEAST64 "I64i" #define SCNdFAST64 "I64d" #define SCNiFAST64 "I64i" #define SCNdMAX "I64d" #define SCNiMAX "I64i" #ifdef _WIN64 // [ # define SCNdPTR "I64d" # define SCNiPTR "I64i" #else // _WIN64 ][ # define SCNdPTR "ld" # define SCNiPTR "li" #endif // _WIN64 ] // The fscanf macros for unsigned integers are: #define SCNo8 "o" #define SCNu8 "u" #define SCNx8 "x" #define SCNX8 "X" #define SCNoLEAST8 "o" #define SCNuLEAST8 "u" #define SCNxLEAST8 "x" #define SCNXLEAST8 "X" #define SCNoFAST8 "o" #define SCNuFAST8 "u" #define SCNxFAST8 "x" #define SCNXFAST8 "X" #define SCNo16 "ho" #define SCNu16 "hu" #define SCNx16 "hx" #define SCNX16 "hX" #define SCNoLEAST16 "ho" #define SCNuLEAST16 "hu" #define SCNxLEAST16 "hx" #define SCNXLEAST16 "hX" #define SCNoFAST16 "ho" #define SCNuFAST16 "hu" #define SCNxFAST16 "hx" #define SCNXFAST16 "hX" #define SCNo32 "lo" #define SCNu32 "lu" #define SCNx32 "lx" #define SCNX32 "lX" #define SCNoLEAST32 "lo" #define SCNuLEAST32 "lu" #define SCNxLEAST32 "lx" #define SCNXLEAST32 "lX" #define SCNoFAST32 "lo" #define SCNuFAST32 "lu" #define SCNxFAST32 "lx" #define SCNXFAST32 "lX" #define SCNo64 "I64o" #define SCNu64 "I64u" #define SCNx64 "I64x" #define SCNX64 "I64X" #define SCNoLEAST64 "I64o" #define SCNuLEAST64 "I64u" #define SCNxLEAST64 "I64x" #define SCNXLEAST64 "I64X" #define SCNoFAST64 "I64o" #define SCNuFAST64 "I64u" #define SCNxFAST64 "I64x" #define SCNXFAST64 "I64X" #define SCNoMAX "I64o" #define SCNuMAX "I64u" #define SCNxMAX "I64x" #define SCNXMAX "I64X" #ifdef _WIN64 // [ # define SCNoPTR "I64o" # define SCNuPTR "I64u" # define SCNxPTR "I64x" # define SCNXPTR "I64X" #else // _WIN64 ][ # define SCNoPTR "lo" # define SCNuPTR "lu" # define SCNxPTR "lx" # define SCNXPTR "lX" #endif // _WIN64 ] #endif // __STDC_FORMAT_MACROS ] // 7.8.2 Functions for greatest-width integer types // 7.8.2.1 The imaxabs function #define imaxabs _abs64 // 7.8.2.2 The imaxdiv function // This is modified version of div() function from Microsoft's div.c found // in %MSVC.NET%\crt\src\div.c #ifdef STATIC_IMAXDIV // [ static #else // STATIC_IMAXDIV ][ _inline #endif // STATIC_IMAXDIV ] imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) { imaxdiv_t result; result.quot = numer / denom; result.rem = numer % denom; if (numer < 0 && result.rem > 0) { // did division wrong; must fix up ++result.quot; result.rem -= denom; } return result; } // 7.8.2.3 The strtoimax and strtoumax functions #define strtoimax _strtoi64 #define strtoumax _strtoui64 // 7.8.2.4 The wcstoimax and wcstoumax functions #define wcstoimax _wcstoi64 #define wcstoumax _wcstoui64 #endif // _MSC_INTTYPES_H_ ] CombBLAS_beta_16_2/ms_inttypes/stdint.h000644 000765 000024 00000017447 13212627400 021472 0ustar00aydinbulucstaff000000 000000 // ISO C9x compliant stdint.h for Microsoft Visual Studio // Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 // // Copyright (c) 2006-2008 Alexander Chemeris // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. 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. // // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. // /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ #error "Use this header only with Microsoft Visual C++ compilers!" #endif // _MSC_VER ] #ifndef _MSC_STDINT_H_ // [ #define _MSC_STDINT_H_ #if _MSC_VER > 1000 #pragma once #endif #include // For Visual Studio 6 in C++ mode and for many Visual Studio versions when // compiling for ARM we should wrap include with 'extern "C++" {}' // or compiler give many errors like this: // error C2733: second C linkage of overloaded function 'wmemchr' not allowed #ifdef __cplusplus extern "C" { #endif # include #ifdef __cplusplus } #endif // Define _W64 macros to mark types changing their size, like intptr_t. #ifndef _W64 # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 # define _W64 __w64 # else # define _W64 # endif #endif // 7.18.1 Integer types // 7.18.1.1 Exact-width integer types // Visual Studio 6 and Embedded Visual C++ 4 doesn't // realize that, e.g. char has the same size as __int8 // so we give up on __intX for them. #if (_MSC_VER < 1300) typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; #else typedef signed __int8 int8_t; typedef signed __int16 int16_t; typedef signed __int32 int32_t; typedef unsigned __int8 uint8_t; typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif typedef signed __int64 int64_t; typedef unsigned __int64 uint64_t; // 7.18.1.2 Minimum-width integer types typedef int8_t int_least8_t; typedef int16_t int_least16_t; typedef int32_t int_least32_t; typedef int64_t int_least64_t; typedef uint8_t uint_least8_t; typedef uint16_t uint_least16_t; typedef uint32_t uint_least32_t; typedef uint64_t uint_least64_t; // 7.18.1.3 Fastest minimum-width integer types typedef int8_t int_fast8_t; typedef int16_t int_fast16_t; typedef int32_t int_fast32_t; typedef int64_t int_fast64_t; typedef uint8_t uint_fast8_t; typedef uint16_t uint_fast16_t; typedef uint32_t uint_fast32_t; typedef uint64_t uint_fast64_t; // 7.18.1.4 Integer types capable of holding object pointers #ifdef _WIN64 // [ typedef signed __int64 intptr_t; typedef unsigned __int64 uintptr_t; #else // _WIN64 ][ typedef _W64 signed int intptr_t; typedef _W64 unsigned int uintptr_t; #endif // _WIN64 ] // 7.18.1.5 Greatest-width integer types typedef int64_t intmax_t; typedef uint64_t uintmax_t; // 7.18.2 Limits of specified-width integer types #if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 // 7.18.2.1 Limits of exact-width integer types #define INT8_MIN ((int8_t)_I8_MIN) #define INT8_MAX _I8_MAX #define INT16_MIN ((int16_t)_I16_MIN) #define INT16_MAX _I16_MAX #define INT32_MIN ((int32_t)_I32_MIN) #define INT32_MAX _I32_MAX #define INT64_MIN ((int64_t)_I64_MIN) #define INT64_MAX _I64_MAX #define UINT8_MAX _UI8_MAX #define UINT16_MAX _UI16_MAX #define UINT32_MAX _UI32_MAX #define UINT64_MAX _UI64_MAX // 7.18.2.2 Limits of minimum-width integer types #define INT_LEAST8_MIN INT8_MIN #define INT_LEAST8_MAX INT8_MAX #define INT_LEAST16_MIN INT16_MIN #define INT_LEAST16_MAX INT16_MAX #define INT_LEAST32_MIN INT32_MIN #define INT_LEAST32_MAX INT32_MAX #define INT_LEAST64_MIN INT64_MIN #define INT_LEAST64_MAX INT64_MAX #define UINT_LEAST8_MAX UINT8_MAX #define UINT_LEAST16_MAX UINT16_MAX #define UINT_LEAST32_MAX UINT32_MAX #define UINT_LEAST64_MAX UINT64_MAX // 7.18.2.3 Limits of fastest minimum-width integer types #define INT_FAST8_MIN INT8_MIN #define INT_FAST8_MAX INT8_MAX #define INT_FAST16_MIN INT16_MIN #define INT_FAST16_MAX INT16_MAX #define INT_FAST32_MIN INT32_MIN #define INT_FAST32_MAX INT32_MAX #define INT_FAST64_MIN INT64_MIN #define INT_FAST64_MAX INT64_MAX #define UINT_FAST8_MAX UINT8_MAX #define UINT_FAST16_MAX UINT16_MAX #define UINT_FAST32_MAX UINT32_MAX #define UINT_FAST64_MAX UINT64_MAX // 7.18.2.4 Limits of integer types capable of holding object pointers #ifdef _WIN64 // [ # define INTPTR_MIN INT64_MIN # define INTPTR_MAX INT64_MAX # define UINTPTR_MAX UINT64_MAX #else // _WIN64 ][ # define INTPTR_MIN INT32_MIN # define INTPTR_MAX INT32_MAX # define UINTPTR_MAX UINT32_MAX #endif // _WIN64 ] // 7.18.2.5 Limits of greatest-width integer types #define INTMAX_MIN INT64_MIN #define INTMAX_MAX INT64_MAX #define UINTMAX_MAX UINT64_MAX // 7.18.3 Limits of other integer types #ifdef _WIN64 // [ # define PTRDIFF_MIN _I64_MIN # define PTRDIFF_MAX _I64_MAX #else // _WIN64 ][ # define PTRDIFF_MIN _I32_MIN # define PTRDIFF_MAX _I32_MAX #endif // _WIN64 ] #define SIG_ATOMIC_MIN INT_MIN #define SIG_ATOMIC_MAX INT_MAX #ifndef SIZE_MAX // [ # ifdef _WIN64 // [ # define SIZE_MAX _UI64_MAX # else // _WIN64 ][ # define SIZE_MAX _UI32_MAX # endif // _WIN64 ] #endif // SIZE_MAX ] // WCHAR_MIN and WCHAR_MAX are also defined in #ifndef WCHAR_MIN // [ # define WCHAR_MIN 0 #endif // WCHAR_MIN ] #ifndef WCHAR_MAX // [ # define WCHAR_MAX _UI16_MAX #endif // WCHAR_MAX ] #define WINT_MIN 0 #define WINT_MAX _UI16_MAX #endif // __STDC_LIMIT_MACROS ] // 7.18.4 Limits of other integer types #if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 // 7.18.4.1 Macros for minimum-width integer constants #define INT8_C(val) val##i8 #define INT16_C(val) val##i16 #define INT32_C(val) val##i32 #define INT64_C(val) val##i64 #define UINT8_C(val) val##ui8 #define UINT16_C(val) val##ui16 #define UINT32_C(val) val##ui32 #define UINT64_C(val) val##ui64 // 7.18.4.2 Macros for greatest-width integer constants #define INTMAX_C INT64_C #define UINTMAX_C UINT64_C #endif // __STDC_CONSTANT_MACROS ] #endif // _MSC_STDINT_H_ ] CombBLAS_beta_16_2/include/._.DS_Store000644 000765 000024 00000000170 13271513354 020751 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/include/.DS_Store000644 000765 000024 00000024004 13271513354 020536 0ustar00aydinbulucstaff000000 000000 Bud1    BLASbwsp  @€ @€ @€ @ CombBLASbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šCombBLASicvpblob¾bplist00ß _backgroundColorBlue[gridSpacingXtextSize_backgroundColorRed^backgroundType_backgroundColorGreen[gridOffsetX[gridOffsetY_scrollPositionY\showItemInfo_viewOptionsVersion_scrollPositionXYarrangeBy]labelOnBottom_showIconPreviewXiconSize#?ð#@K#@(##@’¬Tnone #@P+AMVkz‘©»ÈÝïù"+4=?HQRTYZ[dCombBLASlsvCblobÔbplist00Ú JKLMN _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates «$(-27<@EÔ  WvisibleUwidthYascendingZidentifier , TnameÔUwidthYascendingWvisibleXubiquity#Ô ! \dateModifiedµ Ô%![dateCreatedÔ *, aTsizeÔ / 1 s TkindÔ4 6d UlabelÔ9 ;K WversionÔ ? XcommentsÔAB^dateLastOpenedÈÔ!HYdateAdded#@¸#@(#Tname#@0 2DL`r{˜¡´¶·ÃÌÔÚäïðóôù#%&'0=?@AJVWXabdejstvw|…†ˆ‰˜™›œ¤­®¯¸ÁÐÒÓÔÝÞßéêóü PCombBLASlsvpblob›bplist00Ú FGHIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates Ù %*/49>BXcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ UindexUwidthYascendingWvisible, Ô"$ÈÔ')µÔ+, a Ô01 d Ô56 s Ô:; K Ô?  Ô ' #@¸#@(#Tname#@0 2DL`r{˜¡´¶·ÊÓâîóùþ !'-7?ADEFOPRSU^_abdmoqrs|~€‚‹‘šœžŸ ©«¬­¶·¸¹ÂËÔÙâLãCombBLASvSrnlongTommybwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šTommylsvCblob’bplist00Ø HIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates « "&+05:>CÔ   WvisibleUwidthYascendingZidentifier , TnameÔWvisibleUwidthYascending#XubiquityÔ  ! µ\dateModifiedÔ %[dateCreatedÔ '( Tsizea Ô ,- Tkinds Ô 12 Ulabeld Ô 67 WversionK Ô ; Xcomments Ô @BÈ^dateLastOpenedÔDYdateAdded#@(Tname#@0 .@H\epyŒŽ›¤¬²¼ÇÈËÌÑÚâèòóõöÿ   "#$09>@ABKPRST]cefgpxz{|…Ž™šœ¬µ¿ÀÁÂËÐÙLÚTommylsvpblobYbplist00Ø DEF _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates Ù #(-27<@Xcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ WvisibleUwidthYascendingUindex, Ô ÈÔ$%µÔ *, aÔ/ 1d Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©TommyvSrnlong E DSDB `€(0@€ @€ @ 2DL`r{˜¡´¶·ÃÌÔÚäïðóôù#%&'0=?@AJVWXabdejstvw|…†ˆ‰˜™›œ¤­®¯¸ÁÐÒÓÔÝÞßéêóü PCombBLASlsvpblob›bplist00Ú FGHIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizes_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates Ù %*/49>BXcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ UindexUwidthYascendingWvisible, Ô"$ÈÔ')µÔ+, a Ô01 d Ô56 s Ô:; K Ô?  Ô ' #@¸#@(#Tname#@0 2DL`r{˜¡´¶·ÊÓâîóùþ !'-7?CombBLAS_beta_16_2/include/CombBLAS/000755 000765 000024 00000000000 13271411227 020371 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/include/Tommy/000755 000765 000024 00000000000 13271404146 020156 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/include/Tommy/tommylist.h000644 000765 000024 00000023472 13271404146 022400 0ustar00aydinbulucstaff000000 000000 /* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 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. */ /** \file * Double linked list for collisions into hashtables. * * This list is a double linked list mainly targetted for handling collisions * into an hashtables, but useable also as a generic list. * * The main feature of this list is to require only one pointer to represent the * list, compared to a classic implementation requiring a head an a tail pointers. * This reduces the memory usage in hashtables. * * Another feature is to support the insertion at the end of the list. This allow to store * collisions in a stable order. Where for stable order we mean that equal elements keep * their insertion order. * * To initialize the list, you have to call tommy_list_init(), or to simply assign * to it NULL, as an empty list is represented by the NULL value. * * \code * tommy_list list; * * tommy_list_init(&list); // initializes the list * \endcode * * To insert elements in the list you have to call tommy_list_insert_tail() * or tommy_list_insert_head() for each element. * In the insertion call you have to specify the address of the node and the * address of the object. * The address of the object is used to initialize the tommy_node::data field * of the node. * * \code * struct object { * tommy_node node; * // other fields * int value; * }; * * struct object* obj = malloc(sizeof(struct object)); // creates the object * * obj->value = ...; // initializes the object * * tommy_list_insert_tail(&list, &obj->node, obj); // inserts the object * \endcode * * To iterate over all the elements in the list you have to call * tommy_list_head() to get the head of the list and follow the * tommy_node::next pointer until NULL. * * \code * tommy_node* i = tommy_list_head(&list); * while (i) { * struct object* obj = i->data; // gets the object pointer * * printf("%d\n", obj->value); // process the object * * i = i->next; // go to the next element * } * \endcode * * To destroy the list you have to remove all the elements, * as the list is completely inplace and it doesn't allocate memory. * This can be done with the tommy_list_foreach() function. * * \code * // deallocates all the objects iterating the list * tommy_list_foreach(&list, free); * \endcode */ #ifndef __TOMMYLIST_H #define __TOMMYLIST_H #include "tommytypes.h" /******************************************************************************/ /* list */ /** * Double linked list type. */ typedef tommy_node* tommy_list; /** * Initializes the list. * The list is completely inplace, so it doesn't need to be deinitialized. */ tommy_inline void tommy_list_init(tommy_list* list) { *list = 0; } /** * Gets the head of the list. * \return The head node. For empty lists 0 is returned. */ tommy_inline tommy_node* tommy_list_head(tommy_list* list) { return *list; } /** * Gets the tail of the list. * \return The tail node. For empty lists 0 is returned. */ tommy_inline tommy_node* tommy_list_tail(tommy_list* list) { tommy_node* head = tommy_list_head(list); if (!head) return 0; return head->prev; } /** \internal * Creates a new list with a single element. * \param list The list to initialize. * \param node The node to insert. */ tommy_inline void tommy_list_insert_first(tommy_list* list, tommy_node* node) { /* one element "circular" prev list */ node->prev = node; /* one element "0 terminated" next list */ node->next = 0; *list = node; } /** \internal * Inserts an element at the head of a not empty list. * The element is inserted at the head of the list. The list cannot be empty. * \param list The list. The list cannot be empty. * \param node The node to insert. */ tommy_inline void tommy_list_insert_head_not_empty(tommy_list* list, tommy_node* node) { tommy_node* head = tommy_list_head(list); /* insert in the "circular" prev list */ node->prev = head->prev; head->prev = node; /* insert in the "0 terminated" next list */ node->next = head; *list = node; } /** \internal * Inserts an element at the tail of a not empty list. * The element is inserted at the tail of the list. The list cannot be empty. * \param head The node at the list head. It cannot be 0. * \param node The node to insert. */ tommy_inline void tommy_list_insert_tail_not_empty(tommy_node* head, tommy_node* node) { /* insert in the "circular" prev list */ node->prev = head->prev; head->prev = node; /* insert in the "0 terminated" next list */ node->next = 0; node->prev->next = node; } /** * Inserts an element at the head of a list. * \param node The node to insert. * \param data The object containing the node. It's used to set the tommy_node::data field of the node. */ tommy_inline void tommy_list_insert_head(tommy_list* list, tommy_node* node, void* data) { tommy_node* head = tommy_list_head(list); if (head) tommy_list_insert_head_not_empty(list, node); else tommy_list_insert_first(list, node); node->data = data; } /** * Inserts an element at the tail of a list. * \param node The node to insert. * \param data The object containing the node. It's used to set the tommy_node::data field of the node. */ tommy_inline void tommy_list_insert_tail(tommy_list* list, tommy_node* node, void* data) { tommy_node* head = tommy_list_head(list); if (head) tommy_list_insert_tail_not_empty(head, node); else tommy_list_insert_first(list, node); node->data = data; } /** \internal * Removes an element from the head of a not empty list. * \param list The list. The list cannot be empty. * \return The node removed. */ tommy_inline tommy_node* tommy_list_remove_head_not_empty(tommy_list* list) { tommy_node* head = tommy_list_head(list); /* remove from the "circular" prev list */ head->next->prev = head->prev; /* remove from the "0 terminated" next list */ *list = head->next; /* the new head, in case 0 */ return head; } /** * Removes an element from the list. * You must already have the address of the element to remove. * \note The node content is left unchanged, including the tommy_node::next * and tommy_node::prev fields that still contain pointers at the list. * \param node The node to remove. The node must be in the list. * \return The tommy_node::data field of the node removed. */ tommy_inline void* tommy_list_remove_existing(tommy_list* list, tommy_node* node) { tommy_node* head = tommy_list_head(list); /* remove from the "circular" prev list */ if (node->next) node->next->prev = node->prev; else head->prev = node->prev; /* the last */ /* remove from the "0 terminated" next list */ if (head == node) *list = node->next; /* the new head, in case 0 */ else node->prev->next = node->next; return node->data; } /** * Concats two lists. * The second list is concatenated at the first list. * \param first The first list. * \param second The second list. After this call the list content is undefined, * and you should not use it anymore. */ void tommy_list_concat(tommy_list* first, tommy_list* second); /** * Sorts a list. * It's a stable merge sort with O(N*log(N)) worst complexity. * It's faster on degenerated cases like partially ordered lists. * \param cmp Compare function called with two elements. * The function should return <0 if the first element is less than the second, ==0 if equal, and >0 if greather. */ void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp); /** * Checks if empty. * \return If the list is empty. */ tommy_inline tommy_bool_t tommy_list_empty(tommy_list* list) { return tommy_list_head(list) == 0; } /** * Calls the specified function for each element in the list. * * You can use this function to deallocate all the elements * inserted in a list. * * \code * tommy_list list; * * // initializes the list * tommy_list_init(&list); * * ... * * // creates an object * struct object* obj = malloc(sizeof(struct object)); * * ... * * // insert it in the list * tommy_list_insert_tail(&list, &obj->node, obj); * * ... * * // deallocates all the objects iterating the list * tommy_list_foreach(&list, free); * \endcode */ tommy_inline void tommy_list_foreach(tommy_list* list, tommy_foreach_func* func) { tommy_node* node = tommy_list_head(list); while (node) { void* data = node->data; node = node->next; func(data); } } /** * Calls the specified function with an argument for each element in the list. */ tommy_inline void tommy_list_foreach_arg(tommy_list* list, tommy_foreach_arg_func* func, void* arg) { tommy_node* node = tommy_list_head(list); while (node) { void* data = node->data; node = node->next; func(arg, data); } } #endif CombBLAS_beta_16_2/include/Tommy/tommyhashdyn.c000644 000765 000024 00000014315 13271404146 023052 0ustar00aydinbulucstaff000000 000000 /* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 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. */ #include "tommyhashdyn.h" #include "tommylist.h" /******************************************************************************/ /* hashdyn */ void tommy_hashdyn_init(tommy_hashdyn* hashdyn) { /* fixed initial size */ hashdyn->bucket_bit = TOMMY_HASHDYN_BIT; hashdyn->bucket_max = 1 << hashdyn->bucket_bit; hashdyn->bucket_mask = hashdyn->bucket_max - 1; hashdyn->bucket = tommy_cast(tommy_hashdyn_node**, tommy_calloc(hashdyn->bucket_max, sizeof(tommy_hashdyn_node*))); hashdyn->count = 0; } void tommy_hashdyn_done(tommy_hashdyn* hashdyn) { tommy_free(hashdyn->bucket); } /** * Resize the bucket vector. */ static void tommy_hashdyn_resize(tommy_hashdyn* hashdyn, tommy_count_t new_bucket_bit) { tommy_count_t bucket_bit; tommy_count_t bucket_max; tommy_count_t new_bucket_max; tommy_count_t new_bucket_mask; tommy_hashdyn_node** new_bucket; bucket_bit = hashdyn->bucket_bit; bucket_max = hashdyn->bucket_max; new_bucket_max = 1 << new_bucket_bit; new_bucket_mask = new_bucket_max - 1; /* allocate the new vector using malloc() and not calloc() */ /* because data is fully initialized in the update process */ new_bucket = tommy_cast(tommy_hashdyn_node**, tommy_malloc(new_bucket_max * sizeof(tommy_hashdyn_node*))); /* reinsert all the elements */ if (new_bucket_bit > bucket_bit) { tommy_count_t i; /* grow */ for (i = 0; i < bucket_max; ++i) { tommy_hashdyn_node* j; /* setup the new two buckets */ new_bucket[i] = 0; new_bucket[i + bucket_max] = 0; /* reinsert the bucket */ j = hashdyn->bucket[i]; while (j) { tommy_hashdyn_node* j_next = j->next; tommy_count_t pos = j->key & new_bucket_mask; if (new_bucket[pos]) tommy_list_insert_tail_not_empty(new_bucket[pos], j); else tommy_list_insert_first(&new_bucket[pos], j); j = j_next; } } } else { tommy_count_t i; /* shrink */ for (i = 0; i < new_bucket_max; ++i) { /* setup the new bucket with the lower bucket*/ new_bucket[i] = hashdyn->bucket[i]; /* concat the upper bucket */ tommy_list_concat(&new_bucket[i], &hashdyn->bucket[i + new_bucket_max]); } } tommy_free(hashdyn->bucket); /* setup */ hashdyn->bucket_bit = new_bucket_bit; hashdyn->bucket_max = new_bucket_max; hashdyn->bucket_mask = new_bucket_mask; hashdyn->bucket = new_bucket; } /** * Grow. */ tommy_inline void hashdyn_grow_step(tommy_hashdyn* hashdyn) { /* grow if more than 50% full */ if (hashdyn->count >= hashdyn->bucket_max / 2) tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit + 1); } /** * Shrink. */ tommy_inline void hashdyn_shrink_step(tommy_hashdyn* hashdyn) { /* shrink if less than 12.5% full */ if (hashdyn->count <= hashdyn->bucket_max / 8 && hashdyn->bucket_bit > TOMMY_HASHDYN_BIT) tommy_hashdyn_resize(hashdyn, hashdyn->bucket_bit - 1); } void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash) { tommy_count_t pos = hash & hashdyn->bucket_mask; tommy_list_insert_tail(&hashdyn->bucket[pos], node, data); node->key = hash; ++hashdyn->count; hashdyn_grow_step(hashdyn); } void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node) { tommy_count_t pos = node->key & hashdyn->bucket_mask; tommy_list_remove_existing(&hashdyn->bucket[pos], node); --hashdyn->count; hashdyn_shrink_step(hashdyn); return node->data; } void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) { tommy_count_t pos = hash & hashdyn->bucket_mask; tommy_hashdyn_node* node = hashdyn->bucket[pos]; while (node) { /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ if (node->key == hash && cmp(cmp_arg, node->data) == 0) { tommy_list_remove_existing(&hashdyn->bucket[pos], node); --hashdyn->count; hashdyn_shrink_step(hashdyn); return node->data; } node = node->next; } return 0; } void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* func) { tommy_count_t bucket_max = hashdyn->bucket_max; tommy_hashdyn_node** bucket = hashdyn->bucket; tommy_count_t pos; for (pos = 0; pos < bucket_max; ++pos) { tommy_hashdyn_node* node = bucket[pos]; while (node) { void* data = node->data; node = node->next; func(data); } } } void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg) { tommy_count_t bucket_max = hashdyn->bucket_max; tommy_hashdyn_node** bucket = hashdyn->bucket; tommy_count_t pos; for (pos = 0; pos < bucket_max; ++pos) { tommy_hashdyn_node* node = bucket[pos]; while (node) { void* data = node->data; node = node->next; func(arg, data); } } } tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn) { return hashdyn->bucket_max * (tommy_size_t)sizeof(hashdyn->bucket[0]) + tommy_hashdyn_count(hashdyn) * (tommy_size_t)sizeof(tommy_hashdyn_node); } CombBLAS_beta_16_2/include/Tommy/tommyhash.h000644 000765 000024 00000007416 13271404146 022350 0ustar00aydinbulucstaff000000 000000 /* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 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. */ /** \file * Hash functions for the use with ::tommy_hashtable, ::tommy_hashdyn and ::tommy_hashlin. */ #ifndef __TOMMYHASH_H #define __TOMMYHASH_H #include "tommytypes.h" /******************************************************************************/ /* hash */ /** * Hash type used in hashtables. */ typedef tommy_key_t tommy_hash_t; /** * Hash function with a 32 bits result. * Implementation of the Robert Jenkins "lookup3" hash 32 bits version, * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle(). * \param init_val Initialization value. * Using a different initialization value, you can generate a completely different set of hash values. * Use 0 if not relevant. * \param void_key Pointer at the data to hash. * \param key_len Size of the data to hash. * \note * This function is endianess independent. * \return The hash value of 32 bits. */ tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len); /** * Hash function with a 64 bits result. * Implementation of the Robert Jenkins "lookup3" hash 64 bits versions, * from http://www.burtleburtle.net/bob/hash/doobs.html, function hashlittle2(). * \param init_val Initialization value. * Using a different initialization value, you can generate a completely different set of hash values. * Use 0 if not relevant. * \param void_key Pointer at the data to hash. * \param key_len Size of the data to hash. * \note * This function is endianess independent. * \return The hash value of 64 bits. */ tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len); /** * Integer hash of 32 bits. * Implementation of the Robert Jenkins "4-byte Integer Hashing", * from http://burtleburtle.net/bob/hash/integer.html */ tommy_inline tommy_uint32_t tommy_inthash_u32(tommy_uint32_t key) { key -= key << 6; key ^= key >> 17; key -= key << 9; key ^= key << 4; key -= key << 3; key ^= key << 10; key ^= key >> 15; return key; } /** * Integer hash of 64 bits. * Implementation of the Thomas Wang "Integer Hash Function", * from http://www.cris.com/~Ttwang/tech/inthash.htm */ tommy_inline tommy_uint64_t tommy_inthash_u64(tommy_uint64_t key) { key = ~key + (key << 21); key = key ^ (key >> 24); key = key + (key << 3) + (key << 8); key = key ^ (key >> 14); key = key + (key << 2) + (key << 4); key = key ^ (key >> 28); key = key + (key << 31); return key; } #endif CombBLAS_beta_16_2/include/Tommy/tommytypes.h000644 000765 000024 00000027072 13271404146 022571 0ustar00aydinbulucstaff000000 000000 /* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 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. */ /** \file * Generic types. */ #ifndef __TOMMYTYPES_H #define __TOMMYTYPES_H /******************************************************************************/ /* types */ #include #if defined(_MSC_VER) typedef unsigned tommy_uint32_t; /**< Generic uint32_t type. */ typedef unsigned _int64 tommy_uint64_t; /**< Generic uint64_t type. */ typedef size_t tommy_uintptr_t; /**< Generic uintptr_t type. */ #else #include typedef uint32_t tommy_uint32_t; /**< Generic uint32_t type. */ typedef uint64_t tommy_uint64_t; /**< Generic uint64_t type. */ typedef uintptr_t tommy_uintptr_t; /**< Generic uintptr_t type. */ #endif typedef size_t tommy_size_t; /**< Generic size_t type. */ typedef ptrdiff_t tommy_ptrdiff_t; /**< Generic ptrdiff_t type. */ typedef int tommy_bool_t; /**< Generic boolean type. */ /** * Generic unsigned integer type. * * It has no specific size, as is used to store only small values. * To make the code more efficient, a full 32 bit integer is used. */ typedef tommy_uint32_t tommy_uint_t; /** * Generic unsigned integer for counting objects. * * TommyDS doesn't support more than 2^32-1 objects. */ typedef tommy_uint32_t tommy_count_t; /** \internal * Type cast required for the C++ compilation. * When compiling in C++ we cannot convert a void* pointer to another pointer. * In such case we need an explicit cast. */ #ifdef __cplusplus #define tommy_cast(type, value) static_cast(value) #else #define tommy_cast(type, value) (value) #endif /******************************************************************************/ /* heap */ /* by default uses malloc/calloc/realloc/free */ /** * Generic malloc(), calloc(), realloc() and free() functions. * Redefine them to what you need. By default they map to the C malloc(), calloc(), realloc() and free(). */ #if !defined(tommy_malloc) || !defined(tommy_calloc) || !defined(tommy_realloc) || !defined(tommy_free) #include #endif #if !defined(tommy_malloc) #define tommy_malloc malloc #endif #if !defined(tommy_calloc) #define tommy_calloc calloc #endif #if !defined(tommy_realloc) #define tommy_realloc realloc #endif #if !defined(tommy_free) #define tommy_free free #endif /******************************************************************************/ /* modificators */ /** \internal * Definition of the inline keyword if available. */ #if !defined(tommy_inline) #if defined(_MSC_VER) || defined(__GNUC__) #define tommy_inline static __inline #else #define tommy_inline static #endif #endif /** \internal * Definition of the restrict keyword if available. */ #if !defined(tommy_restrict) #if __STDC_VERSION__ >= 199901L #define tommy_restrict restrict #elif defined(_MSC_VER) || defined(__GNUC__) #define tommy_restrict __restrict #else #define tommy_restrict #endif #endif /** \internal * Hints the compiler that a condition is likely true. */ #if !defined(tommy_likely) #if defined(__GNUC__) #define tommy_likely(x) __builtin_expect(!!(x), 1) #else #define tommy_likely(x) (x) #endif #endif /** \internal * Hints the compiler that a condition is likely false. */ #if !defined(tommy_unlikely) #if defined(__GNUC__) #define tommy_unlikely(x) __builtin_expect(!!(x), 0) #else #define tommy_unlikely(x) (x) #endif #endif /******************************************************************************/ /* key */ /** * Key type used in indexed data structures to store the key or the hash value. */ typedef tommy_uint32_t tommy_key_t; /** * Bits into the ::tommy_key_t type. */ #define TOMMY_KEY_BIT (sizeof(tommy_key_t) * 8) /******************************************************************************/ /* node */ /** * Data structure node. * This node type is shared between all the data structures and used to store some * info directly into the objects you want to store. * * A typical declaration is: * \code * struct object { * tommy_node node; * // other fields * }; * \endcode */ typedef struct tommy_node_struct { /** * Next node. * The tail node has it at 0, like a 0 terminated list. */ struct tommy_node_struct* next; /** * Previous node. * The head node points to the tail node, like a circular list. */ struct tommy_node_struct* prev; /** * Pointer at the object containing the node. * This field is initialized when inserting nodes into a data structure. */ void* data; /** * Key used to store the node. * With hashtables this field is used to store the hash value. * With lists this field is not used. */ tommy_key_t key; } tommy_node; /******************************************************************************/ /* compare */ /** * Compare function for elements. * \param obj_a Pointer at the first object to compare. * \param obj_b Pointer at the second object to compare. * \return <0 if the first element is less than the second, ==0 equal, >0 if greather. * * This function is like the C strcmp(). * * \code * struct object { * tommy_node node; * int value; * }; * * int compare(const void* obj_a, const void* obj_b) * { * if (((const struct object*)obj_a)->value < ((const struct object*)obj_b)->value) * return -1; * if (((const struct object*)obj_a)->value > ((const struct object*)obj_b)->value) * return 1; * return 0; * } * * tommy_list_sort(&list, compare); * \endcode * */ typedef int tommy_compare_func(const void* obj_a, const void* obj_b); /** * Search function for elements. * \param arg Pointer at the value to search. * \param obj Pointer at the object to compare to. * \return ==0 if the value matches the element. !=0 if different. * * Note that the first argument is a pointer to the value to search and * the second one is a pointer to the object to compare. * They are pointers of two different types. * * \code * struct object { * tommy_node node; * int value; * }; * * int compare(const void* arg, const void* obj) * { * return *(const int*)arg != ((const struct object*)obj)->value; * } * * int value_to_find = 1; * struct object* obj = tommy_hashtable_search(&hashtable, compare, &value_to_find, tommy_inthash_u32(value_to_find)); * if (!obj) { * // not found * } else { * // found * } * \endcode * */ typedef int tommy_search_func(const void* arg, const void* obj); /** * Foreach function. * \param obj Pointer at the object to iterate. * * A typical example is to use free() to deallocate all the objects in a list. * \code * tommy_list_foreach(&list, (tommy_foreach_func*)free); * \endcode */ typedef void tommy_foreach_func(void* obj); /** * Foreach function with an argument. * \param arg Pointer at a generic argument. * \param obj Pointer at the object to iterate. */ typedef void tommy_foreach_arg_func(void* arg, void* obj); /******************************************************************************/ /* bit hacks */ #if defined(_MSC_VER) && !defined(__cplusplus) #include #pragma intrinsic(_BitScanReverse) #pragma intrinsic(_BitScanForward) #endif /** \internal * Integer log2 for constants. * You can use it only for exact power of 2 up to 256. */ #define TOMMY_ILOG2(value) ((value) == 256 ? 8 : (value) == 128 ? 7 : (value) == 64 ? 6 : (value) == 32 ? 5 : (value) == 16 ? 4 : (value) == 8 ? 3 : (value) == 4 ? 2 : (value) == 2 ? 1 : 0) /** * Bit scan reverse or integer log2. * Return the bit index of the most significant 1 bit. * * If no bit is set, the result is undefined. * To force a return 0 in this case, you can use tommy_ilog2_u32(value | 1). * * Other interesting ways for bitscan are at: * * Bit Twiddling Hacks * http://graphics.stanford.edu/~seander/bithacks.html * * Chess Programming BitScan * http://chessprogramming.wikispaces.com/BitScan * * \param value Value to scan. 0 is not allowed. * \return The index of the most significant bit set. */ tommy_inline tommy_uint_t tommy_ilog2_u32(tommy_uint32_t value) { #if defined(_MSC_VER) unsigned long count; _BitScanReverse(&count, value); return count; #elif defined(__GNUC__) /* * GCC implements __builtin_clz(x) as "__builtin_clz(x) = bsr(x) ^ 31" * * Where "x ^ 31 = 31 - x", but gcc does not optimize "31 - __builtin_clz(x)" to bsr(x), * but generates 31 - (bsr(x) xor 31). * * So we write "__builtin_clz(x) ^ 31" instead of "31 - __builtin_clz(x)", * to allow the double xor to be optimized out. */ return __builtin_clz(value) ^ 31; #else /* Find the log base 2 of an N-bit integer in O(lg(N)) operations with multiply and lookup */ /* from http://graphics.stanford.edu/~seander/bithacks.html */ static unsigned char TOMMY_DE_BRUIJN_INDEX_ILOG2[32] = { 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 }; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; return TOMMY_DE_BRUIJN_INDEX_ILOG2[(tommy_uint32_t)(value * 0x07C4ACDDU) >> 27]; #endif } /** * Bit scan forward or trailing zero count. * Return the bit index of the least significant 1 bit. * * If no bit is set, the result is undefined. * \param value Value to scan. 0 is not allowed. * \return The index of the least significant bit set. */ tommy_inline tommy_uint_t tommy_ctz_u32(tommy_uint32_t value) { #if defined(_MSC_VER) unsigned long count; _BitScanForward(&count, value); return count; #elif defined(__GNUC__) return __builtin_ctz(value); #else /* Count the consecutive zero bits (trailing) on the right with multiply and lookup */ /* from http://graphics.stanford.edu/~seander/bithacks.html */ static const unsigned char TOMMY_DE_BRUIJN_INDEX_CTZ[32] = { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; return TOMMY_DE_BRUIJN_INDEX_CTZ[(tommy_uint32_t)(((value & - value) * 0x077CB531U)) >> 27]; #endif } /** * Rounds up to the next power of 2. * For the value 0, the result is undefined. * \return The smallest power of 2 not less than the specified value. */ tommy_inline tommy_uint32_t tommy_roundup_pow2_u32(tommy_uint32_t value) { /* Round up to the next highest power of 2 */ /* from http://www-graphics.stanford.edu/~seander/bithacks.html */ --value; value |= value >> 1; value |= value >> 2; value |= value >> 4; value |= value >> 8; value |= value >> 16; ++value; return value; } #endif CombBLAS_beta_16_2/include/Tommy/tommyhashdyn.h000644 000765 000024 00000024327 13271404146 023063 0ustar00aydinbulucstaff000000 000000 /* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 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. */ /** \file * Dynamic chained hashtable. * * This hashtable resizes dynamically. It starts with the minimal size of 16 buckets, it doubles * the size then it reaches a load factor greater than 0.5 and it halves the size with a load * factor lower than 0.125. * * All the elements are reallocated in a single resize operation done inside * tommy_hashdyn_insert() or tommy_hashdyn_remove(). * * Note that the resize operation takes approximatively 100 [ms] with 1 million of elements, * and 1 [second] with 10 millions. This could be a problem in real-time applications. * * The resize also fragment the heap, as it involves allocating a double-sized table, copy elements, * and deallocating the older table. Leaving a big hole in the heap. * * The ::tommy_hashlin hashtable fixes both problems. * * To initialize the hashtable you have to call tommy_hashdyn_init(). * * \code * tommy_hashslin hashdyn; * * tommy_hashdyn_init(&hashdyn); * \endcode * * To insert elements in the hashtable you have to call tommy_hashdyn_insert() for * each element. * In the insertion call you have to specify the address of the node, the * address of the object, and the hash value of the key to use. * The address of the object is used to initialize the tommy_node::data field * of the node, and the hash to initialize the tommy_node::key field. * * \code * struct object { * tommy_node node; * // other fields * int value; * }; * * struct object* obj = malloc(sizeof(struct object)); // creates the object * * obj->value = ...; // initializes the object * * tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); // inserts the object * \endcode * * To find and element in the hashtable you have to call tommy_hashtable_search() * providing a comparison function, its argument, and the hash of the key to search. * * \code * int compare(const void* arg, const void* obj) * { * return *(const int*)arg != ((const struct object*)obj)->value; * } * * int value_to_find = 1; * struct object* obj = tommy_hashdyn_search(&hashdyn, compare, &value_to_find, tommy_inthash_u32(value_to_find)); * if (!obj) { * // not found * } else { * // found * } * \endcode * * To iterate over all the elements in the hashtable with the same key, you have to * use tommy_hashdyn_bucket() and follow the tommy_node::next pointer until NULL. * You have also to check explicitely for the key, as the bucket may contains * different keys. * * \code * int value_to_find = 1; * tommy_node* i = tommy_hashdyn_bucket(&hashdyn, tommy_inthash_u32(value_to_find)); * while (i) { * struct object* obj = i->data; // gets the object pointer * * if (obj->value == value_to_find) { * printf("%d\n", obj->value); // process the object * } * * i = i->next; // goes to the next element * } * \endcode * * To remove an element from the hashtable you have to call tommy_hashdyn_remove() * providing a comparison function, its argument, and the hash of the key to search * and remove. * * \code * struct object* obj = tommy_hashdyn_remove(&hashdyn, compare, &value_to_remove, tommy_inthash_u32(value_to_remove)); * if (obj) { * free(obj); // frees the object allocated memory * } * \endcode * * To destroy the hashtable you have to remove all the elements, and deinitialize * the hashtable calling tommy_hashdyn_done(). * * \code * tommy_hashdyn_done(&hashdyn); * \endcode * * If you need to iterate over all the elements in the hashtable, you can use * tommy_hashdyn_foreach() or tommy_hashdyn_foreach_arg(). * If you need a more precise control with a real iteration, you have to insert * all the elements also in a ::tommy_list, and use the list to iterate. * See the \ref multiindex example for more detail. */ #ifndef __TOMMYHASHDYN_H #define __TOMMYHASHDYN_H #include "tommyhash.h" /******************************************************************************/ /* hashdyn */ /** \internal * Initial and minimal size of the hashtable expressed as a power of 2. * The initial size is 2^TOMMY_HASHDYN_BIT. */ #define TOMMY_HASHDYN_BIT 4 /** * Hashtable node. * This is the node that you have to include inside your objects. */ typedef tommy_node tommy_hashdyn_node; /** * Hashtable container type. * \note Don't use internal fields directly, but access the container only using functions. */ typedef struct tommy_hashdyn_struct { tommy_hashdyn_node** bucket; /**< Hash buckets. One list for each hash modulus. */ tommy_uint_t bucket_bit; /**< Bits used in the bit mask. */ tommy_count_t bucket_max; /**< Number of buckets. */ tommy_count_t bucket_mask; /**< Bit mask to access the buckets. */ tommy_count_t count; /**< Number of elements. */ } tommy_hashdyn; /** * Initializes the hashtable. */ void tommy_hashdyn_init(tommy_hashdyn* hashdyn); /** * Deinitializes the hashtable. * * You can call this function with elements still contained, * but such elements are not going to be freed by this call. */ void tommy_hashdyn_done(tommy_hashdyn* hashdyn); /** * Inserts an element in the hashtable. */ void tommy_hashdyn_insert(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node, void* data, tommy_hash_t hash); /** * Searches and removes an element from the hashtable. * You have to provide a compare function and the hash of the element you want to remove. * If the element is not found, 0 is returned. * If more equal elements are present, the first one is removed. * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. * The function should return 0 for equal elements, anything other for different elements. * \param cmp_arg Compare argument passed as first argument of the compare function. * \param hash Hash of the element to find and remove. * \return The removed element, or 0 if not found. */ void* tommy_hashdyn_remove(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash); /** * Gets the bucket of the specified hash. * The bucket is guaranteed to contain ALL the elements with the specified hash, * but it can contain also others. * You can access elements in the bucket following the ::next pointer until 0. * \param hash Hash of the element to find. * \return The head of the bucket, or 0 if empty. */ tommy_inline tommy_hashdyn_node* tommy_hashdyn_bucket(tommy_hashdyn* hashdyn, tommy_hash_t hash) { return hashdyn->bucket[hash & hashdyn->bucket_mask]; } /** * Searches an element in the hashtable. * You have to provide a compare function and the hash of the element you want to find. * If more equal elements are present, the first one is returned. * \param cmp Compare function called with cmp_arg as first argument and with the element to compare as a second one. * The function should return 0 for equal elements, anything other for different elements. * \param cmp_arg Compare argument passed as first argument of the compare function. * \param hash Hash of the element to find. * \return The first element found, or 0 if none. */ tommy_inline void* tommy_hashdyn_search(tommy_hashdyn* hashdyn, tommy_search_func* cmp, const void* cmp_arg, tommy_hash_t hash) { tommy_hashdyn_node* i = tommy_hashdyn_bucket(hashdyn, hash); while (i) { /* we first check if the hash matches, as in the same bucket we may have multiples hash values */ if (i->key == hash && cmp(cmp_arg, i->data) == 0) return i->data; i = i->next; } return 0; } /** * Removes an element from the hashtable. * You must already have the address of the element to remove. * \return The tommy_node::data field of the node removed. */ void* tommy_hashdyn_remove_existing(tommy_hashdyn* hashdyn, tommy_hashdyn_node* node); /** * Calls the specified function for each element in the hashtable. * * You can use this function to deallocate all the elements inserted. * * \code * tommy_hashdyn hashdyn; * * // initializes the hashtable * tommy_hashdyn_init(&hashdyn); * * ... * * // creates an object * struct object* obj = malloc(sizeof(struct object)); * * ... * * // insert it in the hashtable * tommy_hashdyn_insert(&hashdyn, &obj->node, obj, tommy_inthash_u32(obj->value)); * * ... * * // deallocates all the objects iterating the hashtable * tommy_hashdyn_foreach(&hashdyn, free); * * // deallocates the hashtable * tommy_hashdyn_done(&hashdyn); * \endcode */ void tommy_hashdyn_foreach(tommy_hashdyn* hashdyn, tommy_foreach_func* func); /** * Calls the specified function with an argument for each element in the hashtable. */ void tommy_hashdyn_foreach_arg(tommy_hashdyn* hashdyn, tommy_foreach_arg_func* func, void* arg); /** * Gets the number of elements. */ tommy_inline tommy_count_t tommy_hashdyn_count(tommy_hashdyn* hashdyn) { return hashdyn->count; } /** * Gets the size of allocated memory. * It includes the size of the ::tommy_hashdyn_node of the stored elements. */ tommy_size_t tommy_hashdyn_memory_usage(tommy_hashdyn* hashdyn); #endif CombBLAS_beta_16_2/include/Tommy/tommylist.c000644 000765 000024 00000004743 13271404146 022373 0ustar00aydinbulucstaff000000 000000 /* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 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. */ #include "tommylist.h" #include "tommychain.h" void tommy_list_concat(tommy_list* first, tommy_list* second) { tommy_node* first_head; tommy_node* first_tail; tommy_node* second_head; if (tommy_list_empty(second)) return; if (tommy_list_empty(first)) { *first = *second; return; } first_head = tommy_list_head(first); second_head = tommy_list_head(second); first_tail = tommy_list_tail(first); /* set the "circular" prev list */ first_head->prev = second_head->prev; second_head->prev = first_tail; /* set the "0 terminated" next list */ first_tail->next = second_head; } /** \internal * Setup a list. */ tommy_inline void tommy_list_set(tommy_list* list, tommy_node* head, tommy_node* tail) { head->prev = tail; tail->next = 0; *list = head; } void tommy_list_sort(tommy_list* list, tommy_compare_func* cmp) { tommy_chain chain; tommy_node* head; if (tommy_list_empty(list)) return; head = tommy_list_head(list); /* create a chain from the list */ chain.head = head; chain.tail = head->prev; tommy_chain_mergesort(&chain, cmp); /* restore the list */ tommy_list_set(list, chain.head, chain.tail); } CombBLAS_beta_16_2/include/Tommy/tommychain.h000644 000765 000024 00000014356 13271404146 022510 0ustar00aydinbulucstaff000000 000000 /* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 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. */ /** \file * Chain of nodes. * A chain of nodes is an abstraction used to implements complex list operations * like sorting. * * Do not use this directly. Use lists instead. */ #ifndef __TOMMYCHAIN_H #define __TOMMYCHAIN_H #include "tommytypes.h" /******************************************************************************/ /* chain */ /** * Chain of nodes. * A chain of nodes is a sequence of nodes with the following properties: * - It contains at least one node. A chains of zero nodes cannot exist. * - The next field of the tail is of *undefined* value. * - The prev field of the head is of *undefined* value. * - All the other inner prev and next fields are correctly set. */ typedef struct tommy_chain_struct { tommy_node* head; /**< Pointer at the head of the chain. */ tommy_node* tail; /**< Pointer at the tail of the chain. */ } tommy_chain; /** * Splices a chain in the middle of another chain. */ tommy_inline void tommy_chain_splice(tommy_node* first_before, tommy_node* first_after, tommy_node* second_head, tommy_node* second_tail) { /* set the prev list */ first_after->prev = second_tail; second_head->prev = first_before; /* set the next list */ first_before->next = second_head; second_tail->next = first_after; } /** * Concats two chains. */ tommy_inline void tommy_chain_concat(tommy_node* first_tail, tommy_node* second_head) { /* set the prev list */ second_head->prev = first_tail; /* set the next list */ first_tail->next = second_head; } /** * Merges two chains. */ tommy_inline void tommy_chain_merge(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) { tommy_node* first_i = first->head; tommy_node* second_i = second->head; /* merge */ while (1) { if (cmp(first_i->data, second_i->data) > 0) { tommy_node* next = second_i->next; if (first_i == first->head) { tommy_chain_concat(second_i, first_i); first->head = second_i; } else { tommy_chain_splice(first_i->prev, first_i, second_i, second_i); } if (second_i == second->tail) break; second_i = next; } else { if (first_i == first->tail) { tommy_chain_concat(first_i, second_i); first->tail = second->tail; break; } first_i = first_i->next; } } } /** * Merges two chains managing special degenerated cases. * It's funtionally equivalent at tommy_chain_merge() but faster with already ordered chains. */ tommy_inline void tommy_chain_merge_degenerated(tommy_chain* first, tommy_chain* second, tommy_compare_func* cmp) { /* identify the condition first <= second */ if (cmp(first->tail->data, second->head->data) <= 0) { tommy_chain_concat(first->tail, second->head); first->tail = second->tail; return; } /* identify the condition second < first */ /* here we must be strict on comparison to keep the sort stable */ if (cmp(second->tail->data, first->head->data) < 0) { tommy_chain_concat(second->tail, first->head); first->head = second->head; return; } tommy_chain_merge(first, second, cmp); } /** * Max number of elements as a power of 2. */ #define TOMMY_CHAIN_BIT_MAX 32 /** * Sorts a chain. * It's a stable merge sort using power of 2 buckets, with O(N*log(N)) complexity, * similar at the one used in the SGI STL libraries and in the Linux Kernel, * but faster on degenerated cases like already ordered lists. * * SGI STL stl_list.h * http://www.sgi.com/tech/stl/stl_list.h * * Linux Kernel lib/list_sort.c * http://lxr.linux.no/#linux+v2.6.36/lib/list_sort.c */ tommy_inline void tommy_chain_mergesort(tommy_chain* chain, tommy_compare_func* cmp) { /* * Bit buckets of chains. * Each bucket contains 2^i nodes or it's empty. * The chain at address TOMMY_CHAIN_BIT_MAX is an independet variable operating as "carry". * We keep it in the same "bit" vector to avoid reports from the valgrind tool sgcheck. */ tommy_chain bit[TOMMY_CHAIN_BIT_MAX + 1]; /** * Value stored inside the bit bucket. * It's used to know which bucket is empty of full. */ tommy_count_t counter; tommy_node* node = chain->head; tommy_node* tail = chain->tail; tommy_count_t mask; tommy_count_t i; counter = 0; while (1) { tommy_node* next; tommy_chain* last; /* carry bit to add */ last = &bit[TOMMY_CHAIN_BIT_MAX]; bit[TOMMY_CHAIN_BIT_MAX].head = node; bit[TOMMY_CHAIN_BIT_MAX].tail = node; next = node->next; /* add the bit, propagating the carry */ i = 0; mask = counter; while ((mask & 1) != 0) { tommy_chain_merge_degenerated(&bit[i], last, cmp); mask >>= 1; last = &bit[i]; ++i; } /* copy the carry in the first empty bit */ bit[i] = *last; /* add the carry in the counter */ ++counter; if (node == tail) break; node = next; } /* merge the buckets */ i = tommy_ctz_u32(counter); mask = counter >> i; while (mask != 1) { mask >>= 1; if (mask & 1) tommy_chain_merge_degenerated(&bit[i + 1], &bit[i], cmp); else bit[i + 1] = bit[i]; ++i; } *chain = bit[i]; } #endif CombBLAS_beta_16_2/include/Tommy/tommyhash.c000644 000765 000024 00000012200 13271404146 022326 0ustar00aydinbulucstaff000000 000000 /* * Copyright (c) 2010, Andrea Mazzoleni. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. 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. * * 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. */ #include "tommyhash.h" /******************************************************************************/ /* hash */ tommy_inline tommy_uint32_t tommy_le_uint32_read(const void* ptr) { /* allow unaligned read on Intel x86 and x86_64 platforms */ #if defined(__i386__) || defined(_M_IX86) || defined(_X86_) || defined(__x86_64__) || defined(_M_X64) /* defines from http://predef.sourceforge.net/ */ return *(const tommy_uint32_t*)ptr; #else const unsigned char* ptr8 = tommy_cast(const unsigned char*, ptr); return ptr8[0] + ((tommy_uint32_t)ptr8[1] << 8) + ((tommy_uint32_t)ptr8[2] << 16) + ((tommy_uint32_t)ptr8[3] << 24); #endif } #define tommy_rot(x, k) \ (((x) << (k)) | ((x) >> (32 - (k)))) #define tommy_mix(a, b, c) \ do { \ a -= c; a ^= tommy_rot(c, 4); c += b; \ b -= a; b ^= tommy_rot(a, 6); a += c; \ c -= b; c ^= tommy_rot(b, 8); b += a; \ a -= c; a ^= tommy_rot(c, 16); c += b; \ b -= a; b ^= tommy_rot(a, 19); a += c; \ c -= b; c ^= tommy_rot(b, 4); b += a; \ } while (0) #define tommy_final(a, b, c) \ do { \ c ^= b; c -= tommy_rot(b, 14); \ a ^= c; a -= tommy_rot(c, 11); \ b ^= a; b -= tommy_rot(a, 25); \ c ^= b; c -= tommy_rot(b, 16); \ a ^= c; a -= tommy_rot(c, 4); \ b ^= a; b -= tommy_rot(a, 14); \ c ^= b; c -= tommy_rot(b, 24); \ } while (0) tommy_uint32_t tommy_hash_u32(tommy_uint32_t init_val, const void* void_key, tommy_size_t key_len) { const unsigned char* key = tommy_cast(const unsigned char*, void_key); tommy_uint32_t a, b, c; a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + init_val; while (key_len > 12) { a += tommy_le_uint32_read(key + 0); b += tommy_le_uint32_read(key + 4); c += tommy_le_uint32_read(key + 8); tommy_mix(a, b, c); key_len -= 12; key += 12; } switch (key_len) { case 0 : return c; /* used only when called with a zero length */ case 12 : c += tommy_le_uint32_read(key + 8); b += tommy_le_uint32_read(key + 4); a += tommy_le_uint32_read(key + 0); break; case 11 : c += ((tommy_uint32_t)key[10]) << 16; case 10 : c += ((tommy_uint32_t)key[9]) << 8; case 9 : c += key[8]; case 8 : b += tommy_le_uint32_read(key + 4); a += tommy_le_uint32_read(key + 0); break; case 7 : b += ((tommy_uint32_t)key[6]) << 16; case 6 : b += ((tommy_uint32_t)key[5]) << 8; case 5 : b += key[4]; case 4 : a += tommy_le_uint32_read(key + 0); break; case 3 : a += ((tommy_uint32_t)key[2]) << 16; case 2 : a += ((tommy_uint32_t)key[1]) << 8; case 1 : a += key[0]; } tommy_final(a, b, c); return c; } tommy_uint64_t tommy_hash_u64(tommy_uint64_t init_val, const void* void_key, tommy_size_t key_len) { const unsigned char* key = tommy_cast(const unsigned char*, void_key); tommy_uint32_t a, b, c; a = b = c = 0xdeadbeef + ((tommy_uint32_t)key_len) + (init_val & 0xffffffff); c += init_val >> 32; while (key_len > 12) { a += tommy_le_uint32_read(key + 0); b += tommy_le_uint32_read(key + 4); c += tommy_le_uint32_read(key + 8); tommy_mix(a, b, c); key_len -= 12; key += 12; } switch (key_len) { case 0 : return c + ((tommy_uint64_t)b << 32); /* used only when called with a zero length */ case 12 : c += tommy_le_uint32_read(key + 8); b += tommy_le_uint32_read(key + 4); a += tommy_le_uint32_read(key + 0); break; case 11 : c += ((tommy_uint32_t)key[10]) << 16; case 10 : c += ((tommy_uint32_t)key[9]) << 8; case 9 : c += key[8]; case 8 : b += tommy_le_uint32_read(key + 4); a += tommy_le_uint32_read(key + 0); break; case 7 : b += ((tommy_uint32_t)key[6]) << 16; case 6 : b += ((tommy_uint32_t)key[5]) << 8; case 5 : b += key[4]; case 4 : a += tommy_le_uint32_read(key + 0); break; case 3 : a += ((tommy_uint32_t)key[2]) << 16; case 2 : a += ((tommy_uint32_t)key[1]) << 8; case 1 : a += key[0]; } tommy_final(a, b, c); return c + ((tommy_uint64_t)b << 32); } CombBLAS_beta_16_2/include/CombBLAS/OptBuf.h000644 000765 000024 00000006441 13271404146 021750 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _OPT_BUF_H #define _OPT_BUF_H #include "BitMap.h" namespace combblas { /** * This special data structure is used for optimizing BFS iterations * by providing a fixed sized buffer for communication * the contents of the buffer are irrelevant until SpImpl:SpMXSpV starts * hence the copy constructor that doesn't copy contents */ template class OptBuf { public: OptBuf(): isthere(NULL), p_c(0), totmax(0), localm(0) {}; void MarkEmpty() { if(totmax > 0) { isthere->reset(); } } void Set(const std::vector & maxsizes, int mA) { p_c = maxsizes.size(); totmax = std::accumulate(maxsizes.begin(), maxsizes.end(), 0); inds = new IT[totmax]; std::fill_n(inds, totmax, -1); nums = new NT[totmax]; dspls = new int[p_c](); std::partial_sum(maxsizes.begin(), maxsizes.end()-1, dspls+1); localm = mA; isthere = new BitMap(localm); }; ~OptBuf() { if(localm > 0) { delete isthere; } if(totmax > 0) { delete [] inds; delete [] nums; } if(p_c > 0) delete [] dspls; } OptBuf(const OptBuf & rhs) { p_c = rhs.p_c; totmax = rhs.totmax; localm = rhs.localm; inds = new IT[totmax]; nums = new NT[totmax]; dspls = new int[p_c](); isthere = new BitMap(localm); } OptBuf & operator=(const OptBuf & rhs) { if(this != &rhs) { if(localm > 0) { delete isthere; } if(totmax > 0) { delete [] inds; delete [] nums; } if(p_c > 0) delete [] dspls; p_c = rhs.p_c; totmax = rhs.totmax; localm = rhs.localm; inds = new IT[totmax]; nums = new NT[totmax]; dspls = new int[p_c](); isthere = new BitMap(*(rhs.isthere)); } return *this; } IT * inds; NT * nums; int * dspls; BitMap * isthere; int p_c; int totmax; int localm; }; } #endif CombBLAS_beta_16_2/include/CombBLAS/DenseParMat.h000644 000765 000024 00000007361 13271404146 022716 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _DENSE_PAR_MAT_H_ #define _DENSE_PAR_MAT_H_ #include #include #include #include #include #include "CombBLAS.h" #include "Operations.h" #include "MPIOp.h" #include "FullyDistVec.h" namespace combblas { template class SpParMat; template class DenseParMat { public: // Constructors DenseParMat (): array(NULL), m(0), n(0) { commGrid.reset(new CommGrid(MPI_COMM_WORLD, 0, 0)); } DenseParMat (NT value, std::shared_ptr grid, IT rows, IT cols): m(rows), n(cols) { array = SpHelper::allocate2D(rows, cols); for(int i=0; i< rows; ++i) { std::fill_n(array[i], cols, value); // fill array[i][0] ... array[i][cols] with "value" } commGrid.reset(new CommGrid(*grid)); } DenseParMat (NT ** seqarr, std::shared_ptr grid, IT rows, IT cols): array(seqarr), m(rows), n(cols) { commGrid.reset(new CommGrid(*grid)); } DenseParMat (const DenseParMat< IT,NT > & rhs): m(rhs.m), n(rhs.n) // copy constructor { if(rhs.array != NULL) { array = SpHelper::allocate2D(m, n); for(int i=0; i< m; ++i) { std::copy(array[i], array[i]+n, rhs.array[i]); } } commGrid.reset(new CommGrid(*(rhs.commGrid))); } DenseParMat< IT,NT > & operator=(const DenseParMat< IT,NT > & rhs); template DenseParMat< IT,NT > & operator+=(const SpParMat< IT,NT,DER > & rhs); // add a sparse matrix template FullyDistVec< IT,NT > Reduce(Dim dim, _BinaryOperation __binary_op, NT identity) const; ~DenseParMat () { if(array != NULL) SpHelper::deallocate2D(array, m); } std::shared_ptr getcommgrid () { return commGrid; } IT grows() const { IT glrows; MPI_Allreduce(&m, &glrows, 1, MPIType(), MPI_SUM, commGrid->GetColWorld()); return glrows; } IT gcols() const { IT glcols; MPI_Allreduce(&n, &glcols, 1, MPIType(), MPI_SUM, commGrid->GetRowWorld()); return glcols; } private: std::shared_ptr commGrid; NT ** array; IT m, n; // Local row and columns template friend class SpParMat; }; } #include "DenseParMat.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/DistEdgeList.h000644 000765 000024 00000007750 13271404146 023101 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.4 -------------------------------------------------*/ /* date: 1/17/2014 ---------------------------------------------*/ /* authors: Aydin Buluc (abuluc@lbl.gov), Adam Lugowski --------*/ /****************************************************************/ /* Copyright (c) 2010-2014, The Regents of the University of California 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. */ #ifndef _DIST_EDGE_LIST_H_ #define _DIST_EDGE_LIST_H_ #include #include #include #include #include #include #include "CombBLAS.h" #include "SpMat.h" #include "SpTuples.h" #include "SpDCCols.h" #include "CommGrid.h" #include "MPIType.h" #include "LocArr.h" #include "SpDefs.h" #include "Deleter.h" #include "SpHelper.h" #include "SpParHelper.h" #include "DenseParMat.h" #include "FullyDistVec.h" #include "Friends.h" #include "Operations.h" namespace combblas { /** * From Graph 500 reference implementation v2.1.1 **/ typedef struct packed_edge { uint32_t v0_low; uint32_t v1_low; uint32_t high; /* v1 in high half, v0 in low half */ } packed_edge; static inline int64_t get_v0_from_edge(const packed_edge* p) { return (p->v0_low | ((int64_t)((int16_t)(p->high & 0xFFFF)) << 32)); } static inline int64_t get_v1_from_edge(const packed_edge* p) { return (p->v1_low | ((int64_t)((int16_t)(p->high >> 16)) << 32)); } static inline void write_edge(packed_edge* p, int64_t v0, int64_t v1) { p->v0_low = (uint32_t)v0; p->v1_low = (uint32_t)v1; p->high = ((v0 >> 32) & 0xFFFF) | (((v1 >> 32) & 0xFFFF) << 16); } template class DistEdgeList { public: // Constructors DistEdgeList (); DistEdgeList(MPI_Comm & myWorld); DistEdgeList (const char * filename, IT globaln, IT globalm); // read from binary in parallel ~DistEdgeList (); void Dump64bit(std::string filename); void Dump32bit(std::string filename); void GenGraph500Data(double initiator[4], int log_numverts, int edgefactor, bool scramble =false, bool packed=false); void CleanupEmpties(); int64_t getGlobalV() const { return globalV; } IT getNumLocalEdges() const { return nedges; } IT* getEdges() const {return edges;} packed_edge * getPackedEdges() const { return pedges; } std::shared_ptr commGrid; private: IT* edges; // edge list composed of pairs of edge endpoints. // Edge i goes from edges[2*i+0] to edges[2*i+1] packed_edge * pedges; IT nedges; // number of local edges IT memedges; // number of edges for which there is space. nedges <= memedges int64_t globalV; void SetMemSize(IT ne); template friend void PermEdges(DistEdgeList & DEL); template friend void RenameVertices(DistEdgeList & DEL); template friend class SpParMat; }; template void PermEdges(DistEdgeList & DEL); } #include "DistEdgeList.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/SpCCols.h000644 000765 000024 00000023746 13271404146 022066 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SP_CCOLS_H_ #define _SP_CCOLS_H_ #include #include "SpMat.h" // Best to include the base class first #include "SpHelper.h" #include "csc.h" namespace combblas { template class SpCCols: public SpMat > { public: typedef IT LocalIT; typedef NT LocalNT; // Constructors : SpCCols (); SpCCols (IT size, IT nRow, IT nCol); SpCCols (const SpTuples & rhs, bool transpose); SpCCols (const SpDCCols & rhs):nnz(0), n(0), m(0), splits(0), csc(NULL) { SpTuples tuples(rhs); SpCCols object(tuples, false); *this = object; // its members are already initialized by the initializer list } SpCCols (const SpCCols & rhs); // Actual copy constructor ~SpCCols(); // Member Functions and Operators: SpCCols & operator= (const SpCCols & rhs); SpCCols & operator+= (const SpCCols & rhs); void RowSplit(int numsplits); void ColSplit(int parts, std::vector< SpCCols > & matrices); //!< \attention Destroys calling object (*this) void CreateImpl(const std::vector & essentials); void CreateImpl(IT size, IT nRow, IT nCol, std::tuple * mytuples); Arr GetArrays() const; std::vector GetEssentials() const; const static IT esscount; IT getnrow() const { return m; } IT getncol() const { return n; } IT getnnz() const { return nnz; } int getnsplit() const { return splits; } auto GetInternal() const { return GetCSC(); } auto GetInternal(int i) const { return GetCSC(i); } class SpColIter //! Iterate over (sparse) columns of the sparse matrix { public: class NzIter //! Iterate over the nonzeros of the sparse column { public: NzIter(IT * ir = NULL, NT * num = NULL) : rid(ir), val(num) {} bool operator==(const NzIter & other) { return(rid == other.rid); // compare pointers } bool operator!=(const NzIter & other) { return(rid != other.rid); } NzIter & operator++() // prefix operator { ++rid; ++val; return(*this); } NzIter operator++(int) // postfix operator { NzIter tmp(*this); ++(*this); return(tmp); } IT rowid() const //!< Return the "local" rowid of the current nonzero entry. { return (*rid); } NT & value() //!< value is returned by reference for possible updates { return (*val); } private: IT * rid; NT * val; }; SpColIter(IT * jc = NULL) : begcptr(jc), curcptr(jc) {} bool operator==(const SpColIter& other) { return(curcptr == other.curcptr); // compare pointers } bool operator!=(const SpColIter& other) { return(curcptr != other.curcptr); } SpColIter& operator++() // prefix operator (different across derived classes) { ++curcptr; return(*this); } IT colid() const //!< Return the "local" colid of the current column. { return (curcptr-begcptr); } IT colptr() const // only needed internally by ::begnz() below { return (*curcptr); } IT colptrnext() const // only needed internally by ::begnz() below { return (*(curcptr+1)); } IT nnz() const { return (colptrnext() - colptr()); } private: IT * begcptr; IT * curcptr; }; SpColIter begcol() // serial version { if( nnz > 0 ) return SpColIter(csc->jc); else return SpColIter(NULL); } SpColIter endcol() //serial version { if( nnz > 0 ) return SpColIter(csc->jc + n); // (csc->jc+n) should never execute because SpColIter::colptrnext() would point invalid else return SpColIter(NULL); } SpColIter begcol(int i) // multithreaded version { if( cscarr[i] ) return SpColIter(cscarr[i]->jc); else return SpColIter(NULL); } SpColIter endcol(int i) //multithreaded version { if( cscarr[i] ) return SpColIter(cscarr[i]->jc + n); // (csc->jc+n) should never execute because SpColIter::colptrnext() would point invalid else return SpColIter(NULL); } typename SpColIter::NzIter begnz(const SpColIter & ccol) //!< Return the beginning iterator for the nonzeros of the current column { return typename SpColIter::NzIter( csc->ir + ccol.colptr(), csc->num + ccol.colptr() ); } typename SpColIter::NzIter endnz(const SpColIter & ccol) //!< Return the ending iterator for the nonzeros of the current column { return typename SpColIter::NzIter( csc->ir + ccol.colptrnext(), NULL ); } typename SpColIter::NzIter begnz(const SpColIter & ccol, int i) //!< multithreaded version { return typename SpColIter::NzIter( cscarr[i]->ir + ccol.colptr(), cscarr[i]->num + ccol.colptr() ); } typename SpColIter::NzIter endnz(const SpColIter & ccol, int i) //!< multithreaded version { return typename SpColIter::NzIter( cscarr[i]->ir + ccol.colptrnext(), NULL ); } void PrintInfo() const; private: void SubPrintInfo(Csc * mycsc) const; // Anonymous union union { Csc * csc; Csc ** cscarr; }; IT m; IT n; IT nnz; int splits; // for multithreading void CopyCsc(Csc * source); Csc * GetCSC() const // only for single threaded matrices { return csc; } Csc * GetCSC(int i) const // only for split (multithreaded) matrices { return cscarr[i]; } template friend void csc_gespmv_dense (const SpCCols & A, const RHS * x, LHS * y); //!< dense vector (not implemented) // friend int generic_gespmv_threaded (const SpMat & A, const int32_t * indx, const IVT * numx, int32_t nnzx, int32_t * & sendindbuf, OVT * & sendnumbuf, int * & sdispls, int p_c, PreAllocatedSPA & SPA); }; // At this point, complete type of of SpCCols is known, safe to declare these specialization (but macros won't work as they are preprocessed) // General case #1: When both NT is the same template struct promote_trait< SpCCols , SpCCols > { typedef SpCCols T_promote; }; // General case #2: First is boolean the second is anything except boolean (to prevent ambiguity) template struct promote_trait< SpCCols , SpCCols, typename combblas::disable_if< combblas::is_boolean::value >::type > { typedef SpCCols T_promote; }; // General case #3: Second is boolean the first is anything except boolean (to prevent ambiguity) template struct promote_trait< SpCCols , SpCCols, typename combblas::disable_if< combblas::is_boolean::value >::type > { typedef SpCCols T_promote; }; template struct promote_trait< SpCCols , SpCCols > { typedef SpCCols T_promote; }; template struct promote_trait< SpCCols , SpCCols > { typedef SpCCols T_promote; }; template struct promote_trait< SpCCols , SpCCols > { typedef SpCCols T_promote; }; template struct promote_trait< SpCCols , SpCCols > { typedef SpCCols T_promote; }; // Capture everything of the form SpCCols // it may come as a surprise that the partial specializations can // involve more template parameters than the primary template template struct create_trait< SpCCols , NIT, NNT > { typedef SpCCols T_inferred; }; } #include "SpCCols.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/LocArr.h000644 000765 000024 00000003774 13271404146 021741 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _LOC_ARR_H_ #define _LOC_ARR_H_ namespace combblas { template struct LocArr { LocArr():addr(NULL),count(0) {} LocArr(V * myaddr, C mycount): addr(myaddr ), count(mycount){} V * addr; C count; }; template struct Arr { Arr(IT indsize, IT numsize) { indarrs.resize(indsize); numarrs.resize(numsize); } std::vector< LocArr > indarrs; std::vector< LocArr > numarrs; IT totalsize() { return indarrs.size() + numarrs.size(); } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/SpDCCols.cpp000644 000765 000024 00000107576 13271404146 022531 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include "SpDCCols.h" #include "Deleter.h" #include #include #include #include #include #include namespace combblas { /****************************************************************************/ /********************* PUBLIC CONSTRUCTORS/DESTRUCTORS **********************/ /****************************************************************************/ template const IT SpDCCols::esscount = static_cast(4); template SpDCCols::SpDCCols():dcsc(NULL), m(0), n(0), nnz(0), splits(0){ } // Allocate all the space necessary template SpDCCols::SpDCCols(IT size, IT nRow, IT nCol, IT nzc) :m(nRow), n(nCol), nnz(size), splits(0) { if(nnz > 0) dcsc = new Dcsc(nnz, nzc); else dcsc = NULL; } template SpDCCols::~SpDCCols() { if(nnz > 0) { if(dcsc != NULL) { if(splits > 0) { for(int i=0; i SpDCCols::SpDCCols(const SpDCCols & rhs) : m(rhs.m), n(rhs.n), nnz(rhs.nnz), splits(rhs.splits) { if(splits > 0) { for(int i=0; i SpDCCols (may use a private memory heap) * @param[in] rhs if transpose=true, * \n then rhs is assumed to be a row sorted SpTuples object * \n else rhs is assumed to be a column sorted SpTuples object **/ template SpDCCols::SpDCCols(const SpTuples & rhs, bool transpose) : m(rhs.m), n(rhs.n), nnz(rhs.nnz), splits(0) { if(nnz == 0) // m by n matrix of complete zeros { if(transpose) std::swap(m,n); dcsc = NULL; } else { if(transpose) { std::swap(m,n); IT localnzc = 1; for(IT i=1; i< rhs.nnz; ++i) { if(rhs.rowindex(i) != rhs.rowindex(i-1)) { ++localnzc; } } dcsc = new Dcsc(rhs.nnz,localnzc); dcsc->jc[0] = rhs.rowindex(0); dcsc->cp[0] = 0; for(IT i=0; iir[i] = rhs.colindex(i); // copy rhs.jc to ir since this transpose=true dcsc->numx[i] = rhs.numvalue(i); } IT jspos = 1; for(IT i=1; ijc[jspos-1]) { dcsc->jc[jspos] = rhs.rowindex(i); // copy rhs.ir to jc since this transpose=true dcsc->cp[jspos++] = i; } } dcsc->cp[jspos] = rhs.nnz; } else { IT localnzc = 1; for(IT i=1; i(rhs.nnz,localnzc); dcsc->jc[0] = rhs.colindex(0); dcsc->cp[0] = 0; for(IT i=0; iir[i] = rhs.rowindex(i); // copy rhs.ir to ir since this transpose=false dcsc->numx[i] = rhs.numvalue(i); } IT jspos = 1; for(IT i=1; ijc[jspos-1]) { dcsc->jc[jspos] = rhs.colindex(i); // copy rhs.jc to jc since this transpose=true dcsc->cp[jspos++] = i; } } dcsc->cp[jspos] = rhs.nnz; } } } /** * Multithreaded Constructor for converting tuples matrix -> SpDCCols * @param[in] rhs if transpose=true, * \n then tuples is assumed to be a row sorted list of tuple objects * \n else tuples is assumed to be a column sorted list of tuple objects **/ template SpDCCols::SpDCCols(IT nRow, IT nCol, IT nTuples, const std::tuple* tuples, bool transpose) : m(nRow), n(nCol), nnz(nTuples), splits(0) { if(nnz == 0) // m by n matrix of complete zeros { dcsc = NULL; } else { int totThreads=1; #ifdef _OPENMP #pragma omp parallel { totThreads = omp_get_num_threads(); } #endif std::vector tstart(totThreads); std::vector tend(totThreads); std::vector tdisp(totThreads+1); // extra memory, but replaces an O(nnz) loop by an O(nzc) loop IT* temp_jc = new IT[nTuples]; IT* temp_cp = new IT[nTuples]; #ifdef _OPENMP #pragma omp parallel #endif { int threadID = 0; #ifdef _OPENMP threadID = omp_get_thread_num(); #endif IT start = threadID * (nTuples / totThreads); IT end = (threadID + 1) * (nTuples / totThreads); if(threadID == (totThreads-1)) end = nTuples; IT curpos=start; if(end>start) // no work for the current thread { temp_jc[start] = std::get<1>(tuples[start]); temp_cp[start] = start; for (IT i = start+1; i < end; ++i) { if(std::get<1>(tuples[i]) != temp_jc[curpos] ) { temp_jc[++curpos] = std::get<1>(tuples[i]); temp_cp[curpos] = i; } } } tstart[threadID] = start; if(end>start) tend[threadID] = curpos+1; else tend[threadID] = end; // start=end } // serial part for(int t=totThreads-1; t>0; --t) { if(tend[t] > tstart[t] && tend[t-1] > tstart[t-1]) { if(temp_jc[tstart[t]] == temp_jc[tend[t-1]-1]) { tstart[t] ++; } } } tdisp[0] = 0; for(int t=0; t(nTuples,localnzc); #ifdef _OPENMP #pragma omp parallel #endif { int threadID = 0; #ifdef _OPENMP threadID = omp_get_thread_num(); #endif std::copy(temp_jc + tstart[threadID], temp_jc + tend[threadID], dcsc->jc + tdisp[threadID]); std::copy(temp_cp + tstart[threadID], temp_cp + tend[threadID], dcsc->cp + tdisp[threadID]); } dcsc->cp[localnzc] = nTuples; delete [] temp_jc; delete [] temp_cp; #ifdef _OPENMP #pragma omp parallel for schedule (static) #endif for(IT i=0; iir[i] = std::get<0>(tuples[i]); dcsc->numx[i] = std::get<2>(tuples[i]); } } if(transpose) Transpose(); // this is not efficient, think to improve later. We included this parameter anyway to make this constructor different from another constracttor when the fourth argument is passed as 0. } /* template SpDCCols::SpDCCols(IT nRow, IT nCol, IT nTuples, const tuple* tuples) : m(nRow), n(nCol), nnz(nTuples), splits(0) { if(nnz == 0) // m by n matrix of complete zeros { dcsc = NULL; } else { IT localnzc = 1; #pragma omp parallel for schedule (static) default(shared) reduction(+:localnzc) for(IT i=1; i(tuples[i]) != std::get<1>(tuples[i-1])) { ++localnzc; } } dcsc = new Dcsc(nTuples,localnzc); dcsc->jc[0] = std::get<1>(tuples[0]); dcsc->cp[0] = 0; #pragma omp parallel for schedule (static) for(IT i=0; iir[i] = std::get<0>(tuples[i]); dcsc->numx[i] = std::get<2>(tuples[i]); } IT jspos = 1; for(IT i=1; i(tuples[i]) != dcsc->jc[jspos-1]) { dcsc->jc[jspos] = std::get<1>(tuples[i]); dcsc->cp[jspos++] = i; } } dcsc->cp[jspos] = nTuples; } } */ /****************************************************************************/ /************************** PUBLIC OPERATORS ********************************/ /****************************************************************************/ /** * The assignment operator operates on an existing object * The assignment operator is the only operator that is not inherited. * But there is no need to call base's assigment operator as it has no data members */ template SpDCCols & SpDCCols::operator=(const SpDCCols & rhs) { // this pointer stores the address of the class instance // check for self assignment using address comparison if(this != &rhs) { if(dcsc != NULL && nnz > 0) { delete dcsc; } if(rhs.dcsc != NULL) { dcsc = new Dcsc(*(rhs.dcsc)); nnz = rhs.nnz; } else { dcsc = NULL; nnz = 0; } m = rhs.m; n = rhs.n; splits = rhs.splits; } return *this; } template SpDCCols & SpDCCols::operator+= (const SpDCCols & rhs) { // this pointer stores the address of the class instance // check for self assignment using address comparison if(this != &rhs) { if(m == rhs.m && n == rhs.n) { if(rhs.nnz == 0) { return *this; } else if(nnz == 0) { dcsc = new Dcsc(*(rhs.dcsc)); nnz = dcsc->nz; } else { (*dcsc) += (*(rhs.dcsc)); nnz = dcsc->nz; } } else { std::cout<< "Not addable: " << m << "!=" << rhs.m << " or " << n << "!=" << rhs.n < template SpDCCols* SpDCCols::PruneI(_UnaryOperation __unary_op, bool inPlace, GlobalIT rowOffset, GlobalIT colOffset) { if(nnz > 0) { Dcsc* ret = dcsc->PruneI (__unary_op, inPlace, rowOffset, colOffset); if (inPlace) { nnz = dcsc->nz; if(nnz == 0) { delete dcsc; dcsc = NULL; } return NULL; } else { // wrap the new pruned Dcsc into a new SpDCCols SpDCCols* retcols = new SpDCCols(); retcols->dcsc = ret; retcols->nnz = retcols->dcsc->nz; retcols->n = n; retcols->m = m; return retcols; } } else { if (inPlace) { return NULL; } else { SpDCCols* retcols = new SpDCCols(); retcols->dcsc = NULL; retcols->nnz = 0; retcols->n = n; retcols->m = m; return retcols; } } } template template SpDCCols* SpDCCols::Prune(_UnaryOperation __unary_op, bool inPlace) { if(nnz > 0) { Dcsc* ret = dcsc->Prune (__unary_op, inPlace); if (inPlace) { nnz = dcsc->nz; if(nnz == 0) { delete dcsc; dcsc = NULL; } return NULL; } else { // wrap the new pruned Dcsc into a new SpDCCols SpDCCols* retcols = new SpDCCols(); retcols->dcsc = ret; retcols->nnz = retcols->dcsc->nz; retcols->n = n; retcols->m = m; return retcols; } } else { if (inPlace) { return NULL; } else { SpDCCols* retcols = new SpDCCols(); retcols->dcsc = NULL; retcols->nnz = 0; retcols->n = n; retcols->m = m; return retcols; } } } template template SpDCCols* SpDCCols::PruneColumn(NT* pvals, _BinaryOperation __binary_op, bool inPlace) { if(nnz > 0) { Dcsc* ret = dcsc->PruneColumn (pvals, __binary_op, inPlace); if (inPlace) { nnz = dcsc->nz; if(nnz == 0) { delete dcsc; dcsc = NULL; } return NULL; } else { // wrap the new pruned Dcsc into a new SpDCCols SpDCCols* retcols = new SpDCCols(); retcols->dcsc = ret; retcols->nnz = retcols->dcsc->nz; retcols->n = n; retcols->m = m; return retcols; } } else { if (inPlace) { return NULL; } else { SpDCCols* retcols = new SpDCCols(); retcols->dcsc = NULL; retcols->nnz = 0; retcols->n = n; retcols->m = m; return retcols; } } } template template SpDCCols* SpDCCols::PruneColumn(IT* pinds, NT* pvals, _BinaryOperation __binary_op, bool inPlace) { if(nnz > 0) { Dcsc* ret = dcsc->PruneColumn (pinds, pvals, __binary_op, inPlace); if (inPlace) { nnz = dcsc->nz; if(nnz == 0) { delete dcsc; dcsc = NULL; } return NULL; } else { // wrap the new pruned Dcsc into a new SpDCCols SpDCCols* retcols = new SpDCCols(); retcols->dcsc = ret; retcols->nnz = retcols->dcsc->nz; retcols->n = n; retcols->m = m; return retcols; } } else { if (inPlace) { return NULL; } else { SpDCCols* retcols = new SpDCCols(); retcols->dcsc = NULL; retcols->nnz = 0; retcols->n = n; retcols->m = m; return retcols; } } } template void SpDCCols::EWiseMult (const SpDCCols & rhs, bool exclude) { if(this != &rhs) { if(m == rhs.m && n == rhs.n) { if(rhs.nnz == 0) { if(exclude) // then we don't exclude anything { return; } else // A .* zeros() is zeros() { *this = SpDCCols(0,m,n,0); // completely reset the matrix } } else if (rhs.nnz != 0 && nnz != 0) { dcsc->EWiseMult (*(rhs.dcsc), exclude); nnz = dcsc->nz; if(nnz == 0 ) dcsc = NULL; } } else { std::cout<< "Matrices do not conform for A .* op(B) !"< void SpDCCols::EWiseScale(NT ** scaler, IT m_scaler, IT n_scaler) { if(m == m_scaler && n == n_scaler) { if(nnz > 0) dcsc->EWiseScale (scaler); } else { std::cout<< "Matrices do not conform for EWiseScale !"< void SpDCCols::CreateImpl(IT * _cp, IT * _jc, IT * _ir, NT * _numx, IT _nz, IT _nzc, IT _m, IT _n) { m = _m; n = _n; nnz = _nz; if(nnz > 0) dcsc = new Dcsc(_cp, _jc, _ir, _numx, _nz, _nzc, false); // memory not owned by DCSC else dcsc = NULL; } template void SpDCCols::CreateImpl(const std::vector & essentials) { assert(essentials.size() == esscount); nnz = essentials[0]; m = essentials[1]; n = essentials[2]; if(nnz > 0) dcsc = new Dcsc(nnz,essentials[3]); else dcsc = NULL; } template void SpDCCols::CreateImpl(IT size, IT nRow, IT nCol, std::tuple * mytuples) { SpTuples tuples(size, nRow, nCol, mytuples); tuples.SortColBased(); #ifdef DEBUG std::pair rlim = tuples.RowLimits(); std::pair clim = tuples.ColLimits(); std::ofstream oput; std::stringstream ss; std::string rank; int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); ss << myrank; ss >> rank; std::string ofilename = "Read"; ofilename += rank; oput.open(ofilename.c_str(), std::ios_base::app ); oput << "Creating of dimensions " << nRow << "-by-" << nCol << " of size: " << size << " with row range (" << rlim.first << "," << rlim.second << ") and column range (" << clim.first << "," << clim.second << ")" << std::endl; if(tuples.getnnz() > 0) { IT minfr = joker::get<0>(tuples.front()); IT minto = joker::get<1>(tuples.front()); IT maxfr = joker::get<0>(tuples.back()); IT maxto = joker::get<1>(tuples.back()); oput << "Min: " << minfr << ", " << minto << "; Max: " << maxfr << ", " << maxto << std::endl; } oput.close(); #endif SpDCCols object(tuples, false); *this = object; } template std::vector SpDCCols::GetEssentials() const { std::vector essentials(esscount); essentials[0] = nnz; essentials[1] = m; essentials[2] = n; essentials[3] = (nnz > 0) ? dcsc->nzc : 0; return essentials; } template template SpDCCols::operator SpDCCols () const { Dcsc * convert; if(nnz > 0) convert = new Dcsc(*dcsc); else convert = NULL; return SpDCCols(m, n, convert); } template template SpDCCols::operator SpDCCols () const { Dcsc * convert; if(nnz > 0) convert = new Dcsc(*dcsc); else convert = NULL; return SpDCCols(m, n, convert); } template Arr SpDCCols::GetArrays() const { Arr arr(3,1); if(nnz > 0) { arr.indarrs[0] = LocArr(dcsc->cp, dcsc->nzc+1); arr.indarrs[1] = LocArr(dcsc->jc, dcsc->nzc); arr.indarrs[2] = LocArr(dcsc->ir, dcsc->nz); arr.numarrs[0] = LocArr(dcsc->numx, dcsc->nz); } else { arr.indarrs[0] = LocArr(NULL, 0); arr.indarrs[1] = LocArr(NULL, 0); arr.indarrs[2] = LocArr(NULL, 0); arr.numarrs[0] = LocArr(NULL, 0); } return arr; } /** * O(nnz log(nnz)) time Transpose function * \remarks Performs a lexicographical sort * \remarks Mutator function (replaces the calling object with its transpose) */ template void SpDCCols::Transpose() { if(nnz > 0) { SpTuples Atuples(*this); Atuples.SortRowBased(); // destruction of (*this) is handled by the assignment operator *this = SpDCCols(Atuples,true); } else { *this = SpDCCols(0, n, m, 0); } } /** * O(nnz log(nnz)) time Transpose function * \remarks Performs a lexicographical sort * \remarks Const function (doesn't mutate the calling object) */ template SpDCCols SpDCCols::TransposeConst() const { SpTuples Atuples(*this); Atuples.SortRowBased(); return SpDCCols(Atuples,true); } /** * O(nnz log(nnz)) time Transpose function * \remarks Performs a lexicographical sort * \remarks Const function (doesn't mutate the calling object) */ template SpDCCols * SpDCCols::TransposeConstPtr() const { SpTuples Atuples(*this); Atuples.SortRowBased(); return new SpDCCols(Atuples,true); } /** * Splits the matrix into two parts, simply by cutting along the columns * Simple algorithm that doesn't intend to split perfectly, but it should do a pretty good job * Practically destructs the calling object also (frees most of its memory) * \todo {special case of ColSplit, to be deprecated...} */ template void SpDCCols::Split(SpDCCols & partA, SpDCCols & partB) { IT cut = n/2; if(cut == 0) { std::cout<< "Matrix is too small to be splitted" << std::endl; return; } Dcsc *Adcsc = NULL; Dcsc *Bdcsc = NULL; if(nnz != 0) { dcsc->Split(Adcsc, Bdcsc, cut); } partA = SpDCCols (m, cut, Adcsc); partB = SpDCCols (m, n-cut, Bdcsc); // handle destruction through assignment operator *this = SpDCCols(); } /** * Splits the matrix into "parts", simply by cutting along the columns * Simple algorithm that doesn't intend to split perfectly, but it should do a pretty good job * Practically destructs the calling object also (frees most of its memory) */ template void SpDCCols::ColSplit(int parts, std::vector< SpDCCols > & matrices) { if(parts < 2) { matrices.emplace_back(*this); } else { std::vector cuts(parts-1); for(int i=0; i< (parts-1); ++i) { cuts[i] = (i+1) * (n/parts); } if(n < parts) { std::cout<< "Matrix is too small to be splitted" << std::endl; return; } std::vector< Dcsc * > dcscs(parts, NULL); if(nnz != 0) { dcsc->ColSplit(dcscs, cuts); } for(int i=0; i< (parts-1); ++i) { SpDCCols matrix = SpDCCols(m, (n/parts), dcscs[i]); matrices.emplace_back(matrix); } SpDCCols matrix = SpDCCols(m, n-cuts[parts-2], dcscs[parts-1]); matrices.emplace_back(matrix); } *this = SpDCCols(); // handle destruction through assignment operator } /** * Concatenates (merges) multiple matrices (cut along the columns) into 1 piece * ColSplit() method should have been executed on the object beforehand */ template void SpDCCols::ColConcatenate(std::vector< SpDCCols > & matrices) { std::vector< SpDCCols * > nonempties; std::vector< Dcsc * > dcscs; std::vector< IT > offsets; IT runningoffset = 0; for(size_t i=0; i< matrices.size(); ++i) { if(matrices[i].nnz != 0) { nonempties.push_back(&(matrices[i])); dcscs.push_back(matrices[i].dcsc); offsets.push_back(runningoffset); } runningoffset += matrices[i].n; } if(nonempties.size() < 1) { #ifdef DEBUG std::cout << "Nothing to ColConcatenate" << std::endl; #endif n = runningoffset; } else if(nonempties.size() < 2) { *this = *(nonempties[0]); n = runningoffset; } else // nonempties.size() > 1 { Dcsc * Cdcsc = new Dcsc(); Cdcsc->ColConcatenate(dcscs, offsets); *this = SpDCCols (nonempties[0]->m, runningoffset, Cdcsc); } // destruct parameters for(size_t i=0; i< matrices.size(); ++i) { matrices[i] = SpDCCols(); } } /** * Merges two matrices (cut along the columns) into 1 piece * Split method should have been executed on the object beforehand **/ template void SpDCCols::Merge(SpDCCols & partA, SpDCCols & partB) { assert( partA.m == partB.m ); Dcsc * Cdcsc = new Dcsc(); if(partA.nnz == 0 && partB.nnz == 0) { Cdcsc = NULL; } else if(partA.nnz == 0) { Cdcsc = new Dcsc(*(partB.dcsc)); std::transform(Cdcsc->jc, Cdcsc->jc + Cdcsc->nzc, Cdcsc->jc, std::bind2nd(std::plus(), partA.n)); } else if(partB.nnz == 0) { Cdcsc = new Dcsc(*(partA.dcsc)); } else { Cdcsc->Merge(partA.dcsc, partB.dcsc, partA.n); } *this = SpDCCols (partA.m, partA.n + partB.n, Cdcsc); partA = SpDCCols(); partB = SpDCCols(); } /** * C += A*B' (Using OuterProduct Algorithm) * This version is currently limited to multiplication of matrices with the same precision * (e.g. it can't multiply double-precision matrices with booleans) * The multiplication is on the specified semiring (passed as parameter) */ template template int SpDCCols::PlusEq_AnXBt(const SpDCCols & A, const SpDCCols & B) { if(A.isZero() || B.isZero()) { return -1; // no need to do anything } Isect *isect1, *isect2, *itr1, *itr2, *cols, *rows; SpHelper::SpIntersect(*(A.dcsc), *(B.dcsc), cols, rows, isect1, isect2, itr1, itr2); IT kisect = static_cast(itr1-isect1); // size of the intersection ((itr1-isect1) == (itr2-isect2)) if(kisect == 0) { DeleteAll(isect1, isect2, cols, rows); return -1; } StackEntry< NT, std::pair > * multstack; IT cnz = SpHelper::SpCartesian< SR > (*(A.dcsc), *(B.dcsc), kisect, isect1, isect2, multstack); DeleteAll(isect1, isect2, cols, rows); IT mdim = A.m; IT ndim = B.m; // since B has already been transposed if(isZero()) { dcsc = new Dcsc(multstack, mdim, ndim, cnz); } else { dcsc->AddAndAssign(multstack, mdim, ndim, cnz); } nnz = dcsc->nz; delete [] multstack; return 1; } /** * C += A*B (Using ColByCol Algorithm) * This version is currently limited to multiplication of matrices with the same precision * (e.g. it can't multiply double-precision matrices with booleans) * The multiplication is on the specified semiring (passed as parameter) */ template template int SpDCCols::PlusEq_AnXBn(const SpDCCols & A, const SpDCCols & B) { if(A.isZero() || B.isZero()) { return -1; // no need to do anything } StackEntry< NT, std::pair > * multstack; int cnz = SpHelper::SpColByCol< SR > (*(A.dcsc), *(B.dcsc), A.n, multstack); IT mdim = A.m; IT ndim = B.n; if(isZero()) { dcsc = new Dcsc(multstack, mdim, ndim, cnz); } else { dcsc->AddAndAssign(multstack, mdim, ndim, cnz); } nnz = dcsc->nz; delete [] multstack; return 1; } template template int SpDCCols::PlusEq_AtXBn(const SpDCCols & A, const SpDCCols & B) { std::cout << "PlusEq_AtXBn function has not been implemented yet !" << std::endl; return 0; } template template int SpDCCols::PlusEq_AtXBt(const SpDCCols & A, const SpDCCols & B) { std::cout << "PlusEq_AtXBt function has not been implemented yet !" << std::endl; return 0; } template SpDCCols SpDCCols::operator() (IT ri, IT ci) const { IT * itr = std::find(dcsc->jc, dcsc->jc + dcsc->nzc, ci); if(itr != dcsc->jc + dcsc->nzc) { IT irbeg = dcsc->cp[itr - dcsc->jc]; IT irend = dcsc->cp[itr - dcsc->jc + 1]; IT * ele = std::find(dcsc->ir + irbeg, dcsc->ir + irend, ri); if(ele != dcsc->ir + irend) { SpDCCols SingEleMat(1, 1, 1, 1); // 1-by-1 matrix with 1 nonzero *(SingEleMat.dcsc->numx) = dcsc->numx[ele - dcsc->ir]; *(SingEleMat.dcsc->ir) = *ele; *(SingEleMat.dcsc->jc) = *itr; (SingEleMat.dcsc->cp)[0] = 0; (SingEleMat.dcsc->cp)[1] = 1; return SingEleMat; } else { return SpDCCols(); // 0-by-0 empty matrix } } else { return SpDCCols(); // 0-by-0 empty matrix } } /** * The almighty indexing polyalgorithm * Calls different subroutines depending the sparseness of ri/ci */ template SpDCCols SpDCCols::operator() (const std::vector & ri, const std::vector & ci) const { typedef PlusTimesSRing PT; IT rsize = ri.size(); IT csize = ci.size(); if(rsize == 0 && csize == 0) { // return an m x n matrix of complete zeros // since we don't know whether columns or rows are indexed return SpDCCols (0, m, n, 0); } else if(rsize == 0) { return ColIndex(ci); } else if(csize == 0) { SpDCCols LeftMatrix(rsize, rsize, this->m, ri, true); return LeftMatrix.OrdColByCol< PT >(*this); } else // this handles the (rsize=1 && csize=1) case as well { SpDCCols LeftMatrix(rsize, rsize, this->m, ri, true); SpDCCols RightMatrix(csize, this->n, csize, ci, false); return LeftMatrix.OrdColByCol< PT >( OrdColByCol< PT >(RightMatrix) ); } } template std::ofstream & SpDCCols::put(std::ofstream & outfile) const { if(nnz == 0) { outfile << "Matrix doesn't have any nonzeros" < tuples(*this); outfile << tuples << std::endl; return outfile; } template std::ifstream & SpDCCols::get(std::ifstream & infile) { std::cout << "Getting... SpDCCols" << std::endl; IT m, n, nnz; infile >> m >> n >> nnz; SpTuples tuples(nnz, m, n); infile >> tuples; tuples.SortColBased(); SpDCCols object(tuples, false); *this = object; return infile; } template void SpDCCols::PrintInfo(std::ofstream & out) const { out << "m: " << m ; out << ", n: " << n ; out << ", nnz: "<< nnz ; if(splits > 0) { out << ", local splits: " << splits << std::endl; } else { if(dcsc != NULL) { out << ", nzc: "<< dcsc->nzc << std::endl; } else { out <<", nzc: "<< 0 << std::endl; } } } template void SpDCCols::PrintInfo() const { std::cout << "m: " << m ; std::cout << ", n: " << n ; std::cout << ", nnz: "<< nnz ; if(splits > 0) { std::cout << ", local splits: " << splits << std::endl; } else { if(dcsc != NULL) { std::cout << ", nzc: "<< dcsc->nzc << std::endl; } else { std::cout <<", nzc: "<< 0 << std::endl; } if(m < PRINT_LIMIT && n < PRINT_LIMIT) // small enough to print { NT ** A = SpHelper::allocate2D(m,n); for(IT i=0; i< m; ++i) for(IT j=0; jnzc; ++i) { for(IT j = dcsc->cp[i]; jcp[i+1]; ++j) { IT colid = dcsc->jc[i]; IT rowid = dcsc->ir[j]; A[rowid][colid] = dcsc->numx[j]; } } } for(IT i=0; i< m; ++i) { for(IT j=0; j SpDCCols::SpDCCols(IT nRow, IT nCol, Dcsc * mydcsc) :dcsc(mydcsc), m(nRow), n(nCol), splits(0) { if (mydcsc == NULL) nnz = 0; else nnz = mydcsc->nz; } //! Create a logical matrix from (row/column) indices array, used for indexing only template SpDCCols::SpDCCols (IT size, IT nRow, IT nCol, const std::vector & indices, bool isRow) :m(nRow), n(nCol), nnz(size), splits(0) { if(size > 0) dcsc = new Dcsc(size,indices,isRow); else dcsc = NULL; } /****************************************************************************/ /************************* PRIVATE MEMBER FUNCTIONS *************************/ /****************************************************************************/ template inline void SpDCCols::CopyDcsc(Dcsc * source) { // source dcsc will be NULL if number of nonzeros is zero if(source != NULL) dcsc = new Dcsc(*source); else dcsc = NULL; } /** * \return An indexed SpDCCols object without using multiplication * \pre ci is sorted and is not completely empty. * \remarks it is OK for some indices ci[i] to be empty in the indexed SpDCCols matrix * [i.e. in the output, nzc does not need to be equal to n] */ template SpDCCols SpDCCols::ColIndex(const std::vector & ci) const { IT csize = ci.size(); if(nnz == 0) // nothing to index { return SpDCCols(0, m, csize, 0); } else if(ci.empty()) { return SpDCCols(0, m,0, 0); } // First pass for estimation IT estsize = 0; IT estnzc = 0; for(IT i=0, j=0; i< dcsc->nzc && j < csize;) { if((dcsc->jc)[i] < ci[j]) { ++i; } else if ((dcsc->jc)[i] > ci[j]) { ++j; } else { estsize += (dcsc->cp)[i+1] - (dcsc->cp)[i]; ++estnzc; ++i; ++j; } } SpDCCols SubA(estsize, m, csize, estnzc); if(estnzc == 0) { return SubA; // no need to run the second pass } SubA.dcsc->cp[0] = 0; IT cnzc = 0; IT cnz = 0; for(IT i=0, j=0; i < dcsc->nzc && j < csize;) { if((dcsc->jc)[i] < ci[j]) { ++i; } else if ((dcsc->jc)[i] > ci[j]) // an empty column for the output { ++j; } else { IT columncount = (dcsc->cp)[i+1] - (dcsc->cp)[i]; SubA.dcsc->jc[cnzc++] = j; SubA.dcsc->cp[cnzc] = SubA.dcsc->cp[cnzc-1] + columncount; std::copy(dcsc->ir + dcsc->cp[i], dcsc->ir + dcsc->cp[i+1], SubA.dcsc->ir + cnz); std::copy(dcsc->numx + dcsc->cp[i], dcsc->numx + dcsc->cp[i+1], SubA.dcsc->numx + cnz); cnz += columncount; ++i; ++j; } } return SubA; } template template SpDCCols< IT, typename promote_trait::T_promote > SpDCCols::OrdOutProdMult(const SpDCCols & rhs) const { typedef typename promote_trait::T_promote T_promote; if(isZero() || rhs.isZero()) { return SpDCCols< IT, T_promote > (0, m, rhs.n, 0); // return an empty matrix } SpDCCols Btrans = rhs.TransposeConst(); Isect *isect1, *isect2, *itr1, *itr2, *cols, *rows; SpHelper::SpIntersect(*dcsc, *(Btrans.dcsc), cols, rows, isect1, isect2, itr1, itr2); IT kisect = static_cast(itr1-isect1); // size of the intersection ((itr1-isect1) == (itr2-isect2)) if(kisect == 0) { DeleteAll(isect1, isect2, cols, rows); return SpDCCols< IT, T_promote > (0, m, rhs.n, 0); } StackEntry< T_promote, std::pair > * multstack; IT cnz = SpHelper::SpCartesian< SR > (*dcsc, *(Btrans.dcsc), kisect, isect1, isect2, multstack); DeleteAll(isect1, isect2, cols, rows); Dcsc * mydcsc = NULL; if(cnz > 0) { mydcsc = new Dcsc< IT,T_promote > (multstack, m, rhs.n, cnz); delete [] multstack; } return SpDCCols< IT,T_promote > (m, rhs.n, mydcsc); } template template SpDCCols< IT, typename promote_trait::T_promote > SpDCCols::OrdColByCol(const SpDCCols & rhs) const { typedef typename promote_trait::T_promote T_promote; if(isZero() || rhs.isZero()) { return SpDCCols (0, m, rhs.n, 0); // return an empty matrix } StackEntry< T_promote, std::pair > * multstack; IT cnz = SpHelper::SpColByCol< SR > (*dcsc, *(rhs.dcsc), n, multstack); Dcsc * mydcsc = NULL; if(cnz > 0) { mydcsc = new Dcsc< IT,T_promote > (multstack, m, rhs.n, cnz); delete [] multstack; } return SpDCCols< IT,T_promote > (m, rhs.n, mydcsc); } } CombBLAS_beta_16_2/include/CombBLAS/hash.hpp000755 000765 000024 00000001336 13271404146 022035 0ustar00aydinbulucstaff000000 000000 #ifndef HASH_H #define HASH_H #include /* Replace with if appropriate */ #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) #define get16bits(d) (*((const uint16_t *) (d))) #endif #if !defined (get16bits) #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\ +(uint32_t)(((const uint8_t *)(d))[0]) ) #endif namespace combblas { uint32_t SuperFastHash (const char * data, int len); //void MurmurHash3_x64_32 ( const void * key, int len, uint32_t seed, void * out ); void MurmurHash3_x64_64 ( const void * key, int len, uint32_t seed, void * out ); } #endif CombBLAS_beta_16_2/include/CombBLAS/._CombBLAS.h000644 000765 000024 00000000515 13271410640 022300 0ustar00aydinbulucstaff000000 000000 Mac OS X  2MATTRMäiäcom.apple.lastuseddate#PSôY7com.apple.metadata:kMDLabel_6jssn2uemdvwrbg7n2jkvibysugæZÖ×½ò]— %Õ˜`MÇÀÌN4œîb«‘$lÝD“‹@†på;‘‘-G-Ø£ô=èD™ŒÞ=&”WÓ:DR 4W¯P<¢oÛQÕ¸×3}9M*¿ã»ûCombBLAS_beta_16_2/include/CombBLAS/CombBLAS.h000644 000765 000024 00000007327 13271410640 022073 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 04/29/2018 --------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, John Gilbert------------*/ /****************************************************************/ /* Combinatorial BLAS, Copyright (c) 2018, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy) and University of California, Santa Barbara. All rights reserved. If you have questions about your rights to use or distribute this software, please contact Berkeley Lab's Innovation & Partnerships Office at IPO@lbl.gov. NOTICE. This Software was developed under funding from the U.S. Department of Energy and the U.S. Government consequently retains certain rights. As such, the U.S. Government has been granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, distribute copies to the public, prepare derivative works, and perform publicly and display publicly, and to permit other to do so. */ #ifndef COMBBLAS_H #define COMBBLAS_H // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #if defined(COMBBLAS_BOOST) #ifdef CRAYCOMP #include #endif #include #include #include #define joker boost // namespace #elif defined(COMBBLAS_TR1) #include #include #include #include #define joker std::tr1 #elif defined(_MSC_VER) && (_MSC_VER < 1600) #include #include #include #include #define joker std::tr1 #else // C++11 #include #include #include #include #define joker std #endif // for VC2008 // Just in case the -fopenmp didn't define _OPENMP by itself #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #endif #ifdef _OPENMP #include #endif //#ifdef _MSC_VER //#pragma warning( disable : 4244 ) // conversion from 'int64_t' to 'double', possible loss of data //#endif extern int cblas_splits; extern double cblas_alltoalltime; extern double cblas_allgathertime; extern double cblas_localspmvtime; extern double cblas_mergeconttime; extern double cblas_transvectime; extern double mcl_Abcasttime; extern double mcl_Bbcasttime; extern double mcl_localspgemmtime; extern double mcl_multiwaymergetime; extern double mcl_kselecttime; extern double mcl_prunecolumntime; // An adapter function that allows using extended-callback EWiseApply with plain-old binary functions that don't want the extra parameters. template class EWiseExtToPlainAdapter { public: BINOP plain_binary_op; EWiseExtToPlainAdapter(BINOP op): plain_binary_op(op) {} RETT operator()(const NU1& a, const NU2& b, bool aIsNull, bool bIsNull) { return plain_binary_op(a, b); } }; #include "SpDefs.h" #include "BitMap.h" #include "SpTuples.h" #include "SpDCCols.h" #include "SpCCols.h" #include "SpParMat.h" #include "FullyDistVec.h" #include "FullyDistSpVec.h" #include "VecIterator.h" #include "PreAllocatedSPA.h" #include "ParFriends.h" #include "BFSFriends.h" #include "DistEdgeList.h" #include "Semirings.h" #include "Operations.h" #include "MPIOp.h" #include "MPIType.h" #endif CombBLAS_beta_16_2/include/CombBLAS/dcsc.cpp000644 000765 000024 00000101733 13271404146 022020 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 05/15/2016 --------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2016, The Regents of the University of California 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. */ #include "dcsc.h" #include #include #include #include "Friends.h" #include "SpHelper.h" namespace combblas { template Dcsc::Dcsc ():cp(NULL), jc(NULL), ir(NULL), numx(NULL),nz(0), nzc(0), memowned(true){} template Dcsc::Dcsc (IT nnz, IT nzcol): nz(nnz),nzc(nzcol),memowned(true) { assert (nz != 0); assert (nzc != 0); cp = new IT[nzc+1]; jc = new IT[nzc]; ir = new IT[nz]; numx = new NT[nz]; } //! GetIndices helper function for StackEntry arrays template inline void Dcsc::getindices (StackEntry > * multstack, IT & rindex, IT & cindex, IT & j, IT nnz) { if(j::max(); cindex = std::numeric_limits::max(); } ++j; } template Dcsc & Dcsc::AddAndAssign (StackEntry > * multstack, IT mdim, IT ndim, IT nnz) { if(nnz == 0) return *this; IT estnzc = nzc + nnz; IT estnz = nz + nnz; Dcsc temp(estnz, estnzc); IT curnzc = 0; // number of nonzero columns constructed so far IT curnz = 0; IT i = 0; IT j = 0; IT rindex, cindex; getindices(multstack, rindex, cindex,j,nnz); temp.cp[0] = 0; while(i< nzc && cindex < std::numeric_limits::max()) // i runs over columns of "this", j runs over all the nonzeros of "multstack" { if(jc[i] > cindex) { IT columncount = 0; temp.jc[curnzc++] = cindex; do { temp.ir[curnz] = rindex; temp.numx[curnz++] = multstack[j-1].value; getindices(multstack, rindex, cindex,j,nnz); ++columncount; } while(temp.jc[curnzc-1] == cindex); // loop until cindex changes temp.cp[curnzc] = temp.cp[curnzc-1] + columncount; } else if(jc[i] < cindex) { temp.jc[curnzc++] = jc[i++]; for(IT k = cp[i-1]; k< cp[i]; ++k) { temp.ir[curnz] = ir[k]; temp.numx[curnz++] = numx[k]; } temp.cp[curnzc] = temp.cp[curnzc-1] + (cp[i] - cp[i-1]); } else // they are equal, merge the column { temp.jc[curnzc++] = jc[i]; IT ii = cp[i]; IT prevnz = curnz; while (ii < cp[i+1] && cindex == jc[i]) // cindex would be MAX if multstack is deplated { if (ir[ii] < rindex) { temp.ir[curnz] = ir[ii]; temp.numx[curnz++] = numx[ii++]; } else if (ir[ii] > rindex) { temp.ir[curnz] = rindex; temp.numx[curnz++] = multstack[j-1].value; getindices(multstack, rindex, cindex,j,nnz); } else { temp.ir[curnz] = ir[ii]; temp.numx[curnz++] = numx[ii++] + multstack[j-1].value; getindices(multstack, rindex, cindex,j,nnz); } } while (ii < cp[i+1]) { temp.ir[curnz] = ir[ii]; temp.numx[curnz++] = numx[ii++]; } while (cindex == jc[i]) { temp.ir[curnz] = rindex; temp.numx[curnz++] = multstack[j-1].value; getindices(multstack, rindex, cindex,j,nnz); } temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; ++i; } } while(i< nzc) { temp.jc[curnzc++] = jc[i++]; for(IT k = cp[i-1]; k< cp[i]; ++k) { temp.ir[curnz] = ir[k]; temp.numx[curnz++] = numx[k]; } temp.cp[curnzc] = temp.cp[curnzc-1] + (cp[i] - cp[i-1]); } while(cindex < std::numeric_limits::max()) { IT columncount = 0; temp.jc[curnzc++] = cindex; do { temp.ir[curnz] = rindex; temp.numx[curnz++] = multstack[j-1].value; getindices(multstack, rindex, cindex,j,nnz); ++columncount; } while(temp.jc[curnzc-1] == cindex); // loop until cindex changes temp.cp[curnzc] = temp.cp[curnzc-1] + columncount; } temp.Resize(curnzc, curnz); *this = temp; return *this; } /** * Creates DCSC structure from an array of StackEntry's * \remark Complexity: O(nnz) */ template Dcsc::Dcsc (StackEntry > * multstack, IT mdim, IT ndim, IT nnz): nz(nnz),memowned(true) { nzc = std::min(ndim, nnz); // nzc can't exceed any of those assert(nz != 0 ); cp = new IT[nzc+1]; // to be shrinked jc = new IT[nzc]; // to be shrinked ir = new IT[nz]; numx = new NT[nz]; IT curnzc = 0; // number of nonzero columns constructed so far IT cindex = multstack[0].key.first; IT rindex = multstack[0].key.second; ir[0] = rindex; numx[0] = multstack[0].value; jc[curnzc] = cindex; cp[curnzc] = 0; ++curnzc; for(IT i=1; i Dcsc::Dcsc (IT nnz, const std::vector & indices, bool isRow): nz(nnz),nzc(nnz),memowned(true) { assert((nnz != 0) && (indices.size() == nnz)); cp = new IT[nnz+1]; jc = new IT[nnz]; ir = new IT[nnz]; numx = new NT[nnz]; SpHelper::iota(cp, cp+nnz+1, 0); // insert sequential values {0,1,2,..} std::fill_n(numx, nnz, static_cast(1)); if(isRow) { SpHelper::iota(ir, ir+nnz, 0); std::copy (indices.begin(), indices.end(), jc); } else { SpHelper::iota(jc, jc+nnz, 0); std::copy (indices.begin(), indices.end(), ir); } } template template Dcsc::operator Dcsc() const { Dcsc convert(nz, nzc); for(IT i=0; i< nz; ++i) { convert.numx[i] = static_cast(numx[i]); } std::copy(ir, ir+nz, convert.ir); // copy(first, last, result) std::copy(jc, jc+nzc, convert.jc); std::copy(cp, cp+nzc+1, convert.cp); return convert; } template template Dcsc::operator Dcsc() const { Dcsc convert(nz, nzc); for(IT i=0; i< nz; ++i) convert.numx[i] = static_cast(numx[i]); for(IT i=0; i< nz; ++i) convert.ir[i] = static_cast(ir[i]); for(IT i=0; i< nzc; ++i) convert.jc[i] = static_cast(jc[i]); for(IT i=0; i<= nzc; ++i) convert.cp[i] = static_cast(cp[i]); return convert; } template Dcsc::Dcsc (const Dcsc & rhs): nz(rhs.nz), nzc(rhs.nzc),memowned(true) { if(nz > 0) { numx = new NT[nz]; ir = new IT[nz]; std::copy(rhs.numx, rhs.numx + nz, numx); // numx can be a non-POD type std::copy(rhs.ir, rhs.ir + nz, ir); } else { numx = NULL; ir = NULL; } if(nzc > 0) { jc = new IT[nzc]; cp = new IT[nzc+1]; std::copy(rhs.jc, rhs.jc + nzc, jc); std::copy(rhs.cp, rhs.cp + nzc + 1, cp); } else { jc = NULL; cp = NULL; } } /** * Assignment operator (called on an existing object) */ template Dcsc & Dcsc::operator=(const Dcsc & rhs) { if(this != &rhs) { // make empty first ! if(nz > 0) { delete[] numx; delete[] ir; } if(nzc > 0) { delete[] jc; delete[] cp; } nz = rhs.nz; nzc = rhs.nzc; if(nz > 0) { numx = new NT[nz]; ir = new IT[nz]; std::copy(rhs.numx, rhs.numx + nz, numx); // numx can be a non-POD type std::copy(rhs.ir, rhs.ir + nz, ir); } else { numx = NULL; ir = NULL; } if(nzc > 0) { jc = new IT[nzc]; cp = new IT[nzc+1]; std::copy(rhs.jc, rhs.jc + nzc, jc); std::copy(rhs.cp, rhs.cp + nzc + 1, cp); } else { jc = NULL; cp = NULL; } } return *this; } template Dcsc & Dcsc::operator+=(const Dcsc & rhs) // add and assign operator { IT estnzc = nzc + rhs.nzc; IT estnz = nz + rhs.nz; Dcsc temp(estnz, estnzc); IT curnzc = 0; IT curnz = 0; IT i = 0; IT j = 0; temp.cp[0] = 0; while(i< nzc && j rhs.jc[j]) { temp.jc[curnzc++] = rhs.jc[j++]; for(IT k = rhs.cp[j-1]; k< rhs.cp[j]; ++k) { temp.ir[curnz] = rhs.ir[k]; temp.numx[curnz++] = rhs.numx[k]; } temp.cp[curnzc] = temp.cp[curnzc-1] + (rhs.cp[j] - rhs.cp[j-1]); } else if(jc[i] < rhs.jc[j]) { temp.jc[curnzc++] = jc[i++]; for(IT k = cp[i-1]; k< cp[i]; k++) { temp.ir[curnz] = ir[k]; temp.numx[curnz++] = numx[k]; } temp.cp[curnzc] = temp.cp[curnzc-1] + (cp[i] - cp[i-1]); } else { temp.jc[curnzc++] = jc[i]; IT ii = cp[i]; IT jj = rhs.cp[j]; IT prevnz = curnz; while (ii < cp[i+1] && jj < rhs.cp[j+1]) { if (ir[ii] < rhs.ir[jj]) { temp.ir[curnz] = ir[ii]; temp.numx[curnz++] = numx[ii++]; } else if (ir[ii] > rhs.ir[jj]) { temp.ir[curnz] = rhs.ir[jj]; temp.numx[curnz++] = rhs.numx[jj++]; } else { temp.ir[curnz] = ir[ii]; temp.numx[curnz++] = numx[ii++] + rhs.numx[jj++]; // might include zeros } } while (ii < cp[i+1]) { temp.ir[curnz] = ir[ii]; temp.numx[curnz++] = numx[ii++]; } while (jj < rhs.cp[j+1]) { temp.ir[curnz] = rhs.ir[jj]; temp.numx[curnz++] = rhs.numx[jj++]; } temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; ++i; ++j; } } while(i< nzc) { temp.jc[curnzc++] = jc[i++]; for(IT k = cp[i-1]; k< cp[i]; ++k) { temp.ir[curnz] = ir[k]; temp.numx[curnz++] = numx[k]; } temp.cp[curnzc] = temp.cp[curnzc-1] + (cp[i] - cp[i-1]); } while(j < rhs.nzc) { temp.jc[curnzc++] = rhs.jc[j++]; for(IT k = rhs.cp[j-1]; k< rhs.cp[j]; ++k) { temp.ir[curnz] = rhs.ir[k]; temp.numx[curnz++] = rhs.numx[k]; } temp.cp[curnzc] = temp.cp[curnzc-1] + (rhs.cp[j] - rhs.cp[j-1]); } temp.Resize(curnzc, curnz); *this = temp; return *this; } template bool Dcsc::operator==(const Dcsc & rhs) { if(nzc != rhs.nzc) return false; bool same = std::equal(cp, cp+nzc+1, rhs.cp); same = same && std::equal(jc, jc+nzc, rhs.jc); same = same && std::equal(ir, ir+nz, rhs.ir); #ifdef DEBUG std::vector error(nz); std::transform(numx, numx+nz, rhs.numx, error.begin(), absdiff()); std::vector< std::pair > error_original_pair(nz); for(IT i=0; i < nz; ++i) error_original_pair[i] = std::make_pair(error[i], numx[i]); if(error_original_pair.size() > 10) // otherwise would crush for small data { partial_sort(error_original_pair.begin(), error_original_pair.begin()+10, error_original_pair.end(), std::greater< std::pair >()); std::cout << "Highest 10 different entries are: " << std::endl; for(IT i=0; i < 10; ++i) std::cout << "Diff: " << error_original_pair[i].first << " on " << error_original_pair[i].second << std::endl; } else { sort(error_original_pair.begin(), error_original_pair.end(), std::greater< std::pair >()); std::cout << "Highest different entries are: " << std::endl; for(typename std::vector< std::pair >::iterator it=error_original_pair.begin(); it != error_original_pair.end(); ++it) std::cout << "Diff: " << it->first << " on " << it->second << std::endl; } std::cout << "Same before num: " << same << std::endl; #endif ErrorTolerantEqual epsilonequal; same = same && std::equal(numx, numx+nz, rhs.numx, epsilonequal ); return same; } /** * @param[in] exclude if false, * \n then operation is A = A .* B * \n else operation is A = A .* not(B) **/ template void Dcsc::EWiseMult(const Dcsc & rhs, bool exclude) { // We have a class with a friend function and a member function with the same name. Calling the friend function from the member function // might (if the signature is the same) give compilation errors if not preceded by :: that denotes the global scope. *this = combblas::EWiseMult((*this), &rhs, exclude); // call the binary version } template template Dcsc* Dcsc::PruneI(_UnaryOperation __unary_op, bool inPlace, GlobalIT rowOffset, GlobalIT colOffset) { // Two-pass algorithm IT prunednnz = 0; IT prunednzc = 0; for(IT i=0; i cp[cnzc]) { jc[cnzc] = oldjc[i]; cp[cnzc+1] = cnnz; ++cnzc; } } assert(cnzc == prunednzc); assert(cnnz == prunednnz); if (inPlace) { // delete the memory pointed by previous pointers DeleteAll(oldnumx, oldir, oldjc, oldcp); nz = cnnz; nzc = cnzc; return NULL; } else { // create a new object to store the data Dcsc* ret = new Dcsc(); ret->cp = cp; ret->jc = jc; ret->ir = ir; ret->numx = numx; ret->nz = cnnz; ret->nzc = cnzc; // put the previous pointers back cp = oldcp; jc = oldjc; ir = oldir; numx = oldnumx; return ret; } } template template Dcsc* Dcsc::Prune(_UnaryOperation __unary_op, bool inPlace) { // Two-pass algorithm IT prunednnz = 0; IT prunednzc = 0; for(IT i=0; i cp[cnzc]) { jc[cnzc] = oldjc[i]; cp[cnzc+1] = cnnz; ++cnzc; } } assert(cnzc == prunednzc); assert(cnnz == prunednnz); if (inPlace) { // delete the memory pointed by previous pointers DeleteAll(oldnumx, oldir, oldjc, oldcp); nz = cnnz; nzc = cnzc; return NULL; } else { // create a new object to store the data Dcsc* ret = new Dcsc(); ret->cp = cp; ret->jc = jc; ret->ir = ir; ret->numx = numx; ret->nz = cnnz; ret->nzc = cnzc; // put the previous pointers back cp = oldcp; jc = oldjc; ir = oldir; numx = oldnumx; return ret; } } template template Dcsc* Dcsc::PruneColumn(NT* pvals, _BinaryOperation __binary_op, bool inPlace) { // Two-pass algorithm IT prunednnz = 0; IT prunednzc = 0; for(IT i=0; i cp[cnzc]) { jc[cnzc] = oldjc[i]; cp[cnzc+1] = cnnz; ++cnzc; } } assert(cnzc == prunednzc); if (inPlace) { // delete the memory pointed by previous pointers DeleteAll(oldnumx, oldir, oldjc, oldcp); nz = cnnz; nzc = cnzc; return NULL; } else { // create a new object to store the data Dcsc* ret = new Dcsc(); ret->cp = cp; ret->jc = jc; ret->ir = ir; ret->numx = numx; ret->nz = cnnz; ret->nzc = cnzc; // put the previous pointers back cp = oldcp; jc = oldjc; ir = oldir; numx = oldnumx; return ret; } } // prune selected columns indexed by pinds template template Dcsc* Dcsc::PruneColumn(IT* pinds, NT* pvals, _BinaryOperation __binary_op, bool inPlace) { // Two-pass algorithm IT prunednnz = 0; IT prunednzc = 0; IT k = 0; for(IT i=0; i cp[cnzc]) { jc[cnzc] = oldjc[i]; cp[cnzc+1] = cnnz; ++cnzc; } } assert(cnzc == prunednzc); if (inPlace) { // delete the memory pointed by previous pointers DeleteAll(oldnumx, oldir, oldjc, oldcp); nz = cnnz; nzc = cnzc; return NULL; } else { // create a new object to store the data Dcsc* ret = new Dcsc(); ret->cp = cp; ret->jc = jc; ret->ir = ir; ret->numx = numx; ret->nz = cnnz; ret->nzc = cnzc; // put the previous pointers back cp = oldcp; jc = oldjc; ir = oldir; numx = oldnumx; return ret; } } template void Dcsc::EWiseScale(NT ** scaler) { for(IT i=0; i template void Dcsc::UpdateDense(NT ** array, _BinaryOperation __binary_op) const { for(IT i=0; i IT Dcsc::ConstructAux(IT ndim, IT * & aux) const { float cf = static_cast(ndim+1) / static_cast(nzc); IT colchunks = static_cast ( ceil( static_cast(ndim+1) / ceil(cf)) ); aux = new IT[colchunks+1]; IT chunksize = static_cast(ceil(cf)); IT reg = 0; IT curchunk = 0; aux[curchunk++] = 0; for(IT i = 0; i< nzc; ++i) { if(jc[i] >= curchunk * chunksize) // beginning of the next chunk { while(jc[i] >= curchunk * chunksize) // consider any empty chunks { aux[curchunk++] = reg; } } reg = i+1; } while(curchunk <= colchunks) { aux[curchunk++] = reg; } return colchunks; } /** * Resizes cp & jc arrays to nzcnew, ir & numx arrays to nznew * Zero overhead in case sizes stay the same **/ template void Dcsc::Resize(IT nzcnew, IT nznew) { if(nzcnew == 0) { delete[] jc; delete[] cp; nzc = 0; } if(nznew == 0) { delete[] ir; delete[] numx; nz = 0; } if ( nzcnew == 0 && nznew == 0) { return; } if (nzcnew != nzc) { IT * tmpcp = cp; IT * tmpjc = jc; cp = new IT[nzcnew+1]; jc = new IT[nzcnew]; if(nzcnew > nzc) // Grow it (copy all of the old elements) { std::copy(tmpcp, tmpcp+nzc+1, cp); // copy(first, end, result) std::copy(tmpjc, tmpjc+nzc, jc); } else // Shrink it (copy only a portion of the old elements) { std::copy(tmpcp, tmpcp+nzcnew+1, cp); std::copy(tmpjc, tmpjc+nzcnew, jc); } delete[] tmpcp; // delete the memory pointed by previous pointers delete[] tmpjc; nzc = nzcnew; } if (nznew != nz) { NT * tmpnumx = numx; IT * tmpir = ir; numx = new NT[nznew]; ir = new IT[nznew]; if(nznew > nz) // Grow it (copy all of the old elements) { std::copy(tmpnumx, tmpnumx+nz, numx); // numx can be non-POD std::copy(tmpir, tmpir+nz, ir); } else // Shrink it (copy only a portion of the old elements) { std::copy(tmpnumx, tmpnumx+nznew, numx); std::copy(tmpir, tmpir+nznew, ir); } delete[] tmpnumx; // delete the memory pointed by previous pointers delete[] tmpir; nz = nznew; } } /** * The first part of the indexing algorithm described in the IPDPS'08 paper * @param[IT] colind {Column index to search} * Find the column with colind. If it exists, return the position of it. * It it doesn't exist, return value is undefined (implementation specific). **/ template IT Dcsc::AuxIndex(const IT colind, bool & found, IT * aux, IT csize) const { IT base = static_cast(floor((float) (colind/csize))); IT start = aux[base]; IT end = aux[base+1]; IT * itr = std::find(jc + start, jc + end, colind); found = (itr != jc + end); return (itr-jc); } /** ** Split along the cut (a column index) ** Should work even when one of the splits have no nonzeros at all **/ template void Dcsc::Split(Dcsc * & A, Dcsc * & B, IT cut) { IT * itr = std::lower_bound(jc, jc+nzc, cut); IT pos = itr - jc; if(cp[pos] == 0) { A = NULL; } else { A = new Dcsc(cp[pos], pos); std::copy(jc, jc+pos, A->jc); std::copy(cp, cp+pos+1, A->cp); std::copy(ir, ir+cp[pos], A->ir); std::copy(numx, numx + cp[pos], A->numx); // copy(first, last, result) } if(nz-cp[pos] == 0) { B = NULL; } else { B = new Dcsc(nz-cp[pos], nzc-pos); std::copy(jc+pos, jc+ nzc, B->jc); transform(B->jc, B->jc + (nzc-pos), B->jc, bind2nd(std::minus(), cut)); std::copy(cp+pos, cp+nzc+1, B->cp); transform(B->cp, B->cp + (nzc-pos+1), B->cp, bind2nd(std::minus(), cp[pos])); std::copy(ir+cp[pos], ir+nz, B->ir); std::copy(numx+cp[pos], numx+nz, B->numx); // copy(first, last, result) } } /** ** Split along the cut(s) in terms of column indices ** Should work even when one of the splits have no nonzeros at all ** vector cuts is of length "size(parts)-1" ** \pre{ size(parts) >= 2} **/ template void Dcsc::ColSplit(std::vector< Dcsc* > & parts, std::vector & cuts) { IT * jcbegin = jc; std::vector pos; // pos has "parts-1" entries for(auto cutpoint = cuts.begin(); cutpoint != cuts.end(); ++cutpoint) { IT * itr = std::lower_bound(jcbegin, jc+nzc, *cutpoint); pos.push_back(itr - jc); jcbegin = itr; // so that lower_bound searches a smaller vector } if(cp[pos[0]] == 0) // first piece { parts[0] = NULL; } else { parts[0] = new Dcsc(cp[pos[0]], pos[0]); // Dcsc(nnz, nzc) std::copy(jc, jc+pos[0], parts[0]->jc); // std::copy std::copy(cp, cp+pos[0]+1, parts[0]->cp); std::copy(ir, ir+cp[pos[0]], parts[0]->ir); std::copy(numx, numx + cp[pos[0]], parts[0]->numx); // copy(first, last, result) } int ncuts = cuts.size(); // all except last piece for(int i=1; i< ncuts; ++i) // treat the first piece differently { if(cp[pos[i]] - cp[pos[i-1]] == 0) { parts[i] = NULL; } else { parts[i] = new Dcsc(cp[pos[i]] - cp[pos[i-1]], pos[i] - pos[i-1]); // Dcsc(nnz, nzc) std::copy(jc+pos[i-1], jc+pos[i], parts[i]->jc); // std::copy transform(parts[i]->jc, parts[i]->jc + (pos[i]-pos[i-1]), parts[i]->jc, bind2nd(std::minus(), cuts[i-1])); // cuts[i-1] is well defined as i>=1 std::copy(cp+pos[i-1], cp+pos[i]+1, parts[i]->cp); transform(parts[i]->cp, parts[i]->cp + (pos[i]-pos[i-1]+1), parts[i]->cp, bind2nd(std::minus(), cp[pos[i-1]])); std::copy(ir+cp[pos[i-1]], ir+cp[pos[i]], parts[i]->ir); std::copy(numx+cp[pos[i-1]], numx + cp[pos[i]], parts[i]->numx); // copy(first, last, result) } } if(nz - cp[pos[ncuts-1]] == 0) { parts[ncuts] = NULL; } else { parts[ncuts] = new Dcsc(nz-cp[pos[ncuts-1]], nzc-pos[ncuts-1]); // ncuts = npieces -1 std::copy(jc+pos[ncuts-1], jc+ nzc, parts[ncuts]->jc); transform(parts[ncuts]->jc, parts[ncuts]->jc + (nzc-pos[ncuts-1]), parts[ncuts]->jc, bind2nd(std::minus(), cuts[ncuts-1])); std::copy(cp+pos[ncuts-1], cp+nzc+1, parts[ncuts]->cp); transform(parts[ncuts]->cp, parts[ncuts]->cp + (nzc-pos[ncuts-1]+1), parts[ncuts]->cp, bind2nd(std::minus(), cp[pos[ncuts-1]])); std::copy(ir+cp[pos[ncuts-1]], ir+nz, parts[ncuts]->ir); std::copy(numx+cp[pos[ncuts-1]], numx+nz, parts[ncuts]->numx); } } // Assumes A and B are not NULL // When any is NULL, this function is not called anyway template void Dcsc::Merge(const Dcsc * A, const Dcsc * B, IT cut) { assert((A != NULL) && (B != NULL)); // handled at higher level IT cnz = A->nz + B->nz; IT cnzc = A->nzc + B->nzc; if(cnz > 0) { *this = Dcsc(cnz, cnzc); // safe, because "this" can not be NULL inside a member function std::copy(A->jc, A->jc + A->nzc, jc); // copy(first, last, result) std::copy(B->jc, B->jc + B->nzc, jc + A->nzc); transform(jc + A->nzc, jc + cnzc, jc + A->nzc, bind2nd(std::plus(), cut)); std::copy(A->cp, A->cp + A->nzc, cp); std::copy(B->cp, B->cp + B->nzc +1, cp + A->nzc); transform(cp + A->nzc, cp+cnzc+1, cp + A->nzc, bind2nd(std::plus(), A->cp[A->nzc])); std::copy(A->ir, A->ir + A->nz, ir); std::copy(B->ir, B->ir + B->nz, ir + A->nz); // since numx is potentially non-POD, we use std::copy std::copy(A->numx, A->numx + A->nz, numx); std::copy(B->numx, B->numx + B->nz, numx + A->nz); } } /** * @pre {no member of "parts" is empty} * @pre {there are at least 2 members} * offsets arrays is "parallel to" parts array * it shows the starts of column numbers **/ template void Dcsc::ColConcatenate(std::vector< Dcsc* > & parts, std::vector & offsets) { IT cnz = 0; IT cnzc = 0; size_t nmembers = parts.size(); for(size_t i=0; i< nmembers; ++i) { cnz += parts[i]->nz; cnzc += parts[i]->nzc; } if(cnz > 0) { *this = Dcsc(cnz, cnzc); // safe, because "this" can not be NULL inside a member function IT run_nz = 0; IT run_nzc = 0; for(size_t i=0; i< nmembers; ++i) { std::copy(parts[i]->jc, parts[i]->jc + parts[i]->nzc, jc + run_nzc); transform(jc + run_nzc, jc + run_nzc + parts[i]->nzc, jc + run_nzc, bind2nd(std::plus(), offsets[i])); // remember: cp[nzc] = nnz std::copy(parts[i]->cp, parts[i]->cp + parts[i]->nzc, cp + run_nzc); transform(cp + run_nzc, cp + run_nzc + parts[i]->nzc, cp + run_nzc, bind2nd(std::plus(),run_nz)); std::copy(parts[i]->ir, parts[i]->ir + parts[i]->nz, ir + run_nz); std::copy(parts[i]->numx, parts[i]->numx + parts[i]->nz, numx + run_nz); run_nzc += parts[i]->nzc; run_nz += parts[i]->nz; } // adjust the last pointer cp[run_nzc] = run_nz; } } /** * param[in] nind { length(colsums), gives number of columns of A that contributes to C(:,i) } * Vector type VT is allowed to be different than matrix type (IT) * However, VT should be up-castable to IT (example: VT=int32_t, IT=int64_t) **/ template template void Dcsc::FillColInds(const VT * colnums, IT nind, std::vector< std::pair > & colinds, IT * aux, IT csize) const { if ( aux == NULL || (nzc / nind) < THRESHOLD) // use scanning indexing { IT mink = std::min(nzc, nind); std::pair * isect = new std::pair[mink]; std::pair * range1 = new std::pair[nzc]; std::pair * range2 = new std::pair[nind]; for(IT i=0; i < nzc; ++i) { range1[i] = std::make_pair(jc[i], i); // get the actual nonzero value and the index to the ith nonzero } for(IT i=0; i < nind; ++i) { range2[i] = std::make_pair(static_cast(colnums[i]), 0); // second is dummy as all the intersecting elements are copied from the first range } std::pair * itr = set_intersection(range1, range1 + nzc, range2, range2+nind, isect, SpHelper::first_compare ); // isect now can iterate on a subset of the elements of range1 // meaning that the intersection can be accessed directly by isect[i] instead of range1[isect[i]] // this is because the intersecting elements are COPIED to the output range "isect" IT kisect = static_cast(itr-isect); // size of the intersection for(IT j=0, i =0; j< nind; ++j) { // the elements represented by jc[isect[i]] are a subset of the elements represented by colnums[j] if( i == kisect || isect[i].first != static_cast(colnums[j])) { // not found, signal by setting first = second colinds[j].first = 0; colinds[j].second = 0; } else // i < kisect && dcsc->jc[isect[i]] == colnums[j] { IT p = isect[i++].second; colinds[j].first = cp[p]; colinds[j].second = cp[p+1]; } } DeleteAll(isect, range1, range2); } else // use aux based indexing { bool found; for(IT j =0; j< nind; ++j) { IT pos = AuxIndex(static_cast(colnums[j]), found, aux, csize); if(found) { colinds[j].first = cp[pos]; colinds[j].second = cp[pos+1]; } else // not found, signal by setting first = second { colinds[j].first = 0; colinds[j].second = 0; } } } } template Dcsc::~Dcsc() { if(nz > 0) // dcsc may be empty { delete[] numx; delete[] ir; } if(nzc > 0) { delete[] jc; delete[] cp; } } } CombBLAS_beta_16_2/include/CombBLAS/DenseParMat.cpp000644 000765 000024 00000014412 13271404146 023244 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include #include "DenseParMat.h" #include "MPIType.h" #include "Operations.h" namespace combblas { template template FullyDistVec< IT,NT > DenseParMat::Reduce(Dim dim, _BinaryOperation __binary_op, NT identity) const { switch(dim) { case Column: // pack along the columns, result is a vector of size (global) n { // we can use parvec's grid as long as the process grid is square (currently a CombBLAS requirement) int colneighs = commGrid->GetGridRows(); // including oneself int colrank = commGrid->GetRankInProcCol(); IT * loclens = new IT[colneighs]; IT * lensums = new IT[colneighs+1](); // begin/end points of local lengths IT n_perproc = n / colneighs; // length on a typical processor if(colrank == colneighs-1) loclens[colrank] = n - (n_perproc*colrank); else loclens[colrank] = n_perproc; MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), loclens, 1, MPIType(), commGrid->GetColWorld()); std::partial_sum(loclens, loclens+colneighs, lensums+1); // loclens and lensums are different, but both would fit in 32-bits std::vector trarr(loclens[colrank]); NT * sendbuf = new NT[n]; for(int j=0; j < n; ++j) { sendbuf[j] = identity; for(int i=0; i < m; ++i) { sendbuf[j] = __binary_op(array[i][j], sendbuf[j]); } } // The MPI_REDUCE_SCATTER routine is functionally equivalent to: // an MPI_REDUCE collective operation with count equal to the sum of loclens[i] // followed by MPI_SCATTERV with sendcounts equal to loclens as well MPI_Reduce_scatter(sendbuf, trarr.data(), loclens, MPIType(), MPIOp<_BinaryOperation, NT>::op(), commGrid->GetColWorld()); DeleteAll(sendbuf, loclens, lensums); IT reallen; // Now we have to transpose the vector IT trlen = trarr.size(); int diagneigh = commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&trlen, 1, MPIType(), diagneigh, TRNNZ, &reallen, 1, MPIType(), diagneigh, TRNNZ, commGrid->GetWorld(), &status); IT glncols = gcols(); FullyDistVec parvec(commGrid, glncols, identity); assert((parvec.arr.size() == reallen)); MPI_Sendrecv(trarr.data(), trlen, MPIType(), diagneigh, TRX, parvec.arr.data(), reallen, MPIType(), diagneigh, TRX, commGrid->GetWorld(), &status); return parvec; break; } case Row: // pack along the rows, result is a vector of size m { IT glnrows = grows(); FullyDistVec parvec(commGrid, glnrows, identity); NT * sendbuf = new NT[m]; for(int i=0; i < m; ++i) { sendbuf[i] = std::accumulate( array[i], array[i]+n, identity, __binary_op); } NT * recvbuf = parvec.arr.data(); int rowneighs = commGrid->GetGridCols(); int rowrank = commGrid->GetRankInProcRow(); IT * recvcounts = new IT[rowneighs]; recvcounts[rowrank] = parvec.MyLocLength(); // local vector lengths are the ultimate receive counts MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), recvcounts, 1, MPIType(), commGrid->GetRowWorld()); // The MPI_REDUCE_SCATTER routine is functionally equivalent to: // an MPI_REDUCE collective operation with count equal to the sum of recvcounts[i] // followed by MPI_SCATTERV with sendcounts equal to recvcounts. MPI_Reduce_scatter(sendbuf, recvbuf, recvcounts, MPIType(), MPIOp<_BinaryOperation, NT>::op(), commGrid->GetRowWorld()); delete [] sendbuf; delete [] recvcounts; return parvec; break; } default: { std::cout << "Unknown reduction dimension, returning empty vector" << std::endl; return FullyDistVec(commGrid); break; } } } template template DenseParMat< IT,NT > & DenseParMat::operator+=(const SpParMat< IT,NT,DER > & rhs) // add a sparse matrix { if(*commGrid == *rhs.commGrid) { (rhs.spSeq)->UpdateDense(array, std::plus()); } else { std::cout << "Grids are not comparable elementwise addition" << std::endl; MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } return *this; } template DenseParMat< IT,NT > & DenseParMat::operator=(const DenseParMat< IT,NT > & rhs) // assignment operator { if(this != &rhs) { if(array != NULL) SpHelper::deallocate2D(array, m); m = rhs.m; n = rhs.n; if(rhs.array != NULL) { array = SpHelper::allocate2D(m, n); for(int i=0; i< m; ++i) std::copy(array[i], array[i]+n, rhs.array[i]); } commGrid.reset(new CommGrid(*(rhs.commGrid))); } return *this; } } CombBLAS_beta_16_2/include/CombBLAS/FullyDistVec.cpp000644 000765 000024 00000113153 13271404146 023460 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include "FullyDistVec.h" #include "FullyDistSpVec.h" #include "Operations.h" namespace combblas { template FullyDistVec::FullyDistVec () : FullyDist::value, NT >::type>() { } template FullyDistVec::FullyDistVec (IT globallen, NT initval) :FullyDist::value, NT >::type>(globallen) { arr.resize(MyLocLength(), initval); } template FullyDistVec::FullyDistVec ( std::shared_ptr grid) : FullyDist::value, NT >::type>(grid) { } template FullyDistVec::FullyDistVec ( std::shared_ptr grid, IT globallen, NT initval) : FullyDist::value, NT >::type>(grid,globallen) { arr.resize(MyLocLength(), initval); } template FullyDistVec::FullyDistVec (const FullyDistSpVec & rhs) // Conversion copy-constructor : FullyDist::value, NT >::type>(rhs.commGrid,rhs.glen) { *this = rhs; } template template FullyDistVec::FullyDistVec ( const FullyDistVec& rhs ) : FullyDist::value, NT >::type>(rhs.commGrid, static_cast(rhs.glen)) { arr.resize(static_cast(rhs.arr.size()), NT()); for(IT i=0; (unsigned)i < arr.size(); ++i) { arr[i] = static_cast(rhs.arr[static_cast(i)]); } } /** * Initialize a FullyDistVec with a separate vector from each processor * Optimizes for the common case where all fillarr's in separate processors are of the same size */ template FullyDistVec::FullyDistVec ( const std::vector & fillarr, std::shared_ptr grid ) : FullyDist::value, NT >::type>(grid) { MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int rank = commGrid->GetRank(); IT * sizes = new IT[nprocs]; IT nsize = fillarr.size(); sizes[rank] = nsize; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), sizes, 1, MPIType(), World); glen = std::accumulate(sizes, sizes+nprocs, static_cast(0)); std::vector uniq_sizes; std::unique_copy(sizes, sizes+nprocs, std::back_inserter(uniq_sizes)); if(uniq_sizes.size() == 1) { arr = fillarr; } else { IT lengthuntil = std::accumulate(sizes, sizes+rank, static_cast(0)); // Although the found vector is not reshuffled yet, its glen and commGrid are set // We can call the Owner/MyLocLength/LengthUntil functions (to infer future distribution) // rebalance/redistribute int * sendcnt = new int[nprocs]; std::fill(sendcnt, sendcnt+nprocs, 0); for(IT i=0; i(0)); std::vector recvbuf(totrecv); // data is already in the right order in found.arr MPI_Alltoallv(&(arr[0]), sendcnt, sdispls, MPIType(), &(recvbuf[0]), recvcnt, rdispls, MPIType(), World); arr.swap(recvbuf); DeleteAll(sendcnt, recvcnt, sdispls, rdispls); } delete [] sizes; } template std::pair FullyDistVec::MinElement() const { auto it = min_element(arr.begin(), arr.end()); NT localMin = *it; NT globalMin; MPI_Allreduce( &localMin, &globalMin, 1, MPIType(), MPI_MIN, commGrid->GetWorld()); IT localMinIdx = TotalLength(); if(globalMin==localMin) { localMinIdx = distance(arr.begin(), it) + LengthUntil(); } IT globalMinIdx; MPI_Allreduce( &localMinIdx, &globalMinIdx, 1, MPIType(), MPI_MIN, commGrid->GetWorld()); // it can be MPI_MAX or anything return std::make_pair(globalMinIdx, globalMin); } template template NT FullyDistVec::Reduce(_BinaryOperation __binary_op, NT identity) const { // std::accumulate returns identity for empty sequences NT localsum = std::accumulate( arr.begin(), arr.end(), identity, __binary_op); NT totalsum = identity; MPI_Allreduce( &localsum, &totalsum, 1, MPIType(), MPIOp<_BinaryOperation, NT>::op(), commGrid->GetWorld()); return totalsum; } template template OUT FullyDistVec::Reduce(_BinaryOperation __binary_op, OUT default_val, _UnaryOperation __unary_op) const { // std::accumulate returns identity for empty sequences OUT localsum = default_val; if (arr.size() > 0) { typename std::vector< NT >::const_iterator iter = arr.begin(); //localsum = __unary_op(*iter); //iter++; while (iter < arr.end()) { localsum = __binary_op(localsum, __unary_op(*iter)); iter++; } } OUT totalsum = default_val; MPI_Allreduce( &localsum, &totalsum, 1, MPIType(), MPIOp<_BinaryOperation, OUT>::op(), commGrid->GetWorld()); return totalsum; } //! ABAB: Put concept check, NT should be integer for this to make sense template void FullyDistVec::SelectCandidates(double nver) { #ifdef DETERMINISTIC MTRand M(1); #else MTRand M; // generate random numbers with Mersenne Twister #endif IT length = TotalLength(); std::vector loccands(length); std::vector loccandints(length); MPI_Comm World = commGrid->GetWorld(); int myrank = commGrid->GetRank(); if(myrank == 0) { for(int i=0; i(), nver )); for(int i=0; i(loccands[i]); } MPI_Bcast(&(loccandints[0]), length, MPIType(),0, World); for(IT i=0; i template FullyDistVec< IT,NT > & FullyDistVec::operator=(const FullyDistVec< ITRHS,NTRHS > & rhs) { if(static_cast(this) != static_cast(&rhs)) { //FullyDist::operator= (rhs); // to update glen and commGrid glen = static_cast(rhs.glen); commGrid = rhs.commGrid; arr.resize(rhs.arr.size(), NT()); for(IT i=0; (unsigned)i < arr.size(); ++i) { arr[i] = static_cast(rhs.arr[static_cast(i)]); } } return *this; } template FullyDistVec< IT,NT > & FullyDistVec::operator=(const FullyDistVec< IT,NT > & rhs) { if(this != &rhs) { FullyDist::value, NT >::type>::operator= (rhs); // to update glen and commGrid arr = rhs.arr; } return *this; } template FullyDistVec< IT,NT > & FullyDistVec::operator=(const FullyDistSpVec< IT,NT > & rhs) // FullyDistSpVec->FullyDistVec conversion operator { FullyDist::value, NT >::type>::operator= (rhs); // to update glen and commGrid arr.resize(rhs.MyLocLength()); std::fill(arr.begin(), arr.end(), NT()); IT spvecsize = rhs.getlocnnz(); for(IT i=0; i< spvecsize; ++i) { //if(rhs.ind[i] > arr.size()) // cout << "rhs.ind[i]: " << rhs.ind[i] << endl; arr[rhs.ind[i]] = rhs.num[i]; } return *this; } /** ** Let the compiler create an assignment operator and call base class' ** assignment operator automatically **/ template FullyDistVec< IT,NT > & FullyDistVec::operator+=(const FullyDistSpVec< IT,NT > & rhs) { IT spvecsize = rhs.getlocnnz(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i< spvecsize; ++i) { if(arr[rhs.ind[i]] == NT()) // not set before arr[rhs.ind[i]] = rhs.num[i]; else arr[rhs.ind[i]] += rhs.num[i]; } return *this; } template FullyDistVec< IT,NT > & FullyDistVec::operator-=(const FullyDistSpVec< IT,NT > & rhs) { IT spvecsize = rhs.getlocnnz(); for(IT i=0; i< spvecsize; ++i) { arr[rhs.ind[i]] -= rhs.num[i]; } return *this; } /** * Perform __binary_op(*this[i], rhs[i]) for every element in rhs, *this, * which are of the same size. and write the result back to *this */ template template void FullyDistVec::EWise(const FullyDistVec & rhs, _BinaryOperation __binary_op) { std::transform ( arr.begin(), arr.end(), rhs.arr.begin(), arr.begin(), __binary_op ); }; /** * Perform __binary_op(*this[i], rhs[i]) for every element in rhs and *this, which are of the same size. * write the result output vector, hence the name EWiseOut */ template template void FullyDistVec::EWiseOut(const FullyDistVec & rhs, _BinaryOperation __binary_op, FullyDistVec & result) { std::transform ( arr.begin(), arr.end(), rhs.arr.begin(), result.arr.begin(), __binary_op ); }; template FullyDistVec & FullyDistVec::operator+=(const FullyDistVec & rhs) { if(this != &rhs) { if(!(*commGrid == *rhs.commGrid)) { std::cout << "Grids are not comparable elementwise addition" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } else { EWise(rhs, std::plus()); } } return *this; }; template FullyDistVec & FullyDistVec::operator-=(const FullyDistVec & rhs) { if(this != &rhs) { if(!(*commGrid == *rhs.commGrid)) { std::cout << "Grids are not comparable elementwise addition" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } else { EWise(rhs, std::minus()); } } return *this; }; template bool FullyDistVec::operator==(const FullyDistVec & rhs) const { ErrorTolerantEqual epsilonequal; int local = 1; local = (int) std::equal(arr.begin(), arr.end(), rhs.arr.begin(), epsilonequal ); int whole = 1; MPI_Allreduce( &local, &whole, 1, MPI_INT, MPI_BAND, commGrid->GetWorld()); return static_cast(whole); } template template IT FullyDistVec::Count(_Predicate pred) const { IT local = count_if( arr.begin(), arr.end(), pred ); IT whole = 0; MPI_Allreduce( &local, &whole, 1, MPIType(), MPI_SUM, commGrid->GetWorld()); return whole; } //! Returns a dense vector of global indices //! for which the predicate is satisfied template template FullyDistVec FullyDistVec::FindInds(_Predicate pred) const { FullyDistVec found(commGrid); MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int rank = commGrid->GetRank(); IT sizelocal = LocArrSize(); IT sizesofar = LengthUntil(); for(IT i=0; i(), dist, 1, MPIType(), World); IT lengthuntil = std::accumulate(dist, dist+rank, static_cast(0)); found.glen = std::accumulate(dist, dist+nprocs, static_cast(0)); // Although the found vector is not reshuffled yet, its glen and commGrid are set // We can call the Owner/MyLocLength/LengthUntil functions (to infer future distribution) // rebalance/redistribute int * sendcnt = new int[nprocs]; std::fill(sendcnt, sendcnt+nprocs, 0); for(IT i=0; i(0)); std::vector recvbuf(totrecv); // data is already in the right order in found.arr MPI_Alltoallv(&(found.arr[0]), sendcnt, sdispls, MPIType(), &(recvbuf[0]), recvcnt, rdispls, MPIType(), World); found.arr.swap(recvbuf); delete [] dist; DeleteAll(sendcnt, recvcnt, sdispls, rdispls); return found; } //! Requires no communication because FullyDistSpVec (the return object) //! is distributed based on length, not nonzero counts template template FullyDistSpVec FullyDistVec::Find(_Predicate pred) const { FullyDistSpVec found(commGrid); size_t size = arr.size(); for(size_t i=0; i FullyDistSpVec FullyDistVec::Find(NT val) const { FullyDistSpVec found(commGrid); size_t size = arr.size(); for(size_t i=0; i template std::ifstream& FullyDistVec::ReadDistribute (std::ifstream& infile, int master, HANDLER handler) { FullyDistSpVec tmpSpVec(commGrid); tmpSpVec.ReadDistribute(infile, master, handler); *this = tmpSpVec; return infile; } template template void FullyDistVec::SaveGathered(std::ofstream& outfile, int master, HANDLER handler, bool printProcSplits) { FullyDistSpVec tmpSpVec = *this; tmpSpVec.SaveGathered(outfile, master, handler, printProcSplits); } template void FullyDistVec::SetElement (IT indx, NT numx) { int rank = commGrid->GetRank(); if (glen == 0) { if(rank == 0) std::cout << "FullyDistVec::SetElement can't be called on an empty vector." << std::endl; return; } IT locind; int owner = Owner(indx, locind); if(commGrid->GetRank() == owner) { if (locind > (LocArrSize() -1)) { std::cout << "FullyDistVec::SetElement cannot expand array" << std::endl; } else if (locind < 0) { std::cout << "FullyDistVec::SetElement local index < 0" << std::endl; } else { arr[locind] = numx; } } } template NT FullyDistVec::GetElement (IT indx) const { NT ret; MPI_Comm World = commGrid->GetWorld(); int rank = commGrid->GetRank(); if (glen == 0) { if(rank == 0) std::cout << "FullyDistVec::GetElement can't be called on an empty vector." << std::endl; return NT(); } IT locind; int owner = Owner(indx, locind); if(commGrid->GetRank() == owner) { if (locind > (LocArrSize() -1)) { std::cout << "FullyDistVec::GetElement local index > size" << std::endl; ret = NT(); } else if (locind < 0) { std::cout << "FullyDistVec::GetElement local index < 0" << std::endl; ret = NT(); } else { ret = arr[locind]; } } MPI_Bcast(&ret, 1, MPIType(), owner, World); return ret; } // Write to file using MPI-2 template void FullyDistVec::DebugPrint() { int nprocs, rank; MPI_Comm World = commGrid->GetWorld(); MPI_Comm_rank(World, &rank); MPI_Comm_size(World, &nprocs); MPI_File thefile; char _fn[] = "temp_fullydistvec"; // AL: this is to avoid the problem that C++ string literals are const char* while C string literals are char*, leading to a const warning (technically error, but compilers are tolerant) MPI_File_open(World, _fn, MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); IT lengthuntil = LengthUntil(); // The disp displacement argument specifies the position // (absolute offset in bytes from the beginning of the file) char native[] = "native"; // AL: this is to avoid the problem that C++ string literals are const char* while C string literals are char*, leading to a const warning (technically error, but compilers are tolerant) MPI_File_set_view(thefile, int64_t(lengthuntil * sizeof(NT)), MPIType(), MPIType(), native, MPI_INFO_NULL); IT count = LocArrSize(); MPI_File_write(thefile, &(arr[0]), count, MPIType(), MPI_STATUS_IGNORE); MPI_File_close(&thefile); // Now let processor-0 read the file and print IT * counts = new IT[nprocs]; MPI_Gather(&count, 1, MPIType(), counts, 1, MPIType(), 0, World); // gather at root=0 if(rank == 0) { FILE * f = fopen("temp_fullydistvec", "r"); if(!f) { std::cerr << "Problem reading binary input file\n"; return; } IT maxd = *std::max_element(counts, counts+nprocs); NT * data = new NT[maxd]; for(int i=0; i template void FullyDistVec::Apply(_UnaryOperation __unary_op, const FullyDistSpVec & mask) { typename std::vector< IT >::const_iterator miter = mask.ind.begin(); while (miter < mask.ind.end()) { IT index = *miter++; arr[index] = __unary_op(arr[index]); } } template template void FullyDistVec::EWiseApply(const FullyDistVec & other, _BinaryOperation __binary_op, _BinaryPredicate _do_op, const bool useExtendedBinOp) { if(*(commGrid) == *(other.commGrid)) { if(glen != other.glen) { std::ostringstream outs; outs << "Vector dimensions don't match (" << glen << " vs " << other.glen << ") for FullyDistVec::EWiseApply\n"; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } else { typename std::vector< NT >::iterator thisIter = arr.begin(); typename std::vector< NT2 >::const_iterator otherIter = other.arr.begin(); while (thisIter < arr.end()) { if (_do_op(*thisIter, *otherIter, false, false)) *thisIter = __binary_op(*thisIter, *otherIter, false, false); thisIter++; otherIter++; } } } else { std::ostringstream outs; outs << "Grids are not comparable for FullyDistVec::EWiseApply" << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } // Note (Ariful): multithreded implemented only when applyNulls=false. // TODO: employ multithreding when applyNulls=true template template void FullyDistVec::EWiseApply(const FullyDistSpVec & other, _BinaryOperation __binary_op, _BinaryPredicate _do_op, bool applyNulls, NT2 nullValue, const bool useExtendedBinOp) { if(*(commGrid) == *(other.commGrid)) { if(glen != other.glen) { std::cerr << "Vector dimensions don't match (" << glen << " vs " << other.glen << ") for FullyDistVec::EWiseApply\n"; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } else { typename std::vector< IT >::const_iterator otherInd = other.ind.begin(); typename std::vector< NT2 >::const_iterator otherNum = other.num.begin(); if (applyNulls) // scan the entire dense vector and apply sparse elements as they appear { for(IT i=0; (unsigned)i < arr.size(); ++i) { if (otherInd == other.ind.end() || i < *otherInd) { if (_do_op(arr[i], nullValue, false, true)) arr[i] = __binary_op(arr[i], nullValue, false, true); } else { if (_do_op(arr[i], *otherNum, false, false)) arr[i] = __binary_op(arr[i], *otherNum, false, false); otherInd++; otherNum++; } } } else // scan the sparse vector only { /* for(otherInd = other.ind.begin(); otherInd < other.ind.end(); otherInd++, otherNum++) { if (_do_op(arr[*otherInd], *otherNum, false, false)) arr[*otherInd] = __binary_op(arr[*otherInd], *otherNum, false, false); }*/ IT spsize = other.ind.size(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i< spsize; i++) { if (_do_op(arr[other.ind[i]], other.num[i], false, false)) arr[other.ind[i]] = __binary_op(arr[other.ind[i]], other.num[i], false, false); } } } } else { std::cout << "Grids are not comparable elementwise apply" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } template FullyDistVec FullyDistVec::sort() { MPI_Comm World = commGrid->GetWorld(); FullyDistVec temp(commGrid); IT nnz = LocArrSize(); std::pair * vecpair = new std::pair[nnz]; int nprocs = commGrid->GetSize(); int rank = commGrid->GetRank(); IT * dist = new IT[nprocs]; dist[rank] = nnz; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), dist, 1, MPIType(), World); IT sizeuntil = LengthUntil(); // size = length, for dense vectors for(IT i=0; i< nnz; ++i) { vecpair[i].first = arr[i]; // we'll sort wrt numerical values vecpair[i].second = i + sizeuntil; } SpParHelper::MemoryEfficientPSort(vecpair, nnz, dist, World); std::vector< IT > narr(nnz); for(IT i=0; i< nnz; ++i) { arr[i] = vecpair[i].first; // sorted range (change the object itself) narr[i] = vecpair[i].second; // inverse permutation stored as numerical values } delete [] vecpair; delete [] dist; temp.glen = glen; temp.arr = narr; return temp; } // Randomly permutes an already existing vector template void FullyDistVec::RandPerm() { #ifdef DETERMINISTIC uint64_t seed = 1383098845; #else uint64_t seed= time(NULL); #endif MTRand M(seed); // generate random numbers with Mersenne Twister MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int rank = commGrid->GetRank(); IT size = LocArrSize(); #ifdef COMBBLAS_LEGACY std::pair * vecpair = new std::pair[size]; IT * dist = new IT[nprocs]; dist[rank] = size; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), dist, 1, MPIType(), World); for(int i=0; i > works correctly (sorts wrt first elements) SpParHelper::MemoryEfficientPSort(vecpair, size, dist, World); std::vector< NT > nnum(size); for(int i=0; i > data_send(nprocs); for(int i=0; i(0)); if(totrecv > std::numeric_limits::max()) { std::cout << "COMBBLAS_WARNING: total data to receive exceeds max int: " << totrecv << std::endl; } std::vector().swap(arr); // make space for temporaries NT * sendbuf = new NT[size]; for(int i=0; i().swap(data_send[i]); // free memory } NT * recvbuf = new NT[totrecv]; MPI_Alltoallv(sendbuf, sendcnt, sdispls, MPIType(), recvbuf, recvcnt, rdispls, MPIType(), World); //std::random_shuffle(recvbuf, recvbuf+ totrecv); std::default_random_engine gen(seed); std::shuffle(recvbuf, recvbuf+ totrecv,gen); // locally shuffle data int64_t * localcounts = new int64_t[nprocs]; localcounts[rank] = totrecv; MPI_Allgather(MPI_IN_PLACE, 1, MPI_LONG_LONG, localcounts, 1, MPI_LONG_LONG, World); int64_t glenuntil = std::accumulate(localcounts, localcounts+rank, static_cast(0)); std::vector< std::vector< IT > > locs_send(nprocs); for(IT i=0; i< totrecv; ++i) // determine new locations w/ prefix sums { IT remotelocind; int owner = Owner(glenuntil+i, remotelocind); locs_send[owner].push_back(remotelocind); data_send[owner].push_back(recvbuf[i]); } for(int i=0; i(0)); if(newsize > std::numeric_limits::max()) { std::cout << "COMBBLAS_WARNING: total data to receive exceeds max int: " << newsize << std::endl; } // re-use the receive buffer as sendbuf of second stage IT totalsend = std::accumulate(sendcnt, sendcnt+nprocs, static_cast(0)); if(totalsend != totrecv || newsize != size) { std::cout << "COMBBLAS_WARNING: sending different sized data than received: " << totalsend << "=" << totrecv << " , " << newsize << "=" << size << std::endl; } for(int i=0; i().swap(data_send[i]); // free memory } // re-use the send buffer as receive buffer of second stage MPI_Alltoallv(recvbuf, sendcnt, sdispls, MPIType(), sendbuf, recvcnt, rdispls, MPIType(), World); delete [] recvbuf; IT * newinds = new IT[totalsend]; for(int i=0; i().swap(locs_send[i]); // free memory } IT * indsbuf = new IT[size]; MPI_Alltoallv(newinds, sendcnt, sdispls, MPIType(), indsbuf, recvcnt, rdispls, MPIType(), World); DeleteAll(newinds, sendcnt, sdispls, rdispls, recvcnt); arr.resize(size); for(IT i=0; i void FullyDistVec::iota(IT globalsize, NT first) { glen = globalsize; IT length = MyLocLength(); // only needs glen to determine length arr.resize(length); SpHelper::iota(arr.begin(), arr.end(), LengthUntil() + first); // global across processors } template FullyDistVec FullyDistVec::operator() (const FullyDistVec & ri) const { if(!(*commGrid == *ri.commGrid)) { std::cout << "Grids are not comparable for dense vector subsref" << std::endl; return FullyDistVec(); } MPI_Comm World = commGrid->GetWorld(); FullyDistVec Indexed(commGrid, ri.glen, NT()); // length(Indexed) = length(ri) int nprocs = commGrid->GetSize(); std::vector< std::vector< IT > > data_req(nprocs); std::vector< std::vector< IT > > revr_map(nprocs); // to put the incoming data to the correct location IT riloclen = ri.LocArrSize(); for(IT i=0; i < riloclen; ++i) { IT locind; int owner = Owner(ri.arr[i], locind); // numerical values in ri are 0-based data_req[owner].push_back(locind); revr_map[owner].push_back(i); } IT * sendbuf = new IT[riloclen]; int * sendcnt = new int[nprocs]; int * sdispls = new int[nprocs]; for(int i=0; i(0)); for(int i=0; i().swap(data_req[i]); } IT * reversemap = new IT[riloclen]; for(int i=0; i().swap(revr_map[i]); } IT * recvbuf = new IT[totrecv]; MPI_Alltoallv(sendbuf, sendcnt, sdispls, MPIType(), recvbuf, recvcnt, rdispls, MPIType(), World); // request data delete [] sendbuf; // We will return the requested data, // our return will be as big as the request // as we are indexing a dense vector, all elements exist // so the displacement boundaries are the same as rdispls NT * databack = new NT[totrecv]; for(int i=0; i(), databuf, sendcnt, sdispls, MPIType(), World); // send data DeleteAll(rdispls, recvcnt, databack); // Now create the output from databuf // Indexed.arr is already allocated in contructor for(int i=0; i void FullyDistVec::PrintInfo(std::string vectorname) const { IT totl = TotalLength(); if (commGrid->GetRank() == 0) std::cout << "As a whole, " << vectorname << " has length " << totl << std::endl; } template void FullyDistVec::Set(const FullyDistSpVec< IT,NT > & other) { if(*(commGrid) == *(other.commGrid)) { if(glen != other.glen) { std::cerr << "Vector dimensions don't match (" << glen << " vs " << other.glen << ") for FullyDistVec::Set\n"; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } else { IT spvecsize = other.getlocnnz(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i< spvecsize; ++i) { arr[other.ind[i]] = other.num[i]; } } } else { std::cout << "Grids are not comparable for Set" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } // General purpose set operation on dense vector by a sparse vector template template void FullyDistVec::GSet (const FullyDistSpVec & spVec, _BinaryOperationIdx __binopIdx, _BinaryOperationVal __binopVal, MPI_Win win) { if(*(commGrid) != *(spVec.commGrid)) { std::cout << "Grids are not comparable for GSet" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } IT spVecSize = spVec.getlocnnz(); if(spVecSize==0) return; MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); std::vector< std::vector< NT > > datsent(nprocs); std::vector< std::vector< IT > > indsent(nprocs); IT lengthUntil = spVec.LengthUntil(); for(IT k=0; k < spVecSize; ++k) { IT locind; // get global index of the dense vector from the value. Most often a select operator. // If the first operand is selected, then invert; otherwise, EwiseApply. IT globind = __binopIdx(spVec.num[k], spVec.ind[k] + lengthUntil); int owner = Owner(globind, locind); // get local index NT val = __binopVal(spVec.num[k], spVec.ind[k] + lengthUntil); if(globind < glen) // prevent index greater than size of the composed vector { datsent[owner].push_back(val); indsent[owner].push_back(locind); // so that we don't need no correction at the recipient } } for(int j = 0; j < datsent[myrank].size(); ++j) // directly set local entries { arr[indsent[myrank][j]] = datsent[myrank][j]; } //MPI_Win win; //MPI_Win_create(&arr[0], LocArrSize() * sizeof(NT), sizeof(NT), MPI_INFO_NULL, World, &win); //MPI_Win_fence(0, win); for(int i=0; i(), i, indsent[i][j], 1, MPIType(), win); } MPI_Win_unlock(i, win); } } //MPI_Win_fence(0, win); //MPI_Win_free(&win); } // General purpose get operation on dense vector by a sparse vector // Get the element of the dense vector indexed by the value of the sparse vector // invert and get might not work in the presence of repeated values template template FullyDistSpVec FullyDistVec::GGet (const FullyDistSpVec & spVec, _BinaryOperationIdx __binopIdx, NT nullValue) { if(*(commGrid) != *(spVec.commGrid)) { std::cout << "Grids are not comparable for GGet" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); std::vector< std::vector< NT > > spIdx(nprocs); std::vector< std::vector< IT > > indsent(nprocs); IT lengthUntil = spVec.LengthUntil(); IT spVecSize = spVec.getlocnnz(); FullyDistSpVec res(spVec.commGrid, spVec.TotalLength()); res.ind.resize(spVecSize); res.num.resize(spVecSize); for(IT k=0; k < spVecSize; ++k) { IT locind; // get global index of the dense vector from the value. Most often a select operator. // If the first operand is selected, then invert; otherwise, EwiseApply. IT globind = __binopIdx(spVec.num[k], spVec.ind[k] + lengthUntil); int owner = Owner(globind, locind); // get local index //NT val = __binopVal(spVec.num[k], spVec.ind[k] + lengthUntil); if(globind < glen) // prevent index greater than size of the composed vector { spIdx[owner].push_back(k); // position of spVec indsent[owner].push_back(locind); // so that we don't need no correction at the recipient } else res.num[k] = nullValue; res.ind[k] = spVec.ind[k]; } for(int j = 0; j < indsent[myrank].size(); ++j) // directly get local entries { res.num[spIdx[myrank][j]] = arr[indsent[myrank][j]]; } MPI_Win win; MPI_Win_create(&arr[0], LocArrSize() * sizeof(NT), sizeof(NT), MPI_INFO_NULL, World, &win); for(int i=0; i(), i, indsent[i][j], 1, MPIType(), win); } MPI_Win_unlock(i, win); } } MPI_Win_free(&win); return res; } } CombBLAS_beta_16_2/include/CombBLAS/MMmul.h000644 000765 000024 00000004017 13271404146 021575 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _MM_MUL_H #define _MM_MUL_H namespace combblas { template struct MMmul { const BT & sm1; const BT & sm2; // Constructor MMmul(const BT & smm1, const BT & smm2): sm1(smm1), sm2(smm2) { } // No need for operator BT() because we have the corresponding copy constructor // and assignment operators to evaluate and return result ! }; template inline MMmul< BT > operator* (const BT & smm1, const BT & smm2) { return MMmul< BT >(smm1,smm2); //! Just defer the multiplication } } #endif CombBLAS_beta_16_2/include/CombBLAS/dcsc.h000644 000765 000024 00000012564 13271404146 021470 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _DCSC_H #define _DCSC_H #include #include #include #include #include "SpDefs.h" #include "SpHelper.h" #include "StackEntry.h" #include "MemoryPool.h" #include "promote.h" namespace combblas { template class Dcsc { public: typedef NT value_type; typedef IT index_type; Dcsc (); Dcsc (IT nnz, IT nzcol); Dcsc (IT nnz, const std::vector & indices, bool isRow); //!< Create a logical matrix from (row/column) indices vector Dcsc (StackEntry > * multstack, IT mdim, IT ndim, IT nnz); Dcsc (const Dcsc & rhs); // copy constructor Dcsc & operator=(const Dcsc & rhs); // assignment operator Dcsc & operator+=(const Dcsc & rhs); // add and assign operator ~Dcsc(); bool operator==(const Dcsc & rhs); template operator Dcsc() const; // operator Dcsc() const; // & rhs, bool exclude); void EWiseScale(NT ** scaler); // friend Dcsc::T_promote> EWiseMult(const Dcsc & A, const Dcsc * B, bool exclude); // Note that the second parameter is a POINTER template void Apply(_UnaryOperation __unary_op) { //transform(numx, numx+nz, numx, __unary_op); #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i < nz; ++i) numx[i] = __unary_op(numx[i]); } template Dcsc* PruneI(_UnaryOperation __unary_op, bool inPlace, GlobalIT rowOffset, GlobalIT colOffset); template Dcsc* Prune(_UnaryOperation __unary_op, bool inPlace); template Dcsc* PruneColumn(NT* pvals, _BinaryOperation __binary_op, bool inPlace); template Dcsc* PruneColumn(IT* pinds, NT* pvals, _BinaryOperation __binary_op, bool inPlace); IT AuxIndex(const IT colind, bool & found, IT * aux, IT csize) const; void RowSplit(int numsplits); void ColSplit(std::vector< Dcsc* > & parts, std::vector & cuts); void ColConcatenate(std::vector< Dcsc* > & parts, std::vector & offsets); void Split(Dcsc * & A, Dcsc * & B, IT cut); //! \todo{special case of ColSplit, to be deprecated...} void Merge(const Dcsc * Adcsc, const Dcsc * B, IT cut); //! \todo{special case of ColConcatenate, to be deprecated...} IT ConstructAux(IT ndim, IT * & aux) const; void Resize(IT nzcnew, IT nznew); template void FillColInds(const VT * colnums, IT nind, std::vector< std::pair > & colinds, IT * aux, IT csize) const; Dcsc & AddAndAssign (StackEntry > * multstack, IT mdim, IT ndim, IT nnz); template void UpdateDense(NT ** array, _BinaryOperation __binary_op) const; // update dense 2D array's entries with __binary_op using elements of "this" //! wrap object around pre-allocated arrays (possibly RDMA registered) Dcsc (IT * _cp, IT * _jc, IT * _ir, NT * _numx, IT _nz, IT _nzc, bool _memowned = true) : cp(_cp), jc(_jc), ir(_ir), numx(_numx), nz(_nz), nzc(_nzc), memowned(_memowned) {}; IT * cp; //!< The master array, size nzc+1 (keeps column pointers) IT * jc ; //!< col indices, size nzc IT * ir ; //!< row indices, size nz NT * numx; //!< generic values, size nz IT nz; IT nzc; //!< number of columns with at least one non-zero in them bool memowned; private: void getindices (StackEntry > * multstack, IT & rindex, IT & cindex, IT & j, IT nnz); }; } #include "dcsc.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/SpCCols.cpp000644 000765 000024 00000026717 13271404146 022422 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include "SpCCols.h" #include "Deleter.h" #include #include #include #include #include #include namespace combblas { /****************************************************************************/ /********************* PUBLIC CONSTRUCTORS/DESTRUCTORS **********************/ /****************************************************************************/ template const IT SpCCols::esscount = static_cast(3); template SpCCols::SpCCols():csc(NULL), m(0), n(0), nnz(0), splits(0){ } // Allocate all the space necessary template SpCCols::SpCCols(IT size, IT nRow, IT nCol) :m(nRow), n(nCol), nnz(size), splits(0) { if(nnz > 0) csc = new Csc(nnz, n); else csc = NULL; } template SpCCols::~SpCCols() { if(nnz > 0) { if(csc != NULL) { if(splits > 0) { for(int i=0; i SpCCols::SpCCols(const SpCCols & rhs) : m(rhs.m), n(rhs.n), nnz(rhs.nnz), splits(rhs.splits) { if(splits > 0) { for(int i=0; i SpCCols * @param[in] rhs if transpose=true, * \n then rhs is assumed to be a row sorted SpTuples object * \n else rhs is assumed to be a column sorted SpTuples object **/ template SpCCols::SpCCols(const SpTuples & rhs, bool transpose) : m(rhs.m), n(rhs.n), nnz(rhs.nnz), splits(0) { if(nnz == 0) // m by n matrix of complete zeros { if(transpose) std::swap(m,n); csc = NULL; } else { if(transpose) { std::swap(m,n); csc = new Csc(nnz,n); // the swap is already done here std::vector< std::pair > tosort (nnz); std::vector work(n+1, (IT) 0 ); // workspace, zero initialized, first entry stays zero for (IT k = 0 ; k < nnz ; ++k) { IT tmp = rhs.rowindex(k); work [ tmp+1 ]++ ; // column counts (i.e, w holds the "col difference array") } if(nnz > 0) { std::partial_sum(work.begin(), work.end(), work.begin()); std::copy(work.begin(), work.end(), csc->jc); IT last; for (IT k = 0 ; k < nnz ; ++k) { tosort[ work[ rhs.rowindex(k) ]++] = std::make_pair( rhs.colindex(k), rhs.numvalue(k)); } #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i< n; ++i) { sort(tosort.begin() + csc->jc[i], tosort.begin() + csc->jc[i+1]); IT ind; typename std::vector >::iterator itr; // iterator is a dependent name for(itr = tosort.begin() + csc->jc[i], ind = csc->jc[i]; itr != tosort.begin() + csc->jc[i+1]; ++itr, ++ind) { csc->ir[ind] = itr->first; csc->num[ind] = itr->second; } } } } else { csc = new Csc(nnz,n); // the swap is already done here std::vector< std::pair > tosort (nnz); std::vector work(n+1, (IT) 0 ); // workspace, zero initialized, first entry stays zero for (IT k = 0 ; k < nnz ; ++k) { IT tmp = rhs.colindex(k); work [ tmp+1 ]++ ; // column counts (i.e, w holds the "col difference array") } if(nnz > 0) { std::partial_sum(work.begin(), work.end(), work.begin()); std::copy(work.begin(), work.end(), csc->jc); IT last; for (IT k = 0 ; k < nnz ; ++k) { tosort[ work[ rhs.colindex(k) ]++] = std::make_pair( rhs.rowindex(k), rhs.numvalue(k)); } #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i< n; ++i) { sort(tosort.begin() + csc->jc[i], tosort.begin() + csc->jc[i+1]); IT ind; typename std::vector >::iterator itr; // iterator is a dependent name for(itr = tosort.begin() + csc->jc[i], ind = csc->jc[i]; itr != tosort.begin() + csc->jc[i+1]; ++itr, ++ind) { csc->ir[ind] = itr->first; csc->num[ind] = itr->second; } } } } } } /****************************************************************************/ /************************** PUBLIC OPERATORS ********************************/ /****************************************************************************/ /** * The assignment operator operates on an existing object * The assignment operator is the only operator that is not inherited. * But there is no need to call base's assigment operator as it has no data members */ template SpCCols & SpCCols::operator=(const SpCCols & rhs) { // this pointer stores the address of the class instance // check for self assignment using address comparison if(this != &rhs) { if(csc != NULL && nnz > 0) { delete csc; } if(rhs.csc != NULL) { csc = new Csc(*(rhs.csc)); nnz = rhs.nnz; } else { csc = NULL; nnz = 0; } m = rhs.m; n = rhs.n; splits = rhs.splits; } return *this; } template void SpCCols::RowSplit(int numsplits) { splits = numsplits; IT perpiece = m / splits; std::vector nnzs(splits, 0); std::vector < std::vector < std::tuple > > colrowpairs(splits); std::vector< std::vector > colcnts(splits); for(int i=0; i< splits; ++i) colcnts[i].resize(n, 0); if(nnz > 0 && csc != NULL) { for(IT i=0; i< csc->n; ++i) { for(IT j = csc->jc[i]; j< csc->jc[i+1]; ++j) { IT rowid = csc->ir[j]; // colid=i IT owner = std::min(rowid / perpiece, static_cast(splits-1)); colrowpairs[owner].push_back(std::make_tuple(i, rowid - owner*perpiece, csc->num[i])); ++(colcnts[owner][i]); ++(nnzs[owner]); } } } delete csc; // claim memory cscarr = new Csc*[splits]; #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i< splits; ++i) // i iterates over splits { cscarr[i] = new Csc(nnzs[i],n); sort(colrowpairs[i].begin(), colrowpairs[i].end()); // sort w.r.t. columns first and rows second cscarr[i]->jc[0] = 0; std::partial_sum(colcnts[i].begin(), colcnts[i].end(), cscarr[i]->jc+1); std::copy(cscarr[i]->jc, cscarr[i]->jc+n, colcnts[i].begin()); // reuse the colcnts as "current column pointers" for(IT k=0; k(colrowpairs[i][k]); IT rindex = std::get<1>(colrowpairs[i][k]); NT value = std::get<2>(colrowpairs[i][k]); IT curcptr = (colcnts[i][cindex])++; // fetch the pointer and post increment cscarr[i]->ir[curcptr] = rindex; cscarr[i]->num[curcptr] = value; } } } template void SpCCols::PrintInfo() const { std::cout << "m: " << m ; std::cout << ", n: " << n ; std::cout << ", nnz: "<< nnz ; if(splits > 0) { std::cout << ", local splits: " << splits << std::endl; #ifdef _OPENMP if(omp_get_thread_num() == 0) { SubPrintInfo(cscarr[0]); } #endif } else { std::cout << std::endl; SubPrintInfo(csc); } } /****************************************************************************/ /************************* PRIVATE MEMBER FUNCTIONS *************************/ /****************************************************************************/ template void SpCCols::SubPrintInfo(Csc * mycsc) const { #ifdef _OPENMP std::cout << "Printing for thread " << omp_get_thread_num() << std::endl; #endif if(m < PRINT_LIMIT && n < PRINT_LIMIT) // small enough to print { NT ** A = SpHelper::allocate2D(m,n); for(IT i=0; i< m; ++i) for(IT j=0; jjc[i]; j< mycsc->jc[i+1]; ++j) { IT rowid = mycsc->ir[j]; A[rowid][i] = mycsc->num[j]; } } } for(IT i=0; i< m; ++i) { for(IT j=0; j inline void SpCCols::CopyCsc(Csc * source) { // source csc will be NULL if number of nonzeros is zero if(source != NULL) csc = new Csc(*source); else csc = NULL; } } CombBLAS_beta_16_2/include/CombBLAS/SpImpl.h000644 000765 000024 00000022122 13271404146 021747 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SP_IMPL_H_ #define _SP_IMPL_H_ #include #include #include "PreAllocatedSPA.h" #include "Deleter.h" namespace combblas { template class Dcsc; template class Csc; template struct SpImpl; //! Overload #1: DCSC template void SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy, PreAllocatedSPA & SPA) { // ignoring SPA for now. However, a branching similar to the CSC case can be implemented SpImpl::SpMXSpV(Adcsc, mA, indx, numx, veclen, indy, numy); // don't touch this }; //! Overload #2: DCSC template void SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, int32_t * indy, OVT * numy, int * cnts, int * dspls, int p_c) { SpImpl::SpMXSpV(Adcsc, mA, indx, numx, veclen, indy, numy, cnts, dspls,p_c); // don't touch this }; //! Overload #3: DCSC template void SpMXSpV_ForThreading(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy, int32_t offset) { SpImpl::SpMXSpV_ForThreading(Adcsc, mA, indx, numx, veclen, indy, numy, offset); // don't touch this }; //! Overload #4: DCSC w/ preallocated SPA template void SpMXSpV_ForThreading(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy, int32_t offset, std::vector & localy, BitMap & isthere, std::vector & nzinds) { SpImpl::SpMXSpV_ForThreading(Adcsc, mA, indx, numx, veclen, indy, numy, offset, localy, isthere, nzinds); }; /* The following two functions are base CSC implementation. All overloaded function calls will be routed to these functions. */ // all CSC will fall to this template void SpMXSpV_HeapSort(const Csc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy, int32_t offset); // all PreAllocatedSPA will fall to this template void SpMXSpV_Bucket(const Csc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen,std::vector & indy, std::vector< OVT > & numy, PreAllocatedSPA & SPA); //! Overload #1: CSC template void SpMXSpV(const Csc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, int32_t * indy, OVT * numy, int * cnts, int * dspls, int p_c) { std::cout << "Optbuf enabled version is not yet supported with CSC matrices" << std::endl; }; //! Overload #2: CSC template void SpMXSpV(const Csc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy, PreAllocatedSPA & SPA) { if(SPA.initialized) SpMXSpV_Bucket(Acsc, mA, indx, numx, veclen, indy, numy, SPA); else SpMXSpV_HeapSort(Acsc, mA, indx, numx, veclen, indy, numy, 0); }; //! Overload #3: CSC template void SpMXSpV_ForThreading(const Csc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy, int32_t offset) { SpMXSpV_HeapSort(Acsc, mA, indx, numx, veclen, indy, numy, offset); }; //! Overload #4: CSC w/ preallocated SPA template void SpMXSpV_ForThreading(const Csc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy, int32_t offset, std::vector & localy, BitMap & isthere, std::vector & nzinds) { SpMXSpV_HeapSort(Acsc, mA, indx, numx, veclen, indy, numy, offset); // We can eventually call SpMXSpV_HeapMerge or SpMXSpV_SPA (not implemented for CSC yet) }; /** * IT: The sparse matrix index type. Sparse vector index type is fixed to be int32_t * It is the caller function's (inside ParFriends/Friends) job to convert any different types * and ensure correctness. Rationale is efficiency, and the fact that we know for sure * that 32-bit LOCAL indices are sufficient for all reasonable concurrencies and data sizes (as of 2011) * \todo: As of 2015, this might not be true!!! (ABAB) **/ template struct SpImpl { static void SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy); // specialize this static void SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, int32_t * indy, OVT * numy, int * cnts, int * dspls, int p_c) { std::cout << "Optbuf enabled version is not yet supported with general (non-boolean) matrices" << std::endl; }; static void SpMXSpV_ForThreading(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy, int32_t offset) { std::cout << "Threaded version is not yet supported with general (non-boolean) matrices" << std::endl; }; static void SpMXSpV_ForThreading(const Dcsc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy, int32_t offset, std::vector & localy, BitMap & isthere, std::vector & nzinds) { std::cout << "Threaded version is not yet supported with general (non-boolean) matrices" << std::endl; }; }; template struct SpImpl // specialization { static void SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy); static void SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, int32_t * indy, OVT * numy, int * cnts, int * dspls, int p_c); //! Dcsc and vector index types do not need to match static void SpMXSpV_ForThreading(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy, int32_t offset); //! Dcsc and vector index types do not need to match static void SpMXSpV_ForThreading(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy, int32_t offset, std::vector & localy, BitMap & isthere, std::vector & nzinds); }; } #include "SpImpl.cpp" #endifCombBLAS_beta_16_2/include/CombBLAS/Friends.h000644 000765 000024 00000110411 13271404146 022134 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _FRIENDS_H_ #define _FRIENDS_H_ #include #include "SpMat.h" // Best to include the base class first #include "SpHelper.h" #include "StackEntry.h" #include "Isect.h" #include "Deleter.h" #include "SpImpl.h" #include "SpParHelper.h" #include "Compare.h" #include "CombBLAS.h" #include "PreAllocatedSPA.h" namespace combblas { template class SpTuples; template class SpDCCols; template class Dcsc; /*************************************************************************************************/ /**************************** SHARED ADDRESS SPACE FRIEND FUNCTIONS ******************************/ /****************************** MULTITHREADED LOGIC ALSO GOES HERE *******************************/ /*************************************************************************************************/ //! SpMV with dense vector template void dcsc_gespmv (const SpDCCols & A, const RHS * x, LHS * y) { if(A.nnz > 0) { for(IU j =0; jnzc; ++j) // for all nonzero columns { IU colid = A.dcsc->jc[j]; for(IU i = A.dcsc->cp[j]; i< A.dcsc->cp[j+1]; ++i) { IU rowid = A.dcsc->ir[i]; SR::axpy(A.dcsc->numx[i], x[colid], y[rowid]); } } } } //! SpMV with dense vector (multithreaded version) template void dcsc_gespmv_threaded (const SpDCCols & A, const RHS * x, LHS * y) { if(A.nnz > 0) { int nthreads=1; #ifdef _OPENMP #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif IU nlocrows = A.getnrow(); LHS ** tomerge = SpHelper::allocate2D(nthreads, nlocrows); auto id = SR::id(); for(int i=0; inzc; ++j) // for all nonzero columns { int curthread = 1; #ifdef _OPENMP curthread = omp_get_thread_num(); #endif LHS * loc2merge = tomerge[curthread]; IU colid = A.dcsc->jc[j]; for(IU i = A.dcsc->cp[j]; i< A.dcsc->cp[j+1]; ++i) { IU rowid = A.dcsc->ir[i]; SR::axpy(A.dcsc->numx[i], x[colid], loc2merge[rowid]); } } #pragma omp parallel for for(IU j=0; j < nlocrows; ++j) { for(int i=0; i< nthreads; ++i) { y[j] = SR::add(y[j], tomerge[i][j]); } } SpHelper::deallocate2D(tomerge, nthreads); } } /** * Multithreaded SpMV with sparse vector * the assembly of outgoing buffers sendindbuf/sendnumbuf are done here */ template int generic_gespmv_threaded (const SpMat & A, const int32_t * indx, const IVT * numx, int32_t nnzx, int32_t * & sendindbuf, OVT * & sendnumbuf, int * & sdispls, int p_c, PreAllocatedSPA & SPA) { // FACTS: Split boundaries (for multithreaded execution) are independent of recipient boundaries // Two splits might create output to the same recipient (needs to be merged) // However, each split's output is distinct (no duplicate elimination is needed after merge) sdispls = new int[p_c](); // initialize to zero (as all indy might be empty) if(A.getnnz() > 0 && nnzx > 0) { int splits = A.getnsplit(); if(splits > 0) { int32_t nlocrows = static_cast(A.getnrow()); int32_t perpiece = nlocrows / splits; std::vector< std::vector< int32_t > > indy(splits); std::vector< std::vector< OVT > > numy(splits); // Parallelize with OpenMP #ifdef _OPENMP #pragma omp parallel for // num_threads(6) #endif for(int i=0; i(*(A.GetInternal(i)), perpiece, indx, numx, nnzx, indy[i], numy[i], i*perpiece, SPA.V_localy[i], SPA.V_isthere[i], SPA.V_inds[i]); else SpMXSpV_ForThreading(*(A.GetInternal(i)), nlocrows - perpiece*i, indx, numx, nnzx, indy[i], numy[i], i*perpiece, SPA.V_localy[i], SPA.V_isthere[i], SPA.V_inds[i]); } else { if(i != splits-1) SpMXSpV_ForThreading(*(A.GetInternal(i)), perpiece, indx, numx, nnzx, indy[i], numy[i], i*perpiece); else SpMXSpV_ForThreading(*(A.GetInternal(i)), nlocrows - perpiece*i, indx, numx, nnzx, indy[i], numy[i], i*perpiece); } } std::vector accum(splits+1, 0); for(int i=0; i end_recs(splits); for(int i=0; i= 0 && end_recs[k] == -1) k--; // loop backwards until seeing an non-empty split if(k >= 0) // we found a non-empty split { std::fill(sdispls+end_recs[k]+1, sdispls+beg_rec+1, accum[i]); // last entry to be set is sdispls[beg_rec] } // else fill sdispls[1...beg_rec] with zero (already done) } // else set sdispls[0] to zero (already done) if(beg_rec == end_recs[i]) // fast case { std::transform(indy[i].begin(), indy[i].end(), indy[i].begin(), std::bind2nd(std::minus(), perproc*beg_rec)); std::copy(indy[i].begin(), indy[i].end(), sendindbuf+accum[i]); std::copy(numy[i].begin(), numy[i].end(), sendnumbuf+accum[i]); } else // slow case { // FACT: No matter how many splits or threads, there will be only one "recipient head" // Therefore there are no race conditions for marking send displacements (sdispls) int end = indy[i].size(); for(int cur=0; cur< end; ++cur) { int32_t cur_rec = std::min( indy[i][cur] / perproc, last_rec); while(beg_rec != cur_rec) { sdispls[++beg_rec] = accum[i] + cur; // first entry to be set is sdispls[beg_rec+1] } sendindbuf[ accum[i] + cur ] = indy[i][cur] - perproc*beg_rec; // convert to receiver's local index sendnumbuf[ accum[i] + cur ] = numy[i][cur]; } } std::vector().swap(indy[i]); std::vector().swap(numy[i]); bool lastnonzero = true; // am I the last nonzero split? for(int k=i+1; k < splits; ++k) { if(end_recs[k] != -1) lastnonzero = false; } if(lastnonzero) std::fill(sdispls+end_recs[i]+1, sdispls+p_c, accum[i+1]); } // end_if(!indy[i].empty) } // end parallel for return accum[splits]; } else { std::cout << "Something is wrong, splits should be nonzero for multithreaded execution" << std::endl; return 0; } } else { sendindbuf = NULL; sendnumbuf = NULL; return 0; } } /** * Multithreaded SpMV with sparse vector and preset buffers * the assembly of outgoing buffers sendindbuf/sendnumbuf are done here * IVT: input vector numerical type * OVT: output vector numerical type */ template void generic_gespmv_threaded_setbuffers (const SpMat & A, const int32_t * indx, const IVT * numx, int32_t nnzx, int32_t * sendindbuf, OVT * sendnumbuf, int * cnts, int * dspls, int p_c) { if(A.getnnz() > 0 && nnzx > 0) { int splits = A.getnsplit(); if(splits > 0) { std::vector< std::vector > indy(splits); std::vector< std::vector< OVT > > numy(splits); int32_t nlocrows = static_cast(A.getnrow()); int32_t perpiece = nlocrows / splits; #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i(*(A.GetInternal(i)), perpiece, indx, numx, nnzx, indy[i], numy[i], i*perpiece); else SpMXSpV_ForThreading(*(A.GetInternal(i)), nlocrows - perpiece*i, indx, numx, nnzx, indy[i], numy[i], i*perpiece); } int32_t perproc = nlocrows / p_c; int32_t last_rec = p_c-1; // keep recipients of last entries in each split (-1 for an empty split) // so that we can delete indy[] and numy[] contents as soon as they are processed std::vector end_recs(splits); for(int i=0; i::iterator it = indy[i].begin(); it != indy[i].end(); ++it) { if( ( (*it) >= lastdata ) && cur_rec != last_rec ) { cur_rec = std::min( (*it) / perproc, last_rec); lastdata = (cur_rec+1) * perproc; } ++loc_rec_cnts[i][cur_rec]; } } } #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i= 0; before--) alreadysent += loc_rec_cnts[before][beg_rec]; if(beg_rec == end_recs[i]) // fast case { std::transform(indy[i].begin(), indy[i].end(), indy[i].begin(), std::bind2nd(std::minus(), perproc*beg_rec)); std::copy(indy[i].begin(), indy[i].end(), sendindbuf + dspls[beg_rec] + alreadysent); std::copy(numy[i].begin(), numy[i].end(), sendnumbuf + dspls[beg_rec] + alreadysent); } else // slow case { int32_t cur_rec = beg_rec; int32_t lastdata = (cur_rec+1) * perproc; // one past last entry that goes to this current recipient for(typename std::vector::iterator it = indy[i].begin(); it != indy[i].end(); ++it) { if( ( (*it) >= lastdata ) && cur_rec != last_rec ) { cur_rec = std::min( (*it) / perproc, last_rec); lastdata = (cur_rec+1) * perproc; // if this split switches to a new recipient after sending some data // then it's sure that no data has been sent to that recipient yet alreadysent = 0; } sendindbuf[ dspls[cur_rec] + alreadysent ] = (*it) - perproc*cur_rec; // convert to receiver's local index sendnumbuf[ dspls[cur_rec] + (alreadysent++) ] = *(numy[i].begin() + (it-indy[i].begin())); } } } } // Deallocated rec counts serially once all threads complete for(int i=0; i< splits; ++i) { for(int j=0; j< p_c; ++j) cnts[j] += loc_rec_cnts[i][j]; delete [] loc_rec_cnts[i]; } delete [] loc_rec_cnts; } else { std::cout << "Something is wrong, splits should be nonzero for multithreaded execution" << std::endl; } } } //! SpMV with sparse vector //! MIND: Matrix index type //! VIND: Vector index type (optimized: int32_t, general: int64_t) template void generic_gespmv (const SpMat & A, const VIND * indx, const IVT * numx, VIND nnzx, std::vector & indy, std::vector & numy, PreAllocatedSPA & SPA) { if(A.getnnz() > 0 && nnzx > 0) { if(A.getnsplit() > 0) { std::cout << "Call dcsc_gespmv_threaded instead" << std::endl; } else { SpMXSpV(*(A.GetInternal()), (VIND) A.getnrow(), indx, numx, nnzx, indy, numy, SPA); } } } /** SpMV with sparse vector * @param[in] indexisvalue is only used for BFS-like computations, if true then we can call the optimized version that skips SPA */ template void generic_gespmv (const SpMat & A, const int32_t * indx, const IVT * numx, int32_t nnzx, int32_t * indy, OVT * numy, int * cnts, int * dspls, int p_c, bool indexisvalue) { if(A.getnnz() > 0 && nnzx > 0) { if(A.getnsplit() > 0) { SpParHelper::Print("Call dcsc_gespmv_threaded instead\n"); } else { SpMXSpV(*(A.GetInternal()), (int32_t) A.getnrow(), indx, numx, nnzx, indy, numy, cnts, dspls, p_c); } } } template void BooleanRowSplit(SpDCCols & A, int numsplits) { A.splits = numsplits; IU perpiece = A.m / A.splits; std::vector prevcolids(A.splits, -1); // previous column id's are set to -1 std::vector nzcs(A.splits, 0); std::vector nnzs(A.splits, 0); std::vector < std::vector < std::pair > > colrowpairs(A.splits); if(A.nnz > 0 && A.dcsc != NULL) { for(IU i=0; i< A.dcsc->nzc; ++i) { for(IU j = A.dcsc->cp[i]; j< A.dcsc->cp[i+1]; ++j) { IU colid = A.dcsc->jc[i]; IU rowid = A.dcsc->ir[j]; IU owner = std::min(rowid / perpiece, static_cast(A.splits-1)); colrowpairs[owner].push_back(std::make_pair(colid, rowid - owner*perpiece)); if(prevcolids[owner] != colid) { prevcolids[owner] = colid; ++nzcs[owner]; } ++nnzs[owner]; } } } delete A.dcsc; // claim memory //copy(nzcs.begin(), nzcs.end(), ostream_iterator(cout," " )); cout << endl; //copy(nnzs.begin(), nnzs.end(), ostream_iterator(cout," " )); cout << endl; A.dcscarr = new Dcsc*[A.splits]; // To be parallelized with OpenMP for(int i=0; i< A.splits; ++i) { sort(colrowpairs[i].begin(), colrowpairs[i].end()); // sort w.r.t. columns A.dcscarr[i] = new Dcsc(nnzs[i],nzcs[i]); std::fill(A.dcscarr[i]->numx, A.dcscarr[i]->numx+nnzs[i], static_cast(1)); IU curnzc = 0; // number of nonzero columns constructed so far IU cindex = colrowpairs[i][0].first; IU rindex = colrowpairs[i][0].second; A.dcscarr[i]->ir[0] = rindex; A.dcscarr[i]->jc[curnzc] = cindex; A.dcscarr[i]->cp[curnzc++] = 0; for(IU j=1; jir[j] = rindex; if(cindex != A.dcscarr[i]->jc[curnzc-1]) { A.dcscarr[i]->jc[curnzc] = cindex; A.dcscarr[i]->cp[curnzc++] = j; } } A.dcscarr[i]->cp[curnzc] = nnzs[i]; } } /** * SpTuples(A*B') (Using OuterProduct Algorithm) * Returns the tuples for efficient merging later * Support mixed precision multiplication * The multiplication is on the specified semiring (passed as parameter) */ template SpTuples * Tuples_AnXBt (const SpDCCols & A, const SpDCCols & B, bool clearA = false, bool clearB = false) { IU mdim = A.m; IU ndim = B.m; // B is already transposed if(A.isZero() || B.isZero()) { if(clearA) delete const_cast *>(&A); if(clearB) delete const_cast *>(&B); return new SpTuples< IU, NUO >(0, mdim, ndim); // just return an empty matrix } Isect *isect1, *isect2, *itr1, *itr2, *cols, *rows; SpHelper::SpIntersect(*(A.dcsc), *(B.dcsc), cols, rows, isect1, isect2, itr1, itr2); IU kisect = static_cast(itr1-isect1); // size of the intersection ((itr1-isect1) == (itr2-isect2)) if(kisect == 0) { if(clearA) delete const_cast *>(&A); if(clearB) delete const_cast *>(&B); DeleteAll(isect1, isect2, cols, rows); return new SpTuples< IU, NUO >(0, mdim, ndim); } StackEntry< NUO, std::pair > * multstack; IU cnz = SpHelper::SpCartesian< SR > (*(A.dcsc), *(B.dcsc), kisect, isect1, isect2, multstack); DeleteAll(isect1, isect2, cols, rows); if(clearA) delete const_cast *>(&A); if(clearB) delete const_cast *>(&B); return new SpTuples (cnz, mdim, ndim, multstack); } /** * SpTuples(A*B) (Using ColByCol Algorithm) * Returns the tuples for efficient merging later * Support mixed precision multiplication * The multiplication is on the specified semiring (passed as parameter) */ template SpTuples * Tuples_AnXBn (const SpDCCols & A, const SpDCCols & B, bool clearA = false, bool clearB = false) { IU mdim = A.m; IU ndim = B.n; if(A.isZero() || B.isZero()) { return new SpTuples(0, mdim, ndim); } StackEntry< NUO, std::pair > * multstack; IU cnz = SpHelper::SpColByCol< SR > (*(A.dcsc), *(B.dcsc), A.n, multstack); if(clearA) delete const_cast *>(&A); if(clearB) delete const_cast *>(&B); return new SpTuples (cnz, mdim, ndim, multstack); } template SpTuples * Tuples_AtXBt (const SpDCCols & A, const SpDCCols & B, bool clearA = false, bool clearB = false) { IU mdim = A.n; IU ndim = B.m; std::cout << "Tuples_AtXBt function has not been implemented yet !" << std::endl; return new SpTuples (0, mdim, ndim); } template SpTuples * Tuples_AtXBn (const SpDCCols & A, const SpDCCols & B, bool clearA = false, bool clearB = false) { IU mdim = A.n; IU ndim = B.n; std::cout << "Tuples_AtXBn function has not been implemented yet !" << std::endl; return new SpTuples (0, mdim, ndim); } // Performs a balanced merge of the array of SpTuples // Assumes the input parameters are already column sorted template SpTuples MergeAll( const std::vector *> & ArrSpTups, IU mstar = 0, IU nstar = 0, bool delarrs = false ) { int hsize = ArrSpTups.size(); if(hsize == 0) { return SpTuples(0, mstar,nstar); } else { mstar = ArrSpTups[0]->m; nstar = ArrSpTups[0]->n; } for(int i=1; i< hsize; ++i) { if((mstar != ArrSpTups[i]->m) || nstar != ArrSpTups[i]->n) { std::cerr << "Dimensions do not match on MergeAll()" << std::endl; return SpTuples(0,0,0); } } if(hsize > 1) { ColLexiCompare heapcomp; std::tuple * heap = new std::tuple [hsize]; // (rowindex, colindex, source-id) IU * curptr = new IU[hsize]; std::fill_n(curptr, hsize, static_cast(0)); IU estnnz = 0; for(int i=0; i< hsize; ++i) { estnnz += ArrSpTups[i]->getnnz(); heap[i] = std::make_tuple(std::get<0>(ArrSpTups[i]->tuples[0]), std::get<1>(ArrSpTups[i]->tuples[0]), i); } std::make_heap(heap, heap+hsize, std::not2(heapcomp)); std::tuple * ntuples = new std::tuple[estnnz]; IU cnz = 0; while(hsize > 0) { std::pop_heap(heap, heap + hsize, std::not2(heapcomp)); // result is stored in heap[hsize-1] int source = std::get<2>(heap[hsize-1]); if( (cnz != 0) && ((std::get<0>(ntuples[cnz-1]) == std::get<0>(heap[hsize-1])) && (std::get<1>(ntuples[cnz-1]) == std::get<1>(heap[hsize-1]))) ) { std::get<2>(ntuples[cnz-1]) = SR::add(std::get<2>(ntuples[cnz-1]), ArrSpTups[source]->numvalue(curptr[source]++)); } else { ntuples[cnz++] = ArrSpTups[source]->tuples[curptr[source]++]; } if(curptr[source] != ArrSpTups[source]->getnnz()) // That array has not been depleted { heap[hsize-1] = std::make_tuple(std::get<0>(ArrSpTups[source]->tuples[curptr[source]]), std::get<1>(ArrSpTups[source]->tuples[curptr[source]]), source); std::push_heap(heap, heap+hsize, std::not2(heapcomp)); } else { --hsize; } } SpHelper::ShrinkArray(ntuples, cnz); DeleteAll(heap, curptr); if(delarrs) { for(size_t i=0; i (cnz, mstar, nstar, ntuples); } else { SpTuples ret = *ArrSpTups[0]; if(delarrs) delete ArrSpTups[0]; return ret; } } /** * @param[in] exclude if false, * \n then operation is A = A .* B * \n else operation is A = A .* not(B) **/ template Dcsc::T_promote> EWiseMult(const Dcsc & A, const Dcsc * B, bool exclude) { typedef typename promote_trait::T_promote N_promote; IU estnzc, estnz; if(exclude) { estnzc = A.nzc; estnz = A.nz; } else { estnzc = std::min(A.nzc, B->nzc); estnz = std::min(A.nz, B->nz); } Dcsc temp(estnz, estnzc); IU curnzc = 0; IU curnz = 0; IU i = 0; IU j = 0; temp.cp[0] = 0; if(!exclude) // A = A .* B { while(i< A.nzc && B != NULL && jnzc) { if(A.jc[i] > B->jc[j]) ++j; else if(A.jc[i] < B->jc[j]) ++i; else { IU ii = A.cp[i]; IU jj = B->cp[j]; IU prevnz = curnz; while (ii < A.cp[i+1] && jj < B->cp[j+1]) { if (A.ir[ii] < B->ir[jj]) ++ii; else if (A.ir[ii] > B->ir[jj]) ++jj; else { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = A.numx[ii++] * B->numx[jj++]; } } if(prevnz < curnz) // at least one nonzero exists in this column { temp.jc[curnzc++] = A.jc[i]; temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } ++i; ++j; } } } else // A = A .* not(B) { while(i< A.nzc && B != NULL && j< B->nzc) { if(A.jc[i] > B->jc[j]) ++j; else if(A.jc[i] < B->jc[j]) { temp.jc[curnzc++] = A.jc[i++]; for(IU k = A.cp[i-1]; k< A.cp[i]; k++) { temp.ir[curnz] = A.ir[k]; temp.numx[curnz++] = A.numx[k]; } temp.cp[curnzc] = temp.cp[curnzc-1] + (A.cp[i] - A.cp[i-1]); } else { IU ii = A.cp[i]; IU jj = B->cp[j]; IU prevnz = curnz; while (ii < A.cp[i+1] && jj < B->cp[j+1]) { if (A.ir[ii] > B->ir[jj]) ++jj; else if (A.ir[ii] < B->ir[jj]) { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = A.numx[ii++]; } else // eliminate those existing nonzeros { ++ii; ++jj; } } while (ii < A.cp[i+1]) { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = A.numx[ii++]; } if(prevnz < curnz) // at least one nonzero exists in this column { temp.jc[curnzc++] = A.jc[i]; temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } ++i; ++j; } } while(i< A.nzc) { temp.jc[curnzc++] = A.jc[i++]; for(IU k = A.cp[i-1]; k< A.cp[i]; ++k) { temp.ir[curnz] = A.ir[k]; temp.numx[curnz++] = A.numx[k]; } temp.cp[curnzc] = temp.cp[curnzc-1] + (A.cp[i] - A.cp[i-1]); } } temp.Resize(curnzc, curnz); return temp; } template Dcsc EWiseApply(const Dcsc & A, const Dcsc * B, _BinaryOperation __binary_op, bool notB, const NU2& defaultBVal) { //typedef typename promote_trait::T_promote N_promote; IU estnzc, estnz; if(notB) { estnzc = A.nzc; estnz = A.nz; } else { estnzc = std::min(A.nzc, B->nzc); estnz = std::min(A.nz, B->nz); } Dcsc temp(estnz, estnzc); IU curnzc = 0; IU curnz = 0; IU i = 0; IU j = 0; temp.cp[0] = 0; if(!notB) // A = A .* B { while(i< A.nzc && B != NULL && jnzc) { if(A.jc[i] > B->jc[j]) ++j; else if(A.jc[i] < B->jc[j]) ++i; else { IU ii = A.cp[i]; IU jj = B->cp[j]; IU prevnz = curnz; while (ii < A.cp[i+1] && jj < B->cp[j+1]) { if (A.ir[ii] < B->ir[jj]) ++ii; else if (A.ir[ii] > B->ir[jj]) ++jj; else { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = __binary_op(A.numx[ii++], B->numx[jj++]); } } if(prevnz < curnz) // at least one nonzero exists in this column { temp.jc[curnzc++] = A.jc[i]; temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } ++i; ++j; } } } else // A = A .* not(B) { while(i< A.nzc && B != NULL && j< B->nzc) { if(A.jc[i] > B->jc[j]) ++j; else if(A.jc[i] < B->jc[j]) { temp.jc[curnzc++] = A.jc[i++]; for(IU k = A.cp[i-1]; k< A.cp[i]; k++) { temp.ir[curnz] = A.ir[k]; temp.numx[curnz++] = __binary_op(A.numx[k], defaultBVal); } temp.cp[curnzc] = temp.cp[curnzc-1] + (A.cp[i] - A.cp[i-1]); } else { IU ii = A.cp[i]; IU jj = B->cp[j]; IU prevnz = curnz; while (ii < A.cp[i+1] && jj < B->cp[j+1]) { if (A.ir[ii] > B->ir[jj]) ++jj; else if (A.ir[ii] < B->ir[jj]) { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = __binary_op(A.numx[ii++], defaultBVal); } else // eliminate those existing nonzeros { ++ii; ++jj; } } while (ii < A.cp[i+1]) { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = __binary_op(A.numx[ii++], defaultBVal); } if(prevnz < curnz) // at least one nonzero exists in this column { temp.jc[curnzc++] = A.jc[i]; temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } ++i; ++j; } } while(i< A.nzc) { temp.jc[curnzc++] = A.jc[i++]; for(IU k = A.cp[i-1]; k< A.cp[i]; ++k) { temp.ir[curnz] = A.ir[k]; temp.numx[curnz++] = __binary_op(A.numx[k], defaultBVal); } temp.cp[curnzc] = temp.cp[curnzc-1] + (A.cp[i] - A.cp[i-1]); } } temp.Resize(curnzc, curnz); return temp; } template SpDCCols::T_promote > EWiseMult (const SpDCCols & A, const SpDCCols & B, bool exclude) { typedef typename promote_trait::T_promote N_promote; assert(A.m == B.m); assert(A.n == B.n); Dcsc * tdcsc = NULL; if(A.nnz > 0 && B.nnz > 0) { tdcsc = new Dcsc(EWiseMult(*(A.dcsc), B.dcsc, exclude)); return SpDCCols (A.m , A.n, tdcsc); } else if (A.nnz > 0 && exclude) // && B.nnz == 0 { tdcsc = new Dcsc(EWiseMult(*(A.dcsc), (const Dcsc*)NULL, exclude)); return SpDCCols (A.m , A.n, tdcsc); } else { return SpDCCols (A.m , A.n, tdcsc); } } template SpDCCols EWiseApply (const SpDCCols & A, const SpDCCols & B, _BinaryOperation __binary_op, bool notB, const NU2& defaultBVal) { //typedef typename promote_trait::T_promote N_promote; assert(A.m == B.m); assert(A.n == B.n); Dcsc * tdcsc = NULL; if(A.nnz > 0 && B.nnz > 0) { tdcsc = new Dcsc(EWiseApply(*(A.dcsc), B.dcsc, __binary_op, notB, defaultBVal)); return SpDCCols (A.m , A.n, tdcsc); } else if (A.nnz > 0 && notB) // && B.nnz == 0 { tdcsc = new Dcsc(EWiseApply(*(A.dcsc), (const Dcsc*)NULL, __binary_op, notB, defaultBVal)); return SpDCCols (A.m , A.n, tdcsc); } else { return SpDCCols (A.m , A.n, tdcsc); } } /** * Implementation based on operator += * Element wise apply with the following constraints * The operation to be performed is __binary_op * The operation `c = __binary_op(a, b)` is only performed if `do_op(a, b)` returns true * If allowANulls is true, then if A is missing an element that B has, then ANullVal is used * In that case the operation becomes c[i,j] = __binary_op(ANullVal, b[i,j]) * If both allowANulls and allowBNulls is false then the function degenerates into intersection */ template Dcsc EWiseApply(const Dcsc * Ap, const Dcsc * Bp, _BinaryOperation __binary_op, _BinaryPredicate do_op, bool allowANulls, bool allowBNulls, const NU1& ANullVal, const NU2& BNullVal, const bool allowIntersect) { if (Ap == NULL && Bp == NULL) return Dcsc(0, 0); if (Ap == NULL && Bp != NULL) { if (!allowANulls) return Dcsc(0, 0); const Dcsc & B = *Bp; IU estnzc = B.nzc; IU estnz = B.nz; Dcsc temp(estnz, estnzc); IU curnzc = 0; IU curnz = 0; //IU i = 0; IU j = 0; temp.cp[0] = 0; while(j(0, 0); const Dcsc & A = *Ap; IU estnzc = A.nzc; IU estnz = A.nz; Dcsc temp(estnz, estnzc); IU curnzc = 0; IU curnz = 0; IU i = 0; //IU j = 0; temp.cp[0] = 0; while(i< A.nzc) { i++; IU prevnz = curnz; temp.jc[curnzc++] = A.jc[i-1]; for(IU k = A.cp[i-1]; k< A.cp[i]; k++) { if (do_op(A.numx[k], BNullVal, false, true)) { temp.ir[curnz] = A.ir[k]; temp.numx[curnz++] = __binary_op(A.numx[k], BNullVal, false, true); } } //temp.cp[curnzc] = temp.cp[curnzc-1] + (A.cp[i] - A.cp[i-1]); temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } temp.Resize(curnzc, curnz); return temp; } // both A and B are non-NULL at this point const Dcsc & A = *Ap; const Dcsc & B = *Bp; IU estnzc = A.nzc + B.nzc; IU estnz = A.nz + B.nz; Dcsc temp(estnz, estnzc); IU curnzc = 0; IU curnz = 0; IU i = 0; IU j = 0; temp.cp[0] = 0; while(i< A.nzc && j B.jc[j]) { j++; if (allowANulls) { IU prevnz = curnz; temp.jc[curnzc++] = B.jc[j-1]; for(IU k = B.cp[j-1]; k< B.cp[j]; ++k) { if (do_op(ANullVal, B.numx[k], true, false)) { temp.ir[curnz] = B.ir[k]; temp.numx[curnz++] = __binary_op(ANullVal, B.numx[k], true, false); } } //temp.cp[curnzc] = temp.cp[curnzc-1] + (B.cp[j] - B.cp[j-1]); temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } } else if(A.jc[i] < B.jc[j]) { i++; if (allowBNulls) { IU prevnz = curnz; temp.jc[curnzc++] = A.jc[i-1]; for(IU k = A.cp[i-1]; k< A.cp[i]; k++) { if (do_op(A.numx[k], BNullVal, false, true)) { temp.ir[curnz] = A.ir[k]; temp.numx[curnz++] = __binary_op(A.numx[k], BNullVal, false, true); } } //temp.cp[curnzc] = temp.cp[curnzc-1] + (A.cp[i] - A.cp[i-1]); temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } } else { temp.jc[curnzc++] = A.jc[i]; IU ii = A.cp[i]; IU jj = B.cp[j]; IU prevnz = curnz; while (ii < A.cp[i+1] && jj < B.cp[j+1]) { if (A.ir[ii] < B.ir[jj]) { if (allowBNulls && do_op(A.numx[ii], BNullVal, false, true)) { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = __binary_op(A.numx[ii++], BNullVal, false, true); } else ii++; } else if (A.ir[ii] > B.ir[jj]) { if (allowANulls && do_op(ANullVal, B.numx[jj], true, false)) { temp.ir[curnz] = B.ir[jj]; temp.numx[curnz++] = __binary_op(ANullVal, B.numx[jj++], true, false); } else jj++; } else { if (allowIntersect && do_op(A.numx[ii], B.numx[jj], false, false)) { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = __binary_op(A.numx[ii++], B.numx[jj++], false, false); // might include zeros } else { ii++; jj++; } } } while (ii < A.cp[i+1]) { if (allowBNulls && do_op(A.numx[ii], BNullVal, false, true)) { temp.ir[curnz] = A.ir[ii]; temp.numx[curnz++] = __binary_op(A.numx[ii++], BNullVal, false, true); } else ii++; } while (jj < B.cp[j+1]) { if (allowANulls && do_op(ANullVal, B.numx[jj], true, false)) { temp.ir[curnz] = B.ir[jj]; temp.numx[curnz++] = __binary_op(ANullVal, B.numx[jj++], true, false); } else jj++; } temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; ++i; ++j; } } while(allowBNulls && i< A.nzc) // remaining A elements after B ran out { IU prevnz = curnz; temp.jc[curnzc++] = A.jc[i++]; for(IU k = A.cp[i-1]; k< A.cp[i]; ++k) { if (do_op(A.numx[k], BNullVal, false, true)) { temp.ir[curnz] = A.ir[k]; temp.numx[curnz++] = __binary_op(A.numx[k], BNullVal, false, true); } } //temp.cp[curnzc] = temp.cp[curnzc-1] + (A.cp[i] - A.cp[i-1]); temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } while(allowANulls && j < B.nzc) // remaining B elements after A ran out { IU prevnz = curnz; temp.jc[curnzc++] = B.jc[j++]; for(IU k = B.cp[j-1]; k< B.cp[j]; ++k) { if (do_op(ANullVal, B.numx[k], true, false)) { temp.ir[curnz] = B.ir[k]; temp.numx[curnz++] = __binary_op(ANullVal, B.numx[k], true, false); } } //temp.cp[curnzc] = temp.cp[curnzc-1] + (B.cp[j] - B.cp[j-1]); temp.cp[curnzc] = temp.cp[curnzc-1] + curnz-prevnz; } temp.Resize(curnzc, curnz); return temp; } template SpDCCols EWiseApply (const SpDCCols & A, const SpDCCols & B, _BinaryOperation __binary_op, _BinaryPredicate do_op, bool allowANulls, bool allowBNulls, const NU1& ANullVal, const NU2& BNullVal, const bool allowIntersect) { assert(A.m == B.m); assert(A.n == B.n); Dcsc * tdcsc = new Dcsc(EWiseApply(A.dcsc, B.dcsc, __binary_op, do_op, allowANulls, allowBNulls, ANullVal, BNullVal, allowIntersect)); return SpDCCols (A.m , A.n, tdcsc); } } #endif CombBLAS_beta_16_2/include/CombBLAS/ParFriends.h000644 000765 000024 00000243264 13271404146 022614 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _PAR_FRIENDS_H_ #define _PAR_FRIENDS_H_ #include "mpi.h" #include #include #include "SpParMat.h" #include "SpParHelper.h" #include "MPIType.h" #include "Friends.h" #include "OptBuf.h" #include "mtSpGEMM.h" #include "MultiwayMerge.h" namespace combblas { template class SpParMat; /*************************************************************************************************/ /**************************** FRIEND FUNCTIONS FOR PARALLEL CLASSES ******************************/ /*************************************************************************************************/ /** ** Concatenate all the FullyDistVec objects into a single one **/ template FullyDistVec Concatenate ( std::vector< FullyDistVec > & vecs) { if(vecs.size() < 1) { SpParHelper::Print("Warning: Nothing to concatenate, returning empty "); return FullyDistVec(); } else if (vecs.size() < 2) { return vecs[1]; } else { typename std::vector< FullyDistVec >::iterator it = vecs.begin(); std::shared_ptr commGridPtr = it->getcommgrid(); MPI_Comm World = commGridPtr->GetWorld(); IT nglen = it->TotalLength(); // new global length IT cumloclen = it->MyLocLength(); // existing cumulative local lengths ++it; for(; it != vecs.end(); ++it) { if(*(commGridPtr) != *(it->getcommgrid())) { SpParHelper::Print("Grids are not comparable for FullyDistVec::EWiseApply\n"); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } nglen += it->TotalLength(); cumloclen += it->MyLocLength(); } FullyDistVec ConCat (commGridPtr, nglen, NT()); int nprocs = commGridPtr->GetSize(); std::vector< std::vector< NT > > data(nprocs); std::vector< std::vector< IT > > inds(nprocs); IT gloffset = 0; for(it = vecs.begin(); it != vecs.end(); ++it) { IT loclen = it->LocArrSize(); for(IT i=0; i < loclen; ++i) { IT locind; IT loffset = it->LengthUntil(); int owner = ConCat.Owner(gloffset+loffset+i, locind); data[owner].push_back(it->arr[i]); inds[owner].push_back(locind); } gloffset += it->TotalLength(); } int * sendcnt = new int[nprocs]; int * sdispls = new int[nprocs]; for(int i=0; i(0)); NT * senddatabuf = new NT[cumloclen]; for(int i=0; i().swap(data[i]); // delete data vectors } NT * recvdatabuf = new NT[totrecv]; MPI_Alltoallv(senddatabuf, sendcnt, sdispls, MPIType(), recvdatabuf, recvcnt, rdispls, MPIType(), World); // send data delete [] senddatabuf; IT * sendindsbuf = new IT[cumloclen]; for(int i=0; i().swap(inds[i]); // delete inds vectors } IT * recvindsbuf = new IT[totrecv]; MPI_Alltoallv(sendindsbuf, sendcnt, sdispls, MPIType(), recvindsbuf, recvcnt, rdispls, MPIType(), World); // send new inds DeleteAll(sendindsbuf, sendcnt, sdispls); for(int i=0; i bool CheckSpGEMMCompliance(const MATRIXA & A, const MATRIXB & B) { if(A.getncol() != B.getnrow()) { std::ostringstream outs; outs << "Can not multiply, dimensions does not match"<< std::endl; outs << A.getncol() << " != " << B.getnrow() << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); return false; } if((void*) &A == (void*) &B) { std::ostringstream outs; outs << "Can not multiply, inputs alias (make a temporary copy of one of them first)"<< std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, MATRIXALIAS); return false; } return true; } // Combined logic for prune, recovery, and select template void MCLPruneRecoverySelect(SpParMat & A, NT hardThreshold, IT selectNum, IT recoverNum, NT recoverPct, int kselectVersion) { #ifdef TIMING double t0, t1; #endif // Prune and create a new pruned matrix SpParMat PrunedA = A.Prune(std::bind2nd(std::less_equal(), hardThreshold), false); // column-wise statistics of the pruned matrix FullyDistVec colSums = PrunedA.Reduce(Column, std::plus(), 0.0); FullyDistVec nnzPerColumn = PrunedA.Reduce(Column, std::plus(), 0.0, [](NT val){return 1.0;}); FullyDistVec pruneCols(A.getcommgrid(), A.getncol(), hardThreshold); PrunedA.FreeMemory(); // Check if we need recovery // columns with nnz < recoverNum (r) FullyDistSpVec recoverCols(nnzPerColumn, std::bind2nd(std::less(), recoverNum)); recoverCols = recoverPct; // columns with nnz < r AND sum < recoverPct (pct) recoverCols = EWiseApply(recoverCols, colSums, [](NT spval, NT dval){return spval;}, [](NT spval, NT dval){return dval < spval;}, false, NT()); IT nrecover = recoverCols.getnnz(); if(nrecover > 0) { #ifdef TIMING t0=MPI_Wtime(); #endif A.Kselect(recoverCols, recoverNum, kselectVersion); #ifdef TIMING t1=MPI_Wtime(); mcl_kselecttime += (t1-t0); #endif pruneCols.Set(recoverCols); #ifdef COMBBLAS_DEBUG std::ostringstream outs; outs << "Number of columns needing recovery: " << nrecover << std::endl; SpParHelper::Print(outs.str()); #endif } if(selectNum>0) { // remaining columns will be up for selection FullyDistSpVec selectCols = EWiseApply(recoverCols, colSums, [](NT spval, NT dval){return spval;}, [](NT spval, NT dval){return spval==-1;}, true, static_cast(-1)); selectCols = selectNum; selectCols = EWiseApply(selectCols, nnzPerColumn, [](NT spval, NT dval){return spval;}, [](NT spval, NT dval){return dval > spval;}, false, NT()); IT nselect = selectCols.getnnz(); if(nselect > 0 ) { #ifdef TIMING t0=MPI_Wtime(); #endif A.Kselect(selectCols, selectNum, kselectVersion); // PrunedA would also work #ifdef TIMING t1=MPI_Wtime(); mcl_kselecttime += (t1-t0); #endif pruneCols.Set(selectCols); #ifdef COMBBLAS_DEBUG std::ostringstream outs; outs << "Number of columns needing selection: " << nselect << std::endl; SpParHelper::Print(outs.str()); #endif #ifdef TIMING t0=MPI_Wtime(); #endif SpParMat selectedA = A.PruneColumn(pruneCols, std::less(), false); #ifdef TIMING t1=MPI_Wtime(); mcl_prunecolumntime += (t1-t0); #endif if(recoverNum>0 ) // recovery can be attempted after selection { FullyDistVec nnzPerColumn1 = selectedA.Reduce(Column, std::plus(), 0.0, [](NT val){return 1.0;}); FullyDistVec colSums1 = selectedA.Reduce(Column, std::plus(), 0.0); selectedA.FreeMemory(); // slected columns with nnz < recoverNum (r) selectCols = recoverNum; selectCols = EWiseApply(selectCols, nnzPerColumn1, [](NT spval, NT dval){return spval;}, [](NT spval, NT dval){return dval < spval;}, false, NT()); // selected columns with sum < recoverPct (pct) selectCols = recoverPct; selectCols = EWiseApply(selectCols, colSums1, [](NT spval, NT dval){return spval;}, [](NT spval, NT dval){return dval < spval;}, false, NT()); IT n_recovery_after_select = selectCols.getnnz(); if(n_recovery_after_select>0) { // mclExpandVector2 does it on the original vector // mclExpandVector1 does it one pruned vector #ifdef TIMING t0=MPI_Wtime(); #endif A.Kselect(selectCols, recoverNum, kselectVersion); // Kselect on PrunedA might give different result #ifdef TIMING t1=MPI_Wtime(); mcl_kselecttime += (t1-t0); #endif pruneCols.Set(selectCols); #ifdef COMBBLAS_DEBUG std::ostringstream outs1; outs1 << "Number of columns needing recovery after selection: " << nselect << std::endl; SpParHelper::Print(outs1.str()); #endif } } } } // final prune #ifdef TIMING t0=MPI_Wtime(); #endif A.PruneColumn(pruneCols, std::less(), true); #ifdef TIMING t1=MPI_Wtime(); mcl_prunecolumntime += (t1-t0); #endif // Add loops for empty columns if(recoverNum<=0 ) // if recoverNum>0, recovery would have added nonzeros in empty columns { FullyDistVec nnzPerColumnA = A.Reduce(Column, std::plus(), 0.0, [](NT val){return 1.0;}); FullyDistSpVec emptyColumns(nnzPerColumnA, std::bind2nd(std::equal_to(), 0.0)); emptyColumns = 1.00; //Ariful: We need a selective AddLoops function with a sparse vector //A.AddLoops(emptyColumns); } } /** * Broadcasts A multiple times (#phases) in order to save storage in the output * Only uses 1/phases of C memory if the threshold/max limits are proper */ template SpParMat MemEfficientSpGEMM (SpParMat & A, SpParMat & B, int phases, NUO hardThreshold, IU selectNum, IU recoverNum, NUO recoverPct, int kselectVersion, int64_t perProcessMemory) { int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(A.getncol() != B.getnrow()) { std::ostringstream outs; outs << "Can not multiply, dimensions does not match"<< std::endl; outs << A.getncol() << " != " << B.getnrow() << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); return SpParMat< IU,NUO,UDERO >(); } if(phases <1 || phases >= A.getncol()) { SpParHelper::Print("MemEfficientSpGEMM: The value of phases is too small or large. Resetting to 1.\n"); phases = 1; } int stages, dummy; // last two parameters of ProductGrid are ignored for Synch multiplication std::shared_ptr GridC = ProductGrid((A.commGrid).get(), (B.commGrid).get(), stages, dummy, dummy); if(perProcessMemory>0) // estimate the number of phases permitted by memory { int p; MPI_Comm World = GridC->GetWorld(); MPI_Comm_size(World,&p); int64_t perNNZMem_in = sizeof(IU)*2 + sizeof(NU1); int64_t perNNZMem_out = sizeof(IU)*2 + sizeof(NUO); // max nnz(A) in a porcess int64_t lannz = A.getlocalnnz(); int64_t gannz; MPI_Allreduce(&lannz, &gannz, 1, MPIType(), MPI_MAX, World); int64_t inputMem = gannz * perNNZMem_in * 4; // for four copies (two for SUMMA) // max nnz(A^2) stored by summa in a porcess int64_t asquareNNZ = EstPerProcessNnzSUMMA(A,B); int64_t asquareMem = asquareNNZ * perNNZMem_out * 2; // an extra copy in multiway merge and in selection/recovery step // estimate kselect memory int64_t d = ceil( (asquareNNZ * sqrt(p))/ B.getlocalcols() ); // average nnz per column in A^2 (it is an overestimate because asquareNNZ is estimated based on unmerged matrices) // this is equivalent to (asquareNNZ * p) / B.getcol() int64_t k = std::min(int64_t(std::max(selectNum, recoverNum)), d ); int64_t kselectmem = B.getlocalcols() * k * 8 * 3; // estimate output memory int64_t outputNNZ = (B.getlocalcols() * k)/sqrt(p); int64_t outputMem = outputNNZ * perNNZMem_in * 2; //inputMem + outputMem + asquareMem/phases + kselectmem/phases < memory int64_t remainingMem = perProcessMemory*1000000000 - inputMem - outputMem; if(remainingMem > 0) { phases = 1 + (asquareMem+kselectmem) / remainingMem; } if(myrank==0) { if(remainingMem < 0) { std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n Warning: input and output memory requirement is greater than per-process avaiable memory. Keeping phase to the value supplied at the command line. The program may go out of memory and crash! \n !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; } #ifdef SHOW_MEMORY_USAGE int64_t maxMemory = kselectmem/phases + inputMem + outputMem + asquareMem / phases; if(maxMemory>1000000000) std::cout << "phases: " << phases << ": per process memory: " << perProcessMemory << " GB asquareMem: " << asquareMem/1000000000.00 << " GB" << " inputMem: " << inputMem/1000000000.00 << " GB" << " outputMem: " << outputMem/1000000000.00 << " GB" << " kselectmem: " << kselectmem/1000000000.00 << " GB" << std::endl; else std::cout << "phases: " << phases << ": per process memory: " << perProcessMemory << " GB asquareMem: " << asquareMem/1000000.00 << " MB" << " inputMem: " << inputMem/1000000.00 << " MB" << " outputMem: " << outputMem/1000000.00 << " MB" << " kselectmem: " << kselectmem/1000000.00 << " MB" << std::endl; #endif } } IU C_m = A.spSeq->getnrow(); IU C_n = B.spSeq->getncol(); std::vector< UDERB > PiecesOfB; UDERB CopyB = *(B.spSeq); // we allow alias matrices as input because of this local copy CopyB.ColSplit(phases, PiecesOfB); // CopyB's memory is destroyed at this point MPI_Barrier(GridC->GetWorld()); IU ** ARecvSizes = SpHelper::allocate2D(UDERA::esscount, stages); IU ** BRecvSizes = SpHelper::allocate2D(UDERB::esscount, stages); SpParHelper::GetSetSizes( *(A.spSeq), ARecvSizes, (A.commGrid)->GetRowWorld()); // Remotely fetched matrices are stored as pointers UDERA * ARecv; UDERB * BRecv; std::vector< UDERO > toconcatenate; int Aself = (A.commGrid)->GetRankInProcRow(); int Bself = (B.commGrid)->GetRankInProcCol(); for(int p = 0; p< phases; ++p) { SpParHelper::GetSetSizes( PiecesOfB[p], BRecvSizes, (B.commGrid)->GetColWorld()); std::vector< SpTuples *> tomerge; for(int i = 0; i < stages; ++i) { std::vector ess; if(i == Aself) ARecv = A.spSeq; // shallow-copy else { ess.resize(UDERA::esscount); for(int j=0; j< UDERA::esscount; ++j) ess[j] = ARecvSizes[j][i]; // essentials of the ith matrix in this row ARecv = new UDERA(); // first, create the object } #ifdef TIMING double t0=MPI_Wtime(); #endif SpParHelper::BCastMatrix(GridC->GetRowWorld(), *ARecv, ess, i); // then, receive its elements #ifdef TIMING double t1=MPI_Wtime(); mcl_Abcasttime += (t1-t0); #endif ess.clear(); if(i == Bself) BRecv = &(PiecesOfB[p]); // shallow-copy else { ess.resize(UDERB::esscount); for(int j=0; j< UDERB::esscount; ++j) ess[j] = BRecvSizes[j][i]; BRecv = new UDERB(); } #ifdef TIMING double t2=MPI_Wtime(); #endif SpParHelper::BCastMatrix(GridC->GetColWorld(), *BRecv, ess, i); // then, receive its elements #ifdef TIMING double t3=MPI_Wtime(); mcl_Bbcasttime += (t3-t2); #endif #ifdef TIMING double t4=MPI_Wtime(); #endif SpTuples * C_cont = LocalSpGEMM(*ARecv, *BRecv,i != Aself, i != Bself); #ifdef TIMING double t5=MPI_Wtime(); mcl_localspgemmtime += (t5-t4); #endif if(!C_cont->isZero()) tomerge.push_back(C_cont); else delete C_cont; } // all stages executed #ifdef SHOW_MEMORY_USAGE int64_t gcnnz_unmerged, lcnnz_unmerged = 0; for(size_t i = 0; i < tomerge.size(); ++i) { lcnnz_unmerged += tomerge[i]->getnnz(); } MPI_Allreduce(&lcnnz_unmerged, &gcnnz_unmerged, 1, MPIType(), MPI_MAX, MPI_COMM_WORLD); int64_t summa_memory = gcnnz_unmerged*20;//(gannz*2 + phase_nnz + gcnnz_unmerged + gannz + gannz/phases) * 20; // last two for broadcasts if(myrank==0) { if(summa_memory>1000000000) std::cout << p+1 << ". unmerged: " << summa_memory/1000000000.00 << "GB " ; else std::cout << p+1 << ". unmerged: " << summa_memory/1000000.00 << " MB " ; } #endif #ifdef TIMING double t6=MPI_Wtime(); #endif //UDERO OnePieceOfC(MergeAll(tomerge, C_m, PiecesOfB[p].getncol(),true), false); // TODO: MultiwayMerge can directly return UDERO inorder to avoid the extra copy SpTuples * OnePieceOfC_tuples = MultiwayMerge(tomerge, C_m, PiecesOfB[p].getncol(),true); #ifdef SHOW_MEMORY_USAGE int64_t gcnnz_merged, lcnnz_merged ; lcnnz_merged = OnePieceOfC_tuples->getnnz(); MPI_Allreduce(&lcnnz_merged, &gcnnz_merged, 1, MPIType(), MPI_MAX, MPI_COMM_WORLD); // TODO: we can remove gcnnz_merged memory here because we don't need to concatenate anymore int64_t merge_memory = gcnnz_merged*2*20;//(gannz*2 + phase_nnz + gcnnz_unmerged + gcnnz_merged*2) * 20; if(myrank==0) { if(merge_memory>1000000000) std::cout << " merged: " << merge_memory/1000000000.00 << "GB " ; else std::cout << " merged: " << merge_memory/1000000.00 << " MB " ; } #endif #ifdef TIMING double t7=MPI_Wtime(); mcl_multiwaymergetime += (t7-t6); #endif UDERO * OnePieceOfC = new UDERO(* OnePieceOfC_tuples, false); delete OnePieceOfC_tuples; SpParMat OnePieceOfC_mat(OnePieceOfC, GridC); MCLPruneRecoverySelect(OnePieceOfC_mat, hardThreshold, selectNum, recoverNum, recoverPct, kselectVersion); #ifdef SHOW_MEMORY_USAGE int64_t gcnnz_pruned, lcnnz_pruned ; lcnnz_pruned = OnePieceOfC_mat.getlocalnnz(); MPI_Allreduce(&lcnnz_pruned, &gcnnz_pruned, 1, MPIType(), MPI_MAX, MPI_COMM_WORLD); // TODO: we can remove gcnnz_merged memory here because we don't need to concatenate anymore int64_t prune_memory = gcnnz_pruned*2*20;//(gannz*2 + phase_nnz + gcnnz_pruned*2) * 20 + kselectmem; // 3 extra copies of OnePieceOfC_mat, we can make it one extra copy! //phase_nnz += gcnnz_pruned; if(myrank==0) { if(prune_memory>1000000000) std::cout << "Prune: " << prune_memory/1000000000.00 << "GB " << std::endl ; else std::cout << "Prune: " << prune_memory/1000000.00 << " MB " << std::endl ; } #endif // ABAB: Change this to accept pointers to objects toconcatenate.push_back(OnePieceOfC_mat.seq()); } UDERO * C = new UDERO(0,C_m, C_n,0); C->ColConcatenate(toconcatenate); // ABAB: Change this to accept a vector of pointers to pointers to DER objects SpHelper::deallocate2D(ARecvSizes, UDERA::esscount); SpHelper::deallocate2D(BRecvSizes, UDERA::esscount); return SpParMat (C, GridC); } /** * Parallel C = A*B routine that uses a double buffered broadcasting scheme * @pre { Input matrices, A and B, should not alias } * Most memory efficient version available. Total stages: 2*sqrt(p) * Memory requirement during first sqrt(p) stages: <= (3/2)*(nnz(A)+nnz(B))+(1/2)*nnz(C) * Memory requirement during second sqrt(p) stages: <= nnz(A)+nnz(B)+nnz(C) * Final memory requirement: nnz(C) if clearA and clearB are true **/ template SpParMat Mult_AnXBn_DoubleBuff (SpParMat & A, SpParMat & B, bool clearA = false, bool clearB = false ) { if(!CheckSpGEMMCompliance(A,B) ) { return SpParMat< IU,NUO,UDERO >(); } int stages, dummy; // last two parameters of ProductGrid are ignored for Synch multiplication std::shared_ptr GridC = ProductGrid((A.commGrid).get(), (B.commGrid).get(), stages, dummy, dummy); IU C_m = A.spSeq->getnrow(); IU C_n = B.spSeq->getncol(); UDERA * A1seq = new UDERA(); UDERA * A2seq = new UDERA(); UDERB * B1seq = new UDERB(); UDERB * B2seq = new UDERB(); (A.spSeq)->Split( *A1seq, *A2seq); const_cast< UDERB* >(B.spSeq)->Transpose(); (B.spSeq)->Split( *B1seq, *B2seq); MPI_Barrier(GridC->GetWorld()); IU ** ARecvSizes = SpHelper::allocate2D(UDERA::esscount, stages); IU ** BRecvSizes = SpHelper::allocate2D(UDERB::esscount, stages); SpParHelper::GetSetSizes( *A1seq, ARecvSizes, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( *B1seq, BRecvSizes, (B.commGrid)->GetColWorld()); // Remotely fetched matrices are stored as pointers UDERA * ARecv; UDERB * BRecv; std::vector< SpTuples *> tomerge; int Aself = (A.commGrid)->GetRankInProcRow(); int Bself = (B.commGrid)->GetRankInProcCol(); for(int i = 0; i < stages; ++i) { std::vector ess; if(i == Aself) { ARecv = A1seq; // shallow-copy } else { ess.resize(UDERA::esscount); for(int j=0; j< UDERA::esscount; ++j) { ess[j] = ARecvSizes[j][i]; // essentials of the ith matrix in this row } ARecv = new UDERA(); // first, create the object } SpParHelper::BCastMatrix(GridC->GetRowWorld(), *ARecv, ess, i); // then, receive its elements ess.clear(); if(i == Bself) { BRecv = B1seq; // shallow-copy } else { ess.resize(UDERB::esscount); for(int j=0; j< UDERB::esscount; ++j) { ess[j] = BRecvSizes[j][i]; } BRecv = new UDERB(); } SpParHelper::BCastMatrix(GridC->GetColWorld(), *BRecv, ess, i); // then, receive its elements SpTuples * C_cont = MultiplyReturnTuples (*ARecv, *BRecv, // parameters themselves false, true, // transpose information (B is transposed) i != Aself, // 'delete A' condition i != Bself); // 'delete B' condition if(!C_cont->isZero()) tomerge.push_back(C_cont); else delete C_cont; } if(clearA) delete A1seq; if(clearB) delete B1seq; // Set the new dimensions SpParHelper::GetSetSizes( *A2seq, ARecvSizes, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( *B2seq, BRecvSizes, (B.commGrid)->GetColWorld()); // Start the second round for(int i = 0; i < stages; ++i) { std::vector ess; if(i == Aself) { ARecv = A2seq; // shallow-copy } else { ess.resize(UDERA::esscount); for(int j=0; j< UDERA::esscount; ++j) { ess[j] = ARecvSizes[j][i]; // essentials of the ith matrix in this row } ARecv = new UDERA(); // first, create the object } SpParHelper::BCastMatrix(GridC->GetRowWorld(), *ARecv, ess, i); // then, receive its elements ess.clear(); if(i == Bself) { BRecv = B2seq; // shallow-copy } else { ess.resize(UDERB::esscount); for(int j=0; j< UDERB::esscount; ++j) { ess[j] = BRecvSizes[j][i]; } BRecv = new UDERB(); } SpParHelper::BCastMatrix(GridC->GetColWorld(), *BRecv, ess, i); // then, receive its elements SpTuples * C_cont = MultiplyReturnTuples (*ARecv, *BRecv, // parameters themselves false, true, // transpose information (B is transposed) i != Aself, // 'delete A' condition i != Bself); // 'delete B' condition if(!C_cont->isZero()) tomerge.push_back(C_cont); else delete C_cont; } SpHelper::deallocate2D(ARecvSizes, UDERA::esscount); SpHelper::deallocate2D(BRecvSizes, UDERB::esscount); if(clearA) { delete A2seq; delete A.spSeq; A.spSeq = NULL; } else { (A.spSeq)->Merge(*A1seq, *A2seq); delete A1seq; delete A2seq; } if(clearB) { delete B2seq; delete B.spSeq; B.spSeq = NULL; } else { (B.spSeq)->Merge(*B1seq, *B2seq); delete B1seq; delete B2seq; const_cast< UDERB* >(B.spSeq)->Transpose(); // transpose back to original } UDERO * C = new UDERO(MergeAll(tomerge, C_m, C_n,true), false); return SpParMat (C, GridC); // return the result object } /** * Parallel A = B*C routine that uses only MPI-1 features * Relies on simple blocking broadcast * @pre { Input matrices, A and B, should not alias } **/ template SpParMat Mult_AnXBn_Synch (SpParMat & A, SpParMat & B, bool clearA = false, bool clearB = false ) { if(!CheckSpGEMMCompliance(A,B) ) { return SpParMat< IU,NUO,UDERO >(); } int stages, dummy; // last two parameters of ProductGrid are ignored for Synch multiplication std::shared_ptr GridC = ProductGrid((A.commGrid).get(), (B.commGrid).get(), stages, dummy, dummy); IU C_m = A.spSeq->getnrow(); IU C_n = B.spSeq->getncol(); //const_cast< UDERB* >(B.spSeq)->Transpose(); // do not transpose for colum-by-column multiplication MPI_Barrier(GridC->GetWorld()); IU ** ARecvSizes = SpHelper::allocate2D(UDERA::esscount, stages); IU ** BRecvSizes = SpHelper::allocate2D(UDERB::esscount, stages); SpParHelper::GetSetSizes( *(A.spSeq), ARecvSizes, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( *(B.spSeq), BRecvSizes, (B.commGrid)->GetColWorld()); // Remotely fetched matrices are stored as pointers UDERA * ARecv; UDERB * BRecv; std::vector< SpTuples *> tomerge; int Aself = (A.commGrid)->GetRankInProcRow(); int Bself = (B.commGrid)->GetRankInProcCol(); for(int i = 0; i < stages; ++i) { std::vector ess; if(i == Aself) { ARecv = A.spSeq; // shallow-copy } else { ess.resize(UDERA::esscount); for(int j=0; j< UDERA::esscount; ++j) { ess[j] = ARecvSizes[j][i]; // essentials of the ith matrix in this row } ARecv = new UDERA(); // first, create the object } SpParHelper::BCastMatrix(GridC->GetRowWorld(), *ARecv, ess, i); // then, receive its elements ess.clear(); if(i == Bself) { BRecv = B.spSeq; // shallow-copy } else { ess.resize(UDERB::esscount); for(int j=0; j< UDERB::esscount; ++j) { ess[j] = BRecvSizes[j][i]; } BRecv = new UDERB(); } SpParHelper::BCastMatrix(GridC->GetColWorld(), *BRecv, ess, i); // then, receive its elements /* // before activating this transpose B first SpTuples * C_cont = MultiplyReturnTuples (*ARecv, *BRecv, // parameters themselves false, true, // transpose information (B is transposed) i != Aself, // 'delete A' condition i != Bself); // 'delete B' condition */ SpTuples * C_cont = LocalSpGEMM (*ARecv, *BRecv, // parameters themselves i != Aself, // 'delete A' condition i != Bself); // 'delete B' condition if(!C_cont->isZero()) tomerge.push_back(C_cont); #ifdef COMBBLAS_DEBUG std::ostringstream outs; outs << i << "th SUMMA iteration"<< std::endl; SpParHelper::Print(outs.str()); #endif } if(clearA && A.spSeq != NULL) { delete A.spSeq; A.spSeq = NULL; } if(clearB && B.spSeq != NULL) { delete B.spSeq; B.spSeq = NULL; } SpHelper::deallocate2D(ARecvSizes, UDERA::esscount); SpHelper::deallocate2D(BRecvSizes, UDERB::esscount); //UDERO * C = new UDERO(MergeAll(tomerge, C_m, C_n,true), false); // First get the result in SpTuples, then convert to UDER // the last parameter to MergeAll deletes tomerge arrays SpTuples * C_tuples = MultiwayMerge(tomerge, C_m, C_n,true); UDERO * C = new UDERO(*C_tuples, false); //if(!clearB) // const_cast< UDERB* >(B.spSeq)->Transpose(); // transpose back to original return SpParMat (C, GridC); // return the result object } /** * Estimate the maximum nnz needed to store in a process from all stages of SUMMA before reduction * @pre { Input matrices, A and B, should not alias } **/ template int64_t EstPerProcessNnzSUMMA(SpParMat & A, SpParMat & B) { int64_t nnzC_SUMMA = 0; if(A.getncol() != B.getnrow()) { std::ostringstream outs; outs << "Can not multiply, dimensions does not match"<< std::endl; outs << A.getncol() << " != " << B.getnrow() << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); return nnzC_SUMMA; } int stages, dummy; // last two parameters of ProductGrid are ignored for Synch multiplication std::shared_ptr GridC = ProductGrid((A.commGrid).get(), (B.commGrid).get(), stages, dummy, dummy); MPI_Barrier(GridC->GetWorld()); IU ** ARecvSizes = SpHelper::allocate2D(UDERA::esscount, stages); IU ** BRecvSizes = SpHelper::allocate2D(UDERB::esscount, stages); SpParHelper::GetSetSizes( *(A.spSeq), ARecvSizes, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( *(B.spSeq), BRecvSizes, (B.commGrid)->GetColWorld()); // Remotely fetched matrices are stored as pointers UDERA * ARecv; UDERB * BRecv; int Aself = (A.commGrid)->GetRankInProcRow(); int Bself = (B.commGrid)->GetRankInProcCol(); for(int i = 0; i < stages; ++i) { std::vector ess; if(i == Aself) { ARecv = A.spSeq; // shallow-copy } else { ess.resize(UDERA::esscount); for(int j=0; j< UDERA::esscount; ++j) { ess[j] = ARecvSizes[j][i]; // essentials of the ith matrix in this row } ARecv = new UDERA(); // first, create the object } SpParHelper::BCastMatrix(GridC->GetRowWorld(), *ARecv, ess, i); // then, receive its elements ess.clear(); if(i == Bself) { BRecv = B.spSeq; // shallow-copy } else { ess.resize(UDERB::esscount); for(int j=0; j< UDERB::esscount; ++j) { ess[j] = BRecvSizes[j][i]; } BRecv = new UDERB(); } SpParHelper::BCastMatrix(GridC->GetColWorld(), *BRecv, ess, i); // then, receive its elements IU* colnnzC = estimateNNZ(*ARecv, *BRecv); IU nzc = BRecv->GetDCSC()->nzc; IU nnzC_stage = 0; #ifdef THREADED #pragma omp parallel for reduction (+:nnzC_stage) #endif for (IU k=0; k(), MPI_MAX, GridC->GetWorld()); return nnzC_SUMMA_max; } template void CheckSpMVCompliance(const MATRIX & A, const VECTOR & x) { if(A.getncol() != x.TotalLength()) { std::ostringstream outs; outs << "Can not multiply, dimensions does not match"<< std::endl; outs << A.getncol() << " != " << x.TotalLength() << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } if(! ( *(A.getcommgrid()) == *(x.getcommgrid())) ) { std::cout << "Grids are not comparable for SpMV" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } template FullyDistSpVec::T_promote> SpMV (const SpParMat & A, const FullyDistSpVec & x, bool indexisvalue, OptBuf::T_promote > & optbuf); template FullyDistSpVec::T_promote> SpMV (const SpParMat & A, const FullyDistSpVec & x, bool indexisvalue) { typedef typename promote_trait::T_promote T_promote; OptBuf optbuf = OptBuf(); return SpMV(A, x, indexisvalue, optbuf); } /** * Step 1 of the sparse SpMV algorithm * @param[in,out] trxlocnz, lenuntil,trxinds,trxnums { set or allocated } * @param[in] indexisvalue **/ template void TransposeVector(MPI_Comm & World, const FullyDistSpVec & x, int32_t & trxlocnz, IU & lenuntil, int32_t * & trxinds, NV * & trxnums, bool indexisvalue) { int32_t xlocnz = (int32_t) x.getlocnnz(); int32_t roffst = (int32_t) x.RowLenUntil(); // since trxinds is int32_t int32_t roffset; IU luntil = x.LengthUntil(); int diagneigh = x.commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&roffst, 1, MPIType(), diagneigh, TROST, &roffset, 1, MPIType(), diagneigh, TROST, World, &status); MPI_Sendrecv(&xlocnz, 1, MPIType(), diagneigh, TRNNZ, &trxlocnz, 1, MPIType(), diagneigh, TRNNZ, World, &status); MPI_Sendrecv(&luntil, 1, MPIType(), diagneigh, TRLUT, &lenuntil, 1, MPIType(), diagneigh, TRLUT, World, &status); // ABAB: Important observation is that local indices (given by x.ind) is 32-bit addressible // Copy them to 32 bit integers and transfer that to save 50% of off-node bandwidth trxinds = new int32_t[trxlocnz]; int32_t * temp_xind = new int32_t[xlocnz]; #ifdef THREADED #pragma omp parallel for #endif for(int i=0; i< xlocnz; ++i) temp_xind[i] = (int32_t) x.ind[i]; MPI_Sendrecv(temp_xind, xlocnz, MPIType(), diagneigh, TRI, trxinds, trxlocnz, MPIType(), diagneigh, TRI, World, &status); delete [] temp_xind; if(!indexisvalue) { trxnums = new NV[trxlocnz]; MPI_Sendrecv(const_cast(SpHelper::p2a(x.num)), xlocnz, MPIType(), diagneigh, TRX, trxnums, trxlocnz, MPIType(), diagneigh, TRX, World, &status); } std::transform(trxinds, trxinds+trxlocnz, trxinds, std::bind2nd(std::plus(), roffset)); // fullydist indexing (p pieces) -> matrix indexing (sqrt(p) pieces) } /** * Step 2 of the sparse SpMV algorithm * @param[in,out] trxinds, trxnums { deallocated } * @param[in,out] indacc, numacc { allocated } * @param[in,out] accnz { set } * @param[in] trxlocnz, lenuntil, indexisvalue **/ template void AllGatherVector(MPI_Comm & ColWorld, int trxlocnz, IU lenuntil, int32_t * & trxinds, NV * & trxnums, int32_t * & indacc, NV * & numacc, int & accnz, bool indexisvalue) { int colneighs, colrank; MPI_Comm_size(ColWorld, &colneighs); MPI_Comm_rank(ColWorld, &colrank); int * colnz = new int[colneighs]; colnz[colrank] = trxlocnz; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colnz, 1, MPI_INT, ColWorld); int * dpls = new int[colneighs](); // displacements (zero initialized pid) std::partial_sum(colnz, colnz+colneighs-1, dpls+1); accnz = std::accumulate(colnz, colnz+colneighs, 0); indacc = new int32_t[accnz]; numacc = new NV[accnz]; // ABAB: Future issues here, colnz is of type int (MPI limitation) // What if the aggregate vector size along the processor row/column is not 32-bit addressible? // This will happen when n/sqrt(p) > 2^31 // Currently we can solve a small problem (scale 32) with 4096 processor // For a medium problem (scale 35), we'll need 32K processors which gives sqrt(p) ~ 180 // 2^35 / 180 ~ 2^29 / 3 which is not an issue ! #ifdef TIMING double t0=MPI_Wtime(); #endif MPI_Allgatherv(trxinds, trxlocnz, MPIType(), indacc, colnz, dpls, MPIType(), ColWorld); delete [] trxinds; if(indexisvalue) { IU lenuntilcol; if(colrank == 0) lenuntilcol = lenuntil; MPI_Bcast(&lenuntilcol, 1, MPIType(), 0, ColWorld); for(int i=0; i< accnz; ++i) // fill numerical values from indices { numacc[i] = indacc[i] + lenuntilcol; } } else { MPI_Allgatherv(trxnums, trxlocnz, MPIType(), numacc, colnz, dpls, MPIType(), ColWorld); delete [] trxnums; } #ifdef TIMING double t1=MPI_Wtime(); cblas_allgathertime += (t1-t0); #endif DeleteAll(colnz,dpls); } /** * Step 3 of the sparse SpMV algorithm, with the semiring * @param[in,out] optbuf {scratch space for all-to-all (fold) communication} * @param[in,out] indacc, numacc {index and values of the input vector, deleted upon exit} * @param[in,out] sendindbuf, sendnumbuf {index and values of the output vector, created} **/ template void LocalSpMV(const SpParMat & A, int rowneighs, OptBuf & optbuf, int32_t * & indacc, IVT * & numacc, int32_t * & sendindbuf, OVT * & sendnumbuf, int * & sdispls, int * sendcnt, int accnz, bool indexisvalue, PreAllocatedSPA & SPA) { if(optbuf.totmax > 0) // graph500 optimization enabled { if(A.spSeq->getnsplit() > 0) { // optbuf.{inds/nums/dspls} and sendcnt are all pre-allocated and only filled by dcsc_gespmv_threaded generic_gespmv_threaded_setbuffers (*(A.spSeq), indacc, numacc, accnz, optbuf.inds, optbuf.nums, sendcnt, optbuf.dspls, rowneighs); } else { generic_gespmv (*(A.spSeq), indacc, numacc, accnz, optbuf.inds, optbuf.nums, sendcnt, optbuf.dspls, rowneighs, indexisvalue); } DeleteAll(indacc,numacc); } else { if(A.spSeq->getnsplit() > 0) { // sendindbuf/sendnumbuf/sdispls are all allocated and filled by dcsc_gespmv_threaded int totalsent = generic_gespmv_threaded (*(A.spSeq), indacc, numacc, accnz, sendindbuf, sendnumbuf, sdispls, rowneighs, SPA); DeleteAll(indacc, numacc); for(int i=0; i indy; std::vector< OVT > numy; generic_gespmv(*(A.spSeq), indacc, numacc, accnz, indy, numy, SPA); DeleteAll(indacc, numacc); int32_t bufsize = indy.size(); // as compact as possible sendindbuf = new int32_t[bufsize]; sendnumbuf = new OVT[bufsize]; int32_t perproc = A.getlocalrows() / rowneighs; int k = 0; // index to buffer for(int i=0; i void MergeContributions(int* listSizes, std::vector & indsvec, std::vector & numsvec, std::vector& mergedind, std::vector& mergednum) { int nlists = indsvec.size(); // this condition is checked in the caller SpMV function. // I am still putting it here for completeness if(nlists == 1) { // simply copy data int veclen = listSizes[0]; mergedind.resize(veclen); mergednum.resize(veclen); for(int i=0; i::min(); int32_t sup = std::numeric_limits::max(); KNHeap< int32_t, int32_t > sHeap(sup, inf); int * processed = new int[nlists](); for(int i=0; i 0) { // key, list_id sHeap.insert(indsvec[i][0], i); ++hsize; } } int32_t key, locv; if(hsize > 0) { sHeap.deleteMin(&key, &locv); mergedind.push_back( static_cast(key)); mergednum.push_back(numsvec[locv][0]); // nothing is processed yet if( (++(processed[locv])) < listSizes[locv] ) sHeap.insert(indsvec[locv][processed[locv]], locv); else --hsize; } while(hsize > 0) { sHeap.deleteMin(&key, &locv); if(mergedind.back() == static_cast(key)) { mergednum.back() = SR::add(mergednum.back(), numsvec[locv][processed[locv]]); // ABAB: Benchmark actually allows us to be non-deterministic in terms of parent selection // We can just skip this addition operator (if it's a max/min select) } else { mergedind.push_back(static_cast(key)); mergednum.push_back(numsvec[locv][processed[locv]]); } if( (++(processed[locv])) < listSizes[locv] ) sHeap.insert(indsvec[locv][processed[locv]], locv); else --hsize; } DeleteAll(processed); } template void MergeContributions_threaded(int * & listSizes, std::vector & indsvec, std::vector & numsvec, std::vector & mergedind, std::vector & mergednum, IU maxindex) { int nlists = indsvec.size(); // this condition is checked in the caller SpMV function. // I am still putting it here for completeness if(nlists == 1) { // simply copy data int veclen = listSizes[0]; mergedind.resize(veclen); mergednum.resize(veclen); #ifdef THREADED #pragma omp parallel for #endif for(int i=0; i > splitters(nlists); for(int k=0; k< nlists; k++) { splitters[k].resize(nsplits+1); splitters[k][0] = static_cast(0); #pragma omp parallel for for(int i=1; i< nsplits; i++) { IU cur_idx = i * (maxindex/nsplits); auto it = std::lower_bound (indsvec[k], indsvec[k] + listSizes[k], cur_idx); splitters[k][i] = (int32_t) (it - indsvec[k]); } splitters[k][nsplits] = listSizes[k]; } // ------ perform merge in parallel ------ std::vector> indsBuf(nsplits); std::vector> numsBuf(nsplits); //TODO: allocate these vectors here before calling MergeContributions #pragma omp parallel for schedule(dynamic) for(int i=0; i< nsplits; i++) { std::vector tIndsVec(nlists); std::vector tNumsVec(nlists); std::vector tLengths(nlists); for(int j=0; j< nlists; ++j) { tIndsVec[j] = indsvec[j] + splitters[j][i]; tNumsVec[j] = numsvec[j] + splitters[j][i]; tLengths[j]= splitters[j][i+1] - splitters[j][i]; } MergeContributions(tLengths.data(), tIndsVec, tNumsVec, indsBuf[i], numsBuf[i]); } // ------ concatenate merged tuples processed by threads ------ std::vector tdisp(nsplits+1); tdisp[0] = 0; for(int i=0; i void SpMV (const SpParMat & A, const FullyDistSpVec & x, FullyDistSpVec & y, bool indexisvalue, OptBuf & optbuf, PreAllocatedSPA & SPA) { CheckSpMVCompliance(A,x); optbuf.MarkEmpty(); y.glen = A.getnrow(); // in case it is not set already MPI_Comm World = x.commGrid->GetWorld(); MPI_Comm ColWorld = x.commGrid->GetColWorld(); MPI_Comm RowWorld = x.commGrid->GetRowWorld(); int accnz; int32_t trxlocnz; IU lenuntil; int32_t *trxinds, *indacc; IVT *trxnums, *numacc; #ifdef TIMING double t0=MPI_Wtime(); #endif TransposeVector(World, x, trxlocnz, lenuntil, trxinds, trxnums, indexisvalue); #ifdef TIMING double t1=MPI_Wtime(); cblas_transvectime += (t1-t0); #endif if(x.commGrid->GetGridRows() > 1) { AllGatherVector(ColWorld, trxlocnz, lenuntil, trxinds, trxnums, indacc, numacc, accnz, indexisvalue); // trxindS/trxnums deallocated, indacc/numacc allocated, accnz set } else { accnz = trxlocnz; indacc = trxinds; // aliasing ptr numacc = trxnums; // aliasing ptr } int rowneighs; MPI_Comm_size(RowWorld, &rowneighs); int * sendcnt = new int[rowneighs](); int32_t * sendindbuf; OVT * sendnumbuf; int * sdispls; #ifdef TIMING double t2=MPI_Wtime(); #endif LocalSpMV(A, rowneighs, optbuf, indacc, numacc, sendindbuf, sendnumbuf, sdispls, sendcnt, accnz, indexisvalue, SPA); // indacc/numacc deallocated, sendindbuf/sendnumbuf/sdispls allocated #ifdef TIMING double t3=MPI_Wtime(); cblas_localspmvtime += (t3-t2); #endif if(x.commGrid->GetGridCols() == 1) { y.ind.resize(sendcnt[0]); y.num.resize(sendcnt[0]); if(optbuf.totmax > 0 ) // graph500 optimization enabled { #ifdef THREADED #pragma omp parallel for #endif for(int i=0; i 0 ) // graph500 optimization enabled { MPI_Alltoallv(optbuf.inds, sendcnt, optbuf.dspls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), RowWorld); MPI_Alltoallv(optbuf.nums, sendcnt, optbuf.dspls, MPIType(), recvnumbuf, recvcnt, rdispls, MPIType(), RowWorld); delete [] sendcnt; } else { MPI_Alltoallv(sendindbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), RowWorld); MPI_Alltoallv(sendnumbuf, sendcnt, sdispls, MPIType(), recvnumbuf, recvcnt, rdispls, MPIType(), RowWorld); DeleteAll(sendindbuf, sendnumbuf, sendcnt, sdispls); } #ifdef TIMING double t5=MPI_Wtime(); cblas_alltoalltime += (t5-t4); #endif #ifdef TIMING double t6=MPI_Wtime(); #endif //MergeContributions(y,recvcnt, rdispls, recvindbuf, recvnumbuf, rowneighs); // free memory of y, in case it was aliased std::vector().swap(y.ind); std::vector().swap(y.num); std::vector indsvec(rowneighs); std::vector numsvec(rowneighs); #ifdef THREADED #pragma omp parallel for #endif for(int i=0; i(recvcnt, indsvec, numsvec, y.ind, y.num, y.MyLocLength()); #else MergeContributions(recvcnt, indsvec, numsvec, y.ind, y.num); #endif DeleteAll(recvcnt, rdispls,recvindbuf, recvnumbuf); #ifdef TIMING double t7=MPI_Wtime(); cblas_mergeconttime += (t7-t6); #endif } template void SpMV (const SpParMat & A, const FullyDistSpVec & x, FullyDistSpVec & y, bool indexisvalue, PreAllocatedSPA & SPA) { OptBuf< int32_t, OVT > optbuf = OptBuf< int32_t,OVT >(); SpMV(A, x, y, indexisvalue, optbuf, SPA); } template void SpMV (const SpParMat & A, const FullyDistSpVec & x, FullyDistSpVec & y, bool indexisvalue) { OptBuf< int32_t, OVT > optbuf = OptBuf< int32_t,OVT >(); PreAllocatedSPA SPA; SpMV(A, x, y, indexisvalue, optbuf, SPA); } template void SpMV (const SpParMat & A, const FullyDistSpVec & x, FullyDistSpVec & y, bool indexisvalue, OptBuf & optbuf) { PreAllocatedSPA SPA; SpMV(A, x, y, indexisvalue, optbuf, SPA); } /** * Automatic type promotion is ONLY done here, all the callee functions (in Friends.h and below) are initialized with the promoted type * If indexisvalues = true, then we do not need to transfer values for x (happens for BFS iterations with boolean matrices and integer rhs vectors) **/ template FullyDistSpVec::T_promote> SpMV (const SpParMat & A, const FullyDistSpVec & x, bool indexisvalue, OptBuf::T_promote > & optbuf) { typedef typename promote_trait::T_promote T_promote; FullyDistSpVec y ( x.getcommgrid(), A.getnrow()); // identity doesn't matter for sparse vectors SpMV(A, x, y, indexisvalue, optbuf); return y; } /** * Parallel dense SpMV **/ template FullyDistVec::T_promote> SpMV (const SpParMat & A, const FullyDistVec & x ) { typedef typename promote_trait::T_promote T_promote; CheckSpMVCompliance(A, x); MPI_Comm World = x.commGrid->GetWorld(); MPI_Comm ColWorld = x.commGrid->GetColWorld(); MPI_Comm RowWorld = x.commGrid->GetRowWorld(); int xsize = (int) x.LocArrSize(); int trxsize = 0; int diagneigh = x.commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&xsize, 1, MPI_INT, diagneigh, TRX, &trxsize, 1, MPI_INT, diagneigh, TRX, World, &status); NUV * trxnums = new NUV[trxsize]; MPI_Sendrecv(const_cast(SpHelper::p2a(x.arr)), xsize, MPIType(), diagneigh, TRX, trxnums, trxsize, MPIType(), diagneigh, TRX, World, &status); int colneighs, colrank; MPI_Comm_size(ColWorld, &colneighs); MPI_Comm_rank(ColWorld, &colrank); int * colsize = new int[colneighs]; colsize[colrank] = trxsize; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colsize, 1, MPI_INT, ColWorld); int * dpls = new int[colneighs](); // displacements (zero initialized pid) std::partial_sum(colsize, colsize+colneighs-1, dpls+1); int accsize = std::accumulate(colsize, colsize+colneighs, 0); NUV * numacc = new NUV[accsize]; MPI_Allgatherv(trxnums, trxsize, MPIType(), numacc, colsize, dpls, MPIType(), ColWorld); delete [] trxnums; // serial SpMV with dense vector T_promote id = SR::id(); IU ysize = A.getlocalrows(); T_promote * localy = new T_promote[ysize]; std::fill_n(localy, ysize, id); #ifdef THREADED dcsc_gespmv_threaded(*(A.spSeq), numacc, localy); #else dcsc_gespmv(*(A.spSeq), numacc, localy); #endif DeleteAll(numacc,colsize, dpls); // FullyDistVec(shared_ptr grid, IT globallen, NT initval, NT id) FullyDistVec y ( x.commGrid, A.getnrow(), id); int rowneighs; MPI_Comm_size(RowWorld, &rowneighs); IU begptr, endptr; for(int i=0; i< rowneighs; ++i) { begptr = y.RowLenUntil(i); if(i == rowneighs-1) { endptr = ysize; } else { endptr = y.RowLenUntil(i+1); } MPI_Reduce(localy+begptr, SpHelper::p2a(y.arr), endptr-begptr, MPIType(), SR::mpi_op(), i, RowWorld); } delete [] localy; return y; } /** * \TODO: Old version that is no longer considered optimal * Kept for legacy purposes * To be removed when other functionals are fully tested. **/ template FullyDistSpVec::T_promote> SpMV (const SpParMat & A, const FullyDistSpVec & x) { typedef typename promote_trait::T_promote T_promote; CheckSpMVCompliance(A, x); MPI_Comm World = x.commGrid->GetWorld(); MPI_Comm ColWorld = x.commGrid->GetColWorld(); MPI_Comm RowWorld = x.commGrid->GetRowWorld(); int xlocnz = (int) x.getlocnnz(); int trxlocnz = 0; int roffst = x.RowLenUntil(); int offset; int diagneigh = x.commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&xlocnz, 1, MPI_INT, diagneigh, TRX, &trxlocnz, 1, MPI_INT, diagneigh, TRX, World, &status); MPI_Sendrecv(&roffst, 1, MPI_INT, diagneigh, TROST, &offset, 1, MPI_INT, diagneigh, TROST, World, &status); IU * trxinds = new IU[trxlocnz]; NUV * trxnums = new NUV[trxlocnz]; MPI_Sendrecv(const_cast(SpHelper::p2a(x.ind)), xlocnz, MPIType(), diagneigh, TRX, trxinds, trxlocnz, MPIType(), diagneigh, TRX, World, &status); MPI_Sendrecv(const_cast(SpHelper::p2a(x.num)), xlocnz, MPIType(), diagneigh, TRX, trxnums, trxlocnz, MPIType(), diagneigh, TRX, World, &status); std::transform(trxinds, trxinds+trxlocnz, trxinds, std::bind2nd(std::plus(), offset)); // fullydist indexing (n pieces) -> matrix indexing (sqrt(p) pieces) int colneighs, colrank; MPI_Comm_size(ColWorld, &colneighs); MPI_Comm_rank(ColWorld, &colrank); int * colnz = new int[colneighs]; colnz[colrank] = trxlocnz; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colnz, 1, MPI_INT, ColWorld); int * dpls = new int[colneighs](); // displacements (zero initialized pid) std::partial_sum(colnz, colnz+colneighs-1, dpls+1); int accnz = std::accumulate(colnz, colnz+colneighs, 0); IU * indacc = new IU[accnz]; NUV * numacc = new NUV[accnz]; // ABAB: Future issues here, colnz is of type int (MPI limitation) // What if the aggregate vector size along the processor row/column is not 32-bit addressible? MPI_Allgatherv(trxinds, trxlocnz, MPIType(), indacc, colnz, dpls, MPIType(), ColWorld); MPI_Allgatherv(trxnums, trxlocnz, MPIType(), numacc, colnz, dpls, MPIType(), ColWorld); DeleteAll(trxinds, trxnums); // serial SpMV with sparse vector std::vector< int32_t > indy; std::vector< T_promote > numy; int32_t * tmpindacc = new int32_t[accnz]; for(int i=0; i< accnz; ++i) tmpindacc[i] = indacc[i]; delete [] indacc; dcsc_gespmv(*(A.spSeq), tmpindacc, numacc, accnz, indy, numy); // actual multiplication DeleteAll(tmpindacc, numacc); DeleteAll(colnz, dpls); FullyDistSpVec y ( x.commGrid, A.getnrow()); // identity doesn't matter for sparse vectors IU yintlen = y.MyRowLength(); int rowneighs; MPI_Comm_size(RowWorld,&rowneighs); std::vector< std::vector > sendind(rowneighs); std::vector< std::vector > sendnum(rowneighs); typename std::vector::size_type outnz = indy.size(); for(typename std::vector::size_type i=0; i< outnz; ++i) { IU locind; int rown = y.OwnerWithinRow(yintlen, static_cast(indy[i]), locind); sendind[rown].push_back(locind); sendnum[rown].push_back(numy[i]); } IU * sendindbuf = new IU[outnz]; T_promote * sendnumbuf = new T_promote[outnz]; int * sendcnt = new int[rowneighs]; int * sdispls = new int[rowneighs]; for(int i=0; i().swap(sendind[i]); } for(int i=0; i().swap(sendnum[i]); } MPI_Alltoallv(sendindbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), RowWorld); MPI_Alltoallv(sendnumbuf, sendcnt, sdispls, MPIType(), recvnumbuf, recvcnt, rdispls, MPIType(), RowWorld); DeleteAll(sendindbuf, sendnumbuf); DeleteAll(sendcnt, recvcnt, sdispls, rdispls); // define a SPA-like data structure IU ysize = y.MyLocLength(); T_promote * localy = new T_promote[ysize]; bool * isthere = new bool[ysize]; std::vector nzinds; // nonzero indices std::fill_n(isthere, ysize, false); for(int i=0; i< totrecv; ++i) { if(!isthere[recvindbuf[i]]) { localy[recvindbuf[i]] = recvnumbuf[i]; // initial assignment nzinds.push_back(recvindbuf[i]); isthere[recvindbuf[i]] = true; } else { localy[recvindbuf[i]] = SR::add(localy[recvindbuf[i]], recvnumbuf[i]); } } DeleteAll(isthere, recvindbuf, recvnumbuf); sort(nzinds.begin(), nzinds.end()); int nnzy = nzinds.size(); y.ind.resize(nnzy); y.num.resize(nnzy); for(int i=0; i< nnzy; ++i) { y.ind[i] = nzinds[i]; y.num[i] = localy[nzinds[i]]; } delete [] localy; return y; } template SpParMat::T_promote,typename promote_trait::T_promote> EWiseMult (const SpParMat & A, const SpParMat & B , bool exclude) { typedef typename promote_trait::T_promote N_promote; typedef typename promote_trait::T_promote DER_promote; if(*(A.commGrid) == *(B.commGrid)) { DER_promote * result = new DER_promote( EWiseMult(*(A.spSeq),*(B.spSeq),exclude) ); return SpParMat (result, A.commGrid); } else { std::cout << "Grids are not comparable elementwise multiplication" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); return SpParMat< IU,N_promote,DER_promote >(); } } template SpParMat EWiseApply (const SpParMat & A, const SpParMat & B, _BinaryOperation __binary_op, bool notB, const NU2& defaultBVal) { if(*(A.commGrid) == *(B.commGrid)) { RETDER * result = new RETDER( EWiseApply(*(A.spSeq),*(B.spSeq), __binary_op, notB, defaultBVal) ); return SpParMat (result, A.commGrid); } else { std::cout << "Grids are not comparable elementwise apply" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); return SpParMat< IU,RETT,RETDER >(); } } template SpParMat EWiseApply (const SpParMat & A, const SpParMat & B, _BinaryOperation __binary_op, _BinaryPredicate do_op, bool allowANulls, bool allowBNulls, const NU1& ANullVal, const NU2& BNullVal, const bool allowIntersect, const bool useExtendedBinOp) { if(*(A.commGrid) == *(B.commGrid)) { RETDER * result = new RETDER( EWiseApply(*(A.spSeq),*(B.spSeq), __binary_op, do_op, allowANulls, allowBNulls, ANullVal, BNullVal, allowIntersect) ); return SpParMat (result, A.commGrid); } else { std::cout << "Grids are not comparable elementwise apply" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); return SpParMat< IU,RETT,RETDER >(); } } // plain adapter template SpParMat EWiseApply (const SpParMat & A, const SpParMat & B, _BinaryOperation __binary_op, _BinaryPredicate do_op, bool allowANulls, bool allowBNulls, const NU1& ANullVal, const NU2& BNullVal, const bool allowIntersect = true) { return EWiseApply(A, B, EWiseExtToPlainAdapter(__binary_op), EWiseExtToPlainAdapter(do_op), allowANulls, allowBNulls, ANullVal, BNullVal, allowIntersect, true); } // end adapter /** * if exclude is true, then we prune all entries W[i] != zero from V * if exclude is false, then we perform a proper elementwise multiplication **/ template FullyDistSpVec::T_promote> EWiseMult (const FullyDistSpVec & V, const FullyDistVec & W , bool exclude, NU2 zero) { typedef typename promote_trait::T_promote T_promote; if(*(V.commGrid) == *(W.commGrid)) { FullyDistSpVec< IU, T_promote> Product(V.commGrid); if(V.glen != W.glen) { std::cerr << "Vector dimensions don't match for EWiseMult\n"; MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } else { Product.glen = V.glen; IU size= V.getlocnnz(); if(exclude) { #if defined(_OPENMP) && defined(CBLAS_EXPERIMENTAL) // not faster than serial int actual_splits = cblas_splits * 1; // 1 is the parallel slackness std::vector tlosizes (actual_splits, 0); std::vector < std::vector > tlinds(actual_splits); std::vector < std::vector > tlnums(actual_splits); IU tlsize = size / actual_splits; #pragma omp parallel for //schedule(dynamic, 1) for(IU t = 0; t < actual_splits; ++t) { IU tlbegin = t*tlsize; IU tlend = (t==actual_splits-1)? size : (t+1)*tlsize; for(IU i=tlbegin; i prefix_sum(actual_splits+1,0); std::partial_sum(tlosizes.begin(), tlosizes.end(), prefix_sum.begin()+1); Product.ind.resize(prefix_sum[actual_splits]); Product.num.resize(prefix_sum[actual_splits]); #pragma omp parallel for //schedule(dynamic, 1) for(IU t=0; t< actual_splits; ++t) { std::copy(tlinds[t].begin(), tlinds[t].end(), Product.ind.begin()+prefix_sum[t]); std::copy(tlnums[t].begin(), tlnums[t].end(), Product.num.begin()+prefix_sum[t]); } #else for(IU i=0; i(); } } /** Threaded EWiseApply. Only called internally from EWiseApply. **/ template FullyDistSpVec EWiseApply_threaded (const FullyDistSpVec & V, const FullyDistVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, NU1 Vzero, const bool useExtendedBinOp) { typedef RET T_promote; //typedef typename promote_trait::T_promote T_promote; if(*(V.commGrid) == *(W.commGrid)) { FullyDistSpVec< IU, T_promote> Product(V.commGrid); if(V.TotalLength() != W.TotalLength()) { std::ostringstream outs; outs << "Vector dimensions don't match (" << V.TotalLength() << " vs " << W.TotalLength() << ") for EWiseApply (short version)\n"; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } else { int nthreads=1; #ifdef _OPENMP #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif Product.glen = V.glen; IU size= W.LocArrSize(); IU spsize = V.getlocnnz(); // temporary result vectors per thread std::vector> tProductInd(nthreads); std::vector> tProductVal(nthreads); IU perthread; //chunk of tProductInd or tProductVal allocated to each thread if (allowVNulls) perthread = size/nthreads; else perthread = spsize/nthreads; #ifdef _OPENMP #pragma omp parallel #endif { int curthread = 0; #ifdef _OPENMP curthread = omp_get_thread_num(); #endif IU tStartIdx = perthread * curthread; IU tNextIdx = perthread * (curthread+1); if (allowVNulls) { if(curthread == nthreads-1) tNextIdx = size; // get sparse part for the current thread auto it = std::lower_bound (V.ind.begin(), V.ind.end(), tStartIdx); IU tSpIdx = (IU) std::distance(V.ind.begin(), it); // iterate over the dense vector for(IU tIdx=tStartIdx; tIdx < tNextIdx; ++tIdx) { if(tSpIdx < spsize && V.ind[tSpIdx] < tNextIdx && V.ind[tSpIdx] == tIdx) { if (_doOp(V.num[tSpIdx], W.arr[tIdx], false, false)) { tProductInd[curthread].push_back(tIdx); tProductVal[curthread].push_back (_binary_op(V.num[tSpIdx], W.arr[tIdx], false, false)); } tSpIdx++; } else { if (_doOp(Vzero, W.arr[tIdx], true, false)) { tProductInd[curthread].push_back(tIdx); tProductVal[curthread].push_back (_binary_op(Vzero, W.arr[tIdx], true, false)); } } } } else // iterate over the sparse vector { if(curthread == nthreads-1) tNextIdx = spsize; for(IU tSpIdx=tStartIdx; tSpIdx < tNextIdx; ++tSpIdx) { if (_doOp(V.num[tSpIdx], W.arr[V.ind[tSpIdx]], false, false)) { tProductInd[curthread].push_back( V.ind[tSpIdx]); tProductVal[curthread].push_back (_binary_op(V.num[tSpIdx], W.arr[V.ind[tSpIdx]], false, false)); } } } } std::vector tdisp(nthreads+1); tdisp[0] = 0; for(int i=0; i(); } } /** * Performs an arbitrary binary operation _binary_op on the corresponding elements of two vectors with the result stored in a return vector ret. * The binary operatiation is only performed if the binary predicate _doOp returns true for those elements. Otherwise the binary operation is not * performed and ret does not contain an element at that position. * More formally the operation is defined as: * if (_doOp(V[i], W[i])) * ret[i] = _binary_op(V[i], W[i]) * else * // ret[i] is not set * Hence _doOp can be used to implement a filter on either of the vectors. * * The above is only defined if both V[i] and W[i] exist (i.e. an intersection). To allow a union operation (ex. when V[i] doesn't exist but W[i] does) * the allowVNulls flag is set to true and the Vzero argument is used as the missing V[i] value. * * The type of each element of ret must not necessarily be related to the types of V or W, so the return type must be explicitly specified as a template parameter: * FullyDistSpVec r = EWiseApply(V, W, plus, retTrue, false, 0) **/ template FullyDistSpVec EWiseApply (const FullyDistSpVec & V, const FullyDistVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, NU1 Vzero, const bool useExtendedBinOp) { #ifdef _OPENMP return EWiseApply_threaded(V, W, _binary_op, _doOp, allowVNulls, Vzero, useExtendedBinOp); #else typedef RET T_promote; //typedef typename promote_trait::T_promote T_promote; if(*(V.commGrid) == *(W.commGrid)) { FullyDistSpVec< IU, T_promote> Product(V.commGrid); //FullyDistVec< IU, NU1> DV (V); // Ariful: I am not sure why it was there?? if(V.TotalLength() != W.TotalLength()) { std::ostringstream outs; outs << "Vector dimensions don't match (" << V.TotalLength() << " vs " << W.TotalLength() << ") for EWiseApply (short version)\n"; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } else { Product.glen = V.glen; IU size= W.LocArrSize(); IU spsize = V.getlocnnz(); IU sp_iter = 0; if (allowVNulls) { // iterate over the dense vector for(IU i=0; i(); } #endif } /** * Performs an arbitrary binary operation _binary_op on the corresponding elements of two vectors with the result stored in a return vector ret. * The binary operatiation is only performed if the binary predicate _doOp returns true for those elements. Otherwise the binary operation is not * performed and ret does not contain an element at that position. * More formally the operation is defined as: * if (_doOp(V[i], W[i])) * ret[i] = _binary_op(V[i], W[i]) * else * // ret[i] is not set * Hence _doOp can be used to implement a filter on either of the vectors. * * The above is only defined if both V[i] and W[i] exist (i.e. an intersection). To allow a union operation (ex. when V[i] doesn't exist but W[i] does) * the allowVNulls flag is set to true and the Vzero argument is used as the missing V[i] value. * !allowVNulls && !allowWNulls => intersection * !allowVNulls && allowWNulls => operate on all elements of V * allowVNulls && !allowWNulls => operate on all elements of W * allowVNulls && allowWNulls => union * * The type of each element of ret must not necessarily be related to the types of V or W, so the return type must be explicitly specified as a template parameter: * FullyDistSpVec r = EWiseApply(V, W, plus, ...) * For intersection, Vzero and Wzero are irrelevant * ABAB: \todo: Should allowIntersect be "false" for all SetDifference uses? **/ template FullyDistSpVec EWiseApply (const FullyDistSpVec & V, const FullyDistSpVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, bool allowWNulls, NU1 Vzero, NU2 Wzero, const bool allowIntersect, const bool useExtendedBinOp) { typedef RET T_promote; // typename promote_trait::T_promote T_promote; if(*(V.commGrid) == *(W.commGrid)) { FullyDistSpVec< IU, T_promote> Product(V.commGrid); if(V.glen != W.glen) { std::ostringstream outs; outs << "Vector dimensions don't match (" << V.glen << " vs " << W.glen << ") for EWiseApply (full version)\n"; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } else { Product.glen = V.glen; typename std::vector< IU >::const_iterator indV = V.ind.begin(); typename std::vector< NU1 >::const_iterator numV = V.num.begin(); typename std::vector< IU >::const_iterator indW = W.ind.begin(); typename std::vector< NU2 >::const_iterator numW = W.num.begin(); while (indV < V.ind.end() && indW < W.ind.end()) { if (*indV == *indW) { // overlap if (allowIntersect) { if (_doOp(*numV, *numW, false, false)) { Product.ind.push_back(*indV); Product.num.push_back(_binary_op(*numV, *numW, false, false)); } } indV++; numV++; indW++; numW++; } else if (*indV < *indW) { // V has value but W does not if (allowWNulls) { if (_doOp(*numV, Wzero, false, true)) { Product.ind.push_back(*indV); Product.num.push_back(_binary_op(*numV, Wzero, false, true)); } } indV++; numV++; } else //(*indV > *indW) { // W has value but V does not if (allowVNulls) { if (_doOp(Vzero, *numW, true, false)) { Product.ind.push_back(*indW); Product.num.push_back(_binary_op(Vzero, *numW, true, false)); } } indW++; numW++; } } // clean up while (allowWNulls && indV < V.ind.end()) { if (_doOp(*numV, Wzero, false, true)) { Product.ind.push_back(*indV); Product.num.push_back(_binary_op(*numV, Wzero, false, true)); } indV++; numV++; } while (allowVNulls && indW < W.ind.end()) { if (_doOp(Vzero, *numW, true, false)) { Product.ind.push_back(*indW); Product.num.push_back(_binary_op(Vzero, *numW, true, false)); } indW++; numW++; } } return Product; } else { std::cout << "Grids are not comparable for EWiseApply" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); return FullyDistSpVec< IU,T_promote>(); } } // plain callback versions template FullyDistSpVec EWiseApply (const FullyDistSpVec & V, const FullyDistVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, NU1 Vzero) { return EWiseApply(V, W, EWiseExtToPlainAdapter(_binary_op), EWiseExtToPlainAdapter(_doOp), allowVNulls, Vzero, true); } template FullyDistSpVec EWiseApply (const FullyDistSpVec & V, const FullyDistSpVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, bool allowWNulls, NU1 Vzero, NU2 Wzero, const bool allowIntersect = true) { return EWiseApply(V, W, EWiseExtToPlainAdapter(_binary_op), EWiseExtToPlainAdapter(_doOp), allowVNulls, allowWNulls, Vzero, Wzero, allowIntersect, true); } } #endif CombBLAS_beta_16_2/include/CombBLAS/csc.cpp000644 000765 000024 00000007410 13271404146 021651 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 11/15/2016 --------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2016, The Regents of the University of California 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. */ #include "csc.h" #include namespace combblas { // Constructing empty Csc objects (size = 0) are not allowed. template Csc::Csc (IT size, IT nCol): nz(size),n(nCol) { assert(size != 0 && n != 0); num = new NT[nz]; ir = new IT[nz]; jc = new IT[n+1]; } template Csc::Csc (const Csc & rhs): n(rhs.n), nz(rhs.nz) { if(nz > 0) { ir = new IT[nz]; num = new NT[nz]; std::copy(rhs.ir, rhs.ir+nz, ir); // copy(first, last, result) std::copy(rhs.num, rhs.num+nz, num); } jc = new IT[n+1]; std::copy(rhs.jc, rhs.jc+n+1, jc); } template Csc & Csc::operator= (const Csc & rhs) { if(this != &rhs) { if(nz > 0) // if the existing object is not empty { // make it empty delete [] num; delete [] ir; } delete [] jc; nz = rhs.nz; n = rhs.n; if(nz > 0) // if the copied object is not empty { ir = new IT[nz]; num = new NT[nz]; std::copy(rhs.ir, rhs.ir+nz, ir); std::copy(rhs.num, rhs.num+nz, num); } jc = new IT[n+1]; std::copy(rhs.jc, rhs.jc+n+1, jc); } return *this; } template Csc::~Csc() { if( nz > 0) { delete [] num; delete [] ir; } delete [] jc; } //! Does not change the dimension template void Csc::Resize(IT nsize) { if(nsize == nz) { // No need to do anything! return; } else if(nsize == 0) { delete [] num; delete [] ir; nz = 0; return; } NT * tmpnum = num; IT * tmpir = ir; num = new NT[nsize]; ir = new IT[nsize]; if(nsize > nz) // Grow it { std::copy(tmpir, tmpir + nz, ir); //copy all old elements std::copy(tmpnum, tmpnum + nz, num); } else // Shrink it { std::copy(tmpir, tmpir + nsize, ir); // copy only a portion of the old elements std::copy(tmpnum, tmpnum + nsize, num); } delete [] tmpnum; // delete the memory pointed by previous pointers delete [] tmpir; nz = nsize; } } CombBLAS_beta_16_2/include/CombBLAS/mmio.h000644 000765 000024 00000010042 13271404146 021502 0ustar00aydinbulucstaff000000 000000 /* * Matrix Market I/O library for ANSI C * * See http://math.nist.gov/MatrixMarket for details. * * */ #ifndef MM_IO_H #define MM_IO_H #define MM_MAX_LINE_LENGTH 1025 #define MatrixMarketBanner "%%MatrixMarket" #define MM_MAX_TOKEN_LENGTH 64 typedef char MM_typecode[4]; char *mm_typecode_to_str(MM_typecode matcode); int mm_read_banner(FILE *f, MM_typecode *matcode); int mm_read_mtx_crd_size(FILE *f, int64_t *M, int64_t *N, int64_t *nz, int64_t *numlinesread); // ABAB: Modified int mm_read_mtx_array_size(FILE *f, int *M, int *N); int mm_write_banner(FILE *f, MM_typecode matcode); int mm_write_mtx_crd_size(FILE *f, int M, int N, int nz); int mm_write_mtx_array_size(FILE *f, int M, int N); /********************* MM_typecode query fucntions ***************************/ #define mm_is_matrix(typecode) ((typecode)[0]=='M') #define mm_is_sparse(typecode) ((typecode)[1]=='C') #define mm_is_coordinate(typecode)((typecode)[1]=='C') #define mm_is_dense(typecode) ((typecode)[1]=='A') #define mm_is_array(typecode) ((typecode)[1]=='A') #define mm_is_complex(typecode) ((typecode)[2]=='C') #define mm_is_real(typecode) ((typecode)[2]=='R') #define mm_is_pattern(typecode) ((typecode)[2]=='P') #define mm_is_integer(typecode) ((typecode)[2]=='I') #define mm_is_symmetric(typecode)((typecode)[3]=='S') #define mm_is_general(typecode) ((typecode)[3]=='G') #define mm_is_skew(typecode) ((typecode)[3]=='K') #define mm_is_hermitian(typecode)((typecode)[3]=='H') int mm_is_valid(MM_typecode matcode); /* too complex for a macro */ /********************* MM_typecode modify fucntions ***************************/ #define mm_set_matrix(typecode) ((*typecode)[0]='M') #define mm_set_coordinate(typecode) ((*typecode)[1]='C') #define mm_set_array(typecode) ((*typecode)[1]='A') #define mm_set_dense(typecode) mm_set_array(typecode) #define mm_set_sparse(typecode) mm_set_coordinate(typecode) #define mm_set_complex(typecode)((*typecode)[2]='C') #define mm_set_real(typecode) ((*typecode)[2]='R') #define mm_set_pattern(typecode)((*typecode)[2]='P') #define mm_set_integer(typecode)((*typecode)[2]='I') #define mm_set_symmetric(typecode)((*typecode)[3]='S') #define mm_set_general(typecode)((*typecode)[3]='G') #define mm_set_skew(typecode) ((*typecode)[3]='K') #define mm_set_hermitian(typecode)((*typecode)[3]='H') #define mm_clear_typecode(typecode) ((*typecode)[0]=(*typecode)[1]= \ (*typecode)[2]=' ',(*typecode)[3]='G') #define mm_initialize_typecode(typecode) mm_clear_typecode(typecode) /********************* Matrix Market error codes ***************************/ #define MM_COULD_NOT_READ_FILE 11 #define MM_PREMATURE_EOF 12 #define MM_NOT_MTX 13 #define MM_NO_HEADER 14 #define MM_UNSUPPORTED_TYPE 15 #define MM_LINE_TOO_LONG 16 #define MM_COULD_NOT_WRITE_FILE 17 /******************** Matrix Market internal definitions ******************** MM_matrix_typecode: 4-character sequence ojbect sparse/ data storage dense type scheme string position: [0] [1] [2] [3] Matrix typecode: M(atrix) C(oord) R(eal) G(eneral) A(array) C(omplex) H(ermitian) P(attern) S(ymmetric) I(nteger) K(kew) ***********************************************************************/ #define MM_MTX_STR "matrix" #define MM_ARRAY_STR "array" #define MM_DENSE_STR "array" #define MM_COORDINATE_STR "coordinate" #define MM_SPARSE_STR "coordinate" #define MM_COMPLEX_STR "complex" #define MM_REAL_STR "real" #define MM_INT_STR "integer" #define MM_GENERAL_STR "general" #define MM_SYMM_STR "symmetric" #define MM_HERM_STR "hermitian" #define MM_SKEW_STR "skew-symmetric" #define MM_PATTERN_STR "pattern" /* high level routines */ int mm_write_mtx_crd(char fname[], int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode); int mm_read_mtx_crd_data(FILE *f, int M, int N, int nz, int I[], int J[], double val[], MM_typecode matcode); int mm_read_mtx_crd_entry(FILE *f, int *I, int *J, double *real, double *img, MM_typecode matcode); #endif CombBLAS_beta_16_2/include/CombBLAS/Operations.h000644 000765 000024 00000016564 13271404146 022703 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ /** * Operations used in parallel reductions and scans **/ #ifndef _OPERATIONS_H_ #define _OPERATIONS_H_ #include #include #include #include #include "psort/MersenneTwister.h" namespace combblas { template struct equal_first { bool operator()(std::pair & lhs, std::pair & rhs){ return lhs.first == rhs.first; } }; template struct myset: public std::unary_function { myset(T myvalue): value(myvalue) {}; /** @returns value regardless of x */ const T& operator()(const T& x) const { return value; } T value; }; template struct identity : public std::unary_function { /** @returns x itself */ const T operator()(const T& x) const { return x; } }; // Because identify reports ambiguity in PGI compilers template struct myidentity : public std::unary_function { /** @returns x itself */ const T operator()(const T& x) const { return x; } }; template struct totality : public std::unary_function { /** @returns true regardless */ bool operator()(const T& x) const { return true; } }; template struct safemultinv : public std::unary_function { const T operator()(const T& x) const { T inf = std::numeric_limits::max(); return (x == 0) ? inf:(1/x); } }; template struct sel2nd: public std::binary_function { const T& operator()(const T& x, const T & y) const { return y; } }; template struct bintotality : public std::binary_function { /** @returns true regardless */ bool operator()(const T1& x, const T2 & y) const { return true; } }; /** * binary_function * This is left untemplated because pow() only makes sense for * , , * and C++ can automatically upcast each case to */ struct exponentiate : public std::binary_function { double operator()(double x, double y) const { return std::pow(x, y); } }; /** * @brief Compute the maximum of two values. * * This binary function object computes the maximum of the two values * it is given. When used with MPI and a type @c T that has an * associated, built-in MPI data type, translates to @c MPI_MAX. */ template struct maximum : public std::binary_function { /** @returns the maximum of x and y. */ const T operator()(const T& x, const T& y) const { return x < y? y : x; } }; /** * @brief Compute the minimum of two values. * * This binary function object computes the minimum of the two values * it is given. When used with MPI and a type @c T that has an * associated, built-in MPI data type, translates to @c MPI_MIN. */ template struct minimum : public std::binary_function { /** @returns the minimum of x and y. */ const T operator()(const T& x, const T& y) const { return x < y? x : y; } }; /** * @brief With 50/50 chances, return a one of the operants */ template struct RandReduce : public std::binary_function { /** @returns the minimum of x and y. */ const T operator()(const T& x, const T& y) { return (M.rand() < 0.5)? x : y; } RandReduce() { #ifdef DETERMINISTIC M = MTRand(1); #else M = MTRand(); // generate random numbers with Mersenne Twister #endif } MTRand M; }; /** * @brief Returns a special value (passed to the constructor of the functor) when both operants disagree */ template struct SetIfNotEqual : public std::binary_function { const T operator()(const T& x, const T& y) { if(x != y) { return valuetoset; } else { return x; } } SetIfNotEqual(T value):valuetoset(value) { }; T valuetoset; }; /** * @brief Compute the bitwise AND of two integral values. * * This binary function object computes the bitwise AND of the two * values it is given. When used with MPI and a type @c T that has an * associated, built-in MPI data type, translates to @c MPI_BAND. */ template struct bitwise_and : public std::binary_function { /** @returns @c x & y. */ T operator()(const T& x, const T& y) const { return x & y; } }; /** * @brief Compute the bitwise OR of two integral values. * * This binary function object computes the bitwise OR of the two * values it is given. When used with MPI and a type @c T that has an * associated, built-in MPI data type, translates to @c MPI_BOR. */ template struct bitwise_or : public std::binary_function { /** @returns the @c x | y. */ T operator()(const T& x, const T& y) const { return x | y; } }; /** * @brief Compute the logical exclusive OR of two integral values. * * This binary function object computes the logical exclusive of the * two values it is given. When used with MPI and a type @c T that has * an associated, built-in MPI data type, translates to @c MPI_LXOR. */ template struct logical_xor : public std::binary_function { /** @returns the logical exclusive OR of x and y. */ T operator()(const T& x, const T& y) const { return (x || y) && !(x && y); } }; /** * @brief Compute the bitwise exclusive OR of two integral values. * * This binary function object computes the bitwise exclusive OR of * the two values it is given. When used with MPI and a type @c T that * has an associated, built-in MPI data type, translates to @c * MPI_BXOR. */ template struct bitwise_xor : public std::binary_function { /** @returns @c x ^ y. */ T operator()(const T& x, const T& y) const { return x ^ y; } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/VecIterator.cpp000644 000765 000024 00000000000 13271404146 023314 0ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/include/CombBLAS/MPIType.h000644 000765 000024 00000010554 13271404146 022040 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _MPI_TYPE_H #define _MPI_TYPE_H #include #include #include #include #include namespace combblas { // A datatype cache inspired by (mostly copied from) Boost http://www.boost.org/LICENSE_1_0.txt) /// @brief comparison function object for two std::type_info pointers /// is implemented using the before() member function of the std::type_info class struct type_info_compare { bool operator()(std::type_info const* lhs, std::type_info const* rhs) const { return lhs->before(*rhs); } }; class MPIDataTypeCache { private: typedef std::map stored_map_type; stored_map_type map; public: void clear() { int is_finalized=0; MPI_Finalized(&is_finalized); if (! is_finalized ) // do not free after call to MPI_FInalize { // ignore errors in the destructor for (stored_map_type::iterator it=map.begin(); it != map.end(); ++it) { MPI_Type_free(&(it->second)); } } } ~MPIDataTypeCache() { clear(); } MPI_Datatype get(const std::type_info* t) { stored_map_type::iterator pos = map.find(t); if (pos != map.end()) return pos->second; else return MPI_DATATYPE_NULL; } void set(const std::type_info* t, MPI_Datatype datatype) { map[t] = datatype; } }; /** * C++ type to MPIType conversion is done through functions returning the mpi types * The templated function is explicitly instantiated for every C++ type * that has a correspoinding MPI type. For all others, a data type is created * assuming it's some sort of struct. Each created data type is committed only once **/ extern MPIDataTypeCache mpidtc; // global variable // Global variables have program scope, which means they can be accessed everywhere in the program, and they are only destroyed when the program ends. template MPI_Datatype MPIType ( void ) { std::type_info const* t = &typeid(T); MPI_Datatype datatype = mpidtc.get(t); if (datatype == MPI_DATATYPE_NULL) { MPI_Type_contiguous(sizeof(T), MPI_CHAR, &datatype ); MPI_Type_commit(&datatype); int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); mpidtc.set(t, datatype); } return datatype; }; template<> MPI_Datatype MPIType< signed char >( void ); template<> MPI_Datatype MPIType< signed short int >( void ); template<> MPI_Datatype MPIType< unsigned char >( void ); template<> MPI_Datatype MPIType< unsigned short int >( void ); template<> MPI_Datatype MPIType< int32_t >( void ); template<> MPI_Datatype MPIType< uint32_t >( void ); template<> MPI_Datatype MPIType< int64_t >( void ); template<> MPI_Datatype MPIType< uint64_t >( void ); template<> MPI_Datatype MPIType< float >( void ); template<> MPI_Datatype MPIType< double >( void ); template<> MPI_Datatype MPIType< long double >( void ); template<> MPI_Datatype MPIType< bool >( void ); } #endif CombBLAS_beta_16_2/include/CombBLAS/MultiwayMerge.h000644 000765 000024 00000022157 13271404146 023346 0ustar00aydinbulucstaff000000 000000 #include "CombBLAS.h" namespace combblas { /*************************************************************************** * Find indices of column splitters in a list of tuple in parallel. * Inputs: * tuples: an array of SpTuples each tuple is (rowid, colid, val) * nsplits: number of splits requested * Output: * splitters: An array of size (nsplits+1) storing the starts and ends of split tuples. * different type used for output since we might need int or IT ***************************************************************************/ template std::vector findColSplitters(SpTuples * & spTuples, int nsplits) { std::vector splitters(nsplits+1); splitters[0] = static_cast(0); ColLexiCompare comp; #ifdef THREADED #pragma omp parallel for #endif for(int i=1; i< nsplits; i++) { IT cur_col = i * (spTuples->getncol()/nsplits); std::tuple search_tuple(0, cur_col, 0); std::tuple* it = std::lower_bound (spTuples->tuples, spTuples->tuples + spTuples->getnnz(), search_tuple, comp); splitters[i] = (RT) (it - spTuples->tuples); } splitters[nsplits] = spTuples->getnnz(); return splitters; } // Symbolic serial merge : only estimates nnz template IT SerialMergeNNZ( const std::vector *> & ArrSpTups) { int nlists = ArrSpTups.size(); ColLexiCompare heapcomp; std::vector> heap(nlists); std::vector curptr(nlists, static_cast(0)); IT hsize = 0; for(int i=0; i< nlists; ++i) { if(ArrSpTups[i]->getnnz()>0) { heap[hsize++] = std::make_tuple(std::get<0>(ArrSpTups[i]->tuples[0]), std::get<1>(ArrSpTups[i]->tuples[0]), i); } } std::make_heap(heap.data(), heap.data()+hsize, std::not2(heapcomp)); std::tuple curTuple; IT estnnz = 0; while(hsize > 0) { std::pop_heap(heap.data(), heap.data() + hsize, std::not2(heapcomp)); // result is stored in heap[hsize-1] int source = std::get<2>(heap[hsize-1]); if( (estnnz ==0) || (std::get<0>(curTuple) != std::get<0>(heap[hsize-1])) || (std::get<1>(curTuple) != std::get<1>(heap[hsize-1]))) { curTuple = ArrSpTups[source]->tuples[curptr[source]]; estnnz++; } curptr[source]++; if(curptr[source] != ArrSpTups[source]->getnnz()) // That array has not been depleted { heap[hsize-1] = std::make_tuple(std::get<0>(ArrSpTups[source]->tuples[curptr[source]]), std::get<1>(ArrSpTups[source]->tuples[curptr[source]]), source); std::push_heap(heap.data(), heap.data()+hsize, std::not2(heapcomp)); } else { --hsize; } } return estnnz; } /* "Internal function" called by MultiwayMerge inside threaded region. The merged list is stored in a preallocated buffer ntuples Never called from outside. Assumption1: the input lists are already column sorted Assumption2: at least two lists are passed to this function Assumption3: the input and output lists are to be deleted by caller */ template void SerialMerge( const std::vector *> & ArrSpTups, std::tuple * ntuples) { int nlists = ArrSpTups.size(); ColLexiCompare heapcomp; std::vector> heap(nlists); // if performance issue, create this outside of threaded region std::vector curptr(nlists, static_cast(0)); IT estnnz = 0; IT hsize = 0; for(int i=0; i< nlists; ++i) { if(ArrSpTups[i]->getnnz()>0) { estnnz += ArrSpTups[i]->getnnz(); heap[hsize++] = std::make_tuple(std::get<0>(ArrSpTups[i]->tuples[0]), std::get<1>(ArrSpTups[i]->tuples[0]), i); } } std::make_heap(heap.data(), heap.data()+hsize, std::not2(heapcomp)); IT cnz = 0; while(hsize > 0) { std::pop_heap(heap.data(), heap.data() + hsize, std::not2(heapcomp)); // result is stored in heap[hsize-1] int source = std::get<2>(heap[hsize-1]); if( (cnz != 0) && ((std::get<0>(ntuples[cnz-1]) == std::get<0>(heap[hsize-1])) && (std::get<1>(ntuples[cnz-1]) == std::get<1>(heap[hsize-1]))) ) { std::get<2>(ntuples[cnz-1]) = SR::add(std::get<2>(ntuples[cnz-1]), ArrSpTups[source]->numvalue(curptr[source]++)); } else { ntuples[cnz++] = ArrSpTups[source]->tuples[curptr[source]++]; } if(curptr[source] != ArrSpTups[source]->getnnz()) // That array has not been depleted { heap[hsize-1] = std::make_tuple(std::get<0>(ArrSpTups[source]->tuples[curptr[source]]), std::get<1>(ArrSpTups[source]->tuples[curptr[source]]), source); std::push_heap(heap.data(), heap.data()+hsize, std::not2(heapcomp)); } else { --hsize; } } } // Performs a balanced merge of the array of SpTuples // Assumes the input parameters are already column sorted template SpTuples* MultiwayMerge( std::vector *> & ArrSpTups, IT mdim = 0, IT ndim = 0, bool delarrs = false ) { int nlists = ArrSpTups.size(); if(nlists == 0) { return new SpTuples(0, mdim, ndim); //empty mxn SpTuples } if(nlists == 1) { if(delarrs) // steal data from input, and don't delete input { return ArrSpTups[0]; } else // copy input to output { std::tuple* mergeTups = static_cast*> (::operator new (sizeof(std::tuple[ArrSpTups[0]->getnnz()]))); #ifdef THREADED #pragma omp parallel for #endif for(int i=0; igetnnz(); i++) mergeTups[i] = ArrSpTups[0]->tuples[i]; return new SpTuples (ArrSpTups[0]->getnnz(), mdim, ndim, mergeTups, true); } } // ---- check correctness of input dimensions ------ for(int i=0; i< nlists; ++i) { if((mdim != ArrSpTups[i]->getnrow()) || ndim != ArrSpTups[i]->getncol()) { std::cerr << "Dimensions of SpTuples do not match on multiwayMerge()" << std::endl; return new SpTuples(0,0,0); } } int nthreads = 1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif int nsplits = 4*nthreads; // oversplit for load balance nsplits = std::min(nsplits, (int)ndim); // we cannot split a column std::vector< std::vector > colPtrs; for(int i=0; i< nlists; i++) { colPtrs.push_back(findColSplitters(ArrSpTups[i], nsplits)); // in parallel } std::vector mergedNnzPerSplit(nsplits); std::vector inputNnzPerSplit(nsplits); // ------ estimate memory requirement after merge in each split ------ #ifdef THREADED #pragma omp parallel for schedule(dynamic) #endif for(int i=0; i< nsplits; i++) // for each part { std::vector *> listSplitTups(nlists); IT t = static_cast(0); for(int j=0; j< nlists; ++j) { IT curnnz= colPtrs[j][i+1] - colPtrs[j][i]; listSplitTups[j] = new SpTuples (curnnz, mdim, ndim, ArrSpTups[j]->tuples + colPtrs[j][i], true); t += colPtrs[j][i+1] - colPtrs[j][i]; } mergedNnzPerSplit[i] = SerialMergeNNZ(listSplitTups); inputNnzPerSplit[i] = t; } std::vector mdisp(nsplits+1,0); for(int i=0; i(0)); double ratio = inputNnzAll / (double) mergedNnzAll; std::ostringstream outs; outs << "Multiwaymerge: inputNnz/mergedNnz = " << ratio << std::endl; SpParHelper::Print(outs.str()); #endif // ------ allocate memory outside of the parallel region ------ std::tuple * mergeBuf = static_cast*> (::operator new (sizeof(std::tuple[mergedNnzAll]))); // ------ perform merge in parallel ------ #ifdef THREADED #pragma omp parallel for schedule(dynamic) #endif for(int i=0; i< nsplits; i++) // serially merge part by part { std::vector *> listSplitTups(nlists); for(int j=0; j< nlists; ++j) { IT curnnz= colPtrs[j][i+1] - colPtrs[j][i]; listSplitTups[j] = new SpTuples (curnnz, mdim, ndim, ArrSpTups[j]->tuples + colPtrs[j][i], true); } SerialMerge(listSplitTups, mergeBuf + mdisp[i]); } for(int i=0; i< nlists; i++) { if(delarrs) delete ArrSpTups[i]; // May be expensive for large local matrices } return new SpTuples (mergedNnzAll, mdim, ndim, mergeBuf, true, true); } } CombBLAS_beta_16_2/include/CombBLAS/PBBS/000755 000765 000024 00000000000 13271404146 021121 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/include/CombBLAS/SpParHelper.cpp000644 000765 000024 00000103247 13271404146 023273 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include "usort/parUtils.h" namespace combblas { template void SpParHelper::ReDistributeToVector(int* & map_scnt, std::vector< std::vector< IT > > & locs_send, std::vector< std::vector< std::string > > & data_send, std::vector> & distmapper_array, const MPI_Comm & comm) { int nprocs, myrank; MPI_Comm_size(comm, &nprocs); MPI_Comm_rank(comm, &myrank); int * map_rcnt = new int[nprocs]; MPI_Alltoall(map_scnt, 1, MPI_INT, map_rcnt, 1, MPI_INT, comm); int * map_sdspl = new int[nprocs](); int * map_rdspl = new int[nprocs](); std::partial_sum(map_scnt, map_scnt+nprocs-1, map_sdspl+1); std::partial_sum(map_rcnt, map_rcnt+nprocs-1, map_rdspl+1); IT totmapsend = map_sdspl[nprocs-1] + map_scnt[nprocs-1]; IT totmaprecv = map_rdspl[nprocs-1] + map_rcnt[nprocs-1]; // sendbuf is a pointer to array of MAXVERTNAME chars. // Explicit grouping syntax is due to precedence of [] over * // char* sendbuf[MAXVERTNAME] would have declared a MAXVERTNAME-length array of char pointers char (*sendbuf)[MAXVERTNAME]; // each sendbuf[i] is type char[MAXVERTNAME] sendbuf = (char (*)[MAXVERTNAME]) malloc(sizeof(char[MAXVERTNAME])* totmapsend); // notice that this is allocating a contiguous block of memory IT * sendinds = new IT[totmapsend]; for(int i=0; i().swap(data_send[i]); // free memory } for(int i=0; i().swap(locs_send[i]); // free memory } char (*recvbuf)[MAXVERTNAME]; // recvbuf is of type char (*)[MAXVERTNAME] recvbuf = (char (*)[MAXVERTNAME]) malloc(sizeof(char[MAXVERTNAME])* totmaprecv); MPI_Datatype MPI_STRING; // this is not necessary (we could just use char) but easier for bookkeeping MPI_Type_contiguous(sizeof(char[MAXVERTNAME]), MPI_CHAR, &MPI_STRING); MPI_Type_commit(&MPI_STRING); MPI_Alltoallv(sendbuf, map_scnt, map_sdspl, MPI_STRING, recvbuf, map_rcnt, map_rdspl, MPI_STRING, comm); free(sendbuf); // can't delete[] so use free MPI_Type_free(&MPI_STRING); IT * recvinds = new IT[totmaprecv]; MPI_Alltoallv(sendinds, map_scnt, map_sdspl, MPIType(), recvinds, map_rcnt, map_rdspl, MPIType(), comm); DeleteAll(sendinds, map_scnt, map_sdspl, map_rcnt, map_rdspl); if(!std::is_sorted(recvinds, recvinds+totmaprecv)) std::cout << "Assertion failed at proc " << myrank << ": Received indices are not sorted, this is unexpected" << std::endl; for(IT i=0; i< totmaprecv; ++i) { assert(i == recvinds[i]); std::copy(recvbuf[i], recvbuf[i]+MAXVERTNAME, distmapper_array[i].begin()); } free(recvbuf); delete [] recvinds; } template void SpParHelper::MemoryEfficientPSort(std::pair * array, IT length, IT * dist, const MPI_Comm & comm) { int nprocs, myrank; MPI_Comm_size(comm, &nprocs); MPI_Comm_rank(comm, &myrank); int nsize = nprocs / 2; // new size if(nprocs < 10000) { bool excluded = false; if(dist[myrank] == 0) excluded = true; int nreals = 0; for(int i=0; i< nprocs; ++i) if(dist[i] != 0) ++nreals; //SpParHelper::MemoryEfficientPSort(vecpair, nnz, dist, World); if(nreals == nprocs) // general case { long * dist_in = new long[nprocs]; for(int i=0; i< nprocs; ++i) dist_in[i] = (long) dist[i]; vpsort::parallel_sort (array, array+length, dist_in, comm); delete [] dist_in; } else { long * dist_in = new long[nreals]; int * dist_out = new int[nprocs-nreals]; // ranks to exclude int indin = 0; int indout = 0; for(int i=0; i< nprocs; ++i) { if(dist[i] == 0) dist_out[indout++] = i; else dist_in[indin++] = (long) dist[i]; } #ifdef DEBUG std::ostringstream outs; outs << "To exclude indices: "; std::copy(dist_out, dist_out+indout, std::ostream_iterator(outs, " ")); outs << std::endl; SpParHelper::Print(outs.str()); #endif MPI_Group sort_group, real_group; MPI_Comm_group(comm, &sort_group); MPI_Group_excl(sort_group, indout, dist_out, &real_group); MPI_Group_free(&sort_group); // The Create() function should be executed by all processes in comm, // even if they do not belong to the new group (in that case MPI_COMM_NULL is returned as real_comm?) // MPI::Intracomm MPI::Intracomm::Create(const MPI::Group& group) const; MPI_Comm real_comm; MPI_Comm_create(comm, real_group, &real_comm); if(!excluded) { vpsort::parallel_sort (array, array+length, dist_in, real_comm); MPI_Comm_free(&real_comm); } MPI_Group_free(&real_group); delete [] dist_in; delete [] dist_out; } } else { IT gl_median = std::accumulate(dist, dist+nsize, static_cast(0)); // global rank of the first element of the median processor sort(array, array+length); // re-sort because we might have swapped data in previous iterations int color = (myrank < nsize)? 0: 1; std::pair * low = array; std::pair * upp = array; GlobalSelect(gl_median, low, upp, array, length, comm); BipartiteSwap(low, array, length, nsize, color, comm); if(color == 1) dist = dist + nsize; // adjust for the second half of processors // recursive call; two implicit 'spawn's where half of the processors execute different paramaters // MPI::Intracomm MPI::Intracomm::Split(int color, int key) const; MPI_Comm halfcomm; MPI_Comm_split(comm, color, myrank, &halfcomm); // split into two communicators MemoryEfficientPSort(array, length, dist, halfcomm); } } /* TODO: This function is just a hack at this moment. The payload (VAL) can only be integer at this moment. FIX this. */ template std::vector> SpParHelper::KeyValuePSort(std::pair * array, IT length, IT * dist, const MPI_Comm & comm) { int nprocs, myrank; MPI_Comm_size(comm, &nprocs); MPI_Comm_rank(comm, &myrank); int nsize = nprocs / 2; // new size bool excluded = false; if(dist[myrank] == 0) excluded = true; int nreals = 0; for(int i=0; i< nprocs; ++i) if(dist[i] != 0) ++nreals; std::vector> in(length); #ifdef THREADED #pragma omp parallel for #endif for(int i=0; i< length; ++i) { in[i] = IndexHolder(array[i].first, static_cast(array[i].second)); } if(nreals == nprocs) // general case { par::sampleSort(in, comm); } else { long * dist_in = new long[nreals]; int * dist_out = new int[nprocs-nreals]; // ranks to exclude int indin = 0; int indout = 0; for(int i=0; i< nprocs; ++i) { if(dist[i] == 0) dist_out[indout++] = i; else dist_in[indin++] = (long) dist[i]; } #ifdef DEBUG std::ostringstream outs; outs << "To exclude indices: "; std::copy(dist_out, dist_out+indout, std::ostream_iterator(outs, " ")); outs << std::endl; SpParHelper::Print(outs.str()); #endif MPI_Group sort_group, real_group; MPI_Comm_group(comm, &sort_group); MPI_Group_excl(sort_group, indout, dist_out, &real_group); MPI_Group_free(&sort_group); // The Create() function should be executed by all processes in comm, // even if they do not belong to the new group (in that case MPI_COMM_NULL is returned as real_comm?) // MPI::Intracomm MPI::Intracomm::Create(const MPI::Group& group) const; MPI_Comm real_comm; MPI_Comm_create(comm, real_group, &real_comm); if(!excluded) { par::sampleSort(in, real_comm); MPI_Comm_free(&real_comm); } MPI_Group_free(&real_group); delete [] dist_in; delete [] dist_out; } std::vector> sorted(in.size()); for(int i=0; i(in[i].index); sorted[i].first = in[i].value; } return sorted; } template void SpParHelper::GlobalSelect(IT gl_rank, std::pair * & low, std::pair * & upp, std::pair * array, IT length, const MPI_Comm & comm) { int nprocs, myrank; MPI_Comm_size(comm, &nprocs); MPI_Comm_rank(comm, &myrank); IT begin = 0; IT end = length; // initially everyone is active std::pair * wmminput = new std::pair[nprocs]; // (median, #{actives}) MPI_Datatype MPI_sortType; MPI_Type_contiguous (sizeof(std::pair), MPI_CHAR, &MPI_sortType); MPI_Type_commit (&MPI_sortType); KEY wmm; // our median pick IT gl_low, gl_upp; IT active = end-begin; // size of the active range IT nacts = 0; bool found = 0; int iters = 0; /* changes by shan : to add begin0 and end0 for exit condition */ IT begin0, end0; do { iters++; begin0 = begin; end0 = end; KEY median = array[(begin + end)/2].first; // median of the active range wmminput[myrank].first = median; wmminput[myrank].second = static_cast(active); MPI_Allgather(MPI_IN_PLACE, 0, MPI_sortType, wmminput, 1, MPI_sortType, comm); double totact = 0; // total number of active elements for(int i=0; i wmmpair = std::make_pair(wmm, VAL()); low =std::lower_bound (array+begin, array+end, wmmpair); upp =std::upper_bound (array+begin, array+end, wmmpair); IT loc_low = low-array; // #{elements smaller than wmm} IT loc_upp = upp-array; // #{elements smaller or equal to wmm} MPI_Allreduce( &loc_low, &gl_low, 1, MPIType(), MPI_SUM, comm); MPI_Allreduce( &loc_upp, &gl_upp, 1, MPIType(), MPI_SUM, comm); if(gl_upp < gl_rank) { // our pick was too small; only recurse to the right begin = (low - array); } else if(gl_rank < gl_low) { // our pick was too big; only recurse to the left end = (upp - array); } else { found = true; } active = end-begin; MPI_Allreduce(&active, &nacts, 1, MPIType(), MPI_SUM, comm); if (begin0 == begin && end0 == end) break; // ABAB: Active range did not shrink, so we break (is this kosher?) } while((nacts > 2*nprocs) && (!found)); delete [] wmminput; MPI_Datatype MPI_pairType; MPI_Type_contiguous (sizeof(std::pair), MPI_CHAR, &MPI_pairType); MPI_Type_commit (&MPI_pairType); int * nactives = new int[nprocs]; nactives[myrank] = static_cast(active); // At this point, actives are small enough MPI_Allgather(MPI_IN_PLACE, 0, MPI_INT, nactives, 1, MPI_INT, comm); int * dpls = new int[nprocs](); // displacements (zero initialized pid) std::partial_sum(nactives, nactives+nprocs-1, dpls+1); std::pair * recvbuf = new std::pair[nacts]; low = array + begin; // update low to the beginning of the active range MPI_Allgatherv(low, active, MPI_pairType, recvbuf, nactives, dpls, MPI_pairType, comm); std::pair * allactives = new std::pair[nacts]; int k = 0; for(int i=0; i(), MPI_SUM, comm); // update int diff = gl_rank - gl_low; for(int k=0; k < diff; ++k) { if(allactives[k].second == myrank) ++low; // increment the local pointer } delete [] allactives; begin = low-array; MPI_Allreduce(&begin, &gl_low, 1, MPIType(), MPI_SUM, comm); // update } template void SpParHelper::BipartiteSwap(std::pair * low, std::pair * array, IT length, int nfirsthalf, int color, const MPI_Comm & comm) { int nprocs, myrank; MPI_Comm_size(comm, &nprocs); MPI_Comm_rank(comm, &myrank); IT * firsthalves = new IT[nprocs]; IT * secondhalves = new IT[nprocs]; firsthalves[myrank] = low-array; secondhalves[myrank] = length - (low-array); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), firsthalves, 1, MPIType(), comm); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), secondhalves, 1, MPIType(), comm); int * sendcnt = new int[nprocs](); // zero initialize int totrecvcnt = 0; std::pair * bufbegin = NULL; if(color == 0) // first processor half, only send second half of data { bufbegin = low; totrecvcnt = length - (low-array); IT beg_oftransfer = std::accumulate(secondhalves, secondhalves+myrank, static_cast(0)); IT spaceafter = firsthalves[nfirsthalf]; int i=nfirsthalf+1; while(i < nprocs && spaceafter < beg_oftransfer) { spaceafter += firsthalves[i++]; // post-incremenet } IT end_oftransfer = beg_oftransfer + secondhalves[myrank]; // global index (within second half) of the end of my data IT beg_pour = beg_oftransfer; IT end_pour = std::min(end_oftransfer, spaceafter); sendcnt[i-1] = end_pour - beg_pour; while( i < nprocs && spaceafter < end_oftransfer ) // find other recipients until I run out of data { beg_pour = end_pour; spaceafter += firsthalves[i]; end_pour = std::min(end_oftransfer, spaceafter); sendcnt[i++] = end_pour - beg_pour; // post-increment } } else if(color == 1) // second processor half, only send first half of data { bufbegin = array; totrecvcnt = low-array; // global index (within the second processor half) of the beginning of my data IT beg_oftransfer = std::accumulate(firsthalves+nfirsthalf, firsthalves+myrank, static_cast(0)); IT spaceafter = secondhalves[0]; int i=1; while( i< nfirsthalf && spaceafter < beg_oftransfer) { //spacebefore = spaceafter; spaceafter += secondhalves[i++]; // post-increment } IT end_oftransfer = beg_oftransfer + firsthalves[myrank]; // global index (within second half) of the end of my data IT beg_pour = beg_oftransfer; IT end_pour = std::min(end_oftransfer, spaceafter); sendcnt[i-1] = end_pour - beg_pour; while( i < nfirsthalf && spaceafter < end_oftransfer ) // find other recipients until I run out of data { beg_pour = end_pour; spaceafter += secondhalves[i]; end_pour = std::min(end_oftransfer, spaceafter); sendcnt[i++] = end_pour - beg_pour; // post-increment } } DeleteAll(firsthalves, secondhalves); int * recvcnt = new int[nprocs]; MPI_Alltoall(sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, comm); // get the recv counts // Alltoall is actually unnecessary, because sendcnt = recvcnt // If I have n_mine > n_yours data to send, then I can send you only n_yours // as this is your space, and you'll send me identical amount. // Then I can only receive n_mine - n_yours from the third processor and // that processor can only send n_mine - n_yours to me back. // The proof follows from induction MPI_Datatype MPI_valueType; MPI_Type_contiguous(sizeof(std::pair), MPI_CHAR, &MPI_valueType); MPI_Type_commit(&MPI_valueType); std::pair * receives = new std::pair[totrecvcnt]; int * sdpls = new int[nprocs](); // displacements (zero initialized pid) int * rdpls = new int[nprocs](); std::partial_sum(sendcnt, sendcnt+nprocs-1, sdpls+1); std::partial_sum(recvcnt, recvcnt+nprocs-1, rdpls+1); MPI_Alltoallv(bufbegin, sendcnt, sdpls, MPI_valueType, receives, recvcnt, rdpls, MPI_valueType, comm); // sparse swap DeleteAll(sendcnt, recvcnt, sdpls, rdpls); std::copy(receives, receives+totrecvcnt, bufbegin); delete [] receives; } template void SpParHelper::DebugPrintKeys(std::pair * array, IT length, IT * dist, MPI_Comm & World) { int rank, nprocs; MPI_Comm_rank(World, &rank); MPI_Comm_size(World, &nprocs); MPI_File thefile; char _fn[] = "temp_sortedkeys"; // AL: this is to avoid the problem that C++ string literals are const char* while C string literals are char*, leading to a const warning (technically error, but compilers are tolerant) MPI_File_open(World, _fn, MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); // The cast in the last parameter is crucial because the signature of the function is // T accumulate ( InputIterator first, InputIterator last, T init ) // Hence if init if of type "int", the output also becomes an it (remember C++ signatures are ignorant of return value) IT sizeuntil = std::accumulate(dist, dist+rank, static_cast(0)); MPI_Offset disp = sizeuntil * sizeof(KEY); // displacement is in bytes MPI_File_set_view(thefile, disp, MPIType(), MPIType(), "native", MPI_INFO_NULL); KEY * packed = new KEY[length]; for(int i=0; i(), NULL); MPI_File_close(&thefile); delete [] packed; // Now let processor-0 read the file and print if(rank == 0) { FILE * f = fopen("temp_sortedkeys", "r"); if(!f) { std::cerr << "Problem reading binary input file\n"; return; } IT maxd = *std::max_element(dist, dist+nprocs); KEY * data = new KEY[maxd]; for(int i=0; i(std::cout, "\n")); } delete [] data; } } /** * @param[in,out] MRecv {an already existing, but empty SpMat<...> object} * @param[in] essentials {carries essential information (i.e. required array sizes) about ARecv} * @param[in] arrwin {windows array of size equal to the number of built-in arrays in the SpMat data structure} * @param[in] ownind {processor index (within this processor row/column) of the owner of the matrix to be received} * @remark {The communicator information is implicitly contained in the MPI::Win objects} **/ template void SpParHelper::FetchMatrix(SpMat & MRecv, const std::vector & essentials, std::vector & arrwin, int ownind) { MRecv.Create(essentials); // allocate memory for arrays Arr arrinfo = MRecv.GetArrays(); assert( (arrwin.size() == arrinfo.totalsize())); // C-binding for MPI::Get // int MPI_Get(void *origin_addr, int origin_count, MPI_Datatype origin_datatype, int target_rank, MPI_Aint target_disp, // int target_count, MPI_Datatype target_datatype, MPI_Win win) IT essk = 0; for(int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { //arrwin[essk].Lock(MPI::LOCK_SHARED, ownind, 0); MPI_Get( arrinfo.indarrs[i].addr, arrinfo.indarrs[i].count, MPIType(), ownind, 0, arrinfo.indarrs[i].count, MPIType(), arrwin[essk++]); } for(int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { //arrwin[essk].Lock(MPI::LOCK_SHARED, ownind, 0); MPI_Get(arrinfo.numarrs[i].addr, arrinfo.numarrs[i].count, MPIType(), ownind, 0, arrinfo.numarrs[i].count, MPIType(), arrwin[essk++]); } } /** * @param[in] Matrix {For the root processor, the local object to be sent to all others. * For all others, it is a (yet) empty object to be filled by the received data} * @param[in] essentials {irrelevant for the root} **/ template void SpParHelper::BCastMatrix(MPI_Comm & comm1d, SpMat & Matrix, const std::vector & essentials, int root) { int myrank; MPI_Comm_rank(comm1d, &myrank); if(myrank != root) { Matrix.Create(essentials); // allocate memory for arrays } Arr arrinfo = Matrix.GetArrays(); for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { MPI_Bcast(arrinfo.indarrs[i].addr, arrinfo.indarrs[i].count, MPIType(), root, comm1d); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { MPI_Bcast(arrinfo.numarrs[i].addr, arrinfo.numarrs[i].count, MPIType(), root, comm1d); } } /** * Just a test function to see the time to gather a matrix on an MPI process * The ultimate object would be to create the whole matrix on rank 0 (TODO) * @param[in] Matrix {For the root processor, the local object to be sent to all others. * For all others, it is a (yet) empty object to be filled by the received data} * @param[in] essentials {irrelevant for the root} **/ template void SpParHelper::GatherMatrix(MPI_Comm & comm1d, SpMat & Matrix, int root) { int myrank, nprocs; MPI_Comm_rank(comm1d, &myrank); MPI_Comm_size(comm1d,&nprocs); /* if(myrank != root) { Matrix.Create(essentials); // allocate memory for arrays } */ Arr arrinfo = Matrix.GetArrays(); std::vector> recvcnt_ind(arrinfo.indarrs.size()); std::vector> recvcnt_num(arrinfo.numarrs.size()); for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { recvcnt_ind[i].resize(nprocs); int lcount = (int)arrinfo.indarrs[i].count; MPI_Gather(&lcount, 1, MPI_INT, recvcnt_ind[i].data(),1, MPI_INT, root, comm1d); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { recvcnt_num[i].resize(nprocs); int lcount = (int) arrinfo.numarrs[i].count; MPI_Gather(&lcount, 1, MPI_INT, recvcnt_num[i].data(),1, MPI_INT, root, comm1d); } // now gather the actual vector std::vector> recvdsp_ind(arrinfo.indarrs.size()); std::vector> recvdsp_num(arrinfo.numarrs.size()); std::vector> recvind(arrinfo.indarrs.size()); std::vector> recvnum(arrinfo.numarrs.size()); for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { recvdsp_ind[i].resize(nprocs); recvdsp_ind[i][0] = 0; for(int j=1; j(), recvind[i].data(),recvcnt_ind[i].data(), recvdsp_ind[i].data(), MPIType(), root, comm1d); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // gather num arrays { recvdsp_num[i].resize(nprocs); recvdsp_num[i][0] = 0; for(int j=1; j(), recvnum[i].data(),recvcnt_num[i].data(), recvdsp_num[i].data(), MPIType(), root, comm1d); } } template void SpParHelper::SetWindows(MPI_Comm & comm1d, const SpMat< IT,NT,DER > & Matrix, std::vector & arrwin) { Arr arrs = Matrix.GetArrays(); // static MPI::Win MPI::Win::create(const void *base, MPI::Aint size, int disp_unit, MPI::Info info, const MPI_Comm & comm); // The displacement unit argument is provided to facilitate address arithmetic in RMA operations // *** COLLECTIVE OPERATION ***, everybody exposes its own array to everyone else in the communicator for(int i=0; i< arrs.indarrs.size(); ++i) { MPI_Win nWin; MPI_Win_create(arrs.indarrs[i].addr, arrs.indarrs[i].count * sizeof(IT), sizeof(IT), MPI_INFO_NULL, comm1d, &nWin); arrwin.push_back(nWin); } for(int i=0; i< arrs.numarrs.size(); ++i) { MPI_Win nWin; MPI_Win_create(arrs.numarrs[i].addr, arrs.numarrs[i].count * sizeof(NT), sizeof(NT), MPI_INFO_NULL, comm1d, &nWin); arrwin.push_back(nWin); } } inline void SpParHelper::LockWindows(int ownind, std::vector & arrwin) { for(std::vector::iterator itr = arrwin.begin(); itr != arrwin.end(); ++itr) { MPI_Win_lock(MPI_LOCK_SHARED, ownind, 0, *itr); } } inline void SpParHelper::UnlockWindows(int ownind, std::vector & arrwin) { for(std::vector::iterator itr = arrwin.begin(); itr != arrwin.end(); ++itr) { MPI_Win_unlock( ownind, *itr); } } /** * @param[in] owner {target processor rank within the processor group} * @param[in] arrwin {start access epoch only to owner's arrwin (-windows) } */ inline void SpParHelper::StartAccessEpoch(int owner, std::vector & arrwin, MPI_Group & group) { /* Now start using the whole comm as a group */ int acc_ranks[1]; acc_ranks[0] = owner; MPI_Group access; MPI_Group_incl(group, 1, acc_ranks, &access); // take only the owner // begin the ACCESS epochs for the arrays of the remote matrices A and B // Start() *may* block until all processes in the target group have entered their exposure epoch for(unsigned int i=0; i< arrwin.size(); ++i) MPI_Win_start(access, 0, arrwin[i]); MPI_Group_free(&access); } /** * @param[in] self {rank of "this" processor to be excluded when starting the exposure epoch} */ inline void SpParHelper::PostExposureEpoch(int self, std::vector & arrwin, MPI_Group & group) { // begin the EXPOSURE epochs for the arrays of the local matrices A and B for(unsigned int i=0; i< arrwin.size(); ++i) MPI_Win_post(group, MPI_MODE_NOPUT, arrwin[i]); } template void SpParHelper::AccessNFetch(DER * & Matrix, int owner, std::vector & arrwin, MPI_Group & group, IT ** sizes) { StartAccessEpoch(owner, arrwin, group); // start the access epoch to arrwin of owner std::vector ess(DER::esscount); // pack essentials to a vector for(int j=0; j< DER::esscount; ++j) ess[j] = sizes[j][owner]; Matrix = new DER(); // create the object first FetchMatrix(*Matrix, ess, arrwin, owner); // then start fetching its elements } template void SpParHelper::LockNFetch(DER * & Matrix, int owner, std::vector & arrwin, MPI_Group & group, IT ** sizes) { LockWindows(owner, arrwin); std::vector ess(DER::esscount); // pack essentials to a vector for(int j=0; j< DER::esscount; ++j) ess[j] = sizes[j][owner]; Matrix = new DER(); // create the object first FetchMatrix(*Matrix, ess, arrwin, owner); // then start fetching its elements } /** * @param[in] sizes 2D array where * sizes[i] is an array of size r/s representing the ith essential component of all local blocks within that row/col * sizes[i][j] is the size of the ith essential component of the jth local block within this row/col */ template void SpParHelper::GetSetSizes(const SpMat & Matrix, IT ** & sizes, MPI_Comm & comm1d) { std::vector essentials = Matrix.GetEssentials(); int index; MPI_Comm_rank(comm1d, &index); for(IT i=0; (unsigned)i < essentials.size(); ++i) { sizes[i][index] = essentials[i]; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), sizes[i], 1, MPIType(), comm1d); } } inline void SpParHelper::PrintFile(const std::string & s, const std::string & filename) { int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); if(myrank == 0) { std::ofstream out(filename.c_str(), std::ofstream::app); out << s; out.close(); } } inline void SpParHelper::PrintFile(const std::string & s, const std::string & filename, MPI_Comm & world) { int myrank; MPI_Comm_rank(world, &myrank); if(myrank == 0) { std::ofstream out(filename.c_str(), std::ofstream::app); out << s; out.close(); } } inline void SpParHelper::Print(const std::string & s) { int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); if(myrank == 0) { std::cerr << s; } } inline void SpParHelper::Print(const std::string & s, MPI_Comm & world) { int myrank; MPI_Comm_rank(world, &myrank); if(myrank == 0) { std::cerr << s; } } inline void SpParHelper::check_newline(int *bytes_read, int bytes_requested, char *buf) { if ((*bytes_read) < bytes_requested) { // fewer bytes than expected, this means EOF if (buf[(*bytes_read) - 1] != '\n') { // doesn't terminate with a newline, add one to prevent infinite loop later buf[(*bytes_read) - 1] = '\n'; std::cout << "Error in Matrix Market format, appending missing newline at end of file" << std::endl; (*bytes_read)++; } } } inline bool SpParHelper::FetchBatch(MPI_File & infile, MPI_Offset & curpos, MPI_Offset end_fpos, bool firstcall, std::vector & lines, int myrank) { size_t bytes2fetch = ONEMILLION; // we might read more than needed but no problem as we won't process them MPI_Status status; int bytes_read; if(firstcall && myrank != 0) { curpos -= 1; // first byte is to check whether we started at the beginning of a line bytes2fetch += 1; } char * buf = new char[bytes2fetch]; // needs to happen **after** bytes2fetch is updated char * originalbuf = buf; // so that we can delete it later because "buf" will move MPI_File_read_at(infile, curpos, buf, bytes2fetch, MPI_CHAR, &status); MPI_Get_count(&status, MPI_CHAR, &bytes_read); // MPI_Get_Count can only return 32-bit integers if(!bytes_read) { delete [] originalbuf; return true; // done } SpParHelper::check_newline(&bytes_read, bytes2fetch, buf); if(firstcall && myrank != 0) { if(buf[0] == '\n') // we got super lucky and hit the line break { buf += 1; bytes_read -= 1; curpos += 1; } else // skip to the next line and let the preceeding processor take care of this partial line { char *c = (char*)memchr(buf, '\n', MAXLINELENGTH); // return a pointer to the matching byte or NULL if the character does not occur if (c == NULL) { std::cout << "Unexpected line without a break" << std::endl; } int n = c - buf + 1; bytes_read -= n; buf += n; curpos += n; } } while(bytes_read > 0 && curpos < end_fpos) // this will also finish the last line { char *c = (char*)memchr(buf, '\n', bytes_read); // return a pointer to the matching byte or NULL if the character does not occur if (c == NULL) { delete [] originalbuf; return false; // if bytes_read stops in the middle of a line, that line will be re-read next time since curpos has not been moved forward yet } int n = c - buf + 1; // string constructor from char * buffer: copies the first n characters from the array of characters pointed by s lines.push_back(std::string(buf, n-1)); // no need to copy the newline character bytes_read -= n; // reduce remaining bytes buf += n; // move forward the buffer curpos += n; } delete [] originalbuf; if (curpos >= end_fpos) return true; // don't call it again, nothing left to read else return false; } inline void SpParHelper::WaitNFree(std::vector & arrwin) { // End the exposure epochs for the arrays of the local matrices A and B // The Wait() call matches calls to Complete() issued by ** EACH OF THE ORIGIN PROCESSES ** // that were granted access to the window during this epoch. for(unsigned int i=0; i< arrwin.size(); ++i) { MPI_Win_wait(arrwin[i]); } FreeWindows(arrwin); } inline void SpParHelper::FreeWindows(std::vector & arrwin) { for(unsigned int i=0; i< arrwin.size(); ++i) { MPI_Win_free(&arrwin[i]); } } } CombBLAS_beta_16_2/include/CombBLAS/FullyDistSpVec.h000644 000765 000024 00000037454 13271404146 023441 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _FULLY_DIST_SP_VEC_H_ #define _FULLY_DIST_SP_VEC_H_ #include #include #include #include "CommGrid.h" #include "promote.h" #include "SpParMat.h" #include "FullyDist.h" #include "Exception.h" #include "OptBuf.h" #include "CombBLAS.h" namespace combblas { template class SpParMat; template class DistEdgeList; template class FullyDistVec; template class SparseVectorLocalIterator; /** * A sparse vector of length n (with nnz <= n of them being nonzeros) is distributed to * "all the processors" in a way that "respects ordering" of the nonzero indices * Example: x = [5,1,6,2,9] for nnz(x)=5 and length(x)=12 * we use 4 processors P_00, P_01, P_10, P_11 * Then P_00 owns [1,2] (in the range [0,...,2]), P_01 ow`ns [5] (in the range [3,...,5]), and so on. * In the case of A(v,w) type sparse matrix indexing, this doesn't matter because n = nnz * After all, A(v,w) will have dimensions length(v) x length (w) * v and w will be of numerical type (NT) "int" and their indices (IT) will be consecutive integers * It is possibly that nonzero counts are distributed unevenly * Example: x=[1,2,3,4,5] and length(x) = 20, then P_00 would own all the nonzeros and the rest will hold empry vectors * Just like in SpParMat case, indices are local to processors (they belong to range [0,...,length-1] on each processor) * \warning Always create vectors with the right length, setting elements won't increase its length (similar to operator[] on std::vector) **/ template class FullyDistSpVec: public FullyDist::value, NT >::type> { public: FullyDistSpVec ( ); explicit FullyDistSpVec ( IT glen ); FullyDistSpVec ( std::shared_ptr grid); FullyDistSpVec ( std::shared_ptr grid, IT glen); template FullyDistSpVec (const FullyDistVec & rhs, _UnaryOperation unop); FullyDistSpVec (const FullyDistVec & rhs); // Conversion copy-constructor FullyDistSpVec (IT globalsize, const FullyDistVec & inds, const FullyDistVec & vals, bool SumDuplicates = false); FullyDistSpVec (std::shared_ptr grid, IT globallen, const std::vector& indvec, const std::vector & numvec, bool SumDuplicates = false, bool sorted=false); IT NnzUntil() const; FullyDistSpVec Invert (IT globallen); template FullyDistSpVec Invert (IT globallen, _BinaryOperationIdx __binopIdx, _BinaryOperationVal __binopVal, _BinaryOperationDuplicate __binopDuplicate); template FullyDistSpVec InvertRMA (IT globallen, _BinaryOperationIdx __binopIdx, _BinaryOperationVal __binopVal); template void Select (const FullyDistVec & denseVec, _UnaryOperation unop); template void FilterByVal (FullyDistSpVec Selector, _UnaryOperation __unop, bool filterByIndex); template void Setminus (const FullyDistSpVec & other); //template //void Set (FullyDistSpVec Selector, _UnaryOperation __unop); template void SelectApply (const FullyDistVec & denseVec, _UnaryOperation __unop, _BinaryOperation __binop); //! like operator=, but instead of making a deep copy it just steals the contents. //! Useful for places where the "victim" will be distroyed immediately after the call. void stealFrom(FullyDistSpVec & victim); FullyDistSpVec & operator=(const FullyDistSpVec< IT,NT > & rhs); FullyDistSpVec & operator=(const FullyDistVec< IT,NT > & rhs); // convert from dense FullyDistSpVec & operator=(NT fixedval) // assign fixed value { #ifdef _OPENMP #pragma omp parallel for #endif for(size_t i=0; i < ind.size(); ++i) num[i] = fixedval; return *this; } FullyDistSpVec & operator+=(const FullyDistSpVec & rhs); FullyDistSpVec & operator-=(const FullyDistSpVec & rhs); class ScalarReadSaveHandler { public: NT getNoNum(IT index) { return static_cast(1); } template NT read(std::basic_istream& is, IT index) { NT v; is >> v; return v; } template void save(std::basic_ostream& os, const NT& v, IT index) { os << v; } }; template void ParallelWrite(const std::string & filename, bool onebased, HANDLER handler, bool includeindices = true, bool includeheader = false); void ParallelWrite(const std::string & filename, bool onebased, bool includeindices = true) { ParallelWrite(filename, onebased, ScalarReadSaveHandler(), includeindices); }; template void ParallelRead (const std::string & filename, bool onebased, _BinaryOperation BinOp); //! Totally obsolete version that only accepts an ifstream object and ascii files template std::ifstream& ReadDistribute (std::ifstream& infile, int master, HANDLER handler); std::ifstream& ReadDistribute (std::ifstream& infile, int master) { return ReadDistribute(infile, master, ScalarReadSaveHandler()); } template void SaveGathered(std::ofstream& outfile, int master, HANDLER handler, bool printProcSplits = false); void SaveGathered(std::ofstream& outfile, int master) { SaveGathered(outfile, master, ScalarReadSaveHandler()); } template operator FullyDistSpVec< IT,NNT > () const //!< Type conversion operator { FullyDistSpVec CVT(commGrid); CVT.ind = std::vector(ind.begin(), ind.end()); CVT.num = std::vector(num.begin(), num.end()); CVT.glen = glen; return CVT; } bool operator==(const FullyDistSpVec & rhs) const { FullyDistVec v = *this; FullyDistVec w = rhs; return (v == w); } void PrintInfo(std::string vecname) const; void iota(IT globalsize, NT first); void nziota(NT first); FullyDistVec operator() (const FullyDistVec & ri) const; //!< SpRef (expects ri to be 0-based) void SetElement (IT indx, NT numx); // element-wise assignment void DelElement (IT indx); // element-wise deletion NT operator[](IT indx); bool WasFound() const { return wasFound; } //! sort the vector itself, return the permutation vector (0-based) FullyDistSpVec sort(); #if __cplusplus > 199711L template > FullyDistSpVec Uniq(_BinaryOperation __binary_op = _BinaryOperation(), MPI_Op mympiop = MPI_MIN); #else template FullyDistSpVec Uniq(_BinaryOperation __binary_op, MPI_Op mympiop); #endif IT getlocnnz() const { return ind.size(); } IT getnnz() const { IT totnnz = 0; IT locnnz = ind.size(); MPI_Allreduce( &locnnz, &totnnz, 1, MPIType(), MPI_SUM, commGrid->GetWorld()); return totnnz; } using FullyDist::value, NT >::type>::LengthUntil; using FullyDist::value, NT >::type>::MyLocLength; using FullyDist::value, NT >::type>::MyRowLength; using FullyDist::value, NT >::type>::TotalLength; using FullyDist::value, NT >::type>::Owner; using FullyDist::value, NT >::type>::RowLenUntil; void setNumToInd() { IT offset = LengthUntil(); IT spsize = ind.size(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i< spsize; ++i) num[i] = ind[i] + offset; } template IT Count(_Predicate pred) const; //!< Return the number of elements for which pred is true template void Apply(_UnaryOperation __unary_op) { //transform(num.begin(), num.end(), num.begin(), __unary_op); IT spsize = num.size(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i < spsize; ++i) num[i] = __unary_op(num[i]); } template void ApplyInd(_BinaryOperation __binary_op) { IT offset = LengthUntil(); IT spsize = ind.size(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i < spsize; ++i) num[i] = __binary_op(num[i], ind[i] + offset); } template NT Reduce(_BinaryOperation __binary_op, NT init) const; template OUT Reduce(_BinaryOperation __binary_op, OUT default_val, _UnaryOperation __unary_op) const; void DebugPrint(); std::shared_ptr getcommgrid() const { return commGrid; } void Reset(); NT GetLocalElement(IT indx); void BulkSet(IT inds[], int count); std::vector GetLocalInd (){std::vector rind = ind; return rind;}; std::vector GetLocalNum (){std::vector rnum = num; return rnum;}; template FullyDistVec FindInds(_Predicate pred) const; template FullyDistVec FindVals(_Predicate pred) const; protected: using FullyDist::value, NT >::type>::glen; using FullyDist::value, NT >::type>::commGrid; private: std::vector< IT > ind; // ind.size() give the number of nonzeros std::vector< NT > num; bool wasFound; // true if the last GetElement operation returned an actual value template void SparseCommon(std::vector< std::vector < std::pair > > & data, _BinaryOperation BinOp); #if __cplusplus > 199711L template > FullyDistSpVec UniqAll2All(_BinaryOperation __binary_op = _BinaryOperation(), MPI_Op mympiop = MPI_MIN); #else template FullyDistSpVec UniqAll2All(_BinaryOperation __binary_op, MPI_Op mympiop); #endif template friend class FullyDistSpVec; template friend class FullyDistVec; template friend class SpParMat; template friend class SparseVectorLocalIterator; template friend FullyDistSpVec::T_promote> SpMV (const SpParMat & A, const FullyDistSpVec & x ); template friend FullyDistSpVec::T_promote> SpMV (const SpParMat & A, const FullyDistSpVec & x, bool indexisvalue); template // NoSR version (in BFSFriends.h) friend FullyDistSpVec SpMV (const SpParMat & A, const FullyDistSpVec & x, OptBuf & optbuf); template friend void SpMV (const SpParMat & A, const FullyDistSpVec & x, FullyDistSpVec & y,bool indexisvalue, OptBuf & optbuf); template friend void SpMV (const SpParMat & A, const FullyDistSpVec & x, FullyDistSpVec & y,bool indexisvalue, OptBuf & optbuf, PreAllocatedSPA & SPA); template friend FullyDistSpVec::T_promote> EWiseMult (const FullyDistSpVec & V, const FullyDistVec & W , bool exclude, NU2 zero); template friend FullyDistSpVec EWiseApply (const FullyDistSpVec & V, const FullyDistVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, NU1 Vzero, const bool useExtendedBinOp); template friend FullyDistSpVec EWiseApply_threaded (const FullyDistSpVec & V, const FullyDistVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, NU1 Vzero, const bool useExtendedBinOp); template friend FullyDistSpVec EWiseApply (const FullyDistSpVec & V, const FullyDistSpVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, bool allowWNulls, NU1 Vzero, NU2 Wzero, const bool allowIntersect, const bool useExtendedBinOp); template friend void RandPerm(FullyDistSpVec & V); // called on an existing object, randomly permutes it template friend void RenameVertices(DistEdgeList & DEL); //! Helper functions for sparse matrix X sparse vector // Ariful: I made this an internal function in ParFriends.h //template //friend void MergeContributions(FullyDistSpVec & y, int * & recvcnt, int * & rdispls, int32_t * & recvindbuf, OVT * & recvnumbuf, int rowneighs); template friend void MergeContributions(FullyDistSpVec & y, int * & recvcnt, int * & rdispls, int32_t * & recvindbuf, VT * & recvnumbuf, int rowneighs); template friend void TransposeVector(MPI_Comm & World, const FullyDistSpVec & x, int32_t & trxlocnz, IU & lenuntil, int32_t * & trxinds, NV * & trxnums, bool indexisvalue); template friend SpParMat PermMat1 (const FullyDistSpVec & ri, const IU ncol, _UnaryOperation __unop); }; } #include "FullyDistSpVec.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/SpMat.h000644 000765 000024 00000015002 13271404146 021566 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SP_MAT_H #define _SP_MAT_H #include #include #include #include "CombBLAS.h" #include "SpDefs.h" #include "promote.h" #include "LocArr.h" namespace combblas { // Forward declaration (required since a friend function returns a SpTuples object) template class SpTuples; /** ** The abstract base class for all derived sequential sparse matrix classes ** Contains no data members, hence no copy constructor/assignment operator ** Uses static polymorphism through curiously recurring templates (CRTP) ** Template parameters: IT (index type), NT (numerical type), DER (derived class type) **/ template < class IT, class NT, class DER > class SpMat { public: //! Standard destructor, copy ctor and assignment are generated by compiler, they all do nothing ! //! Default constructor also exists, and does nothing more than creating Base() and Derived() objects //! One has to call one of the overloaded create functions to get an nonempty object void Create(const std::vector & essentials) { static_cast(this)->CreateImpl(essentials); } void Create(IT size, IT nRow, IT nCol, std::tuple * mytuples) { static_cast(this)->CreateImpl(size, nRow, nCol, mytuples); } SpMat< IT,NT,DER > operator() (const std::vector & ri, const std::vector & ci) const; template void SpGEMM( SpMat< IT,NT,DER > & A, SpMat< IT,NT,DER > & B, bool isAT, bool isBT); // ABAB: A semiring elementwise operation with automatic type promotion is required for completeness (should cover +/- and .* ?) // ABAB: A neat version of ConvertNumericType should be in base class (an operator SpMat()) void Split( SpMat< IT,NT,DER > & partA, SpMat< IT,NT,DER > & partB); void Merge( SpMat< IT,NT,DER > & partA, SpMat< IT,NT,DER > & partB); Arr GetArrays() const { return static_cast(this)->GetArrays(); } std::vector GetEssentials() const { return static_cast(this)->GetEssentials(); } auto GetInternal() const { return static_cast(this)->GetInternal(); } auto GetInternal(int i) const { return static_cast(this)->GetInternal(i); } int getnsplit() const // \TODO: Normalize the interface so that nsplit = 1 for serial cases { return static_cast(this)->getnsplit(); } void Transpose() { static_cast(this)->Transpose(); } auto begcol() // serial version { return static_cast(this)->begcol(); } auto endcol() //serial version { return static_cast(this)->endcol(); } auto begcol(int i) // multithreaded version { return static_cast(this)->begcol(i); } auto endcol(int i) //multithreaded version { return static_cast(this)->endcol(i); } template // <-- (requires C++0x to have a default) auto begnz(const typename X::SpColIter & ccol) //!< Return the beginning iterator for the nonzeros of the current column { return static_cast(this)->begnz(ccol); } template // <-- (requires C++0x to have a default) auto endnz(const typename X::SpColIter & ccol) //!< Return the ending iterator for the nonzeros of the current column { return static_cast(this)->endnz(ccol); } template // <-- (requires C++0x to have a default) auto begnz(const typename X::SpColIter & ccol, int i) //!< multithreaded version { return static_cast(this)->begnz(ccol, i); } template // <-- (requires C++0x to have a default) auto endnz(const typename X::SpColIter & ccol, int i) //!< multithreaded version { return static_cast(this)->endnz(ccol, i); } bool operator== (const SpMat< IT,NT,DER > & rhs) const; std::ofstream& put(std::ofstream& outfile) const; std::ifstream& get(std::ifstream& infile); bool isZero() const { return static_cast(this)->isZero(); } IT getnrow() const { return static_cast(this)->getnrow(); } IT getncol() const { return static_cast(this)->getncol(); } IT getnnz() const { return static_cast(this)->getnnz(); } protected: template < typename UIT, typename UNT, typename UDER > friend std::ofstream& operator<< (std::ofstream& outfile, const SpMat< UIT,UNT,UDER > & s); template < typename UIT, typename UNT, typename UDER > friend std::ifstream& operator>> (std::ifstream& infile, SpMat< UIT,UNT,UDER > & s); //! Returns a pointer to SpTuples, in order to avoid temporaries //! It is the caller's responsibility to delete the returned pointer afterwards template< class SR, class NUO, class IU, class NU1, class NU2, class DER1, class DER2 > friend SpTuples< IU, NUO > * MultiplyReturnTuples (const SpMat< IU, NU1, DER1 > & A, const SpMat< IU, NU2, DER2 > & B, bool isAT, bool isBT, bool clearA, bool clearB); }; } #include "SpMat.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/PreAllocatedSPA.h000644 000765 000024 00000020270 13271404146 023450 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _PRE_ALLOCATED_SPA_H #define _PRE_ALLOCATED_SPA_H #include "BitMap.h" namespace combblas { /** * This special data structure is used for optimizing BFS iterations * by providing a pre-allocated SPA data structure */ template // output value type class PreAllocatedSPA { public: PreAllocatedSPA():initialized(false) {}; // hide default constructor template PreAllocatedSPA(LMAT & A):initialized(true) // the one and only constructor { int64_t mA = A.getnrow(); if( A.getnsplit() > 0) // multithreaded { int64_t perpiece = mA / A.getnsplit(); for(int i=0; i(perpiece)); std::vector isthere(perpiece, false); for(auto colit = A.begcol(i); colit != A.endcol(i); ++colit) { for(auto nzit = A.begnz(colit,i); nzit != A.endnz(colit,i); ++nzit) { size_t rowid = nzit.rowid(); if(!isthere[rowid]) isthere[rowid] = true; } } size_t maxvector = std::count(isthere.begin(), isthere.end(), true); V_inds.push_back(std::vector(maxvector)); } else { V_isthere.push_back(BitMap(mA - i*perpiece)); V_localy.push_back(std::vector(mA - i*perpiece)); std::vector isthere(mA - i*perpiece, false); for(auto colit = A.begcol(i); colit != A.endcol(i); ++colit) { for(auto nzit = A.begnz(colit,i); nzit != A.endnz(colit,i); ++nzit) { size_t rowid = nzit.rowid(); if(!isthere[rowid]) isthere[rowid] = true; } } size_t maxvector = std::count(isthere.begin(), isthere.end(), true); V_inds.push_back(std::vector(maxvector)); } } } else // single threaded { V_isthere.push_back(BitMap(mA)); V_localy.push_back(std::vector(mA)); std::vector isthere(mA, false); for(auto colit = A.begcol(); colit != A.endcol(); ++colit) { for(auto nzit = A.begnz(colit); nzit != A.endnz(colit); ++nzit) { size_t rowid = nzit.rowid(); if(!isthere[rowid]) isthere[rowid] = true; } } size_t maxvector = std::count(isthere.begin(), isthere.end(), true); V_inds.push_back(std::vector(maxvector)); } }; // for manual splitting. just a hack. need to be fixed template PreAllocatedSPA(LMAT & A, int splits):initialized(true) { buckets = splits; int64_t mA = A.getnrow(); V_isthere.push_back(BitMap(mA)); V_localy.push_back(std::vector(mA)); V_inds.push_back(std::vector(mA)); // for better indexing among threads std::vector nnzSplitA(buckets,0); int32_t rowPerSplit = mA / splits; //per thread because writing vector is not thread safe for(int i=0; i(rowPerSplit)); V_isthereBool.push_back(std::vector(mA - (splits-1)*rowPerSplit)); //vector isthere(mA, false); for(auto colit = A.begcol(); colit != A.endcol(); ++colit) { for(auto nzit = A.begnz(colit); nzit != A.endnz(colit); ++nzit) { size_t rowid = nzit.rowid(); //if(!isthere[rowid]) isthere[rowid] = true; size_t splitId = (rowid/rowPerSplit > splits-1) ? splits-1 : rowid/rowPerSplit; nnzSplitA[splitId]++; } } // prefix sum disp.resize(splits+1); disp[0] = 0; for(int i=0; i void Init(LMAT & A, int splits) // not done for DCSC matrices with A.getnsplit() { if(!initialized) { initialized = true; buckets = splits; int64_t mA = A.getnrow(); V_isthere.push_back(BitMap(mA)); V_localy.push_back(std::vector(mA)); V_inds.push_back(std::vector(mA)); // for better indexing among threads std::vector nnzSplitA(buckets,0); int32_t rowPerSplit = mA / splits; for(int i=0; i(rowPerSplit)); V_isthereBool.push_back(std::vector(mA - (splits-1)*rowPerSplit)); //vector isthere(mA, false); for(auto colit = A.begcol(); colit != A.endcol(); ++colit) { for(auto nzit = A.begnz(colit); nzit != A.endnz(colit); ++nzit) { size_t rowid = nzit.rowid(); //if(!isthere[rowid]) isthere[rowid] = true; size_t splitId = (rowid/rowPerSplit > splits-1) ? splits-1 : rowid/rowPerSplit; nnzSplitA[splitId]++; } } // prefix sum disp.resize(splits+1); disp[0] = 0; for(int i=0; i > V_inds; // ABAB: is this big enough? std::vector< BitMap > V_isthere; std::vector< std::vector > V_isthereBool; // for thread safe access std::vector< std::vector > V_localy; bool initialized; std::vector indSplitA; std::vector numSplitA; std::vector disp; }; } #endif CombBLAS_beta_16_2/include/CombBLAS/SpParHelper.h000644 000765 000024 00000012553 13271404146 022737 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ /** * Functions that are used by multiple parallel matrix classes, but don't need the "this" pointer **/ #ifndef _SP_PAR_HELPER_H_ #define _SP_PAR_HELPER_H_ #include #include #include #include "LocArr.h" #include "CommGrid.h" #include "MPIType.h" #include "SpDefs.h" #include "psort/psort.h" namespace combblas { class SpParHelper { public: template static void ReDistributeToVector(int * & map_scnt, std::vector< std::vector< IT > > & locs_send, std::vector< std::vector< std::string > > & data_send, std::vector> & distmapper_array, const MPI_Comm & comm); template static void GlobalSelect(IT gl_rank, std::pair * & low, std::pair * & upp, std::pair * array, IT length, const MPI_Comm & comm); template static void BipartiteSwap(std::pair * low, std::pair * array, IT length, int nfirsthalf, int color, const MPI_Comm & comm); // Necessary because psort creates three 2D vectors of size p-by-p // One of those vector with 8 byte data uses 8*(4096)^2 = 128 MB space // Per processor extra storage becomes: // 24 MB with 1K processors // 96 MB with 2K processors // 384 MB with 4K processors // 1.5 GB with 8K processors template static void MemoryEfficientPSort(std::pair * array, IT length, IT * dist, const MPI_Comm & comm); template static std::vector> KeyValuePSort(std::pair * array, IT length, IT * dist, const MPI_Comm & comm); template static void DebugPrintKeys(std::pair * array, IT length, IT * dist, MPI_Comm & World); template static void FetchMatrix(SpMat & MRecv, const std::vector & essentials, std::vector & arrwin, int ownind); template static void BCastMatrix(MPI_Comm & comm1d, SpMat & Matrix, const std::vector & essentials, int root); template static void GatherMatrix(MPI_Comm & comm1d, SpMat & Matrix, int root); template static void SetWindows(MPI_Comm & comm1d, const SpMat< IT,NT,DER > & Matrix, std::vector & arrwin); template static void GetSetSizes(const SpMat & Matrix, IT ** & sizes, MPI_Comm & comm1d); template static void AccessNFetch(DER * & Matrix, int owner, std::vector & arrwin, MPI_Group & group, IT ** sizes); template static void LockNFetch(DER * & Matrix, int owner, std::vector & arrwin, MPI_Group & group, IT ** sizes); static void StartAccessEpoch(int owner, std::vector & arrwin, MPI_Group & group); static void PostExposureEpoch(int self, std::vector & arrwin, MPI_Group & group); static void LockWindows(int ownind, std::vector & arrwin); static void UnlockWindows(int ownind, std::vector & arrwin); static void Print(const std::string & s); static void Print(const std::string & s, MPI_Comm & world); static void PrintFile(const std::string & s, const std::string & filename); static void PrintFile(const std::string & s, const std::string & filename, MPI_Comm & world); static void check_newline(int *bytes_read, int bytes_requested, char *buf); static bool FetchBatch(MPI_File & infile, MPI_Offset & curpos, MPI_Offset end_fpos, bool firstcall, std::vector & lines, int myrank); static void WaitNFree(std::vector & arrwin); static void FreeWindows(std::vector & arrwin); }; } #include "SpParHelper.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/promote.h000644 000765 000024 00000007350 13271404146 022236 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _PROMOTE_H_ #define _PROMOTE_H_ #include "myenableif.h" namespace combblas { template struct promote_trait { }; // typename disable_if< is_boolean::value, NT >::type won't work, // because then it will send Enable=NT which is different from the default template parameter template struct promote_trait< NT , bool, typename combblas::disable_if< combblas::is_boolean::value >::type > { typedef NT T_promote; }; template struct promote_trait< bool , NT, typename combblas::disable_if< combblas::is_boolean::value >::type > { typedef NT T_promote; }; #define DECLARE_PROMOTE(A,B,C) \ template <> struct promote_trait \ { \ typedef C T_promote; \ }; DECLARE_PROMOTE(int64_t, bool, int64_t); DECLARE_PROMOTE(int64_t, int, int64_t); DECLARE_PROMOTE(bool, int64_t, int64_t); DECLARE_PROMOTE(int, int64_t, int64_t); DECLARE_PROMOTE(int64_t, int64_t, int64_t); DECLARE_PROMOTE(int, bool,int); DECLARE_PROMOTE(short, bool,short); DECLARE_PROMOTE(unsigned, bool, unsigned); DECLARE_PROMOTE(float, bool, float); DECLARE_PROMOTE(double, bool, double); DECLARE_PROMOTE(unsigned long long, bool, unsigned long long); DECLARE_PROMOTE(bool, int, int); DECLARE_PROMOTE(bool, short, short); DECLARE_PROMOTE(bool, unsigned, unsigned); DECLARE_PROMOTE(bool, float, float); DECLARE_PROMOTE(bool, double, double); DECLARE_PROMOTE(bool, unsigned long long, unsigned long long); DECLARE_PROMOTE(bool, bool, bool); DECLARE_PROMOTE(float, int, float); DECLARE_PROMOTE(double, int, double); DECLARE_PROMOTE(int, float, float); DECLARE_PROMOTE(int, double, double); DECLARE_PROMOTE(double, int64_t, double); DECLARE_PROMOTE(int64_t, double, double); DECLARE_PROMOTE(double, uint64_t, double); DECLARE_PROMOTE(uint64_t, double, double); DECLARE_PROMOTE(float, float, float); DECLARE_PROMOTE(double, double, double); DECLARE_PROMOTE(int, int, int); DECLARE_PROMOTE(unsigned, unsigned, unsigned); DECLARE_PROMOTE(unsigned long long, unsigned long long, unsigned long long); } #endif CombBLAS_beta_16_2/include/CombBLAS/MPIOp.h000644 000765 000024 00000010235 13271404146 021471 0ustar00aydinbulucstaff000000 000000 #ifndef _MPI_OP_H #define _MPI_OP_H #include #include #include #include #include #include #include "Operations.h" #include "MPIType.h" // type_info_compare definition namespace combblas { class MPIOpCache { private: typedef std::map stored_map_type; stored_map_type map; public: void clear() { int is_finalized=0; MPI_Finalized(&is_finalized); if (! is_finalized ) // do not free after call to MPI_FInalize { // ignore errors in the destructor for (stored_map_type::iterator it=map.begin(); it != map.end(); ++it) { MPI_Op_free(&(it->second)); } } } ~MPIOpCache() { clear(); } MPI_Op get(const std::type_info* t) { stored_map_type::iterator pos = map.find(t); if (pos != map.end()) return pos->second; else return MPI_OP_NULL; } void set(const std::type_info* t, MPI_Op datatype) { #ifdef NOTGNU if (map.find(t) != map.end()) map.erase(t); map.insert(std::make_pair(t, datatype)); #else map[t] = datatype; #endif } }; extern MPIOpCache mpioc; // global variable // MPIOp: A class that has a static op() function that takes no arguments and returns the corresponding MPI_Op // if and only if the given Op has a mapping to a valid MPI_Op // No concepts checking for the applicability of Op on the datatype T at the moment // In the future, this can be implemented via metafunction forwarding using mpl::or_ and mpl::bool_ template struct MPIOp { static void funcmpi(void * invec, void * inoutvec, int * len, MPI_Datatype *datatype) { Op myop; // you need to create the object instance T * pinvec = static_cast(invec); T * pinoutvec = static_cast(inoutvec); for (int i = 0; i < *len; i++) { pinoutvec[i] = myop(pinvec[i], pinoutvec[i]); } } static MPI_Op op() { std::type_info const* t = &typeid(Op); MPI_Op foundop = mpioc.get(t); if (foundop == MPI_OP_NULL) { MPI_Op_create(funcmpi, false, &foundop); int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); if(myrank == 0) std::cout << "Creating a new MPI Op for " << t->name() << std::endl; mpioc.set(t, foundop); } return foundop; } }; template struct MPIOp< maximum,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_MAX; } }; template struct MPIOp< minimum,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_MIN; } }; template struct MPIOp< std::plus,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_SUM; } }; template struct MPIOp< std::multiplies,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_PROD; } }; template struct MPIOp< std::logical_and,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_LAND; } }; template struct MPIOp< std::logical_or,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_LOR; } }; template struct MPIOp< logical_xor,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_LXOR; } }; template struct MPIOp< bitwise_and,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_BAND; } }; template struct MPIOp< bitwise_or,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_BOR; } }; template struct MPIOp< bitwise_xor,T,typename std::enable_if::value, void>::type > { static MPI_Op op() { return MPI_BXOR; } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/RefGen21.h000644 000765 000024 00000026341 13271404146 022063 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ /** * Deterministic vertex scrambling functions from V2.1 of the reference implementation **/ #ifndef _REF_GEN_2_1_H_ #define _REF_GEN_2_1_H_ #ifdef _STDINT_H #undef _STDINT_H #endif #ifdef _GCC_STDINT_H // for cray #undef _GCC_STDINT_H // original stdint does #include_next<"/opt/gcc/4.5.2/snos/lib/gcc/x86_64-suse-linux/4.5.2/include/stdint-gcc.h"> #endif #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #include #include #include #include #include "SpDefs.h" #include "StackEntry.h" #include "promote.h" #include "Isect.h" #include "HeapEntry.h" #include "SpImpl.h" #include "graph500/generator/graph_generator.h" #include "graph500/generator/utils.h" namespace combblas { /* Initiator settings: for faster random number generation, the initiator * probabilities are defined as fractions (a = INITIATOR_A_NUMERATOR / * INITIATOR_DENOMINATOR, b = c = INITIATOR_BC_NUMERATOR / * INITIATOR_DENOMINATOR, d = 1 - a - b - c. */ #define INITIATOR_A_NUMERATOR 5700 #define INITIATOR_BC_NUMERATOR 1900 #define INITIATOR_DENOMINATOR 10000 /* If this macro is defined to a non-zero value, use SPK_NOISE_LEVEL / * INITIATOR_DENOMINATOR as the noise parameter to use in introducing noise * into the graph parameters. The approach used is from "A Hitchhiker's Guide * to Choosing Parameters of Stochastic Kronecker Graphs" by C. Seshadhri, Ali * Pinar, and Tamara G. Kolda (http://arxiv.org/abs/1102.5046v1), except that * the adjustment here is chosen based on the current level being processed * rather than being chosen randomly. */ #define SPK_NOISE_LEVEL 0 /* #define SPK_NOISE_LEVEL 1000 -- in INITIATOR_DENOMINATOR units */ class RefGen21 { public: /* Spread the two 64-bit numbers into five nonzero values in the correct range (2 parameter version) */ static void make_mrg_seed_short(uint64_t userseed, uint_fast32_t* seed) { seed[0] = (userseed & 0x3FFFFFFF) + 1; seed[1] = ((userseed >> 30) & 0x3FFFFFFF) + 1; seed[2] = (userseed & 0x3FFFFFFF) + 1; seed[3] = ((userseed >> 30) & 0x3FFFFFFF) + 1; seed[4] = ((userseed >> 60) << 4) + (userseed >> 60) + 1; } static int generate_4way_bernoulli(mrg_state* st, int level, int nlevels) { /* Generator a pseudorandom number in the range [0, INITIATOR_DENOMINATOR) without modulo bias. */ static const uint32_t limit = (UINT32_C(0xFFFFFFFF) % INITIATOR_DENOMINATOR); uint32_t val = mrg_get_uint_orig(st); if (/* Unlikely */ val < limit) { do { val = mrg_get_uint_orig(st); } while (val < limit); } #if SPK_NOISE_LEVEL == 0 int spk_noise_factor = 0; #else int spk_noise_factor = 2 * SPK_NOISE_LEVEL * level / nlevels - SPK_NOISE_LEVEL; #endif int adjusted_bc_numerator = INITIATOR_BC_NUMERATOR + spk_noise_factor; val %= INITIATOR_DENOMINATOR; if ((signed)val < adjusted_bc_numerator) return 1; val -= adjusted_bc_numerator; if ((signed)val < adjusted_bc_numerator) return 2; val -= adjusted_bc_numerator; #if SPK_NOISE_LEVEL == 0 if (val < INITIATOR_A_NUMERATOR) return 0; #else if (val < INITIATOR_A_NUMERATOR * (INITIATOR_DENOMINATOR - 2 * INITIATOR_BC_NUMERATOR) / (INITIATOR_DENOMINATOR - 2 * adjusted_bc_numerator)) return 0; #endif return 3; } /* Reverse bits in a number; this should be optimized for performance * (including using bit- or byte-reverse intrinsics if your platform has them). * */ static inline uint64_t bitreverse(uint64_t x) { #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3) #define USE_GCC_BYTESWAP /* __builtin_bswap* are in 4.3 but not 4.2 */ #endif #ifdef FAST_64BIT_ARITHMETIC /* 64-bit code */ #ifdef USE_GCC_BYTESWAP x = __builtin_bswap64(x); #else x = (x >> 32) | (x << 32); x = ((x >> 16) & UINT64_C(0x0000FFFF0000FFFF)) | ((x & UINT64_C(0x0000FFFF0000FFFF)) << 16); x = ((x >> 8) & UINT64_C(0x00FF00FF00FF00FF)) | ((x & UINT64_C(0x00FF00FF00FF00FF)) << 8); #endif x = ((x >> 4) & UINT64_C(0x0F0F0F0F0F0F0F0F)) | ((x & UINT64_C(0x0F0F0F0F0F0F0F0F)) << 4); x = ((x >> 2) & UINT64_C(0x3333333333333333)) | ((x & UINT64_C(0x3333333333333333)) << 2); x = ((x >> 1) & UINT64_C(0x5555555555555555)) | ((x & UINT64_C(0x5555555555555555)) << 1); return x; #else /* 32-bit code */ uint32_t h = (uint32_t)(x >> 32); uint32_t l = (uint32_t)(x & UINT32_MAX); #ifdef USE_GCC_BYTESWAP h = __builtin_bswap32(h); l = __builtin_bswap32(l); #else h = (h >> 16) | (h << 16); l = (l >> 16) | (l << 16); h = ((h >> 8) & UINT32_C(0x00FF00FF)) | ((h & UINT32_C(0x00FF00FF)) << 8); l = ((l >> 8) & UINT32_C(0x00FF00FF)) | ((l & UINT32_C(0x00FF00FF)) << 8); #endif h = ((h >> 4) & UINT32_C(0x0F0F0F0F)) | ((h & UINT32_C(0x0F0F0F0F)) << 4); l = ((l >> 4) & UINT32_C(0x0F0F0F0F)) | ((l & UINT32_C(0x0F0F0F0F)) << 4); h = ((h >> 2) & UINT32_C(0x33333333)) | ((h & UINT32_C(0x33333333)) << 2); l = ((l >> 2) & UINT32_C(0x33333333)) | ((l & UINT32_C(0x33333333)) << 2); h = ((h >> 1) & UINT32_C(0x55555555)) | ((h & UINT32_C(0x55555555)) << 1); l = ((l >> 1) & UINT32_C(0x55555555)) | ((l & UINT32_C(0x55555555)) << 1); return ((uint64_t)l << 32) | h; /* Swap halves */ #endif } /* Apply a permutation to scramble vertex numbers; a randomly generated * permutation is not used because applying it at scale is too expensive. */ static inline int64_t scramble(int64_t v0, int lgN, uint64_t val0, uint64_t val1) { uint64_t v = (uint64_t)v0; v += val0 + val1; v *= (val0 | UINT64_C(0x4519840211493211)); v = (RefGen21::bitreverse(v) >> (64 - lgN)); assert ((v >> lgN) == 0); v *= (val1 | UINT64_C(0x3050852102C843A5)); v = (RefGen21::bitreverse(v) >> (64 - lgN)); assert ((v >> lgN) == 0); return (int64_t)v; } /* Make a single graph edge using a pre-set MRG state. */ static void make_one_edge(int64_t nverts, int level, int lgN, mrg_state* st, packed_edge* result, uint64_t val0, uint64_t val1) { int64_t base_src = 0, base_tgt = 0; while (nverts > 1) { int square = generate_4way_bernoulli(st, level, lgN); int src_offset = square / 2; int tgt_offset = square % 2; assert (base_src <= base_tgt); if (base_src == base_tgt) { /* Clip-and-flip for undirected graph */ if (src_offset > tgt_offset) { int temp = src_offset; src_offset = tgt_offset; tgt_offset = temp; } } nverts /= 2; ++level; base_src += nverts * src_offset; base_tgt += nverts * tgt_offset; } write_edge(result, scramble(base_src, lgN, val0, val1), scramble(base_tgt, lgN, val0, val1)); } static inline mrg_state MakeScrambleValues(uint64_t & val0, uint64_t & val1, const uint_fast32_t seed[]) { mrg_state state; mrg_seed(&state, seed); mrg_state new_state = state; mrg_skip(&new_state, 50, 7, 0); val0 = mrg_get_uint_orig(&new_state); val0 *= UINT64_C(0xFFFFFFFF); val0 += mrg_get_uint_orig(&new_state); val1 = mrg_get_uint_orig(&new_state); val1 *= UINT64_C(0xFFFFFFFF); val1 += mrg_get_uint_orig(&new_state); return state; } /* Generate a range of edges (from start_edge to end_edge of the total graph), * writing into elements [0, end_edge - start_edge) of the edges array. This * code is parallel on OpenMP, it must be used with separately-implemented SPMD parallelism for MPI. */ static void generate_kronecker_range( const uint_fast32_t seed[5] /* All values in [0, 2^31 - 1), not all zero */, int logN /* In base 2 */, int64_t start_edge, int64_t end_edge, packed_edge* edges) { int64_t nverts = (int64_t)1 << logN; uint64_t val0, val1; /* Values for scrambling */ mrg_state state = MakeScrambleValues(val0, val1, seed); #ifdef _OPENMP #pragma omp parallel for #endif for (int64_t ei = start_edge; ei < end_edge; ++ei) { mrg_state new_state = state; mrg_skip(&new_state, 0, ei, 0); make_one_edge(nverts, 0, logN, &new_state, edges + (ei - start_edge), val0, val1); } } static inline void compute_edge_range(int rank, int size, int64_t M, int64_t* start_idx, int64_t* end_idx) { int64_t rankc = (int64_t)(rank); int64_t sizec = (int64_t)(size); *start_idx = rankc * (M / sizec) + (rankc < (M % sizec) ? rankc : (M % sizec)); *end_idx = (rankc + 1) * (M / sizec) + (rankc + 1 < (M % sizec) ? rankc + 1 : (M % sizec)); } static inline void make_graph(int log_numverts, int64_t M, int64_t* nedges_ptr, packed_edge** result_ptr, MPI_Comm & world) { int rank, size; #ifdef DETERMINISTIC uint64_t userseed1 = 0; #else uint64_t userseed1 = (uint64_t) init_random(); #endif /* Spread the two 64-bit numbers into five nonzero values in the correct range. */ uint_fast32_t seed[5]; make_mrg_seed(userseed1, userseed1, seed); MPI_Comm_rank(world, &rank); MPI_Comm_size(world, &size); int64_t start_idx, end_idx; compute_edge_range(rank, size, M, &start_idx, &end_idx); int64_t nedges = end_idx - start_idx; packed_edge* local_edges = new packed_edge[nedges]; double start = MPI_Wtime(); generate_kronecker_range(seed, log_numverts, start_idx, end_idx, local_edges); double gen_time = MPI_Wtime() - start; *result_ptr = local_edges; *nedges_ptr = nedges; if (rank == 0) { fprintf(stdout, "graph_generation: %f s\n", gen_time); } } static inline long init_random () { long seed = -1; if (getenv ("SEED")) { errno = 0; seed = strtol (getenv ("SEED"), NULL, 10); if (errno) seed = -1; } if (seed < 0) seed = 0xDECAFBAD; return seed; } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/csc.h000644 000765 000024 00000004376 13271404146 021326 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 11/15/2016 --------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2016, The Regents of the University of California 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. */ #ifndef _CSC_H #define _CSC_H #include #include #include #include #include "SpDefs.h" #include "SpHelper.h" namespace combblas { template class Csc { public: typedef NT value_type; typedef IT index_type; Csc (); Csc (IT size,IT nCol); Csc (const Csc & rhs); // copy constructor ~Csc(); Csc & operator=(const Csc & rhs); // assignment operator void Resize(IT nsize); IT * jc ; // col pointers, size n+1 IT * ir ; // row indices, size nzmax NT * num; // generic values, size nzmax IT n; // number of columns IT nz; }; } #include "csc.cpp" // Template member function definitions need to be known to the compiler #endif CombBLAS_beta_16_2/include/CombBLAS/FullyDistVec.h000644 000765 000024 00000033465 13271404146 023134 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _FULLY_DIST_VEC_H_ #define _FULLY_DIST_VEC_H_ #include #include #include #include #include #include #include "CombBLAS.h" #include "CommGrid.h" #include "FullyDist.h" #include "Exception.h" namespace combblas { template class FullyDistSpVec; template class SpParMat; template class DistEdgeList; template class DenseVectorLocalIterator; // ABAB: As opposed to SpParMat, IT here is used to encode global size and global indices; // therefore it can not be 32-bits, in general. template class FullyDistVec: public FullyDist::value, NT >::type > { public: FullyDistVec ( ); FullyDistVec ( IT globallen, NT initval); FullyDistVec ( std::shared_ptr grid); FullyDistVec ( std::shared_ptr grid, IT globallen, NT initval); FullyDistVec ( const FullyDistSpVec & rhs ); // Sparse -> Dense conversion constructor FullyDistVec ( const std::vector & fillarr, std::shared_ptr grid ); // initialize a FullyDistVec with a vector from each processor template FullyDistVec ( const FullyDistVec& rhs ); // type converter constructor class ScalarReadSaveHandler { public: NT getNoNum(IT index) { return static_cast(1); } template NT read(std::basic_istream& is, IT index) { NT v; is >> v; return v; } template void save(std::basic_ostream& os, const NT& v, IT index) { os << v; } }; template void ParallelWrite(const std::string & filename, bool onebased, HANDLER handler, bool includeindices = true) { FullyDistSpVec tmpSpVec = *this; // delegate tmpSpVec.ParallelWrite(filename, onebased, handler, includeindices); } void ParallelWrite(const std::string & filename, bool onebased, bool includeindices = true) { ParallelWrite(filename, onebased, ScalarReadSaveHandler(), includeindices); }; template void ParallelRead (const std::string & filename, bool onebased, _BinaryOperation BinOp) { FullyDistSpVec tmpSpVec = *this; // delegate tmpSpVec.ParallelRead(filename, onebased, BinOp); *this = tmpSpVec; // sparse -> dense conversion } template std::ifstream& ReadDistribute (std::ifstream& infile, int master, HANDLER handler); std::ifstream& ReadDistribute (std::ifstream& infile, int master) { return ReadDistribute(infile, master, ScalarReadSaveHandler()); } template void SaveGathered(std::ofstream& outfile, int master, HANDLER handler, bool printProcSplits = false); void SaveGathered(std::ofstream& outfile, int master) { SaveGathered(outfile, master, ScalarReadSaveHandler(), false); } template FullyDistVec & operator=(const FullyDistVec< ITRHS,NTRHS > & rhs); // assignment with type conversion FullyDistVec & operator=(const FullyDistVec & rhs); //!< Actual assignment operator FullyDistVec & operator=(const FullyDistSpVec & rhs); //!< FullyDistSpVec->FullyDistVec conversion operator FullyDistVec & operator=(NT fixedval) // assign fixed value { #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i < arr.size(); ++i) arr[i] = fixedval; return *this; } FullyDistVec operator() (const FullyDistVec & ri) const; // & operator+=(const FullyDistSpVec & rhs); FullyDistVec & operator+=(const FullyDistVec & rhs); FullyDistVec & operator-=(const FullyDistSpVec & rhs); FullyDistVec & operator-=(const FullyDistVec & rhs); bool operator==(const FullyDistVec & rhs) const; void SetElement (IT indx, NT numx); // element-wise assignment void SetLocalElement(IT index, NT value) { arr[index] = value; }; // no checks, local index NT GetElement (IT indx) const; // element-wise fetch NT operator[](IT indx) const // more c++ like API { return GetElement(indx); } void Set(const FullyDistSpVec< IT,NT > & rhs); template void GSet (const FullyDistSpVec & spVec, _BinaryOperationIdx __binopIdx, _BinaryOperationVal __binopVal, MPI_Win win); template FullyDistSpVec GGet (const FullyDistSpVec & spVec, _BinaryOperationIdx __binopIdx, NT nullValue); void iota(IT globalsize, NT first); void RandPerm(); // randomly permute the vector FullyDistVec sort(); // sort and return the permutation using FullyDist::value, NT >::type>::LengthUntil; using FullyDist::value, NT >::type>::TotalLength; using FullyDist::value, NT >::type>::Owner; using FullyDist::value, NT >::type>::MyLocLength; IT LocArrSize() const { return arr.size(); } // = MyLocLength() once arr is resized //TODO: we should change this function and return the vector directly const NT * GetLocArr() const { return arr.data(); } // = MyLocLength() once arr is resized template FullyDistSpVec Find(_Predicate pred) const; //!< Return the elements for which pred is true FullyDistSpVec Find(NT val) const; //!< Return the elements val is found template FullyDistVec FindInds(_Predicate pred) const; //!< Return the indices where pred is true template IT Count(_Predicate pred) const; //!< Return the number of elements for which pred is true template void Apply(_UnaryOperation __unary_op) { std::transform(arr.begin(), arr.end(), arr.begin(), __unary_op); } template void ApplyInd(_BinaryOperation __binary_op) { IT offset = LengthUntil(); #ifdef _OPENMP #pragma omp parallel for #endif for(size_t i=0; i < arr.size(); ++i) arr[i] = __binary_op(arr[i], i + offset); } template void Apply(_UnaryOperation __unary_op, const FullyDistSpVec& mask); // extended callback versions template void EWiseApply(const FullyDistVec & other, _BinaryOperation __binary_op, _BinaryPredicate _do_op, const bool useExtendedBinOp); template void EWiseApply(const FullyDistSpVec & other, _BinaryOperation __binary_op, _BinaryPredicate _do_op, bool applyNulls, NT2 nullValue, const bool useExtendedBinOp); // plain fallback versions template void EWiseApply(const FullyDistVec & other, _BinaryOperation __binary_op, _BinaryPredicate _do_op) { EWiseApply(other, EWiseExtToPlainAdapter(__binary_op), EWiseExtToPlainAdapter(_do_op), true); } template void EWiseApply(const FullyDistSpVec & other, _BinaryOperation __binary_op, _BinaryPredicate _do_op, bool applyNulls, NT2 nullValue) { EWiseApply(other, EWiseExtToPlainAdapter(__binary_op), EWiseExtToPlainAdapter(_do_op), applyNulls, nullValue, true); } template class retTrue { public: bool operator()(const T1& x, const T2& y) { return true; } }; template void EWiseApply(const FullyDistVec & other, _BinaryOperation __binary_op) { this->EWiseApply(other, __binary_op, retTrue()); } template void EWiseApply(const FullyDistSpVec & other, _BinaryOperation __binary_op, bool applyNulls, NT2 nullValue) { this->EWiseApply(other, __binary_op, retTrue(), applyNulls, nullValue); } void PrintToFile(std::string prefix) { std::ofstream output; commGrid->OpenDebugFile(prefix, output); std::copy(arr.begin(), arr.end(), std::ostream_iterator (output, " ")); output << std::endl; output.close(); } void PrintInfo(std::string vectorname) const; void DebugPrint(); std::shared_ptr getcommgrid() const { return commGrid; } std::pair MinElement() const; // returns pair of global minimum template NT Reduce(_BinaryOperation __binary_op, NT identity) const; //! Reduce can be used to implement max_element, for instance template OUT Reduce(_BinaryOperation __binary_op, OUT default_val, _UnaryOperation __unary_op) const; void SelectCandidates(double nver); template ::type> void EWiseOut(const FullyDistVec & rhs, _BinaryOperation __binary_op, FullyDistVec & result); using FullyDist::value, NT >::type>::glen; using FullyDist::value, NT >::type>::commGrid; private: std::vector< NT > arr; template void EWise(const FullyDistVec & rhs, _BinaryOperation __binary_op); template friend class DenseParMat; template friend class SpParMat; template friend class FullyDistVec; template friend class FullyDistSpVec; template friend class DenseVectorLocalIterator; template friend FullyDistVec::T_promote> SpMV (const SpParMat & A, const FullyDistVec & x ); template friend FullyDistSpVec::T_promote> EWiseMult (const FullyDistSpVec & V, const FullyDistVec & W , bool exclude, NU2 zero); template friend FullyDistSpVec::T_promote> EWiseApply (const FullyDistSpVec & V, const FullyDistVec & W , _BinaryOperation _binary_op, typename promote_trait::T_promote zero); template friend FullyDistSpVec EWiseApply (const FullyDistSpVec & V, const FullyDistVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, NU1 Vzero, const bool useExtendedBinOp); template friend FullyDistSpVec EWiseApply_threaded (const FullyDistSpVec & V, const FullyDistVec & W , _BinaryOperation _binary_op, _BinaryPredicate _doOp, bool allowVNulls, NU1 Vzero, const bool useExtendedBinOp); template friend void RenameVertices(DistEdgeList & DEL); template friend FullyDistVec Concatenate ( std::vector< FullyDistVec > & vecs); template friend void Augment (FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row, FullyDistVec& parentsRow, FullyDistVec& leaves); template friend SpParMat PermMat (const FullyDistVec & ri, const IU ncol); friend void maximumMatching(SpParMat < int64_t, bool, SpDCCols > & A, FullyDistVec& mateRow2Col,FullyDistVec& mateCol2Row); }; } #include "FullyDistVec.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/VecIterator.h000644 000765 000024 00000012702 13271404146 022775 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef VEC_ITERATOR_H #define VEC_ITERATOR_H #include "FullyDistVec.h" #include "FullyDistSpVec.h" namespace combblas { template class VectorLocalIterator { public: virtual ~VectorLocalIterator() {} virtual IT LocalToGlobal(IT loc_idx) const = 0; virtual IT GlobalToLocal(IT gbl_idx) const = 0; virtual bool Next() = 0; virtual bool NextTo(IT loc_idx) = 0; virtual bool HasNext() = 0; virtual IT GetLocIndex() const = 0; virtual NT& GetValue() const = 0; virtual void Del() = 0; virtual void Set(const IT loc_idx, const NT& val) = 0; }; template class DenseVectorLocalIterator: public VectorLocalIterator { protected: FullyDistVec& v; IT iter_idx; public: DenseVectorLocalIterator(FullyDistVec& in_v): v(in_v), iter_idx(0) {} IT LocalToGlobal(IT loc_idx) const { return v.LengthUntil() + loc_idx; } IT GlobalToLocal(IT gbl_idx) const { IT ret; v.Owner(gbl_idx, ret); return ret; } bool Next() { iter_idx++; bool exists = ((unsigned)iter_idx < v.arr.size()); if (!exists) iter_idx = -1; return exists; } bool NextTo(IT loc_idx) { iter_idx = loc_idx; return iter_idx > 0 && (unsigned)iter_idx < v.arr.size(); } bool HasNext() { return iter_idx >= 0 && (unsigned)iter_idx < v.arr.size(); } IT GetLocIndex() const { if ((unsigned)iter_idx < v.arr.size()) return iter_idx; else return -1; } NT& GetValue() const { return v.arr[iter_idx]; } void Del() { assert(false); } void Set(const IT loc_idx, const NT& val) { v.arr[loc_idx] = val; } }; template class SparseVectorLocalIterator: public VectorLocalIterator { protected: FullyDistSpVec& v; IT iter_idx; public: SparseVectorLocalIterator(FullyDistSpVec& in_v): v(in_v), iter_idx(0) { if (v.ind.size() == 0) iter_idx = -1; } IT LocalToGlobal(IT loc_idx) const { return v.LengthUntil() + loc_idx; } IT GlobalToLocal(IT gbl_idx) const { IT ret; v.Owner(gbl_idx, ret); return ret; } bool Next() { iter_idx++; bool exists = ((unsigned)iter_idx < v.ind.size()); if (!exists) iter_idx = -1; return exists; } bool NextTo(IT loc_idx) { typename std::vector::iterator iter = std::lower_bound(v.ind.begin()+iter_idx, v.ind.end(), loc_idx); if(iter == v.ind.end()) // beyond limits, insert from back { iter_idx = -1; return false; } else if (loc_idx < *iter) // not found, but almost { iter_idx = iter - v.ind.begin(); return false; } else // found { iter_idx = iter - v.ind.begin(); return true; } } bool HasNext() { return iter_idx >= 0 && (unsigned)iter_idx < v.ind.size(); } IT GetLocIndex() const { if (iter_idx < 0) return -1; else return v.ind[iter_idx]; } NT& GetValue() const { return v.num[iter_idx]; } void Del() { v.ind.erase(v.ind.begin()+iter_idx); v.num.erase(v.num.begin()+iter_idx); if ((unsigned)iter_idx >= v.ind.size()) iter_idx = -1; } void Set(const IT loc_idx, const NT& val) { // see if we're just replacing the current value /*if (loc_idx >= 0 && loc_idx == v.ind[iter_idx]) { v.num[iter_idx] = val; return; }*/ // inserted elsewhere // This is from FullyDistSpVec::SetElement(): typename std::vector::iterator iter = std::lower_bound(v.ind.begin(), v.ind.end(), loc_idx); if(iter == v.ind.end()) // beyond limits, insert from back { v.ind.push_back(loc_idx); v.num.push_back(val); } else if (loc_idx < *iter) // not found, insert in the middle { // the order of insertions is crucial // if we first insert to ind, then ind.begin() is invalidated ! v.num.insert(v.num.begin() + (iter-v.ind.begin()), val); v.ind.insert(iter, loc_idx); } else // found { *(v.num.begin() + (iter-v.ind.begin())) = val; } } void Append(const IT loc_idx, const NT& val) { v.ind.push_back(loc_idx); v.num.push_back(val); } }; #include "VecIterator.cpp" } #endif CombBLAS_beta_16_2/include/CombBLAS/myenableif.h000644 000765 000024 00000001006 13271404146 022654 0ustar00aydinbulucstaff000000 000000 #ifndef _MY_ENABLE_IF_ #define _MY_ENABLE_IF_ namespace combblas { // ABAB: A simple enable_if construct for now template struct enable_if {}; template struct enable_if { typedef T type; }; template struct disable_if { typedef T type; }; template struct disable_if {}; template struct is_boolean { static const bool value = false; }; template <> struct is_boolean { static const bool value = true; }; } #endif CombBLAS_beta_16_2/include/CombBLAS/SpTuples.cpp000644 000765 000024 00000022764 13271404146 022671 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include "SpTuples.h" #include "SpParHelper.h" #include namespace combblas { template SpTuples::SpTuples(int64_t size, IT nRow, IT nCol) :m(nRow), n(nCol), nnz(size) { if(nnz > 0) { tuples = new std::tuple[nnz]; } else { tuples = NULL; } isOperatorNew = false; } template SpTuples::SpTuples (int64_t size, IT nRow, IT nCol, std::tuple * mytuples, bool sorted, bool isOpNew) :tuples(mytuples), m(nRow), n(nCol), nnz(size), isOperatorNew(isOpNew) { if(!sorted) { SortColBased(); } } /** * Generate a SpTuples object from an edge list * @param[in,out] edges: edge list that might contain duplicate edges. freed upon return * Semantics differ depending on the object created: * NT=bool: duplicates are ignored * NT='countable' (such as short,int): duplicated as summed to keep count **/ template SpTuples::SpTuples (int64_t maxnnz, IT nRow, IT nCol, std::vector & edges, bool removeloops):m(nRow), n(nCol) { if(maxnnz > 0) { tuples = new std::tuple[maxnnz]; } for(int64_t i=0; i().swap(edges); // free memory for edges nnz = maxnnz; // for now (to sort) SortColBased(); int64_t cnz = 0; int64_t dup = 0; int64_t self = 0; nnz = 0; while(cnz < maxnnz) { int64_t j=cnz+1; while(j < maxnnz && rowindex(cnz) == rowindex(j) && colindex(cnz) == colindex(j)) { numvalue(cnz) += numvalue(j); numvalue(j++) = 0; // mark for deletion ++dup; } if(removeloops && rowindex(cnz) == colindex(cnz)) { numvalue(cnz) = 0; --nnz; ++self; } ++nnz; cnz = j; } std::tuple * ntuples = new std::tuple[nnz]; int64_t j = 0; for(int64_t i=0; i sorted lexicographically} * \remark Since input is column sorted, the tuples are automatically generated in that way too **/ template SpTuples::SpTuples (int64_t size, IT nRow, IT nCol, StackEntry > * & multstack) :m(nRow), n(nCol), nnz(size) { isOperatorNew = false; if(nnz > 0) { tuples = new std::tuple[nnz]; } for(int64_t i=0; i SpTuples::~SpTuples() { if(nnz > 0) { if(isOperatorNew) ::operator delete(tuples); else delete [] tuples; } } /** * Hint1: copy constructor (constructs a new object. i.e. this is NEVER called on an existing object) * Hint2: Base's default constructor is called under the covers * Normally Base's copy constructor should be invoked but it doesn't matter here as Base has no data members */ template SpTuples::SpTuples(const SpTuples & rhs): m(rhs.m), n(rhs.n), nnz(rhs.nnz) { tuples = new std::tuple[nnz]; isOperatorNew = false; for(IT i=0; i< nnz; ++i) { tuples[i] = rhs.tuples[i]; } } //! Constructor for converting SpDCCols matrix -> SpTuples template SpTuples::SpTuples (const SpDCCols & rhs): m(rhs.m), n(rhs.n), nnz(rhs.nnz) { if(nnz > 0) { FillTuples(rhs.dcsc); } isOperatorNew = false; } template inline void SpTuples::FillTuples (Dcsc * mydcsc) { tuples = new std::tuple[nnz]; IT k = 0; for(IT i = 0; i< mydcsc->nzc; ++i) { for(IT j = mydcsc->cp[i]; j< mydcsc->cp[i+1]; ++j) { colindex(k) = mydcsc->jc[i]; rowindex(k) = mydcsc->ir[j]; numvalue(k++) = mydcsc->numx[j]; } } } // Hint1: The assignment operator (operates on an existing object) // Hint2: The assignment operator is the only operator that is not inherited. // Make sure that base class data are also updated during assignment template SpTuples & SpTuples::operator=(const SpTuples & rhs) { if(this != &rhs) // "this" pointer stores the address of the class instance { if(nnz > 0) { // make empty if(isOperatorNew) ::operator delete(tuples); else delete [] tuples; } m = rhs.m; n = rhs.n; nnz = rhs.nnz; isOperatorNew = false; if(nnz> 0) { tuples = new std::tuple[nnz]; for(IT i=0; i< nnz; ++i) { tuples[i] = rhs.tuples[i]; } } } return *this; } /** * \pre {The object is either column-sorted or row-sorted, either way the identical entries will be consecutive} **/ template template void SpTuples::RemoveDuplicates(BINFUNC BinOp) { if(nnz > 0) { std::vector< std::tuple > summed; summed.push_back(tuples[0]); for(IT i=1; i< nnz; ++i) { if((joker::get<0>(summed.back()) == joker::get<0>(tuples[i])) && (joker::get<1>(summed.back()) == joker::get<1>(tuples[i]))) { joker::get<2>(summed.back()) = BinOp(joker::get<2>(summed.back()), joker::get<2>(tuples[i])); } else { summed.push_back(tuples[i]); } } if(isOperatorNew) ::operator delete(tuples); else delete [] tuples; tuples = new std::tuple[summed.size()]; isOperatorNew = false; std::copy(summed.begin(), summed.end(), tuples); nnz = summed.size(); } } //! Loads a triplet matrix from infile //! \remarks Assumes matlab type indexing for the input (i.e. indices start from 1) template std::ifstream& SpTuples::getstream (std::ifstream& infile) { std::cout << "Getting... SpTuples" << std::endl; IT cnz = 0; if (infile.is_open()) { while ( (!infile.eof()) && cnz < nnz) { infile >> rowindex(cnz) >> colindex(cnz) >> numvalue(cnz); // row-col-value rowindex(cnz) --; colindex(cnz) --; if((rowindex(cnz) > m) || (colindex(cnz) > n)) { std::cerr << "supplied matrix indices are beyond specified boundaries, aborting..." << std::endl; } ++cnz; } assert(nnz == cnz); } else { std::cerr << "input file is not open!" << std::endl; } return infile; } //! Output to a triplets file //! \remarks Uses matlab type indexing for the output (i.e. indices start from 1) template std::ofstream& SpTuples::putstream(std::ofstream& outfile) const { outfile << m <<"\t"<< n <<"\t"<< nnz< void SpTuples::PrintInfo() { std::cout << "This is a SpTuples class" << std::endl; std::cout << "m: " << m ; std::cout << ", n: " << n ; std::cout << ", nnz: "<< nnz << std::endl; for(IT i=0; i< nnz; ++i) { if(rowindex(i) < 0 || colindex(i) < 0) { std::cout << "Negative index at " << i << std::endl; return; } else if(rowindex(i) >= m || colindex(i) >= n) { std::cout << "Index " << i << " too big with values (" << rowindex(i) << ","<< colindex(i) << ")" << std::endl; } } if(m < 8 && n < 8) // small enough to print { NT ** A = SpHelper::allocate2D(m,n); for(IT i=0; i< m; ++i) for(IT j=0; j namespace combblas { struct DeletePtrIf { template void operator()(const T *ptr, _BinaryPredicate cond, Pred first, Pred second) const { if(cond(first,second)) delete ptr; } }; template void DeleteAll(A arr1) { delete [] arr1; } template void DeleteAll(A arr1, B arr2) { delete [] arr2; DeleteAll(arr1); } template void DeleteAll(A arr1, B arr2, C arr3) { delete [] arr3; DeleteAll(arr1, arr2); } template void DeleteAll(A arr1, B arr2, C arr3, D arr4) { delete [] arr4; DeleteAll(arr1, arr2, arr3); } template void DeleteAll(A arr1, B arr2, C arr3, D arr4, E arr5) { delete [] arr5; DeleteAll(arr1, arr2, arr3, arr4); } template void DeleteAll(A arr1, B arr2, C arr3, D arr4, E arr5, F arr6) { delete [] arr6; DeleteAll(arr1, arr2, arr3, arr4,arr5); } } #endif CombBLAS_beta_16_2/include/CombBLAS/SpDCCols.h000644 000765 000024 00000041632 13271404146 022164 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SP_DCCOLS_H #define _SP_DCCOLS_H #include #include #include #include "SpMat.h" // Best to include the base class first #include "SpHelper.h" #include "StackEntry.h" #include "dcsc.h" #include "Isect.h" #include "Semirings.h" #include "MemoryPool.h" #include "LocArr.h" #include "Friends.h" #include "CombBLAS.h" #include "FullyDist.h" namespace combblas { template class SpDCCols: public SpMat > { public: typedef IT LocalIT; typedef NT LocalNT; // Constructors : SpDCCols (); SpDCCols (IT size, IT nRow, IT nCol, IT nzc); SpDCCols (const SpTuples & rhs, bool transpose); SpDCCols (IT nRow, IT nCol, IT nnz1, const std::tuple * rhs, bool transpose); SpDCCols (const SpDCCols & rhs); // Actual copy constructor ~SpDCCols(); template operator SpDCCols () const; //!< NNT: New numeric type template operator SpDCCols () const; //!< NNT: New numeric type, NIT: New index type // Member Functions and Operators: SpDCCols & operator= (const SpDCCols & rhs); SpDCCols & operator+= (const SpDCCols & rhs); SpDCCols operator() (IT ri, IT ci) const; SpDCCols operator() (const std::vector & ri, const std::vector & ci) const; bool operator== (const SpDCCols & rhs) const { if(rhs.nnz == 0 && nnz == 0) return true; if(nnz != rhs.nnz || m != rhs.m || n != rhs.n) return false; return ((*dcsc) == (*(rhs.dcsc))); } class SpColIter //! Iterate over (sparse) columns of the sparse matrix { public: class NzIter //! Iterate over the nonzeros of the sparse column { public: NzIter(IT * ir = NULL, NT * num = NULL) : rid(ir), val(num) {} bool operator==(const NzIter & other) { return(rid == other.rid); // compare pointers } bool operator!=(const NzIter & other) { return(rid != other.rid); } bool operator<(const NzIter & other) { return(rid < other.rid); } NzIter operator+(IT inc) { rid+=inc; val+=inc; return(*this); } NzIter operator-(IT inc) { rid-=inc; val-=inc; return(*this); } NzIter & operator++() // prefix operator { ++rid; ++val; return(*this); } NzIter operator++(int) // postfix operator { NzIter tmp(*this); ++(*this); return(tmp); } IT rowid() const //!< Return the "local" rowid of the current nonzero entry. { return (*rid); } NT & value() //!< value is returned by reference for possible updates { return (*val); } private: IT * rid; NT * val; }; SpColIter(IT * cp = NULL, IT * jc = NULL) : cptr(cp), cid(jc) {} bool operator==(const SpColIter& other) { return(cptr == other.cptr); // compare pointers } bool operator!=(const SpColIter& other) { return(cptr != other.cptr); } SpColIter& operator++() // prefix operator { ++cptr; ++cid; return(*this); } SpColIter operator++(int) // postfix operator (common) { SpColIter tmp(*this); ++(*this); return(tmp); } SpColIter operator+(IT inc) { cptr+=inc; cid+=inc; return(*this); } SpColIter operator-(IT inc) { cptr-=inc; cid-=inc; return(*this); } IT colid() const //!< Return the "local" colid of the current column. { return (*cid); } IT colptr() const { return (*cptr); } IT colptrnext() const { return (*(cptr+1)); } IT nnz() const { return (colptrnext() - colptr()); } private: IT * cptr; IT * cid; }; SpColIter begcol() { if( nnz > 0 ) return SpColIter(dcsc->cp, dcsc->jc); else return SpColIter(NULL, NULL); } SpColIter begcol(int i) // multithreaded version { if( dcscarr[i] ) return SpColIter(dcscarr[i]->cp, dcscarr[i]->jc); else return SpColIter(NULL, NULL); } SpColIter endcol() { if( nnz > 0 ) return SpColIter(dcsc->cp + dcsc->nzc, NULL); else return SpColIter(NULL, NULL); } SpColIter endcol(int i) //multithreaded version { if( dcscarr[i] ) return SpColIter(dcscarr[i]->cp + dcscarr[i]->nzc, NULL); else return SpColIter(NULL, NULL); } typename SpColIter::NzIter begnz(const SpColIter & ccol) //!< Return the beginning iterator for the nonzeros of the current column { return typename SpColIter::NzIter( dcsc->ir + ccol.colptr(), dcsc->numx + ccol.colptr() ); } typename SpColIter::NzIter endnz(const SpColIter & ccol) //!< Return the ending iterator for the nonzeros of the current column { return typename SpColIter::NzIter( dcsc->ir + ccol.colptrnext(), NULL ); } typename SpColIter::NzIter begnz(const SpColIter & ccol, int i) //!< multithreaded version { return typename SpColIter::NzIter( dcscarr[i]->ir + ccol.colptr(), dcscarr[i]->numx + ccol.colptr() ); } typename SpColIter::NzIter endnz(const SpColIter & ccol, int i) //!< multithreaded version { return typename SpColIter::NzIter( dcscarr[i]->ir + ccol.colptrnext(), NULL ); } template void Apply(_UnaryOperation __unary_op) { if(nnz > 0) dcsc->Apply(__unary_op); } template SpDCCols* PruneI(_UnaryOperation __unary_op, bool inPlace, GlobalIT rowOffset, GlobalIT colOffset); template SpDCCols* Prune(_UnaryOperation __unary_op, bool inPlace); template SpDCCols* PruneColumn(NT* pvals, _BinaryOperation __binary_op, bool inPlace); template SpDCCols* PruneColumn(IT* pinds, NT* pvals, _BinaryOperation __binary_op, bool inPlace); template void UpdateDense(NT ** array, _BinaryOperation __binary_op) const { if(nnz > 0 && dcsc != NULL) dcsc->UpdateDense(array, __binary_op); } void EWiseScale(NT ** scaler, IT m_scaler, IT n_scaler); void EWiseMult (const SpDCCols & rhs, bool exclude); void Transpose(); //!< Mutator version, replaces the calling object SpDCCols TransposeConst() const; //!< Const version, doesn't touch the existing object SpDCCols * TransposeConstPtr() const; void RowSplit(int numsplits) { BooleanRowSplit(*this, numsplits); // only works with boolean arrays } void ColSplit(int parts, std::vector< SpDCCols > & matrices); //!< \attention Destroys calling object (*this) void ColConcatenate(std::vector< SpDCCols > & matrices); //!< \attention Destroys its parameters (matrices) void Split(SpDCCols & partA, SpDCCols & partB); //!< \attention Destroys calling object (*this) void Merge(SpDCCols & partA, SpDCCols & partB); //!< \attention Destroys its parameters (partA & partB) void CreateImpl(const std::vector & essentials); void CreateImpl(IT size, IT nRow, IT nCol, std::tuple * mytuples); void CreateImpl(IT * _cp, IT * _jc, IT * _ir, NT * _numx, IT _nz, IT _nzc, IT _m, IT _n); Arr GetArrays() const; std::vector GetEssentials() const; const static IT esscount; bool isZero() const { return (nnz == 0); } IT getnrow() const { return m; } IT getncol() const { return n; } IT getnnz() const { return nnz; } IT getnzc() const { return (nnz == 0) ? 0: dcsc->nzc; } int getnsplit() const { return splits; } std::ofstream& put(std::ofstream & outfile) const; std::ifstream& get(std::ifstream & infile); void PrintInfo() const; void PrintInfo(std::ofstream & out) const; template int PlusEq_AtXBt(const SpDCCols & A, const SpDCCols & B); template int PlusEq_AtXBn(const SpDCCols & A, const SpDCCols & B); template int PlusEq_AnXBt(const SpDCCols & A, const SpDCCols & B); template int PlusEq_AnXBn(const SpDCCols & A, const SpDCCols & B); Dcsc * GetDCSC() const // only for single threaded matrices { return dcsc; } Dcsc * GetDCSC(int i) const // only for split (multithreaded) matrices { return dcscarr[i]; } auto GetInternal() const { return GetDCSC(); } auto GetInternal(int i) const { return GetDCSC(i); } private: void CopyDcsc(Dcsc * source); SpDCCols ColIndex(const std::vector & ci) const; //!< col indexing without multiplication template SpDCCols< IT, typename promote_trait::T_promote > OrdOutProdMult(const SpDCCols & rhs) const; template SpDCCols< IT, typename promote_trait::T_promote > OrdColByCol(const SpDCCols & rhs) const; SpDCCols (IT size, IT nRow, IT nCol, const std::vector & indices, bool isRow); // Constructor for indexing SpDCCols (IT nRow, IT nCol, Dcsc * mydcsc); // Constructor for multiplication // Anonymous union union { Dcsc * dcsc; Dcsc ** dcscarr; }; IT m; IT n; IT nnz; int splits; // for multithreading template friend class SpDCCols; // Let other template instantiations (of the same class) access private members template friend class SpTuples; // AL: removed this because it appears illegal and causes this compiler warning: // warning: dependent nested name specifier 'SpDCCols::' for friend class declaration is not supported; turning off access control for 'SpDCCols' //template //friend class SpDCCols::SpColIter; template friend void BooleanRowSplit(SpDCCols & A, int numsplits); template friend SpDCCols::T_promote > EWiseMult (const SpDCCols & A, const SpDCCols & B, bool exclude); template friend SpDCCols EWiseApply (const SpDCCols & A, const SpDCCols & B, _BinaryOperation __binary_op, bool notB, const NU2& defaultBVal); template friend SpDCCols EWiseApply (const SpDCCols & A, const SpDCCols & B, _BinaryOperation __binary_op, _BinaryPredicate do_op, bool allowANulls, bool allowBNulls, const NU1& ANullVal, const NU2& BNullVal, const bool allowIntersect); template friend SpTuples * Tuples_AnXBn (const SpDCCols & A, const SpDCCols & B, bool clearA, bool clearB); template friend SpTuples * Tuples_AnXBt (const SpDCCols & A, const SpDCCols & B, bool clearA, bool clearB); template friend SpTuples * Tuples_AtXBn (const SpDCCols & A, const SpDCCols & B, bool clearA, bool clearB); template friend SpTuples * Tuples_AtXBt (const SpDCCols & A, const SpDCCols & B, bool clearA, bool clearB); template friend void dcsc_gespmv (const SpDCCols & A, const RHS * x, LHS * y); template friend void dcsc_gespmv_threaded (const SpDCCols & A, const RHS * x, LHS * y); template friend int generic_gespmv_threaded (const SpMat & A, const int32_t * indx, const IVT * numx, int32_t nnzx, int32_t * & sendindbuf, OVT * & sendnumbuf, int * & sdispls, int p_c); }; // At this point, complete type of of SpDCCols is known, safe to declare these specialization (but macros won't work as they are preprocessed) // General case #1: When both NT is the same template struct promote_trait< SpDCCols , SpDCCols > { typedef SpDCCols T_promote; }; // General case #2: First is boolean the second is anything except boolean (to prevent ambiguity) template struct promote_trait< SpDCCols , SpDCCols, typename combblas::disable_if< combblas::is_boolean::value >::type > { typedef SpDCCols T_promote; }; // General case #3: Second is boolean the first is anything except boolean (to prevent ambiguity) template struct promote_trait< SpDCCols , SpDCCols, typename combblas::disable_if< combblas::is_boolean::value >::type > { typedef SpDCCols T_promote; }; template struct promote_trait< SpDCCols , SpDCCols > { typedef SpDCCols T_promote; }; template struct promote_trait< SpDCCols , SpDCCols > { typedef SpDCCols T_promote; }; template struct promote_trait< SpDCCols , SpDCCols > { typedef SpDCCols T_promote; }; template struct promote_trait< SpDCCols , SpDCCols > { typedef SpDCCols T_promote; }; // Below are necessary constructs to be able to define a SpMat where // all we know is DER (say SpDCCols) and NT,IT // in other words, we infer the templated SpDCCols<> type // This is not a type conversion from an existing object, // but a type inference for the newly created object // NIT: New IT, NNT: New NT template struct create_trait { // none }; // Capture everything of the form SpDCCols // it may come as a surprise that the partial specializations can // involve more template parameters than the primary template template struct create_trait< SpDCCols , NIT, NNT > { typedef SpDCCols T_inferred; }; } #include "SpDCCols.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/Compare.h000644 000765 000024 00000015131 13271404146 022133 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _COMPARE_H_ #define _COMPARE_H_ #include #include #include "SpDefs.h" #include "CombBLAS.h" namespace combblas { // third parameter of compare is about floating point-ness template inline bool compare(const T & a, const T & b, std::false_type) // not floating point { return (a == b); } template inline bool compare(const T & a, const T & b, std::true_type) // floating point { // According to the IEEE 754 standard, negative zero and positive zero should // compare as equal with the usual (numerical) comparison operators, like the == operators of C++ if(a == b) return true; // covers the "division by zero" case as well: max(a,b) can't be zero if it fails else return ( std::abs(a - b) < EPSILON || (std::abs(a - b) / std::max(std::abs(a), std::abs(b))) < EPSILON ) ; // Fine if either absolute or relative error is small } template struct ErrorTolerantEqual: public std::binary_function< T, T, bool > { inline bool operator() (const T & a, const T & b) const { return compare(a,b, std::is_floating_point()); } }; template < typename T > struct absdiff : std::binary_function { T operator () ( T const &arg1, T const &arg2 ) const { using std::abs; return abs( arg1 - arg2 ); } }; template struct TupleEqual: public std::binary_function< std::tuple, std::tuple, bool > { inline bool operator()(const std::tuple & lhs, const std::tuple & rhs) const { return ( (std::get<0>(lhs) == std::get<0>(rhs)) && (std::get<1>(lhs) == std::get<1>(rhs)) ); } }; /** ** Functor class ** \return bool, whether lhs precedes rhs in column-sorted order ** @pre {No elements with same (i,j) pairs exist in the input} **/ template struct ColLexiCompare: // struct instead of class so that operator() is public public std::binary_function< std::tuple, std::tuple, bool > // (par1, par2, return_type) { inline bool operator()(const std::tuple & lhs, const std::tuple & rhs) const { if(std::get<1>(lhs) == std::get<1>(rhs)) { return std::get<0>(lhs) < std::get<0>(rhs); } else { return std::get<1>(lhs) < std::get<1>(rhs); } } }; template struct RowLexiCompare: // struct instead of class so that operator() is public public std::binary_function< std::tuple, std::tuple, bool > // (par1, par2, return_type) { inline bool operator()(const std::tuple & lhs, const std::tuple & rhs) const { if(std::get<0>(lhs) == std::get<0>(rhs)) { return std::get<1>(lhs) < std::get<1>(rhs); } else { return std::get<0>(lhs) < std::get<0>(rhs); } } }; // Non-lexicographical, just compares columns template struct ColCompare: // struct instead of class so that operator() is public public std::binary_function< std::tuple, std::tuple, bool > // (par1, par2, return_type) { inline bool operator()(const std::tuple & lhs, const std::tuple & rhs) const { return std::get<1>(lhs) < std::get<1>(rhs); } }; // Non-lexicographical, just compares columns template struct RowCompare: // struct instead of class so that operator() is public public std::binary_function< std::tuple, std::tuple, bool > // (par1, par2, return_type) { inline bool operator()(const std::tuple & lhs, const std::tuple & rhs) const { return std::get<0>(lhs) < std::get<0>(rhs); } }; template struct ColLexiCompareWithID: // struct instead of class so that operator() is public public std::binary_function< std::pair< std::tuple , int > , std::pair< std::tuple , int>, bool > // (par1, par2, return_type) { inline bool operator()(const std::pair< std::tuple , int > & lhs, const std::pair< std::tuple , int > & rhs) const { if(std::get<1>(lhs.first) == std::get<1>(rhs.first)) { return std::get<0>(lhs.first) < std::get<0>(rhs.first); } else { return std::get<1>(lhs.first) < std::get<1>(rhs.first); } } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/SpImpl.cpp000644 000765 000024 00000056713 13271404146 022317 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include "SpImpl.h" #include "SpParHelper.h" #include "PBBS/radixSort.h" #include "Tommy/tommyhashdyn.h" namespace combblas { /** * Base template version [full use of the semiring add() and multiply()] * @param[in] indx { vector that practically keeps column numbers requested from A } * * * Roughly how the below function works: * Let's say our sparse vector has entries at 3, 7 and 9. * FillColInds() creates a vector of pairs that contain the * start and end indices (into matrix.ir and matrix.numx arrays). * pair.first is the start index, pair.second is the end index. * * Here's how we merge these adjacencies of 3,7 and 9: * We keep a heap of size 3 and push the first entries in adj{3}, adj{7}, adj{9} onto the heap wset. * That happens in the first for loop. * * Then as we pop from the heap we push the next entry from the previously popped adjacency (i.e. matrix column). * The heap ensures the output comes out sorted without using a SPA. * that's why indy.back() == wset[hsize-1].key is enough to ensure proper merging. **/ template void SpImpl::SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy) { int32_t hsize = 0; // colinds dereferences A.ir (valid from colinds[].first to colinds[].second) std::vector< std::pair > colinds( (IT) veclen); Adcsc.FillColInds(indx, (IT) veclen, colinds, NULL, 0); // csize is irrelevant if aux is NULL if(sizeof(NUM) > sizeof(OVT)) // ABAB: include a filtering based runtime choice as well? { HeapEntry * wset = new HeapEntry[veclen]; for(IT j =0; j< veclen; ++j) // create the initial heap { while(colinds[j].first != colinds[j].second ) // iterate until finding the first entry within this column that passes the filter { OVT mrhs = SR::multiply(Adcsc.numx[colinds[j].first], numx[j]); if(SR::returnedSAID()) { ++(colinds[j].first); // increment the active row index within the jth column } else { wset[hsize++] = HeapEntry< IT,OVT > ( Adcsc.ir[colinds[j].first], j, mrhs); break; // this column successfully inserted an entry to the heap } } } std::make_heap(wset, wset+hsize); while(hsize > 0) { std::pop_heap(wset, wset + hsize); // result is stored in wset[hsize-1] IT locv = wset[hsize-1].runr; // relative location of the nonzero in sparse column vector if((!indy.empty()) && indy.back() == wset[hsize-1].key) { numy.back() = SR::add(numy.back(), wset[hsize-1].num); } else { indy.push_back( (int32_t) wset[hsize-1].key); numy.push_back(wset[hsize-1].num); } bool pushed = false; // invariant: if ++(colinds[locv].first) == colinds[locv].second, then locv will not appear again in the heap while ( (++(colinds[locv].first)) != colinds[locv].second ) // iterate until finding another passing entry { OVT mrhs = SR::multiply(Adcsc.numx[colinds[locv].first], numx[locv]); if(!SR::returnedSAID()) { wset[hsize-1].key = Adcsc.ir[colinds[locv].first]; wset[hsize-1].num = mrhs; std::push_heap(wset, wset+hsize); // runr stays the same pushed = true; break; } } if(!pushed) --hsize; } delete [] wset; } else { HeapEntry * wset = new HeapEntry[veclen]; for(IT j =0; j< veclen; ++j) // create the initial heap { if(colinds[j].first != colinds[j].second) // current != end { wset[hsize++] = HeapEntry< IT,NUM > ( Adcsc.ir[colinds[j].first], j, Adcsc.numx[colinds[j].first]); // HeapEntry(key, run, num) } } std::make_heap(wset, wset+hsize); while(hsize > 0) { std::pop_heap(wset, wset + hsize); // result is stored in wset[hsize-1] IT locv = wset[hsize-1].runr; // relative location of the nonzero in sparse column vector OVT mrhs = SR::multiply(wset[hsize-1].num, numx[locv]); if (!SR::returnedSAID()) { if((!indy.empty()) && indy.back() == wset[hsize-1].key) { numy.back() = SR::add(numy.back(), mrhs); } else { indy.push_back( (int32_t) wset[hsize-1].key); numy.push_back(mrhs); } } if( (++(colinds[locv].first)) != colinds[locv].second) // current != end { // runr stays the same ! wset[hsize-1].key = Adcsc.ir[colinds[locv].first]; wset[hsize-1].num = Adcsc.numx[colinds[locv].first]; std::push_heap(wset, wset+hsize); } else --hsize; } delete [] wset; } } /** * One of the two versions of SpMXSpV with on boolean matrix [uses only Semiring::add()] * This version is likely to be more memory efficient than the other one (the one that uses preallocated memory buffers) * Because here we don't use a dense accumulation vector but a heap. It will probably be slower though. **/ template void SpImpl::SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy) { IT inf = std::numeric_limits::min(); IT sup = std::numeric_limits::max(); KNHeap< IT, IVT > sHeap(sup, inf); // max size: flops IT k = 0; // index to indx vector IT i = 0; // index to columns of matrix while(i< Adcsc.nzc && k < veclen) { if(Adcsc.jc[i] < indx[k]) ++i; else if(indx[k] < Adcsc.jc[i]) ++k; else { for(IT j=Adcsc.cp[i]; j < Adcsc.cp[i+1]; ++j) // for all nonzeros in this column { sHeap.insert(Adcsc.ir[j], numx[k]); // row_id, num } ++i; ++k; } } IT row; IVT num; if(sHeap.getSize() > 0) { sHeap.deleteMin(&row, &num); indy.push_back( (int32_t) row); numy.push_back( num ); } while(sHeap.getSize() > 0) { sHeap.deleteMin(&row, &num); if(indy.back() == row) { numy.back() = SR::add(numy.back(), num); } else { indy.push_back( (int32_t) row); numy.push_back(num); } } } /** * @param[in,out] indy,numy,cnts {preallocated arrays to be filled} * @param[in] dspls {displacements to preallocated indy,numy buffers} * This version determines the receiving column neighbor and adjust the indices to the receiver's local index * If IVT and OVT are different, then OVT should allow implicit conversion from IVT **/ template void SpImpl::SpMXSpV(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, int32_t * indy, OVT * numy, int * cnts, int * dspls, int p_c) { OVT * localy = new OVT[mA]; BitMap isthere(mA); std::vector< std::vector > nzinds(p_c); // nonzero indices int32_t perproc = mA / p_c; int32_t k = 0; // index to indx vector IT i = 0; // index to columns of matrix while(i< Adcsc.nzc && k < veclen) { if(Adcsc.jc[i] < indx[k]) ++i; else if(indx[k] < Adcsc.jc[i]) ++k; else { for(IT j=Adcsc.cp[i]; j < Adcsc.cp[i+1]; ++j) // for all nonzeros in this column { int32_t rowid = (int32_t) Adcsc.ir[j]; if(!isthere.get_bit(rowid)) { int32_t owner = std::min(rowid / perproc, static_cast(p_c-1)); localy[rowid] = numx[k]; // initial assignment, requires implicit conversion if IVT != OVT nzinds[owner].push_back(rowid); isthere.set_bit(rowid); } else { localy[rowid] = SR::add(localy[rowid], numx[k]); } } ++i; ++k; } } for(int p = 0; p< p_c; ++p) { sort(nzinds[p].begin(), nzinds[p].end()); cnts[p] = nzinds[p].size(); int32_t * locnzinds = &nzinds[p][0]; int32_t offset = perproc * p; for(int i=0; i< cnts[p]; ++i) { indy[dspls[p]+i] = locnzinds[i] - offset; // convert to local offset numy[dspls[p]+i] = localy[locnzinds[i]]; } } delete [] localy; } // this version is still very good with splitters template void SpImpl::SpMXSpV_ForThreading(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy, int32_t offset) { std::vector localy(mA); BitMap isthere(mA); std::vector nzinds; // nonzero indices SpMXSpV_ForThreading(Adcsc, mA, indx, numx, veclen, indy, numy, offset, localy, isthere, nzinds); } //! We can safely use a SPA here because Adcsc is short (::RowSplit() has already been called on it) template void SpImpl::SpMXSpV_ForThreading(const Dcsc & Adcsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy, int32_t offset, std::vector & localy, BitMap & isthere, std::vector & nzinds) { // The following piece of code is not general, but it's more memory efficient than FillColInds int32_t k = 0; // index to indx vector IT i = 0; // index to columns of matrix while(i< Adcsc.nzc && k < veclen) { if(Adcsc.jc[i] < indx[k]) ++i; else if(indx[k] < Adcsc.jc[i]) ++k; else { for(IT j=Adcsc.cp[i]; j < Adcsc.cp[i+1]; ++j) // for all nonzeros in this column { uint32_t rowid = (uint32_t) Adcsc.ir[j]; if(!isthere.get_bit(rowid)) { localy[rowid] = numx[k]; // initial assignment nzinds.push_back(rowid); isthere.set_bit(rowid); } else { localy[rowid] = SR::add(localy[rowid], numx[k]); } } ++i; ++k; } } int nnzy = nzinds.size(); integerSort(nzinds.data(), nnzy); indy.resize(nnzy); numy.resize(nnzy); for(int i=0; i< nnzy; ++i) { indy[i] = nzinds[i] + offset; // return column-global index and let gespmv determine the receiver's local index numy[i] = localy[nzinds[i]]; } } /** * SpMXSpV with HeapSort * Simply insert entries from columns corresponsing to nonzeros of the input vector into a minHeap * Then extract entries from the minHeap * Complexity: O(flops*log(flops)) * offset is the offset of indices in the matrix in case the matrix is split * This version is likely to be more memory efficient than the other one (the one that uses preallocated memory buffers) * Because here we don't use a dense accumulation vector but a heap. It will probably be slower though. **/ template void SpMXSpV_HeapSort(const Csc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector & numy, int32_t offset) { IT inf = std::numeric_limits::min(); IT sup = std::numeric_limits::max(); KNHeap< IT, OVT > sHeap(sup, inf); for (int32_t k = 0; k < veclen; ++k) { IT colid = indx[k]; for(IT j=Acsc.jc[colid]; j < Acsc.jc[colid+1]; ++j) { OVT val = SR::multiply( Acsc.num[j], numx[k]); sHeap.insert(Acsc.ir[j], val); } } IT row; OVT num; if(sHeap.getSize() > 0) { sHeap.deleteMin(&row, &num); row += offset; indy.push_back( (int32_t) row); numy.push_back( num ); } while(sHeap.getSize() > 0) { sHeap.deleteMin(&row, &num); row += offset; if(indy.back() == row) { numy.back() = SR::add(numy.back(), num); } else { indy.push_back( (int32_t) row); numy.push_back(num); } } } template void SpMXSpV_Bucket(const Csc & Acsc, int32_t mA, const int32_t * indx, const IVT * numx, int32_t veclen, std::vector & indy, std::vector< OVT > & numy, PreAllocatedSPA & SPA) { if(veclen==0) return; double tstart = MPI_Wtime(); int nthreads=1; int rowSplits = SPA.buckets; // SPA must be initialized as checked in SpImpl.h #ifdef _OPENMP #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif if(rowSplits < nthreads) { std::ostringstream outs; outs << "Warning in SpMXSpV_Bucket: " << rowSplits << " buckets are supplied for " << nthreads << " threads\n"; outs << "4 times the number of threads are recommended when creating PreAllocatedSPA\n"; SpParHelper::Print(outs.str()); } int32_t rowPerSplit = mA / rowSplits; //------------------------------------------------------ // Step1: count the nnz in each rowsplit of the matrix, // because we don't want to waste memory // False sharing is not a big problem because it is written outside of the main loop //------------------------------------------------------ std::vector> bSize(rowSplits, std::vector ( rowSplits, 0)); std::vector> bOffset(rowSplits, std::vector ( rowSplits, 0)); std::vector sendSize(rowSplits); double t0, t1, t2, t3, t4; #ifdef BENCHMARK_SPMSPV t0 = MPI_Wtime(); #endif #ifdef _OPENMP #pragma omp parallel for schedule(dynamic, 1) #endif for(int b=0; b temp(rowSplits,0); for (int32_t i = xstart; i < xend; ++i) { IT colid = indx[i]; for(IT j=Acsc.jc[colid]; j < Acsc.jc[colid+1]; ++j) { uint32_t rowid = (uint32_t) Acsc.ir[j]; int32_t splitId = rowSplits-1; if(rowPerSplit!=0) splitId = (rowid/rowPerSplit > rowSplits-1) ? rowSplits-1 : rowid/rowPerSplit; //bSize[b][splitId]++; temp[splitId]++; } } int32_t totSend = 0; for(int k=0; k disp(rowSplits+1); int maxBucketSize = -1; // maximum size of a bucket disp[0] = 0; for(int j=0; jL2_CACHE_SIZE ) THREAD_BUF_LEN/=2; else break; } THREAD_BUF_LEN = std::min(maxBucketSize+1,THREAD_BUF_LEN); #ifdef BENCHMARK_SPMSPV t0 = MPI_Wtime(); #endif #ifdef _OPENMP #pragma omp parallel #endif { int32_t* tIndSplitA = new int32_t[rowSplits*THREAD_BUF_LEN]; OVT* tNumSplitA = new OVT[rowSplits*THREAD_BUF_LEN]; std::vector tBucketSize(rowSplits); std::vector tOffset(rowSplits); #ifdef _OPENMP #pragma omp for schedule(dynamic,1) #endif for(int b=0; b rowSplits-1) ? rowSplits-1 : rowid/rowPerSplit; if (tBucketSize[splitId] < THREAD_BUF_LEN) { tIndSplitA[splitId*THREAD_BUF_LEN + tBucketSize[splitId]] = rowid; tNumSplitA[splitId*THREAD_BUF_LEN + tBucketSize[splitId]++] = val; } else { std::copy(tIndSplitA + splitId*THREAD_BUF_LEN, tIndSplitA + (splitId+1)*THREAD_BUF_LEN, &SPA.indSplitA[disp[splitId] + bOffset[b][splitId]] + tOffset[splitId]); std::copy(tNumSplitA + splitId*THREAD_BUF_LEN, tNumSplitA + (splitId+1)*THREAD_BUF_LEN, &SPA.numSplitA[disp[splitId] + bOffset[b][splitId]] + tOffset[splitId]); tIndSplitA[splitId*THREAD_BUF_LEN] = rowid; tNumSplitA[splitId*THREAD_BUF_LEN] = val; tOffset[splitId] += THREAD_BUF_LEN ; tBucketSize[splitId] = 1; } } } for(int splitId=0; splitId0) { std::copy(tIndSplitA + splitId*THREAD_BUF_LEN, tIndSplitA + splitId*THREAD_BUF_LEN + tBucketSize[splitId], &SPA.indSplitA[disp[splitId] + bOffset[b][splitId]] + tOffset[splitId]); std::copy(tNumSplitA + splitId*THREAD_BUF_LEN, tNumSplitA + splitId*THREAD_BUF_LEN + tBucketSize[splitId], &SPA.numSplitA[disp[splitId] + bOffset[b][splitId]] + tOffset[splitId]); } } } delete [] tIndSplitA; delete [] tNumSplitA; } #ifdef BENCHMARK_SPMSPV t2 = MPI_Wtime() - t0; t0 = MPI_Wtime(); #endif std::vector nzInRowSplits(rowSplits); uint32_t* nzinds = new uint32_t[disp[rowSplits]]; #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,1) #endif for(int rs=0; rs dispRowSplits(rowSplits+1); dispRowSplits[0] = 0; for(int i=0; i 0) { std::copy(tnumy, tnumy+curSize, numy.begin()+dispRowSplits[rs]+tdisp); } } delete [] tnumy; delete [] tindy; } #ifdef BENCHMARK_SPMSPV t4 = MPI_Wtime() - t0; #endif delete[] nzinds; #ifdef BENCHMARK_SPMSPV double tall = MPI_Wtime() - tstart; std::ostringstream outs1; outs1 << "Time breakdown of SpMSpV-bucket." << std::endl; outs1 << "Estimate buckets: "<< t1 << " Bucketing: " << t2 << " SPA-merge: " << t3 << " Output: " << t4 << " Total: "<< tall << std::endl; SpParHelper::Print(outs1.str()); #endif } } CombBLAS_beta_16_2/include/CombBLAS/papi_combblas_globals.h000644 000765 000024 00000001643 13271404146 025046 0ustar00aydinbulucstaff000000 000000 #ifndef PAPI_COMBBLAS_GLOBALS_ #define PAPI_COMBBLAS_GLOBALS_ #include #include #include char spmv_errorstring[PAPI_MAX_STR_LEN+1]; std::string bfs_papi_labels = {"SpMV", "fringe_updt", "parents_updt"}; int num_bfs_papi_labels = 3; enum bfs_papi_enum { SpMV, fringe_updt, parents_updt }; std::string spmv_papi_labels = {"Fan-Out", "LocalSpMV", "Fan-In", "Merge"}; std::string combblas_event_names [] = {"PAPI_TOT_INS", "PAPI_L1_TCM", "PAPI_L2_TCM", "PAPI_L3_TCM"}; int combblas_papi_events [] = {PAPI_TOT_INS, PAPI_L1_TCM, PAPI_L2_TCM, PAPI_L3_TCM}; int combblas_papi_num_events = 4; // outer index: SpMV iteration, middle index: papi_labels, inner index: papi events // dimensions: <#iterations> std::vector< std::vector< std::vector > > bfs_counters; std::vector< std::vector< std::vector > > spmv_counters; #endif CombBLAS_beta_16_2/include/CombBLAS/FullyDistSpVec.cpp000644 000765 000024 00000232051 13271404146 023762 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include #include "FullyDistSpVec.h" #include "SpDefs.h" #include "SpHelper.h" #include "hash.hpp" #include "FileHeader.h" #include #include #ifdef GNU_PARALLEL #include #include #endif #include "usort/parUtils.h" namespace combblas { template FullyDistSpVec::FullyDistSpVec ( std::shared_ptr grid) : FullyDist::value, NT >::type>(grid) { }; template FullyDistSpVec::FullyDistSpVec ( std::shared_ptr grid, IT globallen) : FullyDist::value, NT >::type>(grid,globallen) { }; template FullyDistSpVec::FullyDistSpVec () : FullyDist::value, NT >::type>() { }; template FullyDistSpVec::FullyDistSpVec (IT globallen) : FullyDist::value, NT >::type>(globallen) { } template FullyDistSpVec & FullyDistSpVec::operator=(const FullyDistSpVec< IT,NT > & rhs) { if(this != &rhs) { FullyDist::value, NT >::type>::operator= (rhs); // to update glen and commGrid ind = rhs.ind; num = rhs.num; } return *this; } template FullyDistSpVec::FullyDistSpVec (const FullyDistVec & rhs) // Conversion copy-constructor : FullyDist::value, NT >::type>(rhs.commGrid,rhs.glen) { *this = rhs; } // Conversion copy-constructor where unary op is true template template FullyDistSpVec::FullyDistSpVec (const FullyDistVec & rhs, _UnaryOperation unop) : FullyDist::value, NT >::type>(rhs.commGrid,rhs.glen) { //FullyDist::value, NT >::type>::operator= (rhs); // to update glen and commGrid std::vector().swap(ind); std::vector().swap(num); IT vecsize = rhs.LocArrSize(); for(IT i=0; i< vecsize; ++i) { if(unop(rhs.arr[i])) { ind.push_back(i); num.push_back(rhs.arr[i]); } } } // create a sparse vector from local vectors template FullyDistSpVec::FullyDistSpVec (std::shared_ptr grid, IT globallen, const std::vector& indvec, const std::vector & numvec, bool SumDuplicates, bool sorted) : FullyDist::value, NT >::type>(grid, globallen) { assert(indvec.size()==numvec.size()); IT vecsize = indvec.size(); if(!sorted) { std::vector< std::pair > tosort(vecsize); #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; ifirst) //if SumDuplicates=false, keep only the first one { ind.push_back(itr->first); num.push_back(itr->second); lastIndex = itr->first; } else if(SumDuplicates) { num.back() += itr->second; } } } else { ind.reserve(vecsize); num.reserve(vecsize); IT lastIndex=-1; for(IT i=0; i< vecsize; ++i) { if(lastIndex!=indvec[i]) //if SumDuplicates=false, keep only the first one { ind.push_back(indvec[i]); num.push_back(numvec[i]); lastIndex = indvec[i]; } else if(SumDuplicates) { num.back() += numvec[i]; } } } } // ABAB: This function probably operates differently than a user would immediately expect // ABAB: Write a well-posed description for it template FullyDistSpVec & FullyDistSpVec::operator=(const FullyDistVec< IT,NT > & rhs) // conversion from dense { FullyDist::value, NT >::type>::operator= (rhs); // to update glen and commGrid std::vector().swap(ind); std::vector().swap(num); IT vecsize = rhs.LocArrSize(); for(IT i=0; i< vecsize; ++i) { // rhs.zero does not exist after CombBLAS 1.2 ind.push_back(i); num.push_back(rhs.arr[i]); } return *this; } /************************************************************************ * Create a sparse vector from index and value vectors (dense vectors) * FullyDistSpVec v(globalsize, inds, vals): * nnz(v) = size(inds) = size(vals) * size(v) = globallen * if inds has duplicate entries and SumDuplicates is true then we sum * the values of duplicate indices. Otherwise, only the first entry is kept. ************************************************************************/ template FullyDistSpVec::FullyDistSpVec (IT globallen, const FullyDistVec & inds, const FullyDistVec & vals, bool SumDuplicates) : FullyDist::value, NT >::type>(inds.commGrid,globallen) { if(*(inds.commGrid) != *(vals.commGrid)) { SpParHelper::Print("Grids are not comparable, FullyDistSpVec() fails !"); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } if(inds.TotalLength() != vals.TotalLength()) { SpParHelper::Print("Index and value vectors have different sizes, FullyDistSpVec() fails !"); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } //commGrid = inds.commGrid; //glen = globallen; IT maxind = inds.Reduce(maximum(), (IT) 0); if(maxind>=globallen) { SpParHelper::Print("At least one index is greater than globallen, FullyDistSpVec() fails !"); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int * rdispls = new int[nprocs]; int * recvcnt = new int[nprocs]; int * sendcnt = new int[nprocs](); // initialize to 0 int * sdispls = new int[nprocs]; // ----- share count -------- IT locsize = inds.LocArrSize(); for(IT i=0; i(0)); // ----- Send and receive indices and values -------- NT * recvdatbuf = new NT[totrecv]; MPI_Alltoallv(datbuf, sendcnt, sdispls, MPIType(), recvdatbuf, recvcnt, rdispls, MPIType(), World); delete [] datbuf; IT * recvindbuf = new IT[totrecv]; MPI_Alltoallv(indbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), World); delete [] indbuf; // ------ merge and sort received data ---------- std::vector< std::pair > tosort; tosort.resize(totrecv); for(int i=0; ifirst) //if SumDuplicates=false, keep only the first one { ind.push_back(itr->first); num.push_back(itr->second); lastIndex = itr->first; } else if(SumDuplicates) { num.back() += itr->second; } } } //! Returns a dense vector of nonzero values //! for which the predicate is satisfied on values template template FullyDistVec FullyDistSpVec::FindVals(_Predicate pred) const { FullyDistVec found(commGrid); MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int rank = commGrid->GetRank(); IT sizelocal = getlocnnz(); for(IT i=0; i(), dist, 1, MPIType(), World); IT lengthuntil = std::accumulate(dist, dist+rank, static_cast(0)); found.glen = std::accumulate(dist, dist+nprocs, static_cast(0)); // Although the found vector is not reshuffled yet, its glen and commGrid are set // We can call the Owner/MyLocLength/LengthUntil functions (to infer future distribution) // rebalance/redistribute int * sendcnt = new int[nprocs]; std::fill(sendcnt, sendcnt+nprocs, 0); for(IT i=0; i(0)); std::vector recvbuf(totrecv); // data is already in the right order in found.arr MPI_Alltoallv(found.arr.data(), sendcnt, sdispls, MPIType(), recvbuf.data(), recvcnt, rdispls, MPIType(), World); found.arr.swap(recvbuf); delete [] dist; DeleteAll(sendcnt, recvcnt, sdispls, rdispls); return found; } //! Returns a dense vector of nonzero global indices //! for which the predicate is satisfied on values template template FullyDistVec FullyDistSpVec::FindInds(_Predicate pred) const { FullyDistVec found(commGrid); MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int rank = commGrid->GetRank(); IT sizelocal = getlocnnz(); IT sizesofar = LengthUntil(); for(IT i=0; i(), dist, 1, MPIType(), World); IT lengthuntil = std::accumulate(dist, dist+rank, static_cast(0)); found.glen = std::accumulate(dist, dist+nprocs, static_cast(0)); // Although the found vector is not reshuffled yet, its glen and commGrid are set // We can call the Owner/MyLocLength/LengthUntil functions (to infer future distribution) // rebalance/redistribute int * sendcnt = new int[nprocs]; std::fill(sendcnt, sendcnt+nprocs, 0); for(IT i=0; i(0)); std::vector recvbuf(totrecv); // data is already in the right order in found.arr MPI_Alltoallv(found.arr.data(), sendcnt, sdispls, MPIType(), recvbuf.data(), recvcnt, rdispls, MPIType(), World); found.arr.swap(recvbuf); delete [] dist; DeleteAll(sendcnt, recvcnt, sdispls, rdispls); return found; } template void FullyDistSpVec::stealFrom(FullyDistSpVec & victim) { FullyDist::value, NT >::type>::operator= (victim); // to update glen and commGrid ind.swap(victim.ind); num.swap(victim.num); } template NT FullyDistSpVec::operator[](IT indx) { NT val; IT locind; int owner = Owner(indx, locind); int found = 0; if(commGrid->GetRank() == owner) { typename std::vector::const_iterator it = std::lower_bound(ind.begin(), ind.end(), locind); // ind is a sorted vector if(it != ind.end() && locind == (*it)) // found { val = num[it-ind.begin()]; found = 1; } else { val = NT(); // return NULL found = 0; } } MPI_Bcast(&found, 1, MPI_INT, owner, commGrid->GetWorld()); MPI_Bcast(&val, 1, MPIType(), owner, commGrid->GetWorld()); wasFound = found; return val; } template NT FullyDistSpVec::GetLocalElement(IT indx) { NT val = NT(); IT locind; int owner = Owner(indx, locind); int found = 0; typename std::vector::const_iterator it = std::lower_bound(ind.begin(), ind.end(), locind); // ind is a sorted vector if(commGrid->GetRank() == owner) { if(it != ind.end() && locind == (*it)) // found { val = num[it-ind.begin()]; found = 1; } } wasFound = found; return val; } //! Indexing is performed 0-based template void FullyDistSpVec::SetElement (IT indx, NT numx) { if(glen == 0) SpParHelper::Print("WARNING: SetElement() called on a vector with zero length\n"); IT locind; int owner = Owner(indx, locind); if(commGrid->GetRank() == owner) { typename std::vector::iterator iter = std::lower_bound(ind.begin(), ind.end(), locind); if(iter == ind.end()) // beyond limits, insert from back { ind.push_back(locind); num.push_back(numx); } else if (locind < *iter) // not found, insert in the middle { // the order of insertions is crucial // if we first insert to ind, then ind.begin() is invalidated ! num.insert(num.begin() + (iter-ind.begin()), numx); ind.insert(iter, locind); } else // found { *(num.begin() + (iter-ind.begin())) = numx; } } } template void FullyDistSpVec::DelElement (IT indx) { IT locind; int owner = Owner(indx, locind); if(commGrid->GetRank() == owner) { typename std::vector::iterator iter = std::lower_bound(ind.begin(), ind.end(), locind); if(iter != ind.end() && !(locind < *iter)) { num.erase(num.begin() + (iter-ind.begin())); ind.erase(iter); } } } /** * The distribution and length are inherited from ri * Its zero is inherited from *this (because ri is of type IT) * Example: This is [{1,n1},{4,n4},{7,n7},{8,n8},{9,n9}] with P_00 owning {1,4} and P_11 rest * Assume ri = [4,1,5,7] is distributed as two elements per processor * Then result has length 4, distrubuted two element per processor, even though 5 and 7 doesn't exist * This is because we are returning a "dense" output, so the absent elements will be padded with 0 **/ template FullyDistVec FullyDistSpVec::operator() (const FullyDistVec & ri) const { MPI_Comm World = commGrid->GetWorld(); FullyDistVec Indexed(ri.commGrid, ri.glen, NT()); // NT() is the initial value int nprocs; MPI_Comm_size(World, &nprocs); std::unordered_map revr_map; // inverted index that maps indices of *this to indices of output std::vector< std::vector > data_req(nprocs); IT locnnz = ri.LocArrSize(); // ABAB: Input sanity check int local = 1; int whole = 1; for(IT i=0; i < locnnz; ++i) { if(ri.arr[i] >= glen || ri.arr[i] < 0) { local = 0; } } MPI_Allreduce( &local, &whole, 1, MPI_INT, MPI_BAND, World); if(whole == 0) { throw outofrangeexception(); } for(IT i=0; i < locnnz; ++i) { IT locind; int owner = Owner(ri.arr[i], locind); // numerical values in ri are 0-based data_req[owner].push_back(locind); revr_map.insert(typename std::unordered_map::value_type(locind, i)); } IT * sendbuf = new IT[locnnz]; int * sendcnt = new int[nprocs]; int * sdispls = new int[nprocs]; for(int i=0; i().swap(data_req[i]); } MPI_Alltoallv(sendbuf, sendcnt, sdispls, MPIType(), recvbuf, recvcnt, rdispls, MPIType(), World); // request data // We will return the requested data, // our return can be at most as big as the request // and smaller if we are missing some elements IT * indsback = new IT[totrecv]; NT * databack = new NT[totrecv]; int * ddispls = new int[nprocs]; std::copy(rdispls, rdispls+nprocs, ddispls); for(int i=0; i ind[vi]) ++vi; databack[j] = num[vi++]; } } DeleteAll(recvbuf, ddispls); NT * databuf = new NT[ri.LocArrSize()]; MPI_Alltoall(recvcnt, 1, MPI_INT, sendcnt, 1, MPI_INT, World); // share the response counts, overriding request counts MPI_Alltoallv(indsback, recvcnt, rdispls, MPIType(), sendbuf, sendcnt, sdispls, MPIType(), World); // send indices MPI_Alltoallv(databack, recvcnt, rdispls, MPIType(), databuf, sendcnt, sdispls, MPIType(), World); // send data DeleteAll(rdispls, recvcnt, indsback, databack); // Now create the output from databuf (holds numerical values) and sendbuf (holds indices) // arr is already resized during its construction for(int i=0; i::iterator it = revr_map.find(sendbuf[j]); Indexed.arr[it->second] = databuf[j]; // cout << it->second << "(" << sendbuf[j] << "):" << databuf[j] << endl; } } DeleteAll(sdispls, sendcnt, sendbuf, databuf); return Indexed; } template void FullyDistSpVec::iota(IT globalsize, NT first) { glen = globalsize; IT length = MyLocLength(); ind.resize(length); num.resize(length); SpHelper::iota(ind.begin(), ind.end(), 0); // offset'd within processors SpHelper::iota(num.begin(), num.end(), LengthUntil() + first); // global across processors } //! iota over existing nonzero entries template void FullyDistSpVec::nziota(NT first) { std::iota(num.begin(), num.end(), NnzUntil() + first); // global across processors } //! Returns the number of nonzeros until this processor template IT FullyDistSpVec::NnzUntil() const { IT mynnz = ind.size(); IT prevnnz = 0; MPI_Scan(&mynnz, &prevnnz, 1, MPIType(), MPI_SUM, commGrid->GetWorld()); return (prevnnz - mynnz); } /* old version // - sorts the entries with respect to nonzero values // - ignores structural zeros // - keeps the sparsity structure intact // - returns a permutation representing the mapping from old to new locations template FullyDistSpVec FullyDistSpVec::sort() { MPI_Comm World = commGrid->GetWorld(); FullyDistSpVec temp(commGrid); if(getnnz()==0) return temp; IT nnz = getlocnnz(); pair * vecpair = new pair[nnz]; int nprocs, rank; MPI_Comm_size(World, &nprocs); MPI_Comm_rank(World, &rank); IT * dist = new IT[nprocs]; dist[rank] = nnz; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), dist, 1, MPIType(), World); IT until = LengthUntil(); for(IT i=0; i< nnz; ++i) { vecpair[i].first = num[i]; // we'll sort wrt numerical values vecpair[i].second = ind[i] + until; } SpParHelper::MemoryEfficientPSort(vecpair, nnz, dist, World); vector< IT > nind(nnz); vector< IT > nnum(nnz); for(IT i=0; i< nnz; ++i) { num[i] = vecpair[i].first; // sorted range (change the object itself) nind[i] = ind[i]; // make sure the sparsity distribution is the same nnum[i] = vecpair[i].second; // inverse permutation stored as numerical values } delete [] vecpair; delete [] dist; temp.glen = glen; temp.ind = nind; temp.num = nnum; return temp; } */ /* TODO: This function is just a hack at this moment. The indices of the return vector is not correct. FIX this */ // - sorts the entries with respect to nonzero values // - ignores structural zeros // - keeps the sparsity structure intact // - returns a permutation representing the mapping from old to new locations template FullyDistSpVec FullyDistSpVec::sort() { MPI_Comm World = commGrid->GetWorld(); FullyDistSpVec temp(commGrid); if(getnnz()==0) return temp; IT nnz = getlocnnz(); std::pair * vecpair = new std::pair[nnz]; int nprocs, rank; MPI_Comm_size(World, &nprocs); MPI_Comm_rank(World, &rank); IT * dist = new IT[nprocs]; dist[rank] = nnz; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), dist, 1, MPIType(), World); IT until = LengthUntil(); #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i< nnz; ++i) { vecpair[i].first = num[i]; // we'll sort wrt numerical values vecpair[i].second = ind[i] + until; } std::vector> sorted = SpParHelper::KeyValuePSort(vecpair, nnz, dist, World); nnz = sorted.size(); temp.num.resize(nnz); temp.ind.resize(nnz); #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i< nnz; ++i) { //num[i] = sorted[i].first; // sorted range (change the object itself) //nind[i] = ind[i]; // make sure the sparsity distribution is the same temp.num[i] = sorted[i].second; // inverse permutation stored as numerical values temp.ind[i] = i; // we are not using this information at this moment } delete [] vecpair; delete [] dist; temp.glen = glen; return temp; } /* // - sorts the entries with respect to nonzero values // - ignores structural zeros // - keeps the sparsity structure intact // - returns a permutation representing the mapping from old to new locations template FullyDistSpVec FullyDistSpVec::sort() { MPI_Comm World = commGrid->GetWorld(); FullyDistSpVec temp(commGrid); if(getnnz()==0) return temp; IT nnz = getlocnnz(); //pair * vecpair = new pair[nnz]; vector> in(nnz); //vector> out; int nprocs, rank; MPI_Comm_size(World, &nprocs); MPI_Comm_rank(World, &rank); //IT * dist = new IT[nprocs]; //dist[rank] = nnz; //MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), dist, 1, MPIType(), World); IT until = LengthUntil(); for(IT i=0; i< nnz; ++i) { //vecpair[i].first = num[i]; // we'll sort wrt numerical values //vecpair[i].second = ind[i] + until; in[i] = IndexHolder(num[i], static_cast(ind[i] + until)); } //SpParHelper::MemoryEfficientPSort(vecpair, nnz, dist, World); //MPI_Barrier(World); //cout << "before sorting " << in.size() << endl; par::sampleSort(in, World); //MPI_Barrier(World); //cout << "after sorting " << in.size() << endl; //MPI_Barrier(World); //vector< IT > nind(out.size()); //vector< IT > nnum(out.size()); temp.ind.resize(in.size()); temp.num.resize(in.size()); for(IT i=0; i< in.size(); ++i) { //num[i] = vecpair[i].first; // sorted range (change the object itself) //nind[i] = ind[i]; // make sure the sparsity distribution is the same //nnum[i] = vecpair[i].second; // inverse permutation stored as numerical values //num[i] = out[i].value; // sorted range (change the object itself) //nind[i] = ind[i]; // make sure the sparsity distribution is the same //nnum[i] = static_cast(out[i].index); // inverse permutation stored as numerical values temp.num[i] = static_cast(in[i].index); // inverse permutation stored as numerical values //cout << temp.num[i] << " " ; } temp.glen = glen; return temp; } */ template template FullyDistSpVec FullyDistSpVec::UniqAll2All(_BinaryOperation __binary_op, MPI_Op mympiop) { MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); std::vector< std::vector< NT > > datsent(nprocs); std::vector< std::vector< IT > > indsent(nprocs); IT locind; size_t locvec = num.size(); // nnz in local vector IT lenuntil = LengthUntil(); // to convert to global index for(size_t i=0; i< locvec; ++i) { uint64_t myhash; // output of MurmurHash3_x64_64 is 64-bits regardless of the input length MurmurHash3_x64_64((const void*) &(num[i]),sizeof(NT), 0, &myhash); double range = static_cast(myhash) * static_cast(glen); NT mapped = range / static_cast(std::numeric_limits::max()); // mapped is in range [0,n) int owner = Owner(mapped, locind); datsent[owner].push_back(num[i]); // all identical entries will be hashed to the same value -> same processor indsent[owner].push_back(ind[i]+lenuntil); } int * sendcnt = new int[nprocs]; int * sdispls = new int[nprocs]; for(int i=0; i().swap(datsent[i]); } IT * indbuf = new IT[locvec]; for(int i=0; i().swap(indsent[i]); } IT totrecv = std::accumulate(recvcnt,recvcnt+nprocs, static_cast(0)); NT * recvdatbuf = new NT[totrecv]; MPI_Alltoallv(datbuf, sendcnt, sdispls, MPIType(), recvdatbuf, recvcnt, rdispls, MPIType(), World); delete [] datbuf; IT * recvindbuf = new IT[totrecv]; MPI_Alltoallv(indbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), World); delete [] indbuf; std::vector< std::pair > tosort; // in fact, tomerge would be a better name but it is unlikely to be faster for(int i=0; i >::iterator last; last = std::unique (tosort.begin(), tosort.end(), equal_first()); std::vector< std::vector< NT > > datback(nprocs); std::vector< std::vector< IT > > indback(nprocs); for(typename std::vector< std::pair >::iterator itr = tosort.begin(); itr != last; ++itr) { IT locind; int owner = Owner(itr->second, locind); datback[owner].push_back(itr->first); indback[owner].push_back(locind); } for(int i=0; i().swap(datback[i]); } indbuf = new IT[tosort.size()]; for(int i=0; i().swap(indback[i]); } totrecv = std::accumulate(recvcnt,recvcnt+nprocs, static_cast(0)); // update value recvdatbuf = new NT[totrecv]; MPI_Alltoallv(datbuf, sendcnt, sdispls, MPIType(), recvdatbuf, recvcnt, rdispls, MPIType(), World); delete [] datbuf; recvindbuf = new IT[totrecv]; MPI_Alltoallv(indbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), World); delete [] indbuf; FullyDistSpVec Indexed(commGrid, glen); // length(Indexed) = length(glen) = length(*this) std::vector< std::pair > back2sort; for(int i=0; i >::iterator itr = back2sort.begin(); itr != back2sort.end(); ++itr) { Indexed.ind.push_back(itr->first); Indexed.num.push_back(itr->second); } DeleteAll(sdispls, rdispls, sendcnt, recvcnt); DeleteAll(recvindbuf, recvdatbuf); return Indexed; } // ABAB: \todo Concept control so it only gets called in integers template template FullyDistSpVec FullyDistSpVec::Uniq(_BinaryOperation __binary_op, MPI_Op mympiop) { return UniqAll2All(__binary_op, mympiop); } template FullyDistSpVec & FullyDistSpVec::operator+=(const FullyDistSpVec & rhs) { if(this != &rhs) { if(glen != rhs.glen) { std::cerr << "Vector dimensions don't match for addition\n"; return *this; } IT lsize = getlocnnz(); IT rsize = rhs.getlocnnz(); std::vector< IT > nind; std::vector< NT > nnum; nind.reserve(lsize+rsize); nnum.reserve(lsize+rsize); IT i=0, j=0; while(i < lsize && j < rsize) { // assignment won't change the size of vector, push_back is necessary if(ind[i] > rhs.ind[j]) { nind.push_back( rhs.ind[j] ); nnum.push_back( rhs.num[j++] ); } else if(ind[i] < rhs.ind[j]) { nind.push_back( ind[i] ); nnum.push_back( num[i++] ); } else { nind.push_back( ind[i] ); nnum.push_back( num[i++] + rhs.num[j++] ); } } while( i < lsize) // rhs was depleted first { nind.push_back( ind[i] ); nnum.push_back( num[i++] ); } while( j < rsize) // *this was depleted first { nind.push_back( rhs.ind[j] ); nnum.push_back( rhs.num[j++] ); } ind.swap(nind); // ind will contain the elements of nind with capacity shrunk-to-fit size num.swap(nnum); } else { typename std::vector::iterator it; for(it = num.begin(); it != num.end(); ++it) (*it) *= 2; } return *this; }; template FullyDistSpVec & FullyDistSpVec::operator-=(const FullyDistSpVec & rhs) { if(this != &rhs) { if(glen != rhs.glen) { std::cerr << "Vector dimensions don't match for addition\n"; return *this; } IT lsize = getlocnnz(); IT rsize = rhs.getlocnnz(); std::vector< IT > nind; std::vector< NT > nnum; nind.reserve(lsize+rsize); nnum.reserve(lsize+rsize); IT i=0, j=0; while(i < lsize && j < rsize) { // assignment won't change the size of vector, push_back is necessary if(ind[i] > rhs.ind[j]) { nind.push_back( rhs.ind[j] ); nnum.push_back( -static_cast(rhs.num[j++]) ); } else if(ind[i] < rhs.ind[j]) { nind.push_back( ind[i] ); nnum.push_back( num[i++] ); } else { nind.push_back( ind[i] ); nnum.push_back( num[i++] - rhs.num[j++] ); // ignore numerical cancellations } } while( i < lsize) // rhs was depleted first { nind.push_back( ind[i] ); nnum.push_back( num[i++] ); } while( j < rsize) // *this was depleted first { nind.push_back( rhs.ind[j] ); nnum.push_back( NT() - (rhs.num[j++]) ); } ind.swap(nind); // ind will contain the elements of nind with capacity shrunk-to-fit size num.swap(nnum); } else { ind.clear(); num.clear(); } return *this; }; template template void FullyDistSpVec::SparseCommon(std::vector< std::vector < std::pair > > & data, _BinaryOperation BinOp) { int nprocs = commGrid->GetSize(); int * sendcnt = new int[nprocs]; int * recvcnt = new int[nprocs]; for(int i=0; iGetWorld()); // share the counts int * sdispls = new int[nprocs](); int * rdispls = new int[nprocs](); std::partial_sum(sendcnt, sendcnt+nprocs-1, sdispls+1); std::partial_sum(recvcnt, recvcnt+nprocs-1, rdispls+1); IT totrecv = rdispls[nprocs-1]+recvcnt[nprocs-1]; IT totsend = sdispls[nprocs-1]+sendcnt[nprocs-1]; std::pair * senddata = new std::pair[totsend]; // re-used for both rows and columns for(int i=0; i >().swap(data[i]); // clear memory } MPI_Datatype MPI_pair; MPI_Type_contiguous(sizeof(std::tuple), MPI_CHAR, &MPI_pair); MPI_Type_commit(&MPI_pair); std::pair * recvdata = new std::pair[totrecv]; MPI_Alltoallv(senddata, sendcnt, sdispls, MPI_pair, recvdata, recvcnt, rdispls, MPI_pair, commGrid->GetWorld()); DeleteAll(senddata, sendcnt, recvcnt, sdispls, rdispls); MPI_Type_free(&MPI_pair); if(!is_sorted(recvdata, recvdata+totrecv)) std::sort(recvdata, recvdata+totrecv); ind.push_back(recvdata[0].first); num.push_back(recvdata[0].second); for(IT i=1; i< totrecv; ++i) { if(ind.back() == recvdata[i].first) { num.back() = BinOp(num.back(), recvdata[i].second); } else { ind.push_back(recvdata[i].first); num.push_back(recvdata[i].second); } } delete [] recvdata; } template template void FullyDistSpVec::ParallelRead (const std::string & filename, bool onebased, _BinaryOperation BinOp) { int64_t gnnz; // global nonzeros (glen is already declared as part of this class's private data) int64_t linesread = 0; FILE *f; int myrank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); if(myrank == 0) { if((f = fopen(filename.c_str(),"r"))==NULL) { std::cout << "File failed to open\n"; MPI_Abort(commGrid->commWorld, NOFILE); } else { fscanf(f,"%lld %lld\n", &glen, &gnnz); } std::cout << "Total number of nonzeros expected across all processors is " << gnnz << std::endl; } MPI_Bcast(&glen, 1, MPIType(), 0, commGrid->commWorld); MPI_Bcast(&gnnz, 1, MPIType(), 0, commGrid->commWorld); struct stat st; // get file size if (stat(filename.c_str(), &st) == -1) { MPI_Abort(commGrid->commWorld, NOFILE); } int64_t file_size = st.st_size; MPI_Offset fpos, end_fpos; if(myrank == 0) // the offset needs to be for this rank { std::cout << "File is " << file_size << " bytes" << std::endl; fpos = ftell(f); fclose(f); } else { fpos = myrank * file_size / nprocs; } if(myrank != (nprocs-1)) end_fpos = (myrank + 1) * file_size / nprocs; else end_fpos = file_size; MPI_Barrier(commGrid->commWorld); MPI_File mpi_fh; MPI_File_open (commGrid->commWorld, const_cast(filename.c_str()), MPI_MODE_RDONLY, MPI_INFO_NULL, &mpi_fh); std::vector< std::vector < std::pair > > data(nprocs); // data to send std::vector lines; SpParHelper::Print("Fetching first piece\n"); MPI_Barrier(commGrid->commWorld); bool finished = SpParHelper::FetchBatch(mpi_fh, fpos, end_fpos, true, lines, myrank); int64_t entriesread = lines.size(); SpParHelper::Print("Fetched first piece\n"); MPI_Barrier(commGrid->commWorld); IT ii; NT vv; for (auto itr=lines.begin(); itr != lines.end(); ++itr) { std::stringstream ss(*itr); ss >> ii >> vv; if(onebased) ii--; IT locind; int owner = Owner(ii, locind); // recipient (owner) processor data[owner].push_back(std::make_pair(locind,vv)); } while(!finished) { finished = SpParHelper::FetchBatch(mpi_fh, fpos, end_fpos, false, lines, myrank); entriesread += lines.size(); for (auto itr=lines.begin(); itr != lines.end(); ++itr) { std::stringstream ss(*itr); ss >> ii >> vv; if(onebased) ii--; IT locind; int owner = Owner(ii, locind); // recipient (owner) processor data[owner].push_back(std::make_pair(locind,vv)); } } int64_t allentriesread; MPI_Reduce(&entriesread, &allentriesread, 1, MPIType(), MPI_SUM, 0, commGrid->commWorld); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "Reading finished. Total number of entries read across all processors is " << allentriesread << std::endl; #endif SparseCommon(data, BinOp); } template template void FullyDistSpVec::ParallelWrite(const std::string & filename, bool onebased, HANDLER handler, bool includeindices, bool includeheader) { int myrank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); IT totalLength = TotalLength(); IT totalNNZ = getnnz(); std::stringstream ss; if(includeheader && myrank == 0) { ss << totalLength << '\t' << totalNNZ << '\n'; // rank-0 has the header } IT entries = getlocnnz(); IT sizeuntil = 0; MPI_Exscan( &entries, &sizeuntil, 1, MPIType(), MPI_SUM, commGrid->GetWorld() ); if(myrank == 0) sizeuntil = 0; // because MPI_Exscan says the recvbuf in process 0 is undefined if(includeindices) { if(onebased) sizeuntil += 1; // increment by 1 for(IT i=0; i< entries; ++i) { ss << ind[i]+sizeuntil << '\t'; handler.save(ss, num[i], ind[i]+sizeuntil); ss << '\n'; } } else // the base doesn't matter if we don't include indices { IT dummy = 0; // dummy because we don't want indices to be printed for(IT i=0; i< entries; ++i) { handler.save(ss, num[i], dummy); ss << '\n'; } } std::string text = ss.str(); int64_t * bytes = new int64_t[nprocs]; bytes[myrank] = text.size(); MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), bytes, 1, MPIType(), commGrid->GetWorld()); int64_t bytesuntil = std::accumulate(bytes, bytes+myrank, static_cast(0)); int64_t bytestotal = std::accumulate(bytes, bytes+nprocs, static_cast(0)); if(myrank == 0) // only leader rights the original file with no content { std::ofstream ofs(filename.c_str(), std::ios::binary | std::ios::out); #ifdef COMBBLAS_DEBUG std::cout << "Creating file with " << bytestotal << " bytes" << std::endl; #endif ofs.seekp(bytestotal - 1); ofs.write("", 1); // this will likely create a sparse file so the actual disks won't spin yet ofs.close(); } MPI_Barrier(commGrid->GetWorld()); struct stat st; // get file size if (stat(filename.c_str(), &st) == -1) { MPI_Abort(commGrid->GetWorld(), NOFILE); } if(myrank == nprocs-1) // let some other processor do the testing { #ifdef COMBBLAS_DEBUG std::cout << "File is actually " << st.st_size << " bytes seen from process " << myrank << std::endl; #endif } FILE *ffinal; if ((ffinal = fopen(filename.c_str(), "rb+")) == NULL) // then everyone fills it { printf("COMBBLAS: Vector output file %s failed to open at process %d\n", filename.c_str(), myrank); MPI_Abort(commGrid->GetWorld(), NOFILE); } fseek (ffinal , bytesuntil , SEEK_SET ); fwrite(text.c_str(),1, bytes[myrank] ,ffinal); fflush(ffinal); fclose(ffinal); delete [] bytes; } //! Called on an existing object //! ABAB: Obsolete, will be deleted once moved to Github (and becomes independent of KDT) template template std::ifstream& FullyDistSpVec::ReadDistribute (std::ifstream& infile, int master, HANDLER handler) { IT total_nnz; MPI_Comm World = commGrid->GetWorld(); int neighs = commGrid->GetSize(); // number of neighbors (including oneself) int buffperneigh = MEMORYINBYTES / (neighs * (sizeof(IT) + sizeof(NT))); int * displs = new int[neighs]; for (int i=0; iGetRank(); if(rank == master) // 1 processor only { inds = new IT [ buffperneigh * neighs ]; vals = new NT [ buffperneigh * neighs ]; curptrs = new int[neighs]; std::fill_n(curptrs, neighs, 0); // fill with zero if (infile.is_open()) { infile.clear(); infile.seekg(0); IT numrows, numcols; bool indIsRow = true; infile >> numrows >> numcols >> total_nnz; if (numcols == 1) { // column vector, read vector indices from the row index indIsRow = true; glen = numrows; } else { // row vector, read vector indices from the column index indIsRow = false; glen = numcols; } MPI_Bcast(&glen, 1, MPIType(), master, World); IT tempind; IT temprow, tempcol; IT cnz = 0; while ( (!infile.eof()) && cnz < total_nnz) { infile >> temprow >> tempcol; if (indIsRow) tempind = temprow; else tempind = tempcol; tempind--; IT locind; int rec = Owner(tempind, locind); // recipient (owner) processor (ABAB: But if the length is not set yet, this should be wrong) inds[ rec * buffperneigh + curptrs[rec] ] = locind; vals[ rec * buffperneigh + curptrs[rec] ] = handler.read(infile, tempind); ++ (curptrs[rec]); if(curptrs[rec] == buffperneigh || (cnz == (total_nnz-1)) ) // one buffer is full, or file is done ! { // first, send the receive counts ... MPI_Scatter(curptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, master, World); // generate space for own recv data ... (use arrays because vector is cripled, if NT=bool) IT * tempinds = new IT[recvcount]; NT * tempvals = new NT[recvcount]; // then, send all buffers that to their recipients ... MPI_Scatterv(inds, curptrs, displs, MPIType(), tempinds, recvcount, MPIType(), master, World); MPI_Scatterv(vals, curptrs, displs, MPIType(), tempvals, recvcount, MPIType(), master, World); // now push what is ours to tuples for(IT i=0; i< recvcount; ++i) { ind.push_back( tempinds[i] ); // already offset'd by the sender num.push_back( tempvals[i] ); } // reset current pointers so that we can reuse {inds,vals} buffers std::fill_n(curptrs, neighs, 0); DeleteAll(tempinds, tempvals); } ++ cnz; } assert (cnz == total_nnz); // Signal the end of file to other processors along the diagonal std::fill_n(curptrs, neighs, std::numeric_limits::max()); MPI_Scatter(curptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, master, World); } else // input file does not exist ! { glen = 0; MPI_Bcast(&glen, 1, MPIType(), master, World); } DeleteAll(inds,vals, curptrs); } else // all other processors { MPI_Bcast(&glen, 1, MPIType(), master, World); while(glen > 0) // otherwise, input file do not exist { // first receive the receive counts ... MPI_Scatter(curptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, master, World); if( recvcount == std::numeric_limits::max()) break; // create space for incoming data ... IT * tempinds = new IT[recvcount]; NT * tempvals = new NT[recvcount]; // receive actual data ... (first 4 arguments are ignored in the receiver side) MPI_Scatterv(inds, curptrs, displs, MPIType(), tempinds, recvcount, MPIType(), master, World); MPI_Scatterv(vals, curptrs, displs, MPIType(), tempvals, recvcount, MPIType(), master, World); // now push what is ours to tuples for(IT i=0; i< recvcount; ++i) { ind.push_back( tempinds[i] ); num.push_back( tempvals[i] ); } DeleteAll(tempinds, tempvals); } } delete [] displs; MPI_Barrier(World); return infile; } template template void FullyDistSpVec::SaveGathered(std::ofstream& outfile, int master, HANDLER handler, bool printProcSplits) { int rank, nprocs; MPI_Comm World = commGrid->GetWorld(); MPI_Comm_rank(World, &rank); MPI_Comm_size(World, &nprocs); MPI_File thefile; char _fn[] = "temp_fullydistspvec"; // AL: this is to avoid the problem that C++ string literals are const char* while C string literals are char*, leading to a const warning (technically error, but compilers are tolerant) int mpi_err = MPI_File_open(World, _fn, MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); if(mpi_err != MPI_SUCCESS) { char mpi_err_str[MPI_MAX_ERROR_STRING]; int mpi_err_strlen; MPI_Error_string(mpi_err, mpi_err_str, &mpi_err_strlen); printf("MPI_File_open failed (%s)\n", mpi_err_str); MPI_Abort(World, 1); } IT * dist = new IT[nprocs]; dist[rank] = getlocnnz(); MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), dist, 1, MPIType(), World); IT sizeuntil = std::accumulate(dist, dist+rank, static_cast(0)); IT totalLength = TotalLength(); IT totalNNZ = getnnz(); struct mystruct { IT ind; NT num; }; MPI_Datatype datatype; MPI_Type_contiguous(sizeof(mystruct), MPI_CHAR, &datatype ); MPI_Type_commit(&datatype); int dsize; MPI_Type_size(datatype, &dsize); // The disp displacement argument specifies the position // (absolute offset in bytes from the beginning of the file) char native[] = "native"; // AL: this is to avoid the problem that C++ string literals are const char* while C string literals are char*, leading to a const warning (technically error, but compilers are tolerant) MPI_File_set_view(thefile, static_cast(sizeuntil * dsize), datatype, datatype, native, MPI_INFO_NULL); int count = ind.size(); mystruct * packed = new mystruct[count]; for(int i=0; i(dist[i])) { std::cout << "fread 660 failed! attempting to continue..." << std::endl; } if (printProcSplits) outfile << "Elements stored on proc " << i << ":" << std::endl; for (int j = 0; j < dist[i]; j++) { outfile << data[j].ind+1 << "\t1\t"; handler.save(outfile, data[j].num, data[j].ind); outfile << std::endl; } } outfile.precision(oldPrecision); fclose(f); remove("temp_fullydistspvec"); delete [] data; delete [] dist; } MPI_Barrier(World); } template template IT FullyDistSpVec::Count(_Predicate pred) const { IT local = count_if( num.begin(), num.end(), pred ); IT whole = 0; MPI_Allreduce( &local, &whole, 1, MPIType(), MPI_SUM, commGrid->GetWorld()); return whole; } template template NT FullyDistSpVec::Reduce(_BinaryOperation __binary_op, NT init) const { // std::accumulate returns init for empty sequences // the semantics are init + num[0] + ... + num[n] NT localsum = std::accumulate( num.begin(), num.end(), init, __binary_op); NT totalsum = init; MPI_Allreduce( &localsum, &totalsum, 1, MPIType(), MPIOp<_BinaryOperation, NT>::op(), commGrid->GetWorld()); return totalsum; } template template OUT FullyDistSpVec::Reduce(_BinaryOperation __binary_op, OUT default_val, _UnaryOperation __unary_op) const { // std::accumulate returns identity for empty sequences OUT localsum = default_val; if (num.size() > 0) { typename std::vector< NT >::const_iterator iter = num.begin(); //localsum = __unary_op(*iter); //iter++; while (iter < num.end()) { localsum = __binary_op(localsum, __unary_op(*iter)); iter++; } } OUT totalsum = default_val; MPI_Allreduce( &localsum, &totalsum, 1, MPIType(), MPIOp<_BinaryOperation, OUT>::op(), commGrid->GetWorld()); return totalsum; } template void FullyDistSpVec::PrintInfo(std::string vectorname) const { IT nznz = getnnz(); if (commGrid->GetRank() == 0) std::cout << "As a whole, " << vectorname << " has: " << nznz << " nonzeros and length " << glen << std::endl; } template void FullyDistSpVec::DebugPrint() { int rank, nprocs; MPI_Comm World = commGrid->GetWorld(); MPI_Comm_rank(World, &rank); MPI_Comm_size(World, &nprocs); MPI_File thefile; char tfilename[32] = "temp_fullydistspvec"; MPI_File_open(World, tfilename, MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); IT * dist = new IT[nprocs]; dist[rank] = getlocnnz(); MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), dist, 1, MPIType(), World); IT sizeuntil = std::accumulate(dist, dist+rank, static_cast(0)); struct mystruct { IT ind; NT num; }; MPI_Datatype datatype; MPI_Type_contiguous(sizeof(mystruct), MPI_CHAR, &datatype ); MPI_Type_commit(&datatype); int dsize; MPI_Type_size(datatype, &dsize); // The disp displacement argument specifies the position // (absolute offset in bytes from the beginning of the file) char openmode[32] = "native"; MPI_File_set_view(thefile, static_cast(sizeuntil * dsize), datatype, datatype, openmode, MPI_INFO_NULL); int count = ind.size(); mystruct * packed = new mystruct[count]; for(int i=0; i(dist[i])) { std::cout << "fread 802 failed! attempting to continue..." << std::endl; } std::cout << "Elements stored on proc " << i << ": {"; for (int j = 0; j < dist[i]; j++) { std::cout << "(" << data[j].ind << "," << data[j].num << "), "; } std::cout << "}" << std::endl; } fclose(f); remove("temp_fullydistspvec"); delete [] data; delete [] dist; } MPI_Barrier(World); } template void FullyDistSpVec::Reset() { ind.resize(0); num.resize(0); } // Assigns given locations their value, needs to be sorted template void FullyDistSpVec::BulkSet(IT inds[], int count) { ind.resize(count); num.resize(count); std::copy(inds, inds+count, ind.data()); std::copy(inds, inds+count, num.data()); } /* ** Create a new sparse vector vout by swaping the indices and values of a sparse vector vin. ** the length of vout is globallen, which must be greater than the maximum entry of vin. ** nnz(vin) = nnz(vout) ** for every nonzero entry vin[k]: vout[vin[k]] = k */ /* template FullyDistSpVec FullyDistSpVec::Invert (IT globallen) { FullyDistSpVec Inverted(commGrid, globallen); IT max_entry = Reduce(maximum(), (IT) 0 ) ; if(max_entry >= globallen) { cout << "Sparse vector has entries (" << max_entry << ") larger than requested global vector length " << globallen << endl; return Inverted; } int nprocs = commGrid->GetSize(); vector< vector< NT > > datsent(nprocs); vector< vector< IT > > indsent(nprocs); IT ploclen = getlocnnz(); for(IT k=0; k < ploclen; ++k) { IT locind; int owner = Inverted.Owner(num[k], locind); // numerical values in rhs are 0-based indices IT gind = ind[k] + LengthUntil(); datsent[owner].push_back(gind); indsent[owner].push_back(locind); // so that we don't need no correction at the recipient } int * sendcnt = new int[nprocs]; int * sdispls = new int[nprocs]; for(int i=0; iGetWorld(); MPI_Alltoall(sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, World); // share the request counts sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i().swap(datsent[i]); } IT * indbuf = new IT[ploclen]; for(int i=0; i().swap(indsent[i]); } IT totrecv = accumulate(recvcnt,recvcnt+nprocs, static_cast(0)); NT * recvdatbuf = new NT[totrecv]; MPI_Alltoallv(datbuf, sendcnt, sdispls, MPIType(), recvdatbuf, recvcnt, rdispls, MPIType(), World); delete [] datbuf; IT * recvindbuf = new IT[totrecv]; MPI_Alltoallv(indbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), World); delete [] indbuf; vector< pair > tosort; // in fact, tomerge would be a better name but it is unlikely to be faster for(int i=0; i>::iterator itr = tosort.begin(); itr != tosort.end(); ++itr) { if(lastIndex!=itr->first) // avoid duplicate indices { Inverted.ind.push_back(itr->first); Inverted.num.push_back(itr->second); } lastIndex = itr->first; } return Inverted; } */ /* ** Create a new sparse vector vout by swaping the indices and values of a sparse vector vin. ** the length of vout is globallen, which must be greater than the maximum entry of vin. ** nnz(vin) = nnz(vout) ** for every nonzero entry vin[k]: vout[vin[k]] = k */ template FullyDistSpVec FullyDistSpVec::Invert (IT globallen) { FullyDistSpVec Inverted(commGrid, globallen); IT max_entry = Reduce(maximum(), (IT) 0 ) ; if(max_entry >= globallen) { std::cout << "Sparse vector has entries (" << max_entry << ") larger than requested global vector length " << globallen << std::endl; return Inverted; } MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int * rdispls = new int[nprocs+1]; int * recvcnt = new int[nprocs]; int * sendcnt = new int[nprocs](); // initialize to 0 int * sdispls = new int[nprocs+1]; IT ploclen = getlocnnz(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT k=0; k < ploclen; ++k) { IT locind; int owner = Inverted.Owner(num[k], locind); #ifdef _OPENMP __sync_fetch_and_add(&sendcnt[owner], 1); #else sendcnt[owner]++; #endif } MPI_Alltoall(sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, World); // share the request counts sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i(), recvdatbuf, recvcnt, rdispls, MPIType(), World); delete [] datbuf; IT * recvindbuf = new IT[totrecv]; MPI_Alltoallv(indbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), World); delete [] indbuf; std::vector< std::pair > tosort; tosort.resize(totrecv); #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i>::iterator itr = tosort.begin(); itr != tosort.end(); ++itr) { if(lastIndex!=itr->first) // avoid duplicate indices { Inverted.ind.push_back(itr->first); Inverted.num.push_back(itr->second); } lastIndex = itr->first; } return Inverted; } /* // generalized invert taking binary operations to define index and values of the inverted vector // _BinaryOperationDuplicate: function to reduce duplicate entries */ template template FullyDistSpVec FullyDistSpVec::Invert (IT globallen, _BinaryOperationIdx __binopIdx, _BinaryOperationVal __binopVal, _BinaryOperationDuplicate __binopDuplicate) { FullyDistSpVec Inverted(commGrid, globallen); // identify the max index in the composed vector IT localmax = (IT) 0; for(size_t k=0; k < num.size(); ++k) { localmax = std::max(localmax, __binopIdx(num[k], ind[k] + LengthUntil())); } IT globalmax = (IT) 0; MPI_Allreduce( &localmax, &globalmax, 1, MPIType(), MPI_MAX, commGrid->GetWorld()); if(globalmax >= globallen) { std::cout << "Sparse vector has entries (" << globalmax << ") larger than requested global vector length " << globallen << std::endl; return Inverted; } MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int * rdispls = new int[nprocs+1]; int * recvcnt = new int[nprocs]; int * sendcnt = new int[nprocs](); // initialize to 0 int * sdispls = new int[nprocs+1]; IT ploclen = getlocnnz(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT k=0; k < ploclen; ++k) { IT locind; IT globind = __binopIdx(num[k], ind[k] + LengthUntil()); // get global index of the inverted vector int owner = Inverted.Owner(globind, locind); #ifdef _OPENMP __sync_fetch_and_add(&sendcnt[owner], 1); #else sendcnt[owner]++; #endif } MPI_Alltoall(sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, World); sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i(), recvdatbuf, recvcnt, rdispls, MPIType(), World); delete [] datbuf; IT * recvindbuf = new IT[totrecv]; MPI_Alltoallv(indbuf, sendcnt, sdispls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), World); delete [] indbuf; std::vector< std::pair > tosort; tosort.resize(totrecv); #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i>::iterator itr = tosort.begin(); itr != tosort.end(); ) { IT ind = itr->first; NT val = itr->second; ++itr; while(itr != tosort.end() && itr->first == ind) { val = __binopDuplicate(val, itr->second); ++itr; } Inverted.ind.push_back(ind); Inverted.num.push_back(val); } return Inverted; } // Invert using RMA template template FullyDistSpVec FullyDistSpVec::InvertRMA (IT globallen, _BinaryOperationIdx __binopIdx, _BinaryOperationVal __binopVal) { FullyDistSpVec Inverted(commGrid, globallen); int myrank; MPI_Comm_rank(commGrid->GetWorld(), &myrank); // identify the max index in the composed vector IT localmax = (IT) 0; for(size_t k=0; k < num.size(); ++k) { localmax = std::max(localmax, __binopIdx(num[k], ind[k] + LengthUntil())); } IT globalmax = (IT) 0; MPI_Allreduce( &localmax, &globalmax, 1, MPIType(), MPI_MAX, commGrid->GetWorld()); if(globalmax >= globallen) { std::cout << "Sparse vector has entries (" << globalmax << ") larger than requested global vector length " << globallen << std::endl; return Inverted; } MPI_Comm World = commGrid->GetWorld(); int nprocs = commGrid->GetSize(); int * rdispls = new int[nprocs+1]; int * recvcnt = new int[nprocs](); // initialize to 0 int * sendcnt = new int[nprocs](); // initialize to 0 int * sdispls = new int[nprocs+1]; IT ploclen = getlocnnz(); #ifdef _OPENMP #pragma omp parallel for #endif for(IT k=0; k < ploclen; ++k) { IT locind; IT globind = __binopIdx(num[k], ind[k] + LengthUntil()); // get global index of the inverted vector int owner = Inverted.Owner(globind, locind); #ifdef _OPENMP __sync_fetch_and_add(&sendcnt[owner], 1); #else sendcnt[owner]++; #endif } MPI_Win win2; MPI_Win_create(recvcnt, nprocs * sizeof(MPI_INT), sizeof(MPI_INT), MPI_INFO_NULL, World, &win2); for(int i=0; i0) { MPI_Win_lock(MPI_LOCK_SHARED,i,MPI_MODE_NOCHECK,win2); MPI_Put(&sendcnt[i], 1, MPI_INT, i, myrank, 1, MPI_INT, win2); MPI_Win_unlock(i, win2); } } MPI_Win_free(&win2); sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i0) { MPI_Win_lock(MPI_LOCK_SHARED,i,MPI_MODE_NOCHECK,win3); MPI_Put(&rdispls[i], 1, MPI_INT, i, myrank, 1, MPI_INT, win3); MPI_Win_unlock(i, win3); } } MPI_Win_free(&win3); NT * datbuf = new NT[ploclen]; IT * indbuf = new IT[ploclen]; int *count = new int[nprocs](); //current position #ifdef _OPENMP #pragma omp parallel for #endif for(IT i=0; i < ploclen; ++i) { IT locind; IT globind = __binopIdx(num[i], ind[i] + LengthUntil()); // get global index of the inverted vector int owner = Inverted.Owner(globind, locind); int id; #ifdef _OPENMP id = sdispls[owner] + __sync_fetch_and_add(&count[owner], 1); #else id = sdispls[owner] + count[owner]; count[owner]++; #endif datbuf[id] = __binopVal(num[i], ind[i] + LengthUntil()); indbuf[id] = locind; } delete [] count; IT totrecv = rdispls[nprocs]; NT * recvdatbuf = new NT[totrecv]; IT * recvindbuf = new IT[totrecv]; MPI_Win win, win1; MPI_Win_create(recvdatbuf, totrecv * sizeof(NT), sizeof(NT), MPI_INFO_NULL, commGrid->GetWorld(), &win); MPI_Win_create(recvindbuf, totrecv * sizeof(IT), sizeof(IT), MPI_INFO_NULL, commGrid->GetWorld(), &win1); //MPI_Win_fence(0, win); //MPI_Win_fence(0, win1); for(int i=0; i0) { MPI_Win_lock(MPI_LOCK_SHARED, i, 0, win); MPI_Put(&datbuf[sdispls[i]], sendcnt[i], MPIType(), i, rmadispls[i], sendcnt[i], MPIType(), win); MPI_Win_unlock(i, win); MPI_Win_lock(MPI_LOCK_SHARED, i, 0, win1); MPI_Put(&indbuf[sdispls[i]], sendcnt[i], MPIType(), i, rmadispls[i], sendcnt[i], MPIType(), win1); MPI_Win_unlock(i, win1); } } //MPI_Win_fence(0, win); //MPI_Win_fence(0, win1); MPI_Win_free(&win); MPI_Win_free(&win1); delete [] datbuf; delete [] indbuf; delete [] rmadispls; std::vector< std::pair > tosort; tosort.resize(totrecv); #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i>::iterator itr = tosort.begin(); itr != tosort.end(); ++itr) { if(lastIndex!=itr->first) // avoid duplicate indices { Inverted.ind.push_back(itr->first); Inverted.num.push_back(itr->second); } lastIndex = itr->first; } return Inverted; } template template void FullyDistSpVec::Select (const FullyDistVec & denseVec, _UnaryOperation __unop) { if(*commGrid == *(denseVec.commGrid)) { if(TotalLength() != denseVec.TotalLength()) { std::ostringstream outs; outs << "Vector dimensions don't match (" << TotalLength() << " vs " << denseVec.TotalLength() << ") for Select\n"; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } else { IT spsize = getlocnnz(); IT k = 0; // iterate over the sparse vector for(IT i=0; i< spsize; ++i) { if(__unop(denseVec.arr[ind[i]])) { ind[k] = ind[i]; num[k++] = num[i]; } } ind.resize(k); num.resize(k); } } else { std::ostringstream outs; outs << "Grids are not comparable for Select" << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } // \todo: Shouldn't this wrap EWiseApply for code maintanence instead? template template void FullyDistSpVec::Setminus (const FullyDistSpVec & other) { if(*commGrid == *(other.commGrid)) { if(TotalLength() != other.TotalLength()) { std::ostringstream outs; outs << "Vector dimensions don't match (" << TotalLength() << " vs " << other.TotalLength() << ") for Select\n"; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } else { IT mysize = getlocnnz(); IT othersize = other.getlocnnz(); IT k = 0, i=0, j=0; // iterate over the sparse vector for(; i< mysize && j < othersize;) { if(other.ind[j] == ind[i]) //skip { i++; j++; } else if(other.ind[j] > ind[i]) { ind[k] = ind[i]; num[k++] = num[i++]; } else j++; } while(i< mysize) { ind[k] = ind[i]; num[k++] = num[i++]; } ind.resize(k); num.resize(k); } } else { std::ostringstream outs; outs << "Grids are not comparable for Select" << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } template template void FullyDistSpVec::SelectApply (const FullyDistVec & denseVec, _UnaryOperation __unop, _BinaryOperation __binop) { if(*commGrid == *(denseVec.commGrid)) { if(TotalLength() != denseVec.TotalLength()) { std::ostringstream outs; outs << "Vector dimensions don't match (" << TotalLength() << " vs " << denseVec.TotalLength() << ") for Select\n"; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } else { IT spsize = getlocnnz(); IT k = 0; // iterate over the sparse vector for(IT i=0; i< spsize; ++i) { if(__unop(denseVec.arr[ind[i]])) { ind[k] = ind[i]; num[k++] = __binop(num[i], denseVec.arr[ind[i]]); } } ind.resize(k); num.resize(k); } } else { std::ostringstream outs; outs << "Grids are not comparable for Select" << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } // apply an unary function to each nnz and return a new vector // can be a constrauctor /* template template FullyDistSpVec FullyDistSpVec::Apply(_UnaryOperation __unop) { FullyDistSpVec composed(commGrid, TotalLength()); IT spsize = getlocnnz(); for(IT i=0; i< spsize; ++i) { composed.ind.push_back(ind[i]); composed.num.push_back( __unop(num[i])); } return composed; } */ /* exp version */ template template void FullyDistSpVec::FilterByVal (FullyDistSpVec Selector, _UnaryOperation __unop, bool filterByIndex) { if(*commGrid != *(Selector.commGrid)) { std::ostringstream outs; outs << "Grids are not comparable for Filter" << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } int nprocs = commGrid->GetSize(); MPI_Comm World = commGrid->GetWorld(); int * rdispls = new int[nprocs]; int sendcnt = Selector.ind.size(); int * recvcnt = new int[nprocs]; MPI_Allgather(&sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, World); rdispls[0] = 0; for(int i=0; i(0)); std::vector recvbuf; recvbuf.resize(totrecv); MPI_Allgatherv(sendbuf, sendcnt, MPIType(), recvbuf.data(), recvcnt, rdispls, MPIType(), World); delete [] sendbuf; DeleteAll(rdispls,recvcnt); if(!filterByIndex) // need to sort { #if defined(GNU_PARALLEL) && defined(_OPENMP) __gnu_parallel::sort(recvbuf.begin(), recvbuf.end()); #else std::sort(recvbuf.begin(), recvbuf.end()); #endif } // now perform filter (recvbuf is sorted) // TODO: OpenMP parallel and keep things sorted IT k=0; for(IT i=0; i #include #include #include #include #include #include #include #include "MPIType.h" namespace combblas { class CommGrid { public: CommGrid(MPI_Comm world, int nrowproc, int ncolproc); ~CommGrid() { MPI_Comm_free(&commWorld); MPI_Comm_free(&rowWorld); MPI_Comm_free(&colWorld); if(diagWorld != MPI_COMM_NULL) MPI_Comm_free(&diagWorld); } CommGrid (const CommGrid & rhs): grrows(rhs.grrows), grcols(rhs.grcols), myprocrow(rhs.myprocrow), myproccol(rhs.myproccol), myrank(rhs.myrank) // copy constructor { MPI_Comm_dup(rhs.commWorld, &commWorld); MPI_Comm_dup(rhs.rowWorld, &rowWorld); MPI_Comm_dup(rhs.colWorld, &colWorld); // don't use the shortcut ternary ? operator, C++ syntax fails as // mpich implements MPI::COMM_NULL of different type than MPI::IntraComm if(rhs.diagWorld == MPI_COMM_NULL) diagWorld = MPI_COMM_NULL; else MPI_Comm_dup(rhs.diagWorld,&diagWorld); } CommGrid & operator=(const CommGrid & rhs) // assignment operator { if(this != &rhs) { MPI_Comm_free(&commWorld); MPI_Comm_free(&rowWorld); MPI_Comm_free(&colWorld); grrows = rhs.grrows; grcols = rhs.grcols; myrank = rhs.myrank; myprocrow = rhs.myprocrow; myproccol = rhs.myproccol; MPI_Comm_dup(rhs.commWorld, &commWorld); MPI_Comm_dup(rhs.rowWorld, &rowWorld); MPI_Comm_dup(rhs.colWorld, &colWorld); if(rhs.diagWorld == MPI_COMM_NULL) diagWorld = MPI_COMM_NULL; else MPI_Comm_dup(rhs.diagWorld,&diagWorld); } return *this; } void CreateDiagWorld(); bool operator== (const CommGrid & rhs) const; bool operator!= (const CommGrid & rhs) const { return (! (*this == rhs)); } bool OnSameProcCol( int rhsrank ); bool OnSameProcRow( int rhsrank ); int GetRank(int rowrank, int colrank) { return rowrank * grcols + colrank; } int GetRank(int diagrank) { return diagrank * grcols + diagrank; } int GetRank() { return myrank; } int GetRankInProcRow() { return myproccol; } int GetRankInProcCol() { return myprocrow; } int GetDiagRank() { int rank; MPI_Comm_rank(diagWorld, &rank); return rank; } int GetRankInProcRow(int wholerank); int GetRankInProcCol(int wholerank); int GetDiagOfProcRow(); int GetDiagOfProcCol(); int GetComplementRank() // For P(i,j), get rank of P(j,i) { return ((grcols * myproccol) + myprocrow); } MPI_Comm & GetWorld() { return commWorld; } MPI_Comm & GetRowWorld() { return rowWorld; } MPI_Comm & GetColWorld() { return colWorld; } MPI_Comm & GetDiagWorld() { return diagWorld; } MPI_Comm GetWorld() const { return commWorld; } MPI_Comm GetRowWorld() const { return rowWorld; } MPI_Comm GetColWorld() const { return colWorld; } MPI_Comm GetDiagWorld() const { return diagWorld; } int GetGridRows() { return grrows; } int GetGridCols() { return grcols; } int GetSize() { return grrows * grcols; } int GetDiagSize() { int size; MPI_Comm_size(diagWorld, &size); return size; } void OpenDebugFile(std::string prefix, std::ofstream & output) const; friend std::shared_ptr ProductGrid(CommGrid * gridA, CommGrid * gridB, int & innerdim, int & Aoffset, int & Boffset); private: // A "normal" MPI-1 communicator is an intracommunicator; MPI::COMM_WORLD is also an MPI::Intracomm object MPI_Comm commWorld, rowWorld, colWorld, diagWorld; // Processor grid is (grrow X grcol) int grrows, grcols; int myprocrow; int myproccol; int myrank; template friend class SpParMat; template friend class FullyDistSpVec; }; } #endif CombBLAS_beta_16_2/include/CombBLAS/FullyDist.h000644 000765 000024 00000023403 13271404146 022465 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _FULLY_DIST_H #define _FULLY_DIST_H #include #include #include "myenableif.h" namespace combblas { template class FullyDist {}; // dummy generic template /** * The full distribution is actually a two-level distribution that matches the matrix distribution * In this scheme, each processor row (except the last) is responsible for t = floor(n/sqrt(p)) elements. * The last processor row gets the remaining (n-floor(n/sqrt(p))*(sqrt(p)-1)) elements * Within the processor row, each processor (except the last) is responsible for loc = floor(t/sqrt(p)) elements. * Example: n=103 and p=16 * All processors P_ij for i=0,1,2 and j=0,1,2 get floor(floor(102/4)/4) = 6 elements * All processors P_i3 for i=0,1,2 get 25-6*3 = 7 elements * All processors P_3j for j=0,1,2 get (102-25*3)/4 = 6 elements * Processor P_33 gets 27-6*3 = 9 elements * Both derived classes, whether sparse or dense, are distributed * to processors based on their "length", so that a conversion does not * need any communication between sparse and dense formats **/ template class FullyDist::value, NT >::type > { public: explicit FullyDist():glen(0) { SpParHelper::Print("COMBBLAS Warning: It is dangerous to create (vector) objects without specifying the communicator, are you sure you want to create this object in MPI_COMM_WORLD?\n"); commGrid.reset(new CommGrid(MPI_COMM_WORLD, 0, 0)); } explicit FullyDist(IT globallen): glen(globallen) { SpParHelper::Print("COMBBLAS Warning: It is dangerous to create (vector) objects without specifying the communicator, are you sure you want to create this object in MPI_COMM_WORLD?\n"); commGrid.reset(new CommGrid(MPI_COMM_WORLD, 0, 0)); } /* ABAB: This clashes with FullyDist(IT globallen) signature on MPICH based systems that #define MPI_Comm to be an INT FullyDist( MPI_Comm world):glen(0) { commGrid.reset(new CommGrid(world, 0, 0)); }*/ FullyDist( std::shared_ptr grid):glen(0) { commGrid = grid; } FullyDist( std::shared_ptr grid, IT globallen): glen(globallen) { commGrid = grid; } FullyDist & operator=(const FullyDist & rhs) { glen = rhs.glen; commGrid = rhs.commGrid; return *this; } IT LengthUntil() const; IT RowLenUntil() const; IT RowLenUntil(int k) const; IT MyLocLength() const; IT MyRowLength() const; IT TotalLength() const { return glen; } int Owner(IT gind, IT & lind) const; int OwnerWithinRow(IT n_thisrow, IT ind_withinrow, IT & lind) const; protected: std::shared_ptr commGrid; IT glen; // global length (actual "length" including zeros) }; //! Given global index gind, //! Return the owner processor id, and //! Assign the local index to lind template int FullyDist::value, NT >::type> ::Owner(IT gind, IT & lind) const { // C++ implicitly upcasts both operands to 64-bit if one is 64-bit and other is 32-bit int procrows = commGrid->GetGridRows(); IT n_perprocrow = glen / procrows; // length on a typical processor row IT n_thisrow; // length assigned to owner's processor row int own_procrow; // owner's processor row if(n_perprocrow != 0) { own_procrow = std::min(static_cast(gind / n_perprocrow), procrows-1); // owner's processor row } else // all owned by the last processor row { own_procrow = procrows -1; } IT ind_withinrow = gind - (own_procrow * n_perprocrow); if(own_procrow == procrows-1) n_thisrow = glen - (n_perprocrow*(procrows-1)); else n_thisrow = n_perprocrow; int proccols = commGrid->GetGridCols(); IT n_perproc = n_thisrow / proccols; // length on a typical processor int own_proccol; if(n_perproc != 0) { own_proccol = std::min(static_cast(ind_withinrow / n_perproc), proccols-1); } else { own_proccol = proccols-1; } lind = ind_withinrow - (own_proccol * n_perproc); // GetRank(int rowrank, int colrank) { return rowrank * grcols + colrank;} return commGrid->GetRank(own_procrow, own_proccol); } /** * @param[in] ind_withinrow {index within processor row} * @param[in] n_thisrow {length within this row} * @param[out] lind {index local to owning processor} * Return the owner processor id (within processor row) **/ template int FullyDist::value, NT >::type > ::OwnerWithinRow(IT n_thisrow, IT ind_withinrow, IT & lind) const { int proccols = commGrid->GetGridCols(); IT n_perproc = n_thisrow / proccols; // length on a typical processor int own_proccol; if(n_perproc != 0) { own_proccol = std::min(static_cast(ind_withinrow / n_perproc), proccols-1); } else { own_proccol = proccols-1; } lind = ind_withinrow - (own_proccol * n_perproc); return own_proccol; } template IT FullyDist::value, NT >::type > ::LengthUntil() const { int procrows = commGrid->GetGridRows(); int my_procrow = commGrid->GetRankInProcCol(); IT n_perprocrow = glen / procrows; // length on a typical processor row IT n_thisrow; // length assigned to this processor row if(my_procrow == procrows-1) n_thisrow = glen - (n_perprocrow*(procrows-1)); else n_thisrow = n_perprocrow; int proccols = commGrid->GetGridCols(); int my_proccol = commGrid->GetRankInProcRow(); IT n_perproc = n_thisrow / proccols; // length on a typical processor return ((n_perprocrow * my_procrow)+(n_perproc*my_proccol)); } // Return the length until this processor, within this processor row only template IT FullyDist::value, NT >::type > ::RowLenUntil() const { int procrows = commGrid->GetGridRows(); int my_procrow = commGrid->GetRankInProcCol(); IT n_perprocrow = glen / procrows; // length on a typical processor row IT n_thisrow; // length assigned to this processor row if(my_procrow == procrows-1) n_thisrow = glen - (n_perprocrow*(procrows-1)); else n_thisrow = n_perprocrow; int proccols = commGrid->GetGridCols(); int my_proccol = commGrid->GetRankInProcRow(); IT n_perproc = n_thisrow / proccols; // length on a typical processor return (n_perproc*my_proccol); } // Return the length until the kth processor, within this processor row only template IT FullyDist::value, NT >::type > ::RowLenUntil(int k) const { int procrows = commGrid->GetGridRows(); int my_procrow = commGrid->GetRankInProcCol(); IT n_perprocrow = glen / procrows; // length on a typical processor row IT n_thisrow; // length assigned to this processor row if(my_procrow == procrows-1) n_thisrow = glen - (n_perprocrow*(procrows-1)); else n_thisrow = n_perprocrow; int proccols = commGrid->GetGridCols(); IT n_perproc = n_thisrow / proccols; // length on a typical processor assert(k < proccols); return (n_perproc*k); } template IT FullyDist::value, NT >::type> ::MyLocLength() const { int procrows = commGrid->GetGridRows(); int my_procrow = commGrid->GetRankInProcCol(); IT n_perprocrow = glen / procrows; // length on a typical processor row IT n_thisrow; // length assigned to this processor row if(my_procrow == procrows-1) n_thisrow = glen - (n_perprocrow*(procrows-1)); else n_thisrow = n_perprocrow; int proccols = commGrid->GetGridCols(); int my_proccol = commGrid->GetRankInProcRow(); IT n_perproc = n_thisrow / proccols; // length on a typical processor if(my_proccol == proccols-1) return (n_thisrow - (n_perproc*(proccols-1))); else return n_perproc; } template IT FullyDist::value, NT >::type> ::MyRowLength() const { int procrows = commGrid->GetGridRows(); int my_procrow = commGrid->GetRankInProcCol(); IT n_perprocrow = glen / procrows; // length on a typical processor row IT n_thisrow; // length assigned to this processor row if(my_procrow == procrows-1) n_thisrow = glen - (n_perprocrow*(procrows-1)); else n_thisrow = n_perprocrow; return n_thisrow; } } #endif CombBLAS_beta_16_2/include/CombBLAS/HeapEntry.h000644 000765 000024 00000004451 13271404146 022447 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _HEAP_ENTRY_H #define _HEAP_ENTRY_H namespace combblas { template class HeapEntry { public: HeapEntry() {}; HeapEntry(IT mykey, IT myrunr, NT mynum): key(mykey), runr(myrunr),num(mynum){} IT key; IT runr; NT num; // Operators are swapped for performance // If you want/need to convert them back to their normal definitions, don't forget to add // "greater< HeapEntry >()" optional parameter to all the heap operations operating on HeapEntry objects. // For example: push_heap(heap, heap + kisect, greater< HeapEntry >()); bool operator > (const HeapEntry & rhs) const { return (key < rhs.key); } bool operator < (const HeapEntry & rhs) const { return (key > rhs.key); } bool operator == (const HeapEntry & rhs) const { return (key == rhs.key); } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/SpTuples.h000644 000765 000024 00000040351 13271404146 022326 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SP_TUPLES_H #define _SP_TUPLES_H #include #include #include #include #include "CombBLAS.h" #include "SpMat.h" #include "SpDefs.h" #include "StackEntry.h" #include "Compare.h" namespace combblas { template class SpDCCols; template class Dcsc; /** * Triplets are represented using the boost::tuple class of the Boost library * Number of entries are 64-bit addressible, but each entry is only addressible * Therefore, size is int64_t but nrows/ncols (representing range of first two entries in tuple) is of type IT * \remarks Indices start from 0 in this class * \remarks Sorted with respect to columns (Column-sorted triples) */ template class SpTuples: public SpMat > { public: // Constructors SpTuples (int64_t size, IT nRow, IT nCol); SpTuples (int64_t size, IT nRow, IT nCol, std::tuple * mytuples, bool sorted = false, bool isOpNew = false); SpTuples (int64_t maxnnz, IT nRow, IT nCol, std::vector & edges, bool removeloops = true); // Graph500 contructor SpTuples (int64_t size, IT nRow, IT nCol, StackEntry > * & multstack); SpTuples (const SpTuples & rhs); // Actual Copy constructor SpTuples (const SpDCCols & rhs); // Copy constructor for conversion from SpDCCols ~SpTuples(); SpTuples & operator=(const SpTuples & rhs); IT & rowindex (IT i) { return joker::get<0>(tuples[i]); } IT & colindex (IT i) { return joker::get<1>(tuples[i]); } NT & numvalue (IT i) { return joker::get<2>(tuples[i]); } IT rowindex (IT i) const { return joker::get<0>(tuples[i]); } IT colindex (IT i) const { return joker::get<1>(tuples[i]); } NT numvalue (IT i) const { return joker::get<2>(tuples[i]); } template void RemoveDuplicates(BINFUNC BinOp); void SortRowBased() { RowLexiCompare rowlexicogcmp; if(!SpHelper::is_sorted(tuples, tuples+nnz, rowlexicogcmp)) sort(tuples , tuples+nnz, rowlexicogcmp); // Default "operator<" for tuples uses lexicographical ordering // However, cray compiler complains about it, so we use rowlexicogcmp } void SortColBased() { ColLexiCompare collexicogcmp; if(!SpHelper::is_sorted(tuples, tuples+nnz, collexicogcmp)) sort(tuples , tuples+nnz, collexicogcmp ); } /** * @pre {should only be called on diagonal processors (others will add non-loop nonzeros)} * @pre {both the implementation and its semantics is meaningless for non-square matrices} **/ IT AddLoops(NT loopval, bool replaceExisting=false) { std::vector existing(n,false); // none of the diagonals exist IT loop = 0; for(IT i=0; i< nnz; ++i) { if(joker::get<0>(tuples[i]) == joker::get<1>(tuples[i])) { ++loop; existing[joker::get<0>(tuples[i])] = true; if(replaceExisting) joker::get<2>(tuples[i]) = loopval; } } std::vector missingindices; for(IT i = 0; i < n; ++i) { if(!existing[i]) missingindices.push_back(i); } IT toadd = n - loop; // number of new entries needed (equals missingindices.size()) std::tuple * ntuples = new std::tuple[nnz+toadd]; std::copy(tuples,tuples+nnz, ntuples); // MCL: As for the loop weights that are chosen, experience shows that a neutral value works well. It is possible to choose larger weights, // and this will increase cluster granularity. The effect is secondary however to that of varying the inflation parameter, // and the algorithm is not very sensitive to changes in the loop weights. for(IT i=0; i< toadd; ++i) { ntuples[nnz+i] = std::make_tuple(missingindices[i], missingindices[i], loopval); } if(isOperatorNew) ::operator delete(tuples); else delete [] tuples; tuples = ntuples; isOperatorNew = false; nnz = nnz+toadd; return loop; } /** * @pre {should only be called on diagonal processors (others will add non-loop nonzeros)} * @pre {both the implementation and its semantics is meaningless for non-square matrices} **/ IT AddLoops(std::vector loopvals, bool replaceExisting=false) { // expectation n == loopvals.size()) std::vector existing(n,false); // none of the diagonals exist IT loop = 0; for(IT i=0; i< nnz; ++i) { if(joker::get<0>(tuples[i]) == joker::get<1>(tuples[i])) { ++loop; existing[joker::get<0>(tuples[i])] = true; if(replaceExisting) joker::get<2>(tuples[i]) = loopvals[joker::get<0>(tuples[i])]; } } std::vector missingindices; for(IT i = 0; i < n; ++i) { if(!existing[i]) missingindices.push_back(i); } IT toadd = n - loop; // number of new entries needed (equals missingindices.size()) std::tuple * ntuples = new std::tuple[nnz+toadd]; std::copy(tuples,tuples+nnz, ntuples); for(IT i=0; i< toadd; ++i) { ntuples[nnz+i] = std::make_tuple(missingindices[i], missingindices[i], loopvals[missingindices[i]]); } if(isOperatorNew) ::operator delete(tuples); else delete [] tuples; tuples = ntuples; isOperatorNew = false; nnz = nnz+toadd; return loop; } /** * @pre {should only be called on diagonal processors (others will remove non-loop nonzeros)} **/ IT RemoveLoops() { IT loop = 0; for(IT i=0; i< nnz; ++i) { if(joker::get<0>(tuples[i]) == joker::get<1>(tuples[i])) ++loop; } std::tuple * ntuples = new std::tuple[nnz-loop]; IT ni = 0; for(IT i=0; i< nnz; ++i) { if(joker::get<0>(tuples[i]) != joker::get<1>(tuples[i])) { ntuples[ni++] = tuples[i]; } } if(isOperatorNew) ::operator delete(tuples); else delete [] tuples; tuples = ntuples; isOperatorNew = false; nnz = nnz-loop; return loop; } std::pair RowLimits() { if(nnz > 0) { RowCompare rowcmp; std::tuple * maxit = std::max_element(tuples, tuples+nnz, rowcmp); std::tuple * minit = std::min_element(tuples, tuples+nnz, rowcmp); return std::make_pair(joker::get<0>(*minit), joker::get<0>(*maxit)); } else return std::make_pair(0,0); } std::pair ColLimits() { if(nnz > 0) { ColCompare colcmp; std::tuple * maxit = std::max_element(tuples, tuples+nnz, colcmp); std::tuple * minit = std::min_element(tuples, tuples+nnz, colcmp); return std::make_pair(joker::get<1>(*minit), joker::get<1>(*maxit)); } else return std::make_pair(0,0); } std::tuple front() { return tuples[0]; }; std::tuple back() { return tuples[nnz-1]; }; // Performs a balanced merge of the array of SpTuples template friend SpTuples MergeAll(const std::vector *> & ArrSpTups, IU mstar, IU nstar, bool delarrs); template friend SpTuples * MergeAllRec(const std::vector *> & ArrSpTups, IU mstar, IU nstar); std::ofstream& putstream (std::ofstream& outfile) const; std::ofstream& put (std::ofstream& outfile) const { return putstream(outfile); } std::ifstream& getstream (std::ifstream& infile); std::ifstream& get (std::ifstream& infile) { return getstream(infile); } bool isZero() const { return (nnz == 0); } IT getnrow() const { return m; } IT getncol() const { return n; } int64_t getnnz() const { return nnz; } void PrintInfo(); std::tuple * tuples; private: IT m; IT n; int64_t nnz; bool isOperatorNew; // if Operator New was used to allocate memory SpTuples (){}; // Default constructor does nothing, hide it void FillTuples (Dcsc * mydcsc); template friend class SpDCCols; template friend class SpCCols; }; // At this point, complete type of of SpTuples is known, safe to declare these specialization (but macros won't work as they are preprocessed) template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; template <> struct promote_trait< SpTuples , SpTuples > { typedef SpTuples T_promote; }; } #include "SpTuples.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/SpParMat.cpp000644 000765 000024 00000513065 13271404146 022600 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include "SpParMat.h" #include "ParFriends.h" #include "Operations.h" #include "FileHeader.h" extern "C" { #include "mmio.h" } #include #include #include #include #include #include #include namespace combblas { /** * If every processor has a distinct triples file such as {A_0, A_1, A_2,... A_p} for p processors **/ template SpParMat< IT,NT,DER >::SpParMat (std::ifstream & input, MPI_Comm & world) { assert( (sizeof(IT) >= sizeof(typename DER::LocalIT)) ); if(!input.is_open()) { perror("Input file doesn't exist\n"); exit(-1); } commGrid.reset(new CommGrid(world, 0, 0)); input >> (*spSeq); } template SpParMat< IT,NT,DER >::SpParMat (DER * myseq, MPI_Comm & world): spSeq(myseq) { assert( (sizeof(IT) >= sizeof(typename DER::LocalIT)) ); commGrid.reset(new CommGrid(world, 0, 0)); } template SpParMat< IT,NT,DER >::SpParMat (DER * myseq, std::shared_ptr grid): spSeq(myseq) { assert( (sizeof(IT) >= sizeof(typename DER::LocalIT)) ); commGrid = grid; } template SpParMat< IT,NT,DER >::SpParMat (std::shared_ptr grid) { assert( (sizeof(IT) >= sizeof(typename DER::LocalIT)) ); spSeq = new DER(); commGrid = grid; } //! Deprecated. Don't call the default constructor template SpParMat< IT,NT,DER >::SpParMat () { SpParHelper::Print("COMBBLAS Warning: It is dangerous to create (matrix) objects without specifying the communicator, are you sure you want to create this object in MPI_COMM_WORLD?\n"); assert( (sizeof(IT) >= sizeof(typename DER::LocalIT)) ); spSeq = new DER(); commGrid.reset(new CommGrid(MPI_COMM_WORLD, 0, 0)); } /** * If there is a single file read by the master process only, use this and then call ReadDistribute() **/ template SpParMat< IT,NT,DER >::SpParMat (MPI_Comm world) { assert( (sizeof(IT) >= sizeof(typename DER::LocalIT)) ); spSeq = new DER(); commGrid.reset(new CommGrid(world, 0, 0)); } template SpParMat< IT,NT,DER >::~SpParMat () { if(spSeq != NULL) delete spSeq; } template void SpParMat< IT,NT,DER >::FreeMemory () { if(spSeq != NULL) delete spSeq; spSeq = NULL; } /** * Private function to guide Select2 communication and avoid code duplication due to loop ends * @param[int, out] klimits {per column k limit gets updated for the next iteration} * @param[out] converged {items to remove from actcolsmap at next iteration{ **/ template template // GIT: global index type of vector void SpParMat::TopKGather(std::vector & all_medians, std::vector & nnz_per_col, int & thischunk, int & chunksize, const std::vector & activemedians, const std::vector & activennzperc, int itersuntil, std::vector< std::vector > & localmat, const std::vector & actcolsmap, std::vector & klimits, std::vector & toretain, std::vector>> & tmppair, IT coffset, const FullyDistVec & rvec) const { int rankincol = commGrid->GetRankInProcCol(); int colneighs = commGrid->GetGridRows(); int nprocs = commGrid->GetSize(); std::vector finalWeightedMedians(thischunk, 0.0); MPI_Gather(activemedians.data() + itersuntil*chunksize, thischunk, MPIType(), all_medians.data(), thischunk, MPIType(), 0, commGrid->GetColWorld()); MPI_Gather(activennzperc.data() + itersuntil*chunksize, thischunk, MPIType(), nnz_per_col.data(), thischunk, MPIType(), 0, commGrid->GetColWorld()); if(rankincol == 0) { std::vector columnCounts(thischunk, 0.0); std::vector< std::pair > mediansNweights(colneighs); // (median,weight) pairs [to be reused at each iteration] for(int j = 0; j < thischunk; ++j) // for each column { for(int k = 0; k(nnz_per_col[fetchindex]); } for(int k = 0; k(nnz_per_col[fetchindex]) / columnCounts[j]); } sort(mediansNweights.begin(), mediansNweights.end()); // sort by median double sumofweights = 0; int k = 0; while( k(), 0, commGrid->GetColWorld()); std::vector larger(thischunk, 0); std::vector smaller(thischunk, 0); std::vector equal(thischunk, 0); #ifdef THREADED #pragma omp parallel for #endif for(int j = 0; j < thischunk; ++j) // for each active column { size_t fetchindex = actcolsmap[j+itersuntil*chunksize]; for(size_t k = 0; k < localmat[fetchindex].size(); ++k) { // count those above/below/equal to the median if(localmat[fetchindex][k] > finalWeightedMedians[j]) larger[j]++; else if(localmat[fetchindex][k] < finalWeightedMedians[j]) smaller[j]++; else equal[j]++; } } MPI_Allreduce(MPI_IN_PLACE, larger.data(), thischunk, MPIType(), MPI_SUM, commGrid->GetColWorld()); MPI_Allreduce(MPI_IN_PLACE, smaller.data(), thischunk, MPIType(), MPI_SUM, commGrid->GetColWorld()); MPI_Allreduce(MPI_IN_PLACE, equal.data(), thischunk, MPIType(), MPI_SUM, commGrid->GetColWorld()); int numThreads = 1; // default case #ifdef THREADED omp_lock_t lock[nprocs]; // a lock per recipient for (int i=0; i > perthread2retain(numThreads); #ifdef THREADED #pragma omp parallel for #endif for(int j = 0; j < thischunk; ++j) // for each active column { #ifdef THREADED int myThread = omp_get_thread_num(); #else int myThread = 0; #endif // both clmapindex and fetchindex are unique for a given j (hence not shared among threads) size_t clmapindex = j+itersuntil*chunksize; // klimits is of the same length as actcolsmap size_t fetchindex = actcolsmap[clmapindex]; // localmat can only be dereferenced using the original indices. // these following if/else checks are the same (because klimits/large/equal vectors are mirrored) on every processor along ColWorld if(klimits[clmapindex] <= larger[j]) // the entries larger than Weighted-Median are plentiful, we can discard all the smaller/equal guys { std::vector survivors; for(size_t k = 0; k < localmat[fetchindex].size(); ++k) { if(localmat[fetchindex][k] > finalWeightedMedians[j]) // keep only the large guys (even equal guys go) survivors.push_back(localmat[fetchindex][k]); } localmat[fetchindex].swap(survivors); perthread2retain[myThread].push_back(clmapindex); // items to retain in actcolsmap } else if (klimits[clmapindex] > larger[j] + equal[j]) // the elements that are either larger or equal-to are surely keepers, no need to reprocess them { std::vector survivors; for(size_t k = 0; k < localmat[fetchindex].size(); ++k) { if(localmat[fetchindex][k] < finalWeightedMedians[j]) // keep only the small guys (even equal guys go) survivors.push_back(localmat[fetchindex][k]); } localmat[fetchindex].swap(survivors); klimits[clmapindex] -= (larger[j] + equal[j]); // update the k limit for this column only perthread2retain[myThread].push_back(clmapindex); // items to retain in actcolsmap } else // larger[j] < klimits[clmapindex] && klimits[clmapindex] <= larger[j] + equal[j] { // we will always have equal[j] > 0 because the weighted median is part of the dataset so it has to be equal to itself. std::vector survivors; for(size_t k = 0; k < localmat[fetchindex].size(); ++k) { if(localmat[fetchindex][k] >= finalWeightedMedians[j]) // keep the larger and equal to guys (might exceed k-limit but that's fine according to MCL) survivors.push_back(localmat[fetchindex][k]); } localmat[fetchindex].swap(survivors); // We found it: the kth largest element in column (coffset + fetchindex) is finalWeightedMedians[j] // But everyone in the same processor column has the information, only one of them should send it IT n_perproc = getlocalcols() / colneighs; // find a typical processor's share int assigned = std::max(static_cast(fetchindex/n_perproc), colneighs-1); if( assigned == rankincol) { IT locid; int owner = rvec.Owner(coffset + fetchindex, locid); #ifdef THREADED omp_set_lock(&(lock[owner])); #endif tmppair[owner].emplace_back(std::make_pair(locid, finalWeightedMedians[j])); #ifdef THREADED omp_unset_lock(&(lock[owner])); #endif } } // end_else } // end_for // ------ concatenate toretain "indices" processed by threads ------ std::vector tdisp(numThreads+1); tdisp[0] = 0; for(int i=0; i::min() //! This is an efficient implementation of the Saukas/Song algorithm //! http://www.ime.usp.br/~einar/select/INDEX.HTM //! Preferred for large k values template template // GIT: global index type of vector bool SpParMat::Kselect2(FullyDistVec & rvec, IT k_limit) const { if(*rvec.commGrid != *commGrid) { SpParHelper::Print("Grids are not comparable, SpParMat::Kselect() fails!", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } int rankincol = commGrid->GetRankInProcCol(); int rankinrow = commGrid->GetRankInProcRow(); int rowneighs = commGrid->GetGridCols(); // get # of processors on the row int colneighs = commGrid->GetGridRows(); int myrank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); FullyDistVec colcnt(commGrid); Reduce(colcnt, Column, std::plus(), (IT) 0, [](NT i){ return (IT) 1;}); // Gather vector along columns (Logic copied from DimApply) int xsize = (int) colcnt.LocArrSize(); int trxsize = 0; int diagneigh = colcnt.commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&xsize, 1, MPI_INT, diagneigh, TRX, &trxsize, 1, MPI_INT, diagneigh, TRX, commGrid->GetWorld(), &status); IT * trxnums = new IT[trxsize]; MPI_Sendrecv(const_cast(SpHelper::p2a(colcnt.arr)), xsize, MPIType(), diagneigh, TRX, trxnums, trxsize, MPIType(), diagneigh, TRX, commGrid->GetWorld(), &status); int * colsize = new int[colneighs]; colsize[rankincol] = trxsize; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colsize, 1, MPI_INT, commGrid->GetColWorld()); int * dpls = new int[colneighs](); // displacements (zero initialized pid) std::partial_sum(colsize, colsize+colneighs-1, dpls+1); int accsize = std::accumulate(colsize, colsize+colneighs, 0); std::vector percsum(accsize); // per column sum of the number of entries MPI_Allgatherv(trxnums, trxsize, MPIType(), percsum.data(), colsize, dpls, MPIType(), commGrid->GetColWorld()); DeleteAll(trxnums,colsize, dpls); // Gather vector along columns IT locm = getlocalcols(); // length (number of columns) assigned to this processor (and processor column) std::vector< std::vector > localmat(locm); // some sort of minimal local copy of matrix #ifdef COMBBLAS_DEBUG if(accsize != locm) std::cout << "Gather vector along columns logic is wrong" << std::endl; #endif for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { if(percsum[colit.colid()] >= k_limit) // don't make a copy of an inactive column { localmat[colit.colid()].reserve(colit.nnz()); for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit < spSeq->endnz(colit); ++nzit) { localmat[colit.colid()].push_back(nzit.value()); } } } int64_t activecols = std::count_if(percsum.begin(), percsum.end(), [k_limit](IT i){ return i >= k_limit;}); int64_t activennz = std::accumulate(percsum.begin(), percsum.end(), (int64_t) 0); int64_t totactcols, totactnnzs; MPI_Allreduce(&activecols, &totactcols, 1, MPIType(), MPI_SUM, commGrid->GetRowWorld()); if(myrank == 0) std::cout << "Number of initial active columns are " << totactcols << std::endl; MPI_Allreduce(&activennz, &totactnnzs, 1, MPIType(), MPI_SUM, commGrid->GetRowWorld()); if(myrank == 0) std::cout << "Number of initial nonzeros are " << totactnnzs << std::endl; #ifdef COMBBLAS_DEBUG IT glactcols = colcnt.Count([k_limit](IT i){ return i >= k_limit;}); if(myrank == 0) std::cout << "Number of initial active columns are " << glactcols << std::endl; if(glactcols != totactcols) if(myrank == 0) std::cout << "Wrong number of active columns are computed" << std::endl; #endif rvec = FullyDistVec ( rvec.getcommgrid(), getncol(), std::numeric_limits::min()); // set length of rvec correctly #ifdef COMBBLAS_DEBUG PrintInfo(); rvec.PrintInfo("rvector"); #endif if(totactcols == 0) { std::ostringstream ss; ss << "TopK: k_limit (" << k_limit <<")" << " >= maxNnzInColumn. Returning the result of Reduce(Column, minimum()) instead..." << std::endl; SpParHelper::Print(ss.str()); return false; // no prune needed } std::vector actcolsmap(activecols); // the map that gives the original index of that active column (this map will shrink over iterations) for (IT i=0, j=0; i< locm; ++i) { if(percsum[i] >= k_limit) actcolsmap[j++] = i; } std::vector all_medians; std::vector nnz_per_col; std::vector klimits(activecols, k_limit); // is distributed management of this vector needed? int activecols_lowerbound = 10*colneighs; IT * locncols = new IT[rowneighs]; locncols[rankinrow] = locm; MPI_Allgather(MPI_IN_PLACE, 0, MPIType(),locncols, 1, MPIType(), commGrid->GetRowWorld()); IT coffset = std::accumulate(locncols, locncols+rankinrow, static_cast(0)); delete [] locncols; /* Create/allocate variables for vector assignment */ MPI_Datatype MPI_pair; MPI_Type_contiguous(sizeof(std::pair), MPI_CHAR, &MPI_pair); MPI_Type_commit(&MPI_pair); int * sendcnt = new int[nprocs]; int * recvcnt = new int[nprocs]; int * sdispls = new int[nprocs](); int * rdispls = new int[nprocs](); while(totactcols > 0) { int chunksize, iterations, lastchunk; if(activecols > activecols_lowerbound) { // two reasons for chunking: // (1) keep memory limited to activecols (<= n/sqrt(p)) // (2) avoid overflow in sentcount chunksize = static_cast(activecols/colneighs); // invariant chunksize >= 10 (by activecols_lowerbound) iterations = std::max(static_cast(activecols/chunksize), 1); lastchunk = activecols - (iterations-1)*chunksize; // lastchunk >= chunksize by construction } else { chunksize = activecols; iterations = 1; lastchunk = activecols; } std::vector activemedians(activecols); // one per "active" column std::vector activennzperc(activecols); #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i< activecols; ++i) // recompute the medians and nnzperc { size_t orgindex = actcolsmap[i]; // assert: no two threads will share the same "orgindex" if(localmat[orgindex].empty()) { activemedians[i] = (NT) 0; activennzperc[i] = 0; } else { // this actually *sorts* increasing but doesn't matter as long we solely care about the median as opposed to a general nth element auto entriesincol(localmat[orgindex]); // create a temporary vector as nth_element modifies the vector std::nth_element(entriesincol.begin(), entriesincol.begin() + entriesincol.size()/2, entriesincol.end()); activemedians[i] = entriesincol[entriesincol.size()/2]; activennzperc[i] = entriesincol.size(); } } percsum.resize(activecols, 0); MPI_Allreduce(activennzperc.data(), percsum.data(), activecols, MPIType(), MPI_SUM, commGrid->GetColWorld()); activennz = std::accumulate(percsum.begin(), percsum.end(), (int64_t) 0); #ifdef COMBBLAS_DEBUG MPI_Allreduce(&activennz, &totactnnzs, 1, MPIType(), MPI_SUM, commGrid->GetRowWorld()); if(myrank == 0) std::cout << "Number of active nonzeros are " << totactnnzs << std::endl; #endif std::vector toretain; if(rankincol == 0) { all_medians.resize(lastchunk*colneighs); nnz_per_col.resize(lastchunk*colneighs); } std::vector< std::vector< std::pair > > tmppair(nprocs); for(int i=0; i< iterations-1; ++i) // this loop should not be parallelized if we want to keep storage small { TopKGather(all_medians, nnz_per_col, chunksize, chunksize, activemedians, activennzperc, i, localmat, actcolsmap, klimits, toretain, tmppair, coffset, rvec); } TopKGather(all_medians, nnz_per_col, lastchunk, chunksize, activemedians, activennzperc, iterations-1, localmat, actcolsmap, klimits, toretain, tmppair, coffset, rvec); /* Set the newly found vector entries */ IT totsend = 0; for(IT i=0; iGetWorld()); std::partial_sum(sendcnt, sendcnt+nprocs-1, sdispls+1); std::partial_sum(recvcnt, recvcnt+nprocs-1, rdispls+1); IT totrecv = std::accumulate(recvcnt,recvcnt+nprocs, static_cast(0)); assert((totsend < std::numeric_limits::max())); assert((totrecv < std::numeric_limits::max())); std::pair * sendpair = new std::pair[totsend]; for(int i=0; i >().swap(tmppair[i]); // clear memory } std::vector< std::pair > recvpair(totrecv); MPI_Alltoallv(sendpair, sendcnt, sdispls, MPI_pair, recvpair.data(), recvcnt, rdispls, MPI_pair, commGrid->GetWorld()); delete [] sendpair; IT updated = 0; for(auto & update : recvpair ) // Now, write these to rvec { updated++; rvec.arr[update.first] = update.second; } #ifdef COMBBLAS_DEBUG MPI_Allreduce(MPI_IN_PLACE, &updated, 1, MPIType(), MPI_SUM, commGrid->GetWorld()); if(myrank == 0) std::cout << "Total vector entries updated " << updated << std::endl; #endif /* End of setting up the newly found vector entries */ std::vector newactivecols(toretain.size()); std::vector newklimits(toretain.size()); IT newindex = 0; for(auto & retind : toretain ) { newactivecols[newindex] = actcolsmap[retind]; newklimits[newindex++] = klimits[retind]; } actcolsmap.swap(newactivecols); klimits.swap(newklimits); activecols = actcolsmap.size(); MPI_Allreduce(&activecols, &totactcols, 1, MPIType(), MPI_SUM, commGrid->GetRowWorld()); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "Number of active columns are " << totactcols << std::endl; #endif } DeleteAll(sendcnt, recvcnt, sdispls, rdispls); MPI_Type_free(&MPI_pair); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "Exiting kselect2"<< std::endl; #endif return true; // prune needed } template void SpParMat< IT,NT,DER >::Dump(std::string filename) const { MPI_Comm World = commGrid->GetWorld(); int rank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); MPI_File thefile; char * _fn = const_cast(filename.c_str()); MPI_File_open(World, _fn, MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); int rankinrow = commGrid->GetRankInProcRow(); int rankincol = commGrid->GetRankInProcCol(); int rowneighs = commGrid->GetGridCols(); // get # of processors on the row int colneighs = commGrid->GetGridRows(); IT * colcnts = new IT[rowneighs]; IT * rowcnts = new IT[colneighs]; rowcnts[rankincol] = getlocalrows(); colcnts[rankinrow] = getlocalcols(); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), colcnts, 1, MPIType(), commGrid->GetRowWorld()); IT coloffset = std::accumulate(colcnts, colcnts+rankinrow, static_cast(0)); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), rowcnts, 1, MPIType(), commGrid->GetColWorld()); IT rowoffset = std::accumulate(rowcnts, rowcnts+rankincol, static_cast(0)); DeleteAll(colcnts, rowcnts); IT * prelens = new IT[nprocs]; prelens[rank] = 2*getlocalnnz(); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), prelens, 1, MPIType(), commGrid->GetWorld()); IT lengthuntil = std::accumulate(prelens, prelens+rank, static_cast(0)); // The disp displacement argument specifies the position // (absolute offset in bytes from the beginning of the file) MPI_Offset disp = lengthuntil * sizeof(uint32_t); char native[] = "native"; MPI_File_set_view(thefile, disp, MPI_UNSIGNED, MPI_UNSIGNED, native, MPI_INFO_NULL); // AL: the second-to-last argument is a non-const char* (looks like poor MPI standardization, the C++ bindings list it as const), C++ string literals MUST be const (especially in c++11). uint32_t * gen_edges = new uint32_t[prelens[rank]]; IT k = 0; for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit != spSeq->endnz(colit); ++nzit) { gen_edges[k++] = (uint32_t) (nzit.rowid() + rowoffset); gen_edges[k++] = (uint32_t) (colit.colid() + coloffset); } } assert(k == prelens[rank]); MPI_File_write(thefile, gen_edges, prelens[rank], MPI_UNSIGNED, NULL); MPI_File_close(&thefile); delete [] prelens; delete [] gen_edges; } template SpParMat< IT,NT,DER >::SpParMat (const SpParMat< IT,NT,DER > & rhs) { if(rhs.spSeq != NULL) spSeq = new DER(*(rhs.spSeq)); // Deep copy of local block commGrid = rhs.commGrid; } template SpParMat< IT,NT,DER > & SpParMat< IT,NT,DER >::operator=(const SpParMat< IT,NT,DER > & rhs) { if(this != &rhs) { //! Check agains NULL is probably unneccessary, delete won't fail on NULL //! But useful in the presence of a user defined "operator delete" which fails to check NULL if(spSeq != NULL) delete spSeq; if(rhs.spSeq != NULL) spSeq = new DER(*(rhs.spSeq)); // Deep copy of local block commGrid = rhs.commGrid; } return *this; } template SpParMat< IT,NT,DER > & SpParMat< IT,NT,DER >::operator+=(const SpParMat< IT,NT,DER > & rhs) { if(this != &rhs) { if(*commGrid == *rhs.commGrid) { (*spSeq) += (*(rhs.spSeq)); } else { std::cout << "Grids are not comparable for parallel addition (A+B)" << std::endl; } } else { std::cout<< "Missing feature (A+A): Use multiply with 2 instead !"< float SpParMat< IT,NT,DER >::LoadImbalance() const { IT totnnz = getnnz(); // collective call IT maxnnz = 0; IT localnnz = (IT) spSeq->getnnz(); MPI_Allreduce( &localnnz, &maxnnz, 1, MPIType(), MPI_MAX, commGrid->GetWorld()); if(totnnz == 0) return 1; return static_cast((commGrid->GetSize() * maxnnz)) / static_cast(totnnz); } template IT SpParMat< IT,NT,DER >::getnnz() const { IT totalnnz = 0; IT localnnz = spSeq->getnnz(); MPI_Allreduce( &localnnz, &totalnnz, 1, MPIType(), MPI_SUM, commGrid->GetWorld()); return totalnnz; } template IT SpParMat< IT,NT,DER >::getnrow() const { IT totalrows = 0; IT localrows = spSeq->getnrow(); MPI_Allreduce( &localrows, &totalrows, 1, MPIType(), MPI_SUM, commGrid->GetColWorld()); return totalrows; } template IT SpParMat< IT,NT,DER >::getncol() const { IT totalcols = 0; IT localcols = spSeq->getncol(); MPI_Allreduce( &localcols, &totalcols, 1, MPIType(), MPI_SUM, commGrid->GetRowWorld()); return totalcols; } template template void SpParMat::DimApply(Dim dim, const FullyDistVec& x, _BinaryOperation __binary_op) { if(!(*commGrid == *(x.commGrid))) { std::cout << "Grids are not comparable for SpParMat::DimApply" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } MPI_Comm World = x.commGrid->GetWorld(); MPI_Comm ColWorld = x.commGrid->GetColWorld(); MPI_Comm RowWorld = x.commGrid->GetRowWorld(); switch(dim) { case Column: // scale each column { int xsize = (int) x.LocArrSize(); int trxsize = 0; int diagneigh = x.commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&xsize, 1, MPI_INT, diagneigh, TRX, &trxsize, 1, MPI_INT, diagneigh, TRX, World, &status); NT * trxnums = new NT[trxsize]; MPI_Sendrecv(const_cast(SpHelper::p2a(x.arr)), xsize, MPIType(), diagneigh, TRX, trxnums, trxsize, MPIType(), diagneigh, TRX, World, &status); int colneighs, colrank; MPI_Comm_size(ColWorld, &colneighs); MPI_Comm_rank(ColWorld, &colrank); int * colsize = new int[colneighs]; colsize[colrank] = trxsize; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colsize, 1, MPI_INT, ColWorld); int * dpls = new int[colneighs](); // displacements (zero initialized pid) std::partial_sum(colsize, colsize+colneighs-1, dpls+1); int accsize = std::accumulate(colsize, colsize+colneighs, 0); NT * scaler = new NT[accsize]; MPI_Allgatherv(trxnums, trxsize, MPIType(), scaler, colsize, dpls, MPIType(), ColWorld); DeleteAll(trxnums,colsize, dpls); for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit != spSeq->endnz(colit); ++nzit) { nzit.value() = __binary_op(nzit.value(), scaler[colit.colid()]); } } delete [] scaler; break; } case Row: { int xsize = (int) x.LocArrSize(); int rowneighs, rowrank; MPI_Comm_size(RowWorld, &rowneighs); MPI_Comm_rank(RowWorld, &rowrank); int * rowsize = new int[rowneighs]; rowsize[rowrank] = xsize; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, rowsize, 1, MPI_INT, RowWorld); int * dpls = new int[rowneighs](); // displacements (zero initialized pid) std::partial_sum(rowsize, rowsize+rowneighs-1, dpls+1); int accsize = std::accumulate(rowsize, rowsize+rowneighs, 0); NT * scaler = new NT[accsize]; MPI_Allgatherv(const_cast(SpHelper::p2a(x.arr)), xsize, MPIType(), scaler, rowsize, dpls, MPIType(), RowWorld); DeleteAll(rowsize, dpls); for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) { for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit != spSeq->endnz(colit); ++nzit) { nzit.value() = __binary_op(nzit.value(), scaler[nzit.rowid()]); } } delete [] scaler; break; } default: { std::cout << "Unknown scaling dimension, returning..." << std::endl; break; } } } template template FullyDistVec SpParMat::Reduce(Dim dim, _BinaryOperation __binary_op, NT id, _UnaryOperation __unary_op) const { IT length; switch(dim) { case Column: { length = getncol(); break; } case Row: { length = getnrow(); break; } default: { std::cout << "Unknown reduction dimension, returning empty vector" << std::endl; break; } } FullyDistVec parvec(commGrid, length, id); Reduce(parvec, dim, __binary_op, id, __unary_op); return parvec; } template template FullyDistVec SpParMat::Reduce(Dim dim, _BinaryOperation __binary_op, NT id) const { IT length; switch(dim) { case Column: { length = getncol(); break; } case Row: { length = getnrow(); break; } default: { std::cout << "Unknown reduction dimension, returning empty vector" << std::endl; break; } } FullyDistVec parvec(commGrid, length, id); Reduce(parvec, dim, __binary_op, id, myidentity()); // myidentity() is a no-op function return parvec; } template template void SpParMat::Reduce(FullyDistVec & rvec, Dim dim, _BinaryOperation __binary_op, VT id) const { Reduce(rvec, dim, __binary_op, id, myidentity() ); } template template // GIT: global index type of vector void SpParMat::Reduce(FullyDistVec & rvec, Dim dim, _BinaryOperation __binary_op, VT id, _UnaryOperation __unary_op) const { Reduce(rvec, dim, __binary_op, id, __unary_op, MPIOp<_BinaryOperation, VT>::op() ); } template template // GIT: global index type of vector void SpParMat::Reduce(FullyDistVec & rvec, Dim dim, _BinaryOperation __binary_op, VT id, _UnaryOperation __unary_op, MPI_Op mympiop) const { if(*rvec.commGrid != *commGrid) { SpParHelper::Print("Grids are not comparable, SpParMat::Reduce() fails!", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } switch(dim) { case Column: // pack along the columns, result is a vector of size n { // We can't use rvec's distribution (rows first, columns later) here // because the ownership model of the vector has the order P(0,0), P(0,1),... // column reduction will first generate vector distribution in P(0,0), P(1,0),... order. IT n_thiscol = getlocalcols(); // length assigned to this processor column int colneighs = commGrid->GetGridRows(); // including oneself int colrank = commGrid->GetRankInProcCol(); GIT * loclens = new GIT[colneighs]; GIT * lensums = new GIT[colneighs+1](); // begin/end points of local lengths GIT n_perproc = n_thiscol / colneighs; // length on a typical processor if(colrank == colneighs-1) loclens[colrank] = n_thiscol - (n_perproc*colrank); else loclens[colrank] = n_perproc; MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), loclens, 1, MPIType(), commGrid->GetColWorld()); std::partial_sum(loclens, loclens+colneighs, lensums+1); // loclens and lensums are different, but both would fit in 32-bits std::vector trarr; typename DER::SpColIter colit = spSeq->begcol(); for(int i=0; i< colneighs; ++i) { VT * sendbuf = new VT[loclens[i]]; std::fill(sendbuf, sendbuf+loclens[i], id); // fill with identity for(; colit != spSeq->endcol() && colit.colid() < lensums[i+1]; ++colit) // iterate over a portion of columns { for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit != spSeq->endnz(colit); ++nzit) // all nonzeros in this column { sendbuf[colit.colid()-lensums[i]] = __binary_op(static_cast(__unary_op(nzit.value())), sendbuf[colit.colid()-lensums[i]]); } } VT * recvbuf = NULL; if(colrank == i) { trarr.resize(loclens[i]); recvbuf = SpHelper::p2a(trarr); } MPI_Reduce(sendbuf, recvbuf, loclens[i], MPIType(), mympiop, i, commGrid->GetColWorld()); // root = i delete [] sendbuf; } DeleteAll(loclens, lensums); GIT reallen; // Now we have to transpose the vector GIT trlen = trarr.size(); int diagneigh = commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&trlen, 1, MPIType(), diagneigh, TRNNZ, &reallen, 1, MPIType(), diagneigh, TRNNZ, commGrid->GetWorld(), &status); rvec.arr.resize(reallen); MPI_Sendrecv(SpHelper::p2a(trarr), trlen, MPIType(), diagneigh, TRX, SpHelper::p2a(rvec.arr), reallen, MPIType(), diagneigh, TRX, commGrid->GetWorld(), &status); rvec.glen = getncol(); // ABAB: Put a sanity check here break; } case Row: // pack along the rows, result is a vector of size m { rvec.glen = getnrow(); int rowneighs = commGrid->GetGridCols(); int rowrank = commGrid->GetRankInProcRow(); GIT * loclens = new GIT[rowneighs]; GIT * lensums = new GIT[rowneighs+1](); // begin/end points of local lengths loclens[rowrank] = rvec.MyLocLength(); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), loclens, 1, MPIType(), commGrid->GetRowWorld()); std::partial_sum(loclens, loclens+rowneighs, lensums+1); try { rvec.arr.resize(loclens[rowrank], id); // keeping track of all nonzero iterators within columns at once is unscalable w.r.t. memory (due to sqrt(p) scaling) // thus we'll do batches of column as opposed to all columns at once. 5 million columns take 80MB (two pointers per column) #define MAXCOLUMNBATCH 5 * 1024 * 1024 typename DER::SpColIter begfinger = spSeq->begcol(); // beginning finger to columns // Each processor on the same processor row should execute the SAME number of reduce calls int numreducecalls = (int) ceil(static_cast(spSeq->getnzc()) / static_cast(MAXCOLUMNBATCH)); int maxreducecalls; MPI_Allreduce( &numreducecalls, &maxreducecalls, 1, MPI_INT, MPI_MAX, commGrid->GetRowWorld()); for(int k=0; k< maxreducecalls; ++k) { std::vector nziters; typename DER::SpColIter curfinger = begfinger; for(; curfinger != spSeq->endcol() && nziters.size() < MAXCOLUMNBATCH ; ++curfinger) { nziters.push_back(spSeq->begnz(curfinger)); } for(int i=0; i< rowneighs; ++i) // step by step to save memory { VT * sendbuf = new VT[loclens[i]]; std::fill(sendbuf, sendbuf+loclens[i], id); // fill with identity typename DER::SpColIter colit = begfinger; IT colcnt = 0; // "processed column" counter for(; colit != curfinger; ++colit, ++colcnt) // iterate over this batch of columns until curfinger { typename DER::SpColIter::NzIter nzit = nziters[colcnt]; for(; nzit != spSeq->endnz(colit) && nzit.rowid() < lensums[i+1]; ++nzit) // a portion of nonzeros in this column { sendbuf[nzit.rowid()-lensums[i]] = __binary_op(static_cast(__unary_op(nzit.value())), sendbuf[nzit.rowid()-lensums[i]]); } nziters[colcnt] = nzit; // set the new finger } VT * recvbuf = NULL; if(rowrank == i) { for(int j=0; j< loclens[i]; ++j) { sendbuf[j] = __binary_op(rvec.arr[j], sendbuf[j]); // rvec.arr will be overriden with MPI_Reduce, save its contents } recvbuf = SpHelper::p2a(rvec.arr); } MPI_Reduce(sendbuf, recvbuf, loclens[i], MPIType(), mympiop, i, commGrid->GetRowWorld()); // root = i delete [] sendbuf; } begfinger = curfinger; // set the next begfilter } DeleteAll(loclens, lensums); } catch (std::length_error& le) { std::cerr << "Length error: " << le.what() << std::endl; } break; } default: { std::cout << "Unknown reduction dimension, returning empty vector" << std::endl; break; } } } #ifndef KSELECTLIMIT #define KSELECTLIMIT 10000 #endif //! Kselect wrapper for a select columns of the matrix //! Indices of the input sparse vectors kth denote the queried columns of the matrix //! Upon return, values of kth stores the kth entries of the queried columns //! Returns true if Kselect algorithm is invoked for at least one column //! Otherwise, returns false template template bool SpParMat::Kselect(FullyDistSpVec & kth, IT k_limit, int kselectVersion) const { #ifdef COMBBLAS_DEBUG FullyDistVec test1(kth.getcommgrid()); FullyDistVec test2(kth.getcommgrid()); Kselect1(test1, k_limit, myidentity()); Kselect2(test2, k_limit); if(test1 == test2) SpParHelper::Print("Kselect1 and Kselect2 producing same results\n"); else { SpParHelper::Print("WARNING: Kselect1 and Kselect2 producing DIFFERENT results\n"); test1.PrintToFile("test1"); test2.PrintToFile("test2"); } #endif if(kselectVersion==1 || k_limit < KSELECTLIMIT) { return Kselect1(kth, k_limit, myidentity()); } else { FullyDistVec kthAll ( getcommgrid()); bool ret = Kselect2(kthAll, k_limit); FullyDistSpVec temp = EWiseApply(kth, kthAll, [](VT spval, VT dval){return dval;}, [](VT spval, VT dval){return true;}, false, NT()); kth = temp; return ret; } } //! Returns true if Kselect algorithm is invoked for at least one column //! Otherwise, returns false //! if false, rvec contains either the minimum entry in each column or zero template template bool SpParMat::Kselect(FullyDistVec & rvec, IT k_limit, int kselectVersion) const { #ifdef COMBBLAS_DEBUG FullyDistVec test1(rvec.getcommgrid()); FullyDistVec test2(rvec.getcommgrid()); Kselect1(test1, k_limit, myidentity()); Kselect2(test2, k_limit); if(test1 == test2) SpParHelper::Print("Kselect1 and Kselect2 producing same results\n"); else { SpParHelper::Print("WARNING: Kselect1 and Kselect2 producing DIFFERENT results\n"); //test1.PrintToFile("test1"); //test2.PrintToFile("test2"); } #endif if(kselectVersion==1 || k_limit < KSELECTLIMIT) return Kselect1(rvec, k_limit, myidentity()); else return Kselect2(rvec, k_limit); } /* identify the k-th maximum element in each column of a matrix ** if the number of nonzeros in a column is less than or equal to k, return minimum entry ** Caution: this is a preliminary implementation: needs 3*(n/sqrt(p))*k memory per processor ** this memory requirement is too high for larger k */ template template // GIT: global index type of vector bool SpParMat::Kselect1(FullyDistVec & rvec, IT k, _UnaryOperation __unary_op) const { if(*rvec.commGrid != *commGrid) { SpParHelper::Print("Grids are not comparable, SpParMat::Kselect() fails!", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } FullyDistVec nnzPerColumn (getcommgrid()); Reduce(nnzPerColumn, Column, std::plus(), (IT)0, [](NT val){return (IT)1;}); IT maxnnzPerColumn = nnzPerColumn.Reduce(maximum(), (IT)0); if(k>maxnnzPerColumn) { SpParHelper::Print("Kselect: k is greater then maxNnzInColumn. Calling Reduce instead...\n"); Reduce(rvec, Column, minimum(), static_cast(0)); return false; } IT n_thiscol = getlocalcols(); // length (number of columns) assigned to this processor (and processor column) // check, memory should be min(n_thiscol*k, local nnz) // hence we will not overflow for very large k std::vector sendbuf(n_thiscol*k); std::vector send_coldisp(n_thiscol+1,0); std::vector local_coldisp(n_thiscol+1,0); //displacement of local columns //local_coldisp is the displacement of all nonzeros per column //send_coldisp is the displacement of k nonzeros per column IT nzc = 0; if(spSeq->getnnz()>0) { typename DER::SpColIter colit = spSeq->begcol(); for(IT i=0; i=k) send_coldisp[i+1] += k; else send_coldisp[i+1] += colit.nnz(); colit++; nzc++; } } } assert(local_coldisp[n_thiscol] == spSeq->getnnz()); // a copy of local part of the matrix // this can be avoided if we write our own local kselect function instead of using partial_sort std::vector localmat(spSeq->getnnz()); #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; ibegcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { typename DER::SpColIter colit = spSeq->begcol() + i; IT colid = colit.colid(); IT idx = local_coldisp[colid]; for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit < spSeq->endnz(colit); ++nzit) { localmat[idx++] = static_cast(__unary_op(nzit.value())); } if(colit.nnz()<=k) { std::sort(localmat.begin()+local_coldisp[colid], localmat.begin()+local_coldisp[colid+1], std::greater()); std::copy(localmat.begin()+local_coldisp[colid], localmat.begin()+local_coldisp[colid+1], sendbuf.begin()+send_coldisp[colid]); } else { std::partial_sort(localmat.begin()+local_coldisp[colid], localmat.begin()+local_coldisp[colid]+k, localmat.begin()+local_coldisp[colid+1], std::greater()); std::copy(localmat.begin()+local_coldisp[colid], localmat.begin()+local_coldisp[colid]+k, sendbuf.begin()+send_coldisp[colid]); } } std::vector().swap(localmat); std::vector().swap(local_coldisp); std::vector recvbuf(n_thiscol*k); std::vector tempbuf(n_thiscol*k); std::vector recv_coldisp(n_thiscol+1); std::vector templen(n_thiscol); int colneighs = commGrid->GetGridRows(); int colrank = commGrid->GetRankInProcCol(); for(int p=2; p <= colneighs; p*=2) { if(colrank%p == p/2) // this processor is a sender in this round { int receiver = colrank - ceil(p/2); MPI_Send(send_coldisp.data(), n_thiscol+1, MPIType(), receiver, 0, commGrid->GetColWorld()); MPI_Send(sendbuf.data(), send_coldisp[n_thiscol], MPIType(), receiver, 1, commGrid->GetColWorld()); //break; } else if(colrank%p == 0) // this processor is a receiver in this round { int sender = colrank + ceil(p/2); if(sender < colneighs) { MPI_Recv(recv_coldisp.data(), n_thiscol+1, MPIType(), sender, 0, commGrid->GetColWorld(), MPI_STATUS_IGNORE); MPI_Recv(recvbuf.data(), recv_coldisp[n_thiscol], MPIType(), sender, 1, commGrid->GetColWorld(), MPI_STATUS_IGNORE); #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i recvbuf[l]) // decision tempbuf[offset+lid++] = sendbuf[j++]; else tempbuf[offset+lid++] = recvbuf[l++]; } while(jGetWorld()); std::vector kthItem(n_thiscol); int root = commGrid->GetDiagOfProcCol(); if(root==0 && colrank==0) // rank 0 { #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i= k) kthItem[i] = sendbuf[send_coldisp[i]+k-1]; else kthItem[i] = std::numeric_limits::min(); // return minimum possible value if a column is empty or has less than k elements } } else if(root>0 && colrank==0) // send to the diagonl processor of this processor column { #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i= k) kthItem[i] = sendbuf[send_coldisp[i]+k-1]; else kthItem[i] = std::numeric_limits::min(); // return minimum possible value if a column is empty or has less than k elements } MPI_Send(kthItem.data(), n_thiscol, MPIType(), root, 0, commGrid->GetColWorld()); } else if(root>0 && colrank==root) { MPI_Recv(kthItem.data(), n_thiscol, MPIType(), 0, 0, commGrid->GetColWorld(), MPI_STATUS_IGNORE); } std::vector sendcnts; std::vector dpls; if(colrank==root) { int proccols = commGrid->GetGridCols(); IT n_perproc = n_thiscol / proccols; sendcnts.resize(proccols); std::fill(sendcnts.data(), sendcnts.data()+proccols-1, n_perproc); sendcnts[proccols-1] = n_thiscol - (n_perproc * (proccols-1)); dpls.resize(proccols,0); // displacements (zero initialized pid) std::partial_sum(sendcnts.data(), sendcnts.data()+proccols-1, dpls.data()+1); } int rowroot = commGrid->GetDiagOfProcRow(); int recvcnts = 0; // scatter received data size MPI_Scatter(sendcnts.data(),1, MPI_INT, & recvcnts, 1, MPI_INT, rowroot, commGrid->GetRowWorld()); rvec.arr.resize(recvcnts); MPI_Scatterv(kthItem.data(),sendcnts.data(), dpls.data(), MPIType(), rvec.arr.data(), rvec.arr.size(), MPIType(),rowroot, commGrid->GetRowWorld()); rvec.glen = getncol(); return true; } template template // GIT: global index type of vector bool SpParMat::Kselect1(FullyDistSpVec & rvec, IT k, _UnaryOperation __unary_op) const { if(*rvec.commGrid != *commGrid) { SpParHelper::Print("Grids are not comparable, SpParMat::Kselect() fails!", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } /* FullyDistVec nnzPerColumn (getcommgrid()); Reduce(nnzPerColumn, Column, plus(), (IT)0, [](NT val){return (IT)1;}); IT maxnnzPerColumn = nnzPerColumn.Reduce(maximum(), (IT)0); if(k>maxnnzPerColumn) { SpParHelper::Print("Kselect: k is greater then maxNnzInColumn. Calling Reduce instead...\n"); Reduce(rvec, Column, minimum(), static_cast(0)); return false; } */ IT n_thiscol = getlocalcols(); // length (number of columns) assigned to this processor (and processor column) MPI_Comm World = rvec.commGrid->GetWorld(); MPI_Comm ColWorld = rvec.commGrid->GetColWorld(); MPI_Comm RowWorld = rvec.commGrid->GetRowWorld(); int colneighs = commGrid->GetGridRows(); int colrank = commGrid->GetRankInProcCol(); int coldiagrank = commGrid->GetDiagOfProcCol(); //replicate sparse indices along processor column int accnz; int32_t trxlocnz; GIT lenuntil; int32_t *trxinds, *activeCols; VT *trxnums, *numacc; TransposeVector(World, rvec, trxlocnz, lenuntil, trxinds, trxnums, true); if(rvec.commGrid->GetGridRows() > 1) { //TODO: we only need to communicate indices AllGatherVector(ColWorld, trxlocnz, lenuntil, trxinds, trxnums, activeCols, numacc, accnz, true); // trxindS/trxnums deallocated, indacc/numacc allocated, accnz set } else { accnz = trxlocnz; activeCols = trxinds; //aliasing ptr numacc = trxnums; //aliasing ptr } std::vector isactive(n_thiscol,false); for(int i=0; i send_coldisp(n_thiscol+1,0); std::vector local_coldisp(n_thiscol+1,0); //vector sendbuf(nActiveCols*k); VT * sendbuf = static_cast (::operator new (n_thiscol*k*sizeof(VT))); //displacement of local columns //local_coldisp is the displacement of all nonzeros per column //send_coldisp is the displacement of k nonzeros per column IT nzc = 0; if(spSeq->getnnz()>0) { typename DER::SpColIter colit = spSeq->begcol(); for(IT i=0; i=k) send_coldisp[i+1] += k; else send_coldisp[i+1] += colit.nnz(); } colit++; nzc++; } } } // a copy of local part of the matrix // this can be avoided if we write our own local kselect function instead of using partial_sort //vector localmat(local_coldisp[n_thiscol]); VT * localmat = static_cast (::operator new (local_coldisp[n_thiscol]*sizeof(VT))); #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; ibegcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { typename DER::SpColIter colit = spSeq->begcol() + i; IT colid = colit.colid(); if(isactive[colid]) { IT idx = local_coldisp[colid]; for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit < spSeq->endnz(colit); ++nzit) { localmat[idx++] = static_cast(__unary_op(nzit.value())); } if(colit.nnz()<=k) { sort(localmat+local_coldisp[colid], localmat+local_coldisp[colid+1], std::greater()); std::copy(localmat+local_coldisp[colid], localmat+local_coldisp[colid+1], sendbuf+send_coldisp[colid]); } else { partial_sort(localmat+local_coldisp[colid], localmat+local_coldisp[colid]+k, localmat+local_coldisp[colid+1], std::greater()); std::copy(localmat+local_coldisp[colid], localmat+local_coldisp[colid]+k, sendbuf+send_coldisp[colid]); } } } //vector().swap(localmat); ::operator delete(localmat); std::vector().swap(local_coldisp); VT * recvbuf = static_cast (::operator new (n_thiscol*k*sizeof(VT))); VT * tempbuf = static_cast (::operator new (n_thiscol*k*sizeof(VT))); //vector recvbuf(n_thiscol*k); //vector tempbuf(n_thiscol*k); std::vector recv_coldisp(n_thiscol+1); std::vector templen(n_thiscol); for(int p=2; p <= colneighs; p*=2) { if(colrank%p == p/2) // this processor is a sender in this round { int receiver = colrank - ceil(p/2); MPI_Send(send_coldisp.data(), n_thiscol+1, MPIType(), receiver, 0, commGrid->GetColWorld()); MPI_Send(sendbuf, send_coldisp[n_thiscol], MPIType(), receiver, 1, commGrid->GetColWorld()); //break; } else if(colrank%p == 0) // this processor is a receiver in this round { int sender = colrank + ceil(p/2); if(sender < colneighs) { MPI_Recv(recv_coldisp.data(), n_thiscol+1, MPIType(), sender, 0, commGrid->GetColWorld(), MPI_STATUS_IGNORE); MPI_Recv(recvbuf, recv_coldisp[n_thiscol], MPIType(), sender, 1, commGrid->GetColWorld(), MPI_STATUS_IGNORE); #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i recvbuf[l]) // decision tempbuf[offset+lid++] = sendbuf[j++]; else tempbuf[offset+lid++] = recvbuf[l++]; } while(jGetWorld()); /*-------------------------------------------------------- At this point, top k elements in every active column are gathered on the first processor row, P(0,:). Next step: At P(0,i) find the kth largest element in active columns belonging to P(0,i). If nnz in a column is less than k, keep the largest nonzero. If a column is empty, keep the lowest numeric value. --------------------------------------------------------*/ std::vector kthItem(nActiveCols); // kth elements of local active columns if(colrank==0) { #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i= k) kthItem[i] = sendbuf[send_coldisp[ai]+k-1]; else if (nitems==0) kthItem[i] = std::numeric_limits::min(); // return minimum possible value if a column is empty else kthItem[i] = sendbuf[send_coldisp[ai+1]-1]; // returning the last entry if nnz in this column is less than k } } /*-------------------------------------------------------- At this point, kth largest elements in every active column are gathered on the first processor row, P(0,:). Next step: Send the kth largest elements from P(0,i) to P(i,i) Nothing to do for P(0,0) --------------------------------------------------------*/ if(coldiagrank>0 && colrank==0) { MPI_Send(kthItem.data(), nActiveCols, MPIType(), coldiagrank, 0, commGrid->GetColWorld()); } else if(coldiagrank>0 && colrank==coldiagrank) // receive in the diagonal processor { MPI_Recv(kthItem.data(), nActiveCols, MPIType(), 0, 0, commGrid->GetColWorld(), MPI_STATUS_IGNORE); } /*-------------------------------------------------------- At this point, kth largest elements in every active column are gathered on the diagonal processors P(i,i). Next step: Scatter the kth largest elements from P(i,i) to all processors in the ith row, P(i,:). Each processor recevies exactly local nnz of rvec entries so that the received data can be directly put in rvec. --------------------------------------------------------*/ int rowroot = commGrid->GetDiagOfProcRow(); int proccols = commGrid->GetGridCols(); std::vector sendcnts(proccols,0); std::vector dpls(proccols,0); int lsize = rvec.ind.size(); // local sizes of the input vecotor will be sent from the doagonal processor MPI_Gather(&lsize,1, MPI_INT, sendcnts.data(), 1, MPI_INT, rowroot, RowWorld); std::partial_sum(sendcnts.data(), sendcnts.data()+proccols-1, dpls.data()+1); MPI_Scatterv(kthItem.data(),sendcnts.data(), dpls.data(), MPIType(), rvec.num.data(), rvec.num.size(), MPIType(),rowroot, RowWorld); ::operator delete(sendbuf); ::operator delete(recvbuf); ::operator delete(tempbuf); return true; } // only defined for symmetric matrix template IT SpParMat::Bandwidth() const { IT upperlBW = -1; IT lowerlBW = -1; IT m_perproc = getnrow() / commGrid->GetGridRows(); IT n_perproc = getncol() / commGrid->GetGridCols(); IT moffset = commGrid->GetRankInProcCol() * m_perproc; IT noffset = commGrid->GetRankInProcRow() * n_perproc; for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { IT diagrow = colit.colid() + noffset; typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); if(nzit != spSeq->endnz(colit)) // nonempty column { IT firstrow = nzit.rowid() + moffset; IT lastrow = (nzit+ colit.nnz()-1).rowid() + moffset; if(firstrow <= diagrow) // upper diagonal { IT dev = diagrow - firstrow; if(upperlBW < dev) upperlBW = dev; } if(lastrow >= diagrow) // lower diagonal { IT dev = lastrow - diagrow; if(lowerlBW < dev) lowerlBW = dev; } } } IT upperBW; //IT lowerBW; MPI_Allreduce( &upperlBW, &upperBW, 1, MPIType(), MPI_MAX, commGrid->GetWorld()); //MPI_Allreduce( &lowerlBW, &lowerBW, 1, MPIType(), MPI_MAX, commGrid->GetWorld()); //return (upperBW + lowerBW + 1); return (upperBW); } // only defined for symmetric matrix template IT SpParMat::Profile() const { int colrank = commGrid->GetRankInProcRow(); IT cols = getncol(); IT rows = getnrow(); IT m_perproc = cols / commGrid->GetGridRows(); IT n_perproc = rows / commGrid->GetGridCols(); IT moffset = commGrid->GetRankInProcCol() * m_perproc; IT noffset = colrank * n_perproc; int pc = commGrid->GetGridCols(); IT n_thisproc; if(colrank!=pc-1 ) n_thisproc = n_perproc; else n_thisproc = cols - (pc-1)*n_perproc; std::vector firstRowInCol(n_thisproc,getnrow()); std::vector lastRowInCol(n_thisproc,-1); for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { IT diagrow = colit.colid() + noffset; typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); if(nzit != spSeq->endnz(colit)) // nonempty column { IT firstrow = nzit.rowid() + moffset; IT lastrow = (nzit+ colit.nnz()-1).rowid() + moffset; if(firstrow <= diagrow) // upper diagonal { firstRowInCol[colit.colid()] = firstrow; } if(lastrow >= diagrow) // lower diagonal { lastRowInCol[colit.colid()] = lastrow; } } } std::vector firstRowInCol_global(n_thisproc,getnrow()); //vector lastRowInCol_global(n_thisproc,-1); MPI_Allreduce( firstRowInCol.data(), firstRowInCol_global.data(), n_thisproc, MPIType(), MPI_MIN, commGrid->colWorld); //MPI_Allreduce( lastRowInCol.data(), lastRowInCol_global.data(), n_thisproc, MPIType(), MPI_MAX, commGrid->GetColWorld()); IT profile = 0; for(IT i=0; i(), MPI_SUM, commGrid->rowWorld); return (profile_global); } template template void SpParMat::MaskedReduce(FullyDistVec & rvec, FullyDistSpVec & mask, Dim dim, _BinaryOperation __binary_op, VT id, bool exclude) const { if (dim!=Column) { SpParHelper::Print("SpParMat::MaskedReduce() is only implemented for Colum\n"); MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } MaskedReduce(rvec, mask, dim, __binary_op, id, myidentity(), exclude); } /** * Reduce along the column into a vector * @param[in] mask {A sparse vector indicating row indices included/excluded (based on exclude argument) in the reduction } * @param[in] __binary_op {the operation used for reduction; examples: max, min, plus, multiply, and, or. Its parameters and return type are all VT} * @param[in] id {scalar that is used as the identity for __binary_op; examples: zero, infinity} * @param[in] __unary_op {optional unary operation applied to nonzeros *before* the __binary_op; examples: 1/x, x^2} * @param[in] exclude {if true, masked row indices are included in the reduction} * @param[out] rvec {the return vector, specified as an output parameter to allow arbitrary return types via VT} **/ template template // GIT: global index type of vector void SpParMat::MaskedReduce(FullyDistVec & rvec, FullyDistSpVec & mask, Dim dim, _BinaryOperation __binary_op, VT id, _UnaryOperation __unary_op, bool exclude) const { MPI_Comm World = commGrid->GetWorld(); MPI_Comm ColWorld = commGrid->GetColWorld(); MPI_Comm RowWorld = commGrid->GetRowWorld(); if (dim!=Column) { SpParHelper::Print("SpParMat::MaskedReduce() is only implemented for Colum\n"); MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } if(*rvec.commGrid != *commGrid) { SpParHelper::Print("Grids are not comparable, SpParMat::MaskedReduce() fails!", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } int rowneighs = commGrid->GetGridCols(); int rowrank = commGrid->GetRankInProcRow(); std::vector rownz(rowneighs); int locnnzMask = static_cast (mask.getlocnnz()); rownz[rowrank] = locnnzMask; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, rownz.data(), 1, MPI_INT, RowWorld); std::vector dpls(rowneighs+1,0); std::partial_sum(rownz.begin(), rownz.end(), dpls.data()+1); int accnz = std::accumulate(rownz.begin(), rownz.end(), 0); std::vector sendInd(locnnzMask); std::transform(mask.ind.begin(), mask.ind.end(),sendInd.begin(), bind2nd(std::plus(), mask.RowLenUntil())); std::vector indMask(accnz); MPI_Allgatherv(sendInd.data(), rownz[rowrank], MPIType(), indMask.data(), rownz.data(), dpls.data(), MPIType(), RowWorld); // We can't use rvec's distribution (rows first, columns later) here IT n_thiscol = getlocalcols(); // length assigned to this processor column int colneighs = commGrid->GetGridRows(); // including oneself int colrank = commGrid->GetRankInProcCol(); GIT * loclens = new GIT[colneighs]; GIT * lensums = new GIT[colneighs+1](); // begin/end points of local lengths GIT n_perproc = n_thiscol / colneighs; // length on a typical processor if(colrank == colneighs-1) loclens[colrank] = n_thiscol - (n_perproc*colrank); else loclens[colrank] = n_perproc; MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), loclens, 1, MPIType(), commGrid->GetColWorld()); std::partial_sum(loclens, loclens+colneighs, lensums+1); // loclens and lensums are different, but both would fit in 32-bits std::vector trarr; typename DER::SpColIter colit = spSeq->begcol(); for(int i=0; i< colneighs; ++i) { VT * sendbuf = new VT[loclens[i]]; std::fill(sendbuf, sendbuf+loclens[i], id); // fill with identity for(; colit != spSeq->endcol() && colit.colid() < lensums[i+1]; ++colit) // iterate over a portion of columns { int k=0; typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); for(; nzit != spSeq->endnz(colit) && k < indMask.size(); ) // all nonzeros in this column { if(nzit.rowid() < indMask[k]) { if(exclude) { sendbuf[colit.colid()-lensums[i]] = __binary_op(static_cast(__unary_op(nzit.value())), sendbuf[colit.colid()-lensums[i]]); } ++nzit; } else if(nzit.rowid() > indMask[k]) ++k; else { if(!exclude) { sendbuf[colit.colid()-lensums[i]] = __binary_op(static_cast(__unary_op(nzit.value())), sendbuf[colit.colid()-lensums[i]]); } ++k; ++nzit; } } if(exclude) { while(nzit != spSeq->endnz(colit)) { sendbuf[colit.colid()-lensums[i]] = __binary_op(static_cast(__unary_op(nzit.value())), sendbuf[colit.colid()-lensums[i]]); ++nzit; } } } VT * recvbuf = NULL; if(colrank == i) { trarr.resize(loclens[i]); recvbuf = SpHelper::p2a(trarr); } MPI_Reduce(sendbuf, recvbuf, loclens[i], MPIType(), MPIOp<_BinaryOperation, VT>::op(), i, commGrid->GetColWorld()); // root = i delete [] sendbuf; } DeleteAll(loclens, lensums); GIT reallen; // Now we have to transpose the vector GIT trlen = trarr.size(); int diagneigh = commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&trlen, 1, MPIType(), diagneigh, TRNNZ, &reallen, 1, MPIType(), diagneigh, TRNNZ, commGrid->GetWorld(), &status); rvec.arr.resize(reallen); MPI_Sendrecv(SpHelper::p2a(trarr), trlen, MPIType(), diagneigh, TRX, SpHelper::p2a(rvec.arr), reallen, MPIType(), diagneigh, TRX, commGrid->GetWorld(), &status); rvec.glen = getncol(); // ABAB: Put a sanity check here } template template SpParMat::operator SpParMat () const { NDER * convert = new NDER(*spSeq); return SpParMat (convert, commGrid); } //! Change index type as well template template SpParMat::operator SpParMat () const { NDER * convert = new NDER(*spSeq); return SpParMat (convert, commGrid); } /** * Create a submatrix of size m x (size(ci) * s) on a r x s processor grid * Essentially fetches the columns ci[0], ci[1],... ci[size(ci)] from every submatrix */ template SpParMat SpParMat::SubsRefCol (const std::vector & ci) const { std::vector ri; DER * tempseq = new DER((*spSeq)(ri, ci)); return SpParMat (tempseq, commGrid); } /** * Generalized sparse matrix indexing (ri/ci are 0-based indexed) * Both the storage and the actual values in FullyDistVec should be IT * The index vectors are dense and FULLY distributed on all processors * We can use this function to apply a permutation like A(p,q) * Sequential indexing subroutine (via multiplication) is general enough. */ template template SpParMat SpParMat::SubsRef_SR (const FullyDistVec & ri, const FullyDistVec & ci, bool inplace) { // infer the concrete type SpMat typedef typename create_trait::T_inferred DER_IT; if((*(ri.commGrid) != *(commGrid)) || (*(ci.commGrid) != *(commGrid))) { SpParHelper::Print("Grids are not comparable, SpRef fails !"); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } // Safety check IT locmax_ri = 0; IT locmax_ci = 0; if(!ri.arr.empty()) locmax_ri = *std::max_element(ri.arr.begin(), ri.arr.end()); if(!ci.arr.empty()) locmax_ci = *std::max_element(ci.arr.begin(), ci.arr.end()); IT total_m = getnrow(); IT total_n = getncol(); if(locmax_ri > total_m || locmax_ci > total_n) { throw outofrangeexception(); } // The indices for FullyDistVec are offset'd to 1/p pieces // The matrix indices are offset'd to 1/sqrt(p) pieces // Add the corresponding offset before sending the data IT roffset = ri.RowLenUntil(); IT rrowlen = ri.MyRowLength(); IT coffset = ci.RowLenUntil(); IT crowlen = ci.MyRowLength(); // We create two boolean matrices P and Q // Dimensions: P is size(ri) x m // Q is n x size(ci) // Range(ri) = {0,...,m-1} // Range(ci) = {0,...,n-1} IT rowneighs = commGrid->GetGridCols(); // number of neighbors along this processor row (including oneself) IT totalm = getnrow(); // collective call IT totaln = getncol(); IT m_perproccol = totalm / rowneighs; IT n_perproccol = totaln / rowneighs; // Get the right local dimensions IT diagneigh = commGrid->GetComplementRank(); IT mylocalrows = getlocalrows(); IT mylocalcols = getlocalcols(); IT trlocalrows; MPI_Status status; MPI_Sendrecv(&mylocalrows, 1, MPIType(), diagneigh, TRROWX, &trlocalrows, 1, MPIType(), diagneigh, TRROWX, commGrid->GetWorld(), &status); // we don't need trlocalcols because Q.Transpose() will take care of it std::vector< std::vector > rowid(rowneighs); // reuse for P and Q std::vector< std::vector > colid(rowneighs); // Step 1: Create P IT locvec = ri.arr.size(); // nnz in local vector for(typename std::vector::size_type i=0; i< (unsigned)locvec; ++i) { // numerical values (permutation indices) are 0-based // recipient alone progessor row IT rowrec = (m_perproccol!=0) ? std::min(ri.arr[i] / m_perproccol, rowneighs-1) : (rowneighs-1); // ri's numerical values give the colids and its local indices give rowids rowid[rowrec].push_back( i + roffset); colid[rowrec].push_back(ri.arr[i] - (rowrec * m_perproccol)); } int * sendcnt = new int[rowneighs]; // reuse in Q as well int * recvcnt = new int[rowneighs]; for(IT i=0; iGetRowWorld()); // share the counts int * sdispls = new int[rowneighs](); int * rdispls = new int[rowneighs](); std::partial_sum(sendcnt, sendcnt+rowneighs-1, sdispls+1); std::partial_sum(recvcnt, recvcnt+rowneighs-1, rdispls+1); IT p_nnz = std::accumulate(recvcnt,recvcnt+rowneighs, static_cast(0)); // create space for incoming data ... IT * p_rows = new IT[p_nnz]; IT * p_cols = new IT[p_nnz]; IT * senddata = new IT[locvec]; // re-used for both rows and columns for(int i=0; i().swap(rowid[i]); // clear memory of rowid } MPI_Alltoallv(senddata, sendcnt, sdispls, MPIType(), p_rows, recvcnt, rdispls, MPIType(), commGrid->GetRowWorld()); for(int i=0; i().swap(colid[i]); // clear memory of colid } MPI_Alltoallv(senddata, sendcnt, sdispls, MPIType(), p_cols, recvcnt, rdispls, MPIType(), commGrid->GetRowWorld()); delete [] senddata; std::tuple * p_tuples = new std::tuple[p_nnz]; for(IT i=0; i< p_nnz; ++i) { p_tuples[i] = std::make_tuple(p_rows[i], p_cols[i], 1); } DeleteAll(p_rows, p_cols); DER_IT * PSeq = new DER_IT(); PSeq->Create( p_nnz, rrowlen, trlocalrows, p_tuples); // deletion of tuples[] is handled by SpMat::Create SpParMat PA(commGrid); if(&ri == &ci) // Symmetric permutation { DeleteAll(sendcnt, recvcnt, sdispls, rdispls); #ifdef SPREFDEBUG SpParHelper::Print("Symmetric permutation\n", commGrid->GetWorld()); #endif SpParMat P (PSeq, commGrid); if(inplace) { #ifdef SPREFDEBUG SpParHelper::Print("In place multiplication\n", commGrid->GetWorld()); #endif *this = Mult_AnXBn_DoubleBuff(P, *this, false, true); // clear the memory of *this //ostringstream outb; //outb << "P_after_" << commGrid->myrank; //ofstream ofb(outb.str().c_str()); //P.put(ofb); P.Transpose(); *this = Mult_AnXBn_DoubleBuff(*this, P, true, true); // clear the memory of both *this and P return SpParMat(commGrid); // dummy return to match signature } else { PA = Mult_AnXBn_DoubleBuff(P,*this); P.Transpose(); return Mult_AnXBn_DoubleBuff(PA, P); } } else { // Intermediate step (to save memory): Form PA and store it in P // Distributed matrix generation (collective call) SpParMat P (PSeq, commGrid); // Do parallel matrix-matrix multiply PA = Mult_AnXBn_DoubleBuff(P, *this); } // P is destructed here #ifndef NDEBUG PA.PrintInfo(); #endif // Step 2: Create Q (use the same row-wise communication and transpose at the end) // This temporary to-be-transposed Q is size(ci) x n locvec = ci.arr.size(); // nnz in local vector (reset variable) for(typename std::vector::size_type i=0; i< (unsigned)locvec; ++i) { // numerical values (permutation indices) are 0-based IT rowrec = (n_perproccol!=0) ? std::min(ci.arr[i] / n_perproccol, rowneighs-1) : (rowneighs-1); // ri's numerical values give the colids and its local indices give rowids rowid[rowrec].push_back( i + coffset); colid[rowrec].push_back(ci.arr[i] - (rowrec * n_perproccol)); } for(IT i=0; iGetRowWorld()); // share the counts std::fill(sdispls, sdispls+rowneighs, 0); // reset std::fill(rdispls, rdispls+rowneighs, 0); std::partial_sum(sendcnt, sendcnt+rowneighs-1, sdispls+1); std::partial_sum(recvcnt, recvcnt+rowneighs-1, rdispls+1); IT q_nnz = std::accumulate(recvcnt,recvcnt+rowneighs, static_cast(0)); // create space for incoming data ... IT * q_rows = new IT[q_nnz]; IT * q_cols = new IT[q_nnz]; senddata = new IT[locvec]; for(int i=0; i().swap(rowid[i]); // clear memory of rowid } MPI_Alltoallv(senddata, sendcnt, sdispls, MPIType(), q_rows, recvcnt, rdispls, MPIType(), commGrid->GetRowWorld()); for(int i=0; i().swap(colid[i]); // clear memory of colid } MPI_Alltoallv(senddata, sendcnt, sdispls, MPIType(), q_cols, recvcnt, rdispls, MPIType(), commGrid->GetRowWorld()); DeleteAll(senddata, sendcnt, recvcnt, sdispls, rdispls); std::tuple * q_tuples = new std::tuple[q_nnz]; for(IT i=0; i< q_nnz; ++i) { q_tuples[i] = std::make_tuple(q_rows[i], q_cols[i], 1); } DeleteAll(q_rows, q_cols); DER_IT * QSeq = new DER_IT(); QSeq->Create( q_nnz, crowlen, mylocalcols, q_tuples); // Creating Q' instead // Step 3: Form PAQ // Distributed matrix generation (collective call) SpParMat Q (QSeq, commGrid); Q.Transpose(); if(inplace) { *this = Mult_AnXBn_DoubleBuff(PA, Q, true, true); // clear the memory of both PA and P return SpParMat(commGrid); // dummy return to match signature } else { return Mult_AnXBn_DoubleBuff(PA, Q); } } template void SpParMat::SpAsgn(const FullyDistVec & ri, const FullyDistVec & ci, SpParMat & B) { typedef PlusTimesSRing PTRing; if((*(ri.commGrid) != *(B.commGrid)) || (*(ci.commGrid) != *(B.commGrid))) { SpParHelper::Print("Grids are not comparable, SpAsgn fails !", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } IT total_m_A = getnrow(); IT total_n_A = getncol(); IT total_m_B = B.getnrow(); IT total_n_B = B.getncol(); if(total_m_B != ri.TotalLength()) { SpParHelper::Print("First dimension of B does NOT match the length of ri, SpAsgn fails !", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } if(total_n_B != ci.TotalLength()) { SpParHelper::Print("Second dimension of B does NOT match the length of ci, SpAsgn fails !", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } Prune(ri, ci); // make a hole // embed B to the size of A FullyDistVec * rvec = new FullyDistVec(ri.commGrid); rvec->iota(total_m_B, 0); // sparse() expects a zero based index SpParMat R(total_m_A, total_m_B, ri, *rvec, 1); delete rvec; // free memory SpParMat RB = Mult_AnXBn_DoubleBuff(R, B, true, false); // clear memory of R but not B FullyDistVec * qvec = new FullyDistVec(ri.commGrid); qvec->iota(total_n_B, 0); SpParMat Q(total_n_B, total_n_A, *qvec, ci, 1); delete qvec; // free memory SpParMat RBQ = Mult_AnXBn_DoubleBuff(RB, Q, true, true); // clear memory of RB and Q *this += RBQ; // extend-add } template void SpParMat::Prune(const FullyDistVec & ri, const FullyDistVec & ci) { typedef PlusTimesSRing PTRing; if((*(ri.commGrid) != *(commGrid)) || (*(ci.commGrid) != *(commGrid))) { SpParHelper::Print("Grids are not comparable, Prune fails!\n", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } // Safety check IT locmax_ri = 0; IT locmax_ci = 0; if(!ri.arr.empty()) locmax_ri = *std::max_element(ri.arr.begin(), ri.arr.end()); if(!ci.arr.empty()) locmax_ci = *std::max_element(ci.arr.begin(), ci.arr.end()); IT total_m = getnrow(); IT total_n = getncol(); if(locmax_ri > total_m || locmax_ci > total_n) { throw outofrangeexception(); } SpParMat S(total_m, total_m, ri, ri, 1); SpParMat SA = Mult_AnXBn_DoubleBuff(S, *this, true, false); // clear memory of S but not *this SpParMat T(total_n, total_n, ci, ci, 1); SpParMat SAT = Mult_AnXBn_DoubleBuff(SA, T, true, true); // clear memory of SA and T EWiseMult(SAT, true); // In-place EWiseMult with not(SAT) } //! Prune every column of a sparse matrix based on pvals template template SpParMat SpParMat::PruneColumn(const FullyDistVec & pvals, _BinaryOperation __binary_op, bool inPlace) { MPI_Barrier(MPI_COMM_WORLD); if(getncol() != pvals.TotalLength()) { std::ostringstream outs; outs << "Can not prune column-by-column, dimensions does not match"<< std::endl; outs << getncol() << " != " << pvals.TotalLength() << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } if(! ( *(getcommgrid()) == *(pvals.getcommgrid())) ) { std::cout << "Grids are not comparable for PurneColumn" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } MPI_Comm World = pvals.commGrid->GetWorld(); MPI_Comm ColWorld = pvals.commGrid->GetColWorld(); int xsize = (int) pvals.LocArrSize(); int trxsize = 0; int diagneigh = pvals.commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&xsize, 1, MPI_INT, diagneigh, TRX, &trxsize, 1, MPI_INT, diagneigh, TRX, World, &status); NT * trxnums = new NT[trxsize]; MPI_Sendrecv(const_cast(SpHelper::p2a(pvals.arr)), xsize, MPIType(), diagneigh, TRX, trxnums, trxsize, MPIType(), diagneigh, TRX, World, &status); int colneighs, colrank; MPI_Comm_size(ColWorld, &colneighs); MPI_Comm_rank(ColWorld, &colrank); int * colsize = new int[colneighs]; colsize[colrank] = trxsize; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colsize, 1, MPI_INT, ColWorld); int * dpls = new int[colneighs](); // displacements (zero initialized pid) std::partial_sum(colsize, colsize+colneighs-1, dpls+1); int accsize = std::accumulate(colsize, colsize+colneighs, 0); std::vector numacc(accsize); #ifdef COMBBLAS_DEBUG std::ostringstream outs2; outs2 << "PruneColumn displacements: "; for(int i=0; i< colneighs; ++i) { outs2 << dpls[i] << " "; } outs2 << std::endl; SpParHelper::Print(outs2.str()); MPI_Barrier(World); #endif MPI_Allgatherv(trxnums, trxsize, MPIType(), numacc.data(), colsize, dpls, MPIType(), ColWorld); delete [] trxnums; delete [] colsize; delete [] dpls; //sanity check assert(accsize == getlocalcols()); if (inPlace) { spSeq->PruneColumn(numacc.data(), __binary_op, inPlace); return SpParMat(getcommgrid()); // return blank to match signature } else { return SpParMat(spSeq->PruneColumn(numacc.data(), __binary_op, inPlace), commGrid); } } //! Prune columns of a sparse matrix selected by nonzero indices of pvals //! Each selected column is pruned by corresponding values in pvals template template SpParMat SpParMat::PruneColumn(const FullyDistSpVec & pvals, _BinaryOperation __binary_op, bool inPlace) { MPI_Barrier(MPI_COMM_WORLD); if(getncol() != pvals.TotalLength()) { std::ostringstream outs; outs << "Can not prune column-by-column, dimensions does not match"<< std::endl; outs << getncol() << " != " << pvals.TotalLength() << std::endl; SpParHelper::Print(outs.str()); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } if(! ( *(getcommgrid()) == *(pvals.getcommgrid())) ) { std::cout << "Grids are not comparable for PurneColumn" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } MPI_Comm World = pvals.commGrid->GetWorld(); MPI_Comm ColWorld = pvals.commGrid->GetColWorld(); int diagneigh = pvals.commGrid->GetComplementRank(); IT xlocnz = pvals.getlocnnz(); IT roffst = pvals.RowLenUntil(); IT roffset; IT trxlocnz = 0; MPI_Status status; MPI_Sendrecv(&roffst, 1, MPIType(), diagneigh, TROST, &roffset, 1, MPIType(), diagneigh, TROST, World, &status); MPI_Sendrecv(&xlocnz, 1, MPIType(), diagneigh, TRNNZ, &trxlocnz, 1, MPIType(), diagneigh, TRNNZ, World, &status); std::vector trxinds (trxlocnz); std::vector trxnums (trxlocnz); MPI_Sendrecv(pvals.ind.data(), xlocnz, MPIType(), diagneigh, TRI, trxinds.data(), trxlocnz, MPIType(), diagneigh, TRI, World, &status); MPI_Sendrecv(pvals.num.data(), xlocnz, MPIType(), diagneigh, TRX, trxnums.data(), trxlocnz, MPIType(), diagneigh, TRX, World, &status); std::transform(trxinds.data(), trxinds.data()+trxlocnz, trxinds.data(), std::bind2nd(std::plus(), roffset)); int colneighs, colrank; MPI_Comm_size(ColWorld, &colneighs); MPI_Comm_rank(ColWorld, &colrank); int * colnz = new int[colneighs]; colnz[colrank] = trxlocnz; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colnz, 1, MPI_INT, ColWorld); int * dpls = new int[colneighs](); // displacements (zero initialized pid) std::partial_sum(colnz, colnz+colneighs-1, dpls+1); IT accnz = std::accumulate(colnz, colnz+colneighs, 0); std::vector indacc(accnz); std::vector numacc(accnz); MPI_Allgatherv(trxinds.data(), trxlocnz, MPIType(), indacc.data(), colnz, dpls, MPIType(), ColWorld); MPI_Allgatherv(trxnums.data(), trxlocnz, MPIType(), numacc.data(), colnz, dpls, MPIType(), ColWorld); delete [] colnz; delete [] dpls; if (inPlace) { spSeq->PruneColumn(indacc.data(), numacc.data(), __binary_op, inPlace); return SpParMat(getcommgrid()); // return blank to match signature } else { return SpParMat(spSeq->PruneColumn(indacc.data(), numacc.data(), __binary_op, inPlace), commGrid); } } // In-place version where rhs type is the same (no need for type promotion) template void SpParMat::EWiseMult (const SpParMat< IT,NT,DER > & rhs, bool exclude) { if(*commGrid == *rhs.commGrid) { spSeq->EWiseMult(*(rhs.spSeq), exclude); // Dimension compatibility check performed by sequential function } else { std::cout << "Grids are not comparable, EWiseMult() fails !" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } template void SpParMat::EWiseScale(const DenseParMat & rhs) { if(*commGrid == *rhs.commGrid) { spSeq->EWiseScale(rhs.array, rhs.m, rhs.n); // Dimension compatibility check performed by sequential function } else { std::cout << "Grids are not comparable, EWiseScale() fails !" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } template template void SpParMat::UpdateDense(DenseParMat & rhs, _BinaryOperation __binary_op) const { if(*commGrid == *rhs.commGrid) { if(getlocalrows() == rhs.m && getlocalcols() == rhs.n) { spSeq->UpdateDense(rhs.array, __binary_op); } else { std::cout << "Matrices have different dimensions, UpdateDense() fails !" << std::endl; MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } } else { std::cout << "Grids are not comparable, UpdateDense() fails !" << std::endl; MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } } template void SpParMat::PrintInfo() const { IT mm = getnrow(); IT nn = getncol(); IT nznz = getnnz(); if (commGrid->myrank == 0) std::cout << "As a whole: " << mm << " rows and "<< nn <<" columns and "<< nznz << " nonzeros" << std::endl; #ifdef DEBUG IT allprocs = commGrid->grrows * commGrid->grcols; for(IT i=0; i< allprocs; ++i) { if (commGrid->myrank == i) { std::cout << "Processor (" << commGrid->GetRankInProcRow() << "," << commGrid->GetRankInProcCol() << ")'s data: " << std::endl; spSeq->PrintInfo(); } MPI_Barrier(commGrid->GetWorld()); } #endif } template bool SpParMat::operator== (const SpParMat & rhs) const { int local = static_cast((*spSeq) == (*(rhs.spSeq))); int whole = 1; MPI_Allreduce( &local, &whole, 1, MPI_INT, MPI_BAND, commGrid->GetWorld()); return static_cast(whole); } /** ** Private function that carries code common to different sparse() constructors ** Before this call, commGrid is already set **/ template template void SpParMat< IT,NT,DER >::SparseCommon(std::vector< std::vector < std::tuple > > & data, LIT locsize, IT total_m, IT total_n, _BinaryOperation BinOp) { int nprocs = commGrid->GetSize(); int * sendcnt = new int[nprocs]; int * recvcnt = new int[nprocs]; for(int i=0; iGetWorld()); // share the counts int * sdispls = new int[nprocs](); int * rdispls = new int[nprocs](); std::partial_sum(sendcnt, sendcnt+nprocs-1, sdispls+1); std::partial_sum(recvcnt, recvcnt+nprocs-1, rdispls+1); IT totrecv = std::accumulate(recvcnt,recvcnt+nprocs, static_cast(0)); IT totsent = std::accumulate(sendcnt,sendcnt+nprocs, static_cast(0)); assert((totsent < std::numeric_limits::max())); assert((totrecv < std::numeric_limits::max())); #if 0 ofstream oput; commGrid->OpenDebugFile("Displacements", oput); copy(sdispls, sdispls+nprocs, ostream_iterator(oput, " ")); oput << endl; copy(rdispls, rdispls+nprocs, ostream_iterator(oput, " ")); oput << endl; oput.close(); IT * gsizes; if(commGrid->GetRank() == 0) gsizes = new IT[nprocs]; MPI_Gather(&totrecv, 1, MPIType(), gsizes, 1, MPIType(), 0, commGrid->GetWorld()); if(commGrid->GetRank() == 0) { std::copy(gsizes, gsizes+nprocs, std::ostream_iterator(std::cout, " ")); std::cout << std::endl; } MPI_Barrier(commGrid->GetWorld()); MPI_Gather(&totsent, 1, MPIType(), gsizes, 1, MPIType(), 0, commGrid->GetWorld()); if(commGrid->GetRank() == 0) { copy(gsizes, gsizes+nprocs, ostream_iterator(cout, " ")); cout << endl; } MPI_Barrier(commGrid->GetWorld()); if(commGrid->GetRank() == 0) delete [] gsizes; #endif std::tuple * senddata = new std::tuple[locsize]; // re-used for both rows and columns for(int i=0; i), MPI_CHAR, &MPI_triple); MPI_Type_commit(&MPI_triple); std::tuple * recvdata = new std::tuple[totrecv]; MPI_Alltoallv(senddata, sendcnt, sdispls, MPI_triple, recvdata, recvcnt, rdispls, MPI_triple, commGrid->GetWorld()); DeleteAll(senddata, sendcnt, recvcnt, sdispls, rdispls); MPI_Type_free(&MPI_triple); int r = commGrid->GetGridRows(); int s = commGrid->GetGridCols(); IT m_perproc = total_m / r; IT n_perproc = total_n / s; int myprocrow = commGrid->GetRankInProcCol(); int myproccol = commGrid->GetRankInProcRow(); IT locrows, loccols; if(myprocrow != r-1) locrows = m_perproc; else locrows = total_m - myprocrow * m_perproc; if(myproccol != s-1) loccols = n_perproc; else loccols = total_n - myproccol * n_perproc; SpTuples A(totrecv, locrows, loccols, recvdata); // It is ~SpTuples's job to deallocate // the previous constructor sorts based on columns-first (but that doesn't matter as long as they are sorted one way or another) A.RemoveDuplicates(BinOp); spSeq = new DER(A,false); // Convert SpTuples to DER } //! All vectors are zero-based indexed (as usual) template SpParMat< IT,NT,DER >::SpParMat (IT total_m, IT total_n, const FullyDistVec & distrows, const FullyDistVec & distcols, const FullyDistVec & distvals, bool SumDuplicates) { if((*(distrows.commGrid) != *(distcols.commGrid)) || (*(distcols.commGrid) != *(distvals.commGrid))) { SpParHelper::Print("Grids are not comparable, Sparse() fails!\n"); // commGrid is not initialized yet MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } if((distrows.TotalLength() != distcols.TotalLength()) || (distcols.TotalLength() != distvals.TotalLength())) { SpParHelper::Print("Vectors have different sizes, Sparse() fails!"); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } commGrid = distrows.commGrid; int nprocs = commGrid->GetSize(); std::vector< std::vector < std::tuple > > data(nprocs); IT locsize = distrows.LocArrSize(); for(IT i=0; i()); } else { SparseCommon(data, locsize, total_m, total_n, maximum()); } } template SpParMat< IT,NT,DER >::SpParMat (IT total_m, IT total_n, const FullyDistVec & distrows, const FullyDistVec & distcols, const NT & val, bool SumDuplicates) { if((*(distrows.commGrid) != *(distcols.commGrid)) ) { SpParHelper::Print("Grids are not comparable, Sparse() fails!\n"); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } if((distrows.TotalLength() != distcols.TotalLength()) ) { SpParHelper::Print("Vectors have different sizes, Sparse() fails!\n"); MPI_Abort(MPI_COMM_WORLD, DIMMISMATCH); } commGrid = distrows.commGrid; int nprocs = commGrid->GetSize(); std::vector< std::vector < std::tuple > > data(nprocs); IT locsize = distrows.LocArrSize(); for(IT i=0; i()); } else { SparseCommon(data, locsize, total_m, total_n, maximum()); } } template template SpParMat< IT,NT,DER >::SpParMat (const DistEdgeList & DEL, bool removeloops) { commGrid = DEL.commGrid; typedef typename DER::LocalIT LIT; int nprocs = commGrid->GetSize(); int gridrows = commGrid->GetGridRows(); int gridcols = commGrid->GetGridCols(); std::vector< std::vector > data(nprocs); // enties are pre-converted to local indices before getting pushed into "data" LIT m_perproc = DEL.getGlobalV() / gridrows; LIT n_perproc = DEL.getGlobalV() / gridcols; if(sizeof(LIT) < sizeof(DELIT)) { std::ostringstream outs; outs << "Warning: Using smaller indices for the matrix than DistEdgeList\n"; outs << "Local matrices are " << m_perproc << "-by-" << n_perproc << std::endl; SpParHelper::Print(outs.str(), commGrid->GetWorld()); // commgrid initialized } LIT stages = MEM_EFFICIENT_STAGES; // to lower memory consumption, form sparse matrix in stages // even if local indices (LIT) are 32-bits, we should work with 64-bits for global info int64_t perstage = DEL.nedges / stages; LIT totrecv = 0; std::vector alledges; for(LIT s=0; s< stages; ++s) { int64_t n_befor = s*perstage; int64_t n_after= ((s==(stages-1))? DEL.nedges : ((s+1)*perstage)); // clear the source vertex by setting it to -1 int realedges = 0; // these are "local" realedges if(DEL.pedges) { for (int64_t i = n_befor; i < n_after; i++) { int64_t fr = get_v0_from_edge(&(DEL.pedges[i])); int64_t to = get_v1_from_edge(&(DEL.pedges[i])); if(fr >= 0 && to >= 0) // otherwise skip { IT lrow, lcol; int owner = Owner(DEL.getGlobalV(), DEL.getGlobalV(), fr, to, lrow, lcol); data[owner].push_back(lrow); // row_id data[owner].push_back(lcol); // col_id ++realedges; } } } else { for (int64_t i = n_befor; i < n_after; i++) { if(DEL.edges[2*i+0] >= 0 && DEL.edges[2*i+1] >= 0) // otherwise skip { IT lrow, lcol; int owner = Owner(DEL.getGlobalV(), DEL.getGlobalV(), DEL.edges[2*i+0], DEL.edges[2*i+1], lrow, lcol); data[owner].push_back(lrow); data[owner].push_back(lcol); ++realedges; } } } LIT * sendbuf = new LIT[2*realedges]; int * sendcnt = new int[nprocs]; int * sdispls = new int[nprocs]; for(int i=0; iGetWorld()); // share the counts sdispls[0] = 0; rdispls[0] = 0; for(int i=0; i().swap(data[i]); // ABAB: Total number of edges received might not be LIT-addressible // However, each edge_id is LIT-addressible IT thisrecv = std::accumulate(recvcnt,recvcnt+nprocs, static_cast(0)); // thisrecv = 2*locedges LIT * recvbuf = new LIT[thisrecv]; totrecv += thisrecv; MPI_Alltoallv(sendbuf, sendcnt, sdispls, MPIType(), recvbuf, recvcnt, rdispls, MPIType(), commGrid->GetWorld()); DeleteAll(sendcnt, recvcnt, sdispls, rdispls,sendbuf); std::copy (recvbuf,recvbuf+thisrecv,std::back_inserter(alledges)); // copy to all edges delete [] recvbuf; } int myprocrow = commGrid->GetRankInProcCol(); int myproccol = commGrid->GetRankInProcRow(); LIT locrows, loccols; if(myprocrow != gridrows-1) locrows = m_perproc; else locrows = DEL.getGlobalV() - myprocrow * m_perproc; if(myproccol != gridcols-1) loccols = n_perproc; else loccols = DEL.getGlobalV() - myproccol * n_perproc; SpTuples A(totrecv/2, locrows, loccols, alledges, removeloops); // alledges is empty upon return spSeq = new DER(A,false); // Convert SpTuples to DER } template IT SpParMat::RemoveLoops() { MPI_Comm DiagWorld = commGrid->GetDiagWorld(); IT totrem; IT removed = 0; if(DiagWorld != MPI_COMM_NULL) // Diagonal processors only { SpTuples tuples(*spSeq); delete spSeq; removed = tuples.RemoveLoops(); spSeq = new DER(tuples, false); // Convert to DER } MPI_Allreduce( &removed, & totrem, 1, MPIType(), MPI_SUM, commGrid->GetWorld()); return totrem; } template void SpParMat::AddLoops(NT loopval, bool replaceExisting) { MPI_Comm DiagWorld = commGrid->GetDiagWorld(); if(DiagWorld != MPI_COMM_NULL) // Diagonal processors only { typedef typename DER::LocalIT LIT; SpTuples tuples(*spSeq); delete spSeq; tuples.AddLoops(loopval, replaceExisting); tuples.SortColBased(); spSeq = new DER(tuples, false); // Convert to DER } } // Different values on the diagonal template void SpParMat::AddLoops(FullyDistVec loopvals, bool replaceExisting) { if(*loopvals.commGrid != *commGrid) { SpParHelper::Print("Grids are not comparable, SpParMat::AddLoops() fails!\n", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD,GRIDMISMATCH); } if (getncol()!= loopvals.TotalLength()) { SpParHelper::Print("The number of entries in loopvals is not equal to the number of diagonal entries.\n"); MPI_Abort(MPI_COMM_WORLD,DIMMISMATCH); } // Gather data on the diagonal processor IT locsize = loopvals.LocArrSize(); int rowProcs = commGrid->GetGridCols(); std::vector recvcnt(rowProcs, 0); std::vector rdpls(rowProcs, 0); MPI_Gather(&locsize, 1, MPI_INT, recvcnt.data(), 1, MPI_INT, commGrid->GetDiagOfProcRow(), commGrid->GetRowWorld()); std::partial_sum(recvcnt.data(), recvcnt.data()+rowProcs-1, rdpls.data()+1); IT totrecv = rdpls[rowProcs-1] + recvcnt[rowProcs-1]; assert((totrecv < std::numeric_limits::max())); std::vector rowvals(totrecv); MPI_Gatherv(loopvals.arr.data(), locsize, MPIType(), rowvals.data(), recvcnt.data(), rdpls.data(), MPIType(), commGrid->GetDiagOfProcRow(), commGrid->GetRowWorld()); MPI_Comm DiagWorld = commGrid->GetDiagWorld(); if(DiagWorld != MPI_COMM_NULL) // Diagonal processors only { typedef typename DER::LocalIT LIT; SpTuples tuples(*spSeq); delete spSeq; tuples.AddLoops(rowvals, replaceExisting); tuples.SortColBased(); spSeq = new DER(tuples, false); // Convert to DER } } //! Pre-allocates buffers for row communication //! additionally (if GATHERVOPT is defined, incomplete as of March 2016): //! - Splits the local column indices to sparse & dense pieces to avoid redundant AllGather (sparse pieces get p2p) template template void SpParMat::OptimizeForGraph500(OptBuf & optbuf) { if(spSeq->getnsplit() > 0) { SpParHelper::Print("Can not declare preallocated buffers for multithreaded execution\n", commGrid->GetWorld()); return; } typedef typename DER::LocalIT LocIT; // ABAB: should match the type of LIT. Check? // Set up communication buffers, one for all LocIT mA = spSeq->getnrow(); LocIT nA = spSeq->getncol(); int p_c = commGrid->GetGridCols(); int p_r = commGrid->GetGridRows(); LocIT rwperproc = mA / p_c; // per processors in row-wise communication LocIT cwperproc = nA / p_r; // per processors in column-wise communication #ifdef GATHERVOPT LocIT * colinds = seq->GetDCSC()->jc; // local nonzero column id's LocIT locnzc = seq->getnzc(); LocIT cci = 0; // index to column id's array (cci: current column index) int * gsizes = NULL; IT * ents = NULL; IT * dpls = NULL; std::vector pack2send; FullyDistSpVec dummyRHS ( commGrid, getncol()); // dummy RHS vector to estimate index start position IT recveclen; for(int pid = 1; pid <= p_r; pid++) { IT diagoffset; MPI_Status status; IT offset = dummyRHS.RowLenUntil(pid-1); int diagneigh = commGrid->GetComplementRank(); MPI_Sendrecv(&offset, 1, MPIType(), diagneigh, TRTAGNZ, &diagoffset, 1, MPIType(), diagneigh, TRTAGNZ, commGrid->GetWorld(), &status); LocIT endind = (pid == p_r)? nA : static_cast(pid) * cwperproc; // the last one might have a larger share (is this fitting to the vector boundaries?) while(cci < locnzc && colinds[cci] < endind) { pack2send.push_back(colinds[cci++]-diagoffset); } if(pid-1 == myrank) gsizes = new int[p_r]; MPI_Gather(&mysize, 1, MPI_INT, gsizes, 1, MPI_INT, pid-1, commGrid->GetColWorld()); if(pid-1 == myrank) { IT colcnt = std::accumulate(gsizes, gsizes+p_r, static_cast(0)); recvbuf = new IT[colcnt]; dpls = new IT[p_r](); // displacements (zero initialized pid) std::partial_sum(gsizes, gsizes+p_r-1, dpls+1); } // int MPI_Gatherv (void* sbuf, int scount, MPI_Datatype stype, void* rbuf, int *rcount, int* displs, MPI_Datatype rtype, int root, MPI_Comm comm) MPI_Gatherv(SpHelper::p2a(pack2send), mysize, MPIType(), recvbuf, gsizes, dpls, MPIType(), pid-1, commGrid->GetColWorld()); std::vector().swap(pack2send); if(pid-1 == myrank) { recveclen = dummyRHS.MyLocLength(); std::vector< std::vector > service(recveclen); for(int i=0; i< p_r; ++i) { for(int j=0; j< gsizes[i]; ++j) { IT colid2update = recvbuf[dpls[i]+j]; if(service[colid2update].size() < GATHERVNEIGHLIMIT) { service.push_back(i); } // else don't increase any further and mark it done after the iterations are complete } } } } #endif std::vector isthere(mA, false); // perhaps the only appropriate use of this crippled data structure std::vector maxlens(p_c,0); // maximum data size to be sent to any neighbor along the processor row for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) { for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit != spSeq->endnz(colit); ++nzit) { LocIT rowid = nzit.rowid(); if(!isthere[rowid]) { LocIT owner = std::min(nzit.rowid() / rwperproc, (LocIT) p_c-1); maxlens[owner]++; isthere[rowid] = true; } } } SpParHelper::Print("Optimization buffers set\n", commGrid->GetWorld()); optbuf.Set(maxlens,mA); } template void SpParMat::ActivateThreading(int numsplits) { spSeq->RowSplit(numsplits); } /** * Parallel routine that returns A*A on the semiring SR * Uses only MPI-1 features (relies on simple blocking broadcast) **/ template template void SpParMat::Square () { int stages, dummy; // last two parameters of productgrid are ignored for synchronous multiplication std::shared_ptr Grid = ProductGrid(commGrid.get(), commGrid.get(), stages, dummy, dummy); typedef typename DER::LocalIT LIT; LIT AA_m = spSeq->getnrow(); LIT AA_n = spSeq->getncol(); DER seqTrn = spSeq->TransposeConst(); // will be automatically discarded after going out of scope MPI_Barrier(commGrid->GetWorld()); LIT ** NRecvSizes = SpHelper::allocate2D(DER::esscount, stages); LIT ** TRecvSizes = SpHelper::allocate2D(DER::esscount, stages); SpParHelper::GetSetSizes( *spSeq, NRecvSizes, commGrid->GetRowWorld()); SpParHelper::GetSetSizes( seqTrn, TRecvSizes, commGrid->GetColWorld()); // Remotely fetched matrices are stored as pointers DER * NRecv; DER * TRecv; std::vector< SpTuples *> tomerge; int Nself = commGrid->GetRankInProcRow(); int Tself = commGrid->GetRankInProcCol(); for(int i = 0; i < stages; ++i) { std::vector ess; if(i == Nself) NRecv = spSeq; // shallow-copy else { ess.resize(DER::esscount); for(int j=0; j< DER::esscount; ++j) ess[j] = NRecvSizes[j][i]; // essentials of the ith matrix in this row NRecv = new DER(); // first, create the object } SpParHelper::BCastMatrix(Grid->GetRowWorld(), *NRecv, ess, i); // then, broadcast its elements ess.clear(); if(i == Tself) TRecv = &seqTrn; // shallow-copy else { ess.resize(DER::esscount); for(int j=0; j< DER::esscount; ++j) ess[j] = TRecvSizes[j][i]; TRecv = new DER(); } SpParHelper::BCastMatrix(Grid->GetColWorld(), *TRecv, ess, i); SpTuples * AA_cont = MultiplyReturnTuples(*NRecv, *TRecv, false, true); if(!AA_cont->isZero()) tomerge.push_back(AA_cont); if(i != Nself) delete NRecv; if(i != Tself) delete TRecv; } SpHelper::deallocate2D(NRecvSizes, DER::esscount); SpHelper::deallocate2D(TRecvSizes, DER::esscount); delete spSeq; spSeq = new DER(MergeAll(tomerge, AA_m, AA_n), false); // First get the result in SpTuples, then convert to UDER for(unsigned int i=0; i void SpParMat::Transpose() { if(commGrid->myproccol == commGrid->myprocrow) // Diagonal { spSeq->Transpose(); } else { typedef typename DER::LocalIT LIT; SpTuples Atuples(*spSeq); LIT locnnz = Atuples.getnnz(); LIT * rows = new LIT[locnnz]; LIT * cols = new LIT[locnnz]; NT * vals = new NT[locnnz]; for(LIT i=0; i < locnnz; ++i) { rows[i] = Atuples.colindex(i); // swap (i,j) here cols[i] = Atuples.rowindex(i); vals[i] = Atuples.numvalue(i); } LIT locm = getlocalcols(); LIT locn = getlocalrows(); delete spSeq; LIT remotem, remoten, remotennz; std::swap(locm,locn); int diagneigh = commGrid->GetComplementRank(); MPI_Status status; MPI_Sendrecv(&locnnz, 1, MPIType(), diagneigh, TRTAGNZ, &remotennz, 1, MPIType(), diagneigh, TRTAGNZ, commGrid->GetWorld(), &status); MPI_Sendrecv(&locn, 1, MPIType(), diagneigh, TRTAGM, &remotem, 1, MPIType(), diagneigh, TRTAGM, commGrid->GetWorld(), &status); MPI_Sendrecv(&locm, 1, MPIType(), diagneigh, TRTAGN, &remoten, 1, MPIType(), diagneigh, TRTAGN, commGrid->GetWorld(), &status); LIT * rowsrecv = new LIT[remotennz]; MPI_Sendrecv(rows, locnnz, MPIType(), diagneigh, TRTAGROWS, rowsrecv, remotennz, MPIType(), diagneigh, TRTAGROWS, commGrid->GetWorld(), &status); delete [] rows; LIT * colsrecv = new LIT[remotennz]; MPI_Sendrecv(cols, locnnz, MPIType(), diagneigh, TRTAGCOLS, colsrecv, remotennz, MPIType(), diagneigh, TRTAGCOLS, commGrid->GetWorld(), &status); delete [] cols; NT * valsrecv = new NT[remotennz]; MPI_Sendrecv(vals, locnnz, MPIType(), diagneigh, TRTAGVALS, valsrecv, remotennz, MPIType(), diagneigh, TRTAGVALS, commGrid->GetWorld(), &status); delete [] vals; std::tuple * arrtuples = new std::tuple[remotennz]; for(LIT i=0; i< remotennz; ++i) { arrtuples[i] = std::make_tuple(rowsrecv[i], colsrecv[i], valsrecv[i]); } DeleteAll(rowsrecv, colsrecv, valsrecv); ColLexiCompare collexicogcmp; sort(arrtuples , arrtuples+remotennz, collexicogcmp ); // sort w.r.t columns here spSeq = new DER(); spSeq->Create( remotennz, remotem, remoten, arrtuples); // the deletion of arrtuples[] is handled by SpMat::Create } } template template void SpParMat< IT,NT,DER >::SaveGathered(std::string filename, HANDLER handler, bool transpose) const { int proccols = commGrid->GetGridCols(); int procrows = commGrid->GetGridRows(); IT totalm = getnrow(); IT totaln = getncol(); IT totnnz = getnnz(); int flinelen = 0; std::ofstream out; if(commGrid->GetRank() == 0) { std::string s; std::stringstream strm; strm << "%%MatrixMarket matrix coordinate real general" << std::endl; strm << totalm << " " << totaln << " " << totnnz << std::endl; s = strm.str(); out.open(filename.c_str(),std::ios_base::trunc); flinelen = s.length(); out.write(s.c_str(), flinelen); out.close(); } int colrank = commGrid->GetRankInProcCol(); int colneighs = commGrid->GetGridRows(); IT * locnrows = new IT[colneighs]; // number of rows is calculated by a reduction among the processor column locnrows[colrank] = (IT) getlocalrows(); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(),locnrows, 1, MPIType(), commGrid->GetColWorld()); IT roffset = std::accumulate(locnrows, locnrows+colrank, 0); delete [] locnrows; MPI_Datatype datatype; MPI_Type_contiguous(sizeof(std::pair), MPI_CHAR, &datatype); MPI_Type_commit(&datatype); for(int i = 0; i < procrows; i++) // for all processor row (in order) { if(commGrid->GetRankInProcCol() == i) // only the ith processor row { IT localrows = spSeq->getnrow(); // same along the processor row std::vector< std::vector< std::pair > > csr(localrows); if(commGrid->GetRankInProcRow() == 0) // get the head of processor row { IT localcols = spSeq->getncol(); // might be different on the last processor on this processor row MPI_Bcast(&localcols, 1, MPIType(), 0, commGrid->GetRowWorld()); for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over nonempty subcolumns { for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit != spSeq->endnz(colit); ++nzit) { csr[nzit.rowid()].push_back( std::make_pair(colit.colid(), nzit.value()) ); } } } else // get the rest of the processors { IT n_perproc; MPI_Bcast(&n_perproc, 1, MPIType(), 0, commGrid->GetRowWorld()); IT noffset = commGrid->GetRankInProcRow() * n_perproc; for(typename DER::SpColIter colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over nonempty subcolumns { for(typename DER::SpColIter::NzIter nzit = spSeq->begnz(colit); nzit != spSeq->endnz(colit); ++nzit) { csr[nzit.rowid()].push_back( std::make_pair(colit.colid() + noffset, nzit.value()) ); } } } std::pair * ents = NULL; int * gsizes = NULL, * dpls = NULL; if(commGrid->GetRankInProcRow() == 0) // only the head of processor row { out.open(filename.c_str(),std::ios_base::app); gsizes = new int[proccols]; dpls = new int[proccols](); // displacements (zero initialized pid) } for(int j = 0; j < localrows; ++j) { IT rowcnt = 0; sort(csr[j].begin(), csr[j].end()); int mysize = csr[j].size(); MPI_Gather(&mysize, 1, MPI_INT, gsizes, 1, MPI_INT, 0, commGrid->GetRowWorld()); if(commGrid->GetRankInProcRow() == 0) { rowcnt = std::accumulate(gsizes, gsizes+proccols, static_cast(0)); std::partial_sum(gsizes, gsizes+proccols-1, dpls+1); ents = new std::pair[rowcnt]; // nonzero entries in the j'th local row } // int MPI_Gatherv (void* sbuf, int scount, MPI_Datatype stype, // void* rbuf, int *rcount, int* displs, MPI_Datatype rtype, int root, MPI_Comm comm) MPI_Gatherv(SpHelper::p2a(csr[j]), mysize, datatype, ents, gsizes, dpls, datatype, 0, commGrid->GetRowWorld()); if(commGrid->GetRankInProcRow() == 0) { for(int k=0; k< rowcnt; ++k) { //out << j + roffset + 1 << "\t" << ents[k].first + 1 <<"\t" << ents[k].second << endl; if (!transpose) // regular out << j + roffset + 1 << "\t" << ents[k].first + 1 << "\t"; else // transpose row/column out << ents[k].first + 1 << "\t" << j + roffset + 1 << "\t"; handler.save(out, ents[k].second, j + roffset, ents[k].first); out << std::endl; } delete [] ents; } } if(commGrid->GetRankInProcRow() == 0) { DeleteAll(gsizes, dpls); out.close(); } } // end_if the ith processor row MPI_Barrier(commGrid->GetWorld()); // signal the end of ith processor row iteration (so that all processors block) } } //! Private subroutine of ReadGeneralizedTuples //! totallength is the length of the dictionary, which we don't know in this labeled tuples format apriori template MPI_File SpParMat< IT,NT,DER >::TupleRead1stPassNExchange (const std::string & filename, TYPE2SEND * & senddata, IT & totsend, FullyDistVec & distmapper, uint64_t & totallength) { int myrank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); MPI_Offset fpos, end_fpos; struct stat st; // get file size if (stat(filename.c_str(), &st) == -1) { MPI_Abort(MPI_COMM_WORLD, NOFILE); } int64_t file_size = st.st_size; if(myrank == 0) // the offset needs to be for this rank { std::cout << "File is " << file_size << " bytes" << std::endl; } fpos = myrank * file_size / nprocs; if(myrank != (nprocs-1)) end_fpos = (myrank + 1) * file_size / nprocs; else end_fpos = file_size; MPI_File mpi_fh; MPI_File_open (commGrid->commWorld, const_cast(filename.c_str()), MPI_MODE_RDONLY, MPI_INFO_NULL, &mpi_fh); typedef std::map KEYMAP; // due to potential (but extremely unlikely) collusions in MurmurHash, make the key to the std:map the string itself std::vector< KEYMAP > allkeys(nprocs); // map keeps the outgoing data unique, we could have applied this to HipMer too std::vector lines; bool finished = SpParHelper::FetchBatch(mpi_fh, fpos, end_fpos, true, lines, myrank); int64_t entriesread = lines.size(); SpHelper::ProcessLinesWithStringKeys(allkeys, lines,nprocs); while(!finished) { finished = SpParHelper::FetchBatch(mpi_fh, fpos, end_fpos, false, lines, myrank); entriesread += lines.size(); SpHelper::ProcessLinesWithStringKeys(allkeys, lines,nprocs); } int64_t allentriesread; MPI_Reduce(&entriesread, &allentriesread, 1, MPIType(), MPI_SUM, 0, commGrid->commWorld); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "Initial reading finished. Total number of entries read across all processors is " << allentriesread << std::endl; #endif int * sendcnt = new int[nprocs]; int * recvcnt = new int[nprocs]; for(int i=0; iGetWorld()); // share the counts int * sdispls = new int[nprocs](); int * rdispls = new int[nprocs](); std::partial_sum(sendcnt, sendcnt+nprocs-1, sdispls+1); std::partial_sum(recvcnt, recvcnt+nprocs-1, rdispls+1); totsend = std::accumulate(sendcnt,sendcnt+nprocs, static_cast(0)); IT totrecv = std::accumulate(recvcnt,recvcnt+nprocs, static_cast(0)); assert((totsend < std::numeric_limits::max())); assert((totrecv < std::numeric_limits::max())); // The following are declared in SpParMat.h // typedef std::array STRASARRAY; // typedef std::pair< STRASARRAY, uint64_t> TYPE2SEND; senddata = new TYPE2SEND[totsend]; #pragma omp parallel for for(int i=0; i vname; std::copy( pobj.first.begin(), pobj.first.end(), vname.begin() ); if(pobj.first.length() < MAXVERTNAME) vname[pobj.first.length()] = '\0'; // null termination senddata[sdispls[i]+j] = TYPE2SEND(vname, pobj.second); j++; } } allkeys.clear(); // allkeys is no longer needed after this point MPI_Datatype MPI_HASH; MPI_Type_contiguous(sizeof(TYPE2SEND), MPI_CHAR, &MPI_HASH); MPI_Type_commit(&MPI_HASH); TYPE2SEND * recvdata = new TYPE2SEND[totrecv]; MPI_Alltoallv(senddata, sendcnt, sdispls, MPI_HASH, recvdata, recvcnt, rdispls, MPI_HASH, commGrid->GetWorld()); // do not delete send buffers yet as we will use them to recv back the data std::set< std::pair > uniqsorted; for(IT i=0; i< totrecv; ++i) { auto locnull = std::find(recvdata[i].first.begin(), recvdata[i].first.end(), '\0'); // find the null character (or string::end) std::string strtmp(recvdata[i].first.begin(), locnull); // range constructor uniqsorted.insert(std::make_pair(recvdata[i].second, strtmp)); } uint64_t uniqsize = uniqsorted.size(); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "out of " << totrecv << " vertices received, " << uniqsize << " were unique" << std::endl; #endif uint64_t sizeuntil = 0; totallength = 0; MPI_Exscan( &uniqsize, &sizeuntil, 1, MPIType(), MPI_SUM, commGrid->GetWorld() ); MPI_Allreduce(&uniqsize, &totallength, 1, MPIType(), MPI_SUM, commGrid->GetWorld()); if(myrank == 0) sizeuntil = 0; // because MPI_Exscan says the recvbuf in process 0 is undefined distmapper = FullyDistVec(commGrid, totallength,STRASARRAY{}); // invindex does not conform to FullyDistVec boundaries, otherwise its contents are essentially the same as distmapper KEYMAP invindex; // KEYMAP is map. uint64_t locindex = 0; std::vector< std::vector< IT > > locs_send(nprocs); std::vector< std::vector< std::string > > data_send(nprocs); int * map_scnt = new int[nprocs](); // send counts for this map only (to no confuse with the other sendcnt) for(auto itr = uniqsorted.begin(); itr != uniqsorted.end(); ++itr) { uint64_t globalindex = sizeuntil + locindex; invindex.insert(std::make_pair(itr->second, globalindex)); IT newlocid; int owner = distmapper.Owner(globalindex, newlocid); //if(myrank == 0) // std::cout << "invindex received " << itr->second << " with global index " << globalindex << " to be owned by " << owner << " with index " << newlocid << std::endl; locs_send[owner].push_back(newlocid); data_send[owner].push_back(itr->second); map_scnt[owner]++; locindex++; } uniqsorted.clear(); // clear memory /* BEGIN: Redistributing the permutation vector to fit the FullyDistVec semantics */ SpParHelper::ReDistributeToVector(map_scnt, locs_send, data_send, distmapper.arr, commGrid->GetWorld()); // map_scnt is deleted here /* END: Redistributing the permutation vector to fit the FullyDistVec semantics */ for(IT i=0; i< totrecv; ++i) { auto locnull = std::find(recvdata[i].first.begin(), recvdata[i].first.end(), '\0'); std::string searchstr(recvdata[i].first.begin(), locnull); // range constructor auto resp = invindex.find(searchstr); // recvdata[i] is of type pair< STRASARRAY, uint64_t> if (resp != invindex.end()) { recvdata[i].second = resp->second; // now instead of random numbers, recvdata's second entry will be its new index } else std::cout << "Assertion failed at proc " << myrank << ": the absence of the entry in invindex is unexpected!!!" << std::endl; } MPI_Alltoallv(recvdata, recvcnt, rdispls, MPI_HASH, senddata, sendcnt, sdispls, MPI_HASH, commGrid->GetWorld()); DeleteAll(recvdata, sendcnt, recvcnt, sdispls, rdispls); MPI_Type_free(&MPI_HASH); // the following gets deleted here: allkeys return mpi_fh; } //! Handles all sorts of orderings as long as there are no duplicates //! Does not take matrix market banner (only tuples) //! Data can be load imbalanced and the vertex labels can be arbitrary strings //! Replaces ReadDistribute for imbalanced arbitrary input in tuples format template template FullyDistVec > SpParMat< IT,NT,DER >::ReadGeneralizedTuples (const std::string & filename, _BinaryOperation BinOp) { int myrank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); TYPE2SEND * senddata; IT totsend; uint64_t totallength; FullyDistVec distmapper(commGrid); // choice of array over string = array is required to be a contiguous container and an aggregate MPI_File mpi_fh = TupleRead1stPassNExchange(filename, senddata, totsend, distmapper, totallength); typedef std::map KEYMAP; KEYMAP ultimateperm; // the ultimate permutation for(IT i=0; i< totsend; ++i) { auto locnull = std::find(senddata[i].first.begin(), senddata[i].first.end(), '\0'); std::string searchstr(senddata[i].first.begin(), locnull); auto ret = ultimateperm.emplace(std::make_pair(searchstr, senddata[i].second)); if(!ret.second) // the second is the boolean that tells success { // remember, we only sent unique vertex ids in the first place so we are expecting unique values in return std::cout << "the duplication in ultimateperm is unexpected!!!" << std::endl; } } delete [] senddata; // rename the data now, first reset file pointers MPI_Offset fpos, end_fpos; struct stat st; // get file size if (stat(filename.c_str(), &st) == -1) { MPI_Abort(MPI_COMM_WORLD, NOFILE); } int64_t file_size = st.st_size; fpos = myrank * file_size / nprocs; if(myrank != (nprocs-1)) end_fpos = (myrank + 1) * file_size / nprocs; else end_fpos = file_size; typedef typename DER::LocalIT LIT; std::vector rows; std::vector cols; std::vector vals; std::vector lines; bool finished = SpParHelper::FetchBatch(mpi_fh, fpos, end_fpos, true, lines, myrank); int64_t entriesread = lines.size(); SpHelper::ProcessStrLinesNPermute(rows, cols, vals, lines, ultimateperm); while(!finished) { finished = SpParHelper::FetchBatch(mpi_fh, fpos, end_fpos, false, lines, myrank); entriesread += lines.size(); SpHelper::ProcessStrLinesNPermute(rows, cols, vals, lines, ultimateperm); } int64_t allentriesread; MPI_Reduce(&entriesread, &allentriesread, 1, MPIType(), MPI_SUM, 0, commGrid->commWorld); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "Second reading finished. Total number of entries read across all processors is " << allentriesread << std::endl; #endif MPI_File_close(&mpi_fh); std::vector< std::vector < std::tuple > > data(nprocs); LIT locsize = rows.size(); // remember: locsize != entriesread (unless the matrix is unsymmetric) for(LIT i=0; i().swap(rows); std::vector().swap(cols); std::vector().swap(vals); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "Packing to recipients finished, about to send..." << std::endl; #endif if(spSeq) delete spSeq; SparseCommon(data, locsize, totallength, totallength, BinOp); // PrintInfo(); // distmapper.ParallelWrite("distmapper.mtx", 1, CharArraySaveHandler()); return distmapper; } //! Handles all sorts of orderings, even duplicates (what happens to them is determined by BinOp) //! Requires proper matrix market banner at the moment //! Replaces ReadDistribute for properly load balanced input in matrix market format template template void SpParMat< IT,NT,DER >::ParallelReadMM (const std::string & filename, bool onebased, _BinaryOperation BinOp) { int32_t type = -1; int32_t symmetric = 0; int64_t nrows, ncols, nonzeros; int64_t linesread = 0; FILE *f; int myrank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); if(myrank == 0) { MM_typecode matcode; if ((f = fopen(filename.c_str(), "r")) == NULL) { printf("COMBBLAS: Matrix-market file %s can not be found\n", filename.c_str()); MPI_Abort(MPI_COMM_WORLD, NOFILE); } if (mm_read_banner(f, &matcode) != 0) { printf("Could not process Matrix Market banner.\n"); exit(1); } linesread++; if (mm_is_complex(matcode)) { printf("Sorry, this application does not support complext types"); printf("Market Market type: [%s]\n", mm_typecode_to_str(matcode)); } else if(mm_is_real(matcode)) { std::cout << "Matrix is Float" << std::endl; type = 0; } else if(mm_is_integer(matcode)) { std::cout << "Matrix is Integer" << std::endl; type = 1; } else if(mm_is_pattern(matcode)) { std::cout << "Matrix is Boolean" << std::endl; type = 2; } if(mm_is_symmetric(matcode) || mm_is_hermitian(matcode)) { std::cout << "Matrix is symmetric" << std::endl; symmetric = 1; } int ret_code; if ((ret_code = mm_read_mtx_crd_size(f, &nrows, &ncols, &nonzeros, &linesread)) !=0) // ABAB: mm_read_mtx_crd_size made 64-bit friendly exit(1); std::cout << "Total number of nonzeros expected across all processors is " << nonzeros << std::endl; } MPI_Bcast(&type, 1, MPI_INT, 0, commGrid->commWorld); MPI_Bcast(&symmetric, 1, MPI_INT, 0, commGrid->commWorld); MPI_Bcast(&nrows, 1, MPIType(), 0, commGrid->commWorld); MPI_Bcast(&ncols, 1, MPIType(), 0, commGrid->commWorld); MPI_Bcast(&nonzeros, 1, MPIType(), 0, commGrid->commWorld); // Use fseek again to go backwards two bytes and check that byte with fgetc struct stat st; // get file size if (stat(filename.c_str(), &st) == -1) { MPI_Abort(MPI_COMM_WORLD, NOFILE); } int64_t file_size = st.st_size; MPI_Offset fpos, end_fpos, endofheader; if(commGrid->GetRank() == 0) // the offset needs to be for this rank { std::cout << "File is " << file_size << " bytes" << std::endl; fpos = ftell(f); endofheader = fpos; MPI_Bcast(&endofheader, 1, MPIType(), 0, commGrid->commWorld); fclose(f); } else { MPI_Bcast(&endofheader, 1, MPIType(), 0, commGrid->commWorld); // receive the file loc at the end of header fpos = endofheader + myrank * (file_size-endofheader) / nprocs; } if(myrank != (nprocs-1)) end_fpos = endofheader + (myrank + 1) * (file_size-endofheader) / nprocs; else end_fpos = file_size; MPI_File mpi_fh; MPI_File_open (commGrid->commWorld, const_cast(filename.c_str()), MPI_MODE_RDONLY, MPI_INFO_NULL, &mpi_fh); typedef typename DER::LocalIT LIT; std::vector rows; std::vector cols; std::vector vals; std::vector lines; bool finished = SpParHelper::FetchBatch(mpi_fh, fpos, end_fpos, true, lines, myrank); int64_t entriesread = lines.size(); SpHelper::ProcessLines(rows, cols, vals, lines, symmetric, type, onebased); MPI_Barrier(commGrid->commWorld); while(!finished) { finished = SpParHelper::FetchBatch(mpi_fh, fpos, end_fpos, false, lines, myrank); entriesread += lines.size(); SpHelper::ProcessLines(rows, cols, vals, lines, symmetric, type, onebased); } int64_t allentriesread; MPI_Reduce(&entriesread, &allentriesread, 1, MPIType(), MPI_SUM, 0, commGrid->commWorld); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "Reading finished. Total number of entries read across all processors is " << allentriesread << std::endl; #endif std::vector< std::vector < std::tuple > > data(nprocs); LIT locsize = rows.size(); // remember: locsize != entriesread (unless the matrix is unsymmetric) for(LIT i=0; i().swap(rows); std::vector().swap(cols); std::vector().swap(vals); #ifdef COMBBLAS_DEBUG if(myrank == 0) std::cout << "Packing to recepients finished, about to send..." << std::endl; #endif if(spSeq) delete spSeq; SparseCommon(data, locsize, nrows, ncols, BinOp); } //! Handles all sorts of orderings as long as there are no duplicates //! May perform better when the data is already reverse column-sorted (i.e. in decreasing order) //! if nonum is true, then numerics are not supplied and they are assumed to be all 1's template template void SpParMat< IT,NT,DER >::ReadDistribute (const std::string & filename, int master, bool nonum, HANDLER handler, bool transpose, bool pario) { #ifdef TAU_PROFILE TAU_PROFILE_TIMER(rdtimer, "ReadDistribute", "void SpParMat::ReadDistribute (const string & , int, bool, HANDLER, bool)", TAU_DEFAULT); TAU_PROFILE_START(rdtimer); #endif std::ifstream infile; FILE * binfile = NULL; // points to "past header" if the file is binary int seeklength = 0; HeaderInfo hfile; if(commGrid->GetRank() == master) // 1 processor { hfile = ParseHeader(filename, binfile, seeklength); } MPI_Bcast(&seeklength, 1, MPI_INT, master, commGrid->commWorld); IT total_m, total_n, total_nnz; IT m_perproc = 0, n_perproc = 0; int colneighs = commGrid->GetGridRows(); // number of neighbors along this processor column (including oneself) int rowneighs = commGrid->GetGridCols(); // number of neighbors along this processor row (including oneself) IT buffpercolneigh = MEMORYINBYTES / (colneighs * (2 * sizeof(IT) + sizeof(NT))); IT buffperrowneigh = MEMORYINBYTES / (rowneighs * (2 * sizeof(IT) + sizeof(NT))); if(pario) { // since all colneighs will be reading the data at the same time // chances are they might all read the data that should go to one // in that case buffperrowneigh > colneighs * buffpercolneigh // in order not to overflow buffpercolneigh /= colneighs; if(seeklength == 0) SpParHelper::Print("COMBBLAS: Parallel I/O requested but binary header is corrupted\n", commGrid->GetWorld()); } // make sure that buffperrowneigh >= buffpercolneigh to cover for this patological case: // -- all data received by a given column head (by vertical communication) are headed to a single processor along the row // -- then making sure buffperrowneigh >= buffpercolneigh guarantees that the horizontal buffer will never overflow buffperrowneigh = std::max(buffperrowneigh, buffpercolneigh); if(std::max(buffpercolneigh * colneighs, buffperrowneigh * rowneighs) > std::numeric_limits::max()) { SpParHelper::Print("COMBBLAS: MPI doesn't support sending int64_t send/recv counts or displacements\n", commGrid->GetWorld()); } int * cdispls = new int[colneighs]; for (IT i=0; iGetRankInProcCol(master); // get master's rank in its processor column int rankinrow = commGrid->GetRankInProcRow(master); std::vector< std::tuple > localtuples; if(commGrid->GetRank() == master) // 1 processor { if( !hfile.fileexists ) { SpParHelper::Print( "COMBBLAS: Input file doesn't exist\n", commGrid->GetWorld()); total_n = 0; total_m = 0; BcastEssentials(commGrid->commWorld, total_m, total_n, total_nnz, master); return; } if (hfile.headerexists && hfile.format == 1) { SpParHelper::Print("COMBBLAS: Ascii input with binary headers is not supported\n", commGrid->GetWorld()); total_n = 0; total_m = 0; BcastEssentials(commGrid->commWorld, total_m, total_n, total_nnz, master); return; } if ( !hfile.headerexists ) // no header - ascii file (at this point, file exists) { infile.open(filename.c_str()); char comment[256]; infile.getline(comment,256); while(comment[0] == '%') { infile.getline(comment,256); } std::stringstream ss; ss << std::string(comment); ss >> total_m >> total_n >> total_nnz; if(pario) { SpParHelper::Print("COMBBLAS: Trying to read binary headerless file in parallel, aborting\n", commGrid->GetWorld()); total_n = 0; total_m = 0; BcastEssentials(commGrid->commWorld, total_m, total_n, total_nnz, master); return; } } else // hfile.headerexists && hfile.format == 0 { total_m = hfile.m; total_n = hfile.n; total_nnz = hfile.nnz; } m_perproc = total_m / colneighs; n_perproc = total_n / rowneighs; BcastEssentials(commGrid->commWorld, total_m, total_n, total_nnz, master); AllocateSetBuffers(rows, cols, vals, rcurptrs, ccurptrs, rowneighs, colneighs, buffpercolneigh); if(seeklength > 0 && pario) // sqrt(p) processors also do parallel binary i/o { IT entriestoread = total_nnz / colneighs; #ifdef IODEBUG std::ofstream oput; commGrid->OpenDebugFile("Read", oput); oput << "Total nnz: " << total_nnz << " entries to read: " << entriestoread << std::endl; oput.close(); #endif ReadAllMine(binfile, rows, cols, vals, localtuples, rcurptrs, ccurptrs, rdispls, cdispls, m_perproc, n_perproc, rowneighs, colneighs, buffperrowneigh, buffpercolneigh, entriestoread, handler, rankinrow, transpose); } else // only this (master) is doing I/O (text or binary) { IT temprow, tempcol; NT tempval; IT ntrow = 0, ntcol = 0; // not transposed row and column index char line[1024]; bool nonumline = nonum; IT cnz = 0; for(; cnz < total_nnz; ++cnz) { int colrec; size_t commonindex; std::stringstream linestream; if( (!hfile.headerexists) && (!infile.eof())) { // read one line at a time so that missing numerical values can be detected infile.getline(line, 1024); linestream << line; linestream >> temprow >> tempcol; if (!nonum) { // see if this line has a value linestream >> std::skipws; nonumline = linestream.eof(); } --temprow; // file is 1-based where C-arrays are 0-based --tempcol; ntrow = temprow; ntcol = tempcol; } else if(hfile.headerexists && (!feof(binfile)) ) { handler.binaryfill(binfile, temprow , tempcol, tempval); } if (transpose) { IT swap = temprow; temprow = tempcol; tempcol = swap; } colrec = std::min(static_cast(temprow / m_perproc), colneighs-1); // precipient processor along the column commonindex = colrec * buffpercolneigh + ccurptrs[colrec]; rows[ commonindex ] = temprow; cols[ commonindex ] = tempcol; if( (!hfile.headerexists) && (!infile.eof())) { vals[ commonindex ] = nonumline ? handler.getNoNum(ntrow, ntcol) : handler.read(linestream, ntrow, ntcol); //tempval; } else if(hfile.headerexists && (!feof(binfile)) ) { vals[ commonindex ] = tempval; } ++ (ccurptrs[colrec]); if(ccurptrs[colrec] == buffpercolneigh || (cnz == (total_nnz-1)) ) // one buffer is full, or file is done ! { MPI_Scatter(ccurptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, rankincol, commGrid->colWorld); // first, send the receive counts // generate space for own recv data ... (use arrays because vector is cripled, if NT=bool) IT * temprows = new IT[recvcount]; IT * tempcols = new IT[recvcount]; NT * tempvals = new NT[recvcount]; // then, send all buffers that to their recipients ... MPI_Scatterv(rows, ccurptrs, cdispls, MPIType(), temprows, recvcount, MPIType(), rankincol, commGrid->colWorld); MPI_Scatterv(cols, ccurptrs, cdispls, MPIType(), tempcols, recvcount, MPIType(), rankincol, commGrid->colWorld); MPI_Scatterv(vals, ccurptrs, cdispls, MPIType(), tempvals, recvcount, MPIType(), rankincol, commGrid->colWorld); std::fill_n(ccurptrs, colneighs, 0); // finally, reset current pointers ! DeleteAll(rows, cols, vals); HorizontalSend(rows, cols, vals,temprows, tempcols, tempvals, localtuples, rcurptrs, rdispls, buffperrowneigh, rowneighs, recvcount, m_perproc, n_perproc, rankinrow); if( cnz != (total_nnz-1) ) // otherwise the loop will exit with noone to claim memory back { // reuse these buffers for the next vertical communication rows = new IT [ buffpercolneigh * colneighs ]; cols = new IT [ buffpercolneigh * colneighs ]; vals = new NT [ buffpercolneigh * colneighs ]; } } // end_if for "send buffer is full" case } // end_for for "cnz < entriestoread" case assert (cnz == total_nnz); // Signal the end of file to other processors along the column std::fill_n(ccurptrs, colneighs, std::numeric_limits::max()); MPI_Scatter(ccurptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, rankincol, commGrid->colWorld); // And along the row ... std::fill_n(rcurptrs, rowneighs, std::numeric_limits::max()); MPI_Scatter(rcurptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, rankinrow, commGrid->rowWorld); } // end of "else" (only one processor reads) block } // end_if for "master processor" case else if( commGrid->OnSameProcCol(master) ) // (r-1) processors { BcastEssentials(commGrid->commWorld, total_m, total_n, total_nnz, master); m_perproc = total_m / colneighs; n_perproc = total_n / rowneighs; if(seeklength > 0 && pario) // these processors also do parallel binary i/o { binfile = fopen(filename.c_str(), "rb"); IT entrysize = handler.entrylength(); int myrankincol = commGrid->GetRankInProcCol(); IT perreader = total_nnz / colneighs; IT read_offset = entrysize * static_cast(myrankincol) * perreader + seeklength; IT entriestoread = perreader; if (myrankincol == colneighs-1) entriestoread = total_nnz - static_cast(myrankincol) * perreader; fseek(binfile, read_offset, SEEK_SET); #ifdef IODEBUG std::ofstream oput; commGrid->OpenDebugFile("Read", oput); oput << "Total nnz: " << total_nnz << " OFFSET : " << read_offset << " entries to read: " << entriestoread << std::endl; oput.close(); #endif AllocateSetBuffers(rows, cols, vals, rcurptrs, ccurptrs, rowneighs, colneighs, buffpercolneigh); ReadAllMine(binfile, rows, cols, vals, localtuples, rcurptrs, ccurptrs, rdispls, cdispls, m_perproc, n_perproc, rowneighs, colneighs, buffperrowneigh, buffpercolneigh, entriestoread, handler, rankinrow, transpose); } else // only master does the I/O { while(total_n > 0 || total_m > 0) // otherwise input file does not exist ! { // void MPI::Comm::Scatterv(const void* sendbuf, const int sendcounts[], const int displs[], const MPI::Datatype& sendtype, // void* recvbuf, int recvcount, const MPI::Datatype & recvtype, int root) const // The outcome is as if the root executed n send operations, // MPI_Send(sendbuf + displs[i] * extent(sendtype), sendcounts[i], sendtype, i, ...) // and each process executed a receive, // MPI_Recv(recvbuf, recvcount, recvtype, root, ...) // The send buffer is ignored for all nonroot processes. MPI_Scatter(ccurptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, rankincol, commGrid->colWorld); // first receive the receive counts ... if( recvcount == std::numeric_limits::max()) break; // create space for incoming data ... IT * temprows = new IT[recvcount]; IT * tempcols = new IT[recvcount]; NT * tempvals = new NT[recvcount]; // receive actual data ... (first 4 arguments are ignored in the receiver side) MPI_Scatterv(rows, ccurptrs, cdispls, MPIType(), temprows, recvcount, MPIType(), rankincol, commGrid->colWorld); MPI_Scatterv(cols, ccurptrs, cdispls, MPIType(), tempcols, recvcount, MPIType(), rankincol, commGrid->colWorld); MPI_Scatterv(vals, ccurptrs, cdispls, MPIType(), tempvals, recvcount, MPIType(), rankincol, commGrid->colWorld); // now, send the data along the horizontal rcurptrs = new int[rowneighs]; std::fill_n(rcurptrs, rowneighs, 0); // HorizontalSend frees the memory of temp_xxx arrays and then creates and frees memory of all the six arrays itself HorizontalSend(rows, cols, vals,temprows, tempcols, tempvals, localtuples, rcurptrs, rdispls, buffperrowneigh, rowneighs, recvcount, m_perproc, n_perproc, rankinrow); } } // Signal the end of file to other processors along the row std::fill_n(rcurptrs, rowneighs, std::numeric_limits::max()); MPI_Scatter(rcurptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, rankinrow, commGrid->rowWorld); delete [] rcurptrs; } else // r * (s-1) processors that only participate in the horizontal communication step { BcastEssentials(commGrid->commWorld, total_m, total_n, total_nnz, master); m_perproc = total_m / colneighs; n_perproc = total_n / rowneighs; while(total_n > 0 || total_m > 0) // otherwise input file does not exist ! { // receive the receive count MPI_Scatter(rcurptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, rankinrow, commGrid->rowWorld); if( recvcount == std::numeric_limits::max()) break; // create space for incoming data ... IT * temprows = new IT[recvcount]; IT * tempcols = new IT[recvcount]; NT * tempvals = new NT[recvcount]; MPI_Scatterv(rows, rcurptrs, rdispls, MPIType(), temprows, recvcount, MPIType(), rankinrow, commGrid->rowWorld); MPI_Scatterv(cols, rcurptrs, rdispls, MPIType(), tempcols, recvcount, MPIType(), rankinrow, commGrid->rowWorld); MPI_Scatterv(vals, rcurptrs, rdispls, MPIType(), tempvals, recvcount, MPIType(), rankinrow, commGrid->rowWorld); // now push what is ours to tuples IT moffset = commGrid->myprocrow * m_perproc; IT noffset = commGrid->myproccol * n_perproc; for(IT i=0; i< recvcount; ++i) { localtuples.push_back( std::make_tuple(temprows[i]-moffset, tempcols[i]-noffset, tempvals[i]) ); } DeleteAll(temprows,tempcols,tempvals); } } DeleteAll(cdispls, rdispls); std::tuple * arrtuples = new std::tuple[localtuples.size()]; // the vector will go out of scope, make it stick ! std::copy(localtuples.begin(), localtuples.end(), arrtuples); IT localm = (commGrid->myprocrow != (commGrid->grrows-1))? m_perproc: (total_m - (m_perproc * (commGrid->grrows-1))); IT localn = (commGrid->myproccol != (commGrid->grcols-1))? n_perproc: (total_n - (n_perproc * (commGrid->grcols-1))); spSeq->Create( localtuples.size(), localm, localn, arrtuples); // the deletion of arrtuples[] is handled by SpMat::Create #ifdef TAU_PROFILE TAU_PROFILE_STOP(rdtimer); #endif return; } template void SpParMat::AllocateSetBuffers(IT * & rows, IT * & cols, NT * & vals, int * & rcurptrs, int * & ccurptrs, int rowneighs, int colneighs, IT buffpercolneigh) { // allocate buffers on the heap as stack space is usually limited rows = new IT [ buffpercolneigh * colneighs ]; cols = new IT [ buffpercolneigh * colneighs ]; vals = new NT [ buffpercolneigh * colneighs ]; ccurptrs = new int[colneighs]; rcurptrs = new int[rowneighs]; std::fill_n(ccurptrs, colneighs, 0); // fill with zero std::fill_n(rcurptrs, rowneighs, 0); } template void SpParMat::BcastEssentials(MPI_Comm & world, IT & total_m, IT & total_n, IT & total_nnz, int master) { MPI_Bcast(&total_m, 1, MPIType(), master, world); MPI_Bcast(&total_n, 1, MPIType(), master, world); MPI_Bcast(&total_nnz, 1, MPIType(), master, world); } /* * @post {rows, cols, vals are pre-allocated on the heap after this call} * @post {ccurptrs are set to zero; so that if another call is made to this function without modifying ccurptrs, no data will be send from this procesor} */ template void SpParMat::VerticalSend(IT * & rows, IT * & cols, NT * & vals, std::vector< std::tuple > & localtuples, int * rcurptrs, int * ccurptrs, int * rdispls, int * cdispls, IT m_perproc, IT n_perproc, int rowneighs, int colneighs, IT buffperrowneigh, IT buffpercolneigh, int rankinrow) { // first, send/recv the counts ... int * colrecvdispls = new int[colneighs]; int * colrecvcounts = new int[colneighs]; MPI_Alltoall(ccurptrs, 1, MPI_INT, colrecvcounts, 1, MPI_INT, commGrid->colWorld); // share the request counts int totrecv = std::accumulate(colrecvcounts,colrecvcounts+colneighs,0); colrecvdispls[0] = 0; // receive displacements are exact whereas send displacements have slack for(int i=0; i is cripled, if NT=bool) IT * temprows = new IT[totrecv]; IT * tempcols = new IT[totrecv]; NT * tempvals = new NT[totrecv]; // then, exchange all buffers that to their recipients ... MPI_Alltoallv(rows, ccurptrs, cdispls, MPIType(), temprows, colrecvcounts, colrecvdispls, MPIType(), commGrid->colWorld); MPI_Alltoallv(cols, ccurptrs, cdispls, MPIType(), tempcols, colrecvcounts, colrecvdispls, MPIType(), commGrid->colWorld); MPI_Alltoallv(vals, ccurptrs, cdispls, MPIType(), tempvals, colrecvcounts, colrecvdispls, MPIType(), commGrid->colWorld); // finally, reset current pointers ! std::fill_n(ccurptrs, colneighs, 0); DeleteAll(colrecvdispls, colrecvcounts); DeleteAll(rows, cols, vals); // rcurptrs/rdispls are zero initialized scratch space HorizontalSend(rows, cols, vals,temprows, tempcols, tempvals, localtuples, rcurptrs, rdispls, buffperrowneigh, rowneighs, totrecv, m_perproc, n_perproc, rankinrow); // reuse these buffers for the next vertical communication rows = new IT [ buffpercolneigh * colneighs ]; cols = new IT [ buffpercolneigh * colneighs ]; vals = new NT [ buffpercolneigh * colneighs ]; } /** * Private subroutine of ReadDistribute. * Executed by p_r processors on the first processor column. * @pre {rows, cols, vals are pre-allocated on the heap before this call} * @param[in] rankinrow {row head's rank in its processor row - determines the scatter person} */ template template void SpParMat::ReadAllMine(FILE * binfile, IT * & rows, IT * & cols, NT * & vals, std::vector< std::tuple > & localtuples, int * rcurptrs, int * ccurptrs, int * rdispls, int * cdispls, IT m_perproc, IT n_perproc, int rowneighs, int colneighs, IT buffperrowneigh, IT buffpercolneigh, IT entriestoread, HANDLER handler, int rankinrow, bool transpose) { assert(entriestoread != 0); IT cnz = 0; IT temprow, tempcol; NT tempval; int finishedglobal = 1; while(cnz < entriestoread && !feof(binfile)) // this loop will execute at least once { handler.binaryfill(binfile, temprow , tempcol, tempval); if (transpose) { IT swap = temprow; temprow = tempcol; tempcol = swap; } int colrec = std::min(static_cast(temprow / m_perproc), colneighs-1); // precipient processor along the column size_t commonindex = colrec * buffpercolneigh + ccurptrs[colrec]; rows[ commonindex ] = temprow; cols[ commonindex ] = tempcol; vals[ commonindex ] = tempval; ++ (ccurptrs[colrec]); if(ccurptrs[colrec] == buffpercolneigh || (cnz == (entriestoread-1)) ) // one buffer is full, or this processor's share is done ! { #ifdef IODEBUG std::ofstream oput; commGrid->OpenDebugFile("Read", oput); oput << "To column neighbors: "; std::copy(ccurptrs, ccurptrs+colneighs, std::ostream_iterator(oput, " ")); oput << std::endl; oput.close(); #endif VerticalSend(rows, cols, vals, localtuples, rcurptrs, ccurptrs, rdispls, cdispls, m_perproc, n_perproc, rowneighs, colneighs, buffperrowneigh, buffpercolneigh, rankinrow); if(cnz == (entriestoread-1)) // last execution of the outer loop { int finishedlocal = 1; // I am done, but let me check others MPI_Allreduce( &finishedlocal, &finishedglobal, 1, MPI_INT, MPI_BAND, commGrid->colWorld); while(!finishedglobal) { #ifdef DEBUG std::ofstream oput; commGrid->OpenDebugFile("Read", oput); oput << "To column neighbors: "; std::copy(ccurptrs, ccurptrs+colneighs, std::ostream_iterator(oput, " ")); oput << std::endl; oput.close(); #endif // postcondition of VerticalSend: ccurptrs are set to zero // if another call is made to this function without modifying ccurptrs, no data will be send from this procesor VerticalSend(rows, cols, vals, localtuples, rcurptrs, ccurptrs, rdispls, cdispls, m_perproc, n_perproc, rowneighs, colneighs, buffperrowneigh, buffpercolneigh, rankinrow); MPI_Allreduce( &finishedlocal, &finishedglobal, 1, MPI_INT, MPI_BAND, commGrid->colWorld); } } else // the other loop will continue executing { int finishedlocal = 0; MPI_Allreduce( &finishedlocal, &finishedglobal, 1, MPI_INT, MPI_BAND, commGrid->colWorld); } } // end_if for "send buffer is full" case ++cnz; } // signal the end to row neighbors std::fill_n(rcurptrs, rowneighs, std::numeric_limits::max()); int recvcount; MPI_Scatter(rcurptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, rankinrow, commGrid->rowWorld); } /** * Private subroutine of ReadDistribute * @param[in] rankinrow {Row head's rank in its processor row} * Initially temp_xxx arrays carry data received along the proc. column AND needs to be sent along the proc. row * After usage, function frees the memory of temp_xxx arrays and then creates and frees memory of all the six arrays itself */ template void SpParMat::HorizontalSend(IT * & rows, IT * & cols, NT * & vals, IT * & temprows, IT * & tempcols, NT * & tempvals, std::vector < std::tuple > & localtuples, int * rcurptrs, int * rdispls, IT buffperrowneigh, int rowneighs, int recvcount, IT m_perproc, IT n_perproc, int rankinrow) { rows = new IT [ buffperrowneigh * rowneighs ]; cols = new IT [ buffperrowneigh * rowneighs ]; vals = new NT [ buffperrowneigh * rowneighs ]; // prepare to send the data along the horizontal for(int i=0; i< recvcount; ++i) { int rowrec = std::min(static_cast(tempcols[i] / n_perproc), rowneighs-1); rows[ rowrec * buffperrowneigh + rcurptrs[rowrec] ] = temprows[i]; cols[ rowrec * buffperrowneigh + rcurptrs[rowrec] ] = tempcols[i]; vals[ rowrec * buffperrowneigh + rcurptrs[rowrec] ] = tempvals[i]; ++ (rcurptrs[rowrec]); } #ifdef IODEBUG std::ofstream oput; commGrid->OpenDebugFile("Read", oput); oput << "To row neighbors: "; std::copy(rcurptrs, rcurptrs+rowneighs, std::ostream_iterator(oput, " ")); oput << std::endl; oput << "Row displacements were: "; std::copy(rdispls, rdispls+rowneighs, std::ostream_iterator(oput, " ")); oput << std::endl; oput.close(); #endif MPI_Scatter(rcurptrs, 1, MPI_INT, &recvcount, 1, MPI_INT, rankinrow, commGrid->rowWorld); // Send the receive counts for horizontal communication // the data is now stored in rows/cols/vals, can reset temporaries // sets size and capacity to new recvcount DeleteAll(temprows, tempcols, tempvals); temprows = new IT[recvcount]; tempcols = new IT[recvcount]; tempvals = new NT[recvcount]; // then, send all buffers that to their recipients ... MPI_Scatterv(rows, rcurptrs, rdispls, MPIType(), temprows, recvcount, MPIType(), rankinrow, commGrid->rowWorld); MPI_Scatterv(cols, rcurptrs, rdispls, MPIType(), tempcols, recvcount, MPIType(), rankinrow, commGrid->rowWorld); MPI_Scatterv(vals, rcurptrs, rdispls, MPIType(), tempvals, recvcount, MPIType(), rankinrow, commGrid->rowWorld); // now push what is ours to tuples IT moffset = commGrid->myprocrow * m_perproc; IT noffset = commGrid->myproccol * n_perproc; for(int i=0; i< recvcount; ++i) { localtuples.push_back( std::make_tuple(temprows[i]-moffset, tempcols[i]-noffset, tempvals[i]) ); } std::fill_n(rcurptrs, rowneighs, 0); DeleteAll(rows, cols, vals, temprows, tempcols, tempvals); } //! The input parameters' identity (zero) elements as well as //! their communication grid is preserved while outputting template void SpParMat::Find (FullyDistVec & distrows, FullyDistVec & distcols, FullyDistVec & distvals) const { if((*(distrows.commGrid) != *(distcols.commGrid)) || (*(distcols.commGrid) != *(distvals.commGrid))) { SpParHelper::Print("Grids are not comparable, Find() fails!", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } IT globallen = getnnz(); SpTuples Atuples(*spSeq); FullyDistVec nrows ( distrows.commGrid, globallen, 0); FullyDistVec ncols ( distcols.commGrid, globallen, 0); FullyDistVec nvals ( distvals.commGrid, globallen, NT()); IT prelen = Atuples.getnnz(); //IT postlen = nrows.MyLocLength(); int rank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); IT * prelens = new IT[nprocs]; prelens[rank] = prelen; MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), prelens, 1, MPIType(), commGrid->GetWorld()); IT prelenuntil = std::accumulate(prelens, prelens+rank, static_cast(0)); int * sendcnt = new int[nprocs](); // zero initialize IT * rows = new IT[prelen]; IT * cols = new IT[prelen]; NT * vals = new NT[prelen]; int rowrank = commGrid->GetRankInProcRow(); int colrank = commGrid->GetRankInProcCol(); int rowneighs = commGrid->GetGridCols(); int colneighs = commGrid->GetGridRows(); IT * locnrows = new IT[colneighs]; // number of rows is calculated by a reduction among the processor column IT * locncols = new IT[rowneighs]; locnrows[colrank] = getlocalrows(); locncols[rowrank] = getlocalcols(); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(),locnrows, 1, MPIType(), commGrid->GetColWorld()); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(),locncols, 1, MPIType(), commGrid->GetRowWorld()); IT roffset = std::accumulate(locnrows, locnrows+colrank, static_cast(0)); IT coffset = std::accumulate(locncols, locncols+rowrank, static_cast(0)); DeleteAll(locnrows, locncols); for(int i=0; i< prelen; ++i) { IT locid; // ignore local id, data will come in order int owner = nrows.Owner(prelenuntil+i, locid); sendcnt[owner]++; rows[i] = Atuples.rowindex(i) + roffset; // need the global row index cols[i] = Atuples.colindex(i) + coffset; // need the global col index vals[i] = Atuples.numvalue(i); } int * recvcnt = new int[nprocs]; MPI_Alltoall(sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, commGrid->GetWorld()); // get the recv counts int * sdpls = new int[nprocs](); // displacements (zero initialized pid) int * rdpls = new int[nprocs](); std::partial_sum(sendcnt, sendcnt+nprocs-1, sdpls+1); std::partial_sum(recvcnt, recvcnt+nprocs-1, rdpls+1); MPI_Alltoallv(rows, sendcnt, sdpls, MPIType(), SpHelper::p2a(nrows.arr), recvcnt, rdpls, MPIType(), commGrid->GetWorld()); MPI_Alltoallv(cols, sendcnt, sdpls, MPIType(), SpHelper::p2a(ncols.arr), recvcnt, rdpls, MPIType(), commGrid->GetWorld()); MPI_Alltoallv(vals, sendcnt, sdpls, MPIType(), SpHelper::p2a(nvals.arr), recvcnt, rdpls, MPIType(), commGrid->GetWorld()); DeleteAll(sendcnt, recvcnt, sdpls, rdpls); DeleteAll(prelens, rows, cols, vals); distrows = nrows; distcols = ncols; distvals = nvals; } //! The input parameters' identity (zero) elements as well as //! their communication grid is preserved while outputting template void SpParMat::Find (FullyDistVec & distrows, FullyDistVec & distcols) const { if((*(distrows.commGrid) != *(distcols.commGrid)) ) { SpParHelper::Print("Grids are not comparable, Find() fails!", commGrid->GetWorld()); MPI_Abort(MPI_COMM_WORLD, GRIDMISMATCH); } IT globallen = getnnz(); SpTuples Atuples(*spSeq); FullyDistVec nrows ( distrows.commGrid, globallen, 0); FullyDistVec ncols ( distcols.commGrid, globallen, 0); IT prelen = Atuples.getnnz(); int rank = commGrid->GetRank(); int nprocs = commGrid->GetSize(); IT * prelens = new IT[nprocs]; prelens[rank] = prelen; MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), prelens, 1, MPIType(), commGrid->GetWorld()); IT prelenuntil = std::accumulate(prelens, prelens+rank, static_cast(0)); int * sendcnt = new int[nprocs](); // zero initialize IT * rows = new IT[prelen]; IT * cols = new IT[prelen]; NT * vals = new NT[prelen]; int rowrank = commGrid->GetRankInProcRow(); int colrank = commGrid->GetRankInProcCol(); int rowneighs = commGrid->GetGridCols(); int colneighs = commGrid->GetGridRows(); IT * locnrows = new IT[colneighs]; // number of rows is calculated by a reduction among the processor column IT * locncols = new IT[rowneighs]; locnrows[colrank] = getlocalrows(); locncols[rowrank] = getlocalcols(); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(),locnrows, 1, MPIType(), commGrid->GetColWorld()); MPI_Allgather(MPI_IN_PLACE, 0, MPIType(),locncols, 1, MPIType(), commGrid->GetColWorld()); IT roffset = std::accumulate(locnrows, locnrows+colrank, static_cast(0)); IT coffset = std::accumulate(locncols, locncols+rowrank, static_cast(0)); DeleteAll(locnrows, locncols); for(int i=0; i< prelen; ++i) { IT locid; // ignore local id, data will come in order int owner = nrows.Owner(prelenuntil+i, locid); sendcnt[owner]++; rows[i] = Atuples.rowindex(i) + roffset; // need the global row index cols[i] = Atuples.colindex(i) + coffset; // need the global col index } int * recvcnt = new int[nprocs]; MPI_Alltoall(sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, commGrid->GetWorld()); // get the recv counts int * sdpls = new int[nprocs](); // displacements (zero initialized pid) int * rdpls = new int[nprocs](); std::partial_sum(sendcnt, sendcnt+nprocs-1, sdpls+1); std::partial_sum(recvcnt, recvcnt+nprocs-1, rdpls+1); MPI_Alltoallv(rows, sendcnt, sdpls, MPIType(), SpHelper::p2a(nrows.arr), recvcnt, rdpls, MPIType(), commGrid->GetWorld()); MPI_Alltoallv(cols, sendcnt, sdpls, MPIType(), SpHelper::p2a(ncols.arr), recvcnt, rdpls, MPIType(), commGrid->GetWorld()); DeleteAll(sendcnt, recvcnt, sdpls, rdpls); DeleteAll(prelens, rows, cols, vals); distrows = nrows; distcols = ncols; } template std::ofstream& SpParMat::put(std::ofstream& outfile) const { outfile << (*spSeq) << std::endl; return outfile; } template std::ofstream& operator<<(std::ofstream& outfile, const SpParMat & s) { return s.put(outfile) ; // use the right put() function } /** * @param[in] grow {global row index} * @param[in] gcol {global column index} * @param[out] lrow {row index local to the owner} * @param[out] lcol {col index local to the owner} * @returns {owner processor id} **/ template template int SpParMat::Owner(IT total_m, IT total_n, IT grow, IT gcol, LIT & lrow, LIT & lcol) const { int procrows = commGrid->GetGridRows(); int proccols = commGrid->GetGridCols(); IT m_perproc = total_m / procrows; IT n_perproc = total_n / proccols; int own_procrow; // owner's processor row if(m_perproc != 0) { own_procrow = std::min(static_cast(grow / m_perproc), procrows-1); // owner's processor row } else // all owned by the last processor row { own_procrow = procrows -1; } int own_proccol; if(n_perproc != 0) { own_proccol = std::min(static_cast(gcol / n_perproc), proccols-1); } else { own_proccol = proccols-1; } lrow = grow - (own_procrow * m_perproc); lcol = gcol - (own_proccol * n_perproc); return commGrid->GetRank(own_procrow, own_proccol); } /** * @param[out] rowOffset {Row offset imposed by process grid. Global row index = rowOffset + local row index.} * @param[out] colOffset {Column offset imposed by process grid. Global column index = colOffset + local column index.} **/ template void SpParMat::GetPlaceInGlobalGrid(IT& rowOffset, IT& colOffset) const { IT total_rows = getnrow(); IT total_cols = getncol(); int procrows = commGrid->GetGridRows(); int proccols = commGrid->GetGridCols(); IT rows_perproc = total_rows / procrows; IT cols_perproc = total_cols / proccols; rowOffset = commGrid->GetRankInProcCol()*rows_perproc; colOffset = commGrid->GetRankInProcRow()*cols_perproc; } } CombBLAS_beta_16_2/include/CombBLAS/BitMapCarousel.h000644 000765 000024 00000014144 13271404146 023422 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.4 -------------------------------------------------*/ /* date: 1/17/2014 ---------------------------------------------*/ /* authors: Aydin Buluc (abuluc@lbl.gov), Adam Lugowski --------*/ /* this file contributed by Scott Beamer of UC Berkeley --------*/ /****************************************************************/ /* Copyright (c) 2010-2014, The Regents of the University of California 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. */ #ifndef BITMAPCAROUSEL_H #define BITMAPCAROUSEL_H // #include #include "BitMap.h" #include "BitMapFringe.h" #include "CommGrid.h" namespace combblas { template class BitMapCarousel { public: BitMapCarousel(std::shared_ptr grid, IT glen, int local_subword_disp) { commGrid.reset(new CommGrid(*grid)); rotation_index = 0; global_size = glen; my_procrow = commGrid->GetRankInProcCol(); my_proccol = commGrid->GetRankInProcRow(); procrows = commGrid->GetGridRows(); proccols = commGrid->GetGridCols(); local_size = SizeOfChunk(); rotlenuntil = RotLengthUntil(); IT biggest_size = global_size - RotLengthUntil(procrows-1, proccols-1) + 63; bm = new BitMap(biggest_size); recv_buff = new BitMap(biggest_size); old_bm = new BitMap(biggest_size); sub_disps = new int[proccols]; sub_disps[my_proccol] = local_subword_disp; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, sub_disps, 1, MPI_INT, commGrid->GetRowWorld()); curr_subword_disp = local_subword_disp; } ~BitMapCarousel() { delete bm; delete recv_buff; delete old_bm; delete[] sub_disps; } // Return the global index of the start of the local chunk (inclusive) IT GetGlobalStartOfLocal() const { return rotlenuntil; } IT GetGlobalStartOfLocal(int col) const { return RotLengthUntil(my_procrow,col); } // Return the global index of the end of the local chunk (exclusive) IT GetGlobalEndOfLocal() const { return rotlenuntil + local_size; } IT GetGlobalEndOfLocal(int col) const { return GetGlobalStartOfLocal(col) + local_size; } bool GetBit(IT index) const { return bm->get_bit(index - rotlenuntil + curr_subword_disp); } void SetBit(IT index) { bm->set_bit(index - rotlenuntil + curr_subword_disp); } template void LoadVec(FullyDistVec & x) { bm->reset(); local_size = x.LocArrSize(); for (DenseVectorLocalIterator it(x); it.HasNext(); it.Next()) if (it.GetValue() != -1) bm->set_bit(it.GetLocIndex() + curr_subword_disp); } IT RotLengthUntil() { int curr_col = (my_proccol + rotation_index) % proccols; return RotLengthUntil(my_procrow, curr_col); } IT RotLengthUntil(int my_procrow, int my_proccol) const { IT n_perprocrow = global_size / procrows; IT n_thisrow; if(my_procrow == procrows-1) n_thisrow = global_size - (n_perprocrow*(procrows-1)); else n_thisrow = n_perprocrow; IT n_perproc = n_thisrow / proccols; return ((n_perprocrow * my_procrow)+(n_perproc*my_proccol)); } IT SizeOfChunk() const { int curr_col = (my_proccol + rotation_index) % proccols; return SizeOfChunk(my_procrow, curr_col); } IT SizeOfChunk(int my_procrow, int my_proccol) const { IT my_upper_limit; if (my_proccol == (proccols-1)) { if (my_procrow == (procrows-1)) { my_upper_limit = global_size; }else { my_upper_limit = RotLengthUntil(my_procrow+1, 0); } } else { my_upper_limit = RotLengthUntil(my_procrow, (my_proccol+1) % proccols); } return my_upper_limit - RotLengthUntil(my_procrow,my_proccol); } void RotateAlongRow() { int source = (my_proccol+1) % proccols; int dest = (my_proccol+(proccols-1)) % proccols; rotation_index = (rotation_index + 1) % proccols; long send_words = (local_size + 63 + curr_subword_disp)>>6; long recv_words = (SizeOfChunk() + 63 + sub_disps[(my_proccol+rotation_index)%proccols])>>6; MPI_Comm RowWorld = commGrid->GetRowWorld(); MPI_Status status; MPI_Sendrecv(bm->data(), send_words, MPIType(), dest, ROTATE, recv_buff->data(), recv_words, MPIType(), source, ROTATE, RowWorld, &status); local_size = SizeOfChunk(); rotlenuntil = RotLengthUntil(); std::swap(bm, recv_buff); curr_subword_disp = sub_disps[(my_proccol+rotation_index)%proccols]; } void UpdateFringe(BitMapFringe &bm_fringe) { uint64_t* dest = bm_fringe.AccessBM()->data(); uint64_t* curr = bm->data(); uint64_t* old = old_bm->data(); IT num_words = (local_size + 63 + curr_subword_disp) / 64; for (IT i=0; icopy_from(bm); } private: std::shared_ptr commGrid; int rotation_index; int my_procrow; int my_proccol; int procrows; int proccols; int curr_subword_disp; IT rotlenuntil; IT global_size; IT local_size; BitMap* bm; BitMap* recv_buff; BitMap* old_bm; int* sub_disps; }; } #endif // BITMAPCAROUSEL_H CombBLAS_beta_16_2/include/CombBLAS/mtSpGEMM.h000644 000765 000024 00000022210 13271404146 022132 0ustar00aydinbulucstaff000000 000000 #ifndef _mtSpGEMM_h #define _mtSpGEMM_h #include "CombBLAS.h" namespace combblas { /* Multithreaded prefix sum Inputs: in: an input array size: the length of the input array "in" nthreads: number of threads used to compute the prefix sum Output: return an array of size "size+1" the memory of the output array is allocated internallay Example: in = [2, 1, 3, 5] out = [0, 2, 3, 6, 11] */ template T* prefixsum(T* in, int size, int nthreads) { std::vector tsum(nthreads+1); tsum[0] = 0; T* out = new T[size+1]; out[0] = 0; T* psum = &out[1]; #ifdef THREADED #pragma omp parallel #endif { int ithread = 0; #ifdef THREADED ithread = omp_get_thread_num(); #endif T sum = 0; #ifdef THREADED #pragma omp for schedule(static) #endif for (int i=0; i SpTuples * LocalSpGEMM (const SpDCCols & A, const SpDCCols & B, bool clearA, bool clearB) { IT mdim = A.getnrow(); IT ndim = B.getncol(); IT nnzA = A.getnnz(); if(A.isZero() || B.isZero()) { return new SpTuples(0, mdim, ndim); } Dcsc* Adcsc = A.GetDCSC(); Dcsc* Bdcsc = B.GetDCSC(); IT nA = A.getncol(); float cf = static_cast(nA+1) / static_cast(Adcsc->nzc); IT csize = static_cast(ceil(cf)); // chunk size IT * aux; Adcsc->ConstructAux(nA, aux); int numThreads = 1; #ifdef THREADED #pragma omp parallel { numThreads = omp_get_num_threads(); } #endif IT* colnnzC = estimateNNZ(A, B); IT* colptrC = prefixsum(colnnzC, Bdcsc->nzc, numThreads); delete [] colnnzC; IT nnzc = colptrC[Bdcsc->nzc]; std::tuple * tuplesC = static_cast *> (::operator new (sizeof(std::tuple[nnzc]))); // thread private space for heap and colinds std::vector>> colindsVec(numThreads); std::vector>> globalheapVec(numThreads); for(int i=0; inzc; ++i) { size_t nnzcolB = Bdcsc->cp[i+1] - Bdcsc->cp[i]; //nnz in the current column of B int myThread = 0; #ifdef THREADED myThread = omp_get_thread_num(); #endif if(colindsVec[myThread].size() < nnzcolB) //resize thread private vectors if needed { colindsVec[myThread].resize(nnzcolB); globalheapVec[myThread].resize(nnzcolB); } // colinds.first vector keeps indices to A.cp, i.e. it dereferences "colnums" vector (above), // colinds.second vector keeps the end indices (i.e. it gives the index to the last valid element of A.cpnack) Adcsc->FillColInds(Bdcsc->ir + Bdcsc->cp[i], nnzcolB, colindsVec[myThread], aux, csize); std::pair * colinds = colindsVec[myThread].data(); HeapEntry * wset = globalheapVec[myThread].data(); IT hsize = 0; for(IT j = 0; (unsigned)j < nnzcolB; ++j) // create the initial heap { if(colinds[j].first != colinds[j].second) // current != end { wset[hsize++] = HeapEntry< IT,NT1 > (Adcsc->ir[colinds[j].first], j, Adcsc->numx[colinds[j].first]); } } std::make_heap(wset, wset+hsize); IT curptr = colptrC[i]; while(hsize > 0) { std::pop_heap(wset, wset + hsize); // result is stored in wset[hsize-1] IT locb = wset[hsize-1].runr; // relative location of the nonzero in B's current column NTO mrhs = SR::multiply(wset[hsize-1].num, Bdcsc->numx[Bdcsc->cp[i]+locb]); if (!SR::returnedSAID()) { if( (curptr > colptrC[i]) && std::get<0>(tuplesC[curptr-1]) == wset[hsize-1].key) { std::get<2>(tuplesC[curptr-1]) = SR::add(std::get<2>(tuplesC[curptr-1]), mrhs); } else { tuplesC[curptr++]= std::make_tuple(wset[hsize-1].key, Bdcsc->jc[i], mrhs) ; } } if( (++(colinds[locb].first)) != colinds[locb].second) // current != end { // runr stays the same ! wset[hsize-1].key = Adcsc->ir[colinds[locb].first]; wset[hsize-1].num = Adcsc->numx[colinds[locb].first]; std::push_heap(wset, wset+hsize); } else { --hsize; } } } if(clearA) delete const_cast *>(&A); if(clearB) delete const_cast *>(&B); delete [] colptrC; delete [] aux; SpTuples* spTuplesC = new SpTuples (nnzc, mdim, ndim, tuplesC, true, true); return spTuplesC; } // estimate space for result of SpGEMM template IT* estimateNNZ(const SpDCCols & A,const SpDCCols & B) { IT nnzA = A.getnnz(); if(A.isZero() || B.isZero()) { return NULL; } Dcsc* Adcsc = A.GetDCSC(); Dcsc* Bdcsc = B.GetDCSC(); float cf = static_cast(A.getncol()+1) / static_cast(Adcsc->nzc); IT csize = static_cast(ceil(cf)); // chunk size IT * aux; Adcsc->ConstructAux(A.getncol(), aux); int numThreads = 1; #ifdef THREADED #pragma omp parallel { numThreads = omp_get_num_threads(); } #endif IT* colnnzC = new IT[Bdcsc->nzc]; // nnz in every nonempty column of C #ifdef THREADED #pragma omp parallel for #endif for(IT i=0; i< Bdcsc->nzc; ++i) { colnnzC[i] = 0; } // thread private space for heap and colinds std::vector>> colindsVec(numThreads); std::vector>> globalheapVec(numThreads); for(int i=0; inzc; ++i) { size_t nnzcolB = Bdcsc->cp[i+1] - Bdcsc->cp[i]; //nnz in the current column of B int myThread = 0; #ifdef THREADED myThread = omp_get_thread_num(); #endif if(colindsVec[myThread].size() < nnzcolB) //resize thread private vectors if needed { colindsVec[myThread].resize(nnzcolB); globalheapVec[myThread].resize(nnzcolB); } // colinds.first vector keeps indices to A.cp, i.e. it dereferences "colnums" vector (above), // colinds.second vector keeps the end indices (i.e. it gives the index to the last valid element of A.cpnack) Adcsc->FillColInds(Bdcsc->ir + Bdcsc->cp[i], nnzcolB, colindsVec[myThread], aux, csize); std::pair * colinds = colindsVec[myThread].data(); std::pair * curheap = globalheapVec[myThread].data(); IT hsize = 0; // create the initial heap for(IT j = 0; (unsigned)j < nnzcolB; ++j) { if(colinds[j].first != colinds[j].second) { curheap[hsize++] = std::make_pair(Adcsc->ir[colinds[j].first], j); } } std::make_heap(curheap, curheap+hsize, std::greater>()); IT prevRow=-1; // previously popped row from heap while(hsize > 0) { std::pop_heap(curheap, curheap + hsize, std::greater>()); // result is stored in wset[hsize-1] IT locb = curheap[hsize-1].second; if( curheap[hsize-1].first != prevRow) { prevRow = curheap[hsize-1].first; colnnzC[i] ++; } if( (++(colinds[locb].first)) != colinds[locb].second) // current != end { curheap[hsize-1].first = Adcsc->ir[colinds[locb].first]; std::push_heap(curheap, curheap+hsize, std::greater>()); } else { --hsize; } } } delete [] aux; return colnnzC; } } #endif CombBLAS_beta_16_2/include/CombBLAS/MemoryPool.h000644 000765 000024 00000005662 13271404146 022657 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _MEMORY_POOL_H #define _MEMORY_POOL_H #include #include #include // For "placement new" (classes using this memory pool may need it) #include namespace combblas { //! EqualityComparable memory chunk object //! Two memory chunks are considered equal if either their beginaddr or the endaddr are the same (just to facilitate deallocation) class Memory { public: Memory(char * m_beg, size_t m_size): begin(m_beg),size(m_size) {}; char * begaddr() { return begin; } char * endaddr() { return begin + size; } bool operator < (const Memory & rhs) const { return (begin < rhs.begin); } bool operator == (const Memory & rhs) const { return (begin == rhs.begin); } char * begin; size_t size; }; /** * \invariant Available memory addresses will be sorted w.r.t. their starting positions * \invariant At least one element exists in the freelist at any time. * \invariant Defragment on the fly: at any time, NO two consecutive chunks with chunk1.endaddr equals chunk2.begaddr exist */ class MemoryPool { public: MemoryPool(void * m_beg, size_t m_size); void * alloc(size_t size); void dealloc (void * base, size_t size); friend std::ofstream& operator<< (std::ofstream& outfile, const MemoryPool & mpool); private: // std::list is a doubly linked list (i.e., a Sequence that supports both forward and backward traversal) std::list freelist; char * initbeg; char * initend; }; } #endif CombBLAS_beta_16_2/include/CombBLAS/StackEntry.h000644 000765 000024 00000000264 13271404146 022635 0ustar00aydinbulucstaff000000 000000 #ifndef _STACK_ENTRY_H #define _STACK_ENTRY_H #include namespace combblas { template class StackEntry { public: T1 value; T2 key; }; } #endif CombBLAS_beta_16_2/include/CombBLAS/Semirings.h000644 000765 000024 00000015225 13271404146 022511 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SEMIRINGS_H_ #define _SEMIRINGS_H_ #include #include #include #include "promote.h" namespace combblas { template struct inf_plus{ T operator()(const T& a, const T& b) const { T inf = std::numeric_limits::max(); if (a == inf || b == inf){ return inf; } return a + b; } }; // This semiring is used in indexing (SpParMat::operator()) template struct BoolCopy2ndSRing { static OUT id() { return OUT(); } static bool returnedSAID() { return false; } static OUT add(const OUT & arg1, const OUT & arg2) { std::cout << "Add should not happen (BoolCopy2ndSRing)!" << std::endl; throw std::string("Add should not happen!"); std::exit(1); return arg2; } static const OUT& multiply(bool arg1, const OUT & arg2) { return arg2; } static void axpy(bool a, const OUT & x, OUT & y) { y = multiply(a, x); } static MPI_Op mpi_op() { static MPI_Op mpiop; static bool exists = false; if (exists) return mpiop; else { MPI_Op_create(MPI_func, true, &mpiop); exists = true; return mpiop; } } static void MPI_func(void * invec, void * inoutvec, int * len, MPI_Datatype *datatype) { if (*len > 0) { std::cout << "MPI Add should not happen (BoolCopy2ndSRing)!" << std::endl; std::exit(1); } } }; // This semiring is used in indexing (SpParMat::operator()) template struct BoolCopy1stSRing { static OUT id() { return OUT(); } static bool returnedSAID() { return false; } static OUT add(const OUT & arg1, const OUT & arg2) { std::cout << "Add should not happen (BoolCopy1stSRing)!" << std::endl; throw std::string("Add should not happen!"); std::exit(1); return arg2; } static const OUT& multiply(const OUT & arg1, bool arg2) { return arg1; } static void axpy(const OUT& a, bool x, OUT & y) { y = multiply(a, x); } static MPI_Op mpi_op() { static MPI_Op mpiop; static bool exists = false; if (exists) return mpiop; else { MPI_Op_create(MPI_func, true, &mpiop); exists = true; return mpiop; } } static void MPI_func(void * invec, void * inoutvec, int * len, MPI_Datatype *datatype) { if (*len > 0) { std::cout << "MPI Add should not happen (BoolCopy1stSRing)!" << std::endl; std::exit(1); } } }; template struct Select2ndSRing { static OUT id() { return OUT(); } static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MAX; }; static OUT add(const OUT & arg1, const OUT & arg2) { return arg2; } static OUT multiply(const T1 & arg1, const T2 & arg2) { // fragile since it wouldn't work with y <- x*A return static_cast(arg2); } static void axpy(T1 a, const T2 & x, OUT & y) { //y = add(y, multiply(a, x)); y = multiply(a, x); } }; template struct SelectMaxSRing { typedef typename promote_trait::T_promote T_promote; static T_promote id() { return -1; }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MAX; }; static T_promote add(const T_promote & arg1, const T_promote & arg2) { return std::max(arg1, arg2); } static T_promote multiply(const T1 & arg1, const T2 & arg2) { // we could have just returned arg2 but it would be // fragile since it wouldn't work with y <- x*A return (static_cast(arg1) * static_cast(arg2) ); } static void axpy(T1 a, const T2 & x, T_promote & y) { y = std::max(y, static_cast(a*x)); } }; // This one is used for BFS iteration template struct SelectMaxSRing { typedef T2 T_promote; static T_promote id(){ return -1; }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MAX; }; static T_promote add(const T_promote & arg1, const T_promote & arg2) { return std::max(arg1, arg2); } static T_promote multiply(const bool & arg1, const T2 & arg2) { return arg2; } static void axpy(bool a, const T2 & x, T_promote & y) { y = std::max(y, x); } }; template struct PlusTimesSRing { typedef typename promote_trait::T_promote T_promote; static T_promote id(){ return 0; } static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_SUM; }; static T_promote add(const T_promote & arg1, const T_promote & arg2) { return arg1+arg2; } static T_promote multiply(const T1 & arg1, const T2 & arg2) { return (static_cast(arg1) * static_cast(arg2) ); } static void axpy(T1 a, const T2 & x, T_promote & y) { y += a*x; } }; template struct MinPlusSRing { typedef typename promote_trait::T_promote T_promote; static T_promote id() { return std::numeric_limits::max(); }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MIN; }; static T_promote add(const T_promote & arg1, const T_promote & arg2) { return std::min(arg1, arg2); } static T_promote multiply(const T1 & arg1, const T2 & arg2) { return inf_plus< T_promote > (static_cast(arg1), static_cast(arg2)); } static void axpy(T1 a, const T2 & x, T_promote & y) { y = std::min(y, multiply(a, x)); } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/000755 000765 000024 00000000000 13271404146 023124 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/include/CombBLAS/Isect.h000644 000765 000024 00000004006 13271404146 021613 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _ISECT_H #define _ISECT_H namespace combblas { /** * Carries information about the intersecting col(A) and row(B) indices of matrix operands **/ template class Isect { public: IT index; // col-row index IT size; IT start; IT current; // current pointer bool operator < (const Isect & rhs) const { return (index < rhs.index); } bool operator > (const Isect & rhs) const { return (index > rhs.index); } bool operator == (const Isect & rhs) const { return (index == rhs.index); } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/ParFriendsExt.h000644 000765 000024 00000064274 13271404146 023277 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _PAR_FRIENDS_EXT_H_ #define _PAR_FRIENDS_EXT_H_ #include "mpi.h" #include #include "SpParMat.h" #include "SpParHelper.h" #include "MPIType.h" #include "Friends.h" namespace combblas { template class SpParMat; /*************************************************************************************************/ /**************************** FRIEND FUNCTIONS FOR PARALLEL CLASSES ******************************/ /************************** EXTENDED SET (NOT COMMONLY USED FUNCTIONS) ***************************/ /*************************************************************************************************/ /** * Parallel A = B*C routine that uses one-sided MPI-2 features * General active target syncronization via MPI_Win_Post, MPI_Win_Start, MPI_Win_Complete, MPI_Win_Wait * Tested on my dual core Macbook with 1,4,9,16,25 MPI processes * No memory hog: splits the matrix into two along the column, prefetches the next half matrix while computing on the current one **/ template SpParMat::T_promote,typename promote_trait::T_promote> Mult_AnXBn_ActiveTarget (const SpParMat & A, const SpParMat & B ) { typedef typename promote_trait::T_promote N_promote; typedef typename promote_trait::T_promote DER_promote; if(A.getncol() != B.getnrow()) { std::cout<<"Can not multiply, dimensions does not match"<(); } int stages, Aoffset, Boffset; // stages = inner dimension of matrix blocks std::shared_ptr GridC = ProductGrid((A.commGrid).get(), (B.commGrid).get(), stages, Aoffset, Boffset); IU C_m = A.spSeq->getnrow(); IU C_n = B.spSeq->getncol(); UDERA A1seq, A2seq; (A.spSeq)->Split( A1seq, A2seq); // ABAB: It should be able to perform split/merge with the transpose option [single function call] const_cast< UDERB* >(B.spSeq)->Transpose(); UDERB B1seq, B2seq; (B.spSeq)->Split( B1seq, B2seq); // Create row and column windows (collective operation, i.e. everybody exposes its window to others) std::vector rowwins1, rowwins2, colwins1, colwins2; SpParHelper::SetWindows((A.commGrid)->GetRowWorld(), A1seq, rowwins1); SpParHelper::SetWindows((A.commGrid)->GetRowWorld(), A2seq, rowwins2); SpParHelper::SetWindows((B.commGrid)->GetColWorld(), B1seq, colwins1); SpParHelper::SetWindows((B.commGrid)->GetColWorld(), B2seq, colwins2); // ABAB: We can optimize the call to create windows in the absence of passive synchronization // MPI_Info info; // MPI_Info_create ( &info ); // MPI_Info_set( info, "no_locks", "true" ); // MPI_Win_create( . . ., info, . . . ); // MPI_Info_free( &info ); IU ** ARecvSizes1 = SpHelper::allocate2D(UDERA::esscount, stages); IU ** ARecvSizes2 = SpHelper::allocate2D(UDERA::esscount, stages); IU ** BRecvSizes1 = SpHelper::allocate2D(UDERB::esscount, stages); IU ** BRecvSizes2 = SpHelper::allocate2D(UDERB::esscount, stages); SpParHelper::GetSetSizes( A1seq, ARecvSizes1, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( A2seq, ARecvSizes2, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( B1seq, BRecvSizes1, (B.commGrid)->GetColWorld()); SpParHelper::GetSetSizes( B2seq, BRecvSizes2, (B.commGrid)->GetColWorld()); // Remotely fetched matrices are stored as pointers UDERA * ARecv1, * ARecv2; UDERB * BRecv1, * BRecv2; std::vector< SpTuples *> tomerge; MPI_Group row_group, col_group; MPI_Comm_group((A.commGrid)->GetRowWorld(), &row_group); MPI_Comm_group((B.commGrid)->GetColWorld(), &col_group); int Aself = (A.commGrid)->GetRankInProcRow(); int Bself = (B.commGrid)->GetRankInProcCol(); #ifdef SPGEMMDEBUG MPI_Barrier(GridC->GetWorld()); SpParHelper::Print("Writing to file\n"); std::ofstream oput; GridC->OpenDebugFile("deb", oput); oput << "A1seq: " << A1seq.getnrow() << " " << A1seq.getncol() << " " << A1seq.getnnz() << std::endl; oput << "A2seq: " << A2seq.getnrow() << " " << A2seq.getncol() << " " << A2seq.getnnz() << std::endl; oput << "B1seq: " << B1seq.getnrow() << " " << B1seq.getncol() << " " << B1seq.getnnz() << std::endl; oput << "B2seq: " << B2seq.getnrow() << " " << B2seq.getncol() << " " << B2seq.getnnz() << std::endl; SpParHelper::Print("Wrote to file\n"); MPI_Barrier(GridC->GetWorld()); #endif SpParHelper::PostExposureEpoch(Aself, rowwins1, row_group); SpParHelper::PostExposureEpoch(Aself, rowwins2, row_group); SpParHelper::PostExposureEpoch(Bself, colwins1, col_group); SpParHelper::PostExposureEpoch(Bself, colwins2, col_group); MPI_Barrier(GridC->GetWorld()); SpParHelper::Print("Exposure epochs posted\n"); MPI_Barrier(GridC->GetWorld()); int Aowner = (0+Aoffset) % stages; int Bowner = (0+Boffset) % stages; SpParHelper::AccessNFetch(ARecv1, Aowner, rowwins1, row_group, ARecvSizes1); SpParHelper::AccessNFetch(ARecv2, Aowner, rowwins2, row_group, ARecvSizes2); // Start prefetching next half for(int j=0; j< rowwins1.size(); ++j) // wait for the first half to complete rowwins1[j].Complete(); SpParHelper::AccessNFetch(BRecv1, Bowner, colwins1, col_group, BRecvSizes1); SpParHelper::AccessNFetch(BRecv2, Bowner, colwins2, col_group, BRecvSizes2); // Start prefetching next half for(int j=0; j< colwins1.size(); ++j) colwins1[j].Complete(); for(int i = 1; i < stages; ++i) { #ifdef SPGEMMDEBUG SpParHelper::Print("Stage starting\n"); #endif SpTuples * C_cont = MultiplyReturnTuples(*ARecv1, *BRecv1, false, true); #ifdef SPGEMMDEBUG SpParHelper::Print("Multiplied\n"); #endif if(!C_cont->isZero()) tomerge.push_back(C_cont); #ifdef SPGEMMDEBUG SpParHelper::Print("Pushed back\n"); MPI_Barrier(GridC->GetWorld()); #endif bool remoteA = false; bool remoteB = false; delete ARecv1; // free the memory of the previous first half for(int j=0; j< rowwins2.size(); ++j) // wait for the previous second half to complete rowwins2[j].Complete(); SpParHelper::Print("Completed A\n"); delete BRecv1; for(int j=0; j< colwins2.size(); ++j) // wait for the previous second half to complete colwins2[j].Complete(); #ifdef SPGEMMDEBUG SpParHelper::Print("Completed B\n"); MPI_Barrier(GridC->GetWorld()); #endif Aowner = (i+Aoffset) % stages; Bowner = (i+Boffset) % stages; // start fetching the current first half SpParHelper::AccessNFetch(ARecv1, Aowner, rowwins1, row_group, ARecvSizes1); SpParHelper::AccessNFetch(BRecv1, Bowner, colwins1, col_group, BRecvSizes1); #ifdef SPGEMMDEBUG SpParHelper::Print("Fetched next\n"); MPI_Barrier(GridC->GetWorld()); #endif // while multiplying the already completed previous second halfs C_cont = MultiplyReturnTuples(*ARecv2, *BRecv2, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); #ifdef SPGEMMDEBUG SpParHelper::Print("Multiplied and pushed\n"); MPI_Barrier(GridC->GetWorld()); #endif delete ARecv2; // free memory of the previous second half delete BRecv2; for(int j=0; j< rowwins1.size(); ++j) // wait for the current first half to complte rowwins1[j].Complete(); for(int j=0; j< colwins1.size(); ++j) colwins1[j].Complete(); #ifdef SPGEMMDEBUG SpParHelper::Print("Completed next\n"); MPI_Barrier(GridC->GetWorld()); #endif // start prefetching the current second half SpParHelper::AccessNFetch(ARecv2, Aowner, rowwins2, row_group, ARecvSizes2); SpParHelper::AccessNFetch(BRecv2, Bowner, colwins2, col_group, BRecvSizes2); } SpTuples * C_cont = MultiplyReturnTuples(*ARecv1, *BRecv1, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); delete ARecv1; // free the memory of the previous first half for(int j=0; j< rowwins2.size(); ++j) // wait for the previous second half to complete rowwins2[j].Complete(); delete BRecv1; for(int j=0; j< colwins2.size(); ++j) // wait for the previous second half to complete colwins2[j].Complete(); C_cont = MultiplyReturnTuples(*ARecv2, *BRecv2, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); delete ARecv2; delete BRecv2; SpHelper::deallocate2D(ARecvSizes1, UDERA::esscount); SpHelper::deallocate2D(ARecvSizes2, UDERA::esscount); SpHelper::deallocate2D(BRecvSizes1, UDERB::esscount); SpHelper::deallocate2D(BRecvSizes2, UDERB::esscount); DER_promote * C = new DER_promote(MergeAll(tomerge, C_m, C_n), false, NULL); // First get the result in SpTuples, then convert to UDER for(int i=0; iMerge(A1seq, A2seq); (B.spSeq)->Merge(B1seq, B2seq); MPI_Group_free(&row_group); MPI_Group_free(&col_group); const_cast< UDERB* >(B.spSeq)->Transpose(); // transpose back to original return SpParMat (C, GridC); // return the result object } /** * Parallel A = B*C routine that uses one-sided MPI-2 features * This function implements an asynchronous 2D algorithm, in the sense that there is no notion of stages. * \n The process that completes its submatrix update, requests subsequent matrices from their owners w/out waiting to sychronize with other processors * \n This partially remedies the severe load balancing problem in sparse matrices. * \n The class uses MPI-2 to achieve one-sided asynchronous communication * \n The algorithm treats each submatrix as a single block * \n Local data structure can be any SpMat that has a constructor with array sizes and getarrs() member * Passive target syncronization via MPI_Win_Lock, MPI_Win_Unlock * No memory hog: splits the matrix into two along the column, prefetches the next half matrix while computing on the current one **/ template SpParMat::T_promote,typename promote_trait::T_promote> Mult_AnXBn_PassiveTarget (const SpParMat & A, const SpParMat & B ) { typedef typename promote_trait::T_promote N_promote; typedef typename promote_trait::T_promote DER_promote; if(A.getncol() != B.getnrow()) { std::cout<<"Can not multiply, dimensions does not match"<(); } int stages, Aoffset, Boffset; // stages = inner dimension of matrix blocks std::shared_ptr GridC = ProductGrid((A.commGrid).get(), (B.commGrid).get(), stages, Aoffset, Boffset); IU C_m = A.spSeq->getnrow(); IU C_n = B.spSeq->getncol(); UDERA A1seq, A2seq; (A.spSeq)->Split( A1seq, A2seq); // ABAB: It should be able to perform split/merge with the transpose option [single function call] const_cast< UDERB* >(B.spSeq)->Transpose(); UDERB B1seq, B2seq; (B.spSeq)->Split( B1seq, B2seq); // Create row and column windows (collective operation, i.e. everybody exposes its window to others) std::vector rowwins1, rowwins2, colwins1, colwins2; SpParHelper::SetWindows((A.commGrid)->GetRowWorld(), A1seq, rowwins1); SpParHelper::SetWindows((A.commGrid)->GetRowWorld(), A2seq, rowwins2); SpParHelper::SetWindows((B.commGrid)->GetColWorld(), B1seq, colwins1); SpParHelper::SetWindows((B.commGrid)->GetColWorld(), B2seq, colwins2); IU ** ARecvSizes1 = SpHelper::allocate2D(UDERA::esscount, stages); IU ** ARecvSizes2 = SpHelper::allocate2D(UDERA::esscount, stages); IU ** BRecvSizes1 = SpHelper::allocate2D(UDERB::esscount, stages); IU ** BRecvSizes2 = SpHelper::allocate2D(UDERB::esscount, stages); SpParHelper::GetSetSizes( A1seq, ARecvSizes1, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( A2seq, ARecvSizes2, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( B1seq, BRecvSizes1, (B.commGrid)->GetColWorld()); SpParHelper::GetSetSizes( B2seq, BRecvSizes2, (B.commGrid)->GetColWorld()); // Remotely fetched matrices are stored as pointers UDERA * ARecv1, * ARecv2; UDERB * BRecv1, * BRecv2; std::vector< SpTuples *> tomerge; // sorted triples to be merged MPI_Group row_group, col_group; MPI_Comm_group((A.commGrid)->GetRowWorld(), &row_group); MPI_Comm_group((B.commGrid)->GetColWorld(), &col_group); int Aself = (A.commGrid)->GetRankInProcRow(); int Bself = (B.commGrid)->GetRankInProcCol(); int Aowner = (0+Aoffset) % stages; int Bowner = (0+Boffset) % stages; SpParHelper::LockNFetch(ARecv1, Aowner, rowwins1, row_group, ARecvSizes1); SpParHelper::LockNFetch(ARecv2, Aowner, rowwins2, row_group, ARecvSizes2); // Start prefetching next half SpParHelper::LockNFetch(BRecv1, Bowner, colwins1, col_group, BRecvSizes1); SpParHelper::LockNFetch(BRecv2, Bowner, colwins2, col_group, BRecvSizes2); // Start prefetching next half // Finish the first halfs SpParHelper::UnlockWindows(Aowner, rowwins1); SpParHelper::UnlockWindows(Bowner, colwins1); for(int i = 1; i < stages; ++i) { SpTuples * C_cont = MultiplyReturnTuples(*ARecv1, *BRecv1, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); bool remoteA = false; bool remoteB = false; delete ARecv1; // free the memory of the previous first half delete BRecv1; SpParHelper::UnlockWindows(Aowner, rowwins2); // Finish the second half SpParHelper::UnlockWindows(Bowner, colwins2); Aowner = (i+Aoffset) % stages; Bowner = (i+Boffset) % stages; // start fetching the current first half SpParHelper::LockNFetch(ARecv1, Aowner, rowwins1, row_group, ARecvSizes1); SpParHelper::LockNFetch(BRecv1, Bowner, colwins1, col_group, BRecvSizes1); // while multiplying the already completed previous second halfs C_cont = MultiplyReturnTuples(*ARecv2, *BRecv2, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); delete ARecv2; // free memory of the previous second half delete BRecv2; // wait for the current first half to complte SpParHelper::UnlockWindows(Aowner, rowwins1); SpParHelper::UnlockWindows(Bowner, colwins1); // start prefetching the current second half SpParHelper::LockNFetch(ARecv2, Aowner, rowwins2, row_group, ARecvSizes2); SpParHelper::LockNFetch(BRecv2, Bowner, colwins2, col_group, BRecvSizes2); } SpTuples * C_cont = MultiplyReturnTuples(*ARecv1, *BRecv1, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); delete ARecv1; // free the memory of the previous first half delete BRecv1; SpParHelper::UnlockWindows(Aowner, rowwins2); SpParHelper::UnlockWindows(Bowner, colwins2); C_cont = MultiplyReturnTuples(*ARecv2, *BRecv2, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); delete ARecv2; delete BRecv2; SpHelper::deallocate2D(ARecvSizes1, UDERA::esscount); SpHelper::deallocate2D(ARecvSizes2, UDERA::esscount); SpHelper::deallocate2D(BRecvSizes1, UDERB::esscount); SpHelper::deallocate2D(BRecvSizes2, UDERB::esscount); DER_promote * C = new DER_promote(MergeAll(tomerge, C_m, C_n), false, NULL); // First get the result in SpTuples, then convert to UDER for(int i=0; iMerge(A1seq, A2seq); (B.spSeq)->Merge(B1seq, B2seq); MPI_Group_free(&row_group); MPI_Group_free(&col_group); const_cast< UDERB* >(B.spSeq)->Transpose(); // transpose back to original return SpParMat (C, GridC); // return the result object } /** * Parallel A = B*C routine that uses one-sided MPI-2 features * Syncronization is through MPI_Win_Fence * Buggy as of September, 2009 **/ template SpParMat::T_promote,typename promote_trait::T_promote> Mult_AnXBn_Fence (const SpParMat & A, const SpParMat & B ) { typedef typename promote_trait::T_promote N_promote; typedef typename promote_trait::T_promote DER_promote; if(A.getncol() != B.getnrow()) { std::cout<<"Can not multiply, dimensions does not match"<(); } int stages, Aoffset, Boffset; // stages = inner dimension of matrix blocks std::shared_ptr GridC = ProductGrid((A.commGrid).get(), (B.commGrid).get(), stages, Aoffset, Boffset); std::ofstream oput; GridC->OpenDebugFile("deb", oput); const_cast< UDERB* >(B.spSeq)->Transpose(); // set row & col window handles std::vector rowwindows, colwindows; std::vector rowwinnext, colwinnext; SpParHelper::SetWindows((A.commGrid)->GetRowWorld(), *(A.spSeq), rowwindows); SpParHelper::SetWindows((B.commGrid)->GetColWorld(), *(B.spSeq), colwindows); SpParHelper::SetWindows((A.commGrid)->GetRowWorld(), *(A.spSeq), rowwinnext); SpParHelper::SetWindows((B.commGrid)->GetColWorld(), *(B.spSeq), colwinnext); IU ** ARecvSizes = SpHelper::allocate2D(UDERA::esscount, stages); IU ** BRecvSizes = SpHelper::allocate2D(UDERB::esscount, stages); SpParHelper::GetSetSizes( *(A.spSeq), ARecvSizes, (A.commGrid)->GetRowWorld()); SpParHelper::GetSetSizes( *(B.spSeq), BRecvSizes, (B.commGrid)->GetColWorld()); UDERA * ARecv, * ARecvNext; UDERB * BRecv, * BRecvNext; std::vector< SpTuples *> tomerge; // Prefetch first for(int j=0; j< rowwindows.size(); ++j) MPI_Win_fence(MPI_MODE_NOPRECEDE, rowwindows[j]); for(int j=0; j< colwindows.size(); ++j) MPI_Win_fence(MPI_MODE_NOPRECEDE, colwindows[j]); for(int j=0; j< rowwinnext.size(); ++j) MPI_Win_fence(MPI_MODE_NOPRECEDE, rowwinnext[j]); for(int j=0; j< colwinnext.size(); ++j) MPI_Win_fence(MPI_MODE_NOPRECEDE, colwinnext[j]); int Aownind = (0+Aoffset) % stages; int Bownind = (0+Boffset) % stages; if(Aownind == (A.commGrid)->GetRankInProcRow()) { ARecv = A.spSeq; // shallow-copy } else { std::vector ess1(UDERA::esscount); // pack essentials to a vector for(int j=0; j< UDERA::esscount; ++j) { ess1[j] = ARecvSizes[j][Aownind]; } ARecv = new UDERA(); // create the object first oput << "For A (out), Fetching " << (void*)rowwindows[0] << std::endl; SpParHelper::FetchMatrix(*ARecv, ess1, rowwindows, Aownind); // fetch its elements later } if(Bownind == (B.commGrid)->GetRankInProcCol()) { BRecv = B.spSeq; // shallow-copy } else { std::vector ess2(UDERB::esscount); // pack essentials to a vector for(int j=0; j< UDERB::esscount; ++j) { ess2[j] = BRecvSizes[j][Bownind]; } BRecv = new UDERB(); oput << "For B (out), Fetching " << (void*)colwindows[0] << std::endl; SpParHelper::FetchMatrix(*BRecv, ess2, colwindows, Bownind); // No lock version, only get ! } int Aownprev = Aownind; int Bownprev = Bownind; for(int i = 1; i < stages; ++i) { Aownind = (i+Aoffset) % stages; Bownind = (i+Boffset) % stages; if(i % 2 == 1) // Fetch RecvNext via winnext, fence on Recv via windows { if(Aownind == (A.commGrid)->GetRankInProcRow()) { ARecvNext = A.spSeq; // shallow-copy } else { std::vector ess1(UDERA::esscount); // pack essentials to a vector for(int j=0; j< UDERA::esscount; ++j) { ess1[j] = ARecvSizes[j][Aownind]; } ARecvNext = new UDERA(); // create the object first oput << "For A, Fetching " << (void*) rowwinnext[0] << std::endl; SpParHelper::FetchMatrix(*ARecvNext, ess1, rowwinnext, Aownind); } if(Bownind == (B.commGrid)->GetRankInProcCol()) { BRecvNext = B.spSeq; // shallow-copy } else { std::vector ess2(UDERB::esscount); // pack essentials to a vector for(int j=0; j< UDERB::esscount; ++j) { ess2[j] = BRecvSizes[j][Bownind]; } BRecvNext = new UDERB(); oput << "For B, Fetching " << (void*)colwinnext[0] << std::endl; SpParHelper::FetchMatrix(*BRecvNext, ess2, colwinnext, Bownind); // No lock version, only get ! } oput << "Fencing " << (void*) rowwindows[0] << std::endl; oput << "Fencing " << (void*) colwindows[0] << std::endl; for(int j=0; j< rowwindows.size(); ++j) MPI_Win_fence(MPI_MODE_NOSTORE, rowwindows[j]); // Synch using "other" windows for(int j=0; j< colwindows.size(); ++j) MPI_Win_fence(MPI_MODE_NOSTORE, colwindows[j]); SpTuples * C_cont = MultiplyReturnTuples(*ARecv, *BRecv, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); if(Aownprev != (A.commGrid)->GetRankInProcRow()) delete ARecv; if(Bownprev != (B.commGrid)->GetRankInProcCol()) delete BRecv; Aownprev = Aownind; Bownprev = Bownind; } else // fetch to Recv via windows, fence on RecvNext via winnext { if(Aownind == (A.commGrid)->GetRankInProcRow()) { ARecv = A.spSeq; // shallow-copy } else { std::vector ess1(UDERA::esscount); // pack essentials to a vector for(int j=0; j< UDERA::esscount; ++j) { ess1[j] = ARecvSizes[j][Aownind]; } ARecv = new UDERA(); // create the object first oput << "For A, Fetching " << (void*) rowwindows[0] << std::endl; SpParHelper::FetchMatrix(*ARecv, ess1, rowwindows, Aownind); } if(Bownind == (B.commGrid)->GetRankInProcCol()) { BRecv = B.spSeq; // shallow-copy } else { std::vector ess2(UDERB::esscount); // pack essentials to a vector for(int j=0; j< UDERB::esscount; ++j) { ess2[j] = BRecvSizes[j][Bownind]; } BRecv = new UDERB(); oput << "For B, Fetching " << (void*)colwindows[0] << std::endl; SpParHelper::FetchMatrix(*BRecv, ess2, colwindows, Bownind); // No lock version, only get ! } oput << "Fencing " << (void*) rowwinnext[0] << std::endl; oput << "Fencing " << (void*) rowwinnext[0] << std::endl; for(int j=0; j< rowwinnext.size(); ++j) MPI_Win_fence(MPI_MODE_NOSTORE, rowwinnext[j]); // Synch using "other" windows for(int j=0; j< colwinnext.size(); ++j) MPI_Win_fence(MPI_MODE_NOSTORE, colwinnext[j]); SpTuples * C_cont = MultiplyReturnTuples(*ARecvNext, *BRecvNext, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); if(Aownprev != (A.commGrid)->GetRankInProcRow()) delete ARecvNext; if(Bownprev != (B.commGrid)->GetRankInProcCol()) delete BRecvNext; Aownprev = Aownind; Bownprev = Bownind; } } if(stages % 2 == 1) // fence on Recv via windows { oput << "Fencing " << (void*) rowwindows[0] << std::endl; oput << "Fencing " << (void*) colwindows[0] << std::endl; for(int j=0; j< rowwindows.size(); ++j) MPI_Win_fence(MPI_MODE_NOSUCCEED, rowwindows[j]); // Synch using "prev" windows for(int j=0; j< colwindows.size(); ++j) MPI_Win_fence(MPI_MODE_NOSUCCEED, colwindows[j]); SpTuples * C_cont = MultiplyReturnTuples(*ARecv, *BRecv, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); if(Aownprev != (A.commGrid)->GetRankInProcRow()) delete ARecv; if(Bownprev != (B.commGrid)->GetRankInProcRow()) delete BRecv; } else // fence on RecvNext via winnext { oput << "Fencing " << (void*) rowwinnext[0] << std::endl; oput << "Fencing " << (void*) colwinnext[0] << std::endl; for(int j=0; j< rowwinnext.size(); ++j) MPI_Win_fence(MPI_MODE_NOSUCCEED, rowwinnext[j]); // Synch using "prev" windows for(int j=0; j< colwinnext.size(); ++j) MPI_Win_fence(MPI_MODE_NOSUCCEED, colwinnext[j]); SpTuples * C_cont = MultiplyReturnTuples(*ARecvNext, *BRecvNext, false, true); if(!C_cont->isZero()) tomerge.push_back(C_cont); if(Aownprev != (A.commGrid)->GetRankInProcRow()) delete ARecvNext; if(Bownprev != (B.commGrid)->GetRankInProcRow()) delete BRecvNext; } for(int i=0; i< rowwindows.size(); ++i) { MPI_Win_free(&rowwindows[i]); MPI_Win_free(&rowwinnext[i]); } for(int i=0; i< colwindows.size(); ++i) { MPI_Win_free(&colwindows[i]); MPI_Win_free(&colwinnext[i]); } MPI_Barrier(GridC->GetWorld()); IU C_m = A.spSeq->getnrow(); IU C_n = B.spSeq->getncol(); DER_promote * C = new DER_promote(MergeAll(tomerge, C_m, C_n), false, NULL); // First get the result in SpTuples, then convert to UDER for(int i=0; i(B.spSeq)->Transpose(); // transpose back to original return SpParMat (C, GridC); // return the result object } } #endif CombBLAS_beta_16_2/include/CombBLAS/BitMap.h000644 000765 000024 00000011270 13271404146 021721 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.4 -------------------------------------------------*/ /* date: 1/17/2014 ---------------------------------------------*/ /* authors: Aydin Buluc (abuluc@lbl.gov), Adam Lugowski --------*/ /* this file contributed by Scott Beamer of UC Berkeley --------*/ /****************************************************************/ /* Copyright (c) 2010-2014, The Regents of the University of California 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. */ #ifndef BITMAP_H #define BITMAP_H #include #define WORD_OFFSET(n) (n/64) #define BIT_OFFSET(n) (n & 0x3f) namespace combblas { class BitMap { public: // actually always rounds up BitMap() { start = NULL; end = NULL;}; // default constructor BitMap(uint64_t size) { uint64_t num_longs = (size + 63) / 64; // VS9: warning C4244: 'initializing' : conversion from 'uint64_t' to 'unsigned int', possible loss of data start = new uint64_t[num_longs](); // zero initialized by default end = start + num_longs; } ~BitMap() { delete[] start; } BitMap(const BitMap & rhs) { uint64_t num_longs = rhs.end - rhs.start; // VS9: warning C4244: 'initializing' : conversion from 'uint64_t' to 'unsigned int', possible loss of data start = new uint64_t[num_longs]; end = start + num_longs; std::copy(rhs.start, rhs.end, start); } BitMap & operator= (const BitMap & rhs) { if(this != &rhs) { delete [] start; uint64_t num_longs = rhs.end - rhs.start; // VS9: warning C4244: 'initializing' : conversion from 'uint64_t' to 'unsigned int', possible loss of data start = new uint64_t[num_longs]; end = start + num_longs; std::copy(rhs.start, rhs.end, start); } return *this; } inline void reset() { for(uint64_t *it=start; it!=end; it++) *it = 0; } inline void set_bit(uint64_t pos) { start[WORD_OFFSET(pos)] |= ( static_cast(1l)<(1l)<(1l) <> (bit_offset+1); } else { temp = 0; } if (!temp) { next = (next & 0xffffffc0); while (!temp) { it++; if (it >= end) return -1; temp = *it; next += 64; } } else { next++; } while(!(temp&1)) { temp = temp >> 1; next++; } return next; } inline uint64_t* data() { return start; } void copy_from(const BitMap* other) { std::copy(other->start, other->end, start); } void print_ones() { uint64_t max_size = (end-start)*64; for (uint64_t i=0; i #include namespace combblas { class outofrangeexception: public std::exception { virtual const char* what() const throw() { return "Index out of range exception"; } }; } #endif CombBLAS_beta_16_2/include/CombBLAS/SpMat.cpp000644 000765 000024 00000013610 13271404146 022124 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include #include "SpMat.h" #include "Friends.h" namespace combblas { template SpMat SpMat::operator() (const std::vector & ri, const std::vector & ci) const { if( (!ci.empty()) && (ci.back() > getncol())) { std::cerr << "Col indices out of bounds" << std::endl; abort(); } if( (!ri.empty()) && (ri.back() > getnrow())) { std::cerr << "Row indices out of bounds" << std::endl; abort(); } return ((static_cast(*this)) (ri, ci)); } template bool SpMat::operator== (const SpMat & rhs) const { return ((static_cast(*this)) == (static_cast(rhs)) ); } template void SpMat::Split( SpMat< IT,NT,DER > & partA, SpMat< IT,NT,DER > & partB) { static_cast< DER* >(this)->Split(static_cast< DER & >(partA), static_cast< DER & >(partB)); } template void SpMat::Merge( SpMat< IT,NT,DER > & partA, SpMat< IT,NT,DER > & partB) { static_cast< DER* >(this)->Merge(static_cast< DER & >(partA), static_cast< DER & >(partB)); } template template void SpMat::SpGEMM(SpMat & A, SpMat & B, bool isAT, bool isBT) { IT A_m, A_n, B_m, B_n; if(isAT) { A_m = A.getncol(); A_n = A.getnrow(); } else { A_m = A.getnrow(); A_n = A.getncol(); } if(isBT) { B_m = B.getncol(); B_n = B.getnrow(); } else { B_m = B.getnrow(); B_n = B.getncol(); } if(getnrow() == A_m && getncol() == B_n) { if(A_n == B_m) { if(isAT && isBT) { static_cast< DER* >(this)->template PlusEq_AtXBt< SR >(static_cast< DER & >(A), static_cast< DER & >(B)); } else if(isAT && (!isBT)) { static_cast< DER* >(this)->template PlusEq_AtXBn< SR >(static_cast< DER & >(A), static_cast< DER & >(B)); } else if((!isAT) && isBT) { static_cast< DER* >(this)->template PlusEq_AnXBt< SR >(static_cast< DER & >(A), static_cast< DER & >(B)); } else { static_cast< DER* >(this)->template PlusEq_AnXBn< SR >(static_cast< DER & >(A), static_cast< DER & >(B)); } } else { std::cerr <<"Not multipliable: " << A_n << "!=" << B_m << std::endl; } } else { std::cerr<< "Not addable: "<< getnrow() << "!=" << A_m << " or " << getncol() << "!=" << B_n << std::endl; } }; template SpTuples * MultiplyReturnTuples (const SpMat & A, const SpMat & B, bool isAT, bool isBT, bool clearA = false, bool clearB = false) { IU A_n, B_m; if(isAT) { A_n = A.getnrow(); } else { A_n = A.getncol(); } if(isBT) { B_m = B.getncol(); } else { B_m = B.getnrow(); } if(A_n == B_m) { if(isAT && isBT) { return Tuples_AtXBt(static_cast< const DER1 & >(A), static_cast< const DER2 & >(B), clearA, clearB); } else if(isAT && (!isBT)) { return Tuples_AtXBn(static_cast< const DER1 & >(A), static_cast< const DER2 & >(B), clearA, clearB); } else if((!isAT) && isBT) { return Tuples_AnXBt(static_cast< const DER1 & >(A), static_cast< const DER2 & >(B), clearA, clearB); } else { return Tuples_AnXBn(static_cast< const DER1 & >(A), static_cast< const DER2 & >(B), clearA, clearB); } } else { std::cerr <<"Not multipliable: " << A_n << "!=" << B_m << std::endl; return new SpTuples (0, 0, 0); } } template inline std::ofstream& SpMat::put(std::ofstream& outfile) const { return static_cast(this)->put(outfile); } template inline std::ifstream& SpMat::get(std::ifstream& infile) { std::cout << "Getting... SpMat" << std::endl; return static_cast(this)->get(infile); } template < typename UIT, typename UNT, typename UDER > std::ofstream& operator<<(std::ofstream& outfile, const SpMat< UIT,UNT,UDER > & s) { return s.put(outfile); } template < typename UIT, typename UNT, typename UDER > std::ifstream& operator>> (std::ifstream& infile, SpMat< UIT,UNT,UDER > & s) { return s.get(infile); } } CombBLAS_beta_16_2/include/CombBLAS/FileHeader.h000644 000765 000024 00000007020 13271404146 022533 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _COMBBLAS_FILE_HEADER_ #define _COMBBLAS_FILE_HEADER_ #include "CombBLAS.h" namespace combblas { struct HeaderInfo { HeaderInfo():fileexists(false), headerexists(false) {}; bool fileexists; bool headerexists; uint64_t version; uint64_t objsize; uint64_t format; // 0: binary, 1: ascii uint64_t m; uint64_t n; uint64_t nnz; }; // cout's are OK because ParseHeader is run by a single processor only inline HeaderInfo ParseHeader(const std::string & inputname, FILE * & f, int & seeklength) { f = fopen(inputname.c_str(), "rb"); HeaderInfo hinfo; memset(&hinfo, 0, sizeof(hinfo)); if(!f) { std::cerr << "Problem reading binary input file\n"; f = NULL; return hinfo; } char fourletters[5]; size_t result = fread(fourletters, sizeof(char), 4, f); fourletters[4] = '\0'; if (result != 4) { std::cout << "Error in fread of header, only " << result << " entries read" << std::endl; return hinfo;} if(strcmp(fourletters,"HKDT") != 0) { rewind(f); fclose(f); hinfo.fileexists = true; return hinfo; } else { hinfo.fileexists = true; hinfo.headerexists = true; } size_t results[6]; results[0] = fread(&(hinfo.version), sizeof(hinfo.version), 1, f); results[1] = fread(&(hinfo.objsize), sizeof(hinfo.objsize), 1, f); results[2] = fread(&(hinfo.format), sizeof(hinfo.format), 1, f); results[3] = fread(&(hinfo.m), sizeof(hinfo.m), 1, f); results[4] = fread(&(hinfo.n), sizeof(hinfo.n), 1, f); results[5] = fread(&(hinfo.nnz), sizeof(hinfo.nnz), 1, f); if(std::accumulate(results,results+6,0) != 6) { std::cout << "The required 6 fields (version, objsize, format, m,n,nnz) are not read" << std::endl; std::cout << "Only " << std::accumulate(results,results+6,0) << " fields are read" << std::endl; } else { #ifdef DEBUG std::cout << "Version " << hinfo.version << ", object size " << hinfo.objsize << std::endl; std::cout << "Rows " << hinfo.m << ", columns " << hinfo.m << ", nonzeros " << hinfo.nnz << std::endl; #endif } seeklength = 4 + 6 * sizeof(uint64_t); return hinfo; } } #endif CombBLAS_beta_16_2/include/CombBLAS/SpDefs.h000644 000765 000024 00000007337 13271404146 021742 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SP_DEFS_H_ #define _SP_DEFS_H_ #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #ifdef _STDINT_H #undef _STDINT_H #endif #ifdef _GCC_STDINT_H // for cray #undef _GCC_STDINT_H // original stdint does #include_next<"/opt/gcc/4.5.2/snos/lib/gcc/x86_64-suse-linux/4.5.2/include/stdint-gcc.h"> #endif #include #include #include #include #include "SequenceHeaps/knheap.C" #include "psort/psort.h" #include "psort/psort_samplesort.h" #include "psort/MersenneTwister.h" #include "CommGrid.h" extern int cblas_splits; // TODO: move this inside namespace namespace combblas { #define ONEMILLION 1000000 #define MAXLINELENGTH 200 #define MINLINELENGTH 2 #define PRINT_LIMIT 50 #define EPSILON 0.01 #define FLOPSPERLOC 0 // always use SPA based merger inside the sequential code #define HEAPMERGE 1 // use heapmerge for accumulating contributions from row neighbors #define MEM_EFFICIENT_STAGES 16 #define MAXVERTNAME 64 // MPI::Abort codes #define GRIDMISMATCH 3001 #define DIMMISMATCH 3002 #define NOTSQUARE 3003 #define NOFILE 3004 #define MATRIXALIAS 3005 #define UNKNOWNMPITYPE 3006 // Enable bebug prints //#define SPREFDEBUG //#define IODEBUG //#define SPGEMMDEBUG // MPI Message tags // Prefixes denote functions // TR: Transpose // RD: ReadDistribute // RF: Sparse matrix indexing #define TRTAGNZ 121 #define TRTAGM 122 #define TRTAGN 123 #define TRTAGROWS 124 #define TRTAGCOLS 125 #define TRTAGVALS 126 #define RDTAGINDS 127 #define RDTAGVALS 128 #define RDTAGNNZ 129 #define RFROWIDS 130 #define RFCOLIDS 131 #define TRROWX 132 #define TRCOLX 133 #define TRX 134 #define TRI 135 #define TRNNZ 136 #define TROST 137 #define TRLUT 138 #define SWAPTAG 139 #define ROTATE 140 #define PUPSIZE 141 #define PUPDATA 142 enum Dim { Column, Row }; // force 8-bytes alignment in heap allocated memory #ifndef ALIGN #define ALIGN 8 #endif #ifndef THRESHOLD #define THRESHOLD 4 // if range1.size() / range2.size() < threshold, use scanning based indexing #endif #ifndef MEMORYINBYTES #define MEMORYINBYTES (196 * 1048576) // 196 MB, it is advised to define MEMORYINBYTES to be "at most" (1/4)th of available memory per core #endif } #endif CombBLAS_beta_16_2/include/CombBLAS/BitMapFringe.h000644 000765 000024 00000016015 13271404146 023056 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.4 -------------------------------------------------*/ /* date: 1/17/2014 ---------------------------------------------*/ /* authors: Aydin Buluc (abuluc@lbl.gov), Adam Lugowski --------*/ /* this file contributed by Scott Beamer of UC Berkeley --------*/ /****************************************************************/ /* Copyright (c) 2010-2014, The Regents of the University of California 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. */ #ifndef BITMAPFRINGE_H #define BITMAPFRINGE_H // #include #include "BitMap.h" #include "CommGrid.h" namespace combblas { template class BitMapFringe { public: BitMapFringe(std::shared_ptr grid, FullyDistSpVec & x) { cg.reset(new CommGrid(*grid)); MPI_Comm World = x.getcommgrid()->GetWorld(); MPI_Comm ColWorld = x.getcommgrid()->GetColWorld(); MPI_Status status; // Find out how big local chunk will be after transpose long num_local_send = x.MyLocLength(), num_local_recv; diagneigh = cg->GetComplementRank(); MPI_Sendrecv(&num_local_send, 1, MPI_LONG, diagneigh, TROST, &num_local_recv, 1, MPI_LONG, diagneigh, TROST, World, &status); // Calculate new local displacements MPI_Comm_size(ColWorld, &colneighs); MPI_Comm_rank(ColWorld, &colrank); int counts[colneighs]; counts[colrank] = num_local_recv; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, counts, 1, MPI_INT, ColWorld); int dpls[colneighs]; dpls[0] = 0; std::partial_sum(counts, counts+colneighs-1, dpls+1); long total_size = dpls[colneighs-1] + counts[colneighs-1]; local_offset = x.LengthUntil(); local_num_set = 0; // Compute word/byte displacements and send counts for gather word_dpls = new int[colneighs]; byte_dpls = new int[colneighs]; send_counts = new int[colneighs]; for (int c=0; c>6; byte_dpls[c] = word_dpls[c]<<3; send_counts[c] = (counts[c] - (64-(dpls[c] % 64)) + 63)>>6; } // Compute subword displacements and transpose exchange details trans_subword_disp = dpls[colrank] % 64; MPI_Sendrecv(&trans_subword_disp, 1, MPIType(), diagneigh, TROST, &local_subword_disp, 1, MPIType(), diagneigh, TROST, World, &status); trans_words_send = (num_local_send + local_subword_disp + 63)>>6; trans_words_recv = (num_local_recv + trans_subword_disp + 63)>>6; // Allocate bitmaps local_bm = new BitMap(num_local_send + local_subword_disp); next_bm = new BitMap(num_local_send + local_subword_disp); trans_bm = new BitMap(num_local_recv + trans_subword_disp); gather_bm = new BitMap(total_size); } ~BitMapFringe() { delete local_bm; delete next_bm; delete trans_bm; delete gather_bm; delete[] send_counts; delete[] word_dpls; delete[] byte_dpls; } void LoadFromSpVec(FullyDistSpVec & x) { local_bm->reset(); for (SparseVectorLocalIterator spit(x); spit.HasNext(); spit.Next()) local_bm->set_bit(spit.GetLocIndex() + local_subword_disp); local_num_set = x.getlocnnz(); } void LoadFromUpdates(IT* updates, long total_updates) { local_bm->reset(); for (long i=0; iset_bit(updates[i] - local_offset + local_subword_disp); local_num_set = total_updates; } void LoadFromNext() { local_num_set = next_num_set; } void SetNext(IT local_index) { next_num_set++; } void IncrementNumSet(int num_updates) { next_num_set += num_updates; } BitMap* TransposeGather() { MPI_Comm World = cg->GetWorld(); MPI_Comm ColWorld = cg->GetColWorld(); MPI_Status status; // Transpose bitmaps MPI_Sendrecv(local_bm->data(), trans_words_send, MPIType(), diagneigh, TROST, trans_bm->data(), trans_words_recv, MPIType(), diagneigh, TROST, World, &status); // Gather all but first words #ifdef BOTTOMUPTIME double t1 = MPI_Wtime(); #endif MPI_Allgatherv(trans_bm->data()+1, send_counts[colrank], MPIType(), gather_bm->data(), send_counts, word_dpls, MPIType(), ColWorld); #ifdef BOTTOMUPTIME double t2 = MPI_Wtime(); bottomup_allgather += (t2-t1); #endif // Gather first words & add in gather_bm->data()[0] = 0; uint64_t firsts[colneighs]; firsts[colrank] = trans_bm->data()[0]; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), firsts, 1, MPIType(), ColWorld); for (int c=0; cdata()[word_dpls[c]-1] |= firsts[c]; next_bm->reset(); next_num_set = 0; return gather_bm; } void UpdateSpVec(FullyDistSpVec & x) { IT *updates = new IT[local_num_set]; IT bm_index=local_subword_disp, up_index=0; if (local_bm->get_bit(bm_index)) // if the first valid bit is 1 updates[up_index++] = bm_index - local_subword_disp; // ABAB: local_subword_disp is NOT subtracted (as local_subword_disp is equal to bm_index) bm_index = local_bm->get_next_bit(bm_index); while(bm_index != -1) { updates[up_index++] = bm_index - local_subword_disp; // ABAB: local_subword_disp is subtracted bm_index = local_bm->get_next_bit(bm_index); } x.BulkSet(updates, local_num_set); delete[] updates; } IT GetNumSet() { IT global_num_set = 0; MPI_Allreduce(&local_num_set, &global_num_set, 1, MPIType(), MPI_SUM, cg->GetWorld()); return global_num_set; } int GetSubWordDisp() { return local_subword_disp; } BitMap* AccessBM() { return local_bm; } private: std::shared_ptr cg; BitMap* local_bm; BitMap* next_bm; BitMap* trans_bm; BitMap* gather_bm; int* send_counts; int* byte_dpls; int* word_dpls; int colneighs; int colrank; int diagneigh; IT local_offset; IT local_num_set; IT next_num_set; int local_subword_disp; int trans_subword_disp; long trans_words_send; long trans_words_recv; }; } #endif // BITMAPFRINGE_H CombBLAS_beta_16_2/include/CombBLAS/SpParMat.h000644 000765 000024 00000051163 13271404146 022241 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SP_PAR_MAT_H_ #define _SP_PAR_MAT_H_ #include #include #include #include #include #include #include "SpMat.h" #include "SpTuples.h" #include "SpDCCols.h" #include "CommGrid.h" #include "MPIType.h" #include "LocArr.h" #include "SpDefs.h" #include "Deleter.h" #include "SpHelper.h" #include "SpParHelper.h" #include "DenseParMat.h" #include "FullyDistVec.h" #include "Friends.h" #include "Operations.h" #include "DistEdgeList.h" #include "CombBLAS.h" namespace combblas { /** * Fundamental 2D distributed sparse matrix class * The index type IT is encapsulated by the class in a way that it is only * guarantee that the implementation will ensure the requested semantics. * For instance, if IT=int64 then the implementation can still use 32 bit * local indices but it should return correct 64-bit numbers in its functions. * In other words, DER can be SpDCCols while IT=int64_t */ template class SpParMat { public: typedef typename DER::LocalIT LocalIT; typedef typename DER::LocalNT LocalNT; typedef IT GlobalIT; typedef NT GlobalNT; // Constructors SpParMat (); SpParMat (MPI_Comm world); // ABAB: there is risk that any integer would call this constructor due to MPICH representation SpParMat (std::shared_ptr grid); SpParMat (DER * myseq, std::shared_ptr grid); SpParMat (std::ifstream & input, MPI_Comm & world); SpParMat (DER * myseq, MPI_Comm & world); template SpParMat (const DistEdgeList< DELIT > & rhs, bool removeloops = true); // conversion from distributed edge list SpParMat (const SpParMat< IT,NT,DER > & rhs); // copy constructor SpParMat (IT total_m, IT total_n, const FullyDistVec & , const FullyDistVec & , const FullyDistVec & , bool SumDuplicates = false); // matlab sparse SpParMat (IT total_m, IT total_n, const FullyDistVec & , const FullyDistVec & , const NT & , bool SumDuplicates = false); // matlab sparse SpParMat< IT,NT,DER > & operator=(const SpParMat< IT,NT,DER > & rhs); // assignment operator SpParMat< IT,NT,DER > & operator+=(const SpParMat< IT,NT,DER > & rhs); ~SpParMat (); template void Square (); float LoadImbalance() const; void Transpose(); void FreeMemory(); void EWiseMult (const SpParMat< IT,NT,DER > & rhs, bool exclude); void EWiseScale (const DenseParMat & rhs); void Find (FullyDistVec & , FullyDistVec & , FullyDistVec & ) const; void Find (FullyDistVec & , FullyDistVec & ) const; template void DimApply(Dim dim, const FullyDistVec& v, _BinaryOperation __binary_op); template FullyDistVec Reduce(Dim dim, _BinaryOperation __binary_op, NT id, _UnaryOperation __unary_op) const; template FullyDistVec Reduce(Dim dim, _BinaryOperation __binary_op, NT id) const; template void Reduce(FullyDistVec & rvec, Dim dim, _BinaryOperation __binary_op, VT id, _UnaryOperation __unary_op) const; template void Reduce(FullyDistVec & rvec, Dim dim, _BinaryOperation __binary_op, VT id) const; template bool Kselect(FullyDistVec & rvec, IT k_limit, int kselectVersion) const; template bool Kselect(FullyDistSpVec & kth, IT k_limit, int kselectVersion) const; //sparse case template bool Kselect1(FullyDistVec & rvec, IT k_limit, _UnaryOperation __unary_op) const; // TODO: make private template bool Kselect1(FullyDistSpVec & rvec, IT k_limit, _UnaryOperation __unary_op) const; // TODO: make private template bool Kselect1(FullyDistVec & rvec, IT k_limit) const; // TODO: make private template bool Kselect2(FullyDistVec & rvec, IT k_limit) const; // TODO: make private IT Bandwidth() const; IT Profile() const; template void MaskedReduce(FullyDistVec & rvec, FullyDistSpVec & mask, Dim dim, _BinaryOperation __binary_op, VT id, bool exclude=false) const; template void MaskedReduce(FullyDistVec & rvec, FullyDistSpVec & mask, Dim dim, _BinaryOperation __binary_op, VT id, _UnaryOperation __unary_op, bool exclude=false) const; template void Apply(_UnaryOperation __unary_op) { spSeq->Apply(__unary_op); } IT RemoveLoops(); // returns the number of loops removed void AddLoops(NT loopval, bool replaceExisting=false); void AddLoops(FullyDistVec loopvals, bool replaceExisting=false); template void OptimizeForGraph500(OptBuf & optbuf); void ActivateThreading(int numsplits); // SpParMat PruneI(_UnaryOperation __unary_op, bool inPlace = true) //PruneI(__unary_op, inPlace, grow, gcol); return SpParMat(getcommgrid()); // return blank to match signature } else { return SpParMat(spSeq->PruneI(__unary_op, inPlace, grow, gcol), commGrid); } } template SpParMat Prune(_UnaryOperation __unary_op, bool inPlace = true) //Prune(__unary_op, inPlace); return SpParMat(getcommgrid()); // return blank to match signature } else { return SpParMat(spSeq->Prune(__unary_op, inPlace), commGrid); } } template SpParMat PruneColumn(const FullyDistVec & pvals, _BinaryOperation __binary_op, bool inPlace=true); template SpParMat PruneColumn(const FullyDistSpVec & pvals, _BinaryOperation __binary_op, bool inPlace=true); template void UpdateDense(DenseParMat & rhs, _BinaryOperation __binary_op) const; void Dump(std::string filename) const; void PrintInfo() const; template operator SpParMat< IT,NNT,NDER > () const; //!< Type conversion operator template operator SpParMat< NIT,NNT,NDER > () const; //!< Type conversion operator (for indices as well) IT getnrow() const; IT getncol() const; IT getnnz() const; template int Owner(IT total_m, IT total_n, IT grow, IT gcol, LIT & lrow, LIT & lcol) const; SpParMat SubsRefCol (const std::vector & ci) const; //!< Column indexing with special parallel semantics //! General indexing with serial semantics template SpParMat SubsRef_SR (const FullyDistVec & ri, const FullyDistVec & ci, bool inplace=false); SpParMat operator() (const FullyDistVec & ri, const FullyDistVec & ci, bool inplace=false) { return SubsRef_SR, BoolCopy2ndSRing >(ri, ci, inplace); } void Prune(const FullyDistVec & ri, const FullyDistVec & ci); //!< prune all entries whose row indices are in ri and column indices are in ci void SpAsgn(const FullyDistVec & ri, const FullyDistVec & ci, SpParMat & B); bool operator== (const SpParMat & rhs) const; class ScalarReadSaveHandler { public: NT getNoNum(IT row, IT col) { return static_cast(1); } void binaryfill(FILE * rFile, IT & row, IT & col, NT & val) { if (fread(&row, sizeof(IT), 1,rFile) != 1) std::cout << "binaryfill(): error reading row index" << std::endl; if (fread(&col, sizeof(IT), 1,rFile) != 1) std::cout << "binaryfill(): error reading col index" << std::endl; if (fread(&val, sizeof(NT), 1,rFile) != 1) std::cout << "binaryfill(): error reading value" << std::endl; return; } size_t entrylength() { return 2*sizeof(IT)+sizeof(NT); } template NT read(std::basic_istream& is, IT row, IT col) { NT v; is >> v; return v; } template void save(std::basic_ostream& os, const NT& v, IT row, IT col) { os << v; } }; template void ParallelReadMM (const std::string & filename, bool onebased, _BinaryOperation BinOp); template FullyDistVec> ReadGeneralizedTuples(const std::string&, _BinaryOperation); template void ReadDistribute (const std::string & filename, int master, bool nonum, HANDLER handler, bool transpose = false, bool pario = false); void ReadDistribute (const std::string & filename, int master, bool nonum=false, bool pario = false) { ReadDistribute(filename, master, nonum, ScalarReadSaveHandler(), false, pario); } template void SaveGathered(std::string filename, HANDLER handler, bool transpose = false) const; void SaveGathered(std::string filename) const { SaveGathered(filename, ScalarReadSaveHandler(), false); } std::ofstream& put(std::ofstream& outfile) const; std::shared_ptr getcommgrid() const { return commGrid; } typename DER::LocalIT getlocalrows() const { return spSeq->getnrow(); } typename DER::LocalIT getlocalcols() const { return spSeq->getncol();} typename DER::LocalIT getlocalnnz() const { return spSeq->getnnz(); } DER & seq() { return (*spSeq); } DER * seqptr() { return spSeq; } template void SparseCommon(std::vector< std::vector < std::tuple > > & data, LIT locsize, IT total_m, IT total_n, _BinaryOperation BinOp); //! Friend declarations template friend SpParMat Mult_AnXBn_DoubleBuff (SpParMat & A, SpParMat & B, bool clearA, bool clearB); template friend SpParMat Mult_AnXBn_Synch (SpParMat & A, SpParMat & B, bool clearA, bool clearB); template friend int64_t EstPerProcessNnzSUMMA(SpParMat & A, SpParMat & B); template friend SpParMat::T_promote,typename promote_trait::T_promote> Mult_AnXBn_ActiveTarget (const SpParMat & A, const SpParMat & B ); template friend SpParMat::T_promote,typename promote_trait::T_promote> Mult_AnXBn_PassiveTarget (const SpParMat & A, const SpParMat & B ); template friend SpParMat::T_promote,typename promote_trait::T_promote> Mult_AnXBn_Fence (const SpParMat & A, const SpParMat & B ); template friend SpParMat MemEfficientSpGEMM (SpParMat & A, SpParMat & B, int phases, NUO hardThreshold, IU selectNum, IU recoverNum, NUO recoverPct, int kselectVersion, int64_t perProcessMem); template friend FullyDistSpVec::T_promote> SpMV (const SpParMat & A, const FullyDistSpVec & x ); template friend FullyDistVec::T_promote> SpMV (const SpParMat & A, const FullyDistVec & x ); template friend FullyDistSpVec::T_promote> SpMV (const SpParMat & A, const FullyDistSpVec & x, bool indexisvalue); // output type is part of the signature template friend void SpMV (const SpParMat & A, const FullyDistSpVec & x, FullyDistSpVec & y, bool indexisvalue); template friend void SpMV (const SpParMat & A, const FullyDistSpVec & x, FullyDistSpVec & y,bool indexisvalue, OptBuf & optbuf); template friend SpParMat::T_promote,typename promote_trait::T_promote> EWiseMult (const SpParMat & A, const SpParMat & B , bool exclude); template friend SpParMat EWiseApply (const SpParMat & A, const SpParMat & B, _BinaryOperation __binary_op, bool notB, const NU2& defaultBVal); template friend SpParMat EWiseApply (const SpParMat & A, const SpParMat & B, _BinaryOperation __binary_op, _BinaryPredicate do_op, bool allowANulls, bool allowBNulls, const NU1& ANullVal, const NU2& BNullVal, const bool allowIntersect, const bool useExtendedBinOp); template friend void LocalSpMV(const SpParMat & A, int rowneighs, OptBuf & optbuf, int32_t * & indacc, IVT * & numacc, int32_t * & sendindbuf, OVT * & sendnumbuf, int * & sdispls, int * sendcnt, int accnz, bool indexisvalue, PreAllocatedSPA & SPA); template friend void LocalSpMV(const SpParMat & A, int rowneighs, OptBuf & optbuf, int32_t * & indacc, VT * & numacc, int * sendcnt, int accnz); private: typedef std::array STRASARRAY; typedef std::pair< STRASARRAY, uint64_t> TYPE2SEND; class CharArraySaveHandler { public: // no reader template void save(std::basic_ostream& os, STRASARRAY & chararray, int64_t index) { auto locnull = std::find(chararray.begin(), chararray.end(), '\0'); // find the null character (or string::end) std::string strtmp(chararray.begin(), locnull); // range constructor os << strtmp; } }; MPI_File TupleRead1stPassNExchange (const std::string & filename, TYPE2SEND * & senddata, IT & totsend, FullyDistVec & distmapper, uint64_t & totallength); template void Reduce(FullyDistVec & rvec, Dim dim, _BinaryOperation __binary_op, VT id, _UnaryOperation __unary_op, MPI_Op mympiop) const; template // GIT: global index type of vector void TopKGather(std::vector & all_medians, std::vector & nnz_per_col, int & thischunk, int & chunksize, const std::vector & medians, const std::vector & nnzperc, int itersuntil, std::vector< std::vector > & localmat, const std::vector & actcolsmap, std::vector & klimits, std::vector & toretain, std::vector>> & tmppair, IT coffset, const FullyDistVec & rvec) const; void GetPlaceInGlobalGrid(IT& rowOffset, IT& colOffset) const; void HorizontalSend(IT * & rows, IT * & cols, NT * & vals, IT * & temprows, IT * & tempcols, NT * & tempvals, std::vector < std::tuple > & localtuples, int * rcurptrs, int * rdispls, IT buffperrowneigh, int rowneighs, int recvcount, IT m_perproc, IT n_perproc, int rankinrow); template void ReadAllMine(FILE * binfile, IT * & rows, IT * & cols, NT * & vals, std::vector< std::tuple > & localtuples, int * rcurptrs, int * ccurptrs, int * rdispls, int * cdispls, IT m_perproc, IT n_perproc, int rowneighs, int colneighs, IT buffperrowneigh, IT buffpercolneigh, IT entriestoread, HANDLER handler, int rankinrow, bool transpose); void VerticalSend(IT * & rows, IT * & cols, NT * & vals, std::vector< std::tuple > & localtuples, int * rcurptrs, int * ccurptrs, int * rdispls, int * cdispls, IT m_perproc, IT n_perproc, int rowneighs, int colneighs, IT buffperrowneigh, IT buffpercolneigh, int rankinrow); void AllocateSetBuffers(IT * & rows, IT * & cols, NT * & vals, int * & rcurptrs, int * & ccurptrs, int rowneighs, int colneighs, IT buffpercolneigh); void BcastEssentials(MPI_Comm & world, IT & total_m, IT & total_n, IT & total_nnz, int master); std::shared_ptr commGrid; DER * spSeq; template friend class DenseParMat; template friend std::ofstream& operator<< (std::ofstream& outfile, const SpParMat & s); }; template void PSpGEMM(SpParMat & A, SpParMat & B, SpParMat & out, bool clearA = false, bool clearB = false) { out = Mult_AnXBn_Synch (A, B, clearA, clearB ); } template SpParMat::T_promote,typename promote_trait::T_promote> PSpGEMM (SpParMat & A, SpParMat & B, bool clearA = false, bool clearB = false) { typedef typename promote_trait::T_promote N_promote; typedef typename promote_trait::T_promote DER_promote; return Mult_AnXBn_Synch (A, B, clearA, clearB ); } } #include "SpParMat.cpp" #endif CombBLAS_beta_16_2/include/CombBLAS/DistEdgeList.cpp000644 000765 000024 00000033375 13271404146 023436 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.4 -------------------------------------------------*/ /* date: 1/17/2014 ---------------------------------------------*/ /* authors: Aydin Buluc (abuluc@lbl.gov), Adam Lugowski --------*/ /****************************************************************/ /* Copyright (c) 2010-2014, The Regents of the University of California 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. */ #include #include "SpParMat.h" #include "ParFriends.h" #include "Operations.h" #ifndef GRAPH_GENERATOR_SEQ #define GRAPH_GENERATOR_SEQ #endif #include "graph500/generator/graph_generator.h" #include "graph500/generator/utils.h" #include "RefGen21.h" #include #include namespace combblas { template DistEdgeList::DistEdgeList(): edges(NULL), pedges(NULL), nedges(0), globalV(0) { commGrid.reset(new CommGrid(MPI_COMM_WORLD, 0, 0)); } template DistEdgeList::DistEdgeList(MPI_Comm & myWorld): edges(NULL), pedges(NULL), nedges(0), globalV(0) { commGrid.reset(new CommGrid(myWorld, 0, 0)); } template DistEdgeList::DistEdgeList(const char * filename, IT globaln, IT globalm): edges(NULL), pedges(NULL), globalV(globaln) { commGrid.reset(new CommGrid(MPI_COMM_WORLD, 0, 0)); int nprocs = commGrid->GetSize(); int rank = commGrid->GetRank(); nedges = (rank == nprocs-1)? (globalm - rank * (globalm / nprocs)) : (globalm / nprocs); FILE * infp = fopen(filename, "rb"); assert(infp != NULL); IT read_offset_start, read_offset_end; read_offset_start = rank * 8 * (globalm / nprocs); read_offset_end = (rank+1) * 8 * (globalm / nprocs); if (rank == nprocs - 1) read_offset_end = 8*globalm; std::ofstream oput; commGrid->OpenDebugFile("BinRead", oput); if(infp != NULL) { oput << "File exists" << std::endl; oput << "Trying to read " << nedges << " edges out of " << globalm << std::endl; } else { oput << "File does not exist" << std::endl; } /* gen_edges is an array of unsigned ints of size 2*nedges */ uint32_t * gen_edges = new uint32_t[2*nedges]; fseek(infp, read_offset_start, SEEK_SET); fread(gen_edges, 2*nedges, sizeof(uint32_t), infp); SetMemSize(nedges); oput << "Freads done " << std::endl; for(IT i=0; i< 2*nedges; ++i) edges[i] = (IT) gen_edges[i]; oput << "Puts done " << std::endl; delete [] gen_edges; oput.close(); } #if 0 // Adam: // commenting out because there is a compiler error with MPI_File_open. template void DistEdgeList::Dump64bit(string filename) { int rank,nprocs; MPI_Comm World = commGrid->GetWorld(); MPI_Comm_rank(World, &rank); MPI_Comm_size(World, &nprocs); MPI_File thefile; MPI_File_open(World, filename.c_str(), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); IT * prelens = new IT[nprocs]; prelens[rank] = 2*nedges; MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), prelens, 1, MPIType(), commGrid->GetWorld()); IT lengthuntil = accumulate(prelens, prelens+rank, 0); // The disp displacement argument specifies the position // (absolute offset in bytes from the beginning of the file) MPI_File_set_view(thefile, int64_t(lengthuntil * sizeof(IT)), MPIType(), MPIType(), "native", MPI_INFO_NULL); MPI_File_write(thefile, edges, prelens[rank], MPIType(), NULL); MPI_File_close(&thefile); delete [] prelens; } template void DistEdgeList::Dump32bit(string filename) { int rank, nprocs; MPI_Comm World = commGrid->GetWorld(); MPI_Comm_rank(World, &rank); MPI_Comm_size(World, &nprocs); MPI_File thefile; MPI_File_open(World, filename.c_str(), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &thefile); IT * prelens = new IT[nprocs]; prelens[rank] = 2*nedges; MPI_Allgather(MPI_IN_PLACE, 0, MPIType(), prelens, 1, MPIType(), commGrid->GetWorld()); IT lengthuntil = accumulate(prelens, prelens+rank, static_cast(0)); // The disp displacement argument specifies the position // (absolute offset in bytes from the beginning of the file) MPI_File_set_view(thefile, int64_t(lengthuntil * sizeof(uint32_t)), MPI_UNSIGNED, MPI_UNSIGNED, "native", MPI_INFO_NULL); uint32_t * gen_edges = new uint32_t[prelens[rank]]; for(IT i=0; i< prelens[rank]; ++i) gen_edges[i] = (uint32_t) edges[i]; MPI_File_write(thefile, gen_edges, prelens[rank], MPI_UNSIGNED, NULL); MPI_File_close(&thefile); delete [] prelens; delete [] gen_edges; } #endif template DistEdgeList::~DistEdgeList() { if(edges) delete [] edges; if(pedges) delete [] pedges; } //! Allocates enough space template void DistEdgeList::SetMemSize(IT ne) { if (edges) { delete [] edges; edges = NULL; } memedges = ne; edges = 0; if (memedges > 0) edges = new IT[2*memedges]; } /** Removes all edges that begin with a -1. * Walks back from the end to tighten the nedges counter, then walks forward and replaces any edge * with a -1 source with the last edge. */ template void DistEdgeList::CleanupEmpties() { // find out how many edges there actually are while (nedges > 0 && edges[2*(nedges-1) + 0] == -1) { nedges--; } // remove marked multiplicities or self-loops for (IT i = 0; i < (nedges-1); i++) { if (edges[2*i + 0] == -1) { // the graph500 generator marked this edge as a self-loop or a multiple edge. // swap it with the last edge edges[2*i + 0] = edges[2*(nedges-1) + 0]; edges[2*i + 1] = edges[2*(nedges-1) + 1]; edges[2*(nedges-1) + 0] = -1; // mark this spot as unused while (nedges > 0 && edges[2*(nedges-1) + 0] == -1) // the swapped edge might be -1 too nedges--; } } } /** * Note that GenGraph500Data will return global vertex numbers (from 1... N). The ith edge can be * accessed with edges[2*i] and edges[2*i+1]. There will be duplicates and the data won't be sorted. * Generates an edge list consisting of an RMAT matrix suitable for the Graph500 benchmark. */ template void DistEdgeList::GenGraph500Data(double initiator[4], int log_numverts, int edgefactor, bool scramble, bool packed) { if(packed && (!scramble)) { SpParHelper::Print("WARNING: Packed version does always generate scrambled vertex identifiers\n"); } globalV = ((int64_t)1)<< log_numverts; int64_t globaledges = globalV * static_cast(edgefactor); if(packed) { RefGen21::make_graph (log_numverts, globaledges, &nedges, (packed_edge**)(&pedges), commGrid->GetWorld()); } else { // The generations use different seeds on different processors, generating independent // local RMAT matrices all having vertex ranges [0,...,globalmax-1] // Spread the two 64-bit numbers into five nonzero values in the correct range uint_fast32_t seed[5]; uint64_t size = (uint64_t) commGrid->GetSize(); uint64_t rank = (uint64_t) commGrid->GetRank(); #ifdef DETERMINISTIC uint64_t seed2 = 2; #else uint64_t seed2 = time(NULL); #endif make_mrg_seed(rank, seed2, seed); // we give rank as the first seed, so it is different on processors // a single pair of [val0,val1] for all the computation, global across all processors uint64_t val0, val1; /* Values for scrambling */ if(scramble) { if(rank == 0) RefGen21::MakeScrambleValues(val0, val1, seed); // ignore the return value MPI_Bcast(&val0, 1, MPIType(),0, commGrid->GetWorld()); MPI_Bcast(&val1, 1, MPIType(),0, commGrid->GetWorld()); } nedges = globaledges/size; SetMemSize(nedges); // clear the source vertex by setting it to -1 for (IT i = 0; i < nedges; i++) edges[2*i+0] = -1; generate_kronecker(0, 1, seed, log_numverts, nedges, initiator, edges); if(scramble) { for(IT i=0; i < nedges; ++i) { edges[2*i+0] = RefGen21::scramble(edges[2*i+0], log_numverts, val0, val1); edges[2*i+1] = RefGen21::scramble(edges[2*i+1], log_numverts, val0, val1); } } } } /** * Randomly permutes the distributed edge list. * Once we call Viral's psort on this vector, everything will go to the right place [tuples are * sorted lexicographically] and you can reconstruct the int64_t * edges in an embarrassingly parallel way. * As I understood, the entire purpose of this function is to destroy any locality. It does not * rename any vertices and edges are not named anyway. * For an example, think about the edge (0,1). It will eventually (at the end of kernel 1) be owned by processor P(0,0). * However, assume that processor P(r1,c1) has a copy of it before the call to PermEdges. After * this call, some other irrelevant processor P(r2,c2) will own it. So we gained nothing, it is just a scrambled egg. **/ template void PermEdges(DistEdgeList & DEL) { IT maxedges = DEL.memedges; // this can be optimized by calling the clean-up first // to lower memory consumption, rename in stages // this is not "identical" to a full randomization; // but more than enough to destroy any possible locality IT stages = 8; IT perstage = maxedges / stages; int nproc =(DEL.commGrid)->GetSize(); int rank = (DEL.commGrid)->GetRank(); IT * dist = new IT[nproc]; #ifdef DETERMINISTIC MTRand M(1); #else MTRand M; // generate random numbers with Mersenne Twister #endif for(IT s=0; s< stages; ++s) { #ifdef DEBUG SpParHelper::Print("PermEdges stage starting\n"); double st = MPI_Wtime(); #endif IT n_sofar = s*perstage; IT n_thisstage = ((s==(stages-1))? (maxedges - n_sofar): perstage); std::pair >* vecpair = new std::pair >[n_thisstage]; dist[rank] = n_thisstage; MPI_Allgather(MPI_IN_PLACE, 1, MPIType(), dist, 1, MPIType(), DEL.commGrid->GetWorld()); for (IT i = 0; i < n_thisstage; i++) { vecpair[i].first = M.rand(); vecpair[i].second.first = DEL.edges[2*(i+n_sofar)]; vecpair[i].second.second = DEL.edges[2*(i+n_sofar)+1]; } // less< pair > works correctly (sorts w.r.t. first element of type T1) SpParHelper::MemoryEfficientPSort(vecpair, n_thisstage, dist, DEL.commGrid->GetWorld()); // SpParHelper::DebugPrintKeys(vecpair, n_thisstage, dist, DEL.commGrid->GetWorld()); for (IT i = 0; i < n_thisstage; i++) { DEL.edges[2*(i+n_sofar)] = vecpair[i].second.first; DEL.edges[2*(i+n_sofar)+1] = vecpair[i].second.second; } delete [] vecpair; #ifdef DEBUG double et = MPI_Wtime(); std::ostringstream timeinfo; timeinfo << "Stage " << s << " in " << et-st << " seconds" << std::endl; SpParHelper::Print(timeinfo.str()); #endif } delete [] dist; } /** * Rename vertices globally. * You first need to do create a random permutation distributed on all processors. * Then the p round robin algorithm will do the renaming: * For all processors P(i,i) * Broadcast local_p to all p processors * For j= i*N/p to min((i+1)*N/p, N) * Rename the all j's with local_p(j) inside the edgelist (and mark them * "renamed" so that yeach vertex id is renamed only once) **/ template void RenameVertices(DistEdgeList & DEL) { int nprocs = DEL.commGrid->GetSize(); int rank = DEL.commGrid->GetRank(); MPI_Comm World = DEL.commGrid->GetWorld(); // create permutation FullyDistVec globalPerm(DEL.commGrid); globalPerm.iota(DEL.getGlobalV(), 0); globalPerm.RandPerm(); // now, randperm can return a 0-based permutation IU locrows = globalPerm.MyLocLength(); // way to mark whether each vertex was already renamed or not IU locedgelist = 2*DEL.getNumLocalEdges(); bool* renamed = new bool[locedgelist]; std::fill_n(renamed, locedgelist, 0); // permutation for one round IU * localPerm = NULL; IU permsize; IU startInd = 0; //vector < pair > vec; //for(IU i=0; i< DEL.getNumLocalEdges(); i++) // vec.push_back(make_pair(DEL.edges[2*i], DEL.edges[2*i+1])); //sort(vec.begin(), vec.end()); //vector < pair > uniqued; //unique_copy(vec.begin(), vec.end(), back_inserter(uniqued)); //cout << "before: " << vec.size() << " and after: " << uniqued.size() << endl; for (int round = 0; round < nprocs; round++) { // broadcast the permutation from the one processor if (rank == round) { permsize = locrows; localPerm = new IU[permsize]; std::copy(globalPerm.arr.begin(), globalPerm.arr.end(), localPerm); } MPI_Bcast(&permsize, 1, MPIType(), round, World); if(rank != round) { localPerm = new IU[permsize]; } MPI_Bcast(localPerm, permsize, MPIType(), round, World); // iterate over for (typename std::vector::size_type j = 0; j < (unsigned)locedgelist ; j++) { // We are renaming vertices, not edges if (startInd <= DEL.edges[j] && DEL.edges[j] < (startInd + permsize) && !renamed[j]) { DEL.edges[j] = localPerm[DEL.edges[j]-startInd]; renamed[j] = true; } } startInd += permsize; delete [] localPerm; } delete [] renamed; } } CombBLAS_beta_16_2/include/CombBLAS/ThreadedFriends.h000644 000765 000024 00000014676 13271404146 023615 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _THREADED_FRIENDS_H_ #define _THREADED_FRIENDS_H_ #include #include "SpMat.h" // Best to include the base class first #include "SpHelper.h" #include "StackEntry.h" #include "Isect.h" #include "Deleter.h" #include "SpImpl.h" #include "SpParHelper.h" #include "Compare.h" #include "CombBLAS.h" #include "PreAllocatedSPA.h" namespace combblas { template class SpTuples; template class SpDCCols; template class Dcsc; // multithreaded HeapSpGEMM template SpTuples * LocalSpGEMM (const SpDCCols & A, const SpDCCols & B, bool clearA, bool clearB) { IT mdim = A.getnrow(); IT ndim = B.getncol(); IT nnzA = A.getnnz(); if(A.isZero() || B.isZero()) { return new SpTuples(0, mdim, ndim); } Dcsc* Adcsc = A.GetDCSC(); Dcsc* Bdcsc = B.GetDCSC(); IT nA = A.getncol(); IT cnzmax = Adcsc->nz + Bdcsc->nz; // estimate on the size of resulting matrix C float cf = static_cast(nA+1) / static_cast(Adcsc->nzc); IT csize = static_cast(ceil(cf)); // chunk size IT * aux; Adcsc->ConstructAux(nA, aux); int numThreads = 1; // default case #ifdef THREADED #pragma omp parallel { numThreads = omp_get_num_threads(); } #endif IT* colnnzC = estimateNNZ(A, B); IT* colptrC = prefixsum(colnnzC, Bdcsc->nzc, numThreads); delete [] colnnzC; IT nnzc = colptrC[Bdcsc->nzc]; std::tuple * tuplesC = static_cast *> (::operator new (sizeof(std::tuple[nnzc]))); // thread private space for heap and colinds std::vector>> colindsVec(numThreads); std::vector>> globalheapVec(numThreads); for(int i=0; inzc; ++i) { IT nnzcolB = Bdcsc->cp[i+1] - Bdcsc->cp[i]; //nnz in the current column of B int myThread = omp_get_thread_num(); if(colindsVec[myThread].size() < nnzcolB) //resize thread private vectors if needed { colindsVec[myThread].resize(nnzcolB); globalheapVec[myThread].resize(nnzcolB); } // colinds.first vector keeps indices to A.cp, i.e. it dereferences "colnums" vector (above), // colinds.second vector keeps the end indices (i.e. it gives the index to the last valid element of A.cpnack) Adcsc->FillColInds(Bdcsc->ir + Bdcsc->cp[i], nnzcolB, colindsVec[myThread], aux, csize); std::pair * colinds = colindsVec[myThread].data(); HeapEntry * wset = globalheapVec[myThread].data(); IT hsize = 0; for(IT j = 0; (unsigned)j < nnzcolB; ++j) // create the initial heap { if(colinds[j].first != colinds[j].second) // current != end { wset[hsize++] = HeapEntry< IT,NT1 > (Adcsc->ir[colinds[j].first], j, Adcsc->numx[colinds[j].first]); } } std:make_heap(wset, wset+hsize); IT curptr = colptrC[i]; while(hsize > 0) { std::pop_heap(wset, wset + hsize); // result is stored in wset[hsize-1] IT locb = wset[hsize-1].runr; // relative location of the nonzero in B's current column NTO mrhs = SR::multiply(wset[hsize-1].num, Bdcsc->numx[Bdcsc->cp[i]+locb]); if (!SR::returnedSAID()) { if( (curptr > colptrC[i]) && std::get<0>(tuplesC[curptr-1]) == wset[hsize-1].key) { std::get<2>(tuplesC[curptr-1]) = SR::add(std::get<2>(tuplesC[curptr-1]), mrhs); } else { tuplesC[curptr++]= std::make_tuple(wset[hsize-1].key, Bdcsc->jc[i], mrhs) ; } } if( (++(colinds[locb].first)) != colinds[locb].second) // current != end { // runr stays the same ! wset[hsize-1].key = Adcsc->ir[colinds[locb].first]; wset[hsize-1].num = Adcsc->numx[colinds[locb].first]; std::push_heap(wset, wset+hsize); } else { --hsize; } } } if(clearA) delete const_cast *>(&A); if(clearB) delete const_cast *>(&B); delete [] colptrC; delete [] aux; SpTuples* spTuplesC = new SpTuples (nnzc, mdim, ndim, tuplesC, true); return spTuplesC; } } #endif CombBLAS_beta_16_2/include/CombBLAS/BFSFriends.h000644 000765 000024 00000050112 13271404146 022470 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.4 -------------------------------------------------*/ /* date: 1/17/2014 ---------------------------------------------*/ /* authors: Aydin Buluc (abuluc@lbl.gov), Adam Lugowski --------*/ /****************************************************************/ /* Copyright (c) 2010-2014, The Regents of the University of California 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. */ #ifndef _BFS_FRIENDS_H_ #define _BFS_FRIENDS_H_ #include "mpi.h" #include #include "SpParMat.h" #include "SpParHelper.h" #include "MPIType.h" #include "Friends.h" #include "OptBuf.h" #include "ParFriends.h" #include "BitMap.h" #include "BitMapCarousel.h" #include "BitMapFringe.h" namespace combblas { template class SpParMat; /*************************************************************************************************/ /*********************** FRIEND FUNCTIONS FOR BFS ONLY (NO SEMIRINGS) RUNS **********************/ /***************************** BOTH PARALLEL AND SEQUENTIAL FUNCTIONS ****************************/ /*************************************************************************************************/ /** * Multithreaded SpMV with sparse vector and preset buffers * the assembly of outgoing buffers sendindbuf/sendnumbuf are done here */ template void dcsc_gespmv_threaded_setbuffers (const SpDCCols & A, const int32_t * indx, const VT * numx, int32_t nnzx, int32_t * sendindbuf, VT * sendnumbuf, int * cnts, int * dspls, int p_c) { Select2ndSRing BFSsring; if(A.getnnz() > 0 && nnzx > 0) { int splits = A.getnsplit(); if(splits > 0) { std::vector< std::vector > indy(splits); std::vector< std::vector< VT > > numy(splits); int32_t nlocrows = static_cast(A.getnrow()); int32_t perpiece = nlocrows / splits; #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i(*(A.GetDCSC(i)), perpiece, indx, numx, nnzx, indy[i], numy[i], i*perpiece); else SpMXSpV_ForThreading(*(A.GetDCSC(i)), nlocrows - perpiece*i, indx, numx, nnzx, indy[i], numy[i], i*perpiece); } int32_t perproc = nlocrows / p_c; int32_t last_rec = p_c-1; // keep recipients of last entries in each split (-1 for an empty split) // so that we can delete indy[] and numy[] contents as soon as they are processed std::vector end_recs(splits); for(int i=0; i::iterator it = indy[i].begin(); it != indy[i].end(); ++it) { if( ( (*it) >= lastdata ) && cur_rec != last_rec) { cur_rec = std::min( (*it) / perproc, last_rec); lastdata = (cur_rec+1) * perproc; } ++loc_rec_cnts[i][cur_rec]; } } } #ifdef _OPENMP #pragma omp parallel for #endif for(int i=0; i= 0; before--) alreadysent += loc_rec_cnts[before][beg_rec]; if(beg_rec == end_recs[i]) // fast case { std::transform(indy[i].begin(), indy[i].end(), indy[i].begin(), std::bind2nd(std::minus(), perproc*beg_rec)); std::copy(indy[i].begin(), indy[i].end(), sendindbuf + dspls[beg_rec] + alreadysent); std::copy(numy[i].begin(), numy[i].end(), sendnumbuf + dspls[beg_rec] + alreadysent); } else // slow case { int32_t cur_rec = beg_rec; int32_t lastdata = (cur_rec+1) * perproc; // one past last entry that goes to this current recipient for(typename std::vector::iterator it = indy[i].begin(); it != indy[i].end(); ++it) { if( ( (*it) >= lastdata ) && cur_rec != last_rec ) { cur_rec = std::min( (*it) / perproc, last_rec); lastdata = (cur_rec+1) * perproc; // if this split switches to a new recipient after sending some data // then it's sure that no data has been sent to that recipient yet alreadysent = 0; } sendindbuf[ dspls[cur_rec] + alreadysent ] = (*it) - perproc*cur_rec; // convert to receiver's local index sendnumbuf[ dspls[cur_rec] + (alreadysent++) ] = *(numy[i].begin() + (it-indy[i].begin())); } } } } // Deallocated rec counts serially once all threads complete for(int i=0; i< splits; ++i) { for(int j=0; j< p_c; ++j) cnts[j] += loc_rec_cnts[i][j]; delete [] loc_rec_cnts[i]; } delete [] loc_rec_cnts; } else { std::cout << "Something is wrong, splits should be nonzero for multithreaded execution" << std::endl; } } } /** * Step 3 of the sparse SpMV algorithm, without the semiring (BFS only) * @param[in,out] optbuf {scratch space for all-to-all (fold) communication} * @param[in,out] indacc, numacc {index and values of the input vector, deleted upon exit} * @param[in,out] sendindbuf, sendnumbuf {index and values of the output vector, created} **/ template void LocalSpMV(const SpParMat & A, int rowneighs, OptBuf & optbuf, int32_t * & indacc, VT * & numacc, int * sendcnt, int accnz) { #ifdef TIMING double t0=MPI_Wtime(); #endif if(optbuf.totmax > 0) // graph500 optimization enabled { if(A.spSeq->getnsplit() > 0) { // optbuf.{inds/nums/dspls} and sendcnt are all pre-allocated and only filled by dcsc_gespmv_threaded generic_gespmv_threaded_setbuffers< Select2ndSRing > (*(A.spSeq), indacc, numacc, (int32_t) accnz, optbuf.inds, optbuf.nums, sendcnt, optbuf.dspls, rowneighs); } else { // by-pass dcsc_gespmv call if(A.getlocalnnz() > 0 && accnz > 0) { // ABAB: ignoring optbuf.isthere here // \TODO: Remove .isthere from optbuf definition SpMXSpV< Select2ndSRing >(*((A.spSeq)->GetInternal()), (int32_t) A.getlocalrows(), indacc, numacc, accnz, optbuf.inds, optbuf.nums, sendcnt, optbuf.dspls, rowneighs); } } DeleteAll(indacc,numacc); } else { SpParHelper::Print("BFS only (no semiring) function only work with optimization buffers\n"); } #ifdef TIMING double t1=MPI_Wtime(); cblas_localspmvtime += (t1-t0); #endif } template void MergeContributions(FullyDistSpVec & y, int * & recvcnt, int * & rdispls, int32_t * & recvindbuf, VT * & recvnumbuf, int rowneighs) { #ifdef TIMING double t0=MPI_Wtime(); #endif // free memory of y, in case it was aliased std::vector().swap(y.ind); std::vector().swap(y.num); #ifndef HEAPMERGE IU ysize = y.MyLocLength(); // my local length is only O(n/p) bool * isthere = new bool[ysize]; std::vector< std::pair > ts_pairs; std::fill_n(isthere, ysize, false); // We don't need to keep a "merger" because minimum will always come from the processor // with the smallest rank; so a linear sweep over the received buffer is enough for(int i=0; i::min(); int32_t sup = std::numeric_limits::max(); KNHeap< int32_t, int32_t > sHeap(sup, inf); int * processed = new int[rowneighs](); for(int32_t i=0; i 0) { // key, proc_id sHeap.insert(recvindbuf[rdispls[i]], i); ++hsize; } } int32_t key, locv; if(hsize > 0) { sHeap.deleteMin(&key, &locv); y.ind.push_back( static_cast(key)); y.num.push_back(recvnumbuf[rdispls[locv]]); // nothing is processed yet if( (++(processed[locv])) < recvcnt[locv] ) sHeap.insert(recvindbuf[rdispls[locv]+processed[locv]], locv); else --hsize; } // ofstream oput; // y.commGrid->OpenDebugFile("Merge", oput); // oput << "From displacements: "; copy(rdispls, rdispls+rowneighs, ostream_iterator(oput, " ")); oput << endl; // oput << "From counts: "; copy(recvcnt, recvcnt+rowneighs, ostream_iterator(oput, " ")); oput << endl; while(hsize > 0) { sHeap.deleteMin(&key, &locv); IU deref = rdispls[locv] + processed[locv]; if(y.ind.back() != static_cast(key)) // y.ind is surely not empty { y.ind.push_back(static_cast(key)); y.num.push_back(recvnumbuf[deref]); } if( (++(processed[locv])) < recvcnt[locv] ) sHeap.insert(recvindbuf[rdispls[locv]+processed[locv]], locv); else --hsize; } DeleteAll(recvcnt, rdispls,processed); DeleteAll(recvindbuf, recvnumbuf); #endif #ifdef TIMING double t1=MPI_Wtime(); cblas_mergeconttime += (t1-t0); #endif } /** * This is essentially a SpMV for BFS because it lacks the semiring. * It naturally justs selects columns of A (adjacencies of frontier) and * merges with the minimum entry succeeding. SpParMat has to be boolean * input and output vectors are of type VT but their indices are IT */ template FullyDistSpVec SpMV (const SpParMat & A, const FullyDistSpVec & x, OptBuf & optbuf) { CheckSpMVCompliance(A,x); optbuf.MarkEmpty(); MPI_Comm World = x.commGrid->GetWorld(); MPI_Comm ColWorld = x.commGrid->GetColWorld(); MPI_Comm RowWorld = x.commGrid->GetRowWorld(); int accnz; int32_t trxlocnz; IT lenuntil; int32_t *trxinds, *indacc; VT *trxnums, *numacc; #ifdef TIMING double t0=MPI_Wtime(); #endif TransposeVector(World, x, trxlocnz, lenuntil, trxinds, trxnums, true); // trxinds (and potentially trxnums) is allocated #ifdef TIMING double t1=MPI_Wtime(); cblas_transvectime += (t1-t0); #endif AllGatherVector(ColWorld, trxlocnz, lenuntil, trxinds, trxnums, indacc, numacc, accnz, true); // trxinds (and potentially trxnums) is deallocated, indacc/numacc allocated FullyDistSpVec y ( x.commGrid, A.getnrow()); // identity doesn't matter for sparse vectors int rowneighs; MPI_Comm_size(RowWorld,&rowneighs); int * sendcnt = new int[rowneighs](); LocalSpMV(A, rowneighs, optbuf, indacc, numacc, sendcnt, accnz); // indacc/numacc deallocated int * rdispls = new int[rowneighs]; int * recvcnt = new int[rowneighs]; MPI_Alltoall(sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, RowWorld); // share the request counts // receive displacements are exact whereas send displacements have slack rdispls[0] = 0; for(int i=0; i 0 ) // graph500 optimization enabled { MPI_Alltoallv(optbuf.inds, sendcnt, optbuf.dspls, MPIType(), recvindbuf, recvcnt, rdispls, MPIType(), RowWorld); MPI_Alltoallv(optbuf.nums, sendcnt, optbuf.dspls, MPIType(), recvnumbuf, recvcnt, rdispls, MPIType(), RowWorld); delete [] sendcnt; } else { SpParHelper::Print("BFS only (no semiring) function only work with optimization buffers\n"); } #ifdef TIMING double t3=MPI_Wtime(); cblas_alltoalltime += (t3-t2); #endif MergeContributions(y,recvcnt, rdispls, recvindbuf, recvnumbuf, rowneighs); return y; } template SpDCCols::SpColIter* CalcSubStarts(SpParMat & A, FullyDistSpVec & x, BitMapCarousel &done) { std::shared_ptr cg = A.getcommgrid(); IT rowuntil = x.LengthUntil(); MPI_Comm RowWorld = cg->GetRowWorld(); MPI_Bcast(&rowuntil, 1, MPIType(), 0, RowWorld); int numcols = cg->GetGridCols(); SpDCCols::SpColIter colit = A.seq().begcol(); #ifdef THREADED SpDCCols::SpColIter* starts = new SpDCCols::SpColIter[numcols*cblas_splits+1]; for(int c=0; c::SpColIter* starts = new SpDCCols::SpColIter[numcols+1]; for(int c=0; c void UpdateParents(MPI_Comm & RowWorld, std::pair *updates, int num_updates, FullyDistVec &parents, int source, int dest, BitMapFringe &bm_fringe) { int send_words = num_updates<<1, recv_words; MPI_Status status; MPI_Sendrecv(&send_words, 1, MPI_INT, dest, PUPSIZE, &recv_words, 1, MPI_INT, source, PUPSIZE, RowWorld, &status); std::pair* recv_buff = new std::pair[recv_words>>1]; MPI_Sendrecv(updates, send_words, MPIType(), dest, PUPDATA, recv_buff, recv_words, MPIType(), source, PUPDATA, RowWorld, &status); #ifdef THREADED #pragma omp parallel for #endif for (int i=0; i>1; i++) { parents.SetLocalElement(recv_buff[i].first, recv_buff[i].second); } bm_fringe.IncrementNumSet((recv_words>>1)); delete[] recv_buff; } template void BottomUpStep(SpParMat & A, FullyDistSpVec & x, BitMapFringe &bm_fringe, FullyDistVec & parents, BitMapCarousel &done, SpDCCols::SpColIter* starts) { std::shared_ptr cg = A.getcommgrid(); MPI_Comm World = cg->GetWorld(); MPI_Comm ColWorld = cg->GetColWorld(); MPI_Comm RowWorld = cg->GetRowWorld(); MPI_Status status; // get row and column offsets IT rowuntil = x.LengthUntil(), my_coluntil = x.LengthUntil(), coluntil; int diagneigh = cg->GetComplementRank(); MPI_Sendrecv(&my_coluntil, 1, MPIType(), diagneigh, TROST, &coluntil, 1, MPIType(), diagneigh, TROST, World, &status); MPI_Bcast(&coluntil, 1, MPIType(), 0, ColWorld); MPI_Bcast(&rowuntil, 1, MPIType(), 0, RowWorld); BitMap* frontier = bm_fringe.TransposeGather(); done.SaveOld(); #ifdef THREADED const int buff_size = 8192; std::pair* local_update_heads[cblas_splits]; for (int t=0; t[buff_size]; #endif // do bottom up work int numcols = cg->GetGridCols(); int mycol = cg->GetRankInProcRow(); std::pair* parent_updates = new std::pair[done.SizeOfChunk()<<1]; // over-allocated for (int sub_step=0; sub_step::SpColIter::NzIter nzit, nzit_end; SpDCCols::SpColIter colit, colit_end; std::pair* local_updates = local_update_heads[id]; // vector > local_updates; colit_end = starts[dest_slice*cblas_splits + id + 1]; for(colit = starts[dest_slice*cblas_splits + id]; colit != colit_end; ++colit) { int32_t local_row_ind = colit.colid(); IT row = local_row_ind + rowuntil; if (!done.GetBit(row)) { nzit_end = A.seq().endnz(colit); for(nzit = A.seq().begnz(colit); nzit != nzit_end; ++nzit) { int32_t local_col_ind = nzit.rowid(); IT col = local_col_ind + coluntil; if (frontier->get_bit(local_col_ind)) { // local_updates.push_back(make_pair(row-sub_start, col)); if (num_locals == buff_size) { int copy_start = __sync_fetch_and_add(&num_updates, buff_size); std::copy(local_updates, local_updates + buff_size, parent_updates + copy_start); num_locals = 0; } local_updates[num_locals++] = std::make_pair(row-sub_start, col); done.SetBit(row); break; } } } } int copy_start = __sync_fetch_and_add(&num_updates, num_locals); std::copy(local_updates, local_updates + num_locals, parent_updates + copy_start); } #else SpDCCols::SpColIter::NzIter nzit, nzit_end; SpDCCols::SpColIter colit, colit_end; colit_end = starts[dest_slice+1]; for(colit = starts[dest_slice]; colit != colit_end; ++colit) { int32_t local_row_ind = colit.colid(); IT row = local_row_ind + rowuntil; if (!done.GetBit(row)) { nzit_end = A.seq().endnz(colit); for(nzit = A.seq().begnz(colit); nzit != nzit_end; ++nzit) { int32_t local_col_ind = nzit.rowid(); IT col = local_col_ind + coluntil; if (frontier->get_bit(local_col_ind)) { parent_updates[num_updates++] = std::make_pair(row-sub_start, col); done.SetBit(row); break; } } // end_for } // end_if } // end_for #endif #ifdef BOTTOMUPTIME double t2 = MPI_Wtime(); bu_local += (t2-t1); t1 = MPI_Wtime(); #endif done.RotateAlongRow(); #ifdef BOTTOMUPTIME t2 = MPI_Wtime(); bu_rotate += (t2-t1); t1 = MPI_Wtime(); #endif UpdateParents(RowWorld, parent_updates, num_updates, parents, source_slice, dest_slice, bm_fringe); #ifdef BOTTOMUPTIME t2 = MPI_Wtime(); bu_update += (t2-t1); #endif } bm_fringe.LoadFromNext(); done.UpdateFringe(bm_fringe); #ifdef THREADED for (int t=0; t #include #include #include #include #include // TR1 includes belong in CombBLAS.h #include "SpMat.h" #include "SpTuples.h" #include "SpDCCols.h" #include "CommGrid.h" #include "MPIType.h" #include "LocArr.h" #include "SpDefs.h" #include "Deleter.h" #include "SpHelper.h" #include "SpParHelper.h" #include "FullyDistVec.h" #include "Friends.h" #include "Operations.h" namespace combblas { template class SemanticGraph { public: SemanticGraph(IT total_m, IT total_n, const FullyDistVec & , const FullyDistVec & , const FullyDistVec & ); // matlab sparse typename typedef SpParMat < IT, NT, SpDCCols > PSpMat; // TODO: Convert to 32-bit local indices typename typedef FullyDistVec PVec; private: PSpMat SemMat; PVec SemVec; } } #endif CombBLAS_beta_16_2/include/CombBLAS/SpHelper.h000644 000765 000024 00000044361 13271404146 022276 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #ifndef _SP_HELPER_H_ #define _SP_HELPER_H_ #include #include #include #include #include #include "SpDefs.h" #include "StackEntry.h" #include "promote.h" #include "Isect.h" #include "HeapEntry.h" #include "SpImpl.h" #include "hash.hpp" namespace combblas { template class Dcsc; class SpHelper { public: template static std::vector find_order(const std::vector & values) { size_t index = 0; std::vector< std::pair > tosort; for(auto & val: values) { tosort.push_back(std::make_pair(val,index++)); } sort(tosort.begin(), tosort.end()); std::vector permutation; for(auto & sorted: tosort) { permutation.push_back(sorted.second); } return permutation; } template static void push_to_vectors(std::vector & rows, std::vector & cols, std::vector & vals, IT2 ii, IT2 jj, NT2 vv, int symmetric, bool onebased = true) { if(onebased) { ii--; /* adjust from 1-based to 0-based */ jj--; } rows.push_back(ii); cols.push_back(jj); vals.push_back(vv); if(symmetric && ii != jj) { rows.push_back(jj); cols.push_back(ii); vals.push_back(vv); } } static void ProcessLinesWithStringKeys(std::vector< std::map < std::string, uint64_t> > & allkeys, std::vector & lines, int nprocs) { std::string frstr, tostr; uint64_t frhash, tohash; double vv; for (auto itr=lines.begin(); itr != lines.end(); ++itr) { char fr[MAXVERTNAME]; char to[MAXVERTNAME]; sscanf(itr->c_str(), "%s %s %lg", fr, to, &vv); frstr = std::string(fr); tostr = std::string(to); MurmurHash3_x64_64(frstr.c_str(),frstr.size(),0,&frhash); MurmurHash3_x64_64(tostr.c_str(),tostr.size(),0,&tohash); double range_fr = static_cast(frhash) * static_cast(nprocs); double range_to = static_cast(tohash) * static_cast(nprocs); size_t owner_fr = range_fr / static_cast(std::numeric_limits::max()); size_t owner_to = range_to / static_cast(std::numeric_limits::max()); // cout << frstr << " with hash " << frhash << " is going to " << owner_fr << endl; // cout << tostr << " with hash " << tohash << " is going to " << owner_to << endl; allkeys[owner_fr].insert(std::make_pair(frstr, frhash)); allkeys[owner_to].insert(std::make_pair(tostr, tohash)); } lines.clear(); } template static void ProcessStrLinesNPermute(std::vector & rows, std::vector & cols, std::vector & vals, std::vector & lines, std::map & ultperm) { char * fr = new char[MAXVERTNAME]; char * to = new char[MAXVERTNAME]; std::string frstr, tostr; double vv; for (auto itr=lines.begin(); itr != lines.end(); ++itr) { sscanf(itr->c_str(), "%s %s %lg", fr, to, &vv); frstr = std::string(fr); tostr = std::string(to); rows.emplace_back((IT1) ultperm[frstr]); cols.emplace_back((IT1) ultperm[tostr]); vals.emplace_back((NT1) vv); } delete [] fr; delete [] to; lines.clear(); } template static void ProcessLines(std::vector & rows, std::vector & cols, std::vector & vals, std::vector & lines, int symmetric, int type, bool onebased = true) { if(type == 0) // real { int64_t ii, jj; double vv; for (auto itr=lines.begin(); itr != lines.end(); ++itr) { // string::c_str() -> Returns a pointer to an array that contains a null-terminated sequence of characters (i.e., a C-string) sscanf(itr->c_str(), "%lld %lld %lg", &ii, &jj, &vv); SpHelper::push_to_vectors(rows, cols, vals, ii, jj, vv, symmetric, onebased); } } else if(type == 1) // integer { int64_t ii, jj, vv; for (auto itr=lines.begin(); itr != lines.end(); ++itr) { sscanf(itr->c_str(), "%lld %lld %lld", &ii, &jj, &vv); SpHelper::push_to_vectors(rows, cols, vals, ii, jj, vv, symmetric, onebased); } } else if(type == 2) // pattern { int64_t ii, jj; for (auto itr=lines.begin(); itr != lines.end(); ++itr) { sscanf(itr->c_str(), "%lld %lld", &ii, &jj); SpHelper::push_to_vectors(rows, cols, vals, ii, jj, 1, symmetric, onebased); } } else { std::cout << "COMBBLAS: Unrecognized matrix market scalar type" << std::endl; } lines.clear(); } template static const T * p2a (const std::vector & v) // pointer to array { if(v.empty()) return NULL; else return (&v[0]); } template static T * p2a (std::vector & v) // pointer to array { if(v.empty()) return NULL; else return (&v[0]); } template static bool is_sorted(_ForwardIterator __first, _ForwardIterator __last) { if (__first == __last) return true; _ForwardIterator __next = __first; for (++__next; __next != __last; __first = __next, ++__next) if (*__next < *__first) return false; return true; } template static bool is_sorted(_ForwardIterator __first, _ForwardIterator __last, _StrictWeakOrdering __comp) { if (__first == __last) return true; _ForwardIterator __next = __first; for (++__next; __next != __last; __first = __next, ++__next) if (__comp(*__next, *__first)) return false; return true; } template static void iota(_ForwardIter __first, _ForwardIter __last, T __val) { while (__first != __last) *__first++ = __val++; } template static Out copyIf(In first, In last, Out result, UnPred pred) { for ( ;first != last; ++first) if (pred(*first)) *result++ = *first; return(result); } template static T ** allocate2D(I1 m, I2 n) { T ** array = new T*[m]; for(I1 i = 0; i static void deallocate2D(T ** array, I m) { for(I i = 0; i static IT Popping(NT1 * numA, NT2 * numB, StackEntry< OVT, std::pair > * multstack, IT & cnz, KNHeap< std::pair , IT > & sHeap, Isect * isect1, Isect * isect2); template static void SpIntersect(const Dcsc & Adcsc, const Dcsc & Bdcsc, Isect* & cols, Isect* & rows, Isect* & isect1, Isect* & isect2, Isect* & itr1, Isect* & itr2); template static IT SpCartesian(const Dcsc & Adcsc, const Dcsc & Bdcsc, IT kisect, Isect * isect1, Isect * isect2, StackEntry< OVT, std::pair > * & multstack); template static IT SpColByCol(const Dcsc & Adcsc, const Dcsc & Bdcsc, IT nA, StackEntry< OVT, std::pair > * & multstack); template static void ShrinkArray(NT * & array, IT newsize) { NT * narray = new NT[newsize]; memcpy(narray, array, newsize*sizeof(NT)); // copy only a portion of the old elements delete [] array; array = narray; } template static void DoubleStack(StackEntry > * & multstack, IT & cnzmax, IT add) { StackEntry > * tmpstack = multstack; multstack = new StackEntry >[2* cnzmax + add]; memcpy(multstack, tmpstack, sizeof(StackEntry >) * cnzmax); cnzmax = 2*cnzmax + add; delete [] tmpstack; } template static bool first_compare(std::pair pair1, std::pair pair2) { return pair1.first < pair2.first; } }; /** * Pop an element, do the numerical semiring multiplication & insert the result into multstack */ template IT SpHelper::Popping(NT1 * numA, NT2 * numB, StackEntry< OVT, std::pair > * multstack, IT & cnz, KNHeap< std::pair,IT > & sHeap, Isect * isect1, Isect * isect2) { std::pair key; IT inc; sHeap.deleteMin(&key, &inc); OVT value = SR::multiply(numA[isect1[inc].current], numB[isect2[inc].current]); if (!SR::returnedSAID()) { if(cnz != 0) { if(multstack[cnz-1].key == key) // already exists { multstack[cnz-1].value = SR::add(multstack[cnz-1].value, value); } else { multstack[cnz].value = value; multstack[cnz].key = key; ++cnz; } } else { multstack[cnz].value = value; multstack[cnz].key = key; ++cnz; } } return inc; } /** * Finds the intersecting row indices of Adcsc and col indices of Bdcsc * @param[IT] Bdcsc {the transpose of the dcsc structure of matrix B} * @param[IT] Adcsc {the dcsc structure of matrix A} **/ template void SpHelper::SpIntersect(const Dcsc & Adcsc, const Dcsc & Bdcsc, Isect* & cols, Isect* & rows, Isect* & isect1, Isect* & isect2, Isect* & itr1, Isect* & itr2) { cols = new Isect[Adcsc.nzc]; rows = new Isect[Bdcsc.nzc]; for(IT i=0; i < Adcsc.nzc; ++i) { cols[i].index = Adcsc.jc[i]; // column index cols[i].size = Adcsc.cp[i+1] - Adcsc.cp[i]; cols[i].start = Adcsc.cp[i]; // pointer to row indices cols[i].current = Adcsc.cp[i]; // pointer to row indices } for(IT i=0; i < Bdcsc.nzc; ++i) { rows[i].index = Bdcsc.jc[i]; // column index rows[i].size = Bdcsc.cp[i+1] - Bdcsc.cp[i]; rows[i].start = Bdcsc.cp[i]; // pointer to row indices rows[i].current = Bdcsc.cp[i]; // pointer to row indices } /* A single set_intersection would only return the elements of one sequence * But we also want random access to the other array's elements * Thus we do the intersection twice */ IT mink = std::min(Adcsc.nzc, Bdcsc.nzc); isect1 = new Isect[mink]; // at most isect2 = new Isect[mink]; // at most itr1 = std::set_intersection(cols, cols + Adcsc.nzc, rows, rows + Bdcsc.nzc, isect1); itr2 = std::set_intersection(rows, rows + Bdcsc.nzc, cols, cols + Adcsc.nzc, isect2); // itr1 & itr2 are now pointing to one past the end of output sequences } /** * Performs cartesian product on the dcsc structures. * Indices to perform the product are given by isect1 and isect2 arrays * Returns the "actual" number of elements in the merged stack * Bdcsc is "already transposed" (i.e. Bdcsc->ir gives column indices, and Bdcsc->jc gives row indices) **/ template IT SpHelper::SpCartesian(const Dcsc & Adcsc, const Dcsc & Bdcsc, IT kisect, Isect * isect1, Isect * isect2, StackEntry< OVT, std::pair > * & multstack) { std::pair supremum(std::numeric_limits::max(), std::numeric_limits::max()); std::pair infimum (std::numeric_limits::min(), std::numeric_limits::min()); KNHeap< std::pair , IT > sHeapDcsc(supremum, infimum); // Create a sequence heap that will eventually construct DCSC of C // The key to sort is pair so that output is in column-major order for(IT i=0; i< kisect; ++i) { std::pair key(Bdcsc.ir[isect2[i].current], Adcsc.ir[isect1[i].current]); sHeapDcsc.insert(key, i); } IT cnz = 0; IT cnzmax = Adcsc.nz + Bdcsc.nz; // estimate on the size of resulting matrix C multstack = new StackEntry< OVT, std::pair > [cnzmax]; bool finished = false; while(!finished) // multiplication loop (complexity O(flops * log (kisect)) { finished = true; if (cnz + kisect > cnzmax) // double the size of multstack { DoubleStack(multstack, cnzmax, kisect); } // inc: the list to increment its pointer in the k-list merging IT inc = Popping< SR >(Adcsc.numx, Bdcsc.numx, multstack, cnz, sHeapDcsc, isect1, isect2); isect1[inc].current++; if(isect1[inc].current < isect1[inc].size + isect1[inc].start) { std::pair key(Bdcsc.ir[isect2[inc].current], Adcsc.ir[isect1[inc].current]); sHeapDcsc.insert(key, inc); // push the same element with a different key [increasekey] finished = false; } // No room to go in isect1[], but there is still room to go in isect2[i] else if(isect2[inc].current + 1 < isect2[inc].size + isect2[inc].start) { isect1[inc].current = isect1[inc].start; // wrap-around isect2[inc].current++; std::pair key(Bdcsc.ir[isect2[inc].current], Adcsc.ir[isect1[inc].current]); sHeapDcsc.insert(key, inc); // push the same element with a different key [increasekey] finished = false; } else // don't push, one of the lists has been deplated { kisect--; if(kisect != 0) { finished = false; } } } return cnz; } template IT SpHelper::SpColByCol(const Dcsc & Adcsc, const Dcsc & Bdcsc, IT nA, StackEntry< OVT, std::pair > * & multstack) { IT cnz = 0; IT cnzmax = Adcsc.nz + Bdcsc.nz; // estimate on the size of resulting matrix C multstack = new StackEntry >[cnzmax]; float cf = static_cast(nA+1) / static_cast(Adcsc.nzc); IT csize = static_cast(ceil(cf)); // chunk size IT * aux; //IT auxsize = Adcsc.ConstructAux(nA, aux); Adcsc.ConstructAux(nA, aux); for(IT i=0; i< Bdcsc.nzc; ++i) // for all the columns of B { IT prevcnz = cnz; IT nnzcol = Bdcsc.cp[i+1] - Bdcsc.cp[i]; HeapEntry * wset = new HeapEntry[nnzcol]; // heap keys are just row indices (IT) // heap values are // heap size is nnz(B(:,i) // colnums vector keeps column numbers requested from A std::vector colnums(nnzcol); // colinds.first vector keeps indices to A.cp, i.e. it dereferences "colnums" vector (above), // colinds.second vector keeps the end indices (i.e. it gives the index to the last valid element of A.cpnack) std::vector< std::pair > colinds(nnzcol); std::copy(Bdcsc.ir + Bdcsc.cp[i], Bdcsc.ir + Bdcsc.cp[i+1], colnums.begin()); Adcsc.FillColInds(&colnums[0], colnums.size(), colinds, aux, csize); IT maxnnz = 0; // max number of nonzeros in C(:,i) IT hsize = 0; for(IT j = 0; (unsigned)j < colnums.size(); ++j) // create the initial heap { if(colinds[j].first != colinds[j].second) // current != end { wset[hsize++] = HeapEntry< IT,NT1 > (Adcsc.ir[colinds[j].first], j, Adcsc.numx[colinds[j].first]); maxnnz += colinds[j].second - colinds[j].first; } } std::make_heap(wset, wset+hsize); if (cnz + maxnnz > cnzmax) // double the size of multstack { SpHelper::DoubleStack(multstack, cnzmax, maxnnz); } // No need to keep redefining key and hentry with each iteration of the loop while(hsize > 0) { std::pop_heap(wset, wset + hsize); // result is stored in wset[hsize-1] IT locb = wset[hsize-1].runr; // relative location of the nonzero in B's current column // type promotion done here: // static T_promote multiply(const T1 & arg1, const T2 & arg2) // return (static_cast(arg1) * static_cast(arg2) ); OVT mrhs = SR::multiply(wset[hsize-1].num, Bdcsc.numx[Bdcsc.cp[i]+locb]); if (!SR::returnedSAID()) { if(cnz != prevcnz && multstack[cnz-1].key.second == wset[hsize-1].key) // if (cnz == prevcnz) => first nonzero for this column { multstack[cnz-1].value = SR::add(multstack[cnz-1].value, mrhs); } else { multstack[cnz].value = mrhs; multstack[cnz++].key = std::make_pair(Bdcsc.jc[i], wset[hsize-1].key); // first entry is the column index, as it is in column-major order } } if( (++(colinds[locb].first)) != colinds[locb].second) // current != end { // runr stays the same ! wset[hsize-1].key = Adcsc.ir[colinds[locb].first]; wset[hsize-1].num = Adcsc.numx[colinds[locb].first]; std::push_heap(wset, wset+hsize); } else { --hsize; } } delete [] wset; } delete [] aux; return cnz; } } #endif CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/knheap.h000644 000765 000024 00000022346 13271404146 024552 0ustar00aydinbulucstaff000000 000000 // hierarchical memory priority queue data structure #ifndef KNHEAP #define KNHEAP #include "util.h" const int KNBufferSize1 = 32; // equalize procedure call overheads etc. const int KNN = 128; // bandwidth (also size of the binary heap) const int KNKMAX = 128; // maximal arity const int KNLevels = 4; // overall capacity >= KNN*KNKMAX^KNLevels const int LogKNKMAX = 7; // ceil(log KNK) /* const int KNBufferSize1 = 3; // equalize procedure call overheads etc. const int KNN = 10; // bandwidth const int KNKMAX = 4; // maximal arity const int KNLevels = 4; // overall capacity >= KNN*KNKMAX^KNLevels const int LogKNKMAX = 2; // ceil(log KNK) */ template struct KNElement {Key key; Value value;}; ////////////////////////////////////////////////////////////////////// // fixed size binary heap template class BinaryHeap { // static const Key infimum = 4; //static const Key supremum = numeric_limits.max(); typedef KNElement Element; Element data[capacity + 2]; int size; // index of last used element public: BinaryHeap(Key sup, Key infimum):size(0) { data[0].key = infimum; // sentinel data[capacity + 1].key = sup; reset(); } Key getSupremum() { return data[capacity + 1].key; } void reset(); int getSize() const { return size; } Key getMinKey() const { return data[1].key; } Value getMinValue() const { return data[1].value; } void deleteMin(); void deleteMinFancy(Key *key, Value *value) { *key = getMinKey(); *value = getMinValue(); deleteMin(); } void insert(Key k, Value v); void sortTo(Element *to); // sort in increasing order and empty //void sortInPlace(); // in decreasing order }; // reset size to 0 and fill data array with sentinels template inline void BinaryHeap:: reset() { size = 0; Key sup = getSupremum(); for (int i = 1; i <= capacity; i++) { data[i].key = sup; } // if this becomes a bottle neck // we might want to replace this by log KNN // memcpy-s } template inline void BinaryHeap:: deleteMin() { Assert2(size > 0); // first move up elements on a min-path int hole = 1; int succ = 2; int sz = size; while (succ < sz) { Key key1 = data[succ].key; Key key2 = data[succ + 1].key; if (key1 > key2) { succ++; data[hole].key = key2; data[hole].value = data[succ].value; } else { data[hole].key = key1; data[hole].value = data[succ].value; } hole = succ; succ <<= 1; } // bubble up rightmost element Key bubble = data[sz].key; int pred = hole >> 1; while (data[pred].key > bubble) { // must terminate since min at root data[hole] = data[pred]; hole = pred; pred >>= 1; } // finally move data to hole data[hole].key = bubble; data[hole].value = data[sz].value; data[size].key = getSupremum(); // mark as deleted size = sz - 1; } // empty the heap and put the element to "to" // sorted in increasing order template inline void BinaryHeap:: sortTo(Element *to) { const int sz = size; const Key sup = getSupremum(); Element * const beyond = to + sz; Element * const root = data + 1; while (to < beyond) { // copy minimun *to = *root; to++; // bubble up second smallest as in deleteMin int hole = 1; int succ = 2; while (succ <= sz) { Key key1 = data[succ ].key; Key key2 = data[succ + 1].key; if (key1 > key2) { succ++; data[hole].key = key2; data[hole].value = data[succ].value; } else { data[hole].key = key1; data[hole].value = data[succ].value; } hole = succ; succ <<= 1; } // just mark hole as deleted data[hole].key = sup; } size = 0; } template inline void BinaryHeap:: insert(Key k, Value v) { Assert2(size < capacity); Debug4(cout << "insert(" << k << ", " << v << ")" << endl); size++; int hole = size; int pred = hole >> 1; Key predKey = data[pred].key; while (predKey > k) { // must terminate due to sentinel at 0 data[hole].key = predKey; data[hole].value = data[pred].value; hole = pred; pred >>= 1; predKey = data[pred].key; } // finally move data to hole data[hole].key = k; data[hole].value = v; } ////////////////////////////////////////////////////////////////////// // The data structure from Knuth, "Sorting and Searching", Section 5.4.1 template class KNLooserTree { // public: // should not be here but then I would need a scary // sequence of template friends which I doubt to work // on all compilers typedef KNElement Element; struct Entry { Key key; // Key of Looser element (winner for 0) int index; // number of loosing segment }; // stack of empty segments int empty[KNKMAX]; // indices of empty segments int lastFree; // where in "empty" is the last valid entry? int size; // total number of elements stored int logK; // log of current tree size int k; // invariant k = 1 << logK Element dummy; // target of empty segment pointers // upper levels of looser trees // entry[0] contains the winner info Entry entry[KNKMAX]; // leaf information // note that Knuth uses indices k..k-1 // while we use 0..k-1 Element *current[KNKMAX]; // pointer to actual element Element *segment[KNKMAX]; // start of Segments // private member functions int initWinner(int root); void updateOnInsert(int node, Key newKey, int newIndex, Key *winnerKey, int *winnerIndex, int *mask); void deallocateSegment(int index); void doubleK(); void compactTree(); void rebuildLooserTree(); int segmentIsEmpty(int i); public: KNLooserTree(); void init(Key sup); // before, no consistent state is reached :-( void multiMergeUnrolled3(Element *to, int l); void multiMergeUnrolled4(Element *to, int l); void multiMergeUnrolled5(Element *to, int l); void multiMergeUnrolled6(Element *to, int l); void multiMergeUnrolled7(Element *to, int l); void multiMergeUnrolled8(Element *to, int l); void multiMergeUnrolled9(Element *to, int l); void multiMergeUnrolled10(Element *to, int l); void multiMerge(Element *to, int l); // delete l smallest element to "to" void multiMergeK(Element *to, int l); int spaceIsAvailable() { return k < KNKMAX || lastFree >= 0; } // for new segment void insertSegment(Element *to, int sz); // insert segment beginning at to int getSize() { return size; } Key getSupremum() { return dummy.key; } }; ////////////////////////////////////////////////////////////////////// // 2 level multi-merge tree template class KNHeap { typedef KNElement Element; KNLooserTree tree[KNLevels]; // one delete buffer for each tree (extra space for sentinel) Element buffer2[KNLevels][KNN + 1]; // tree->buffer2->buffer1 Element *minBuffer2[KNLevels]; // overall delete buffer Element buffer1[KNBufferSize1 + 1]; Element *minBuffer1; // insert buffer BinaryHeap insertHeap; // how many levels are active int activeLevels; // total size not counting insertBuffer and buffer1 int size; // private member functions void refillBuffer1(); void refillBuffer11(int sz); void refillBuffer12(int sz); void refillBuffer13(int sz); void refillBuffer14(int sz); int refillBuffer2(int k); int makeSpaceAvailable(int level); void emptyInsertHeap(); Key getSupremum() const { return buffer2[0][KNN].key; } int getSize1( ) const { return ( buffer1 + KNBufferSize1) - minBuffer1; } int getSize2(int i) const { return &(buffer2[i][KNN]) - minBuffer2[i]; } public: KNHeap(Key sup, Key infimum); int getSize() const; void getMin(Key *key, Value *value); void deleteMin(Key *key, Value *value); void insert(Key key, Value value); }; template inline int KNHeap::getSize() const { return size + insertHeap.getSize() + ((buffer1 + KNBufferSize1) - minBuffer1); } template inline void KNHeap::getMin(Key *key, Value *value) { Key key1 = minBuffer1->key; Key key2 = insertHeap.getMinKey(); if (key2 >= key1) { *key = key1; *value = minBuffer1->value; } else { *key = key2; *value = insertHeap.getMinValue(); } } template inline void KNHeap::deleteMin(Key *key, Value *value) { Key key1 = minBuffer1->key; Key key2 = insertHeap.getMinKey(); if (key2 >= key1) { *key = key1; *value = minBuffer1->value; Assert2(minBuffer1 < buffer1 + KNBufferSize1); // no delete from empty minBuffer1++; if (minBuffer1 == buffer1 + KNBufferSize1) { refillBuffer1(); } } else { *key = key2; *value = insertHeap.getMinValue(); insertHeap.deleteMin(); } } template inline void KNHeap::insert(Key k, Value v) { if (insertHeap.getSize() == KNN) { emptyInsertHeap(); } insertHeap.insert(k, v); } #endif CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/heap2.h000644 000765 000024 00000007616 13271404146 024306 0ustar00aydinbulucstaff000000 000000 // 4ary heap data structure #ifndef HEAP2 #define HEAP2 #include "util.h" template struct KNElement {Key key; Value value;}; // fixed size binary heap template class Heap2 { // static const Key infimum = 4; //static const Key supremum = numeric_limits.max(); typedef KNElement Element; Element *data; int capacity; int supremum; int size; // index of last used element public: Heap2(Key sup, Key infimum, int cap):size(0),capacity(cap) { data = new Element[cap + 2]; data[0].key = infimum; // sentinel data[capacity + 1].key = sup; supremum = sup; reset(); } ~Heap2() { delete data; } Key getSupremum() { return supremum; } void reset(); int getSize() const { return size; } Key getMinKey() const { return data[1].key; } Value getMinValue() const { return data[1].value; } void deleteMinBasic(); void deleteMin(Key *key, Value *value) { *key = getMinKey(); *value = getMinValue(); deleteMinBasic(); } void insert(Key k, Value v); void sortTo(Element *to); // sort in increasing order and empty //void sortInPlace(); // in decreasing order }; // reset size to 0 and fill data array with sentinels template inline void Heap2:: reset() { size = 0; Key sup = getSupremum(); int cap = capacity; for (int i = 1; i <= cap; i++) { data[i].key = sup; } // if this becomes a bottle neck // we might want to replace this by log KNN // memcpy-s } template inline void Heap2:: deleteMinBasic() { Assert2(size > 0); // first move up elements on a min-path int hole = 1; int succ = 2; int sz = size; Element *dat = data; while (succ < sz) { Key key1 = dat[succ].key; Key key2 = dat[succ + 1].key; if (key1 > key2) { succ++; dat[hole].key = key2; dat[hole].value = dat[succ].value; } else { dat[hole].key = key1; dat[hole].value = dat[succ].value; } hole = succ; succ <<= 1; } // bubble up rightmost element Key bubble = dat[sz].key; int pred = hole >> 1; while (dat[pred].key > bubble) { // must terminate since min at root dat[hole] = dat[pred]; hole = pred; pred >>= 1; } // finally move data to hole dat[hole].key = bubble; dat[hole].value = dat[sz].value; dat[size].key = getSupremum(); // mark as deleted size = sz - 1; } // empty the heap and put the element to "to" // sorted in increasing order template inline void Heap2:: sortTo(Element *to) { const int sz = size; const Key sup = getSupremum(); Element * const beyond = to + sz; Element * const root = data + 1; while (to < beyond) { // copy minimun *to = *root; to++; // bubble up second smallest as in deleteMin int hole = 1; int succ = 2; while (succ <= sz) { Key key1 = data[succ ].key; Key key2 = data[succ + 1].key; if (key1 > key2) { succ++; data[hole].key = key2; data[hole].value = data[succ].value; } else { data[hole].key = key1; data[hole].value = data[succ].value; } hole = succ; succ <<= 1; } // just mark hole as deleted data[hole].key = sup; } size = 0; } template inline void Heap2:: insert(Key k, Value v) { Assert2(size < capacity); Debug4(cout << "insert(" << k << ", " << v << ")" << endl); Element *dat = data; size++; int hole = size; int pred = hole >> 1; Key predKey = dat[pred].key; while (predKey > k) { // must terminate due to sentinel at 0 dat[hole].key = predKey; dat[hole].value = dat[pred].value; hole = pred; pred >>= 1; predKey = dat[pred].key; } // finally move data to hole dat[hole].key = k; dat[hole].value = v; } #endif CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/knupdown3.C000644 000765 000024 00000005345 13271404146 025167 0ustar00aydinbulucstaff000000 000000 // benchmark with (insert insert delMin)^N (insert delMin delMin)^N // just in time RNG // version 3: use easier to handle binary heaps #include #include #include #include #define DEBUGLEVEL 0 #include "util.h" #define KNH //#define H2 //#define H4 //#define HSLOW #ifdef KNH # include "knheap.C" # define HTYPE KNHeap # define HINIT heap(INT_MAX, -INT_MAX) #else # ifdef H4 # include "heap4.h" # define HTYPE Heap4 # define HINIT heap(INT_MAX, -INT_MAX, n) # else # ifdef H2 # include "heap2.h" # define HTYPE Heap2 # define HINIT heap(INT_MAX, -INT_MAX, n) # else # ifdef HSLOW # include "heap-CLR.h" # define HTYPE Heap2 # define HINIT heap(INT_MAX, -INT_MAX, n) # endif # endif # endif #endif #define rand32() (ran32State = 1664525 * ran32State + 1013904223) #define getRandom() ((rand32()), (int)(ran32State >> 2)) inline void onePass(HTYPE& heap, int n) { int j, newElem, k, v; double insertSum=0, deleteSum=0; static int ran32State = 42 << 20; Debug3(cout << heap.getSize()); Assert(heap.getSize() == 0); for (j = 0; j < n; j++) { newElem = getRandom(); heap.insert(newElem, j); Debug0(insertSum += newElem); Debug3(cout << newElem << "in "); heap.deleteMin(&k, &v); Debug0(deleteSum += k); Debug3(cout << k << "out "); newElem = getRandom(); heap.insert(newElem, j + 1); Debug0(insertSum += newElem); Debug3(cout << newElem << "in "); } Assert0(heap.getSize() == n); for (j = 0; j < n; j++) { heap.deleteMin(&k, &v); Debug0(deleteSum += k); Debug3(cout << k << "out "); newElem = getRandom(); heap.insert(newElem, j); Debug0(insertSum += newElem); Debug3(cout << newElem << "in "); heap.deleteMin(&k, &v); Debug0(deleteSum += k); Debug3(cout << k << "out "); } Assert0(deleteSum == insertSum); Assert(heap.getSize() == 0); } int main(int argc, char **argv) { Assert(argc > 1); int n = atoi(argv[1]); int i; int ran32State = 42 << 20; int repeat = 1; double startTime, endTime; if (argc > 2) repeat = atoi(argv[2]); //#ifdef H2 //HTYPE *temp = new HTYPE(INT_MAX, -INT_MAX); //HTYPE& heap =*temp; //#else HTYPE HINIT; //#endif // warmup onePass(heap, n); startTime = cpuTime(); for (i = 0; i < repeat; i++) { onePass(heap, n); } endTime = cpuTime(); // output time per insert-delete-pair and this time over log n double timePerPair = (endTime - startTime) / n / repeat / 3.0; double timePerCompare = timePerPair / (log((double)n) / log(2.0)); cout << n << " " << timePerPair * 1e9 << " " << timePerCompare * 1e9 << endl; } CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/knwiggle.C000644 000765 000024 00000005504 13271404146 025043 0ustar00aydinbulucstaff000000 000000 // benchmark with (insert insert delMin)^N (insert delMin delMin)^N // just in time RNG // version 3: use easier to handle binary heaps // knwiggle: access sequence is // (insert (insert delete)^k)^N (delete (insert delete)^k)^N #include #include #include #include #define DEBUGLEVEL 0 #include "util.h" //#define KNH #define H2 //#define H4 #ifdef KNH # include "knheap.C" # define HTYPE KNHeap # define HINIT heap(INT_MAX, -INT_MAX) #else # ifdef H4 # include "heap4.h" # define HTYPE Heap4 # define HINIT heap(INT_MAX, -INT_MAX, n) # else # ifdef H2 # include "heap2.h" # define HTYPE Heap2 # define HINIT heap(INT_MAX, -INT_MAX, n) # endif # endif #endif #define rand32() (ran32State = 1664525 * ran32State + 1013904223) #define getRandom() ((rand32()), (int)(ran32State >> 2)) inline void onePass(HTYPE& heap, int k, int n) { int i, j, newElem, key, v; double insertSum=0, deleteSum=0; static int ran32State = 42 << 20; Debug3(cout << heap.getSize()); Assert(heap.getSize() == 0); for (j = 0; j < n; j++) { newElem = getRandom(); heap.insert(newElem, j); Debug0(insertSum += newElem); Debug3(cout << newElem << "in "); for (i = 0; i < k; i++) { heap.deleteMin(&key, &v); Debug0(deleteSum += key); Debug3(cout << key << "out "); newElem = getRandom(); heap.insert(newElem, j + 1); Debug0(insertSum += newElem); Debug3(cout << newElem << "in "); } } Assert0(heap.getSize() == n); for (j = 0; j < n; j++) { heap.deleteMin(&key, &v); Debug0(deleteSum += key); Debug3(cout << key << "out "); for (i = 0; i < k; i++) { newElem = getRandom(); heap.insert(newElem, j); Debug0(insertSum += newElem); Debug3(cout << newElem << "in "); heap.deleteMin(&key, &v); Debug0(deleteSum += key); Debug3(cout << key << "out "); } } Assert0(deleteSum == insertSum); Assert(heap.getSize() == 0); } int main(int argc, char **argv) { Assert(argc > 2); int n = atoi(argv[1]); int k = atoi(argv[2]); int i; int ran32State = 42 << 20; int repeat = 1; double startTime, endTime; if (argc > 3) repeat = atoi(argv[3]); //#ifdef H2 //HTYPE *temp = new HTYPE(INT_MAX, -INT_MAX); //HTYPE& heap =*temp; //#else HTYPE HINIT; //#endif // warmup onePass(heap, k, n); startTime = cpuTime(); for (i = 0; i < repeat; i++) { onePass(heap, k, n); } endTime = cpuTime(); // output time per insert-delete-pair and this time over log n double timePerPair = (endTime - startTime) / n / repeat / (2.0*k + 1.0); double timePerCompare = timePerPair / (log((double)n) / log(2.0)); cout << n << " " << timePerPair * 1e9 << " " << timePerCompare * 1e9 << endl; } CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/heap-CLR.h000644 000765 000024 00000007054 13271404146 024636 0ustar00aydinbulucstaff000000 000000 // Binary heaps using a straightforward implementation a la CLR #ifndef HEAP2 #define HEAP2 // #include "util.h" template struct KNElement {Key key; Value value;}; // fixed size binary heap template class Heap2 { // static const Key infimum = 4; //static const Key supremum = numeric_limits.max(); typedef KNElement Element; Element *data; int capacity; int supremum; int size; // index of last used element public: Heap2(Key sup, Key infimum, int cap):size(0),capacity(cap) { data = new Element[cap + 2]; data[0].key = infimum; // sentinel data[capacity + 1].key = sup; supremum = sup; reset(); } ~Heap2() { delete data; } Key getSupremum() { return supremum; } void reset(); int getSize() const { return size; } Key getMinKey() const { return data[1].key; } Value getMinValue() const { return data[1].value; } void deleteMinBasic(); void deleteMin(Key *key, Value *value) { *key = getMinKey(); *value = getMinValue(); deleteMinBasic(); } void insert(Key k, Value v); void sortTo(Element *to); // sort in increasing order and empty //void sortInPlace(); // in decreasing order void print() { for (int i = 1; i <= size; i++) { cout << data[i].key << " "; } cout << endl; } }; // reset size to 0 and fill data array with sentinels template inline void Heap2:: reset() { size = 0; Key sup = getSupremum(); int cap = capacity; for (int i = 1; i <= cap; i++) { data[i].key = sup; } // if this becomes a bottle neck // we might want to replace this by log KNN // memcpy-s } template inline void Heap2:: deleteMinBasic() { int i, l, r, smallest; Assert2(size > 0); data[1] = data[size]; data[size].key = getSupremum(); size--; i = 1; for (;;) { Debug3(print()); l = (i << 1); r = (i << 1) + 1; if ((l <= size) && data[l].key < data[i].key) { smallest = l; } else { smallest = i; } if ((r <= size) && data[r].key < data[smallest].key) { smallest = r; } if (smallest == i) break; Element temp = data[i]; data[i] = data[smallest]; data[smallest] = temp; i = smallest; } } // empty the heap and put the element to "to" // sorted in increasing order template inline void Heap2:: sortTo(Element *to) { const int sz = size; const Key sup = getSupremum(); Element * const beyond = to + sz; Element * const root = data + 1; while (to < beyond) { // copy minimun *to = *root; to++; // bubble up second smallest as in deleteMin int hole = 1; int succ = 2; while (succ <= sz) { Key key1 = data[succ ].key; Key key2 = data[succ + 1].key; if (key1 > key2) { succ++; data[hole].key = key2; data[hole].value = data[succ].value; } else { data[hole].key = key1; data[hole].value = data[succ].value; } hole = succ; succ <<= 1; } // just mark hole as deleted data[hole].key = sup; } size = 0; } template inline void Heap2:: insert(Key k, Value v) { Assert2(size < capacity); Debug4(cout << "insert(" << k << ", " << v << ")" << endl); size++; int hole = size; while (hole > 1 && data[hole >> 1].key > k) { data[hole] = data[hole >> 1]; hole = hole >> 1; } data[hole].key = k; data[hole].value = v; } #endif CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/multiMergeUnrolled.C000644 000765 000024 00000003745 13271404146 027060 0ustar00aydinbulucstaff000000 000000 // body of the function multiMergeUnrolled // it will be included multiple times with // different settings for LogP // Note that in gcc it is sufficient to simply // use an addtional argument and to declare things an inline // function. But I was not able to convince SunCC to // inline and constant fold it. // Similarly I tried introducing LogP as a template // parameter but this did not compile on SunCC { //Entry *currentPos; //Key currentKey; //int currentIndex; // leaf pointed to by current entry Element *done = to + l; Entry *regEntry = entry; Element **regCurrent = current; int winnerIndex = regEntry[0].index; Key winnerKey = regEntry[0].key; Element *winnerPos; Key sup = dummy.key; // supremum Assert2(logK >= LogK); while (to < done) { winnerPos = regCurrent[winnerIndex]; // write result to->key = winnerKey; to->value = winnerPos->value; // advance winner segment winnerPos++; regCurrent[winnerIndex] = winnerPos; winnerKey = winnerPos->key; // remove winner segment if empty now if (winnerKey == sup) { deallocateSegment(winnerIndex); } to++; // update looser tree #define TreeStep(L)\ if (1 << LogK >= 1 << L) {\ unsigned udiff = (LogK-L)+1;\ Entry *pos##L = regEntry+((winnerIndex+(1<> udiff);\ Key key##L = pos##L->key;\ if (key##L < winnerKey) {\ int index##L = pos##L->index;\ pos##L->key = winnerKey;\ pos##L->index = winnerIndex;\ winnerKey = key##L;\ winnerIndex = index##L;\ }\ } TreeStep(10); TreeStep(9); TreeStep(8); TreeStep(7); TreeStep(6); TreeStep(5); TreeStep(4); TreeStep(3); TreeStep(2); TreeStep(1); #undef TreeStep } regEntry[0].index = winnerIndex; regEntry[0].key = winnerKey; } CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/heap4.h000644 000765 000024 00000013502 13271404146 024277 0ustar00aydinbulucstaff000000 000000 // 4ary heap data structure #ifndef HEAP4 #define HEAP4 #include "util.h" const unsigned long LineSize = 64; // cache line size (or multiple) template struct KNElement {Key key; Value value;}; // align an address // require: sz is a power of two inline char *knAlign(void *p, unsigned long sz) { return (char*)(((unsigned long)p + (sz - 1)) & ~(sz - 1)); }////////////////////////////////////////////////////////////////////// // fixed size 4-ary heap template class Heap4 { // static const Key infimum = 4; //static const Key supremum = numeric_limits.max(); typedef KNElement Element; int capacity; Element * rawData; Element * const data; // aligned version of rawData int size; // index of last used element int finalLayerSize; // size of first layer with free space int finalLayerDist; // distance to end of layer public: Heap4(Key sup, Key infimum, int cap) : capacity(cap), rawData(new Element[capacity + 4 + (LineSize-1)/sizeof(Element) + 1]), data((Element*)knAlign(rawData, LineSize)) { data[0].key = infimum; // sentinel data[capacity + 1].key = sup; data[capacity + 2].key = sup; data[capacity + 3].key = sup; reset(); } ~Heap4() { delete [] rawData; } Key getSupremum() { return data[capacity + 1].key; } void reset(); int getSize() const { return size; } Key getMinKey() const { return data[1].key; } Value getMinValue() const { return data[1].value; } void deleteMinBasic(); void deleteMin(Key *key, Value *value); void insert(Key k, Value v); void print(); //void sortTo(Element *to); // sort in increasing order and empty //void sortInPlace(); // in decreasing order }; // reset size to 0 and fill data array with sentinels template inline void Heap4:: reset() { size = 0; finalLayerSize = 1; finalLayerDist = 0; Key sup = getSupremum(); for (int i = 1; i <= capacity; i++) { data[i].key = sup; } } template inline void Heap4:: deleteMin(Key *key, Value *value) { *key = getMinKey(); *value = getMinValue(); deleteMinBasic(); } template inline void Heap4:: deleteMinBasic() { Assert2(size > 0); Key minKey, otherKey; int delta; // first move up elements on a min-path int hole = 1; int succ = 2; int layerSize = 4; // size of succ's layer int layerPos = 0; // pos of succ within its layer int sz = size; size = sz - 1; finalLayerDist++; if (finalLayerDist == finalLayerSize) { // layer empty now finalLayerSize >>= 2; finalLayerDist = 0; } while (succ < sz) { minKey = data[succ].key; delta = 0; // I could save a few assignments using // a complete case distincition but // this costs in terms of instruction cache load otherKey = data[succ + 1].key; if (otherKey < minKey) { minKey = otherKey; delta = 1; } otherKey = data[succ + 2].key; if (otherKey < minKey) { minKey = otherKey; delta = 2; } otherKey = data[succ + 3].key; if (otherKey < minKey) { minKey = otherKey; delta = 3; } succ += delta; layerPos += delta; // move min successor up data[hole].key = minKey; data[hole].value = data[succ].value; // step to next layer hole = succ; succ = succ - layerPos + layerSize; // beginning of next layer layerPos <<= 2; succ += layerPos; // now correct value layerSize <<= 2; } // bubble up rightmost element Key bubble = data[sz].key; layerSize >>= 2; // now size of hole's layer layerPos >>= 2; // now pos of hole within its layer // Assert2(finalLayerSize == layerSize); int layerDist = layerSize - layerPos - 1; // hole's dist to end of lay. int pred = hole + layerDist - layerSize; // end of pred's layer for now layerSize >>= 2; // now size of pred's layer layerDist >>= 2; // now pred's pos in layer pred = pred - layerDist; // finally preds index while (data[pred].key > bubble) { // must terminate since inf at root data[hole] = data[pred]; hole = pred; pred = hole + layerDist - layerSize; // end of hole's layer for now layerSize >>= 2; // now size of pred's layer layerDist >>= 2; pred = pred - layerDist; // finally preds index } // finally move data to hole data[hole].key = bubble; data[hole].value = data[sz].value; data[sz].key = getSupremum(); // mark as deleted } template void Heap4:: insert(Key k, Value v) { Assert2(size < capacity); Debug4(cout << "insert(" << k << ", " << v << ")" << endl); int layerSize = finalLayerSize; int layerDist = finalLayerDist; finalLayerDist--; if (finalLayerDist == -1) { // layer full // start next layer finalLayerSize <<= 2; finalLayerDist = finalLayerSize - 1; } size++; int hole = size; int pred = hole + layerDist - layerSize; // end of preds's layer for now layerSize >>= 2; // now size of pred's layer layerDist >>= 2; pred = pred - layerDist; // finally preds index Key predKey = data[pred].key; while (predKey > k) { // must terminate due to sentinel at 0 data[hole].key = predKey; data[hole].value = data[pred].value; hole = pred; pred = hole + layerDist - layerSize; // end of preds's layer for now layerSize >>= 2; // now size of pred's layer layerDist >>= 2; pred = pred - layerDist; // finally preds index predKey = data[pred].key; } // finally move data to hole data[hole].key = k; data[hole].value = v; } template inline void Heap4:: print() { int pos = 1; for (int layerSize = 1; pos < size; layerSize <<= 2) { for (int i = 0; i < layerSize && pos + i <= size; i++) { cout << data[pos + i].key << " "; } pos += layerSize; cout << endl; } } #endif CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/util.h000644 000765 000024 00000005111 13271404146 024250 0ustar00aydinbulucstaff000000 000000 // this files contains all the application independent little // functions and macros used for the optimizer. // In particular Peters debug macros and Dags stuff // from dbasic.h cdefs, random,... //////////////// stuff originally from debug.h /////////////////////////////// // (c) 1997 Peter Sanders // some little utilities for debugging adapted // to the paros conventions #include #ifndef UTIL #define UTIL // default debug level. will be overidden e.g. if debug.h is included #ifndef DEBUGLEVEL #define DEBUGLEVEL 3 #endif #if DEBUGLEVEL >= 0 #define Debug0(A) A #else #define Debug0(A) #endif #if DEBUGLEVEL >= 1 #define Debug1(A) A #else #define Debug1(A) #endif #if DEBUGLEVEL >= 2 #define Debug2(A) A #else #define Debug2(A) #endif #if DEBUGLEVEL >= 3 #define Debug3(A) A #else #define Debug3(A) #endif #if DEBUGLEVEL >= 4 #define Debug4(A) A #else #define Debug4(A) #endif #if DEBUGLEVEL >= 5 #define Debug5(A) A #else #define Debug5(A) #endif #if DEBUGLEVEL >= 6 #define Debug6(A) A #else #define Debug6(A) #endif #define Assert(c) if(!(c))\ {std::cout << "\nAssertion violation " << __FILE__ << ":" << __LINE__ << std::endl;} #define Assert0(C) Debug0(Assert(C)) #define Assert1(C) Debug1(Assert(C)) #define Assert2(C) Debug2(Assert(C)) #define Assert3(C) Debug3(Assert(C)) #define Assert4(C) Debug4(Assert(C)) #define Assert5(C) Debug5(Assert(C)) #define Error(s) {std::cout << "\nError:" << s << " " << __FILE__ << ":" << __LINE__ << std::endl;} ////////////// min, max etc. ////////////////////////////////////// #ifndef Max #define Max(x,y) ((x)>=(y)?(x):(y)) #endif #ifndef Min #define Min(x,y) ((x)<=(y)?(x):(y)) #endif #ifndef Abs #define Abs(x) ((x) < 0 ? -(x) : (x)) #endif #ifndef PI #define PI 3.1415927 #endif // is this the right definition of limit? inline double limit(double x, double bound) { if (x > bound) { return bound; } else if (x < -bound) { return -bound; } else return x; } /////////////////////// timing ///////////////////// #include // AL: sys/time.h does not exist on Windows, and this function is never used. /*#include inline double wallClockTime() { // struct timespec tp; timeval tim; gettimeofday(&tim, NULL); double t1=tim.tv_sec+(tim.tv_usec/1000000.0); return t1; // clock_gettime(CLOCK_REALTIME, &tp); // return tp.tv_sec + tp.tv_nsec * 1e-9; }*/ // elapsed CPU time see also /usr/include/sys/time.h inline double cpuTime() { //struct timespec tp; return clock() * 1e-6; // clock_gettime(CLOCK_VIRTUAL, &tp); // return tp.tv_sec + tp.tv_nsec * 1e-9; } #endif CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/knheap.C000644 000765 000024 00000055171 13271404146 024507 0ustar00aydinbulucstaff000000 000000 #include "knheap.h" #include ///////////////////////// LooserTree /////////////////////////////////// template KNLooserTree:: KNLooserTree() : lastFree(0), size(0), logK(0), k(1) { empty [0] = 0; segment[0] = 0; current[0] = &dummy; // entry and dummy are initialized by init // since they need the value of supremum } template void KNLooserTree:: init(Key sup) { dummy.key = sup; rebuildLooserTree(); Assert2(current[entry[0].index] == &dummy); } // rebuild looser tree information from the values in current template void KNLooserTree:: rebuildLooserTree() { int winner = initWinner(1); entry[0].index = winner; entry[0].key = current[winner]->key; } // given any values in the leaves this // routing recomputes upper levels of the tree // from scratch in linear time // initialize entry[root].index and the subtree rooted there // return winner index template int KNLooserTree:: initWinner(int root) { if (root >= k) { // leaf reached return root - k; } else { int left = initWinner(2*root ); int right = initWinner(2*root + 1); Key lk = current[left ]->key; Key rk = current[right]->key; if (lk <= rk) { // right subtree looses entry[root].index = right; entry[root].key = rk; return left; } else { entry[root].index = left; entry[root].key = lk; return right; } } } // first go up the tree all the way to the root // hand down old winner for the respective subtree // based on new value, and old winner and looser // update each node on the path to the root top down. // This is implemented recursively template void KNLooserTree:: updateOnInsert(int node, Key newKey, int newIndex, Key *winnerKey, int *winnerIndex, // old winner int *mask) // 1 << (ceil(log KNK) - dist-from-root) { if (node == 0) { // winner part of root *mask = 1 << (logK - 1); *winnerKey = entry[0].key; *winnerIndex = entry[0].index; if (newKey < entry[node].key) { entry[node].key = newKey; entry[node].index = newIndex; } } else { updateOnInsert(node >> 1, newKey, newIndex, winnerKey, winnerIndex, mask); Key looserKey = entry[node].key; int looserIndex = entry[node].index; if ((*winnerIndex & *mask) != (newIndex & *mask)) { // different subtrees if (newKey < looserKey) { // newKey will have influence here if (newKey < *winnerKey) { // old winner loses here entry[node].key = *winnerKey; entry[node].index = *winnerIndex; } else { // new entry looses here entry[node].key = newKey; entry[node].index = newIndex; } } *winnerKey = looserKey; *winnerIndex = looserIndex; } // note that nothing needs to be done if // the winner came from the same subtree // a) newKey <= winnerKey => even more reason for the other tree to loose // b) newKey > winnerKey => the old winner will beat the new // entry further down the tree // also the same old winner is handed down the tree *mask >>= 1; // next level } } // make the tree two times as wide // may only be called if no free slots are left ?? necessary ?? template void KNLooserTree:: doubleK() { // make all new entries empty // and push them on the free stack Assert2(lastFree == -1); // stack was empty (probably not needed) Assert2(k < KNKMAX); for (int i = 2*k - 1; i >= k; i--) { current[i] = &dummy; lastFree++; empty[lastFree] = i; } // double the size k *= 2; logK++; // recompute looser tree information rebuildLooserTree(); } // compact nonempty segments in the left half of the tree template void KNLooserTree:: compactTree() { Assert2(logK > 0); Key sup = dummy.key; // compact all nonempty segments to the left int from = 0; int to = 0; for(; from < k; from++) { if (current[from]->key != sup) { current[to] = current[from]; segment[to] = segment[from]; to++; } } // half degree as often as possible while (to < k/2) { k /= 2; logK--; } // overwrite garbage and compact the stack of empty segments lastFree = -1; // none free for (; to < k; to++) { // push lastFree++; empty[lastFree] = to; current[to] = &dummy; } // recompute looser tree information rebuildLooserTree(); } // insert segment beginning at to // require: spaceIsAvailable() == 1 template void KNLooserTree:: insertSegment(Element *to, int sz) { if (sz > 0) { Assert2(to[0 ].key != getSupremum()); Assert2(to[sz-1].key != getSupremum()); // get a free slot if (lastFree < 0) { // tree is too small doubleK(); } int index = empty[lastFree]; lastFree--; // pop // link new segment current[index] = segment[index] = to; size += sz; // propagate new information up the tree Key dummyKey; int dummyIndex; int dummyMask; updateOnInsert((index + k) >> 1, to->key, index, &dummyKey, &dummyIndex, &dummyMask); } else { // immediately deallocate // this is not only an optimization // but also needed to keep empty segments from // clogging up the tree delete [] to; } } // free an empty segment template void KNLooserTree:: deallocateSegment(int index) { // reroute current pointer to some empty dummy segment // with a sentinel key current[index] = &dummy; // free memory delete [] segment[index]; segment[index] = 0; // push on the stack of free segment indices lastFree++; empty[lastFree] = index; } // multi-merge for a fixed K=1< void KNLooserTree:: multiMergeUnrolled3(Element *to, int l) #include "multiMergeUnrolled.C" #undef LogK #define LogK 4 template void KNLooserTree:: multiMergeUnrolled4(Element *to, int l) #include "multiMergeUnrolled.C" #undef LogK #define LogK 5 template void KNLooserTree:: multiMergeUnrolled5(Element *to, int l) #include "multiMergeUnrolled.C" #undef LogK #define LogK 6 template void KNLooserTree:: multiMergeUnrolled6(Element *to, int l) #include "multiMergeUnrolled.C" #undef LogK #define LogK 7 template void KNLooserTree:: multiMergeUnrolled7(Element *to, int l) #include "multiMergeUnrolled.C" #undef LogK #define LogK 8 template void KNLooserTree:: multiMergeUnrolled8(Element *to, int l) #include "multiMergeUnrolled.C" #undef LogK #define LogK 9 template void KNLooserTree:: multiMergeUnrolled9(Element *to, int l) #include "multiMergeUnrolled.C" #undef LogK #define LogK 10 template void KNLooserTree:: multiMergeUnrolled10(Element *to, int l) #include "multiMergeUnrolled.C" #undef LogK // delete the l smallest elements and write them to "to" // empty segments are deallocated // require: // - there are at least l elements // - segments are ended by sentinels template void KNLooserTree:: multiMerge(Element *to, int l) { switch(logK) { case 0: Assert2(k == 1); Assert2(entry[0].index == 0); Assert2(lastFree == -1 || l == 0); memcpy(to, current[0], l * sizeof(Element)); current[0] += l; entry[0].key = current[0]->key; if (segmentIsEmpty(0)) deallocateSegment(0); break; case 1: Assert2(k == 2); merge(current + 0, current + 1, to, l); rebuildLooserTree(); if (segmentIsEmpty(0)) deallocateSegment(0); if (segmentIsEmpty(1)) deallocateSegment(1); break; case 2: Assert2(k == 4); merge4(current + 0, current + 1, current + 2, current + 3, to, l); rebuildLooserTree(); if (segmentIsEmpty(0)) deallocateSegment(0); if (segmentIsEmpty(1)) deallocateSegment(1); if (segmentIsEmpty(2)) deallocateSegment(2); if (segmentIsEmpty(3)) deallocateSegment(3); break; case 3: multiMergeUnrolled3(to, l); break; case 4: multiMergeUnrolled4(to, l); break; case 5: multiMergeUnrolled5(to, l); break; case 6: multiMergeUnrolled6(to, l); break; case 7: multiMergeUnrolled7(to, l); break; case 8: multiMergeUnrolled8(to, l); break; case 9: multiMergeUnrolled9(to, l); break; case 10: multiMergeUnrolled10(to, l); break; default: multiMergeK (to, l); break; } size -= l; // compact tree if it got considerably smaller if (k > 1 && lastFree >= 3*k/5 - 1) { // using k/2 would be worst case inefficient compactTree(); } } // is this segment empty and does not point to dummy yet? template inline int KNLooserTree:: segmentIsEmpty(int i) { return current[i]->key == getSupremum() && current[i] != &dummy; } // multi-merge for arbitrary K template void KNLooserTree:: multiMergeK(Element *to, int l) { Entry *currentPos; Key currentKey; int currentIndex; // leaf pointed to by current entry int kReg = k; Element *done = to + l; int winnerIndex = entry[0].index; Key winnerKey = entry[0].key; Element *winnerPos; Key sup = dummy.key; // supremum while (to < done) { winnerPos = current[winnerIndex]; // write result to->key = winnerKey; to->value = winnerPos->value; // advance winner segment winnerPos++; current[winnerIndex] = winnerPos; winnerKey = winnerPos->key; // remove winner segment if empty now if (winnerKey == sup) { deallocateSegment(winnerIndex); } // go up the entry-tree for (int i = (winnerIndex + kReg) >> 1; i > 0; i >>= 1) { currentPos = entry + i; currentKey = currentPos->key; if (currentKey < winnerKey) { currentIndex = currentPos->index; currentPos->key = winnerKey; currentPos->index = winnerIndex; winnerKey = currentKey; winnerIndex = currentIndex; } } to++; } entry[0].index = winnerIndex; entry[0].key = winnerKey; } ////////////////////////// KNHeap ////////////////////////////////////// template KNHeap:: KNHeap(Key sup, Key infimum) : insertHeap(sup, infimum), activeLevels(0), size(0) { buffer1[KNBufferSize1].key = sup; // sentinel minBuffer1 = buffer1 + KNBufferSize1; // empty for (int i = 0; i < KNLevels; i++) { tree[i].init(sup); // put tree[i] in a consistent state buffer2[i][KNN].key = sup; // sentinel minBuffer2[i] = &(buffer2[i][KNN]); // empty } } //--------------------- Buffer refilling ------------------------------- // refill buffer2[j] and return number of elements found template int KNHeap::refillBuffer2(int j) { Element *oldTarget; int deleteSize; int treeSize = tree[j].getSize(); int bufferSize = (&(buffer2[j][0]) + KNN) - minBuffer2[j]; if (treeSize + bufferSize >= KNN) { // buffer will be filled oldTarget = &(buffer2[j][0]); deleteSize = KNN - bufferSize; } else { oldTarget = &(buffer2[j][0]) + KNN - treeSize - bufferSize; deleteSize = treeSize; } // shift rest to beginning // possible hack: // - use memcpy if no overlap memmove(oldTarget, minBuffer2[j], bufferSize * sizeof(Element)); minBuffer2[j] = oldTarget; // fill remaining space from tree tree[j].multiMerge(oldTarget + bufferSize, deleteSize); return deleteSize + bufferSize; } // move elements from the 2nd level buffers // to the delete buffer template void KNHeap::refillBuffer1() { int totalSize = 0; int sz; for (int i = activeLevels - 1; i >= 0; i--) { if ((&(buffer2[i][0]) + KNN) - minBuffer2[i] < KNBufferSize1) { sz = refillBuffer2(i); // max active level dry now? if (sz == 0 && i == activeLevels - 1) { activeLevels--; } else {totalSize += sz;} } else { totalSize += KNBufferSize1; // actually only a sufficient lower bound } } if (totalSize >= KNBufferSize1) { // buffer can be filled minBuffer1 = buffer1; sz = KNBufferSize1; // amount to be copied size -= KNBufferSize1; // amount left in buffer2 } else { minBuffer1 = buffer1 + KNBufferSize1 - totalSize; sz = totalSize; Assert2(size == sz); // trees and buffer2 get empty size = 0; } // now call simplified refill routines // which can make the assumption that // they find all they are asked to find in the buffers minBuffer1 = buffer1 + KNBufferSize1 - sz; switch(activeLevels) { case 1: memcpy(minBuffer1, minBuffer2[0], sz * sizeof(Element)); minBuffer2[0] += sz; break; case 2: merge(&(minBuffer2[0]), &(minBuffer2[1]), minBuffer1, sz); break; case 3: merge3(&(minBuffer2[0]), &(minBuffer2[1]), &(minBuffer2[2]), minBuffer1, sz); break; case 4: merge4(&(minBuffer2[0]), &(minBuffer2[1]), &(minBuffer2[2]), &(minBuffer2[3]), minBuffer1, sz); break; // case 2: refillBuffer12(sz); break; // case 3: refillBuffer13(sz); break; // case 4: refillBuffer14(sz); break; } } template void KNHeap::refillBuffer13(int sz) { Assert(0); // not yet implemented } template void KNHeap::refillBuffer14(int sz) { Assert(0); // not yet implemented } //-------------------------------------------------------------------- // check if space is available on level k and // empty this level if necessary leading to a recursive call. // return the level where space was finally available template int KNHeap::makeSpaceAvailable(int level) { int finalLevel; Assert2(level <= activeLevels); if (level == activeLevels) { activeLevels++; } if (tree[level].spaceIsAvailable()) { finalLevel = level; } else { finalLevel = makeSpaceAvailable(level + 1); int segmentSize = tree[level].getSize(); Element *newSegment = new Element[segmentSize + 1]; tree[level].multiMerge(newSegment, segmentSize); // empty this level // tree[level].cleanUp(); newSegment[segmentSize].key = buffer1[KNBufferSize1].key; // sentinel // for queues where size << #inserts // it might make sense to stay in this level if // segmentSize < alpha * KNN * k^level for some alpha < 1 tree[level + 1].insertSegment(newSegment, segmentSize); } return finalLevel; } // empty the insert heap into the main data structure template void KNHeap::emptyInsertHeap() { const Key sup = getSupremum(); // build new segment Element *newSegment = new Element[KNN + 1]; Element *newPos = newSegment; // put the new data there for now insertHeap.sortTo(newSegment); newSegment[KNN].key = sup; // sentinel // copy the buffer1 and buffer2[0] to temporary storage // (the tomporary can be eliminated using some dirty tricks) const int tempSize = KNN + KNBufferSize1; Element temp[tempSize + 1]; int sz1 = getSize1(); int sz2 = getSize2(0); Element *pos = temp + tempSize - sz1 - sz2; memcpy(pos , minBuffer1 , sz1 * sizeof(Element)); memcpy(pos + sz1, minBuffer2[0], sz2 * sizeof(Element)); temp[tempSize].key = sup; // sentinel // refill buffer1 // (using more complicated code it could be made somewhat fuller // in certein circumstances) merge(&pos, &newPos, minBuffer1, sz1); // refill buffer2[0] // (as above we might want to take the opportunity // to make buffer2[0] fuller) merge(&pos, &newPos, minBuffer2[0], sz2); // merge the rest to the new segment // note that merge exactly trips into the footsteps // of itself merge(&pos, &newPos, newSegment, KNN); // and insert it int freeLevel = makeSpaceAvailable(0); Assert2(freeLevel == 0 || tree[0].getSize() == 0); tree[0].insertSegment(newSegment, KNN); // get rid of invalid level 2 buffers // by inserting them into tree 0 (which is almost empty in this case) if (freeLevel > 0) { for (int i = freeLevel; i >= 0; i--) { // reverse order not needed // but would allow immediate refill newSegment = new Element[getSize2(i) + 1]; // with sentinel memcpy(newSegment, minBuffer2[i], (getSize2(i) + 1) * sizeof(Element)); tree[0].insertSegment(newSegment, getSize2(i)); minBuffer2[i] = buffer2[i] + KNN; // empty } } // update size size += KNN; // special case if the tree was empty before if (minBuffer1 == buffer1 + KNBufferSize1) { refillBuffer1(); } } ///////////////////////////////////////////////////////////////////// // auxiliary functions // merge sz element from the two sentinel terminated input // sequences *f0 and *f1 to "to" // advance *fo and *f1 accordingly. // require: at least sz nonsentinel elements available in f0, f1 // require: to may overwrite one of the sources as long as // *fx + sz is before the end of fx template void merge(KNElement **f0, KNElement **f1, KNElement *to, int sz) { KNElement *from0 = *f0; KNElement *from1 = *f1; KNElement *done = to + sz; Key key0 = from0->key; Key key1 = from1->key; while (to < done) { if (key1 <= key0) { to->key = key1; to->value = from1->value; // note that this may be the same address from1++; // nach hinten schieben? key1 = from1->key; } else { to->key = key0; to->value = from0->value; // note that this may be the same address from0++; // nach hinten schieben? key0 = from0->key; } to++; } *f0 = from0; *f1 = from1; } // merge sz element from the three sentinel terminated input // sequences *f0, *f1 and *f2 to "to" // advance *f0, *f1 and *f2 accordingly. // require: at least sz nonsentinel elements available in f0, f1 and f2 // require: to may overwrite one of the sources as long as // *fx + sz is before the end of fx template void merge3(KNElement **f0, KNElement **f1, KNElement **f2, KNElement *to, int sz) { KNElement *from0 = *f0; KNElement *from1 = *f1; KNElement *from2 = *f2; KNElement *done = to + sz; Key key0 = from0->key; Key key1 = from1->key; Key key2 = from2->key; if (key0 < key1) { if (key1 < key2) { goto s012; } else { if (key2 < key0) { goto s201; } else { goto s021; } } } else { if (key1 < key2) { if (key0 < key2) { goto s102; } else { goto s120; } } else { goto s210; } } #define Merge3Case(a,b,c)\ s ## a ## b ## c :\ if (to == done) goto finish;\ to->key = key ## a;\ to->value = from ## a -> value;\ to++;\ from ## a ++;\ key ## a = from ## a -> key;\ if (key ## a < key ## b) goto s ## a ## b ## c;\ if (key ## a < key ## c) goto s ## b ## a ## c;\ goto s ## b ## c ## a; // the order is choosen in such a way that // four of the trailing gotos can be eliminated by the optimizer Merge3Case(0, 1, 2); Merge3Case(1, 2, 0); Merge3Case(2, 0, 1); Merge3Case(1, 0, 2); Merge3Case(0, 2, 1); Merge3Case(2, 1, 0); finish: *f0 = from0; *f1 = from1; *f2 = from2; } // merge sz element from the three sentinel terminated input // sequences *f0, *f1, *f2 and *f3 to "to" // advance *f0, *f1, *f2 and *f3 accordingly. // require: at least sz nonsentinel elements available in f0, f1, f2 and f2 // require: to may overwrite one of the sources as long as // *fx + sz is before the end of fx template void merge4(KNElement **f0, KNElement **f1, KNElement **f2, KNElement **f3, KNElement *to, int sz) { KNElement *from0 = *f0; KNElement *from1 = *f1; KNElement *from2 = *f2; KNElement *from3 = *f3; KNElement *done = to + sz; Key key0 = from0->key; Key key1 = from1->key; Key key2 = from2->key; Key key3 = from3->key; #define StartMerge4(a, b, c, d)\ if (key##a <= key##b && key##b <= key##c && key##c <= key##d)\ goto s ## a ## b ## c ## d; StartMerge4(0, 1, 2, 3); StartMerge4(1, 2, 3, 0); StartMerge4(2, 3, 0, 1); StartMerge4(3, 0, 1, 2); StartMerge4(0, 3, 1, 2); StartMerge4(3, 1, 2, 0); StartMerge4(1, 2, 0, 3); StartMerge4(2, 0, 3, 1); StartMerge4(0, 2, 3, 1); StartMerge4(2, 3, 1, 0); StartMerge4(3, 1, 0, 2); StartMerge4(1, 0, 2, 3); StartMerge4(2, 0, 1, 3); StartMerge4(0, 1, 3, 2); StartMerge4(1, 3, 2, 0); StartMerge4(3, 2, 0, 1); StartMerge4(3, 0, 2, 1); StartMerge4(0, 2, 1, 3); StartMerge4(2, 1, 3, 0); StartMerge4(1, 3, 0, 2); StartMerge4(1, 0, 3, 2); StartMerge4(0, 3, 2, 1); StartMerge4(3, 2, 1, 0); StartMerge4(2, 1, 0, 3); #define Merge4Case(a, b, c, d)\ s ## a ## b ## c ## d:\ if (to == done) goto finish;\ to->key = key ## a;\ to->value = from ## a -> value;\ to++;\ from ## a ++;\ key ## a = from ## a -> key;\ if (key ## a < key ## c) {\ if (key ## a < key ## b) { goto s ## a ## b ## c ## d; }\ else { goto s ## b ## a ## c ## d; }\ } else {\ if (key ## a < key ## d) { goto s ## b ## c ## a ## d; }\ else { goto s ## b ## c ## d ## a; }\ } Merge4Case(0, 1, 2, 3); Merge4Case(1, 2, 3, 0); Merge4Case(2, 3, 0, 1); Merge4Case(3, 0, 1, 2); Merge4Case(0, 3, 1, 2); Merge4Case(3, 1, 2, 0); Merge4Case(1, 2, 0, 3); Merge4Case(2, 0, 3, 1); Merge4Case(0, 2, 3, 1); Merge4Case(2, 3, 1, 0); Merge4Case(3, 1, 0, 2); Merge4Case(1, 0, 2, 3); Merge4Case(2, 0, 1, 3); Merge4Case(0, 1, 3, 2); Merge4Case(1, 3, 2, 0); Merge4Case(3, 2, 0, 1); Merge4Case(3, 0, 2, 1); Merge4Case(0, 2, 1, 3); Merge4Case(2, 1, 3, 0); Merge4Case(1, 3, 0, 2); Merge4Case(1, 0, 3, 2); Merge4Case(0, 3, 2, 1); Merge4Case(3, 2, 1, 0); Merge4Case(2, 1, 0, 3); finish: *f0 = from0; *f1 = from1; *f2 = from2; *f3 = from3; } CombBLAS_beta_16_2/include/CombBLAS/SequenceHeaps/hold.C000644 000765 000024 00000005330 13271404146 024157 0ustar00aydinbulucstaff000000 000000 // benchmark with (insert insert delMin)^N (insert delMin delMin)^N // just in time RNG // version 3: use easier to handle binary heaps // hold.C: generage inputs in the hold model #include #include #include #include #define DEBUGLEVEL 0 #include "util.h" //#define KNH #define H2 //#define H4 //#define HSLOW #ifdef KNH # include "knheap.C" # define HTYPE KNHeap # define HINIT heap(INT_MAX, -INT_MAX) #else # ifdef H4 # include "heap4.h" # define HTYPE Heap4 # define HINIT heap(INT_MAX, -INT_MAX, n) # else # ifdef H2 # include "heap2.h" # define HTYPE Heap2 # define HINIT heap(INT_MAX, -INT_MAX, n) # else # ifdef HSLOW # include "heap-CLR.h" # define HTYPE Heap2 # define HINIT heap(INT_MAX, -INT_MAX, n) # endif # endif # endif #endif #define rand32() (ran32State = 1664525 * ran32State + 1013904223) #define getRandom() ((rand32()), (int)(((unsigned int)ran32State) >> 4)) inline void onePass(HTYPE& heap, int n) { int j, newElem, k, v, old; long long insertSum=0, deleteSum=0; static int ran32State = 42 << 20; Debug3(cout << heap.getSize()); Assert(heap.getSize() == 0); // build heap for (j = 0; j < n; j++) { newElem = getRandom(); heap.insert(newElem, j); Debug0(insertSum += newElem); Debug3(cout << newElem << "in "); } // hold phase old = 0; for (j = 0; j < 2*n; j++) { heap.deleteMin(&k, &v); Debug0(deleteSum += k); Assert0(k >= old); Debug0(old = k); Debug3(cout << k << "out "); newElem = k + getRandom(); heap.insert(newElem, j); Debug0(insertSum += newElem); Debug3(cout << newElem << "in "); } Assert0(heap.getSize() == n); // finish up for (j = 0; j < n; j++) { heap.deleteMin(&k, &v); Debug0(deleteSum += k); Debug3(cout << k << "out "); } Assert0(deleteSum == insertSum); Assert(heap.getSize() == 0); } int main(int argc, char **argv) { Assert(argc > 1); int n = atoi(argv[1]); int i; int ran32State = 42 << 20; int repeat = 1; double startTime, endTime; if (argc > 2) repeat = atoi(argv[2]); //#ifdef H2 //HTYPE *temp = new HTYPE(INT_MAX, -INT_MAX); //HTYPE& heap =*temp; //#else HTYPE HINIT; //#endif // warmup onePass(heap, n); startTime = cpuTime(); for (i = 0; i < repeat; i++) { onePass(heap, n); } endTime = cpuTime(); // output time per insert-delete-pair and this time over log n double timePerPair = (endTime - startTime) / n / repeat / 3.0; double timePerCompare = timePerPair / (log((double)n) / log(2.0)); cout << n << " " << timePerPair * 1e9 << " " << timePerCompare * 1e9 << endl; } CombBLAS_beta_16_2/include/CombBLAS/PBBS/utils.h000644 000765 000024 00000013644 13271404146 022442 0ustar00aydinbulucstaff000000 000000 // This code is part of the Problem Based Benchmark Suite (PBBS) // Copyright (c) 2010 Guy Blelloch and the PBBS team // // 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. #ifndef _BENCH_UTILS_INCLUDED #define _BENCH_UTILS_INCLUDED #include #include #include #if defined(__APPLE__) #define PTCMPXCH " cmpxchgl %2,%1\n" #else #define PTCMPXCH " cmpxchgq %2,%1\n" // Needed to make frequent large allocations efficient with standard // malloc implementation. Otherwise they are allocated directly from // vm. #include static int __ii = mallopt(M_MMAP_MAX,0); static int __jj = mallopt(M_TRIM_THRESHOLD,-1); #endif #define newA(__E,__n) (__E*) malloc((__n)*sizeof(__E)) namespace utils { // ADAM: added inline to remove "defined but not used" warning static inline void myAssert(int cond, std::string s) { if (!cond) { std::cout << s << std::endl; abort(); } } // returns the log base 2 rounded up (works on ints or longs or unsigned versions) template static int log2Up(T i) { int a=0; T b=i-1; while (b > 0) {b = b >> 1; a++;} return a; } // ADAM: added inline to remove "defined but not used" warning static inline int logUp(unsigned int i) { int a=0; int b=i-1; while (b > 0) {b = b >> 1; a++;} return a; } // ADAM: added inline to remove "defined but not used" warning static inline int logUpLong(unsigned long i) { int a=0; long b=i-1; while (b > 0) {b = b >> 1; a++;} return a; } inline unsigned int hash(unsigned int a) { a = (a+0x7ed55d16) + (a<<12); a = (a^0xc761c23c) ^ (a>>19); a = (a+0x165667b1) + (a<<5); a = (a+0xd3a2646c) ^ (a<<9); a = (a+0xfd7046c5) + (a<<3); a = (a^0xb55a4f09) ^ (a>>16); return a; } inline int hashInt(unsigned int a) { return hash(a) & (((unsigned) 1 << 31) - 1); } inline unsigned int hash2(unsigned int a) { return (((unsigned int) 1103515245 * a) + (unsigned int) 12345) % (unsigned int) 0xFFFFFFFF; } // compare and swap on 8 byte quantities inline bool LCAS(long *ptr, long oldv, long newv) { unsigned char ret; /* Note that sete sets a 'byte' not the word */ __asm__ __volatile__ ( " lock\n" " cmpxchgq %2,%1\n" " sete %0\n" : "=q" (ret), "=m" (*ptr) : "r" (newv), "m" (*ptr), "a" (oldv) : "memory"); return ret; } // compare and swap on 4 byte quantity inline bool SCAS(int *ptr, int oldv, int newv) { unsigned char ret; /* Note that sete sets a 'byte' not the word */ __asm__ __volatile__ ( " lock\n" " cmpxchgl %2,%1\n" " sete %0\n" : "=q" (ret), "=m" (*ptr) : "r" (newv), "m" (*ptr), "a" (oldv) : "memory"); return ret; } // The conditional should be removed by the compiler // this should work with pointer types, or pairs of integers template inline bool CAS(ET *ptr, ET oldv, ET newv) { if (sizeof(ET) == 8) { return utils::LCAS((long*) ptr, *((long*) &oldv), *((long*) &newv)); } else if (sizeof(ET) == 4) { return utils::SCAS((int *) ptr, *((int *) &oldv), *((int *) &newv)); } else { std::cout << "CAS bad length" << std::endl; abort(); } } template inline bool CAS_GCC(ET *ptr, ET oldv, ET newv) { return __sync_bool_compare_and_swap(ptr, oldv, newv); } template inline ET fetchAndAdd(ET *a, ET b) { volatile ET newV, oldV; abort(); do {oldV = *a; newV = oldV + b;} while (!CAS(a, oldV, newV)); return oldV; } template inline void writeAdd(ET *a, ET b) { volatile ET newV, oldV; do {oldV = *a; newV = oldV + b;} while (!CAS(a, oldV, newV)); } template inline bool writeMax(ET *a, ET b) { ET c; bool r=0; do c = *a; while (c < b && !(r=CAS(a,c,b))); return r; } template inline bool writeMin(ET *a, ET b) { ET c; bool r=0; do c = *a; while (c > b && !(r=CAS(a,c,b))); // while (c > b && !(r=__sync_bool_compare_and_swap(a, c, b))); return r; } template inline bool writeMin(ET **a, ET *b) { ET* c; bool r = 0; do c = *a; while (c > b && !(r=CAS(a,c,b))); return r; } template struct identityF { E operator() (const E& x) {return x;}}; template struct addF { E operator() (const E& a, const E& b) const {return a+b;}}; template struct absF { E operator() (const E& a) const {return std::abs(a);}}; template struct zeroF { E operator() (const E& a) const {return 0;}}; template struct maxF { E operator() (const E& a, const E& b) const {return (a>b) ? a : b;}}; template struct minF { E operator() (const E& a, const E& b) const {return (a struct firstF {E1 operator() (std::pair a) {return a.first;} }; template struct secondF {E2 operator() (std::pair a) {return a.second;} }; } #endif // _BENCH_UTILS_INCLUDED CombBLAS_beta_16_2/include/CombBLAS/PBBS/radixSort.h000644 000765 000024 00000007514 13271404146 023260 0ustar00aydinbulucstaff000000 000000 // This code is part of the Problem Based Benchmark Suite (PBBS) // Copyright (c) 2011 Guy Blelloch and the PBBS team // // 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. #ifndef _S_RADIX_INCLUDED #define _S_RADIX_INCLUDED #include #include #include #include "utils.h" namespace intSort { // Cannot be greater than 8 without changing definition of bIndexT // from unsigned char to unsigned int (or unsigned short) #define MAX_RADIX 8 #define BUCKETS (1 << MAX_RADIX) // a type that must hold MAX_RADIX bits typedef unsigned char bIndexT; // input in A, output in B template void radixStep(E* A, E* B, bIndexT *Tmp, int* counts, int n, int m, F extract) { for (int i = 0; i < m; i++) counts[i] = 0; for (int j = 0; j < n; j++) { int k = Tmp[j] = extract(A[j]); counts[k]++; } int s = 0; for (int i = 0; i < m; i++) { s += counts[i]; counts[i] = s; } for (int j = n-1; j >= 0; j--) { int x = --counts[Tmp[j]]; B[x] = A[j]; } } // a function to extract "bits" bits starting at bit location "offset" template struct eBits { F _f; int _mask; int _offset; // ADAM: moved initialization of _f to the front to remove "x will be initialized after y" warning eBits(int bits, int offset, F f): _f(f), _mask((1<>_offset);} }; // Radix sort with low order bits first template void iSort(E *A, int n, int m, F f) { int bits = utils::log2Up(m); // temporary space E* B = (E*) malloc(sizeof(E)*n); bIndexT* Tmp = (bIndexT*) malloc(sizeof(bIndexT)*n); int* counts = (int*) malloc(sizeof(int)*BUCKETS); int rounds = 1+(bits-1)/MAX_RADIX; int rbits = 1+(bits-1)/rounds; int bitOffset = 0; bool flipped = 0; while (bitOffset < bits) { if (bitOffset+rbits > bits) rbits = bits-bitOffset; if (flipped) radixStep(B, A, Tmp, counts, n, 1 << rbits, eBits(rbits,bitOffset,f)); else radixStep(A, B, Tmp, counts, n, 1 << rbits, eBits(rbits,bitOffset,f)); bitOffset += rbits; flipped = !flipped; } if (flipped) for (int i=0; i < n; i++) A[i] = B[i]; free(B); free(Tmp); free(counts); } } // ADAM: added inline to remove "defined but not used" warning static inline void integerSort(uint32_t *A, int n) { uint32_t maxV = 0; for (int i=0; i()); } template void integerSort(std::pair *A, int n) { uint32_t maxV = 0; for (int i=0; i()); } #endif // _S_RADIX_INCLUDED CombBLAS_beta_16_2/3DSpGEMM/SUMMALayer.h000644 000765 000024 00000006135 13271404146 020702 0ustar00aydinbulucstaff000000 000000 #ifndef _SUMMA_LAYER_H_ #define _SUMMA_LAYER_H_ #include #include #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" #include "Glue.h" #include "CombBLAS/mtSpGEMM.h" #include "CCGrid.h" namespace combblas { // SplitB is already locally transposed // Returns an array of unmerged lists in C template void SUMMALayer (SpDCCols & SplitA, SpDCCols & SplitB, std::vector< SpTuples* > & C, CCGrid & CMG, bool isBT, bool threaded) { typedef PlusTimesSRing PTDD; int stages = CMG.GridCols; // total number of "essential" summa stages IT ** ARecvSizes = SpHelper::allocate2D(SpDCCols::esscount, stages); IT ** BRecvSizes = SpHelper::allocate2D(SpDCCols::esscount, stages); // Remotely fetched matrices are stored as pointers SpDCCols * ARecv; SpDCCols * BRecv; int Aself = CMG.RankInRow; int Bself = CMG.RankInCol; // Set the dimensions SpParHelper::GetSetSizes( SplitA, ARecvSizes, CMG.rowWorld); SpParHelper::GetSetSizes( SplitB, BRecvSizes, CMG.colWorld); for(int i = 0; i < stages; ++i) { double bcast_beg = MPI_Wtime(); std::vector ess; if(i == Aself) ARecv = &SplitA; // shallow-copy else { ess.resize(SpDCCols::esscount); for(int j=0; j< SpDCCols::esscount; ++j) ess[j] = ARecvSizes[j][i]; // essentials of the ith matrix in this row ARecv = new SpDCCols(); // first, create the object } SpParHelper::BCastMatrix(CMG.rowWorld, *ARecv, ess, i); // then, receive its elements ess.clear(); if(i == Bself) BRecv = &SplitB; // shallow-copy else { ess.resize(SpDCCols::esscount); for(int j=0; j< SpDCCols::esscount; ++j) { ess[j] = BRecvSizes[j][i]; } BRecv = new SpDCCols(); } SpParHelper::BCastMatrix(CMG.colWorld, *BRecv, ess, i); // then, receive its elements comm_bcast += (MPI_Wtime() - bcast_beg); double summa_beg = MPI_Wtime(); SpTuples * C_cont; if(threaded) { C_cont = LocalSpGEMM (*ARecv, *BRecv, // parameters themselves i != Aself, // 'delete A' condition i != Bself); // 'delete B' condition } else { C_cont = MultiplyReturnTuples (*ARecv, *BRecv, // parameters themselves false, isBT, // transpose information (B is transposed) i != Aself, // 'delete A' condition i != Bself); // 'delete B' condition } comp_summa += (MPI_Wtime() - summa_beg); C.push_back(C_cont); } SpHelper::deallocate2D(ARecvSizes, SpDCCols::esscount); SpHelper::deallocate2D(BRecvSizes, SpDCCols::esscount); } } #endif CombBLAS_beta_16_2/3DSpGEMM/Makefile-edison000644 000765 000024 00000002737 13220066202 021564 0ustar00aydinbulucstaff000000 000000 # C++ compiler CXX := CC CXXFLAGS := -std=c++11 -O2 -DNDEBUG -fopenmp #-DDEBUG # C compiler CC := cc CFLAGS := -O2 COMBBLAS = .. $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator %.o : %.c $(CC) $(CFLAGS) -o $@ -c $< %.o : %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< %.o : %.cxx $(CXX) $(CXXFLAGS) -o $@ -c $< all: mpipspgemm test_mpipspgemm RestrictionOp clean: rm -rf mpipspgemm rm -f *.o rm -rf ../*.o rm -f ../graph500-1.2/generator/*.o rm -f ../graph500-1.2/generator/libgraph_generator_seq.a SUMMALayer.o: SUMMALayer.cpp mtSpGEMM.o $(CXX) $(CXXFLAGS) -o $@ -c $< mpipspgemm: mpipspgemm.o SplitMatDist.h Reductions.h SUMMALayer.h $(COMBBLAS)/MPIType.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(COMBBLAS)/mmio.o $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq test_mpipspgemm: test_mpipspgemm.o SplitMatDist.h Reductions.h SUMMALayer.h $(COMBBLAS)/MPIType.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(COMBBLAS)/mmio.o $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq RestrictionOp: RestrictionOp.o SplitMatDist.h Reductions.h SUMMALayer.h $(COMBBLAS)/MPIType.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(COMBBLAS)/mmio.o $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq CombBLAS_beta_16_2/3DSpGEMM/CCGrid.h000644 000765 000024 00000003073 13212627374 020121 0ustar00aydinbulucstaff000000 000000 #ifndef _CC_GRID_ #define _CC_GRID_ namespace combblas { class CCGrid { public: CCGrid(int c_factor, int gr_cols): GridLayers(c_factor), GridCols(gr_cols), GridRows(gr_cols) { MPI_Comm_rank(MPI_COMM_WORLD,&myrank); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); layer_grid = myrank % c_factor; /* RankInFiber = layer_grid, indexed from 1 to c_factor */ RankInLayer = myrank / c_factor; /* indexed from 1 to layer_length */ RankInCol = RankInLayer / GridCols; /* RankInCol = MYPROCROW */ RankInRow = RankInLayer % GridCols; /* RankInRow = MYPROCCOL */ // MPI_Comm_split(MPI_Comm comm, int color, int key, MPI_Comm *newcomm) MPI_Comm_split(MPI_COMM_WORLD, layer_grid, RankInLayer, &layerWorld); MPI_Comm_split(MPI_COMM_WORLD, RankInLayer, layer_grid, &fiberWorld); MPI_Comm_split(MPI_COMM_WORLD, layer_grid * GridRows + RankInLayer / GridRows, RankInRow, &rowWorld); MPI_Comm_split(MPI_COMM_WORLD, layer_grid * GridCols + RankInLayer % GridRows, RankInCol, &colWorld); #ifdef DEBUG printf("Rank %d maps to layer %d (rankinlayer: %d), row %d, and col %d\n", myrank, layer_grid, RankInLayer, RankInCol, RankInRow); #endif }; int nprocs; int myrank; int GridRows; int GridCols; int GridLayers; // GridLayers = c_factor int RankInRow; int RankInCol; int RankInLayer; int layer_grid; // layer_grid = RankInFiber MPI_Comm layerWorld; MPI_Comm fiberWorld; MPI_Comm rowWorld; MPI_Comm colWorld; }; } #endif CombBLAS_beta_16_2/3DSpGEMM/CMakeLists.txt000644 000765 000024 00000000273 13271404146 021407 0ustar00aydinbulucstaff000000 000000 add_executable(mpipspgemm mpipspgemm.cpp) target_link_libraries(mpipspgemm CombBLAS) add_executable(test_mpipspgemm test_mpipspgemm.cpp) target_link_libraries(test_mpipspgemm CombBLAS) CombBLAS_beta_16_2/3DSpGEMM/._.DS_Store000644 000765 000024 00000000170 13271513672 020550 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/3DSpGEMM/.DS_Store000644 000765 000024 00000014004 13271513672 020334 0ustar00aydinbulucstaff000000 000000 Bud1%  @€ @€ @€ @ E%DSDB`€ @€ @€ @CombBLAS_beta_16_2/3DSpGEMM/Glue.h000644 000765 000024 00000000364 13212627374 017722 0ustar00aydinbulucstaff000000 000000 extern double comm_bcast; extern double comm_reduce; extern double comm_split; extern double comp_summa; extern double comp_reduce; extern double comp_reduce_layer; extern double comp_result; extern double comp_trans; extern double comp_split; CombBLAS_beta_16_2/3DSpGEMM/RestrictionOp.cpp000644 000765 000024 00000017357 13271404146 022172 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" #include "Glue.h" #include "CCGrid.h" #include "Reductions.h" #include "Multiplier.h" #include "SplitMatDist.h" #include "RestrictionOp.h" using namespace std; using namespace combblas; double comm_bcast; double comm_reduce; double comp_summa; double comp_reduce; double comp_result; double comp_reduce_layer; double comp_split; double comp_trans; double comm_split; #define ITERS 5 int main(int argc, char *argv[]) { int provided; //MPI_Init_thread(&argc, &argv, MPI_THREAD_SINGLE, &provided); MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 6) { if(myrank == 0) { printf("Usage (random): ./mpipspgemm \n"); printf("Usage (input): ./mpipspgemm \n"); //TODO: not meaningful here. Need to remove it. Still there because current scripts execute without error. printf("Example: ./RestrictionOp 4 4 2 ER 19 16 \n"); printf("Example: ./RestrictionOp 4 4 2 Input matA.mtx\n"); printf("Type ER: Erdos-Renyi\n"); printf("Type SSCA: R-MAT with SSCA benchmark parameters\n"); printf("Type G500: R-MAT with Graph500 benchmark parameters\n"); } return -1; } unsigned GRROWS = (unsigned) atoi(argv[1]); unsigned GRCOLS = (unsigned) atoi(argv[2]); unsigned C_FACTOR = (unsigned) atoi(argv[3]); CCGrid CMG(C_FACTOR, GRCOLS); int nthreads; #pragma omp parallel { nthreads = omp_get_num_threads(); } if(GRROWS != GRCOLS) { SpParHelper::Print("This version of the Combinatorial BLAS only works on a square logical processor grid\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Abort(MPI_COMM_WORLD, 1); } int layer_length = GRROWS*GRCOLS; if(layer_length * C_FACTOR != nprocs) { SpParHelper::Print("The product of does not match the number of processes\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Abort(MPI_COMM_WORLD, 1); } { SpDCCols *A; string type; shared_ptr layerGrid; layerGrid.reset( new CommGrid(CMG.layerWorld, 0, 0) ); FullyDistVec p(layerGrid); // permutation vector defined on layers if(string(argv[4]) == string("input")) // input option { string fileA(argv[5]); double t01 = MPI_Wtime(); A = ReadMat(fileA, CMG, true, p); if(myrank == 0) cout << "Input matrix read : time " << MPI_Wtime() - t01 << endl; } else { unsigned scale = (unsigned) atoi(argv[5]); unsigned EDGEFACTOR = (unsigned) atoi(argv[6]); double initiator[4]; if(string(argv[4]) == string("ER")) { initiator[0] = .25; initiator[1] = .25; initiator[2] = .25; initiator[3] = .25; } else if(string(argv[4]) == string("G500")) { initiator[0] = .57; initiator[1] = .19; initiator[2] = .19; initiator[3] = .05; EDGEFACTOR = 16; } else if(string(argv[4]) == string("SSCA")) { initiator[0] = .6; initiator[1] = .4/3; initiator[2] = .4/3; initiator[3] = .4/3; EDGEFACTOR = 8; } else { if(myrank == 0) printf("The initiator parameter - %s - is not recognized.\n", argv[5]); MPI_Abort(MPI_COMM_WORLD, 1); } double t01 = MPI_Wtime(); A = GenMat(CMG, scale, EDGEFACTOR, initiator, true); if(myrank == 0) cout << "RMATs Generated : time " << MPI_Wtime() - t01 << endl; } SpDCCols* R; SpDCCols* RT; if(myrank == 0) cout << "Computing restriction matrix \n"; double t01 = MPI_Wtime(); RestrictionOp( CMG, A, R, RT); if(myrank == 0) cout << "Restriction Op computed : time " << MPI_Wtime() - t01 << endl; SpDCCols *B = new SpDCCols(*A); // just a deep copy of A SpDCCols splitA, splitB, splitR, splitRT; SpDCCols *splitRTA; SpDCCols *splitRTAR; SpDCCols *splitC; SplitMat(CMG, A, splitA, true); SplitMat(CMG, B, splitB, false); SplitMat(CMG, R, splitR, true); SplitMat(CMG, RT, splitRT, false); if(myrank == 0) { printf("\n Processor Grid (row x col x layers x threads): %dx%dx%dx%d \n", CMG.GridRows, CMG.GridCols, CMG.GridLayers, nthreads); printf(" prow pcol layer thread comm_bcast comm_scatter comp_summa comp_merge comp_scatter comp_result other total\n"); } SpParHelper::Print("Computing A square\n"); splitC = multiply(splitB, splitA, CMG, false, true); // A^2 delete splitC; splitC = multiply(splitB, splitA, CMG, false, true); // A^2 SpParHelper::Print("Computing RTA\n"); splitRTA = multiply(splitRT, splitA, CMG, false, true); delete splitRTA; splitRTA = multiply(splitRT, splitA, CMG, false, true); SpParHelper::Print("Computing RTAR\n"); splitRTAR = multiply(*splitRTA, splitR, CMG, false, true); delete splitRTAR; splitRTAR = multiply(*splitRTA, splitR, CMG, false, true); // count nnz int64_t nnzA=0, nnzR=0, nnzC=0, nnzRTA=0, nnzRTAR=0; int64_t localnnzA = splitA.getnnz(); int64_t localnnzR = splitR.getnnz(); int64_t localnnzC = splitC->getnnz(); int64_t localnnzRTA = splitRTA->getnnz(); int64_t localnnzRTAR = splitRTAR->getnnz(); MPI_Allreduce( &localnnzA, &nnzA, 1, MPIType(), MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce( &localnnzR, &nnzR, 1, MPIType(), MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce( &localnnzC, &nnzC, 1, MPIType(), MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce( &localnnzRTA, &nnzRTA, 1, MPIType(), MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce( &localnnzRTAR, &nnzRTAR, 1, MPIType(), MPI_SUM, MPI_COMM_WORLD); if(myrank == 0) { cout << "----------------------------\n"; cout << " nnz(A)= " << nnzA << endl; cout << " nnz(R)= " << nnzR << endl; cout << " nnz(A^2)= " << nnzC << endl; cout << " nnz(RTA)= " << nnzRTA << endl; cout << " nnz(RTAR)= " << nnzRTAR << endl; cout << "----------------------------\n"; } delete splitC; delete splitRTA; delete splitRTAR; } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/3DSpGEMM/SplitMatDist.h000644 000765 000024 00000017327 13271404146 021411 0ustar00aydinbulucstaff000000 000000 #ifndef _SPLIT_MAT_DIST_H_ #define _SPLIT_MAT_DIST_H_ #include #include #include #include #include #include #include #include #include // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #include "CombBLAS/CombBLAS.h" #include "Glue.h" #include "CCGrid.h" namespace combblas { template SpDCCols * ReadMat(std::string filename, CCGrid & CMG, bool permute, FullyDistVec& p) { double t01 = MPI_Wtime(); double t02; if(CMG.layer_grid == 0) { std::shared_ptr layerGrid; layerGrid.reset( new CommGrid(CMG.layerWorld, 0, 0) ); SpParMat < IT, NT, SpDCCols > *A = new SpParMat < IT, NT, SpDCCols >(layerGrid); SpParHelper::Print("Reading input file....\n"); A->ParallelReadMM(filename, true, maximum()); A->PrintInfo(); std::ostringstream tinfo; t02 = MPI_Wtime(); tinfo << "Reader took " << t02-t01 << " seconds" << std::endl; SpParHelper::Print(tinfo.str()); // random permutations for load balance if(permute) { if(A->getnrow() == A->getncol()) { if(p.TotalLength()!=A->getnrow()) { SpParHelper::Print("Generating random permutation vector.\n"); p.iota(A->getnrow(), 0); p.RandPerm(); } SpParHelper::Print("Perfoming random permuation of matrix.\n"); (*A)(p,p,true);// in-place permute to save memory std::ostringstream tinfo1; tinfo1 << "Permutation took " << MPI_Wtime()-t02 << " seconds" << std::endl; SpParHelper::Print(tinfo1.str()); } else { SpParHelper::Print("nrow != ncol. Can not apply symmetric permutation.\n"); } } float balance = A->LoadImbalance(); std::ostringstream outs; outs << "Input load balance: " << balance << std::endl; SpParHelper::Print(outs.str()); return A->seqptr(); } else return new SpDCCols(); } template SpDCCols * GenMat(CCGrid & CMG, unsigned scale, unsigned EDGEFACTOR, double initiator[4], bool permute) { double t01 = MPI_Wtime(); double t02; if(CMG.layer_grid == 0) { DistEdgeList * DEL = new DistEdgeList(CMG.layerWorld); std::ostringstream minfo; int nprocs = DEL->commGrid->GetSize(); minfo << "Started Generation of scale "<< scale << std::endl; minfo << "Using " << nprocs << " MPI processes" << std::endl; SpParHelper::Print(minfo.str()); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, false ); SpParHelper::Print("Generated renamed edge lists\n"); std::ostringstream tinfo; t02 = MPI_Wtime(); tinfo << "Generation took " << t02-t01 << " seconds" << std::endl; SpParHelper::Print(tinfo.str()); SpParMat < IT, NT, SpDCCols > *A = new SpParMat < IT, NT, SpDCCols >(*DEL, false); delete DEL; SpParHelper::Print("Created Sparse Matrix\n"); A->PrintInfo(); if(permute) { SpParHelper::Print("Perfoming random permuation of matrix.\n"); std::shared_ptr layerGrid; layerGrid.reset( new CommGrid(CMG.layerWorld, 0, 0) ); FullyDistVec p(layerGrid); // permutation vector defined on layers p.iota(A->getnrow(), 0); p.RandPerm(); (*A)(p,p,true);// in-place permute to save memory std::ostringstream tinfo1; tinfo1 << "Permutation took " << MPI_Wtime()-t02 << " seconds" << std::endl; SpParHelper::Print(tinfo1.str()); } float balance = A->LoadImbalance(); std::ostringstream outs; outs << "Load balance: " << balance << std::endl; SpParHelper::Print(outs.str()); return A->seqptr(); } else return new SpDCCols(); } /** ** \param[in] rowsplit {split along the row? true for B matrix} ** \param[out] splitmat {split a matrix from layer 0 into CMG.GridLayers pieces} **/ template void SplitMat(CCGrid & CMG, SpDCCols * localmat, SpDCCols & splitmat, bool rowsplit=false) { double t01 = MPI_Wtime(); std::vector vecEss; // at layer_grid=0, this will have [CMG.GridLayers * SpDCCols::esscount] entries std::vector< SpDCCols > partsmat; // only valid at layer_grid=0 int nparts = CMG.GridLayers; if(CMG.layer_grid == 0) { double split_beg = MPI_Wtime(); if(rowsplit && nparts>1) localmat->Transpose(); // local rowsplit is performaned local transpose and ColSplit localmat->ColSplit(nparts, partsmat); // split matrices are emplaced-back into partsmat vector, localmat destroyed for(int i=0; i< nparts; ++i) { std::vector ess = partsmat[i].GetEssentials(); for(auto itr = ess.begin(); itr != ess.end(); ++itr) { vecEss.push_back(*itr); } } comp_split += (MPI_Wtime() - split_beg); } double scatter_beg = MPI_Wtime(); // timer on int esscnt = SpDCCols::esscount; // necessary cast for MPI std::vector myess(esscnt); MPI_Scatter(vecEss.data(), esscnt, MPIType(), myess.data(), esscnt, MPIType(), 0, CMG.fiberWorld); if(CMG.layer_grid == 0) // senders { splitmat = partsmat[0]; // just copy the local split for(int recipient=1; recipient< nparts; ++recipient) // scatter the others { int tag = 0; Arr arrinfo = partsmat[recipient].GetArrays(); for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { // MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) MPI_Send(arrinfo.indarrs[i].addr, arrinfo.indarrs[i].count, MPIType(), recipient, tag++, CMG.fiberWorld); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { MPI_Send(arrinfo.numarrs[i].addr, arrinfo.numarrs[i].count, MPIType(), recipient, tag++, CMG.fiberWorld); } } } else // receivers { splitmat.Create(myess); // allocate memory for arrays Arr arrinfo = splitmat.GetArrays(); int tag = 0; for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { MPI_Recv(arrinfo.indarrs[i].addr, arrinfo.indarrs[i].count, MPIType(), 0, tag++, CMG.fiberWorld, MPI_STATUS_IGNORE); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { MPI_Recv(arrinfo.numarrs[i].addr, arrinfo.numarrs[i].count, MPIType(), 0, tag++, CMG.fiberWorld, MPI_STATUS_IGNORE); } } comm_split += (MPI_Wtime() - scatter_beg); if(rowsplit && nparts>1) splitmat.Transpose(); //transpose back after row-splitting std::ostringstream tinfo; tinfo << "Matrix split and distributed along layers: time " << MPI_Wtime()-t01 << " seconds" << std::endl; SpParHelper::Print(tinfo.str()); } } #endif CombBLAS_beta_16_2/3DSpGEMM/Reductions.h000644 000765 000024 00000012733 13271404146 021143 0ustar00aydinbulucstaff000000 000000 #ifndef _REDUCTIONS_H_ #define _REDUCTIONS_H_ #include #include #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" #include "Glue.h" #include "CCGrid.h" namespace combblas { /*************************************************************************** * Distribute a local m/sqrt(p) x n/sqrt(p) matrix (represented by a list of tuples) across layers * so that a each processor along the third dimension receives m/sqrt(p) x n/(c*sqrt(p)) submatrices. * After receiving c submatrices, they are merged to create one m/sqrt(p) x n/(c*sqrt(p)) matrix. * Assumption: input tuples are deleted * Inputs: * fibWorld: Communicator along the third dimension * localmerged: input array of tuples, which will be distributed across layers * Output: output array of tuples, after distributing across layers and merging locally in the received processor * ***************************************************************************/ template SpTuples * ParallelReduce_Alltoall_threaded(MPI_Comm & fibWorld, SpTuples * & localmerged) { double comp_begin, comm_begin, comp_time=0, comm_time=0; int fprocs, fibrank; MPI_Comm_size(fibWorld,&fprocs); MPI_Comm_rank(fibWorld,&fibrank); IT mdim = localmerged->getnrow(); IT ndim = localmerged->getncol(); if(fprocs == 1) { return localmerged; } // ------------ find splitters to distributed across layers ----------- comp_begin = MPI_Wtime(); std::vector send_sizes(fprocs); std::vector recv_sizes(fprocs); std::vector recv_offsets(fprocs); std::vector send_offsets = findColSplitters(localmerged, fprocs); for(int i=0; i), MPI_CHAR, &MPI_triple); MPI_Type_commit(&MPI_triple); // ------------ Allocate memory to receive data ----------- comp_begin = MPI_Wtime(); int recv_count = 0; for( int i = 0; i < fprocs; i++ ) { recv_count += recv_sizes[i]; } std::tuple * recvbuf = static_cast*> (::operator new (sizeof(std::tuple[recv_count]))); recv_offsets[0] = 0; for( int i = 1; i < fprocs; i++ ) { recv_offsets[i] = recv_offsets[i-1]+recv_sizes[i-1]; } comp_time += (MPI_Wtime() - comp_begin); // ------------ Communicate split tuples ----------- comm_begin = MPI_Wtime(); MPI_Alltoallv( localmerged->tuples, send_sizes.data(), send_offsets.data(), MPI_triple, recvbuf, recv_sizes.data(), recv_offsets.data(), MPI_triple, fibWorld); // WARNING: is this big enough? comm_time += (MPI_Wtime() - comm_begin); // -------- update column indices of split tuples ---------- comp_begin = MPI_Wtime(); IT ndimSplit = ndim/fprocs; if(fibrank==(fprocs-1)) ndimSplit = ndim - ndimSplit * fibrank; IT coloffset = fibrank * ndimSplit; #pragma omp parallel for for(int k=0; k(recvbuf[k]) = std::get<1>(recvbuf[k]) - coloffset; } // -------- create vector of SpTuples for MultiwayMerge ---------- std::vector< SpTuples* > lists; for(int i=0; i< fprocs; ++i) { SpTuples* spTuples = new SpTuples (recv_sizes[i], mdim, ndimSplit, &recvbuf[recv_offsets[i]], true); // If needed pass an empty object of proper dimension lists.push_back(spTuples); } // -------- merge received tuples ---------- SpTuples * globalmerged = MultiwayMerge(lists, mdim, ndimSplit, false); comp_time += (MPI_Wtime() - comp_begin); comp_reduce_layer += comp_time; comm_reduce += comm_time; ::operator delete(recvbuf); delete localmerged; // not sure if we can call ::operator delete here return globalmerged; } template SpDCCols * ReduceAll_threaded(std::vector< SpTuples* > & unreducedC, CCGrid & CMG) { typedef PlusTimesSRing PTDD; IT mdim = unreducedC[0]->getnrow(); IT ndim = unreducedC[0]->getncol(); // ------ merge list of tuples from n/sqrt(p) stages of SUMMA ------- double loc_beg1 = MPI_Wtime(); //SpTuples* localmerged = multiwayMerge(unreducedC, true); SpTuples* localmerged = MultiwayMerge(unreducedC, mdim, ndim, true); comp_reduce += (MPI_Wtime() - loc_beg1); // scatter local tuples across layers SpTuples * mergedSpTuples = ParallelReduce_Alltoall_threaded(CMG.fiberWorld, localmerged); loc_beg1 = MPI_Wtime(); // TODO: this is not a good constructor. Change it back to SpTuple-based constructor SpDCCols * reducedC = new SpDCCols(mergedSpTuples->getnrow(), mergedSpTuples->getncol(), mergedSpTuples->getnnz(), mergedSpTuples->tuples, false); comp_result += (MPI_Wtime() - loc_beg1); delete mergedSpTuples; // too expensive return reducedC; } } #endif CombBLAS_beta_16_2/3DSpGEMM/OldReductions.h000644 000765 000024 00000016035 13212627374 021606 0ustar00aydinbulucstaff000000 000000 /* * This file contains current obsolete functions * Kept for use during future needs (if any) */ #ifndef _OLD_REDUCTIONS_H_ #define _OLD_REDUCTIONS_H_ // localmerged is invalidated in all processes after this redursive function // globalmerged is valid only in fibWorld root (0) upon exit template void ParallelReduce(MPI_Comm & fibWorld, tuple * & localmerged, MPI_Datatype & MPI_triple, tuple * & globalmerged, int inputnnz, int & outputnnz) { int fprocs, frank; MPI_Comm_size(fibWorld,&fprocs); MPI_Comm_rank(fibWorld,&frank); if(fprocs == 1) { globalmerged = localmerged; localmerged = NULL; outputnnz = inputnnz; return; } else if(fprocs % 2 != 0) { SpParHelper::Print("Not even sized neighbors, can't merge\n"); return; } int color = frank % 2; int key = frank / 2; MPI_Comm halfWorld; MPI_Comm_split(fibWorld, color, key, &halfWorld); // odd-even split if(color == 0) // even numbered - received { MPI_Status status; int hissize = 0; MPI_Recv(&hissize, 1, MPI_INT, frank+1, 1, fibWorld, &status); tuple * recvdata = new tuple[hissize]; double reduce_beg = MPI_Wtime(); MPI_Recv(recvdata, hissize, MPI_triple, frank+1, 1, fibWorld, &status); comm_reduce += (MPI_Wtime() - reduce_beg); int i=0, j=0, k = 0; tuple * mergeddata = new tuple[inputnnz + hissize]; while(i < inputnnz && j < hissize) { // both data are in ascending order w.r.t. first columns then rows if(get<1>(localmerged[i]) > get<1>(recvdata[j])) { mergeddata[k] = recvdata[j++]; } else if(get<1>(localmerged[i]) < get<1>(recvdata[j])) { mergeddata[k] = localmerged[i++]; } else // columns are equal { if(get<0>(localmerged[i]) > get<0>(recvdata[j])) { mergeddata[k] = recvdata[j++]; } else if(get<0>(localmerged[i]) < get<0>(recvdata[j])) { mergeddata[k] = localmerged[i++]; } else // everything equal { mergeddata[k] = make_tuple(get<0>(localmerged[i]), get<1>(recvdata[j]), SR::add(get<2>(recvdata[j]), get<2>(localmerged[i]))); ++i; ++j; } } ++k; // in any case, one more entry added to result } delete [] recvdata; delete [] localmerged; localmerged = NULL; return ParallelReduce(halfWorld, mergeddata, MPI_triple, globalmerged, k, outputnnz); // k is the new input nnz } else // odd numbered - sender (does not recurse further) { MPI_Send(&inputnnz, 1, MPI_INT, frank-1, 1, fibWorld); MPI_Send(localmerged, inputnnz, MPI_triple, frank-1, 1, fibWorld); delete [] localmerged; localmerged = NULL; } } // localmerged is invalidated in all processes after this redursive function // globalmerged is valid on all processes upon exit template void ParallelReduce_Alltoall(MPI_Comm & fibWorld, tuple * & localmerged, MPI_Datatype & MPI_triple, tuple * & globalmerged, IT inputnnz, IT & outputnnz, IT ncols) { int fprocs; MPI_Comm_size(fibWorld,&fprocs); if(fprocs == 1) { globalmerged = localmerged; localmerged = NULL; outputnnz = inputnnz; return; } int send_sizes[fprocs]; int recv_sizes[fprocs]; // this could be made more efficient, either by a binary search or by guessing then correcting //MPI_Barrier(MPI_COMM_WORLD); double loc_beg1 = MPI_Wtime(); int target = 0; int cols_per_proc = (ncols + fprocs - 1) / fprocs; int split_point = cols_per_proc; int send_offsets[fprocs+1]; send_offsets[0] = 0; for( int i = 0; i < inputnnz; i++ ) { if( std::get<1>(localmerged[i]) >= split_point ) { send_offsets[++target] = i; split_point += cols_per_proc; } } while(target < fprocs) send_offsets[++target] = inputnnz; for(int i=0; i(localmerged[i]) >= split_point ) { if( target == 0 ) send_sizes[target] = i; else { send_sizes[target] = i-send_offsets[target]; } send_offsets[target+1] = i; target++; split_point += cols_per_proc; } } send_sizes[fprocs-1] = inputnnz - send_offsets[fprocs-1]; */ //MPI_Barrier(MPI_COMM_WORLD); //comp_reduce += (MPI_Wtime() - loc_beg1); double reduce_beg = MPI_Wtime(); MPI_Alltoall( send_sizes, 1, MPI_INT, recv_sizes, 1, MPI_INT,fibWorld); //MPI_Barrier(MPI_COMM_WORLD); comm_reduce += (MPI_Wtime() - reduce_beg); int recv_count = 0; for( int i = 0; i < fprocs; i++ ) recv_count += recv_sizes[i]; tuple *recvbuf = new tuple[recv_count]; int recv_offsets[fprocs]; recv_offsets[0] = 0; for( int i = 1; i < fprocs; i++ ) { recv_offsets[i] = recv_offsets[i-1]+recv_sizes[i-1]; } //MPI_Barrier(MPI_COMM_WORLD); reduce_beg = MPI_Wtime(); MPI_Alltoallv( localmerged, send_sizes, send_offsets, MPI_triple, recvbuf, recv_sizes, recv_offsets, MPI_triple, fibWorld); //MPI_Barrier(MPI_COMM_WORLD); comm_reduce += (MPI_Wtime() - reduce_beg); loc_beg1 = MPI_Wtime(); int pos[fprocs]; for( int i = 0; i < fprocs; i++ ) pos[i] = recv_offsets[i]; outputnnz = 0; globalmerged = new tuple[recv_count]; while( true ) { // find the next entry int nexti = -1; int r = INT_MAX; int c = INT_MAX; for( int i = 0; i < fprocs; i++ ) { if( pos[i] < recv_offsets[i]+recv_sizes[i] ) { if( std::get<1>(recvbuf[pos[i]]) < c ) { c = std::get<1>(recvbuf[pos[i]]); r = std::get<0>(recvbuf[pos[i]]); nexti = i; } else if( (std::get<1>(recvbuf[pos[i]]) == c) && (std::get<0>(recvbuf[pos[i]]) < r) ) { r = std::get<0>(recvbuf[pos[i]]); nexti = i; } } } if( nexti == -1 ) // merge is finished break; if( outputnnz > 0 && std::get<0>(globalmerged[outputnnz-1]) == std::get<0>(recvbuf[pos[nexti]]) && std::get<1>(globalmerged[outputnnz-1]) == std::get<1>(recvbuf[pos[nexti]]) ) // add this one to the previous std::get<2>(globalmerged[outputnnz-1]) = SR::add( std::get<2>(globalmerged[outputnnz-1]), std::get<2>(recvbuf[pos[nexti]]) ); else { // make this the next entry in the output globalmerged[outputnnz] = recvbuf[pos[nexti]]; outputnnz++; } pos[nexti]++; // it was a bug since it was placed before the if statement } //MPI_Barrier(MPI_COMM_WORLD); //comp_reduce += (MPI_Wtime() - loc_beg1); delete [] recvbuf; delete [] localmerged; localmerged = NULL; } #endif CombBLAS_beta_16_2/3DSpGEMM/ReadMatDist.h000644 000765 000024 00000011331 13271404146 021156 0ustar00aydinbulucstaff000000 000000 #ifndef _READ_MAT_DIST_H_ #define _READ_MAT_DIST_H_ #include #include #include #include #include #include #include #include #include // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #include "CombBLAS/CombBLAS.h" #include "Glue.h" namespace combblas { template void Symmetricize(PARMAT & A) { PARMAT AT = A; AT.Transpose(); AT.RemoveLoops(); // needed for non-boolean matrix A += AT; } /** ** \param[out] splitmat {read matrix market file into layer 0, and split into CMG.GridLayers pieces} **/ template void Reader(string filename, CCGrid & CMG, SpDCCols & splitmat, bool trans, bool permute, FullyDistVec& p) { std::vector vecEss; // at layer_grid=0, this will have [CMG.GridLayers * SpDCCols::esscount] entries std::vector< SpDCCols > partsmat; // only valid at layer_grid=0 int nparts = CMG.GridLayers; if(CMG.layer_grid == 0) { //SpDCCols * localmat = GenRMat(scale, EDGEFACTOR, initiator, CMG.layerWorld); shared_ptr layerGrid; layerGrid.reset( new CommGrid(CMG.layerWorld, 0, 0) ); SpParMat < IT, NT, SpDCCols > *A = new SpParMat < IT, NT, SpDCCols >(layerGrid); //A->ReadDistribute(filename, 0, false); A->ParallelReadMM(filename); // random permutations for load balance if(permute) { if(A->getnrow() == A->getncol()) { if(p.TotalLength()!=A->getnrow()) { SpParHelper::Print("Generating random permutation vector.\n"); p.iota(A->getnrow(), 0); p.RandPerm(); } (*A)(p,p,true);// in-place permute to save memory } else { SpParHelper::Print("nrow != ncol. Can not apply symmetric permutation.\n"); } } SpDCCols * localmat = A->seqptr(); double trans_beg = MPI_Wtime(); if(trans) localmat->Transpose(); // locally transpose comp_trans += (MPI_Wtime() - trans_beg); double split_beg = MPI_Wtime(); localmat->ColSplit(nparts, partsmat); // split matrices are emplaced-back into partsmat vector, localmat destroyed for(int i=0; i< nparts; ++i) { std::vector ess = partsmat[i].GetEssentials(); for(auto itr = ess.begin(); itr != ess.end(); ++itr) { vecEss.push_back(*itr); } } comp_split += (MPI_Wtime() - split_beg); } double scatter_beg = MPI_Wtime(); // timer on int esscnt = SpDCCols::esscount; // necessary cast for MPI std::vector myess(esscnt); MPI_Scatter(vecEss.data(), esscnt, MPIType(), myess.data(), esscnt, MPIType(), 0, CMG.fiberWorld); if(CMG.layer_grid == 0) // senders { splitmat = partsmat[0]; // just copy the local split for(int recipient=1; recipient< nparts; ++recipient) // scatter the others { int tag = 0; Arr arrinfo = partsmat[recipient].GetArrays(); for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { // MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) MPI_Send(arrinfo.indarrs[i].addr, arrinfo.indarrs[i].count, MPIType(), recipient, tag++, CMG.fiberWorld); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { MPI_Send(arrinfo.numarrs[i].addr, arrinfo.numarrs[i].count, MPIType(), recipient, tag++, CMG.fiberWorld); } } } else // receivers { splitmat.Create(myess); // allocate memory for arrays Arr arrinfo = splitmat.GetArrays(); int tag = 0; for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { MPI_Recv(arrinfo.indarrs[i].addr, arrinfo.indarrs[i].count, MPIType(), 0, tag++, CMG.fiberWorld, MPI_STATUS_IGNORE); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { MPI_Recv(arrinfo.numarrs[i].addr, arrinfo.numarrs[i].count, MPIType(), 0, tag++, CMG.fiberWorld, MPI_STATUS_IGNORE); } } comm_split += (MPI_Wtime() - scatter_beg); } } #endif CombBLAS_beta_16_2/3DSpGEMM/test_mpipspgemm.cpp000644 000765 000024 00000011215 13271404146 022566 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" #include "Glue.h" #include "CCGrid.h" #include "Reductions.h" #include "Multiplier.h" #include "SplitMatDist.h" using namespace std; using namespace combblas; double comm_bcast; double comm_reduce; double comp_summa; double comp_reduce; double comp_result; double comp_reduce_layer; double comp_split; double comp_trans; double comm_split; #define ITERS 1 int main(int argc, char *argv[]) { int provided; //MPI_Init_thread(&argc, &argv, MPI_THREAD_SINGLE, &provided); MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc != 8) { cout << argc << endl; if(myrank == 0) { printf("Usage (input): ./mpipspgemm \n"); printf("Example: ./mpipspgemm 4 4 2 matA.mtx matB.mtx matB.mtx threaded\n"); printf("algo: outer | column | threaded \n"); } return -1; } unsigned GRROWS = (unsigned) atoi(argv[1]); unsigned GRCOLS = (unsigned) atoi(argv[2]); unsigned C_FACTOR = (unsigned) atoi(argv[3]); CCGrid CMG(C_FACTOR, GRCOLS); if(GRROWS != GRCOLS) { SpParHelper::Print("This version of the Combinatorial BLAS only works on a square logical processor grid\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Abort(MPI_COMM_WORLD, 1); } int layer_length = GRROWS*GRCOLS; if(layer_length * C_FACTOR != nprocs) { SpParHelper::Print("The product of does not match the number of processes\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Abort(MPI_COMM_WORLD, 1); } SpDCCols splitA, splitB, controlC; SpDCCols *splitC; string type; string fileA(argv[4]); string fileB(argv[5]); string fileC(argv[6]); { shared_ptr layerGrid; layerGrid.reset( new CommGrid(CMG.layerWorld, 0, 0) ); FullyDistVec p(layerGrid); // permutation vector defined on layers double t01 = MPI_Wtime(); SpDCCols *A = ReadMat(fileA, CMG, true, p); SpDCCols *B = ReadMat(fileB, CMG, true, p); SpDCCols *C = ReadMat(fileC, CMG, true, p); SplitMat(CMG, A, splitA, false); SplitMat(CMG, B, splitB, true); SplitMat(CMG, C, controlC, false); if(myrank == 0) cout << "Matrices read and replicated along layers : time " << MPI_Wtime() - t01 << endl; type = string(argv[7]); if(type == string("outer")) { for(int k=0; k #include #include #include #include #include #include #include #include // These macros should be defined before stdint.h is included #ifndef __STDC_CONSTANT_MACROS #define __STDC_CONSTANT_MACROS #endif #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS #endif #include #include "CombBLAS/CombBLAS.h" #include "Glue.h" namespace combblas { template SpDCCols * GenRMat(unsigned scale, unsigned EDGEFACTOR, double initiator[4], MPI_Comm & layerworld, bool scramble) { double t01 = MPI_Wtime(); double t02; DistEdgeList * DEL = new DistEdgeList(layerworld); ostringstream minfo; int nprocs = DEL->commGrid->GetSize(); minfo << "Started Generation of scale "<< scale << endl; minfo << "Using " << nprocs << " MPI processes" << endl; SpParHelper::Print(minfo.str()); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, scramble, false ); // don't generate packed edges, that function uses MPI_COMM_WORLD which can not be used in a single layer! SpParHelper::Print("Generated renamed edge lists\n"); ostringstream tinfo; t02 = MPI_Wtime(); tinfo << "Generation took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); SpParMat < IT, NT, SpDCCols > *A = new SpParMat < IT, NT, SpDCCols >(*DEL, false); delete DEL; SpParHelper::Print("Created Sparse Matrix\n"); float balance = A->LoadImbalance(); ostringstream outs; outs << "Load balance: " << balance << endl; SpParHelper::Print(outs.str()); return A->seqptr(); } /** ** \param[out] splitmat {generated RMAT matrix, split into CMG.GridLayers pieces} **/ template void Generator(unsigned scale, unsigned EDGEFACTOR, double initiator[4], CCGrid & CMG, SpDCCols & splitmat, bool trans, bool scramble) { std::vector vecEss; // at layer_grid=0, this will have [CMG.GridLayers * SpDCCols::esscount] entries std::vector< SpDCCols > partsmat; // only valid at layer_grid=0 int nparts = CMG.GridLayers; if(CMG.layer_grid == 0) { SpDCCols * localmat = GenRMat(scale, EDGEFACTOR, initiator, CMG.layerWorld, scramble); double trans_beg = MPI_Wtime(); if(trans) localmat->Transpose(); // locally transpose comp_trans += (MPI_Wtime() - trans_beg); double split_beg = MPI_Wtime(); localmat->ColSplit(nparts, partsmat); // split matrices are emplaced-back into partsmat vector, localmat destroyed for(int i=0; i< nparts; ++i) { std::vector ess = partsmat[i].GetEssentials(); for(auto itr = ess.begin(); itr != ess.end(); ++itr) { vecEss.push_back(*itr); } } comp_split += (MPI_Wtime() - split_beg); } double scatter_beg = MPI_Wtime(); // timer on int esscnt = SpDCCols::esscount; // necessary cast for MPI std::vector myess(esscnt); MPI_Scatter(vecEss.data(), esscnt, MPIType(), myess.data(), esscnt, MPIType(), 0, CMG.fiberWorld); if(CMG.layer_grid == 0) // senders { splitmat = partsmat[0]; // just copy the local split for(int recipient=1; recipient< nparts; ++recipient) // scatter the others { int tag = 0; Arr arrinfo = partsmat[recipient].GetArrays(); for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { // MPI_Send(const void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm) MPI_Send(arrinfo.indarrs[i].addr, arrinfo.indarrs[i].count, MPIType(), recipient, tag++, CMG.fiberWorld); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { MPI_Send(arrinfo.numarrs[i].addr, arrinfo.numarrs[i].count, MPIType(), recipient, tag++, CMG.fiberWorld); } } } else // receivers { splitmat.Create(myess); // allocate memory for arrays Arr arrinfo = splitmat.GetArrays(); int tag = 0; for(unsigned int i=0; i< arrinfo.indarrs.size(); ++i) // get index arrays { MPI_Recv(arrinfo.indarrs[i].addr, arrinfo.indarrs[i].count, MPIType(), 0, tag++, CMG.fiberWorld, MPI_STATUS_IGNORE); } for(unsigned int i=0; i< arrinfo.numarrs.size(); ++i) // get numerical arrays { MPI_Recv(arrinfo.numarrs[i].addr, arrinfo.numarrs[i].count, MPIType(), 0, tag++, CMG.fiberWorld, MPI_STATUS_IGNORE); } } comm_split += (MPI_Wtime() - scatter_beg); } } #endif CombBLAS_beta_16_2/3DSpGEMM/RestrictionOp.h000644 000765 000024 00000042650 13271404146 021631 0ustar00aydinbulucstaff000000 000000 #ifndef RESTRICTION_OP_H #define RESTRICTION_OP_H //#define DETERMINISTIC #include "CombBLAS/CombBLAS.h" #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #include #endif #ifdef DETERMINISTIC MTRand GlobalMT(1); #else MTRand GlobalMT; // generate random numbers with Mersenne Twister #endif namespace combblas { template struct Select2ndMinSR { static T2 id(){ return T2(); }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MIN; }; static T2 add(const T2 & arg1, const T2 & arg2) { return std::min(arg1, arg2); } static T2 multiply(const T1 & arg1, const T2 & arg2) { return arg2; } static void axpy(const T1 a, const T2 & x, T2 & y) { y = add(y, multiply(a, x)); } }; template struct VertexType { public: VertexType(){parent=-1; prob=0.0;}; VertexType(T pa, double pr){parent=pa; prob=pr;}; friend bool operator==(const VertexType & vtx1, const VertexType & vtx2 ){return vtx1.parent==vtx2.parent;}; friend bool operator<(const VertexType & vtx1, const VertexType & vtx2 ){return vtx1.parent struct Select2ndRandSR { static VertexType id(){ return VertexType(); }; static bool returnedSAID() { return false; } //static MPI_Op mpi_op() { return MPI_MIN; }; static VertexType add(const VertexType & arg1, const VertexType & arg2) { if((arg1.prob) < (arg2.prob)) return arg1; else return arg2; } static VertexType multiply(const T1 & arg1, const VertexType & arg2) { return arg2; } static void axpy(T1 a, const VertexType & x, VertexType & y) { y = add(y, multiply(a, x)); } }; template struct MIS2verifySR // identical to Select2ndMinSR except for the printout in add() { static T2 id(){ return T2(); }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MIN; }; static T2 add(const T2 & arg1, const T2 & arg2) { std::cout << "This should have never been executed for MIS-2 to be correct" << std::endl; return std::min(arg1, arg2); } static T2 multiply(const T1 & arg1, const T2 & arg2) { return arg2; } static void axpy(const T1 a, const T2 & x, T2 & y) { y = add(y, multiply(a, x)); } }; // second hop MIS (i.e., MIS on A \Union A^2) template FullyDistSpVec MIS2(SpParMat < IT, INT, DER> A) { IT nvert = A.getncol(); //# the final result set. S[i] exists and is 1 if vertex i is in the MIS FullyDistSpVec mis ( A.getcommgrid(), nvert); //# the candidate set. initially all vertices are candidates. //# If cand[i] exists, then i is a candidate. The value cand[i] is i's random number for this iteration. FullyDistSpVec cand(A.getcommgrid()); cand.iota(nvert, 1.0); // any value is fine since we randomize it later FullyDistSpVec min_neighbor_r ( A.getcommgrid(), nvert); FullyDistSpVec min_neighbor2_r ( A.getcommgrid(), nvert); FullyDistSpVec new_S_members ( A.getcommgrid(), nvert); FullyDistSpVec new_S_neighbors ( A.getcommgrid(), nvert); FullyDistSpVec new_S_neighbors2 ( A.getcommgrid(), nvert); while (cand.getnnz() > 0) { //# label each vertex in cand with a random value (in what range, [0,1]) cand.Apply([](const double & ignore){return (double) GlobalMT.rand();}); //# find the smallest random value among a vertex's 1 and 2-hop neighbors SpMV>(A, cand, min_neighbor_r, false); SpMV>(A, min_neighbor_r, min_neighbor2_r, false); FullyDistSpVec min_neighbor_r_union = EWiseApply(min_neighbor2_r, min_neighbor_r, [](double x, double y){return std::min(x,y);}, [](double x, double y){return true;}, // do_op is totalogy true, true, 2.0, 2.0, true); // we allow nulls for both V and W //# The vertices to be added to S this iteration are those whose random value is //# smaller than those of all its 1-hop and 2-hop neighbors: // **** if cand has isolated vertices, they will be included in new_S_members ****** new_S_members = EWiseApply(min_neighbor_r_union, cand, [](double x, double y){return (ONT)1;}, [](double x, double y){return y<=x;}, // equality is for back edges since we are operating on A^2 true, false, 2.0, 2.0, true); //# new_S_members are no longer candidates, so remove them from cand cand = EWiseApply(cand, new_S_members, [](double x, ONT y){return x;}, [](double x, ONT y){return true;}, false, true, 0.0, (ONT) 0, false); //# find 1-hop and 2-hop neighbors of new_S_members SpMV>(A, new_S_members, new_S_neighbors, false); SpMV>(A, new_S_neighbors, new_S_neighbors2, false); FullyDistSpVec new_S_neighbors_union = EWiseApply(new_S_neighbors, new_S_neighbors2, [](ONT x, ONT y){return x;}, // in case of intersection, doesn't matter which one to propagate [](ONT x, ONT y){return true;}, true, true, (ONT) 1, (ONT) 1, true); //# remove 1-hop and 2-hop neighbors of new_S_members from cand, because they cannot be part of the MIS anymore cand = EWiseApply(cand, new_S_neighbors_union, [](double x, ONT y){return x;}, [](double x, ONT y){return true;}, false, true, 0.0, (ONT) 0, false); //# add new_S_members to mis mis = EWiseApply(mis, new_S_members, [](ONT x, ONT y){return x;}, [](ONT x, ONT y){return true;}, true, true, (ONT) 1, (ONT) 1, true); } return mis; } template void RestrictionOp( CCGrid & CMG, SpDCCols * localmat, SpDCCols *& R, SpDCCols *& RT) { if(CMG.layer_grid == 0) { SpDCCols *A = new SpDCCols(*localmat); SpParMat < IT, bool, SpDCCols < IT, bool >> B (A, CMG.layerWorld); B.RemoveLoops(); SpParMat < IT, bool, SpDCCols < IT, bool >> BT = B; BT.Transpose(); B += BT; B.PrintInfo(); FullyDistSpVecmis2 = MIS2(B); mis2.setNumToInd(); mis2.PrintInfo("mis2"); FullyDistSpVec mis2neigh = SpMV>(B, mis2, false); // union of mis2 and mis2neigh mis2neigh = EWiseApply(mis2neigh, mis2, [](IT x, IT y){return x==-1?y:x;}, [](IT x, IT y){return true;}, true, true, (IT) -1, (IT) -1, true); // mis2neigh with a probability FullyDistSpVec> mis2neigh_p(mis2neigh.getcommgrid(), mis2neigh.TotalLength()); mis2neigh_p = EWiseApply>(mis2neigh, mis2neigh_p, [](IT x, VertexType y){return VertexType(x,GlobalMT.rand());}, [](IT x, VertexType y){return true;}, false, true, (IT) -1, VertexType(), false); // mis2neigh2 with a probability FullyDistSpVec> mis2neigh2_p(mis2neigh.getcommgrid(), mis2neigh.TotalLength()); SpMV>(B, mis2neigh_p, mis2neigh2_p, false); // mis2neigh2 without probability FullyDistSpVec mis2neigh2(mis2neigh.getcommgrid(), mis2neigh.TotalLength()); mis2neigh2 = EWiseApply(mis2neigh2, mis2neigh2_p, [](IT x, VertexType y){return y.parent;}, [](IT x, VertexType y){return true;}, true, false, (IT) -1, VertexType(), false); // union of mis2 and mis2neigh and mis2neigh2 FullyDistSpVec mis2neighUnion = EWiseApply(mis2neigh, mis2neigh2, [](IT x, IT y){return x==-1?y:x;}, [](IT x, IT y){return true;}, true, true, (IT) -1, (IT) -1, true); mis2neighUnion.PrintInfo("mis2neighUnion"); if(mis2neighUnion.getnnz() != mis2neighUnion.TotalLength()) { SpParHelper::Print(" !!!! Error: mis2neighUnion does not include all rows/columns. !!!! "); } // At first, create nxn matrix FullyDistVec ci = mis2neighUnion; FullyDistVec ri = mis2neighUnion.FindInds([](IT x){return true;}); // this should be equivalent to iota SpParMat> Rop(B.getnrow(), ci.TotalLength(), ri, ci, (NT)1, false); // next, select nonempty columns FullyDistVec cimis2 = mis2.FindInds([](IT x){return true;}); // nonzero columns Rop(ri,cimis2,true); SpParHelper::Print("Rop final (before normalization)... "); Rop.PrintInfo(); // permute for load balance float balance_before = Rop.LoadImbalance(); FullyDistVec perm_row(Rop.getcommgrid()); // permutation vector defined on layers FullyDistVec perm_col(Rop.getcommgrid()); // permutation vector defined on layers perm_row.iota(Rop.getnrow(), 0); // don't permute rows because they represent the IDs of "fine" vertices perm_col.iota(Rop.getncol(), 0); // CAN permute columns because they define the IDs of new aggregates perm_col.RandPerm(); // permuting columns for load balance Rop(perm_row, perm_col, true); // in place permute float balance_after = Rop.LoadImbalance(); std::ostringstream outs; outs << "Load balance (before): " << balance_before << std::endl; outs << "Load balance (after): " << balance_after << std::endl; SpParHelper::Print(outs.str()); SpParMat> RopT = Rop; RopT.Transpose(); R = new SpDCCols(Rop.seq()); // deep copy RT = new SpDCCols(RopT.seq()); // deep copy } } // with added column /* template void RestrictionOp( CCGrid & CMG, SpDCCols * localmat, SpDCCols *& R, SpDCCols *& RT) { if(CMG.layer_grid == 0) { SpDCCols *A = new SpDCCols(*localmat); SpParMat < IT, bool, SpDCCols < IT, bool >> B (A, CMG.layerWorld); B.RemoveLoops(); SpParMat < IT, bool, SpDCCols < IT, bool >> BT = B; BT.Transpose(); B += BT; // ------------ compute MIS-2 ---------------------------- FullyDistSpVec mis2 (B.getcommgrid(), B.getncol()); // values of the mis2 vector are just "ones" mis2 = MIS2(B); mis2.PrintInfo("MIS original"); // ------------ Obtain restriction matrix from mis2 ---- FullyDistVec ri = mis2.FindInds([](IT x){return true;}); // find the vertices that are not covered by mis2 AND its one hop neighborhood FullyDistSpVec mis2neigh = SpMV>(B, mis2, false); mis2neigh.PrintInfo("MIS neighbors"); // ABAB: mis2 and mis2neigh should be independent, because B doesn't have any loops. FullyDistSpVec isection = EWiseApply(mis2neigh, mis2, [](IT x, IT y){return x;}, [](IT x, IT y){return true;}, false, false, (IT) 1, (IT) 1, true); /// allowVNulls and allowWNulls are both false isection.PrintInfo("intersection of mis2neigh and mis2"); // find the union of mis2neigh and mis2 (ABAB: this function to be wrapped & called "SetUnion") mis2neigh = EWiseApply(mis2neigh, mis2, [](IT x, IT y){return x;}, [](IT x, IT y){return true;}, true, true, (IT) 1, (IT) 1, true); mis2neigh.PrintInfo("MIS original+neighbors"); // FullyDistVec::FullyDistVec ( shared_ptr grid, IT globallen, NT initval) // : FullyDist::value, NT >::type>(grid,globallen) FullyDistVec denseones(mis2neigh.getcommgrid(), B.getncol(), 1); // calls the default constructor... why? FullyDistSpVec spones (denseones); // subtract the entries of mis2neigh from all vertices (ABAB: this function to be wrapped & called "SetDiff") spones = EWiseApply(spones, mis2neigh, [](IT x, IT y){return x;}, // binop [](IT x, IT y){return true;}, // doop false, true, (IT) 1, (IT) 1, false); // allowintersect=false (all joint entries are removed) spones.PrintInfo("Leftovers (singletons)"); FullyDistVec ci(B.getcommgrid()); ci.iota(mis2.getnnz(), (IT)0); SpParMat> M(B.getnrow(), ci.TotalLength(), ri, ci, (NT)1, false); SpParHelper::Print("M matrix... "); M.PrintInfo(); SpParMat> Rop = PSpGEMM>(B,M); SpParHelper::Print("R (minus M) matrix... "); Rop.PrintInfo(); Rop += M; SpParHelper::Print("R without singletons... "); Rop.PrintInfo(); FullyDistVec rrow(Rop.getcommgrid()); FullyDistVec rcol(Rop.getcommgrid()); FullyDistVec rval(Rop.getcommgrid()); Rop.Find(rrow, rcol, rval); FullyDistVec extracols(Rop.getcommgrid()); extracols.iota(spones.getnnz(), ci.TotalLength()); // one column per singleton // Returns a dense vector of nonzero global indices for which the predicate is satisfied on values FullyDistVec extrarows = spones.FindInds([](IT x){return true;}); // dense leftovers array is the extra rows // Resize Rop SpParMat> RopFull1(Rop.getnrow(), Rop.getncol() +extracols.TotalLength(), rrow, rcol, rval, false); SpParHelper::Print("RopFull1... "); RopFull1.PrintInfo(); SpParMat> RopFull2(Rop.getnrow(), Rop.getncol() +extracols.TotalLength(), extrarows, extracols, (NT)1, false); SpParHelper::Print("RopFull2... "); RopFull2.PrintInfo(); RopFull1 += RopFull2; SpParHelper::Print("RopFull final (before normalization)... "); RopFull1.PrintInfo(); float balance_before = RopFull1.LoadImbalance(); FullyDistVec perm_row(RopFull1.getcommgrid()); // permutation vector defined on layers FullyDistVec perm_col(RopFull1.getcommgrid()); // permutation vector defined on layers perm_row.iota(RopFull1.getnrow(), 0); // don't permute rows because they represent the IDs of "fine" vertices perm_col.iota(RopFull1.getncol(), 0); // CAN permute columns because they define the IDs of new aggregates perm_col.RandPerm(); // permuting columns for load balance RopFull1(perm_row, perm_col, true); // in place permute float balance_after = RopFull1.LoadImbalance(); ostringstream outs; outs << "Load balance (before): " << balance_before << endl; outs << "Load balance (after): " << balance_after << endl; SpParHelper::Print(outs.str()); SpParMat> RopT = RopFull1; RopT.Transpose(); R = new SpDCCols(RopFull1.seq()); // deep copy RT = new SpDCCols(RopT.seq()); // deep copy } } */ } #endif CombBLAS_beta_16_2/3DSpGEMM/Multiplier.h000644 000765 000024 00000004415 13271404146 021150 0ustar00aydinbulucstaff000000 000000 #ifndef _MULTIPLIER_H_ #define _MULTIPLIER_H_ #include "CombBLAS/CombBLAS.h" #include "CCGrid.h" #include "SUMMALayer.h" namespace combblas { template SpDCCols* multiply(SpDCCols & splitA, SpDCCols & splitB, CCGrid & CMG, bool isBT, bool threaded) { comm_bcast = 0, comm_reduce = 0, comp_summa = 0, comp_reduce = 0, comp_result =0, comp_reduce_layer=0; int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); std::vector< SpTuples* > unreducedC; MPI_Barrier(MPI_COMM_WORLD); double time_beg = MPI_Wtime(); SUMMALayer(splitA, splitB, unreducedC, CMG, isBT, threaded); MPI_Barrier(MPI_COMM_WORLD); double time_mid = MPI_Wtime(); SpDCCols * mergedC; mergedC = ReduceAll_threaded(unreducedC, CMG); MPI_Barrier(MPI_COMM_WORLD); double time_end = MPI_Wtime(); double time_total = time_end-time_beg; /* int64_t local_nnz = mergedC->getnnz(); int64_t global_nnz = 0; MPI_Reduce(&local_nnz, &global_nnz, 1, MPIType(), MPI_SUM, 0, MPI_COMM_WORLD); if(myrank == 0) { cout << "Global nonzeros in C is " << global_nnz << endl; } */ int nthreads; #pragma omp parallel { nthreads = omp_get_num_threads(); } if(CMG.myrank == 0) { double time_other = time_total - (comm_bcast + comm_reduce + comp_summa + comp_reduce + comp_reduce_layer + comp_result); //printf(" ----------------------------------------------------------------------------------------------\n"); //printf(" comm_bcast comm_scatter comp_summa comp_merge comp_scatter comp_result other total\n"); //printf(" ----------------------------------------------------------------------------------------------\n"); //printf("%10lf %12lf %12lf %10lf %12lf %12lf %12lf %10lf\n\n", comm_bcast, comm_reduce, comp_summa, comp_reduce, comp_reduce_layer, comp_result, time_other, time_total); printf("%4d %4d %5d %6d %10lf %12lf %12lf %10lf %12lf %12lf %12lf %10lf\n", CMG.GridRows, CMG.GridCols, CMG.GridLayers, nthreads, comm_bcast, comm_reduce, comp_summa, comp_reduce, comp_reduce_layer, comp_result, time_other, time_total); } return mergedC; } } #endif CombBLAS_beta_16_2/3DSpGEMM/mpipspgemm.cpp000644 000765 000024 00000015431 13271404146 021533 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" #include "Glue.h" #include "CCGrid.h" #include "Reductions.h" #include "Multiplier.h" #include "SplitMatDist.h" using namespace std; using namespace combblas; double comm_bcast; double comm_reduce; double comp_summa; double comp_reduce; double comp_result; double comp_reduce_layer; double comp_split; double comp_trans; double comm_split; #define ITERS 5 int main(int argc, char *argv[]) { int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 8) { if(myrank == 0) { printf("Usage (random): ./mpipspgemm \n"); printf("Usage (input): ./mpipspgemm \n"); printf("Example: ./mpipspgemm 4 4 2 ER 19 16 outer\n"); printf("Example: ./mpipspgemm 4 4 2 Input matA.mtx matB.mtx column\n"); printf("Type ER: Erdos-Renyi\n"); printf("Type SSCA: R-MAT with SSCA benchmark parameters\n"); printf("Type G500: R-MAT with Graph500 benchmark parameters\n"); printf("algo: outer | column \n"); } return -1; } unsigned GRROWS = (unsigned) atoi(argv[1]); unsigned GRCOLS = (unsigned) atoi(argv[2]); unsigned C_FACTOR = (unsigned) atoi(argv[3]); CCGrid CMG(C_FACTOR, GRCOLS); int nthreads; #pragma omp parallel { nthreads = omp_get_num_threads(); } if(GRROWS != GRCOLS) { SpParHelper::Print("This version of the Combinatorial BLAS only works on a square logical processor grid\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Abort(MPI_COMM_WORLD, 1); } int layer_length = GRROWS*GRCOLS; if(layer_length * C_FACTOR != nprocs) { SpParHelper::Print("The product of does not match the number of processes\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Abort(MPI_COMM_WORLD, 1); } { SpDCCols splitA, splitB; SpDCCols *splitC; string type; shared_ptr layerGrid; layerGrid.reset( new CommGrid(CMG.layerWorld, 0, 0) ); FullyDistVec p(layerGrid); // permutation vector defined on layers if(string(argv[4]) == string("input")) // input option { string fileA(argv[5]); string fileB(argv[6]); double t01 = MPI_Wtime(); SpDCCols *A = ReadMat(fileA, CMG, true, p); SpDCCols *B = ReadMat(fileB, CMG, true, p); SplitMat(CMG, A, splitA, false); SplitMat(CMG, B, splitB, true); //row-split if(myrank == 0) cout << "Matrices read and replicated along layers : time " << MPI_Wtime() - t01 << endl; } else { unsigned scale = (unsigned) atoi(argv[5]); unsigned EDGEFACTOR = (unsigned) atoi(argv[6]); double initiator[4]; if(string(argv[4]) == string("ER")) { initiator[0] = .25; initiator[1] = .25; initiator[2] = .25; initiator[3] = .25; } else if(string(argv[4]) == string("G500")) { initiator[0] = .57; initiator[1] = .19; initiator[2] = .19; initiator[3] = .05; EDGEFACTOR = 16; } else if(string(argv[4]) == string("SSCA")) { initiator[0] = .6; initiator[1] = .4/3; initiator[2] = .4/3; initiator[3] = .4/3; EDGEFACTOR = 8; } else { if(myrank == 0) printf("The initiator parameter - %s - is not recognized.\n", argv[5]); MPI_Abort(MPI_COMM_WORLD, 1); } double t01 = MPI_Wtime(); SpDCCols *A = GenMat(CMG, scale, EDGEFACTOR, initiator, true); SpDCCols *B = GenMat(CMG, scale, EDGEFACTOR, initiator, true); SplitMat(CMG, A, splitA, false); SplitMat(CMG, B, splitB, true); //row-split if(myrank == 0) cout << "RMATs Generated and replicated along layers : time " << MPI_Wtime() - t01 << endl; } int64_t globalnnzA=0, globalnnzB=0; int64_t localnnzA = splitA.getnnz(); int64_t localnnzB = splitB.getnnz(); MPI_Allreduce( &localnnzA, &globalnnzA, 1, MPIType(), MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce( &localnnzB, &globalnnzB, 1, MPIType(), MPI_SUM, MPI_COMM_WORLD); if(myrank == 0) cout << "After split: nnzA= " << globalnnzA << " & nnzB= " << globalnnzB; type = string(argv[7]); if(myrank == 0) { printf("\n Processor Grid (row x col x layers x threads): %dx%dx%dx%d \n", CMG.GridRows, CMG.GridCols, CMG.GridLayers, nthreads); printf(" prow pcol layer thread comm_bcast comm_scatter comp_summa comp_merge comp_scatter comp_result other total\n"); } if(type == string("outer")) { splitB.Transpose(); //locally transpose for outer product for(int k=0; k0 && kgetnnz(); MPI_Allreduce( &localnnzC, &nnzC, 1, MPIType(), MPI_SUM, MPI_COMM_WORLD); if(myrank == 0) cout << "\n After multiplication: nnzC= " << nnzC << endl << endl; delete splitC; } } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/psort-1.0/._.DS_Store000644 000765 000024 00000000170 13271513305 020765 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/psort-1.0/.DS_Store000644 000765 000024 00000024004 13271513305 020552 0ustar00aydinbulucstaff000000 000000 Bud1   spblobÉbp  @€ @€ @€ @docbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šdoclsvCblob’bplist00Ø HIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates « "&+05:>CÔ   WvisibleUwidthYascendingZidentifier , TnameÔWvisibleUwidthYascending#XubiquityÔ  ! µ\dateModifiedÔ %[dateCreatedÔ '( Tsizea Ô ,- Tkinds Ô 12 Ulabeld Ô 67 WversionK Ô ; Xcomments Ô @BÈ^dateLastOpenedÔDYdateAdded#@(Tname#@0 .@H\epyŒŽ›¤¬²¼ÇÈËÌÑÚâèòóõöÿ   "#$09>@ABKPRST]cefgpxz{|…Ž™šœ¬µ¿ÀÁÂËÐÙLÚdoclsvpblobYbplist00Ø DEF _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates Ù #(-27<@Xcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ WvisibleUwidthYascendingUindex, Ô ÈÔ$%µÔ *, aÔ/ 1d Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©docvSrnlongdriverbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šdriverlsvCblob’bplist00Ø HIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates « "&+05:>CÔ   WvisibleUwidthYascendingZidentifier , TnameÔWvisibleUwidthYascending#XubiquityÔ  ! µ\dateModifiedÔ %[dateCreatedÔ '( Tsizea Ô ,- Tkinds Ô 12 Ulabeld Ô 67 WversionK Ô ; Xcomments Ô @BÈ^dateLastOpenedÔDYdateAdded#@(Tname#@0 .@H\epyŒŽ›¤¬²¼ÇÈËÌÑÚâèòóõöÿ   "#$09>@ABKPRST]cefgpxz{|…Ž™šœ¬µ¿ÀÁÂËÐÙLÚdriverlsvpblobYbplist00Ø DEF _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates Ù #(-27<@Xcomments^dateLastOpened[dateCreatedTsizeUlabelTkindWversionTname\dateModifiedÔ WvisibleUwidthYascendingUindex, Ô ÈÔ$%µÔ *, aÔ/ 1d Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©drivervSrnlong E DSDB `€(0@€ @€ @Ô 4 6 s Ô9 ;K Ô=  Ô %  #@(Tname#@0 .@H\epyŒŽ¢«ºÆËÑÖÞãðù')+,-68:;<EFHIKTUWXZcdfgirsuvxƒ„…Ž‘šŸ¨H©docvSrnlongdriverbwspblobÉbplist00×  ]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar  _{{654, 441}, {770, 436}} %1=I`myz{|}~™šdriverlsvCblob’bplist00Ø HIJ _viewOptionsVersion_showIconPreviewWcolumns_calculateAllSizesXtextSizeZsortColumnXiconSize_useRelativeDates « "&+05:>CÔ   WvisibleUwidthYascendingZidentifier , TnameÔWvisibleUwidthYasCombBLAS_beta_16_2/psort-1.0/driver/000755 000765 000024 00000000000 13271404146 020364 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/psort-1.0/include/000755 000765 000024 00000000000 13271404146 020514 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/psort-1.0/README000644 000765 000024 00000002676 13212627400 017757 0ustar00aydinbulucstaff000000 000000 PSORT v1.0 ---------- The psort code is made available under the MIT license. If you link the Funnelsort code with -DUSE_FUNNEL, then the GPL applies. The paper describing this code is in doc/ The source code for the implementation is in src/ The driver code is in driver/ Please email Viral Shah if you have any questions. URL: http://gauss.cs.ucsb.edu/code/index.shtml -- Copyright (c) 2009, David Cheng, Viral B. Shah. 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. CombBLAS_beta_16_2/psort-1.0/doc/000755 000765 000024 00000000000 13212627400 017631 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/psort-1.0/doc/psort.pdf000644 000765 000024 00003232252 13212627400 021504 0ustar00aydinbulucstaff000000 000000 %PDF-1.4 %ÐÔÅØ 5 0 obj << /S /GoTo /D (section.1) >> endobj 8 0 obj (Introduction) endobj 9 0 obj << /S /GoTo /D (section.2) >> endobj 12 0 obj (Algorithm Description) endobj 13 0 obj << /S /GoTo /D (subsection.2.1) >> endobj 16 0 obj (Local sort) endobj 17 0 obj << /S /GoTo /D (subsection.2.2) >> endobj 20 0 obj (Exact splitting) endobj 21 0 obj << /S /GoTo /D (subsubsection.2.2.1) >> endobj 24 0 obj (Single selection) endobj 25 0 obj << /S /GoTo /D (subsubsection.2.2.2) >> endobj 28 0 obj (Simultaneous selection) endobj 29 0 obj << /S /GoTo /D (subsubsection.2.2.3) >> endobj 32 0 obj (Producing indices) endobj 33 0 obj << /S /GoTo /D (subsection.2.3) >> endobj 36 0 obj (Element routing) endobj 37 0 obj << /S /GoTo /D (subsection.2.4) >> endobj 40 0 obj (Merging) endobj 41 0 obj << /S /GoTo /D (subsection.2.5) >> endobj 44 0 obj (Theoretical performance) endobj 45 0 obj << /S /GoTo /D (subsection.2.6) >> endobj 48 0 obj (Computation) endobj 49 0 obj << /S /GoTo /D (subsection.2.7) >> endobj 52 0 obj (Communication) endobj 53 0 obj << /S /GoTo /D (subsubsection.2.7.1) >> endobj 56 0 obj (Space) endobj 57 0 obj << /S /GoTo /D (subsection.2.8) >> endobj 60 0 obj (Requirements) endobj 61 0 obj << /S /GoTo /D (subsection.2.9) >> endobj 64 0 obj (Analysis in the BSP Model) endobj 65 0 obj << /S /GoTo /D (section.3) >> endobj 68 0 obj (Experimental results) endobj 69 0 obj << /S /GoTo /D (subsection.3.1) >> endobj 72 0 obj (Experimental setup) endobj 73 0 obj << /S /GoTo /D (subsection.3.2) >> endobj 76 0 obj (Comparison with Sample sorting) endobj 77 0 obj << /S /GoTo /D (section.4) >> endobj 80 0 obj (Conclusion) endobj 81 0 obj << /S /GoTo /D [82 0 R /Fit ] >> endobj 86 0 obj << /Length 2612 /Filter /FlateDecode >> stream xÚ•Ërܸñ®¯`íe©ZEðÍÜüØuy+ŠKÉl ša™$fIÐ’6?Ÿ~‘CItÊ9h4ºýz{/ôÞž…ß_]Ÿ]ü¦2OåA¤ÒÄ»¾õ” ƒ2μ,Kƒ8L½ëÚûä¿<ßÅaä_UºÕ7­áÕ‡ó(óõ ÛÖ´²o×ô{^¼l÷vhÜ¡ãå­xòÚöçQî;ÓÏw0±ƒο\ÿ ²ôv‘ JUë¡B>‡Æ™ÊMƒ„ÎAÒ LÓ…Þ©TQ{À!ˆ•â£o42úÖÔÀH| x|}0 #Q‰/ʪ 2B°ª·¡. †fEÍ„›®qÍ7>ÊñÏôŸÜoÊ$á<Ö|”†"Ü • 2ÿ]_ F€«±FYöô`R>2rz8WþÄR(.¢—,ÆX¤nê~Fô¶t×5œGž ÏØßšÁöœ$½%þ8U¸D‹Ç±¯z©¤Õ üðöîeñQFÝ׌zÒï>0¤`>µaÀ¸¤_Àdâ1û@ìßLMëvlãÄ¿@}åÛ’zW~$×øÕ ¤“0^f4³…“0E²_4‡«EÂ8åÜsíx†N„UkG!ï,ƒìÑ5j(,Žè0`«#ˆ<92 TŠ l…ƒ\ƒ¼mê!ñè ´)DFkYÎP"0 øŽn›<ƒŒS lÝÈäÄyЃ©QÄP~Ý@þinÀuЙÎBic¬-eŒ<-€Õ æàgY’ùï!q¬½IÂÙÞ…2¢²Ê®;uc,UàÕF²€Wº‡ak|9¢EF bT?j¡}‡¨Zö@/Dç­&ù¿òj–IóðçŠs˜Î0.®'Ù ôÙeÒ°ðÛæ†š"A”Y鿟ÞÚÄ£ÞGMÁ@ÚhÐhÌ? KºbXn80a3Éi¨ŒF#®©ÝëºÝÇÊ$rdñº'7:‚1„õœÞSp2‚^ÿò O –ûš’QÍ€k ¸îØj'¸‡‹âUJq¨H`×LI2#³èÑ¢ˆ ¡QÈ/p‚~aÑXw}kuÍ´´ôIº*“Onvpîø·‹‹½žÆ1¨Æ`ªÆ›ÀÔÓEeksÑôµ¹ƃëZ!•cZ‰“LÚè1sìЖGLQ{ª(…d~ƒŠ ¡AÍóë‰2S)Ij’VŽ¾Íˆ2žò.I8Yê$šEc±›%¡PYfb$º^f0œü(feŸÒu½TúÿÍ:ÇÄWæ?É1Ü*½ÇðþÒÀœ*ŒÓhËI"ÐÆ‚È’Ü Q+;:ž¡ŠÒuˆ¾d×õ=’RÚ¬»eÊú„Ñ564½å±Óû¾ÆÄð²ÕÃ~F²ÑÏ3ÚÎDŽõy®Ø³ qCÆS½% †-¥Fþ{lâ°Ò†±ßéYêÞº­K™ÞN{,ãIDš‹LéÔùÄ܃™¸|q â—‡þ? ¥Yj±{Ël®MÇeI0è6…iËæ²_…†YÈXpBZ;¡%‰k·i’S…[®ÈT)i:`£yirº ¯éæ0>4¦Ü=ç$ÉL ãÑ :p¥ÊHû:™”ÁJ'9 D³§(cçg-ÍÖ­îÍlzì;n•èz#ƒ´ì4½´ëì´t9°¦:§Rˆ 'è+:C¸.ä7ád[;|ŪÅ*ÑÆ¢tj‰Òˆ$s#„Ä,3<5Bˆ×oä6©¥×C,å'bÒ;F­«^ˆ÷óçÔ@7ƒVIÿc𢱜4† ÖŒ#¿ ”hÉr„"ìôÎP˜WjžQfÔ¶o­Ù2Õɨi²r1 È´ñSnã4ÞhÁAä4­u<é¨U™ùªliäF€ñQãFdz.Ê8Ÿ[="ÇÉvã©…Úꌾ¥dqòñí•Îõ3_ð©çHφ Hß&°ê×q{é×áà:¸q“Úìhî;áõå"lŸ“tQ‚³¼çÜLà+ûùõ1Ž2P+x„4Û¼N4¹ m0,‘Æ&ƒÆ–¥¹†@ׄÉ^’áœths}LžÀŠ èy‘áÓFC»AY©ˆÓÓ«¶Àg’anà",#¾©¶Üøt£H-7-“ÞðæxBŽûiô\DtÏ; {¨{É×9u T’;ˆœC'z}8WF¤B‰ ƵPÜ!ò.¶¶Ïs Hå¨~äàÉûE còÀôùúgÁ¾¼Zú¾qf7÷‚—ï®å«íZ p«^ayÄå£ÏÏ4ÀH+À ÄKa²ü¶2òÕ4·w¾Ð%ô `µ ž½Ý§Wµ,Ž£ÇÙ5g_™!Çg„ä‹;ÉpЇŸñÃáÍÉOgVsüëµÐ|ÍËWz¸àü!mýõ#ÚB>hŠ+|¼Ë “òÿ°ºÿV`åLú¼2g•Ô\£ÏMôÝoä«Çv¯÷¬H[de1¿ØŸêlÿ¢ endstream endobj 82 0 obj << /Type /Page /Contents 86 0 R /Resources 85 0 R /MediaBox [0 0 612 792] /Parent 98 0 R /Annots [ 83 0 R ] >> endobj 83 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [163.432 228.947 170.879 237.97] /A << /S /GoTo /D (cite.blellochsort) >> >> endobj 87 0 obj << /D [82 0 R /XYZ 71 721 null] >> endobj 88 0 obj << /D [82 0 R /XYZ 72 720 null] >> endobj 6 0 obj << /D [82 0 R /XYZ 72 391.957 null] >> endobj 85 0 obj << /Font << /F16 89 0 R /F17 90 0 R /F24 91 0 R /F44 92 0 R /F8 93 0 R /F45 94 0 R /F47 95 0 R /F25 96 0 R /F38 97 0 R >> /ProcSet [ /PDF /Text ] >> endobj 104 0 obj << /Length 3207 /Filter /FlateDecode >> stream xÚ½ZK“ÛÆ¾ëWðÈ-‹Ì#—ާœRbÇÚT’C» - иkýûtÏ/É•J•Ëb gzº¿þúÍ÷‹lñ÷WÙ•ë¯(\³]Pƈ¢|¡©!yF›ý«÷¿g‹-¼üÇ"#Üä‹g7u¿àJ­%Œw‹w¯þ=[$§l!¤&‚æ—ÉL1Bsùëí«¿ü$Ä£˜ZÜÞÁbj¡3C”‹ÛíâýòûÝ}Ý”ÝÞÜü~ ë-V”#¥ûsu8voð ¬#GëJ$Ïaº_äf%™\>›®nülJdz5É4³ŸëÑ Î¦ãŒú.±• 5SªÔ"”0ÚÏèêÎî¼`Å®ØU×¾·OEµûäÇÛ²íšr}슭`÷uuŸØžQAè2¬~Hì¿âì¡ÅT‡‡¦Þm[7- ¿d¦ð_ŽÝËŒ0©zW/8ÊY{PI‰ê­÷œR%·7Ø3àïØ=~ÐÚ}1Ûµ¬M+Ô”Tjz0Û^5i ÐÜÃ"˜oSW-«Ì4ªnú³G“û»4 òœ0îmßëÜ™¯÷r8gƈïƒ1ŒùIt6ëýJ ½|’Í„ÝÕ›9]&N’c1žTuu@ü´hN“søHo%\¬%ä\oHЖ",ËÏ Ê "`D~Fí\ŠÖÄð0‡%µþcÑ;¬Š™ê‹?í&˜£=ìÊ®ë¡89k¥­íì›é64'Z¡(†¦½(2™YøCO%Ê—?Ô{pï¹<{C—MWÆÚã¾=ãµ&ë(˜JO  Œœ@!"›“0>AÂw7+¦ó8ub @¶f½ÿÙj›"Fþ©T3„ÎÅúx*LWr*–_i%$…F„îökb»0kňP|´r­vëž=&õ7è÷;š²# 4ŽŸ?m›ÀÓÆøÀé˜G9ù.é+&À{‘hÆ„ú1eÙ!>£&Ï  •=û¿¹YQ¥ýEÅ»Tt£È–gÉP ’aà“$a}ÎþÓò9ØÆîvEt€bWD'µ1G ~x뮬¶sÏ>ϤŒ*bPR^Ä"ŧlšR!ü~4éÛ”Ãå[ÿ°H…aÒ—O¶;$øUšõ^ø!˲S¹UG¥ óaÎ79rÝý®^Gæilõ˜Š9R~ñœÑšþL¥5i¥) þÿ‡ÒNìR¦hÏò©Ø}:M À=ñ…Í9_ø ¼Þ£×njSàxð¾„`g‡¡F'ó`Ÿb Š©jB H– íÝ?b¨!gÜ$#Ý(œ‚ó&VÉzzj“ÇUzšð|L§°lŠ·¶ ª‘Dçfʧ݃íR´Ɇ¼ÝÄW‹nú ¢Û!ÝÊS´c™_njݾ¦úí ]ß•MÛ]Í8¿¦ /(  ‚ÖJ\KwÕ‹Ò]}5Ýõyù(ݵMPNÑ*aÑLOѺ³Í}ÑDuÚêê9Š´c¬“—i#_Œ&Í ‚`’ã4˜'3òߊ¦>e…óu[Y%ó—œdò+$ÁBæ%Š8Þlêf;”Ÿõì8Àl%ÐhJrù´|!·É ÀÖ$£zl“7+‹"ÅÎ4 IFxÆ_ÌêmQmÛ3†š*2‰û"YÐ*ÈRÍ©`bb˜3×ûš…÷©‹@>Õb"!I‰ˆj–âºFÃL}=Ô®˜‚dUÑi²šöM5ôÕN"ó<Ê!¢ˆ¤[¾Å°ÛHû¨e°f„‚2E/uÂ`ºcÿnë•ï4ôÈ9q Ú5Á’;³Yãjbíjª‰aÎ6N8ío·£¾«È °Hþ%}×Ó0.‚IÜgt€£\7G•šÌP§Õ ¹‰ Qã§òþˆƒs¾¤\!$d,_öu¾AC8ÞÂßÕã\Ä~ˆ›áú!EÓN`2âzÈÎ`k˜K²,óc¹Á­A9U®WËwöøx#%6þV<3>úsš-ßÕN½› ²šû…ü†¨\R1Ãéòwÿ›mÑnšrÕí²ð¬¿ôbøÛ¡¢ÄÝAŠ”µqmJ/ÐÐùõò—c3{á*þìëÅö8‡x`bÎX¶A™7 OÝ<ú;D4^‹U›²¨P® Õ Ũ“26,¾ lîYf<LOù)ïMâ†óya¢•ÅÈÊîÍ(ÊŽ1òtOŸ¿$¶ÐÀÄ ŠÌRˈ1«hˆ@ý6»ú>©9>P•X\Aä#Y¡!Ýp O/>ƒÒd9VåÆ"HH´”QÄÔÄ9]Þ×X¹àèÙùUëoœ©á ¸,ï+?¶þâ0Äj×»Š`¥XqqÃ@ªõ±íÜ ¬`=ðL˜W„œ¯Àࡼð#?«hîêfo«M˜884Ülj7gÞ=?xxð§£À‘\MçNRw°›  ÛO{?²xáË5È},w>×Áçëß#:N¦•{‰Âa¼+žp ºÞ‚Êñv]V˜ß5:Ý6ÑÓZðA2ÿ ¤{ÁÎÖÇ&®Óomé¿Õ̳í@…¶q–Ïärm[§þ­¿Ý•ëÆ6ŸüM[ßuè¼ÞªŽCñéqƒüç0å#>øá›oü“w·oý„ÿ»°«u{“µî;ÛþÉ[¿o@'®[…¹ÿüõgÿä%ªF|=>zdƒÕ\4 ƒÛg<Ìß8\âÐ!§øùe°˜è4Ž„}žÝùkë0]…àç¿öP𸮠¯ K»ó=Æøð;÷£Äû°²nGzÜœ±ëÜ 6ReÁIõÔ4[:vÖ™Ž}aÊP»ø±=B…^˜³‰Þ4â·ký|os`+?j 0ê&<¶U< ÜîîÃs§-¸†ßÓQXI,c À‹wD¼CßBî² lÏV1P{Ul¼¾z¶:(­ßÅGÇtËÏ4ø:„€ÀU‰þ(„ØøÍ²%A¸€äË´äpÛ T ‰Áð-,—?ºP}ˆg„a_ °°Fô|®ÐÈ¡°{@d*ª@ækØ¥°bˆÌ†µ ¼å?ÄúmœÀI4µGžÁÙ¥ ƒ½5ÿàŒKúâÈEý°HxµþâO6ùà6^—AËæ=ˆÉÖ23dþ®‘ø}Û÷A‡®Fî‘Çîœ|È|> æÒµƤbwMa·ŸüÌ]m·~´mCZy¬ââOh;ÿ]ïû/Ä1 †Ÿ;bòŠhQê`2—Íç¬Í/Z{·(£Eµª»‰iš2ɦ×O8ª¯G0IÈ s¥ÙÈ{÷öÑWd&üIq¼9<ˆœïv÷©uR`]TB¤(ÌE¼!öäÝÈ[Ò³ÂCšRø4”Ûú%{&ú„Ú1ãbôá3=ˆ¾-\›4–4î;˺ŒbÞª³]æÊR¢¸š”h³„Î0l½÷“œ»ÂækÏ`ƒJœ]I.fÉÛ æÂ, Lvp3_øâ“u$rO5œôQ(׆&Tê| Z4¬¡ËÛˆp(ÜÆSa˜÷‹Íÿ¥bîú z˜!ÒÈKbˆþµ¡ ê±þr™$TϽ¯mÏ,Ýïý ¡' N‰>(l“@Qú%@QFŽò]ªl¢°›é ócR €<ý"àN·õät,ÐÇ$82>ÃFïô`Öz0¦BŸ"|øç”iW¦ g3’Š¥ê„žá~òoCã\  Õb9äû8§÷xïiÉV¤(EeàΈ\3¢”¾$;0=¾a§Íæ“æo~ÑLIjtî;š”äÆ ðŸùü÷Çþ·) D« BªeÓµ?„ü|ˆèŸåSø)¥Ñxßþ¿R«R z0X¦Ç^X•è…AÎ!UÙBÚ˜µ±lÛX’P÷£ÿ@œì'€¾ä4§Ä}¡Ó!Inð3‹Vlò c†—ׄŒ—cÙs†rò¡÷üΡ%ƒÌwæM½³êÓž<òõ ÆÍ$¸ui \C¿íu Åž¸bÏÍaó‚Ó<)pk÷±1åƒq¨4``Ûǰ]žÔþêRÝéä>+4½peµ|¯¯­'­ƒ€‘ºÙ†dE,ÿe»c㨯]Ìs‚ð‚‡ºÜŽˆdb›uÙ…*ë›j½£g¨~ºP·÷=Ù6e럻cµª¡½ñÑ`¸ªm;ëy3€ÓàÀ9„K˜ñÀþÿ±@ª‡ endstream endobj 103 0 obj << /Type /Page /Contents 104 0 R /Resources 102 0 R /MediaBox [0 0 612 792] /Parent 98 0 R /Annots [ 84 0 R 100 0 R 101 0 R ] >> endobj 84 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [198.086 392.528 210.988 401.551] /A << /S /GoTo /D (cite.SS99) >> >> endobj 100 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [527.489 338.331 534.936 347.354] /A << /S /GoTo /D (cite.mpi) >> >> endobj 101 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [267.637 169.092 275.084 180.782] /A << /S /GoTo /D (Item.7) >> >> endobj 105 0 obj << /D [103 0 R /XYZ 71 721 null] >> endobj 107 0 obj << /D [103 0 R /XYZ 126 653.682 null] >> endobj 110 0 obj << /D [103 0 R /XYZ 126 635.37 null] >> endobj 111 0 obj << /D [103 0 R /XYZ 126 615.326 null] >> endobj 114 0 obj << /D [103 0 R /XYZ 126 587.707 null] >> endobj 115 0 obj << /D [103 0 R /XYZ 126 558.899 null] >> endobj 116 0 obj << /D [103 0 R /XYZ 126 516.557 null] >> endobj 117 0 obj << /D [103 0 R /XYZ 126 475.628 null] >> endobj 118 0 obj << /D [103 0 R /XYZ 245.727 440.079 null] >> endobj 10 0 obj << /D [103 0 R /XYZ 72 280.119 null] >> endobj 102 0 obj << /Font << /F44 92 0 R /F45 94 0 R /F11 106 0 R /F10 108 0 R /F13 109 0 R /F7 112 0 R /F8 93 0 R /F1 113 0 R /F49 119 0 R /F47 95 0 R /F21 120 0 R /F53 121 0 R /F18 122 0 R >> /ProcSet [ /PDF /Text ] >> endobj 130 0 obj << /Length 3814 /Filter /FlateDecode >> stream xÚÕ[IoäÆ¾Ï¯häD#šµ,&q€8ñŒl# {T7%ÑÓÝ”IöŒ'¿>o+²H•ÔRbO‹ºªX¬å­ß{Ê6·›ló§W™ü~uõê‹7¶Ü(•Vy®7W7›RoʬJ3«6W»Íw‰NÕÅ¥R:O¾í..J¶õ~Ë<º~¼xwõç/Þ¸ÊÒ*«.m.u–æUÎï_Ý5—¶(“ï3eûaäÎ06÷Ø*’C}¡ËäwÚ#v>\h—tïñ¯¼[±ó‰;{8ôèøÒçKºþB%²x}¿o·õõ^Þ;ùõG¹ï»k˜¾oò‚¼xWwéÅ¥³yò µÿ^ÞeÒ|¢£wó‚@¼²2in+¾ò°íî鈰±UÒÝðïx‡«akO»O2Øñï®ùÐò\—~ Y±mºý(¯âm›O2Ø7p¸Ñoeým×·ãÝá5ôs“ÔÃúHº?vü ¾éåd©·ßG¯h$–-\òÏ gø²*ÚÃýþ·¡ItèyHÀFß cßnǶ;ú'õ¸š3]»:9 ÍŽÜ5½ßì®;íeôz&9ì¼óôaqÅu»yýȺ#¾êœßZ7 TÐrrÿºš}ëçm»Ã}Ý·ÝÀ¡Tð¯L>vÇKx^ï÷Í~zyw’w›hWdUòGd²åE+¹¼¬~k!  îøcE. ¼-—„+3>¹ßae"É,‘iÿjX•• uYEjL [ÑLjº+Ôuåg‘KåÕ%¶¦viUMK^ñ­6eZ•Í€=Ë.—°5‹[dc›Ze7Á¤ï³<‹mhSmõSW(€ˆÎO€U°&Ï 4](h*¹¹@Ó 2¹Lx„=t§I5j.yß±p]ÈŽ!û.µ.Ó<³ÌE±– –lÜša`6?¸Û\ùÓ¶‘ë”iáŠiÊD‘%?œ†1¶œ1©Òî™Ü(žÃâÜØEöËÓ¢t‹ýb—4©qjµñ Õ[`W{Ü"É ÔwèJ›\G). ¨2MèÐ÷œHË ç%˜:ÈÑ}3’a(D£à—„£Þ"Oïx„ù;Ð#âa¸¨®¬è­ºVð½f©ËÇæ'<†)ÄQbkKú YFšФφÏn÷Ý5]_`‹-ƒ¾ö§ÁµÊÓJEI¾d]žªª iþ™ÄÅüBâ‚TÔÉ—QÃçRçª_T-€×/¹gjoqZU–ìÕ×W¯~|…æ7Û¨ÉËT•f~%ueµÙ^}÷.Ûìà!¸‹ÔÂ!>ÒÔòÒ Úï7o_ýqàb;\ªP°RN¤ò>vð@aÜd½­-MâGÄ‘ïÛ´Éÿ˜FøO'ÏíCÝù]€¶hûO`Œü™ šMîk‰z~rÓíÁ7ÁÀG™pÓwÿ’lÑ]Mÿá€É-¬ÇÍ0¯¡ÛÀP˜<“Ç$´Ëtu´7ŒÌñávA=c3É®I1­w@&@Úøœ± ´8ZÁ»ûˆÖý&V”L+ž…–Rl¨M•šñŽO±ÃÂñpl@`‹žöc}lºÓ £Í¾‘0»³Ôòì¶å€Fž ö¢öÛùóþ"ϧÙÄVÜûm‡´Â¡VÞüŽôÕ#Ûô·nüãO¯à ÊÊ3¨a䈉ÎËä# p~}$Æ"É;Þ„…½„*»iz„~ FUKà8²¯ï™¥è‡G`&^·Çº—öÐÔýrÇý¾éê¹ ’$˜ß±ÅpÈ*˜Wd>ãk4Üö" ,:D}7€KçÜ9ƒ›‡×®•á¶iQ•/ÂðL_[€]ÉÝ’À=ÀöÒ,׬šà‰Àë°x!bõ‚Ç -‚ɽ€ª¢Tô¨áF Ž8q†ƒÃÀ#À€}KÁ_ž)ô†ý¼ëAæ&¿ŽGx¢il¼oX+ÃáQû1d0k× Û¾ºã1™ÞÌÚµü£òÉÀr(Ï’·~g8·ìLÙ—ãy;û¦íòÞ•‘Ô.ƒ×>®S¨í£'y?:g+œ„Yn¢5ñ†`èŽ޲ÆX÷·ß“˜¯Á[‡9£2­§fììX¿H±Ù/‚Òs”dÓ^ÎæYêrt¾U.|R “°–e ,4.ƒù}Ô)êY1R/ÌñËCj³‡ÆzxÏc~äЭ8 ò–ƒ_mxpXå›ða`²e5Ž’e/¬¡>È"ÂCà†Õ¶bÎêØõµwC"ïÅ‹-´£N~äá'L´~`¡´"çï÷p«Ó-nLþü‹9Jv’ $áÅÎv M±^ϘÁßÝ”ÖÑ>óˆÐ]aâqfvûf{ꇖ½3;Ot&^-4¥.ÁËsR‹NˆJâ|¸“Õ•¦D„.ÄŒCT¿µóã-3·ãÞCOK£¡ÑË(”GÜÓxêNÀÁ2´¿ì´…“<Äêþ2jø`WŒ´ÒešóÂð ënçňõñ¶áGž!S<ðƒ Ê8íí‰!Eá¥}+SÑ:àh`6¡ý1šP±6ͪÉ}nW!kŒX˜q™¥íDÞ·Q¿§ÁÚ4Ë ï$ê‘O%SRþräMKNÄfóçœAYA[Úì"C3ø;%cñ G€2W/¤CT*çÖ@þ•|Ú!ñ-Z Ÿ&´f®jëàUŒJðõ²DpC‹—¶&R^*³4›°ÎM¼* óÜ…£hc¹‘ú!jœÓÌüG›-—)R«›ý­Ž‹. 1BV™Åš‡(©€e‹òl¬øQ¥ÙüµÆp:„fÐ&µ˜EŸY%~ZT¤ V±€ókÌÙ>]@‚…Œ]Ú‚ÅvÓRÄG¢Š:ªdÖ„õ£×bÓ}î³[y3Îa/äМ“C¥Ánê¬$Úÿ{IüÝg’Ä…Þ®}¬… Ëëäbeu±dIcäÁ ¾zŠ:Ä¿o*òŒ’Žͱ´ª+Ø•ê ÁSìwsž¥çÎ;Ac‘°‚¾OXYû Ò@!ý™ðT.8„S”C³AxÁ'n3}ÚIWCšì¸½¯ýè=¡ªŽ0€/øÓÅ=àf¬@¯êù7Ñ(ÑËQðcQ5bœ‰ÀûÈ~˜ )^¾Ÿ‡š[®ç/‹ ÖC3[ƒ<¬±A½'¨ÇJÊ‚°l<æQ®ø\i‹GÁwϨ{œ.3øÐ#¬‡ò°±‡ÈñÖΫ´Bœbm5 ¯î¢ŸŠ>XÚî•[Ìí #™:JÁq¢NIÔ在³Ÿ!QwF-žÔß½D-Ö•×ÿ9áì3W„„;£ß/Íp‚¸_ùbUX_Œe$ÀŸÌˆöŒã~ ãÇ> endobj 125 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [452.195 515.7 465.097 524.723] /A << /S /GoTo /D (cite.SS99) >> >> endobj 126 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [521.858 423.522 528.583 436.731] /A << /S /GoTo /D (Hfootnote.1) >> >> endobj 127 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [193.731 398.545 201.178 407.568] /A << /S /GoTo /D (cite.BFP+72) >> >> endobj 131 0 obj << /D [129 0 R /XYZ 71 721 null] >> endobj 14 0 obj << /D [129 0 R /XYZ 72 720 null] >> endobj 18 0 obj << /D [129 0 R /XYZ 72 573.025 null] >> endobj 22 0 obj << /D [129 0 R /XYZ 72 458.662 null] >> endobj 132 0 obj << /D [129 0 R /XYZ 72 268.005 null] >> endobj 133 0 obj << /D [129 0 R /XYZ 72 244.556 null] >> endobj 137 0 obj << /D [129 0 R /XYZ 72 203.657 null] >> endobj 138 0 obj << /D [129 0 R /XYZ 72 168.298 null] >> endobj 139 0 obj << /D [129 0 R /XYZ 88.588 143.39 null] >> endobj 128 0 obj << /Font << /F47 95 0 R /F8 93 0 R /F11 106 0 R /F21 120 0 R /F49 119 0 R /F44 92 0 R /F18 122 0 R /F24 91 0 R /F1 113 0 R /F22 134 0 R /F27 135 0 R /F19 136 0 R /F38 97 0 R /F39 140 0 R >> /ProcSet [ /PDF /Text ] >> endobj 151 0 obj << /Length 3324 /Filter /FlateDecode >> stream xÚÍËŽãÆñ>_¡[8°ÕîÙÍu|°‘u°AÇñ9¬÷À‘8b)R!)ïúïSÕUM‘šžñFbC˜!»»úUïåæ°‘›¿ÞÈ«§‚ÿr£6ªtBm éE¹Ùoþs£ñ“:š;¾xsT›¿ô7ßÃ/mi¥í¼Ô7w7_|ë7JŠR–js÷°qz“û\ï7wûÍÛìÛæpêÛ­)ÊL¿ºÝæ*Ï^¬Ž§–;ëõî<5}GÍþžcÝÖ»©éÔœ‡:NhëcÝÝjŸM£€s½®vØ~$€®¿Ýj—í~×C=ž Ïg}·yÁžžU¢á´ýy¼}w÷7¸ðV‘Û’.2TÝV´Jáà‹o•ZÞÜá­‡iü'ÑvãDéd€ÈEáÊeÎÈ‘ ¤@[) ,¸uB*K` ­ðlÅ þ˼  ÏálFeUw«²=žSg‡zéÄã©m&zmè¢=·"Ä„½noŒU)<¬%î "~”R¦Y^ã†/¢I•ßf9˜GT=¤PUíg©l¡T´ÌŠþÎ6ùÄèì®굡e’H·j^ H»È£2û÷­7kM‘í*ô ªÛ¦{O}¨=ñI>Œ£nA6q[>Öû&ÎŽS¨o¤FŸô˜u0íB±!®wÕyäÃßVkx" ð^Á‚gtøttÅB”@Õ•MhºÓM«.Apz!ôÁKWä±¥™¶ÖdobÿHÏŠQý(øÛ¾; ùÐÑ0Øès;ÅMªéj;š½ÄöFìá{0W¡÷ØÃ„Dc$£ÍNXÀ]„ë;è`gñÙ6]] Ì:ͱ¦Þ·lÈq‡GÿD“–åW6¼«ç`ÃRÊ\Oxðh³ï:Þ-Úæ±¹o¹+0ÓìáyGzWÎz‚Cƒr_˜sf| ÞEìÔžkVÊšé‘'Ge.Ï{Éïñ#*…=¢z4`;«Ýî|Ä gp:Bd"ymàØ5%G꤈H‘ÈÁ ˆfϳ)RRq¹ÜÏ“‰ÃÞ«a¬×kÎsâr*2¼¡Ÿ[ñ!&ÖÉûá±ÎñpVš«{ ‹ θ¨C ÂR£xjýè0¡:öçh˜±Äü?b¼Š(…MGd`ï@f“ô|ág]ú]B{zQ@X!£òÎe2¶ÚÎVó”´‡Ö—‹U@Ák ÂgIý΀ÿ¥#åË#µ‡´EYD¸*iÃ\áVÞÔóÁÏèSÎÿëpªhISšì«Ô’ ÜJèrå ý&b™—ˆºø@ì¸D¯ð§µ€€Ö+Ù¸§öæõÝ"]¢x ੘ ­ Y‘·ïäfƒ 1,ÊæC=9”A·›n¾§ÜÉj;\ʹ–R€…ò%nóp"ÔÕKŠ%GÆÕó‰£pˆDÇ~`Ó N¸»¶kߢé>èðITóhhK°Ž³]²Aa„îT¾²OTxXÓ‘¥¥Ä®A¾¼ì«©¢·9ϰ~jì°wvJö.Ê¥«€Óš¶}Þš×msl:ÒÇÊQЬ|Ö‚o91gù}|!\é?äÑ{ÊÀ6VØ ìë»f‡ÕvóJÚ ðkhG›” _Ú%;Ó «yå¯sf"•oê:Õ´ñˆoõÕœqªOÔÅÕ¡ ¾tó˜ŠP5¼ÛYuui?XªYy,ÏE»‚BÌ »ìúÍ9RŸGÞ-DaýšŒÈµxhéB:%¥ÀKµÅ¯RY/è\÷Ò ØÉ¯-lö ÿgn`dê'LiàëbÑ’uTÙö„îÊR~ he±ã!ŽpÎ4öÿG¾ŒVä%Çö¿Ô·FÎȯ ù‹Œã…¹ þ3ròRä*_sCR“+0oùRršÜ„1ÒŸëߢÇ=—Þo ¦0/áÅ©•ܾ€û+yƒÅ¯ž±ÞÎûÿÑÝ'ݾLôÒ=g©×º² ŠR¯’_ë t.¶ OÂe xfå °Ñ‹uèÅb;¨_x‚¤’ÜaþñJ.ÁªNC Èp°pÕ°Za^C°W”žæø£'°û[Ì´Wû]tøDÊGŽ€,.êÃ¥³T-CÒKÊ?¸nHéÜû!ÍjbzŠñ[4*/!äÃC= 1õüÎ9'Ïä¤ð¶üÝ, fc0ø'¥ìŠ†Ú¸àÀ-……-à\¢r™ýÐo!¨=·SÕÕXñÚoäé) øëž—+æ7]LÁÔ ;”à(£‰{Z¸4´I6Ú©¡Z B†¤íˆ$O`ÎøQ*œ—aNð"È¡íï‰ôž3òh+²7ïC‡¿”ý(q#TÚ»Žt¹d²ÕäºêRÆ›cÊ»¼ärŒDyÎ8—*&•ž ˜qW²Î0‚8.¨ŸL.– ‹Enw\»V؃j Ï‘ƒ}ˆœäÁ´²œ%wŸP¹Xó«©Jkgô2AÊńԒ˜××EjÍ+Ð Y«äsbãR>íú¹ ˜Ä^ᣠ‡‹/y…•WLÙ/”g»ó´‡\êžKvXU=a­Â> endobj 146 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/bfs-bsearch.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 155 0 R /BBox [0 0 198 101] /Resources << /ProcSet [ /PDF /Text ] /Font << /F1 156 0 R/TT2 157 0 R>> /ExtGState << /GS1 158 0 R >>>> /Length 1165 >> stream 0 G 0 J 0 j 2.25 w 1 M []0 d /GS1 gs 1 i 121 76 m 85 58 l 67 40 l 76 22 l 67 40 m 58 22 l S 0.562 w 85 58 m 103 40 l 112 22 l 103 40 m 94 22 l S 2.25 w 121 76 m 157 58 l 139 40 l 148 22 l S 0.562 w 139 40 m 130 22 l 157 58 m 175 40 l 184 22 l 175 40 m 166 22 l 58 22 m 53.5 4 l 58 22 m 62.5 4 l 76 22 m 71.5 4 l S 2.25 w 76 22 m 80.5 4 l S 0.562 w 94 22 m 89.5 4 l 94 22 m 98.5 4 l 112 22 m 107.5 4 l 112 22 m 116.5 4 l 130 22 m 125.5 4 l 130 22 m 134.5 4 l S 2.25 w 148 22 m 143.5 4 l S 0.562 w 148 22 m 152.5 4 l 166 22 m 161.5 4 l 166 22 m 170.5 4 l 184 22 m 179.5 4 l 184 22 m 188.5 4 l S BT /F1 1 Tf 10.125 0 0 10.125 19.1853 76 Tm 0 g 0 Tc 0 Tw (1)Tj 0 -1.7778 TD (2)Tj T* (3)Tj T* (4)Tj T* (5)Tj ET BT 10.125 0 0 10.125 1.1881 89.5 Tm (I)Tj ET BT 10.125 0 0 10.125 5.3089 89.5 Tm (teration)Tj ET [2.25 6.75 ]0 d 26.5 69.25 m 197.5 69.25 l 26.5 51.25 m 197.5 51.25 l 26.5 33.25 m 197.5 33.25 l 26.5 15.25 m 197.5 15.25 l S BT /TT2 1 Tf 6.75 0 0 6.75 96.25 80.5 Tm ([1 2 3|])Tj -5.6667 -3 TD ([1 2|])Tj -2.3336 -2.6667 TD ([1|2])Tj -0.3331 -2.6667 TD [([1])-3533([|2])]TJ 17.2165 5.3333 TD ([3|])Tj -5.2165 -2.6667 TD ([|3])Tj 4 -2.6667 TD ([3|])Tj ET endstream endobj 155 0 obj << /CreationDate (D:20060928222820-07'00') /ModDate (D:20060928222820-07'00') /Producer (Apple pstopdf) >> endobj 156 0 obj << /Type /Font /Subtype /Type1 /FirstChar 49 /LastChar 116 /Widths [ 556 556 556 556 556 556 556 556 556 278 278 606 606 606 444 737 722 722 722 778 722 667 778 833 407 556 778 667 944 815 778 667 778 722 630 667 815 722 981 704 704 611 333 606 333 606 500 333 556 556 444 574 500 333 537 611 315 296 593 315 889 611 500 574 556 444 463 389] /Encoding /WinAnsiEncoding /BaseFont /NewCenturySchlbk-Roman /FontDescriptor 159 0 R >> endobj 157 0 obj << /Type /Font /Subtype /TrueType /FirstChar 32 /LastChar 124 /Widths [ 600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 600 600 600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 600 0 600 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 600] /Encoding /WinAnsiEncoding /BaseFont /Courier /FontDescriptor 160 0 R >> endobj 158 0 obj << /Type /ExtGState /SA false /SM 0.02 /OP false /op false /OPM 1 /BG2 /Default /UCR2 /Default /HT /Default /TR2 /Default >> endobj 159 0 obj << /Type /FontDescriptor /Ascent 737 /CapHeight 722 /Descent -205 /Flags 34 /FontBBox [ -195 -250 1000 965] /FontName /NewCenturySchlbk-Roman /ItalicAngle 0 /StemV 92 /XHeight 464 /StemH 45 >> endobj 160 0 obj << /Type /FontDescriptor /Ascent 753 /CapHeight 562 /Descent -246 /Flags 35 /FontBBox [ -28 -250 628 805] /FontName /Courier /ItalicAngle 0 /StemV 51 /XHeight 426 /StemH 51 >> endobj 142 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [192.199 394.973 199.646 403.995] /A << /S /GoTo /D (cite.CLR90) >> >> endobj 143 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [204.308 394.973 217.21 403.995] /A << /S /GoTo /D (cite.Rei78) >> >> endobj 144 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [224.027 340.776 236.929 349.799] /A << /S /GoTo /D (cite.SS99) >> >> endobj 145 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [463.826 139.599 471.273 152.5] /A << /S /GoTo /D (figure.2) >> >> endobj 152 0 obj << /D [150 0 R /XYZ 71 721 null] >> endobj 153 0 obj << /D [150 0 R /XYZ 120.922 598.937 null] >> endobj 154 0 obj << /D [150 0 R /XYZ 72 509.145 null] >> endobj 26 0 obj << /D [150 0 R /XYZ 72 256.034 null] >> endobj 149 0 obj << /Font << /F8 93 0 R /F11 106 0 R /F24 91 0 R /F21 120 0 R /F49 119 0 R /F18 122 0 R /F44 92 0 R /F1 113 0 R >> /XObject << /Im1 146 0 R >> /ProcSet [ /PDF /Text ] >> endobj 166 0 obj << /Length 4167 /Filter /FlateDecode >> stream xÚÕ[Ýä¶‘ß¿¢5ÈŽ,R¤Dyã‡=ÀrÀ%—x;À΃¦[Ó#[-u$µ×{}~Å"õÕì™ØrO¢ÈY,ÖwQÉî¸Kvx“¸ç|xóÅ7f'’¸H ±ûð¸Ëå.OŠ8Qx;쾋»þî>-Ò¨*÷wÒDOüVîÇú'z¯ø½/ÛcßÝgRF|t0í3°Ô”ѹïîî1r¸ì«ÁA»ú$º‘_ǧräñÚÁu­›£sËOnþ±ì•ƒ®šêTµ´ø8¼½»W‰‰>2*ûðŸ Á½Hc­ ÞkÕÔ§º-GšG¤~M!±ƒ»{-vÑþNDn#x{ì»MùÅ7B,É)ta°’]á=ì(.D¬ å!¾OtRØ)ÜÇ8–êÛ½‹,.ÒŒw®yã¤( M}|"ÂŽ ž¨«ÒèÔõª±¬ÂXé™#1ð’X4>âË'×./G/ü–Ž =1 V?ÝùޏîȰ¨÷!æIe«${ 1AÔne+¹• ÚíX‰Er}ð[|òh_6ŽO´‰e¶f“Su¨KKíŒo‰SdÑÃ…hpàÞ±ûA‚yÈ©pͪUGvÚøÏú®<ìK«Obän§KuTµnJÖ²Yô-°æAú”x~à‡Šž?vÔ¥ÝX(«‡âµÊ´Ÿ­”Ñ*‰¾¹38~;¬‰ r5sú.ƒkðú6Ëã»J×Zè&@¬LF½iB8…Îm#ųg/c‘eâÉ’'câãa-IGƃÈ%¢áÌâÔ˜ÿ2Xãì,Í݆˜¤Œ “¯‰7™;•3…¿$¦"«ÑYŠ(ã@†q´@wSWÜåduV̕ŭõ¬Àºq„­³°*"îÌ=PÝ$J{ÞÚmތїKÂãÝûÊäì?¨üCý#ãHkæ‹yÑ.L¾Ü­‹çD Ü8´ÝC×ÕÉ{:è{€ùï?¹©ª²Ÿx…¦sj!¬`¡§v }¬GäÂÇ“â~Zö†f‡™]y V˜X¤r—Æ…6¼ÛµÒÞp›‰‚Y€¬›ÆíðÜÔãŠF–‹ŒOaVx&QîØ/ÏX°sMC" ™ce…~rE¶ÎV'`͉› ìà”[ ëpM9ˆË‰'ãÂóL'Ê(Lò›GîݲÑÛË8«éU½56 ¿õÁº °Ø@¥k ½$mÞžê¦ì›OwFEħ:u„À Ã-¶‘~îÜϽÜFê¶±De% i6©!4ÜÙP÷aЩQºxÉ×¼÷0+Ú8Óî]hõ¶úÙ{Þ á×®m>¹GuߤѾëûjpžƒWgéÂG{bò9œÃÐví}u:l–¡î¯ý(S>ü¾;/“QO@èúä†Zæ#!¢X˜1Î[Ä0’º4cÙVÝeàáqÂÞÏfmOÊpàŽÉglÝ~Ÿµí1¸0)4)ôÛcNòY}‡¶ä±Ì’%R´¨õ1©]2Y4Ú µ1}(àHó8‡X;&8˜Àİ#Ëxéÿ}I&Ô»%c¥±IRÿÅA»ÏïCs@«9OE`°¶TÁ=äð½ŸÝ‚Ž•)[°q‹L˜Aˆ8ÕÏûê<“’xºG?ÞX—‡†è¼Þ>ð)á2UÜûÑ)KûÆj.|ÕV@{”Rª±ÝZ‰’ç BNFDnŒˆxF$´õ´ÖÓÖþ<¾L®ÂÅ .ˆ¥J_EAÆÏy DNEÎ )úËSwi½¶¤GP ™Á]U¿ öò9ìÁãÅä^ÿ.LH€¨Í+KK0ÖÎ ;{µeBîP·WKŸÇ8C†æÈp$g|3dš¶^d eÐ7_xówç6xz§á&f·?½ùîoÉî€1¼XA¹|´'ìN¤d šÝ·oþÂyžÕj4S¦Ó:ö[ + ™(P)Ïä/ÜÿgØëix5‰ÒꕌOºè«àl&ÎùM„ñÿ=+jŠ‚±÷_Ί4“{½ÄŠ:6ÀgÁŠöôØ¥ÍЭɭP¦ý÷ÃCc%³#cu[½?–uKÞ¿´~ ^VöUªÊĉx+ÜàPÈÝ, m`¢ ÞÙªæd’¹4»ÑFØh®ý"c¿à7Ü…¢ˆ¡µÿMX;×kÿ)oÃHÏ‚{KN2©_’“ìJÝ,7…UÒ{WR¬6µ^‰v¡>ëÐcÇÙp¾r€lü`²ËJ çmR«¿´®/œ¹0$$ûíMs@¿ZDÏåž²É*3ì…ÙÎ=8Ú»GÔé+¹E®…;Ô&ÇŠžO]s`áí+›G©\Ôäàåè¾.) p½çr˜ˆE¹}vÅ¥†:ÝdRi«Tì Á‰N¬rŒÑÖ{'Šª(œ(=ç ÒøÀ¢:|ywŸS˜ë¡Üƒ…˜²unÎÙh§gˆnå44¶;Ç%ûTZ×ÓF-ôÞµ¨ÄkNW–¡Êj¥àɶ‡Cä2þ¹«[¤øÄ©eo.E£#ín=Zôƒï-Ǿ:r­‡^—þ-}ô¸]À1‡Ë[z,àë!¥É²¶óÛj×âs´ëç*Wý:µ1 ©x)™.Ó8}eÄðkÚÕ4e‘a¹FЪVéÆ2Mî™ØåHË8…È DÿÝÛ˜ê—̆ê1‡šª™×$˜ˆÂIÙ×sÞö™e.l!”’eýÒ¤WÎè—ܹʬãýTŽ}ýsØÀC>gÇåÛPÂ:Á榷œ)-5ÔèÿYM.„àC±áËuØŸÃB¦3Í^?“Å/V«¥~¤‹<>7›ñ×jÏÊÜ"W‰»2’*Ngy½I¬ ÂfXÝÑLL´·çcÕH`½"a˜ül?9W\­$;ËW’üõûhÃG@ˆà¹´fÿ2‰( ¡ñBMÅ—›=|ûôY¢Ÿ†ê¸,¶§Úp‚7d5Tlö„¨{¹1,¡Ïú_+>ìg®Æ$AíA Ï“|E¥Ø @OÚ¬pˆ×d¬Í‹Ø¥ìHzÏá‚Pž­q ª@ä2­y®™å&D‘͆ @ ŠBëÁì'“Ôÿ<Û‰ÏÕDXà ¾›K¸."Ëù‘ù·U  “tZ€j ‰tæÓW'„g¤SCR lô³zÑš ½“„‹´Él/Ìõ¹æY>ÙHKSfÛQh•ƒ+Äåà«lsí¡»ŒÐÔ$ð®ö$}¥B.2ÌVÉEa“ëɾ}šy1CÛq<÷–o>1IC}]‘Ëè¾8Tð§ëï.–7À šÑ›¼Ã¥w°¶+çtõ¦€²ïX²me@%6 ´O›ÁfÕg_ýFßÑõm‹d*¡Œìr` ¶/=´ñ'³¡,ϵÍί¦¤ F*‹þç‰ürñõ_jƒŠŠ<Ô¶·J‡hÚŽ-Éì/p¿C¦¡PÅ$Œ§€0öz„‘"À%Ôõ-¸3׃e›îÍÃ\•D÷wA†‡þO' ºƒQß±h5¡Ua+e¾uЮM³Ì§-¦7<ÁtNë÷*¨¸‚U’¹­H ,ånc/$sñ^ú;ÑÂÅ)›ÏÄÅÆªšù>Áµ/˜Â®Ê™`!eš*±:¿:è2Y¾<¾C ¨ƒIß4õàH’÷ýÂzp†\®7%â]‚ª¸• Òy¬ç'·ÎJ†ÎjËD¹Ô¬=¯¸Ã$fCã­¿!˜èUlI•™Á–cÔêvõ¤® Lc?Ýé lq §ãd¶|fÏÈgb3 (¾A¨ OÝ…ò „̤Téƒ-`ÐØñs1í­ŠE^ûß×LÁqjNI+òU,ÜÿpJbM·$%¬¾^_RX]­JsD#n¨"DØ[bòWäÁ_£ +iþé“~§lUm=™'žÙè;Î7a¾ÈUH”ÄÖÁ;©â$qbY¤ü«”¿ y)ÂØDQZ“,ù2/ ½.‡QG_ýýR÷+§½6üåŒk×ÅuénØ3#94Uúo’ÚUqžÊUn×U¶íe`~:ËsFÌ3:úŒžPÏä…‰õÞ®“Í Æw|÷Éͬ8H׫ù ×{üEš´€l«M}Ë_ ³QÁÄ£z±«ÿ™ZÖ¯E{ŸOέÂÔZr>Ys­E±©Ï'Ûl²ÔÑ×ì5¤ÂVrm=Í)L%«åÅ8„Æ0®.„>q‡+lðUDåîâ(RQwíž%?%#J¯~óN%CxÿqúÐ;.]Ãþ‹»þ{êæÂA®|£Ed®Î\i÷·5¦¸ÏU¿Ýà'nÁííGnZ/JÉ<£kõAÅè1 ÎBÐGÎÉo:”‰oˆ d¤»6ìÖ#§|º~–GïpwëßáŸîí®.OÀÐX˜o{q"-Ìtq‚<>ak‹«Б`¢<Ï\¾ZAÝ IÖxÅ¢©±™@ò¥ˆE×ÕMâXôýÞíÜd‘» à|WØÄæ ›ðW)…Ë…±™—žI2VÍppù¾+ãR,p¡‚Äìr?°5±,& GÚ‘CåÁÎíV­Û¡ò7¸¥ô¿™~|ÙÏ#gžC.3q>›×&€†™{vÖ7t§Ý©;¸6"Nf}1>M?¯È‚Ï¡ãGÌ;ÊŒù«ôcKï>hís%ç)†òdÚBßú'žŒ ¢ÿy.|{_ò_ô÷IÅ„Bÿ]§ÒéW¦%+߸Š.áÓ›FQ„ñwtôVþD½í endstream endobj 165 0 obj << /Type /Page /Contents 166 0 R /Resources 164 0 R /MediaBox [0 0 612 792] /Parent 98 0 R /Annots [ 147 0 R 148 0 R 163 0 R ] >> endobj 147 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [412.118 691.768 419.566 704.67] /A << /S /GoTo /D (figure.2) >> >> endobj 148 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [232.246 665.276 239.693 676.966] /A << /S /GoTo /D (figure.2) >> >> endobj 163 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [469.136 597.53 475.861 610.739] /A << /S /GoTo /D (Hfootnote.2) >> >> endobj 167 0 obj << /D [165 0 R /XYZ 71 721 null] >> endobj 30 0 obj << /D [165 0 R /XYZ 72 417.749 null] >> endobj 34 0 obj << /D [165 0 R /XYZ 72 185.547 null] >> endobj 168 0 obj << /D [165 0 R /XYZ 88.588 130.78 null] >> endobj 164 0 obj << /Font << /F8 93 0 R /F11 106 0 R /F18 122 0 R /F49 119 0 R /F21 120 0 R /F44 92 0 R /F24 91 0 R /F1 113 0 R /F47 95 0 R /F19 136 0 R /F38 97 0 R /F39 140 0 R >> /ProcSet [ /PDF /Text ] >> endobj 175 0 obj << /Length 3211 /Filter /FlateDecode >> stream xÚÝZ[ã¶~ß_áG ²ÖŠuKЇô²m‚6iÚú°ÉƒÆÖØBdË‘äÝßïðÊ’†ž™ÍhP 0&©£Cžû…ŠV»U´úë«hñ«ð?Z©•*’UšÄ«ÍáÕ/ã"ý÷-¼ùæ Wn_ý€¿%¢5aZ ª?Þ¾zó6_©(,¢B­nïW…¢á*ÕEhò|u»]½ ÞÖ»sWݬã8â/oÖÆ$Á×GžWË鑇í=ýš`è*Y9TÝ®>îxòa_`/Ï7:Îúw³Æÿª›£:u-Ö³`ƒTÐ÷m×3@-¿Çv`È’ç'‹¦%Œn<uxóÓí·øL|±”(Œ¢„)?•]Ù4UœE`óÁC“²Ùµ]=ì4UÁ‡º¨M{°ôœõ¦*^¬Þ߀ˆª{iS*KûÀ ÷]+ˆê¡<ç®›Á4­% HëöÈKCË/•W!ê#‘ ×*S0]]uh+‚":´t.—ƒ¬µ½ŒÚ㦠!òDߟ;ºNS–@OC}(Y<òï°wûêØ;ToorH¤s ýPb›×2½woVîùé<Œ[y¨)›®*·ÄÚ$±Bª¶¯Ii"(/nËX”PF‚xo5c"ÀïÀöžN©òào-ù ^:#½cÏHh匴FôÚß#?êÜ»½Àò eÓn;"-f;­C릔Š:NÔ†˜ªANI3’ž†)T„–F¤ô+6ÅVÌÌ)æ¤Ô·U=·JdYaTpËbÄÃòОÛèÖ®è%ËâË>ùeÈjqN=ÌÜ$èQמ‡ÑuôCub8V7o•šº-eÒ0ÉRlcÑÿiÍ`3ç–„F› LùP™P›Ø=X°“É'XTÈ@&[)xÏ$ѵV©3¼3 µŠ\‡~Lé$ø‡xÆÇøÁ*…Iaø•ïD a8p·å†D¸çÙèÉ7Väy}_ú™¤U˜Ç#Nž½14±vNS g¾[³Ùl8aè¬ —Gû؈•0¨—N–ô””ŒžP8 èÛG5C H)ÛIõ Ž»¦b$rÒ&è’ušÎ•ª¯~9Wâ¯ò :KŠo² ¯%Da\žÀ´‘‰´âN¢üyœeÁ½õP´¼ÇA¬¢b‚÷ï:;¸EkE˜oÈ_ž†3ì÷WîÎu³•½ö¶ÕÉS‰”µGÁ­ÕúD¢WâœbLDa}jÜZŸÊO÷KS-^Ù”}5bó©G‘…éÓêQ„ʌÞüU'ˆ%'ž(ÜÏ ÑâíGîc­/I,‚Óa´¾•Xà^'ØŒ ÞíYÏ<é8 ‹Â·öГ…FôˆÕ¬(B…*׋ C”!(HhTE F+ )²ˆGªbà ×wMÃ^O šª¼gôð½œêAá–ŒJBÖk$ûÖŠ›w%üÜóŒ€×­_ÅÂ;¯€³<4‰rôþ,n«˜Âä!¼ÄŹEÊëFÕè }L€›MŒ‡ óÓÄaœ«¾òìeÂÜ/qÙzAV¾¤jøì-t ÈuûX 5Nö< tñkx/Y g,°g²’dçQöí¡ò¸c´zŠj¤ðE:å$ò”ÿr9Ðí¤hø2àwvßx–Y@|#6P‹Úm ·ÛLœfa”ds›q"vNhZ¹ñ šñ\J¥a–Lä¡ yXŠ/|¥aœŽºÙì¼V€ó›{¹ùž^>J·º¢•ã‘B’RH’.‰n”",{¤T<±ž ¿ Fy`ùƒiõc¤ÒM0ö@¯’á+E^2 Bb( OeÝ-Ð-´<<ëö~}jʼÉõ$£‘ HØp±Æäºx!_Âú}Ýž Aš\ê;O'É)&ueã à$+[›áÙÉúøªƒÎ¨àÕrÓµ½ *ùÍ÷7 Æ]] .R¤R¦ã×h‡žS`L»þ‡Y1lWÝ»M¡lq©’p%£ êÓjµê®Æb¸•rv”ázÊ‘xLŠmÛùTR­RâDfLÃüØŸbóä5ÏǦùú*¬Ý#_Âÿt£D[}Òü®jË{“8Ë5\VcÁi†\êÒ³ƒ ðïr^ Q7×¹);^®l„=&ø´ø±Ü œ&%¶Îä9*QOµGY¾;Ã2âJr¾•pµLƒYrè!N’¾v¥1ÇJ]RÄa¡Å¨ã5·›s~ LÙçŽ`P0ÛjÕšOÕÉë¶ðûåÀs©æ°b‰ %”F`MÝ·²µuòôà’×Û™ÔBY0¢F瘨§¦z‹¼’ÒNq±²UkÎ]‡8ÖÊóŠ”G˜nD »Ê2£©l·‚¯ÑŽ“2Öe=Ià-nþ± <ÈSÁ`¼•å™’7­¼¹G8¨äQ#$4ãNvya»c¼#i†’Øzk-Éq%*³Y­‰…iW ç¥r|Ô{ã’V&LÒ1ŽlŸ‹ŸŒò§‚Q¦éæIÆ6]DÁÄ)ÑÂ?C;ØöLb›T§ó }¢ØN7ðèJq§H²ò¹‹”'ÍrED6ËyæôpÒ3ú²n–]J7©I³ ðõ®¬¯¥‰9,fZ.Îr±3×tœ<ê›IMÔËg/V#{±ãx!.B([˜Q\ &' -+žWF¨å:#G¼ô:^ýåvÒO6(³ ¯`{aªRj¿û)ZmñÆš8]}° [¯›Õ¿_ýÀåyeCð©ZÅ:Eîœ>)µGŠÃ,Õ"¹­7¹M!¥ç5ÄüÖ™Þ£FOŒº93‰4z´kô$Òè'‚B8´¶—ÙÚ˜\ï%Û¹Þýû{Eš’*Ñ£T¦Œµ¹nPí/tÂD#ÏnE'ÌDHX¿š•#QìQØx¢è!L‘dï­s5X¥­ >¡åö—fþöJç<™öÞ¸ ùÁšO©UˆTÚ*¸°Q=Vp«PZë0ò§ˆliqMÁGTŠUÌ”nQ¹A-ó©‚ßzͺÂÞô ÛÀ˜½¨D~QW“•‰]4Î[*Ç&T¹šÕfÞ¾cé§=uˆüExF{þ§7&jâÏ× Üì-¹Ô±JøwbLí3V¥PÚs®XUÛó‰!ë£ßž`¢—NO_Là”Z”M—dÎvܳ€û<<á6¦ÍNjkä´æ®ž,ôäþÓò¨;!Çðš'ÐõÒ\Q¿ì%ê—ýÖêg[úR¼Q¾ù±<ÔG™°ô‘vDž°©>"ů¤“§ ¿d¿åKÎ=½a˜ÜbJ“Ñ–kŽå. Ø×cBÓOnw3y\æ⬯ò/}+L¥T²0*Ч]6u˜DæyŸ®ÖY¨iá)Ÿ¿Ôgë_ë³µÖÁþåôç{¶û‰w8'OpàÌpÂ5í×ɺ3zIžqôºÈâKÿуnŸŸð%YˆzóQÂ3%*·vø˜¨ùNDÕŒ¨F–™ý¹°}Y…¹Ê_–t&W“ÎyT‚x¦5ÂÙ?#é$TEf€JÛð§$^1š‹&×þ:)zæþäå²$æº×HÌV¢ž/â@³2×$Øä$ O0vMÈœ·`YŒþÃþÄ-@ Ï}¹“aÙ×[JçEÎÁãý; ½÷ŠZå¶áòŒÕ&ŸÔÆ¿fµ³üT{í$Jç÷ _x‘r õLõF‡ñJGi˜Åñg©!e€ÐQÍuý¸h¡ZDÂx ׂÃèDî^Œ2tõd{ßóßÑODÊØö|ܺ@™öîo(/lRÁP¿sÅÆnæüî¿v ÒEHDe{n$"º#F\NÝ[þÜ„ìB¥ädݤ}¹hµ;þ¶ŸlrMY]/k׫x2ÞëÒd’UÙ)Õü7ÒÇ$Ô6³Ïº»zèJbý¯\>¢gkxºó¢æ„ÍHsiY.ºo錟A@²Ž”ä|9oðâeA˜Ú†z®yÂExDî®Øz“$syÚûúk%dL÷›úÿ­„4yä/!)¿-¦^ÂWBfpnØÂ@tÙg‚(†Ë‡åcü‰å#iÖ=’’\)qÞi2w¥ì©:}²|Ä0Óŧ!z¶~´Ÿ¸$¨¹4$*楡᯼|É;ÒX“=÷¡Íå*~^^Wòåjú8Í|_»LÊĘ?‘áfe‹T-\)ÒÇUgW&ºf¢/®£ö˜$<¿Ë.ŽwàâX’Àh士tãý/ƒ:” endstream endobj 174 0 obj << /Type /Page /Contents 175 0 R /Resources 173 0 R /MediaBox [0 0 612 792] /Parent 98 0 R /Annots [ 169 0 R 171 0 R 172 0 R ] >> endobj 170 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/tree-merge.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 180 0 R /BBox [0 0 222 67] /Resources << /ProcSet [ /PDF /Text ] /Font << /F1 181 0 R/TT2 182 0 R>> /ExtGState << /GS1 183 0 R >>>> /Length 504 >> stream 0 G 0 J 0 j 0.562 w 1 M []0 d /GS1 gs 1 i 33 13 m 42 22 l 51 13 l 69 13 m 78 22 l 87 13 l 105 13 m 114 22 l 123 13 l 141 13 m 150 22 l 159 13 l 42 22 m 60 31 l 78 22 l 114 22 m 132 31 l 150 22 l 60 31 m 96 40 l 132 31 l 177 13 m 186 22 l 195 13 l 186 22 m 204 31 l 213 13 l 204 31 m 168 49 l 96 40 l S BT /TT2 1 Tf 7.875 0 0 7.875 30.75 4 Tm 0 g 1.6856 Tc 0 Tw [(01234567891)1685.6(0)]TJ /F1 1 Tf -2.3494 0.8571 TD 0 Tc (0)Tj 0 1.1429 TD (1)Tj T* (2)Tj T* (3)Tj T* (4)Tj -1.3145 1.4286 TD (Height)Tj ET endstream endobj 180 0 obj << /CreationDate (D:20060928222826-07'00') /ModDate (D:20060928222826-07'00') /Producer (Apple pstopdf) >> endobj 181 0 obj << /Type /Font /Subtype /Type1 /FirstChar 48 /LastChar 116 /Widths [ 556 556 556 556 556 556 556 556 556 556 278 278 606 606 606 444 737 722 722 722 778 722 667 778 833 407 556 778 667 944 815 778 667 778 722 630 667 815 722 981 704 704 611 333 606 333 606 500 333 556 556 444 574 500 333 537 611 315 296 593 315 889 611 500 574 556 444 463 389] /Encoding /WinAnsiEncoding /BaseFont /NewCenturySchlbk-Roman /FontDescriptor 184 0 R >> endobj 182 0 obj << /Type /Font /Subtype /TrueType /FirstChar 48 /LastChar 57 /Widths [ 600 600 600 600 600 600 600 600 600 600] /Encoding /WinAnsiEncoding /BaseFont /Courier /FontDescriptor 185 0 R >> endobj 183 0 obj << /Type /ExtGState /SA false /SM 0.02 /OP false /op false /OPM 1 /BG2 /Default /UCR2 /Default /HT /Default /TR2 /Default >> endobj 184 0 obj << /Type /FontDescriptor /Ascent 737 /CapHeight 722 /Descent -205 /Flags 34 /FontBBox [ -195 -250 1000 965] /FontName /NewCenturySchlbk-Roman /ItalicAngle 0 /StemV 92 /XHeight 464 /StemH 45 >> endobj 185 0 obj << /Type /FontDescriptor /Ascent 753 /CapHeight 562 /Descent -246 /Flags 35 /FontBBox [ -28 -250 628 805] /FontName /Courier /ItalicAngle 0 /StemV 51 /XHeight 426 /StemH 51 >> endobj 169 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [476.565 448.108 484.012 462.055] /A << /S /GoTo /D (figure.3) >> >> endobj 171 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [429.034 396.638 436.481 405.661] /A << /S /GoTo /D (cite.BF02) >> >> endobj 172 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [441.156 396.638 448.603 405.661] /A << /S /GoTo /D (cite.Dem02) >> >> endobj 176 0 obj << /D [174 0 R /XYZ 71 721 null] >> endobj 177 0 obj << /D [174 0 R /XYZ 139.667 643.038 null] >> endobj 38 0 obj << /D [174 0 R /XYZ 72 524.294 null] >> endobj 42 0 obj << /D [174 0 R /XYZ 72 309.312 null] >> endobj 178 0 obj << /D [174 0 R /XYZ 166.065 239.433 null] >> endobj 179 0 obj << /D [174 0 R /XYZ 72 156.753 null] >> endobj 173 0 obj << /Font << /F8 93 0 R /F11 106 0 R /F47 95 0 R /F21 120 0 R /F49 119 0 R /F24 91 0 R /F18 122 0 R >> /XObject << /Im2 170 0 R >> /ProcSet [ /PDF /Text ] >> endobj 194 0 obj << /Length 3012 /Filter /FlateDecode >> stream xÚí\IsÛ8¾ûWèHW"4öe–KOMf©šš™Š«úô‘h›U’èˆT»óïû=”H’‹ödR}±¸€À[>¼0ÝÍèìoWtðûãÍÕïìŒQâ¨c³›Û™UD13Ô*áÉrö!ãäúç›î?š}˜+J³•›r¯®ç‚²,_W»Í57Yî«Ûð»ØVu=¿ž³ìášeÛêzÎm¶(êºÚbž-ªõúží6å"oÊjƒ]ýðŽ±î ¸²„;½ûñÜ„6\Î q†ú&†X7Ä)Ú|¤TÄf¬ÓlΈbj6×D+Z.B³4áÒÌ8ÑδäMLBCÑŽë—%E´qm ÂÞâ¼mÖÜC?û5òîpÇdEäLËÂbU¬‹ÀÛ:M„Q¨ƒZDå«*#‰drÖitJùöF±Iª±’¶«|aMœTÔžpH®ç”àïJ#ðåÿwë×´íÊŸ~‰ßÞ¦†Ç%\ðWfˆxC–Aã‹:Ü–ë‡Õ—WÆÌ–k"{Œª'èÍŒµ8~J‘xXÕc·}©1Êö-ã<Å ¡dK#ößt—b:2à2I†cö¶ôŸÄ`-0i¬¨µÖd?âB³……¦,ð ÀMÁÍ„)Ô^/T¥‡ßm.ƒ’·w9þ˜ì6¯›"=A +£R_#gú|!Õ)d>ZÈz`0ñ[¿"ÃÔëj„7ÅÁXÝ@²ƒµNÚ½d#ÃÑ›.ŸÃõ¢Ú­–á².Ãú¿Ê›"  l›jÓI"……Žew«".JV£Šù0ô2ÎnÛ‹¦j¼Ë‹ v‘V=pôA…S†þ(c{FÆ:ÈX–±š^ÆO ùÉzåWôØèOamLΖKPhÙíäR!׳•§ó礀€ÌÈeÉŒY–ÌÔËÒÛ ­÷eëá-º?ø›‡ŸD\·m¾,è>“ˆ¸!Ô)Ž\¸b„iC´T­[¯ã*ûKµ~Ø5÷»7:p‰8%Ê ¼>¡T¶@÷K(¿æërŸßž¶Ö†-ý`×´=…&h…þêS»ÄBë|¹,7w-‘»ázH;~¨¢qã òÒ¸wàÈGÍ¢Œ£¨Öó”›cè¹{——›º‰ ãõ=âõ}Qm‹â”Uza]Üø>€?¤èÍ…!´Õ¶jñ"vÔwzp°LöG¥Äfí ¼:_ýõæêózftÆf\b¡•R .+f‹õÕ‡Ÿél /A-À…Ó³Gßt ëh–«Ùû«ÿ†X¹×Еå3%Èã.òQxQš‡VE’vèœrγ7Ie3Dü£'ý#Íe‡Úy͈#g¶3ۤKˆùâôx¾7ñÐju—4°÷àn’ƒv‡%éÍpyÊ ݤ{ÎìÒ”õªötRýžpV½I (:2ë+A_fÏÕ_a&Fàè/’r ÿçèï2Á 硈ŽW#}I»pëèQKF Æ@Í0vèƒ C£D_…%0°ßT+A‰‘œäaM‡‡ƒÜTK ÀPjÝ™ˆS[„ búf| 41~¡‰õD4©Ô9¤RcŠŸC*7©Æ•zi."P™³@¥G›©ÔÅHe¡OnÇYÙ¤RŒKA(¬y5¤Rg‘ÊöjÐ pÇËô¥2çËNE|r/y¯C/óø®Zc&£ö7¸¯«mµ½m L‹æ[Ÿ÷µŠzÛ·)Z¼ÞÕ}Ú5èT¬–!Ó‚UÒ^*åÐçcÙÜ'E¶Äø·V'sOÓ¥}«Ät)Q·@µ¦½Òò0‹a8‡ú(2 ¶Ràä¢(i±R|,qi Z?ÀaEv)œöËûp†P˜ùD°„ÔÛ''ÑŽ•U¤ ÔÊ“cˆQ]ÍS)ש§x,šö¾Bð¹Z¼h¢½SÄ 4èO>s^ ÀWu׌Ûã…ϽâU,‡/Û0ÁùÓB¡_TÖÅö.fY¥·Õ÷Mñ^É£¹F&¡BöWºKíêDv\ãìLrè–Ú‹²ƒàÓsô²X(U~Svu‚ÏÒ¸I8¤3Sq W¯Jû—½•úÙ0ÐJLà %Íótà5ãV°X0ʵ_Ó•°gV9ŒVÍT«Ük¤Ð¡™Ä¡œeSãœÉ£ÞãÄ÷¿MɽDz‡;î¹0pœ Œª©„3¬£­%‰ ŽS¹ÈÔ:¡œ— DDôˆUa®ˆžÚ–¨„{ƘÎhJbÜsfáFãRnˆÖ#àÀ¾„Àp ¬‡…½ÈÚüÒµ¯ä³;œ‚ˆñá q¹Ý>Ôõó;‚€[A*ït>È&¡RA·—OwO©;Ýt¸ pw¶¯“6“0<¦§˜¨Äjœ˜J®ˆ´n ^ósxí¦N½Çk>^ËËñÐ ÂNL9®ø¼7¿C‰t‘ÒR†éW9›)(ç€sÉ䓾´ „ž®´â.$Ð:ö#íq†qFÀ'éÖ]ßA¦çÍ;ÛvFøgž?²ùµCj‘Œa˜TRÇTžª‹÷»LÒ™QpDƒ¨´»íö,$ª&×såd¬zÃä{GPm4+'Lýãeµ‰–ñIÐ+³mµƒ…¹âýÃ=Üæží'õþ“þÇ¢£v‡1ÁpIj:7XÁ½õ&:<ê/AŸÓ…„Jó²^Âb2C>r4Ö&Ì7úKñd›÷z ¸…õc}7^Cê b‡9i²‚ßÈN‘exDuß±çmkXš{L(È™qP¸£(¢ÍÞ?ä‹â<,Ý(&žò²‡@9é/YBȳÛôÙº¬S¦§oŽT •Ø<Ò9Xb@`LúÐî„›ÔêÑÑ Z€´¸BáãQº~L7<-†'9±Zí¹&zÿàÉîGI(Â\¥‹8iÓû¦õá0Ñ endstream endobj 193 0 obj << /Type /Page /Contents 194 0 R /Resources 192 0 R /MediaBox [0 0 612 792] /Parent 198 0 R >> endobj 195 0 obj << /D [193 0 R /XYZ 71 721 null] >> endobj 196 0 obj << /D [193 0 R /XYZ 72 720 null] >> endobj 46 0 obj << /D [193 0 R /XYZ 72 617.915 null] >> endobj 197 0 obj << /D [193 0 R /XYZ 166.065 319.052 null] >> endobj 50 0 obj << /D [193 0 R /XYZ 72 267.183 null] >> endobj 54 0 obj << /D [193 0 R /XYZ 72 169.95 null] >> endobj 192 0 obj << /Font << /F8 93 0 R /F11 106 0 R /F24 91 0 R /F21 120 0 R /F49 119 0 R /F47 95 0 R /F18 122 0 R /F44 92 0 R /F1 113 0 R >> /ProcSet [ /PDF /Text ] >> endobj 208 0 obj << /Length 3636 /Filter /FlateDecode >> stream xÚÝZKoä6¾ûWho2ÆÍˆoiƒ’Å$H×&rÈÌAî–ÛÂvK©;3ίß*©—év{€Ýö$Ф¨b±ê«+K¶I–|s•ùçW·WŸ}­lÂ9+´Éí}bEb³‚eŠ'·›ä÷T°üzŹÐé/Õ§º«öUs-yzì¯ßß~÷Ù×yÂ3Vddzd%2¦ Mß~Sÿy-ò>X kÓãCÕWÔ¼ÃGÚžšM͸©{ ËZ»ä׫ÒÎþ§dƲÜ$&W°S=Ûç| 9+„…ZÃÃ41Ã.eñ*žÉ×òlñGÎ4×áMDò Ó*ØõJ {œØ¡kˆ-iÕñ‹GênïI„þ¯@œµòTµ €:RÊôPvånWíðM¥}ÛëDiKïþ ~ùé7 2"öôåþ°«ÆÏ©”*«Y…"ªTú»Ó}Tù,é¶ 5~ùæ*œÄ€ÔÐqóì\÷ ®_õL¹X~ðÞï¥ì‰ê¦=Vêªzž#ø ÅïA˜—‡™•AC‘3cu02…72_6åî±w§j5ýž¤šÐøêן©ñ*-O7p>/š¡/A Œ á ÒÿZõÍú¬ÏC×6í ‡“ÓÆ© F‡ÓÑ©6Z•MÕ¯»úŽ„iCS4üðCLŒÁhö­[ ¨÷¿/iÑþ±?V{jé¢ÁãCWUÔ‰dî+ ¬ÿ{Tß5ÀF~Wšý„‹F.Ï5Á±°2uÚvr€è9ÐÑ(’{‚ž9k¾vØØƒ¨÷ŸGA\H&ü¿¿g™z$V-FBöuSï=1®£Üƒá±`JбÞûO<ÁNý Ÿ/#ýé®<©¦ßwΙ¯…€âþÙÔ+bƒjõ‚[kg)>G™äiÙlbû׆ImÂî¶Ñ³A™·/3ã€-ËÒ}Uö§®¢G\Q¤wð§õÆÉ ¼:maÚ?¶î`¢¹5ú¾Üú¡¾þËèô××¹LÛÝ®ulªÁXºåHé`jSî]'¶×m3š6bl¼'ÖTpNœñº‡¤Jqè²( Ô»wWMÔ„ËÄ(Èï2É# …&M¤3ô@‹— z¡]B£Ã#Ħˆ&N€ëèÔ S†Ï ™„šz¦nB»˜_ÔñÏ‚üK¹°Hð#•å(ø˜m¾­ „ó4&O­÷5Ù!8×ò…αRÀÿx‘¼&ÆKÎÀ;›òÿ=ð_ܱÑÔ;ñ-ltÉ‚.Áp½F´€IÐFNÐdïô“aEd/™mÞ†s&Íxn<σÙ ‘‡S aü1¶Tû¶{ôív”ðàlô4ä4LRÂcynhrJ‰ô·kôýü2ø!8±Y|@4×}숲ÜD'+ä_µ@GÓ`¼—¢‡÷qÿ>{A¯0:Y:„göuê¹ÀðMt × SÚ(^Š1vCÇŠ»%ø0±x/CC7wˆçžjÁ´Táo(,yc˜9«Áqj6ÞßTÐõ?}æ\¼¤wÏÕÿ@.uÉ)# ‘ºàb&1¿!Tôí~CôñJ’@Pxº{œJ‰ŸK* ‚ÇØ1Ù~c^Fb1Nr *å‰áåí'M´ ƒ¥ Æèæ.V÷éDc»¸(A¬©‡c½õAŽšÄ0–ùˆ5ûÈd$Z‘`¯ 3ÁÄõÑpT€Ã+˜i¤3¡ %‚õÛ bÍs=vN²ùE,ÇP°"—°v!}(¿ŽN²¦ì¶‘•rÆq÷"Q!Q„‹Dsëë3‰ ðþ”åç2*g9œ‰º¬µÏ»°\³Ì—©cÀÑÔíƒS‰þ&Æi䳣ЌI!^pÀöˆ8TI¶QÀ^dþڤȜŸB€“4ªÜ°BMüÄ SÌù¹°ëa1k üVsI˜sÀ h÷Äæ " }Á6óH¾l™®AGq2+ˆß ´:#€BCP‘™K&0«)Î2ÌÀ‹KÂl­~Â0p¥À¬åñÉEÎ2{Ñ Dðó$‡Åf$Ç9>æì.föS·VM·}‰r€ºeò%;žÿì8˜K@K˜ºBm °àB¢S‡Ù p +4ïÊED.S&­>Ö.Pqq…Îm{«Ü0˜H9¤-½Œ $zŸä…ò³iͲx û–V’‘Ê ösSÒfÆû×}¢ÿ`.òøSâ…*°—„ÚÈxˆj6Uû.ã’Þé’ÝNNíÚŠz±„ņ7º©Ö]Uö®"^ÛÆÏì¼(Ù|¬­È Î.-Æ«T´ˆÎ@:%+¹Æ‘êٔǯ­¸¶ ë„…c²€`»l‚,Aw¸±Å¶ ­ ýÜoЮºj ÿ¼+“YúÓ©£¾êc(©ƒÄ{—Í.4»ª?íŽ}¸XÙàO¼'á霋àFоÚzíç„~ädÛ”w»jR(¬L¨/ é«#Þ"‘’"± Ï9(VtÅ«!`e=úA¨S%t»`5÷× X1ãxc^¦~Sî7Ý+ÛsŸMÓ8ã>ÙB\¤ÏWB;xá7=b€+Ò±dMA—t¨Pˆùµô“kT¡ç¾œ œ1’ˆjs:Pþ§Ò&±µs¾Úðߣ ¤XVÌ-ª¹2°Ö“Ë,/„ÃûšB8ËóôÇöèP¹;QRn¤±«÷e®~cƒU2¿›žf:4·ÍñÕU¦ž[@J½¸jIôŒ[uð×=¥/ŒZ^›/y uC]]9cÕÍ|­úØW»{jS©Œ[ú9Ó ]Š[¿T3+!t7 Õàÿ#½cÉÃLßRã˦bz¡Ô‹7Ëf]=n…«PQˆ´iQ°?Ð V‹b‹„ ŸÕþPw¾ˆ&xŒu•9<ýðP«…sûÓv[!tº¨d¹ða•[‹¢<7uLùá0n= #…í)= µôùëàyi…³M`0.©Ä“Œ¿lkúêx:¼\w­„˜Ë,3v:â%Ö‹„ÓÄŽApÚÄÒ¸¥zþãÍjœúaê?K œñ2ŸVåÑ·Nذ±ð 0tå " °týóZ›4Qîqû5±ƒl(ÇÅ}é½:8ãr¢3ˆN`ØUbrBNxÝÕw]éêTàÅ£Þ8êtÊ/ðª»/ו«K+\‰½nûu“¤R¿fsÛèTÚ@ùê>×z…lÎV!9ˆy¢ÞܧâòÙˆAÿ j`Ê9 endstream endobj 207 0 obj << /Type /Page /Contents 208 0 R /Resources 206 0 R /MediaBox [0 0 612 792] /Parent 198 0 R /Annots [ 188 0 R 189 0 R 190 0 R 191 0 R 199 0 R 200 0 R 201 0 R 202 0 R 203 0 R 204 0 R ] >> endobj 188 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [375.398 657.657 382.845 666.68] /A << /S /GoTo /D (cite.blellochsort) >> >> endobj 189 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [387.519 657.657 400.421 666.68] /A << /S /GoTo /D (cite.SS92) >> >> endobj 190 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [405.095 657.657 417.997 666.68] /A << /S /GoTo /D (cite.HJB98) >> >> endobj 191 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [485.156 657.657 498.057 666.68] /A << /S /GoTo /D (cite.Goo96) >> >> endobj 199 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [319.255 608.387 332.157 617.41] /A << /S /GoTo /D (cite.Val90) >> >> endobj 200 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [86.356 567.739 99.258 576.762] /A << /S /GoTo /D (cite.GS96) >> >> endobj 201 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [352.035 508.231 359.482 526.001] /A << /S /GoTo /D (equation.2.2) >> >> endobj 202 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [400.978 456.899 408.425 465.922] /A << /S /GoTo /D (cite.FG03) >> >> endobj 203 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [142.763 343.84 155.665 352.863] /A << /S /GoTo /D (cite.Goo96) >> >> endobj 204 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [406.246 127.99 413.693 137.013] /A << /S /GoTo /D (cite.mpi) >> >> endobj 209 0 obj << /D [207 0 R /XYZ 71 721 null] >> endobj 58 0 obj << /D [207 0 R /XYZ 72 720 null] >> endobj 62 0 obj << /D [207 0 R /XYZ 72 641.198 null] >> endobj 66 0 obj << /D [207 0 R /XYZ 72 301.836 null] >> endobj 70 0 obj << /D [207 0 R /XYZ 72 163.529 null] >> endobj 206 0 obj << /Font << /F47 95 0 R /F8 93 0 R /F11 106 0 R /F18 122 0 R /F49 119 0 R /F21 120 0 R /F24 91 0 R >> /ProcSet [ /PDF /Text ] >> endobj 226 0 obj << /Length 2215 /Filter /FlateDecode >> stream xÚX[Û¶~ß_¡GÝ/yKN›`‹½¬s€mh›¶ èâŠT6Û_ß¹éb¯³=0`QÃ!9~óÍP¡wôBïÓ]xõŒà?ô"¯Ìƒ(-½,L‚*ª¼]s÷×]„uO CDðö¾I¼»»ßá7vÅQTe1Žø¿'J¯'Z'!–dÞzaÙ‡ÍÝÛ¥…AV‘·9xEì¥e¤aámöÞ£ÿч^¯ÖI•ûé»Õ:Ë+ÿa§jµ5µq«¸ôŸ¹Óië,7Õ8à¼Zƒ‚î]ßè=Ë -Züø3ŒÒo«È‡!‘O:™î»m­V°æoýfµN£Ø:™Z¦ÞáÒ'ÕM{NÒÕb×ÐàÿV àŽî°ú²ù¼±Ž’ K+Þ!,FZ;mm×Ûö˜–þ'ò?š•—¾…}órðb,?»­ÕýW\L‹Z׊ú !²F7]ÿÌmÕ³ñÆéçÊD†YX냃g$ŠÒóD³×õ¥t\fWÖéÞÞÚÎÙ›ã ' ‰Ôš ëEyP%9`# ò(â1ÿ]• žFRfþ`5xû1Í– IÊ (J˜‚X·÷\çXõNYY4jªvkº8 Š2¹œÎ)@ûÓfð8/ÁâÔK!,ʪ@ ?~ ½=tÂæƒ¤*½'Rm¼$Hak¡W{„ô«e‹8HŠˆæÊa ¯~{qDQ6šxè;g9‚<õŸ~`Ƀƒ]ª~Ïâ 9³9×ÊiîÿÅl{€»"4”9ÑÃæ:u<Áu k¥eΙñ‚5 âa,&¾Õ šÐEöFƒ ÚlôÀ",9‚@Òµ¢AÞ 4ÊbSKJ¥ž1°‘¸k.QŽæÕ`.B€LGú˜ã¤0fG,ró¤dö8ËYr©Ö ¿ÄÜÉÕ\᳨¢»0.ñÓÍV%à÷ŸY`ÚÛ9kD0øœNÛÏ`xÃmÓ@&Aû“ìçBå‚FÑPX*°çÞ44> ýÿir:H®ŠRòPŸÕ?}à×N&Ÿ¨&Š&rcÍvBíK*Ûum t¤Å=_boÍŠ|ÇRÈ8ºoµû÷ä‹UO?´\Üh.ŒÇZGj h™*<æ\”““ À‘u*ÈœiáÀ°~¾¨µàF Áf)° E9-#SrFþ{Ë#?€q‡ÚÑ"¥Tn8óD°¦~%bf!Á·`êLE¶Æ%@òŽù[©äÚíæœLfnŒ”AŠ9ÊØˆQŠR\€\ò ]ÊE Y—‹e dn!¯Ó0÷“ º¢e“'ºú)¥änê¯=p)$¬¡ ƪ],ÕÈëåhÈ…ª‘ynÞËh®«ê™‡dòͬˆ¾!õöûç´G~Lt/Ó §óÂ- QDÅBqz㘦ñœ–AcßÉ\›‡HöeÖR-…0¼;ñ2¥#m/KŠWvJ®]WÊõ$„Ç^9e‰ßÐO$éøÉÖ…!ݪËÈ©V(…›AÇ忉ʦbÔ¬U”«@8_̱‡Š0Ѳ]=ßAÑØQ>]B? C–nM]›ÛUÈ%É0r+ÎÒWØÑuí~$XQo˜$ðJÀùÆ _cÌ€ðµ°I¢ØÏ®ƒ†rÀi.¢±Aùô‘`ÁÌÆHás´L{û^-†Ç09_Æ/!Y9+Ü*,´Ó —¾B¡¨rTÃ;}„Ñ{pK…1\€Hª°®ÅÆ´ÚpGlAfhÁùC fîÇ·ãå•›ž=7Õ‰éb”…{§oD …ž±—¥™JqhOùº—e‹Œ\Ôò Ž VÉŠ’À_áã1[5U¹è®¸œ®')¤dUÛŽ•Ζ&¾BÁ5cLºÞcÂB âŸÿNü•Ÿ_W㘋øÒXÛð“IäÜ…Ý|Ì!S >‘j^а0Õ%òjyø^ZQ6§üÄEANb±„3øQŽG f—pmÅ´Ž2Æå¬)â£v,&¸ô°‰,LfçƒF·>,ìœBÈa‰ö*Bó\¾ÃË{&EP4ù™@s¦RÎøÍù'ëà版…Â’øK–Škdés¶ã¶\¬5\‰àÂ?¹ËÇ;!H–·˜|̨3>â$ â"ñ 3óœ7y£ûis÷i¶ý9 endstream endobj 225 0 obj << /Type /Page /Contents 226 0 R /Resources 224 0 R /MediaBox [0 0 612 792] /Parent 198 0 R /Annots [ 205 0 R 216 0 R 221 0 R 222 0 R 223 0 R ] >> endobj 217 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/altix-fixed-psize.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 229 0 R /BBox [0 0 2404 2394] /Resources << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 230 0 R >>>> /Length 55 /Filter /FlateDecode >> stream xÚ+TT(TÐH-JN-()MÌQ(Ê ™˜(¡‘±%„‘œ« ï™k¨à’Ô°[ endstream endobj 229 0 obj << /CreationDate (D:20070215210654-08'00') /ModDate (D:20070215210654-08'00') /Producer (Mac OS X 10.4.8 Quartz PDFContext) >> endobj 230 0 obj << /Length 231 0 R /Type /XObject /Subtype /Image /Width 2404 /Height 2394 /ColorSpace 232 0 R /Interpolate true /SMask 233 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÝÑu«:Û.Я·àRƒ[H n!·çÎ-¸·àÜBZp 9ÛãÏÈXƒ/bΫ½³, !„ÿûPÉŒ¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+Øæ¥ß²Eº\.Çãñããc·ÛýS°ÿ|}}ÏçE¶º ;üÄse…Í_ú-U˜¯¯¯çiÒÑ—“e·8v ø‰êŒe…Í_úÕ/ÆívÛï÷¥.'Ën Õ]@œªXïPVë=íŽ;/;ƒaûIÕ¡n‹ìšÛíVðr²ìÖ]1ðT5îòñˆÔÏÏϯ¯¯ëõjÏld(+€õžve…@cý¤j‰P·ÓwÍ÷÷wÁÓVÙ­ºb(.橪ÔEåáp¸\.ö2@Û#@Y!¬÷´++Ö{˜ë‹ÂÖíô]óññQð´Uvk8PWP\ÌSUÙKËÝnw>Ÿíký6ÐjO"+€õžve…Àzs}Qغøççó¹ç±fé_o·Ûï/_¯×¯¯¯Ãáý”²[Ã!€º‚➪æ¸ÀLß(};]¿ ´×“È `½§]Y!°ÞÃ\_¶n'þù~¿ÏnaÜ;Ên ‡ê Š {ªšï2ÓYX¿ ´×“È `½§]Y!°ÞÃ\_¶n§üy× ›þ.©®ìÖp ® ¸È§ªY¯4½ÁP¿ 4Ö“È `k#gpГ(¤0ÓŸŸN§ç¿ýüü÷EÊn ‡ê Š‹|ªq€?‘z>ŸÓWØívý“ÀnÝÑo-õ$²B0òô$hE|~~\zPvk8PWüÄí¿^¯]“Àûý^ÐoÍô$²B0òô$hEdgƒl ‡ê ‚ŸøbàÙµ“çóYÐomô$²B0òô$h?ZKFËQWà ŠP¶ëõšÝÚn·Ó49 žDVFþ€ž `¦þøø²5¨+~â‹|€}}e7x½^5ý6Ð@O"+#@O‚0ÓGË Ñª+ØÔAÔjV˜ìv»ç ~}}iúm žDVFþÅÝï÷óùüùùùÏû;öû}úaú§ô ÷öýý}:‡ÃóÖÒÓ?¥_˜õ‹ÇôÑÿ\?>=]ßn·à»ãQÏ»ãñÔ ôÃôOé‹T¸%8ÕUú *3ý÷Üû1à®Lå¹\.Ùc$æÝcÄ‚U½x§š~òÜze…ÛìíWôíôqNÃëªrwš¾Z™¤ïþøÄô)Ïå|ôéRUÌÔ†D-uäÆ7Nôh?Ïç÷ÔYê+l*+L rú÷]°Ukf €5ö$²BØÚÈôG ùÃté—.Ò¯½œS:ŸÏÙ©¡g‡Ã¡lÒ”®Ó6ŽÒUmºàÖ–Rõ¦R ¬À¿ûïÎ=iwejÅÚ»²ø12¼6Ö8žŸòé VueÿEj½___~uâž}ëÏË6¼úÍ8ZñÛ§n3쟞ó1ƒö\¦\ã©3Twú;?z÷Ç"ýyµATœQè²_¹ìy³ëQ™Y¿§Îúó«k¼´Ì¾µpà+ lcZŬg|óö@„¬⟯ƒ|Dÿ¦ËœóHC^oq¹\²¸é—®³¦×Ïív7I• çNÔTu©~þ†‘6òî®,²Ù•s#éX¨<Ô_Ew·`U×ôÖ¼Öß .YỈßùÌ—­7ì¹LXª®jž:Cu§¥vâñxœ2Ï_sdºàWŽpÞ|÷)š[Î Ó‘5n› ¶±:­bÖ3¾y{ Î BVñÏ×A>¢çO§ÓèÁÃ?³[é¢iÊýäc¦é—±‹¿°#]¿½ÈЮëÇM0þÎòM¬‡EveON)ÏV-+¬VÕÕä)=¡¬pu]Ä[ó™aÏeúÀéuUÿÔª;-¸÷ûýˆ…HõQ‹¹Ë~å²çÍw¬=·™ó–³ÂÛ\°Õl³žñÍÛuN²Bˆ¾òÙ?,rõ÷û²•Ëå2}42ú®æÑ³sÅs®çIJ5Œ´O'–dJj³Ô®¬pŒTꯢ»[°ª×x Ë #wz«Ï {.ÓN¬«EN¡ºÓâýÞ[“ü‹ ¢–=r—ýÊ¥ÆV¤ü»ÝnH»•ßæ‚m¬r«˜õŒoÞ¨s‚@üóuèºz*rïwÁI’¯Ì>Es</—Ë߻ӯ×kÿë6–Z´ÈB¤ù–ãR›weWó.uŒ´4Oþ3CVX§ªç6SP(+ ÛEü¼¿˜(ì¹La~æÝSg¨î´xm¼U†Ws/uä.ø•Ëž7 ~…tñ2bÞrVøî3H—jcõ[Ŭg|óöÀŠÆ¢ú{AWð#Þº¥9;­1"XL’¶–þüïÚË­½;½Ùõ Õ—ïµO%é*È'_MÔó$Øßjü[ªôÕÒOR]¥êªÌ‚ c¿ß§þ]õy»ÝÒ§÷Ì!Œ˜f\vWNvÁ]䜿‹˜rÑá\¦,XWÕN¡ºÓBÒáQõõz튆7œ¦¯ßõú¿—Ù_ý6¶VaB˜©'‘@ÃsGe?¢Â=ð5·–T™r{»Ý|ôVvžp·ÛU¸E¼l£ÍÞßnœ]Y°*²e¬ê"²mxàësÔOcYa“]DÌs™>pE§ÎV»Óqߨþ jñ#wÁqcÙ_=k܆Ȯô²SZÊ ¿¿¿SãLßúxz+]Ñg+! ÿB>Z£Í>Á¬š:ήŒsŒÈ ×5å’mÃé ^ê5¶kZí"žËô+:u®·;½þçt:}ýçãÿôçÑQ‹¹ Žç«À‰x½ÍÕe…E¼ÌË‚VM¶ ò@ä„® V4ò—>”}MÌân]7óÿ.PJWô£ŸgXó[gï´ïŸðŒ³+Í“¯ô .^]£ïZŸé5¶kZí"žËô+:u®¥;MC—ǧì¥RSUõQ‹¹ Ž‹È¶‡‰ ÔèãhƒYáår xm²VaB{‚Ð5@ع£²ÑXV8qÒ)àèhÈ7ÚívŸŸŸ§Ó©à ½e¿õˆWÄÙ•æÉWú¯®â³[²Â-tÏeúÀ:ƒw§÷û=U^>±`®<ˆŠpä.5nŒyÞü‘“*dxÀá°’Ô9Aèš øÜQÙh,+LW¦Žº¦ ûïé}\žOyìUñoýîãìJóä+ý‚‹WW¶ O¹Ù^V¸…."à¹L¸¢SgØcö~¿w=ºpÖ\yáÈ]jÜùŠ)Nßp*85ÚwŸxP¹m§U˜B d…°®+ß&g“&níµÔl?Ù7 —®Í#\˜¿»Á8»Ò<ùJ¿àâÕ'5hr×´ÚE<—é×xÔ‡:f'c&6àšƒ¨ Gî"ãÆÈWL²Â¬Ãá0ú¹è‹VM¶ ò@„¬ÖxåÛÞlÒô­5™þü÷r‰7«||¼5ó#+ xŒÈ ×5å"+œuk²BY¡¬p{í|>¨$_ÿçúî÷ûÄ2TDÅ9rë#_1É ëÓ>=ƒkE×&²B€hê‚üe…Ñfiæ®î'{øýò€Lj¬p]S.²ÂY·&+”Ê ãïµþ p·ÛÇËå2äáÌÓ¿Q…AT´QhÍqcä+¦d…­^›È ¢]D¨g>ò—nç"+]S‡Ñãºãñ¸H£-’6püÊ WÚE(€¬p ]DÀƒ]¸Æ£>Â^»Ýn]C‘ý~¹\©ÛYQ1›hqcä+&YáªÛ˜¬`bO"+€¶çŽ›M*¸µMd®×ë××Wº6÷@C&èÊÖäý~Ï.(XÅ®4O¾Ò/(+l{×È õÍg…#N¡öÚÇÇGÁì©øÞŸc¼‰Î:nŒ|Å$+\u“LìId…ÐðÜQÙh,+Üï÷ÏRÿ}+õÝï÷ßËó!¯,©¼+SÙ²¯)YÅ®4O¾Ò/¸xue'ʆO>zª$`u5–ŸÝ’n¡‹x.Ó.RW£OAöZ3Yá»ÝÆ(tÁÚÎVàè§×f—¤¥XÅ(b¥e«vXµ×*V1ÎÖØ»Ê `kWܲÂ_Ùçþ%·ÛM \d–oô~]|ž|àóèVQ]e…?¥g·d…[è"žËô‹ÔÕÄÞ#fV¸Æg¾ûÑmŒB¬íì;æ8£E^úÙÌZä+l¤U¬bœ¬±w•ÀÖ®¸e…¿ºÖòì÷ûéwf¦×|ïLº˜~…˜­jï+ìš4]òú»2Î12úí*kéîÚË ÎnÝn7YẈ€ç2}`ý¶=åÔd¯egã/—˸d× )CýAÔ²Gî‚ãÆRº*ðt:½»©ìâÜᙵ¬0þaÕX«XÅ8XãÜ£¬⟯ƒ|D{Yaòùù9ÇDÍïÕeå–s<§;;ÃöòqXEveWº1ðÞÝ »2Î1r8ʾÿ.Zw×^VØu¼;»Õ5µµñ¬°É."à¹LX¹mO?uFØk¥vVjêÙM _ÛXµà‘»à¸± ® |kmf×q4ü‹È WqXµÔ*V1ÎÖ8÷(+€øçë ÑdVØu÷éc¢fÄ#°Ò%íß;äi9___#®Ê».Š_ÞÛ?ý•]ýÖv"ìÊ8ÇH×J“fÁ×^VøÓq—øðÙ­ÔÈ»¶ +l²‹x.ÓNüvõOöZ×o­‘éZN8â9¨5Q ¹ Ž ê©À§ÎžãhxýË ×rX5Ó*V1ÎÖ8÷(+€øçë ÑdVøÓ» gøµmú´ççh-Ør>??‡O×tM² ybWÕÇ!Ô]W»#ž¥³ø®ŒÓª»f9Ò÷šò¨8Ý]“Yáõzíj½ý« Ó>íº‹^VØpð\¦œ>?SÿÔ¹ø^K­n\×÷;€É>Åtâ;« ¢–:rüÊeõ /ÛOOå/¸8wu—–ÛXó­bã|`s²Bˆ¾ò­f…?Ý«ùu8Ò•ãõzý;c“®ÅÒOÒecÏZž#½TøTÈTÔ.ÓOÒûñxì™dr{jÕí÷ûçOOÕ˜~’}AÛßb¨‡ewe¨Vݳ[{äŸÝ‘ªåñD£UtwMf…?•ú=”Ò>ú{K|úïóùÜó'²Â¶»ˆ€ç2}`©ù™š§Î{­§é¦ýø¨Š¿6õ~/0³Âjƒ¨¥ŽÜe¿rYéxéi?©À«î÷8êùï¾rQV°5ß*âó5Î=Ê  þù:ÈG4œö_Q®bh4GáÓ%óR=åM= îÊP­úåÙb6Ú•v ¥ô¬¯©Ùµ½kšé"žËôËžµÇ:#ìµY»¾‰Yá܃¨¥ŽÜÅ¿rA=ÏœçÝãHV°5ß*âó5Î=Ê  þù:ÈG´þ ¸¯{SYaª¥>zJP¸ì®l#_Ew×jVøÓû–œº¶`×4ÖE<—éuÙkÓ»¾äãã£+ ;ˆZêÈð•C:ÿžCW=ŠˆS¶ÅÛXó­"ø8XãÜ£¬⟯ƒ|DóYáÏ«·ÆŒ˜³ªÖr†?†kˆéïãX0(\pWFkÕã&IVÑÝ5œþL¸W<5ÔÇá#+ÜBð\¦\ã©3Î^Ýõý3tQ†QK¹A¾rA阸<3HྺKËÈm¬íV|œ¬qîQVñÏ×A>b YáϬ™~kwÚˆP'º\.Ãß\Ö3³ônÉ»®èŸkª¿+¶êTøwçjVÑݵŽÛqçó¹Ô7ÚÈ®Y{ð\¦\ã©3Ô^±¿~—N,ÃRƒ¨Ü _¹¬qÑÄãHV¼5Ü*"ó5Î=Ê  þù:ÈGl$+ü«éÁ}×äLº(^¶ ¥’ÿ¾¼þ-éO®×kÁÊ¿ßï©$ë0UÝ߉¾²RmWÆlÕiG¼5I²Šî®ù¬ð­—~ퟛÞe…[è"žËôk°íSg´®ï±¿C—6QqŽÜ _¹ Ç×Ieþ§¥ÿ}|ÞV¯M6Ø*Œó"cQY!Å™ªœ:XÅXTV@ñA¦j§NV1•P|©ZÀ©€UŒEe…dªpê`cQY!Å™ªœ:XÅXTV@ñA¦j§NV1•P|©ZÀ©€UŒEe…dªpê`cQY!Å™ªœ:XÅXTV@ñA¦j§NV1•P|©ZÀ©€UŒEe…dªpê`cQY!Å™ªœ:XÅXTV@ñA¦j§NV1•@ód…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IV,î|>g‡ûý^å0døº©oa¼Ã œ‘é‘.$³•™.F}鋤_;ŸÏgäö®4»âB·¤ <É €:‡Cvâb¾Ë·ç©’ _óûûûx<;¥_NÒvIÊ_WW’5þ-[»*T-ÏçìÀu Ç½XsçqiØv:ºò‘åL[WÎ&'ÿ¹­îŸk–-œ‘W=HH•Ù=˜Že}@Ùó¸¬ít:Õ,ÌóÕâÜ_óëëkÜ*ýa«%ic¾BVHÛW…ªà翬aôªËççgñ8œÏç)a6{÷ž±ö&»Þ•ðëz½6F^û ¡k'¦KQý@Áó¸¬çûû;;l˜õÑ—ÙKÅù>î~¿w=œjø­ÝE–XÆ)IKó²BÚ¾*T-h¢Æ¥=Ëʦ‡qEnÇ:ŸÏóòñj€ô£ÕUO~~~¾Œ}Û^¶Ý§9û-¢=> à[VÌ-]ͽ~-û¡3}VÏ 2ÞŸ™ÒÅ)ÉâS(qJ"+$þU¡jAeãÞzlû"3fßßßo+û ‰ö&ë[V8“ìêàtøèèJÇe…Àçó9EÍ÷‰÷û½ëÆà™>±àÔÍÄËØ8%Y| %NId…Ä¿*T-h¢lÖô2T˜1˧—-mc“‡]‹Ñþ1pÑåz‡yÍôÀÙ›'Ëî>€ö†Ü²B`Öé—Êé~ÔŸØó^›T’Óét½^×è¥ÿHÿ›~ØSÈÑ/ÔˆS’S(qJ"+$þU¡jAe³^…ŸŸŸAÔ?Ã×ÇP*<Óìp8 y4è¸Yó¸ßïÓ¯Ï翃½_×ÿ¤o‘¾ËðPŒ8T×;y˜öàb§_k`x¹…¸ëAÞ³sÈ-+æ“M¯ >}ô1K“¤m¾|ÉÈ•®ëÐ!A[ú…®¿‘¥Æ)I)”8%‘ÿªPµ ‰²M=1Üð7÷ýºÝniLÕÆ(aÿøöñÄwGkiüœ¾ÚË‘óvŽß®›ç›d…«¾hoÈ-+j^wïv»6Å¿c×4Ëívòçé׺î`_oIæ¾¶1ßT ÐLƒM”–\¯×™­ðýýM!KîJ=ÿáv­ˆÜÎñ›}Äëçf}ì†!âܲmÛÒB€Y!PWöúúz½.8°)û»–ò½õ»^òÖ-âqJRaøÚÆ |S5@3 4QZÒõôÑ÷Y½t¿ßÿYÙôÖŸ÷¼£0•¼ìð,›nçøÍ.½ügÖ·®"Î-{?À²wBDrË €9d~||,;°)û³O¶)µ$ð­'äÄ)I…ákƒðMÕÍ4HÐDiF×JÝÕöwHü»ÆpzñfM:¦$›ë=~»î¸{ükvaZ© Ö±‚ì-–È €j²éUÁé—•R³Ù9Š·Ö§$†¯m Â7U4Ó A¥í‘êáp˜éã뛆ÿ~×Kç^•†‚¿ÙÊFŽßìÓbÓ‡ü«!b|Ù¥…ÞZ +x\Tø +ÌÞï=zŠ)ýaÙW .R’:Ã×6ᛪši ‰ÒŒìj£Ëå¡lÙ³æ³O§Óv²Âìw¿Cßì0{Öׯë*ëìª0ä–d_°Rü™NË^Ag§qÒ/Xc§Ói]%Yp'Þn·T'|Ò‡Tø_¹øÓÜDP:UýÏ’‡ô¿é‡éŸ*é?ÿ=Þír¹|~~þ3”Š”@jÒkyÒT*g*íñxü§%ÿ~—T½Óƒû™U¿[žl«H?|·U¤>$õ$é[dÛØ¬LÀŠûp«Óï=öéóyœ;ÒÓ?-Ò¡é“—=;G([ö1þ Œ:[Ýë@%›¦MLè²³/·§$ë¾¾;á°Á¬°çP㦦߽¼kEÌôÂD˜ꉄŽÇãårù»„äz½ö¿aí­>vzß­ðoøÛ*JEuã­b«nsOGLY%gÒCŸ\\6'ªÿ~íd;´€Ð^dø]ç˜ÊoýòLÕ6+üé}0òð!wO/WäZ2»Œ´þ£o"Ÿ²e…@Aq®Âêg…WÞ[!§$ _÷ûýétúû­¿¿¿SSì™~wfrƒYaÏ ðn·Kí!Uøï¡þ#ýïñxìImŠÔùð%ý0æSï~ºŸ–™jµ¿·Ì›ï>žnbÃXø·ÂµÁbú“´µ¿ ~ÈÖÞmc«®Ø‰‡Û¬Ó=§ýݳ¿Î£7KeîéÐé7ôÉÕºâß 8ß{ñÚ~Wh=K‡/Bœ©º"g…?Ýwe ïjº)—:$ãÜÑ ö”-+ z^±Ôíâ³T²—Ãs$téƒÖR’E†¯©½õO†÷Ì¢W[ŠµÆ¿í¹?üe½õÌÌ¿•8Ipú s¿ß»j-¸´°ë)‚—tÍ$ŸI›Ò0bþ­VÑß¼Gl­+y«5P±·‚§ïé«TRÝ>‡Âõû }òzÞ³–ʹÈ}nÙ;î€Å~—õî+‡¿Ü0þðrŽÍ¦C¦ëæ„!ÅîzúhÙÎáùºiÁ{ž²e…À¬×Ý‹¬Gû‘V/Iåák*ÏÀ%3]“ðo=glSYa×ôi:º>Hªç¥Z¥: ïXºnt^’²¦?_®«z~£)*fáG´Šþ%Nïn­+þÞÆš©Ø"‡[ÁÓwvnÜãÒ™ñ÷ÛUî4ôÉóéìðËU½ÅÍñ`ù–†ß«ž?éº÷`JÓm)+ìïjúÏh=÷u”=Ÿ1¯,6KVÔoLÌ­bNVdgº&Nse/“Çe…‹”¤æðõÝ'>uÝ«<¼qn*+ìš;}ë%]óEÓ: ‰ºæyêT6¹1œÝGëvtÃ[ø­¢ÿÕo¥¶6°µT±E·‚§ïìí¦ûé¬Qÿ}vúäùô,-üûÿyÄë|²ƒº½¬ðg=YaöŽ‘—‡Cö`²h.Úq¾Ívݡџúu=}´øUdö~H×;Y!P\¨ë¯Y*q.ÏcNÌZÏoÍŽþÊοÕylãºþ¶kâtÄ\eWD;e¡Ó¸åÙçŒ-òÔ©çåcãr‡)ï´ý‡a ?®UtÍLÜÚÀ6ÖXÅN?Ü ž€² 6y¶ähúä¹ (ñãØ|D‡çóyÜhdÄXe©›îV=€×9¼<¬²Ñ”ûÚË ºooèºí°«kšø.Èá_p]‡À¬§lY!PÊóµÞ‚Ïu‘†­½EŠ‘½ÏyøôÎv²Âì¬éèU¥ÙiÏsË÷~Ù/5Z¶Ž^J“l2w=®bW]ø¬ìKÜF·ŠÑm¬½Š~¸,LSúä úŸDÚ/t‡Ãá±ðp¦aÏROÌnø ËÞÜ8pLXvù[“Yáý~ÏžŒ²½Íô÷íN?Q®ké.À¬§lY!PÊóäÏáphr²BV¸ºØõÀë¶“f§wF'Ùdà-÷~ñ…ãd—žìoÞ„?®bW]ø¬ì\qÙ­ icíUìôÃmî¬p]ë õÉu y‡éÀè0mjô’Ã&ÙVñ¦¹²Ô0Þz}áo%†e«e‘ù½øó„JÿÓñ®Ãq]wÃYáO÷Ë@X×½s¿=ð9Ä\ðÈ ’³zž”[ðeñ²Â°µ·T1²w8œØHV8%N-¾Í S¦5s¶F?§®ëK ™í,Õ0ÖRøÈ[S±s¦ëÁ’©NŸTè?õÉSÜï÷óùœ šG¯1ØÞd…dãò!ÏjîïXÆ…ømg…?7l<*<ûOoíˆqžïÒ‘Û$+fõ|Ÿ­¬pîÍÊ §LÈ ÿ‘·™x®öÆæ¥Ë¾–nÊ—ñW«.|䭩ع Óµ~í÷•XçóyÖ‡ÝM¤O^Ö#4<ErÃÊ‹7d…ûtúC/»ÄèLšÏ S…g—afÕyès¸ìËRBÍ˜É €ùÑF>Á7.+œµS^ü$+,8'³Íyé‚KT&^Ž•jk)|ä­©Ø …RÉ»Ýîóóót:E[l¨Oåv»].—¯¯¯Ãá°ßïçˆ ³5z­±¬ðYöy#–•eÀˆ5qÍg…?¯îÙø{óFµ§C›Êø‘[ºò’†­½‹!+\Qsjl^zø}õS¡U>òÖTl…ÂtÝ%Ò?eýˆ'>¶¥ÁƬ0ç7=žû÷/=+›Ë Ÿe3¾¯ÌfŽiã5«kE—ÙwDN_•ÙÆ+@¨q¦¬hòÊkÖJv2gâ6³Ezy«sœ’¬b'Ê 9jÌKW»›©bW]øÈ[S±uŽÖìËj‡ûüü\*4Ô'¯ËõzízEæÀ§,–}ƒ›¬ð]wŒXÎÖµ©wûŠd…Ijù=-­òšnSÙ?²B`KW^õ³Â‰s‰ã §$«Ø‰²ÂEŽóÒ?²BY¡Š]ôhM§Å‰«8ÓI°~b¨O^£ûýþõõ5nýTöë¼Äm YáË$wºôÕªk]Ya:.ºžÜ›šý–¯XBÍ˜É €&¯¼êg…‹¼B(ÔËŒâïDYá"GyéY¡¬PÅ8Zûœ!F<«0àHFŸ\ÁívëJFzÖ fÕ8âÉ– î‘È{¶ÂcŸÓGT«®ue…?€}TZµ7¼b5c&+š¼òšu ’oœ#¡{y«mœ’¬b'Ê 9jÌKÿDš>Ó0¯Æ˜Y¡Š»ZÎçóáp¨¶n(`¿¡O®£ë1•©ùuýÉív{w)b´ÆvÏf·s¸\.uªk]Yaÿ¢Îžƒ¢ù+V€P3f²B É+¯úYáÄå§Ó©TV¸HIâïÄì{Þ.+¬¿e…qJ"ÒR±5·V­Z®×k:µ‡wW½•ì7ôÉÕdó‘þu‚ï.EŒÖØÂîÙþ÷åôÖ3c7’fÌ.x¡a*àGVÌìy¶­þË}êLV̱ònÜ Á8%‰¿§<[u#Ya¨GÚ66/}ôÖ"=䈪Xuá#oMÅÆ9Zï÷ûotXü1ƒ£é“×®k-[ÏŸd[`…Ç6Ÿþ¯¢áOÔÜBVصZv©kçòTëÒB‘³zžÔZ$`ª0Y‘½ì}ëFâ!µ7dÒ8NIâïÄËå2ú©G[Î '® ÉVûç¥ç˜ó¯V«.|䭩ؘGëãÜÚÿļ:/.Ô'¯]öýß´kÖÜ=CÛYáum ïšÏ Ó!½+¦k5w…Çí>'ø‹¼“`q²B`VÏ“ZÕžÓU²¢ø]ßÙ«æu•$øNÌ®—¸s#Yá”**¾ÍÆæ¥³¹ÃZí»êÂGÞšŠy´þºßï]Ï-œxON…Ó–>9ˆRO'ž;Îh;+Ì®Ö,òîÑl7>üí{Íg…]5ߵذÂÚçû%d…À6É €Y=O©-2çYg²"{·ÿð‡ýãûû{ô¼Pœ’߉S–ðl$+ÌÞx?qïg«}È-÷ÍKgë¶ÂœØ| c-…¼5óhÒ‰ÕÙMúäŒø¦] õ¬o-l8+ÌŽlK=1£+ó¸>®í¬0ûôÝn÷¸HÉþk© ·ÇóýuîýXÅEV4yñ5÷@%{gþ臒eç&­qJy'ŽxX©’¬èo»æÓFGÏ]Õ>d­±y鮺­ð¸­ùÆ* yk*6æÑúìC;ëIŸ¼vÙ éeÚÛµßgíÎ »«RÛÏ>Žc`°ÛpVØõ²Î¿7éeo]˜û!Ï¡nmX¬˜U¨‡ºÌ=PÉÎÿ èÐ?²èxÃsœ’DÞ‰Ù 4þC¢êÿmvÊ«lô#tôàyàŽ.>Ì›ÞUÙƒÙ|ö÷é£u-Ü®‹/{~5¾•M^U¨dgðF¬ÉÞ=þÖÛ:â”$æNìz%J’¬ëo»¦¼F¼&©«Ú+¼Äg‘atÝ™KŸuê yk*öÙèê=ÛívÓ!Ù}Tíµ’úä cŒ´7ç¬fïþÀ® åQàâ#m5+ÌŽ‹¿ÙhlÈøyJuì*ËîÁ®U±]GY×ïϱÄ>Ô}­Ñæîd…ÀÜË ¼ê®K× Þ[3N]¯óxk(NIîÄ®[Êßj™›z×aפÍ[ ºª}øÌO“ïÆšé±r¿À" c…¼5û죰Ç=ÒüwÆ~JMfŸÛYó9±úä:cŒÔÆ &†ÙfóîÀ¬k×ÏñàÄV³Â)/Ñß¾»þwJuì* îÁ®–ß“œ¦þ9»›æØSÏ ~µ?¢‘s{žÖXê•…u*gð¦ÏÝ,ÉÜ;ñ­ì²ë{½ûJ—Me…Ó_ÓSíÃw_“YaOÝŽ['r>Ÿÿΰ-Õ0â>òÖTì?ºžË:"Çùçþ‰aWoVóýVúäšcŒ4j¸2+5³lˆ3bHœ6•]°öwƒEnèêZø޳mÿ÷*~\×Aú²£˜R]»ÊREêêj²OØG•}"÷óbÌe¯t¢M¶È €‚²w“Îô¾‰“=3x/纞VðUƒ‹”¤Âð5]×™:èù^ïÞ¨¼©¬ð§{jHÕõT{µµœKuCôÔÏð8#ýNÚÎó}øsWŪ yk*ö¯žÙæwOIÙÓÇð˜¯kLý×øê“ëO‘=šÊ[#ØÔ>{öÔ¸1Õ˸09#Âë´åôWýKW}¶ýé¸.Uן• ˆ_æP3sSFԳòÖTì_ý'¦ì¹ì1Q?ü$’~ÿ±©&ÕÓOÒi¥¿7[äEÌú䥦ÈRE¥šLCÙçÖò¨çGƒy™è^5$.üÞ¤VÊóÜDE}t©·¸ÁYç¹$™ïª¤ëÖ‚þœkbÍ”ê*‹©k9íð÷žwÈJ=£&Î­È €E¦³æ¸ƒ7ÔHfà”ËÀyž²•¿TIÂîåq¯Û`VسRuœw«½á¬°ì¡Z?Zuá#oMÅþêšu÷ËÎQŸÃç½ËÒ'¯tЬÔCkSÃû_u³VàÜûw\x7Ú¸hrbÍ”ê*§©ë~Å—OØË¹}ñ9Íô²B`Ëd…@Ù§]¿05’~Ë÷1VÌ’ÄÜË£¿×³ÂŸÞ×ǼkÄzж³ÂŸëÈ"§B«.|ä­©Ø_ãÎesŸD–}³•>ySd»Ý®Ô ×.—KÏ:²âMýåúÙà“‡Ùen3=€tô'.u“IÙ"¥¦Rjv×+&§îÙ0÷ÝW´DVT½)´øµX´‘Ìô®T<§$Ñöò”ïµÍ¬pÙæÔ|Vøóê-u3­.U«.|䭩؇q¹Ø?)¬,þ\n}rñÊ<ŸÏ3­çý]…Zv@•¶ÖÿJÄéÉfê‚–9òäa× µY_‹Ð•sõ¼=p©Ûº ›=‹Ç­Âî¹gfÊ{³çÖe_ËÐÒd‹¬èò<ñ²ÛíV1°™XªÑ8Åg 㔤ì^N éz½Ž˜þ8_·Ù¬pÁæ´…¬ðç¿I¶éKÉÒ†¯[)X«.|䭩؇Tþw{ûç\.—®·h½¹–Z¶ÞS|Ã}rÚ¹ýïvwÎC¤ñLÿû¦GD„©Þmä‘'»nºXdÞµç†ÉRwVLï*G©+mëé£ÿ4ﮯ3宿çmz)°q²B Žìë3Þ} Í"›é{wŽw¾Ù¤8%)¸—å4|ú7ýÚô9Þg…æôÖË’¾¾¾&.¦ØHVø·zßëKÇìˆGr¯Šõ>xS±?ﯢê©ÌÓé4"4LRvôR¶ÓÐ'—•F ©|||LY¶™¶Pm4õH9G/Lß4•vô)òäa¶N*Y!°àÀcà“Кq¹\Òe{úÖÿÌô¦ÿM?Lÿ4ñ½k,IqéßÏÏÏfööûýáp¨9_·×ëõÑœþ™ˆKÿûhN1çÕ×â1SýhÒÏ!QúajØa+yÕ…×*"{<+ò¹«œÅÒÏSý üúiSN,UZ6úøÏãÌäqÜú䥎»Ôê~›JW€ø·Á,8äømØÇ㱫´Ÿ§_~¼°Ù®²1Ù#Bƒ6NVT“½»ÕÔd†zçÀ"d…@5÷ûÝÒB‘]T¸Šeã³’5eßaai!³Ê.*¬ðæJ€ød…@MÙ¥…»ÝNÍ0Ÿ^äjQ!À/Y!PYö­…ÞÀLN§“ëP€.²B ²ìÒÂäûû[åPVºØÌ^„ZTð +ê;ŸÏφý~¯f(ëãããù 4]–ª€Y!çbÍ`((ûŒtAªf~É €Et=ær¹¨¦K˜^ð’¬XJöåòÉívS9L‘.-³—œéRTåü%+t8žG»ÝÎ+æ-]Tî÷ûçëÍtªrþ!+–½|ËÒ5¸€qWšÙ 0q¥ ðLV,«ë±0âBÞÕzá@–¬XÜù|îŠ UÃu…éÂSådÉ `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’À6É `›d…°M²BØ&Y!l“¬¶IVÛ$+€m’6ær¹|}}}||ìv»¿» ýoúaú§ô ,L¨jBV8‡Ê5ðýý}<‡ï‘ôËéOš/L¨jˆFV8‡¯Í]___ãöKúÆ ªZ’w»ÝªÕÀý~^Ãø–ôçi#&Tµ„%+,.›RÍñA÷û}¿ßOß;i#Ós±8… U-‘É ºßŸÕj`âÒ¹–Ñ5S˜PÕ™¬°”ËåòüšÂùj çe|©§Óéz½þ.‹Kÿ‘þ7ý°§„é_(L¨jNV8Î#fJ¾¾¾ºÖÎWßßߣ³­ô ]›6»ê„ª€ød…uj¬lºÒÉÛí6äÏÓ¯eÿ=ûN½Ãá0nkéG¿Ý/TaBU ÀZÈ ëTi©gŸ´y>ŸÇm-ýáóÖN§Óê ªZÖBVX§JKmüãããyã÷û}ÜÖÒ>o-}Äê ªZÖBVX§JçÛøÄWée_ó·ºÂ„ª€µÖ©Ò"[þþþ.øV¾‡ì»ùÒ­¨0¡ª`Ed…uª´È–¯×ëó–¿¾¾¦l3û¦¿ôA+*L¨jXYa*-²ål€51;Nã¶§0¡ª`Ed…uª´È–çXì6zQ^œÂ„ª€‘Ö©Ò"[þøø¨Š¥ZQaBU ÀŠÈ ëTi‘-Ë ãW ÀŠÈ ëTi‘-gC±ïïï)Û¼ßï³ÂE ªZVDVX§J#oyÜfã&Tµ¬ˆ¬°N•FÞ²¬°ò~BVX§J#oYVXy?!+¬S¥‘·,+¬¼‚Ö©ÒÈ[–VÞõý?`d…²BY¡¬PVÛ$+”Ê ÛË K‰zHh›¬PV(+´®0»®PVÍ“Ê e…²Âž¬ð@[d…É ?>>Šoù~¿?o3}Њ ªZd…@e²Â-g…·ÛmÊ6¯×ëó6Gg…‹&TµÄÏ €¶È C©œ^¯×)Û,›.R˜PÕ¹‹@“d…¡ÌW___uB±ôA+*L¨j‰ÜEÈ  I²ÂP*g…çóyÊ6O§SÁ¬p‘„ª–È]„¬š$+ e¾˜c±ÛèEyq ªZ"w²Bh’¬0”ùjàv»=oùóósÊ6³/ûK´¢Â„ª–È]„¬š$+ eÖxÞò~¿Ÿ²ÁÝn7º´q ªZÂv²Bh’¬0”Yk »Þí~¿ÛÚ÷÷÷óÖÒG¬®0¡ª%l!+€&É C™µ²ïÑ;ŸÏã¶–þpÊ›þâ&Tµ„í"d…Ð$Ya(³Ö@öÝ|‡ÃaÜÖÒNy+_œÂ„ª–°]„¬š$+ eîȾJïûûûÝídŸ´™6¾Ò„ª–˜]„¬š$+ eî8ÏÛÿüü|w;éOž·“6¾Ò„ª–˜]„¬š$+ eîÈ.|K®×ëð¤_ÎnäÝ…xq ªZbv²Bh’¬0” 5]û6ñí~ãVá…*L¨j ØEÈ  I²ÂP*Ô@׺är¹ôÿíétêúÛq«çâ&Tµì"d…Ð$Ya(ujàëë««¶÷ûýétúûìÍûýžþ7ýp·ÛuýUÚ`… U-ѺY!4IVXSœ*Úï÷¥>ýããcbµÄ)L¨j ÕEÈ  I²ÂšâTÑý~/’‹¥¤MM¬–8… U-¡ºY!4IVXS¨*šž‹LÄâ&TµÄé"d…Ð$YaM«¨ç%}ýæx_œÂ„ª–]„¬š$+¬)f}~~ÿ¸ôËéOfª¢8… U-‹w²Bh’¬_—Ëåëëëããc·ÛýÝéÓÓ?¥_Ø`aBUËR]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²Bàe!+€&É —]„¬š$+^v²Bh’¬xÙEÈ  I²ÂÿÏÞ5®d&„IaR †›Â¤pSà‘GR˜&b˜H´®šÝ)lv«%uŸþ¾ÒÃ^…lÙYÿz¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„Vy^Ô—Wóÿ–W:¡áˆÐ !Ï‹úCÑ £^ÝšZ! D+¡BžõÿG½õ®W±šVcÑ pDh…çEý¥ë­¤½{×Ñ `8Z!Ž­R½®‹ëÞ]+…0"­G„VÙ^Úe¯ü»B! J+¡BÂWwAé+ü–PãÒ pDh…óõ¾’¯ …04­G„Vi<°DÕ/üâÇï~Ú20­G„Vi<ÿ¿e5®åV(Ô ` Z!Ž­Òxþb¹]Wþs%j…0­G„Vi<_³Üè€á²²A`Z!Ž­2i• ¡ƧáˆÐ !™V¹P(€Ñi…@8"´Bȧí?FºþñC [Z!Ž­R:,ê‰Ð-­G„VYõ“ õD8…V„#B+„Ä®æÂ[¡ž{Ð pDh…[ŽP¨'@­G„VéM õDøD+¡ „B=€ i…@8"´B˜âõ®ê‰ÌG+¡Bþ{U ›êꉤ¤áˆÐ !ù+ýF\ùÏ[¹0$ê‰tE+¡Bæ—ùí¸þ•ê\¨'ê‰ôC+¡BÚ×øêt«– õD=€ch…@8"´BÈù_ …·Za?¹POÔØN+¡BÂWw WZá@¹POÔX§áˆÐ !ÛK» ®·ÂL¹POÔf¦áˆÐ !Õëº,†­pª\¨'ê‰Yi…@8"´BÈó¢.…%­ðÖŸÑõD€Ah…@8"´BÈó¢.…ŸV.\-ì;Š¡ž¨'tE+¡Bžuq(\¾´Â¥8nÜIÅPOÔ£áˆÐ !Ï‹º8.×ZáRü!Ľ)†z¢žЄV„#B+„”ÖCá§îºaÏw=QOøH+¡B>%½¯ð[=çºÇ=QOæ¡áˆÐ !™ÂÒWþÝ¡saÝÃ…ž¨'9h…@8"´BȤ¼ñݵBî\X÷¨¢'ê‰@ÿ´B Z!¤qWÝ»wÉsa݃ž¨'çÒ pDh…Æ]]¯b5­°ùBOÔ€]i…@8"´BH㮨W·¦VxðADOÔ€-´B Z!¤qWÑÛoeŽ<Ðè‰z"°B+¡ä£ê‰z"°h…@ÁˆÐ æ¤ê‰z"¤§áˆÐ ¸E1ÔõDšV„#B+` ÅPOÔ [Z!Ž­€½)†z¢ž§Ð pDh…ô@1ÔõDhN+¡0 ÅPOÔà.Z!Ž­€LC=QO€¿´B Z!³Q õD=€Ih…@8"´BøJ1ÔõDÐ pDh…PG1ÔõD:§áˆÐ `?Š¡ž¨'p"­G„VçR õD=€h…@8"´BèŸb¨'ê‰TÐ pDh…ƒb¨'ê‰|¢áˆÐ aN——þßÅ£óP õD=1ç‰ý=Gd¿•€ß/h…@4"´B˜ÐÇP(Ÿ(†z¢ž8Þ‰ý=xÝšŽ#Œú~A+¢¡Âl¾†B¹¸—b¨'ê‰}ØßóxV¬æÀÀï´B Z!LåV(” æC=QO<îÄþž‡ëÞu<þ0öû­ˆF„VóX…r!p<ÅPOÔ›Û?w­à€áß,h…@4"´B˜DI(” )†z¢žXzz_vgË¿+@†w Z!­fP åB`DŠ¡ž¨'þ÷ ¿à¾~K(€´B Z!¤wo(” ”C=q’žîjÉ×…BHC+¡Bnu¡P.æ¤ꉃöći‰ª_øÅßý´e`8Z!Ž­Û åB€«C=±Ãžøé(,«¹pý+·B¡VƒÒ pDh…ÕöP(ÔQ õă{â×y¹]Wþs%j…0(­G„V)µ …r!ÀNC=±ùR˜ KºäÕƒâe #Ò pDh…OÛP(œE1ÔOù£P™h…@8"´BHfP(tK1Ôø ¢ŒK+¡B&û…BÝ`\Š¡žØÉßUާáˆÐ !ÃB¡’b¨'Ê‹’V„#B+„4úi…b"@JŠ¡ž(/Àp´B Z!¤1P+T²R õDyº¢áˆÐ !4­PLÈM1ÔåE8ŒV„#B+„LæÉ…J"@nŠ¡ž(/@Z!Ž­’Ñ ÅD€I(†z¢¼!­G„VÉw­àÈB¶Y¡шР\ÅDÀy”¼h‘‡¼¾Wöð–×1‚„ƒB+¢¡à ˜’(&€“+yÑ"/Žz‰¯àá*ü–GrN ­ˆF„V€ë]b¢’N·åůòEw¿äëB!¤Z!­—¶”D1œƒ¡0ZÆÊdŸcKTýÂ/~üî§-£Ó pDh…¸%&*‰à¬ yÑ2V^üôœYVsáúWn…B­rÐ pDh…¸f¥$Љ€S5yÑ2V^üúXnÀ•ÿ\ …Z!ä áˆÐ p…JLT'oò¢e¬¼xõ˜.7:`ÉöomV„#B+ÀÅ(%QLœÑ!/Î"…BHL+¡àÒ“˜¨$Îñ…B¡RÒ pDh…à*“˜(&€?äE¡P(„”´B Z!¸¦¤$*‰€SAyQ( !%­G„V.‰‰b"àQ^ …BHI+¡€+EJ¢’8idª¼(Â<´B Z!¸($&Љ€3IfË‹B!LB+¡€K@J¢’8·d¼( ´B Z!¸Ú#&ЉN8çÌ‹B!¤§áˆÐ Àµ%QIp :m:ô,…Ü´B Z!àJŽ˜(&NJgþŒ¡g$¦áˆÐ m”D%p¦š5~úú­Õ<‹ +­G„V¸D#&Љ€s׬¡ðk+” a*Z!Ž­p5FITçá@@ÊÚ•Pxµ^îŽ\“Ð pDh…@'ÄD‹†µ75¤<Å]…W[¡\óÐ pDh…À”D‹Âµ‘Rž÷†¡ðV+” aZ!Ž­ÈAL´ˆ_«ïñ=bPI(\i…r!L1(´B Z!0%Ñ2só˜@J·ß]­P.„ü³B+¢¡,b¢%o5“P ¥òP¶B¹’ ­ˆF„VR-ƒ65Ÿ¸€¬ÊCá§• WÓ !ϸРhDh…Û‰‰–s›U@bå¡pùÒ —â\èq† ãB+¢¡ìMI´ãü+¬[y(\®µÂ¥øCˆÀðãB+¢¡œNL´´ítþh#8™¼µÂ]7Ð pDh…{ž›zxoÅ›ðÏ2€sÅõÞWø-¹RÒ pDh…£S-éÿö"P~¸¾ZõF€Ai…@8"´B€Ü|,Ñ"À<§yáš7 G+¡LNI´È…ã®då&¢áˆÐ Xác‰%†8I+\¹Õ6Qh…@8"´B¶P-Â"œ~V¾rÃÍCÐ pDh…ìÇÇ-Â"ì}–uâÊ@ÿ´B Z!'R-Â"ìG+¡Ð31Ñ",@5­G„VÀ¸”D‹°+´B Z!‰I‡U€™i…@8"´B¦%Z„ErÓ pDh…pËä¹P‹V„#B+€:¹s¡9 ‹$ áˆÐ `'ãæBÿzª°@Z!Ž­NÑm}óg-Â"@Z!Ž­útJS{ÎEàS&§áˆÐ `D{”²ç¹i|Â"@>Z!Ž­ØÞ¿žQ…E€t´B Z!ä°%lÉ¢°’V„#B+€4êrÕ­[naQXè–V„#B+€LîP…¡°Õn , ‹GÒ pDh…LyZÚ)¶Úa„Ea`#­G„Vù”£NBa«»†°¨*|¥áˆÐ  ¥ºHäA@U€L´B Z!d5y(ÜûáBX€þi…@8"´BHL(<÷QEX€si…@8"´BÈM(ìÿñGX€h…@8"´BHO(Ìq¤…Eà^Z!Ž­f ÎsLUEà/­G„V“ }„Eaf£áˆÐ `B!÷>OP…EšV„#B+ Žê', ‹Ð9­G„VÀT?aQX€ãi…@8"´Bz£ú ‹Â"4¡áˆÐ šê', ‹p‹V„#B+`ªŸ°¨*2!O˜™V„#B+€«T?UQX$O˜œV„#B+€í„?aQX¤C+ áˆÐ àHªŸ°(,r  X´B `Dh…Ð-ÕOX©ãh…@8"´BÈAõ…EþðÀi…@8"´B˜ê',ªŠYyÄ€´B Z!°Nõ…ÅQx€O´B Z!Ðê§* ‹g‘M¯´B Z!p ÕOXò)Kà*­G„VôOõ…ÅþQVà­G„V$£ú ‹S…EÃX¡áˆÐ €™©~¢E.„Ä´B Z!@!ÕOX” ±h…@8"´B€=¨~ª¢\œN+¡œNøåB`Z!Ž­` ªŸ°¨å´B Z!@VªŸ°¨Âä´B Z!‹°(,j…‘V„#B+à^ª_ú°èI9h…@8"´Bv¥ú =i! ­G„V@?ÀÓsáe³ž‡†V„#B+`DBá¹ðïf=Á ­G„V@zrá]¡P.„4´B Z!|4g.üºMÏH@+¡@µ¹ðê\H@+¤C¿~ýzzzz||üöíÛÇ'Ãå?/_¼|ë²Âœ;sֈРà}æÂ[[s¼ ­[>oooÿþûoùsã²òå&3ìÌé#B+€“ …BÈM+ä–O£ÛûX<==Õ=C.7̽3=Œ­F´= …žVÈU¿ÿ>ìX¼¿¿ý ã].7¿l$ßÎô3"´BÔ–\( ´B®ºÚËöøAïïïß¿ßþ<¹ld{¡ëjgºZ!¤ñðaeËÀFu¹P(„Ih…|òþþþãÇÃŽÅÆñ}ú@_¦éjDh…ÆÃ>QïA+€¾Ý› …B˜‡VÈG¿~ýúúg ÷;+ð²///¯¯¯? wù—ÿ¼|qe/ßͱ3½­ÒxØ¡ë=h…0‚ò\(ÂT´Â™ý ^OOO·>K¸ß±x{{«®l—nÝö²ÙÑw¦Ã¡B­ÓÞƒVã(É…B!ÌF+œSÇâVüýûwÉÍ/«]½ùe³£ïL‡#B+„T¿ÚÕ=¡†ó\Åã‰i…s:ýXÜúßëëkùF~ýúÕäÓ|]íLŸ#B+„l¿Z4>¡%i…s:ýX\ý〟»úyÀËÆÇÝ™>G„V l+}B! M(þÒ çtú±øöí[“à]ýHàeããîLŸ#B+„œ¿ j{ŸP …ÀZáœÎ=Wÿºß?ÿüS·µË «ÿÎ`o;ÓíˆÐ !廀¥ªúݺ‰÷0ô…¡¦¥R~p[müê¿ùùóçϺ­]nøuk///#îL·#B+„¬o–;saI(ôVƽVàÝ=LH+¤üà¶Úøããã׿¿¿×mírï[»üˆw¦Û¡Bâóÿ¥8†Bï& Áå`Z!åw¿oü£~Wÿààˆ;ÓíˆÐ !ñÉa.,…ÞPÀÐW <0­òƒÛdËooo ÿ>àWÿJàåµ3=­²žüæB¡²Ò )?¸M¶üúúúuËOOO[¶yõo^~ÐX;ÓóˆÐ !™»ra¸…0.­òƒÛdËWSÚÆ<÷òòR·Í®v¦ç¡B>msaùtE+¤üà6Ùò»«þx`W;ÓóˆÐ !¥³r¡°ýÐ )?¸M¶üøøxLž»ü ±v¦ç¡BVýçBav¥R~p›lY+qDh…ØÕ\ø0f.à^Z!å·É–¯æ¹···-Û|oØ ÏÚ™žG„V¹Í µEøK+¤üàö¼åºÍvµ3=­Ò …Eæ¤R~p{Þ²V¸ëˆÐ aB¡°À„´BÊnÏ[Ö wZ!L1ù¯->l(,›VHùÁíyËZá®#B+„ücÿv(\É…Ï«¤=a€þi…”Üž·¬î:"´BH>óË:`E.,'íi‹œB+¤üàö¼e­pסBæOÜ5 ‹Â"ÇÓ )?¸=oY+Üûuú§Ù\-A÷vºçŽézç/tL+D+ìdgzþ¿8…ˆ}¢°ЊVHùÁm²å=>vWýñÀ®v¦ç¡Bžñ¾!.«7é¶nˆ}¢¶°B+¤üà6Ùòïß¿¿nùÇ[¶yõÏ^~ÐX;ÓóˆÐ !¥ŠP¸ñ†ã>,‹Â"•VHùÁÝoã߿߲Áoß¾UïmW;ÓíˆÐ !Ÿ½/w.Üø8 , ‹À@´BÊn«_ýäÝûû{ÝÖÞÞÞ¾níò#FÜ™nG„VÉ4)}ra“G mQXÎ¥R~p[müê_ôûùógÝÖ.7Üò7»Ú™nG„V™4l|ráÁ‡ aQXšÓ )?¸­6~õ¯þóÏ?u[»ÜpËßìjgºZ!¤Ñ¼îÉ…ÝV„Em(¡R~pnÿêõ{{{»w;WÿÍÏËÆÇÝ™>G„ViìÑõ´ÂO„Ea¦¥R~pnÿßÿýºý?~Ü»ËM¾nç²ñqw¦Ï¡B;E=­pžç ¢°Éh…”܆ۿú¼‹×××ò\V¾º‘{?ØÕÎô9"´BHc¿¢§²‹Ú¢°Ò )?¸mÄÕOá•ÿu¿«g°îó€½íL‡#B+ 9±OX Z!å·í¸õi¾‹_¿~­ßöåååÖmë>Ç×ÕÎt8"´BÎ%ö ‹Ú"ìD+¤üà6ÿ)OOO·Žû÷ïß_^^>þ+ ïïï—ÿ¼|ñÛ·o·nuÙ`ŽémDh…ŒEï…E(¤ΩŸƒõýû÷V?ýññqãÃÒÕÎt5"´BÓû„Ea‘)ÞîöLð€Ñi…“¾Oìæ`½¿¿7)t—\6µñaéjgºZ!ü¡÷i‹Â"£¾ÜçøzÚ@Zá¤ï{:XÛ ]Ã6×ÕÎô3"´B¨ ö ‹Â"½Üá¨y&@Zá¤ïû;X+.pݰ«éaDh…°7±OXÔÙ÷=`ëáÈ@Zá¤ï»©oR£E(6Ò pDh…@>Ò›Ôh E+ F„Vð‰ô&5æ^„B˜‡V„#B+hKzӇ˅^¶•V„#B+è‡ô&5Ÿ ½î 1­G„V†ô&5Þ› ½j 7­G„VÀÒ[Öz¸ò- 7­p?’(iF„VÀv"]ç¡P.„9i…­ˆ¤$Z!§“ù…r!LH+ÜÈ#À #B+`tBaa(” a6Za‡3xºrֈР˜ÜT¡ðb¹¶¾‹Ô’VØç½V éjDh…Pm¸P(ÂT´Â®î¬hHŸ#B+€³œ åB˜‡V¸ë=ÝûGxs̈Р`D[B¡\“Ð ÞÇ?®Ž­†s«ñÝj‚+ß’ !1­pû½›a7˜|Dh…0–ŠP¸¬® BVZaõ]³KÌ3"´BK](\VWÓ !%­°âN9ÞCÎZ!Œ¥:.«+k…Vx×=êeÈË…;"´BË–P¸¬ÞD+„d´Â»Ó㨗 9jDh…0®ŠP¸ñ†À@´Âð¾t~åBZ! jcï“ !=­pý¾ qµBöZ!Œ¨Ié“ !7­på¾ tµBvZ! §aã“ !1­ðê}ôhj…ì4"´BKóº'BVZá×û2ôÕ ÙcDh…0–=ºžV)i…ŸîK‚cªÒ|Dh…0–¢žVùh…ïKšÃªÒvDh…0–ýŠžVÉh…ïK²#«ÒpDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’Vx:…”þG„V)i…'ÒIeDh…’V¸Eõ«¥Œ5"´BHI+¬¶eo}¸’±F„V)i…ÕŽùP¡VH#B+€”´Âj‡…B¹ÓG„V)i…uŽüP¡\Èé#B+€”´Â:ÍCaÉšž®œ5"´BHI+¬Ó¶–¯ìË)#B+€”´Â: [aóŸÍG„V)i…Úþ±Â»Ö÷Œå”¡@JZa…cBáâŸ!¥›¡@JZa…ÃZá⣅ô1"´BHI+¬ 2ۈР %­°B«V¸ß­ íˆÐ  %­°‚VÈl#B+€”´Â Mþ)Q­F„V)i…´BfZ!¤¤VÐ ™mDh…’VX¡b?«ï—VH#B+€”´Â:wíç–û¥ÒÈР %­°Î1­p „Ü#B+€”´Â:建å~ …t2"´BHI+¬V²·ï”VH'#B+€”´Âj{ß)ÿ)ýŒ­RÒ ·Øõi…ô3"´BHI+܇ ™dDh…’V¸‘2ÈР %­p»cB¡Vȉ#B+€”´ÂVÞ¡ÞF„V)i…mm¿ >TH‡#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…GÞå”AG„V)i…GÞ­AG„V)i…GÞ­AG„V)i…GÞ­AG„V)i…GÞ­AG„V)i…GÞ­AG„V)i…GÞ­AG„V)i…ÕöÛU}ÞF„V)i…ÕvÝO¹®F„V)i…uöÞOÿú(]­RÒ ë°ŸZ!ýŒ­RÒ ë°“>ZH?#B+€”´Â:Çì¤VH'#B+€”´Â ‡í¤ÒɈР %­°Â‘;©ÒÈР %­°Â‰­P.䔡@JZa­ÙF„V)i…´BfZ!¤¤VÐ ™mDh…’VXáÈýÔ éaDh…’VXA+d¶¡@JZacvu „Ü#B+€”´Â:Z!S­RÒ ë°·B!ýŒ­RÒ «íºÏÃ=äZ!¤¤n±ÇžûhxDh…’V¸E«»à¡ ÷ˆÐ  %­p£‡£x®râˆÐ  %­p;éG„V)i…MxÈ="´BHI+lH($ëˆÐ  %­°9•|#B+€”´Â¨„dZ!¤¤@dô¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’VØœGƒ|#B+€”´ÂV<$Z!¤¤n÷p ÏXNZ!¤¤nôp,ÏXNZ!¤¤nñp8ÏXNZ!¤¤V{8ƒg,§Œ­RÒ ë<œÄ3–SF„V)i…uD=¦Z!¤¤Ö‘™jDh…’VXA(d¶¡@JZa¡ÙF„V)i…´BfZ!¤¤V ™mDh…’VXA+d¶¡@JZa­ÙF„V)i…´BfZ!¤¤VÐ ™mDh…’VXG+dª¡@JZa-dª¡@JZa­©F„V)i…Õ´BæZ!¤¤VóÑBæZ!¤¤n¡2ɈР %­p#¹F„V)i…ù—H™aDh…’V¸\Hú¡@JZa‡“xÆrʈР %­°ŽVÈT#B+€”´Â:Z!S­RÒ ëh…L5"´BHI+¬£2ՈР %­°ŽVÈT#B+€”´Â:Z!S­RÒ pDh…’V„#B+€”´B Z!¤¤áˆÐ  %­G„V)i…@8"´BHI+¡@JZ!Ž­RÒ pDh…’V„#B+€”´B Z!¤¤îM%ÁˆÐ  %­p'>DI¦¡@JZa[>MIÊ¡@JZaC>SIÖ¡@JZaR)¹G„V)i…ÛyH?"´BHI+ÜȃÀ #B+€”´Â-šì¿\Hÿ#B+€”´Â-šï¶VHŸ#B+€”´Âjûí°\Ho#B+€”´Â:{ï­\HW#B+€”´Â:ìªVH?#B+€”´Â:Çì§VH'#â?ìÝÝuÙ¹µQ„À”cè˜B§À˾d J)0¦À‚>Œög ‘Ò[Ø»þöÚsŽº8¶%U,¯â1šZ!DÒ ÛÕ åB™­"i… v{’Z!'™­"i… ö|’Z!g˜­"i… ´Bf›­"i… ´Bf›­"i… ´Bf›­"i… ´Bf›­"i… ´Bf›­"i… v{’£\â'B+€HZa›}ž§VÈI&B+€HZa›£Z¡;–C&B+€HZa›žªrž‰Ð  ’VØfëg;ÖÕ ~"´Bˆ¤6Ûî …œm"´Bˆ¤öXýizˆŸ­"i…=Vyþ×ø‰Ð  ’VØÉE`†‰Ð  ’VØÏ ~"´Bˆ¤®B(${"´Bˆ¤®H($u"´Bˆ¤®K%$r"´Bˆ¤nD"$i"´Bˆ¤nM"$`"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+„Y]~:€@Z!PN„VSº|:€ _á´B œ­¦¤€×^@>­°ë'¨ ž³fÊ 'B+„ù\~s¾ ’i…]?Amó„µBÎ6Z!ÌG+¯¼€)h…í?AmùlåBN5Z!LæòǘðeK+lÿ J+dš‰Ð a2Z!x à5ÌB+lÿ!j㧪rž‰Ð a&—0áË “VØøÔöOÕG 9ÏDh…0­¼ð&¢6þµËóÔ 9ÉDh…0Ëâ˜ðeH+lü!J+d¦‰Ð aZ!x à5ÌE+lù!j¯çéCÊI&B+„9\î<€ _i´Â–¢v|’Z!g˜­æ €×^Àt´Â–Ÿ£´B&›­&pq8i¼ 00!­°åç(­É&B+„ è ‡Ãáp8üï `Þ vžxÆ×‚ßM„Vé¼±ép8‡C‚€©C€VØyâ_ ~7Z!¤ó¤Ãáp8ý¦Zaç‰g|-øÝDh…Í[…‡Ãáp8$³‡­°óÄ3¾ün"´Bˆæ==‡Ãáp8ý€ÙC€VØyâ_ ~7Z!äòæ›Ãáp8‡ € þè?÷}¾ŠVÈ!¡B.ï’9‡Ãáp8ôG!@+üÑîã~('B+„Pk¾5ôÂ{݇ÃáÐ& Zá*ç>Ü—€…¡B(­üO @Ð ›Þ]ÝøÙŽu5ˆŸ­­ÿ–‹·^`ÂV¨c:ú#´ÂSå<¡³M„V‰´B §x  a9$H­pçVØùœG¼h…€P(€VèJ:ú#ò^ j…ë\c ´B@+öÉ[®­þèpø) âÞ(´B­pÍ‹°Êet¯¢'…Þ*­ ÒáÐA+´B­p‹€Vh…Þ*€Éƒ‘+ìvr8$Hˆ{£Ð e²³\w)Z!0J(ôÓð8ˆ;Z!ú£Ãᇠ¼WàÆ† C€V¸Âž …äN„V¼þ÷c2x "A:$^$¸ aFZáš«ª:Z!xŸÐϳà5À¯„-ôG‡ÃÏkœìE0­pým ‰›­¼OègOð`ÐפÃág@ú^!ù´Â­FV%$h"´Bð>¡ŸÀk¯~&l¡?:þßÊ4¯€|Zძ2øDh…à­­ÅïE'ýÁÔ›º3¿H Òáðÿªú^á´B œ­¼%€—^ø¾ƒþèpd¥4­øÿ´B œ­¼Yä­BðÀkv»@‚t8¶ßrÉþV”¡‚wu¼U^x@À+UÐäi…ÀÿÑ r"´BðŒ· Àk¯póƒéÐHZ!PN„VÞ*ñ>!x à5ìù_(ÐZ!°­('B+okxŸ¼ðþ{ ú£C.>Ó r"´Bðþƒ÷ Àk¯À÷A‚Ô HZ!PN„VCÓ á&ôG¹ØV¸”˜‰Ð €í[HZ!p ­p]ý'%/r‰Р€á[èr!°„V¸–OJ+äl¡üБ µBH¤®bõ“’ 9ÕDh…Û¶-Q´ÂN”VÈ©&B+‹:†V,¤vÚ“x}}}~~~|||xxøùÛqû—·óöÝþ@üDh…(nB¡\‘´Â›ž”þìññqçKññññ÷ß/ÿžÞþðí¯¤N„VÀF´<­8–VØì®gÞvRZáÿüò™¾­/ÅóósÛízû‹‘¡0}pãP(B­°Ù]OX+ìñþþ¾Û¥¸^¯Ÿ?Ãx—Û_¿=HØDh… j…I+lsïSÕ {|ï¶øB×ëõÛ·oý7ííAbr¡VÛ6Ê…C+lsïól>©Ésáõz}zzÚíÆèüDá/Ÿ.Lš­¢Ëi…mî}’Zaƒ×××Ï¿¦p»ëð‡ßQx{///oooÿû´àíÿ¸ýËÛ¿ù‡gxûOc&B+€Ñ\v9€ái…- {ÿ“Ô Kÿ©o7ÏÏÏ¿û,áv×áãã£9ùÝþÀïþîía3&B+€Ñh…À"ZaËÂÞÿ µÂÕo¤uŸÃïêäûûû’¿~ûc_þõÛÃfL„V£Ñ E´Â–…Õ ·¼žûß¿ûPáÛÛÛòy}}üh¡VcÒ E´Â–…Õ ·¼žûß_þ¦Â†~ùáÄÛƒL„V£Ñ E´Â–…Õ ·¼žûß«|ðËÏ'Þ<`"´BÑ?›qm!†VØ@+Üôzî|c|ù«ÿú믶G»ýÅæ_zxæ‰Ð `8ÿl̆ Za­ðØ ¾îuøò@úýû÷¶G»ýÅÏöòò2úDh…0­XB+l°[+å‚ì|Á×½Ÿüz½¶=Úí/~~´Û—}"´BË?»p!€VØ áI®Õ ç¼K7½1Vÿ ƒ_þöÃÑ'B+€±h…ÀBZa›Z¡îp)>>>Vüe…ÿñå¯,¼}¡¡'B+€±h…ÀBZa›{Ÿ§V¸îÕ^ëR¼½½}~äçççžÇüò Þ¾ÐСÀX´B`!­°ÍÖ­p¬«±ÿÕ^ëR|Ùõ:[áËËËêyøDh…0­XH+lv×Sí ‹“‡Â»·ÂÎÏnñYÅÃ'B+€HZa³»žpsUÔ lÙ ÷i…·/4ôDh…I+ì±ü97ôD¡ðÏWf•GÖ N„V‘´ÂN ŸüN*à"r‘Wyä/[áÇÇGÏc^¯W­‚VØÉE8ê"Ÿù‘“¾‰Z!Ó û¹‡\á3?²V A+\‹P¸óµ=ó#k…À´Â©„{^Ø3?²V A+\P¸Ï%=ó#k…À´ÂH„[_Ì3?²V A+ÜD¸Å<ó#Ÿê»¼ÖÿS €$Z!Záêœt'ÿü¹Bƒ a´B´ÂÕ9éNÖ  ˜VÈÌ­ðññqõG¾^¯Ÿóö…´Bàl´B´Â_¼¿¿÷<æÛÛÛçÇÌh…nE£r~;·Â···žÇÔ €Qh…œßv7Æóóó>­ðö…†ž­"i…œßέðû÷ï=ùòò¢CÐ §x‰¶ø àŸU<|"´Bˆ¤H'í¹P«<òûûûçG~zzêyÌ/âí =Z!DÒ {4?aµtŸëÜöàß¾}ëyÀ‡‡‡¤o¢VÁ´Âf=ÏÖ‡+w»Ô¥/?x½^Ûíãããó£Ý¾Äè¡@$­°Ù>*Ô lÜ ¿üõ‚ß¿o{´Û_\ý ža"´Bˆ¤6Û-Ê…›^/eá_ýÕöh·¿˜ôË h…M+l³ç‡ 孯Ɨ¿aðãããÞÇùò@z{ð€‰Ð  ’VØfõP¸äON{—n}5þþûïÏÿôôtïãÜþÊçǹ=xÀDh…I+l³n+\þ‡ç¼K·¾_~ðæíímùƒÜþð—ÒðùÄN„V‘´Â6+¶ÂÕ¿JžnŒ/?¸üW ~ùKÛ>œxΉР ’VØ`Ý_VxןŸó.ÝáÆøÝG o^__ÿüw_^^~÷wGÿPá­¢i… ö …?ücH÷½ÏÏÏ¿» ¿}ûöòòòó?’ôz½Þþåíß|xxøÝߺ=`ÌDh…I+l°[+ü1ÇG Ïsç|ûöm­¯þøø˜4Z!DÒ h…[_Ï£îœëõºJ.¼=Èí¡’&B+€HZaƒµZávk,§ºsúsaR(ü¡@4­°V¸õõ<üÎùÃï.ü³ŒßQøy"´Bˆ¤6Xå%ªn}#u>«§§§å_îö‡o%u"´Bˆ¤6Ð §òúúúüüüøøøðððó7âö/oÿæí?ºýø‰Ð  ’VØ@+d¶‰Ð  ’VØ áy6Ÿ—VÈ&B+€HZa›»žgÏyi…œa"´Bˆ¤¶Ù§tAÈž­"i…m–?Õžó 9ÉDh…I+l¶äÙvž”VÈI&B+€HZa³­OÊ?€”óL„V‘´Â›ž‘VÈy&B+€HZa*d’‰Ð  ’VØÉ‡ ™a"´Bˆ¤öÛ'j…8Z!DÒ ×²â‰…œm"´Bˆ¤®«ÿ|¨N„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…ÿ;—°ï¬ÌÊŠ¡@$­ðçs‰ù¶úH&ëN„V‘´Â_Î%à{êßÊê¡@$­ðó¹ ý õ«Ùb"´Bˆ¤~y.ƒ~73΂N„V‘´Â?œË@ßÇÑŸ?'Ÿ­"i…>—!¾‰C?y†˜­"i…幜ü;8î3g ‰Ð  ’V¸ðtNø½î 3îDh…I+¼ëŒNò]åy3Z!DÒ NêÀï×ÉŸ©¡@$­°ùÔ<%æ™­"i…ýg7ÃÓ`ò‰Ð  ’V¸î9òuÝÆl=Z!DÒ 7=Ó­¿„˜}&B+€HZá©NV(䜡@$­ðœg-rª‰Ð  ’VØF%dª‰Ð  ’VØÉ`†‰Ð  ’V¸‰à‰Ð  ’V¸e˜‰Ð  ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ ·#€3Z!DÒ ×ÕRò"'œ­"i…kYñ¤´BÎ6Z!DÒ W±úIÉ…œj"´Bˆ¤vÚ褴BN5Z!DÒ ;mwRZ!ç™­"i…=6=)-ä<¡@$­°Ù]Ï¼í¤´BN2Z!DÒ ›Ýõ„µB†ž­"i…mî}ªZ!CO„V‘´Â6÷>Ïæ“’ 9ÃDh…I+lsï“Ô z"´Bˆ¤6hx’Z!CO„V‘´Â ÏP+dè‰Ð  ’VØ@+d¶‰Ð  ’VØ@+d¶‰Ð  ’VØ@+d¶‰Ð  ’VØ@+d¶‰Ð  ’VØ@+d¶‰Ð  ’VØ`·V8Ê!~"´Bˆ¤6hx’kµBw,‡L„V‘´Â6;´B*ä<¡@$­°Í½ÏS+dè‰Ð  ’VØfëV8ÖÕ ~"´Bˆ¤6»ë©ö„E¡Ã'B+€HZa³»žpsUÔ 9ÃDh…I+ì±ü97ôD¡óL„V‘´ÂN ŸüN*à"?Z!DÒ ;¹Ì0Z!DÒ û¹ÄO„V‘´Âµ…O„V‘´Â©„¤N„V‘´ÂÕ …äM„V‘´ÂH„$M„V‘´ÂH„Œ>Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+\«AÞDh…I+\‹«AðDh…I+ìwÙ‘;–C&B+€HZa§Ë¾Ü±2Z!DÒ {\vçŽå‰Ð  ’VØìrw,‡L„V‘´Â6—ƒ¸c9d"´Bˆ¤¶Qô˜j"´Bˆ¤¶Q ™j"´Bˆ¤6 ™m"´Bˆ¤6 ™m"´Bˆ¤6 ™m"´Bˆ¤6Ð ™m"´Bˆ¤6Ð ™m"´Bˆ¤6Ð ™m"´Bˆ¤6Ð ™m"´Bˆ¤6Ð ™m"´Bˆ¤¶Ñ ™j"´Bˆ¤¶ñÑB¦š­"i…m´B¦š­"i…Í´Bæ™­"i…Í|´y&B+€HZa¹I&B+€HZa­I&B+€HZa'¹&B+€HZa?¹ø‰Ð  ’VØï²#w,‡L„V‘´ÂUh…dO„V‘´Â~>WHüDh…I+ìtÙ—;–C&B+€HZaËîܱ2Z!DÒ ›]ŽàŽå‰Ð  ’VØærw,‡L„V‘´Â6ŠSM„V‘´Â6*!SM„V‘´ÂB!³M„V‘´ÂB!³M„V‘´ÂB!³M„V‘´ÂZ!³M„V‘´ÂZ!³M„V‘´ÂZ!³M„V‘´ÂZ!³M„V‘´ÂZ!³M„V‘´Â6Z!SM„V‘´Â6>ZÈT¡@$­°VÈT¡@$­°™VÈ<¡@$­°™2ÏDh…I+ì!2ÉDh…I+ì¤2ÃDh…I+ì䣅Ì0Z!DÒ ûÉ…ÄO„V‘´Â6—ƒ¸c9d"´Bˆ¤¶Ñ ™j"´Bˆ¤¶Ñ ™j"´Bˆ¤¶Ñ ™j"´Bˆ¤¶Ñ ™j"´Bˆ¤¶Ñ ™j"´Bˆ¤¶Ñ ™j"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´Â%'uÔ¹+§œd"´Bˆ¤.9©£Î]+ä$¡@$­pÉIuîZ!'™­"i…KNê¨s× 9ÉDh…I+\rRG»VÈI&B+€HZá’“:êܵBN2Z!DÒ —œÔQç®r’‰Ð  ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+,OÊM‚‰Ð  ’VXž”›¡@$­°<)7 &B+€HZayRnL„V‘´Âò¤Ü$˜­"i…åI¹I0Z!DÒ Ë“r“`"´Bˆ¤–'å&ÁDh…I+,OÊM‚‰Ð  ’VXž”›¡@$­°<)7 &B+€HZayRnL„V‘´Âò¤Ü$˜­"i…åI¹I0Z!DÒ Ë“r“`"´Bˆ¤–'å&ÁDh…I+,OÊM‚‰Ð  ’VXž”›¡@$­°<)7 &B+€HZá!'5ÊÕ­‚i…‡œ”VÈX¡@$­ð“Ò k"´Bˆ¤rRZ!cM„V‘´ÂCNJ+d¬‰Ð  ’VxÈIi…Œ5Z!DÒ 9)­±&B+€HZayRnL„V‘´Âò¤Ü$˜­"i…åI¹I0Z!DÒ Ë“r“`"´Bˆ¤–'å&ÁDh…I+,OÊM‚‰Ð  ’VXž”›¡@$­°<)7 &B+€HZayRnL„V‘´Âò¤Ü$˜­"i…åI¹I0Z!DÒ Ë“r“`"´Bˆ¤–'å&ÁDh…I+,OÊM‚‰Ð  ’VXž”›¡@$­°<)7 &B+€HZayRnL„V‘´Âò¤Ü$˜­"i…åI¹I0Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´Bˆ¤åDh…I+ʉР ’V”¡@$­('B+€HZ!PN„V‘´B œ­"i…@9Z!DÒ r"´BÌeûˆ åDh…0­XF+ʉР`0Z!°ŒV”¡À`´B`­('B+€Áh…À2Z!PN„VƒÑ e´B œ­£Ëh…p—ÇÇÇ©nQ­†¤Ëh…p—‡‡­8;­XF+„åÞßßg»EµB’V,£ÂrŸÿ¤Z!pFZ!°ŒVK\¯×§§§ oQ­†¤Ëh…Pz}}ýük µB༴B`­~q½^ßþõüüü»Ïj…À©i…À2Z!tÞÒ3L„VƒÑ e´Bè¼¥g˜­£Ëh…ÐyKÏ0Z!œÝe÷ˆ Bç-=ÃDh…p:—£ ‚V·ô ¡ÀÁ.ç;€Z!tÞó3L„V{»œþ"h…ÐyÏÏ0Z!lî2ÚDÐ ¡óžŸa"´BXÙeüˆ Bç=?ÃDh…Ðëw´Bè¼çg˜­îvI?€Z!tÞó3L„V…Ë|A+„Î{~†‰Ð àW§ìwÿü—V,¤Bç=?ÃDh…ÌîÜeð3­XH+„Î{~†‰Ð ˜ÎhqP+Úh…ÐyÏÏ0Z!ùƒZ!ÐF+„Î{~†‰Ð H“UµB ™V÷ü ¡0¼ô8¨m´Bè¼çg˜­€ñLµB V÷ü ¡pvs—A­h¦Bç=?ÃDh…œŽ8øe"ü¯=¿04­:ïù&B+à`ÊàµN+Ò ¡óžñ9Àɉƒ_Z~Oõd€!h…Ù WùŸ\|®€­‰ƒ«~vÏç …|®:ïù&B+`eÊàÆõM+Ò ¡óžŸa"´Bz‰ƒûæ6­XH+„Î{~†‰Ð ¸ÏE<¸¯i…ÀBZ!tÞó3L„V@AÊ 2À ´B œ­(ˆƒâ cÒ r"´BàWâ 8@­('B+€Ù)ƒÊ ¡´B œ­¦#ŠƒÌA+ʉР Ÿ8(0%­('B+€4Ê 2ÿÒ r"´Bž8(ÀW´B œ­£ *ƒ°ŒV”¡ÀÙ‰ƒâ 4Ñ r"´B8qP€5h…@9Z!LT`Z!PN„V{ÅAØ…V”¡À¶.â 8ÇÐ r"´BX™2¨ À9h…@9Z!ôÅA8%­('B+€û(ƒÊ B+ʉР  Šƒ0&­('B+€_‰ƒâ DÐ r"´Bf§ *ƒJ+ʉР˜Ž8(À´B œ­€pÊ 8³Ò r"´BÒˆƒÊ ð/­('B+`xâ 8|E+ʉРŒ2¨ Ëh…@9Z!g'Šƒ@­('B+àtÄAqXƒV”¡p0eP¶¡åDh…ìM€]h…@9Z!ÛR•Aà Z!PN„VÀÊÄAq8­('B+ —8(§¤åDh…ÜGT€Ah…@9Z!qPƤåDh…üJ€Z!PN„V0;ePBi…@9Z!ÀtÄAq˜ƒV”¡„S•A`VZ!PN„VFþ¥åDh…ÃÅA€¯h…@9Z!À`”Ae`­('B+8;qPh¢åDh…§#ŠƒkÐ r"´B€ƒ)ƒÊ À6´B œ­`oâ 8° ­('B+Ø–2¨ D+ʉРV&Šƒç åDh…½ÄAqà”´B œ­à>Ê 2ÀÿcïÞŽWF¶ ¸€ Ø€ ¸€ <žG\À¹€ ¸€ ¸Ð'cWüL!)…®yY+:&fwW‰KQ©L>$™Ð è¡Dˆƒâ @ž´B :Dh…ÿã8(B+¢C„VÔNT ¥Ñ!B+ª#ŠƒuÐ è¡…S•A€Zi…@tˆÐ €Òˆƒâ ÿÑ è¡ÙÅAÚh…@tˆÐ  o+d¯ ²2@´B :Dh…·Z¡8(0ŠVD‡­òV^+TÅAf¢Ñ!B+€¼Ð ÅAe€eh…@tˆÐ  o9¶BqP`Z!"´BÈ[ú­PT؈VD‡­ò–`+ÅAÒ Ñ!B+€¼¥Ð ÅAq€$i…@tˆÐ  oë·BeP Z!"´BÈÛŸJÿˆƒ¥Ñ!B+€¼)ƒÊ tÐ è¡@ÞÄAq:h…@tˆÐ  oâ 8´B :Dh…7eP€Z!"´BÈ›8(@­ˆZ!äM ƒVD‡­r¢ *ƒ0˜VD‡­Ò圢â L Ñ!B+€T(ƒÊ ÌJ+¢C„VÛø“Íq2¥Ñ!B+€•üÉõ8™Ò è¡À"þ”óG€Li…@tˆÐ `Jþ#@¦´B :Dh…ðµ?uý SZ!"´Bˆ­kÿ£ @¦´B :Dh…ð±$ΩÜmÕ ½L }Z!"´Bj—ù1}ëÜ/È‘VD‡­€ºweÀ5î'­ˆZ!%ûS`Ô €´B :Dh…”£‚2¨Ãi…@tˆÐ ÈX}eP+†Ó è¡ eP+¾¡Ñ!B+ QÊ VL£Ñ!B+ ÄÁÁqð­‹@.´B :Dh…l@œ¯Ói…@­ˆZ!kP«rZ!ÐE+¢C„VÀü”Á3œVtÑ è¡0•2¸itÓ €.Z!"´B¾\.Šƒi%6­è¢Ñ!B+ wq¨ ¦Ô´B ‹VD‡­€ÿ] *ƒ™å3­è¢Ñ!B+¨š2¨—åÒ è¡TDT€šh…@tˆÐ ŠåRƒÊ P7­ˆZ!@!”Aeài…@tˆÐ r¥ *ƒ½´B :Dh…yPÅA€/i…@tˆÐ R¤ *ƒ“i…@tˆÐ ¶çRƒÊ À´B :Dh…kS•A€Uh…@tˆÐ § *ƒ[Ð è¡ÌLÒ Ñ!B+˜DTR¥Ñ!B+øf¥$*ƒÙÐ è¡t¯‹”Ae cZ!"´B€·…2¨ ”C+¢C„VÔKЦÑ!B+j¡ *ƒ•Ñ è¡er©Ae zZ!"´B Ê 2À­ˆZ!%eP F+¢C„Vd@øžVD‡­HŽ2¨ 0­ˆZ!°1—TX†VD‡­X•2¨ °­ˆZ!°,eP`#Z!"´B`NÊ 8@2´B :Dh…ÀxÊ 2@´B :Dh…ÀàE‚8¨ ­ˆZ!б$P•Aò¦Ñ!B+þo   *ƒE+¢C„V•RÅAJ§Ñ!B+€*(ƒÊ õÑ è¡@\jP­0Dh…PeP€Z!"´BÈ2¨ ÀZ!"´BH2(À(Z!"´BH‹2¨ ÀL´B :Dh…°¥?â 2KÑ è¡Àª”AeÖ¢Ñ!B+€)ƒÊ lG+¢C„V³QÅAH‰VD‡­Fr©AeÒ¦Ñ!B+€aóceP€Ìh…@tˆÐ  cB¬ *ƒ7­ˆZ!ü7ýU•A(VD‡­€)ƒâ T@+¢C„V@ù\jP€*i…@tˆÐ (2¨ ÿÑ è¡=ePÚh…@tˆÐ ÈŒ2(Ãh…@tˆÐ Hš2¨ ci…@tˆÐ HˆK *ƒÀ|´B :Dh…lFT€%i…@tˆÐ X2¨ +Ò è¡°eP6¥Ñ!B+`Ê 2$F+¢C„VÀ.5¨ ÉÓ è¡0`j¨ *ƒ@~´B :Dh…´Í•AeÈžVD‡­ePФÑ!B+¨Ž2¨ uÐ è¡Î¥•A VZ!"´B€¢(ƒÊ ÀÿÑ è¡äMT:h…@tˆÐ r¢ Šƒƒi…@tˆÐ Ò¥ *ƒh…@tˆÐ RáRƒÊ À¬´B :Dh…ÛP•A€…i…@tˆÐ V¢ *ƒëÒ è¡,B¶¦Ñ!B+˜2¨ ¤G+¢C„Vð5—Tr Ñ!B+ˆÍŠ”Ae KZ!"´B€i2¨ ”@+¢C„VÔN ¥Ñ!B+ê¢ *ƒÕÐ è¡%s©Ae bZ!"´B(Ç mË3  ­ˆZ!”£ÂV¨ *ƒtÓ è¡B9Šo…Ê 8À7´B :Dh…PŽÂZ¡2¨ 0VD‡­Ê‘u+t©Ae€¹i…@tˆÐ ¡µBeP`yZ!"´B(GÊ­PTXVD‡­Ê‘N+TÅA Ñ!B+„rlÕ •Ae€$i…@tˆÐ ¡ë´B—TÈ„VD‡­Êñ§Æ?Ê tÑ è¡B9”AeÞh…@tˆÐ ¡Ê 8o´B :Dh…PeP€7Z!"´B(‡8¨ À­ˆZ!”CTàVD‡­Ê¡ *ƒðF+¢C„VåPÅAx£Ñ!B+„r(ƒÀ­ˆZ!”£‚Jè‡ Ãi…@tˆÐ ¡ŵB?R˜B+¢C„VåȼúÀ¼´B :Dh…PŽÜZ¡Ÿ,J+¢C„VåH»úùÀÊ´B :Dh…PŽ”Z¡ŸlN+¢C„V娮zî AZ!"´B(ÇZ­Ð3 YÐ è¡B9–o…@F´B :Dh…P­x£Ñ!B+„rh…À­ˆZ!”C+Þh…@tˆÐ ¡Z!ðF+¢C„VåÐ €7Z!"´B(Æÿ[ž'2¢Ñ!B+„bh…À;­ˆZ!C+Þi…@tˆÐ ¡Z!ðN+¢C„VÅÐ €wZ!"´B(†V¼Ó è¡B1´BàVD‡­Š¡ï´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­ˆZ!I+¢C„VEÒ è¡@‘´B :Dh…P$­Y4Ms¹\‡Ãn·{ÿñ…ÿ þ)|g)ß!B+€"i…Lñ|>ÏçóðŸføâð-Y?äÃáPÕKT+€‚i…Œv¹\ÆýLÃ7æû¨8©ùÒ áõz}^÷•ðía#Ù=ðÇãQÛKT+€‚i…|ëõzí÷ûé?Ù°‘ìrak!­aˆÐ  HZ!ßšxDᯣ syÔ¯×ët:UøÕ  `Z!_é¹Fán·»^¯÷ûýßÑ‚áÿ„ÿ ùy¿¿¦ÿ¨›¦éy5 Z!I+d¸çó9:ù…/èúÞ°Ù¤æOâ .—K×±„Z!P­áºÂÙãñòíáËZ¿=l6‘è%Ú5Dh…P$­º*¼ßïÃ7Ò4Mʇz‰v Z!I+d Ö+Ž8$°õàİñ£—hס@‘´BÚív³Øz|bØx ÑK´kˆÐ  HZ!C´^jðx<ŽÛZøÆÑ=\”—hס@‘´B†h=éív·µðŸ[»^¯é?Z!P­!‡ÃçæõzÛZøÆÏ­…›HÿyÐ €’h…ŒûYO¼Â`ëÕs|´B _Z!QÏçsÆ‹þh½da¸¡ÄŸ ­(‰VHÔý~ÿü¹\.—)Ûl½b¸¡ÄŸ ­(‰VHTk×›Ø ¯×ëìÛ\V”D+$j‰c—8VqZ!P­¨Ãá°N+ 7”øS¡%Ñ ‰Ò û_ó5 Z!I+$ªµ>ŸÏ)Û|½^ZaFC„VEÒ ÷ƒNv³9>éZ!I+dÜ:ÙÍæøT¤?Dh…P$­q?èd7›ãS‘þ¡@‘´BÆý “ÝlŽOEúC„VEÒ ÷ƒNv³9>é?­(˜VˆV¨j…P'­­°’û<‚ʦ¢:®°ç4Å@Á´B´B­pÃ_v`)ç©ns‡ÃaöŸËëõúÜf¸!­P+V¦òm+|<S¶y¿ß?·©äB+¬Dk+¼ßïS¶©dM+¬ÄårY§†Êñ5ïTH+¬Dk+¼ÝnS¶y½^µB€|i…•XâÀ%ŽUÜê5ïTH+¬Äãñøü¹œN§)Ûl½b¸¡_ó^!@…´ÂšÖûý~Êw»]Ž?k/Q€Za=Z|½^ã¶ö|>?·n"Ó×¼—P!­°­—¼Ýnã¶¾qö nøš÷ò*¤Ö£õ’…ÇãqÜÖÂ7æx±Â¿Z!ÀÿÑ «Òz…ÁçóùívZO@6žïkÞk¨VX•óùüùÓ9Nßn'|ËçvÂÆg|ù­üš÷Ú*¤V¥õxÀà~¿ßHøâÖŒ8>Q+ØVX›ÖC‡_j°õ¢‡ãNü«lJ+¬Mס…AÓ4ýß{½^»¾wÄA…µB€Mi…º\.]?©ý~½^ßOIúz½Â†¿Üív]ß68ûËoå×¼WP!­°Nûý~®îápXâå—ÚKÚk(VX§×ë5K.  ›Zâå—ÚKÚk(VX­é¹pb(ü«lJ+¬\ϵ û¾FáÀ—_j/i/ ¬\zÃ0,ˆÎçó·wéx<6Mã•€9À·“Ëå2|·¦ ³J'Í ¶§+à=üÇã!@š´BXÂç bÞí7Ms<§ï£/—˼ ¨°zšrÂóæ0C̺^¯ãv¸‡Ãa–ƒÒœ@‘KïÜßý^]±Õ+ 6§ÀìZ?)7ãö¿ý(c¿ý~?˧o·Û\w)¬Åb€9@°£l=Å÷W¾»¼ ”ºôÎú=üðØ&]>€¤h…0»Ö÷îß}?)wiâá„Þ0À`¸®ów­|˜C‚(xéõ{øýox±A‘#ž_vªÕsÎÄwß»Ýnt›ë™ó‡ÍžÏç¦i~-ˆî÷ûÏåäB̾2c(œxtaj˜Wj{º|ßÿ\.òT8âùe NMÓôœ¢dÝ÷ñx “ðûýþÞæÂJ$üÍõzí?YYø×çüûý>û?föªïWþý7è9ëW0d—ø„f—Úž.Ó7Þ‡\¯Ä‹ Rñ´Bèñ³ ÂaÈ8Ý}‡;0ð]¾çóÙ³lùö­Âðð[·îÏ·Of×{˜³\ʘtí.Ãß¿'®½vOÐqŒC:XBj{ºßxx>/6HpÄÓ `‰½çBwàr¹„5È·[èzcs·Û}µÖµÏ·¡ðGO.t.2ÌþvË?|Ïv©Çãq–Ïù$5!€¥÷û)ìé²{ã}ø‰Ó½Ø Ó•ŽV€½çæ­0,:¦D´®5Ëð+­·Nû§¼¹μ×Q€bæ]{ɯ+~ûh"Xt¿ŸÎž.¯7Þ[?¼ÎÅÕV:Z!öžî w»]ôŸgïz­Û<^uT>h=¨0L F¼{¾¥õd¤ßfÇD&$°Ôöty½ñþy¢ý~ßõlx±A¦+­{Ïö†­anøeÖ[O@:â¬,ïZ/€èDd˜´Ö½ÑÑéºâð&çýž8!€Ä͵§Ëè÷Ïãÿ}ÀI>€’V:Z!öžì '†¹…¦¹æ¿Ì~Þ￟ù¹^¯ÙMH qsíérY,‡éDÏ9W-ù¡¤•ŽVLà'žýC+€uöb­' =ŸÏS¶y»Ý9šÏéÈ(Û\{º,v—MÓ|ÞÉ÷S¹ÚéCF ­fß½–wWçº>{¾Ï¬³;Ÿ›mš¦˜}®½?fì.[Ï„ðë”évúÑ0¥Àæ«€ôïjë÷Nß29‘ £…ŒV³ï^“º‡­y.½^ߨsíÂÏåCÓ4á[Âÿv¹Ô«s€®×ë¼;îD&-³OH à¥÷¼ïØO€Œ2Z!̾{Mä¾u]^pà'Ç­†w@%À K×é¾§Ðub´2&$PØÒ{‰÷íGäÂÖÓ„Ç[À&`!£ÀÒ»×îXë¾Ø÷x>Ÿ‡Ãaô„ár¹8çæ­n·Û¼ôìµsŸ@‘KïŸSñǰv¾^¯÷ÿ|.¢Ãß„¿oš&|Y×IPß}õ©ÝÖ ÉW[ £…ŒV³ï^S¸c]o Îr²¯° iýÌdØøå‰b€9@¿®w®ü|>‡l$ìdo·[ÿg{²ž@ÙKïoýìú{.þià¦Z–üö3Kòd´Ñ `öÝëæ÷ªëÔ ÇãqúÒ£u)ô~¥çó–E=orþZª8)æ¿tÍì}Ÿþs”Á¯nøsÜAØ/9ü?|M¦HAš{º°lï¹\ÈÞ×zêòóù\Æ&€Vëì^Ó\­ ¿þxÏz¡µvžáÑð=5€9ÀßÞ3‘ÎeÑV¸Ü„Røž®õt@C-l]ø«Ÿòd´Ñ `öÝë†÷§ë}Ű˜¾Zù*¾{<=küwF5/'̆ìÖç²ÜQ‹NH`sYìéº.Ò!ÅÏï]?åÈh!£Àì»×ÔV+/ªþã|>O¼Ãóùì/†Ž.Àà—¦iæÊ‚«íyÀærÙÓ…5øˆk)~~ýèú)@F ­fß½–·Z¹ßïs¸,Ü™ž³’ºv!濼^¯®ã†8NÏç³uW>äŠEIMH`syíéZ?¯Ûb”|-d´B˜}÷ZÞjåp8ÌÛõº0\ôÂIï ìv¿*†»Ýîr¹<ŸÏŸoo½hÑìÑ ([v{ºÖô¯»ÿlÄ« ¶]Èø¥€Ùw¯…­VZÏ[²ßï'n¶+z/s€.¯×+ì÷ÏçóápøŸ¹<Æ®Óz]`0£ÖSd7!€õå¾§±èÖ ¡Î…Œ_j˜}÷ºôTÿó’CÿV+á_ózŒ¦˜,ªõ¤§Ó)» ¬©Œ=]ëÉ|V»ð‡õ>d´Ñ  £©ï&«•Ö››ë¶L'0XTëÏ™&P¶böt×ëu‰ YïCy ­r™ú>®ýï¢SýÖ%RÓ4³¬¿6üˆ#ä2m‰£ ¶šÀ:JÚÓÇÏGq»ÝL–ÀBF+€§¾MÓtí|Ïçóú‹‹Yn´õ Lo3`0‹®c"Âþ7Ç ¬ ¤=]×Å W»Ò¢|-d´BHê{»Ýºö¼+| °õ¤%³œ†4,µ6üˆ#¤?˜¢õì£S*ÜvBK+lO׺âÞï÷&K`!£@vSßÖéýŒ'z>Ÿ­·~¹\¦l¶ë#Ž.r€9Àt­¡pÊ¡›OH`Q‰ìéæº¹®3©®=åÈh!£@ÊSß®7ú‚0ó_ía‡ÙïCëBÌ H0Xnþ0ús>‰LH`å]çú{ºç˜r¦ÐžK.š,Z!ä5õm½ÆÐÏ9CV»¾ÀÖ NY7uÚÅA…˜LöË]ó‡ÑgMgBKHjO÷ëô#î@O(\ùLªòd´Ñ  Ù©o×je“ Ös>–ûýþÕ¦.—‹K`0Än·¸ »ãžc"¦Ì’šÀ:ûô­öt­Ÿö “!w&|M×r{â5‹Sž,³ünj…ìÔ÷ÏZ†Ü™°èèú°åð;Þï÷®Ó™:û(æ]·²ÛíÎçsÓ4ï{Û°k;ÖÛíþ)|AÏŽ~â»IMH`}úV{ºžo«éËåöþï§÷ù™\¯×ãñØëÛ#iwµ zžgì=çÚ]¦öÖ\. ÂÚ$¬P~fV1áoú߯ 0XènL?,B+ÀN?…V8Å&×¶»‡š=Ï3öž¥¶Â¿ráçóÙ+ s€%îÆ,;Y­;ý¬[á&¡ð¯Vuzžgì= n…ÿË…=×.üVÓ4^f˜Ì~7v»Ý·ÎkB‰ïôSh…ûý~ýSö?¯7¨dÐó<`ïYv+üÑsåÁáG:lr¥xH0ånìv»Ûí–Ý› ÞO °þ¸=]X&_¯×YÎç¶“àëõ• zžgì=kh…?î÷ûétúö ÌËå²á' ý9À¸»vÊK°¯`§¿þžîñx\¯×ŸÑÝï÷·Û-…æÚÝC̓žç*t¿ß/—Ëñxü\Èìv»ð—§Ó),s¶ºJdªišóùö¤aúùN =,T²â»ûŸ)AÏ¢ûv»ù\.¥@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤@´B¨“VuÒ  NZ!ÔI+€:i…P'­ê¤”ºv;üçt:].—ûýîi`–õ¦VãÚíx<6Mãù`Þõ¦VËÚm·ÛÝn7Ï*Z!@k·Ãáð|>=·Ö›Z!@k7×1°ÞÔ ª]»¹‚!€õ¦V×Ú­ÿ[žÏçý~¿Ýn§Ói·Ûõ¯Ú‡'ÀzS+ÈeíöÕî÷ûápèZµí÷{O2€õ¦VËÚmÄv®×k×Âív»yž¬7µB€,Önã6u¿ß[·¶Ûí<ÏÖ›Z!@k·Ñ[»\.­¼ßïžjëM­ ýµÛ” îv»Ï ^.O5€õ¦VþÚmÊÏçóç‡C‚ý~¿_.—pßöûýû½ ÿþ2üÓ:‡CvÝŸçít:]¯×Çã1ã-¾^¯Ûí~Raû¿Ún¸Çã1ÜŸyoñÇóù %<¢p»Ÿ'ªýy°ó>íëßbj¯®%lø¬PÀzS+Hí6eƒ­W-ì¿dáðûð¹ŽÇã¯þò“'ÞÃçóÙu®ÔVá‹ÃíÎþ̇»îóð»žÃóù¾kâO'<{Ãoñz½N¤áÙ Ûù,¡ýÂÏ4|׸ǻþ-nøêZí×gÃg€’Ö›Z!@úk·)|½^ßnsÈ×)kCîÞWg¡Ó¨~[ ?ãËívûöFÇçQ`‹á”ÄÂ]¸ðwû«;°þ-nûêZç×gÃg€ÂÖ›Z!@úk·•·ýú ¦ÿ^½^¯o‰ú%|ûô ›¦™e9ܨæ/ÓCψ˜ž«áÇ0βü_ÿSxu-ýë³á³ @‘ëM­ ýµÛÊÛìùúÇã1<ÁôÜDØÎ\…nÊ…üZ/æ¸ôŠxÊ1ŒïÂþH§·³oìú·˜È«kÑ_Ÿ ŸUJ]oj…é¯Ý¦lpÆs~{4Ü·witÐwü×\Íî«SÏžÏç¦iÞ/$w¿ßû/H7üܧãÎw:åÁ®‹‰¼ºýõÙêY àõ¦VþÚmÊï÷{ë¥Ê¾½#.ýÖµýžøµÛí …ûü/Єÿþó|>‡ê9]ä·OËõzí?ÿdø‚÷»ñóLÞn·žØ7úFÃCîR­?Äïmqă ÏjxnÃöß·óóœ‡Ûó´§v‹é¼º–ûõÙðY àõ¦VþÚmÊ[ûÂétZaýغñžh=J®§•|uý¾žô¶ßïÿF·îêç^ýßÒub̦i†ÜççóÙúíý?Ê¿½ÇÙ <,1ÜtxæŦ¤n1©W×B¿>>«”½ÞÔ Ò_»MÙ`ëaVýqaàðßÁwïßûx<þ|÷¹å®à65ðÂp=—¢r„Ý®c¬¢Ýí—ðØßŸÞ7:0ö?öþÞuêË'n ÷_!Mê“zu-ôë³Õ³ @ñëM­ ýµÛè­5M3"|D—~‡ÃaÈÁwŸºNà90åüè :K_סg߆ÂþÖó5­¡ç«£ÕzžÀþ{~<§\èðSøÑïv»¤n1W×r¿>[=«¿ÞÔ Ò_»ÛT×I Çã”õãW‡Â½ë:ìkÄÇ5ОG×ǨÇãÑU»Ïƒ Çeš®çð«›w0Úpëßb:¯®å~}6yV¨a½©¤¿v±×ëÕzöÑ zLS׊ïx¨Õæ!Ç•Ïë5TO*ßC×òa±Þ*K{CJ<÷û½Ód›•Óin6›ñI›þ#]£ž>³´*+É7Õ V’»µ¯O‘îUΜ9pbƮˬüûãñ8ê­üÒ†× öÛA›Í¦îÓVîÐô‘–µã®¦ÿÆGר§Ï,­ €|S­ ˜Üíz½\‡ÛUùTÔÀGå#{ OØõ~Rlˆ¼/+챃*ŸCüýXej¼³°NÿŽ®QOŸYZù¦Z!@¹Ûn·ëZA(µV8Æ:ôÛð‰óñ6ë°Ùl‡Ãù|ÎÒ ãj…³ìGä›j…ËÍÝ6›M¿‡æÆH÷FJ!;-¶òÇ~Ý[Ú ³çãÇ£ëÒ¶Ûí«äÔoºÔ‰¿1ÂÑ5öÊ̲oª,1wÛï÷C¦Ö\U­°Œdüë†\¯×! ?]‹MS~ãJj…³ìGä›j…Ás·Íf³ÛíöûýétÊ2ñ Záâ’ñ6r¿ß>䘳N•¦É¾q=µÂYö#kÎ7Õ "än¬ƒZa|üt: ü¢®¬Nð«ªε(5ßT+ˆŸ»°j…¡rçËå²ßï{çþÇã1Ô7®°V8×~ ¼|S­ ~îVÀ:„­>ŸÏâwhƒÛív:öû}×i-¯×kœo\m­pÆý@1ù¦Z!@üÜ­€uØívŸËøvÅôñÊw±M¹_m·ÛÏ/ù¶¸çóù.9µyKfœoŒptÅ9…§ß,:ßT+ˆŸ»°•ÕœÏ4¥¯ŽýÖ¶Y ”YÜï÷ãñ8å ïzc„£+Ô)<ï~`Yù¦Z!@üÜ­€u8NŸËL¿œr™•“±_ÙVù¥7|JÏçóp8Té÷A¾1ÂÑêްXJ¾©V?w+`.—K¿§´t}N°r¶Ûí¨9Ë—fWÙÔ£nE§oŒpt…:…ãìGâç›j…ñs·ÖáñxT.öù|ö[`ú`åÓu]‡† 7Ë—fW9!ç¨Gf§oŒpt…:…ãìGâç›j…ñs·2Öa³Ùd|WZå£dé+z¬ÃØÓV~éâ&~œþÈìôŽ®8§ðrW€é£AµB€ø¹[ëPùæ¾–õ—O•µ¡¯¯“«| ÝØOùÕ}éý~wdæúÆGWœSx¹«ÀôÑ Z!@üÜ­Œu¨›(ò|>w]TúH¿’_Ý:ì÷û!›v¿ß^úV÷¥é#½'Éü½ðæ÷Ím6›Ûí6ƾ«ûÞé¿1ÈÑ5êé3K«°†|S­ ~îVÌ:‡áOØ¥?®\HËY=ëÖ¡åScŸÞ öøÒåÂwUëëÞ<C¾¨rNκŸþã]ã>sµ*Åç›j…ñs·bÖ¡îá¯öºRNûǾÖ¡ë‹ o·Ûv»mÓD _š–ÐcÔËåò{žÌ–{ót:õ¨4Õµùõz òqŽ®Ÿ‘k…Ó·*Åç›j…ñs·’Ö¡îå}m¦‹¬›²ëS ë°ÛíÚÌôx½^Ó_vj¢†•o_ýI“–óù6½N{óp8´/U>‰Öü*Àé¿1ÔÑ5A­pâV ì|S­ ~îVØ:ü~ï³rq<o·Û»p–þ#ýoúåglÈ ×ÖáµÀóùü{5~þ{Šðr¹ÔMtÙ¦‰>û~mâç÷>ô›ÓéôYìW+üýui±iášK¿¹^¯ÍmÞPQþC]“Õ §lU Î7Õ âçn…­CÃ\‘ýô˜1}¤¹\8RFœýK‡Ô {kž¬uúo utM_+œ U(8ßT+ˆŸ»•· /†ëªå«è>e/¶üÞ¯O¯¦õŸxüáë7†:º–R+ìÚª”šoªÄÏÝŠ\‡á¥ºôñOþY‡\•»Ýn×þ{›ß]˜÷«æŸì¡Í›û¦ÿÆPG×H§O„V È|S­ ~îVð:œN§Ù‹·ÛmH!f¿ß÷xéÛãñ^¦LKhóàÛõzM+9¼"Ùþ!»é¿1ÎÑ5Þé¤U(,ßT+ˆŸ»•½ÇãxK‡é—ûý>W ¤m|5uZfZrå£g¯OM‘¥,;ý7Æ<º²«³·*Ë¢Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vë¤Vk¸¸kD•àŠ -cµB˜Ëý~¿\.Çãq·Ûm·Ûϳi³Ùìþ“þæt:Ýn·Çã¡Ý1*€ÔLT ®˜ë0V+„éóД`¦d³÷Y¶ßïÏçsZŽÆžk1Ž"šÍ'@¨N+§$‹>ŒÕ a2·Ûm·Ûe<ÝRV›rÛ´Xm «Í›\‹q€Ôlöx@8B}pZ9%Yôa¬Vx<ûý~¼ó.e¦§ÓI;# [aÞäZìDp€Ôlöx æÀ© œê´rJù0V+„±]¯WgÄÌnÔ ±¯EHͤf?j… ‰–|ɃᇱFu¹\þ™ŠÖF¶Â¼IoàDp€Ôlöx@­$Œà´rJ²èÃXH óf£ûýþ|>ßn·Çãñû³Ïç3ýòz½žN§ô7›ÍÆÙ‡ë—Ñq¦Á‰R³€‘†Z!HÁiå”dч±Z!Œ$¥“ͯ±HéjÊ:Û/ð~¿§Ôu»Ý:ûpý2z Ît"8@j'ÒP+ #8­œ’,ú0V+„1¤L³á49RÑ?GZ‚³aǾÖ, 5[z¤¡V€+&Ì~«2áøïû§ø=NuçÈårÉ•óþùÇÂ0£qìkÖzÿGjVtj¦V..¬ù0V+dªÃïß??olë)…Ìþ]ïYfÃ4ŽÆ±¯XëuB­Pj¶ÔHC­WL˜ý0V+d’cïßÊŸR·÷r¹Ô½c¤o|½€Ã‘†0Lãhû€µ^'Ô ¥fK4Ô pÅ„ÙcµBÆ?ðþmø)r“‡Ã¨SÜ€ë—ËÆ±¯íkþÿë„Z¡Ôl©‘†Z!®˜0ûa¬VÈÈGÝ¿_ÊÛêív[yv<‡Ã4ö5#\'Ô ¥fK4Ô pÅ„ÙcµBÆ<äþmù³’sm+Ÿ²æËår8v»ÝŸõO‰vúåétº^¯Ïçs¼uH ¯\‡Íf“~s<Ó¿ŽºvPW÷û=}oúö´~¯Òk¥õYC–Úüv»¥vH›¼ûOåj¿þéµ›² RoœÔ,iK_‡Çç`Ý«5R‹Ïçtôí¯=òçà|÷'ÓŸ/kÎGÆî+ú]\^+~ŸŽCCÇÀ’“µÂÒR³?ÞQÍgH“.¯ÃC)µÂ²S¶bû"Ö¤3Fûë õë¶4?é—]‡ËR›¤–Ùï÷ŸKK¿œ Ñ"B¾žô¯Ú!Nh1×HTüžÁ«w¬VÈ8ÇÛ¿~Öp®Å6ên»­”.—éz”w5Òë¦ ú”â–÷%ýwïϾ³ÆØûÓï 6[‘Ö*]Uÿ\ëë¤ ô\¬ñvÖ+$«<üZJût`í;lo“§GËäÊGº~{úãáZ§}ñŽ–+“‘óöc\\Ò.xOL7äâ0y¢V¸¼Ô¬Íº¥¸¨åU,–CÜ6T§äÌ8À3eë|†íƒ$Jy8Y“NpjÏí—ê罤Fþ:ÈÚ¡®®ôÙSe?‹ƒ B¶oÒ÷èÍ4t#Qsõ K¼`… ’Õ á`û·ÇOñçZØûÓ¢e×]Ù—fÙ®´~—¹´æiýç½L½ÀYvÐ×­H×ñÑHËÛu–R+̸’½_šðZœ³–IÇÅ»ÚõþöôÁ!‘aË}ÑæŒ^Љ0o_1Þñ0üâ0y¢V¸¼Ô¬ùB“V²ýæïxf¤ø?ãÇË«ÆLÙºŸcûP‰y®NܤcŸÚ3Fû%…ú_/Çã±ë6Ö5r:r:éY„„ìq…}Wµæ„\ÄHÔ,=Ãr/X¡‚dµBriÿöþ)û\ ØÕ¤‹ãð^4Þ™ÏØE®θƒ¶"í¯Þq]Ë•Ya­°wÐíZÜ# ÌTÔe|A¢åöç΂N„yûРޱ.°œ$D­py©YÃ…æ|>÷¾T5 ÏXP˜&ÚŸþ¢3eë|F‹íG-¾ÌµÀé›t¼S{Þh¿°P¿ae®×k®Fî}—~®r¡AÈŸ`µÂ1F¢æê}Á $?!ëaöïÀŸ¡îÂ×ff)¥î7½(#xÔ çÝAu[‘åâû5Yg­ðuU× -Ôµ¸÷yY‚ŠŒßÞ¯»nØ„®7‚.èD˜·¯˜òhëá31µÂå¥fu—¿,C©G•ë¬ÆLÙzŸ¡bûQ‹/s-p–&éÔž7Ú//Ô¯kŸá[úž9'KlH‘Î d®ž$òHÔ\=ÃÒ/Xqºã'd=ÆþÍò³ôvhè âÌ{œ÷Ý;fï½ôZáì;¨r YF-^·-MyÁš8oš¸JçZÜuR”¼AEöȰÇbÝ&¤S¯ëmu :æí+¦<ź@ødL­py©YåŠ ¹ÿÏÐß”Qå k…1S¶!ÁgœØ~¼ókÆÎÕ¤cœÚóFûE†úuÝøð-J;ë'ß#W¯¥„,¬V˜k$j®ž¡€ VœnGK’ïû7ãÏ¢›¢áÒÓãV14_£ÓJžÏçÛíö{US*}½^S÷Ût¤ê´uýV#ý&ý¾Í´ Í ‚ì Nñ×úüY™æÚMó¼O‹®¦Iqøí?¿ÿ85~úÍ×ÝôOÇI±‚\‹›ïQLCÚð?‘|ú߯¯änùíÍ“t¥xõÏ!úZáôËæP6ýÁðs¿_2² aÞ¾¢G˜ºÐ×úyqiù‚x!5S+\^jÖé2š¶¥ëe4]Ú&‹*×V+ ›² >ƒÄö£ž_s-pÆ&ìþØi¢ýRCýöíœúÔª¿owyå¶ KCÚÛciéï‹„LKû¥Ã#lmb¹\§LÝÍŠíÕòîÍÞÓ-«V8K_Qw<ÌrqÖ›ù T+ ›š5\FÓšôxbÝT C O ÍÈF]rü”m`ð9cl?Áù5ËçmÒ‘j…ÓGûe‡ú ÏKvªzÔuPy—Ö¾» >Ùûõ‹1/y³DMß3sÁ Õí¨–²øþ 5jפ«ök~é?³…=¤ßõ\o•Oâ´éQ+WcÈCŠ©ÙAu[Ñã©Ï—ÊÛ„ήvô «ÊÛÛÚOëa*ïI©kshuå­ò¶ÉöQb®»7—u"„ê+*?Þ;üQ+úw~bÍ}35«»Ðôþý©–‘² >gŒí'8¿fYà¼M:Æ©=K´_v¨Ÿ±ë̸´ãñXÀ d×ׯ¿äÍ>5}ÏPÌ+N·£VXÐþUàû7ÚÛëžpo/…=i!)·ÍÒÑU&C"«º{t›ïSªËa‡ÜÁ[F­0È£Y*Ï…ö›Vv­°òªöS_FØ„¹Öáz½Ÿ¥Í)ÓòN×\wo.ëDˆÓWÔ¹€Šu¾Ý‡ŸpïIŒ–št¡©my%U+,#e|ÿÄÉôçmÒOíŒÑ~ñ¡~Æ•©,¨õ,Í dä/ÔHTïžAž½ÛQ+,hÿ*ð…«þÔ¿«‡ÍfózË@Þ¾wà;=îÊ·¼L÷^`4F³TÎî¡áB6û\÷ËmÆ·8¶? [Þ\—÷κ¥œqúŠÊã¡÷”DR`XÇê'\­0Zj6Ò…¦nж Vf%µÂø)ÛðàS­0ûÕ ‡GûŇúW¦²Ô{NÅ!¥ fåQWÒ d#Q½{ {öÃX­° ý«À±Vøó¿‰Žóžh)Ïíq÷Ôçj´/ÖÔ©¼¹«9`«l‰vµÂ ;hŒf©›~¼¼ÑƒuÆ™CžÅËþ½oõòŽû±÷Åj…½ûŠºLgàñ Öúv~"Ö C¥fã]h*7°ÍجZáÊS¶ÙcûPIVÞÎÛ¤¡j…ý¢ý5„úc× gYZäAÈ/L,i §˜q€b.XqµÂ‚ö¯_ÐZá+°Éxëï´´ýƒ•à½' oîÏîÏ2¿”Ët¿ÙAÑ®û‹=XgœY9EüÀûôúTÃSGØz"é+*'¤~<ˆu¾Ý‡Ÿ µÂ ©Ù¨šÊg.Ú¼ôG­på)Û¼±}´$+ïçmÒj…kõ3®LÞa·ÞK3ÿÀ+` ˜ Vœ@­° ý«À·VøNKÓe±²2ûMË7TÞ{3ðÙÿº¾¥arƒÊÕhÿòÜE\¦û-0È ˜Xý,¼V˜NüÛÒéúÏîš{ƒÅ™•CRy§ßüT9Ê7pÒ—Ê™R&a[艤¯éxë}»?qk…R³Q/4•yMðÚ¢ýâS¶cûhÃày8o“†ªö[àBý¼+ai!ç=ð7ÕoÅ\°ât;j…d:º–Q­ âñx¤¾:]›rM€ÓæÙùÊÑïéCšÞƒð ºL÷[`0±úYZ­ð~¿§¦V¸˜ÔlÔ MïyÚÕ ¥l3Æö“‚εÀ›´ÔZaa¡~yµBƒÓìbF¢z/°Œ VœnG­¬Ç˜BaÇã•¢î÷ûNg_s§ù2Ýu®žà™©ZáÚFÒ ;Æî[V‚Sù ‰:)–KýÛÀh­2 øvûw'ÜiV“Éö…Za×O ÏĺÀ¢’1µÂŤfc_hf‰ÿÕ KªNÛO6 :×glÒj…kõÕ Õ W>Õ{e\°ât;j…ä>Ì 3{=EÞæòæ)òþ™Ö"ÒáP¸ ;(fbvô …ãÀ‹Kpê&Šo­¥Ï^.—N¡Ú¨m¸&ò‰¹ Üæ#dbj…‹IÍb^¿Ô ¥l3Æö“‚θÀ¹š´ÔZaa¡~yµBƒã­R‘#QCXÀ+N·£VÈGšBá(Ç×ÛY:7—éà8µÂÅÜï÷×ÓP×âÊWt’ⴖϪª®mà[Ÿ¥V¸˜Ô,æõK­PÊ6cl?Ù(è¼ œ¥IÕ ê«„\ùHÔÀ.ý‚§ÛQ+dœƒM¡p,ͯmmx¹³Ëtð œZá²F:…g›Íf·Û½&:HÎçóí"7N?i£†O•ßæUãj…j…k@øÖg©.&5‹yýR+”²ÍÛO6 :û§oRµÂE„új…!W>5|‹¾`ÅévÔ íxS(KÃTÌ sÝ9aÕ ¦þ¨6z>Ÿ_#×<-ßÅYR­ð=t6pNŒívÛ|7¾Z¡ZáÚ¾õYj…‹IÍb^¿Ô ¥l3Æöŧ´36©Zá"Îè•Ô BùlÙ#Q¹¸Ð VœnG­19…±Ôõ{é÷.ÓºËK¬¢œN§†·zLu^^­ðåñx¤Öø:CWC„6׆«–Ñ…ðźÀ¢²/µÂŤf1“#µB)ÛŒ±}ñ)íŒMZj­°°P_­Ð äÊG¢ò.pq¬8ÝŽZ!#u …£h˜î¦S0ýTÌc¼ÈÝ^îpô ® wpMÿºù¥\‹ÓÁ|½^SpÛ鯆ù*o¨kyûÜ×U­Œ½gßj…]Çiß“«3$ÐØg©.&5õB“.³.W[+,)e›1¶/>¥±I ¨®!Ô_I­0È ä3èâG¢Æ;qÁŠÓí¨2þ§P8JdÞõ)¸ê*`ŒWö@w‰U¨Ñƒºá 6ÓL-«qÆîÄRK¦Fû:‡F]Ð;Þ)3×ÛB‡Ñ"w¡)#(ià[Ÿ¥V¸˜ÔlÔ ZáxK.>e›1¶/>¥±IK­ê—W+4™÷³ÅDMv>Ƽ`ÅévÔ ™äØS(œîŒ®ûûÊ'¯§¿M"`Œädżî‡=HE¿‡Î×8“IzÃÔçó¹ýލûãNÒBúEàj…3öÇãqŒ.T¬ ,*=P+\Lj6ê…¦r޲à-¥VX|Ê6cl_|J;c“P+\C¨_^­0ò äk…ÅDÍr>ƹ`… _Õ ™äðS(œ9!­ÌS—8ñjŒñ‚\à‚ì ˜×ýP£Ûíöó#—Ë¥¼Æ™XÝmru·ÁWž2Cî©kÂ'a[è0Z¾¢²ÂÛæŠ•œ_À:ÒµÂŤf£^h*G½Ú«j…R¶cûâSÚ›´€ZáBýòj…!ó~¶ø‘¨ÏǬPá«Z!S …£ŸÑ o_­œ‹fúGª+c¼áÑBö Ü·žõ[™ ;(æu?ÔèÁ³î«¾tºS®ò”x[ÝKåLmîaµB˜ò\ïV™ºæë·ò.Ó$¥·S¶Oe´0p€!ÝQ囎‡´Iï• ²ƒ^÷{Ï 9YªeÒY¼ºYå{ïŽôÁ!73Œ½/BûŠº[û]ŽSÃV6¸X@j–%5ã2Z÷ôDËÐhÆ‚ÂxANö%—²¶†q2ýy›´ŒZañ¡~‘µÂȃï16©Za1A²Z!Lv®m·Û, N›Ë\Ë+]]84pê†ßñÛ×)nꢅÞmU9ýxûî¨ò…ËýîO{>Ÿ•w» L“'ÞA¯û•Y^Úﳕ+3dÕ¼Ϥã6K_W™6¿Nºò”éýêÊÛ¾7{_„:ÔWt=CSÂUד‹u¤fS³ì—Ñʧ¨Úß“9cAa¼ gŒ%œ²ÍÛç'ÓŸ·I˨êY+ Òaf¿Çx–1œâG¢zÏN\Æ+N·£VcŸk©oɘ–6D5m.suׂáWê<¿Cñ~ÑBË„èÏ—Ö¥çí»£\µË†;ÓÚ¯Lôlü¿rf§,oî±V•ç`¿"³Ãà8w·¦–xW[e:ß|ÏaÝ)Óã.ÁºÞµý©7ö¾u"Äì+êž§xÝrüõùŽ” 4ˆu¤fS³¼—Ѻ•i¿½3Æ rÆXrÁ)Ûì±}^q2ýy›´˜ZaÙ¡~©µÂ få ôÆÉ2™qÞ’F¢†¯ ™þ¼MZR­°àP¿àZaAȆ«öă½?[öHÔðZáÒ/XÑ‚dã'0ñ¹–"œ|¦,]ìþ\˜Rg•~™þ©ÍíO_£ÜÊËë×ÕK×ß׺ý¹Ä§ßœÏçá¼7¯Ã+mÿs?XZŸ6SFô莚W&í…ô¿ùÕ Ïìì#ì hãÿ ՜ϣåÕéŠÿÚGÙתáv²¼}ÞÍø:£G:fbÖ +;ºÊ^®ùXmyWXóSÆiÒüY×iî`»¾Ëf‚}êDˆÙWüÔߨU]Ö ÎšåJÍÚÄü¯0æ÷b_«”®SÍëÓu¢Åy #9ã-¹È”-HlŸQLÞ&-©VXp¨_p­0H‡Y÷\Þôƒ½?[öHT®Zár/XaƒdµBXĹֻPøõ3Y?Ðò>½60¼;ê4¨»/nEËÚÄ–=¶·÷Z5Œ9L|¯Ñi2\§ðïdaÞŒl'ÂOÈZá+³ksçgCªõJ(ĺR³QS³ñV¦Ç›óF rF]ry)[œØ>£™þ¼MZX­°ÔP¿ìZaAÈìƒÓgÐDe¯.ñ‚Ud,ɲÑt™ëW(ž4åê†_©_0¼;Ê2—ÅëÁÿ\}ã¼;(`FÐéV¨±K$ óèvr<À¥Ö {Äf#ÿ…ÂÉöE¨á'j­ðçÛ{ç›{ò÷µU¬ 555 5Æ5{AaŒ gì%–²…Šís‰éÏÛ¤åÕ ‹ õ‹¯„Þ¼î š+ƒ.x$*N­p%…µB˜0M˨“¨§KCËX4{<oJyé:¥óáý÷ ¹žC-¼_x‘±oœqÌzgy#­UÚ;CNó÷ÝŒ?Á*5=d VëÝ¿5OF:vd8;ˆv"„­¾Òö½èçËÐźR³QS³1Ö§ëÔ£¹®z1£ý±—\XÊ-¶ÏeöLÞ&-²VX^¨¿†Za„AÈÞUæß· Ì›A—:Õoå]°fïvÔ a<¯×açÍLS7ØõZ_¥žpxïúš»ß|Î-_°û[º¸ÿþ®ŒÏ¡ôˆ[þì‘ì}ã,;(ì݃=vÐxk•ånÆŸå× _Gé{à=ù†zgm^ka_„:‚× ß—ã×kþ\‘SǘZòõf‡Ê¯r¼Hx 5Ë•šÕݶÔ/øOWº!qT„‚Bö g‚%—”²ŒíçŸ3fú36iÁµÂ’Bý•Ô —;™Ž¥÷m³gÐEŽDõ^`y¬y»µB˜&3íú2ÙÊÔoÔÛn·[Gü^WçßWÌÞMÔòÛS;|Þ+›·;jŸžÿ¹ÎŽš²M¼ƒ"g]ÇOÆ^«öîk@)ï1êZüºu¿G_7$ί;_:­FeÇ2ûY³ˆaµÂŒ+ÓõÙy¤fý®zbª”&ÌÉÄŒö'[r)[ØØ~úøy¤L–&-¾VXF¨¿ªZáÊ!sµga#QÃïä/ì‚5W·£VÓ'§©?O‘gº*íþSy*m·ÛôOéoÒ_Ïûº^¯ÓEäµzŸ×׊¥Ž4uÂÙûÒwßþç~°WS¤ª»ÇcŒî(í¦´™vÐï;Óæ:„fÜA¡¤à0ŸðnÔJSž;ïõùL7Ò!Vfx4»,¿ÔÏ{ø_§Rú×Q[&õïùÏqòõ.S'•/ÔHMªe¤f£ŽÒ|æG•ÁÌ×I´?cøT^Ê!¶Ï%H¦_R“ õYh‡Y÷Pêû ¿‹6¥wÍ…ª¥öoš€ÞRnøu**Åõ ˆBÕ Y6üQ9)ûºÅõÊ‹BÕ Y6¸²àZ ûXg7¨Vóà·óùüyY9ZY €P ¼(T­eÀÛý~¯¼¬Ün7€,@¨P^ªVȲ(éŠp<{¿m¤nô`·Ûi[d1B}€"£PµB@– @yW„ív{:Ú$<ŸÏô÷u!®;ÅõJBÕ Y6_6›Íáp8N·ÛíÏxÂóùL¿<ŸÏûý¾!¾=Y €P Ô(T­e â­³ÝnŸÏ§†@ ÔЪ²lVñ=@ ÔЪ²lVñ£Èb„úºSµB@– Àª"ÞÍfs»Ý4&²¡>€îT­e° ×ëõp8l·Û~¡ì~¿OKÐŒÈb„úë‰BÕ Y6åy>Ÿ·Ûí|>ŸN§Ý*o*N¿ßï÷éoÜ] €,@¨°Î(T­ЧVë¤Vë¤Vë¤Vë¤Vë¤Vë¤V‘Ýn·Óét8v»Ýf³ùsf¥ß¤ßï÷ûô7—Ëå~¿k1 =µBˆæù|^.—ý~ßïtKLO Ñ’ùR«Yì Z<ð–Rªìßøx<Œœ –«”Ròx8rwûýþz½jU0°€=´‰~Oá’ýÇ£‘$ÀÃcµB˜ÆóùÌX%ü“äžN'‚ífíCò>Z˜22#'BS€å†Çj…0¶ëõ:Áix845XÐnöÐf0$§ÓÉȉÐ`¹á±Z!Œj¤Ç ‰``A»Ù#ÀÁÛí6å×Ù)Bz€Èá± Œáù|îv»6·³Çëõú™¨Þþs:öû½3 ,h7{È8’òµ,ßu¹\äkBS€¥‡Çj…0†ívûuÖÐûýÞ~×ëµù)Em´›=ü´ Éòháf³1r"4( T(Î ~x¬VÈxÇ௟µ8ŸÏ çËétþ•CG°ôE³@„\«€,®nj—ì~Î'Swã¨#€Pá±Z!s$¯k)Þï÷†“år¹dü®Çãñû•ˆ8`é!Šf€ÞéVðÅFˆ4RõùËý~ßû[n·[Ëoç-ñ÷çü?oR}lÓ$¤K¯ð&ý~¹9μa[Á ôÕ ™)a-¼VX7ÀÞoB›i¤dv¿ß·?ÙÓ¯{¶éFRzRù&‘ÊÉ]¿fÁ)ªÛ;Ÿ;kHfTê¦eïù.°ÍÇSÓ¥<·îQß¼SgoŸW ßðòÓʇ—;åõ¯â`§Óÿó»µ'‹=ƈU¦ï-£ç,.ûšìã  iÎÝ:EËŸ!}Zæ¨ÑH1­ä€¤ÓV¼K`•¨NAWËtéµðÔ&¯V†jÓ„ô‹ÛæjX rx¬VÈ©jáåÂÊÛJ#?T˜V¸e©2q¯ –r¨ãñØi}R•2Óº„¨S6ýòN‹lÚHcc× SrÝ£òU×Ô·Ïù|rEN§g›»‘3Æíª…Ö çê-£ç,1õƒK ùK¹É3öÍy_öø9E\-ËL•·0õ{ưåV´Y·Qƒ®ôíu“üLÒÛæmX rx¬VÈyjáµÂºáý˜o*l˜.uÞ‚Úõzí½>2©”yf*{MmÑ›¶ Záívë=ŠòÙÔ·OÊâ{Ôëòú)ÖPK¬ÎØ[F;ÎXh–ý#‹ ùý•Ó–5µÏÒÏï‡ óF#i•zß¶40øºíc’i‚®€µÂÙöÙˆ ˜ï ^ó¦-¢V˜åvë×#roÎÏ8·ûN|í –U+œ½·Œvœ°è|¬÷Ÿ– ýù›~~N)ó9¥CÌ{ɺÞ4X·i!]Ÿ•kø–®¯iXJ­pö°-HÑÃcAÓ&¦å— ëõú: àĚ߃–r™ôÞ®˜þ7ý²9Íéúìdû¾%e7×ëõw½õµ> ™iêöXZ›÷¾­aÓQ+¬Ë¬ÿÕ©•Ò¶7—º¾ctàæ|zíßß;i+ÒJžN§†£¢Ç:§æJ˼ýç÷?ŸÏô›´iMšÇ…š›nAµÂ½e´ã€¥geþ ÔÁ?ÓïÑÂÏpèóy½áÑHsˆ¸Ýn_Wÿß•ÍWа¥jÿœZÝÉ#·æ¥¥Í|E§6ó ·y/Ã4!}À°-NÑÃcµB¦ÍJ˯Ö*‡zp£!ÙÜív_Ÿl~úðÛS»NÓ鑟ÞKëºKÝ´%Ö S’Û|T7¼0Ëó_-?[9‡U§ÒRZBe]¸å:¿ : o]éôÈêxÅxÕÛ){ËhÇ9$f_ÿ©øÁ–ÖPMû,ÁTÞ :0I+Pwoü4ÞøÔfžÕìJ]ƒ®´ù- d×뵡¶5M-l Õ°@äðX­°ôÐOê&÷hŸ…M .ïèôœKݘiá¹ú¥Ô˜-ïtms»cZáKëôzˆR7mYµÂ”J·Ü´ôgu5¯áû½ågëöN›×ÿ}Ž\ýÞœ¯ëœÈ/åü:~5°6åØ`ðÞ2Úq€Äl‰Y÷x ò’Ýǵy¨px4R—`vzá`CÁ¨e Ði®õ?ó`üüwƒYZá×½‹‚®w4=°µÂéöP DIXÔ ¥¤~ÆOIë!é:ƒåxêf éúvûŸú;TÛ/ªáÇN•‹º´%ïÒ:5Q©›¶”Zaʬ»ÎÏS7ð4s´Ö}{Bá{xá=Þ5v¯Rw\µ/¨Å¬Æé-£çHÌŠ¬VNòP÷.‰télc ‰F²$&/uå–7µ¶œç¤_àZtõžú `­p–°-ZÃQµB)©Ÿùj…qÞUywbï)R+Ÿ„jÿrÆ7[~]“¼K;»Ü6mµÂ~-Swvtz(¸÷æTv^"S7(ÔéQßÞ*ïxo߱ĬÆé-£çHÌŠ¬þÔ8é4¡ý)ÆÓ“h DIXÔ ¥¤~ÆOIëN6/’˜@åí¯CfŸ«{ÃZË{3v#•™oïìÐ{É¡zËhÇ9³Rk…uÙŸG ?ㄆG½z_Ç+#‡÷’õ~P1ïãr_ƒ®Þ“dΞbÄ Û6,%aQ+”’úY}­pŒg‡¼â|ì‚ÚŒK+uÓÊ®εßËx\aµÂP½e´ã‰YÁµÂÊëõïûô*ÿ ¡dÖû²û9äð{{*Ÿ5kS?)x¨»ßl`ÐUL­°wسa€( ‹Z¡”ÔϺk…ß•kŸÆ¶¼å5c[ )Yޱ´R7M­0ûúÔM@ZF˜1û°C¿%Gë-Õ $f³)ãæG ?ÿµ¹ÖÖï²[´÷~SasÐf:‹‘‚‡ÊY߇]j…1ˆ’°¨JIý¬»VXù¢ºSê½TNÓæ•ëyÛ*ÔÒJÝ´²k…³¬OåÌB-gñÌóù¼ý'­íé?»ÿÙn·Ã£‚hµÂh½e´ã‰Yٵ†VþSó³Zý.»•fy™u¿·Œ<ŒtS+ŒÍŠ! „E­%åÄ¥hÖ­Í«7ú 2•Záz’ß’Ö§òÄlSjÕý~?ŸÏiÝšK¹¢‚hµÂh½¥Z!Ùó´¯ÿTü`HóGv»]e©îó¶Ÿ‘®ã•÷ÍŸoÿð¸Š9ºç ¬0=õƒK ióÁËå2ð¡ÂµÂ.V­P …ç j…L—‰®·\X7SM›—÷…Í‘—’¨®'ù-i}溘>ŸÏÊ·òý¹Éár¹´|y¢Zá‚!ÀÚÒ³É>¾ Á–Ÿ­‹—Z>T3‰<¨.ëøC@!)ƒZ!Ó%£ë­>ŸÏº³ãñx,7G^D £V¸žä·¤õ™ëbZ÷ôëÞ†Ëå’z³åW1GWâ$B8€Uåf³,$þ`HËÏÖ=ZØþ}ôj…]Û5 •bD®ÎÞ°@äðX­’ѵ— ëÞjq>Ÿç]±Ê{b[>1Ôìñxô{”R­pqKS+ÌþññNÌ w5‡~Ã%Õ £õ–j…ôÎÍf_TØÁ!iT§Ic2Özf¼ût¤àa·Û ©ÃL1‚„m1ˆ«2B>ºöZáñxŒ9 éHùÂOÍÌ«m¦åQ+\ÜÒÔ ³|¼³AÝòCÞ¬ZR­0Zo©V@¿Ä,Ô£ †´ÿøçl )”Zn4*x¨ÜÌëõ:ïÚ–Z+œ½a€Èá±Z!#dk¯Þï÷ºdÆä.9#=í˜Ò¯è V¸¸¥©fÿø~¿Ÿþ1äÊÞ`àý %Õ £õ–j…ôHÌb.3Î`Hûÿ™¡kÈÔïÛ+CÄÓé´Ð6¬SyŸíðÍT+ŒÙ°@äðX­Üé£ZáÿS9]ËGíÆSùz²!½UŽ«·ÉDÔ ·4µÂìïÄlP9Ur§;äƒW—­·T+ kb|±C‚{e4²ßï kÃÊÛ¨†gÍj…1ˆ«’5wT+lÊìf´°rî»,3£VÖFÛl©Záâ–VùÙ!ïqS+¬<1ǾžŽñœ’j…ÑzKµB:%fKYò¼ƒ!Á¿½.Dì÷^é°mX7'ÏÀÍT+ŒÙ°@äðX­Æðgšš?£Ísåw?#zÒés µÂ¹’ß!pµÂºîbøûD&NóKªFë-Õ `¹¾¼ÑÈ\ӎ׆•wRÍ8ßÅO)Ó°lX rx¬V#ix´ðx<Ž÷½¯±èº­|íÅÀ‰õ*§Ôk9=ŽZáâ––}îJµÂºsÔ)‹Q+øœãÀµ Õ[ªÀr/|y£‘¹-¯ ³¿G»îN¼ÃÚYZ>`ÑÃcµBIã…Ãoç«“Û|&¾ÿ ט|úàG¢Ô ·´Ýn—·ü­VØpbŽ7eq´9H{Ïc<ÞZ…ê-Õ `¹¾Þßž‚„éo=¾ ë&^è÷e +ËÖ 6,9ºäbµÂ€ DÕ aTuÁyÞ;B?Gé;ýqï¹õêR†ö©‡Záâ–Vw{s×GÀnL]g­°nÖâÞ•©çóù^fË“·ßw=º ²Ú7]ê óN‘”e‡Æé-Õ `¹¾1¢‘áåÂæXqâ6L©AÝf¦•üzïÜý~o.f­¶V­a€Èá±Z!Œ*…ß ‘d»Ý™g/eˆ•ËoþTÝ*u¯Ë: ï«.niu“ë¶®áqÂ5× úŠÔKt}ºðOÏðùu5ßNƒN¿‡˜îʺq°á÷ÌÜ¡AzKµBXî…oà·7Ü}Úo2ÉÏ.BVÞ9öû&Û¾þž þ~¿§ß¤ßW¾N]­0fÑÃcµB[Ýkþ<ÎÓòïe¦è½÷cY ·¶©]¦?hÈXӺ͕¶«N³´ºÂÇ×’VJB›sÕ5× ›OÌ–U³Ôþ•÷Tþq]ÒfЩM•°Ó¶×½Ë¯k—’}‡é-Õ `¹¾ßÞ&½n=joUŠL*#ùm˜ÂËöÅ©±G«Jª†jX rx,H€ ´)¾ŸUI¹^JâþÔ\ÒÒ/O§SÃ\ÎÄæw)¦l"ýÁŸaðô¿é—͉F×÷‹©.qiÍ%­ãñøç~9-Ý5× ¾Ý÷»Ùl>oýMÿýµs¨ü®†)­*¿(íÓQweCM-uŒÍ[=ê¡·T+€å^ø†{søý'‘üýÁWÈ”¢Žáï››  ÛgÍÍêni›f— Ûâ4,9Þˆõ{²úµÂ¨ DÕ a—Ë%㔤Çãñ]yé!}¶wÊ™>ø'}˜1ñQ+œxiÏç³Ç‘óç¹WµÂ†± Ï²¥ÿygMÝ~~q®mïwPMv„ÌÕ[ªÀr/|Ù¿ýz½¿ûôõ~êöÁÉ,m˜Vïõ²ì?¹sZùY½Þ¯ý;mXÛ´„ivÙ"¶¹ˆ«ÂŒî÷{ŠÃû Ó§^1|®•¹ÝnêéÛ”!¦L|Ô gYÚåri9Xñ§´°®OJäÓ™Þi8(u)÷¯Ìî³ :½&:ξ+ûT× çê-Õ `¹¾‘¾=E=î+{•{Ühº¬à¡÷ã“Ã7¶ì°m`ÑÃcµBˆàñx\¯×Óé´ßïS°ý9HþºÁ/ýkú›”v­´—–œÖ$埫ñõ&CÖì}ØÔ3šh ûý~>Ÿ‡Ãçsmïv¾\.ÏÍô-©“ùüŠWÿ“V`È#̽WæO_´Ýn_Û›Ögø zK`ÑR8ôŽ?Éwà”Å!Â,«A*§âq¨hXàE­€RU¾øûÏ{а°fj…”ªrjÖ)'Çа@pj…”ÊP•†z\ÖÕ Xºóùü9Nu8´Œ†ÞÔ (Ïý~¯§ºÝnGÃoj…DóÏ?ÿÇÞï¿««gív; «a€?áZ!¡¼‡•¶Ûíétj_Ûz>Ÿéïë©<û¦a€ºð@­€ >Ç—6›Íáp8N·ÛíO…ëù|¦_žÏçý~ß0Bu<5¬†¾†j…Ì+ûðÔv»}>ŸVÃc‡j… ¤ž¥a€%†j… —q`êp8¨giX`‚ð@­€,² Im6›Ûí¦15,0vx V@F×ëõp8l·Û~ƒQûý>-A3jXà+µBÂz>Ÿ·Ûí|>ŸN§Ý*sK¿ßï÷éo<ï¦a€NÔ `Ô `Ô `Ô `Ô `Ô `Ô &îc5 A¨LÜÇj‚P+„EŸ\ξ%6€ T+`Ñ'—³o‰‡Í Õ XôÉUÀÙWv§jëÆ[=?ÀB©VÀ¢O.µB[aeôü ¥Z‹>¹Ô m]„•Ñó,”j,úäR+´uVFϰPª°ˆ³ÒöÚºÈ+£çX(µBXÄYi{m¦ ;µB²û÷Ÿß?3þqag¥íµuš€ìÔ ÉîwEïkQ¯ß_ªÚ^[§©N­ìþõšëz=þL­ÐöÚ:M @j…d÷Y×k(íuýµBÛkë45¹¨2†öÕ½N°†BáZ¡Z¡¦`*j…Œ¤e¯ý¿®¤Pø  ó|>¯×ëñxÜív›Íæ÷j¤ÿM¿Lÿ”þ ýÙ‚¶7­íív;N¯íJ*¿÷õOéÏÒ>öfØ•é·/—Ëápø³û¶Ûmúeú§NÇdÚ¿çóy¿ß.-ý2ýS– y*7çµïã0׉PI­ñ´©ôµü§õ f-èÜn·ý~ßþÔNœ>2o·Ó¼9§Ó©Ó}–lºV âìÍ)›zÈf¶ùìãñ8mV2íñ¯û+íÓºbñéK³W _Ïthµoù´=N4€6Ô Õ×z_›ß¯ªPø3Suéñx´¬žT>‹×©ž2o«ŸËå² ½¤©³4Ñóù<Ös³ÙÜï÷ÊïºÝnŠt÷þ§´¨?Ïêv*Í{ÆÈN­±5Wý¾þrm…Ÿ9ªK—ËeÊjÚk…½+5j…Cšèz½ö^Û?å´ï†á)W€Oj…L£®Øð¿ë,þL[]ÊX(l_.\n­°}=t–½­©‡¬d™B+gÇMË?NY6ÿµ´ò =]ä¥VÈd*ë€_œ•ãDçó¹¹2’þàv»ýþHúßôËæ©ÓLÙítý¢ý~:nÿùýÇÏç3ýæz½Çæ7Êýù`½°©³¯dÚ5iý~9æë€lØ_ Çj¥¥¿ïº š …Ûíöu–ý~^5­Ò×ã0ý“wY¨2%…ÂÞgeöoi(aìv»ß”Jéª0zo{WdN§S§µºÝnu•šNÏ—…ê'hê‘j…_gÝìô„lï¥í÷ûNÍò|>ëls–ý4ôÓ!íš §VX¶~òù™·v9ÍITWÂøúTàou³;¦…GØÞ׳c½gkl(ô´_¦ZáÀËPÚƒ- Ûï÷_/F鈸´NOó¥•þÂÁ†²¾G €áÔ Ë¦º§VXér¹ /a¼Ô=÷Ô~Q‘;Çã1°¢ªVØû2´Ýn;ÕyëvVÞ¥µ?°‡/á­®\èÑB`8µÂ²©î©Vªœ]³ëüŠo•O`¥¯ˆ³½CT>Ö¾­Ô û}öp8ôxh®îaÀŒKKÇCËWNdÚé¹Ýß*Ÿám–ÔQ+,›êžZá§ëõšw>ÃôÁʦ/а½Ýn·!“¬ªN¹V•µNï—̵´Ê‡ {¯IÃYÖ{Š]€µÂ²©î©~ø´T¥!ßï4êj4AöfC+HUv{?|7¤L\yF¤iä¼*¼¨–MuO­°åòÇeÖ½š-ÂöλGÔ §\«ÊêÞ,Kûœæ·ý³¨u*_3z8\é€!Ô Ë¦º§VøÇÀ5T¾±ÍƒTj…‹ØE4ÑšuÆ¥U~pø€•'ïyM~Ô K§º§VøGå+ØN@úR9ébúºy·÷«çóyûÏù|>ýg÷?Ûívx·¦V8ñZEXZå€' ­[ŸÞ3¬¼¨2¥˜µ¹Eœ•¹^ùʳËå2|ɽ'Hœ¸Ó¸ßïçó9­Xs)0W·¦V8ñZEXZeÝ<~笓Z!“‰ü(_ü³2×Âw»ÝHO<õž qšNã~¿‡Ê‰RÕ ç]ryµÂʳ̈ÉH&Ó¨«6üïjË…£žD•ŲÇã1|É•ïhk3AâØÆív£vaoÆ9´Ô S+ÄH&h¨6ÿfåÂQO¢€ o•žÏçxU› gï«Nau…²0’ÉØšk_¹Âr¡Za–Uºßïº5µÂ‰×J­ #™ŒêkÕ¯Íï×V.T+¾J …›Íf·Ûí÷ûÓÎçó톯¡ZáÄk¥VЉ‘LÆÓ¦Þ×òŸVU.T+¸JÏç³òµŒ¿‡Ëår¿ßÇn4µÂ‰×*l­ÐˆI­‘´¬ôµÿ×õ” Õ ®Òétjx„ðr¹<ŸÏÉM­pâµR+èD­1´¯ñuúƒ•” G=‰*¸kùx]³ÇãQY››x{ŸÏgó„]«„Ã×P­pâµ [+L'ˆKZ!Ùuªîuý›5” G=‰v»ÝçÂß/æ"-äsÉéë&ÞÞËåRW(œe¨N¼V–6ÞYZ!Ùuªëõø3µÂ!‡ÃçÂÏçóð%§…ô«ÐåÝÞÊ lóxãHk¨V8ñZEXÚ~¿ÿüÔétruR+$»NE½~©VØ[å»ü†v÷Rù&Ä6ó.NPÜøª8µÂ5Q„¥UžeI¿×eŒJ­qV޽üûý>d™éã½W;r©hø2Õ }ä=ªMC ¤V‹8+3.¿òej§!­œ€´å¤‹yŸT+œ¬©c6Q¥Uže-R+„Eœ•—¹\òÖŒÒ+x½^Û|¼÷ä¥í[Ϥc4uÌ& ²´tðW~öx<êâ€PÔ ageÞ¯¨¬ív»~KKòÄÊŸÏçŒ+s¹\z·U]i5ÎÞØ8½›:×fY+¬;Ë¿Ýï÷ív«ÃR+„Eœ•y¿¢®þÕc&ÒÊÙG;DŽÇãRc›õéW}<u3I.´V˜·©smf©µÂº³lx¹ðù|žN'—W µBXÄY™ý[êzêT.¬+v*?Õ•TúÕSêæ~ì´´ßµ˜’j…y›:×f–Z+ü©yó%`=ÖçóÈÔa©Â"ÎÊìßr»ÝêNØívûõvéê ÷û½ýšÔ½î°ërÞêÊ mª3mª„Ë­foê,›Yp­°¡Á_%õöUÚtÆU–æu˜À@j…°ˆ³rŒ/:ŸÏ §ív»Mð§h˜þ7ý2ýSÃ{¼ÿ®¡ìx8®×ëãñxÿqúï´§Óé5AèçÒæ~Ül6ÇãñÏŸÏçk»f-£V˜½©³lfÁµÂŸÆ¢üï–OíŸsíÕøé°¬{z×åÈB­qVŽô]Íeˆz¼ñð§]=¥S÷ÒP몮0poÎÒÔÃ7³ìZáOcñÚå˜Z!,â¬ïë2– û _:=Ó÷µežÏgÝL¤Ç´¨’j…Ù›zøf_+üV¢uyF¥V‹8+GýÆæÉH[jÿæµJÏç³yjÓ®ÝËãñè·À÷l¥ï9! «foêµÂÒ™ñq×—´@&0Z!,â¬ûKï÷{ïBFúàï—Üõö|>{¬Cóû=Cw:Òg³ì‘˜]bö¦V+léz½âõõÚÍ,'€Z!,⬜æ«o·[§âZúã÷Ãw¹\.—NÅ”¯ l_Iv>ŸW ‡ï‘È]bƦV+ìz¢õ˜û÷U"¼ßï:I #µBàçóy½^Çãn·ûSKJÿ›~™þ)ýÁgM-£ûý~:>W`»Ý¾Và|>w*S¾øùPä~¿OK[m &{SÓ^jØÔ¼‡Ãá³ýï‚Ëåâ)B`$j…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…°Nj…ðíÙ1 àùW];6hr…Ðä  É@“+€&WM®šø0 g6® endstream endobj 231 0 obj 76055 endobj 232 0 obj [/ICCBased 234 0 R] endobj 233 0 obj << /Length 235 0 R /Type /XObject /Subtype /Image /Width 2404 /Height 2394 /ColorSpace /DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÖ! ðù7½i8:I$5€'àÈcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–F Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXK#à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,¥€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÒÀcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcià±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±4ðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–F Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXK#à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,¥€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÒÀcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcià±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±€µ[Ç4 Âü»^‚„Ý­.Àcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÒÀcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcià±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±4ðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðX|@cºLN endstream endobj 234 0 obj << /Length 236 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xÚ}’OHQÇ¿³%B¬e&RðN¶Wí`ŒÝõoʶ¬k¦²Î¾ÙÞÌn%Bˆ.AÖ1ºXÑI:†‚b]"è(‚—í73»îˆÚƒ7ï3¿ÿ¿ß{@](mšz€yÃÉþ(»;>Áê7P‡A+­Xf$‘v™lqdí}…䜛áãõÿ] ‚U€Æ¬ÇמöxÀáû¶iO:¬äÒb“¸M¤’1âWÄg³>žöq†[ ñ2ñMÅ'"()Y'æ±ld4ƒä—‰»2–’'&ßÀSg^™öÐ}8õ¹&›°€åwÀ¥Öš,Ô \V:k²Ý¤;©iÝR;;\‘Œu?ÊåÝV þ°ÿ¼\þûº\ÞC9¾u¥(J•IÒÀëÃ]ýÜàBS˜s_ QP5ûFz¼Úë׋Gõ%«t{3qW°D÷0vz ¼ü \}\ø$€Ôu¡ºmþÀÍ+˜…–ÍÙ¬C–;XØ9:Y„^g±BÞ,Ú\°ACioci]g®©Å·¸(ñL;òz±Úï9ÚAnŒŽÐIó ¨Üê­°4“I÷ÐÝ x#Ã{zwA¼¨j}ƒÎ…Ðþ¤Š¾Q¥óš=˜ò8Ðmèñá Ã(Äo{1±cÚÑd5¾Ué­ÊgÒ·t¶üÆlaȱi"ßÐ\.5æ±”šËÅâ^Å8tph0èk€!‰~D† TÒhd¡‘”»6‚ØÂì±–:>f¤ß&Ÿm×çŠäíxÝA4Ž…¶ƒLþ&ÿ–·ä%ù­ük±¥ªiÄ”¦¬?ûCqÌÕ¸m¥&/¾By#¤Õ‘%iþ 'ËW©¯:ÕXl©Errð'ñ=_—Ü—)Œi7Ò¬›©äê,úF|ÙNšٮͯ6×rm^™Ü ®ÍšUáHWü «Ãÿ5;¿?ÿͰh endstream endobj 235 0 obj 9292 endobj 236 0 obj 706 endobj 218 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/cluster-fixed-psize.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 237 0 R /BBox [0 0 2401 2401] /Resources << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 238 0 R >>>> /Length 52 /Filter /FlateDecode >> stream xÚ+TT(TÐH-JN-()MÌQ(Ê ™*!œ‘œ« ï™k¨à’Ô®ØM endstream endobj 237 0 obj << /CreationDate (D:20070215232916-08'00') /ModDate (D:20070215232916-08'00') /Producer (Mac OS X 10.4.8 Quartz PDFContext) >> endobj 238 0 obj << /Length 239 0 R /Type /XObject /Subtype /Image /Width 2401 /Height 2401 /ColorSpace 240 0 R /Interpolate true /SMask 241 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÝ[uë:Ã6Ð !J!B¡B!·ÿ](„B(C(„B(ô×XoG¿Fv}%ÙšójïµV][ÖÉz|øï?Øš/ "BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"B€í]ÖÙ“çóy¹\‡ÃÇÇǯýÙÿs:Â?ȼ)Ðü£ œLŠV}Y—yÇççg’+Ä„›Í0ÊÀ¨É¤ˆ`Õ—u9wàz½¦ºBL¸)Ðü£ ŒLŠ`«ƒò¨ñÚ°dëîK‘"Mr..—Kª+Ä„›Í¨v”™p±özýéñx<ŸÏ÷ûÝÙ Ô$"€õÊ"B ÎîN±)Òùçâ~¿§ºBL¸)ÐüšG™$—oÇãññx8³’\µÀZe!´Ü¥Ø7§;ù¹Øív©®n Õ[ù52 /⇃ Pß PÃäª ê”E„`á¾9Ý©ÎEÏÛ>??¯×ëóù|ýËð·Ûí|>‡è¯H¸)Toå£X òQ&ù¥\ØagYß P䇀še!X¸°oNwªs}"ãããcÂã 7…ê­| T>Ê,q5÷ùùéDë{j’ôPí ," öÍéNr.º>ê4a¹5á¦P½•òúG™….褄ú^€J†$=$làÚİ:‡Öö­µÓ=ç\œÏçT‹“ 7…ê­|”Ô?ÊŒm¹ÇãçëO½qTß °h‡&"×&€Î§»Gt•òv»•ݪ·òQ>Pÿ(3§å>ŸÏÓéÔ³ ë|}/ÀÌMD®MNwý~Ÿê—&ܪ·òQ>Pÿ(3¿å>¯Õ÷,Ñ¡‰Àµ  sÀéÎóKUWTåM5“$»ô|>»RÂðWλ¾`r‡&"×&€Î§{Ô/Ýï÷Å7…ê­|”Ô?ʤj¹÷û=º©Ëåâ¼ë{&wh"BpmèpºGýR!z3å«h&›‰¿:¾´è]£ú^€9šˆ\›äq¿ßO§S¸Hßívßûþ;üIøóð·Ã7u½^Çã¯M½VŸ‡¿]bÿÇår ×àïŸ8ùøøxÅív«¶üC±¼Êÿý%Eá‡Ãkÿ—{UQO8ŸÏ¡xú½õœ¸°'áHCQÿ:š€…‹<›GhtÑ*ú]?õi"ÂÍwו×¢-n½ÙrãÝðòÉÓ1†î+T¹ð‹^ÇýÜÏiFèÍ–˜f™áiÅçróÛÅ{ ÿûÚóTÕrÛa¨uó°HEÊV\†ŒíÐD„°ê¡¼øÆ‡ü`¸˜íúvƯkÒþµ”p¡®‡LHv»] 0ìתWôW‡¬gqæñx|~~ŽšÎ…“5| óÏ Š"líW˜ÛUÒ.ä9q›À= %?|9q-ø¥û–ä[mó<¯V¹h‘¦­cKlj-­þû¿bš÷ZÂVÕ›lq›éÍ2Œw•tŒÑZ=\øÙ$}òÒ3œz¦…¥Ž4á|l`m ÿlÚ;3·:z×õ®ÑÊ›ÌÒ`‰ÑÙR9Ðκ¢ˆV=”ßxÿÞn·!áàO§Óéý· Ó¾u'ìÿä°páY¶Â„B»06´ý³DoužPj>qÉ›ÀñxÌ<·¯³ã*U°ÙšgØŸ '««Q/q.ÖÖÓê_î÷ûŸ=pU½YÁ·Þ,ÏxWIǘäd͹/(Ï §†ia©#MeZ‰…»ÏíD„Ó¶¶®&3¡,1:[*ÚYWÀª‡òâïúÁÉ àïÑÞœ™9)áäýO˜QNÖuqòÙ]×<á÷ا-·Ì'.CXzn_gÇUª`ó4Ï±ËøINâ†#ÂJZýؕتz³‚-ní½YÎñ®’Ž1á)›ðÄP¶Nñia©#MeÂÝ}“3ta…)gXbt¶T´³®("€UåÅ7ýÁp):9Ôûu¯õü™ ˜á2Õ~‘H"áRÀ´ˆpÂÃIʭȉ[º d˜Û×Ùq•*Øu5Oa=­þõW×ë5ÉYË\[ܪ{³Ìã]%cÚîkÔ›ásÎpÊö3EŽ4¡™Ï¬%é–E„+Ræ °Äèl©hg]QD«Ê‹o|¹IÅãñHµ 3ê = A»Ýîx<Þn·Ÿß” —Þ—Ëåp8týö̯7 ûÖµ'Ñýÿö¿çB¾TûdA©·hÈó{ëì¸Jì¢Ê›«jõ_ãïi©ª7+ØâVÝ›eï*éKýöÌ3œ‚í±È‘&4óñ± wúµ†Æ­UU¤ü`‰ÑÙR9Ðκ¢ˆV=”ßøÀÂ~¿?ŸÏ??:®p¯×ë„[LæÂÕëû¦z^ó5êÔ®U?ï±ïyy`ίÀtéÈh1&©¡TCþ,‡×ZÿËÙ~®ZT{â&7?K`æ—¼ÖÞqÕP°iõ?‡]¬ [ø“p€=«ÍËE„ Õ±„›ªªÕO¸§¥ªãª¤Å­®7Ë<ÞÕyšÂfO§SØ~8ê_a_8®?C‡áó´Ì3œ‚í±È‘¦Ž®ç\ÿé^5¤?ÁIò]ÂÍ\ÄEŸ† g¼žŠT°,7:[¶½®("€UåÅ7>ä1ý7‡ñËŸ›êyß×ÀrˆÞõ.-®³õ芺nîM.zkñÿÞB6jmççšÀÌ Š´ÿ¬õ¬$ Ïv ž¸ùõö|>Ϭ·mF„ 6­ž°!où µ4tq]+]Kד #š[ý÷ÎüŠŠ¾þ—Ô¼–m«:®JZܪ# ã]=§ép8„š<ö- =ÝàŸ›Ê?Ã)Õ‹i*á»^{*gOÞó¸°ÁQ÷Œm;"Œ–RôQ»"©lXnt¶l{]QD«Ê‹o¼ÿúkàmŸ= V©65äK7]—ÒcWÀf¾FlŽèâÛ„¯1~ýˆn'W€þ¥€_Wås¬({â’ÔÛ®UÓ_hj-"ÌV° uÕÒ±‹uÉ‹4ÿ”dSÕ¶úï:ÆžÖâÇUI‹[iD˜m¼Û@ÇØ5OûóN‰ü3œRí1ÿ‘&Ôõ†ÉÓà®ê‘äm“¸ˆëª“ÑÆ[¤"•­ËÎÀm¯+Š`ÕCyñ'ùrDÐuÇiªM ÙHôæÕ ï>ÌØõ  ´¢ï›üåµ×ç 'T€p Æ–[׳Cž®*{â’ÔÛ®%Ä9K"kï¸j(Ø„ºîŸ°T%"¬¶ÕOëý*9®JZÜêz³ÌãÝ6:Æè\åp8Ô6Ã)Õói*]UkÔ ÿ»†Ëán8"Œ¾ö¶ë¡Ôü©xXnt¶l{]QD«Ê‹o<ÉGC¾ºïiŸ°~ÝÔŸK¾ÑûrG]ÒþY,£Þ’´– Ø„G]øs9¢ø‰KUo£Ë>“åv"ÂÌ›J×mÿC¥‹4ß2Su¶ú×'–æôóe«’·ºÞ,çx·™Ž1™M»%)s¿§=®w©-zfǾ&tþë8¶v}O¡+ÜÏ_Å+ÀB£ó—ˆØúº¢ˆV=”ßxª½JøäÝ´¥§h°8ùVÛèÖ2¼k´’ˆpÚ¦º¬è¿¨/~âR•@ôÝJïüo'"Ì\°©L»o¡ª"­*"¬³ÕÏïáËW%-n“½Yªñn3c×]UÕ‚íq½KmÑ/N¸f{ÝÈü]êù(C=Ysñ °Ü·,€Û^WÀª‡òâOµW]ËeÙ6õþzÒ?ßyÕãû›¿na-RIæ¼W'sµŒ¾$¶ÿ‹Ÿ¸š€ˆ0Oß’¶bOþì—ˆp3­¾ªãª¤Å5Nï¶Ô1¦Š—›álùçrIt=ü5áé­™›ÚXDN}ô•¡p™+R `¹SoØöº¢ˆV=”ßxª½ºßï7ý‘iïü¾œ/ò ¯è’ãœE­ÌÕ2ú¯#ÕpâD„•÷-ÅWÂvn‹´žˆpK­¾ªãV>Þm¬cLr+×r3œ²í1ÿ\.‰h :yê ÅÞZ³öˆðñx„ s¹\ŽÇcÏ·Úÿ|ómæŠTCLëÐD„°ê¡¼øÆ«z´dò¦¢/´™yŸm‘WxE¿.4çë9™«eôDô¬fÔpâjh"šËgl­®³Hë9kõõW%§¸ˆpZϰ–Žñ•q\¯×Ó?á¸öÿDßC8ð·çœá”mùçrI„=!õ^bkk‰g õ§ÿ©ºÌ©† "(2$é! ìP^|ãÛˆ£7ó¯ñZ2úÆ—Ýnw>Ÿ'¼í'çaŽ}쥆'"lªo™ ZKç<ÒxD¸±V_Ïq‰+ï*ïG˜„jÜÎYMÊ9Ã)ÛóÏå’˜óÜß»9¤µ†¢ø³d®H5T!@þ!I ë]›JµñmD„=ßøXÝLiÈ]ÇÇã1\z?ÚªeôÕ^»Ý®æ'"lªo™ ZKç<ÒxD¸±V_Ïq‰+ïªí/—ËœXpÔoÏ6é³=.7—«p°›óÆþÍG„Ã?nž³"ÕPD„™‡$!¬zm*ÕÆ·öéc]3¥èm·ý÷!ŸN§É—ðÉsÔk8q"¦ú– ¢µtÎZ\ãáÆZ}=Ç%"¬|¼«°c¼\.iëm=3œâí1ó\n¹2ù9µðƒ[š%|xpÔYÎY‘j¨"B€lC’ˆ¶±6•dãÛˆÿË%OU‰¾ kˆÃáp¹\ÊVË´·Ù‹×Ûqm&"¬!V˜¿'õœŽµúzŽKD¸Š¶\Éiz>ŸKŸÏ!o s‰ý~ÿùùyúçö?¯§ŠæH†N%ÓÂls9ƒ]…뱡ù‡$‡æ©H"B€õ®+Š`ÕCyñ‹«)=Éw¿nòv ¡ˆ°©¾¥†ß."Š·1 [iDØŸ¾¾köçüaæ,=égZ˜g.g°[õE\%"B€õ®+Š`ÕCyño8"ÜFµy<§ÓiÈ ÿïÂOU¾"TÉ6Õ·ÔðÛE„[jõõ—ˆpm¹øi 3Š®9Cø«áiT’Yn†S[=\z.g°[õE\%ID°ÞuE!¬z(/¾qáZ<ŸÏëõz<G}?èt:嬖Ç#úŽ£šOœˆ°©¾¥†ß."nµ+ûj)";ÞÕpšÂD¢k¶p¿ß fòNµõp¡¹Ür dò3Œ]•m¥ÝHm»´DEª¡ˆ¦uh"BXõP^|ãÛˆ£Èc×»Ö%ÝårùüüÜívý³»Çã‘­ZÞn·è×Xj>q"¦ú– ¢MlN-m<"ÜX«¯ç¸D„™÷mìxWÃi Ó†$ùàÒ'zþ g-ÓÂTs¹$¢…6ù;zÓHµÝHå˧I*R @D0­CÀª‡òâßpDx½^©Zá2¼ç%çó9[µ ¿ë}kaßj>q"¦ú– Ò.š}‰·Õêë9.aæ};ÞÕpš¢S…ãñXs·ƒØuáW¬´YãòéØŠTCLëÐD„°ê¡¼øÆ·‡Ã!º^7p9¢Ùj–ð¬E?bòç{½ŠŸ¸j#Â9_»«¡|6&?AG„kõõWÍáÚ{³TãÝ—ˆ0ã\õ´°Ô¨}òk«&£Ï¯ MDXj·‹W€Ìa%cÀüMD«Ê‹o|3áår‰þÔœ÷¶PÍRµ®žüyïqñWCˆ®ÉTò°ƒˆð«c©yÚ—¡ºîÒ_ºª:[jõõW%§x“½Yªñî«ÊˆpÂ"y©ÇÜFýÒUO KzçóyÎÓýÃÜð~@DXj·‹W€åÊ¹æ± `~ß."€UåÅ7¾™ˆð«ãÃ@Ónýåv»MþÇØ Ø?¿dÔ#üìØ #ÉYëZ-˜¡”=q54è)Ó¨z:®-E„]KÍc—ÖC íªíK—Cm§c3­¾žãªäo²7K8Þ•=M“~ü)úÁ²!¿=ÿ §T{̤©t…;‡ÃaøF¢7ÕŒŠ™D„¥*Rñ °\9×<6Ì’D„°ê¡¼øÆ·ö,[}~~N{µÔívû¾¨ÌVI&/^E_ìs>Ÿ=kÇ#zkîð¥ì‰«¡ DO\%o@ö_‹ \¾ Õøx<μ ÛRD¸™V_ÏqUrŠ7Ù›%ïÊž¦®^hx?Öµø?¼Ýåœá”jù4¡®&<ðéËðÏæÇ²"‚©lX®œk›’\–Š`½Cyño)"üê¾sõõšËå2pEèñx„‹è_÷Ÿç¬$¯½õ³Ñ»…ÿ|õYWY…ÃRV]OW}0¡à‰«¡Þv»á+·v\‹»—þ\_ U·çg›7Óêë9®JNñêz³Ìã]ÙÓÔuvþ|X)I?–†Sª=9ÒTBt=} ³§¸z䱟€¬He+Àrå\óØ0HÀª‡òâßXD®@»îðÿyl¸Ð¾Ýn?/WÇ? þvΛӞÇáK—]·Êÿ¹nÙ_Vûýþt:…’ù¹¤ð*«°å®‚š°TðÄUÒz 3õ¯£§#üI85¯Õ˜:;®E„ýç(´”P ®2…ÿ~UË„Wa‹·Ñêë9®zNñºz³Ìã]ñÓÔS9CÅ s‰Ÿ‡ú±ð'©ú±ü3œRí±È‘&Ôóôåw=ù>œðV’±j‰ËV¤‚`Ñr®vlH8¡À‡òâßXDøÕ{›hýÓ¤QK—ßW¯]×¼CÖ-*« wä–:q•ÔÛžçSÊÖLa†*ÚfD¸V_ÏqÕsŠ×Õ›e¦0m(ÕåŸá”j¥Ž4¡!4á=™"Ââ©TX´œ«›j˜Ðê÷`W—"Ÿz¾´Òˆp¹uËJòÁ‚'®žz;ùØë츶Î\_zéÚÂÒåPíéX{«¯ç¸ª:Å+êÍ2w5œ¦þï¢Ñ•3Ö6Ã)Õ+™SÍ4üéÑþ'ÎV=è—Ý¥²©HXºœë›ŠOhõ{°«Ká/Ïçsþ"Øz#Â?¿+´Ð s·ÛÍ\ËÊâê©·“Ÿt¨³ãÚdDø5#%ün•"Â-µúzŽ«ªS¼¢Þ,óxWÉiš¼ø¿ßï_"Œ‡ÏpJµÇ²GšÐÌGÉ&hòËáp˜³TöÄUXo‡—¼ˆ°”çó9°rÇ_µQD¸¥V_ÏqÕyŠWÑ›åïª:Mó²P2ï‰Øœßžm†S|ZXüHS¹\.Høg?£äÍ úew©xEÊV²•sUcÀü®RD0Ðív —„á’y¿ß¿_†?<á„+ñîÜ~¹ßïçóùÏ}ž¼bùç„0ì@Ø~øE¿î¹ ÿ~õårɰµÆ7_8œãñø~È¡äÆ¿ c‰d–áB} M T¿_­ãu‚ÂTDZ½Þ¬žÞlã]‘:ù:;ï‡JcÑ].=é§=?ÒTç+ìäû—§¶P¼"m¯˜ik$" à S±`¼€Ê'´"BÒÎ0 Æ;¨|B+" í S±`¼€Ê'´"BÒÎ0 Æ;¨|B+" í S±`¼€Ê'´"BÒÎ0 Æ;¨|B+" í S±`¼€Ê'´"BÒÎ0 Æ;¨|B+" í S±`¼€Ê'´"BÒÎ0 Æ;¨|B+" í S±`¼€Ê'´"BÒÎ0 Æ;¨|B+" í S±`¼€Ê'´"BØ*!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„°´Ëå?>>L‡C´^Ýn7å×D„D¸^îz~°Á|ðKDhvDKMØüãz½¶¶BhzFrÏç³ëYÂÇã¡|€ü㚈’ˆÎó—Ë÷û}åCp ó„üIŸl‘U7ÆvÔ…9áÏ0ZfusŒ®ï)ˆa¬®”°Á¯“+šÍŠ ÇñxÌüüàn·.·"BÚl2Œí¨ç„†>>ÅZæ÷û½ÍBÓ3Ò•G…¬e6+"€—®O†Ké…~cô"ÝuÂ}Òf“A%T al™i·Û]¯WeKåÝûû݉"B”Õõ>J¬n6«ó qÏç3zɼèjÏû[FE„i÷ADH›M•P%„±m$‰ÏÏOÅKµÝ{Ï+FCÕ W=ß/N ÿq»ÝÎçóápªfÊêOÑï{îv»å^Fè«E„Vô£Ë½$\,„+ñU Á"B!«k2¨„*!Œm#RB6ß½Gï‡üøøhᓚFFeµ±õ@_-"€„¢ïYî#ã×ëµç%?.6› *¡JcÛHBçóY!S[÷Þõ.ÄòA#£²Ê#úQÂå¾Zè«E„Jô…Ÿ©¾ðzQOp:ºž.´"B¬« ª„ªÜï÷0£»\.ѧE´8jîÞÏçs˽j§Ê*ƒ0@¼Ý~¿W2@ž¾ZD gòI^ ²!xÕóIš *¡J µ‘hæòr¹\”3UUÝ×Wº%ÒȨ¬x‰Þ@ÒNCÊöÕ"B˜&úÎÏ$®wçÍŽh³É ‚62JôN3_$¤Âª}kŠ¢CY¥õ|>ßKo·Û) C_-"€ ®×ëû x: Ù.kØy³#Úl2¨„ Œ}ürŸ´FÕVuŒŒÊ*›Óéô^€×ëUÉK÷Õ"B˜àý~ÚÝn—äÂ/ášwÞìˆ6› *!h#cu=H¨œ©ªê6þ‰4TYeó|>ßßSä‹„@†¾ZDcE—tÎçsÙ!ÛEb ;ovD›M•´‘±¢¯•Ó設êŠ5Re•MôKµ¾H,ÝW‹`¬è‹¡R=B¸™ë¯á;y½^Çã¯3w»]ø“p•ôxt&s^ýùŸ§,aµÉ6Ï\ÝÚ`8³árlÔàjàš_ÃÜ>ɦ‚‹¬'×31û.Ûè0÷Š\ÇþÞhM®ó¦)`D„dõã}øË¼z¹ˆ0ú•~áâ®Ú…”z–n·Û„5Øð# =û6;$*¸/T¤“⪆ݞP¢-}x8˜ö¦ëlÅX°)å¹ gpìÊ䨹Â~@ך¶šM¾_¨Ú Ø{\õÊCWÑei#ÑndTÝ~Ÿ†¦4üÇ£#ÑŸcˆpQÓf?Oß„,sw1sS‹Á™×“뙘½„QéϲM24̼ï`‰~[D@³‡Ãœµ… W¨2ïd¸’»°9yÁ¿µˆpÎ É´¶gÏŸÏç¨Û¹ë|ÎäC˜p, Ûu »°ÌYšS—rcÁ¦”áJ0tû«¾ØÔµVµQv™4mµŒ>’69ªËÜei#ÑûôF½ôò}?êN¿èƒKÖIáræ\VÌ)ŠÌÝÅœM-=ç\O®dbö29Ó)Õ¬Þ{’E§á¥š¤]W²Û +Àüå  Mþb,Ø”–¾L¸8Yd¤ÓµV¸±Ð÷FóOÀR}ÛºH—U¤DçáÃOA×oÑÿ󔉒çþ“UG„†à›ð2ä5hóÛu=»ª¤ZU—ŠcÁ¦´è•`ׂ|Wa†ÿ …Ù“8dî t­ÕN J  «eté>í- ‹vY¥ÚHô`¾­´ëFµï‰¾ýoÈÏŠ—íþ“õF„y†à g¡ª‰Ù×ø»×¦]‰Tu{ °%"B˜éýŠ ÿ[F¿¶†Ë·óùüsmçñx„+ žËºáwQ6†Òë)«_Îá?Âÿö_ÛŽú0ÐÀ½ßïÉþù©×õ{ô]V)Óêª~>hÐó«?Ku~»®g·‡T€WKŸðøIØT¨9ï›ê©K£ž‹)RŒ•4¥äƒK×ùò®Èè9ÍÙèZ—›„Ý;Na?C™ü û^%Ù¿Lý_¹—¦&\þ}ï.¦M)KuY¥ÚHt ì™[ £Ç›ç ²óÌ ¯;z¯?ó¯è³Øµ•y’M傪*UMÌ&ܽ6í¨ßQmŸkVJD3½ÏÕ§}c¨’ª²PtýI_ÏeÝÀbo!" eØõÞ¹ÏÏÏžîù‚FØàðWÙ yŸOÿÊ[¸À¯jb}&"TÅŸ‹ðýg$ZoC©.Ú®«ÚíQ`øËšþÜTÏ«Œ+/ÆJšRÚf}âæ¿o˾‘Ûíös•2[? kM>%8¡…Ž}¤·'Qªçqȱ‰öT¡ë˜ð¹‚]V©62†DÆý¡Ò1ZÚCžë&×sÄSF–_•°¶2Ÿ¿©"CðU¥æ‰Ù÷Îüºßõë·ë¼ªYª/rg2°="BH>˜Žz( ªª‚ãñ8p¬gjÈZˆ»Þñ5äá¯î5äáÙwÿ%óÀÒµœÿ:]+*c—µ'œ”9íº¶Ý[z²ŒT›R— c%M)íàÝ™i÷Õ|‡Ùº]k=º ³Èw'·‘pÊB±‡3ÕLËËvY¥ÚHt‡‡¬ö÷{ÈÚ{ôQ¦E§£5Ì3+¼îè¹³hTþZÊwPX[™ÏßT‘!8yU©vböýDü¨*7Jô±e«1À¢+u"BXÑD}½án·{%Õõ©‘!kƒ›»®yG½­ëfþ9os» Ñu ùŸÒ–Æ„;¢K‘ûý~¡v]ÛnO8w]7¤ÚÔ,ÆJšRÚÁ%ú”Çä'¿^ߦÌÓèZk­K‡Ãa‹*Ã§Ë*ÛF¢õáÏŸêƒýÞé}pØ)‰Óêš0L kB“é˜W‚“W•:'f¯KÚ ÷ Wrs2°1"B˜ãýâ¢Ôë>Ö&\[ôBu-?½àõ.»¯÷Ööìù„UšèÚÝŸïiEïUžüÅ«±M`rå©p·'¬`t=î4¡.E7õg]*[Œ•4¥´ƒËz¯u­«8#õ| zŽÐÉLË5ÊvYeÛHôƱ_Î}?„þ{Ï¢{;ðÓ`"„º!\îáè•F„EÎZ†×•Ÿ˜Í¹¯c¬÷œwà çlVD@SÞW$&_¤TuýUÿNv=HøçÕÙæ#ÂèÇ5&¼{-ú¾¯Ì÷ÆÏÙ‡T¢ÑÒä;®£[ë¹´Ÿ\’+Ýí_¦Ýà=|SÖ¥²ÅXISêZëÔõ®¹Í,ªìv»p¦F­9—í²Ê¶‘èk=úŸý•+½žûõ0ZÿïþÒ÷ÈÊÛƒˆ°Ô¸_çÄ,gH÷~—ìê^THDÛ˜¥·~u¼Shìã‹»n¿Ÿp_ëœM¥:ÑÑÅ·Ìû½šÍy‘]ôF÷ž'&þJw{¹ 0mSe‹±’’ÌÖÿŽ,]ëŠV3¶·¨2üI¨‚]V mdlHôkÿ:´÷È '}ˆ>º˜¿öŠó,[Š—‚—¾ú[éÄl²zîO¶DDs¼¿ëc¹wÚ¬èR=ÏNFß5úgD»íˆ0z©;ù>êè'K†ÔðͬcGVðLDÿõœ i‡¿ÒÝx Ù6U¼¿¶Foí(òý¸Qt­+ZÍØä¢Êà²]V m$ú(Ùð÷z¹ë{1ö”áûo~È"ÂT¢µÿK‚;Ÿ²ç½È¼ô;dV:1K¸Ï«{O8°–Iµˆ&/b”z¢Áˆ0ú*ª?¯s·FohŸüdëä­Õ°ð•D´ŽÍlã£ÞŸ6íðWºÛµmªx1~­ ù]t ¿þ{àu­E<Ðâ®×ëéŸPyöÿD_h¹íˆpH)ÛeÕÐF¢%ÐõUÇ÷Lóû¯†¿kô=p~È"ÂEûç¥Ç”•F„E†à„‡¼±‰Ù4"B`½³Yå @;#i×ZD‘1}„—HC®’¶N~8%jòC›YÇŽ>©š³L;ü•îvm›*^Œ_[Œ£…ü~ècì—ײѵæñx"õ¾Í²]V%m¤k~øý³¿ª_¸ðžT~OcïOÛ^÷^ÏЭ-ÎÈê†Ô̧`òœp?761KUK×v "BH;KÏ|Rá¥z¶Œ¾úæÏ·Íl;"ŒÞxÿññ1­„£ ¹CnÖÝLD-Ï  dn2+ÝíÚ6U¼¿D„ÆÜ<û£kÝ|ÿPçlÔÃte»¬JÚH×μ íý¤cOîë]£ïYÉ„gÖR½n÷«ùˆ°HÍO[æ™Ï{æ!8á~nlb6MÏ-i;7! ôþ®ËRïúh-"|ÿšÌÀ‹ÄmG„]OLøVWôk>?ß²•çD—­Ø]å¹ô ¬fþJw»¶M/Æ/áà~©TW kÝLÿPçlÔKYËvY•´‘ž ~>ŸÇ>z}×è¯íL;Ìè1f~œvaW…YôÓiË|-QO‚îçÆ&fÓ¼¿m5çá[%"€9¢/]©gL_ÅÄcÚ¦&ÌhÛáWǽ÷î/Þ*<ð‰€-­‡h=|­²H“Yén×¶©²ÅøUqDXê«»û]ë¶û‡ ëdôV¨þ‡éÊvY5´‘ž å9ê-£/Ñwþš‚N{ˆ)Z\Ó¾¬ÚxDØU˜‹Þ´9ùðk8ï™<í,bK³T;PpJl†ˆ’¦Ó^³ö –œ;Ùµn6ä.îÍG„ï7·½ýþkömº[Z~R8ç‡?&þJw»¶M•-ÆJJ2ºžYð–õR}‚®uÑ38a…³Ô#¥y 3T•hÓëO£ÊvY5´‘oï9ãûŸL;¿‡Ãáש™V¼ï}¿È4É”xéª[ÕuGWÝ[.:™|ø5œ÷Ìžv±¥‰YªK`«1ÀB›ˆ†{¿ð)òE€v"®u³w o>"ìZ^µþ½GwøB߯‰~õ2IK¿ÝnýOFÌ9ü•îvm›*XŒ•”dt=sòÓáçÜE]Ëó~o]k*Ñ5ö;ÎÑ/Rm&"|ÞmøºzÁ.«†6òsoû©† Ñ×VŒ}ñ]×gÝFiW4ÖZDØU÷¦½öUúãøÉ‡_ö¼‚ÓÎ"¶41KÒoLþä+ÀŸ›ˆ†{ÿhx‘¹z#aW>8üVáÍG„=ëĺVÆ_zo,"ìY‹e5íÕF¡º~¯™,tø+ÝíÚ6U°+)É®þdη“&¯ãEwfÚ+ÚRýv]ëüYÓ¨70„Fו"m "|<]Õlàô²l—U¼ _ìžëõé´ø²³ãòÐ^¢ÉK›aOÝ ­fTJþñw=\âðËž÷"CpÚYÄ–&f¼_½Á`¬IDÓ®S²}¡ÚKõQpñ8äú½ëÝ2£îo!" Õ¯ëÛÃáÐS9{–^G}æc{ŸšêY‘%jæÀ õü|>ÿ:;ËþJw»¶M•*ÆJ¿ëmfÓÞªý«èFýìœWL'¡kM¢ë<þùxK(¨žøl¥áíŸÐŽÇc×Pc[\Á.«xùi~‚6dÝlÎm]ÛìÏ\~fX"ŸU¨§4† 7¡}ý*ØœE—缂ÓÎ"¶41KRɧ=Å 0|ª#"€ÉÓõüßiZuDø½Ìr:n·ÛÏ‹ÍP¼áOŸw-:e[b]Ñ~ýuÓûççgøß…þ#üoÿÊè{}·ö<¾ú³TCÃõ³6¾*pøóð·]ux¹Ã_én×¶©RÅXOIöt¿Çãñ×Q‡üÕi¿Vðúw#lù|>éÀg>y‘Š®5‰žõ*ßӀûýþg1V&7|¸`—U¼üÔ(ýlYÏÎ邺ž¨}½†1œˆŸJ(‡Ð[þyf›¿þz»l¨ÒïÖ«{ ­ð ~Áó^jN8‹ØÒÄl¬è½²ùoK6IDó½ßʘü]£Û šiŒºµ‘ˆ°ql¬±¯$Ú^DøÕ{ôr-qþá¯t·kÛT‘b¬çð{žà{€=ë¢ï·ˆ|¯v­ãM~¼h]ë|~'ÎÜfæs"¥º¬âmd`!Œ-ÏžÜsÎ#<ý¾e8S‹ÓÖ½åæ9Ï{©!8á,bK³±ÞSÑQ_z(;›°yÑ Ÿ´¯>³Œ–ê5íD„_½÷½7öfû¯F„¯Õ’á7rדµ­t·kÛTþb¬êð'û¢£Àäw”ͤk¯ç9šºrÆ-ªì÷ûióÉ"]Vñ62ðÔ$ÜÔÌ»æD'ÿýïõ¼"ÂäuoéyN©ó^pN5‹ØÒÄll—þþ{ó¿¶Ø*!̽4íÛÏD„©Ö‡›Š¿fßP=íþ­F„¯Æ>e;Ö¶ÒÝ®mS™‹±ªÃŸ|»þB£ÀŸß­[š®u¾É+ùûýþ•Ël;"œð±°â]Vñ6òKôƒeÓ’Çhu U±`C'wfCØdDø5;€Ë3Ï)rÞ Á©f[š˜=Lo*ŸÍŠhMôB/á¼}Ãáù|ž°ó‡ÃaZñ¶~ý{¥[ÏG@z''hÃáw‘î÷ûù ,~&íá¯q·+ÜT¶b¬íðï÷û„ÛõmäñxÌÄã{´x? kiÂÒîÏÔl«a˜á$|6$g—U¼üaN{5hô]£Ir̯ñqj8?‹HDø.Œ5sª}8#ýO﹋oþy/;'™Elib6\ônäùÏY,ºÞ(" AÑ^… š‡ì&ßßP¸\./Ã5àœµ£#Âï¸R7ÿÉ…ÍG„ß­~ÂJK¨À¡gøâ¸%]»]m]ÊPŒu~Ïg‰FýŠëõ:¡Ã¤}‰÷|ºÖù=Ò¥ÝPzï‹·Ûˆá…ӡиB»X(þÎÓeo#¿D\Jx¢SE™ÃB˜*¿‘ˆ0UµžÎg¾é(íy/;§šEl`b6\ôn‡„ý€ˆRy¿Ä W@Še”p.‚Âú¯Â|- Í_§â~¿‡kóP¿Ùð¿¯õɲïî[©p‘Šîóó3ÔÒ÷u k¿Mí¶³_ƒp8ÇãñýCOþ0üUè®.^…>'üã? °ò¥0]ëüvôªQJo«]–62¼”~š×XMjr¨Ï¯‚ýUý^e:Ÿ²ctÁó^jN8‹hab= MHHD /v–{Јè i§½0 ‹ˆо8Å8À@ѯzOœˆŠ~‘ðx<*`ˆãñè+„@"BH+úÑy3yàO÷ûý}UÁW%ˆ ÃdþããÃëF€~ï« ÷û]Éɉ ¹è+A¼nèa=ÈIDÉ=ŸÏÝn÷> ^¯W…¼»^¯ï+ »ÝÎ[‰€…ˆ` ·Û-:&z7ðKô«%ÁívS8ÀBD„°èëAÜþüô|>£Ÿ ôŠQ`Q"BXNt†þPJ|uçƒá°(!,çñxDGFó| Øï÷Ñ¥ƒÇã¡p€E‰`Q]%<ž%€f=ŸÏÃáà„@)"BXÚårñ,!ðSôý¢ÁårQ8@"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""€¦ˆ )"BhŠˆš""„œÇù|þüüÜï÷¿šIø“ðçáoÿÙü>‰iÓ{:¶ôo¼Ýnï¿´Kø—áßor€âD„´i·Ûe«¥Ïçsx0÷+¤Kõ4_ ûTBDHƒî÷{¶Zý]£„-l`€zˆiPôyº%~Ñüln~BWÃ>UÒ”çóùùù™§–>„hÚÛ>kØ 6"BÚq½^ß?A¸\-íùößáp¸\.?ŸË ÿþ$üyÏ7Wº@mD„lÕóù¼ýs:ºž\®–^.—èoùøøècgøÛðo¢?¶¹º}*$"D­^¢–FWüøøx>Ÿþlø7Ñ„.lsuûTHDˆZ¼–v=¾7$›{ ÿ2º…ëõº¢}ê$"D­N^K£ŸóKòšÐ°åíP'!juÚZ}ønÚû9£o øšÐâûTKDˆZ¶–FŸ¼;6~jÚ“€5ìP-!ª}ÚZúùùù¾ñûý>aSá§Þ7¶¿Š}ª%"DµO[K£oæL¸«CÞZÃ>Õ¢Ú'¬¥Ñî÷ûÉ ?;öS€5ìP3!ª}ÂZz»ÝR}ð%ú)Àð[*ß f"BTû„µô|>¿oùt:MÞ`øÙ÷ †ßRù>5¢Ú'¬¥Ñ4íz½NÞ`ô‘Àþ¼¯†}j&"DµOXK£Ÿí›óNÎh5¢Ú'¬¥Klyì6kØ f"BT{aÁò¢Ú‹ –ŸˆÕ^DX°Ärú°"BŠE„4ED"BaÂ.˰@ýD„ "¦zŠPDÀ*ˆAD("Lþõ‚ˆPD(" )"BXcD¸ßïÓnùù|¾o0ü–Ê÷¡þˆP+ B"BÈ\í“l9ÏÝn·É ?›$"̼5÷«"Bª%"„ÌÕ>É–E„õ÷«"Bª%"„ÌÕ>É–O§SÚxîz½¾o0ü–Ê÷¡æ~UD@µD„¹Ú'Ùòù|~ßrøÃÉŒæ}ý¬ajîWE„TKD™«}’-GßÉ9ç»ãñ8ö‘Àö¡æ~UD@µD„¹Ú'ÙòóùLûÙ¾è‡Ão©|jîWE„TKD™«}ªïv»„ßTØþ*ö¡Ú~UD@µD„¹Ú§Úøápxßøý~Ÿ°©è+CÃöW±Õö«"Bª%"„ÌÕ>ÕÆ/—ËûÆÇã„ME?¶¿Š}¨¶_P-!d®ö©6þxáö£ÉZð|>n!üËè†g|5ìCýªˆ€j‰!sµOû+v»Ýû¯øøø’Ð…þåü7…Ö°ö«"Bª%"„ÌÕ>í¯èzˆïãã£ÿC~÷û=šÍ¥zMhæ}¨°_P-!d®öÉKWÊ|~~^¯×ûýþýÃ_.—ÃáÐõ#Ó¾XÃ>Ô֝Ѝ–ˆªªÕvãñx$ÜþçþjÞ‡ÚúU!ÕBUµzÚžÜn·$¿ýç³~k܇ªúU!ÕBUµzòÎÌOèægs5ìC=ýªˆ€j‰¡ªZ=gÇ~¿ŸðKÃO¥z·g ûPI¿*" Z"B¨ªVÏß«ëõ:<¤ ÿòv»%/™ö¡x¿*" Z"BؤÇãq>Ÿ???ߣºð'áÏÃß.ýÔ^ ûP°_P-!Àýªˆ€j‰–èWE„TKD°D¿*" Z"B€%úU!Õ,ѯЍ–ˆ`‰~UD@µD„Kô«"Bª%"X¢_P-!Àýªˆ€j‰–èWE„TKD°D¿*" Z"B€%úU!Õ,ѯЍ–ˆ`‰~UD@µD„Kô«"Bª%"X¢_P-!Àýªˆ€j‰–èWE„TKD°D¿*" Z"B€%úU!Õ,ѯЍ–ˆ`‰~UD@µD„Kô«"Bª%"X¢_P-!Àýªˆ€j‰–èWE„TKD°D¿*" Z"B€%úU!Õ,ѯЍ–ˆ`‰~UD@µD„Kô«"Bª%"X¢_P-!Àýªˆ€j‰–èWE„TKD°D¿*" Z"B€%úU!Õ,ѯЍ–ˆ`‰~UD@µD„Kô«"Bª%"X¢_P-!Àýªˆ€j‰–èWE„TKD°D¿*" Z"B€%úU!Õ,ѯЍ–ˆ`‰~UD@µD„Kô«"Bª%"X¢_P-!Àýªˆ€j‰–èWE„TKD°D¿*" Z"B€%úU!Õ,ѯЍ–ˆ`‰~UD@µD„ðÿÙ»£ó¶u-£§„´RCZH i!ö[ZH n!5¤·à4š93_‹¢@›ÜÖúøpO"+ Im‘ø¯lˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•Y87®'Åÿm'>@"ˆ˜«! çÆ»÷°åÕ=R"JH„sU"dáÜøÏ–·žó*&…$B€ˆ¹*²pnÜ伕¢·õ1!PN"ˆ˜«!˧GqÔÛô}ØD"ˆ˜«!wϲ´Wþ»ú °•D1W%BÖN’‚ÀWø[ú PA"ˆ˜«!ΓG™¯ä×õA ŽD1W%Bn=½syûþâûßýðÌë$B€ˆ¹*rëé?]V+áú¯Üëƒ!PB"ˆ˜«!·žn\•ÿ\éƒ!PB"ˆ˜«!·ž–\î俇Ûʬ“"æªDÈ¢V•ð¢;H„sU"äžV•PªI„sU"dEÛï8ºòIC€E!@Ä\•YwL%Ô€E!@Ä\•y(C%Ô`N!@Ä\•)±X ó$B F%DÌU‰BýöA ú%DÌU‰r£öA Ò’"æªDÈ&söA Î"DÌU‰m§¨!À$B€ˆ¹*²áœ©JcÿC¢I„sU"¤ô„¹“ÿVþs±®S 5DxO"ˆ˜«!EgËýð·þ+•PCÔà/‰ b®J„<>U{‰0ºjˆ"ó"æªDȃódµÞK„çVB QC`!@Ä\•Y;IõÁ•D˜¹jˆ"½"æªDÈÝ3¤ ®'Â~+¡†¨!„D1W%B–O²>ø0ŽZ 5D €cH„sU"dáÜ(îƒ%‰ðÞ>ÍM(Ô(!DÌU‰…s£¸~xpáÃV2ТŒÀ;!@Ä\•Y87Šûàå&^Š+aÝßM:Ô5D€©H„sU"dáÜ(¥Dx)þÈaéPCÔF"DÌU‰uë}ðÃ6}aª‘†hÓr’"æªDÈŠ’ÌWø[©*aÅ¿ZC´iˆç¼UmÙáqÎ\æªDÈ=…¯üw;ª„{FC´iˆ!oU[viÝ#)Ⱦ> ÌU‰EåioÓ†©„{OC´iˆ5oU[vZÅÃè`}@"˜«!·6E½­™¤Öíd Ñ&]}|«Ú²[¶>Æ~€>Ö$B€€¹*rkSΫx˜DXwdDÛœy«ü_½éú t³8 ÌU‰[›Z^Ý#%Âæ‡IC´ Ü ÿ]å¿«@O+!@À\•¹µ)äÅ=˜†ÇQC´õÞKþæ…¿¥@_$B€ˆ¹*´¤C ±¯†øðïVòëú tG"ˆ˜«!pt¨!žÞ?|ôxýOø‹ïׇš !@Ä\•jÒ¡†Ýo†éÊó¯ÿʽ>(@r!@Ä\•8Ò¡†¸³!ÞîÉËýð·òŸ+}P"€ä$B€ˆ¹*'’5Ä­¤–„ÈÅï•™I„sU"’SeÄVTÔ G!@Ä\•ÞI‡bݧõAè‚D1W%B`xÒ¡†Øö'$G’"æªD jˆz"¤%DÌU‰à!éPCÔà,!@Ä\•ö“5D=‚H„sU"8€t¨!ê‰PG"ˆ˜«!@Ò¡†¨'À"‰ b®J„½P5D=€ I„sU"†PhÓD1W%B€yLÞG=€I„sU"à¯~ûà‡__y˜0ê‰ôE"ˆ˜«!åÒöÁÛDZ õD=€ÃH„sU" ¡³úàb"LR õD=€=$B€ˆ¹*p¤ >x/vW õD=‘¤WÎ[N€¸À¤ä!@À\•H¥®®$Â+¡ž¨'rÜ•ó–ÃZ÷Hg ܽr–æªD@ö7¬²ÀWþ»sVB=QO¤í *|p«ç€©/È%B€€¹*úÝêNFÙšUB=QO$b=|p“'€Ù/È%B€€¹*²û}óùïfoߪŠû`I"Üú„è‰z"%iý‘;Ÿ ¸H„1sU"dß›æó‡Í>Z¾Uç¼ËÍš|y%Üú·RúôD=ÑPZß]å¿kŸ@Ñ¥¸D0W%Bv¼c>/nö Ðì­ª¸^–Öä +aè?AéÓõıçÒâN(ü-}J¯Ã%B€€¹*Rûvù¼²Ù?@›·ªâ>x¹³&_ø‘Ã$”>=QOìn4-þÓJ~]€ á!@À\•©z¯|~¸ÙK@[ë}ðÃ6}áH;=QO<àwyûþâ½ÿóƒQ‹$B€ˆ¹*²ýò¹p³¯€VJ2_áo͹¯ôé‰zbÛÑeµ®ÿÊʇ£M{X$DÌU‰ï’Ï›6{ د0ð•ÿ®ÕøŠŽž8yO,ùÁ¦Ÿ|å?׿y²ù‹$B€ˆ¹*²å-ò¹b³ß€=ÊÓÞ¦X=@è‰ãõÄ’lºé9M$('DÌU‰â÷ÇçêÍÞêlZBßúkòI(zâ„ÍÑ,€M$B€ˆ¹*Röæø¼s³ ›–Ð+fY¾¯£ž8Òf@9‰ b®J„¼3>7ÙìI`«MKèu´2?ä©‚žè‡'ÀH$B€ˆ¹*òèmñ¹áf›l yqfàó =QO€ü$B€ˆ¹*²úžøÜ|³WèˆÒ§'ê‰p:‰ b®J„ÜC|Úì[†¤ôé‰z"D"æªDÈwÃçÃ6{€ )}z¢ž…$B€ˆ¹*²ôVøœjsD˜œÒ§'ê‰ÌL"ˆ˜«!Ko…Ï}mü¥ôé‰z"ƒ‘"æªDÈÒ[áó`›c ‹”>­PO ?‰ b®J„,½>϶9èðت Žz"›H„sU"äλá³MC€jÓöÁ¿¾ò0`Ô($DÌU‰ûoˆÊ †G©Þ&ÂÐJ¨'ê‰3"æªDÈ=OOOŸ† /Q2÷ÁÅD˜¤ê‰z"@§$B€ˆ¹*²èï‘´'#À—4‡õÁ{‰°»J¨'ê‰$½‘ßrÄ=Ž~”æªDÈÃÅ´Vý«d‰I%Ô É%P]\I„WB=QOä¸ù-‡µî‘Î2¾J„sU"¤d}ì˜>¨!jˆúú¹,ð•ÿPOÔi;ˆ Üê9àœw@‰ `®J„ÜjÞæŽ\JR 5D¹x¾“Q¶&B•POÔ‰˜EÜä à´w@‰ `®J„Üj[ß²-©„"l¾r.îƒ%‰pë¢'ꉔL¤õGî|*8ùíO"˜«!‹Zõµ—ŒTB >^9ç¼ËÍš|y%luÁ†ž¨'N8”wWùïÚçdï“æªDÈÖE§±û †¨!À•sq¼,­ÉVÂS.íÐõÄ1æÒâN(ü-}€Þø$B€€¹*R±”4sÔ5Df¼r.;kò…9L~ˆž¨'¦M‹ÿ´’_×èã]O"˜«!u Dú Œ¨!àêè¶~xÀ¦/ìr=QOŒ~Å]ž‡¿xïÿü`ÔD1W%Bª—}¶öAKL¢ŒÀ×E+©øò9/,ÑõĺÑeµ®ÿÊʇ£M{²‘"æªDȞŜ¶}в’†¨!ÐÝÑúêŸ~z¢žXøº¸Ü+ÿ¹þ͓Ͳ‘"æªDÈÎ%š#û ¥$ QC ÕµÐÃGî|*\ꉶ=±ä›nzN €.H„sU"$záÅò‘†¨!0äUPɃ›P ®6ÑmR#'/-ŸH›àœdù$‘æªDXÕÑûjˆ¾í*¸òD+´©¤X],;Ê×YÄÝ“M"˜«!`%GCì¨!úጀ‹ÏV}Pš´I4X`,8v…¿åгv¦I„sU"¬Þhˆ½4ÄÉü"àŠ´<Ò}øõ•‡ù ¤Mjdïã£Ý^òëŽ%KÙ!@Û¹*Vl4Ä.âð?`puÚ°Þ&Â.*¡ÔhS{˜—G±ïá/¾ÿÝÏ ï—²%B€¶sU",ÔȈ>ºÐãeêzò{ø‹WB©Ñ&5</«•pýWîõA‰Û¥l‰ í\•‹3¢JÐÝ%ëÃØWòë*¡Úh“[µËýð·òŸ+}P"äv)["h;W%BÀ‚Œ†h“€¾._K2_áo©„R£ÍVQOÑËüWò§ß{Bx¿”-´«!€¥ QLzº/ |å¿«J6[ÃÔ¨·”-´«!ÀCÖ4D%Èr/_¼Àþ°ý©„¨¶ IQ¤|)["h;W%B€ý¬ hˆJ"pÄü–ö‡áoå ] #5ÚôA.eK„mçªDp·ü¢˜4¸‘ß²À^ñ°ºI暩ѦrÌR¶DÐv®J„¸—וDàñü–öÛêWX ]ZƒÚ¨’îP"˜«!€µÑÐæõ}ÜÈoY`_¬~…9ä"5"5Fnú ›ß%B€€¹*XÄP mJ"ŒqíwêHÇWBƒ‚u!@Ä\•,MLX ÅG%Æ»®[yLÅ—ãzµñ°JèÕÍC!@Ä\•èeÍ¡yôIF1†¹f[Xõ“à²BS£ÉC!‰ b®J„t´˜pVT•DÈ|=öð‘;Ÿ ÒÞ 0@:ô:¢„D1W%BúZ"ÈÜ•D1Ž¿Ö*yp“'„®ï#ÈüÑB/’"æªD@w÷þ½÷A1QI€V—R…nõœ0Ãímûà‡_¿÷0g/ë$B€ˆ¹*ÐãMý$}PIT`åJ©üÁ Ÿܘ°©Þ&B• !@Ä\•èôÞ\ÅDf¾.:ñÁÀþÛ™yúàb"¼î•M$B€ˆ¹*Ðïmµ>¨$*‰$ä ‡Ýå‰P%d+‰ b®J„t}G\×»¸ÑVÅDzähÞ[õÁ{‰P%d‰ b®J„ô~'[ÝO¼mJâ)`ª´’>¸’UBÊI„sU" ¯›ÐÂüÔ¶îÿ¢$ЉÃs€©ÜK{›¡JHéù&ÌU‰€¾”¤¥³úàþb¢’Ø)»˜Jy|˜UBŠN9‰ `®J„t§®(MõEITd׳)ïƒ\ø0‰§œD0W%Bz4IŒÛ!(‰bb»5û˜Ny¼Ü$ÂKq%´ŸùÿSN"˜«!Ò£÷b¢’X°€ œ3*¥Dx)þÈ!üïY$ÌU‰€~éƒçî[”ÄÉc¢ÏW\õÁØô…ð—D1W%BÆ»uw™dÿ#&\}V€KYæ+ü-÷q¬"æªDÀ÷¤î+{9L(‰=–DßaàRøÊ×Ý÷H„sU"`È;Sûd¤EÄÄT1ÑÏa¸lI{›àžŽE!@Ä\•ïþÔÞ˜vE%Ñ·E8ëj|ÏÍšJÈ:‰ b®J„Àlk(‰*!@ÛËì·zNf#DÌU‰@ D€êëçò7|Z¦"DÌU‰ œ¨$ꉛB^܃™‡D1W%B€æ¤@1QO€V$B€ˆ¹*œH TõDX'DÌU‰ ?)PIÔ˜–D1W%B€‘¨b¢žÀ`$B ÈëëëÏŸ?¿}ûöåË—/Õë¯\ýú»×ÇŒ:W%B€ Iꡞ@/$BÞm¡‹þÿþ}û‡Þs}äõñãÍU‰€{dA•POàt! ïÓ§O‡½RÞÞÞÊãà‡P8Ì' %BZQ ë6 ROà!‰ÆöçÏŸÃ^)‹Ö&×gf®J„F%\éƒ>Ò¨'°H"„±-~¦/âÚ߇©„!i _ }‹T=€B!ŒêíííÛ·oǼR^__¾{ÿŽ£!½ë´ú‘‹6= œDCzyy¹ý„q¯”•Ÿ?øõë×_¿~½ÿlàõ_åúë+?—p€¹*0¼T)Íg´é‰ÃÙÀ0$BÀÛÛÛïÿñãÇ{ŸŒ{¥üúõkñOùüùóúw ½þîõ1‹_{}ÎÞçªDE²§¹‰zz"‡q0‰:¿4=ÿ•²øqÅÏŸ?¿½½=üÚëc+áõ9{Ÿ«!”ØŸ½žÐõDZ„qdŠD_žüJ¹÷Â’>ø¯ë#Ÿáåå¥ë¹*@¡=1KòÓõDŽZq¼D_ žüJYü‘‚[¿Mèbg¼>s×sU"€ru‰êÞW>9z¢žHñò‹£À€$BèüõÌWÊâë¾Gèâw+-ÿ(b¹*À&[ÃSalõ×@OÔ'^{±o“D_¦žùJYüôß÷ïß+žêúUû?˜j®J„°UyN ꃭþÂè‰zâ@ /öÃ’aôKÙÀWÊ·oßnŸüÏŸ?OuýªÛ§º>¿sU"€ %‘(IlõOCOÔ/ªØŒL"„±…¾R¿;hÿjÝ÷,M2W%B¨S†ìôD=±õŠŠZ Àà$B[Ü+eñ~ùò¥ú ¯_;Æ#”`§Éû`ôîBOÔ –S|¦€ñI„0¶¸WÊïß¿[ý Â-þ8ÂëŸÒé\•`}ðô‹ž8mOôW˜„Dc‹{¥üüùóö™üøQý„ׯ½}ÂëŸÒé\•`'}°‹C€ž8XOôó˜‡Dc‹{¥,½———ê'\üXâžæxî\•`?}p˜ƒ…ž¨B@6!Œ-ø£÷|_ÐÅDøõë×NçªDMèƒSVôD•#ÂØzO„×?¥Ó¹*@+ú =QR€æ$BÛÁ‰ðÏŸ?ÕOøöö&‹ôA*NôDaVH„0¶¸WJÄ3ñº–º ôé‰6m`f!ŒM"ùíS]Ÿ¿ß¹*@§ÊûÎñ}0âß…¶hK}ÿU ÂØB_)_¿~½}ò?þT<Õâ·-½>¿sU"€~•”š¾ú`ó]¶hóGz'ÂØB_)¿~ýº}òïß¿W<Õâ"¼>¿sU"€®ÕûmçDX´©„F"„±…¾R^__[}wÐÅ¿çõùû«!ôNÌ¿ÏÑm*!Õ$B[ô+åË—/û?ý·øiÄë3w=W%B€>8Þ‘BXT á_!Œ-ú•²X÷®ÞÞÞ ŸáúÈÅgèô»Œ^$B‹>8ùÁE[”•Dc;à•òéÓ§Û?âóçÏ%•ðú˜ë#[}·ÒTsU"€aèƒl:%%Bº ÂØx¥Üû áçϟטàŸ?û`×!¼H„0"}¸³mQ"ä!Œí˜WʽÒwõíÛ·———?þü}ðõÿúõëëׯ÷¾¤ßŸBø~®J„0}<ç¢>È~!t-Éëèõõµá_`ý³‡½ÌU‰Æ£ÒèÁ!t-Ïëè÷ïßMþô÷Ÿ7ìz®J„ôH¤²ú?6pK"„®¥zí¯„ôÁ‹DÀ”¤½´•ÐÇŸY$Bײ½Ž^__¿|ùRñ‡^¿ª÷ï/úa®J„ð] ýUî‘¡k9_G///å¡ðúÈß¿7W%Bˆ£VôA‰÷$B ÈëëëÏŸ?¿}ûv› ¯¿rýõëïóÉÁÛ¹*@6SUÂÅ'tð—D1W%BFw•ðÞ³9”ü%DÌU‰&wV%Ô)!DÌU‰Øj%Ô)$DÌU‰¨°§ꃔ“"æªDÔ©«„ú ›H„sU"ªm­„ú [I„sU"ö(¯„ú $B€ˆ¹*;•TB}:!@Ä\•€ýžªØo<$DÌU‰hB$‚D1W%B }æ$B€ˆ¹* 郴%DÌU‰hK¤!‰ b®J„@sú ­H„sU""èƒ4!DÌU‰¢²ŸD1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%BÒ’"æªD@Z!@Ä\•HK"ˆ˜«!iI„sU" -‰ b®J„¤%DÌU‰€´$B€ˆ¹*–D1W%Bö¿s¾ÛZ’"æªDÈη͛  ‰ b®J„ì|Û”€8!@Ä\•ÙóžyghC"ˆ˜«!{Þ3%B ”D1W%Bªß0W7€$B€ˆ¹*Rý†)Ñ$B€ˆ¹*R÷nY°ì%DÌU‰ºwK‰8€D1W%B*Þ*‹7€]$B€ˆ¹*RñV)Ç"æªDÈÖ÷É@=‰ b®J„l}Ÿ”€ÃH„sU"dÓ›dÕPI"ˆ˜«!›Þ$›nH„sU"¤üòÀ à¿I„sU"¤ü2ÇLD"ˆ˜«!…olÀP$B€ˆ¹*RøöØÿôG"ˆ˜«!%ï£o@R!@Ä\•)yoœxÎ$DÌU‰‡oŒ6Î"DÌU‰‡oŒ6õÎ"DÌU‰õwÅæÛÓR€!@Ä\•YWŒN„+|ü"æªDÈÊ[bÐöÔ‚z3"æªDÈÊ[bæD¨ª‡Ì@"ˆ˜«!÷ÞC·§“¨‡Ð‰ b®J„Ü{?2ª‡"}‘Ùs˜ì1¸7W%B§ìÛSWÔC8…D89û‚æªDÈâЕÕCõ€ $Â99=W%B§ïa™éihê!ì$NåŸÃÙçL;W%BÇp†–ô4:’'ñÏ©ì&œ«!‹Ã8-RÕCf ïŸ4 ¦š«!÷Dö¯^ÿæê¡zÀÁ$ÂEïCÇVæªDÈ¢øÎ5æ¿K=ThK"Ò);Í‘‚÷sU"dѨ‰ðIJ€( PA"ÏéûÊÁÂ\•YtT±²OÔCõ€$‘¤ÚK“ÏU‰[aÂÝ¥ª‡s’‡‘vç8jÌ9W%BnI„}íIõP=˜D8†ü»Åc¶¹*rK"i? ˆ"@×$Âô²O;¦š«!·$ÂIz¨ä'ö®»½áð1É\•¡/ê¡zÈÊå› ã‘{×ã®pø˜a®J„0 õP=œ›c’{×ïNpø{®J„0 Q@þbÓ.€!I„½ëz8| ™ä'DÌU‰àÖÓèäB !@Ä\•¶RµEà0!@Ä\•RmÂ"´%Òê€Ú'ð~®J„ÇPmÚ"TçT½÷(œ«!ÀéüèC›°÷H„ íƒH„]PmÚ"3“'Ý ÌU‰ kê¡MX`xᄎI„“ÏU‰`H¢¡M[` ál냎“ÏU‰`Bú MX„ÐÛY'-4$NwO%Â!sU"à½I¡"©-Bܽ¬ÓÚ’§»­jÚKfŸ3ç\•(4ØG}ÔÑ&,t/ëD€¶$Âén«%ÂòÛçÌ9W%Bö“µE›$Ä¿7šN hN"œë¶ªÝw-¼ÝΜsU" ZÚ|öÔ9%N[$Ὤ# ÍI„sÝVUí®­ûÙAÁ\•8—D¨-Ú„Å‘nd&ˆ Îugÿ‹ï5й*Ø11ë aQ[¤Ý¬½ $¹î¬I„$Ä\•Ha(j‹6aq”aÔ纹j‘ƒþ l®J„d¾*<`“ð„E›¶xÔ¼jH„s-I„pÔ\•È|UxV¶Pè´E›°3¬€Í$¹ƒö}¿P‰ÊçªD@æ«Â¾„<',Úfm‹!’çZ ’ᨹ*ùªp’Ï()tÚ¢­ç°è®@,‰p®Å {L"„ê¹*ùªÐ·1\!Ïi‹6o€H„s-íN„u=Ï„sU" ¹ÈFc—",Ú´BÈN"œMùÛ³‡ÌU‰…Ù¶teQ Ø*>¸åX -Ú$EX$Φp§íÙÃŽ æªDÈòxŒY²ÈTD„E›ªÀä$Âٔ캻×AÁ\•Y«KV¬€ GEæ=úh‹6hä'N(z÷:(˜«!Ëã±õ‚‘( ŽDHÚÓaÑæ‚€ÃH„ ݽ H„¬ Évk@–“€j!ƒlh‹’"TçtØG¦«!wçd‹ekCÀ!ÎR„EI$Âiéƒ:W%B֦徕 =ÀN!No´E›ª€D83‰âæªDȃY»òbÉš“ç$B›¤0!‰pr w©#ïçªDÈ­®©XL¹÷%>¶§¿¹“*ª’"I„\ZÔ=‡>ÌU‰[·ß‚oÓúHI” »K‚âÏ6JЬ“"æªDÈ­ÅÔU¸äQØ%B˜êZB"ŒN„’¢¤îÏ c!@Ä\•¹ugéñ*Fy”€’+I~ ¡ŸÀhS àl`!@Ä\•)_£»¬®Jèƒ@žë‰PR´IŠÓsô‡D1W%B6­¶]ª–!ôA ‹K›—òh“iÊ`(!@Ä\•Ùºtv±ÌH"Tm.çYÐ%‰ b®J„¬;¥Z€’9âÒFõ“%EYЉ b®J„<”¼Z}âzµ"çIŠ6×õ‘# ú#DÌU‰‹Ë&Ö€id¿Qô$E›Kû-ó :#DÌU‰ºu' n°`&£]S(z’¢mÜ«{w0 H"ˆ˜«!KIV ôD˜Ìì— Šžªhëä•ë¾€1I„ÝßP¥çUÆœsU"dëê%=¦½ K?ö’¢kü6Ëon5’DØ;‰rÎU‰ý7œ>Z¨'ÀðâkŽÝ‹¤hÛy¥ï€aI„½“!ç\•Ù0Éï÷Á•J¸² áV_O€^H„³$E÷‡DØ;‰rÎU‰Ò1^–ÿ¶VÂrnà­ÀYŽ .t‘]þ@‰°w!䜫!E3|Kø‹«„z¢8…Dè £*ÚÜp"‰°w!䜫!øÛ¼­….íʃ›vë°N"$ÉI‚¤èèm jH„ÝO‰RÎU‰òk¸Â>Øc%Ô-0Ûm¤S ØD"¤Ó“ IÑæR®-Áfጳ#`¿9ða®J„lº†+ìƒcWB=ÑÝ>gM¡äO L"džóIÑæâ—— r)["œkvÄì4ÇÞÏU‰ò ¸M}P%ÔÝÌ“óNÒ À$\`KŠ6÷(uyÉINåR¶D8ÑìÛi¼Ÿ«!åp÷ÖÖ— TB=ѽ:ÍgQž'€a¸ÆVmnb਻:§%•KÙáD³#r§9p‘ÙxõvY탗Õ4ù@"z¢[qiçŽuŒ !—Ù’¢Í-TÝ„9¨_Ê–'‘{Ì‹DÈ– ¸KA¼¬>lÿ… Û=ÑͶ¡´gwÙçp.WÚú ÍæzÜÍœ€KÙáDã#x9( Rxw)Õy!h¹@Ot/=Ãåa_œÂõö‘‰P²´¹IâÛ8G]KÙá,ã#~9( Rr wÙØ/«_’ó*ÐÚ‚žèV¹ßÛËÐ/ú¢ž^ }FÒæ.ŠG·bö3»–²%ÂYÆÇ!{ÌAÁ\•©»ËýBËè‰î„_q—ª¿pÉÏB5êÀÍŽD˜- Ž6·Y½/ðÛ9D,eK„³L3¡ãœsU"dë-óa_nÍ=ÑnÉ6ýgãMéâÓttyw}çæBp´¹kµô.±”-NB"„ÃæªDȦûßSžÄ‚zâ´÷±%?ØôÞ“öAC èî _"m¶ÜÁѧ,‰ZÊ–'!ÂasU"¤üföô§â€ÕôÄT·©%?Øtñ®²¼šH@_×ùÑbîG›m÷œDHÔR¶D8 ‰›«!…·¥©žæë è‰ÃߎšEÀ0Ëf!j£Í‡™s)["œåZçæ¸`®J„,ŠXB·,ßïÑGOr3ˆ€N×ÌØ\®#8Ú$Br.eK„³\îH„pÔ\•¹Ôò$ÂáOôD7®Ñkfi/y\·#8ÚÜl½”-NtżÓ)¹³ëå™éë¼BOtÇ P· 5Ée‹ {G7Lp»”-Ntżߴܥê‰6·ÊÀâ‚–«÷Žn}˜v)["œèŠ'r×9(p‘÷ûè‰î±€ÞÌ\A¸Aptû´KÙá\=1{ÏA÷sU"ÜΣ'ÚÜœ€kZ? ‚£Ò’'Ô|:(p;W%Bwëz¢Í-=¸FU  8Â2‰pNMv¦#+sU"p3®'ÚäExpSv¢z Ù.;íaÜàpv.„á¼÷bDÎU‰Àíö0­PFÔ!ê¶4æ”s&9/#ígÜ!’D8õí˜casU"p7=R,|€O,Ú,@°íž4àÌq6i¯ íg¼8û;ŽÂGáìwdÄÌU‰ÀÍò`}°a%ô Qm–-øßÒÖ瀓 È|±g?ãe…DH6!ŽDÌU‰À½ðx}0C%Ôm–ÌU‰€.îd«cßT•P[´Yd ¿Ý·o`˜Û4°I"¤úÙE°2W%BòߨîÌ|*¡¶h³4Óò´v/Ù½ÀH÷q®C9’D1W%B’køTByѦ-î\{ÿý÷*þ½÷¾ÄwöºQsEI‰ b®J„twÛy¹S÷.÷K%Ômòâ·ÓfÓ_»p”™ù@ç÷j!Q$B€ˆ¹*Ð×=çeµ^V°éžSŒ“mÚâÊ@()å£ÌØ:¿]“‰"DÌU‰€Žî9/}ð²ú°cî9Å8mÑ6X^¬þ?ü³o”ôs»&H"ˆ˜«!½Üs^6.ª.é§"ÆÉ‹¶‘j£>Ls»&K"ˆ˜«!]Üs^ªÕK*á0Ä8mÑæû£ŒqbßòD1W%Bú½÷ ýB÷õÈ‹6©à¬û{˜÷$B€ˆ¹*Ðé½ça_ÎYkh‹6µ˜övÀæ=‰ b®J„ôxãyÊ“ÐÑzÚ¢Mj„!޾¼r„×óö3I„sU" »ÏÓŸŠá×"mR#äôÒðŠr^–ÛÏü%}€ì[末!}Ýu¦zB¬c -ÚÔF8tY,à”ö2Ò^ZÛÏü%Î~ ä @Ì\•èè®3çsbyÑ&5ÂAëc­ÏF§7ù Ù~æ/‰pêëÂæªD@/wÉŸN\] }÷T›Ô8ûºYínw¼èŽD8ÝuÎáìs末!;éƒëá/%”mjcóöRµ»î}‰oÃ@fálì@8f®J„œhø>8U%”mRãÁ3ó²ñ_ZÒ%B’§b×ÁasU" GõA•Pm´Iq£ïRü—,ìƒ! I„S±Óà°¹*0›ãû J(5ÚlMjãâ)z)xòò>(D8; ›«!ݨ¶|*¡Ôh³¥Æ•J¨Ð;‰p®;/{ Žš«!<¾KݾÀ®¢6Ú|'UhB"œëæËƒ£æªDnQk?€Sý-ÒÈm²#äꨗg¦ká\ƃ£æªDnQw|ƒ¾òJx™ ©Ñ¦9;¯Ž’?-çš0ö5W%Bxp‹ºïxVÂÞÉd¨6Ù¦½:ú'ës2 ‰p® cÁQsU"€·¨;úàeõK,‚î+m²#¤½:jrÂ{±N"œnÈØcpÈ\• \EÜù…´=X 5ÚdGh¢áùéTç!‰pº cÁ!sU"€B;3ŸJ8ÒqµÑ&;B“ÓÉ9I ‰pºñb§Á!sU"€MŸJè©Ñ¦92’'€ó‡BáŒãÅNƒø¹*ÀC ÓžJHÐ ‰tx»Õý–Í&;²Iõñr )'Î8[ì7ˆŸ«!¬kõTB2ŸÛ Ö àsŽ6Ù‘êwKÕî½÷%®ŽX$ÎÉ~ƒè¹*Àºˆœ'2ök„T}ðøJ¨9ÚdÇÙ&ÿeãÞ(郮ŽxO"œ“]ÑsU"€uA«UÁàáë‚V}°÷J(;ÚdÇüãúRü—/샮ŽxO"œ–½¡sU"€uqKUÁà€—­>¨ÊŽ6[óæxﻦ?üãÊû «#Þ“gfïAÜ\•`Ñ`}P%”m¶¸­¤êƒT“góÏáìs末!DKÒUB”,[¶Ï3ꃔg#Â1sU"€^´ |*!š£M¤#ál$B8f®J„ЋŠeöM?2LðBv´éƒ$$NwÁ#Â!sU"€.T/³oª„;É[ÈŽ6}æ$Âé®y$B8d®J„Ð…=Ëìå•0m Ùq¼Md+‰pºk‰™«!taçÇp +á ´-dÇl•ЄgD8Ý5D‡ÌU‰º°ÿÛô•TBêh[hŽÕ•Ðá!‰ b®J„ÐêoÓçûûõ~”a¤ìhþPH"ˆ˜«!ôegæS eT ¹‚DP±W%BØ„º'B‡½‘ÓUB.%TìU‰8Ó*}P%ä"!@Å^•€‹|—ö¾K'þ“JÈ_I„\}^¦'öªDœïŠ>¸?ù*!§I„Ü~LæÇ{U"Îw]ÜŸü0‰$BV< c„½D\îê>¸?ùÁ!ß‘qP±W%Bà|·ôÁýÉß"²H"ì¾sœÔìU‰¸Î}ðÆßHCa[ÎJ÷ªD\áÆÌ§r&‰°-g¥{U".µJàS 9‡DØÓíÃtpz¯J„ÀEVL{*!%6´î,îU‰8ßêQO%ä4‰°¡Šé9ø²W%Bà|9O"䉰›º¹9ø¼W%Bà|E-O"ä;a7¥ss(°—€ËÕ…<‰Ea7ÕCs( N"leÀМ H„„“[31炽*L"lE"„a{U" –DØŠDÃöªD@,‰°‰†íU‰€Xa+! Û«!±$ÂV$B¶W%BbI„­ šs‰€pa7ã¡™Ós¯J„Ä’»)›C½D@<‰°›º¹9ø¼W%BbI„ ULωÀ—½*K"lhÝ:XÜ«!±$žn¦ã€Ó{U" –DØ–³€Ò½*K"lËY@é^•ˆ%6ç h¯J„Ä’qP±W%BbI„¬x@Æ{‰€x!·“¹Áñ^•ˆ%rõy™œØ«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$žVŸžã€/{U" –DØPÑèœ|Þ«!±$†$B°W%BbI„ ÕÍ͉À^" žDØMéÜ ì%BâI„ÝTÍ¡€D@8‰°‰ÆìU‰€Xa+†æ\@" œDØÊ˜‰9ìU‰€da+! Û«!±$ÂV$B¶W%BbI„­H„0l¯J„Ä’[‘aØ^•ˆ%¶"°½*K"lE"„a{U" –DØÊ€¡9'v#˜½*K"ìf|"4szîU‰€Xa7¥ss(°—ˆ'vS7:‡Ÿ÷ªD@,‰°¡Šé9ø²W%BbI„ ­;C'‹{U" –DØÓíÃtpz¯J„Ä’ÛrPºW%BbI„9 ¨Û«!±$ÂæœíU‰€X!Ž*öªD@,‰Èa/O"äöc278Þ«!±$B®>2#‚{U" –DP±W%BbI„{U" –DP±W%BbI„{U" –DP±W%BbI„{U" –DP±W%BbI„œR†çïU‰€X!çÐ¥¿ÝTé¼W%BbI„¬5^ÇŸ÷ªD@,‰°³uÇëDàó^•ˆ%¶U1^'{‰€xaOEãu"°—ˆ'öT7^‡!á$†Ο¡DÈŠ›Ü!á$†ÎDÈŠ~üø!@‰°›‹æ¶J"t.¼¿¿÷¹1$BÂI„Ý\4´ëÆë\8vü]F%B¸‰°›‹&&r»Ýn÷üüÜê +N"låÒ‰I„ÜèõõõøGJ„p_a+—ŽK"ä"»Ýîí¿~ÿþýÝ;%B¸;‰°‰¨'øÜ{U" –DØŠDHÔ|î½*K"lE"$ê >÷^•ˆ%¶"õŸ{¯J„Ä’[‘‰z‚ϽW%BbI„­ H„Î7†D@8‰°•K'¶J"4vö!$‘»‘I¸ñ$B¸#‰°›ÒDèPpoì%BâI„Ý\4´[z¢C᯷ÇÜ{U" –DØÐùs»:&:ιCæÞ«!±$†ÎÝu%щpË]7Í^•ˆ%ötæ¯ÈˆŽƒó÷ªD@,‰°§3Çxb¼ŽƒÛo¹¹÷ªD@,‰°-gÁÝï·¹÷ªD@,‰°3Á}o¶¹÷ªD@,‰°9§Àï´¹÷ªD@,‰uOÇ09ÿ›{¯J„Ä’Yë€ÌKo°¹÷ªD@,‰ÛÉܸîÖšï¹Û"ré‘'ÂÛÿ¯ÞE@*ï" ‡D @‰@"€!0€D9$B`‰rH„À!ä$BÈ!²Öš —n€¹÷ªD@,‰°§«§ç|Ëmw¯J„Ä’*탂uïºíîU‰€XaCÕ}ÐYpæý3÷^•ˆ%64&:þzÿ̽W%BbI„Ý ëƒN„¿ÞBsïU‰€Xa7!!7žD÷"v³n<çÃÌœ½DI$ÂnÖJ„ç°™³— ‰DØÊŠßeôü7vö!$‘[¹n\—ÎÙ¡pæ˜{¯J„Ä’[ð½ï5êÖjÿt–'¶2&°ý­%J„$“[Y%ýAÌtkI„!É$ÂV$B6ôßú^•ˆ%¶rã÷ •ùßú^•ˆ%¶"°½*K"låÒ‰I„põ^•ˆ%¶r{"¼î2yîU‰€Xa7çOì– ;ìU‰€Xa7gí– ;ìU‰€da7çŒîÆñ:ìU‰€daCÕãu(Ø«!É$†JÇëP@" œDØÓ°·:ÚîU‰€Xa[ú ”îU‰€Xag!ÔíU‰€Xas+ŽÔ‰Àç½*K"d¿FÝsðe¯J„Ä’*öªD@,‰ b¯J„Ä’*öªD@,‰ b¯J„Ä’*öªD@,‰ b¯J„Ä’*öªD@,‰ b¯J„Ä’*öªD@,‰pë¦ù[+¼L¶W%BbI„[7Ç_Ü›@™o¯J„Ä’·n‚¿»ïË”{U" –D¸u[ÿëûQ’̺W%BbI„[·é 8>&Þ«!±$­ÛîsïU‰€XáÖmqŽ{U" –D¸u››†ã£É^•ˆ%N`+3qv´Ú«!±$Â9äÅÁÑm¯J„Ä’§;§FϽ*K"œIÔ”Í÷ªD@,‰p>wŸ•ÃÂ^•H&Né.ssRðy¯J„Ä’'V=Cg'öªD@,‰pz1œ­öªD@,‰° qïU‰€Xa+â Û«!±$žTïU‰€XasæE{U" –DÈ-ÇdbðÝ^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%rþIœ¿W%BbI„œ@—þvS¥ó^•ˆ%²Öx|Þ«!±$ÂÎÖ¯Ï{U" –DØVÅxì%BâI„=׉À^" žDØSÝx H„„“:†!\½W%BbI„ ?:‰®Þ«!±$Ân.šÛ*‰Ð¹Ðs¯J„Ä’»¹hh××¹`¯J„$“»¹hb!\½W%BbI„­\:1‰®Þ«!±$ÂV.—DWïU‰€Xa+! Û«!±$ÂV$B¶W%BbI„­H„0l¯J„Ä’[‘aØ^•ˆ%¶"°½*K"le@"t. N"låÒ‰­’ž{U" –DØDcöªD@,‰°›ÒDèP`/O"ìæ¢¡ÝÒ Í÷ªD@,‰°¡óçvuLt"Ø«!±$†ÎÝu%щ`¯J„$“{:s€WdDÇ!á$žÎã‰ñ:8½W%BbI„m9 (Ý«!±$ÂÎÔíU‰€XasNŠöªD@,‰uOÇ0A" œDÈZd†ðy¯J„Ä’¹ý˜Ì Ž÷ªD@,‰«ÌˆàÄ^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!Õd¶ôÜ«!±$Âæ íU‰€XagêöªD@,‰°§‡L›ž{U" –DØÐÃXNϽ*K"lH"„{U" –DØÍÃpfNϽ*K"ìÆaÌ^•ˆ%¶bn0l¯J„Ä’[14¶W%BbI„­ Û«!±$ÂVL †íU‰€Xa+&ÃöªD@,‰°ƒa{U" –DØŠ‰Á°½*K"lÅÄ`Ø^•ˆ%¶bb0l¯J„Ä’»11³W%BbI„݌٫!±$Ân ÆìU‰€XaCæöªD@,‰°'sƒê½*K"ìÉè z¯J„Ä’Û2=(Ý«!±$¶LJ÷ªD@,‰°3‡u{U" –DØœC¢½*K"ìÌ¡@Ý^•ˆ%öô0iÓs¯J„Ä’zËÀé¹W%BbI„ I„0`¯J„Ä’»yÎÌé¹W%BbI„Ý ŒÙ«!±$ÂnÌ ÆìU‰€Xa+†ÃöªD@,‰°Cƒa{U" –DØŠ‰Á°½*K"lÅÄ`Ø^•ˆ%¶bb0l¯J„Ä’[11¶W%BbI„­˜ Û«!±$ÂnL ÆìU‰€Xa7&cöªD@,‰°Cƒ1{U" –DØ¡Á€½*K"lÈÜ`À^•ˆ%ödnP½W%BbI„=TïU‰€Xa[¦¥{U" –DØ™éAÝ^•ˆ%vó0œ™Ós¯J„Ä’»‘aÌ^•ˆ%v#˜½*K"ìF"„1{U" –DØDcöªD@,‰°‰ÆìU‰€Xa7!ŒÙ«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$Âiœ9‡á<Ëè¹W%BbI„Ó!j¯J„Ä’§!BÔ^•ˆ%NC"„¨½*K"œ†DQ{U" –D8 ‰¢öªD@,‰p!DíU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€X!@Å^•ˆ%TìU‰€Xá4L¢öªD@,‰p¦Q{U" –D8 Ó€¨½*K"œ†i@Ô^•ˆ%NÃ4 j¯J„Ä’§aµW%BbI„Ó0 ˆÚ«!±$Âi˜DíU‰€Xá4L¢öªD@,‰p¦Q{U" –D8 Ó€¨½*K"œ†i@Ô^•ˆ%NÃ4 j¯J„Ä’§aµW%BbI„Ó0 ˆÚ«!±$Âi˜DíU‰€Xá4L¢öªD@,‰p¦Q{U" –D8‡TžeôÜ«!±$ÂiH„µW%BbI„Ó!j¯J„Ä’§!BÔ^•ˆ%NC"„¨½*K"œ†DQ{U" –D8 ‰¢öªD@,‰p¦Q{U" –D8 Ó€¨½*K"œ†i@Ô^•ˆ%NÃ4 j¯J„Ä’§aµW%BbI„Ó0 ˆÚ«!±$Âi˜DíU‰€Xá4L¢öªD@,‰p¦Q{U" –D8 Ó€¨½*K"œ†i@Ô^•ˆ%NÃ4 j¯J„Ä’§aµW%BbI„Ó0 ˆÚ«!±$Âi˜DíU‰€Xá4L¢öªD@,‰p¦Q{U" –D8 Ó€¨½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!±$B€Š½*K"¨Ø«!p‡ú >‘*öªD\@"d,‰ b¯J„À$BÆ’*öªD\@"d,‰ b¯J„À$BÆ’*öªD\@"d,‰ b¯J„À$BÆ’*öªD\@"d,‰ b¯J„À$BÆ’*öªD\@"d,‰ b¯J„À$BÆ’*öªD\@"d,‰ b¯J„À$BÆ’*öªD\@"d,‰ b¯J„Àÿ{¸à‰ b¯J„0­‡m^ð‰DP±W%B؆‡6|"TìU‰îàÁ%r‰ b¯J„p+QO"¤ŒDP±W%Bø…N"$‰DP±W%Bf&·I„lœDP±W%B6C;“éG"¨Ø«!÷!„mçúÏ'!ƒI„{U"€mð5yÜ¢®‚ÞwëˆÁ$B€Š½*À6øš<óÝr®€Þ'’O"¨Ø«!lƒ¯É~ÿ¸¶Ùû$BòI„{U"€mð5yFÞ ®6½O"$ŸD!>>>þüùóüüüøøøåÆ>üÊá×ÿõð1µ•½*À6øš¼“ué}!-I„pwoooÇYð;‡<|üÿšÇǹ÷ªDÛàkòŽÉ¥÷I„´$Âív»óãà—P¸¹wþøñC"âøšü”3wé}¡uÄßH„p/ïïï7Þð‡GØô_vî½*À6øš|þ]zßÄYÐ:â~$B¸‹Ûûà¶*áâ›%çÞ«!lƒ¯Éž†KïkÒû¬#âI„0ÞÇÇÇŠ·}øwÝívÏÏÏ­ž°!lÉÄ_“—Ò$?îØû¬#âI„0Þ‰Ÿ?øôôôòòòù½‡ÿ}ø•ïŸø¹„±Ó×××ãA(A¶ò5yMïc[½ïræÀ`! öòò²x÷þüùóôw =ü×ÃÇ,þÞÃc&üÕv»ÝÛýþýû»wJ„@–»$BMïCç:bt &Â`‹ïªûùóçn·ûëï=|Ìb%<<æÿ)í {´W%BØLïCïË`Ú &ÂHß½…ðœ>øÃG.>Âëëë=ÿ)í {´W%BØáLïCÊà€L"„‘¤à¥ß&t±3ùžÿ”ö„=Ú«!Œ£—é}ÈIÛçLL"„aßxÝ÷]ün¥ç¿1ä >÷^•à,ê˜Þ‡Þǹ L"„aßý÷ëׯ+êð»n7âÝŸàsïU‰€ùiazzëqç0˜Dýàn$Bãíím­DøÅGxøSB?’€¯Ÿ'H„¸i]ÚzÀÝH„0ÆŸ?ŽïØß¿_ý€‡ß{ü€‡?%ô³(‰øúy‚DÈt·œË%ê}Û!‹Eïõõõê\|[â-Íqü˜{¯J„ð·Ï$B’îWã0'ê}=I„0Æâ¼åû‚.&§§§ÐOæ$Bàëç ¡ƒv¹"ššD¨÷ô$ÂcááO ýŒP"¾~ž :#—º—³ÌDïhI"„1áûûûÕ¸Ûí$Âä½*Àß>O7>^—º7ÍûÝš ÖëÿaÐ'gw솞!pôy‚Dx¿É¸Ô=fI„^L¸šDƒ>Ó%Bà_Ÿ'̘…*—º'ÞpÄ^I"„AŸ&J„!ð¯Ï¡PåR÷$Boj ‰}î+J„À¿>Op¹>$B¸‰}î+J„À¿>Op¹Ô=ŠÛ_Î:€<! úÜW"ÜÔv•ôO(€n¤+—ºÇí&ÛZ^$‚D(Vü_/$B¦ f¹Ô=’ëÞVWI„ J„!h[.éŠùêÞ* €ž$B%B‰€m‘ºÔ=¡VätèI"„1×½cw»Ýñþ‰0*ºó8ãŸË.uî,ÂûrÄô$‹‰ðíííê<ü^‰0y¯J„Éaêêä> '‰Æ%B¶DøøhÃ@O!Œñû÷ïuáëëëñþ”Я¯H„Üóߣ.‰u¾åv '‰ÆøóçÏñ{øÅ«p±9Þò€ã7ÀÜ{U" ×€/Ëæo`רZ'ª{Â=@O!Œ±ø}AoyÓ߯_¿Ö}[âø 0÷^•È%ÒçVte§7‰P݃ž8ô$»ÝnݸøÃ Jè—$Bza–Ùæ}åš.¢I„êw$Â0?~üXñ¦=~¨Ããç~ÅH"€ f‰{ß!.9L"T÷¸7‰†yzz:¾ißß߯x¨Åo[zxüܯ3I„ôÂ,:k—Þ'ª{t'Â0///Ç7í¯_¿®x¨ÅDxxüܯNI„ôÂ,:8—Ø'ª{t'Â0k}wÐÅûÿðø¹_Ð’ è…Y"lp.±O"´Žà$‰Fz||¼ýÝ‹ïF<<òZÏÊa`î½*Ë×ä·5L—Þ'ZGP@"„‘ëÞÁn·;ó¹øW|—Q‰°t¯J„äò5ù;NÆ%öu‚Ö$‘a°?~ߺ?þ<§>æð‘¥ß­T"\k¯J„äêð5yÕLìc|ﳎ`S$Bì»7þüùóô|_ìƒ×½…p/ïU‰€\Ûúš¼j¦÷±‰Ø×aÀ\$Bï»Òwðüüüúúúþþþ¿>üï———§§§ï~Ë?…ðô³rؘ{¯J„äºã×äU3±YcßUÌîH"„ñ>>>V¼íO¿÷ðŠgeÈ|ë{U" —à%ö¡÷e0F¸#‰îâíím•{þóû ×zV†<Á·¾W%BrÉdzb_À;’á^n¯„·ôÁ½DX¼W%BÊþaíûû&áÈàŽ$B¸£ÇÇÇ+nõÃïºúû‹þõYòßú^•ø÷‹¥KìCïã+§ w$Âݽ¾¾ž ùööVú¬ y‚o}¯J„,=Uê/`‹Ï\—Þ‡ Ô•{îH"„þüy~~>Î…‡_9üúá¿ÞþÎA†íU‰¥×B‰æzƹÄ>n㎀;’*öªDÈÒk¡DO—؇ÞG 7!Ü‘DP±W%B–^ %BÜÞ.—Þ'öA"¨Ø«!K¯…!-oK—ê‡Ø@‰ b¯J„,½J„lç^r¹$B½€©I„{U"déµP"t¸\IN"û@"¨Ø«!K¯…¡³s¹":šD(ö€DP±W%B–^ %Âéî’ö6°ŒHï =‰ b¯J„,½J„1ƒrI{ÍßãÖuÔ^ˆà$B€Š½*²ôZ8{"T \ºžD8êÔ¼¤À$B€Š½*²ôZ™(—´'Þûô½>Àx!@Å^•Yz-t¹¤=º$B œDP±W%B–^ ].]‰ˆ TìU‰¥×B—KÚ#ãgóI„ОDP±W%B–^ ]Òdü >‰Ú“*öªDÈÒk¡K×CÚk³Ž€l!@Å^•Yz-tI{H{Œ*öªDÈÒk¡´bS§H„{U"déµP×CÚ#‚Ó$B€Š½*²ôZ(í!íÁ]H„{U"déµP"D‘!‚*öªDÈÒk¡Dˆ’B76 TìU‰¥×B‰PÚƒž €DP±W%B–^ %B]"x®!@Å^•Yz-”åˆ TìU‰¥×ÂŽ‰Ð±@ ‰ b¯J„,½n&:+˜›DP±W%B–^ ë/€3H„{U"déµP""H„{U"déµP""H„{U"déµP""H„{U"déµP""H„{U"äØê2p‰ b¯J„“€!@Å^•9&!$B€Š½*rL"BH„{U"ä˜D„*öªDÈ1‰!TìU‰c!B"¨Ø«!Ç$B „DP±W%BbI„{U" –DP±W%BbI„{U" –DP±W%BbI„{U" –DP±W%BbI„{U" –DP±W%BbI„{U" –DP±W%BbI„{õÿØ»Û"Õ•- ÀWÂXÀÂhÀ°€,` hÀB,`aß®3U»v BVX¤Ÿç×9³!,ºó¦;"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"ˆ¨«"BÒDÔU!i‰"ꪈ€´D„uUD@Z"B€ˆº*" -!@D]–ˆ ¢®ŠHKDQWE„¤%"„¢ëºãñ¸Ûí¾¿¿Tå/åïå_Ëkìã몈€´D„4îr¹Ü‡h}Ê+ËëíÑ4÷[µîº*" -!ͺÝn㣴_±ZÎñwÉ÷èëëKDˆiÓõz}ñ`+K°G/n޺몈€´D„4èõ4-[J˜ªÃ×]WE„¤%"¤5]×ÍxÈe˜q4ùÝn·Ýn×Ô +" 9!­xZßv»=Nÿޤ+ÿ]þRþ>ð?{4à|>ß?‚PDï%"¤)§Ó©zäl6›á96Ë¿–×Tß[–i~Ün·Ë‡CßÈA!¼ˆ¦TG´m6›Ûíöð½å5ÕL­,Ó9aï몈€´D„´£oÀݘ4íGyeu çó¹ñ=rÂÞ×U!i‰iGõ|ÏNªYMåÊ’ß#'ì}]–ˆFT‡ËM›#´:·çø{«Ü#'ì}]–ˆFTÇÊí÷û ‹*ïz}ìÞÊöÈ {_WE„¤%"¤»Ýîþ€¹^¯UÞu¿¨²|{ôlXw]–ˆFTçÒœñxž6Ãg;{$"€V®:v¯,y®3âs÷hÂþ:&`ÝD„¼]5 +n·ÛÈ%”WV—0aNÎYŽáT{4a“°n"B2øúúº?l6›Í˜L­¼¦¼2tnÏ Çpž=š°¿HX7!ô »Ûl6ÃÞ»^¯Õ4mò€»¹Žá<{4a°n"B’èËÅŠÝnw>Ÿ¯×ëß—ÿ>NÛí¶ï-“ŸÙ7ã1œd&쯣ÖMDH]×ÍxÈ Ô›pF|è9€{"Bò¸\.³oÿŽÎ›ëŒøÐ=r:÷D„¤òz¦öJ>øgîˆðí{ätî‰ɦëºïïï ‡Yy×äùEžºGNgàžˆœÎçóøX­¼òr¹„žºGNgàžˆÌº®;»Ýî>\+)/ÿúúÈA{4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„°¼®ëN§ÓápøþÏýOgùãn·+¯ñYÁG»Ýn—Ë¥œìåŒ.çõ×××ýÉ^””—-S|ŽÇãÏÆTËNù×ò_(G-”£²1çóù§_¶Ùl~mIùKùû~¿/ý2 $X¥ëõZz@å4¸8óS‘J­(C#õÐŽûöÞÂPú•¥QwßÞó +Sún¥›y¡é¡Ò+,½Ô Mªöy«Ê+u?A9 *Gc”UkrZŽÊöl·Û§¶¤lüñx Ê€%›å\ß9úU溗ûCi¬ˆ€vÜgs‹­ºëºÒŠó ëVÎôÒË{½ñ¼Ýng¼Sýv»Mëÿ–w¹a”£åë@©Z}÷SùfA9zÝår™pMþ_‡ÃAPŸXŽž½g»OYÈä ð£i¬’ˆ€FToG_`½¥óøJóÏÍ6­ÏçsPé{Š;TA9š¥7péÞ7 ÊÑ»f¿m$h¹ýÜQ9ánn¤áÔpy€ÏUD½ÒÒm|ñ5_´Ü´~q›×óA)!(G³”£ñ†g]ðÍ‚r4¹Ýn·º8ÊÑý¼£Ï¦„ŸÛHéáò&ª4Øú.7…®·´Óü†‚¦õ‹&?°ëº7Ã$6 -ðˆÒãñ¨]ÊQD9ο¿¿Ký)‹ý÷Riü”¿<œ–Ð㛡år4!%üÐFN —7øPçóy O·Þ‡ùàv»-ýÍÒûuáýz½þtEË ü†Â:šÖÏ÷_ÃñÊÿ–ZñðA¥Óv3ðüÁ²=e½ÿnÌÏ–ü”¾Kg¾_PŽBŸ½UÚl®-€rTŽúÚE#æ5üøBÏ%„.GåìÞï÷¥òüºOà§"•öIù×á[Ê ÖÝH£ÍSCDÀ'*¢ËJËêa *îj8ôt{h¤i]ªÐÈû9‡ŸŒSŠÆ\U¨ô‡g -ÿÚwÌ 6 M(G#œÙ7 ÊÑ„rÔ7BùÙ¶M_³üÝ WŽ~n›q¦Ô® pü¤+×H£µSCD@S¿h[2p«4焃°úBTzŽÓÒ´» ž-ÕÞëÈ9púfâ*ËôƒrÑ’)Ëùìfß,(GÏ–£ò²ï}êK MÉTŽŽÇã´öÌÀ”Åe™«l¤ÑΩ!" Í_´ˆ-1š5¹£÷×,w¹÷uÇ÷û.¦Ïgß2(Gónjõj[߼ǾYPŽž-GÕvÑ+3¨Woi0~Z(G¥ñUåƒi4BD@Ë¿h³oFéVW4<³À¿}Æ'°ª^Z¶ÃX½žV–ì åhÞµÜÉùòìÚ0K9ª¶‹^¹ëiöÌø,}7d®¯‘F#D„´ü‹6ï6t]çV.àEÕ›BÇ_wª^WŸ6GhµãiúPŽf\EõÞªŸùú\[f)G³·gú:}¾&hD†"°@#vˆhùmÞm¨>™b¿ßûv€ñúžg:òíÕ›Z§¢ò.÷<€r×|ªÖ«¿/¸¶ÌRŽ"Љ{{ˆn¤áx  ÷¬êd___FÜKÖ«ê½ Óæ:®vø–y†go¤Ñ2!-ÿäÅ-ü¾•XºŸ¥ÁVú§÷ybùãv»-ÿjà!P¸fdq¨^{e^ÐjDxÿÈ0@9z¨:#ÖÀuu×€¹ÊQµþüÛ«Že¹Ãv ¥ÅR-DÕÉÒ?®‘FãD„´ü“7Ë’‡GÙ”þfßó/ªJKÏm¨Ðrß³ZF¾}™ˆÐ¥0PŽžu»Ýž½×ݵ`ÆrT]ȯ; †›L}9ã[Ò`¥&”Ò1p=ç-ãgo¤ˆ€–òfYru¶™Ò˜,½È9m…¯\Õ>TõvÐçÑzånÒêU}!(GÏV’û)^Usm˜·=L za¥swŸúõ=ÐИX“§®Ø¼ò4‡<4ø#" íŸ¼Y–\}ø×äpð_ûýÞí¨ÞoðÔÝé…Nó”£ËÜß~?fV.Ř½]¯×û;úžN§ŸµT»{ïš]ˆ3¦8”sÿ½ó>Í[ሀ¶òfYò,i`¶™+€…õÍ^õÔí "B I9úk¿ßO»Š¥øó–£¿ª‘ßÀX!£u «ÁÈç–~\UÄ‘/" ÙŸ¼Y–J ¡ÕYø~ú¡OÝ*"’”£ÕiýFÎ˧ø€r4c9ú¥ëº‘à ï¯Ãªk5rau:âO¯Š8òE„´ù“7Ë’ö.Kƒm·ÛN§Ëåò«åVþr>Ÿ÷û}õöÔ¿JÔ7+¶ÝngyÀˆHRŽþôÜè>~n.Å”£ Çÿ•.Øä›ûa5"úz"Bàíå¨:Ö„å(> Íu%¼¼·ú¼ø1Ë,›4<¨ÐUzhÊápXò€òA " åŸ¼´K®öCÍ5 «Ñ××+^y´ˆx{9šë¢™âÊÑ,­£¢:ðç©Ò4šëšRb\êÌUExØêÐÈO^Ú%WïL‹hsËèë½x÷iõ Ø+ ¬Þu¯r4¾]4y×@9š¥uT]ò´{/ûFe-¾DhDµ‹4û@¸ªcZÝ"BùÉK»äËåRJÂ÷Ÿ.´¯WK=™·‰A9zûE×@9©:úoò}›ê{„vT§UŸqÞ'ù KÒ\ åŸ¼Y–<û°?ý·¥ùá£E÷õD„ÀÛË‘ˆÈÓ::ŸÏ÷‹Ýívl !´#´—$dašë´ü“7Ë’_¶Å ¼ÅÀ³æçêëUç¿z%"¬^Xó\TPŽÞ~‘A ”£1ª#}&O€<Ü|1y>Hõ¦î¯¯¯è3Â[Zï>grþäͲä곪_¹&½ÁÀòúz“§ººw<ïWQþ8yÕÌñ•«/G"B O먚åu]±åž M‰h,Óg„·´Þ}ά²ýö#躟TXêì}½êŒ7¯ ú«nùë·@+.G"B Oë(¨b\¯Wµ7{X¬Ïoi½ûœX_ûí¯ê5ùÒº{e™qÓV «4êëUKÇ+ŨÞ{¯‹ ÊÑÛ/2¸æÊÑ{û€j4nÞ"°dŸÞÒz÷9°²öÛ¿"â¼Ð‡_Ë(Å¡ôéªÅ'îi5¥øÌXëÜ«ÊÑúZn@;åHDD˜q(ñ'6ÒX!-ÿä͵ðj‹î•gIW(öâÈD`I]×½¥¯·Ýnç*GÕ{Êò}¹ }zË h¤U×5Ë”éj´¬úÐÀ 7uh#•ÐòOÞ\ ¯NÿJ‹®z‘ÿ|>ûá#ôÝVZ”îäòÝÕi7T+[ôö«)G™[n@#å¨:eúëO7å 4®:/è³}®Ïm¤±2"BZþÉ‹îðv]7aiå]Õ¥™‚>B5¤[,è¯i³ƒÎXÖ€ËQæ–ÐH9ªÞï´Ùl^\luÊ—Ãáà‹†ô]±yªš}t#•ÐòOތ˯ޡ:íVÒêB³LÀG¨^2z}òá×Ëѳw¢Î5yÐr9ÊÜrZ(Gçó9b®Ñ꣟?¢´A½­§Z)ŸÞHceD„´ü“7ãòûî{önÒ¾åÌòÈ Tu™Ÿ›Õ—ÜWFÆoCy¥o@9ZwË h¤Uã¼òÇÉkw|¢Ÿ‹3¯—¾š6~–Ñ4ÒXßÙ!" ÙŸ¼yWÑ÷˜éñmž ûÛíÖ7ÉUÇÿþ\2Z¾¯W½6²×Y^S­fÓf+/G™[n@ åèp8Ìx5¾ïîç„OiW”š0ùÑ }éÞø»1×ÑHc­g‡ˆ€òæ]Eõ±õÛ{íÐÒ¬>)ÃSᣋ̻¦î»ß`³Ù ×¢ëõÚw·ƒ!„ ­¬å4RŽúÚ6ÛE#»{îç„+Gåô?ã+@é UoÂüQÕT#~¬E„4ò“7ûZúnRýÛy, Ëç–/ÿ}>ŸnEó¬jЮžVµú®†ýtBKaùU‹Ju껩ÕôY -ßÍwm”£¹ÊQßп¿CŠÞÙu¹a•åèë뫜ÝÇãñr¹üz`ùKé" _®yö&Y ~¬àƒ~ѦmÉÃäS Ûíêiu©ëº×;y6@9šq/|¹ M« }, Ü@u»Ý.—Ëñx¸‡J>—£g‹€,?ÖðA¿hÓ¶¤´z‘òAP…«K³?å×ý´€r$">®Ïç÷FÀÊÊÑ„" ‹Á5|Ð/Ú+Û3ð`Á‘<òT¡×ëÒë)¡|”#!°Žr4ðÌåg•îžïZ.GÓŠ€,?ÖðA¿h/nÒårxžõð,7îGUh®ºÔuÝ÷÷÷„u•w™_”#!°²r4üøø1 $7sÂÇ)ýšãñ8ËM¯Y ~¬àƒ~ÑfÙ°Óé4¾ºÛí\U(¢.ÏçñA¡k_ ‰—£Ûí6!,(5 $øt?Yᴧü^d1ø±€Æ[¡¿.Ñ}}•¿ì÷ûóùlä °L-*}Ûû¸°ü¥ü½ü«€všF§Ó©tÇJCè>1üþÏáp(5Ÿ¬Ïõz- œã?'{õÎÉív[^àöVLDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Ð!4EDM@SD„Dt¿ÿ³ßïÇãõzõ|t/OD ó8Á~¿ïºÎ‡ °Ž^žˆ@çq¤ív+(XM/OD ó8Òñxô©¬¦—'"Ðyc·Ûù`VÓËèd€÷öòD„<ìŸçš µëºò½ìv»²ä__MY{ùcÙ’²=óî`Ùþêþ?ŸsYé,û¸äºREŸû•ð–^žˆ€‡Çi‹ê›kôõUw]w<·Ûí×××}x1róN§Ó¯·÷)/‹˜µìÅ~¿ßãÞl6e3&‡2å½÷±àÀ.‡ ²ƒ»Ýî©« eË7[Þ˜y]o9Š–9;Þõ1°|/ODÀÃÎã2Kóâëõú0Âx¸U—Ëed¬ó+â™kxÝív;“»Þåx*‘™¶¿?ŽÇã´|6iºª6òÓ^r]o<Š¢ÏŽw}Œ¼«—'"àaçq™¥ ¿x|„1¼I¯dsEyû‹Ÿðõz½Ÿ¼1®þÔ@žDòÙ\ì"Ã’ëzïQzv¼ëcà½<!;Ë,màÅçóy–žé‹ã¤¦EfÿZ2‹¹Ýn³d‘OíòŒ;øp—\×Û¢¸³ã]#ïíå‰xØyœ¶¨®ëª->»êgÇÁõ-ÿÅ‘_¯%\2‹ÈËWP>ÒËåòï£˶N§ívÛ·ºò–‡;X80ÁæýJËÿ–•$nIÖ•á( :;Þõ1ðö^žˆ€‡Çi‹ªnúþþ~jÕÆÁU~¹\Þò+ )ÿQþw8|yö±kYÌÃ8¦úÌ»áÕõ…}Ÿ-80êÃ]îKŽÊ^Œ9Zʱñú€Ðˆue8Š‚ÎŽ·|Œdèå‰xØyœ¶¨jú00rjdÇs³ÙÇ_ÙÊÏ ¸Ÿ5Þ/ùv»US¶ŸùÿMåîߨ—¡”¼ñÞ}˜2>¶ûó_8õkK^\éV>·ò)ÙÔ¾ˆ#@ÿô -F®ôïnþûAeXW’£(âìxËÇ@’^žˆ€‡Ç ËéKÎçóä~ë÷÷÷SáÅ_}“CŽ*UÇ'ŠxZÜS{T>Õ¿aÓ³Ÿ|ùûøõyøÄªÃå¦ÍÈú÷ãʰ®$GQÄÙñ–€$½<!;–SèrxÚð<œÏNìùW_ÚµÛíÆ/¤oØÈ!`}£Ï¦%žå£ø$«›:áÓ;NOÍ[&ùT.ù¯òÆÍf“a]IŽ¢ˆ³cù€<½<!;Ï.¤š.= 4ðˆ·§¦ô³1ÏN:aTÝ_}CTNVBøTŒõðéûÜ–¼J°ü‰·Eg‡ ;-÷òD„<ì<>µ„¾ Ž–zeÇÕÇêyüߘý3lª: r`8Þ+ªSGNV]Zß7²îˆðíGQÄÙáÂ@˽<!;#ß{¹\ªSŽÌS"º™}ã¶& ¼š¼¨ê»&Ï›:ì~FÓív;yiÕñûýþí»¹äº2EŸõ1ª—'"àaç±ïÅ]×].—Óé´ßïû·7~®Ëˆnf5äš<‚¯€ÏZ>ŸgŸÉ8Ùõze¤ÛðÆ÷}zÕà•€rÀ’ëÊp Œ¤ê剈îH‡Ã´¬mÉu½ý(ú.ù1ðé½<!€Îã€ív;c±;K.pɾóÿ–2° ÕÉNG6ÏοºØºÞ~…HK~e|h/OD óXX´Ýn_[$"üˆŽÿÃí?“—üõõõTê´ÌºÖ.ü•ð½<!€Îãb}=áGtüÇl×u“Ǧý O?çëZ}D¸ðW@†^žˆ€‡ÇÏ]µˆðÇR×u‡Ãa³ÙL¸¼PÞ•g]-D„Ëe¼·—'"àaçñsW]]æäáNå³D„Aã­ÒöÓËþžÏçý~ÿýý=þ ÃápH²®·E˹K~e¼¥«%"àaçñsW] 8&?3±¼±ú(ÆÅ6`ÂÎ^¯×lÇXÙ¤Óé´Ûí¾¾¾†/2t]—a]o?Šþ¼;ÿ]ò+`™®–ˆ€‡ÇÏ]u5Ü9ŸÏÓ–VÞ8KDx:">Àywv—ËeàxÇã1úÞ~ýÉ4Dtɯ €¸®–ˆ€‡ÇÏ]õáp˜q:Ä K«¾e·û?{÷v¥ºÎµö HHHÛuG NˆHØÚ“öUãÛØòA²Õ{«‹µjÆ[ÒãÃaЬ}¯ãñ˜ÿVW{a]—àlž÷J¾=ó»‹ìœ_S µD„ü<.÷­k¯ØÚn·qK /ì{5Yí l6›)pÎ÷]mp6Ѷ×÷½’oEÏ,49çWÀèC-!?Ë}ëÇã1ÖÓÂKjÞ"âUS<ްéÃNt_Óq55Tï•|+zfÎù•0úPKDÀÏÁã¢ßºö¢­ˆ»DÖ^3ÕåR²Ú˜è~Œûý¾öBŸ TiÛ^ß÷J¾å9 cj`¹£<!?‹~ëóùwÝÖ»!è5­À†õ©}¯‰ž~¸Üm¯ï{%ߊD„Œ[¶‰ø9x\ô[7å2ûý¾ûBj¯Îë˜5­@Ü*ƒëõÚò„ÁðOµïqÉÛ÷û¶\ïþév»E/<¼¶ûµ–s¾W&[Ñ{ÇüÍ@>£<!?KëÚ»;v¿¶.üÙÀЭi¶Ûm¯”0üñßÊ4ýÍåri³‡×ÆÝqôz½îv»ö÷DÖ6Ñù|Nþ^™lESìIš€LFy"B~—þÖÇ£éÚºý~ß’š…jºò«×þZ.$ìxŸÉÛíö‘1µüqÓ:¿V;¼]Ç5¿ßïçóù£é~~}¯·èõÕ^Ör•åœï•ÉV4]D8s3É(ODÀÏÁã Þºåںׅ`áþšðá›.ûŠ{’`øû–¥m6›ãñþæ=0ºÝna5Âïk“©–÷ Ùn·íã÷ð骪úxÇðßá7á÷á_›â°Ž__xùù|î’5}5áƒçð^™lE“F„37#9ŒòD„ü<®ã­›n!îÞŒ#®ÀÏ–iºÈkº÷múûÝn>øõz}¿¾,üwøMø}SÙ~yÝœï•ÉV4CD8g3|”'"àçàq5oÝ~IWGŸ=7Ý tl™ûýþóZÂ"Â8·Ûm¶)Žö÷Êd+š3"œ§H;Êðs𸦷xXÜõƒïªªšmþx<ŽÇã²"Âívû3lšó½2ÙŠrŽ£›€„C-!?+{ëëõÚt³ÄöÇö}þ`“ûý¾Ûí†Lïw€ìòy‡¼Ý_ Ôþ º°JÃ/¯;]nV9ç{e²M±wäÐŒ¤j‰ø9x\å[WUÕ1â þxô¸^¯½šý~?d5ú¾ÝßcéÎçs¯Dòr¹D¼QxI¯w™ÿ½ÒnE“îÉ›€ù‡Z"BJv»ÝN§Ó~¿ÿxl_øßðËðOSßAññx\.—ðF»Ýîc6›Møåñx 0â…Z×ë5¼Ýáp ÿŽ·Â/_|ø›†¦;ŸÏ?ßh”k3ç|¯ ·¢?HÂf`""B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ€e*šPfàÈåÈLѱˆ# @™Ž\@Q‹ˆÈÊõz=ŸÏ‡Ãa·Ûm6›ïNf÷Ïñx¦™ŠðO×½^ßO–¶]¡ì×ø*2ú¡ª;Çc“ˆ}>yÇ""„ öÍÿjÖ÷I«ªªí7F™©øówÊ´M e¿¦ÀW ”ÑïˆUÝ9›D„èóÌ;!¤Þ1ÿkùYÙ‡­}‚Éétýn·›îe¿¦ÀW ÓïˆUÝ9›D„èóÌ;!$Ý+ÿûù³¦Ïû~?¢?·ÛÍ–€£³ƒ©¦ðU èwD„ªîM"Bôù@拈Òí’ÿuüQÞ¤r¿ß«ª:»ÝîûI.á—§Óéz½NñÖÇ#¼õ~¿ÿ˜áù{ßB&y~ßkr>Ÿ¿¿‘á_G¶ûEØCÛ†O>àîŸÚU ¿Íþær¹ ¼}Y¶M>Ú«¾g\ÿ>~h«qïÞ6ª°ØÚNàõŽ9w -§ÛÍööß½Üf³ ¿9aóS2A‡~GD¸†~øýúÀÐ~ôŠ¡—}õ‚Úda>Õør‹ºuXtÓ©ñfë¥û¶gh„Ћz}5Ó5Qòyžï·þ{ßœöh>ÿ„@¶;µc«6‰!£ýñ¿^?+î…ò\ÕP¶5Õ?ßB5걊PÌÔÞªé}_ó$¡&‰hÛ¿‘Ñ¿Üy¾‚.«ÝqMB­Ø}1õawÈ×ñª´?ÆhÝ…×F§rë"ÂÎÕqg|ß †Ì^þmrÛ?üYøãúä¿!Ø÷à=ü2ó­:ÕnþSXT÷ ìx<éí¡€ÚZD˜{Õýs•B!Ž5]Ž€¡ÇŽ˜ýÒ&½^;ú1ÕtM>Õx÷j$·¢.íÎ;Ê¢¦nº©wL5ÞèCãÚ«Ô¿{éöö }ÅßÓlö-#… çyzíJ[Ť# …N̹S/èØw4!¤Øÿ‹øYk/”Û¡3TѵP¨†¼u(>C)ñ¾MEËâF óí«Ö¤Ë裘Ÿùˆ<ÏX»¯ùçîÆvƾc¾ãâ7þð’¸áO—ïrÒBæ[uªÝ¼}«š®·‡ÊkaîUw{÷UUUß.±ï£E„Ý?o>Õxßj$Ÿ¢n¢=eÎEÍÓtÓí˜j¼q7˜ˆö¬í¥»‡ƒícžg¶ˆ0Û 9wêÅ;`àŽ&"„Ù÷Äÿ¢VÙ eu{¸Êm”Ú5”OÑ%kt‡œaD8óWдÚÑe|ÇX}DøÒ÷·Lꊰ3&)Š"ËCfJ6x÷aQæ[uªÝ¼e‚Bù T™"ÂÜ«î¦îë~¿w¿¦cŠªoô×.="̤«F2)ê¦ØSf^ÔlM7ÅŽ©Æwƒ±=‡ätC¾š´ó<}S­Q¶Š•M̼S/ñØw4s0ïnøßÀŸEüÚáÿðóÁFJޱ*·´Èr#Â$_Aíˇǵ?Ï3,$"ì;™C]1îÎØý}ž£×ù´¬søâr›Àw€<Ãn^+â2å7tëÑD„¹WÝM“‡i«¾)^»Üˆ0Ÿj<ºÉ¡¨›(¦™sQs6Ýè;¦oÜmoÄöžÓEœYæ<ÏÊ&fÞ©zì€;š9 ˜qüo”Ÿå¶@SM˜üêû–º1”"aµ?ž;j†0h¹…H¯»LT7.+"LõLw4l¿—W9áÿúÜÖ,y]¶±¦OQ»†ÿ}=n~`Q4ðúÁèsÓ:÷¿g¾U§ÚÍ¿õšêT~CÏBDD˜{Õ=]oÜñ"aûçͪ®F’uí)s.jæ¦ËjØRx7i{Ž•ÓõújŠçYÙ„Àœë°ÜcäÐÛÛø¡Ãøßˆ? m„–;o¤M ›ŠÀŸçµÜ5¢ã'j?«íõ€ì"$Ô6á7á÷?ËìüG É¿‚އ¶ÝnÖäý^ú¯áCË}±º¦8˜Ž8"Ÿ1lláÆöü¨«Ãfù³$žíôþQ4}.s—Ëå{{Ø1¾6¤÷àÕæí“fÝ»ÓÚ—GŒß3ߪsØÍ_]wË»×ööaÂv5Jo”Ú"Âܫq¨ Coü¾’?{ãà½óœm¾4Im3ÝQ «j|x5’¤¨›nO™sQ37Ý A†oêö ->NÄMQ¢Âñ½¨–¯¦×¥|9Ïó|‡M¯yž°n?gYÚ„Àœ;õr0zo/"„ ö¾ÿFÿYhS´Ü ‹ûž­7ŠÚëwB9×ñÙÊMµ_ø¤]^Þ2Õߥ -Ö¿©Uó&ÿ ~Ô~n–¡4x@Ì0" £’°ùõ=E³ec´uEÓè¾×cÖÃé}\Хݚöß°íµLx¶<š',°ËLi÷Ê0ì_³µ£°×:d¾Uç°›?nú׫·«ÝÛCÕ¶ˆ0÷ª»Ë…ðíkÕ’{F? :É1(U·Ðj<¢IRÔMº§Ì¶¨ù›nÒˆP7n/ý³=»_MùsQ-7n]úƃ‡Ãáu¾hß»£ÚËp"î¿T;4ë×çu/7"Lû´œÇ>|ì0$¦Égna”|¿ßç?.®]óè‹,^O܈ÛrzÝ?§éZÂè[®ý;äNt‹ˆçÜÍkÏ ˜;zŠYFÙí'—«³ªºÇ:Ü4]KøsúQD¸¬j|H52Q7õž2Û¢æoº)"B5Þ¸“Æ}#’–kØGYT—…¤íÖšhçä,."œbB`¶zÑÇÞÛ‹)cû—ëå¶ß•ýçôE8¬_.—QnŽT{rW¯ÉùŸ}lËРéÔ²Žç+ŽR&Ó~M/‰(ÝkŸðsì0]õžð];ŒêX§­+æ÷Ú¶ê~›ÐöP—ë›zÚãñ8p~8ÿˆpÎݼéôò|®Ð„±ûS?¹D„ùTÝquZ¯Ù×ù“ˆ$öè ̳X,úX™v€6Ó¾cªñÆý."bµ¦k¸"š¥vQ?¿š<çy:^F—íÑ'á„Àl;µq¥ŒÕD„½ýËõþËçIˆCæ+Þ'ÒC™ÊèÁcmÁ= R»´–Yú¸rwÜb&yD˜ö+±jï§‘0Kx€nå?.žÿÝk»ÁˆÁcôæ7ä§ Úª“ïæµ_ô¸ÓÊ<2+»ýdô ÄLªîq»¯¦ #¢¨ÅMÒŽ¾À<«ñÕˆˆ0zQ+ˆÕxÉ·½¸+ïº/êçWcžÇ„@ÜNmœE)c5!Eoÿr½Œ"Âç¯g÷êðˆÓÛ¾o[ÑñîµjŸÍ>c÷w¾„ð¹Øˆ0íW0b 4MU¥ªÞšµe¸ÚCn¶ù³ëzŠèEM×ૌãvóQž„bèÊÒÊn?E„™TÝ£w_µ…tûЉ×]',ê2)¤GYÔüM—CD¨ÆËgh<Ê¢Ìó˜ˆÛr}ì€{¨ˆb¶¹^^áß»ûCRºÜ©û-Â_Žuó‡–"¤él±ÚwÒ….1"Lûä3f²Úµ£¹!ÃɈÁfôù¥µéÏ¡¨ˆp†Ý|Èvòœ>‚›©Ÿ¼"ÂäU÷ÝWí½FÛ3$"\w5ž°¨Ë¤eQó7Ýr#ÂõÕxc­Àˆs‹2ÏcBà9^¸¼ c ÜCE„³ýËõrŒÿê¨ãñØòTë^:>ȸöÎOê>F¨}÷è{’<—¦ý rhéª÷IÐ÷û=|M—ËåôO¨™wÿ´ßÇ,ÿLí î˜-BÓEw_c-MD8Ã:Ô^¹ýE?E„,£ìö“cD˜°êž¢ûª-#ÛçñD„«¯ÆSu™L­²¨ù›.‡ˆP—ÏÐxø¢2œç’1å9²^Ä„À":@ȧÒRÌö/×Ë7"|Ÿµ5U8.œ¸èr¯=÷x¶¢ºö݇œÛ¶Äˆ0íWÏ8(ÿˆ0 ªª ßׇå?.®=ø%ôHa÷ò¦oq×ý5‰»&QD8Ã:ŒûE?E„,£ìö“oD˜¤êž¢ûšùjµµF„+«ÆSu3LÜͶ¨ù›n¹áúj¼uD„æyL,¨„|* !̲÷-&¤ËG8ø†±8 LjÛ"ýœ¯ñVK½bí»9·m‰aÚ¯ ŸqPÎa(’‡Œ–v©”Bk„)Œý£22Q'1Súβµ{Pß»óeµ›@®ÕöR£ºªî)º¯Ú›¹m6›‰V`­áʪñTEÝ åhòâtM·Üˆp}5Þ:"Bó<&ÔB>•†ˆæÚåƒãL_œN§ŽU_û)|cÝa)®W¬}÷!5Æ#´_A>ã <#Â0÷ Zĸ¸öZ¼¡/ =RÜ ¯vÑç%†F´žˆp†uÈ|rVTj‹ó­º3éE„«¯ÆSu3LÜ͹¨™›n¹áúj¼uD„æyL,¨„|* !̸Êǎݵ·qr"ñl½b&Š GU"Âäáãñ˜âÜËEŒ‹Ÿ 7‡éb¿ß‡aTÂI¡é#(»Îæ[ugÒŠW_§*êf˜¸›yQs6ˆ0Ÿo¡y ê!ŸJCDóî†òÁ‘Ýï÷–ê¥åFJǧˆPDX7èr#‘Ífö»P<¿P~ý?¯‹à>ž2ßQh™Žãáw1ÓGq‘-"Ì·êΤ?®¾OUÔÍ0q7ÿ¢fk:a>5žˆPD¸¦ Et€fØ$"„,öDùàøšÎó9JÇeªD„iÔöáÀë®û?o†¹ôˆð5 }öàëÂéZ)ç™<·jÓGPF…-"Ì·êΤ?®¾OUÔÍ0q—dQó4ˆ0ŸOD("\Ó„À":@H3lB.;£|p|µ·IßívyVà"ÂgÝ"Âw§Ó©éàþ©{‰»‚ˆðo\>xÜóÙëòïD„¦`E嵈0ߪ;“þPD¸újϳý‡ŠóÜÍk7!'”*¿¡aßæ[uOÑ}…#ì÷ÒB—;Ñ .·OUÔ­ox2gÓ-7"\_·âˆ°´yKé!Ÿ2^D©wLù`šš¶ö.ëqõO„Li °´_A>%h&aJí öã†3EE„ã‚Ú•o éj·ÿèN ni"ÂÖ¡v{¿ß/wúr­E„ùVÝSt_µµt†IDæáʪñTEÝú†'s6Ýr#ÂõÕxëˆ3œçÍyôYÙ„À":@ȧŒBû¦|pµ7Noy@píE7íO0Qmá:ÿ€b¬ûHÄ­@Ú¯ Ÿ4“0e­M1›¦g»÷Úþ£Ÿi^; ûyº¬ˆp†uhº_ôs'•ßаoˆ󭺧è¾"næ&"\}5žª¨›¡Af ÍßtË×Wã­#"Ìpž§ý ÷|Ž>+›XDi†M"BÈw÷," …Ùt7¯-[б¦g1WU5CS4b4séXûªÙÎKû¬² 6"Hè~¿÷Zÿ¦í?âiMoýs‚BD˜°§¾bTù û†ˆ0ߪ{ôî«¶þ9,"\}5žª¨›¡Af¾gΦ[nD¸¾oa†óñoxŸ}“ü¸¹öÊ£¸/(nÒ~9—ÁCX`DU¡ÕwŒ˜U¨=_±Ë‰3G„óoÕ™lÛM§ˆè蜛 ,´ê·7®}ØÓÏûÈåŽþ\ª! \S5¾ŽµJ>@›¹é®¬Æ[GD˜çlS6qDHød>®Œ>‡-bešVãç©tSoówP¯­®×k›ZïgŸZ¯i~ ì-[~ˬûƒ6¦kðL¶ê¬¦;ºì&Êo`ÅUwÓ1+¼¼Ëa«¥lî$I"F? Nq„]M5ž¶¨›aÿa€6Ó-="\S·¦ˆ0m·Ö²I´ŸÎ‘vd½² Et€Û0ÁI/ŽÅahj°P;}LÃÿ†_†ú9öéu“ùPÏ´Üüÿo¡Bïþ^F†ÿ¿ ¿ÿ} ˆ–·ÞívaáïÅXøïð›öS³úvÈ-ç§}¯À«ý6Wß#B¯ “qPËI¤á úøÔ¯!á¯-aÄ•iùšÎ€í2±¸ˆ°ï¤åÀ“½[Îqýkù¿Õÿñ³Ù»_G0iƒg²Ug5ÝÑ}i™ØW~ ­ºÛ—ÊÎpøX“W¥×~¹Í<'ÆdUæ¾ÀÕTãÉ‹º±$ Íßt+ˆWSã­,"L;ÏÓÒQ7Íóä0²^Ó„À":@Èv˜ "–Þ E<„ºýö “v‰Ó½u÷Ê9‡#Bª¯ “qPˉñq0neÂP(íÆUDØ>iù7áÖ4úë>cùõÜã^÷E™´Á3Ùªs›î2ƒôc*¿…VÝ­C÷KçR%£G_àjªñŠºQ$ Íßtëˆ×Qã­,",|žÇ„À":@Xñ0A; {¡è³tZn2u—8d˜ÿ77’jââÿw߉áG„$_A>ã èÏ>îÊ´?õ ‹¦aÅÌ#‚äTß›} ?÷8≩<“­:·éŽˆD8 0ÿ.U~ ­ºÓæƒÏ¤IĸÄ)¸Žj<“¢n©hó7Ýj"ÂÔxë‹—>ÏÓ´„IÛm5‹èa­Ãs@‹Çãñ}÷±¼W×Ñ«7¼š9¤Ûív¯“vÈÑñDh±QV ÕWÏà%ú<ÃÑW&zc¾5®#"üù°†gÞEüY8dÏsfÎk­Â±>¼õ÷|Šõ Ë ã»ðŽs8á½Bã„7šº”ºÝná}|Þðß-g<®ì+Ègïû#¿N= ÿêðwÖíØø¯ÕøX‡W›¯þîá†vþ¹íMôE¼öÄÚÓÚþ™lÕ‹Vûh¾Ï TÝIªîŸÅê_ Z{à åñjfÞF? N}„]A5ž¶¨«,L2@[zÓ©ñÈjžç{p×>Ï“CC™Ð²2"B€¢zxͰ&µ4ñVÛŠU5EÕä"B€µöðš`MöûýwWï´U@±  Æ€¸š\D°Ö^³¬Æý~×ÕŠU5ŒX“‹ÖÚÃk€Õ¨}^˜‡ÔŠU5D×ä"B€µöðš`‡Cm?»Ý4 XPã@\M."Xk¯Y²ê¨÷û}UU½æ|®×kí¹åAø½V«j<ˆ®ÉE„kíá5 @¶õn·;N—Ëåz½Þï÷÷¿ ÿ~þu»Ý¶TݯP¬¨ñ WM."Xk¯YÖZŠWU¥IÅ*€’³D„™÷ðš`•¥øápО€b@IŽY"B€ü{xͰ¾Rܹå€b@©ŽY"B€Eôðš`M¥øn·»ÝnZP¬¨ñ`þc–ˆ`A=¼fÈÇñxÜívÑwº^¯ÚP¬¨ñ`Äš\D°Ö^³dèz½VUu:vÿl6›Ú“É÷û}ø³F€b@Õä"BX+!EDE@QD„P!EDE@QD„P!EDE@QD„P!EDE@QD„P!EDE@QD„P!EDE@QD„P!EDE@QD„À²Üï÷ªªN§ÓîŸÞf³Ù„_î÷ûð—Ë%ü±€"B`n·ÛñxÜl6]Ðáp¨ªêñxhF˜®xÐ,š€¥ÏŒX0ŸËçø¥¨`Y!ÜõzmŸa蕆¥iR˜¢xÐ,š€¥ÏœN§± xSŠ –>@ Ýn·±ÂÁ›‘VU¥yaÜâA³LÝJš€©gB<Ê»Sj9–>@©œÏg],¨xÐ,S·’f`†€á§ÒÝïwu¸Z€u ÌÃ3{<?O<Ö5AnŃf™º•4;3Ì ¿ðx<ªÃÕr¬c€`˜ÓãñØn·?'.ŽÇcUUß¼þs:öû½® æ,4ËÔ­¤Ù˜gàr¹ )æM¨åXÓAD̦ýჇÃá;lq»ÝZNcÖÚ0bñ Y¦n%ÍÀ<3¡ ^~UU¦Ôr¬i€ "„„ûèÛÏúµÄyÛí¶W8ø=Y±ÙltM@ÚK³¬ Œ^tÞTiü&ºðþ(¹›nbû€l"BÈdýúY³ËåÒrñàãñþg5Û€™K,ͪ’Î|±iOß—þí÷ûQêù¦‹ mŸíADYNl¬yWjyjÉáp÷þ®U´…3—Xš`Ñ•ôÿV>¿®þ î÷{ß…<2 ,Ó7@B–³kN ›n1:n>øçu†³ ˜¹ÄÒ,i‹é¬˜Õáé|>¬Ão·ÛÇÂ2`q!ä:«±Î½é~¿7=e”û‹dRbi€äõtòEåyxª½§G¯Rüp8Ծܖ5@B–ó«M O§Sm×q»Ý2\Û°Va…÷ûýv»ý4Ã/Ã?͹گ•Ùívï÷† ÿ~ÓwM.—ËñxüXT~~þÕgYœûý~>Ÿ¿·Õˆ&Çõz}m+üÚ ^ß]ôiá…aù¡A^oñq'´÷ÆyíÈCÞ«¥ÄÒ§­l£ZU'YHÎ3¯ú¾­Gè{»wàM¯î€Þ´ªªÃáð}XǑױ#ý—õ…ò£öC½—C Ñ áADYNf¬6"œó£C„Áæ÷SZj…? <ÅÔÍûÊ| ·›òŽö‰‘0ˆnŠh¿?TÄ€zMŸå9êWô¢:6iSö1{7o6î±þ~¿7Ýj¸iµÃì˜ß½æ¦:î¹ßÂk{5ѤµÓè%Vn}Ú¤- °Ž®§WVŽ·ôɵwöèxÀý®Áþe8ÅAÇ£ÆßQ,¬^Ä™?Ý×ü/û>?-ü²ãÛ…Ü÷•˜MBíôjá°üˆæ]t!šC¬g¸$"„eLc¬6%|=ðÛ߬BÂx3"b/™" Ëì¨ý<¼{ 6$º]Ógy.!"ŒhÒãñ˜ª⾸÷oðgG1J³Ûí:žë¾”ˆpé}ZÄF (¯ãêéõâí}òwÂòzžàÏzK•5îAÜ!¬ûgé{ä ÂÏXªKÖU]j¡¸w\t!šC¬g¬$"„eÌa¬6"¬—u?ÛvC"Œ^7hú9ôƒß^[µÄaCfWz%kkú,ϼ#›tžv¸Ýn}'‘"ÞwÄb¦Ëet‹ˆWÙ§ÄUØ£¿d¡3ïGçïóC~.3"[Nö±|Š>jŒ[Ǿ„£XÇkÖÚß%”²óÏÀ,ºÍ¡Ö3PÂf/ÖœFOÅÏ£ûÍj¦Î_n·[töqâôðÙ•îÁš>Ë3ãˆpÄ&§¾g óƒŸ÷§Í?"\eŸ0¤Îå—^{¿ÿÁ÷-"Væßw×ýøõxîJ=üøÕ’†u;á߇¡Ò¨ªªåΓonÙ´æ}OQëÞt SÓ6 Àz†H"BÈ}ÒbÍ)aíiŸ'§Ò~Ëš¹ˆðáÛ§ º?ëc×ê|>¿?"-Œ¬ÃH9â*¡°¨ð]|/êû4ò¾'`¯é³<~7ékº¬¥º? fH;|?®èçD_øß°æákª=A½×ª†:ÂÒÂ2?f¢^{ñ릾ô`æmf¹}Úˆ-@{ÁÝëÖ7ðñ7ßÜ–>ÿ£—Þn·£›Â¾Ÿ—žµÜÕ¼ËQ¬ö…§¨Õ.<Ô!íQ¨X¾£ÏPÓ†ßÿ\‡u¢94 Àº"BÈiÆbÍaí%-9Lù†±dÓÝr‡CËEŽ-% ìxud—û#µŸgÛý”ÚŸ‹j¹{OiŸå¹ˆðg;œÏç„íÐ25ÔåS×ëõcû¹ªûý>|õ½NMožÎKhG¯Ù éÓ†o´?kîŸÿ´â€Ÿ%SÓ)|ßwø¾+éÀÞ»v¼°Ýnßã§ö#`mÔå‹KаüpœúÈ_‰ØëÚ·"êòÌ…pÜ oÚtp_w!šC¬{€ "¤ìé?Ñ?½ÕŽìrxaÓíø:®[ÓÈ·ã­ùÚ§ :^¹Ó2úkQ?ŸË¶²ÏòÌ>"ìÞMù×ÔíÐøvœèû›ÕùË­¦ëš¶–ŽËÉ*"\AŸ6p£ÔÜE×Üý&Ô§‘ÔænCŽ_MWŠõ=ó'î ØåZ¹^ÌŸ¦óÖÂa±ïò‰ç<¦'o@€õ ÖD„˜®ð“YDØýæuišFè~ƒÁï “?]­£<üëYw“¨qÕe!kú,ϼ#Â^íд‘OÝMßcÜôZè+º\0Dmµßï“Ì8E/p}ÚÀPs‹{M¾Ï-ù>j|çwµgÑ 9 Ö}" qO7h¿å1KmEo=³‰g>¦'o@€õ ÖD„˜®ð“nºb¬GÕ«v¡û-õÚG¾]®ÙøØ¯?M'èFÄ1µ‹êòÔÈ5}–gÆaD;Ô>ÖgÒvhº„0ç«Àj{ƒï§,Í3ã½À<û´™7Z@Í-"ìu4©íö?ªôï±öÈ}üª½„°×ù-?W£ýPØòÔ¼ˆ(ªý¸}ÆTáÌÇô`=ƒ5!¦+üd&ß,jŸWÒñÖ‚ïjoÍ×%\«YâΗÒ>Ë3ãˆ0b¢·ÏèuXb¾Ót³$ý[ôWÓ§ Ùh5·ˆ°oŸü}ß{<÷!6]}¨=­+údÂÚ¥µŸè2äÝ}ËãFŸë.Dsh@€õ ÖD„˜®ð#"|Ót¥LÄ©ÂÑ‹«Y®×kòE­é³<×Îßc]RšC¥”¤‹[ > Ps«¹ãúäÚódþúüïs®šÂ»ïOÞñ^ßµjïfp<gªŒòtʼnª²E¢™4 Àzk"BLWøþš=ˆ¾Ð©öAf?o«8V³Ün·ä‹ZÓgyЬCíÛMý$Áé*¥$ýÛˆ÷w]hŸ&"5·Ÿ9#ÂgÝ…„— ~äw-×pÅuݵ[Ä%ðí‘öâ!·³q%—[ˆfҀ묉1]áGDøæû¡*-÷Mšhi9äPÉçò\”uˆ~aí¾}K¨Ýï÷ëõz¹\Nÿì÷ûÝ?µ÷°ZbD¨OÔÜjî莴6Çy<ß矴\Ã×u×ÞˆràÍúFKStjïw}\~.6"Œ^T& °žÁšˆÓ~D„o⮑iwýŽˆ0ÛEY‡èÖ>ˆpøÓ|"Üï÷ð¾‡Ã¡=^ØdêÓ5·š{HGú} ‡Ñ_Nq-Þ÷ŒÃ;ü¾k2ÅAgÜãò³¼ˆ0“XÏ`MDë™/YIý ‰QÔŽ:£ÏXޏ£ÑSD˜ñ¢¬Cô ÇݳâTU5$\SD¨O”àM•ôJŠìñ:ÒÚû:ö:¬Œxüšy²bŠƒNm)r»Ýf.–[ˆfÒ€ë ˆ!éüDAa©A—^ññxÄ--¼0¢Wf»(ëý´çTUõñȤr"B}@—ú»×¬o ý%íÇП‹ëºÇ=pçæ\”6¸PZ  Kýýg«™hIû…„?K÷_"Âäåˆ`mC$!ä5EQVD8úãæ“L›Œ¾@a¶‹²9lÕÝ=.wÝl6»Ýîp8œþ¹þŸ×¥v"µöi€â»½z^kJÑ‘6]Bþ:†Îyü&/D„k%‰!£)ŠâRÂóùñX«Ü¦MF_ ˆ0ÛEY‡¶êîÚóÁãñx¹\~ÞrSD¸Ö> P|ÿo‚—,tàç«N§Sí C1?çñKD˜¼<¬m $"„Œf)Š‹¯×ë¸ÏÉJ5m2îE„Ù.Ê:ä°UwÔ4™ùºT¹{'#"\kŸ¨¼'}á²f~¾ªéBÂ.ÇÓLŽ_™tD„+(lV5VB.³…¦„qÏ4™y}¢#˦é”̇ÞyŽâÅs‹^‡™Oh¹%Úív[Ü75ú>¨OTÞ³½|µw—‡¸§ˆßm6›áÅIÂæM~LϤÖ3\B.…F„ûý>«{†·þ^Ÿëõ·´ÚË$~:a¶‹²Ñ/wÏú©ªªQòÁçò#B} ì^+¯)%ŒîHï÷ûÇ«Âof>~ I‚29èŒ^•fÒ€ë1‰!‹‰ŠrS¦™üŽs£«u^.—¸¥…ŠE„Ö¡iÏšîzáï+^\è75dú4@Ù=J•¼š”pHGú~L ‡Ú¿ò9èÔ–(¯ÇÌ¡DO~LϤÖ3hBsåF„M·­ë>í0®Úç—E:ã–&"ÌvQÖ!ú…µûÂt»ùv»ë ó¥G„ú4@Í=V‰¼Ž”pþŽtÄãWÜÙ>YµÕù|þ^æ~¿Ÿy%—[ˆfÒ€ë7‰!‹éŠ¢ï5Z{.è¤7!lQ{Ìv»[ZmNñóüga¶‹ëÞ•Ïò"ÂÚ=k³ÙÌYÞäßJS,PŸ¨¹G¬E„i_ÓU³µU¨Ç}Lpia& °žq“ˆÒOW”Ö>Üê5 =Öû9´lšahºª1âÆ§ßOlé8€f»¨³ìÒ"¦Ýa¢3D„ú4€‰ŠcaÚã×tw)Ÿ­­j}ia& °žq“ˆOWˆÿµO™è>„‰dÓÔ^&1ꬽ?R—‹wD„Ù.ªvÛ8ŸÏÉgEr˜™‰k½Ÿ±[îÖ2z$:îv«OŠª¹—²äT3y¾ã~¿ŸóÂÙÚªé*Ÿ«öмúˆ0‡XϸIDd éŽ1£§„ïÃÀ¦¿©}ÂEßQç3ŸE„Ù.ª6ËŽxúIÓ¿”vñÙ1]H8Ö5]7×åµµÁÜk¢×DŸ@ÂŽ4úÃ!&í#Ë'j«¦:°×yS¡ª©­4Jˆsh@€uD„ÀüšÎá|E0ÃÏþ6ýeÓLx¯$¨ö´çŽsò"ÂlÕ´•öÚ>›òšÕG„M{VÜM/_;uÓƒkÃÜãñØkùµOA(¹j2zMôi$ìH‡¼c8Ð{WÉ¡Aª¶jºƒJX¥ŸEQXó¦—æÐ€ë ˆ$ZÎäÜl6Ñ—…qbííhZ^Ò”uø{iþ>ü&”7M™iiaòX÷AD$ÑtO¿÷ pÂà®ãÒGUU-‰Lûk›Æí—4¶$ ÝŸœ""ÌyQM›Sû ›rêÒ"– ;Þ²òv»}´d¯‰£ívÛž†5l¹¢y”»`uO*Güêõi¤êH¾cËI;áHЇŽ£PŒ…jíãh˜°­ºÔÓÍÀ,="LÞ€ë ¨¯€TÚÏ}?54Œñ¯×ëÇœ@øÍår ÆŸwùÙ5µœ´üwnêß»‡ÿÿÛžu¿tHD˜ó¢š.O{EØUU½§?áKjËå±¥E„¯6iiÍfZøc×n?ñ»éZš½éÜò.1î(·G{]yúñ1Ãú„ß„îë5:úW¯Oà¹Àˆ0’~–Rá€j°køïð›ðûð¯MGä´mÕ½ðhÒÔ2y£/*a¬{€ "ºÝnï3u×4⹩½˜""ÌyQí×ÁÍp¸\zDøû¬ï¦wiÏ"ç)lªªñ-†õú4>;ŸC8ú}†¶r½nt_rD˜°Ö=@i=.—FÛl6í·…eÔÙ÷i_Ù½‡,j}ásXîów¯Ë’#±ö¬ŸïÛrÉgGM9c÷OÚýÒ"B}KŒŸÿ®µ>¤æ>£NàyJ{áaªX÷ADä``ÓòÈ’©GÑ×Úd;ô~Šÿ_Ñ9Ëñx|Ý«ðˆpÄ|¢oj·Û ü¦^¢/|Èj&m•}€€E„Ïç?ù'ÈðU-´w4ÊíÑ".|˜ô«×§˜X\Døw~³‘p\>ŸÏÏ4OÞV¡ª kÆ,eCøßð‘CÙóþÀë–• Çëü‹À)šwÎX÷AD䦪ªŽ§†~'ƒa¨Ø%Gè²'Õ#®UÌè-"üÖq’j»Ý~o"Â÷fìu­_Ø©ûî_¿©×-ˆ?& G¼v¯W*7u³ëÓÌ,1"Œ+þnÐk\°ˆƒNíÇ\D˜IóÆ5 Àº"B [×ë5 íÇc»Õ^˜~8ÂßÄ]íòÓív;Nµ§§†_6žÊн6‰°á½§-á¿[NWæÛãñ¸\.¯–üع^öúð?Oøoï=^]GÂ=7|„×:|ds¯óÛÃ?M×wéÓXåÐ ªBñÿ}l} ^Dz%DÎø#ƒ ß~4 P8!À²Ô>Ù9â©ÁPÅ,Kíƒf¾X4!À‚ÜïwÓ/` !À‚|<^Ùsô4 @!ÀR‡Ú¹—Ûí¦q4 @w"B€9ýïÿÛï÷UUõŠ¥®×kíåoAø½Ô€}k*!Àl¾ó©Óét¹\®×ëý~ÿËð¿á—á_·ÛmËÄËÇ«4 è[S‰&5î¬KUUP¤­©D„íFœr9P¤­©D„?¹üM¬¦¦t1|¦e·ÛÝn7 ¨ÖT"B€îŽÇãn·‹¾1æõzÕ€` !@×뵪ªÓé´ûg³ÙÔ^ï¶ßïÃß¶4 ÀˆD„P!EDE@QD„P!EDE@QD„P!EDE@QD„P!EDE@QD„P!EDE@QD„P!EDE@QD„P!EDÀ GÍ!3Y4 @>D„ÌpdÑ,ù«ìvôH¹}Å>@>LÈ«ìvôH¹}Å>@>LÈ«ìv–Õ#­¬çÌáãL±pÀjˆUv;"ÂÂ?Žˆ …ˆXe·#",üãˆZˆUv;"ÂÂ?Žˆ …ˆXngåÓù8s®ƒ°"B˜Ùÿûïï'ᯣ³òé|œ27€D„0³÷ ïg–÷—"BŸÎÇq0h!"„™}dyíq^ÄŸ‰}:ç)"h%"„™}Çy-‰^ß¿út>N  À@"B˜_÷P¯×¬8|Š} ÀxD„DÇh¯û¿®;|¦K|n·ÛétÚï÷Ûíöý­Ãÿ†_† ù§{<×ëõ|>‡µÝýSûvá÷¯Ot¹\ÂK–øeå¶ÑÛ[ø.6›Íßj‡ÿ¿é»±…ïñx<~,êõE‡ß‡býï÷{UU‡Ãá{3 »ÌëS„­Ñ!’B*]¾Žÿ´ú|ð™"ñ©ªê#[iþ,üqÂî·vùçóy¿ßwüßÂk££œT‚©›4úsuyaØ„>bè¦$·ý{y<§Ó©ãv;bPÖ¿)}®}ë°’“h`!$ô3æëòûòÁ缩ÓõzHÖÂKzejIò¬¾v»]Äe’"Â^/ ›M—pðÝétú~—îáà»Ãády9ŸÏ„ˆÒjû~þ²|ð9cê²üÌn¦î~§^~ßk$E„_øx<ŽÇcÜ~D{Crº!)aôú˜QD„\Kä×þ›ròÁç\©ÓápÞãuŒ<½îH)"ìòÂÛíê}\‚7<§ëmÿy<}/~”@>D„ƒ¦à¯å‹ÊŸ³¤N¯ì¸,+" î÷{>_V>MšöP¾”±rºîßï³5Ül6Çãñz½¾?jðv»UUµßï›Þ=¼Äæ$"„LÔÆ? ï¬F\þõzméÄ>"ðáÛ/Ýþ\Âq[l·ÛN§ªªÂŠ}„A¯þ©ý"Êî—zåp ˜¡IGÃwt>Ÿßþ¾©ËåqqkXTøB¿~?Ê¥|MaßÏg †UjÊ{í2À@"Bȇ|°og5ÖÂGÓ-‡ÃûÅPß/lŠoÂ[^8ç KØï÷UUõ½:¬%úì¸(aßCmØŠÚÛör¹t¿igû¢Â&1ð`]{½ív»}O$Û·±Ú”0ì2Ž0!%‹»pÏO’ÈrÒ¾¥é£UUuyùù|ø|·ÜzΦOôó±|>ÎR"ÂívÛñ깦/eÄEuyâäý~å>¥Cö8`8!%ꉟÍiE¯û.6]KØñB {ÎÚ;Rî÷û¥|œED„ÝCä—¦k]ÇZT—…Ônê÷­½˜1luL0!%ꉟ QE¯Û„>_•aÏYÛ,Ûív)'ÿˆ0"VkºlÇ;|þ\ÔÏ„®öÂ^aúÏféµßÑD„”L¨'" j‹ÖñŽšïjoÞ¸ LíCÓý$—òqò#V`Ä+ïâ"àÚ`±×-F.ͽF`"BJ&Ô6]ýq)ÓEåÙsŠskëõšvQß·'íxïÙZ—Ëå{ŽÇ£cÌ@DHÉ„z"ÂÚ"úhµÏï o‘êÓ¥jsáD+s»Ý.ªö%×Ûþ©)=Žæ!"¤dB=áétú^løåÌKKÒsÞï÷ëõz¹\Nÿì÷ûÝ?µw^>óÈL.ªöVºOTl_‡Ž÷æR2¡žˆ0úº¿ZÑ×$ÎÓsÞï÷ªª‡C{8¼?N·2 ¶œ|šHDùÈ*’[Dg5|±µaô…QÑ7Nœºç¬ªjH,("¸ÌuD„µ;‹šÊtd"à ÷òï¬&Zìãñˆ[ZxaÜzN×sVUµÙlfîÏE„Ó­LÂE»!© -Ó}ƒ¦ø¯åKK çŒç_àŸîñxLqÙWÂ/+ùSxDø¿¹8"À L÷Ar-Á_ûoŠJ E„½<.wÝl6»Ýîp8œþ¹þŸ×u”"±–)"T3@nL÷AZí‘ßÏ_–“Š{iÏÇãårùy?Ua† ""Faºúöuù}!)¡ˆ°»ÓéÔÔ‡êþ¤Ea† ’[D¨€…B*]b¾ŽÿTBJ8gDØ=Dû^˜<"lZ‡àv»ÍÓæ"ÂéVFDŒBDIt øºÿëêS‰ú–Ýn÷½Øëõ·´ðÂ聾·˜óÓUU5J>øfÙ U»³DlW@D„0¿îÑ^¯?XwJ8gDx¹\â–^˜<"<µœ³ÍE„Ó­LnaôΤ%"„™õ õúþÍŠS‰ú–ÚÇö…_μ´?Ýv»ëºHa† ’pQµ›w\ú $'"„™õŠó"þLDØKíuÛí6niµñ\—ˬÖ!Mýe%ß` kw–Íf£W€%ÂÌzeyq)"ìîñxÔ.ù~¿÷]TxIí¢Â[ÌùéD„Ó­CáaÓÎRU•ŽGD3ëäM÷Çëè¬FYrí¥÷­½ cÇ k?ÝÌwqQÙF„qMšUƒ¤]Ô~¿¯½°KdED,·³eÉçó9úê¿?/°ª)ã.Î+kº(2í—Õ݈M:üs­&" XûªÃá €eËí¬FYrSº·ßï»/¤öêªî9ãn·û~møeÄÇ©]ÔñxìµÚ§Î-+"±I‡®5ÝHv³ÙÔ¾0âÂÛ×ë5ú1 @_"B`¹ÕX ¯½Gh÷k£ÂŸ MšV âê¿ãñX»¨ÛíÖååÇ£)î\VD8b“ÿ\kŠ[âã°#ÄÝq4|)‘®¾æ!"–ÛYµðÇãÑtmÔ~¿oI=Zµ^h»ÝnMýgÇhï碶Ûmû¢ÂÚ6ÅjKŒGlÒáŸkMá³ùšÙ×f_UUÇ-ÿ~¿ŸÏç]O_óËí¬F\~˵Q¯Ë£Âüá?Âÿ6]<wµZSFùºMhXÚ{ìr¿ßÃoN§Ó+¬ùXTícøÞ?Hxùßßn·ŸŸe‰á¸M:ðs­," íÖ²ýmiUU}4røïð›ðûð¯Mߎ¾æ!"–ÛYû]®¡ë(â¹lUUÕÇ^¯×´ýy&‚›tàçZYDøl½HSÁ‹ "–ÛYþ.Ý/¦k¿~*îÝ^™Õ½mz"awM9c>_ÖÌMúþ¿î÷{tó* 9!°ÜÎjŠ7x-aÄõƒ¢¯Ìª]ZtܹÛí^7‡\AD8n“Š?„ídx­`€$D„Àr;«‰Þëz½¶<Æ®IxIßç~»ÝnWf5--"¾©ªjx›gu ±IE„MûËn·X'„ïè|>¿?¸˜”ˆXng5é;VUÕ1( öž¬ w:ze”-‹êß„·ûh֎ؤ"Âö--âÂÕ°q† ï~¿ëâ`f"B€·Ûít:í÷û+ÑÂÿ†_† 0Ñ[_.—ãñ¸Ûí>²­ðÖá—áŸÎçsÇëß½5óGÈ͈MJËÆ6ªÃáðÝίLðµÕ…ïÂ5ƒˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""€¢ˆ ("B(ŠˆŠ""àÿkÏÝŸú +HQ„¢ E@Ê á³ÍLû endstream endobj 239 0 obj 75873 endobj 240 0 obj [/ICCBased 242 0 R] endobj 241 0 obj << /Length 243 0 R /Type /XObject /Subtype /Image /Width 2401 /Height 2401 /ColorSpace /DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÖ1 ðò'Ýb˜´Ó†+ |( À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÒÀ`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –F À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`°40X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒ¥€Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`i`° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XK#ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  `íÖ1ƒ þ­×ÃA/ €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒ¥€Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒp4XQÓÜ7k endstream endobj 242 0 obj << /Length 244 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xÚ}’OHQÇ¿³%B¬e&RðN¶Wí`ŒÝõoʶ¬k¦²Î¾ÙÞÌn%Bˆ.AÖ1ºXÑI:†‚b]"è(‚—í73»îˆÚƒ7ï3¿ÿ¿ß{@](mšz€yÃÉþ(»;>Áê7P‡A+­Xf$‘v™lqdí}…䜛áãõÿ] ‚U€Æ¬ÇמöxÀáû¶iO:¬äÒb“¸M¤’1âWÄg³>žöq†[ ñ2ñMÅ'"()Y'æ±ld4ƒä—‰»2–’'&ßÀSg^™öÐ}8õ¹&›°€åwÀ¥Öš,Ô \V:k²Ý¤;©iÝR;;\‘Œu?ÊåÝV þ°ÿ¼\þûº\ÞC9¾u¥(J•IÒÀëÃ]ýÜàBS˜s_ QP5ûFz¼Úë׋Gõ%«t{3qW°D÷0vz ¼ü \}\ø$€Ôu¡ºmþÀÍ+˜…–ÍÙ¬C–;XØ9:Y„^g±BÞ,Ú\°ACioci]g®©Å·¸(ñL;òz±Úï9ÚAnŒŽÐIó ¨Üê­°4“I÷ÐÝ x#Ã{zwA¼¨j}ƒÎ…Ðþ¤Š¾Q¥óš=˜ò8Ðmèñá Ã(Äo{1±cÚÑd5¾Ué­ÊgÒ·t¶üÆlaȱi"ßÐ\.5æ±”šËÅâ^Å8tph0èk€!‰~D† TÒhd¡‘”»6‚ØÂì±–:>f¤ß&Ÿm×çŠäíxÝA4Ž…¶ƒLþ&ÿ–·ä%ù­ük±¥ªiÄ”¦¬?ûCqÌÕ¸m¥&/¾By#¤Õ‘%iþ 'ËW©¯:ÕXl©Errð'ñ=_—Ü—)Œi7Ò¬›©äê,úF|ÙNšٮͯ6×rm^™Ü ®ÍšUáHWü «Ãÿ5;¿?ÿͰh endstream endobj 243 0 obj 9315 endobj 244 0 obj 706 endobj 205 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [118.883 391.603 126.33 400.626] /A << /S /GoTo /D (cite.funnelsort) >> >> endobj 216 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [195.701 323.857 203.148 332.88] /A << /S /GoTo /D (cite.openmpi) >> >> endobj 221 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [123.16 213.342 139.092 225.032] /A << /S /GoTo /D (subsection.3.1) >> >> endobj 222 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [342.887 172.695 350.334 184.384] /A << /S /GoTo /D (figure.5) >> >> endobj 223 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [373.962 132.047 381.409 143.737] /A << /S /GoTo /D (figure.6) >> >> endobj 227 0 obj << /D [225 0 R /XYZ 71 721 null] >> endobj 228 0 obj << /D [225 0 R /XYZ 121.805 493.957 null] >> endobj 224 0 obj << /Font << /F8 93 0 R /F45 94 0 R >> /XObject << /Im3 217 0 R /Im4 218 0 R >> /ProcSet [ /PDF /Text ] >> endobj 258 0 obj << /Length 2285 /Filter /FlateDecode >> stream xÚ•XYoä¸~÷¯Ð£ ¸5"u'O¹f‘ Á"±<8ó –ØmfuôŠÒÌ8ùó©‹juf‘…«X,É:?vœƒ8øá!¾û*ø*(‹¨Êó ‹“¨RUÐô??ÄQ\ÑôJÛ/Ƈ?÷YðÇñáïðç§´ª¢ª,üŠÿ[Q~¯èÄp°, ›“ýþåáÃÇ2PqTÅ• ^NA¡ƒ´Œ£4.‚—6x ?Úó2™ÇCªÒ0ûÍã!ó𹩻úh;;?ê2|ÇÉ,œ›ËÕ~Áåñf:SoZæ-R¼î_±J¿úÉË4;ÓóÀÙÿˆž/o¶3,Þà–oõp¶Ã™'ç7‘pjéñÿQ6æ‰ñôøéå/`…ƒJ¢,­øf°I5ƹqrÑã!+³ðd!OÁ©’2Ü—¶ÃuüÎLŸAÖˆØ8ð·«§³aRîãnÜXHõ¦§w¦ë‰oggÓÌ`uZ¦ÀBYÜ=ªtªðÄ ÅsµÛ»ÚTcºd’eà›ðöðmºÅÍfr<‹Ú'{~Ã%¤8BkT£ê$‰r¥XµëkÔ««dÏÞŽgÀàôýÖÄi¢Â>– ^%Ì×yª9LÆ®ž…K.%ä  zh„K!„„ëÑ$¿ú@EØ-éPO`†” íÖ’™ã ©Å–à˜ºc;n£›‡\:*ç%ðØ­DÕвàXßLdö@åQ•ä·ÞÜÚ«îÜè-çc{ èCÛâÈ w&¿'>.DC7¢k9[&„Ú¢„Àz~ù+sdGæÖÝyœÀÐ=YL§áƒ× ëF &^ J[:hŽe9b½äí”¶¶eâÝšNH×ùdíÞ™#wšgÜ ; \¾S—ù0ž—®^…&#Ôæl ¦'„¼Ùµ‘ši¢½[ù ¦4öê8àÃ$©ÏuŒÃ <¸Í**á[i 0®é`#3ð¼\¤q(!ÿˆ†áNÃïºÙ~å9+sÒì0L¹mB3AL2ÅMø˜RÐX3ín­«H«„oDî£tþîl£È$G‘z®û v7¤}„Áw}ÇQVIaz6Ÿ9j1Òª‚Je©±ÉV™~‚‹gqü ©BuŸ’Oß&]JÿŠx•í$=N4ã}¨5<‚Øœ™rb ¢©ª …M—ådªLyc¥Š1ê ›‚TµîªMl{œk.c/(¥„ÁœœýoãJG\óòaÇ_R^@šË½p(Ö7ºìhÉ¢´\µPeôµ0Jà#^ËŒ)¯€à†h .’j™}Ȇô[ƒÏœ506õVómóf^k4¨µF*´.£\W·Ö´³ã«cs¨æY©ýeÄ Z€J‹˜$ò(NÓ %Ó^ û𧗠­RØ0tQDE‘ †}ý-ÌW#°Oð…${°JJ ºà`0!ݛݪ,Šó4©H—énžEe•IT䚥̮¿Ë,õ—3€ù6`àfV{aWQš—¿@¦ÕªýÈð€™šø†x 8ÙôÎ|¦q+-<\ËÐBIòiB'•B¡Ö@àôv lN‹ìg˜K½Ha™fJP6”»\s×¶)­!Ð †ó{%öªr\å|Îbs+dr4ƒ¿ ðšJc EB‘Næ§µë㈺|¹æŒ‰¨úÄe°M=[*É´G'€ÀÉFT¤¿E…×<Ñi¼Á”Ž|¹²ãä"Ž4<¾Ä…vÇÉy”~~2±œ˜:gâ^P5)Ž®Á…\B·"¶³µü^\ƒsooX#Àc²œ‚"y§iì™2üö™ÞÛÓ;ŸÈtÎ`¹RØS0ü;ÂID@™”™"”]îÊ …V)$ov÷^Û yÑpbïĵ¼yb,³w.¶} ëH ogÑÇf´£BÁç·Ù¼Å ØRŒHS{â‹ÛÓÿ8ன ™T`"ôFôIH7æ ¹€6C©Ê~çw# 0~ÊwL¢=B]uÔîn»†:çõ$`Ë™·.dô2JóõetØ»­-ýZxŸ;HóY^—0äxIè™>tDÖOý¬ Ÿ-E'&˜øPÚ®Wç÷Š7UËrä •m®|V‚Ó!ÃпÈQmO¶,Ø—ßýq ­çúé ú&¾ŸÍ…©õ!wkH”Ù5-ÓàÑ(–+ySúÐ\ß&8x÷Î"üØÆà$>¿i/Tùãõýd;Q«ÿr¬%¥YàÄßsÅ©Ç\@æŠS͈bÛ#?Û >Q½‡”n[Õw8}íû Ö+ã`ÔÑ·'¥÷o˵ôV\øÒµ>^¡/04ðëïG4† ¶Š³È8þ¶’Ÿ_ШÐ? ¡‡à;¢)H¿¥±~íº­ýJ°=—åß"ê2Xýòœê¡®ö;`Ôñ Án+'É Ø|maœÌl¥ðqk/¨µ;–àX­ÎCšòÁ‰ÒíáiÁ‰¿˜,^3cmt˜}¿¡w%Ð@Áˆ<°Ãz½[BÞ¤©â˜M´¾þˆ©q@T=u²×lI`Ó0q8sOyÄÍÞ7äÈÝ€¢Ÿó´öFÖ>ßµ¾ë!(c{™²ÃÞ8x£/Gþ!ÃÇò ^8&)截àUÍA—è`!=¿Y¦«qõýÔuë—}ëê¼qøŸÜË„U ‡ ¡î«¥gâÂúå‰Ð\Áòs‹~5T¢š< ,ðàe™™½­¿ÅšÈP¿pƒb±‘hj'€WÙãZÊ7‹ÿ½8¿ï +à™7{ÍrJþ°Ãá}ÿßÛ×§†çJ–®HPg9_SÅ7BðÚøàF:' endstream endobj 257 0 obj << /Type /Page /Contents 258 0 R /Resources 256 0 R /MediaBox [0 0 612 792] /Parent 198 0 R /Annots [ 250 0 R 251 0 R 253 0 R 254 0 R 255 0 R ] >> endobj 219 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/altix-fixed-proc.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 260 0 R /BBox [0 0 2404 2394] /Resources << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 261 0 R >>>> /Length 55 /Filter /FlateDecode >> stream xÚ+TT(TÐH-JN-()MÌQ(Ê ™˜(¡‘±%„‘œ« ï™k¨à’Ô°[ endstream endobj 260 0 obj << /CreationDate (D:20070215210850-08'00') /ModDate (D:20070215210850-08'00') /Producer (Mac OS X 10.4.8 Quartz PDFContext) >> endobj 261 0 obj << /Length 262 0 R /Type /XObject /Subtype /Image /Width 2404 /Height 2394 /ColorSpace 263 0 R /Interpolate true /SMask 264 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÝmqë:£ÐB)”B1”B)„B „B(„B0„B(„¾š“¹N+9þ,ÙZë×9ÝmìȲ,ë±äÿýXÉ? 1²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬ú$+€>É  O²B蓬€‘w‹uwé~¿ŸN§ÏÏÏ_;ööö~þéx<žÏçZ´ß’À¾¹´«Ï&+p·ØÎÍÝõzý;æ¹ä&4ûí·$°o.mäí³É `ÓWÞÙ—f×q`»m©bi­œs¦Çãñùù™ñ&4ûZl¨«ÁKÛ¼ÕçÌÇà9óñ~¿;¸ûl²BØô•WVtØ–*–ÖÊ9ËaºÝnooo¯tÙ?ÐbC]m^Úrݺ†¯v8„†Uúl²BØô•WVdl£ì§úP++|<ïïï¯tÙ?'…º¡Ð ®f/mÙïa?>>n·›#®y¶Ònk% ú•WVìû–_cµ‰r^~˜f,§6¼¡ìˆ“BÝPhPW³—¶Bw²‡ÃÁA×¼j·µPñÊ++ö}˯±ÚD9/üóëõšºŠ½¿¿ŸN§ð Çãû÷o·Ûår9Ñ eÿ@œ…uµ|i+w3¾ÚÏ/…æh¼ÝÖJ@­+¯¬Ø÷-¿Æjå¼ðϟØ].—y_'ûâ¤Ðø(4¨«åK[ÑûYq¡æØV»­•€mõÃ]ÇAS#+$W9/ùóûýýóëõ:ï»dÿ@œ…u5~i›qî‡ovþt:}}}‰ 5ï@ 'µ¬ôÃM¦F}¨UÎçóßM}}µó8)4’ êjüÒ¶üÜ_ðííÍ» 5ï@Å“ZVúဦFS£>ﻄ KVTËþ8)4’ êjüÒ–ëÜ?cÎÖмEOjY!臚MúP«>D_À´d©±ìˆ“B#©Ð ®Æ/mÏýè ʧPj‚æ(wRË @?ÐÔhjÔ‡Zõ!û¦Õm4’ œ\ÛݽÃáv¾ßï*ƒ:©e… hj45êC;Yá‰f" ‘Th°û“«©K[Þsÿñx¤†O§“Ê y Ô²BÐ45šõAVI…[9¹vœþK¼Ÿ1øüüT4ï@¡“ZVúáE=óùn÷~½eãýý=ü0üÓ¤mÜï÷Óénÿ~Zøaø§ÒëÒ„Ï~ç>„ÇËåÒþ‹±Â†ý {ö9ìùßÁ‡àp8„"½Ýn+ìOØ™°¹_¥úööö,ÒÒûÐàa _ùytB!ü:4¡ ÂÎhj¶XæÕÞç×y¶½¿ZKYaçd'dé«í¤B[¹Õ õóz½†->K úö·ï.гJêUVïƒU<£[ë.ÿ.Ïêô«?Ï©g5ÎUŒ½e…¡è²¼²°n•[¹’£žÔ²Bè³¾p+cþü~¿§žý%ܾ½¼E:ŸÏ©Q_ÂF³í<ÞÿÞ`ïÆõzm°‚…½úüüœÚÇ›Åæ­$¡ðK ®yXÇHØ¥PοNœŠ·›h«—ùúÆŸSÏÓ*œ«]²WÅrØàEa^MŠ5’6’«]m[kuŸáàŒnϯv2×Hþj}°{³u¿{ݾtøåŸ­¶(û¦Ã…lá7ª[åÖ©$Eû òèpàQV›¸d·¶•á?<¯¤z{{K=Ìî›& k<}€/>jäˆVô¾¯'ECIŽÌ[³Üɾ¬$ãŸ7°¹ÊsýÃúò¤ ÷ì3Æ0Ç?-+\¿Ì×4ïœú—Žÿ¾U. #wx̾i$»j$W¾Ú¶Öêf<  ;–+÷ÁšêÍVÿîÝï÷Ùß%üáò‡îúÉ J ñ*·f%)ÚO@‡²BØÄ%»µ­ üyjŘ1~=%‚/ Ã,¿Óüû½ª˜1:·°Ë7ð‡§Óiö¦båÆë@\¯×Ù#xã÷DV¸~™¯&|º7wd…/ /wx|•ÖHöÓH®µm­ÕÍ{LgGfë÷ÁÚéÍÖýîy…;‹5CgYá¼oT·Ê­\IŠöäÐáÀ£¬6qÉnm+Ñ?_2«åÛ÷³”Y†¾—Œç,}S%¸\bùqɕ氚ý|{ÅÚ*É,cc DV¸~™ohôRVøòûÖ½( ìðÔ¹üÉÉZWÛÖZÝì­âûûûÔݨÒk¤áªûÝ›íK\ ZV8ãÕ­rëW’¢ýyt8ð(+€M\²[ÛJjüdy_âùÂú\O„>?m†¼C+˃Ë%¦®[4+\2§àWeÛÜaMUÑ\“ºßJ™¯ \P(+lê¢Úáð!SÛUdd­«mk­n‰†qÒ~­>X gtÝïžWöjL-ê<+¼ßï©Æ­Í*W¥’í'È  ÃGY!lâ’ÝÚVÆ÷ Â]Ûårùùæ…ëõz:îYÆ”f|Zøý©…3<´òþþ¶¶ûóÁò°KaCa÷ö$üÓú‘†§g†›Ð°Û¿†}Âÿ†¿: Àæ­$aCçóùç;Ažå9|Óöp[‡uR¦›¥’„} e5¼¡ÀîßÇŽvë˜z>jxg¿¦¾H®Ç÷Ô$¶Væy…Z:ÁÃÏG®Nð¦¸âajç2ÚÈE¡ô=»Frgd­«m³1|`Øî¤É¼ïö¹lEÅ>Xõ3ºâwÏk rS’á ,³Ë÷z›š$˜ª~«\#•¤t?A = <Ê `—ìÖ¶òr.áÈ›²©·x©O›4›/ug:é-÷ŒkN-L=a;o5ÔPßãNË+ɤØ4•eŒ3lá°¾\hhdÝHˆ&Õ.YáúežWªV||LÝ«ðû²ÂÆ/ “V-û;{â{2iª(4’;k$k]m<ˆ¡nÏ^~ q{ù™uû`uÏèºß=¯T˜4á+õn…Iküv•¦º%©Y½u«\#•¤t?A = <Ê `—ìÖ¶2°¢Ñ¤›²Ô«(ò~Úø‘åŸðr€eÍ©…ÑA¿…;n-^2¦’|~~ŽŸ¡ð-õÀí˜1ÃFëÀŒ©‹ª¥†D&­¸ÛsVX«Ì3JÕêÙ/i•6~Q9^x­‘Ü_#Yåj»ËV7uv¼L*öÁªŸÑuûŸ¥ÖoœQ’©yšã?ª«¬pàÍõ­U¹v*IÑ~Â?Y!ô1ð(+€M\²[ÛÊÂÇÅJMÌøi‡ÃaäŸGï4'=úò>wÍŽèc®3F3Ö±Ù…9{°®Ãš±žÿK,µ´ü­R;k[+󌢵zÉKQe…_rMÀ×HvÒHV¹ÚîµÕÎÑ{¹Ä}Å>Xõ3ºnÿ3£h œúvƒá›”ñ÷ýd…ZS§Å*×N%)ÚOø'+„>e…°‰Kvk[ɸ“Ñ!ˆÙSc–|Zt|löžüK¯Ÿ3{-¬mÝÓeßztìëåjç°®°.â¤ïÕmVX±ÌsI æO¶ÓÈE¡Ù¬°©‹B® øId¡«íŽ[Ýè Ç— Öª?-œÑû-¼\.y×O•äÈ©²d…Aá@á×*œ¦*I¹~žÎkàe£!+€}ù”ØJÆŒŽÀÌž|7o<ç):$µdìý_îG»·uO—}ë©Ñƒ­Ö¼]§hO/¾É²Ÿ­•y.Ñ=Y8~.+lü¢wº–FR#™ýj»ãVwÞ]kÕŸÎè}Œ.\«düÑ9­‡¬0µòp³U®©JR®Ÿ°§ó>©e…°û1Ÿ[)Vù´¿kÈ,оÃ"ܵU¬K«½è§DMŽ®ó3<ÖÎaÍ[ ËOYaÝÆ*û‰0ãÍ8\šÍ ›º(”«{ɞɌWÛ}·º¹²Âú`-œÑuûŸEúÂe-SËì EZ¸{·Û-I| ªV•kª’”®!RèaàQV=ŒùdßJÆ\rלñÓ¢¸|`t˜kµå³¢/ïXmë%jrtaàÕEMVÃàëW¤]ŽZçm3[¸(´™¶vQ(T÷4’7’¹®¶ûnuç}¯*}°FÎèºýÏ,–,T2¬‘ ¾ú9r»Ý·>ŸÏ‡Ã!Z&¿J~xr\•*×Z%‘ËOjY!ô0æ“}+yw²…O‹>2½pɦÔþÌ^auªÔº—£Y¹&G—#x²½©ÃÚZ "+Üè8I´V\Á¬ÍêÔæají¢P¨îi$;o$s]m7Ýê†Ñõ?á[ÿóñÿ¢ÄÈݨÒk䌮ÛÿÌ"úô…kK>EW˜\?ˆ_§!ÊâePX«ÊµVId…@#í¶V6×—޹)ÛúUtÈèç-gÑ›_|ê“íMVÃàëÍ]f…ѱ©1ÃŒÍV§6Sk…BuO#Ùy#™ëj»­V÷v»N§¯¯¯á(páðW•>X#gtÝþgÑìiáZß…Sw%êr5d °V•k­’È Úm­´?æ“}+ûË ?>>þ·¢ÕªÓ˜U}ÂôõzÍ~½ÚèåÀj?MVÃàëÍ]f…ÑZ½|Úˆ¬°ñ‹B¡º§‘켑ÌuµÝD«û|!ÚË~QÆÓvý>X;gtÅþgË—Úí­D]´!ZhÒsPœn+‰¬¨Þnk%`c> É ˹\.ã÷*Üe~~æº.ôÅ'}¬aðÜòË ×›’6~Qj$[¾Ú6~:‡²DUo°ÖÎ]±ÿ™E4xºßïË?9úNÉŠ+QmˆfûúúšZÚëW¹Ö*‰¬¨ØnË `[c>- µðiÿ[ך5*õ 1÷ÑáoÏçó¼›èF/›:¬†Á×ÿš»Ì ›Êv|˜Z»(¬yÜ5’ý4’ëô¦*~»Ð)Ÿ5ØkꌮÕÿlÿv¬©«@Ñ/5ãЇÙq[ §[ÅJ"+¼ Pΰ‰~¸¬°åÑ•ì¢ï¯)ý|¯¬°t_ÿ;Ê û9L²B¤¬pëYáívk¡/·f¬µ†«Jÿ³ýÛ1Yá·ÿÇóùœå@W?Ý*VY!Ðà]€r€MôÃe…®dw½^—¿£gÒ{Cd…¥ Ä0øúßQVØÏa’j$e…›Î '…¡ƒôñññ\“08N×ÿ—å{­Ök°7»~ÿ³ýÛ±~²Â*{R÷t«Xž²B Á»å ›è‡Ë û¼ý9ŸÏ ãz¹>O³YánNmÃà›+ó–¯²ÂÆO6»FrÇýÆMg…¡£ò2,x.9x»ÝV+äú`ÍVÚ5ûŸíßŽÉ ÷Qåd…RØßÀ£¬:óÉ»Yá¦Ýï÷pýùù9ûÞ¹bM–¶y·Yn²ÂMT'Yáv»FrÇýÆMg…Çãq` áŒáeü^Eû`WÚuúŸíßŽÉ ÷Qåd…RØßÀ£¬:óÉ»•N²Â*oHYÿ&úr¹ÇIßÖZ™çz½þý̰ç›8¬†Á×ÿš²ÂMT§ e…/ kwd?d®«mSññx Ì%œ77­PeÈÞÛPo¶\ÿ3‹è¼Ô‘³P_~ñh„½õ©ý3{•k­’È å'µ¬zóɾ•ýe…ÑÛÆï÷Ôôãùüí×××ËÅ»^޶µ0zÙÔa5 ¾þ×ÜeVX¨VË ¿(ª{ÉÎÉ\WÛ¦bèÆ¤‚ÂÆÿå}°íöf3ö?[¾Ô6urmñ¶“Óm^%‘ËOjY!ô0æ“}+ûË £«Ó¬öôr›ÂêÀ¢=§Óiýš]Ulà05uX ƒ¯ÿ5e…›¨Nm¦Ö. …êžF²óF2×Õ¶©ƒøõõ5oÞVS•a^l½Ù…ýÏ,¢µ(Ë¦Ã‡Ì ²e…­U¹Ö*‰¬X~RË  ‡1Ÿì[Ù_V ·*dêùü*O·FoäÃnâ°_ÿkî2+<%†‚e…_ Õ=dçd®«mSñýý}ÞÎ7X¦öÁöÔ›ÝÿÌ"Z’K¦¦~‹LµÖößÜ lû§[ÅJ"+–ŸÔ²BèaÌ'ûVö—F{Ym¥£ÆÍ{D¿DMžú"ž¦«aðõ¿æ.³ÂèÓæË‡‚e…_ Õ=dçd®«mS±Ä;û*V†I}°õfKL]Ò6fÙttÅË1‹È 7qºU¬$²B`ùI-+€Æ|²oeYaê;_†ôév»å¬[²—Ëå﾿¿oå°_ÿkî2+L o÷¢Ðìajê¢P®îi${n$3^mÛ9ˆ ¶köÁöÔ›×ÿ,zf…]ªõd…[9ÝjUY!°ü¤–ÀîÇ|Jle—YaêE¦þkc°nÞ[xÚ9¬›_8ó¢µrÛeVø¯ÌØ”¬°ñ‹B¹º§‘칑Ìxµmç î,+œºõõf+–|´$®0¶6reYáVN·Z•¤JVØà5XrRË  Ï›bYá_ÑéƒÃá ZV¬KÍ¥zy‹ÚÎamm|ö*X*·½f…%Ʀd…_ÊÕ=dÏdÆ«m;qgkNÝúÎz³K>õöºÙu)üaôÃ!ÛPd£»Wè‹4UIJ‚­\%ͬú¼)–Ž¿ Â`–b¿Ýn#WñÊuO—å.ºÎÇÇÇš‡5”Ûì'±9¬­ ƒ‡#ø÷ÏO§ÓžšÄ½f…ÙǦRè0µsQ(Z÷4’Ý6’y¯¶Äè[R™çµû`ÕÏèºýϼéŒ;­™ã_o'+ÜÐéV¥’”>[¹&KÚmY!ì{̧ÐVöš¦†ƒ–°<ãñXåAô¯¯¯…Óïù¤Ô3Öèj<ãŸemä°¶6 Žà’1«M4‰{Í S{2cl*ÔáÔ‚u²Â¦. EëžF²ÛF2ïÕ¶‘ƒýó†îC÷iv Y±VýŒ®ÛÿÌ(U’3&ò§N®ñGDV¸­ÓmýJRúlåš,i·e…°ã1Ÿr[ÙkVø/ñÌ䤗õ Œ«T\´jöísô Û1/GË2ï)”^êÆyÒ=x ‡µµaðÔàF®y-4V;Î £ÃYSÏ‹ñdYak…*ó4’Ûm$k]m[8ˆ©E8'¸¿xÉR¨ë÷ÁêžÑu¿{^©Iš“΋ÔÉ5)g‘6[å©$¥ÁV®‰À’FCV›¸d·¶•g…©÷D|߯¿!º^¯Ñû¾Šuéãã#ìÿãñ˜7.4éùüT~~~Ž|3K(ÀÔÝw0ò[´sX[(“•âÊ}Íg…¡þ§_8=_Ž’… ?prÉ ¼(”®{É5’µ®¶ÄÔדI §f…ë÷ÁêžÑu¿{^©7xïïï/§Ü†_Èm'µ$²Âf«\#•¤b?¡©k"°°)+€ö/Ù­meÇYáðßÏ'EÃ读¿p~r:R†V¿qþyëz8þ~…p³~~>üÆ<¯;¦ CY…Íý¼‘î@¸a¾2iÆzhíÖ‘¡‰ðe/—ËÏý,‡phž ²m¢IÜqVø/=w&uo·ÛËS[VØìEa…º§‘ÜS#YëjÛÈA˜4ýööú?¿ŽÝ³4B) ¬8º<+\³V÷Œ®þÝó E1üžçׯÂ?>¹¦¾èMVØr•k¡’¬p6qMru e…ÐÏ­–¬pö(Ó†úK…öÉ[3ÖÜÖkƒÃàc۬ƵšÓÖî€&EòîSõ‹Â:uO#¹›F²ÖÕ¶ƒ80¦=Uj0¿å>XÝ3ºµî_ƒWÛ/³“6^åªW’Fž)’À¦e…°‰Kvk[Ù}V¸ðnhÇã‡ãïšÛ)ªxXÛ D&Í¡6x4üDúÈaðÔŠ¦S;…ÕêžFrd­«m;14kcVZ~ép8Ìk!9(UÎè…O“ Aá?YáN·º•dCÐþ5¨ÛtîÀoµd…cÜï÷ŒÏ¥?­ù¦•,£d?|óQ¹Î猥GÛ9¬mƒ?yaÓ&šÄ²ÂpgÛüïÇÛ»d…_Ö¬{É4’µ®¶MÄP“—ß³þkVÂÎ/É~>Ýúó=G³D¸ùW˜á[LÝÖk³H(Ø£ˆ›h{È ŸŽÇ㌑ÞÛíÖÂEas‡©ÊEaýº§‘Üt#YëjÛÚAœý4EhT–üݨØ«~F7õÝ3 ×ÍÙÁkøÃ%7²Â­T¹Z•dµCÐø5XÒhÈ `—ìÖ¶ÒUVøt½^g,,óTù9 _E¸§;ŸÏ3î —DœbÒ΄bÏ2°úam<™:°¼‰&±Ÿ¬ðß3GFVæpöý=§d…_jÕ=ä¦Éõ¯¶mÄñ1YøµÓéô7)X²Uú`4\M}÷¼Å8éKE/»{ê´³{íT¹õ+Éʇ Ùk"°¤ÑL½õ;N___o‘ÞßßÃÏÃÍf¸jsˆãçþÿ]C&|£ðóð¯áwFœ/ûŠÏÛùPVKò¹á_W{˜|ë‡5—pÐÇãßBø.PJå¢[– õ3£ÏÏÏ_g÷÷9µï ì¢àkvØHnëjÛ±û;‘'´™Ë»=MõÁ<£Ûù³ær¹DϬç× ÿ~¡‡“«ýËV­*·ûJâÆ¶KV@ÝΧbW[šê@Ê X­ó©XÀÕ€¦:²BVë|*pµ ©¤¬€Õ:ŸŠ\mhª)+`µÎ§bW[šê@Ê X­ó©XÀÕ€¦:²BVë|*pµ ©¤¬€Õ:ŸŠ\mhª)+`µÎ§bW[šê@Ê X­ó©XÀÕ€¦:²BVë|*pµ ©¤¬€Õ:ŸŠ\mhª)+`µÎ§bW[šê@Ê X­ó©XÀÕ€¦:²BØ=Y!ôIV}’@Ÿd…Ð'Y!ôIV}’@Ÿd…Ð'Y!ôIV}’@Ÿd…Ð'Y!ôIV}’@Ÿd…Ð'Y!ôIV}’@Ÿd…Ð'Y!ôIV}’@Ÿd…›ðþþíh…Ÿ+øf|ÒÍË/²B€¦|}}¥îÍÇþ¾ïõz=¿%ÞÞÞ‡Ãù|¾ßï+ìÆét ›û»'ß;MøÛí¦–À€p­ —ïÔUõûÂú¼Ð‡n@¸ ¯p­ Û½\.a>??Ãþ„û»·ß»ºaWwÙÛ4ã“Ù…JžŠ CXùìòÒ)+hÇétZ?(üøøX¿Gw¿ßS©hTØÉóùœw®×ëápH„ XkTÚt»ÝÂõ1·ôùù¹Îc9—Ë%ôCfïjè<„o>dÞÖw<ò–êÊ>Çm³”¶>j:†£¬¹Ø4Y!@Ën·[ª‹Uô ö¿ãE¿æÔ”ð×4„Ùãrß®×ëìøûdµÄµõï£GK„+þáp›½>6ï8Xè Líœìxäm8~ ÿZb£²ÂB HêPZg`Ód…-ߌ§FWŠÞŒGÊmî|>/ïp~~~ÎO—LvHÉ>á¶â~¿‡ër¹Q¦páÎ2mÉ£Jã÷säD{yxì­h·vI)•+á}Œš¦Ži¨ðãØ.Y!@³RãW¥s¨ãñ¸Z.ãÝûûû¼ }…úÀ^Ý@‡.—Ë&Æš†ÆÌÞ%xÙEÙëÈÛ˜9›áw²oWVXTêI?½_€í’´)5ÔVb8å§Ô£Â%¶UâaþÏ3T“èG–µJ5…®BÞÅQ³ìð^GÞF®Þ}»²ÂÒR)ðòWP…¬ A©ÕGßßß‹®í3°NTöm …á_Ïçó¯w…ÿ=NÃËšÍ(¢áÕÃÂæŽÇcØô¯²ÂO¾Œ;-F @'Æ…áª.åáúkž]¸v‡^.—pÍ ¿3&`ÊÞÕùûnÄgWäW¿â¹«ÁsWe…ÆO2Í0É W¸[ùû~s+‘l—¬ A©'u˽¦0ÜÔG—-Ô£ØVø§—# Ã/Bš:õ25B8¾´‡×1›·2*lÈõzŽÞÎçó¤!\…Ãå5FÌ C`ê…;|lø«—ùæŒÞú [ô‘ªè1;ƒ¬p©ªô*(” +hÍý~_á¾;lå93îx<ŽY‰k……©aèÀü…_Çw‰CQLúÛŸå™Ï´)ûöx<†Ó·%óŒÂ6ú UÆîG–ýü÷ß4º>ÕŒÜú [ª³—kùBEgls¼Ôóž”ØY!@kR£L¹†Pª÷èR_pƬÉÔpÓÛÛÛÔ™~K­Å”}ø š2°\@®µ¸ÿ.€0õχ§=f\º!t'¢ý\}¶­ÔŠè3]ŸŸŸáŸ¢«Cä]¶]V¸ŽÔ™úÕ `[d…MI-á•qü¤n.õgçt©WáŒ/±···\oÉIe—ÞZÀ^ ÄpÇã1û¶¾ç1MúõL ½ úïúä¹úl[©à@ŒX´»ÛC±¯/µÐÇÂgðX™¬ )Ñ9w“fÉêæÚztàhá*©·áT9‚%¾ 4+•äí½üô|îhüïL{,>ýZŸ[u:Švww_ìMnS ¶EVÐŽè°IöÇò+öèÊ-9¨<ëÄÔ˜¤ê À.¥–÷\ø P.©(sÍgŠžë¥çê³m·Vü\ò=Ú'̸(„¬peÑ©…yו (Y!@#RÓúå—ëÑ}?ZŸýòè‹k-CºÝ.ñår Çè×B¸oooá'Çã±Ê<Í\Ç÷·û5œõü‚áŸÂ/¬0Áäy²‡óúëëk`gÂ/äfÿ¹Ñ¿gÊó—(gɇ›ø;tüñŸð}C;·‚ÕÚnƒõ­Ð·«U¶ý¬PzÏBþµWß»ÔþÈRË«¼SØô(Fû˜o—ëÚ'+\Yjja#Ï 0»G'+XY4G«Ò•*·Ñ·1æZž(úÈØ£“´Ð1«r]®GW4΋ùVyOʆ²ÂPø#SÂ_±Â¼£6²dÎçóËqò1›»ßïÑxzäwÌmœN§%·Taf¤´cJr ™}n†ónvÉ/Éejm·n}[ç´ª[¶Ù5{°¢ ËË' ªÏßLí[õ™³©·$·ó.Åmõ(ö÷þVÔhb•+ÆÝAV*ðÀ£>³Ï¯óbáŠí<÷@Ƭ`M©ç{«LÄ(×£+ÚWŒ±®?ç"ºjÅù e¾$½ ßhÆèôË p½^Gf[/·•Z k’,ƒÉá,ž4­o¸ØGnôñx,Ovæ½ÌkFØ‘å^²Öv«×·N«ºe›]›küaªûÞ½I#KÕ£áÔ±ÞʤÂÛÌ 'u„ÊÕçd…ÿÑý*ÀùÔ óýÔÞ6µf2ë\ e…óDçvíïu{EûŠ×ëµ…‰™ÑØ÷ã㣩® iê˜Ò@•z}Fµ™1Y²Äb\ÿC¦¥ï­òntR&’±ä'µµ¶ÛB}+}ZÕ-Ûu®¶ÕV–4¶â$¾Ôeaû¹\*ÙТˆ[d‹6;©2ž’YÐ>²Âƒë…ÎxçæÀÃY"ÚèÃQÕ›f_:e…-ôÊfŒ”Û™Æ?ù_"+\?¤›4DVëh.™½²$ÎNU€Ûí6u—¶’=Ú˜=Æ•eúÒÔó%{:9~üpR,•ñ^²Öv©oEO«ºe›]›+Ë“K&-غï©lsf;§5÷9Uæ©kÖÊE×rVøoðq‚I•y`–b®UySkV¸×Øh¯CV°¦Ô8I­±¬•³Â\S!¢¯_Y?+ŒÎW|/Ò¤qæ°Ÿ?—Œ»ßï—Ëext}Ò*¯©á©Œ·à «†ú~áײxáLJëg÷/3»ÃáŠ÷çi <ìL(ÒY²Ñ÷÷÷ç×ÿyÒ=rØ™\)üÓËó4ÖÿŒT~}ÙçÞ>¿ïÀzh/˹ÖvÛ©oåN«ºe›]³+Õ ÿÚŸ1 r­5?ê[hvjMxh·>j×òG'Á ?Y½ú,_µrOYá¿ôìÝ1è—’qÞ_*ŽÜP@Ð-Y!@uÑÌ6ßp·Pta¢Œc›Õû¢©u¢*. ÷²¯jZ¨Ã{x¿ßFþÇO)}‹102üñññr?Ã/ „“†¹¢Éõ¤p!|Bøµ¿#¨Ã’z1☯ÿo0Ly §¢Æð‡#ë(ῳØ^þU­í¶SßÊVË6» ¬P¼Ãû3ð ÔZ ý ·xao«„˜©³âÌ-vl¦ŠÆÙÃW½èüåå•yIÑ5¸ÂF8ËRmòÈ*ºÄgåAt?k-–@Æ¡Y!@iѦâ«=Êõè¢c­Gª÷E£_°î°äËAéñ•šÏ2¾®NZÙïëõz>ŸŸ»ýüÔøù¤:–ZékÒ`Z*Z1Å&|åŸÃn¿™Z.rÒ´ÖHe`ÏSÐæÍ#;ü}(‡³Öv›ªo…N«ºe›Ý&V8 #Û‡Ô Ö£)/×w ­âò™b“¤Ž×¤ _›£vÍîm*5Ž¿S×…•ygYáðúeS6ïâ>O´7²­Œ O²B€6»d+©åǸ˸LhݾhjRaÅC9Ðáÿüüœñ©ÔÌš‘McîÎçó¼a«Ô’ À¦ž½ùQ©eöZ|᯾ÏÔï¤VýšñõS#Šìј`á€|8q^N¯®µÝ¦ê[¡ÓªbÙf×þÁ Å2uæ]ª©uÅžZ8¼¼j!© V­•Z·Ø±Ér® Ó£iþÂõÛ÷—þ\ïwøAŽÔó%–Mõ‡Ýñl±×!+Xͼg°×ï"=Èõe+öES³<*®%;P&³çr¦©‘ør’ã’‡Û£å?û9öèÄÀ‘G3:@=éBQ·Ûm`ÄuƲo¢±þÀ×B®ÐˆÕÚnSõ­ÐiU±l{;Xã§Žù^'ÍÍxKf¨f¡<Ã>‡îAöÚ%+\_ôìùESø…3Ñv™þKÏáèf¤æØZ4ÕW¬¸ ?Ëo+d…¥¥¦´ÖE,úṦV,ÉÔš“ÕÇ$³—Itjä.^ød{ö7E¦Bü—svR“òŠÖ„è¸Ü’Ó*õõS‡©Ö©Wk»íÔ·r§ÕnníÛ?X³¿Zt¡¿ŒSõsíÒ$aÿÇœÏçåÑáj+.ËxjŒ)ðTº´¤ì5+x7q4]MuKоì ú$öbz€É êŠ>J]w¬¯hnù;ïRRMµb#/gÉ^&©A§Ù;3{NÍOѬöp8,ùÌy/ÜY’¥æÝÕ…ƒr“&*ö–¶SßÊV»¹µoÿ`å½ôLz³j ©L3¼½½=_²™w¼k[xC_aáÜÀhþµdâÛ^³ÂïßYœ©U/f/Š>Ré”°Z¯CV°šè¨ZÅ5Äþ˜ßøüüœ=pq>Ÿ£ƒ!+ôES3C™¼P¢Lf?.¾òàÛÂi)óÒç*oû{D–çÑåS™~­÷¦5µÝ*õ­ÜiÕàÛW¬‘WŸêeþ|1eÞa¥Ð/šzÔd…+[øÎÁìÙ÷޳ÂéÓ¿VHMõ-½ tƒw7Ìî;É ÖÑà“·¥{t“ÞÞÞÆ+ýûoŽÛáp“,ZV©Iví ª—(“茳1C@…PtÀ<Ëäš©©è’I—³Ec‹åmH´TS$£CÄ+Ì®²Ývê[ÑÓªÖ1u°~»FF`GÆ †óÞ¿™½WPepl+ƒlË_Q—ýýÝûÎ ºÓßsSMÄ k6¸j +tud…KD³ÂºoôX¡G—zÓÊwbø|]Q(‡_£L·Û-üðt:}}}™¶Pt\b (lçÙéG3:4fÙBU+:\¶pÁ§èùwá lóD'fiC¢çfô7Ö.:»¶ÊvÛ©oEO«ZÇÔÁjíz½D¨*¡U¾âÏX˜tä‹8e…Õϵ©ËËç]1x÷Yaê&⻡Žö“×yqÒãF´¼Êéü¿u ìIê wcÎâð·álw­¼ÝvêÛ ûSë˜nârÖÂÁÚåÌívù–Ãá)•ÎîÈ 'íä¼çdR(¬\tÛÊ Ç´Õë¿ÝÀø0À>Ʀd…=ßJWÙ¥óùŸ¾HqÞ™µÂv;Ì ëÓ}4…ö§“˜™kÒõz­8HÕIVÎèÕžT™Úzô–´Ï¡‡¼~OÕø0À>Ʀd…=ßJo±Gw¿ßË­3¶Ý ðŸ¬pÝ—þ=1ÏçóËUί·ÛmVX÷˜n½q(´?ýŒÀ¤rᇂ2.hYëÐ4~ˆ‡g}æ5õE“]e…/‹¶» f4ݲB€ºý±ºñÓ{tѥƲ¼¬pÓAa¡£¬1fg¡ª]1ËŠ[Ñ :lný=™Zªµ^7\˜—Ëåx€Í È VÈÊr55ŽQZôM‚Ë?v`øåëë«ý ð_Ya¹“hê7­r:7؆ŒñœžΣ—/ÂË{¢-ßn;õ­µ»Ö1ÝDãPè`õ3 g_~ßÔ+ W¨„=d…¥IÁt’<ÒV±o0»£@kC²B€ÕÈ —‹ŽV-Ÿ500Ƹ”„–æñxœ7W¨PÕ ‡£ÐóóÑ:0pô£3,J?ÉÝèšS·– -ÞÀä”r8o»íÔ·–[ìZÇ´·ƒÕÕÌŒ§0«t#ÙCV= r­3}lÒÉÛCVøx<¢múÖ|TCV°§¾–¬`Ѭpê;YVè"¶\†Ñ!‘…yëÀ0K®× n÷hF#€1•¶PÕŠf—YòÜh5a.·'S¿þúo&Z.5·ôèâÔí¶SßÚo±kÓ~–¬ðå÷Ȭ‹†&=d…Ëã¼˃ȲÂhõ~–Rªù]ç¥±Ñ­Ë 6Ú×’¬cö­•»ˆÍ`tñ¥%#!ÇcàÝ[ucÜFŽæì•Á U­èãë/_6Ft)Å:º'¥OŸÔF7±Fî/Ñü%ˡ̸ÝvêÛ&ZìZÇ´“ƒ%+|y¹Oµ¥Ÿ©Ø}V˜š³™ëÒ“úüñËî>+L-ñýJÖTP¾Â3o ÞÝ0ï"%+XMôN¿îœ mõèòN̼ßïÑçäŸ.—Ë>:üK>0ÂìpvåÁ·ïá²yRïø“ÇãQ¥æTYa¯„e^e»Ô·M´ØµŽi'«Ÿ˜Tä7f–èÀó?åçÝg…‡Ã¡t÷5ÚŸsí;+L?/ý¡O’z{lénI•%Ù(q‘’¬¦Ü¬‡êã+ˆ>·<{£Ôðo–±åÝÍ%oÇ+Wµ¢{µp¥Áè|¨—¡Ñ=)½îVjîÀ§Öj&m·ú¶‰»îîû`5rôW¸DF“©‘ L-,·ç»Ï £!TÞµ¢OÓï!gÏ ÇOi,}@S&ýíl ôl‹ž³óf^Ð`§]V°šÔý~ÅAþ­ôèR¯b™7‘ú´ç¤¹-f.%ŽfjÄuäZ¹ª•:|³GöR«Ÿ½|?oµ):ÙssïÖ¬ÛþLÚn;õퟬp;ÿ]g…Eç2§zJã;Ko-,šì;+L%Py;K©smäõtIÑ•‹º²ÐÔ Ñf-úX]ÑÎmƒ·6̾HÉ ªwÉ*.w¹‰]jøw^8’Hy>¤½éÁ¼G3:<5~ͱ¢U+:²7{B_tͺ‘Ó¢_3üíŠt»ÝÖzM­3–k–ÇË­gDŽ?ÄZÛm¿¾e9¡j•mv;>XMe…Ïô¡Äs©¤oüüд±P\¸ï¬pöÔÚ,‡~äq_RtÑÓ<ËšËh´ð‡w/µ ïÂÖ)©ç—Üñlqè@V°¦èHHÅ Aí÷è¢ËRÍ{F:ü~jÔ¥Ü(ÊFfª Æ ­Z©øxÆAL}Ó‘¹[*zžM„Zúý™S¿þò¸pÌÖ¿‹zámÑ¢h km·©úVè´ªX¶Ùíø`µ–~·uÃkô¤Ê9¼iöy‘ûÎ ‹>š2æä-]tÑ5o³¼ `áMÈpc +/ñÁÞe Àj×wY!Àš¾e.b#eu¿ßSOGÏ›ZÇ©ÐøI#Gsêàÿ@¢:i°½tÕJÍ­›´“©o:þ” ŕړqöù|þùi¿9pjÌÿ™ŽÉ ¦K©EíÎëZÛmª¾Í k•mv{=Xmf…ßáÅÂð(ÔºkôŒfmà™ŠŒó"CË™z¢i]²Ô¬±‹0¤B®ÒKþ¦ªÊòŸG.]¯×ɳÇ:RŸVq Øu:üŸŸŸ#¿c(ùÔ0ûÔ±ÁÒUk`ÚȘàð ¹’èá ,cCÁþJ _WêÕNßqÆøAÎÔé6þÈ>c‚‘Õão(9r®D­í6UßVÈ ×/Ûìöz°ZÎ ²ár3ébŠtøŠ?»½Œ ŸUb|UÿÙ‡¿~1âvñOKÖ͵¹1“Ô–ÝÀõta²6{¯CÙ‹N÷Ëžó¦ŠnýGDÈÛ£“Tì•z·.ßÏ!¦óùük´ö~¿‡Ÿ„Bÿ:U-‚øßºìð‡â …ŠúçHQøïð“ãñ80§cÒê£;“÷û¦fs|?¿ìÏ?yV³áo:ãd”{&wá.—ËÏGžu>ûÀhóðF_.²7æt›Q‡Ë<|Ó¿[|Ö±ðóá-?2Qk»Mշղ•Ë6»]¬­\hþÖœ_Õà»ò„_.ðy´~'’¡A õ¯Käw›þéÙb¿ì¥Œ94›è`,™è—÷x½¬ Kiàñ€gü=pù.±W©ŽÁ¤µ=S'WÆG82Nª Í¬ ¨èÈj¸£ßP°Öž¼½½-yVYV8ÛŒ•¸Ö)“áœb†ÙÓ%²ïɘâ? žqëµêX u»z}[?+\¹ÝØSãÐyV˜Å ðiÌcë÷6ÑÁXòÁyRéä¼ Ä:•$û^¥¦iO8°Ši®÷uFãȼþ ²B€¤^#RbþÅž²ÂÏÏÏ…c†²Â5üW+“Œ‰À®ìÙĘ–_³š©cÔíºõmCYaÝ p—«·¬0”y®ÅÞÛÝÛÛÛñxœ7®µFt¶]é$(z¤^N…[^JÃ+Ê®–\Óg¼^sà¢å“CS îfa€}“´Ü1;[鮼'ooo3FHjõ‡w6„;»ä×,“áõ׌62Nô¿JØý~X?-ûÖG®¿7ÞÈqÅZÛmª¾:­Ú)Û=5{Í ŸïW}¹vè‹~‰*4ü&Ê,O4ßíö;µ’ yÔ-/¥P±çÕêŒ{5ðæÄÙ÷èÂgS«¯»ËØî”¬`}«Ý_o=+|{{Ë89¥Ï¬0༠`áDΕËäv»Í˜qVoø¨…XŸψh/—ËòÄçùjÅ—¥¶5{ Æ’yCµ¶ÛN}+wZµS¶»iöšþlëÆ¼ppjTèõÍßBëšwŽa8qÂuvjµo¿ƒQñUtÑíWŒ,»⌶"ã^¥Î¦p^ÌnWæú ×ø]í¹GV»ÚÊ Ö—zr8û¢mÍ ŸÅ’WVì·–>ÿ)T­ñAÀ¼¬jüΔv{RÞ~9Ë”Õè9êð¤ðîýýýt:-ÌYæ ƒÏ;ãžÓ‹fL#ÉÖ¶ÛB}+}ZµS¶;hvŸþlîBÛµ0kž4)¯bUÿ®ð¡¥æê¬PD_E7ã½ÞKiêV¹öjàÚ½°-xqáìšZE¡å«ë(g€1¢#Tooo{ý¾—Ëåx<~||DGŸ>þ~á|>gÈÛáÿù ÏÑÑÃáJû×PXøÉ‘Ïv„¯ê[ôk†ÿ ? ÿ~aoz»ÝN§S(Û¿³¾w¦D±_¯×ïíþö §á÷¦³œq?7÷÷~Óð¯áwò>Pk»ÍÖ·•­ƒµ9¡=y^îŸ5'5KëÙ…ß ¿YèI­th Þ„ŽY´Á|^"ÿ†ß ßN/¥¡V<{­¿êÃ÷Õ34}Õ«nEÑÓ$œ#jÀ¦‡d…U\¯×u¦Òy‡_±Eê’=‡§›#+hJôyûO-¤J‡_±Ejî­’ØY!@SL-d…¿b`¹Ô› M*ØY!@kRS ½,‰\~ÅÀBáöĤB€}´æ~¿GûT‡ÃAá¥Ã¯XXèxŸç}ZôM‹ã—ëljgÜÈNVÛ:ûV؇ٟ–š=·Åip ;Y!lëì˸Ñ56Ît‹ÎÅ ÚÖÎ4¸?P‚¬¶uzfüü誘—ËeÉg†?ÿû™aCÛÚ™÷J¶NÏŒŸÿñññ÷óÇ’ÏŒ®´6´­ip Y!lëô,úùïïïË?6|ÈŒÝnjgÜ(AVÛ:=s}øívûû៟ŸË?9|ÈßO›ÛÊÎ4¸?Pˆ¬¶uzæúðrïΛñ¦¿¦v¦Áý€Bd…°­Ó3ׇGC«,qØétšúÉMíLƒû…È a[§g®Æa×ëuù'‡É’ÖÚ™÷ ‘¶NÏ\þññ±f6·•ipàÿØ»£«Æ‘­£„@ ‚cp )ð8óF N)8§ _ëç®ËF¶J¥sªö^ý2Ý #é ûÝ+Ñ !×éYjãZa¢ý€•h…ëô,µñÉv:–où|>i…[íLÀý€•h…ëôlrãѾҼï<ÜE+„\§g“× ílB+„\§g“× ílB+„\§g“× 8\ÿ`Z!h…Z¡V¨ôI+­P+Ô ³ S ,­´B­P+L±®P+(N+­P+Ô µÂ'–Ñ UϦPö•æ}çµB€6h…ÀªgÓn·[iãçóùrËãËeÙ™€û“®:yÒ ú­ðããcù–Çc‘V¸ÕÎÜŸDcK+(B+ê·Âãñ¸|Ë¥ZáV;p-­ ­Xõlzyy©™ÃƗ˲3÷'ÑØÒ ŠÐ UϦ×××Ë¿¿¿/ßòÛÛÛå–Ǘ˲3÷'ÑØÒ ŠÐ UϦ÷÷÷•¢Õ¡-ÔÎÜŸDcK+(B+V=›>>>.7þû÷ïå[Þï÷—[_.ËÎÜŸDcK+(B+Ö>›.7¾Ûí–oöùùùݵ3÷'ËØÒ ŠÐ µÏ¦ÝnW|ûçóù±ÊjgîO–±¥¡kŸM///ÅwÞäoú_(×ÎÜŸ,cK+(B+Ö>›&ÓÕÂßÊ7~úc‰-ÔÎÜŸ,cK+(B+*œM“/q>ŸÛÚä36çïs¨ ¸?)Æ–VP„VT8›&Wº‡Ç¶öúúºd-^¨ ¸?)Æ–VP„VT8›ŽÇc©Õs×ÖÍ/‘qgîOб¥¡uΦçççËWÙï÷÷ngü”ËíŒÏ»3÷'þØÒ ŠÐ :gÓûûûä -\çhÜxÞ ¸?ñÇ–VP„VT;›v»Ýäk:nîø7>=ûÎÜŸàcK+(B+„æÅ9ÝN§Ó î÷ûÃáðí7ëÿùöö6ùhÍ?~Liñw&àþ[Z!@Z!4/Ôév8ÊîÆ’l†Ú™€ûyli…Eh…мh§[Á"6njá›jgîOر¥¡BóžnEŠX©Es¡v&àþÄ[Z!@Z!4/æév:v»Ýc/=~bÙ_ÃjgîOÀ±¥¡Bó"ŸnÇãq¿ßÏÑñƒÇOYé µ3÷'ÔØÒ ŠÐ ÍÏçÃáðòò²Û힟Ÿ¿žÔãŽ9þÓøã‡õ¶3÷'ÈØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ ŠÐ H7¶´B€"´BÒ-­ ­€tcK+(B+ ÝØÒ  ¸Ÿþýógæö÷]+ ÛØÒ  ¸¯EïǨ÷ØGj…¿ïZ!ÙÆ–VÅ}‹z·»Þ¦ý¾k…d!wÙõn¤½{?F+Œû}× È6¶´BXÃüºw×…¡¿éZ!ÙÆ–V+™Ùøæÿ«Pý;®mli…°ž9¥oæ? … ¾ÝZ!ÙÆ–V«ú±÷Íù{¡0Ç÷Z+ ÛØÒ  ¸¾~ª~?þå×ý¶eBÑ H7¶´B( 7sáí¿¹ µÂ€´BÒ-­ŠûçÂp½ÞøÏ¡P+ H+ ÝØÒ  ¸¦ W:ànlP´BÒ-­ÖP*BaZ!鯖V+Yž ¡0­€tcK+€õ,É…ƒP˜V@º±¥Àª.cŸPØ*­€tcK+€µÝU …¼´BÒ-­*˜ Ç?BaRZ!鯖Vk›_ ¯åBïaŽo´V@¶±¥Àz¨„—¹ÐÛ˜æÛ­mli…°’%¡ðO.ô6fúŽk…d!·¼þùãÍÌô}× È6¶´B(«`(” “}ëµB²-­J)òÐQ¹0ñ mli…°\©%„ÿü?¹0ëa mli…°PÁ…„Ÿ­ðÚ6½ÕÑ­€lcK+€‡-_B8Ù å¬ǃV@¶±¥ÀŠT­P.LyTh…d!Ü«`(¼Ñ åÂ|†V@¶±¥À|K*ád(¼Ý åÂd‡‡V@¶±¥À +áµPøc+” 3$Z!ÙÆ–V?Z/~k…ãkÍÏ…¾/Ꭽ€lcK+€–WÂÛ¡ð²³s¡ïN¸£E+ ÛØÒ àš ¡ðÛ«üyé»>˜(ŒV@¶±¥À¥õâà×ð÷éééß?¾îÃíÏ"âa£mli…ðUÍJ8ü åÂôV@¶±¥À›‡B¹0÷ñ£mli…0T©„3C¡\˜ø(Ò È6¶´B:W§Þ å¬ǒV@¶±¥г°¡pN.ôí w8i…d!}ªV …?æBßÄp•V@¶±¥С,¡ðv.ô} w\i…d!]©P ¯½ôc¡ð2÷èÒ È6¶´B:±a%–…B¹0Í1¦mli…ô {(” sfZ!ÙÆ–V@Û¶­„C¹P(&8Ø´B²-­€Vm^ ‡Ò¡P.Œ~Èi…d!Mj5Ê…¡:­€lcK+ 1*á°f(” ã{Z!ÙÆ–V@3–TÂ\¡P. zj…d!mR ?i…„Z!ÙÆ–V@v¡*á'­°ÓCQ+ ÛØÒ H-`(´ÂnF­€lcK+ ©˜•ð¡°ÇcR+ ÛØÒ H'x%üC(ìîÈÔ È6¶´BrI W]QX*eRþàÔ È6¶´B²°œðéËâxˆxˆj…d!ñU¨„ñ—>]|-ŒpªV@¶±¥œå„“¡P+Œx¬j…d!a©„7B¡VñˆÕ È6¶´BbJ +T§ë_Žƒ$ÜA«mli…Dc9¡P˜õÐÕ È6¶´Bâ衎û 6{k…d!A4 ¿î‰PØæ1¬mli…l®“å„ß… ÉZ!ÙÆ–VÀ†ú¬„?æB¡0ëñ¬üí|>‡———Ýn÷üüüõ"3þçø—ã?0~Xo;gli…l¥óPx- …‰i­ˆg·ÛmrvÇý~?ÿš3~ðø)=ìL´±¥PŸJx- …¹l­ˆçׯ_•ÏîÓét(g?qüôVw&æØÒ ¨iI%Ì ÿ)Ía“æðÖ `Îçså³ûýý}ùõgÜH{;vli…Tc9¡PØø®Á¼¾¾Ö<»‡C©Kи©–v&òØÒ ¨@%üºŸBa³Ç¹VDòññQóì.²ˆ¯Ô‚¾P;|li…¬M(¼ÜI¡°ÍC]+¸ W:»O§Óí_ü÷ööv<¿~ÊøŸ‡Ãa¿ßßøÄÇ~]`¨‰?¶´BÖ£ÞØO¡°Á^+8ŸÏ“]õìÞív“¯õëׯ۟;~Àøa“Ÿ¾ßï³ïLü±¥°†,•pX —ï§PØÚa¯[8NŸËâ^__¯e²UÏîñÕ'_è÷ïßó72~ðäF¾-̵3)Æ–V@q–ÎßO¡°©#_+ê rvOÊVáM>tÜxÞI1¶´B R ØO¡°ã_+êŠpv_[Çw>ŸïÝÔø)“›úñÁ¡1w&ËØÒ (¢B%Ì  ýžZ!PW„³{òq‡Ãá±­Mþ¦Åù µ3YÆ–VÀr– … Z!P]„³»ìK\[Í—qg²Œ-­€%²TÂaA(T ™{:h…@]›ŸÝ“Ïü\¸ònrmàøB¹v&ÑØÒ x˜å„B!Z!ÌÚg÷äS:ßßß—lsüôËmŽ/”kg-­€XN¨2q^h…@0kŸÝ»ÝîrûçóyÉ6'Ÿü9¾P®I4¶´BîÒC%œ  |?;´B ˜µÏîËÿúõkùfÇ<°Û¡v&ÑØÒ ˜ÏCG…Bn Z!̪g÷ÇÇÇåÆ÷ûýò-¹ÜòørYv&×ØÒ ˜ÃCGUB~>M´B ˜UÏîõ~—ß¿y0ÔÎä[Z!·yè¨PÈÜ“E+‚Yõ잌hEòÜÛÛÛ½[µ3¹Æ–VÀ –ª„Üq¾h…@0õ[áñx\¾åq#EZáV;“kli…L²œP%äî³F+‚YõìÞív5óÜørYv&×ØÒ øfI%¬ =t”XçŽV£n²3¹Æ–VÀW–ª„<~úh…@0õ[áétZ¾åóù\¤nµ3¹Æ–VÀ'•P(déI¤Á¬zv‡ÚxÞ¯tó±¥0ô UBV?´B ­0þWºùØÒ :g9¡JH±³I+‚Ñ ã¥›-­ [*¡PHásJ+‚Ñ ã¥›-­ OB¡JHùÓJ+‚Ñ ã¥›-­ 7*¡PÈZ'—V£ÆÿJ# ‚Ïb@ó*TÂQÉý]³:(Ši…€V¨fû¿¸EZáV;“kli…íIT ‡¡0f%üßU:í=i…)N=­¦~+<Ë·\ªnµ3¹Æ–VÐË 7…ÿûêÊÕ=¡0ÍÙ§Á¬zv¿¼¼ÔÌsãËeÙ™\cK+h†J¤þ÷e–h|Ba¦sP+‚Yõì~}}½Üøûûûò-¿½½]ny|¹,;“kli… èä¡£3Ca¨oÍÂÒ'&;µB ˜UÏî÷÷÷•"Úá/ÔÎä[Z!@v–† …ÿûªí}Ba¾“Q+¢Í 5ÏîËÿþý{ù–÷ûýå–Ǘ˲3¹Æ–VW'Ë 3VÂo;ö@õ»ö)ÁÃh理V³öÙ}¹ñÝn·|³ÏÏÏìv¨I4¶´B€¤,'Œ\Í.wï®\8'j…ÏJ­fí³{·Ûßþù|~¬ú…Ú™DcK+HG%ŒßË&÷sf.œ µÂˆç¦V³öÙýòòRüwùMþæÁñ…ríL¢±¥$ÒÉCGg†ÂÈß©k;üc.œ µÂˆg¨V³öÙ=™Òþ–ÀñÓK~¡v&ÑØÒ ²°œ0W&{  …éOR­¦ÂÙ=ùçóù±­M>ósþ>‡Ú™,cK+ˆ¯“å„ÍTÂ?îÊ…Ba §ªVSáìž\yw8ÛÚëëë’µ¡v&ËØÒ "óÐÑìlI.„Ât'¬VSáì>¥Vó][Ç7¾DÆÉ2¶´B€°sµB ˜:g÷ûûûä Ýõ¸ÎÉÇ‡ŽÆçÝ™cK+eI%´œ0²§{þ…YÏ_­ˆ6}jÝ»ÝnòµÆ¿?N·?wü€Ÿž}gâ-­ ŽN–öV ‡;Cád.tvä8…µB ò| súŸN§Üï÷‡ÃáÛoúÿóíímòQŸü˜öâïLü±¥Dà¡£–ÞÈ…N4'²VTž2‘NÿÃáPv7–<ð3ÔÎ[Z!ÀæÓµB²-­`U*¡Pxo%” ŸïZ!ÙÆ–V°žNB¡J¸F(” SžòZ!ÙÆ–V°Ë › …OuC¡\˜ïÄ× È6¶´B€²TBË …ÕäÂ,§¿V@¶±¥$ª„KBá§ù¹pW­€lcK+(B%ôÐчCáåëÎÌ…„»h…d!ÀB¹*á° zèhñPxãÕçäBÂ] ´B²-­` Ë -'¼÷Y£óß«¶Sl›­€lcK+xŒJ¨> çï\˜ì² mli…÷ZR › …Í|gƒ,'üF.ÌtqÐ È6¶´B€»¨„–>\ ~»äÂ4×­€lcK+˜)W%„B•0T(ü$æ¸Jh…d!À– …K*a‘wL+Lp¡Ð È6¶´B€ÛTB•pa%,ø¦i…Ñ/Z!ÙÆ–VpM? Ûø¶>¥ …Ÿ´ÂÐ ­€lcK+˜d9¡PøX%tît}ÝÐ È6¶´B€oúYN¨_Kèôéýê¡mli…T¨„–Ö÷d9!Õ®!Z!ÙÆ–VðÉrBË UB–^F´B²-­ ŸJ83¶ñmõÐQ6¸˜h…d!й\¡ÐCGg½K–²ÕõD+ ÛØÒ €nyè¨å„*!…¯*Z!ÙÆ–VtÈCG-'ôÐQV¹¶h…d!ÐË UBË Yëò¢mli…@?,'T UBÖ½Èh…d!ЕP(ôÐQj\j´B²-­hžP¨ZNH¥«V@¶±¥ S Ûë_*!¡¯9Z!ÙÆ–V4iI%L =tÔCG‰råÑ È6¶´B =–ª„–²ÍÅG+ ÛØÒ €–¨„:ª²å%H+ ÛØÒ €f…–zè(_…´B²-­h€Jh9¡JHˆk‘V@¶±¥©¥«„ÂP¨zè(ѯHZ!ÙÆ–Väe9¡Ph9!±.JZ!ÙÆ–Vd¤ª„*!/MZ!ÙÆ–VäR¡¦ …Ù¿§:J;(­€lcK+±œP(´œÐ×(­€lcK+RHW ‡¡P%T Éz¥Ò È6¶´B >Ë K`:J³+­€lcK+"S -'T ÉtÉÒ È6¶´B ¦®::3fÿžzè(í_¸´B²-­ÈrBµœ”×.­€lcK+Béj9¡‡Žª„´vÓ È6¶´B µœÐCGIÓ È6¶´B U -'¤…K™V@¶±¥Û²œÐCGUBÚ¹ i…d!°•%•Ðr˜@%ôÐQ•æ^‡µB²-­šºßŸ— çÿk¢J8,…:ê¡£Pæ"¬mli…ÐÚ-ÿŒ\8óŸ,'ì|9¡Jw_µB²-­¼ëÿ)Îù{•ÐCGUB¸ûò«mli…Іoùæv.¼üËÞ::3æ=,'„Mh…¤!´á2âÜÈ…ßþÆrBË UB(B+ ÝØÒ   “5çZ.\øK ó.'T =tV¥nli…ІkY§H|¬ZNX“å„V@º±¥@3êäB•0•âÐ H7¶´Bhɪ¹0ãCGg†Â¤ßn…h´BÒ-­³R.´œ0•Ò H7¶´BhÏeôém9¡Jè¡£° ­€tcK+€&u[ Û…: Ái…¤!4{ïï¡£–ª„Pù«mli…Ðæ¿å„*¡‡ŽBýk¯V@¶±¥@k·ü*¡‡ŽZN[]µB²-­Ú¹Ù_öÐÑ™¹p=W §ß•Ò]‡µB²-­Z¸Í/Q Ì…kì¹å„Óo‹‡ŽBÒ«±V@¶±¥@ú{ü¢¡ðZ.\cÏU‰÷ÄrBH}AÖ È6¶´BH|wÿh¼ñŸ—¹p=_{9aÒo¨Jé/ËZ!ÙÆ–V)ïë—-¼ý7þ¬±ç::ý¶xè(´qqÖ È6¶´BÈwS¿øá¢“ë+äB•pâ=±œZº>k…d!dº/ô[¯ýjÂõr¡å„Óo‹J]¥µB²-­rÜȪ„·[áµZ¸ó*áÄ{â¡£ÐäµZ+ ÛØÒ  ú-ü²JøïTq»ñ¯CÑ\¸örÂŒßP…–¯ØZ!ÙÆ–V¡ïßW…·[a©\è¡£Óo‹Jm_´µB²-­‚Þ¹/®„ÿ^n?~̰,ZN8ñžxè(ôpéÖ È6¶´BwÏ^¢þ{3º}Ëósá;o9áÄ{b9!ôs× È6¶´Bˆuþ~(¼l…Ãì\x{çUÂïoˆJ½]õB²-­¢Üªª„·CáåkýÙ9¹ðÚÎ{èèÄ{â¡£Ðá•\+ ÛØÒ `û›ô5—Nv¥k±éå¡£ï‰å„Ðíõ\+ ÛØÒ `ËÛóê•ðÓÌú1KYNøý Q ¡ó«ºV@¶±¥Àf÷æU*ádZšÿ¯×>ÌrÂK: h…¤!lpW^k9áµ´t×\~¤Jøå„Àÿ.ïZ!ÙÆ–VUïÇ+V™Ï½ö1“´œðû;©_/òZ!ÙÆ–VõnÆ+V»~ÉàµT oóÐQàûu^+ ÛØÒ  ÆmxÝå„w…Â9¹píP˜îªÓW{­€lcK+€uoÀ«WÂBá\h9á÷7ÄCG×|­€lcK+€µn½kU™û3§î=ð):j9!¥.2_ùµB²-­V¹ïŽT ‡{ªß½o9¡JÈ Çi/þZ!ÙÆ–V…︫,'¼k—–„?•ÐCGÙâ(#íÐ È6¶´B(v¯ì¡£ŸB…ÂtßSË Ùî(#ç Ð È6¶´B(s£¯µB¡å„*!+käœZ!ÙÆ–VKo±ã=tô“Jø%ÌáF‰ mli…ðøÍuȇŽ~ £éˆ#á\Ð È6¶´Bxä¶:p%Ö…*¡‡ŽRë #ÛtÐ È6¶´B¸ûž:p%b„Â\ßP%ð¡G¶¡mli…pÇÝtÔ_Mø‡å„w¿c*!Ñ=R ­€lcK+€Y÷ѱ:úió…¹¾§:J’TÃB+ ÛØÒ àç›èð•pX?þ˜ }C=t”lÇ yæ…V@¶±¥À­ÛçðýcÃV˜ë{ª’ð0$ÏÔÐ È6¶´B˜¾qÎðÐѯ6i…¹¾§:Jæ#‘$³C+ ÛØÒ àû-s¶Jø©~+Lô=õÐQò$™ Z!ÙÆ–VÝ/'¬„T …¹¾§*!­d"Z!ÙÆ–Vÿ»SÎó« o ÿz7‡———Ýn÷üüüõŠ7þçø—ã?0~XWcK+ s*a{+ UBš;0µÂ<3E+¸i·Ûmr©9ûý~þpüàñS:[Z!ÝòÐÑ»BaŠFæ¡£4w`Ê…Ù&‹VpÓ¯_¿*_jN§Óe œiüÄñÓ›[Z!òÐÑÇBaäLæ¡£4zlj…Ùæ‹VpÝù|®|©y_~17ÒöØÒ èŠJ¸$†e*!͘raÚ)£\÷úúZóRs8J]ÇM5<¶´BúÑF%¶ …“™‡ŽÒÓÊ>>j^jЬ(l~u¡V@?ÚøÕ„C•J˜%zè(Q=UùCÔq£L¹ WºÔœN§Û¿…ðíííx<~ý”ñ?‡Ã~¿¿ñ‰íýîB­€4óÐÑaA(wO(T ‰}`j…­ ­àoçóyòÑ£«^jv»ÝäkýúõëãããöçŽ0~Øä§ï÷û&Ç–V@ÃT¯í¯Pè¡£„ô´ÅBέèÞétú\£÷úúz­Ù­z©_}ò…~ÿþ=#ãOnäÛjÄ6Æ–V@“tÜx{cK+ 1:útó«È UB¢zÚúñfVô<\j®-*<ŸÏ÷njü”ÉMýøÓtcK+ []•ð§Px-¶ =t”¦VØô$Ò ž§c€KÍä³C‡Ãc[›üM‹w=Ë4ÅØÒ hä>·•J8¬³œðF. ›Ï,'$ª§H6Œ´B çàRSö%®--llli…¤¿ÃmåW+/'¼Ë¶ßS•¨žâý!ØHÒ žÇäÖ—šÉ.\8¹Pq|¡–Æ–V@â{[]¡¦…:JÔcS+ìf0i…w^» nò‘¡ïïïK¶9~úå6Çjili…d½±õÐÑÙ¡pHÑ,'$ª§À6›´B€;¯]·¿Ûí.·>Ÿ—lsò1¤ã µ4¶´BòÝÒzèè=Ë ÿ¼V䎦ÕSø?›PZ!À×®U·ÿëׯå›7ÒêR+ ßÍlC„‡¿ŠöB¡‡ŽóØüg}¾=Aç”Vpçµ«ÔÆ?>>.7¾ßï—oyÜÈå–Ç—kfli…ä¸U ï|èè5¡ºƒJHT¡+¡c;ô´Ò û¼dxcaÁÉRjãëýbÁ5~ b¨±¥à¶¡J8l±œð›ÅÁCG‰*G%t„ÇXZa'W ï3”;wJm|²èi…ooo+m9ÈØÒ }÷ÚЯ&6]NøMÞP(£óØüg#¾gáÆ–VØðÂ{ëœG¥6>Ù Çãò-Ñ `ƒûV-½œ0•öŽÍ6å;nxi…í]6â§ŸªÔÆw»]ÍV8¾\3cK+ âM«‡Žª„:J†cóŸ­ùþ…›_ZaKW‡œSôpZ•Ú¸VøðØÒ ˆu»ê¡£B¡å„d86ÿ‰Á·0ÜÓ Û¸4ãÌ¢í“«ÔÆ'[áétZ¾åóù¬@Õ¶::XNøù&¨„4wlþ‰od¸Y¦f¿4æü¢ÕSÌÆ7[Z!Ûߢª„w†ÂßV¥ÉcÓ‘ÉM+Ì{i¨õ^ù¦Ðõ Ö CŽ-­€ïOUBË UB2›ŽLf 5­0é¥a»7Ç÷…¾Æ°Vrli…lvgÚÖ¯&,'ü|¶´B6¸'õÐÑ—zè(M›ŽLîpZa®«CÈ·Â7ˆÞN:ß|li…Ô¾!m« B¡‡Žª„D>6™<2ã´ÂDW‡ðï€ïœw6a|CX[儵¿¦5CaŽïk•‡Ž:}¨x`>92y|*h…*¡bZa¶ÿ‹Ë“V@…ÿí´ÖCGkYk.'Lð}­²œÐéCÝóɑɢÁ æ…v´B­Jþ¯¦*¡å„*!9L•ãA+LÞ í9h…Z!”üŸLUÂšË ¿6Òÿcñ_[öÐQBòÐQB >­0m+´ÿ½îv»•6~>Ÿ/·<¾\c­Ðñ @á»Î*Ë +QkWÂ¥»÷÷Ÿb_u¹?1¿­´bõPè-fÖøÓ ã_,ò¥Z!NÀI“­ðããcù–Ç£VwÜoÖzèhÍ/ªÂrÂ;¹B.T É@%$ÒÔ ã_2ZùµB8n+´Âãñ¸|ËZ!Üq³Ù\%Â/'üo?Kçš•PŽ¡òAêÈd­9¨Æ¿p4ôj…¤>h‹½///5[áørÍŒ-­€2·™:z(,¿Ãår¡å„„g9!!§¡VÿÚÑÖW§’ôˆ]ãRóúúz¹ñ÷÷÷å[~{{»ÜòørÍŒ-­€¥7˜->ttȳœðûn—È…*!±©„ž‰Zaü+Hs_šVHºÃu¥KÍûûûJEo½ dli…<~k©ÆXNø}ÿ—åB%0%üdÔ ã_GZüº´B«ë]j>>>.7þû÷ïå[Þï÷—[_®™±¥ðà}¥Ji9á÷/äÑ\h9!YNH†á¨ÜyíZuû»ÝnùfŸŸŸ[½Bj…tôo¯õ@.T ‰J%$ÏˆÔ î¼vÜþn·+¾ýóù¼R‚Œ3¶´BôÐÑýtù¢ós¡‡ŽÕ“PH²A©Üyí*¸ý———â¿Xpò× Ž/ÔÒØÒ ˜{#Ùb%ò/'ücò¥Ì…:J`*! g¥Vp絫àö'»ÞÂ_Y8~zñþmli…ü| é¡£—þqmnäB•¨TBÒNL­àÎkW…—8ŸÏmmò¤-]µB~¾yôÐÑðË ¿º+zè(!yè(Éç¦Vp絫ìKL.<míõõµøBÅ€cK+à꣇ŽfXNøÍÚ¹P‹aÕ³S%$ýèÔ î¼v•}‰ãñXjiáµE…ãK46¶´B&îUÂTË ¿Y)j1¬zvª„42@µB€;¯]Å_åùùùòUöûý½Û?år;ãÆÛ!Ý-6ú« ‡¡0c‰¸Ü=%*¥­1ªrÿ7ÎÛ‚S ¬÷÷÷ɺëÙ¡“Ï2ooli…üï>±Ñ_M8´þÐÑkŠTBË YûìT im˜j…<úýò.Ñí¹°Æ ív»É×ÿþt:ÝþÜñn|z“cK+`h÷¡£CgË ¿©³œÐéÃc§æÚ•ÐÁÉ6óT+lõšµàýñ†Óù™²Éµèt:ÝØà~¿?ß~íàøŸooo“ÏýãÇΘtli…óÐÑÆ–þ÷å{è(-žŽL¢OU­°ÉkV•Pè=§Õ“e«kÑáp(»=}ôëØÒ ºå¡£­.'ôÐQš<<™ä˜­Za“W®Gßo;N–m¯Esḩ†Ç–VÐ'm²‹CáœVèô¡ò±i¡+™Æ«VØäõ«Ö¢Bï<í,›_‹ŠäÂ&W~[Z!@oí/›_‹N§Ón·{ì¥ÇOlïw^Ž-­  W¡ïå„ ÎÌ…Î jž*!™G­VØÞUl…V8çüó4s¾¹Çý~?ÿEÇ?¥“±¥ô á_M8¬¿œ0ø7·ÈSFo˜3ˆZǦ‡Ž’àj…í]ËŠ>€´È;ŸÏ‡Ãáååe·Û=??=ãÆÿÿrü§ñÆëjli…íߪ„–Îx¾èµvQñð´œü3W+lïrVtQañX>¶´B€–oý*áà¡£:JC‡§# ü mli…åo¸TÂvC¡‡Ž˜‡ŽB€Ÿ´B²-­ ä­V¿šp°œP%¤¡ÃÓÁ %Ð È6¶´B€27Y:Z(Æüþzè(­^ œpÇÏZ!ÙÆ–VPà«J8t¹œÐCG‰ÍrBö#V@¶±¥,º·òÐQË UB²žNXñ­€lcK+xð®ÊCG-'ôÐQ²žNXýÇ­€lcK+¸û~ª›J8xè¨å„4t„:8¡Æ ZaüËhN( ÎØÒ î»™R =tT%$Ûáéà„z?'h…ñ/¦Z!ÀßcK+˜{Õͯ&,'ôÐQZ9<œPû§­0þ%U+ø{li…?ß@yèhÓË =t”Ø,'„T?3h…ñ¯ªZ!ÀßcK+øáîI%´œP%$Ûáéà„Í~lÐ ã_[µB€¿Ç–Vpõ¾ÉCG-'ôÐQòžB!lúÃVÿ «ü=¶´B€‰;&mz9¡‡Ž›J™„Ð ã_dµB€¿Ç–Vð×½RO•pX =tTˆ!Ôáéà„(?Hh…­^ W{g¼çÀæcK+øïFI%ôÐQ%ßá)B¤Ÿ%´Â&¯Ñk¾3Þv`ó±¥ ýj¡¿å„:Jl*!4ô…VØäezå·Å;l;¶´B ÷›#µœP%$ááéà„ ?Wh…í]©«¼-Þy`ñ¥]ß©„–zè(ùO¡ÿh¡¶w½Þ¢zóšcK+:½'òÐQ•ÐrB¡Nˆþ†VØÞ%»Ö{âͶ[Z!ÐÝÝ‡Ž …*! Ok]!ÇZacWíŠï‰÷Øjli…@G÷AUÂÁrB¥‘ÃÓrBÈóÆVØØµ[+:[Z!ÐËMJX4Fûþzè(±©„ÐÍÏZac—ïºï‰÷Ødli…@û·?~5¡å„*! Ok]!åOZacñM[¡oPgli…@Ë7>:j9¡‡Ž’óupBÖŸ=´ÂÆ®ãZ!ÐÁØÒ €fïzT¦—zè(±©„ÐëZacWs­è`li…@ƒ÷;=ttX =tTˆ!Îái­+´ðCˆVØØ5]+:[Z!ÐÔŽ‡Žzè¨‡Ž’óupB#?Šh…]Öë¾-¾À&cK+¹Çé¯-'ôÐQbS ÿÿD+lì⮌-­háG%´œP%$áái­+4ø3‰VØÞU¾ÖÛâ[l5¶´B ÷­_Mh9¡‡Ž’óupB›?™h…í]èk½3¾ÀVcK+²ÞÔxè¨Jh9!9P'´üó‰VØÞå¾Ê›ãý6[Z!òŽF% UBRžB!´þ#ŠVØäuå÷Çûl;¶´B Ù½L,'ôÐQš8BœÐÅ*Za“—þ5ß"ï?°ùØÒ €4w1:ºB(ŒõÅZNHh*!0ãÇ­°Õ°ÂåÍ‚Œ-­xü¶âK‰[õƒû¬„ƒå„*!-žB!töÓ‘VØð0(ôŽyçhcK+¿­ø;ÉÍÿà»6«ZNè¡£$=BœÐãOGZaÛ#¡o5Psli…Àã·mnæßµM¿š°áå„:Jl*!pÿOGZaó³ÁÛ47¶´BàñÛŠ©B7çƒïÚ å„:ª’êð ¡ïŸŽ´Â†„÷hkli…À¢;‹Ù¹ð®P =tÔCGÉx„:8­°—QáÝ[Z!°ôæb^.œÿ¯½=ttèi9¡‡ŽÛê•Ðñ íÿ\¤v56¼Ï@cK+ Ü_ÌÈ…3ÿÉrBË UBÒ¡ŽO࿟j´Â>GˆwÈ<¶´B Ì-ÆO¹pÎß«„–zè(¹ŽP'ðý'"­°ó‰â]Ž-­xØ·ÿ%üv.üñ/û©„COË =t”Øk´BÒ-­xØäÿ0~-.ÿ¥„=/'T …‚¡Nà6­€tcK+ví!/•UBbˆs„ªØÀZ!鯖V,Q-¦þŸè-'T É~„:>™´BÒ-­Xhí\h9¡J(İáêàî¢nli…Àr“ÿ˹JØÉrB%¶'¡¨I+ ÝØÒ €"T¯U~^Ò 9ÕyÏ:cK+~¸wX3ÞÎ…aß•ÐCGI}„:>pÒ [:[ð¶uÆ–V\½k(Úoüçe. ûžxè¨å„d?BŸÀº?>i…Mžxç:cK+&îVX0xûo¾þSØ·ÅrB•ÔG¨ã¨ñC”VØäìÑ ¦Ç–Vüw›°æ“EüË?¾3–zè(©PÇ'Pï§)­°½ñã]Z[Z!0¬öK ¿þ/ðsþ>`.\»i:Jx–I~¦Ò Û›@Þ õ±¥@×÷ë$ÂË x£FÎ…:ª’úu|üp¥66„¼3@cK+€NïÖ¬„ÿNŲÿzm6|,'ôÐQR¡ŽO`³±´ÂÆF‘·è`li…ÐݽÀÊ•ðß+±ìö ‘r¡Jh9!©RÇ'°åZZacÓÈÛt0¶´Bèå`…8?þØ ƒäµ—9TBbS Ì?qi…Í$ï ÐÁØÒ  ýþW[38?~ûàáJršÜÛjo”å„:JÞ#Ôñ Dù¹K+ll2yOø?öîîºmdKÀ¨Bp N1(…NÁ)ø±ý攂SP NA)( ×xÆmKõsΩ½_n·,·À« ß L[Z!T^öü„WBáÛÿ˜_ÿdc.p V¸ÐCG Ïí„@‰Õ—VXlrrL€¦-­ ®öûÿòÁýóâüÅü·%v=V½+á:·:ï˜2BO Ü2L+,6E9&ÀÓ–V¥Öù£*á ×ç·üóÁöUB%ó ‹1­°ØDå˜ L[Z!TXÞK„¯ÏoüWc.ï{è¨Û I=HO îªL+¬7]9&@õiK+€Ü û•ðâ%úíÿ¶÷u~·ª„䡯'}m¦Ö›´ ú´¥@Ö%}ÔJøÞUú]_Ðé‚¿Û =t”Ì#T(2¬Ð´ÂzS—ÃTŸ¶´BH¶’‘ýÇìºJ¿÷kÚ^öï] #ä %<•X`©¦–œÀ ô´¥@š5|¤JøË® õ7|Y«‹ÿn'T É;BO Ù‚M+,992@éiK+€èK÷¦‰°U%üe×µúÛ¾ò`p;¡‡Ž’y„ …@•›VXu>sd€ºÓ–VqíQáo»®Õ÷ûâ÷¨„n'$ï 5>¬ë7­°ê”æàu§-­".×ÃWÂàW [(;¡‡Žÿ,T ÕuZaíyÎ!*N[Z!L^™«„-ô®„‹ÜNè|dÊu»+Pji§Ö›áæqð1Ó–Vsäa;*¡‡Ž’wŸ@µ5žVXo’Ó êÓ–V£—â*a;åo'ôÐQ⟅*!À_+=­°ÞT§Õ§-­­Àc$ÂJÞÝN¨’t„ºÝ¨¼äÓ ëMxZ!P}ÚÒ  ûÚÛ„­õ¾pþè¡£Œhã©ñ _øi…õæK­¨>mi…ÐqÕ­¶æ¡£n'¤ÛXë>HO`‰åŸVXoÊÔ êÓ–VíÛan'T é9ܺŽP¡Xf¨\‚j…@ñiK+€–Ël•°·zè(CF\—Aj|k­µB²M[Z!4X]7M„U+áŸýîæ?¨ºžã®ñ 5>—…Z!Ù¦-­­«%ÂmÞ†¼Ûþ”‡Žª„tw­F¨P¬º8Ô È6mi…pãŠZ%Ü콜wÛŸr;¡‡ŽÒsô5¤Æ'°ôQ+ Û´¥À¾…´D¸Óõ®wÃÉ~;¡‡Ž’yª„­µB²M[Z!l]B«„ûm©{{¿Þí„* “Æ P°aŨmÚÒ àãųJx“í™oï׫„* Ç¡J°mݨmÚÒ àÝ5³DxÀ‘;‹…B%Îy9«¢ÀB H­€lÓ–VVË*á1*á‡B%$ÌyévB€ËH­€lÓ–Vÿ-’c$Âì—Öã„ÂÉÇÁCG vjª„#Ö“Z!Ù¦-­^ÜHØŽÛ _þ¬j…,´V ûðÐQ•È'è€Vè(ü·àÔ ™_j(áùùyñóQ+`•¥¯DØÓôGÎüÙ=t”ð'hïVè¼^yj…+̯Ž6TñõëW­P+ ø¢W%ì¬w(¼ž gþàn'$üÙÙðeˆìXj…µçWÇ ùùó§ÓP+ ìZW"eb+œùS«„Ä>/›¿ Q€ Q­°òÒ÷Îa‡2Þ …Z!¤_媄cMi…3^%öÙïe|l]Žj…•—¾ Ž˜ÃÓ=??_|ô¨Vhl~}«Î0¾NûIÝNHøÓq@+t”>^”j…e×½”#Ã<===>>><<|ýúõt:ùè{;mi…ä^ÖJ„³ …3F•Øgá¨V§ZaÍo¯CäàCÀʧ-­€¬ Z•0Ðþ«o(œù£yè(±O¾±/6¬QµÂšËÝŽÇÁ‡h• N[Z!ÉÖ±M¡JØbóU3zè(ÁϼI/>Z¬j…—»ÝŽã¡>*œ¶´BÒ¬`%ÂpÛ®²U ~òi…q—¬ZaÁEïˆ#ãøCœÊ§-­€kW•0âžkD(ä=t”àg^€W®ZaÁuï ÃâøCʧ-­€¸KV‰0ènëÛÈP8ì-ðÐQ‚Ÿya^\]Áj…Õ–¾w[¡·*ã‘§-­€ˆ‹U•0îNjt(óF¨„?ó‚½x«V[ý=&Þ˜~Žk…0™ªÆÝC}› {¿:Jð3/ä €÷W³Zaµ5°VkãZ!L[J„¡wOßJ†B%ø™øÀûËZ­°ÚJX+„µÎq­&¬KUÂè[§ÛÛ_ÕPhPÑû´ ÿàýÅ­VXm1¬ÂZç¸Vã–£1¡ suÓÔà&Á€¡ÐCG ~æÝüúß§Ì^åj…Õ–ÄZ!¬uŽk…0b!êFÂ;¦f =t”à§ÝÁP8€7 àãµ®VXmU¬ÂZç¸V}— *a޽Rûß9˜=Zô>íâWBã`ëŠW+¬¶6Ö a­s\+€.+O‰0ÓF©q%¼’ ‡½S:JðÓ.K%4Ô6-}µÂjËc­Ö:ǵBh¼æT 3m‘¾õ …osá˜7ËCG ~Ú¥ …Æ<ÀÇ `­°Ú"yÜañ@ís<ò´¥Ð~©Ù4ª„CöG}+á”÷K%$ò9—´ù¯„µÂ‚Kåi­ÐÁ‡ñ'¸VG™a¾QˆJØö]óÐQ"Ÿs©+¡ÁðñzX+,¸`qd(|‚ÇŸ¶´BÚ,/UÂ|{¢¾}™ÑÝÁSL[Z!‡V•aÖ QßÛ ÿEYB¡‘Fïsn@(|ÙèµB€^Ëc­°æâ¹ãÁqð¡êÙhÚÒ ¸q=©fÝ u¿ð•ï ‡Žùœs;¡ e‘¬Ö\B÷:D><»œ¶´Bv¯$UÂÄû A·¾ÒïMôÐQ"Ÿp*!ÀŠKe­°ìBºñrä!ì©]ãs z’½·¿=Cá‡}—·²%4n>6w„BÇ ôÚK+\,nÛ„BG Ô \+È´¹Ö µB€º‹=•°ò^&P%|õgoÈ…*!aO5·pË:\+È´¿Ö µB€rk<‰°ø.&ÐCGyû¶çB%쩦pû‚\+È´ËÖ µB€B«;•°øþ%âCG_.5»— ¹ÐCG‰|¶©„Z–k…eä]‘#Oí§-­ ÷¢®i"T £n^ÂÝNøÛ{ßðJ.T {ªùÕ„4XŸk…e×低#%Oí\Ó–Vu9'.±m z;áŸvåB%æ©ævBš­ÒµÂšËò»Á­ÐÁ‡‰g÷‚Ó–Vo!§®²g‰{;á+½s¡1I¿óL% ñZ]+¬¹2ï{p|¨zvg™¶´B€4ë7‰p¡ÝJ‚Û _é” Kºžj*!ííZaÍõy÷#ãàC³[+ èÊM%\k«’ævÂWÞþ]:JØóL( ×Ò]+,¸>¿›Ò (s‚ÇŸ¶´B€Ðk6•p­MJ¾Û _iR ÝNH×óL% ï^+,¸JtXÈòQYoÚÒ ".Õ$·0¹+á?ˆJHàóL% ûJ^+,¸P× ¡Îùë³ñâ´¥ÄZ¤©„+n^²>tôÂÏâ¡£D=Ï„BG€­°àZýnb+ô@ÌÊzÓ–Vbm#ºÖ=|ÛòÍí„n'$òØôá Àî…½VXmÅ>ô˜x ÅGe½iK+˜¼*s#áº{–"·Þµx¢D›>?¸qy¯V[´k…PêöÙxqÚÒ ¦­ÇTÂuw+ëÞNøë¿po.4fè?6UB-òµÂjKw­JÂ>/N[Z!Àèe˜D¸úVeÝÛ ÿüïܘ †ŒM¿š€v«}­°Úê]+êO[Z!À¸˜J¸úu†"·¶zÊèõ/3`56ÝN@Ó5¿VXm ¯õ§-­ ûº«i"T Ó^dXú·¾÷ŸýÞ0 ˜*!ÿZaµe¼VÔŸ¶´B€Ž+.‰åo'üðw^üz56=t€Ö[­°Úb^+êO[Z!@—µ–JÈÿmöW¯„×C¡\Èıé³€.­°Úz^+êO[Z!@Ë%–DÈÛü í] åB¦ŒM§tÜh…ÕVõã‹·˜5mi…mW*!ÿíñ=tt_(üõCÉ… ›>Nè¾/Ð .ï§µB3mi…G—U*!mðÝN¸;Ê…Œ›>N±;Ð ®ðµB ø´¥ܸš’y½µ¯p;áÝPøòÎ׼ȅô›>T·MÐ ®óGǘ8mi…@²5Ì}mÖ«„\ÚÚ»pG(üe{.dñÓK% Ó†E+¬¹Úï{p`î´¥ÉÖ0‡¶í_|üÛI„.hÇ»nàv­¡ðíß»1²ò¦lâÖ\ów<>>0}ÚÒ €dk˜7Ñmãùžn$äý}}úÛ ï†„Â+ÿ[r!kž^B!)7,ZaÙ•—Cäà¦-­H¶†¹Tß¶|ñmßP%äýMýê·6À†=í¦JÀì ‹VXvýßø@9ò@œiK+ò-c6çÂÛ+ú"òñ¾^%l<†›>]±[Ñ +ï1‡8mi…@ʕ̶\¸ýßvz©„¯¬þÐÑ~ÃØ¹àô e ÏÛ¶·*Zañí€Ãœ¶´B ëbfC.ö+UÂ5. ¤èè°Jxóã¤Xöôr;!àãM+¬³OÑ ëï s Ú´¥‰×3åÂ-ÿ\"dÛ¥o'9ž «[*!àCN+¬¶IÑ —Ø8à@©iK+ryuí÷z.üðª„l¸àvÂÑCÚi²Îé¥>ç´Âz´Âe¶ 5PgÚÒ €\Þ^¾’ ¯ÿ‰ WrßN8¸6ü‰œ/åÏ-¿šði§V¥.¶_p Ó–Värñjð{¹ðÊÿT ùh׿ôí„ƶ³¦ê¹åvBÀgžVX›V¸äÞÁárO[Z!Ë{ׄûý B•pÉÍ¾Û gb LŸ´ƒÎaœ’¦.¿•pH|Ó–V¤3+J„klísßN¨öÜòÐÑ GZ˜§$ h…¤›¶´B £Á¹P%\„‡ŽÕ˜n'|¼… pJÒ€V@ºiK+’úuq8æ„®Z§³òCGjBL•pÊQ&À)IZ!é¦-­H³zñ éÀCG l¢M¹3?„ pJr|Û¢mÚÒ € k•eP%\–Û m‚ L¿špîá&À)I‹ŒV@¶iK+B¬L&•A‰pY+ßNhxr`º0› L€S’[­€lÓ–VLX‡Ì.ƒ*áâRßNè¡£Ä<«TÂo…0ñÎ8§dÆÍŽV@¶iK+º¯:"•A•pq©o'¼s;!QO,•°Ä»!L°öÀÎû"àöG+ Û´¥-Q³`“PèýÍnÙÛ rBL¡0à{"L?^Z!-öDZá¢{ ÇHã×*ƒ»Bá{­P.,½y_ôvB9†€Ó°ly’{yyåyýõù¦.|ÑU+¬¸×¸Ü `Ø´¥@©ù½tÜ ¯´B¹°èæ}ÅÛ ççWAëžR~5á´1ïååµ3É…e–\ù¢«VXn¯±ã@i…@ºiK+€Ä³ù2Yð¶Px½¾w«œ;÷¬·Vx訫 O©%n'”Z¼¼ %9­€]µÂr;އèæcèø³¦-­rLÜ+Ý0Ø*^o…ra¡m»Û §æWAkOù*¡”ã%É¡ò¢«VXkDZïàh…@ºiK+€ˆÓ´2Ø"~Ø åÂü{öo' wÓ–« …N©™•м$9´B³d­‹®Za­}Ǿ#Ó°z €1Ó–Vó'eeðX+|y§ž¼:¶¿L+̳a÷ÐÑvt´Äù4èvB¡J’­Ð,¹ÌEW­°ÖÖcßa9r ½À”iK+€ÑS°,xäzË›øò~|yÓ _6çB5öV=åCGïŠÝNØêstöù¤z‰€ Ò㢫VXh÷q§å§-­:ζn<\¯Õ—«¡ðåR+|Ù|"!÷é:©ÿÙ\ûƒVZ!A.ºj……ö Z!PÚÒ  Ùܪ v(ƒ×}ø}¶üóÉ…»f·«„M~BWA§ä1¿šPÑ›ÑÝ´Bˆµ¦5K.|ÑU+,´ Ù}L´B Ý´¥À3©28¼ ¾²åÛnüWraøíyÊÛ ëWÂ&?çRWA#Ä)­žÏ´BMr+òõ¢«VXh3¢õ§-­6Í›Ê`°+9ÿ¢íÿV. ¼7_îvÂd—I¼ š´g©„kT0G‰E“\ÜuŒV¸îEW­°Ð~D+êO[Z!\˜%eÁØ”¶ÿ½»¾À5±x»r·f¸r›÷*èR7¾-V ýr4‡W’Ã,Éô‹®Za¡]‰VÔŸ¶´BVŸÝ0˜íºÖ®ÿŒ½_ãz]¤-y¾Û —«„M~ìãWAýλº·ŠGZ¡$Gv†ôÊ]µÂB­¨?mi…¬5ý)ƒù¯Eìú¯ºáË\u °_îvÂÜ×Å8•PòÓ õ ؼÈÔ ×¹èªÚžh…@ýiK+ òd§ –¼ì0ä*¨ÓgÒ6\%tJzeª„¬Ù í´Â•/ºj……6)ãZ¡·˜5mi…Ô™ÚdÁE.2h…e÷à::éôTÖ„B¡°Òƒ4µBˆÄ2~å‹®Za¡}ÊÌVèøc¦-­€”³˜W¾¤à*hÁÝw¾Û ï"ÜN(Š­ýº4ZTB¿;Ï, ±Xد|ÑU+¬µaÙwX´B Ý´¥`ÎR]@økûä*h±}÷Z·Æz WÈäwÓç¼Jhª5KBD–ú+_tÕ kíYF´Bǘ8mi…„›¡”A— >ØA¹ ZfÇívB•Pò› 5¾zs¨£NI‚\tÕ kí\ö™Ž¡ã̶´B&ÏGÊ +»÷iZaíöðÛ Š;·J~g·š_sxÁ)I‹®Za¹ýË„Vè°æ-­€qS,è:@›MšV˜}—½»NïG*¡ä§šø²ð¾¹èª–ÛÅì88{¡ƒLŸ¶´BzM4Ê  q½6iZ¡7+G%ôÐQáO%4£ç rÑU+,¸¹Ù|ˆn¾Ñ‘fM[ZaÐwç+Ø¿à¶.eÐe·Î;4­°ÊqV UB­0Õ¯&ô¹˜‚E @‹®ZaÁÒæ£tÛ퇎<0qÚÒ ƒ¾;_ÖÞþÅ ¿-pÃYö²L¾W]a›ºCÓ g¡p‡Ž†ú)$‘·šd²³’rÑU+¬¹Ýv¬v•D‡ˆ0mi…Aß7׺7~q«ï \9}¶qÃoíCIt=mêö¬n+éT„YM+T ÅÚ ÈEW­°ì~Ô1*N[ZaÐwçÒ5ð-_ÜäžA¿¸Õ÷œþù£ ºzlo¯Jl:Úâ¡£YK–VØwâP ÙÇj ÈEW­°ò–ÔÊM[ZaÜ7hsÝÛõB!t=_²å{eе²œ3/¯¬·ÖL]Za¯aÄ턦ètÑU+,¾+u´ZÓ–Vú=ÚV¶ÿ[¡zŸ/±ó½2¨ VÙ•yyµŒP*¡Vï£ÞCG ýEW­°þÆÔ¡ M[Zaô·iCSØø¯„Bp>¾DÊ÷² ,˜g‹åå5§yè¨V8å½^óÑ4q.ºj…«lgd Ä´¥&x§>* [þ¹PÇV~ÿ÷º|Zý}¡nã¿zý5>(”A—LÇŸ#^^Yž'©æj…>LUBHwÑU+\kOìðɧ-­0¦WÛùëÕïÃøç¿u¡nZóíÈ…[þù…/Øõ® *ƒÇ¹—Wê_×5*ƒZaÀ`Ӽ誮»·vH„Ó–VÓÛ}ý•\xýŸ¼ ].€=ë¼¹ðÃx±$^9%•Aeðæ±êåU¬ñ¹0臧V8ïƒÛla/ºj…$š¶´Â˜.îîß+€Wþç•PèŠìYïÈ…×ÿÉ{'ý}J*ƒ«gAUËKæS Ó}j…3œJÁ/ºj…$š¶´Â˜ÞÛæßv=ÿÊ7¶­¿wäÂ+ÿó½PøÞ—É‚Y/~Ê[^Ÿ‡Ž.õxÐ ‡5•â_tÕ H4mi…aµÊ…/B!4XïÈ…Ÿ•í¾•28þM÷’ùp;¡eÕ–U«ŠÚi‚q;!d¹èªhÚÒ #k{w¡Wº—S »f¹°mvTo{×¼¼´B•P%ì½dÕ ›ÏL*!äºèªhÚÒ ƒ“ ½¼BtÛ»6¹°as\å— ŠVjÝÅ1£òѧǘ‡ŽZ©^Y¯j… G¥1 /ºj…$š¶´ÂøäB/¯à­pcékÓß0¨‚‰}F‘ —÷!Ã*aŪVØhTúÕ„õ¢«V@¢iK+LA.ôò Þ ?Œ}Mj£2èåQœZáü{Þ}ÛþJT =t´ùJU+<>*ÅkH}ÑU+d×ç°s§-­0‹‹—/¯8­ðJï;ž•A/ÙK+œ’Zv•Á‰•ðÅí„ñ–©Ë·B•V¿èªrÃûå(³¦-­0¡ÐËkÙV¨ Š}¬Ü 'mc;VB­½FÕ UBpÑU+,éÈñqÀàÓ–V˜ôRŒvãåUø¤² ÞGV˜vÿû-Ëí„:mªºxÑ ‹ s`Ö´¥f¼#ÜxyEk…Ç¿‰2¨÷kˆ®»ÿÍq;áÛ ‰wö¨„ÀŸ]µÂr{¥ŽÃ¤˜¶´Â|o\‰_j†'ÊVj…M¾2(ö¡ÎÞù.q;¡"C§H%^]tÕ Ëí˜ÝTèÈS¦-­0Ù»vSæpUú…‰VÙQÔûØqU\+l¿íu;¡"ÄQiL@á‹®Za­ÓЛ |`ü´¥fzËÞé€Wþ§\]ûZûWË‚Õê±oæµy­ðÕÖ5Çí„*!Ï·W.ºj…µöMí[á–/säaÓ–V˜æýz¿^ÿ'r!tŠnïuÀ+ÿóJ.LW ½/íz­ðÏ iýÛ ¢}“°ÈEW­°ÖÖ©åH›|1@ÛiK+Ìñf]l ÿùëb” ¡k+¼R¯ÿ“÷rá”2èÕû’rÐZluÝN¨È0zTú€¥.ºj…µ6P-o*lþŸ¶´Âø!>¬ [þùÛ/pñns×O®ÿáÅöæ2èÝûä˜Þ窄N.FJcV»èªÚ@µ|éÞ?âøc¦-­0ø¥ˆ-·#müW¯¾Æµw¸­Ê÷hOÓy¿lr=tÔYÉà!évBXô¢«VXh5â¤ÿ.€ƒÓ–Vù‚ÄÆ_s¶ýßþùe®Ûà ­P¾×ûˆÀ{}Óö¶þCGËôZ”«„Àþ‹®Za¡ÍÔ¸Vø"“¦-­0ìe‰¡ðÃVøî·" &E+”ïÅ>ÖùÌ,··Mp;¡‡ŽqEî¡£À~Za¹ýT³VØõ¯82mi…AßÍ¡pK+” g,#¼"¾Ž´ÂÞù^ÅÓûØÎ0Û³«Mp;áÛ ©µ’3&`õËzZa±¥á¨_Vxä¯88mi…AßÍ¡pc+¼˜'ç=˯Río¯1ù^ïƒ ÑÍ[Úâ·úÌ!ښϘ^´Âz D­X`ÚÒ ƒ¾;›CáöVø6O¬=Í{-ÝþŽ·Âù^샌ð »K·ú°bè¨4ÿ]ÖÓ ‹­µB`iK+ úîlλZáë/.5m{iãZa§|¯÷v²Ño'ôÐQŠ-% Hà¯ËzZaµ–VÔŸ¶´ÂŠ—+¶½¯…Ûß‘VØ/ßë}Àá=lñÛ }6mÑiLoi…åöY»É‘#é-¦L[ZaÅ‹![¡Ð¦ý¥þåzò=`ë¡£Š ã†¤J¼G+,·ÕÒ úÓ–VXñÒÅ0!´iZ¡VDÙºzè¨"øQ©×i…å6\»‹V¤›¶´ÂrW/n B›öÇ•K|Z!tÓê¡£¢ C×ÙÆ$ð!­°âÎkP+ô³¦-­°â5 /áÖWí´B âvµò턊 ÑVØÆ$°‘VXqóµã°´½©Ð[Œ™¶´ÂŠW2¼´?í/ß)éÒ"°g£ývB¥ÒÚZ%vÑ +nÁ¶™ƒ‡ÑñfM[ZaÅë^ÚŸöרâ?>l—êvBŸ™Œ[XÀ^ZaÑØÇÇçøatüYÓ–VXñ’†—ö§ý5æp16§•o'ôiI´%µ1 ÜF+,ºë~ `â´¥V¼°¡ý!f5æðv¦¡o'ôÐQ*-¦ÍÝÀZaÝM™V”¶´ÂŠ—7´?\àjÌ{L݆¾ðÎí„ZIÀqZaéÝ™PÔœ¶´ÂrW8´?íö¼wÀ¼­¨‡Žú„dÐÚ€šÐ KoдB æ´¥V¼Î¡ýi4æ½flB=tÔg#ƒ†¤ùhH+¬¾S |`ä´¥V¼Ú¡ 4fxc÷ž:êS‘ACÒL 4§®±kkyÜy`ú´¥V¼æ¡j4f8·œeo'ô‘H¨E³ t¢®´}kpÄy ´¥–»ì¡Š5$ÝfºÐ\Ì !i}ô£nÚÒ ƒÒ µ?–šùßN¨Ri¡l% ô¦nÚÒ ƒÒ ]±`‘9¿î턦x>SC‡¤ Œ¡nÚÒ ƒZ£zŸX}Â/z;¡Å›‡Õ õ± £nÚÒ ƒÊÙ ½o°y_÷vBeà·û´”Ó H7mi…AÅh…Þ賩÷ÐQ,p·äB•ÈG+ Ý´¥Õ¿fxÅwëªT%RÒ H7mi…Ai…Pln÷ÐQ¬n·.LýjB 1­€tÓ–V”V•&ö¨·Þ¹«Û6CÒ€"Ð H7mi…Ai…PcJ/z;¡(C¨¥­ Ä¡fئÝù¡þœ¶´Â¨îZ!ج-};¡À˜¥­ „¢fØ©UûÑÜ œ¶´Â˜þíÏA€ž;µ ·ª„Œ?ÜN,E+̰_+õÓy`,p|ÚÒ cÒ  óN­àí„ÖÌw*!V˜aËVçôË%&Ó–V“V9·in'„†CO%òÑ 3lÜŠüŒ+¼YÀ˜iK+ŒI+€„Û4•Ú@¿šÈG+̰wKÿ“®ófc¦-­0&­RmÓCÆãÖVè@ai…Ùvy…7úÞN[Z!•ë¡£*!7ž=_ÿ;Æ´B€ÿh…@d@zð6À‹7*žÿ¢—Œ|ômš¶´B µo'ôÐQšœ:Ç+áÿ3­à?Z!ÙÅG†þøñãÈ÷<ÿñ·ßóüåØk…Z!œÛ UBn8oZUÂ1¼a@.Z!Ùétz{¦???ùžCzþ‹rìµB­È,Ýí„*!ÓOš\•Ш2Ò Ø×R^ûüùóño{þ&I?@|ôi…@æÅíB·J*?c’VBHG+ÂúùóçÛÓüþþþøw>“·ßùü×%Ø-k…Z!ÓôGîû¯U ™zºd…F8‹V„Õï öø5ˆƒöÌZ¡V$Ô;^Ï…ûþS=t”©çJJhœ¹h…@X‹^“Vøýû÷Nß¹ûÎY+Ô €„fµÂÝÿn'dÞYR©ê@.Z!ÖÅVøøøxü;Ÿ¿‰V˜zÚÒ €\¦´Â}ÿ…*!SO‘‘•ðeÏ Õ €h…@X§Óid+<ÿu ¶ÐZ¡V$4¸îûoóÐQ¦žƒ+á/Z!ÀŸ´B ,­pãgé‚Ó–V¤0Þ¹©çÄ”JÀ[Z!ÖÅVøôôtü;???k…©§-­Ȩw(Ü÷£2ïTP BÑ À×R:žæI?C|ôi…@ÚÅm”Pè¡£L<TB€€´B ðå­°ÈvóiK+R-k£ú´B ìöúÚëÏò¦ý …ç»1õfófK¢dÞÌh…€V˜ç3ÄGŸVÜXüzÕEÞ´¿¡pH%”ix³Q òoi´B ªÓéÔé4~~~ûÏV˜«:G€Ë³o·´ÂKmnV(Üži¼Ý¼þ#+¡Ã ÐV„u±þüùóøw~||Ô SO[Za ^)€„3Ù®ß3¸-Ò… …ÞnÞ_È©„éi…@X[áãããñï¬fŸ¶´ÂüÞ^2€T3Ù®Px,v …J Vq*!@zZ!Ö—/_F¶Âó_—`g®j…Eh…¤Ãn¨„raP¨ÔÐhýÖ±~#i…@X_¿~}{šÿøñãøwþþýûÛï|þëìϵB­°‚÷® @üÅØ•ðzéÛ^O›µB¯µÆU¹P¬¡ÛòL%¨L+‚»xàÃÃÃmßíëׯÍoTº™× µÂ¬ö^z€Á«¬î¿ð—N¹P¯¡óÚL%¨L+‚{||luká{7žÿІŸiã?Kœ¶´Â„´BÏRC*áoo¿F%$ðªìP%4ðRÐ WÝ ;†dòéÓ§·cõþþ~ï÷9ÿ‘·ßçüÍDN[­0é´sÓ ìŒú>tô½¿·I%ôÐQ†,ÉTB€ú´ÂÅ6ÂG¡#Ï?~ü¸8ðv=;ôâ³LÏÎß¼íÙ4þ,^pÚÒ ÓÍ?Z!ñ6GßßNøÊ˜Û ½ÑXŒ©„«Ð ר·<†ŽúáÉð£²Þ´¥&šˆ¿ áþ(ñí„[¡w•€½´ÂÒáŽÇÐÁgІ¹ðü­:_?*ëM[Za¢¹H+ Ææ(Ùí„¿¾íÞ\èF%àZaݽðÝàVèø3F“\xäŽÂO±€•õ¦-­0Ë\ÔèWn3o' yÀŸ“™·Þ …¿þKÌ‹ÖW*!mi…åöÈZ!PÚÒ ƒÏEr!c7AYo'Ü åB++¿š€´ÂrÛäÝÇD+ÒM[Zaä‰hì €Å·?‰o'¼-Ê…–U*!Íi…å6ËZ!PÚÒ #ODr!£ö>Ón'¼ ÿuÛs!ÖT*!7Ð Ëí—µB þ´¥†…æ½XjדûvÂCáÛ¿tc.Ä‚J%àZa¹]³VÔŸ¶´Â°³\@ÿ-OîJxC(üeK.dñ¥”JÀm´Ârg­¨?mi…ag!¹€ž›¬mÕn‹(•€´ÂrÛg­¨?mi…ag!¹€>Ûœ¬mÞn¤Ë'•€æ´Âr›èq­Ð[Ìš¶´Â°³P˜•ö8)o'ì—o4 '•€¶´Ârûè™­ÐñÆL[Zadÿv ¬º»™v;á]ÔPxe¶5` í‘•ÐØàCZaÅ õ¾Ã¢é¦-­0¬»‘ ÖÛ׿ð`Á‘„’Žk•€€´ÂŠ{ê­Ðñ&N[ZaXÿö$,³£Iy;áøˆ# å×: @XZaÅõ¾#sÃ1tü¹Ó–VÓ¿C(†Õ·3sn'¼Ë /N¾ÆOÌA­œVXt=¡:ìÀ°iK+ŒéßQäB€¢»˜%n'lžrD¢ÈƒZ% ­°è.{ÇÁÙ{ |`ú´¥Æôï@r!@¹-LåJèý]sP«„d¡ÖÝko=D7ßèȳ¦-­0¦ǺõÊÑv.•:êý]sP«„ä¢ÖÝqo=J·Ý~èȧ-­0¦g 2ïY*?tÔû»æ V ÈH+,½õÞt¬v•D‡ˆ0mi…«‘ *îVòÝN(âpeD«„ä¥V߀;æ@ÁiK+\\Ph“2çvB¥ÓˆV ÈN+\`'î€Õ¦-­pM~}!@‰íIÙÛ ½¹ 瑕Р­pý¸£ ”š¶´Â•¹Á í®$ßí„"†•J@8Zá2s‡¨3mi…‹“ îG&ÜNè¡£ôË*!õh…‹íÐd Â´¥"äÙƒ”½Ð›»ÚXö« ¨J+\r·îð¹§-­— rC€[¥Â@V ¨M+\~óîù¦-­ßäB€¨ ¥Â@V XV@ºiK+äOr!@(:J™±¬°­€tÓ–VÈ+r!@én'”rh=¦TBòÑ H7mi…¼å×Ì5åvB¥ù@V XV@ºiK+ä=r!ÀUo'ôÎ.5ŠUB–¥nÚÒ ¹B.)Ýí„jM”J@Z!é¦-­ëäB€1ÆßNè¡£´Â#+¡¡@XZ!é¦-­-C€žû»š·zg×Â*!ü¦nÚÒ ÙH.è³¹óÐQ_•^Ñ H7mi…l'4ÝÖyè(‰Ç¯_Mi…¤›¶´Bv‘ Zlè­°î¥(;mi…„¢×ú:J‚qª@ZaékŽPsÚÒ ‰F..ò=t”èƒT%€\´ÂêW" à´¥\ô_Û{è(Ñ©Ji… \RÇÑÆL[Z!1uÈ…æVà÷ª>Áí„*ÏÊ#td%4„ -­p« Z!PmÚÒ ‰Ì †@ëõü Û =t”ÛF¨JÙi…Õ/,Üi…@½iK+$8¹h·ž¯s;¡w³ÞðT  ­°ô……;­(9mi…Ä'‡ó n'Ty–ž~5!T¢Ö½¶p§U§-­üúBàÀb~Äí„:Ê cS%€z´Â¢×î´B ð´¥’ˆ +y%îðT  $­°âå…;­¨=mi…ä"Û–ñ:JÜá©@aZaÅ‹ P|ÚÒ IG.>ZÃ{è(AǦJåi…¯388@ñiK+$£ HCXsõ^çvBïf±±©À"´Âr— þ´¥’—\ü½z~;¡Ö³æÀT `)Za¹«  PÚÒ IM.^FÝNè¡£ì˜*!,H+,wÍÁ1êO[Z!ÙÉ…°ü¢½Èí„ÞÊJ£rd%4x ­°ÜeǨ?mi…àתËõè·j= ŽJ•§–»øà˜õ§-­2äBXl­î¡£Ä’: ¼h…¯?8&@ýiK+¤¹ÖX¥{è(±†¤Jü¦–» á˜õ§-­bäB(½>÷ÐQb I•xE+,w-Â1êO[Z!%)†PqqÞývBe×T €·´ÂŠW$ ø´¥R•\…ÖäEn'ôV–’*!ð­°âu ‡(>mi…&B‰yèÛ ŸÕÆ£J\§V¼4á°Å§-­ÚäBȼï~;¡‡Ž.2”Z|†«„À&ZaÑk PyÚÒ Yb án'ô>FJ‡?ÀUB`­°èe G¨éÔOИ€´Âº×+ ì´¥²¹2,¼=t”–êÖOo•¸‘VXúª…ƒÔœ¶´B–"Bàõ¶‡ŽÒ~XíÿèV €C´ÂÒ×. æ´¥² Å‚­´=t”.#kççö_M§V¿ˆá§-­5É…fÝ÷vB]ypmþÐV €f´ÂŠ×.¦qð1Ó–VȲäB˜½Ò®p;¡÷1ìøÚö¡­i…¯`h…@ñiK+der!Ì[fǽP÷©1ļTBà-­°âE ­(>mi… ÂØvßÛ =t•˜H+¬x)C+ŠO[Z!¼È…0nuþvBobЦ³h…¯fh…@ñiK+„_äBè¼®Ž{;¡ôSl¬©„ÀDZaÅkZ!P|ÚÒ á7¹º-ª=t”qÃM%&Ò +^ÖÐ âÓ–V¯(†Ðt9í¡£ q+¡Ñ¼h…$œ¶´BxK.„{e¸S €¹´BÒM[Z!\$± ‹‡Ž2aÜ©„ÀtZ!é¦-­Þ#ÂM[•ô·z“=¿šˆ@+ Ý´¥ÂuŠ!ìÙ§xè(ÆJÄ¡nÚÒ áCr!lØ¡t¼ÐCGyoÜÿ…:pV@ºiK+„-äB¸º=ñÐQFº/ã8N+ Ý´¥ÂFr!\Ú˜½ÐÃ$«Ž¸®/ƒ8N+ Ý´¥Â.Š!ü±+ñÐQÆ ·1/ƒ8H+ Ý´¥Â^r!xè(#‡ÛÈ—A¤nÚÒ ár!ËòÐQF·I/€Ûi…¤›¶´B¸\È‚}úó<ÿÏó?<ÿ«óœ¿ÌH¿iK+„‰äBzn>þùGÎÿóáááþþþÊŒó» Óý€ï…B­B1dó²ÖCG¹e਄h…y.ªh… u:.žeŸ?þùóçõ?{þ‚ó—]üã÷÷÷~À½žŸŸ/>zT+tžB4r!-h=t”[ÎÄJèè+Ð ó\ZÑ çñññâ)öÏ?ÿlÿ&ç/¾øM^ݬç|ëééé×=Œ_¿~}¯ij…Z!Ä$òÎRÖCG¹eฑ`­0Ï­q.ö©ø¸Îó7÷¶ý¨\pÚÒ !2¹7³›‡Ž²oȨ„#i…y®±h… òÞ=wÏÏÏ{¿Õù\üV>äsÙÐG߯iK+„øC^ÒÞNè›8dTB€ñ´Âú6N[Z!¤ .ÎCGÙ5^üRB€Y´Â<[´B¦ ¶#ãí½;ïü€ ?*œ¶´BÈB.\u-ÑøvB­=^ÜH0—V˜ç’‹VÈŸÏyð6À‹÷ñÿ"?`«Ê§-­‘ Wã¡£l)*!@Zaž«.Z!#\|¢æ?Ž|Ïóû=Ï‘°Õgé‚Ó–Vé(†+u;¡fy¤¨„¡h…y®½h…Œp:Þ¶ççç#ßóâS:Ï‘°Õgé‚Ó–VÉ…µyè([†‰JV˜çò‹VÈœ‘öùóçãßöüM‚Œáì? V¨BjraÑчŽòñ0™X }€ë´Â<a´BºûùóçÛ‘vü;Ÿ¿ÉÛï|þëü€M>Kœ¶´BÈK.ŒºÔü/áÝö§ üRB€Œ´Â4o­þN§ÓÈ”vþëü€M> œ¶´B(C.œº¼ü8ðíýúí¡ÐCG‹&7ä¥湘£ÒVÿtêi…P’b8cm¹5óíýz]j©„h…y®çh…tw1¥===ÿÎÏÏÏa[a®Щ§BUráØ…å·1/-<ˆTB€2´Â<—t´Bæ ³ß|Щ§Bará¨Ù0_%t;a¨¤£¦Ù“k…Lf)¾ù:? SO+„ÚäÂþSá„Jè¡£e†DP’V˜fg®2i˜¥øæëü€N=­V v›=t”[ŽJP›V˜f‹.Ì1c˜i…Ñ~@­P+„EÈ…fe÷¨Q V ¦Ù¨k…ÌfZa´P+Ô araÓé£àí„Α®CF%X‡V˜f»®2c˜i…Ñ~ÀÔ}m?ðC ¶æ¹pU:êìè¶ŸšY €9´B­´B­0ÛÿÅåN+„•(†×ûÃBᘇŽ:#ºí¤fÞHèø̼v j… j…Z!›\xóbX+T “šþ¸QoÀüËZ¡VZ¡V¨áÉ…7¬Œÿøi¿µ|ýùKœ¶´Bà=+ß`xû3E‡‡BµÉî—ЄV˜æZ€Vȳ×ÁßèwþãÍóܲ? V¨Z0nª„Ûs¡Jþ w#! i…i®h…ÌlgÏÏÏ·}·‹Ïçœ;€Sÿ€Z¡Vl±N.ÜW ·äB ¼FS èA+Lsi@+d”‹wÉ=<<Üöݾ~ýÚü>¾•@­P+6Ú>ÒÃCáÝ·óß*;ÉLcÞj•€~´Bà•ÇÇÇVwÞ½wÏÝù¯høRã}úôç xþŸçxþWç/8™~Ó–V4$Îú„ïýàï}±³ë]•ˆO+ Ý´¥ÍM,†Sᇿˆð½\È–·T% ­€tÓ–Vô0%Ϊ„†B¹ð¶MJ@:Z!é¦-­èdd.œ•·‡B¹p×vG% )­€tÓ–Vô3&N¬„»Bá¯%^ßèL¬„Ž>Çi…¤›¶´B ·~ÅpX"|ï–ÀÛ‚”\xq‹ãFB Ð H7mi…ÀÍsáÈJx[(¼òƒ¿È…ìlTB*Ñ H7mi…À}saç'‹î …[~ð­P% "­€tÓ–V Ó%ùýƒCá®üeÝVè—P–V@ºiK+kU $‹­ð¶µ%.À„§nÚÒ ñŽäÂ17öèP ,`Z!é¦-­˜â†\0îMQë•,•€µh…¤›¶´B`–¹0æ„7רe’–JÀŠ´BÒM[Z!0ׯß87¾4m|ÕÛÖÌJè„`.­€tÓ–VL×5¯„/ê^ÅÎåFBÐ È7mi…@o$|ûŸ×¶Lª]*!ü­€tÓ–VLðFÂW:Å©üÍK%€¿h…¤›¶´B`¢8•ðúg¿>•¶|ù¥„pV@ºiK+Æ‹#!½ß=o%i…¤›¶´B`¤,7Òï­ó~P˜V@ºiK+Æp#¡·Îû@yZ!é¦-­è»p t#á¯oÀ„wO%`Z!é¦-­èµd t#á«oÆ wO"`5Z!é¦-­h¼R w#áÅ}ß=•€5i…¤›¶´B Ù5î„rá¸wO%`eZ!é¦-­ÿaïîÎY²m– ¸€ Ø€ rô¸Ï.à.`.à.èÄíºÚ§ºHdF&kEŒñõË®–RùÃ$&‘ÀÔ©i¾–Pc8ËÔ€®€tÖ®?) SNè ¹€oi Å€&é H7lé —ç¢![Buáò×ÐBBø‹®€tÖ®xa»%œVþrµ„0‘®€tÖ®øyòë ÿ†Ë_C-!< + ݰ¥+M;ô„÷öP]¸Ø5TÀt…¤¶t…ÀÀl3êBÂAêÂY/£…„ð<]!é†-]!ð_óÌ`ßHøänûúÂ9®¡–^¥+ ݰ¥+þÿ 3RE8®rê~¡–ÞLW@ºaKWû¯%œÒ:õZj  ]!é†-]!t+`EX¥uê¬.|gK(Dð]!é†-]!ôæWÔ–°b÷ôÄj 1´ÂÑnØÒB?ÂV„3uOÖ…n7 qé H7lé ¡y¿b·„³ÖOmÕ…ZBˆNW@ºaKW ^.S?½£.¬Þ-úRBÈAW@ºaKWíù¥%üoË~}aÝ¥ˆ@&ºBÒ [ºBhIŠ–ð]%ÔRua•®ÐíF %]!é†-]!4 KEøöjþºpú·j  1]!é†-]!¤–¨% ÒCÍ\Né µ„ž®€tÖ®2ÊUì¡æi Ç}¿á×{[Bi€Št…¤¶t…Kœ–°ßf)¤–­ ¿´„Ð6]!é†-]!„m!a3Ë o-¾ÀЗ@›t…¤¶t…Í/-á;į -$€øt…¤¶t…GÌŠ°±›Ž>¶.Ô@ºBÒ [ºBx»_[Âæ—þ%Z]¨%€\t…¤¶t…ðF‘+Â~–ÞŠÐj  #]!é†-]!,ïWø–°·å„·ÞUª 5]!é†-]!,)NKx¯"ìy9á_® µ„Ð]!é†-]!, ÅBBË o-Sj  ºBÒ [ºB˜wÚ–a!¡å„ÍWj  1ºBÒ [ºB˜k–d!¡å„Ϙ£+T@{t…¤¶t…Pyž–g!¡–ð%o¼û¨“Yè H7lé ¡Ú -ÕBB7aù®Ð9€\t…¤¶t…0ub–m!¡å„¡®¹“-ÑnØÒÂø)Y¶…„–¼òN;´DW@ºaKW/ÏÄÒ¶„–†¼þ¿œvh†®€tÖ®^˜ƒ¥­-'Œú(øý? ºBÒ [ºBøyꕼ%´œ0ðcA]MÑnØÒ£IW˜–p\Eh9aøG„®š¢+ ݰ¥+„¹Vþ…„–fx\¨  5ºBÒ [ºBø¯YV˜–ð{Ö§%løÑ¡+€Öè H7lé á+ØBÂïùž›Ž¶ýQ@ƒt…¤¶t…t.ÚB¯ -¡å„©)ºBh®€tÖ®nE[Hø=ͳœ°‡Ç‹®Ú¤+ ݰ¥+¤7~Oð,'ìê£+€é H7lé éGÌ…„ß³;Ë »zøè  MºBÒ [ºBš÷«Ñ–ÐrÂåý3çÚ + ݰ¥+¤a‘+ÂïIå„iü33g + ݰ¥+¤=¿šn -'|]!ð#]!é†-]!-‰_~i súgÎ3d§+ ݰ¥+¤¿’´„_n:š–®x†®€tÖ®Ôâ´„OÌß,'LLW¹Ù_Ý´„_3/'ôH芮€tÖ®0¦_3Ô…Ïl3NK¸ÄIvÓQªÒnØÒÆô«v]øxƒ]-$ürÓQæ¡+ ݰ¥+ «b]ø`S]-$üžGYNÀ,t…¤¶t…‘U© -ò¿à ¿gP–0#]!é†-]apëÂÈ-áÒgÒrBf¦+ ݰ¥+Œot]h!á÷ÄÉrB– + ݰ¥+Œé¯*jD]h!á÷¬ÉrB¢+ ݰ¥+Œé¶z©.ì|!á÷|ÉrB¥+ ݰ¥+Œi°™z².Ô~ͼœÐã€AºBÒ [ºÂ˜îUTëBá—›Žð>ºBÒ [ºÂ°^ª µ„_n: À»é H7lé #[¸.LZ~ÏŽ,'àÍt…¤¶t…Á-SöÙZN@]ºBÒ [ºÂøf­ óV„ß“"Ë ˆBW@ºaKW˜Bõº0õBÂïéå„Ä¢+ ݰ¥+Ìb°Ïêp!á÷\ÈrBÂÑnØÒ&2¥(l`!á÷,ÈrB‚ÒnØÒæ2¢(l£"üži ˆKW@ºaKW˜ÎEaÀ£vÓQâÓnØÒ¦ó œX=^7 ]!é†-]a.?¶ãê¸Çk9!yè H7lé y²|©.Œ{°–®€tÖ®0‹—À8úÁZN@BºBˆïz½‡Ýn·ÙlV«ÕŸùòŸåËÿU~ ü˜tÂ;¶t…) v_o.z¯.Œ~¤––®";NÛíöù”.¿âÛ>á›Í¦ç§>]a#ŠÂuaè#µœ€Ìt…Óår¹­„žT~±üºlõ„¯×k]¡®0¸ÑEa®ºÐrB +„€ŽÇãô8”8ÀöNøõzíü©OWßÄ¢ðwG¿.ÔÐ]!Ds8j%¢lÊ6vÂ???u…ºÂÈ^* ¿þ_aëB7 %ºB¥Ê·È« £`®~>Ÿ=õé #Q~=ühu¡›ŽÐ]!Äq¹\)Þ~¿?NþJùÏÃá°Ýnübœï.Œv€¹Nø½¢PWHãŠÂ¯‡?§+´œ€&é !ŽÍf3øÀ^¯×çóùñï–(?6øëÛíÖ¦>á×ëuðÖ£ºBOÑŒ. ¿þðÛ»BË h˜®‚8Nƒêç7R~xp#-Žs€ÁOøårù½€ñóóó^¡©+Ô4¥(üzø+oì -' mºBb°±Bmðö˜eã0Å ÷Ô÷ä°¥+Œot/§P³œ€è !‚{kÜ®×ë«›*¿2¸©oªÙÕ†=ážúž¶t…ÁM¬Æ"4k–Ð ]!D0x+ËÃá0nkƒßm÷Ò­5›?À°'ÜSߓÖ®0²*ÕØû5Ë 芮"¨ûx¾·ÒÍÆ?ážúž¶t…aUlÇÞR´i è®Þnð~˜—®›+È?ážúž¶t…1U/È–lÜÜt€>é áíï`y<§l³üúí6Ër€ÙO¸§>]adsd ”nn: @Ït…ðv›ÍæöÁ|½^§lsð®˜å9Àì'ÜSŸ®0²™:²Y«7Ë 蜮Þîö‘¼^¯§o¶l$HF¢`êî©OWÙ|Ù[¶œ¾t…ðnçóùö‘¼Ýn§o¹lävËåÏu~€ÙO¸§>]!•¢d9!ü?ºBx¯ù¾çnŽoåk೟pO}ºB&‡ÈrBø?ºBx¯Á‚©JuµßïgÚrêÌ~Â=õé ™– Ë à¿è Ὣ«Óé4}Ëe#a»Â7`öî©OWÈØìXNt…ð^›ÍfÉêªü¹Î0û ÷Ô§+dTp´„0LWï¥+\øu…m [ºBžŽŒ›ŽÀ#ºBx¯Áêêr¹Lßòõz Û¾ñ³ŸpO}ºBž‹›ŽÀÏt…ð^³>’#Ä$Úf?ážút…<—Ë à)ºBx/]áÂ×¶1lé ¹ŸË àºBx/]áÂ×¶1lé ¹Ë à5ºBx/]áÂ×¶1lé ¹‰†å„0†®ÞKW¸ðÆu…m [ºBþ;–ÀHºBx/]áÂ×F{´ö»1¤{ó.'t~hž®t…ºB]aº¸üÒòŸYŒ–&Ò‚®PW¨+Ô’nþ2ëMG_ú¡+]¡®PW¨+ä-s?þ7î·þÇrB˜HWïµÙlfz$_¯×Û-—?×ùf?ážúþì =dþ˜Äÿüõ¿q¿Uw9¡ë@‡t…ð^ƒÕÕù|ž¾åÓé¶+|ãf?ážút… ¸×èø•ZE¡‹@·t…ð^ƒÕÕétš¾åÈ]á0û ÷Ô§+Ìîq¯7âW,'€)t…ð^»ÝnÉêªü¹Î0û ÷Ô§+L홂ïÕŸ·œ¦ÐÂ{}~~Þ>’Çãô-ï÷ûÛ-—?×ùf?ážút…y=_ó½úó–ÀhºBx¯ãñ8SÁ4_)–ú³ŸpO}ºÂ¤¦¬´œæ£+„÷:ŸÏ·äé[Þn··[.®óÌ~Â=õé 3Ò@XºBx»ÛGòf³™¾ÙÕj$#Ñ0õ ÷Ô§+Lø¢%TÀ ]!¼Ýf³©þ`¾^¯35b `êî©OW˜‹å„œ®Þn·ÛUÿž»Áoå+Èf?ážút…‰XNñé áík¦‰ß W~½zÖ̦>ážút…YXN)è !‚ÁÇóõz·µÁûa¾7 Ñ0ï ÷Ô§+Lòœf9!ä +„W¥‡q[ûüü¬¾n®±Ì{Â=õé 㳜ÑB§Ó©ÖJ·{kÜÊŸ¨Ûìó„¾([ºÂ°Þ~ëQ—^¢+„ V«ÕíCz»Ý¾ºò+·Û)¯Û0à }Q:¶t…1Í]>® AWAÇÁGõô[keãucÛÀ<á£/J‡Ã–®0¦wu…Î<Œ¦+„86›Íà»üûåryü»åüzõضq€ÑögôEépØÒÆô–®Ði€)t…ÇåryððÞn·‡Ãá¯oÁ+ÿ¹ßïoƒù¯k¯±mã£íÏ|O•í [ºÂ˜î p˜NW¡‡º¡˜r3Ì9²êîçÆ'‡-]aXŠBÈEWÑTl¯Ê¦fŠmp<7>9lé #›»(t† "]!T¥½š¸Àík¶®0ÎÜÏO[ºÂÈþùçE!d¡+„˜.—Ëf³„ò‹S¾2ï™Ø¶q€÷Çsã“Ö®0¬ïêÖ£‚®";NÛíöù”.¿²@lÛ8À€ûã¹ñÉaKWÓ?ÿ­zQ¨.€êt…ßõz=»Ýn³Ù¬V«?óå?Ë?–ÿ«ü@ù1è„w2lé cúçFõ¢PWué H7lé cúgHÝ¢PWué H7lé cú玊E¡®êÒnØÒ†U«.TÀ2t…¤¶t…‘M¯ …°]!é†-]apSêBE!,IW@ºaKW߸ºPQ ÓnØÒ¦ðj]¨(€åé H7lé ³x¾.TÀ[è H7lé y¦.TÀ»è H7lé sùgç  + ݰ¥+LGQ1é H7lé 3R@@ºBÒ [ºÂ¤…®€tÖ®0/E!„¢+ ݰ¥+LMQqè H7lé ³S@ºBÒ [ºÂ(  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐnØÒT¡+ ݰ¥+¨BW@ºaKWP…®€tÖ®  ]!é†-]!@ºBÒ [ºB€*t…¤¶t…Uè H7lé ªÐ­ëõz8v»Ýf³Y­V>ÆÊ–,ÿWùòc°½ýiþê·1lé ªÐŸN§Óv»}þ!W~¸üŠlc²\ýÍfÓóSŸ® "]!üv¹\n+˜'•_,¿îóîO®«¿^¯u…ºB€*t…PÇ鿲˜qr]ýëõÚùSŸ® "]!‡ZÀ²)˜kÒ]ýÏÏO]¡® ]!«²¦,òêÂhjÒ]ýóùì©OWP‘®ž].—ÇßC·ßïO§ÓŸ¿Rþóp8l·Û¿ç» £`¨ýIwõï…ºBFÓÒ³Íf3ø@Z¯×çóùñï–(?6øëÛíÖÆßŸDWÿz½ÞzTWèI `"]!Ý:Nƒ¢ç7R~xp#­Gs€Ñö'øÕ¿\.¿×0~~~Þë4u…ºB€ét…tk°‚±(lðŽ”eã0òþ„½úžúž¶t…Uè éÓ½ee×ëõÕM•_ÜÔ÷±ìêCíOä«ï©ïÉaKWP…®> Þ=òp8ŒÛÚàwɽt7Ëæ0ÔþD¾úžúž¶t…Uè éSÝÇϽÅe0æþD¾úžúž¶t…Uè éÐà-('.\ªVþŒ¶?Á¯¾§¾'‡-]!@ºB:4xÓÈãñ8e›å×o·YþŒ¶?Ù¯¾§>]!@EºB:´Ùln<×ëuÊ6oDYþŒ¶?Ù¯¾§>]!@EºB:tûÈY¯×Ó7[6ä1íCíOö«ï©OWP‘®ÞœÏçÛGÎv»¾å²‘Û-—?×ù†ÚŸ®¾§>]!@EºBz3ßWËÍñEx `¨ýiàê{êÓT¤+¤7ƒN•¶h¿ßÏ´åÔj¸úžút…é éÍ`[t:¦o¹l$lWøÆ µ? \}O}ºB€Št…ôf³Ù,Ù•?×ù†ÚŸ®¾§>]!@EºBz£+\øu… <—v8lé ªÐÒ›Á¶èr¹Lßòõz Û¾ñCíOWßSŸ® "]!½™õ‘áaíCíOWßSŸ® "]!½Ñ.¼q]a¢ë›hØÒT¡+¤7ºÂ…7®+Lt} [ºB€*t…ôFW¸ðÆu…‰®o¢aëwW@EºBt…ºB]¡®PWÐ']!ºB]¡®PW˜e 0jÔ¥+DW¨+Ôê S¬+ :]!ºB]¡®PWØáE€Î5ó¾®" ]a´ µ? \}O}ºB€êt…ôc³ÙÌôȹ^¯·[.®ó µ? \}O}d¤+$ˆÁ¶è|>Oßòét Û¾ñCíOWßSé b°-:NÓ·¹+|ã†ÚŸ®¾§>2ÒÄn·[²-*®ó µ? \}O}d¤+$ˆÏÏÏÛGÎñxœ¾åý~»åòç:?ÀPûÓÀÕ÷Ô@FºB‚83u:óõP©0Ôþ4põ=õ‘® Îçóí#çããcú–·Ûíí–ËŸëüCíOWßSé ‰ühÜl6Ó7»Z­‚<&£`¨ýÉ~õ=õ‘®86›MõÏõz©„jàCíOö«ï©€Œt…ıÛíªµÜàá•?ä£íOö«ï©€Œt…Ä1ØìLüÒºòëÕ¨f0Ôþd¿úžúÈHWHüäõz·µÁ[P¾÷íCíOê«ï©€Œt…„2¸ìp8ŒÛÚççgõ¥j`¨ýI}õ=õ‘®PN§S­Åe÷–••?Q1&Ù0ÔþļúS$ @pºB¢Y­V·¡ívûêvʯÜn§l¼nL8ÀPûðêOyˆ3Áé ‰æx<>ЦßZ³(¯“0Ôþ¼úS$â @pºBÚl6ƒ¤òï—Ëåñï–xðëÕcÒÆ†ÚŸh'gʃD–NWH@—ËåÁÃi»Ý‡¿¾x®üç~¿¼óä¿~lšFĤ µ?ÑNάO•ÂÀ{é ‰ép8Ô}N¹ÿäíPm¢œ/]!ÒVŨlj¦˜´q€÷'ÚÉñÜ@“t…DV¥0šuMYsBíŒçFš¤+$¸Ëå²ÙlÆ=ðÊ/Nü–ºcÒÆ†ÝŸ8;ã¹€&é Iát:m·Ûçrå‡Ë¯,“60øþDØÏ4IWH"×ëõp8ìv»Íf³Z­þ|Œ•ÿ,ÿXþ¯òåÇ`{ûÓüÕ€åé  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ú¤+€>é  OºBè“®ˆìz½‡Ýn·ÙlV«ÕŸŒòŸåËÿU~ üØwòr¹”}øüüÜüÇí\h½^—/?p<]SDh5’§Ó© vÛívp4ü=Jþ ËÐéò!’ËD²üÝ2½·3Â&Ò_ºBZwûVUÆ‘´Ïñôt:m·ÛçÏOùáò+Kîáù|.¯æþz•ç:ФHVŒä¯e‰¤HŠäèùøøxõ’­×ëý~ïQ‘Éê.—ËK‘,¼Ãá €"éšöüÚÖåÉ®"™eæÁ@Ûþú ­Éd —Ëep=“ïu,ðéèÃápûÐrER$«GRW(’._ðQòt:½ú™™[ŸŸŸC‘ÉZ;3¢¸ÿwY“;`ˆ¤kÚák[—O$»Šd®ýY˜? »^¯sJ&“ÕÇéçj¾7:¦¿)*’")’1/h‡ñI‘œøøyi‰ÄÇn‘ÉN&®‡ÃaúΔ\ëîEÒ5íäµ­Ë'’½E2×þ,σ€†}~~šLæRå-ŽßªßI©ÌGNÛ¼H$Er\$u…"éòÅ%Ïçóq·Û%ER$Ç©2Mým½^[Ê$’®iÛ¯m]>‘ì0’éögyü´êÞ»X&“aUùÔåLŸÀ,§Ñ75/I‘I]¡Hº|GÉÇEáf³ùüü>Œ’")’¯ªXþ«óÕ…"éš6üÚÖåÉ#™nÞƒ€®f&“a].—Ço<î÷û¿¾Þ½üçápx|´*Š~fõDÙß{ø×_,ÿRþýÏI‘ɘ´«xФHŽ%??ŸÜ`Ù·_ÞT¶c”I‘|Þ㢰ü¿åïÞîÌ_ÔÁÕ…ÝÖ…"éš6üÚÖåÉ#™nÞŃ€Æ”—´ƒ÷ Xr2é*Œpï=Ãõz]¦m?Îëî-ú+¯¶æ›4þ~¡÷ä'<Ë#³¼ú[­V")’"A·w˜I‘œÉòø\øÌnÜz°æâ¯wPER$Eò¥áìßÚýDzïr¹<è&:¼-°Hº¦&Ò")’íE2Ñþ¼—®€ìÊ+ÜߟÁ+ãûƒÏ¨›LFV®àô‘ÝûLõ”÷|"tµZõóN¦HŠdHV|IØÏCE$E²b$ßK™²øèÞrÊcÕ()’"9:AÅKõýƒoUk~º+’®©‰´Ë'’mG2øþ„¢+ ·QÌdòùcYìOÎÐF|lrðsÑSÞo¼÷‘ÎòÚ­óïpI‘|K$kì;Úë&DR$ëFò^É>ñ–h÷ÞS±PQ$E²·QòÞ›œ#âs¯vløž"ÙL$C]Ó&Ò")’¹"vš‰¡S €É¤Éd-÷Þ$QÆÝ{sÜû»Ýnpkûý^úDR$—d-÷öª½Ïi‹¤HÖäàÊ£*·(\=ÑÞÍER$ëFòÞžŒÎîݸdßÄU$ÃF2Ú5m~"-’"™ëšFÞŸfbè”`2i2YËà;„£ß”\.ôÒý^¿Ê+Û=‘Éå#YÑàÇšü¶HŠdÝH.p¨òŽå`Ca©¯HŠäˆHNan½^›¸ŠdØHF»¦ÍO¤ER$s]ÓÈûÓL RL&M&cžÃZ_CVëÖ1")’"ê¢ÜûÒ&¿üE$E²n$W«ÕL'¡“¯I‘¬øàŸéžÀ÷FÉ&×1‰d‘lûšvõÛ")’ºÂ¤1tJ0™4™¬bpùÞŸˆ~©¼ÍÚjµò…")’o‰dEƒ;Óê7¿ˆ¤HÖä¬WSW(’"ùR$'«U>ÕÖÉ=E²HF»¦ÍO¤ER$Ó]ÓÈûÓL Ròs&“¡&“ƒ·U9S¶9x+³—îº^¯;yE&’"™"’µÜ[.1q†HŠd'‘¬õLÍ?TDR$ˆäàý´«|§öù|îç6¤"™=’ÑNxÛi‘ÉŒ×ÔttúÐ`2i2¹Ì Þês⃷jy~ÝÐàÇA[]v$’"?’ÍñÕN")’ýDrðjN| ô·ÁµZ‰¤HŠäK{Rëƒmƒ7îðö"?’ÑNxÛi‘Ɍ״Û@Å3 +ÀdÒdr™™és˃ ŸüÝ~nó"’"™"’µ ~  çE…")’¯ÎàoUy»rpÑD‡ß,’"ùüáÌZç ~´¦ÃɰHÆd´ÞðDZ$E2é5íöPñ è 0™4™\à@ïqTå½ÁÁ·8ÊŸw:\Ú ’"'’µ ~Ò»çE…")’¯Frð³4U„Á-‘I‘|Ë¥¼b‡÷<Éà‘ŒvÂÛžH‹¤H&½¦Ý>*ž]!&“&“ È|_¾0ú«"w©Ê—¿ ’"9Ç­ ŸdQ¡HŠäôHîÏôw,ï®fÅ„HŠäãHÎz)ÍÝn'’"*’ÑNxÃi‘ɼ״Û@Å3 +ÀdÒdr™ïCËûý~Ü–w»mФHƉd-ФHV‰äàm'¾i98òöù)‘Éç·¼|WØá÷£‰dðHF;á O¤ER$ó^ÓnÏ€®“I“Éw½åRåËPßâxæõ”/ƒI‘ É*î-†ê|Q¡HŠäˆH>XZø»õëÒ÷¤Ê·;‰¤H¶ÉÁKYëû Wûê E2Z$£ðV'Ò")’©¯i·€Šg@W€É¤Éä2¸Òg¾×S?¾Å1øÆÈíwL”;»Ý®lð¶[\­Våß?>>öû½‰")’S"YËàJ(‹ ER$ÇErpà_ï^>YÄ~µSÏËùER$ŸäàÇÛªìL{‘l5’ÑNx«i‘ÉÔ×ÔØ7ý è 0™4™ìð]Ð?±y8¿Yþõz]~KER$ßõG  E…")’u#ùñññãðW~æñ®ÞËfϳI‘|>’ƒ;Sñæ½ÞÜÉø‘ŒvÂu…")’ÙO¸±OW€Édü‘t™ñ4È[.UÞÀwë¤Áo‚øýL‡ÃáÁ—4ý¨ün‡ß>/’"91’U &w·ÛÉ£HŠä”HŽ˜ƒÃ_ùÉ¿ö¹üõ{¼é|=¾HŠäó‘¼ÛaÅÕ›{"?’Ñ®i“i‘Éì×ÔØ0^†`2i2¹ð±ñÁw]v»Ý”–ð¯Û™Öú‘É"9ݽ…K'Q$E²ÊÆO§Óóããz½þ]ÞKåf³±ÚW$EòùÏýU¼ÞÜÉø‘ìj®+’")’îO3 +ÀdÒd²ÃwA? Z×z½vw5‘ÉeŽhð3ØÝXØ()’"9qンyué½›u‹¤HŽØø½Ú=é¨-’"™ýšê ER$³?°MW€É¤É¤·\~[ +¬û‘o‘I,Y.Ñ{æ6µ¾ Q$Eòï$ë ER$û‰¤ …}Èy€É¤Édü×S35zó}›ŒHФ®ð¥ÐYT(’"Yeãƒ{pÏÃçKÃÕju>ŸER$EòùßûxLuºB‘TLt2‘I‘Ôê u…˜LšLê gÚŸ{ùI‘Éêî}“E…")’U6¾ÛíFúåwiøã ¦ãñ(’")’Ïoü|>?ó¥Ûm·[‘IÅDói‘I]¡®PW€É¤É¤·\æÛŸÓéäíP‘Ée.E…")’óm¼ [U„2,>þBÃ>WФHŽÞøãûÞ¿ä÷mK}äF$ýl\$ERW(hK>ä¼ ÀdÒdòÖàû„UÎÃàí˜~¼uÒ¬WsðóÞ½½ë"’"ùR$G°¨P$Er¾HÞ»Õáè"þp8<8Eöû")’GÉãñøÌ7>h I÷à§F·HŠd'ÅDi‘I]¡®PW€É¤Édœ·\ª,%\Ç÷Þ®p¿ß{‰'’"9÷ãpUÅjµ2 ФHNäççgõúàz½Þ[`è[}ER$Çm­¤òÕ[’–ÑóÏ,†½ì¤HФb¢í‰´HФ®PW¨+ÀdÒ\bðXæþ‹ƒ¯§ª¼1îõÔà'±kìà.­×k‘I‘œ5e}.…I‘¬ÉÁE µŠø{uaoK ER$+Ž’—Ë¥ »Ý®lç¶:,ÿRþýóóóx<Þmðƒ7FI‘ŒÉh'<ûDZ$E²ÉkÚí âÐ`2i2¹À ~Ê|¯§ÊŸñú®Ö•÷nÝ&’")’³¾EcQ¡HŠd•HÞ/´ÖÝ}ï ‘ûý^$ER$—wûñ¹[ ‘ŒÉh'¼Ÿ§‘ÉVO¸wlt…˜LšK¼k29xƒ£ãñ8}˃7üüñ-Íù> jæ)’"9_Ëðà]‹ ER$kEòããcÖ!rð-Öív+’")’ üæß[ ‘ŒÉh'¼“§‘ɆO¸wlt…˜LšK¼k2Y^:ÍôªgÜ+µ¹¿œÅ4I$E²ú›'ÿ²¨P$ErÖH~ýYÅ{„ºS·HŠä¬£äó[‰¿¬P$ãG2Ú ïä)B$E²áî]!&“æïšLžÏçÛ?úññ1}˃_³òã7ËÏzƒ53O‘ÉW#9ñÍ‹ ER$+FrîKéNÝ")’ó’/ü`€QR$F2Ú ïä)B$E²án.ª+ÀdÒ\â“É™¾¥ýökVž9œù^ß}Ýy´·EO")’3Îàö-*I‘¬I‘ɼ£äóo@Zk2,’"Ùü5mþ)B$E²ín.ª+ÀdÒ\â“ÉÁÛNÜæ`+÷äË´ùê†Á»«Uyñ(’"Ùp$Ÿ1¸"Ø¢B‘ɺ‘¼”—ËÅ£E$E2ø(ù’¹¿™T$E²íkÚüS„HŠdÛ'Ü\TW€É¤¹Ä'“»Ý®ú—/ Þ°ü¡g~w¾¼ ~ùË“{%’"Ùm$ŸaQ¡HŠä‘¼”¿,Éê{‘É·Oï°ÑÛ׆Šd®HF;ám?Eˆ¤Hf¼¦Ý>*ž]!&“&“ËÈàkŸ‰w:üDô“¯Ñ½ù¾f¢·uO")’Õ+†{‹ +V")’"ùug­DÅ;©Õ÷")’KŒyÏ‹ôE2~$£ð¶Ÿ"DR$3^ÓnÏ€®“I“ÉÅdð^¯×q[\˜ðü±Üûõ‰·Y»·Ùч)’"ÙI$Çm¼Ïû2‰¤HÎÉÁµ²Á÷B???ER$Er%nÆS‘ÌÉ×´§‘ɤ״Û@Å3 +ÀdÒdr±|{pôg˜ßèxé܃ Ë?Vûeâ6ER$;‰ä«ïù›•DR$ç‹äàZ‰Zq¾\.ƒ¯rp‘ÉnGÉçÝ[¤ßùx*’)"í„7ù!’"™÷švû¨xt…˜LšL.v §Ó©ÖÇ/ï}ðò¥7:îíÏè—xƒ_þÒçÛ/")’üФH.É{¯÷§ß¢pðS:~KšHŠä[¦ˆ÷ŠÂ>¿M$ÓE2Ú oï)B$E2õ5íöPñ è 0™4™\ò@V«U•ewƒo6–¿ºÁ¯k÷vhy‰7xt}Ö")’ã"9È¢B‘É…#y/tëÂÁ%}~KšHŠdÅQòIƒ_Õý»¬ïíVù"™7’ÑNxKO")’ D²Ï@Å3 +ÀdÒdrÉ©rg³{o6Žøæ÷{+‹ý~ÿÒvÖëµûª‰¤HNŒä-‹ ER$—ä½Ü^‚T68øF¨OÔˆ¤H.àr¹Üût\·3U‘LÉh'¼§‘Éf"Ùç âÐ`2i2¹ðÜ{³¢üûårý^Çè7¬ž(ÛüqáÒõz}°…ò‰¤HŠä÷Þ?±¨P$ErÖHÞ{ÿóßÅO.|‘I‘ì-’>*ž]!=bGÒeÆÓ÷Žàå5уcßn·‡Ãᯠüç~¿¿·$aú›÷–þ{k¦ò×Ë>üù¶LùϲŸß~iøËèER$gäûfQ¡HŠä‘|Üñý;ÒÝîOù[ÏìRÃ=…HŠä|£ä“é+ÿïàÝû, E²¥Hƹ¦ñ'Ò")’F2æþôC]!&“&“¯*¯˜êž±‰wh¹^¯ëÂ. ER$çŽä¿,*I‘|o$$f¢†{ ‘ÉùFÉ*;°Z­ºZQ(’Eò+Xv"-’"Ùg$¿t…a"àu:&“&“ ¿¤zò6hÕ­ Û. ER$ˆä—E…")’1"ùÌêB=…HŠäb‘œ¾ÛíöÉ[˜Š¤H†d´Sp"-’"Ùs$5_A"àu:&“&“K¾¤ªø©ËëõºÛí¼¾I‘ É{·fòåJ")’ Gòt:ýx3C=…HŠä2‘œXÓ÷¹0_$Û‹dÀSm"-’"Ùs$5_A"àu:&“&“Ï{ð}î?zæûâ~Gôãã£í¯–I‘\,’%‰÷RfLI‘\>’×ëuúòKô")’óErtKØÃ‡ÙDR1ñÞSðµ­HŠdŸ‘Ô|‰€×é˜LO_u:·ûí’„¹ßl</½Ðë§%I‘\&’÷ØUÐDR$£’×뵌#nÙ]FI_Ž&’"¹ü£kµZív;ëñEÒ5]òÔ|mëò‰do‘t)ƒDÀët`Ê;»Ýn³Ùüµ²¯ügùÇò•XòÆevi½^—üüüìäF1ˆd„H‚H‰dù+eø+ƒàæ?×G|||”è󆇈ä|‘ü7zƒ­ýïH–(Ú§kÀ¨ 0HW}Ò@Ÿt…Ð']!ôIW}Ò@Ÿt…Ð']!ôIW}Ò@Ÿt…Ð']!ôIW}Ò@Ÿt…Ð']!ôIW}Ò@Ÿt…Ð']!ôIW}Ò@Ÿt…Ð']!ôIW}Ò@Ÿt…Ð']!ôIW}Ò@Ÿt…Ð']!Àÿ²ww×Éã\À§Z j Z NçŒh¨hòéÖ“•l#ÿJ–®kå`&wâÙ–öÖ¶e¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!¬(g¯|O€¢}µBÈ3g¯|OÈÁý~¿\.Çãq·Ûm·ÛÏsc³Ùìþ~æt:Ýn·Çã¡ÝV”w¨0U ù[58N—ËEÉ`dƒW¾'$t¿ßÇc¸¢Oí÷ûóù¶ã<X{¢§V …l³ÙGEÃa ^ùžÄívÛívν:°Yç!@1‰žZ!€²¯Ýn7ø £j¼ò=aaÇc¿ßÏ7e´ÙlN§“ó ¤DOØ …ìåx»¯Ð1ýÇã¶à\È?ïP+ 2…°ÓéÔ‘BÞn7M=mƒ—´'Ì­ãò¼\.“ü‰çóùöW4;@ny‡Z!‘)ä°Mu¼ m·Ûiêɼ˜=aVžN§ÉÿÖï3†Z ·¼C­€ÈrðÖ—"|y<ZûG­Åµñ7›ÍLñõnD-[Þ¡V@d 9xkO0Ïg­ý£VÈâ‡Ã¬«°–¼C­€ÈrÌÛ ûý^kÿ¨²¸ívëQ_y‡Z!ñ)ä˜ ^¯×u½²ðù|†}>a7›ÍÛ"á›áŸÂ„[¸ÁÃ_¼\.‡Ã!ìÃç^N§ûýžÕ¡ÿëñx4î|°Ýn_û?a«FèÆ] û¾þ©×΄x>Ÿ÷ûýçÖÂ7Ã?åS‰3±³° O~@¢§V@¯rÌï÷û€D²×χ?q:>çáÃw‡Cü®Þn·ý~Ÿ‡¿²@ƒ‡ØöxæçG¼Ks$û¯z\ÛƒlÂ'Óª1ŸâñxD¶g8¯¾VpÂü<÷Ú>ZÃÌ'v†í[†S^ËŸü©â%µB€âSÈ™ÒÒñ»q¹\Þý¶óÇ#²ÜÓXžSêÞçøªÖ[sÀ3J“ú˜£3íGøú)Â6Çc¯= ¡íÍÛíÖ«”ÉkÛv,“çÚʨ&9ùRÅKj…ŧ3¥¥cvãv»ENÅݽËå2>)\êØçÓé4x¶ÛmßrôOT ®½þ5`UÕŽOѶî€= °×#¨Y• Ûö*“'ÚÖ^+Lxò¤Š—Ô ŠO!gJK‡íFßGú÷mÀS{ëNÕ8“”ú– §:ô«Î.PVkkÏñú÷éÑÛí¶êJPÛ©5ìΤ ÊdÊ+íÉ*^R+(>…³ÁÇãѶ´ã€Ý¸ßï}Wöëø+ W[fMÕ·Ûí‡~ÚZÉ€²Z[;ŒßÝn÷3îaÏÏ­%ÑqÚgû:Å´×ÑZN~€Tñ’Z!@ñ)ä˜ ¶-üØ]+i{ÊfÂtõ|>w—r¼­Êþ7|³û‰¿ð3åìûý>´Àß]z<¡y»Ÿ²àíŸÛí6|ŠptÞÞ̘m4¦›Š?{ó9ùgXj…t§¶ÓQ(ŒyÌmªç¹>]¯×iWùk+ÏE.á¸Ì "w»Ý|{Òø\UÌ_ìÛ¤1‡~Âöl¬Ú þ\Ónmm+gÆ û6r¹\&,.9éÔv¦E.Û›ÛÉ0 ÃR+ &…컑Ûí6ò|>ÏõfŽÇ»k.‘*Nžb·UiçÛ“Æ?rÕÊÁÏjMØž§ñà'¼·ÖëÕ–sèX%¸¯Ð2¯—‡æÐEÚív%¾ŸÉêäèe©™BÆÿúý~oœèîûÄÍ|‰gã–G>Õö²TŸ´q‘Õ¯õ‹Á{òùçÆ—ÀWÈŒyÚkîZa&[›Ðõzmic¹vØ;"¿þ­Æ:éøg Ô¬+©F~´L¶6­pUNø€áÈ÷Š.ÓJm¯kìõ\dn'?@ß(K­€%SÈ^+ûÍ”x6DF.@úÒøŒÒ×òÜLŸ´ñc~}.iØž4Ö\F®Áض?1ëNÛž9omráò<ŸÏá:𪇬ï‹öh¥¶uzc.ØœO~€$‰žZ!€ròBáÏlõ‚Æî.—Ëø-ç°fæ¯aÏ% Û“Æ"é|§âÂíYU­ð×ãñ8ŸÏûý~ªµI{]bs·Rèˆë¡ÜËíäX>ѱH!#—"œd7ƺÝn7Ós@9¬™9rg†íIc“&œ|P+œÖóù §S¸„‡Ã˜c¿¶çÜ­´ßïŸÚ°\jn'?À‰žˆ@ ùÕápx<SíÆøO×øœÔ°=|Óøæ²å×̳YµÂum-•pª¿ª‡E·ißUúÒöZƾ ¥æyò,–è‰X¤__Rv<ÇÔàò© Í½ñµ× ÿYÖÂí©VøÕý~|ËaÌ’¼³¶Rãó¶cV!ÎíäHºhg€jSÈÝN§Óår™ä1=µÂåwF­p][ËM¸ð¿>ióháL­Ôøäop<î-ͼ+JôD,RÈTŸZáò;£V¸®­åér¹t¾˜'øæh¥çóÙøäcøæäW™™7`E‰žˆ€È²˜ÝP+\ËþgrˆÕ ¸^¯c–!£•Âßó ÅÊ(Pp¢§V@d YÌn¨®eÿ39Äj…Ãìv»¶ÅŠ—o¥¶Gï÷{½%ÀàÐE­€È²˜ÝØl6s” ~ZÞ†þ\’O>Ñ€2Í„µÂIÞY™Ã™£V8LÇJ¤ ·R㵜Ïç™N¶„'?@ßÐE­€È²˜Ýh|Üév»ßrØH&OQ Þ™a{2_“æpæ¨ÓX:_¾Vø|>/…š¶?X&NV+ 2…,f7_[6ÉFa#ù¼­qgŽÇã{²ßï?ët:•q«Nûa®6Öò¶ÛíÈ×f{ò ÏÔ ˆI!‹ÙÓé4ÓCFUȘªÁŸ´qg.—Ë{ÒØ¤ûý¾ŒX­pÚ»d­ðx<νFhn'?À€ðL­€˜²˜Ýh\œ3æ­‚_5¾ 1f5Â9>iãÎ|-‘ Û“Æ& ¦zt+홣V8í‡Ýn·Ë´ÒõzmÜδ „ævòô²Ô ˆL!KÚÆ-ßï÷1Û ¿>x‡'ÿ¤;Sö#¤Z‰Q­0mE´eVåm»ç8'³:ùúÆ-j…D¦%íFã+ÆF.CÚ¸ægäR„“ÒÆùú²Â1{ÒØ¤©ž®R+ŒÙ‘Åñ¯ÚÖÿüºîøV gÝv»]liЬN~€¾Q–Z!‘)dI»q¹\¦}‹YøÅÆ ^¯×å?iÛÎÄ|ºÁ{Ò¶ÞcL2ó3§ÔZá¬Ï¾=ŸÏ¶¹£˜ ÚÈVj,Þm·Û™ŠwYü}£,µB"SÈÂv£ñu~»ÝnØÖÂ/Žyâ´Ÿ´qgx±±I#Ÿ#‹q¿ßc^u÷£VØg7B“Nûþ¾—¶Gí"ŸÞÓJçóyŽE†×ròô²Ô ˆL! Û¶G ¬DÚ¸àg¯2Á„Ÿ´mg"ëAcö¤­IÇWLžÏçétJõþDzk…¿Uò +†m'aü£»ƒ[©í%‰‘OøNÞŸ,òô²Ô ˆL!ËÛ¶Gz• ÛÊ"ñ¶}Ò ¢¶íLükÚF¶yã#c–»ü[(Q+œûâ ‡oda+œ´/ ì{ k¥¶Õw—Y 4““ o”¥V@d YÞn´=‚¹0cøŽê@¯Û6_» ;ÓQ£‰MÛÈ6o+ÖüÖOãKQá5–>—?sê©þ-—_¯×^o÷ '|Çã„}øÖJ—Àà……ûÊääªÐB¹m¯6û­†x+†ÿ ßì(Ìá¦ÍÙ÷ûýkOþ>lø|>cv¦×ê‹ãÛ¼£û·u¹\Þ6|´×Çé.9-æTX+ü{ Ç×Áz{Ðõuú… ?Ð}¾¶Ó«ò8 •ºO›ef½r8ùÆj…ÔS+ü™¡Ä0à‡óåé}—”œ¤Í;ÞݶÌ̃ZaÂSn|¡pX+e2ë•üäHjg€RSÈ‚wcÂrá€Bá|9û€wÏMÕæ1X©ÖP+ WDßBáÏšk…ÉO~€äA v(5…,{7º#¯67_ÎÞkéÑ9Úüñxt¼Ïq˜ÈwÏ©~õ|>ÃéúuíÐ16›Í°“ðgåµÂ´'?@ÚI­ à²øÝ¸ß÷Ã/¾½Ímä'Ýl6·ÛmØþŒÙ™ÉÛüz½†Ï2¾êt<ã?”Za¼Ðª1/ì{¼ú¾²s|+e8ë•ääH2©œBV²·Ûm¿ßÇ'Âá‡Ã¯LþIÃf_ÿt¿ßã—HÝív#wf¦6{5`¡×W•$´@ÚOQv­ð×ãñ8ŸÏ½NþÆóvð³„#[)ÛY¯…O~€´!“Z!ex>Ÿ×ëõx<îv»·'ƒÂÿ†o† ?0àElƒ…?w:>÷g»Ýî÷ûóù¼Š'n·[ØÕÃáðùA^ŸåÕ¶—ËŃT …Æo¯#Õö|ëëx…Ÿ ?9¾b^<'?œZ!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô €¹C ÍxÀˆiÄ„]­j…P§ûý~¹\ŽÇãn·Ûn·Ÿ×òf³Ùý'üÌétºÝnÇC»5T•5xÀˆ Å_­j…P[²²øÑ¾Æ÷ûýù|Ûј Š à,5Ÿ5ŒªŒt —•Kò¿ZÕ ¡·Ûm·ÛMx±o6›ãñ6«m¡ÚäT$€³@ÖP|Ö V¢P`îËÊ% ɯVµB(ÞãñØï÷ó]õ!ý?NÚ±n…É©HÀ…à,5Ÿ5$¬A.•\V.IH~µªBٮ׫kòL!Õ q¬E²†âAµBË€ËÊ% ù_­"(Øårùg)Z±n…É©ÞÀ…à,5¨Î÷ëÆ_Ë@%—•K’_­"¨9åßï÷çóùv»=¿¿û|>Ã7¯×ëét ?³Ùl\û=MÑæ].YC…YƒZ!ˆoõy@ä§VE 9{÷»B.—KHíã7x¿ßÏçóv»uícô4E#˜w!¸d õd j… ¾Ö;ä‘WœZ!”'¤óéñxì•ï¿y<a ®}ĺhÇZ³ÈjÈ‚j…1e®VµBª9ÿÿýý*þÞN§¶+ôr¹L5±ðöWœcˆu5ŽÆq¬²ìÂæÿ’55¨€ÁŠ¿ZÕ ©ãäÿ÷í«àÛq{pÈÓ'ÿ[¿w ;ÍëjãXdÙ…©ÊÖ:ª`Ä–¹ZÕ ©àÌÿ·ñ«ÔÏ{¹\ÚÞ62Ó_|½åÄ™†XWãhÇ Ë.L­PÖ°ÖAP­#&°ÌÕªVHé§ý¿_E~äÃá0ë:B`ô4ØiÇÚ±VÕ…©ÊÖ:ª`Ä–¹ZÕ )úœÿ÷ëWyŸz»Ý6^›ÇÃ)b]ƒc Ô×…©ÊÖ:ª`Ä–¹ZÕ )÷„ÿ7ò«’+};ÿx<.—ËápØívoû¿ÝnÃ7O§Óõz}>ŸóíCØxã>l6›ðãñþuÖp€úºßïáŽÑß]z¯°?5ĺ¡Ío·[h‡ð‘wÿiÜí×?½Ó$3™7Nh–ðI_§ÇçŒè«5B‹ÏçpÍô×_GäíäüíO–¿^jNúæî+† .¯ßç¡ùy˜­ S+,-kxó;à~޶¡ç?Ê«–Mv©°&MˆÖ…¶}Òpþ„oöÉ mZf¿ßn-|sFËy~ì÷ä ÿúÖù„©&Iòï Xtçj…”x¶ÿÛ뫆+=ÿˆ®íÞæF!& ƒþ´»6ضÓ§þŽÚá¿7øäkŽ£¿üŠùa¯BèòPµ QPªár¾ƒõŠ{O¿Hᘎ¬}gÛÛ„ÆÐ2S%}}ÿzøáñZ¯cñ›’4f|+ºÒös .áü®þ7fpþog¡V¸¾¬!fßÂÙÁ†˜gÌ„ÛȆêõëó¿ g~òÌ&zÅEÙ†™ÄðÓnp±&]àÒNˆ–…N;„Fþšÿ†vh«+}öT“_Å™ÌÅ7éïÄÂ2É]“$©z†5X,Ÿ ¨RÜ©þâ¯ôloÎ £päøØ8`Mò¹ÂF†ÅaÏÃþ§…æÞ`’ôõS„`i@ÈyOÔZj…îäà7e „è72³›£xâçÁ=üâ˜ð;òXÄ\Ñ+ºÒöóãàÿvj…ë˺ûÀ°“ñs˜‡Ú™BÓ ½¼ZažÙD߸(ð3«œqª .ܤs_Ú Ñ’¢Ð¯ÃÁñxìûÛ9œ9½Št#³élçÇŒ°¿U­´óc«˜$IÒ3¬wÀbù\@­²Îó•}¥gØŸ‡düPŒ1v†9×  ŽOŽ×àà9rg*¬Î,r‹D݆4miu&)Iüµ³¢ !m_±Àù Ò†‰âcµÂõe }àù|Ü‹vL',(,ˆ.?å™M ‹‹r ;g-¾¤ÚàòM:ߥ6-, íØ™ëõ:U#¾|ªr¡ù±ŸÌj…sL’¤êV=`±|.`ƒ‚NòG~ÐmÑEÌò)K c\7üÌEP+L{€Ú>Å$Î×`¯ÎZáë^µ¾‘pV‘Àà»' i&üëúëŽÐ÷nÛ]iûŠ%ÏF‘6ŒKÔ ×—5´õÌ“L¥.ðÔY+Ì3›evÎZ|IµÁ$M:Ó¥6-/ mkŸñŸôwQ—I dcŠtæÇ¦êIrž$IÕ3¬}ÀbáÞÕ áÿNòµövèòY\zÚ@hp`6_ ´öZaòÔ¸…I¦†^÷†-9\.œœ.\¥Ê'è»òÌ´!Íäá÷€yÚ¶.½¾÷.®èBHÛW,y6Š´a\ž V¸¾¬¡qÇÆÜÿ6õ·dÀSa­0ÏlbL\”OØ9ßõ•pƒ©štŽK;m ZdÚÖÿDá`ýL÷ÈÕkkæÇ «N5I’ªg(`ÀbáÞÕ C)§÷¿~­º):Æ÷÷ÃÌ¡; ;y>Ÿo·Ûß]}<×ë5Œq™]ø§^ŸnØn„ï„ïǬM±Ò4-“Ô+RzíÏÛÎt×nº×Zu­04HHvnÿùûáñÃw¾¦z®<–I$Ð}#h8ÂK—Âÿ~}ïyä_ï^ -$o§èk‡Ã7»ó…ðã¯ýaߊ.„´}Å€,;t¡¯+ôspé>EÚ0.UP+\_ÖЫ‡Ÿ¥ozÝÅžÚj…Ùfcâ¢LÂÎY¯¯TLؤ‹Ýº¹L ZjßΡ ­ú÷v—WÚÕѱtdd¶~¾Èù±°µÏ“ç5Ãκpyñë´]S¶“$Iz†2,rè]Õ YÛ¹ýïä_ëm0ØuGi_Av¯í°¤ÅÜÃÜ1-Ÿò‡Ýh‹â›(EåÓ´|PÌp¶Ö½?á_Û>K¯Ãæì¦JN_y_¯[;ÖÉïu?d&‘@Ûg Í™"…Öû¼ãndVsÉ„èèFÆß??a,—ç…U_Ñ}>Ä.!e“šÁÔÙ‚Záú²†¯Ý{ýCØÙ=Ї¾£G|rÉZáZѲ‰aqQ°sîë+Õ6éµÂÅÑ‚£Ð˜vþºêf¯u`o­ï3_ùÏ…ïG®hÎÀŽF^¬kÊv’$IÏPÆ€EÚÞU­¡g׿¾ò©T~‚B3à~§I´=ÿÞkAõŽ08r¼kÛÐt}o¢#xIµÂLP÷ ˜:‚Þ‘·%礿îuüZ„ŽÄ$~›9Dm7Ë k™pªÿ6KÌÏ·µa¯§ÛÖ½éµBZ|¨ÖxáïM¡%Õ î+:·‹ÊŽ\ ã´ÁWF5Êœ³†¯“lã?f|ç¬VXX618.JvÎ}}%Ù`Ú&µV¸p ZpúõYÂÈVŠyv2¾†Ò¶µ^¹Cæóc»Ý®ïüXÛmH‹uMÙN’,ß33`‘ªwU+dÜÙ¥À—Q­°û&áîÞgÕsxór[83oж¢è Óÿ1Ìäu X!Rê{º¶Hñ_«›¢IÆ×¹rˆ'{M!~ §G8Ùbæ©.™¶;Bã7y‹ìà5åÖU+LÒW´IÈ8mð•Q­0Û¬¡£‡{2àuŠmOñ)<­4Y˜uËùg#㢄aç×W’ ¦mÒ™j…Ë¢eG¡ÏKöªz´uPÓn-¾»Ë|~lðëóò’O’,ß33`±|ïªVÈg—ß¿Y­€:à] !4z-âý¶$ûÜSú}ßÉõ«ñIœ˜a«q7Ƽ–¥˜Za&¨íS xêó¥ñ^¬‘Ke;EÓWã=„ñk§äðoü›©‹9µú®<ó«ñÞÔøP|ª[d×u!dÕW4þúà,ûG­bÓ_y­}šgÖÐÖŽKGNý©–‘MŒ‹† \_I6˜¶I縴“¢eG¡¶sÛÀn-ä×Ìõ}ÝaþC^òI’å{†b,î]Õ ™èìRàû7··%¶-#/Ä–a#—Ëe’Ѥ1C¾¶ÝÝ}3XÛDÁ˜Û¤Ë¨fr€æh–Æk!þ£•]+l¼Q-~éË>Bª}¸^¯ã—‰¹d"o'žêÙu]ùôm“ícP‘6…¦ ¾²{ObnYÃL}`ãthd'¯VXF61>.*fhÎ' MÛ¤ /í Ñâ£Ð w¦± 6¸›³5óc9ŸxYM’ îä’ –Ô ™âìRàË®VøÓþ¬6›ÍëUÓp#2p#Ö´Áa†±Ðà fr€æh–ÆÕs(‡å0Œ&¡Àz›qä}¤ñ—aäŒÓÞ¾¸– !Ÿ¾¢ñ|¼î“üŽ¢Ó_ÙÕ sËfêÛ–h[`g*©æŸMŒ‹Ô 'ß Záø@´ø(tÂi¬ ^SqLE)“³ñ¬+i~¬€I’Á=ƒ\’a'‰Z!] |9Ö þ·šô´—ùétp‹ÚçnÄkÚ4ÞA×7¶ÆÈÙŒ2j…™ 9š¥m÷ò¦hê æÇ<‹7ùßùˆ´)4mð•i­0“¬aÖ>°ñ™‹˜—þ¨VžM¤ ;s‹ÿ§Ý`Ú&- VXC:áÎL;#4xkæÇò?ñ HQ‹°Xøn< ¿¢XhØ39@f¯?k«Þï÷p‡ðoäߊ²ÈÆgä³ Ãï‘«µt|œ„kme~!dÒW„¼rŽóA¤ Óua«¬ÙÕ™5ÌÚ¦zH­†@´øl"aعØô`’ ¦mÒj…5D¡åÕ 3é0 Í…Í•1I2aIz ÷®j…”rz+ñº&ŒaÐl 2#}]òzÌÆ'ìˆwcüí[¥Nt')Ô D¿á*žð5CëÊ"¿~ð„Ž.\ìSÅÃ3u&?ù­µ•ù…sšvpþïå¤V¸š¬aÖ>pðâj…²‰„açbÓƒ©6˜°IK­…–W+4?¶ÌÑ)f’dð˰X¸wU+¤ 3\¡pÇã5°ßï{]ûÝ#KαPß‘2OÿÕ k›¢ ì‡o]Ydã{:Ú„€9ôo#CâÆ¨;ôŸ“t½–ŽYìX¨öý­ñ —H¦ëÂÔ W“5ÌÝ& MÕ Kª.v.6=˜jƒ ›´€Za Q¨Z¡Zaå“$ƒ7XƀŽ«Z!eä …{=ªóœ~÷yÿ,ksYE™ <³×l§hBü6_–±º,²m5þ˜8üîåréÏúÁs›ËùBȹ ͹͡¾$A­p5YCž]«Z¡l"aعØô` ¦jÒRk……E¡åÕ ÍÍ·KEN’ŒÙ` ÷®j…wž+Îâñx|½g¸c eE¨®nŠæ~¿ç0šg 4¾÷¡— G>¨V¨VXÛ, ¬ßþ'çÆ&|¨ñï#ˆyŸ»Z¡Zam³4°òô@­p5YCž]«Z¡l"aعØô`ò .ߤj…«ˆBÕ ÍU>I2~ƒ«°X¸wU+¤Ð³]¡p.ë]w,(”Iw¡V¸Š\àG­°Óóùü潋ˆ|gIµÂßùÉ‘ l·ÛîGÔ Õ k›¥•çj…«ÉòìZÕ e ÃÎâ³­„MªV¸Š+º’Z¡ù±1¿[ö$ÉT\é€Å€Z!åžð …si\Â÷ÅB&ºËË^s›¢9NwÇ XO¾¼ZáËãñ­ñu´Ž08ÕW+,£ ŸF‰´aº.L­p5YCžq»Z¡l"aØY|¶•°IK­…ªš«|’dÚ ®nÀbáN@­¢Ïy…ÂYt¬)Ô«ÿY~½ë éœ'º½A#Ã)špºvÜ&7ìd.µVø_¯×Aôº®c‘Æ»#ïQüº« Nòc¡VØ¡ñ¼ú]Á¦˜yWXsV V¸š¬aÖ>0ôÌI —ÕÖ KÊ&†Åg[ ›´€Za Qh%µÂLæÇÖ˜Ü?I2ßõ¸Š‹…;µBJ?í gIúö3E°}eH—=Ñ]FöšÕMÛœ[ÌZ^ëjœ¹;±Ð’¡Ñ¾.TÒ–YÌwɤšÆ\é\eÎ]hH»J𥕧j…«ÉfíÕ çÛrñÙD°³øl+a“–Z+,, -¯Vh~lÚß-~’d±ë1Ï‹…{WµB*8ó —ëOÚ~¾ññöåïEÉ0Î$ŠÈäå\e5E¶a­®q²¡Žõ7Îçsühûá^ÂF†¥9j… ûŠãñ8G*Ò†éº0µÂÕd ³ök”eþðÑZj…Åg ÃÎâ³­„MZ@­°†(´¼ZaÎóck¬?I’äzÌgÀbù(]­ N~…ÂÄYcbÆ…w;Ã@:“("“”gp•ÕÍv»ýü•ËåR^ã,¬í^Ķg /™17.vg: Lc®ëBÈ­¯h¬ðÆ<¨RÉõD®j…«Éfíg½bâ(µBÙD°³øl+a“P+¬! -¯Vh~lÚß-~’$áõ˜Ã€ÅòQºZ!uœÿ …³÷'¯¸m\ðgùçÖéñ!ÙäQĘ·ž Û™LPžÁUVS4s¼Ú@­ð¥×툗ÌÈ{_—ûˆ¹½S­0a_:í9ºP‘6PaÖ0k88ŽR+”M$ ;kŸå“ÐäMZ@­°†(´¼Z¡ù±i·øI’´×cò‹…{WµB`ªL°û™šÆ_Yx™…¶@:·Xh̲ƒw&‡”gp•­puÁ|žúö“ϱŒì£Ô ÓöEÞ„·³¬7k˜©¼^¯Ãª–?j…#6XL6±Š´tdû$IBÓ6iµÂ¢Ðòj…™t˜mcôê’»â'IÒ^É,î]Õ ¡¼+}ädõW+| GÛVº^øÖ©Æ}ùÊÂ1áä«% Þ™LP†ÁÕw© Þ+µÂ|N¼ÆKfä2¤÷ìEÞÛ™¤V˜êBȰ¯˜ü~ËÐ÷Š´:³†™zø1ï‡Ê­V8ßø;fËeg…ía>IhÚ&-£VX|Zd­0çù±‘±ZaaµÂ·° ¨B%Wú|÷#µÅ_£šÆ[yƒãñ¸dû4†d#—YÓ6¾NzL› Þ™LP†ÁÕà5!Ëg­AšäÄk[ºðá¿8æf†¹EVB†}EÛý–Æãа .ÒjÈæèáÛžžˆµæ'ßrÙÙDa{˜Oš¶I˨…Y+Ìy~läí¯Ö U+dÕ3`j…PꕾÝn'É"cb‰Èp¢-æ¹>Æß ùë:Bm!Ùà¶j\ã=¾3l|«õ°›ŸÏgã-…#ç">@W©t8îI"®Æs€Ú ^+ Ãy;I_טew¿³»ñ’üšïƃßÌ},²ºVÔWô½BCVÛÖ“‹´z²†É{øÆ§¨âoLXP˜oücËgÉÃÎiå“„¦mÒ2j…ÅG¡EÖ 3é0'¿ý5ÉôBñ“$ƒW'.cÀbáÞU­ʾÒC>aîß:ÆÄmîøp(d(¿ùΰ,2ë|û£ms ñáTµËŽÛÿâw&“ô“ÙüãòY“¼ÊyÀ^5^ƒÃâ´pÍŽ?ó¹…8´ÌÈ[çLºoìl»dÜŠÙÖ»Æ_zs‹¬.„<ûж‡V^÷u}ˆ&daÝó3"m ž¬aÚ¾mgâ?o‚Â|ãï[.8›HvN+Ÿ$4m“S+,; -µV˜I‡Ù¸f&™›pÞ’&IÆY]yö?íoæúM©Â%ü÷ 'XøNø~Ç+ÒêÌ&yÒ¡#Nî5R',(Ì7þδåR³‰ÂÎ å“„¦mÒbj…eG¡¥Ö Kšë¨{.Ü?I2~iÖUX,Ü»ªBUWz Â0ÚëuÉ_ï1ëu»NÛ ¹~s“øÑüv»5îXL Ð6½>Ë×4ìaÇÍ“:öæ A{÷‘ »Ú Ø™PniWG›$yëwÛé“MÄÀ«®þ猟èhk“˜Î­ã&Þ˜%ÝÂtdˆ½N°¹EnB†}ÅëtŠŸoi²†»·ßï#ßäÛýÄS¯–° 0ßø;Ó–KÍ&r;§•Iš¶IKª…\+Ìd~¬cÔ^x~lðï–=I2¾V¸ö‹$¹€ ¨êJadˆðÃ0"Š·Ñ?Œá›áŸbî1ûšJ4Æ0_w/9¯}{‹£ÂwÎçóøUô»÷á57òvÓ]ØŸ˜u9t†Ý;ŽBø¿üj„Ž…FöÌ9 Üæÿ;ª9ŸgË«BXõ:F“ïUÇ={!Bþ¼eôuEÏtÎäY+lìè{¹îs5òֻÃ>„xÛ×éî`û¾0hc‘Õ…g_ñÓ~ïe_m©™8¨$kˆ G_#ìß;v)t¡ÝûÓw¡Å´…™Æßù¶\d6‘IØ9¡L’дMZR­°à(´àZa&fÛsyËÏ þݲ'I¦ª®wÀ"m. V®ôø©ƒ¾…¯ãøb½PäÍ10¾3ìeuh‹sVw€rK»bøŸwð^uLì,|g^+£×Š%S]Â3²´iï*.„Ÿ,k…¯ô9æöÚŽ|ö•µ‰´š³†ùvfÀ›i 3¿³n¹¼l"Ÿ°sB9$¡i›´°Za©QhÙµÂLæÇ&Ïg—Oî ž$™¼V¸Æ‹õæÚ*ÉúC,1¬P8>3ª½a|g8É‚!¯Õ¦ê™Ó  Ó®^÷›Í]"éXG·—ãñ8ò.µV8 ž0½P(\ìXdu!üäZ+|]¤ÃÚ*ôä¿c«H¨9kÈjŽ+yAaŽñwî-–MdvN%‡$4m“–W+,2 -¾V˜ÉüØøÞàu_Pªä®àI’|j… …rµBX0¨…Þ{Ö•êÃøù–nÇcÂ;^ú®›=fzÿw…À©žC’ý¾Udž9áÊ0íœJÏ´Wá茹ÌoýɬR3À$Á_ƒû·îÅHç¿—9¹]ÙÖ ³þø^ôóó"m æ¬aŽýé»ôèTržèÜ[.,›È-ìœJò$4m“Y+,/ ­¡V˜ÃüØà*óßÛÒ&w¥N’ Û`yËô®j…Pª×;ǧMÿÃXÓ÷Z_…áfüöZ|آّo1þ+DPÿÖ„Ï¡ ߎÈä=s’”í-šÐ|{5É-£?믾ÎÒ1yÍÈÞæ[§x1ïŽÏáXdu!d^+üŽ_ïÂx‘CÇZòõúŒÆ¯qRNxT’5´ÝQ3,. ð˜!>‡‚Âäãï[.)›È0ìLÚM˜„&lÒ‚k…%E¡•Ô ×;?Î¥ßÛ’'wEN’ Þ`y ô®j…PCúß÷½ùõ¬÷Ün·ø½B ¿aÉà&Šüë¡>oHž¶3ŒŸy ffÍ‹>@9§]}'©æÞ«ølâ5k7í9“U$ðz>b@_7&™j»^zíFcÇ’üªYÅ…°ŠZá„;Ó÷Ùy€õf r¯á>D°ÉÙ<ÑŶ\F6‘mع|h7Sš¤I‹¯–…VU+¬|~lªö,l’düMæ… XÌÚ»ªBm3aÐ á}úwÿi¼·Ûmø§ð3á'Ç'×}ƒ¢0R¿vïspíX­ÂH7ù€õ;€¾Ýt÷jŠðOm7ÒÌцÃ>æÛú{û_ªS(áÊJˆÀÃÕñÙ¿-ZiÉkçw>sºpJ‡Ÿ2¬ËßõóA‰×¥þuÖ– =Æï…üvž|½•×…@ã[KB“j ’¬ák€ý ÝÇٯѻ@4áÈ^^6‘CØ9•L’Ð’šTÊJ;̶‡RGØü»h“$zWÛj…sOe@/!ÿºÞ€Q(“Ûj…sô®š€ÁWþq{' À@ ÀÁ¶Z!À½«fÀÈ Ðýo¯V – çóùsX9Z` `Ž`[­`ŽÞU³0Àý~oVn·›Æ؈B˜#ØV+˜£wÕ,uŽÇãqð+]Ú¦hv»¶Øš@ ÀLÁ¶Z!À½«f¨yDØn·§Ó)~ºæù|†Ÿo °ÝÎ °Ø¢Pæ ¶Õ æè]5 €!Øl6‡Ãát:Ýn··I›çó¾y>Ÿ÷û}Gt}<5, ;`ˆB˜/ØV+0•@žñöv»}>ŸÐ °D¡ä9jˆÞLe0S¼mŠÐ °D¡äŸ·Ûí|>ŸN§ÝïÜßßï÷ágÜ À…*ØV+€â©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…@›Ûív:‡Ãn·Ûl6o×uøNøþ~¿?s¹\î÷»€uQ+þz>Ÿ—Ëe¿ß»ØÃ/†_Ñ’ó@¯Y6K¢ýÉÿâãñ0¥ ¶ Û,@­ê2ôÃá0ÕU¿ßï¯×«V…<zÍâˆÀ°Y‚Íf3ù_<¦Ä6d›¨B žÏç„U·™„Óéä1CÈm ×, ÛÍ`í³Ó>Z’S ¢&2ÏÔ ¡l×ëuNàp8hjÈg ×, ÛÍ`í³Ó>Zx:L)ˆšÈ< P+„‚Íô8¡~2è5KÂvsD(`–àv»-ùçÑ&ɳ5(ÏóùÜív1÷ Çëõú9pûÏétÚï÷úX×@¯Y¶›#@³!•˜äo].©„¨ €Udj…PžívûuÕÐûý¿ÁëõÚý”¢6‡|zÍ’°Ýʘ%˜äÑÂÍfcJAÔÀZ²µB(IwQo·Û=a[~>Ÿm/ÑìÀÚ$Í€Y‚ -Œ|¨Ð ™dj…”züùªÅù|î¸ZO§Óø?ÑX1t¶k4 €4 †£mÕ‘É-ü\ê¤ížFg,,Ÿ¨Rß A-åÂûýÞq©^.— ÿÖãñøûJD'°öI³”š d¾ÙÁÛ~s¿ßþ+·Û-ò¯‚ I VH•3Uœ¨¯)œ¶Pøë÷C'°öI³ÈjÈ.ÚÁƇþ¿¼àï]…¿‚ Ÿ,@­*§Ê/v¼d’¥GÛ¼îvÎk4 @ÙÉ@VÌpl|èïUàë«qµ“WÙÑ ™dj…Ô:=Pø¹ºÙl¯Ðívë„ø i€âóä›Ê|ü|pØ£…Ÿ(þ.gj€L²µBªœ(¼\ØñPáívËyÏŸÏçõz=»Ýî­Üþ7|3üSøðcËïXhÕÃáð6g²ÝnÃ7Ã?õÚ¥Çãq>Ÿ÷ûýçÖÂ7Ã? ^ßÉGËÙý~?NŸçöï‰ó·÷{ž´]›áFßp²…>*´Ò«hœ¢|µX~,4Ú$gÔã™l{ËUŸç@…YA’ä?Kðú§Æ— ö}´°ñùÄß”dÖ!øoôòyÿäo,±üX¹@$ÿyÃ÷×~§( nX€µBÌ TS+l›`¶jÐ2n·Û~¿ïj¯{Ætb!l|]Kãâ®_§BÊÙvt>Ö˜ô³Ô6ù¸3rƒ1¿šî|>·=ê;íÁ“·Ïkž¤ãå§/÷šÅo ¬±Õ+ˆŒä_ÿ}a}c±L´¹Šˆ"UÃ$ÏÔ ©o> ðraã½»9?Tv8²ÆÔ˜ÍWP ‰êñxìµ?!« é[ÖÙkÊâå7÷ôÑfšPš»Vx:T¾Úšzáö9ŸÏcâpyÆÜò=aR­´V˜ª·Ìí<˜<=˜õ×8Kð7Ìsÿaˆ9»S’ÉC» D–™ï®öŒa䧈ٷYãð×ÛÖŸY&ÚÌ<¢H۰ɳµBê› (¼VØ6½Ÿç› ;–KM[P»^¯ƒ÷ç-]}>Ÿcž™š¼¦¶ê¶¢ZáívTøv×ÊTå´ÑKß[k:>EßÕ<‹²ª&(rhX€äY€XˆÊæÊ¯~-ñdbòŒlª ðI§zÝ2=IÚûyvÍmµÂIîi="·ðÇù™çžê僓¯½Áºj…É{ËÜÎs€ùR…Á?VXîðö3Ã-ü\íäsµ¯7~ ìŽ^¶Ûík`ú[Ù|MݱDø§øçÔÚNœ0¨èÞZø˜¯Àéíc¾‚´˜W,mfQäӰɳµBjJý˯¶Í*gõàFGF¿Ûí¾>ÿØýÒùñ÷÷]ƒ¨×#?ƒ·Ö÷–úÑÖX+m˼ôÊè_ÚnŽßTÇ}¤½*m¹á´[ëÕD¥~´µÔ 7›MßEÚ^fÖ¶¿> Pø;‡ó;©8w¯Òv^ÅÔò¬æÓ[ævžr_Ë× ×h{ÍAèÕ#‡¿1å$1óK[¹0ò~ËÈ%8†ÅTmñÀà§ò3¬&‰(rkX€Eó2µBäýµÖ óyTã- ƒ—Hm|*þåŒ#ïhýº'Óníx<Žìð øh«¨k™¶«£×CÁƒ?Nc×ÑëM=m3o½õ¬ñ±‚øŽ%ÏZa>½enç9 gðµ|­ð§åÑÂÆÂÜg\ÑÖÏ(×·ïûúòîO7f °Gü¸Ùk­õ¢ß5Fι5,À¢y™Z!òþÒóþ¶ 3æm h¼ÇxÌêsmoX‹¼¡tÂN¬qzap²9~k¥~´UÔ ïLcÁkãÞv/}>÷|ÕxoùÈ56ç ½V×[ævžr_Ij…‘6Êmã×à!¦ñ¡Â1£IÛ@³†ÆTKp|j{ömLr—[­0ID‘aÃ,š—©"ﯵV˜ÉY2þq¹È$1òÑ› ÛªqV$þ ǘ­c­ìZaãB‘ ÷ÆÛò×U»i›ßK>·3xËYõ–¹ç€œÁW’Za[ÌðöháçÖñ¨×à!¦qPy›Óà§}\îk<0x‘ÌäÑo>E† °h^¦Vˆ¼_­0¿^hä3cÞ#?wA-áÖJýhe× S÷2ÞWX­0«Þ2·ó3øJU+lJþÞBÖø%³Á#Âç’ão;i|Ö,¦~4Ó¸Öv+ÔÈx ˜Záàˆ"φH>¦Vˆ¼_­p㟕‹Ÿ+ˆ¼¯x¶S²œck¥~4µÂÉ÷§mÒ2‚œäs;öœ[o©VÈÔ u?Zøù¯Ýµ¶a#Bc<9øM…Ý#TÌJ 3k ’Ô ólX€äÓhj…ÈûÕ —Ñø¢º‘Kê½4®Aó^ûiÛ*«­•úÑÊ®&ÙŸÆå›"Wñ]Ìóù¼ý'ìíé?»ÿÙn·ãc’Üj…¹õ–¹ç€œÁWÂZaÇ‹ÿ©ûY­a#B〓¼gyØÂû3k3ÅÅÔ s ´„7Àšò2µBL<,—vçu™ç°o1ï7&“õ‚Ô ë™a(i/̘Rû¬î÷ûù|ûÖ] œ*&É­V˜[o©VÔ–B|ý§âg ºe·Û5–ê>ïH™iˆi¼õ%áÀ=Ó¸ÖØÎããµÂ< y VHщ¾Zá÷·„,¦mVaü–3Y/H­°ž†’ög¾ s€ûý~8×ɬªV˜[o©VT•?ôúRg †&^7á@™pŠcÉZáøx@­0φHž¨Rn¢_c¹°-qN5óÿWãüÿÈ7È¿4®w´üzAj…õÌ0”´?îÝï÷…û‡ÐGÍ1ï—|ngØ–së-Õ zò‡Á?VØ,Áà¤c™U>*©Ît ¨Zaž < P+¤Ü\_­0¯Zá¬=Fy½Za=3 %íOò¡üù|Î7ã—¼wÊjÊ1Ÿ“D ä™T˜ç@™Ï¸¦V¸®óGx¬= P+¤ÄŒ¿ÞZáóùl»6Çz'"V‘'ªÖ3ÃPÒþ¤ÊÛ‚~ÝÛp¹\Bo¶Þó*Ï)¬|N$OÚd#ùÏDþnÛ£…ñ¯JW+ì»Ù¾RVÑoεÂä <‚U+¤¸Œ¿öraÛ«CÎçsÚk¼ñ8ò‰¡nÇcØ£”j…«ÛšZáä¿>߅١㮆Ãá0l®¦¤Zan½¥Z!PjÚ|SÙÎŒ‰ð{­g2a­'á‘3k»ÝnL6Ãè7“ˆ"φHÁªR\Ò_{­ðx<æ¹ éLIÙOËÊ«1k©®nkj…“ÿú|f‡¶ÇƼYµ¤Zan½¥Z!PdÎÕs›%ˆÿõÏ…Â(¿Þ2«q­ñc^¯×´{[j­0yÃ$`Õ ).ﯽVx¿ßÛ.Ï„tp8fzÚ1ldXÑA­pu[S+œü×÷ûýò!7ö#ïg(©V˜[o©V”—3ä¹Í|f âým­€¾£ù°¿Þ½œN§•¶a›Æ[@ÇLµÂ< y«VHYy¿Záÿ׸~]ä£vói|=٘Lj~5ΫǤ{j…«ÛšZáä¿>ß…Ù¡q©ä^!d~^Ürn½¥Z!PXÎùfs˜%Èü¯7”ûý¾°6l¼Ãg|B§V˜gÃ$`Õ )(ïW+ìJŸ“?ZظöÝ$+£6ÖFc>©Záê¶Öø»cÞã¦VØxaÎ=šÏñš¡’j…¹õ–j…@I9ÃZ¶œv– ó¿Þ½ {åq¶mض\ÌÈ©V˜gÃ$`Õ ¡Ã†HÁªB‘:-Ïd›°Ùî~à÷¦š“¿8æ‘(µÂÕmm·ÛM[þV+ì¸0ç[²8·5H¯c<ß^eÕ[ªÔ6Kÿ_ã×òwE.߆mk {‚2Ä6mïµ_æÃæQdذÉ#XµB(UãKÛg*>ŸÏ¿7÷vüdc5ø ¦ÆÊQü¡j…«ÛZã³Ãî'mãÅÖ Û~qü’Å÷û}»ÝF^¹c:¥¶ÊÚ˜ž$t¡3…^‘¿›Oo©VPÛ,Á*þz[ufª4§-ŒY¸ Gð¾óñx´Å½uÖ 3lX€äY€Z!¬-šö¶ÛÏYú^?¿S+\ÝÖÚî!ïûXÇÝ¿uÖ ÛV-\™z>Ÿ¿ÛŒ¼x‡ý­ÇãѶ Y|Ó…ÎpÚu¨&9 ùô–j…µÍ¬â¯· ”ãË…ÝaÌÂm¢Ö¶vòëm]÷û½»˜Um­0·†Hž¨BÁBŽÓQ ¶Ûí˜uöBÞ¸ýîßjÛ¥^àm©Y¯é}µÂÕm­mqÝø'à:'¬¹VØÑW„^¢ïÓ…o=Ãç´Õ|{ÍìýÇy(Û&Ç?˜0ò€fÒ[ªÔ6K°–¿Þqcä°Å$?£‹Ú°ñ¦¦¿÷†Èêï*å÷û=|'|¿ñMßj…y6,@ò,@­ÊÖö.†·Çy"_ð÷»Í" ~,«ãΘÚeøŽi°o©æFÔ —ÙZ[áãkI+dúÝ5× »/ÌȪYhÿÆû¸­‰™Ù‹©öúìmïòëÛ¥L~@3é-Õ j›%XË_ïÁ_wÅÄßöÍÆ 3‡6 ‘O|qjîiœ’j…Y5,@ò,@,Å‹)þ>«ê)¿Õ\ÂÂ7O§SÇZ½úîw)†”-üÀÛ4xøßðÍîl®ïûÅÔ ×¸µî’Öñx|;_gNä©[s­ðçÛÍÕ›Íæóþêðß_;‡Æ¿Õ±nXã ÇtÖCÙQS c÷§žõ É¡·T+¨m–`E½;2|Ëqþþâk4âø÷Í-Іñ ]·¶»­–9dFù4,@ò,@­j’ ¹ï™ìÛļߡ—ïðR+\éÖ¦:yÚªN §×’ïÏäfÇt¿Pµ—¶þmÚ™Æ=Þ$gHòÞR­ ¶Y‚uýõŽ–Ék–iÃÐu¿]¢[øÝWµT­0φHž¨B%žÏgäŒc„\)þq• 'À Ô W»µIÖ z­u©V8ë…Ù½__¨)ôlm/²ìõÁ{=±¸d­0yo©VPÛ,Áêþú˜{~ÖR+|ÅNÕ÷þ.»¡V˜gÃ$ÏÔ ¡*!ždrþSß—¾t/¯)þE$³¦«j…KnmÌzAÛíö÷Mmj…&¼??ô èñxŒ)ûþÞÈý“´½Ì’°·T+¨m–`=.YƤjÃî÷Þ#ôwõµÂ\ y Vº\..Iz<+/„ߜׇ_|ËÑf—j… oíù|8sÞž{U+l®¬‘ϲ…_{1PÛq#÷TŸ}ØIµØ’ª·T+¨m–`½ýz½Ž¿1òõêäøq3I†Ý{½Çù-­ ;ý׫ŸßÞAß¶·a ˲UD© y VÕºßï!ÙV4 ¿õJ”¦Ú™ÛíÖ«^~8¦ ±dv©V˜dk—Ë%rFè­´”a;g¸?Ç#\齿ÜBçp>Ÿ§P:ÄÏì½:žüP;©®¦ê-Õ j›%Xû_߀[ž^%Â÷@®k\üøäø[vD1²a’gj…Àãñ¸^¯§Ói¿ß‡Œæs’üueø×ð3!õî[ˆ¶ö$$韻ñõNNjö{Ú´3šh¤ûý~>Ÿ‡Ãçsm¿í|¹\F^›á¯„NæóO¼úŸ°ca¼3o}Ñv»}}Þ°?ãïXÐ[À|ÂHýÀ|æ8¿czˆaƬU²®i\%Æ©¢a€ª¨T¨ñÔoKô£a€â©T¨qiÖ%×mа9P+¨9 ð£VPŸóùü9s8´Œ†j£VP•ûýÞ8s»Ý4ކj£V°"ÿüóÏñxüþ»¶zÖn·Ó°¨3 R+X‹ßù–ív{:âk[Ïç3ü|Ûìgß4,Py¤V¿Ï‰—Ífs8N§Óív{«p=ŸÏðÍóù¼ßï;¦nŽÇ£†Õ°€(H­ s“ÏÛl·Ûçó©a5, R+Èœz–†È6 R+˜Õ„36‡ÃA=Kâ µB€µ˜d®f³ÙÜn7©aÔ Väz½‡ív;l–f¿ß‡-hF ð¢V°FÏçóv»ÏçÓé´ûOãcnáûûý>üŒçÝ4,À'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤VuR+€:©@Ô  Nj…P'µB¨“Z!ÔI­ê¤Vü¿öîîÆY\ð)aZ …ÔÒBZàvïhÒ5Ð-ÐÂk£¢ þCìçÑ^ì$àØé{e¤IVi’@šd…&Y!¤IVi’@šd…&Y!¤IVi’@šd…&Y!¤IVi’@šd…IÍðº€Y!@R3¼nàAV†¶±ŸÔm ™<È ÀÐ6ö“º 4€yÚ)ý¸§¬Cµn»ÊxîÌ&/C[V(+üêÊxîÌ&/C[V(+üêÊxîÌ&/C[V(+üêÊxîÌ&/ÞÎ Ú«uG®ŒçÀl²Bà휠½Z§«¢$+$2ÿüïŸÇ<8²9A{µNWDIVHdž½·¡Þ¼#e…Ú«uº ²B"ó'ÔÎõf&+Ô^­ÓÕÑ™×\o Ú›zŒ¬P{µNWÄDVH|Ƨ{“H!(ü•Ê u5@Jd…DidÆ7þÛD‚Âß:mÛÞn·ëõz:~~~ž«þ †¯Âá°/jo¨mUUyžßÛtþîý«pXh`Ó4\ÍÃVfÞE,Ëòr¹ü¹|Y–…ÃW“îÉp}‹¢8ŸÏ¯¥…ÃW«ÜÃèlνûp­–¬XIúF~•NPøûÑ@§ªªóù<~b ‡S>;é 7'ÏóI-zl¦&Pǹš{võ’fŽ9·išËå2¦’኿½^ášö…Å„]=1¼'žáÖßó¡3ð-d…DìmÞ7æó¤‚ÂߥKMÓŒLO:×âMÊS>`ÍS–å]̓tõ*]Ô¶íõzTÏŸŸŸº®;«ªªI!Ý«ÿ*õg­î¤hÞCˆ’¬¸ §~o?L-(üýDºT–åžiÚ7f…³“Yá’.ºÝn³kû'. ×nÉ Óåqa¨Àì8~ ]@d…Do ûþ$Á ðw÷tiäÖŽ#wJÜÒÛsvͲlj\(+œWÉÐÏËïÌÇrתª>Ò…s°Ê8&Y!)èKþL3(üÝ7]Z1(~oV8>ýÈÕʞ׫†*½½ÃWÞ]Ñ’Aáì9aõ_ˆ0N§Ós€Ò)0ÂLZô´]{Ÿ™<Ï'Õªªª¾¤fÒú²CMÈ;tõFYáÛ]7'­]Úù|žÔ-mÛö-3Ê~ýpK{¦@d…lgÞR>ÿ}6»Üg÷EoW>ëÛÝ1~„öÞ׎ÍÞ­q è_¦¬páC0\Á‘ èÎçóÛGa¸c–6i5_¨üòÄú–@d…lGº'+ìT–åòã®oÝÓø¢Ž»\.eYÖu½u§É w®Õa³BO ¬(LúÆ›N\(+\X¥<Ï––eÙ¶ín&+ܹV²BàëÈ ‰ÏøŒo҉ą›áÎw#—× kš¦3›Û¹½mÛ¬%œš.¯¡¬pçZ6+ Ä£è$+$2“Ò½©Ç¤n:„O§Ókáó- y-9üÜÎí-˲/(üÈ‘î\«#”¶Ý(¢$+$2“r½‡É —¸\.¯…E±¼äPȼ„nÝöv6pÌòÆj(+ܹVG(í|>¿ž•ç¹§ÐIVHd&…zóŽ”ÎÖù.¿%kî:Cº1ùȺíͲ쵴²,?uEd…;×ê¥u޲óùìét’™I‰ÞvG6'¬UxçN¡ —ÝÝu¾ q̾‹;„; _'+ü¢.:Bi£,˜÷ºL z²Bà휰uùu]/)3œ>»ÚGŽŠ–—)+üê`Ý»Ú6¤@'Y!ðvNX±üΗ©-܆´sÒ‘›.®»PV¸[W³‹RZç(³´è$+ÞÎ +–_–庙Q8±³ÀÛí6æôÙ›—Žï={nÑÕÇ좃”nþÎs¯×«)øCV¼Öý‰ÎÌèt:Í+-œ¸äˆ§E±beʲœÝW}Ñêq®æÂΙÝÕk53ʬ°o”-¼ŸÕue™ " +ÞÎ ëþD_þ5c'ÒÎÝG'"×ëuIÔ8¦>óbЦiúv’üÒ¬pÝ®^«™±f…}£ly\ضmžçî Y!ðvNXýWú=MŠ û‚ÂIñS_¤2/OéÛûqRiÏYLLYáº]½V3cÍ {rÞ…lF}^ïL&D@V¼Vÿ•ªªú¦‹,ËÞ¾Ã.0ƒÔu=¾&}¯;œZÎC_ :&“~oV¸zW¯Ò̈³Â¿GêãSÚ0â:£y&D@V¼¶ø¡¢(&,ËÂBÃðgø0|5pâŒ÷ß ÄŽ—Ëåv»5Mó88ü¨Fžç÷ B_KØûñçççz½þ)°mÛ{»v#+\½«WifÄYáï`(ÿÜóá¦ý3Öîn˾ջî Y!ðvNØè·†cˆf¼ñðw\ž2irHĦê Fx5?ÒÕË›wVø;^{¸¿²B`Äœ°ÝÏ­Î ï&­é{Û3mÛöíD:Éõz EÅ”®ÞÕË›}Vø»,¢õp€èÉ ·s¦¿8¼éHãß¼Ö©mÛá­M§NnMÓÌ+ð±[écOÈȲÂÕ»úWV8B¸!W\îz 4a@d…ÀÛ9aë­ëzvN|~ÉÝlmÛΨÃpóÖÐåyÎ]åŠsB^½«e…#Ýn·å+^ï¯Ý\eÐG +ÞÎ ûütUU“µpðcñÝZʲœ¦¼-p|:+Šâ9%\~EŽ> stream xÚìÖ! ðù7½i8:I$5€'àÈcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–F Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXK#à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,¥€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÒÀcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcià±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±4ðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–F Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXK#à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,¥€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÒÀcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcià±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±€µ[Ç4 Âü»^‚„Ý­.Àcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÒÀcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcià±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±4ðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇÀcx,€ÇðX à±< Àcx,å±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±<€ÇðX à±< Àcx,<€ÇðXx,à±<–ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðXx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcà±<€ÇÀcx,€ÇðX à±<€ÇòX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇðX Àcx,å±<€ÇðX Àcx,€ÇðX à±< Àcx,<€ÇðXËcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðXx,à±ðX Àcx,à±<€ÇòX Àcx,<€ÇðXx,à±ðX Àcà±<€ÇÀcx,à±<€ÇðXËcx,à±ðX Àcà±<€ÇÀcx,€ÇðX Àcy,à±<–ÇðX Àcy,à±< Àcx,<€ÇðX|@cºLN endstream endobj 265 0 obj << /Length 267 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xÚ}’OHQÇ¿³%B¬e&RðN¶Wí`ŒÝõoʶ¬k¦²Î¾ÙÞÌn%Bˆ.AÖ1ºXÑI:†‚b]"è(‚—í73»îˆÚƒ7ï3¿ÿ¿ß{@](mšz€yÃÉþ(»;>Áê7P‡A+­Xf$‘v™lqdí}…䜛áãõÿ] ‚U€Æ¬ÇמöxÀáû¶iO:¬äÒb“¸M¤’1âWÄg³>žöq†[ ñ2ñMÅ'"()Y'æ±ld4ƒä—‰»2–’'&ßÀSg^™öÐ}8õ¹&›°€åwÀ¥Öš,Ô \V:k²Ý¤;©iÝR;;\‘Œu?ÊåÝV þ°ÿ¼\þûº\ÞC9¾u¥(J•IÒÀëÃ]ýÜàBS˜s_ QP5ûFz¼Úë׋Gõ%«t{3qW°D÷0vz ¼ü \}\ø$€Ôu¡ºmþÀÍ+˜…–ÍÙ¬C–;XØ9:Y„^g±BÞ,Ú\°ACioci]g®©Å·¸(ñL;òz±Úï9ÚAnŒŽÐIó ¨Üê­°4“I÷ÐÝ x#Ã{zwA¼¨j}ƒÎ…Ðþ¤Š¾Q¥óš=˜ò8Ðmèñá Ã(Äo{1±cÚÑd5¾Ué­ÊgÒ·t¶üÆlaȱi"ßÐ\.5æ±”šËÅâ^Å8tph0èk€!‰~D† TÒhd¡‘”»6‚ØÂì±–:>f¤ß&Ÿm×çŠäíxÝA4Ž…¶ƒLþ&ÿ–·ä%ù­ük±¥ªiÄ”¦¬?ûCqÌÕ¸m¥&/¾By#¤Õ‘%iþ 'ËW©¯:ÕXl©Errð'ñ=_—Ü—)Œi7Ò¬›©äê,úF|ÙNšٮͯ6×rm^™Ü ®ÍšUáHWü «Ãÿ5;¿?ÿͰh endstream endobj 266 0 obj 9292 endobj 267 0 obj 706 endobj 220 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/cluster-fixed-proc.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 268 0 R /BBox [0 0 2401 2401] /Resources << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 269 0 R >>>> /Length 52 /Filter /FlateDecode >> stream xÚ+TT(TÐH-JN-()MÌQ(Ê ™*!œ‘œ« ï™k¨à’Ô®ØM endstream endobj 268 0 obj << /CreationDate (D:20070215234710-08'00') /ModDate (D:20070215234710-08'00') /Producer (Mac OS X 10.4.8 Quartz PDFContext) >> endobj 269 0 obj << /Length 270 0 R /Type /XObject /Subtype /Image /Width 2401 /Height 2401 /ColorSpace 271 0 R /Interpolate true /SMask 272 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÝÙqë8×(ÐÁ)8Åàœ‚SÐë}S JA)(¥ ”¹¨Oõ»\Hs‰Mb­§n›f`“àÿÀÞüb"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦p*w¿ßO§ÓÇÇÇápøuaoooé‡éŸŽÇãù|®u@ ~Kû¦k øàMˆÀܰÖÅ\¯××¥Î9SÎââ·$°oº6¼ À†úÙɱ^Øn[*Y¢¥s©lz<§œÅh±¡®€]Û´‰êó=Çäùžãý~—¹oB„°¡~Vˆh°-•,ÑÒ¹H6Ýn····‚=]ñZl¨+f×VjêšníëëK¬ îàMˆ6ÔÏ Û(ש<Ô >÷÷÷‚=]ñ¢R( ê ÛµŸÃ‡Ûí&Ç5ï@•Ú-Dêg…}Ïô5V›HçùÙ4aÏ´þ? *…²!Ñ ®°]ÛB3Ù¯¯/™®y¶Ø€k.àŸ!`¦oE¢™ò0óϯ×kW/öþþ~:Ò/<ïß¿Ýn—Ëåx<‡ì‰Š•Bã#Ñ ®È]Ûr“Ùtk?o Í;°•\sB„€™¾‰FÊÃÌ?®^¾º\.Ón§øQ)4> êŠÜµ-:Ÿ%Ô¼mÀ5yø­×M!¥ÒyΟßï÷ìŸ_¯×i÷Rü€¨‰uïÚ&ÔýtGéâO§Óçç§(¡æU»…ÀðÐÔ <¬sêóùüzêÏÏÏ8D¥ÐHJ4¨+x×6¿î§|{{ó]BÍ;¡v €á7 ©AyXçÔÙ׿l›Vü€¨I‰uïÚJÕýãñسÂl7Í;°Ní"Ão@Sƒò°Î©³Wš³ŸXñ¢Rh$%Ô¼k+X÷³ïK>¥DP4ïÀ µ[ˆ ¿M ÊÃFO­l£‘”h rm÷ò¾¾¾º™ï÷»Â K×n!B0ü45(µN=ó5ï ‘”h°ûʪk+[÷G×"óétR4ïÀÒµ[ˆ ¿M ÊC­S ¢‘”hÀ†º¶âu?ûíÅäããCaмK×n!B0ü.èñxœÏç4Ëûõ÷÷÷ôÃôO£>¢q¿ßO§Sš¾-ý0ýÓқϤãgoçy é‡Çãñr¹ÄÿèUºÂtéjÓ5§+]sH¾¾¾R’Þn·®']L:ݯT}{{{&éÒ×0[Ó-?s'%¯¬I •.FS³Å4¯Þð>oçÙöþj-…ï4’4’K÷¶£måV7•ÏëõšÎøLì—ݾ‡@Ï"½Ð¨²ú¬bŽ6þœ/Ïâô« ?ëÔ³—JÆÖB„)éŠ|ްn‘[¹X£JÕn!Bhaø=ó,Cþü~¿w=ÿùKšµý93:ŸÏ]‹9¿¤“_Òy®·¿Î+û/ãz½,`éª>>>ÆŽè&D`Ë’”øK¬®™­C$]RJç_k=§âàMbõ4_ßð:õ¬V© ¬Ö)/ŠË0`§0-ò’ k$l$Wëm£µºÏ˜à„aϯv²Ôþjc°€£Ùº÷^w,~yB2n«-*~êԑͼ£ºEnB²è8AtZ^"€€t´³ôÿùãñèùÊ|ÖÛÛ[×£›iº4j5ãé{Ý{¾t¨ YÙé^œçBSJ ³™ÀþYH†2~Î[K¥çúÙúg¥KSõ K—ßy"\?Í×4­N}/ ¿ß*ÂÀ rmɦÉ•{Ûh­nÁ 9°\y j4[ýÞ ºßï“ï%ýáügíÚ ö¤@ð"·f!Ytœ :-¯@ @À:ÚYzþ¼k[˜!~-=9~Ϧ ˜?Á|½¯*&,ÊÍàõüáétš|êžhrðlíIëõ:yánø•®Ÿæ«I·Sw*×Hˆ°b§ðç/ÒÉvÉõ{Ûh­nÙ<)[ g4[÷ÞËJ3‹5cÍB„Óî¨n‘[¹,:N€–W … `í,Ù?ŸóË·ï''‹¬xÏYÆïÙߦJ¼rŽùùR*DXjjòÓì³µ+%‹,e I!ÂõÓ|C‹–B„ÞoÝN¡ç‚Ǿ¹¯‘l¡‘¬ÕÛFku‹·Šïïïc/£Ê,HÃU÷ÞÃŽ¥nø,D8áŽê¹õ É¢ãÑhyRˆvÐÑÎÒµl2äðü}©ç?Ç~Ñ~¡•ùñÊ9Ænúºhˆpο Ûæ²µ«ˆ–zµ¡©Õï­¤ù –‹ †êº.8dl»ª‘l¡‘¬ÕÛFku—hG-Ý׃E¨Ñuャâ§!¥¨ñáý~ïjÜb¹*…dÑq‚è´¼)D;èhg>H“µËåòó« ×ëõt:õLUz–’&-ýþØÄé_QyOgLçýùyº¤t¢ty=W’þiý׎ú_ÆLsÏtÙ¿V{Òÿ¦¿:=»¼–-$éDçóùç÷>žéÙ?×NW¸­l• Ï‹• ~0Eˆpý4_Z©N…3U“_¥:ýwúIªqéÖJ}´îßÞC„:…®7zöt›É¥vVèm£µº]§~¶¿Žölÿ,ÒÃ/£â¬z®{ïeõoØ›¦-¿Jò÷ô¤?8ž~ai£Ø®OWt=ÿY·ÈÕ*$‹ŽD åH!BØAG;Ë1ÀŸ›zØròÑÆ>sþx<º^‡øùžIâØ¨Ö|]‹<éJ®Ù¦éóëC¹E Iº¶”Vý—‘¼gÚ>$;âdëržÕ1é_»n¤ÔÃÆ{j£¥yY©TwUðôóÛÁ¥ ÞÓW̦8ÝhNaéºFrgd­Þ6l&¦¦óŽzu·ç»]7©¨8«^£+Þ{Y=‘Ö!)™~¡'Tä]ò½N`»^ ì*~‹\B²ô8Áš?4µ)D;›a-q–?ß8ëYš0³ë:Ú¨w÷º&¤£>¿Ò3U\óE®çi§myšRà{¹i~!-í a _*Œ­î&4°lô†F•.!ÂõÓ¼¬®R}8Æ^Uú}!ÂਭÉ^ß•ø~u´+)4’;k$kõ¶31•íÉ;½÷ÚþÉèêÕ­b!""O¤3~ _T)îz½ÎÿþΨo‚. V¿×¿G!Âv²IˆP#)D¸éá¨ø` ‡çƃÉétºþŸ"÷µÚ,àhvýñgüéX;!Â*WR·ºULO!B òt@:@Àá·a›³žóùž7§½‰¶Pa(>ÛÐhv¹ñgÙ·P¾súçg#×[o‘â7˜Å‹\´B"D¬ÝB„РKˆð—ìlñû4íx>mûùùùç].²EX´ •­V¿×¿Í]†*ÕB„Á;……ÊžF²ñF²To*Ó0¦+>¼ñŸ?Ûîh¶àø3rWªrmqÛHu›VH„‚µ[ˆZ˜a þ’Ý‚fµg•cJóÓžyN§Óú%9»uXO6…ÊV«ßëߦá&ŠSÌlŠÖ),Tö4’7’¥zÛP™øùù9í-­P…aÚl£Ù™ãÏ"²¥¨È©ÓA¦Å¯…£¹h…Dˆ(X»… …–á/ÙÕ°4[T »žÆ¯ò,kvþž®pÙjõ{ýÛÜeˆðëëk‰`!ÂàÂBeO#Ùx#Yª· •‰ïïïÓ.>`a;ÛÓhvòø³ˆlJÎyõ[6®TkÿÍM`ãW·Š…Dˆ(X»… …–á/Ù]VÛÎ(¸iä/Q’Ç~d'T¶Zý^ÿ6w"Ì>[>Xˆ0x§°PÙÓH6ÞH–êmCeâßã«XFÁv6š]â…Ð9mc‘Sg·µ²€á&ª[ÅB"D¬ÝB„РKˆpà6¾×èÓív+µF7ç2.—Ëëßßß·’­V¿×¿Í]†»V€·Û)„ͦPÂreO#Ùr#Y°·“‰[È5Ç`{ÍN.Z³Ò%Õº#!­T·Z…Dˆ(X»… …–á«®Px‘ð_Œ5ºi_؉“­›XýžùžE´tÛeˆðß2KRB„Á;…åÊžF²åF²`o'w"{öf+¦|6%gn#™}Imà>B„[©nµ I•aÀ>(R»… …–á«ìcóÉ××—bY}®ëÍ©?g¦q²5Úê÷ä­®6”n{ .±$%D¼SX®ìi$[n$ ö¶q2qgŽ=ûÎF³S¾ëËt“ËRúÃìS–mh²ÑË[èFB’¥³`+}"P¤Ý"€fXB„Ãç>IšIöÛí6p«®RS¹"·ìŽ7‡ÃaÍlMé6ù¹ë Ùmõ;åà럟N§=5‰{ _’ê: lŠÓ),Zö4’Í6’e{Û ™˜Í¸9…yZ Yq V½F×–H¼€lÉþé:! U·*…dé,ØJŸiÀ… …–á¨U ùë*Çãxêqô‚ÙšÝrgø“«A²5ÚêwÊÁ9KU›h÷"캒 KR© wíJ'DªSX´ìi$›m$Ëö¶A21{ÓVìÓðir Yq V½F×Ô•’^Ûïª\ÃsDˆp[ÕmýB²tl¥OŠ4àB„РKˆ°Kö ÉQâéYN©¸3ÕäYsöyÚ!>+ò–SJ½®ùò¨©w„l¶úݵ¦Qê-ƒÕŽC„ÙU¬±õ¢gYˆ0Z§PåíävÉZ½m„LìÚisTƽà9û®?«[£ëÞ{Y]¯dŽª]•kTxEˆ0l‘ RH–΂­ô‰@‘\ˆZ˜a véúÄ÷4mø<èz½f§{ËÒápH×ÿx<¦-z¿+ ?>>~u%%`פ;xq²5ÚêwOš¬¼þ¶Ümî8D˜ÊWö¥êùçâX*ð=•Kˆ0`§°tÙÓH¬ÕÛÉÄ®Û"\ V·F×½÷²º¾Î™¼¿¿ÿù‚mú…žpí¨–Dˆ0l‘ RH*ŽBõ‰@©‘¤!ì~†%D8m¢÷ó¹Ð4ñü5éKS§ô“ÓéÔõ hõùòÏë×××ë-¤9rúIúyÿ- y:wH¦´J§û9^@š§g?‡4aÓ³8Ù0Ò³"‘nör¹üÌèg:¤¬y‰&qÇ!ÂÝoÊtåàívû³j †íV({É=5’µzÛ ™ØóŠôÛÛ[ÿüÊ»gj¤TêÙVt~ˆpÍ1XÝ]ýÞËJIÑGÏúõ+ñÓû+ר¸ F.r É Y°‰>(>’"€½Î°„'/.mht´ÐõÏù"Æš-[®~Y?ŒYŒk5§Ñæ;£â}‹æòz§°NÙÓH¬ÕÛÆÉÄž¥ì±ºÖð#ÁêÖèhÿ€½í„Õ /rÕ IG‰D`+B„РKˆpÑIÐŽ— ‡O–c.UÌÖ˜qQoLœïô?>põ»kÛRÙ§SX­ìi$÷ÑHÖêmãdbjÖ†l§ü§¯¯¯i-dL©R£w|*šü'D¸…êV·¬“ñûD ÈHR#ñgXB„CÜï÷‚O¡?­ù•"‹c? üªÑrCÍ û‹ÆÉÖ˜«ßÇcZŒiMb !”ƒ“—kþûñe.!ÂàšeO#¹ƒF²Vo*SIžóED}§Ã„˨;«[£ãÜ{Yý›I.ë"ÜD‘«XHÖÉ‚ø}"a$©€M̰„‡K“ÄùÏç·oÖÿ„Jºø9ჟϲþü†ÑäŒHsÞi‰™îbìDËÖ°q”°7Ñ$¶"|:xo·[„NasÙT¥SX¿ìi$7ÝHÖêm£eâä‡(R£ú3¦]FÅ1XõêÞ JýæäxkúÃ9!­¹Z…dµ,Þ'EZ!Bha†%D8Öõz°{Ìs-åç:|i*w>Ÿ'LœçD6{2bÔŤd/òò`õl »ž¼‰&±á¿ÿ½'2°0§Ú÷Z§„ƒw µÊžFrÓäú½mÌLK¿v:^s.£Ê,HÃêÞË&㨛Êv»{Ĺ¼8EnýB²r„í"­‡!@ÿŒït:}~~‡×™Ñûû{úyšc¦ySÌ•Ÿ×ÿºQLº£ôóô¯éwfF6ÿ>gñ)­^Sòy é_W{t|ëÙZJÊôãñøšß)Ri¹ˆ-ó¥ò™òèãããWíþ®Sû.À:·Ù`#¹­Þ6BÞ½¾¶“ÚÌùÞPc°€5:ν—’jÍårÉÖ¬çí¤J¿ÐBåŠßmÕ*r»/$&°B„¬9Ô”, · æHRˆ€…†š’ô¶ÄI °ÐPS²€Þ€˜#I!BjJÐÛs$)DÀBCMÉz[bŽ$…Xh¨)Y@o @Ì‘¤! 5% èmˆ9’"`¡¡¦d½-1G’B„,4Ô”, · æHRˆ€…†š’ô¶ÄI °ÐPS²€Þ€˜#I!BjJÐÛs$)DÀBCMÉz[bŽ$…Xh¨)Y@o @Ì‘¤!ì•!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!Bàéýý=Û§ŸKØ–…Öèî÷ûétúøø8¿þöö–~˜þéx<žÏgI ¦êÀ»6!BhÍççgפãñxÌ9rúóëõz<Ó)‡ÃÛÛÛë)ÒÏÓ¿žN§Ûí¶ô¦‹I'úúúJ'}j=—5V»XHñ5º4~ 6µhÙ“*Òœº+J˜¦®Ò(Þµ @SN§SñøàårùúúêšÈôKx¿ß Þàõzs1Àv—Až6%i„à"¶XÚ§*M >>>¬Zö¤–ž(aꌤPkl,D[w»Ýºzð ñÁëõÚõBâXé83cs¡.Øë2ˆŠØPiŸ6_xÝ DˆÐ²'+KÓó®ºæy ÔØX_ šetÍ÷'Ì2¦-ô›ü“Pì~äóósæöì°BiŸ0_˜¶‡!°¸®ç{ÓäWDkê ¾®—ì¦ÅÂH„ºßzË =.—‹D&ri{ û‹ "—“fÇ&ªÀ†ÆÆšzër¹tm–m\1!J¸è; X±í[,í£Žp½^{¾Ëy:Ò/ü|wév»¥)Æñx<B„HÀ…¤ »gT€ 5õP×£i²?y‹’žÁ@:lšÈœÏç_Ëé¿ÓOÒÏÿ|>yìÆ§=‡J7žNw<Ó©öy1~ÁÐÒX)8nÕJû¨#<#}"“T™”€EæòÙím7 „Kgˆ¦ëÉÃ9+Z]Oßï÷3®«J‡ÃÌ‹I3¦tüá7˜®¼gx3ð¦€P&¯Z<ŸkJÒáÏG›ÆŽ[ TiJÃÝìR-¤–=%`]]%œ¼# e"€vtÍ÷gN(~íÆ9-ÚØ³Ñ¨þZ¦›¶”‘*û|¦íF`£J­Z¤AB Ð‹„l½´g?yÖøز§Œ£ëñZϲó[f!BØ·®-ƒfnKò<ÈÇÇÇÌYI‘$Î ~ëÚÅe~rë+»jѳçÀñx”Úlº´g÷Þoücg–=%`i6ê5v`¡–Yˆv¬ë5½ùß×{{{+µïP6ˆ9j²“.¦Ô F×..¾H›S|Õ¢ëÉ+‹´l½´gËvãÏÈYö”€¡d_õm|7` HË,D;–쿽½…ºÈ® k]Ov'1{Àæ`t­ÐZ aë¥]©–&0¾4‘÷Œ P¼e"€½ZvM©u=ÙÕ?3/؜⌮O<[ aë¥Ýèw…DJÀ%&ª^$f¶ÌB„°WٷᢽBpþصú§DÀ¶,Ñ¡[ a—¥]ˆ0ø Eò”}‘0Mü¥ 0g+DûÓçŠùM½hƒŠír.—Ë××ׯ fÓD2ýäx<Þn·íéÇãñ}w¿¦ÆÏLÿ”~a…æ¤Ê•êÑççgÏ•¤_H¿¶ÐI•Ì÷÷÷gþ.tûÏ”OÇOgIçz]?|Þòét*XÀªœ4`a[®*ÕJÛv2+¥Þ3‘]Õ÷%ÅOØëõšná™¶=àûnÊ6z‘;ôC*¶ù´„»Ôlˆp¹Ù¾—=WTIÀí確^$ŒÙ ›-À.¥ ÝVzêl4³î¢D¨¤r1) ÓDxÈP-Mç,MJ™ïò×ÌõmÇëõš}%¶Kúå%6Ûy<iJþº„ÒŸÚ§Ói΄}ÂISÚ–ºý±)ÿþsîºÊI+¶5ëTÝ´-.ff¥:›’+û„ÿ«”›Ñ’4]Ò„Bò³ÑKMV¨u×ÕB„éÞ«t4Ûü•+W¶šLx’íõ“ÖÞ¦É~ûÏ7Š—®•?&¸õeÀf“ï7ZÙXm vû‘ìDµâ¥Þn·PßpìqÍYIw4a]úÏáßõz8C2Ù°LŠâ¨’ûÓ|àIÇüœdZèaB˜cþì ÊI#¶êTÝ´-.ff Ϧ‚U5øŠA© Õr}è4]=ïœ7ôë¶ùµ*Wöt^Æ,RdzéÿçA„«ô kÌv"\a<°þ sÅösÍiÑk-(Þ"€Fd¨.²VqÙ§ñç?Š?G6ÀZqãÓ숫ÔtxìúmÏð/ªë‰Ö ƒÆ ¯Föl"49ñ³ñ⥇ÊeO:v™´`Ê¿å*' RØ–®SuÓvÞ­zfYt M›# <ªßWñbœMÌyލn›_±r¥²1¿.t=7v¯ÑiW"D¸~¿°òÀlO!ÂuÆ+9ê¶Ÿ«M‹†wFu'ÑÀ&@Ë~Ý­;GMëê®(f't÷hízì¹Ôúm‘¹pÊʱ—´æ"Æ´ùr‘÷)ê®uŒ}.zÔjF©[®rÒ8…mÑ:U7m‹‹™YEžÖøoÒ×Ó6±bP}¹²l1î HM~q£n›_½re—¸G%fO+7|0™Í…!$!•û…õf» ®6X3wª·ŸëL‹ºt½Òn%XÀ¯-€hº¦Kq¾vôÔõ5Ÿê×™ÓUü¬Ò¨æt??r¿ß/—KÿÒßñxœy1ÓVlÆNx¿WãÓ/üúJúßôÃþ…ú±!ò?—¾¾¾RÚþ,®)µÓ•¤ôì¹’9'}ÞûÏeÏg§‹éY‹Hÿ4d¥´«J~¯÷þºßç?o9•½i·\夡 ÛruªnÚ6³ºZã_×3¤5®þ©Ç®{IEâú??9µ*é'¶?Õï«`1.þºm~„Ê• [Œ–ô¤ÃÌ-O‡<&D¸f¿Pe`¶ášãÕV˜#´Ÿ+L‹ztí5mÊD#D-È.õTü”^vN×5߬¾@Úõ‚@Åÿ}¥ÌM™Þ…iÙ3ÇþåÇ¥=óýÃáðçu¦_èY>eîÚýlø²g:Búµ×%ˆþ?éú¶Î{ÿ×»Ì;dŵkÁ$ýíÀòŸRøu½7àIC¶åêTÅ´]¢×ØJf¥äí¿žž¯hÅyá.]aº‘QËŒ=_\ª¸S÷¿B õ=[ÆMεêm~„Ê5sk÷þÁð7s³±û!/3n=D¸ôíTe`öo/!ÂŠã… Uö³úZz6gn„"D-ÈF‚ª/<þù®Aúy„‡³+]u÷ûs9zø¡º^`^yÕâùþÑwýêzkiò…ÕmóƒT®®¸ÏÌ4,’CþVˆ°îìféÙ¿]„ëŽ*TAÚÏ¥§EÓÒ!¾å@dB„Ðl?ù+9ë <Ò§îºèSño -š’i8üí¿o]Oûœ §Ùî´ÜìÚ™gÂ.¯]Ï9T×ZÊ„e¨§ôWß˧]¿ÓµYЄ{ïZñè&g×ÀG ³ªÿýå*' UØ–«SÓ¶¸ø™•’eì+ð]íLÝîf¾®¦¬âK ­{LÈôPm~œÊ•– LÛ?¿ö5¹j|r@ˆpUf3Ó$NÙ¨;X¢Pi?—žÍ™ºZÖkˆ Ž®ç±'Ä’Vx¤Ùå×××ú×Ö•tÙµ¦ê{´–]n¹~ûç+sfÁÙôŸü(löó!¹™]®ûi§ì*DÏ’cvgr.g_èè¿÷ì;K×Í*' UØ–«SÓ¶µÌþòàûš¹lA´—Z÷¸\.s:…êm~œÊ•½ø!Iñº¶ÿz;C6IȆŸ¾*D¸Ž*³™i§lÔ,Q¨‚´ŸKO‹æLë"ò§©höš‹<ÊÞsÒ­Ä—ËÓPsáìÚE‘ÐvvâÜõnB¶T,½ómœåŽì~k]R~||Ì_PZÿ¤A Û v• -k÷™³ËNe`‰viã¢TSºÞ{ÂLAÚüh•+»Ÿdס=»Œvõæ]‡šù]3!Â¥U˜I“Pe£âx@ˆÐôØÄš¡!ì¾»~ÍÙ·|bšmÅÿµ"\´Ü?x•êóߺú/¦ëƒ5C”ÒßžÏç 5hå“)lë\O• ÝJ!³¢uÙ)»—[€Ýe)Ê`úÃmAÚüh•+XÌ~ñõѲנÞÀ½F³ß@~"Üý¼f!Šã²éjÌüOˆØÑš¡!ì¾»ÙÙ˜]×#ßsl.>øOˆpŃ þëøÎË(ŸŸŸc_EYó¤M…keè&z4!Â_²á!Â?uU»^F"ìòúbcvÿó׫½õî5šýF[ÝÒ%Dí’v"¬5"4ý6±¾$D»ïîã_vvƒ©ìÓãsl1>øOˆpŃ >]¯×ù ;£Òaµ“¶"¬•¡ñ{4!ŸFÅSq:Ïè’Ôƒ_ÿOÀ¡ÈÒ“Àü׽רa—ì[N¯¿öë«…Ãw}ðì5b2êC„ÿ„˜×ì)DXe< Dhúlb}IˆvßÝÇ¿ììV]‡Ã¡à)zÖ?#Çÿ ®xð8!Âï|>ÏÜrpBÍZᤠ†+fè>Z†å®'Hõïù¦ÞÏ—GR¸cS!®½F³oÀýkcIyÚÁ³ïô}Ç¿ËêÀÇɆì5úFõ!ÂB„ Ìkv"\<°BˆÐ¼Ûâ<0¿Ñ"€Ýw÷®|»ñÁB„+\ˆðÕý~?ŸÏ¿ÞÚµˆí¤Í†+fèÖ[†å®'Hõ?=/ NøUS!ž•–ȉ0D˜Ýô×ûJCví9ÚŸ×9êC„ÿ„˜×ì2D¸æx@ˆÐôØÄú’!ì¾»[zv³éøàB)3ùµÍ…²)ûËØ•º®ì¢÷Ê—16Ik}úíÏļ\.ÇãqÔÃç37¨,~Ò …-ÂÂN• Ýb˰\fE˜P¿¾–õó­«i]¤áØáúm~´ÊõôúZ߯‘ÉÀ]F»Žös¯Ñ×ÐÀ‹ŒÜ&ì/DXe`V$M¶Õ_,7X!DXq̹·*Œ¸ Àndgv¿¶iÚÐ@eþa³ûVÍ\üÜAÊD .WnGÝi•ê³Ý:û|þ<Õ£?·(,XÑæŸ4Ha[ºNm%C7Ñ2,—Yr¿«—|Ý•q[÷µæÅŒZ ÒæG«\O¯ü•Œw}êßktÈN¤ë—.!Âhõ¥‘árã²—m̡ʬðµ`g„`÷„z]ÿ)²ø¹ƒ”Én+·þÃÀßRv¼vì'†—®ÜÏî¶Tä2zdOºæ‹Z¥(zöªZ( §4Ha[ºNm1C̬¹ŸMä ¯S…-ÕÑB„AÚüh•ëér¹ô¼2öú¯ýo“õï5úšçó¹zé"Œ00+’&»é/fŽÊ^v´1³!°EB„°{Ùá„E•uí:ç˜Ùå¯!ob·Ä’Ër²Ðð/²,ÆÍƒ®Å„å.cì½§ ÚbûÓõ:Ò¢+cO¤°-]§¶›¡MeV„ÜÝŒqþ°¡©aצëC]AÚüh•ë)»cÞw âב‡²{ö}}=jÂv…B„Û-¨K§ÉÎú‹Éã²—mÌv+!B`lÛ%D{2ùí°º²ÏOžÝ<žOiĘ®0ýœü-…†Ù'`g¾Æò”Ý"©ë½Úìe,=¾í:é&vÁ}µÄIeO¤°-]§6¡ídV„Ü_âÓNM…³˜žUë m~´Êõí5¨÷’÷êÚMô59íÞ³Y9çKyB„fEÒ$ZÙ¨5({ÙÑÆÌªÌFçþ@´õ%!BØ“ìþNñßHÊ~}fÚ»~÷û=ûNį§Ç·>„›sÀìzf×û«Í…‹¯¥üÿvjöý…ŠMö¤Ý/ñFpñ“F(l+Ô©ífh;™6Dð˜a/¦kË‚žý÷‚´ù¡*Wÿ€ðßø]F¿Ç„6íÅ´ìÍÎÙÞ_ˆ0ÈÀl~šD+µÆëô›µÆÌªLõÍx}¬/ Àž,÷Xø&¦ÿ]³×" _¡†pÅç’'×Ë ÿ²W5s/©ìÊmÄ<{Ko×Óõ‘—¾HXeŽ0ê¤A [Å䊟¡d–áÖ p6õç#H›ªrõcS:OØeô)»×èë{7Ó¶˜(¾O¯aÙü4‰V6j¥FñÕC™#dÊÌ· Àîu…Û"Dz¤L¸æ®Ïd<_‘Ûh´åßZ[ô œ­/7üëʾÉË]+·ý¡ç®ËXt®ÝµOݶ>šYwÁdÔIƒ¶ŠÉ?CÉ,n½wíjÞÿb~6?Tåêϲ×KžVÙ½F_3nÚ(1[æä£aÙü4‰V6j¥FñV¨1sõLÙâĈ9¹"€zü‚ò”ݽ¤ë½¿±Ï±wÅŸOoz¢TvÄ•}ªyøþ‹ÿ²Ë“Ï.Î yï {ég–¢TÔ{³÷^ðÓ™ýg^@‘Õ¶lîÊÄ*' UØ–«SÓ¶¸gV„ u6Aæ´<]ëùûèCêÚbtHVoó£U®þã¼^çð}!zÞô»×úÀÁç´½;Ò£«D­VÚÃ.ñU˜ÍL“8e£îx Û,ÌœHi?#T™®€©õ`lÛ%D;“Ý€¥à£•ßË)óç›=û‚ˆ½gâ<ˬ°C¸i‡êJ¨áY¹èð¯kayB&vÝéÕƒ®póäUÓTD¿9öÞç¯x 9ûwΦt›ùöP6õºÚŸ*' UØ–«SÓ¶¸gV„ u6M¦5w©°um·³ajW{îtHêUoó£U®!½ðä8KÏ'ªg¶i]ëócGÈé÷»¢B„µf3Ó$NÙ¨;È~]tæ'0‚´ŸªL6yGíí 4HˆZðº¥RÙÏþzîzò\¬kÇ˱k5=+?ûøV{‘}Àz©£V—þu-ƒŒºÈ®;X RZu]Æ„kSùy´žßìÚ°nø—"{:†g®#uý»^÷¨rÒP…íßÂ!ÂZi[Ü^3+„ºk{TçþÚÚì5D˜ªÒŸw:°‚Ômó£U®!-Ïäˆ^v`\d«®]þ†¿àÖÿœ›aÝÙœ4‰S6ꎺÂy3cyAÚÏêU&[/ö1ÿV ÀÎtMâf>;Ú?¢HSÎá ,é {æ¼c7*¾à³•!ÜÇÇÇÀ{ìy~j¼ÙžÀñWÓ/ô, kô\ÆÀe”ª¿Ö þL«þÍÐÒ¡†/§¤ëÏV±Q9›R2qøBÖ„‡ü«œ4Za[!D¸~Ú·×Ì 2¡îê †,´ n7D˜îîú?§ÓéëëëÏ—ÑF­{×mó£U®Q‹Wcûç^£sö«ìÐþºJW•}Hˆ0ÎÀlfš)uÇ=µoÎãFAÚϺU¦+ÖŽ ØÇú’!´Ðé—z¤°$f‹i"y¹\~-+¥ÿM?LÞžXÕ´yÍë 8„KÛ”¹)…NöŸ ›)/ú—4Çîw´Bšô?íŸnçy³¿JWúaÿŽ-ÿý‹3©§_HEúgä=ý÷3Í'oµ×¿öÝçóùW ËXúyÿI{N¨rÒh…mµáÊi[Ü.3+H/Ó³i[¶­{•”t=m]ðáBƾS±ÍØíþÔS´¦½™Øs©“?D8$Sõù5<[´îì8DXk`6'M‚”êãž§ž˜ödYðö³n•éjº­„ÆÆÒªËNyf®¬3¢˜ðêŸáj‹™«Í…ÿÜXi¬iŸ¤,~ÿÍø2ÔÒg¯RÆ"ìê…mýáÊÆžZ†½†ÿõ.áŽÕ‹Ù÷2ÈäÚQ«ÍÙí~ë‰ZNûn`ϧmK¸DêuņVkRâ/ñ­?0 R³æ”ê-ÞXÞ´Ü©Þ~Ö­2ÙÞvfÛ ´@ˆÑõi¡"o,7Š˜¶/Šáš‹™«¥IÁU 9“åâ‹QK¯¥„ þYÆ‚캅mC!ºñÁ]fVœ uϧ¾Fùúúêú׎—A‡ÃœñU•6?l·ûÔó9ÂicÅžm Çn§­>C6¡í÷ŒT F˜ÍL“e#Âx`Ô«‘£n°nûY±Êtµi»ùа鱱!Dî÷§=zýËü©nÙå5! &/ˆ­™&ý[Ÿ­Ô(øˆòð¸¥êPð¥ž!g/»FQå¤Ñ ÛBu*NÚî©eØqˆðÙìÌéßS‘ûîYÚ ŽúâU¨6?r·ÛÓˆMÛe´øZäj{bšJö÷"Œ60›Ÿ&ÕËF„ñÀäPiðö³b•éÚw×dM:@‹NÒtuÈW×Y^k3D˜mZú||üüîI‘‹YîÞSI›<ëŸùZǯŇ™O­§?Ÿ–½\.ókÙóë _hM¿–mâö"<üOªé~—è_*¶ùa»Ýì›bsn0;0žÿ!Â×ËX}~Ö‹ÔvB„« Ì ¦Iݲd<0öÇàíg­*Óõ"mä1‡!4%;œ³GÓŸ“²4é~F ³AÃôÃtIéÒoF{'esC¸×)šÛ¦þ5ïN?I3åô¯;Hðt —Ë%{›éŸ‹·éV¸ÓÛí–ŠzJØ×rþ}%K¤ù³Š=ÏûºÀòþþþ}êRK?ÏøºCÔófÓ¿¦ß)µ.]ë¤a ÛrE¨VÚʬ­èzèÙ•o«lÔúm¾ÊUÊwºu%š$ÚÇÀl»e£îxà»ùÕ°|7kóƨÞ~.*fM]³¦˜¼¾$D{Õõ÷²/Re'YÚq¹\²sÃ¥ßpvCˆZ“}›o¹ Ym'YÚ‘}…°àgUÝ"€Öx‘p¯C8ÉЈ®¯z…NˆÔõ"¡oÇlz'YZ&ï^!æ"€Ýï÷l—ýõõ%q¶;„“,-8ÙYašìK`8!BhÓ××W¶×¾Ýng£C8ɰ{iÚî¡_ !BhS×¶$ïïï¶ÝèN²ì^š¶ûtP„!4ër¹d;îÏÏO‰³Å!œdØ·®Ò_âc @Ë>??³}÷ù|–8›ÂI€KSuú @˺¶õQÂ-á$ À^u}‚УÀdB„`–ÑÕƒ›elk'Yv)MÏ»fîžï&"N§S¶%ÜÐN²ìOš˜§éyv˜¦óÒ˜Lˆø×ýQBQ á$ ÀÎôÄ}‚˜Iˆxêšt¤ŸKœM á$ €©:À@B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„Ð!BhŠ!4Eˆš"DM"€¦@S„ )B„°3Çã|>}}‡···Ÿ51ýoúaú§ô é×Z»àIˆŠ;UªÀõzýøø^1Ó/§?iáb€Ÿ„¡¸÷÷÷•«Àý~K”þ0ýù^/x%De=•«Àår™_IÓAöw1@–!”u<׬çó¹T=M‡ÚÓÅ]„¡ Ûí¶f(òÊ^©×÷B] ÐCˆJéŠ.Tî÷{ÿGýN§Óõzýù'éÏçóÇÇGÏNû`¨‹ú Â|Ç#»¿è¢Uàp8dÏõþþ~»Ýúÿ6ýBúµìŸ||lýb€~B„0Áý~¾w<»¢c‹Vtöì‰>??‡$ýrö ¿^÷ÛÖÅ"„ªLñËÈÆ%'¼s—Ýç3|»üIˆV¨2e¯¡ë­½Çã1öPéO²‡úswИ !D+T™²×Ý“ó|>O;Zö+ŠÃ÷ u1ÀB„°B•Yá&­ëݽ-^ 0„!¬Pe ^@vcÏ™ïÙeßL'ÚÖÅ  uªàñ³[q^.—9ÇLþzÌt¢m] 0!¬P§ ÿp8¼ÿñxÌ9fv{Ït¢m] 0!¬P§=þûûûüæƒL¸ìP $D+Ô©R¿Ýn¯ÿøø˜ät×#§Ómåb€á„a…:UêàË}§oÂWC] 0œ!¬P§J<;+•;Ncêb€á„a…:UêàÙ¨Üõzät"!ÂZ 'D+Ô©R?kFåÒé¶r1ÀpB„°B*up!B`>!BX¡N•:x6*w¿ßçùñx Öº`8!BX¡NíòàÛ½Shœ!¬P§vyp!BØ(!BX¡NíòàB„°QB„°BÚåÁ…'øì¡!B„B„B„B„M""DˆPˆp7!²­¢.`¯„…""ôáë[„B„;&D(Dˆ¡¡aWˆð?öBˆ0KØ!B!B!B!B€½"Ì6Bˆ°ˆÃá°ÐÁÇë‘Óé¶r1[ ª,»!D(DHãÖÞn·ùG¾^¯EB„µ.f[}„!ÀÎ Ò¸õC„×ëuþ‘K…k]̶ú!B€""¤q‹V¯¯¯5£rét[¹˜mõB„;#D(DHã­Çãñõà—Ëeþ‘O§Óë‘Óé¶r1Ûê#„vFˆPˆÆ-Z.—ËB±³ ñ¾P³­>Bˆ`g„…iÜ¢Uàv»½üóósþ‘?>>^œN·•‹ÙV!D°3B„B„4né*ðzðÃá0ÿ°ooo.;ÔÅl¨"Ø!B!B·t8Åÿx<¦ûB]̆ú!B€""¤qKW¯¯¯âßéË~U0h[³¡>Bˆ`g„…iÜÒU A›ùÀôçÓ"}¡.fC}„!ÀÎ Ò¸ª@öÇcÚѲ{¿æP³•>Bˆ`g„…iÜ U ûžÝù|žv´ãñ8çMÀP³•>Bˆ`g„…iÜ Uàz½–zw¯ë­½tŠ-^ÌVú!B€""¤qëT···×³|||Œ=Nú“×㤃o÷b6ÑG쌡![§ \.—ì‰FíÉ™Ý#4IßîÅl¢"Ø!B!B·Z8Ùs¥Ÿßï÷þ¿M¿Ðóç[¿˜ø}„!ÀÎ ²oqêÈý~ï9àÇÇÇù|þõ¿ô¿§Ó)»Ÿç·?#zñ/&~!D°3B„B„ì[¨:r>ŸË^Æœ]=C]Lð>Bˆ`g„…Ù·hu¤``.jf℺˜È}„!ÀÎ ²oëH‘À\©WöB]LØ>Bˆ`g„…Ù·˜uä~¿‡i§NXö“¡.&f!D°3B„B„ì[ä:r½^?>>†Ÿ4ýrú“…*ÔÅDë#„vFˆPˆêz<çóùëëëp8¼½½ý¬‰éÓÓ?¥_H¿ÖÚÅÄé#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ`g„…úû!B€""èï#„vFˆPˆ ¿"Ø!B!B€þ>Bˆ€ÿÏÞ½G‘« v¤@ Ž&Ràr¸#§@ Ä@ ¤à¼{/f<Æ]ýw$•ôë}ß,Æ´qZ’¿¥2ŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„DŒD(Äc„Dí}}øúúqâ'“ö“%B€pŒ ½·!ïnËÛ÷™áÔ˜D(„c„Dí½kyqÎÛñiáì˜D(„c„Dí]ç¼ èmý‰‰P"ˆÇ‰N±>êmú}‰P"¸7FH„p–•ioýÕùçª%B€pŒàDkßÊÿ¤òß…!J„á!À¹îf¾5®òǵ!J„á!@{¿ñr/öÝý÷ÿõÝ+3'‰P"ˆÇ‰ÚûûO/a%ŒÿäV”g&J„ñ!@{_y¹þ‚ÿôA‰pf¡D!´÷÷’—ùïîGð‚ÌI"”â1B"€S”ª„/ú W$B‰ #$B8ËñJø¢²D"”â1B"€½K{öR„D(Äc„D'Ú÷ËõAî\W¡DŽ!4¶; ꃬ½Æ$B‰ #$Bhàx|û¡rçz“%B€pŒ ’²Y0®„Ž6\{¡DŽ!T5 Þª„;ï¯C‰P"ljŽhÙ+¡SÀÂe)J„á!ÀVçfÁwN —¨D(„c„Dkt•UBî\®¡DŽ!ÜÒCþûûᅢÿ©²|éJ„!@8FH„ðVWÛÿþŸøOTB.c‰P"lj€É^o%¿ÅÿúûOný³Mþ¹ª%B‰ #$B&ÔC\Ù¡JÈ+\"”Â1B"`]eÁõ}ðV"T ‰®v‰P"lj€Ä:l‚[û`UBn^ù¡DŽ!™ô¹UðHŒ¡JÈò J„á!0º²àŽ>x7ª„,Ü¡DŽ!#1 ƉðÖg¾ûf/ÿs}%tÌ{ƒH„!@8FH„Œbô,$ ¾\%—ՕÐ53ïÍ"J„á!г4M0H„/·“ßõAxýÕ[™ôÆ‘%B€pŒèJ¦­‚qø{u÷ÓÖüyðúÌxI„!@8FH„œnÂ,øjÍç¯üO*!ÿÝS¡DŽ!§ÈW„Û w¿sÝ_¡DŽ!ÍL»UpÑúÙô *!/¡DpoŒ¨J\´éÕ¶~ŽJˆD(Äc„D@Y3ÿbÁõvÿ¾ÂR¯IòÛP"”Â1B"à8Yp«M¯¿ï3%©oI‰P"lj€}rgÁÚGoÓת÷ɤ½=%B‰ #$BÖ³UƸU%B‰ #$Bž CÞ¹¡DŽ!ïÈ‚0ü],J„á!ðâ B²;Z"”Â1B"˜–,iïn‰P"lj`*ž SÜé¡DŽ!@n~± ÌxãK„!@8FH„ùÈ‚0û›€D(„c„Dƒ_,ü÷† J„á!ŒËVÁI<<|}ýp4Xõæ J„á!ŒEœÍÛ>¨²öB"”Â1B"èœ_,8³ë>¨²ê}C"”Â1B"è,ÈËí>¨rÿ=D"”Â1B"èDî,èün÷A•;ï'¡DŽ!À‰ldÑš>¨½·H„!@8FH„É‚ÄÖ÷A•›ï3¡DŽ!@m~± ëmíƒ*!Ëo;¡DŽ!@ ~± ;ìëƒ*! oA¡DŽ!@)² Géƒ*!ïߎ$B‰ #$B€#¨ò"J„÷Ɖà•,HKõú JˆD(Äc„DLÎD9Eí>¨ÎþÎ&J„á!³ñ‹éDHÝ7:‰P"lj˜,HÚdA‰‰P"¸7FH„@V~± =hŸ%B^$B‰àÞ!™Ø*HÎÍ‚ú /¡DpoŒ€ÑÉ‚œ®“&¨òߣD(„c„D Ç/¤fÁ×W¡D!0YôœßöA¡D!ЭÜYÐùÅYðººÌfÿ”%B€pŒ€®Ø*HÆÊ‚·ú Knê÷R‰P"lj8—'ˆÒ‰º^Ù>èò›ú­U"”Â1B"Ú“éD?ÛýVB—"ïßf%B‰ #$B  ¿XNt˜VB—% o¹¡DŽ!P,H':o‚G*¡ë“å·_‰P"lj(ËDéÄpYpG%t¡ró­X"”Â1B"ò‹éÇèYpS%tŽ3K„!@8FH„À² ýH–WVB—.wÞ¥%B‰ #$B`%¿X~ä΂w+¡Ë˜ûïØ¡DŽ!°UNœÒšçé5•Ðõ̪wo‰P"ljxG¤“dÁw\Ø”y'—%B€pŒ¿X~Ì™ßq…Sà]"”Â1B"€9É‚ô#k( ÒYúÂ$B‰ #$Bˆ–ÏëÒÞúÿº£z‚(]‘aŒñK"”Â1B"€;+èoåZÙýbAz# Âxƒ—D(„c„D÷Ñ÷2ßš?ßñ$RYÉ‚0öÈ%J„á!À¢wM!Ž}wÿðí}}Y¿X®œÒeA¨D"”â1B"€E׉!¨„ñŸ¼ýO¶ ÒY²’%B€xŒ`ÑbqXþ^N}j¨,È]² Ì@"”â1B"€E·D'ùOd“¬YЙ…[$B‰ #$B¸e”J( ²H„™I„!@¨Âp‹,ì#J„ñ!Àªu´,H+² P`Ø’%B€pŒàÎ ÚD©O ^¡DŽ!DËg»©F*Ž_¡DŽ!,/œû~¦¨4¢Sš ,“Žb¡DŽ!¼_2—þµƒE*¡ó2(Y8g,“%B€pŒàõò®üüÏ#•Ðé”,œ?œI„!@8FH„ðÏJùÀöÀøO6UB'bPY³ 3 £j¡DŽ!‚èb\_ ‚AÉ‚@¿C›D(„c„DÀì ä¿aðV þ–#?(Yct“%B€pŒ˜wi\"ƉðV%tðÇ" ãq¡DŽ!3.ŠÄÁŇ…ÿõÖ—s:' ct¡DŽ!Ó­ˆK÷Á8ª„IÙeA˜t°“%Bà_ÏÏÏOOOŸ?~||üðáÃÛ·…Ëÿ¼üáå?]>áòiS!ó8¿Þ®9w?çE%ì•,äò$B‰zòøøxÊýøãÇOŸ>­—¸|òå¯L2FH„ÌàxüfwÕo}%tjN! ù>‰P"„ž|üø±ñýøëׯë.¹Òå/^þzú1B" ½Ú}ð:¾¬®„ÎN3² 0ר'J„ÐçççÆ÷ã÷ïß¿c\^$÷!X‘8x·¾ûB¯_}Ó'Sƒ,Ì;J„!tãË—/-ïǧ§§Ro——JXé~üõëWü¿}ûöãÇ·åò?Ÿžž>}úüÅ|¿—P" Ÿqp1ܬÿ¯êO² ÀQ"”áTÏÏϋϭz?>>>.~­?þüù3þ»—O¸|Úâ_ÿôéSÊ1B" É:·É“EoœMŸ í“5 :³@•aQ"”¡­_¿~ýÞ‘÷åË—[©®êýxùê‹_诿þZÿ"—O^|‘w{sŒ!¹Mâ`Ps¶~Ž<´’,°sd”%Bèûþ-þÏXì’;6.>tôòâùƉ€±—·­â`œuv|šTt‹,P`|”%Bèûþ-ûo¸µ…ðùùyëK]þÊâKÝ}Tépc„DÀ¨ ÛVO]Ówö}¦lôJ( ôééiß«-þÅM,bŒrUÛ0® =õ>91Y â@)J„Ð÷ýÛàß°ûÕnm$L6FH„ ¶žmçJR6A Ðãp)J„Ð÷ý[ð°ø”у›þ·%^¾P¦1B"`˜•l“'‹:ÎÅÉ‚' š¡D}ßà_ñ¹ ß¿?òš—¿~ýš—/”iŒc+E8yÜ”%Bèû/øúׯÿüü|ä5Ÿ5zùB™Æ‰€Þ°ž,:Y £ÑS"”¡ï¼êëüøñøË^^$ëÛˆD@ïKWOíž,Ðé*J„Ð÷ ^êÅþüyýâŸ>}:þÊ—¹~åË—K3FH„tºn»tJ”6£¡D}ßà¥^¼Þ/ ¬ñ+»#$Bº[±z²hgdA€ñS‰P"„¾oðR/¾òŠ$Âoß¾UzåNƉ€ŽÖªž,Ú Y`ì!U"”¡ï¼Ô‹/&Â?~åË‹H„Ðb¡*ž-ktfGU‰P"„¾oðR/þøøØ2^¾\š1B"àü%ª'‹žGÈ9¶J„!ô}ƒ—zq‰p÷!pæâÔ“EÏ äa%B‰ú¾ÁK½øb"üõë×ñW~~~– ÊÊTlH˜k•%Bèû÷â§!'¬IÓ=Yômëç8Ë‚óµ¡D}ßà^üô1B" éj4ã“E¯Ù‰G8e”6¸¡D}ßà^üô1B" ÝR4ã“EoU³þ ² À¼c®D(Bß7¸?}Œh±M÷dÑhE´¿´,0ûÈ+J„Ð÷ îÅO#$Bª¯@3n|YéÚEY€‰P"„îop/ÞûåïPÅ5ˆƒg~sk?š}¡¯eAw@ÝQJ"\¢Ô€D8z",ø#y¨ñ“É&O=óûÛöQõſʂ¼K$Â%J H„¡D@ÅKfÞ<ø°·ÁÕxͯ² ËãŠD¸D©‰P"”¨òÉäqðáX’+øR_eA¢áJ"\¢Ô@¾DøøøXéÅŸŸŸ¯_ùòå’%B×'G—™Mž,zî÷xVÔë? ºþº—%B‰ú¾ÁK½øb"üùóçñWþñã‡DwÖ˜õûàéߣ,( Œ44K„!ô}ƒ—zñÅDøãÇã¯,@´ºœ ¾ŒÓeAþ %B‰ú¾ÁK½øçÏŸ[&ÂË—K3FH„ì\Wˆƒú ,@ÝaZ"”¡ï¼Ô‹ùòåúÅ¿ÿ~ü•¿}ûvýÊ—/—fŒس¨œ#¾ôÚeAîŒÔ¡D}ßà¥^üû÷ï•B^½øØÉ!°m99M|é©þ]Ÿk Õx-J„Ð÷ ^êÅþüyýâýõ×ñWþôéÓõ+_¾\š1B"`íBrš'‹þ& 0ð¨-J„Ð÷ ^õõ¿ì‡²¾H„lXEÎÿýQƒ,À°·D(Bß7xÁ×||,þúÏÏÏ•Êc?c„DÀõã|qð7Y€‡o‰P"„¾oð‚¯ÿùóçâ¿4pñW^¾P¦1B"àæÊq²'‹¾3ztL=ˆK„!ô}ƒ|ýÅœwð×^þzñìØÛ!°¼lœ8¾®ºnøg—%Bèûoð%žŸŸ÷½ÚâSF3½‡H„,/ëÇÁbVÿ}Ð ÀÂh.J„Ð÷ ^öK,nú{zzÚ÷j_¾|)¾-±Ã1B"à¿¥âÜO]tùwØ]«ÜÓ%B‰ú¾ÁË~‰?~”ÚHxk áåK$#$BþY'ŠƒWúÙ?èú`Û°.J„Ð÷ ^ü«|øðáú«|úôiëë\þÊõë\^<ß!àÉ¢‹NLöî¡D}ßàÅ¿Ê÷ï߿Ц„.>°ôâòâùƉ`fž,zËY}Ð5 @™!^"”¡ï¼Æz||\üZ—?ÿõëWüw/Ÿüõ”c„D0-qðÆO0¾6îƒ.EÊò¡Dƒß¿;þ%¿~ý ^ðÓ§OOOOï~¥àå~ûömñ᢯îæÅAlj`Bž,zûçú )Æz‰P"„Áïß}ÿ˜§§§²ÿŒd};FH„SñdÑð‡ú YF|‰P"„ÁïßÝÿž‚•ðòR‰Ç‰`âàík|ÕH5èK„! ~ÿù'©„)÷¾#$B€x²hø3áO ß¡_"”aðû÷à¿êׯ_û¾ôå/æûýƒ×c„D›'‹†?Í8º+P Ó €D(Âà÷o‘Û?>}ú´þ‹^>ùòW&#$B€ÌK?q0úQF™§†êƒô8%Bà_ÏÏÏOOOŸ?~||üðáÃÛ·…Ëÿ¼üáå?]>áòiS!@ÎEŸ'‹†ÊþVA}€îf¡DŽ!@¶åž'‹†ÊÆÁÅJ¨pþ|@"”Â1B"HµÖC•ú`úãÀxS‰P"lj É*Ï“Eïјhb J„á! ¿¾ódÑ{NŒƒú çL$B‰ #$B€±wâà=•úàå•õAú!H„!@8FH„£.ë@§ó‰P"lj`¼'‹®Poóà;ú =Î$B‰ #$B€ÁVsâà Íúàoú Ðàíͬ˜0H„!@8FH„ìãT}c»ú€{Ó‰P"lj`€œ'‹®Óxó @³·7‰Í“‰P"lj ÷å›8¸Ž>$õpãÂùƒD(„c„DÐïÂÍ“EW¼$BvÍ"$B‰ #$B€—lž,ºšÍƒ@jáÜžKH„!@8FH„Ý­×ÄÁÕjôAW ÐÕûœDÈÎé„D(„c„DÐÑJÍ“E·°yÈîaÅܘTH„!@8FH„]¬Ñ!ef¡DŽ!@‹%˜8¸‡‹sx(÷oæ¡DŽ!@ÝÅ—'‹îbó ÚCx3‘%B€pŒ*®¼lÜÎÃEŒZ}À¿“‰P"lj ÊšKÜÅÃELoig|À¿S‰P"lj ðjë@Ôm†~ëãþ7!‘%B€pŒJ.µÄÁ]<\ôÝ«ËøßœD"”Â1B"(³È÷òpQ`¬7­>@"”îŒ!ÀÑå•'‹îeó 0Ä{Õ€ J„wƉàÐÚJÜKº}JñÁôS‰P"lj`çªJ<ÀÃEÞÞ–Ò}0ýDE"”Â1B"ؼžòdÑl:y7:ýãß·/•:Ó‰P"lj`ÛbJ<@Î}ê$ þù&RgÆ"J„á!¬]FÕƒú ‡‹eßx:l‚­ú J8ý¼E"”Â1B"¸¿€òdÑclš½ßtÞ%BÚÍ^$B‰ #$B€;«'qð}¨ý63VlØU¹'0¡DŽ!ÀÍu“'‹&5ÞZm‚!M§1¡DŽ!ÀŠɓE³y(øŽ’£ J„4ÌH„!@8FH„ï—Kâàaú pü$e|ëÏŒWò_.ò"J„÷Ɖ࿅’'‹– ûÞ<Ò7Á ¬„öòϬF"”Â1B"xñdÑBl6½gL˜ãï¿xv”C¦žÛH„!@8FH„â`ú °æ­bæ&¸æX”íƒrÈÔÓ‰P"lj˜zeäÉ¢…ˆƒÀ­·MpëÑÑ)3É‘%B€pŒ€I×Dž,ZˆÍƒÀõƒ,¸FöŠôAo§³Ou$B‰ #$B`Æ‘8XH>èú„ß 4ÁÖ¾ƒ}ЛêÔ³‰P"lj˜k)äÉ¢åØ<Ó XŸùv÷Aï«SÏy$B‰ #$B`–E'‹–ãá¢0%Y°°­±oGôÖ:õÌG"”Â1B"¦X‰ƒåx¸(Ìs»k‚UíK~[?Ùu<ïäG"”Â1B"’¯}[<÷-hñ³x\Vžª‰·S ‰P"ljH»êñdÑ¢<\²Þܶ žåÖ?ïn%\ß½ÍN=’%B€pŒ€œKq°(…L7´&Ø•Pdí\H"”Â1B"²-vH05’%B€pŒ€áW7ž,Z̓0Ä* Î|ò^ôAâ ’D(„c„D ¼®ñdÑ <\z¾A5AçïõÛ-w¦I¡DŽ!0ê¢F¬ÀÃE¡·›RtþÖWB7 Ì”$B‰ #$B`¼åŒ'‹V`ó ôs;Ê‚ÎߎJèÎáý|I"”Â1B"FZÈx²hú œ{ j‚NáÁJè.baÖ$J„á!ìbÄÁ:<\N¹ódAç¯à,Lœ$B‰ #$B`€õ‹'‹Öaó ´¼á4A§°Ô¯T Y5}’%B€pŒ€Þ/6Ö¡Bí›LtkôA•µ3(‰P"ljèwÙ"Vãá¢PéÞ’Â}P%dÕ›™óÂ×βà­3¸û\{ßææÔK"”Â1B"Z,@ÄÁújôAG•n¯wYÐéL_<éÞÀYž}I„!@8FH„@Ý¥‡8؄̓¤¿Æ5Agt¬&¸þ$¹¼³0“%B€pŒ€Z‹OmÂÃEÉziÏÖmL¿UpQÁ+Á›9ïgb¡DŽ!PeÅ!6áá¢$»¢mtFgÈ‚¯úAÆžŒI„!@8FH„@áµFý8èž¿Ùÿý*!¤#$BàÅ“EObó`ŽÓ~4þr²`òëI6Ìî$ÂAbVÿÿl•²Ž!̾4ðdÑ“èƒiÎdåD¨ Î~ y‚(°sŽ'v_²Æú …oŒ`êu8xMs&W|ÔxMYpŠKÇVAàÐ4O"¼¸ zšÜz0Ð!À¤+O=‰ÍƒÉÎg¡D¨ ºVdA ôdO"9Ž{¦Üz0Ð!ÀtkO=>˜ì|®þ8òw5Á).OÊOù$Â1á8ÓZ‰†#$B˜k! žGÌwJ7&BYÐe!  g}ဉp´É­Dc!̲ðdÑóØ<˜ò¬ñ¡ ¦¿œYàæÜO"*Ž<Ñ•aÔ1B"€ü“O=•>˜õÄj‚3_‡¶ Ì%ÂqáøÓ]‰†#$BH>óO%f=±² ³. ½O%ÂAa–I¯Dã!¤ó{²è©lÌ}z5A§ÙDÞ§‚á‰0×ìW"„ÁƉÎö=Yôlú`îÓ›¾ Ê‚² aB(vŸ3Î%BiŒ ÛT_<›8˜øÜÚ*è¼z‚(0ÌœP"ì;œ>FH„g’ïÉ¢g³y0ñ¹ÕN[Áf†¡DŽ!d˜Þ{²hjôAGõô³* :—² 0êüP"”Â1B"€áçöâ`lÌwJsdAgÑDy§ˆ¡DŽ! <«÷dÑx¸h²ó9úÎAçÏVA€&Š¡DŽ!Œ:¥·y°.šéd¶üÐŒF=s² 0Ð\Q"”Â1B"€ñ&óâ`lLs&ÛFã3O†›1J„!@8FH„0Ò4Þ“Eûàá¢9Nã¹jÑ@'L†œ7J„!@8FH„0ÌÞæÁ>x¸h‚sØÃ‡û±óó¤ ÃO%B‰pãÑvX`¶1B"€fïâ`7lúìõöáJèê$Ù*d›@J„*Ø®ƒìÞyƉºž·{²h7<\tè³×ç‡ àôÓ# ™§‘aÆþuä›Rwc„DýNÚm솇‹zÞFøpbü †n(H9FH„Pwâ-vÉÃE{;!]•Á[ÿ¬}ða”Ã*  7 •%B‰Ç‰jM¹=Y´K6vu6ºƒ·þ}ÅûàCÏÔD†ž‹J„¡D„c„Då'Ûž,Ú+}°ŸSÑsŒÿ•eûàCW‡ÒVA€L3R‰P"”pŒ ðL[ì•8ØÃI%Æÿâ‚ß|ÇQH9)•çN„G¾}‰&#$B(6ÇödÑ^Ù<ØÃI« Þý§7~O`óÔT"”%B #$B(0»ödÑŽéƒçþ¡ã`ümÔþë² ‡&¨aêDx÷{‘»c„DG§Öâ`ÇÄÁ}'q°ê÷Sü/z‚(Åæ¨a¢Dø²=ÛüE„!d#$BØ?©ödÑŽÙÞ™¶ ¾³ø%VV‡n>(Bî1B"`à9íR›[óÉ›^Pì–̓U®2x­Y´U€Se‰0{ð*ûÍêƒ0á!0ö´vu%Üô ž,Ú9­whÅÁX½>( Pxž,ÎѼŠ|›¶œc„DÀð3Ûu•pýµy°s.Zé¸*ƒ­“'ˆPu’,j^á!ar»¢®üOâ`çl,~DÅÁ–ËVAÚÍ%B‰ #$B’ÌoïUÂ5îÉ¢=ópÑâGTlv°dAN˜K„!@8FH„ŒëÝÏÆãJxý‡6ÄÃE Kq°Þ! ~S¡&@c¡D!ãºþiyP ßý‰88›KÈNâ`ÖCöö{¬W ½!°’D(Äc„DÀ¸x~«Ü3èÉ¢§ðpÑ"GѶÁîú›-X ½°ƒD(Äc„DÀ¸ný,½H ´yðt.züÚ6ØæØÝúÆwWB·?ÇI„!@—S'Â_8}Œ(?ÕlõìÐõ}0N„*aéŸ*ØŽ*ÓÍä%B‰p/œ5FH„,Ì)ƒ¿­üiÿúÿ*¬ø¡ÁTµmpì3áÀ0é”^"L«~/!L2FH„ü7Eª þ¶þ§ý›>A8b0ÏÃE•ÁáO†c À¼s{‰P",÷åÜPrŒ± þ¶éþ[?GA¸ñã‚6Ú6˜ä|8¼L=É—%—†Ù.Ùq‚1B"˜wB8l|µéów|šˆðçÏ fx¸h/qÐõvð”8 ¦O„•¾‰æ#$B€éæã—ÁW»{bÁ—Dö‡‹Ú6ØåU§À‘i¿D8A",þéƒ0Õ!Ì2ýKT_mú»õ>9½Ô›mìõª«j&šÿK„éjWío*åA‚1B"H>ëËXiøSˆ¬µm°ã«ÎæA(²§I„E¾¯¬ Ɖ çdO¤ÀO R>\Ô¶Á¾¯:› ÔŠ@"ÌX»j|w¹Œ!@ª9ž2H™Ÿ<äÛhÛ`¢KÎæAèa="J„á!že)ƒôDt_´¾äl€N&¡DŽ!@™É•2Hgºß\TÌ{½Ù<¯b$Âìµë¡wd#$B€›ó%e‘~>ÐÕæAÛ³_o6@ÿˉ0oízhËÝYljàý4Id°ŸtõpQep‚KÎæAb]#&­]͹› ë!ü3;Ròç<\Ô¶Á9®7}ZàH„k×ÃÜMuŒ€Ù'EÊ ÿ| ‡ÍƒÊà4×›‡‹ÀX+‰0]íz8‰» ²Ž!0é\HdìœþpQÛgºÞl€—<¡Dè Ám3_ê!0ã2Yd|g?\´—8èJht½Ù<ƒ®}$Â\µKȃ²>~ü(J„À3e,ÎÛ?þtOI„@¶é2HRÍ7Ú68ñŦ@šõ‘D(WnõA‰`ÈY2H^m.jÛàô×›‡‹@¦…’D˜:ºÂa«çççÅç‹J„® `¼ÉŒ2Hv .jÛàô›ÍƒoÅ$J„0·_¿~ýøñãéééË—/YߎŒ!0Þ4F$»†›mÄæAHºn’%B˜wM=ÑûÑ1B"†™½(ƒÌ²–iÐ=S”C×Ó , $B‰æ]VK„«Æ‰è}Ò¢ 2×B¦öÃE•A] N. ³’’%B˜we-®#$B Ó¹Š2ÈtK˜ª›mäÏëÍæAH¿¤’s%‰j¿ŸL8FH„@_SeI×/õú 2ÈŸ›>“¬­$B‰æ]bK„«Æ‰èbf¢ 2õâ¥ÆÃEmdébópQ˜g‘%¦N„*!ÙpŒ€3'$Ê ³¯JzƒnŸ<›Íƒ0ÛjK"L—$B¨ú†3á!'ÌC”A(Ým$ºØl€ —]aöD¨BÙ7œ ljh7ý¨VKÅA爆둄qÐiíôb³yæ\I„€DõÞp&#$B ú¬C„?V"e6Ú6Èý‹ÍæA˜y!&NUB(ø†3á!µ&Ê ,,C ôAÛYu±Ù<“¯È$¤ @"„Jo8Ž!PxŽ¡ ÂÍ5È¡8hÛ k¯4}§I„*!”zÙpŒ€2S e¢Õǡ̓¶ ²ábópQà÷M"Ì›TB¨ñ†3á!‡fÊ Ü_zì샶 ²íJ³yx»X“s%€‡ó¸›È¸N—%B`ïDB„µëޱã 38Ì•fó ðnÕ&J„!l¹¡&#$B`ÃüA„ +Ž=›mdÏÅfó p½|“%B‰¶ÜPŽ!pÚÐ}ô£lú[nœYÅÁ‰®4}¸µŽ“%B‰¶ÜPŽ!ps¶  Âε†mƒ4¹ÒÄA XÐI„¡D[n¨ ljx?IPaÿ*öAš\iú pwe'J„!l¹¡&#$BàŸ¹2G—¶ ÒäJ«]m‰'J„!l¹¡&#$B˜}Ù¨ B™õ…mƒÔ¿ÌlÖ¯õ$Bµ Øò†3ô?`½!Ê ÓÄ0÷Ó Ûª±UÛ<èÐ@òÙªDøÇ” À.B»`Fö BéeÅWÛiq¥Ù<l]ýÙE([Þp&#$B˜bÜW¡Êš¢÷8èe¸ÌôA`ß2P"”-o8Ž!dî•A¨µš°m&WZµ‡‹:¶=(J„À–7œ ljŽòÊ ÜX¼ù8²”°m¦«ÍƒÀž…¡D([Þp&#$BÈ3¸+ƒ®®>ö­#lä„‹ÕæA`ó Q"”-o8Ž! ?¦+ƒ°np0Ú6È)—©ÍƒÀÎ¥¢D([Þp&#$Bu(WaËôÿÆÇúåƒmƒœv™Ú<ìY3J„!°å gÂ1B"€ÁFpevMÿw'BÛ9÷µyعx”%B`Ë΄c„Dc ÜÊ ˜û‡ñªá«mƒœ{êƒÀÎU¤D([Þp&#$Bèz¼V¡ÄÜW"ôLQο:=\Ø¿œ”'NŽì¸G&#$Bèq˜V¡ÜÄÅÇŽ¿¢ ÒâÒ´yØ¿®”'KŽ ¼_&#$BèhtV¡ÂÄu"´m¾®K›€C L‰pšàÈ@‘»fÂ1B"€óeeªÍú۸ѨtQº~€m+M‰p‚àø@Á{gÂ1B"€ÓÆbeêÏúÅA\‘.!`Ï’S"Ìž"({ûL8FH„ÐzV¡Õ”_$Áé*v®=%¼ À‚7Ñ„c„DF^ešOùÅAF¿]HÀþE¨D˜4<´â&"óM"” Á€« ÂIóý>ã ãZôpQ ÑjT"œ8¶yHvM8FH„PeœUáÌ™þ×}Yæî_tDZíZ´y8qY*fLÅ¿;•ip J„PxxUጩý¦Ì·¯ºã8t]Ú<´_ŸJ„é@½ïK%dºõšD(@©QU„¶sù ðO„o_ÁMGÉ ÔæA åBU"œ#Ö>þn%ÒÜ,¶!_!ìL•Ah5»ßQ÷VB7/V›€+V‰0×Oø|G*!yÖeÞ@Ö!lC•A¨?ß×õ &•ÐiÃæA £¥«D8A"ô«À ï'ùƉÖÊ Ôœ¿‰zÇûàŽJèäñâá¢@‡kX‰0{"ý Ápï'ùÆ‰îŒ˜Ê T˜­üxÍy5a\ HŽ\ÐÞÞ€º‹Y‰0ÑOø[~/6’aæ dÝ!Àò@© BÑéy¥}pM%tF9xe{‡ª¯j%ÂÔ‰0Ó—ƒQÞOò!ü1>*ƒPh>Þæ£}"tj9x•{Ÿ-o%B‰p/œ5FH„ð¢ B‰Eä)•úàâo$tŽ9~¹{«Ú­s%B‰p/œ5FH„L=*ƒp`ÕØÃG³Dè|sðÒ÷†´^ðJ„á _8kŒ¨8Ö¼ée'~rüוAX¿Lìêãõf©Ô_+¡û‘ã·÷|à„%¹D(ò倳Ɖ€ŠcÍŸílý'|Ùà/*ƒ/ »m‚K7N½¯ Go ïüÀ9Kr‰P"äËg!Çš«Ž¶ò“K½æ­¿¢ Ââ*p &تª„º=¼ÿg.É%B‰p/œ5FH„Tk–šÚšO.þ‚Ê ,.ûF ‚!£Ü'†àä%¹D(ò倳Ɖ€ºÃÍ꨷é‚ÏT!Xçåh‚mû Jèž±ys=.¦N„õ¾—LÇ ˆÇ‰€ê#Îö´·ãE”AX\Ûåk‚!ß?Æ —ŸD˜+u˜ÝMuŒh1謨„+ÿÓ»ÏQáÝb.}”éöF2"}­Ä%Â쉰Ʒc !L5FH„4w¶l¼û•Ax]ÀÍÖ%Bú¼© @wËp‰P",t ÜMuŒ¨çÝFãJx÷•Aè-žÒ_]¦±ÿÿv!rþ fh:$æ«]U¿#}&#$Bê¹þéÊG†^ÿ‰2ȬÁ÷M°Ò‡[’‘'I„ÿÇÞÝ·£ N i!-¨µÒ‚wÎÜ‚[p ªÁ-¸µ Oßz’u,üHàÅ}_:™YG‘ AêY0Y>WÔ-¤ç‰€r¿&«„‰Té‰&8||­[ú ÃÜQgñ Ð.‰0dðÊþébo. =GH„½ƒ‘¿ùkç¢û¾tÿ¿^P¤æ; FþF°ÉÎõž@'çké›&¸òPÊû· ®îƒNK}hòÞ\"Œ›½l( Ë!ö> FþF°ÉÎò†@%G}¼—2Èá·kšàƃ(o¬„ÎQqþ €D½|ÙDÀÆ9B"d~Ô[ôú d9½”Aš¸-Óó>…ú Ó7‹€®¾g;è_¶°e޹ÍN{óÿW}Ž/eŽ ‚œÚâ óƒC×Zý6C"ì&Ù2Àº9B"äßñ0#ðÍüŸôAº^Ê oþÉ~¿¥ îp¼»xÐ(w [<DûÎD"ì,„Ù&ÀÒ9B"äCb*óÍù÷ú éáå¥ ÒÊ­•&¸ó‘R¨Þ,ÃâA Ûof$ÂŽs˜íÌ™#$ÂÎ}ùŠ#û&ÿåçÿÕ—'õû“—2C÷OšàÇH¡88xÁã´àضxèå+ ‰PHÎaç¿ëHTÂô¿냾BYpTÊ@^^Åú 3 wŸš` GG¹Åƒék'¹>Äÿ¢I"”’s„DعÁo<ÆÂ_â}0Æ·(—WöÌ·O%tžçϽ¦&Xáq±[L\öà˜÷pQ ö׿¡D06GH„ûÞcÝ÷ÿ‰7,8’•/¯jžá9_éÿN§w4Áúˆ=ãàà•ƒÄÁoñ ÐÃ׿¡D06GH„䪄o%»xyµïŽêƒ_þ}âÇ|yËØÝ¤ ØÊ±°óâAœ,zþúW"”Ææ‰[^^â+úàc"œ_ Æ{¥ 6y”èƒnŒ|ý+J„cs„DÈ•ÐK¼ãØ>8˜ÓÄ©»?š`óÁÂâAª:;X<ôðõ¯D,ú\Š*ô9GH„ü¡z‰wØÇáØtÒîƒ&'Rx¸(µ)Œ. “¯%Âaåï T;GH„|¦*wpTL$B•°'š`Ì6áá¢ÔvÊЀ~¾þ•#5¯¢ÊBBèpŽùbþwû^âdìƒéDxùÿ*8i7î›&ØC°xªÎ . ôöõ¯D>6ôþ@ms„DÈ#}P¼ƒÒ}ð6ô%múøº©„‚}5¥¶S‰Ñtøõ¯D;¶ø+€zæ‰A­÷AQ*Ióûàd"T ½}Ñ»M.JU§)Ðí׿a˜àµÏDZºš#$BÆêƒZ tÕæ÷Á/?|[ò·£:cWuË¢ j RÛùÅèú$†O„­ÿ"àð9B"dt„hàëÄÜg’Ûxø»=$ÂÛìJh;{¢ :'ü}/©RѹÆÁ t}K.J„uÿ"àð9B"dxxX!ˆxGî“É-ÙoC‰ð6{É!{Þ”h‚N5ã7’.JEçG1Ðû-¹D(îùY8–{®„Æ Ì1ÿðYôŽÄ-Wúš Á“éžÑâA*:I9ê¾%ÂV~pÔ!òïx˜êƒ‰DXa%´C¡‹ŽÐ¥?ãŸi¯ *nõA*:[Z$B‰°•_5GH„ÜæõÁt"œ_ mmèÊ¢“ÀŠsb»–×Âw‹â µœ¹œ ÆH„a+¿8jŽ™Ùï3Ãß?ðíñ»—±·ºµèKÚu?é»ß‹wMÐ`Øë¶ÔâA*:‹Z ¡DØÊ¯Žš#$ÂÞ‡Á¼>8'ª„À£EßÓ–ûáp¾i‚¾ÿ?èžT¤¢3š¡&J„­ü:à¨9B"ì}Ìp~%´6^› ‚¾ó¯à†T¤–³›sÀaìDXî³DÚn@zŽ{3úà¢D8X mg€¥×ãš ïùkºµxŠNs†ÀLa°Ôu`"t4AÔ9B"ì}Ì‰pf%´&/À5Aßí×zªRË)Ï9`‰P"¬ü‡Ï!ŒÍ’þ¶ææû€WÜš ù¢…;PqZÎ}†ÀRaøDXâãxÊ(t5GH„|XÛUB€¹WÙš Ù¡©{O‹©å<èİŽD¯v•þD!7˜#$BnoV"¼©„WÖš ¹ Ù»Îü}ÐVÅâA€I„$Â\J„ç‰Í}P%²]ê~9«4û¯ Òô-§ÅƒÔr~4´¶C¯BŸ+ðs„Dع©/[–%›Jl¸;±´óÜitžsz¸(Õœ+y¶“»J„«?]ìͤ牰s™úàD%´9פÕ'BMÐY=ôèá¢TsÒtÈB"ŒÚ¼²|ÌN¶ž#$ÂÎ%¿oY™§Þ`àÊtòÄRÓ•&H¸#ÐâAê8:#d$Î^¶eŽ;—¯~Û’îUr¢Ð}ßß±çá¢Ts&5®ò’cg/ Ø>GH„ŒM2¡_@C'œŽÎuš »ßQz¸(UœU¦J×/[ Ø8GH„ N/^%TsΉ|rÓ9îFÒâAj¹‚4® ‘{ˆ_¶°eŽœ[¼J¨é„çl¦ RÇ-¤>H-׎Æ@9a? Ì–ÖÍ!SŠ——:IeçœVÏ`ÿ9”±ÅÈ£‡‹Rŵ£ó@iao!Ì6–Î!“‰——@I}'œ6NY‚ uß-ZH׈@%_ÿJ„!ÀØ!ò1K„”^š &HóÓ¹8HˆÎ–U}ý+J„cs„DHìgú@© j‚t1—[2Pj‚ðé>NäøKCƒ  Baøæ•÷ƒK„Ðá!voŸïó#Ü@ÔŒ¨ ‚ŃTrQhPÔI"ŒÝ¼²p ¡Ã9B"ì^ðDx,IêO„¦š½ÌßmUW„Ùã qp ‰0pð*ôÁ%BèmŽ»'†%HþI¤š ýNÞRÁµ AP9‰0jð*úÁ%BèjŽ»'’ŸFU‰Ð!I¨iÛÃE©àBй  aÈDXúƒ[H]Í!·’AǶ%Òx¦ª>˜¨„Ž¢òpQ2(‹B“ãÕ®EŸN"&ç‰òiPaÙDhàÑ‹É9œôA€H„]%ÂÉÞò[MuŽ‘Á±Ühô(c:áá¢dQ. Љ0X"\ú‰$B`rŽ;·W ú=™H„°íÐÃEÉ7œ,è‰DØC"œÿóå~Ðî!vN"J_Àîò‚ˆŃTp:v¥Ð.‰0|"\ôóEÐè!vN"J_ÀJ„°êÖOäø±Ð4‰0R"\ñY$B`rŽ;'¥¯a%BX~ëçá¢|v€D(J„@zŽ;'¥¯a%BXrfñ ÇŸ‚(€$ÂØ‰pé)ýë€ç‰€¢×°!̾Ó9þäkD„!J„!ž#$BJ³HfÜy¸(ù†“‡‹ J„!05GH„åQÆ0uçeñ ù†“Ńü&J„!ž#$BŠ’!yÛ¥’o8Y<À'¡D(é9B" œÿìÂv¦Ù[~´u‹ %BèjŽ²Þ¯y¸(™Æ’>ÀaÔæ5ó. ˆú ô9GH„ùîÔ<\”LcÉÃEØF" œ½l( Ë!dºG³xÉâArc—/[ Ø>GH„›oÍ<\”LcÉâA2‘ÃÇ/›Ø8GH„ÛnÊ<\”ÉâA²’{è_¶°eŽÖÞ‹YØÅ^þô*÷ÎÙã @i¡D˜ño0t@AÈ9B"BòpÑ^vt™JøÍâA'J„!ž#$B ‹ûÚÝ*¡Åƒ J„!ž#$B }°»=ž»Z<@ ¡D(é9B"ÂðpÑN÷{¾J¨†DØy"Üòñ%BèdŽ€,ì}䨄. @$¡D(é9B"Z§rÛV - ‰0v"œü,!09GH„@ÓÄAþ7VUB‹I"Œ”o˳]Æ¿ˆP"„¨s„D4{dñ ·/ûnQ%´x€À$žaÞ%„!D#$B Í›#}ÿ÷¸gVB‹ˆM" ŸÇ>ÎÆÏ®B?s„D4xg$ò¯Á]9Y - <‰0Þj¸9jûg—¡Ÿ9B"šº!²x¿ŒíбJèá¢tB"ì'füàž2 ]Í!ÐÎÝ>È€E•ÐÃEè„D²vI„@Æ9B"¹U®E4J"ŒZ»ôA ×!ÕßþX<È´ì•ÐâAš&J„!ž#$B î{Ÿü}ÐVêq_[<@·$ÂÀÁk·>(Bì9B"*¾ë±xe¶÷A‹ˆA" ¼ò~X}:œ#$B Ê;e%‹à&v³&.ËÇ´„úœ#$B ¾eÃøÑ@"Ô¼¦æ‰¨ì®ÐâA6Œ€ÿ’%B€ô!ÕÜz¸(ÆÏ¶—!@0¡Dž#$B Ž›AeÃøY˜çWBÛ€FI„!@zŽ€£o-dÃøY»Tpòl[š&J„é9B"½ÔÙ0~¶=J4ñc¶-­“%B€ô!ÇÝz¸(kO¦¿jpð‡ ‰P"HÏ!pÄ­ŸÅƒl?ù¡J@T¡Dž#$B`÷û>}µƒgí+=xTBâ‘%B€ô!ûÞôy¸(kO™>ø1„TB‚‘+O„!{¥ mÍ!°×‚Ńl?eâ J@Taý‰0XP³Nš›#$B`—;}µƒ§|T ˆG"l"†ijå -Î!PþNAdíàÙ%ª„Ä#¶’d5Û#4:GH„@ÉÛ‹Y;xJþ̓æWBhŽDØP"lº¬…ù Ðá!ÅnôAÖžµ¯õòï—D@ë$¶a‹}-ÀG€Îç‰(s§ ²jäTÐ+!4G"l16”ØZÿïs„D¸M°xµƒçè,èo" ‰°ÑDXhk÷¿ø2GH„@Ö;}U#§Ž•ƒ!aH„M'Âj[[£ÿÙÀà!ùîÄAVœµ¯ÿ!! ‘ëXÁš¦>ÍÍ!ãNÁâAÖžµqðßA"À‰°•†Uÿ¶8Qç‰Ø|³¿Úª]ŒœÍ}pŸDh@Ð"‰0ÒÓ;ùïo}«“s„Dl»e°xU#gs” A"l+fUõ)blR`rŽ€µ· .ʪ‘“¯J„0F"l±gø‰âmL`rŽ€U÷.ʪ‘“/J„ 6Zµvû€á·$09GH„Àò‹Y>l ôÁ}!´H"l:l}«˜ƒ ÂÌ!°ä&ÅÃEY5r ÄA‰$ÂyKŠÎ!0ûÞÄÃEY>lŠöÁÛM"€Aa˜È%…托q?bñ «FN±>˜áwH„„&K]â }Ž€©Û}U#§é‡‹Ê…4N"Œ¼l( ×!É[eù°)·xðÀ8¨!Љ°‡òeã[批Ѱx¥ƒæÿ_1jˆ´F"ì°‚ÙÀ¢9B"†n+ôA>†Â‚W׋5D*#Šbé9B"îË<\4ö.ò²xPC *¡Dž#$BàәŃî¹#_jˆTH"”Òs„Dü¾Ó«Úm¼,Ô¨“D(¤ç‰¸­êƒâàò­êUnñ >¨!°D(¤ç‰:gñà¶Í×髆‡‹Š‰" ¡Dž#$Bè™>8´Q¼{¸è ;|^K5D€I„!@zŽ [ÅA½¦…‡‹6—=.UC¨™D(¤ç‰€’×÷¾d®öΫýŃ Kß‹›H„¢Ó;À$B‰ =GH„”¼¾÷r·]µöAMä¸WzçV¸x0F"ÔÿpÇdƤ‰P"HÏ!nxûÚ';ÇA£Öê7_µq°‡D¨!š pÇdBd5‰P"HÏ!nx{Ù™êª_Ñ8¸g”5D3î˜ÌwŒ‘%B€ô!à†·‹]‘ì€JA?Õ/ÌâA‰PC4ÅàŽÉtÆœ¯%B‰`lŽpÃkã{)JÍ-üßníP QCÀE»ÙŠñ¯%B‰`lŽpÃk{z‰D•ôÁ¶Î3ÿAC4IàŽ‰ê¿þ•%B€±9B"À oðMäeiXíóµ—ê×ÎâÁÒÜ~O4mó?^CÔpÇDç_ÿJ„!ÀØ!à†·Šâ¥úY<¸JÓ‰°†O§!jˆôr18ÉÐë׿¡D06GH„tzÃë[eÕ/|U)yÔ“Õb'Â>¾†¨!º˜±'šþúW"”Ææ‰€87¼¾VýT¿òhmM­óDXÃöÑ5D3và$CÍ_ÿJ„!ÀØ!Ðö ¯—ê§úíxhVøq$Âú7 †¨!º˜±…'üúW"”Ææ‰€¶ox½T?Õo—ã²Ún‡ØÂ¢†ØûÅ àŽÉI†’_ÿJ„!ÀØ!pð«—ê§úU@×¼‰ìýÃ9?hˆÁ¿¶}€Öïªèþë_‰P"›#$B7¡^ªŸêç¬ÐÖÃE?3Têçl£!¶ýõµoïÁ ‘s,ý+J„cs„DèšÙµ(¸™õRýT?ç•æ‰s—†Xõ=…;&p#ãJã_ÿJ„!ÀØ!º€w- nнT?ÕÏi©¹ÅƒtÅ™PC<ò¦Ãn@¼œ!iüë_‰P"›#$Bw®EÁµ—ê§ú9·YuR5D///O 9æ¬H÷_ÿJ„!Ìq½^_^^~ýúu:¾ÿþù¨¹ÿãý_Þÿ§ûÜ̶’ñ•¢kQiÏËͬDXô©žú 5ôAÛ4D ÑËËËÿ£O"D"”!¶Ëår>ŸçD÷¾ÿ‘üt:õ|~‘Õ=/7Åa—Ÿ8ÈáqИ QCôòr‚DˆD(Blïïï™l¦û¼ÿñ¦?þ?$B‰Ðw‹a¯EÝz¹³–kª~åâ >h·xÐ5D/÷8eI„H„!,òúúºý€º¿I£ÿz½v~~©4º»ôr‡î†7ë¾né”\ šèLࢆèš$B$B‰P"„Ï^^^rS÷·jq <==I„!n*½Üæã†·Ý>¨õ˜º-4D Ñå:8áH„H„!,’eý`Ók ßÞÞœ$B$B/_à†·Ñ8¨š·-4Ä~r¡KJp’‘‘%ÂíÛÐÁ‡÷÷÷ôß0øüü|¹\>ÿ‘û?¾¼¼œÏçÄlèï%ëƒ!¾jô’ö sJpÃ;}õîᢔ=˜ÄA`O¾½w5,»«rÇDù¯%ÂÀö®˜ôãt: îë?~¼½½MƵû þñóù\ÿg¿^¯ƒÏ•¾pôR÷öZ=É´} oñ $‹ I„.顯"wLTðõ¯D8äýàŠI.—ËàŽþùóçü7¹ÿðà›|Y{Xƒ÷÷÷%OOOcmT"”}çè%íáfÖ ïî—ñ.JÙcH%‚W{î˜Èûõ¯D5dÿà¢I3ÙŠ€ƒ½¿yëç“ç‰Ðµ¨—[xÜ7ý-_›—ñ.JÁYÚy pu!â_ËH„Ü$ÂÐ  ÐMbx½^—¾Õý ¾Õä£J+?Ÿt8GH„®EÕ=pgÝô—x­]Ã[ý+ÆKE?¸DØàB_^^Ö½Ûà_ê·è¥í&‘扰wÒîÐqûßʼn>HÙÉÙhÈ~}âfÇ5?¸c¢õ¯%Â`  ô×M"ÛuçŽ-$lúHépŽ»?/¸áÅm>nx÷¹2ñpQ ÎÌN°….`Ü1™MÀí’ã%€EŸN"ä³Á§Œn\ô7¸,ñþ‹Ú=Ÿt8GH„½sËûÜð¿&±x²s²ÑûŽÉNwLnÕYG"ì*Nþð–ßâh `ð¹ ¯¯¯[ÞóþÇßóþ‹Ú=š:œ#$B7¼!ŽÜð–¼ÞÐ)8!;ɸcÜ1¹å§‰0XXú‰$B>;N{öz½nyÏÁgÞQ»T‡s„Dè†W"T÷À o±‹ ¥àTlŽˆqǸcò½…H„=$Âù?_îÑèøùñãÇö·½¿I££Å8—Ùç†WÅs‰ŽÞ&‹):›h"Ý1î˜|ÿ@!aøD¸èç‹þ.*÷ööö¸OÏçóöw¾¿Éã;ß]£'œç‰Ð ¯DèêÈ}¡Rp6T‚Ý1=ó%EI„‘ÀŠÏ"òG¹¿4°Ä_qXí1rŽÝðv•íp ü†8H©é×L°?7)´K"”%B> †¼,‰ðùù¹Ð;WxL…œ#$ÂÞ5’í( …K ‹)8ñš.!Ð.‰0v"\úGJÿ:j6˜/—Ëöw¾¿‰DØô!öÎcsò\W胜u €£H„´K"”%B>œN§=áý×5zÂépŽ{'d¸¨)5åúnàX!í’%B‰áÌN‡s„DØ;‰`Óå„Ńœl €ÃI„´K"”%B> &Â÷÷÷íï|½^%¦ç‰°w!Àúk }R3­¯”*!Ð.‰P"”I\M¼y£Û¤¡9B"ìþì ¬»)5Ç*õh—D(J„$®&Þ¼ÑmÒÐ!ºáuà °ðÂâAfŒ‹Ü1¹càP¡D¸îƒK'¿£ó‘9B"tÃë†`ÉõCþ>h«F$%Â\‰ÐÑÔü7a”ÿììs„DØ9‰`ÉŃŃL ‹€:H„ÁÀÒÏ"’>¸šxóF·ICs„DØ9‰`Þeƒ‡‹25HôA &¡D¸âƒë&1¿²c õ¼gÅPHŸvH„62оü}Ð6¦X´i€•·²áðÝ[/•gÅ·ÑÕðôÿä›DØ7‰`ò¦*ûâAÛ4šb‹mZ`ýݬD8|*ôìõ5‰P"”‰J"HÜNéƒLÞ[Z<TzO+߯õz–~ð[ ‰P"”IÆî¥<\”ÉK‹€zok%Âá;¹€­gðsÍÿà7N§S¡{½^ßùþë$¶¡c~_!üSbñ  j[ØDÌôúúZ(ä•‹‡>Î!=ópQ&Fˆ‡‹m’{H¶s¼½½=îëŸ?nçóùüøÎ÷_×è±Óá!Ð'‹™$Í’ûI¶ +ÉétÚþ¶ß¿ot,9 $B:¾.ÒIŽ‹€ÆI„½%Û„„Óé”}×_¯×Båñ¨ã¥Ã9B" 7â #ÄâA }aÏ Àvà‹_¿~eÿKÿŠÃû/j÷épŽè‡ÅƒLŒ‹€($B þÌyÿ:ÂûÏž=át8GH„tBdb„X<"J0yˆ]¯×uï6ø”ц1牀ž®‚ÄAƇ‡Åƒ@8¡Ÿ .ú{yyY÷nOOOÙ—%~ÂépŽˆÍâA&FˆÅƒ@D¡Ÿ].—\ Ç–ÞEÆÿN‡s„D@`ú ©á¡qI„|ñýû÷ǃâ|>/}Ÿûy|Ÿû›ç=ð÷?át8GH„D%’. „&JðÅëëëàq±è¡ƒ,½»¿yÞÿN‡s„D@<21B,¢“%xt:û¿OÿÙû$þxöÿN‡s„D@0%ú ­gxX<ôA"”àÑûû{â9ŸÏ///_þJÁû?>??>\ôɼ¸âÀ¯ð|oŽˆÄâARÃÃâA ¡Dƒ^^^òVë1:yàWx>‰7GH„Äàᢤ†‡Åƒ@g$B‰Æd¬„÷·*tàWx>‰7GH„àᢤ†‡Åƒ@$B‰²TÂ-ë'ü Ï'ñ所ÖY<ÈèØ°xè•DØO"´ÑXçýýýt:­÷?¸úïœ9z+<(âÍ!-_{¸(ãÃÃâA caøoø¿íÅÑÛår9ŸÏóÇÃý‡ïd‡a\áÑoލæÊöoéÏ{¸( }èžDøþoûr4õàz½¾¼¼üúõët:}ÿþýó¸ÿãý_Þÿ§ûÜ̶Š4GH„Ôqqûµå-ýy‹ùwlx¸(€D·v}Û£ ¢Î!\ÜG½E?¬r³xà7‰0díúvGD#$B޾¸M¥½E?ìá¢]$‹>‘ãÕ®oq4AÔ9B"àЋÛéÀ7ÿ'-ìw Y<ð7‰0XíúvGD#$BŽ»¸ý§èKýébY<0D"ì*ðÀŠ9B"à +Û#û ídY<0B"ì$êÀê9B"àˆËZ‹Ù6„,H’#5}(1GH„ì~Y«²mY<0E" Ÿ r`ã!°ï5­‡‹²aüèƒóH„±¡lŸ#$Bv¼ µx ãÇÃEf“%B€ô!°×Õ¬>ÈÚÁcñ ÀB¡Dž#$Bv¹”Y;x,XN"”Òs„D@ùëXù kŃ«H„!@zŽ(|ûOÑ—väX<°D(¤ç‰€’W°ÿìð€Ž‹¶‘#%ÂÇíc„Û牀’—¯{'BÛ¼ù1cñ @aìD¨Û牀’—¯»&B¼ùcñ @&¡Dž#$BJ^¾î”mêæ‡Š>•D,ÞÈðذxà aÈÚõíŽ&ˆ:GH„l»4Ý”üôÁ°C8”D¯v};ˆ£ ¢Î!k¯Kó, ÔŽ 8šD¬v};Ž£ ¢Î!«®Ks>/T 56,¨€DØU"4às„DÀÂ+ÒÌŸàX%Ô‰ÚTC"ì$êÀê9B"`Éåh‘>øX ¥¢öƆŃ5‘#5}(1GH„Ì»-g²*ÔG" Ÿ r`ã!0ãBTddlX<P%‰0v"4Âís„D@òôø8¨U:6,¨˜D(¤ç‰€ñëÏR}ðϯÐ[ÔM"”Òs„DÀЕgÁŃ_~—`ÔØØÐZ J„é9B"àá²³øâÁ/£fƆ‡‹4B"”Òs„DÀ§ Îý~¡50<,h‡D(¤ç‰€ßW›‡õÁšQ½cÃâA€ÖH„‘áãö1Âís„DÀáqª‡‡Åƒ ’c'B•Ø>GH„Ó4K"”Òs„DÐ-qÔð°x ea°Dxó¬Q ÷!ôIdtlX<Ð>‰0|"T s„DÐ!qѱañ @a¼DxS ¬s„DЋú @ aÈDxó¸Q ß!ôCdtlx¸(@,a'‰P%VÏ!@,$5<,G"Œšo*!iŽÂ%K„ߎãh‚¨s„D˜Åƒ¤†‡ÅƒqI„¡¤ç‰ *qѱañ @t¡Dh£é9B"ˆÇâARÃÃâA€H„¡¤ç‰ ˜r}жm~lX<Ð ‰P"´Ñ€ô!„añ ©áañ @O$B‰ÐFÒs„DƒÅƒŒŽ } ?¡Dh£é9B"hѤ†‡‡‹tI"T»Òs„DÐ4‹I ‹z%J„é9B"h”Ѥ†‡Åƒ}“%B€ô!´ÈâARÃÃâA€îI„!@zŽÚbñ ©áañ ÿ%J„é9B"hˆ>HjxX<Ào¡Dž#$B€F®“ÅAƇ‡ÅƒüM"”Òs„DÐÂE²>Èøð°x€¡Dž#$B€º/ÅAƇ‡>À‰P"HÏ!@Åׯú ãÃÃÃE'J„é9B"¨õÂXd|xX<@’D(¤ç‰ ¾Kb‹0ƒD(¤ç‰ ²ëaqñáañ óH„!@zŽª¹¶xñáañ KH„!@zŽê¸ °DØD"œÿùí8Ž&ˆ:GH„G_ Z<Èøð°x€U$B‰P"Òs„Dpèu 8Èøð°x€µ$B‰P"Òs„DpРŃŒ}€m$B‰P"Òs„DpÄå_©>hÛF. Àf¡D(é9B"Ø÷ÂÏâAƇ‡Åƒd"J„!ž#$B€¯ú,d|xX<@>¡D(é9B"ØåzÏâA’#ÄâA²’Õ.€ô!”¿Äµxñáañ H„!@zŽJ^ÜZ<, $‰P"HÏ!@kZ‹IŽ‹(L"”Òs„Dû‚Vd|xèƒìB"”Òs„DïRV$9BÄAö"J„é9B"Èt«2><ôAö%J„é9B"Ø|+’!Åú m À‰°‰D(bÎ!À¶ 9}ä±x€#H„!@zŽ6\ʼnƒŒ‹8ŽD(¤ç‰`Õõ›Åƒ$GˆÅƒJ"”Òs„D°üâMd|xX<@$B‰ =GH„K.Û,$9B, ¡Dž#$B€Ù×lâ ãÃÃâAj"J„é9B"˜qµfñ Ébñ •‘%B€ô!L]ª‰ƒŒ}€*I„!@zŽÆ/Ò,$9B<\€ZI„!@zŽF®ÐJõAÛ6ȱx€ŠI„!@zŽ®Í,$9B, z¡Dž#$B€¿/Ì,$9B, ¡Dž#$B€ß—d’!ЉP"HÏ!ÀÍâA&GˆÅƒ4E"”Òs„DtÎâA&FˆÅƒ4H"”Òs„DôÌâA&FˆÅƒ´I"”Òs„DôÉâA&Fˆ>@Ë$B‰ =GH„@‡ôA&Fˆ‡‹Ð8‰P"HÏ!ÐqéAbñ í“M„r4AÔ9B"ú¡21B, ‰P"”ô!=™$ˆD(J„@zŽðôA&FˆÅƒ„#J„!ž#$B 6q‰bñ I„¡D¤ç‰ˆÊâA&FˆÅƒÄ%J„!ž#$B $q‰bñ ¡I„¡D¤ç‰ÆâA&Fˆ>@$ÂF¡¡ ì6GH„@$â #ÄÃEèƒD(¤ç‰ˆÁâA¦‰ÅƒtC"”Òs„D 21B, 3¡Dž#$B i2=H, ?¡Dž#$B ]åú md„X<@¯$B‰ =GH„@‹,dzX<@Ç$B‰ =GH„@s,db„X<@÷$B‰ =GH„@C,dzX<¡D05GH„@+,db„èƒð›D(¤ç‰háÑâA¦‰‡‹À'¡Dž#$B úëC‹™$Àß$B‰ =GH„@ÅW†¶¿?½Ê½³Åƒð…D(¤ç‰¨õ²P ±ËTÂoþæAH’%B€ô!õ]‰ƒöfJhñ L’%B€ô!•]郱vhîJhñ Ì!J„é9B"ª¹"ƒîÙ|•ÐâA˜I"”Òs„DÔq9¤†Þ¿9*¡Åƒ0ŸD(¤ç‰¨àZHì`/o¨„. KI„!@zŽC¯‚,ìiw¯ª„. +H„M$B€ç‰8îÂOŒïË~YZ -€u$B‰ =GH„À—|öâqï̬„À¡Dž#$B`÷ë=q°#ƒ»i²Z<I„!@zޝô,ìÎØÎ«„@¡Dž#$B`¯Ëh#À¡Dž#$B ÓUœÅƒŒŒ ‹`w¡Dž#$B Ç%œÅƒ Œ|/ƒ‘%B€ô!Û.Þ,dh`lKó+¡M ƒ$B‰ =GH„À†+7}‡Q‘i©àäØÔ J„é9B"V]³‰ƒ<ŒŠÜMü˜­ i¡Dž#$B`ù›>ÈßC¢Ø_58øÃÀ$‰P"HÏ!°äRMäï!Q²ª„°šD(¤ç‰˜}¦òi<ìÒUBXG"”Òs„DÌ»Hù=ö탃D%€E$B‰ =GH„ÀÔå™Åƒü GôA•V%B€ô!Ék3qÿŽ„Cû JKI„!@zŽ‘«2‹ùïH8¢ÞF~æ¦À<¡Dž#$B`è’L¤HœìƒŸÿæWBà ‰P"HÏ!ð÷ŘŃäLóûààÉÌJ|!J„é9B">]‰‰ƒÝÜëçôÁôÒœJ|!J„é9B"n²×_2¸nxW°”D(¤ç‰(×mÛ6ÀAqpÑ1À`‰P"HÏ!ôÌâÁÞÀqqpÅ1Ì`>‰P"HÏ!tËâÁ®÷þ¡qpõ 1Þ`&‰P"HÏ!tÈâÁ®÷þÑqpã 1ê`‰P"HÏ!ôÆâÁ~w}q0Ë81ö`’D(¤ç‰úañ`¿»þˆ8øñ« ÃÒ$B‰ =GH„Ð ‹;ÝïÇÅÁ冊A ¡Dž#$BÏâÁN÷ûÑq8D(¤ç‰bÓ{Üéâ tO"”Òs„DQ‰ƒîwq%B€©9B"„ôÁwº8ü&J„é9B"„`ÄÁwº8üM"”Òs„D‘èƒÝíqq"J„é9B"„0ÄÁ¾v·8Œ“%B€ô!Böµ»ÅA`ŠD(¤ç‰Z'v´¯ÅA`‰P"HÏ!´ËâÁŽöµ8,!J„é9B"„F‰ƒ½ìèÝû mH„!@zŽ¡9ö²£ÅA`-‰P"HÏ!´Eìb/‹ƒÀ6¡Dž#$Bh…Ń]ìeqÈA"”Òs„D\#•ꃶm-»Xò‘%B€ô!BõWGFßÅâ ›D(¤ç‰ê¾4²x0ôþ€2$B‰ =GH„PëE‘Ń¡÷¯8”$J„é9B"„*¯ˆ,Œ»swƒö;tH"”Òs„D•] Y<w犃À^$B‰ =GH„PӅŃA÷¬8ìK"”Òs„Du\Y<tÏŠƒÀ$B‰ =GH„PÁõ>q·ŠƒÀq$B‰ =GH„pè•8q·ŠƒÀÑ$B‰ =GH„pÜe>nŸŠƒ@$B‰ =GH„pÄ8nŸŠƒ@M$B‰ =GH„°ûÕŽ>k‡Šƒ@}$B‰ =GH„°ï¥Ž8hoŠƒ@­$B‰ =GH„°×EŽÅƒö¦8ÔM"”Òs„D»\ሃQv¥8´@"”Òs„D…¯m,Œ²+ÅA ¡Dž#$B(ya#†Øâ ЉP"HÏ!”¹¤±x0Ä~€6I„!@zŽ¡ÀõŒ8ØþN€–I„!@zŽ!때ŃíïDqhŸD(¤ç‰ò]ƔꃶíN{P¢%B€ô!BŽ ‹߃â ‹D(¤ç‰6_½X<Øò$B‰ =GH„°áºÅâÁ–wŸ8Ä%J„é9B"„µ-6»ïÄA :‰P"HÏ!,¿\±x°Ù}'}%B€ô!ÂÂk‹ÛÜqâ ЉP"HÏ!̾J±x°Í'ý‘%Bjv½^_^^~ýúu:¾ÿþy”Þÿñþ/ïÿÓýî?æRnŽaÞ%Š>Øà^€^I„!uº\.çóyþ ½ÿðýø€ÙN§žÏ!Ì»8Ükâ Ð7‰P"¤6ïïïYj¦û¼ÿq0£?~H„!$¯LôÁÖv™8 J„Tæõõuû¾¿‰˜Åõzíüü BòšDlm—‰ƒ¿I„!õxyyÉ5†ïoån÷ôô$J„0rA¢6µ¿ÄA€¿I„!•Ȳ¼®æµ„Í}À··7ç‰F®FÄÁvv–80D"”¨Áûû{ú/à{~~¾\.ŸÿÈý_^^ÎçsâÖó÷6÷Çú D³x°¥%Œ“%jp:‡â?ÞÞÞÒöþ÷üãçóÙ\êz½>_T"tœ‚8ØÌž¦H„‡»\.ƒãðçÏŸóßäþÃoòeižøèýýýcÅâÓÓÓXÊ”%B:gñ`3{J˜G"”8Ü`–Z±>nð™œ÷7÷óžO:œ#$B:'¶±›ÄA€%$B €c­°»^¯KßêþGßjòIžÝ~@燙s„DH·,lc7‰ƒËI„Ç|~æËË˺wüKô=ϳ«èü0sŽé“8ØÀ>Ö’%*Ò«ßml˜ñ|Òá!Òß™ÙâÁê÷‘8°D(p Á‡pn\ô7¸jïþ‹|À\ç“牮”냶mž$ä Jh𱙯¯¯[ÞóþÇßóþ‹|À\'œç‰NXNÅV¼wÄA€2$B €£¼½½=޽óù¼ýïoòøÎ÷_çf9át8GH„fñ`½»F(I"”8J¹¿S¯Äߨçt~ ÌâÁzw8PžD(p”ÁΕ¥ =??zçÞ> óƒDHTVº_ÄA€½H„G,h—Ëeû;ßߤÚDØÖt~‰ÇâÁJ÷‹8°/‰Pà(§ÓiÏ‚vÿu>`–N‡s„DHú`;E8‚D(p‰°þèü †8Xé~"Je° ½¿¿oçëõZm"lë:?H„Ä Ö¸SÄA€CI„U æ&Þ¼Ÿèü Òþ™V¬o§ˆƒ%ªÌM¼y?ÐùA"¤ñÓ¬>XÙª!JT5˜›xó~> óƒDHËçXq°¦Ý!TF"”¨j07ñæý|@ç‰6Ϯִ;ÄA€*I„U æ&Þ¼Ÿèü Òà©U¬f_ˆƒ“%ªÌM¼y?°éóCÞ³âG(„º\ óƒDHS¿ÅƒÇmüÝã ý°‰Pà(ooocïçÏŸÛßù|>?¾óý×ù€YN8Î!ÇMú·ñÅÁÿcïî®”Çmv¤@ ŽHx¼çœ))8R¨«ÑµOí:…,Œ-[šsôK×G¹ÀÀ’ì倢©U¬êóÜ4ÍôÅîv»•|ª·þ僊&¤“?´æ•ƒPªø ¦i²üÇLÅ\…/P>¨ùÐXïäÁ­yå @5T„*>èt:e¿§^ô€áy¹§Â1BEȲ½~ð«]9P¡ €ж]ïÖ~={+Wí ”*B–╃ŸXíÊA€*©U¬ð#ýx<Æ--zÎÏ~¤7ý僊³B?¸ø:WTLE¨à³¢çĵm;niçó9ûY{5¿@ù "äýoÍË»¿¢\èmRTOE¨à³n·[®óìúΰ "ã­Œ8e=T8F¨ü•ùÛâøýà¼ï‘r€©U|Ün·{þ‡w—~åy9aáy¿he¼À)ë¡Â1BEȰïK¼Ëñ+ÊÁYÞ å ¿¨U|Üõz~ߺ~fôzžAXxÞ/Z/pÊz¨pŒP2àË’*õÞ}¼~0ó»£à‰ŠPÀ4Mý(†Ÿw]—þÝð€Ä¯gÿ¢•ñ§¬‡ Ç!¯¾)¯«½w¯ÌóÖ(è¡"T°]×%>‡Ã¡mÛ?wÜ ÿ{¹\¢×Þüñ²}ñE+ãΚ'å*B’ߣ¡ß—“—|_”ƒ$©U„¬DÛ¶y?ÆS®À9Ç·cU/pÖ<)oŒPÒÿ%úŸ¹ÿÓL½ý¦(@E¨"d=2–haQ3}ÑÊx³æIyc„Šžo~peïˆr€ÁT„*BV%K‰6ëéue¼ÀYó¤¼1BEHìë£\ÓÛ¡àM*B!kÓu]Ó4ã>ºá'Þžïå­Œ8kž”7F¨yúîèWó^(EE¨"dn·Ûápþ¡ ¿²À­Œ8kž”7F¨ù¿_œ÷ƒÞ‚ÿ¼ÊA&PªY³ÇãѶíétjšf·Ûýþ”†ÿ ? ÿæ2ß¡"ä×(ìäÁ¼ ÊA&SªÒc„Šÿ‚<øé·@9@&*B!@zŒPò5g?¨¥´þ•ƒd¥"T¤Ç!¼ÿ •¯`*B!@zŒPVnî~0Ñ־敃ÌFE¨"H*ÂÊ}¤"¬}+˜™ŠPE#T„•[¾"¬zm+X„ŠPE#T„•[¸"¬w=+XŠPE#T„,ÓÖ»z•ƒ,NE¨"H*B¾æl kn¬”ƒ|ŠŠPE#T„|ýÛCÍÔÖ¹>•ƒ|–ŠPE#T„ütLî?˜a&£`T„*B€ô¡"¬ÜŸ²){?XO‡¥`=T„*B€ô¡"¬Üs딽,¾ÉR°6*B!@zŒPV.Z?åí î³”ƒ¬“ŠPE#T„•ëë¡2öƒE¶ZÊAÖLE¨"H*Brµ„•[ÊAÖOE¨"H*B¾r´„5t[ÊA¶BE¨"H*B¾Mi ‹¯·”ƒl‹ŠPE#T„ü×–Ýp)Ø"¡Š =F¨ùíÝ–°à’K9Àv©U„é1BEÈÃ[ÂR{.å [§"T¤Ç!φ´„EV]ÊAÊ "T¤Ç!Qÿo”í¾^å %QªÒc„Š>•^ÊAÊ£"T¤Ç! ew^ÊAJ¥"T¤Ç!iEÖ^ÊAʦ"T¤Ç!/•Ô|)¨ŠPE#T„ Q@ù¥ *B!@zŒP2Påý r€ QªÒc„Šá¶X)¨ŠPE#T„”J9@µT„*B€ô¡"¤<ÊA*§"T¤Ç!%QÀ—ŠPEðjŒPRå üPªÒc„Š­SÀ*B!@zŒP²]ÊAˆRªÒc„Š-úgñ~Ð:`CT„*B€ô¡"d[”ƒð’ŠPE#T„l…rRªÒc„Š L”ƒð¡Š =F¨YõÄ@9ïSªÒc„Š•N ”ƒ0–ŠPE#T„¬n2 €iT„*B€ô¡"dEÓå ä "T¤Ç!«˜,^ê(˜ŠPE#T„|xèW@n*B!@zŒPò±A_9óPªÒc„аr¿ë¶Å–¬€Y©U„é1BEX¹æi û«€¨U„é1BEX¹fh £ËTÀbT„*B€ô¡"¬Ü?¹[ÂæùO9éU„é1BEHÆ–P9k "T¤Ç!_™ZBå ¬„ŠPE#T„ügžÖ*`=T„*B€ô¡"ä¿£ðØ–P9«¢"T¤ÇaåþÔm#ZBå ¬ŠPE#T„•{îÝÞj •ƒ°B*B!@zŒPV.ZÀ i •ƒ°Z*B!@zŒPV®¯‰K´„ÊAX9¡Š =F¨y«%TÀú©U„é1BEÈ×R-¡r–¡"T¤Ç!ßfm •ƒ°$¡Š =F¨ù1GK¨€å©U„é1BEÈo[Bå |ŠŠPE#T„üí°!*B!@zŒPòlt?¨€5PªÒc„ЍwûAå ¬‡ŠPE#T„ô™éüA+æ¦"T¤Ç!½ƒr8®%´J`*B!@zŒP‘_•€oµ„Ö',IE¨"H*B"Ãñ°úoÈìLXžŠPE#T„ü‹ß9=0ñ`k>EE¨"H*BþÏ@ûï+yŸÁ¾_>EE¨"H*Bþ; ¿ßj `…T„*B€ô¡"ä?CðØ~PKk£"T¤Ç!_“ûÁï;j `%T„*B€ô¡"ä­~ð+ùOZBX¡Š =F¨+7¢üJ>@K§"T¤ÇaåÆõƒ_ɇ©à³T„*B€ô¡"¬Üè~ð+ù`!|ŠPE#T„•›Ò~%EEŸ¢"T¤Ç!?Fôƒ˜ƒŠPE#T„|›Xói `=T„*B€ô¡"ä+SÁ§%€•PªÒc„ŠŒÕž–Ö@E¨"H*ÂÊe/õ´„ðq*B!@zŒPVnŽ:OEŸ¥"T¤ÇaåfêòT„ðA*B!@zŒPVn¾"OEŸ¢"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„é1BEP¡Š =F¨ £"T¤Ç!@aT„*B€ô¡"(ŒŠPE#T„…QªÒc„Š 0*B!@zŒPFE¨"H*B€Â¨U„üñx<Ú¶=NMÓìv»ßŒð¿á‡áŸÂÂü@+¼’1BEP¡Š·Ûíp8 ÿœ„‡_ñË^áMÓÔœ*B€"©U„]×=7A…_ ¿î–ºÂ÷û½ŠPEP¡Šëõ:ý3â–·ÂGåù "(’ŠPEX¹¶ms}l¢¼ÀÂVøù|VªÊ£"TÖ,Ëélk>—pm/p[+ü~¿Ë!@‘T„*€ju]—¾áÝår¹Ýn¿%üoÛ¶‡Ã!ñ‹ë¹/áÚ^à¶Vx_?¨" *B@µš¦‰¾ûûýþ~¿§7< <,úë‡ÃÁ Üô <Ñ닪…@1T„*€:Ýn·è[<‡/$<8º?§Ây+_á]×}Ÿ®x>ŸûzL¡Š $*B@¢MЈóÑ¢×À ÷7±ÂåÃÀ1BEP¡  B}g´=w~%º¨—Wάê®v…ˇc„Š 0*B@…¢×«lÛvÜÒ¢÷­{ëú™Å¿ÀÕ®pù0pŒPFE¨¨PÞ7½ï¼6/pý+\> #T„…Qªj½èåÄ“þ¢gÉ…?ä®|…ˇc„Š 0*B@m¢—©¼^¯S–~ýy™áy[_áòAEP$¡  6MÓ<¿ãÇcÊ2£—¾ È Üú —*B€"©Uµy~»÷ûýôņ…¬äƒ´¶¸é.T„ERªªr¿ßŸßîÃá0}Éa!ÏK®ò¸õ.T„ERªª2ß=ìæ¸ã^/pë+\>¨Ф"TT%Ú+ei¬.—ËLKÞô Üú —*B€"©UU‰6V·Ûmú’ÃBV[~ðn}…Ë!@‘T„*€ª4M³dcþ\å/pë+\>¨Ф"TTEE¸ð T–1F¨ £"TT%ÚXu]7}ÉÇcµá_àÖW¸|PIE¨¨Ê¬o÷>Kk{[_áòAEP$¡  **Â…®",cŒPFE¨¨ŠŠpá…«Ë#T„…Qªª¢"\xá*Â2ƈ"©U„*B¡ŠPE¨"¨ŠŠPE¨"Tª‹©ó¦¢! T*B¡ŠPE¨"táóY„LE¨"TªU„*ÂO/ ³§z»+¡Šp¾.T„³§z»+„·4M3ÓÛýx<ž—þ\å/pë+\>À¨™"ÚXÝï÷éK¾Ýn«­?ø·¾Â嬊)¢Õív›¾ä5W„|[_áòÖ@EȧÓiÉÆ*ü¹Ê_àÖW¸|€5P2Åù|~~»¯×ëô%_.—ç%‡?Wù Üú —°*B¦¸^¯3õJóua›~[_áòÖ@EÈ÷ûýùí>Ó—|8ž—þ\å/pë+\>À¨Éþjšfúbw»ÝJ>Hk{›^áòÖ@EÈDMÓdÇÇLEX/pÓ+\>À¨™èt:e¿‡]ôŽ{áy[_áòÖ@EÈDÑviâÝñ¯goÁŠy›^áòÖ@EÈLŸ¢Çã1niÑ‹^~öS´¶¸Ý.` T„L=­mÛqK;ŸÏÙÏ’+ìnw…ËX!ÓÝn·\çµõÑþDÆÏöÖ_à:Wøè7Å7¦"$‹Ýn÷ü¾‡w—~åy9aáy?Û¼À®ðÑoН,LEH×ë5úÖO¿~fž÷³]À \á ý¦øúÀÂT„äÒ4MôÝ?ïº.ý»á‰_ÏþÙ.ã®íùŒ~S|w`a*Bréº.ñ8mÛþ¹Ã]øßËå½Öå—m׈Ïv/pmÏg¾<ñ值T„dÔ¶mÞOΔ+^Îñ\Õ \áó ° *BòÊXZ…EÍôÙ.ã®ðùØ!Ùe)­&žÎö5[E¸ž¸Âç#@`T„̡뺦iÆ}ZÂ/N¹ÞÏv/p…ÏG€À&¨™Ïív;Ã?'áÁáWøl—ñWø|l‚й=¶mO§SÓ4»Ýî÷#üoøaø§ð€ð0/Ð  "€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B`¸ÇãѶíétjšf·ÛýîÃÿ†† ûà“ìº.<‡óùÜüëyf²ßïÃÏî׫÷„ g>˜3·Û-ÌI‡CtÒò=™ùž´„Ž·„̸ 7lõ=ñR¤ðÖ_.—ïâŸwÿçp<Ãcî÷»Õ%d„LµT„,éy×Çǵ:G·0ë;Ã×Oxpø•%ŸaØ® “Ï?3dï£2Bf%o¨À‘3rÆdæÏ“9ï¾eûýþr¹8LBÈ!óR×uo…Løàµmë XÀ‡0l?‚C„_ÌÒã!#d¶Å·€%=ÏTmð®_˜øEk8 \àh±0Ï·d–"d„Lå!£"DÎÈ™å'3·ÛíÝ#šžÏgE¡2B¦ïÉŒ8áç¬ZÙ¢q‡D…åLü@ !#d¶Å·€Å<¹ÇsÑìÂämúºšo8}'›/¦25‡ŒŠ9#g–œÌ„ÏÏ[ÿpŠ…2ÝbjÛvú“ Iå „ ™~ØÉ³)g{ !#d¶Å·€ÅœÏg¼Û’eî7}£oÿI–ã$}1…Œ©9dT„È9³Ødæ~¿Ïñ&žN'_L!#d„Ì·\ç‘ýóïeÝ;l+fúþ†“2BF†ø¶Kß^¼«•å@µ™t§ÑW5K2BFÈ|ä 8rFÎÔ<™I÷ƒMÓœÏçÛíög_Yø­ð_Þjyô^\„Œ)i‹)ã®ûNóÙ„ù¾ÂãÆ!#d„Œ ±- Àð½"6xשëºôެËåòç¶×áÛ¶M_>+ËAbCÂOãûþù‹á'á翟¤¯§25‡ŒŠ9#g˜Ì$><çóyàÃsKÜ„(,Ç—TÈ™š·˜Ò»îÿ†¿ûüdþlEOó±ýïàn· oñ÷Q(!dž?/KŸç !#dŠÉßV¦ c}ôj9KnðzFèÛæo¶,¢û7úNñ “Ãùvž|O’>™a¶˜¼×BFÈ™9,p¥8ä ›È™ðù‰ž8äiоºý~ïû[‰é*!#d„L “!6xK‹æ]‡¹nÈ•ë"!#dfÕwËT§Ê9SgÎìv»™V‚{ž !#dfº”qßdÆ¥Œ+=ÿý­CP„Œ25LNT„Øà-r.=YoŽ#ÄÞÚ[ÝHÙívî?(d„ŒÉ2EŸŒóz䌜©6gf}7í~2>9•‡Lt+)ËQ”.e\³¾úFÈ!cr¢"`ý[£æ¢E/@1ñ¾Ñ a½uÐè=Ó’ƒ2¹Bfî=*³EÎÈ™íæL® ÿQAÈ™!½¶y–µGoHç2€‚HÈ!SÏW^E€ Þ:ç¢ÑëyNÜ‘½6Åðsj¢GÐ9%!#dr…LFsܪ9#g63Ñwsâ^¾oÑcv»/©2õ„Lô™ä:2zdWqDBFÈ™J¾ò*BlðÖ9é8®èi€wU—DÈ™òB&—èñ N!”3r¦òœ‰þV–c¢§¸M³2U…̬;Ø£>Ù DBFÈ™J¾ò*BlðV8^ê!˾¦èÜoà­¨!2³†L.уlB(gäLå9=Ò)˰è’Û¶õ%2B¦ž™õ­Œ^êð#rgaÑù[ÛàBFÈ™æ*BlðV8ï~^£¯¥}JY.}2B&#§Ê9#g†?Ÿé‡1D/fß‹2µ…̬oetns:|I‹ý¿uþ»2B¦€¹‡Š¼ÎEç;ˆër¹Œ[rôîØ Ÿ„2‡L.N!”3rFÎô‰^¤kâ‘ Ñ ’c¨„Œ©-d–ß{ïŽð5ˆ1o7BFÈ™æ*Blðš‹f¼|tî7d–»†û‹!d„LÁ!“EßYBN!”3rFÎ|%O$ü>–àÝ{úDŸI–»!d„̶B&úVæºMXôle{ïk=²å­ Y !#d ˜{¨°Á[á\4zÌ|sÑ—s¿èŒñù"üáa×ëõt:…>WŠa'üüx<^.§ !#dÛ‘âB9#gäÌèvÐ <¨ z‹"×X2B¦Î‰N™åÉ”÷Qa ¾ÃZÞj…„Œ2Ì=T„Øà5ýø^µ—¹µm½ãvB˜â¾u$BFÈ2Y„Hq ¡œ‘3ræ¥ãñør–“~ª}£2B¦Î‰>™Œ×¶·BÑÕóaºBFÈ™âç*BJÚàÝèè¶’¹h–½Üã® ½Tþ÷]{Ú¶MÜÙç¥ð»£ïþƒ2Å„LÑ,zëv-È9SIÎD'6ÑYJxäŸçþzßaQúA!#dª ™è 3Îìã­Mß)„ïn; !#d ˜{¨°Á[á\tÖ—9báÑéèétšRþ92×Uô2Bf‹!3]ß=²EÎÈ9u»Ý†OcöûýwWØ5MÓ8aYÈ™šCfî»!ÛÇ[•0}ŽPá‡BFÈ™ ç*Blðš‹~|¯Zôð¹¼öû½cï…Œ±ë~ôŽ”¾{Šù2Ê9#g¢A½{=N2BFÈôýJ®s|ìã­Jß¹ê#.q)d„Œ)`î¡"À¯¹h aÞCà2Bf[!3QßN~§Ê9#g^ sÑWEP !#d~ôÝç4ü|â“ 1eo=ú.…ýî]…Œ2ÅÌ=T„Øà5ýø\t¿ß9Š>LMÛ¶½ÝnÏwí ? ÿôòÚ¤áÙ¥/d„Œ]÷oq ¡œ‘3rfÜÂCzL<‹°išçt d„L‘!Ó·ýŸiwUHÜùÝ—´7…Œ2Ì=T„Øà5]gE˜åÉôm ù’ !£"|©ï~N!”3rFÎô9N£wÀ~w…/¼}98„Œ)c2s¿ß‡ÜÉ}¢ÃáàKZ†D?~>ñ°7!#d„Ls!6xÍE ®ûnÈn÷š2*—œB(g䌜ykáav‘eX˜½¤oVè\B!#d*ŸÌ¤ïªð–ïË: ªT³öƒBFÈ™2æ*BlðV8îwʵ 2â ³¾›Ñm"ÓQ!#dª ™œBˆœ‘3oåLß…¹Fï€mÛ6±Š« d„ŒÉÌõzrïÄ~ûï;¿Go}SxÖcî~PÈ!SÆÜCE€ ^sÑŒG¤GÏÚûlEx¹\–)2Bfµ!3Bô¸Ù°ëÛ'g䌜‰:ŸÏÙw=¾Ó ÝXYÈ“™o!gÞ½$`˜äüN§h|…'éKºi ôƒBFÈ™2æ*Blð®puÍý£sÑ,3´qsÑè‘i¹^lô)…)®ï©2õ„L–¿â`W9#gäLŸèáú¹*èk H(d„ŒÉÌ®ëÂ,åt:…å<ïÌ? ??ŸÏ×ëõ9:¢‡Eù†nÚ2ý 2B¦Œ¹‡Š¼ÎE£—‚Ÿo.þ܈¹q®-—¾ ùž !SOÈdÙ`w ¡œ‘3r¦Oô¢ ¹®KÜ7“¹\.¾§BFȘÌL÷|¸¦+®lÚý~ïÛoŸ·2BFÈ”1÷P`ƒ·Â¹hô:×ëuú’£Wõ|¹‹l¾ÃçÊû´ d„ÌÜ÷t !rFμ›3ÇãqÖ™Lt/âápð=2BÆdf¢èÍ—?X%0Ñ’ý 2B¦Œ¹‡Š¼ÎE£w‹Î2c7ËûÒô&-BFÈT2oq !rFμ›3Ñë¹eÜëªéBFȘÌÌ$Z%¸GØF-Ü !#dʘ{¨°Á[á\4ºíp<§/9z‘ù—wÜžõò\_*B!#dª™‰›êN!”3rFΤsfî·ÒUÓ…Œ1™™Iô_Ï-ŠnSÿ|ngº}­2B¦€¹‡Š¼ÎE£4ËÕàŸ/2?äåÌ77þêÙ«æ„ !#dª ™‰Ë—rFÎÈŸ„ŒÙÊdf¸èsm…±¤è‰Z ¼¡BFÈ™æ*BlðÖ9^Ioâ2£eÜÀ)î|ûä£×ærwl!#dj ™!ú޾v ¡œ‘3rfÄ[ÙuO BFÈ,<™yËÜ÷Qåƒïã2w|2BFÈ0÷P`ƒ·Î¹hØXÈ~uúèún•Ìw)Œè•îŽ-d„Lm!3„S‘3rf\ÎDßÊŒ7ýqI!#dLf²‹^ÈÅMN·%ŒÑŽi±ƒÜ„Œ2Ì=T„Øà­s.7N¼àCô±óÛh‘7ßuø$d„Lm!óRß)„wò#gäL©9ÝC›ñ:ZÑWê’BFȘÌL .[IÒu]ô.oK~¨„Œ2Ì=T„Øà­s.Ú·GßÇÛ)-aôä·ò2BÆdfœèýß—?õŒqÂ{Ô7(|äL1!#d„Ls!6xkž‹f¹.Vßvʈ;b'n©¦šo-§ïÖí®Ê%d„LÍ!ÝÙâB䌜™ž3}a2úÈù°Àè¾>%d„ŒÉÌ]×õii+ú¶pßÝX2BFȘ{¨°Ák.ú£o~æx£'£w^¥/œòò¤žÇã‘XBø'_O!#d*™[ÓN!DÎÈ™ws¦oßÏaüOýKOf‚—/ !#dÊžÌ|çÉÀSr”&qê™]÷[%; !#d„L©1¢"`%cÊ ÇµeF·Ïާa>™xí‡Ã!Ì ÿì*ÿ{¹\úŽlŸ¾ó*qxä÷*Â_Ïá÷|5üoxžéyéG.½‚2+ ™—ÏÍ:rFÎÈ™q9“®ö~&$ÏÏ'ü­!OÉ~6!#dLf~¯tž„^ÀP¤H•\_j!#d„ŒQSlðn}.úÕ;éÑ&Çøx<Ò-áúA!#d„Ì3§Ê9#g²çLú€¥‰ìg2BFÈäzgw»H‘**B!#dĈмæ¢y§£/¢•–·%Ô !#dž9…PÎÈ93SÎ 9—Ð~6!#d„Ì?]‡Ãaà%)>UT„BFȈ!6xÍEsMG3Þ!Ì'O§ÓzÚ„Œ),dú.Ëc'¼œ‘3rfú3¹Ýn//½e?›2BfùOWˆ&WK**B!#dĈмæ¢Ï÷¹~iÈ}´ÞÃv<çxJ!S@È„lqÒ±œ‘3rfÖœy<ÓO' OÉ~6!#d„L–w6lU9xRª¨…Œ#*BlðÝÒn·[úž×ÏG¶Ï½ó*Ì3ßš$+…Œ2i}‘":䌜‘3y=0qùô0™qR³2Bfú;»ÛíN§“<‘**B!#dðmÞÝ£fzMÓü9/üoøaø§ð€%/{•xJûý>üð|>g¼!P[ÈrfÖ'f)a®Òü+zäÿñx pÚ ™´Ÿ0‰~ð2ááO;ö „Œá‡Šª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠª¢"€ª¨ **B¨ŠŠÖ³1k³øÔ¦‡Š[Ç•?Öà~¿·m{:š¦Ùï÷ÏŸÝn×ü+<æ|>ßn·®ë¬7Æmz¨¶¸±öSœÏç¶m5WxåÏ„ºßï§Ó)|£GïŠ9—Ë%,Ç瀌[*B€Ml¬ív»Óé¤+·Â+&|Äívkš&ã>™ï‹õ9`Ž­N!Àš7Öš¦}>Qµ+¼ògº®;óíœÙívçóÙ瀙¶:Í!V»±v:¬Ûá+¼ògÂ’®×ëªvÑøØêT”´±¶ßï‡5ßï÷U½õ¿u]}òÁ~¿ÿ~þ×êÀ7:ú”Âó ? ÿôÖ“ /ðr¹‡ç¥…†ZOgÊÂVøáXçŒTE°¶µ) ¼ßï#6ÙÞz|øçóùy÷{øÉñxþTo·Ûáp¾É~e^`ßɘÏ/yôSšc³ú»†ë;m-*¼Ò)kuÈ«èºnàú Ÿ«—ÅMxÏŸ½¾—¶†¢på»PÆ=·î\Zþðé­N!À 7ÖfÚœþ4Ú¶ýs¢ß¸'ßuÝÀ–'ÚÊMé}ÒÏyx™õ§»qFRö·~È»“÷%¼|a™§Óé­g^Bßé™·Ûí­h%·üë{b+9‹­ŒŠð#~€MouªV¸±6Óà”§q»ÝîùôÚ¶¾ù9º÷I<çóù<úùì÷ûw[†¼çO®\qéÔÄ«è»æíˆg^à['œ®ª%ì{V+9mëá?ü›ÞêT¬pcm¦ ÀqOãÝÁÒÏmÄ9z‰‹æZ9YZ†w[Â\o}âÒ² ´i}ësúýs®èívÛtÔ÷Ñ÷^I­dçÒg?ü›ÞêT¬pcmÊ»®ë»~㈧q¿ßß½|_â¯dìÿ?{wt¥,îðxK˜lÁlÁlÁÛ½³[°k°[°…ùò­ç€„äyÎ{±;£ I~‡,“vŠ7›ÍÌ»>mD2 Mk+‡ñ«±ÝnÇ=Úù¹´EtöÙ¾*qÙóh-?Àª{"B€ ;kcØ6»cwDÒöLMÂŽáétêNpÂÞ¦^ ÿ~Øý|_øÀD½ãÝnJàï*Ýï÷P¼ÝÏTÇÙv}wD²ÙlžEú÷ÙÆ×&t$¿áWñCÆ—gø£áOÿ Åžû·cM:vý€¥…Ï/rŽwœGæ§Í¤ šnd)2÷ÏáàXu¯SDagmÌÛ¬îèjêŽaÇxþv»ýú,Uø@GZ4þ©··Œà|>wÇÝiF>6rׇuh{X/¦H;CÛ‘Agßù{=^:xi»Ýn‘s<ì©î0kÙ—.2¼ÓvôF>‡›ÉÁ°ê^§ˆ ÃÎÚà¥u„Ý1D¯g|.—Ë[*~>ŸŸÑLãòÛÆó{=Ø6çäøé=‡­O[ÊùÔÈ]ß–÷zŸZGtù,Õ×'#—³Ûí¾{á¹´¥ûš†^ê!ÇE†wÚ $2^ÏäàXu¯SDagmðÒ:^Ü6¾ÏøõÙº6m-öÏjKåâÕ‘Ñ Øº1QÔ˜]ßöÆÉEÚ”D>KÕñp\¯§;Û¶(íÒ”Oݾ‡Ÿ3 S!Dnu>?Àª{"B€ ;kÃÕñPÌ×ïTOo5j|ó×àYS¹ð'Æøàä¨-ú‰yqÌ®o|«ïk_“åÈ"m{Ž2aÞšpi‡Ãa©3}À›=7›MØŠ°wÞÞº˜m4¦šŠ?zó9øVÝëdØY°œŽ|0æ¡¶TOo}º\.i§òkKå"çiœçåÛívº5i|Š*æ/ö-Ò˜]Ÿ°<ÚÁÛ•viI´M/¬XÈù|N˜Î9¼Óv¤EÎÍ›ÛÁ°ê^§ˆ ÃÎZß…\¯×‘ï×KøôÖ›)æjŒZ"KLÞ™m g§[“ÆÍ95åà'³–gãa<øy®Æ¥õzmå:¦î+”ÌóÅ 9TA‘¶ÛmãN‰¯g²:øVÝëdØY‹ÿúívkßîû|Ít]¼Æ%|ªíedKmiãLª_c‹ÁkòùçÆ'_Ó`Æ<Û5uD˜ÉÒº\.ÌÇãqð95[)µ=DÙëy½¬~€U÷:E„vÖ:>»Ý®×ëù|>_ƒ†øÇs&êâM÷$×°Tn¢-mLi‡½ÿñëßjŒGÇ?ñÔ¸§&.5rÓ2YZZá¬Lø8áÈw†ÎSJm¯bìõdn?Àª{"B€R;k½¦ï›¨‹×˜ƒŒœeô©ñ‰¤¯©ÜD[Ú¸™_ŸB¶&QËȉÛÖ'f’Ï´å™óÒ’ §çét çi³>ì²¾/Ñ›¡”Ú&ã9as>øô:E„¹uÖz僿“Å×ÏçñKÎabÌ—aO! [“ÆltºCqæò¬*"|¹ßï§Ói·Û¥š€´×)6u)…Ѝ1ð˜^n?@å½NÍ'€Ü:k}ŸÍùl„|»ÝNôÔOcŽ\™akÒX¤ vóE„i=p8…Sx¿ßÙ×ñxN]J»Ý®ñ½s¢ævðÔÜëÔ|Ȫ³¶ßïï÷{ªÕ¿uOE [Ã7o%›bÌ1‹®kiK ‡ú34lÌÚ:,ûÒ§¶W.ö 5σ Î^§æ@>µŸŸŸÃá0&zË'8›zákÿ™×Ìå)"üêv»E¾Á0fÞÝIK©ñéÚ1S çvðTÕëÔ|È¡³¶ýÏñx<ŸÏIÊο2"Âu--7áÄÿú\ã„•Rãs¾Ááp˜¹¶4Æ ù°–ÎZ1«!"œgýE„9Ÿ\“:ŸÏ»/æy½)Jéñx4>ç~˜ü,3ÆI;J9Œï¬³"µ¬&»XD8Àår3×è¥þî˜×#V¾Cæi´‹2쬳"µ¬&»XD8Ìv»m›‘xþRj{°ñv»•Q[”ÑëdØY+f5~~~¦H ~[ÞtþÜ"[¶h@:“0"Lò>ÊŽá0ÓÎ\JçBp:&:Ø<øVÝëdØY+f5nº^¯ã—’É3SƒWfØšLW¤99"Âaóù#ÂÇã1x¾Óeë€ {"B€ ;kŬFã+É’À8fNË¿ùˆˆpê“+쾑yV8h_Ø÷VJmSìÎ3ág&?Àª{"B€ ;kå­FÛG‘³/†t„½f5l[H|dV¦#š‰ÛÈ2oËh^±i|¶¨1ñœÿÈ©'"ü›’_.—^oî |ÇÃ}gñVJ§ÀàÙƒûÊäàXu¯SDag­ÈÕh{mÙ+( xË Ãÿ†väqAø@ÚÞñn·{®ÉßG GÌÊôšbq|™w¯¨óùüV°aÓž›Ó4ÍäTþ=‡Ãsg½=Öú<ü¯ÂºÀçrzŽJ©û°™g|)‡ƒ`Õ½N!@†µRW#y²0àm†ÓõˆûΙ¤Ì;ÞË6O_D˜É€Ì°|pX)e2¾´øÁ°ê^§@†µ‚W#aJ8 œ®w<à½r©Ê<æq*a a8#ú惿kŽ?øVÝëÔ‚Ȱ³VöjtÏ8:]$7]ï¸×ü¢S”ùý~ïxWã0‘ï•~õx<Âáúu‚Ð1~~~†„¿+—=øVÝëdØY+~5n·ÛàQýðÅ·7µÜÒŸŸŸëõ:l}ƬLò2¿\.a[ƇM‡Ã!~£D„ñB©Æ¼L°ïþêû:Îñ¥”áøÒ"?Àª{"B€ ;k•¬ÆõzÝívñ]Îðáð•ä[ûüÕív‹Ÿu»ÝŽ\™‰Ê<¬Õ€Ù\ŸáH(e·¢ìˆðå~¿ŸN§^ãq;øÉÁ‘¥”íøÒÌ?Àª{"B–õx<.—ËápØn·oÏ…ÿ ? ¿ ð’µÁŸ;Ÿë³Ùlv»ÝétZÅsF×ë5¬ê~¿ÿÜç¶<Ëö|>{ljA¡ðŸÇÛsOµ=ÍúÜ_á3á“ãƒòâ9øþ@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDUµ( €†¸bŠÎ!Tåv»ÏçÃá°Ýn7›Íç)üóó³ýOøÌñx¼^¯÷û]¹Úá€ÞІ¸bBIgˆ*ék‡NtèP>µw»Ýét ËQ˜ «€£½•Ÿÿ™‹ÒFS³Â¾¡ÚÀ‰à(@o%Ï‹¯ˆô¡Ài唄ø#_ *ìqïv»Óét½^ï÷ûßï>ðÃËår<Ãg~~~œò¸h!Ñ–v"8Ð[YËÅWDúPà´rJBü‘/"„’„.s÷û8B—<ô¬ãx»ÝB÷|³Ù8åqÑ4B¢-íDp" ·’ùÅWDúPà´rJBü‘/"„b„Þtǹy8zu·ßÜï÷°§<šš(ûZ± ·RäÅWD€+&ÔvÖˆ)ý°ÿ÷õ¯ø=m'æù|NÕ¯û+Ž145ŽÂ±¯øSuNÿOo¥èÞŠˆ\\`¶³FDHÑÇü¿oÿ ÞØŽ›rC79ùßzÝ£ë0CSSá(û€?U§ˆPoe­_!®˜PÛY#"¤Üþ߯¥nïù|n{£ÇDñù&Gšš GáØ×ü©:E„z+k½øŠpÅ„ÚÎ!…íÿvü+r“÷ûý¤“ö€‹¦kœÂ±¯ík€ˆªSD¨·²Ö‹¯ˆWL¨í¬Râ¡þï×åmõf³i<%ï÷»C45ö5À\U§ˆPoe­_!®˜PÛY#"¤¸ãüßÈ•œà«Xùûý~>Ÿ÷ûýv»}[ÿÍf~x</—Ëãñ˜nÂÂ×áçç'üäp8„ßNºvP_·Û-üÝð×Ã>ú»JÏýÖ§†¦f(óëõÊ!lòö?«ýüÕs7%ˆË¼pB±„-}Ÿ’ÏÒ%v:ÂQ4Ñ_î‘·ƒóUŸÌ¾ÔÜ皺®vqy®@øy8 Cqai½•7¯ ýçU>\qÆ·.D„e÷b hî©°"]°\Oë·mKÃñ~Øw)”I(™Ýn÷¹´ðà -çq¹×Á~ûVù4-–œÉ¿fpÁZK›\DHAù¿½þÕp‚çß j»£¸Qh„knÚÕ l›øèSh›½.šá¿xò5ÅÞŸÅlEX«ÐrxkÏ´ ¥®’Óí¬g³³ñð‹öéÈÈ;ÛÚ&΀’IÕçêû×ÇÇWh½öÅ«GÐØáZщ°l]1ÅÅ%ì‚×T{c..@íráúz+1ëš ‘{hkgYP½¾>ÝuÁ¦<{1½ÚcÙ6w3é;¤]àlE:é½l¸ŒÖoÚËA(ä¯ýîPmqÒgM•ü,Îd\.¾H_ót*ËœYªfX㫎ôDDHÙGø¿þ‚g{Kd¸F^ž¯I¶+,dØ¥<¬yXÿe›"S/p‘ôu+B[e@‹+òN¤µD„ WrðÛ2l„ÆgdÇjŠÌ.4_ÿõðÅ1­ßÈ}sF¯èDX¶®˜îxqòh—‹××[é®{ÃJÆ]þ½ÄOÔ$Nøõò"Â<{1}Ûc6w³ê«¦ZàÌE:õ©½`¸¤Öï×ËÁápè»m…Žœ^ÙÜÈ^|¶ãr®°¯0kÙq¹U Î,R3¬÷‚UG€""¤àÃûßÁÿÊ>Á3¬NC`ü•"ÁZ°Ì9"\pulEØ_ƒÛ®‘+SaD8¸aŸ[`@£7aK¦­W›I þÜYщ°l]1Ãñ  +o—‹××[é¨{O§ÓàÚ»cdxÁažðü×Á<{1ÃÚc¹5w'Í\–ZàüE:Ý©½l¸°ÖoÇÊ\.—T…<ø~õT)¡q¹ßÌ"Â)g–ªV}Áª#CRê±ýïÈBÛÅ=f®’9…KL·ÙLÔˆ- "\vµmE’Æ×¶Váó±¾ Ѭƒï9LÒ’Iø×‡U×›Ð÷×ËÖsذÎΉˆp}½•¶+B’Ô™ZuF„yöb·Ç²jîNš¹,µÀEŠt¢S{Ùpy­ß¶ò¿¥¯©c’äbc²9ãr©j’œg–ªÖ~Áª#FRäýo’k/‡ŽJ8Ÿ œÓ¶C·‹¦k‡¬="\|5.!ÉÈÌóެ9¯’3÷ g§òiôæ%mK&yëwÀ0iÛ&„S¯ïƒ+:–­+æ<5°aýáúz++6æÞû·¿9ZF„yöbÆ´ÇòiîNw~-¸À¥ŠtŠS{Ùp‘­ß¶j|ü…õ›î«çҌ˦œYªf(à‚UG’""¤¼£úß„ÿV]—×w¡L¡»Vòt:]¯×¿«z¿ß/—K¸Ätt¬Â¯zmݰÕ? ?™b¥½¤LvP¯†Ês}ÞV¦;²éžÉjÕa(Ð׸þçï‡Cᇟ|ÝMÿôœæ+“@÷í—á`þÖ[ ÿûõ•⑽{Ú±Ð&;DŸ+~ØÝ\îëp­èDX¶®ÐÉ Uèó ý¼¸D¾à^“VÕE®¯·Òëʶ¥ï•%Ôö³5´j‹³íÅŒieÒÜôüZj élwŠÎÓ.µõ_Ρ ¥ú÷.—gw¯£béè XZø|‘ãraiŸÏsd#uá0óR×´US¶ƒ3‹Ô e\°êSD„vHÿ›üßzK#\kº/ô˾æ#¬^ÛmW¡sçpÇh||;¬FÛ…8¾ˆB›¤£]·Æ^R>;(æ*–Ö½>á·mÛÒëv¬é®q©ú†ÏnW¯ö:æ¢ïub& €¶m ÅÙC ¥÷yŸÛÈNMÌ)>ÐQŒ¿k=a.Ï!«º¢ûxˆ¿¸„“ž”ÒK®¯·òõ²Z¡¹ÛÝÀW–Žš<þaÉ9#µ4€ èÅ k-ØÜúüZj é ál à‚[¿1åüujÍ^³Í ^Zß'¼ò— ?œ¶4…<[Õ”íàÌ"5C¬*z*"BÒTÿú—O@ùµÚî2J¢íaó^“–w´B#/7m«Š®ï­ËáZRD˜Éê>€ãwSG›säÍÀ9ô‘Ÿw~õ@G¿ ~™94ÚnQV2áPKÌçÛʰ×3€m“ÌôšŽ,¾…ÖxÛÞëVÌ’"™늎ãaÀ̱#/.ÀG}á_FÑdν•¯ckã73þ¢ ",¬3¸=¶lswêók‘.[¤“F„37€ ný~}r0²”bž”ŒNÚ–Ö«Ï’ù¸Üv»í;.×v÷ÑlUS¶ƒ3ó× Å\°ªè‰IPÉõ2Š»oÍí~º|RmM¾/5nkÄtÛÛVc@#6ÃÞ÷˜f²ƒ:®S¡¡Ò÷pmkŸÄ;­n„dü)oåÐhë5‚÷)á`‹i¯¦:eÚîÃŒ_T䩃'p[WD¸H]Ñv<,rq>Î(ÿ2гí­t\Yš xUbÛ\cò¦•vR&]rþ½˜‘í±›»3œ_‹,pÙ"("œ¿\vë·ãéÈ^aG[•viñÕ]æãrƒ_­˜ç%oñÁ™ùk†b.XUtD„¤?¨äzÿf5Íé€÷„–És¢ì·iϧÉïû¾­—Æçnb®«1æÕ'ÅD„™ì ¶­ðŒçSãP#ç Êv„¤¯Æ;÷â'*Éao·›¨‹9´úNóòÒxGh|K8Õ©ë:²ª+¿>¸“û+"„ÄÝÿòšà4ÏÞJ[Ý;¸=uEãñ0x’%Ý+˜àjá_van½•‰êÞ¶yØfX™J"Âü{1ãÛc"Âä ŽoßúM¸2ÑÏà‰ÇI™T˜G]Iãr Î ®ôa×~Q2î ’ëåþþoÆæ´gwè˸1ìs5â3š6÷­u7JKcä`Ba&;hŠbi›G½¼’:ÛÒcž¼KþwG>ÅÐv;eû¢†ˆpp]ÑÖ›yÃYœðU>ëêÄ}ÝðÐ&]8ÙS5G'ªL~ó›Ø*ó!ç*tÙ‹ GED¸šÞʤuïàùÉE„z1 6wg\j i©aa­ßò"Bãróìbg/°Œ V=!eØòÁî÷û³¾ÛízòÝ{ÎM‘¾³eÞûÖ6BNØ)vߺ:qïÂhÚ«¡~Ù"mlô†ú3I%Ükž–Ùö…ˆ°ï·Æ÷w4°aýýájz+S×½‹4‰E„%E„ó7wg\j ia ­_¡ˆ°òÁ™Á ,ã‚UEOEDH±Ç¶|0±çsñ1Åwσ÷ϼVÑåÏê"žÉʳó˜íIh>M×È_]'®mÆû˜iøîù|îÕtÃs’ÊùDȹ ͹̹:'"ÂÕôVò¬ÒE„z1 6wg\pKi©aa­ßò"BãrÓ­R‘ƒ3cXÀ«ŠžŠˆ’oùà$î÷û×;u;*pM‘Ì/â"ÂÕÜn·.âY5ß­ÐKh‹F> ("Ö6H :E„«é­äY¥‹õblîÎ6¹ì)Rá*Z¿"Bãr•ÎŒ\àÚ/XUôTD„~„˧ÒýÚÙŽ—SkŠd~®k„¤Wôççg»Ý>§nN§Óõr.œaÂFŸó?æUé"Bamƒ$À ÓXD¸šÞJžUºˆP/fÁæîl£‘‹/pþ"®¢õ+"4.WùàÌø®ú‚UEOEDHù¹|p*sJwÌÞ“I-!"\ESüWDØéñx|me=gfˆ|ÏfIákxpä,›Í¦ûA¡ˆ°¶A`Ði,"\Mo%Ï*]D¨³`s·ø^Þ‚E*"\Å]IDh\nÌwËœIµÀ•^°ªè©ˆ©â8—N¥­n?×1¾]^ç1·’ãñØqOÚ€9ÛË‹Ÿî÷{(¯sŽu´B—ÚpaUèø^Œ6¬¿C""\Mo%Ïþ‚ˆP/fÁænñ½¼‹´Ôˆ°°Ö¯ˆÐ¸\åƒ3i¸º V=!µêòÁItLàÓ«Ú™Né Û±9o{KE†#$ápí¸9mØÁ\jDøÖ½\.¡ßë-3ðëª6ö/ß"ÂÇÕkº˜b†=þ§±ˆp5½•IëÞpEX$¯¬6",©³`s·ø^Þ‚EZ@DXCë·’ˆ0“q¹5v*‹œ™î|\Å«ŠžŠˆŠŽvùà$½¾'þD Ⱦ2lÇ–=¾]Fç1«’¶!¯˜‰³ÖU8SWb¡$C¡}¤­a?Ý)³Ô(âJ‡ s®BC¯§¤A`Ði,"\MoeÒºWD8Ý’‹ïÅ,ØÜ-¾—·`‘–Öú-/"4.—ö»ÅÎÌv>æyÁª¢§""¤®^>8_5ÒöùÆgÉç¿$Ãvl&ñLvPžm›¬FHB«iØ#f«+œÙ„ÎHÇd§Ó)~G´}¸—°a½ á‚uÅáp˜¢ ÕÀ†õ·˜E„«é­LZ÷6ND–ù£Fk‰‹ïÅ,ØÜ-¾—·`‘ÖÐú-/"Ìy\nañƒ3‹œù\°jn-‹)÷˜—.Üénì‡jæÕΰ›ÉE<“”gÛ&«’Ífóù•óù\^á̬íÀ¶;üO™1· vw4fE\éPa&uEc°óxH%çTÜb®¦·2iÝÛ8ØÓ~êÅ,ØÜ-¾—·`‘ÖÐú-/"4.—ö»ÅÎ,x>æpÁª¹µ,"¤èÃ^>8y5ÒñöØÆÙuæH¼±;¾E”ü">æfÃV&“”gÛ&«’)^ "|êu`ã)3òŽÁ§Æ¹5bnª.XW„J{Š*T`¶Þʤuïàö›ˆP/fÁæîlå3çwñ"- "¬¡õ[^Dh\.íw‹œYö|\ü‚UmkYDŒìˆu?AÓø•™ç4hkÇæÖ3ÇÂà•ÉaåÙ¶É?"\][:O}ë‡äC#ë(á²uEc¶»àM¤z+™LÜ}¹\†…•¿" ,¦³ŠîðÈòY¤ó»l‘ÖÐú-/"̤Âl»F¯®SYüà̲çã⬈¡ì|äõWÓ|m ¶Í&=ó Kë0òu„cêÀäS ^™LvP†m›)î ¼V"Â|¼ÆSfä\£wÊEÞQ¹HD¸Ô‰a]‘ü.ÇP÷j`z+söV&º²Œy÷Snát×ý1K.»SØæÓù]¶Hˈ‹oýæ<.7²m ",,"üuÇìB%,"„’Nðéîjk¶}mT4Þ@„.üœåÓØ"9§Á˜:°ñMÍcÊdðÊd²ƒ2lÛ žøq¶î¤‰F9ðڦǼ;ÂÇÜÃ0õ¾ÈêDȰ®h»ËqØå8lck`z+ÓõV¦¸²´=+ÙZX0G˜îºŸ|Ée÷b [Ã|:¿Ëiañ­ß"#œÇåFÞmk¢Q!IJXD…à›Í&I'.æRy5okòœŒâoõë¤=m-¢ÁeÕ8z|ØøÂèa·Þ=ÆùF̼ƒ2lÛ4ödÃ~_¤ÁÓ¸2cvP[ε¢6[8n“ÔuÜî×a7ž2ƒß Ý¸sãkƒ©÷EV'ŠꊾghèT¶Õ䨀ÞÊÔ½•äW–Æg¦âïN\0G˜îº?Å’ îÅ,ÞÜM+ŸÎï²EZFDX|ë·Èˆ0“ 3ùݶ‹ k?83x â2.XUµÉE„Pü êÏ„][Ì¥¼íz7¾5:¯îưQd§ïí¶ AÄש"ËŽ›îâW&“ô›Ù°ã\UIÞ’<`­ÏÁaͤpÎŽ?€ó¹q7”ÌÈö‡,ºo§l;eÜÙV»ÆŸzSN„<늶GEžwS}t%t‚º‡G4°½•©{+i¯,m+¿½ æÓ]÷§XrÁ½˜Å›»iåÓù]¶H‹‰Ëný–fRa6®À€‘$ãr çÚ-ipfÌÕ\°*l“‹¡ø<\¤F^ëCÝÞxlß©$Ún3<ÅßFHdåÓx¡é;ªßöð`ß:°m*¤Ð剜 ¾ãþÉr;è7³aÿ¶&ôø»ì¬U[·º×Ê|î”"‘ Ѷyr¾¾¡mt¢W}Òv ÷.›z_du"äYWü¶¿ýêÕ£ §ðßC4`á'áçWX l@oeÎÞJ’ç:Úç½Z æÓ]÷'Zr©½˜š» åÓù]¶H‹‰Ëný––4.×wÎЩ,~pfüü««¾`UÛ&B 'x¨ŸÃU¬×›ˆ¿ÞÙÕë&™¶—m½ºñÓëõÚ¸b1—à¶Qý°-_¯_a ;nYP¶oh3wï©°ªÝ ò+“ÃÊ­×ÓQ&‹¼P»íð‹iÌÇ´?WþfŒgh+“˜Ê­ãÖÙ˜ùÓÂ::h½°©÷En'B†uÅópŠîÐÀôV2ì­´-d·ÛE¾¸ûù¦^›¶`Ž0Ýu¢%—Ú‹É¡¹›V&ße‹´¤ˆ°àÖoÁa&ãrWí™Çå·ìÁ™ñáÚ/X•·É`@ 'xhÅ…v¨¥Ãýíâ*äðÃð«˜;»¾¶ä›_W/´1žëöÖŒ ?9Nãgªï^‡çÐÄÛ­na}b&ÁPv¯LØ á ùY³Œ¬sØA¹ ûw„8ŸG˳B«æ¹’¯UÇr¡úy£æóŒžè˜É3"l¬èk¹îc5ò†·îgŠÃ:„¼­ÀstW°}_Ê3þÈêDȳ®øm¿ã±¯¶ž‘v ·2io%¦ü¼²ÿ]ìs•BÕݽ>}gS\6G˜èº?Ý’‹ìÅdÒÜM(“Îï²EZRDXpë·àˆ0“ ³í)¼ùÇå·ìÁ™Táz/XÚä"Bp‚GöÜûæƒ_/£³U>‘· ÆÂø:°W#§C[3cu;(·^OLûyÀö^«Žq•™àÌ#Â1zM’êþÛ!Z¶×¹Šá7ˈðÙ{¹©µ£;ùì4i`z+ó÷V¦[™i.›#LtÝŸtÉåõbòiî&”CçwÙ"-,",µõ[vD˜É¸\ò~ôüÊ‚g’G„k¼`i“‹Á þu{X>8¾c˜ªòßyÂø:0ÉìÏ© RUÈËî  {=½îòš:é˜,·—Ãá0ò.5"ÐþLØ»ζ/²:~sŸ'é°² 5ùëÚª è­Ìß[ÉjhkñaŠëþÔK.¬“Us7•:¿Ëiya‘­ßâ#ÂLÆåÆ×ÏÛ–êT<8“OD(\u›\9ÃRííPyN:|¸üE¾‰£Ûý~Ox¿ÍSß¹©ÇŒê¿¦LõÔɘÑëÍ +äwP†½žÁ=Ù‰Ö*ì1§ùëFÍßÌš’4Èÿ\¿uÏ8:uëwž}‘Û‰mDøêtÇ×¢Ÿ/s×ÀôVæï­L±>}çMu!ȳ<õ’ ëÅäÖÜMeñÎï²EZdDX^ë·†ˆ0‡q¹Ááòß»–íT–:83lå]° &"„<_ç¶÷ªú¾¯Çú*Ôö㯠ÏY¾‡MLù‚à¿BæïßJøÔÉ€¶ÙÛI^!/²ƒ²½1rÀšn­’ܨù»þˆðy”ŽéVŒ|Üàm¸cp+æµì9심N„Ì#Â×åøù¾‰·+r¨CI>_QÑxà5މiÞz+“öVÚnàÖ•ÿ˜¦E9Bòëþ K.©“aswÙ&eÂÎï‚EZpDXRë·’ˆp½ãráXzÝ-°x§²ÈÁ™Á ,ï‚U*!ÔÖûîû2ÜÆîí¤wn\¯×ô=[ [ƒ‹(ò¯‡rø¼ 8m?ñÖ–˜´[:óʹ×ÓwŒh굊oÌ?ÍÒ3Y5žO% ¨ëÆôeÚΗ^«ÑX±,~Ö¬âDXED˜peú>) ·’ðBЫ™Z΋_Üól϶ä2z1Ù6wçoRNÔù]¤H‹ËhýVV>.—ª< œO{a¬òˆ¡æx¸f…Öu¸ònÿÓxþn6›ð«ð™ðÉñ}Û¾m’p¡|®Þçµõ¹báb.4ɯ¯ë×Û­nÏ¢¿j»}eŠ:0즰™o;èïMwKB î ¬„p8;> áU¡”æžð<•Âo'-™Pc¼Nä·ãäë ´Nß ŠTÉz+“öV¾6ìŸ]†Æëû×^ƒð‚-Šòz194wSɤó[R‘jý²Ò ³íÔ×6ÿ*ÚàŒÚu-D„sŽ$@/¡ÿûur-4ì´~HÒèI Ó츩@Ã@ë€)½"B# ¸²¨~U¿ª_€ªj]!€¦,‹;NŸ—•ý~¯d4ì´~˜¢Ñ+"0’À²n·[ãeåz½* {­_¦hôŠŒ$0òŠp8¿6¥m„d»Ý*[ {­_&jôŠŒ$䊰ÙlŽÇcühÉãñŸokW»‰@Ã@ë€é½"B# ¤½"üüüì÷ûãñx½^߯LGøáétÚívêÃá `4ì´~˜®Ñ+"0’@VÍìÍfóx<,€†=€Ö/yÖÞZÑ€JU€ {­_­_€zjo! RU¶™½ßïhØhýmí-"P°™ýóós½^&€†=€Ö/yÖÞ"B# üu¹\öûýf³Ö~Þíva Š@Ã@ë€9½"B# $ñx<®×ëét:Ûÿ4Þ/~¾ÛíÂgÜ8  a õ ÀR^!”JDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDU@UD„P!TEDUo®×ëñxÜï÷Ûíöçççít? ?ßívá3çóùv»)1X!<óù¼Ûí†ãá‹áëa!Jr¾¾+{Ö5(zÉÿâý~7‚¡MÀ¯ˆª:Èûý>ÕɾÛí.—‹R…<¯ïŠÅ€u Jüüü$ÿ‹‡ÃÁ†6¿"B¨ØãñH¾uäÇ£‡ !·ë»bY°Üì6(‘öAÂÐI1‚¡µ@dk\DEº\.3œûûý^QC>×wŲ`¹Ù#0lP"탄Çãц֑­q!”g¢‡þùõ]±,Xnö ”¸^¯sþ9;E+@k\Fåy<Ûí6æNÝÃáp¹\>;ã×ÿÇÝnçô‡u]ßË‚åfÀàA‰Ð…Iò·Îç³.ŒÖ½Zã"B(Æf³ù:5èív‹_àåré~&Q™C>×wŲ`¹Ù#0fP"Ƀ„???F0´ÖèÛBº³¼ív{¿ß‡-ùñx´½ÔC±ko)X|Pbüƒ„‘ºô 5."¤ìÿÏ¿ZœN§Ž“ôx<ŽÿA¡£ X{»H±m÷£€ŽMÛÜ&É$üœP¥íJG,µtUD„TÝA¯%%¼Ýngèù|Nø·î÷ûß×:à€µ·‹ i{ ™/6‡‹oèS|þp·Û þ+×ë5ò¯¸ôPQWEDHíô*ŽÏŽW¦Í_^:à€µ·‹ ö@þ):"ümyKÂà7#ü½‰ñõ¡K?µwUD„ÔÞ;/?%ìxéF’ùEÛ<ïÔuÌko)¦è„dµÀ /¾ø=s½¾çTy¦.ýTÝU¢w^zDøóóÓxbn6À×v‘b`¢~Èâ‹Êüâûùèß° ?H|ÍYêÒ@Õý!µ÷Ë O ;!¼^¯9¯ùãñ¸\.‡Ãa»Ý¾¥œáÃïÂÂÇæ_±PªûýþmÈb³Ù„†_õZ¥ûý~:v»ÝçÒÂïO¦dÓrv»ÝŽÇãç±ý:°s^ù°ã^ÇIÛ¹>0rÿ†ƒ-ÔQ¡”ž•@ãá³Ä‚ð±PhIލ56c²­-W}œÌÖYd!ùJ<ÕøÁ¾6>øê MzéÿÛjú¼]óÕ†™ÿ=CâyÅ?_o³Ù–LÁ äßRS§¼ðˆ°m\}Ø=óöÝn_Ä;cê®Ðk|%Jã ®_{ú¡Ç×¶w>wÖ˜Þ_©›–ür3r1_Eúòmö¦8yù<‡):^lÚø¨r¯±‹g&Øëôÿü‹}³ìÙ öm»˜³M9ȶƈpÁÚ2·ã ¤žIò¯¬zPâï¯/‘QÚgâó÷´è°JƒoàyMüºñ—éyÚ!F„‹·d/X Ò¾‰ˆz{áå§„m]ÔÓé”ÛªÆO¥3KOªÊ0tóǯØëiÆ[ —1(`ÓÖ¸yð Xòò ;eÎö@òæÇf³ùZt«‹—­-s;Î ëŸ$ùpƒ?ÐÄLðØøáÛM2©.Ði[M}ïäéØŠ¾s†ÌÖÉ*"\¼%“CÁ•vLD„ÔÛ/?"üšìd"y‡(Õ¸w’‡§ž7*'éu~Þö\ó¦­""Lr'ùó¸™7çwš;™ço“|­ Ö.^[ævœ”×Eü±Âú,oŸö áçœ*ŸsäyWUßÛçÚ¶",¤ï“q¥ïë Ö.ޒɤ`€J{%"B*í|—Ÿ¶=Øõu®¿™u¿ã,ô×ÂÞÞœþ7ü°»+×÷IÉø -ôà.—Ëߘõ¹>½ïŽU°´˜wºÕ°i«ˆÛFÞŽêPJaÛ»ÓŸ¾ï¹9_Gºžû÷ïàUØŠ°’Çã±ã¨°Î¡¸Â2¯ÿùûáÇã~Ö!¬I÷ØWwÑ­("Ì¡¶Ìí8(²£Òë¥J¼}f؃„Ÿ-„ϧóÆ_ »[M›ÍæyAüh>¯‰Ým˜ð«ø§ÒÚELؘé^ZØÌgƒím3ŸÃ˜÷ÌÓÊͰ%“OÁ•vID„TÚó.?"lLÎê1Žõv»ýú´c÷ûÜÇßyÛwŸ^ø ^Zß=Xꦭ1" ùãÅIžöŠünã¬\½¥°„Æ88rŸƒ!½NáŽ×Çôz@uºfÌt¡íœµenÇ9@©}•¯¿*~P"òc!ÚgòÒx«äÈ tX¶‹ZÌú·ó ˜ÉT“ìôm‡„ÍÌÅ.—KG¤5O›*·–LV TÚ2S7׿Áÿj›®$¾§9ƒ¶¾U¯§ZÚ&º OU†ÂŒ¼‰7æNΰÂ#—Öë=¥nÚº"Âý~¹iácmQ×øýùݶ½ój¿Ïѹ¿›óuÃ9à…›_ÇèFF`sŽf^[ævœ諬µ¯ÒÿÙxëèÚÄ | ¡¼Æô¦®UÚŽ«ø-ψ0ŸÚ2·ã@_¥žˆ°q–ƒ¶w(„«IäewÌ:I[ý©-%Œ¼½3r¢am¹¶vÈàgÿ3ŒiÉäV°@¥ý#!ºÝ•E„ù¼é©ñÆËÁó 6>÷ÿâÅ‘÷‘~]“´K;#ëù6má°’i;;z=u޶àÒJÝ´²#Â¥ö{ï†+,"̪¶Ìí8ÐW©-"l¼„ý½c­ñIÙà+Ñç,‘ãïri|²,&6šèzÚvçÕÈvH1áà–Lž TÚ?¢Û-"œÝø'ãâ»ê‘wó&,«1IåK+uÓD„É×§m–Ñ2Ú6‹­ [rnµ¥ˆÐWñoÙˆð÷Ûƒ„Ÿ¿íŽØ†]‰Û±ƒßBØ}eŒ™Ïa¢ëiãlçãÛ!"Â< ¨´$"D·[D8»Æ—М7ï©qŸ˜WƧ-«¬–Vꦕ.²>s%ENÕ;›ÇãqýOXÛã¶ÿ³ÙlÆ7Er‹s«-s;ÎôU*Œ;^Øø«î'³†]‰÷Kòîæa³úOt=¨RLD˜[O³ HÕ„¢ßŸ´×›×ÙúżCd˜L&çÖÓÁ/i}O̘„}R·Ûít:…uëNS5Er‹s«-E„ót]¾þªøA‰î¯l·ÛÆ„îó˜‰.mwÚ,Ø`˜èzÚXÎãÛ!"Â< ¨´?""¤–~¶ˆðû›8fÓÖ©¿äL&çÖÓÁ/i}¦;1¸Ýnûý¾q2̪"ÂÜjK!À ý–^(uPbØUlÀËÝ^ Q™3"ßæY°@¥]!Uô³kL Ûú­K øÿÕ8ì?òåìO“ Í?9ˆ°ž~IëÓø˜Þív›¹~uÔÃn‹­ [rnµ¥ˆ`ê~Ëà6(1¸³3Ï\"•D„Ýq*"̳`€J{%"Bªèj‹óŠ'­(rèV‹ëéà—´>‹_ÁÇtn‹×NYøåsh7:-1½Ž²SÂaׂÆ t|^ +‰_l©aæ½l ØŽ‰ˆò»Ú•¦„o°ÊáÍb¿"Âõt¢s^šˆ0ù×—½‚ßn·š""Âuç%uZ’eÕƒ1_ì~OqÌ»~E„+\¬ˆP³ ˜º5."¤¬Þv¥áñx<ßN¶ãk馉ëéà—´> ^Á{åƒ???Ûív·Ûÿs:®ÿ“áq•çR>‰v# ÇßÙ(5%|-8ŸÏ#!ü®p±"BÍ*`êÖ¸ˆ‚zÛõ¦„msïļ˜/Ûq€µtÓD„õtðKZŸ¥®àÇ£ñ{o÷6œÏçÈ#ŠWth7z,ÿÌøõ JD~·­ ùažè|®§"Âu?šU@ª&„ˆ‚:ÜõF„ǣ피ßïëXE7MDXO¿¤õYê ÞöÈóó–†óùj³õWyŽ åsh7º+‹,$ÿA‰Èï¶=HÿúuaßÅöm˜eÕêÎ9"\¼`€J;)"BJîpמ¶½žãt:-»b·ûF>Ôí~¿{pRD¸º¥‰“}º³CÇÍ ûý~ØPIIanµ¥ˆ mweñEe;(1¦gÑkÖ”„Ï‚÷aNt=Ýn·câ× [Ý™´dò,X Ò~Šˆ’ûܵG„‡Ã!ϹF'êý¶L¯3шpuK&ÿút'f‡¶›ÿǼ5µ¤ˆ0·ÚRD°¯’Õs”ˆÿúçt¡u±Þ tV×ÓÆÍ¼\.Ë®m©áâ TÚURr·»öˆðv»µ• v`ƒý~?ѳa!òáê–&"LþõÝn7ÿCǵÁÈÛJŠs«-E„©ú*y.3ŸA‰ø¯¿ÍHз1ì¯7¶šŽÇãJ˰Mã§ã7SD˜gÁ•vUD„Ûíþ¿ÆIê"¬›Nã«ÇÆ<4ôÒ8œÓÛ®ni"Âä_ŸîÄìÐ8r¯›ÿ3?®F.9·ÚRD¤¯’ùbs”Èü¯7^ w»]aeØxCÑøŽ¤ˆ0Ï‚*íªˆ)³Û-"ìê½.þ aãwI¦?mŒDc¶TD¸º¥5~wÌ;ÚD„'æÔñ)^åSRD˜[m)"ßWYË’—”Èü¯·µš†½F9Û2l›”fäfŠó,X Ò®ŠˆŠö6ñÎÛ óR}Øß òŽ®Öüý)áRü1Á·ˆ°­ºÿb”™‡2JŠs«-E„¬¢1°Ôz©¹F§+ÃÆ{Šœðá·”yH2,X@k\DEêxðp8L÷wŸCÐm¿m|ÇÈÙóçÍ‹œðGD¸º¥%Ÿ RDØvbN:/ñ*"‘O5Ž\«¬jK!«h ,u^êAÂéÊ0ùk£ÛîI[°¥·HÉgX°€Ö¸ˆŠÔñ áø;Û„ÅvŸþ¯¤Š_ó”ˆpuKÛn·iSoalj9ݼĹM4:x²âéÖ*«ÚRDÀ<üÿz¸nÎæüeØ6óÀ°ç%C›ª±­UaD˜aÁZã"B(UãûÐ'J Çß[j;>ÙØ‹ü¼Rc`¦ˆpuKk|µ¥ˆ€y%Vñ×ÛB™TÝ«¶æÓÌeØØrè»™÷û½­½]gD˜aÁZã"B(X[$íÍ®Ÿƒó½>ÐÑ+ë¶ÔЄˆpž¥µå_“¬ÐÑîî×vŸ˜‘aY(ÿÆÛ?ÜVÄ ¬Å„ƒ½¶½í=}}«”ä;4“ÚRDÀ<ƒkùë-‡çM8ñw…‹ucã6‡2 -®øLjêQ£’"¬ Ð×&⍄¯'SB6tTߢ–°„ðÃãñØ1¡_¯Ó¿û=‰¡Ç>ð6úþ7ü°»3Õ÷Ýa"Â5.­;É:oðóÈ‰Ô è~¿I{_·Oÿ.=Ïs„,X[Š˜gPb=4fNŒÓ|Zª »ßqüyKÒßIÚE„¹, 5."„ Ïç„óއWà2@øîànuøâ[iÁΈpæ¥=GÎÛS®"ÂŽñ®‘O®…¯¿½|§m?Ž¿}:Õ¶;¨f;B–ª-E„Ì3(±Þ¿~¹\Æß‡ù|süõz‘2 «÷|7ô[w2¬|hl<_'ýö^û¶µ K˜g—­¢%³TÁZã"B¨Öív }aYaøÖ³Ÿ’je®×k¯˜ |8&}˜³s'"\diçó9r@æ-Qʰœ3\ŸûýÎô^C^¡r8N#IÖž³'ߕê™#Â¥jK!ó J¬ý¯‡ î€;¬žÉà€[.×u=ü°äø-»%3²`­q!p¿ß/—ËñxÜív¡Cñ96þ¼w1ü6|&ô|ûþÇ KkúÈŸ«ñõþIjö:lÚŽE4Òív;Nûýþó)¶W9ŸÏç‘çfø+¡’ùüÏú'¬À˜–¯Ì[]´ÙlžÛÖgü jK(Oh!¼NŸ}«W["´ÆÌˆ²®iœ‹Æ¡¢`€Eˆ˜Zã{®ßæÿGÁ³0µÆùWçœBÁü%"`j†Œ,+BD@*§Óés¼h¿ß+ ,EDÀtn·[ãxÑõzU8 XŠˆ€6ÿüóÏápün»¶k»Ý*X ,ÛÐè5¼³ÙlŽÇc|¤õx<ÂçÛ‹<é¦`€LZ#"BÞ|Žóüüüì÷ûãñx½^ß‚­Çã~x:v»]ÇHÑápP° È­5""à)ù0Ñf³y< VÁ…µFD„Åc)X@kDDP•„Dûý^Œ¥`€òZ#"B€Â$úùù¹^¯ SÁ…µFD„Eº\.ûý~³Ù Úíva ŠQÁ¹ðÕãñ¸^¯§Óéx—~~•äè^ÆÍy®Ãë8Lu¢ÙR˜˜€/òWõ䃿‹æ8×ëu·ÛÅ×'áÃá+ËÖuÝ›s<{mÑgRÓ7xÊgoÎYÔc63æ»÷û}¿ßǬdØã_÷Wاmñ›ðG“…Ï 3Zñ%VcÀ‰¬…ˆò|ùb~^U>ø»P¨t¿ß#C“Æ'ïzÅ(ËæVÜÏçíÍLŠ:I=ÃáÐk=~~n·[ãߺ^¯½²¹‘{ÿSXÔÛ“¹½yO@‘D„©;ìûúÃÚòÁß%B¥óù¸ÙlžgÙß§SÃ*}=ï¼—Š!"¤xòÁÁUAò¿Ò‘\l·Û¿¹I£ðŽð¥×#NÓmïß æx<öZ«ëõÚÐôzš,«zx†¢ž("ü:µf¯ça/m·Ûõ*–ÇãÑö8dÌYöÛ™ã‡CÚ5Ê "$¹aîù·ld9ϙۖ\|}ð¯¶)ÃÂsØÞç“bƒ§dìÈwâ—)"yí {0òq¹Ýn÷õ ŽØ‘Këõì^Xùñ/ìHó=He’œPODØè|>O.žÚžrŠ_TÎ5Õý~¤Š_û6›M¯x·mg¥]Zü=~ /m)¡   "B’ê‰5N¡ÙwÅ—Æç­ÂŸÈg{Çh|,¾¬D„þ»ßï<"×öè_Â¥…ã!ò볕özJ÷¯Æ'vãÏ2 g"B’ê‰?].—´“†/6.0ü¡¶w¤ëõ:f&UáœkÕ˜£õzwdª¥5>B8xM:βÁóèù’œPODøiä³QÆ~Éï_‹™qêšêz½NÙä°7ó9´D„‰€^Œ%RªŽà¯û'u¦„“ž¹.|ºUz<Ó…59x>{_D8ÿÕÜeŠa,‘"uG~_XaJ("L²J·Û-‡ºTD8óZ‰€Õ1–Hy¾†}1?¯-%Ž_¥^ùàÏÏÏv»ÝívÇÿœN§ëÿŒ_CáÌk%"VÇX"…‰‰ù"UUJ("¹JÇ£ñ•‹í÷ûóù|»Ý¦.4áÌk•mD芴R’È€/þ·õ¤„"‘«t<;<ŸÏÇc¶BμV"B`uD„#>ÚëõJRÂIÏÜÆÇë"¦ëv¿ß#¹™·÷ñxt<9Ø7¿†"™×*Ûˆ0œ . @#!eèêõýL )á¤gîv»ý\øë¥{c„…|.9ü¹™·÷|>·åƒ‹ìáÌk•ÃÒ¦;Ë€"‰)C¯8oÀÇD„cì÷ûÏ…ŸN§ñK Ì¥ÝÞÆ Œy˜q¢5μV9,m·Û}~ëx<º:D„”¡W–7ì“"ÂÁßÓ7æ »—Æl.&I»½›Íæsiçóy©=""œy­rXZãY¶Ûí\€F"BÊÐ+È›îÃ…U©Þ8èȇìžßr3¹â ™ÎÈ×À‰WTD9,­ñ, †½ (žˆø>Tj\þív³ÌðõÁ«sB4~™"ÂUijsD„Àïô¡Rã‹ÒFÎ5Ú8ËhäÌŠiŸûÎVÔyQ&Kk<ËãñèšÅ¿s…Jm8õJ ÛòÁ^©S[’2,Fi›à±×ÒþF0%E„i‹:Õf–þ¶<¶ù°ëóydª0 "Bàw®Péz½¶Õ›ÍæëûéÂ:âÛí¿&m¯2컜—¶ô3&”‰ ×&/ê$›YpDØQàÏ$=>œ g\c"¯Â€ˆßC¥ÓéÔQWl6›ð·¬0üoøaøUǼۮ#mÜï÷—Ëå~¿¿>þ;¬Æñx|Îú¹´Ž ~~‡ÃÛÇs»:¦-#"L^ÔI6³àˆð·3‹ÿ[òá };מ…˶gu]Ó "BàwÞP©;}`ÀÛ ãb”^uZGÖW[šáÞ\¤¨ÇofÙáogfíšüŠöª`º?—0%–>õz‚ïkÉ<¶éF{9aQ%E„É‹züfþŽKf]Ó x"Bàw‰P©{ÆÑHñoUkôx<ºç/í[§Ýï÷a |MIúšø±°ˆ0yQÿŠ#„2áíOa*L(€ˆø](TºÝnƒó‹ðÅ¿/°ìñx X‡î{bîx<†ï&Ù#yÖÃÉ‹ZDér¹Œ¾õùJÍ$'!ð»h¨t½^{ejáïGíR9ŸÏ½2”¯ ŒeÂÇN§ÓßppüɹNXÔ"¾'Ú€ ~ŸÉàívSI@aD„@Çår9Ûíö-B ÿ~~>ð¥%t»ÝŽÇãç l6›ç œN§^éäkŸ@îv»°´j“—äEM¼P°¡x÷ûýgùÿÝçóÙ3ƒP0!TEDü_{v À0èþÔYi¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Š"€E)ŠR!¤(BHQ„¢ E@Ê áÃõ_G endstream endobj 270 0 obj 92053 endobj 271 0 obj [/ICCBased 273 0 R] endobj 272 0 obj << /Length 274 0 R /Type /XObject /Subtype /Image /Width 2401 /Height 2401 /ColorSpace /DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÖ1 ðò'Ýb˜´Ó†+ |( À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÒÀ`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –F À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`°40X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒ¥€Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`i`° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XK#ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  `íÖ1ƒ þ­×ÃA/ €ÁÀ`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒ¥€Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒ`°0X À``° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0X ƒ`°  À`, €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À``° €ÁÀ`,ƒ€Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X À`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X À`,ƒ`° –Á0X À``° €ÁÀ`,ƒ`° €Á0XË`,ƒ`°0X À``° €Á0X À`,ƒe° €Á0X,ƒ`°0X À`,ƒ`° €ÁÀ`,ƒ€Á0X ƒ`° €Á2X À`, €Á0X,ƒ`° –Á0X À`,ƒ`°  À`, €Á0XË`,ƒ`° €Á0X ƒ`°  À`,ƒe° €Á0X,ƒp4XQÓÜ7k endstream endobj 273 0 obj << /Length 275 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xÚ}’OHQÇ¿³%B¬e&RðN¶Wí`ŒÝõoʶ¬k¦²Î¾ÙÞÌn%Bˆ.AÖ1ºXÑI:†‚b]"è(‚—í73»îˆÚƒ7ï3¿ÿ¿ß{@](mšz€yÃÉþ(»;>Áê7P‡A+­Xf$‘v™lqdí}…䜛áãõÿ] ‚U€Æ¬ÇמöxÀáû¶iO:¬äÒb“¸M¤’1âWÄg³>žöq†[ ñ2ñMÅ'"()Y'æ±ld4ƒä—‰»2–’'&ßÀSg^™öÐ}8õ¹&›°€åwÀ¥Öš,Ô \V:k²Ý¤;©iÝR;;\‘Œu?ÊåÝV þ°ÿ¼\þûº\ÞC9¾u¥(J•IÒÀëÃ]ýÜàBS˜s_ QP5ûFz¼Úë׋Gõ%«t{3qW°D÷0vz ¼ü \}\ø$€Ôu¡ºmþÀÍ+˜…–ÍÙ¬C–;XØ9:Y„^g±BÞ,Ú\°ACioci]g®©Å·¸(ñL;òz±Úï9ÚAnŒŽÐIó ¨Üê­°4“I÷ÐÝ x#Ã{zwA¼¨j}ƒÎ…Ðþ¤Š¾Q¥óš=˜ò8Ðmèñá Ã(Äo{1±cÚÑd5¾Ué­ÊgÒ·t¶üÆlaȱi"ßÐ\.5æ±”šËÅâ^Å8tph0èk€!‰~D† TÒhd¡‘”»6‚ØÂì±–:>f¤ß&Ÿm×çŠäíxÝA4Ž…¶ƒLþ&ÿ–·ä%ù­ük±¥ªiÄ”¦¬?ûCqÌÕ¸m¥&/¾By#¤Õ‘%iþ 'ËW©¯:ÕXl©Errð'ñ=_—Ü—)Œi7Ò¬›©äê,úF|ÙNšٮͯ6×rm^™Ü ®ÍšUáHWü «Ãÿ5;¿?ÿͰh endstream endobj 274 0 obj 9315 endobj 275 0 obj 706 endobj 250 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [475.552 391.603 482.999 400.626] /A << /S /GoTo /D (cite.funnelsort) >> >> endobj 251 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [471.398 348.834 478.845 360.524] /A << /S /GoTo /D (figure.7) >> >> endobj 253 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [174.792 301.148 182.239 310.171] /A << /S /GoTo /D (cite.blellochsort) >> >> endobj 254 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [188.721 301.148 201.623 310.171] /A << /S /GoTo /D (cite.HJB98) >> >> endobj 255 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [208.105 301.148 221.006 310.171] /A << /S /GoTo /D (cite.SS92) >> >> endobj 259 0 obj << /D [257 0 R /XYZ 71 721 null] >> endobj 247 0 obj << /D [257 0 R /XYZ 122.405 493.957 null] >> endobj 74 0 obj << /D [257 0 R /XYZ 72 334.565 null] >> endobj 256 0 obj << /Font << /F8 93 0 R /F47 95 0 R /F11 106 0 R /F49 119 0 R /F21 120 0 R >> /XObject << /Im5 219 0 R /Im6 220 0 R >> /ProcSet [ /PDF /Text ] >> endobj 279 0 obj << /Length 650 /Filter /FlateDecode >> stream xÚuSKÜ ¾Ï¯àR‰H yL˜ÞúÚ>n•æVõÀdH&RR »ÝþúL¶»Ý©"ÅðÙþlÃ'=áäÓŽÿcü9DÈŠUIª=gR’vÚýÜq†ÞÍFpKHÀÍ—©!Ìî|›+ßÈò¿l›[Ig~䨑¦ UÙ°r/ÉñL¾ÓÛ¡_­Îòªtÿ&Ë÷`—T5ڳXAÇaN^««ž³BRï[²vÚvºõïZ)=Ëò†Kú6ÅùK¢[l&¨9zJáÃïDßk$…’ÊöÚ¾†MUÒVð^ÛìÇñ+Œž‹’ÕÕçIå h/èàК“Óö.ô©Ï,prúÙ„í}V4T£'(ššvÆb–›Ô8âÒ›`9L¬~ÖVy¢ á3.q‰(ÎC^öÄÓI!ßV1G£Rˆþ•޳õPâʰfÎò²|<i»€V´W§äXH}!ÁÇGå\²²™4%E½Éˤôøå !n|ù«JÕíEù\‰…´%VB¢ð¨+c'5· 5]°œ¶ª yÑ9\Àp7˜ÕaÀ¤m?Ì °÷CÂ[3-ÊÆ{ƒÝýà/¸rôŒ‰ÜGɇÕI¹- {ví6Þ‡.$ʆ¦ž´R5jVÿt‡AX$øsÊLÊ|@4¾\.0“Ýž ìƒ %N²Õó4@æpL+äNadòÏràJMv×]ÇÎ[휱nÓ ¨´uA,<“`Eý*<³ªÆ S!þ&t˜£lòLÁ±âÑ¡+>> endobj 249 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/cluster-seq-speedup.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 281 0 R /BBox [0 0 2435 2406] /Resources << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 282 0 R >>>> /Length 56 /Filter /FlateDecode >> stream xÚ+TT(TÐH-JN-()MÌQ(Ê ™›*¡‘‰˜‘œ« ï™k¨à’Ô° Y endstream endobj 281 0 obj << /CreationDate (D:20070406030934-07'00') /ModDate (D:20070406030934-07'00') /Producer (Mac OS X 10.4.9 Quartz PDFContext) >> endobj 282 0 obj << /Length 283 0 R /Type /XObject /Subtype /Image /Width 2435 /Height 2406 /ColorSpace 284 0 R /Interpolate true /SMask 285 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÝÑu«¼º6ÐUBZp ©!-¤…´àËóß¹·à\ƒ[p i!Gãó8ùc¼€„çßÅÞk­8XÁ«Ä¿Ôâx%‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆb/$4 àšœ¾]¯×Ãáðñññþþþç“ßÞÞÒ¦¿Úï÷ÇãQûöA$À–\.—týöõõ•.çv»Ýã(}»ÒKÒ¿I{çó9]j7ÖªJ× ÀÆNßçóù1|”)ÀË&‚H€ ¸\.___ooo£GïÃá>Gc²LUlõlëšØð˜\n·ÛÝoVnó:§ïïïï™L‘›1ô³§ÞÞÞR•”>VÛ2SUlûlëšØð˜÷¼/õp8¤ðûAàËår:öûýí}CÚœ=MYÔ£$…üøø¸]ãýy÷DºäKx»ÒKÿ¦dÝ~ NTUN‹/r¶uÍà(€ wòp—ËES³Qý–'zòD´®ç³Û[Çã 7M¤’çp8ô¬á¯Í‰ªJÀiñEζ®ðâüvp’Êí§‹U¾¿¿kjZÕ¯×köÇÓQ aÁ`"ˆhHªez†ß¯¯¯)/»OéŒêÌT•€Ó¢ÆÁކ×ìä©Üî#=Ië>»x×çç§Vƒ‰  -ûý~î÷J|ÿù-šð2œ5v4¼Z'?]}*õ6MwøÏÏO‹²=ƒ‰   =C†×,éwÝŸŽÔòÌT†€Ó¢ÆÁކ—êä]/ѳ:+­wølßž²l°±ÁD Єì2·÷BÎôoï£ÔòÌW†€Ó¢ÆÁކ×éä]u½c‡Ö;¼^ ô‚H€&d—¹\”Tà´¨q°£a¾N~½^M¨²Éï9_ 0D4a·ÛeGÝTÈh-ÃÀiQã`GÃKurªl²Ã "§g:A$@»cx‹ßåt:}}}ýyƒÀÛÛ[ú“ý~¹\fýí×ëõx<~~~>¾Â`·Ûݶ!máÜï2X}3Ò'wm@ú“ôçéoÿÄܯ|Þ_·ÓÆî÷ûwIÛÿøuÒ_-ÐÿëQÉ€ÐJÏI­‘ÚäñƘ÷ÿ¤Îs8f:ª3ïNúÛWî'J©+Þz쟱î>ÐÍtmVáÁ{>ŸSSÜFþžw“ÝwSÈMn•7κcږΟ8¬yw,0Žmrü©¿“/PÚ×p^VwlfÞiôÏ "Á`"ˆØÌÞÐõjª†º˜},ŽÂ+ëÛr׃¥YikSí¼½Í(ß·}q_þ·ªFLÿÀú;m¬Ô‹>>>Ê;^úÇC;^¶c–ŸÙù«ôKë?u×û¬cöú{νÿ‡éÓ‰5Œ™éÓÊÇÌôÅïCA¶ÝÚ3[9”Ò&¥¾÷g³K:ÖV,¨çÛY·™ÿGÿ”Éáj¯Z×Ó8Ï}ÚÒôÑò»£†qlV]}üY¾Î ùÌq×´uÖ¼‹\ÙÃdÄËkÒÅÛã&Mÿ[亽9üºÈt¼Î$¶  Ý1¼’­ÿŒ6²|÷÷¤GÔ·KÅHa-Ÿ-‹6³ãvÄ}–㥂ÈÕ;m ëõÚu zÉ7*ŸwM=|¾¤»Þ§3bªaù#±°»–lØÂs‰£{NÈ´m c温ԴÙ]‡C»cf‡RשªÆ»ü‘·V‚ÈÀý.ò:S§Ç´ÅÎÅsŸ€¶1}´Öî¨akhâqôø³d;n,ê 7Pó.|pe뵯¯¯¡›ýœ¡ ’=xûw« R”¯6‰-ˆhw ¿}1üŠ÷p8Œ>§¤JjbYʇé“NÉ6£«æ]æ¼ßV‘µn§Õ•†Ì1”úù|÷êwÍË š"XëH|Ú]ÓáY8q´XÏ1¸ÙÆÌÊƒÈ ¥òƒhÊ–¼`9÷ /2¦-y.žû´é£wG ãX[w„Vrí)4¦,®RIÍ»üÁ•ý#ž- 9̳» ÿC‘¢xµIlA$@ýº*‹UVê+<ÅDD£ËÏTIÕpSn ›R¾B¹z§5úØÑCMö7ö/IT(;57h\ñHì鮩«|}}Õv¥ØsFl¶1ó§î ²ÂC)$c*·_3ˆ¼Mê=¯U5W°î˜¶ü¹xÖPëÓGëîŽƱæ&GŒ? |£)˜]7uÊÕr%5ï*W×mƒúLvIÕÃWg±%‚HQl•  ]=ö!ËÎqŠ™rOïÄ{Ã+²Ñ÷ˆÖ°óͨo/ˆ\·ÓV>ïZ2'ÐÕá'¾@³ëÙ´òcaÝ#±«»¦OÚåè9ƒæ¥Ã7Û˜Yí˜Yí¡r÷Há|ãË‘#2—zæ ÖÓV9ÏzjzúhõÝQÃ8ÖâÄãºwÞÆvÝ®Ìkô¥r%5ïŠW6ØÔž=§‰ò@3»#ž–„‚HQl•  ]=ó¢«ß&:è“ äô]~¯({½^S¥Ð_¼ ]Ú±¿"K-v8Ò6ün·Ûf¤2¤gZ&ýÕ ¦®a3ú·!}TjÛ?Ûþwú“´›ÒN‰zc]+EÖŠ6Vÿê²ïïï·¾÷ûGÒÿMØ?–þÁ¸Ù€‰3cÙ¦N›ÚÊ‘ØuÃy…WÚý둦‘šåÏôTú¿é§R‡ïyâfÆÌ®m¸>%/”lw̬öPê·ÿŒr%ãöÓåî›"S›ÜÎø¾æí¤ÿt7ýø:€Jæ ÖÓV<Ïwjwú¨†ÝQÃ8¶JU¸ðø³À7Š‘F'­•Ô¼ë\ÙqP!ÖÓåχfÇÕ§o«DŠ`«‘íêZuä^e¬[u>=;¤ËûTJôW4©,ê™ó)ð3ý–lr«ƒJ>§§˜*/jjØŒ´ ]…UúóÂÂ*U¸=3íNªWÕicõLJ”ô½ôz¦žÞ#Ý5½9ú~‰®Ñ¯üÍ•«‰ ]iwé›îÁÔCg„60f–ŸgSÏì9‚Ú3[9”ÒGõoLúÛ®/RÉC'¸ôMSƒ z¶¥ç-uå·üTD®8¦­{.^ìÔʤP%»£Âql¾ªpÅñg:wÄÕl×cw£wY%5ïêWö!Ó¨ûËï¥ÌÖËOÌl=ˆlôŒÔ0K,ˆ¨ÙÓOÒuòÄõg:Å º#±ëk–×h]UÞ ^ô”$…3W5lF×6¤Òlh*Ô­U•ÌD.ÜicuMJ ºi¿ë]EO—êê*C¸ëJ6­CõøxÎý 幯´§/ûgÇÝ»bëcfjü¡cæõzÝØ˜Yÿ¡T¾›zçA;ºæ 2í¯qGîOïäöô%©—´î˜¶î¹x±P+“B•ìŽ Ç±9ªÂÕÇŸêÜÂ+ßGPÖÓ'æê¯yW?¸&V©O—ïžÒOV l•  iýEö¯ä³Ö)&mɈ‡ÂºîŠ,©ºæ~UdýuYIDUÃftmÃè;Š_$ˆ\¾ÓÆêŠíFô½®Û¤Ÿ~Tv¾åíímÜ7ʶmá¼M%Bɸ6iõE¶³;nâ"çөd××q‰Ú·›Ö°ÙÅm&ÎK"çè´±²7OyûÌÏØ›Ò»ß:5‘½Ñºp‰¤J„ŸèÇ.ø½5ì©)‘÷æÇÌÊ¥ÑÛ=QN×[»Gâµ½º¡–¬ç\¼À ¨þI¡ªvG…ãX&Ž? Œ$ë.ÓZÉy¹þ±îéOý¹sòvì<ÞNùt¶$Û/X#‹ €%ÏžF€eÜÞP;€ï÷ûÑÏHÎqFÈ~Áþ ëñG¦WâÙ<¥ÿÁ®6cJ|ÓDURa9®Ó.p±7ñÙç®'ÅÆÍ ê„]/?½;VªÊ§<â{àT2fNüî›3·z(Ma~¶Dþ´?¹Ö˜VϹxQÿ ®ªÝQá8ÖÖf=[2‡ò¯’órUW6 ºŠÑ-x}Ì÷Ÿ^Ÿ<î‘›Ù‘ÀV "¶äûû;ðÑÈßqäˆ{¤ç8#doËìyéI¶l™þ†¾lßSYÔ°S¦GZ©Jê "‡vÚXó-c5.`ízãÞÄö,œ¥©d@h«*Ï.¨;÷ oÆÌÊÇÌ J‚ȶúp+cZmçâ"kÛ?‚È­Œ„3)y3u%çåÚ®ìÓÁý7@>þÈ-EͶðÐ=RX "­DlO*UÒ%tv¦eÊâ0Cß3Ç![MôÜŽ˜½3äa´lÕ¼Ùm(\0ª•ª¤Î rh§•½3aâúH7ÙU’žÖ×]kÕÞ)ÝõLeáW2 ´U•g“ß¹W‘5fV>fnûPzå 2Ôçÿ¤3×þ?ïÿ§ÿª²ªoQá˜VÛ¹øÅƒÈÚvG—Ä«°³Ž? L¥ú„tízû¾é›fÜt³D%çå&êŽþ*,».kö¯úŸ¦Ï¾+³p"­DlX*pR)—J›¨%[­ 8Çaèm™Ùšeùò³†ÍȆӟËD†wÚXÙ׉ëñöÌx”¬é»ž£ÈþÒòƬd@h«*ïz³ç”ç囨S£ç“_aÌÜö¡ôRAäårIŠièžx÷ںߢþ1­¶sñ‹‘^½f¹ðø³ÀTê”|º®QÿH%çå&®ž µk]Ö›A«³f÷ækÖÈb @DlÏí.ÓtIœ.˜³A@¡ò×è,–éô¬÷2回ïjØŒì6L¿QVÞicÍ´ß&¬]“À£¿Nù TÉ€Ð\UþôV–ÔŸÓÉ%u‰¨ |cfåcæ¶¥W"/—Kº |±x[³‘•Œi+ž‹_<ˆ¬ðÒ襂ȵƟ¦RC§'–íYž¨æšw݃+û´i×R*]ë²Þ Zõ±)ÊWÕD[%ˆxY·aJVƒù£pZ¦†¹8AäÜ…¡ ²òú+;ÏS¸éÓdÐ=ÆOäéýÒS~cUBsUyvu©.i¤sÊÄ9üjÇÌ¡K”ouÌD¶{I滯­ÙÈåÇ´ÚÎÅ/DVxiô"AäºãÏS©!ŸœÆ™®”¶'ɪä¼\áÁ•}GF×MŒ=ë²fÿAÏG=þÒòWv "­Dps[§dmœÂ×ÛÕ0÷oY5oF…S£‚ȦçI~¢×u|zŸðôå…+Z¬Ê»ÞªV2‡Ÿ~öx<šÀ7fV>fnûPÚj™ŽÁùfª››¬aL[ñ\üâAd…—F›"kêEÙ§ÿúC½škÞuÛ<›`f_ ñø&úÇè°puÖì»)ËofD[%ˆàñrýé3’%Ó/‚Ȫ6äúŠ…’ ²dNàé-ÓÓï²DN‘ùüü,Ü_ÆÌÊÇLAdsç‘ì¤è+‘5Œi+ž‹‘‚È%U2þ4Ô‹~ºŸpìzªN9¨‚Ⱦ#ãñåÑaáê¬Ù÷P¬Ø»‘@Í×`‚HºÞãvSòÒyAdU›aR}ÅBIY8ÁÒóxcöæðò—­T5 ´[•§½0ý¥N%±3+3‘mG¥éOãóm1Òäp8œÿÏöf#×ÓV< "‘‹©güi¨ýt,(Úsé+ˆì‘}þñŸý¹»kÝ×’ÕYotT³"­DÐ¥ç:%«³VDn¯V]q‘•w€jƒÈì­=¯zÉÎ!t½–¥‰B¸Ýkæ´ï&.°¶Ûíú«7fV>fnûPÚXÙóº±ßué¸.\5n{³‘­i‚È \m5ˆ¬jüië*®kuÖì£|/RóŽþðlÑqO·ï}õÏ?È.ßúS¶:ëcXYþ‚ÈA$°]‚HztÍ̤?ob.NYÕîØv‘Ug¨6ˆìúñ?Ó]“ý©e…pë×Ì×ëõx<>]Ç»gÞÞ˜)ˆ¬sOm,ˆÌ¾]÷>Š}×áÏvg#ÛÓ‘¸4ÚjYÕøÓÜU\ Ë8l£îÈ®§úçaö’uY{>í首¿ òG l— €= ´®rÍ™½;´'Ínà—ÊÍw¶]x3Lª¯ÕJC;m¬ìèƒjáAEý p0û~®ìÓÖÙ¨d5¼:„íUå© O§SÚ#ƒž*êÙƒÕŽ™C§L·:fnûPÚR™½‹ã>ØŽëϯ0>¦Õv.~ñ ²ÂK£M‘µ?Í]ÅM"—?/W[w<>¢ø§+\—µëÓ~/ÒòXú ½yR l• €¡×ü ‘Ù¤ì#W³ªa3fÚAdx§m¥ïMÿ^]ÃËãÜTö[Œ˜]©d@Ø|U~{°èóóóé‚l]óÆÌÊÇÌmJ[ "»n'+Y`¿‰>lLw.~ñ ²ÂK£M‘µ?Í]Å ªÁ7_óN¬;²w?ö´v׺¬7ý«³–¬ÝúR5²˜xzjD0¢šûš3»ÂOÏ-èÙ%¶FþmÿSœý«³>î‹ãñØô…S?0÷©M Àˆ"hîkÎl‘ÕsmŸÍ€ÆESÔ°ÙA§×§‚ÈðN»@ß›r/zÿTÃЕ½[þÏ›¶²_a\†^É€ð‚Uy×c]·²3+3·}(m)ˆ|\>nú ÈldÔ˜¶â¹øÅƒÈ /6DÖ6þ´uw¹\²¿¢ëu´5×¼5ÔÙ…‚ï éŸ/‰Ë{Vg}|v~è*.‚H`«‘Œ8StAs_s}ÿEv—÷| RÃfdï#^Ÿ†W%S^#Rg¹îK[²}oâíè]Uö¸|ði7xüE£·¿’á5«òAOF3+3·}(m)ˆœãd62jL[ñ\üâAd…—F› "kÚºŠË>Á×sRsÍ[IÝñÞsк¬]W‰·¼õ1ññõ7V#‹ €þA @Q³Ê*CÙŠìi$šÝŒåWªY}3ºvåŠexög§¬[a9®Ó.Ð÷¦³?Ý÷iø¨ìsg÷&Ûo§,ñTÉ€ð‚UùÐ>³úž ìä›37|(m>ˆ¬­¿ò˜¶Ö¹øÅƒÈ /^'ˆÜLך{K²·:ô_×\óÖPwt½&r躬7]«³>~ÚˆB7V#‹ €þA ÐÊ>ñª~Ü{áº:ág„q/¿èz•ÏÂ7ˆÖ°s†Sörø’M‘5¼±%» WIÊNŒ{X,[Èß›=û‹¦5• ¯Y•úÊÕŽ™_¹™1sÇÒAäô§à‘›ÓÖ:¯D.¶D‹—F‚ÈæÆY·¤ëÚ¸¿p«¹æ­¡îÈÞܘšzĺ¬]WeéñqqÚ×i«‘Å@ÿ€ ˆhh Ÿ/ÔȾL¡¼¢‰=#t=Ð÷tZ£ke›’EWÕ°s†Söòûû{lƒÔDŽî´±º^e5z3º¦GF4Ùž6;;M챕 ?‚Èg_¹Ú1sâ꬛37|(MüÀ¨e«C¶ÊÒ¬õô½ªÎÅsïˆ™Ž‚ _Yšµ¹1dÖ-ɞ©RÉy¹æº#[nŒn®ìê¬ûnD¼±YLô‚H€¶ÆðT•ÌQÝwÝWYÄž²wNgçC&ÞXøÛår)YlsõÍ/ »>°ðÇoý‘Tâu-a´â¬Â”N+Û÷R‘8=2åý/ÙŽ”~Köϧu• ­\3§æ 9¿d—Õêï„«ï©®9½Ñ ’}ïd»cæV¥‰˜!§,(=e«ºnóˆ®Ûš¬mL[å\<÷Ž˜é(Øð¥Ñ&ƒÈÚÆŸ™Êðð-é:;—´^Í5o uÇãG=njùbA=®N|ÇÆjd1Ð? "ZÃÓ¥u`ÙSæVg„®)ü¾]uëôº,]Ûß+…&6#ûÛG†é7våÔÓ¨Úӿ請W "'vÚX]}oÄÓ…]ßkbî*ÞãÎÚ„V®™ï]eâS ÙI•þ»»kØSÙ±eÄ´Ò&ÇÌ­J?0»¦ýô±kÜVeGìq³Ái˜Þ‡ëyJ«ª1mùsñÜ;b¦£`×F› "k˜Jø™ý— %MWyÍ»zÝ‘·§ SÙ›N§?Ƹ±YLô‚H€vÇðÛcDS>6Õz=Õå+Á†<|×sß Z¦k‰›ÑkÛþ.ÇÊOv«oFWý5¨1{ŠÜAçý®µSÅT¸ˆMO÷,²Öê´±ºêÐA›Ôõ½¦Ï.v½Žv¦ç)jÚ "'NÝg*¹í|cf׃37y(Í”ÏÎq·ÆÓŸêšÌ´1;eAäZcZ%çâ¹wÄLGÁ†/6DÖ6þ,P†O©¾Ÿ~ÍÂ'õ*©yë¬;º†îѹáÓ ¼qï­h¥Fnh8jžÄDl` OŸéÒwÐ[ ÒÅyÿ%ë »X»>äãã£ðš¼ÿ.¾A_­í”ô[Ê‹â´UÙVjb3zÞû™vîÓ鸴y={dÄy¿«¿ív»þý›6µ0º 9¦Vé´±º^XY¸¶sú=3å«;4°†¡Å ò÷í.å3!]sk%'”ÆÌ®#Ú˜¹ÕCiâö´É”¡rôVuõÀ’é’ é rù1­’sñÜ;b¦£`×F› "k(à 6}µó‡C:5?}ªnPz[IÍ[mÝ’ö–×/£+—&j䶆# þIlA$ÀÆðt½š.GSÑ‘.éÿÌšÞê ôW%EÐÓëÞ¡›—.°Sý•6à÷ÇÞ6)žýÛ3b}Ëž‚è÷&ÝêÏõü­TìOi[ÙŒ®û“ç׿ûIªÈÒÆ<½©rÜy¿¿5R·üÓ=n-гlÑLAäZ6Vÿ]»ião_íÏJØÿ½xJqŽçIW?Û "³'—왥¿‰ DZ}OõoÀ‹™›<”f}Pô±ÃÜÚ!An»)|«z1x{{K}æÏöÜŽß™ºMAäÂcZ çâvÄGÁ†/¶DV5þ´8•:îQâJjÞ:뎞®5îAËž­÷‚Ȇj䶆# ¡IlA$ÀËŽáãRÈY7oôÊNO×]æd·úf š!Ÿû¼µ1]•Wë6Vø® ŸáIîºGâ‚ÈÅŽˆÕÇÌÂ{ËKΤ37y(MÿÀ’Ià¡ßwÊV=½Íc±>\y¹Ø˜¶ú¹x1ÇQ°áK£­‘U?–áã*ˆJjÞ ëŽžxtÜû{>pÜZ¸ ÕÈÍ GÀ‹Lbkg€¦+ tê_+iÅtµç¹³ùÂܵŽÄV®™Cæ}NY}èž2™v_Ilccæ&¥=8ÓV¥½3år1÷sY병õŒi+ž‹—ÙáGÁ†/6DÖ3þ44•:èMŽõ×¼µÕÙ³À¸uYoæ{V·ò¹ÅáX— `n/%M$ÓÕøÄW0t㦀>>>F¿í=ët:MŸŒº½ßdÊݪënÆ~¿Q~þ¾_4꼟vîˆJùO©Èª§ÓJ;qôÔÄÄ;´û‡²®_:wK.$6tÍœgÊ} ¿oeŸ¾›3SÓýþE37y(­»æÛªÑ7 ¥nÿûÈÝÀld=cÚZçâÅvDìQ°áK£m‘•Œ?ML¥¦V}oCÍ5oUuGö¾²që²Þd“Ö‰ms5r£Ã°"A$À–¤«ô¡¯)Ÿ¯ê9#ÇòLÅÂ|Ï”¥OñË­·~Km›‘úLá¯N»ìqGÄž÷Ë#¿?ó YõtÚØþ?h¸ÈöXÙZ;| ØŽÄæ®™oÏà8¿L¿aØY혹ÉCi­}4wyS>#}»!-¶ÛT5îÕ3¦-.^xGD¾4Ú|YÃøSáTêûÒx’¾ìÜØ5Ô¼•ÔÙhNùŽÙ)'¾ ²¡y{%PùÙS P¡tIœÊ½t-šŠŽ[™ÓµjGú«ôoÒ¿Œ½ÔzF¸M¥ê&mÀŸKëÛ&¥¿]ì²ôÝS xk«Çëü[+¥MM›4Ó³`ënÆ=Âþó\í}GÌú­§)n½âO!|ûîá÷ ·Ûi£¤ ¾·ùŸ/õ»Ù›û^šhŸÇ‡ñoÝ&ýmú77lTµ§îFטÙu¼ÌôâZc¦C©P:ÒUÖc#Ü[`Yèìö<Ί§^½À‘kL{ÁsqmGK£Õ{‚ñç5ÏË®M^ï”D°ä)F³ Óƒ ¯\ "˜é£YÐiƒŒA€W®…‘ÌtŠÑ,è´€AÆ À+ׂHf:ÅhtZÀ cà•kaA$3b4 :-`1Èðʵ° €™N1š0ÈdxåZX ÀL§Í‚N d 2¼r-,ˆ`¦SŒfA§ 2^¹D0Ó)F³ Óƒ ¯\ "˜é£YÐiƒŒA€W®…‘ÌtŠÑ,è´€AÆ À+ׂHf:ÅhtZÀ cà•kaA$3b4 :-`1Èðʵ° €™N1š0ÈdxåZX L!ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ €)%¤f ¼ŠDPXBjÊ«HA$…%¤f ¼ŠDPXBjÊ«HA$…%¤fÑ€åE €ÂR³h@€ò"H @a ©Y4 @y$ˆ °„Ô, ¼DPXBj P^ "(,!5‹(/‚‘–šE”A‚H KH͢ʋ A$…%¤fÑ€åE €ÂR³h@€ò"H @a ©Y4 @y$ˆhÚõz=ŸŸŸïïïooo¿ ´ôÓ~}}¥þÙôr3öýý}:öû}jŸÝn÷çk¾ÿ'µÛáp¸\.5øÂýáÏoOß4ýaúÖé»§pì‘›ñýý}<C´é‡ÂDrJÅ^-~àù|þøøZê¦yÚtë×#úÃççgjöÈår¹e¾oÚÇ2lƒ `‡Ã”Âíýýýt:(!§”ŸáõìÐ9ŸÏY؈D²+¼[±¸>†ôF<Y¸Í%æp€mD´îz½zê­ÇÛÛÛÐrJù^Ïú„ý~?wå»Jqýýý==]M†®@ût›Ïçsa6ê €mD4ír¹,VÁm)ˆüüü\ Ý–/®cûÃñx Ù#ßßß___¦àÕ"Úu<—¬à6D Å "ÃSéAÏEvmsú„¡‹Ä:´`‘zš:}}}N§ßAÒõz=ŸÏûý¾géΡ%ä”ò3¼ž-ùÁÔ=öùùù§ÑnM}k·jƒÈþþ°Ûí‡Cú¿_þ˜úCú²©Ÿô…é¯ ßÙõL¥ixY‚H€}÷Ta‡Ã¡äÒ?{L †–SÊÏðz¶ä»B·ý~_˜¸].—Çg*Wi„ß{³ë=¡ïïï×ëõé'¤ÎÐÕRË,<Ãà€mD´¨ëѼÝnW˜¦ÝÇßÙÜÐrJù^Ï>ý©®Ç!Ë ýÓt÷øo•F¸ëZlvÐK{ž©,éTå³ÙÇNÓ®I[{{w§¶A М®4mD y“~j¿ß¿By˹Æ=ñ×åt:½½½­Ò7×ëuz yÓ•E–4QÉü@Ú¤q]h‘  9Ù7<–¿Ë¯ËårÙívCKÈ)ågx=ûô§²ë—–¬\:_ >ýc³éjÉò¼Y÷HúOïš8ÃP¾ò-°‚H€¶t=³v>ŸW)!øÙÀ\·hã·g‡|ý]¯}ºzmלÀn··ò-Ð:A$@[²¿M ž&– ülàn/ˆÌ¾rb*=îË®×Az^– `uÜétZëW/ð³¸bëýÌD¾½½=>8ñ3Çc6R\þÛÛ+`‘uêZ—uÅrŸ üÀì;"—yžtŽFÈ®Ë:úíwçóyD+™JÊA$@‡ÃcÍõññ±b ¹ÀÏ~`vÑÑÅV o„죋!o }üØ···…¿Ð:A$@C²9Ú~¿_±„\àg?0›ÜýnÉYãÈðFȾ r­M5!”” ‚H€:½¿¿ÏôÜèrŸýÀÇ—*>¾cq¿ß§V %Ã!ÛÖªîM%e‚  NÙW^.—KÈ~6öO§Sy=ûöööññ•K "€—"ˆh½ˆkâ·×Dþt¿)²$—L?{<Ç…’Ë4‚ ¨¹†D4TÄ5ñÛ« ":Þ®8Èçççõz]w÷ "€æjXA$@CE\¿½¶ 29ŸÏOßùÔ~¿_q÷ "€æjXA$@CE\¿½Â òæx>Fg‘h„¦;'ÐJ +ˆ¨Sv)ÑËå²b ¹ÀÏ.ów×ëõt:í÷ûAOJ–¬ÑºL# }se ÝØ$A$@C²ÑØù|^±„\àg—ùÀ.·‡%???Ÿ¾Pòé­áÛ¼nXk­D4$»vèápX±„\àg—ùÀçó¹gùÖ§;"|›³SòlfmÝØ$A$@CöûýcÍõùù¹b ¹ÀÏ.óåŽÇcö·¿¿¿/¼ÍÙþðññQÏ ƒc^™  !çóyŲ+<ˆœòvËuËÏÏÏÏÇßþöö¶ð6wõ‡§‹ÄÖÖ=€MD4äûû;[vN§µJÈ)?;åm†ë–Ÿ—ËeÄ̱ÍÙÏ\euV@I™ ˆ¨Vöµ€O¯„,üÙÝn÷ø³Çãq•Y«5²?r½^ÃûÃ*EšJÊA$@µº^P8åéÂ)%dáϾ¿¿?þì×××*³Vk¼½½…ï¸Óé”Ý’)mÛèj#ˆØF)÷öö6ñ!¸Ëå²Ûí†þÞÂßï÷#^ª˜•¾fö%“~cHb›]šõés©Ù4öp8Lܘl¾9ñÓAcz÷^§zDÔ,êMY õûûûþ™CKÈÂ_ÑõàÞÐX0ýû®Ü­dcnÿìóós₨Ù]ðô!Äô¢ÒØßº’žEvŒéÝØ$A$@s¾¿¿»Â¸Ýn7ô¹ÈãñøûÓ†–åÛ<ñAΞ!‡‘ãÈìãIúó§M=Ó£‹Ùg-oöûý¸>ö'l7Ãàh€W&ˆhÑù|î©ÂJ‚­ïïï?ä¬AdÒ#> O¯×köY‰Aäý1ÒÔåaè”ÇQÓéÚò§!æèO¾¥½åYgêZÙ=5n†Á¡ ¯L Шþlîíí-ýƒÓéôû¡¿ô¿Ïçó~¿ÿøøQÁM¬øúÃÓ´µéüÓÿ==›–Ýóд ÇãñÏj±i“ÒŸ¤?ï³ðáÊžGÓç÷ï¯)Í{ÿ_ðö[RS÷Áqmë8€W&ˆh×Ó¥Jc+¸é_Ôw¥“kUÁƒž7œ¯¸îyY䥽 `™LݰŒð,rh 9hk¿¿¿w»ÝÄ-¼½ô°ž rè=ã9´©§‚H –  uÏÁõ¿è0¤â»\.£7o·ÛÝߥ8nc߉9ÑétÚ£ÓØÂÏ¿^¯= ÀÎÑ1»°%‚H€ ¸^¯L?þçÕ…%䈭ýþþ‘”‡9N£ŸIüÓb¿ßh¹@ úékNO]oo-|ý¥  ¤LD´èz½~}} ŠŸv»Ýáp( Ôb+¾ãñX¸©ûýþq 'nLúÀ´#ÉAÙ\T Œnêóù<"¡¾}ÇûçSf•ðÊ‘Ûs¹\‡Ãçççãcwooo鿾¾ŽÇãèúN§´1¶ó¾‘#>á|>ß›ëqÑÔÛÆ¤¿Mÿfh6W¸³öû}úBÉ´%·FH¿÷éꃾãcúyÿ]©W„d¬?‚H`‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H œ 'ˆ "€p‚H¨ß¿_ÿ-ù³ã.A$Tïߨ<ñŸ XkàD@õþŠÿ ".A$TïßðTñŸ XwàD@ ‹RH`ýQK (Œ¥@C– Úñ4d”BµŒW‚HhJOÔ(…*¬‘P½ÿùå§#pìJ!ÿü,À2‘P¿ÿùÿýŒJ!‘À’‘Àÿª÷?ž¦ÙŸÒ’¿™A$ ˆ€7(‚ìJ!‘˜A$°@™UWP³òòßʬRÈ,óà ˆ‘@R˜Bf³H­÷Èü0"Å‚H­Õú7ü¿{ ©õܨ%ˆ§]A$Ôíß„ÿ¤"a¹±K N»‚H¨Ø¿Éÿñ› –¾‘à´+ˆ€Zý ú;A$,7‚ "ÁiW õù7ᥲÈ‚HXnD‚Ó® *3:‚¼“EvDÂrC™ œv‘P“é)dòcÖ‚HXn4D‚Ó® êð/(…ìÉ"DÂrcš œv‘ÿËÞ'Žl u¤@ ŠHA)ðzßHA)‚b R _}ã9=n#°J[¥ÒZß<Üã6˜–eírÿWd m…|Ö""!î²&D‚±+DÀÒ’'Èg-!â®lB$»B$,j¾ ù§Eò‡ q7!Œ]!ò1…Ô""!î'D‚±+DÀæH_ϬE¾ DBÜUNˆcWˆ€pÉ+äç×"Ÿ"!îB'D‚±+D@ ù+ä-²— q—;!Œ]!¢Ì·k/!ò‘ qW¡û´Þ‡‡"Ç® @yTH ‘w"™á\:ãîalÛ6ÕM‘Ïžª{mߤûäÞ'ùqecWˆ $¶cå!â.ÅB$IÏ¥Ãá0qçÒgïêX×õ[ÏÓ{;䈛{·iw‡fæcWˆ *$/‘w5"Id·Û¥ºO°·!¾ÕþžÝùý*ê’ðm+s»B$e°+¯ ‘wA"Éϳ›"‡?Cï–ªMÓŒ{=§Ói⯫»B$p#$¿"!îš,D’Ÿg7!N<±|=«»B$«f;V"!îÊ,D²ž3sàc{÷excï-–©¶¢Ídì ‘¬—íXNˆ„¸‹³ÉzÎÌíÝIõr¹Ly=½»Åv_¨¤±+D°Rn„ä-B$Ä]Ÿ…Hòs»ÝÏ¢ªª>¼ûÌLJßï÷)/©wwÖá/icWˆ`ulÇÊB$Ä]¥…HòÓ4ÍãYt>ŸGŸÕûý~ú«êž¤Ôs[ˆ`lÇÊ8B$Ä]¨…HòÓ{KãívòØëõúøØÃá0ýUuOòøÌÝ—+fì ‘¬ˆ ÉhB$Ä]«…H2Ó¶íã)t<>|¾7sœã­'³»B$«`;V&"!îŠ-D’“Þûßz‡ÇÞ\˜$DžÏ癞9“±+D?’é„Hˆ»h ‘d£÷^Èw·?í ‘Ý3Ïôò„Hc;V’"!îº-D’ëõÚûŒ#bïûKÎ"»/WÌØ"È™!IEˆ„¸K·ÉBÚ¶½\.u]ïv»ÞÓ¦ûø[÷B~"G]!€<ÙŽ•´„Hˆ»€ ‘dy²Õu=ü}!¿ë ‘·Ûmúëï^ ÁlÇJrB$Ä]Ã…Hr:Ùv»]]×Sºá¬g`©§· @žÜÉ„Hˆ»Œ ‘ds²].—™¾Ê¬…bÆ® @>lÇÊ|„Hˆ»˜ ‘ät²}Ý9â­!_•Yÿ ÅŒ]!€LØŽ•Y ‘w="Éòd«ªjÜ ’Bäè±+D’¹ ‘wI"Éød«ªê~¿Oÿ*³þŠ»B$˲+1„Hˆ»° ‘,§ýÇét:/Ο·vjÝZˆLû£ú•#`3TH•^B$‘lÍý~?NÏN¡Ûí&DÎýÿÿó!D°”¶cuPyFˆ!’mºÝnûýþñê>(D ‘”ÉÄ"AˆdË·FöžEMÓ‘B$E±+K"AˆdËÚ¶}<‹v»ÝÇVU5ÓØ[H»/WXˆtúcŽíXU†"!îR/D’¥Þ Z/—˯ì ‘×ëuúKêÍ£B$Œ¼B:¤ 'DBÜÕ^ˆ$KçóùñDªëú×ö†È¶m§¿$!¦ûP!Yš q×|!’,®~u]G†È!mt-cWˆ`n¶c%B$Ä]ö…HVurþú¨Óé4nO×_õÞ¤Ù}¹bÆ® À¬TH2!DBÜ•_ˆdU'篺\.3åÂùg&cWˆ`&¶c%+B$Ä]ÿ…HVurþú¨ëõúø¨ãñ8ýõ‡Çgî¾\1cWˆ`*$¹"!n‘¬êä÷À!o.ù«ÝnWê¹-D0Û±’!!⦀I–zolx.UU•ü$¼ßï3õÍ|Æ® @Zn„$OB$Ä !’,õ¾ÕãÀðW×uò7sì}=Ý*iì ‘¤b;Vr&DBÜ8"ÉRoLþz£áÄ·‰ìžŸS=Ûõzí=‹Þ mÛ¦º)òÙíÝ—(lì ‘Œf;VVDˆ„¸é D’è,Úï÷ÓÛܳ Ù¹Ýno=Õn·{|’ÃáðîKêòø<Ý“—7v…HÆQ!Y!â„IÒ³h¿ßÞõÙmŒº®ß}¶Ëå2ýÎÊÞ-^ßz·Ê]!€wÙŽ•5"!nL‘ÌsÇáµîz½>K~_·ŽØRµSUUïvÿõþÊî^<¼È±+Dð’•"!nR‘Ì|UUu:.—Ë[»ÿÙ}°®ëÞ=T¿»^¯ã^Øív{ñ´‡Ã¡išÇWu>Ÿ{·c½IìZÆ® Àp¶ce½„HˆB$yŸE7Amš&«×“óØ"Ȭš qóBˆ$ã³hô½ß%l‘£ßþrcWˆàW¶c¥B$ÄM !’Éöû}òsfÈÛ8—¤Ey/ä÷±+DðšíX)ƒ qƒCˆ$…ëõ:ä݇èždŽo·[UU9TÑlÇ® À n„¤B$ÄÍ!’¤®×ëét—ü‡ÃÜ{Ÿ¶mÛ}•·^R÷Œ]!€^¶c¥0B$ÄM!’Ù´m{>Ÿ¿ºdošì>x8ºOè>ó~¿‡½°îk5MS×u÷~ÜÅÙýÏîƒÝuŸù’r»B$lÇJy„Hˆ"B$»B$}THŠ$DBÜ"ÁØ"ø›íX)˜ qÓDˆcWˆà’² ‘7P„H0v…HþÇv¬Oˆ„¸™"D‚±+Dð7B²B$Ä!Œ]!`ólÇÊv‘7\„H0v…H€m³+›"DBÜ|"ÁØ"6Ìl q#FˆcWˆØ$Û±²MB$Ä !Œ]!`{lÇÊf ‘7k„H0v…H€Q!Ù2!âÆ Æ® °%¶ceã„Hˆ›8B$»B$À6xSHø"!rî‘`ì ‘`;Vø"DBÜè"ÁØ"JçFHøCˆ„¸é#D‚±+D”Ëv¬ðƒ q3HˆcWˆ(”íXá‘ qcHˆcWˆ(‘!¡— q“HˆcWˆ(‹íXá!âæ‘ Æ® P^"!n$ ‘`ì ‘¥ð¦ð+!⦒ Æ® P7BÂB$Ä &!Œ]!`ålÇ Ã ‘7ž„H0v…H€5³+¼Eˆ„¸ %D‚±+D¬–!á]B$Ä )!Œ]!`…lÇ ã‘7ª„H0v…H€µ±+Œ&DBÜ´"ÁØ"VE…„)„HˆXB$»B$ÀJØŽ¦"!nl ‘`ì ‘k BBB$ÄM.!Œ]! {¶c…T„Hˆ^B$»B$@ÞÜ ‘7¿„H0v…H€\ÙŽ’"!nŠ ‘`ì ‘Y²+ÌAˆ„¸A&D‚±+DäÇ0!âf™ Æ® ۱¬„Hˆ›hB$»B$@6lÇ s"!n¨ ‘`ì ‘I}¯„£¨BÂL„HˆˆB$»B$@Rï·HÛ±B!⢠Ʈ ÔÇ›-R…„HB$Ä D!Œ]! ©wZ¤íX!˜ qQˆcWˆHm`‹t#$Ä"!n ‘`ì ‘3xÝ"mÇ K"!n ‘`ì ‘óxÖ"mÇ "!n ‘`ì ‘³ùHñŸ ‘7…H0v…H€¤~TB²"DB!"Òzl…¶c…l‘Fˆ„H€´‹¡ ù"!Œ ‘iÍ]!a˜Bˆ„0B$ D$7:Aª07!‘€ 0Û±Bž„Hˆ…B$»B$@jn„„l ‘7 …H0v…H€t>&ü§BB!âf¢ Æ® È[Áq`‹tT!-!âÆ¢ Æ® ˆÛ_‚C s"!n2 ‘`ì ‘“Þ|õÙ§9¤0!↣ Æ® 0Áô·€ìýd`&B$ÄH!Œ]!`¬!R‹„HB$ÄMI!Œ]!`”t!²·Es"!nP ‘`ì ‘oúõßëù)DB!âÆ¥ Æ® ðŽä òY‹æ DBÜÄ"ÁØ"›¯Bþi‘À¬„HˆšB$»B$À0sWH-‘77…H0v…H€ßÌô¦ŸO>LJù‘7=…H0v…H€—æ¸òûók‘Iˆ„¸*D‚±+D<7Ó?h‘Fˆ„¸*D‚±+Dôùˆª_„Hˆ!DBÜ$"ÁØ"̽k/!‘7L…H0v…H€¿EÞùƒ s"!nž ‘`ì ‘ß,X!B$ÄT!Œ]!à*$l€ qƒUˆcWˆXèM!xB$ÄÍV!Œ]!Ø<7BÂv‘7^…H0v…H`ÃlÇ [#DBÜ"ÁØ"€­²+l qsVˆcWˆ6ɰMB$ÄZ!Œ]!Ø6Kˆ„¸i+D‚±+D[âM!aã„Hˆ›¹B$»B$°Þ"!nì ‘`ì ‘À6¸ø"!rò ‘`ì ‘@élÇ ü!DBÜü"ÁØ"€¢ÙŽøNˆ„¸,D‚±+Dår#$ðƒ qSXˆcWˆ ¥B„HˆÄB$»B$Po <#DBÜ8"ÁØ"€²xSHà!â&² Æ® ÄÀkB$Ä e!Œ]!(‚íX!„HˆÍB$»B$°~¶c"!n: ‘`ì ‘ÀʹNˆ„¸-D‚±+Dk¦Bo"!nF ‘`ì ‘À:ySH`!â&µ Æ® ¬7…Æ"!nX ‘`ì ‘ÀÚ¸Mˆ„¸y-D‚±+Dëa;V`"!⦶ Æ® ¬„íXé„HˆÜB$»B$°n„’"!nv ‘`ì ‘@öTH !âÆ· Æ® dÌ›Bi ‘7Ä…H0v…H WÞHNˆ„¸9.D‚±+DYr#$0!âF¹ Æ® dÆv¬À|„HˆèB$»B$Û±³"!n¦ ‘`ì ‘@6Ü ÌMˆ„¸±.D‚±+DyP!B$ÄMv!Œ]!Xš7…‘7ß…H0v…H`QÞˆ$DB܈"ÁØ"€å¸&DBÜ”"ÁØ"€%ØŽX„ q³^ˆcWˆÂÙŽXŠ qã^ˆcWˆb¹X q_ˆcWˆ©À²„HˆúB$»B$›B9"!nô ‘`ì ‘Àü¼)$ !⦿ Æ® ÌÌ@>„Hˆ[‘`ì ‘ÀllÇ äFˆ„¸e€ Æ® ÌÃv¬@†„Hˆ[ ‘`ì ‘À Ü äIˆ„¸Å€ Æ® ¤¦BÙ"!n= D‚±+DéxSH sB$Ä­ „H0v…H o äOˆ„¸… Æ® ¤àFH`„Hˆ[‘`ì ‘Àd*$°B$Ä-„H0v…H`Û±ë"DBÜ"AˆcWˆÆr#$°:B$Ä­„H0v…H`X#!â– B$»B$ð¦X-!â B$»B$ðo ¬š qk!Œ]!ÌÀÚ ‘·l"ÁØ"€aTH B$Ä­„H0v…Hà7Þ(† që!Œ]!xI‚J"DBÜBˆcWˆžS!‘·Š"ÁØ"€>¶cŠ$DBÜZBˆcWˆ$O*$ !â–B$»B$ð77B"!nE!D2§¶mÏçs]×UUí÷û'Æn·ë>~<»Ï¹^¯NïeÇ® |Q!² ‘·¨PjH­mÛº®³ãÝo·›Ó{‘±+DÞØ!â–J ‰´m{<“œ0Ýó$É‘Nï·Æ® çM!"!nu¡ÔÂn·K~Ú4MãôŽ»B$l™!í"!n¡Ôñ‰t<ÞacWˆ€m²+°5B$Ä-3”ò>‘¦´H§÷[cWˆ€ ²+°AB$Ä­4”f>‘v»Ýáp8NmÛ^¯×ïê>Ò4ͯï,9zVgã[cWˆ€­q#$°MB$Ä-6„Hæ9‘v»]]×?Êã çóùÅ)t»ÝRÞ¾YÏÆ® ›¢B›%DBÜzCˆ$õ‰TUUÛ¶#žäv»í÷û„´:ß»B$l„7…6Nˆ„¸U‡Iºit‚üã~¿?k‘Ý%9½}³ž]!¶À›B‘·ð"Ia·Û].—$Ou½^S½S¤³ñ­±+D@ñÜ ð)DBäÚCˆ$?‡Ã!Éî¬ÎÆ·Æ® ³+ÀB$Ä­@„HòÓ4ÍãYTUU’ÓÛá}6v…H(•íX¾"!n"D’ŸÛí–äDr6¾5v…H(’!~"!n"D²ž3s‘'ÙÎØ" <*$À#!â–"B$ë93y’íŒ]!JâM!ž"!nA"D²ž3s‘'ÙÎØ" Þà!âÖ$B$ù¹^¯gÑn·Krz;¼ÏÆ® ep#$ÀkB$Ä-K„Hòs¹\Ï¢ªª’œÞï³±+DÀÚÙŽ`!â'B$ù©ëúñ,ê>˜äôvxŸ]!VÍv¬ ‘·>"ÉÏn·{<‹š¦Irz;¼ÏÆ® ëåFH€á„Hˆ[¢‘d¦w_ÖÎý~OrzWÿxv®~ýéétjšæz½njì ‘°R*$À[„Hˆ[¥‘d¦·‡¥NïãñX|‘"`¥¼)$ÀB$Ä­U„HròìvÈîãËžÞUUµm[öØ"`]¼)$À8B$Ä-W„H²q¿ß{ß²û`&§w]×#vˆ]ËØ"`EÜ 0š q+!’l‡Þógô­ˆsœÌûý¾¼)DÀŠØŽ`"!âÖ-B$y8ŸÏ½'ϸw‡œõô.¯E ‘°¶c˜Nˆ„¸¥‹IÚ¶}vòLI~_ïíx8NÿhÿçÙkètŸv<{wˆ-¸E ‘° n„HBˆ„¸Õ‹ÉÒ®×ë³3çr¹,õªÚ¶}¶UìŸ÷‹,lì ‘3 !â0B$‹zQ!O§Óâ/¯mÛwGÞn·Â~„¿r$—*¤ƒ l™ B$[p¿ß÷û}ï9s<½ÈàÿÿŸ!24ÛB:¨ÀÆ ‘ D²å ™Û;0v/æÙ}‘e¼S¤ ™r#$À „H"Q!³Ò¶mï«mšFˆæø5`&B$‘¨¹é}Íu]"Ÿ°¸9¶cuTþ"!nU#Dk¥²s>Ÿ_sUUÅŒ]!r¼B:¤?‘·°" ´Þ ùùdwÖÝnWÌØ"`q*$@!âÖ6B$Q®×ë³3$ÿ ùâ祘±+DÀ‚>TH€(B$Ä­p„HBP!?…H`Þ ’ q‹!’ù•Q!?…H`n„&DBÜ:GˆdfMÓ<;1ŽÇãŠ*ä§ $e;V€E‘·Ú"™Óù|~Q!×õw齯s¿ß3v…Hˆd;V€¥‘·à"™Íñx|vJÔu½º¿Îåryü‹TUUÌØ" Œ!$DBÜšGˆd÷û½ªªgçCÓ4küKÕuýøw9NÅŒ]!b¨Ë"!nÙ#D’ÚívÛï÷ÏN†Ëå²Ò¿×n·{üë´m[ÌØ"`nÞ B$Ä-~„H’ê}#Å?º?]éß«mÛ‚Ïm!xSH€L‘·þ"I§išgçÀ~¿¿ßïëý«õÞãy<K»B$ÌÇù"!n $D’ÈétzvTU\!Ó¾峿Úív+iì ‘0Û±äFˆ„¸…Éd÷ûýx<>ûî/rÛàŸ:ý=ŸÝæYÌíŸB$ÌÆv¬"!n-$D2Yï¶¥_Îçóâ'vUU£ïŽ|q›g1·C~ ‘07BiýßÇÿýù/íŸnîH ‘¶"™í,º\.Y½¤º®¯×ëÀgè^|†uÖ±+D@B*$Ü÷˜øØþ‘ù)DBäŠHˆd%gÑ[§Óëg8çó¹mÛ]²ûHÓ4Çãq·Û½xxI›²~»B$$áM!™üè‰?’â ‘ÿ!ÂÖEB$+9‹†È)öûýý~/rì ‘07…æó˜¿WÅ_?(DþwX„H[ ‘¬ä,Ê!D–w/ä÷±+DÀDn„æö",¾þˆ ù×a"!lu$D²’³hñYØûB>Ž]!F³+æY^|ñ?UÈŸÇPˆ„°5’ÉJ΢·N§ûýÞ4Í~¿Ou#äív+~ì ‘0ŽíX`½‘ñ×ÿ·ÿ  aË$!’•œEãN§ÛíÖ4Íápñ…v»]]×e'ÈïcWˆ€Ü ,B…œtô„H[) ‘lÆív»\.§Óép8TUµÛíËc÷ñãñx>Ÿ¯×ëÖÆ® Àf}¯„£¨Bsûq¹x«BºÔ|'DBÜ*KˆcWˆ`Û>Þo‘Þˆ÷xÑQ!]m>…Hˆ\e ‘`ì ‘lÛÇ›-Ò›B‹ø~Ñw/¤ Î!âVYB$»B$ÛöñN‹t#$°”$ÒeçSˆ„ÈU– Æ® Àæ i‘¶c7ü}!»ÿ\vžF!–XB$»B$üÖ"mÇ ,ë­ù¬E:ŒÿL!ÂÖWB$»B$üãY‹t#$° q ò±E:’ÿR!ÂWB$»B$üÏGŠÿTH ‰‰ ò{‹t0ÿ:°B$„­¬„H0v…H¶íG%T!Å¥JþsHÿ:¼B$D"!€{l…ÞXHò©Eöd!¢‘€ ÀÆ=C7B‹H¸«ùê8 ‘Eˆ„H6nb‚T!éÒÞüøuñÑ"Ÿm!¢‘€ 3Ý©B¿š#;þ¹þh‘ýÇ\ˆ„(B$ DÀ§!p©ä³ù¬E:òB$Ä­¯„H0v…H6O…"%Üõuˆìm‘Ž¿ qK,!Œ]!€ û˜ðŸ ¼+í»@ ‘-ÒwAˆ„¸…– Æ® ÀV½¶HGè5G|Îñÿë{!DBØZKˆcWˆ`“FÜöøúRà™¤kTÏ·Cˆ„°å– Æ® ÀÆLÙ|õÙ§9ª@¯En„ü|²w´oÇ¿ß!Â]B$»B$[2ý- {?à‡÷býzZäÓo aë.!Œ]!€ÍøH"µHàµàùõE{k£Ùÿ "!lé%D‚±+D°ïÿ÷ú_þ…HàÑ" òË ‘ÿ~›„H[€ ‘`ì ‘”îc†ù)DßÄoÄúã ü#!òSˆ„È5˜ Æ® @Ñ’'Èg-ئEÞ òñeLùÓÍ}Ë„H[† ‘`ì ‘”k¾ éßóÏÉÒg™ïš a+1!Œ]!€}Ì_!¥زÈ!íÄß;!ÂÖcB$»B$Å™#A~=³FïÅꀧÿ ‘¶$"ÁØ"(Kò ùãù•Ø, ²ï£ a«2!Œ]!€R|Ì_!¿H°5dQßM!ÂÖfB$»B$E˜o;Ö^Âl„Yà÷Tˆ„°å™ Æ® ÀúWÈ/òO‚,óÛ*DBØ MˆcWˆ`Ͷcí%"@©$È’¿¹B$„­Ó„H0v…HVkÙ )r/VG{™o± aK5!Œ]!€uZd;V `Þr+ßh!ÂVkB$»B$+äFH ! r[ßn!ÂlB$»B$«b;V ! r‹ßt!–mB$»B$ëa;V  r»ßz!ÂVnB$»B$+áFH  rÓß}!ÂoB$»B$Ù³+JäŽv¦ç€ aK8!Œ]!€¼ÙŽHB‚äß3Aˆ„°Uœ Æ® @ÆTH`:oÉ_çƒ a 9!Œ]!€,ÙŽ˜N‚¤ç¬"!l9'D‚±+D˜H‚äé¹!DBØŠNˆcWˆ 3¶c¦ ùå "!lQ'D‚±+D7BSHü~’‘¶®"ÁØ"ȃíX)"o„t´×}ª‘¶º"ÁØ"È€íXÑìÅÊ{'Œ a ûÓ×’ a–ŸP!ÂÖ®B$»B$ÀJØŽÖ¢7öþéë‡H0ËO¨ aËW!Œ]!` Ü ëò¢Eþøˆ Ñ?žB$„­`…H0v…H€¼ÙŽVêY‹LõF$ŒüÙ"!l+D‚±+DdÌv¬°j ›ã” éý` ‘¶”"ÁØ"r¥BB–M~ê¡ç§Rˆ„°Õ¬ Æ® ۱ªýøÑ“ !+B$Ä­i…H0v…H€Ì¨°v?€$äCˆ„¸e­ Æ® Û±B~ü J!âV¶B$»B$@6Ü e !gB$Ä-n…H0v…H€ ØŽ #AB¾?žB$„-q…H0v…H€¥ÙŽJùvŽ6Œù!"!l•+D‚±+D,ÊP7B ~N…H[è ‘`ì ‘ ±+”dúÃC¤£ “~Z…H[î ‘`ì ‘K°+#I‚Ø"mHð3+DBØŠWˆcWˆ§BB1ÒVÈ-Ò¡†d?¶B$„-z…H0v…H€@¶c…bŒŽŒ/þgo‹t¨!ñ¯ aK_!Œ]! Š e˜x«ãë|ÿ#‡fù"!lõ+D‚±+D„°+ Éž«½w>ö~¾³ü ‘¶"ÁØ"æçFH(@ª·}òq!füY"!l ,D‚±+DÌÉv¬P€$7Bþ"[¤#³üD ‘¶"ÁØ"fc;VX»‰ ò15¾‘?¾¢ã³ü\ ‘¶"ÁØ"æáFHXµäýq Gfÿé"!l=,D‚±+D¤f;VX»¥*¤Ÿwˆø"!lU,D‚±+D$e;VXµàùùä à3þ˜ ‘¶0"ÁØ"ÒQ!a½¹ òëKûÙ‡Ðv!ÂÖÆB$»B$@"¶c…õŠI__«÷ÇÜEâ~Þ…H[ ‘`ì ‘“ySHX¯˜!¿Å!w€ê…H[$ ‘`ì ‘ÓØŽV*l/Ö_wà¹À\?ûB$„­“…H0v…H€ Ü kùv_}ÊŸ ®B$„-•…H0v…H€QlÇ +µÈ]@F!ÂÌB$»B$ÀûlÇ k´Ô^¬@^—!ÂÖÌB$»B$À›Ü «#Aÿ]„H[6 ‘`ì ‘ƒÙŽVG‚~^„H[< ‘`ì ‘Ã¨°:1Òq†•]„H[? ‘Ì©mÛóù\×uUUûýþlj±ÛíºÇîs®×kØ«ºßïMÓ|½ªî5<¾¤îºOè>mScWˆxÍ›Bº¸xz}"!l -D’ZÛ¶u]?fÇ!ºÞn·Y_ÛápþzºO±+D¼àFHX øå*!DBØ*Zˆ$‘¶mÇc’¦{žä9²{ªªÆ½žî³æÑLÆ® ÐËv¬°"$0èZ!DBØZZˆ$…œ&Ñ4Mª—w¹\¦¿žîIÊ»B$À#۱Šx;H`èåBˆ„°å´IÆ'ÒñxœþÚš¦É°f8v…H€Ÿ¿íºVÂÀ{ !ÂVÔB$yŸH[d’{!‹¿/Rˆøù{® +!Ac.B$„­«…Hf>‘v»Ýáp8NmÛ^¯×ïê>Ò4ͯï,9ú>ÄÛíöúÏçs÷_R÷‚_<°¼÷‹"þú%×v¬°$0òê!DBØÒZˆdži·ÛÕuý£<¾p>Ÿ“·¿ªªzŸm¿ßÿúºOè>­÷á‡Ã¡È±+D|ª°n„&]C„H[] ‘¤>‘ªªúq›á@·ÛíYû±Ak÷¦?Õ³»5Çý3»B$°õßmmÇ k A ®$B$„­±…HÒH£ä÷ûýY‹ìþè­§ê½rÄÍŒ½Û´vO^ÞØ"€Mÿb«BBö$H ÙõDˆ„°e¶I »Ýîr¹$yªëõ:ý"ŸÝùnÍüü§ö>Õð]g×2v…H`³lÇ ù“ ”—!ÂVÚB$ùé½ qú–ªo¥ÌïN§S’Ýb3»B$°Mn„„̹Ha"!l±-D’Ÿ¦i&ö<|vSdacWˆ¶Æv¬9 ˜ëò"DBØ’[ˆ$?·ÛmʉԻ/ëÄ{o±œø†˜¹]!ØÛ±BÎ$H`Þ‹Œ a«n!’õœ™Û»“êÄ·°ìþøœÝ*iì ‘À†~ët#$d,¦B:ΰéëŒ a o!’õœ™[UÕãcï÷û”×Ó»;ë[»Åæ?v…H`¿oª17BAW!–ßB$ë93G?v¿ßOIÝ“”zn ‘ÀV~Ù´+äJ‚B¯9B$„­À…Hòs½^Ï¢Ýn7ú±‡Ãaú«êžäñ™»/WÌØ"€Uø^ G?P…„¬H@ôeGˆ„°Õ»I~zßqà>¨ó½™ão=™ÕØ"€Uøx¿EÚŽ²åí e.>B$„­Þ…HòS×õãYÔ}pÈc{sa’y>ŸgzæLÆ® ¬ÂÇ›-R…„<Ù‹Xò$DBØê]ˆ$?»Ýîñ,jšfÈc{CdÛ¶Ó_U÷$B$Àâ>Þi‘¶c… IÀò"!ÂVïB$™éÝ[µs¿ß‡<¼ªªÈ9pÃØUŒ]!X‹-Ò! ÈâZ$DBØÒ]ˆ$3½%ñp8Ly¸9dì ‘Àмn‘¶c… ¹ÈèŠ$DBغ]ˆŒ=˜N¹×žÝÙ}|à3ô†ÈÛí6ýµÝïw! £_ß´HÛ±Bn$H »ë’ a‹v!r¹èôûá~¿÷¾;d÷Á‰ß‘Y¿ÝÅŒ]!Xßoå)þÓ5`>$éÕIˆ„°»¹ôqsþq8zÑ[« ‘£Ç® ¬Âô BB¾ÿàR!g`ÌJˆ„(Bd&Í©x>Ÿ{Ìðw‡|ñÝ™õ[_ÌØ"€Uxl¶c…Üþ©Á@æ—)!¢‘ù®-Ÿ‡mÛ>;,÷û}ú÷hÖ ˜±+D«ðX"THÈè$H`+!¢‘Y«mž„×ëõÙ¹\.I¾M³žÅŒ]!X…‰ Òv¬0ã¿0HÀZ®WB$D"³:Pl‘/*äétJõšõ4(éÌüÊ‘™›¯B:¶0òŸBn„tœdW-!„È¥TOµÓï~¿ï÷ûÞƒp<3l…¥~¿¾ßi¬Ã Û±:¨0BØ^¬5òÚ%D‚¹ž£”ÏsS!»¿ûÖB¤ lˆ!!$°â+˜ BäQæO¾Á )D ‘@áFmÇúºE:¨0‚ ¬û"&D‚™ý!ZËóoªBvªªšé`v/ìñ™»/WXˆtUòý ýà8°E:ªðö?#DÝéP3^Ê„H[à ‘£QÌw¡ÈSnÖ ùù$D^¯×鯼m[!`±_Ïß¿íñõ'8¤ðö? H@14!–ñBäûÇgÕ_hYsWÈÏ'!²mÛéÏ,D,ó»ù„ÍWŸ}š£ ïýÓ vY"!l1/D¾|¾Va'Ûõz}v†¤ªº®#Cd÷劻B$ã/æÓß²ï“7þÝ ¤B:Î@è•Mˆ„°õ¼ùæñ þ^s¦ÅTÈÎétzü—Ëeú3ŸÏçÇgî¾\1cWˆ²û­;%꺞ïëVU•üHÞï÷™úf>cWˆ–ÿ5|¶ )À/ÿ8àFH`S=!ÂVøBä›Ç§¤/7ŸûýÞ[¿4M3ëW¯ë:ù›9ö¾õä¬95~ì ‘À’¿€Ï_!Eèÿg Øà¥Oˆ„°u¾ùæ! þv¬ñˆÝn·ý~ÿìd˜‡è†w‚í½»3àï9v…H`±_½gH_Ϭ†À/ÿ& AÛ¼ú ‘¶Ô"ß?De|­™\¯×gB÷§ žØ÷û}ܳõîËZÌD~ ‘À²¿w§®?ž_þ p#$°åk  a«}!òýCTÆ×šCÓ4ÏÎý~?ºŽÐ{ãè-aO§Sò[,3»B$ý÷üò‹8ý;€ ¸ ‘¶æ"ß?J|¡9ôÖº/UUEVÈNÛ¶©nŠ|v;d÷% »B$ú»ölÛ±ö’HàS‚øs="!lÙ/DŽ:Jëý*s¸ßï½w .{çàn·{|1‡ÃáÝçéòø<Ý“—7v…H îíØ ùE(aã$H€ÿ.‰B$„­ü…ȱj_b&ûýþÙ·þ|>ÿ?{÷vÕÊ’%Pôš€ ¸ ppÏúĹ€ Ø€ rA.ÐEõ¹:èA>"vsðÓ÷€D%‰b«3µÕwõþþ¾>ŒÞ ¬§ïoÙ"€ˆýuÔíX¯’K“ !~¾0 ‘6ÿ ‘KUsm^ëv»Ý­[Ňû_{ú„;_Þå²+DÕ÷›VH pýåQˆ„°-€¹âXµòà}œE ŽÌáp¸ó8OOOûýþÇ[=žþÏ···«·cýã׈Ùè²+Du7[ÜŽ†%AÜ{‘"!l D®8\M{g7e=_v…H âÎÑ…¹Ó©Ž3Ððë¤ a!2Ç!jú 'ÿ_Z°EžªãeWˆªìUHˆÜã»`Ê«¥ aÛ!2Ç!"«þ/-Ò"»¼ò|Ù"€òF·c…°Ý½ 0ý5Sˆ„°™ã ‘µÿ—‡Ýn·ìO_ØßûB^.»B$Px·èBHÛÚK³^6…HÛ‘9‘ó¿ôãããééiús>ùô%ƒ,»B$PlŸ¨BBئÞÛA,xñ"!lk Dæ8Dƒä`Çãq¿ß¿¼¼ìv»‡‡‡óÃxú?OÿñôO§O8}ÚPË® ”ÙG»+ÄlçÝ‹`ñK¨ a»,Ç!"Ù|Ù"€›h6ò$ÀÊR!Â6YŽC$D²ù²+D«¶ÏnÇ 1»x `ýk© aÛ ,»B$°òÏ *$ìß] PêUˆ„°‚ –]!Xó··c…Ú;w  ìëª a›!,»B$°ø .„„ª{v  Æ«« aû!,»B$°àO *$ÔÞ°‡THÇñVˆ„°]ƒ –]!˜û÷·c…ª[uBT}™"!lã D‚eWˆfý1Á…Po“.A¼Ø ‘¶w"Á²+DÿŒ BB½í¹ ö’+DBØBˆË® Lù‚Û±B½½¹·ƒˆ|Õ"!l!D‚eWˆ~ý‚ •vå.„ˆí"!l!D‚eWˆîÿõÀíX¡Æ~\‚ØêXˆ„°­„ –]!¸õw*mÆ%H€ _„…HÛP‘`Ù"€«4p;V¨± w!$Àæ/ÅB$„í)„H°ì ‘Àå_ \ Å7à$@’d!¶B$Xv…Hàüo*$ßzK©^–…HÛ\‘9‘€ËæË® |¹+ÔØwKÙ^™…HÛ_j9‘ÉæË® ¸ ï¸] óõYˆ„°-† –ã ‘l¾ì ‘0ôßTH(»×– 2¿J ‘¶ÑÔr"!’Í—]!Æý〠wÙ$@þ×j!Âö‚ZŽC$D²ù²+DÀ ð¦Pp‹R!g€µ/×B$„m7µ‡HˆdóeWˆ€ÿ,àBHøÞŸÕÀ5ÿêBH€6^ö…HÛqj9‘ÉæË® cýA@…„ó­ñßYðοÞÿB  —}!Âö‚Z²ãղ+DÀ@»Q·c…[ã‹>xë_ï• ÐÆË¾ a[!2ÙÀa«eWˆ€Qö¡.„„Ë­ñµPxõ_ƒhãe_ˆ„°Ý‡™ï§à€°É²+D@ÿÛOîìŽo·ÈÿÅ…Í¿æ ‘¶"Sþâ—]!:ß{º+üºA¾Ñ"׿¤ ë_ˆ„°mˆ™òá€¿ì ‘Ðó®S…„‰{äBÍQ‚Hýj/DBØNDˆÌúƒpL^v…Hès¿év¬0w›¬BtÿR/DBØ~DˆLü³pLˆ\v…Hèp§©BÂ4?Nr  oB$ÄmI„ÈÄ? Ç„ÈeWˆ€Þ¶™nÇ “]žê$@Ç„HˆÛ•‘‰‘Ë® ]m0] sü8ác*¤Ã°!â6&Bdî‡cBز+D@'[Kæ N~­¶%DBÜöDˆÌýãpL[v…Hèa_év¬°xw,AŒóš/DBØEˆÌýãpL[v…Hh~SéBHX¶/– F{å"!l“"Dæþq8&„-»B$4¼T!aÙŽx]‚ôv­¾þ ‘¶U"sÿ8–]!ZÝKº+,Û¯®C¤C n "!l·"Dæþq8&„-»B$4¹‘T!aÁF¸D‚œÒ"j€¤  a!2÷ÏÂa!lÙ" ±]¤Û±Â‚-pÑy§E:Ô©—!¶-BdÃBز+D@K[Hæn~WDÆ;ÿçÕéhd_„HÛ¹‘¹Ž aË® Íl!ÝŽfm{W_êxÿ¿œÿ“£ ÐÆÒ DBØæEˆLüSpdˆ\v…Hhc éBH˜µç-qÏÕ«W>^ý| ÕAˆ„°ý‹™øà;DD.»B$dßEª0k·[îm§üw! ¥5Bˆ„°]Œ™ã©l¾ì ‘zsêv¬0}Ÿ».Aþ¨wBäe‹tðÚX)„HÛÈhj9‘ÉæË® yw¦.„„‰;ÜÕ ò25Þ‘?žÔ õBˆ„°½Œ¦–ã©l¾ì ‘qOªBÂôíméþ8‘#ÐÞ’!DBØŽFVËqˆTH6_v…HH·!u;V˜¸±Ý(AúÍhuá"!lS£¬å8D*$›/»B$äÚª0eKž ¿nü®ùY´´|‘¶¯×r" ’Í—]!²ìCÝŽ¦lf7½«_4€¶!Âv7ÛÖ‡ÈIH’eWˆ€›P~ÝÆ®K³*ä÷3^ý…òëÐðR"DBØGˆË® I6énÇ ¿îacä·)ÿÝo@K«‰ a{!,»B$dØ¡»îï^ïÅúã©'þ“ß;€fÖ!¶9B$Xv…HØvo®BÂý}kìÛA^~kþ€Œ+‹ a›!,»B$l¸1w;V¸³c ;H†X_„HÛï‘`Ù"`«]¹ !áÖ^U‚ Þ*#DBØ–GˆË® ñûqîlT%Hª.4B$„m|„H°ì ‘¼w;V¸µE• Xn„HÛû‘`Ù" r'®BÂÕÍiT‚ô+€ qÛ!,»B$ÄìÁÝŽ®nK½$ÁK a› !,»B$lÀUH¸º'• ˆ_}„HÛ ‘`Ù" öîÛíXár7*A°Õ$DBØVHˆË® U·Þ.„„ûPo À¶+‘ a»!!,»B$TÚt«ðc*Aa="!lO4^ˆü§ÎO"—]! o?ÝŽÎ÷žë¤{±PrU"!l[$D ‘Xv…H(¾÷t!$œo<%HR-LB$„팄H!Ë® w*$œo9%H.OB$„í„H!Ë® ¥¶œnÇ 6›Þ€´‹” a[$!RˆÄ²+D@‘ý¦ ßÛLo @ò¥Jˆ„°]’)DbÙ"`åNÓíXá{ƒ)AÐÄ‚%DBØ^Iˆ"±ì ‘°f›©BÂ÷îR‚ •5Kˆ„°í’ ¶è@5ñÈ0kÙ"`ÉžÎíXA‚ ¹•Kˆ„°“¹è(µòà0}Ù"`ö†Î…ØQF%H¿ ”\¿„HÛ4 ‘óRÀOÁ™Iü²+DÀŒ}œ ‰½¤·ƒ ÝULˆ„°­“9ÿ<‹3“øeWˆ€©;8·cÅFR‚ é…Lˆ„°Ý“9ÿµû,pgÙ"`ÒöÍ… ¾…” è`9"!l%DΞ .—]!nî×TH†Ý0®KîÅ @ÒNˆ„°Í”9óøD>ó“ÈeWˆ€+Û4·ceäÝ¢ @¯kœ a[*!ræñéééà|Ù"àçM…dØ}¢ @ß+ a»*!ræñéééà|Ù"௠šÛ±2æÑÛA0Âz'DBØÆJˆœy|zz:8_v…HøwwæBHÜz;HÆYõ„HÛ[ ‘3OOOçË® _nÇʘ»B €ÑÖ>!ÂvXBäüãùtNQ–]!ÜŽ•·„$.B$„m²„ÈùÇ'ò霢„-»B$£o]Éh›A €aA!ÂöYBäüCù³pŠ¶ì ‘Œ»5V!m• ý.t)"!l·%D.:DM?\]v…HÝ»+Cm½$‘¹á"—¢€gq~¹ì ‘Œ¸)V!j÷'AÀ÷š(DBØžKˆ\q”Zy|˜²ì ‘ ·#v;VÆÙ÷Ip¾2 ‘¶í"×¥äÓ—]!€öÂ*$ãìø¼$\®B$„m¾„È*ÏcÂâeWˆ`”°Û±2È^O‚€[«¤ aû/!²è±Š|(»ì ‘ ± v!$#ìòÖ%H÷b ÿµRˆ„°-˜™ïp ‘l²ì ‘t¾ÿU!d‹'AÀ¯Ë¥ a1Y-ßsZ²É²+DÐóæ×íXas'AÀÄESˆ„°½˜™ì 9'ÙjÙ"èvçëBHºßÖy;H˜µt ‘¶"Ó7g#Û.»B$îÝTHºßÐy;HX°€ ‘¶)"=ç!–]!€Þ¶l*$}oå$HX¼Œ ‘¶/"7=ŒÎ@ò,»B$]mÓ¼)$}ïã$HX³’ ‘¶5"糎œË® @?[3BÒñN‚€õë© a»3!,»B$ÝlrUH:Þ»E%H§=ý¯ªB$„íÑ„H°ì ‘ô±Ãu;Vzݵy;H(»¶ ‘¶M"Á²+DÐÁöÖ…ôºe“  øò*DBØNMˆË® @Ó[’^7k$TZd…HÛ¯ ‘`Ù"hwWëv¬t¹MóvPu©"!lË&D‚eWˆ Ñ-­ I4 \!ÂvmB$Xv…HšÛ̺+ýmÍÖ%H÷b€Ë® a{7!,»B$mídUHúÛ—I¹ò ‘¶}"+Fç­,»B$mì¿ÜŽ•Îvd$į¿B$„íà„ÈÊGoÙ#83‰_v…HØ…¹’žöbÞ¶Z……HÛÄ ‘•Ú²Gsf¿ì ‘¤Þˆ©ô´ óv°íZ,DBØVNˆ¬|Ä? ““àeWˆ ïFÌíXéfÿ%A@†Yˆ„°ÝœYùp-~X''ÁË® @Ò˜ !éfó%A@’EYˆ„° YùX-~p''ÁË® @º]˜ I7Û. R-ÍB$„më„ÈÊjÍã;?‰\v…HríÂÜŽ•>6\Q Ò3h!ÂvvBd‰£tç“×<…ó“ÈeWˆ üfêìcñª´ºÕòvv™"!lW(D®;J¿~òšgq~¹ì ‘”ßLÍo‘nÇJ'û, 2¯ÔB$„í …È¥‡hâç¯y"ç'‘Ë® @ùýÔÌ©BÒÃK‚€üëµ a»B!rÑ!šþùõžÊ.»B$å÷SsZ¤Û±ÒüÞÊÛA@+«¶ a»B!rþ!šõùUŸ .»B$U¶TÓZ¤ !i{W%A@[k· a[B!rþñ™õ%+ŸÎ)Jز+DPkcu·Eº+mï§Ö%H÷b€mVp!ÂöƒBäüã3ëKV>S”°eWˆ âÞêF‹t;VÚÞLIÐè".DBØfPˆœy|æ~Ií§ƒRË® @ÝíU‰±†,Û( š^Ê…HÛ ‘3ÏÜ/©ýtPjÙ"(îGOQ!éaåí  ƒ]ˆ„(BäÜã3÷Kj?”Zv…HŠ»¬*ÿ¸+ín¼$t³¬ ‘Eˆœ{|æ~Ií§ƒRË® @q—yE…¤ÉM“ -îB$D"矹_Rûé Ô²+DPÜÊév¬¤Ø1IÐßú.DB!rîñ™û%µŸJ-»B$5¨4¼W’  ×U^ˆ„(BäÜã3÷KVþ,œ¢„-»B$µ6VnÇJs»¤¨éd€mÖz!ÂöƒBäÌã3÷Kª>\v…Hªìª\I[û#o #¬øB$„m …Èù‡hÖç×{"(»ì ‘ÞL­ønØfs$AÀ ‹¾ aC!rþ!šõùk~ÎO"—]!€’;©9Áqb‹tT©¸-’ `¨¥_ˆ„°½¡¹èMÿü5?ç'‘Ë® @±mÔüËï‚CJÅ ‘·ƒ€!¶‡B䢣4ý“ÿœœ/»B$vO+n¾zëÓUjm…$Hv "!l“(D.=J?yñÀÉIð²+D°vë´ú- ¯~2üÜœÕÀ5ÿê^¬0î8!DBØ>Qˆ\w ~ýÌeß™Iü²+D°jÓT"Dj‘LÚÂüÝïüëý/” `ÜqBˆ„°­¢¹ú@ÝÿÌeGÞ™Iü²+D°p»4ÿã~¾"ùe soýëý¯’ `ÜqBˆ„° £YèXÝú´ÜiéôÞdÙ"X2uW‘_B$÷·0×*áÕ½ÿ%ÞÆ'„HÛ3*5É× 'Þn· øîôžµì ‘̹K'È[-~îbn·ÈÿÅ…À•YBˆ„°m£R“éˆsâ=>> ‘Ù–]!€yóvµ ©é0i#s£Ey#H :$„HÛ9*5iÚ8gÝñxŒùŸïôžµì ‘L´ëWHq‡I{™rÍQ‚€±¦!ÂöJMŽã6ÔY÷úú*D&\v…H&Ùä÷#«<,ÙÎHÀ‚Bˆ„°-¤R³õÑí”ûüü ;NïYË® Àï3vé ùãñ妸qzÄ'H§%´Nˆ„¸]¤R³Ý1ð|»U!#C¤ßú[Ë® À½éº~…ü&úð«ËÓÃ…À,B$Äí%…ÈØã9ì™v<¯Þ‘UˆÌ³ì ‘Ü­«ÝŽõ*é‡û~œ$Ðþê D"!nÁ"©ãp8|||ì÷û×××Ýn·ÉIâlœµì ‘\Ÿ«c+ä7ˆ‰§‡ ô²ºBñS‹{„Hˆ{a"ÉqR…}'~@·–]!€ŸuÔíX¯’¸zx;H ¯5œW¡„Hˆ{m"ÉqR…}'~@·–]!€¿ÆéM+$ü²‹‘ ×X(~jq{–"!ìµIˆ$ÇIöøÝZv…Hþ¥·¸+LÚ¿¸+Ðí2 ΫÀ‰Bˆ„°—'!’'UØwâtkÙ"øß íBHÒn^VTȉ!ÒA6]f¡ø©Å¡Bˆ„°—'!’'UØwâtkÙ"p;Vòn[Ö%È)-ÒA,¶à¼Š-„H{…"ÉqÖ5ýD},»B$À胺۱’vÏR¨BÞi‘2f±…â§צ !Â^¡„HrœuM?QË® 0ô”îBHrîVæGÆ;ÿçÕé ÉÖ[p^…ÌB$„½H ‘•£slâájú‰úXv…H€Açs’´[•E—:Þÿ/çÿäY—\(~jq1f‘ö"%DV>zËaÀ³®é'êcÙ"Fܺ+97)+î¹zõÊÇ«Ÿï8YW]p^Õ6„H{"+´e6àY×ôõ±ì ‘ÃíUHrîPÖ½ùã­[° ‘@S«.?µø{Þ"!ìuJˆ¬|Ä?àhg]ÓOÔDz+D ´t;VÒnOV$Èû!ò²E:Ú@ⵜW•G!Â^ª„ÈʇkñÃŽvÖ5ýD},»B$À(Û@’œ“uBN ‘?žÅ1r¯½>Ô´â§gƒ‡ a/UBdåcµøÁG;ëžh÷_·~^ßÿúúúºßï???‡Zv…H€!ö€nÇJÎ]I‰9…C 4²üúð!ãVž=„H[…ÈÊjÍãuÖ¥=½ŸŸŸ»/’B$À(@B’p?RèBH-èhùõáCˆ¬?‘¶ ‘%ŽÒO^óCuÉOïÝn÷ññÑ÷²+Dô¼õS!ɹ©œ ¿nœÆŽ<~öáC‹¬<„‘¶ ‘ëŽÒ¯Ÿ¼æY†:ëš8½_^^ŽÇc¯Ë® Ðíîv¬$܆„\ùý\Îa ©E؇!2d"!l-"—¢‰Ÿ¿æ‰†:ëZ9½ûk‘B$@Ï›>B’pR-A~ý}ÿyF'3ÐÈ"ìÇ5‘¶ ‘‹Ñôϯ÷DãÒl§w-RˆèsðV!I¸û¨y!ä÷SÜ:cÏ@ ë°BdÔL"DBØŠ(DÎ?D³>¿êsõtÖUz¢Ýn÷ôôôú_ÿïê'ÿÓéÓžŸŸ†j‘B$@‡S·Û±’pëQ­Bž?Ë“Ö) d]„}øÐ"ÃÇ!ÂÖE!rþñ™õ%+Ÿnœ³.Û7ùñññôôtÿý";[v…H€~Fn’l›ŽúBþqÿ¼uVÉVàIÿ•\(DVN„H[…ÈùÇgÖ—¬|ºqκœßêÇÇÇ«#‡Cg¿Âß9€¶•®Ž(k7Õ*¤c ´¼ü ‘l\fGŸO„H"³Ÿ¹_"D6"OŽÇããããÕïùùù¹§ÿÿŸ„H€!ÿ*ôPñO|5/„tx–—_-!rë)Eˆ!RˆlüÐu"¿[ä­ë"ûx§H!`Ì?ƒ =Ôýûž !˯IîÕ=¨‘ D ‘ºžBä×ïÑzõÛÞï÷B$-þTè¡â_ö\ X{µH„Èä㊠B¤Ùø¡ë,Dž\½AëËËKg!Ò«"@cÓu…Û±:ª¬Ú_T«Ž-Ðþ ,D’¨BþÿǨ‹ aK¦9óøÌý’ÚO×ÍY—ÿÛ~{{»ü¶w»]7Ë® ÐÞh]áv¬Ž*Ëw5/„tx–—_-!2ßÜ"DBØÂ)DÎ<>s¿¤öÓusÖåÿ¶¯Þõáá¡›eWˆhl®v!$©¶*$`í"i¯BŽÛ"…Hˆ[>…È™Çgî—¬üYŒsÖùÎ7_v…H€f&j’T °ü†~ D‚"+0B$„­ BäÌã3÷Kª>WOgï|óeWˆhc(U!Iµ›P!k¯Igàx3Œ a¯bBäüC4ëóë=Qggï|óeWˆh`"õ¦äÙGH€µWˆ¤Ÿ“p¼IFˆ„°W1!rþ!šõùk~Cu¾óÍ—]! û8êBHòl"THÀÂ+DÒÕ©8Þ0#DBØ«˜¹èMÿü5?…¡Îºüßöçççå·ýøøØÍ²+Dä¡UHòl$HÀÂ+DÒá 9ÞH#DBØ«˜¹è(MÿäÅ?…Ñκüßöûûûå·½ÛíºYv…H€¤ó³Û±’gï BÞÛÿ©ÌÏÆ9)D–œj„H{"—¥‰Ÿ¼øG0ÚY—ÿÛ~yy¹ü¶___»Yv…H€ŒÃ³ !I²k «îv Ò &Nªò³ a뫹î@ýú™Ëþ€g]þoûáááòÛþøøèfÙ"rÍ*$y¶ *$`áÝ:AzÍ™óªÊx#DBØ+D®>P÷?sÙ‘ð¬Kþ=||tü“"ÒÍÌnÇJ’Í‚ XuÓTH/žÃrRUr„H[h…ÈBÇêÖ§-8àcžuÉ¿çÇÇÇËïùùù¹§eWˆÈ20«$Ù)¨€%7S‚ôú9&çU­9Gˆ„°åVˆLv¸†=ëŠ?Å~¿/õh¯¯¯W¿çÃáÐÓ²+Dl?*»+y¶ $`ÕÍW!½ŠÈyUkÔ"!lÅ"3±‘ϺO±ÛíÖ¿‡ã~¿¿ú ws9ä— dNV!I²Ap!$`ÉM™ ½ŽÉyUkà"!lÝ"Ó´ÁϺzO±Ûí_yëZÈž.‡ü"2 ÉnÇJ’Ý Xr³&H/§cr^Õšy„H[z…ÈÇÍYð///ŸŸŸáýýýêûB~{{{ëoÙ"6›]I†} !ëíº éSƒYkò"!l"·>zKuÿ¡žžžÞÞÞ>>>~tÉÓÙï÷ÏÏÏw¾¼§›²ž/»B$Àó I’M Xo%Hjø"!l"·;†ÈV!rÇÇÇãñØå²+DDOÎnÇJ†í€ !ëíŠ éøB«# a+±{<„¦Cd×Bž/»B$@èØìBH2ì$HÀbëBHs "!l="iä¤Úü{èì}!/—]! h`V!ɰ p!$`½• aäYHˆ„°%Yˆ¤‘“jÖ÷p<÷ûýããc© !‡C÷Ë® 1-»+¶Õ*¤c ´¹Øª0Þ8$DBØÂ,DÒÈIµì›9ûýþééiÁ3><<¼¼¼ô Ï—]! ú¨¬B²ùðïBHÀJ+AB$D.ÏB$Ã8ïïﯯ¯OOO»Ýîááá²<žþûóóóÛÛÛçççhË® Pqçåv¬d˜ü] Xl—VHÇz›‹„H[¡…H°ì ‘U·]*$›Ïü.„¬´.„Χ#!ÂÖi!,»B$@½=—Û±²ùÀïBHÀJ+A?$!–j!,»B$@¥ — !ÙvÔw!$`™U!«c’ a ¶ –]! øVK…dó9ß…€•V‚nMJB$„­ÙBdìÁtÊ‘vÙ"Šm ÜŽ•m'|B–Ù¥Òñ…Qæ%!ÂVn!r»èô#Õ²+D”Ù#¸’mÇ{°Æºøud"!lý"·>nNB’,»B$ÀÚm‚ ɶƒ½ Xf%H`âà$DBØ.Dæ8hNE6_v…H€UÛ·ceÛ©^…¬±*$0}v"!l!"Ó.g#Û.»B$Àò‚ Ɇó¼ Xc%H`î%DBØZ.Df:VNH6\v…H€%Û·ceÛa^…¬±‹*¤ã £QB$„­èBd¦¥E²á²+DÌÞ&¨l8ÆK€Ö…ÀâQJˆ„°u]\+w”J=”Ó’M–]!`Þ6ÁíXÙp†W! ¬ !5Ó” aK»¹ú(åyLX³ì ‘3æyB²Õô.AVWBëg*!Âx!rÅ!Jþà0kÙ"&Íð*$Žî*$`• "c• ak¼¹ôµòø0qÙ"~ŸÞÝŽ•­†v °ºº+Pp¸"!l¥"¢˜Ÿ‚ó“ÈeWˆøenw!$[Mì*$`iu!$Pv¾"!l½"矦Ÿn-»B$ÀÍq]…dÃq]‚¬®.„ŠXB$„-ùBäüãö\ÎO"—]!àú îv¬l5¨»°´º¨4h ‘¶ð ‘3OðÏÂ)Jز+D\™ÒUH¶šÒ%HÀÒ*Aõf-!ÂÖ~!ræñéééà|Ù"þÎÝŽ•­æsB–V¨=q ‘6‘3OOOçË® ðïd®B²Õp.AÖU º„H›„șǧ§§ƒóeWˆøßXîv¬l2–»°®Î¯Ž/°pô"!l"gŸžžΗ]!àË…l5“K€EÕ…@äô%DBØ@ DÎ<>==œ/»B$0úæH…d“iÜ…€uU‚âg0!Âf!ræ! þq8E [v…H`è‘Û±²É(^­B:¶@S‹ª „aB$„MBäüCÔÇsÁeWˆÆÝ¹’ø!Ü…€EU‚6Æ„H„Èù‡¨ç‚Ë® Œ¸!R!Ùdw!$`QY!_ ð<&DB؈ DÎ?J<\.»B$0ÜnÈíX‰Ÿ½] XQ] d˜Ê„H„ÈEG©Ýg[Ë® ŒµR!‰¼] XQ%H É`&DBج D.=P->ÜYv…H`”MÛ±?r»°¢ª@ªñLˆ„°‰Aˆ\z¬š{|¸¿ì ‘À; ’øyÛ…€U‚²MhB$„ BäŠcÕʃÔeWˆúßþ¸+Á“¶ !ËéÌ éøAsš a£ƒ¹âp5ñÈ0qÙ"€Î÷>.„$xÌV!k© !´£š a„™ã©½l¾ì ‘@·»’à[‚,§.„’lB$„ÍYŽC$D²ù²+D}nyÜŽ•àéZ…¬¥.„òÏlB$„MYŽC$D²ù²+Dîw\Iä\-AÖR her"!l˜ÐÈr"!’Í—]!èj§£B!Âf!,»B$çâv¬ÄÌÆ$0ôâéBH`˜©Oˆ„°‰DˆË® dÞ³¨ÄLÅ.„¬Ÿ$0Îì'DBØP"Dnt„{d[v…H ã8ív¬ÄŒÄÕ*¤c ´°xªÀx㟠a£‰¹õu’dÙ"€tCµ ! †] Œ¾xJÀC  aÓÉØ!²Ò÷,ÔÒâ²+D‰u’˜IØ…Àè‹çÔ éø½ÍB$„Í(BäÖRŽ$ɲ+DY¦t·c%`v!$0ôÊéBH`øiPˆ„°IEˆ,ú »‡-í.»B$bDw!$° !¡WN @ˆ„Àaeà™­Bj‘l»ì ‘ÀÆó¹ IÀèëBH`ô•S…øïX(DBØÈ"DfªZ$.»B$°åp®B0÷º}å” þ2"!lj"…H,»B$°ídîM!©=ñºzÙœQ!_`”ùPˆ„°ÙEˆ ‘ë?j/»B$°ÍXîBHj»*$0îšéBH€#¢ ą!2¬B.ûBg&ñË® DÏä*$µ] }Ùt!$ÀAQˆ„°!Fˆ¬"k-\v…H t w;VjO¹*$0îšéBH€ßfE!ÂF!r»Ë!ï?‚““àeWˆâ¦qBRu¾• ¡×L `ÂÄ(DBØ4#Dnz9äýq~¹ì ‘@Ä®BR{¸U!¡×L÷b˜64 ‘6Ó‘B$–]!ˆÂÝŽ•ªc­ Œ»`º`æè(DBØd3dˆ xƒÈ"ãü$rÙ"€ºC¸ IÕ™V…Æ]0] 0z"!l¸"\Yü»‚Ë® Ô¿ÝŽ•ªÓ¬ ½`º`Ñ )DB؈#D&‘Z$‘Ë® T™½UHªŽ²*$0ô‚)A,#…H›r„È4!òËE‘lºì ‘@ùÁÛíX©7ÄJÀ¸«¥ °z˜"!lÖ"s¼Adño æ.»B$Pxêv!$õ&Xt©”  Í“B$„MZ¯~‡6žW…HÜ…È¥¤Þs9-ÙdÙ"`ÜMÁœà8±E:ªãΖ«äýé{ÅòáÃ…˜Z…H›à…È™G&àÈ;-ÙdÙ"`ÐÁüËï‚C:ô`Y®B^m‘ް×*>Z‘~™² ®B$„òB$Xv…Hs/°âæ«·>ÍQw¤\ïüŸ?Z¤#ì…ʇB¤_i€Dã« a3½ –]!ܬ~ È«ŸÌˆÃäŠKïÿ—?²×'>Zo‘~±Ò ±B$„Í÷B$Xv…HmP"Dj‘|­¸ë­©Ezeò᣿é× ã+DBØ /D‚eWˆ€¡¶åBäÕÉ(3d‰w~¼õvB¤×¤<ïèÇ *W~ÃòN³B$„MüB$Xv…Hdø_ôñëÛ] ‘#%*äyù޹$!’¶B¤_r€ì­ a£¿ –]!F˜üK'È[-’ÎGÇB ò~ˆ¼l‘ŽüÀ/EZ$UHë!@c­ a{!,»B$t?öW«n=7ÐÐX4A:¯¼ ‘‘l9Ü ‘¶"Á²+D@Çý © 11†WHçÕ¨/?Z$}TH- ý|+DBØÆ@ˆË® ½Nûä÷#kFÍŠQ Òy5ð ‰ @ø”+DBØAˆË® ]Žú¥+äÇ׌†¯‚t^÷’3/j‘´V!µH€Üƒ® a[!,»B$ô7çW®ß4£žGÄúBþ8…œWüØ,ŒƒB$B$%Ç]!Âö B$Xv…Hèiªß£‡Ãyyþ8¯x¥YÞ£îÑŠ3Ùy0ÌÐ+DBØÈ%D‚eWˆ€nÆûjo y‡`ÔÕdX!¿n‡HçU//0Å®IŒhô¬v^ä{…H¹„H°ì ‘ÐÇlx!ä‚Q3al‚œræ8¯š}i)OTÁˆFOoç@ÞéWˆ„°‘KˆË® ­Oõ›VHšŸ·HtúºRë #Z<ÏW©g`!ÂF.!,»B$4=Òoq;VúUHÖ¾¢TìB$MŸðÎ+€Ôc° a#— –]!Úç]Éâ!P‚dÕkID ã ™ó `ÐaXˆ„°Í† –]!æUHO€•+¤#Üï«È6ýñ«f-rÒŽÌy0î<,DBØ®CˆË® Íñ*$‹g?Bzýhª?þ!Qƒó `Ü©Xˆ„°ˆ –]!Úšá½)$˦> Ò+Gƒýñ›´Rƒó `èÙXˆ„°­ˆ –]!à]ɲ‘O…ôš‘þÍïüÏŒ¨Áy0ôx,DBØžDˆË® MŒî*$ˆ= Ò F³ýñÁˆœWCÉB$„mN„H°ì ‘nw;V–Mz¤—ŠFn¾zŸ`D Î+€¡çd!Âv)B$Xv…HH>´»’3ž !½H´ßÿŒ¨Áy0ô´,DBØvEˆË® iÇu’Óéå¡—þPkf"!lë"D‚eWˆ€œ³º É‚ÑN…ô ?ü:6 ‘¶‡"Á²+D@ÂAÝ›B2w¨“ ½$$ˆÎ áYˆ„°ÍŒ –]!²Mé.„dîD'Az1ЦÏÏB$„íj„H°ì ‘g>W!™;˹Ò+›¯Ì¢…HÛÞ‘`Ù" Épîv¬Ìšâ$ÈÑ_ôG€¥³´ a[!,»B$d˜Ì]ɬ®r…t„ÿêë«Çi!Âö>>žžž¦Ÿº§O>}É Ë® %Çlo ɯ3˜ Ùö¯¬7hy"!l»%D²…ÇÇÇà“äp8\ÖωN_xúòî—]!ŠÍØ.„äþô%A6ü˪?t1“ ‘¶ï" w<ƒO’÷÷÷õ§ñéAú^v…H(0]«ÜŸ»$È{¿4ÉSÝ| £É\ˆ„° ˜I¸×××È“d¿ß—:“OÕñ²+DÀÚÑÚíX¹?t©¿ÿÒdûÕ:Î…HÛø ‘ÄúüüŒPˆ$ÇYWðñw»ÝåãÇ5yõ'êiÙ"àúèâM!¹Ÿ$ȸß'7_ ÂH/DBØ>Pˆ$ÇYWõñ×?ìéAz=·…H¸9W¸’ËÙI‚Œû}Ò¨3Õ ‘¶"ÉqÖ•zðÏÏÏËzzZÿȧ¹|äÓÓu³ì ‘ð×Ä¢Br95¹rƒß*ý€ ³½ a[A!’g]©¯÷fŽ5Þz2Õ²+DÀ¿ãŠÛ±òc^’ 7ø­òæT›ð…HÛ ‘ä8ëJ=øÕ\X$D¾½½Uzä$Ë® ÿ›U\Éa©r…ôû¤?=ä ‘¶-"ÉqÖ•zð«!òããcý#ŸDˆ€Î§’c’ !£ŸôGBF}!Â6‡B$9κR¾Ûí"CäééºYv…HFQÜŽ•óI‚ŒûMòæ„üB$„í…Hrœu¥\ˆ\¼ì ‘ =Ÿ¨œOG*dÄïPtˆtbðïÌ/DBØvQˆ$ÇYWêÁ¯†ÈÃá°þ‘Ç£ }'nÇÊŸ¹H‚¬þÛ"Õ\™ü…HÛ7 ‘ä8ë<øæË® Àˆc‰ ÉùPT¹Búí‰l‘Îgî ÿB$„í…Hrœu|óeWˆ`¸™ÄíXù3¹²âïMhˆt20i DBØ6Rˆ$ÇYçÁ7_v…HÆH\É÷ $AÖú‰k‘Ncfo„HÛO ‘ä8ë<øæË® À(£ˆ ÉŸ)H…¬òë"À,ß ‘¶±"ÉqÖyðÍ—]!€!æ·cå{þ‘ Ëÿ®„}Àê aÛK!’gÏð+ü# [¥+¤#Ú¨Ú r¤_‘ > À¦@ˆ!!RˆŒýÿÿùGˆ o*$+dß²‰þ¨EPrk D‚‰)D ‘PhU!‘ Ëýrüþ6ŽB$Ù7B$‘‘B¤ %&Ñâo é ¶÷Grí¯ÅÔP!µH ì„H""Ùív•üx<^>òéé: ‘ÎOºš:JWH‡´½9§~‚ìýwbj<; 17h€;!Â6¢B$9κR~5D~~~®ä!š™7THX ×þBÌëB$-í„HÛ‘ ‘ä8ëJ=øÕùññ±þ‘…Hhfبp;VGµ±ñF…\þÛ°°? ‘´´e"!l_*D’ã¬+õà///‘!òôtÝ,»B$L.„üï äÂ߃ Rˆ ™ƒ aT!’g]©}}½|ð÷÷÷õüöövùȧ§ëfÙ"èaÌP!ÿcBå Ùão@±þÆy@½ƒ a;U!’g]©¯” ë%Î$Ë® @Û† 9øŸ†¾rˆþèw€’;!¶¬B$9κRþùùyùàÏÏÏëùéééò‘OO×Ͳ+DÐðtáM!Gþ‚™,A~Møý"Øx!DBØÆUˆ$ÇYWõñw»Ýú‡}xxèõÜ"h{´p!äÈ=´B&½òû›sQ$ l%„HÛÁ ‘ä8ë >þn·+þøÇã±RßÌ³ì ‘´7T¨#ÿÝ`Ä™º?þ!DÐÀ†Bˆ„°­¬I޳®à㿼¼3Ç«o=yz¢ž–]!€Æ& ·cùc%È6úãB$ ì)„HÛÓ ‘ä8ë >þÕh¸òm"O_^ÎéÁû[v…HRÏ.„ó¤ 5öB$„m}…HrœuÅŸåýýýêͺ¥êÕ[¼žœ¼¿eWˆ éä BŽùgž/„Ô`ë‡ a{`!’g]'ÚívWŸëô߇Ãý¯=}Â/ïrÙ"È86¸ë€è6Aêfß!DBØfXˆ¤‘“jÁwr8î<àÓÓÓ~¿ÿñV§ÿóíííêíXÿø5b6ºì ‘¤*\9à_*WÈFNdý*o=„HÛ ‘4rR-ûföû}Ùo£³›²ž/»B$‰Æ rÀ¿tx!¤ Y7 B$„í…H9©?[äé¡:^v…H²ÌnÇ:Ú_zKú#¤ß†‘¶I"iä¤Zó-i‘]^ y¾ì ‘¤$TÈѶÿýTHýÚÙ‰‘¶["iä¤Zù]‡Ýn·ì©O_ØßûB^.»B$ÛOnÇ:ÔÆ_‚Ô`«ýˆ a{f!’FNª"ßÛÇÇÇÓÓÓô'=}òéKYv…H¶œTÈÑvý•+dâÓV‚€[!¢‘ èx<î÷û———Ýn÷ððp~®žþÏÓ<ýÓéNŸ6Ô²+D°ÙDêv¬Cí÷›¿R€ö7&B$D"!€-ÇQBŽ3r´ õGèh{"DB!"ØfU!‡š7®ú#t·C"!Š ‘l0…ºë8“F« Ò%Ðï>Eˆ„(B$ D=‚ºrœ1£½©?À[!¢‘€ @èü©B2`4v!¤þ#mX„Hˆ"DB$A“§ 9Èh!A®NÎ"¨»m"!Š ‘DŒÞr¹¢™ éHxç"DB!"¨>sº²‰‘ଠ®ù×Ü R„Hˆ#DB$§M²¡‘àï8xç_ïaÊ©?g»!¢‘€ @­QÓíXÛ .*á­½ÿUÉ*¤·€.¶?B$D"!€*s¦ !› ®µÂ«ÿzçóÓ$H—@··?B$D"!€òC¦ ÙèTp»Eþø/µ+dà©§?Àx{!¢‘€ @ÉñR…l}0¸Ñ"×”ÇúBêÀœ Q„H@ˆ ØléM!û˜ J4G È»ë"!Š ‘”,]ÙÓx½BêÀŠ- Q„H@ˆ`íH©BváÇñO™ õG !‘€ ÀªyÒíX{qùSH“ õG $!‘€ ÀòaÒ…9ÿA¤¹R‚Ê"!Œ ‘,#UÈîdJú#P‘ a„H@ˆ`ö ©Bv9lÿŽú#²ý"!Š ‘Ì ½)dÃ@ Rnn‚„Hˆ"DB$3¦GBv6lœ ] l±"!Š ‘LšUÈÎ€Õ rz‹,t6é@¡ ‘ Q„H@ˆà÷¡ÑíX{ZúË%È_[äêóH*l‹„Hˆ"DB$¿LŒ.„ìfѯ oµÈÕ'‘þTÛ ‘Eˆ„HnΊ*d7ËýºÈxúÁÞù?/[äº3H‚*o‘„Hˆ"DB$×E·cíc¡_}©ã÷÷þ9û§/ýȾQ"!Š ‘\™UÈ–ø7\ýóþõ?^þ«þ$Ý. ‘Eˆ„H~ŽˆnÇÚúâ^èmÏÈSþ{Ù©?µ6MB$D"!€‡C²õe½P‚¼ì‰ÿi}ˆt $P}ë$DB!"øßdèv¬M/èåäÕ˜¸æ_õG ×Jˆ„(B$ DðåBȦ—òåñâÇ÷Ȯþl°"!Š ‘£„*d»‹x…V!õG`³Í” Q„H@ˆzt;ÖF—ï: 2 DºØ~K%DB!"Æ]ÙâÂ]3AÖ«ú#hc%DB!"UÈæ–ìÊ ²FˆÔ€ŒÛ+!¢‘€ 0ܨB6·X‡$ȲR‚òn²„Hˆ"DB$ÀXãŸ7…lk™ŽJ¥B¤þ4°Õ"!Š ‘ípg‹¿Ð…ÙèØYðrHýȾá"!Š ‘ípó[¤Û±¶´4‡'È oÊêÇ l°í"!Š ‘íp3[¤Û±6³(7ž gµH?n`³Í— Q„H@ˆho„›Ó"]ÙÆrÜK‚ü5DúYÛoÁ„Hˆ"DB$@“SÜ´©B6°÷• oµH?h ÑFLˆ„(B$ D´:ÈÝm‘Þ²%¸Óy"ý”€ŒÛ1!¢‘€ Ðð,w£EzSÈì‹o× rò›—l´)"!Š ‘ms%>$ȸew”)D‰·fB$D"! 9?*¡ ÙÆ‚;V‚Ô"€Ä4!¢‘€ МËVøÛ±f^jMB$u›&DB!"šsY\™t‘:Aj‘@ÖÍš Q„H@ˆhÎÊ©BF,¯¤ ¤Ý² ‘Eˆ„H€©yÖFd`¸H¶q"!Š ‘­rÞ2Û’ÚN‚Œ½‚ ÙöMˆ„(B$ D49Ź2ÕbÚZ‚"€¡7qB$D"! ±ùmŇ Y~m3AÆpzI·rB$D"! ¥ámNpœØ"Õ… h ò?[sž7tB$D"! ™Émþe÷?Á!]¸tJB$Ðô¶Nˆ„(B$ D40³­¸ùê­OsT—,š¤ t°¹"!Š ‘Ù¶Õoyõ“™·\JB$ÐÍOˆ„(B$ D¤žÖJ„H-rÕBÙi‚üº{‘@Ï=!¢‘€ wT+"¯¶H~Y"ûMß„H`Ðíž Q„H@ˆÈ8¤-úøµ ‘SÇÞä7!tÓ'DB!"ÒMh¥ä­É•eqŒ 0ôÖOˆ„(B$ DäϪUHAê—±ÇéÇ pe(DB!"² fõ+¤8u})” †Ú ‘Eˆ„H€SY…ùýÈ*Õ½E0}‚šÊo…Hˆ"DB$Àö#Yé ùãñåª+ËŸ 0ì–Pˆ„(B$ Dl `lB$„"!`³I,ªB~S¯$H¾„H$DB$À6cXµ7…¼c؆%Aðï&Qˆ„(B$ Dl0ƒ^ùÃh%K‚àçVQˆ„(B$ D„N_›Vȱ8 €«F!¢‘€ 7zmq;Ö—6 €;ÛF!¢‘€ 4w¹2`Që.Aú™”ß< ‘Eˆ„H€ê— °œåN.H´…"!Š ‘uÇ-²öB&A0k#)DB!"*ÎZÞ²ê&A°`;)DB!"j Z.„¬·xI,ÞT ‘Eˆ„H€ò#– YoÙ’ X¹µ"!Š ‘…ç+·c­´`IÙ` ‘Eˆ„H€’Õ !k,U$·™B$D"! ÌX¥BÖX¤$HŠo6…Hˆ"DB$@™ÊíX‹/O}%H¿#‰¶œB$D"!`í@¥B–]˜$Hªn<…Hˆ"DB$ÀªiÊíX .I‰¤»°ô³ý"!Š ‘ ç(²àb$A¶ "!Š ‘K†(·c-µ IoE…Hˆ"DB$Àì Ê…E €ÿcï¯ÛÆÙ…]§µ TƒZP jÁ?“nA-¸ÕàÔ‚ZÈæ¿“q,’¦Hà!\×Ò:ßÙ™D“WÂn“Xå© Q„H@ˆxbï¤B&Yz$HVüX*DB!"¦nœÜŽuù¢#A°ú‡S!¢‘€ 0i×äBÈ…Ë @!Q…Hˆ"DB$À7û%ráB#APÔU!¢‘€ 0¶YR!—,1%Hÿ-ÔóqUˆ„(B$ D î”|)äìÅ¥ÔéH„H#DB$@ÿ6É…ó– €Â?º ‘Eˆ„H€¯$rÞ‚"A°‰°B$D"!à¯Ý‘Û±ÎXJ$H6ô1Vˆ„(B$ Dü·5r!䳋ˆ Àæ>Ì ‘Eˆ„H€ßnÇ:cù ØèGZ!¢‘€ àv¬Ï-$›þ`+DB!"Ö·C*äô%C‚ ‚·B$D"!hz/äv¬‹Z¤1€ a„H@ˆÝ©— €Ê>ê ‘Eˆ„H Å-Û±NY ŠLî ÀÒ¼B$D"!hnÿãBÈo— €z ‘Fˆ„H ¡ ùí¢ AP;!‘€ ´²íq;Öñå@‚  B$„"!hbÏãBÈ‘…@‚ %B$„"!¨ÃS]…ü\—üS € ‘Fˆ„H æ­N¥×B~I#ÿtüJ4Hˆ„0B$ DÕîsêýRÈÇ&8ôOÇÿ” @ƒ„H#DB$Pç&§ê/…샽ÿtül1AÛ,ýX-DB!"Ú¶7µWÈÿÍÞÃ-rüW I.`µÏÔB$D"!¨joSïíX{&ð™ö‹ %H*#DB!"z66 \ùuOÝ%Hª'DB!"¶4MVÈÿMã$<õQZˆ„(B$ D›ßÏ´W!¿üU%H˜Nˆ„0B$ DÛÞÌ´ô¥<þ…%H˜Hˆ„0B$ DÞÉ´z;ÖÏg ž"DB!"Mîa®¿ÿý"AÀ B$„"!ØÞ¦ÉÛ±þ5u÷%ÅîèPmìýGë&HÀÕ>V ‘Eˆ„H`c»—†/„ü]ÅwAìüáZˆ„(B$ D›Ù·´]!KKî ÀF ‘Fˆ„H`›–VoÇš¤?Α$µ"!Œ ‘Àv,MVÈ´ rz‹” ¨› a„H@ˆJß®´w;ÖL òÛ)AÐ!‘€ ”»Qi¯B.ŒÝk0òµH €v‘Fˆ„H Ð]Jc·cMr©ãÇ+1þ+_Z¤ @k„H#DB$P⥥ !SÝpõÏ‹ñí/>þS €v‘Fˆ„H ¬ÍIK2á×>~~=¦üúPˆ” ¨ž a„H@ˆ Ú™4s;Ö„ ò±'NüG_þ©þ@#„H#DB$Pʶ¤ !Ó&ÈÞ«Ÿú§. 5B$„"!(bOÒ@…L’?ýü±ð!AÐ&!‘€ ¬¼i B¦M«„H€j‘Fˆ„H`Í­Hí_ ™#A&©Ó[¤Q @e„H#DB$°Ú>¤ê !3%ÈÈiˆP%!‘€ ¬°©ºBfM +äH‹4D¨˜ a„H oÃ[ˆ¢ççJoÇš¤?Ž'È€i|P=!Â(5Þ†· „NÎ5^– “WÈÏ-Òà B$„Qj0¼ o!š–k¬‘ 2_ˆ48hŠ a” oÃ[ˆ"æäê*d|‚ÌV!µHÚ"DB¥ÃÛð"ìr]_ ¹J‚Ì\!íÖhˆ a” oÃ[ˆòÎÆ]Yi‚Ô"h‹ a”ZÞ^–¡eWˆÒÏÃUÈÚ¤ @C„H#DÒÚðö² -»B$x®åv¬m$H-€†‘Fˆ¤µáíeZv…H å \Å…«ôÇõ¤ @+„H#DÒÚðö² -»B$fî­¢B6™ …HZ!DB!’Ö†·—ehÙ"ïöoÇZf‚ l”P?!‘´6¼½,CË® ,u7^!‹M±K@ý„H#DÒÚðö² -»B$°hÊÝòíX O1ŒaÚ!DB!’Ö†·—ehÙ"™“íf+d’þ˜é» ®Á` B$„"imx{Y†–]!˜3Ónóv¬¤ @Ë„H#DÒÚðö² -»B$ðô4»Á !%H-„H#DÒÚðö² -»B$ðÄ»Á ¹V‚œòÚü,†± @ „H#DÒÚðö² -»B$0uvÝÚíX%H!þ:+"!ŠIkÃÛË2´ì ‘À¤©uSBV“ Oˆ¹B$L=1"!ŠIkÃÛË2´ì ‘À÷óêv*dM òƒ ÉÎ „Hˆ"DÒÚðö² -»B$06£n§BÖ— €Ä§B$D"imxþ14V?þéËËËåryojÙ"Áét#_ ¹JÌô]Fä#DB!Ã{Üétª¾H ‘ÀØ\º… !%Hà‰“!¢‘ÞS‡ëõZ÷²+D_gÑ-TÈ’¤{±@™„H#DbxOw>Ÿï÷{­Ë® ü5…;V ˜yª DB!Ãû)ûý¾¾)D_çϲ/„” €Eg B$D"1¼µH!øoæ,»BJ@‚!¢‘Ô=¼‡Ãñx|ùÇõ_½¿ùãu¿ít:ív»¦Z¤ üoÚ,µB&é$ð¿£!¢‘Ðëz½ÇñlÙ"¡õ-A‘_ )AÉ ‘Fˆ„×ëuäêÈÛíVÙÂ9hQê ™`FZ)AN žzZC Ê!D‚ …¸ßïûý¾w`ŸN§š~þç‡ mÊp-äÒÏ$“ B$Õ"‡®‹¬ã›"…HhW†Û±.úPW‚T! LB$‘P”ëõÚ;¶/—‹ lUIB–Ÿ ] Õ"Aˆ„ÒôÞ õ|>W"½ÑÐÄêŸáv¬ó§ ê¤…"!Œ ½¾¾>ŽíÃáPͲ+DB+K†Û±Îœ|$H` B$„"a¢Þ»³îv»j–]!šX÷˨«ôG øßÑ„ Q„HXøßK5Ë® õObÜŽU‚V'DB!þ÷RͲ+DBÍÓWR‚ !DB!þ÷RͲ+DBµs×Ú·c• €¢‘Fˆ„…ÿ½T³ì ‘Pçĵê…$P !‘0ÑûûûãØÞï÷Õ,»B$ԶįZ!ëK*$TCˆ„0B$Lôööö8¶‡C5Ë® U­ï+ÝŽ5I” €¬„H#DÂDçóùql¿¼¼T³ì ‘PÏâ¾Æ…[IîÅ ‘Fˆ„‰v»ÝãØ¾^¯Õ,»B$Ô°¬¯Q!×J3þ·JÀo! ‘0Åõz­xl ‘PÉš^!%HhìƒÃ¯?¯°iB$„"aŠý~ÿ8°O§SMË® Û^Ðc¿R‚„ö>5üúòðšÛ%DB!’ZöårIõl///½ûv»Õ´ì ‘°áI/ðBH šÜYýê}xe€"!ŒIÅûp8,ÿÇËåÒ;ª«¹ò· ›žî+¤ ­n«~<¼>À ‘Fˆ¤ú}8f_9t-dM—Cþ"a»s]ÔíX%HhxOõëÛ‡W Ø!‘´3°ÏçóûûûÄgx{{ëý^ȯ¯¯õ-»B$ll¢ ¹r•þ(AB1ª_^+`[„H#DÒàÀ>¯¯¯×ëõK—ì~år¹œN§Ýn7òÇkº)ëçeWˆ„ÍLq!²Ö©BÂäÝÔ¯§^1`C„H#Db`?e¿ßßï÷*—]!¶1¿å¿ë†¤ !!ÛVê׌‡× Ø !‘ØÓÕw-äçeWˆ„ Ln™+¤ üž[!µH`C„H#Db`OTÙ÷B>.»B$”>³å¼« ü»‰úµðá5Ê'DB!’*Ýï÷Ëå²ßïS]y»Ýª_v…H(w±ÎV!“ôG *úhð+ÉÃ+ Nˆ„0B$u»Ýn—Ëåx<η»Ýî|>× ?/»B$ºR繫 <|.ø•ðáõJ&DB!’vÜn····———ãñx8v»Ýcyì~ýt:½¾¾¾¿¿·¶ì ‘qµýô˜ýS])A} ~%xU€b ‘Fˆ„HȾÚ>ß"sÜŽu­9÷ aŸ~ezxm€2 ‘Fˆ„HȾÚ>Ù"sÜŽU‚>ü xx€¢‘Fˆ„HȾÚ>Ó"˼²Ì©B¬ýÿ¯ÒÞ ˜ B$Xv…HÈ¿àNk‘VH ¶¼ÕÿUÁÃû$'DBàé„ –]!BÖÜÑYà—B†%H÷b…»ú_m>¼õÀ B$Y‘`Ù"!jÙh‘E})äÏçI»ÿå!SK‘xŽ!D‚eWˆ„À•7ÅC‚„föêb¢L ¤'DBàᆠ–]!òúÔ ¬$¬½'—eJƒßk@!?÷ ‘`Ù"!¯Ç²ö£˜Û±J¾ýÖødJÿsðšCˆ„À~B$Xv…HÈëK_+äBH òï´;™’§ÿñÊ@ˆ„ÀOB$Xv…HÈü_Ù§ü÷lÀ›’%H(`S½ípö¿“Húl½4J¡mÊ2^0¯ÐV#DBÜþ\ˆË® ™ÿ+û»ΫCQ‚„ðýs Í1_(ü¹€FÙH4ñ:<À¼&@ÏY q[t!,»B$ä\jzâÄ9%#Ö‘ UH Þ*×ÜsÔÀŸ9i”uôM–U˜WøBˆ„À]º –]!²­³ÙBd¾¹¤?JlyWÜbsL›ü~®J£ÜDjq}(+0¯ð™ u!,»B$dXa‡«âò/ˆ,3Aº+ÙýþÒst½ŸeÓ(K¨0îUËêÌ«ü!DBà^]ˆË® I×ÖѪ8ï=[!%Èæ7x]¿¾‚cÖx÷sã4Ê€¹Â÷fRÈóZ„HÜ® ‘`Ù"!Ѫ:ëñíÅ’$OîîZ^ðg[ÛŒÿêeJ-’B– ¯8"!jß.D‚eWˆ„¹Ëh† 9½EÎj dÅ›ºmºjŽÁÍqÈô –ã?|™ÒC<"ráð€Q!B¶îB$Xv…H˜µ†.x|{«Õÿ? ‘$;º-ºjŽ…4Ç!SªÙ*ÿíË”R&É—¯$4~"*DBÈî]ˆË® O®ž‹+ä‡ñ[­>^)A2°+ôе‚`Q}sRÂß!æï,Sz(ª-,|^I`èDTˆ„ ¼ –]!&¯›é*äxˆì½Gká R…\i/WÄ¡«à¸Ýæ8¤²ÿüeJjŠªª $<"!äðJˆË® VÌtýq¼B] œ ]¹‘Ü ‡®šceÁqD;3€LéááaSmžˆ ‘r~%D‚eWˆ„ï–ˤ rz…” ÝÅe?tÕÛiŽCL¿eJ-¨÷DTˆ„#,!,»B$ /”ëUÈgCdŠ¿¯¹¡-\âCWÍQsbXòúÈ”Z$Pò‰¨ !§XB$Xv…Hè["3$ȧ*äô™âï+AnkÿÖô)±æÏTü¥“)=<„H „Q!B²„H°ì ‘ð÷â˜'AΨã-2ÑßW‚ÜÜæMsÔW`6ˆyUEI!ˆ<"!ä,KˆË® ŸVÆØ Ùý¡‘ÿs¨E&úËJ[ܹiŽšãj¼+¾àZä¼¹BVó"€ñQ!B޳„H°ì ‘ðÏš˜-AŽTÈÇ9Þ"ýe%È mÕGÍz´Ù"¶JT!€L'¢B$„œn ‘`Ù"i~5Ì™ ‡*ä?£?D‡Ëßdu;17V!½*#ÚOUH}"*DBÈñ— –]!’†×ÁÌ r(Dþ©½!r¨EF&Hi&ÑFË—9jŽPœ-æ' €šir€OD…H9"Á²+DÒê"¸^…œ"ÿ¬YÞ>ªÅ;×iŽP·ã‘ô† d:"!äMˆË®I{Ë_H‚œR!‡BäG=ù§îŲMjýÛ²4Gà‹øx$º—Wÿ¥@;'¢B$„œ° ‘`Ù"iiá[#AöE“ç»]Ð/-žö›Ç XÉã‘܆ d="!äNˆË®IK^ 2¦BÊ:¿uÆ*NûM\°]Ëã‘ÐÆº-RŽ€ND…H9£"Á²+DÒÀz^!GÛŠ™d£VU!MSPŸ%ñ(É\¡Ói‘Z$0~"*DBÈ!ž –]!’ªWº’dÖ YYôk­&%hʼxd™ø2Ó"åH¨’ a„H@ˆ¤Ú5®°™/Dn1É Ëà&*¤Yøý|‹4¥48´H ž a„H@ˆ¤ÂÕ­È™£B[…ÄÄ€öWT…4í#¦Ïc&§‘&GB$„"!’Ú–¶Ø ùd¬©$AЉ…Üét•¿§I˜aÊAêÔMŽ€`B$„"!²Ý%@‚€2h‘Iˆ„0B$ D6:ÿüuú#m’# † a„H@ˆlnæo5Azë(Ÿ „H#DBdCs¾ [ G@VB$„"!²‰Ù¾Ô©?@/-ò"!Œ ‘õOõTH f# !‘€Yó$_^‚Ôà)Z$$'DB!"ëœÞ[JÞnª'G@BB$„"!²Â¹]‚€êh‘Š a„H@ˆ¬jV_»BêiÞ‰)š$GÀrB$„"!²’ù¼ÙÐû!D0L‹€…„H#DBäægòbîÅê¬ÉÞ!€ïÈ‘0› a„H@ˆÜö4^u‚lô"˜F‹€y„H#DBäV'ð2îŪ?fy{„H&“#àYB$„"!r{Sw Ò%yß$!€gh‘ð!‘€¹¥I»Òé­"XNŽ€‰„H#DBäffì¾R‚ z·„HfÑ"` !‘€¹¹ººé="ÈGŽ€qB$„"!²èYº®é"ˆ¡EÀ!‘€Yèü,A¶üþ ‘¤ G@/!‘€Yâä\K…ô ‘¬K‹€GB$„"!²¬i¹Šé}"(Š Ÿ ‘Fˆ„ÈR&d ÒÛ)D ‘Fˆ„Èõ§âí'Hob²·Sˆ '9~ ‘Hˆ„È•çáWHïÙÊx’ B$„"!rµxË ÒSî&#h™ a„H@ˆ\aîÝl‚ôH“TC‹ YB$„"!2tÖ• Ë•¥IZ"GÐ !‘@ë!ò¹|óéÿx>úl±BÖð®yH“ŒÒ"h a„H@ˆœS!Ÿj‘L›yS<¤I‘#h‡ a„H@ˆœòøçƒó×_ýÿ_œòg%ȹ/»‡4 @$-€F‘Fˆ„Èyrz‹ÜD‚ŒU=¤IÊ$GP=!‘€ùm…‘ß¶È’+d¦­ÁÇ×Vš`Ë´Hê&DB!"‡¢ác…üÕ½Hÿ>[äÐ÷In2Aj‹£ñû—7ä¯$M• @­„H#DBäX4œ"¿¶—çCdhÔ§ÆBä*ÿ^i )Z$U"!Œ ‘ãÑðóÿÕ"ÿ*}üSτȠ©3F~çf1ÿ‹¤I–“#¨Œ a„H@ˆü6~sEdï™"õǦƭ„Hi€$´Hj"DB!"§·ÈÇùm… ‘úãv;ã¦C¤4 Àlr$u"!Œ ‘Eþ/|üÓ"¿„Èþ`òñ z4DJ¤ÆšB¤4 ÀtZ$"!Œ ‘_+äp‹ü"G*ä—S!uF!RšT'ÂÈ‘lš a„H@ˆì©ßµÈo+äç™5Aꌥ”ÇqUñ .M4N‹`»„H#DBdo…ù È‘/‘ìm‘Õ$HñÙq•»¯5x¨4 °•Í•™Ú!G°EB$Ä}F"Á²Û|ˆøÀûcz‹üï7÷=ÕVZKƒOcrü…¥Éš´½WWÆ”J‹`£'¢B$D|F"Á²Ûvˆ¯SZä×ßœ­EJåtÆ$G11ÿ‹¤Ézޏìc\a\WP¶|9Òk @¦Q!"># ‘`Ù"¿ ‘#-²çw¦‘:c™©qù!ÌZÿ{¥É­z;ØÇ¸Â¸2® x.`+„HˆûŒ(D‚eWˆü®B>"{[dª:£3ÖtSÎ "MnãÜÁ>ÆÆ•qe\Wm†µHJ8"!b//D‚eWˆœ{9äô‹"R]j\~öRòË%M–x€éã ãʸ2®Œ«ºöÃ.`õQ!"öòB$XvÛ‘¿ü^R!§´H…±¾Î˜äàÅ·^6Q'“NV`ɱWWWÆÕº{H-€U‘·—"Á²Û|ˆüÜ"gTÈÞùß6s?Uÿ)={ä²õ\šŒ>ätK¶Eи¸¸2®ÖÝ4º4€xB$Äíå…H°ì ‘ÿ¶È)òW÷:uŸy§µÈʾØÑ,iÏ[ê{;¤É¼Çž`ɹWWW¬¾QÔ"ˆ$DBÜ^^ˆË®ùßáä 9µEþûÿêŒ4KšLyê–Ü‹ q…q…qÅÚ;C—FFˆ„¸½¼ –]!rZ…ìþ?ŸCäÈoûÒ"¥FøCšœ4ê–¬‹ q…q…qE»A-€B$Äíå…H°ì ‘‡È‘¼ø%D~ß"cC¤ÁÌI“5|%,‚ÆÆÆ%mÿäH‹`nB$ÄMcB$4OˆœX!{Cä”)5ÂS¤I!gWÆÆ•-Ÿ `ÌJˆ„¸iLˆ„æ ‘ã!òóÿÕ"¿ùSÏ„H£zI“B$Î4¼ªÆžùʘis'GX3"!n"iÏý~¿\.çóùp8ìv»Ïcµû?»_ìþQ÷ºßÖÔ²ÛtˆNŠ_vfC!rüJƒ4éáááá!:ƒõî´°©Ó"‹ E0߉¨ Ó˜IK®×ëñxœ>t»ßÜý‘F–]!ò±'>îÌFBäèŸ"H“Õž=zšßÅeÉ‘Æ`"…H˜Æ„HÚp»Ý‡Ã¼ÜýÁîW¿ì¶"ï@½¤IJò¨ »[Ži\®„H!¦1!’¼½½-ÆÝ“Ô½ì ‘¶tÐiÒÃÃÃÃÃÃÆ}Ó[µ”9Ò¸\ ‘B$LcB$µ»\.©Fr÷T/»B¤-´Išôðððððð°µßÖÞ,Y‹4ZWB¤ Ó˜IÕ’\ Yýu‘B¤-ð™4éáááááááAùû±9ÒûTwpõ×tg²šp"*DBÄ4&DR¯Ûí6þͯ¯¯×ëõóéþÏËår<Gþ`}ß)Dúü Œ“#=<<<<<<Ì7`K[¤·¨ëàªgº3Y}w"*DBÄ4&DR¯ÃáÐ; ÷ûýûûûøŸí~C÷Ûzÿøñx¬rÙm6DFäÀÔ!Dzx´ýøÙ’ø×Óóð¨øH9oŽôFÛ8ÁŸY!Ÿ˜ëš$DBÜ4&DR©ëõÚ;O§Óô'é~sï“|¹Ž²ŽeWˆ"’'¡ã ãj[ãJó/#wY!ÒÝIŒ *’«¥Ó]“„Hˆ›Æ„H*Õ{9䌋{oÓÚ=y}Ë®)D%O/öŒ0®Œ+yÔ£å|™#GºÛ!«#ƒŠäã*ÁŒ×$!â¦1!’ ]y¿ßŸ}ªîô>Õ·7wÝܲ+Dj‘@ÉŽƒ}#Œ+ãʘñh<_&o‘îvH|02¨H>®ÒLzM"!n"©Qï-U/—˼g{yyYx‹×M,»B¤ ”<9ØG0¸2®Œ+‰3@Âén‡$ F3Lª ùÍk’ q³I3{ö³ ]YÙ²+D ‘@É“’ƒ}#Œ+ãʸŠWª_Ý-Ò݉ïJU³o}ŽGʯÇm’ ëö!’Më½/ë {/±ìþE5-»B¤ ”};Î2®Œ+Œ«´¤C!ÒKZ~‹Œ¸Ûá¶äYÝB3t“ BÎ`M"!nú"©Ëûûûã<ËŸ¹{’ÇgîþuÕ,»B$@ëŸÙ1®Œ+Œ«Ú©fBdÝùòÙq·CæÁjo¡ilo¿Bþ5Àš$DBÜ¢!DR—|_æ˜ã«'‹Zv…H€mi8&¸¸¢B¤|¹V‹Œ¸Û¡Gk Áæn¡i,m!#æ«ÿ=“„Hˆ[…HêÒ› “„È×××LÏ\Ȳ+D”½is°qÅ6øÎM6:_É…5̉92ân‡­ÅÍo¡©ßÕûxÚÜ\‘·—"©Koˆ¼^¯ËŸ¹{!ûWÆÆk^¬Ó ¤ÀíæË§BgÆ»z¸öm½A¥ßyh‘SND…HˆØË ‘Ôåp8D†Èî_WͲ+D8ØÇ¸2®0®(õðbKWpKÛJ–›¾ˆÉ£À+× *!r['¢B$Dìå…Hê"DÎ^v…HûWÆÆ¥^4q+ie0>DJB¡‡‡éD²ïå…HêÒ"o·Ûòg¾ßïB$ö1®Œ+Œ+Œ«Ö^œZéB¤ „HZÒ›xòÕ—]!À,Æ•q…q…qå%"=<<Àþ<­Ç¡:‘ D ‘€ %D’p´ÜíP-r ÍÒ•/‡ÝzˆL8À>?§µÃ¡:‘ D®ûŸ0ÀŠBîÒIsîv( 4Œ *ò…È$ìá ›&D‚ BdÌ‘®ˆ¤¡ë_òßíP@i9Tû-·AWD‚ B$Wþ»j%n¡iPQZ‹ìºY÷\B$ éM<9'ÿÝ…·Ð4¨(ªE]€¬{.!’º‡L#ð~¿?>s÷¯óšÀöüHÜ"ŸM%" *Êi‘cC ëžKˆ¤.½!òýý}ù3_¯W!êp·C„È%-²÷©¼Èµ™“ÕS³Öò¿ÀlB$•é ‘×ëuù3 ‘P€»"DÎn‘CÏãEn0œ-¿.Òë ¬Kˆ¤2çó92Dvÿ:¯9lNÀÝ"çµÈ‘'ÑïLVÏÎZ°:!’ʼ¼¼<ŽÀ···åÏüúúúøÌÝ¿Îk›ãn‡¤÷ã÷òùÍ“`²zfÖR¨"–G!’º¼½½eÊ…ù'ÌÝIïÇÒùý3Àè 6eŽòêÑË£I]ÞßßGàétZþÌÇãññ™»×6ÇÝIïÇ¢9éÓ“˜y Xy"i`T‡åO»ÛíŒm¨ƒ»’Þù-rꟅYó˜I Xsy"©ÎápH>ï÷{¦¾ Ä{êô~F…tæo\T5ä¼VÀZ„Hês>Ÿ“™cïWOvÿ"¯6ÔÄ݉?F¹GW X‘I}z£á¯‰ìþxò¸ H"lðl <¯°.!’vöý~Ÿ÷l½÷e5° V¹GŽIÌØóÊ«"©RËeÞ³½¼¼$¿Ä nB$Uº^¯©.Šº²ûWx†‘Ôj·Û=Åãñøìótäñyº'÷ Œ"©ÕÛÛ[ïh|ê–ª½·xítOî!DR±ÃáÐ; »_¿Ýnã¶û #Ük 0Nˆ¤b·ÛmdXÇËåòå«»ÿóõõµ÷v¬|1"©ÛårI;˜Ý”` !’ê%l‘ÝSy=¦"iA’éZH€é„Hq»Ý‡Ã¼ÜýAß ð!’¦\¯×ãñ8}èv¿¹û#^7€g ‘4è~¿_.—óù|8v»Ýç±ÚýŸÝ/vÿ¨û ÝoóZÌ#DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$œ $'DÉ ‘@rB$ÀÖ]¯×———ãñx8wb‡t¿áííív»y¹ˆ™¯¯¯çó¹ûýþËÈÜívݯŸN§î÷¼¿¿{¹X®›â.—K7Ý}Ì{óa7ÿ̇^.óãûýÞ ¡¿U÷wxü+uÿ¨û ÝoóWT+*›ÿ­ëÖ‘7±<+ΟKØÐ2y¹ ‘OyÜç{MH®ÛÛ[O™ø±û<øìƬûhÙ}„tˆjñÊ7,Ïçóã ÆÝt\f\Í[7»ÁóxˆçSjËV±óC÷w;Óÿ>Ýoîþˆ÷Ô¸²ù7¨"•ÍÿÖ½½½•3´lÚHB$ÀS7i^Òê>!¥zqøü±ñÙ#÷G///N$,^ Ç䌓±^Ýó8Ù0®&º\.óЬªÁkVÉóC÷„½—MÑýAó•qeóoP *›C+ÕвiV!D<õ!ÑGn#ª^>&¢§.Ùø–ûZ¼–[~2öèr¹xïlŠF,?’õ&Æ(|~èA+©qµîºcóoPÙüZaC˦X‹ 0ÝËË‹)ެÆ4ÑëÃл:ŸÏ^[‹WŸ)º)ÑÛgSôè~¿'ùI~oâ¦Ï’Ì—ËÅ1¬qµîºcóoPÙüZ‘ëŽM;PÙdhïÔÇ7wÛëë«õ”Ù‡Ãáåååz½~¹=N÷§>¾‹düÇ_}x´x•ù™ÂÈ´)züWϾ«Uµ²3‡…óC’k!]dd\ÙüTù•Í¿¡•¼EÚ´õM†öN@ n¦8R™ræU2 }ÛËÄïæ¸^¯#_ƒÕ=×Ùâ•ü3Ån·;eÝßçË€¼\.ß^Úæ:#›¢)ÿê?ºñöúúúx0ÛýJ÷ëŸïnç­\ýÌaÅù¡ãÇû£èñ¯4~ƒD_•Õø¸²ù7¨*›ÿ†Ö~¿?ŸÏÝðèÞ©Ï_ßÙýÿOYt:_¤M;°ÅÉPˆøØöÞ|ÌGBo¹ã…jy"êý‘æî£ë³Ÿ=|}9tÅâ5û3E7bÏçóôñ9~Uˆ³}›¢ß. ™x=Z÷¿âr¹tCÔºÖ™C óÃÐÉü”…uä²Üãñèoy\ÙüT©•ÍÝC«{»Ñ2qHtƒ¡‡#û›v`£“¡ 4®Û8}ü¸×ËËËÈOšâH¥ûda=e\ïÉ÷öóÏÍ>eèøëÙO²X¼?Stÿöy‡ZÝÿ„¡³}÷z²)¹~m·Û9GÝÊ™C!óC÷wXþTC†ÍŽ+›ƒ*á ²ù¯uhuÃ`FJY¹ž½(Ò¦(a2"S¢)Ž`ÝgÉéßuååjväøqÓ¡³ó>Óøâµð4cÊ”8ûä:6E#ç]ÆÆ&ÆRQóCoUŸq1cïóœê7;®lþ ªTƒÊæ¿Ê¡Õ- ßÁ¡+[Ïç³M;PëL§ô€)ÑGß~Ï‚ÁÆårYøyó©á—ä™imñÚívïŠù­¡ŸØ÷¥3-oІîHöúúê*_ióÃÐQüŒƒÓ¡XàT¿ÁqeóoP%T6ÿõ ­TË÷þ ÍS?cÓlë¦SzÀ”hŠc¹¡oÚúYG¯X›z/¸HrÈÙ;Ò\Êañ*sÌ»ÑS³ãj(uk¨·Éš˜ê–ª³OM{÷ræ«Ç•Í? •Í?O½ƒ+Î6í@î˜NéS¢)Ž…zÒõÏõìv»LƒaèR¯¹Å«ÀéÑ)Y³ã*Õ]4©xûôÔüvèZI+›’*›¶òÚ´¹?`:¥L‰¦8–ºÉŸŸ4ØŸš6ñäX¼æ¹ÝnF¦qõ¡÷€k·Ûùú¡f-œz/°]xíFï%–©îÂG5ëŽÍ¿A5ý=µùg+ï M;û¦SzÀœiŠc¶¡ŸcÜï÷gžT‡ðFšÅËß™’ߣne”xH8özoŒ¹ðÛ²zo”çÖÁæ4›Ò†H›Ê|(`Ét!DØb‘I÷²÷XõËõãƒaá©é‡Þa톢×ÜâåïL ïQïÅkî÷Å’±×{§ß…Çû½É@5§Ùü“6DÚü3ô®»â˜»€%Ó… `‹E&½÷ï꼿¿lôê=¼Jòy³÷"ß¼fñòw¦÷È/I>öÆ/IK»R{§Ìi6ÿÕ¼÷ÔæŸ!½·¬ÿøžY›v`‹Ë¢ `‹E½7ëýWƒ?†Î¯–ŸÆ÷>s÷ñÖknñZWïiùqýÇ•a@Úù¡÷Ï&9ïžäÛÌD›ëŽÍ¿A5cPÙü3¤÷ºþÛífÓlôó£ 0}Îô²0Qï/ý£Áƽ?º¼üsè늼à¯2Ǽû¶6®z‡Áº?óÏÖç‡|_æ˜ã«'©`ݱù7¨NV6ÿôÞ²þt:Ù´Ûýü(DLŸ3½,LÑû³‚# 6>ÛívC»¬ÙçœçóÙ!¿Å«L½ƒ³ûEoeSãªw¸ÄŒ%óCo.L"»Õ3Ó3³ÝuÇæß Z2¨lþ™8¥,üšc›v`ÝÏB$€‡$Ôûòã_Kd°ñÙÈÏEœh=û ´÷çi“|O¯åzßÜ7¬µqåK÷H>?ô†È$ß:Ú»ª ‘Œ+›r *›¾}ûJø-›v`áçG!ÀÇCRé>$öž¦v¿8òùÑ`ã‹Þ7ýrà9ñûA†~žÖ¥F¯ ¼­ûóÞ«ÞÛÇ=~—_÷ÛºÓM‡Ãáq©ÝívݯŸN§××Wó[ν_­•/Dº7]#ãÊæŸLƒÊæŸ÷®÷kˆS­_6íÀºŸ…HIeèƒÃøç>ƒG§ÓéÛMW÷{Æ?“}]‘ƒ‹W!zKÁc¢îqõíõeÝT6´¼Ùï÷~DëÎB$1ëŽÍ?©•Í›º7ôã笆îÐÛýz oŸM;°üó£ àã!I ý ë·§^½z¿…ª÷Ãi÷;¿üŒôý~Ÿw2†Å+ÌÐOVÏþ:$6:®zçºap¹\F¾9kÊôh8mÔòù¡÷ÈtâõDãz¯á"W6ÿdT6ÿÍt“L —Ú´Yç:!0gšâ˜nè§O§\‘a°1äz½N?„ßï÷‡C£ñp8$9ƒÅâµÜý~ïÛÝ/z[W½ßå7riÀ³ŽÇ£û†mK’ù!ë µskv\Ùü“{3cóßæžêqu¡BÞ;›v Ó\'Døxȳ†¾‰ãt:l,×{JÿìeAîRhñ*J±ß€Cü¸ê½r-­ý~ïz I2?‘d]wlþɺ™±ùomOUòe†6í@޹NˆðñgÝn·¡@5ØH8Ìf_äÂâUÚ_rèÎc¾h¦Íq"Þ–“­ÌB$ùÖ›636ÿMí©z¯ˆ,ág¨lÚ|s àã!ÓÝï÷ý~ß{ñÅôÁlŒ±…?}8üÀªÅ«Ýh«îŸÙæ¸ê]FOäN§ÓåréÆÏã·bu¿Øý£oïæúÔÒÌÖç!’LëŽÍ?¹736ÿ î©FÞÊ/´ibæ:!0gšâøÖÐJžúñEƒ‘O½çêÝgÒ———§.#r"añ*ÁÐ½ì ¼aã*íw$].—‘Ù=›w³Xiç!’LëŽÍ?Y736ÿm}+ãßM;<× ‘€9ÓÇÓé”äë 6zõÞ g·Û}`ÝgÒËå2t&ÖûÕE~~Õâµ–‘——ï]³ã*ù—[u³ÜȬè­eJ>?‘äW6ÿdÝÌØü·éúnüŒ¿­‘wjµirì–…H™¡û˜ê+9 6õžtuN‡N¦Jìv»¾p„Ö¯¡{Ù}‘yãZW™ráP/0Þ ”c~"I>®lþɺ™±ùç÷w7æùa*›v ÓnYˆðñg }]¼¹ÁÆçóyöèú8”ÿ¢4wÔ±x9Ððµ}ÆU¾'uÞТdš„Hã*í¸²ù'ëfÆæŸÏn·ÛÐwÑÚ´Ûýü(DøxH’áq8 6–{{{ëýqègŸçz½ŽŒ¶xÅp a\­õäCáÀal9òÍB¤q•v\ÙüTù636ÿô¹T—`Û´…|~"|? ‘>Ræ’j6xБï§^‡Ž#|®´xåÏ4Œ«)z¿Ö*Õ“÷6£€¯Xbõù¡wÕëÆC¦A%DV?®lþ ª“•Í?ãzžê|>Û´[üüh0}Îô²à,‚|z樂ê‚C÷}ê>Þzå-^ɽ¿¿Mb4Œ«/zOJS ’¡©Ï{º¢˜ùá|>G†ÈäÇ”6®lþ ª“•Í?3Vœ´?úbÓ„}~´G˜>gzYpA>§Ó)Ó©é‡ÞSÙãñè•·x¥å@øzJ¾+×ìè 6?ôÞí0Éíìz¯OñÅ£Õ+›ƒ*ÇdeóϺÛ›v rî²GˆßïaIµÎò¨÷–8 ?ºE¡Å+€ ãêY½ÁHˆ¬Räüðöö–)æKœ”<®lþ ª“•Í?+nclÚà¹Ë x¿‡%Õ:Ë*ÓŽ[E¹õÞaìÃétr a\M6 ¯/3ï"x~è=_íþEËŸùx<>>s÷¯ó×=®lþ ª“•Í?k ›v ~î²GXåC–Të,ñÓŽAeåÓ{»Â„‡ÿÔ:®ò£ßg°»ÝÎ{l•ù!Ó÷juãÇJÚาù7¨rLV6ÿ¬òÚ´«Ì]öHvìl;n·›ÁFùoqï—}8ŸÏÞãêÙçOÕ {ïJ—¤FQþüÐûõ£ Ÿ³7mQM+û1ƒÊæŸ0CwOµi¶¸Õ"ìØ1Ø(v$$üÚ)W™OrèÆUïiÿ‡ËåâM1®¾•ï^—½?óïœ-̺óC÷F'_U{¿zÒˆjj\ÙüT6ÿ„é]tfÿô‹M;°îVGˆðñƒô~0Lx{œ´Ÿd1Ÿtn·Û~¿ú\ð$ºÇUo.Ì÷u~ŽÚb¬>?ô®z ÇUï…$溦ƕͿAeóO˜ÞŸ¨™÷Ó/6íÀê[!ÀÇC 6Šý¤Ù¹ßïIž¿÷øôååÅ+o>™gènQ /g£‘qÕ{ÉÆòÛÓ =mªI•ò燴`hDy»[W6ÿ•Í?†ÑЦ(a«#DøxˆÁF zh9ÕÏEßn7<Í' ].—¡ûý^è1®žÕ{éb÷‹Kžóåå%ùs²­ù¡÷~ö%±½#*áµK´³îØüT6ÿÌ[tftj›v ­Ž àã!…ïÖ–ßH°÷¿ûìé57Ÿ$<ù¸ß— ãj†ëõšvöúáÿî_ä ͪ¨ùah\Íøk ]™bD58®lþ *›zßÊ×××TÏ6´y6RÛ´ålu„H1Ø(ÿHdÉqDï%!¾(Í|2Ãý~N® 2®êýª¬y3U7Pw»/Æ VæüÐ;f\Û{¤ß=¹÷½ÍqeóoPÙüÓûßõ~¿_þ*#wR~×z›v ´­Ž àã!匌ì²ÎçóŒ'ì=;u o>™g¿ßÏ„?N›ëÔȱÛS£«{ž¡ê~tY•9?$¹óáÐYîŒ/êºcóoPÙüW¿YêÆØìæ;t-ÿ³ãÁ¦(m«#DøxˆÁF9†ŽLÿ\|1ñSíý~ùë§~žóÉ·(œÆWIŒß@ìÛK Æç½îy+W9pX}~ºØ¶ûõo—Âî7ŒüqozËãÊæß ²ùçÛqu:¦«÷÷÷‘k»‘ðÔTmÚ—Z!0%šqA9ÆÏþ|ª½\._Žåo·[÷+¯¯¯C?í² ‹× çG,ïxƒ›¢‘àÿ¸Ä ›âº‰îóY\÷vóáÈÙ[Õtà0c€u‹ãÈót+æãz:e1u¤ßø¸²ù7¨*›ÿêÇÕápèÞå···Çå¦ûÅóùÜ{#ñ%ï M;PýRëuœ¹³?Q_ÈA„ÅKˆ¤ØMÑý~o‘3¨58Ì`—Ë%í¿Ý%ƕͿA•vPÙüWi›v ú¥Öë 8sg,7åG£ŸµÛíDX¼„H ߥm‘*de³XÂ9û›¿¨o\ÙüT •Í¿q•°#Û´Õ/µ^gÀ™8‹ ‰ëõúí]z¦;O}±/gÆÕZo_7YÏgÁÈ@J;À’´H×BW6ÿU¾Aeó¿]ÉïçðcÚwÛ´Í.µ^gÀ™8‹ •ûý¾ü§£»Ï°_¾Ž‹—Iù›¢%ç±§ÓÉWøUyà°p€u£¢[ã„©{\ÙüT •Íÿv½¿¿Où¶Ç‰—².üa*›v ú¥Öë 8sg¤u¿ß»¢3~Îöt:¹“ÅKˆdÓ›¢nö{ªIu8$`×ëõx<>uU‘#}ãÊæß ŠT6ÿ›Ö½///ó~î¥[q’ÜÏÁ¦¨~©õ:dr¿ßßÞÞ>>Øö~¶í~ñt:u¿Á‘)PÙìw¹\Îçs7Ë}¹Ö`¿ßw¿ØÍ{î™IªAÕýŸÝ/vÿ¨û nlØü3[÷¾¼¾¾Ž¿ƒÇãñã´âu"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"€ä„H 9!HNˆ’"àÿØ»¿+U}¾oÀw ¶` Ö@ ¶@ žî3[ [°[ Z˜7ëçzfÍ;?\ך“=[1b`òåcÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DŒ®žÞZÀ "úTOo 9x½^UU•ey¹\Îçóß¾q:.ÿ ¹ÝnÏç³®kû X«€DÀ^óß‘Äív«ªJ1q‡¼%¬èõz•eŽèÑ¥zQ÷û=lG?²ºv!ˆ€Ý æO§SY–Éq;üà-aÏçór¹$¬Ùß'°YýÈóÚ… ¶>˜¿\.£çFv‡¼%,¬®ë¢(æ+ÞO§ÓívÓ€l¯]¨D`Óƒù²,íÛþ;üà-aIÇ#«^?Öºv¡€íæÏçsÓ4öpŸ~ð–°˜ªªþ·ýÈÿÚ…J¶;˜—EöÜáo Ëè“BEq¿ßŸÏç¯û½†9üòñxÜn·ð˜Óé$ˆöqíB% 滟R×õóù¼ßï×ëU¹Àß}KX@8f»ïêXUÕ Cõõz…“@8À‘À*¬ 6:˜´…ªªº§G¹_dÚ¾¿–0·¦iºÐ)ߨë:lA_.`‘°ÑÁüˆíÜn·ŽÁüóù´«Óîð=µ„¹užUU%y‰¦i~½ŠÝÌZÀ "`£ƒùq›ê¸Ýår±«“ïðÝ´„YuL‡¼ÝnÉ_ë{v¤=ÌZÀ "`£ƒùÑ[‹®ÐøV×µ½ý%ˆdqmß8N3½âû~”ö<0k+ˆ€æGo­cîÕý~··¿‘,îz½Îº(+À*¬ 6:˜Ÿ²Á¶Ô£( {ûKÉâÎç³IÊÀþJiA$lt0?eƒÇc[·‰lš&´¹,ËÐÂÓéôkíÊðËð_ááa ïððŠUU]¯×І¿­ºÝn¯×+«þ§º®£Îçó»ý ÷jÏ:ڤОðËð_ƒÞàý~/ŠâïÖÂ/Ãåó)±–aç€]^»DÀFóS6øz½F é=>¼Äívû{‘?üæz½öoêóù,Š¢Iž²Ào°mbéß·<ºIs”]ï°¯m ^Tx§SöjŸwQ×uÏýúÕÇx(¼Á¿}¯í­åGf^bk[†–ïüpðk‚HØè`~¦az3ªªú5iq\ãëºî™%E³¿)éRw›ûGf¿Ò³«’ô}>´oáã»Û,ËrPKÂ[h›jú|>ÅL™Üб­a™ÌÈÛG¹J瀃_»DÀFó3Sšñ|>{^çÿؼªª¦—'£Ó¥Ž6ßn·Ñí9ŸÏC³Œ´sÁF»?Xl¶ã]´­<¢%á š<›UÙÖªLæâm=ˆ\±óÀÁ¯]"`£ƒù™ „qÍ:©­»m#æv,«˜jç$É2†f‘©>úŽÅxÈìÚöçôú{ÞëóùÜtÌÔÖµÆuàLNA™\|X·óÀÁ¯]"`£ƒù)¬ëºmÅËÍx½^C<ìx•„)äè(gÖ¢é|>/üѧ bFdvmûaz3.—Ë×´iª·¶ŠŽnŸí-,×=޶Òùàà×.‘°ÑÁü” ¶­‡ÙÄ´ÍJX8Üï÷îœ(<à×b•áŸá—ÝsÃfªžŠ¢{àg“꺻·{~èív[ì£ïbÎçó{—þœ§ùý:òåð_ý§vöߟáEÃKÿŒÞÞŸoGK:>ú[ _åï8ŽF¬è›É)h¾+=¿]C瀃_»DÀFóS6Ø“uds©Áårù8/,< #“š>ƒïWQUUwѽôhÏin?úІ¶‰‡}véWg4<1Nºâå ©²£·VÅ*Çxø¤º#³uo¹JùßÖ{{Î)ΤóÀÁ¯]"`£ƒùÑ[ëˆ<ºÃŽAó•ǯì/l¼ªªwÝ~[j0h>cÛ*ÓDמ¶,£ç”®‰}[â<è>wqÏyagAöÜNQû^è·¶Öd·IkhðZ6W)ÿÛvHÏ?“οv!ˆ€æGo­ã†zÓkŠóÛ´-M9(5xkËþúoª# ñî¦^S>ú¶;ŽØ¥mqLÏyaýÍTm{Gi·6bÿ$Ñ=)òW?ü»Fñ†NASN=ßu>~íB ÌÛTÇŸ×ÕSÍD‹ŠÞ‘mô:™Ñì/¼Ä”>:Ÿj ˜ú̬œòÑG'” ½]æ·h~Ýs—¶Í M˜ê&ÜZY–ké#î¸z>ŸÃ»ŸÎ¯»af{ šršêß{óéüpðk‚HØè`~Äv:RÈ>ôRÍDûëñx¤]ü°-ûë¹²å27å¼\.óµ$:#¬Ï+Ý¥}>ú„û3 ~_i·–DÛ‚¢ý…ö‡TU•0—\²üoëi=W3έóÀÁ¯]"`£ƒù¡y>Ÿï{˜p&Ú/sLL‹:=§X&/vÚ"àùZ}ûó=Ë,áþŒvãÑsÓ¢[t;Ñ9t,žt®Ð|%@tËçsµÝ$n­w]{öc82º%_nz¾]8´Ï<µ¹ƒÈL¶–Ðãñˆv˜)n·Ûècj±½Ô6!tÐÜì:?üÚ… 6:˜ïxüëõz>ŸUU•eù1Îè?Õh¦`¾Yi㲿™Þi4 w_ί a§ÏÞŠ~R³.0Ûó­e²µ´ÂQ™pjäÄ{¹.³—Ún‘9hFgn~íB GÌZðp¦ š¶L\—õ-:»êcö7Ó;¾Í3ªÆµ$èL\š²­=}–EM»?sÞZráð¼ßïá8MxÔ‡lèÍ ØKmË÷9`sîüàÚ… Ž9˜”B~ÍFD§ VU5}Ë9,%úmÜŒªq-‰&°óuÅ…÷ç¡‚Èou]ßï÷¢(R-Ù:è›{/…Q4l1å0·Î®]"à€ƒù¡óŒ¾f»¹\fšÁ”ÃR¢3®%Ñ]ºb(ˆL«išÐÂ!|½^§|Öý—<{/Eo8bÙÜ:?¸va‡Ì_¯×º®S5cú»‹Îð×Â_¢w‹[~)Ñ)›Dnkkk ]ýMF½ëÞö­íV˜C×ͳó€káp„Áüét*ËrJÀ—O<7÷Æ·DþoY ïOAäG¯×«ç%û¬T<ë^ŠÎž²8sn\»0€]æ/ÿ¹ÝnUU%™`(ˆ\¾1‚Èmm-7áÀÿ8G²Ï¤È™öRtÎrP–eæ—>\€µFãö3,3˜ßM3‘Ë´_™óÁ5«ªª:>¾>sçØKMÓDçl†_æéÃ5Xk4n?À2ƒùÝ4C¹•ögò "Gx<SVgc/…×rÛʃ °•k‚HØè`~7ÍDn¥ý™|Ä‚Èq.—KÛÎË略Iš¯×kgKpíB ›Ì獵ÓiŽ<â«åtáåVy§áÈ€‘IîšCÏDŽÓ±@ëÂ{)z,÷û}¦Î¶b瀃_»DÀFó»iFt¢Öóùœ¾å°‘Læn̸–Ì·Ksè9‚Èq¢¹üòAdÓ4£Wˆ]÷|Œ(‘°ÑÁünš½U\’¹Qa#ùÜ/Ú˜²,çhIQŸu»ÝöÑ‘É/ ,¹—¢Aáù|žxkÈl;?üÚ… 6:˜ßM3n·ÛLÓ£¢gŸHbŽwmLUUs´$ºK‹¢ØGD&¿,°Ø^*Ërî¥Ssëüpðk‚HØè`~7͈®YÚçNŽEï>Ùg‘Æ9Þi´1ó—q-‰îÒ Õ¤³u{Ž 2í›=ŸÏËì¥ÇãÝNÚuSsëüpðk‚HØè`~O͈nùõzMÙfxúè'§ÑÆô [Ó¾…µ¨Dæ -¡[f±â¶ãqŽ>™U瀃_»DÀFó{jFô¶nWg.…Ús…Æäï4Ú˜7ˆœÒ’è.]k^˜ ²O3&&ïµ-‹úq}àé{)ôºóù¼ØŠ©Yu~8øµ A$lt0¿§fTU•öÎqá‰Ñ >åßi[cú¼»Ñ-i[³Oú™yÏÙk9묽¦iÚªø>ñÜĽMÏçóLÉ`V~íB Ìï¬Ñ[(^.—q[ OœrßÉ´ï4Ú˜æfFwiÏp}¼^¯>·üDiFØ¥iï™øÖ6I°ç¼ã){é~¿Ï±öòV:?üÚ… 6:˜ßY3Ú&EŽX 5ºê  "á;mkLϰiJKÚvéô8¦išÛí¶Ö=7÷D~Gð ãȶNØÒñè½ÔvcÊžs““ŸO–ïüpðk‚HØè`~Íh›Ä4(‹lË\úO‡l{§#Ö‰mkLÿ[ãMÜçÑɘSVý™Â"ç>¸ÂÇ715 6zsÆ¡}`Ü^j[”x™%R3éüpðk‚HØè`~Íh›<Õs½Êð€ŽèaÐ:mé …Æt@ýo7qŸ·%Aßálÿœ+¼£h®º|Ï9Nù3‹<ƒî¨:|ÇDÈ¡ëÛKÑC`ôzËCeÒùàà×.‘°ÑÁü.›Ñv;¹ï82<àW"þ~Ù‘úái«§¢(Þ-ù9M²iš>´(åô}Þïþ̹ªªúµcÃ[{¿îŸãÚ3¥1É÷ùãñïez¤U–eÿ7%ˆì/ìÕ>7yúy ½Mêô½”áõ‡U:?üÚ… 6:˜?H3žÏgQýK’ðàð”äï4löý_¯×«ÿʱ—ËebcfÚç¡U#Ö¿}G0a¬û.öD~«ëú~¿êüÑ~;z伔íõ‡…;?üÚ… È_Ó4Ç£,ËËåòkNSøgøeø¯ð€7¿-¼ÜívûÛžóù\Åý~ßÄœ©çóšz½^ÿ¾‘÷{yïÛªªL[QØùïþöþ¤Úfæ¾?¯ð˜ðÈéqüîéü– HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚Hüùô×Ê_:@é¤zF‘¨¦•ÒÐ_Ó4UU•ey¹\N§ÓÏÑrøgøeø¯ð€ð0ûê ¥“ê øD Êg§¸ƒ—Ã*qT+?=ŸÏ¢(ú_& Oñym¢zÚnåä<D´9ŸÏNq[/¥¿‘¨VR¨ëúoúÙSxbxºO-óêI Ì1DD5Mã·ƒRúK €je²Çã1ý’Q؈Ï.çêI Ì1DDÝn7§¸”Ò_‚HT+ÓTU•êªQØ”/ÛêI Ì1Düõz½œâöQJ "P­Ld.¤y‘›¨ž‘ÀCA$@ϺÞ)n‹¥ô— ÕÊXu]wßùñ~¿?ŸÏŸO ÿ¬ªª(ŠŽ'º_džÕ“ ˜c("ˆøÖ4Mt#§¸í–Ò_‚HT+c].—èkÏç×ëÕýÜð€ð°èÓ‹¢ðfX= "€9†"‚HàÈêº~_7Tôm%¶SœJ\7à€ÕJxõè ]¯×þ ŽnäןÃ/à ºíK]×÷û½(Š¿[ ¿ ÿ°Ý2Ïn> ÇãQ–eØç§ÓégÛÂ?Ã/Ã…Œ¸}Ït¯×ëv»ýmØw«œ¤€ö¡×¿ž?öÕÜÃÔèJªÇráé·^Hõ”Oõ´Ë Rõ«'‘°»C]¹Ò~•‡ª6ú=äè…X(º•Ïm‹N)¨W,‡s«ÄŸÏgQýÿ(‡Oÿ’yŸwzËý~ÿU>wô.§jàÏãß Wxf­V¢ãü‰IMtuÖðBª§|ª§íV^ª'Èy˜*ˆ„Ýê‚Èm”ö 78T:eY:Ÿ‡²èõzµtçóyèˆPz¯òYì#ˆ¬ëºç…‹èåY/eD¿0ß-ôŸ¶ÞòzÅ¿?®ðÌW­DÇoÓ7-"TOùTO» "UOÕ0U »;Ô‘Û(í—lpt¤ž~Õ;¡$ôÒ$Õ´ 2ì·é ç¸”ñ|>{~·OïŽz±âßèWxæ¨V -:Ylú–£¥Ä*cBÕÓÎ*/Õä´+ðD¿yÞÿ›´i…²,3ù<þôP¹·«ž¾ŽDªž ³*C u~‘›lpt˜þ_í³µéwÙØq%>ý[Ð= Øž_N{,D;Ô€D£Gé¡ roW=}/ˆT=@fU† >êü"÷SJ¯¸µ£‘ÑgM¼)OÛíf–?Òö.`ÂèQz(ˆÜÛÆUO_Ç "UOY•!ˆ„…:?‚ÈM6xJÙ5ÇÖU‰Oÿ t›èíEÂË)¥ਗ¤‡‚Ƚm\õôu° RõùU‚HXø¨ó#ˆÜjƒ³ÚÚ¡*ñèMa&®,ô]_(¼ÜòÇ‚ñä1z” "÷¶qÕÓ×Á‚HÕäWe"á‡úÞÁ¬Î™Çi° r­—¾^¯ŸRUÕô6òwËáå”ÒÀc×ÍG;®V‘‡­ž6÷Òª'ØÄ0U »;Ô‘»*•ÒG«Ä/—˸%€>Š®ê^N) ¬4|•Bf: D¶zÚÜK«ž`ÃTA$ìîPDîª@VJ­Þ‹¤®ëé=$z»™ðrJi`½¬2Ç• ò°ÕÓæ^Zõ›¦ "aw‡º rW²Rúh•x†—}”ÒÀœƒX)dvªè,³$[nšfÜ,3ÕÓ^ËŸÜJÕÌñT »;Ô‘(í•Ò‚H¥´ñðc)d^ªhùz½¦oyôr—ª§½–?¹•*ª'˜ã¨ vw¨ "7PÚ+¥‘JiãàÇð@ ™Ñ€*ÃûöZþäVª¨ž`Ž? ‚HØÝ¡.ˆÜ@i¯”D*¥€ÿ„ …Ìe@U–å’Adx9ÕÓaËŸÜJÕÌñT »;Ô‘(í•Ò‚H¥´ñðg …Ìb@u»ÝþnüñxLßòý~ÿ»åðrª§Ã–?¹•*ª'˜ã¨ vw¨ "7PÚ+¥‘mN§ÓLwä©ëúï–ÃË)¥€œÆ´RÈõTÇc¦¸p¾ˆSõ´ÑògâsUO°‰aª vw¨ "7PÚ+¥‘m2¼#RXvX+…\y@õz½þnüz½NßrQ3ÅFª§–?Ÿ«z€M S‘°»C]¹Ò^)-ˆls½^ÿ>å~¿Oï!Ñ…°ú\PRJ‹l¥+¨ÆE0E篩žŽ\þL|®ê 61LDÂîuAä6J{¥´J<*º\U’ïŸG‹ô>Kl)¥€5·RÈ5Tщf·Ù4ÍLù¦êi»åÏÄ窞`ÃTA$À>FûJé}TâÑ%€úÜ‹ä£è÷Ïû,[¤”VßJ!WP•e™üfŽÑ[O†R=¹ü™ø\Õlb˜*ˆØÇh_)½›J<ú¬‰·Î‰Þè§ç{QJ­Z‰††'šE§˜M 7UO;(&>WõùS‘ûí+¥wS‰E±ÌeŸðBJiÕJÿ—hšfÜ֢벮8T=íæ¥UOÿ0U °Ñ¾Rz7•xUUÑ'Öu=nÿ‡'F7ØóûçJi€V+Ñ,&ŒTÇmm¾{ù©ž¶^þL|®ê ò¦ "ö1ÚWJï©Þ‘är¹ŒÛÿá‰S”8`µ½ûÞ¸I‘mÓ!ûÜqOõ$ˆT=©žØú0U °Ñ¾RzO•xÛ×zG|i<úUöAßfWJ³Z‰æ;=¨ü)ºxfÿdGõ$ˆT=©žØô0U °Ñ¾Rzg•xô²ÏÐjº­ŽtÙG) pÌjåñxÌ7"í¹Ð¥êI©zR=°õaª `£}¥ôÎ*ñ¶µ°‚óùüq«ð€èšBo¯×K)  Zù¨mH~ÿñ|áOW=)¾´ê r¦ "§Ä}œ•Ò;«ÄƒûýÞÑÙBAð«¦ÿ ¿ ÿÕñÄð€u+_〭T+u]wl°(Šªª¢#Òèr¬ß>†˜ª'AäÐ}¨z€cý)œ‘ÙÖ³_["¿ÚWmÄ}R”ÒG®VÚnÀ7Úº‹²ªžvùÒª'Ø÷8ÖŸBÀ)Q™m=ûµñ 2m5=¢ŽVJ¨Vf‘aSÙîaÕÓÖƒHÕìuëO!à”(ˆÌ¶žýÚ~ùõi•¡¹¯ù(¥T+I²ÈæBªžvùÒª'Ø÷8ÖŸBÀ)Q™m=ûµ‹ 2x½^—Ëe\· Oœr¥4€jåë¿ûE®5"U=í¾üI»UO°³q¬?…€S¢ 2Ûzök/AäÛóù,Š¢‡ Oɪk)¥6]­¬2"U=í¾ü™£Çªž`7ãX `IMÓ<²,/—ËétúùW8ü3ü2üWx@x˜}ÀL#ÒªªºG¤áF¤¨žàP‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€cƒÅž€a§ñ³îì{Ô'ˆÈSÓ4UU•ey¹\N§ÓÏ3møgøeø¯ð€ð°£5†¹Ç‹=ÃNãgÝØ÷¨O Ðær¹¬rŠ{>ŸEQô?ñ†‡§¡1äv%dÊÓ"¶X­¬2jͧ1Û~¬5ÐD´9ŸÏ Ÿâêºþ{=¡§ðÄðô½6†ý] 1ŠØVµ²â¨5ŸÆ"†W‘QMÓ,|Š{<ÓOÂa#ûk »¼b°¡jeÝQk>D ®"¢n·Û’§¸ªªR‡Ã¦öÔöz%Ä(`+ÕÊê£Ö|#ˆ:\Düõz½–<Å%™~˜j*bVaÇWBŒ"6Q­ä0jͧ1‚H€¡ÃA$@Ϻ~¦S\]×Ý7[¼ßïÏçóçSÂ?«ª*Š¢ã‰ãnјUcØ÷•£€ü«•LF­ù4F 0t¸"ˆøÖ4Mt£YOq—Ë%úZçóùõz}¼ }zQ[o û¾bµ’ɨ5ŸÆ"†W‘À‘ÕuýžÐ*ú¶ nÖS\xõè ]¯×þ Žnä×ÔÅm5†Ç®„¨V²v?lw°$ˆ„ƒŸ~ü8%®pŠ‹^O10º2jØøvÊÂbO çj%Ÿa§ñ3ÀŽÇ±‚HØñYáÏS⢧¸¶ˆMÓ ÝTxJtS×Sͳ1¸`&ˆ4~8Â8V ;>+"×=ÅEW1­ªjÜÖ¢÷‹é¿ªjVaÝa±§mµ’Õ°Óø`ÇãXA$ìõ”Ðò㔸Ü).íK´ÍCÜbcX÷@X«Oµ’sícü °§q¬ özJD®{Š‹.…:qÎ`tVcx¡m5R Ò‘ÆÏÇ "a—çƒÎ–¨£‹—>)Û Oÿ»ÍðBÛjÌºš¦©ªêz½^.—Ÿ-?ŸÏá—á¿Ý4³®ëûý^Åß­…_†ÿ p!eŽ1t¿²,Ãn?N?Ûþ~þ+<`ÄýO'z½^áøÛªï&9ñp4ÿþ÷ïû'íÿªV¶Ûà¶ù{ FSëÞzÞøÙ:ÚDÂ1Ï‚ÈÕ+Ä_eõÛÄÊ.º jx¡m5f­O³®ëè,ÎhœúqçTUݫљ§SâÈu¯„ä³ðÛóù,Š¢ÿ %ï"ô–ûýþëÊIGïrúà8~†‰óÄžÿ%ˆÜMùz½zÈÃH{Ê(nÅAln9æòãgCh8à0U <ôøaöÒþïÆÏçóô͆ŒhvVYþÓ EnY–ƒþØ…Š¸í›Ø¡0¾ñnUU­ÒQwDÖuÝ3ù^È|ÑéÆÝBÿY÷{þ°˜_yâ¯H±Ïï‘û"û'ðW(6îË“‚ÈÇφÐpÀaª x2D®^Ú‡)ZGOßrôë¬ÝYVYþÓŒ.!ÛÓ¯÷Ò4Í ¯'É"‘_ÿÍ?>€I¾ÿŸÏgϯp÷é]°K#ÅŸ©âÇ_ "÷DŽH~ÆO#²HAäŠãgCh8à0U G;ôþqΜñ7ßýGÜí1«Æ,ùi6M3â{׿|8”ÌÓÿ†Ž(œ‘Ó?ÄŸËä¦ÚÿS®§¥Z6¡#XìþÍ‘Sȯ½‘a´3zRÞ”,òàAäºãgCh8à0U G;"s(í£V’ìï~¿ÝrVYòÓ±€jÛ]/“”Ìãî¡yð 2áU”q×RÚ>Ç$I21ò×/vüóà)ä×^‚ÈT†ÞWâÈAäêãgCh8à0U ‡: üqÎ\4ˆ|>ŸÓ·—7"ˆ\«1«ÿ,Ëòñxü¼ãIx÷û½c Ž’yÄÖ†Î=rMº~.á¿zò{ÿw_嘣kEñ«=¡c„»ûZP’#ò ?þ¨VvD†QSUUGMÝwuTe6ˆÌaül ¦ "áP§Ad&¥}´Ž›/ûëžg—UcVü øñ'ƒ¾<ö3ü 踜ÒÜ>áv»u·'üoÛüÜq«]ÀI!ó©VVŸN§0„î^W³ûÞ G€9 bW|z&ãgCh8à0U ³v~’þì§´Dfõ°,Ëžw)Š¢OÉãgChØ7A$,ØùDæ¿ñí¾Ó‰ÍèÿUÛ>=n­,Ëü/e¬{ 'úÍç¡«Úv á%îZmïk•Ûªp¢CAäê ‘‚½E¿ñØr߃È|ÆÏ†Ð°o‚HXþ°ó#ˆÌ㇠"Çm*ºÑèižÓ·vÀ òñx¤]©íBVŸïQ§íáeYf2‰€# "7Ýà)§£‘YŸ ¡`ß‘°üaçG™ÿÆ‘ƒDÿéÿíß>[›~ƒ•M<}ôs§O#íyù¢ÏWÄÓöðè:Wƒúô':DnºÁ¯×kô6Df5~6„€}DÂò‡AdþDÒv’µ¶vÀ 2úĉw5m»_çÂ=MAäŠ×p‘°'ÝÁâÇ_9‹D®5¤Df8¤7„€ÌG}‚HØë/ˆÌ¹´Ïê>Þ4$óOS¹â5A$ìIw¤Øç÷‚HA¤ r[ãgCh8à0U »<ÜgþQÚOU–å’Ù_x¹­4fŸ¦ rÅk8‚HØ“î<±ç "‘‚È Ÿ ¡à€ÃTA$ìòpDf^Úßn·¿<Ó·|¿ßÿn9¼ÜV³‰OS¹â5A$ìIw˜8åU+i° Ri ùú‘°¿c}‘¥ý$Çc¦„nDª˜Uc6ñi "W¼†s:fº¥i]×·^ÎUàhÕÊVãîq¨ 2·ñ³!4p˜*ˆ„ýë‚ÈüKûhÉ|½^§o¹(Š¡efVÙħ)ˆ\ñNn·4uØ_µ²•¾%ý¡‚ÈÜÆÏ†ÐpÀaª öw¬ "7QÚ+Ù>Š~ßu[ÉÿÓD®x çz½þ}Öý~ŸÞI¢+ Lä]EvY­l¢ÁÑñ[Ÿ[Ò*ˆÌmül ¦ "aǺ r¥}ô‹©·Ù4͸H1«Æäÿi "W¼†]ï7ÉÞè%šk»Šì²ZÙDƒ£ã·ªª2Ä®øôÜÆÏ†ÐpÀaª öw¬ "7QÚ—e™üþ‰Ñ»=öùJpVÉÿÓD®x 'ºTŸ;Ñ|ÀûqÑ*WQ€]V+›hptüV×u†ƒØŸžÛøÙ8LD¬RËDsº‰_L~%µOž˜Ucòÿ4‘ë^É>qâ½G£wJ·¬±«(Àª•ü¿õÌ×Dæ6~6„€S‘kÕ2Ñ—hšfÜÖ¢K¡N,¨×jL柦 rÝk8EQ,“›‡r8lµ’yƒ£ã·ž °-ˆÌjül ¦ "ÖªeFßÓ$jâ½?²jL柦 rÝk8¡[FŸÛg®¨ðÄèûLàuØkµ’sƒÛÆo=„G "³?BÀ‡©‚H€µj™èÝ:ÆÍCl›Øç6&óOS¹ú5œèýh.—˸ ¶vä ò«}m¨Ñ†Þ%ÇU`÷ÕJæ—¤úßš0‡AìºOÏaül Ʊ‚HÀ)q•s`ÂøoD%žscæû¸÷±µƒ‘i¯¥¸Š¨V¶õŽÆ•G"W?B€q¬ pJ\ë˜$þK5ý0«ÆÌôqïck‚ȯOkLe{ËU`CÕJ†ïht¹qð rÝñ³!4Ç "§ÄÏu]_.—q/ž˜öVŒY5fŽ{[D¾½^¯Uº««(Àqª•´ïèt:=ŸÏqC¸‰å† rÅñ³!4Ç "§ÄÕÏ¡/Š¢ÿ‹†‡§Ì´£²jL&•© 2‡k8™tWWQ€£U+©ÞQ‰½ÿëõzõ_,ôr¹L/7‘ë–{†Ð`+ˆÈAÓ4UU•ejíÓéôëËÃá—á¿ÂÂÃŽÖøØ]Gww Ð]²Fh·Ûíïî|>Eq¿ß³ZtÅøà/A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘@r‚H 9A$œ HN $'ˆ’DÉ "€ä‘d5µ[öA t{½^UU•ey¹\Îçóß?ô§ÓéòŸð˜Ûíö|>뺶ß=µ[öA 8 ®xÖrÎ$g¯×«,ËÓé4zPÅý~Û±3q8 A$à4(ˆ„_žÏçårI88NeY†ÍÚ·8 ‡ pD·º®‹¢˜oHp:n·›ýŒÓ À"§AA$¼=u`V®7Nƒ‚HªªúßRìmœŽÀõFÀiP }RÈ¢(î÷ûóù¬ëúçs›¦ ¿|<·Û-<æt:à4À— pDrxÏç³û®ŽUU5MÓƒ¯×ë~¿ŸÏgœŽL 8 :kqdMÓtü/ËrPùK]×a 1œ„ŽI 8 :kqd·Û­íÏwUUI^¢iš_¯b·ã$ p‚HÀiÐY‹Ãê˜y»Ý’¿Ö÷ìH{'a€#DNƒÎZVUUm÷…œéß÷£´çq8A$à4è¬Åa]¯×Ye'a€#DNƒÎZÖù|Žu]Û98 0‘ ŽxàÿøYò¹: îïC½^·Ûír¹œN§Ÿï4ü¦,ËÇã1w꺮ªêz½†Wüµ·Ïçsøeh^hFÓ4 _4líù|†-‡÷xùOôã~ÿ×»G àöô‡;||ïú׳ï7ûëá6#l¹­á7á÷áe‡:®Ûc–!ˆ„#øcóÄÿ "szõ>Ïmšæ~¿ÿ ÛÜn·äoðJ´M»‹º^¯Ïçsô+¾“Ç¢(FÿÍ ­ mž˜Ô úd¿câhp³Ê0`C‡g]×m Ìþýd“gîË÷ðl›Ñÿƒø>Ê–9 .ù!ÿ °0A$ñÀ)þO™Ù«|îív‘Á%œ†SUUÏ ô¯¢(ÆE ÿxM¹ObÏO¶Ï.Ze0÷Ľ$»14²òõ3ÞMõîVéá6cÜñýý‡ã‘«÷X€å "áˆþðTñ‚Èü^½ã¹Ïçst0LÏ"›¦i[uî–¤ýû•6ýù€þŸÑ*ÀäSö’ ÷û}ôÇöüÄN¾bÏ­¡«¬8tÜP¹nX‹ zì wœB~í1ˆ12털×ëµâ´ÄäÂÎçóˆ½ÑñɆ­•e™Éßжkî%a§ìÆTéÛèN¾nϪáYë7D®ÞcV$ˆ„ãþýâÅ}§_» "“\î~Ï÷ŽÒ†##¦kÍñWlD*×öɆ÷2t²ê¬=°c¡Èº®ó<<§Ìöý1o±‡çÓŒùRÈ‘ëöX€u "áÐg€O!ãîSȯÝ‘m©âý~ÿ¹Øf]×Ç£ûne#çìGÎçó»?çõ¼[R–eǵúð_ý§µíÛíöüÏχ͆ß|lÀˆ½Ñ6é,·¿¡M7t­QJøˆÃ{ÚÉC¯Ø\ϤÝm›zq?7ø>ÜÂÇ>”T·FÝD¹bX Ž~hB~í=ˆ¼ÝnÝóÚÂÿžÏç$Ó›¦iÛÔåré3½®ãjý/¿ÿŒc³Mòê¸ocx kýyµ†O­;ÒZ÷f‘}îºMw|ú^QÓ'~fÒÃshFhCÛ‘~ßs•×plv„nkÀç"—ì±9DÂÒŸ¤?3×= '9_¯×ž“›:2…A³´Úî{8èÞsÓ¬z6&<2´dÄZ—ß{£-å™i…ØðI=_ÿž8¶@oìžõžºZ˜á(eÐì°¶·Ù?pϤ‡çÐŒ¶6\.—¡³hÛ¢ðµN೑ ÷X€"aéƒÎ 2Ñ«w̸:‹-<>º©þñS]×ÓÑ·¶ˆd±5 ÛÞËý~Oûç5ìœÕ×>ýú4)²{ßµF)¡%#æ…µÝAµÏ§IÏ¡mm:kx¾ÓàŠòé±™DÂÒAd¢WŸ8ò—è¤ÈþÙ_tòΠäî§ðºÑ€u±Ï%:á«(ŠT^ÃÌ*JqóÊóùü¾óæãñ˜o­Èé‰ðOS"æLzx͈¶aèm.g= ®ø!Ÿ A$,}ÐùD&zõ´-Fo=§8E¯–žõÕ>Goô‚«CE§ˆžÏçé^ÃF{Ó;À ᩪ*a.™üðŒ†hûj&=<‡f´EcSæÉî>ˆ\¾ÇäC Kt~‘‰^=mËï÷ûèè-šaM\À3í̯¡&Þ·î+õ|ÕeDçÇs:Þ÷¾Ìíðl[t=<‡fDOs1Adò A$,}ÐùD&zõ´-o»MdŸçþ]ÖuÐäÁ¨èb¡×ëuÍêýj´Çã]¤wŠÛí6zŽä»1ú»ã¼Lzx͈î½w¨Ìó4˜ç?¢ÇäC Kt~‘‰^=“ 2ºTãô©‹Ñö,¹ á1ƒÈ¯ÿfƒ&œ9ñ¶˜sìÆèÀŽÛ¡fÒÃshFÛº¬»9 æyàí±YDÂÒAd¢WÏä |tFU’Ù:Ñ?“ìö¦ižÿ¹ßï·ÿ\þÏù|Nòçlë Ã. ;§{oŒX¯uèý1çØÑÅE;æfÒÃshF´ EQ8 fÕc²"ˆ„£ŸÚÓ½™‚¿Mœ7ñê™\Þ·.·æõzÝï÷ëõ:1YÛJ¿J¨®ë°ëŠ¢Hµdë e<çØC§"fÒÃshFtªìô©y‚È´= +‚H8ôàSÔx„,R9qƒ—Ëå ôŽ^¯×õzMxÓíô«™¼g’Þn·°W§|îÇcÅÝu:n¶˜IÏ¡Ñ6LŸ•)ˆLÛc²"ˆ„ãþýBÆÝg‘‚ȉÌ3ˆ|>Ÿs4l+ýj1u]¿£É¢(íÉž÷‹œi7n.üDæ}̪ÇdE =ö‡Ä‹ûÎ"‘7ø¿e}lOÓ4óE6[éWky/ÛgýÛžw¸Ë!Öɤ‡çÐŒ S6A$@æ‘pÄx°¸ã,R9qƒY‘¯×+“?gÿkX×õÇ9’}&E "³j† r= +‚H8â?*RDæöê‚È_¥§Óér¹EqûÏý~þŸ$»×_àªªŽ üïZ»Q)ˆœiƒ‚H€_‘pÄlž(ˆÌêÕs"—ÿ›¦9N—­ªêõz-°{ý5|{<SVgÍ6ˆ<Ú™*Ÿ#ÏÓ`λ`]‚H8â?!LDæóê‚ÈŸn·[ÇäǪªú,šð}ùkø­í–á÷kíFAdVmD:Ÿ;&ˆœ‘S6}V]×K~ˆMÓtL»A&Ù½þ~ëX u•Ýø|>¥¢9ôðLš!ˆÜDÈŠ pDNÙ`t¾Û÷—ÑuõYüs¦Ýë¯á·º®7DæÐÃ3iÆLmD¦í±YDNƒ‚È),Šâï³n·Û’âõz®ÈºbÇð×°Ï`c•Ý]Å·£ÇæÐÃ3i† r= +‚HÀiP9eƒÑ‹äEQ,ù!žÏç¿m¨ªjÅŽá¯aŸÁÆ*»1šèuô–zx&Í(ËrŽDL™¶ÇdE 8 "§l0ºl`0îÎŒ [>ñöy‚ȹ? óù¼ÊnÚ[rèá™4ã~¿Ï†&? ¾^¯=ø™Ü¥`A$à4(ˆœ¸Áè—\9p±‹ÿ[éWYiKÐúÜÁ3ùn|<#"ÑÕ{x&Íhû(s; NY-6·\ȇ pDNÜ`tåÀ%çj "§¼Í)ÓÇúˆ®çÙsmÉä»qÜWïáù4#ù ĉŸròe™s;ð3¹E)Àh‚HÀiP9qƒÑ9;AY–+îFK³öoç|¹FÓ4mcƒ>ñYÚÝØ6¡ïcWY½‡çÓŒh.ÖgrëLŸòårI»C²:ðG÷X€|"§AAäô žN§Ñ³Þúx½^«FÈ)/ž{¨ ò½Øã”-Û´Mâë]¥ÝÑÙs=oq¸nϧm‡Æèhlâ±v»Ýþ>7ì¥-iš&tˬü)= ‚HÀiP9}ƒmiÂôˆ¤išï¬¡í1Ñøàr¹Œx¹º®Û²³}‘ß;-aÙëôÏ­îÆ¶Æô|¿ëöð¬š}õ‡[xÅéÇZÛ,Ñ¡Ý8<¾-ä]ëÀŸØc2!ˆœ‘I6–8eåÏŸÉHwcÚˆAÑÌß—;`ù*MLµêºŽNæÚ’L¾ë˜é6hMÑ{xVÍh;LíÌŽDuÐÁÒ¶öïétêyëÌŽî‘êÀ_«Çä@ 8 "“l°®ëŽ?§Ó©¶õ|>£×á;žÒ6›©O4Ó'‚ÓµæøkÒ4MŸ%?*˲mñÉåwænúÿõz±ªdž™ÎZ=<·f|œÛ3›ž¾+ÂF¦7潌sžA¤Ø.A$à4(ˆL¾Áº®ÎOìoÁðºSòˆÓéô½øáA‚ȦiªªšâtïÕžwÇ[æðL²¾åZ=<«f„Î3hÓhð—ê`y½^SòÐðôùüz,ÀZ‘€Ó  r¦ >éSßwxüu_£ÑÛíösÊÞA‚Èoa÷¹ÉãÐÏî~¿'ï“UUëW¡cŒ˜•™[Ï­áÀ‘u~ –ðᎈeuÑ9ü|z,Àò‘€Ó  r¦ ¾=ŸÏwp{Ç"?ÓŠAúG3ï°ìïÕþ£‘ßêº;dÊL·w€2zdÏÝXUUÿF†8ß´²UzxVÍ}¦çK‡ìï‘ö`éŸúýúîÁÜ~>=`I‚H–ñ|>ï÷ûõz½\.“‚óù~_–eUUSf‡ýôz½n·ÛßIRá7EQ„Æ$Œv)|Ç#ìÃ÷§Ö6ÝìýÙ…Ç„G¦MO>*ÞKˆnó·S½›þw±9eË÷ð¬šñaÿšWûýAÌú® ýöÝ+~¥®ï÷ž$%ßAX€ ØÐ(ÅnAØ A$°¡QŠÝ‚ °‚H`C£»=`+‘À†F)v z,ü¿öìØ †aàï?uvxÔîF) €B$0´Rœ °Bˆ†VгàcV‘ÀÐJq|,À !Z)΂X!DC+ÅYð±+„H`h¥8 >`… ­gÁǬ"€¡•â,øX€B$0´Rœ °Bˆ†VгàcV‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä„H 'D9!È ‘@NˆrB$"€œ ä¿•Ú endstream endobj 283 0 obj 85551 endobj 284 0 obj [/ICCBased 286 0 R] endobj 285 0 obj << /Length 287 0 R /Type /XObject /Subtype /Image /Width 2435 /Height 2406 /ColorSpace /DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÖ1 ðò'Ý’˜´Ç†+ À¿pË‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚i`Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á4°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0,€À‚X0,€°` À‚X0,€À‚X0 fÁ,€À‚X0,€À‚X0 fÁ,€À‚X0,€°` À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á,˜F À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°`L# `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0`íÖ1@°þ­±EpÀ‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚i`Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚`Á, À‚`Á,€³` À‚`Á, À‚X0 `Á, À‚`Á, À‚X0 `Á, À‚`Á,€³` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á4°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á°` `Á°` À‚Y0 `Á°` `Á,˜°` `Á°` `Á,˜°` `Á°` À‚Y0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°`Ì‚X0 €°`X0 `Á,€°`X0 €°`X0 `Á,€°`X0 €°`Ì‚X0 €°`X0 €°` ư@H endstream endobj 286 0 obj << /Length 288 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xÚ}’OHQÇ¿³%B¬e&RðN¶Wí`ŒÝõoʶ¬k¦²Î¾ÙÞÌn%Bˆ.AÖ1ºXÑI:†‚b]"è(‚—í73»îˆÚƒ7ï3¿ÿ¿ß{@](mšz€yÃÉþ(»;>Áê7P‡A+­Xf$‘v™lqdí}…䜛áãõÿ] ‚U€Æ¬ÇמöxÀáû¶iO:¬äÒb“¸M¤’1âWÄg³>žöq†[ ñ2ñMÅ'"()Y'æ±ld4ƒä—‰»2–’'&ßÀSg^™öÐ}8õ¹&›°€åwÀ¥Öš,Ô \V:k²Ý¤;©iÝR;;\‘Œu?ÊåÝV þ°ÿ¼\þûº\ÞC9¾u¥(J•IÒÀëÃ]ýÜàBS˜s_ QP5ûFz¼Úë׋Gõ%«t{3qW°D÷0vz ¼ü \}\ø$€Ôu¡ºmþÀÍ+˜…–ÍÙ¬C–;XØ9:Y„^g±BÞ,Ú\°ACioci]g®©Å·¸(ñL;òz±Úï9ÚAnŒŽÐIó ¨Üê­°4“I÷ÐÝ x#Ã{zwA¼¨j}ƒÎ…Ðþ¤Š¾Q¥óš=˜ò8Ðmèñá Ã(Äo{1±cÚÑd5¾Ué­ÊgÒ·t¶üÆlaȱi"ßÐ\.5æ±”šËÅâ^Å8tph0èk€!‰~D† TÒhd¡‘”»6‚ØÂì±–:>f¤ß&Ÿm×çŠäíxÝA4Ž…¶ƒLþ&ÿ–·ä%ù­ük±¥ªiÄ”¦¬?ûCqÌÕ¸m¥&/¾By#¤Õ‘%iþ 'ËW©¯:ÕXl©Errð'ñ=_—Ü—)Œi7Ò¬›©äê,úF|ÙNšٮͯ6×rm^™Ü ®ÍšUáHWü «Ãÿ5;¿?ÿͰh endstream endobj 287 0 obj 9411 endobj 288 0 obj 706 endobj 252 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/merge-perf.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 289 0 R /BBox [0 0 2441 2402] /Resources << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 290 0 R >>>> /Length 56 /Filter /FlateDecode >> stream xÚ+TT(TÐH-JN-()MÌQ(Ê ™˜*¡‘‰˜‘œ« ï™k¨à’Ô¯dR endstream endobj 289 0 obj << /CreationDate (D:20070406031023-07'00') /ModDate (D:20070406031023-07'00') /Producer (Mac OS X 10.4.9 Quartz PDFContext) >> endobj 290 0 obj << /Length 291 0 R /Type /XObject /Subtype /Image /Width 2441 /Height 2402 /ColorSpace 292 0 R /Interpolate true /SMask 293 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÝÝ‘â<Û.Ð/R b R Ÿ}F ¤@ Ä@ ¤àت—ª©®F¦nËòZ5'Ó ¶ldK­ Éÿ÷°TO`ÝD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$@4 MD“@Ñ$°Ú¿}Vx þdÃå”ö÷‚hf×4Íår9»Ýn³Ùüì`§ÿ¦¦_¥¤—­³<£üíSÍq+<T\K€µý½ š€u6ý—Ëeô=>ým·ÛÅWÚÛí¶ßï»÷·Ó‹Ó[ÖSžo€K,̼Ѥ;9ê 0úß ¢IXgÓ¿ÙlFßãñxÔO`ѶÛmd¥}<ïahGééíu—gôà #šd¡ý §xŠ&@Ó?åÄɦiôX´lžnw×ëuxß;m¤ÖòLq\baD“,´¿á´ ÒðM€¦ʉ“§ÓI?EËÖá‰öu¹\Æê~òƒÒÊ3Ñ p‰…M²Ðþ†Ó‚J ÀS4 šþ7#>!N?E»ßïa•v”ù‰#ÎU,­<ÓÝ‘–XÑ$ ío8-¨´mª-ªð¡°¸Îð¸eȆ¡=&fSíñ5ƒÒÊ3õÇ]M½]áy âZ *-ÀÚº^¢IXUÓŸ}®Ü¥³³®Úž^çCaqá Ð6E±išo7•Þ’ÝÔŸK°–\ž€»šz»Âó@ŵTZ€µu½D“°ª¦ÿÙ²úâãñè·—÷YW¯¥ õ¨£3ô÷‚ý+Õ‚îia÷íUµømwŒT˜ôÃô«¯J’Ž.µæûýþ}ké‡ }™{Àevo|Ýî^‡“Né{îuP鳞·­þ«V4 «júÿývøÄÉ÷Ri›÷ÞÏk<ð}`çƒTÈ!“¶¾:Š#iÙ$±ûNS³³Ï²ÒÆÿ=p¿ßWp΋½dÊ^q{³k¨þ¼ TžY>ÍtÍv¼ÖÒQ„ÝIÆ­–¥%›é4fïTmÒ‹‡ßOºEª½çóùWÒf®À½Ž¶²Ì£èr©ÔñŽ‘jÈŸ·ÓttÙûpö¸F(ˬ$“^†ñ_ßÞîþÝô‚¿{0ËŸç¢IXUÓÿï·'Nf3ˆŸcV£÷.—KÇѪì8O¿Ì¥ãQt)[—Ý=Ž•¿¤½§2Ôq΋½dFßøv»¾ÙìÀòËüiv"ï$STËr¢ÉÞ÷·W4“ôFþó(n·[Çñ½?÷Õ–-ÎÒ7›ñœ—|Éô–ÎCvÔqø–³32þ<í¥•'øÓì1þs$|ôÔcÒjYH49Êý­w4óyyóÞMi·¸’ÛÊòâÃ!d—ªîWŒtt=¦Ñ˜N–YIb.ðÎÕ&ưžñIÑ$¬ªéÿù‚ìXq—²²S&˜ÕOÈf(aÃzŽâÛ9ŸwÔc×t}³yÏyÉ—LoÓ=„±ß##K+Oا9ʰ|tråÑäˆ÷·oŸ‰üù(FI ™^l[¹”£hûp‡×Þ~³«UD¦áeV’°Ë0¦s5zwΟ3@­ã“¢IXUÓÿë5ý&NžÏç÷…ËzìýOã£õÖk;Š´‘o¿áÿa/ß.7ißlös^ø%ÓOvèu”(ðýbì²åÒʳ¸¿}¾]üvÍÑäèõ=ÒÉìvF™;6ÖtãŠm+tmWúð2¼Ö”k&]ï§÷–YI"/ÀÎÕÝ9εŽOê8Àªšþ_¯é7qò=•{ÿòüð~Âça´ív{>Ÿo·ÛÏ õñx\¯×ãñø!7L¿ê>É¥m¦Àˆ}¤Ï[K‡™>Ž÷ÃL?I?ï².ÜâÎyá—L?ÙAéQž!•†Ó/šœ±<óþí“®£tþ<üµúû¾:¨ÕF“Ù¨úg*ñº¥üªB釟‹ô‚é*ï"½*Ãç€uÞçÁÛV.ë(ºW’´Ç´ßŸ?}UÚÅøPŸ{l­Ç<ôb+Iäe8õÀ×çY±©ðé~…¹é¿vçü9Ô:>)š€U5ý_öa¬é=J{Ÿ2ùmó•¾z¶Ñ‡ñѧQd¿»ÝR_£‹Ùí8Ðýx<ê8çå_2=ˆ&KûÛç«iwmÃòݯÓuF“m÷·¯N~Û2˜_­©ûçÝ»ãÝ)½¬-}˜ëþVr[¹¬£øs¦dÇSÑe5ƒîYUÛÖ¾ªo‹èPÅ_†ã¶ømS&û-hŸ>š÷OεŽOŠ&`UMÿûËš¦ÉÎ'Ênóz½vlý‡ôÚR¶¯†Ñ^ÚÓÆFÑoPºm)×Þpê8çå_2=d£Àîsî>È^¿ý¢ÉËù·Ï~¿ïqÁ VM¶ÝßzÜRÚ¢áî›ú0söÛD¾-€è±Ææp…·•Ë:Š+|•.µθ[ë~rÊïPÍuŽÛâg§¸ìö¤Ãië,‹h4ýÙWf§¥d‡­Þ¶—!ý„ìÏ·û|tG{Æšø•ýÚÿ§Žs¾ˆK¦œ-÷Ûxiå û4{ŒÆ¿d#×î×È £ÉìýíÛ…(ÿÉFÃÝo)giu9´Y¾zQx[¹¬£±’´}“aÄ­Ç:*ÉŒ—á¸MUv’ø(_ø¨€h4ýÙWvœ8™ýšzÛ˜Rï~BöþC»¶L¡ËÌ…±&>¼k›R4d «Žs¾ˆK¦œ-?E“QÅÈ.ŠØñ2Y[4™_?dÕÓ¶[JÇIRãV†!5aDå·•Ë:Š+I6¿ë}PC¶VG%™è2·<ÇzŒ­‰&`=MÛ‹³ß«ÿ5Ãèý«û¾ Þ»Ÿnø¼Þ³Æý’ÿç“9d)ךÎùR.™B¶üMF£m1é ³Ährà<¯î·©ŽÓ0Ç­ Ùf¿zöå(Êo+—u#V’ì÷¸z¯ÝZÇúVG%™è2MÌûç¹hVÕô·½8ûÕúŸ#iÙ|HèF\pøovŠb—p¢ÞNÛ¼ƒkÕqΗrɲå§h2°Ù%»Œó¯-šœâþÖöȼøÊÐöœ»à^G[YÎQLMβµŠ;TÃOr@49Ë#h–2Ø"š€U5ý^ÿyâäûo?Dõë'dŸ‡OµËauYøk¢ÞNvµÃá†uœó]2%lù)š ,Fö&Ùåéf«Š&‡Lòúl–h¸ãWò:ÚÊ¢ŽbÄÏtHŒ>âÖêîP•MfŸ5YxŸ`Þ?ÏE“°ª¦ÿÃë?<“(û«ÏS`úõ²ßƸøX[yº¬®6Qo'û訫VsÎtÉ”°å§h2°Ù+wêéB‹‹&'º¿=[V§œ:.³’×ÑVu%/ï©CUà½ñ—ìWÆzÀÒ‰&@Óÿù-»Ý.;õ> <ÑÚhÙ‘ç¹ÎÆs²ñçìyþõdϰc,íœ/ë’)áTˆ&Ê1Ët¡ÅE“]ž\ÜO9KsÏ^Éëh+‹:Šú¢Éê;TEE“Ù»ÓÏoP(€5M€¦ÿó[ÚÞ{<5¬_?!»¯û-‘Ñäð¹ uœóe]2³où)š ,†hrÈW\†¦å,Í={%¯£­,ê(ê‹&«ïPM>[Vœþµ®õétJ÷11%`|R4 kkúÿ|W—±¬éf©¬$šÌnvøP•hr–8ï–Ÿ¢É.ÞI ³¸h2;>ÿç·YºÈ.-¾Ä•$‡MŠ&u¨J‹&³o“n\ûý^R ¬y|R4 «júÿ|WvZJù/#Ž\­$šœk³¥óÅ]2]d‡ˆGÙrÓ4=æ‘•Vž}š¢É%ßõE“u´•EE}ÑdõªÒ¢Égû'»$•é½—ËEL ¬g|R4 «jú»¼q»Ý~hë;¦ FÒ–²YÑäԟ˳% ¼ßï÷Üo‰ËÒʳ OS4)š|Š&kl}D“¢ÉQ®âì#>¿r8F™fPø`‹hVÕôwyãår8eòi$m9›MNý¹<Ë{ú^O\ʧ)šM>E“5¶>¢IÑäXWqjÿ|îäŸN§“?d€ºÇ'E“°ª¦¿ã{ÛUºG EE³œÃ¥lV.àTd§QL¦Ý-«<Õ_¼¢Éy?Ö•D“‹»yu+‰&kja‹&_.—ËÀÇ}n·[ë»¶ˆ&`UMÇ÷¶Mœì^Iûv³Ã D“e~ÜÉétzßòõz¾åóùÜc¶EiåYúÅ;iaD“ÞåD“õµ>¢ÉÅUÚ£ɗÇã‘zÔûý¾w:éÏ ÖÁÑ$¬ªéïþö÷‰“é'Sï=û®Ÿ¹3Qog¢å4ë8ç˽d>¸^¯výBÆÒʳ”Oó~¿÷›K¾ªh2;é~”'™¦ûÒû–»4L+‰&×Vu+‰&kêP-"šüuK­aj%¿šMieW ÖÁÑ$¬ªéïþö÷ˆár¹L½÷é7Ë9üê0‡Ç7uœóå^2d#­Ãá0|ËÙ¹Æ@¥•g)Ÿfï'i®*š,ðI¦õE“u´•EE}ÑdõªÅE“¿¼&T¦–÷ÏSZÖ¨r°E4 «jú»¿½išÞS&{ï=›kÌøñ‰z;Ù'ý ?Ì:Îùr/™o7ÞýÉ­d‡4—XžE|šÙåj»,ú:Êý ´ÁÑ$¬ªé/|ïÙÕ ÷û}eç0;À><ªãœ×zÉd'° Üæ¯ï|U‘J+Ï">ÍlèÖe:ùª¢Éì-e”9¹Ùóß%j©/𬣭,ê(ê‹&«ïPÕM¾´=ç}®f`Ò® hVÕô¾÷ìz}3.f5Ñ9Ì.§9ü0ë8çµ^2Ù©²WñÍ>2²Ë$¾˳ˆO3;'´Ë³ÛVMfo)ßλï~þ»,PY_4YG[YÔQÔMVß¡ª/š|¶|c”û'@iƒ-¢IXUÓ_þÞ³o¬c…·Ÿ²cì_=ͳÖs^ë%“íN%ËŽavŒK+OùŸföGŒWM¶½kà#GÛ¾Ñ1Ke(áá:ÚÊrŽ¢¾h²úU•ÑäÀ²[D“°ª¦¿ü½·=m§‚i?þÅøìZšK<ç_2ãžä!Ÿx™å)üÓÌ^³ç„®-šÌÞR¦¾;®NYe4YG[YÎQTMÖÝ¡šâæÖe|ݽw€°ŽhVÕô—¿÷ìd®¹–…œî¶}1¾ßt†Ûí–†¹Äs^ñ%ÓûI…YßèWZyJþ4ÇqìµE“mOLë=ìßvþ;ÎÉ­2𬣭,ç(ªŒ&ëîP Ü`ï¢ëõpD“°ª¦{oKÙ®wúÏý~ßn·³ŸÃÝn7ü0G6`Zî9¯ø’ñÉ_mS¿V-­<%šÙ«µã”½çú¢É¶[J:#žÿîÓÌ«Œ&«i+ 9Š*£Éº;T7˜½«œÏçÞã(í]ö{k½ïœ%¶ˆ&`UMÿ"öÞ6ãfø`ZÓ4ÿævÍ~Û‚¡×ÜÉ?ã¡ûýþ9”\è9¯û’ÉŽwO¸þÉ.Ó×cAàÒÊSæ§Ùv¡uˆ^a4ÙvKé1‘¶íüw¿5ÕMÖÑVrµF“w¨nðx<ŽØlý»¿ \6»þÀÚ–°ê#šMÿRöÞ6£°÷’§?ÇÐ ‰&Ÿ-#c?£®×ëÏa®ûýž~’~¾ÝnGï›rÎë¾dÚ–×¾k÷Å-K.ÏÔŸfAã¶£û*À]a4ùlŸ®5Jíú*A¨5𬦭,á(j&+îP Ü`[hÛ/±ýu‹ëP¶-õŸ~î`ÑD“ é_ÊÞÛž,öoPºûØÑívËn—p›¦é2NÝ7+äœWÉ´§Ÿÿ9˜™^ðáíu”gê¿}RÁ:ÖäT?\ž_-{»ÎhòÃÄðtbÿœsš^ð!Rùj¬¾âh²Ž¶²„£¨8š¬µC5Ýié¶µ5[Š÷´×j®@MD“ é_ÐÞ? kÿüjúårù5Äýx<ÒOÎçóð‡0œÃ¶/É«mPkqç|qáo‹ñy”x¿ß¿ŸÞ×¹Í.š:d>`™å û¸SùÓQ¤cùYÔ¦i^G÷ù;ßN]g4™¤3ùá4¦“üúÞk×çóÿíóà*Ž&«i+g?ŠŠ£ÉZ;TÃ7øáËéx-[ñ:©¯õjûº·8énv<ßÏí«¹I?ÿ|ngoLŠMÀ‚šþeíýÃ3’b:-1çð~¿·­|ØEzïk¼«Žs¾¸Îp’Œ~’.ZZy ÿÛ§Çr«&Ÿí+²öÖãi•uG“Õ´•óEÝÑd•ª˜Ä¶ã!—ÓÜ,e|R4 «jú·÷!GK‰&Ÿÿûòüç9h&Kþ[.¬Žs¾¸Îp¿ÂŒ8J<ÊÐeiå)öãþ²µE“ÏQÓɹäsÑd5måŒGQ}4Y_‡j” öëzÅD“rI îñIÑ$¬ªé_âÞ?<Ï®·ŽÏî >‡ŸŸ­ö>Dÿk™¯:Îùâ:ýË3J8âüÄÒÊSàÇÝûèVM>ÿZÙuêú5D“Õ´•sÅ¢ÉÊ:T£l°÷#¿mgÈÚ…7¦ÅŽÆˆ&`AMÿr÷~½^‡ü¤-Çîî™å¦â½ä÷k¸,~·Û¥ò§Sño¦äçÒ¦-,îœ/®3<ð³î=JœÞ8úY-­<ã~ܯ¥ûàÀ£M>ÿ·rõ\µk%ÑdMmeüQ¬$𬩒ŒµÁÔ¡êqkÊžÕÞs0}ñ,ÛǨl|R4 «jú—¾÷ÛíÖcmÀ×Úý~_Ö9^ÚQ&*ŸóÅu†G9Ã_g¦¿ž.:‘ÒÊ3ÖÇÊùúUª–Ý«tºˆ†hrÞÚµªh²¦¶2ò(VMÖQIF_Ç૸¶m;MÓ¤MõÈ(ËùÀRFcD“@¼Ûív>Ÿ‡Ãn·{MÚn·¯ †—Ëe%ã<ÙgH¥3àœ/Åk<3À÷ÓûoÂlzAØdŠÒÊ3…ëõz:Þ0Uæý~Ÿj»š<]íJ'ÿsíj›Î ïÛZ§7Æý~ok^'!¨î_–øybß—}ÝëÒoÓkŠúÀèD“µÊ>(ð|>;3ÌB4 P«ì²l¾‡À\D“«êé9-ÌE4 P¥óùüÞ+;Î sMÔç~¿g{e·ÛÍÉ`.¢I€{hÇã±÷C!ÛrÉÝnçÜ0#Ñ$@±=´ív{:ºg”MӤ׷uÉL™`^¢I€ò{h›Íæp8œN§Ûíö+©lš&ýð|>ï÷ûý±ãñèÄ0/Ñ$@õ=´ívÛ4 À¼D“u÷Ðä’B4 Pqíp8È%(„h ÊÚf³¹ÝnN&åM”æz½‡ívÛ¯ëµßïÓœFJ#š(VÓ4·Ûí|>ŸN§ÝÿdgG¦Ÿï÷ûôÓ$(™h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢IVèv»Ïçãñ¸Ûí¶Ûí¯êºÙlÒχCzÍý~)RÓ4—ËåU¤T€÷ò¤_¥¤—ùø€…M2£ÝnVUn·Ûñx|"»Ho|<Ól¿ßw/Lzqz‹Ê,Žh’½…£ïâv»‡QªqÚθeÚÚ{8ÛQzãti)ÀD“Ì¥iš©«Ê¯•QGq¹\F)Ûõz^˜´ X Ñ$s9NSW•‰êóápX°ËåRZT 05Ñ$³¸ßïUeº*=$e¾¤¹“Àâˆ&‰×–KFF“›Íf¿ßŸN§Ûí–Êóó]é'—ËåÏ'Tö›®øx<>?Aò|>§¼—'•öÃ=w(Ÿh’HMÓd×q ‹&7›Íñxü•E~p>ŸÇ w»]vSÛíöÏR¥¤—eß¾ßïÕ. p¢I&õx<^“þN§S[*M¦½ÿšØýÚÁo—uM¾¶éœýŽ Œh’¢ªÖeèJþÓ4M[:™~Õ};Ùp¶Ç„Çìâ®iãjP2Ñ$EU­q˰Ùl®×ë(›j{>f÷'N¶M™ü*Ü|IoÉnªûBµñD“UµJ>¢ì\Åîk±fbížlþ’}dç· ÌDMRTÕ*ùˆ.—ËeTÇ=Þ¶‰“*P,Ñ$EU­’èñxô.sv5ד³Ó0>U`:¢IŠª{µ–9»þêÀç`¦·¿o3íHÕÊ$š¤¨ºWk™w»Ýû›¦R˜ìš®Ý˜&š¤¨ºWk™ßßµÝn‡—'mÄå,…h’¢ê^ɾßïïÞl6ýÞ¸ßï‡)mä}ËiwjP Ñ$EÕ½’ œ}¶c—T§{(ä°˜ˆh’¢ê^É>ïN?üóÙq”hò|>O´e€Ñ‰&)ªî•\àÍfó^àËåòç³Ñäív^¤´Ñ$°¢IŠª{Å–6»(kÒ4ÍŸïÝív‘Ñd—5fâ‰&)ªî[Úl¼¸ßï{¿W4 ¬h’¢ê^™Em›2™~ÞåíÙhòñx /XÓ4¢I`)D“U÷ ,gÓ4Ù§L¦–p¤®8`)D“U÷ ,ç~¿ÏµûЬ¢I€§h’Âê^i…<ŸÏÙrv|ÊdÀ‘ºâ€¥MRTÝ+ª„·Û­­J7MSÈ‘ÖzÅý?€9½Ñ$¢Éx÷û½­>_¯×rŽT4 0"£÷ šD4YN.y:Š:ÒOã¸w! ’Ñ{M"šŒÔ4Ív»Íïp8”v¤uÏšMÁŒÞƒhÑd ¹dúùW˜MŽMþÀ”D“ šD4YG.)šM…M‚hÑd¹d²Ûí&:ÒTª÷-§ÝUMºX€I‰&!Œh’¢êÞ\…™.—|¶D“÷û}x±o·›h` Ñ$„MRTÝ›¥$“æ’Ï–hòv» /¹h`8Ñ$„MRTÝ‹/Æý~o«·£ä’ÉñxŒŒ&Óîªé ˆ&€¢I#š¤¨º\†€\29NïÛ¿^¯Ã·|>Ÿß·œvWMO@4 MBÑ$EÕ½ÈÄä’Éõz(@œ.ô,¤' šˆ&!Œh’¢ê^ØÞ/—K[u=#æ’Ï– 4íeø–÷ûýû–Óîªé ˆ&€¢I#š¤¨º³ëì:¨#&†]v·Û ßìf³©õrMaD“F4IQu/`¿‡Ã¡­¢ljvºÛíF?ئi&J<Ëé ˆ&€¢I#š¤¨º7雦ÉF„/—Ëeº]ÇÑ ™}„åtéê,=Ñ$@4 aD“U÷¦ÛÝãñØn·mUt`Jø§lŒ8pñØìôÏ©$¸' šˆ&!Œh’¢êÞDûºßïêgúí\ÇÛ4M¿­eWs­éZMaD“F4IQuoŠ].—¶š¹Ýn{‡ƒßÊNr콊ìét}f=Ñ$@4 aD“U÷FßK6Â{Ùíva¹dr»ÝÆš8Ù6e2í¢²ž€h š„0¢IŠª{#n¿išìDÅ'n6›÷’ì÷ûo·“Þò¾´ñúz¢I €hˆ&)ªî¸ýívÛV!Ïçó,‡|½^‡ç¤mykÚx}=Ñ$@4 aD“U÷êö¼Þn·k[]öñx|~ozÁ‡·WÙMD“F4ɲªViuûÛ‚=Ùï÷—Ëå×##ÓÏçsv×þŒ5ÚMD“F4ɲªViu»GÁ.—˸»®l)ן=Ñ$@4 aD“,«j•V·ûUòÓÉ´©Š{¢I €hˆ&YVÕ*­n÷®ä£¤“UΗüÙMD“F4ɲªViu{H%<»Ý®ßîÒë{¾ä{O@4 MBÑ$˪Z¥Õíá•üv»í÷ûî;J/NoYIO@4 MBÑ$” išËår<w»Ýf³ùyù¤ÿ¦¦_¥¤—­ª' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' šˆ&!Œh(¶' š„¾­ûôÿ*"š„0¢I Øž€hú¶î¢I€/ˆ&!Œh(¶' š„¾­»hà ¢I#šŠí ˆ&¡oë.šø‚hˆ&b{¢IèÛº‹&¾ š„0¢I Øž€hú¶î¢I€/ˆ&!Œh(¶' š„¾­»hà ¢I#šŠí ˆ&¡oë.šø‚hˆ&b{¢IèÛº‹&¾ š„0¢I Øž€hú¶î¢I€/ˆ&!Œh(¶' š„¾­»hà ¢I#šŠí ˆ&¡oë.šø‚hˆ&b{¢IèÛº‹&¾ š„0¢I Øž€hú¶î¢I€/ˆ&!Œh(¶' š„¾­»hà ¢I#šŠí ˆ&¡oë.šø‚hˆ&b{¢IèÛº‹&¾ š„0¢I Øž€hú¶î¢I€/ˆ&!Œh(¶' š„¾­»hà ¢I#šŠí ˆ&¡oë.šø‚hˆ&b{¢IèÛºGý¨‚hˆ&b{¢IèÛºÇþX8Ñ$„MÅöD“зuŸãÀb‰&!Œh(¶' š„¾­û|ÿH4 aD“@±=Ñ$ômÝçþ°(¢I#šŠí ˆ&¡oë^Æ?€…MBÑ$PlO@4 }[÷Âþ”M4 aD“@±=Ñ$ômÝ‹üP*Ñ$„MÅöD“зu/ø@yD“F4 ÛMBßÖ½ø%MBÑ$PlO@4 }[÷…ü(ƒhˆ&b{¢IèÛºOþï¿ÿþPÕMBÑ$PlO@4 }[÷ˆhòE@ T@4 aD“@±=Ñ$ôó_8%°h¢I#šŠí ˆ&¡Ÿÿæ# –H4 aD“@±=Ñ$ôóßÜ”À²ˆ&!Œh(¶' š„~þ+ƒ€X Ñ$„MÅöD“ÐÏÿŸ½;ºm\Ù(zCP NA1(§àôÙý甂SP NA)(…b 4úY”,’ÅÃSUkÁà¾g‘¼åâøxöPÊD ò“&!Œ4 ¤¤I˜çW>%™4 a¤I í$ MÂ<©qÖYJ 'iÂH“@ÚI@š„ybÒäìs ”@6Ò$„‘&´“€4 µ(€ªI“FšÒNÒ$TG j$MBiH; H“P)¨‹4 a¤I í$ MBÕJ Ò$„‘&´“€4 (€ü¤I#Mi'iš!P™I“FšÒNÒ$4F r’&!Œ4 ¤¤Ih’@ d#MBiH; H“Ð0ÈCš„0Ò$v&¡y%4 a¤I í$ MBÈ$ðûï×V× PÛ’&!Œ4 ¤¤IX øýíkËÙ&PRiI“@ÚI@š„•g€ß£_Û^•@ Ä“&!Œ4 ¤¤IXsøýàkóË(€HÒ$„‘&´“€4 «ýöÿýãW†ë(€Ò$„‘&´“€4 ëüêÿýäW’ (€µI“FšÒNÒ$¬ð{ÿ÷¤¯Ÿ‡Ãóxøæá%=\L¶I@š„æ ”a«'PÀÒ$„‘&ÙÐËËKðV¹\.·=ôIà ‡—·z19'i:!P†­ž@ £¤I#M²•ëõ¼U>>>–oæá í]LÚI@š„~LíkêäÂ,(x´Bš„0Ò$[9‘[åt:•ÚÏáZº˜Ì“€4 ½(ƒP €/Ò$„‘&ÙÄççgäV)òˆb©ÇS]LòI@š„> ”Á (P€4 a¤IâÝë’+m•ËåòøCßßßÏçó¿/þñt:‡/œ÷Q©.&ÿ$ MBÏÊà(è™4 a¤I"]¯×Ñ÷q]u«ì÷ûÑs½¼¼|~~>~íð ÷¾üp8Ô~1ù'i(ƒP  OÒ$„‘&YÕårùzèïx<Þ«r«n•áì£'z}}}þ Ã7äÛãu]L“€4 |(ƒP  7Ò$„‘&IµµŠ_ÆhñŒáèû©¯÷bª˜¤Ià_eð ”ôCš„0Ò$©¶VÙk¸÷”âõzz¨á%£‡úñ]Xs^L-“€4 Ü(ƒP  Ò$„‘&IµµÊ^Ãè{ŸžN§yGý”Ìçß‹5ÕÅÔ2 H“À=ÁuR ,Ö( %iÂH“¤ÚZ×0ûh÷žU¬ñbj™¤Ià12rJ&MBi’T[«àŒ¾êÂç GŸ|NT×ÅT4 H“À3ÊÈ(h’4 a¤IRí½‚Ç}ËÓ%Ç^~{ÌáDu]LE“€4 52ÕÅÔ5 H“Àreä ”TJš„0Ò$©ö^©ƒ6»"5ðýý}ê‘S]L]“€4 1µ¬©“E–Q  "Ò$„‘&Iµ÷J|´žÏçåGR$Mnu1uMÒ$P@¹É2 ”TAš„0Ò$©ö^©ƒï÷ûÈ8œ®–‹©k&âÊM–Q  9iÂH“¤Ú{¥.M¶1 H“ÀJÊM–Q  -iÂH“¤Ú{¥>Z/—Ëò#_¯×"ir«‹©k&U ”›,£@ @BÒ$„‘&Iµ÷šŸ—¹TšÜêbꚤI ¿©YM,µ’%I“Fš$ÕÞ+uð···È8œ®–‹©k&Z”[­¤@ @Ò$„‘&Iµ÷Jüx<Þüããcù‘ßßßo<œ®–‹©k&º”[­d±F @¯¤I#M’jï•:øÇÇÇJÍnFgLu1uMÒ$P#r«•(˜Mš„0Ò$©ö^©ƒ~~Þüõõuù‘‡Ã푇ÓÕr1uMÒ$P/r«•(˜Aš„0Ò$©öÞªÇßï÷Ë»Ûíf\vª‹©h&Ú ”[­¤@ À$Ò$„‘&Iµ÷ ¿ß?þõzS]LE“€4 ´A Üj%Jž$MBi’T{¯àñßÞÞŠãè§F'ªëb*š¤I %åV+)Pð#iÂH“¤Ú{?Zî~Âãðòy…1ÕÅT4 H“@{‚ë¤@)Pð iÂH“¤Ú{§¸^¯óŽ6úªÏ_sª‹©e&V ”›¬¤@ À=Ò$„‘&Iµ÷Êžbô¹ÂÓé4ïhÇãqÉ“©.¦–I@šÚ&Pnµ˜%ßH“Fš$ÕÞ+{Šóù\êYÅ{O)§¨ñbj™¤I åV‹)Pð—4 a¤IRí½âgÙív·g9S3¼äö8ÃÁ뽘*&iè‡@¹Õb ”ü‘&!4Iª½Wü,£'šôÞ§£ïÅ:^ïÅT1 H“@oÊ­S èœ4 a¤IRí½5N´ßïGÏ5üç—Ëåñk‡oxðòÚ/&ÿ$ M}(·ZL [Ò$„‘&©kk͸’Ëåòà€‡Ãát:}û”ÆáßßßGß7õ¯Kbþ‹É? H“@Ï‚¥Å(z&MBi’º¶Ö¼‹9Ne/cÉ»§¦º˜ä“€4 tnjP(Ë®§@ ÐiÂH“Ôµµf_OÁ 8já⤺˜Ì“€4 ðG Üz=J€H“F𤮭µä’ŠÁR(¦º˜´“€4 ð—@¹íz ”m“&!Œ4I][káU].—ý~?ïÔà Ë~¤cª‹É9 H“ߔۮ§@ Ð*iÂH“ÔµµŠ\Ûù|>ÏŸtøæá%+-Tª‹É6 H“£Êm×S h4 a¤Iºu½^O§ÓÛÛÛ~¿ßívÿîØá‡ÿpø¯†o¾­·‹É3 H“”Û®g™@iLÈAš„0Ò$v&~$Pn»ž%@¤I#Mi'iàIÁuR (#MBiH; H““”®§@ P5iÂH“@ÚI@š˜A Üp=J€JI“FšÒNÒ$Àlå†ë)PTGš„0Ò$v&(7\O "Ò$„‘&´“€4 P„@¹áz ”U&!Œ4 ¤¤I€‚Ê ×³d£`Ò$„‘&´“€4 Pœ@¹áz ”iI“FšÒNÒ$À¦¦4u²ø’ ”ÙH“FšÒNÒ$ÀzÊÍ—T ÈCš„0Ò$v&Ö&Pn¾¤%@Ò$„‘&´“€4 C Ü|IJ€mI“FšÒNÒ$@$ró%(¶"MBiH; H“ñÊÍ—T ˆ'MBiH; H“[(3¬ª@ Fš„0Ò$v&¶%PfXU €4 a¤I í$ Md\'J ž4 a¤I í$ Mä!Pn¾ª%Àz¤I#Mi'i róU(Ö MBiH; H“9 ”›¯ª@ P–4 a¤I í$ Md&Pn¾ª%@)Ò$„‘&´“€4 Ÿ@¹ùª ”ËI“FšÒNÒ$@-ÊÍWU XBš„0Ò$v&ê"Pn¾ª%À<Ò$„‘&´“€4 P©M\iaËJS<Ð iÂH“@ÚI@š¨”@™daJ€'I“FšÒNÒ$@ÕÊ$ +PüHš„0Ò$v& P&YXàiÂH“@ÚI@šh†@™daJ€QÒ$„‘&´“€4 Ð2É ”ßH“FšÒNÒ$@“Ê$ +Pü%MBiH; H“ (“,¬@ ðGš„@Ò$v&š\'ÊÕ%@¤I#Mi'i e†…(€žI“FšÒNÒ$@WÊ +P}’&!Œ4 ¤¤I€ ”V z#MBiH; H“Ý(3,¬@ ôCš„0Ò$v&:'PfXXè4 a¤I í$ MðG Ì±°%Ð6iÂH“@ÚI@šà/2à”@«¤I#Mi'i€M-hêäªË+P-‘&!Œ4 ¤¤In ”©–W Ú MBiH; H“Ü#P¦Z^¨4 a¤I í$ Mð˜@™jyJ ^Ò$„‘&´“€4 À3ÊTË+P5’&!Œ4 ¤¤Iž'P¦Z^¨‹4 a¤I í$ M0•@™jyJ Ò$„‘&´“€4 À<eªå(€ü¤I#Mi'i€%‚ë¤@)Põ’&!Œ4 ¤¤I–(ó,¯@ ¤%MBiH; H“”"PæYÞbÒ@9Ò$„‘&´“€4 @Yežå(€T¤I#Mi'i€5”y–W ’&!Œ4 ¤¤IÖ#PæY^Øœ4 a¤I í$ M°62Ïò ”À†¤I#Mi'i€ežå(€MH“FšÒNÒ$a¦æ3urí(€HÒ$„‘&´“€4 @02Û ”@ iÂH“@ÚI@š`e¶.Ö(î&!Œ4 ¤¤I6$Pf[aX4 a¤I í$ M°92Û ”À¤I#Mi'i€$Êl+,PeI“FšÒNÒ$©”ÙVX J‘&!Œ4 ¤¤I(³­°@ ,'MBiH; H“¤\'JX•4 a¤I í$ Mœ@™j…J`6iÂH“@ÚI@š  e¶E(€©¤I#Mi'i€Š”ÙY ž'MBiH; H“TG Ì¶È%ð iÂH“@ÚI@š Re¶E(€Ç¤I#Mi'i€ª ”ÙY î‘&!Œ4 ¤¤I Pf[d¸%MBiH; H“´aj8S'cÖY þ’&!Œ4 ¤¤IZ"Pæ\gø#MB iH; H“´G Ì¹Î%tNš„0Ò$v&h•@™sJè–4 a¤I í$ MÐ62ç:— ”þ€ªH“FšÒNÒ$=(s®³@ ]‘&!Œ4 ¤¤Iú!Pæ\g:!MBiH; H“ôF Ì¹Î%4Oš„0Ò$v&èSp(J@š„0Ò$v&è™@™pJh•4 a¤I í$ M€@™pJh4 a¤I í$ MÀ2á: ”ÐiÂH“@ÚI@š€ ” ×¹d£¶#MBiH; H“pK L¸Î%ÔNš„0Ò$v&à2á: ”P/iÂH“@ÚI@š€ÇÊ„ë,P@¤I#Mi'i~45™©“aK-P@E¤I#Mi'iž$P¦]jª MBiH; H“0‰@™v©JHNš„0Ò$v&`2íR ”–4 a¤I í$ MÀleæÕ( iÂH“@ÚI@š€…ÊÌ«-P@Ò$„‘&´“€4 E”™W[ € ¤I#Mi'i (3¯¶@ Û’&!Œ4 ¤¤I(.¸N ”%TAš„0Ò$v&`%eÚÕ(`Ò$„‘&´“€4 «(Ó®¶@ Á¤I#Mi'i”iW[ €0Ò$„‘&´“€4 aÊ´«-P@iÂH“@ÚI@š€`eÚÕ(`UÒ$„‘&´“€4 ›(Ó®v±@éO4ø_Ò$„‘&´“€4 (Ó®¶@ ÅI“FšÒNÒ$lkj/S'ƒ\ €R¤I#Mi'i2(“/¸@ ËI“FšÒNÒ$ä!P&_p–&!Œ4 ¤¤IÈF L¾à%Ì#MBiH; H““@™|ÁJ˜Jš„0Ò$v& 32ù‚ ”ðÏçãñx8öûýíŽÝÿ¿á>>>.—KÀõ\¯×Óéôöö6œw·Ûý{1Ã?ÿáð_ ß0|[W“€4 4F  X·™õ0ìD2%@JÒ$„‘&éÊù|~}}º_^^ÞßßWÊ‚Ã%‡ç/føæá%LÒ$Ð$rÕ[’&ƒÏ+Sä!MBi’NœÏçoO#Îp< ÊËå2úÌæ3†Æ<ιí$ M (WZ«‚i2òdJ€mI“Fš¤y×ëuÒc‰?úøøX~UÃA’\IæI@šš'P_¥•ÒdäõÈ”ñ¤I#MÒ¶ÏÏÏ5¶ôÛÛÛ’«:N¥®d8TÓ€4 tB ,¸>i2úÚdJ€õI“Fš¤a»ä~¿?çóùÛ;£¯úøøx{{{ü°¯¯¯ó®ªÈó’Í?;)M½™š«ºª“™ÓdôuÊ”ë&!Œ4I«tÉãñøä5žÏç9gêU ç}\Kßß߇“~»†Óéôø=iÛûÜIiè“@YdY¶J“Ñ×,S”#MBi’&]¯×Ñg_^^>??§íÁsŽß2âî…Îg.lø†áÛF_~8šœ¤I Oeç?PO Oš„0Ò$M:£ùïz½Î;à½g0÷ûýó9ŸÏËßvøæ"‘´ŠI@šz&Pú±Ê”a¤I#MÒžëõºÆ»žÞ ‹Ï?†9úÈäŒGßÜuR$­e&J?\™ €4 a¤IÚs:n7áÛÛÛò#>±øä‘ï•ÍrÞk¯3Þ«6ù$ M|(ýˆeJ€UI“Fš¤=£Év£:ùä㊣Yót:Í»’Ñw¬ôưULÒ$À¿J?h™`%Ò$„‘&iÏn·[iÞ{\qö½¶É•T4 H“·J?q™ 8iÂH“t²«·=øè»¹.|Èqô1ÌáD-MÒ$À=ÁuR ¬ñç.S>>¤Ii m% wšL 'M‚4 K|||ÜîÃÃá0õ8çóùñ‡Q>óìäª7E«wœ4 JUò·ÊÙÕ~“)€"¤I&a¶ëõ:º'}@ä¿N§ÓƒíýãaGãæzÿ¦ÃéK“¶4@ñrú_¸K@Õ»®®L ¤"MBi’öÇÛMx:–óz½Þ{|òÇ7‰}áóUùÀù|–&–6P–ê›4³ýdJàyÒ$„‘&iÌ胄»Ý®ÈÁïÕÉÇN޾ê|>/¿i€­d ”eŸ¾¤½M(SI“Fš¤1£o¾z<‹üÞ[ž¿¿?xÕÛÛ[dšN×Ì$ M$—$P®ñÞ°4¼eJà–4 a¤IóúúºRü2ڇ׌¾ÁìÇÇÇò‹y_¯Ãf˜¤I€ü¦&žâur½O®¤“=YM©Ö$MBi’Ƽ¼¼L}ÃÕIFTNúà%+Äõ¢g’I@š¨ÅVr¥­¤Û)S@·¤I#MÒÖ.xü{ïéúà%ŸŸŸ·ßÿúúºüb‡Ã푇Ó53 H“u‰”Å?³ûS¦€>I“Fš¤‡-½ù)n¿¿ß/¿’Ýn×êí&MÔ+ ô3¢ÙLéÿsI“Fš¤‡-}¹\Ö>Åã—ì÷ûâ÷Åèó›EŠgžI@š¨Wò@éÄì½*S@“¤I#MÒÖ.øñ‹£Ap·Û=~ÕÛÛ[ñ«ýËáD-MÒ$@í2J?–ïX™š!MBi’ÆŒ>ŸX䃿ŒÁŸU}Õ«^¾j„Í0 H“mH(ýh(¸oeJ¨š4 a¤I3ú|âàz½9þh<óîµÙW5úðfK÷š4 ОœÒÏ…5v¯L Õ‘&!Œ4IcFŸO,õàäår=øççç¯mš§ÓiÞ•ÇUM2 H“íQ'émË”PiÂH“ô³«gwÀ¿‡Ãía_^^žyíù|.õàä½G&‡S46 H“­Ê(ýDˆÙÌ2%¤%MBi’öŒ>Q¸¼NŽ>ö8阻Ýîöå‡ÃaꕌÒáàíMÒ$@Û<;IŸ[Z¦€„¤I#MÒž{~y{{›qÀÑ8Øï÷ϧțÍÞ+¤ÃÁÛ›¤I€H“ô¹±k-•Ð(iÂH“4é^üû€á“:^¯×Ï`.—ˤ Ûï÷÷燾áÁË›œ¤I€~lž&J6ÙÞ2%$!MBi’V=NŠW¢ñr¹ ÿÉûûû½'%ÿúüüœzUÃÁp8ãíõT7 H“°G)¡–ÛJ¦Ø–4 a¤É%kUã) ¢I@š€‚4J¨â¶’)6!MBirÉBUz¨e&` ¥„ºn.™ €4 a¤ÉÙ Uû‰ ÿ$ MÀÚ4J¨èæ’)Ö#MBiröBÕ~"È? H“Æ£”PÝý%S$MBirö*5p.H> H“° 꺿R”J€ÊI“Fšœ·J-ÒNÒ$lN¦„ºî/™`iÂH“óV©¥ÓAÚI@š€<4J¨î“)ž'MBirÞ*µt:H; H““L ÕÝb>žà1iÂH“óV©¥ÓAÚI@š€ä4J¨ñ.“)nI“Fšœ·J-ÒNÒ$TD¦„ï2™à‹4 a¤Éy«ÔÒé í$ M@ªn”2%=ßh2%Ð3iÂH“óV©¥ÓAÚI@š€Úy”*½ÑdJ 7Ò$„‘&ç­RK§ƒ´“€4 Íð(%T}»É”@ó¤I#MÎ^¥ÎÉ'išäQJ¨úv“)&I“Fšœ½PµŸòOÒ$´Í£”Pû'SÍ&!Œ49{¡j?䟤Iè‡G)¡ö;.E©˜Kš„0Òä’…ªô,PË$ M@‡žRš„L¤I#Mi'iHB¦„VïÄf3%0‘4 a¤I í$ MÙTÝ(eJÜŒeJ`"iÂH“@ÚI@š2ó(%4|3VŸ)‰¤I#Mi'i¨‚G)¡íû±ÊL L$MBiH; H“@uÒ$Äý”&ç®Ò¤—,'i ?RB†»Fš„…¤I#MÎ[¥©/Yõ\Ðê$ MTd¥Â"Sâf‘&!€4 a¤ÉÙ 5éû×;4< H“•Ò(!ø‘&a!iÂH“³jÒ÷/ùqØ¥t; H“µó(%„ÝÒ$,!MBirÉB=ÿýK~v)ÝNÒ$@K4JXï^&a!iÂH“KÖêùožý³°Eéy&šäQJ²‘&!Œ4¹p­žüæÙ?[”ž'i y%H“Fš,²\?~ç¼ýIç“€4 ÐR°!iÂH“¥–ëñwÎ[û“Î'i O%Á¤I#M–]±{ß6cÙmNLÒ$2%¤I#Mæ\4;“€4 À¿4JÖ#MBi2áºÙ– Mð€L @YÒ$„‘&³-= ¤Iž£QP„4 a¤ÉT«gC¿“€4 ÀódJf“&!Œ4™d mE¸¤If¨ºQÊ”›&!Œ4¹ùJÚ„po&XÈ£” H“´G£HHš„0Òä’… Xa[”ž'i€¶É”IH“Fš\²P Wø™o³Eéy&è„F °-iÂH“³jÉ ùfh~&èL Oš„0Òäì…Z²¼ÅOMNÒ$=Ó(ÂH“Fšœ½J+½Pš„?Ò$ü/™`UÒ$„‘&ç­Ò’å]é\ÐÞ$ MÀ7U7J™HKš„0Òä¼UZ²¼ë›¤IxÀ£”¥H“Fšœ·J«¾PšižçQJ€…¤I#MÎ[¥Ùk»Þé ½I@š€©‘]J·“€4 ©x”h•4 a¤ÉåkõäzÎ>‹]J·“€4 ii”@K¤I#MƬՒSØ¥t; H“ŸG)H“FšŒY®%Ç·Kév& .%P©ÿcïîŽSǺ5ŒžHˆH|ù]’‚R b RP }Võ®êrmË6èçõ´4Æ]{ƒ——Î7«ž#!MBŒ4X±)G¶EÙò$ MÀï%S¿ˆ4 1Òd`ŦÙeË“€4 + QõI“#M.½hi‹²åI@š€•‘)_¥“B†4 Áÿi“&—ZºéG³?Ùø$ MÀZi”Ïp'ÄH“ü_7irþœë¯`²ñI@š€-)yÆ,$I“#Me'i6åW7Êy£¡oÀ„0ib¤I ì$ MÀfmùVʲ_| +&MBŒ4 ”¤I`k·RüÊKØib¤I ì$ Mï­þVÊ:ßt [#MBŒ4 ”¤I`Ð*o¥üñ/¸„-“&!FšÊNÒ$ð­uÜJùƒ_m ü#MB4 ”¤Iày¿ýVJu~4 1Ò$Pv&€q6˜)ýÑa ib¤I ì$ Mýoê$¬Œ4 1›M“« ¦*0+›¤I`F[h”þÊ0Ž4 1[N“+ ynPe}“€4 ,aÝ·RúûÂÒ$Äl³×ÉÁZgx‰4 1Òä¼ Rðƒt»Ý^½MrÞ?Áõz~eÍþ\Ùj“€4 ¬Ï¼÷N~v4ë /‘&!FšüÁe%Dß÷§Óégÿ]×͵Ú¡V< H“À*ÍU'uI˜‹4 1jÚ/Ž(™t¿ßG?Áu®?Ç,÷K®þÞIiX·éuR—„I“£©Y"Q2à~¿»ÔÇãñr¹Ün·¿¾É±ý¤ý¼ýëÄ?J;ì×ß ùçÓÿúè®ëÞôGëûÞIiX½)uR—„yI“£¬•Z(Qr9_wÉÃáð佇}ßw]·ÛíÆFû ÁØï÷í ¿ý>»åóx<®r&€uW'uI˜4 1úšÛ‚/îUÜívÝ¥¸œöAƒçp:ž?Èg_”û-’“€4 ¬Þ«uR—„%H“#´ÕY=»q9ŸÝlx:ú¾Æà-“#nx|¸k;øú&iØ‚çë¤. ‘&!FšüÁÅ´ý2Îçóàú_.—äi|vËäˆ6ÚÞ2x¨o ûë&i؈gê¤. Ë‘&!FšdÝ> ‚oooá3|k×uãŽÖÎâƒaÅ$ MÛñ¿Q¬ÌBš„i’u›ëª ]k£öÙ“+›¤I`StIø)Ò$ÄH“¬X×uwãn·K~¿äƒ7oN¼Éqð6ÌöAkš¤I`ktIøÒ$ÄH“¬Ø~¿/z½^§³½½Âƒj¤I`ƒtIÈ“&!Fšd­oT<?r2ƒÏ•xóæà3]ê\h&€mÒ%!Lš„i’µ*õ¼Óg²ßï§vð¶Ð5MÒ$°Yº$$I“#M²½½Ûí~äLî÷ûÇ“9ÓÜòñÈíãV3 H“À–é’#MBŒ4É* ~ãår©s2³|)ä_aYj&€Ó%!Cš„i’U:ŸÏun' ˆ³¤ÉËå²Ð‘‹LÒ$ MBŒ4É*•úÆÁ49Ë·^¶ƒH“I“#M²>}ß?óÝŽíe×ëõ|>‡)s·ÛµŸŸN§Ëå2ñvËvœdšl·šI@š¤Iˆ‘&YŸoï%ìºîx<¾´‡÷û}{׸ó‘&GOÒ$ MBŒ4Éú ~ãõzýçß(¹ÛíFïäöÞ?ÇyÉ`š|<ÓÓÁûC¥I€—H“#M²>ƒßíx>Ÿ§DÉ¿ž Û÷ýÄ«lÑKx5“€4 H“#M²>ƒw)Îk¿ß?ÿ”ÒäèI@š¤Iˆ‘&YŸ@š|顬ÒäèI@š¤Iˆ‘&YŸý~ÿÌ·FžN§®ën·Û_…±ïûöÃöOß>¶}Ð3Ov•&GOÒ$ MBŒ4ɦvõn·;ŸÏOÞíøG×u_Êv´qç³è/»šI@š¤Iˆ‘&ÙÈ®Þív]×;`ß÷Çãqôc]·–&çý¿%Àr¤I&aÞ]ýÒ’ƒN§Óà‘ÛϥɅþŸ”þOš–'M‚4 kÝgßb)MJ“Àï%M‚4 kÝív<øõz•&¥Ià—’&Aš„šµnðÆÉ···/Þr8:Ÿ¾ï?¹}ÜÊÒ¤- ,Jš„i’ìê¹~¹\^­ƒiò~¿O?™Á»8¥I€—H“#M²>»Ýn¹M8X÷ûýoL“í8 Œ4 ðib¤IÖg0ö}?ËÁŸ¡úõ&?ŸÏÉ4Ù>n5“€4 H“#M²>ËÝ¥øÅUóÅëßÞÞ>¾þz½N?“Á§Ë~ýÅ—¿k&€ib¤IÖg0þ`š¼^¯ Äå¢g‘I@š¤Iˆ‘&YŸ®ë½—ðÕM~¿ß?¾þt:M?“ãñøñÈíãV3 H“@€4 1Ò$ë³\ üç“ïšÜív¯^h‡ÃaúÉ´Ï]ëå&M1Ò$ÄH“ldc[Ÿt»ÝFtÆÁ¯¿œx&ƒ‘t–âYg&€ib¤IVi¹'^.—G>ŸÏ_¿«½`ö/…ü ËoÏäwMÒ$ MBŒ4É* Äå¾Þ±ëº¯ß5˜'žO{ûì¹³Ú$ MÒ$ÄH“¬ÒàÃN›Çã±ÄaÛÏÇ]kϼñ¥3YÙ$ MÒ$ÄH“¬Õàíí‡SŽùöö6ú˜ƒ79~{»åKg2Ëm¡¥&i&!Fšd­n·Ûàn]ï÷ûàÛM9Ÿ7N~vËä“gò‹&i&!FšdŇÃ\u²ïûÝn÷ñPí#ž?ÈàFÜÈ9xCh;øú&i&!FšÌ,¦ö#>»Ï±¹\./g¿ß§ýÓóǹ^¯ƒyéA¬ƒ†mÚÁ×7 H“@€4 1Òdf ÇÁþœnð;ÿ»áñÛ' ö}ÿÅÚ?½z>ŸÝÈÙ~þx<¾~o{Áo_å$ MÒ$ÄH“™¥w4ûsŸÝðøGû×Ëår»ÝÞçcûÏ®ë>»?qÄ­Žÿy<_óx<¶Ïý+˜¶ÿlg8ø×ÿ|›5é$ MÒ$ÄH“™u}@[tº¾ï¿®“#Œë’t]7ïɬìQ®ï'i&!FšÌ,ÚèÃÚ¢³˜·NNé’ÌX'Û¡V< H“@€4 1ÒdfÅFÜKß÷çó¹N œ¥N®ò~É÷“€4 H“#Mf–kÊñíÒÝn·Ýn7úfÉy¿Ò±íp8Œ;™öÆõ}¿äÇI@š¤Iˆ‘&g\«/^<å#ìÒÙu]÷Rœ=J¾w»ÝŽÇãó'Ó^ÜÞ²‘I@š¤Iˆ‘&gY«o_<åSìÒ…ô}ßuÝù|>ÝJ¹ßïÛßÞÞbOLýâdÚ¶¶j/h/ÛÔ$ MÒ$ÄH“êÉ×Où »”ÍNÒ$ MBŒ49e¡žýr+ž¤I @š„irôB½ôúE? Ö: H“@€4 1ÒäèUzé-?ÎFe›“€4 H“#MŽ^¥—Þ2ñãlT¶9 H“@€4 1Òä¸Uzõ-K¬r&€ib¤Éq«ôê[–þ8Xå$ MÒ$ÄH“ãVéÕ·,ýq°ÊI@š¤Iˆ‘&Ç­Ò«oYúã`•“€4 H“#MŽ[¥Wß²ôÇÁ*'i&!Fš·J¯¾eéƒUNÒ$ MBŒ49n•^}ËÄ¿ˆÊ6'i&!Fš·J¯¾eÑÏ‚µNÒ$ MBŒ49z¡^zýr+ž¤I @š„irôB½ôú)»”ÍNÒ$ MBŒ49e¡žý”¿…]Êf'i&!Fšœ²VÏ¿xôßÂeË“€4 H“#MN\«'_<úa‹²åI@š¤Iˆ‘&gY®o_9îO`²ñI@š¤Iˆ‘&çZ®¯_9nýíO6> H“@€4 1Òä¼+öÙËF,»Í‰I@š¤Iˆ‘&k.š‰I@š2¤Iˆ‘& ®›m Ò$#MBŒ4YméìIøGš‚¤Iˆ‘&K­ž ï'i&!Fš,²†¶"|œ¤I @š„iòÇWÒ&„Ï&i&!Fšü‘Uµñà™I@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4Y íR6; H“@€4 1Òdñ¥“&Ùò$ MÒ$ĈkeMšÄ$ MÒ$Ĉk5WLšÄ$ MÒ$Ĉk—Kšiˆ‘&!F\«¶VÒ$ü#MAÒ$Ĉk¥Jš„÷“€4 H“#®Í²P6,1 H“@€4 1ÒäÄ…²…`¹I@š¤Iˆ‘&§¬’ý‹NÒ$ MBŒ49z•lXz&€ib¤Éq«dç@`&€ib¤Éq«dç@`&€ib¤Éq«dç@`&€ib¤Éq«dç@`&€ib¤Éq«dç@`&€ib¤Éq«dç@`&€ib¤Éq eç@`&€ib¤ÉÑ eóÀÒ“€4 H“#MŽ^(›–ž¤I @š„irôZÙ<°ô$ MÒ$ÄH“SÖÊþE'i&!Fšœ¸\¶,7 H“@€4 1ÒäÄå²…`¹I@š¤Iˆ‘&§¯˜] MÒ$ MBŒ49Ë¢ÙH°Ä$ MÒ$ÄH“•×M÷dã“€4 H“#®U^7i’OÒ$ MBŒ¸VvѤILÒ$ MBŒ¸VsŤILÒ$!MBŒ¸Vp¹¤I&€ibĵjk%MÂ?Ò$$MBŒ¸Vj¡¤Ix? H“@€4 1âÚ, e#Á“€4 H“#MN\([–›¤I @š„irÊ*Ù?°è$ MÒ$ÄH“£WÉæ¥'i&!Fš·Jv&i&!Fš·Jv&i&!Fš·Jv&i&!Fš·Jv&i&!Fš·Jv&i&!Fš·Jv&i&!Fš·Pv&i&!Fš½P6,= H“@€4 1Òäè…²y`éI@š¤Iˆ‘&G¯•ÍKOÒ$ MBŒ49e­ìXt&€ib¤É‰Ëe Ár“€4 H“#MN\1[–›¤I @š„irúŠÙE°Ð$ MÒ$ÄH“³,šKLÒ$ MBŒ4Yj¡¶váùI@š¤Iˆ×J-”4 ï'i&!F\+µPÒ$¼Ÿ¤I @š„q­ÔBI“ð~&€ibĵR %MÂûI@š¤Iˆ×J-”4 ï'i&!F\+µPÒ$¼Ÿ¤I @š„q (; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!fãiòù³ý¿Ÿc—²ÙI@š¤Iˆ‘&¥I(; H“@€4 1Ò¤4 e'i&!Fš”&¡ì$ MÒ$ÄH“Ò$”¤I @š„iRš„²“€4 H“#MJ“Pv&€ibÄ5 ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib6ž&UT¨< H“@€4 1Ò¤4 e'i&!Fš”&¡ì$ MÒ$ÄH“Ò$”¤I @š„iRš„²“€4 H“#MJ“Pv&€ib¤IiÊNÒ$ MBŒ4)MBÙI@š¤Iˆ‘&¥I(; H“@€4 1Ò¤4 e'i&!Fš”&¡ì$ MÒ$ÄH“Ò$”¤I @š„iRš„²“€4 H“#MJ“Pv&€ib¤IiÊNÒ$ MBŒ4)MBÙI@š¤Iˆ‘&¥I(; H“@€4 1Ò¤4 e'i&!Fš”&¡ì$ MÒ$ÄH“õÙ¥lv&€ib¤IiÊNÒ$ MBŒ4)MBÙI@š¤Iˆ‘&¥I(; H“@€4 1Ò¤4 e'i&!Fš”&¡ì$ MÒ$ÄH“Ò$”¤I @š„iR„²“€4 H“#MJ“Pv&€ib¤IiÊNÒ$ MBŒ4)MBÙI@š¤Iˆ‘&¥I(; H“@€4 1Ò¤4 e'i&!Fš”&¡ì$ MÒ$ÄH“Ò$”¤I @š„iRš„²“€4 H“#MJ“Pv&€ib¤Ii’ ú¾ïºî|>‡Ýn÷~O¶ÿl?lÿÔ^Ð^¶©I@š¤Iˆ‘&¥IÝï÷Ìf¾ÝnÇãñù«©½¸½e#“€4 H“#MJ“|Ô÷ý_·..±CÇápwMµ7¶·¯~&€ib¤Ii’öûýÒ›ùz½N¿²ÚAÖ= H“@€4 1Ò¤4É_N§ÓÒ›¹ëº¹.®v¨OÒ$ MBŒ4)MòÞårYz3Ïr¿äêï”&€ib¤Ii’ÿ< '~Äãñøú$/—Ëív{ÿ–öŸ]×Ç/Þ¸¾ï”&€ib¤Ii’?î÷{`3‡ÁÃî÷ûvßžág_‚y<W9 H“@€4 1Ò¤4IÓ÷}`3ßn·ÁcžN§çòÙWaþu¯å:&i&!fãiþù·K~v7â¼›yð–É7<>ܵ|}“€4 H“#MÂg÷!λ™?»e²ïûWõÙ=žß>ö×MÒ$ MBŒ4ÉÆ½½½ nÚëõ:ïf  ]×ÍxÚ/=öWLÒ$ MBŒ4É–u]7¸c/—ËgWǼ×Úè£}vãäÊ&i&!Fšd³î÷ûàvýïÞÃ7óàÓ\'Þä8xfû 5MÒ$ MBŒ4É6}v×á~¿ÿúê÷qƒÏ_½^¯S~…ÁGζZÓ$ MÒ$ÄH“lPß÷ûýþãFÝív퟾¾:Æ}âápøx¨÷Ÿ5î·øxÌöAkš¤I @š„i’ |js¿ß¿½:æºÐÞßž9Ú``]Ó$ MÒ$ÄH“lÍà³U¯:×füRËãñ8ýwiù6°þêI@š¤Iˆ‘&Ù”®ë·èåryòêñ¡Ë})ä_aYj&€ib¤I¶cðîÅæt:=uŒøÜÁ€8Kš¼\. ¹È$ MÒ$ÄH“lÄãñÜœ_|çã¢iòv»Mÿ¥ÚA¤I€‰¤Iˆ‘&Ù‚¾ï÷ûý`—lÿôÒÕ1âÓ‡C2M¶[Í$ MÒ$ÄH“lÁñxÜ™÷ûýÕ«cħK“£'i&!FšdõÎçóà¶ü6.š&Çô_­ï{i`"ib¤IÖ­ëºÁ=Ù~>îê˜ë*[ô^Í$ MÒ$ÄH“¬Øý~ܧÓiôÕ1×U¶è%¼šI@š¤Iˆ‘&Y«Çã1¸ŸÞ©4ùã“€4 H“#M²J}ßï÷û[±ý°ýÓ”«c®«lÑKx5“€4 H“#M²JÇãqp+Þï÷‰WÇ\WÙ¢—ðj&i&!Fšd}N§Óà>¼Ýnӝ޹®²E/á5ý_•?`9Ò$H“0N×uƒ›°ýü§ªß¢ÅZ¯¸÷wMš‹€EI“ M·ÛmpžN§¬~‹^k½â¤I FšiæÚχÃág«ß¢ÅZ¯8iˆ‘&Aš„Yöó~¿ïûþg«ßápXè¢h¿ÚÇ#N±eÓ¤ ,Jš„i’uïçÇã1ïÕ1â8ƒiò~¿Oÿ}`+M¼Dš„iûyéM>˜&o·ÛôßWš˜Nš„iûyéM~>Ÿ“i²}Üj&i&!FšÄ~^z“¿½½}|ýõzþû^.—Gn·šI@š¤Iˆ‘&±Ÿ—Þä×ëu¡€¸\ô,2 H“@€4 1Ò$öóÒ›ü~¿|ýétšþûÇGn·šI@š¤Iˆ‘&±Ÿ›üãë‡Ãôßw·Û­õr“&€ib¤Ixéêw¨Ãá0ûuÑ÷ýBųÎ$ MÒ$ÄH“ðÒÕ1îPçóyö/…ü ËöAkš¤I @š„i^º:Æj0#NüºÉööÙsgµI@š¤Iˆ‘&ᥫcÞ£õ}?îhƒOs]Óµ&M1Ò$ÄH“ðÒÕ1úhƒ79v]7îhooo³ß†Yp&€ib¤Ixéê}´Ûí6דŸÝ2Ù>be“€4 H“#MÂKWÇ”îv»<¯§½åãqÚÁ×7 H“@€4 1Ò$¼tuL9àõz<æKb|0lÓ¾¾I@š¤Iˆ‘&ᥫcâ1‡ÃàaÛÏÇ×ïm/øâí«œ¤I @š„i^º:&óñx|q¥Ç®ëþúÊÈöŸ—Ëeð!®ÿù6kþÒI@š¤Iˆ‘&ᥫcúa»®›÷úZÙ£\ßOÒ$ MBŒ4 /]³yÆ:ÙµâI@š¤Iˆ‘&ᥫc®ƒÏR'Wy¿äûI@š¤Iˆ‘&ᥫcÆã?Ãá0îšjo\ß÷K~œ¤I @š„i^º:fÿ”Ûív<Ÿ¿šÚ‹Û[62 H“@€4 1Ò$TÐ÷}×uçóùp8ìv»÷—OûÏöÃöOííe›š¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“@ÙI@š¤Iˆ‘&²“€4 H“#Me'i&!FšÊNÒ$ MBŒ4 ”¤I @š„i(; H“@€4 1Ò$Pv&€ib¤I ì$ MÒ$ÄH“lÍív»\.çóùp8ì÷û¿öên·k??Ní5÷û=vV}ßw]÷ç¬Ú9|<¥öOííe›š¤I @š„i’-¸Ýnçóùcˆ|F{ããñXôÜŽÇãóçÓ^ÜÞ²‘I@š¤Iˆ‘&Y±Ûív:fÙÃí8³ÊvÀÃá0î|Ú ¦E&i&!Fšd­þz,ê,º®›ëô®×ëôóiY÷$ MÒ$ÄH“ØÛ¯Þ>9ýܺ®+XK NÒ$ MBŒ4‰½®“³Ü/¹ú{'¥I Fš„i’ îíÝnw<ßÞÞn·Ûý~ÿ®ö“®ë¾ý†ÊÑ÷*>¯¿Aòr¹´søxJí„¿xãú¾wRšb¤Iˆ‘&ÙÎÞÞívçóù¯ù…Ëå2{ <ƒGÛï÷ßžX{A{ÙàÛÇã*'i&!Fšd {ûp8üu+â“Çg5pÄc]Û9L?ÔgwtŽû‹OÒ$ MBŒ4ɺ÷öè(ùŸ¾ï?«“íŸ^:Ôà-“#nx|¸k;øú&i&!Fšd­v»ÝõzåP÷û}ú7N~vËä«}óŸké࡞Vío™¤I @š„iž1x£âô±¾7ß{{{›å³Å'i&!Fš„gt]7ñªó^ŸÝ8¹²I@š¤Iˆ‘&áÇcÊÞ|šëÄ›oÜøÅšÕ&i&!Fš„)Ë“ï|þêįÂloÿxÌöAkš¤I @š„i¦\,O¾÷p8||oß÷SÎgð™®/=c¶þ$ MÒ$ÄH“0åbýÞý~?ý”ÚAÖz¹I“@Œ4 1Ò$<ã~¿ÜØ»Ýnô{Çãô³jùxäöq«™¤I @š„iž1øÅŽO>=u¹/…\â+,KMÒ$ MBŒ4 Ï8ŸÏ7vûá3ï ˆ³¤ÉËå²Ð‘‹LÒ$ MBŒ4 ÏØív7v×uϼw0MÞn·égÕ"ML$MBŒ4 ß|"kÓ÷ý3o?É4ùäcfÅ$ MÒ$ÄH“ð­Á¶x<§¼]š|føöîöHYemè ÁLHÁHÁLLLHáy»öÔ;55£|Ü4¸ÖSuæQD6pw÷e7¢I €hˆ&aÜДÉô÷·ÐM¶m;ߺ®MÌ$š„0¢IÑu]ïS&Óg^e«^‡i ˆ&€¢I#š„eYöžÒo-Ç*šœÜMD“F4 Cn·[ïùüúS&G®²U/áôD“@Ñ$„MB¯¦i†Îç®ëæ_e«^‡i ˆ&€¢I#š„gÇcèd¾ßï‹\e«^‡i ˆ&€¢I#š„_FrÉëõºÔU¶ê%|¤»ÊW@ °Ñ$ˆ&a]×ÏçÞÓør¹d˜õŠû9kR»X•hD“U.™þþî#&E“¢I`D“ š„cä’¢IÑ$9Ñ$ˆ&á¹dRÅJEÚ±ç-§;X4éüV%š„0¢IX5—ü7M>ù{Þ4h`&Ñ$„MòáÖÎ%ÿ D“MÓÌß²h`>Ñ$„MòÉÇÐI»T.™TUM¦;LK@4 MBÑ$+&—L®×ëóGÜï÷ù[¾ÝnÏ[Nw˜–€h š„0¢I>SX.™Üï÷•ÄõBÏLZ¢I €hˆ&ù@u]«—ËeÙ\òß@ š>hþ–˲|Þrú¸Ã´D“@Ñ$„MòizA]0.|ñB+ŠbþfO§ÓQ/7Ñ$F4 aD“|”Ëå2t–VUµÞçE±øuÑuÝJ‰g>-Ñ$@4 aD“|ˆ®ëzóÁ/u]¯úéUU-þPÈÞGX®°Æ·D“@Ñ$„Mò Ú¶=ŸÏCçç̈ð½1âÌõc{g€|—È–€h š„0¢IïñxŒœœé_7¼Öº®›¶µÞÕ\t­‰&€0¢I#šäØêº:-Ïçóädp‚ÞIŽ“’½^¯‹OÃ̰% šˆ&!Œh’ëÍï¾E™K&MÓ,5qrhÊdúˆƒµD“@Ñ$„MrH]×õÎRÜvváétzÞ™²,ßÝNzËóvÒÆ×MD“F4É!Ï硳ñv»mµW÷û}~T:¹¦¯% šˆ&!Œh’:±7ÏZ`¶mÛñ÷¦Œ¼ý-Ñ$@4 aD“8±#Ïð¶mG¶S–e]׿™þïívë]ÄõÛŸ±æN[¢I €hˆ&qbŸáu]/ûé[ÊõgK@4 MBÑ$Nìø3|Át2mêÀ-Ñ$@4 aD“8±79ÃI'9_ògK@4 MBÑ$Nì­Îð¶m‹¢˜ö‰éÇ{¾äsK@4 MBÑ$NìmÏð¦iʲ|ý³Ò‹Ó[>¤% šˆ&!ŒhrÐu]]×UUEq:~^>éÿ¦?¦J/H/û¨–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢I Û–€h š„0¢IÈA×uu]WUUÅétúyù¤ÿ›þ˜þ)½ ½ì£Z¢I €hˆ&a[MÓ”eùúÕ”^œÞò!-Ñ$@4 aD“°•¶m‹¢˜vM¥7¦·¾% šˆ&!Œh6q¿ßç_Yi#Çn ˆ&€¢I#š„xu]/uq¥M¸% šˆ&!Œh‚-2_òðs'E“@Ñ$„MB¤¶mÇŸ y»Ýš¦ùù–ôëº.ËräÇ{î¤h#š„0¢IˆTEï5r>ŸÇø{Ó ÒËzß^–å![¢I €hˆ&!LÓ4½Èåry}#éŽù5×ò-Ñ$@4 aD“¦wÊä„ ½‹»¦¯% šˆ&!Œhb M™ìºîÝM¥·ônêÏ%aw×MD“F4 1zb­ëzÚÖ®×ëÌ…awÑMD“F4 ^k“·64qò`-Ñ$@4 aD“ w5×™“{§a¦:RK@4 MBÑ$è]õ~¿ÏÙfzûó6Ó©% šˆ&!ŒhEñ|]t]7g›½kº¦:RK@4 MBÑ$lr¡Ïçù›M9êå&šˆ&!ŒhÖöx<ž/в,ço9mäyËéãÓMD“F4 k[ï¡k<Â2«–€h š„0¢IX[o€¸H4y»ÝVÚr&-Ñ$@4 aD“°¶Þh²išù[NMÌ$š„0¢IX[Q‘Ñdú¸Ã´D“@Ñ$„MÂÚD““[¢I €hˆ&am½ÑdÛ¶ó·Üuh`&Ñ$„MÂ&WÙ.6¾yK@4 MBÑ$lr•íbã›·D“@Ñ$„MÂ&WÙ.6¾yK@4 MBÑ$lr•íbã›·¾¢I€0Æ´A4 ¢IÑ$@cÚ šÑ䎢Éÿì“hD“ šüØY“ÁŒiƒhD“ŸMÇ#š„M®²]l`A¢IX[Q+]]×=o9}œcdH4 kë&Çü-7M#šöB4 kë&›¦™¿eÑ$°#¢IX[UU‘Ñdú8ÇÈhÖv½^Ÿ/Šûý>Ë·ÛíyËéãs C¢IXÛý~_)@\/ôXœhÖöx<ž/ŠËå2ËeY>o9}œcdH4 ›\hEQÌßìétr¹{!š„EQ,~]t]·Râ °Ñ$¨ªjñ‡Bö>Â2}£ äI4 zcÄ™›Lo_<îXh6¼Öº®›¶µÞÕ\]k@ÎD“£w’c]×Ó¶v½^Ÿ† °*Ñ$Ähšf©‰“CS&ÓG8Î@¶D“æt:=_eY¾»ô–çí¤;Â@ÎD“æ~¿÷^ o-ÄÚ»0l’6î9MB¤¢(z¯‘ô÷¶mÇß›^0òvÇÈœh"µm;r¥”eY×õ¯GF¦ÿ{»Ýzqýög¬ °9Ñ$«ëzÙëËR®À.ˆ&!Þ‚édÚ”ã ì‚h6±H:i¾$°#¢IØJÛ¶EQL»¦Ò=_ØÑ$l«iš²,_¿šÒ‹Ó[7`wD“ƒ®ë꺮ªª(ŠÓéôóòIÿ7ý1ýSzAz™cì”h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&às4Ms»Ýªª*Šâ|>ÿª×§Ó)ýýr¹¤×<°]º^¯eY¦~nBÿI/¸ßïmÛú/ÀH5y®n_¥-¾º®êH†u€ÌïÞÆfø@ÊÄ{ŽáVíh§^ösûé+ 䦽J½ûw÷'}‹ÛíÖuSà“KÛ·T¤Þª&i'ëºþ„: À^îÞÇ›}<Æ„ø´òûòÜ^£¯=!ûë•¶³`×;íØétš¹K×ëU@ ði¥í§wCÉ_ó_î÷ûQë,ûº{`l6u͆ºxN9ŽZþ`_RÇmíZ9?û{6šIúâeY.¸Kó–ØKiû)•¤ùE$•¤É¿rɳΰǻ÷ÆfG&Ÿ:ë8jù€}¹^¯k×Ê• úår™¼KC+üÌTU•3 àJÛ·¥f»|­>mÂK†u€Þ½÷>6;^—u²üÀ¾Ä<ƒc½š>­ß=žKEq½^›¦ù5>œÞu¿ß«ªÿy³\€O(m_Ì%¿M˜;™[`¿wï]ÍÞn7cÂ|Zù€}IèÂêûét*Ëò+ Lûóó]é/u]ÿ9êûîšE#ß:íÆ‹ÓUÒ¾E1²gÀ±KÛ¿¿rÉô¯©B¥zñ«|Ün·ñåÄÏçó»édVu€]ß½÷;”z¿ß °xQMÀRº®ë]ì.,šL}íªª~õ²GŒÿüõõåïÒïóx>Ÿ_ß™W:¿¿Æ¢8Xiù¬ôOf‹©r”ï.žO`ïw量øÀg¯—?Ñ$Ì”º¥_?¯½^¯#Óý¢ÉôéÓb»ôÎçóÌ‹zG’'ÌOù³ œ¾¦³ਥmdü󭺤_ä'.ùÔYö~÷ÞãPjêÍ`©¢,š€­jëû0¹»ý³Ë9Ôï~%[ê±Îœ ’¾ÔüÑivTÚ†’Ð wþ¡”ót:í®Î°Ç^Ò+E6çÃ8òõ °jS4 ËÖÖe÷át:Ýï÷E654„ûʳTz'§¼»h^¯ÞG½,²er+mC¿H™<°<´<øë ˤΰÇ^Ò+E6çÃøçc7 °RS4 ËÖÖœ¿Qïù^Y­¨÷‹ÌmìU¶¦+À!KÛä24¢wXõ|>ï«Î°Ç^Ò+E6Ûo=ôè硟ý8OX°)š€ekkÎߨwòã+9àétZé›-ëô8Xi[imðôö|ÖŸ\gØc/é•"»£¯œÜn·û|h&ûêcŠ&`ÙÚšó7¿Ý¶—­Éð ¥­w´,Ëù[ÎgmðÉu€=ö’vÚ¯ZÃö{¢¨ZÀÚ}LÑ$,[|¹Ï½ïêºîc#€Òö®ªª†fgÌÔ;ĺ՚®ŠÀÇ–¿]”€¡E ~ÖMµ €™åO4 ™w`w±ÏCO!™¿?½?Q>NÎ.€ƒ•¶¢(ž·Ü4Í"ï]x|©ŸÐ¾mÀ‡D“©2žÏçÞþ×Ï¢©–0³ü‰& óì.ö¹·»Èóî÷ûJëûUi[5=L…c½Üóðm>$šì]ýùÍj3ËŸh2ïÀFê]òî•)ŠC}Øù£¾½[®ëÚÙp°Ò¶jѼ^¯ÏOÜK`½¤}u{keïb8Æ„˜YþD“\|sÞáÞ)НL~ì}cï/lß2ô §ÀñJÛª7ü¦iž7^UÕ^ê,{ì%í¨{X×uïîõ>ôY €™åO4 ÁÅ7箪jòÈmïB|3:Ù»?½½cö^Úâ£ÉøLpN`½¤]t{§…&—Ëåý\rëBŠ& ¸øæ¼Ã½ñâ‹«§ŽLœüêÕ¾û¼°Þaäóùì¼8diëÝòRÏšì†MΩ³ì±—”÷°mÛÞéy`fùM@pñÍvo‡²Åׇ…{Nüë©^©çûʦ†~¸;gyXr.mçóygçS”ç×YöØK¯Dņ:P_ÿšºQu]¯ÑJߢ·þ¦?Ž|AcÂÌ,¢I.¾Ùîmo¸,Ë·6r¹\þl-¤×Œ5=èD. pàÒÖ[†\Ä{ó¢¼H`½¤‘Jô®Ô™Z°[”¾È„ž—1a‚ËŸhfß¹$Àíîî=5M\võcÂ̬¢I.¾‡ìq-¾7y›CË ÍÜ,û*m÷ûý•§FŽ„’_ƒ´½¿Ÿ™°„ÝVu€=ö’VÕ»ÏUU½X¾ç|5cÂ̬¢I.¾‡ìq_¯×Ň|Ó. MŸô .€*m© ¼»¾kY–?ËPojšf/u€=ö’Vu»Ý^ŸùüʶmÑÏ`]HÑ$ßãõ¸{§LžN§Eö|(4Æ ði¥­mÛº®«ªJ¥á¹¨¥¿¤¿_¯×ûýþ\#z׆ÝK`½¤µõ®é:Ô ûßvœfºÊ l^|ãwãñx îEzܽ‹¯.õ¯¡¥bo·› àcKÛ»žW…ü€­ø: À{IYeÑ$YU+årè*îºÇ}¹\V]%¯ªªÞ•úœ`ŸYÚÞÕ¶íëOãʰΰÇ^RVEY4 @VÕJ¹€ºŠ»îq÷.‚´`w¾w©¢ô¡N0€,mô>k‘ŸÐÈ%öè0woÑ$;­VÊ äÐU\Cï:«_.—Ë‚=æÐš®N0€O+mÓôþ„fGu€=ö’²*Ê¢I²ªVÊ äÐU\\ï ‘ï÷V€Ò6Mïj®óKad`½¤µõÎýZ[F4 @V]Hå‚‹oÀçö>üqÙGkýù5Û¶=À‘`ï7ä5ˆ\gØc/im÷ûýù‹E¡1@þ]HÑ$©ËÖu]ê•麮þfê)/ø¥ž·:œ`ŸPÚæxkFI¶u€=ö’ÖVUÕów¹^¯äß…MÀaºlmÛö>Nkñ¬ð—Þnþ‚ "mø{`¶-mkT¨ÉCÐ[ÕYöØKZÛétZ|Y€C6Ȱ )š€ctÙz'†|KÿºÞ×ìý½nÒuÝ"Ûï]y)æ÷ÀlXÚfJ•bÁ_¶lXgØc/iUMÓlX‘ 0³jˆ&à]¶º®‡Jóù|^*"Ò;«q©‰“mÛøÀÒ¶Reœ6dÛ: À{I«êºàÂ5Çh mR4 {ï²õÎ ùžÓã^ï¹-eY.û˜0ò/m3 EWUµÓ: À®ïÞË>Ñr諵m«1À.º¢IØo—-u¨{; þÑìxÇN|èÛ-د «Ò6ßív[j‚L>u€]ß½¿#ÑùÏ‚úùMä÷2& À̪!š€ývÙz—ñùr»Ý‚»ÿ#̓ ³TÒ{çKÎyLù—¶9Ú¶M5bÁŠåSgØõÝûWwfò/-G~6eòŸh€ÙUC4 ûí² •ãûýÿe‡ž8ùåt:½Øïºn¤ÇÜé ¸´}û*/Îslšfd‚Ìä'gUgØïÝ{蜯—§´óùD®Æ„˜_—E“UmÍ­¸¿¾cã‘â÷BCu]ÿZȨmÛô—Ô¡š)9sl€½”¶ç=/é_O§ÓJµ#·: ÀNïÞã[H TÑR]ûU°Ò_Rü³ÒÅ/Q«Š[]vœP[?6šü7ütÈEÈ%_Ú–Ý“Óé4§vˆ&Ô²€hrŽ OR^é;ñذ.;Ψ­ŸMþ{mîdðØ2{)m îIY–3GkE“jYÎÑdü|É‘¯ãÄ`úì8 ¶~x4ùï¿¥‡þ\^/rl€½”¶Eö$Õ _ ÀæÜ‰Ö›ØE-Ë-š ~¾äŸ_ljÀ†uÙq@mM&]×ÍŸ>YÅ"cË쥴ÍÜ“ÓéT×õ¾:ÑzÓ»¨esîÞ©s”ÊÓù|^j²dÛ¶¹a'ÖeǵU49¿žºÛVpøÀÒ6mON§SUU‹Ñ$€Z¶ìÝ»mÛÔ?*ËrÚÏoR±Û6”9ÂN<6¬ËŽ3ð¬ëºûý~½^‹ÿôN¼\.é¦I|WÞß·|U“ô‚º®s¤€w¥úõUìʲLEíù‰é/_½¤Ûíæw›ì”h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €ht>Ÿ{»Æéï¸Ì>œh–r¹\†‹®ëùˆÇãQ×uUUEQôæ#§Ó©øOzÍõzmš¦m[ÿi`AérJ'ÓMÀñ!š`íÖã÷ ùõz­ëÚ9pT·Ûm½\òñxTU•ûæeY¦=LÛñ_ æI'Ó…æø Mßzë{†W| €£jÞøÙÀxÏB4 Àë­ÇÉ[Z÷ÌÒgÀ.ôN™½ñ|>ç<‘\^Ô;qRÛ`¤g!šàõÖ㜠Þï÷}=n²ëº´ÏUU¥=ü5ð˜þoúcú§ô‚ô²€iÛ¶®ëËå2²3éˆ??ô×µóùœþx½^W:_?m?}Ês‚Sü'}åÛíöxOøÀ' .‰&x½õ¸R‹tÚn¼;ãc|ß^\ÑîÅuêæ¥‘•o×k¢/û¡ï5/xð_ÿÊ›|h&çÛÚ—Õ¶ÇvY½ßå|>ÏßòPd6ó’ɈAwõmok‹¶¤³îÝtrhZîü+ô;šošf“ä7ÛLïжºœ2\Mðzëq·æŒOëÚÇãñî}#Ÿ²ø çäÑÈEfÒ½û_mñ0ô­¡æwW\ä+oò¡ùœo«^VÛÛ˜;áív›¿å‘Ó`“‡lnM.{Wßö¶¶ÒÌ"Wú"Sò‹¢ø·Ü¼Å¯­½.Ï›ÌÐìÚ“€Á%Ñ$sZs68´|Üø˜äÐì•[ªãtL»—^ðk‰ÅôÓÇ× œ^ü9–^UU:Œ?‡ÇÛ¶M;s½^Gvf·žÏ篯ÿs¶NúдigFr„ôO¯LðŸqs¹\~}߯þúÊ#Kfø¡Yoë]VÛÛÅ ]ÓV-þeä€O˜—ÛˆÁ¶wõmok#_3äéToþóóÅi³é/îÀÿÞ\ï÷õ#ùU\~fâ_·‘¹#MØÚë Ÿf{“úýÕ"· €ƒ .‰&x½õ8gƒCÓ®×kL#¶wû##ØEQü9w)½`dxö­É®ëfOi ½c¿ãošAóÊ×ÿ7´ÿ—ý24RÞûbòó©•á‡fu¾­wYmxl×Ð{z¿þüÖ9—|º0·zèäJ% 쮾ùmí××L;“ÞõV9yÚæ[ Y¨ö­YÞ“·V–å‹_*ç›Lï¾-2Éà`ƒK¢I^o=NÞÚÈ üøðû[+Ý=Ï•H¯ëúk,´wûC#Øo%-j÷Öé†fsL˜?•¾òÏÒ‘WåÅo=Um$nßó¡É/Óæ˜¤}þþ¯™Û‡æv¾­tYm{l×Ð{U.øð¸?CŸ´¯Ï&˹DÞÕ·½­ýüšiO&Ϙ X—úBÚ½¿ËÈdà ÉàÐÖæL´Ïä&Ó{º|Îà’h€×[“·6òDªùغ®§-~8´„à[#Ø_†fÙ¼¸©¡áÖÉë:¦w}ó¡× ­>7áë ãÏ0êÍe^Ÿ”Ôë~¿ÿ{wwº®…ô”@ ´@ ih×ýF ´@ Ô@ ´@ ¹‡q2öMdÇ¿ËË0çÈýû$¶,lÙÒ‡äöIm‹ì4Ûù6Óeµ`ÝF6ƒf…í'¿¥?Wú]Ñ- ¬U_¼Y›PÓ±tÿ&CËœÜ^Y^SI¦ÝZ—Ï(y#Ó´p½GJÀà’h€ÁOÃ6Õ2ýä×Å©¦iTU×^<Á¡:¤ã€gu•Î^ï5kªù–™tÕ1ÞÁKÏUÓçöïΠê²Üâ‹ì4Ûù6Óeµ`ÝΡ)7œöˆ¼c±Ôóó•…ß^ç—öÙª/Þ¬Mkä,¼¦¹¨j¸iªã„[+»öF¦)uu,@òÁ%Ñ$ÝŸl§%—ì2^7Õ4Ÿšf7 Bl 2~cÕTE³ÎªŽ öz‹YÇÃoù˜éP,Õ‹És¾ÍwY½X±i.óä;jZ€´»rå–œÏç 3šøhr|«ž¡Y 8 G.Ý<¬$Õ”vpÝÞZþF¦ú”eß ppI4 @÷§Ç¾iÛï>“eÂiß žµÑbØ —ê4Ÿ1Ã郋:rø´ï|¥·Š&óœoó]V/ÖC¬®š;Ó…Ù²äu_›ÍæùŠÆ´WÊ|­z†fmZMÙhü‡X½›ž@:8rÍßÈT×@;aÖ2¸$š ûÓc÷?¿ÝnÕݾ#üÁÃã#ç5-æ6 $¾Ï®êçÌŽî3qšT—¦,§Aª_d§©Î·ù.«¥êv&Õ¸p¾÷ >_x7m÷¼”vð9Üö¾L³–ê^Õh2àq¥®J³P`ƬòÚ÷õˆ«‹&“4k}?ëë¿Ê'~ü×î?í'À"Uakù™Å³l€÷\Mxzœ#—üŒÍPÎçóø-÷]ý¯Z’¹«æYó6½êjäT¯aÐÜ;Ís¾ÍzY-R·óY*šür¿ßO§ÓÇÇÇT ½ö:åVM&iÖÚÝn·ò™–+tdô¼HUgØZþFF4 3¸$šðôØ}]ÁIŠ1þèæ úK.TwºH¡ËÚ¿å´)2á€süNóœo³^VK} ‘màÈ׃öœ^Wªn¿ß¹~»¿•ouÑdžfí§çË—'|—è"UdkÉ™iW¾0¸$šðô8Ø~¿6¤?Ó3guls’С:,Ù²ú_uîLߥûÊ3†¹\z-Jùññ1~Ì9~§yηY/«¥>ÐÈ60OñÊGÿ +Köº—}Ïïç›E“å3š£`‹Tu’­åodŒ•Ì7¸$šðôØeTðp8Œ‰`fzæœõQ¶×Æy¨þ'V{ašÞÖåì*{>Ÿ 8ï4ÏùPžE>аË$m‹ý\)´Ë2¡¿®÷;÷áG¶ê }”Óx¾´t‘ªÎ³µäŒ±2€øuõ ðÎO»Çóù<ɤ0Ñäz»Ý§ú’¸¹§åFîô­¢É¥>ИË$Ó]êí×y”]rÑä˜fív»exyÕh2y#c¬ þA]=¼ðÓãËC4™måÃëõ:þEl}_f¶Ów‹&—ú@_² æ|>·Tlù¯ þËG“½rÉr™ìv»ç¢£Åétºþg|u½p4™¹‘1Vÿ ®ž^øéñeŠ!šÌù$>ŸG.¸Ýnû®Ô°Ó7Œ&ü@_þ2é¨å•|]Öt}h2¾ÚËéúk^ö\S´ã;…E“«kdŒ•üút$š ûÓãËC4™ùIþ~¿ŸÏç_¥lgζӷ&ü@_þ2é¢)²)ÿ¾àá¿v4y<[&HxË¡hruŒ±2€_ŸŽD“tz|™bTgµtœÃÒî~¿WG¤ãKÒ·Vµ_S}^.—ãñØk^ÌÈ5ú&ßižómñ«{‘tÂËd©)œÃ´,ëºà- 2š nÖÊéÑ2SrØÉ#š\W#Ótxª .‰&öôø2ŨŽX~½ÛkŒ²‘^s”æ+É"‡?·çÔ˜ý~ÿë’‰FHãwšç|Kuu/õ¾üeòwõ¾O4™áójÊ‚»¬ ;Gu½g4¹l#3¬Mx·Á%Ñ$ÝŸ_¦ûýþçfO§Óø-—ô—®.@7IIZTwºÈÄ´1®×kËò}3Õá°æ9ßR]Ý>Ð/M¶Œ,x ˜iËšµê•þë,æùªK4ßȈ&:vID“t|z|™bT_6fbË—êÐtËðø|%é{økÜÅ›5 º®èŸ£s7/Ó[MÐýéñÅŠQ{<]±üáà9VÕÃ,;r<óv»µ¼T®zøgTßû³“$Õ¥ùš>ÇEvºŠóm’ j©ºl¦Zlùï7?Îñ=„¦)„çêVO°IÖ¥ŒoÕÚµj¥ÙuÓW©ê [KÞÈ4…ã)ƒK¢I?=¾X1šF},³Y]Z³û ôñx¬þùà‘ÒÇãñµÍ¾‡?~¿ËÞ¿>ÜRu#gUk¯ižÔ";Ív¾ÍtY-X·3©¦{S•äçÅ>a@Ùt†tŸÄW] v’õliÕcšµjµkÆËÇÔ.¿s4™¼‘©^5ëzA-@̨Žh€îO¯WŒ¦)6½Ò¢¦ û0þãñh*Év»í;wò|>ÿ½µ–߬Îñ³Îäߣ÷£É‘CÍÕÉ/-oñ[d§ÙηY£É¥êv§Ói¾×M6}ad‚Vª½4ã¯ë¦Œoüôù[õ›µ¦Is½*íçîD“kidª-ü$s^lpI4 @÷§Ç×+Æõzmz²í²Êbù…–að^ãœ-%é8²ýx<¾…’¿VWÓû ¿"˜î#ê¥üÕȬׇûÌe:F±-ø-Ó”Ùi¶ó- šŒ¯Û94E#glué¡—«ér¹ôúZB)mËdɾըÒ8ŒLpænÕ—mÖš¾~Ð%íJŠ&Ó62M'Þ"_«H>¸$š ûÓãK£:1êïÀ¨ü·̨üßò-S“†M”¨®÷÷ˆzù…Ëåòw,Rþw)Ìñx¼ô_{$ú•’œÏço•ðÜu9Ìö4dX¥Ôm9ØŸ;}<å_Ê¿·ï´%9Zd§Ùη°h2¸nÚ ù^¹Ø^ußêá«öÊ/´ŸÃæ_·äàÏä´¥-Z¶U_°YkYQ¶Ú†??IJǖ6\4¹ŠF¦©m÷< \M0æéñU‹Ñ>b9À€·ÎT’.ÕÕ2–>ëÞgÚcû”¨Evší|‹&Ãê6¦qØn·ké¡Î%?»|S]ø“pK5kŸ­‘î€N4¹–F¦úa ~xíÁ%Ñ$ÝŸ_¸¦E#‡"'Ï­ºìtL‘*šüu„9ɰö²çÛŠ¢ÉesÉÏæ·ŽŸc–K–3d@.ùÔk*_ªhr©fí³õÝÁ½‡²)Ñä*™¦Õ\Kây0¸$š`ÌÓãk£}¥ÍÈAÎ çûtÖý~Ÿp²O—½O2zßwx‘f;ßfº¬òÔm@+t8FnöùrØ_b£|"#ë°rX “´êñÍÚ×~Ç|²åƒûZ•T4¹ŠF¦i=v“¢IF>=¾|1n·Ûàqìò‡¾¯ljäĺòçß^¹ÕÅår?öû|¥Z—Ú(»<-kð¼°Evšê|›ï²ÊS·š;t(Ÿc——Eö½'y!æç¿é䀳4ÕÍ%¸Yûª·a×ÂñxüûäM®¢‘™é ¯:¸$š ûÓã›ãz½öù,¿< ìâ™YôTßn·§Óiäˆk9œÁèsèþv»õÝÝsúØ€Ñæ¾aÁâ;Mr¾Í}Yå©Û©.ØÅfËŽÊÅ;2v)>ÇlÓr°½¢„7—àfí©{*úL“6Ý¢ÉüLÓBÙš2€<ƒK¢I¨z<—Ëåp8ìv»ocËåÿ–,ÿ©üBÌÜ®Ûív:öûýÏéK_…9ŸÏ“æz½~í÷çûv»ýÚõTc°ïñç<²çÁ–ÿZ~gpXd§™Ï·™N¡¥êv¼jR ?ßËU>îãñø¬º¦y‹Ïk°üNùÍ™¾ ñ­!*;úyŠ~5ås (Æêšµ¯zû9÷¹œZÉO~̯ªésùd}4Ÿ¢Ièïz½ÆLœÖår¹T‡ü=@ Ñ$ P·8ëÄI ¿ê”ÉÒ\¨€'Ñ$ `â$ðMÓ[&M™ø"š€aš&N®î ÀxåÂ7eàW¢Iæ~¿W{Á‡ÃAåÀ»9Õ¡4*à‹h;ÕŽðívS9ð>Ê%ï‹ ]ˆ&`°¦%·Û­e]á}”KÞòÎ]ˆ&`ŒËåRí ï÷{•ï iötiTÀ7¢Ii¿ßW»ÃçóYåÀk+—¹/'t'š€‘š–uõÒIxmM¯˜´”+@Ñ$Œ×”P xIåÒnºê}' ‰h&q:ªâív+„S.êriW/ùÒ¨€&¢I˜JÓK'¥“ðJZrI¯˜h'š€ 5åßU¸ÌÞœh šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I€÷|ä›|/Ûí¶º£òï+ª«×8 €œD“ïùÈ7í.öû}S¢÷x<Ælùz½žN§Ãá°Ûí~憛ͦü{Ù{ùÛí6þ@Ji›ÒɲçÀ¢IH{ueÖ3vÂíŸN§isÉëõz8š"Âvåï÷û˜ÃiI'Ë‘:tåtô ùíX=àêJ¶3vªßn·¦¸o.y½^›f_öU¶3& ,%oÚò$s3xí®œŽ,{;Vϸº‡’íŒdËÇc³ÙLá5mgŒóù<øÐš"×RΑKÔðò]9=Xöv¬žpu%Û;É–›&9ÈgºˆÆ¼ ²…—NèÊéèÀênÇê7P÷P²±ã7{¹\šÞö˜ê²“$–c©n³»ó @WNGrÞŽÕ3n î¡d;cGn³i)×ív;l½Ó– ¡ìèãããx<^¯×oëÄ–9ŸÏ¿¾¡rðÊ®åXÊYÖ@WNGVt;Vϼà TµðVglÓt¾¯˜l)äf³){é¾ÁÓéÔòDz¿ß‡¬é¥“Ã&‡à‰ÜUE“ [Š3¶»ûý>yZ÷÷vv»ÝõzV°ê$Ç™–uw IFÜPþ|ýè–ÂËŸ±»Ý®ºÍ1kœŽ %¿4-Á:¦xå«,¥uv+y"œÿÇ50ô®*š çÝäÏ·Ÿ÷¼:x“3öz½Nû>ǧÍfs¹\&9Þ¦%XÇ”°ümu›#ƒT€¨'BѤ> 佫Š&ès+ùSýÑ-…W=c«S&7›MªCþøø˜vM×ÏÃS'€Õ>Š&õ ï]U4 @çûÈŸ–ÝRx½3v¦)““«Nr#š8 ¬ù‰P4©y甆IºÝDþüú£[ /vÆVg#f›2YÜï÷9®ÓêÄÉR'Î1 ý¡hRòÞUE“t¸ƒüéø£[Ê7·Ûíx<îv»o)Où—Ãá0ÕÛWQ˜ûý~>Ÿ÷ûýÏ%R·ÛmùÇR¶R†Çã‘äŒmÊû²M™œï:mš8YjÆ¥ äîÈ&õ³tOVQ ‚甆I~»}üéõ£[š|ïã7Øe ¥çx:ª“Î~*ÍY'¬0Ue說]ºÕÝŸ¦JÇ|̪¡S3‡ÃaE{3µºÙR3n @î>Œhr}À^{ùúšåÏ·ôzÇz|÷$s1Xê®*š õÞñgÀniæ½D“¥ËÜ÷ù¡ôIK_{ŽÃ‰,ÌO¥»Ý1­.:ì»ÁS3ëJåfºNוÏü÷l$š\A°ã^ºô)2wOÒ€甆Išoÿè–¦Ýû¬ÑäõzÜÇ,‚© ó·Ò_þù•æÅ뤻Ëå²¢µLK-ÍôN̦Umƒ×"èùD(š\Að×½tïÑdîž$,‹ßUE“4Ü5þŒüÑ-͹÷ù¢Éóêû ØT…ùRMÊëûnÇIΙý~_Ošó"­©»Ýn’W—™êµj@xF4¹‚>`Ë^JO¤iùŽe[¶{’­d¸«Š&¨Ý2þLò£[špï3E““|÷õ¹>Ïz 3G{À·‚'9gª9N9/Òê¸ÍTkÏ–£öÐ ¬­#š\A°i/åá¿ïò/™»'©Š@’»ªh€÷‹?þè–fÛûLÑdS´w:®×ë×ßÞï÷ËåR‘÷åïß_Wa~íqo·Ûgþžù,Æáph(ÿ©ûÎñqÓQ¤íûW«nªïQ7­éj$H܇M® Ø4pÂñØ Ý“<Å Ï]U4 Àÿß,þLþ£[šjï1Ñäñxl)aù¯Õu2û.•™ª0¥_Ü´Ýn×å-MôžGöW‹1É«çÐôZÌ ‡)ªã!içˆ&WÑœ{<6I÷$I1HuWMäk®ÿøIŒŠ&{=Qì÷ûŽ‘Pùµ¦oÀŽœ!¸Taš^Ókú^Ë7Š;–düGüññ±¢·+V×ï¶o¯OvÚ]¯ÛÁó³¾H4[4YÅ/—Ë·U;®×kéh<ÉÜ=IR {M¬£¹–'® š »o®(šÜl6}—?-¿_ÝTéw¯®0MË~XV´©ßÝñ+Á3}ÄÝ?”HMS&§-mÓ^ܰ€V~D“Ã{š¥71,zKÒ=ÉÓK`±G!Ñ$À:šky¢hrÆNñL¹U÷ù‰ßTç*Ž\¼t‘ÂTßY9xÍϲÓÁKªŽüˆKÕU·Ðe©¥`Ms]'_{¶iDÅ7´€g~D“Czš¥G0æi3I÷$O/ €Å…D“ëh®å‰¢É;Å3E“ƒËS]Þg·Û­«0ÕèªûQüÔ”~[ÄiŽ:iš=𰵬.<[ô0ÛE5cGÀËuðüˆ&ûõ4·Ûm—ÇþüÝ“T½${M¬£¹–'Š&gìg‹&O§Sµ3¾®ÂT3Í‘¹Õà/¬“j%Œ@˜Iµœó½²úFËÁß÷Þ©ƒçG4Ùc/ƒW€IØ=IÕK`±G!Ñ$À:šky¢hrÆNq¶hrä½$…ù9¥®{ºÚä|>WG*殓ê*IÙ^àÒôIÍ·Èê*ªHÙÁó#šŒÞK’îIª^‹= ‰&ÖÑ\ËE“3vWE““¦ºNÑø/îV ÓeöâÈ:É?=ðv»5]P—Ëe¦®e2)¯ƒçG4º—$Ý“l½${M¬£¹–'® š\öö½ìS)Caª_Üä-„?7»Ùl殓j4™ç¥Š-¹ä¬sCéüˆ&C÷’¤{’­—ÀbB¢I~¿Yd Þ@×»wÑä䬾BeÁOÿ…£ÉÇã±Ýn›^Ê3ë®E“Àªú0/¾vp¦½$éždë%ê®*šàÇýB.™¨ã#šÌ_˜j–·ÞE€«~¿ß¿[rÉòï3½bòKuA*¾@Ö>Œhr}À™ö’¤{’­—@ª»ªµ[†\2º[¶wÑää|‡hrñËpÙ\2sÍÔ\D“+xðM‘x‹§7zÜ5ä’¡ÝÒ°½‹&'ßà?±æ®“„xrÉOÑ$°¦>Œhr~3í%I÷$[/ €TwU7šorɸniØÞE““oP49«$¹ä§hXSF4¹‚?Ѥi€·x*q# ÷½ãÝsÉOÑdî"‰&'¯“TxyrÉOÑ$°¦>Œhr~¢I#ÒoñTâFÀÛÇ[ç’Ÿ¢ÉÜEJM®÷ŒÍs8·Û­é3>—üMkêÈ&WðàM¾[?€äwUÑ$î ï›K~Š&sI49yy’N¶\Òè °ª>Œhr~¢IÞâ©D4 Àð›È›æ’Ÿ¢ÉÜEJMÞï÷•ž±Õ?Žæ’e§}€õôaD“+èFF“ñÝ“l½$RÝUE“t¾¼c.ù)šÌ]¤ …Ùív?ÿäz½®ôŒ]üpÎçsÓsæ~¿_$—,J ü,O©+· eF4¹‚>àL{IÒ=ÉÖK Õ]U4 @Ÿ[ÉÛå’Ÿ¢ÉÜEÊP˜Ÿr<WzÆ.;†p:ZrÉÛÑ$°ª>Œhr}À™ö’¤{’­—@ª»ªh€žw“÷Ê%?E“¹‹”¡0¥ýóOJO|¥gl5š<ŸÏ%ßï÷MO˜‡ÃaÙv :—S4 díÈ&WМi/Iº'ÙzI¤º«Š&èCy£\ò3e4y»Ý<Ñä7ÕùtñïgœªNªcs½¹ÔU5 FV /Üœi/Iº'ÙzI¤º«Š& C·´×ÞǬ®)šœcƒÕ¿Z*·Y'Õ%Ugýzóý~ßn·MÏ–—Ë%C;P]ªÔ•@0Û^’tORõ’HuWM@†ni“jd3f™hrŽ Vs«¥¾<²Nª_oÞl63•öv»µ•]'lª±~΢°–>àÜ{IÒ=IR RÝUE“¡[Ú¢)Xüõe|÷û½:ÛK49ÓK…·Tòf³éþÅàëõZýÜcêäv»U7RpÖ‡ÒÉ׌DÓÇZjIó ˜v/Iº'IŠ@ª»ªh2tKÛû_-wÞÃáP~áüßÓéT]“S49÷Û?¬¯KKïûÛ’¼¥ÃþüàÚ§¸†ÕɬSÿ‰5²´Õ9¤zôóï%C÷$O1ÈsWM@†ni»_Wdí¨)¯\¶B^)šül}J̃Ö$uR=å¶ÛíŠJ§ºZ«ËÏ&|!&ëêÆìeñîIªbä®ê^:ŒíGËëù:z¾ÈC4°Á.ß NM^.—ùÖt]Q4Ù´üTεgXQ0¬§¹l÷$[1ÈpWu/€$ÆvM¯ÿëb»Ý~½O4³Áûý¾Ûí¦}Ê*Œ<„¦„×òP:ÉùÐô¶V #€>àŠzš vO€Å甆IÈÓal÷x<ôã¾½P4¹ÁËå²ÙlF>\•-‡îÓ§:„ùR¹E“óå³¼y0¾§¹H÷$m1Xð®*š€lÆvçó¹c?îx<>ÉG4Ù×õzð¶Ðg_ûkºkü!4­eZÎÀU<”Žÿøš^ˆc@p½=ÍàîIòb°È]U4 kt¹\JìÛ$ÊÒM+ÿRþÝ‹ðr*½ïÓéT:àåcú™/o·ÛçÇw>Ÿ“„_ÕÑ€7ù¼ªß(uâL@÷äm{Iô"š ×(ÁL'ó»\.Õc/uâÄèB4 @/Õ×›¾ÃÄÉê”ÉRN €ŽD“ôòž'›Þ2iÊ$@w¢Iújš8ùx<^òxËq™2 0žh€¾î÷{õ©ïp8¼äñÇêñ–zp2t'š`€ÃáP}ð»Ýn/v¤åˆÞ*‡˜h€šÖ8Ýn·/¶¬k9¢·Z½`>¢I†¹\.Õg¿ý~ÿ2ÇØ49´» /Ñ$ƒí÷ûêãßù|~£+GñòÙ+@$Ñ$ƒ5-ëú/lzŤ¥\M0FS„W¬7Â+%o:¨µG® M0Òétª>n·Û5¦“¥Ì¥äÕ#*GêãL4 ÀxM/\]:Ù’KzÅ$ÀH¢I&Ñ”è•w|Š&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šˆ&€¢I €h šüü¯=8$ôÿµ+lð¡w’b endstream endobj 291 0 obj 64469 endobj 292 0 obj [/ICCBased 294 0 R] endobj 293 0 obj << /Length 295 0 R /Type /XObject /Subtype /Image /Width 2441 /Height 2402 /ColorSpace /DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÖ1 Â0ü›»–HèÕà‹pÁˆ1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜FF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`Ä40bF #`Ä0bF #`Ä0bF Àˆ1#`Ä0bF #`Ä0bF #`Ä0bF Àˆ1#`Ä0bF #`Ä0bF #`Ä0bF Àˆ1#`Ä0bF #`Ä0bF #`Ä0bF Àˆ1#`Ä0bF #`Ä0bF #`Ä0bF Àˆ1#`Ä0bF #`Ä0bF #`Ä0bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#¦€0b1#€0b1#€0bF̈1#€0b1#€0b1#€0bF̈1#€0b1#€0b1#€0bF̈1#€0b1#€0b1#€0bF̈1#€0b1#€0b1#€0bF̈1#€0b1#€0b1#€0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆi`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bFL##`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0b¬Ý:$`Ö¿õKp·E@1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆi`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bFL##`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF #`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0bF Àˆ1Œ€Àˆ1Œ€Àˆ1#fÄŒ€3bF Àˆ`ÄŒF Àˆ`ÄŒF Àˆ1#`ÄŒ˜0bF #`Ä0bF #`Ä0bF Àˆ1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF #`Ä0bF #`ÄŒ˜0bF̈1#€0b1#€0b1#`ÄŒ€0b1#€0b1#€0b1#`ÄŒ€0b1#€0b1#€0b1#`ÄŒ€0b1#€0b1#€0b1#`ÄŒ€0b1#€0b1#€0b1#`ÄŒ€0b1#€0b1#€0b1#`ÄŒ€0b1#€0b1#€0b1#`ÄŒ€0b1#€0b1#€0bF̈1#fÄŒ€Àˆ1Œ€Àˆ1Œ€0bF Àˆ1#`ÄŒF Àˆ`ÄŒF Àˆ`ÄŒ€3bF Àˆ1#`Ä0bF €ßˆ¹"*9* endstream endobj 294 0 obj << /Length 296 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xÚ}’OHQÇ¿³%B¬e&RðN¶Wí`ŒÝõoʶ¬k¦²Î¾ÙÞÌn%Bˆ.AÖ1ºXÑI:†‚b]"è(‚—í73»îˆÚƒ7ï3¿ÿ¿ß{@](mšz€yÃÉþ(»;>Áê7P‡A+­Xf$‘v™lqdí}…䜛áãõÿ] ‚U€Æ¬ÇמöxÀáû¶iO:¬äÒb“¸M¤’1âWÄg³>žöq†[ ñ2ñMÅ'"()Y'æ±ld4ƒä—‰»2–’'&ßÀSg^™öÐ}8õ¹&›°€åwÀ¥Öš,Ô \V:k²Ý¤;©iÝR;;\‘Œu?ÊåÝV þ°ÿ¼\þûº\ÞC9¾u¥(J•IÒÀëÃ]ýÜàBS˜s_ QP5ûFz¼Úë׋Gõ%«t{3qW°D÷0vz ¼ü \}\ø$€Ôu¡ºmþÀÍ+˜…–ÍÙ¬C–;XØ9:Y„^g±BÞ,Ú\°ACioci]g®©Å·¸(ñL;òz±Úï9ÚAnŒŽÐIó ¨Üê­°4“I÷ÐÝ x#Ã{zwA¼¨j}ƒÎ…Ðþ¤Š¾Q¥óš=˜ò8Ðmèñá Ã(Äo{1±cÚÑd5¾Ué­ÊgÒ·t¶üÆlaȱi"ßÐ\.5æ±”šËÅâ^Å8tph0èk€!‰~D† TÒhd¡‘”»6‚ØÂì±–:>f¤ß&Ÿm×çŠäíxÝA4Ž…¶ƒLþ&ÿ–·ä%ù­ük±¥ªiÄ”¦¬?ûCqÌÕ¸m¥&/¾By#¤Õ‘%iþ 'ËW©¯:ÕXl©Errð'ñ=_—Ü—)Œi7Ò¬›©äê,úF|ÙNšٮͯ6×rm^™Ü ®ÍšUáHWü «Ãÿ5;¿?ÿͰh endstream endobj 295 0 obj 9413 endobj 296 0 obj 706 endobj 280 0 obj << /D [278 0 R /XYZ 71 721 null] >> endobj 248 0 obj << /D [278 0 R /XYZ 122.637 450.918 null] >> endobj 276 0 obj << /D [278 0 R /XYZ 118.899 163.162 null] >> endobj 277 0 obj << /Font << /F8 93 0 R >> /XObject << /Im7 249 0 R /Im8 252 0 R >> /ProcSet [ /PDF /Text ] >> endobj 301 0 obj << /Length 1581 /Filter /FlateDecode >> stream xÚ}WÉŽÜF ½ÏW>©KÖ¾ø– qà\âxÈaœCM«º[±–¶JÊØon¥¥G6h±X‹Ë#‹ œ³8Ü7Ïþ'tÂ"õ‹2v’¼ô‹"sŽíÝ—»Àçmû$¦}CoÞ·¥ó[÷7üì–gµy+u¿>ܽyW8aà—A:''œ$Íü8Oœ‡ÊytßÕçiÐ/‰J·x{ð²8uïõÿ‡¨põ Ü(Ü«²Ñ ‹™~ëîÌ Õœû¡/‡Ðm^è~CYǾ…·uå¼<Ü_˜»VXBæ?£&¢ôWueûÚÔã¨sø÷áOðÕ c?MJ¶¿Ò°ÓÖ®^Çîxúé|áÅrL'®Ñ>Žußñf?׃G~žú¡5Ì}"V?Іº½6ºÕÆcTø²aeý‰ŒB ¡Áœ3ÖÆ‘Ÿ)[kF gÆD`èá¤Ü=jCaƒø¤ià¾Gã²Â%æA=@èÂÌ£2,ýÖyu©ªÉ­ÃŒ! "^›q¨Ÿ&qu·?£;ýÔT¼dŸ5KuL2jøv(b{Ð3Š)ÖH Là›z ÎnvÔq豙Ѻ'r¼ŸºJ µ–x`P Ӣ©«jDà Þ0óË8cåËÐ@Q)(ÃEº´N¯êŽ²Í™ƒ Nƒ-nyŸ1ˆ#ÆžcÅŒªþ„±Ð嶉ùˆ,D°`Ðoí‰ Ó$r‘d ak(ìuÌ¢¥˜#´¸Þ‡³tƒ²G+¶È{tR˜ñ)Œ“Rb &%ú[Àˆ’3æ{%gæS±ªX±’[l­âG\ê·;˜˜ ç<hx蘩Õ#yá—Ieg²äØË^M’Ÿå=n(0;HÒx”´ Τx«›·^ñf-V œHZï‚ÜúÕ’F.ÔIÉOñ¨AuUß2=ÂVä<É©œœJÖ­vPêâÁ žEôµ(É×{Û“î­¯ämÑ‚í¸1ªZzˆmml 6FÆ(ÚfùïqÝI'žÌˆùƯ\Ô¶ Äî©ošž pºgQ =ʼÝ"Œ„/ ý<ËØØð¦Ï>zi¸Vˆ`LƒfˆŸíZÆ6 úöÒI€û wJÈ%h%^vö(ò£PºQ´oÐî±^®Fm£\©QI»éåÖôun‡§^ºÔ¸mƒœõ^!Äú6JÐâ]?ÂE(ªm;ϱsºú9mµRiÙçF÷ó˜$»çÝK!î‡âçÓ}–ËN¯Í†Ö^ý7qÞCw´Ñ½í(ó¥oo$_:õî( aÎ@E² >¬¹ìp›„]{g0ÿ4PùÅùÛ™€7×’á5k.ð"*–‹(¤K ¤ ÆTøq¹V#w¾ÙÈ”A!Fkm V%ƒ_﵂ç ÜqT±y¹¸•—Ö-¡D`“FXË@‘Š"ûa|ÐL7ÊX<E~“c’/Se‹cQ¹÷ @Ñ|yªU‹5­²% ‹NS£B}ýîȈAÄºìø©¿Bëcr@ 0IíÝÚ)·Hú5*„‘C8&c¹AÐ7èbf-êÙe> endobj 297 0 obj << /Type /XObject /Subtype /Form /FormType 1 /PTEX.FileName (./fig/psort-vs-samplesort.pdf) /PTEX.PageNumber 1 /PTEX.InfoDict 309 0 R /BBox [0 0 2402 2401] /Resources << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 310 0 R >>>> /Length 55 /Filter /FlateDecode >> stream xÚ+TT(TÐH-JN-()MÌQ(Ê ™)!af$ç*è{æ*¸äu®õN endstream endobj 309 0 obj << /CreationDate (D:20070215234834-08'00') /ModDate (D:20070215234834-08'00') /Producer (Mac OS X 10.4.8 Quartz PDFContext) >> endobj 310 0 obj << /Length 311 0 R /Type /XObject /Subtype /Image /Width 2402 /Height 2401 /ColorSpace 312 0 R /Interpolate true /SMask 313 0 R /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÝݑ꺂Ð)1)¯óF ¤@ Ä@ ¤@ ŒêP·««‘Œ-ÿÉÖZSs÷·-˲¬Éÿþÿ^@1¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!U=ó*€—”˜Éý~¿\.Çãq·Ûm·ÛÏçˆÍf³ûOøÌétºÝnÇC¹Ñÿ™W±¼¤„À„]ˆŸÿÓét¹\ŒöCî÷ûñx BöóÅ~¿?ŸÏa; “¼–bxI €Y»›Íæx<Š ¡·Ûm·Û ø ñnCÂf•-nXŠà%%ÊèBìv;Ó‚q– yÇ~¿ï‰c³ÙœN'匦 5zö‡¥„À,]ˆãñ¨l ?ËŠ… ×ëÕsš2ÔXDØÓ:0Wb»Ý>ŸO%\ìYV,tu¹\þMEi£)C €žýaOëÀŒ]AaÉgY±ÐI›ˆp¿ßŸÏçÛíö祡ÿx½^O§SøÌf³ñܦ 5ÆîK €Œ.DóWÇív;ŸÏ‡ÃAP¸Ö³ ¿…K¾ùM‚—Ë¥Ó•~¿ßCÚÏhÊPC`¤þ°”ÈèBtÚÂårižä…+8ËÔìù|6_à}~ ðx<ÂÔO4e¨!0xXJdt!2¶s:º·ÛMQ¯à,S§†«ûr¹ ò'žÏ矿¢ØÑ”¡†@Ïþ°”ÈèBämªáµe»ÝNQ¯ã,S›†‰„§Óið¿õ3¯PÉ£)C €žýa)!Ñ…ÈÞZtåÀ·Çã¡´×q–©J*ýßl6#ýÅ÷;•<š2ÔèÙ–]ˆì­5L;:ŸÏJ{g™ª‡Q×MjŒÔ–]ˆ>Le ûý^i¯æ,Sívkv0š2ÔXbXJdt!úlðz½òjÂçó6u:Â?sŠÝŽÇãù|¾ßïƒËû¯‡í‡¿²Ùlþ,´øþÓáácužåªÌ[‹}Ê(ödÝn·p²ÞojŽ‚ð±pf'Ž;Ã^.—ÃáðgßBÕ ÿþS§V%ì|¨xûýþskáÚøè~šÍÏ63”vÿ d¼ª *z^Þ…ùS[ÆhóÝìÜì`Ôçw)!е Ñgƒ÷û½gGâv»í÷û®½”¡†Ä»þõðáð•)ÏB(á÷xòç`òáp£g8^õ‹ÎSë(7ˆ†Dá.¢*þ”QξcÁŒ3õ»Jtç2.áP%RS­ÿ‡óug§2Ð?ÂíS‡=´PÔ×뵜Ö;´MM˜M•gÿ6ßÍ®¶›Ìøü®Û tíBŒÔ-ùúÅÛíÖrÜ{Œ‘ÌÇã‘ý×ÃLJý— ü™ë‘úÖRz€áˆÆ[Í2l$û…zóVÅET ;ñ k؈°köêfWóÍ–øt¬Û uv!úl0µ¼^jtºy‚Ìápü3þgøÖétjX|¯åÞ6¯˜ö9|àÏÊŠá†lžG>Ðÿ,ä­ 7FÏpìJ SÂÉí³ÍèxxsD2oU,VC=̘s7Y#ÎH8/·ÿüþpØáð/áTÇæT¢Ó’ªí¯¦ðwÃ_ÿ°¾›”†ihm2¶Ö5ï´Xk¨-¿Ë-ìXøsÍáT§å…{^bÍa¨Ïïÿw­~Bsm ÿ©Í…àfçfÓ?K €Œ.DŸ ¦æJ¤†‚SC¯áó-ˆûýþùG[~±aÈôë<©ð†áÓN+ûÝ[+¼_—"ÎN R¯;l^pƪX²Taþ+³¿ ð÷΄“ÕéÒkx Z§iwƒ,MÙiªWöÖºæï_÷$àù|n¾FBkÙsµŸ”Ú§) {˜šÝÙ¦Á5mm²N7;7;˜æéXJôìBdo­!PˆF yËB¦\.—ŸAà6ŸOwš‘Z °ÓJzVxûœoŠ1û;˜à,.UmºÎRù]ºò¼U±p_Ó«ý~ßs…ØžØñxÌ;S¯Æð¨ý6¿Îl5Di¡Rjkò÷¯;Ó¿"µ_¸²OS–úK§ 6ÄmÍ¥êfçf<K €AºÙ[kxyVôóÑAãNÃΟ®×kÃ;¤‚¤N#Æo© &í7ÕrêPöܺòûuÑšÐæõ™‹4àÔ¡¢Ê¹“Ôlµ®…8óuA<]ß6–î*œ¦÷{Ç 9´Tö1ý%mN³—®í™~Žqi¤~R2ÞÎDçžëâvœèfçfc“Cu!Úý~¿GK[Î×è3%að£î9¿)µ’Þgaöí"ÓtzyV*°Î+¢r2¬r¼_…6ì#Æét*avaÉ)áŒ[£õˆV¡6±]ÞÎ|þ¹N9iTôW sŸÝìÜì`–þ¿”ÈèB4|þ~¿ßn·Ëår<¿†Ûí¶y‚@t‘É> ÁµÑrMÊÄCßãå¹DçN§&º…6gf©Š .ê'øJ²/“/±>Ì[£õˆ^§©××öÜ™h d¿‘°ù>ÒÐb¸Ù¹ÙÀ,OµRB`®.Ä׈ð•+vý±OÑ€£ç loÑuØFúî– ¬©¥[Î|IÍFlóõYªâ¢…b9ŸÏÑì£Ï2¤ƒ¼›,º··ÿ„}>ýg÷?͇0Ë%VÔÖÆh=ÂYÈ{ iÆÎD'ýõ\n4µ3 úٹÙÀؤ„@9]ˆ6á+ý*ºQçEGk;-kÙépFúî–ˬ»Ý.{X;Zþ-gëÌR×áñxœÏçý~?ÔJ¤ƒ\Œ÷û=ìU¸úz昳\b«O »NÄë³3Ñ;EŠåTEÞRYIø÷†oEßDùsö3Þ)%œ²õºÙ¹ÙÀ)RB £ QÔ>ëõz::M»hX-:ÿ¨ål£¯»šW8ý}{WtrYtâá ~^i8ƒÍµ4 ÿT’¼)œRÂÉZèÁÍ¡pöÎd¼äÔÍÎÍ–HJ Õ…(y‡ß3/‡Ã×ÅSIAt6:I­«)‡¾×}–_‰QèèižÐ R€þU‘æsÝPKSÁbó"¥ã]&RÂb›Êñ|7;7;(Š”ª ±”¿Ýn µÏçè·¢ï°K}¸“°‘¼ØÂÀiTtˆûÏ™ŠL¡V,¢*òõY&õùèUÜs™Y)ád­GtµØ6³n3v&za.n†¯›] ]éÉZJtíB,ëR³ŠRó¢Ô}¦ ýˆÉŽ4ô]ÃYŽžÙ?ïኞ͹& u­Š|}–I}>TƒÏ‡òŸë2‘v ¼Úœ¾Œ‰6ÓÿÀÍÎÍæz²–]»‹;ŠN‹¢K¥õœ…ô]®Mbeà´Óžÿ~¯Ög™r*§©Š4Ÿè?‰ð×Ï÷\fVJ8Yë‘}ú2v&Úà/w`7»º40`XJdt!w÷û½Ó|Íž&؇)ÏÂrÏòñxl˜ ŸwyÏ>Õ Z©§aÒÓdÑÕ,—غSÂëõÚ)î¿3Ño-nÑQ7»zº40`XJdt!V Ñõîz®ÃâÑr]»YN{N¼šFôµƒ?…-óÙ§­¬Ý?Sø*š7/A)%œlkƒuŸæíLê~ Nèf·Ê›ŒÔ–]ˆÕHêíNÙc‰©0ëz½–p²W‡+Án·‹æGÏçs¤Wn¹¦>g¼‰WÑóø5ıâèd[vgRóF[ž»¼‰Î^ ŽÇ£››¬†”ª 1Ù_ßl6ƒ ßE@ÛívͺëWD“¬ö¯û,DwoÞ•9Û‹Žr‡#Šþ{vuš±*¶éÏ—ÐDl·Û1ÛSó¼šßTv› %VÖrÓÔ’öêw¾#%ìôõ®H(êTSÙ©½ís,ÑYf}Ôý¬?nvnvPÚó»”ÈèBÌø×ßéCÃûÈ~käo3«(õ†¬–‹+†4 ;· †¦9 ©7IuÝϹ4ìËר^¿öç‹}Ê8×ëµSɇ*×0…°}‰¥‚6‰O›|PJØ\öû}ËWÑ…¦2u²º^¶}Ž¥¹ {Ø>H G­ÃnvnvPìó»”hß…(¡³ÝnÇãårù3€ù|>ÿ„oZÎÔ8ŸÏÍû>ðgÂÿ ÿØ0 *cRÛg¡aŒ÷ôü.±ðÿ‡ÃqïÒmHóïºzmÏciË~ÎçÅþnÃa6_ìnvnvPþó»”øÚ…Xz¦ÓÚbͰ2⪠ÎB›áñ’{€ ‘P^ÜP`U\tJ8ˆöáÛ×ì¸ÓŸ–N\2ì_2_[’‘–ÝìÜì`ÏïÊjèB,º“1ò<àØiÞŒ¶iÎB§)Tö›#žTÅÊSÂpít]36|¾a)ËöŽÇcØ””pÊ ÷ž¸AJ¦OŠTTJèf·Ö›”üü®œ †.Äd}þßZ¾'ëSójl£Ž{OvžÏç×õKîÇÁK¾¨ªXæ)Õ&o^Íi)~ö•ûx<úì[øÓ?³P¥„“=ffÏüªdBµp"j›÷ºÙ¹ÙÀ,C|RB £ 1å\¯×ìŸý÷œˆôÇý~Ï7_lÿú¹ÏB(¢Œc,¤®†NíaÏS_HU,ü„òoóbÁ®!]×÷šEkuÞ‰;N¿Ï””°ý×/—K^ìÎTŸ«uØ’ —|ÿðîýÌ6í¿››L?Ä'%2ºÓïÆ{¾RÆjûÚ–n·[§ÝîùF¼éÏB×þrªktÔ7oừâR:ááÏçsϼ#|={2TTûÄçM~æ,R®_ït¥„ë´Ì¦2ìUÆ:œïëý~¿»Ù¹Ù@!¤„À:Ün·óù|8v»ÝçÄ¥Ífþ=ü×ð™ŒÚ–žÏçõz=áoýc|ï@øOáƒLa›K(½Óéôy€¡ÌßJ¸ÿ˜°ª¸nÇ#\¡"½K)5yç]©ÂgÂ'G­T?µúsÔ~¿¯ùL û˜ù§µ¼\.ÑÖò}ÒÃ]DSùûzÿŒ–~Æp8CEunvnv0öã›”€3 À"ߤ„ ø˜©Xñø&%`ÀÇLŰˆÇ7)!>f*€E<¾I ð1S±,âñMJÀ€™Š`oRB|ÌT,‹x|“0àc¦bXÄã›”€3 À"ߤ„ ø˜©Xñø&%`ÀÇLŰˆÇ7)!>f*€E<¾I ð1S±,âñMJÀ€™Š`oRBX1)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„P)!ÔFJµ‘@m¤„ÀÛv»^¿áßÎjZ¶ž¢…ÇåP~ççñxœÏçý~¿Ûíþly³Ù„ ÿét:].å ƒ0¢ÀʺORB¨ÍápHuhŸÏ§ò‘zâÆåP~ççv»}&ƒU ×èL2‹çó™ ‡ƒò`qÝ')!Tå|>O~>ºzÒœ`gæM Ý PO˜à)5tbwÿ9¡£{¿ß)5t~³Û~¿7\£3É\‚Âp3R>¬õùKJKw¿ßSW?;Ïž4'Ø)! í™(÷‚žŽÇããñP¶¬µóë6›áIæõ|>SךŸ¬PÕó—þ,¨›z–µí9{Òœ`g¤„,´g¢XPiÁe5ˆÃá`9}Öwi˜Á$%t_V€Ký{³Ù¸PÛó—î”/õ:±ßb:¤„³ìŒ”ã Ò‚§ÔëõªYÓ}$c¡Q)! p<—ËÅ ðü¥;å»^¯©å˜Fý»©_ÖyPš`g¤„—•<¥Nð£8˜ì>r»ÝÞÑy>ŸÃ~Ï` cáIðt:½ß¯s_V€åHý<;Üž…ßý¥„°n©Ìn¨…/Ö×PH =0âr•zYÏçTÿót:)m]ᇃµFÝ—`±žÏ§Éì,ôî/%„K½Ýþr¹ÌÛ°xP*ÿ@´ð¸Ü@¥…%^V©ßȧeé>Z·+ë™û²,JtÂoå˰ˆ»¿”V,ú ¹ÙlfoX<(• Zx\n ÒÂ/«Ô ­«•¥WxµZ™(Àòm6?S`qw)!¬Õ _RÂõˆ—¨´°ÄË*õVnW+K¯ð¢÷eX>Ó XâÝ_JkµßïÇžHø’®÷@´ð¸Ü@¥……^VžUYe…—º/+ÀEˆN'Üï÷J€bïþRBX¥Ô¨‡H¸ÊçŽö;ü|>Cy‡?+»†‡‚ð/§Óé~¿/÷‰¯ÌÊüz½ÇP¾ÞÅþSøÀ,ïg §;œôÏû٫ŵ!Ñêl·Ûw ï_Ô£V³i¡SíîOØ™ðá?uÚ“ptçóy¿ßn-ücøOá+¨!^†“µïæî}8¡H?‡…ßÎõ¼÷š¥·ÞKoºC‰Ýn·pï²mxßÏU9vãPÈe5ͳêì-aÉý"ƨð‹N _/©ê>еZ÷¨Ô¹ °ÌžäPRÓ ¼ ;*%„  …\¡«L ÃSXx¢iÓ$†œ>K‹Ìøâ’ÒBÆPŒÑé±)áÃýuisáÑ5<˜G/ú)<í–?žj?G!„k!»¨Çh <„ÇãѲÅ5äë`H8ºTðy\ƒEL\¼%\†Ó÷“»6w?Þéð"ZïÕ7ÝïX0£’ü€íúË„ÅõS¾Ð–pÆ++z™düìðz½öŸV󹑯+¥ ^»úo°ÓV0J“q½„·¿£õžÇ78^g`ÞÀ’Ÿ5~~8ôùDöa?}< UPæó—”ê¹Þgé”®,%l?Úÿçy-o,NJø.ó–™H4¥í3fþõ(ƒdÆØl±Â3{Ë1ó¡*ùàíCi‡6˜úÍFJØÿT ¹ÝnUŸB>}ñ–pNÙO§8»¹ö7ƒ˜«õ^}Ó=`…œ~‰‰iî©wsg óÒÎxeEû½ÏÑít-èÅÛ|Z¥„ó>|Ïç>»jì×9ÝëK Çî Ì8Xì³F›ëú§ËùÙ6ŒÚB±¢¿³k‹5¥„㊿'ÎPÖ‘¦Vk™f´á(ÂswöCnPZP*gÿሌã°}(ðRMqÆn„£ë3o¨0Wñ–pNÖOîs‹)ð>;cë½ú¦{Ø:9HnUZ_1Lô\BvÆ–pÆ++ú3ferGË¿y#R¹n áÁ3ã§M©P•ù¸4Æ'è Ì2Xì³FûŽAÆ%0ÆJ{¢”B±¢?Öj‘¥ÙG~fÙáAžk2‚ÂÊSÂŒi›)yÓRG1Èó{9K½…Çí¹f¦ Õ>”váäö¯½?¿ëHMŠ™lPzÆâ-á2œ¦Ÿ<`sWÂ}vÞÖ{õM÷àoy?d*¹¯Í&šƒ†Â[¯¬P7ú_ ©ŸÍt]t4cO¤„³Ü†½^š÷M)á4éûe>kt]ñc¨›Ñ ³Ú ¨'J)!,èb?ŸÏëùYtSÙ5«­9%ü1y¨¡æA~û/ëu<‹ÆéH Ò>xƒüx>Ô´×p?)omq5¤„Ëp‚~r×ei ïÏÞz¯¾é£2ï å°u8Hõ™¾1{K8©u*φV®}Ú= _»ÖRÂéo ƒÌx­0%œ¬30q£Ìg°…®k dÔ‡ÔÄvV¬xèÛý ‘ê‡ÏµLÖŠSÂý~ž‚¿ñáñx\¯×æG¼Óé4Mé-:%l~‰Én· øó®ð?Ã?6wÍÊ;U†?»ô® Ícz³¿;¬ù±}»Ý¾ê÷ÞO%ox²ÿ©å _ÿö¡ÌCˆ 1üÝß+?¿+mÃn4Ô猭eŒÏ^¼%\†c÷“›çІ‡ðçþgøV¸›4¬C;W«RBë½ú¦;u,¡JÜþóûÃáZ ÿòõªœ÷¸¬Ãc¼‘°„–pÞ++Ú³íÔ¡m(‡öÓ‚¢ÙÓ×7$J '¾)|„Þ˜ß÷µp½¼oj ÕµÌÇ¥78eg`ÊÀ2;êyAvF}H-:ZìKꨄ”jFè¹ÈR!#?…4•¡0ÓEóƒIózƒí_YgJØð8¹Ûí¾–^ø@à CÿILŸ£dÍ»ÔðZ–yçn„ZšÚ±6åÜ•[ˆþäi)噽Á;ãUª¥tÔGꌞٹVy€ÁïRB(VôÇ„3Žh­,%ìÔ¥O=×´?u¦„©ÇÉN…ŸZ§±Ó¢¯_—êjùØ>–zöŸñUP©ÑÔN/ûhúË~ãÛ á÷æ[žâ†ßgŒ¥¶Ö©¾•P¼^†ÃÞÔRsò~dNÍOû9K«RHë½î¦û}€áòÌž‰Ð0l»¸u'ÞSBÍoø­Å×¹få·„³_Y©è§gv½ ¢åðõ»RÂÙŸCÿe½ü4\b¿[॔gÞçí ŒW©Ê侮ùû3`!òâ ª%%„j¯ô>¯b™kä§´¦2tæ3F{ŽÕW˜¦V€éô8ù–JiÛoªa>iוÙRÿs]›©p2Ê9õðÞæ'¾}ªYɇðë4¶“:œa·Ö¾p )Þ/ÃaojÑh£Ó:~ŸÂá̲€@9­÷Š›î±oE½Ãº¿Œ3^`KXÈ•زx¿¾ü+ûÒnqJ '“j03"·ð­Ÿt{)å™·Áy;#UªÂ;êvi¼ÿ¤^•kØ €I aõR¿õm¿Äå4-ÏâšÊŒÇ™æ3Òr ®Â”0:””ý{ËhJÛ~ü¼ç<”6‡Ös [t8"{|8:C¡M9÷©f%B^%Iý¨`À­µŸSSHñx{S‹N™ñ–ÝG9­÷Š›î5»a¤ÇÒëõÚsà·„–°++ºómŠâsxÿóˆÚ,²M ÚÜÔ¤„“‰FÉÞ¿™ªBÍqð RÂy;#Uª’;êËtdK¥¥ó.J@处°z©ßp–Öò,®©ì³Áè@\Ë—¹Ô–¦~l™ý•Ji[N¶2ô© <®öÙ“T9þÖg•¹¥BËìƒê³µrŠ·ÀËpØýYM_º¨Ö{­M÷=ÆN‹r/å)u¿ßçÍý,¡%,çÊŠV˜6aåçÝTÞM­ÍdF)á4R³½zÎç]\yæmpÞÓ:Æ_/¼£ž±LGÑ{LpiÀ,Ï_RB(At)¡yDz¤„©§æÂ{gùzÏÙO-‡§ÚÿØÊ½@9Å[àe(%,¿õ^kÓ=¬žoš[ÜSjÆê£%´„+¸²þL’z_;Ÿ3§¾Æ”Ñ¢˜¥™•¶¯ÛÓ<„J |Z/¼£ž½¼@žè4Û¹–ø€—”*ýí¼ëbI _=~@X[J8Æz¹©e^¦¯ åLõý¬ýG¼£/Núº†Ø€+Ó.îÆ®!}¶VNñxN.ñwEµÞkmºWÜCû7•NùZ -aQWV4§kžŒóù·ÞãŸáû×ÛÁçé˜`]Ž‘6¸Ê”pÆÛÙZSÂÉ:Ó<ü® £¾¦!*'%„Õ+ð‡jRÂWWÒW•Ž·úÙ,)mËœþrˆöo%¢G÷u/¯@Vpíkú­U¼¯µ§„ÑW-n5ËÒZo)áâzhÿ&jf›)$%´„¥]YÑyµÍoîþüÊ;âŒo×ÓÑrŒ]J8> §ÌÛ”R=æí ^kí¨g+p¹'*'%„Õ‹¦„ó.z/%|%~@8ö$šÅ¥„ÑR깬Ö[tÑ›±SÚb/‡èqi%2VËÌ+BÉ[+ªx ¼ ‡ÝŸèoH¦_ kŒ{ÜŒ­÷*›î®Bý¹ýç|>Ÿþ³ûŸèpt)a§-<÷ñ†#Nyë:6[BKXÚ•M‚š{³ÑåF£ÿ©yæTôýŒ-O‡”pѤåòþó6 …Ty;ƒŠ;êy²)SÞý¥„°&RÂ2wx–I4‹K £ÈÍ¿Qïó¬:ýR‡…\ѱÁ¹®¼YÁ!”¼µ¢Š÷µö”0Ú:ý´_DVXZë]gJx¿ßÏçs(œæ°ÌG¹Á÷$\8ш­}"VBK¸ˆ~QÃ}j¹Ñ·N‹ŽFÏæ\µKJز®N¶ â RÂy;ƒŠ;êÀ”w)!¬þ2ïùú’Òž;–¸ÃRÂ6_/ã.d©ÃB.‡h9ÏÕ+È+BÉ[+ªx_kO _‰¥ÿþ̾9N¡+61,­õ®*%¼ßï‡Ãák-ª-%ü)œ†Ì´ù…z%´„ö‹¢ó4S©åFß:-:úYíç©I }X\yfopÆÎÀà…°âŽzžaß)#Ýý¥„°úËÜ.•°ÃÓ/.%Œ>’qGÍ–¸Ôáx#9RB)a™Åûª %Œ®¤—®ý~_ZhXZë]IJêÀWëÊúŠá2I½7ÇL%´„ö‹¢ K¦V mXn4ú†M}þÑöï8“N Ç7ñ…4 åT;RBC"ÔFJu^æv©„–Îû·ŽxbAQOú ¡ä­U¼¯ RÂWú…DmÆ Ãw/—˼‰ai­÷êSÂpºÇƒ]_ŠÎ›ûš¸•ÐØ/ŠÆ‹ÑW%†Zú5×k¹èhô}ˆí()áê÷Ö‘ÎØ˜¦¤„ÆN(‡”ê¼ÌíR ;,%”¾¤„cž)á*‹÷UGJøJ¼Ä§“Ãá0×ãRÂ)E)a³T¨Ú0MJ˜ò9Ãñs’à+öžµÏ\¯å¢£ÑW¶ÍX»¤„¥íÕjR¹:RBC"ÔFJu^æv©„–J _RÂ1Ï‹”p•Åûª&%|ý7ã©ÿÛåN§ÓÊnôRÂß:E„¡:ív»÷ªtÁù|¾ýOiÇ5öžD×Éü׸訔0%:ÝéócÞ`˜Zδ͢£Ÿ¹Iû—¾¤„<î­)%œ¥3 %4$@m¤„Pçen—JØa)¡”ðUpJ¸¬Ëm‡PòÖJk´ëI ß.—KÏÅ$·ÛíÄ J §Ñðн߳HBj¹c=)ajÑÑè$¸Jî×ÙÎìû‰žêêŸDW%}µ[tô3IlÿR—”°‚¾åÊRÂé;란/%€–7&)!¬þ2·K%ì°”PJø’–z^Ê)O)aù—á4åóx<.—ËŸÉ8ÆWÓªH œN§†iƒo¤ª'%lx®ö~½ñè2¡&.µYn´ak_wµýK _R ú–«L §ì H ‰P)!Ôy™O<§`é]â1v8ºDØn·+ù±wú¯G§Htê4¢•ZüjõOÍÑ}XÖëÌVp%o­¨â}ÕšþiÁ®×ëétê4­`Ê¥GKk½WÙtNÎú=÷*¯§'%ìšNÜØ/zûœÜ÷§OÛr¹ÑÔÖ~/:ú9´ý~Û ¬/%¯®.®<Ç;Aãu¦)„U¾:¹çÜàE ³H aM¢O Vª°g>ûG—º’NV{g)ÿb/‡¢Z‰¼YÁ!”¼µÒî#RÂ?Þ3 ‡Ã×Å''ûPi­÷*›îè2Ñå—ØC+0%,¡%,°_ôöù¢À?%Ùr¹Ñ·æEGÛ,Iº¸a})á¼×K%)áxÁ÷yÅõYš\˜æþ(%„5‘–¹ÃŸC_ÇLfìþëá9÷ó+^=Ó©üÛŒ2­r¨9ºlєӎúÈ ¡ä­U¼/)á·q§†…Èi?Û(­õ^eÓ-䮓ªŠ­Ì¦„%´„ö‹Þ®×kÃıÏÿÚ<§¬yÑÑÏq¹\}y­1%Œ^/“݃êL ì ¾Ï+î¨gŸ )!E‘ÂêES®OÓëë™Ï¾ÃÑqž6祪”0úÆ¥>³$šË¿œx&®áÑró ºÜVp%o­¨â}I [HÍ2›lª´Ö{•M÷窌ý;xõ¤„Ñ…ß›_ÚUBKX`¿è-ºzÞOñgãm²ì†EG?çIu]·PJ¸èºº¸òœñewßçwÔ<5RBf$%„Õ‹öÉgœò’&†8ZŽrT•FfÙs¢DCù·™c»Ê¡æh9ÿ›é¦y²‚C(ykEïKJØÎÍz¶*3¶Þ«lºÇxÍS=)atî[ó¨u -aý¢Ÿ¹ÞOaf,‘ZVô3ŽÌ8üèyìóÊ<)aûë¥ü¤Àê1}g`ð}^qG}5C4TNJ«]AhÆ9 /)aâì-G9ªJ ÇxT¥gÌR ¹¢»1˳갧fY‡PòÖÊ)Þ—”pü†n¼Ó4Wë]OJ¸šÚØ{9ÿº_ -aiý¢©Wv]nô-µèèçÖ2¦§E¶Ï뤄Ÿ¢ÓKÏ -¶)°zLuOvYAG=ϼKò@Ëû£”Öd¼_¯` j®ŽŽMµùeõ«¾”0ú ÕsÁ¢hù·ŒÎך¦Þ]2ýO|³ d‡PòÖÊ)Þ×BRžSºVp·-ªõ–.®‡6êžD¨6V -aiý¢æ'ŽPÔ˾Eýœ€“±Êîà«õJ Û×ÕiÖTìS&VéKcŒŽÍZ;êyúO߀ îRBX“Ô/9çZ)îU}J˜›jùäU[J˜z¡Föƒjªü[þ¶y­)ajíµ–áu —Û ¡ä­•S¼^†ÅõÌÛ¼Õz[qtq=´Q÷$úÎîæ—–Ó–Ö/j>kŸ{Û¾¬¢‹Ž~ž»Œ'šhès¥„êê÷Ç>eR`õ˜¾4Æèجµ£¾ŽñBµWú4ë½,ëavšŽ>{¶ÿÁvm)aêA5û§ÈÑòoÿËöµ¦„©rîùóéßî÷ûרž²‚C(yk…o—a´UÉ^6*”ó #¨ÑEƦ™ÄQ`ë½Ê¦;Z }®ÇÔþÊúŠ©µF[–^ -aQý¢æM}îjûÅQ¦|¶v£¢oË;äçó™ªQ“Uø’¬¢ûŠºgòõ2éS&…Ty;ÃvlÖÝQÏ L [PZ·MJ+]ßc– å?ÌŽ½Ã©GÅö¦„©qËŒõµRåßþátÅ)aªœû?¼?ŸÏŸ!—Q d‡PòÖ )Þ/ÃèÛ¸²WöþißzN ‹ŽsN|ë/§õ^eÓ-“¼¨(T¶ÔZpkJ CSÓp˜-‹®„–°¨~Ñ×f§OÃ]û±›–¢ïʄϧR)as•ȵ[^&}ʤê1og`؎ͺ;ê¢ÅÛu‘g˜àùKJ+ó¹bÏ¿Y_M¸Ž”0ã‘-5Ó顠”ð•þéi§±TùwºVœ¾Ò‹°áÑ»ç3û4Oî+8„’·VBñx¦F½ò†¼þ4qyÃѹfñ ¥Ö{•Mwj(»SÅû¼W™†ëèëa¶¿:Jh Ëéµiy²C½è#Lÿ¥QRËýµŸãÖ0GLJø§ Ruu»ÝvQ·ß[©L ©óv†íج»£>TרÿTMüùKJ+“z@èùÓÄE­ ÒT†Çœ–ÏJ·Û­áçО‘ëL C6,6õõǽá Ϥ†Í×6¯îiÛ„2ŽŒ] +8„’·VBñx6KF*—º×´œ|îF…´ÞkmºSÃïmÆZÛäƒ M áÝþs>ŸÇã×ùh]‡¾Kh Ëéu^èºå¯‹Žf/\™ q¾FWa—¢ó€¤„uµå¥ÎÈŸ|pÔ”°ê1og`؎ͺ;êC•Àô¿ã€öÝx)!¬ûbékëkI¾îÛ~¿…U~¯ïAªðïÍÃS]]gJøúö{òPÈïSðçáñkùw½ Ö~ÌùùIsx„ÿSÚ¡ò¿ ¼ùçÓÈ ¡ä­Í^¼e^† ãíáxC;ÿûîð.ŠÓéô^ü°ý'´fÇãñ³lß·›ðïÍe;×OƒJh½×Út7¬Þ¶ÙlBmùS÷~z& o–œŽ$cvL -a!ý¢?ªVÞņ½Í{)a›3®ðßqÌØΊSÂWb}ÅæÆêϲë1ö,“ªÇì;6ëî¨Õ)2`@Á”Ö'Ú—îóp=}òʦ2c`ªÚ”ð•þeo¶Œ7ø¬>%|5;Op!R +8„’·6oñ–y¶òjyÈåÜnÆî„LÙz¯¸énÈí*Ǭø)µÏ¥1{KXH¿è†ì2ï‚ Ì[ŸpðÒKeCKéÞ/ô.0jJXBõ˜½Å°c³îŽzWÑ[mÿ¶ñü¥œav©—׌1³@J8êÀTÍ)á°ƒ yc5¤„=G yr_Á!”¼µ‹·ØË°Ó$‚‰SÂy#ÂZï7Ý ¯ýêäx<¦ÞƵ֧ÔÝn׳á×…U¿^;mV£möN*¥„×Õ RÂÙ«G ¡:6ëî¨Ò å½b–õü%%„’¯÷¼ŸõÎÒ°¬¬©Ì~¨<%|}[bkì1óJRÂ÷cì€ST~†d§,BÉ[›«x‹½ ³Gÿlgħ̡§[ïu7Ýábì3šªÜOÚRIJØéÕWŶ„…ô‹>E±¼åF߯›åÚi~öê'÷”¶4à ²æËd2™·z”Ъc³îŽz'©Õw UPfj %„Uš¬SZCJøRË{®éùÛu)áû±=û¡rÆò_âØÎõzí?Lñ~ãLËb¼@–~%om–â-ù2|>ŸMS´T³ÀÿgfÐï'•`®Ö{õMw8Ñyuæt:ý®$«O C)‘›ÏÒÒ/ú2Öçw‰Ñ´k–:l»ýçíRÂöBeë9©0|ýë4ÒyoëCU:CulÖý¬ÑóJãgÛPÈó—”Ê|(ûÇã6,E5•á‘ígX¦ý£nxÈê¹8ÒKJøK(ÌNÏÎáÃó–ÿrÇvB¹e 鼨»®06R,÷ O §/Þò/ÃpWí4ÞÕ046•1BØ'k˜¬I™¸õ®¤én?Ö>v>Ÿ?ÇW–îþ.‡p°ý;¥µ„…ô‹¢màçßês€ÑG˜ž/%Ìn·ÿëC]8õ¤„?ç4TûN÷ÊívmµŠº­Q=Jè Õ±Y÷³F^ÛøoœWÀÀ,Ï_RBXŠèóEŸ%€ø=4 w»ÝŸg¨ðHŠ=<Õêÿ$<;‡ÂOˆŸ…þç{x0| ´ 5 áCeOñŸ¥ý®íïOÁÅVø‚²÷û=uSȈ-~ìçò_ï¶.ü×ð™A²­÷:êÞgdöî,¨’h ]Ywæßå–*4E4F{õs±Domï+¥„êZBõ˜·30lǦžd4iýùí1ÌKJµZL3 r×ë5:3Á}öêr¹ µ'aSÎ2À›”€×·ˆp·ÛN§ÛíögéÎð­ëõz<›W(=y{5È,B3 >I hˆO§SË—úÝn·†W†ítÝ«ðw›ƒËóùþèŸ}¸\.Í‹¦zG!ÀKJP½çó ¸Ýnï÷{×­5Ìþû“è}•ÊÛìXø@øXôëûýÞITît:E“¸çó™·ÁÔÌÄÝn×~#·Û­ÿâ¥áÃä•ë#%¨ÙóùcYÎTÆ×~rbt"aÆ4Àèê£òJ€U’Ôìr¹|êÇþ[ŽÎãk¹åTȘ1½1ƒf,¦ °&RB€šE§Ú ’ E_PØr_4a¼\.y{]RµÓÊ¥ë#%¨Ùf³iP75‰¯Íw‡jî³'k%%¨Ù¨ƒºy.7Úsê_trbøC* ý—Th¨×ÿµßø×oE½^¯}ö$ºüiøC* ý—T(:¨Û3’{{<Ÿ[Þl6_¿¸Ûí.£‹Ž¶|I"@=í¿” Ûív¤ø,:}o¿ßýâç·ÂNŽt¤*P-)!@Í¢/ìä}Ñ-_.—æoÝï÷¼lñ«°‘Ï-‡?§u’Ô,:㯂]á³Ípñx/ãu‡Ë%%¨Üf³Iîf‡hÇãñskçóùë£YÞ )aøë#m`‰¤„•k˜N‡çóÙiƒ·Û-û݂є°ÿò§©½’Õ’ú÷'M{<m6}±`ûõKw»Ý”)aøsÎ>P')!Áápø:Ø>ÓØ].—ž¯8”LCJÀ[ôÍ}Ÿ6›MøäŸ©…Ïçs¿ß÷Œ_‰”°å4Æfa¥„?¤„ü¸Ýn›Í¦åØïv»}Ç…©)„»Ý®kÀ7ê ³l€æ&QJP³ÓéÔsXx³Ù\.—Œ?-%˜†”€OÇ£ý¤Â?òòÁ7)!À4¤„Àoÿ@«#z>Ÿ=çîv»Ûí&%*g¬ÒúˆRï%Üív§Ó)üß±³B)!€”k•ƒ¨çó9ú†Áßaßóù¼\.ûý¾å¡‡ð)!€”¬À¿ÿþo]s ‡Ãçèî~¿Oe|íãÂÍfs¿ß¥„RB`)á?bV™ÇÏ#=m¾ûŽ £ë”þv½^¥„RB@J°PëK ¯×ëçaî÷û®Û¹ÝnÍ/.l3£0úE)!€”˜,%TQ+K ŸÏgt\·ÓË»\. ÃÅ_7ÍÇ;ÒðçTi NRBೋ+%h°²”ðt:}ê^.—>Û|>Ÿ©I…_W1~±ýk Ün7)!À)!ðÙÅ•4XSJ^·ÙlÙx*(lžNýÖívë¿?RB€ß¤„ÀgWJÐ`M)atuÐÓé4ÈÆSk™žÏç†oÇ)SÂðçTi NRBೋ+%h°¦”ðp8ŒɽE#¿ý~ßð•è ¨×ëµÿΜÏçñ"Q€Å‘Ÿ]\)!@ƒ5¥„Ûí¶ëŠ D§ï…?Úð•ëõ:R–7^þ°DRBೋ+%h°¦”pìÝÔ¢£ _¹ß?ýwf¿ßn9ü9U¨“”øìâJ H ÇþŸŸßívý÷d³Ù¾hnŸ¥„PyWJÐ`õ)áãñûO4e·Û >ÎÕ8Hø°¦[€”*ïâJ ¬>%ðU}Ñln³Ù4ëx<¾WÑ׆?¤>Õ’Ÿ]\)!@ƒ5¥„ÑY{ƒ¼ð-šÍ}ÁýVϽ _5X)!ðÙÅ•4XSJµ<ŸÏA¶ÍæN§Ó×/»WÑ)Æ®ÊI Ï.®” ÁšRÂ謽¡¦>èÆï÷û×ïFãÅËå’·'§ÓiÔ)“K$%>»¸RB€kJ _éQâìHîÇ~ÿÿìÝÛuêH¸¨ÑHHHÁ}ÞHA)1)(Ÿí½}|,QÖ]u™sðÒk™Ë”ü»¾œ»7{<‡\÷ñx,u:á» Ã]x15S DV {ϳ› {Ou›‡Ã¡{õóù<ö‘ôÆÊpã^É@åTB ;⪄…UÂw§Ú}¹^¯n°7̧Óiøí,òn¨ïbe¸q¯d r*!ÐqUB€ˆÂ*áçû÷}ÚÝÀÛ¶œ™¼^¯Qìt:½«ÞTø‚ÈÕ½ŒTB ;⪄åUÂÏèûŽþ<‰¯iš_ç÷z½ÂŸÜn·wç~{>ŸcU¸ñÈ †{ì>ž!fl¬(’JtG\• ¢ÈJøùþÍ91!~išfÙGâ½F¾¨„@wÄU "J­„ŸÃÎ(ëp8LN„_ …ß: *!ÐqUB€ˆ‚+áçïØy8–Ú>ŸÏmÛÎT‹„Bgü¤ÝW%ˆ(»mÛÎ?©ðt:ýúÄÀ™^¯W¸ÍÉÆgü¢ÝW%ˆ(¾~iÛ¶išãñ8vCør¹Ì|‹ÑˆÇãq>ŸG̸l¬(†JtG\• ¢’Jø­mÛûýþññqúOï™z—Ë%|Áf=î«`^¯×p׿Þ5ügøÃðWá y³S€R©„@wÄU "j«„I%º#®J¡P•莸*!@„J@TB ;⪄*!P D¨„@%º#®J¡P•莸*!@„J@TB ;⪄*!P D¨„@%º#®J¡P•莸*!@„J@TB ;⪄*!P D¨„@%º#®J¡P•莸*!@„J@TB ;⪄*!P D¨„@%º#®J¡P•莸*!@„J@TB ;⪄*!P D¨„@%º#®J¡P•莸*!@„Jc­”T Xc©„Póˆ«D¨„0Özí@•€e—J•¸*!@„J£¬Ú„ X|ù¨„Póˆ«D¨„0ÊÚá@˜€׎J•¸*!@„J£lP „ Xdᨄ`ÄU "TBn›j MÀR G%„ÊG\• B%„á6KÚÌ_5*!qUB€•†S !£å©‚W%ˆP a -“<‹¬•*qUB€•Ú¸È0sɨ„`ÄU "TBH%„¼V¨JF\• B%„TBÈk…ª„`ÄU "TBH%„¼V¨JF\• B%„TBÈk…ª„`ÄU "TBH%„¼V¨JF\• B%„TBÈk…ª„`ÄU "TBH%„¼V¨JF\• B%„¶Lò,²jTB¨|ÄU "TBnÇJèɇ±«F%#®J¡Âp*!d´âª„*!L¦G@=ëѪ„G\• B%„9Ä(~1Z˜ïˆ«D¨„0“ E®DË qUB€•!@@kÐ"…ÂF\• B%„IÝê³T¡àW%ˆP a%r$µâTB¨pÄU "TB  ÝW%ˆP (€JtG\• B% *!ÐqUB€•€¨„@wÄU "TB  ÝW%ˆP (€JtG\• B% *!ÐqUB€•€¨„@wÄU "TBX‰ÜI­8•*qUB€•$=@v«ÏR…‚G\• B%„EPÀ´H¡°W%ˆP a&Š\‰–'0⪄*!Ì¡D@Ù‹ÑÚ„¬G\• B%„iô¨mIZ•݈«D¨„0Á?[ñTCR«Òó y¸*!@„J,R$ ØxUª„PÛˆ«D¨„0ÖâA•€µ¦Juޏ*!@„J£¬—„ Xi©„P툫D¨„0ʪá@˜€Å—J5¸*!@„JÃm´ Xví¨„Póˆ«D¨„0Ü6Õ@›€¥ŽJ•¸*!@„JÃm– ´ ˜¿jTB0⪄*! ´e2'`‘U£Bå#®J¡Â@÷yf.•Œ¸*!@„J©„× U Áˆ«D¨„0Jy­P•Œ¸*!@„J©„× U Áˆ«D¨„0Jy­P•Œ¸*!@„J©„× U Áˆ«D¨„0Jy­P•Œ¸*!@„J©„× U Áˆ«D¨„»kÛ¶išëõz:‡ÃÏíèðŸáÃ_…/_æ¹Ú×–É@ž€EVJ•¸*!@Dy•ðŸmÍy¨Çã|>¿¯ðÅá*^´I½ºr¿#(xyª„`ÄU "TÂ]vŒ_¯×étšváŠáê^ºé¼º2½¨ayª„Pùˆ«D¨„Ûïßï÷ù÷nÄ«7‘W×6/`O>¤pð÷Ïl½'9†»ó]ê¤ÝW%ˆP ßYäDÂõ>@p;È—JtG\• B%|gþ‰„ŸoZÞ"•ðv»­tËDÝòT ¡æW%ˆP {õž8öDÂÏ7•ðñxÌ„½§:ª„k »å©BÍ#®J¡ö:óO$ü|sBâz•0ÜïݪTÈkyª„Pùˆ«D¨„]MÓ,r"á§JXÕòZž*!T>⪄*aW×ëuÂMõV µ±«m[•p{ªäµâª„*á·ÅO$üT Kä9‡\Ö¦JFÜïA€®56OrÜJZãDB•°žái•P òâ\Â/—Ë¥÷ù™s"¡JXOŒðœ€J¨„yQ ƒ×ëÕûäÌ<‘P%T •P Ò¤~®v"¡J(*!°{%ôlôR ×;‘08N+m2÷~b¸;/émÈŪT ¡òW%ˆP Ïçswøp8,r㽕ðù|οåÇã¡îK>€Ä—¤JF\• ¢òJØÛÚ‚¦i¹ýÞJît¥G®nLA€”×£JF\• ¢òJØ[ñ–:‘0¸^¯[VÂpw^Ò ÙŨ‚W%ˆ¨¹®}"aðññѽýûý>ÿ–o·ÛJŸ¥HÄ?ûñäC ËÓó y¸*!@DÍ•pí ƒûý¾RË[¯?¡BåËÓó y¸*!@Dµ•°·ß-{"að|>»wq¹\æßòù|îÞr¸;/éU©„Pùòôª×ëu:¦íQ‡+ú,B€_TB ;⪄ÅWÂw1.…Ðöx<ÞçØ+|ñƤÈdr$¸èTB¨mÄU "jxÇÑĵmÛ4Íõz=N‡ÃáçvtøÏð‡á¯Â„/ó\%Nz€L ¥ ¥Ž¸*!Òï,ÿçûâÙ¡Âì9_€€¢V¢E ÅŒ¸*a†sÛ§”ÿ3ÎO: *!Ì›óe(v1Zžûˆ«æv$·} {®;Ïlù3κ¡ÂÔ!_‰€Z–¤µ ™Ž¸*aV‡qÛ§°ÿºóœÀ–?ã¬; *!LòŨnUZ˜Ýˆ«æs ·} ©¬;Ï lù3κv§Âø!ÿ=ê\˜žgÈkÄU 39€Û>…´Öç¶ügÝûR aä¿XJ$`ûµ©BU#®J˜ÃÑÛö)¤¸î…¼×ç“LXd}ñ’¶ÙBQ aÀo+›öyf.•Œ¸*aJGi;½¶µ)aÝ9츸8¢En¡¨„0à×L•rZ¡*!qUÂdÑöx]\\\\\äB Ý-•lp©„Ó U Áˆ«¦q|¶¯ëâââââ¢Io¡¨„0`K%„œV¨JF\•0ã³]\éHz E%„{\*!ä´BUB0⪄iŸmغ¸¸¸¸¸è†@Ò[(*! ÛæR !å©‚W%Læøl{ÖÅÅÅÅÅE:’ÞBQ aØ6×vÉ@ž€™KF%#®J˜Ì!Úf¬‹‹‹‹‹‹n¤»…¢°=.•²Yž*!qU”ŽÒv_]\\\\&^þÍtd½…¢Âà=®}*¡gF-•Œ¸*aRVÚ>ýã..8ê†Àì-•Ú¦h0Õ¨„`ÄU ±Òö©íhØ>XxVA(Ô  È-•†Û h0Õ¨„`ÄU S°Òö©hØ>Xx>a›uWF4@^[(*!Œ²q%ô„ÃØU£‚W%LÄfÛ§ÀªeÁ3 Û¬»‚O]4@Ê[(*!Ìì¹Ü8Ô³*UB0⪄)Øwûæ!£u—Îÿ @m[(*!ÌLYÜ2Ô³$UB0⪄‰Hmû ëÒ\w)ÿ¯º!ÃvçÏ3ÞBQ ! !d@²ËÓó y¸*a"Òß>Áºƒ}×]Fÿç€nÀ›­9ð¼·PTBH'Cìòôû¡Ãuv[(*! ;Ü©„Põòôõ|Â+κƒ-×]%ï],ÌѸ˜-•†ôTB¨zyzž!¯W%LÐRÛ§žIXcÅYw°åºµÖ¤CÝ A¿%m¡¨„0츧BÕËÓó y¸*ašVÚ>f®8ë¶\w‹¬5ÝP7Ø‘ãma[(*! ;ô©„PõòôǨ¬·PTB2ŸÄTBà÷ˆ«”Ç)‡¶åoh,örq,*~ E% óÉM%~¸*!@%tC{õ]%‚A/ ‡TBŠóTBà÷ˆ«TK7¬y_¨‡Ê–õ%½†ÓÙBQ È|,T ß#®JÀ7oUZI7tâRþ;<òY* oÕ#Õ÷ÍzÍ'²…¢ù ©¿G\•€8ݰ°nè“ܱQÜ­~;š~ݬe’ŠJ@æ3§JüqUBÆrÊa¾éÐG¡¥ö<»dþ¶?uoÓ²Ja E% óU%~¸*!‹Ð Óï†5|Þb:ÏžKIÕoã#Oï Zƒ)l¡¨„d>Ъ„ÀïW%`%ºaRé°ÈXÜþ q©9üms¨ywk~¦¤°…¢ù¬¿G\•€-I‡»tÃ2>QqÕ¦‹ê—ÂE"L| E% k*!ÐqUBö¥®süüÄ¥¹‹ð—Ñ‘D"L E% k*!ÐqUBRSêgíÒ SûÀD¹Mõ %Â|·PTB²¦ÝW% ºašç*ÊmÂ_͆©$Â2¶PTB²¦ÝW% Sº¡‹êÇÚÕoÁC“DXÀŠJ@ÖTB ;⪄”D:tQýؽµ ?H„ym¡¨„dM%º#®J@ÙtCáOõKçÈ#f½…¢5•莸*!µI¹€ènªŸðWù¡Æ!:å-•€¬©„@wÄU àÓ)‡Âª_ÏXâ[(*!YS @/§ª~¨W[IUB@G€òÖ©J5Œ¸*!TB"˜F%„(l…ZÔPüˆ«@=$B€ TBKY€2¦µ Ÿ*!TE"K%„iôÈ}IZÅPüˆ«@m$B€QTB˜Ih€LW¢Å Ÿ*!TH"N%„¥È ÑT ¡†W%€:I„©„_^¯WÓ4§ÿtw†Çcøóð÷û}Ù»nÛ6Üõõz ·8~ÞiøÏð‡á¯Â„/órÍ‚î¹,=«ŠqUB¨–D0Då•ðù|^¯×_mn³]âÇãq>Ÿ‡ßoøâp/Ú\¨òŠS ¡†W%ˆ¨¶6Ms<÷Ú%~½^½g,®®î¥› ’]k–'?⪄VÂÇã1öäÁew‰ï÷ûüêÅßø”µ‰ÔS ¡†W%ˆ¨ª¶m{¹\öÝ%nšf©ÍêpS^ÀÙQ% Åe=Bñ#®JQO%|>Ÿ“ßbt©]âEÎ"tFaä Ø}MY†Püˆ«DTR ŸÏçŸ{¿çóùv»=_Ÿúþ$üyøÛ™»ÄáfãŸ6øuï¿îºišŸwÝå3 s'RÀ.KI%„F\• ¢†JO„§ÓiàymÛ6Ms8¦=ŒpG½àx<†Gøç?á݉çóÙ˸jl¿ˆ¬;(~ÄU "Н„‘3ø‡Ã¯s÷Öî¨÷1\.—á7òîC7ûW°Í¶Y;*!Ô0⪄ÅWÂw§à].—¶m7{½'N8 °÷ÝGÃ{%F¼€ V…Ÿ*!@DÙ•ðz½önóÞn·-Æ» 'dÊp•Þ›úó=KÉ”x‹/•êqUB€ˆ‚+á»6÷ññ±ñ#é}§Ð¦i¦ÝZxü3ß¹”숰È2Q ¡¶W%ˆ(¸.õ&Ÿó-»ÕüîtB/æˆ0au¨„P툫D”Z ›¦éîî‡-?‹ðKï)3Oýë=91Ü‘×s=Ä øsQ¨„`ÄU "J­„Çã1‘ŽÖû¡÷û}Îm†«§ðNªìN¼€Þå *!ÀŸŠ¬„½§ïN§]LïŸÎ<¥±÷MG÷ú’ñTB ;⪄EV¤޳ûHŽÇãü›í=YÒ먖JtG\• ¢ÈJØû‰„»<’çóÙ}0çóyþ-‡éÞr¸;/i N*!ÐqUB€ˆò*aïgöÝn·tÌ" ¸ÆÇäK%º#®JQ^%¼^¯éœd×Ûò©„·Ûm¥[È‘JtG\• ¢¼J˜ÔöõVÂE>!1܈JðM%º#®JQX%lÛvÈç†/»ßï×ëõt:u«âáp~¹\n·ÛÌ“ÃílY ÃÝyIuR DV ÿ<îišóùaðr¹4Móx<~ž¶mÆ¿úóMJà yëQ•`*!ÐqUB€ˆÂ*a<^¯×ç~iš&Ò Ã­M{<«þc½¤:©„@wÄýtèZcó$µ]âÃáÐ4Í´lÛö|>¿û—þÙW}~R{òTB@%ÈE çŽ:°×åré½åðç*!€J¨„Ù©¡.rËï>ñP%P • ;*á@Ç£÷Æï÷»J ÉVBÏ@/•p¸ÞÓ ?>>"W9N+=ž¶m»·îÎK¨“JtG\• B%îv» s½•ðù|Î0½ç6ª„@µTB ;⪄…UÂÃá°Þ¦no˜;‘«ôVÂp;+=•¨–JtG\• ¢°JØ[åÚ¶]äÆ{ßä3¾i|½^·¬„á:©„@wÄU "j¨„‹T¹/c7?>>º_¿ßç?’Þ·?H"@ÁTB ;⪄…UÂÞ*·c%¼ßï+µ¼õú#@ŽTB ;⪄…U¦iV=Ãnì¦ñóùì~ýår™ÿHÎçs÷–ÃÝyIuR DV ׫rŸo>—ðp8įսÊétšÿ`ÂýÚ¾ˆlUB0⪄…UÂϾâ?CÞ@ÇcBòëý¨Ä™¤·W.Š9ø«„`ÄU "Ê«„ë½çívëÞòõz_+|Áâ Øûq‡>€‚©„@wÄU "Ê«„½-o½lš&~­Þ¢7óñ„«/^n¥| JÀ+H%„šG\• ¢¼JØûnœÁëõZãfßÿyÝÉWõH¼˜7³Þóï; Ë.•*qUB€ˆò*á盓þÂιÍÉ·Ù{êߟ'!Žz$‹œ,É«¶a_>*!Ô<⪄EVÂÇãÑ»»;9Ì=ŸÏÞ w4çñL8ð݉„ ó­„ Xpí¨„Pùˆ«DY ƒÓé´T(lÛöp8to*ÜÅðé½… §7öž&nÜ+y3TaY8*!qUB€ˆR+ỳÿ‚Ûí6êvŽÇcïí„¿~;÷û½÷FF½Shï;—áÆ½’·±M5Ð&`©…£Bå#®JQj%ü|óù}ß§þùmÛFn!üÕØÇóîôÆðç¯×+~Ýð‘«{of³d MÀüU£‚W%ˆ(¸ïNüþöv»=ŸŸþ³išwgíM8ðÛëõŠÜæù|÷û«]†ÿ °÷]F¿ýYYJ-O•Œ¸*!@DÙ•°mÛx(œ`Z"üÒ4ͲÆ{niËd OÀ"«F%„ÊG\• ¢ìJø¹t(œ“¿, ÃMyoiã^ OÀÌ%£‚W%ˆ(¾~þ ¯×k:Un‘Pè,Âí©„× U Áˆ«¦q€^ÿLRC%üòx<‡ÃäS—ýø¿pk§ÓiÚƒ WôY„»P !¯ª‚W%Lã­@¢ê©„_š¦•çïƒ?=óù<üÁ„/Wñ¢Ý‹Jy­P•Œ¸*ah•U[%üÒ¶mÓ4×ëõt:ý:Áðx<†?üøøØì-=#&ügøÃðWá —y¹îK%„¼V¨JF\•0´J‰ª³Â*!äµBUB0⪄i UBH”J©„× U Áˆ«¦q€V  Q*! ¤B^+T%#®J˜ÆZ%€D©„0ЖÉ@ž€EVJ•¸*ah•¥Âp;VBO>Œ]5*!qUÂ4Ð*!$J%„áTBÈhyª„`ÄU Ó8@«„(•†Û¦h°ÔÂQ ¡òW%Lã­@¢TBeíp MÀ²kG%„šG\•0´J‰R a”UÛ0‹/•jqUÂ4Ð*!$J%„±Vʬ´‚TB¨vÄU Ó8@«„(•ÆZ<"¨°öÂT ¡ÎW%Lã­@¢TB˜`‘š IÀÆ«R%„ÚF\•0´J‰R aI,I ñW%Lã­@¢TB˜L€zÖ£U 9ޏ*aè­.ÀH*!Ì!F@ñ‹Ñ„|G\•0ô`•f’! È•hyB#®J˜Æz× ðžJ‹  €5h‘Ba#®J˜Æ: С‚¤ÈnõYªPðˆ«¦q€NïüG%„•È ÔŠS ¡ÂW%Lãðê¦P•莸*aèL.P•€¨„@wÄU Ó8@çv€j¨„@%º#®J˜Æ:Û ”N% *!ÐqUÂ4ÐE\ D*!P ¦q€^ýòïäBK% *!ÐqUÂ4ÐUÂoN0€TB  ÝW%Lã½u%Ü¡@žTB  ÝW%Lã½g%”  B%„eé ÚÒS ¡ÚW%LãD%T  K%„EÌï ª¬½UB¨mÄU Ó8@'W åBø¢ÂËöU¶YŒ*!T2⪄)ø7åB?FHŒJ“-Þ„ Øl=ª„PÈ«¦àßÜÈ…ÔC%„iVê Âl¶$UB(~ÄU S°E×[çŽäBЧ«öa6[•*!”=⪄)ج®zŠ!ER a¬µû‚6[.L• qUÂl_ W½w¹€’¨„0ʨˆ B:kS%„ G\•0ûVÂõ†\@TBeT>˜Ü´ Xpmª„P툫¦ ‘J¸êCR È”Jà *!ì¾€„¶PTBòþ}[%:#®J˜ø‘ÛÅÅÅE^¤î|©l¡¨„äý+·JtF\•0åö‹‹‹‹¶Hõ?ûRÙBQ ÈûWn•茸*aʇmm?øâª„ ­Õ:P µE—Ê™gÈ` E%„í~CP"`§•¥Bm#®J˜àÑZ%„Ü#šgÆE^\çÀž[(*!Œ˜òg´1¶\˜*!T>⪄©ªÕ P =Ÿ.•µE•Èc E%„¡#þ&‰P€EÖ¦J•¸*aj‡jµÊHZžamQ[\çÀn[(*! ò§†I¶_›*!T>⪄I§Õ P ‘]TB Í-•†îpmu"¡*3צJF\•0©ã´Z}1(§åféi‹.B!PêŠJƒ~ßÞöDBaf.O•*qUÂtÒj…þ5Áò©–=þÝþ^öò¢‹Œdº…¢ ߷W¨„C¾Ì3sV™JÕŽ¸*a:é’*¡oçöVù>–¼ŸÙ¿M¤Ó]dD`÷-•ý¾½èÛ.òÅ@dᨄPùˆ«&r„¶)Ê‚äB‹n¥WÚ¢’Ô¼…¢ ߷=‘pñ;ËS%~ޏ*a"Ghž¬A.´èyå /ʈ@[(*!üýËö¢o7:ö*ž˜°ÐTB¨|ÄU 9BÛÏdmŠáÏIåoº+Òi‹.~ì@Ù[(*!ü½ÃµÉÛμ/°BUBàS%Lìm»’ÍÈ…+=Bašß´E%Xj E%„¿w¸6¬„ŸB!Ì^¡*!qUÂDŽÐ¶"Ù^®¹ðŸLÖ¹½€‘eD ¾…¢Âß¿m/W W½;°BUB@%Lsš(éå—úöùúÍÒ«íH‹J(#@ñ[(*!üýÛöVJ8çîÀ U O•01ÞüR_Љn“«„$v”V ]”DH ¥ÈJ˜ËŽqÛ¶MÓ\¯×Óét8~ÞxøÏð‡á¯Â„/órMí5ùu¸ÞݪŸ*abTBjxY&´®Rı]" QRF€Í¶PTÂ]vŒÇù|~_á‹ÃU¼hÓyEM~®ww`…ª„À§J˜’­öf!•—èþÛÞ*!ÕÿP¨¤:½QF€·PTÂwŒ_¯×étšváŠáê^º)¼¢&¿×»;°BUBàS%L‰JHµ¯ÕÝv¸UB¬Ð‚ÞkÔ»¤*‰°ÍŠJ¸åŽñý~Ÿ¿áF¼zwEM{®ww`…ª„ÀÏW%LJˆ×íÖ'Ú/‡me] eD‡Åâ· |›n¡¨„›í7M³Ô]‡›òÞ÷5íE¸Þݪ?G\•0*!^Àûœ`h;¶Zøk/¹:ßmUId‘}߀á[(*á6;Æ‹œEèŒÂ¤^Q‹_%r]ßÈë˜ì>⪄)P ñJ.°ÿÿï"•WBѱ5Ÿµ ÀÛ-”z*áŽçõzÅ?mðv»=ŸW ÿÙ4Íù|Ž\Ñgîû¢ZéE(OÀ"Ç|•*qUB` r!ðó‘ .ÿ–N"tðÝpmп…¢nàt:õ>¤ãñø|>ã× _¾¬÷êçóÙËxÇÕJ¯@y9æ«„Pùˆ««ª7?~Q •Dè¬Ö&ý[(*áÚGïã¹\.Ão$|qïü:‘^T3ƒ6‹,O•*qUB`3uCàÇ/"{­:ÝPFtŸº0èÙBQ ×Ö{"á„Ó{ß}4ܸWò¾¯«_/­ù‰A›€¥Ö¦J5¸*!°½*r!ðã‘”Wt(#Özˆ÷c `ôŠJ¸ªw'¶m;ö¦ÂUzoêÏ÷,eÕ×Õ²}A›€×¦J¥Ž¸*!°£U¶¸m!Cz¿ˆd½ê¤C%±Ä~’LÙBQ WÕûN¡MÓL»µ™ï\Ê/-•2]˜*!<⪄@"JË…ÀÊküG´*öŸ&#ʈëmøI0a E%Ì葼;Ћy÷ï©Dy­J•ŠqUB 5…äB`ÍuZ(ÜýPeÄQ{~˜LÛBQ ×Óûv£3Oýë=91Ü‘×ó¾¯.•òZ•*!?⪄@Ê2.†Àšk9»J¸ûS$#*‰?÷ü0˜¶…¢®§÷ Bï÷ûœÛ WïÞf¸#¯çÝ_`k$Ba_’*!T2⪄@òk…À:ë·àP˜ÂÓ(#Vý<˜¾…¢®çt:uFÛ¶sn³÷MGÃy='ò2›ü’S%`½Å¨B…#®Jä%§3 å–­J˜ÎS­$:] Ú-•pˇq<çßl¸Û×…½ØT X|ª„Pùˆ«ùÊæ“ ÁRU kú^ȈB!@©[(*áJžÏg÷1œÏçù·n¤{Ëá:©„@wÄU ¤^ í³b…ª„¾Y2¢˜ÿŠJ¸’õ>@p;(옯Bå#®J&éJh«R%ô UÅD€l·PT•ô¶¼E*áív[é–Š9æ«„Pùˆ«¥J·Ú(j=üʈ.~FYo¡¨„+é­„Çcþ-‡Q âÇ|•*qUB xéVB[¡€#32¢Ÿ @>[(õTÂÓÞmÿ~ýíÇÇGÓ4‹|Æ_ï}­W ÃÝyIuR UI´Úéè;\£$ú $µ…RO%ër¹ÌÉ…*!@FÇ|• qUB Ê©È^&@ÞtCÑO_`û-•0ît:MK{½•ðõzÍÿ×µm«¬qÌW ¡˜W%ªœŠìV”O:ýx–ÝBQ ‡¸^¯mÛÎ «þ½¤J÷ÃTB 3⪄@•S‘ýH>%B?¼Q[(*á@ÇãqT(T ò=æ;ÆBî#®JT9Ùqào*ᯋòèG;T¾…¢® UB€¬ùޱõˆ«UNE«_þgÕ†"@éj;‘ÐiŒb"‹N¢d¶…Rj%⪄;’ HðçKÖ¡p÷'GLt1м…¢1aÇøù|v¯r¹\æ?˜óùܽåpw¾M€C´J¨„‰Ð Hê'KÙ•0‘çPLt1‡å¸…¢þ©·÷Ç?¯Ø½Öétšÿx‡ƒík€ÈÁV%#®J¹€Ýš… >Ûb¢JH"[(*áŸzß;tHï _³ø>sÛ¶+ÅG€L©„@wÄU $°×•0ßoŠ’(²öŠJø§ëõ:í{¯8ó{“e¸#ß& Z*!ÐqUB€di…lÿãC%,þ{'&ª„LÞBQ ÿÔûŸÇãÏ+ö½™M®¾xyÈšJtG\• }r!›ýÔP }ÅD¡w[(*aÜãñ˜³]Ü{ݶm§=˜Þ·µw TN%º#®J¹€µX¨„^b¢JÈ»-•0îx<Î9°÷Ô¿¦i¦=˜ÅONÈJtG\• ;Z!ÂP!& …µm¡V ç4¸®Þ*¼^¯·ðîTÄ §¾;‘pÈ{ŸL%º#®J/¹HäÍUÅl¡”W ƒÓé4¿5MÓ»Q<öܽÞ5<ŸÏcO¸J÷vÂ{%•S äN+ ˜I$BCUú[(EVÂïV¸ì{{Ž=‘ðËý~Ÿ_{ß¹47î• TN%º#®JP ¹(~bQ Ùq ¥àJøíz½>ŸÏ·p¿ß{?‹ðËív›ð¨N§Sï­…?ÿ³9†/ˆ\ÝËxÎKÅ“ùUB¨|ÄU Ê#53e}d! m¡ÔP ¾Ïçív{<¿¢aø“¦i.—KﻃN~¯Ño¯×+þ¨Â½ÿz‹ÔðŸá¡ö¾Ëèä³y÷Rñl@ÁG~•êqUB€Ri…€iG%dÁ-”ª*áÇã±mÛÉìݧNæ½F©xNÀ‘_%„bF\• xr!`Ê3šœÒÚBQ ‡˜|áO †ÂÉŸ·È—Š'ù ÷W%¨‡\°Ö¤¤M%hÚgöZ$:‹p³—Šg ª=ò; @î#®JP­`ÙQjÍDhZJB‘•°mÛ¦iŽÇãR§.þÙáO§Ó´Ç®è³—" @…ËY%„zF\• Zr!ÀBTÂÂY ¿½^¯¦iÎçó„­àÃáp½^WíqÇcÔc _®âE»äN#€Š—³#?⪄•Ó æùg“ {*»þôz½î÷ûÇÇÇù|>N‡Ã¡›ß_.—Ûíö|>7{`_g>^¯×î£úzHá¯Â„/ór]ëH§@• ÙÚ‡âG\•€/r!À$*aùê©„ð÷!O2€šÖ¯%Ÿ*!¿È…c¨„åS ¡çØ'@éËV%„F\•€^Z!À0*aùTBx{T èkCñ#®J@œ\¥–O%„¿…:·NUB¨aÄU H.XiRŠ Q$@%„(l…ZÔPüˆ«0ŠV°ì€$¦O%„±”(caZËPüˆ«0\°È\¤¦O%„iôÈ}IZÅPüˆ«0‡V‡6àyÞ—J3 éJ´x¡øW%`r!` R K¥ÂRäÈhª„PÈ«°,¹0ü¨„…Q aYºä²ô¬V(~ÄU XƒV{TÂb¨„°õR^q*!Ô0⪄¬J.L;*aîTBX• É®5ËŠqUB¶!)•¶!F@RKL%„F\•€-i…dG%„-©ÎⲡøW%`r!¹P aò쾦,C(~ÄU Ø‘V@úTBØ—H»,%•jqUBR ,•R VÀö‹ÈºƒâG\•€¤È…¤F%„¤h°ÍÚQ ¡†W% AZ!éP !Aâl°j,4(~ÄU H™\ÀîTBH™x‹/•êqUB² °•Ò'^À"ËD%„ÚF\•€Œh…lO%„Œˆ0au¨„P툫#¹€Í¨„#ñþ\*!qUBò¥°•ò%^@ïrP •€bÈ…¬G%„ˆ ÝW% $r!‹S (€JtG\•€òh…,H% *!ÐqUB &0ŸJ@TB ;⪄Ô@.ø¿ìÝÝqâÊÚ€Ñ HA1)—sI JˆHA)ø¨Î|åÏÇB?Ý’º{­š‹½=F02_ú)€ÙTB2 ÝW% Z!3¨„d@%º#®J@äBÆS È€JtG\•€bi…Œ¡•莸*!È… P È€JtG\•¾É…t©„d@%º#®J¿h…ü¤•莸*!¼#ð¥•莸*!|$”L% *!ÐqUBI+(“J@TB ;⪄0•\P•€ ¨„@wÄU `­ *!P ÀBr!@ÞTB2 ÝW%€PäB€,©„d@%º#®Jai…™Q È€JtG\•"‘ ò •莸*!Ä&$M% *!ÐqUBX‡V(•€ ¨„@wÄU `er!@ZTB2 ÝW%€Mh…©P È€JtG\•¶%ìœJ@TB ;⪄°r!À>©„d@%º#®J»¢ìJ@TB ;⪄°Or!ÀN¨„d@%º#®J;'lK% *!ÐqUBHB­ðOü?1©„° b/(• qUBHKz¹P%§ÂÊ”Øje©„PÚˆ«@ŠRj…*!8•¦ZÒÄXsaª„Pøˆ«@ÒÈ…*!8•&Y'êdmª„Pøˆ«@ö› UB q*!L2;H°þÚT ¡ðW%€œì±ª„@âTB˜dµª°pmª„`ÄU  KeåB€˜TBoå °pyª„Pøˆ«@ÞŠÈ…1©„0^ŒJ8æÛœyX²ÊTB(vÄU  ™·B€˜TB/ìÛùf``ᨄPøˆ«@QòÌ…1©„0^Ø¿"°ŸÇäÝÇ&þz"ñÜòT ¡äW%’#k*¼6Móî5za7‡{_H8ãe€½ï>ÚÜ=y ¤»~|ÏR‚Ð õå©BQ#®J$J(ÖQr%¼^¯½›À÷û=ìæpo‹¬ë:àÍžôΥ̦&@ËS%„BF\•H—P¬ ØJX×uïðívûz³Û<ûºÂíÝË Ý™W +@fËÓr†¼G\•HšPÄVf%|>Ÿ½Û¿ß¯È ¸9Üûv£ _ú×ûâÄöŠÜŸ×!.@6 ÓB†¼G\•HPDU`%|÷Z¼ãñøý=7‡{ß ô~¿/ù'ô¾'j{EîÏ+“ éõh Cö#®Jd@(â)­6Ms<»¿‡Ã¡ý«ïo ¸9\UU÷P?¯kÞ¿¢{ÌöŠÜŸ7¡5@º+ÑÊ…¼G\•ȆPÄPZ%ì}¯ÎÖóùüùm7‡‡_´8[oëtÞ–âi-@k²qUB 'B!\Q•°÷Í?{ßÿ3Ôæpï žN§åÿ–ö ['›Ð •¥gµBö#®Jd&Ø»üW9•°®ëÞ-ßÛíÖýæP›Ãñ>@0ÆÇ–;_q)d?⪄@~„B  B*aïkúZçó¹÷ûCm÷¶¼ •ðv»E:2aɰ۵fyBö#®Jd)X(ôŠWB%|½^½›½Ÿµ>åÿ¨ö *aBÄØá*³0!ûW%r%Ad_ ›¦9½‰°ý«w— µ9\UÕš•°½:wéS%`?‹Ëz„ìG\•ȘP,—}%ŸËÿ½½ï°ªîä¾·~k2@%º#®Jð%ïåýŽ£›o÷VÂÇã±üß«¦{¯[á&ùÑ`=ª„`ÄU ~ €.•0ê¦ñårY³¶Wç.½Û;›óüpž!­W%øI(~Q £n_¯×î÷ßï÷åÿÞÛíÖ=r{uîÒ;¼›9oÍoçÒqUB€_‚½û(•0ê¦ñý~ÔòâõGBÝÍœ.Èï·€ó i¸*!@—P|S £n?ŸÏî÷ŸÏçåÿÞÓéÔ=r{uîÒ{¸›9QëoçÒqUB€^ÁB¡'I8•0ö¦q÷û«ªZþï=¶¯wx7sŠ ïßÎ3¤5⪄ï…ÀWF•0¬€›ÃUUßgnš&R|dÆC8€=/O•JqUB€B! ö ¸9|¹\‚€`ïǶWä·þÃ9=?n«„Pøˆ«   p*a¯€›Ã½EoáG¶^™zÇp6`ÿÛ*!>⪄ …P2•°WØÍáÞ£5M3ïh½o7jïzµ;†“ =n«„Pøˆ«Œ!@±TÂ^a7‡{_úW×õ¼£]¯×à/NÈòq[%„ÂG\•`<¡ ¤ö »9üxNu]ÿúxÁöo·[ﻌ~ûXŠ}ÜV ¡ðW%˜M(€¨„½"m×uv¿Ú{¦rçÑ`Ã¥§B±#®J°„PÙS {ÅÛ ÛCùIíü>3é.¤J@ì5¨Bi#®J°PyS {EÝ ½ŠpÏw•Ù}A•€u£J…Œ¸*!ÀrB!dL%ì{søõzUU5oº½ Ï"Üóý$x%& ÆzT ¡„W%B(€\©„½ÖÙ~<§ÓiüîtûÍíEütö|'YÞ„ XmIª„ýˆ«$@~TÂÍ5MS×õår©ªêp8üÜŽnÿ·ýbûWí7´ßæ\íGÔ¾ LÀj«R%„¼G\• ¬`/*öA%„©b÷mÖ\˜*!d<⪄Á …•&™TBØÏÚT ¡ÀW%ˆÁÇ@6TB˜dR>˜Ý´ ¸6UB(vÄU "  *!Œ75¨„°ùòT ¡äW%ˆG(€ ¨„0ÞÔj°Ê0cÕ¨„Pøˆ«D%@êTBoj2XÒä X¸dTB0⪄± …4•Fš‘ TBØv…ª„Pøˆ«¬@(€t©„0’JÉ­P• qUB€Õ…"•FšÑ TBØp…ª„`ÄU Ö$@rTBI%„´V¨JF\•`eÁÞ}X…J#©„Ö U Áˆ«¬O(€„¨„0’Ji­P•Œ¸*!À&‚…BO 2•FR !­ª‚W%ØŠPIP a$•ÒZ¡*!qUB€ …°*!Œ´f%”'`ù U Áˆ«lK(€S a¤m+¡óSWJF\•`sB!ì™JãMM*!l¸ „ Â$“ÂÁÔ¾ L@ص©Bá#®J°CB!ì‡JSϳ_u¨J@¨…©BÉ#®J°OÁB¡gi°ŒJS/ó^r¨J@Ø…©B±#®J°[B!ìJ3Œì“¢$ñV¥Jeޏ*!Àž …°9•æÑ# ´%iUBr#®J°sB!lK%„ÙÄ(d=Z˜èˆ«ìŸPR a %ò^ŒÖ&$=⪄I `+*!,$C@–+Ñò„ F\• !B!¬O%„ È` Z¤Ùˆ«¤E(€•©„ôÉ­>K2qUB€ä{÷Q`•"‘`W+N%„G\• EB!¬F% *!ÐqUB€D …žÆÀ •€ ¨„@wÄU Ò%À TBØ?mÆ,•ø5⪄I  6•v¾ó¯MÀø•¢?G\• uB!D¥ž7ÿµ ˜ºXTBàK%ȈPñ¨„°Ûýmæ­•P r"@$*!ì3h°dɨ„`ÄU 2#@p*!ì°h°|Õ¨„`ÄU 2#@X*!ì-hjᨄPøˆ«ä'Ø»*!ì¬Êiví¨„Pòˆ«dI(€PTBØIÐ& Þ R ¡ÌW%ÈU°PèyeS aE@˜€¨‹H%„2G\• cB!,§Â×Ö¡P•€ØëH%„2G\• oB!,¤Â?[uIVXJ*!”9⪄Ù ` •¾­ŸÄXa5©„P숫”@(€ÙTBøiÍ: DÀ: J%„bG\• B!Ì£Â/ëV[¹*!;⪄E `*•ºb7 Ö\¶*!;⪄¥ `•zEʬ¿fUB(vÄU  ìÝG *!¼¼¨°É‚U ¡ØW%(“P#©„0 `,`«ÕªB±#®JP¬`¡ÐA²¦° ½@w€ —ªJÅŽ¸*!@É„BøH%„&Ŷ]§*!;⪄… `˜JcÌ®rl¾HUB(vÄU  `€J#ÍZìa…ª„P숫ð%À{*!Œ7© °“å©B±#®JÀ?B!ôR aªù@e€ý,L•JqUB~ à•f(ìjUª„Pòˆ«ð‹P?©„0¾©/I«²qUBº‚½û(¤O%„ÙÄHt=ª„PȈ«ÐK(€TBXBY€´£JE¸*!ï …ž)2•Ò`ÿ+Q%„2G\•€B!¨„°œ »]†*!”<⪄  (œJAh °Ï5¨BÉ#®JÀGB!%S !Yv¸UB(yÄU C( X*!¤,ÀÞVŸJ%¸*!# …”I%„°ÄØÕÒS ¡äW%`¡€Ò¨„œ¾ûYw*!”<⪄L%P•b`'‹N%„’G\•€‚½û(ìžJRàÇ{XÈÎ3¤5⪄Ì#P•TB°­DÈrÄU ˜-X(ôT€S A% ÙJ„,G\•€%„B²§‚J²•Yޏ*! …äM%•,d+²qUB– ȘJ*!XÈV"d9⪄!+•TB°­DÈrÄU E( K*!P –P@fTB2 ÝW% 8¡€œ¨„d@%º#®J@ ÁÞ}¶¦•莸*!‘…äA% *!ÐqUBâ  =×`;*!P •P@êTB2 ÝW% 6¡€¤©„d@%º#®JÀ „BÒ¥•莸*!ë HTQ•°išÇãq½^ÏçsUU‡Ãá×öïñxl¿Þþíív{>Ÿ«Ýªº®/—K÷&µÿÛ~±ý«öÚoswxG%º#®JÀj„BRTB%¼ßï—Ëåx<ÎØn/øz½"ݰÇãq:Æß˜ö›Û‹¸Ót©„@wÄU X™P@Z2®„Çã|>Ùn¶¶G«ªjÞi//\$J%º#®JÀú„B’k%ì¾›èru]¹m÷û}ùiâÞ ðM%º#®JÀ&‚½û(D–k%Œ´?¼<¶GØ[µð°¯B–#®JÀV„B’ ®Ù悼ŠÐ+ Ö|ØW !ÝW%`CÁB¡'£DS`%<—Ë¥®ëÇãÑ4Í÷EÚÿn¿Ò~ýt: ï?ŸÏ7éõz ÚàívkoÀÏ‹Œ¹=>£àK%úF\•€m …ì\9•ðx<Þn·‘M­išËå2PôfܤöRïªåÇìØ~Cûm½?NîÆ*!ÐqUB6'°g%TÂóù<ïÕÇ#ÔË ßª½mãÒ~sïA~½ @*!ÐqUBö@(`·ò®„§Óiár¾û$ÁËå2é8½/$œñ2ÀÞw÷ÚF€üöUBà爫°B!û”k%<¡^a×ø&…¹w/$üù©ˆ#µ øQ‰ÙP °B!;”k% èÝË Ç¡÷B뺞w{®×ëÂw.ÈJtG\•€½ Ø•ð£w/ß„°[ÍËo@~TB ;⪄ìPÀ~¨„c,Ù+î}»Ñ…/ýë}qb¨wXÈæZ%„ÂG\•€} öŒJ8Æ’½âÞ7½ßïKnOï› ¶Wä'x V /•€Ý Ø•ð£×ëÕÝ(®ªjäÅÛïì^¼iš%7©÷MGÇß$€ü¨„@wÄU س`¡Ð³UæR ?ªëº»Q|»ÝF^¼{Ùãñ¸üVµ±} 0ð`«‚W%`ç„B¶¥~ÔûbÀ×ë5æ²Ïç³{ÙÓé´üVµé¹½:?/ L*!ÐqUBöO(`C*á°ÇãÑÝ%>ŸÏ#/ïc|Ü!@ºTB ;⪄$A(`+*á€ÞWNúTÁÞ–¤Þn·HGH‘JtG\•€T…lB%|§÷U„SßÕ³·¶GŽtóTB X*!ÐqUB"°>•°ëù|ö~êߌÀ×û™†ñ*a{u~|S©ëZV Áˆ«¡€5©„ÿ<ûý~¹\‡CïÎpûõI¯"üG%Ü9ò^Î*!>⪄¤H(`5ÅVÂIÛ—ËeügþÔ[ _¯×òÛßÞ•0ÒÝÀiìÕUB(dÄU HT°w€A*á€Ãáp¹\–D½¨›Ìv°cœÃ°ßìêQ]%„ÒF\•€t …¬@%|ç~¿Gº–¨ÿwé…ç0ì÷ûyTW ¡ÀW% iÁB¡§³¼¡~|-áŒ#¾–¨ÿwé…ç0ì÷»zTW ¡´W% uB!Q©„cTU5聾*arwƒ°ßìóQ]%„BF\•€ …Ä£Nj…MÓ,¿–¨ÿwé…ç0ì÷{~TW !û÷{Ѐ¤ …N&?ÅØŸÏå7©·]ª„s禄ÉÒZË*!>⪄dL(¢S  Žq»Ýº{Å—Ëåã{+áãñX~“TÂ`sSr€d)®e•ŠqUBò,z¾ ôR  Ž1;É].—5+á˜pIÏC] ü˜` Ùy†´F\•€ì …@D*aT±«aÖvñõz÷V¥õ¾¼±½:?©P?\•,d+RqUBJ ±¨„P Ç®†YÛÅ÷û=RË‹×ý|UB°Š­DÈcÄU („PD¡@%»fm?ŸÏî¥ÎçóòÛs:ºGn¯ÎO*ìX%KØJ„tG\•€r…@x*aT±«aîvñ¼4üèp8ؾ^秬‚õk%B¢#®J@Q„B 0•°*á½/ ¹]\UUð}æ¦i"ÅG¾öÚ ý\`+×y†´F\•€ …@0*aTÂ1z?^pd•»\.Á?@°÷ö´Wä'ø!P% ÖJ„”G\•€2 …@*aTÂ1zKßÈ*×[ô~4a{ñàå‘QŠ’¤³(JÎòT Áˆ«P8¡XD%,@~•°ý·Ün·PG{>Ÿ½Å“ªÜãñõrÂw/$l¯Ây{—d©,O•Œ¸*!…À|*a²¬„­ãñ¸<œ½K„­×ë5éP‡Ã¡{Óé4õ&µé§=¸{òš÷.É’Xž*!qUBø ÙTÂäZ ¿[áì7ö|÷ÀÖår™z´ûý¾ü5‰½ï\:é²YTB 3⪄ðPLðgÅ?l-ïJø3ÆOiÏçó]û÷½ïÚªªª÷€í×?¾2±ý†‹»¨„@wÄU à›P¼õg»?l­Jø³©]¯×ûýþëýHÛÿm¿x¹\zßô§çó9^¯ÃžN§º®»·êv»õ¾Ëèì÷>Ès–Q Έ«ÀOB!ðþìæ[+­.´ð½=ëºÞÕíÈg´Q Έ«@—P%ú³×?lM%oö«  gä"@†“ŽJtF\•z{Q!°[ùÃÖò«„Çã1ø¶ð˜/H(ô*B€ÿ|TB 3⪄ðŽP¹ù“æ¶–_%l=ŸÏ1Ÿ08F{/Ù{½^UUí!YñQYn€=­8• qUBàc !m²øÃÖ²¬„ßžÏçõz×ãN§Sì·ô|<íµLºIíEÜiwýÀ,=@j«ÏR…ŒG\•† …’?9þakyWŸÇívû {»aûÅÓéÔ~CûMÓ¬vÃÚëªëúr¹´7à×ëÛÿm¿ØþUû kÞ$ætDP% öÂT ¡ÌW%€y‚U(AFYP%,v E%„±ùÑò0‘VJÅŽ¸*!Ì&Â[ùfA•°Ø-•Æþˆ„ ¾|TB(yÄU `‰í’VLT ‹ÝBQ aÔoƒøá@›€°kG%„’G\• )T©YðÖø××:×¶PTBõËa•j M@¨…£Bá#®JË …Aì v*a9[(*!Œú]±V2Ð&`ùªQ Áˆ«@B!’G:•°œ-•>ÿÞX1ÈdÕ¨„Pøˆ«@(B!É“§'9•°œ-•>ÿY·È°pɨ„`ÄU  ¡ÄÈ‚‹œJXΊJŸ«¨„Ô U Áˆ«@pB!û% †Žn*a9[(*!|þ%£BR+T%#®J1…ì…,¹²©„ål¡¨„ðùwŽJI­P•Œ¸*!DìÝGaYpݬ¦–³…¢Âç_A*!$µBUB0⪄PÈdAU¶PTBøüI%„¤V¨JF\•¢  =áæ›,( ²ÅŠJŸA©„Ô U Áˆ«@lB!KÉ‚² ;ØBQ áóï+•’Z¡*!qUBXPÈ4² ,Èþ¶PTBøüëkÅd O@U£Bá#®Jë " Ê‚ì~ E%„Q¿Ð¶«„N>L]5*!qUBXPÈÿ“eARÛBQ aÔï·Uª6¡ŽJ…¸*!¬I(,—,( ’øŠJ£~ÝůÚ„];*!”<⪄°>¡°² ,H^[(*!Œý3|ù¨„Pòˆ«À&„ ɂ² Yo¡¨„0ö÷a´| L@¤¤B±#®J[ öî£lE”)i E%„ ¿!CGUb/L•ÊqUBØP˜YP¤à-•¦ýÎ Q$ XyUª„PÚˆ«À¶‚…BÏÈceAP aޯе8Õ°«Ué …l¡¨„$M%º#®J)ÚËgÊ‚@1[(*!IS @¢ ¬„² °áŠJ@ÒTB ;⪄®ì+¡,ìg E% i*!ÐqUBHZf•Pv»…¢4•莸*!¤.éJ( ©l¡¨„$M%º#®JH¨Ê‚@rTB2 ÝW%€<ì¶Ê‚@êTB2 ÝW%€lì¤Ê‚@fTB2 ÝW%€¼žüË‚©„äðDA%:#®Jy=ù—S ÈቂJtF\•òzòŸU"ôóö@% ‡' *!ÐqUBÈëÉÚ•ÐØ!•€ž(¨„@gÄU  ¯'ÿ‰UB?1`ÿTBrx¢ W%€¼žüï½úÉQ ÈቂJtF\•òzò¿»Jèg¤N% ‡' *!ÐqUBÈëÉÿö•ÐÈŒJ@OTB 3⪄דÿ *¡³äM% ‡' *!ÐqUBÈëÉÿ•ÐiŠ¢Ã•茸*!äõä?þ€Â¨„äðDA%:#®Jy=ùW S ÈቂJtF\•òzò¯¦Ã•茸*!äõä_%L% ‡' *!ÐqUBÈëÉ¿J˜J@OTB 3⪄ד• 0•€ž(¨„@gÄU  'ãs’Ò¨„° b/(• qUBȉJœJ+S"`«•¥Bi#®J9Q ‚S aª%í@Œ€5¦J…¸*!äD%N%„IÖI„zY›*!>⪄• 8•&™$ Xmª„Pøˆ«@NTB€àTB˜dµª°pmª„`ÄU  '*!@p*!Œ·ò … X¸âª„TBi·]x]`…ª„À—J0‚J#­Y ¿„BX¼BUB0⪄TB)`%Œzu`…ª„€Jð‘J#­ö¡„K®¬P•øR FP a$•ÒZ¡*!qUB€*!Œ¤BZ+T%#®J0@%„‘TBHk…ª„`ÄU U Çív»\.UUÇ_{¿‡Ã¡ýúù|n¿çù|®v«š¦©ëúß­joC÷&µÕ~Cûmî®ÛšÑ –Ty®P•Œ¸*!À€ì+áãñ¸\.Ý&8F{Á×ëõ¶N§ñ·§ýæö"î´[Q !­ª‚W%k%|<çó9Èžp{œà­°=`UUónO{Á¨í’wf$•¶]¡*!>⪄²¬„¿Þ·3ˆº®Cݼûý¾üö´qï]ßj•Pž€åËS%#®J0 ËJisø|>/¿mu]ï0\2û®5õ~¸ä>ìüÃúü–!$=⪄TÂ5CaWzEáÞîZ“¯Èɇ­ù­DHwÄU ”V ‡Ãétº^¯Çãù|þ¼Tû•º®?~šáìWð½^¯áO¼ÝnímèÞ¤ö\ÐgîäÞ5ò8ûZœy˜ýȯB±#®J0 Jx8.—˯,8àv»sUUõíx<~¼aí7´ßÖ{ñÓéän¼í½+x_Ð&`嵩B®#®J0 ûJXUÕ¯èôz½Þ…¹ï;ÚÞ†å‡z÷:Çyÿ@‚ÜÁTBH}aª„ñˆ« ȸÎîƒßš¦y Û¿št¨ÞÎx`ï»¶wOÞä>&BÒ«R%„ìG\•`@–•ðp8Üï÷ ‡z>ŸË?ðÝ §¦Æ¯ÿ†ËÞC3U‚P !ƒU©Bö#®J0 ËJVïË÷–¿Sè¤ÎøÓõz ò&¨,´Z"& ø’T ¡W% ~T×õÂ7ù »Õüîå„~R›Ûüd!ÞbT ¡ÀW% ~ôz½–ì÷¾Ýè—þõ¾8qá‡0²Dš J@ðe¨Bá#®J0@%cÉ^qï„.üØÄöâÝc¶Wä'x V /•`•pŒ%{ÅUUu/Û4Í’ÛÓû¦£“Þ „j• qUB€*áKöŠ»<ËoR{Û×ÃÔ*!>⪄TžÏgw£øp8̾ìétZ~«ÚƒtÜ^ŸP&•莸*!À•ð£Þùöžñ>@0ÆÇ¤K%º#®J0@%üèr¹t7ŠÛ/޹loË R o·[¤#¤H%º#®J0@%üèp8t7ŠëºsÙÞJøx<–ߪö *!À7•莸*!À•pXï[†¶š¦sñªªÖ¬„#ß ?*!ÐqUB€*á°ÞÌw:–\\%N%º#®J0@%ðî…„í×G¡·¾^¯å·­i•à›JtG\•`€JøNÓ4½ŸHØ~qüA¢n2ÛÁÞmò^ƒV"¤8⪄TÂwN§Sïñ¤÷ U ³÷g-N5ìa1:ÏÖˆ« P {Ýn·ÞýáñŸHøJ˜±?ërÂaKÒy†´F\•`€JØõx<Þí7M3éP*a®þ¬Î9‡=¬JçÒqUB€*á/ÏçóÝæðý~Ÿz4•0K¶à´Ã¦ó i¸ßƒ.]16O²L„×ëuo!/³“¯D京@%TB€òZÂoMÓÇÞ³t>Ÿwò¢•¬M+TB€Œ©„aûõ©G¨›!œ"P • *a¼D¨–“!œP ÝVBg —J/¶ªªŠ´ÉÜÞ°î‘Û«s—ŽG5€W¨J%¸*!À€Â+aÔDøõ¦>ŸÏå·üñx¨„+“ ¹ªBá#®J0 äJ;~½©„Çcù‘UÂõIÜ U ¡ðW%Pl%|>Ÿï6C%ÂÖårY³¶Wç.^i­P•Œ¸*!À€2+á:‰°u½^»Wq¿ß—ùv»uÜ^»t¿Ûõ½\.ñ®·ªªàûÌMÓDŠ Ó •å©‚W%VB%lš¦7ÕýS×uÔk¿\.Á?@°÷㣶NþÑ •å©‚W%–}%|½^ÇãñÝ~ïÂZ7FoÑ[ø§½¯‹\áß‚d -O•Œ¸*!À€¼+áóùØìmÿv›Ñ{íMÓÌ;ZïÛÚ»^ÓI¬M•Œ¸*!À°Œ+a]×ï¶yÇãìH7CïKÿf¿Óéõz þâDÆS •µ©‚W%k%ìMiÿTUµf"l=P/'|÷BÂö*Ü™W£Àþ¦JF\•`X~•°išÞ×îmûš»Ãáн1§ÓiêqÚ‹tÓÜ=yMÂ$±0UB0⪄ò«„ÇãñÝîîívÛêVÝï÷åÕò]ýlîž¼2áv¾*UB0⪄Ãò«„ï¶v7OiUU½{Ô×ë5|Ùö.în¼‡»™sû|ðW ¡äW%PN%Ü|Çøõz çt:ÕuýëãÛÿ½Ýn½ï2úícadµ{šsÙ?ø;ÏÖˆ« P ×Ü1®ë:ìµ{¯Ñ¼ï]Úìmy:ÏÖˆ« P WÞ1 ÛC¹ç}ïÒ&`oËÓy†´F\•`€J¸þŽqPèU„%Ü»´ ØÛòtž!­W% n²cüz½ªªšwí}a!÷.mö¶¯<Ü4M7š(¼1¼Ý®; )!(g•wq¥„ R¶mW~>ŸÓGªC#ö^€¿¤„@·‹+%HÞï÷5Ç뺞k°:4eøÝ.®” ¡ð”ðý~¯9<Ë,B3 º¤„@·‹+%H(9%ì‹nš&ý´ÁÇãñz½þ}Køg]××ë5ñFÏ(ø#%b]\)!@B™)aÛ¶Ñ.:|¹\¢Ë:ŸÏï÷;ýÞð‚ð²èۯ׫Ý@Jt»¸RB€„BR¦i>óòî÷{_Z·è8pXztA·Ûmx#áÅÑF~Ì@(”èvq¥„ Ù§„;ŽF“#¦Fï>·'…“Ý.®” AJ¸Â8pßD¶m¿m*¼%ÚÔ¯÷,È›”èvq¥„ RÂÆ£w ­ëz\kÑ'*~uçRG{)!”ÐÅ•$H Wžw}Ó íÌ@ɤ„@·‹+%H.=½ÝèÄ©ÑɉaAög XRB ÛÅ•$dŸþjéqàè BŸÏç”6ÃÛ»m†ÙŸbI nWJ %\zør¹tÛoÛvJ›Ñ›Ž†ÙŸbI nWJ %\z¸Ûøù|žÞlhÄð5@ú`.%„»¸RB€)á¢ãÀï÷»ÛøõzÞrh¤ÛrXœ]zú·¿O¾,ØC9ÛÎp¬.®” AJ¸è8ðr\âq‡¾})!(g•9uq¥„ RÂEÇ£YÞ,)áãñX¨eß¾””³J„<º¸RB€)áú)áëõšÞrhDJ¸Ð·/%å¬!.®” AJ¸è8ðårY3% ‹³KOÿö¥„ œU"äÑÅ•$H ¥„%“‚rV‰qWJ %\?%lšfzËmÛJ úö¥„ œU"äÑÅ•$H >nãÌ¥„PxWJ %”äz0—Bá]\)!@‚”PJëÁ\J…wq¥„ RB)!@®s)!ÞÅ•$H ¥„¹Ì¥„Px÷oG€®%O¤„RB)! %Ø3s £›EãRBÇXdLJÝ,:Æ‚” cRÂèfÑ8€”Ð1òH m €()á¢ãÀ—Ëe¡ÆÛ¶í¶g—Ì¥„€”àWRÂõSÂ÷û=½å×ë%%HÌ¥„PxWJ %\?%|½^Ó[–üz0—Bá]\)!@‚”pÑqપÖL ÃâìÒ€ƒ¹”üJJ¸è8ðý~ï6þ|>§·üx<º-‡ÅÙ¥s)! %ø•”pÑqàçó¹P–·\þhHìÿÛŽ¯ F²J„»¸RB€)á¢ãÀï÷»Ûøív›Þòõzí¶g—^t± Y%Bá]\)!@‚”péqànã—Ëez³§ÓÉðõúû€”ŽUÈ* ïâJ ¤„K_.—ÙÛoÛv¡ðÑ> %„œ Y%Bá]\)!@‚”péqપf€`ôq‡aAöç¥÷)!«U"ÞÅ•$H —Ž&zMÞ>{òX2)!äZÈ* ïâJ ¤„+ŒGѶí¸Ö¢·5v½Î> %„c²J„»¸RB€)á ãÀÑ©u]kí~¿Ï>9 ˃¹” ïâJ ¤„+Œ¿^¯¹¦öM$ ‹°3%“Ý.®” AJ¸Î8ðétê.åz½~ÛNxK·Ð¸=(œ”èvq¥„ RÂuÆŸÏgtA_Ý)4zçÒ 4nO '%º]\)!@‚”pµqàËå]Vø{Ó4é÷†$Þn7Ý.®” !û”p?ÅMÓ$¼^¯u]ÿx¼`øçãñˆÞeô¯_F€H nWJ %\s ¸®ëyWýF=ÚK áÐ]\)!@‚”påâƒÂД`Ñ£½”ÝÅ•$H ×(ž%(4‹`…£½”ÝÅ•$H 7(nšær¹Œ[tx£g¬s´—¡»¸RB€)á†ů×ëz½_hxqx‹`µ£½”ÝÅ•$dŸî_Û¶u]WUu¹\N§Ó¿ÃÑáŸáá…„—ÙV}¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!üï|M°‡B¶áX]\)!@‚”¤„ U"dÙÅ•$H AJ Y%B–]\)!@‚”¤„ U"dÙÅ•$H AJ Y%B–]\)!@‚”¤„ U"dÙÅ•$H AJ Y%B–]\)!@‚”$w!K ¡Ø.®” AJr(¡¥„P`WJ %é”SÈRB(ª‹+%HŸoòÛ Ž^ÅRB(§‹+%H¿$wñJ ¡¨.®” AJ]òȵl¥„PTWJ %„>Rȯ`¥„PTWJ %„4ÙäTªRB(ª‹+%HÂBȦH¥„PNWJ %„áD AyJ ¡œ.®” AJß(À¡ SJåtq¥„ RBM¬‡«G)!ÕÅ•$H a"ᨥ„PTWJ %„Yˆà5(%„¢º¸RB€)!ÌHÐ;¯>)!ÕÅ•$H avâØmÝI ¡¨.®” AJ 7À®*NJvq¥„ RBXޏvRnRB(³‹+%HÂÄ °Ã¢“Bi]\)!@‚”æ%n€—ž”ÊéâJ ¤„0A¢¥„PNWJ %„éD p 2”B9]\)!@‚”¦.ÀáŠQJåtq¥„ RBA¬‡®J)!ÒÅ•$H á+È 6¥„PHWJ %„D M…J ¡.®” AJ¿"@fu*%„Bº¸RB€)!$ˆ Ëj•B!]\)!@‚”¢¤kÍJ ¡œ.®” AJ?È ïÊ•B9]\)!@‚”þ’@ õ+%„rº¸RB€)!ü‘BIU,%„rº¸RB€)!H œB–BQ]\)!@‚”äPB!K ¡À.®” AJÈ»¥„PlWJ %„ÿ¯ öPȶ3«‹+%H‚”²J„,»¸RB€)!H A!«DȲ‹+%H‚”²J„,»¸RB€)!H A!«DȲ‹+%H‚”²J„,»¸RB€)!H A!«DȲ‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” AJ@¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” AJ@¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” AJ@¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” AJ@¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” AJ@¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” AJ@¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” AJ@¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” AJ@¤„@·‹+%H)!ÐíâJ ¤„d@Jt»¸RB€)!Ý.®” ¡À”°mÛº®«ªº\.§ÓéßáßðÏðÇð¿Â ÂËìG!%º]\)!@BQ)áëõº^¯ÃGƒÃ‹Ã[ì$û'%º]\)!@B!)aÓ4—Ëeܘpxcx»]`Ϥ„@·‹+%H(!%|>ŸÓG†C#ö€Ý’Ý.®” !û”°®ë¹‡CSv€}’Ý.®” !ï”p–Y„f쟔èvq¥„ §„MÓ¤Ÿ6øx<^¯×¿o ÿ¬ëúz½&Þè…;$%º]\)!@BÆ)áår‰ŽñžÏç÷û~oxAxYôí×ëÕn°7RB ÛÅ•$äš¾^¯èïívÞHxq´‘3Øœ”èvq¥„ ¹¦„щ„#¦Fï>·ç슔èvq¥„ Y¦„} Û¶ý¶©ð–hS¿Þ³€5I nWJeJ½Sh]×ãZ»ßïï\ ÀÒ¤„@·‹+%HÈ2%œwh·o:¡`çG~)!ÞÅ•$ä—Fo7:qê_trbXý`'¤„@·‹+%HÈ/%ŒÞ ôù|Ni3¼½ÛfXý`'¤„@·‹+%HÈ/%¼\.Ýqݶm§´½éhXý`'¤„@·‹+%HÈ/%ìêžÏçé͆F èà/%]\)!@Bf)áûýîê^¯×é-‡Fº-‡ÅÙ…ö@Jt»¸RB€„ÌRÂå ¸Ä㘋”èvq¥„ ™¥„Ñ,o–”ðñx,Ô2ÓI nWJPBJøz½¦·‘ì–”èvq¥„ ™¥„—ËeÍ”0,Î.°RB ÛÅ•$H ’ì™”èvq¥„ %¤„MÓLo¹m[)!ÀnI nWJYJ¸è ®c€cÿ¥„PxWJ %ÜIãÌ~ˆ–Bá]\)!@‚”p'0û!ZJ…wqø•”PJ %”‚” 4RB)!€”PJ™¥„ÿ`]RB¤„ÀRB¿„õ} ¨8Pt€Šƒ#]®£ÄRB)¡”Ê<2Ø è@ÅŠTPHÑI Š=‘I ו è@ÅŠTPlÑI Š=‘I ו è@ÅŠTPlÑ].—…uÛ¶í¶gçØÉ‰LJ¸®E*Pt â€b‹.š¾ßïé-¿^/)!ÀžOdRBÀu%(:Pq€¢[tÑ”ðõzMoYJ°ó™”p] ŠT è@ÅÅ]UUk¦„aqv€œÈ¤„€ëJPt âE*(¶èî÷{wP÷ù|Noùñxt[‹³óìäD&%\W‚¢(:Pq@±E÷|>Êò–˘åD&%\W‚¢(:Pq@±E÷~¿»ƒº·ÛmzË×ëµÛrXœ`''2)!ຨ8@ÑŠJ.ºî îår™Þìét2\ p ƒ¿”P¶ è@ÅŠTPTÑ].—ÙÇuÛ¶](|`®™”p] ŠT è@Å%]UU³?@0ú¸Ã° {À~NdRBÀu%(:Pq€¢”\tÑDo⣠ÃÛgO˜÷D&%\W‚¢(:Pq@áEÚmÛv\kÑÛ:LìÿÈ/%G¨8@ÑŠŠ*ºèÔ¿º®Çµv¿ßgŸœÀì'2)!ຨ8@ÑŠ /º×ë5×t¾‰„av€]Ȥ„€ëJPt âE*Pt§Ó©;º{½^¿m'¼¥ÛNhÜ>°·™”p] ŠT è@ÅŠîù|Fx¿ºShôÎ¥AhÜ>°·™”p] ŠT è@ÅŠ.¸\.Ñ1Þð÷¦iÒï /H¼Ý°Ã™”p] ŠT è@ÅŠîÏI_b¤÷z½Öuýãñ‚áŸÇ#z—Ñ¿~MØäD&%\W‚¢(:Pq€¢û¨ëzÞña÷Øí‰LJ¸®E*Pt âE÷׌AahÊ®°Û™”p] ŠT è@ÅŠî_³…fìüD&%\W‚¢(:Pq€¢û¡išËå2nL8¼Ñ³ö"“õz½®×ëðÑàðâðÛ `ÿ¤„¤µm[×uUU—Ëåt:ý;üþþþWxAx™mpRB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%€ÒH  4RB(”J#%à‡¦i꺾ßï—ÿtêçó9ü=¼àù|Ú\ìÖëõz<UU…Ý5ì´?vãÓéþ~»ÝÂkÞï÷jkÕ¶m¨¯ÏZ…uè®Rø_ááe¾ATp¸¢ kºˆ×ë5Ú‡üô-?}ÈÐáô%r,¡{öÙÃCeu;r¯’V«;g^`RB€mu‡P¶Z“pñ®1»W»ŽðÈëõ »qwœdˆðÆEG,ú]¯×áë^Þâ;EÅ-}îsvCÑͲb·ÛíÛU äñxøa ;÷|>÷Swœy€½‘l«{‰·þ:Ôu=îJÓž78Ú™}ü$4R1Dx£ñTÜBÚ¶íûmŒoE7|ݾýY×ý~—¢îJ8ó;$%ØPÛ¶Û3§ìøÙÜôÁÉ®º®çZ½çó9}}ÜÝ·„Ä/d|Ñ(º!ýدæÈ;Ù¡îœy€’lè~¿ouÌlÛv–ߣúɵ3 dúºÕum0·ZÅ}%}ôE£èÒúîÖ;QUU¾q2®»qݹlμ@Q=WÙ£GWÖYôè[Œ:ÂSNgfâàÉ,³M²@Å-áñx8»¡èF]:"¼\.÷ûýõzý¸«ax×çùné‰Q’ ò®»Aag^ Àž«l€q£+.ú¯ëõúx<ºÃ;á/áïÿÞ]ÊWÉž;3§Ó)쮟±Ê°çÿØ™ëºþuFíè|¡vÒ#¨Ÿë®Rúîmž#ƒŠ›nH‚ï‹FÑèI†5xž «—xbohÇ÷Înëî|>WUj'ìÆÿ>O3ü÷¾\ð£ZK8óíùH –®£7]ç˜ùëo¿NV Ÿ"\Q†ËR_(;ìÌ„=³ªªá#0éYEス¾ÁÏóùüëŠ%fû^¯Wß8*n¹ó ëݯ=ÀèLÀ!g·®Ddÿã‡4°yÝ…<”ÒÀz •Š4qÍUÔ™8\ÏGJ0¯pÙõù%çý~Oüjz…cfbrS¸Ò4C™PbãöäP}Á܈{1…u˜ÞTßo¿•**n´¶m]¿ è¦]ô§n¡ågT}¥/¸ÿ6Fåê.ÔȈ<Ñ!üv:á¡Ï¼À±z>RB€=c—[ŸÄ…áè±ØI¡3ù+TA_|[ Ñߌ˜½c•STÜìKtý‚¢ØÂ‘ú’”q¹ ÌXw¡'6q÷î›0[UU g^à@=)!À~ޱ ­LßMo‡oŠC;Nï”û«¾ _=±¥o´sÄðKßx¬STÜ¿>›Éõ Š.-¼lbØñUyÎÒ2Œ®»¹îÞýñØW?ú:î™8)!ÀÞŽ±K¬I_lq¿ß}Mð¯èô½éw =ö½·›C¡âf)¥ o–‡¯E7ð½³üp%Z‰æÎ“‡½hV;óG!%ØÛ1v‰5™ëþ‡½èD‰¯*ç­ë¾é„¾)TÜÄ¥üM¯ÄPt‹ît:-T,Nvdlo»÷:g^à@¤„{;Æ®s%x:<~ºš¦™R˜Ñy»žœ8×]°àÐ7DßÝÕþ¦ëÝÀ}~ÑbQ‰”vU˜ñ™È ¯"%Èé‚1úz,Q˜Ñ»N|”LôFUnŒŠ¢oG83®|.† Šn®§înØ+†Œ/ú”0cß@JÍZtf“[ÊÀB…½»ïÄÔhÌ¡ŠQqCj'ú;™³é]¿ è¦¤„ óÜJÕ—E¢»÷¶9'>à×¾” › 4÷*„5 3=ei´hÒá›BÅ8ï÷{ås1äQtÑ“Ñ,aGtÖ¼'h“‡èÓ>ÆÍø"8ú‚” › 4?̆¯Da6°j¢ïe34òkÒ¥U\Zôö¿ÑyO®_Pt‹®/yŸþó³hËu]û¾È@ôFMÓäwæJJ°·ƒðŒíG˜½í/Waç¢U3p¢Är\âq‡pôŠKˆNÜè;º~AÑM9ÍMÿáJßóC}Yd úô‡Ûí–ß™8.)!ÀÞÂ3¶_U•ùG0½j‡¼7šåÍ’>…Z†ãV\Ÿè,‰Ä¨¬ëÝð¢;N}Wý£»]%¿j#}磉O¬Þá™84)!ÀÞÂ3¶ïYfð­èøçÀ›žESÂYý!º”Â+.ªišè¹5ñ„P×/(ºáE—˜NøÉâ¿?¢'¸Yé ÛŠîÛ{øÅæìg^à褄{;ÏÕxôöMÝG¤…—=ŸÏªª.—K7U W‘áï·Ûíñx˜„Höú?ŽyFŸ;³\JèÞP^qÑ_ôç1á‰6]¿ è¾*ºèD¤?bøÌµ¾™VúœZØ£O”ž«[¸«3/)!ÀÞÂs5þë䣺®û.`ûœÏg?4%cј¯›­õv)!,Tq]}'µtâàúE÷m;·Ûí×NcxMú Ø÷üP!GööÏ/ûîÊþ¾‡}{ö3/)!ÀÞÂs5}Ùç‘1u]'+ó«ðÞÑžÝêûqõð½=:ð2p>EZtj°”Â+ùM¿&õ®_Ptsõ3£ÆðʧÂpR—éÃίãú„3Ô&ëÍ~æ ìØH V8ÏÕxôi‰¸~ëz½º5 Ù;s´4ÂwRѺX¨¸„¾IICæ¿+.Ý8¯×kx¯ò|>â¾j½\.³ü®6¼ŽëÖW¸øÚÉŽ½ÄA(§c#%Xó Ùwl¤„k„çjü|>¹t½Ýnu]¿^¯î#cÂÃÿúõ&¥aAn=Êq…ý¼oßþvÇ–š÷9UEOv_˜Šn´ÐÂĹ„—ËÅT&²¹ŽKìçN-\ô ”Ö±‘¬p^á?âu]'²ÂКo“#ê»Qá¸ûDI aÍŠûÓÿ´¯&n(.Ý8}Ï%¼\.÷ûý«›ZÈ Éã:î×ý|ýTnуPlÇFJ°èAxÑÆ§<ù%\ÕöǺï(G”6¹ßh_"%WÜív›åKŠ E7Bô†¡Ÿùo†®cèv&zÝg‰šÙÄq½þŠ+½Ï¯yÒE@6¤„{;/Úøô,¯oT6üÝÊôݨpÊÎ,%„Õ*®®ë¹žq¦¸Pt³ô¯×k_Æ7<.qaú×n„H6š¦é{xî¡@f¤„{;ï¿ñ¾áYC:ÂrÃ&RBX§âúž÷¤¸àÏÂé@èìEgŽèL¦\hF!ÙÔã\3ßwrò#%ØÛAøG/<=Þ‚ý[tØ$:à9×jϘ‰ÀÑ+î@³€!§Ó\_Þ1ºÙ¾[Olv%úËÓétă%)!ÀÞ‡hüñxÈ,8œ¥‡M¢)á,³!¢ãK*Žb+nÞg<¹~AÑ }ÎÚÄ)Qa­ú&ztÙˆæ·as]K 6<ÏÕxô™/s5Í,VxÄŒ¶Â°Itœ3ËB'%¤ØŠûßv|³{š‹N$œk>T_P(Ô ÑXVUu¸¾.%WÓ{;ÏÕxt¼e®Ëþ[NùNÙ§÷ûÝ×K™qؤªª5SÂÙ—à('%„õOsÑ»ƒÎu·ù¾Žåãñðå’~îµN_È’«i€½„çj|¹iM+¬<Ìhµa“è­Øf¹Tô'èžJ±'%„õOs·ÛmÑŽeô—6×ëÕ÷‹ë¾ýôu€r:*®¦2¸ZŒRBJ³æ°Éóù\(Ë[.„#Vœ”Ö?ÍEïd8cûîfë¾ý÷u€r:*®¦Ž~µøgáCý‘rÑ*ø¸Ýn³›DGi‚¦·|½^»-‡ÅùŠ)³â¤„°þinéŠp7{\÷í¿¯ ”ÓQq5 pè«Åå‹?=#9§ÓÉwÊ~DïÒ9o! )êY:ŠKÿ ·ò%ŒëÝj½ÖÕ9]÷mÒ× 騸š8îÕbºý¹‚¼è]¡fIC`ÑÇ'}TUµÜr£ÏØf4”Wn¨¸£œ‹!¢‹.«iÅ¿ê»)h±g^`ÿÅRB€ Â3¶¿Ü] £¿\uAÊ´mê>êº^té¡ f€`ôq‡Ê w”s1äQtÑÅÍø„\÷© cѾÜè_|epæöQ,%Øð ì¿âq.†<Š.šJÌxWÃycØ•è¯ÈÆýâ+3/°ÿ‹b)!À†áÛþ*{úí¡úš ÷²¡¾»9Í8‹vt]®Ž¾róu£âŽr.†<Š.sÌØý‹þ$æ~¿ûÞ9º¾¾ÜˆD/›3/°ÿ‹b)!À†áyôþ8¥Íûý>{›0Q]×}ýóù¼f„ç=Ó6Zn3ÎÝ€£WÜ!ÎÅAÑEçúÍuJjšFêA®¢}¹ {Ng^`ÿÅRB€ Âó.âõzÍûÄŠ¾Ÿ°†ùBÙJßðËçfe+›ô݈Õèûñ¹rCÅë\ yÝrÏA‹þªí|>ûêÙä¤ðx<æj­ïÒéÛx=³3/°ÿ‹b)!À†áÙ—Ò÷tûC:á ôt:yj ûöÉèܽmçÜEËdÄ|Ûè¨ihÜ÷ŽŠ;ܹ2(ºDN1%(ìû€x͆'…óù<ýGY‰„D–g^`ÿÅRB€ ³/%q}úÕeC;ázÙý Ø•¾}rÞßk–Û²õxŠ ä]q‡8CE×7Ãý£ªª Fãhìä¤ ptZÝws‰o‹%Ë3/°ÿ‹b)!À†á%”¾Gͯ?”mÛ6ÑBø_¾JöÖ“Ùõh¶mÃ:WUÖðt:ý»ÎáŸáá…„—­°2MÓÔu}»Ý+^0ïí[ÿ]èoí|>‡?Þï÷…¶Àgã‡öÃR²ºûÌç#?÷û}è…îsËã È”€­ºS|¿ß#:Ÿ_½>,âCt3ˆÛí6|U_¯×õzÞy/oYâ[hÛ¶®ënª’^üx<¦Ä…#6ï\[àÛÿ÷+˜ò©7Yè†ûÛšeµí¶(a¸FJÀ ÝÎ…º²ÓW£®ëӯƭ|Ó4Ý4d ðÆyCÇã1¥KÖçù|~»Ð![2‘ìL™éöz½FoüÑ Ú& Ý|[§¬¶Ý¶å ×H X¡Û¹PWvÊj¼^¯©Ö¯«W×õôŽthdúÆošæ«©| aã \hÛ¶Ó3`Ü#ï÷ûú—3›,tûÛ eµí¶(j¸FJÀ ÝÎ…º²ãV£mÛªªæêâÞn·¹úÒ_ÝÝ´+qkÖåúöó.ôÛèjÆ?ü#o²ÐìoK—Õ¶Û ´á)!+t;§4Ø4͈Én}«ñ~¿¿½1fb)³‡£ƒÂYæ—}û­ÍžK~5£ð«Lj®¼ÉB÷³¿-ZVÛn[€‡k\…°B·sJƒÏç³ïñj߮Ƹ(­oéÇÿ…Õ /øñd´ðÏðÇôý9à ¾ÝD¿¦uUU…Íøo×4MX™ûýžX™) =ŸÏŸÿïÃBÃj„•I$Já yFah9~ýø¼Ÿþ|äëõ:î#o²Ð]ío˕նÛ Ìá)!+t;§4Ø7Ãè~¿¯Óû¶ŸÈÈ.—KÓ4é^Èn¾zB_Û¶3 ÐBxY7¹K¿¥ïˆC>þŸdæ•þf?úrÆðÞ!!ãçìîZ;\è®ö·åÊjÃm Pìp”€º£[KD`?¦MîýF§)…ÆëºþÜà1Ú~_FöÕ̬ûýÞ7ox#}©B#ã•¿ÂGþ7¬I¼²/ºýêÁ‚‰ä+½æ}óξJWÿýԿͽ-toûÛBeµí¶(v¸FJÀ ÝÎÑ­õå¿¶9¤ïZ×õ·9ÚGß-¿ÊÈ>ú¦Ô lª/^~„wýÝæ}¯é{Räˆß¦§FÏ7dbÂóùL?ér“…îm[¨¬6ܶ%×H X¡Û9®©Ä\³_„t¯uøm £¢wG¼^¯ãZ‹NpDo#9ðÑ~é-Ÿ˜_ÍtFm­ïYi'Ùý陊UUÕ”6£ùûu²XtNß”ÀnôªNÌ%¿œXTJ¸Ÿým¹²ri °ÉU­”€ºÃßþ~¿£™Ñ·)ØrÕhËïŽØ÷¤¿k2dFØÝ»_&îM:Pô¹{a7ØÕßd¡»Úß–+«­¶-@9¤„lÕíL¼þý~¿^¯º®«ªŠ>íG 5pÖÒBÕè$Çé1ÙŸžgÏ%æèõÝntÑ/7š.~"az«&âàècì–žD¹ÉB÷³¿-ZV›l[€¢H 8t·sxDøg±8ã~¿Ï~ûÇèM Ãâú^ÿx<ÆÝ4rŠè¤¿YƒØmöt:õ½¸o¶é,w”ÝÕB÷³¿-ZV›l[€¢H 8n·ó«ˆðϺqF]×Ó[þö®›Ñ5ù5å™(--·Û|µ­þÝKDK›,t?ûÛ¢eµÉ¶0\#%`ÿÝÎá×BÕËå²Ðdºoﺹܚ|ûñ7¹¸rsÚ°Û„ 2cÀ´þB÷³¿-ZV[}¡†k¤„ì¶Ûy»Ýš¦™k5¦ºh–1n ˆ>ò/q×Íè£ÜÞï÷¢_î~RÂçó9¼°¯×ëôŒiý…îg[´¬¶úB ×H Ø[·ót:UU5% Y¨³ºhø«Æ7éÿo]é•é{˜Ý½+¼·®ëÓÊ ÝÏþ¶Âúlò…®‘°m·óòŸûý^×õ,S¥¤„ǽ¦þq¢ÏI\z²êš -*%Üê 0\#%`¹ng6«!%ÜUJøç¿çëýúH»Ù|¹ÚBKK ·úBÊ®‘°B·3›Õîó ®ë‰ÏL<ŸÏßÞ¯r……˜nø…dLJÀVÝÎlVCJ¸çK€¦i꺾^¯£s¥½-´Ø”pÃ/ KRB¶êvf³Ñ{!¾ßïé-7MÓm9,ný5ùv«îó1pa­žÏçý~ÿjJÚÄ;UξÐýìo›W÷&_(@6¤„lÕíÌf5¢ ÅëõšÞrh¤ÛrXÜúk²ÉÇ_ÚgVÚívûõ™w3Þ¦rúB÷³¿íªº·úBŽKJÀVÝÎlVãv»u›}<Ó[t[‹ë{}ô6Œ³¬IBt¡‡›®õz½7±\hŽ[è~ö·]U÷¾P€c‘°U·3›Õ¸ßï#²•!¢yP"€[nM¾ýø×ëõˆ;j]×Ñä×ùtk.t?ûÛ®ª{?_(ÀQH تۙÍjDïÓøëÓ܆ˆÞ81qoÉèš,½©ûzЛ:F“²Y¾Í¹ºŸýmWÕ½«/ऄlÕíÌi5¢-¿ßï)m†·»ÂmÛFßò|>×ß°‡»éèè;þBw²¿íªºwõ…‚”€­º9­Fô hoõë<£k²ôýûwÐé„›ì®_-t?û۟ݧ„‡XC€ý\.I X¡Û™Ójô=þ¬išq †7Ž›Ø·&¿Þ7rаVÑ…VUew]b¡ûÙßþH KJÀVÝÎÌV#úL·Ñ“øÂG?L-ú1Ã{'Îì{¿ßçóù«Ôu=ËæM/ý³³$¡ÑTö}›,ôûÛ,µÕ¶(„”€­º™­Fßô®÷ŒÞûqxâv¿ß£oŒ´mû·Ío?þô pÈÒÿ~¹aÓžO—Øz}“"7YèÞö·…ÊjÃm P)![u;ó[¾ùt_7}‘ÍÀ‰]þ‹ÕúÖä|>;£°®ë[K¼2:íã~¿ØžÿæƒS‰ÑRtÞYþ¾Ÿ…îm[4%ÜjÛ;\#%`…ng~«ñz½úºÄçóù×Û'†$‚¶¯rÄš œ#Ö¶í|ð×ÍÕ÷l»¿©ÓðI…aý£éÕW_nؘa‰SÑn"9dæ& ÝÛþ¶BJ¸þ¶(v¸FJÀ ÝÎ,Wãñx$:Æçó9¼àG|þþþWâáß®IUUéÌ.¼àù|þ;E+üwX™ûý~½^ÇõíÓéäßyau]ÿØŸE‡Ù7µmDJøïf¶»Ð¶mÃ_ÂßÓ MÌbÛd¡{ÛßVK WÞ¶e×H X¡Û™ëj¤ŠF=-45qºYxûgÀ ñ|>§g=ŸG(Ùaq‰'*~õaÛ¶þ×_è®ö·åÊj?Û œá)!+t; Y×ëõUÒ^<"¢išªª¾ŠíÎçóãñ˜˜°„3"£ü„ƒï÷ûÛÅ…µ­ëzDº4<ŽÜÉBw²¿-]VûÙ¶% ×H `^mÛ>ŸÏªª.—Ëœ.ü3ü1ü¯ð‚ÿ³wG×Éã Û@O ´¨!-Ð-p;w´@ ihh!¿Ö°&¾ [–lÙÚ{åâœwc,Yz<ÏŠ§Ûív>Ÿ‡ÃëÒ³Ÿ“¹\.ÙOæz½þ¼îkXùñññóÒ¹’߯Ž}³á¿†ßIˆ#«zÑšë[¡*´ÔµØ )!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ­‘@k¤„Ð)!´FJ‹Œº]ÔFPà¶ñ³”Jº]ÔFPà¶ñ³”ÖÕ5ïv»ý¿N§Óår¹ßï®$n¢ª†£ÉlãQ“QÃ`óãg)!¬½kÞívÇãQ\ˆ›HJk£šŒ›?K `3]ó~¿¿Ýn.lý¥ì²Try •ŽBg÷ˆšŒ®`óãg)!l¬k>®må¥ì²TryÍñâQ“QÃõ ´<~–ÀöºæÇãá W[Ê.K%—×/î55\@Ëãg)!l²kÖ\Ê.K%—×/î55\@Ëãg)!TÕ5÷ÿÉý~¿^¯çóùp8 ·ZÊÌvyÍñâQ“QÃõ ´<~–@U]ó¨#\.—Ýnç…Û.eVW: Ml¾†ëAØÆøYJ@M]Õ???ÍvÍ Ç9N=]óõzUµ6Pʬ¨t:š Ø| ׃°ñ³”€jú©þü´Ù5§êr¹tuÍûý^íÚF)³–ÒQèh‚`ó5\À6ÆÏRBêè¤þ‰þ4Ø5'íxÚãñèêÏç³j¶Rf¥£ÐÑÁæk¸€mŒŸ¥„,Ý=ýóö§©®yʇCô˜ŸŸŸjÚfJ™úKG¡£ ‚Í×p=Û?K X´oúgàO;]ó”~}}ey4áãñ‡:Ná?>>^Çóù|»Ý²_–竇ã‡WÙív¿_:üßçK‡_¿Öf)SéŒ:l¸‰ž÷ÚŸÚþSÕ×{yÃMz¹\‡Cx/¿ßZhUÂ?†ÿ4ê.¾ßï¡Íùüü|=ZøÇðŸJo­Ž};ÏsÿʱtÓÔuIŸæyUÿ\‡zš pò×ë5\¥góþú~w1Ï‹9ÿvÙ¸—íÁ£#“g‰¿öæá$§Ÿƒ”pKM\#7@mÓ RB–ë˜þõÓH×<倷Ûmb}½^???Çöþ¹¦èǾzøåð's–ÂÏrt~¾Äˆ«h ü3 þt¹\Æç5žNX¾͸Ã潉æ)!¿üx<Â]-‚W¡Ö­«á ­A×Òæ×·övÎ9ÔÉ®Déð¢Ùs¥g0÷:±ÜÓ›¦äKúœÒÿ¹‘§4,Ó¥g,˜Ð­üy/S‚‰mß‹ôàyPÄSâ×9kx¡dÙaÃüM\öqWµÃZ€µLEJ X¨Wú'á§…®¹Pwÿö¯×ëÀyø±Ýý~O~õð‡Sfr^®Ëåòv¹Äˆ«h ŒNáÇ,Ç;«:¢S‘yo¢JRÂè›};‘^ɇþwÊ=T¡Qo-ÜY]o-´'£æ®““î.Cnüžö0Ë¢›páùàkžµlJ˜ñvK.Ömß‹ôào„Î?í~‘N¹Ciâ²»*Ö¬k*RJÀ]Ò?É?›ïš u÷ý•0Gšq0p¹\jž@¾^¯çÐJŒ¸ŠÖÀè•ÿøøÈr Ç–Ht6ïíAV—¯NQ5=ï®kÓã„·öx<¦,@›†˜>Ã<½ÈB…Yð#[U)ar*±íûq‘¼çÏçsòK÷|a P-m9%\°‰Ë>îªmX °º©H)!³÷GÿLüÙv×\¨»ïù“äU*Y_}ÈÞS£Ja쪨#®¢50¼Á苎š‡ï †Æn:šv&ëJ ³Ì[.þL¨®³š~/ÿ, ÎMIpzvož3²Ìò Š-¥„ϯ1Œ½¶}?.Òƒw]Š,©S®ò-ñ·›I —mâ²»ªÖ¬q*RJÀ¼Ñ?Y~6Ü5O9àý~ïú~~ןŒÝ0ï` ûLNBPØõn·ÛØ5&%F\¥+at;ÇQˆê©BÃ'{£3–CV5®(%Ì2þ¿¤g>ÎÐp%ì ½DßùÖ€<– ïüyrdY."\uJ˜ÐÎoø~\ªïiä ‚gJµ™.ÞÄewÕ3¬XéT¤3öDÿdüÙj×<å€]«ººfËûì‡pÀ?3?áÿ†¿:N=›<Ûþ}ÉÂ9‡_øó8˜ðÃ?öÏî†_˜^ ióó%F\¥+at>-”ïð#ôÌé __½àCž¸¢”°+bøSÏï÷{¸ïúôe“4üÝ… ïå÷cCŸ·pOé¹»Ž6*ïþiâúó‹gyýNÀŸEN¯çLµBªÿ4ÂÑÂMúç4Âÿÿn¥Py¦<Ì+o'ÕUíŸçÿ§&?ßÂÛ‹9öØêý¸`>ê’†:9ö’Žêƒ¤„£Þ{ M\öqW%ÃZ€õNEJ ˜«ú'ûÏ&»æ)ìúuׄ[ׄOøý³=·ÛíõEþaOLð;ˆ ¿Ð“&LÿN{ÆQPåã¥h²<|Vÿ|ãðU6Ñã!ÏÄË[¨t†Ô–p—õ×óð_»è¥m®;çgŠ·ñ¨uÄÉG»Î+´{]×|H»ôÝûå‡áÁG8®Æ9üûÀð=Ü›=y¶äwú®À¨Æ¹çÙd£Šnõ~\°{=ù…{¡ÿ4Â%íÉe†ÜnÓkéŠz,u¦’&®Ü¸kÁ›`ÕS‘RB€–:‚üT•QæíI»ž1×µÒ¡ë×ióº\.?SOC~¿kžjÔJÀ® GíW6j›»×o¡ÿ,ÞYiJØUmþùÛ­½ÎËEëÿ]cJjËÀËÒ“-ø4´·ëž[O@0µÛu´Qª«>zêVOt>ðdºNc¿ß-÷‰7x–”0¼ägDö„ùÉûqÙüím;ü¥»²ìáÙ«”puM\¡qײ7ÀZH šïD{[N {æýýèÔܨɽW___=Ï@ü=÷2}žê©ëkíÃ5p)Sò$pýã¥èªÌ{è½ÝØp`A$G½ëJ Ãå»9a×´gÂ^š¥?S„"5ÛõÕ¼GÞL?®Yô! l×i$?fqÙ”pº® 2ü+%›¼ìÁ{.éçççð5€ý}ÐÄÈiž¾®”°’&®Ü¸kÙ›`-¤„Íw¢½Í¦„=_íîš!‰.ÐH˜ßK–Æî ø#º†hø¬N®¥L3”r!Ñ|yÈ ük­{-Ù!ëA¢ïCJø½ª9ÞáK–†Ü/g>+yw] 3m`Eúî˜aûœÓþÛjH»=±5ÌUÉ+i¾¢  †w›¼ìÁ».iòÍ21–®«‰+7îZö¦X )!@óho›)aÿ³áº&[–êÇ£›²c[×–zwäZÊTº”ˉ†tCfà_§î£‡z{œèdãÀ5>-ÌñF#’ä•eU½»hÑ'¿µ)G‹æS.rW»ÔߪtÅ%c—¼å*¯Jš¯hÃ2|géMÞËMöWX¯ª”pEM\Ñq—é)€)“`RB€f:ÑÞS® ÐÞ~K|©~|âzŸ¨)+Mò.3©ç:Ïp’¾·ÿœ´ý2ÿÛ¸6Z%æ¹¼«˜ãn«;êá›5Ô–áÍWòönS²¤h2%›ûNZ¹-ë‰ ÔRÂÅŸ®Xáý¸±”°ëËN•×𥄕4qEÇ]¦§’GMRB€–:ÑÞ¦RÂÛí¢8·¼ÔÕ¢¯;qK¨®85L+­b¼ÍéÆ.zzÎ ¾N¡¿Ýtôu¿á±H s¼]ߨê3E omÊÑ^+áôÜ'úÖþÛ!º›eÂSÃr•W=¯žwQÉý¸ìãK\håcI WÔÄÍÝ|†/@¤„Íw¢½u§„·Ûíz½^.—ãñOû3ÿÓÿ­ìè\Jo›6q㸋L0–(åEDWÇôg¯òŒz£‰mÏq¢¿?ü!_RÂJ>S¤jJ¾ŸñhÑ?L~\WÁõ´±y¯F–ò’V{?.Òƒ­Ño= é ¤„kiâJ_·eo €µ4߈öVfñ6"üî˜‘Ë»Ùæ«èƒÃ&n7úÝM«ôcr)×Ö8Dw{ë_Ýn4úŸú¿Ì}NåðmЙãÝjJXÉÑ¢+b&îÅ×u>=û©FOcàÎÉ…®ð</ô8×ÏçÓ¿öÿ‰Nû/þ.j¸éÁ‹^è—UJ/Lk¤©¤‰+}Ý–½)ÖBJÀ¢ÝP9Ý&»æ!áwǬÑï|­Ä¤Jtgâ~z=ogÁ¯f;~¹óì™ëëÚnôiÔ¦£Ñàx¶Ë+%\üdj8Zô;ó_«è½0|]m‰+\¨âÝn·ÐJ„f¡?ÌõÁs“÷ã"=xÑ+°È´FzJš¸Ò×mÙ›`-¤„,݉‹wÍ£¦”‡l[x½^sM­ì÷ûB_h¯mç«ÙŽŸKôÑ„]Ï‹ìÚnôiÔ¦£¯UbÔâ))a% תm—æÿ¸T¨y¬'_{>K÷m¿#%¬¶/z’7$—®¥‰›áZð¦hy*RJÀÈÎHDXªk>]™N—èf]v»ÝçççÄÙ•èÎØÓŽŠ&S î|5Ûñs‰îöÖµShÏv£Ñ_è9Ôć%I +i¸V}4)aéÒ ï¢ÄEnü~œ¿¯³å”®¥‰›áZð¦hs*RJ@j$"ÌÙ5ïv»ãñ˜´u=ÆeÈ놿½\.£¦VŠj›­šçø¹DcÖè##C‰¿Íõn:}bøÇÙ.¯”pñ“©áhÿ›×ŠjãôS ÍE¹„Âý8s^g]•®¥‰›çZê¦hj*RJ@Ž.ID˜Þ5ïÿu:.—K–UxÑÖZÆ(%¬y¼ôºÒ3ºÛÛë£^s½›ŽFŸ"4çå•.~2RškãÄSŠ~ @J˜×œ=xuUJ¸–&n¶;h‘›`ÛS‘RBÊôJ­G„ß5Í4^¯×éÏŠòHD)aÍã¥è7ð_íÏ »¶u²éèëTÞ¨‡~K ×ßpI k®SþvTDš‘ý~ÿÜý/8ŸÏ×ÿ,û.VѪÏÖƒ×YW¥„kiâæ¼ƒæ¿)V=)%`¹Ž©éˆð»¾™ÆËå2qk¸þš¤„5—¢+ûþ<íu»Ñ认ßÃ6}MG=”ð[J¸þ†«Ú”p3=Â"Jh(ÞNÑ?7÷¸Ã°”°†¼Îº*%\]í|æ¼)ÖBJ@}}S»áw­3÷ûýr¹üY/6jFe©·,%œ^ôo¿K?d»Ñž£½½8£JømŽwý —”°æÚ˜ü·¡ÝèY6˜ðä/)a =xuUJ¸º;óùÌsS¬…”€*»§F#Âï5äG÷ûýëëët:ú2vÏMÑÕ%cƒ¡®SNG/^ ë/½.î Eÿûn7Úu´ß›Ž^¯×„òÊ{yÍñ.~2Õ¦„ó?”j3)áëŠãßëÓæH kèÁ‹^×ᵪ­n¬ºYð¹{ ÞAån €µPkÕbDø½¶üèû¿ïc‡·[ÉuÍG§eþìi™f‘ Æí•òëƒÿœíÀíFŸú7²%iéË+%\üdj8Z¹vi”B§1†Ý»8í_ð]l©ïÎÕƒ½RÂr‡­¤‰«ðÊxS¬…”€Š;©æ"ÂïÕÎ4>]¯×ží›ºž.w8†ÿò(¯‘ÓÀIi)áo___=‹=_ÿkÿ:ÐþMG_ëÏår™ùòJ ?™ŽmÊæ_<²™”0ÚÎ'¬^ö]lµïžÒƒ½Ñ]jK/ll¤©¤‰«üšxS¬…”€ºû©¶"ÂïMÌ4v­éúþtpÊ“Ñyé¥vN[o)G· ü™ûs‘‡Lû÷l:úúÕý„ Ф„ko¸j8Z´]úüüœùÂFWòNŸÉŸ?Cy½ëÓ¾°ì»ØvßÖƒ½Ñ€fHµ‘®¥‰[Å”|S¬…”€ê»ª†"Âï­Ì4ŽZ6ÝRlâ“§ènQC–áH ÿxáÿ™KµÝèS×¶¢¯qdZ5®½áªáhÑviþ-æ¢ ¢§ÏäÏŸ¡”xš”°†¼èH®6Rµ4qk¹ƒJ¬†¨‡”êïšW÷.n·Û¨7ýåþ+³ŸÃœ¥°ºRîz4áØíFŸº6}=ZÚ’R)áÚ®JŽýÙwäëšÉ_°¼2¦„«{›ï»§ôžÙ¯@t³ëÊëÆZzJš¸µÜAS†”õ“@ý]óæßHtW±‰›ŽF¿ø=p Î")áÄe5EE£Šp Û>E7}Ý-mCÂs¼ÓKGJ¸º£u=Žjæµ6%¾D!%”f;U=8¯¶”°P2ñ°•4qk¹ƒÌt&%€ú»æÍ¿‘®g¾$Ï€EW«ý~þݲ¥¼jUúZjC¶}Šn:ºßï³ÌUN,¾B¥#%\ÝÑ¢K™FÕó,J|‰ÂŽ£RÂìo'ïèZE;°Î,X7 õ %[I·–;ÈL°aRB¨¿kžíÕw»]–¬*º5Ó~¿ïé±Òã5oµÒ­t)DOï|>×\3_ÏùµÈ†¯oêŠqv–Ë^|…JGJ¸Æ£EÛ¥äU®Ñ¦òm=Ïþ%Š®½žÑÛjÊe\ä]T~?.؃g¿¯ëÍG=‘sÁºQ¨)tØš¸¢wв7ÀZH  þ®yæW?—x¼nùöÛé]S¾ Kf¢{Žšõ*] ÑÇü 1-Ó)çβœabñ*)áÖÕ.MŸE<?÷TÚ[H˜ /ÚµÇ`é %Ú,§Í±‡îi©wQùý¸`ž÷ tuâÃãžëF¡¤Ða+iâ¾ ?Ïq©›`-¤„P׼ȫ'OªD¿q=d¡Y×wÚG…]³‹£&ÓJ—B×¼\®oï—ÐU¬ÉÓe¯›Ž¦m›½ø •Ž”p¥G‹.áõˆ´?~Ož<™®Œ~TÛØÌ¡tím8ê¶z½tRÂzzð,+^Cwuâ£jû‚u£PRnØPC÷]8%\ê¦X )!Ôß5/øêûýþr¹ |<\Ïî5#]Ï!zn>ùvAø…žÉ®Q“9¥K¡g¿Íš'úÇ{cÏüí¦£i%œ^|…JGJ¸Ò£õWÔÝn7|–>´QÑdHÓÚu¡Ñ{;ïΰë;sf(]ç0$‰’J —íÁ»JäóósàW> ÒSQGõ ÖB=H¹aC MÜ÷,)áü7ÀZH  þ®¹†ÁÇÇÇñx¼\.ÒºÇãþ%ü{×÷ÿG-(è__Î!üŸÿ7ücÿö•cŸÝ3C)ôšáJ~}}ý¾bᇷy:žûì-U9{vùKÛ÷¬§Ô’J˜¥øJ”Ž”p½GëùÃïŠñÚ<>ëFhú›Ç!çе¯«ZÞn··ÍòÌJÏbÆÐ€„þåÏ[xv.áêõ´Ÿ_ç祄Uõà=W`ÔɄнl'^g2ÿ°aæ&®ôTϰ`-S‘RBàÇõz=ŸÏ‡Ãa¿ß¿nuµÛí¿‡ÿ~gúÔw—Çãñõõu<Ãký™{ž@øOáVý5ïpõN§Óë ×üùÞ>wŠÒ¡\ ù:oÿSC.—KöyæpÀðÒŸŸŸZæg›\âËÝY¯‹Ãû*Ú­èÁóöàoçž1M´ÿ©±[]«U¨™§cZ°‰Ûð° *RB ïÄ‚Ë+ÌK €ä‰—V:˜—É . ¬t0/%’'\Xé`^J$O,¸,°ÒÁ¼”HžXpY`¥ƒy)!<±à²ÀJóRB ybÁe€•楄@òÄ‚Ë+ÌK €ä‰—V:˜—É . ¬t0/%’'\Xé`^J$O,¸,°ÒÁ¼”HžXpY`¥ƒy)!l˜”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBXdÔí²€;ÈQPÛhVJ¥GÝ. ¸ƒ\ŵf¥„°®®y·Ûíÿu:.—Ëý~w%qU55Hw ¢8”&«ÍJ `í]ón·;âBÜDRBp_» Ô\J€ÚF³RBØL×¼ßïo·› [)»,•\ÞB¥£ÐÁä‚(Žì^®&¨c-f¥„°±®ùx<º¶•—²ËRÉå•‚fÓa-ýŽ”€?)!l¯kþøøx<®pµ¥ì²Try¥„ ÙtAXK¿#% ÄÀOJ›ìš…5—²ËRÉå•‚fÓa-ýŽ”€?)!TÕ5÷ÿÉý~¿^¯çóùp8 ·ZÊÌvy¥„ ÙtAXK¿#% ÄÀOJUuÍ£Žp¹\v»gn»”Y]é(tp¹ ŠcE/­Ž´<š•P¸û)ÿ³­®9á8§Ó©§k¾^¯ªáJ™•ŽBw ¢8VôÒê@Ë£Y)!…»)Ḯ9íP—Ë¥«kÞï÷ªá6J™µ”ŽBw ¢8VôÒê@Ë£Y)!…»)Ḯ9ùhÇ㱫w¾ßïjâ6J™U”ŽBw ¢8VôÒê@Ë£Y)!…»)Ḯ9ùhÇ£«w>ŸÏjâ6J™U”ŽBw ¢8VôÒê@Ë£Y)!…»)ḮyʇCô˜ŸŸŸjâfJ™úKG¡ƒ;ÈQ+ziu  åѬ”€ÂÝ”p\×<å€___YMøx<¡N§SøÃ×£Çãñ|>ßn·ì—åùêáøáUv»Ýï—ÿ÷ùÒ᯵YÊÔ_:£n¢ç½ö§¶ÿTõú/ãü-FxÅëõ^ñÙP]M_~-œÞÌ/‡3¼\.‡ÃáϹ…ëþ1ü§Q-X8ùpõ>??_þ1ü§™ßÝOýÚ>‡«=½”Ë5›áBEËåy1jKÑþ¥«n¤dC·Ï¨3 ­JW±†+3j½ô[oýnœÓ2Ù¼áÖ»§ŠfYÇ”vÒä ?wÏÜl©~6ž.r?féU8˜ÿ“µ”€ÁÝ”0ÏLÑÄî¾ÿ¯2‹ŒƒËåRí´ÿsÚgàŒV‰WѽòY®á؉έ½=ÈêRÂáÕ)ª’ p‘#oÙ¥Í6÷œ×†Ï ÅNlʒɴưç­Ïçä“é‰AgèÕœ>i?ý¾ wýÆ>wÏßl©~6›.x?féÕ6˜>m(% d÷#%,5Ñ4ª»ïù“äY_}ÈNP£JaìÊ #®¢50¼Á苎JOºÂ‘±›Ž¦ÉºRÂ,³ˆ‹?¡i©#ûÇ–±³«D¦_“ŸÑY¥\³è¹fõ³\ç±ï¨g î9¿ˆ’åk0U}î^¤ØRýl3%\ö~Ì>Ò«j eÚÐx€’Ý”°ÔDÓ«ûýÞõmù®?»?^ÞÁ@öy•„ °ë-Ün·±k¾JŒ¸JWÂè–†£ØÔS…†O½F燬j\QJ˜e6ûIÏ|ÌhÁ£Ä'—±-FWÚ8ýLBõøÎ·åy´éomʺ×)+”§7†y#‰äìµ\D¸Ôçî¥Z€-ÕÏSÂÅïÇì#½zÒY>úÏP¸û‘–šhzÕµª«kƸÑÊápü3þoø«ÓéÔ³!ÞÀ³íß%,œsø…?g ÿ7ücÚ~az)¤Mí–q•®„ÑÙ­P¾ÃÐ3Ã6üÛþÑ >ä ‰+J »"¿?õü~¿‡û®?@_ê¡E˶]0üú¯ß¿üx<¿„ó µ¨xÔÅ^²áuëÿ~dê³ùê9™ž–-áh£²þ±•6ܰc+í¨VebcØI|||s×ôW×ôKøýs/·ÛíõEþaÏTùïÉð¨ð =3êÓ¿ažqTùx)š,_ˆÔ?û7|Õ[tþvHÌ1çlmÞJî²þzþk×"µ´Íu§[°Åø}UÃe ¯8ê6ïyæÔ¨ewY¶Âµ†:ùhY¶üý3'>Ÿû :TÚžyï· {–3œa×3¤sùîýËð,)œFW• ÿ>ð¡’÷Ô–¦Z€ÍÔω^®&:r%÷c¹‘Þ²Ý"@¡ù )!À¦{?u¥“y{Ò®gÌu-–éúþs¦jßÿ®û™òû]³F£VvíÎ7j÷°Q›¾~'ügÝÇJS®j3ðÏßn´5p–,Z†üíSÂP[^–ž aþ§.Ûb<¯j¨oi/÷Ý;Y=ü˜o×,—ž¨"a’¹ëh£*ÉÛ“~¨®`kxº]¢Qõ ³žï? ¼ª]§±ßïÇÞ¼[ém´›©Ÿß¥„•Ü…Fz‹w‹Ùç+¤„ ô~¶œö<Ð*úûщ²QSm¯¾¾¾zžø{&dú¬ÑSח̇jàržäP¦þñRtUæÀmßî‰7° ’£Þu¥„árÝ,´krì~’Ó-ØbäÒõäÖá_NèÙ4oÔ´p×™ä=Ú¨µgEðð5Vý­ÊÄ)ýÙ.ÅSW01¤ÎwFÂó"ëéG–m¶Q?§ÿùºRÂJîÇr#½ t‹@ƒ¤„Í÷~6›ö|Ѻk¾"º¬&a¶-A4Z»?Þè:šás,¹–óÌPÊ…Dóå!¹Ék­{-Ù!«3¢A؇~¯*%¾„pÈý2q2Á‚-FFÑ…-ߌ%Ûµ0ãÑÞD=omìc^L d“oÌè¤}ò»ˆ¶C:—èiŒ}¬amýȲ-À6êçô?_WJXÉýXn¤·nh” ùžÀÏ6SÂþgÃuM},ÕG„7eÅ®ÝØ.¶Êµœ§t)— é†ä&¯KôPoú¸ænE)aòÑ¢ÁVò¢¤ ×ääÚ>|âŒ!Zí“‹uúѲ—o4 xJi'~¦Ü)]K×Е@]G\ÛÝ·ì9l ~fù󥄕ÜEGz&Ä€5’4ßøÙ`Jص!áÛïl/ÕO_óòjÊú ¼Ë¾V:‰”v’¾EÿŒZ^¿Zÿ6®V‰y.ï*RÂ趺£¾ÙNM~kâ#Þ2^„hÓ¼ÑÜÄô³Dùv}}¥ÜÉD;‚)ÙÜwÒb¨è ;1Ö—n ~fù󥄕ÜEGz&Ä€5’4ßøÙTJx»Ý¢&§%—zØYôu'nÐÔµv£†IžUŒ—¢9ÝØõ2Ï™º×ò·›Ž¾î¨9|F½…”°ë›5|”˜ÿñˆË–Né”pÁ£•¨cÑÍr‡Äi'óúrÓÃôèStûÛ´è»Nx[mýȲ-ÀêgmMPé#Wr?Îßׯ±[š"%h¾'ð³î”ðv»]¯×Ëår<£³[fcú¿#}œJém §/x>5ÏtßâÇÏ"ºø¥ZûõOžQo4±í9Nô÷‡?tOJ8›EZŒÙ>Í_²S¾ÛPâh³=•lÈÝp2Ñ+ü´þ»¯§Úç-Öªú‘e[€µ×Ï › ¢G®ä~,}Ý6Ó-M‘4ßøYAJ˜ÅÛˆð»c~,ïf›¯¢Ïš¸ÝèSto«¦û?~ѽ×ú¿ŸÝn4úŸú¿Z}NåðMÉZH +©E‹´c…3¹þë|>ŸþµÿOt:wÙ)úªŽV¢ŽE¿~PháOt‘ÑÄí »N¦gcØèi ÜþZ °áú™ëÏ×’Vr?–¾n«è† ¤„î~VÖm¬k~wÌáüÎ×JLqDgT&nÅÖóv܇j¶ã—;Ïž™·®íFŸFm: Žg»¼R‰·Xéã­Ûíê[¨`ý!`®0RÂQæ\øý¢Èümx´A¾8Z °Õú™ëÏ×’Vr?–¾nuv‹ C#)!…»)á¬]ó¨ÙÈ!Û–†^¯×\ûý¾Ð×ËkÛ‡j¶ãç}4a×ó"»¶}µéèk•µîFJ8§ù[Œ.ϧ²¾=)á²u,y‹é„“‰v.óæ-ÔÇiÖ^?sýùZRÂJîÇ®[=Ý"À‚S‘RBÞu?R™ºæÃáЕét‰nöØe·Û}~~NœëˆN§Œ=í¨h2µà>T³?—èÞk];…öl7ý…žCM|t‘”pNó·¯ÂÑJÌ?/R›O ¿g\ø#%Üv °öú™ëÏ¥„µ¥„5t‹ NEJ ÖýH ËvÍ»Ýîx<&m]Uòºáo/—˨‰Ž¢ƒ‡ÚæŽæ9~.ј5úÈÈPâos½›ŽFŸ‡þq¶Ë+%¬¼ÅøSñÊÍ7m¸W­”°þ{!WJ¸™n½å`õ³Î&h-ÛŠî 9o €ŒC#)!…»)áò³£Üï÷Ëåòg½Ø¨ù¥Þ²”pzÑ¿ýfûíF{ŽööâŒz(á·”°ŽjS®ÅB ìY6˜ðD')aý÷‚”P °±úYg´–ÆmuwÐ<7@Æ¡‘”€ÂÝ”°–Y‹´‰Ž¯¯¯Óé4ê«Ñ=;&E× †ºN5",^ ë/½.î Eÿûn7Úu´ß›Ž^¯×„òÊ{y¥„•·¯kW¯L[p!%œ­Ž½Þã¯MJ®“‰þÉüÏùj0%,Úl¦~ÖÙÍÜ-øÜ½ï r7@Æ¡‘”€ÂÝ”°ÆY‹ä¹ŽËår8ÞnØ5{$ù³§eš¥¦û6Vʯ üs¶·}êßttÈ–¤¥/¯”°ò#º nZUÉr1¥„Õ6Ëå:—Q F³-ÀfêgMP¹#Wr?Vxe¼)2¤„î~¤„+˜µHp½^{6SêzºÜápþË£¼FN£)áo___=‹=_ÿkÿ:ÐþMG_ëÏår™ùòJ רb$¬9Íu1¥„£Dw‹-´p,Z»æ_#%ÌÛl¦~ÖÙ•;r%÷cåwÐÄ› ãÐHJ@áîGJ¸²Y‹QºVút}?:)7eYÐhš°Ô>fë-åè?SU.ò°¦gÓÑ×/Ò'lG&%Üv‹ñZÒÒä\SJ8Jt|Hñ%œL´s '0s .ÇžŽ4Ûl¦~ÖÙ•;r%÷ã*î ä› ãÐHJUuÍ«{£ûD7øš¸2è)ºwÓRÂ?^s™Ÿ™½QÛ>um+úG¦U)á¶[Œ·’ÎVÇ’‹/ád¢Ëü»öEWµOGšm6S?ël‚ʹ’ûq-wP‰Uó£†FRB¨ªk^Ý»¸Ýn£ÞHô—û7®Ì~s–ÂêJ¹ëÑ„c·}êÚtôõhiKJ¥„Ûn1f‹)ˆm§„Ñí‹?>>ÊLô¯fÞä°+1Z˜Ø_o ~VØÍßm-µéhåwДA,@–¡‘”ªêš7ÿF¢{|MÜt4ú5ìË7I '.†**:ËN8a»Ñ§è¦£¯Û‘¥m#Yb¶vzéH s½)á²÷”ë<åÁdi'Óõ„¯™—/•ø&L³-À–êg¹&(ˈ"û‘+¹×r™[–xH ÀœÀœo¤ë ,É“QÑÕj¿Ÿ·l)$o…ZU¾–ÚíFŸ¢›Žî÷û,3‡‹¯PéH s½;ŽÎv´yöXvi']6ª±Ê¢Ä7a¤„¨ŸYþ¼Üˆ"û‘+¹×r™[–xH  Í9Ýn—ef)ºQÒ~¿ïé±Òã5oµÒ­t)DOï|>×\3_ÏùµÈ†/éŠqöyË^|…Jg“)á"-F´€Ò–>u}Ka‘‚ØpJøº‚xÔãù’O&Ú¹L¬3jïÛÆ*û7a&VÚ Œ6S?§ÿy¹E‰#×p?íC—½)2~r”@U]ó̯~8&.ÌyÝ:òíwÅ»æ<V[D÷5Uº¢ùb."Z¦SÎ?:%›eqÁÄâ+T:›L i1¢7xÚÜi8í®ð¤„yO¦«Y>ž|2]Ëô`âñxüTÝ´óO¨·áE§WÚ Œ6S?§ÿy¹E‰#Wr?~~ñR7@ÆOŽRB¨ªk^äÕ“§8¢ß²Ð¬ë棂®¹¾QóZ¥K¡k–,×wéKè*ÖäÉ«×MGÓ¶‡Í^|…JgÃ)áÌ-Fמu£ è÷|²”pÔŸ-åp©»šåQm{öÅV£ž:÷¶þ¼ý“®ú6ê"ô$,í´Û«Ÿÿ¼Üˆ¢Ð‘k¸¿ §„KÝ?9J  ª®yÁWßï÷—Ëeàãáz&Þ‡¬˜èz*ÐsóÉ·_é¿Ð3õ4jj¥t)ôì·YóPÿxo왿Ýt4í¡„Ó‹¯Pél>%œ³ÅèúFÁæ!ù ”°ÿ–ÿüüâ‡f¹«°ÆÞãSÞKkÎpxêÞQ4TRñºN Ôö·QB8Þ+ÙZ °±úY®zOQ:r ÷ã÷,)áü7@‰Ù)!ÔÐ5×00øøø8—ËåOZ÷x<¿„ïú6þ¨¯÷÷¯/ ç~áÏ „ÿþ±ûʱљ¡zÍp%¿¾¾~_±ð¿ÃÛÛäßàÏ«îˆþfy죻&¾—žo¡ü~;¯5öyƒ‡·Ù_c‡œC×2Ø®¶åv»½½SÚl¶W?Kðfïàšm6V?gÁÓêL¹#/~?ΟÎ\'jøè'%€\]óª 3çQ"ÂÙJaÔ²¦FV=&LÛ|¬gÛ±±ë8²_öÒ‘æm1Ç ß:][A.R-¤„iÓÝY®Ì”È#W³Ü¿„m`®=±Ònc̰¥ú9½4Ë(ÊyÙûqE)¡ˆXãÀ^Jy»æÙ^=ˬûoŸ ôªëÑÒó*ó”ÂãñH›.^°~FkHÚv£O—q•+¾ì¥³É”pÙã~¿O \ÂÉÿ„ÑRÂÙ>*& ו Õ&ãBÔ§QÏ mKrìò{µ`Kõszi–Q«,x?ºƒêH,;´–@Þ®yÎøúúš2ù{ßï'%¸ÝnÉsGá§<Àe¶R—(á=.X?£Ë<Ó¶}ŠÆÁ£¦ÜË_ÞÒÙdJ¸x‹‘¸œN§ß/'%þç—Ë%m<”Ô”N!ï• õvúdþó!˜i}M¨ ýÚïUÛZ€-ÕÏ\_n)4¢(=VYä~,wÕ3˜24’@U]óü§ñx<.—KÂ,Ç”YÓ¨ëõ:ê4Â/OÙ¬r‘R;«¹`ýŒ>H(m»Ñ§è¦£SJ˜½ør•ÎVSÂZŒá3Ìá×Îçó뼫”pìŸ*îÃáPg³Î*aƒëg¥Òîý´~_=Ú¯i¶T?3–f¹Eé±ÊÌ÷cé;¨ž4@ÚÐHJü¸^¯çóùp8ì÷û×]§v»]ø÷ð_ÃïLŸ5íòx<¾¾¾ŽÇcx­?“TÏÿ)üª¿t®Þétz}ƒáš?ß`¸ÂÓg2Q:[m1~ÊèuÖççgѪ©ŠZæËåm™Ÿ¥þë*šåß•ö5ù¹ÍÃÛÉ>u^:TÑ?7ËÏ\]X0[ ÐNý¬ªÏš¡7\ð~Üð@`ìÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ࣢Ov¨ŸÐÚÐZJ&%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%€ÖH  5RBh”Z#%`ÈÑeØ)!¬Ëív»\.Çãq¿ß||¼v»»Ýnÿ¯ð;§Óéz½Þïw×éCD—`K¤„° ·Ûíx<îv»äîøóóó|>‡ã¸˜¤ ]€-‘@å®×ë~¿ÏØ/ïv»ãñëÚ2jˆè²l‰”ªu¿ß???ËuлÝît:¹Î "º,¨o°ùäRBXÜ××—nšª†ˆ. êlþ#¹éGXÖårùß\\m]Ô7ØüGrÓ° !áçççù|¾^¯÷ûý÷ß>ð___§Ó)üÎn·ÓM“kˆè² ¾Àæ?’K `)×ëµÿI‚—Ëåñx ?àív;ŸÏºi&]Ô7ØüGr)!,âñxôô§ÇãqT>øÇý~GÐM“>¢Ýèý~W¨aˆè² ¾Àæ?’K ¨¡êýi¨S®ólÇõz=NÇãqÿ¯èÉ?ÿSøµ¯¯¯™ãÎp†—Ëåp8ü9·ðá?…_~´pòçóùóóóõháÚùÝ…ëù¼ò6§}^íÛíV¢6æ:ùp­¢Eó¼ž?fT-u„ó gÎù5åVþPL¡zL/‘®WVƒPô¯5!ü§U\Æo)!Ôô‘\J@ }””°¶nô ~~~& >>>ÆÆsC®ÒŸß¹ßï]Û·þÞÎÛ“ 'Ü•þ^tJV˜÷­…KýõõUôdÆzæ¶]kf».i¨uÞ­á¬n„\òØW¿<ñ2Žª·Û활¾Æ¦¡@K|Ñ_@ÆüRBï ü´Ò)׳(ãØ ùa‹ý#p­ŽÇã¨3Ùív]ë¼®×ë¨H«ô[˜þÉeÒêOö]¸,Ö» ¹ê¹ B­˜—ˆíî÷{ò«‡?L(Ö‡!¥\â3ˆ.2~ä—°x%%üQÏZª¼Ãƒ´è§g¤ñõõ•|2‚ÂpbS–L¦…=oí|>'ŸLO šv2c…‹9=V{-£EœN§GÈ¡^-п=ÿÐL LK4&ºLH#% ÂÞiðϦt%)?{ômoØðññ16(ìZn™°ÎîŸeV×ëu‘H«ë­e‰Ø²\ç„:®C KP³˜^Ǧ\ÏŒ¯žÐ¤ôœÿؼ%½&¤‘PaïÔfJØ“LYµÃ†±EWÚ8ýLöûýw¾ÅbÏ£MkÉ»t¾^¢é'3öå—]Q8v'Û¼#äìe–ûîYÄc«h‰ÆD¯ i¤„ÔÖ5üÙŽžíÖÜÍ6løüü??óŽÊÂ<ŸÏýÙý~†/J8¢ 'ÙµÀ3÷Óè‰ÆFÅYtUÝp&#Ëp³¼®Fø‡=ùàÛ+~¡'+~ —þ\à,BJ +ð“õg’·éÕçççØjy‡ Çã1yËÇžä(WZNo`jÓ¥%d@]GµêííÉL¯HÃ÷™œ8¢ëÚŸsÔƒ{²9—v-$L»Âø¹ †ü~×-3j%`×>ºÃ÷¡µ—éëÊÊp ÃVËõ H#%Ðø©'%ì_Nø'.|Ýf°~÷û}bäѳÖrTjÓu&y6*ë)ë„Sv­ ˜¯MÑe¹O]AáœË £‘ëÄøúúÚívo­kKÏ„+Ùµ6sà¡®ºMNo}‚€EH t~êI ¿{ŸNØi=øçÁyuŠ®2¾9g×ò¥„x¢k`Æ£…7;qT6jÉØoÙ)#ºh¬–üF¢ëà†Dl¹DWóÍs£Ew:»“mx%s­º-Q߀¼É¥„Mu~ªJ ¿»wkn¿ß‡ƒ\.— CÃèþw>L;“hü4ê!‰y–}MëžRòÉDÓÉä«úݽÀ6yçÛ,£å^÷ëë+ïn«]WrÈ&ƹVÝÖv‘ qRB]ŸÚRÂïîçˆ%ØívÏ'…URẢŠ)C—´3‰æ•É‹Ô&¦Ÿß‚’®í:‹žL4㞸5nÞʼnYFË3¼îôÕ©KgÈâÄŒ«n«ºÈÐ8)!€®ÀO…)á÷O.ËÛ#ŸN§VÖœ.x´ƒ¨h’Ù%ŸÌë+ŽŠJ£¢Ûð‡«ë ±{ôu'Þ¿]ûÐ.R9ç<>0ü#¹” ©®ÀO)á÷¿Ëî2.*Ìø±ÃþöäĤÐÑJ ¢¢«ðB(t2Ñ‹0}Ñ_4²‹é(Ñç–~õéKS»¤ÇRBØ$)!€®ÀOµ)áÓãñ8ŸÏѨbÊ6¤…ëÎöú¯pΧíÿÓÿ¦ ]ò„–:Z‰AT(…´Uxi']ô7q»Ñ®óIÞv¬hÒšw¿ÍWѯLÜnô)ºéèÛàXJ›$%`ñ¾¨ò®÷ûý|>~~æÚ‰ôr¹L?«ÛíÎêp8LÌ1§ ]ò„–:Z‰ATò*¼´“‰&P冬óÜwÑè³ôÊÜh4™å†MÛ¾UJ›$%`ñ¾HJ˜à¹dït:‡ý~ŸÜG'?^ív»…—ÎøäÄ)C—¼¡¥Ž6[J8dãÊ´“™Rka¾­çá’†›1\í\‰aôJfY•™K `“¤„,Û­bÏÏU¸ßïÏÜðóósT7=6ׯR" š2tÉ;Zêh…Qi‡•þöõõ5j;ßpN £¹d¸Ç³4 Û·J `“¤„,ÛI yn:dÐ!O©{z<åb )C—¼¡¥Ž¶”ðóšó†êz:áÐ0üíår•}¿ —À&I X°*öÃÿw¿ßß®.’_Ün·F RÂyÎJJø*úÔÅQ‡ÃÀõ€RBŸ `RBì…¤„³¹\.==uø¯ý>*"Üívûýþ¹ébp>Ÿ¯ÿé~Lºä-u4)aý#ÌP‡§?‹3ÜKU†äƒK `“¤„,Õþᯞg«õo:úx<Þ&#Ï=o·[òðcÊÐ%ï@h©£m5%ÜÞ­ªúÄ­w?>>úðJ }‚€H Xª ’ί+ÚÿÞóW§Ó©gÙàØ®}K g9™‰‡•¾u¿ßCå»oOP¸Ô•”=É¥„”îfùᯞ}Gÿ_{w{¤¨`ô†0)˜‚1L ¦` †` ¦@ Æ@ ¦` {»–*ËZ¦i>d|Ïù¹+60à>ÛзÈý~˜?8¶\~¬°ìÆßm‰‹¨ëõ:6 OY™ìR…OâûÕÒglšæt:šc8pëÑììݹº?®j6÷¯¿s®ùþ@ùWr•€¥Ï?*á[dÁðɺ/,ߤ´âòc…e7þnP ³ìñ<ÊPZ:pÒ1òã­zû:ûr#Y·K¨„ð‘TBÞrþQ ·vêï{ýáp¨˜yT±+,»ñw[â"*{·Ø)lW&{ûÍ’÷Á®×ëÀ]IÏçsùq×÷âQÒ›TD•>’JÀ[Î?*áÖNý}¯ßív¯/¾\.³¯Ã ËnüÝ–¸ˆÊö©’ÍW·2Ù(™ÖÁq×7'·o_v$§Là}ÈöÇK®JI%à-ç•pS§þÝn7êõŸ4§®°27_ÝÊdïf9p_ÍPFMËÍŽäÄ9¼ìMP¼—©JI%€8ú"ÎÀ¥ÕÒÕ ËnüÝfê¦iFáYV&»`ð›ŽvÚ¶5ªÙ§7Ysý=°æûå_ÉUBxËIybøÑñxÌž©nA©®ön³õ”§V¯Lß3øL';ªÙ‘œxÓÑì|Æ’[¾¥Nœ³ Ô}%W à]'ååf]Ýï÷¾3õ@ÁqÇÑÕÞmÞ•é›7Z¸íªW&;19ŽñQ£Ú÷(Ãê£/-˜}ôÉVÞ9_ÕݘH%€­”w»Ýÿ<Þ7ÉkxvÒ~¿5÷ðG}ícʥ˼Bïz·yW&íEu³Æ¦¯L¶øLÜmžµm[xßÔY¤3Ëñ˜½Ûg:¾ÆŽäð"cåÂg.}…Ÿ]·óù켋R `›'åý~?c+ÌÞi°djRvÁºN‘~P_©T ç]™¾Í]¾GMY™¾<=Þï÷Óé´òåå#¦OœBûXóò)–}#YqßѾ]¢p‹,}…Ÿ½raÁª©„°å“ò~¿ŸVn·[vZYá Nûn 9j­žãŽJ8jñ±e* u_•–&~–ìÔ°)÷Ô}Ý…Þr`V·ÂìDÂ’G‘öM̵5ûv‰ò ·ô~_kþ)Pþ•_%€­”‡CÓ4|Õ¶íÀÂò)}¢$÷”ôA•pxøþþ.yrÜŸ¿Ï"ìÛXÃOŸœý³ô=ï§ÊëOúPÙÝøf×î Çsà(9ûž/Yxkâô‚bûc£\èHµÃ”¯$0–J¿ë¤¼ÛíŽÇãår¹^¯ÿLkºßïéÓ_¥ Ì|¼Oaæ¸ä××WúYMÓ<¯I·çóyàþ¢*aÅp8Ò¨¦±}ÞpÝhŸN§á->öîµÓGf o=¢nO~^0íKÝþ3ܸ·p`>ŒÙ#qø#NKLC1¼Ý^ñÏà§?Þ%F=õo…M0P3»ÿ#ñ<\ÝN’vûî—ŒÓÔQ  ÔIyl"ì ü~ÅV WÞ*nÛ8ËÈ ôå_ty¹…2\+Œ}²á › $+û®¿âß=|s€mž”»@0*þù;7jàV–åŽÇcz+•pÍ îÉnsÌ”ôóÁ•°b£Ì Ç&Â?«TÂdÔìcß5`›ÿîá›;T¸ßï—ËåÇ;…NñõõUø„»W·Ûmʺ¥ý¸)¢J¸ÚUÙØ.12iÏ™q.ê¨GjÎb–>þ¬ú¾õ诨Æ?þ¬û%ãôuTBØšÛíVò`Á±‘nÔcȲî÷{ÝdŸÓéô<{Q%,_ür¹ÔUª´¥ÆÎ]nd’¦i¦ç¶î9˜…ó›QZùêin§ñþ£mÛê䚬ºÕ®ðÓøT|@g ˜ñ+¿J[p»ÝÎçóÄ<‘¯ž»”Už{º4ùšET±‹_.—òÝàp8TO!\hdÒŠUÜ9³‹ƒmÛ¾÷xìfûV³ÇÍ4Œ£V#½xâ.±òþØ8îd3~åW `kn·[Ó4§Óép8ìÿÊžsw»]ú«ôšôÊé©h@Û¶éG¼®Fú“ïïïóùüö¦óWeÏ/è*ÕñxLƒüOCé6zúÛ‰SÕV“vδ“t;ókêvãôIÓ'ZæàØõö›>Q·E=Ò¶N¿²ûC·é¯Ò ~Ë.1ðKæŸO÷Ø=Òð.ú[>žJ°Ù«2ÃÀBTB€Í^•¢löªÌ°°•`³We†€…¨„›½*3,,D%ØìU™a`!*!Àf¯Ê Q 6{UfXXˆJ°Ù«2ÃÀBTB€Í^•¢löªÌ°°•`³We†€…¨„›½*3,,D%ØìU™a`!*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!D£@4*!Dóó? š‹¦ endstream endobj 311 0 obj 85952 endobj 312 0 obj [/ICCBased 314 0 R] endobj 313 0 obj << /Length 315 0 R /Type /XObject /Subtype /Image /Width 2402 /Height 2401 /ColorSpace /DeviceGray /Interpolate true /BitsPerComponent 8 /Filter /FlateDecode >> stream xÚìÖ! ðù7½i88K$5€àÄa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à°–F Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃÒÀa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXK#‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àaià°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡¥€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°4pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à°–F Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃÒÀa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXK#‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àaià°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡k·Ž ‚õo-ˆ-‚ à°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXK#‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àaià°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃÀa8,‡€ÃpX ‡à°€ÃrX Àa8,‡à°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡€ÃpX Àa9,‡à°–ÃpX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡¥€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡å°€ÃpX8,‡à°pX Àaà°€ÃpX Àa8,‡å°€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°pX Àa8,‡à°€ÃrX Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpX8,‡à°–ÃpX Àa9,‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°4pX Àaà°€ÃÀa8,‡à°€ÃpXËa8,‡à°pX Àaà°€ÃÀa8,‡à°€ÃpX ‡à° Àa8,€ÃpXËa8,‡à°€ÃpX ‡à° Àa8,‡å°€ÃpX Àa8,‡€ÃpX ‡à° Àa8,‡°n² endstream endobj 314 0 obj << /Length 316 0 R /N 3 /Alternate /DeviceRGB /Filter /FlateDecode >> stream xÚ}’OHQÇ¿³%B¬e&RðN¶Wí`ŒÝõoʶ¬k¦²Î¾ÙÞÌn%Bˆ.AÖ1ºXÑI:†‚b]"è(‚—í73»îˆÚƒ7ï3¿ÿ¿ß{@](mšz€yÃÉþ(»;>Áê7P‡A+­Xf$‘v™lqdí}…䜛áãõÿ] ‚U€Æ¬ÇמöxÀáû¶iO:¬äÒb“¸M¤’1âWÄg³>žöq†[ ñ2ñMÅ'"()Y'æ±ld4ƒä—‰»2–’'&ßÀSg^™öÐ}8õ¹&›°€åwÀ¥Öš,Ô \V:k²Ý¤;©iÝR;;\‘Œu?ÊåÝV þ°ÿ¼\þûº\ÞC9¾u¥(J•IÒÀëÃ]ýÜàBS˜s_ QP5ûFz¼Úë׋Gõ%«t{3qW°D÷0vz ¼ü \}\ø$€Ôu¡ºmþÀÍ+˜…–ÍÙ¬C–;XØ9:Y„^g±BÞ,Ú\°ACioci]g®©Å·¸(ñL;òz±Úï9ÚAnŒŽÐIó ¨Üê­°4“I÷ÐÝ x#Ã{zwA¼¨j}ƒÎ…Ðþ¤Š¾Q¥óš=˜ò8Ðmèñá Ã(Äo{1±cÚÑd5¾Ué­ÊgÒ·t¶üÆlaȱi"ßÐ\.5æ±”šËÅâ^Å8tph0èk€!‰~D† TÒhd¡‘”»6‚ØÂì±–:>f¤ß&Ÿm×çŠäíxÝA4Ž…¶ƒLþ&ÿ–·ä%ù­ük±¥ªiÄ”¦¬?ûCqÌÕ¸m¥&/¾By#¤Õ‘%iþ 'ËW©¯:ÕXl©Errð'ñ=_—Ü—)Œi7Ò¬›©äê,úF|ÙNšٮͯ6×rm^™Ü ®ÍšUáHWü «Ãÿ5;¿?ÿͰh endstream endobj 315 0 obj 9310 endobj 316 0 obj 706 endobj 298 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[1 0 0] /Rect [180.413 366.655 187.86 378.344] /A << /S /GoTo /D (figure.8) >> >> endobj 302 0 obj << /D [300 0 R /XYZ 71 721 null] >> endobj 303 0 obj << /D [300 0 R /XYZ 122.877 469.923 null] >> endobj 304 0 obj << /D [300 0 R /XYZ 72 323.12 null] >> endobj 305 0 obj << /D [300 0 R /XYZ 72 300.905 null] >> endobj 306 0 obj << /D [300 0 R /XYZ 72 278.69 null] >> endobj 307 0 obj << /D [300 0 R /XYZ 72 258.595 null] >> endobj 308 0 obj << /D [300 0 R /XYZ 72 236.38 null] >> endobj 299 0 obj << /Font << /F8 93 0 R /F11 106 0 R /F18 122 0 R >> /XObject << /Im9 297 0 R >> /ProcSet [ /PDF /Text ] >> endobj 325 0 obj << /Length 2928 /Filter /FlateDecode >> stream xÚYYsÛ8~ϯÐ#]cqH‚çî““ŠS™Ij]‘+©ÚL`–¸á¡Éq¼þóÛ(J¦SyÀFãèëë¬v«`õîUpÖ¾¾}õûu¾ ¿Špu{¿Ê_)µÊ‚Âb ”«¯^è_|»ýcš´úºN‚À»¹ˆrOÛ¡ª®­ÚÝÅZå¹Wwk ouÍߥ4÷ª–Ûnµá~ßÙ{w4ÍÜwV††½tLmÓâfÄZxö"ôºq˜¶ìà ½ƒlÓsÛàŒq‹¿{ჳÁ×ÃE”yƺ]t‹Â­Båñj M,vcìŽ7‰#ØÄÝôüÑÝs+ÂAÏš­©þÆåMÉ”{Û5 ¢XÇô º¬FŠöµá_¬ã,ôn÷HI ŠEÔ}«ÇÞ0¥AÁñ˜ÀìN Ü(Pp&Ì©:Tì5¬j¥ÀXL7ë{[™¶¬™ƒ”C‡™Ïœa-š‹"? SÞ,ZtgøXebxìéºï¸·×=w!X^ NÑsË­ù1XÍ]ÛmÉ]4K¬Ro‹Zé¹­¶O>_:{­ït­Û­œ‡¼ ;dÒ¢¬À ª»—»ö0ðöÕ¤Ó"ñtY¢­ŠÔ‰"F«ë]ï‘©x’Ù( ‰çHUཿ6 èô¤½R>u5 †<>KY«í9Ça½äè~pØU£ÊÁ“áxèܹ®QÔ4tœ)„¸.¹W‘ŠgŠL½F#é‘×” –iÚÙb(]Õ3i²hr´hŠ]cûܶäÐnkôتݎ–»š‡i!ø`lœ“+|ö÷ë8[…±¯â4B8ÛøQ¦VY¾Ê"Þ?ÿ £Ä{ÓµÛzìñ4õHáÌÌIÏù‚[€øQ€ \ ³ú Ø®¹ÙW;p§()Ü1'߸D_IˆcYz@Z}W»•µÕumjþ #£Åë]g«aßðçÌqñT sMÏ_÷¤I‚³ÎÂêKVÐ;]µ=:œs#…„²é’À‚r»C|`ÄG'pBÈK”÷/¶Wg¡(eEB?àГ^×2BenÛÔ{óÛoÜÙ à\Ú–üuKZ‡ÝÑßdøPÝYmÅeÈ+±óñæý¥s©ïœУnmw7²Ø1âQè•Üç@ñÐ>cHê*=Ýò’:Éf S¬¨à‰ÓeûKg´’LŒ>ŠçŒ 5O’ÀIIÚn„Êì ÍàØ(p@õI˜à™˜cÛõ2ŸRžqJ‚°ãV’åzI°£9İ)Ç-¡UžÂˆˆ p.81@WmØÉ$3ÒQ…á<ŽÂ$ôt°…PKü8/Ú`kÒ,l"îB©pàÝÀó˜&…Fr^h`’°\g 7ùL¸Ôx·ÖÖòF½¨%Iý_G?ÌŠªç±ªû ‹ê8øéê…® ³`ømEð D²°M%ì “<´–»ŒÒ¸8òÌ•pÒÃv ×&sF‚=bì+y(--8( l€µ=UCàÏÁÿ3Ób@’P‰ëÞÅ ¦Í —û…ðœ–ÁL—ô.oDxïêy`ªLáN8+;•*ü J\á¹.R(­ìúF.’Éœ9}¸FGÇZ¨c£ÔϲÜ1M2¹¸Àþ¾G`¯ƒÌO”:ÕUç.¾6 ¶ê9š›p`7V¥¼ý¸òÆA/S";QO¿x5N/ÅŸÌ=\lÀ²ýIÞx^ªƒ7òÍX&~ ¿-½½ó¹Dz+íkU÷¸H¨„¦„{sÆ÷ÁT=XŽ«¾×BÿèZ½ÛõgsÝ^7µþ1L37BýCÚM¾,3Ù8³eÿ­w{íÏÒi°ú¾r½?I(ˆ×« EÓâ U=¡AΈíƒòY@!ôä]ry›ÁÄV i§rË4R&W­0¼ù¸ŽÎ;ŠPW²™uê剟%ÓõêÆ^$X$X\'iüXǤH&\&ÆNEóJv[·æ 8µíˆ),¶«‹®ôù£Ç< ®‰ÛöÕØ0•ê¨$ðn4-Œ3t^Ya®)dã2ˆ}»¯:ù/IÐ/Ä#T~ÆNt„7|”Ñ;‚I•xê L‰Ýœó‘áãrU)²Ê"'Ö‹"|öŽ*^A©¢”¯ÑblÓÅÄÄØ\òñ'!}‘öš/S„—qì}&zGÓà 47烴Ÿ¤ î{9Ìrœo¥…œ£ö?_â´ðn«Æ0+g~WºÅâ¤8Â5ëÏ[~€Xp¹y|ˆ"þPã÷¤$ŸÞ&ÞHöcªàeîmá~ÔHrZ»}ÑÂQúE¡fFLÌþÇùSœò t5îF~=y ‹L0K€§T/¢>åm¨M½×V²?Wq©+ÜR=®Iå;c¥´²»Ÿ¢Š ž"w|\boô}bÂÍîÒHíîjp¨ º±gÊÁVMrÍÀ)PvŽf Gâ—p$r?Ì~F8t HÀBgÙ 1‘g›Rb¨†=“Þ·òzÂO8¸9Eä"h„!ˆØÄ›*ÐXëÀôfv_[†h DŠ€B)/ ‹§(ÊÑlp™Û€îàžk×"RŸøõgz ´µÞ‰ïEAý2Ä/gͨàÜík¹ƒ_]2哌\ãÎ'þD'pµpüIœ¹÷YJD(~êrø`ÿ¶ÝAª1–ß"ªÑ±9þÅDÞÅ®…S~#æ©`|æY/åÍ÷ôè3†c^YºO(ðüwBºö™åý ëJ·Ý%?÷±œ™À ´¼‹<à³xæ}—5LY e,?÷è(U~–þzb,RF®ã¿:©·©~Ð;$t¿ÐUò{O%1·?«¤ÖIæ§ ´¡R? bÉ+§¯soo_ýkÎ2 endstream endobj 324 0 obj << /Type /Page /Contents 325 0 R /Resources 323 0 R /MediaBox [0 0 612 792] /Parent 332 0 R /Annots [ 317 0 R 318 0 R 319 0 R 320 0 R 331 0 R 321 0 R 322 0 R ] >> endobj 317 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [461.571 491.313 469.018 500.336] /A << /S /GoTo /D (cite.BF02) >> >> endobj 318 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [473.948 491.313 481.395 500.336] /A << /S /GoTo /D (cite.Dem02) >> >> endobj 319 0 obj << /Type /Annot /Subtype /Link /Border[0 0 0]/H/I/C[0 1 0] /Rect [522.034 437.116 534.936 446.139] /A << /S /GoTo /D (cite.SS99) >> >> endobj 320 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [530.822 267.271 540.996 280.173] /Subtype/Link/A<> >> endobj 331 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.428 254.328 411.173 266.017] /Subtype/Link/A<> >> endobj 321 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [244.09 168.043 466.78 180.945] /Subtype/Link/A<> >> endobj 322 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [309.132 118.429 537.966 131.331] /Subtype/Link/A<> >> endobj 326 0 obj << /D [324 0 R /XYZ 71 721 null] >> endobj 327 0 obj << /D [324 0 R /XYZ 72 720 null] >> endobj 328 0 obj << /D [324 0 R /XYZ 72 675.338 null] >> endobj 78 0 obj << /D [324 0 R /XYZ 72 600.219 null] >> endobj 330 0 obj << /D [324 0 R /XYZ 72 284.544 null] >> endobj 99 0 obj << /D [324 0 R /XYZ 72 289.027 null] >> endobj 141 0 obj << /D [324 0 R /XYZ 72 225.864 null] >> endobj 186 0 obj << /D [324 0 R /XYZ 72 187.678 null] >> endobj 245 0 obj << /D [324 0 R /XYZ 72 138.064 null] >> endobj 323 0 obj << /Font << /F8 93 0 R /F47 95 0 R /F11 106 0 R /F62 329 0 R /F45 94 0 R /F53 121 0 R >> /ProcSet [ /PDF /Text ] >> endobj 346 0 obj << /Length 2350 /Filter /FlateDecode >> stream xÚXÉvÛ¸Ýç+´¤Î‰bàÔ;yŒ¸ÛÇr'‹¤°„È<æ &©$n/Þ¯¿*TQ–(JÉFÔ­á‚ÑrŒ.ßܞܿywª‘ü4HÅèþÛ(Õ¾–z©hYŒ¾xÓ±Œ¼|YÕYûXŒ'2UÞy¹ÌJ;…gë¬\Ò )<ûsÅS…-Û¦7Û>Zê\duÓR÷sU?5ÕŠ¾ª[M\šü¹Íæ4³s”l¾µõ?÷×p§‰P~¨S:÷iUÔRG>Œ÷<'„úAI#ïA©M9·ÍyÌÊŒfHWÄ'½´_¡lÝ….R›–´"+'«ÜÌ-íÓTuKàÁÔ°{êý…äyµ$Ù/LcóªX™:kH­Û£•NòàE…—"_Fœ^ù1¸ #q]­kˆ™!a÷1 ¥èáJ<´vx‚;¾„*fD!†}D„„=D“AD êÕ^-k†•Ž]Äùí0¨Àbßóøm]9׬V¼ô +g,qg CFS`3Gí@'‘wŠâ±÷h'ÕCž}ϪuC;ˆ|ÞtCðÂeáÛ× ëÐòô`l˜Ï"áP“QÀ™::@óÞï,×0~Ïž —k«&[4ä²'´ãœz A‡ÜM~£¿@·K]·¶¦Ù<³åbŒÃrÉ-ëˆM…»‚†§¹)²¶‚ÀŽ–=é¤w³ñ”mêïÙô5*¤œd¯ÎÏÏ©wº9–K¾æ ×"”Ïèÿ{f dàtÐê\r !8˜‡:³9Þ#–.ºàh7ëlÒ,—½éPr>7,-(¯uQK F-¡?ˆ“Ã,?_2z 5+,eˆa®/@1`Ù½P#‚ÁXƒî¦¤ B"!ŒØúlXaÕf M Ð9e¹k^7nSåæ)kŽg.©½3 ³ùÆUJ¸ ÑÐ×’¾7Ö·¨Šì?» ‰,Æ”4ŒI—˜ˆ4pI¬w€qÞ ¸ˆCžÌni¤`çµùP’ÔêP’ÄJ3ÞðHØ ²Öt:€¦L ^ ƒ)¥z‘JòÄãQàk(yzð‰#%vÁKiHt?¾%””ŽZü‚T¸tV $2n,&ÈK#´[@èO€2Bг9}uÔsˆlÈzT"õ£xSwý’g¨˜È¶&tdâh.ä1*½Ïp^*ÓØ—º—rœIçŸ=&âØgÉì]–(èóÑ¥ªŸ·)jÏ¿3èúFЩD±¿@®äâå÷ ÷!ìù‚{¤z» ü(êa#µç•ëÅ_Tì”Z½—KåÉ<ÃO—XH8Ùì1£áën†km~v9 …ŒR º þªuØa¹ðã !ׯír;D)ü{0;9Múaí½È0%Ç„öÖ8ÏÁahØÒÎÐRï Bm=¬îi”ó!(O(¹÷à ègƒ‰+~Ò£Ôžôë󊊯øÕRÙÇ ªjï±—á·QWa¡Ëí'zÍD¦‡!æ(:I¢£à2¨’K#n ÀìLÅ2=7æ‚v^¿È~?M…ûEÇž)5“ Å‘ÝQ•Á'-¡•¥[J§‚Cƒþt´ ˜$Øõ"„x¥ýWjD¾‚¼‚(¦-„Þ‘:¿óœ- endstream endobj 345 0 obj << /Type /Page /Contents 346 0 R /Resources 344 0 R /MediaBox [0 0 612 792] /Parent 332 0 R /Annots [ 333 0 R 334 0 R 348 0 R 335 0 R 349 0 R 336 0 R 337 0 R 338 0 R 350 0 R 339 0 R 340 0 R 351 0 R 341 0 R 342 0 R 343 0 R ] >> endobj 333 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [327.377 669.253 452.963 682.154] /Subtype/Link/A<> >> endobj 334 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [357.034 646.737 540.996 659.639] /Subtype/Link/A<> >> endobj 348 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.428 633.188 176.784 646.09] /Subtype/Link/A<> >> endobj 335 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [252.879 610.672 540.996 623.574] /Subtype/Link/A<> >> endobj 349 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.428 597.123 124.269 610.025] /Subtype/Link/A<> >> endobj 336 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [391.874 574.608 522.661 587.509] /Subtype/Link/A<> >> endobj 337 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [142.353 498.501 540.996 510.191] /Subtype/Link/A<> >> endobj 338 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [284.441 448.281 540.996 461.183] /Subtype/Link/A<> >> endobj 350 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.428 435.338 182.996 447.028] /Subtype/Link/A<> >> endobj 339 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [177.275 412.217 370.589 425.118] /Subtype/Link/A<> >> endobj 340 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [293.715 376.152 540.996 389.053] /Subtype/Link/A<> >> endobj 351 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [93.428 363.209 224.542 374.898] /Subtype/Link/A<> >> endobj 341 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [247.518 304.022 537.966 316.924] /Subtype/Link/A<> >> endobj 342 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [204.282 267.958 372.593 280.859] /Subtype/Link/A<> >> endobj 343 0 obj << /Type /Annot /Border[0 0 0]/H/I/C[0 1 1] /Rect [170.915 231.893 382.417 244.794] /Subtype/Link/A<> >> endobj 347 0 obj << /D [345 0 R /XYZ 71 721 null] >> endobj 161 0 obj << /D [345 0 R /XYZ 72 688.887 null] >> endobj 124 0 obj << /D [345 0 R /XYZ 72 665.766 null] >> endobj 215 0 obj << /D [345 0 R /XYZ 72 629.701 null] >> endobj 187 0 obj << /D [345 0 R /XYZ 72 593.636 null] >> endobj 246 0 obj << /D [345 0 R /XYZ 72 544.628 null] >> endobj 214 0 obj << /D [345 0 R /XYZ 72 467.916 null] >> endobj 212 0 obj << /D [345 0 R /XYZ 72 431.851 null] >> endobj 211 0 obj << /D [345 0 R /XYZ 72 395.787 null] >> endobj 162 0 obj << /D [345 0 R /XYZ 72 359.722 null] >> endobj 123 0 obj << /D [345 0 R /XYZ 72 323.051 null] >> endobj 210 0 obj << /D [345 0 R /XYZ 72 286.986 null] >> endobj 213 0 obj << /D [345 0 R /XYZ 72 250.921 null] >> endobj 344 0 obj << /Font << /F53 121 0 R /F8 93 0 R >> /ProcSet [ /PDF /Text ] >> endobj 352 0 obj [377.8 319.4 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 552.8 319.4 319.4 844.4 844.4 844.4 523.6 844.4 813.9 770.8 786.1 829.2 741.7 712.5 851.4 813.9 405.6 566.7 843 683.3 988.9 813.9 844.4 741.7 844.4 800 611.1 786.1 813.9 813.9 1105.5 813.9 813.9 669.4 319.4 552.8 319.4 552.8 319.4 319.4 613.3 580 591.1 624.4 557.8 535.6 641.1 613.3 302.2 424.4 635.6 513.3 746.7 613.3 635.6 557.8 635.6 602.2 457.8 591.1] endobj 353 0 obj [498.8 490.1 592.2 351.7 420.1 535.1 306.7 905.5 620 497.5 515.9 459.2 463.7] endobj 354 0 obj [611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 351.8 351.8 351.8 935.2] endobj 355 0 obj [1121.5] endobj 356 0 obj [445.6 511.6 660.9 401.6 1093.7 769.7 612.5 642.5] endobj 357 0 obj [531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 295.1 295.1 295.1 826.4 501.7 501.7 826.4 795.8 752.1 767.4 811.1 722.6 693.1 833.5 795.8 382.6 545.5 825.4 663.6 972.9 795.8 826.4 722.6 826.4 781.6 590.3 767.4 795.8 795.8 1091 795.8 795.8 649.3 295.1 531.3 295.1 531.3 295.1 295.1 531.3 590.3 472.2 590.3 472.2 324.7 531.3 590.3 295.1 324.7 560.8 295.1] endobj 358 0 obj [306.7 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 511.1 306.7 306.7 306.7 766.7 511.1 511.1 766.7 743.3 703.9 715.6 755 678.3 652.8 773.6 743.3 385.6 525 768.9 627.2 896.7 743.3 766.7 678.3 766.7 729.4 562.2 715.6 743.3 743.3 998.9 743.3 743.3 613.3 306.7 514.4 306.7 511.1 306.7 306.7 511.1 460 460 511.1 460 306.7 460 511.1 306.7 306.7 460 255.6 817.8 562.2 511.1 511.1 460 421.7 408.9 332.2 536.7 460 664.4 463.9 485.6] endobj 359 0 obj [531.3 826.4 531.3 559.7 795.8 801.4 757.3 871.7 778.7 672.4 827.9 872.8 460.7 580.4 896 722.6 1020.4 843.3 806.2 673.6 835.7 800.2 646.2 618.6 718.8 618.8 1002.4 873.9 615.8 720 413.2 413.2 413.2 1062.5 1062.5 434 564.4 454.5 460.2 546.7 492.9 510.4 505.6 612.3 361.7 429.7 553.2 317.1 939.8 644.7 513.5 534.8 474.4 479.5 491.3] endobj 360 0 obj [777.8 277.8 777.8 500 777.8 500 777.8 777.8 777.8 777.8 777.8 777.8 777.8 1000 500 500 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 1000 1000 777.8 777.8 1000 1000 500 500 1000 1000 1000 777.8 1000 1000 611.1 611.1 1000 1000 1000 777.8 275 1000 666.7 666.7 888.9 888.9 0 0 555.6 555.6 666.7 500 722.2 722.2 777.8 777.8 611.1 798.5 656.8 526.5 771.4 527.8 718.7 594.9 844.5 544.5 677.8 762 689.7 1200.9 820.5 796.1 695.6 816.7 847.5 605.6 544.6 625.8 612.8 987.8 713.3 668.3 724.7 666.7 666.7 666.7 666.7 666.7 611.1 611.1 444.4 444.4 444.4 444.4 500 500 388.9 388.9 277.8] endobj 361 0 obj [736.1 736.1 527.8 527.8 583.3 583.3 583.3 583.3 750 750 750 750 1044.4 1044.4 791.7 791.7 583.3 583.3 638.9 638.9 638.9 638.9 805.6 805.6 805.6 805.6 1277.8 1277.8 811.1 811.1 875 875 666.7 666.7 666.7 666.7 666.7 666.7 888.9 888.9 888.9 888.9 888.9 888.9 888.9 666.7 875 875 875 875 611.1 611.1 833.3 1111.1 472.2 555.6 1111.1 1511.1 1111.1 1511.1 1111.1 1511.1 1055.6 944.5 472.2 833.3 833.3 833.3 833.3 833.3 1444.5] endobj 362 0 obj [569.5 569.5 569.5 569.5 569.5 569.5 569.5 569.5 569.5 569.5 323.4 323.4 323.4 877] endobj 363 0 obj [892.9 339.3 892.9 585.3 892.9 585.3 892.9 892.9 892.9 892.9 892.9 892.9 892.9 1138.9 585.3 585.3 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 892.9 1138.9 1138.9 892.9 892.9 1138.9 1138.9 585.3 585.3 1138.9 1138.9 1138.9 892.9 1138.9 1138.9 708.3 708.3 1138.9 1138.9 1138.9 892.9 329.4] endobj 364 0 obj [404.2 472.7 607.3 361.3 1013.7 706.2 563.9 588.9] endobj 365 0 obj [469.4 353.9 576.2 583.3 602.6 494 437.5 570 517 571.4 437.2 540.3 595.8 625.7 651.4 622.5 466.3 591.4 828.1 517 362.8 654.2 1000 1000 1000 1000 277.8 277.8 500 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 777.8 500 777.8 500 530.9 750 758.5 714.7 827.9 738.2 643.1 786.3 831.3 439.6 554.5 849.3 680.6 970.1 803.5 762.8 642 790.6 759.3 613.2 584.4 682.8 583.3 944.4 828.5 580.6 682.6 388.9 388.9 388.9 1000 1000 416.7 528.6 429.2 432.8 520.5 465.6 489.6 477 576.2 344.5 411.8 520.6 298.4 878 600.2 484.7 503.1 446.4 451.2 468.8 361.1 572.5 484.7 715.9] endobj 366 0 obj [571 571 856.5 856.5 285.5 314 513.9 513.9 513.9 513.9 513.9 770.7 456.8 513.9 742.3 799.4 513.9 927.8 1042 799.4 285.5 285.5 513.9 856.5 513.9 856.5 799.4 285.5 399.7 399.7 513.9 799.4 285.5 342.6 285.5 513.9 513.9 513.9 513.9 513.9 513.9 513.9 513.9 513.9 513.9 513.9 285.5 285.5 285.5 799.4 485.3 485.3 799.4 770.7 727.9 742.3 785 699.4 670.8 806.5 770.7 371 528.1 799.2 642.3 942 770.7 799.4 699.4 799.4 756.5 571 742.3 770.7 770.7 1056.2 770.7 770.7 628.1 285.5 513.9 285.5 513.9 285.5 285.5 513.9 571 456.8 571 457.2 314 513.9 571 285.5 314 542.4 285.5 856.5 571 513.9 571 542.4 402 405.4 399.7 571 542.4 742.3 542.4 542.4] endobj 367 0 obj [638.9 963 638.9 963 963 963 963 963 963 963 1222.2 638.9 638.9 963 963 963 963 963 963 963 963 963 963 963 963 1222.2 1222.2 963 963 1222.2 1222.2 638.9 638.9 1222.2 1222.2 1222.2 963 1222.2 1222.2 768.5 768.5 1222.2 1222.2 1222.2 963 365.7 1222.2 833.3 833.3 1092.6 1092.6 0 0 703.7 703.7 833.3 638.9 898.1 898.1 963 963 768.5 989.9 813.3 678.4 961.2 671.3 879.9 746.7 1059.3 709.3 846.3 938.8 854.5 1427.2 1005.7 973 878.4 1008.3 1061.4 762 711.3 774.4 785.2 1222.7 883.7 823.9 884 833.3 833.3 833.3 833.3 833.3 768.5 768.5 574.1 574.1 574.1 574.1 638.9 638.9 509.3 509.3 379.6 638.9 638.9 768.5 638.9 379.6 1000 924.1 1027.8 541.7 833.3 833.3 963 963 574.1 574.1 574.1] endobj 368 0 obj [312.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 312.5 312.5 342.6 875 531.2 531.2 875 849.5 799.8 812.5 862.3 738.4 707.2 884.3 879.6 419 581 880.8 675.9 1067.1 879.6 844.9 768.5 844.9 839.1 625 782.4 864.6 849.5 1162 849.5 849.5 687.5 312.5 581 312.5 562.5 312.5 312.5 546.9 625 500 625 513.3 343.7 562.5 625 312.5 343.7 593.7 312.5 937.5 625 562.5 625 593.7 459.5 443.8 437.5 625 593.7 812.5 593.7 593.7] endobj 369 0 obj [525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525] endobj 370 0 obj [722.2 583.3 555.6 555.6 833.3 833.3 277.8 305.6 500 500 500 500 500 750 444.4 500 722.2 777.8 500 902.8 1013.9 777.8 277.8 277.8 500 833.3 500 833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8 500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 277.8 777.8 472.2 472.2 777.8 750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8 680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 500 277.8 277.8 500 555.6 444.4 555.6 444.4 305.6 500 555.6 277.8 305.6 527.8 277.8 833.3 555.6 500 555.6 527.8 391.7 394.4 388.9 555.6 527.8 722.2 527.8 527.8 444.4 500 1000 500 500 500] endobj 371 0 obj [319.4 575 575 575 575 575 575 575 575 575 575 575 319.4 319.4 350 894.4 543.1 543.1 894.4 869.4 818.1 830.6 881.9 755.6 723.6 904.2 900 436.1 594.4 901.4 691.7 1091.7 900 863.9 786.1 863.9 862.5 638.9 800 884.7 869.4 1188.9 869.4 869.4 702.8 319.4 602.8 319.4 575 319.4 319.4 559 638.9 511.1 638.9 527.1 351.4 575 638.9 319.4 351.4 606.9 319.4 958.3 638.9 575 638.9 606.9 473.6 453.6 447.2 638.9] endobj 372 0 obj [826.4 295.1 826.4 531.3 826.4 531.3 826.4 826.4 826.4 826.4 826.4 826.4 826.4 1062.5 531.3 531.3 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 826.4 1062.5 1062.5 826.4 826.4 1062.5 1062.5 531.3 531.3 1062.5 1062.5 1062.5 826.4 1062.5 1062.5 649.3 649.3 1062.5 1062.5 1062.5 826.4 288.2 1062.5 708.3 708.3 944.5 944.5 0 0 590.3 590.3 708.3 531.3 767.4 767.4 826.4 826.4 649.3 849.5 694.7 562.6 821.7 560.8 758.3 631 904.2 585.5 720.1 807.4 730.7 1264.5 869.1 841.6 743.3 867.7 906.9 643.4 586.3 662.8 656.2 1054.6 756.4 705.8 763.6 708.3 708.3 708.3 708.3 708.3 649.3 649.3 472.2 472.2 472.2 472.2 531.3 531.3 413.2 413.2 295.1 531.3 531.3 649.3 531.3 295.1 885.4 795.8 885.4 443.6 708.3 708.3 826.4 826.4 472.2 472.2 472.2] endobj 373 0 obj [272 326.4 272 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 272 761.6 462.4 462.4 761.6 734 693.4 707.2 747.8 666.2 639 768.3 734 353.2 503 761.2 611.8 897.2 734 761.6 666.2 761.6 720.6 544 707.2 734 734 1006 734 734 598.4 272 489.6 272 489.6 272 272 489.6 544 435.2 544 435.2 299.2 489.6 544 272 299.2 516.8 272 816 544 489.6 544 516.8 380.8 386.2 380.8 544 516.8] endobj 374 0 obj [693.3 654.3 667.6 706.6 628.2 602.1 726.3 693.3 327.6 471.5 719.4 576 850 693.3 719.8 628.2 719.8 680.5 510.9 667.6 693.3 693.3 954.5 693.3 693.3 563.1 249.6 458.6 249.6 458.6 249.6 249.6 458.6 510.9 406.4 510.9 406.4 275.8 458.6 510.9 249.6 275.8 484.7 249.6 772.1 510.9 458.6 510.9 484.7 354.1 359.4 354.1 510.9 484.7 667.6 484.7 484.7] endobj 375 0 obj << /Length1 1155 /Length2 6047 /Length3 0 /Length 6760 /Filter /FlateDecode >> stream xÚ­“g<œí¶Æu¢G‚„`‚(QÁ袇hQ#DÁ0f˜e´è!Z” z‹ÞBˆÞ¢÷HtщÞ;áÌûî³ßäìýõüžùðü×Z÷º®gÝkÜ×Öå“#,!Ê8šO_P  !ÿ\P È/ Oúàb†"àŠhˆ@P\\  ±Ä¾`"¢¤ G jc‹p)pÿUÈ9@P+ 8@Ãm qÀö°²€tVPÃÁ:@t (Òæ'€¡Vh€%Ä 'þåIn€þ;;þ;åA¢°¦\Ûä`M‚p†X“5X5ÖËÿ‡­ÿl®ì ƒiZ8üÕþïIýWÞ ÃüoÂÁÑ A4`þŸ¥†™“GÀþKFmƒZÉÁm`€À¿BP”2Ô Ö†¢­lÖ0äï8þO ØÁýmh`¤­¨¬Æó¿wúwRÛ GëaÿiûWõß,ø›±ÓABÝÆØñ b ±Ï¿ß^þ‡˜Ü †ÂmB"¢ $ÒCŠÝ,‰<P8‸aùá4ö;/€5Iú×…ŠŠ€r…þ&Ð#Põ7‰€Zÿ˜¨ý›°•ºÿöf¿I ´üMØ.Vÿ6g…€awçßAlcð(Bþ@¬Í( ÚþXûÐ?Ûöb¥~#ö €èb׈ø…±µø‚B ãï´(–°»‹øÃ© Ö òÄzAýþпâùSÛý»+¶EB~KŠ`ý¡]ÀŽÕùoüï“—G¸yð=ÂN‹O{Û\ .,êõ*­œ‘Hý÷ß»§ÿfk(v«!7ˆéÄ(ÂJ2À.¾*(ß[)k¨€ð!®¼Íç·šÍßÈüÇ£pa9=OΔ??/L¼E¹B¸ÂâzɈzSïù¬_yÇ×émÂÈÕŠ‹ùJ¢{ ãóýD ̲#ë‰ÿU]Õ÷ÃM\­á™ÞüX£ìö”ÝŽ m.E½ŸÄÓ÷qZŒ]>µ$ˆ‚ž+'Âô_–s<º¯Cà”,üú‡+ul4ÁAo»×$ÕPzÇvw…$–¯³èŠú£®uÔŠ‘–ñŸµSÖE~ê×^„¯ŠvñÑÍ/wÊÎÈfõ #;îi,u´:Âå>$Æß°½ ³WêQÅÄJFà'í»õàì1\…³“榽 Þ‘ñ9€¼bÎ^Ó®:'æXýE.î ¸ÈbßÙÁ»êÒhåÞA7­nù"t&þk w&„mÆ'c""éѶgØcT$³.›í“T›cU¬!ÈöŠTÁ ñ6’¬–ûþºÐÌ óÑíÇœ}h+hµC¤÷ª­÷JÉræ?ª›LÙø*¥pC%O€UF%iÑiÉÁk-‚©Äq\g>Ép÷¸Cm£MänÐ]ÖßÄõ<ÇpfÚF5ºÜèN¾‚ð©×¨>ºÚ'Ô^û{u¼£¬`<«T"k‚ä €¿·þU>u l«{µPüݹ&énï†ÉFJ¼:Õî f⇔ž)!SˆqÛ}=¢<'_¶H©Qû§³³¥°.? ¸Eß åPÔ+5¼‰Ø,~£0þ‘ó€ “Q?¶l®ò3>ÕôÏ×y5È; lg—€.Uœ-ÜÙhÃsö ¥â¹‰—ëèðU~–á»Ãú“…´ÃºTÚ»VÅŽl×îiSw˜ÀM/½Ýá^·£…Õ)äÍëû+ “p|fLYÔD]Q nS‡7¾^¼aˆ´úYAÄXe”2ó¨op«Ï_k»ýcˆ¦n@ü{ëø«]ÐÌâG6¬vÔ $·¿z» J+;õ™Û»™ÕÖè;wË}¢iwoÊ¡KÞ“ã­G䩘ûŠ»áéÑ¥7Ÿ-jiOŠ}{ ³ìŠâ_4\ñ|ý4(—¢iŽŠOÍ“¿¶håŸÐ3 :Ɉgÿ¥ÏøpÙÄÆàÇËC¶÷÷ûö~t\ÓJ2ÑÔxe±ISìP^˜PD•Ï W?Zê6a¨,AÈcºuÏ ç†×ÛæwFÞ¦âú?VAãõ ;3 TåëŒÈ\è‡ Ü¢‹ûY ßw}p:bWÙñÒc¾l•/7LJoîw¨[Ŭڿ ´ ¬NП£ÊÞ‡0¯¨ÆŸô~¾îdé†Ó?¢Í–Ão8R|©u(oÍœ…‹´Õ’üüè ZšðS·pútD=aåõ¤w ÔO²+‹c†ß÷òý:¦‰”@))“WÏêÛoj髿ÙLÈ^Á£\YÜž9[{3©»L‰ìÅd6¨©Ì¶èV2Dî„'õEo‡Ç‘RGEˆ¦‚騙 €A?D{“!ýô ´þ央Ç3qÓ„Ñûx!Ö£¨Ýw.›ë{=!dÝx¿¼úXmDa݃&ßuC/áC™ƒ|ñÜ­;€—~'Í”SFÞ=‡•㺟}ÇÕc³¦w?¾å³¯>¾’¹ mRÕ¡>Õî5u‡E:‘)»W y!¦_&óC9¢2u¨õ˜_·´ƒ¼k¢ÉÕ…7›1„ey–~Owè”´s@É?Õ¾ß-sÝ+ §Kj‰œ-ßx‘+ÔNÿI>R Í›ËÓmæ š>˜î ݆·f}y NÜÈå¹X"âÐèUúðK©èÉ“™m~vüá/¹³&h°/V›ðjädîñ^ª7Jã11 „ÝÖmÇXˆÓ¨ä†@*S?,ŽÉÒ?tªŠ}xC×öþíœ[GDæÚÉT¸)ŸNN©gý˜¾–½ÂÈ”Ó=—39Á¶"Zõ¦Ë–ßÞðZ-õòR€Ò>y/ŠÏÂáþgýÊæå©: ±œŠ¦Kešd* ƒòýÆÎ˜¼C!›©ÎØö~A©óg_Ñ1F;é«X1㇧r99™î:_,LáJ¨ÂÞÀÃ’A™øtÓØ»,¥"¢ž5ï„; €ò Ûý ñ–œdÊCEÏ6/‚FWnu"ú~…ëMDùÏ`Æá‰rDÌ5Ý€¯¢q282Ö¨Ni ú}KP~Õ+‚%hqñ…ËÓ}U½œgrûýnþ*Ž +5kL©¡ƒ¤¹œŠKa oÅ 'ô¸z~ ‡«™ý|®:ÕíÍyg@s¥:48á=ñtP‡U}š³IB**Ÿ/—»læH¨š8džªeOa àOЄpz,1ঠ¨çFTÑÄy\ôï!Ç<ìÎmE‘ö„W­ëAµœKsŸÐN=žžV¯PæëJÏž*sí´x¶¸5+õDÿôÀÔ‹·w<™‰¤ïÓ^*xÂÙ¶à,YݤºQâÔs‡¨ófIq(éJ¾|iV7)ãÚ˜ ¿ ÷c…Ÿ/[øu ÛRW‰#ŽjIZ‚îŒDæq;5ù¿ùLÊ*ÆMsYT6ÞVšÝ¬·Ï2Ã\z{ç ¢w¥‰—â¥Q™ÿ꜂%¦kÌjk:÷0ïÆçÄwÄ’Í…Ý:­‰#ûîxõ/æúRä§M?¸8U›#ä^BK¶>íØu J–¤ÞÜ(e9å€y,%Nâ“T¯‡Õéܧ…¢„ÏùâåÑžU«¸ó¹”0|!éTsÅ‹yÔÝv ù Z‚¢&"{I*ÂâhÜ··‚µŒ„Mzø®IŶ¾Î×Þ‚ ã÷Ev+ã £œ(©gÆÂ•ž‡wOàOÏÉÌÃÍ"ßV¢-ëEjaœyžaŸtò:]6;R\ß¹Éï‰ò–Æ "©ú¾Xvðq5F«ì„Nûîï>|¢Ãõ¨!ó¦#¥oÛÙßôÀDRGZZ5¢OüœLW“D mõëÇ>îñéJ¿ÌZìG°ê^-¦th{Ü!ešxhpTAííÿËåDžÕe/ÇÄe›¦¦u_~.ðK,ˆçèTG06Koá=¹¤9(¯]œnöúª~Øžœ6ieÐ7Ñ'› šft#xå2k)8¼®7´L"˜KðklªïÛ‘Ùzòñö‚„BfTå»ÄÎ/P4N4H&T_˜R” zñDl·¡³¢&¼6qŸÒý+Þ¦|‰Hê(ÞxrEJ5Ôõ”óÕ~ŽÊ+P+×m©ì¸ ëŸ|Ç”Kÿµã€,åV³€¤CþCTXÜÕâ™ “̘4˜ópáû“è}’-’¶†\Éð¨í ž™&/_Y –ù0RÌÐz'µZdcDúçV*7{tL_·˜Aw2AFÛf „n“'QF5 ÉÃŽ¯¬7 ÄÏ•<{ìmRÞKètOÎZl}ÕW}qÈ@ ç.PÖ=sÍP’ä—c\~ø€Hdzý3YúuÎHFý8ë f¹v'_ ñ® ¥Sð†ØÃéà ¡âmNñ`ÿ|{5eÅox†þ¯^4—¿Úka*Ð’œ¡¦¨X®ù\£$Mßc&ú–M'89ñÔpí-¾>§3¡nŽRx‘0σ'r°¡ ü~[vò.£úÀëHIM]o‹vb¡ç~}%B¾ä5È2:k(íÉØ fÙ°¦ Qžê¯[~(£ôªô”ÞýÃZPE›>áìÁ÷´œù®2*õi‰£X¯}õMœ4óbUig¢¯ŽÈë' ó¸ÌÎu#-‘/›üÛãÖ<çI v< ­|ëü/’:´¨n¿¿ûkQÞ5‹C«LLšlçâŽOáœ"^¦¸+ÀJøôîjŠÚöëíç«é\ødõRû—ÓO×^úP}6OS¯/‚Ñ&çÞ|¥{ŽÈJú%üšÒ²ùªÒ<ŽÔ|Úû@ÆYQìDÀç AÛ¨¡û'&JâÄáž’uÙë“\íëÇZR$§õɬصÜ_³(D–º$?ZÙiø-â¼U,}òè¨ïÞ®¯Â2™yíÂ_Ÿ&¼D¤œÆµØÑÕ¿·ÕêšLèži2¹.?2aK¼ú¹»]÷fB6ÞÓ=रnë.ï6`tv̺U¢Éo‚ÛJ6§ýô›íaüÖïÅŽÇ4ò×TF:nÓYõääõ€fó¤Õôó{ÚÔø³î j¨³ñê×`ŸËž ‰FâPGXþBR=í¨OŒÓOæf¾÷¦ i—<¸ç÷Ç ñTWïýý2—?¿VUøäõ<)sTÈgx©µ÷ý7XtW½`øÙ “ñgHF!8œlsÚ{!u-WÑôÆÑ¡Ya XC—³'GÑýÙÙ¢ÂW[‰žƒµm²Mªe´¢~¸ÂHŒwóÁv£ë0õfÌ„†ø 5$—ä$múii= n:ÜJÙ?tž>³Èâí|ÌoÿúnÏy=s°š?õm5Ï@õ>`æ^[]oÕŒþ¼L ãÚ8kƒ÷ Á®O çXî1îEÖ2j5i™\󦸸k1Q»ÑlÝkß›Ñäy¡CÿX$m×EÚuFçtÜñN©Ú,•l25Ü×H µ*Úïvù&Æší×*gè¡8úºÝè¨ÚDoVÒw&"90­$‘(Jé vô©.#Àb[¡r):nêzPí–2Ïïj†fÉÂ)$…ž¼/G<½M¾ÒHôãf±Ð=ëà‡qÁJ-DÄB9bíGçIößJú”Áw= ³ÐÙ¨³ÑVo˜\™º˜ËO½ËN§8²íòCgž> ›35Wîæ!ûuê--EM+תáöýÀ—8ÝÙ½UY«¡^í£€Ï‹.³¢•(“ËÕü»\ïBºFÃüA…ö8¦‹’”ó‹8„¿»ûݰ™ÌÚåƒó‘–ZÏi¼-Ø©º6Dq–A†Ùé‡t_èT€Âî‰é"ài/yžçŒ)’'kêæ¹ITí´Òy—èq‰,=£.ªrRÖöûä˜ò¨¶ŸŠ·®ñí‘Üð”pQÇ0Kg:²"‡u:)!…²òï­¢q(Ù‘áNâKÏ:ôlÓ$Å|Òèm+5sŸ°H¤l Û¢&br?üf'íó«x,HJͳ2ë”·\œ3`¸ƒ†Ž˜i—d aµÏl&:)¨ñ+4#ˆð­É?IvcúÔÏ’ähÕmÜh'/ŒÜ”ÌËâÍg—Àíý¹Gw8Hë¼g(‘[ßò¦ÂÑ@õq#‡EéE^zª$/g婿!úK—윔AÕ)fÅ킳 ß¾éHf¼ý¸[±Îi¶+‰O#](ÝGri‹j ó*E}xO•€¾·î:?E^ÙÉê\&¶èwäl?'eSÙ1©S[åõ`ß0ízóÔˆN­ ›1<+Ú¹Ž¡EžiQ¸5ÝH^tÅ%!i‹ç¯W ~jÔü^ú©œì™£2'.¹ÈàwÒ-ëü2œP ¼‚ü•‡›˜JhWI³áŸãi³Òu cž3V’Èh´®»iˆT:ÙÙTúðÆÞ”)OxHdáŒÄ…_§ÿi`ª&½íá£ÿ`Gvw/GR>hf38e½ßD¹Ý¾`îल+¯—òR‘U ޱ0w_³ (Iœk™ÂϾ-7Ä« dŠ LÍRv#È[âè7lAKpÄ3R)ŸJïÎEŽ,nX]¿xAø]Jsùkåµ§ª€~wähbÕ‘6²Æ"F"ªnTï;þÐ ]Ò(Ë‹,ß'œwµvðÔÛ c~v¼Ø?rTâKŽsnxö@6½1œ€æ¢ 6&vƒ(MÚ&Xy)JùÝ\.‰Œú#.ñ«a·uÚÅìî«y/<ÁϪÖÚ‰¹ÒêŠoÀ{™–Þ².}Œ+"ŸM+wËÚäêÒÊp¢‡S×úEºpãÞ' (óH÷ú3_uÍ`ÂÐàÛ>ï‡kbÞbÉÆŠ59š¡ÀFñâŒà%m“³ZÆdûÙz霛aÄ–ˆy„ø#3òDž›î Ì¼ûvÙZ½ÜkïU]‘ÎS’âc=Ç»’äï¼¥‰“ cCÜ ™x¿ž6õ´¯D çdK‰ªg— %𮯖 W-‹/îN·šÛß¿ª1./¥ò¶òƒÙR „ük :üc>áàÁ$z”•Zp¾Ò^åþÒhÓ/*€œÍ†ç6.Žð^R©‹NãšÎuíÇÎ}}!áÑÚ#¾µq®Ç.§¦}z”ÖA)LC§Èx!m±ù†\§ˆÅyQc -F»³lÓ·1šg­ÉÀîòAç-Ÿ%,!Ï4c½u7Ê”K›£丰ÚÌYû …\ö"/ê_¿ÊxÞNÈPgü–(‚)NN0AqýË…¸[ñºàÑWôù?Ò=Ö£¬™'¬³œ\âõµ¥$$Tí9·Vc¯µÜ¬’IEECnöG÷ºÓ|ú$í("ÛÊÈàì!6GèÂ&îo=<îsö[G£»‰”}´²gR ÷µÆk渨!ÛEݰøÖ_z}R)Ù{X´dý’|õg4×G!ò '¯Ú¡òïw[½…¿WÚ&Ó5¸0£œB­ZqÂ$aW![xÜ9ëÁS¨£§H_¼0†öçyfïB¶Ý€å&/:#¯£¤.8V>¾wR¡ŒºËÆÏdŠAŒÉøD¯»è yZ—ØŒè¯w)Çi©Œ åw¾†ZZ"ÂbFfÚV}FAŽ5e_e'·`ejãd?³¿8F©ïÕÕi/ƒŸ‹Iqq<ò<+L±¯ˆ˜U þÐÛè *ìô8¬Òö…@JüP?ש·+±­%Ã+«üúbÝÛ)èÓÚøg¶OZH-'Ç%食ù)uz–¡ðsµ‚Rêº<½d[HS]„…'ÝûâÝ> endobj 377 0 obj << /Length1 1376 /Length2 7391 /Length3 0 /Length 8195 /Filter /FlateDecode >> stream xÚ­”e\”í¶‡ABºA„¡»;¥†Ž‘î`ˆºGºSBRº”FJ@BZRZ:Dº‘8ó¾ûìWÏÞ_Ïïùò\÷ªÿZw0Ѐ´8¤­a–`yÔƒ‡“G «&£Ïà àáäÆb`u[¸C`P9 w°(€GD„ ía àåðŠòñ‹ ðc1daÎ>®[;w³,Ë_NBi'°+ÄÊ P³p·;!rXY8´`V°»'@ÚÑ ùW„@ìvõ[sbñð¬!VîK°-ŠÅõ—"%¨ ô¯ekç›<Á®nQf„HB¢5 êè°Û`q©ÃµÀ%ÿ¢þ3¹¼‡££º…Ó_éÿžÒÙ-œ Ž>ÿësröp»Ô`Ö`Wèºêÿ%Næø_e”Ü-!VÒP[G0€û_K7yˆ7Øq·²ØX8ºÿ^C­ÿSbl à’×—WÕÓeûßýüÛ²€@ݵ}œÿIû—÷ßÌó›Óq…xŒ¸9¹¹yŽˆïß&ÿQ µ‚YC ˆ! °puµðÁBœ  üx¨5ØöF(æâ„ÂÜ!ÄHà˜+Ö_Û)(à’þké_$à’ùMB.Ùß$ à’ûM".à?$ÄàRúMˆ,ª¿ ‘Eíæp~/€Kó7!²hý&~—ö?„8;\¿ QÝêâáF$µþy\à1®môoDY›ßˆ²6Ï?"þ2Ã<\ÿ@¸Øþiv b†?1(Ç?¡Õé7ò ´B—B´…@ÁØâaÿ ?"öÌmο͈ޜ7 öG÷<­. Bëð ´ºý.ÿ=ÁB¸»!ŽÞïD‰ß£@¨s·sÿ1,D?î^°?-yüˆ¼~#âÙâú#;/"ŸÏßøßWHFæíÇØ^DYD_"!nøÿq´òpuCÝÿ~ñßlA\[0Øl…57³ µOk /,üZÆŠ,cÛ”¨^×9ÙŽ2ûÙ±dPÅ…u¹Vÿ¦2“o m‹Úë–Â-ºÍÿåˆüaKbúôý–§ùV¦ïG ý“L5ŸMgÚË=üÖÆ©³ŸÈËCå)E½oúöòAÌrÚÛiºŒ<뻲B…ôå3u"Âjùh4 Ò]Ê#ù#–¼R’P—ÆB^ÙG`4³-ÃÚ<ˆn 1ò’ïÍP¬GtÍ/UÞøà®Þ$_Õš”E¯{ä©eÄWÝZ-Rgó9žÄfM1²»gTw8ÏWOå^k`<å¯PˆRÂ…D·´Ñè Õ$¾g kZ*h³37T¿ .œ¡´SQxhˆôÛ_©ŸÄ‹oùHÇ¡ãa$ßÃ{ÏœÅBÿ Ì§6—0¼«ªÈ rK¬ÁÛ±—/~iÓI@ ¢ŸÞ çPZn˜ý9”:'Ò‹%(aW”è"§Kp$¾nŽ $Œv謫&¨#¢¾Xe…+Ìo¯TOüƒ.Oß)Å)®o(-iA÷eæñ“%lY“‚šjå…Ùoôµ©)!Å:@ /ºç‡%š=#ËøìEÆqBMS±+©ÕtcÎQÔÉÁ] [-3*þmÓSë›äWýÚ†½ õé5O5‘ +„ŸxZ¼‡Ä´Á”90Îúp€õBw‰A]dž¶îM¥ÊÏð.€mþiøõÁœÞNûfé´:m»ü=«Å.óÃÑúD¡:}¬@ Id¶>]½fl¿Kò"YLcØ·$ŸÝÜú}\½}œ,5ÕÏâ„ÁY½ösWìpB1ûl º¾GŸYésÙûepñò㢋”ô_Fê±–mE>ÊËq|´F¼fèºßc†«å3t)ãÍÂÓ‘«=úKXÇòÉ} ‡hA!ßkí`j¢ÐŒtjsi.ŨûJøKÿ¤¾ÞSÉ]ÇŸ˜Èñ`YH¹G‹ƒÙèµ¢³ CÍׯЧ•Jú mbaßEÛ>µg]5¥+ð¡F=ì·´®Üù ×vëf­E­ B©ü1B€…ƒ0¼¶¿‘à³Z:Ôàâ1† ˜d'I=cƤÌgUø^Ñ¿pìÖ¶—!úìçÁ¹…™[eöyœ‰¥ßƒÚüÇCíV J¦¿{çàãmaµžâª‘(ÂÂεéȎꬳ¯¶¨\îÜ/¨}òd7$s®ëcU/¦³ûSb°¿-Ìòa¯ôšóϧŽA­õ0 ÍôvóWýŒILb¾»ï%  ï(M2§†47ÐU'[¸ˆ¤õAñ¤ô+SxkÍ|$ÓTÍöe C”¬¯@§TMüÛvÊâ¹8(¹Æë¨?ÐÝ‘3–ý“yÀQË÷ p·áÂåõìÓwÄ´fF‰0èêÄ–ÃÕãïZàìnà Õïo2gaå4q;,\V9~èv‚'ÉJâí°³öB űg@ ›·øù«ù«éõ†G2ýZz8v=\ƒæ¬&"¹„¨mW¹rk/‘–ÂÕ.TXÞÍ&=N 7IËÉIb¸â °6ç ™l°\ŽÉ[wþÈøŽ>/Âh+hÂøD<§wÁ»Ÿ ²iE®7ä|›vÌœ'¢ß‘(m;ëÛË„ì«êW$”VÙú’ÔCõ‹=Ûî¼íÎOyÙ–Œ®ØÄº>òeºl§™Pݯí8Q¤œ±—f}Åp-9/óÚ£ë¥Åá¡ ÖàìXÖbŸ^/WT]l*§¶‹')YEùF?.½ >'R£­w¯¶žî0*òV\¸éþi×’“TÊ‹‰ãÉ|ç1=ÔXsҨ¶ãUüО“Ïr'ä¥Ö=ËéšTÖÌrd‘¥°–LER“°O9Ë‹þªuîÁXiKî¡F:s£Ð-è Òû¨±s‘º“Óô Ÿ©û»mé#8µé6ðH[lß/®g@rl˜¾~з.öÖåNÇÀ›d¤ˆP_é»@õ `â]/næƒ [v ,?Ô[!M i±ƒåʾ)€`©ú+7f¶#ÀÍ·º6m/ΑŸø:¶-»,šR½Ýo$FOëþÂ)hþCØÕíõƒÍûVªSŠ·‰hêK¥tn²¾/‘ãc&ùgSÆ9'»„/-mã­ÃJN¶d¾ ÛKL=¡H[b\ùAg†nrnrÊ0þösEP-uü${‹tyk‰dÈ™Cü–ìãk5ÔÙè {®!M±²(Éâô“¢%=”Oì²ù‰ÏÔ^HZ’¾›S?/Wø†âug/ûÑh+ûKµ¤ŸýøñÀ—Õ¹¦ŒWLp˜*ÿÞNÙ¡¾Ô£Çkø&†4à/Äçc‚«o›’ýŠl6 )>j…“4šã$ùŽz-V4ÍÍ”Ý5Ö†ßGU„r5¢W.o|­©xþ¨ýð†¼ÝàsÉý‹Pr¬â9ì dÎbT@Îé¯þñÂÊÚ%I^UOBoÞÄ.p¯ê­ºÍ"þÞü.æçµ>ì~ ‘ ÅÃë¦t‹N¹eÃÝ1Ïpw³«èÀ¥Ü¬ÌûQ—ʼzÇP7ÎŒR.ëþ.a239îíôùN÷A гTŽQºÁ¬"öÀ§–Š^¼“Iu&„õÂ^ƒÀ#Þ¤ku†X@ÙñãÅô/—|-3ú­€Vi ‰sÿæÓˆ,“‰ ¨æAï'¤ËW§pÜ/ž™1h¹w]è9{ò£n|%ÐÈn#û»©HM-yÃÌ8¿èj³æÌ5½Y¬£¥Æ)S¥jLí“&A j–òÈ‘tÿ«µs.—/QÊ—±)ƒ=‰BN_,Ho†JÛ'|nšË·—ìÜVÈá1Ÿ§jRH‹Œâó=)¯ n-"âš,a‘ì“çIœQ_ò¸aH0{N‰Fjªt54èñè8оl¬dN–<Ëúu·2¶â&`™ÕzW#Ó1_ƒqpm‰73¸¸+ìëçÞæ;üö>ë…*XK®soË;NXü‹gORó²gëÉë+Zô¶3ÿ&ÌÙåÒjþÍNYÔI‘ò‚úW„)žŒ¹ÂþµÊlXÑÞàäs3‚ØFõ£Ñ„³ÃÇ}ÃNôë&ÏÇ›^„ÖEÓŸFöøäÚ d…†2™Et%tö Vþ)¢Gdç‘€–ñüM}_p7è¢+=·~"Ðâtùñk·˜·Kˆ^TÓ^ŒÓn‹ùµ7ZlrîÔ‰­YÕ÷˜ã“Èð+8`°qzÐTCEE#®·ÍÓ{ób3jYtä‘xŸ¼6òûYR²Þ þ'dìB–èhï°AÓG„ßVM‰ ›¯ÏU$ç†MÃØ¯WÑ…ºÌÇÜŽïÏ;£Í^(ñò™Ms°`ƒÝî‹§Hrûµ'’±CmeøQtߋʫÆxö0í²)õ‘åùe;H éJ&7F5–[9=š™$­À‡ä”áªÉVív¡è„* ºmìóV|ïvܵ¯>ádÍt«:Þ«¢È¬¡.õ=!9YÕ5Úlø ‘†@‡n7¨;ˆÍGV¢Ꭰ‹¥üP$un·bS&Ϋ±oîh›K³Ç+¯°X1ý¸&jÃåôóCãÓQÝÊÉÇ,fÂh``0_jÚûŸ˜Å>“Hý°ñ—Zt_ˆ7JK `þÉå¥×o"ûÒÞEl˜û¨uæI%¤¤ûŒY~‚Ø{«fÒžã¢×ª*m9É×´ç™ý[µc©lVGu Ï´þ$bÊE»À„?œ‹R¸ÛãÀO„ˆ"lQF´aAÕ®¯ït3Gø+ЧÉðN?@Ø“x\öÓ(XZ{±a½ß˜®ÀìAâ¡Ç|J ¿–—È}Ðç‚åºÛcTùêÃ%(xÉMÍÚ›îo4¬¦lÞ·ôà¬Ú]¦%x–ւȘ HðHãÃ8'ÌÝŽ–ÕpšïÄbøÍÙ£oš–¦žÏDäQm$|£9+¬¶¡6,»è@¡ÝpÓdÜiÒ¦ß>w:,~ĸXhÒä¥ó‚’˜–»eIfwúÒ‚wôsӉ޸¯QW·e£Offê¤l²¨$[èÜdŠ©›ˆ×uÑÂ%j/µð«=ñgi¿æ!§»„)ÞÚ¤ö˜¬Ã±ðœÜ¯©m·¤+¦ÇžßX 4n6¿¡\‘UŠŠ”%gFK2Š 1*ån9 äÇlFì­5V•2íµÝç]¢lNõØŽÅ%<ÕW_ŽÄø*`ŠÒ%Û.³Ï®C­láßX®Væê2&®JC-WL*^´n-l ä®{ÖÝnÁs\ÝCñáEhîw÷/äiAÆs××9ÒÅI ï-}µ³‰Œ$©n©s;&é0¬™L¶ÖMÞßUáè2£<´H;ª ]Äɻ۷Iûu<6ö$Ô@ÖVh ™&’ %ë)”½\â½óFÖä)½…úL_U÷J%—³z}oäýI¥:ëâ OÜçÝØ¦v·ÍþI .ky®òjõüŽÒÇRœ‹;rqG7Wr–<¾Jaié`^,0ší¸Ç±ZÄ\ ã§'Eó))Û¥°-NI­«§XìéÎz'Z GEe×1düy™Z˜ì„ùp¯dÔüz?söst~j+f{?:òLü‘ˆ?~åBLyøìù[GµLø`tÑb2%›>Q±ª¢BõwýøfÙǷĶcZÞè=ûè{ÏWOÙÝ­¤J_X>=ÛÕY‚´¾³íŽ‡ä¹ öÇ9Ä4Îs: Ú»³«kJ®©qS1¼ì~ÒŒj Ä‚P×}&¤j¢=[á ½¨JÏ(<"ÐØI¢en*H©">ÒGÁpŸŠ°7º)o>Z°$ýŸ@¾Óõ¢F Ø n–°¨Ø™Å‹‰E¢ÒQðsÀ¹ËP¬ª®ºU„óñ½z~|ø¼övÁù.%ŽvPg£†ÕµÕæù"À¾® ™=4½1 `«QÙ5§t6ðžÇ§Žú%‹?ºo÷^K˜ÛÜÚ²&Ç>3 Ü‹§Yc×!¤Þ_Ò ß%H?e“t(ØO<7˹쵱!£®Á:ðœntÖJÌÕ¼ÚùÒ,À¦„wÌC÷l-™²@¦ on˜»ÝéY^h2*îbÛ£Žo,~NÃ9Í“³Ãx^K”Q¸”¿Q9§ì¾aÏïn‹„;f11þSÈù õýâúgË'*®C)ŒíV–ª¥¨\1Ãö½¢!;Ÿ+nS«¿î]ìN'ú4Ôj…ïÉ)Û5Ú¨LUündE\mœ0´zÆÉDl¢’M|¿êÓÄ,—»`›¿Çiʽ¤¼ßCc¶: ŠïCMã¤Î€TÔâV£•jž}Kþxp«»›ö‹WeÝ4Ë—æ@Æõ^”ØñyŽDD·Ð/öéÀM I\ž®݉&ý&6ùÞæ Ü29~m9@ûWñ…ìŸJh õhO(F‘ús6´ùˆI@âç¥ÁTÁö”çØÕ¬„êòšrotaO•üÏ^L¯‚tÜû˜9I» SÚ0:öYHÖ†è湌¥¸7†$ˆñ‹¹È>qóË'ÓDŸnž$Ê+Êñ£¤bB yÅ×üþe<Ö²·ÆÍîJõ0ÚÀÕ¶˜¯Ó[јýë¨X˜¤ûPKå‹â¹Ÿ¸i±é½SP»¶Ù¼qMíþd!ÿãýc²‹È’ê«NgZJy“pò©7«C‘ôRÞ ÃÑF >+ÜÍz|œd¯MâäÏíâE";{Ã…–³²-p^w²Åª3Lê–0Èñ4ô½ ÁÈ~‚m)pDFY{Ì’ñ|£•Ú4'ýã2VÎ¥ 4W‚?Ie)$‘'ÊÀ`ßÌ‚Gç^NáXÀúÎûÛ¼œ;Õ‚IÉ|øsÕü´íØóâÑÁ+¦Sö©%‡µÕ®¢áOù£KúÒ.¼)Ÿ¸>&@ëŽ!k3‚ñWeɶ"Ã/Ÿeá¦@¯_Ú$á;.`ÁêM£4EÒ]ÿu¥N'ü,KyyyÊT9'pw¼M“. ‚R¸PU|!{õ!„òÛ.8e}õø>ÑQÉ*ßW§À®ˆíb”5?öûªöî‹íZCW—Cu¨JÀ+}ì=‘ ¼‹jÚ°£ˆ÷ëûÝÕuÏå)å’lë}ô¿Ì•L0UÁM]ƒ—’HF“ø¼§£vm)Œv°ƒ·G¾~Ë¢è3ñóȯ®vií ®¾iÑ ì³'6ù¹ü‚ëY˜à@måF{©HAŒ~|ˆÇPt…äÃæÝO3câT]Å>øûV§H.bw– ‹J¹Á©ªéÚX]Y.Ó<õüž~ê#†vx„Gz“¦_u ;meÇ%þyþb•>žÑ FrŸko{+ãÍa¨)ÝON-9ƒhÛRè2h“AK&˜æ ›Ã.®…þjÑø#Úû­×м×,9¡4¦V_o”ì[Œ+áÁº*fžé]MÜ“–É %ng4³D²| ¥BN$ lôø[¾\%O6|»D5¨˼2‡_µ/Pмï}p./q™yŽcôÃÜM ½|´¼^øt·uÔS"NÜ”*»ó«•wZ<÷âÍd.Ò%þb¹E²P "øÝ¢ÓŠ$É<¬“eKWÅL¯¿u> endobj 379 0 obj << /Length1 818 /Length2 1876 /Length3 0 /Length 2437 /Filter /FlateDecode >> stream xÚ­’y<ÔûÇÕ(ŒdíP¶Ÿ"™%Æ0Š3¦±d=&e‰sÆÌÏÍÖ,̆Æ!dID)%iˆAÉÖFB²Gw´Ç’êH®“,¹£nÝ{;ÿÞ×ïŸßçy>ßçû~}ž¯‰‘ÁKf†‚ÎL× Cbœ'Ž€C"$ 51Á±A"—Êdì!rA €´³CX@ÚH4ÆÚcm5pL–€M¥„s3œùª `é ›J"2O"7¤Ëfˆ4€À$QA®`i4Àwõð9 ;$àH$@¦’¸@(H¡2 ðU$7F@.“y¬/­HÍ‘Af2Hs@†Hf2h€ †Aá^LÙ] ŒäÿõípgæE¤¯ŽÿÓß D:•&ø·…Igñ¸ ðd’A6ã[ëð3'H¦òèßvݸD•„ePh €ø\¢rœ©|ìCå’Â0"~ªƒ ò·²ä>!À±x¬ÎâËN?u}ˆTwŸ€õuîªý“FþGËbSù@†@ eFÙ÷å/ø›Ûð “LeP€(€ÈfPÙë‘)¨ 2È@¾ c0¹²#€,!ÆdCWWj‹à>«¥ÏÊ €¾*ÙÎàįÊÀìpñµ„DZpöIÎý$ÿžˆ““-ó[îDÉøh4€F!„ÿã#ñØlÁýôÜd±~ÑaTÙ@’ Ò&É>!âTÝÑÒX|QoÙ:sŽ^^IBgcbMž=ìÕs{~v}Ï>Ðàz¢zU¢–ûƽNC½Ø\‡i‹&Àÿ¡Þ¦¢e^x† 0Mzu0díý‚Ö&Uú¹” ©á¬z‹éÁ­sC‘ë®x¼D¢/n~sL¾ôñÀòl졸®RåÓä­c¹Ðà‚[9àþɡ̎…×.Ì"¦T|_ûà™9ÍÒÍMÏzª’g—Zà ù=o÷’œ6ÜE¼Š\›ÎÜØSe·A|·ùÞLÞÛôPaôImgO.´¸:'´Ü™JºÝÕ}¸É’3*wØ›Ñ6Ü9Õ¶Ð+yü˜iôi¦Þ)ªøÊm³ MåYL}šòqýÒf}“Êm(s;¤°ý)/Œ) ¦éâ?x&’2w~aà”5Ë?E‚Y¹žcd?f± ô :”mÒfrŒÍÏ¿<2éóÒU·-Žû½˜Ï ×õ’Ohh•à'Ö<ªÂãl­ŽÆÅÞ+dÔW,#–¸BÆprÚK-ßnt³Ä$3-òÀ”è¾²Òí§RÎ{»%ø^KÄœÇË¿)Ïe‰åmÖÖ Ë‘ë³Æ›uòZÛµ|Þ‰§tN¡‡ØŒlî‡tªØ+³‡î{o5©Î·øài;ZWŸ«nòþˆîÔœý9‘ŽÛXû­-$Dw}[^¬’úŽºdé¸?•ÛÐ[CÜfþW^Ãmº§æŒµºRID¢d«î0ÌM¹:–óz$Žp¦í»]¾RÅì¦ 'Ò'ßÔǺ™ ~7- Äþ’›ÉéÞr`ï Mt­?‘³õH¢S%Xºa,ÓQ×Ömøñ˜¿’’¯^ý£<­ïSnå£í»²¾NPÚ™Öº¾/»Å«ÚöNó‚Ymn Q#—22á•J‘, +ŒÇ šP”’iÛ8þ™îxÈ­ØÅ‰ºzê{ɸ{ŽŽFc5röZžà 8É9Oå꺺¿Ô'¥ä·@9.ÍÓÑ-xYø}/Ó‡Óuš~kòCMÍŒ¦wú÷<–L_ö†í×· z°R­ µž3õžžŸR{Æfà³UO3ž·Õ»)ßíÍR®ÉóQS¬Ž1§ ænÈs°>‰sô¸ì³ùR}ð·g[J>ˆŸ”Qœ„.ôÓÝ‹®Y0ü9Èœ ü‘QçÑ«„âG)½SsZ檛ޭuŒÕtí÷ƒH€÷{DÒ±kæ}B˜‘!‹ñ¨ÍÉŠÿ8Ñzõ×ãÁ~ûMÊÚ¶Fn¤// ï%PÁíÂgî\Ї¸ž¸‘á.ЏQ­Aî´ÑZÄÀ©…Kk7¾dwX5¶¬Ýì•~Mx)ÃûâOùæÜC5”~¹Zl/g×}4œîŸôØ>$‚6q²("ÓÈQ[Œ ƒóX½ Ôâ,ú@íŒ_ìyí¥h?Æ7¡j`^>B"¡îý޳/VûFA1Í­ûhÅÍ8;y;VV<1":¯¿f¾À( ‚xûTÉ?8š¡i•›µÔÁ1ÈT\ÐË k,¦ÎwðÂ÷p\²!–s¶-î@¶ÊÉ)CKí¦Lâîº$6˜”\O]y†ÙûÞÚ(Ó±<¾ ¶ýJÐy‚8iOŽn!¡¸½$¾ 5¯s÷:¤x§WR«ÔáAáÊ‹ŠŽÂ›¤(Œº`ûqyE–eÔXwÑaVáÃÓµ¿’jÉz4’vUº[Î%Å3ñ—úR•Ÿ©ÖëDXI³.Ú‘jÊ^ój`³#»ôå)––#´ãENU:AŠQüŠŸÑëE—§¶ ÈGzÓ,ĸ,Þy¬È s¤þíÐ^.½þÒHËõ×wÈ]9EgÇýl@8@nLËxB’y‡‘Í3b[»e‹ ™úäŸwq6sߨl‡Iqü>@©ÅQ˜›•©+¹Y_Uj[îø°üéüĨ"ïg1JŠéÃ÷ýkp}×ÔçUãT­,*ÛÁjWÖpQ8Ñ¡ê/H5ø}ñ•åÓvÇíóh‹ÉyŸ<»D’ Œ÷÷Ï ¬t›»¶Œê7ë?gX´ÜϘòÀ»cM1 TaÝå46l4ìó×îÖRóÃhoï³-§øa¼ Ø|ƒcüay]cØéÎ%n™6êªò“‹êº°ä•šº:Ï›Ìú_ <üÖšƒw{#j¦ù÷f©¡2hßÛ=‰©FEdÁ§~»Ä}Az\XÅ­abÚä®ðg5 M‹¸åøn§’@W¿\GRə۲^Eì¦úܘ¹¶ÑP«'†5}K5óòÌö-ï>æý3; bò‡'˜jÑ[^oAuŒ4©®yÞy&ÙæO̸÷Þ©Nedï,+T5†Mª§óñ˜P¤ÁóFw1‰ï7Xt×áüGS«6…ÌÑuÉ=!ØÂ)ÌŽ÷íåÊôà&„Æh—Zwö®t„ú|m:Ó•YÙ§)âlm\rFoh,²¿oÓ5Q>†é{¥Ìo] O¦›oü 5z¶ýèÜ¿ñµ™- endstream endobj 380 0 obj << /Type /FontDescriptor /FontName /ALEABC+CMCSC10 /Flags 4 /FontBBox [14 -250 1077 750] /Ascent 514 /CapHeight 683 /Descent 0 /ItalicAngle 0 /StemV 72 /XHeight 431 /CharSet (/P/S/a/hyphen/r/t) /FontFile 379 0 R >> endobj 381 0 obj << /Length1 985 /Length2 2327 /Length3 0 /Length 2956 /Filter /FlateDecode >> stream xÚ­“y<”ûÇS¶”’DÑ\Ïhd 3ƒleËd5R3ÏŒ©YcK²S$K¶ˆ¨±”He)MÙŠAÉ®BT(n¶º–ÂoR÷Öíþû{=ÿ<ç¼Ï÷s>¯s¾_Y¨²!žæšÒ¨ e„ B0Æ Ž àB’•5¦ƒX‰F5Á2@¡­Ð>d© :H¤Žº–,`LódÒID o¬ð­H0¤€tK0X†Hájà°dÀކ# ¦ `H&¶ßNx¶ 7H÷ñ*B€'á€;H$Q…T¿Y² h€æ÷4ÞÇóoä Ò½¹¦ùo.®GÊÚ{Á“¼=ÉXæOÿ2@ÿïöþ»6##š 2RPFjsG€P×P45‘ÁÿªÄùйk¯‚»ý¿c‰{W@ÐÄ õ÷Ðpº'Ó*¢ŠN£òÛ‹ùyŒˆ•¬n?ê|°)¼/‘‡ÌâòR,?²t=sû–Q¾Qi¿¯RÞgk‚·™N…z]Hï^õuÍ ¨–:2›‰a¾ó”™ŸØz¿¢ëÓ¤uÇ`KQŠóÕÆìéÇWläMìÇ^A×Õõ½S{)b¿æÓL²Ctdù^5¨­HºWQŒzô€ŸHJïÀ³ðÓ'£«”^ÎÐj|D—òs“W\?Wq°ï¶Ëø¾Û¶Zª8¼KdP€C@RÚƒnÔï™s‹ ùãŠÔ@ARÈÒ9j¹ìúPÓùCcé±ÐîÓ‚Ä ŽÉ#G§éW‰zpÞksÃj¤è•ÔblÓ+Ê£§ÓôùÚ»so†È?QŸ×ªÝ‰awEiçUZY ÎŽ‰Œ 3æÌ^Ò¶µºq׉Ëkeûí|´¿ƒ’§t}«Áê"$Hò DlCȆ'|’¸îñ!Þ¥f„q^3ÑE¤}x[³ŒVAÉÆ¼Óæb¾èðìásC&G0}EÖÊtÔÔj²:` ›æš·×zå__Ÿ›êI‡V^멚Û;åp ¥p›@)Ú¼ìŠÎHVSkƒñ'ï[±„$©oàÃ$"m6±+KOÙ¥eÈN(‘W?ÖV‹L³µo†zÄž¸Æ+€t8~ø~¾Ÿ¹lϲ~[pj¿ù¿9–E:œ<ñÖŒs_½ FA'í‚›áAÐb‹¬‹1N†„Ø[ER„’voM\®×d¬‹v¿½Ñ]Îc' :æ¸ANJݬ9ªAqa‘µ«ÊS„‚ŠgÆX7_.¿—øFŸ÷qÊ]Ç rËìjcñ»¦÷îyp6ݪM¡±å>àT y¬LÚ¨ôäù}¹çÓä\X=ÝàþÃâo••2°÷ü,Ö¡›ÌÕ¢·ìßûÂñ¢±åódiÙCüvçà@ Ô¿ÃÖƒÖ~ZJÖ¼© xCV¥Ô²dNÄf¯ä—d/ô¼6(_¸Í€«ö7ßþB“Ý£ïÓn¹ÜJÈûÊÞÀV“Ýiß—0V>Œ26®ó…fYfX)汈YôcDZ—ˆÙ„ŠMK¢z"Œñ¡áé8íÐ-Ç9Îüv‚ŠYœ]Î[Ÿæ÷]Êaöˆ é‘#Æ‹îã{¡±JYº³Ñ¿|r•s´¦ðß¾*¹ÉíÀîY¾¸ÚîÈ<›:ºÝÅÁ‰·ò­©áT0¸þ³̯k#Êýþ§Fs¹ÛÓzŒ´»Í°Ð®ó+ä·:ëJû:{Êty8³(Û…ÈA‘ŽÜw%ŠóuÏ=ÄÝ&I!ÖÉ*ÞK ¶Þtñƒ‡6Ø:äjÆt9Ö.êãÛ7H»?ˆ(—,äe2ÖÎJ2ù8Ð\W±~—5j†ÿÀ¶CÌà/Ë ŠÂwRo¢á¯¶fÞ#VOÆo|è–blŒ¹ö‡¿‹@tzC/æ}bøûÓ;ú"ÏdìŽ4_ˆÛRà×M*¾:7ã4ÜúÑ×0øËmzÂÍiMNóLçîiÇ?z'Ümåó-æoÙ,•a&Éñ)¢5Ëu§TžuH&¶®n‹´[Š{kš/*¥ÖR6[1=삎}ŒÏ‰©“MÉÍ\ýÔ3T×·Î , Ë{û¼Á­ïb—Ïj¥€¹*zÛiÅ…ªÐ¼;RØkg}À@¾$BPÞèàË+ì•T£<ѺÑîÚÍ ÞlÉåm™fâ9ÈÄfªééMá;u²dE¢µKr.ŸFB&Ëùw•}Gõ>aôºÛ]g<åì¾qe«Nô­ÜÁí‚2mdDÃã¨dË{d¾&®Ýyvœõðrrsˆk÷à‡m®®‡\,T^Óa€Ð–Ó¡±Åq‹Dùäú½åO+a…!Ì«Àe‰HzŸÍb¡¤ŽaLý-Á¬üNœÛ¯z…„ º‡&w«D+÷/±Ú˜³GA<Ù\¤óŽsN¾ÓkñùùÈ7"-Si6:±­hdÁ»úÊ“]ýò2½¯¶“Ç_ÎHysâ]‡g#ÆÅEÊûü† O-Ña&VbëÅgq•™WyÒÅ)Fg? *¶›¡ß¬'Y[Œöc!©lY½eŠÉÙSaõ+Î¥²ª§þµ,ùÜë ;-ù ÉIÉÕÇdjY~p9»ž†ŸYn…'…׽ΨžïŠ õ–­uk{£,õfáM%|–Ÿ³½ÙÞŽ-]ͳE ˆjPj/Ñ]°+×0íiLè~wßå~Ѿ'dsa-T„°kaðQ¬<ku0ƒVdA–½špüâû²›~õfVÆ‚ºÖëš'• ÆËJSšÃOqÙ#`P[6­µ³Iølߪt–½ ÍÜ¢ØØgÊ>ø©Y¯ŽM£­bEK¿¬"ÿ ±ëA_Ñ,ô“âÑ3è-þ3—«Øö€æ™Ìó0ölþ)ØÚL÷ÉA„äâ¹—|N`ñî®õzm µÚŽ^øÎ|’–‘¾ÖžÒC‹ÕØÁÕ#_‹wQwƒ;…ÃP9¦«½Cèà,—])i$ý®áŽ{¼a÷…‹w ¨<ž;‘4ÜÍš¯Áèø’[¶ôp‰Q‚ËÆtõU£„ªŠûpÓI¹‘ü¦s—`­ŠfSà<íÞ‹A¿¹²´œÖœñ©ý—.ÌdÖðÞßdž>'8wGY•0FÀEõ«?"på~’ñQ×`ï³Oµ  š ÕKçõ¯­[„Dae.´µ<™Ñ{}žêyýù¤L¡ÓT¿C­k—ã&o°“’ó騷g‘­Ü˜¤vÇÊí"ƒÕ‘ü:¬ jŸKž<(ýê"àÚ1˜D }·ÁÅÏ£˜OÅ«ù¤ÚSÝ|ì‚ÍjqMÚžS×?[¯6Vä,É»ùÈÅ:}úÓQÐeàéþÐjXe«*2ä‰j/°¯>w&çht2þ9¿›äÃQÅ9ÅíìÔ/‹xÏÄBÛÛŽÙ3vÛòFjšÊò7ìâ³”)¬ÖÌŽ:rŒ­ 1Ut®£WV!z 0ÌUKu#ÏÜŠ&ÁgdÛâT´MµkŸ,€‘´ÅnZ8§aw y¾]·(ÍvÉv<sûVÂØeòÍM‚an9nÅÝ¥ÕZ7 !jžóîéR³‰7N1õ®íKž1äÜ:æÿ>ÌÓZ endstream endobj 382 0 obj << /Type /FontDescriptor /FontName /MRLXRD+CMEX10 /Flags 4 /FontBBox [-24 -2960 1454 772] /Ascent 40 /CapHeight 0 /Descent -600 /ItalicAngle 0 /StemV 47 /XHeight 431 /CharSet (/braceleftbt/braceleftmid/bracelefttp/bracerightbt/bracerightmid/bracerighttp/parenleftbigg/parenrightbigg/summationdisplay/summationtext) /FontFile 381 0 R >> endobj 383 0 obj << /Length1 1208 /Length2 6658 /Length3 0 /Length 7406 /Filter /FlateDecode >> stream xÚ­–uXÔmÓ÷¥K¤[`I a©¥APAéŽ]Xbw锤)ééînAº¤»S:xöºîç¾ô½žßcÿù}fæœùžsÎDz0¨ipI[ÀÍÀrp˜/7¯(à…ŠŠ"/ùÍÃËÂòÂlê…ÃdLÀ¢^A€’³-€À#$ â á²^ÀîPK+'Û ö¿‚„Òv`¨¹)  bêd¶Cæ07µhÀÍ¡`'wn€´­-@ý¯Žu°#ØÁlÁËË °€š;ÌÀ–P.ð/MŠ0 ô³…3â¿.°ƒ#R€ío™ì¤H 8ÌÖ`†àUáÈj`¤–ÿ²þ\ÎÙÖVÕÔî¯ôwêÿøMí ¶îÿ·C8;*p °ìß¡ÚàÿˆS[@íþíUt2µ…šKÃ,mÁ.^nÿØ¡ŽrP7°…ÔÉÜ 1µuÿmÃ,þ­Ù¿¿u•”¥e•9ÿ÷iÿvª™BaNoÜ`Ïïè¿™÷7#›äuèópóðð"‘¿ÿ~þ«˜,Ìn…Yø@‚SSw\ä! ðä@a`7Ø ©È ƒ;!y €ÀpÿzWA(ý—éo_þ&ðÕ?$̪ý&~Pã7 €oþ!äsM2‹ù?ú‹àvv¿ýÈ9-þ@^üò€?Y×òD:-ÿZäSÿA^ ú"ïdý"ÅÙü íï|H)¶`GÇ?üH½vÿ ²žóo/òQ°?)þ"£¿oެ„@.ü÷]ùJHåE¶ñ‹ð"ÝHAžG®ŽÓûËäò"µºþÿwŸ?‡»yrñó¸ø@ÈùàxÞþ?æÎ`˜Óß;œêÿ2Š\0Ø lŽ;3 7 °þTó>ß[6{¤ƒÝñqbnÀ@K`U¢÷Þ¢ñ„Cíð0Ý Q ‘5´0þ@¹h-SÕH{õxj§‰zðIÙ3“4°Í÷ ŒP{Ó¿vØ¥†lÍЯ#j9g™Î—\Z0J_îð eQ„¢çOMÞžzÛø .Ïà%X0mÄã¦7¾Œkm/EõßìËóá39½Iç$w3T c6óø‰"aé*hß%~áð°»AVqy“mègtÒD”4D± Nhe$PK0$êHÖ×)«üÀ^eYcH|8¦èÿ6^A[Qó‚¦ “Ø<ŸÆVB>‡ \Ö¡×j' kZ/{íOlS6Â'Ë‹¨cQ7¹¾e9aŽ@?3üùsñò>ŒªõÐÜ“áe ª6§pò36Î=ö*Œñà]S«˜-'ég ‹xRa_톞­xÕ_©tD¾û’Næ3Íðáîˆ^Û}Êxª¢ÇYQ×m ëö@D·ËÉ %¼—¤{HЗjTQ·2nè‹y'Àã¤n’öÓpsÎu 2É<ÍÝEÝMORÞÅky!/ZBTp1UïïȬŒ”ºÓæëi÷WÚÌÆÆŠ«ù–o½ÄßvŒ‘Ó,ÿTéÏ% 9y67«À‚ETS±›×Á»¿ÔcÆ` W%ÜP+“vÄys8v9…áBTªc½f°99ÖãSÏA«#Qfõ.‡œŒŸÅØÄ¯$\0yHU†Ò¢«oÒúµ¼fýRmç‘v÷bœ[ì°6‡÷ÎÖ÷/ˆ3²©†Ì{\hE­Š?êfæ€7åæaÛþÚÝ¥UºÔ† “)¢ägú”äóv~_í ŠvŒH)O!$ŸÈ6' ÁJGá§´ ÌJ62˜º Õú P2*}¥µàÄÚý`q´Ÿß(Í}€#ºê)ºÇ¡ ð×f} Wèž0™púué‡S—»6±W˜WÓàí!û*„¹„@¹SE@³ðyßAÈ`Þ7Íšû˜a¬d¦Fã*#} Ôq°†…Ô"Y_ŸlŽ”ö›ôÄVß9NÈë»d,¨šÂ”ÂÝg¯ñÒrnžÞ÷$¶¸*úºVS0‚½€ôðJ>Hݘ‰¤Ä¼sçH Ê–v€3•¨z»*ÏÑŒ¹p0m`½á¿2xcÏÂãànageÉ«Ûø€joÐíi¼sG\Ò¯0á%Ògq•à«Lõ*ÅŠ.ø ¯×c0k-¹íÂ]ê–¼@е â–äÜ@ž¶öñ‡'"ž§ÆÄqRº0¸ö1v§¾9šHºýüD3$÷bz'Ðd|•ÔÝHì®qÜM5䟸¿ÈJÌ`®Þ“æ)2‡Zž€3!D·W`Øfh)iP/©uQ)§se}Xú½£­…€wâTŠhOa~ 7ã }™L2´þØǶ›Ï+õr~g“)*up'Ÿ`Âôrá[µ7gšn¯[ìr~×EÑÂëo¦L¢‘AûÔ§„úÅÍ'y *Kå) )ê­íS’×§ì‘*8× ÕfõSçÚJB…;JÊ:R§È&ù›ýºè䙯ËõèÁc´W™£nñzB›Cî™Ý;!8Ÿ2 ’iÕ¶Œ/Z¤ 8cuZ90›LÁYq É™U×”ˆµ-ËžpuÁE8>ʘ$Š¿ÿÚIü œJÓFwq 3â8_™$–³u7ùžé¼B =mR%‚ð“ `2^ŸŠ79lnŸY›èó™'$Ìjñ®yé—×%|ŒË†n7'ßSÌØ}g tŽÎýjÆ’G]uµe]Eˆ{ÍÑ|ƱX¿,áÁ/è°ýíçà²{¨‚¼¦”À¹×jÚK~EJXx^Là¼lR :Ro÷n[ÚÑóxƒ’*6·ÍÄSÄÜÏRН}™~‹ºK»¶bª˜MÆ2)K²4p ï°Qb= yõ8Ò\¿>&&r ²/{ð4šŸ§`8ÍÔ4Ä~©ônv/F@û™?ayËzÇ•õŒÔ}ôO]¬ãèZ!¸?žwpZ7ÚB’ÄBST½©åéuª`ß‘`ÄÔÆ(û ¦ƒ ¥±Â¼Xͤ/&›H8Ä®+sà‰±ûõôŒ—›QN¬EŽˆÀ ˜‡«qÖ®ä†~ËãÚ€)†‰K¬IÖ|jk)ÿì&m6ÞY‰Vgå³UƲ"”fë©c­âuTxàüiÝ«,`Ô¹3¿ôF $/¼Æ =œcÛ)=É/*i,›j¹7‰Éý¡ï29¶Yôíã¯x÷lÞªS ²„zUê:Ô`³ÛÖÁðJvwë¥À@V#TÞmdI/™¿Öb¯j­……øÑ‘.Ën^ø‡cîŠì~¦¹§Bûh÷¨¾xY`£”ËÙ"?럧VŸ)ß2®`¥à(¾Aò]zFÌùê|®ÑvOÚW5%Ý7Jÿ=óÌij#/†"ÁVÊl«)Ùò'2æE«*ë*ÆRŠþŒˆÐ<ÇÉOºkÌ H½Ü8VÚëMhes‹ÕAgï¶+¼¥\û]<ÅÀ·|W~»#Ë ±Ð=&WzTÂ&3£ðÄ|.sdÎ9ÉQot²ðiþ혻‰èõpá.ÃT]RG*¬šF(š!³¶êÅÝáþaö»<ù~¯p$|Î@Únð,a.ãùã*ŽàI.…ÑÍ,—7,!„ýÅ&cº0—±%²¬”©]Û‡"Ç‚[>ákr â}„œ18hD¼ãŒäü}¢Ò™wÞk%a§5Zä*¦÷‰´¸ª ‡ ã©À8,gÖšÁI'„(v¼è•²"OÃOþû÷suµ}÷SeQÒV,hO¼¨ nzlÝœS*Fojæä‘F¶©Èž†Œ”ov{ϲz.|»„RC˜–òN›¦D~Ö¸a»ú™åÍF61ÿ¸%îu`8 (—°ÿRÛ›ç·5c£·Æ hòaí¸>àCEª0FÓ8žîƒïòźomŸ¥îœym1 ?›Z6~ç,E]³¦öë©Ccz¸èÙ¦ñArüsZ–ò5Œâ­À! Ù¨ö²+;˜Å:»´«)Æ+TZ.m§*õŸš ¬à=]Y™7ŸºËÏ©åÛ²Ü2|ZPêö@#“T0n$Òa;§SŠâ…p|êFGs–D𠫇û:$¢b²ˆLÀôÆt¹$V±¾ ýùh»}y&Àïë{^\yµ Ûƒìu]¬wC”tÙ¥«:[¡Å|åw›žP‚Øš¾!c³²ÄXBm|%|ÌV¬n~up+45Ý6ñ‚éIxZ1èò$Qí¬<‰–ÓðeÓ¶‘/|?]!UË\ŸÆ€f(êq`rëš¼©˜(™M™`}žìblü2H{Ä<”M÷xÀúŠJàó&’RµÈ[ŸÆö”‘U¥ÄÄHk^‚—Ë_ÎO1Ç 8ÈÊyò†¼p2òíbõ Šyw†Môé†ùG_ݯ}jÞAý°0¹ Ùsø$>á8©rß¿FrFŽ{¶&Ñ«ìà MF"Éë.W^éÌ&%âá´k]><˜­]‰>5?mê$~¢þ>Ð’o‹Ü‡èG^K`ÝËGíø,ÉïˬuGk±î"ß³VËtÞ|+;Õü…ýÔŠ¨4ÔUdüêŠ6Ç>µá&+ïhráØÍ„fÛT]cVÕ|È ‹»m0æÃìÚÜlpê“Qž=VgÁ2ðø‚·ñJS¼ï!1¹±É»ÝÀ@)/HŒ_7CÿŠ®z­J(Oˆ¿®K¨T¼zõÁÜ9¦UÞ@5h†¯öô8ßû~FýÔK³´=¼Hÿ}ÔÎ|60U?ÓqBjü\t܇*[«c+AÇ—gïÛÞhøºªÛ(¹ôÇ¡º`Û¼[ ŠnÍBf|O3ݳHé{?C>ÄÂW·•)F…¬jï_Ü-ÅRùº7¬sàW~4œ½¦d)‚w¬Ø,ßÚ«¦±Ö•ÊMqxœ=¿|jtžäû¯*–G -MÍ÷+!ÄÄ":î#ÛDqöpÑf3„â˜QÖ,ý‰Ói}„²‹òÓƒ·>ÿVêÓ#/ÈU4/ôGѼuR5å“kŸ¹¯óü†Ÿë8è8`|—k÷$²ih)Ä>hÇšÄë œ6îéÂp}ð¼™g•äÛ®žh¿z?Ä‚Û? :KÓÊ~·6`ÓŒŽ[Œþ¥ *¯3ô¹o —ê¤ùá…8Aíì{Õ· )øÒ=84”§1‰%—x¯-Œ]½®Âï u ö4§²:‰ØbV+š¯e)<NØû0Üâº:”±Œ .‹¡zÅ–ýpWƦúc÷­òIˆµ¡áü¾‹€*¡ëf×ë|6×Þ,2·ô§e~‡g“ÊeJÀÝ¡gSòUy¯ÎZl¿òÛèêƒ5É(^º(™´ž¬§)9‚ÛB*‡"m(éVI›S{ó÷´­è åð/Úo9ºA9ƒ­pú¾agíÑ·þÞû9™;zEÄãr\Éyœ‘Xõz‘užœx8åÆá—á0¾w“<÷¡‚êþrä)uƒ2"s”¸$Ò8SÄð}e˜í¾eˆUmRÏxh*¼#q8ìÇà ¦ûL6ãeçÇ©­jßEÊôës$¶­ѺG¢‘÷„îâ]§¡Ñ•Ÿj‡& &Fk/ÈaF_'KJÉ5ãÀbòóôµLS¿…+‰¥k+â¾öW«µoô‘Þ¿`y¶ÖûAÈÎ'™›TöG+KבwËk¶·…ÜYÜôã²/ü[ŠÑcnÛ¯Âsoí¯7ñ[*R7}íXÁ߃A÷G"y¬Žï(¦æÍÌ齄u}zOzÐ=ÃÞO:mOÈæ¿*W_)©•A+©åàO8˜sºÕ9*o¡T†§=„¹­’„oÑZbûü´ÃpxméŠÍê:ËÔ’qYZ&Oôò ÓTf! ª¨Ò!ù&êo±‹Õf àÕ§Ïz•‡¢ÐÎL ƒŒÇвQÇlÒçôZ*íæ9?~ê×—}µsÍ@?ôÇ¡)rv™¸2Oò0cU÷Šr‡¿_}[PÓFÕí%›d¶þøè…ŒØZ-6ßkÿ*2˜ÝŽ'çmOÐå7U/²qGV‹gŽwá’Ÿù©ä'ßXª{Ù0~âS?L¶è‘Îa£g[ztCÂÿœUïøðë®táWFËF=ÂlÜq‰äáDôÁ)…CLÝB)÷`Ÿ]>5h‚8å.>4Ì|¬P³kìg×E.Iüš_<:•ÎæCV¶âûRyž+هݡÒèÑ!à]Ì׽䈯þ"½¤ÍÞR©g‰áÜ OpðîZ9‰fs¸xØ“:PìÀ^”[Æafû,ç÷eľ‚rò íùÃÙ^EÉŸe+Ž¢èF±øÝ/D:®#hc¶È†ÎÑ|W?ï]O}+°l3?hmê•ì͓ĔPͲÒÑsö¬”×À4| 4öÓƒ«s4½‡†ÄÊ*¦o1åÕºÔx}QšâDÆw€5#6úP&-ʳ‡ ¶0YÆy<þó†ÕÇwœÛ® ˜vµlj‹¥P)ò·_³„¾¤¥²={‡Õ$p¾ž;G®Äe¬8B/Bd·.[ã¬û€ÆzÛïdéX4lžß^³×j…+Ñ¡Àèj'r¤$€½AÊ“Åó³æC¥s̈˜ÉIÃx±«H” –\«x/®ãë“6¶µÀV‰eøE_i@FªåÅ{˜•¦FòØx+öñº‚e ¿¤BNWšeAoÔº·M=ô¸À`aô$ÊKFOS¨£0>?CµÛ“ÜéAyýzGa"ÓêšÌ{ž¼Np1öá…RÂ’”¨ïƒzéDŽÂ•2ZíZtbèêOعð†KœÏxc‘&“MVÂ8^gÁ~—ã•‹Ö8Zr=ÆõËÀ˜»·$&kkR¨Žûç7oƒÐ×òhÊ~ÝhÀóÖ—’yKZŠújQñks¬ÞÆ šžµ¼î‹©õ¼¬tN6jbÉÔEì*¿ÅTplyà„½—Àοҽû~í9¡ÊP–nÿb];Ú­zªÖõä ‹ã6¨ôsÿg¢ÌÊ^ÓÆÍuÚût£'‰.ØÅÌQ$·s˜?ðZÍÕ 5ÆUušüb†´Éo÷Ôå>ŠÛ»²–x²ñrØP˜å^a~<'ŸsÒRTj¶ö3FõùâˆË\·@Ÿöó¢&­‚hÑZiVõØ_ï^h)@ ü=É•/í;è])‘#z¨ùmZòÓœ ‚¾_µG|4xM¿t¥/DÃç±8FŒnFÂb⪒C÷ÛØÙØ­:'ý ß9 Ï߈˜ë“4¬ZÊ’ÑÃ:J¶|•s”â ® o%o¢ß’SV´æ=Ô >sñNˆ¾òCÐf–±J c"Œ_×ç‡DÓVrzK­ê-4«J׫f×9÷Ò?ÑÕ'掿Þ]u^Äš~s’™ŠFÖê_åŠ+LÈ禳f2BgOé˜,H…ßGÎ*Í£1Z‰z ka3+ÚéŽqôë/Ï-½!-<õ&´RÝò5Ҟƨ)C²E’Â5%yõ ûry T΄‘-ÕlNxKåÒµmtr,=8®^LÀší¸ Û©ˆ±›«ôhC!944à¿(üb~.ÙDiÿ3· M|Ý”7pëÃ7ï,eÙÊØ×¥›í¿t*é‡,"Ý\8Š–Ô/‚3ù¥‰øQÇÅJs׃ªO@ìšææ™|ˆR/^y7³KÙZ£ùêè®àœ‘83A·Mi÷±Ï?ì9ü›2Iqcú~Œ‰6ʰî,3c££‚{×SäÝ'g(±&„¶³éöɼÊ3Á Ò¡€ê¨8Óze˜„k>ç&ŸÄ²t‹•Â7 J´lÇ1¦€Dœ`þZ)oPŸã U¾AWhNXÇk¢á!”72Û»üy«©ÃpFÃOó¯ƒL'Ua´ê±’ÖúèÑ·?E¡rWó‚4/üÖOò•=¯z²ýúâ¢[fÎcežÔu›–ž7dÌÞQ–Ê.®~E{_QriÞG¶Pø´RüAòú0ö¸õ[C󪜆´¹tÍ{pE¨C• ¾òoNk,ô½z夆HèmíAòt3õyíªïžt åÌòö=.VJCþ„½©ŠÓ¯¼¬ÂjU”r‘´‹¥ "ß±W˜‚ ¾…5‚âI9éÖÔTbx÷±ÎŒŠhW_k4:=kVa(î58&§èfáRФÖ»³Ôd¨å“ú4ï`ÒŽåû`o½Eâ1 …w$—uH’e¥¥àÓAääg]ºÏëôÁé§ßš®ïÝ¥ÏK98ŸQÙ¿³“©cl×—$"ïó=ø4J¼K§*)ÏV Ý`íìÖ|_k¸H6I褻Þ§ YÀ¶ËÄJ›•µÏZ©Úêä+ºò*°ÓWõûŠtWKW­41K,˶~•­«Š3mTgí*Gï/ÅíàO–£™a0ï@YŽv() µØeÎ×'³8\AÛ°}T(ì:î_˜÷Ô÷FŸ!¸:nóÖ$Åðö‹k®¶Pæ=¸š¬Hjû€Ä‡½ü$%ïY Œ¦ÊH·(£‹Ìë2ô×8¸©i¯UŠÂIÛçs¨óßs½ì©Àµ¡×3Ðrð« û“ÕÜŒË=`ST ¥ÇøÕo_±ú¨Súaëa·Âe­õø5ã[g’zo΃ä)û·¸Q¿ý:ô…¡ endstream endobj 384 0 obj << /Type /FontDescriptor /FontName /JHKAEK+CMMI10 /Flags 4 /FontBBox [-32 -250 1048 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 72 /XHeight 431 /CharSet (/A/L/O/P/S/T/a/c/comma/d/e/f/g/greater/i/j/k/l/less/m/mu/n/o/p/period/pi/r/s/theta/v/w) /FontFile 383 0 R >> endobj 385 0 obj << /Length1 775 /Length2 1633 /Length3 0 /Length 2172 /Filter /FlateDecode >> stream xÚ­’y<”kÇßÈÒ ûAÈ#Êl– Ç.²Œ-/’1ó cF³`²eË–ƒNÄK¶ì‘µRJH^'kd¤HBÄK‹ä >}Þοïçùçþ]×ï¾îïó»oUeG'-3Å´¢éZ(8ʰÀbmô©ªZPA¢-qtÐ@éëë¶ €Ö]m]4L° ø3©—7P·ÐØ2a3? áqd‹£{ƒ~Üx p¢à!΄f$pbk 8Ò@jH€ÃP(€áé€'è‘aˆ-$2‘`¾• ÿï­JãBêÛ˜’@!“˜$Âîi —åÿõóp+‰ä€óÛ¿Ô?Ú8?ˆÄüÛ@ñógÐA*€¥@*ùgëið$@ ¿Ÿ»6t ›‘½H  …Ò#u¾Õ!š!:Þ âH4p»’ ?“pãÛæ@81s9nvðÛÅn÷q™îÌôäó¶FýÐ܈¨PàŠ„#‘(®‘û}_ýé¬#d<…‘½´®€£RqL’; ­« £ˆLƒ0ˆ Œ€“)tî€L(@¤Pa[·ŠBêh«ö·Äß…þÛòŸhnN æ>-´.÷D´ Àè"CÿLJgP© ™¾ý†¸1}×Dˆ›,x{ˆ‚7Œö¹Öp©4ìHAoŸM!³8úÏæ˜ºLCøÜ+C‰gÔÆgpï²xŒ¸T.–α«Dˆºãr f!ÁNˆ˜añ)<–“À"~ÎÍçIîãG¢~ÙñÓl¥Iÿƃ‹#*«cÍ|UöïP˜|9NÂÎÒçC+a¾áOÇÙB•·é°³¹Möià©™±”®ÏóÖ” »è‰ŒkUò+[îÑË~ßQ‘LýÄ\,ïPÉ’«uUÚ¢zi;îÅhF[Èê ÇŠrïôÈ ÒFbÂežoÙú)´ð$´ŸNpبEjç˜%OÌí/ÒÙ»3ž©&·]y¹¤s(OPÀâz€`Z”ÏкV[‹‰@¸LÐÕ€Éh&Ùû¸@•Ç “êW<ÄA·÷I°!×ßÍ»¼É§¬Ïtâ›Åâ·f:ÔV:šÄ,›ú£X…^§­Çp¯‘Eî!EK²š&Ÿ]–WyЧš–;ËJ·©<'KÑv­HÏ'êÜï–P^ÛÉvSk–öâÿNãUßh–˜6½2czÅ6N´ðÄj¥EµÞ£=“±ÄÈâó­É%4Êôk’©4T>À }Y*_u{Ÿ[osÿ¤¡‡fvuz¢ »K=4~Ÿ±”.[¤RtCÖ%tÖ•ü¡0¾UÚé”rKu“Ëcㆠ ÖC€,Ðö±ß™89g.d<_“«ð6Ö˜-ªâ`™]x°gt‡iüJñŠ…K?Aeׇ£‚QwÇAU+2HrÑä$-ƒ÷le ±¯.OØü (órYìûel&áCªYÁ®ê‡Zcz6…{çmŒ„p/9\¼_Tr!SÖ}µÂ¨4ÌâDZn¤`}Í`³”Æë´èáz8:½²fßWåãŸ?¨ŒóL¹ÿšÔÍ·,·“ƒ2îÚôø±NîX»Vv ¸è#q“¡Týî€BûuþZYëÚ†´^ñêÂâ¥ý±ïΔå]ÜtŠ ¶×ߨÄdc0Ú™âmŒ™w®í ]Ù#Oï2³ã=´<ÉÄgj–6¹Žßº«Nsïls£\¢ø.~@¯¾T”·Ä,ðŸò=—¿›½Ó\¢"mrô|+jVñ‹z4ù©üþQ„–¼bXZJeöîÈö>aõþiY±Ýj-àà’-Ìa6~°)Üh×4o7gé ÌNiKõÁî¿’÷ÔD)‡Ç¿i©Òª†­Sí­–Òÿ f {žÅÁ#Ynî.á{[nŸL\xXÑgrëžcëçQ)e „ϩքхÆk)aìºø¦ÖŒ¦ìY†]_©¨ËÓ»ð«XôËÈ<Ó´Ó×ÛGº4»s„:{ÝZò|obxÁ%ÞƒÈy!ý;2r{º+±oGEm‡Ôo:w²ŒWE“n^uæÔü"šœBWz µylëIs¼<¶oy]‘?"q—Gã¹|Áè@´¼`ËÚói>$¾?kÎ]³p!ªš9~[J (o˜(ÌI–ˇßÛ[¥!R ‚RM$®J¾9¡ÿ„QISL¹$ú…÷®¡EÉÿ}&Þ±&äy§–›ûíi†¯¿éÈ¢kA)¨;¶¼Ö÷hþᕊ±‘©öÌ^x€qß$ÚËhrP¦Óyç'§§3úl”¿ð{û5Å…6~=Òý¥§ºýÒXÍ9y±'÷1Y”œuO\{kÝÅÞq»kE•\b>–öe†ëåÕ $ÍX&¼áCj’³®e_Œa<ÿÄÖä<Ì[ÝŠ—mfÕV¼XmkSSeˤؠ‹‘äƒÃóN€ùºB¤vkʾ eÇi[ÇúWM›Ú’½dh Êmël²ZE|¼e\¨*W¥¹&aãßÊŸå³ýÆ;Kg%£”KHrCö{F#ÒÕ2ëOv©$>gÝ<¤=½[e§,x;Gë"·K‰OMë¾[’½Òiÿ*¤) endstream endobj 386 0 obj << /Type /FontDescriptor /FontName /NEAZQA+CMMI6 /Flags 4 /FontBBox [11 -250 1241 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 85 /XHeight 431 /CharSet (/i/k/p) /FontFile 385 0 R >> endobj 387 0 obj << /Length1 789 /Length2 1770 /Length3 0 /Length 2318 /Filter /FlateDecode >> stream xÚ­’yÈæˆ¡€ÍK˜@ Ia1éa¤ÂQ,ñi ˜åÿõëpGîAb,Ž_ ê_m¢‡ýc`1Bx\ àYÍüÕºüΆ)ñk×…K¢Cdæ~: 1Fh£ïuˆã @ŠÄ%Ó*‰Î—ê “ò+‰8¾%”‡»ƒ›³þ÷‹]êy‘ &×7,Ð?ÍKóS‹#bC m€FcÄFñócµû—³˜dbî ±Æ‰Í&…ÁÑâQ†X,Ž & @ Œ2`²¸â-€8˜?*‹ _¼U    ÅÚ?Ò@ý—4PÁ?%Æ@…,ɰ­-KކXñù `‚Eÿñ?62͙ܥ?JÚM…Ä9ƒ $ÃzYd\LPzí‘â‡Â®)G#³(æ^clu&ÎàõÜʇì+¾àúʱÊAP©RÚ[·K( ûL½Öæ`[8Û¯Ü&¿QE™$¿&î‘hÏkmRdä{5 5rErP{ê)¿QªÜ} crVým¼dq_ï·Á‘÷Ÿ ÈfP´ÿJƒïΫwOýFŸ&u̾qb²„íªÄ¬©UóêMCÝÁå3ÍŽçáa}|³§+ Ïõj›ç”C•ÞO{¬n¦\dZÞ¸zI7Èô(ù"”ón`Ë’j%—7aËKÃ+ݲnñŒHزNÈò[ÈA´{Þ#ž‰K*ñ«lØ7JŒ‰»h}YóÀhÂ)x&]Óæ4yånge|sèñÐ…ó߄Ұñ<áC3ÜëË̵•HîÂùùáÙ¹Öw‡44 ù•J‡¦$Ì$jÀÓ§+?†¥Ãë†KùMcZ.H½ cy–)œTòòÒ–AÙ.~ÙÕžûij~ù½¸„ "a¾õ ó6Ê(`à›x¿bÔþ0;5òZ`AþÉŽH«ø"¹??¨óãq’jU…}¶2›Kã¦6XI .éb¢iP%óÚ§GÇ'rãÞoÒ¹ŸÐË&·Î.tߟ©KöÉ Æ–Ýpr6 Ý­|Î~Å䞊p„V$lv£ãHQî)ýŒØùšÓÝjýWfù~ƒÇ/Ú…ND}XFÈ €!'&D ½¹SÖÍšckê`m²JGuÕ‚i6>K›î«òPÓ“×)pPålK{€L|‘CJß¹}—¨(©6U@ût(Ni]ÝÁ~Ëô-߉õíû‰ñéí²ž%C°ˆže¤7¯7w-¨¯ZiÙi²µq%Gæþ‘îxãÊ0ûñÎÌ;ŰÇþѹµ·ßD–˜öµÕzmá\‹ þ0{}ae‹ü Àu@®äsrþ‚Üf¿;Mf„8¢YÛ£»[E.q{¥Wó{)z½WãsÐê³ÂĄ̂Ë-ÍI¢÷ÖïŸXÌiÞü¬¸j¨Ù_ QëûÑÅgp’ à®Ã “7Lð,\˜o‹y: •ô\ßN˜ê"´p Ÿt‚þQK5Û9°:äìLÞ†v­Œi«s9ú+^é’Îe¸µëî® Ö¼é…`3DŧücÖ¿Úà8íui¦ÒvÒxÀ4¼ }2Ïk¤MqûMÓpÔù¨(Ú½x»ùŒi{Ooµ¯ê7EÛ} òxóÊî»^ºñþïW´<^RàªÃÑnÚÖj Ï ÍÁ}ú U­^˜…ñ‘Kçd]Þ‘ëo}ËûÖß kmþ endstream endobj 388 0 obj << /Type /FontDescriptor /FontName /NLEKCH+CMMI7 /Flags 4 /FontBBox [0 -250 1171 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 81 /XHeight 431 /CharSet (/i/j/k/p) /FontFile 387 0 R >> endobj 389 0 obj << /Length1 897 /Length2 3191 /Length3 0 /Length 3793 /Filter /FlateDecode >> stream xÚ­’y<”ûÛÇ-‰ìФ,·-ÆØb({²æ0vÒŒ¹M“YcM8çRRdSdOÊ–¥Åà ‘)û¾GNH=S=çwzúýû¼î¾ïëúÜŸïç¾®[QÖÁIÍE@‚§ x’T Ììì¬ôÖQCƒWQÑŒ"HÞAaT__°ÆšZ€Æ ˜ŽLG›W0#„1è $@ÙLå›è`‚‰_°C.€8–‡/ 8|1 )\0ÁbÇooŽ`H Qê¼P(€Âø’$ˆÆày!ß"YáýÀ‰eTpÀ?­Ä (©°B¢xl8€ýx!öÖm +ËÿG¬_ÍOc±öÜ7ûoƒú¯6‡Á†ÿ¯€€ &DÀŽ€‰ø_¥®àlv ŒûµkEB`1¾&x4Ô ÚêÚ?꘠Ә0å€!ù^üØ ð{Ä£~MÂß÷[¸Íñ‹ýÞs@`ð$xxhü+þÎЙ5""& ðÔP×Ѐ²„¬çŸ“÷/wYà} (  hêè"ΫÁ²ÒÔÑ.A †`+0DO ±^Xƒ¹ øˆ¼ß¶ÊZ ñ­ôƒôˆïª¡@Ð?¡Áü„ºäâOÈròÿ YV¸ª@ð?¡& ø YÎAÿA](‹°ˆ  ß+ÿ=ZSSBØ%5Mm@MS‡õ­ß¼Oèh\þ?Bß`"Ä“¾ÿ½¬ýÃ~ÖNA0 ôåê'øÄ^̨‰+޲ ¾*áR ’Ì*Šíj&?Ê2P7n°ÿ ±¶J!‹\Ä” §¿·)‡ üt&%jL"Û/9Aȃ"í¢*ùÙNÜ+¾ï¼ÎqtPÚž ár†dfj¯ ËoL„4sUÚ.BOH¼¿º§x g-Ê?úåä_&J~.×›òÄ6 ty;q³s{Ù’@% Ýë÷ÊÞ8°;$ñ|Œá?*¥bÇÙÕÓ²2mE¹˜†ÍlÜpt`—’XÔÅ×숄 ó— ÷Ïô éß Š¾®‰Ó¾5j–üׄ9¹î‹X®`æÅ´°”±M'”jßÑâ‹ÝÃ&@Ø ¹ªWÑŒÔy\o™êqƒ¶]Z.’`ÙWqÞ¾yù‚`Uý|nþØg—Qf¼¹ÇþbÓgÖó‡zwˆ÷PJd4—Üç¸âÅV4~›VÞ„€ÂÆåYE&,Ûb¸çc{_µd•x‡^¾Rôº›±„^¯@¼mÜ“¶0¬§]‹#MKçbÕË ƒ~Oö.6¤ž¤ž·Ì˜"W72JÓD•ߪýfÏÓâRøf^wL_o>‘7É]gp,‘ÿlï‚©ë'&Õ‘·Ý³õA¨4{­ê ¶f÷奇á›DÈ[nƒ…wáklyÝsœç¼£Ë³Å*Ó\\Šz9Û™qðÖ~_)?Bus]¦{’[˜É ýåØbswdódžXß÷•‹~Jg³ìùAOÔb©ìSk‰ÑZKƇSË ;¡¼£úŸe÷[úô¬&ÎDå/ˆè´o}w¹®† <w5îiÔ©\ÎPÛ§ßué0H‚­ßrh@¦:€¦'³üe÷ÂêÙ‘ÑÍfpæ…š*ù£‡33õËÞ .7¡‡±'ozj÷›êe~([=\ z±š«€z—Üã Lww^MñãL]ðm eƒ_»È¥×6§–×Õÿñ~ÒøðÌ·. ¤3nEædçÈ´TòüÈk[f5¨\t€C@ Ô>¯SÓœxq‹¤}9-¨ºm‡ºiupóN²BÏdFRš¶ïÆ´š‚êKŽ }K1+¿¯âŽ×áÇNLös]Í›|eméë@ù¨iµôfhûZÅ= Ùúª¾pË;×g|˜ÇRDn˜1}$ýPwÅ\·­r²UzmˆçÍ”0ËW—¦¸9û¹Ï…h"ʈfŠ5{Rÿe´)âoÛ<1Œ£¡Ï…gE±†*Û´¿§¤szR^E‡$è÷óœH5yØìéî$ñFk¼üH-d`}OTcà~¯'×¾pÿÙ¾½;âÑÛê¤r¨†= ðûhZùØÿ¸½~|™„é™êd%› ;‚_CÙÑ麼{|Ô+Çß×Èéò>]Û¬5xOºÒ¤'Ô¸ßb[ðV•Ù ²<¹@ìnÖô¬äÞœË=òï>µÅŒ{»Jb’s´·Mô›€«+ªyb9JR}Ñ«êu6ÝAh4y9­9õHÇj» × ðq›AøéH먰tíÏl;ÚÛ%Žò˜³Ì-É9…{Ù»Ôj Ú_·ÃœKW>G½SX®Œ Àž‡'¶ýe³Ùh~¾oßQ&sHiíØ;Å‚ƒtwRãã,ÐæÞãK…n®4ƒm‘LfL¡FDsøƒcGŽÇZéÎIE‰O«\ðêŸá]NXåOÌ ó]윟pÆ ©ãÏ=úÊbü®\¥«YNo%ùæºnØ/Pï"6¨´<´*úM;_Î^iÍfôã*ì¯>i@Ù Õç®H’ÚâܺððY×µ .DDƒ¼?ãF:6qy"‚V¦ccäEû0`ÿD ѺCìžþÞNU„lpÜ^LºÌ:Úó/oo„ðð)‚]./6èýÅgƒ°& gD!eý@^äBÚÇPÜ«Á£F r Ï1Î ž4=oq~´_¾™‹¯w²6×>ŒÉÚ‰L(¾§¢DJŠ„<Ô=Œ´}qÝ¿lÁ`9á33bcR_ÝùçQzIþP—`üeRz"Ú˜ìœ>«ÒXÍg¹\ûÔ¦"sC9œä÷(ž.¹p¸Š”%¡^&R8—ùuÔab4!sWdÅÙ‘8B{¦<ëúµµ® ,aäÑx_¥Øi®cj¶3Z×òsÄZÄûUdÙ‰Ûü/yI øÁM!Ý6m!+¡û ™W8(RôyçOvª'ß䵨w.$íIïÝ×ÑFÄ*+§Ñù—ºrR OÛ•†)`† —h²]¢"eÉúeht3ƒ7áXúôsYúñ­•WM’È×VoEo÷Õû RŽeÁ‡ò²÷>¼×ô@Ç,Y ŠSðÒT®Sº(!ò7ÑöáÕô ¾_´õ4*^å®ÜZ¹ëêåoä÷–CäÜ=o{éL¾é1Ë.ýüªéƒùˆôö¯ïRFJ”\å벜Ç¡³7±rúÞãÖâ3ö±m*¥ƒ†QR*Ð-Ï6“ •ìãU1¼=3u#W®N… ^K;uÇk•2·Ïeç3ÎsË×l0±ÐÀE­û â ‡™Ók¦Î!‡¶•ìKÚY:“¤•܍оƒÊ IfÚhÂ7ÖËÐñtª@¢²:ÇI‹j¡aöÆïÄÆÀÚâœdwÅΑÉê?L†¹¸>²Èíßý£'óz(ö«Žc ™ä…|È#/½Á!ø ,\׹Ȥ@é Ö…Uš(üu4ÜÑ¡ž;[ÒGf”Ñz£9ë|w;¸VXä“lâ<îs’[À¨~£²!/n¤rrÚ.¤ú¡n]¶IÏ3›y«¢’µJIT…†K²ŸÚ’Ÿ¼]~Æ1˜¬ñâÌËKOtŽÍ¥ÆNn9ɧbóË {ýÎñ½&7%V!Mi3ÍËoìVãI\êƒ8‰Ø Ãøµ„Ó×IÅLéçÇæ×ÄKžZéÖº®½é“öaÎ0]¿—¾=™Y¼vz÷‡6¿8Õdü¦À»q‹ÍÊëýÎçæÐ5Þ‰Þ±Ùt9ê±ÔK×úúgOF®»ù1F¬n‹¢«’ ærƉ•cÛÍ«×n‰§x”ù2zÿâ/÷¬LIûÝ«íošù`ž1A’§;ÿÑ#´@ÄŒ,îã`x\¶t—SÄð¡,ó)O é •3ZêóáÑÇÝ SÂnN^do¤SK~£½»S#Å™ºCýsdtŸ}Í™SâÍxb˜õ†ý;ÿ—Z”U˜"hu¿&AN§–¢Bž€´XÞî,×á³íS²güðšÎ” ùò*f~•W¯±k¼çú›µ¤7ò)¥¼w³ölJÇÄÂMØW£G0)ƒEÆ\_Úó*Øh#®¿oæŸMÔߪ«2ØÀÈÔÍïO@½óIÖN¿0Å©·6€í=]Ô_‰$7]‹ºoPš"Vk}h¦…v`Óm).µÚñ2$®|ù¶l·T7ÑQ>‰º)—Dã‡eÖ¸é‚4ǬÐIU[Ù–‹ÙìwÒuº†snÙÆ1%ÓíÖ„Ê“ãï3Ä]ûÕæÀ¶ƒZª¾ÑÊ=]fŽd+¡÷Ì]%6ñú Èàø[ð±(}9ý»J–o0ZÅÓNúa*@Å j´–u5I|L'ÐÞã”üÀªÇTPYªe |úkÝ›¦LçýŠRl”²¸â‚µ˜ä+ù¹µ¦f'ÎÌ”^¥q­d8ÃýJyéÖfþ‡èã²]3Z¬ÝŠ)u)ÝÒ\è äpû±{)s¯ÌÁÎŽî<ZjbÈlóîS¹¼7quj¬ü¨mN¾èj›U“ÖŸõŠ/yFÇS?K«š8%7û”ž ó'KD¾§*ÃNr÷JÛŒÙIw˜Û—TÔ#‹Ø:+wM*K%RØ´BÇݺª*T8îú;ª¸šÝ¢'±ûÝšñV1RÇßt$[EmŸTŸ:œ7øzo˜C?Ï9>Ö•@SWŠˆò°¨S¤ñårº%] ¼:7`.Óô…4ÛjO ?n} mP ·¤(Ùgç÷Í7}t$ó¨ÐMÛûœEX@ƒ#pÍð¶%†z‘§ç3ã.'ìëûB}g™T먚ò;„×/üçŽlõÊ®ÙÊ¥ý÷*j endstream endobj 390 0 obj << /Type /FontDescriptor /FontName /KLZKTK+CMMI8 /Flags 4 /FontBBox [-24 -250 1110 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 78 /XHeight 431 /CharSet (/a/c/g/i/j/k/m/n/p/s/slash) /FontFile 389 0 R >> endobj 391 0 obj << /Length1 776 /Length2 1440 /Length3 0 /Length 1984 /Filter /FlateDecode >> stream xÚ­’{<”iÇScÖÊê^VL˜S¦iÆ+Æq&Ö(!Õ˜yð0žaÌ0&ô&RêµlEN‹R‘”BjŠ’T:Hj ‘r/KI*vhûôyí¿ïçùçþ]×ï¾îïý»£Ÿ<˜æt/rä!s"ŽHv †3È–ÚÈÈŽ±0±g  R©€‹ HëB#¯§‘Éh#`Ç áÃAÁ`b‡7Q= âÃl,A0&›Áfq“dž!A й\à9¿#xB‘? âàÐD"àÀl€‚`ŸGrFy€òµÌ†kEAüH0YÀÄ$‡‡pc Dã7ód§A2–ÿÖâáŽB.w3+l~ü|Pÿh³Â`nÌß^X¸PñƒÇøÈb«7ô•q`aØâ®³€Å…Ùt$ˆ s¢Ž`ñµG:Â"ˆã ØÁ Å„êÂYL"‹o¿Åžééåeúõaz,xÅ„C€ðݼ ‰ßµ,">,~@”eß·•ÿ¢³6#A€DÞX|>+M"‘É`7ÀH$Æãž@¶È‚‰<>zþU‰À/Ôþ–žû]-ž¿ ÿyC[[žh·9‰ ÌIdÙ‘ PÈ„¸ÿ1²…|>„~"YNßt ,‹‚D-íà±-C2/í/‰w8ùð¬6R/ëLâ=IRU–%n¸ÛR½_óÀ Z=I„À¥j£®exN ¹Gç=¶y7Ÿô Óüã la6SiŒ=¼}ÇÒÛù·—‡å®y^c:öÜðýË(‰Â·!"å„ÎhŠ|ÉÓŽ/“ñ¡{Z{¤?ç¾É@ûç×»ƒ¶¾L»;3âÄ;É“žº­½=û½Æ¬T§±«-´óÇ,ê¡|Ʋ'YVŸ• XQ`o+š˜ºFºíª “†ÒÛÐñþÎÎQ÷얲׫å?~öØÖòÎ÷w‰”¼ÿãƒÞ‘À!Æ1(³U¤ïW»­÷úH÷1Ã{º³š ]isŽŠåê,oKrÖ©Â%Á[l©µ‡—\È¿:žÛô¸BÙ Sà0{¸aH)Ú¤²Á Íõí¹iCw»ÜñcˆvX$:9çµÿÃö,dügñ°^A)jŽÂÈsªä󚑞x|ßA7:ýFx^Š~רÕD]ÛÒ†ÍÊó7,Tã¼ûVîUÕ ­ŠŽ5tQR«{{G¯óz]~°Ä*6²éS”š¼fí·ƒŠÐ]Ê~UÇI#6q¥Õ÷|¤ÏuFv^;i+ÖªKè”ùœº§ö+¶Ú'¨~ZñæÔý¦jýW‰3uE mýÑöµñr?Ï©î+‘ ¿î'Z£âÕõQ'ôÑÊÀ!á¿ö3mzŠÝ¸ÿNNjJ¥“+ŸM+K¸}–?œçû®  \+Ê7K7º<%Ÿ ê¡½k»ªŽêÞµC½'_u61À©Üê }ĺ£3ô㔋}ɤ¡bÒ¢Aùíœaë(.¥[3š„C{±ºâ[Ý[ÕêLMî-—ZÇÄ.­ Ÿ~N£öøtÝð}?:Ä›ôuV¢¸¡úJsŒß‘¼=}ý¡ç„õ÷o¹4/q3iV©pÇ3¯¾(ÿ’gš l¿yãvÄøz·ð¬ÿc¹¹V† =ÚØæ“ÿø¡TÉl‚Wȗؽû Ÿ Éú4Åe¥•æëH£LÂ}1ÆÓzW°ÍBõŠIq ô*äWžI£ØÞ4xé; öDÉ~`CmðR²?Õ?[àåti|ösüÓîZ–2wS«ÙÏ´<ƒk׬CâÓÿCšq²;v´o0Ž;à5?6QÕý߉&?­¯bZ×k+nß»C¡k8½%¸ïÞìIsiq³–˜•C×\ް7Kg½x›©æ)޶V ©¾ÛRÑvw.vã¤é³GL²Mñ§ÂúJô«5…ØžÓy[ï®Õ}pñJœÖCœ&Õ,ÝNî Vû¨ú‘ë&&ñËíñi¿#ä ÑÚB ÉéÐ?A[ougFÇÑ]®Ê›½Z_m¹nwôC‘3iÙEL¨Îtâ… ‹¥ÀIŸ±ü¡Ï‡½÷‹û¼­)J8·É¬&;}s|«kŽŸBMc=3ik'kúõêËòÌíU7ÎÚ–óOósä§wÇÄÃWUöië©÷2Ì Ü ‘R.×q×u?{È|ðL6R@<™1Å÷ÓŸÍúµ¶÷:ëq%eÚøÅ©@Wœ;“ûWŽ»Y¤üV Ì Â*Ö=w]Mˆ(ªÏ{6SÃ#‡ÿÊnÔjˆ"=˜ž‰õ 4R»™">½dm´Õò7™Çn™R§œÏqå%Ó(K‹U‘±ÉVÞçVulÿðtÓ#‰•¾b ¶x½Ä*;)Ú ~ës²‘8yg̼q£àÈ+-½¹Ïý>SW^&¤:'&Nšv=93TFødŠjÊ®cT=å 7nzmdÅ9ï<¡tHƒqaõ{º ß{º½öxêŒÆŸÅmÂJ¼1öV“~äHñx­ØÓs„¦–¯ônKβOEÎÚwÃSšôÅd«f3 ½Ûy Õ~B³»ÿ]>®Eò‰ðþ銕JœµžÝEAÞ°z+(û çÀé endstream endobj 392 0 obj << /Type /FontDescriptor /FontName /UDSRTT+CMMI9 /Flags 4 /FontBBox [-29 -250 1075 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 74 /XHeight 431 /CharSet (/f/l/r) /FontFile 391 0 R >> endobj 393 0 obj << /Length1 2096 /Length2 15344 /Length3 0 /Length 16471 /Filter /FlateDecode >> stream xÚ­·stäß²ÿMlk’Ž&¶'Û¶mÛÉįĶmÛ¶m?ùžsï™9÷÷ﳺW¯~UÕ®zïÚµû³š„@Fžš×ÀZÏPÈÚÊšž†þ;€_RŽž@OCGÇMBÂog¨ë`jm% ë`ø@ÏÎÎ2ÔûüòùþÎÌô™šÀomãjgjlâ ã'ÿ'ˆÀkihgª¯kÔu01´üÌ¡¯k·Ö75tp¥ðZXäþYa3´7´s24 ¦§˜ê;ô M­ iÿ‘$jed `ý·ÙÀÑæ]N†vöŸ¢dÿ’Iøi`meá 004‚¦•²þ¬fø©åÿYÿ7¹£……”®å?éÿiÔÿãÖµ4µpýŸkKGC;€¤µ¡Õÿ U6ü·6ICSGËÿëuе0Õçµ2¶0ÐýÛdj/dêbh cê o0Òµ°7ü—ÝÐÊàÿŠøìÜ¿$ÐÊʉ«(HPþûLÿå“Ñ5µrPpµùOÖ‚ÿÅôø³;v¦.uºÏöÒ~¾þ÷›æÿ©%h¥om`je ``fèÚÙéºBNÏ'1Üé¦V†.C—OÁ´4VÖŸKŸ=ñYÛAÿs ,ÌZÞLÿ&-ßbÐòÿ!6­ÀbÐ þ‡Xé´Bˆ@+ü‡´"ˆ@+ú‡˜´bèS‹øúÔ"ñ‡>µHþ¡O-RèS‹ôè³Ñ´Ò–†Æºÿ±°}ZdþЧ¹?ô©Gþ}êQøCŸzÿЧ¥?ô©Gù}êQù±ÖSûCŸ‘´|^\Z]ýÏ©üãÿ\«÷‡>{§g§«onè`ahäðÇÎøû¿¯ÔŸ õÿCÌŸÉô­->¯æÿZ˜˜þ±XZþ%îSŸÁdøh`jhghojÿWЧ?"?g‹Öð¿êÒ3|*ú}]{“?CñÏ[ÇÏý'Íg·þàg€Ñ_Èôšþ•ó“ÿ 3ã?èôGý?†?É™ÿ ·v´û«Úg€ñ_ø™ÿ:¦Ïã4qµ11´ú+âÓöW}ºÏmšý…Ÿ1ÿ ?›û÷Ö>;où×Ö>»ú'3óçR+S«¿´ÿ³wë?b>[ÿ—ûs36ÜŸÉltí ­þk˜èÿÇúß#ÀøYËÆÐNßÐê¯P–ÙL­ÿ4Ógsl,ÿ:ãúiûg’>Ø:Z;èYüWYF¦?ŽÿS™ý<ÿm¦ÿç`ÿ:úÏ.ÿ)Ëü¹ÈÞÐÒô¿ç”ùŸC§¿‡ù3‰ýç¯àônÓÞâïy£§ÿÜ埲̟íq0±3ükl?ÛèàlýׂÏŽáç‰:ý…ŸÊœÿšÆÏÕ.ágz׿ð³]nÄ}fr3´ûw©ÿ÷iÀÇgíâNÍð©ðóƒîŸ+È`ga÷ü¯H}G»Ï³uø×Ãöófý/™~>‚ ] õ¡—¬õ9üÍ‹½s§J¾Pó7FKÕvÍ¶Ãø-Å[ŒˆÛRlÔ¨<—¦ À|9Àw~űmóºôµNš?pÒ9HqkÆQ¹I‘tÝ·!|ô;Ehm˜»;c–žÙ-ŽSÍëO¿8Í–!P8„X#êVwªëNõgaUJ±P ¨ùÆH ‡˜d[Ì´îŒ ¶>éçeÙD¹zmÝæˆüœ ™õë]ã- I¿ ŸQn‡Uîû¬(/¡ú£o¨’'`,o8ø²)7"ê—X™î†-¨=}…1!,étIÕܦBðF¢¹Q°§ÍH©m’'¶”Ow›k]Aúéö~HøÍžÓ&þË›xËçÒá—Cv„’¹Ú¸f3" µþ”Ô)T7zø»4El^/hBî¸wGžM^Ï{hÓ]Äg3>òW/i=„¾2Çõxâ}rv´x‘<×Û|‘®4•[j2³árH’¯3’ $ÈëHÖ`ú‡m YÏÏ…ýïKXÕ%kÅr«­úõ­ùùàú½çNZRð8,Qk6Zˆ@– Wq^HÓô8‚*Új¹ÍÍwTuæ1Ae ²B]`4ò¹ÝÎÙÆ¿ø³ÕÓ§Ïú…i´ÓPåæUQJñÛ‘å;®+Âë-Ò§ëÀ/¤EŽ’‚ª[ÏNaôá,äíú ÁFxÛ¬m¢…{(Ði'¦ÞmÓ§Ú(= ½ UÚ:YŽ\'i‰§*ô„2“LªY~Y±ôÍ9`«ã/HŸI6²ÅC7§Ñd>2ùâ íÞâ~Ú f¨æ§µ¿[)VOR 8I%Ž Ñ) ³ÍZƒŠÛ]pظ}˜ãTî·ŽÔ~&¤ÒOÂ:žÀS&Ã%¤Ù1Wþ𛢅ÐÒ0|éé5,Õ{›;…z-Ð¥ Kæ‡$K'Yû¶Éû£n¥™4Æë`fGlW[p,u}õ K8Žpf_«J ¸!¾0àqÅÉÆH:¿ßbå0iQku=ÿ÷ŠNøKìÔò‚ŽmVéâдÓq\UGÛïCU&¡.ˆØ…úY GòHÊJo:(ÝðaG½Žu® jȯwAí¸nb?”ó¾°éÿ€™ç¯F—¬νé%—÷¡1«›ÅÿJzwS¥¦ØDÍ}<áiy±‘’KþPÐØþÜ/âõ¦Øfš ª°B$è+l.&Š0îÝz²X„|ÿ5K'g"ìêô& æãÅ`±ÊÓÛfo‰Åæ–WyÿÁäzÓ¨´9vx34Ý£wOl²ù¹š0†Bvjk6_‚Ä{ß¿xMå#oFÍûªÝ™Yy¿ÿê@7åÄ@xœråé\‰}§ …#U„¯ùDUÐjÐΡ†ß¨•¤ºwTëŠ9äºÎ,DÔ˜Z>ºí&qÀ YyÓÑ—ß¾öÜÀ·„<±ïÁsÄ­½NÇÌ®Nl«'¢›&äp@AÃÍ<Št°þúº^òÀy^ÑïˆÚ ,¬C©¡3j#Òµžm0ƒGÑÐ6ƒÎTÉ|Ø#ÚBBÐ9½¢Eåð̹’Í-W»ÕBžrž3èÏð|¼Úá¦s˜ÞÝý^­ÙyXè¢ÎðR½â´ü‹˜‰f]œ+¢¥L]sXh:lg«ƒUÔ%}§v¥b\ñéúÃâ'Y´§æŽ"==èP릵D`a~%ë©]i HÉ ÆÞ±ˆ y£ípAÞ4¤–r¸ %ÿÓÈxpòÃæD¾Á¾÷}zEØ” ÊZ9Ù;Jõu§bØM×%ãý=2›f)¼}!âs.õÁo­8ËøÞÉ[òQνê£Dêaµ"Xÿ_©¹é5{_=hŽ4d‚¨7èÀXå©OûòM©ä»àÝ#ÇK^ßã(îÛIUÌzR E‰Ç-§+tä/›v@f‘?ü—Ð5½å•§A'LÞȒ䥖.>è2”àøÉ90¹ùðƒÔÊìÝG ™çŽ×Ò;9 ËäÃmÒ€í¨ÎHO×dăڀÁÛïàóµK1ˆBvåf¿µ^Jê„nãBX@%õ]åS^Ø€»º¹Úë_xk8\ˆÚvÓ_hÁCÿ4o»r$VWF¸eà~$$Œã…â¼€àxãþ8DMmŠÜØÔHªÇNë¨9@ ßË Ûd‡EƵ”Í{åy*e¥Ç¤ï¼Â^ƒ‰/þHþµ‘àÑ7 Ý é"¸¸_ú|i¿24ˤ»ï•­vµG õÀ‹ò0Pr]LÚRú 5¯êå]äqEó‘ü$LâȾ@$K|´³Ê‰­ÿâ¶J›Þ/Ž*¥š±XÇ&Q˜'¼Ýš °`+8ŠP)ŠÂ1C·' 7Qà™Ö¹.F–Ü ÿªŸñp¤´¸9ÞrÒªŒÇÝ 5à;¦‰š{ —¿Í•·X†®šükƒr*¤ô)µ•G=@‘Ôy¾Êãg¸êp Ù&K¡¬—{“¢þ‘Ú}9?ó¦;yz*’O¬+صUX­3ÊG%¶ŒËº:³WÏ5úI]Ñ©)ZY+§;vÁI¢.ÂÀ¢ƒÁU}noËû.(\v¹z"ù§Å9ßëÝâx™÷Û¥<ÃW hTÙRG‰¾æðÔÍë<@bJþir ¬/Ð~ã`4côfrV†Œ|FÕ-±^ÔR% þ†Å1V÷ÒåŠáد@ü žVæx&˜~Èsóœ&¸¯œvÝÁA‚IÃ`µŽ•*…ens¸À9¾tkfs꾬»‰èaÐ\•¡B/ÿÕæŠL:jAë0š{Òž¬gSdÙRë ±ü¹Éïh´§å‹š`Ë~£E8yYÄ(‹7µM-LMK]ÅKÔÄ$ètÌe–mÀD™<µh‚Û´«•}<+U¤EZÐrTj“ÿ†–¯“’nX„u@gƒ"QݬÀâbUtŠjMuš\3ѳh§Æ2 ‡jqr»‡e¡Çhçvæ”%ó“['ë/êÅ® Cï&wôR¶þ¹}BÃëiVb :Fß}%͆"F~GeóòÞéט:œ×›ƒ5¨ÐèèŽí²¸8¦Ê膗e¨#_Š¢0N}f‹Yx ¿L;)þXÎÇ Fÿ–G½H'¿G®…È÷¯`lÇ2ClÙ° Xb¥Ë"Ø¢‹b"ûIçƒÙUßËSÊÈÓ,éa„L –4<óÐTdåÎÌÃqÔeË={tÔážÙ‘”]ù*sqV,ñ±XÛx¸¡P®ªS—ÀÕË#ΆÛXÍÛµë=–õŒ…Ù4Ûu×¶!m} ÆéÕ;’÷ä"H1RóèñÚ¯K¦Šþ ˜Öò½.gâi‡IÌŽoÍø‘¼l‡"ûˆYµa_´œ2ÌTð 0À!Îü¦ã,$æ"«×‹;©Èo¨ð¶˜IÅÛr6Z•µöè"¨ÃóθTXb`™ÚÄûsk- cæêŽ‹h lLwÁ¹7ÛaOìèc×î¢G#íöû@ôâ)æ¥`‡˜ID[A–ñ¨]êp¨hÔ È–ÝHr€JW<#×GÂÃë_›kj^"”¦­­ÏœÖwL–¯Xˆ&‚Ûvƒ^“º?ý E’$(Ë;ÒÇš¸›Œc~ó ÝA,‰j‡ñßY uW%µœñg×÷Î/ÖhIÈ ïú‡ÁOôëgIÁ/´‘f˜TÍ ùý7ì Ñb“ý2Ù>ÂÓ4jà‰ ¯ÝÛbú?Ö5ÕÁ§J5œ›|4®ï¦ÆKÀ‘Wú–8°"¦\mˆÖÓH6LœóÉÚ²›“ Ýo6ͤ+Ø Û &»·’‘oGÌößY–ñvÝ{Ÿ-  $xI³{d~«²<ÌNAœ°·`aõgU²¿»NëýsqæŒèËy¿L+èPD •¾§Œ¬ù[7à»|-y êocª fm‡š|1!ú«Ò6ËPûèº:B…þ) rJ5Mx“°f¨=Ÿ!јBé“ðƒÇ„k6žá°Ç«©ùU˜¡sœJZÌ9Ƭö©˜†úlƒØæótÜ®ka2¡_s3JF¡žÔßt#BЀ^Ò%‰éCž‡Å(ÔÒê¼áY8Vä%ã †(¦÷¿Ç5®—À-¢BÄ/Di“LTo¶”²·h„²œûCjª+j‘–Y5,€ÉH™ýíí¾V‚){ÿìš¤Ñ ²Â'~M“EÝ3‰k °ÔùþÍ ¿¾’ó£uñ{ƒÿþ!’u‰ØC 3„u¿»U섓‘?ð4~nÝq€FbÏx^ÑÐYĬbÝL^šÌhO"RÞL¦Œh]CSó´Ø#CŽ>0&´êås&OR/•ùiÙê­u²A;B/ú…}€3bB+)‡Î•µ €;mö²dÞBÐO£ñ^ÿ!i— óœì› ->>*HŠ!a)í "”ÑÒÔþìäY©Ë&ìŠùè 0 ƒ6ÈÇÌJ”’ÿ4 ì½»árÆ|f´­^­»­´p±èúYÊ{˜û^÷Èa€‘X¦(§»ƒ®îH/[µŽá ŽSð`Sö' ínâ*€J«­ÛéÒ­«m‚çx±pùÈò ù9SÆq‘KѸú­.¬ÏoâŠð 0“Q”8,'¼à•›Ú:"ØÜTY’…Âó¾ŒaX)®9kÖA;"F‚óÕn¢Ämzùx§$wêiÇ`çÞŸüßrã‹6KO\¡_Ê=[‡/*;Ô'ïg¸ÀU ~¿ùX$^Ëúðr{Øä÷G«ñ@yäSe“Œ[åhy¯¢<1÷ßW-:£â…§¯mFƒ·½´ñ”ŸVIæ#¤äᨩÜÁ¸€£ãN¸îáàÄîzyBôq°î!€_‰ñUPC­Eú™ãòb•röîΖ‹EM-'ßV¾q€ì}­›¼K?á~p͆Üõì}Ã_?…¨- ±ðeÉ3¿¢âˆ¹×“‹ÔûR’ÕÔHD3á}Ù&$Ùž#t:}ùìtSÜÃ< Ž‹ÝV÷©~­IQâe” ÓUy.-Û1«X¼þ£ŸØ‚h½¢Mä^}¨êm¤†#Žv€îáÙiÙ%"7PƃœÜË êèqT”Œ¤ Ö’5HSãÎTGÚбˆþfÖŠÉ%XY^ LõK ³c"8s¶p‹Å´Yy®ô,‚— z]¨8ê" ¶fÙ÷ò‰Æ‡' )ì}h «ßü ÷ú>Ö|ò…`ä¿SeGi2Ž€E6v0Äp]Ùcáï@ý4RpòE"53o´å{:¾CjF*0ØÚŸ‡ºO[CZá+) »”¶I‚¼U(]™*l¿¢¡oÖš;pÚSJM0¡Ð' ‡šÏ?ý”No‹J‹%-Áºëª;QIÜ}¼ACSúv¸Ô¶ìö TvïSS†oþLSŠë i+QÉY‡Š–©)hùÃ)9ÂôŒJ¼ÿºqŽÏóógÀlxP(üý¢H—‘POÙŸ$î›jÈüRw –îYÍ—ðZðLh/ÂÖ¦þˆìò!ü´Ò6UM¼´nÂvÍõ´a/×0o«gÏéÉæà_éÇ5¿Iõ.Ä ÖöëòLucŠ9*ËÊl öq“n“éTÂ`@[açÌHyuI þxâЮÐr««4„‚!Lȸ‚äÔã?íZ®†æ˜V㼬ù‚ëvð’bމùÅ¿­ð´ ÛµŠÁ„¸/îŒyïNòðÒÝòSt%£y‚ßÈé2CX±µS‹%mcé¡§h|•9<;ðVÁîôJ“¡½ˆ¾üü`iu,‹rmp­+\øQo@ÇÄ(o&×ÞD¡EQΙ½ŒàvðÌé Æø’׈¥gêŽØDs:1ç 'Ûú*ƾn5K˜Š´UlòàÐXéJyä|™¿H5úÑÑ4N1Ý?)Iò$ˆ”Ub3‹ö„åG } J¾*€Î'wÅo·ceû+*95 :ù^“±ÍöURZÞ&Œ23Ò¸}ëdº©ÁK*KÐè©vöa–u^¬ãåU3.ý‰#ƒ\¢ÿ¯©ßG\TŽ~m½jøÜµúó¶‘ÊvæOÇËHR¾Ì’ôÑVõ¢E ßÀ&Œ†/MBÃz;_¬î•“BhBK9§ÙèZÜÈÚ¼}È[yéb†x£;ÕJzFâÒ'´x7IîÙ§.š§YyL`÷ŒHlt*wmÅEF¤UÎðÇN‡í)š±çQ>g]0 ç®>§QIå‰Í<Þ¥çÅi6Û,v¿h4†@ûmšFùŒvmÐ [›«Õ p°CXQ¸J=v0#µ4˜N¦‰ô¬À—ƒTò O$e¸)ðlJcÛ©ÿ”w÷êŒõC;_:] 2q·îOiD©M‰O²ún]•}y mtìÖåðÍ™nŸ‰í—{¬þ”O uÛÅéƒ@¯Ã¯S l+ócèN8RÇPºGéøÓìzð>Ô.Ü¥ãP‰˜_µÌ²“˜-þ²­”±£cœ0K;ŽJÓNü¸Ž²sÔDMÍüjÔ‡$S‹g Gç Lî6ð!ÅS¶|{³Ô©ƒìí;í’%«˜zû!WBÞtÀ—·æ{ߡЛ¤4ð™r–M6ÝjPT>@ƒjGéâÜ5OŽjmŸª/ôH{T¥M{ Ö,oQ 7H®íW~Ay¯¡g{ï>!‡ Ü,i6fVMáuÕµ­of®AE€ oèOuxa¹mPÃt7*°ßðaë|"Å…}GTÔO#±‡R$O^pŸJ¦X…=Öf\“ÜúvDŒnÈ«X¿#[o¨1y,N‡›‰å›ºíí[²m“+PZDS6¢¤”Qožš„˜·îª¥WWßoþ€M§¹ºK¬Æ%ŸÖpq!Ói N*þ;»z Âi˜êm£.µ³Ä¬"¡=ÖÓÕÌi¯Ž)’%@W—ÊœÌèãÂjwøÖ ¬Ùãüù0ñ°«df~eÆæ¸ôa·Æqw²wäk0u˜'¾öR¿³>dŸû ,“™ÏH'\Ióä¡2´S½dö $3‰EµïQ±F a*-¤’ /2ýýW2ÝeV¨¡VwªTbÐ@»Í.q÷œÖç…„séFFç GYë_zÄŸ†vÀ0M—24UBu)c_,…X¯¸F_¶oõ)†¿ÝÏ‘[u oª<½ŒÏoYñÑàWV®µSa@@€-úµO‰‚ãÞ°õª3›úÐR7¤ÙîmmÅ7ܲçºçˆ¥âá”í‘kß¼¯­oö]ØèÂD\–&¬ùuÂÅüºoɵ˜f¼kŠ åø6ižºÿØ‚ÅHW’9üãýìTrASR¡íWBœÀöMs×îìoB“híøIéJŸÄòw cŠq®yÞĆ%WÜ$]”¡Ÿ B_ØÅ„î b âÞ]¡t«…ÙH¸°{æOÄ¿P1ÙéÎ÷ÖµŸ4sG𑨃pp(^YÕ`ÙÌ{D;޻·,‰ÂÄ—ÒúÜáwËùÅÐôc"¨¢ °LeÖÁ™ç+ã›a}; ]¹¼û1ŠÏN‰æj5Çj™Gg±-BÖ'ËÂ?Ew4y’ûžâñõ•+©5ÚÊ-°r4Æë!zcüçn+z2¦q| CRxªUET É!An#™Ôô®]êµ;ˆƒ›°©ü\H…šÂ0Fr•j=£i„ç‚a”ÏÙoØ|÷ˆfmDLôRcí—üÐÀwîõ˜tXÙO$÷ñ9uö ûè [Öj±k®EjôŒ7h7´Œ6ÍŽyö®7îŠiLˆ3Ñ §jÏáêi ô,^¿BwÔ<„Ä­Ú=ÅÀíãOš8Êò ìåvlW¸e¿ÌÐK;†&Åê¥NÄí½0Ã_d‰>vˆZ&T‡•àù^Ñ˺Q—¥Ç… ¸DÈ!ÖûË]4@ÍÕ§œ(«¿Š¶ÌèˆPi·#{ÜÑñ*qØ"°ÈèØ"‡A83íï#ŸÖøi¬,G# á4죷¡ºñpÕ`ùÀ¢K¼Ö‘vd¯Ð·oëVßz(ps_›}è´›‰ÃºÙBÍQ%núì¢+Ñ¢=€S[#2ă³Uõcïxâþãcwmç¬?Iyí«œQµE8ËC2xIõ}aÝ݃ý\6ÿ—뾉p›·B¹oiÓ©+c\™qØï1–;<°CÁæíÀ‚y~t½JÊY«»Iæ$$?Çï?×O¡é XÀn›TŒ¿š¾Å4ýü5xÔeã¢c9Ö`ì½Û6×IëÏ<^%ZJjg{ ¯ŸÃ(år1—jýœ.W{åÝp¦íc•Ì+ÄŠKÔÑ—L;–æð Na…YlƇþè7[(yÎ4T~å„€šÌæ3æÖ)ªwj 2ÆûÞÖ)ãPVÒ–WËt ºE@)_»?­Ueœ<Õ±1LHI7=]ìfÁ¬1ÞVÀ-à*gÀoæFd(Ʊ†,ÅHÚw18ÀAÛö=c‚T*× ˆH6”‚ĺL ôæOÈlÄÞ”‰7JÑí˜ÌŽŠî~U4¼™æ^¯±àIé.LûÂmZê‡_ϼ±mK­ÂN‡ÊÉ<=ô«àæ ׺*›ÁÖÇaŠ3¶÷]Æ>¬z0Iw&ýÝHq܈€P3RP‹áu²ÓY5&{);œŸ w—ÕÀk/ÿ™yVÍw½ØåãÕÿ#|ùl U¸{(¶é) zÒ¦[Mï»êªé19izßvTì ÇWÂ,G è‹Ѩã˜Õ…ŽrÅR*§Á]RBey/œ}~[Àé3M‘˜š‘§Ï~:fOµê²pÈþ›¢Ó¼k 1º„óG¸uôv(€/²iP{G u6ÕÎáÀêñ u½u=ZoÇEv{šº>ÉÞ§ý"${Þ†Kt„½'F^tj˜§øðœ÷÷GB472ŠûÌ@ÉPºÜÃC)Í»ÂÌ3v'³Úï'Œ·^œ’‘ë_þZŽú&ƒI/mñž‡4ºˆ3i3¨W÷¿YL@öÙB†4oêQW™}Vµ/r@Q«Ä½™fݯEà Ïò‹L'ȬiÁ«»—Bß2ê×n¢‘=4Ä´©zÒ ?Fæaa8­êë:¯Øvì¥IÝßÐÏ*al/³oÏ÷ˆ¾ãÀ;·õ뚸‚æ´èׯغ_x‰7BÚï¡la/~oЉ:ˆbeŠƒý€nRüݦ™ñê«ý–ÕDZi‹‚ŸL>Æ{n¢L¹èêî­Øßü>Ñ ]ô*‚3><¶^ÔéÌ9úMÇþ*‚IˆÖLúóïxt`Df\õ¦6–ç*´7ûøÇÿúK(,Îkú Ö¯Û‰Dc¶ƒ-ЫöÊøü ÿªø]ÅWº…HîÁÕßœdµ[Ñ`¬œ¤»åJ&!Þ²8E.|·ø™ç +“/© Ô/ººpSÑ:¾²r–=¬ŠÝs=„ÃË0`ã«+g–å®(à½o ”-ñæÛãÐß½u ¼Ä(ˆP_ÌoŽHnøc™UêŸ÷§ÁvÓX9êUÔv½ëÝœ†OºOc>L¢<™Ë‡@• \ïÔ48Ë•¿Dáù̯÷4ÑNp'øˆ Ä+ü\F„›Cc-bg—­T w.p.e›£<{¿R*ÓúH#=ºÏ»:È5ld¡¤×ýº¥´¬SƒÈÊQ>—ÁŒ³zUèÞ1bk^µ^d¶Ã¶ÖñÎ3è2•ÉÏ+¤QQ|\¹~‰2àlšm œ.:¯$mv·CŠñYtܱÓÊ¡ç6§(|47þâ_Ù4[­ÏŸ>I²Û"¡_îr_pŠ? ~Çés*“GÄeÓø=y‰TIæ´œs!Û‡bêýæƒÕ\àc‚æp‡äç e÷½Ã`zŸHª©ƒ‰@Œ‰Ü*Åõ'nR­)QЃ†}æêU@ Œ¸Ð³˜IE2rO°«8ýžϬeàÃv8Ù:æ&ÒÂb¬jëAipj$3ËØGS²?ëUkKsIP‹æ<ù±Ù«âN‘ô+ª–RQ…Y¨éÏßá›OähCMf—‘ೈæºUeÒ€)ã”.7ž!ìï!J{H<Ó|úŽbÌOsÖô^汊NŬ3äŒ|g™­eìN¶!‡þû°¹¬&ZeU¼Ü åê×Á¯t¢‚i}?¯0Qc•¨¤¾|û¦Ômd–ˆùÍ\ŸðÔù\)gjÝ…ÐþëlŠæ ì5lò —“¬/5ד§…^«né—3tÌå«óXæÊ¼,ÜŠº_‚?[4m`TñÏÚ¢âýGîæóÀ|»É@á­g?v#„ƒ>þžOÞg¢ù\…z ‰7yr10vì;ùÚ¸ô§’]”-8ËìœÁ#µÂ¦ÂPpupÿ5„ïÈwÈš¨²öÖü¡CAÐð 0áë¬ÍÒÑZŠ!à]Ô·Ê @¥_Á×LMú-Ö僴º”ðÄ\T«ò‘ÍužìËIÿ¶Ÿn£Dr7ÍGiÜzãùå÷0h0ŽLOÒ+,;ú×÷”ì5&þÓþ×™£{'mh¹"Y²TÓmiƒìµ7O3›ûC„ ±$¤ï=h—#u Èp'<èªgïÚ–L𸢸|VéÑ×ø×õÛÈçtÃô©uä;Ç|'É“-!D1/ŒÊææž("ǧXÑLó$S]Úܳ]°·±/¼Y&t6dªe«ËJoçQYB¬|\gËt†„oŠÂñå|œT-’s¤›â]g¬ /šà5iXÇ|¾Çâ]Ì쉅ìõ/¤¶#}ZïñŒ~õæü·ŽìSV^fâ0(mÍ[ßÀ]“¿yžzL¦U:ëÞÑü W™múÌËÊ@¼8„€áÝdòLªŸÞ 2Ó‹ðÒY´Îbê˜ P\®ËÚEpµº >¦íüÆ«Á5ÞòÒíDXrôjÐ;ƒç 9:íjsNëþé`Ógˆ‡ jLR™äBæq $¦ˆÅ9`ˆ2¾Îô¿Ì4ÙZù:½Óƒ£Øý¬§*© ‡ ŧ僭*yxgkÁ˜ZáÌTÙ^„_{b4AÆî¾BÀø0ê©ÝgcÂ50½Ì÷·¿`~m[z3¯”Üj² V±äsôÙ'ÓɈøO”˜@2 ‘¹M)øÑë2Õ%GÁåCh§?Îv|‡.¨z¤¬RƒªDj—+Ô`¹Vw†yµÍ„Ï¥äA¨hö¾…Ï .“í}ár6ãcÄ‹j†ùèScTÅWqLSÞ+œ¦€öÆUcgb4Þö6šºç ÂÊ’¥7.e(àAêð7Ç` ÷š¤1LIí£“™)™û!ýf×+Bç1’bæºpAªÐëí|šƒŸN*ÝXB‰šÏ¶:$w·ZãÒ¿.Ä£ Ôä-ËÈY_lb$Žj¼Pp53UJæSоöU+©?à‹µÛ˜»“×+Xˆ¢d ¿É2’©6>‚÷Yôd'(×FoÜÓ ½½×˜+P÷k®krIÍvÆÒUV[øE耜ž…$l'IYÞüÖÂø£7á¾&òð?·ø”´‡ú)ìYëãõÌ×’ìbàóΊHZÉP$“˜¯—z•à8åX3eA6úŽ|…:bÃ*l½¯[®‡—‹»-î⻓à\°s©­|ME|¿Õ*¿ƒÓk6]ÉĈÓ&q°q÷Ý3¾™HƒÅδFYŠÎ´¬äÌ!Áf:OÊ—<[µÆ›yXb§6|g¶ÃÿŠÑ”qIùoŸ/»B8ˆä`¶{®à ˆìŽˆ‹/¯HÄqÄS„>éÄ2ÇØs‰Ñò­z=°ï첆=Èd·Üµ‹DËþÁ£¢pc2ãyPª˜Iw*8ta¿2YÜ °ŸVŽçPŒëhúÚöµpŽ»fÑ¥Aÿ;êp*Ë~¹MBöëe‘ºíÈ É}÷ `³vˆ—õéLÒ1“©|\¨4Bx¢„ÌètíâÁ97àÝñ¦4ï¤*AÍ›Xå>ÉÎ'ÇE­F±jW®\)ChºˆCNó¥XvAâeï)Âb]ß=òN:Ÿ²ÃÈn16Ø‹ñ´"€Cøyƒ‚¯.kµü'´GàjNlÒ+ãÏFnašB¹èK~qCE—äBýÛ_üÊžâ3H‘­P‚Ž¥3Z [TÄ_òP  Ù‡m8ã7Iá¨ëf\î»kø6³âs‰ºã‹´Î¬iBÑhwRbK]ž5\–±PX•Ð~È,\ ~ÓfDkŽ'1ÄB®p ÑŸ3õ:G%°N´=IU w‹½°¹ˆvêG€3fHFëfÂËz‚+}.}ð§"¯è‰Ýô‰|Èqh:ìùIR _¹†oh‚Mß8†ùL~VÕ€Øo0µ£y‡¥,ßxbV•{`”d™ŨoI üåžzLüòa úCëkQÈQ±,QmÆv€ZRØ¡è¨Õ›ð¢B‡\4¹}lIª{_AÍ„ QL}:ÑI8¹uÙzõƒ^¥û áèƒZ1"†o#K»¯¸&Ó^Çêàûf½`¢™ÑpI´™zy eßhwó?±‹Ù6mˆtiá@¸É3G [ÎcSTÖ¥È7B“øm¹Š4„?=AõÞ«DqªxBôE'/í5”!á\kú*`“¤ëÈ• MßŃÐä?И|Ãæù~…Áv›®ŽbJ³ r¹Jm«*곉X~ Ø„ÀÚš¹ cu·bûŠ+Ãöì¸:׌ ªd–Êb¿í'σg²r‚¡ö¨o‘•§ÉYEk÷Í—/ä 7g-ÝãCÁº¯f¼½õËÑ ƒ)\²ÂºgßEëjÆ4¨*”åû>W:ÒYŒ·bD.Z•6²²` ”2ÔNÊæåÖµ•MÿÆF#AhªAÒÔþê÷Ú¼(¢à*êAQ÷“ç ÌÑŒQ¬§(/õ`*m^°;áDÉêÆå39Ï4¼œ£9Ž%ѳªT=Á½ ¬é /®´öqçõ¡ïÝ}`*~äKñxáˆ2ü¿#ðçÈt‚Ó³xµ¿É]ÅK9Rn¸b¦‡ÁŒ6 8 üUÇB.H÷d¯ZbÕEŸ/ ¨ØÐü30à °Êw¦}L'U |ÍÝá2³¥Z”)†k] bÛÿäÕ R¹Óp  ‰ró~&®3ÊôUÇ å'žêi͵ؤj²åÕ l;¦ÑÛÖù%#B¡•?MÛ²o¡Q!gèCøøR˜Î’8g9µ}Ü/°l÷úÛoð2ß÷GOh‰É;ûJÉi\¢v66Í=©%­ÚÌBm¢oãSŸð«Ç £ËÏÛìð§DÛj ¾ž>ƒáUÛ×Àm£Ó1‰Íú©%ÜŠHµ-˪’’®Ø`…3ÑJkJïV½qM3BaLU9W®—ŽFWšÑá„HÍ:žnäRË[iMÓì#90DœY_%¡—…‡þ¶ãÅ *ÄŠ& H!辈¹Ç—{'m»§Ó"asÚ–h‚t/ZŠË÷׫Óx¨ª$T&@8u„·cÝ ” cÙ{…Èô˜C9kÜ9¡§™Žå eË×ÖÆk£x¹¦SL쟘1˜5çY÷ÊÈÆ?ï'¦ò±Ê‹Ïö*MŠ|»Õ(ŠÆƒŠÔ6ååØ à°çJY_z¯Ïëïg^Ò¤Ï]*øÜœJY¸ž…›ÂŸ<¸$L΋¥ýBL9Þ%C1É‚  bñ%ñ ‚ŸÆ«¦xS™§¡#NÔðð»¢*ç3¦aÂ[Ô Ø8I”»ÑøÌ÷_9f"ãÈ%iƒÀäc=²C 7`|MRà÷7 ®í½ï#Ç­U`8z©Â¡?äôl~ÔŽîä¬5hÿ4ò: rd¢8]QÖQ¥lÈ>N@' ñ¡ ™iY¿ë¹#;5(!ñú5xcÂìö½}ÕÊÍå·8Z ø†ÿ˜ß‘Ác*Înmâ+^Á€þòòöíáå"ú׊,s-ýï üµ™Ø÷‡;', NˆÉÍÔfÒ2†T³.ÑC0â½ÉFåñžG²Â7QêÕš©BÞàê2!© `‹Þ?óÓµ/l5ßQ”⾈ãúhƒÜÃ_ÃâGÖ™È]E²Ì `4|”¤ x,âùQç¶Œ˜J[¶ÏY],Mp®÷?‡õέ«|˜‹¢Ÿæ’e¹&yB)5ªOí•+@$ :S*ø2ôL4 À­Ã»KºØ"3ÿ±3„‹ ê鸱X"¡µ1©A2ðÓŠU2ѽ üÚvÔ4ÛDõÛƒF ±ð¸“ºêº¤›iZ”vÂ$]ÔÉëahZ¹KÄrÓãS3ÉÑ¡‡3<ÒQ0óŽ&Žë 135ö”ë…ÙŸ%Øp%¥ .þ¼ÁVó›ªÜ §CDJ.¸îBwa®*Ò?R¿SÊôµV t†THg_(‚!+y>ÍQÀ!K^¼‹_“zB9çJ. 7÷Aṁj,7¶»(àÉÀ1»pÓ=„W`çšÅ|g\ùyçŒËz.©5 5º"ÕâGô ´{) ~'ÃGÞ·³/CYC´žŠùÍ¿ÝÀ9t ¦Û¦ ™,¿‰÷ÌÈÊqgcAs[öyƒ 2÷õ4=ê\_–ÉëÄ@#uH†M vëJ’o<Œçäz/ŒVÀQÊœ#_¨iÛ´÷lª1qÜvj‹+ñTÙ •Ay}h‘—:…ó§9F$S5ø›››6¦£XÚ;/,Ly%ñ"ŸVên®B€Æ¶‰Û²°uR’ò«Py×7N«â‡ë¾p ËÂqÖÏ{yhn³zŠ bôfEq"Ló ·NçøLe9é]} +¨ÎDvA§Q´¢݉N¶”_Οå&hÒö.z-KêÜ2©cÜËß?†Ò¶ý«*}3 A gë$^¯ ñ=ë÷Ê€ÏÒAq<ÎÔÆMÁiMÞ+à2Iêkºø’ÉùTËšM:™1te oM˜N£&Í;‡µEF±\•ücðÏr…ÊTÄ©áˆi½˜”Š=ÞÉò9Ò£9ˆ@ÁZ¥.ùã¬à§€å–«„À±øLj™èŽ»—¹»à®!ªþ…_/‹˜:YƒÖµ4§hƒOÛSAñÚµ)±·nØÎ ¸¾ ÄhjŒ°A{OX'¹ë2pBW•“ †Šn•Û¯Š×ìy‘þÃËÍH½ËÜþÊF݋߯M`†sÝ×ûoµRt|·n=)¿²öTÔË¿ÔÍÄ«ët1Œ5ùRèYf£>»ÿ)U³¡7›ÙŸÚD~*kÌNîY«ˆÃ£ýPom~#©/T¨EÂÈ‘¥‰¥âl®¤ˆíÛ‘$*¾­½tîøl›`ÚÁû(ÁY›ºÃŽN°Í\R©"cEqtnåÕùÝ~ù(_HP*Œ¿3"l! ;éÑ-Þê8垊.Ø7[à9h¨Ì¨®;§¦³¾}-3[㤱¥jØuTš“wPgY] žð{¹”š†ºiëq0÷„*|ÒŸ¹zÎQ9þ'ŠM¾ ¬-ɳíû¸båÏ[wãw3‡ßµõK‚ !ÛÔ½ ªqfÅþ`CXŒ:bxİlv¬žÚMG*zmÞ™ê÷Ÿ=×[)”7KÇ$Û¸y^˜xFÖgˆ"` ô?Þ˜]ŒÅ£´©é–)A•ªrM·šQíQÒKOÍ”œw­*تﱸL‹¬ÁûâKªrä=w6˜lf‘З³ëÑ”ͧ.€. }¥øæ\/Umõl F.Gõ}»áÇð¯¼~Rz­[!mÌp¨ÏŠÐ#|­}Y ’ÞÇJ+ bzG`42¾ãd8ÛWR¸/Sœ¡ú½ž¯ÜµKŃ­ÈŠ»«XV¬[’»ã~¸ÄJ1U‘9a˜®â@XÐôûžž>UDÞVuñº7†BCºžÀI LXLjصÜwÂÛ#‚ÍuÜ >sÖnÚ§–tŽ$[焬"1²ŒañŽ-¦ë_€Îý˜½áé~/òTj»½ùLÓ7Ê@-!a–ª7ãLR®äóá¥9c–žü)üðMª.Ý´ïZ%Þ®‰L ˆ3 ï†f®m:äewkîÅØLV·£õ„D ŸŠ.ß©ð`ˆ³8K€-ü΃+Ü”DüãI¸—l9;ê£T¯zz~ZhIM$ß)W²§¥¯³¶ý„Ño4¹?>ÚLÊ*‚­l}n`á\ãÃÐ/ ;Õ¹ÒñÞ˜8ˆSÌ÷òé(·LL[›`+³æ¼ÃuÞÒóáâé TMX[HÚTuKd/¡ ¨ãIMÄð*ñ7:µ3éÑ„I|ÛCJÜT ÐåP<µ›,8Ì88îwúÝ}¦ÚÈRqøèÊ%Î0ŽûXkS”¯.,fú¡DðùèÀÏÒc艿;~qoï_¸wU‚—ÖUØårÙî¶OÂz‹A,FÄþˆt „µëç¤@Â,˳TOE«s¬è×ê«A§Ox¡ Õúy/À‚Ç2žÛý&±4F¨8©+à{äªÉå`äýÒÁ1<ñÎŒRl×UäÅÒ]ù¤É\ï ûªõÃÆ“Ï9pch¨¶ …¤úÇóô·Änòpǃ9÷v„D£ßƒ4ÏÕ×ãc6h>ÿøÐ¯Ü™›šª&L‹,„É{c£Ý,ȨÕxÑyULÜ5_Bcë^€1s,.¦¬'Ì H½¶õñhÝožäH;ûp¾˜W³¤ú¢Fíp`:¹qQ@—›û cš}Õ`<-…ƒ~B¤À]ð–ý†.Þû–pÞ•"à“hc Œ8(ôu½°U‚£ñJb¯‹ ªS(Ñ#Î(/äÖë![º¯ñ<[Âiý»Ø¢ˆ*•;ížDa*ˆV“´[µ äV] [.£~TÒÆmÛ”r‘+­²«ÏZ¹Êœ@Ù#ÿ¶T}w7p¢î n—CÿU.Ððõ^©&æÕ[ó«¶K¢t™ £‚)Y?ÑÞJ§Z sûåî9ŠZ˜2†Ý‡,}{”¡â‘/… Ond|7KPA·;û;l‰Ï/â¡Éix¸¬su딄‰¦‡¥ÿ棋G endstream endobj 394 0 obj << /Type /FontDescriptor /FontName /QRKXTL+CMR10 /Flags 4 /FontBBox [-251 -250 1009 969] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 69 /XHeight 431 /CharSet (/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/Omega/P/R/S/T/U/V/W/X/Z/a/acute/b/bracketleft/bracketright/c/colon/comma/d/dieresis/e/eight/endash/equal/f/ff/ffi/fi/five/fl/four/g/h/hyphen/i/j/k/l/m/n/nine/o/one/p/parenleft/parenright/percent/period/plus/q/quotedblleft/quotedblright/quoteright/r/s/semicolon/seven/six/slash/t/three/two/u/v/w/x/y/z/zero) /FontFile 393 0 R >> endobj 395 0 obj << /Length1 1184 /Length2 5856 /Length3 0 /Length 6583 /Filter /FlateDecode >> stream xÚ­“e\”k×öéTI‘f¤kºEº;‰†˜!†”’NénééABJA@éVBÞÙ{¿÷Öç~¾>¿ëËõ_kë8®u­“í¡Ž>PÎn Q‚ÃÜ€|’yM=A€?›<bá…Ã,Ü ’ €œ»-@ *)$.É/Bć;{! ¶vnNy®¿ŠÄrNÔÊдp³ƒ8¡{XY8ôáVPˆ›@ÎÑ ÷× W€Ä‚ð€Xó ¬¡VnKˆ-FúË*Ìû'líîüŸ”áŠ6àD›ä -ZÃaŽ^kˆ H ŽÖ‚ ü_˜úïæJZNµÿkHÿ+máuôúÿp'gw7  ·† `ÿ]j ùÇ›&ÄêîôßYU7 G¨•ÌÖàÿ'uU‚zB¬u nVv GWÈßqÌú¿M çö·†¾¼º.˜çŸÿùwNÇ s3ðrþ·ë_ųÀoFOõ˜òóñó   ÑÏÞžý—–"Ì n …¡BD`@Xx¡7M"f ñ@<цA|0¸ú=?€ Aô×ï€äþ ýC¢ø7‰@ò¿IRøMâ¿$&)ÿ&aHí7¡»hþKâ‚Þo€ôZÝè_Bo Èâ7¡Õ-ZÝê_FëYÁœ~W ðó@Ö ÚäDËÚþèóv z$Ð?­ìø¢¥~£Zö/Š =à°?¤Ð7 ÿíý…ÎèÿÛZñ¢KÜ~7D·wCÂÿH£Ýxüî‡&oâŸüÿÞA0îéEÐ#ˆ‰ðûý:+wsûûz£ù?lE¯=â ±"Zœƒ[I½²O}ZöR±p²—lÛ¯ÕÐ3ÓI¼ðÓ±xTÝ…ûK½ÉUEŽMÜM&ä5kd‡¯î¸Òa K|ÚìÍ¦Ç‹Í ïV:““ M¯ gæóà]’ö·ÏöD0µ§¿Œ•%=)È>ÜÍ×áT0øŽ¿ôã©Gã»ÌW¢b&Jކa!õìBõHÓ\Ê…Ö‘¤I 8Ë‚_Ú‡´ð|>†w¸“_ä%Þ˜mOËšReÍŠ>ƒMÌÎÛÍ99ÚYDE«ð3EÆf¶¶]_UeO ½eOƒ}|”•h!ªØÂ’0;ºFëÛ˜ý’/hécÈXžR¯~³$Ûu‰FëÉ;@Á§öÌs>Þ£eX“Ö^›I fÅ¢P“ïVaÞ”§î¼ª¶+¡Ó©%›ö‹”¦°•y?s‹;‹qiªäp–åUþózär:®YYVuVúýÈþ5ñk^›ý„ÝÆ÷„ÜåøÑk–ÊùBOg?•éLc'—ˆkÝ"v iM#<`VæŠý=vU/·l%â°k}[Yj¦¨ïñÌuÀpm»é>KÍDªa&‰Å­–¨Ø[¨Ã÷¢ |묧±ƒtæyÎ1‡¾OÎ9|FZÙ"Ä3;ipΙì¹KÓ¤1jËÙ… “jâiã+ËGÞSÎ ËYeÎÏ881Ñ5G{õ åS%Ós‘bIO|jûïøø–Ëyÿné<×üÖçKX×4³ÿn>§ú¥AúÌý4¬D…ë—K߈{ÍòÍqÊ«± ¼×a936Èìܳ‹÷žlÓÔû¼¯ ü8ìëçG=]9[]ùØkLí™ø5à:ìn@&ÆÏ¬6ÇÎèà¨S:}øš·"þ›|»Ò¶•tì¤É«@öEšÌ³¤¦;ìaTR Ïvu„²œ/-åúî+÷3¼ãöÖGì|¡dÍÖEÓŸdùk˘EìK•–nS̓ƪ¹{¾¿sª’¹†¢ü©d#ª¾{W…örSJ$½¢ø»„²GMÎæ“´aÚÒŒo™y!cS¢™ó·&»þ4 úWµ?Ø]sPê>ÿl|òÚ°èTþµdºÚr–,Xõ¾-ŸÂ¯ÍHоôŒáâÆ>£ñØ´}8y©‘±}Ö¦lF)oŽ-átêpðÍGwÑŸ*ØíxµAa€LšÑu_ ƒ=þõQÖSéôŸÞ—a¦*fœÂÕcÃõƒ@ÌŽ'ßÛ— ÅPëpÛîû}]_0ô7õ¨B0±Ý7üm^mÁ¹+#ØÂŒ ÓSSUiÇøUz:|íþÜyê%Gp_²ìPϳR£q‹å\ S˜3„oGûÌv˜+›†ƒ D&1¬:¸‘އ’…Ws8[N»æpÉ£˜¨‹9{¢ôOWg¹w:ØA/>ÁAùš`%orRè2j‡·—4ÆŸ™>K"ýüPÊ]ZÂ(‰[ž¦[™µ¦”9Õ®°ùIµqèú`y!¿ yìÇŽ,6W¥—ëÎÅ•|;iæ– 6iYK·iôº¸<}R¸7•?‘Ž;átë »a3á=™¤¤^( sZ+ˆôǃ/_¿ÃµéžkczY 5 ]‘8ãç‹9ñuJ/]&½HS;Èyörí^”QÖ•í¶ÙhÓV·[´ø´»fJ5ñµ¯/’¤7$¦°ÆÃJò®ÒÔB@qþÅE¡[ø«tÌU£© LYëýãÐYÚÁÀqÉŠ¹tÜó?\1Rsï9¢gÊwrÕ±KdSBËG].ÓŽ.¸Gñ71íëB%šºpò,{¯2 Üx7 OÅbQIåFî+y|`8?uÎóW<ÓÒDQ—†j’Ê£j§,KcuoRQsò›UÊOe‰šGÖŠ'Äüúû 0T©Þ› 6²Ö÷‘?Lß$VîÈ[5SÊ/KcwX©g0h{ zž_ó¬ÎŸt?÷Ÿß(W^ !V ó?i1}zY£Ä¹Ü—Û‚k{æûøbð»ꊻ•>nhjÉR§NS4[#á·-m ÖwÙ'ŠhæØ:àZ?­¡ëêðÐ ƒ·úÁ+´Ÿwa”Åï$ ïNµ.Çô… {8 c'b'4™áeÛ«É6ùÕg1eåU)nE¤àV 7>^i´´¢V_i&—.\¾}Õ3`Ø`çÛýzRvv1f)>ö²Y[»/1<åPv*1'+#%‡°y¾mŸÈJ×RÈþ$<ÜmFSµ |*Sh.4‰Û|ÒBº!¦srcž Kýúèiá½&Û;Ö($“É¥ZÒ0¦=³•*Ö‘¾*7³|$Ô1ñÝÛ­ÄIsq’(ž•\þÚÑq5·AIôcÙ4ï¤Æ«Íìâ\§½}Å“:ý¯”±GX¬ÃrÂwƒc¦Ä„hŽï¦WŤ$ñï~Iþ6ÕOÏÝ^@¨¨³‰7·SÛ|¨A‚/P'ÔǸßÊè ¼– ªPœP3øeâþ`LGWR]5¡í+bŒö¾†Aû««#â&;_áìhJ7‰HGRñÕNCž¤[`Ù[,fV|¡È"ê™ØsJÞ;Ù2I®Å1€UˆÉðá3“‰ÔKÐÄ‘_y¦O¾9 ÓüPÂëÄÅ…_œ|ˆuKHtN%% ¨ë~Úëªs¹„ üq/Âv¾"˳ý¡úظT½âÂ×:‡øÌ¸ªªÖ–ÄïiEÝ•j?\"ú€!(ivEVpÓèäÉ0?xŽ\1Ìg®ClË›ÿt¦®>áÛuŽõ; 0û•°Å“!< Œ¶ÁÈmã‰k 'ÿŸö¨3zä›õìëZz”÷È7‡hÎù9ÏwŸÍû½*‘­žx ÇÃK)ƒ‡QL sŒ}ÒT’ò‘É+¥¨[FêëZÃÖü½ÎT7rE'⽈~Æx"&`™ X‡ŒÆe¹hö¤½¾9E›û~Þ^Ø]Í_šë¥vl-s°rÕ»5A´ÚFëÙn][åf\òæžÛ3&u àca—ÎÆŸŸ«ìÌvxßà žfæ>ÃeX«~Vl{µt#¿›|w/6c“¢î¶—<îYk”ƒ*À㊳>àLéûêðBLò¶ ÒùMÇ:ÓägG£$K—F¸/®^o6màÙÐm€·kç‚eêŒ^̦+“‡ów{}%^*Ï+‘:e^É_6QŸkóú¦£É–j‰÷å4ELP@쌳»%X×ÚfM½é±C”Ùøn™NõQ.&(zsk,Ûv±Âµ‚µTµü¥Û§Éµ……z±õ%m¡|Iöþr`ÅkÖþh5èBS¦.¸ù½ˆ¿Ê&wS†"¬cÅ1‹(é#Ÿ/ÁVadÕUèšêÆìñ‘íðJW±‚Ç o¥óAx@-£gWeh8ŒŒ?kµL~F<˜àÎ §ˆë÷¿ÄÃ:‚Á='^°7ØËm"šØl賕gJÉá`–>”uéQÉ=šoñzràËl™ÕB@hñãv¬Ý>ÊENƒðÓ‹#p.‘Õ•ËÅ_tE<'x}!ôà¬F”ñhµGHE•yŒ ¸÷€TDétÅr®)>$1*(1¹n¬õþC[™!{&2€_ÒxXú´®W±ií§•Ž á¢#r”ž½@´>’/8>¢³ÈŸì$7$87¯\.°N<æa+íäqÿFd>Þ/N›Þ‡³6àÏ|,!QÑÜ´Çpq1:Ê„cž8DÜ@ñº›—Œ‘‚bF×fÁÊ>s<õœõÀb‰R¾’Û4GÔëÃÉ×ÛÈ”Q”r¸Ø°}W°Î¬÷æñø7ˆ´¨Py {ªCnê^=I 2·²ð'×ѵ‹=ÿÑOzXfFЫî"/^‰{v°õþ¼Úþç¤|ŠSûX›‰G¦µ ó|($–|#þëýð*9³SC-¹;T€ÉçmsØ«eÁY¬Á qäýVs\­¬H bxÍ-ü&E%ÚÉ6ý Ñþ,+eödìµ>ÙÓš zÝqëýðXÎP £—àÌá-϶Jî$“®¨ê˜K!OüM)I¥º÷ê\寛d5מ=•ÍæŒÎ•½•·TµMZ xªœ55NŸœZQ­<à²ó”êÞx…TÏ~sã›[3/µ}÷0 ¯¡•³)¢öx¼m BE¤™èÚã{ž:0^0B,²à:µ/n:¦f—¦­€L%|£õçöÜ~èÉJŸ)LM‚¶g²òøH¼SçñyŒZøþ.Ve#†¾¿Äv)9?oë©0„\¾nQ{TäÍ•¿FcÅÛA¿àò,C^¼’½á9$¸-¥cªBàHõéþ†m£y;ŸÙnÏ–Y~láA už}V~Z̾ð GÁŠþÏážrìØÊàŽM¦J§]LÉÛÃp/§ÂvºUPRÄ!~êEíG… 5"(¶¹KÌùŠã§©ºê€+už Ý[S·.wËI±b÷×ÔŒ›»¥7©Æ¢8#{Ú„”Ø­M@Ò'¸ñ´DÊÕÕÚ’ö},Ý#þœ`pL–S€â›öLÚ R”tàà»)66yŠ¡U3ŸE<‰]ÝDFœÅtï}ÂŽÊv—%Ùo¯kÛå>¶ºò{Çí»VJqF0×V&Õ0gòFÔ!¼ëÁn3ý¦Ë” ÅÀñ†“î,¦Ø%¾gn²É±øñ×Àt])˪¢ãÇ™‹Ö¥Âša‚'«EÍ_6‚ÏT»ñðfŠì±®†cBàUªaÍjë~t~JA0_³Ï… …|6$EKrºVË!¿°Ô]Qê„/Cw:'R·;­ÍŠAÜyê:ý"½ !ÓBgKyb\Ð(³Ö4ábP¿D÷þàX/§›Ée_—{ž“Ñó/è!%2“±@¨*URBºë›œÜ+QQå>TžÙ5ü`‡-3'$vö­ÁٱȃJd‚¦‹…6Ûáe݆B‚.*rT•‘L/C?aeS+då-Årg·;´¨Ë­ËÛéæ¾î}‘B€Åý™-IóÔÖÇ*?Ǽ u z»ö¥ÑûÙ·JŠJ«EÌ›“^¾.¤0ú»ÊÖ7ƾM-K<&•¡Ô;Ô,íùRN›¶V2‹J’aÇ0ë]ËRCÁýænÝUÚÖO%°jÉ_¥áCb‚“c$!æ÷Ž}5_L( œó)>®õ¿ŸMF°ˆ!ÂIû4Ïn¥­sFzOì{Š\ÎŽÊS¢i`3“Ïcª‡ªë¬Tf¿bá¼Ë>®ÅŽ»†fý4Zõ4·ÂÌML3:DÏéÈA’@–·»_C¿TFG66€ë€²!ÉD sxgEØ*CŠ#$È·o‡H’F†÷ÎýÅ¿mGê)¿sö{èéG}ïQUÌÇkQpŽWÙ?iñµêA]êÔÅàÅçÚw௸ÕoAFr0vóqW>­¾æG{ˆÆT}ß Ë »]ë8$Õ¹@j‚àÓée—­®>[o9~ÕU佉_œŠÔ˜'ÈÅ1‘Uÿ‚ªgS‹‹¦W?hÜOC{§Ì¾ G”=ØWäðdF–î Us šR:Þ‰2³Z€§ö|Zç©7¯‘p;Š Á0×ê"` õ~ZÂNeN³Y,m{=äÍsíKð*„}£ %]=²¬K\j~ì·µ6Kðl EÖhCÖH~_˜ÈFÊŽhbU Ì4ó‘N¶êÉô(Ì@¯Ï> ×=ÛNã4N÷ÈéÃÄÓ×z€Ú+fÒÖ£Ð`ìmêTzuš!éÉãøúd”»›œœ\ÿùBÞ‹ŸË›'Ž'íûš.ïQJ ö »§»ße >DIŒ·ðÃË™a$öQùÜ9sÌÛT¬RÇV³ñÆkû³Òs–ñè²5Ö\²JÆ1Çìnb)˜l‡Ó9ý:,—h#’ñajíøV›iëØ…‹q.ÒvÚ´kï|3Æ"=,is €ÝÑÂð…ÄÓ'Á‡ñÕU/<Ém¶*xIéÓ\-=P˜Ô'gMËLÝÆt°°¥¼¹cw² ¤zYy[j¿–ÝÒ yOoú]éÒnß+ëÔ_R†'ˆ|Ãuq¸ ß{O¦V_M¶mîà¶ @ƒ‹aýèÃ×I×…2_ÄX¨=»™ê…Ú&ÀˆúŒM£oVô9A˜°} Á‡Emo;BÏ —Ã'Ëë#Ɇ;ý©4ø¦ù[ßi†ÒÙû½° Ø£ Q-¥XT±ÐÂÉ@@© >(½DÝŸ¬‚ÍŒ¿ê‡ÓC›O#eȦƒ‰“ Ã¿Þ 4Ê_>S4S#i%¹%ðKìŠÏz¸K1czØzÆÓGéä°‘?' ©]¯x1¿’Wg~C4¦’˜üæó¡žLÉ%'›×Óõ”·óÅáà좒Ö0ðÕ„)y_ݾ_ÌQ—Ì;Aiâ:A©ã«® …K9êÞ¶&k9'i\lÖmÕ«nÍPÜwޏ —¥ï-‹ó÷Š`¡!¦^\®à茙Y;âÈ}ሌ£ì¶Û*%.²–7a ¯¸]÷OMîúó§nhZÃPõE¡Ú%Ts4¯*D¸Û¤Ü²Á~“™• |¾z'y}Gê¶ %ç{òLpcŸk> endobj 397 0 obj << /Length1 1044 /Length2 4412 /Length3 0 /Length 5071 /Filter /FlateDecode >> stream xÚ­”g<\{·Çµè]D ¶ˆè†Ñ ‚è£GD7Œ0£ŒÞ{‚¨Ñ{ï%$DoAQ¢÷Þ¢…A”;9ç>çäžçíýì7û»ÖÚë÷ÛÿµöfcÑÒ啵BZÀ‘¯Ÿ€ ¯®# ðñ²±É;à(8ñŠ‚Iâ‬« æD$Å$ÀB„l€<ÒÑÓnc‹8ä9‰²0g¸%¨CQ¶0tK¨= ‹´„ÃPž|€¬½= óë @æsvƒYñ VpK`³#A¿ © ¬‘€è_a+WÇÿ¤Ü`Î.hSÚ$'€¶h…DØ{V0kB­C;ùÿ0õïæŠ®ööP‡_íÒ¥¡p{Ïÿ-@:8º¢`΀:Ò æŒøw©>ì/oê0+¸«Ã¿³*(¨=ÜRacøÿ Á]á0+-8ÊÒ°†Ú»ÀþŒÃVÿ6>·?-€4 ôeŸqÿ5Ï?sZP8õÄÓñ﮿Šÿd}:ÎpÀˆŸŸ_]ˆ¾þsgò/-„%Ò Ž@/„°uv†z¢7M€·GXÁ<˜Ú0ˆD¡Ðgâ X# SDÉþ ýE¢Hþoã@Zÿ Òý›Ð³Aÿ!1dñ‰ Ë¿Iíû ÁÈú7D·µù …íoˆ¶ÿ Ñ:ö¿!ZÈá@ûEü†h]äoˆÖuü ÑBο!ZÈå7@¨ßý¶®ÿ ÝÙóOüï¹ËÉ!=¼y^°0? .$ ˆ ‰ûþŸ:KWggõç'…^žÿ°5½j0˜Ì’pfi)b—\Vê§?Rv‹ SΦ!V£®c¬•(x:Ó¾è5'®ÅÚgåiT¤›·6™Ý/\"Z|´¿:ŦL\oº™o¦y52<û–¦î¹áxïGð.Ysýø÷=aLÍÑÅÒ×=™‡½»¹ZŸláͳ`t¹½íL}¦˜f¯Zû@E‡<Å©ô…Pø‚;ùëxœ…á`?»pü÷ÜsGÈWÊ‹|üœ„k3<Óœ\Bêšý[Ø™ à7ˆÄü¡d¶Î:£JO8å%8Z‚ãùÇ,P˜²ªäjêç†ÙZCšžŽÿ|ns8„^ºº÷„Csÿ¡å£Ð&åK;,DRÒ„ž«2†÷ùçi™²º™Å®˜¤žÒ*£ù„{ÛŒ7¦l$»bÊ‚ìþŸço»ŒÁÁÇCÅjA¹5™Å_ª4›ªY÷Oò—}ÅÅLt¨•ºËÊ£n©¸"š¢¿+T«ªø”Þ¯ø$5¡€Œæñymgt¤Á—˜’ø£_S…Êùi›¢*kXÈ{<«2|g¥hºò`ðb!ï2•ç¾8OÁk$!bGYLã˜fùàãDK2Ðe±AlîÚ1’}ö8mÁ¢iÔ;AÇD ¿«týév6D`n¤ðí¼¢[è…ûVðžB|ÇøÞ»CããOY \$«Ö˜}™åßÌ7S»Ê²Ò[?¢ðÐ!K¿>ú#ý-ˆq}+GLH_…+Ï˶ Õ^Nðf´ÚAx/~ƒéè¶;»O;—¦¯ÛS­’Uf'Oœr\ý²ö*“»L¡Dºs8«òq|3n«®õ‰ Åd&p¼Šk骷¯ñÛ"m UmýýSpsæ¼]äpƒ*èËÞÝ‹¸œªÄ»úDZg2yÓMË<>E,õZÔ$yYh.”Û–¾sAi2ê¡ÚcìU ¬2H±Ê›lZM ýx¥=ÎÞ!´«(Y¾øžm#£¯$’·ý’‹oùþޤÿ™vö÷á"·)'晸C4v÷º¦AÈI„ÀÌöºJ©jä³ÄŒX¼xÏùrVµ®’‘¹ègGöKðqÒ‘Æe‹‹eböŽÛ8¦/û ªËq*Õ›Fyj ]ÍÄ ÃFëf‘"þ =§#F¹‡çœ†ƒ-âÚY`_&æöš`™÷êÍoÎûÆKã„çÜ»¸™p³Lj¨£sd¾¿d7) ³†EO)~)ÚßtRƲRZŠ ü–†ИLH˜#= —œ©\½¯ƒ¸Ë•Ñ}?C¬ðí~[;펭w޽?uºÀ¦k*çç2Iž˜zpØä“ÂB¸ñÒ×ÓÏÛ=H3Ûe¡þíÉTýÅG!-‚è[B<7s‹[ÉYMÐ]qi9Ï®aɇ%äÕEÄtØ):æTUTbýeC÷ÎøDÉŸÕÚú@u¢žŒ§l5Hx`oq•:?Ðþ€èø£®1­FEèNCvœdîã·™øœàOæ`‚nÔ­‡A}•íн¯TºµÁD¦ü3š ÆÒ.ëN8rË/èÓ‘ËÌ^÷7¼ØQÛ(.¢¡XÁNÉ"ФÔ= PÖ… òª\¯—Üq`V¨^Q¨£ i미ÿ%é±°S^Z„€NñŠ–;•J01_÷€A’üH\SV"ŒjgÞï©.oâØ]Ú?0W-žQ3çO†'5‹£úY»U%«Vv){™ßr;Òê÷Nž-fg)ï‰5[+Ý|þTzÂÄ9a´rr •«©½ZÔð[þñøÁõíüÓ2üÝp#Gl,™t$-Hn.7ñ¢6渨‘˜ôa;æÜžÜèõǦñÈÀYIØê;¨dØé¹“áŸhÝEüñ%Á~ëñóŒ¦RÏeïC»æÂ¿‰CˆÂ÷Íz˜cTÉó’õדå}¯rÕ6ÉäÇH°+Cý… r).KPØ» !uÅc?E"¨ÊéïkWu¤Ãö†1wz:E#‚-M®??äªç÷a}.éÝ:µ±ªmjðÉèjË·œ•w€/p‰jFáÕíQú:CjPc™‹FY숆æ“!7£åwìŒ×Zú5-ã’f6]BÏÏz#MÏxËÕ›¹ÀëžßפB§!oš1SùŸh[D~“ñïW»·SCÙ4éREÑ$gȸJ U{‰¼M¹RšÍ‘ÃÌbW?ïfç~·¯íîóÉÇ«¿éùFÓu&Xе¶šË5pûLø!øÅ•LJö ¢\›C"|Y§™~¨Sù(vG²Gè‹<ЫÉÑ\;ì‰0Ë maˆdø†châP>Þ<©JPî+稣†ž\êá6ëJxãuŽ‹ÍÆÏéPocCwßšMw7Ç.õ~Ø8õë'Ü)» ¾èÍê\HŠ8™ ƒùó< £ðš/Äæ£·…Â9çBæ¶S*sIqŸ8­¿$q×5:W­žw'ÉüºpxÈqcn²°Ø÷åð5¥\^ ?§=8ºò€/¬GÕBY±â½*™Øõð^ÆçE½ >fÂÓÙ=+ÃðôÁJ-Dµ›j¾[-FZ}Χ3ߢàÜ8¤¨øiI¿daÅI0&Ò«“[eè¨u9ú“3û`å„..fÞø¤•”ŽX%Á|Ì'hÙˆ¯²»qj_pÉëó±ÏíQq:oy,^ª<}ÓÛ@n7äƒt}nSòÖƒ Šü§îÅZå%VEoÖ•(!$¾û}ŠÝÒ× ÔŠçjˆw­Áìå;KÍÙxtCf2®7¡™ï·“=Ìž>7Ø÷aÌšv˜b»0ý‘}ê;¼º>£Ï;“¹2‘¤B¾£3Õî»=VyØAŽ=å•Ô¦Ùö2Š{‘pOÏÏ¥R>\Å B¯¸åUo›Î;»#ü9¢µuGÅ ÷¹3ú¯@r¯Å„¨ÀD§½K±ç#K,™DÑÝå9KvçåÐqÜp²PÎ^êl§z³(SàÐW•;·a ùVñõ؇LRX¨  ðéêé¹°?&:¥’v¥:ÏÃZÇÍP»wøÅÐqà²S|K…s¸Ë¦9œòU³¾ÒÜÊÈž¢Ê‰9ªëºöðFKàÛScj>«õPOkªßÌè­ù6¯µËBcÚ¢jB’¨ñß(5sT3_.ð, tíI¥WÆHžÓ~O.{ïYuV¬K4ïB¨HDŒå…²!ç7XõŽRÃ]³ë©÷j‹Õ7¬…ÕHä$'!¸°†>ù‹CÂËÌ2×S Óû¥°îï´®žÆ”ÔÎýñÊT.'ÔÊV×61‡! <â  |Ïæ4p¬Ê§¾‡È/`åiã¼¢õï3‹/X´ÝÁ¹Vé².;W>÷ì“1xQÏØÀhp?oÂ×~½±aQ'oœaC ®}|&s*6›%¯™«lÉÖ5áé~½ùN1Ù.ýðI3]CŠÔÉ5ÄsÇWëÁü»èÞÃr’v_1®6ÒÈ^ÝÑÞŠ“PîrÆ™/oÖÎàÔËr½ò)ØÆªƒ÷®ºèÌAkoG/Ôpç "/¦IqcÉÏ–â¢3îO`Û¹Þšþ6ùB]I.Å SìÔö~|VmÏëüsÜz¦‚Ëd/7¯óÓÝlÏXÚö(ó:…³àB~^ÕW™œK–‚v~™_¯"¼³q6 1 ‡§w#LœâlHÚ[Ev?JY ^áw3V±ÐξîÃâÆç§´ˆ”“šnäܤˆï$¦ÐpŸ{$J€Ù£Í®ÞŸú©¿Šƒ¦£Ñy”†‚å®»p±­¢¿#èÚ\°Ëè& ©Û¢ÂaEE[@ÒvxkßBœ% 6VÝBÚŠÞµ¤(Q-i·’?£iß3’‹û˜ù"ÜÆ0¤´&üQj¯Ù‰Ð}[ždOa˜/`J'V\¦îeÔä>WY Îs*[¾Ät*œnKß”D/ Ú>67Šå¨¸¾®wµßGGA ~juð4¯öÌX‡Ý¹ÜÜËös•œÊñ@-î›Ð3o‡»årº1 'Æm¶þ¿Ôÿ„ãl½ÆÚE'ÄܞȠŒ½f3mUIù.ZκyšÒÙ ¥¶½¯ÄäÑ-¥sa¦™šÅTlhå*ŠÿƒŒˆÄ_ûª‰ÃçëÚÔNšÅ¾ûó6F'ÔººioLЋï­sæí—)Ùµ9Èw[Þß›KôŸ`™~·UµÊg<ÆŽ#§*žÒê…iSW6‡±e1à»Ý_Ž=ßG=0£~óߟ=‹ +¯NÓk6ójè®Lqœ€û¾<´*"Ü©Y²”j+• !œbèö4vá\½SKòhvŸÅ„Q_Õx˜ûLáûH”péíž"“âݯ?&¨²Å'̱8|~ÓÓÍd–’/œ-?¯Ne4ß¾,àï`3&ùaüHkË&À‹’‘cÄxþÍubôþ¥Ýïx©TT)Æ?}çö-§vüý•¸º¢÷ügýy!x€Âûôã…kÕˆáÍ»S)Å•´–¼»×Ǭ؅LîÑXEŒ½Ò³#‡¸Ƶçé­&‰ˆV&E¯¯4±c—‰ïÕ;3Akã½]â’ܶš¹<Œ¾:LøN¡Ûô,ÙbL·²™öo›O:ø©(kCÛãÛ¯Ä.ž7ª/;pÍÇ#a1m>RùD þAe@T¼ûkrÛjÌ>Å%IÁU}¨¶¼ ~Mb}«â ,â'ˆ ”Û¿#Ã9ˆ¢ÈÐ÷ [#’°WÍ ¡w`•ׇ½Jbâ¿”xÿ£ºIƒ˜Âø Ê5ªÒKPîAÒ¨¦Ý§É®%q¨`Ю ép¸«ÂôÅÏÒµzLñ¡½LÿìŠVíM5†ÊƒC|9ÅK‡/EHñH±ÿp‹Rá]‘ƒ‚‡sÍÆ˜Ž ' XÏ»±./iécRâ°Káï0ÞhZ,ÖÁø:¡¤\%d»AŸø£š‹Æã²’´ÇdOz±©ÙÝ‚Mƒ&6|³emï iµ®¸/4FÚ ÇÏDz›i½ÒmtŸíX¡xÝÊ!\ŽM×DS{cs¯{Æ^¨.%£¥~|þhMãÉ›ìLr Òí¹E¢d-Ë£‚3?ËéH;Û`Ú+gßn¬rØ ¾¦´†tÓd5l7A DþL°" Žjù³L ð–¨"t(•o³ò%o¦õ¤5Î'ïboi )ø¾4Ѿ`Æ]Âw†ÔCOCÅËò_`þØ^2™3.—¼;œ;í~Gú‘†&uʆD÷ÑÅš•YUP›¿»_޵ìË ŒÕ:G=ÂÛ‹X…sŒ©Ô<½sI¹ý¶rz3Øæ¸ñZÙ~ä?›Ü Aš[˜öj ÔÒ^Z£3Gqdœ¦ß*3¯—M‹è¨Ã¡aß6º,¦§øSUì§¹|Nó¶5¸¨Úž{l\φz@”úÏîѦ춽î£{Uä!ÇL6ú-f©Ñ!Ί[RïÊ…1+¹JÔTþ«LAöi)Ƶl-0|[â°Xï” endstream endobj 398 0 obj << /Type /FontDescriptor /FontName /NYWAFX+CMR17 /Flags 4 /FontBBox [-33 -250 945 749] /Ascent 694 /CapHeight 683 /Descent -195 /ItalicAngle 0 /StemV 53 /XHeight 430 /CharSet (/A/C/P/S/a/b/c/e/f/g/h/i/l/m/n/o/p/r/s/t/u/y) /FontFile 397 0 R >> endobj 399 0 obj << /Length1 769 /Length2 1292 /Length3 0 /Length 1835 /Filter /FlateDecode >> stream xÚ­RiXS×5"R>ÀA=ЂŒÂ$2„ TˆR©Æä.&÷&7 “’¯•áÉ  àHÁBEe*b+¢È 2Ê   *hñ(§ï‚µï{ôïûÖ^gŸuÖÞúºÌÝfÎt?ä†"b3*‰jè^>4@%Qˆúút b‰aqe‰!;@µµ¥g ˜S•fgakG1'ê:*ˆÆ`n˜ÒEÖÀ™a0›…/–8 âã=Ø,زaHMÎ<ðYÂ" ‰H¥ÌƒýFˆäE?H( ¬?Ó‰àK)ÂD¸)`ˆ›4¸EŠð¢ %’w¢ø]îäÿajys7 ·“Å_lgô·*‹ó¢ÿ¬£|D aÀ å@²\ê}¶æq` yÕCÌâÁlg„˃å3‹Üà(ˆÃ„Åì0Ê≠%B8ËMà±-Y ø2njô©>ç^ ­±x RÒÖ¢pô†Çví±=¨gÙÈìc–,ýùU–3…=h­jèÖu ÎreüvfÀ¦!Øï_»ÂJªzˆû¯^¶¾Ñð`v|ÍøÇª·L¿vÏÚPóVù4*@ûÀD·?Ï»ÿD0ãyãÝûï‹àà"ËjiókŽp-%®úã×Þw®¤n0Œ7´}±ñNC4žF˘X_³ûZzÃ…ÊTO÷ä$eáeÁmë—pë¦ÞÀ—9[{ë¶V_ßMç$†,$ÛðÇ]ënÔ7iv¨8Õæ< ž<ÿ²YÜ¿æá˜cÑcõ± d•é:û…¼_42³yšÔsyY1-±O÷mõ×wÇ\áO\C¯¿óX¸t÷ø;Ó £ÎF_uÇÛ2ÿ©˜OÇýÖéŒÞôúwé>Ãj–¯•UËe÷çe¦¥¿›ÖÝ´Ý~_=lÍ/c7i$)bky‘=5‰äÆÚ·¼¸6 á³ö3Rå&°Km[B–©ÿÍCvåc›Oî57P©O?Ý`óh‡S[ª øIɯä?g+y endstream endobj 400 0 obj << /Type /FontDescriptor /FontName /ZUELSB+CMR6 /Flags 4 /FontBBox [-20 -250 1193 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 83 /XHeight 431 /CharSet (/equal/one/two) /FontFile 399 0 R >> endobj 401 0 obj << /Length1 770 /Length2 1283 /Length3 0 /Length 1829 /Filter /FlateDecode >> stream xÚ­Ry<”û·ä”q0g.Õ¼œ#Ì c,%ƒ±Ô±D–9ZŒ™oy™…¡(Kæ¦,e-»Û¨œ,WÎÕ܈6»’D¸çJÙé$œóÒížÏuþ½Ÿß?¿çû}~Ïóý}ŸGGËÍÀ„ýA{˜Í3 ,[gw2@0Ä£ttl9 Ál;:´ææ€Âˆx€`jalnA$ t[8$‚ñ][½µ$2@ 9ƒÎœé¼ 0©Á ³˜¼C€Âbîk/¸€;È9a ÓE LˆÁüÁ@ˆ2ZÓãÄ€ò˜ÉùJ…." ÐEDêˆD&ÌfEL0eä#½@DÉÿCÔÆâö|Ë…¼VñèO,=bEü‡‡ƒCø<8ÃLÃÞ˜ê ~‘æ 2!~ðFÖ‰GgA ;ø/ĵ‡ Ó â1‚€:‹ ®ã ›¹QbÛº#WšË!Oýõi®SntˆÍ;òߢk¹ë1áñ† _¼!O@‘óõvlC+*›3!6²$S€ÎáÐ#PÈ^  8M 6 ÑkdȆyȱ$ €9¨µaš#0”Œ×sÀfƒÄf€Q$È×?ÓÆœ6 ’" iL 2 õ?‰ >‡²y넘õ5€kAP2P¯z`†¥ðdFM¼8šZÔY&‡“¶ ¼{Ù¥ªþ¹Dá|oŠ4«´ùP(n°Ògéf¶ªÒ˜Ü6|Y“›tïÌá6û©˜ÐË™/VÇÂüƲ#Öô™ÍvŽ Ñþx~B¹®¦{þ=IÚõÙ`‹8VüðÚô£‰7]»#¿l~­%ÕàVÝ#4%ûØg³<â*wk¹£3Cʼn& áèôÔMç£O&l©ÕïŸïñ·.mÉO[=ºPÛ\—·Ìjœ/'ΚLø­F8]©¸õ›»Mrá·ÉÝ+Ü‘´£ÝJJ·Cê#©;55ž8”RRZûEt“!+ø¦9~/åâÕA¯ðpÕâŽò3‰±f6±+=hÜ¿'W™Þ'—0Ø=űêâæ—x¯mÐ[Aeüì÷º³åÂ)Ìþ·*¹Ö{B Ê–Ž ”nW5/í÷DJñÀ›ûTÕ}¿:r2K÷½“~/,Ó^‘:ÎFKÛœ º|E#ªku‚÷æ“Ï öñ´Ò0Çoªúe®5òö7¯RÞYÄ%:»&–»}E&ô Í2ÀÞ‘<]Û~JfåzIذܲ†Q«¯k 7 ªkä‡[÷å?¢NÒ·… ¼dO‰ZIU/àÖð¢ô€ý)’ޤQ ÖD·ÝG=sB|Ð/^½Øª²ÍSâØØ"Ê’W3j*XŸ¹þ!OèéT0¾0\aÒ~Í=ýaAI#‘©ÙÆ5Mn™?nk<"Ê}5JK ¦æëË\ðÛ™×úÝ„”V´P%ó‹(Ý’=÷ÝDÔº˜V`ž9b,ß.Ÿí˜9Kí³––*:­+orî<æb’Š7ÊÁwy›q%¶?@c“Þ¸å„^ÖÕ÷>ß_šwýé·ÎÄz)³äÓí†qêeÞC^|I‚äΡ–À]ØF‹‹§üÚóÏÆ¤¾.BŽJ¤KÂMK¼hvLëùa·R\éý¥«÷.¤tOY¨©sQ¥ähÂolV2ÒZâí·§ ©4é0ÎEKžWï=à#kÌÁåXiëj43]±Çß¡§.ýëöæ‰FбSj(Ééi(–YÄuHöפfLýEùU­–£Æulíçƒþ8òK«¿%°–¶ûⶪéJ윫§‚H®é ÑG šÌb̫ۥu0WSW›º#{s³òÈy[Ž \íri˜îvރ̻=´ïnÝ…Ç~¤˜«Á_È:¬à潯-f¸3d£øˆ¥öq|Ÿõ}ÌY÷sBWœ¿WøãÔbÞN¯Ý»þúúZxù--ªõžñ¥!³ùG{3Ü“¿Éàõ~k³Kµ#N¹Då§´š¶ÚfáЮ¼÷Sâ²ZïÑ(ôéŽÀÃùÍN»~è„g¤Šl[cºœë…õõò%i»_Ú­ˆ ~ß•œ‘ð^¤d±¦A/í(=ÈfakÎ@|Mõ‚§d¡Xc¤ Eš#ŒÉx{ì¿9¿g¸9õ©Yµ/fJÜ:·£ŠÒauP ûþûš3+2WÍioh#¦tz®ú˸r¹þ^±ÓõU“%?SœõdÇn÷’RŸ ü™Ç|ªGëÞÿ¯ls¥ endstream endobj 402 0 obj << /Type /FontDescriptor /FontName /OYDNKU+CMR7 /Flags 4 /FontBBox [-27 -250 1122 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 79 /XHeight 431 /CharSet (/equal/one/zero) /FontFile 401 0 R >> endobj 403 0 obj << /Length1 851 /Length2 2120 /Length3 0 /Length 2705 /Filter /FlateDecode >> stream xÚ­Uy8”{¶ÇTe ¯d)Ì‚1‘%Y²¯Ä0ï0ΘaŒa$G„‰cK˜pDÈÄ9I²¥ìRv’‰s¤lÉžìßTß9¯óïw½ÿüžû¹ïçw_÷ó»®W^ÖÆ^ÕKòMHDŠ*ŠÐŒ,í´‘—7"ƒ žD4ÆP@mF#ƒ`@  4µÕÑÚ,`D  ‘ñ>¾@ÉèÄg 0ðÉxo °ÄP|AÖ o °'yãA €ÝgE`d*ˆ…B‹÷¦^ ž}öcFÄ‘ÔWðW‹ ’ƒX¦%–ÉË"–D$Ð,ˆƒÀ¬H¬»@–“ÿ‡©ï‡›VÿÏãYý«‹ñÇhÿí“ü‚) °$aA2ñ{ª3øÕš%ˆÅûß5£`xo¢à_!| >ÄÚà)Þ¾C¿à û½ Vl_,À\Î8™Û*Ùæ—– O¤8Ðþú™û¥F|«YÙñ¡€+ ‡#XDÖ÷×Éý»«Î½IX<‘õš†LÆÐ ¬wÁªÀE€'bÁP eù…A‰$ K°"¹àHdÈçej"ÌZ þ‚ ÕŽLþ@ÀÕ˜Ï?J-ö®`$"øMÎHñ%ƒÿ@à,$„ôMÁÒ‡ä¯À¿s34$…^TU×TÕXJPHø¥ÿ!z“É ‘òåI²Òÿ«ÆáY»ÁPÐ22DòÖ¹âw£*¶$âLAO)÷IvCŸ‡ÉV÷ûë÷G¿ü…PÔixrìw—Í»YÂüï¸ßÉ„lKÅ×…Û>7Y¸˜œ9¸ûŽêù.+쑤Ër–%ímÀ±õè9ÚªÕ÷Hvë¾±g%iço·Ð[çòm”Œ¦ö1eÙž¸R+Ÿd_ÑD¹˜dãb~WP—µÌ ,¹ª÷:D0-…ëuwt„_oµòè©.Xh³€7/u×mu¦ï´ë‘œAMwâ‹Áaß!‚/&!ÑTYC&±àzö£šíMFL¿G½J!9“8p*'£y¦Z.e°óO‰ðJz4Š9ó,?DeòX­9šáã šÞ™eÃG¿—*Õ“¹õ"8v¨"”8èÜöv¸T¯$¢¦?C¼÷q·¹?–g0? qåßz¬Îl€r;e„©höøµ=`R¸Ã…»Ëî²÷¡™‡½}Á.IJ=<_T!h/íf|û™ÅÏ%$<3öž´I‹¿0+òZ]`HG¯KÈ´|®å©¦ß³´¹™ñ´¬Ô°6 ¶CySÿ¼imœ«4ÉýÀŠ'Ôü“x?»m¸ÿ±XU=pªíù¬b¡ [ÂkÜEÅ­ (ïxy=ýÕUµAüšÑ\„,Ò šÕR‡Ý=4k ê—T—rë )Ç-~Pdøz \‰8j-8ëHæhÁŸì£üI w®…H†JzH$.I«›é9çÜw·è‘9µfw֣܅ûA>“ÖÆ±/³8 ‚ëÈà]¨#wð,;ï–IïØ1ÿÐ`Ï»«s±wšì;šêcd}•{vSÍÙÐ@¾Æ§6ü2dlR²2y®,´#t¬»W¶q‡^øÆúNƘf:á‰H”Õj0^Nôò-ŠZ/GJ<Ú0<•Ê“i¿|R䨅×áÆ$mkün!Éàö\‰ Öý§vŽ˜åH½º‰Ô I "Î>j w¦jU:¦1|?œ™ö™OŽ© L®„wµâ%ãÞ8zèžë¶ùC±Ç*%)šyžüN¥âê®&­ÿîFóÈWyš®LÔʃti é%C÷噚dÛ}±öœŠ˜Ìì°rä¦3׋ü{E¹êÉ‚:w[¶/ó¯lÙ:m½ù© Ù:™h¹1þœ]óùrëB5‚CäŠ;[çÄý„ø¥‡!q•[™Õ‚i£Õ©$Ë0W§4þ—¯”-„ûú;8×¶™Kn†å¸¬¹D}#£‰‡ï•g·o¡Ïî¶©_5,ïÅpî}Øb“Eiû£o¤cSÙÍ]­ûòöÞ·?¥Ž*COHôÜlSKéÚ¸)²‡éÛ\ û*ÃÜ´õĦ®\Ÿ¸î§?oŠ,îy0h-77“N*Q®6m3zrúh½é[¿:ÔÃU¤õfq¦íUÀÐßû‰QŽ4‚·öòG˜…»gNS;V=É¡x¹XLóUÏôômÁýü%Šk±rÎ’¸–ù²¤UNÆI†@Öíµ8‚Äe:¾KW}ð¶î=¨.¡G»æzÝ<¥ž èd"©þá–ͳ% m_ÕQ«•?éU´le:!âöМ!T²ðøWV?36‹[—¦ØÈÔ^¥y7— j'["^Ö%$ß~é6lÌ ÝÆJkÏäø«£¹;8#»þfXý!+_ÿ39úôÂÙ š¦³Ë•³É‡ÇÙ:ýØbÑâO˜É´æòˆ%JðÐ07ßx¶ÔbÞºÌE¨°Ëfò@¤{MÞnŽeiZŒk½hGz9NÌWvÙº®%&Q;̳íòŒzùޝ òCÈ ³‹­©ÞÎoˆ•Œ.;5Ã\ŽÙä®±DCD‹í%}&/È{Zèè€ìÞ7¸²6m¶héðkç°èÚ£Ær´Œä–É8@¯[‡íJ*ºävblžUˆðCu¤h²CCŒ¬m²“”©1JÅi·n<ÒÁ÷F]ÖL½4!ÀÆËTNk_ò’ʨˆÊ+žÈøÌ‚<C1Œ¬/.êõ‰·¬{D‚Ocš­ªËe¸~·œ{9™¯€Þ‹> ¾]zAÅ&{§Ð%]7íF™jÞLÓPˆÊ—Ô–IdÅ–w`Rdg˜‡’X‚^œƒË&@I¡pàãX,Žmv쇦õICÓ×yÆøaš¿JŒ©!rÛ˜}|$Ž9ùÃåÊkýò̰–él›§*ÏM}sÒœ-+"h‰_|WŒC©A¯·9“ U¸Ûª 2ªà‰«÷€¦l·?ý±Ã*¸Ç/£jå µëÖÛjœpø¹(> endobj 405 0 obj << /Length1 1323 /Length2 7084 /Length3 0 /Length 7859 /Filter /FlateDecode >> stream xÚ­–eX›[³°q(î îî^ÜŠwMÁÝ¡X¡H)^Š”RÜ‹»»ww÷RüËÞï÷îöìó÷\ùóÜkfͺŸÉš\¡£R×d“:Z€äÜØ¸Ø¹„Ò*B.vNT::i¹ÄÑAÆÜ $ àâHºƒÜœ.~a!a>!T:€´£“· lí`”fú+I irXš;TÌݬAöЖævMGKÈÍ› igÐøk‡+@ä rñÙQ¹¸@ˆ¥À†8 rüå£è`åøÏ2ÐÝé¿!‹+T À•d@ŽvÞ È •CÕzjò!õïârîvvªæö•‡öèEÍí!vÞÿ?îhïäîr¨8A.ÿNÕýGM„¸Ûÿ;ªèfn±”tÛœÿY‚¸ÊA¼@@uˆ›¥5ÀÊÜÎô÷:Èøo hÛþVàPRÐ’Ô‘fùûÛü;¤nqpÓòvú§è_¹3×o†öÆâ0ädçää‚&B?ÿ}2þ×Q²–Ž@ˆô:ðñÌ]\̽Q¡÷J|_.Äò€¼ ¾ìŽnÐ-hKüVŽ.¨}™ü|É¿–þCü©ß$àþM‚™H€ À!ÿ›xŠ¿ ºOåäp¨ÿ&h¦æoâphý&¨‹öo‚ºèüCЛÃaþ› .¿IÀaùñBkZ:ÚÛÿÎæâ„*ÿ@¨;èäpXýÆ¿òG* þ¡Õ­Ÿ5¶öv²9ü‘]ûs?TÜö„šÛýPuûßÈý£t9ÿ@¨šÓﳡýq‚Î’ãoÆ•uþy ÅÝ¡ð÷=ÿ}—?*ìúBëþ™ õwÿ¡þ ôÏ?zöþÿ÷Xµ‰•¡ÂDýœžŒ³ÞÃ,uF­]ä%*˜CꎴP~=¹T;íðw•ôK ¡vÑȣ³-Ç¡ÍÜ“1ÎÙ1 ým1é»â©Q¸ïa&í˜ÁèyçHÕ+©ÔjƦþg'–Uæ/Ÿì0k ÓòõÛìŒ?’²Ù¬|räu22êÜÏ]TÓw·Þö3l-|Ô©©À-Ô)WkTý[ãrëw~~Tµ­­úUûàËÈåªñڱШ(íÜòåºþ|ÃIqÎKа¹—¥û´7¬{^¯U¿”^öýô®L›ÕAþæ)<ÄîëÎqu„)2¢ó‡€êØ]K§òV¿Ay0`þÅÖ•ú«±ÂÃõGËᓽéRÃó9üÎâù$Â6›o"E`Ì8„®¿¼V@&n–Q Qöuß7~U“‹¾ ˜V¸˜wŸB¢yñt‘já>ê õzÞK”0´NÂË”u»* œ®\"% ÎS­>f#¼+ ‰L]<úô"h3§á’ît,wO\ŒÍÜ[Û(;¼â{pËtÝQ“ƒó ÁJSö6Ž;#Æjgûl˜)8ì¼–D?:r¼ó2j\FV³Ñç"F"¹?•M é‘]o7”o5õ;џ礄ÑÚ–?èmÑËs|¢>Xƒ N Ω«;‡5²à,ËÆ ³¹$€Ä&KÝÒ_)'&ø1ÕiÁ<á>ê0¥ï0#Ïk‡ûÝ3fú„Š6·KJß2}'&Õíj¾%–ãœë<„_ê;¿Àá@c!Ý[E;w‘‡EóW°za-suAÜ6ãþtŠrŒ¨ÍFdB4âª*›n1 ö¯Eº:öÝâÌ!æÅµgåÔM}þ*/ܬ)=}«-ïú#Gþ$Qåg‰%Ø®O#f~ï?H.ûFÂ\‘€Ñ'¡™?GDñøÀÝ÷¨êbˆ·5œŒˆÿ¤à“Û×ïÜì,‹’²üó¬Eó,­3‚PªðYéþûÇ#Ílê[$¢&Ø.TCi:d…ŸÈ¢Áè±ÔûmÎJÓ7TÒx.Ž?ß|È_³ì»ùZG-Ùš¡27Í{ðºá]ÊJÿaè¦ímøšV†úiUCÍ`†÷>÷“öõÑIJ‰G¬azx]ÖÊŒk?ÓáÆË¾Ÿ:!„'¹Zûôz õ»¸B˜Î--.Óï°ºÐCܽ¸Ë¤7}ý«Œƒ%„>ëùÞjû²Ç‰"85ا•ó5c¶›*+~¸yS$¾ÔNú¤ u´Ðözj=Fµ;¡=¨ïí›Õc^ÚÝêa)‰m¶Ô³©æoÓC´—= ªn-Ä>­R¢!x…mÒ“ __Ó'õxÝø—löPÊ‹üχlÆ]¹š9ç’ZüãN(®JºÝÆI ÚhÚ²aŠó¢ë—¦VQ£Sê— 3drÂeÕõbYXÚ/-…—Cl÷œ÷5ïôFƒ¶ê·RÓ†!ø…áD¡ƒ2{l%–=7ÀÏçVñrs¹•¿bÒI@sLtLs×_ëŒâ/Ý›‹|n“_ZM¥<ŒG¹2¾c2="¹Õ‘dÖ.ê NÎUÌê„Üažm2Ð ǯ>ÛQä LɶÛßey»kúÙ¹ôF搜.Âø!Ú+>I}ØžmGŒîd d¨µxk€rF?…ZüÐëNsº©fíaŸ’TÙ½½ÄÔÍÓRC¼¬ý– ¨ ʼÜ}´â·'›·dã´JØ'û¥…W-&= ˉÀ“)!¨Œ›+%w^1à ;¦hÃbc)X"zÜ$ws…¯•£Ó¤Z1ÏyHàñ>\ä(xXÌTl¨û:©ß.HØm‰NÞDŸab'Hqw ‡íÏ`TÙÝÀ_¼Ý{î‡}ñó©wc ÈZü…°4Îã‚-¸Ç¬ôè`³ÌÄ(Oþ•Ê'2©ÈTQxEÝ&fU6‡7f¤ð“ ‘÷ëÄGÛoÓÄóÒkDòSæ0¿­d¼üVŒo&”ör*w°ê‘#Ôɬž>GNVàF{EÙ|p Cw™MsÞ7~"]"ƽÄf7bÕBíí’WQß,Ø/=á*͘æB¾K_gk?á S¾½B¥ÛpH®n¦‚1ië·#°ÄºØâ¿¯oŒ¹FöÎ’SE:0ða ÖR{¬šøÏx^žwE‘m“ÑE}4t•–2ªëh””ôšÿ¢î÷ËíWÓl5Å+{«RÕxÂË$ÅkZY¼„T×ôCZž0J)^3I‘ç%ËÖ´5£ ûyHçÕ@Ó„” NÁËLãU•³1m[Æ0+bߤZ.¡ã¡³=µüù¡E… ´ l 4áÝ^“¬²Ä–à`zò––n›·†|-+gôÞÄ# §å*$ϸÕï,J¢‚–ï‘Ý~¶XÚè`¾†]‹öÇJ;ÎÞ8ÂrÊ6ÐÖS<¤)ó–^ÆOçÊ]M¬×þ*¥A씈éò4öâ(´w1ér•°tZ` 7#­+¾Û¥´ªcœrcă"-½'®s”ĵ&CÙŠã3dùÌJêñ±“‚.tþ¶Ž¯5~/²üV™½@ëS¯Ž щÇU>6ìäd1óÌ/9q#•ÇZé€m:ÓßJ[ÈãVD%0_Ä%+>ë[`¨ÓFk• T‰ÖëÖM`§é 'u2TåÚQë¿0±Tš›ðRù²­¯¤ÝšÔ†(ÿ“‘ìî9D²˜XÙóªq0Nëv5uÀg©@Ì“|­<Éî— ;¾”~ûL(èŒhù”íh°—CG ¶ü=üFM½mëà ó÷üOý:Û£·µw+‰­Jù‘o§ïÞïµI.xáïŒêjI-†(Z‹ QG¯h=ÖþÔßÞuòU³ªÕýdÖ¶\…-̪KóÒtºQ§!$¬¶Ds~Q§ŠÍ(ëVPÐ ;ñ9Ø£s[Laˆs!aA¡slK+×\ •q#è7ãî•røŠË’‹¯h¼äìÌ¢céÖ‹—¸¶Kór¾)QîÆk]fU“’îX¦{ ·³ä»“£˜—×6Õrë붦Jñ‰$?3cÿ’oÓÕ*‘ñŬ>çï\á<`–iê·­\/mdîšëÏŽY†ßz$õ ÖdÛÏZ­Ï²‰-1…}džÖ«§þùdÒ!˜Ûãà÷¾5lcÎ_7î‚¥äv/Í ~ÑÙL¤ÑÎÓ\y!„³õê²N•Éâ•çX&¾ÌnÅ/;ml&³ëPkˆæÃŒ‡¯`9oòiÂ_ZnÒyðXåÀ§¥‡;l›üáãxeâP ºìwùß,d­ñ¶"N²G;aû.’ti2¥Ì>sŠøú7Öàƒ Û†œ¢([ZÔþ0BhõVçí'†ûzLu£žÙ±(õzu6ØWQš½:muÆÀ+Å" +µ‚wÅà‘mzåÑQîÈD¢†m*Ð.„ôÞ9¤¤@J?F5ÔEL¸ŽÜµ"KÒ$ä´%<}`Û©ƒa²ø+®û-Ÿž·Ò[[9#\„(åá.+âêh„Fä™)T°|m1µEyžµ„º ÛÀS Ç'VCî~Ѭá~7Ïþì;ì Io9)ã¤ÈEË}NäÁK9 t…¬ØU|SJÚÍp=4 '1dùóýz7öÙÅýK0º“.ª“±N+€õArW5b,:ëØ ×¡*ªÅŸí´Ì<<u}ˆYEÀWã)<ˆ¥?ð5áxnbǨÇÐ@Ï¿ºÞÅ¢x¬°|äízv¯Ÿ¬;ÿ”"ðEulPå|¤dŠa¾‡.æùÓ2–ÎçsU¡â•Žëbýv-À”\SP•cÈÄôæO?#µ½ß‹ïÉRÖ{¼×Uê{‘ULh&e%`±ÅÝʲ%>+•—åË®úžò“AKlI˜SšÒÔ2¢uZ¡ü=é ¬“þÇL ¢¨ŸŸæa˜4ê/µ*ÏôÙ‘ oÝî÷„ñÕtèD<Æ(·æGÑöïnžéÁn×§ˆ Ø 5æž7yÕo.ÐâÕÆýàf4¿o´©í)s±ÚY+úªß:á–8„(ÇZ§":½ÈVœ¨nP«}B6?¨ dÛ:w©/\–>v1ž m“†`Ť&m\†àÒ²ÂpÒªv²Õ 2‰bU=‰èÃŽSlhfdv¸(ÚÍžôÄÔc_ßG #íkÆ"î쇺ßal›4¾.¼é¬ïÔ­ªcÅDE'à¶W¨5ÿæCªú+7Õ¥èc²åXôÂ{jÁ¦ U—0 4¦†pÌ*'Ýœ7+ìeß&xj?"ßFõIß~‹Õ¶Ißu¬öž2÷¦@óÝBNBÿx3H|Á0\PÍ]•"ŸO—ûÑL¤šØA|û^™Yúh¥»)…eÒKïµ\y6Þ("Bú#«ã{}¼Ó%aQ2‰ï5t wËâ6 ªÞÚâV±×8†«v\=ã,\O…^ÁÒ,_P.ü(¯ Áµw,„²3E}ù8Ñ¿øZQÛd4Úòl´®Vý8%ã®*šå‰¨d{- 0ûÜ¿/{Ïž½êk¤Zuk$ •ÍGàæ’ÑÁ”"à/YÚ·E¡Þ–&4òA BMs¶m°À2g\ÎË2!èØ‘Õ‰ý¤ŽÀ³Í?fW ¾“¬òî6Oˆnt÷ŠBqZÇväéˤËE êhY«Ó÷&ñó[<ê?híÆî+ÔƒŽÀ¦ôŠ7³ðg)‚äÓðây‹ Ók¿Û’\»£9l]&%VÖ0–‘ˆm$Ûeî0˜ ˆEX`š&ê*;5銇b€Í’…b­U©Eh}±¿ð?Cþ欄$ì®ë¿Ï4МY׳±bèj—¯cbQîŒeÂm¢µ+±Ø|—-—CžïñtнŽv¥&rØwsцí9s8*²VÅ¥ÏÇ’¶|­’¸Ð+—)xPÆI ó²#§õ:`ŸY®FòùŠ­vjÄ“Ÿs·09%¯‰4Áµ¯uéG´]wi}÷²Põ|§ÉPÀœ|Tc„FÍ!l]R]ÈGÔÈø±õ êmÞXq bãy†Õ!D"ÐãéW–]?®«1rÇT^ÂY§N,_µ‘›þªƒg®çX¯â¦´ –.$R›º‚qqû—¦ç9Rd®Û[¥ÝIr‹êmËå®`º¾À4 öyfŒ¬V\ÞqÛ÷*+™å·¸p×F?(‹þzK¹†ãi;us‚»K”¼Ž G0Òu œ%ßn–ÒM0xôŸ¬Ù\àz6«9—ÐÑŒ^)&^ØHn­]äïI0wFdôù~KTìCúðë¡éqX2=ý탗žçNìº Ì3ê A!È'¹É\p}è‹2æ®§fÞ5YZ/?*2‡\Uk,ÑÎŽ¨5›o´/“z;<ê?€¯×c™üT9¾7äe}P-)š”Œ~OIÇœe{Z@-.BF¨Røz»Z¦6 銧>“Ñhœb#ÌÎûŠw5m¯ÏÜÚÃÕãªvüÈ̼+êºùè™d.ÿ–—×jÒÃßåÊïÒ Ô?·wÁÍ2 }©Âtñ±`ªaˆ4]ƒýö]¼u¼ýh[VÔ¨óH憸¹àÖ¬`Y•w-Wµ^Á;Ÿ'ïDj‡)åU_gŽëv+YN‚É<Ì7ì×ò`y‹ôÉwnȸìù,Ï uÔ'Ó˜yJ¢êîäÅÇä_gÌ×ÓÔÕ= ¡øàÙfÔ'nJ‚Ó^ЈÇàƒ¦à)œC£ý2KüíBÏn©ò!›Žó[$Eù,l̬²Üq‹qfàFâð,/G[¢ÉbB 0ªß9c5ÿ¢zÎç øNµÅxEY´0iBT†HĈ«[»]=æBí,Z4Ö3IšWã×Å?ê÷ðAlU]¬!¤…-‹0ÓùhÊžY.%Óƒ0« ñÔ>qí‰í|›ã“bÉ[ýðÔéå?‡št „f|b‘Úv»g!Zƒ®9¬ëÊ% Љc¾n÷cÑPÁ,BÕ~ä”X`²]@;Õö VºõµÃžNœ¯gÔîpù7~ÒG:kƒªyŸÉñr§?<ùYÆ"‡õ°Qeeùc®_Ñ(E\1:" :ðD¦çSZÑdÓMlúŒ­LGH.ÜŒø)Zª„ˆH`Mͨ4îÅ‚ê—_ìh‘ÆM >Ÿ1¿,™)üì{¹fØn$)lßlz_µƒxi‘¶†¾ñ9à¶B'æèTñ>Š×®«×ÔtÈ÷ Ú>8HWòñ¢oµ¢ø)DY¯G½]í?Yô­8 !%?(Ã1‚9¹3¦ck0g72½È#‹Ñ6 H+X•+¸ƒÝ5"1{–€’ô„m„L1ê!´fÝheæê ªq{¡‘?QÎøIöùLÊ~*›Ùêh†¬;˜ëŽ…21Ö¥J*^ÐcW çBÿ,:? ªÈÆùJno¾™Õa§~$k7#}¿X§ª¢Cõ¦7­@AÀζ’{#‘z7ÁW:ß?GÞƒÑ;éS—tFŒÕ¤-½4>žV&Xïë?ÔÑY‚ÚRc4¢pV÷Ú’óè· ‡ƒ‡ÓoaÄd»—å}Nø}Ç’&Xøî6ÿ£"ù¤ækj¯ý¨NJW'”ðau€‹M}Œy3Ÿ¹—×,pU+:ø¡¿U9Ì ï0œâøâ"rT~?ó…kŒÍÅ‘z?BK·…Å÷ÓjÔ7=åjD}–ɢƽoQD…oÃ{qëUÒSȇBs!¯Ó.{à|%ߺ]Vnú޽è;ØâݸêÈ=þÅSüÈQw7Aä J2K4šŒùU:£¯(ùáóðtÿIÂ&´Ç´·ì{EµOŠPzY#ª³@>_X¬Õš¯ Ööá–ãd@¦ãšÙü`%ÀèÀƒ#%§`¤9üéf‘ø¤t\“ÌŽ¼&±¸PPIÁB-rº@?5õ)nဠ &e!êÂv\ItjH–¨«8IdnµÛ~'m€à§t{Ûkøã3RùöÝ>‚>Q ãÊó>Ó”w0I†ü;Õ¯sB/Õ– Ñ>SSlõÞc6O*­¿çÛð\QmRF+Ð3–`z)æûñËG7K"MÚ¶‰ãiÞ¢Ñn£7ioói6=ÛLpPS²cêÊÉœ¢˜–0–j=Ø:¶L@GU£¥gqUßgpMR›|Xýá1)7¯ÚJ$i‹´ƒ>c'õYsÛXì¹Û½°+$RökÝÝ«õEŠäbÂÜ[±è·@•,CÅ;_ì®C2¿TäâÉõÕ›W'«Ùm/¾¨N¿°„“àyÒÞ¤·Ý1ÃiUµ—ΑZ°T|s~~I@SǺÚRw¨¹fiÕâ°³r'Ëåqânõ×ò3ÌÈûËTùíœhbÄzĤø¨Kù³¼;ÄH`Fÿ@“(¿ÁňÀ¡cšÒl]"ÒÒ™°Ûÿ™eÐ!\ð+…g°Ç:Ëœ”KÁ²åë"÷jra5°bet÷å”D®Q)¤Þ(m¨ÊEïÚkÖ¡kø¬åÛg§FA¼_*ÝGì¢ñb¾.õ)CGØÄh5$YçQ¤©Î‹!k+…æïÃq¿7ÖÍ­auz|0äŸUC*#Ö[*'e˜4’‘š%È[SÄhQÏkQAé¡wr¯Ûä}êÇØ+›CVíå?Ö·Òt9çJˆE“òƒ öäàÿ€q%9ÐÁº‘ýZ]þ:g¹Òðÿô”oó endstream endobj 406 0 obj << /Type /FontDescriptor /FontName /JHTAVC+CMR9 /Flags 4 /FontBBox [-39 -250 1036 750] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle 0 /StemV 74 /XHeight 431 /CharSet (/A/B/C/D/G/I/M/P/S/T/U/V/a/b/c/comma/d/e/f/fi/g/h/hyphen/i/k/l/m/n/o/p/period/q/quoteright/r/s/t/u/v/w/y) /FontFile 405 0 R >> endobj 407 0 obj << /Length1 1130 /Length2 2886 /Length3 0 /Length 3595 /Filter /FlateDecode >> stream xÚ­SyÞ Š0UTþÀM0 Úãƒòå¦yEçh·l×(«VMYÃÍ–-©W™£éébÍ-‡Œ÷-ÒÕÛªjLv‹?jO úÇ>á“‹qIÁËŠmײ¦í”?«ýR³f»Ÿ¬ÞYÝÉwÖ]‰%Â(ç26œ¥QĬ²œ{õrµr\€ ‡&FSz©x¿EoBÅœ‹eË£K,7§PÆÜò£ù+_ð›;­šÊ–Znó>°›Ô6qqf-…C´QZNqJG@UoU§Ûø}¼ªÿ iþžçÞËÓÁ;ƽw-écò¤/4‹L‘<< ó8ºiù T–ö¾ŠKu­"V¡tH*6*}.¼1Ò7ž!ç´ŸoSÐ)Þ7{MùÒÈÒrI‘Ä ~Ok€žyB§îfi±D‰(¹þàþªÌøˆ>Öèƒêð*›@O?\{àyÜí‚馩>ºâa)SÀž#|˜k¦Uã+?2wÈ-UÏyBÄ>â*¤Rj7k?*¤I—±X•]òžYBöæÌœkiØót‘<ÛîQ—+Ú˜7 áåWZ‡„¸uNy d;Ö$Žž€…«½„ë¿Øe˜p©.qÊ–ƒš¨{X]çÙhÖ±âu÷2†_€•Aíú5;çµaä>KÂF×ÙÉáç1ø UÔX¯ôÎõƒ~•”N×àåÞÚv1]5ÈÙÉ‹>ªe¶¡ˆ‰Ï½þ“{Zhk†‘Å]ùYãqÏ Ì&BŠ[ ð^Z>$±ãÈm•Eµh‰üyÊx醠ôºÊën°aö\„cõZ}Fõ’änvþ׸‘ím/ŽvéQ‡ß”™³øØ°L†|Bv]ÙÛÃoËV£ûI`by!¨j«»Ü"‡\ž²ž[‘X“°=´Ó;k¸¡#ñVbξÄ4½lÝ%?dhö^N|ß(Ñ`œñlÔ^ÿdë+”d˜aþn«ÖƄʑ¦œ+Ô®ÝìQ—c³ Òv¯èŒ¦zk~ê™»ÚÕ)?œ ôÜT©%ŽÜG3äv:0ßl›ã~…ýiËý ½d)Í iÕƒlj‰6»DÔ&|J> 9”׭„STí¶¦4ˆ¨ýÝ6Ô[7’ N¸ÚëÊ ÍÔJ#íšDþàºò&短ØQ7´BWüYjò£ã×B’ø´9+"å·g”ìâ‹.±zçÛ×c#Rnꢦýö–m€sËëˆÄqK­K¯ˆ…c(Yœs¸Æ··ÍuOïvr¿]㻯ñER"ãƒ7•3’Úx78S¥’­p˜ëÃý|JSCÙyÚ§4ëåíŽã!e'ž¶Êì’,GvVV;JË,õTN»Óm˜#sá€Û6áý”À?yµGÛú×›«¦÷Ú8ú¹á{ªj,/V,©?ôy–äTËÁzYeÜš†tÁÔŠzØÈùÓÛ’òb©œ5¼ˆM¶ûÝ'ÚÄ./»´8>cð‡åCQ‡’¤O!)®/+çþ)¤æ¼«â”øXÝå>aJ×?IÚ©eU­“}GÂn²=ÀÄ^—ÄQÂÂ&ëá¯lgÂN*ËMmßó§/µvމF¥¬{ÊŠMÍù”k:ÂãÀú‚|EàŒì þÑ“ïå¸b¸7…œrh×÷Ù;0Å2y&ý²ØÍ å*1T¿\ù-¾¿ÉPO¯‘Á“{?ÞQÉ\36zp2[IÅ·£Ò"OT²btKá|‡P°˜[Ί{=êôsûíR¶Ê€<¹ýÔœ j9-xç$ãcLGt…³ L÷«æEžæ›° N=†µ|•½ÍÈ”™~&±M'æˆMQÑñ‚h¹`É­ ½õ[ ï ,j¡ŽVñn=r·“Ë$‰øÎÂ.—H$-Éë<®£°ý&‰Ò}=‹£ˆ¢~CëHSjdOkEuf³ÆváåÛK²)÷x„’ŽÿƒÓ«È¼øt±½;'ö"èU•Îèêè{¶+¼ˆcD4g·]w>œ¿z…­:>‘ØÕ r©Pªf6<Äy“/è¨f=6Y¦áì²,%NŽD_-oÂÞö—u8kN«Ûܦ;xq¤¿ûhTÞí6ëØwæýtû$ˆô>ÛMáq\óàF‘î' Ý7ù“kÝp½êœ¼+£à6–· îܶv¬žv@Jï‰àá[y[³C¦ï²é&èÒ‚z݆Äáøü¸¦ ®§3.EW¢ñÒYKØVõ3GxÑá‚8í€gB'w%g-ÃóÞ0:¾9I‹‚ÈK¸?~R¾ˆ—/øâ !éyÅÏâ‡Hïñèþz¾Wi >zuŸW9ÁN#–¿6¼¿CñJ½YVµ×%½[nå5ļùqh]ŸìêÛÑÅcù’ìæjÓËÍ^aâo'´ûöm BVUQ¸¢Œ3&®&%õ–‹o½gü‡»Ç»Ÿ'%¿œYþëÑñÝ‹&Ñb}÷žæ屪žµñ835¢›Ù~v_ –hØõŽú{¯}⚇nX7=Ñ“¥¥KÒ©S׿ò“*Í¢To£ÅÚX] ÜO?gÿ9ç„MDX¨° ¶·²Ý€öÊ*î'vè’û„CÛ¥ùȵ¯>:i³óÖÁ ’#wévž[\‘-‹8°ôt¿á«k`›yö°”BÓ¾VÀ®ø”ÚÅ󾩓na»°zf~#énnÖß:¤Ÿ_Ïä~(ò&ˆ5±‘ëæ‰mm¸ VSIä)™Ï}¼Ýßc«EpŒo™ÈXupò]ð+¾×òjñéó¹ÄiºlT˜Ã‹¾,Ù¥Û¨šõ!ÇÂ×í!Cñk¬M£þ¥\ÂO§gßsFFzû Þ ,êÃÉÉç»ö Ì7‡˜½¦UÖŠC¥i(xj†»tŽñÄW»Ñ|×Wí‚“¯údàºýue {„W©DqC:¯<öã¶ÐOT‚Mú±|ÃèAûÎyÌ;¤ =¿ý>«EÊJ§3*#a<ºÆøÈ–Bùæûy_ †XöÊ›Å&ϪL\{ÑAEv(©k㢟øÂâæ$}àEÚÖ2´×h%ŒSìX¨´¤ +™Þ(©wkŸ’z}‘}¬†À˜3:–‡låûÒYæôõ»Ç Iw¦ó4Ò¥Ü" &3VÒÒò#o–U*ød‰O°†sV#Ñ-¡Ùmà уU*UºÀŸE¦iþ'&” endstream endobj 408 0 obj << /Type /FontDescriptor /FontName /RLMMGD+CMSY10 /Flags 4 /FontBBox [-29 -960 1116 775] /Ascent 750 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 85 /XHeight 431 /CharSet (/arrowdblright/arrowright/bar/braceleft/braceright/ceilingleft/ceilingright/element/floorleft/floorright/greaterequal/infinity/lessequal/lessmuch/minus/multiply/owner/periodcentered) /FontFile 407 0 R >> endobj 409 0 obj << /Length1 815 /Length2 1535 /Length3 0 /Length 2104 /Filter /FlateDecode >> stream xÚ­Ry<”knAéSÊX’×aŽ¥f7ÖÈ’5K¶ì4æ}›yï˜1dGe‹±UÒ¨1ŠNN IÊQ'Šs*QÒrJ8”Ò2 ßÐׯïëüûýžžûº®ç¾¯ßu?íÞX9°.–ˆ#švnÞÆG@a0vˆÊ…YÈ6*2ˆffDÀ&–)ÁÄœD4'¡0€+šÇé . og° 2l¢ L£"€•Ë€¢¤=hT&àÍ¢Á—‡l˜LÀkáE àÅ@œ8Ä¡ˆD„i\ ¢Ã ¿`ÈÙÍL¾Â`lô7*âÄHMúR“€Ô"ÈB˜<„v£ðî,é,HêäÿaêÇæ±L¦;5j¡ýBHÿ ©Q0“÷+*:– q7q¥~ÐWonÇFýÈ:s©L˜fƒÐ™€%ádÊWŽq€ã!pÌ¥1€ÝTf ´ˆCø£iz‹Fð®ö~6›¾nu‘ÛA…®/ßÅ‹5ñ{-͈ÇA@” ¥çÛ-ä‡Yö QŒ*‡Cå¡ÒV$ H 0Bñ/5ŒÇ!,®ô M&ØÍâ –JðÔiRpL¤t'Œr'’ˆ¤ÒéÒ¿c¤oÎü/˜àc ÚÂW]ÿ†­-+>k`ÍŒL"™d˜˜'þŽËá@wñ·IýV[€ xˆ†¸Ï¢Y¤E¾˜^•d_Þ{F//¹Q=:°¢ÕÿRÒ1Lô¦=µé\ñØÉgõ‡ÚÑ‘}ª.4‹"eNZŽ{vè´Hùl}æ‰SþyÉ”Üo4/5³Xqn“Þ˜s;{ç‘Rþ3 óÎó’ ÍÓ:¿ÜX%_Ù–7||_a­¡W¾áOê&•+†–ª yC#÷¾hh˧ ƒ¼†v…$TøuSî;‡4R÷(]^eö0Ç6B×ÇjÚKˆ-`ê×sS‡òì¯éâ]>»:?¼L°§<<Ö£ª5å)óRwµ¶»¦e÷:ˆ$¨²ñ·ÒîÌÊ_£ä¯°¼à×™Lq_¸%šÑ:¬Iô©ÑKx¥ÍÞ4RK戕BsÉÑ’ý—æ„[Þ=Y’]†-ðн'h'8%£KÿþÛË¿#i;É;·0³F.FrÏù&»&ÅÒþβÊõ€ðå…ËA*6_j­ƒ’ZTºOÐH¾ãá{ÛÚO‰ÌÏÛGœÜÄ ÚY™Éî¨õ™vø$£R`Õç¨æ[Ù’vt5>iÕÝYßõ!f±»Š«ëŸò'X’¢€g´ͼc¦^óAÿƽ¶ ­íÄÂw3ncejÏχɽîCüÕwÓãIpÌ…µ— ÓJt>=–;#»¬*ôu®ø—wÈU³M´åH×£°ð+æ¶œ~«Z—º ð£r0AETÔ‘§7Š"´åÏ|àšÏ&*]™ZÏ;÷$sKÏ¡Ÿú÷ îcëÖNúÖ£t—‹Ä¢¸4¡QñtefÞ~M§meíÛjÂüFÅÖMo,töÒ¥«)Û"´áì°oÎ_m] Q¹Y Gõ×Zùr…!%¿\å]¦LX¶n0Î6+‰UkN™º:œÇ‡eA ©¾Øù¹™_}üÔ4[Å3½Ž–‡WŸ™ËEíÜhOpî¸ø=ìd¸'„øf*?¹9ééà´;qL!øóÛ©º6E¹ž»ã‡pV^Ú¸£ÝÆßnÞé8Æ¿n:áyn©5?Ÿ~Íy¦Åë” ùšóÛè•å]t]«ÒWKF¼KaÍPáL­n1µzÛýúªhÆ9'´Á'o÷¥Ì99Ú͵žk£ßû4×µµpo¹zLFÔ§j’ZlBùÑ‘éöâ |­©‚ßc m² }½?iï¨îo¯"àîPÇÀ×]¼Ï…™åÊËLã–lèLÏ}!ÞjùŵmÇIþD÷u­uÖ‚5QÂÕþcšïN6.é½t³tÙopÇÔ®›%¦+;ÄNªÀëÁ“ñ¬’ŸSSóu‡-ý.ÐCó²Ë^‰‚s<$Á£ÉCóû¦F Jް5Òö¹­k9S:µ¤‰q僵¼·Ὣê–^ºÂÑÛñA„€áž—µ² §ÔÏêJ\ìηÀ ï•Wf´|šS»Ee®$¯êçÈ &º›Êl؈ÇêØ²*&"ãè¨& ,„“w©§a½‰Ç"g—="WÎ~~O¨‰›Ï·¬—‘²uz¤ùÏÉ SUØæemž˜QÑ@{ùxïƒç¢¯nìºà"’–}c£_Ya¬.—n·Oënû¬Š#Máåéç“§ד:°Ž\Ù"¼olQ×BªNë÷íÔsm:lÚz:±£·¥ÝsƒzÅ£ók•%ÙCp á9dÜÀ odΨ¨uQkìš(gl×êÙÂ*Š _nº­$¹·÷ÆZý…×Çü½º£­£ïjíE‘Á﷒ͲË6DË»žXš½²0=D½â†R–ÉÙ\ bÑMÙê®^ŸO˜]]·FäïúVå'-æšâ6 ú³Ë±†ãnÆO'+¢Ý¢‚ÒôÖäÝVS‰ÔÛküLÎÛ֜ԥÚ5qñnØñŒBvBQ¹N[ZÖï²âDÈX¿fÚí þðxå5þ¿ÀûN§vC²Ó§g:¹"ã §Èóò›â²îÑ´ÕK59.Æ&%¥ÛÏø'ß2û«›1jÍG‘ŠÚ €·Z'ªoç]Ã1Ë3K>4—¨éêZ?y-³Ÿž2óf*^ÜÄÈPÌ]!+þ7ÅÓü/ endstream endobj 410 0 obj << /Type /FontDescriptor /FontName /LEWZAZ+CMSY6 /Flags 4 /FontBBox [-4 -948 1329 786] /Ascent 750 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 93 /XHeight 431 /CharSet (/asteriskmath/dagger/daggerdbl/section) /FontFile 409 0 R >> endobj 411 0 obj << /Length1 763 /Length2 651 /Length3 0 /Length 1179 /Filter /FlateDecode >> stream xÚ­’LgÇU¶ j,™ÛôÖb¯½B-¶ŠB•f¡P¢¨ì¸{[n¶wõzE:†l¢ˆY‚.ÈŠ€ ²É$®òÃ"”-NqÁ*ll™1bâ]ËŒþ»Ü?÷|Ÿïû¼Ÿ÷û¾Â%­(†d2aCs" Å@¥Ö~$*A„B qŽbèu8[¹1VÀd@"WH1…LŠŠ1ÛXÊÅ0ÕR·IbL¥œjœË‚&~–!(ÈÙPc4‚÷ HÈfCE0 ÁLh hDìЧõ OɤÕü²• Y ÂxÈ¥€G$Úh$Ô#âD†ß ò$ÿÔôáqV£17¹Ç»Cz­›(£í_c2[9È5CB–žnݧØÔ¤¬¦éÝx7RD m0B Â"QI„lªAYâ¨Hj(ŽÈzÜhÒät>=ˆ8-yCjlbøÔ­zzœ¢¹T›É+³§Æ^Õ|F,•Ò%¨D‚ñFþ{ù·eÚ^ëi‚!)Ú¤²gY܆HøQR™ äb€¢I˜`,Fi†ã—>™< gXÄ}© 6Q´ÕâV=Bd›YЇw+¯Ÿ+6–ÉÉñoP´R†L*“y”4ï?FÂʲæó Ù8ó-ÝÞ¿W\uПVß«hŸÕ™ÿ Õž'(—„4w­µ˜E£wº^”× ¾ÑµýžrÈþ™½=ÌÞ—òóxòâÍEš|¯(}çñé ]+tí–¶£Õv¾~°*Ý{üPQ€·ÿ9{-²bÇpßähÂpmÄh}ä³à—Êõ® µí[nôîØõ[UÔáÂToò~å¥ GyV@GáÉýM£j4ð‹¬ì‚›EÆk{¼[{©ƒ×Kf¼wÚ¹z2Ü+cNáÇ"ŽØ­hØõ´E@Ý9ìI&.éó›nu\/úuve ›0f8¤+©?Õ0è/FW]¼¿ú€b²Nû(ýv–tݸê¬Mx¼gÍ¢MÄeCtp®ãöÜCrÇ)¯Ž0öJa²ÿŽëTFáXb±Ó)oÿ0xäBöóAaORñ¼'©Zåݺ7 L¬i¬:s^•& ½{4Màg÷ÛJä›#6Ø endstream endobj 412 0 obj << /Type /FontDescriptor /FontName /ZQLTBN+CMSY7 /Flags 4 /FontBBox [-15 -951 1252 782] /Ascent 750 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 93 /XHeight 431 /CharSet (/minus/prime) /FontFile 411 0 R >> endobj 413 0 obj << /Length1 851 /Length2 1690 /Length3 0 /Length 2273 /Filter /FlateDecode >> stream xÚ­Ry<”m Ù%I!Ñ-a2ÌÆØßìEL¨±5fžx2 ³ÄØË*”•5‘JQ’,)²&¥¤—JÚ!kÄ7ôõöûzÿý~Ï?Ïuι®ûÜçºÕUwt,( _ÈšAgë`QXc`¹ƒ°Ç`Q uuK&Dbà ú62X##,°àø,` Œq8c,_,\&ìçÏËÍ‹"`Aƒ˜0™D;HlˆÆŸA&QA†!6,¨Tà²ØÁ. b€(( ,P`2øB~0]½hÈŽ¾ ~ÂNà/êÄdñMßäfÀ·HaЩ\@öI ü³ ¾“ÿ‡©?‡[s¨TGmqübHÿ¢I4˜Êý¯€A ä°!&ØÁ @LúŸRwè§·æÐþdíØ$*L¶ ûQ! ƒÕCatñ? ˜e ‡@”0›ìö‘¨,h ‡è”?­ðÓ[2‚ÞfGØcé†ü¹Õ%n' ¦³wq!€ù-^ª±¿k~FL8x`P –/ä¿þ¼þ8ËŠNfP`ºÀáõ‰É$q%0üQ8<„aL§@! áF£è 6¿ð“‰ûL‰Å¥ê4‰ÅO fðwâ¿H.áX )$??~ˆ¿1Ü/ŒâKýÆ4 ¦sXÿz†È„ù÷ÿÝɱ òâk^ÿ×Ö­Œ0] Ð1â[Çb ñÀÀÀ(â„d“ ÑÙK/’Ÿú¯zÌß…@d‰Ïd“Øý§nÆ_Š´Êï(F‹Ï<,ùôB´vwEäi˜AÀç8«ú± óúË“ëå+Ø“MÒW3Ë9õ?¿úryRî…Ý©ÑøT7Až“”±b©9hWä–y&®_ɸ†ù6;QMûŸZÔC©ÊÇ­Äüꩾg’²Z.'´6®3xÁíX+ÏíýÐ5§¤*“ãáòêŽV¸’WrdR®‡Ì å3Þ骇ÚÓ,Ð=u¶xÿU½nûZÃÚ6DOô¾èÜx¹ôž®ÆEh?ÙÓ~Óú6\Ëi}œHE‘þQ¹à ;Ҫ׮v2‚WǘôVë­h¡èŽ4„m  ºÚlüÚV'”vs×(k®šÞ[Á.Q÷ìzW;í+gdÖâã Θ"Ëèús¦­ª¶¦Æui¶DëOÈç@áäÀSbïéÑVÏÉBI7;“6âò›Æ¸«i­½Û2*¾ù8ezi皨ݨTl*ÊQèm?mïŸ×mh‘f΢ϜÕk‘nY‹q؈š5Ó7N#óï¼p>¡Å‘ÛN}òXËýÒç•bØ)ø»ReùþL\z)Oα)“m5©Ñ‘È»ÝW”B™Æ‘ÐÙ5‡×ÇSp íQîÆ¼²[^u2 )¼ÿ)÷˜ø ^}ócM{Š®=&ôhGL¬ºåŸï «FÄØ¯7ï|¾Q¤ê :åÃñÙ7WmW{y~z²Ç«yËür2Òõ“SÚ( i(Ï“ãHfR@@ª¯‹ºÁÖ:’IÐL“Þö­³Îé;]Ü=3JbR…÷r×#Pî²^ìeùX]0q·WeêÓàv m©©r±+© –†jÎ2\vR)ÊgÁTÝ“« ‹ñŠ V¶Éz(—ÔR'=4?Pì:óäófŒš›£|JÀíMU·‡’Mä{äPÞhB‰Ü&žðÕªñ)ñ»Í!×Ã唳K¦bç–ywÇÞK«Ž÷‘ßuI¸¹æä\(âè¡×c¹N+cÌ=\îw.¯Ô£¬x´WŠäÌ*uqž úÚs²8ô®ÿðP¶4®éš6&ÓÑá ̲ñêq<&$À»ÎïZר|NìéûA%™]ôìq Â÷ºMåj1ÛÀŒ±º¾¡ù~\ÒñZI Â!õ!›£Úþ®óñ¸^>Ž„ùGð™‡×™›IµÊG£aÔÛºxZ¡äñvï}¡Æ¥¹¼ÖH­…Z·üªtꊙ£½}&a¶¥~ǮܰÏ[Qü%xCY<×Y/ê¡ä¸¿YµÇ^b•ÜI{™);ÙÔý鵋æ·ï8ŸÓçº%‚œf™Êo˜­Ïbp™gzò²J¾¦®ëX“ðeÂòdŠˆ~iÑÓ¤_5717Íà–b{“ƒã6Jvl&ÌýUyùhÙ“1¹‘³bjG¦âR]}æx‰ï…”œÜV“VcÈ)a ÃSÚºCWcJojQPZ®hñw¹Gwl'oŸiº]Ú>û*æZá£_ZFÎ7ÞȪOx}1èˆI»i¾^ÞY«>S Že"çm‰,ïoäÈu¤ší[‘qœ…Omœ¦¼êìw›ÕçRï›nyå´Ç&ô‡²Ex¹ì;îî>JqM@pÉ¡Šß t ¹ïR,gÜœ&.Ûqß+qEb«)V§@ü¸uWVDãô­ùŒÓFÃǪ2zÄd¹‰ fÖÕ GÞPxë\¿Ðß|hžÈã´4)¦ô¿¹2ݤ´Ûî–žµgœ ­6^T%dü©¨×&‘‚+uŽBÈ,VÿßÅäÄU½ƒÑ‚ý÷»/j Œn,ÏÄlP¯W·˜;œÂ`r{ÑÝô2ÝŠ`¼¬Õd•}·èØ bn/àÎ|Ž~•KÌ/©¸mœ½Kɦcb™Æta%bÅð§sCÕ÷Û¬Snì¦+9ÃíÙõº§&Ê&ãfL̳û’dì×ÐFk²9 ¸Ù’¶ÚëŽ'%.ê§¿u^![?\MPDÞ;%攌ªJçy?É—n´µÆ¥ #dgg÷¶Jµw¾,¿]-­ýeÒñ›uá¥Æ3p¦&ïözñ„>­Ãe‰‘%Š~:'·mùð¸í•x‡JF½ÚBü «¿ºŸ:tH6„H‡ù†®¯»bdmïÚOð-Œ`œa"l»¼óTúû²½F½lÝÏÎØ M¯v¸'×$i6zÜ•¨".W”dnku.Ï'Fß÷kêr0T endstream endobj 414 0 obj << /Type /FontDescriptor /FontName /DISYCV+CMSY8 /Flags 4 /FontBBox [-30 -955 1185 779] /Ascent 750 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 89 /XHeight 431 /CharSet (/asteriskmath/dagger/daggerdbl/minus/prime/section) /FontFile 413 0 R >> endobj 415 0 obj << /Length1 1396 /Length2 10013 /Length3 0 /Length 10831 /Filter /FlateDecode >> stream xÚ­”eX›ÝÒ¨KR¬hÑR‚»w—âîR,8‚»»—âÜŠkÑâÅݽw?¼{ßnÏ>Ï•üȽfÖ̽æyV(HU„MÀF °­#È•S•2€ŒÌÌ"H¢¡£ØVÌÐÄrs³$@F/?^¾<ì<@$ €(ØÎ bafî ¥ù'‰ l‚XÚä ÍA6/5Œ ­*`c £#@ØÚ üÏ€2Èq™0" cG€È̉é')[S0€óßË&NvÿrA^¤ÔÿÒ¤¼Hš€m­Ý& S$&yðK7ЋËÿ­ÿ..ádm-ohóOùMêÿ‰ÚXX»ýOØÆÎÉÈM@ÛÿNÕý[NdbádóßQ)GCk ca[3k€ÈÆÈÌöïu  W‰¢…£±9ÀÔÐÚô¯u­É›¼Ìï_Lò"ÒJ"ÂtÿóhÿT4´°uTu³˜ÿdÿ‹øeH W€óË”/‰/Ÿÿý¥÷_ÍÄmÁ&¶fv€!bè†ôò½;À°°5¹@®/ÆLŒ¶`Ç—-€—ÉxLÁ¤ž+;€IøŸ¥'€Iôq˜Äþ7€Iü?ÄÉ `’øC¬&©?Ä`’þC&Ù?ôÒAî½tÿq½ÔTüC/5UþÐKMÕ?ôRSý½ÔÔø½¼lL†襃Ñz9ƒñÈüÒÐä/˜@ÿÁ—q2ýû=ù“À`2ý“ð Å_”ÍþÂgó¿ðeÔá‹§Õ_ø"jý¾˜ÚüÁ—·‡Éö/|1ÿÙ^rÁ¶ ¿Â/fvÂ/±{¹}࿎ |QûKø¢æðgË™ƒµ¡Ã_îÀ—ÁþÒÞÑúkX/~Ž.à¿6¼Ôpúƒ,/q׿ð¥€ÛŸ~/GwAþ½ûÿ½J""`W +€å¥Ï‹;€›ƒÛëÿÊ4v‚@@¶Žÿú¿z¹‘ÿ˦/—r#ÍÏ€y-“ꃋ½Ås–ÀÓ8|H) nú–ÂËx°Â‹9iS}<ð´(EO<”)g21e_ïöì÷Pa šÃèGŦÉNUA86>ÐÕý#³¯ Í&#lwžxË®îxìrÕ¹¾RvÈ™ƒW<;óxîmå3²6œlB¶ˆ¤—Ù"›Rß[ºû- ÎÏçÿÀÕM½ÄzšÇïZž°ZBMáŽÈ”ƒNá@d¦q5]A¦YÏkB¹?=W}ÛE#apžQL™“Gå,,´ïF'²œ°„ÇâŒ!ùÍ!Šý½6PªúÍ…QoÐÿçTYÿÓ·ø¸Üi™7¿×ƒzíéC°°{“{ÌT#‘_‘ýAä—h)Éüë‘a„Dãlc$ä·E3åðÄÈj*5‰˜éÍêî-—T–_ë‚ï•·—TˆÁïe]œESSa"Oãç¡ ÍðnÜ*kw ù57(2 !½a~¬öû†ê›Ú¢iÕ?ód½:¦Oç%¹|zŒèbYç}ò™NÓ´ú{îÃØñsº”ˆòëM,\g y78?qN‹4K0‚y¥±P¼{R"ÁEö¦TaŠG>=m@lq³‹.öÚÐBëw˜) lT?Óy6RÇ€Õúµx¾ïìrX6ûÁgjþQ,+@=\˜Ë%ص¡Ä¬ÊqUöG–‰\ŒåR #tEÁ“0ÊíÃC»XM|šï‹kŠÕDr̖ŃQBˆ8˜vmN=^Ëò4Q]­x,5¯KS<1[ˆX™‹“g¾XAÍ´æcèNHdÃäN>T¤äR u¿œ¡¯n¬›pÊ„¿v=¡Ã¤Dˆ‘_¥ÙA0wC­Ï2Ðß˃®xË`i%?°ž|—Õññ°–Å ¶À©¡nu,máûŸ‡ç»ö-Ï….7Öº¼ó´¬°ý‚ ç‚ üê [á7ÙXC’“G–®Ð Z-L³ñ[•ù$JSWÅk­— Ùv`°”§1xTuh%ª%LO„HQ!vH²U’5ДhwE¹)(<èãІ öÑ‘¨ùEN9‡ÕóÂPO¤ÌFÊ ¿ýˆ÷Öb™ãF|Dünmë«^yèYà«Õ‘lC¯íÒHEPÃá"ÄF“Ý«¨ÿ×ÒhªÙú†Ân³ÎÂí9Oí)ÁÏŠþ³JUõÈ+ñî)æÌتáOo<:ŒlDZÉ'°¹¸ÝNõg­¸8 È«·p¢å¡80ô5, €'DIU#HÝ2«2Ü´c=õCÒôò.}¸˜°‘0=Fï¬#VG¸É™]HM–W„s<\—f0Á+åu(ù?6‰ÚtdÆìºÔܾî€îá©9íHçè8ùûv6Õú¢èŠéçÆ°V Qnö<Šæ:BsÝ›J$å¢ÚÂãxbgjá†F•BÓJ.”×C7>£„Çuf—¤B~[Òt…óõÒݰü»²›öTW ª¶Œ˜d%–Õœ›•¶Á”GÕ•s£3”e%I+e¢D!œ+øT‚+½÷HŠZž•uýÆÛFVžï_s ±Éîmž{[ÔôNo> iŸ?Õ&=fK¯üüЀãú®×©'Ïsfë¾n¨] €uìùºWNÍÚå ÒÓZrö}$1ç<´GØâWi]š” {Ưʞ´ú-ïÆu …“;ó‡0ÚGHúiß ‰ ¢4¼5Å«Ûw òòXnxcØ?Í…‡ciÛ‰¨²^çDèÿ [_Vž ò¢Z ÈêÝ0(ß’ ¦b  ¿ÖAÆm“tÅ® ýqA×â¯LÌ<,-`O“Ù0÷ûNÒEzª¯×ã½æØ´<¿’^òÃøËš$Ô N|(r^›àÛ®teÝDD=U½DiîïÖA]dŸM¹ãSßi³<{éÞ¶±ðK*“]; #)µ«¦Ê•–šd¤iØmÍÙ”©úó±ñk@6>r§A­è¿iOC›?n,}ì°òprh·Ô>¡¶éc•©c­æÊHð†Ã T½¶Í€ÿ:>òÍ%q°ÎFU ûX.˜û @$ëfÆ©Y§Mi…¹VÊæÔ%±òùÙ&‰Êºˆ]¢.ÙÔU=)=¿¯¡åeîn¹ê|Œ YXÿÈ#´KȺççÔqE²0_± ’BÇ]ó¦õöêñ1«ù@sÒ;(\bAhf\éÏ|Ç0å·_ä&æ¡u†·OÆÍ9£ڼŒ36läÕräe´ÔXвkÖ)÷k²ãŸZV:'~Ø‹K€óLaº{á94Q¹bÒ¢£K¬¥XšpËUß–Æ»X*[œ©p~¨¿¥CÂwŸDB?ÐÖ#¡K þ"Ò”ñê.ø*‡Gg²pEôX,ùSÑ׉Î(>w¾ÛpÜšV-zUIÆwÞçªÚ}æ2ߤB Ñ2réåÜ!ryÍ~ x6Fwß’íU7À «¾Ï Ì2UƒZWGNI j8œíà¢dÃFÉJ\IBuiÉððtˆçdðLŠ^ Ð1¿ØvA ׸ @ÔÎUš+¬ ˆå¦‚JCh=ŠÄûÊ3’5)s˜BM`¤Õ–U| ÙAsI_‚÷k>üš rS]à¾øFïj«BŽ0GEí¹O;a7À¿%+æ ·-¦¸«vÍÕÓ‡œ‡éæ¨ÌÓð¤d Š î¨KOÑi0V¦U>ÏOwƒÞRs|«Ž¾œ›üaJ!šô{ë—€qtG ¶鯳w²+o}ª•‡§¿,u³”Lá¡u¦4StÛ þÜ=Ú)Áyxh•*ã­Ó¾ì®€—¤Wu/T>_ ´ô˜è÷¿áÐ"CÙäIXø˜Cç.ËÙ3F³¹0ú]ƒ‡±Âœ†yjmMКÜFþÕÞѹ³?ØÛ‰DþâÒýãÖ}±M€äG‚&-CöOßK{¨IrÆË®E ]LGB8e«pj-psŽM?’ ÃÓ³ƒüxݨå°('O5¼ä§¾ØÆTÎ6mA.cPåØc<1@ô;n¤³·Vp)uh‹…C”2¶œ‚üά¯É áÙèuvZAâ}u„Ɔ¶¯\Ó Mj»8Åz««¿ŽÍ×ÎO|LJTVÌŒ&úžÉÕ•ÚY|eê.E­<Ã`"¥÷žÌ¹iÍi¾6’W*x×UwœõÂÌ”ÒëmXF/'ÓjáêÖNÍ3‚0¹–ïVØ™Bƒ^Îëi¨7IudzH‰eòC^{=– ]¶ÅvêŠÄC{|1Ãð 0’3K«¡Hnbì>¡Yzt/¢Q°2¹ž¨w€/tšš åŒýÕ×Á¶[^¦†wpˆµc!x^éH¡ë„J¥#\J½j‰‰ÆïTožR$^#E´‘?ºîûN°Èñì œ]h´žÿâ” ñLïÜtzʱFyÿvŽJÙÐ6ró® ›ú拪m^/jCÑÈÇÊ+0Âý0éÄç& o˜Ú ¥¼ ª9ê5–kCG*‘ŸÞÈŽ°ÔuØÿæî•ŸL?–°—ÿŸxF`%eY¡Q}žÊï#BœOì7ÑÇ«m=kf0”ùƒÚ‘®C#Zb¼Ïù[¸‘=:ï7~´×RÙÄÜuRŸ1ì”Ê| ¶ŠL ¡KkÊ•,»©rÙ{¬Qa~?Éüä$×Û¨ :š‘Y9m4ÕßÓù³6bß@­ÚÜÏ ÞUo˜à/jlkŸ‡pn~˜*δsyx•Wöå{7žþ†Ž,¢VÞÔæT„’ÉY~Ô¨Gã¶¹ª ‰OQûɼ2e° ôK’ÁÄI¹éÛ‘Õ Véz_E=ˆÑ¹…LêR¨€oÒTˆÐ)D-ç…R4eM+~a}Kìá6$$Ä&wŸ÷»» ¿ÏºáÐ+@7Ii´û:úzP…¾˜¤DÔ%T­£^s }óC=D_+z?1õƒÖü,£³÷âüZ¼«ß¯èy#ÃÀÀCd|XÙ±ºp7øJÐéøu„¾γ(jE°¥cøMâ–ÂI~µOLI׊°Ëâ®xùô™B•e…séoó%î¦ç?Ä‹"tºc¥>k„kɶUev™¹`â(Úì¤ç‘{WÕ'ðu PgÉJÜûê¸àoäAk¾mù\-°5ä(þxZß­£Ó/¸\Çã©ÆÈLØe%@@±!YAhÊJTxóùy:Õ?¶}ͶùQ‘–Óµ?ẀS!Æ Å›’§g#fX¥ƒvÙ!¿žPA‘húv ·gåÜT¬¦;:ËG{‚wú£VÉèAz—§5&‡;ëF}4äTó†6‘ÂVžÏë5í‡ãéI£Tº ÎÑ×y›_ª-×Aá¸ÌúCÍÐ3ƒ»gr3OáŽ^üÙdnÍQ¹•aVk?»¸ÞìY%¥Ãü}‰2/‡ÁÞ·¸ñï*ŠzÕ‚ !|N*ã }‰—’æØ‡’*½‚êàþtÁ'•?«qT+b|ùEsÊזּ KöiTŠ ­ÀAÑ#UZ 8£X’ï䤡îO˜¾1|åô`uûÝyN¾qpÞ‡ŒÙ¨lŽú1‚ÖÙÛóüCŽ¥º2Þ(¦2â]ÄÜYáe#¹_XAoíꔩ}§àæfíÝ>vñvà†‡ìè=½ ÷Óöm”¶Í£›ˆf.€vk¡hÛ=G¿ï¡e5 ÀIršHGºN€\ÅåKL_ß·È^?z>K÷3£Ò•T›?GÝ–Óº³Ë¼³Î¬kä©ý<Ì·HÒóFûðƒEln´qÕì¸Å;§­¢¦¸õÔ.Àk{Ù‚1È"{™SšÌÅA;ÑóCá(ª¶éC:#”R6ÉL—ÆœY›Á‡)t<†°ab®€QµSÑQÙæÖd tÌü†˜Á9óÁuØúÑVÝRŽPÎÄ%ÐúQ?ø ¶s % m»K˹E?ó€këX˜ JÛÝng“’èûà£ÎÙ7&Z5Z±ÑÈ^\І1–³Þ[Åœp/K\ ¯ÇÐ|ÌqjJ„7ϼ¹ß.TÈ#Aé\uìSE…æ® e°™yô¢‹ßl}t« Å#ý#r׬¦ÝIh™'À$Œ#­qªx+ø¬<=mÂÅ®jÁªØPàóCÞÑ$t3ìÜI³“†ägíe¤dr÷µââ!Vš“tHÖáqùÅlÓž_þÑ„GìSܤ™×â)O‡ ‰*¯‹u)/!-x¹ô¨›“h便ñ%ˆ:`·Nv† |ß±°½í7Zîи\“ý­Ý+ã-R‰•Q›O é^:DõÁÄÚJ¥¿×Œ þÜ œ(ÆO,µŒ€*ðh¼æqÕó™å[BÛ¦HøÛ)|“QÎý"øµÐ²\Š×èµ·=c*jÏåb4ˆ\é­m¢5%ˆš6¢‡¤½¤ pÊ Lt0ܲþãÔl 1 PRcjÜg(÷èi¹>¶Êž –ˆ¸¦?hÙc¿@u1YI?á5ûG;KÙGué)e»~Ü4&a'~÷l™•вɋÉ3L>ä2цݫ /s•o¢6_Çßúnœ½ú ”á"ß}£ý+›fÐÈÏ:"¹o°¤ðg ‡\à¬,0­‚é%ÇæÖµì­5*ôk}<¬à‚J¥lH–‘à``Ó¼¸ lºáGê×up¶ÃÏâ(®™©/xžvÁæ®ÔÄù0z úé„3¢Ô#Û?’ð~¸ümz‡ÌãÔ3…»4×lù@P¡¢ìj·š9 Õ•ý¹cgº+?t=ö3o¼WÒ$ -9Ôë—s•Ìôn`56¨8ÆÖb=دÉn8)Ë©ˆØÉÅ^±É ©6Ø@jÞ½¼ò¸ýMÉÈt€ˆCWÛ©ó.¥± €™¿™€ë‹#N`p?9ÇÙ­aŸqˆÃ‚ì²–Å,+лÁþéºYßdµßL0Þ½¹èëV2£‰¥µm$¸I&GõV¹# ó¿þfL ß/å çVÈs ‘ƾÆ^Vß’Úraa¨µ£ÕU ü®ÏrÊYiõQÝ‚óí@ÁÄ‘2±z^öT΋œ¶Š+Vk,I³°öŸ/tk2Íÿñ•À²–Ÿ¦6C8‡âbf§ÔJKÞDÀе9 ©ÝìÁ’ÌÁà éû¢Ÿo&„³Ã—ý«-ý·Ò„wÏ$sÓ–{`¶@O !ò–çÛ¹$ƒ‡ÊjeDb™Jµœv­MNQjrœ=‡¶¦šA`ÆsKÊ'$ÇØi)LüxlýÂK1©IÌ<¹g÷:®Ùà€ðk%˜Ö2ôQ é«ß‡¬›UƒŠ¢‡¨hÈÛjÚò¸ý$KÞilµˆ=…R:Q[sÛ± ï-E&]§éY…ãE¶˜IËOV¬×¨iàKñüõ°-h‚¬bËqïê—ì^JSõ_ßG¬»ÓåÒç¿p(0¶¢ñn Ùp°3Ì„&¸¢×PK lÑÑ÷í)UOéØáf- Ù—߉¥õêÍé<2oݼ֮Ê@ _ö,pÙ«xŸ¥!íÙu·ÆDIwÐ&(mM°BÞy[Zü‰Î#6`® å3ç=X³÷ä|ÏÅëä @ìQUž4@r"lJ2{S!èq²W¸ÞL°(›puT·ºÿ@Q¹«Å04ûŲK´°O¿9AŒ‡W:Ö^ËRÝ44}ÍX¸›ÿbçÙXXœÈ™ÐÄ!¥î\™Ê%kS]1òÅí­g…oöóÓÌdk¼ƒ5þ$”Á'|Nó‚(žš¿ ‹VYš1ºa žDÊ{)ôÚŠp¢K\›[m6üZG4³ïE÷"iëôvÕ„2çËFX>‚ãkoŸYËs?—#`º*¿Îà-Ï$cJ'ãÔÕ_à÷’}žÂdr¤ßUe60ÅêXÌ_!o>ç#<1ŠU[Îó44ô²0Ëõ¯ÐSóE(Ê£¨ÕaÏâ4®cŸ]mH[òåÙNÑ"aäí—"ɺQ>dÅXm³}-1žÛUˆ€ýX”àSŽâäØ$ñ +ÅS†]ʽƒºS%Tü¹7 Ü„eEÍ"tNøzÁîÞûÙùÖýI¶¾QCt§Èý Â}ö&éÍö;%yTâç Ÿ]Á–s=”¤çIl×ÙÀ‡S{™ëÂ$Ø3Œ±xh‰U¸O&Ï“¡j»zS+Gmáv?ZcS_K7ˆêص 0¦…ˆò­<ŠQ €õ5·±+ï«Ûæt¦‚Ê*yü›`Ín³;Ô– ’›}˜õþwÈçUfØeƒ$½› 5zŠ û@äò‹Ll‹Ä<ß‘Ø/§v k—œ}áî¬10û¤üñ"[™´&"á&›­ª¤®BõKÓ[›õ`@³žÖò2bêa‘Æ{´±»ti§k½©.9¤,!Ì…mü:Îr%¿lyyÚsS‡Ÿ,ÎX£ V¦~š*š8ˈ+sýr© ï5ì¼|ÛñlÕø³ËD n<hNPŽ%ùt‘š³Ic†'³ÚÖF2Ç8¿‹bßÜh~NBÍâõòÌ×UÚòÜ h`´„òò¬, “ªå¶UøácÅw{þå§?nÆyô|íN?ó#9è.¨ÆsÇϼVq±c¸÷±íXr–ݧ¥,Þ^Ëœ|ñ]Ûoç¿ušyéPKèúÂüªWÑνs"pü;ùÕ[…¤oܼÛðZ¥ß! ûD«ÝÊLK0UX›èÀ¯r¼,›oË­› WH…¡§ ^ÿÞÚs%yôwzç­@=Uõ5/Î4'[=Ì]è–?7lv1ô8µDBM…4IeX}NÖ•›¿ôe² ‚õ”Öv3|‰ý½ïÃ3yÄlÑÉÙ–í$½èrL n¢jÎBxÎ?eÈRZéµ!Ý?cy-ÖNj…¢Ïº®û¿®ãœŸ®m{æ7»¢./L¢í8®,}x¬ÒÆØ•ðPeG9Ûæ9‰;°T¬4~oÐ Óà© 6À-j»æaFMðfGY¢£ö‰ ß ?O¡\áÀ»ZÏ­SßÏTÃÏ ‹;ÅÚßú©,õOÿ«óvš4ݯNÿ ña†haG·ãœDì®ïô›Òº±?Q®”ž\Ö wȲ[lÝV÷Ö{i!'ju‚_$I¯?·8(kT€½3w5mF°–¹ÝzáKèU¥hÉ9[Ä<ö?‘78,–:¼úê‰æÞ…dåt­4Ò+É®¨}WÇ¿‘_Œs3¶aÒä¤ÊÙdä@oÛ;4÷ £ÏÆ4ß7~›²lò 5Î7·"|:™cûý¨† ™¢ra ï­@Ê6™}…[LWWGÜ D'âÛ2&.Z-¢Ùâ›ÄšHd}äÜÚöá ÝáëÐò[îG’«"¯xt¯šáEóNg—O^îŒcáíë}­2¶×Ùxº@ÖõL4eàñÅ—Š-ÄF4/‘œqMÎXn1i1ÇgeAÐ+{K@‘}*²Y •½ª½ÃD;sQYGÄQ#¶¨[úkŒ÷qönwaL4Ìc0¸ç¯ñ]Ý|ìÓí£ûÀjé;ˆç9Ósü„±öd}u¦€[bÍJc1·¬,„·Šªë3wi'ÑM:8úØï{D«îŠø÷~5~<Ê2: f+Öï—í;…ÿN#2°¦h!¸,‰oæJAe"+(”å¾a„HD*0qÝuÆÅìn¶¸©Ð̆c¸ø8eæ½O¯Òm¨°ïɉ¸¶Ö_.eJÈôÙ”÷kÂIªp5ð¥ ~½ݨ=xÜ~7)·z£lÊ+²mOÞ|æÂŒ•-C=?k7³¼gñ¬ªŸ/‚´úBsÎÇÇ{Í‚øÝ”9aá-À1N¹£üFø\ÍÉDß7>æäè[Ì•#ž_Þ «¶àžL¨Dånä‹AÀ,Õþ0ž ½O$o=¯Éܱ5·ÙK†.‡ˆ\öq µúªït¼ÝÅùmç^]{õÝ9Äǰ…ÌXê}š‹ùTG} ÁðŒ Ž¹ºžU1%Uцù\ÀÖgŸøJËßÃQ_éï¥^µ†+8šXýŠÖ9véÿ{½ß?€å ž/ÍþŽ{ì³W=“lÞÐIøÛHý- ˜Î_eë¹ð×ó+C¬x÷ë±2¥ƒPRÖˆôÞüd…ãd —`TÔ”¼bh@¯MªÝy=ÙÇ÷µ;¦Žyy~Òt›ó7g Ûö“ý=¥óõf~ôojMØæ ˜‰£H‹¾Ê>% “‚O$:@¶‡$ÀÊ ¢ñýúŽ_ø2bm…™?û­åä}[)·q"IÈÞËCõanãÍww±ÂÌ0ˆ‡Ÿ› 3+Ÿ÷“ 9jЃ/ ¸”9Ë»¡¤1z»pÙ9¨u¯†Õ™\Y?¸h;K^pªÌZ½I]î~ãWxÊQhè&£Ç†!qEB<ÉB¨„6†x«±RRðÎ&hÌ»TœÁržXÀàŸÃ~bü¸ОXc#š„:Û³\‡ócûªªÆ¿ý£2à }!™PAW$ùÎs¯þki¬ò¼‚”úæSs?¬Civ‘ÜøIÚ œ!¸œÜî9gQZR÷aÔ\UHºN´°ñîë-3I£%¬v^z d+*7Ö[=áVÙ¨I{ ‡Ç¯~6ÝÖÏýv+¢W+ˆ-E~]ã•×Çá'ëçž–œcÇ2©]ìšú¸ñÂQn¢3ÕÂìÉÛZydÄ15ÛQÍ*^:,ôˆjàï3A¾™„úŠŒKVåÏÔw•š´ S»UÃmð˜Š×B-î Ä•ç¦É¸.­&×nOŸ¸osvu éuÙê{ûi½îŠ!,d.§c¢Ÿ2NqFT²Bó9`e¤K«$Gq©¬I³OÝ Íó]e´ú‡ÌoÚœa`áÝSßßb(‡ö¯”6ŠÔC1×’pi\[RÄ%±®S’r¼2Œ5UÛ§#:Œ£ Œt¹èh¿0_|ÉÇÛCk„ú`ªÐèš™ÀÀþ^qÛ endstream endobj 416 0 obj << /Type /FontDescriptor /FontName /NBJQBA+CMTI10 /Flags 4 /FontBBox [-163 -250 1146 969] /Ascent 694 /CapHeight 683 /Descent -194 /ItalicAngle -14 /StemV 68 /XHeight 431 /CharSet (/A/C/D/E/F/I/J/L/M/N/P/S/T/V/W/a/b/c/d/e/eight/f/four/g/h/i/k/l/m/n/o/one/p/period/r/s/slash/t/three/two/u/x/y/zero) /FontFile 415 0 R >> endobj 417 0 obj << /Length1 1384 /Length2 7552 /Length3 0 /Length 8370 /Filter /FlateDecode >> stream xÚ­—eX”í¶Çin‘¡‘º‘¥»›†r(énI))‘¤¥»›!•nFD8ó¾{ïWÏ>_Ï5óa~+ÿk=÷ý\×0Шi²KZ9Y€äœ îì\\Âie--.N'§&ƒ´+ÈÜì‘1w ¸„„¸’ήn~§0/7ü‹ÉvrövÛØº˜¥_ü$t¹‚-Í!esw[#¼†¥¹@ÓÉ r÷æH:84þÊph€Ü@® +L..€ØÒ`²C0iz±vüËluþËäê`þ[æ \¤•ÄÁ`²Æª8Á»àZþ?dýwq9¨ƒƒŠ¹ã_åÿÞÔÿñ›;‚¼ÿáäè u¹”¬@®ÿÕýKœ2È uüoï+ws°¥$ÄÆàü— ì&öY©Ý-mî®PÐßfÄê¿5À7÷· ¾–º¢² ë¿êßN5s0Ä]ËÛùŸªEÿÍ\¿¾W°À¾_.x üóŸ_ÆÿÕLbéd†Ø¸ùøæ®®æÞ˜ðã'>À.bò€¼à‚'wx ¾?€µ“+æ_O”Ÿ”üËô/¥“ (ó p€r¿‰Tú‡¹@ßÄjþ&xí~€æ¿ ÞÁâ7 €–ÿÜgéä?fÿ±ðòþeqtüÏÅ eõr€ ?.ËúwÁ¿È êú‡.Ôæ„×·ýÝ ®ÛÖÛÙù#nÿðaìÿ@¸b‡?>Žão„à¥à· èô»<Ö ò‡t.¸Xçßnx®³¹+â²vÿmåú·õ_·ì3üÑ8Ãï™Ó‹á‚öÇà\ð9Ü~'ÀÇps0w³ý#^ãwE>x#w[WÐo}|pAîžN$Àk@ÿ@ø*<þ@øxž¿‘žíõÂË{ÿÿ÷II9y½aç°sóðx¸‚œœ~ÿ+Ìê _ûßï(ø]ü[ƒáòYbÎÏ9YŠ„Ú¥Õ‡—úËÀ>¡² JÙ4$¨ÔvN·a…|KDt(VtaY©Ñ»+Ë$|ºƒºCíyOéÝê«>&wä’>û°ãa¶“éÓD©w–©ì½íL{r€ÛR?sqȇ¨:µ2Rš¢ÿ±/û´ÿ _YFk}‰¡ËУ®++”_@O.ÓA;"¬†‘‡F/Ý¥4’7bÙ/% ey"Äß.£‘uñ‡S+”à®#/ùÁáÖ-&˜[*ç[ÄXD&;¹à²‘ŒÍír¬1¡¶¨È`‘UÑ•Q3Ó ™o¹^耭k6Wwy#ÌÜo<±`#ßPbF‡ ¹é¡—Éáh Ë6”+ãÙ»!Qãä.–½ MÏR=±†|Lc*©Ã÷y¤Ryþ¯>Œô¬Ü` ]ëf+_–M>“éR9|jàûº—.éæb«”îA%ÏEÇ~"Æ#þD‘òÜ¡ó‡Þ®­sæü†->†ŸÏI:ý™Gù²¯¨¥ìý$÷íHâ y£zÚ¤¤œÕÈo×ʱCö†!Z.3jïÏØÕHÒeüWåÛ øeØXìMyÚ¼ëSQêæ}ØI ¤Ë|sÉ%*ÓV>ë„©¿îÿ¥cí¹'œd5¡˜æ9 ˜íbO @舗B8{NÝ*|Ýaû^_c=IÓUFmQ‘‘íÓ›8N)Þ†8©tiÂ}N!WÔ`ÌW™ªé F[-5|¿:™-‹Zãô]€g$!"„èý ‡Sr½ø yªfà׺fã‹&΃D“NZ!n?’]>RVôVØPÈÅà¹-”e \yD¦-GàÃ!ß PšÍ­d–ïï?à»t¥¢·ËZèT½)g3í©8aOæŒqp¤¡sxÊp:i=ak¿íDKä³ Ÿœ\B?¹A!HoÈ/ÃäZÏ"•ð‹s.úÍY”ÜôA˜vDë¸M›Ï+í_ñËF׎"!°ÉË^Œä˜Ö¼†0H=úv½~Z }¦ŒÜœ?Úe€ûâ<ýØ?`6ùõ'ÉÏ£ï#´Û­¶¹>sõÛz?öú–”Ò#?c¨ÝÁºbuTê)pÉ ޶;$ØD—÷%Xº©v¶Bb9%9Y3úqþ3ÏÃG.9£—á|"³ò¨•ÂêË*[SœIw«Õ“óɯT]£™aä¦!mÎK¡½Ä´©Ôxu|óž^âÕ$¶$õÍÛ£ÏZÏ«](LQ’Ï('¸kgQ޽Ú=ËÅnèß+RbÊÿ‰—Ý—í_¼Åý@"q)”‡3®å_L4ÁU7öþ{{3»;,|è<$G5#ìp­/%ïçÏ× ³|–øù¹­Ž ;•YþìˆW£œ´É¼Õ°¶Ùè–©ºHãec7{úæ]:÷h€Ð÷ܯ³{Ç×õ׫ª ©1d€M=ô5v6 ¥äAJg§ ~­_<Ó½3/`K¼Z‘Âø@^,lM£%®orxªú‚°Y/æSß¿ CéÙ÷Ô"‘«ÓG™4“'ö‰dWQ³7£nJ}ZNöQõš1pû1¬†æ?Žö.û¦L: ÒÕ­ÀbCè—¯c…ÜÚVcó3Šš%|?YœDB4û”ºD§¯õqcÏ‘X_] É6$è<ó~O¦yZoŠ‹‚»ª¹8Éž›ÝdöxüãVˆ‡˜¥ƒàtƤÏc]“¸™x¦ßGTŠPÁÑ´Óñí² øªñȘ@ƒåz”4ñ­Ô³$g/‹¡õe"ý±oˆsC…c/b<®«¼¸ÚR õž7¹Âë;®oJfñ&&—ÕćQË{俚4šæÕ‰[#®lOäðr\y° 7xïÆœ2Çló­ÎÞ`mß¡#o"°÷&™Žî²´L|óûÒÞ KÑPêU…OàÜç~[nGN5ŒŽ€ÒW«1n…‚-ß×§F bk?ÀÒ‘[®[*NGð9ß[¦B¿÷²qN”r. w:×·ò¶ß.vàMì[ù€0JŽÒÏÉ_a%þ’n¤0´ «"Û‡´8ö`Ž\ÎÖ¯1Cüi»ïÇÇ3›ËÞNaÝLTgùña¯ \|évV|PREÁBÿ>;BZB÷h u¡Ò‹úä MÆŠ”P® |Mª3†àØÒÜd|ýr)‰:±-Çðû»o?Óz’ìê-òrŸ}ëÆY奿¶Vfk± ùyýSäây‡¯wY/ª&ߘî#¾2+½þ¶9œÔa5æìkèI5Cm3¦vÄyØ>"à¶}ßyŒˆq\tdj)Þãú˜‚zþ6B{äg2È «[|~œ’ëþ°0¼(uàiZ}ÕpH‰Œä-a1`ؘ|,¹NkÂ&IÅ9ôèHL ”(,¨öëPpsPU^cû÷Y·Ûù¹oEó€KëëCuÚR$vã7 ·\ȹÑ–Š¿XSýÖ^ènk½¤ÚÓÞ4—°jyM>”óíŽÌª¹&¿ë°Y3Z$cŒL%R¡EòŒè7ßô&ù‘—ý-IÈÆë¿ gÉ-»+Ò–¸úK‚ùctf02ÕÖ»ù7úI Ä©¥oèûd~­<’hv–ë.>¡6§ó1Žç÷¤ ™–6m¯ÃD¸.?cTâò„NÀQ/Ì´|íÛ¢ä‘5‚Ç[coWÌõ!ãäfœª‚ªf§Ô&h¶ß>îð¸ÝZY7aw4ŽÿÜ1cl>v(WB‹QÈËA9˜¢'òyñ,lzõˆ?hÕI\)YÈŸ}«šÅtÃh¤Ówð>œ²šë˜ñÙpœZZ•òÆÅÝèôt2¶"ç “½ÔŸ6SWzºŸ­  ›Ó–/2 &«.œ«÷¦ ¶íË”ø)ÝÇõ]ˆ|Âôå¾ëºŽ³CRÂ^N¾Îk‘Z3F£[KiÙ‚R$v°&¡#ŽÀ·=…Âú–Sr™.‹ÁÒñ òÞ6$AIS(‚M°÷¦5ó²…ß^cíã!Ùë‘ð¿mÛèçK6_Tˆ%óœi´Åä—ŽÝßKAËH6ÄÂXM‡¬ìu=c½ŠH‚•ļ‘vÞ.ãQ—u ‚â_G‰¿¨ +í……kq˵1Áu£KI¤Ã1þm”âÓU²0æ×é™:iYƒ—¬Ã¯5Ž †QÆ»ž?/RF¸-Â4Œù÷VûC]šÖuZ¦3°èé.°Fn•¹õšó£ãËUŽñލûL]”E‚°q‰›ž Üén °³Ý]Åw'>öÙ4âS,ÃZ¾ó¿..77Íc5£QgÚ¶Ðü*ÁÛ3Ñw™aG«pí{Fx®+¯…RÞEþum¸[¯šLݸ£ux=TÏöSrøó¬{¢ÚT¾’ Ëu>CÉÖïQ¥@~µ‹9Y›¥ôªMÌ:›Ì¤{‰>Í)Þ¬¶`zÅA˜Zß³óœûçÖN"Ê›…HBàü1'©÷ø!öÝœÿÚËÕkeSÂê'"Ttœ‹¾·ÉZ]>µãýÒÙLÆu+VþÍÀEòw$7üC?  °eM©²4uÆk“zs2B›5UߤwÙFYêVˆX?1³/5¥ÃÄäl’B­uû8‘[´À±_®ÛXý Ý_ÖôM2;¬-„öL×a€Jœ•нÍsÏce„EwÃíXËþôlÜk·jñõu#÷¤¼lQ¼°Ô‡­¹žV@ŽðѸ.ƒ£³f˜[¾]ö[´äY”Ñ;1•UG}ÅyÊ\FE­3ÃlË \ý Ú®™[¿Ôø“'ý‡ý5ªÝ„f½?L>´dä@¥IUëÖ¯¾–éEtëÞ_0*HÑáÆ¼ï‘´â†V¦FK†4‹ldd{ù|H+ï p‰-Veãío‘AD¼#Ϧ¨ÓMÈ‚Þ÷X FoËÙp´lkîŠF>×”B€–m†^•?9j¨>z#ÐÍËy=€M/U&·æô”1íóâÔœÖiótír‹j͈nN¬öžM¼ÿñÜËtG„w½W*¶²åõ½½òýmÁ{+êÌ‘G°½îF±Ä™ÆA+©¡L$â:Ï‚"œ*ÙËÈ…-mM§Í;NtË| ªê‹¨”p\¤BvâÀ®Œq†¶¡¨]µcòáüÈâEøËܖ–R!ùDɽ'ÚÃ?÷¬F8B>O± „Büøaz²xzùK-ýg ¨£o“ºnSuPQ…âxã56•Tv¤GMÚìΕzD œÏ'²óºîè“érB3h%íbÈ÷¡ge( .bÖ'Q;ócÇÒ}ܽ\£?’0AzúêN\ DÃ|¸÷XÌÆv¢±k”uàÉýÃþ[‚î ëÃ¥þW4SÒbto U¨ ôµüYãÓO¥ww_LÍOÀì:rŽW ‹­ ’7ÈtD™%ÀeѦe)[ ´åëJé² ípÒÖ~RŽR«®9—Sþܘ#7Z¶ï×ͳî ¼Öí¬ùz—ùÜ“æëÖõ2r³N •AÄÇý_+æE7M f{Üw–!Ÿ~TÝønŠnE+‹­B*]È(ƒã]:‰Òc\]æjFA|äš„<ƽ§›g*Ïï¨ôîZ{2o³ ·vq8NgæV‚ݞ;ûõ’]sÎ,ÎH´•!]ÝOŒkû¾=}o°Méàa_è>Ö5uö2«9BÈÀì#uvÿÄK’"#HÿdŸ=øMàÆPvoúXôûÍOâ PÜeR„F׳Ӳg_§€=˵K9]©„#¡ïOåP½Ù–Ë˜ÌøçÄ#Šu¤‚†®œú|%b¯®5S͵Ÿ×w­É«:ÍÖ„,›ËTOå±,y/c»lõîQY×\|¡ú\F}g·®!÷a³,[÷2§§ØST*ÌhIò‘Çø2H7¶~ªL²Uò~öQº%•€âý½P û( úã…D&ENu@BusM¹cs»Ù©Ü|5Æd,1û×Yé·±;¹ú}3Ð6¨ˆÖFã1aºÓ±‹êToœwǛȖ. žàë¬^Š¡õ‰'Æ’Pqïb>Óþ„J´å\9}  6øI¶ Y2‚Pª:/¨Žê~7¯Fc(ðòñ×`@Ï/“O)àäåŠ0¹ Ä8Öó æ|.eUÈRb'‚I-›ê““«ks0¢§˜ßŸ£_·¸¤Ñõ:G;°ŽR¨G¤K‡¤û }[Ec.žëí8-÷dÓˆ#1® œÇO¾5ïØg^úØW&fA9)PHRyÜ’•©Û‘âµN™µMó+KƒOCïó™Úiüïý[¯Üú?j¯Ðï¼÷–Q,¾ÔŒï½ïyQ]‹Öľb]zCf½¾ÊXuÊpð|«qbLY8#ò€Ø¦MÝz੾÷`™3`gtUqž7üs^+”d q0nµS6ÂCQf3ø«yñq‰‚§Rÿ0nè!¬Ï¶Fáåª~±Š”‹hl„‡÷¶½mz?€–Zh¼G©tQD»ÉýBÌ ó‹ËÇypOû=Sl±¥òÐLq yñf« ï^ÆfØ…Š5RC#¢ ŠIE6úûR}³gíù#U°3ªŽÞuzãÇlÁ4gZÙ®:” åŠA÷s[›ù6Ýj‘ÎëLn7²“(€/…hÖcPåT§.aŽz|[XÏú4˜¼f Ø7-@­þ¥‹V‚ø˜ÊJ ®†Ⱦ^‰×ª»¢Øˆ1²–…¬<úbY±”>¯ÍJÊ{`¼z~NÕ@…f†Ö€¦BÙÐ.2Û³Õ‰VsºéVm’@?{d_-Í™åÛ`÷…°Ž±<ÜÃö¢2 <˜yú‘c -yÞKL™±úløßÅÉmKÓº¤(AU‚¯¢ža ç½ÈiO1ÛyVƒgJkÁñ‚ÅhÒ—"’Yd¹ó‚ž€éE9½Æó@Ã#+¢i<-¯±œ¢cËšÕ‹ èP6›+³'’_©i^ÞËÞËÎgåï´8gß¶ÝOäB¨E×zœh¦¯%+-¿[ûVç Ìi“«€d»» 0Õ–1“ ƇçODZÙ luë·ŒítïUDaa8ž Ñ*ˆ›0»”l|Ç+ÙÓžìÞ6!!ÁàúÝèõÚ%e»›I¢èz†â›Ë ²…eWþ°Jîl£ö>{Ô/åôQYÔ"`²ž5zKMIž¸Nœ]Åĵr÷±ÃÔ $/|<ØË‚¯öZ3&ÞÏdQ–­D/â5úHä%O5N‘?;­Ø˜´Æ%á½ðªûìŽðä¿EµDr¹®dí1DÄ¿®ýiúу0‡u_qnsOFvÓ"é=hì‡P;°B©Yˆˆ8ðБ¹ïëA¡!±p;¿š.˜u’iãy©Å“ŽX CM9팚¶–Ú¦Þ‹àw»\83T»Z‡ÓĤ›ì­S¡˜¢t) æøE*%ôLy7ŠÔH…Àdódë²ʰ¦ ÅFçTßɹç¶a˜3æÈ3\Z™$e†¤”&¢?û%H¨PТo(<Ú2MV:w\ •AÊ¡YÚgͽH0_§”ãŸn aÉz̘7æýoMÈÒ­íB%R‹²D.åâ4?Y_²Öž¦$Ó¬ªùMˆßS@¼¥INë£ÉòUØBëî3ÄŒìLEVî³vecbÌOQC×rŒ*Pѿ֬s f\רŒ'îh%¢9~W<”›¬ýjC±ÇÁ×Å8¨Å‡²#YÊ^ªéVã¨ö<.ƒR¢¾VxRž ùIîDÖRZp—…å”Ma¤4¤Î9uJ© ¾]“–ªÐ×´iÂ^©·$e}›¼r¿µ} f<ŒU/¥%Ñ æwC3¤‰¢±ƒÙ‹¾Î©n’²Íš 0”] kï>€ÿãh7+$¸¹•È€½]ó~sà)QÅ(‘ý™¿9l3€÷€ŸE±û„‰ùðSfÿÇï’$Úú7SkÈtmƒÆMéx]% ÛÎ÷MFüÛ,‚Ïbg?¤ÛTƒ^äÙPahÉy²g3†°Ú|šäiZb€y·oKÊü6hß³õÚS“Ú^f4¦o¶íGJ¾}Yö3D¼˜ß“Ê™ôúI»Ë4,ÊXDPD<‰ïó7fjA^×~7±ÝÑwÜ’“nJGõ#èÖýeϲŠ»Vb£ÄC'f»oeÑvÊÚ‹CÑlwˆš—Ü ˆbTÁ˜žæ-&Ô7üßóŽ9俞ÿ|±Åø¼£<¯%¯þbâÌ)ZüÞÄÝ`¸‘¾ÕèaO‰|2] S?ÚÛYñÙ° ªPØ^Šý¤HdL AÕk±dØÓQ'º“ªTØÌàPrd49ÁwgSÅÏ\gÂ[XE߬#)éO#­e‚-ã¨Þžw4¬ÊŸ –Œîüô±õà;=´Ô‚,½9tSáÔ‘\%÷:#ÿ\;Ž_–kåo¯r~¨À«€&A†b(k«º¦Œ[HªƒÑœ¹ÇoúÒ3ÄR:å\Š—¿…űD~£?ITÅ28åÏ&Î@6HÌ>kú‰-nÊ›$iop§u,ÿ¥Ëš c{l¿öç±êXNñEõÏñ•økzõJ$}Jei‰&ÀÚ>ÉÒ‚6™„aþÚÕ¼=ƒ¹&*=½Ÿ nûcé–¾à ðèöPËÇ7éD ‰††V²HÌŠ×váéâv$MìÈüüô=Zºêí­“"~_ ‹kµqÓœû®ØP^«¹+ Çúá¬FÎVUz ß­«f¿íú¥slâë»Ô°0.QYÃ27ƒóÉS:lð¶XQ¼ƒÎÚµÊat¡ÝzdÏ»V‡Übél]¼ ®.ÿ?[£ endstream endobj 418 0 obj << /Type /FontDescriptor /FontName /YTQKMN+CMTT10 /Flags 4 /FontBBox [-4 -235 731 800] /Ascent 611 /CapHeight 611 /Descent -222 /ItalicAngle 0 /StemV 69 /XHeight 431 /CharSet (/A/C/D/F/L/R/S/U/a/b/c/colon/comma/d/e/f/four/g/h/hyphen/i/k/l/m/n/o/one/p/parenleft/parenright/period/r/s/slash/t/three/two/u/v/w/x/y) /FontFile 417 0 R >> endobj 419 0 obj << /Length1 754 /Length2 958 /Length3 0 /Length 1509 /Filter /FlateDecode >> stream xÚeR{\Li®šX—.lY>~RÑåÌÔ4•ËÊèâRÒýª=93s2sÎ4s†™M’)]T»å–H2I´HÖÊV„’J[ɽˆ-äšú¡=SY?üq¾ó¾ïó½Ï÷¼ÓÙ^>VÎ"u%pÒŠn 9_4Ð ¢2AT¢ÓL+& Q1Êr@ÊE(‹É¬EYCLš©© ÎaB!Š“Ýp0„©D < §Ù¨¹Wà\Ðí¿ÃØ„H.Æx|˜KP4²ƒÏµCp‡OÊÇpX ¶)yê—â´”KÑÃBL ÷„…(0E‡õ†¥Á÷ ÚðFyR,½·‚„âŒó(€FBž‰!T"BàP1‰rÔY0—j…Úp‰±aI ªI£$~8 0õ"$‰8°¢CÐ7˜/CÖã¨D˜£P:Ò‚¯aWL†r¼0á., #qJ‹DMm>: ‘ë(Îbæ¸@>Z9Õ£áÊm]Ý]ƒ|í0îc8éKós½ê„aŸþÅ÷€I1&!ú%ê"þ·Â¾yÏG†óƒi`±–Ó ŠŠÁd‚h:À¨òe•QµØXãI¥‘”Œ\BLSo„l¼Ô!Ú÷e,[FÈ@´ÃP“èL¦`±1_K@¤b1µ…ÃKFõã³ÏŨ™¢¨ EhíÿÈÂøÈ=§·ÝìrèFáØ[‰¢|¥Ê??kðøÛä»ÛKÖè:rëgg^ßÕ3‰á´þØÇ¢†³öY2ì`— ?»Ó‘-I¹øz 1xÃ@T¢$žñ¾ÑX}2sè,9>Š«Ø³·íê»û÷îkL}.tU–¡á¢ÎØ–gy—ÑBË·5Å,°¹ñyN¹Íkc[(vÐ(Ã`¬ŸÄ¾4ó­Þ/‹¢›í%½c'ñé Ù‘·{ïç+ µ¯vä=xsBiQaR–};úI]æËݾ;7í¶/P½(šÁrᬞԸö²^n1vÜrÖ§u7Üû ýÕs¼÷Kš…f¬ÔÏ}ØÌÜe/ù«Ê³»"IÛS™6Á¥ÁâR2\iÜ%9¨ Ózã¼ÌîŸüDj}nªSt'êÒÍØÐ$ôZËo•©3L ¯ò†’ÜÃ3꣛«zÆåôMìµ;,Y”çxJš4ý„kêS¬ÉÔÚ†á½Ï¯|N»Á£´Š‰­æÏëZr>ͼ¹H‘§;'#À´k¦w Iù©[3v‹pàV¥‰Òÿ®äÃFýÌô¡}ǽXEò°\]ï8÷ÊÇ…ÕñµgŽœ_¹ý‡‹é·}g¹‘iNüMðk¿£¦U±kËj•ëvb¶É1ótŽò¬¢Ùî©VvÓæÖj¨Rl.«¯ÎD¥çö¥¾b¯qÛt¶óÊËÐþ6Z³¼‚ïó®wA¾÷SÝòòü:;<®°µoùW¶(jعµïݳ ´ ðz’ÆÉ‡G% ÖiÌÔ:¡Ñ¡ú ëÜÀËò?&f·_¼t;åúæéŽN¿o†ë¯Ðšg(°}êE-•è&#bËFÿ1÷¯øÒ‹œÍJãRìævѱi'òo¾›¿ÿ`Pn±.rysÚΚÎåŸé šÛ—NÇ.ŒÕœzéÅø­u•Þ=uÆÛ”ã㟶Nëî> È=RÿçݸÆKר5ÎöÐõMá¿©)p[Vãí¨å¶ÃòÚñ®VUL›ãÒÜ¥SªR56­ê¨Tü ç 'Oïm¸Ö‘ãÓ_b&âë`ÉYDV¹òIÙ³ ãë»&*º<ܶö‰ÜqJ‘WU•—Ò3/Bu~tŽ;K‘4¥ÇmBËòm÷ ù¬ù‘ÙÎ:Kîõgi ìm­²+œöjÕéä?>å|\ÜJBʶÇC9sSêœì³w«œµLL¶¾J÷ ÉòÚb^|1Z7·4[ÜGZ‚îó6ËœÆ|£“šëÒÏ”dM8–Z—ÞÕÞ¡·¢A¤¸œfúú`Oç±`apKP¿°¥é„5öÝØÛ¸ endstream endobj 420 0 obj << /Type /FontDescriptor /FontName /XFHFYX+TeX-cmex8 /Flags 4 /FontBBox [-29 -2957 1554 772] /Ascent 45 /CapHeight 0 /Descent -600 /ItalicAngle 0 /StemV 287 /XHeight 431 /CharSet (/P) /FontFile 419 0 R >> endobj 92 0 obj << /Type /Font /Subtype /Type1 /BaseFont /VYPDFJ+CMBX10 /FontDescriptor 376 0 R /FirstChar 46 /LastChar 117 /Widths 371 0 R >> endobj 95 0 obj << /Type /Font /Subtype /Type1 /BaseFont /FXFLWV+CMBX12 /FontDescriptor 378 0 R /FirstChar 46 /LastChar 121 /Widths 368 0 R >> endobj 329 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ALEABC+CMCSC10 /FontDescriptor 380 0 R /FirstChar 45 /LastChar 116 /Widths 352 0 R >> endobj 113 0 obj << /Type /Font /Subtype /Type1 /BaseFont /MRLXRD+CMEX10 /FontDescriptor 382 0 R /FirstChar 18 /LastChar 88 /Widths 361 0 R >> endobj 135 0 obj << /Type /Font /Subtype /Type1 /BaseFont /XFHFYX+TeX-cmex8 /FontDescriptor 420 0 R /FirstChar 80 /LastChar 80 /Widths 355 0 R >> endobj 106 0 obj << /Type /Font /Subtype /Type1 /BaseFont /JHKAEK+CMMI10 /FontDescriptor 384 0 R /FirstChar 18 /LastChar 119 /Widths 365 0 R >> endobj 134 0 obj << /Type /Font /Subtype /Type1 /BaseFont /NEAZQA+CMMI6 /FontDescriptor 386 0 R /FirstChar 105 /LastChar 112 /Widths 356 0 R >> endobj 108 0 obj << /Type /Font /Subtype /Type1 /BaseFont /NLEKCH+CMMI7 /FontDescriptor 388 0 R /FirstChar 105 /LastChar 112 /Widths 364 0 R >> endobj 120 0 obj << /Type /Font /Subtype /Type1 /BaseFont /KLZKTK+CMMI8 /FontDescriptor 390 0 R /FirstChar 61 /LastChar 115 /Widths 359 0 R >> endobj 140 0 obj << /Type /Font /Subtype /Type1 /BaseFont /UDSRTT+CMMI9 /FontDescriptor 392 0 R /FirstChar 102 /LastChar 114 /Widths 353 0 R >> endobj 93 0 obj << /Type /Font /Subtype /Type1 /BaseFont /QRKXTL+CMR10 /FontDescriptor 394 0 R /FirstChar 10 /LastChar 127 /Widths 370 0 R >> endobj 90 0 obj << /Type /Font /Subtype /Type1 /BaseFont /LSCKQB+CMR12 /FontDescriptor 396 0 R /FirstChar 44 /LastChar 118 /Widths 373 0 R >> endobj 89 0 obj << /Type /Font /Subtype /Type1 /BaseFont /NYWAFX+CMR17 /FontDescriptor 398 0 R /FirstChar 65 /LastChar 121 /Widths 374 0 R >> endobj 136 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ZUELSB+CMR6 /FontDescriptor 400 0 R /FirstChar 49 /LastChar 61 /Widths 354 0 R >> endobj 112 0 obj << /Type /Font /Subtype /Type1 /BaseFont /OYDNKU+CMR7 /FontDescriptor 402 0 R /FirstChar 48 /LastChar 61 /Widths 362 0 R >> endobj 122 0 obj << /Type /Font /Subtype /Type1 /BaseFont /XEVJSB+CMR8 /FontDescriptor 404 0 R /FirstChar 48 /LastChar 108 /Widths 357 0 R >> endobj 97 0 obj << /Type /Font /Subtype /Type1 /BaseFont /JHTAVC+CMR9 /FontDescriptor 406 0 R /FirstChar 12 /LastChar 121 /Widths 366 0 R >> endobj 119 0 obj << /Type /Font /Subtype /Type1 /BaseFont /RLMMGD+CMSY10 /FontDescriptor 408 0 R /FirstChar 0 /LastChar 106 /Widths 360 0 R >> endobj 96 0 obj << /Type /Font /Subtype /Type1 /BaseFont /LEWZAZ+CMSY6 /FontDescriptor 410 0 R /FirstChar 3 /LastChar 122 /Widths 367 0 R >> endobj 109 0 obj << /Type /Font /Subtype /Type1 /BaseFont /ZQLTBN+CMSY7 /FontDescriptor 412 0 R /FirstChar 0 /LastChar 48 /Widths 363 0 R >> endobj 91 0 obj << /Type /Font /Subtype /Type1 /BaseFont /DISYCV+CMSY8 /FontDescriptor 414 0 R /FirstChar 0 /LastChar 122 /Widths 372 0 R >> endobj 121 0 obj << /Type /Font /Subtype /Type1 /BaseFont /NBJQBA+CMTI10 /FontDescriptor 416 0 R /FirstChar 46 /LastChar 121 /Widths 358 0 R >> endobj 94 0 obj << /Type /Font /Subtype /Type1 /BaseFont /YTQKMN+CMTT10 /FontDescriptor 418 0 R /FirstChar 40 /LastChar 121 /Widths 369 0 R >> endobj 98 0 obj << /Type /Pages /Count 6 /Parent 421 0 R /Kids [82 0 R 103 0 R 129 0 R 150 0 R 165 0 R 174 0 R] >> endobj 198 0 obj << /Type /Pages /Count 6 /Parent 421 0 R /Kids [193 0 R 207 0 R 225 0 R 257 0 R 278 0 R 300 0 R] >> endobj 332 0 obj << /Type /Pages /Count 2 /Parent 421 0 R /Kids [324 0 R 345 0 R] >> endobj 421 0 obj << /Type /Pages /Count 14 /Kids [98 0 R 198 0 R 332 0 R] >> endobj 422 0 obj << /Type /Outlines /First 7 0 R /Last 79 0 R /Count 4 >> endobj 79 0 obj << /Title 80 0 R /A 77 0 R /Parent 422 0 R /Prev 67 0 R >> endobj 75 0 obj << /Title 76 0 R /A 73 0 R /Parent 67 0 R /Prev 71 0 R >> endobj 71 0 obj << /Title 72 0 R /A 69 0 R /Parent 67 0 R /Next 75 0 R >> endobj 67 0 obj << /Title 68 0 R /A 65 0 R /Parent 422 0 R /Prev 11 0 R /Next 79 0 R /First 71 0 R /Last 75 0 R /Count -2 >> endobj 63 0 obj << /Title 64 0 R /A 61 0 R /Parent 11 0 R /Prev 59 0 R >> endobj 59 0 obj << /Title 60 0 R /A 57 0 R /Parent 11 0 R /Prev 51 0 R /Next 63 0 R >> endobj 55 0 obj << /Title 56 0 R /A 53 0 R /Parent 51 0 R >> endobj 51 0 obj << /Title 52 0 R /A 49 0 R /Parent 11 0 R /Prev 47 0 R /Next 59 0 R /First 55 0 R /Last 55 0 R /Count -1 >> endobj 47 0 obj << /Title 48 0 R /A 45 0 R /Parent 11 0 R /Prev 43 0 R /Next 51 0 R >> endobj 43 0 obj << /Title 44 0 R /A 41 0 R /Parent 11 0 R /Prev 39 0 R /Next 47 0 R >> endobj 39 0 obj << /Title 40 0 R /A 37 0 R /Parent 11 0 R /Prev 35 0 R /Next 43 0 R >> endobj 35 0 obj << /Title 36 0 R /A 33 0 R /Parent 11 0 R /Prev 19 0 R /Next 39 0 R >> endobj 31 0 obj << /Title 32 0 R /A 29 0 R /Parent 19 0 R /Prev 27 0 R >> endobj 27 0 obj << /Title 28 0 R /A 25 0 R /Parent 19 0 R /Prev 23 0 R /Next 31 0 R >> endobj 23 0 obj << /Title 24 0 R /A 21 0 R /Parent 19 0 R /Next 27 0 R >> endobj 19 0 obj << /Title 20 0 R /A 17 0 R /Parent 11 0 R /Prev 15 0 R /Next 35 0 R /First 23 0 R /Last 31 0 R /Count -3 >> endobj 15 0 obj << /Title 16 0 R /A 13 0 R /Parent 11 0 R /Next 19 0 R >> endobj 11 0 obj << /Title 12 0 R /A 9 0 R /Parent 422 0 R /Prev 7 0 R /Next 67 0 R /First 15 0 R /Last 63 0 R /Count -9 >> endobj 7 0 obj << /Title 8 0 R /A 5 0 R /Parent 422 0 R /Next 11 0 R >> endobj 423 0 obj << /Names [(Doc-Start) 88 0 R (Hfootnote.1) 139 0 R (Hfootnote.2) 168 0 R (Item.1) 107 0 R (Item.10) 137 0 R (Item.11) 138 0 R] /Limits [(Doc-Start) (Item.11)] >> endobj 424 0 obj << /Names [(Item.12) 154 0 R (Item.13) 179 0 R (Item.14) 196 0 R (Item.15) 304 0 R (Item.16) 305 0 R (Item.17) 306 0 R] /Limits [(Item.12) (Item.17)] >> endobj 425 0 obj << /Names [(Item.18) 307 0 R (Item.19) 308 0 R (Item.2) 110 0 R (Item.20) 327 0 R (Item.21) 328 0 R (Item.3) 111 0 R] /Limits [(Item.18) (Item.3)] >> endobj 426 0 obj << /Names [(Item.4) 114 0 R (Item.5) 115 0 R (Item.6) 116 0 R (Item.7) 117 0 R (Item.8) 132 0 R (Item.9) 133 0 R] /Limits [(Item.4) (Item.9)] >> endobj 427 0 obj << /Names [(cite.BF02) 186 0 R (cite.BFP+72) 141 0 R (cite.CLR90) 161 0 R (cite.Dem02) 187 0 R (cite.FG03) 215 0 R (cite.GS96) 214 0 R] /Limits [(cite.BF02) (cite.GS96)] >> endobj 428 0 obj << /Names [(cite.Goo96) 212 0 R (cite.HJB98) 211 0 R (cite.Rei78) 162 0 R (cite.SS92) 210 0 R (cite.SS99) 123 0 R (cite.Val90) 213 0 R] /Limits [(cite.Goo96) (cite.Val90)] >> endobj 429 0 obj << /Names [(cite.blellochsort) 99 0 R (cite.funnelsort) 245 0 R (cite.mpi) 124 0 R (cite.openmpi) 246 0 R (equation.2.1) 178 0 R (equation.2.2) 197 0 R] /Limits [(cite.blellochsort) (equation.2.2)] >> endobj 430 0 obj << /Names [(figure.1) 118 0 R (figure.2) 153 0 R (figure.3) 177 0 R (figure.4) 228 0 R (figure.5) 247 0 R (figure.6) 248 0 R] /Limits [(figure.1) (figure.6)] >> endobj 431 0 obj << /Names [(figure.7) 276 0 R (figure.8) 303 0 R (page.1) 87 0 R (page.10) 259 0 R (page.11) 280 0 R (page.12) 302 0 R] /Limits [(figure.7) (page.12)] >> endobj 432 0 obj << /Names [(page.13) 326 0 R (page.14) 347 0 R (page.2) 105 0 R (page.3) 131 0 R (page.4) 152 0 R (page.5) 167 0 R] /Limits [(page.13) (page.5)] >> endobj 433 0 obj << /Names [(page.6) 176 0 R (page.7) 195 0 R (page.8) 209 0 R (page.9) 227 0 R (section*.1) 330 0 R (section.1) 6 0 R] /Limits [(page.6) (section.1)] >> endobj 434 0 obj << /Names [(section.2) 10 0 R (section.3) 66 0 R (section.4) 78 0 R (subsection.2.1) 14 0 R (subsection.2.2) 18 0 R (subsection.2.3) 34 0 R] /Limits [(section.2) (subsection.2.3)] >> endobj 435 0 obj << /Names [(subsection.2.4) 38 0 R (subsection.2.5) 42 0 R (subsection.2.6) 46 0 R (subsection.2.7) 50 0 R (subsection.2.8) 58 0 R (subsection.2.9) 62 0 R] /Limits [(subsection.2.4) (subsection.2.9)] >> endobj 436 0 obj << /Names [(subsection.3.1) 70 0 R (subsection.3.2) 74 0 R (subsubsection.2.2.1) 22 0 R (subsubsection.2.2.2) 26 0 R (subsubsection.2.2.3) 30 0 R (subsubsection.2.7.1) 54 0 R] /Limits [(subsection.3.1) (subsubsection.2.7.1)] >> endobj 437 0 obj << /Kids [423 0 R 424 0 R 425 0 R 426 0 R 427 0 R 428 0 R] /Limits [(Doc-Start) (cite.Val90)] >> endobj 438 0 obj << /Kids [429 0 R 430 0 R 431 0 R 432 0 R 433 0 R 434 0 R] /Limits [(cite.blellochsort) (subsection.2.3)] >> endobj 439 0 obj << /Kids [435 0 R 436 0 R] /Limits [(subsection.2.4) (subsubsection.2.7.1)] >> endobj 440 0 obj << /Kids [437 0 R 438 0 R 439 0 R] /Limits [(Doc-Start) (subsubsection.2.7.1)] >> endobj 441 0 obj << /Dests 440 0 R >> endobj 442 0 obj << /Type /Catalog /Pages 421 0 R /Outlines 422 0 R /Names 441 0 R /PageMode/UseOutlines /OpenAction 81 0 R >> endobj 443 0 obj << /Author()/Title()/Subject()/Creator(LaTeX with hyperref package)/Producer(pdfTeX-1.40.9)/Keywords() /CreationDate (D:20090320151735-07'00') /ModDate (D:20090320151735-07'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.1415926-1.40.9-2.2 (Web2C 7.5.7) kpathsea version 3.5.7) >> endobj xref 0 444 0000000001 65535 f 0000000002 00000 f 0000000003 00000 f 0000000004 00000 f 0000000000 00000 f 0000000015 00000 n 0000004773 00000 n 0000852793 00000 n 0000000060 00000 n 0000000090 00000 n 0000009397 00000 n 0000852670 00000 n 0000000135 00000 n 0000000175 00000 n 0000014227 00000 n 0000852596 00000 n 0000000226 00000 n 0000000255 00000 n 0000014279 00000 n 0000852472 00000 n 0000000306 00000 n 0000000340 00000 n 0000014335 00000 n 0000852398 00000 n 0000000396 00000 n 0000000431 00000 n 0000022196 00000 n 0000852311 00000 n 0000000487 00000 n 0000000528 00000 n 0000027340 00000 n 0000852237 00000 n 0000000584 00000 n 0000000620 00000 n 0000027396 00000 n 0000852150 00000 n 0000000671 00000 n 0000000705 00000 n 0000033854 00000 n 0000852063 00000 n 0000000756 00000 n 0000000782 00000 n 0000033910 00000 n 0000851976 00000 n 0000000833 00000 n 0000000875 00000 n 0000037570 00000 n 0000851889 00000 n 0000000926 00000 n 0000000956 00000 n 0000037688 00000 n 0000851765 00000 n 0000001007 00000 n 0000001039 00000 n 0000037744 00000 n 0000851704 00000 n 0000001095 00000 n 0000001119 00000 n 0000043468 00000 n 0000851617 00000 n 0000001170 00000 n 0000001201 00000 n 0000043520 00000 n 0000851543 00000 n 0000001252 00000 n 0000001296 00000 n 0000043576 00000 n 0000851418 00000 n 0000001342 00000 n 0000001381 00000 n 0000043632 00000 n 0000851344 00000 n 0000001432 00000 n 0000001469 00000 n 0000434210 00000 n 0000851270 00000 n 0000001520 00000 n 0000001569 00000 n 0000712178 00000 n 0000851195 00000 n 0000001615 00000 n 0000001644 00000 n 0000004386 00000 n 0000004513 00000 n 0000008426 00000 n 0000004827 00000 n 0000001694 00000 n 0000004671 00000 n 0000004722 00000 n 0000849167 00000 n 0000849025 00000 n 0000850299 00000 n 0000847444 00000 n 0000848883 00000 n 0000850584 00000 n 0000847587 00000 n 0000850017 00000 n 0000849733 00000 n 0000850727 00000 n 0000712291 00000 n 0000008577 00000 n 0000008728 00000 n 0000009453 00000 n 0000008280 00000 n 0000004992 00000 n 0000008877 00000 n 0000848164 00000 n 0000008930 00000 n 0000848452 00000 n 0000850158 00000 n 0000008988 00000 n 0000009045 00000 n 0000849450 00000 n 0000847875 00000 n 0000009103 00000 n 0000009161 00000 n 0000009219 00000 n 0000009277 00000 n 0000009335 00000 n 0000849874 00000 n 0000848596 00000 n 0000850440 00000 n 0000849591 00000 n 0000718776 00000 n 0000718320 00000 n 0000013716 00000 n 0000013866 00000 n 0000014020 00000 n 0000014679 00000 n 0000013569 00000 n 0000009674 00000 n 0000014174 00000 n 0000014391 00000 n 0000014448 00000 n 0000848308 00000 n 0000848018 00000 n 0000849309 00000 n 0000014505 00000 n 0000014562 00000 n 0000014619 00000 n 0000848739 00000 n 0000712347 00000 n 0000021418 00000 n 0000021571 00000 n 0000021723 00000 n 0000021875 00000 n 0000018473 00000 n 0000026833 00000 n 0000026983 00000 n 0000022252 00000 n 0000018318 00000 n 0000014913 00000 n 0000022024 00000 n 0000022077 00000 n 0000022139 00000 n 0000019939 00000 n 0000020063 00000 n 0000020510 00000 n 0000020873 00000 n 0000021015 00000 n 0000021225 00000 n 0000718263 00000 n 0000718719 00000 n 0000027134 00000 n 0000027512 00000 n 0000026686 00000 n 0000022438 00000 n 0000027287 00000 n 0000027452 00000 n 0000033283 00000 n 0000031159 00000 n 0000033434 00000 n 0000033586 00000 n 0000034085 00000 n 0000031012 00000 n 0000027720 00000 n 0000033739 00000 n 0000033792 00000 n 0000033966 00000 n 0000034028 00000 n 0000031961 00000 n 0000032085 00000 n 0000032536 00000 n 0000032738 00000 n 0000032880 00000 n 0000033090 00000 n 0000712404 00000 n 0000718434 00000 n 0000041890 00000 n 0000042049 00000 n 0000042200 00000 n 0000042352 00000 n 0000037799 00000 n 0000037352 00000 n 0000034259 00000 n 0000037464 00000 n 0000037517 00000 n 0000037626 00000 n 0000850842 00000 n 0000042504 00000 n 0000042656 00000 n 0000042806 00000 n 0000042961 00000 n 0000043113 00000 n 0000043265 00000 n 0000220480 00000 n 0000043688 00000 n 0000041686 00000 n 0000037969 00000 n 0000043415 00000 n 0000718833 00000 n 0000718662 00000 n 0000718605 00000 n 0000718890 00000 n 0000718548 00000 n 0000718377 00000 n 0000220637 00000 n 0000046294 00000 n 0000133467 00000 n 0000224017 00000 n 0000330129 00000 n 0000220791 00000 n 0000220947 00000 n 0000221098 00000 n 0000221364 00000 n 0000046130 00000 n 0000043834 00000 n 0000221249 00000 n 0000221302 00000 n 0000046659 00000 n 0000046803 00000 n 0000123065 00000 n 0000123088 00000 n 0000123125 00000 n 0000132613 00000 n 0000133424 00000 n 0000133446 00000 n 0000133831 00000 n 0000133975 00000 n 0000210055 00000 n 0000210078 00000 n 0000210115 00000 n 0000219626 00000 n 0000220437 00000 n 0000220459 00000 n 0000712461 00000 n 0000718491 00000 n 0000434148 00000 n 0000607817 00000 n 0000435271 00000 n 0000433321 00000 n 0000433479 00000 n 0000532062 00000 n 0000433630 00000 n 0000433790 00000 n 0000433943 00000 n 0000434266 00000 n 0000223853 00000 n 0000221487 00000 n 0000434095 00000 n 0000224381 00000 n 0000224525 00000 n 0000319727 00000 n 0000319750 00000 n 0000319787 00000 n 0000329275 00000 n 0000330086 00000 n 0000330108 00000 n 0000330492 00000 n 0000330636 00000 n 0000422896 00000 n 0000422919 00000 n 0000422956 00000 n 0000432467 00000 n 0000433278 00000 n 0000433300 00000 n 0000607879 00000 n 0000607941 00000 n 0000435159 00000 n 0000434428 00000 n 0000607764 00000 n 0000435639 00000 n 0000435783 00000 n 0000521541 00000 n 0000521564 00000 n 0000521601 00000 n 0000531208 00000 n 0000532019 00000 n 0000532041 00000 n 0000532421 00000 n 0000532565 00000 n 0000597241 00000 n 0000597264 00000 n 0000597301 00000 n 0000606910 00000 n 0000607721 00000 n 0000607743 00000 n 0000609846 00000 n 0000706936 00000 n 0000707483 00000 n 0000609714 00000 n 0000608052 00000 n 0000707086 00000 n 0000707139 00000 n 0000707201 00000 n 0000707257 00000 n 0000707314 00000 n 0000707370 00000 n 0000707427 00000 n 0000610213 00000 n 0000610357 00000 n 0000696516 00000 n 0000696539 00000 n 0000696576 00000 n 0000706082 00000 n 0000706893 00000 n 0000706915 00000 n 0000710796 00000 n 0000710948 00000 n 0000711101 00000 n 0000711253 00000 n 0000711628 00000 n 0000711825 00000 n 0000712518 00000 n 0000710616 00000 n 0000707607 00000 n 0000712015 00000 n 0000712068 00000 n 0000712121 00000 n 0000847730 00000 n 0000712234 00000 n 0000711441 00000 n 0000850959 00000 n 0000715325 00000 n 0000715508 00000 n 0000715882 00000 n 0000716261 00000 n 0000716469 00000 n 0000716703 00000 n 0000717078 00000 n 0000717269 00000 n 0000717644 00000 n 0000717828 00000 n 0000718024 00000 n 0000718947 00000 n 0000715081 00000 n 0000712651 00000 n 0000718210 00000 n 0000715696 00000 n 0000716072 00000 n 0000716891 00000 n 0000717457 00000 n 0000719030 00000 n 0000719476 00000 n 0000719571 00000 n 0000719668 00000 n 0000719694 00000 n 0000719762 00000 n 0000720146 00000 n 0000720603 00000 n 0000720950 00000 n 0000721568 00000 n 0000722006 00000 n 0000722107 00000 n 0000722433 00000 n 0000722501 00000 n 0000723082 00000 n 0000723729 00000 n 0000724420 00000 n 0000724869 00000 n 0000725216 00000 n 0000725866 00000 n 0000726281 00000 n 0000727044 00000 n 0000727456 00000 n 0000727813 00000 n 0000734693 00000 n 0000734990 00000 n 0000743305 00000 n 0000743642 00000 n 0000746198 00000 n 0000746430 00000 n 0000749505 00000 n 0000749858 00000 n 0000757384 00000 n 0000757690 00000 n 0000759981 00000 n 0000760205 00000 n 0000762642 00000 n 0000762867 00000 n 0000766779 00000 n 0000767024 00000 n 0000769127 00000 n 0000769352 00000 n 0000785944 00000 n 0000786498 00000 n 0000793201 00000 n 0000793496 00000 n 0000798687 00000 n 0000798947 00000 n 0000800901 00000 n 0000801131 00000 n 0000803079 00000 n 0000803310 00000 n 0000806134 00000 n 0000806384 00000 n 0000814363 00000 n 0000814683 00000 n 0000818398 00000 n 0000818799 00000 n 0000821022 00000 n 0000821278 00000 n 0000822575 00000 n 0000822806 00000 n 0000825198 00000 n 0000825467 00000 n 0000836419 00000 n 0000836755 00000 n 0000845245 00000 n 0000845595 00000 n 0000847222 00000 n 0000851044 00000 n 0000851121 00000 n 0000852865 00000 n 0000853045 00000 n 0000853215 00000 n 0000853382 00000 n 0000853544 00000 n 0000853734 00000 n 0000853926 00000 n 0000854144 00000 n 0000854322 00000 n 0000854493 00000 n 0000854658 00000 n 0000854828 00000 n 0000855028 00000 n 0000855248 00000 n 0000855493 00000 n 0000855607 00000 n 0000855733 00000 n 0000855829 00000 n 0000855928 00000 n 0000855966 00000 n 0000856093 00000 n trailer << /Size 444 /Root 442 0 R /Info 443 0 R /ID [ ] >> startxref 856408 %%EOF CombBLAS_beta_16_2/psort-1.0/include/psort/000755 000765 000024 00000000000 13271404146 021663 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/psort-1.0/include/psort/psort_seqsort.h000644 000765 000024 00000004466 13271404146 024775 0ustar00aydinbulucstaff000000 000000 /* Copyright (c) 2009, David Cheng, Viral B. Shah. 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. */ #ifndef PSORT_SEQSORT_H #define PSORT_SEQSORT_H #include "psort_util.h" namespace vpsort { template class SeqSort { public: template void seqsort (_ValueType *first, _ValueType *last, _Compare comp) { SeqSortType *s = static_cast(this); s->real_seqsort (first, last, comp); } char *description () { SeqSortType *s = static_cast(this); return s->real_description (); } }; class STLSort : public SeqSort { public: template void real_seqsort (_ValueType *first, _ValueType *last, _Compare comp) { std::sort (first, last, comp); } char *real_description () { std::string s ("STL sort"); return const_cast(s.c_str()); } }; class STLStableSort : public SeqSort { public: template void real_seqsort (_ValueType *first, _ValueType *last, _Compare comp) { std::stable_sort (first, last, comp); } char *real_description () { std::string s("STL stable sort"); return const_cast(s.c_str()); } }; } #endif /* PSORT_SEQSORT_H */ CombBLAS_beta_16_2/psort-1.0/include/psort/funnel.h000644 000765 000024 00000034375 13271404146 023337 0ustar00aydinbulucstaff000000 000000 // The Funnelsort Project - Cache oblivious sorting algorithm implementation // Copyright (C) 2005 Kristoffer Vinther // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #ifndef FUNNEL_H_INCLUDED__ #define FUNNEL_H_INCLUDED__ #include #include #include #include #include #include #include namespace iosort { typedef unsigned short height_t; typedef unsigned short basic_order_t; typedef unsigned int order_t; template inline height_t logc(order_t k) { height_t h = 0; for( order_t i=k-1; i; h++, i/=order ); return h; } /* Computes floor(log()), not ceil(log()) :( - Also beware of little- and bigendianness(?) template<> inline height_t logf<2>(order_t k) { float f = static_cast(k); return static_cast(((*reinterpret_cast(&f)) >> 23) - 127); }*/ template inline order_t pow_of_order(height_t h) { order_t k = 1; for( ; h; h--, k*=order ); return k; } template<> inline order_t pow_of_order<2>(height_t h) { return 1< inline order_t pow_of_order<4>(height_t h) { return 1<<(2*h); } template<> inline order_t pow_of_order<8>(height_t h) { return 1<<(3*h); } template<> inline order_t pow_of_order<16>(height_t h) { return 1<<(4*h); } template class bfs_index { public: inline bfs_index() : i(1) { } inline operator order_t() const { return i; } inline bfs_index operator=(const bfs_index& rhs) { i = rhs.i; return *this; } inline bool operator==(const bfs_index& rhs) const { return i == rhs.i; } inline bool operator!=(const bfs_index& rhs) const { return i != rhs.i; } inline void child(basic_order_t ch) { i = static_cast(order*(i-1)+ch+2); } inline void parent() { /*assert( i>1 );*/ i = static_cast((i-2)/order+1); } inline basic_order_t child_index() const { /*assert( i>1 );*/ return static_cast((i+order-2) % order); } private: order_t i; }; template class default_splitter { static const int alpha = 16; public: template static inline order_t prefered_order_output(diff_type N) { return static_cast(std::sqrt(static_cast(N)/alpha)); } template static inline order_t prefered_order_input(diff_type N) { assert( false ); return static_cast(std::sqrt(static_cast(N)/alpha)); } static inline size_t switch_to_cache_aware() { return static_cast(alpha*(order+1)*(order+1)); } static inline height_t split(height_t h) { return h/2; } static inline size_t out_buffer_size(order_t k) { return static_cast(alpha*k*k); } }; template struct nop_refill { template inline bool operator()(const Merger*, FwIt, FwIt) { return false; } }; template class special_ { }; template, class Pred = std::less::value_type>, class Refiller = nop_refill, class Alloc = std::allocator::value_type> > class merge_tree { friend class special_; public: typedef unsigned int order_t; enum order_tag_ { order = Order }; enum mmth_tag_ { MAX_MERGE_TREE_HEIGHT = 16 }; typedef Splitter splitter; typedef typename std::iterator_traits::value_type value_type; typedef Alloc allocator; typedef RanIt iterator; typedef Pred predicate; template struct rebind { typedef merge_tree other; }; private: struct Node; friend struct Node; typedef typename allocator::pointer TPtr; typedef typename allocator::size_type size_type; typedef typename allocator::template rebind::other nallocator; typedef typename nallocator::pointer NPtr; struct Node { struct Edge { typedef typename allocator::pointer TPtr; typedef typename allocator::template rebind::other nallocator; typedef typename nallocator::pointer NPtr; inline void construct(order_t k, height_t height, size_t *buf_size, allocator alloc, nallocator nalloc); inline void destroy(allocator alloc, nallocator nalloc); typename allocator::template rebind::other::pointer child; typename allocator::pointer head, tail, begin, end; private: static inline void fill_dflt(TPtr b, TPtr e, allocator alloc); } edges[order]; inline void construct(order_t k, height_t height, size_t *buf_size, allocator alloc, nallocator nalloc); inline void destroy(allocator alloc, nallocator nalloc); }; typedef std::pair stream_t; typedef typename allocator::template rebind::other sallocator; typedef typename sallocator::pointer SPtr; public: inline merge_tree(order_t k) : k(k), cold(true) { construct(k); } inline merge_tree(order_t k, const allocator& alloc) : k(k), cold(true), alloc(alloc), salloc(alloc), nalloc(alloc) { construct(k); } ~merge_tree(); static inline order_t min_order() { return order; } class stream { private: SPtr pair; public: stream(SPtr pair) : pair(pair) { } inline RanIt& begin() { return pair->first; } inline RanIt& end() { return pair->second; } }; class stream_iterator { private: SPtr ptr; public: stream_iterator(SPtr ptr) : ptr(ptr) { } inline order_t operator-(const stream_iterator& rhs) { return static_cast(ptr-rhs.ptr); } inline stream operator*() { return stream(ptr); } inline stream_iterator& operator++() { ++ptr; return *this; } inline stream_iterator& operator++(int) { stream_iterator ret = stream_iterator(ptr); ++ptr; return ret; } inline bool operator==(const stream_iterator& rhs) { return ptr == rhs.ptr; } inline bool operator!=(const stream_iterator& rhs) { return ptr != rhs.ptr; } }; inline void add_stream(RanIt begin, RanIt end) { *last_stream++ = std::make_pair(begin,end); } inline stream_iterator begin() { return stream_iterator(input_streams); } inline stream_iterator end() { return stream_iterator(last_stream); } inline void reset(); inline void set_refiller(const Refiller& r) { refiller = r; } inline const Refiller& get_refiller() const { return refiller; } template inline FwIt empty(FwIt begin, FwIt end) { return empty_buffers(root, begin, end); } template inline OutIt empty(OutIt begin) { return empty_buffers(root, begin); } template inline It operator()(It begin, It end); template inline OutIt operator()(OutIt begin); private: template static inline FwIt empty_buffers(NPtr n, FwIt begin, FwIt end); template static inline OutIt empty_buffers(NPtr n, OutIt begin); static void compute_buffer_sizes(size_type *b, size_type *e); void construct(order_t k); inline void go_down(typename Node::Edge& e, bfs_index index); inline void go_down_cold(typename Node::Edge& e, bfs_index& index); void warmup(NPtr n, typename Node::Edge& output, bfs_index index); template inline FwIt copy_root(typename Node::Edge& de, bfs_index index, FwIt begin, FwIt end, std::forward_iterator_tag); template inline RIt copy_root(typename Node::Edge& de, bfs_index index, RIt begin, RIt end, std::random_access_iterator_tag); template inline OutIt copy_root(typename Node::Edge& de, bfs_index index, OutIt begin); inline TPtr copy(typename Node::Edge& de, bfs_index index, TPtr b, TPtr e); template inline FwIt fill_root(FwIt begin, FwIt end) { return special_::fill_root(this, begin, end); } template inline OutIt fill_root(OutIt begin) { return special_::fill_root(this, begin); } void fill(NPtr n, typename Node::Edge& output, bfs_index index) { special_::fill(this, n, output, index); } void fill_leaf(typename Node::Edge& output, bfs_index index) { special_::fill_leaf(this, output, index); } private: order_t k; order_t leaf_index; bool cold; NPtr root; SPtr input_streams, last_stream; Pred comp; allocator alloc; sallocator salloc; nallocator nalloc; Refiller refiller; }; /***** * Alloc::pointer iterator template specialization */ template class merge_tree { typedef typename Alloc::pointer RanIt; friend class special_; public: typedef unsigned int order_t; enum order_tag_ { order = Order }; enum mmth_tag_ { MAX_MERGE_TREE_HEIGHT = 16 }; typedef Splitter splitter; typedef typename std::iterator_traits::value_type value_type; typedef Alloc allocator; typedef typename Alloc::pointer iterator; typedef Pred predicate; // template // struct rebind { typedef merge_tree other; }; private: struct Node; friend struct Node; typedef typename allocator::pointer TPtr; typedef typename allocator::size_type size_type; typedef typename allocator::template rebind::other nallocator; typedef typename nallocator::pointer NPtr; struct Node { struct Edge { typedef typename allocator::pointer TPtr; typedef typename allocator::template rebind::other nallocator; typedef typename nallocator::pointer NPtr; inline Edge** construct(order_t k, height_t height, size_t *buf_size, Edge** edge_list, allocator alloc, nallocator nalloc); inline void destroy(allocator alloc, nallocator nalloc); typename nallocator::pointer child; typename allocator::pointer head, tail, begin, end; private: static inline void fill_dflt(TPtr b, TPtr e, allocator alloc); } edges[order]; inline Edge** construct(order_t k, height_t height, size_t *buf_size, Edge** edge_list, allocator alloc, nallocator nalloc); inline void destroy(allocator alloc, nallocator nalloc); }; typedef typename Node::Edge* stream_t; typedef typename allocator::template rebind::other sallocator; typedef typename sallocator::pointer SPtr; public: inline merge_tree(order_t k) : k(k), cold(true) { construct(k); } inline merge_tree(order_t k, const allocator& alloc) : k(k), cold(true), alloc(alloc), salloc(alloc), nalloc(alloc) { construct(k); } ~merge_tree(); static inline order_t min_order() { return order; } class stream { private: stream_t edge; public: inline stream(stream_t edge) : edge(edge) { } inline typename allocator::pointer& begin() { return edge->head; } inline typename allocator::pointer& end() { return edge->tail; } }; class stream_iterator { private: SPtr ptr; public: inline stream_iterator(SPtr ptr) : ptr(ptr) { } inline order_t operator-(const stream_iterator& rhs) { return static_cast(ptr-rhs.ptr); } inline stream operator*() { return stream(*ptr); } inline stream_iterator& operator++() { ++ptr; return *this; } inline stream_iterator& operator++(int) { stream_iterator ret = stream_iterator(ptr); ++ptr; return ret; } inline bool operator==(const stream_iterator& rhs) { return ptr == rhs.ptr; } inline bool operator!=(const stream_iterator& rhs) { return ptr != rhs.ptr; } }; inline void add_stream(typename Alloc::pointer begin, typename Alloc::pointer end) { (*last_stream)->head = begin, (*last_stream)->tail = end, ++last_stream; } inline stream_iterator begin() { return input_streams; } inline stream_iterator end() { return last_stream; } inline void reset(); inline void set_refiller(const Refiller& r) { refiller = r; } inline const Refiller& get_refiller() const { return refiller; } template inline FwIt empty(FwIt begin, FwIt end) { return empty_buffers(root, begin, end); } template inline OutIt empty(OutIt begin) { return empty_buffers(root, begin); } template inline FwIt operator()(FwIt begin, FwIt end); template inline OutIt operator()(OutIt begin); private: template static inline FwIt empty_buffers(NPtr n, FwIt begin, FwIt end); template static inline OutIt empty_buffers(NPtr n, OutIt begin); void construct(order_t k); static void compute_buffer_sizes(size_type *b, size_type *e); static inline void go_down(typename Node::Edge& e, Pred comp); static inline void go_down_cold(typename Node::Edge& e, Pred comp); static void warmup(NPtr n, typename Node::Edge& output, Pred comp); template static inline OutIt copy(typename Node::Edge& de, OutIt begin, Pred comp); template static inline FwIt copy(typename Node::Edge& de, FwIt begin, FwIt end, Pred comp, std::forward_iterator_tag); template static inline RIt copy(typename Node::Edge& de, RIt begin, RIt end, Pred comp, std::random_access_iterator_tag); template static inline OutIt fill(NPtr n, OutIt begin, Pred comp) { return special_::fill(n, begin, comp); } template static inline FwIt fill(NPtr n, FwIt begin, FwIt end, Pred comp) { return special_::fill(n, begin, end, comp); } private: order_t k; order_t leaf_index; bool cold; NPtr root; SPtr input_streams, last_stream; Pred comp; allocator alloc; sallocator salloc; nallocator nalloc; Refiller refiller; }; } // namespace iosort #include "funnel.timpl.h" #endif // FUNNEL_H_INCLUDED__ CombBLAS_beta_16_2/psort-1.0/include/psort/psort_samplesort.h000644 000765 000024 00000025037 13271404146 025463 0ustar00aydinbulucstaff000000 000000 /* Copyright (c) 2009, David Cheng, Viral B. Shah. 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. */ #ifndef PSORT_SAMPLE_H #define PSORT_SAMPLE_H #include "psort_util.h" #define MPI_PSORT_TAG 31337 namespace vpsort { using namespace std; // sample n_out elements from array in _without_ replacement // done with two sweeps of size n_out // in is touched, but restored template static void sample_splitters(_RandomAccessIter in, _Distance n_in, _ValueType *out, long n_out) { if (n_out >= n_in) { // TODO } else { _Distance *picks = new _Distance[n_out]; for (int i=0; i ((n_in - i) * drand48()); out[i] = *(in + picks[i]); *(in + picks[i]) = *(in + (n_out - i - 1)); } for (int i=n_out-1; i>=0; --i) { *(in + (n_out - i - 1)) = *(in + picks[i]); *(in + picks[i]) = out[i]; } delete [] picks; } } static inline void set_splitters(long *part_gsize, long n_parts, int nproc, long *dist, long *out) { // impl 1: get all but last about correct size; last might pick up slack long ind = 0, cum = 0; for (long i=0; i dist[ind]) { out[ind] = i; if (cum - dist[ind] > dist[ind] - cum + part_gsize[i]) { --out[ind]; cum = part_gsize[i]; } else { cum = 0; } if (++ind >= nproc - 1) break; } } } template static void redist (int *from_dist, _ValueType *current, int *to_dist, _ValueType *final, int rank, int nproc, MPI_Datatype &MPI_valueType, MPI_Comm comm) { int ** starch = new int*[nproc]; for (int i = 0; i < nproc; ++i) { starch[i] = new int[nproc]; for (int j = 0; j < nproc; ++j) { starch[i][j] = 0; } } int i = 0, j=0; int from_delta = from_dist[0], to_delta = to_dist[0]; while ((i < nproc) && (j < nproc)) { int diff = from_delta - to_delta; if (diff == 0) { starch[i][j] = from_delta; ++i; ++j; from_delta = from_dist[i]; to_delta = to_dist[j]; } else if (diff > 0) { starch[i][j] = to_delta; ++j; from_delta -= to_delta; to_delta = to_dist[j]; } else { starch[i][j] = from_delta; ++i; to_delta -= from_delta; from_delta = from_dist[i]; } } MPI_Request send_req[nproc], recv_req[nproc]; MPI_Status status; int sendl = 0, recvl = 0; for (int p = 0; p <= rank; ++p) { int torecv = starch[p][rank]; if (torecv) { MPI_Irecv (&final[recvl], torecv, MPI_valueType, p, MPI_PSORT_TAG, comm, &recv_req[p]); recvl += torecv; } int tosend = starch[rank][p]; if (tosend) { MPI_Isend (¤t[sendl], tosend, MPI_valueType, p, MPI_PSORT_TAG, comm, &send_req[p]); sendl += tosend; } } int sendr = 0, recvr = 0; for (int p = nproc-1; p > rank; --p) { int torecv = starch[p][rank]; if (torecv) { recvr += torecv; MPI_Irecv (&final[to_dist[rank]-recvr], torecv, MPI_valueType, p, MPI_PSORT_TAG, comm, &recv_req[p]); } int tosend = starch[rank][p]; if (tosend) { sendr += tosend; MPI_Isend (¤t[from_dist[rank]-sendr], tosend, MPI_valueType, p, MPI_PSORT_TAG, comm, &send_req[p]); } } for (int p = 0; p < nproc; ++p) { if ( starch[rank][p] ) MPI_Wait(&send_req[p], &status); if ( starch[p][rank] ) MPI_Wait(&recv_req[p], &status); } for (int i = 0; i < nproc; ++i) delete [] starch[i]; delete [] starch; } template void parallel_samplesort(_RandomAccessIter first, _RandomAccessIter last, _Compare comp, long *dist, SeqSort<_SeqSortType> &mysort, long s, long k, MPI_Comm comm) { typedef typename iterator_traits<_RandomAccessIter>::value_type _ValueType; typedef typename iterator_traits<_RandomAccessIter>::difference_type _Distance; int nproc, rank; MPI_Comm_size(comm, &nproc); MPI_Comm_rank(comm, &rank); MPI_Datatype MPI_valueType, MPI_distanceType; MPI_Type_contiguous (sizeof(_ValueType), MPI_CHAR, &MPI_valueType); MPI_Type_commit (&MPI_valueType); MPI_Type_contiguous (sizeof(_Distance), MPI_CHAR, &MPI_distanceType); MPI_Type_commit (&MPI_distanceType); _Distance n_loc = last - first; progress (rank, 0, "Sample splitters", comm); // randomly pick s*k sample splitters long n_samp = s*k; _ValueType *s_splitters = new _ValueType[n_samp]; sample_splitters(first, n_loc, s_splitters, n_samp); // send the sample splitters to the root long n_parts = nproc * k - 1; _ValueType *p_splitters = new _ValueType[n_parts]; if (rank != 0) { MPI_Gather(s_splitters, n_samp, MPI_valueType, NULL, 0, MPI_valueType, 0, comm); } else { _ValueType *gath_splitters = new _ValueType[nproc * n_samp]; MPI_Gather(s_splitters, n_samp, MPI_valueType, gath_splitters, n_samp, MPI_valueType, 0, comm); // root sorts sample splitters serially sort(gath_splitters, gath_splitters + (nproc * n_samp), comp); // root picks pk-1 splitters, broadcasts double stride = (double) (nproc * n_samp) / (nproc * k); for (int i=0; i boundaries[cur]) { ++cur; } part_proc[i] = cur; out_counts[cur] += part_lsize[i]; } delete [] boundaries; delete [] part_lsize; long curs[nproc]; _ValueType **s_bufs = new _ValueType*[nproc]; for (int i=0; i void parallel_samplesort(_RandomAccessIter first, _RandomAccessIter last, long *dist, MPI_Comm comm) { typedef typename iterator_traits<_RandomAccessIter>::value_type _ValueType; STLSort stl_sort; int nproc; MPI_Comm_size (comm, &nproc); long s = nproc, k = nproc; parallel_samplesort (first, last, less<_ValueType>(), dist, stl_sort, s, k, comm); } } /* namespace vpsort */ #endif /* PSORT_SAMPLE_H */ CombBLAS_beta_16_2/psort-1.0/include/psort/sort.timpl.h000644 000765 000024 00000026726 13271404146 024164 0ustar00aydinbulucstaff000000 000000 // The Funnelsort Project - Cache oblivious sorting algorithm implementation // Copyright (C) 2005 Kristoffer Vinther // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include #include #include #include #include #include namespace iosort { namespace base_ { enum tag_RECURSELIMIT { RECURSELIMIT = 24 }; template inline void insertion_sort(It begin, It end, Pred comp) { for( It j=begin+1; j::value_type e = *j; for( It i=j; begin inline T median(const T& a, const T& b, const T& c, Pred comp) { if( comp(a,b) ) if( comp(b,c) ) return b; else if( comp(a,c) ) return c; else return a; else if( comp(a,c) ) return a; else if( comp(b,c) ) return c; else return b; } template inline BidIt partition(BidIt begin, BidIt end, T pivot, Pred comp) { for( ;; ) { for( ; comp(*begin,pivot); ++begin ); for( --end; comp(pivot,*end); --end ); if( !(begin inline void quicksort(RanIt First, RanIt Last, Pred comp) { typename std::iterator_traits::difference_type Count = Last-First; while( RECURSELIMIT < Count ) { RanIt part = partition(First, Last, median(*First,*(First+(Last-First)/2),*(Last-1),comp),comp); if( part-First < Last-part ) // loop on larger half quicksort(First, part, comp), First = part; else quicksort(part, Last, comp), Last = part; Count = Last-First; } if( Count > 1 ) insertion_sort(First, Last, comp); } template inline Int logc(Int k) { height_t h = 0; for( order_t i=k-1; i; h++, i/=2 ); return h; } template void batcher_sort(RanIt begin, RanIt end, Pred comp) { typedef typename std::iterator_traits::difference_type ItDiff; ItDiff t = logc(end-begin)-1; ItDiff p = 1< void inplace_base_sort(RanIt begin, RanIt end, Pred comp) { #ifdef __ia64__ batcher_sort(begin, end, comp); #else // __ia64__ quicksort(begin, end, comp); #endif // __ia64__ } } // namespace base_ template class stream_slices { typedef typename std::iterator_traits::difference_type diff; public: class iterator : public std::iterator, diff> { friend class stream_slices; public: inline iterator& operator++() { begin = end; end = end+run_size; if( --big_runs == 0 ) --run_size; return *this; } inline iterator& operator--() { end = begin; begin = end-run_size; if( ++big_runs == 0 ) ++run_size; return *this; } std::pair operator*() { return make_pair(begin,end); } bool operator==(const iterator& rhs) { return begin == rhs.begin; } private: inline iterator(RanIt begin, diff run_size, size_t big_runs) : begin(begin), run_size(run_size), big_runs(big_runs) { if( big_runs ) ++run_size; end = begin+run_size; } RanIt begin, end; diff run_size; size_t big_runs; }; stream_slices(RanIt begin, RanIt end) : b(begin), e(end), order_(Splitter::prefered_order_output(end-begin)) { if( order_ < 2 ) throw std::logic_error("Splitter::prefered_order_output returned less than two"); run_size = (end-begin)/order_; size_t rem = static_cast((end-begin) % order_); run_size += rem/order_; big_runs = rem % order_; } inline diff order() const { return order_; } inline iterator begin() { return iterator(b, run_size, big_runs); } inline iterator end() { return iterator(e, run_size, big_runs); } private: RanIt b, e; diff run_size; size_t order_, big_runs; }; template inline Diff run_size_(Diff d) { size_t order = Splitter::prefered_order_output(d); Diff run_size = d/order; size_t rem = static_cast(d % order); run_size += rem/order; size_t big_runs = rem % order; if( big_runs ) run_size++; return run_size; } template inline std::pair build_cache_(Diff run_size, MAlloc& malloc, TAlloc& talloc) { Merger *begin_cache, *end_cache; int rec_calls = 0; for( Diff d=run_size; d>static_cast(Splitter::switch_to_cache_aware()); d=run_size_(d), ++rec_calls ); begin_cache = end_cache = malloc.allocate(rec_calls); for( Diff d=run_size; d>static_cast(Splitter::switch_to_cache_aware()); d=run_size_(d), ++end_cache ) new(end_cache) Merger(Splitter::prefered_order_output(d), talloc); return make_pair(begin_cache,end_cache); } template void merge_sort_(It begin, It end, OutIt dest, Merger* cache, Alloc alloc); template inline void sort_streams_(MPtr cache, It begin, size_t order, size_t run_size, size_t big_runs, Alloc alloc) { typedef typename std::iterator_traits::value_type T; typename Alloc::template rebind::other talloc(alloc); typedef typename Alloc::template rebind::other::pointer TPtr; TPtr tmp = talloc.allocate((size_t)(run_size)); It last = begin; size_t i; if( big_runs ) { merge_sort_(last, last+run_size, std::raw_storage_iterator(tmp), cache, alloc); for( i=1, last+=run_size; i!=big_runs; i++, last+=run_size ) merge_sort_(last, last+run_size, last-run_size, cache, alloc); run_size--; for( ; i!=order; i++, last+=run_size ) merge_sort_(last, last+run_size, last-run_size-1, cache, alloc); run_size++; } else { merge_sort_(last, last+run_size, tmp, cache, alloc); for( --order, last+=run_size; order; --order, last+=run_size ) merge_sort_(last, last+run_size, last-run_size, cache, alloc); } std::copy(tmp, tmp+run_size, last-run_size); for( TPtr p=tmp+run_size-1; p>=tmp; --p ) talloc.destroy(p); talloc.deallocate(tmp,static_cast(run_size)); } template inline void add_sorted_streams_(MPtr merger, It end, size_t order, size_t run_size, size_t big_runs) { if( big_runs ) { merger->add_stream(end-run_size, end); end -= run_size; --run_size, --big_runs; for( --order; order!=big_runs; --order, end-=run_size ) merger->add_stream(end-run_size, end); ++run_size; for( ; order; --order, end-=run_size ) merger->add_stream(end-run_size, end); } else for( ; order; --order, end-=run_size ) merger->add_stream(end-run_size, end); } template inline void add_streams_(MPtr merger, It begin, size_t order, size_t run_size, size_t big_runs, Pred comp) { size_t i; for( i=0; i!=order-big_runs; ++i, begin+=run_size ) base_::inplace_base_sort(begin, begin+run_size, comp); ++run_size; for( ; i!=order; ++i, begin+=run_size ) base_::inplace_base_sort(begin, begin+run_size, comp); for( ; i!=order-big_runs; --i, begin-=run_size ) merger->add_stream(begin-run_size, begin); --run_size; for( ; i; --i, begin-=run_size ) merger->add_stream(begin-run_size, begin); } template void merge_sort_(It begin, It end, OutIt dest, Merger* cache, Alloc alloc) { typedef typename std::iterator_traits::value_type T; typedef typename std::iterator_traits::difference_type ItDiff; typedef typename Merger::stream Stream; order_t order = Splitter::prefered_order_output(end-begin); if( order < 2 ) throw std::logic_error("Splitter::prefered_order_output returned less than two"); ItDiff run_size = (end-begin)/order; size_t rem = (size_t)((end-begin) % order); run_size += rem/order; size_t big_runs = rem % order; cache->reset(); if( run_size > static_cast(Splitter::switch_to_cache_aware()) ) { if( big_runs ) run_size++; sort_streams_(cache+1,begin,order,run_size,big_runs,alloc); add_sorted_streams_(cache,end,order,run_size,big_runs); } else add_streams_(cache,begin,order,run_size,big_runs, typename Merger::predicate()); (*cache)(dest); } template inline void merge_sort(It begin, It end, OutIt dest, const typename Merger::allocator& alloc) { typedef typename std::iterator_traits::difference_type ItDiff; if( end-begin > static_cast(Splitter::switch_to_cache_aware()) ) { order_t order = Splitter::prefered_order_output(end-begin); if( order < 2 ) throw std::logic_error("Splitter::prefered_order_output returned less than two"); ItDiff run_size = (end-begin)/order; size_t rem = (size_t)((end-begin) % order); run_size += rem/order; size_t big_runs = rem % order; if( run_size > static_cast(Splitter::switch_to_cache_aware()) ) { if( big_runs ) run_size++; typename Merger::allocator::template rebind::other malloc(alloc); std::pair cache = build_cache_(run_size, malloc, alloc); sort_streams_(cache.first, begin, order, run_size, big_runs, alloc); for( Merger *pm=cache.second-1; pm>=cache.first; --pm ) malloc.destroy(pm); malloc.deallocate(cache.first, cache.second-cache.first); } Merger merger(order,alloc); if( run_size > static_cast(Splitter::switch_to_cache_aware()) ) add_sorted_streams_(&merger, end, order, run_size, big_runs); else add_streams_(&merger, begin, order, run_size, big_runs, typename Merger::predicate()); #ifdef _DEBUG OutIt e = merger(dest, dest+(end-begin)); merger.empty(dest, dest+(end-begin)); size_t N = 0; for( typename Merger::stream_iterator i=merger.begin(); i!=merger.end(); ++i ) N += (*i).end()-(*i).begin(); assert( e == dest+(end-begin) ); #else // _DEBUG merger(dest); #endif // _DEBUG } else { base_::inplace_base_sort(begin, end, typename Merger::predicate()); std::copy(begin, end, dest); } } } // namespace iosort CombBLAS_beta_16_2/psort-1.0/include/psort/psort_util.h000644 000765 000024 00000021055 13271404146 024243 0ustar00aydinbulucstaff000000 000000 /* Copyright (c) 2009, David Cheng, Viral B. Shah. 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. */ #ifndef PSORT_UTIL_H #define PSORT_UTIL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef PSORTDEBUG #define PSORT_DEBUG(_a) _a #else #define PSORT_DEBUG(_a) #endif #include "psort_seqsort.h" #include "psort_splitters.h" #include "psort_alltoall.h" #include "psort_merge.h" namespace vpsort { using namespace std; static double psort_timing[10]; template bool is_sorted (_RandomAccessIter first, _RandomAccessIter last, _Compare comp, MPI_Comm comm) { int nproc, rank; MPI_Comm_size (comm, &nproc); MPI_Comm_rank (comm, &rank); int not_sorted = 0; typedef typename iterator_traits<_RandomAccessIter>::pointer _IterType; typedef typename iterator_traits<_RandomAccessIter>::value_type _ValueType; for (_IterType iter=first+1; iter bool is_sorted (_RandomAccessIter first, _RandomAccessIter last, MPI_Comm comm) { typedef typename iterator_traits<_RandomAccessIter>::value_type _ValueType; return is_sorted (first, last, std::less<_ValueType>(), comm); } static inline void progress (int rank, int step, const char *s, MPI_Comm comm) { MPI_Barrier (comm); psort_timing[step] = MPI_Wtime (); if (rank == 0) { PSORT_DEBUG (cout << step << ". " << s << endl;); } } template void print_perf_data (_Distance *dist, MPI_Comm comm) { STLSort stl_sort; MedianSplit median_split; OOPTreeMerge oop_tree_merge; print_perf_data (dist, stl_sort, median_split, oop_tree_merge, comm); } template void print_perf_data (_Distance *dist, SeqSort<_SeqSortType> &mysort, Split<_SplitType> &mysplit, Merge<_MergeType> &mymerge, MPI_Comm comm) { int rank, nproc; MPI_Comm_size (comm, &nproc); MPI_Comm_rank (comm, &rank); /* AL: This is the original code. This is legal in C but not in C++ because C++ string literals are const char* instead of just char*, so this code has constness errors. char **stage = new char*[5]; stage[1] = mysort.description(); stage[2] = mysplit.description(); stage[3] = "alltoall"; stage[4] = mymerge.description(); */ const char *stage[5] = { "", mysort.description(), mysplit.description(), "alltoall", mymerge.description() }; if (rank == 0) cout << endl; double rtime[5]; for (int i=1; i<=4; ++i) { double time_i = psort_timing[i] - psort_timing[i-1]; if (rank == 0) { cout << i << ". " << setw(30) << left << stage[i] << setw(10) << right << ": " << setprecision(6) << time_i << " sec" << endl; rtime[i] = time_i; } } long n_sort = 0L; for (int i=0; i void print_perf_data_samplesort (_Distance *dist, SeqSort<_SeqSortType> &mysort, MPI_Comm comm) { int rank, nproc; MPI_Comm_size (comm, &nproc); MPI_Comm_rank (comm, &rank); /* AL: This is the original code. This is legal in C but not in C++ because C++ string literals are const char* instead of just char*, so this code has constness errors. char **stage = new char*[6]; stage[1] = "sample splitters"; stage[2] = "partition"; stage[3] = "alltoall"; stage[4] = mysort.description(); stage[5] = "adjust boundaries"; */ const char* stage[6] = { "", "sample splitters", "partition", "alltoall", mysort.description(), "adjust boundaries" }; if (rank == 0) cout << endl; double rtime[6]; for (int i=1; i<=5; ++i) { double time_i = psort_timing[i] - psort_timing[i-1]; if (rank == 0) { cout << i << ". " << setw(30) << left << stage[i] << setw(10) << right << ": " << setprecision(6) << time_i << " sec" << endl; rtime[i] = time_i; } } long n_sort = 0L; for (int i=0; i class Split { public: template void split (_RandomAccessIter first, _RandomAccessIter last, _Distance *dist, _Compare comp, vector< vector<_Distance> > &right_ends, MPI_Datatype &MPI_valueType, MPI_Datatype &MPI_distanceType, MPI_Comm comm) { SplitType *s = static_cast(this); s->real_split(first, last, dist, comp, right_ends, MPI_valueType, MPI_distanceType, comm); } char *description () { SplitType *s = static_cast(this); return s->real_description (); } }; class MedianSplit : public Split { public: char *real_description () { string s ("Median splitter"); return const_cast(s.c_str()); } template void real_split (_RandomAccessIter first, _RandomAccessIter last, _Distance *dist, _Compare comp, vector< vector<_Distance> > &right_ends, MPI_Datatype &MPI_valueType, MPI_Datatype &MPI_distanceType, MPI_Comm comm) { typedef typename iterator_traits<_RandomAccessIter>::value_type _ValueType; int nproc, rank; MPI_Comm_size (comm, &nproc); MPI_Comm_rank (comm, &rank); int n_real = nproc; for (int i = 0; i < nproc; ++i) if (dist[i] == 0) { n_real = i; break; } copy (dist, dist + nproc, right_ends[nproc].begin()); // union of [0, right_end[i+1]) on each processor produces dist[i] total values // AL: conversion to remove dependence on C99 feature. // original: _Distance targets[nproc-1]; vector<_Distance> targets_v(nproc-1); _Distance *targets = &targets_v.at(0); partial_sum (dist, dist + (nproc - 1), targets); // keep a list of ranges, trying to "activate" them at each branch vector< pair<_RandomAccessIter, _RandomAccessIter> > d_ranges(nproc - 1); vector< pair<_Distance *, _Distance *> > t_ranges(nproc - 1); d_ranges[0] = pair<_RandomAccessIter, _RandomAccessIter>(first, last); t_ranges[0] = pair<_Distance *, _Distance *>(targets, targets + (nproc - 1)); // invariant: subdist[i][rank] == d_ranges[i].second - d_ranges[i].first // amount of data each proc still has in the search vector< vector<_Distance> > subdist(nproc - 1, vector<_Distance>(nproc)); copy (dist, dist + nproc, subdist[0].begin()); // for each processor, d_ranges - first vector< vector<_Distance> > outleft(nproc - 1, vector<_Distance>(nproc, 0)); #ifdef PSORTDEBUG double t_begin=0, t_query=0, t_bsearch=0, t_gather=0, t_finish=0; double t_allquery=0, t_allbsearch=0, t_allgather=0, t_allfinish=0; #endif for (int n_act = 1; n_act > 0; ) { for (int k=0; k queries(n_act); for (int k = 0; k < n_act; ++k) { // AL: conversion to remove dependence on C99 feature. // original: _Distance ms_perm[n_real]; vector<_Distance> ms_perm_v(n_real); _Distance *ms_perm = &ms_perm_v.at(0); for (int i = 0; i < n_real; ++i) ms_perm[i] = i * n_act + k; sort (ms_perm, ms_perm + n_real, PermCompare< _ValueType, _Compare> (medians, comp)); _Distance mid = accumulate( subdist[k].begin(), subdist[k].end(), 0) / 2; _Distance query_ind = -1; for (int i = 0; i < n_real; ++i) { if (subdist[k][ms_perm[i] / n_act] == 0) continue; mid -= subdist[k][ms_perm[i] / n_act]; if (mid <= 0) { query_ind = ms_perm[i]; break; } } assert(query_ind >= 0); queries[k] = medians[query_ind]; } delete [] medians; #ifdef PSORTDEBUG MPI_Barrier (MPI_COMM_WORLD); t_query = MPI_Wtime() - t_begin; #endif //------- find min and max ranks of the guesses // AL: conversion to remove dependence on C99 feature. // original: _Distance ind_local[2 * n_act]; vector<_Distance> ind_local_v(2 * n_act); _Distance *ind_local = &ind_local_v.at(0); for (int k = 0; k < n_act; ++k) { pair<_RandomAccessIter, _RandomAccessIter> ind_local_p = equal_range (d_ranges[k].first, d_ranges[k].second, queries[k], comp); ind_local[2 * k] = ind_local_p.first - first; ind_local[2 * k + 1] = ind_local_p.second - first; } #ifdef PSORTDEBUG MPI_Barrier (MPI_COMM_WORLD); t_bsearch = MPI_Wtime() - t_begin - t_query; #endif // AL: conversion to remove dependence on C99 feature. // original: _Distance ind_all[2 * n_act * nproc]; vector<_Distance> ind_all_v(2 * n_act * nproc); _Distance *ind_all = &ind_all_v.at(0); MPI_Allgather (ind_local, 2 * n_act, MPI_distanceType, ind_all, 2 * n_act, MPI_distanceType, comm); // sum to get the global range of indices vector > ind_global(n_act); for (int k = 0; k < n_act; ++k) { ind_global[k] = make_pair(0, 0); for (int i = 0; i < nproc; ++i) { ind_global[k].first += ind_all[2 * (i * n_act + k)]; ind_global[k].second += ind_all[2 * (i * n_act + k) + 1]; } } #ifdef PSORTDEBUG MPI_Barrier (MPI_COMM_WORLD); t_gather = MPI_Wtime() - t_begin - t_query - t_bsearch; #endif // state to pass on to next iteration vector< pair<_RandomAccessIter, _RandomAccessIter> > d_ranges_x(nproc - 1); vector< pair<_Distance *, _Distance *> > t_ranges_x(nproc - 1); vector< vector<_Distance> > subdist_x(nproc - 1, vector<_Distance>(nproc)); vector< vector<_Distance> > outleft_x(nproc - 1, vector<_Distance>(nproc, 0)); int n_act_x = 0; for (int k = 0; k < n_act; ++k) { _Distance *split_low = lower_bound (t_ranges[k].first, t_ranges[k].second, ind_global[k].first); _Distance *split_high = upper_bound (t_ranges[k].first, t_ranges[k].second, ind_global[k].second); // iterate over targets we hit for (_Distance *s = split_low; s != split_high; ++s) { assert (*s > 0); // a bit sloppy: if more than one target in range, excess won't zero out _Distance excess = *s - ind_global[k].first; // low procs to high take excess for stability for (int i = 0; i < nproc; ++i) { _Distance amount = min (ind_all[2 * (i * n_act + k)] + excess, ind_all[2 * (i * n_act + k) + 1]); right_ends[(s - targets) + 1][i] = amount; excess -= amount - ind_all[2 * (i * n_act + k)]; } } if ((split_low - t_ranges[k].first) > 0) { t_ranges_x[n_act_x] = make_pair (t_ranges[k].first, split_low); // lop off local_ind_low..end d_ranges_x[n_act_x] = make_pair (d_ranges[k].first, first + ind_local[2 * k]); for (int i = 0; i < nproc; ++i) { subdist_x[n_act_x][i] = ind_all[2 * (i * n_act + k)] - outleft[k][i]; outleft_x[n_act_x][i] = outleft[k][i]; } ++n_act_x; } if ((t_ranges[k].second - split_high) > 0) { t_ranges_x[n_act_x] = make_pair (split_high, t_ranges[k].second); // lop off begin..local_ind_high d_ranges_x[n_act_x] = make_pair (first + ind_local[2 * k + 1], d_ranges[k].second); for (int i = 0; i < nproc; ++i) { subdist_x[n_act_x][i] = outleft[k][i] + subdist[k][i] - ind_all[2 * (i * n_act + k) + 1]; outleft_x[n_act_x][i] = ind_all[2 * (i * n_act + k) + 1]; } ++n_act_x; } } t_ranges = t_ranges_x; d_ranges = d_ranges_x; subdist = subdist_x; outleft = outleft_x; n_act = n_act_x; #ifdef PSORTDEBUG MPI_Barrier (MPI_COMM_WORLD); t_finish = MPI_Wtime() - t_begin - t_query - t_bsearch - t_gather; t_allquery += t_query; t_allbsearch += t_bsearch; t_allgather += t_gather; t_allfinish += t_finish; #endif } #ifdef PSORTDEBUG if (rank == 0) std::cout << "t_query = " << t_allquery << ", t_bsearch = " << t_allbsearch << ", t_gather = " << t_allgather << ", t_finish = " << t_allfinish << std::endl; #endif } private: template class PermCompare { private: T *weights; _Compare comp; public: PermCompare(T *w, _Compare c) : weights(w), comp(c) {} bool operator()(int a, int b) { return comp(weights[a], weights[b]); } }; }; class SampleSplit : public Split { public: char *real_description () { string s ("Sample splitter"); return const_cast(s.c_str()); } template void real_split (_RandomAccessIter first, _RandomAccessIter last, _Distance *dist, _Compare comp, vector< vector<_Distance> > &right_ends, MPI_Datatype &MPI_valueType, MPI_Datatype &MPI_distanceType, MPI_Comm comm) { int nproc, rank; MPI_Comm_size (comm, &nproc); MPI_Comm_rank (comm, &rank); // union of [0, right_end) on each processor produces split_ind total values _Distance targets[nproc-1]; partial_sum (dist, dist + (nproc - 1), targets); fill (right_ends[0].begin(), right_ends[0].end(), 0); for (int i = 0; i < nproc-1; ++i) { sample_split_iter (first, last, dist, targets[i], comp, right_ends[i + 1], MPI_valueType, MPI_distanceType, comm); } copy (dist, dist + nproc, right_ends[nproc].begin()); } private: // return an integer uniformly distributed from [0, max) inline static int random_number(int max) { #ifdef _MSC_VER return (int) (max * (double(rand()) / RAND_MAX)); #else return (int) (max * drand48()); #endif } template static void sample_split_iter (_RandomAccessIter first, _RandomAccessIter last, _Distance *dist, _Distance target, _Compare comp, vector<_Distance> &split_inds, MPI_Datatype &MPI_valueType, MPI_Datatype &MPI_distanceType, MPI_Comm comm) { typedef typename iterator_traits<_RandomAccessIter>::value_type _ValueType; int nproc, rank; MPI_Comm_size (comm, &nproc); MPI_Comm_rank (comm, &rank); // invariant: subdist[rank] == last - start _Distance subdist[nproc]; // amount of data each proc still has in the search _Distance outleft[nproc]; // keep track of start - first copy (dist, dist + nproc, subdist); fill (outleft, outleft + nproc, 0); _RandomAccessIter start = first; _ValueType query; _Distance sampler; while (true) { assert(subdist[rank] == last - start); // generate a guess // root decides who gets to guess if (!rank) { _Distance distcum[nproc]; partial_sum (subdist, subdist + nproc, distcum); // ensure sampler is a rank with data; lower is that first rank _Distance lower = lower_bound(distcum, distcum + nproc, 1) - distcum; sampler = lower_bound (distcum + lower, distcum + nproc, random_number (distcum[nproc - 1])) - distcum; } MPI_Bcast (&sampler, 1, MPI_distanceType, 0, comm); // take the guess if (rank == sampler) { query = *(start + random_number (subdist[rank])); } MPI_Bcast (&query, 1, MPI_valueType, sampler, comm); // find min and max ranks of the guess pair<_RandomAccessIter, _RandomAccessIter> ind_range_p = equal_range (start, last, query, comp); // get the absolute local index of the query _Distance ind_range[2] = { ind_range_p.first - first, ind_range_p.second - first }; _Distance ind_all[2 * nproc]; MPI_Allgather (ind_range, 2, MPI_distanceType, ind_all, 2 , MPI_distanceType, comm); // scan to get the global range of indices of the query _Distance g_ind_min = 0, g_ind_max = 0; for (int i = 0; i < nproc; ++i) { g_ind_min += ind_all[2 * i]; g_ind_max += ind_all[2 * i + 1]; } // if target in range, low procs to high take excess for stability if (g_ind_min <= target && target <= g_ind_max) { _Distance excess = target - g_ind_min; for (int i = 0; i < nproc; ++i) { split_inds[i] = min (ind_all[2 * i] + excess, ind_all[2 * i + 1]); excess -= split_inds[i] - ind_all[2 * i]; } return; } // set up for next iteration of binary search if (target < g_ind_min) { last = first + ind_all[2 * rank]; assert(outleft[rank] == start - first); for (int i = 0; i < nproc; ++i) { subdist[i] = ind_all[2 * i] - outleft[i]; } } else { start = first + ind_all[2 * rank + 1]; for (int i = 0; i < nproc; ++i) { subdist[i] = outleft[i] + subdist[i] - ind_all[2 * i + 1]; outleft[i] = ind_all[2 * i + 1]; } } } } }; } /* namespace vpsort */ #endif /* PSORT_SPLITTERS_H */ CombBLAS_beta_16_2/psort-1.0/include/psort/psort.h000644 000765 000024 00000011546 13271404146 023212 0ustar00aydinbulucstaff000000 000000 /* Copyright (c) 2009, David Cheng, Viral B. Shah. 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. */ #ifndef PSORT_H #define PSORT_H #include "psort_util.h" namespace vpsort { using namespace std; /* SeqSort can be STLSort, STLStableSort Split can be MedianSplit, SampleSplit Merge can be FlatMerge, TreeMerge, OOPTreeMerge, FunnelMerge2, FunnelMerge4 */ template void parallel_sort (_RandomAccessIter first, _RandomAccessIter last, _Compare comp, long *dist_in, SeqSort<_SeqSortType> &mysort, Split<_SplitType> &mysplit, Merge<_MergeType> &mymerge, MPI_Comm comm) { typedef typename iterator_traits<_RandomAccessIter>::value_type _ValueType; typedef typename iterator_traits<_RandomAccessIter>::difference_type _Distance; int nproc, rank; MPI_Comm_size (comm, &nproc); MPI_Comm_rank (comm, &rank); MPI_Datatype MPI_valueType, MPI_distanceType; MPI_Type_contiguous (sizeof(_ValueType), MPI_CHAR, &MPI_valueType); MPI_Type_commit (&MPI_valueType); // ABAB: Any type committed needs to be freed to claim storage MPI_Type_contiguous (sizeof(_Distance), MPI_CHAR, &MPI_distanceType); MPI_Type_commit (&MPI_distanceType); _Distance *dist = new _Distance[nproc]; for (int i=0; i > right_ends(nproc + 1, vector<_Distance>(nproc, 0)); // ABAB: The following hangs if some dist entries are zeros, i.e. first = last for some processors mysplit.split (first, last, dist, comp, right_ends, MPI_valueType, MPI_distanceType, comm); // Communicate to destination progress (rank, 2, const_cast(string("alltoall").c_str()), comm); _Distance n_loc = last - first; _ValueType *trans_data = new _ValueType[n_loc]; _Distance *boundaries = new _Distance[nproc+1]; alltoall (right_ends, first, last, trans_data, boundaries, MPI_valueType, MPI_distanceType, comm); // Merge streams from all processors // progress (rank, 3, mymerge.description()); mymerge.merge (trans_data, first, boundaries, nproc, comp); delete [] boundaries; delete [] dist; delete [] trans_data; MPI_Type_free (&MPI_valueType); MPI_Type_free (&MPI_distanceType); // Finish progress (rank, 4, const_cast(string("finish").c_str()), comm); return; } template void parallel_sort (_RandomAccessIter first, _RandomAccessIter last, _Compare comp, long *dist, MPI_Comm comm) { STLSort mysort; MedianSplit mysplit; OOPTreeMerge mymerge; parallel_sort (first, last, comp, dist, mysort, mysplit, mymerge, comm); } template void parallel_sort (_RandomAccessIter first, _RandomAccessIter last, long *dist, MPI_Comm comm) { typedef typename iterator_traits<_RandomAccessIter>::value_type _ValueType; STLSort mysort; MedianSplit mysplit; OOPTreeMerge mymerge; parallel_sort (first, last, less<_ValueType>(), dist, mysort, mysplit, mymerge, comm); } } #endif /* PSORT_H */ CombBLAS_beta_16_2/psort-1.0/include/psort/MersenneTwister.h000644 000765 000024 00000033143 13271404146 025176 0ustar00aydinbulucstaff000000 000000 // MersenneTwister.h // Mersenne Twister random number generator -- a C++ class MTRand // Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus // Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com // The Mersenne Twister is an algorithm for generating random numbers. It // was designed with consideration of the flaws in various other generators. // The period, 2^19937-1, and the order of equidistribution, 623 dimensions, // are far greater. The generator is also fast; it avoids multiplication and // division, and it benefits from caches and pipelines. For more information // see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html // Reference // M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally // Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on // Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. // Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, // Copyright (C) 2000 - 2003, Richard J. Wagner // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // 2. 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. // // 3. The names of its contributors may not 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. // The original code included the following notice: // // When you use this, send an email to: matumoto@math.keio.ac.jp // with an appropriate reference to your work. // // It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu // when you write. #ifndef MERSENNETWISTER_H #define MERSENNETWISTER_H // Not thread safe (unless auto-initialization is avoided and each thread has // its own MTRand object) #include #include #include #include #include class MTRand { // Data public: typedef unsigned long uint32; // unsigned integer type, at least 32 bits enum { N = 624 }; // length of state vector enum { SAVE = N + 1 }; // length of array for save() protected: enum { M = 397 }; // period parameter uint32 state[N]; // internal state uint32 *pNext; // next value to get from state int left; // number of values left before reload needed //Methods public: MTRand( const uint32& oneSeed ); // initialize with a simple uint32 MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array MTRand(); // auto-initialize with /dev/urandom or time() and clock() // Do NOT use for CRYPTOGRAPHY without securely hashing several returned // values together, otherwise the generator state can be learned after // reading 624 consecutive values. // Access to 32-bit random numbers double rand(); // real number in [0,1] double rand( const double& n ); // real number in [0,n] double randExc(); // real number in [0,1) double randExc( const double& n ); // real number in [0,n) double randDblExc(); // real number in (0,1) double randDblExc( const double& n ); // real number in (0,n) uint32 randInt(); // integer in [0,2^32-1] uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32 double operator()() { return rand(); } // same as rand() // Access to 53-bit random numbers (capacity of IEEE double precision) double rand53(); // real number in [0,1) // Access to nonuniform random number distributions double randNorm( const double& mean = 0.0, const double& variance = 0.0 ); // Re-seeding functions with same behavior as initializers void seed( const uint32 oneSeed ); void seed( uint32 *const bigSeed, const uint32 seedLength = N ); void seed(); // Saving and loading generator state void save( uint32* saveArray ) const; // to array of size SAVE void load( uint32 *const loadArray ); // from such array friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); protected: void initialize( const uint32 oneSeed ); void reload(); uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; } uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; } uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; } uint32 mixBits( const uint32& u, const uint32& v ) const { return hiBit(u) | loBits(v); } uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const { return m ^ (mixBits(s0,s1)>>1) ^ (-loBit(s1) & 0x9908b0dfUL); } static uint32 hash( time_t t, clock_t c ); }; inline MTRand::MTRand( const uint32& oneSeed ) { seed(oneSeed); } inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) { seed(bigSeed,seedLength); } inline MTRand::MTRand() { seed(); } inline double MTRand::rand() { return double(randInt()) * (1.0/4294967295.0); } inline double MTRand::rand( const double& n ) { return rand() * n; } inline double MTRand::randExc() { return double(randInt()) * (1.0/4294967296.0); } inline double MTRand::randExc( const double& n ) { return randExc() * n; } inline double MTRand::randDblExc() { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } inline double MTRand::randDblExc( const double& n ) { return randDblExc() * n; } inline double MTRand::rand53() { uint32 a = randInt() >> 5, b = randInt() >> 6; return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada } inline double MTRand::randNorm( const double& mean, const double& variance ) { // Return a real number from a normal (Gaussian) distribution with given // mean and variance by Box-Muller method double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance; double phi = 2.0 * 3.14159265358979323846264338328 * randExc(); return mean + r * cos(phi); } inline MTRand::uint32 MTRand::randInt() { // Pull a 32-bit integer from the generator state // Every other access function simply transforms the numbers extracted here if( left == 0 ) reload(); --left; uint32 s1; s1 = *pNext++; s1 ^= (s1 >> 11); s1 ^= (s1 << 7) & 0x9d2c5680UL; s1 ^= (s1 << 15) & 0xefc60000UL; return ( s1 ^ (s1 >> 18) ); } inline MTRand::uint32 MTRand::randInt( const uint32& n ) { // Find which bits are used in n // Optimized by Magnus Jonsson (magnus@smartelectronix.com) uint32 used = n; used |= used >> 1; used |= used >> 2; used |= used >> 4; used |= used >> 8; used |= used >> 16; // Draw numbers until one is found in [0,n] uint32 i; do i = randInt() & used; // toss unused bits to shorten search while( i > n ); return i; } inline void MTRand::seed( const uint32 oneSeed ) { // Seed the generator with a simple uint32 initialize(oneSeed); reload(); } inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) { // Seed the generator with an array of uint32's // There are 2^19937-1 possible initial states. This function allows // all of those to be accessed by providing at least 19937 bits (with a // default seed length of N = 624 uint32's). Any bits above the lower 32 // in each element are discarded. // Just call seed() if you want to get array from /dev/urandom initialize(19650218UL); int i = 1; uint32 j = 0; int k = ( N > seedLength ? N : seedLength ); for( ; k; --k ) { state[i] = state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; state[i] &= 0xffffffffUL; ++i; ++j; if( i >= N ) { state[0] = state[N-1]; i = 1; } if( j >= seedLength ) j = 0; } for( k = N - 1; k; --k ) { state[i] = state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); state[i] -= i; state[i] &= 0xffffffffUL; ++i; if( i >= N ) { state[0] = state[N-1]; i = 1; } } state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array reload(); } inline void MTRand::seed() { // Seed the generator with an array from /dev/urandom if available // Otherwise use a hash of time() and clock() values // First try getting an array from /dev/urandom FILE* urandom = fopen( "/dev/urandom", "rb" ); if( urandom ) { uint32 bigSeed[N]; uint32 *s = bigSeed; int i = N; bool success = true; while( success && i-- ) success = fread( s++, sizeof(uint32), 1, urandom ); fclose(urandom); if( success ) { seed( bigSeed, N ); return; } } // Was not successful, so use time() and clock() instead seed( hash( time(NULL), clock() ) ); } inline void MTRand::initialize( const uint32 seed ) { // Initialize generator state with seed // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. // In previous versions, most significant bits (MSBs) of the seed affect // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. uint32 *s = state; uint32 *r = state; int i = 1; *s++ = seed & 0xffffffffUL; for( ; i < N; ++i ) { *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; r++; } } inline void MTRand::reload() { // Generate N new values in state // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) uint32 *p = state; int i; for( i = N - M; i--; ++p ) *p = twist( p[M], p[0], p[1] ); for( i = M; --i; ++p ) *p = twist( p[M-N], p[0], p[1] ); *p = twist( p[M-N], p[0], state[0] ); left = N, pNext = state; } inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) { // Get a uint32 from t and c // Better than uint32(x) in case x is floating point in [0,1] // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) static uint32 differ = 0; // guarantee time-based seeds will change uint32 h1 = 0; unsigned char *p = (unsigned char *) &t; for( size_t i = 0; i < sizeof(t); ++i ) { h1 *= UCHAR_MAX + 2U; h1 += p[i]; } uint32 h2 = 0; p = (unsigned char *) &c; for( size_t j = 0; j < sizeof(c); ++j ) { h2 *= UCHAR_MAX + 2U; h2 += p[j]; } return ( h1 + differ++ ) ^ h2; } inline void MTRand::save( uint32* saveArray ) const { uint32 *sa = saveArray; const uint32 *s = state; int i = N; for( ; i--; *sa++ = *s++ ) {} *sa = left; } inline void MTRand::load( uint32 *const loadArray ) { uint32 *s = state; uint32 *la = loadArray; int i = N; for( ; i--; *s++ = *la++ ) {} left = *la; pNext = &state[N-left]; } inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) { const MTRand::uint32 *s = mtrand.state; int i = mtrand.N; for( ; i--; os << *s++ << "\t" ) {} return os << mtrand.left; } inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) { MTRand::uint32 *s = mtrand.state; int i = mtrand.N; for( ; i--; is >> *s++ ) {} is >> mtrand.left; mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; return is; } #endif // MERSENNETWISTER_H // Change log: // // v0.1 - First release on 15 May 2000 // - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus // - Translated from C to C++ // - Made completely ANSI compliant // - Designed convenient interface for initialization, seeding, and // obtaining numbers in default or user-defined ranges // - Added automatic seeding from /dev/urandom or time() and clock() // - Provided functions for saving and loading generator state // // v0.2 - Fixed bug which reloaded generator one step too late // // v0.3 - Switched to clearer, faster reload() code from Matthew Bellew // // v0.4 - Removed trailing newline in saved generator format to be consistent // with output format of built-in types // // v0.5 - Improved portability by replacing static const int's with enum's and // clarifying return values in seed(); suggested by Eric Heimburg // - Removed MAXINT constant; use 0xffffffffUL instead // // v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits // - Changed integer [0,n] generator to give better uniformity // // v0.7 - Fixed operator precedence ambiguity in reload() // - Added access for real numbers in (0,1) and (0,n) // // v0.8 - Included time.h header to properly support time_t and clock_t // // v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto // - Allowed for seeding with arrays of any length // - Added access for real numbers in [0,1) with 53-bit resolution // - Added access for real numbers from normal (Gaussian) distributions // - Increased overall speed by optimizing twist() // - Doubled speed of integer [0,n] generation // - Fixed out-of-range number generation on 64-bit machines // - Improved portability by substituting literal constants for long enum's // - Changed license from GNU LGPL to BSD CombBLAS_beta_16_2/psort-1.0/include/psort/sort.h000644 000765 000024 00000002547 13271404146 023033 0ustar00aydinbulucstaff000000 000000 // The Funnelsort Project - Cache oblivious sorting algorithm implementation // Copyright (C) 2005 Kristoffer Vinther // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #ifndef SORT_H_INCLUDED__ #define SORT_H_INCLUDED__ namespace iosort { template inline void merge_sort(It begin, It end, OutIt dest, const typename Merger::allocator& alloc/* = typename Merger::allocator()*/); template inline void merge_sort(It begin, It end, OutIt dest) { merge_sort(begin, end, dest, typename Merger::allocator()); } } // namespace iosort #include "sort.timpl.h" #endif // SORT_H_INCLUDED__ CombBLAS_beta_16_2/psort-1.0/include/psort/psort_alltoall.h000644 000765 000024 00000006340 13271404146 025072 0ustar00aydinbulucstaff000000 000000 /* Copyright (c) 2009, David Cheng, Viral B. Shah. 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. */ #ifndef PSORT_ALLTOALL_H #define PSORT_ALLTOALL_H #include "psort_util.h" namespace vpsort { using namespace std; template static void alltoall (vector< vector<_Distance> > &right_ends, _RandomAccessIter first, _RandomAccessIter last, _ValueType *trans_data, _Distance *boundaries, MPI_Datatype &MPI_valueType, MPI_Datatype &MPI_distanceType, MPI_Comm comm) { int nproc, rank; MPI_Comm_size (comm, &nproc); MPI_Comm_rank (comm, &rank); // Should be _Distance, but MPI wants ints char errMsg[] = "32-bit limit for MPI has overflowed"; _Distance n_loc_ = last - first; if (n_loc_ > INT_MAX) throw std::overflow_error(errMsg); int n_loc = static_cast (n_loc_); // Calculate the counts for redistributing data int *send_counts = new int[nproc]; int *send_disps = new int[nproc]; for (int i = 0; i < nproc; ++i) { _Distance scount = right_ends[i + 1][rank] - right_ends[i][rank]; if (scount > INT_MAX) throw std::overflow_error(errMsg); send_counts[i] = static_cast (scount); } send_disps[0] = 0; partial_sum (send_counts, send_counts + nproc - 1, send_disps + 1); int *recv_counts = new int[nproc]; int *recv_disps = new int[nproc]; for (int i = 0; i < nproc; ++i) { _Distance rcount = right_ends[rank + 1][i] - right_ends[rank][i]; if (rcount > INT_MAX) throw std::overflow_error(errMsg); recv_counts[i] = static_cast (rcount); } recv_disps[0] = 0; partial_sum (recv_counts, recv_counts + nproc - 1, recv_disps + 1); assert (accumulate (recv_counts, recv_counts + nproc, 0) == n_loc); // Do the transpose MPI_Alltoallv (first, send_counts, send_disps, MPI_valueType, trans_data, recv_counts, recv_disps, MPI_valueType, comm); for (int i=0; i class Merge { public: template void merge (_ValueType *in, _ValueType *out, _Distance *disps, int nproc, _Compare comp) { MergeType *m = static_cast(this); m->real_merge (in, out, disps, nproc, comp); } char *description () { MergeType *m = static_cast(this); return m->real_description (); } }; // p-way flat merge class FlatMerge : public Merge { public: char *real_description () { string s("Flat merge"); return const_cast(s.c_str()); }; template void real_merge (_ValueType *in, _ValueType *out, _Distance *disps, int nproc, _Compare comp) { _Distance heads[nproc]; copy (disps, disps + nproc, heads); for (int i = 0; i < disps[nproc]; ++i) { int min_head = -1; for (int j = 0; j < nproc; ++j) { if (heads[j] < disps[j+1] && (min_head < 0 || comp (in[heads[j]], in[heads[min_head]]))) { min_head = j; } } out[i] = in[heads[min_head]++]; } } }; // A tree merge class TreeMerge : public Merge { public: char *real_description () { string s("Tree merge"); return const_cast(s.c_str()); }; template void real_merge (_ValueType *in, _ValueType *out, _Distance *disps, int nproc, _Compare comp) { // round nproc up to next power of two, pad disps int nproc_p; for (nproc_p = 1; nproc_p < nproc; nproc_p *= 2); _Distance disps_p[nproc_p + 1]; copy (disps, disps + nproc + 1, disps_p); fill (disps_p + nproc + 1, disps_p + nproc_p + 1, disps[nproc]); int merged = 0; for (int i = 1; i * 2 < nproc_p + 1; i = i * 2) { for (int j = 0; j + 2*i < nproc_p + 1; j += 2*i) { inplace_merge (in + disps_p[j], in + disps_p[j + i], in + disps_p[j + 2*i], comp); } merged = 2*i; } std::merge (in, in + disps_p[merged], in + disps_p[merged], in + disps_p[nproc_p], out, comp); } }; // An out of place tree merge class OOPTreeMerge : public Merge { public: char *real_description () { string s ("Out-of-place tree merge"); return const_cast(s.c_str()); }; template void real_merge (_RandomAccessIter in, _RandomAccessIter out, _Distance *disps, int nproc, _Compare comp) { if (nproc == 1) { copy (in, in + disps[nproc], out); return; } _RandomAccessIter bufs[2] = { in, out }; _Distance *locs = new _Distance[nproc]; for (int i = 0; i < nproc; ++i) { locs[i] = 0; } _Distance next = 1; while (true) { _Distance stride = next * 2; if (stride >= nproc) break; for (_Distance i = 0; i + next < nproc; i += stride) { _Distance end_ind = min (i + stride, (_Distance) nproc); std::merge (bufs[locs[i]] + disps[i], bufs[locs[i]] + disps[i + next], bufs[locs[i + next]] + disps[i + next], bufs[locs[i + next]] + disps[end_ind], bufs[1 - locs[i]] + disps[i], comp); locs[i] = 1 - locs[i]; } next = stride; } // now have 4 cases for final merge if (locs[0] == 0) { // 00, 01 => out of place std::merge (in, in + disps[next], bufs[locs[next]] + disps[next], bufs[locs[next]] + disps[nproc], out, comp); } else if (locs[next] == 0) { // 10 => backwards out of place std::merge (std::reverse_iterator<_RandomAccessIter> (in + disps[nproc]), std::reverse_iterator<_RandomAccessIter> (in + disps[next]), std::reverse_iterator<_RandomAccessIter> (out + disps[next]), std::reverse_iterator<_RandomAccessIter> (out), std::reverse_iterator<_RandomAccessIter> (out + disps[nproc]), not2 (comp)); } else { // 11 => in-place std::inplace_merge (out, out + disps[next], out + disps[nproc], comp); } delete [] locs; } }; #ifdef USE_FUNNEL class FunnelMerge2 : public Merge { public: char *real_description () { return ("Funnel(2) merge"); }; template void real_merge (_RandomAccessIter in, _RandomAccessIter out, _Distance *disps, int nproc, _Compare comp) { if (nproc == 1) { copy (in, in + disps[nproc], out); return; } iosort::merge_tree<_RandomAccessIter, 2> merger (nproc); for (int i=0; i { public: char *real_description () { std::string s ("Funnel(4) merge"); return const_cast(s.c_str()); }; template void real_merge (_RandomAccessIter in, _RandomAccessIter out, _Distance *disps, int nproc, _Compare comp) { if (nproc == 1) { copy (in, in + disps[nproc], out); return; } iosort::merge_tree<_RandomAccessIter, 4> merger (nproc); for (int i=0; i void merge_tree::Node::Edge::fill_dflt(TPtr b, TPtr e, allocator alloc) { TPtr p = b; try { value_type dflt; for( ; p!=e; ++p ) alloc.construct(p,dflt); } catch( ... ) { if( p != b ) { for( --p; p!=b; --p ) alloc.destroy(p); alloc.destroy(p); } alloc.deallocate(b,e-b); throw; } } template void merge_tree::Node::construct(order_t k, height_t height, size_t *buf_size, allocator alloc, nallocator nalloc) { order_t lk = pow_of_order(height-1); int i = 0; try { for( ; i!=order && lk < k; ++i, k-=lk ) { edges[i].construct(lk, height-1, buf_size, alloc, nalloc); } if( i != order ) { assert( k <= lk ); edges[i].construct(k, height-1, buf_size, alloc, nalloc); ++i; } for( ; i!=order; ++i ) { edges[i].child = NPtr(); edges[i].begin = edges[i].end = TPtr(); } } catch( ... ) { if( i ) { for( --i; i; --i ) edges[i].destroy(alloc, nalloc); edges[i].destroy(alloc, nalloc); } throw; } } template void merge_tree::Node::Edge::construct(order_t k, height_t height, size_t *buf_size, allocator alloc, nallocator nalloc) { begin = alloc.allocate(*buf_size); head = tail = end = begin+*buf_size; try { fill_dflt(begin,end,alloc); if( height > 1 ) try { child = nalloc.allocate(1); try { nalloc.construct(child, Node()); child->construct(k,height,buf_size+1,alloc,nalloc); } catch( ... ) { child->destroy(alloc, nalloc); nalloc.destroy(child); throw; } } catch( ... ) { nalloc.deallocate(child, 1); throw; } else child = NPtr(); } catch( ... ) { alloc.deallocate(begin,*buf_size); throw; } } template void merge_tree::Node::destroy(allocator alloc, nallocator nalloc) { for( int i=0; i!=order; ++i ) edges[i].destroy(alloc, nalloc); } template void merge_tree::Node::Edge::destroy(allocator alloc, nallocator nalloc) { if( child != NPtr() ) { child->destroy(alloc,nalloc); nalloc.destroy(child); nalloc.deallocate(child,1); } if( begin != TPtr() ) { for( TPtr p=begin; p!=end; ++p ) alloc.destroy(p); alloc.deallocate(begin,end-begin); } } template void merge_tree::compute_buffer_sizes(size_type *b, size_type *e) { height_t sp = Splitter::split(static_cast(e-b)); if( e-b > 2 ) { compute_buffer_sizes(b, b+sp); compute_buffer_sizes(b+sp, e); } b[sp] = Splitter::out_buffer_size(pow_of_order(static_cast(e-b-sp))); } template void merge_tree::construct(order_t k) { if(!( order < k )) throw std::invalid_argument("Merge tree too small"); height_t height = static_cast(logc(k)); if( height > MAX_MERGE_TREE_HEIGHT ) throw std::invalid_argument("Merge tree too high"); bfs_index bfs; for( height_t l=1; l!=logc(k); ++l ) bfs.child(0); leaf_index = bfs; size_type buffer_sizes[MAX_MERGE_TREE_HEIGHT]; compute_buffer_sizes(buffer_sizes,buffer_sizes+height); root = nalloc.allocate(1); try { nalloc.construct(root, Node()); root->construct(k,height,buffer_sizes+1,alloc,nalloc); try { order_t K = k+order-1; K = K - K%order; last_stream = input_streams = salloc.allocate(K); SPtr p = input_streams; try { stream_t dflt = stream_t(RanIt(),RanIt()); for( ; p!=last_stream+K; ++p ) salloc.construct(p,dflt); } catch( ... ) { if( p != input_streams ) { for( --p; p!=input_streams; --p ) salloc.destroy(p); salloc.destroy(p); } salloc.deallocate(input_streams,K); throw; } } catch( ... ) { root->destroy(alloc, nalloc); nalloc.destroy(root); throw; } } catch( ... ) { nalloc.deallocate(root,1); throw; } } template merge_tree::~merge_tree() { order_t K = k+order-1; K = K - K%order; for( SPtr s=input_streams; s!=input_streams+K; ++s ) salloc.destroy(s); salloc.deallocate(input_streams, K); root->destroy(alloc, nalloc); nalloc.destroy(root); nalloc.deallocate(root,1); } template void merge_tree::reset() { if( !cold ) { stream_t dflt; for( SPtr s=input_streams; s!=last_stream; ++s ) { salloc.destroy(s); salloc.construct(s,dflt); } cold = true; } last_stream = input_streams; } template template FwIt merge_tree::operator()(FwIt begin, FwIt end) { if( cold ) { bfs_index index; for( basic_order_t i=0; i!=order; ++i ) { index.child(i); go_down_cold(root->edges[i],index); } } cold = false; return fill_root(begin, end); } template template OutIt merge_tree::operator()(OutIt begin) { if( cold ) { bfs_index index; for( basic_order_t i=0; i!=order; ++i ) { index.child(i); go_down_cold(root->edges[i],index); } } cold = false; return fill_root(begin); } template template FwIt merge_tree::empty_buffers(NPtr n, FwIt begin, FwIt end) { for( int i=0; i!=order; ++i ) { for( ; n->edges[i].head!=n->edges[i].tail && begin!=end; ++n->edges[i].head, ++begin ) *begin = *n->edges[i].head; if( n->edges[i].child ) begin = empty_buffers(n->edges[i].child, begin, end); } return begin; } template template OutIt merge_tree::empty_buffers(NPtr n, OutIt begin) { for( int i=0; i!=order; ++i ) { begin = std::copy(n->edges[i].head, n->edges[i].tail, begin); if( n->edges[i].child ) begin = empty_buffers(n->edges[i].child, begin); } return begin; } template void merge_tree::go_down(typename Node::Edge& e, bfs_index index) { assert( e.tail == e.head ); e.head = e.begin; if( e.child == NPtr() ) fill_leaf(e,index); else fill(e.child,e,index); e.tail = e.head, e.head = e.begin; index.parent(); } template void merge_tree::go_down_cold(typename Node::Edge& e, bfs_index& index) { e.tail = e.end, e.head = e.begin; if( e.child ) warmup(e.child, e, index); else if( leaf_index <= index && input_streams+order*(index-leaf_index) < last_stream ) fill_leaf(e,index); e.tail = e.head, e.head = e.begin; index.parent(); } template void merge_tree::warmup(NPtr n, typename Node::Edge& output, bfs_index index) { for( basic_order_t i=0; i!=order; ++i ) { index.child(i); go_down_cold(n->edges[i],index); } fill(n,output,index); } template template FwIt merge_tree::copy_root(typename Node::Edge& de, bfs_index index, FwIt begin, FwIt end, std::forward_iterator_tag) { for( ;; ) { for( ; de.tail!=de.head && begin!=end; ++begin, ++de.head ) *begin = *de.head; if( de.tail == de.head ) { go_down(de,index); if( de.head == de.tail ) return begin; } else return begin; } } template template RIt merge_tree::copy_root(typename Node::Edge& de, bfs_index index, RIt begin, RIt end, std::random_access_iterator_tag) { for( ;; ) { for( ; de.tail!=de.head && begin!=end; ++begin, ++de.head ) *begin = *de.head; if( de.tail == de.head ) { go_down(de,index); if( de.head == de.tail ) return begin; } else return begin; } } template template OutIt merge_tree::copy_root(typename Node::Edge& de, bfs_index index, OutIt begin) { do { for( ; de.tail!=de.head; ++begin, ++de.head ) *begin = *de.head; go_down(de,index); } while( de.head != de.tail ); return begin; } template typename merge_tree::TPtr merge_tree::copy(typename Node::Edge& de, bfs_index index, TPtr b, TPtr e) { for( ;; ) { if( e-b < de.tail-de.head ) { for( ; b!=e; ++b, ++de.head ) *b = *de.head; return b; } else { for( ; de.tail!=de.head; ++b, ++de.head ) *b = *de.head; go_down(de,index); if( de.head == de.tail ) return b; } } } /***** * Order 2 basic merger template specialization */ template class special_ { friend class merge_tree; template static FwIt fill_root(merge_tree* that, FwIt begin, FwIt end) { typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node Node; bfs_index<2> index; for( ;; ) if( that->root->edges[0].head != that->root->edges[0].tail ) if( that->root->edges[1].head != that->root->edges[1].tail ) for( TPtr l=that->root->edges[0].head, r=that->root->edges[1].head;; ) { if( begin == end ) { that->root->edges[0].head = l; that->root->edges[1].head = r; return begin; } if( that->comp(*l,*r) ) { *begin = *l, ++l, ++begin; if( l == that->root->edges[0].tail ) { typename Node::Edge& de = that->root->edges[0]; that->root->edges[0].head = l; that->root->edges[1].head = r; index.child(0); that->go_down(de,index); index.parent(); if( de.head == de.tail ) break; l = that->root->edges[0].head; } } else { *begin = *r, ++r, ++begin; if( r == that->root->edges[1].tail ) { typename Node::Edge& de = that->root->edges[1]; that->root->edges[0].head = l; that->root->edges[1].head = r; index.child(1); that->go_down(de,index); index.parent(); if( de.head == de.tail ) break; r = that->root->edges[1].head; } } } else { index.child(0); return that->copy_root(that->root->edges[0], index, begin, end, typename std::iterator_traits::iterator_category()); } else if( that->root->edges[1].head != that->root->edges[1].tail ) { index.child(1); return that->copy_root(that->root->edges[1], index, begin, end, typename std::iterator_traits::iterator_category()); } else return begin; } template static OutIt fill_root(merge_tree* that, OutIt begin) { typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node Node; bfs_index<2> index; for( ;; ) if( that->root->edges[0].head != that->root->edges[0].tail ) if( that->root->edges[1].head != that->root->edges[1].tail ) for( TPtr l=that->root->edges[0].head, r=that->root->edges[1].head;; ) if( that->comp(*l,*r) ) { *begin = *l, ++l, ++begin; if( l == that->root->edges[0].tail ) { typename Node::Edge& de = that->root->edges[0]; that->root->edges[0].head = l; that->root->edges[1].head = r; index.child(0); that->go_down(de,index); index.parent(); if( de.head == de.tail ) break; l = that->root->edges[0].head; } } else { *begin = *r, ++r, ++begin; if( r == that->root->edges[1].tail ) { typename Node::Edge& de = that->root->edges[1]; that->root->edges[0].head = l; that->root->edges[1].head = r; index.child(1); that->go_down(de,index); index.parent(); if( de.head == de.tail ) break; r = that->root->edges[1].head; } } else { index.child(0); return that->copy_root(that->root->edges[0], index, begin); } else if( that->root->edges[1].head != that->root->edges[1].tail ) { index.child(1); return that->copy_root(that->root->edges[1], index, begin); } else return begin; } static void fill(merge_tree* that, typename merge_tree::NPtr n, typename merge_tree::Node::Edge& output, bfs_index<2> index) { typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node Node; for( TPtr p = output.head;; ) if( n->edges[0].head != n->edges[0].tail ) if( n->edges[1].head != n->edges[1].tail ) for( TPtr l=n->edges[0].head, r=n->edges[1].head;; ) { if( p == output.tail ) { output.head = p; n->edges[0].head = l; n->edges[1].head = r; return; } #ifdef USE_COND_MOVS_ #ifdef _MSC_VER __asm { mov eax, p mov ebx, l mov ecx, r mov edi, dword ptr [ebx] ; edges[0] key in edi mov edx, dword ptr [ecx] ; edges[1] key in edx cmp edi, edx ; compare keys cmovl edx, edi ; small key in edx cmovl edi, dword ptr [ebx+4] ; small pointer in edi cmovge edi, dword ptr [ecx+4] ; small pointer in edi mov dword ptr [eax], edx ; output small key mov dword ptr [eax+4], edi ; output small pointer setl dl mov edi, ebx ; cond-inc edges[0] add edi, 8 test dl, dl cmovnz ebx, edi mov edi, ecx ; cond-inc edges[1] add edi, 8 test dl, dl cmovz ecx, edi add eax, 8 ; inc out mov r, ecx mov l, ebx mov p, eax } #else // _MSC_VER #ifdef __GNUC__ asm("movl\t(%1), %%edi\n\t" // edges[0] key in edi "movl\t(%2), %%edx\n\t" // edges[1] key in edx "cmpl\t%%edx, %%edi\n\t" // compare keys "cmovll\t%%edi, %%edx\n\t" // small key in edx "cmovll\t4(%1), %%edi\n\t" // small pointer in edi "cmovgel\t4(%2), %%edi\n\t" // small pointer in edi "movl\t%%edx, (%0)\n\t" // output small key "movl\t%%edi, 4(%0)\n\t" // output small pointer "setb\t%%dl\n\t" "movl\t%1, %%edi\n\t" // cond-inc edges[0] "addl\t$8, %%edi\n\t" "testb\t%%dl, %%dl\n\t" "cmovnzl\t%%edi, %1\n\t" "movl\t%2, %%edi\n\t" // cond-inc edges[1] "addl\t$8, %%edi\n\t" "testb\t%%dl, %%dl\n\t" "cmovzl\t%%edi, %2\n\t" "addl\t$8, %0" // inc out : "=r"(p), "=r"(l), "=r"(r) : "0"(p), "1"(l), "2"(r) : "edx", "edi"); #else // __GNUC__ register TPtr nh = l+1; bool b = that->comp(*l,*r); *p = b? *l : *r; l = b? nh : l; nh = r+1; r = b? r : nh; ++p; #endif // __GNUC__ #endif // _MSC_VER if( l == n->edges[0].tail ) { typename Node::Edge& de = n->edges[0]; n->edges[0].head = l; n->edges[1].head = r; index.child(0); that->go_down(de,index); if( de.head == de.tail ) break; l = n->edges[0].head; } if( r == n->edges[1].tail ) { typename Node::Edge& de = n->edges[1]; n->edges[0].head = l; n->edges[1].head = r; index.child(1); that->go_down(de,index); if( de.head == de.tail ) break; r = n->edges[1].head; } #else // USE_COND_MOVS_ if( that->comp(*l,*r) ) { *p = *l, ++l, ++p; if( l == n->edges[0].tail ) { typename Node::Edge& de = n->edges[0]; n->edges[0].head = l; n->edges[1].head = r; index.child(0); that->go_down(de,index); index.parent(); if( de.head == de.tail ) break; l = n->edges[0].head; } } else { *p = *r, ++r, ++p; if( r == n->edges[1].tail ) { typename Node::Edge& de = n->edges[1]; n->edges[0].head = l; n->edges[1].head = r; index.child(1); that->go_down(de,index); index.parent(); if( de.head == de.tail ) break; r = n->edges[1].head; } } #endif // USE_COND_MOVS_ } else { index.child(0); output.head = that->copy(n->edges[0], index, p, output.tail); return; } else if( n->edges[1].head != n->edges[1].tail ) { index.child(1); output.head = that->copy(n->edges[1], index, p, output.tail); return; } else return; } static void fill_leaf(merge_tree* that, typename merge_tree::Node::Edge& output, bfs_index<2> index) { typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::stream_iterator stream_iterator; stream_iterator left = that->input_streams+2*(index-that->leaf_index); stream_iterator right = left; ++right; //assert( left < that->last_stream ); //assert( that->input_streams <= left ); RanIt l=(*left).begin(), r=(*right).begin(); for( TPtr p = output.head;; ) if( l != (*left).end() ) if( r != (*right).end() ) for( ;; ) { if( p == output.tail ) { output.head = p; (*left).begin() = l; (*right).begin() = r; // TODO: Refill return; } if( that->comp(*l,*r) ) { *p = *l, ++l, ++p; if( l == (*left).end() ) break; } else { *p = *r, ++r, ++p; if( r == (*right).end() ) break; } } else { assert( r == (*right).end() ); if( output.tail-p < (*left).end()-l ) for( ; p!=output.tail; ++p, ++l ) *p = *l; else for( ; (*left).end()!=l; ++p, ++l ) *p = *l; output.head = p; (*left).begin() = l; (*right).begin() = r; // TODO: Refill return; } else if( r != (*right).end() ) { assert( l == (*left).end() ); if( output.tail-p < (*right).end()-r ) for( ; p!=output.tail; ++p, ++r ) *p = *r; else for( ; (*right).end()!=r; ++p, ++r ) *p = *r; output.head = p; (*left).begin() = l; (*right).begin() = r; // TODO: Refill return; } else return; } }; /***** * Order 4 basic merger template specialization */ #define MOVE_SMALL(i,n) *begin = *head[i], ++head[i], ++begin; \ if( head[i] == tail[i] ) \ { \ stream[i]->head = head[i]; \ index.child(static_cast(stream[i]-n->edges)); \ that->go_down(*stream[i],index); \ index.parent(); \ head[i] = stream[i]->head; tail[i] = stream[i]->tail; \ if( head[i] == tail[i] ) \ { \ --active; \ head[i] = head[active]; \ tail[i] = tail[active]; \ stream[i] = stream[active]; \ break; \ } \ } #define MOVE_SMALL_(i,n) *begin = *head[i], ++head[i], ++begin; \ if( head[i] == tail[i] ) \ if( go_down(that, i, active-1, index, head, tail, n->edges, stream) ) \ { \ --active; \ break; \ } template class special_ { friend class merge_tree; template static bool go_down(merge_tree *that, order_t i, order_t active, bfs_index<4> index, TPtr *head, TPtr *tail, typename merge_tree::Node::Edge* edges, typename merge_tree::Node::Edge** stream) { stream[i]->head = head[i]; index.child(static_cast(stream[i]-edges)); that->go_down(*stream[i],index); head[i] = stream[i]->head; tail[i] = stream[i]->tail; if( head[i] == tail[i] ) { head[i] = head[active]; tail[i] = tail[active]; stream[i] = stream[active]; return true; } else return false; } template static FwIt fill_root(merge_tree* that, FwIt begin, FwIt end) { typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node::Edge Edge; Pred& comp = that->comp; bfs_index<4> index; TPtr head[4], tail[4]; Edge *stream[4]; order_t active = 0; for( Edge *p=that->root->edges; p!=that->root->edges+4; ++p ) if( p->head != p->tail ) { stream[active] = p; head[active] = p->head, tail[active] = p->tail; ++active; } switch( active ) { case 4: for( ;; ) { if( begin == end ) { stream[0]->head = head[0]; stream[1]->head = head[1]; stream[2]->head = head[2]; stream[3]->head = head[3]; return begin; } if( comp(*head[0],*head[3]) ) { if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,that->root) } else { MOVE_SMALL(1,that->root) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,that->root) } else { MOVE_SMALL(2,that->root) } } } else { if( comp(*head[1],*head[3]) ) { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,that->root) } else { MOVE_SMALL(2,that->root) } } else { if( comp(*head[2],*head[3]) ) { MOVE_SMALL(2,that->root) } else { MOVE_SMALL(3,that->root) } } } } case 3: for( ;; ) { if( begin == end ) { stream[0]->head = head[0]; stream[1]->head = head[1]; stream[2]->head = head[2]; return begin; } if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,that->root) } else { MOVE_SMALL(1,that->root) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,that->root) } else { MOVE_SMALL(2,that->root) } } } case 2: for( ;; ) { if( begin == end ) { stream[0]->head = head[0]; stream[1]->head = head[1]; return begin; } if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,that->root) } else { MOVE_SMALL(1,that->root) } } case 1: stream[0]->head = head[0]; index.child(static_cast(stream[0]-that->root->edges)); begin = that->copy_root(*stream[0], index, begin, end, typename std::iterator_traits::iterator_category()); case 0: return begin; default: assert( false ); return begin; } } template static OutIt fill_root(merge_tree* that, OutIt begin) { typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node::Edge Edge; Pred& comp = that->comp; bfs_index<4> index; TPtr head[4], tail[4]; Edge *stream[4]; order_t active = 0; for( Edge *p=that->root->edges; p!=that->root->edges+4; ++p ) if( p->head != p->tail ) { stream[active] = p; head[active] = p->head, tail[active] = p->tail; ++active; } switch( active ) { case 4: for( ;; ) if( comp(*head[0],*head[3]) ) { if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,that->root) } else { MOVE_SMALL(1,that->root) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,that->root) } else { MOVE_SMALL(2,that->root) } } } else { if( comp(*head[1],*head[3]) ) { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,that->root) } else { MOVE_SMALL(2,that->root) } } else { if( comp(*head[2],*head[3]) ) { MOVE_SMALL(2,that->root) } else { MOVE_SMALL(3,that->root) } } } case 3: for( ;; ) if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,that->root) } else { MOVE_SMALL(1,that->root) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,that->root) } else { MOVE_SMALL(2,that->root) } } case 2: for( ;; ) if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,that->root) } else { MOVE_SMALL(1,that->root) } case 1: stream[0]->head = head[0]; index.child(static_cast(stream[0]-that->root->edges)); begin = that->copy_root(*stream[0], index, begin); case 0: return begin; default: assert( false ); return begin; } } static void fill(merge_tree* that, typename merge_tree::NPtr n, typename merge_tree::Node::Edge& output, bfs_index<4> index) { typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node::Edge Edge; Pred& comp = that->comp; TPtr head[4], tail[4]; Edge *stream[4]; order_t active = 0; for( Edge *p=n->edges; p!=n->edges+4; ++p ) if( p->head != p->tail ) { stream[active] = p; head[active] = p->head, tail[active] = p->tail; ++active; } TPtr begin = output.head; switch( active ) { case 4: for( ;; ) { if( begin == output.tail ) { output.head = begin; stream[0]->head = head[0]; stream[1]->head = head[1]; stream[2]->head = head[2]; stream[3]->head = head[3]; return; } if( comp(*head[0],*head[3]) ) { if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,n) } else { MOVE_SMALL(1,n) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,n) } else { MOVE_SMALL(2,n) } } } else { if( comp(*head[1],*head[3]) ) { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,n) } else { MOVE_SMALL(2,n) } } else { if( comp(*head[2],*head[3]) ) { MOVE_SMALL(2,n) } else { MOVE_SMALL(3,n) } } } } case 3: for( ;; ) { if( begin == output.tail ) { output.head = begin; stream[0]->head = head[0]; stream[1]->head = head[1]; stream[2]->head = head[2]; return; } if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,n) } else { MOVE_SMALL(1,n) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL(1,n) } else { MOVE_SMALL(2,n) } } } case 2: for( ;; ) { if( begin == output.tail ) { output.head = begin; stream[0]->head = head[0]; stream[1]->head = head[1]; return; } if( comp(*head[0],*head[1]) ) { MOVE_SMALL(0,n) } else { MOVE_SMALL(1,n) } } case 1: stream[0]->head = head[0]; index.child(static_cast(stream[0]-n->edges)); output.head = that->copy(*stream[0], index, begin, output.tail); case 0: return; default: assert( false ); return; } } static void fill_leaf(merge_tree* that, typename merge_tree::Node::Edge& output, bfs_index<4> index) { typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::SPtr SPtr; assert( that->input_streams+4*(index-that->leaf_index) < that->last_stream ); assert( that->input_streams <= that->input_streams+4*(index-that->leaf_index) ); Pred& comp = that->comp; RanIt head[4], tail[4]; SPtr stream[4]; order_t active = 0; for( SPtr p=that->input_streams+4*(index-that->leaf_index); p!=that->input_streams+4*(index-that->leaf_index+1); ++p ) if( p->first != p->second ) { stream[active] = p; head[active] = p->first, tail[active] = p->second; ++active; } TPtr begin = output.head; switch( active ) { case 4: for( ;; ) { if( begin == output.tail ) { output.head = begin; stream[0]->first = head[0]; stream[1]->first = head[1]; stream[2]->first = head[2]; stream[3]->first = head[3]; // TODO: Refill return; } if( comp(*head[0],*head[3]) ) { if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { *begin = *head[0], ++head[0], ++begin; if( head[0] == tail[0] ) { stream[0]->first = head[0]; head[0] = head[3]; tail[0] = tail[3]; stream[0] = stream[3]; break; } } else { *begin = *head[1], ++head[1], ++begin; if( head[1] == tail[1] ) { stream[1]->first = head[1]; head[1] = head[3]; tail[1] = tail[3]; stream[1] = stream[3]; break; } } } else { if( comp(*head[1],*head[2]) ) { *begin = *head[1], ++head[1], ++begin; if( head[1] == tail[1] ) { stream[1]->first = head[1]; head[1] = head[3]; tail[1] = tail[3]; stream[1] = stream[3]; break; } } else { *begin = *head[2], ++head[2], ++begin; if( head[2] == tail[2] ) { stream[2]->first = head[2]; head[2] = head[3]; tail[2] = tail[3]; stream[2] = stream[3]; break; } } } } else { if( comp(*head[1],*head[3]) ) { if( comp(*head[1],*head[2]) ) { *begin = *head[1], ++head[1], ++begin; if( head[1] == tail[1] ) { stream[1]->first = head[1]; head[1] = head[3]; tail[1] = tail[3]; stream[1] = stream[3]; break; } } else { *begin = *head[2], ++head[2], ++begin; if( head[2] == tail[2] ) { stream[2]->first = head[2]; head[2] = head[3]; tail[2] = tail[3]; stream[2] = stream[3]; break; } } } else { if( comp(*head[2],*head[3]) ) { *begin = *head[2], ++head[2], ++begin; if( head[2] == tail[2] ) { stream[2]->first = head[2]; head[2] = head[3]; tail[2] = tail[3]; stream[2] = stream[3]; break; } } else { *begin = *head[3], ++head[3], ++begin; if( head[3] == tail[3] ) { stream[3]->first = head[3]; break; } } } } } case 3: for( ;; ) { if( begin == output.tail ) { output.head = begin; stream[0]->first = head[0]; stream[1]->first = head[1]; stream[2]->first = head[2]; // TODO: Refill return; } if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { *begin = *head[0], ++head[0], ++begin; if( head[0] == tail[0] ) { stream[0]->first = head[0]; head[0] = head[2]; tail[0] = tail[2]; stream[0] = stream[2]; break; } } else { *begin = *head[1], ++head[1], ++begin; if( head[1] == tail[1] ) { stream[1]->first = head[1]; head[1] = head[2]; tail[1] = tail[2]; stream[1] = stream[2]; break; } } } else { if( comp(*head[1],*head[2]) ) { *begin = *head[1], ++head[1], ++begin; if( head[1] == tail[1] ) { stream[1]->first = head[1]; head[1] = head[2]; tail[1] = tail[2]; stream[1] = stream[2]; break; } } else { *begin = *head[2], ++head[2], ++begin; if( head[2] == tail[2] ) { stream[2]->first = head[2]; break; } } } } case 2: for( ;; ) { if( begin == output.tail ) { output.head = begin; stream[0]->first = head[0]; stream[1]->first = head[1]; // TODO: Refill return; } if( comp(*head[0],*head[1]) ) { *begin = *head[0], ++head[0], ++begin; if( head[0] == tail[0] ) { stream[0]->first = head[0]; if( output.tail-begin < stream[1]->second-head[1] ) for( ; begin!=output.tail; ++begin, ++head[1] ) *begin = *head[1]; else for( ; stream[1]->second!=head[1]; ++begin, ++head[1] ) *begin = *head[1]; output.head = begin; stream[1]->first = head[1]; //head[0] = head[1]; //tail[0] = tail[1]; //stream[0] = stream[1]; //break; return; } } else { *begin = *head[1], ++head[1], ++begin; if( head[1] == tail[1] ) { stream[1]->first = head[1]; break; } } } case 1: if( output.tail-begin < stream[0]->second-head[0] ) for( ; begin!=output.tail; ++begin, ++head[0] ) *begin = *head[0]; else for( ; stream[0]->second!=head[0]; ++begin, ++head[0] ) *begin = *head[0]; output.head = begin; stream[0]->first = head[0]; // TODO: Refill return; case 0: return; default: assert( false ); return; } } }; /***** * Alloc::pointer iterator template specialization */ template void merge_tree::Node::Edge::fill_dflt(TPtr b, TPtr e, allocator alloc) { TPtr p = b; try { value_type dflt; for( ; p!=e; ++p ) alloc.construct(p,dflt); } catch( ... ) { if( p != b ) { for( --p; p!=b; --p ) alloc.destroy(p); alloc.destroy(p); } alloc.deallocate(b,e-b); throw; } } template typename merge_tree::Node::Edge** merge_tree::Node::construct(order_t k, height_t height, size_t *buf_size, typename merge_tree::Node::Edge** edge_list, allocator alloc, nallocator nalloc) { order_t lk = pow_of_order(height-1); int i = 0; try { for( ; i!=order && lk < k; ++i, k-=lk ) { edge_list = edges[i].construct(lk, height-1, buf_size, edge_list, alloc, nalloc); } if( i != order ) { assert( k <= lk ); edge_list = edges[i].construct(k, height-1, buf_size, edge_list, alloc, nalloc); ++i; } for( ; i!=order; ++i ) { edges[i].child = NPtr(); edges[i].begin = edges[i].end = TPtr(); } return edge_list; } catch( ... ) { if( i ) { for( --i; i; --i ) edges[i].destroy(alloc, nalloc); edges[i].destroy(alloc, nalloc); } throw; } } template typename merge_tree::Node::Edge** merge_tree::Node::Edge::construct(order_t k, height_t height, size_t *buf_size, Edge** edge_list, allocator alloc, nallocator nalloc) { if( height > 0 ) { begin = alloc.allocate(*buf_size); head = tail = end = begin+*buf_size; try { fill_dflt(begin,end,alloc); try { child = nalloc.allocate(1); try { nalloc.construct(child, Node()); return child->construct(k,height,buf_size+1,edge_list,alloc,nalloc); } catch( ... ) { child->destroy(alloc, nalloc); nalloc.destroy(child); throw; } } catch( ... ) { nalloc.deallocate(child, 1); throw; } } catch( ... ) { alloc.deallocate(begin,*buf_size); throw; } } else { child = NPtr(); head = tail = end = begin = TPtr(); *edge_list = this; return edge_list+1; } } template void merge_tree::Node::destroy(allocator alloc, nallocator nalloc) { for( int i=0; i!=order; ++i ) edges[i].destroy(alloc, nalloc); } template void merge_tree::Node::Edge::destroy(allocator alloc, nallocator nalloc) { if( child != NPtr() ) { child->destroy(alloc,nalloc); nalloc.destroy(child); nalloc.deallocate(child,1); } if( begin != TPtr() ) { for( TPtr p=begin; p!=end; ++p ) alloc.destroy(p); alloc.deallocate(begin,end-begin); } } template void merge_tree::compute_buffer_sizes(size_type *b, size_type *e) { height_t sp = Splitter::split(static_cast(e-b)); if( e-b > 2 ) { compute_buffer_sizes(b, b+sp); compute_buffer_sizes(b+sp, e); } b[sp] = Splitter::out_buffer_size(pow_of_order(static_cast(e-b-sp))); } template void merge_tree::construct(order_t k) { if(!( order < k )) throw std::invalid_argument("Merge tree too small"); height_t height = logc(k); if( height > MAX_MERGE_TREE_HEIGHT ) throw std::invalid_argument("Merge tree too high"); bfs_index bfs; for( height_t l=1; l!=logc(k); ++l ) bfs.child(0); leaf_index = bfs; size_type buffer_sizes[MAX_MERGE_TREE_HEIGHT]; compute_buffer_sizes(buffer_sizes,buffer_sizes+height); last_stream = input_streams = salloc.allocate(k); SPtr p = input_streams; try { stream_t dflt; for( ; p!=last_stream+k; ++p ) salloc.construct(p,dflt); try { root = nalloc.allocate(1); try { nalloc.construct(root, Node()); root->construct(k,height,buffer_sizes+1,input_streams,alloc,nalloc); } catch( ... ) { root->destroy(alloc, nalloc); nalloc.destroy(root); throw; } } catch( ... ) { nalloc.deallocate(root,1); throw; } } catch( ... ) { if( p != input_streams ) { for( --p; p!=input_streams; --p ) salloc.destroy(p); salloc.destroy(p); } salloc.deallocate(input_streams,k); throw; } } template merge_tree::~merge_tree() { for( SPtr s=input_streams; s!=input_streams+k; ++s ) salloc.destroy(s); salloc.deallocate(input_streams, k); root->destroy(alloc, nalloc); nalloc.destroy(root); nalloc.deallocate(root,1); } template void merge_tree::reset() { cold = true; last_stream = input_streams; } template template FwIt merge_tree::operator()(FwIt begin, FwIt end) { if( cold ) for( int i=0; i!=order; ++i ) if( root->edges[i].child != NPtr() ) go_down_cold(root->edges[i], comp); cold = false; return fill(root, begin, end, comp); } template template OutIt merge_tree::operator()(OutIt begin) { if( cold ) for( int i=0; i!=order; ++i ) if( root->edges[i].child != NPtr() ) go_down_cold(root->edges[i], comp); cold = false; return fill(root, begin, comp); } template template FwIt merge_tree::empty_buffers(NPtr n, FwIt begin, FwIt end) { for( int i=0; i!=order; ++i ) { for( ; n->edges[i].head!=n->edges[i].tail && begin!=end; ++n->edges[i].head, ++begin ) *begin = *n->edges[i].head; if( n->edges[i].child ) begin = empty_buffers(n->edges[i].child, begin, end); } return begin; } template template OutIt merge_tree::empty_buffers(NPtr n, OutIt begin) { for( int i=0; i!=order; ++i ) { begin = std::copy(n->edges[i].head, n->edges[i].tail, begin); if( n->edges[i].child ) begin = empty_buffers(n->edges[i].child, begin); } return begin; } template void merge_tree::go_down(typename Node::Edge& e, Pred comp) { assert( e.tail == e.head ); assert( e.child != NPtr() ); e.tail = fill(e.child, e.begin, e.tail, comp); e.head = e.begin; } template void merge_tree::go_down_cold(typename Node::Edge& e, Pred comp) { assert( e.child != NPtr() ); e.tail = e.end, e.head = e.begin; warmup(e.child, e, comp); e.tail = e.head, e.head = e.begin; } template void merge_tree::warmup(NPtr n, typename Node::Edge& output, Pred comp) { for( int i=0; i!=order; ++i ) if( n->edges[i].child != NPtr() ) go_down_cold(n->edges[i], comp); output.head = fill(n, output.head, output.tail, comp); } template template FwIt merge_tree::copy(typename Node::Edge& de, FwIt begin, FwIt end, Pred comp, std::forward_iterator_tag) { for( ;; ) { for( ; de.tail!=de.head && begin!=end; ++begin, ++de.head ) *begin = *de.head; if( de.tail == de.head && de.child != NPtr() ) { go_down(de, comp); if( de.head == de.tail ) return begin; } else return begin; } } template template RIt merge_tree::copy(typename Node::Edge& de, RIt begin, RIt end, Pred comp, std::random_access_iterator_tag) { /* for( ;; ) { for( ; de.tail!=de.head && begin!=end; ++begin, ++de.head ) *begin = *de.head; if( de.tail == de.head ) { go_down(de, comp); if( de.head == de.tail ) return begin; } else return begin; } */ for( ;; ) { if( end-begin < de.tail-de.head ) { for( ; begin!=end; ++begin, ++de.head ) *begin = *de.head; return begin; } else { for( ; de.tail!=de.head; ++begin, ++de.head ) *begin = *de.head; if( de.child != NPtr() ) { go_down(de, comp); if( de.head == de.tail ) return begin; } else return begin; } } } template template OutIt merge_tree::copy(typename Node::Edge& de, OutIt begin, Pred comp) { do { for( ; de.tail!=de.head; ++begin, ++de.head ) *begin = *de.head; if( de.child != NPtr() ) go_down(de, comp); else break; } while( de.head != de.tail ); return begin; } /***** * Alloc::pointer iterator, order 2 basic merger template specialization */ template class special_ { friend class merge_tree; template static FwIt fill(typename merge_tree::NPtr root, FwIt begin, FwIt end, Pred comp) { typedef typename merge_tree::NPtr NPtr; typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node Node; for( ;; ) if( root->edges[0].head != root->edges[0].tail ) if( root->edges[1].head != root->edges[1].tail ) for( TPtr l=root->edges[0].head, r=root->edges[1].head;; ) { if( begin == end ) { root->edges[0].head = l; root->edges[1].head = r; return begin; } if( comp(*l,*r) ) { *begin = *l, ++l, ++begin; if( l == root->edges[0].tail ) { typename Node::Edge& de = root->edges[0]; root->edges[0].head = l; root->edges[1].head = r; if( de.child != NPtr() ) merge_tree::go_down(de, comp); if( de.head == de.tail ) break; l = root->edges[0].head; } } else { *begin = *r, ++r, ++begin; if( r == root->edges[1].tail ) { typename Node::Edge& de = root->edges[1]; root->edges[0].head = l; root->edges[1].head = r; if( de.child != NPtr() ) merge_tree::go_down(de, comp); if( de.head == de.tail ) break; r = root->edges[1].head; } } } else return merge_tree::copy(root->edges[0], begin, end, comp, typename std::iterator_traits::iterator_category()); else if( root->edges[1].head != root->edges[1].tail ) return merge_tree::copy(root->edges[1], begin, end, comp, typename std::iterator_traits::iterator_category()); else return begin; } template static OutIt fill(typename merge_tree::NPtr root, OutIt begin, Pred comp) { typedef typename merge_tree::NPtr NPtr; typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node Node; for( ;; ) if( root->edges[0].head != root->edges[0].tail ) if( root->edges[1].head != root->edges[1].tail ) for( TPtr l=root->edges[0].head, r=root->edges[1].head;; ) if( comp(*l,*r) ) { *begin = *l, ++l, ++begin; if( l == root->edges[0].tail ) { typename Node::Edge& de = root->edges[0]; root->edges[0].head = l; root->edges[1].head = r; if( de.child != NPtr() ) merge_tree::go_down(de, comp); if( de.head == de.tail ) break; l = root->edges[0].head; } } else { *begin = *r, ++r, ++begin; if( r == root->edges[1].tail ) { typename Node::Edge& de = root->edges[1]; root->edges[0].head = l; root->edges[1].head = r; if( de.child != NPtr() ) merge_tree::go_down(de, comp); if( de.head == de.tail ) break; r = root->edges[1].head; } } else return merge_tree::copy(root->edges[0], begin, comp); else if( root->edges[1].head != root->edges[1].tail ) return merge_tree::copy(root->edges[1], begin, comp); else return begin; } }; /***** * Alloc::pointer iterator, order 4 basic merger template specialization */ #define MOVE_SMALL_PTR(i,a) *begin = *head[i], ++head[i], ++begin; \ if( head[i] == tail[i] ) \ { \ stream[i]->head = head[i]; \ if( stream[i]->child != NPtr() ) \ { \ merge_tree::go_down(*stream[i], comp); \ head[i] = stream[i]->head; tail[i] = stream[i]->tail; \ } \ if( head[i] == tail[i] ) \ { \ head[i] = head[a-1]; \ tail[i] = tail[a-1]; \ stream[i] = stream[a-1]; \ break; \ } \ } #define MOVE_SMALL_PTR_(i,a) *begin = *head[i], ++head[i], ++begin; \ if( head[i] == tail[i] ) \ if( go_down(stream,head,tail,comp) ) \ break; \ template class special_ { friend class merge_tree; template static bool go_down(typename merge_tree::Node::Edge** stream, typename merge_tree::TPtr* head, typename merge_tree::TPtr* tail, Pred comp) { typedef typename merge_tree::NPtr NPtr; stream[i]->head = head[i]; if( stream[i]->child != NPtr() ) { merge_tree::go_down(*stream[i], comp); head[i] = stream[i]->head; tail[i] = stream[i]->tail; } if( head[i] == tail[i] ) { head[i] = head[a-1]; tail[i] = tail[a-1]; stream[i] = stream[a-1]; return true; } return false; } template static FwIt fill(typename merge_tree::NPtr n, FwIt begin, FwIt end, Pred comp) { typedef typename merge_tree::NPtr NPtr; typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node::Edge Edge; TPtr head[4], tail[4]; Edge *stream[4]; order_t active = 0; for( Edge *p=n->edges; p!=n->edges+4; ++p ) if( p->head != p->tail ) { stream[active] = p; head[active] = p->head, tail[active] = p->tail; ++active; } switch( active ) { case 4: for( ;; ) { if( begin == end ) { stream[0]->head = head[0]; stream[1]->head = head[1]; stream[2]->head = head[2]; stream[3]->head = head[3]; return begin; } if( comp(*head[0],*head[3]) ) { if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL_PTR(0,4) } else { MOVE_SMALL_PTR(1,4) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL_PTR(1,4) } else { MOVE_SMALL_PTR(2,4) } } } else { if( comp(*head[1],*head[3]) ) { if( comp(*head[1],*head[2]) ) { MOVE_SMALL_PTR(1,4) } else { MOVE_SMALL_PTR(2,4) } } else { if( comp(*head[2],*head[3]) ) { MOVE_SMALL_PTR(2,4) } else { MOVE_SMALL_PTR(3,4) } } } } case 3: for( ;; ) { if( begin == end ) { stream[0]->head = head[0]; stream[1]->head = head[1]; stream[2]->head = head[2]; return begin; } if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL_PTR(0,3) } else { MOVE_SMALL_PTR(1,3) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL_PTR(1,3) } else { MOVE_SMALL_PTR(2,3) } } } case 2: for( ;; ) { if( begin == end ) { stream[0]->head = head[0]; stream[1]->head = head[1]; return begin; } if( comp(*head[0],*head[1]) ) { MOVE_SMALL_PTR(0,2) } else { MOVE_SMALL_PTR(1,2) } } case 1: stream[0]->head = head[0]; begin = merge_tree::copy(*stream[0], begin, end, comp, typename std::iterator_traits::iterator_category()); case 0: return begin; default: assert( false ); return begin; } } template static OutIt fill(typename merge_tree::NPtr n, OutIt begin, Pred comp) { typedef typename merge_tree::NPtr NPtr; typedef typename merge_tree::TPtr TPtr; typedef typename merge_tree::Node::Edge Edge; TPtr head[4], tail[4]; Edge *stream[4]; order_t active = 0; for( Edge *p=n->edges; p!=n->edges+4; ++p ) if( p->head != p->tail ) { stream[active] = p; head[active] = p->head, tail[active] = p->tail; ++active; } switch( active ) { case 4: for( ;; ) if( comp(*head[0],*head[3]) ) { if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL_PTR(0,4) } else { MOVE_SMALL_PTR(1,4) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL_PTR(1,4) } else { MOVE_SMALL_PTR(2,4) } } } else { if( comp(*head[1],*head[3]) ) { if( comp(*head[1],*head[2]) ) { MOVE_SMALL_PTR(1,4) } else { MOVE_SMALL_PTR(2,4) } } else { if( comp(*head[2],*head[3]) ) { MOVE_SMALL_PTR(2,4) } else { MOVE_SMALL_PTR(3,4) } } } case 3: for( ;; ) if( comp(*head[0],*head[2]) ) { if( comp(*head[0],*head[1]) ) { MOVE_SMALL_PTR(0,3) } else { MOVE_SMALL_PTR(1,3) } } else { if( comp(*head[1],*head[2]) ) { MOVE_SMALL_PTR(1,3) } else { MOVE_SMALL_PTR(2,3) } } case 2: for( ;; ) if( comp(*head[0],*head[1]) ) { MOVE_SMALL_PTR(0,2) } else { MOVE_SMALL_PTR(1,2) } case 1: stream[0]->head = head[0]; begin = merge_tree::copy(*stream[0], begin, comp); case 0: return begin; default: assert( false ); return begin; } } }; } // namespace iosort CombBLAS_beta_16_2/psort-1.0/driver/psort_bench.sh000755 000765 000024 00000000632 13212627400 023225 0ustar00aydinbulucstaff000000 000000 #! /bin/bash for i in `seq 256 -32 4 `; do for j in 1000000 10000000 100000000 1000000000 10000000000; do mpirun -np $i ./psort $j s o #include #include #include #include "MersenneTwister.h" #include "psort.h" #include "psort_samplesort.h" static void check_result (double *work, int mysize) { int nproc, rank; MPI_Comm_rank (MPI_COMM_WORLD, &rank); MPI_Comm_size (MPI_COMM_WORLD, &nproc); /* for (long p=0; p(), MPI_COMM_WORLD)) { if (rank == 0) std::cout << "Correctly sorted" << std::endl; } else { if (rank == 0) std::cout << "NOT correctly sorted" << std::endl; } } void genData (char type, int rank, double *work, int mysize) { MTRand mtrand (rank); switch (type) { case 'r': /* 1:n */ for (long i=0; i(), dist, stl_sort, sample_split, oop_tree_merge, MPI_COMM_WORLD); vpsort::print_perf_data (dist, stl_sort, sample_split, oop_tree_merge, MPI_COMM_WORLD); } else if (merger == 't') { vpsort::parallel_sort (work, work + mysize, std::less(), dist, stl_sort, sample_split, tree_merge, MPI_COMM_WORLD); vpsort::print_perf_data (dist, stl_sort, sample_split, tree_merge, MPI_COMM_WORLD); } else if (merger == 'f') { vpsort::parallel_sort (work, work + mysize, std::less(), dist, stl_sort, sample_split, funnel_merge2, MPI_COMM_WORLD); vpsort::print_perf_data (dist, stl_sort, sample_split, funnel_merge2, MPI_COMM_WORLD); } else { if (rank == 0) std::cerr << "Invalid arguments" << std::endl; } } else if (splitter == 'm') { if (merger == 'o') { vpsort::parallel_sort (work, work + mysize, std::less(), dist, stl_sort, median_split, oop_tree_merge, MPI_COMM_WORLD); vpsort::print_perf_data (dist, stl_sort, median_split, oop_tree_merge, MPI_COMM_WORLD); } else if (merger == 't') { vpsort::parallel_sort (work, work + mysize, std::less(), dist, stl_sort, median_split, tree_merge, MPI_COMM_WORLD); vpsort::print_perf_data (dist, stl_sort, median_split, tree_merge, MPI_COMM_WORLD); } else if (merger == 'f') { vpsort::parallel_sort (work, work + mysize, std::less(), dist, stl_sort, median_split, funnel_merge2, MPI_COMM_WORLD); vpsort::print_perf_data (dist, stl_sort, median_split, funnel_merge2, MPI_COMM_WORLD); } else { if (rank == 0) std::cerr << "Invalid arguments" << std::endl; } } else if (splitter == 'l') { vpsort::parallel_samplesort (work, work+mysize, std::less(), dist, stl_sort, nproc, nproc, MPI_COMM_WORLD); vpsort::print_perf_data_samplesort (dist, stl_sort, MPI_COMM_WORLD); } else { if (rank == 0) std::cerr << "Invalid arguments" << std::endl; } check_result (work, mysize); if (rank == 0) std::cout << std::endl; // Cleanup delete [] work; MPI_Finalize(); return 0; } CombBLAS_beta_16_2/psort-1.0/driver/Make.inc000644 000765 000024 00000000066 13212627400 021731 0ustar00aydinbulucstaff000000 000000 CXX=mpic++ CXXFLAGS=-O2 -DUSE_FUNNEL -Wall LIBS=-lm CombBLAS_beta_16_2/psort-1.0/driver/Makefile000644 000765 000024 00000000230 13212627400 022012 0ustar00aydinbulucstaff000000 000000 include ./Make.inc psort: psort_driver.cc $(CXX) $(CXXFLAGS) -I../src psort_driver.cc -o psort $(LIBS) strip psort clean: rm -f *.o psort results CombBLAS_beta_16_2/cmake/CombBLASConfig.cmake000644 000765 000024 00000000167 13271514735 022172 0ustar00aydinbulucstaff000000 000000 find_package(MPI REQUIRED QUIET) find_package(OpenMP QUIET) include("${CMAKE_CURRENT_LIST_DIR}/CombBLASTargets.cmake")CombBLAS_beta_16_2/BipartiteMatchings/ApproxWeightPerfectMatching.h000644 000765 000024 00000125005 13271404146 026770 0ustar00aydinbulucstaff000000 000000 // // ApproxWeightPerfectMatching.h // // // Created by Ariful Azad on 8/22/17. // // #ifndef ApproxWeightPerfectMatching_h #define ApproxWeightPerfectMatching_h #include "../CombBLAS.h" #include "BPMaximalMatching.h" #include "BPMaximumMatching.h" #include #include #include #include using namespace std; namespace combblas { template struct AWPM_param { int nprocs; int myrank; int pr; int pc; IT lncol; IT lnrow; IT localRowStart; IT localColStart; IT m_perproc; IT n_perproc; std::shared_ptr commGrid; }; double t1Comp, t1Comm, t2Comp, t2Comm, t3Comp, t3Comm, t4Comp, t4Comm, t5Comp, t5Comm, tUpdateMateComp; template std::vector> ExchangeData(std::vector>> & tempTuples, MPI_Comm World) { /* Create/allocate variables for vector assignment */ MPI_Datatype MPI_tuple; MPI_Type_contiguous(sizeof(std::tuple), MPI_CHAR, &MPI_tuple); MPI_Type_commit(&MPI_tuple); int nprocs; MPI_Comm_size(World, &nprocs); int * sendcnt = new int[nprocs]; int * recvcnt = new int[nprocs]; int * sdispls = new int[nprocs](); int * rdispls = new int[nprocs](); // Set the newly found vector entries IT totsend = 0; for(IT i=0; i(0)); std::vector< std::tuple > sendTuples(totsend); for(int i=0; i >().swap(tempTuples[i]); // clear memory } std::vector< std::tuple > recvTuples(totrecv); MPI_Alltoallv(sendTuples.data(), sendcnt, sdispls, MPI_tuple, recvTuples.data(), recvcnt, rdispls, MPI_tuple, World); DeleteAll(sendcnt, recvcnt, sdispls, rdispls); // free all memory MPI_Type_free(&MPI_tuple); return recvTuples; } template std::vector> ExchangeData1(std::vector>> & tempTuples, MPI_Comm World) { /* Create/allocate variables for vector assignment */ MPI_Datatype MPI_tuple; MPI_Type_contiguous(sizeof(std::tuple), MPI_CHAR, &MPI_tuple); MPI_Type_commit(&MPI_tuple); int nprocs; MPI_Comm_size(World, &nprocs); int * sendcnt = new int[nprocs]; int * recvcnt = new int[nprocs]; int * sdispls = new int[nprocs](); int * rdispls = new int[nprocs](); // Set the newly found vector entries IT totsend = 0; for(IT i=0; i(0)); std::vector< std::tuple > sendTuples(totsend); for(int i=0; i >().swap(tempTuples[i]); // clear memory } std::vector< std::tuple > recvTuples(totrecv); MPI_Alltoallv(sendTuples.data(), sendcnt, sdispls, MPI_tuple, recvTuples.data(), recvcnt, rdispls, MPI_tuple, World); DeleteAll(sendcnt, recvcnt, sdispls, rdispls); // free all memory MPI_Type_free(&MPI_tuple); return recvTuples; } // remember that getnrow() and getncol() require collectives // Hence, we save them once and pass them to this function template int OwnerProcs(SpParMat < IT, NT, DER > & A, IT grow, IT gcol, IT nrows, IT ncols) { auto commGrid = A.getcommgrid(); int procrows = commGrid->GetGridRows(); int proccols = commGrid->GetGridCols(); IT m_perproc = nrows / procrows; IT n_perproc = ncols / proccols; int pr, pc; if(m_perproc != 0) pr = std::min(static_cast(grow / m_perproc), procrows-1); else // all owned by the last processor row pr = procrows -1; if(n_perproc != 0) pc = std::min(static_cast(gcol / n_perproc), proccols-1); else pc = proccols-1; return commGrid->GetRank(pr, pc); } /* // Hence, we save them once and pass them to this function template int OwnerProcs(SpParMat < IT, NT, DER > & A, IT grow, IT gcol, IT nrows, IT ncols) { int pr1, pc1; if(m_perproc != 0) pr1 = std::min(static_cast(grow / m_perproc), pr-1); else // all owned by the last processor row pr1 = pr -1; if(n_perproc != 0) pc1 = std::min(static_cast(gcol / n_perproc), pc-1); else pc1 = pc-1; return commGrid->GetRank(pr1, pc1); } */ template std::vector> MateBcast(std::vector> sendTuples, MPI_Comm World) { /* Create/allocate variables for vector assignment */ MPI_Datatype MPI_tuple; MPI_Type_contiguous(sizeof(std::tuple) , MPI_CHAR, &MPI_tuple); MPI_Type_commit(&MPI_tuple); int nprocs; MPI_Comm_size(World, &nprocs); int * recvcnt = new int[nprocs]; int * rdispls = new int[nprocs](); int sendcnt = sendTuples.size(); MPI_Allgather(&sendcnt, 1, MPI_INT, recvcnt, 1, MPI_INT, World); std::partial_sum(recvcnt, recvcnt+nprocs-1, rdispls+1); IT totrecv = std::accumulate(recvcnt,recvcnt+nprocs, static_cast(0)); std::vector< std::tuple > recvTuples(totrecv); MPI_Allgatherv(sendTuples.data(), sendcnt, MPI_tuple, recvTuples.data(), recvcnt, rdispls,MPI_tuple,World ); DeleteAll(recvcnt, rdispls); // free all memory MPI_Type_free(&MPI_tuple); return recvTuples; } // ----------------------------------------------------------- // replicate weights of mates // Can be improved by removing AllReduce by All2All // ----------------------------------------------------------- template void ReplicateMateWeights( const AWPM_param& param, Dcsc*dcsc, const std::vector& colptr, std::vector& RepMateC2R, std::vector& RepMateWR2C, std::vector& RepMateWC2R) { fill(RepMateWC2R.begin(), RepMateWC2R.end(), static_cast(0)); fill(RepMateWR2C.begin(), RepMateWR2C.end(), static_cast(0)); #ifdef THREADED #pragma omp parallel for #endif for(int k=0; k= param.localRowStart && mj < (param.localRowStart+param.lnrow) ) { for(IT cp = colptr[k]; cp < colptr[k+1]; ++cp) { IT li = dcsc->ir[cp]; IT i = li + param.localRowStart; // TODO: use binary search to directly go to mj-th entry if more than 32 nonzero in this column if( i == mj) { RepMateWC2R[lj] = dcsc->numx[cp]; RepMateWR2C[mj-param.localRowStart] = dcsc->numx[cp]; //break; } } } } MPI_Comm ColWorld = param.commGrid->GetColWorld(); MPI_Comm RowWorld = param.commGrid->GetRowWorld(); MPI_Allreduce(MPI_IN_PLACE, RepMateWC2R.data(), RepMateWC2R.size(), MPIType(), MPI_SUM, ColWorld); MPI_Allreduce(MPI_IN_PLACE, RepMateWR2C.data(), RepMateWR2C.size(), MPIType(), MPI_SUM, RowWorld); } template NT Trace( SpParMat < IT, NT, DER > & A, IT& rettrnnz=0) { IT nrows = A.getnrow(); IT ncols = A.getncol(); auto commGrid = A.getcommgrid(); MPI_Comm World = commGrid->GetWorld(); int myrank=commGrid->GetRank(); int pr = commGrid->GetGridRows(); int pc = commGrid->GetGridCols(); //Information about the matrix distribution //Assume that A is an nrow x ncol matrix //The local submatrix is an lnrow x lncol matrix int rowrank = commGrid->GetRankInProcRow(); int colrank = commGrid->GetRankInProcCol(); IT m_perproc = nrows / pr; IT n_perproc = ncols / pc; DER* spSeq = A.seqptr(); // local submatrix IT localRowStart = colrank * m_perproc; // first row in this process IT localColStart = rowrank * n_perproc; // first col in this process IT trnnz = 0; NT trace = 0.0; for(auto colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { IT lj = colit.colid(); // local numbering IT j = lj + localColStart; for(auto nzit = spSeq->begnz(colit); nzit < spSeq->endnz(colit); ++nzit) { IT li = nzit.rowid(); IT i = li + localRowStart; if( i == j) { trnnz ++; trace += nzit.value(); } } } MPI_Allreduce(MPI_IN_PLACE, &trnnz, 1, MPIType(), MPI_SUM, World); MPI_Allreduce(MPI_IN_PLACE, &trace, 1, MPIType(), MPI_SUM, World); rettrnnz = trnnz; /* if(myrank==0) cout <<"nrows: " << nrows << " Nnz in the diag: " << trnnz << " sum of diag: " << trace << endl; */ return trace; } template NT MatchingWeight( std::vector& RepMateWC2R, MPI_Comm RowWorld, NT& minw) { NT w = 0; minw = 99999999999999.0; for(int i=0; i(), MPI_SUM, RowWorld); MPI_Allreduce(MPI_IN_PLACE, &minw, 1, MPIType(), MPI_MIN, RowWorld); return w; } // update the distributed mate vectors from replicated mate vectors template void UpdateMatching(FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row, std::vector& RepMateR2C, std::vector& RepMateC2R) { auto commGrid = mateRow2Col.getcommgrid(); MPI_Comm RowWorld = commGrid->GetRowWorld(); int rowroot = commGrid->GetDiagOfProcRow(); int pc = commGrid->GetGridCols(); // mateRow2Col is easy IT localLenR2C = mateRow2Col.LocArrSize(); //IT* localR2C = mateRow2Col.GetLocArr(); for(IT i=0, j = mateRow2Col.RowLenUntil(); i sendcnts(pc); std::vector dpls(pc); dpls[0] = 0; for(int i=1; i(), localC2R, localLenC2R, MPIType(),rowroot, RowWorld); } int ThreadBuffLenForBinning(int itemsize, int nbins) { // 1MB shared cache (per 2 cores) in KNL #ifndef L2_CACHE_SIZE #define L2_CACHE_SIZE 256000 #endif int THREAD_BUF_LEN = 256; while(true) { int bufferMem = THREAD_BUF_LEN * nbins * itemsize ; if(bufferMem>L2_CACHE_SIZE ) THREAD_BUF_LEN/=2; else break; } THREAD_BUF_LEN = std::min(nbins+1,THREAD_BUF_LEN); return THREAD_BUF_LEN; } template std::vector< std::tuple > Phase1(const AWPM_param& param, Dcsc* dcsc, const std::vector& colptr, const std::vector& RepMateR2C, const std::vector& RepMateC2R, const std::vector& RepMateWR2C, const std::vector& RepMateWC2R ) { double tstart = MPI_Wtime(); MPI_Comm World = param.commGrid->GetWorld(); //Step 1: Count the amount of data to be sent to different processors std::vector sendcnt(param.nprocs,0); // number items to be sent to each processor #ifdef THREADED #pragma omp parallel #endif { std::vector tsendcnt(param.nprocs,0); #ifdef THREADED #pragma omp for #endif for(int k=0; kir[cp]; IT i = li + param.localRowStart; IT mi = RepMateR2C[li]; if( i > mj) // TODO : stop when first come to this, may be use < { int rrank = param.m_perproc != 0 ? std::min(static_cast(mj / param.m_perproc), param.pr-1) : (param.pr-1); int crank = param.n_perproc != 0 ? std::min(static_cast(mi / param.n_perproc), param.pc-1) : (param.pc-1); int owner = param.commGrid->GetRank(rrank , crank); tsendcnt[owner]++; } } } for(int i=0; i(0)); std::vector sdispls (param.nprocs, 0); std::partial_sum(sendcnt.data(), sendcnt.data()+param.nprocs-1, sdispls.data()+1); std::vector< std::tuple > sendTuples(totsend); std::vector transferCount(param.nprocs,0); int THREAD_BUF_LEN = ThreadBuffLenForBinning(24, param.nprocs); //Step 2: Compile data to be sent to different processors #ifdef THREADED #pragma omp parallel #endif { std::vector tsendcnt(param.nprocs,0); std::vector> tsendTuples (param.nprocs*THREAD_BUF_LEN); #ifdef THREADED #pragma omp for #endif for(int k=0; kir[cp]; IT i = li + param.localRowStart; IT mi = RepMateR2C[li]; if( i > mj) // TODO : stop when first come to this, may be use < { double w = dcsc->numx[cp]- RepMateWR2C[li] - RepMateWC2R[lj]; int rrank = param.m_perproc != 0 ? std::min(static_cast(mj / param.m_perproc), param.pr-1) : (param.pr-1); int crank = param.n_perproc != 0 ? std::min(static_cast(mi / param.n_perproc), param.pc-1) : (param.pc-1); int owner = param.commGrid->GetRank(rrank , crank); if (tsendcnt[owner] < THREAD_BUF_LEN) { tsendTuples[THREAD_BUF_LEN * owner + tsendcnt[owner]] = std::make_tuple(mi, mj, w); tsendcnt[owner]++; } else { int tt = __sync_fetch_and_add(transferCount.data()+owner, THREAD_BUF_LEN); copy( tsendTuples.data()+THREAD_BUF_LEN * owner, tsendTuples.data()+THREAD_BUF_LEN * (owner+1) , sendTuples.data() + sdispls[owner]+ tt); tsendTuples[THREAD_BUF_LEN * owner] = std::make_tuple(mi, mj, w); tsendcnt[owner] = 1; } } } } for(int owner=0; owner < param.nprocs; owner++) { if (tsendcnt[owner] >0) { int tt = __sync_fetch_and_add(transferCount.data()+owner, tsendcnt[owner]); copy( tsendTuples.data()+THREAD_BUF_LEN * owner, tsendTuples.data()+THREAD_BUF_LEN * owner + tsendcnt[owner], sendTuples.data() + sdispls[owner]+ tt); } } } t1Comp = MPI_Wtime() - tstart; tstart = MPI_Wtime(); // Step 3: Communicate data std::vector recvcnt (param.nprocs); std::vector rdispls (param.nprocs, 0); MPI_Alltoall(sendcnt.data(), 1, MPI_INT, recvcnt.data(), 1, MPI_INT, World); std::partial_sum(recvcnt.data(), recvcnt.data()+param.nprocs-1, rdispls.data()+1); IT totrecv = std::accumulate(recvcnt.data(), recvcnt.data()+param.nprocs, static_cast(0)); MPI_Datatype MPI_tuple; MPI_Type_contiguous(sizeof(std::tuple), MPI_CHAR, &MPI_tuple); MPI_Type_commit(&MPI_tuple); std::vector< std::tuple > recvTuples1(totrecv); MPI_Alltoallv(sendTuples.data(), sendcnt.data(), sdispls.data(), MPI_tuple, recvTuples1.data(), recvcnt.data(), rdispls.data(), MPI_tuple, World); MPI_Type_free(&MPI_tuple); t1Comm = MPI_Wtime() - tstart; return recvTuples1; } template std::vector< std::tuple > Phase2(const AWPM_param& param, std::vector>& recvTuples, Dcsc* dcsc, const std::vector& colptr, const std::vector& RepMateR2C, const std::vector& RepMateC2R, const std::vector& RepMateWR2C, const std::vector& RepMateWC2R ) { MPI_Comm World = param.commGrid->GetWorld(); double tstart = MPI_Wtime(); // Step 1: Sort for effecient searching of indices __gnu_parallel::sort(recvTuples.begin(), recvTuples.end()); std::vector>> tempTuples1 (param.nprocs); std::vector sendcnt(param.nprocs,0); // number items to be sent to each processor //Step 2: Count the amount of data to be sent to different processors // Instead of binary search in each column, I am doing linear search // Linear search is faster here because, we need to search 40%-50% of nnz int nBins = 1; #ifdef THREADED #pragma omp parallel { nBins = omp_get_num_threads() * 4; } #endif #ifdef THREADED #pragma omp parallel for #endif for(int i=0; i tsendcnt(param.nprocs,0); for(int k=startBinIndex; k(recvTuples[k]); IT lcol = mi - param.localColStart; IT i = RepMateC2R[lcol]; IT idx1 = k; IT idx2 = colptr[lcol]; for(; std::get<0>(recvTuples[idx1]) == mi && idx2 < colptr[lcol+1];) //** { IT mj = std::get<1>(recvTuples[idx1]) ; IT lrow = mj - param.localRowStart; IT j = RepMateR2C[lrow]; IT lrowMat = dcsc->ir[idx2]; if(lrowMat == lrow) { NT weight = std::get<2>(recvTuples[idx1]); NT cw = weight + RepMateWR2C[lrow]; //w+W[M'[j],M[i]]; if (cw > 0) { int rrank = (param.m_perproc != 0) ? std::min(static_cast(mj / param.m_perproc), param.pr-1) : (param.pr-1); int crank = (param.n_perproc != 0) ? std::min(static_cast(j / param.n_perproc), param.pc-1) : (param.pc-1); int owner = param.commGrid->GetRank(rrank , crank); tsendcnt[owner]++; } idx1++; idx2++; } else if(lrowMat > lrow) idx1 ++; else idx2 ++; } for(;std::get<0>(recvTuples[idx1]) == mi ; idx1++); k = idx1; } for(int i=0; i(0)); std::vector sdispls (param.nprocs, 0); std::partial_sum(sendcnt.data(), sendcnt.data()+param.nprocs-1, sdispls.data()+1); std::vector< std::tuple > sendTuples(totsend); std::vector transferCount(param.nprocs,0); int THREAD_BUF_LEN = ThreadBuffLenForBinning(32, param.nprocs); //Step 3: Compile data to be sent to different processors #ifdef THREADED #pragma omp parallel for #endif for(int i=0; i tsendcnt(param.nprocs,0); std::vector> tsendTuples (param.nprocs*THREAD_BUF_LEN); for(int k=startBinIndex; k(recvTuples[k]); IT lcol = mi - param.localColStart; IT i = RepMateC2R[lcol]; IT idx1 = k; IT idx2 = colptr[lcol]; for(; std::get<0>(recvTuples[idx1]) == mi && idx2 < colptr[lcol+1];) //** { IT mj = std::get<1>(recvTuples[idx1]) ; IT lrow = mj - param.localRowStart; IT j = RepMateR2C[lrow]; IT lrowMat = dcsc->ir[idx2]; if(lrowMat == lrow) { NT weight = std::get<2>(recvTuples[idx1]); NT cw = weight + RepMateWR2C[lrow]; //w+W[M'[j],M[i]]; if (cw > 0) { int rrank = (param.m_perproc != 0) ? std::min(static_cast(mj / param.m_perproc), param.pr-1) : (param.pr-1); int crank = (param.n_perproc != 0) ? std::min(static_cast(j / param.n_perproc), param.pc-1) : (param.pc-1); int owner = param.commGrid->GetRank(rrank , crank); if (tsendcnt[owner] < THREAD_BUF_LEN) { tsendTuples[THREAD_BUF_LEN * owner + tsendcnt[owner]] = std::make_tuple(mj, mi, i, cw); tsendcnt[owner]++; } else { int tt = __sync_fetch_and_add(transferCount.data()+owner, THREAD_BUF_LEN); std::copy( tsendTuples.data()+THREAD_BUF_LEN * owner, tsendTuples.data()+THREAD_BUF_LEN * (owner+1) , sendTuples.data() + sdispls[owner]+ tt); tsendTuples[THREAD_BUF_LEN * owner] = std::make_tuple(mj, mi, i, cw); tsendcnt[owner] = 1; } } idx1++; idx2++; } else if(lrowMat > lrow) idx1 ++; else idx2 ++; } for(;std::get<0>(recvTuples[idx1]) == mi ; idx1++); k = idx1; } for(int owner=0; owner < param.nprocs; owner++) { if (tsendcnt[owner] >0) { int tt = __sync_fetch_and_add(transferCount.data()+owner, tsendcnt[owner]); std::copy( tsendTuples.data()+THREAD_BUF_LEN * owner, tsendTuples.data()+THREAD_BUF_LEN * owner + tsendcnt[owner], sendTuples.data() + sdispls[owner]+ tt); } } } // Step 4: Communicate data t2Comp = MPI_Wtime() - tstart; tstart = MPI_Wtime(); std::vector recvcnt (param.nprocs); std::vector rdispls (param.nprocs, 0); MPI_Alltoall(sendcnt.data(), 1, MPI_INT, recvcnt.data(), 1, MPI_INT, World); std::partial_sum(recvcnt.data(), recvcnt.data()+param.nprocs-1, rdispls.data()+1); IT totrecv = std::accumulate(recvcnt.data(), recvcnt.data()+param.nprocs, static_cast(0)); MPI_Datatype MPI_tuple; MPI_Type_contiguous(sizeof(std::tuple), MPI_CHAR, &MPI_tuple); MPI_Type_commit(&MPI_tuple); std::vector< std::tuple > recvTuples1(totrecv); MPI_Alltoallv(sendTuples.data(), sendcnt.data(), sdispls.data(), MPI_tuple, recvTuples1.data(), recvcnt.data(), rdispls.data(), MPI_tuple, World); MPI_Type_free(&MPI_tuple); t2Comm = MPI_Wtime() - tstart; return recvTuples1; } // Old version of Phase 2 // Not multithreaded (uses binary search) template std::vector>> Phase2_old(const AWPM_param& param, std::vector>& recvTuples, Dcsc* dcsc, const std::vector& colptr, const std::vector& RepMateR2C, const std::vector& RepMateC2R, const std::vector& RepMateWR2C, const std::vector& RepMateWC2R ) { std::vector>> tempTuples1 (param.nprocs); for(int k=0; k(recvTuples[k]) ; IT mj = std::get<1>(recvTuples[k]) ; IT i = RepMateC2R[mi - param.localColStart]; NT weight = std::get<2>(recvTuples[k]); if(colptr[mi- param.localColStart+1] > colptr[mi- param.localColStart] ) { IT * ele = find(dcsc->ir+colptr[mi - param.localColStart], dcsc->ir+colptr[mi - param.localColStart+1], mj - param.localRowStart); // TODO: Add a function that returns the edge weight directly if (ele != dcsc->ir+colptr[mi - param.localColStart+1]) { NT cw = weight + RepMateWR2C[mj - param.localRowStart]; //w+W[M'[j],M[i]]; if (cw > 0) { IT j = RepMateR2C[mj - param.localRowStart]; int rrank = (param.m_perproc != 0) ? std::min(static_cast(mj / param.m_perproc), param.pr-1) : (param.pr-1); int crank = (param.n_perproc != 0) ? std::min(static_cast(j / param.n_perproc), param.pc-1) : (param.pc-1); int owner = param.commGrid->GetRank(rrank , crank); tempTuples1[owner].push_back(make_tuple(mj, mi, i, cw)); } } } } return tempTuples1; } template void TwoThirdApprox(SpParMat < IT, NT, DER > & A, FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row) { // Information about CommGrid and matrix layout // Assume that processes are laid in (pr x pc) process grid auto commGrid = A.getcommgrid(); int myrank=commGrid->GetRank(); MPI_Comm World = commGrid->GetWorld(); MPI_Comm ColWorld = commGrid->GetColWorld(); MPI_Comm RowWorld = commGrid->GetRowWorld(); int nprocs = commGrid->GetSize(); int pr = commGrid->GetGridRows(); int pc = commGrid->GetGridCols(); int rowrank = commGrid->GetRankInProcRow(); int colrank = commGrid->GetRankInProcCol(); int diagneigh = commGrid->GetComplementRank(); //Information about the matrix distribution //Assume that A is an nrow x ncol matrix //The local submatrix is an lnrow x lncol matrix IT nrows = A.getnrow(); IT ncols = A.getncol(); IT nnz = A.getnnz(); IT m_perproc = nrows / pr; IT n_perproc = ncols / pc; DER* spSeq = A.seqptr(); // local submatrix Dcsc* dcsc = spSeq->GetDCSC(); IT lnrow = spSeq->getnrow(); IT lncol = spSeq->getncol(); IT localRowStart = colrank * m_perproc; // first row in this process IT localColStart = rowrank * n_perproc; // first col in this process AWPM_param param; param.nprocs = nprocs; param.pr = pr; param.pc = pc; param.lncol = lncol; param.lnrow = lnrow; param.m_perproc = m_perproc; param.n_perproc = n_perproc; param.localRowStart = localRowStart; param.localColStart = localColStart; param.myrank = myrank; param.commGrid = commGrid; double t1CompAll = 0, t1CommAll = 0, t2CompAll = 0, t2CommAll = 0, t3CompAll = 0, t3CommAll = 0, t4CompAll = 0, t4CommAll = 0, t5CompAll = 0, t5CommAll = 0, tUpdateMateCompAll = 0, tUpdateWeightAll = 0; // ----------------------------------------------------------- // replicate mate vectors for mateCol2Row // Communication cost: same as the first communication of SpMV // ----------------------------------------------------------- int xsize = (int) mateCol2Row.LocArrSize(); int trxsize = 0; MPI_Status status; MPI_Sendrecv(&xsize, 1, MPI_INT, diagneigh, TRX, &trxsize, 1, MPI_INT, diagneigh, TRX, World, &status); std::vector trxnums(trxsize); MPI_Sendrecv(mateCol2Row.GetLocArr(), xsize, MPIType(), diagneigh, TRX, trxnums.data(), trxsize, MPIType(), diagneigh, TRX, World, &status); std::vector colsize(pc); colsize[colrank] = trxsize; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colsize.data(), 1, MPI_INT, ColWorld); std::vector dpls(pc,0); // displacements (zero initialized pid) std::partial_sum(colsize.data(), colsize.data()+pc-1, dpls.data()+1); int accsize = std::accumulate(colsize.data(), colsize.data()+pc, 0); std::vector RepMateC2R(accsize); MPI_Allgatherv(trxnums.data(), trxsize, MPIType(), RepMateC2R.data(), colsize.data(), dpls.data(), MPIType(), ColWorld); // ----------------------------------------------------------- // ----------------------------------------------------------- // replicate mate vectors for mateRow2Col // Communication cost: same as the first communication of SpMV // (minus the cost of tranposing vector) // ----------------------------------------------------------- xsize = (int) mateRow2Col.LocArrSize(); std::vector rowsize(pr); rowsize[rowrank] = xsize; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, rowsize.data(), 1, MPI_INT, RowWorld); std::vector rdpls(pr,0); // displacements (zero initialized pid) std::partial_sum(rowsize.data(), rowsize.data()+pr-1, rdpls.data()+1); accsize = std::accumulate(rowsize.data(), rowsize.data()+pr, 0); std::vector RepMateR2C(accsize); MPI_Allgatherv(mateRow2Col.GetLocArr(), xsize, MPIType(), RepMateR2C.data(), rowsize.data(), rdpls.data(), MPIType(), RowWorld); // ----------------------------------------------------------- // Getting column pointers for all columns (for CSC-style access) std::vector colptr (lncol+1,-1); for(auto colit = spSeq->begcol(); colit != spSeq->endcol(); ++colit) // iterate over all columns { IT lj = colit.colid(); // local numbering colptr[lj] = colit.colptr(); } colptr[lncol] = spSeq->getnnz(); for(IT k=lncol-1; k>=0; k--) { if(colptr[k] == -1) { colptr[k] = colptr[k+1]; } } // TODO: will this fail empty local matrix where every entry of colptr will be zero // ----------------------------------------------------------- // replicate weights of mates // ----------------------------------------------------------- std::vector RepMateWR2C(lnrow); std::vector RepMateWC2R(lncol); ReplicateMateWeights(param, dcsc, colptr, RepMateC2R, RepMateWR2C, RepMateWC2R); int iterations = 0; NT minw; NT weightCur = MatchingWeight(RepMateWC2R, RowWorld, minw); NT weightPrev = weightCur - 999999999999; while(weightCur > weightPrev && iterations++ < 10) { if(myrank==0) std::cout << "Iteration " << iterations << ". matching weight: sum = "<< weightCur << " min = " << minw << std::endl; // C requests // each row is for a processor where C requests will be sent to double tstart; std::vector> recvTuples = Phase1(param, dcsc, colptr, RepMateR2C, RepMateC2R, RepMateWR2C, RepMateWC2R ); std::vector> recvTuples1 = Phase2(param, recvTuples, dcsc, colptr, RepMateR2C, RepMateC2R, RepMateWR2C, RepMateWC2R ); std::vector< std::tuple >().swap(recvTuples); tstart = MPI_Wtime(); std::vector> bestTuplesPhase3 (lncol); #ifdef THREADED #pragma omp parallel for #endif for(int k=0; k(recvTuples1[k]) ; IT mi = std::get<1>(recvTuples1[k]) ; IT i = std::get<2>(recvTuples1[k]) ; NT weight = std::get<3>(recvTuples1[k]); IT j = RepMateR2C[mj - localRowStart]; IT lj = j - localColStart; // we can get rid of the first check if edge weights are non negative if( (std::get<0>(bestTuplesPhase3[lj]) == -1) || (weight > std::get<3>(bestTuplesPhase3[lj])) ) { bestTuplesPhase3[lj] = std::make_tuple(i,mi,mj,weight); } } std::vector>> tempTuples1 (nprocs); for(int k=0; k(bestTuplesPhase3[k]) != -1) { //IT j = RepMateR2C[mj - localRowStart]; /// fix me IT i = std::get<0>(bestTuplesPhase3[k]) ; IT mi = std::get<1>(bestTuplesPhase3[k]) ; IT mj = std::get<2>(bestTuplesPhase3[k]) ; IT j = RepMateR2C[mj - localRowStart]; NT weight = std::get<3>(bestTuplesPhase3[k]); int owner = OwnerProcs(A, i, mi, nrows, ncols); tempTuples1[owner].push_back(std::make_tuple(i, j, mj, weight)); } } //vector< tuple >().swap(recvTuples1); double t3Comp = MPI_Wtime() - tstart; tstart = MPI_Wtime(); recvTuples1 = ExchangeData1(tempTuples1, World); double t3Comm = MPI_Wtime() - tstart; tstart = MPI_Wtime(); std::vector> bestTuplesPhase4 (lncol); // we could have used lnrow in both bestTuplesPhase3 and bestTuplesPhase4 // Phase 4 // at the owner of (i,mi) #ifdef THREADED #pragma omp parallel for #endif for(int k=0; k(recvTuples1[k]) ; IT j = std::get<1>(recvTuples1[k]) ; IT mj = std::get<2>(recvTuples1[k]) ; IT mi = RepMateR2C[i-localRowStart]; NT weight = std::get<3>(recvTuples1[k]); IT lmi = mi - localColStart; //IT lj = j - localColStart; // cout <<"****" << i << " " << mi << " "<< j << " " << mj << " " << get<0>(bestTuplesPhase4[lj]) << endl; // we can get rid of the first check if edge weights are non negative if( ((std::get<0>(bestTuplesPhase4[lmi]) == -1) || (weight > std::get<4>(bestTuplesPhase4[lmi]))) && std::get<0>(bestTuplesPhase3[lmi])==-1 ) { bestTuplesPhase4[lmi] = std::make_tuple(i,j,mi,mj,weight); //cout << "(("<< i << " " << mi << " "<< j << " " << mj << "))"<< endl; } } std::vector>> winnerTuples (nprocs); for(int k=0; k(bestTuplesPhase4[k]) != -1) { //int owner = OwnerProcs(A, get<0>(bestTuples[k]), get<1>(bestTuples[k]), nrows, ncols); // (i,mi) //tempTuples[owner].push_back(bestTuples[k]); IT i = std::get<0>(bestTuplesPhase4[k]) ; IT j = std::get<1>(bestTuplesPhase4[k]) ; IT mi = std::get<2>(bestTuplesPhase4[k]) ; IT mj = std::get<3>(bestTuplesPhase4[k]) ; int owner = OwnerProcs(A, mj, j, nrows, ncols); winnerTuples[owner].push_back(std::make_tuple(i, j, mi, mj)); /// be very careful here // passing the opposite of the matching to the owner of (i,mi) owner = OwnerProcs(A, i, mi, nrows, ncols); winnerTuples[owner].push_back(std::make_tuple(mj, mi, j, i)); } } //vector< tuple >().swap(recvTuples1); double t4Comp = MPI_Wtime() - tstart; tstart = MPI_Wtime(); std::vector> recvWinnerTuples = ExchangeData1(winnerTuples, World); double t4Comm = MPI_Wtime() - tstart; tstart = MPI_Wtime(); // at the owner of (mj,j) std::vector> rowBcastTuples(recvWinnerTuples.size()); //(mi,mj) std::vector> colBcastTuples(recvWinnerTuples.size()); //(j,i) #ifdef THREADED #pragma omp parallel for #endif for(int k=0; k(recvWinnerTuples[k]) ; IT j = std::get<1>(recvWinnerTuples[k]) ; IT mi = std::get<2>(recvWinnerTuples[k]) ; IT mj = std::get<3>(recvWinnerTuples[k]); colBcastTuples[k] = std::make_tuple(j,i); rowBcastTuples[k] = std::make_tuple(mj,mi); } double t5Comp = MPI_Wtime() - tstart; tstart = MPI_Wtime(); std::vector> updatedR2C = MateBcast(rowBcastTuples, RowWorld); std::vector> updatedC2R = MateBcast(colBcastTuples, ColWorld); double t5Comm = MPI_Wtime() - tstart; tstart = MPI_Wtime(); #ifdef THREADED #pragma omp parallel for #endif for(int k=0; k(updatedR2C[k]); IT mate = std::get<1>(updatedR2C[k]); RepMateR2C[row-localRowStart] = mate; } #ifdef THREADED #pragma omp parallel for #endif for(int k=0; k(updatedC2R[k]); IT mate = std::get<1>(updatedC2R[k]); RepMateC2R[col-localColStart] = mate; } double tUpdateMateComp = MPI_Wtime() - tstart; tstart = MPI_Wtime(); // update weights of matched edges // we can do better than this since we are doing sparse updates ReplicateMateWeights(param, dcsc, colptr, RepMateC2R, RepMateWR2C, RepMateWC2R); double tUpdateWeight = MPI_Wtime() - tstart; weightPrev = weightCur; weightCur = MatchingWeight(RepMateWC2R, RowWorld, minw); //UpdateMatching(mateRow2Col, mateCol2Row, RepMateR2C, RepMateC2R); //CheckMatching(mateRow2Col,mateCol2Row); if(myrank==0) { std::cout << t1Comp << " " << t1Comm << " "<< t2Comp << " " << t2Comm << " " << t3Comp << " " << t3Comm << " " << t4Comp << " " << t4Comm << " " << t5Comp << " " << t5Comm << " " << tUpdateMateComp << " " << tUpdateWeight << std::endl; t1CompAll += t1Comp; t1CommAll += t1Comm; t2CompAll += t2Comp; t2CommAll += t2Comm; t3CompAll += t3Comp; t3CommAll += t3Comm; t4CompAll += t4Comp; t4CommAll += t4Comm; t5CompAll += t5Comp; t5CommAll += t5Comm; tUpdateMateCompAll += tUpdateMateComp; tUpdateWeightAll += tUpdateWeight; } } if(myrank==0) { std::cout << "=========== overal timing ==========" << std::endl; std::cout << t1CompAll << " " << t1CommAll << " " << t2CompAll << " " << t2CommAll << " " << t3CompAll << " " << t3CommAll << " " << t4CompAll << " " << t4CommAll << " " << t5CompAll << " " << t5CommAll << " " << tUpdateMateCompAll << " " << tUpdateWeightAll << std::endl; } // update the distributed mate vectors from replicated mate vectors UpdateMatching(mateRow2Col, mateCol2Row, RepMateR2C, RepMateC2R); //weightCur = MatchingWeight(RepMateWC2R, RowWorld); } template void TransformWeight(SpParMat < IT, NT, DER > & A, bool applylog) { //A.Apply([](NT val){return log(1+abs(val));}); // if the matrix has explicit zero entries, we can still have problem. // One solution is to remove explicit zero entries before cardinality matching (to be tested) //A.Apply([](NT val){if(val==0) return log(numeric_limits::min()); else return log(fabs(val));}); A.Apply([](NT val){return (fabs(val));}); FullyDistVec maxvRow(A.getcommgrid()); A.Reduce(maxvRow, Row, maximum(), static_cast(numeric_limits::lowest())); A.DimApply(Row, maxvRow, [](NT val, NT maxval){return val/maxval;}); FullyDistVec maxvCol(A.getcommgrid()); A.Reduce(maxvCol, Column, maximum(), static_cast(numeric_limits::lowest())); A.DimApply(Column, maxvCol, [](NT val, NT maxval){return val/maxval;}); if(applylog) A.Apply([](NT val){return log(val);}); } template void AWPM(SpParMat < IT, NT, SpDCCols > & A1, FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row, bool optimizeProd=true) { SpParMat < IT, NT, SpDCCols > A(A1); // creating a copy because it is being transformed if(optimizeProd) TransformWeight(A, true); else TransformWeight(A, false); SpParMat < IT, NT, SpCCols > Acsc(A); SpParMat < IT, NT, SpDCCols > Abool(A); FullyDistVec degCol(A.getcommgrid()); Abool.Reduce(degCol, Column, plus(), static_cast(0)); double ts; // Compute the initial trace IT diagnnz; double origWeight = Trace(A, diagnnz); bool isOriginalPerfect = diagnnz==A.getnrow(); // compute the maximal matching WeightedGreedy(Acsc, mateRow2Col, mateCol2Row, degCol); double mclWeight = MatchingWeight( A, mateRow2Col, mateCol2Row); SpParHelper::Print("After Greedy sanity check\n"); bool isPerfectMCL = CheckMatching(mateRow2Col,mateCol2Row); // if the original matrix has a perfect matching and better weight if(isOriginalPerfect && mclWeight<=origWeight) { SpParHelper::Print("Maximal is not better that the natural ordering. Hence, keeping the natural ordering.\n"); mateRow2Col.iota(A.getnrow(), 0); mateCol2Row.iota(A.getncol(), 0); mclWeight = origWeight; isPerfectMCL = true; } // MCM double tmcm = 0; double mcmWeight = mclWeight; if(!isPerfectMCL) // run MCM only if we don't have a perfect matching { ts = MPI_Wtime(); maximumMatching(Acsc, mateRow2Col, mateCol2Row, true, false, true); tmcm = MPI_Wtime() - ts; mcmWeight = MatchingWeight( A, mateRow2Col, mateCol2Row) ; SpParHelper::Print("After MCM sanity check\n"); CheckMatching(mateRow2Col,mateCol2Row); } // AWPM ts = MPI_Wtime(); TwoThirdApprox(A, mateRow2Col, mateCol2Row); double tawpm = MPI_Wtime() - ts; double awpmWeight = MatchingWeight( A, mateRow2Col, mateCol2Row) ; SpParHelper::Print("After AWPM sanity check\n"); CheckMatching(mateRow2Col,mateCol2Row); if(isOriginalPerfect && awpmWeight #include #include #include #include #include #include #include #include "BPMaximalMatching.h" #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #include int cblas_splits = 1; #endif using namespace std; using namespace combblas; bool prune, mvInvertMate, randMM, moreSplit; int init; bool randMaximal; bool fewexp; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } struct VertexType { public: VertexType(int64_t p=-1, int64_t r=-1, int16_t pr=0){parent=p; root = r; prob = pr;}; friend bool operator<(const VertexType & vtx1, const VertexType & vtx2 ) { if(vtx1.prob==vtx2.prob) return vtx1.parent > PSpMat_Bool; /* Remove isolated vertices and purmute */ void removeIsolated(PSpMat_Bool & A) { int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(A.getcommgrid()); FullyDistVec nonisoRowV; // id's of non-isolated (connected) Row vertices FullyDistVec nonisoColV; // id's of non-isolated (connected) Col vertices FullyDistVec nonisov; // id's of non-isolated (connected) vertices A.Reduce(*ColSums, Column, plus(), static_cast(0)); A.Reduce(*RowSums, Row, plus(), static_cast(0)); // this steps for general graph /* ColSums->EWiseApply(*RowSums, plus()); not needed for bipartite graph nonisov = ColSums->FindInds(bind2nd(greater(), 0)); nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A.operator()(nonisov, nonisov, true); // in-place permute to save memory */ // this steps for bipartite graph nonisoColV = ColSums->FindInds(bind2nd(greater(), 0)); nonisoRowV = RowSums->FindInds(bind2nd(greater(), 0)); delete ColSums; delete RowSums; { nonisoColV.RandPerm(); nonisoRowV.RandPerm(); } int64_t nrows1=A.getnrow(), ncols1=A.getncol(), nnz1 = A.getnnz(); double avgDeg1 = (double) nnz1/(nrows1+ncols1); A.operator()(nonisoRowV, nonisoColV, true); int64_t nrows2=A.getnrow(), ncols2=A.getncol(), nnz2 = A.getnnz(); double avgDeg2 = (double) nnz2/(nrows2+ncols2); if(myrank == 0) { cout << "ncol nrows nedges deg \n"; cout << nrows1 << " " << ncols1 << " " << nnz1 << " " << avgDeg1 << " \n"; cout << nrows2 << " " << ncols2 << " " << nnz2 << " " << avgDeg2 << " \n"; } MPI_Barrier(MPI_COMM_WORLD); } void ShowUsage() { int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank == 0) { cout << "\n-------------- usage --------------\n"; cout << "Usage (random matrix): ./maximal \n"; cout << "Usage (input matrix): ./maximal \n\n"; cout << " \n-------------- meaning of arguments ----------\n"; cout << "** er: Erdos-Renyi, g500: Graph500 benchmark, ssca: SSCA benchmark\n"; cout << "** scale: matrix dimention is 2^scale\n"; cout << "** edgefactor: average degree of vertices\n"; cout << "** algo : maximal matching algorithm used to initialize\n "; cout << " greedy: greedy init , ks: Karp-Sipser, dmd: dynamic mindegree\n"; cout << " default: dynamic mindegree\n"; cout << "** (optional) rand: random parent selection in greedy/Karp-Sipser\n" ; cout << "** (optional) moreSplit: more splitting of Matrix.\n" ; cout << "(order of optional arguments does not matter)\n"; cout << " \n-------------- examples ----------\n"; cout << "Example: mpirun -np 4 ./maximal g500 18 16 ks rand" << endl; cout << "Example: mpirun -np 4 ./maximal input cage12.mtx dmd\n" << endl; } } void GetOptions(char* argv[], int argc) { string allArg=""; for(int i=0; i degCol) { FullyDistVec mateRow2Col ( A.getcommgrid(), A.getnrow(), (int64_t) -1); FullyDistVec mateCol2Row ( A.getcommgrid(), A.getncol(), (int64_t) -1); // best option init = DMD; randMaximal = false; //showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + KS init = KARP_SIPSER; randMaximal = true; //showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + Greedy init = GREEDY; randMaximal = true; //showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + KS init = KARP_SIPSER; randMaximal = false; //showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + Greedy init = GREEDY; randMaximal = false; //showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); } int main(int argc, char* argv[]) { // ------------ initialize MPI --------------- int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { ShowUsage(); MPI_Finalize(); return -1; } init = DMD; randMaximal = false; moreSplit = false; // ------------ Process input arguments and build matrix --------------- { PSpMat_Bool * ABool; ostringstream tinfo; double t01, t02; if(string(argv[1]) == string("input")) // input option { ABool = new PSpMat_Bool(); string filename(argv[2]); tinfo.str(""); tinfo << "**** Reading input matrix: " << filename << " ******* " << endl; SpParHelper::Print(tinfo.str()); t01 = MPI_Wtime(); ABool->ParallelReadMM(filename, true, maximum()); t02 = MPI_Wtime(); ABool->PrintInfo(); tinfo.str(""); tinfo << "Reader took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); GetOptions(argv+3, argc-3); } else if(argc < 4) { ShowUsage(); MPI_Finalize(); return -1; } else { unsigned scale = (unsigned) atoi(argv[2]); unsigned EDGEFACTOR = (unsigned) atoi(argv[3]); double initiator[4]; if(string(argv[1]) == string("er")) { initiator[0] = .25; initiator[1] = .25; initiator[2] = .25; initiator[3] = .25; } else if(string(argv[1]) == string("g500")) { initiator[0] = .57; initiator[1] = .19; initiator[2] = .19; initiator[3] = .05; } else if(string(argv[1]) == string("ssca")) { initiator[0] = .6; initiator[1] = .4/3; initiator[2] = .4/3; initiator[3] = .4/3; } else { if(myrank == 0) printf("The input type - %s - is not recognized.\n", argv[2]); MPI_Abort(MPI_COMM_WORLD, 1); } SpParHelper::Print("Generating input matrix....\n"); t01 = MPI_Wtime(); DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true); ABool = new PSpMat_Bool(*DEL, false); delete DEL; t02 = MPI_Wtime(); ABool->PrintInfo(); tinfo.str(""); tinfo << "Generator took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); //Symmetricize(*ABool); //removeIsolated(*ABool); //SpParHelper::Print("Generated matrix symmetricized....\n"); ABool->PrintInfo(); GetOptions(argv+4, argc-4); } // randomly permute for load balance SpParHelper::Print("Performing random permuation of matrix.\n"); FullyDistVec prow(ABool->getcommgrid()); FullyDistVec pcol(ABool->getcommgrid()); prow.iota(ABool->getnrow(), 0); pcol.iota(ABool->getncol(), 0); prow.RandPerm(); pcol.RandPerm(); (*ABool)(prow, pcol, true); SpParHelper::Print("Performed random permuation of matrix.\n"); PSpMat_Bool A = *ABool; PSpMat_Bool AT = A; if(ABool->getnrow() > ABool->getncol()) AT.Transpose(); else A.Transpose(); // Reduce is not multithreaded, so I am doing it here FullyDistVec degCol(A.getcommgrid()); A.Reduce(degCol, Column, plus(), static_cast(0)); int nthreads; #ifdef _OPENMP #pragma omp parallel { int splitPerThread = 1; if(moreSplit) splitPerThread = 4; nthreads = omp_get_num_threads(); cblas_splits = nthreads*splitPerThread; } tinfo.str(""); tinfo << "Threading activated with " << nthreads << " threads, and matrix split into "<< cblas_splits << " parts" << endl; SpParHelper::Print(tinfo.str()); A.ActivateThreading(cblas_splits); // note: crash on empty matrix AT.ActivateThreading(cblas_splits); #endif SpParHelper::Print(" #####################################################\n"); SpParHelper::Print(" ################## Run 1 ############################\n"); SpParHelper::Print(" #####################################################\n"); experiment(A, AT, degCol); SpParHelper::Print(" #####################################################\n"); SpParHelper::Print(" ################## Run 2 ############################\n"); SpParHelper::Print(" #####################################################\n"); experiment(A, AT, degCol); SpParHelper::Print(" #####################################################\n"); SpParHelper::Print(" ################## Run 3 ############################\n"); SpParHelper::Print(" #####################################################\n"); experiment(A, AT, degCol); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/BipartiteMatchings/._.DS_Store000644 000765 000024 00000000170 13271513713 023106 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/BipartiteMatchings/.DS_Store000644 000765 000024 00000014004 13271513713 022672 0ustar00aydinbulucstaff000000 000000 Bud1%  @€ @€ @€ @ E%DSDB`€ @€ @€ @CombBLAS_beta_16_2/BipartiteMatchings/ApproxWeightPerfectMatching.cpp000644 000765 000024 00000023032 13271404146 027320 0ustar00aydinbulucstaff000000 000000 #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #include int cblas_splits = 1; #endif #include "../CombBLAS.h" #include #include #include #include #include #include #include #include #include #include "BPMaximalMatching.h" #include "BPMaximumMatching.h" #include "ApproxWeightPerfectMatching.h" using namespace std; using namespace combblas; // algorithmic options bool prune,randMM, moreSplit; int init; bool randMaximal; bool fewexp; bool randPerm; bool saveMatching; string ofname; typedef SpParMat < int64_t, bool, SpDCCols > Par_DCSC_Bool; typedef SpParMat < int64_t, int64_t, SpDCCols > Par_DCSC_int64_t; typedef SpParMat < int64_t, double, SpDCCols > Par_DCSC_Double; typedef SpParMat < int64_t, double, SpCCols > Par_CSC_Double; typedef SpParMat < int64_t, bool, SpCCols > Par_CSC_Bool; void ShowUsage() { int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank == 0) { cout << "\n-------------- usage --------------\n"; cout << "Usage: ./awpm -input \n"; cout << "Optional parameters: -randPerm: randomly permute the matrix for load balance (default: no random permutation)\n"; cout << " -optsum: Optimize the sum of diagonal (default: Optimize the product of diagonal)\n"; cout << " -noWeightedCard: do not use weighted cardinality matching (default: use weighted cardinality matching)\n"; cout << " -output : output file name (if not provided: inputfile.awpm.txt)\n"; cout << " \n-------------- examples ----------\n"; cout << "Example: mpirun -np 4 ./awpm -input cage12.mtx \n" << endl; cout << "(output matching is saved to cage12.mtx.awpm.txt)\n" << endl; } } int main(int argc, char* argv[]) { // ------------ initialize MPI --------------- int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { ShowUsage(); MPI_Finalize(); return -1; } init = DMD; randMaximal = false; prune = false; randMM = true; moreSplit = false; fewexp=false; saveMatching = true; ofname = ""; randPerm = false; bool optimizeProd = true; // by default optimize sum_log_abs(aii) (after equil) bool weightedCard = true; string ifilename = ""; string ofname = ""; for(int i = 1; iParallelReadMM(ifilename, true, maximum()); // one-based matrix market file t02 = MPI_Wtime(); if(AWeighted->getnrow() != AWeighted->getncol()) { SpParHelper::Print("Rectangular matrix: Can not compute a perfect matching.\n"); MPI_Finalize(); return -1; } tinfo.str(""); tinfo << "Reading input matrix in" << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); SpParHelper::Print("Pruning explicit zero entries....\n"); AWeighted->Prune([](double val){return fabs(val)==0;}, true); AWeighted->PrintInfo(); } else { ShowUsage(); MPI_Finalize(); return -1; } // ***** careful: if you permute the matrix, you have the permute the matching vectors as well!! // randomly permute for load balance FullyDistVec randp( AWeighted->getcommgrid()); if(randPerm) { if(AWeighted->getnrow() == AWeighted->getncol()) { randp.iota(AWeighted->getnrow(), 0); randp.RandPerm(); (*AWeighted)(randp,randp,true); SpParHelper::Print("Matrix is randomly permuted for load balance.\n"); } else { SpParHelper::Print("Rectangular matrix: Can not apply symmetric permutation.\n"); } } Par_DCSC_Bool A = *AWeighted; //just to compute degree // Reduce is not multithreaded, so I am doing it here FullyDistVec degCol(A.getcommgrid()); A.Reduce(degCol, Column, plus(), static_cast(0)); // transform weights if(optimizeProd) TransformWeight(*AWeighted, true); else TransformWeight(*AWeighted, false); // convert to CSC for SpMSpV calls Par_CSC_Double AWeightedCSC(*AWeighted); Par_CSC_Bool ABoolCSC(*AWeighted); // Compute the initial trace int64_t diagnnz; double origWeight = Trace(*AWeighted, diagnnz); bool isOriginalPerfect = diagnnz==A.getnrow(); FullyDistVec mateRow2Col ( A.getcommgrid(), A.getnrow(), (int64_t) -1); FullyDistVec mateCol2Row ( A.getcommgrid(), A.getncol(), (int64_t) -1); init = DMD; randMaximal = false; randMM = false; prune = true; // Maximal double ts = MPI_Wtime(); if(weightedCard) WeightedGreedy(AWeightedCSC, mateRow2Col, mateCol2Row, degCol); else WeightedGreedy(ABoolCSC, mateRow2Col, mateCol2Row, degCol); double tmcl = MPI_Wtime() - ts; double mclWeight = MatchingWeight( *AWeighted, mateRow2Col, mateCol2Row); SpParHelper::Print("After Greedy sanity check\n"); bool isPerfectMCL = CheckMatching(mateRow2Col,mateCol2Row); if(isOriginalPerfect && mclWeight<=origWeight) // keep original { SpParHelper::Print("Maximal is not better that the natural ordering. Hence, keeping the natural ordering.\n"); mateRow2Col.iota(A.getnrow(), 0); mateCol2Row.iota(A.getncol(), 0); mclWeight = origWeight; isPerfectMCL = true; } // MCM double tmcm = 0; double mcmWeight = mclWeight; if(!isPerfectMCL) // run MCM only if we don't have a perfect matching { ts = MPI_Wtime(); if(weightedCard) maximumMatching(AWeightedCSC, mateRow2Col, mateCol2Row, true, false, true); else maximumMatching(AWeightedCSC, mateRow2Col, mateCol2Row, true, false, false); tmcm = MPI_Wtime() - ts; mcmWeight = MatchingWeight( *AWeighted, mateRow2Col, mateCol2Row) ; SpParHelper::Print("After MCM sanity check\n"); CheckMatching(mateRow2Col,mateCol2Row); } // AWPM ts = MPI_Wtime(); TwoThirdApprox(*AWeighted, mateRow2Col, mateCol2Row); double tawpm = MPI_Wtime() - ts; double awpmWeight = MatchingWeight( *AWeighted, mateRow2Col, mateCol2Row) ; SpParHelper::Print("After AWPM sanity check\n"); CheckMatching(mateRow2Col,mateCol2Row); if(isOriginalPerfect && awpmWeight0) { // inverse permutation FullyDistVecinvRandp = randp.sort(); mateRow2Col = mateRow2Col(invRandp); } if(saveMatching && ofname!="") { mateRow2Col.ParallelWrite(ofname,false,false); } } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/BipartiteMatchings/gathertest.cpp000644 000765 000024 00000004011 13212627400 024054 0ustar00aydinbulucstaff000000 000000 #include "../CombBLAS.h" #include #include #include #include #include #include #include #include int main(int argc, char* argv[]) { // ------------ initialize MPI --------------- int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); { int totalen = (int64_t) atoi(argv[1]); int locallen = totalen/nprocs; totalen = locallen * nprocs; vector ind1 (locallen, -1); vector ind2 (locallen, -1); vector val (locallen, 0.0); vector disp(nprocs); vector recvcnt(nprocs); for(int i=0; i recv1 ; vector recv2 ; vector recv3 ; if(myrank == 0) { recv1.resize(totalen); recv2.resize(totalen); recv3.resize(totalen); } double t1 = MPI_Wtime(); MPI_Gatherv(ind1.data(), locallen, MPIType(), recv1.data(), recvcnt.data(), disp.data(), MPIType(), 0, MPI_COMM_WORLD); MPI_Gatherv(ind2.data(), locallen, MPIType(), recv2.data(), recvcnt.data(), disp.data(), MPIType(), 0, MPI_COMM_WORLD); MPI_Gatherv(val.data(), locallen, MPIType(), recv3.data(), recvcnt.data(), disp.data(), MPIType(), 0, MPI_COMM_WORLD); double t2 = MPI_Wtime() - t1; if(myrank == 0) { cout << "time : " << t2*2 << endl; } } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/BipartiteMatchings/auction.cpp000644 000765 000024 00000034612 13271404146 023363 0ustar00aydinbulucstaff000000 000000 #include "../CombBLAS.h" #include #include #include #include #include #include #include #include using namespace std; using namespace combblas; bool prune, mvInvertMate, randMM, moreSplit; int init; bool randMaximal; bool fewexp; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } struct VertexType { public: // careful: secondMaxProfit should be -\max wij by default // how can we pass that info to static VertexType id() so that SpMV can use it? VertexType(int64_t oi=-1, double p = 0, double smp=-9999999) { objID = oi; price = p; secondMaxProfit = smp; }; friend bool operator<(const VertexType & vtx1, const VertexType & vtx2 ){return vtx1.price < vtx2.price;}; friend ostream& operator<<(ostream& os, const VertexType & vertex ){os << "(" << vertex.objID << ", " << vertex.price << ", " << vertex.secondMaxProfit << ")"; return os;}; // variables for the object int64_t objID; // ultimately it will be the object with the max profit for a bidder double price; //ultimately it will be the max profit for a bidder double secondMaxProfit; //it will be the 2nd max profit for a bidder }; // return the maximum and second maximum profits struct max2 : public std::binary_function { const VertexType operator()(const VertexType& x, const VertexType& y) const { VertexType ret; if(x.price > y.price) { ret = x; if(x.secondMaxProfit < y.price) ret.secondMaxProfit = y.price; } else { ret = y; if(y.secondMaxProfit < x.price) ret.secondMaxProfit = x.price; } return ret; } }; struct SubMaxSR { static VertexType id(){ return VertexType(); }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPIOp::op(); }; // addition compute the maximum and second maximum profit static VertexType add(const VertexType & arg1, const VertexType & arg2) { return max2()(arg1, arg2); } // multiplication computes profit of an object to a bidder // profit_j = cij - price_j static VertexType multiply(const double & arg1, const VertexType & arg2) { // if the matrix is boolean, arg1=1 return VertexType(arg2.objID, arg1 - arg2.price, arg2.secondMaxProfit); } static void axpy(const double a, const VertexType & x, VertexType & y) { y = add(y, multiply(a, x)); } }; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_Bool; typedef SpParMat < int64_t, bool, SpDCCols > PSpMat_s32p64; typedef SpParMat < int64_t, int64_t, SpDCCols > PSpMat_Int64; typedef SpParMat < int64_t, float, SpDCCols > PSpMat_float; void maximumMatching(PSpMat_s32p64 & Aeff, FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row); void auction(PSpMat_s32p64 & A, FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row); template bool isMaximalmatching(PSpMat_Int64 & A, FullyDistVec & mateRow2Col, FullyDistVec & mateCol2Row, FullyDistSpVec unmatchedRow, FullyDistSpVec unmatchedCol); /* Remove isolated vertices and purmute */ void removeIsolated(PSpMat_Bool & A) { int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(A.getcommgrid()); FullyDistVec nonisoRowV; // id's of non-isolated (connected) Row vertices FullyDistVec nonisoColV; // id's of non-isolated (connected) Col vertices FullyDistVec nonisov; // id's of non-isolated (connected) vertices A.Reduce(*ColSums, Column, plus(), static_cast(0)); A.Reduce(*RowSums, Row, plus(), static_cast(0)); // this steps for general graph /* ColSums->EWiseApply(*RowSums, plus()); not needed for bipartite graph nonisov = ColSums->FindInds(bind2nd(greater(), 0)); nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A.operator()(nonisov, nonisov, true); // in-place permute to save memory */ // this steps for bipartite graph nonisoColV = ColSums->FindInds(bind2nd(greater(), 0)); nonisoRowV = RowSums->FindInds(bind2nd(greater(), 0)); delete ColSums; delete RowSums; { nonisoColV.RandPerm(); nonisoRowV.RandPerm(); } int64_t nrows1=A.getnrow(), ncols1=A.getncol(), nnz1 = A.getnnz(); double avgDeg1 = (double) nnz1/(nrows1+ncols1); A.operator()(nonisoRowV, nonisoColV, true); int64_t nrows2=A.getnrow(), ncols2=A.getncol(), nnz2 = A.getnnz(); double avgDeg2 = (double) nnz2/(nrows2+ncols2); if(myrank == 0) { cout << "ncol nrows nedges deg \n"; cout << nrows1 << " " << ncols1 << " " << nnz1 << " " << avgDeg1 << " \n"; cout << nrows2 << " " << ncols2 << " " << nnz2 << " " << avgDeg2 << " \n"; } MPI_Barrier(MPI_COMM_WORLD); } int main(int argc, char* argv[]) { // ------------ initialize MPI --------------- int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { //ShowUsage(); MPI_Finalize(); return -1; } // ------------ Process input arguments and build matrix --------------- { PSpMat_Bool * ABool; PSpMat_s32p64 ALocalT; ostringstream tinfo; double t01, t02; if(string(argv[1]) == string("input")) // input option { ABool = new PSpMat_Bool(); string filename(argv[2]); tinfo.str(""); tinfo << "**** Reading input matrix: " << filename << " ******* " << endl; SpParHelper::Print(tinfo.str()); t01 = MPI_Wtime(); ABool->ParallelReadMM(filename, false, maximum()); t02 = MPI_Wtime(); ABool->PrintInfo(); tinfo.str(""); tinfo << "Reader took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); //GetOptions(argv+3, argc-3); } else if(argc < 4) { //ShowUsage(); MPI_Finalize(); return -1; } else { unsigned scale = (unsigned) atoi(argv[2]); unsigned EDGEFACTOR = (unsigned) atoi(argv[3]); double initiator[4]; if(string(argv[1]) == string("er")) { initiator[0] = .25; initiator[1] = .25; initiator[2] = .25; initiator[3] = .25; cout << "ER ******** \n"; } else if(string(argv[1]) == string("g500")) { initiator[0] = .57; initiator[1] = .19; initiator[2] = .19; initiator[3] = .05; cout << "g500 ******** \n"; } else if(string(argv[1]) == string("ssca")) { initiator[0] = .6; initiator[1] = .4/3; initiator[2] = .4/3; initiator[3] = .4/3; cout << "ER ******** \n"; } else { if(myrank == 0) printf("The input type - %s - is not recognized.\n", argv[2]); MPI_Abort(MPI_COMM_WORLD, 1); } SpParHelper::Print("Generating input matrix....\n"); t01 = MPI_Wtime(); DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true); ABool = new PSpMat_Bool(*DEL, false); delete DEL; t02 = MPI_Wtime(); ABool->PrintInfo(); tinfo.str(""); tinfo << "Generator took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); Symmetricize(*ABool); //removeIsolated(*ABool); SpParHelper::Print("Generated matrix symmetricized....\n"); ABool->PrintInfo(); //GetOptions(argv+4, argc-4); } // randomly permute for load balance SpParHelper::Print("Performing random permutation of matrix.\n"); FullyDistVec prow(ABool->getcommgrid()); FullyDistVec pcol(ABool->getcommgrid()); prow.iota(ABool->getnrow(), 0); pcol.iota(ABool->getncol(), 0); prow.RandPerm(); pcol.RandPerm(); (*ABool)(prow, pcol, true); SpParHelper::Print("Performed random permutation of matrix.\n"); PSpMat_s32p64 A = *ABool; FullyDistVec mateRow2Col ( A.getcommgrid(), A.getnrow(), (int64_t) -1); FullyDistVec mateCol2Row ( A.getcommgrid(), A.getncol(), (int64_t) -1); auction(A, mateRow2Col, mateCol2Row); } MPI_Finalize(); return 0; } void auction(PSpMat_s32p64 & A, FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row) { int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); int64_t nrow = A.getnrow(); int64_t ncol = A.getncol(); FullyDistSpVec fringeRow(A.getcommgrid(), nrow); FullyDistSpVec umFringeRow(A.getcommgrid(), nrow); FullyDistVec leaves ( A.getcommgrid(), ncol, (int64_t) -1); vector > timing; vector layers; vector phaseMatched; double t1, time_search, time_augment, time_phase; bool matched = true; int phase = 0; int totalLayer = 0; int64_t numUnmatchedCol; FullyDistVec objects ( A.getcommgrid(), ncol, VertexType()); // set index to value objects.ApplyInd([](VertexType vtx, int64_t idx){return VertexType(idx, vtx.price, vtx.secondMaxProfit);}); FullyDistVec bidders ( A.getcommgrid(), nrow, VertexType()); // future: I need an SPMV that could return a dense vector of type other than the input bidders = SpMV(A, objects); // Remove bidders without any profitable objects FullyDistSpVec activeBidders ( bidders, [](VertexType bidder){return bidder.price>0;}); // remove matched bidders // we have done unncessary computation for already matched bidders // option1: bottom up style // option2: masked SpMV activeBidders = EWiseApply(activeBidders, mateRow2Col, [](VertexType vtx, int64_t mate){return vtx;}, [](VertexType vtx, int64_t mate){return mate==-1;}, false, VertexType()); // compute bid activeBidders.Apply([](VertexType bidder){return VertexType(bidder.objID, bidder.price - bidder.secondMaxProfit);}); // I don't care secondMaxProfit anymore // place bid // objects need to select the best bidder activeBidders.DebugPrint(); FullyDistSpVec bidObject = activeBidders.Invert(ncol, [](VertexType bidder, int64_t idx){return bidder.objID;}, [](VertexType bidder, int64_t idx){return VertexType(idx, bidder.price);}, [](VertexType bid1, VertexType bid2){return bid1.price>bid2.price? bid1: bid2;}); // I don't care secondMaxProfit anymore bidObject.DebugPrint(); // just creating a simplified object with the highest bidder // mateCol2Row is used just as a mask FullyDistSpVec successfullBids = EWiseApply(bidObject, mateCol2Row, [](VertexType vtx, int64_t mate){return vtx.objID;}, [](VertexType vtx, int64_t mate){return true;}, false, VertexType()); //mateCol2Row.DebugPrint(); // bidders previously matched to current successfull bids will become unmatched FullyDistSpVec revokedBids = EWiseApply(successfullBids, mateCol2Row, [](int64_t newbidder, int64_t mate){return mate;}, [](int64_t newbidder, int64_t mate){return mate!=-1;}, false, (int64_t)-1); mateCol2Row.Set(successfullBids); //cout << " djkfhksjdfh \n"; //successfullBids.DebugPrint(); //revokedBids.DebugPrint(); // previously unmatched bidders that will be matched FullyDistSpVec successfullBidders = successfullBids.Invert(nrow); // previously matched bidders that will be unmatched FullyDistSpVec revokedBidders = revokedBids.Invert(nrow); // they are mutually exclusive successfullBidders.DebugPrint(); revokedBidders.DebugPrint(); mateRow2Col.Set(successfullBidders); revokedBidders.Apply([](int64_t prevmate){return (int64_t)-1;}); //mateRow2Col.Set(revokedBidders); //objects.DebugPrint(); } CombBLAS_beta_16_2/BipartiteMatchings/MatchingDefs.h000644 000765 000024 00000010345 13212627400 023712 0ustar00aydinbulucstaff000000 000000 // // MatchingDefs.h // // // Created by Ariful Azad on 8/22/17. // // #ifndef MatchingDefs_h #define MatchingDefs_h #include "../CombBLAS.h" #include namespace combblas { // Vertex data structure for maximal cardinality matching template struct VertexTypeML { public: VertexTypeML(T1 p=-1, T2 com=0){parent=p; comp = com; }; friend bool operator<(const VertexTypeML & vtx1, const VertexTypeML & vtx2 ) { if(vtx1.comp==vtx2.comp) return vtx1.parent struct VertexTypeMM { public: VertexTypeMM(IT p=-1, IT r=-1, double w=0){parent=p; root = r; comp = w;}; friend bool operator<(const VertexTypeMM & vtx1, const VertexTypeMM & vtx2 ) { if(vtx1.comp==vtx2.comp) { if(vtx1.parent==vtx2.parent) return vtx1.root struct SelectPlusSR { static T2 id(){ return 1; }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_SUM; }; static T2 add(const T2 & arg1, const T2 & arg2) { return std::plus()(arg1, arg2); } static T2 multiply(const T1 & arg1, const T2 & arg2) { return static_cast (1); // note: it is not called on a Boolean matrix } static void axpy(const T1 a, const T2 & x, T2 & y) { y = add(y, multiply(a, x)); } }; // Usual semiring used in maximal and maximum matching template struct Select2ndMinSR { static T2 id(){ return T2(); }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MIN; }; static T2 add(const T2 & arg1, const T2 & arg2) { return std::min(arg1, arg2); } static T2 multiply(const T1 & arg1, const T2 & arg2) { return arg2; } static void axpy(const T1 a, const T2 & x, T2 & y) { y = add(y, multiply(a, x)); } }; // Designed to pseudo maximize weights on a maximal matching template struct WeightMaxMLSR { static T2 id(){ return T2(-1, std::numeric_limits::lowest()); }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MAX; }; static T2 add(const T2 & arg1, const T2 & arg2) { return std::max(arg1, arg2); } static T2 multiply(const T1 & arg1, const T2 & arg2) { return T2(arg2.parent, arg1); } static void axpy(const T1 a, const T2 & x, T2 & y) { y = add(y, multiply(a, x)); } }; // Designed to pseudo maximize weights on the augmenting paths // for boolean matrix (T1 <=> bool), this semiring converts to Select2ndMax semiring template struct WeightMaxMMSR { static T2 id(){ return T2(-1, -1, std::numeric_limits::lowest()); }; static bool returnedSAID() { return false; } static MPI_Op mpi_op() { return MPI_MAX; }; static T2 add(const T2 & arg1, const T2 & arg2) { return std::max(arg1, arg2); } static T2 multiply(const T1 & arg1, const T2 & arg2) { return T2(arg2.parent, arg2.root, arg1); } static void axpy(const T1 a, const T2 & x, T2 & y) { y = add(y, multiply(a, x)); } }; } #endif /* MatchingDefs_h */ CombBLAS_beta_16_2/BipartiteMatchings/makefile-mac000644 000765 000024 00000006473 13271404146 023457 0ustar00aydinbulucstaff000000 000000 CXX := mpicxx CXXFLAGS := -std=c++11 -std=gnu++14 -O3 -fopenmp -fpermissive -DTHREADED -DGNU_PARALLEL #-DNDEBUG #-DTIMING COMBBLAS = .. $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator %.o : %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/Tommy/%.o : $(COMBBLAS)/Tommy/%.c $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/usort/%.o : $(COMBBLAS)/usort/src/%.cpp $(CXX) -I$(COMBBLAS)/usort/include $(CXXFLAGS) -o $@ -c $< TOMMYS = $(COMBBLAS)/Tommy/tommyhashdyn.o $(COMBBLAS)/Tommy/tommyhash.o $(COMBBLAS)/Tommy/tommylist.o USORT = $(COMBBLAS)/usort/binUtils.o $(COMBBLAS)/usort/parUtils.o ApproxWeightPerfectMatching.o: ApproxWeightPerfectMatching.cpp ApproxWeightPerfectMatching.h Utility.h BPMaximumMatching.h BPMaximalMatching.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< awpm: ApproxWeightPerfectMatching.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq BPMaximumMatching.o: BPMaximumMatching.cpp BPMaximumMatching.h BPMaximalMatching.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< bpmm: BPMaximumMatching.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq BPMaximalMatching.o: BPMaximalMatching.cpp BPMaximalMatching.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< bpml: BPMaximalMatching.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq auction.o: auction.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< auction: auction.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq clean: rm -rf ../*.o rm -f bpmm bpml awpm auction rm -f *.o rm -f ../graph500-1.2/generator/*.o rm -f ../graph500-1.2/generator/libgraph_generator_seq.a CombBLAS_beta_16_2/BipartiteMatchings/BPMaximumMatching.h000644 000765 000024 00000044533 13212627400 024676 0ustar00aydinbulucstaff000000 000000 #ifndef BP_MAXIMUM_MATCHING_H #define BP_MAXIMUM_MATCHING_H #include "../CombBLAS.h" #include #include #include #include #include #include #include #include #include "MatchingDefs.h" double tTotalMaximum; namespace combblas { /** * Create a boolean matrix A (not necessarily a permutation matrix) * Input: ri: a dense vector (actual values in FullyDistVec should be IT) * ncol: number of columns in the output matrix A * Output: a boolean matrix A with m=size(ri) and n=ncol (input) and A[k,ri[k]]=1 * This can be done by Matlab like constructor, no? */ template SpParMat PermMat (const FullyDistVec & ri, const IT ncol) { IT procsPerRow = ri.commGrid->GetGridCols(); // the number of processor in a row of processor grid IT procsPerCol = ri.commGrid->GetGridRows(); // the number of processor in a column of processor grid IT global_nrow = ri.TotalLength(); IT global_ncol = ncol; IT m_perprocrow = global_nrow / procsPerRow; IT n_perproccol = global_ncol / procsPerCol; // The indices for FullyDistVec are offset'd to 1/p pieces // The matrix indices are offset'd to 1/sqrt(p) pieces // Add the corresponding offset before sending the data std::vector< std::vector > rowid(procsPerRow); // rowid in the local matrix of each vector entry std::vector< std::vector > colid(procsPerRow); // colid in the local matrix of each vector entry IT locvec = ri.arr.size(); // nnz in local vector IT roffset = ri.RowLenUntil(); // the number of vector elements in this processor row before the current processor for(typename std::vector::size_type i=0; i< (unsigned)locvec; ++i) { if(ri.arr[i]>=0 && ri.arr[i]GetRowWorld()); // share the counts int * sdispls = new int[procsPerRow](); int * rdispls = new int[procsPerRow](); partial_sum(sendcnt, sendcnt+procsPerRow-1, sdispls+1); partial_sum(recvcnt, recvcnt+procsPerRow-1, rdispls+1); IT p_nnz = accumulate(recvcnt,recvcnt+procsPerRow, static_cast(0)); IT * p_rows = new IT[p_nnz]; IT * p_cols = new IT[p_nnz]; IT * senddata = new IT[locvec]; for(int i=0; i().swap(rowid[i]); // clear memory of rowid } MPI_Alltoallv(senddata, sendcnt, sdispls, MPIType(), p_rows, recvcnt, rdispls, MPIType(), ri.commGrid->GetRowWorld()); for(int i=0; i().swap(colid[i]); // clear memory of colid } MPI_Alltoallv(senddata, sendcnt, sdispls, MPIType(), p_cols, recvcnt, rdispls, MPIType(), ri.commGrid->GetRowWorld()); delete [] senddata; std::tuple * p_tuples = new std::tuple[p_nnz]; for(IT i=0; i< p_nnz; ++i) { p_tuples[i] = make_tuple(p_rows[i], p_cols[i], 1); } DeleteAll(p_rows, p_cols); // Now create the local matrix IT local_nrow = ri.MyRowLength(); int my_proccol = ri.commGrid->GetRankInProcRow(); IT local_ncol = (my_proccol<(procsPerCol-1))? (n_perproccol) : (global_ncol - (n_perproccol*(procsPerCol-1))); // infer the concrete type SpMat typedef typename create_trait::T_inferred DER_IT; DER_IT * PSeq = new DER_IT(); PSeq->Create( p_nnz, local_nrow, local_ncol, p_tuples); // deletion of tuples[] is handled by SpMat::Create SpParMat P (PSeq, ri.commGrid); //Par_DCSC_Bool P (PSeq, ri.commGrid); return P; } /*************************************************************************** // Augment a matching by a set of vertex-disjoint augmenting paths. // The paths are explored level-by-level similar to the level-synchronous BFS // This approach is more effecient when we have many short augmenting paths ***************************************************************************/ template void AugmentLevel(FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row, FullyDistVec& parentsRow, FullyDistVec& leaves) { IT nrow = mateRow2Col.TotalLength(); IT ncol = mateCol2Row.TotalLength(); FullyDistSpVec col(leaves, [](IT leaf){return leaf!=-1;}); FullyDistSpVec row(mateRow2Col.getcommgrid(), nrow); FullyDistSpVec nextcol(col.getcommgrid(), ncol); while(col.getnnz()!=0) { row = col.Invert(nrow); row = EWiseApply(row, parentsRow, [](IT root, IT parent){return parent;}, [](IT root, IT parent){return true;}, false, (IT)-1); col = row.Invert(ncol); // children array nextcol = EWiseApply(col, mateCol2Row, [](IT child, IT mate){return mate;}, [](IT child, IT mate){return mate!=-1;}, false, (IT)-1); mateRow2Col.Set(row); mateCol2Row.Set(col); col = nextcol; } } /*************************************************************************** // Augment a matching by a set of vertex-disjoint augmenting paths. // An MPI processor is responsible for a complete path. // This approach is more effecient when we have few long augmenting paths // We used one-sided MPI. Any PGAS language should be fine as well. // This function is not thread safe, hence multithreading is not used here ***************************************************************************/ template void AugmentPath(FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row,FullyDistVec& parentsRow, FullyDistVec& leaves) { MPI_Win win_mateRow2Col, win_mateCol2Row, win_parentsRow; MPI_Win_create((IT*)mateRow2Col.GetLocArr(), mateRow2Col.LocArrSize() * sizeof(IT), sizeof(IT), MPI_INFO_NULL, mateRow2Col.commGrid->GetWorld(), &win_mateRow2Col); MPI_Win_create((IT*)mateCol2Row.GetLocArr(), mateCol2Row.LocArrSize() * sizeof(IT), sizeof(IT), MPI_INFO_NULL, mateCol2Row.commGrid->GetWorld(), &win_mateCol2Row); MPI_Win_create((IT*)parentsRow.GetLocArr(), parentsRow.LocArrSize() * sizeof(IT), sizeof(IT), MPI_INFO_NULL, parentsRow.commGrid->GetWorld(), &win_parentsRow); IT* leaves_ptr = (IT*) leaves.GetLocArr(); //MPI_Win_fence(0, win_mateRow2Col); //MPI_Win_fence(0, win_mateCol2Row); //MPI_Win_fence(0, win_parentsRow); IT row, col=100, nextrow; int owner_row, owner_col; IT locind_row, locind_col; int myrank; MPI_Comm_rank(MPI_COMM_WORLD, &myrank); for(IT i=0; i(), owner_row, locind_row, 1, MPIType(), win_parentsRow); MPI_Win_unlock(owner_row, win_parentsRow); owner_col = mateCol2Row.Owner(col, locind_col); MPI_Win_lock(MPI_LOCK_SHARED, owner_col, 0, win_mateCol2Row); MPI_Fetch_and_op(&row, &nextrow, MPIType(), owner_col, locind_col, MPI_REPLACE, win_mateCol2Row); MPI_Win_unlock(owner_col, win_mateCol2Row); MPI_Win_lock(MPI_LOCK_SHARED, owner_row, 0, win_mateRow2Col); MPI_Put(&col, 1, MPIType(), owner_row, locind_row, 1, MPIType(), win_mateRow2Col); MPI_Win_unlock(owner_row, win_mateRow2Col); // we need this otherwise col might get overwritten before communication! row = nextrow; } } //MPI_Win_fence(0, win_mateRow2Col); //MPI_Win_fence(0, win_mateCol2Row); //MPI_Win_fence(0, win_parentsRow); MPI_Win_free(&win_mateRow2Col); MPI_Win_free(&win_mateCol2Row); MPI_Win_free(&win_parentsRow); } // Maximum cardinality matching // Output: mateRow2Col and mateRow2Col template void maximumMatching(SpParMat < IT, NT, DER > & A, FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row, bool prune=true, bool randMM = false, bool maximizeWeight = false) { typedef VertexTypeMM VertexType; int nthreads=1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif PreAllocatedSPA SPA(A.seq(), nthreads*4); double tstart = MPI_Wtime(); int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); IT nrow = A.getnrow(); IT ncol = A.getncol(); FullyDistSpVec fringeRow(A.getcommgrid(), nrow); FullyDistSpVec umFringeRow(A.getcommgrid(), nrow); FullyDistVec leaves ( A.getcommgrid(), ncol, (IT) -1); std::vector > timing; std::vector layers; std::vector phaseMatched; double t1, time_search, time_augment, time_phase; bool matched = true; int phase = 0; int totalLayer = 0; IT numUnmatchedCol; MPI_Win winLeaves; MPI_Win_create((IT*)leaves.GetLocArr(), leaves.LocArrSize() * sizeof(IT), sizeof(IT), MPI_INFO_NULL, A.getcommgrid()->GetWorld(), &winLeaves); while(matched) { time_phase = MPI_Wtime(); std::vector phase_timing(8,0); leaves.Apply ( [](IT val){return (IT) -1;}); FullyDistVec parentsRow ( A.getcommgrid(), nrow, (IT) -1); FullyDistSpVec fringeCol(A.getcommgrid(), ncol); fringeCol = EWiseApply(fringeCol, mateCol2Row, [](VertexType vtx, IT mate){return vtx;}, [](VertexType vtx, IT mate){return mate==-1;}, true, VertexType()); if(randMM) //select rand { fringeCol.ApplyInd([](VertexType vtx, IT idx){return VertexType(idx,idx,GlobalMT.rand());}); } else { fringeCol.ApplyInd([](VertexType vtx, IT idx){return VertexType(idx,idx);}); } ++phase; numUnmatchedCol = fringeCol.getnnz(); int layer = 0; time_search = MPI_Wtime(); while(fringeCol.getnnz() > 0) { layer++; t1 = MPI_Wtime(); //TODO: think about this semiring if(maximizeWeight) SpMV>(A, fringeCol, fringeRow, false, SPA); else SpMV>(A, fringeCol, fringeRow, false, SPA); phase_timing[0] += MPI_Wtime()-t1; // remove vertices already having parents t1 = MPI_Wtime(); fringeRow = EWiseApply(fringeRow, parentsRow, [](VertexType vtx, IT parent){return vtx;}, [](VertexType vtx, IT parent){return parent==-1;}, false, VertexType()); // Set parent pointer parentsRow.EWiseApply(fringeRow, [](IT dval, VertexType svtx){return svtx.parent;}, [](IT dval, VertexType svtx){return true;}, false, VertexType()); umFringeRow = EWiseApply(fringeRow, mateRow2Col, [](VertexType vtx, IT mate){return vtx.root;}, [](VertexType vtx, IT mate){return mate==-1;}, false, VertexType()); phase_timing[1] += MPI_Wtime()-t1; IT nnz_umFringeRow = umFringeRow.getnnz(); // careful about this timing t1 = MPI_Wtime(); if(nnz_umFringeRow >0) { /* if(nnz_umFringeRow < 25*nprocs) { leaves.GSet(umFringeRow, [](IT valRoot, IT idxLeaf){return valRoot;}, [](IT valRoot, IT idxLeaf){return idxLeaf;}, winLeaves); // There might be a bug here. It does not return the same output for different number of processes // e.g., check with g7jac200sc.mtx matrix } else*/ { FullyDistSpVec temp1(A.getcommgrid(), ncol); temp1 = umFringeRow.Invert(ncol); leaves.Set(temp1); } } phase_timing[2] += MPI_Wtime()-t1; // matched row vertices in the the fringe fringeRow = EWiseApply(fringeRow, mateRow2Col, [](VertexType vtx, IT mate){return VertexType(mate, vtx.root);}, [](VertexType vtx, IT mate){return mate!=-1;}, false, VertexType()); t1 = MPI_Wtime(); if(nnz_umFringeRow>0 && prune) { fringeRow.FilterByVal (umFringeRow,[](VertexType vtx){return vtx.root;}, false); } double tprune = MPI_Wtime()-t1; phase_timing[3] += tprune; // Go to matched column from matched row in the fringe. parent is automatically set to itself. t1 = MPI_Wtime(); fringeCol = fringeRow.Invert(ncol, [](VertexType& vtx, const IT & index){return vtx.parent;}, [](VertexType& vtx, const IT & index){return vtx;}, [](VertexType& vtx1, VertexType& vtx2){return vtx1;}); phase_timing[4] += MPI_Wtime()-t1; } time_search = MPI_Wtime() - time_search; phase_timing[5] += time_search; IT numMatchedCol = leaves.Count([](IT leaf){return leaf!=-1;}); phaseMatched.push_back(numMatchedCol); time_augment = MPI_Wtime(); if (numMatchedCol== 0) matched = false; else { if(numMatchedCol < (2* nprocs * nprocs)) AugmentPath(mateRow2Col, mateCol2Row,parentsRow, leaves); else AugmentLevel(mateRow2Col, mateCol2Row,parentsRow, leaves); } time_augment = MPI_Wtime() - time_augment; phase_timing[6] += time_augment; time_phase = MPI_Wtime() - time_phase; phase_timing[7] += time_phase; timing.push_back(phase_timing); totalLayer += layer; layers.push_back(layer); } MPI_Win_free(&winLeaves); tTotalMaximum = MPI_Wtime() - tstart; //isMaximalmatching(A, mateRow2Col, mateCol2Row, unmatchedRow, unmatchedCol); //isMatching(mateCol2Row, mateRow2Col); //todo there is a better way to check this // print statistics double combTime; if(myrank == 0) { std::cout << "****** maximum matching runtime ********\n"; std::cout << std::endl; std::cout << "========================================================================\n"; std::cout << " BFS Search \n"; std::cout << "===================== ==================================================\n"; std::cout << "Phase Layer Match SpMV EWOpp CmUqL Prun CmMC BFS Aug Total\n"; std::cout << "===================== ===================================================\n"; std::vector totalTimes(timing[0].size(),0); int nphases = timing.size(); for(int i=0; i void removeIsolated(PARMAT & A) { int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(A.getcommgrid()); FullyDistVec nonisoRowV; // id's of non-isolated (connected) Row vertices FullyDistVec nonisoColV; // id's of non-isolated (connected) Col vertices FullyDistVec nonisov; // id's of non-isolated (connected) vertices A.Reduce(*ColSums, Column, std::plus(), static_cast(0)); A.Reduce(*RowSums, Row, std::plus(), static_cast(0)); // this steps for general graph /* ColSums->EWiseApply(*RowSums, plus()); not needed for bipartite graph nonisov = ColSums->FindInds(bind2nd(greater(), 0)); nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A.operator()(nonisov, nonisov, true); // in-place permute to save memory */ // this steps for bipartite graph nonisoColV = ColSums->FindInds(bind2nd(std::greater(), 0)); nonisoRowV = RowSums->FindInds(bind2nd(std::greater(), 0)); delete ColSums; delete RowSums; { nonisoColV.RandPerm(); nonisoRowV.RandPerm(); } int64_t nrows1=A.getnrow(), ncols1=A.getncol(), nnz1 = A.getnnz(); double avgDeg1 = (double) nnz1/(nrows1+ncols1); A.operator()(nonisoRowV, nonisoColV, true); int64_t nrows2=A.getnrow(), ncols2=A.getncol(), nnz2 = A.getnnz(); double avgDeg2 = (double) nnz2/(nrows2+ncols2); if(myrank == 0) { std::cout << "ncol nrows nedges deg \n"; std::cout << nrows1 << " " << ncols1 << " " << nnz1 << " " << avgDeg1 << " \n"; std::cout << nrows2 << " " << ncols2 << " " << nnz2 << " " << avgDeg2 << " \n"; } MPI_Barrier(MPI_COMM_WORLD); } /* * Serial: Check the validity of the matching solution; we need a better solution using invert */ template bool isMatching(FullyDistVec & mateCol2Row, FullyDistVec & mateRow2Col) { int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); for(int i=0; i< mateRow2Col.glen ; i++) { int t = mateRow2Col[i]; if(t!=-1 && mateCol2Row[t]!=i) { if(myrank == 0) std::cout << "Does not satisfy the matching constraints\n"; return false; } } for(int i=0; i< mateCol2Row.glen ; i++) { int t = mateCol2Row[i]; if(t!=-1 && mateRow2Col[t]!=i) { if(myrank == 0) std::cout << "Does not satisfy the matching constraints\n"; return false; } } return true; } template bool CheckMatching(FullyDistVec & mateRow2Col, FullyDistVec & mateCol2Row) { int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); int64_t nrow = mateRow2Col.TotalLength(); int64_t ncol = mateCol2Row.TotalLength(); FullyDistSpVec mateRow2ColSparse (mateRow2Col, [](IT mate){return mate!=-1;}); FullyDistSpVec mateCol2RowSparse (mateCol2Row, [](IT mate){return mate!=-1;}); FullyDistSpVec mateRow2ColInverted = mateRow2ColSparse.Invert(ncol); FullyDistSpVec mateCol2RowInverted = mateCol2RowSparse.Invert(nrow); bool isMatching = false; if((mateCol2RowSparse == mateRow2ColInverted) && (mateRow2ColSparse == mateCol2RowInverted)) isMatching = true; bool isPerfectMatching = false; if((mateRow2ColSparse.getnnz()==nrow) && (mateCol2RowSparse.getnnz() == ncol)) isPerfectMatching = true; if(myrank == 0) { std::cout << "-------------------------------" << std::endl; if(isMatching) { std::cout << "| This is a matching |" << std::endl; if(isPerfectMatching) std::cout << "| This is a perfect matching |" << std::endl; } else std::cout << "| This is not a matching |" << std::endl; std::cout << "-------------------------------" << std::endl; } return isPerfectMatching; } // Gievn a matrix and matching vectors, returns the weight of the matching template NT MatchingWeight( SpParMat < IT, NT, DER > & A, FullyDistVec mateRow2Col, FullyDistVec& mateCol2Row) { auto commGrid = A.getcommgrid(); int myrank=commGrid->GetRank(); MPI_Comm World = commGrid->GetWorld(); MPI_Comm ColWorld = commGrid->GetColWorld(); MPI_Comm RowWorld = commGrid->GetRowWorld(); int nprocs = commGrid->GetSize(); int pr = commGrid->GetGridRows(); int pc = commGrid->GetGridCols(); int rowrank = commGrid->GetRankInProcRow(); int colrank = commGrid->GetRankInProcCol(); int diagneigh = commGrid->GetComplementRank(); //Information about the matrix distribution //Assume that A is an nrow x ncol matrix //The local submatrix is an lnrow x lncol matrix IT nrows = A.getnrow(); IT ncols = A.getncol(); IT m_perproc = nrows / pr; IT n_perproc = ncols / pc; DER* spSeq = A.seqptr(); // local submatrix Dcsc* dcsc = spSeq->GetDCSC(); IT lnrow = spSeq->getnrow(); IT lncol = spSeq->getncol(); IT localRowStart = colrank * m_perproc; // first row in this process IT localColStart = rowrank * n_perproc; // first col in this process // ----------------------------------------------------------- // replicate mate vectors for mateCol2Row // ----------------------------------------------------------- int xsize = (int) mateCol2Row.LocArrSize(); int trxsize = 0; MPI_Status status; MPI_Sendrecv(&xsize, 1, MPI_INT, diagneigh, TRX, &trxsize, 1, MPI_INT, diagneigh, TRX, World, &status); std::vector trxnums(trxsize); MPI_Sendrecv(mateCol2Row.GetLocArr(), xsize, MPIType(), diagneigh, TRX, trxnums.data(), trxsize, MPIType(), diagneigh, TRX, World, &status); std::vector colsize(pc); colsize[colrank] = trxsize; MPI_Allgather(MPI_IN_PLACE, 1, MPI_INT, colsize.data(), 1, MPI_INT, ColWorld); std::vector dpls(pc,0); // displacements (zero initialized pid) std::partial_sum(colsize.data(), colsize.data()+pc-1, dpls.data()+1); int accsize = std::accumulate(colsize.data(), colsize.data()+pc, 0); std::vector RepMateC2R(accsize); MPI_Allgatherv(trxnums.data(), trxsize, MPIType(), RepMateC2R.data(), colsize.data(), dpls.data(), MPIType(), ColWorld); // ----------------------------------------------------------- /* if(myrank==1) { for(int i=0; ibegcol(); colit != spSeq->endcol(); ++colit) // iterate over columns { IT lj = colit.colid(); // local numbering IT mj = RepMateC2R[lj]; // mate of j if(mj >= localRowStart && mj < (localRowStart+lnrow) ) { for(auto nzit = spSeq->begnz(colit); nzit < spSeq->endnz(colit); ++nzit) { IT li = nzit.rowid(); IT i = li + localRowStart; // TODO: use binary search to directly go to mj-th entry if more than 32 nonzero in this column if( i == mj) { w += nzit.value(); //cout << myrank<< ":: row: " << i << " column: "<< lj+localColStart << " weight: " << nzit.value() << endl; } } } } MPI_Barrier(World); MPI_Allreduce(MPI_IN_PLACE, &w, 1, MPIType(), MPI_SUM, World); //MPI_Allreduce(&w, &gw, 1, MPIType(), MPI_SUM, World); //MPI_Reduce(&w, &gw, 1, MPIType(), MPI_SUM, 0, World); //MPI_Allreduce(&w, &gw, 1, MPI_DOUBLE, MPI_SUM, World); //cout << myrank << ": " << gw << endl; return w; } template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } } #endif CombBLAS_beta_16_2/BipartiteMatchings/makefile-nersc000644 000765 000024 00000005123 13212627400 024013 0ustar00aydinbulucstaff000000 000000 CXX := CC CXXFLAGS := -std=c++11 -std=gnu++14 -O3 -fopenmp -fpermissive -DTHREADED -DGNU_PARALLEL #-DTIMING COMBBLAS = .. $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator %.o : %.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/Tommy/%.o : $(COMBBLAS)/Tommy/%.c $(CXX) $(CXXFLAGS) -o $@ -c $< $(COMBBLAS)/usort/%.o : $(COMBBLAS)/usort/src/%.cpp $(CXX) -I$(COMBBLAS)/usort/include $(CXXFLAGS) -o $@ -c $< TOMMYS = $(COMBBLAS)/Tommy/tommyhashdyn.o $(COMBBLAS)/Tommy/tommyhash.o $(COMBBLAS)/Tommy/tommylist.o USORT = $(COMBBLAS)/usort/binUtils.o $(COMBBLAS)/usort/parUtils.o BPMaximumMatching.o: BPMaximumMatching.cpp BPMaximumMatching.h BPMaximalMatching.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< bpmm: BPMaximumMatching.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq auction.o: auction.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< auction: auction.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq ApproxWeightPerfectMatching.o: ApproxWeightPerfectMatching.cpp ApproxWeightPerfectMatching.h BPMaximumMatching.h BPMaximalMatching.h ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParMat.h ../ParFriends.h ../SpParMat.cpp ../SpDefs.h ../SpTuples.cpp ../SpImpl.h ../SpCCols.h ../SpCCols.cpp ../csc.cpp ../SpImpl.cpp $(CXX) $(CXXFLAGS) -o $@ -c $< awpm: ApproxWeightPerfectMatching.o $(COMBBLAS)/MPIType.o $(COMBBLAS)/mmio.o $(COMBBLAS)/MPIOp.o $(COMBBLAS)/MemoryPool.o $(COMBBLAS)/CommGrid.o $(COMBBLAS)/hash.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMBBLAS)/CommGrid.o $(TOMMYS) $(USORT) $(CXX) $(CXXFLAGS) -o $@ $^ -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq clean: rm -rf ../*.o rm -f bpmm auction rm -f *.o CombBLAS_beta_16_2/BipartiteMatchings/BPMaximumMatching.cpp000644 000765 000024 00000044352 13271404146 025235 0ustar00aydinbulucstaff000000 000000 #ifdef THREADED #ifndef _OPENMP #define _OPENMP #endif #include int cblas_splits = 1; #endif #include "../CombBLAS.h" #include #include #include #include #include #include #include #include #include "BPMaximalMatching.h" #include "BPMaximumMatching.h" using namespace std; using namespace combblas; typedef SpParMat < int64_t, bool, SpDCCols > Par_DCSC_Bool; typedef SpParMat < int64_t, int64_t, SpDCCols > Par_DCSC_int64_t; typedef SpParMat < int64_t, double, SpDCCols > Par_DCSC_Double; typedef SpParMat < int64_t, bool, SpCCols > Par_CSC_Bool; // algorithmic options bool prune, randMM, moreSplit; int init; bool randMaximal; bool fewexp; bool randPerm; bool saveMatching; string ofname; /* Remove isolated vertices and purmute */ void removeIsolated(Par_DCSC_Bool & A) { int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); FullyDistVec * ColSums = new FullyDistVec(A.getcommgrid()); FullyDistVec * RowSums = new FullyDistVec(A.getcommgrid()); FullyDistVec nonisoRowV; // id's of non-isolated (connected) Row vertices FullyDistVec nonisoColV; // id's of non-isolated (connected) Col vertices FullyDistVec nonisov; // id's of non-isolated (connected) vertices A.Reduce(*ColSums, Column, plus(), static_cast(0)); A.Reduce(*RowSums, Row, plus(), static_cast(0)); // this steps for general graph /* ColSums->EWiseApply(*RowSums, plus()); not needed for bipartite graph nonisov = ColSums->FindInds(bind2nd(greater(), 0)); nonisov.RandPerm(); // so that A(v,v) is load-balanced (both memory and time wise) A.operator()(nonisov, nonisov, true); // in-place permute to save memory */ // this steps for bipartite graph nonisoColV = ColSums->FindInds(bind2nd(greater(), 0)); nonisoRowV = RowSums->FindInds(bind2nd(greater(), 0)); delete ColSums; delete RowSums; { nonisoColV.RandPerm(); nonisoRowV.RandPerm(); } int64_t nrows1=A.getnrow(), ncols1=A.getncol(), nnz1 = A.getnnz(); double avgDeg1 = (double) nnz1/(nrows1+ncols1); A.operator()(nonisoRowV, nonisoColV, true); int64_t nrows2=A.getnrow(), ncols2=A.getncol(), nnz2 = A.getnnz(); double avgDeg2 = (double) nnz2/(nrows2+ncols2); if(myrank == 0) { cout << "ncol nrows nedges deg \n"; cout << nrows1 << " " << ncols1 << " " << nnz1 << " " << avgDeg1 << " \n"; cout << nrows2 << " " << ncols2 << " " << nnz2 << " " << avgDeg2 << " \n"; } MPI_Barrier(MPI_COMM_WORLD); } void ShowUsage() { int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(myrank == 0) { cout << "\n-------------- usage --------------\n"; cout << "Usage (random matrix): ./bpmm \n"; cout << "Usage (input matrix): ./bpmm \n\n"; cout << " \n-------------- meaning of arguments ----------\n"; cout << "** er: Erdos-Renyi, g500: Graph500 benchmark, ssca: SSCA benchmark\n"; cout << "** scale: matrix dimention is 2^scale\n"; cout << "** edgefactor: average degree of vertices\n"; cout << "** (optional) init : maximal matching algorithm used to initialize\n "; cout << " none: noinit, greedy: greedy init , ks: Karp-Sipser, dmd: dynamic mindegree\n"; cout << " default: none\n"; cout << "** (optional) randMaximal: random parent selection in greedy/Karp-Sipser\n" ; //cout << "** (optional) diropt: employ direction-optimized BFS\n" ; cout << "** (optional) prune: discard trees as soon as an augmenting path is found\n" ; //cout << "** (optional) graft: employ tree grafting\n" ; cout << "** (optional) moreSplit: more splitting of Matrix.\n" ; cout << "** (optional) randPerm: Randomly permute the matrix for load balance.\n" ; cout << "** (optional) saveMatching: Save the matching vector in a file (filename: inputfile_matching.txt).\n" ; cout << "(order of optional arguments does not matter)\n"; cout << " \n-------------- examples ----------\n"; cout << "Example: mpirun -np 4 ./bpmm g500 18 16" << endl; cout << "Example: mpirun -np 4 ./bpmm g500 18 16 ks diropt graft" << endl; cout << "Example: mpirun -np 4 ./bpmm input cage12.mtx randPerm ks diropt graft\n" << endl; } } void GetOptions(char* argv[], int argc) { string allArg=""; for(int i=0; i degCol) { FullyDistVec mateRow2Col ( A.getcommgrid(), A.getnrow(), (int64_t) -1); FullyDistVec mateCol2Row ( A.getcommgrid(), A.getncol(), (int64_t) -1); // best option init = DMD; randMaximal = false; randMM = true; prune = true; showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); maximumMatching(A, mateRow2Col, mateCol2Row,prune, randMM); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + KS init = KARP_SIPSER; randMaximal = true; randMM = true; prune = true; showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + Greedy init = GREEDY; randMaximal = true; randMM = true; prune = true; showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + No init init = NO_INIT; randMaximal = false; randMM = true; prune = true; showCurOptions(); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option - randMM init = DMD; randMaximal = false; randMM = false; prune = true; showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option - prune init = DMD; randMaximal = false; randMM = true; prune = false; showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); } // default experiment void defaultExp(Par_DCSC_Bool & A, Par_DCSC_Bool & AT, FullyDistVec degCol) { FullyDistVec mateRow2Col ( A.getcommgrid(), A.getnrow(), (int64_t) -1); FullyDistVec mateCol2Row ( A.getcommgrid(), A.getncol(), (int64_t) -1); // best option init = DMD; randMaximal = false; randMM = true; prune = true; showCurOptions(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); if(saveMatching && ofname!="") { mateRow2Col.ParallelWrite(ofname,false,false); } mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); } void experiment_maximal(Par_DCSC_Bool & A, Par_DCSC_Bool & AT, FullyDistVec degCol) { int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); FullyDistVec mateRow2Col ( A.getcommgrid(), A.getnrow(), (int64_t) -1); FullyDistVec mateCol2Row ( A.getcommgrid(), A.getncol(), (int64_t) -1); double time_start; // best option init = DMD; randMaximal = false; randMM = true; prune = true; time_start=MPI_Wtime(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); double time_dmd = MPI_Wtime()-time_start; int64_t cardDMD = mateRow2Col.Count([](int64_t mate){return mate!=-1;}); time_start=MPI_Wtime(); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); double time_mm_dmd = MPI_Wtime()-time_start; int64_t mmcardDMD = mateRow2Col.Count([](int64_t mate){return mate!=-1;}); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + KS init = KARP_SIPSER; randMaximal = true; randMM = true; prune = true; time_start=MPI_Wtime(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); double time_ks = MPI_Wtime()-time_start; int64_t cardKS = mateRow2Col.Count([](int64_t mate){return mate!=-1;}); time_start=MPI_Wtime(); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); double time_mm_ks = MPI_Wtime()-time_start; int64_t mmcardKS = mateRow2Col.Count([](int64_t mate){return mate!=-1;}); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); // best option + Greedy init = GREEDY; randMaximal = true; randMM = true; prune = true; time_start=MPI_Wtime(); MaximalMatching(A, AT, mateRow2Col, mateCol2Row, degCol, init, randMaximal); double time_greedy = MPI_Wtime()-time_start; int64_t cardGreedy = mateRow2Col.Count([](int64_t mate){return mate!=-1;}); time_start=MPI_Wtime(); maximumMatching(A, mateRow2Col, mateCol2Row, prune, randMM); double time_mm_greedy = MPI_Wtime()-time_start; int64_t mmcardGreedy = mateRow2Col.Count([](int64_t mate){return mate!=-1;}); mateRow2Col.Apply([](int64_t val){return (int64_t) -1;}); mateCol2Row.Apply([](int64_t val){return (int64_t) -1;}); if(myrank == 0) { cout << "\n maximal matching experiment \n"; cout << cardGreedy << " " << mmcardGreedy << " " << time_greedy << " " << time_mm_greedy << " " << cardKS << " " << mmcardKS << " " << time_ks << " " << time_mm_ks << " " << cardDMD << " " << mmcardDMD << " " << time_dmd << " " << time_mm_dmd << " \n"; } } int main(int argc, char* argv[]) { // ------------ initialize MPI --------------- int provided; MPI_Init_thread(&argc, &argv, MPI_THREAD_SERIALIZED, &provided); if (provided < MPI_THREAD_SERIALIZED) { printf("ERROR: The MPI library does not have MPI_THREAD_SERIALIZED support\n"); MPI_Abort(MPI_COMM_WORLD, 1); } int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { ShowUsage(); MPI_Finalize(); return -1; } init = DMD; randMaximal = false; prune = false; randMM = true; moreSplit = false; fewexp=false; saveMatching = false; ofname = ""; randPerm = false; SpParHelper::Print("***** I/O and other preprocessing steps *****\n"); // ------------ Process input arguments and build matrix --------------- { Par_DCSC_Bool * ABool; ostringstream tinfo; double t01, t02; if(string(argv[1]) == string("input")) // input option { ABool = new Par_DCSC_Bool(); string filename(argv[2]); tinfo.str(""); tinfo << "\n**** Reading input matrix: " << filename << " ******* " << endl; SpParHelper::Print(tinfo.str()); t01 = MPI_Wtime(); ABool->ParallelReadMM(filename, true, maximum()); // one-based matrix market file t02 = MPI_Wtime(); ABool->PrintInfo(); tinfo.str(""); tinfo << "Reader took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); GetOptions(argv+3, argc-3); if(saveMatching) { ofname = filename + ".matching.out"; } } else if(argc < 4) { ShowUsage(); MPI_Finalize(); return -1; } else { unsigned scale = (unsigned) atoi(argv[2]); unsigned EDGEFACTOR = (unsigned) atoi(argv[3]); double initiator[4]; if(string(argv[1]) == string("er")) { initiator[0] = .25; initiator[1] = .25; initiator[2] = .25; initiator[3] = .25; if(myrank==0) cout << "Randomly generated ER matric\n"; } else if(string(argv[1]) == string("g500")) { initiator[0] = .57; initiator[1] = .19; initiator[2] = .19; initiator[3] = .05; if(myrank==0) cout << "Randomly generated G500 matric\n"; } else if(string(argv[1]) == string("ssca")) { initiator[0] = .6; initiator[1] = .4/3; initiator[2] = .4/3; initiator[3] = .4/3; if(myrank==0) cout << "Randomly generated SSCA matric\n"; } else { if(myrank == 0) printf("The input type - %s - is not recognized.\n", argv[2]); MPI_Abort(MPI_COMM_WORLD, 1); } SpParHelper::Print("Generating input matrix....\n"); t01 = MPI_Wtime(); DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true); ABool = new Par_DCSC_Bool(*DEL, false); delete DEL; t02 = MPI_Wtime(); ABool->PrintInfo(); tinfo.str(""); tinfo << "Generator took " << t02-t01 << " seconds" << endl; SpParHelper::Print(tinfo.str()); Symmetricize(*ABool); //removeIsolated(*ABool); SpParHelper::Print("Generated matrix symmetricized....\n"); ABool->PrintInfo(); GetOptions(argv+4, argc-4); } if(randPerm) { // randomly permute for load balance SpParHelper::Print("Performing random permutation of matrix.\n"); FullyDistVec prow(ABool->getcommgrid()); FullyDistVec pcol(ABool->getcommgrid()); prow.iota(ABool->getnrow(), 0); pcol.iota(ABool->getncol(), 0); prow.RandPerm(); pcol.RandPerm(); (*ABool)(prow, pcol, true); SpParHelper::Print("Performed random permutation of matrix.\n"); } Par_DCSC_Bool A = *ABool; Par_DCSC_Bool AT = A; AT.Transpose(); // Reduce is not multithreaded, so I am doing it here FullyDistVec degCol(A.getcommgrid()); A.Reduce(degCol, Column, plus(), static_cast(0)); int nthreads; #ifdef THREADED #pragma omp parallel { int splitPerThread = 1; if(moreSplit) splitPerThread = 4; nthreads = omp_get_num_threads(); cblas_splits = nthreads*splitPerThread; } tinfo.str(""); tinfo << "Threading activated with " << nthreads << " threads, and matrix split into "<< cblas_splits << " parts" << endl; SpParHelper::Print(tinfo.str()); A.ActivateThreading(cblas_splits); // note: crash on empty matrix AT.ActivateThreading(cblas_splits); #endif SpParHelper::Print("**************************************************\n\n"); defaultExp(A, AT, degCol); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/BipartiteMatchings/BPMaximalMatching.h000644 000765 000024 00000041037 13212627400 024645 0ustar00aydinbulucstaff000000 000000 #ifndef BP_MAXIMAL_MATCHING_H #define BP_MAXIMAL_MATCHING_H #include "../CombBLAS.h" #include #include #include #include #include #include "Utility.h" #include "MatchingDefs.h" #define NO_INIT 0 #define GREEDY 1 #define KARP_SIPSER 2 #define DMD 3 MTRand GlobalMT(123); // for reproducible result double tTotalMaximal; namespace combblas { // This is not tested with CSC yet // TODO: test with CSC and Setting SPA (similar to Weighted Greedy) template void MaximalMatching(Par_DCSC_Bool & A, Par_DCSC_Bool & AT, FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row, FullyDistVec& degColRecv, int type, bool rand=true) { typedef VertexTypeML < IT, IT> VertexType; int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); int nthreads = 1; #ifdef _OPENMP #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif FullyDistVec degCol = degColRecv; //unmatched row and column vertices FullyDistSpVec unmatchedRow(mateRow2Col, [](IT mate){return mate==-1;}); FullyDistSpVec degColSG(A.getcommgrid(), A.getncol()); //FullyDistVec degCol(A.getcommgrid()); //A.Reduce(degCol, Column, plus(), static_cast(0)); // Reduce is not multithreaded FullyDistSpVec unmatchedCol(A.getcommgrid(), A.getncol()); // every veretx is unmatched. keep non-isolated vertices unmatchedCol = EWiseApply(unmatchedCol, degCol, [](VertexType vtx, IT deg){return VertexType();}, [](VertexType vtx, IT deg){return deg>0;}, true, VertexType()); FullyDistSpVec fringeRow(A.getcommgrid(), A.getnrow()); FullyDistSpVec fringeRow2(A.getcommgrid(), A.getnrow()); FullyDistSpVec deg1Col(A.getcommgrid(), A.getncol()); IT curUnmatchedCol = unmatchedCol.getnnz(); IT curUnmatchedRow = unmatchedRow.getnnz(); IT newlyMatched = 1; // ensure the first pass of the while loop int iteration = 0; double tStart = MPI_Wtime(); std::vector > timing; #ifdef DETAIL_STATS if(myrank == 0) { cout << "=======================================================\n"; cout << "@@@@@@ Number of processes: " << nprocs << endl; cout << "=======================================================\n"; cout << "It | UMRow | UMCol | newlyMatched | Time "<< endl; cout << "=======================================================\n"; } #endif MPI_Barrier(MPI_COMM_WORLD); while(curUnmatchedCol !=0 && curUnmatchedRow!=0 && newlyMatched != 0 ) { unmatchedCol.ApplyInd([](VertexType vtx, IT idx){return VertexType(idx,idx);}); if(type==DMD) { unmatchedCol = EWiseApply(unmatchedCol, degCol, [](VertexType vtx, IT deg){return VertexType(vtx.parent,deg);}, [](VertexType vtx, IT deg){return true;}, false, VertexType()); } else if(rand) { unmatchedCol.Apply([](VertexType vtx){return VertexType(vtx.parent, static_cast((GlobalMT.rand() * 9999999)+1));}); } // ======================== step1: One step of BFS ========================= std::vector times; double t1 = MPI_Wtime(); if(type==GREEDY) { SpMV>(A, unmatchedCol, fringeRow, false); } else if(type==DMD) { SpMV>(A, unmatchedCol, fringeRow, false); } else //(type==KARP_SIPSER) { deg1Col = EWiseApply(unmatchedCol, degCol, [](VertexType vtx, IT deg){return vtx;}, [](VertexType vtx, IT deg){return deg==1;}, false, VertexType()); if(deg1Col.getnnz()>9) SpMV>(A, deg1Col, fringeRow, false); else SpMV>(A, unmatchedCol, fringeRow, false); } // Remove matched row vertices fringeRow = EWiseApply(fringeRow, mateRow2Col, [](VertexType vtx, IT mate){return vtx;}, [](VertexType vtx, IT mate){return mate==-1;}, false, VertexType()); if(myrank == 0){times.push_back(MPI_Wtime()-t1); t1 = MPI_Wtime();} // =========================================================================== // ======================== step2: Update matching ========================= fringeRow2 = EWiseApply(fringeRow, mateRow2Col, [](VertexType vtx, IT mate){return vtx.parent;}, [](VertexType vtx, IT mate){return true;}, false, VertexType()); FullyDistSpVec newMatchedCols = fringeRow2.Invert(A.getncol()); FullyDistSpVec newMatchedRows = newMatchedCols.Invert(A.getnrow()); mateCol2Row.Set(newMatchedCols); mateRow2Col.Set(newMatchedRows); if(myrank == 0){times.push_back(MPI_Wtime()-t1); t1 = MPI_Wtime();} // =========================================================================== // =============== step3: Update degree of unmatched columns ================= unmatchedRow.Select(mateRow2Col, [](IT mate){return mate==-1;}); unmatchedCol.Select(mateCol2Row, [](IT mate){return mate==-1;}); if(type!=GREEDY) { // update degree newMatchedRows.Apply([](IT val){return 1;}); // needed if the matrix is Boolean since the SR::multiply isn't called SpMV< SelectPlusSR>(AT, newMatchedRows, degColSG, false); // degree of column vertices to matched rows // subtract degree of column vertices degCol.EWiseApply(degColSG, [](IT old_deg, IT new_deg){return old_deg-new_deg;}, [](IT old_deg, IT new_deg){return true;}, false, static_cast(0)); // remove isolated vertices unmatchedCol = EWiseApply(unmatchedCol, degCol, [](VertexType vtx, IT deg){return vtx;}, [](VertexType vtx, IT deg){return deg>0;}, false, VertexType()); } if(myrank == 0){times.push_back(MPI_Wtime()-t1); t1 = MPI_Wtime();} // =========================================================================== ++iteration; newlyMatched = newMatchedCols.getnnz(); times.push_back(std::accumulate(times.begin(), times.end(), 0.0)); timing.push_back(times); #ifdef DETAIL_STATS if(myrank == 0) { printf("%3d %10lld %10lld %10lld %18lf\n", iteration , curUnmatchedRow, curUnmatchedCol, newlyMatched, times.back()); } #endif curUnmatchedCol = unmatchedCol.getnnz(); curUnmatchedRow = unmatchedRow.getnnz(); MPI_Barrier(MPI_COMM_WORLD); } IT cardinality = mateRow2Col.Count([](IT mate){return mate!=-1;}); std::vector totalTimes(timing[0].size(),0); for(int i=0; i void WeightedGreedy(Par_MAT_Double & A, FullyDistVec& mateRow2Col, FullyDistVec& mateCol2Row, FullyDistVec& degCol) { typedef VertexTypeML < IT, double> VertexType; int nthreads=1; #ifdef THREADED #pragma omp parallel { nthreads = omp_get_num_threads(); } #endif PreAllocatedSPA SPA(A.seq(), nthreads*4); int nprocs, myrank; MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); double tStart = MPI_Wtime(); //unmatched row and column vertices FullyDistSpVec unmatchedRow(mateRow2Col, [](IT mate){return mate==-1;}); FullyDistSpVec unmatchedCol(A.getcommgrid(), A.getncol()); // every veretx is unmatched. keep non-isolated vertices unmatchedCol = EWiseApply(unmatchedCol, degCol, [](VertexType vtx, IT deg){return VertexType();}, [](VertexType vtx, IT deg){return deg>0;}, true, VertexType()); FullyDistSpVec fringeRow(A.getcommgrid(), A.getnrow()); FullyDistSpVec fringeRow2(A.getcommgrid(), A.getnrow()); FullyDistSpVec fringeRow3(A.getcommgrid(), A.getnrow()); IT curUnmatchedCol = unmatchedCol.getnnz(); IT curUnmatchedRow = unmatchedRow.getnnz(); IT newlyMatched = 1; // ensure the first pass of the while loop int iteration = 0; std::vector > timing; #ifdef DETAIL_STATS if(myrank == 0) { cout << "=======================================================\n"; cout << "@@@@@@ Number of processes: " << nprocs << endl; cout << "=======================================================\n"; cout << "It | UMRow | UMCol | newlyMatched | Time "<< endl; cout << "=======================================================\n"; } #endif MPI_Barrier(MPI_COMM_WORLD); while(curUnmatchedCol !=0 && curUnmatchedRow!=0 && newlyMatched != 0 ) { // anything is fine in the second argument unmatchedCol.ApplyInd([](VertexType vtx, IT idx){return VertexType(idx,idx);}); // ======================== step1: One step of BFS ========================= std::vector times; double t1 = MPI_Wtime(); SpMV>(A, unmatchedCol, fringeRow, false, SPA); // Remove matched row vertices fringeRow = EWiseApply(fringeRow, mateRow2Col, [](VertexType vtx, IT mate){return vtx;}, [](VertexType vtx, IT mate){return mate==-1;}, false, VertexType()); if(myrank == 0){times.push_back(MPI_Wtime()-t1); t1 = MPI_Wtime();} // =========================================================================== // ======================== step2: Update matching ========================= fringeRow2 = EWiseApply(fringeRow, mateRow2Col, [](VertexType vtx, IT mate){return vtx.parent;}, [](VertexType vtx, IT mate){return true;}, false, VertexType()); FullyDistSpVec newMatchedCols = fringeRow2.Invert(A.getncol()); FullyDistSpVec newMatchedRows = newMatchedCols.Invert(A.getnrow()); mateCol2Row.Set(newMatchedCols); mateRow2Col.Set(newMatchedRows); if(myrank == 0){times.push_back(MPI_Wtime()-t1); t1 = MPI_Wtime();} // =========================================================================== // =============== step3: Update unmatched columns and rows ================= unmatchedRow.Select(mateRow2Col, [](IT mate){return mate==-1;}); unmatchedCol.Select(mateCol2Row, [](IT mate){return mate==-1;}); if(myrank == 0){times.push_back(MPI_Wtime()-t1); t1 = MPI_Wtime();} // =========================================================================== ++iteration; newlyMatched = newMatchedCols.getnnz(); times.push_back(std::accumulate(times.begin(), times.end(), 0.0)); timing.push_back(times); #ifdef DETAIL_STATS if(myrank == 0) { printf("%3d %10lld %10lld %10lld %18lf\n", iteration , curUnmatchedRow, curUnmatchedCol, newlyMatched, times.back()); } #endif curUnmatchedCol = unmatchedCol.getnnz(); curUnmatchedRow = unmatchedRow.getnnz(); MPI_Barrier(MPI_COMM_WORLD); } tTotalMaximal = MPI_Wtime() - tStart; IT cardinality = mateRow2Col.Count([](IT mate){return mate!=-1;}); std::vector totalTimes(timing[0].size(),0); for(int i=0; i bool isMaximalmatching(Par_DCSC_Bool & A, FullyDistVec & mateRow2Col, FullyDistVec & mateCol2Row) { typedef VertexTypeML < IT, IT> VertexType; int myrank; MPI_Comm_rank(MPI_COMM_WORLD,&myrank); FullyDistSpVec fringeRow(A.getcommgrid(), A.getnrow()); FullyDistSpVec fringeCol(A.getcommgrid(), A.getncol()); FullyDistSpVec unmatchedRow(mateRow2Col, [](IT mate){return mate==-1;}); FullyDistSpVec unmatchedCol(mateCol2Row, [](IT mate){return mate==-1;}); unmatchedRow.setNumToInd(); unmatchedCol.setNumToInd(); SpMV>(A, unmatchedCol, fringeRow, false); fringeRow = EWiseMult(fringeRow, mateRow2Col, true, (IT) -1); if(fringeRow.getnnz() != 0) { if(myrank == 0) std::cout << "Not maximal matching!!\n"; return false; } Par_DCSC_Bool tA = A; tA.Transpose(); SpMV>(tA, unmatchedRow, fringeCol, false); fringeCol = EWiseMult(fringeCol, mateCol2Row, true, (IT) -1); if(fringeCol.getnnz() != 0) { if(myrank == 0) std::cout << "Not maximal matching**!!\n"; return false; } return true; } } #endif CombBLAS_beta_16_2/ReleaseTests/IndexingTest.cpp000644 000765 000024 00000013211 13271404146 023140 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.5 -------------------------------------------------*/ /* date: 10/09/2015 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2015, The Regents of the University of California 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. */ #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; template pair< FullyDistVec, FullyDistVec > TopK(FullyDistSpVec & v, IT k) { // FullyDistVec::FullyDistVec(shared_ptr commgrid, IT glen, NT initval) FullyDistVec sel(v.getcommgrid(), k, 0); //void FullyDistVec::iota(IT globalsize, NT first) sel.iota(k, v.TotalLength() - k); FullyDistSpVec sorted(v); FullyDistSpVec perm = sorted.sort(); // FullyDistVec FullyDistSpVec::operator(FullyDistVec & v) FullyDistVec topkind = perm(sel); FullyDistVec topkele = v(topkind); return make_pair(topkind, topkele); } int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 6) { if(myrank == 0) { cout << "Usage: ./IndexingTest " << endl; cout << "Example: ./IndexingTest ../mfiles B_100x100.txt B_10x30_Indexed.txt rand10outta100.txt rand30outta100.txt" << endl; cout << "Input files should be under in tuples format" << endl; } MPI_Finalize(); return -1; } { string directory(argv[1]); string normalname(argv[2]); string indexdname(argv[3]); string vec1name(argv[4]); string vec2name(argv[5]); normalname = directory+"/"+normalname; indexdname = directory+"/"+indexdname; vec1name = directory+"/"+vec1name; vec2name = directory+"/"+vec2name; ifstream inputvec1(vec1name.c_str()); ifstream inputvec2(vec2name.c_str()); if(myrank == 0) { if(inputvec1.fail() || inputvec2.fail()) { cout << "One of the input vector files do not exist, aborting" << endl; MPI_Abort(MPI_COMM_WORLD, NOFILE); return -1; } } MPI_Barrier(MPI_COMM_WORLD); typedef SpParMat > PARDBMAT; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); PARDBMAT A(fullWorld); PARDBMAT AID(fullWorld); PARDBMAT ACID(fullWorld); FullyDistVec vec1(fullWorld); FullyDistVec vec2(fullWorld); A.ReadDistribute(normalname, 0); AID.ReadDistribute(indexdname, 0); vec1.ReadDistribute(inputvec1, 0); vec2.ReadDistribute(inputvec2, 0); vec1.Apply(bind2nd(minus(), 1)); // For 0-based indexing vec2.Apply(bind2nd(minus(), 1)); ACID = A(vec1, vec2); if (ACID == AID) { SpParHelper::Print("Indexing working correctly\n"); } else { SpParHelper::Print("ERROR in indexing, go fix it!\n"); } FullyDistVec crow(fullWorld); FullyDistVec ccol(fullWorld); FullyDistVec cval(fullWorld); A.Find(crow, ccol, cval); FullyDistSpVec sval = cval; sval.DebugPrint(); pair< FullyDistVec , FullyDistVec > ptopk; ptopk = TopK(sval, 3); //ptopk.first.DebugPrint(); //ptopk.second.DebugPrint(); // generate random permutations FullyDistVec p(fullWorld); FullyDistVec q(fullWorld); p.iota(A.getnrow(), 0); q.iota(A.getncol(), 0); p.RandPerm(); q.RandPerm(); PARDBMAT B = A(p,q); A.PrintInfo(); B.PrintInfo(); float oldbalance = A.LoadImbalance(); float newbalance = B.LoadImbalance(); ostringstream outs; outs << "Old balance: " << oldbalance << endl; outs << "New balance: " << newbalance << endl; SpParHelper::Print(outs.str()); SpParHelper::Print(outs.str()); // B = P A Q // get the inverse permutations FullyDistVec pinv = p.sort(); FullyDistVec qinv = q.sort(); SpParHelper::Print("Sorts are done\n"); PARDBMAT C = B(pinv,qinv); if(C == A) { SpParHelper::Print("Double permutation successfully restored the original\n"); } else { SpParHelper::Print("Error in permutation\n"); } inputvec1.clear(); inputvec1.close(); inputvec2.clear(); inputvec2.close(); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/ParIOTest.cpp000644 000765 000024 00000005071 13271404146 022352 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; // Simple helper class for declarations: Just the numerical type is templated // The index type and the sequential matrix type stays the same for the whole code // In this case, they are "int" and "SpDCCols" template class PSpMat { public: typedef SpDCCols < int64_t, NT > DCCols; typedef SpParMat < int64_t, NT, DCCols > MPI_DCCols; }; class StdArrayReadSaveHandler { public: array getNoNum(int64_t index) { return array(); } template array read(std::basic_istream& is, int64_t index) { array strarray; string str; is >> str; // read into str std::copy( str.begin(), str.end(), strarray.begin() ); if(str.length() < MAXVERTNAME) strarray[str.length()] = '\0'; // null terminating char return strarray; } template void save(std::basic_ostream& os, const array& strarray, int64_t index) { auto locnull = find(strarray.begin(), strarray.end(), '\0'); string str(strarray.begin(), locnull); os << str; } }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 2) { if(myrank == 0) { cout << "Usage: ./ParIOTest " << endl; cout << " is an absolute address, and file should be in Matrix Market format" << endl; cout << " is an absolute address, file is in general triples format (MCL calls this label input)" << endl; } MPI_Finalize(); return -1; } { string Aname(argv[1]); string Bname(argv[2]); typedef PlusTimesSRing PTDOUBLEDOUBLE; typedef SelectMaxSRing SR; PSpMat::MPI_DCCols A, B; A.ParallelReadMM(Aname, true, maximum()); FullyDistVec > perm = B.ReadGeneralizedTuples(Bname, maximum()); if (A == B) { SpParHelper::Print("Parallel Matrix Market I/O working correctly\n"); } else { SpParHelper::Print("ERROR in Parallel Matrix Market I/O"); A.SaveGathered("A_Error.txt"); B.SaveGathered("B_Error.txt"); } perm.ParallelWrite("PermutationVec.mtx", 1, StdArrayReadSaveHandler(), true); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/SplitMergeTest.cpp000644 000765 000024 00000006271 13271404146 023456 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 11/15/2016 --------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2016, The Regents of the University of California 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. */ #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; #define NSPLITS 5 int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); { typedef SpParMat < int64_t, int, SpDCCols > PSpMat_s32p64_Int; double initiator[4] = {.57, .19, .19, .05}; DistEdgeList * DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, 12, 8, true, true ); // generate packed edges SpParHelper::Print("Generated renamed edge lists\n"); // conversion from distributed edge list, keeps self-loops, sums duplicates PSpMat_s32p64_Int G(*DEL, false); delete DEL; // free memory before symmetricizing SpParHelper::Print("Created Sparse Matrix (with int32 local indices and values)\n"); SpDCCols G_perproc = G.seq(); G_perproc.PrintInfo(); SpDCCols G_perproc_copy = G_perproc; vector< SpDCCols > splits(NSPLITS); G_perproc.ColSplit(NSPLITS, splits); SpDCCols G_perproc_reconstructed; G_perproc_reconstructed.ColConcatenate(splits); G_perproc_reconstructed.PrintInfo(); if(G_perproc_copy == G_perproc_reconstructed) { cout << "ColSplit/ColConcatenate works" << endl; } else { cout << "ERROR" << endl; } } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/MultTest.cpp000644 000765 000024 00000020062 13271404146 022316 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.6 -------------------------------------------------*/ /* date: 6/15/2017 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc --------------------------*/ /****************************************************************/ /* Copyright (c) 2010-2017, The Regents of the University of California 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. */ #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; #ifdef TIMING double cblas_alltoalltime; double cblas_allgathertime; #endif #ifdef _OPENMP int cblas_splits = omp_get_max_threads(); #else int cblas_splits = 1; #endif // Simple helper class for declarations: Just the numerical type is templated // The index type and the sequential matrix type stays the same for the whole code // In this case, they are "int" and "SpDCCols" template class PSpMat { public: typedef SpDCCols < int64_t, NT > DCCols; typedef SpParMat < int64_t, NT, DCCols > MPI_DCCols; }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 6) { if(myrank == 0) { cout << "Usage: ./MultTest " << endl; cout << ",, are absolute addresses, and files should be in triples format" << endl; } MPI_Finalize(); return -1; } { string Aname(argv[1]); string Bname(argv[2]); string Cname(argv[3]); string V1name(argv[4]); string V2name(argv[5]); ifstream vecinpx(V1name.c_str()); ifstream vecinpy(V2name.c_str()); MPI_Barrier(MPI_COMM_WORLD); typedef PlusTimesSRing PTDOUBLEDOUBLE; typedef SelectMaxSRing SR; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); // construct objects PSpMat::MPI_DCCols A(fullWorld); PSpMat::MPI_DCCols B(fullWorld); PSpMat::MPI_DCCols C(fullWorld); PSpMat::MPI_DCCols CControl(fullWorld); FullyDistVec ycontrol(fullWorld); FullyDistVec x(fullWorld); FullyDistSpVec spycontrol(fullWorld); FullyDistSpVec spx(fullWorld); A.ParallelReadMM(Aname, true, maximum()); #ifndef NOGEMM B.ParallelReadMM(Bname, true, maximum()); CControl.ParallelReadMM(Cname, true, maximum()); #endif x.ReadDistribute(vecinpx, 0); spx.ReadDistribute(vecinpx, 0); ycontrol.ReadDistribute(vecinpy,0); spycontrol.ReadDistribute(vecinpy,0); FullyDistVec y = SpMV(A, x); if (ycontrol == y) { SpParHelper::Print("Dense SpMV (fully dist) working correctly\n"); } else { SpParHelper::Print("ERROR in Dense SpMV, go fix it!\n"); y.ParallelWrite("ycontrol_dense.txt",true); } //FullyDistSpVec spy = SpMV(A, spx); FullyDistSpVec spy(spx.getcommgrid(), A.getnrow()); SpMV(A, spx, spy, false); if (spycontrol == spy) { SpParHelper::Print("Sparse SpMV (fully dist) working correctly\n"); } else { SpParHelper::Print("ERROR in Sparse SpMV, go fix it!\n"); spy.ParallelWrite("ycontrol_sparse.txt",true); } // Test SpMSpV-bucket for general CSC matrices SpParMat < int64_t, double, SpCCols > ACsc (A); PreAllocatedSPA SPA(ACsc.seq(), cblas_splits*4); FullyDistSpVec spy_csc(spx.getcommgrid(), ACsc.getnrow()); SpMV(ACsc, spx, spy_csc, false, SPA); if (spy == spy_csc) { SpParHelper::Print("SpMSpV-bucket works correctly for general CSC matrices\n"); } else { SpParHelper::Print("SpMSpV-bucket does not work correctly for general CSC matrices, go fix it!\n"); } #ifndef NOGEMM C = Mult_AnXBn_Synch::DCCols >(A,B); if (CControl == C) { SpParHelper::Print("Synchronous Multiplication working correctly\n"); // C.SaveGathered("CControl.txt"); } else { SpParHelper::Print("ERROR in Synchronous Multiplication, go fix it!\n"); } C = Mult_AnXBn_DoubleBuff::DCCols >(A,B); if (CControl == C) { SpParHelper::Print("Double buffered multiplication working correctly\n"); } else { SpParHelper::Print("ERROR in double buffered multiplication, go fix it!\n"); } #endif OptBuf optbuf; PSpMat::MPI_DCCols ABool(A); spx.Apply(bind1st (multiplies(), 100)); FullyDistSpVec spxint64 (spx); //FullyDistSpVec spyint64 = SpMV(ABool, spxint64, false); FullyDistSpVec spyint64(spxint64.getcommgrid(), ABool.getnrow()); SpMV(ABool, spxint64, spyint64, false); ABool.OptimizeForGraph500(optbuf); //FullyDistSpVec spyint64buf = SpMV(ABool, spxint64, false, optbuf); FullyDistSpVec spyint64buf(spxint64.getcommgrid(), ABool.getnrow()); SpMV(ABool, spxint64, spyint64buf, false, optbuf); if (spyint64 == spyint64buf) { SpParHelper::Print("Graph500 Optimizations are correct\n"); } else { SpParHelper::Print("ERROR in graph500 optimizations, go fix it!\n"); spyint64.ParallelWrite("Original_SpMSV.txt",true); spyint64buf.ParallelWrite("Buffered_SpMSV.txt",true); } ABool.ActivateThreading(cblas_splits); //FullyDistSpVec spyint64_threaded = SpMV(ABool, spxint64, false); FullyDistSpVec spyint64_threaded(spxint64.getcommgrid(), ABool.getnrow()); SpMV(ABool, spxint64, spyint64_threaded, false); if (spyint64 == spyint64_threaded) { SpParHelper::Print("Multithreaded Sparse SpMV works\n"); } else { SpParHelper::Print("ERROR in multithreaded sparse SpMV, go fix it!\n"); } // Test SpMSpV-bucket for Boolean CSC matrices SpParMat < int64_t, bool, SpCCols > ABoolCsc (A); PreAllocatedSPA SPA1(ABoolCsc.seq(), cblas_splits*4); FullyDistSpVec spyint64_csc_threaded(spxint64.getcommgrid(), ABoolCsc.getnrow()); SpMV(ABoolCsc, spxint64, spyint64_csc_threaded, false, SPA1); if (spyint64 == spyint64_csc_threaded) { SpParHelper::Print("SpMSpV-bucket works correctly for Boolean CSC matrices\n"); } else { SpParHelper::Print("ERROR in SpMSpV-bucket with Boolean CSC matrices, go fix it!\n"); } vecinpx.clear(); vecinpx.close(); vecinpy.clear(); vecinpy.close(); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/CMakeLists.txt000644 000765 000024 00000004775 13271404146 022606 0ustar00aydinbulucstaff000000 000000 # Top level directory has the include files ADD_EXECUTABLE( MultTiming MultTiming.cpp ) ADD_EXECUTABLE( MultTest MultTest.cpp ) ADD_EXECUTABLE( ReduceTest ReduceTest.cpp ) ADD_EXECUTABLE( TransposeTest TransposeTest.cpp ) ADD_EXECUTABLE( IteratorTest IteratorTest.cpp ) ADD_EXECUTABLE( IndexingTest IndexingTest.cpp ) ADD_EXECUTABLE( SpAsgnTest SpAsgnTest.cpp ) ADD_EXECUTABLE( GalerkinNew GalerkinNew.cpp ) ADD_EXECUTABLE( IndexingTiming IndexingTiming.cpp ) ADD_EXECUTABLE( FindSparse FindSparse.cpp ) ADD_EXECUTABLE( ParIOTest ParIOTest.cpp ) TARGET_LINK_LIBRARIES( MultTiming CombBLAS) TARGET_LINK_LIBRARIES( MultTest CombBLAS) TARGET_LINK_LIBRARIES( ReduceTest CombBLAS) TARGET_LINK_LIBRARIES( TransposeTest CombBLAS) TARGET_LINK_LIBRARIES( IteratorTest CombBLAS) TARGET_LINK_LIBRARIES( IndexingTest CombBLAS) TARGET_LINK_LIBRARIES( SpAsgnTest CombBLAS) TARGET_LINK_LIBRARIES( GalerkinNew CombBLAS) TARGET_LINK_LIBRARIES( IndexingTiming CombBLAS) TARGET_LINK_LIBRARIES( FindSparse CombBLAS) TARGET_LINK_LIBRARIES( ParIOTest CombBLAS) ADD_TEST(NAME Multiplication_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA/rmat_scale16_A.mtx ../TESTDATA/rmat_scale16_B.mtx ../TESTDATA/rmat_scale16_productAB.mtx ../TESTDATA/x_65536_halfdense.txt ../TESTDATA/y_65536_halfdense.txt ) ADD_TEST(NAME Reduction_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA/sprand10000 ../TESTDATA/sprand10000_sumcols ../TESTDATA/sprand10000_sumrows) ADD_TEST(NAME Iterator_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA sprand10000) ADD_TEST(NAME Transpose_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA betwinput_scale16 betwinput_transposed_scale16) ADD_TEST(NAME Indexing_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA B_100x100.txt B_10x30_Indexed.txt rand10outta100.txt rand30outta100.txt) ADD_TEST(NAME SpAsgn_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA A_100x100.txt A_with20x30hole.txt dense_20x30matrix.txt A_wdenseblocks.txt 20outta100.txt 30outta100.txt) ADD_TEST(NAME GalerkinNew_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA/grid3d_k5.txt ../TESTDATA/offdiag_grid3d_k5.txt ../TESTDATA/diag_grid3d_k5.txt ../TESTDATA/restrict_T_grid3d_k5.txt) ADD_TEST(NAME FindSparse_Test COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 4 $ ../TESTDATA findmatrix.txt) CombBLAS_beta_16_2/ReleaseTests/._.DS_Store000644 000765 000024 00000000170 13271513300 021720 0ustar00aydinbulucstaff000000 000000 Mac OS X  2Fx @ATTRxxCombBLAS_beta_16_2/ReleaseTests/.DS_Store000644 000765 000024 00000020004 13271513300 021501 0ustar00aydinbulucstaff000000 000000 Bud1 eLists  @€ @€ @€ @CMakeLists.txtIlocblob;(ÿÿÿÿÿÿFindSparse.cppIlocblob…(ÿÿÿÿÿÿ Galerkin.cppIlocblob˜ÿÿÿÿÿÿGalerkinNew.cppIlocblob…˜ÿÿÿÿÿÿIndexingTest.cppIlocblob;ÿÿÿÿÿÿIndexingTiming.cppIlocblob©ÿÿÿÿÿÿIteratorTest.cppIlocblobÿÿÿÿÿÿ makefile-macIlocblobóÿÿÿÿÿÿmakefile-nerscIlocblob;xÿÿÿÿÿÿ MultTest.cppIlocblob©xÿÿÿÿÿÿMultTiming.cppIlocblobxÿÿÿÿÿÿ ParIOTest.cppIlocblob©èÿÿÿÿÿÿReduceTest.cppIlocblob;Xÿÿÿÿÿÿ Roofline.cppIlocblobóXÿÿÿÿÿÿSpAsgnTest.cppIlocblob…8ÿÿÿÿÿÿSpAsgnTiming.cppIlocblobó8ÿÿÿÿÿÿSplitMergeTest.cppIlocblob;¨ÿÿÿÿÿÿTransposeTest.cppIlocblob…¨ÿÿÿÿÿÿVectorIndexing.cppIlocblobó¨ÿÿÿÿÿÿ VectorIO.cppIlocblob;ÿÿÿÿÿÿVectorIOPermute.cppIlocblob©ÿÿÿÿÿÿVectorTest.cppIlocblobÿÿÿÿÿÿ E DSDB `€ @€ @€ @locblob©ÿÿÿÿÿÿVectorTest.cppIlocblobÿÿÿÿÿÿCombBLAS_beta_16_2/ReleaseTests/SpAsgnTest.cpp000644 000765 000024 00000012456 13271404146 022600 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.5 -------------------------------------------------*/ /* date: 10/09/2015 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2015, The Regents of the University of California 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. */ #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; template pair< FullyDistVec, FullyDistVec > TopK(FullyDistSpVec & v, IT k) { // FullyDistVec::FullyDistVec(IT glen, NT initval) FullyDistVec sel(v.getcommgrid(), k, 0); //void FullyDistVec::iota(IT globalsize, NT first) sel.iota(k, v.TotalLength() - k); FullyDistSpVec sorted(v); FullyDistSpVec perm = sorted.sort(); // FullyDistVec FullyDistSpVec::operator(FullyDistVec & v) FullyDistVec topkind = perm(sel); FullyDistVec topkele = v(topkind); return make_pair(topkind, topkele); } int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 8) { if(myrank == 0) { cout << "Usage: ./SpAsgnTest " << endl; cout << "Example: ./SpAsgnTest ../mfiles A_100x100.txt A_with20x30hole.txt dense_20x30matrix.txt A_wdenseblocks.txt 20outta100.txt 30outta100.txt" << endl; cout << "Input files should be under in tuples format" << endl; } MPI_Finalize(); return -1; } { string directory(argv[1]); string normalname(argv[2]); string prunedname(argv[3]); string rhsmatname(argv[4]); string assignname(argv[5]); string vec1name(argv[6]); string vec2name(argv[7]); normalname = directory+"/"+normalname; prunedname = directory+"/"+prunedname; rhsmatname = directory+"/"+rhsmatname; assignname = directory+"/"+assignname; vec1name = directory+"/"+vec1name; vec2name = directory+"/"+vec2name; ifstream inputvec1(vec1name.c_str()); ifstream inputvec2(vec2name.c_str()); if(myrank == 0) { if(inputvec1.fail() || inputvec2.fail()) { cout << "One of the input vector files do not exist, aborting" << endl; MPI_Abort(MPI_COMM_WORLD, NOFILE); return -1; } } MPI_Barrier(MPI_COMM_WORLD); typedef SpParMat > PARDBMAT; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); PARDBMAT A(fullWorld); PARDBMAT Apr(fullWorld); PARDBMAT B(fullWorld); PARDBMAT C(fullWorld); FullyDistVec vec1(fullWorld); FullyDistVec vec2(fullWorld); A.ReadDistribute(normalname, 0); Apr.ReadDistribute(prunedname, 0); B.ReadDistribute(rhsmatname, 0); C.ReadDistribute(assignname, 0); vec1.ReadDistribute(inputvec1, 0); vec2.ReadDistribute(inputvec2, 0); vec1.Apply(bind2nd(minus(), 1)); // For 0-based indexing vec2.Apply(bind2nd(minus(), 1)); PARDBMAT Atemp = A; Atemp.Prune(vec1, vec2); // We should get the original A back. if( Atemp == Apr) { SpParHelper::Print("Pruning is working\n"); } else { SpParHelper::Print("Error in pruning, go fix it\n"); } A.SpAsgn(vec1, vec2, B); if (A == C) { SpParHelper::Print("SpAsgn working correctly\n"); } else { SpParHelper::Print("ERROR in SpAsgn, go fix it!\n"); A.SaveGathered("Erroneous_SpAsgnd.txt"); } FullyDistVec crow(fullWorld); FullyDistVec ccol(fullWorld); FullyDistVec cval(fullWorld); A.Find(crow, ccol, cval); FullyDistSpVec sval = cval; // sval.DebugPrint(); pair< FullyDistVec , FullyDistVec > ptopk; ptopk = TopK(sval, 3); //ptopk.first.DebugPrint(); //ptopk.second.DebugPrint(); inputvec1.clear(); inputvec1.close(); inputvec2.clear(); inputvec2.close(); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/VectorTest.cpp000644 000765 000024 00000005250 13212627400 022634 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include "../FullyDistSpVec.h" #include "../FullyDistVec.h" using namespace std; using namespace combblas; template struct IsOdd : public unary_function { bool operator() (T number) {return (number%2==1);} }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); try { FullyDistSpVec SPV_A(1024); SPV_A.SetElement(2,2); SPV_A.SetElement(83,-83); SPV_A.SetElement(284,284); SpParHelper::Print("Printing SPV_A\n"); SPV_A.DebugPrint(); FullyDistSpVec SPV_B(1024); SPV_B.SetElement(2,4); SPV_B.SetElement(184,368); SPV_B.SetElement(83,-1); SpParHelper::Print("Printing SPV_B\n"); SPV_B.DebugPrint(); FullyDistVec FDV(-1); FDV.iota(64,0); SpParHelper::Print("Printing FPV\n"); FDV.DebugPrint(); FullyDistSpVec FDSV = FDV.Find(IsOdd()); SpParHelper::Print("Printing FPSV\n"); FDSV.DebugPrint(); FullyDistSpVec SPV_C(12); SPV_C.SetElement(2,2); SPV_C.SetElement(4,4); SPV_C.SetElement(5,-5); SPV_C.SetElement(6,6); SpParHelper::Print("Printing SPV_C\n"); SPV_C.DebugPrint(); FullyDistSpVec SPV_D(12); SPV_D.SetElement(2,4); SPV_D.SetElement(3,9); SPV_D.SetElement(5,-25); SPV_D.SetElement(7,-49); SpParHelper::Print("Printing SPV_D\n"); SPV_D.DebugPrint(); SPV_C += SPV_D; SPV_D += SPV_D; SpParHelper::Print("Printing SPV_C + SPV_D\n"); SPV_C.DebugPrint(); SpParHelper::Print("Printing SPV_D + SPV_D\n"); SPV_D.DebugPrint(); FullyDistSpVec SPV_E(3); SPV_E.SetElement(0,3); SPV_E.SetElement(1,7); SPV_E.SetElement(2,10); SpParHelper::Print("Printing SPV_E\n"); SPV_E.DebugPrint(); FullyDistSpVec SPV_F = SPV_C(SPV_E); SpParHelper::Print("Printing SPV_F = SPV_C(SPV_E)\n"); SPV_F.DebugPrint(); FullyDistSpVec SPV_H = SPV_C; FullyDistSpVec SPV_J = SPV_H(SPV_F); int64_t val = SPV_J[8]; stringstream tss; string ss; if(val == SPV_J.NOT_FOUND) { ss = "NOT_FOUND"; } else { tss << val; ss = tss.str(); } if(myrank == 0) cout << ss << endl; SPV_J.SetElement(8, 777); val = SPV_J[8]; if(val == SPV_J.NOT_FOUND) { ss = "NOT_FOUND"; } else { tss << val; ss = tss.str(); } if(myrank == 0) cout << ss << endl; } catch (exception& e) { cout << e.what() << endl; } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/IndexingTiming.cpp000644 000765 000024 00000011234 13271404146 023453 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; #define ITERATIONS 10 template bool from_string(T & t, const string& s, std::ios_base& (*f)(std::ios_base&)) { istringstream iss(s); return !(iss >> f >> t).fail(); } int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./IndexingTiming Input/Force/Binary //" << endl; } MPI_Finalize(); return -1; } { typedef SpParMat > PARDBMAT; PARDBMAT * A; // declare objects if(string(argv[1]) == string("Input")) { A->ReadDistribute(argv[2], 0); } else if(string(argv[1]) == string("Binary")) { uint64_t n, m; from_string(n,string(argv[3]),std::dec); from_string(m,string(argv[4]),std::dec); ostringstream outs; outs << "Reading " << argv[2] << " with " << n << " vertices and " << m << " edges" << endl; SpParHelper::Print(outs.str()); DistEdgeList * DEL = new DistEdgeList(argv[2], n, m); SpParHelper::Print("Read binary input to distributed edge list\n"); PermEdges(*DEL); SpParHelper::Print("Permuted Edges\n"); RenameVertices(*DEL); SpParHelper::Print("Renamed Vertices\n"); A = new PARDBMAT(*DEL, false); delete DEL; // free memory before symmetricizing SpParHelper::Print("Created double Sparse Matrix\n"); } else if(string(argv[1]) == string("Force")) { double initiator[4] = {.6, .4/3, .4/3, .4/3}; DistEdgeList * DEL = new DistEdgeList(); int scale = static_cast(atoi(argv[2])); ostringstream outs; outs << "Forcing scale to : " << scale << endl; SpParHelper::Print(outs.str()); DEL->GenGraph500Data(initiator, scale, 8 * ((int64_t) std::pow(2.0, (double) scale)) / nprocs ); SpParHelper::Print("Generated local RMAT matrices\n"); PermEdges(*DEL); SpParHelper::Print("Permuted Edges\n"); RenameVertices(*DEL); SpParHelper::Print("Renamed Vertices\n"); // conversion from distributed edge list, keeps self-loops, sums duplicates A = new PARDBMAT(*DEL, false); delete DEL; // free memory before symmetricizing SpParHelper::Print("Created double Sparse Matrix\n"); } A->PrintInfo(); FullyDistVec p; p.iota(A->getnrow(), 0); p.RandPerm(); SpParHelper::Print("Permutation Generated\n"); PARDBMAT B = (*A)(p,p); B.PrintInfo(); float oldbalance = A->LoadImbalance(); float newbalance = B.LoadImbalance(); ostringstream outs; outs << "Running on " << nprocs << " cores" << endl; outs << "Old balance: " << oldbalance << endl; outs << "New balance: " << newbalance << endl; SpParHelper::Print(outs.str()); MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i > clusters(nclust); int nperclus = A->getnrow() / nclust; for(int i = 0; i< nclust; i++) { int k = std::min(nperclus, A->getnrow() - nperclus * i); clusters[i].iota(k, nperclus * i); clusters[i] = p(clusters[i]); } for(int i=0; i< nclust; i++) { B = (*A)(clusters[i], clusters[i]); B.PrintInfo(); } MPI_Barrier(MPI_COMM_WORLD); t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i< nclust; i++) { for(int j=0; j < ITERATIONS; j++) B = (*A)(clusters[i], clusters[i]); } MPI_Barrier(MPI_COMM_WORLD); t2 = MPI_Wtime(); if(myrank == 0) { cout<<"Indexing Iterations finished"< #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; #define ITERATIONS 1 // Simple helper class for declarations: Just the numerical type is templated // The index type and the sequential matrix type stays the same for the whole code // In this case, they are "int" and "SpDCCols" template class PSpMat { public: typedef SpDCCols < int, NT > DCCols; typedef SpParMat < int, NT, DCCols > MPI_DCCols; }; #define ElementType double int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./MultTest " << endl; cout << ", are absolute addresses, and files should be in triples format" << endl; } MPI_Finalize(); return -1; } { string Aname(argv[1]); string Bname(argv[2]); typedef PlusTimesSRing PTDOUBLEDOUBLE; PSpMat::MPI_DCCols A, B; // construct objects A.ReadDistribute(Aname, 0); A.PrintInfo(); B.ReadDistribute(Bname, 0); B.PrintInfo(); SpParHelper::Print("Data read\n"); { // force the calling of C's destructor PSpMat::MPI_DCCols C = Mult_AnXBn_DoubleBuff::DCCols >(A, B); int64_t cnnz = C.getnnz(); ostringstream tinfo; tinfo << "C has a total of " << cnnz << " nonzeros" << endl; SpParHelper::Print(tinfo.str()); SpParHelper::Print("Warmed up for DoubleBuff\n"); C.PrintInfo(); } MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(1,"SpGEMM_DoubleBuff"); double t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::MPI_DCCols C = Mult_AnXBn_DoubleBuff::DCCols >(A, B); } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); MPI_Pcontrol(-1,"SpGEMM_DoubleBuff"); if(myrank == 0) { cout<<"Double buffered multiplications finished"<::MPI_DCCols C = Mult_AnXBn_Synch::DCCols >(A, B); C.PrintInfo(); } SpParHelper::Print("Warmed up for Synch\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(1,"SpGEMM_Synch"); t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::MPI_DCCols C = Mult_AnXBn_Synch::DCCols >(A, B); } MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(-1,"SpGEMM_Synch"); t2 = MPI_Wtime(); if(myrank == 0) { cout<<"Synchronous multiplications finished"<::DCCols >(A, B); SpParHelper::Print("Warmed up for ActiveTarget\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(1,"SpGEMM_ActiveTarget"); t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::DCCols >(A, B); } MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(-1,"SpGEMM_ActiveTarget"); t2 = MPI_Wtime(); if(myrank == 0) { cout<<"Active target multiplications finished"<::DCCols >(A, B); SpParHelper::Print("Warmed up for PassiveTarget\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(1,"SpGEMM_PassiveTarget"); t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::DCCols >(A, B); } MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(-1,"SpGEMM_PassiveTarget"); t2 = MPI_Wtime(); if(myrank == 0) { cout<<"Passive target multiplications finished"< #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" #include "../Applications/TwitterEdge.h" using namespace std; using namespace combblas; // One edge = 16 bytes (rounded up from 11) // One parent = 8 bytes #define INC 256 #define L1 4096 // maximum entries of edges+parents combined to fit 32 KB #define REPEAT 1000 // all the local variables before the EWiseApply wouldn't be accessible, // so we need to pass them as parameters to the functor constructor class twitter_mult : public std::binary_function { private: time_t sincedate; public: twitter_mult(time_t since):sincedate(since) {}; ParentType operator()(const ParentType & arg1, const TwitterEdge & arg2) const { if(arg2.isFollower() && arg2.TweetSince(sincedate)) return arg1; else return ParentType(); // null-type parent id } }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); { int64_t len = INC; time_t now; time ( &now ); TwitterEdge twe(4, 1, now); // 4 retweets, latest now, following while(len < L1) { // FullyDistVec(IT globallen, NT initval) FullyDistVec tvec(nprocs * len, twe); FullyDistVec pvec; pvec.iota(nprocs * len, ParentType()); MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); // initilize (wall-clock) timer time_t now = time(0); struct tm * timeinfo = localtime( &now); timeinfo->tm_mon = timeinfo->tm_mon-1; time_t monthago = mktime(timeinfo); for(int i=0; i< REPEAT; ++i) pvec.EWiseApply(tvec, twitter_mult(monthago)); MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); if(myrank == 0) { cout<<"EWiseApply Iterations finished"< #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; #define ITERATIONS 10 // Simple helper class for declarations: Just the numerical type is templated // The index type and the sequential matrix type stays the same for the whole code // In this case, they are "int" and "SpDCCols" template class PSpMat { public: typedef SpDCCols < int, NT > DCCols; typedef SpParMat < int, NT, DCCols > MPI_DCCols; }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 4) { if(myrank == 0) { cout << "Usage: ./Galerkin " << endl; cout << ",, are absolute addresses, and files should be in triples format" << endl; } MPI_Finalize(); return -1; } { string Aname(argv[1]); string Sname(argv[2]); string STname(argv[3]); MPI_Barrier(MPI_COMM_WORLD); typedef PlusTimesSRing PTDOUBLEDOUBLE; PSpMat::MPI_DCCols A, S, ST; // construct objects A.ReadDistribute(Aname, 0); S.ReadDistribute(Sname, 0); ST.ReadDistribute(STname, 0); SpParHelper::Print("Data read\n"); // force the calling of C's destructor { PSpMat::MPI_DCCols C = Mult_AnXBn_DoubleBuff >(A, ST); PSpMat::MPI_DCCols D = Mult_AnXBn_DoubleBuff >(S, C); SpParHelper::Print("Warmed up for DoubleBuff (right evaluate)\n"); } MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(1,"SpGEMM_DoubleBuff_right"); double t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::MPI_DCCols C = Mult_AnXBn_DoubleBuff >(A, ST); PSpMat::MPI_DCCols D = Mult_AnXBn_DoubleBuff >(S, C); } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); MPI_Pcontrol(-1,"SpGEMM_DoubleBuff_right"); if(myrank == 0) { cout<<"Double buffered multiplications (right evaluate) finished"<::MPI_DCCols C = Mult_AnXBn_DoubleBuff >(S, A); PSpMat::MPI_DCCols D = Mult_AnXBn_DoubleBuff >(C, ST); SpParHelper::Print("Warmed up for DoubleBuff (left evaluate)\n"); } MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(1,"SpGEMM_DoubleBuff_left"); t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::MPI_DCCols C = Mult_AnXBn_DoubleBuff >(S, A); PSpMat::MPI_DCCols D = Mult_AnXBn_DoubleBuff >(C, ST); } MPI_Barrier(MPI_COMM_WORLD); t2 = MPI_Wtime(); MPI_Pcontrol(-1,"SpGEMM_DoubleBuff_left"); if(myrank == 0) { cout<<"Double buffered multiplications (left evaluate) finished"<::MPI_DCCols C = Mult_AnXBn_Synch >(A, ST); PSpMat::MPI_DCCols D = Mult_AnXBn_Synch >(S, C); } SpParHelper::Print("Warmed up for Synch (right evaluate)\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(1,"SpGEMM_Synch_right"); t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::MPI_DCCols C = Mult_AnXBn_Synch >(A, ST); PSpMat::MPI_DCCols D = Mult_AnXBn_Synch >(S, C); } MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(-1,"SpGEMM_Synch_right"); t2 = MPI_Wtime(); if(myrank == 0) { cout<<"Synchronous multiplications (right evaluate) finished"<::MPI_DCCols C = Mult_AnXBn_Synch >(S, A); PSpMat::MPI_DCCols D = Mult_AnXBn_Synch >(C, ST); } SpParHelper::Print("Warmed up for Synch (left evaluate)\n"); MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(1,"SpGEMM_Synch_left"); t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::MPI_DCCols C = Mult_AnXBn_Synch >(S, A); PSpMat::MPI_DCCols D = Mult_AnXBn_Synch >(C, ST); } MPI_Barrier(MPI_COMM_WORLD); MPI_Pcontrol(-1,"SpGEMM_Synch_left"); t2 = MPI_Wtime(); if(myrank == 0) { cout<<"Synchronous multiplications (left evaluate) finished"< #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; template class PairReadSaveHandler { public: PairReadSaveHandler() {}; pair getNoNum(IT index) { return make_pair((NT) 0, (NT) 0); } template pair read(std::basic_istream& is, IT index) { pair pp; is >> pp.first >> pp.second; return pp; } template void save(std::basic_ostream& os, const pair & pp, IT index) { os << pp.first << "\t" << pp.second; } }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 5) { if(myrank == 0) { cout << "Usage: ./VectorIO base(0 or 1)" << endl; cout << "Example: ./VectorIO vertex_dict.mtx clusters.mtx original_clusters.mtx 0" << endl; } MPI_Finalize(); return -1; } { string vec1name(argv[1]); string vec2name(argv[2]); string voutname(argv[3]); int base = atoi(argv[4]); FullyDistVec vecdict, clusters; // keep cluster ids as "string" for generality // the "index sets" of vecdict and clusters are the same // goal here is to just convert the indices in "clusters" to values in vecdict vecdict.ParallelRead(vec1name, base, // keeps lexicographically larger one in case of duplicates (after warning) [](string s1, string s2) { cout << "Unexpected duplicate in dictionary" << endl; return std::max(s1, s2); }); clusters.ParallelRead(vec2name, base, // keeps lexicographically larger one in case of duplicates (after warning) [](string s1, string s2) { cout << "Unexpected duplicate in unpermuted vector" << endl; return std::max(s1, s2); }); vecdict.PrintInfo("dictionary"); clusters.PrintInfo("unpermuted cluster vector"); // FullyDistVec::EWiseOut(const FullyDistVec & rhs, _BinaryOperation __binary_op, FullyDistVec & result) // Perform __binary_op(*this[i], rhs[i]) for every element in rhs and *this, write the result output vector FullyDistVec > newclusters; clusters.EWiseOut(vecdict, [] (string s1, string s2) { return make_pair(s1,s2); }, newclusters); newclusters.ParallelWrite(voutname, base, PairReadSaveHandler(), false); // don't print the index } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/VectorIOPermute.cpp000644 000765 000024 00000006252 13271404146 023576 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; #define myidlen 35 #define myseqlen 150 template class ShortRead { public: ShortRead(){ id[0] = '\0'; seq[0] = '\0'; qual[0] = '\0'; }; ShortRead(const string & s_id, const string & s_seq, const string & s_qual) { s_id.copy(id, IDLEN); s_seq.copy(seq, SEQLEN); s_qual.copy(qual, SEQLEN); id[IDLEN] = '\0'; seq[SEQLEN] = '\0'; qual[SEQLEN] = '\0'; } template friend ostream& operator<<( ostream& os, const ShortRead & sread); private: char id[IDLEN+1]; // (+1)s are for null termination char seq[SEQLEN+1]; char qual[SEQLEN+1]; template // NS: no-shadow friend class ShortReadSaveHandler; }; template ostream& operator<<(ostream& os, const ShortRead & sread ) { os << sread.id << " " << sread.seq << " " << sread.qual << endl; return os; }; template class ShortReadSaveHandler { public: ShortReadSaveHandler() {}; ShortRead getNoNum(IT ind) { return ShortRead(); } MPI_Datatype getMPIType() { return MPIType< ShortRead >(); // utilize the MPI type cache } void binaryfill(FILE * rFile, IT & ind, ShortRead & val) { size_t entryLength = fread (&ind,sizeof(ind),1,rFile); // read the index first entryLength += fread (&val,sizeof(ShortRead),1,rFile); if(entryLength != 2) cout << "Not enough bytes read in binaryfill " << endl; } size_t entrylength() { return sizeof(ShortRead); } template ShortRead read(std::basic_istream& is, IT ind) { string s_id, s_seq, s_qual, s_null; getline (is,s_id); getline (is,s_seq); getline (is,s_null); // basically the '+' sign getline (is,s_qual); return ShortRead(s_id, s_seq, s_qual); } template void save(std::basic_ostream& os, const ShortRead & tw, IT ind) { // make it compatible with read... } }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 4) { if(myrank == 0) { cout << "Usage: ./VectorIOPermute " << endl; cout << " is a binary file" << endl; } MPI_Finalize(); return -1; } { FullyDistSpVec > ShortReads; ShortReads.ReadDistribute(string(argv[1]), 0, ShortReadSaveHandler(), true); // read it from binary file in parallel } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/IteratorTest.cpp000644 000765 000024 00000006022 13271404146 023166 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.5 -------------------------------------------------*/ /* date: 10/09/2015 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2015, The Regents of the University of California 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. */ #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./IteratorTest " << endl; cout << "Input file should be under in triples format" << endl; } MPI_Finalize(); return -1; } { string directory(argv[1]); string name(argv[2]); name = directory+"/"+name; typedef SpParMat > PARMAT; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); PARMAT A(fullWorld); A.ReadDistribute(name, 0); // read it from file int count = 0; int total = 0; for(SpDCCols::SpColIter colit = A.seq().begcol(); colit != A.seq().endcol(); ++colit) // iterate over columns { for(SpDCCols::SpColIter::NzIter nzit = A.seq().begnz(colit); nzit != A.seq().endnz(colit); ++nzit) { // cout << nzit.rowid() << '\t' << colit.colid() << '\t' << nzit.value() << '\n'; count++; } } MPI_Allreduce( &count, &total, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); if(total == A.getnnz()) SpParHelper::Print( "Iteration passed soft test\n"); else SpParHelper::Print( "Iteration failed !!!\n") ; } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/makefile-mac000644 000765 000024 00000010751 13212627400 022266 0ustar00aydinbulucstaff000000 000000 INCDIR = /opt/local/include INCADD = -I$(INCDIR) -I$(INCDIR)/mpich2 OPT = -O3 -DMPICH_IGNORE_CXX_SEEK #-DNDEBUG (disables important assertions) DEB = -g -O0 -fno-inline -DMPICH_IGNORE_CXX_SEEK -DCOMBBLAS_DEBUG COMPILER = mpicxx -std=c++14 -DTHREADED -fopenmp FLAGS = $(DEB) COMBBLAS = .. # # build Graph500 generator # $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator CommGrid.o: ../CommGrid.cpp ../CommGrid.h $(COMPILER) $(FLAGS) $(INCADD) -c -o CommGrid.o ../CommGrid.cpp mmio.o: ../mmio.c ../mmio.h mpicc $(FLAGS) $(INCADD) -c -o mmio.o ../mmio.c MPIType.o: ../MPIType.cpp ../MPIType.h $(COMPILER) $(FLAGS) $(INCADD) -c -o MPIType.o ../MPIType.cpp hash.o: ../hash.cpp ../hash.hpp $(COMPILER) $(FLAGS) $(INCADD) -c -o hash.o ../hash.cpp MemoryPool.o: ../MemoryPool.cpp ../SpDefs.h $(COMPILER) $(FLAGS) $(INCADD) -c -o MemoryPool.o ../MemoryPool.cpp TransposeTest.o: TransposeTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(INCADD) $(FLAGS) -c -o TransposeTest.o TransposeTest.cpp IteratorTest.o: IteratorTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(INCADD) $(FLAGS) -c -o IteratorTest.o IteratorTest.cpp MultTest.o: MultTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(INCADD) $(FLAGS) -c -o MultTest.o MultTest.cpp MultTiming.o: MultTiming.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(INCADD) $(FLAGS) -c -o MultTiming.o MultTiming.cpp ReduceTest.o: ReduceTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(INCADD) $(FLAGS) -c -o ReduceTest.o ReduceTest.cpp VectorIndexing.o: VectorIndexing.cpp ../FullyDistSpVec.cpp ../FullyDistVec.cpp ../FullyDistSpVec.h ../FullyDistVec.h $(COMPILER) $(INCADD) $(FLAGS) -c -o VectorIndexing.o VectorIndexing.cpp VectorIO.o: VectorIOPermute.cpp ../FullyDistSpVec.cpp ../FullyDistVec.cpp ../FullyDistSpVec.h ../FullyDistVec.h $(COMPILER) $(INCADD) $(FLAGS) -c -o VectorIO.o VectorIO.cpp SplitMergeTest.o: SplitMergeTest.cpp ../FullyDistSpVec.cpp ../FullyDistVec.cpp ../FullyDistSpVec.h ../FullyDistVec.h $(COMPILER) $(INCADD) $(FLAGS) -c -o SplitMergeTest.o SplitMergeTest.cpp ParIOTest.o: ParIOTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h ../SpParHelper.cpp $(COMPILER) $(INCADD) $(FLAGS) -c -o ParIOTest.o ParIOTest.cpp TransposeTest: MemoryPool.o CommGrid.o MPIType.o TransposeTest.o mmio.o $(COMPILER) $(FLAGS) $(INCADD) -o TransposeTest TransposeTest.o MemoryPool.o CommGrid.o MPIType.o mmio.o MultTest: MemoryPool.o CommGrid.o MPIType.o MultTest.o mmio.o $(COMPILER) $(FLAGS) $(INCADD) -o MultTest MultTest.o MemoryPool.o CommGrid.o MPIType.o mmio.o MultTime: MemoryPool.o CommGrid.o MPIType.o MultTiming.o mmio.o $(COMPILER) $(FLAGS) $(INCADD) -o MultTime MultTiming.o MemoryPool.o CommGrid.o MPIType.o mmio.o IteratorTest: MemoryPool.o CommGrid.o MPIType.o IteratorTest.o mmio.o $(COMPILER) $(FLAGS) $(INCADD) -o IteratorTest IteratorTest.o MemoryPool.o CommGrid.o MPIType.o mmio.o SplitMergeTest: MemoryPool.o CommGrid.o MPIType.o SplitMergeTest.o mmio.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(FLAGS) $(INCADD) -o SplitMergeTest SplitMergeTest.o MemoryPool.o CommGrid.o MPIType.o mmio.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq ReduceTest: MemoryPool.o CommGrid.o MPIType.o ReduceTest.o mmio.o $(COMPILER) $(FLAGS) $(INCADD) -o ReduceTest ReduceTest.o MemoryPool.o CommGrid.o MPIType.o mmio.o VectorInd: MemoryPool.o CommGrid.o MPIType.o VectorIndexing.o mmio.o $(COMPILER) $(FLAGS) $(INCADD) -o VectorInd VectorIndexing.o MemoryPool.o CommGrid.o MPIType.o mmio.o VectorIO: MemoryPool.o CommGrid.o MPIType.o VectorIO.o mmio.o $(COMPILER) $(FLAGS) $(INCADD) -o VectorIO VectorIO.o MemoryPool.o CommGrid.o MPIType.o mmio.o ParIOMM: MemoryPool.o CommGrid.o MPIType.o ParIOTest.o mmio.o hash.o $(COMPILER) $(FLAGS) $(INCADD) -o ParIOMM ParIOTest.o MemoryPool.o CommGrid.o MPIType.o mmio.o hash.o clean: rm -f TransposeTest rm -f SplitMergeTest rm -f MultTest rm -f ReduceTest rm -f VectorInd rm -f VectorIOPerm rm -f IteratorTest rm -f ParIOMM rm -f MultTime rm -f *.o cleanout: rm out.* rm err.* CombBLAS_beta_16_2/ReleaseTests/FindSparse.cpp000644 000765 000024 00000013444 13271404146 022601 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.5 -------------------------------------------------*/ /* date: 10/09/2015 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2015, The Regents of the University of California 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. */ #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; template void Symmetricize(PARMAT & A) { // boolean addition is practically a "logical or" // therefore this doesn't destruct any links PARMAT AT = A; AT.Transpose(); A += AT; } int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 3) { if(myrank == 0) { cout << "Usage: ./FindSparse " << endl; cout << "Input files should be under in appropriate format" << endl; } MPI_Finalize(); return -1; } { string directory(argv[1]); string matrixname(argv[2]); matrixname = directory+"/"+matrixname; typedef SpParMat > PARDBMAT; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); PARDBMAT A(fullWorld); // declare objects FullyDistVec crow(fullWorld); FullyDistVec ccol(fullWorld); FullyDistVec cval(fullWorld); A.ReadDistribute(matrixname, 0); A.Find(crow, ccol, cval); PARDBMAT B(A.getnrow(), A.getncol(), crow, ccol, cval); // Sparse() if (A == B) { SpParHelper::Print("Find and Sparse working correctly\n"); } else { SpParHelper::Print("ERROR in Find(), go fix it!\n"); SpParHelper::Print("Rows array: \n"); crow.DebugPrint(); SpParHelper::Print("Columns array: \n"); ccol.DebugPrint(); SpParHelper::Print("Values array: \n"); cval.DebugPrint(); } // Begin testing a Matlab like use case // Example provided by Arne De Coninck // X = ones(size(A,2),1) // M = [X' * X, X' * A; A'* X, A' * A] A.PrintInfo(); Symmetricize(A); FullyDistVec rowsym(fullWorld); FullyDistVec colsym(fullWorld); FullyDistVec valsym(fullWorld); A.Find(rowsym, colsym, valsym); FullyDistVec colsums(fullWorld); A.Reduce(colsums, Column, plus(), 0.0); FullyDistVec numcols(A.getcommgrid(), 1, A.getncol()); #if defined(COMBBLAS_TR1) || defined(COMBBLAS_BOOST) || defined(NOTGNU) vector< FullyDistVec > vals2concat; vals2concat.push_back(numcols); vals2concat.push_back(colsums); vals2concat.push_back(colsums); vals2concat.push_back(valsym); #else vector< FullyDistVec > vals2concat{numcols, colsums, colsums, valsym}; #endif FullyDistVec nval = Concatenate(vals2concat); nval.PrintInfo("Values:"); // sparse() expects a zero based index FullyDistVec firstrowrids(A.getcommgrid(), A.getncol()+1, 0); // M(1,:) FullyDistVec firstcolrids(A.getcommgrid(), A.getncol(), 0); // M(2:end,1) firstcolrids.iota(A.getncol(),1); // fill M(2:end,1)'s row ids rowsym.Apply(bind2nd(plus(), 1)); #if defined(COMBBLAS_TR1) || defined(COMBBLAS_BOOST) || defined(NOTGNU) vector< FullyDistVec > rows2concat; rows2concat.push_back(firstrowrids); rows2concat.push_back(firstcolrids); rows2concat.push_back(rowsym); #else vector< FullyDistVec > rows2concat{firstrowrids, firstcolrids, rowsym}; // C++11 style #endif FullyDistVec nrow = Concatenate(rows2concat); nrow.PrintInfo("Row ids:"); FullyDistVec firstrowcids(A.getcommgrid(), A.getncol()+1, 0); // M(1,:) firstrowcids.iota(A.getncol()+1,0); // fill M(1,:)'s column ids FullyDistVec firstcolcids(A.getcommgrid(), A.getncol(), 0); // M(2:end,1) colsym.Apply(bind2nd(plus(), 1)); #if defined(COMBBLAS_TR1) || defined(COMBBLAS_BOOST) || defined(NOTGNU) vector< FullyDistVec > cols2concat; cols2concat.push_back(firstrowcids); cols2concat.push_back(firstcolcids); cols2concat.push_back(colsym); #else vector< FullyDistVec > cols2concat{firstrowcids, firstcolcids, colsym}; // C++11 style #endif FullyDistVec ncol = Concatenate(cols2concat); ncol.PrintInfo("Column ids:"); PARDBMAT M(A.getnrow()+1, A.getncol()+1, nrow, ncol, nval); // Sparse() M.PrintInfo(); // End Arne's test case } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/SpAsgnTiming.cpp000755 000765 000024 00000005147 13271404146 023112 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; #define ITERATIONS 10 #define EDGEFACTOR 8 int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 2) { if(myrank == 0) { cout << "Usage: ./IndexingTiming " << endl; } MPI_Finalize(); return -1; } { typedef SpParMat > PARDBMAT; PARDBMAT *A, *B; // declare objects double initiator[4] = {.6, .4/3, .4/3, .4/3}; DistEdgeList * DEL = new DistEdgeList(); int scale = static_cast(atoi(argv[1])); ostringstream outs, outs2, outs3; outs << "Forcing scale to : " << scale << endl; SpParHelper::Print(outs.str()); DEL->GenGraph500Data(initiator, scale, EDGEFACTOR, true, true ); // generate packed edges SpParHelper::Print("Generated renamed edge lists\n"); // conversion from distributed edge list, keeps self-loops, sums duplicates A = new PARDBMAT(*DEL, false); // already creates renumbered vertices (hence balanced) delete DEL; // free memory before symmetricizing SpParHelper::Print("Created double Sparse Matrix\n"); float balance = A->LoadImbalance(); outs2 << "Load balance: " << balance << endl; SpParHelper::Print(outs2.str()); A->PrintInfo(); for(unsigned i=1; i<4; i++) { DEL = new DistEdgeList(); DEL->GenGraph500Data(initiator, scale-i, ((double) EDGEFACTOR) / pow(2.0,i) , true, true ); // "i" scale smaller B = new PARDBMAT(*DEL, false); delete DEL; SpParHelper::Print("Created RHS Matrix\n"); B->PrintInfo(); FullyDistVec perm; // get a different permutation perm.iota(A->getnrow(), 0); perm.RandPerm(); //void FullyDistVec::iota(IT globalsize, NT first) FullyDistVec sel; sel.iota(B->getnrow(), 0); perm = perm(sel); // just get the first B->getnrow() entries of the permutation perm.PrintInfo("Index vector"); A->SpAsgn(perm,perm,*B); // overriding A with a structurally similar piece. A->PrintInfo(); double t1 = MPI_Wtime(); for(int j=0; j< ITERATIONS; ++j) { A->SpAsgn(perm,perm,*B); } double t2 = MPI_Wtime(); if(myrank == 0) { cout<< "Scale " << scale-i << " assignment iterations finished"< #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; // Simple helper class for declarations: Just the numerical type is templated // The index type and the sequential matrix type stays the same for the whole code // In this case, they are "int" and "SpDCCols" template class PSpMat { public: typedef SpDCCols < int, NT > DCCols; typedef SpParMat < int, NT, DCCols > MPI_DCCols; }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 4) { if(myrank == 0) { cout << "Usage: ./ReduceTest " << endl; cout << ",, are absolute addresses, and files should be in triples format" << endl; } MPI_Finalize(); return -1; } { string Aname(argv[1]); string Bname(argv[2]); string Cname(argv[3]); ifstream inputB(Bname.c_str()); ifstream inputC(Cname.c_str()); MPI_Barrier(MPI_COMM_WORLD); shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); PSpMat::MPI_DCCols A(fullWorld); FullyDistVec colsums(A.getcommgrid()); FullyDistVec rowsums(A.getcommgrid()); A.ReadDistribute(Aname, 0); colsums.ReadDistribute(inputB, 0); rowsums.ReadDistribute(inputC, 0); FullyDistVec< int, double > rowsums_control(fullWorld); FullyDistVec< int, double > colsums_control(fullWorld); A.Reduce(rowsums_control, Row, std::plus() , 0.0); A.Reduce(colsums_control, Column, std::plus() , 0.0); if (rowsums_control == rowsums && colsums_control == colsums) { SpParHelper::Print("Reduction via summation working correctly\n"); } else { SpParHelper::Print("ERROR in Reduce via summation, go fix it!\n"); } inputB.clear(); inputB.close(); inputC.clear(); inputC.close(); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/makefile-nersc000755 000765 000024 00000015214 13212627400 022642 0ustar00aydinbulucstaff000000 000000 # notes for configure: # -fno-exceptions does not work with MPICH2 # -fno-rtti does not work with tr1:tuples BOOST = $(HOME)/boost_1_45_0/ # notes for configure: # -fno-exceptions does not work with MPICH2 # -fno-rtti does not work with tr1:tuples GCCOPT = -O2 -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ #-DNDEBUG (disables important assertions) GCCDEB = -g -fno-inline -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ #-DDEBUG OPTPGI = -fast -Mipa=fast,inline -Msmartalloc --zc_eh -DMPICH_IGNORE_CXX_SEEK -DGRAPH_GENERATOR_SEQ COMPILER = CC GCCFLAGS = $(GCCOPT) -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -std=c++14 INTELFLAGS = $(GCCOPT) -w -D__STDC_CONSTANT_MACROS -D__STDC_LIMIT_MACROS -std=c++11 -std=c++14 PGIFLAGS = -I$(BOOST) $(OPTPGI) -DCOMBBLAS_BOOST -DBOOST_HAS_STDINT_H FLAGS = $(GCCFLAGS) -fopenmp -DTHREADED -DCOMBBLAS_DEBUG LINK = -lm COMBBLAS = .. $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a: $(MAKE) -C $(COMBBLAS)/graph500-1.2/generator mmio.o: ../mmio.c ../mmio.h cc $(FLAGS) $(INCADD) -c -o mmio.o ../mmio.c CommGrid.o: ../CommGrid.cpp ../CommGrid.h $(COMPILER) $(FLAGS) -c -o CommGrid.o ../CommGrid.cpp MPIType.o: ../MPIType.cpp ../MPIType.h $(COMPILER) $(FLAGS) -c -o MPIType.o ../MPIType.cpp MemoryPool.o: ../MemoryPool.cpp ../SpDefs.h $(COMPILER) $(FLAGS) -c -o MemoryPool.o ../MemoryPool.cpp TransposeTest.o: TransposeTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o TransposeTest.o TransposeTest.cpp FindSparse.o: FindSparse.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o FindSparse.o FindSparse.cpp MultTest.o: MultTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o MultTest.o MultTest.cpp IndexingTest.o: IndexingTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o IndexingTest.o IndexingTest.cpp SpAsgnTest.o: SpAsgnTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o SpAsgnTest.o SpAsgnTest.cpp SpAsgnTiming.o: SpAsgnTiming.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o SpAsgnTiming.o SpAsgnTiming.cpp IndexingTiming.o: IndexingTiming.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h ../SpParVec.cpp $(COMPILER) $(FLAGS) -c -o IndexingTiming.o IndexingTiming.cpp MultTiming.o: MultTiming.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o MultTiming.o MultTiming.cpp Galerkin.o: Galerkin.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o Galerkin.o Galerkin.cpp GalerkinNew.o: GalerkinNew.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o GalerkinNew.o GalerkinNew.cpp VectorIO.o: VectorIOPermute.cpp ../FullyDistSpVec.cpp ../FullyDistVec.cpp ../FullyDistSpVec.h ../FullyDistVec.h $(COMPILER) $(FLAGS) -c -o VectorIO.o VectorIO.cpp ParIOTest.o: ParIOTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h ../SpParHelper.cpp $(COMPILER) $(FLAGS) -c -o ParIOTest.o ParIOTest.cpp ReduceTest.o: ReduceTest.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o ReduceTest.o ReduceTest.cpp Roofline.o: Roofline.cpp ../SpDCCols.cpp ../dcsc.cpp ../SpHelper.h ../SpParHelper.h ../SpParMat.cpp ../Friends.h ../ParFriends.h $(COMPILER) $(FLAGS) -c -o Roofline.o Roofline.cpp TransposeTest: MemoryPool.o CommGrid.o MPIType.o TransposeTest.o $(COMPILER) $(FLAGS) -o TransposeTest TransposeTest.o MemoryPool.o CommGrid.o MPIType.o $(LINK) FindSparse: MemoryPool.o CommGrid.o MPIType.o FindSparse.o $(COMPILER) $(FLAGS) -o FindSparse FindSparse.o MemoryPool.o CommGrid.o MPIType.o $(LINK) MultTest: MemoryPool.o CommGrid.o MPIType.o MultTest.o $(COMPILER) $(FLAGS) -o MultTest MultTest.o MemoryPool.o CommGrid.o MPIType.o $(LINK) IndexingTest: MemoryPool.o CommGrid.o MPIType.o IndexingTest.o $(COMPILER) $(FLAGS) -o IndexingTest IndexingTest.o MemoryPool.o CommGrid.o MPIType.o $(LINK) SpAsgnTest: MemoryPool.o CommGrid.o MPIType.o SpAsgnTest.o $(COMPILER) $(FLAGS) -o SpAsgnTest SpAsgnTest.o MemoryPool.o CommGrid.o MPIType.o $(LINK) SpAsgnTiming: MemoryPool.o CommGrid.o MPIType.o SpAsgnTiming.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(FLAGS) -o SpAsgnTiming SpAsgnTiming.o MemoryPool.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq $(LINK) IndexingTiming: MemoryPool.o CommGrid.o MPIType.o IndexingTiming.o $(COMBBLAS)/graph500-1.2/generator/libgraph_generator_seq.a $(COMPILER) $(FLAGS) -o IndexingTiming IndexingTiming.o MemoryPool.o CommGrid.o MPIType.o -L$(COMBBLAS)/graph500-1.2/generator -lgraph_generator_seq $(LINK) MultTime: MemoryPool.o CommGrid.o MPIType.o MultTiming.o $(COMPILER) $(FLAGS) -o MultTime MultTiming.o MemoryPool.o CommGrid.o MPIType.o $(LINK) Galerkin: MemoryPool.o CommGrid.o MPIType.o Galerkin.o $(COMPILER) $(FLAGS) -o Galerkin Galerkin.o MemoryPool.o CommGrid.o MPIType.o $(LINK) GalerkinNew: MemoryPool.o CommGrid.o MPIType.o GalerkinNew.o $(COMPILER) $(FLAGS) -o GalerkinNew GalerkinNew.o MemoryPool.o CommGrid.o MPIType.o $(LINK) VectorIO: MemoryPool.o CommGrid.o MPIType.o VectorIO.o mmio.o $(COMPILER) $(FLAGS) -o VectorIO VectorIO.o MemoryPool.o CommGrid.o MPIType.o mmio.o $(LINK) ReduceTest: MemoryPool.o CommGrid.o MPIType.o ReduceTest.o $(COMPILER) $(FLAGS) -o ReduceTest ReduceTest.o MemoryPool.o CommGrid.o MPIType.o $(LINK) Roofline: MemoryPool.o CommGrid.o MPIType.o Roofline.o $(COMPILER) $(FLAGS) -o Roofline Roofline.o MemoryPool.o CommGrid.o MPIType.o $(LINK) ParIOMM: MemoryPool.o CommGrid.o MPIType.o ParIOTest.o mmio.o $(COMPILER) $(FLAGS) -o ParIOMM ParIOTest.o MemoryPool.o CommGrid.o MPIType.o mmio.o $(LINK) clean: rm -f IndexingTiming rm -f IndexingTest rm -f SpAsgnTest rm -f FindSparse rm -f TransposeTest rm -f MultTest rm -f MultTime rm -f ReduceTest rm -f Galerkin rm -f Roofline rm -f VectorIO rm -f ParIOMM rm -f *.o rm -f ../graph500-1.2/generator/*.o rm -f ../graph500-1.2/generator/libgraph_generator_seq.a cleanout: rm out.* rm err.* CombBLAS_beta_16_2/ReleaseTests/VectorIndexing.cpp000644 000765 000024 00000004310 13271404146 023463 0ustar00aydinbulucstaff000000 000000 #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 4) { if(myrank == 0) { cout << "Usage: ./VectorIndexing " << endl; cout << "Example: ./VectorIndexing ../TESTDATA sp10outta100.txt sp30outta100.txt" << endl; cout << "Input files should be under in tuples format" << endl; } MPI_Finalize(); return -1; } { string directory(argv[1]); string vec1name(argv[2]); string vec2name(argv[3]); vec1name = directory+"/"+vec1name; vec2name = directory+"/"+vec2name; ifstream inputvec1(vec1name.c_str()); ifstream inputvec2(vec2name.c_str()); if(myrank == 0) { if(inputvec1.fail() || inputvec2.fail()) { cout << "One of the input vector files do not exist, aborting" << endl; MPI_Abort(MPI_COMM_WORLD, NOFILE); return -1; } } MPI_Barrier(MPI_COMM_WORLD); FullyDistSpVec vec1, vec2; vec1.ReadDistribute(inputvec1, 0); vec2.ReadDistribute(inputvec2, 0); vec1.PrintInfo("vec1"); vec1.DebugPrint(); vec2.PrintInfo("vec2"); vec2.DebugPrint(); FullyDistVec dvec; dvec.iota(100, 1001); // with 100 entries, first being 1001 dvec.PrintInfo("dvec"); dvec.DebugPrint(); auto subvec1 = dvec(vec1); subvec1.DebugPrint(); auto subvec2 = dvec(vec2); subvec2.DebugPrint(); FullyDistSpVec vecA(12); for(int i=0; i<12; i+=3) vecA.SetElement(i,i); MPI_Barrier(MPI_COMM_WORLD); vecA.DebugPrint(); FullyDistVec dvecA; dvecA.iota(12, 0); // with 12 entries, first being 0 auto subvecA = dvecA(vecA); subvecA.DebugPrint(); inputvec1.clear(); inputvec1.close(); inputvec2.clear(); inputvec2.close(); } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ReleaseTests/GalerkinNew.cpp000755 000765 000024 00000013045 13271404146 022751 0ustar00aydinbulucstaff000000 000000 /****************************************************************/ /* Parallel Combinatorial BLAS Library (for Graph Computations) */ /* version 1.5 -------------------------------------------------*/ /* date: 10/09/2015 ---------------------------------------------*/ /* authors: Ariful Azad, Aydin Buluc, Adam Lugowski ------------*/ /****************************************************************/ /* Copyright (c) 2010-2015, The Regents of the University of California 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. */ #include #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; #define ITERATIONS 10 // Simple helper class for declarations: Just the numerical type is templated // The index type and the sequential matrix type stays the same for the whole code // In this case, they are "int" and "SpDCCols" template class PSpMat { public: typedef SpDCCols < int, NT > DCCols; typedef SpParMat < int, NT, DCCols > MPI_DCCols; }; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 5) { if(myrank == 0) { cout << "Usage: ./GalerkinNew " << endl; cout << " are absolute addresses, and files should be in triples format" << endl; cout << "Example: ./GalerkinNew TESTDATA/grid3d_k5.txt TESTDATA/offdiag_grid3d_k5.txt TESTDATA/diag_grid3d_k5.txt TESTDATA/restrict_T_grid3d_k5.txt" << endl; } MPI_Finalize(); return -1; } { string Aname(argv[1]); string Aoffd(argv[2]); string Adiag(argv[3]); string Tname(argv[4]); // A = L+D // A*T = L*T + D*T; // S*(A*T) = S*L*T + S*D*T; ifstream inputD(Adiag.c_str()); MPI_Barrier(MPI_COMM_WORLD); typedef PlusTimesSRing PTDD; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); PSpMat::MPI_DCCols A(fullWorld); // construct objects PSpMat::MPI_DCCols L(fullWorld); PSpMat::MPI_DCCols T(fullWorld); FullyDistVec dvec(fullWorld); // For matrices, passing the file names as opposed to fstream objects A.ReadDistribute(Aname, 0); L.ReadDistribute(Aoffd, 0); T.ReadDistribute(Tname, 0); dvec.ReadDistribute(inputD,0); SpParHelper::Print("Data read\n"); PSpMat::MPI_DCCols S = T; S.Transpose(); // force the calling of C's destructor; warm up instruction cache - also check correctness { PSpMat::MPI_DCCols AT = PSpGEMM(A, T); PSpMat::MPI_DCCols SAT = PSpGEMM(S, AT); PSpMat::MPI_DCCols LT = PSpGEMM(L, T); PSpMat::MPI_DCCols SLT = PSpGEMM(S, LT); PSpMat::MPI_DCCols SD = S; SD.DimApply(Column, dvec, multiplies()); // scale columns of S to get SD PSpMat::MPI_DCCols SDT = PSpGEMM(SD, T); SLT += SDT; // now this is SAT if(SLT == SAT) { SpParHelper::Print("Splitting approach is correct\n"); } else { SpParHelper::Print("Error in splitting, go fix it\n"); SLT.PrintInfo(); SAT.PrintInfo(); //SLT.SaveGathered("SLT.txt"); //SAT.SaveGathered("SAT.txt"); } } MPI_Barrier(MPI_COMM_WORLD); double t1 = MPI_Wtime(); // initilize (wall-clock) timer for(int i=0; i::MPI_DCCols AT = PSpGEMM(A, T); PSpMat::MPI_DCCols SAT = PSpGEMM(S, AT); } MPI_Barrier(MPI_COMM_WORLD); double t2 = MPI_Wtime(); if(myrank == 0) { cout<<"Full restriction (without splitting) finished"<::MPI_DCCols LT = PSpGEMM(L, T); PSpMat::MPI_DCCols SLT = PSpGEMM(S, LT); PSpMat::MPI_DCCols SD = S; SD.DimApply(Column, dvec, multiplies()); // scale columns of S to get SD PSpMat::MPI_DCCols SDT = PSpGEMM(SD, T); SLT += SDT; } MPI_Barrier(MPI_COMM_WORLD); t2 = MPI_Wtime(); if(myrank == 0) { cout<<"Full restriction (with splitting) finished"< #include #include #include #include #include #include #include "CombBLAS/CombBLAS.h" using namespace std; using namespace combblas; int main(int argc, char* argv[]) { int nprocs, myrank; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD,&nprocs); MPI_Comm_rank(MPI_COMM_WORLD,&myrank); if(argc < 4) { if(myrank == 0) { cout << "Usage: ./TransposeTest " << endl; cout << "Input file and should be under in triples format" << endl; } MPI_Finalize(); return -1; } { string directory(argv[1]); string normalname(argv[2]); string transname(argv[3]); normalname = directory+"/"+normalname; transname = directory+"/"+transname; typedef SpParMat > PARBOOLMAT; shared_ptr fullWorld; fullWorld.reset( new CommGrid(MPI_COMM_WORLD, 0, 0) ); PARBOOLMAT A(fullWorld); PARBOOLMAT AT(fullWorld); PARBOOLMAT ATControl(fullWorld); A.ReadDistribute(normalname, 0); // read it from file, note that we use the transpose of "input" data AT = A; AT.Transpose(); ATControl.ReadDistribute(transname, 0); if (ATControl == AT) { SpParHelper::Print("Transpose working correctly\n"); } else { SpParHelper::Print("ERROR in transpose, go fix it!\n"); } } MPI_Finalize(); return 0; } CombBLAS_beta_16_2/ms_sys/sys/000755 000765 000024 00000000000 13212627400 017554 5ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/ms_sys/sys/time.h000644 000765 000024 00000000001 13212627400 020652 0ustar00aydinbulucstaff000000 000000 CombBLAS_beta_16_2/ms_sys/sys/mta_task.h000644 000765 000024 00000000001 13212627400 021517 0ustar00aydinbulucstaff000000 000000