diff --git a/ICCAD16_openram_paper/.gitignore b/ICCAD16_openram_paper/.gitignore new file mode 100644 index 00000000..566fccac --- /dev/null +++ b/ICCAD16_openram_paper/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +*.bak +openram.pdf +main.aux +main.bbl +main.blg +main.log +main.out +main.pdf +main.synctex.gz diff --git a/ICCAD16_openram_paper/PID4416171.pdf b/ICCAD16_openram_paper/PID4416171.pdf new file mode 100644 index 00000000..5951ed26 Binary files /dev/null and b/ICCAD16_openram_paper/PID4416171.pdf differ diff --git a/ICCAD16_openram_paper/README b/ICCAD16_openram_paper/README new file mode 100644 index 00000000..26e94748 --- /dev/null +++ b/ICCAD16_openram_paper/README @@ -0,0 +1,11 @@ +#To create a PDF version of the paper, named: openram.pdf, +#use this command: ./t.sh +# +#This script is used to correctly setup the references/bib +#for the paper and uses pdflatex to create the PDF. +# +#DEBUGGING: +#If the generation of the PDF failed, read the error messages +#and line numbers and use these keyboard commands to get out +#of the current error messages: Ctrl+D + diff --git a/ICCAD16_openram_paper/abstract.tex b/ICCAD16_openram_paper/abstract.tex new file mode 100644 index 00000000..1dfc5a28 --- /dev/null +++ b/ICCAD16_openram_paper/abstract.tex @@ -0,0 +1,14 @@ +\begin{abstract} +Computer systems research is often inhibited by the availability of +memory designs. Existing Process Design Kits (PDKs) frequently lack +memory compilers, while expensive commercial solutions only provide +memory models with immutable cells, limited configurations, and +restrictive licenses. Manually creating memories can be time consuming +and tedious and the designs are usually inflexible. This paper +introduces OpenRAM, an open-source memory compiler, that provides a +platform for the generation, characterization, and verification of +fabricable memory designs across various technologies, sizes, and +configurations. It enables research in computer architecture, +system-on-chip design, memory circuit and device research, and +computer-\allowbreak aided design. +\end{abstract} diff --git a/ICCAD16_openram_paper/acknowledgments.tex b/ICCAD16_openram_paper/acknowledgments.tex new file mode 100644 index 00000000..ae9e22e8 --- /dev/null +++ b/ICCAD16_openram_paper/acknowledgments.tex @@ -0,0 +1,7 @@ +\section{Acknowledgments} +\label{sec:acknowledgements} +This material is based upon work supported by the National Science +Foundation under Grant No. CNS-1205685 and CNS-1205493. Many students +have contributed to the project throughout their studies including +Jeff Butera, Tom Golubev, Seokjoong Kim, Matthew Gaalswyk, and Son +Bui. diff --git a/ICCAD16_openram_paper/acm_template/sig-alternate.cls b/ICCAD16_openram_paper/acm_template/sig-alternate.cls new file mode 100644 index 00000000..5ff4b1cd --- /dev/null +++ b/ICCAD16_openram_paper/acm_template/sig-alternate.cls @@ -0,0 +1,1650 @@ +% SIG-ALTERNATE.CLS - VERSION 2.5 +% "COMPATIBLE" WITH THE "ACM_PROC_ARTICLE-SP.CLS" V3.2SP +% Gerald Murray - May 23rd 2012 +% +% ---- Start of 'updates' ---- +% Changed $10 fee to $15 -- May 2012 -- Gerry +% Changed $5 fee to $10 -- April 2009 -- Gerry +% April 22nd. 2009 - Fixed 'Natbib' incompatibility problem - Gerry +% April 22nd. 2009 - Fixed 'Babel' incompatibility problem - Gerry +% April 22nd. 2009 - Inserted various bug-fixes and improvements - Gerry +% +% To produce Type 1 fonts in the document plus allow for 'normal LaTeX accenting' in the critical areas; +% title, author block, section-heads, confname, etc. etc. +% i.e. the whole purpose of this version update is to NOT resort to 'inelegant accent patches'. +% After much research, three extra .sty packages were added to the the tail (ae, aecompl, aeguill) to solve, +% in particular, the accenting problem(s). We _could_ ask authors (via instructions/sample file) to 'include' these in +% the source .tex file - in the preamble - but if everything is already provided ('behind the scenes' - embedded IN the .cls) +% then this is less work for authors and also makes everything appear 'vanilla'. +% NOTE: all 'patchwork accenting" has been commented out (here) and is no longer 'used' in the sample .tex file (either). +% Gerry June 2007 +% +% Patch for accenting in conference name/location. Gerry May 3rd. 2007 +% Rule widths changed to .5, author count (>6) fixed, roll-back for Type 3 problem. Gerry March 20th. 2007 +% Changes made to 'modernize' the fontnames but esp. for MikTeX users V2.4/2.5 - Nov. 30th. 2006 +% Updated the \email definition to allow for its use inside of 'shared affiliations' - Nov. 30th. 2006 +% Fixed the 'section number depth value' - Nov. 30th. 2006 +% +% Footnotes inside table cells using \minipage (Oct. 2002) +% Georgia fixed bug in sub-sub-section numbering in paragraphs (July 29th. 2002) +% JS/GM fix to vertical spacing before Proofs (July 30th. 2002) +% +% Made the Permission Statement / Conference Info / Copyright Info +% 'user definable' in the source .tex file OR automatic if +% not specified. +% +% Allowance made to switch default fonts between those systems using +% normal/modern font names and those using 'Type 1' or 'Truetype' fonts. +% See LINE NUMBER 255 for details. +% Also provided for enumerated/annotated Corollaries 'surrounded' by +% enumerated Theorems (line 848). +% Gerry November 11th. 1999 +% +% ---- End of 'updates' ---- +% +\def\fileversion{v2.5} % for ACM's tracking purposes +\def\filedate{May 23, 2012} % Gerry Murray's tracking data +\def\docdate {Wednesday 23rd. May 2012} % Gerry Murray (with deltas to doc} +\usepackage{graphicx} +\usepackage{epsfig} +\usepackage{amssymb} +\usepackage{amsmath} +\usepackage{amsfonts} +% Need this for accents in Arial/Helvetica +%\usepackage[T1]{fontenc} % Gerry March 12, 2007 - causes Type 3 problems (body text) +%\usepackage{textcomp} +% +% SIG-ALTERNATE DOCUMENT STYLE +% G.K.M. Tobin August-October 1999 +% adapted from ARTICLE document style by Ken Traub, Olin Shivers +% also using elements of esub2acm.cls +% HEAVILY MODIFIED, SUBSEQUENTLY, BY GERRY MURRAY 2000 +% ARTICLE DOCUMENT STYLE -- Released 16 March 1988 +% for LaTeX version 2.09 +% Copyright (C) 1988 by Leslie Lamport +% +% +%%% sig-alternate.cls is an 'ALTERNATE' document style for producing +%%% two-column camera-ready pages for ACM conferences. +%%% THIS FILE DOES NOT STRICTLY ADHERE TO THE SIGS (BOARD-ENDORSED) +%%% PROCEEDINGS STYLE. It has been designed to produce a 'tighter' +%%% paper in response to concerns over page budgets. +%%% The main features of this style are: +%%% +%%% 1) Two columns. +%%% 2) Side and top margins of 4.5pc, bottom margin of 6pc, column gutter of +%%% 2pc, hence columns are 20pc wide and 55.5pc tall. (6pc =3D 1in, approx) +%%% 3) First page has title information, and an extra 6pc of space at the +%%% bottom of the first column for the ACM copyright notice. +%%% 4) Text is 9pt on 10pt baselines; titles (except main) are 9pt bold. +%%% +%%% +%%% There are a few restrictions you must observe: +%%% +%%% 1) You cannot change the font size; ACM wants you to use 9pt. +%%% 3) You must start your paper with the \maketitle command. Prior to the +%%% \maketitle you must have \title and \author commands. If you have a +%%% \date command it will be ignored; no date appears on the paper, since +%%% the proceedings will have a date on the front cover. +%%% 4) Marginal paragraphs, tables of contents, lists of figures and tables, +%%% and page headings are all forbidden. +%%% 5) The `figure' environment will produce a figure one column wide; if you +%%% want one that is two columns wide, use `figure*'. +%%% +% +%%% Copyright Space: +%%% This style automatically reserves 1" blank space at the bottom of page 1/ +%%% column 1. This space can optionally be filled with some text using the +%%% \toappear{...} command. If used, this command must be BEFORE the \maketitle +%%% command. If this command is defined AND [preprint] is on, then the +%%% space is filled with the {...} text (at the bottom); otherwise, it is +%%% blank. If you use \toappearbox{...} instead of \toappear{...} then a +%%% box will be drawn around the text (if [preprint] is on). +%%% +%%% A typical usage looks like this: +%%% \toappear{To appear in the Ninth AES Conference on Medievil Lithuanian +%%% Embalming Technique, June 1991, Alfaretta, Georgia.} +%%% This will be included in the preprint, and left out of the conference +%%% version. +%%% +%%% WARNING: +%%% Some dvi-ps converters heuristically allow chars to drift from their +%%% true positions a few pixels. This may be noticeable with the 9pt sans-serif +%%% bold font used for section headers. +%%% You may turn this hackery off via the -e option: +%%% dvips -e 0 foo.dvi >foo.ps +%%% +\typeout{Document Class 'sig-alternate' <23rd. May '12>. Modified by G.K.M. Tobin/Gerry Murray} +\typeout{Based in part upon document Style `acmconf' <22 May 89>. Hacked 4/91 by} +\typeout{shivers@cs.cmu.edu, 4/93 by theobald@cs.mcgill.ca} +\typeout{Excerpts were taken from (Journal Style) 'esub2acm.cls'.} +\typeout{****** Bugs/comments/suggestions/technicalities to Gerry Murray -- murray@hq.acm.org ******} +\typeout{Questions on the style, SIGS policies, etc. to Adrienne Griscti griscti@acm.org} +\oddsidemargin 4.5pc +\evensidemargin 4.5pc +\advance\oddsidemargin by -1in % Correct for LaTeX gratuitousness +\advance\evensidemargin by -1in % Correct for LaTeX gratuitousness +\marginparwidth 0pt % Margin pars are not allowed. +\marginparsep 11pt % Horizontal space between outer margin and + % marginal note + + % Top of page: +\topmargin 4.5pc % Nominal distance from top of page to top of + % box containing running head. +\advance\topmargin by -1in % Correct for LaTeX gratuitousness +\headheight 0pt % Height of box containing running head. +\headsep 0pt % Space between running head and text. + % Bottom of page: +\footskip 30pt % Distance from baseline of box containing foot + % to baseline of last line of text. +\@ifundefined{footheight}{\newdimen\footheight}{}% this is for LaTeX2e +\footheight 12pt % Height of box containing running foot. + +%% Must redefine the top margin so there's room for headers and +%% page numbers if you are using the preprint option. Footers +%% are OK as is. Olin. +\advance\topmargin by -37pt % Leave 37pt above text for headers +\headheight 12pt % Height of box containing running head. +\headsep 25pt % Space between running head and text. + +\textheight 666pt % 9 1/4 column height +\textwidth 42pc % Width of text line. + % For two-column mode: +\columnsep 2pc % Space between columns +\columnseprule 0pt % Width of rule between columns. +\hfuzz 1pt % Allow some variation in column width, otherwise it's + % too hard to typeset in narrow columns. + +\footnotesep 5.6pt % Height of strut placed at the beginning of every + % footnote =3D height of normal \footnotesize strut, + % so no extra space between footnotes. + +\skip\footins 8.1pt plus 4pt minus 2pt % Space between last line of text and + % top of first footnote. +\floatsep 11pt plus 2pt minus 2pt % Space between adjacent floats moved + % to top or bottom of text page. +\textfloatsep 18pt plus 2pt minus 4pt % Space between main text and floats + % at top or bottom of page. +\intextsep 11pt plus 2pt minus 2pt % Space between in-text figures and + % text. +\@ifundefined{@maxsep}{\newdimen\@maxsep}{}% this is for LaTeX2e +\@maxsep 18pt % The maximum of \floatsep, + % \textfloatsep and \intextsep (minus + % the stretch and shrink). +\dblfloatsep 11pt plus 2pt minus 2pt % Same as \floatsep for double-column + % figures in two-column mode. +\dbltextfloatsep 18pt plus 2pt minus 4pt% \textfloatsep for double-column + % floats. +\@ifundefined{@dblmaxsep}{\newdimen\@dblmaxsep}{}% this is for LaTeX2e +\@dblmaxsep 18pt % The maximum of \dblfloatsep and + % \dbltexfloatsep. +\@fptop 0pt plus 1fil % Stretch at top of float page/column. (Must be + % 0pt plus ...) +\@fpsep 8pt plus 2fil % Space between floats on float page/column. +\@fpbot 0pt plus 1fil % Stretch at bottom of float page/column. (Must be + % 0pt plus ... ) +\@dblfptop 0pt plus 1fil % Stretch at top of float page. (Must be 0pt plus ...) +\@dblfpsep 8pt plus 2fil % Space between floats on float page. +\@dblfpbot 0pt plus 1fil % Stretch at bottom of float page. (Must be + % 0pt plus ... ) +\marginparpush 5pt % Minimum vertical separation between two marginal + % notes. + +\parskip 0pt plus 1pt % Extra vertical space between paragraphs. +\parindent 9pt % GM July 2000 / was 0pt - width of paragraph indentation. +\partopsep 2pt plus 1pt minus 1pt% Extra vertical space, in addition to + % \parskip and \topsep, added when user + % leaves blank line before environment. + +\@lowpenalty 51 % Produced by \nopagebreak[1] or \nolinebreak[1] +\@medpenalty 151 % Produced by \nopagebreak[2] or \nolinebreak[2] +\@highpenalty 301 % Produced by \nopagebreak[3] or \nolinebreak[3] + +\@beginparpenalty -\@lowpenalty % Before a list or paragraph environment. +\@endparpenalty -\@lowpenalty % After a list or paragraph environment. +\@itempenalty -\@lowpenalty % Between list items. + +%\@namedef{ds@10pt}{\@latexerr{The `10pt' option is not allowed in the `acmconf' +\@namedef{ds@10pt}{\ClassError{The `10pt' option is not allowed in the `acmconf' % January 2008 + document style.}\@eha} +%\@namedef{ds@11pt}{\@latexerr{The `11pt' option is not allowed in the `acmconf' +\@namedef{ds@11pt}{\ClassError{The `11pt' option is not allowed in the `acmconf' % January 2008 + document style.}\@eha} +%\@namedef{ds@12pt}{\@latexerr{The `12pt' option is not allowed in the `acmconf' +\@namedef{ds@12pt}{\ClassError{The `12pt' option is not allowed in the `acmconf' % January 2008 + document style.}\@eha} + +\@options + +\lineskip 2pt % \lineskip is 1pt for all font sizes. +\normallineskip 2pt +\def\baselinestretch{1} + +\abovedisplayskip 9pt plus2pt minus4.5pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus3pt% +\belowdisplayshortskip 5.4pt plus3pt minus3pt% +\let\@listi\@listI % Setting of \@listi added 9 Jun 87 + +\def\small{\@setsize\small{9pt}\viiipt\@viiipt +\abovedisplayskip 7.6pt plus 3pt minus 4pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus2pt% +\belowdisplayshortskip 3.6pt plus2pt minus 2pt +\def\@listi{\leftmargin\leftmargini %% Added 22 Dec 87 +\topsep 4pt plus 2pt minus 2pt\parsep 2pt plus 1pt minus 1pt +\itemsep \parsep}} + +\def\footnotesize{\@setsize\footnotesize{9pt}\ixpt\@ixpt +\abovedisplayskip 6.4pt plus 2pt minus 4pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus 1pt% +\belowdisplayshortskip 2.7pt plus 1pt minus 2pt +\def\@listi{\leftmargin\leftmargini %% Added 22 Dec 87 +\topsep 3pt plus 1pt minus 1pt\parsep 2pt plus 1pt minus 1pt +\itemsep \parsep}} + +\newcount\aucount +\newcount\originalaucount +\newdimen\auwidth +\auwidth=\textwidth +\newdimen\auskip +\newcount\auskipcount +\newdimen\auskip +\global\auskip=1pc +\newdimen\allauboxes +\allauboxes=\auwidth +\newtoks\addauthors +\newcount\addauflag +\global\addauflag=0 %Haven't shown additional authors yet + +\newtoks\subtitletext +\gdef\subtitle#1{\subtitletext={#1}} + +\gdef\additionalauthors#1{\addauthors={#1}} + +\gdef\numberofauthors#1{\global\aucount=#1 +\ifnum\aucount>3\global\originalaucount=\aucount \global\aucount=3\fi %g} % 3 OK - Gerry March 2007 +\global\auskipcount=\aucount\global\advance\auskipcount by 1 +\global\multiply\auskipcount by 2 +\global\multiply\auskip by \auskipcount +\global\advance\auwidth by -\auskip +\global\divide\auwidth by \aucount} + +% \and was modified to count the number of authors. GKMT 12 Aug 1999 +\def\alignauthor{% % \begin{tabular} +\end{tabular}% + \begin{tabular}[t]{p{\auwidth}}\centering}% + +% *** NOTE *** NOTE *** NOTE *** NOTE *** +% If you have 'font problems' then you may need +% to change these, e.g. 'arialb' instead of "arialbd". +% Gerry Murray 11/11/1999 +% *** OR ** comment out block A and activate block B or vice versa. +% ********************************************** +% +% -- Start of block A -- (Type 1 or Truetype fonts) +%\newfont{\secfnt}{timesbd at 12pt} % was timenrb originally - now is timesbd +%\newfont{\secit}{timesbi at 12pt} %13 Jan 00 gkmt +%\newfont{\subsecfnt}{timesi at 11pt} % was timenrri originally - now is timesi +%\newfont{\subsecit}{timesbi at 11pt} % 13 Jan 00 gkmt -- was times changed to timesbi gm 2/4/2000 +% % because "normal" is italic, "italic" is Roman +%\newfont{\ttlfnt}{arialbd at 18pt} % was arialb originally - now is arialbd +%\newfont{\ttlit}{arialbi at 18pt} % 13 Jan 00 gkmt +%\newfont{\subttlfnt}{arial at 14pt} % was arialr originally - now is arial +%\newfont{\subttlit}{ariali at 14pt} % 13 Jan 00 gkmt +%\newfont{\subttlbf}{arialbd at 14pt} % 13 Jan 00 gkmt +%\newfont{\aufnt}{arial at 12pt} % was arialr originally - now is arial +%\newfont{\auit}{ariali at 12pt} % 13 Jan 00 gkmt +%\newfont{\affaddr}{arial at 10pt} % was arialr originally - now is arial +%\newfont{\affaddrit}{ariali at 10pt} %13 Jan 00 gkmt +%\newfont{\eaddfnt}{arial at 12pt} % was arialr originally - now is arial +%\newfont{\ixpt}{times at 9pt} % was timenrr originally - now is times +%\newfont{\confname}{timesi at 8pt} % was timenrri - now is timesi +%\newfont{\crnotice}{times at 8pt} % was timenrr originally - now is times +%\newfont{\ninept}{times at 9pt} % was timenrr originally - now is times + +% ********************************************* +% -- End of block A -- +% +% +% -- Start of block B -- UPDATED FONT NAMES +% ********************************************* +% Gerry Murray 11/30/2006 +% ********************************************* +\newfont{\secfnt}{ptmb8t at 12pt} +\newfont{\secit}{ptmbi8t at 12pt} %13 Jan 00 gkmt +\newfont{\subsecfnt}{ptmri8t at 11pt} +\newfont{\subsecit}{ptmbi8t at 11pt} % +\newfont{\ttlfnt}{phvb8t at 18pt} +\newfont{\ttlit}{phvbo8t at 18pt} % GM 2/4/2000 +\newfont{\subttlfnt}{phvr8t at 14pt} +\newfont{\subttlit}{phvro8t at 14pt} % GM 2/4/2000 +\newfont{\subttlbf}{phvb8t at 14pt} % 13 Jan 00 gkmt +\newfont{\aufnt}{phvr8t at 12pt} +\newfont{\auit}{phvro8t at 12pt} % GM 2/4/2000 +\newfont{\affaddr}{phvr8t at 10pt} +\newfont{\affaddrit}{phvro8t at 10pt} % GM 2/4/2000 +\newfont{\eaddfnt}{phvr8t at 12pt} +\newfont{\ixpt}{ptmr8t at 9pt} +\newfont{\confname}{ptmri8t at 8pt} +\newfont{\crnotice}{ptmr8t at 8pt} +\newfont{\ninept}{ptmr8t at 9pt} +% +++++++++++++++++++++++++++++++++++++++++++++ +% -- End of block B -- + +%\def\email#1{{{\eaddfnt{\vskip 4pt#1}}}} +% If we have an email, inside a "shared affiliation" then we need the following instead +\def\email#1{{{\eaddfnt{\par #1}}}} % revised - GM - 11/30/2006 + +\def\addauthorsection{\ifnum\originalaucount>6 % was 3 - Gerry March 2007 + \section{Additional Authors}\the\addauthors + \fi} + +\newcount\savesection +\newcount\sectioncntr +\global\sectioncntr=1 + +\setcounter{secnumdepth}{3} + +\def\appendix{\par +\section*{APPENDIX} +\setcounter{section}{0} + \setcounter{subsection}{0} + \def\thesection{\Alph{section}} } + +\leftmargini 22.5pt +\leftmarginii 19.8pt % > \labelsep + width of '(m)' +\leftmarginiii 16.8pt % > \labelsep + width of 'vii.' +\leftmarginiv 15.3pt % > \labelsep + width of 'M.' +\leftmarginv 9pt +\leftmarginvi 9pt + +\leftmargin\leftmargini +\labelsep 4.5pt +\labelwidth\leftmargini\advance\labelwidth-\labelsep + +\def\@listI{\leftmargin\leftmargini \parsep 3.6pt plus 2pt minus 1pt% +\topsep 7.2pt plus 2pt minus 4pt% +\itemsep 3.6pt plus 2pt minus 1pt} + +\let\@listi\@listI +\@listi + +\def\@listii{\leftmargin\leftmarginii + \labelwidth\leftmarginii\advance\labelwidth-\labelsep + \topsep 3.6pt plus 2pt minus 1pt + \parsep 1.8pt plus 0.9pt minus 0.9pt + \itemsep \parsep} + +\def\@listiii{\leftmargin\leftmarginiii + \labelwidth\leftmarginiii\advance\labelwidth-\labelsep + \topsep 1.8pt plus 0.9pt minus 0.9pt + \parsep \z@ \partopsep 1pt plus 0pt minus 1pt + \itemsep \topsep} + +\def\@listiv{\leftmargin\leftmarginiv + \labelwidth\leftmarginiv\advance\labelwidth-\labelsep} + +\def\@listv{\leftmargin\leftmarginv + \labelwidth\leftmarginv\advance\labelwidth-\labelsep} + +\def\@listvi{\leftmargin\leftmarginvi + \labelwidth\leftmarginvi\advance\labelwidth-\labelsep} + +\def\labelenumi{\theenumi.} +\def\theenumi{\arabic{enumi}} + +\def\labelenumii{(\theenumii)} +\def\theenumii{\alph{enumii}} +\def\p@enumii{\theenumi} + +\def\labelenumiii{\theenumiii.} +\def\theenumiii{\roman{enumiii}} +\def\p@enumiii{\theenumi(\theenumii)} + +\def\labelenumiv{\theenumiv.} +\def\theenumiv{\Alph{enumiv}} +\def\p@enumiv{\p@enumiii\theenumiii} + +\def\labelitemi{$\bullet$} +\def\labelitemii{\bf --} +\def\labelitemiii{$\ast$} +\def\labelitemiv{$\cdot$} + +\def\verse{\let\\=\@centercr + \list{}{\itemsep\z@ \itemindent -1.5em\listparindent \itemindent + \rightmargin\leftmargin\advance\leftmargin 1.5em}\item[]} +\let\endverse\endlist + +\def\quotation{\list{}{\listparindent 1.5em + \itemindent\listparindent + \rightmargin\leftmargin \parsep 0pt plus 1pt}\item[]} +\let\endquotation=\endlist + +\def\quote{\list{}{\rightmargin\leftmargin}\item[]} +\let\endquote=\endlist + +\def\descriptionlabel#1{\hspace\labelsep \bf #1} +\def\description{\list{}{\labelwidth\z@ \itemindent-\leftmargin + \let\makelabel\descriptionlabel}} + +\let\enddescription\endlist + +\def\theequation{\arabic{equation}} + +\arraycolsep 4.5pt % Half the space between columns in an array environment. +\tabcolsep 5.4pt % Half the space between columns in a tabular environment. +\arrayrulewidth .5pt % Width of rules in array and tabular environment. % (was .4) updated Gerry March 20 2007 +\doublerulesep 1.8pt % Space between adjacent rules in array or tabular env. + +\tabbingsep \labelsep % Space used by the \' command. (See LaTeX manual.) + +\skip\@mpfootins =\skip\footins + +\fboxsep =2.7pt % Space left between box and text by \fbox and \framebox. +\fboxrule =.5pt % Width of rules in box made by \fbox and \framebox. % (was .4) updated Gerry March 20 2007 + +\def\thepart{\Roman{part}} % Roman numeral part numbers. +\def\thesection {\arabic{section}} +\def\thesubsection {\thesection.\arabic{subsection}} +%\def\thesubsubsection {\thesubsection.\arabic{subsubsection}} % GM 7/30/2002 +%\def\theparagraph {\thesubsubsection.\arabic{paragraph}} % GM 7/30/2002 +\def\thesubparagraph {\theparagraph.\arabic{subparagraph}} + +\def\@pnumwidth{1.55em} +\def\@tocrmarg {2.55em} +\def\@dotsep{4.5} +\setcounter{tocdepth}{3} + +%\def\tableofcontents{\@latexerr{\tableofcontents: Tables of contents are not +% allowed in the `acmconf' document style.}\@eha} + +\def\tableofcontents{\ClassError{% + \string\tableofcontents\space is not allowed in the `acmconf' document % January 2008 + style}\@eha} + +\def\l@part#1#2{\addpenalty{\@secpenalty} + \addvspace{2.25em plus 1pt} % space above part line + \begingroup + \@tempdima 3em % width of box holding part number, used by + \parindent \z@ \rightskip \@pnumwidth %% \numberline + \parfillskip -\@pnumwidth + {\large \bf % set line in \large boldface + \leavevmode % TeX command to enter horizontal mode. + #1\hfil \hbox to\@pnumwidth{\hss #2}}\par + \nobreak % Never break after part entry + \endgroup} + +\def\l@section#1#2{\addpenalty{\@secpenalty} % good place for page break + \addvspace{1.0em plus 1pt} % space above toc entry + \@tempdima 1.5em % width of box holding section number + \begingroup + \parindent \z@ \rightskip \@pnumwidth + \parfillskip -\@pnumwidth + \bf % Boldface. + \leavevmode % TeX command to enter horizontal mode. + \advance\leftskip\@tempdima %% added 5 Feb 88 to conform to + \hskip -\leftskip %% 25 Jan 88 change to \numberline + #1\nobreak\hfil \nobreak\hbox to\@pnumwidth{\hss #2}\par + \endgroup} + + +\def\l@subsection{\@dottedtocline{2}{1.5em}{2.3em}} +\def\l@subsubsection{\@dottedtocline{3}{3.8em}{3.2em}} +\def\l@paragraph{\@dottedtocline{4}{7.0em}{4.1em}} +\def\l@subparagraph{\@dottedtocline{5}{10em}{5em}} + +%\def\listoffigures{\@latexerr{\listoffigures: Lists of figures are not +% allowed in the `acmconf' document style.}\@eha} + +\def\listoffigures{\ClassError{% + \string\listoffigures\space is not allowed in the `acmconf' document % January 2008 + style}\@eha} + +\def\l@figure{\@dottedtocline{1}{1.5em}{2.3em}} + +%\def\listoftables{\@latexerr{\listoftables: Lists of tables are not +% allowed in the `acmconf' document style.}\@eha} +%\let\l@table\l@figure + +\def\listoftables{\ClassError{% + \string\listoftables\space is not allowed in the `acmconf' document % January 2008 + style}\@eha} + \let\l@table\l@figure + +\def\footnoterule{\kern-3\p@ + \hrule width .5\columnwidth % (was .4) updated Gerry March 20 2007 + \kern 2.6\p@} % The \hrule has default height of .4pt % (was .4) updated Gerry March 20 2007 +% ------ +\long\def\@makefntext#1{\noindent +%\hbox to .5em{\hss$^{\@thefnmark}$}#1} % original +\hbox to .5em{\hss\textsuperscript{\@thefnmark}}#1} % C. Clifton / GM Oct. 2nd. 2002 +% ------- + +\long\def\@maketntext#1{\noindent +#1} + +\long\def\@maketitlenotetext#1#2{\noindent + \hbox to 1.8em{\hss$^{#1}$}#2} + +\setcounter{topnumber}{2} +\def\topfraction{.7} +\setcounter{bottomnumber}{1} +\def\bottomfraction{.3} +\setcounter{totalnumber}{3} +\def\textfraction{.2} +\def\floatpagefraction{.5} +\setcounter{dbltopnumber}{2} +\def\dbltopfraction{.7} +\def\dblfloatpagefraction{.5} + +% +\long\def\@makecaption#1#2{ + \vskip \baselineskip + \setbox\@tempboxa\hbox{\textbf{#1: #2}} + \ifdim \wd\@tempboxa >\hsize % IF longer than one line: + \textbf{#1: #2}\par % THEN set as ordinary paragraph. + \else % ELSE center. + \hbox to\hsize{\hfil\box\@tempboxa\hfil}\par + \fi} + +% + +\long\def\@makecaption#1#2{ + \vskip 10pt + \setbox\@tempboxa\hbox{\textbf{#1: #2}} + \ifdim \wd\@tempboxa >\hsize % IF longer than one line: + \textbf{#1: #2}\par % THEN set as ordinary paragraph. + \else % ELSE center. + \hbox to\hsize{\hfil\box\@tempboxa\hfil} + \fi} + +\@ifundefined{figure}{\newcounter {figure}} % this is for LaTeX2e + +\def\fps@figure{tbp} +\def\ftype@figure{1} +\def\ext@figure{lof} +\def\fnum@figure{Figure \thefigure} +\def\figure{\@float{figure}} +%\let\endfigure\end@float +\def\endfigure{\end@float} % Gerry January 2008 +\@namedef{figure*}{\@dblfloat{figure}} +\@namedef{endfigure*}{\end@dblfloat} + +\@ifundefined{table}{\newcounter {table}} % this is for LaTeX2e + +\def\fps@table{tbp} +\def\ftype@table{2} +\def\ext@table{lot} +\def\fnum@table{Table \thetable} +\def\table{\@float{table}} +%\let\endtable\end@float +\def\endtable{\end@float} % Gerry January 2008 +\@namedef{table*}{\@dblfloat{table}} +\@namedef{endtable*}{\end@dblfloat} + +\newtoks\titleboxnotes +\newcount\titleboxnoteflag + +\def\maketitle{\par + \begingroup + \def\thefootnote{\fnsymbol{footnote}} + \def\@makefnmark{\hbox + to 0pt{$^{\@thefnmark}$\hss}} + \twocolumn[\@maketitle] +\@thanks + \endgroup + \setcounter{footnote}{0} + \let\maketitle\relax + \let\@maketitle\relax + \gdef\@thanks{}\gdef\@author{}\gdef\@title{}\gdef\@subtitle{}\let\thanks\relax + \@copyrightspace} + +%% CHANGES ON NEXT LINES +\newif\if@ll % to record which version of LaTeX is in use + +\expandafter\ifx\csname LaTeXe\endcsname\relax % LaTeX2.09 is used +\else% LaTeX2e is used, so set ll to true +\global\@lltrue +\fi + +\if@ll + \NeedsTeXFormat{LaTeX2e} + \ProvidesClass{sig-alternate} [2012/05/23 - V2.5 - based on acmproc.cls V1.3 ] + \RequirePackage{latexsym}% QUERY: are these two really needed? + \let\dooptions\ProcessOptions +\else + \let\dooptions\@options +\fi +%% END CHANGES + +\def\@height{height} +\def\@width{width} +\def\@minus{minus} +\def\@plus{plus} +\def\hb@xt@{\hbox to} +\newif\if@faircopy +\@faircopyfalse +\def\ds@faircopy{\@faircopytrue} + +\def\ds@preprint{\@faircopyfalse} + +\@twosidetrue +\@mparswitchtrue +\def\ds@draft{\overfullrule 5\p@} +%% CHANGE ON NEXT LINE +\dooptions + +\lineskip \p@ +\normallineskip \p@ +\def\baselinestretch{1} +\def\@ptsize{0} %needed for amssymbols.sty + +%% CHANGES ON NEXT LINES +\if@ll% allow use of old-style font change commands in LaTeX2e +\@maxdepth\maxdepth +% +\DeclareOldFontCommand{\rm}{\ninept\rmfamily}{\mathrm} +\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf} +\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt} +\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf} +\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit} +\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl} +\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc} +\DeclareRobustCommand*{\cal}{\@fontswitch{\relax}{\mathcal}} +\DeclareRobustCommand*{\mit}{\@fontswitch{\relax}{\mathnormal}} +\fi +% +\if@ll + \renewcommand{\rmdefault}{cmr} % was 'ttm' +% Note! I have also found 'mvr' to work ESPECIALLY well. +% Gerry - October 1999 +% You may need to change your LV1times.fd file so that sc is +% mapped to cmcsc - -for smallcaps -- that is if you decide +% to change {cmr} to {times} above. (Not recommended) + \renewcommand{\@ptsize}{} + \renewcommand{\normalsize}{% + \@setfontsize\normalsize\@ixpt{10.5\p@}%\ninept% + \abovedisplayskip 6\p@ \@plus2\p@ \@minus\p@ + \belowdisplayskip \abovedisplayskip + \abovedisplayshortskip 6\p@ \@minus 3\p@ + \belowdisplayshortskip 6\p@ \@minus 3\p@ + \let\@listi\@listI + } +\else + \def\@normalsize{%changed next to 9 from 10 + \@setsize\normalsize{9\p@}\ixpt\@ixpt + \abovedisplayskip 6\p@ \@plus2\p@ \@minus\p@ + \belowdisplayskip \abovedisplayskip + \abovedisplayshortskip 6\p@ \@minus 3\p@ + \belowdisplayshortskip 6\p@ \@minus 3\p@ + \let\@listi\@listI + }% +\fi +\if@ll + \newcommand\scriptsize{\@setfontsize\scriptsize\@viipt{8\p@}} + \newcommand\tiny{\@setfontsize\tiny\@vpt{6\p@}} + \newcommand\large{\@setfontsize\large\@xiipt{14\p@}} + \newcommand\Large{\@setfontsize\Large\@xivpt{18\p@}} + \newcommand\LARGE{\@setfontsize\LARGE\@xviipt{20\p@}} + \newcommand\huge{\@setfontsize\huge\@xxpt{25\p@}} + \newcommand\Huge{\@setfontsize\Huge\@xxvpt{30\p@}} +\else + \def\scriptsize{\@setsize\scriptsize{8\p@}\viipt\@viipt} + \def\tiny{\@setsize\tiny{6\p@}\vpt\@vpt} + \def\large{\@setsize\large{14\p@}\xiipt\@xiipt} + \def\Large{\@setsize\Large{18\p@}\xivpt\@xivpt} + \def\LARGE{\@setsize\LARGE{20\p@}\xviipt\@xviipt} + \def\huge{\@setsize\huge{25\p@}\xxpt\@xxpt} + \def\Huge{\@setsize\Huge{30\p@}\xxvpt\@xxvpt} +\fi +\normalsize + +% make aubox hsize/number of authors up to 3, less gutter +% then showbox gutter showbox gutter showbox -- GKMT Aug 99 +\newbox\@acmtitlebox +\def\@maketitle{\newpage + \null + \setbox\@acmtitlebox\vbox{% +\baselineskip 20pt +\vskip 2em % Vertical space above title. + \begin{center} + {\ttlfnt \@title\par} % Title set in 18pt Helvetica (Arial) bold size. + \vskip 1.5em % Vertical space after title. +%This should be the subtitle. +{\subttlfnt \the\subtitletext\par}\vskip 1.25em%\fi + {\baselineskip 16pt\aufnt % each author set in \12 pt Arial, in a + \lineskip .5em % tabular environment + \begin{tabular}[t]{c}\@author + \end{tabular}\par} + \vskip 1.5em % Vertical space after author. + \end{center}} + \dimen0=\ht\@acmtitlebox + \advance\dimen0 by -12.75pc\relax % Increased space for title box -- KBT + \unvbox\@acmtitlebox + \ifdim\dimen0<0.0pt\relax\vskip-\dimen0\fi} + + +\newcount\titlenotecount +\global\titlenotecount=0 +\newtoks\tntoks +\newtoks\tntokstwo +\newtoks\tntoksthree +\newtoks\tntoksfour +\newtoks\tntoksfive + +\def\abstract{ +\ifnum\titlenotecount>0 % was =1 + \insert\footins{% + \reset@font\footnotesize + \interlinepenalty\interfootnotelinepenalty + \splittopskip\footnotesep + \splitmaxdepth \dp\strutbox \floatingpenalty \@MM + \hsize\columnwidth \@parboxrestore + \protected@edef\@currentlabel{% + }% + \color@begingroup +\ifnum\titlenotecount=1 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\@finalstrut\strutbox}% +\fi +\ifnum\titlenotecount=2 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\dagger$}\rule\z@\footnotesep\ignorespaces\the\tntokstwo\@finalstrut\strutbox}% +\fi +\ifnum\titlenotecount=3 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\dagger$}\rule\z@\footnotesep\ignorespaces\the\tntokstwo\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\ddagger$}\rule\z@\footnotesep\ignorespaces\the\tntoksthree\@finalstrut\strutbox}% +\fi +\ifnum\titlenotecount=4 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\dagger$}\rule\z@\footnotesep\ignorespaces\the\tntokstwo\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\ddagger$}\rule\z@\footnotesep\ignorespaces\the\tntoksthree\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\S$}\rule\z@\footnotesep\ignorespaces\the\tntoksfour\@finalstrut\strutbox}% +\fi +\ifnum\titlenotecount=5 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\dagger$}\rule\z@\footnotesep\ignorespaces\the\tntokstwo\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\ddagger$}\rule\z@\footnotesep\ignorespaces\the\tntoksthree\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\S$}\rule\z@\footnotesep\ignorespaces\the\tntoksfour\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\P$}\rule\z@\footnotesep\ignorespaces\the\tntoksfive\@finalstrut\strutbox}% +\fi + \color@endgroup} %g} +\fi +\setcounter{footnote}{0} +\section*{ABSTRACT}\normalsize%\ninept +} + +\def\endabstract{\if@twocolumn\else\endquotation\fi} + +\def\keywords{\if@twocolumn +\section*{Keywords} +\else \small +\quotation +\fi} + +\def\terms{\if@twocolumn +\section*{General Terms} +\else \small +\quotation +\fi} + +% -- Classification needs to be a bit smart due to optionals - Gerry/Georgia November 2nd. 1999 +\newcount\catcount +\global\catcount=1 + +\def\category#1#2#3{% +\ifnum\catcount=1 +\section*{Categories and Subject Descriptors} +\advance\catcount by 1\else{\unskip; }\fi + \@ifnextchar [{\@category{#1}{#2}{#3}}{\@category{#1}{#2}{#3}[]}% +} + +\def\@category#1#2#3[#4]{% + \begingroup + \let\and\relax + #1 [\textbf{#2}]% + \if!#4!% + \if!#3!\else : #3\fi + \else + :\space + \if!#3!\else #3\kern\z@---\hskip\z@\fi + \textit{#4}% + \fi + \endgroup +} +% + +%%% This section (written by KBT) handles the 1" box in the lower left +%%% corner of the left column of the first page by creating a picture, +%%% and inserting the predefined string at the bottom (with a negative +%%% displacement to offset the space allocated for a non-existent +%%% caption). +%%% +\newtoks\copyrightnotice +\def\ftype@copyrightbox{8} +\def\@copyrightspace{ +\@float{copyrightbox}[b] +\begin{center} +\setlength{\unitlength}{1pc} +\begin{picture}(20,6) %Space for copyright notice +\put(0,-0.95){\crnotice{\@toappear}} +\end{picture} +\end{center} +\end@float} + +\def\@toappear{} % Default setting blank - commands below change this. +\long\def\toappear#1{\def\@toappear{\parbox[b]{20pc}{\baselineskip 9pt#1}}} +\def\toappearbox#1{\def\@toappear{\raisebox{5pt}{\framebox[20pc]{\parbox[b]{19pc}{#1}}}}} + +\newtoks\conf +\newtoks\confinfo +\def\conferenceinfo#1#2{\global\conf={#1}\global\confinfo{#2}} + + +%\def\marginpar{\@latexerr{The \marginpar command is not allowed in the +% `acmconf' document style.}\@eha} + +\def\marginpar{\ClassError{% + \string\marginpar\space is not allowed in the `acmconf' document % January 2008 + style}\@eha} + +\mark{{}{}} % Initializes TeX's marks + +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} + +\def\@begintheorem#1#2{% + \parskip 0pt % GM July 2000 (for tighter spacing) + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {{\sc #1}\hskip 5\p@\relax#2.}% + ] + \it +} +\def\@opargbegintheorem#1#2#3{% + \parskip 0pt % GM July 2000 (for tighter spacing) + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {\sc #1\ #2\ % This mod by Gerry to enumerate corollaries + \setbox\@tempboxa\hbox{(#3)} % and bracket the 'corollary title' + \ifdim \wd\@tempboxa>\z@ % and retain the correct numbering of e.g. theorems + \hskip 5\p@\relax % if they occur 'around' said corollaries. + \box\@tempboxa % Gerry - Nov. 1999. + \fi.}% + ] + \it +} +\newif\if@qeded +\global\@qededfalse + +% -- original +%\def\proof{% +% \vspace{-\parskip} % GM July 2000 (for tighter spacing) +% \global\@qededfalse +% \@ifnextchar[{\@xproof}{\@proof}% +%} +% -- end of original + +% (JSS) Fix for vertical spacing bug - Gerry Murray July 30th. 2002 +\def\proof{% +\vspace{-\lastskip}\vspace{-\parsep}\penalty-51% +\global\@qededfalse +\@ifnextchar[{\@xproof}{\@proof}% +} + +\def\endproof{% + \if@qeded\else\qed\fi + \endtrivlist +} +\def\@proof{% + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {\sc Proof.}% + ] + \ignorespaces +} +\def\@xproof[#1]{% + \trivlist + \item[\hskip 10\p@\hskip \labelsep{\sc Proof #1.}]% + \ignorespaces +} +\def\qed{% + \unskip + \kern 10\p@ + \begingroup + \unitlength\p@ + \linethickness{.4\p@}% + \framebox(6,6){}% + \endgroup + \global\@qededtrue +} + +\def\newdef#1#2{% + \expandafter\@ifdefinable\csname #1\endcsname + {\@definecounter{#1}% + \expandafter\xdef\csname the#1\endcsname{\@thmcounter{#1}}% + \global\@namedef{#1}{\@defthm{#1}{#2}}% + \global\@namedef{end#1}{\@endtheorem}% + }% +} +\def\@defthm#1#2{% + \refstepcounter{#1}% + \@ifnextchar[{\@ydefthm{#1}{#2}}{\@xdefthm{#1}{#2}}% +} +\def\@xdefthm#1#2{% + \@begindef{#2}{\csname the#1\endcsname}% + \ignorespaces +} +\def\@ydefthm#1#2[#3]{% + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {\it #2% +% \savebox\@tempboxa{#3}% + \saveb@x\@tempboxa{#3}% % January 2008 + \ifdim \wd\@tempboxa>\z@ + \ \box\@tempboxa + \fi.% + }]% + \ignorespaces +} +\def\@begindef#1#2{% + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {\it #1\ \rm #2.}% + ]% +} +\def\theequation{\arabic{equation}} + +\newcounter{part} +\newcounter{section} +\newcounter{subsection}[section] +\newcounter{subsubsection}[subsection] +\newcounter{paragraph}[subsubsection] +\def\thepart{\Roman{part}} +\def\thesection{\arabic{section}} +\def\thesubsection{\thesection.\arabic{subsection}} +\def\thesubsubsection{\thesubsection.\arabic{subsubsection}} %removed \subsecfnt 29 July 2002 gkmt +\def\theparagraph{\thesubsubsection.\arabic{paragraph}} %removed \subsecfnt 29 July 2002 gkmt +\newif\if@uchead +\@ucheadfalse + +%% CHANGES: NEW NOTE +%% NOTE: OK to use old-style font commands below, since they were +%% suitably redefined for LaTeX2e +%% END CHANGES +\setcounter{secnumdepth}{3} +\def\part{% + \@startsection{part}{9}{\z@}{-10\p@ \@plus -4\p@ \@minus -2\p@} + {4\p@}{\normalsize\@ucheadtrue}% +} +\def\section{% + \@startsection{section}{1}{\z@}{-10\p@ \@plus -4\p@ \@minus -2\p@}% GM + {4\p@}{\baselineskip 14pt\secfnt\@ucheadtrue}% +} + +\def\subsection{% + \@startsection{subsection}{2}{\z@}{-8\p@ \@plus -2\p@ \@minus -\p@} + {4\p@}{\secfnt}% +} +\def\subsubsection{% + \@startsection{subsubsection}{3}{\z@}{-8\p@ \@plus -2\p@ \@minus -\p@}% + {4\p@}{\subsecfnt}% +} +%\def\paragraph{% +% \vskip 12pt\@startsection{paragraph}{3}{\z@}{6\p@ \@plus \p@}% original +% {-5\p@}{\subsecfnt}% +%} +% If one wants sections, subsections and subsubsections numbered, +% but not paragraphs, one usually sets secnumepth to 3. +% For that, the "depth" of paragraphs must be given correctly +% in the definition (``4'' instead of ``3'' as second argument +% of @startsection): +\def\paragraph{% + \vskip 12pt\@startsection{paragraph}{4}{\z@}{6\p@ \@plus \p@}% % GM and Wolfgang May - 11/30/06 + {-5\p@}{\subsecfnt}% +} +\let\@period=. +\def\@startsection#1#2#3#4#5#6{% + \if@noskipsec %gkmt, 11 aug 99 + \global\let\@period\@empty + \leavevmode + \global\let\@period.% + \fi + \par % + \@tempskipa #4\relax + \@afterindenttrue + \ifdim \@tempskipa <\z@ + \@tempskipa -\@tempskipa + \@afterindentfalse + \fi + \if@nobreak + \everypar{}% + \else + \addpenalty\@secpenalty + \addvspace\@tempskipa + \fi +\parskip=0pt % GM July 2000 (non numbered) section heads + \@ifstar + {\@ssect{#3}{#4}{#5}{#6}} + {\@dblarg{\@sect{#1}{#2}{#3}{#4}{#5}{#6}}}% +} +\def\@sect#1#2#3#4#5#6[#7]#8{% + \ifnum #2>\c@secnumdepth + \let\@svsec\@empty + \else + \refstepcounter{#1}% + \edef\@svsec{% + \begingroup + %\ifnum#2>2 \noexpand\rm \fi % changed to next 29 July 2002 gkmt + \ifnum#2>2 \noexpand#6 \fi + \csname the#1\endcsname + \endgroup + \ifnum #2=1\relax .\fi + \hskip 1em + }% + \fi + \@tempskipa #5\relax + \ifdim \@tempskipa>\z@ + \begingroup + #6\relax + \@hangfrom{\hskip #3\relax\@svsec}% + \begingroup + \interlinepenalty \@M + \if@uchead + \uppercase{#8}% + \else + #8% + \fi + \par + \endgroup + \endgroup + \csname #1mark\endcsname{#7}% + \vskip -12pt %gkmt, 11 aug 99 and GM July 2000 (was -14) - numbered section head spacing +\addcontentsline{toc}{#1}{% + \ifnum #2>\c@secnumdepth \else + \protect\numberline{\csname the#1\endcsname}% + \fi + #7% + }% + \else + \def\@svsechd{% + #6% + \hskip #3\relax + \@svsec + \if@uchead + \uppercase{#8}% + \else + #8% + \fi + \csname #1mark\endcsname{#7}% + \addcontentsline{toc}{#1}{% + \ifnum #2>\c@secnumdepth \else + \protect\numberline{\csname the#1\endcsname}% + \fi + #7% + }% + }% + \fi + \@xsect{#5}\hskip 1pt + \par +} +\def\@xsect#1{% + \@tempskipa #1\relax + \ifdim \@tempskipa>\z@ + \par + \nobreak + \vskip \@tempskipa + \@afterheading + \else + \global\@nobreakfalse + \global\@noskipsectrue + \everypar{% + \if@noskipsec + \global\@noskipsecfalse + \clubpenalty\@M + \hskip -\parindent + \begingroup + \@svsechd + \@period + \endgroup + \unskip + \@tempskipa #1\relax + \hskip -\@tempskipa + \else + \clubpenalty \@clubpenalty + \everypar{}% + \fi + }% + \fi + \ignorespaces +} +\def\@trivlist{% + \@topsepadd\topsep + \if@noskipsec + \global\let\@period\@empty + \leavevmode + \global\let\@period.% + \fi + \ifvmode + \advance\@topsepadd\partopsep + \else + \unskip + \par + \fi + \if@inlabel + \@noparitemtrue + \@noparlisttrue + \else + \@noparlistfalse + \@topsep\@topsepadd + \fi + \advance\@topsep \parskip + \leftskip\z@skip + \rightskip\@rightskip + \parfillskip\@flushglue + \@setpar{\if@newlist\else{\@@par}\fi} + \global\@newlisttrue + \@outerparskip\parskip +} + +%%% Actually, 'abbrev' works just fine as the default +%%% Bibliography style. + +\typeout{Using 'Abbrev' bibliography style} +\newcommand\bibyear[2]{% + \unskip\quad\ignorespaces#1\unskip + \if#2..\quad \else \quad#2 \fi +} +\newcommand{\bibemph}[1]{{\em#1}} +\newcommand{\bibemphic}[1]{{\em#1\/}} +\newcommand{\bibsc}[1]{{\sc#1}} +\def\@normalcite{% + \def\@cite##1##2{[##1\if@tempswa , ##2\fi]}% +} +\def\@citeNB{% + \def\@cite##1##2{##1\if@tempswa , ##2\fi}% +} +\def\@citeRB{% + \def\@cite##1##2{##1\if@tempswa , ##2\fi]}% +} +\def\start@cite#1#2{% + \edef\citeauthoryear##1##2##3{% + ###1% + \ifnum#2=\z@ \else\ ###2\fi + }% + \ifnum#1=\thr@@ + \let\@@cite\@citeyear + \else + \let\@@cite\@citenormal + \fi + \@ifstar{\@citeNB\@@cite}{\@normalcite\@@cite}% +} +%\def\cite{\start@cite23} +\DeclareRobustCommand\cite{\start@cite23} % January 2008 +\def\citeNP{\cite*} % No Parentheses e.g. 5 +%\def\citeA{\start@cite10} +\DeclareRobustCommand\citeA{\start@cite10} % January 2008 +\def\citeANP{\citeA*} +%\def\shortcite{\start@cite23} +\DeclareRobustCommand\shortcite{\start@cite23} % January 2008 +\def\shortciteNP{\shortcite*} +%\def\shortciteA{\start@cite20} +\DeclareRobustCommand\shortciteA{\start@cite20} % January 2008 +\def\shortciteANP{\shortciteA*} +%\def\citeyear{\start@cite30} +\DeclareRobustCommand\citeyear{\start@cite30} % January 2008 +\def\citeyearNP{\citeyear*} +%\def\citeN{% +\DeclareRobustCommand\citeN{% % January 2008 + \@citeRB + \def\citeauthoryear##1##2##3{##1\ [##3% + \def\reserved@a{##1}% + \def\citeauthoryear####1####2####3{% + \def\reserved@b{####1}% + \ifx\reserved@a\reserved@b + ####3% + \else + \errmessage{Package acmart Error: author mismatch + in \string\citeN^^J^^J% + See the acmart package documentation for explanation}% + \fi + }% + }% + \@ifstar\@citeyear\@citeyear +} +%\def\shortciteN{% +\DeclareRobustCommand\shortciteN{% % January 2008 + \@citeRB + \def\citeauthoryear##1##2##3{##2\ [##3% + \def\reserved@a{##2}% + \def\citeauthoryear####1####2####3{% + \def\reserved@b{####2}% + \ifx\reserved@a\reserved@b + ####3% + \else + \errmessage{Package acmart Error: author mismatch + in \string\shortciteN^^J^^J% + See the acmart package documentation for explanation}% + \fi + }% + }% + \@ifstar\@citeyear\@citeyear % GM July 2000 +} + +\def\@citenormal{% + \@ifnextchar [{\@tempswatrue\@citex;}% +% original {\@tempswafalse\@citex,[]}% was ; Gerry 2/24/00 +{\@tempswafalse\@citex[]}% % GERRY FIX FOR BABEL 3/20/2009 +} + +\def\@citeyear{% + \@ifnextchar [{\@tempswatrue\@citex,}% +% original {\@tempswafalse\@citex,[]}% +{\@tempswafalse\@citex[]}% % GERRY FIX FOR BABEL 3/20/2009 +} + +\def\@citex#1[#2]#3{% + \let\@citea\@empty + \@cite{% + \@for\@citeb:=#3\do{% + \@citea +% original \def\@citea{#1 }% + \def\@citea{#1, }% % GERRY FIX FOR BABEL 3/20/2009 -- SO THAT YOU GET [1, 2] IN THE BODY TEXT + \edef\@citeb{\expandafter\@iden\@citeb}% + \if@filesw + \immediate\write\@auxout{\string\citation{\@citeb}}% + \fi + \@ifundefined{b@\@citeb}{% + {\bf ?}% + \@warning{% + Citation `\@citeb' on page \thepage\space undefined% + }% + }% + {\csname b@\@citeb\endcsname}% + }% + }{#2}% +} +%\let\@biblabel\@gobble % Dec. 2008 - Gerry +% ---- +\def\@biblabelnum#1{[#1]} % Gerry's solution #1 - for Natbib -- April 2009 +\let\@biblabel=\@biblabelnum % Gerry's solution #1 - for Natbib -- April 2009 +\def\newblock{\relax} % Gerry Dec. 2008 +% --- +\newdimen\bibindent +\setcounter{enumi}{1} +\bibindent=0em +\def\thebibliography#1{% +\ifnum\addauflag=0\addauthorsection\global\addauflag=1\fi + \section[References]{% <=== OPTIONAL ARGUMENT ADDED HERE + {References} % was uppercased but this affects pdf bookmarks (SP/GM October 2004) + {\vskip -9pt plus 1pt} % GM Nov. 2006 / GM July 2000 (for somewhat tighter spacing) + \@mkboth{{\refname}}{{\refname}}% + }% + \list{[\arabic{enumi}]}{% + \settowidth\labelwidth{[#1]}% + \leftmargin\labelwidth + \advance\leftmargin\labelsep + \advance\leftmargin\bibindent + \parsep=0pt\itemsep=1pt % GM July 2000 + \itemindent -\bibindent + \listparindent \itemindent + \usecounter{enumi} + }% + \let\newblock\@empty + \raggedright % GM July 2000 + \sloppy + \sfcode`\.=1000\relax +} + + +\gdef\balancecolumns +{\vfill\eject +\global\@colht=\textheight +\global\ht\@cclv=\textheight +} + +\newcount\colcntr +\global\colcntr=0 +%\newbox\savebox +\newbox\saveb@x % January 2008 + +\gdef \@makecol {% +\global\advance\colcntr by 1 +\ifnum\colcntr>2 \global\colcntr=1\fi + \ifvoid\footins + \setbox\@outputbox \box\@cclv + \else + \setbox\@outputbox \vbox{% +\boxmaxdepth \@maxdepth + \@tempdima\dp\@cclv + \unvbox \@cclv + \vskip-\@tempdima + \vskip \skip\footins + \color@begingroup + \normalcolor + \footnoterule + \unvbox \footins + \color@endgroup + }% + \fi + \xdef\@freelist{\@freelist\@midlist}% + \global \let \@midlist \@empty + \@combinefloats + \ifvbox\@kludgeins + \@makespecialcolbox + \else + \setbox\@outputbox \vbox to\@colht {% +\@texttop + \dimen@ \dp\@outputbox + \unvbox \@outputbox + \vskip -\dimen@ + \@textbottom + }% + \fi + \global \maxdepth \@maxdepth +} +\def\titlenote{\@ifnextchar[\@xtitlenote{\stepcounter\@mpfn +\global\advance\titlenotecount by 1 +\ifnum\titlenotecount=1 + \raisebox{9pt}{$\ast$} +\fi +\ifnum\titlenotecount=2 + \raisebox{9pt}{$\dagger$} +\fi +\ifnum\titlenotecount=3 + \raisebox{9pt}{$\ddagger$} +\fi +\ifnum\titlenotecount=4 +\raisebox{9pt}{$\S$} +\fi +\ifnum\titlenotecount=5 +\raisebox{9pt}{$\P$} +\fi + \@titlenotetext +}} + +\long\def\@titlenotetext#1{\insert\footins{% +\ifnum\titlenotecount=1\global\tntoks={#1}\fi +\ifnum\titlenotecount=2\global\tntokstwo={#1}\fi +\ifnum\titlenotecount=3\global\tntoksthree={#1}\fi +\ifnum\titlenotecount=4\global\tntoksfour={#1}\fi +\ifnum\titlenotecount=5\global\tntoksfive={#1}\fi + \reset@font\footnotesize + \interlinepenalty\interfootnotelinepenalty + \splittopskip\footnotesep + \splitmaxdepth \dp\strutbox \floatingpenalty \@MM + \hsize\columnwidth \@parboxrestore + \protected@edef\@currentlabel{% + }% + \color@begingroup + \color@endgroup}} + +%%%%%%%%%%%%%%%%%%%%%%%%% +\ps@plain +\baselineskip=11pt +\let\thepage\relax % For NO page numbers - GM Nov. 30th. 1999 and July 2000 +\def\setpagenumber#1{\global\setcounter{page}{#1}} +%\pagenumbering{arabic} % Arabic page numbers GM July 2000 +\twocolumn % Double column. +\flushbottom % Even bottom -- alas, does not balance columns at end of document +\pagestyle{plain} + +% Need Copyright Year and Copyright Data to be user definable (in .tex file). +% Gerry Nov. 30th. 1999 +\newtoks\copyrtyr +\newtoks\acmcopyr +\newtoks\boilerplate +\global\acmcopyr={X-XXXXX-XX-X/XX/XX} % Default - 5/11/2001 *** Gerry +\global\copyrtyr={20XX} % Default - 3/3/2003 *** Gerry +\def\CopyrightYear#1{\global\copyrtyr{#1}} +\def\crdata#1{\global\acmcopyr{#1}} +\def\permission#1{\global\boilerplate{#1}} +% +\global\boilerplate={Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee.} +\newtoks\copyrightetc +\global\copyrightetc{Copyright \the\copyrtyr\ ACM \the\acmcopyr\ ...\$15.00} % Gerry changed to 15 May 2012 +\toappear{\the\boilerplate\par +{\confname{\the\conf}} \the\confinfo\par \the\copyrightetc.} +%\DeclareFixedFont{\altcrnotice}{OT1}{tmr}{m}{n}{8} % << patch needed for accenting e.g. Montreal - Gerry, May 2007 +%\DeclareFixedFont{\altconfname}{OT1}{tmr}{m}{it}{8} % << patch needed for accenting in italicized confname - Gerry, May 2007 +% +%{\altconfname{{\the\conf}}} {\altcrnotice\the\confinfo\par} \the\copyrightetc.} % << Gerry, May 2007 +% +% The following section (i.e. 3 .sty inclusions) was added in May 2007 so as to fix the problems that many +% authors were having with accents. Sometimes accents would occur, but the letter-character would be of a different +% font. Conversely the letter-character font would be correct but, e.g. a 'bar' would appear superimposed on the +% character instead of, say, an unlaut/diaresis. Sometimes the letter-character would NOT appear at all. +% Using [T1]{fontenc} outright was not an option as this caused 99% of the authors to 'produce' a Type-3 (bitmapped) +% PDF file - useless for production. +% +% For proper (font) accenting we NEED these packages to be part of the .cls file i.e. 'ae', 'aecompl' and 'aeguil' +% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +%% This is file `ae.sty' +\def\fileversion{1.3} +\def\filedate{2001/02/12} +\NeedsTeXFormat{LaTeX2e} +%\ProvidesPackage{ae}[\filedate\space\fileversion\space % GM +% Almost European Computer Modern] % GM - keeping the log file clean(er) +\newif\if@ae@slides \@ae@slidesfalse +\DeclareOption{slides}{\@ae@slidestrue} +\ProcessOptions +\fontfamily{aer} +\RequirePackage[T1]{fontenc} +\if@ae@slides + \renewcommand{\sfdefault}{laess} + \renewcommand{\rmdefault}{laess} % no roman + \renewcommand{\ttdefault}{laett} +\else + \renewcommand{\sfdefault}{aess} + \renewcommand{\rmdefault}{aer} + \renewcommand{\ttdefault}{aett} +\fi +\endinput +%% +%% End of file `ae.sty'. +% +% +\def\fileversion{0.9} +\def\filedate{1998/07/23} +\NeedsTeXFormat{LaTeX2e} +%\ProvidesPackage{aecompl}[\filedate\space\fileversion\space % GM +%T1 Complements for AE fonts (D. Roegel)] % GM -- keeping the log file clean(er) + +\def\@ae@compl#1{{\fontencoding{T1}\fontfamily{cmr}\selectfont\symbol{#1}}} +\def\guillemotleft{\@ae@compl{19}} +\def\guillemotright{\@ae@compl{20}} +\def\guilsinglleft{\@ae@compl{14}} +\def\guilsinglright{\@ae@compl{15}} +\def\TH{\@ae@compl{222}} +\def\NG{\@ae@compl{141}} +\def\ng{\@ae@compl{173}} +\def\th{\@ae@compl{254}} +\def\DJ{\@ae@compl{208}} +\def\dj{\@ae@compl{158}} +\def\DH{\@ae@compl{208}} +\def\dh{\@ae@compl{240}} +\def\@perthousandzero{\@ae@compl{24}} +\def\textperthousand{\%\@perthousandzero} +\def\textpertenthousand{\%\@perthousandzero\@perthousandzero} +\endinput +% +% +%% This is file `aeguill.sty' +% This file gives french guillemets (and not guillemots!) +% built with the Polish CMR fonts (default), WNCYR fonts, the LASY fonts +% or with the EC fonts. +% This is useful in conjunction with the ae package +% (this package loads the ae package in case it has not been loaded) +% and with or without the french(le) package. +% +% In order to get the guillemets, it is necessary to either type +% \guillemotleft and \guillemotright, or to use an 8 bit encoding +% (such as ISO-Latin1) which selects these two commands, +% or, if you use the french package (but not the frenchle package), +% to type << or >>. +% +% By default, you get the Polish CMR guillemets; if this package is loaded +% with the `cm' option, you get the LASY guillemets; with `ec,' you +% get the EC guillemets, and with `cyr,' you get the cyrillic guillemets. +% +% In verbatim mode, you always get the EC/TT guillemets. +% +% The default option is interesting in conjunction with PDF, +% because there is a Type 1 version of the Polish CMR fonts +% and these guillemets are very close in shape to the EC guillemets. +% There are no free Type 1 versions of the EC fonts. +% +% Support for Polish CMR guillemets was kindly provided by +% Rolf Niepraschk in version 0.99 (2000/05/22). +% Bernd Raichle provided extensive simplifications to the code +% for version 1.00. +% +% This package is released under the LPPL. +% +% Changes: +% Date version +% 2001/04/12 1.01 the frenchle and french package are now distinguished. +% +\def\fileversion{1.01} +\def\filedate{2001/04/12} +\NeedsTeXFormat{LaTeX2e} +%\ProvidesPackage{aeguill}[2001/04/12 1.01 % % GM +%AE fonts with french guillemets (D. Roegel)] % GM - keeping the log file clean(er) +%\RequirePackage{ae} % GM May 2007 - already embedded here + +\newcommand{\@ae@switch}[4]{#4} +\DeclareOption{ec}{\renewcommand\@ae@switch[4]{#1}} +\DeclareOption{cm}{\renewcommand\@ae@switch[4]{#2}} +\DeclareOption{cyr}{\renewcommand\@ae@switch[4]{#3}} +\DeclareOption{pl}{\renewcommand\@ae@switch[4]{#4}} +\ExecuteOptions{pl} +\ProcessOptions + +% +% Load necessary packages +% +\@ae@switch{% ec + % do nothing +}{% cm + \RequirePackage{latexsym}% GM - May 2007 - already 'mentioned as required' up above +}{% cyr + \RequirePackage[OT2,T1]{fontenc}% +}{% pl + \RequirePackage[OT4,T1]{fontenc}% +} + +% The following command will be compared to \frenchname, +% as defined in french.sty and frenchle.sty. +\def\aeguillfrenchdefault{french}% + +\let\guill@verbatim@font\verbatim@font +\def\verbatim@font{\guill@verbatim@font\ecguills{cmtt}% + \let\guillemotleft\@oguills\let\guillemotright\@fguills} + +\begingroup \catcode`\<=13 \catcode`\>=13 +\def\x{\endgroup + \def\ae@lfguill{<<}% + \def\ae@rfguill{>>}% +}\x + +\newcommand{\ecguills}[1]{% + \def\selectguillfont{\fontencoding{T1}\fontfamily{#1}\selectfont}% + \def\@oguills{{\selectguillfont\symbol{19}}}% + \def\@fguills{{\selectguillfont\symbol{20}}}% + } + +\newcommand{\aeguills}{% + \ae@guills + % We redefine \guillemotleft and \guillemotright + % in order to catch them when they are used + % with \DeclareInputText (in latin1.def for instance) + % We use \auxWARNINGi as a safe indicator that french.sty is used. + \gdef\guillemotleft{\ifx\auxWARNINGi\undefined + \@oguills % neither french.sty nor frenchle.sty + \else + \ifx\aeguillfrenchdefault\frenchname + \ae@lfguill % french.sty + \else + \@oguills % frenchle.sty + \fi + \fi}% + \gdef\guillemotright{\ifx\auxWARNINGi\undefined + \@fguills % neither french.sty nor frenchle.sty + \else + \ifx\aeguillfrenchdefault\frenchname + \ae@rfguill % french.sty + \else + \@fguills % frenchle.sty + \fi + \fi}% + } + +% +% Depending on the class option +% define the internal command \ae@guills +\@ae@switch{% ec + \newcommand{\ae@guills}{% + \ecguills{cmr}}% +}{% cm + \newcommand{\ae@guills}{% + \def\selectguillfont{\fontencoding{U}\fontfamily{lasy}% + \fontseries{m}\fontshape{n}\selectfont}% + \def\@oguills{\leavevmode\nobreak + \hbox{\selectguillfont (\kern-.20em(\kern.20em}\nobreak}% + \def\@fguills{\leavevmode\nobreak + \hbox{\selectguillfont \kern.20em)\kern-.2em)}% + \ifdim\fontdimen\@ne\font>\z@\/\fi}}% +}{% cyr + \newcommand{\ae@guills}{% + \def\selectguillfont{\fontencoding{OT2}\fontfamily{wncyr}\selectfont}% + \def\@oguills{{\selectguillfont\symbol{60}}}% + \def\@fguills{{\selectguillfont\symbol{62}}}} +}{% pl + \newcommand{\ae@guills}{% + \def\selectguillfont{\fontencoding{OT4}\fontfamily{cmr}\selectfont}% + \def\@oguills{{\selectguillfont\symbol{174}}}% + \def\@fguills{{\selectguillfont\symbol{175}}}} +} + + +\AtBeginDocument{% + \ifx\GOfrench\undefined + \aeguills + \else + \let\aeguill@GOfrench\GOfrench + \gdef\GOfrench{\aeguill@GOfrench \aeguills}% + \fi + } + +\endinput +% + diff --git a/ICCAD16_openram_paper/acmcopyright.sty b/ICCAD16_openram_paper/acmcopyright.sty new file mode 100755 index 00000000..e8de127a --- /dev/null +++ b/ICCAD16_openram_paper/acmcopyright.sty @@ -0,0 +1,221 @@ +%% +%% This is file `acmcopyright.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% acmcopyright.dtx (with options: `style') +%% +%% IMPORTANT NOTICE: +%% +%% For the copyright see the source file. +%% +%% Any modified versions of this file must be renamed +%% with new filenames distinct from acmcopyright.sty. +%% +%% For distribution of the original source see the terms +%% for copying and modification in the file acmcopyright.dtx. +%% +%% This generated file may be distributed as long as the +%% original source files, as listed above, are part of the +%% same distribution. (The sources need not necessarily be +%% in the same archive or directory.) +%% \CharacterTable +%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +%% Digits \0\1\2\3\4\5\6\7\8\9 +%% Exclamation \! Double quote \" Hash (number) \# +%% Dollar \$ Percent \% Ampersand \& +%% Acute accent \' Left paren \( Right paren \) +%% Asterisk \* Plus \+ Comma \, +%% Minus \- Point \. Solidus \/ +%% Colon \: Semicolon \; Less than \< +%% Equals \= Greater than \> Question mark \? +%% Commercial at \@ Left bracket \[ Backslash \\ +%% Right bracket \] Circumflex \^ Underscore \_ +%% Grave accent \` Left brace \{ Vertical bar \| +%% Right brace \} Tilde \~} +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{acmcopyright} +[2014/06/29 v1.2 Copyright statemens for ACM classes] +\newif\if@printcopyright +\@printcopyrighttrue +\newif\if@printpermission +\@printpermissiontrue +\newif\if@acmowned +\@acmownedtrue +\RequirePackage{xkeyval} +\define@choicekey*{ACM@}{acmcopyrightmode}[% + \acm@copyrightinput\acm@copyrightmode]{none,acmcopyright,acmlicensed,% + rightsretained,usgov,usgovmixed,cagov,cagovmixed,% + licensedusgovmixed,licensedcagovmixed,othergov,licensedothergov}{% + \@printpermissiontrue + \@printcopyrighttrue + \@acmownedtrue + \ifnum\acm@copyrightmode=0\relax % none + \@printpermissionfalse + \@printcopyrightfalse + \@acmownedfalse + \fi + \ifnum\acm@copyrightmode=2\relax % acmlicensed + \@acmownedfalse + \fi + \ifnum\acm@copyrightmode=3\relax % rightsretained + \@acmownedfalse + \fi + \ifnum\acm@copyrightmode=4\relax % usgov + \@printpermissiontrue + \@printcopyrightfalse + \@acmownedfalse + \fi + \ifnum\acm@copyrightmode=6\relax % cagov + \@acmownedfalse + \fi + \ifnum\acm@copyrightmode=8\relax % licensedusgovmixed + \@acmownedfalse + \fi + \ifnum\acm@copyrightmode=9\relax % licensedcagovmixed + \@acmownedfalse + \fi + \ifnum\acm@copyrightmode=10\relax % othergov + \@acmownedtrue + \fi + \ifnum\acm@copyrightmode=11\relax % licensedothergov + \@acmownedfalse + \@printcopyrightfalse + \fi} +\def\setcopyright#1{\setkeys{ACM@}{acmcopyrightmode=#1}} +\setcopyright{acmcopyright} +\def\@copyrightowner{% + \ifcase\acm@copyrightmode\relax % none + \or % acmcopyright + ACM. + \or % acmlicensed + Copyright held by the owner/author(s). Publication rights licensed to + ACM. + \or % rightsretained + Copyright held by the owner/author(s). + \or % usgov + \or % usgovmixed + ACM. + \or % cagov + Crown in Right of Canada. + \or %cagovmixed + ACM. + \or %licensedusgovmixed + Copyright held by the owner/author(s). Publication rights licensed to + ACM. + \or %licensedcagovmixed + Copyright held by the owner/author(s). Publication rights licensed to + ACM. + \or % othergov + ACM. + \or % licensedothergov + \fi} +\def\@copyrightpermission{% + \ifcase\acm@copyrightmode\relax % none + \or % acmcopyright + Permission to make digital or hard copies of all or part of this + work for personal or classroom use is granted without fee provided + that copies are not made or distributed for profit or commercial + advantage and that copies bear this notice and the full citation on + the first page. Copyrights for components of this work owned by + others than ACM must be honored. Abstracting with credit is + permitted. To copy otherwise, or republish, to post on servers or to + redistribute to lists, requires prior specific permission + and\hspace*{.5pt}/or a fee. Request permissions from + permissions@acm.org. + \or % acmlicensed + Permission to make digital or hard copies of all or part of this + work for personal or classroom use is granted without fee provided + that copies are not made or distributed for profit or commercial + advantage and that copies bear this notice and the full citation on + the first page. Copyrights for components of this work owned by + others than the author(s) must be honored. Abstracting with credit + is permitted. To copy otherwise, or republish, to post on servers + or to redistribute to lists, requires prior specific permission + and\hspace*{.5pt}/or a fee. Request permissions from + permissions@acm.org. + \or % rightsretained + Permission to make digital or hard copies of part or all of this work + for personal or classroom use is granted without fee provided that + copies are not made or distributed for profit or commercial advantage + and that copies bear this notice and the full citation on the first + page. Copyrights for third-party components of this work must be + honored. For all other uses, contact the + owner\hspace*{.5pt}/author(s). + \or % usgov + This paper is authored by an employee(s) of the United States + Government and is in the public domain. Non-exclusive copying or + redistribution is allowed, provided that the article citation is + given and the authors and agency are clearly identified as its + source. + \or % usgovmixed + ACM acknowledges that this contribution was authored or co-authored + by an employee, or contractor of the national government. As such, + the Government retains a nonexclusive, royalty-free right to + publish or reproduce this article, or to allow others to do so, for + Government purposes only. Permission to make digital or hard copies + for personal or classroom use is granted. Copies must bear this + notice and the full citation on the first page. Copyrights for + components of this work owned by others than ACM must be + honored. To copy otherwise, distribute, republish, or post, + requires prior specific permission and\hspace*{.5pt}/or a + fee. Request permissions from permissions@acm.org. + \or % cagov + This article was authored by employees of the Government of Canada. + As such, the Canadian government retains all interest in the + copyright to this work and grants to ACM a nonexclusive, + royalty-free right to publish or reproduce this article, or to allow + others to do so, provided that clear attribution is given both to + the authors and the Canadian government agency employing them. + Permission to make digital or hard copies for personal or classroom + use is granted. Copies must bear this notice and the full citation + on the first page. Copyrights for components of this work owned by + others than the Canadain Government must be honored. To copy + otherwise, distribute, republish, or post, requires prior specific + permission and\hspace*{.5pt}/or a fee. Request permissions from + permissions@acm.org. + \or % cagovmixed + ACM acknowledges that this contribution was co-authored by an + affiliate of the national government of Canada. As such, the Crown + in Right of Canada retains an equal interest in the copyright. + Reprints must include clear attribution to ACM and the author's + government agency affiliation. Permission to make digital or hard + copies for personal or classroom use is granted. Copies must bear + this notice and the full citation on the first page. Copyrights for + components of this work owned by others than ACM must be honored. + To copy otherwise, distribute, republish, or post, requires prior + specific permission and\hspace*{.5pt}/or a fee. Request permissions + from permissions@acm.org. + \or % licensedusgovmixed + Publication rights licensed to ACM. ACM acknowledges that this + contribution was authored or co-authored by an employee, contractor + or affiliate of the United States government. As such, the + Government retains a nonexclusive, royalty-free right to publish or + reproduce this article, or to allow others to do so, for Government + purposes only. + \or % licensedcagovmixed + Publication rights licensed to ACM. ACM acknowledges that this + contribution was authored or co-authored by an employee, contractor + or affiliate of the national government of Canada. As such, the + Government retains a nonexclusive, royalty-free right to publish or + reproduce this article, or to allow others to do so, for Government + purposes only. + \or % othergov + ACM acknowledges that this contribution was authored or co-authored + by an employee, contractor or affiliate of a national government. As + such, the Government retains a nonexclusive, royalty-free right to + publish or reproduce this article, or to allow others to do so, for + Government purposes only. + \or % licensedothergov + Publication rights licensed to ACM. ACM acknowledges that this + contribution was authored or co-authored by an employee, contractor + or affiliate of a national government. As such, the Government + retains a nonexclusive, royalty-free right to publish or reproduce + this article, or to allow others to do so, for Government purposes + only. + \fi} +\endinput +%% +%% End of file `acmcopyright.sty'. diff --git a/ICCAD16_openram_paper/appendix.tex b/ICCAD16_openram_paper/appendix.tex new file mode 100644 index 00000000..a917d0dd --- /dev/null +++ b/ICCAD16_openram_paper/appendix.tex @@ -0,0 +1 @@ +\appendix diff --git a/ICCAD16_openram_paper/architecture.tex b/ICCAD16_openram_paper/architecture.tex new file mode 100644 index 00000000..cd3f9e16 --- /dev/null +++ b/ICCAD16_openram_paper/architecture.tex @@ -0,0 +1,200 @@ +\section{Architecture} +\label{sec:architecture} + +% Overview of SRAM blocks +The OpenRAM SRAM architecture is based on a bank of memory cells +with peripheral circuits and control logic as illustrated in +Figure~\ref{fig:structure}. These are further refined into eight major +blocks: the bit-cell array, the address decoder, the word-line drivers, +the column multiplexer, the pre-charge circuitry, the sense amplifier, +the write drivers, and the control logic. + +\begin{figure}[tb] +\centering +\includegraphics[width=8cm]{./figs/sram_structure.pdf} +\caption{An OpenRAM SRAM consists of a bit-cell array along with decoder, + reading and writing circuitry and control logic timed with a replica + bit-line. +\label{fig:structure}} +\end{figure} + +% we don't implement these yet, so don't give a tutorial on them +%% General memories and Register Files (RF) are both examples of what an +%% memory compiler can generate. General memories usually have shared +%% read/write ports whereas RFs typically have separate ports. All of +%% these options are permitted through the use of different types of +%% memory cells such as 6, 8, and 12 transistor (T) cells which contains +%% 1-4 access transistor pairs and their associated bit-lines. Some basic +%% memory array options are available below: +%% \begin{itemize} +%% \setlength{\itemsep}{0pt} \setlength{\parskip}{0pt} +%% \item Standard 6T cell for single-port memory +%% \item Dual-port 8T cell for dual-port memory or separate read/write ports +%% \item Four-port 12T cell for dual separate read/write ports +%% \item Custom sense amplifier designs for different performances +%% \item Different types of address decoders for different performances +%% \end{itemize} + +\begin{figure*}[tb] +\centering +\subfigure[Read operation timing]{ +\includegraphics[width = 8cm]{figs/timing_read.pdf} +\label{fig:timing_read}} +\subfigure[Write operation timing]{ +\includegraphics[width = 8cm]{figs/timing_write.pdf} +\label{fig:timing_write}} +\caption{OpenRAM uses a synchronous SRAM interface using a system + clock (clk) along with control signals: output enable (OEb), chip + select (CSb) and write enable (WEb).} +\label{fig:timing} +\end{figure*} + +{\bf Bit-cell Array:} In the initial release of OpenRAM, the $6$T cell +is the default memory cell because it is the most commonly used cell +in SRAM devices. $6$T cells are tiled together with abutting word- and +bit-lines to make up the memory array. The bit-cell array's aspect +ratio is made as square as possible using multiple columns of data +words. The memory cell is a custom designed library cell for each technology. +Other types of memory cells, such as $7$T, $8$T, and $10$T cells, can be used +as alternatives to the $6$T cell. + +{\bf Address Decoder:} The address decoder takes the row address bits +as inputs and asserts the appropriate word-line so that the correct +memory cells can be read from or written to. The address decoder is +placed to the left of the memory array and spans the array's vertical +length. Different types of decoders can be used such as an included +dynamic NAND decoder, but OpenRAM's default option is a hierarchical CMOS +decoder. + +{\bf Word-Line Driver:} Word-line drivers are inserted between the +address decoder and the memory array as buffers. The word-line drivers +are sized based on the width of the memory array so that they can drive +the row select signal across the bit-cell array. + +{\bf Column Multiplexer:} The column multiplexer is an optional block +that uses the lower address bits to select the associated word in a +row. The column mux is dynamically generated and can be omitted or can +have 2 or 4 inputs. Larger column muxes are possible, but are not +frequently used in memories. There are options for a multi-level tree +mux as well. + +{\bf Bit-line Precharge:} This circuitry pre-charges +the bit-lines during the first phase of the clock for read +operations. The precharge circuit is placed on top of every column in +the memory array and equalizes the bit-line voltages so that the +sense amplifier can sense the voltage difference between the two +bit-lines. + +{\bf Sense Amplifier:} A differential sense amplifier is used to sense +the voltage difference between the bit-lines of a memory cell while a +read operation is performed. The sense amplifier uses a bit-line +isolation technique to increase performance. The sense amplifier +circuitry is placed below the column multiplexer or the memory +array if no column multiplexer is used. There is one sense amplifier for +each output bit. + +{\bf Write Driver:} The write drivers send the input data signals onto the +bit-lines for a write operation. The write drivers are tri-stated +so that they can be placed between the column multiplexer/memory array +and the sense amplifiers. There is one write driver for each input +data bit. + +%% \subsubsection{Bit-cell and Bit-cell Array} +%% A bit-cell class is provided to instantiate the custom designed memory +%% cell located in the technology directory. Then the bit-cell array class +%% will take the single bit-cell instance to dynamically generate the +%% memory array. Using the functionality of GdsMill, we can rotate and/or +%% mirror an instance. Doing so, will allow us to abut the power rails. + +%% \subsubsection{Address Decoder} +%% The hierarchical decoder is the default row address decoder that is +%% used in OpenRAM. The hierarchical decoder is dynamically generated +%% using the inverter and NAND gates with the help of basic shapes. The +%% height of each decoder row will match the height of the memory cell so +%% that the power rails can be abutted. OpenRAM also provides a NAND +%% decoder as an alternative. NAND decoder uses NMOS and PMOS transistors +%% created by ptx class. User can define type of the decoder in the +%% configuration file. + +%% \subsubsection{Word-line Driver} +%% The word-line driver will be a column of alternating "mirrored" +%% inverters instances that is used to drive the signal to access the +%% memory cells in the row. The inverters will be sized accordingly +%% depending on the size of the memory array. + +%% \subsubsection{Column Multiplexer} +%% The column multiplexer is an optional block that is used depending on +%% the size of the memory array. By generating an instance of a 1-1 +%% multiplexer, we can then tile them to create bigger multiplexers such +%% as 2-1, 4-1, etc. OpenRAM has two options for column multiplexing. +%% Single-level-column-mux is the default column multiplexer but user can +%% choose Tree-Column-Mux in configuration file. Both multiplexers use +%% transistors created by ptx class. + +%% \subsubsection{Precharge and Precharge Array} +%% The precharge circuitry is dynamically generated using the transistor +%% instances and various basic shapes. The precharge class dynamically +%% generates an instance for a single column. The precharge array class +%% takes that instance and tiles them horizontally to match the number of +%% columns in the memory array. The width of the precharge cell is +%% determined by the width of the user-created memory cell. + +%% \subsubsection{Sense Amplifier and Sense Amplifier Array} +%% The sense amplifier is user-designed analog circuit that is placed in +%% the technology directory. The sense amplifier class instantiates the +%% library cell and the sense amplifier array takes that instance to +%% create a horizontal array matching the number of output bits for the +%% memory. When designing this library cell, the user should match this +%% cell's width and bit-lines to the memory cell's. + +%% \subsubsection{Write Driver and Write Driver Array} +%% Similar to the precharge classes, the write driver class will generate +%% an instance for a single bit and the write driver array will tile them +%% horizontally to match the number of input bits for the memory. The +%% write drivers will be dynamically sized accordingly based on the size +%% of the memory array. + +%% \subsubsection{Control Logic} +%% There will be a control logic module that will arrange the +%% master-slave flip-flops and the logic associated with the control +%% signals into a single design. Flip-flops are used to drive the control +%% signals and standard library cells such as NAND and NOR gates will be +%% used for the logic. A RBL is also generated using parameterized gates +%% and Replica Cell (RC). RC is a 6T SRAM memory cell which is hard-wired +%% to store a zero in order to discharge the RBL and generate the sense +%% amplifier enable signal in read mode. + +%% \subsubsection{Additional Arrays} +%% In addition to the eight main blocks, there are helper modules that +%% help simplify the designs in the eight main blocks. We have a +%% flip-flop array class that takes the custom designed master-slave +%% flip-flop library cell to create a tiled array. We also have the +%% tri-state array class that will generate the array of tri-states for +%% the DATA bus. + +% Overview of signal inputs and timing +{\bf Control Logic:} The OpenRAM SRAM architecture incorporates a +standard synchronous memory interface using a system clock (clk). The +control logic uses an externally provided, active-low output enable +(OEb), chip select (CSb), and write enable (WEb) to combine multiple +SRAMs into a larger structure. Internally, the OpenRAM compiler can +have $1$, $2$, or $4$ memory banks to amortize the area/power cost of +control logic and peripheral circuitry. + +All of the input control signals are stored using master-slave (MS) +flip-flops (FF) to ensure that the signals are valid for the entire +clock cycle. During a read operation, data is available after the +negative clock edge (second half of cycle) as shown in +Figure~\ref{fig:timing_read}. To avoid dead cycles which degrade +performance, a Zero Bus Turn-around (ZBT) technique is used in OpenRAM +timing. The ZBT enables higher memory throughput since there are no +wait states. During ZBT writes, data is set up before the negative +clock edge and is captured on the negative edge. Figure~\ref{fig:timing_write} +shows the timing for input signals during the write operation. + +The internal control signals are generated using a replica bit-line (RBL) +structure for the timing of the sense amplifier enable and output +data storage~\cite{RBL:1998}. The RBL turns on the sense amplifiers +at the exact time in presence of process variability in sub-$100$nm +technologies. + diff --git a/ICCAD16_openram_paper/background.tex b/ICCAD16_openram_paper/background.tex new file mode 100644 index 00000000..7fe8928f --- /dev/null +++ b/ICCAD16_openram_paper/background.tex @@ -0,0 +1,93 @@ +\section{Background} +\label{sec:background} +% brief origin/background of memory compilers + +% Existence of memory compilers from the beginning +Memory compilers have been used in Electronic Design Automation (EDA) +design flows to reduce the design +time long before contemporary +compilers~\cite{broderson:sicompiler,johannsen:blocks}. +However, these compilers were generally not portable as they were +nothing more +than quick scripts to aid designers. Porting to a new technology +essentially required rewriting the scripts. However, the increase in +design productivity when porting designs between technologies has led to +more research on memory array +compilers~\cite{cabe:flexible,huang:array,poechmueller:array,Xu:2007}. + +% Reason why compilers evolved to today's current version +As technology entered the Deep Sub-Micron (DSM) era, memory designs +became one of the most challenging parts of circuit design +due to decreasing static noise margins (SNM), increasing fabrication +variability, and increasing leakage power consumption. +This increased the complexity of memory compilers dramatically as they had to +adapt to the ever-changing technologies. Simultaneously, design +methodologies shifted from silicon compilers to standard cell place +and route methods which required large optimized libraries. During +this time, industry began using third-party suppliers of standard cell +libraries and memory compilers that allowed their reuse to amortize +development costs. These next-generation memory compilers provided +silicon-verification that allowed designers to focus on their new +design contribution rather than time-consuming tasks such as memory +generation. + +% Commercial industry memory compilers' description and cost +Contemporary memory compilers have been widely used by industry, but +the internal operation is typically hidden. Several prominent +companies and foundries have provided memory compilers to their +customers. These memory compilers usually allow customers to view +front-end simulation, timing/power values, and pin locations after a +license agreement is signed. Back-end features such as layout are +normally supplied directly to the fab and are only given to the user +for a licensing fee. + +% Examples of commercial compilers' drawbacks +Specifically, Global Foundries offers front-end PDKs for free, but not +back-end detailed views~\cite{globalfoundries:2015}. Faraday +Technologies provides a \enquote{black box} design kit where users do +not know the details of the internal memory +design~\cite{faraday:2015}. Dolphin Technology offers closed-source +compilers which can create RAMs, ROMs, and CAMs for a variety of +technologies~\cite{dolphin:2015}. The majority of these commercial +compilers do not allow the customer to alter the base design, are +restricted by the company's license, and usually require a fee. This +makes them virtually unavailable and not useful for many academic +research projects. + +% Describe the problem (no free open-source that is widely distributed) +In addition to memory compilers provided by industry, various research +groups have released scripts to generate memories. However, these +designs are not silicon verified and are usually only composed of +simple structures. For example, FabMem is able to +create small arrays, but it is highly dependent on the Cadence design +tools~\cite{fabmem:2010}. The scripts do not provide any characterization capability +and cannot easily integrate with commercial place and route tools. + +% Industry's attempt to provide academia a memory compiler +Another recent, promising solution for academia is the Synopsys +Generic Memory Compiler (GMC)~\cite{Goldman:2014}. The software is +provided with sample generic libraries such as Synopsys' $32$/$28$nm and +$90$nm abstract technologies and can generate the entire SRAM for these +technologies. The GMC generates GDSII layout data, SPICE netlists, +Verilog and VHDL models, timing/power libraries, and DRC/LVS +verification reports. GMC, however, is not recommended for +fabrication since the technologies it supports are not real. Its sole +purpose is to aid students in VLSI courses to learn about using +memories in design flows. + +% Academia's' attempts at a memory compiler +There have been multiple attempts by academia to implement a memory +compiler that is not restricted: the Institute of +Microelectronics' SRAM IP Compiler~\cite{Xu:2007}, School of +Electronic Science and Engineering at Southeast University's Memory IP +Compiler~\cite{Chen:2012}, and Tsinghua University's Low Power SRAM +Compiler~\cite{Wu:2010}. These are all methodologies and design flows +for a memory compiler, but there are no public releases. + +% State what we are looking for in academia. -- duplicate from introduction +%% With all these attempts, there still isn't a complete solution for +%% academia's research needs. Researchers need a memory compiler that is +%% open-source, platform- and tool-portable, technology independent, and +%% can generate fabricable memory designs. + + diff --git a/ICCAD16_openram_paper/camera_copy.pdf b/ICCAD16_openram_paper/camera_copy.pdf new file mode 100644 index 00000000..4a82bbb1 Binary files /dev/null and b/ICCAD16_openram_paper/camera_copy.pdf differ diff --git a/ICCAD16_openram_paper/conclusion.tex b/ICCAD16_openram_paper/conclusion.tex new file mode 100644 index 00000000..3e822b70 --- /dev/null +++ b/ICCAD16_openram_paper/conclusion.tex @@ -0,0 +1,21 @@ +\section{Conclusions} +\label{sec:conclusions} +This paper introduced OpenRAM, an open-source and portable memory +compiler. OpenRAM generates the circuit, functional model, and layout +of variable-sized SRAMs. In addition, a memory characterizer +provides synthesis timing/power models. + +The main motivation behind OpenRAM is to promote and simplify +memory-related research in academia. Since OpenRAM is open-sourced, +flexible, and portable, this memory compiler can be adapted to various +technologies and is easily modified to address specific design +requirements. Therefore, OpenRAM provides a platform to implement and test +new memory designs. + +Designs are currently being fabricated to test designs using the +OpenRAM framework in SCMOS. We are also continuously introducing new +features, such as non-6T memories, variability characterization, +word-line segmenting, characterization speed-up, and a graphical user +interface (GUI). We hope to engage an active community in the future +development of OpenRAM. + diff --git a/ICCAD16_openram_paper/embed_fonts.sh b/ICCAD16_openram_paper/embed_fonts.sh new file mode 100755 index 00000000..54688ed9 --- /dev/null +++ b/ICCAD16_openram_paper/embed_fonts.sh @@ -0,0 +1,5 @@ +#!/bin/bash +psfile=${1%.pdf}.ps +gs -dSAFER -dNOPLATFONTS -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sPAPERSIZE=letter -dCompatibilityLevel=1.4 -dPDFSETTINGS=/printer -dCompatibilityLevel=1.4 -dMaxSubsetPct=100 -dSubsetFonts=true -dEmbedAllFonts=true -sOutputFile=temp.pdf -f $1 +mv temp.pdf $1 +pdf2ps $1 $psfile diff --git a/ICCAD16_openram_paper/figs/Freepdk_Area.pdf b/ICCAD16_openram_paper/figs/Freepdk_Area.pdf new file mode 100644 index 00000000..36f91e9f Binary files /dev/null and b/ICCAD16_openram_paper/figs/Freepdk_Area.pdf differ diff --git a/ICCAD16_openram_paper/figs/Freepdk_Read_Access_time.pdf b/ICCAD16_openram_paper/figs/Freepdk_Read_Access_time.pdf new file mode 100644 index 00000000..bec17822 Binary files /dev/null and b/ICCAD16_openram_paper/figs/Freepdk_Read_Access_time.pdf differ diff --git a/ICCAD16_openram_paper/figs/Results.pdf b/ICCAD16_openram_paper/figs/Results.pdf new file mode 100644 index 00000000..c63a8ba6 Binary files /dev/null and b/ICCAD16_openram_paper/figs/Results.pdf differ diff --git a/ICCAD16_openram_paper/figs/Results2.eps b/ICCAD16_openram_paper/figs/Results2.eps new file mode 100644 index 00000000..f532a0ef --- /dev/null +++ b/ICCAD16_openram_paper/figs/Results2.eps @@ -0,0 +1,4599 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: (MATLAB, The Mathworks, Inc. Version 8.5.0.197613 \(R2015a\). Operating System: Mac OS X) +%%Title: /Users/stineje/Desktop/Results2.eps +%%CreationDate: 2015-11-23T09:50:58 +%%Pages: (atend) +%%BoundingBox: 0 0 512 937 +%%LanguageLevel: 2 +%%EndComments +%%BeginProlog +%%BeginResource: procset (Apache XML Graphics Std ProcSet) 1.2 0 +%%Version: 1.2 0 +%%Copyright: (Copyright 2001-2003,2010 The Apache Software Foundation. License terms: http://www.apache.org/licenses/LICENSE-2.0) +/bd{bind def}bind def +/ld{load def}bd +/GR/grestore ld +/M/moveto ld +/LJ/setlinejoin ld +/C/curveto ld +/f/fill ld +/LW/setlinewidth ld +/GC/setgray ld +/t/show ld +/N/newpath ld +/CT/concat ld +/cp/closepath ld +/S/stroke ld +/L/lineto ld +/CC/setcmykcolor ld +/A/ashow ld +/GS/gsave ld +/RC/setrgbcolor ld +/RM/rmoveto ld +/ML/setmiterlimit ld +/re {4 2 roll M +1 index 0 rlineto +0 exch rlineto +neg 0 rlineto +cp } bd +/_ctm matrix def +/_tm matrix def +/BT { _ctm currentmatrix pop matrix _tm copy pop 0 0 moveto } bd +/ET { _ctm setmatrix } bd +/iTm { _ctm setmatrix _tm concat } bd +/Tm { _tm astore pop iTm 0 0 moveto } bd +/ux 0.0 def +/uy 0.0 def +/F { + /Tp exch def + /Tf exch def + Tf findfont Tp scalefont setfont + /cf Tf def /cs Tp def +} bd +/ULS {currentpoint /uy exch def /ux exch def} bd +/ULE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add moveto Tcx uy To add lineto + Tt setlinewidth stroke + grestore +} bd +/OLE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add cs add moveto Tcx uy To add cs add lineto + Tt setlinewidth stroke + grestore +} bd +/SOE { + /Tcx currentpoint pop def + gsave + newpath + cf findfont cs scalefont dup + /FontMatrix get 0 get /Ts exch def /FontInfo get dup + /UnderlinePosition get Ts mul /To exch def + /UnderlineThickness get Ts mul /Tt exch def + ux uy To add cs 10 mul 26 idiv add moveto Tcx uy To add cs 10 mul 26 idiv add lineto + Tt setlinewidth stroke + grestore +} bd +/QT { +/Y22 exch store +/X22 exch store +/Y21 exch store +/X21 exch store +currentpoint +/Y21 load 2 mul add 3 div exch +/X21 load 2 mul add 3 div exch +/X21 load 2 mul /X22 load add 3 div +/Y21 load 2 mul /Y22 load add 3 div +/X22 load /Y22 load curveto +} bd +/SSPD { +dup length /d exch dict def +{ +/v exch def +/k exch def +currentpagedevice k known { +/cpdv currentpagedevice k get def +v cpdv ne { +/upd false def +/nullv v type /nulltype eq def +/nullcpdv cpdv type /nulltype eq def +nullv nullcpdv or +{ +/upd true def +} { +/sametype v type cpdv type eq def +sametype { +v type /arraytype eq { +/vlen v length def +/cpdvlen cpdv length def +vlen cpdvlen eq { +0 1 vlen 1 sub { +/i exch def +/obj v i get def +/cpdobj cpdv i get def +obj cpdobj ne { +/upd true def +exit +} if +} for +} { +/upd true def +} ifelse +} { +v type /dicttype eq { +v { +/dv exch def +/dk exch def +/cpddv cpdv dk get def +dv cpddv ne { +/upd true def +exit +} if +} forall +} { +/upd true def +} ifelse +} ifelse +} if +} ifelse +upd true eq { +d k v put +} if +} if +} if +} forall +d length 0 gt { +d setpagedevice +} if +} bd +%%EndResource +%%BeginResource: procset (Apache XML Graphics EPS ProcSet) 1.0 0 +%%Version: 1.0 0 +%%Copyright: (Copyright 2002-2003 The Apache Software Foundation. License terms: http://www.apache.org/licenses/LICENSE-2.0) +/BeginEPSF { %def +/b4_Inc_state save def % Save state for cleanup +/dict_count countdictstack def % Count objects on dict stack +/op_count count 1 sub def % Count objects on operand stack +userdict begin % Push userdict on dict stack +/showpage { } def % Redefine showpage, { } = null proc +0 setgray 0 setlinecap % Prepare graphics state +1 setlinewidth 0 setlinejoin +10 setmiterlimit [ ] 0 setdash newpath +/languagelevel where % If level not equal to 1 then +{pop languagelevel % set strokeadjust and +1 ne % overprint to their defaults. +{false setstrokeadjust false setoverprint +} if +} if +} bd +/EndEPSF { %def +count op_count sub {pop} repeat % Clean up stacks +countdictstack dict_count sub {end} repeat +b4_Inc_state restore +} bd +%%EndResource +%FOPBeginFontDict +%%IncludeResource: font Courier-Bold +%%IncludeResource: font Helvetica +%%IncludeResource: font Courier-BoldOblique +%%IncludeResource: font Courier-Oblique +%%IncludeResource: font Times-Roman +%%IncludeResource: font Helvetica-BoldOblique +%%IncludeResource: font Helvetica-Bold +%%IncludeResource: font Helvetica-Oblique +%%IncludeResource: font Times-BoldItalic +%%IncludeResource: font Courier +%%IncludeResource: font Times-Italic +%%IncludeResource: font Times-Bold +%%IncludeResource: font Symbol +%%IncludeResource: font ZapfDingbats +%FOPEndFontDict +%%BeginResource: encoding WinAnsiEncoding +/WinAnsiEncoding [ +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /.notdef /.notdef /.notdef +/.notdef /.notdef /space /exclam /quotedbl +/numbersign /dollar /percent /ampersand /quotesingle +/parenleft /parenright /asterisk /plus /comma +/hyphen /period /slash /zero /one +/two /three /four /five /six +/seven /eight /nine /colon /semicolon +/less /equal /greater /question /at +/A /B /C /D /E +/F /G /H /I /J +/K /L /M /N /O +/P /Q /R /S /T +/U /V /W /X /Y +/Z /bracketleft /backslash /bracketright /asciicircum +/underscore /quoteleft /a /b /c +/d /e /f /g /h +/i /j /k /l /m +/n /o /p /q /r +/s /t /u /v /w +/x /y /z /braceleft /bar +/braceright /asciitilde /bullet /Euro /bullet +/quotesinglbase /florin /quotedblbase /ellipsis /dagger +/daggerdbl /circumflex /perthousand /Scaron /guilsinglleft +/OE /bullet /Zcaron /bullet /bullet +/quoteleft /quoteright /quotedblleft /quotedblright /bullet +/endash /emdash /asciitilde /trademark /scaron +/guilsinglright /oe /bullet /zcaron /Ydieresis +/space /exclamdown /cent /sterling /currency +/yen /brokenbar /section /dieresis /copyright +/ordfeminine /guillemotleft /logicalnot /sfthyphen /registered +/macron /degree /plusminus /twosuperior /threesuperior +/acute /mu /paragraph /middot /cedilla +/onesuperior /ordmasculine /guillemotright /onequarter /onehalf +/threequarters /questiondown /Agrave /Aacute /Acircumflex +/Atilde /Adieresis /Aring /AE /Ccedilla +/Egrave /Eacute /Ecircumflex /Edieresis /Igrave +/Iacute /Icircumflex /Idieresis /Eth /Ntilde +/Ograve /Oacute /Ocircumflex /Otilde /Odieresis +/multiply /Oslash /Ugrave /Uacute /Ucircumflex +/Udieresis /Yacute /Thorn /germandbls /agrave +/aacute /acircumflex /atilde /adieresis /aring +/ae /ccedilla /egrave /eacute /ecircumflex +/edieresis /igrave /iacute /icircumflex /idieresis +/eth /ntilde /ograve /oacute /ocircumflex +/otilde /odieresis /divide /oslash /ugrave +/uacute /ucircumflex /udieresis /yacute /thorn +/ydieresis +] def +%%EndResource +%FOPBeginFontReencode +/Courier-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-Bold exch definefont pop +/Helvetica findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica exch definefont pop +/Courier-BoldOblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-BoldOblique exch definefont pop +/Courier-Oblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier-Oblique exch definefont pop +/Times-Roman findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Roman exch definefont pop +/Helvetica-BoldOblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-BoldOblique exch definefont pop +/Helvetica-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-Bold exch definefont pop +/Helvetica-Oblique findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Helvetica-Oblique exch definefont pop +/Times-BoldItalic findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-BoldItalic exch definefont pop +/Courier findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Courier exch definefont pop +/Times-Italic findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Italic exch definefont pop +/Times-Bold findfont +dup length dict begin + {1 index /FID ne {def} {pop pop} ifelse} forall + /Encoding WinAnsiEncoding def + currentdict +end +/Times-Bold exch definefont pop +%FOPEndFontReencode +%%EndProlog +%%Page: 1 1 +%%PageBoundingBox: 0 0 512 937 +%%BeginPageSetup +[1 0 0 -1 0 937] CT +%%EndPageSetup +GS +1 GC +N +0 0 512 937 re +f +GR +GS +1 GC +N +0 0 512 937 re +f +GR +GS +1 GC +N +67 218 M +463 218 L +463 70 L +67 70 L +cp +f +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +67 218 M +67 70 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +123.571 218 M +123.571 70 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +180.143 218 M +180.143 70 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +236.714 218 M +236.714 70 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +293.286 218 M +293.286 70 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +349.857 218 M +349.857 70 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +406.429 218 M +406.429 70 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 218 M +463 70 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 218 M +67 218 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 168.667 M +67 168.667 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 119.333 M +67 119.333 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 70 M +67 70 L +S +GR +GS +[1 0 0 1 265.00018 237.39999] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-46 11 moveto +1 -1 scale +(Total Size \(Kbits\)) t +GR +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 218 M +463 218 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 70 M +463 70 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 218 M +67 214.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +123.571 218 M +123.571 214.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +180.143 218 M +180.143 214.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +236.714 218 M +236.714 214.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +293.286 218 M +293.286 214.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +349.857 218 M +349.857 214.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +406.429 218 M +406.429 214.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 218 M +463 214.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 70 M +67 73.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +123.571 70 M +123.571 73.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +180.143 70 M +180.143 73.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +236.714 70 M +236.714 73.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +293.286 70 M +293.286 73.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +349.857 70 M +349.857 73.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +406.429 70 M +406.429 73.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 70 M +463 73.96 L +S +GR +GS +[1 0 0 1 67 222.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-3.5 11 moveto +1 -1 scale +(0) t +GR +GR +GS +[1 0 0 1 123.57143 222.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(20) t +GR +GR +GS +[1 0 0 1 180.14285 222.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(40) t +GR +GR +GS +[1 0 0 1 236.71428 222.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(60) t +GR +GR +GS +[1 0 0 1 293.28571 222.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(80) t +GR +GR +GS +[1 0 0 1 349.85715 222.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(100) t +GR +GR +GS +[1 0 0 1 406.42856 222.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(120) t +GR +GR +GS +[1 0 0 1 463 222.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(140) t +GR +GR +GS +[0 -1 1 0 40 176] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(Area \(mm) t +GR +GR +GS +[0 -1 1 0 34 123] CT +/Helvetica 9 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(2) t +GR +GR +GS +[0 -1 1 0 40 117] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(\)) t +GR +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 218 M +67 70 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 218 M +463 70 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 218 M +70.96 218 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 168.667 M +70.96 168.667 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 119.333 M +70.96 119.333 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 70 M +70.96 70 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 218 M +459.04 218 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 168.667 M +459.04 168.667 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 119.333 M +459.04 119.333 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 70 M +459.04 70 L +S +GR +GS +[1 0 0 1 62.6 218] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 4 moveto +1 -1 scale +(0) t +GR +GR +GS +[1 0 0 1 62.6 168.66667] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-17 4 moveto +1 -1 scale +(0.1) t +GR +GR +GS +[1 0 0 1 62.6 119.33333] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-17 4 moveto +1 -1 scale +(0.2) t +GR +GR +GS +[1 0 0 1 62.6 69.99999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-17 4 moveto +1 -1 scale +(0.3) t +GR +GR +GS +0 0 1 RC +[2 2] 0 setdash +2 LJ +2 LW +N +72.657 215.612 M +89.629 211.34 L +157.514 192.803 L +429.057 77.96 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +68.6 219.4 M +68.6 211.6 L +76.4 211.6 L +76.4 219.4 L +68.6 219.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +85.6 215.4 M +85.6 207.6 L +93.4 207.6 L +93.4 215.4 L +85.6 215.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +153.6 196.4 M +153.6 188.6 L +161.4 188.6 L +161.4 196.4 L +153.6 196.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +425.6 81.4 M +425.6 73.6 L +433.4 73.6 L +433.4 81.4 L +425.6 81.4 L +S +GR +GS +1 0 0 RC +[8 2 4 2] 0 setdash +2 LJ +2 LW +N +72.657 215.627 M +89.629 211.34 L +157.514 196.512 L +429.057 113.487 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 220 M +77 211 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 211 M +77 220 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 216 M +94 207 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 207 M +94 216 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 201 M +162 192 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 192 M +162 201 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 118 M +434 109 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 109 M +434 118 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +72.657 215.643 M +89.629 211.622 L +157.514 197.285 L +429.057 140.548 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +67 215.5 M +78 215.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +72.5 221 M +72.5 210 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 220 M +77 211 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 211 M +77 220 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +84 211.5 M +95 211.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +89.5 217 M +89.5 206 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 216 M +94 207 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 207 M +94 216 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +152 197.5 M +163 197.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +157.5 203 M +157.5 192 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 202 M +162 193 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 193 M +162 202 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +424 140.5 M +435 140.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +429.5 146 M +429.5 135 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 145 M +434 136 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 136 M +434 145 L +S +GR +GS +[8 8] 0 setdash +2 LJ +2 LW +N +72.657 215.39 M +89.629 211.646 L +157.514 197.328 L +429.057 143.377 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +77.5 215.5 M +76.545 212.561 L +74.045 210.745 L +70.955 210.745 L +68.455 212.561 L +67.5 215.5 L +68.455 218.439 L +70.955 220.255 L +74.045 220.255 L +76.545 218.439 L +77.5 215.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +94.5 211.5 M +93.545 208.561 L +91.045 206.745 L +87.955 206.745 L +85.455 208.561 L +84.5 211.5 L +85.455 214.439 L +87.955 216.255 L +91.045 216.255 L +93.545 214.439 L +94.5 211.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +162.5 197.5 M +161.545 194.561 L +159.045 192.745 L +155.955 192.745 L +153.455 194.561 L +152.5 197.5 L +153.455 200.439 L +155.955 202.255 L +159.045 202.255 L +161.545 200.439 L +162.5 197.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +434.5 143.5 M +433.545 140.561 L +431.045 138.745 L +427.955 138.745 L +425.455 140.561 L +424.5 143.5 L +425.455 146.439 L +427.955 148.255 L +431.045 148.255 L +433.545 146.439 L +434.5 143.5 L +S +GR +GS +1 GC +N +67 423 M +463 423 L +463 276 L +67 276 L +cp +f +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +67 423 M +67 276 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +123.571 423 M +123.571 276 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +180.143 423 M +180.143 276 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +236.714 423 M +236.714 276 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +293.286 423 M +293.286 276 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +349.857 423 M +349.857 276 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +406.429 423 M +406.429 276 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 423 M +463 276 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 423 M +67 423 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 393.6 M +67 393.6 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 364.2 M +67 364.2 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 334.8 M +67 334.8 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 305.4 M +67 305.4 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 276 M +67 276 L +S +GR +GS +[1 0 0 1 265.00018 442.39999] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-46 11 moveto +1 -1 scale +(Total Size \(Kbits\)) t +GR +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 423 M +463 423 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 276 M +463 276 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 423 M +67 419.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +123.571 423 M +123.571 419.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +180.143 423 M +180.143 419.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +236.714 423 M +236.714 419.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +293.286 423 M +293.286 419.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +349.857 423 M +349.857 419.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +406.429 423 M +406.429 419.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 423 M +463 419.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 276 M +67 279.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +123.571 276 M +123.571 279.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +180.143 276 M +180.143 279.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +236.714 276 M +236.714 279.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +293.286 276 M +293.286 279.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +349.857 276 M +349.857 279.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +406.429 276 M +406.429 279.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 276 M +463 279.96 L +S +GR +GS +[1 0 0 1 67 427.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-3.5 11 moveto +1 -1 scale +(0) t +GR +GR +GS +[1 0 0 1 123.57143 427.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(20) t +GR +GR +GS +[1 0 0 1 180.14285 427.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(40) t +GR +GR +GS +[1 0 0 1 236.71428 427.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(60) t +GR +GR +GS +[1 0 0 1 293.28571 427.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(80) t +GR +GR +GS +[1 0 0 1 349.85715 427.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(100) t +GR +GR +GS +[1 0 0 1 406.42856 427.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(120) t +GR +GR +GS +[1 0 0 1 463 427.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(140) t +GR +GR +GS +[0 -1 1 0 43 381] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(Area \(mm) t +GR +GR +GS +[0 -1 1 0 37 328] CT +/Helvetica 9 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(2) t +GR +GR +GS +[0 -1 1 0 43 322] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +0 0 moveto +1 -1 scale +(\)) t +GR +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 423 M +67 276 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 423 M +463 276 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 423 M +70.96 423 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 393.6 M +70.96 393.6 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 364.2 M +70.96 364.2 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 334.8 M +70.96 334.8 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 305.4 M +70.96 305.4 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 276 M +70.96 276 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 423 M +459.04 423 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 393.6 M +459.04 393.6 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 364.2 M +459.04 364.2 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 334.8 M +459.04 334.8 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 305.4 M +459.04 305.4 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 276 M +459.04 276 L +S +GR +GS +[1 0 0 1 62.6 423] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 4 moveto +1 -1 scale +(0) t +GR +GR +GS +[1 0 0 1 62.6 393.60001] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(10) t +GR +GR +GS +[1 0 0 1 62.6 364.20001] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(20) t +GR +GR +GS +[1 0 0 1 62.6 334.79999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(30) t +GR +GR +GS +[1 0 0 1 62.6 305.39999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(40) t +GR +GR +GS +[1 0 0 1 62.6 276] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(50) t +GR +GR +GS +0 0 1 RC +[2 2] 0 setdash +2 LJ +2 LW +N +72.657 420.787 M +89.629 416.86 L +157.514 399.794 L +429.057 303.362 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +68.6 424.4 M +68.6 416.6 L +76.4 416.6 L +76.4 424.4 L +68.6 424.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +85.6 420.4 M +85.6 412.6 L +93.4 412.6 L +93.4 420.4 L +85.6 420.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +153.6 403.4 M +153.6 395.6 L +161.4 395.6 L +161.4 403.4 L +153.6 403.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +425.6 307.4 M +425.6 299.6 L +433.4 299.6 L +433.4 307.4 L +425.6 307.4 L +S +GR +GS +1 0 0 RC +[8 2 4 2] 0 setdash +2 LJ +2 LW +N +72.657 420.8 M +89.629 417.056 L +157.514 402.995 L +429.057 329.987 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 425 M +77 416 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 416 M +77 425 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 422 M +94 413 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 413 M +94 422 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 407 M +162 398 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 398 M +162 407 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 334 M +434 325 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 325 M +434 334 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +72.657 420.814 M +89.629 417.077 L +157.514 403.509 L +429.057 350.546 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +67 420.5 M +78 420.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +72.5 426 M +72.5 415 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 425 M +77 416 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 416 M +77 425 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +84 417.5 M +95 417.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +89.5 423 M +89.5 412 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 422 M +94 413 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 413 M +94 422 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +152 403.5 M +163 403.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +157.5 409 M +157.5 398 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 408 M +162 399 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 399 M +162 408 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +424 350.5 M +435 350.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +429.5 356 M +429.5 345 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 355 M +434 346 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 346 M +434 355 L +S +GR +GS +[8 8] 0 setdash +2 LJ +2 LW +N +72.657 420.54 M +89.629 417.098 L +157.514 403.546 L +429.057 352.912 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +77.5 420.5 M +76.545 417.561 L +74.045 415.745 L +70.955 415.745 L +68.455 417.561 L +67.5 420.5 L +68.455 423.439 L +70.955 425.255 L +74.045 425.255 L +76.545 423.439 L +77.5 420.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +94.5 417.5 M +93.545 414.561 L +91.045 412.745 L +87.955 412.745 L +85.455 414.561 L +84.5 417.5 L +85.455 420.439 L +87.955 422.255 L +91.045 422.255 L +93.545 420.439 L +94.5 417.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +162.5 403.5 M +161.545 400.561 L +159.045 398.745 L +155.955 398.745 L +153.455 400.561 L +152.5 403.5 L +153.455 406.439 L +155.955 408.255 L +159.045 408.255 L +161.545 406.439 L +162.5 403.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +434.5 352.5 M +433.545 349.561 L +431.045 347.745 L +427.955 347.745 L +425.455 349.561 L +424.5 352.5 L +425.455 355.439 L +427.955 357.255 L +431.045 357.255 L +433.545 355.439 L +434.5 352.5 L +S +GR +GS +1 GC +N +67 629 M +463 629 L +463 481 L +67 481 L +cp +f +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +67 629 M +67 481 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +123.571 629 M +123.571 481 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +180.143 629 M +180.143 481 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +236.714 629 M +236.714 481 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +293.286 629 M +293.286 481 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +349.857 629 M +349.857 481 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +406.429 629 M +406.429 481 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 629 M +463 481 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 629 M +67 629 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 599.4 M +67 599.4 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 569.8 M +67 569.8 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 540.2 M +67 540.2 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 510.6 M +67 510.6 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 481 M +67 481 L +S +GR +GS +[1 0 0 1 265.00018 648.40002] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-46 11 moveto +1 -1 scale +(Total Size \(Kbits\)) t +GR +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 629 M +463 629 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 481 M +463 481 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 629 M +67 625.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +123.571 629 M +123.571 625.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +180.143 629 M +180.143 625.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +236.714 629 M +236.714 625.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +293.286 629 M +293.286 625.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +349.857 629 M +349.857 625.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +406.429 629 M +406.429 625.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 629 M +463 625.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 481 M +67 484.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +123.571 481 M +123.571 484.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +180.143 481 M +180.143 484.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +236.714 481 M +236.714 484.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +293.286 481 M +293.286 484.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +349.857 481 M +349.857 484.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +406.429 481 M +406.429 484.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 481 M +463 484.96 L +S +GR +GS +[1 0 0 1 67 633.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-3.5 11 moveto +1 -1 scale +(0) t +GR +GR +GS +[1 0 0 1 123.57143 633.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(20) t +GR +GR +GS +[1 0 0 1 180.14285 633.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(40) t +GR +GR +GS +[1 0 0 1 236.71428 633.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(60) t +GR +GR +GS +[1 0 0 1 293.28571 633.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(80) t +GR +GR +GS +[1 0 0 1 349.85715 633.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(100) t +GR +GR +GS +[1 0 0 1 406.42856 633.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(120) t +GR +GR +GS +[1 0 0 1 463 633.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(140) t +GR +GR +GS +[0 -1 1 0 45.6 554.99994] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-44.5 -3 moveto +1 -1 scale +(Access time \(ns\)) t +GR +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 629 M +67 481 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 629 M +463 481 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 629 M +70.96 629 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 599.4 M +70.96 599.4 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 569.8 M +70.96 569.8 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 540.2 M +70.96 540.2 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 510.6 M +70.96 510.6 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 481 M +70.96 481 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 629 M +459.04 629 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 599.4 M +459.04 599.4 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 569.8 M +459.04 569.8 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 540.2 M +459.04 540.2 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 510.6 M +459.04 510.6 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 481 M +459.04 481 L +S +GR +GS +[1 0 0 1 62.6 629] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 4 moveto +1 -1 scale +(0) t +GR +GR +GS +[1 0 0 1 62.6 599.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 4 moveto +1 -1 scale +(2) t +GR +GR +GS +[1 0 0 1 62.6 569.79999] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 4 moveto +1 -1 scale +(4) t +GR +GR +GS +[1 0 0 1 62.6 540.20001] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 4 moveto +1 -1 scale +(6) t +GR +GR +GS +[1 0 0 1 62.6 510.60001] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 4 moveto +1 -1 scale +(8) t +GR +GR +GS +[1 0 0 1 62.6 481] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(10) t +GR +GR +GS +0 0 1 RC +[2 2] 0 setdash +2 LJ +2 LW +N +72.657 616.257 M +89.629 609.464 L +157.514 602.36 L +429.057 596.44 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +68.6 620.4 M +68.6 612.6 L +76.4 612.6 L +76.4 620.4 L +68.6 620.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +85.6 613.4 M +85.6 605.6 L +93.4 605.6 L +93.4 613.4 L +85.6 613.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +153.6 606.4 M +153.6 598.6 L +161.4 598.6 L +161.4 606.4 L +153.6 606.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +425.6 600.4 M +425.6 592.6 L +433.4 592.6 L +433.4 600.4 L +425.6 600.4 L +S +GR +GS +1 0 0 RC +[8 2 4 2] 0 setdash +2 LJ +2 LW +N +72.657 613.904 M +89.629 609.316 L +157.514 601.916 L +429.057 590.52 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 618 M +77 609 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 609 M +77 618 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 614 M +94 605 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 605 M +94 614 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 606 M +162 597 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 597 M +162 606 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 595 M +434 586 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 586 M +434 595 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +72.657 616.272 M +89.629 606.8 L +157.514 600.88 L +429.057 529.1 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +67 616.5 M +78 616.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +72.5 622 M +72.5 611 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 621 M +77 612 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 612 M +77 621 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +84 606.5 M +95 606.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +89.5 612 M +89.5 601 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 611 M +94 602 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 602 M +94 611 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +152 600.5 M +163 600.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +157.5 606 M +157.5 595 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 605 M +162 596 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 596 M +162 605 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +424 529.5 M +435 529.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +429.5 535 M +429.5 524 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 534 M +434 525 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 525 M +434 534 L +S +GR +GS +[8 8] 0 setdash +2 LJ +2 LW +N +72.657 613.075 M +89.629 609.168 L +157.514 599.252 L +429.057 483.072 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +77.5 613.5 M +76.545 610.561 L +74.045 608.745 L +70.955 608.745 L +68.455 610.561 L +67.5 613.5 L +68.455 616.439 L +70.955 618.255 L +74.045 618.255 L +76.545 616.439 L +77.5 613.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +94.5 609.5 M +93.545 606.561 L +91.045 604.745 L +87.955 604.745 L +85.455 606.561 L +84.5 609.5 L +85.455 612.439 L +87.955 614.255 L +91.045 614.255 L +93.545 612.439 L +94.5 609.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +162.5 599.5 M +161.545 596.561 L +159.045 594.745 L +155.955 594.745 L +153.455 596.561 L +152.5 599.5 L +153.455 602.439 L +155.955 604.255 L +159.045 604.255 L +161.545 602.439 L +162.5 599.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +434.5 483.5 M +433.545 480.561 L +431.045 478.745 L +427.955 478.745 L +425.455 480.561 L +424.5 483.5 L +425.455 486.439 L +427.955 488.255 L +431.045 488.255 L +433.545 486.439 L +434.5 483.5 L +S +GR +GS +1 GC +N +67 834 M +463 834 L +463 686 L +67 686 L +cp +f +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +67 834 M +67 686 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +123.571 834 M +123.571 686 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +180.143 834 M +180.143 686 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +236.714 834 M +236.714 686 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +293.286 834 M +293.286 686 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +349.857 834 M +349.857 686 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +406.429 834 M +406.429 686 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 834 M +463 686 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 834 M +67 834 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 807.091 M +67 807.091 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 780.182 M +67 780.182 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 753.273 M +67 753.273 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 726.364 M +67 726.364 L +S +GR +GS +0.873 GC +2 setlinecap +10.0 ML +N +463 699.455 M +67 699.455 L +S +GR +GS +[1 0 0 1 265.00018 853.40002] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-46 11 moveto +1 -1 scale +(Total Size \(Kbits\)) t +GR +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 834 M +463 834 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 686 M +463 686 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 834 M +67 830.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +123.571 834 M +123.571 830.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +180.143 834 M +180.143 830.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +236.714 834 M +236.714 830.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +293.286 834 M +293.286 830.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +349.857 834 M +349.857 830.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +406.429 834 M +406.429 830.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 834 M +463 830.04 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 686 M +67 689.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +123.571 686 M +123.571 689.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +180.143 686 M +180.143 689.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +236.714 686 M +236.714 689.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +293.286 686 M +293.286 689.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +349.857 686 M +349.857 689.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +406.429 686 M +406.429 689.96 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 686 M +463 689.96 L +S +GR +GS +[1 0 0 1 67 838.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-3.5 11 moveto +1 -1 scale +(0) t +GR +GR +GS +[1 0 0 1 123.57143 838.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(20) t +GR +GR +GS +[1 0 0 1 180.14285 838.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(40) t +GR +GR +GS +[1 0 0 1 236.71428 838.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(60) t +GR +GR +GS +[1 0 0 1 293.28571 838.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 11 moveto +1 -1 scale +(80) t +GR +GR +GS +[1 0 0 1 349.85715 838.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(100) t +GR +GR +GS +[1 0 0 1 406.42856 838.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(120) t +GR +GR +GS +[1 0 0 1 463 838.40002] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-10.5 11 moveto +1 -1 scale +(140) t +GR +GR +GS +[0 -1 1 0 45.6 759.99994] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-44.5 -3 moveto +1 -1 scale +(Access time \(ns\)) t +GR +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 834 M +67 686 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 834 M +463 686 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 834 M +70.96 834 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 807.091 M +70.96 807.091 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 780.182 M +70.96 780.182 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 753.273 M +70.96 753.273 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 726.364 M +70.96 726.364 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +67 699.455 M +70.96 699.455 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 834 M +459.04 834 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 807.091 M +459.04 807.091 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 780.182 M +459.04 780.182 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 753.273 M +459.04 753.273 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 726.364 M +459.04 726.364 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +N +463 699.455 M +459.04 699.455 L +S +GR +GS +[1 0 0 1 62.6 834] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-7 4 moveto +1 -1 scale +(0) t +GR +GR +GS +[1 0 0 1 62.6 807.09088] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(10) t +GR +GR +GS +[1 0 0 1 62.6 780.18182] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(20) t +GR +GR +GS +[1 0 0 1 62.6 753.27271] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(30) t +GR +GR +GS +[1 0 0 1 62.6 726.36365] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(40) t +GR +GR +GS +[1 0 0 1 62.6 699.45453] CT +0.149 GC +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +-14 4 moveto +1 -1 scale +(50) t +GR +GR +GS +0 0 1 RC +[2 2] 0 setdash +2 LJ +2 LW +N +72.657 808.652 M +89.629 811.8 L +157.514 802.543 L +429.057 799.825 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +68.6 812.4 M +68.6 804.6 L +76.4 804.6 L +76.4 812.4 L +68.6 812.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +85.6 815.4 M +85.6 807.6 L +93.4 807.6 L +93.4 815.4 L +85.6 815.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +153.6 806.4 M +153.6 798.6 L +161.4 798.6 L +161.4 806.4 L +153.6 806.4 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +425.6 803.4 M +425.6 795.6 L +433.4 795.6 L +433.4 803.4 L +425.6 803.4 L +S +GR +GS +1 0 0 RC +[8 2 4 2] 0 setdash +2 LJ +2 LW +N +72.657 802.193 M +89.629 790.838 L +157.514 794.444 L +429.057 784.999 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 807 M +77 798 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 798 M +77 807 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 795 M +94 786 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 786 M +94 795 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 799 M +162 790 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 790 M +162 799 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 789 M +434 780 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 780 M +434 789 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +72.657 794.148 M +89.629 782.227 L +157.514 769.903 L +429.057 752.6 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +67 794.5 M +78 794.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +72.5 800 M +72.5 789 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 799 M +77 790 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +68 790 M +77 799 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +84 782.5 M +95 782.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +89.5 788 M +89.5 777 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 787 M +94 778 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +85 778 M +94 787 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +152 769.5 M +163 769.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +157.5 775 M +157.5 764 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 774 M +162 765 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +153 765 M +162 774 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +424 752.5 M +435 752.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +429.5 758 M +429.5 747 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 757 M +434 748 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +425 748 M +434 757 L +S +GR +GS +[8 8] 0 setdash +2 LJ +2 LW +N +72.657 772.378 M +89.629 771.786 L +157.514 751.255 L +429.057 713.044 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +77.5 772.5 M +76.545 769.561 L +74.045 767.745 L +70.955 767.745 L +68.455 769.561 L +67.5 772.5 L +68.455 775.439 L +70.955 777.255 L +74.045 777.255 L +76.545 775.439 L +77.5 772.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +94.5 771.5 M +93.545 768.561 L +91.045 766.745 L +87.955 766.745 L +85.455 768.561 L +84.5 771.5 L +85.455 774.439 L +87.955 776.255 L +91.045 776.255 L +93.545 774.439 L +94.5 771.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +162.5 751.5 M +161.545 748.561 L +159.045 746.745 L +155.955 746.745 L +153.455 748.561 L +152.5 751.5 L +153.455 754.439 L +155.955 756.255 L +159.045 756.255 L +161.545 754.439 L +162.5 751.5 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +434.5 713.5 M +433.545 710.561 L +431.045 708.745 L +427.955 708.745 L +425.455 710.561 L +424.5 713.5 L +425.455 716.439 L +427.955 718.255 L +431.045 718.255 L +433.545 716.439 L +434.5 713.5 L +S +GR +GS +1 GC +N +189 64 M +189 2 L +322 2 L +322 64 L +cp +f +GR +GS +[1 0 0 1 228 11.07317] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +0 4 moveto +1 -1 scale +(16-bit word size) t +GR +GR +GS +0 0 1 RC +[2 2] 0 setdash +2 LJ +2 LW +N +195 11.073 M +225 11.073 L +S +GR +GS +0 0 1 RC +2 setlinecap +10.0 ML +2 LW +N +205.6 15.4 M +205.6 7.6 L +213.4 7.6 L +213.4 15.4 L +205.6 15.4 L +S +GR +GS +[1 0 0 1 228 25.69106] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +0 4 moveto +1 -1 scale +(32-bit word size) t +GR +GR +GS +1 0 0 RC +[8 2 4 2] 0 setdash +2 LJ +2 LW +N +195 25.691 M +225 25.691 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +205 30 M +214 21 L +S +GR +GS +1 0 0 RC +2 setlinecap +10.0 ML +2 LW +N +205 21 M +214 30 L +S +GR +GS +[1 0 0 1 228 40.30894] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +0 4 moveto +1 -1 scale +(64-bit word size) t +GR +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +195 40.309 M +225 40.309 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +204 40.5 M +215 40.5 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +209.5 46 M +209.5 35 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +205 45 M +214 36 L +S +GR +GS +0 1 0 RC +2 setlinecap +10.0 ML +2 LW +N +205 36 M +214 45 L +S +GR +GS +[1 0 0 1 228 54.92683] CT +/Helvetica 12 F +GS +[1 0 0 1 0 0] CT +0 4 moveto +1 -1 scale +(128-bit word size) t +GR +GR +GS +[8 8] 0 setdash +2 LJ +2 LW +N +195 54.927 M +225 54.927 L +S +GR +GS +2 setlinecap +10.0 ML +2 LW +N +214.5 54.5 M +213.545 51.561 L +211.045 49.745 L +207.955 49.745 L +205.455 51.561 L +204.5 54.5 L +205.455 57.439 L +207.955 59.255 L +211.045 59.255 L +213.545 57.439 L +214.5 54.5 L +S +GR +GS +0.149 GC +2 setlinecap +10.0 ML +1.2 LW +N +189 64 M +189 2 L +322 2 L +322 64 L +189 64 L +S +GR +GS +[1 0 0 1 83.37605 287.43033] CT +/Helvetica-Bold 12 F +GS +[1 0 0 1 0 0] CT +0 11 moveto +1 -1 scale +(SCMOS) t +GR +GR +GS +[1 0 0 1 84.0391 493.55234] CT +/Helvetica-Bold 12 F +GS +[1 0 0 1 0 0] CT +0 11 moveto +1 -1 scale +(Freepdk45) t +GR +GR +GS +[1 0 0 1 83.6773 708.67529] CT +/Helvetica-Bold 12 F +GS +[1 0 0 1 0 0] CT +0 11 moveto +1 -1 scale +(SCMOS) t +GR +GR +GS +[1 0 0 1 84.1773 79.50815] CT +/Helvetica-Bold 12 F +GS +[1 0 0 1 0 0] CT +0 11 moveto +1 -1 scale +(Freepdk45) t +GR +GR +%%Trailer +%%Pages: 1 +%%EOF diff --git a/ICCAD16_openram_paper/figs/Results2.fig b/ICCAD16_openram_paper/figs/Results2.fig new file mode 100644 index 00000000..11eff33c Binary files /dev/null and b/ICCAD16_openram_paper/figs/Results2.fig differ diff --git a/ICCAD16_openram_paper/figs/Results2.m b/ICCAD16_openram_paper/figs/Results2.m new file mode 100644 index 00000000..250bf24a --- /dev/null +++ b/ICCAD16_openram_paper/figs/Results2.m @@ -0,0 +1,55 @@ +clc; + +X = [ 2 , 8 , 32, 128]; +Y1_Freepdk =[0.0048413475625; 0.0135007585125;0.051075243575; 0.283865472]; +Y2_Freepdk=[0.0048094391125; 0.0135007585125; 0.0435568917; 0.2118510735]; +Y3_Freepdk=[0.0047775306625; 0.0129289229375; 0.0419903161125; 0.156997489175]; +Y4_Freepdk=[0.0052897701375; 0.0128789376875; 0.0419019176625; 0.1512635205]; + + +Y1_SCN3ME =[0.75276018; 2.08835631; 7.89312366; 40.6931238]; +Y2_SCN3ME =[0.74817639; 2.0216187; 6.804401625; 31.6371744]; +Y3_SCN3ME =[0.7435926; 2.01449475; 6.62959215; 24.64420014]; +Y4_SCN3ME =[0.83660283; 2.0073708; 6.61707036; 23.839544025]; + +Y1_T_Freepdk =[0.861; 1.32; 1.8; 2.2]; +Y2_T_Freepdk =[1.02; 1.33; 1.83; 2.6]; +Y3_T_Freepdk =[0.86; 1.5; 1.9; 6.75]; +Y4_T_Freepdk =[1.076; 1.34; 2.01; 9.86]; + +Y1_T_SCN3ME =[9.42; 8.25; 11.69; 12.7]; +Y2_T_SCN3ME =[11.82; 16.04; 14.7; 18.21]; +Y3_T_SCN3ME =[14.81; 19.24; 23.82; 30.25]; +Y4_T_SCN3ME =[22.9; 23.12; 30.75; 44.95]; + + +subplot(4,1,1) +plot (X, Y1_Freepdk, X, Y2_Freepdk, X, Y3_Freepdk, X, Y4_Freepdk,'LineWidth',2); +grid on; +ylabel('Area (mm^2)','FontSize',12, 'Color','k'); +xlabel('Total Size (Kbits)','FontSize',12, 'Color','k'); +subplot(4,1,2) +plot (X, Y1_SCN3ME, X, Y2_SCN3ME, X, Y3_SCN3ME, X, Y4_SCN3ME,'LineWidth',2); +grid on; +ylabel('Area (mm^2)','FontSize',12, 'Color','k'); +xlabel('Total Size (Kbits)','FontSize',12, 'Color','k'); +subplot(4,1,3) +plot (X, Y1_T_Freepdk, X, Y2_T_Freepdk, X, Y3_T_Freepdk, X, Y4_T_Freepdk,'LineWidth',2); +grid on; +ylabel('Access time (ns)','FontSize',12, 'Color','k'); +xlabel('Total Size (Kbits)','FontSize',12, 'Color','k'); +subplot(4,1,4) +plot (X, Y1_T_SCN3ME, X, Y2_T_SCN3ME, X, Y3_T_SCN3ME, X, Y4_T_SCN3ME,'LineWidth',2); +ylabel('Access time (ns)','FontSize',12, 'Color','k'); +xlabel('Total Size (Kbits)','FontSize',12, 'Color','k'); + + +grid on; +legend({'16-bit word size', '32-bit word size','64-bit word size', '128-bit word size'},'Location','northwest','orientation', 'vertical' , 'FontSize',12, 'LineWidth',1.2); + + + + + + + \ No newline at end of file diff --git a/ICCAD16_openram_paper/figs/Results2.pdf b/ICCAD16_openram_paper/figs/Results2.pdf new file mode 100644 index 00000000..19ad9558 Binary files /dev/null and b/ICCAD16_openram_paper/figs/Results2.pdf differ diff --git a/ICCAD16_openram_paper/figs/Scn3me_Area.pdf b/ICCAD16_openram_paper/figs/Scn3me_Area.pdf new file mode 100644 index 00000000..af4f2eaf Binary files /dev/null and b/ICCAD16_openram_paper/figs/Scn3me_Area.pdf differ diff --git a/ICCAD16_openram_paper/figs/Scn3me_Read_Access_time.pdf b/ICCAD16_openram_paper/figs/Scn3me_Read_Access_time.pdf new file mode 100644 index 00000000..39aa1c89 Binary files /dev/null and b/ICCAD16_openram_paper/figs/Scn3me_Read_Access_time.pdf differ diff --git a/ICCAD16_openram_paper/figs/density_data/f_plot.gp b/ICCAD16_openram_paper/figs/density_data/f_plot.gp new file mode 100644 index 00000000..9bb5981f --- /dev/null +++ b/ICCAD16_openram_paper/figs/density_data/f_plot.gp @@ -0,0 +1,14 @@ +set terminal pdf dashed +set output "../Freepdk_Area.pdf" +set palette color +set xlabel "Total Size (Kbit)" +set ylabel "Area (mm^2)" +set key below +plot 'freepdk45_size.dat' using ($1/1024):($2/1e6) with line title '16-bit word' lt 0 lw 5 lc 0 ,\ + 'freepdk45_size.dat' using ($1/1024):($2/1e6) with points title '' lt 0 lw 5 lc 0 ,\ + 'freepdk45_size.dat' using ($1/1024):($3/1e6) with line title '32-bit word' lt 1 lw 5 lc 1 ,\ + 'freepdk45_size.dat' using ($1/1024):($3/1e6) with points title '' lt 1 lw 5 lc 1 ,\ + 'freepdk45_size.dat' using ($1/1024):($4/1e6) with line title '64-bit word' lt 2 lw 5 lc 2 ,\ + 'freepdk45_size.dat' using ($1/1024):($4/1e6) with points title '' lt 2 lw 5 lc 2 ,\ + 'freepdk45_size.dat' using ($1/1024):($5/1e6) with line title '128-bit word' lt 3 lw 5 lc 3 ,\ + 'freepdk45_size.dat' using ($1/1024):($5/1e6) with points title '' lt 3 lw 5 lc 3 diff --git a/ICCAD16_openram_paper/figs/density_data/freepdk45_size.dat b/ICCAD16_openram_paper/figs/density_data/freepdk45_size.dat new file mode 100644 index 00000000..305ba87c --- /dev/null +++ b/ICCAD16_openram_paper/figs/density_data/freepdk45_size.dat @@ -0,0 +1,7 @@ + +2048 4841.3475625 4809.4391125 4777.5306625 5289.7701375 +8192 13500.7585125 12978.9081875 12928.9229375 12878.9376875 +#16384 25605.7869 22997.2777125 22908.8792625 22820.4808125 +32768 51075.243575 43556.8917 41990.3161125 41901.9176625 +#65536 142381.5903 86382.658775 79459.1013 79292.00325 +131072 283865.472 211851.0735 156997.489175 151263.5205 diff --git a/ICCAD16_openram_paper/figs/density_data/s_plot.gp b/ICCAD16_openram_paper/figs/density_data/s_plot.gp new file mode 100644 index 00000000..01e4d0eb --- /dev/null +++ b/ICCAD16_openram_paper/figs/density_data/s_plot.gp @@ -0,0 +1,14 @@ +set terminal pdf dashed +set output "../Scn3me_Area.pdf" +set palette color +set xlabel "Total Size (Kbit)" +set ylabel "Area (mm^2)" +set key below +plot 'scn3me_size.dat' using ($1/1024):($2/1e6) with line title '16-bit word' lt 0 lw 5 lc 0 ,\ + 'scn3me_size.dat' using ($1/1024):($2/1e6) with points title '' lt 0 lw 5 lc 0 ,\ + 'scn3me_size.dat' using ($1/1024):($3/1e6) with line title '32-bit word' lt 1 lw 5 lc 1 ,\ + 'scn3me_size.dat' using ($1/1024):($3/1e6) with points title '' lt 1 lw 5 lc 1 ,\ + 'scn3me_size.dat' using ($1/1024):($4/1e6) with line title '64-bit word' lt 2 lw 5 lc 2 ,\ + 'scn3me_size.dat' using ($1/1024):($4/1e6) with points title '' lt 2 lw 5 lc 2 ,\ + 'scn3me_size.dat' using ($1/1024):($5/1e6) with line title '128-bit word' lt 3 lw 5 lc 3 ,\ + 'scn3me_size.dat' using ($1/1024):($5/1e6) with points title '' lt 3 lw 5 lc 3 diff --git a/ICCAD16_openram_paper/figs/density_data/scn3me_size.dat b/ICCAD16_openram_paper/figs/density_data/scn3me_size.dat new file mode 100644 index 00000000..91d5163f --- /dev/null +++ b/ICCAD16_openram_paper/figs/density_data/scn3me_size.dat @@ -0,0 +1,5 @@ + +2048 752760.18 748176.39 743592.6 836602.83 +8192 2088356.31 2021618.7 2014494.75 2007370.8 +32768 7893123.66 6804401.625 6629592.15 6617070.36 +131072 40693123.8 31637174.4 24644200.14 23839544.025 diff --git a/ICCAD16_openram_paper/figs/density_delay_plot.gp b/ICCAD16_openram_paper/figs/density_delay_plot.gp new file mode 100644 index 00000000..b62153bd --- /dev/null +++ b/ICCAD16_openram_paper/figs/density_delay_plot.gp @@ -0,0 +1,100 @@ +#!/usr/bin/gnuplot +# +# Demonstration of a common use scenario of the multiplot environment. +# +# AUTHOR: Hagen Wierstorf +# + +reset + +set terminal pdf dashed size 8cm,12cm +set output "Results.pdf" +set palette color + +unset key + +# Enable the use of macros +set macros + +# MACROS +# Margins for each row resp. column +# top of top fig, bottom of top fig +TMARGIN = "set tmargin at screen 0.9; set bmargin at screen 0.575" +# top of lower fig, bottom of lower fig +BMARGIN = "set tmargin at screen 0.525; set bmargin at screen 0.15" +# left of left fig, right of left fig +LMARGIN = "set lmargin at screen 0.1; set rmargin at screen 0.48" +# left point of right fig ,right most +RMARGIN = "set lmargin at screen 0.52; set rmargin at screen 0.9" + +# Placement of the a,b,c,d labels in the graphs +POSA = "at graph 0.6,0.2 font ',5'" +POSB = "at graph 0.5,0.2 font ',5'" + +### Start multiplot (2x2 layout) +set multiplot layout 4,1 +# --- GRAPH a +set key outside center vertical top box 3 +set lmargin at screen 0.2; set rmargin at screen 0.9 +set tmargin at screen 0.88; set bmargin at screen 0.68 +#@TMARGIN; @LMARGIN +#@NOXTICS; @YTICS +set label 1 '45nm Area' @POSA +set ylabel "mm^2" +plot 'density_data/freepdk45_size.dat' using ($1/1024):($2/1e6) with line axis x1y1 title '16-bit word size' lt 0 lw 5 lc 0 ,\ + 'density_data/freepdk45_size.dat' using ($1/1024):($2/1e6) with points axis x1y1 title '' lt 0 lw 5 lc 0 ,\ + 'density_data/freepdk45_size.dat' using ($1/1024):($3/1e6) with line axis x1y1 title '32-bit word size' lt 1 lw 5 lc 1 ,\ + 'density_data/freepdk45_size.dat' using ($1/1024):($3/1e6) with points axis x1y1 title '' lt 1 lw 5 lc 1 ,\ + 'density_data/freepdk45_size.dat' using ($1/1024):($4/1e6) with line axis x1y1 title '64-bit word size' lt 2 lw 5 lc 2 ,\ + 'density_data/freepdk45_size.dat' using ($1/1024):($4/1e6) with points axis x1y1 title '' lt 2 lw 5 lc 2 ,\ + 'density_data/freepdk45_size.dat' using ($1/1024):($5/1e6) with line axis x1y1 title '128-bit word size' lt 3 lw 5 lc 3 ,\ + 'density_data/freepdk45_size.dat' using ($1/1024):($5/1e6) with points axis x1y1 title '' lt 3 lw 5 lc 3 + +# --- GRAPH b +unset key +set tmargin at screen 0.68; set bmargin at screen 0.48 +#@TMARGIN; @RMARGIN +#@NOXTICS; @NOYTICS +set label 1 '180nm Area' @POSA +set ylabel "mm^2" +plot 'density_data/scn3me_size.dat' using ($1/1024):($2/1e6) with line axis x1y1 title '16-bit word size' lt 0 lw 5 lc 0 ,\ + 'density_data/scn3me_size.dat' using ($1/1024):($2/1e6) with points axis x1y1 title '' lt 0 lw 5 lc 0 ,\ + 'density_data/scn3me_size.dat' using ($1/1024):($3/1e6) with line axis x1y1 title '32-bit word size' lt 1 lw 5 lc 1 ,\ + 'density_data/scn3me_size.dat' using ($1/1024):($3/1e6) with points axis x1y1 title '' lt 1 lw 5 lc 1 ,\ + 'density_data/scn3me_size.dat' using ($1/1024):($4/1e6) with line axis x1y1 title '64-bit word size' lt 2 lw 5 lc 2 ,\ + 'density_data/scn3me_size.dat' using ($1/1024):($4/1e6) with points axis x1y1 title '' lt 2 lw 5 lc 2 ,\ + 'density_data/scn3me_size.dat' using ($1/1024):($5/1e6) with line axis x1y1 title '128-bit word size' lt 3 lw 5 lc 3 ,\ + 'density_data/scn3me_size.dat' using ($1/1024):($5/1e6) with points axis x1y1 title '' lt 3 lw 5 lc 3 + +# --- GRAPH c +set tmargin at screen 0.48; set bmargin at screen 0.28 +#@BMARGIN; @LMARGIN +#@XTICS; @YTICS +set label 1 '45nm Access time' @POSB +set ylabel "ns" +plot 'timing_data/freepdk45_timing.dat' using ($1/1024):2 with line axis x1y2 title '16-bit word' lt 0 lw 5 lc 0 ,\ + 'timing_data/freepdk45_timing.dat' using ($1/1024):2 with points axis x1y2 title '' lt 0 lw 5 lc 0 ,\ + 'timing_data/freepdk45_timing.dat' using ($1/1024):3 with line axis x1y2 title '32-bit word' lt 1 lw 5 lc 1 ,\ + 'timing_data/freepdk45_timing.dat' using ($1/1024):3 with points axis x1y2 title '' lt 1 lw 5 lc 1 ,\ + 'timing_data/freepdk45_timing.dat' using ($1/1024):4 with line axis x1y2 title '64-bit word' lt 2 lw 5 lc 2 ,\ + 'timing_data/freepdk45_timing.dat' using ($1/1024):4 with points axis x1y2 title '' lt 2 lw 5 lc 2 ,\ + 'timing_data/freepdk45_timing.dat' using ($1/1024):5 with line axis x1y2 title '128-bit word' lt 3 lw 5 lc 3 ,\ + 'timing_data/freepdk45_timing.dat' using ($1/1024):5 with points axis x1y2 title '' lt 3 lw 5 lc 3 + +# --- GRAPH d +set tmargin at screen 0.28; set bmargin at screen 0.08 +#@BMARGIN; @RMARGIN +#@XTICS; @NOYTICS +set ylabel "ns" +set xlabel "Total Size (Kbits)" +set label 1 '180nm Access time' @POSB +plot 'timing_data/scn3me_timing.dat' using ($1/1024):2 with line axis x1y2 title '16-bit word' lt 0 lw 5 lc 0 ,\ + 'timing_data/scn3me_timing.dat' using ($1/1024):2 with points axis x1y2 title '' lt 0 lw 5 lc 0 ,\ + 'timing_data/scn3me_timing.dat' using ($1/1024):3 with line axis x1y2 title '32-bit word' lt 1 lw 5 lc 1 ,\ + 'timing_data/scn3me_timing.dat' using ($1/1024):3 with points axis x1y2 title '' lt 1 lw 5 lc 1 ,\ + 'timing_data/scn3me_timing.dat' using ($1/1024):4 with line axis x1y2 title '64-bit word' lt 2 lw 5 lc 2 ,\ + 'timing_data/scn3me_timing.dat' using ($1/1024):4 with points axis x1y2 title '' lt 2 lw 5 lc 2 ,\ + 'timing_data/scn3me_timing.dat' using ($1/1024):5 with line axis x1y2 title '128-bit word' lt 3 lw 5 lc 3 ,\ + 'timing_data/scn3me_timing.dat' using ($1/1024):5 with points axis x1y2 title '' lt 3 lw 5 lc 3 +unset multiplot +### End multiplot diff --git a/ICCAD16_openram_paper/figs/layout.pdf b/ICCAD16_openram_paper/figs/layout.pdf new file mode 100644 index 00000000..01fe9622 Binary files /dev/null and b/ICCAD16_openram_paper/figs/layout.pdf differ diff --git a/ICCAD16_openram_paper/figs/layout.pptx b/ICCAD16_openram_paper/figs/layout.pptx new file mode 100644 index 00000000..15736c1d Binary files /dev/null and b/ICCAD16_openram_paper/figs/layout.pptx differ diff --git a/ICCAD16_openram_paper/figs/methodology.eps b/ICCAD16_openram_paper/figs/methodology.eps new file mode 100644 index 00000000..57dd0aa7 --- /dev/null +++ b/ICCAD16_openram_paper/figs/methodology.eps @@ -0,0 +1,866 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.8.8 (http://cairographics.org) +%%CreationDate: Mon Oct 17 14:59:32 2011 +%%Pages: 1 +%%BoundingBox: 0 0 630 399 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%EndComments +%%BeginProlog +/cairo_eps_state save def +/dict_count countdictstack def +/op_count count 1 sub def +userdict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/B { fill stroke } bind def +/B* { eofill stroke } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +%%EndProlog +11 dict begin +/FontType 42 def +/FontName /f-0-0 def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 1 /uni004D put +Encoding 2 /uni0065 put +Encoding 3 /uni006D put +Encoding 4 /uni006F put +Encoding 5 /uni0072 put +Encoding 6 /uni0079 put +Encoding 7 /uni0020 put +Encoding 8 /uni0043 put +Encoding 9 /uni0070 put +Encoding 10 /uni0069 put +Encoding 11 /uni006C put +Encoding 12 /uni0028 put +Encoding 13 /uni0050 put +Encoding 14 /uni0074 put +Encoding 15 /uni0068 put +Encoding 16 /uni006E put +Encoding 17 /uni0029 put +Encoding 18 /uni004C put +Encoding 19 /uni0067 put +Encoding 20 /uni0063 put +Encoding 21 /uni0061 put +Encoding 22 /uni0045 put +Encoding 23 /uni0046 put +Encoding 24 /uni002F put +Encoding 25 /uni0052 put +Encoding 26 /uni0041 put +Encoding 27 /uni0047 put +Encoding 28 /uni0044 put +Encoding 29 /uni0053 put +Encoding 30 /uni0049 put +Encoding 31 /uni0062 put +Encoding 32 /uni002E put +Encoding 33 /uni0056 put +Encoding 34 /uni002D put +Encoding 35 /uni0064 put +Encoding 36 /uni0073 put +Encoding 37 /uni0054 put +Encoding 38 /uni0077 put +Encoding 39 /uni007A put +Encoding 40 /uni0075 put +Encoding 41 /uni002C put +Encoding 42 /uni0078 put +Encoding 43 /uni0042 put +Encoding 44 /uni006B put +Encoding 45 /uni0055 put +Encoding 46 /uni0066 put +/CharStrings 47 dict dup begin +/.notdef 0 def +/uni004D 1 def +/uni0065 2 def +/uni006D 3 def +/uni006F 4 def +/uni0072 5 def +/uni0079 6 def +/uni0020 7 def +/uni0043 8 def +/uni0070 9 def +/uni0069 10 def +/uni006C 11 def +/uni0028 12 def +/uni0050 13 def +/uni0074 14 def +/uni0068 15 def +/uni006E 16 def +/uni0029 17 def +/uni004C 18 def +/uni0067 19 def +/uni0063 20 def +/uni0061 21 def +/uni0045 22 def +/uni0046 23 def +/uni002F 24 def +/uni0052 25 def +/uni0041 26 def +/uni0047 27 def +/uni0044 28 def +/uni0053 29 def +/uni0049 30 def +/uni0062 31 def +/uni002E 32 def +/uni0056 33 def +/uni002D 34 def +/uni0064 35 def +/uni0073 36 def +/uni0054 37 def +/uni0077 38 def +/uni007A 39 def +/uni0075 40 def +/uni002C 41 def +/uni0078 42 def +/uni0042 43 def +/uni006B 44 def +/uni0055 45 def +/uni0066 46 def +end readonly def +/sfnts [ +<00010000000a008000030020636d61700252f32d00001fd00000009c63767420ffd31d390000 +206c000001fc6670676de7b4f1c4000022680000008b676c79661cacd2b0000000ac00001f24 +68656164dd84a2d0000022f40000003668686561104507920000232c00000024686d7478d559 +18d200002350000000bc6c6f6361aa82b2fc0000240c000000606d617870046a063a0000246c +00000020707265703b07f1000000248c0000056800020066fe96046605a400030007001a400c +04fb0006fb0108057f0204002fc4d4ec310010d4ecd4ec301311211125211121660400fc7303 +1bfce5fe96070ef8f2720629000100c90000061f05d5000c00bf403403110708070211010208 +080702110302090a0901110a0a09420a070203080300af080b050908030201050a061c043e0a +1c00040d10fcecfcec11173931002f3cc4ec32111739304b5358071005ed071008ed071008ed +071005ed5922b2700e01015d405603070f080f09020a15021407130a260226072007260a200a +3407350a69027c027b07790a80028207820a90021604010b0313011b0323012c032708280934 +013c035608590965086a097608790981018d0395019b03145d005d1321090121112311012301 +1123c9012d017d017f012dc5fe7fcbfe7fc405d5fc0803f8fa2b051ffc000400fae100000002 +0071ffe3047f047b0014001b00704024001501098608880515a90105b90c01bb18b912b80c8c +1c1b1502081508004b02120f451c10fcecf4ecc4111239310010e4f4ece410ee10ee10f4ee11 +12393040293f1d701da01dd01df01d053f003f013f023f153f1b052c072f082f092c0a6f006f +016f026f156f1b095d71015d0115211e0133323637150e01232000111000333200072e012322 +0607047ffcb20ccdb76ac76263d06bfef4fec70129fce20107b802a5889ab90e025e5abec734 +34ae2a2c0138010a01130143feddc497b4ae9e00000100ba0000071d047b0022005a40260612 +09180f00061d07150c871d2003b81bbc19100700110f0808065011080f501c18081a462310fc +ec32fcfcfcec11123931002f3c3ce4f43cc4ec32111217393040133024502470249024a024a0 +24bf24df24ff2409015d013e0133321615112311342623220615112311342623220615112311 +33153e01333216042945c082afbeb972758fa6b972778da6b9b93fb0797aab03897c76f5e2fd +5c029ea19cbea4fd87029ea29bbfa3fd870460ae67627c00000000020071ffe30475047b000b +0017004a401306b91200b90cb8128c1809120f51031215451810fcecf4ec310010e4f4ec10ee +3040233f197b007b067f077f087f097f0a7f0b7b0c7f0d7f0e7f0f7f107f117b12a019f01911 +015d012206151416333236353426273200111000232200111000027394acab9593acac93f001 +12feeef0f1feef011103dfe7c9c9e7e8c8c7e99cfec8feecfeedfec701390113011401380000 +000100ba0000034a047b001100304014060b0700110b03870eb809bc070a06080008461210fc +c4ec3231002fe4f4ecc4d4cc11123930b450139f1302015d012e012322061511231133153e01 +33321617034a1f492c9ca7b9b93aba85132e1c03b41211cbbefdb20460ae6663050500000001 +003dfe56047f0460000f01a240430708020911000f0a110b0a00000f0e110f000f0d110c0d00 +000f0d110e0d0a0b0a0c110b0b0a420d0b0910000b058703bd0e0bbc100e0d0c0a0906030008 +0f040f0b1010d4c4c4111739310010e432f4ec113911391239304b5358071005ed071008ed07 +1008ed071005ed071008ed0705ed17325922014bb00a544bb008545b58bd0010ffc000010010 +001000403811373859014bb0145458bd00100040000100100010ffc0381137385940f0060005 +080609030d160a170d100d230d350d490a4f0a4e0d5a095a0a6a0a870d800d930d120a000a09 +060b050c0b0e0b0f1701150210041005170a140b140c1a0e1a0f270024012402200420052908 +2809250a240b240c270d2a0e2a0f201137003501350230043005380a360b360c380d390e390f +30114100400140024003400440054006400740084209450a470d490e490f4011540051015102 +5503500450055606550756085709570a550b550c590e590f501166016602680a690e690f6011 +7b08780e780f89008a09850b850c890d890e890f9909950b950c9a0e9a0fa40ba40cab0eab0f +b011cf11df11ff11655d005d050e012b01353332363f01013309013302934e947c936c4c5433 +21fe3bc3015e015ec368c87a9a488654044efc94036c00010073ffe3052705f000190036401a +0da10eae0a951101a100ae04951791118c1a07190d003014101a10fcec32ec310010e4f4ecf4 +ec10eef6ee30b40f1b1f1b02015d01152e0123200011100021323637150e0123200011100021 +3216052766e782ff00fef00110010082e7666aed84feadfe7a0186015386ed0562d55f5efec7 +fed8fed9fec75e5fd34848019f01670168019f470000000200bafe5604a4047b0010001c003e +401b1ab9000e14b90508b80e8c01bd03bc1d11120b471704000802461d10fcec3232f4ec3100 +10e4e4e4f4c4ec10c4ee304009601e801ea01ee01e04015d2511231133153e01333212111002 +2322260134262322061514163332360173b9b93ab17bccffffcc7bb10238a79292a7a79292a7 +a8fdae060aaa6461febcfef8fef8febc6101ebcbe7e7cbcbe7e7000200c10000017906140003 +0007002b400e06be04b100bc020501080400460810fc3cec3231002fe4fcec30400b10094009 +50096009700905015d1333112311331523c1b8b8b8b80460fba00614e900000100c100000179 +061400030022b7009702010800460410fcec31002fec30400d10054005500560057005f00506 +015d13331123c1b8b80614f9ec00000100b0fef2027b0612000d004f400f069800970e0d0700 +03120600130a0e10dce432ec113939310010fcec30014bb0135458bd000e00400001000e000e +ffc03811373859014bb00f5458bd000effc00001000e000e0040381137385901060215141217 +23260235341237027b86828385a0969594970612e6fe3ee7e7fe3be5eb01c6e0df01c4ec0002 +00c90000048d05d500080013003a40180195100095098112100a0802040005190d3f11001c09 +041410fcec32fcec11173931002ff4ecd4ec30400b0f151f153f155f15af1505015d01113332 +3635342623252132041514042b0111230193fe8d9a9a8dfe3801c8fb0101fefffbfeca052ffd +cf92878692a6e3dbdde2fda800010037000002f2059e0013003840190e05080f03a9001101bc +08870a0b08090204000810120e461410fc3cc4fc3cc432393931002fecf43cc4ec3211393930 +b2af1501015d01112115211114163b01152322263511233533110177017bfe854b73bdbdd5a2 +8787059efec28ffda0894e9a9fd202608f013e000000000100ba000004640614001300344019 +030900030e0106870e11b80c970a010208004e0d09080b461410fcec32f4ec31002f3cecf4c4 +ec1112173930b2601501015d0111231134262322061511231133113e013332160464b87c7c95 +acb9b942b375c1c602a4fd5c029e9f9ebea4fd870614fd9e6564ef00000100ba00000464047b +001300364019030900030e0106870e11b80cbc0a010208004e0d09080b461410fcec32f4ec31 +002f3ce4f4c4ec1112173930b46015cf1502015d0111231134262322061511231133153e0133 +32160464b87c7c95acb9b942b375c1c602a4fd5c029e9f9ebea4fd870460ae6564ef000100a4 +fef2026f0612000d001f400f079800970e0701000b12041308000e10dc3cf4ec113939310010 +fcec301333161215140207233612353402a4a096959596a08583830612ecfe3cdfe0fe3aebe5 +01c5e7e701c20000000100c90000046a05d500050025400c0295008104011c033a00040610fc +ecec31002fe4ec304009300750078003800404015d133311211521c9ca02d7fc5f05d5fad5aa +00020071fe56045a047b000b0028004a4023190c1d0912861316b90f03b92623b827bc09b90f +bd1a1d261900080c4706121220452910fcc4ecf4ec323231002fc4e4ece4f4c4ec10fed5ee11 +12393930b6602a802aa02a03015d01342623220615141633323617100221222627351e013332 +363d010e0123220211101233321617353303a2a59594a5a59495a5b8fefefa61ac51519e52b5 +b439b27ccefcfcce7cb239b8023dc8dcdcc8c7dcdcebfee2fee91d1eb32c2abdbf5b6362013a +01030104013a6263aa0000010071ffe303e7047b0019003f401b00860188040e860d880ab911 +04b917b8118c1a07120d004814451a10fce432ec310010e4f4ec10fef4ee10f5ee30400b0f1b +101b801b901ba01b05015d01152e0123220615141633323637150e0123220011100021321603 +e74e9d50b3c6c6b3509d4e4da55dfdfed6012d010655a20435ac2b2be3cdcde32b2baa242401 +3e010e0112013a2300000002007bffe3042d047b000a002500bc4027191f0b17090e00a91706 +b90e1120861fba1cb923b8118c170c001703180d09080b1f030814452610fcecccd4ec323211 +393931002fc4e4f4fcf4ec10c6ee10ee11391139123930406e301d301e301f3020302130223f +27401d401e401f402040214022501d501e501f50205021502250277027851d871e871f872087 +2185229027a027f0271e301e301f30203021401e401f40204021501e501f50205021601e601f +60206021701e701f70207021801e801f80208021185d015d0122061514163332363d01371123 +350e01232226353436332135342623220607353e0133321602bedfac816f99b9b8b83fbc88ac +cbfdfb0102a79760b65465be5af3f00233667b6273d9b4294cfd81aa6661c1a2bdc0127f8b2e +2eaa2727fc00000100c90000048b05d5000b002e401506950402950081089504ad0a05010907 +031c00040c10fcec32d4c4c431002fececf4ec10ee30b21f0d01015d13211521112115211121 +1521c903b0fd1a02c7fd3902f8fc3e05d5aafe46aafde3aa0000000100c90000042305d50009 +002940120695040295008104ad08050107031c00040a10fcec32d4c431002fecf4ec10ee30b2 +0f0b01015d13211521112115211123c9035afd700250fdb0ca05d5aafe48aafd370000010000 +ff4202b205d50003002d4014001a010201021a03000342029f008104020001032fc439393100 +10f4ec304b5358071005ed071005ed5922013301230208aafdf8aa05d5f96d000000000200c9 +0000055405d50013001c00b14035090807030a06110304030511040403420604001503041595 +0914950d810b040506031109001c160e050a191904113f140a1c0c041d10fcec32fcc4ec1117 +391139393931002f3cf4ecd4ec123912391239304b5358071005ed071005ed1117395922b240 +1e01015d40427a13010500050105020603070415001501140216031704250025012502260327 +06260726082609201e3601360246014602680575047505771388068807980698071f5d005d01 +1e01171323032e012b01112311212016151406011133323635342623038d417b3ecdd9bf4a8b +78dcca01c80100fc83fd89fe9295959202bc16907efe68017f9662fd8905d5d6d88dba024ffd +ee8783838500000200100000056805d50002000a00ba40410011010004050402110505040111 +0a030a0011020003030a0711050406110505040911030a08110a030a42000307950103810905 +09080706040302010009050a0b10d4c4173931002f3ce4d4ec1239304b5358071005ed0705ed +071005ed0705ed071008ed071005ed071005ed071008ed5922b2200c01015d403a0f00580076 +0070008c000507010802060309041601190256015802500c67016802780176027c0372047707 +780887018802800c980299039604175d005d090121013301230321032302bcfeee0225fe7be5 +0239d288fd5f88d5050efd1903aefa2b017ffe81000000010073ffe3058b05f0001d00394020 +00051b0195031b950812a111ae15950e91088c1e02001c1134043318190b101e10fcecfce4fc +c4310010e4f4ecf4ec10fed4ee11393930251121352111060423200011100021320417152626 +23200011100021323604c3feb6021275fee6a0fea2fe75018b015e9201076f70fc8bfeeefeed +011301126ba8d50191a6fd7f53550199016d016e01994846d75f60fecefed1fed2fece250000 +000200c9000005b005d500080011002e4015009509810195100802100a0005190d32001c0904 +1210fcecf4ec113939393931002fecf4ec30b2601301015d0111332000111000212521200011 +100029010193f40135011ffee1fecbfe42019f01b20196fe68fe50fe61052ffb770118012e01 +2c0117a6fe97fe80fe7efe96000000010087ffe304a205f00027007e403c0d0c020e0b021e1f +1e080902070a021f1f1e420a0b1e1f0415010015a11494189511049500942591118c281e0a0b +1f1b0700221b190e2d071914222810dcc4ecfcece4111239393939310010e4f4e4ec10eef6ee +10c6111739304b535807100eed11173907100eed1117395922b20f2901015db61f292f294f29 +035d01152e012322061514161f011e0115140421222627351e013332363534262f012e013534 +24333216044873cc5fa5b377a67ae2d7feddfee76aef807bec72adbc879a7be2ca0117f569da +05a4c53736807663651f192bd9b6d9e0302fd04546887e6e7c1f182dc0abc6e42600000100c9 +0000019305d500030039b700af02011c00040410fcec31002fec30014bb0105458bd0004ffc0 +00010004000400403811373859400d30054005500560058f059f05065d13331123c9caca05d5 +fa2b0000000200baffe304a40614000b001c0038401903b90c0f09b918158c0fb81b97190012 +1247180c06081a461d10fcec3232f4ec31002fece4f4c4ec10c6ee30b6601e801ea01e03015d +013426232206151416333236013e01333212111002232226271523113303e5a79292a7a79292 +a7fd8e3ab17bccffffcc7bb13ab9b9022fcbe7e7cbcbe7e702526461febcfef8fef8febc6164 +a8061400000100db000001ae00fe00030011b7008302011900180410fcec31002fec30373315 +23dbd3d3fefe000100100000056805d5000600b7402704110506050311020306060503110403 +000100021101010042030401af0006040302000505010710d4c4173931002fec3239304b5358 +071005ed071008ed071008ed071005ed5922b2500801015d406200032a03470447055a037d03 +8303070600070208040906150114021a041a052a002601260229042905250620083800330133 +023c043c053706480045014502490449054706590056066602690469057a0076017602790479 +057506800898009706295d005d21013309013301024afdc6d301d901dad2fdc705d5fb1704e9 +fa2b0001006401df027f028300030011b6009c020401000410dccc310010d4ec301321152164 +021bfde50283a40000020071ffe3045a06140010001c003840191ab9000e14b905088c0eb801 +970317040008024711120b451d10fcecf4ec323231002fece4f4c4ec10c4ee30b6601e801ea0 +1e03015d0111331123350e0123220211101233321601141633323635342623220603a2b8b83a +b17ccbffffcb7cb1fdc7a79292a8a89292a703b6025ef9eca86461014401080108014461fe15 +cbe7e7cbcbe7e7000001006fffe303c7047b002700e7403c0d0c020e0b531f1e080902070a53 +1e1f1e420a0b1e1f041500860189041486158918b91104b925b8118c281e0a0b1f1b0700521b +080e07081422452810fcc4ecd4ece4111239393939310010e4f4ec10fef5ee10f5ee12173930 +4b535807100eed111739070eed1117395922b2002701015d406d1c0a1c0b1c0c2e092c0a2c0b +2c0c3b093b0a3b0b3b0c0b200020012402280a280b2a132f142f152a16281e281f2920292124 +27860a860b860c860d12000000010202060a060b030c030d030e030f03100319031a031b031c +041d09272f293f295f297f2980299029a029f029185d005d7101152e012322061514161f011e +0115140623222627351e013332363534262f012e01353436333216038b4ea85a898962943fc4 +a5f7d85ac36c66c661828c65ab40ab98e0ce66b4043fae282854544049210e2a99899cb62323 +be353559514b50250f2495829eac1e0000000001fffa000004e905d50007004a400e06029500 +81040140031c0040050810d4e4fce431002ff4ec3230014bb00a5458bd000800400001000800 +08ffc03811373859401300091f00100110021f071009400970099f09095d0321152111231121 +0604effdeecbfdee05d5aafad5052b0000010056000006350460000c0201404905550605090a +0904550a0903550a0b0a025501020b0b0a061107080705110405080807021103020c000c0111 +00000c420a050203060300bf0b080c0b0a09080605040302010b07000d10d4cc173931002f3c +ec32321739304b5358071005ed071008ed071008ed071005ed071008ed071005ed0705ed0710 +08ed5922014bb00a544bb011545b4bb012545b4bb013545b4bb00b545b58bd000dffc0000100 +0d000d00403811373859014bb00c544bb00d545b4bb010545b58bd000d00400001000d000dff +c0381137385940ff050216021605220a350a49024905460a400a5b025b05550a500a6e026e05 +660a79027f0279057f05870299029805940abc02bc05ce02c703cf051d0502090306040b050a +080b09040b050c1502190316041a051b081b09140b150c250025012302270321042505220622 +0725082709240a210b230c390336043608390c300e4602480346044004420540064007400844 +09440a440b400e400e560056015602500451055206520750085309540a550b6300640165026a +0365046a056a066a076e09610b670c6f0e7500750179027d0378047d057a067f067a077f0778 +0879097f097b0a760b7d0c870288058f0e97009701940293039c049b05980698079908402f96 +0c9f0ea600a601a402a403ab04ab05a906a907ab08a40caf0eb502b103bd04bb05b809bf0ec4 +02c303cc04ca05795d005d13331b01331b013301230b012356b8e6e5d9e6e5b8fedbd9f1f2d9 +0460fc96036afc96036afba00396fc6a000000010058000003db0460000900b4401a08110203 +0203110708074208a900bc03a905080301000401060a10dcc432c411393931002fecf4ec304b +5358071005ed071005ed5922014bb00b544bb00c545b58bd000a00400001000a000affc03811 +373859014bb0135458bd000affc00001000a000a004038113738594042050216022602470249 +07050b080f0b18031b082b08200b36033908300b400140024503400440054308570359085f0b +6001600266036004600562087f0b800baf0b1b5d005d1321150121152135012171036afd4c02 +b4fc7d02b4fd650460a8fcdb93a803250000000100aeffe30458046000130036401903090003 +0e0106870e118c0a01bc0c0d09080b4e020800461410fcecf4ec3231002fe432f4c4ec111217 +3930b46015cf1502015d1311331114163332363511331123350e01232226aeb87c7c95adb8b8 +43b175c1c801ba02a6fd619f9fbea4027bfba0ac6663f0000001009eff1201c300fe00050019 +400c039e0083060304011900180610fcecd4cc310010fcec30373315032313f0d3a48152feac +fec001400001003b000004790460000b015a4046051106070604110304070706041105040102 +0103110202010b110001000a11090a0101000a110b0a0708070911080807420a070401040800 +bf05020a0704010408000208060c10d4c4d4c411173931002f3cec321739304b5358071005ed +071008ed071008ed071005ed071005ed071008ed071008ed071005ed5922014bb00a544bb00f +545b4bb010545b4bb011545b58bd000cffc00001000c000c00403811373859014bb0145458bd +000c00400001000c000cffc0381137385940980a04040a1a04150a260a3d04310a5504570758 +0a660a76017a047607740a8d04820a99049f049707920a900aa601a904af04a507a30aa00a1c +0a03040505090a0b1a03150515091a0b2903260525092a0b200d3a013903370534073609390b +300d4903460545094a0b400d590056015902590357055606590756085609590b500d6f0d7801 +7f0d9b019407ab01a407b00dcf0ddf0dff0d2f5d005d09022309012309013309010464fe6b01 +aad9febafebad901b3fe72d9012901290460fddffdc101b8fe48024a0216fe71018f00000003 +00c9000004ec05d5000800110020004340231900950a0995128101950aad1f110b080213191f +05000e1c1605191c2e09001c12042110fcec32fcecd4ec111739393931002fececf4ec10ee39 +30b20f2201015d01112132363534262301112132363534262325213216151406071e01151404 +232101930144a39d9da3febc012b94919194fe0b0204e7fa807c95a5fef0fbfde802c9fddd87 +8b8c850266fe3e6f727170a6c0b189a21420cb98c8da000100ba0000049c0614000a00bc4029 +0811050605071106060503110405040211050504420805020303bc0097090605010406080108 +00460b10fcec32d4c4113931002f3cece41739304b5358071004ed071005ed071005ed071004 +ed5922b2100c01015d405f04020a081602270229052b0856026602670873027705820289058e +08930296059708a3021209050906020b030a072803270428052b062b07400c6803600c890385 +0489058d068f079a039707aa03a705b607c507d607f703f003f704f0041a5d71005d13331101 +33090123011123bab90225ebfdae026bf0fdc7b90614fc6901e3fdf4fdac0223fddd000100b2 +ffe3052905d50011004b40160802110b0005950e8c09008112081c0a38011c00411210fcecfc +ec310010e432f4ec113939393930014bb0105458bd00120040000100120012ffc03811373859 +b61f138f139f13035d133311141633323635113311100021200011b2cbaec3c2aecbfedffee6 +fee5fedf05d5fc75f0d3d3f0038bfc5cfedcfed6012a012400000001002f000002f806140013 +0070401c0510010c08a906018700970e06bc0a02130700070905080d0f0b4c1410fc3cc4fc3c +c4c412393931002fe432fcec10ee3212393930014bb00a5458bd0014ffc00001001400140040 +3811373859014bb00e5458bd00140040000100140014ffc03811373859b640155015a015035d +01152322061d012115211123112335333534363302f8b0634d012ffed1b9b0b0aebd06149950 +68638ffc2f03d18f4ebbab000000000200030000000000140001000000000034000400200000 +0004000400010000f02effff0000f000ffff10000001000000000006006800000000002f0000 +000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013 +001400150016001700180019001a001b001c001d001e001f0020002100220023002400250026 +002700280029002a002b002c002d002e013500b800cb00cb00c100aa009c01a600b800660000 +007100cb00a002b20085007500b800c301cb0189022d00cb00a600f000d300aa008700cb03aa +0400014a003300cb000000d9050200f4015400b4009c01390114013907060400044e04b40452 +04b804e704cd0037047304cd04600473013303a2055605a60556053903c5021200c9001f00b8 +01df007300ba03e9033303bc0444040e00df03cd03aa00e503aa0404000000cb008f00a4007b +00b80014016f007f027b0252008f00c705cd009a009a006f00cb00cd019e01d300f000ba0183 +00d5009803040248009e01d500c100cb00f600830354027f00000333026600d300c700a400cd +008f009a0073040005d5010a00fe022b00a400b4009c00000062009c0000001d032d05d505d5 +05d505f0007f007b005400a406b80614072301d300b800cb00a601c301ec069300a000d3035c +037103db0185042304a80448008f0139011401390360008f05d5019a06140723066601790460 +04600460047b009c00000277046001aa00e904600762007b00c5007f027b000000b4025205cd +006600bc00660077061000cd013b01850389008f007b0000001d00cd074a042f009c009c0000 +077d006f0000006f0335006a006f007b00ae00b2002d0396008f027b00f600830354063705f6 +008f009c04e10266008f018d02f600cd03440029006604ee007300001400b606050403020100 +2c2010b002254964b040515820c859212d2cb002254964b040515820c859212d2c20100720b0 +0050b00d7920b8ffff5058041b0559b0051cb0032508b0042523e120b00050b00d7920b8ffff +5058041b0559b0051cb0032508e12d2c4b505820b0fd454459212d2cb002254560442d2c4b53 +58b00225b0022545445921212d2c45442d000001000000020000322394a85f0f3cf5001f0800 +00000000bab9f0b800000000bac26791fe89fe1d0a4c076d0000000800010000000000000001 +0000076dfe1d00000abcfe89fe890a4c00010000000000000000000000000000002f04cd0066 +06e700c904ec007107cb00ba04e50071034a00ba04bc003d028b000005960073051400ba0239 +00c1023900c1031f00b004d300c903230037051200ba051200ba031f00a4047500c905140071 +0466007104e7007b050e00c9049a00c902b20000058f00c90579001006330073062900c90514 +0087025c00c9051400ba028b00db0579001002e3006405140071042b006f04e3fffa068b0056 +04330058051200ae028b009e04bc003b057d00c904a200ba05db00b202d1002f0000002200a0 +010a016c01be01f602e602e60332038003a803c6040a044a048804c40500052c054e05b205fe +069406c406ee0714079e0818086c08ac09280952099e09b20a220a380a840b340b6c0c8a0cfc +0d380d540e220e7a0ef20f3a0f9200010000002f004d00070042000400020010004000070000 +0415056800030001b8028040fffbfe03fa1403f92503f83203f79603f60e03f5fe03f4fe03f3 +2503f20e03f19603f02503ef8a4105effe03ee9603ed9603ecfa03ebfa03eafe03e93a03e842 +03e7fe03e63203e5e45305e59603e48a4105e45303e3e22f05e3fa03e22f03e1fe03e0fe03df +3203de1403dd9603dcfe03db1203da7d03d9bb03d8fe03d68a4105d67d03d5d44705d57d03d4 +4703d3d21b05d3fe03d21b03d1fe03d0fe03cffe03cefe03cd9603cccb1e05ccfe03cb1e03ca +3203c9fe03c6851105c61c03c51603c4fe03c3fe03c2fe03c1fe03c0fe03bffe03befe03bdfe +03bcfe03bbfe03ba1103b9862505b9fe03b8b7bb05b8fe03b7b65d05b7bb03b78004b6b52505 +b65d40ff03b64004b52503b4fe03b39603b2fe03b1fe03b0fe03affe03ae6403ad0e03acab25 +05ac6403abaa1205ab2503aa1203a98a4105a9fa03a8fe03a7fe03a6fe03a51203a4fe03a3a2 +0e05a33203a20e03a16403a08a4105a096039ffe039e9d0c059efe039d0c039c9b19059c6403 +9b9a10059b19039a1003990a0398fe0397960d0597fe03960d03958a410595960394930e0594 +2803930e0392fa039190bb0591fe03908f5d0590bb039080048f8e25058f5d038f40048e2503 +8dfe038c8b2e058cfe038b2e038a8625058a410389880b05891403880b038786250587640386 +85110586250385110384fe038382110583fe0382110381fe0380fe037ffe0340ff7e7d7d057e +fe037d7d037c64037b5415057b25037afe0379fe03780e03770c03760a0375fe0374fa0373fa +0372fa0371fa0370fe036ffe036efe036c21036bfe036a1142056a530369fe03687d03671142 +0566fe0365fe0364fe0363fe0362fe03613a0360fa035e0c035dfe035bfe035afe0359580a05 +59fa03580a035716190557320356fe035554150555420354150353011005531803521403514a +130551fe03500b034ffe034e4d10054efe034d10034cfe034b4a13054bfe034a4910054a1303 +491d0d05491003480d0347fe0346960345960344fe0343022d0543fa0342bb03414b0340fe03 +3ffe033e3d12053e14033d3c0f053d12033c3b0d053c40ff0f033b0d033afe0339fe03383714 +0538fa033736100537140336350b05361003350b03341e03330d0332310b0532fe03310b0330 +2f0b05300d032f0b032e2d09052e10032d09032c32032b2a25052b64032a2912052a25032912 +032827250528410327250326250b05260f03250b0324fe0323fe03220f032101100521120320 +64031ffa031e1d0d051e64031d0d031c1142051cfe031bfa031a42031911420519fe03186403 +1716190517fe031601100516190315fe0314fe0313fe031211420512fe0311022d0511420310 +7d030f64030efe030d0c16050dfe030c0110050c16030bfe030a100309fe0308022d0508fe03 +0714030664030401100504fe03401503022d0503fe0302011005022d0301100300fe0301b801 +64858d012b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b002b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b1d00> +] def +FontName currentdict end definefont pop +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 630 399 +%%EndPageSetup +q +0 g +1.005231 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 -1 0 398.268463 cm +220.387 54.336 m 351.496 54.336 l 351.496 88.773 l 220.387 88.773 l +220.387 54.336 l h +220.387 54.336 m S Q +BT +9.6 0 0 9.6 240.400003 325.596368 Tm +/f-0-0 1 Tf +[<01>1<02>2<03>2<0405>-1<060708>-1<0403>2<090a0b>1<02>2<05>]TJ +0 -1.25 Td +[<0c0d>-1<060e0f>-1<0410>-1<11>]TJ +ET +1.60016 w +q 1 0 0 -1 0 398.268463 cm +175.582 163.477 m 175.582 168.566 160.82 172.695 142.613 172.695 c +124.402 172.695 109.645 168.566 109.645 163.477 c 109.645 158.383 +124.402 154.254 142.613 154.254 c 160.82 154.254 175.582 158.383 +175.582 163.477 c h +175.582 163.477 m S Q +q 1 0 0 -1 0 398.268463 cm +175.504 212.371 m 175.504 217.461 160.742 221.59 142.535 221.59 c +124.328 221.59 109.566 217.461 109.566 212.371 c 109.566 207.277 +124.328 203.148 142.535 203.148 c 160.742 203.148 175.504 207.277 +175.504 212.371 c h +175.504 212.371 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +175.023 163.754 m 175.023 212.371 l S Q +q 1 0 0 -1 0 398.268463 cm +109.645 163.477 m 109.645 212.09 l S Q +1.60016 w +q 1 0 0 -1 0 398.268463 cm +311.406 163.895 m 311.406 168.988 296.645 173.113 278.438 173.113 c +260.227 173.113 245.469 168.988 245.469 163.895 c 245.469 158.801 +260.227 154.676 278.438 154.676 c 296.645 154.676 311.406 158.801 +311.406 163.895 c h +311.406 163.895 m S Q +q 1 0 0 -1 0 398.268463 cm +311.328 212.789 m 311.328 217.879 296.566 222.008 278.359 222.008 c +260.152 222.008 245.391 217.879 245.391 212.789 c 245.391 207.695 +260.152 203.57 278.359 203.57 c 296.566 203.57 311.328 207.695 311.328 +212.789 c h +311.328 212.789 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +310.844 164.176 m 310.844 212.789 l S Q +q 1 0 0 -1 0 398.268463 cm +245.469 163.895 m 245.469 212.508 l S Q +1.60016 w +q 1 0 0 -1 0 398.268463 cm +439.926 166.688 m 439.926 171.781 425.164 175.91 406.957 175.91 c +388.75 175.91 373.988 171.781 373.988 166.688 c 373.988 161.598 388.75 +157.469 406.957 157.469 c 425.164 157.469 439.926 161.598 439.926 +166.688 c h +439.926 166.688 m S Q +q 1 0 0 -1 0 398.268463 cm +439.848 215.582 m 439.848 220.676 425.09 224.801 406.883 224.801 c +388.672 224.801 373.914 220.676 373.914 215.582 c 373.914 210.492 +388.672 206.363 406.883 206.363 c 425.09 206.363 439.848 210.492 +439.848 215.582 c h +439.848 215.582 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +439.367 166.969 m 439.367 215.582 l S Q +q 1 0 0 -1 0 398.268463 cm +373.988 166.688 m 373.988 215.305 l S Q +1.60016 w +q 1 0 0 -1 0 398.268463 cm +252.73 339.355 m 252.73 344.445 237.973 348.574 219.762 348.574 c +201.555 348.574 186.793 344.445 186.793 339.355 c 186.793 334.262 +201.555 330.133 219.762 330.133 c 237.973 330.133 252.73 334.262 252.73 +339.355 c h +252.73 339.355 m S Q +q 1 0 0 -1 0 398.268463 cm +252.656 388.25 m 252.656 393.34 237.895 397.469 219.688 397.469 c +201.48 397.469 186.719 393.34 186.719 388.25 c 186.719 383.156 201.48 +379.027 219.688 379.027 c 237.895 379.027 252.656 383.156 252.656 +388.25 c h +252.656 388.25 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +252.172 339.633 m 252.172 388.25 l S Q +q 1 0 0 -1 0 398.268463 cm +186.793 339.355 m 186.793 387.969 l S Q +BT +9.6 0 0 9.6 75.557163 243.454333 Tm +/f-0-0 1 Tf +[<12>18<04>1<130a14>1<15>-2<0b>]TJ +18.684519 -2.996216 Td +[<1216>1<17>1<18>-1<17>1<19>41<1a01>]TJ +0 -1.25 Td +[<1b1c1d>-2<1e1e>]TJ +12.805588 0.0276477 Td +[<120a>1<1f02>2<05>-1<0e>-1<06070c>-1<200b0a>1<1f11>]TJ +-26.891736 1.133586 Td +[<1d>-2<090a>1<14>1<02>1<18>-1<12>110<21>1<1d>]TJ +0 -1.25 Td +[<21>79<02>2<05>-1<0a0b>1<0413>]TJ +8.43187 5.747114 Td +[<17>73<05>21<0410>-1<0e22>-1<16>1<10>-1<23>]TJ +0 -1.25 Td +[<0d0f>-1<0624>-1<0a>1<14>1<15>-3<0b>]TJ +25.203725 -0.0305608 Td +[<16>1<24>-1<0e0a03>2<15>-2<0e>-1<02>2<23>]TJ +0 -1.25 Td +[<25>29<0a03>2<0a10>-1<1318>-1<0d>35<042602>2<05>]TJ +ET +1.005231 w +q 1 0 0 -1 0 398.268463 cm +156.406 248.234 m 287.516 248.234 l 287.516 282.676 l 156.406 282.676 l +156.406 248.234 l h +156.406 248.234 m S Q +BT +9.6 0 0 9.6 168.816605 130.165637 Tm +/f-0-0 1 Tf +[<01>1<02>2<03>2<0405>-1<060708>-1<0f>-1<15>-2<05>-1<15>-3<14>1<0e02>2<05>-1<0a +27>]TJ +10.126953 0 Td +[<02>2<05>]TJ +-10.126953 -1.25 Td +[<0c0d>-1<060e0f>-1<0410>-1<11>]TJ +ET +0.921608 w +[ 5.529648 0.921608] 0 d +q 1 0 0 1 0 398.268463 cm +0.461 -281.039 m 116.008 -281.039 l 116.008 -248.191 l 0.461 -248.191 l +0.461 -281.039 l h +0.461 -281.039 m S Q +BT +9.6 0 0 9.6 4.795825 134.635925 Tm +/f-0-0 1 Tf +[<1d>-2<0a>1<03>1<28>-1<0b>1<15>-3<0e0405>]TJ +0 -1.25 Td +[<0c02>1<201320>-1<0710>-1<1324090a14>1<02>2<29>-1<0724>-1<0902>2<14>1<0e +05>]TJ +10.197266 0 Td +[<02>2<11>]TJ +ET +0.874385 w +[ 0.874385 1.748769] 0 d +q 1 0 0 -1 0 398.268463 cm +331.52 249.285 m 435.379 249.285 l 435.379 282.18 l 331.52 282.18 l +331.52 249.285 l h +331.52 249.285 m S Q +BT +9.6 0 0 9.6 345.902591 136.871082 Tm +/f-0-0 1 Tf +[<16>1<2a0e>-1<0515>-3<14>1<0e0405>]TJ +0 -1.25 Td +[<0c02>1<201320>-1<0708>-1<15>-2<0b0a>1<1f05>20<02>2<11>]TJ +-23.853645 -9.999148 Td +[<1a1010>-1<040e15>-3<0e02>2<23>]TJ +0 -1.25 Td +[<25>29<0a03>2<0a10>-1<1318>-1<0d>35<042602>2<05>]TJ +7.661297 2.021848 Td +[<120a>1<1f02>2<05>-1<0e>-1<06070c>-1<200b0a>1<1f11>]TJ +0 -1.25 Td +[<1d>-2<090a>1<14>1<02>]TJ +ET +1.6 w +[] 0.0 d +q 1 0 0 -1 0 398.268463 cm +280.633 88.879 m 145.965 152.578 l S Q +151.75 248.425 m 153.273 252.686 l 144.52 245.007 l 156.012 246.901 l +151.75 248.425 l h +151.75 248.425 m f* +0.723173 w +q 1 0.473029 0.473029 -1 0 398.268463 cm +66.083 181.103 m 68.975 178.209 l 58.853 181.101 l 68.977 183.995 l +66.083 181.103 l h +66.083 181.103 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +280.074 89.438 m 280.074 153.695 l S Q +280.074 250.972 m 276.875 254.171 l 280.074 242.972 l 283.273 254.171 l +280.074 250.972 l h +280.074 250.972 m f* +0.8 w +q -0.000000000000000061 1 1 0.000000000000000061 0 398.268463 cm +-147.297 280.074 m -144.098 276.875 l -155.297 280.074 l -144.098 +283.273 l -147.297 280.074 l h +-147.297 280.074 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +285.27 89.043 m 340.207 121.863 l S Q +334.711 279.69 m 330.324 278.581 l 341.578 275.585 l 333.605 284.077 l +334.711 279.69 l h +334.711 279.69 m f* +0.686779 w +q -1 0.597406 0.597406 1 0 398.268463 cm +-298.881 59.975 m -296.137 57.226 l -305.75 59.973 l -296.135 62.722 l +-298.881 59.975 l h +-298.881 59.975 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +155.465 265.453 m 116.348 265.453 l S Q +149.062 132.815 m 145.863 129.612 l 157.062 132.815 l 145.863 136.015 l +149.062 132.815 l h +149.062 132.815 m f* +0.8 w +q -1 -0.000000000000000122 -0.000000000000000122 1 0 398.268463 cm +-149.062 -265.453 m -145.863 -268.656 l -157.062 -265.453 l -145.863 +-262.254 l -149.062 -265.453 l h +-149.062 -265.453 m S Q +122.75 132.815 m 125.949 136.015 l 114.75 132.815 l 125.949 129.612 l +122.75 132.815 l h +122.75 132.815 m f* +q 1 -0.000000000000000122 -0.000000000000000122 -1 0 398.268463 cm +122.75 265.453 m 125.949 262.254 l 114.75 265.453 l 125.949 268.656 l +122.75 265.453 l h +122.75 265.453 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +252.395 219.602 m 218.934 247.098 l S Q +223.879 155.233 m 224.32 159.737 l 217.699 150.155 l 228.383 154.792 l +223.879 155.233 l h +223.879 155.233 m f* +0.618042 w +q 1 0.821886 0.821886 -1 0 398.268463 cm +14.403 254.873 m 16.876 252.401 l 8.224 254.872 l 16.874 257.345 l +14.403 254.873 l h +14.403 254.873 m S Q +1.005231 w +q 1 0 0 -1 0 398.268463 cm +341.922 104.066 m 473.031 104.066 l 473.031 138.508 l 341.922 138.508 l +341.922 104.066 l h +341.922 104.066 m S Q +BT +9.6 0 0 9.6 354.334622 274.333276 Tm +/f-0-0 1 Tf +[<01>1<02>2<03>2<0405>-1<060708>-1<0f>-1<15>-2<05>-1<15>-3<14>1<0e02>2<05>-1<0a +27>]TJ +10.126953 0 Td +[<02>2<05>]TJ +-10.126953 -1.25 Td +[<0c0d>-1<060e0f>-1<0410>-1<11>]TJ +ET +1.6 w +q 1 0 0 -1 0 398.268463 cm +408.594 137.887 m 408.594 156.578 l S Q +408.594 248.089 m 405.395 251.288 l 408.594 240.089 l 411.797 251.288 l +408.594 248.089 l h +408.594 248.089 m f* +0.8 w +q -0.000000000000000061 1 1 0.000000000000000061 0 398.268463 cm +-150.18 408.594 m -146.98 405.395 l -158.18 408.594 l -146.98 411.797 l +-150.18 408.594 l h +-150.18 408.594 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +157.699 221.309 m 200.727 247.016 l S Q +195.23 154.538 m 190.844 153.429 l 202.098 150.433 l 194.125 158.925 l +195.23 154.538 l h +195.23 154.538 m f* +0.68678 w +q -1 0.597403 0.597403 1 0 398.268463 cm +-251.189 -93.67 m -248.444 -96.418 l -258.057 -93.672 l -248.443 +-90.923 l -251.189 -93.67 l h +-251.189 -93.67 m S Q +2.4 w +[ 7.2 7.2] 0 d +q 1 0 0 -1 0 398.268463 cm +22.473 236.957 m 597.465 236.957 l S Q +BT +16 0 0 16 5.708505 64.082971 Tm +/f-0-0 1 Tf +[<2b15>-2<14>1<2c>1<22161023>]TJ +0 -1 Td +[<01>1<02>2<0e0f>-1<042304>1<0b04>1<1306>]TJ +0.674965 15.682794 Td +[<17>73<05>21<0410>-1<0e22>-1<16>1<10>-1<23>]TJ +0 -1 Td +[<01>1<02>2<0e0f>-1<042304>1<0b04>1<1306>]TJ +ET +0.921608 w +[ 5.529648 0.921608] 0 d +q 1 0 0 1 0 398.268463 cm +514.727 -138.27 m 630.273 -138.27 l 630.273 -105.422 l 514.727 -105.422 +l 514.727 -138.27 l h +514.727 -138.27 m S Q +BT +9.6 0 0 9.6 519.059866 277.406592 Tm +/f-0-0 1 Tf +[<1d>-2<0a>1<03>1<28>-1<0b>1<15>-3<0e0405>]TJ +0 -1.25 Td +[<0c02>1<201320>-1<0710>-1<1324090a14>1<02>2<29>-1<0724>-1<0902>2<14>1<0e +05>]TJ +10.197266 0 Td +[<02>2<11>]TJ +ET +1.6 w +[] 0.0 d +q 1 0 0 -1 0 398.268463 cm +513.824 122.125 m 474.711 122.125 l S Q +507.426 276.143 m 504.227 272.944 l 515.426 276.143 l 504.227 279.343 l +507.426 276.143 l h +507.426 276.143 m f* +0.8 w +q -1 -0.000000000000000122 -0.000000000000000122 1 0 398.268463 cm +-507.426 -122.125 m -504.227 -125.324 l -515.426 -122.125 l -504.227 +-118.926 l -507.426 -122.125 l h +-507.426 -122.125 m S Q +481.109 276.143 m 484.312 279.343 l 473.109 276.143 l 484.312 272.944 l +481.109 276.143 l h +481.109 276.143 m f* +q 1 -0.000000000000000122 -0.000000000000000122 -1 0 398.268463 cm +481.109 122.125 m 484.312 118.926 l 473.109 122.125 l 484.312 125.324 l +481.109 122.125 l h +481.109 122.125 m S Q +1.60016 w +q 1 0 0 -1 0 398.268463 cm +488.539 29.785 m 488.539 34.879 473.781 39.004 455.57 39.004 c 437.363 +39.004 422.602 34.879 422.602 29.785 c 422.602 24.691 437.363 20.566 +455.57 20.566 c 473.781 20.566 488.539 24.691 488.539 29.785 c h +488.539 29.785 m S Q +q 1 0 0 -1 0 398.268463 cm +488.465 78.68 m 488.465 83.77 473.703 87.898 455.496 87.898 c 437.289 +87.898 422.527 83.77 422.527 78.68 c 422.527 73.586 437.289 69.461 +455.496 69.461 c 473.703 69.461 488.465 73.586 488.465 78.68 c h +488.465 78.68 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +487.98 30.062 m 487.98 78.68 l S Q +q 1 0 0 -1 0 398.268463 cm +422.602 29.785 m 422.602 78.398 l S Q +BT +9.6 0 0 9.6 441.005374 347.123721 Tm +/f-0-0 1 Tf +[<25>167<02>2<14>1<0f>]TJ +0 -1.25 Td +[<120a>1<1f05>-1<15>-2<05>-1<06>]TJ +ET +q 1 0 0 -1 0 398.268463 cm +422.809 52.973 m 353.52 73.09 l S Q +359.664 326.964 m 361.848 330.929 l 351.984 324.733 l 363.629 324.784 l +359.664 326.964 l h +359.664 326.964 m f* +0.768277 w +q 1 0.290323 0.290323 -1 0 398.268463 cm +312.613 162.063 m 315.689 158.991 l 304.933 162.064 l 315.686 165.135 l +312.613 162.063 l h +312.613 162.063 m S Q +1.6 w +q 1 0 0 -1 0 398.268463 cm +282.879 22.918 m 282.32 51.973 l S Q +282.441 352.694 m 279.305 355.956 l 282.289 344.694 l 285.703 355.831 l +282.441 352.694 l h +282.441 352.694 m f* +0.799852 w +q 0.0192309 1 1 -0.0192309 0 398.268463 cm +-40.128 283.213 m -36.928 280.015 l -48.128 283.215 l -36.93 286.413 l +-40.128 283.213 l h +-40.128 283.213 m S Q +BT +9.6 0 0 9.6 199.049588 390.974713 Tm +/f-0-0 1 Tf +[<2d24>-1<02>2<05>-1<071d>-2<0902>2<140a>1<2e>-1<0a14>1<15>-2<0e>-1<0a>1<04 +10>]TJ +0 -1.25 Td +[<0c26>-1<04>1<05>16<230724>-1<0a>1<27>-1<02>2<2907>-1<03>2<02>2<03>1<04 +050607>]TJ +10.234375 0 Td +[<240a2702>1<2907>-1<15>-2<240902>2<140e0705>-1<15>-3<0e0a04>1<29>-1<07 +02>2<0e>]TJ +10.169922 0 Td +<1420>Tj +ET +1.6 w +q 1 0 0 -1 0 398.268463 cm +327.57 265.453 m 288.457 265.453 l S Q +321.172 132.815 m 317.973 129.612 l 329.172 132.815 l 317.973 136.015 l +321.172 132.815 l h +321.172 132.815 m f* +0.8 w +q -1 -0.000000000000000122 -0.000000000000000122 1 0 398.268463 cm +-321.172 -265.453 m -317.973 -268.656 l -329.172 -265.453 l -317.973 +-262.254 l -321.172 -265.453 l h +-321.172 -265.453 m S Q +294.855 132.815 m 298.055 136.015 l 286.855 132.815 l 298.055 129.612 l +294.855 132.815 l h +294.855 132.815 m f* +q 1 -0.000000000000000122 -0.000000000000000122 -1 0 398.268463 cm +294.855 265.453 m 298.055 262.254 l 286.855 265.453 l 298.055 268.656 l +294.855 265.453 l h +294.855 265.453 m S Q +1.370434 w +q 1 0 0 -1 0 398.268463 cm +219.727 282.898 m 219.727 328.188 l S Q +219.727 75.561 m 216.984 78.304 l 219.727 68.71 l 222.465 78.304 l +219.727 75.561 l h +219.727 75.561 m f* +0.685217 w +q -0.000000000000000061 1 1 0.000000000000000061 0 398.268463 cm +-322.707 219.727 m -319.965 216.984 l -329.559 219.727 l -319.965 +222.465 l -322.707 219.727 l h +-322.707 219.727 m S Q +Q +showpage +%%Trailer +count op_count sub {pop} repeat +countdictstack dict_count sub {end} repeat +cairo_eps_state restore +%%EOF diff --git a/ICCAD16_openram_paper/figs/methodology.pdf b/ICCAD16_openram_paper/figs/methodology.pdf new file mode 100644 index 00000000..be5be05d Binary files /dev/null and b/ICCAD16_openram_paper/figs/methodology.pdf differ diff --git a/ICCAD16_openram_paper/figs/methodology.svg b/ICCAD16_openram_paper/figs/methodology.svg new file mode 100644 index 00000000..017d762e --- /dev/null +++ b/ICCAD16_openram_paper/figs/methodology.svg @@ -0,0 +1,1232 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Memory Compiler(Python) + + + + + + + Logical + LEF/FRAM GDSII + Liberty (.lib) + Spice/LVS Verilog + Front-EndPhysical + EstimatedTiming/Power + + Memory Characterizer(Python) + + Simulator(e.g. ngspice, spectre) + + Extractor(e.g. Calibre) + AnnotatedTiming/Power + Liberty (.lib) Spice + + + + + Memory Characterizer(Python) + + + Back-EndMethodology + Front-EndMethodology + + Simulator(e.g. ngspice, spectre) + + Tech Library + + User Specification(word size, memory size, aspect ratio, etc.) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ICCAD16_openram_paper/figs/sram_structure.pdf b/ICCAD16_openram_paper/figs/sram_structure.pdf new file mode 100644 index 00000000..d225a632 Binary files /dev/null and b/ICCAD16_openram_paper/figs/sram_structure.pdf differ diff --git a/ICCAD16_openram_paper/figs/sram_structure.svg b/ICCAD16_openram_paper/figs/sram_structure.svg new file mode 100644 index 00000000..51eaf1c7 --- /dev/null +++ b/ICCAD16_openram_paper/figs/sram_structure.svg @@ -0,0 +1,1968 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Bit Cell Array + + Column Mux + Sense Amp Array + Write Driver Array + + Input Data MS-Flop Array + + Tri Gate Array + AddressMS-Flop + ANDArray + Decoder + Wordline Driver + Predecdoer + Control Logic&Replica Bit-line + + + + + + + + + + + + Precharge Array + + + + + + + + + + + + + + Bank Select + + Address Bus + + + + n + BidirectionalData Bus + + m + + + + + + + + + Bank + + + + + 6T + + + CLK + + CSb + + OEb + + WEb + + diff --git a/ICCAD16_openram_paper/figs/sram_structure.vsd b/ICCAD16_openram_paper/figs/sram_structure.vsd new file mode 100644 index 00000000..8c9b08cd Binary files /dev/null and b/ICCAD16_openram_paper/figs/sram_structure.vsd differ diff --git a/ICCAD16_openram_paper/figs/timing_data/f_plot.gp b/ICCAD16_openram_paper/figs/timing_data/f_plot.gp new file mode 100644 index 00000000..999c399f --- /dev/null +++ b/ICCAD16_openram_paper/figs/timing_data/f_plot.gp @@ -0,0 +1,14 @@ +set terminal pdf dashed +set output "../Freepdk_Read_Access_time.pdf" +set palette color +set xlabel "Total Size (Kbit)" +set ylabel "Read Access time (ns)" +set key below +plot 'freepdk45_timing.dat' using ($1/1024):2 with line title '16-bit word' lt 0 lw 5 lc 0 ,\ + 'freepdk45_timing.dat' using ($1/1024):2 with points title '' lt 0 lw 5 lc 0 ,\ + 'freepdk45_timing.dat' using ($1/1024):3 with line title '32-bit word' lt 1 lw 5 lc 1 ,\ + 'freepdk45_timing.dat' using ($1/1024):3 with points title '' lt 1 lw 5 lc 1 ,\ + 'freepdk45_timing.dat' using ($1/1024):4 with line title '64-bit word' lt 2 lw 5 lc 2 ,\ + 'freepdk45_timing.dat' using ($1/1024):4 with points title '' lt 2 lw 5 lc 2 ,\ + 'freepdk45_timing.dat' using ($1/1024):5 with line title '128-bit word' lt 3 lw 5 lc 3 ,\ + 'freepdk45_timing.dat' using ($1/1024):5 with points title '' lt 3 lw 5 lc 3 diff --git a/ICCAD16_openram_paper/figs/timing_data/freepdk45_timing.dat b/ICCAD16_openram_paper/figs/timing_data/freepdk45_timing.dat new file mode 100644 index 00000000..8b8be34e --- /dev/null +++ b/ICCAD16_openram_paper/figs/timing_data/freepdk45_timing.dat @@ -0,0 +1,5 @@ + +2048 0.861 1.02 0.86 1.076 +8192 1.32 1.33 1.5 1.34 +32768 1.8 1.83 1.9 2.01 +131072 2.2 2.6 6.75 9.86 diff --git a/ICCAD16_openram_paper/figs/timing_data/s_plot.gp b/ICCAD16_openram_paper/figs/timing_data/s_plot.gp new file mode 100644 index 00000000..4f66564e --- /dev/null +++ b/ICCAD16_openram_paper/figs/timing_data/s_plot.gp @@ -0,0 +1,14 @@ +set terminal pdf dashed +set output "../Scn3me_Read_Access_time.pdf" +set palette color +set xlabel "Total Size (Kbit)" +set ylabel "Read Access time (ns)" +set key below +plot 'scn3me_timing.dat' using ($1/1024):2 with line title '16-bit word' lt 0 lw 5 lc 0 ,\ + 'scn3me_timing.dat' using ($1/1024):2 with points title '' lt 0 lw 5 lc 0 ,\ + 'scn3me_timing.dat' using ($1/1024):3 with line title '32-bit word' lt 1 lw 5 lc 1 ,\ + 'scn3me_timing.dat' using ($1/1024):3 with points title '' lt 1 lw 5 lc 1 ,\ + 'scn3me_timing.dat' using ($1/1024):4 with line title '64-bit word' lt 2 lw 5 lc 2 ,\ + 'scn3me_timing.dat' using ($1/1024):4 with points title '' lt 2 lw 5 lc 2 ,\ + 'scn3me_timing.dat' using ($1/1024):5 with line title '128-bit word' lt 3 lw 5 lc 3 ,\ + 'scn3me_timing.dat' using ($1/1024):5 with points title '' lt 3 lw 5 lc 3 diff --git a/ICCAD16_openram_paper/figs/timing_data/scn3me_timing.dat b/ICCAD16_openram_paper/figs/timing_data/scn3me_timing.dat new file mode 100644 index 00000000..4d65931e --- /dev/null +++ b/ICCAD16_openram_paper/figs/timing_data/scn3me_timing.dat @@ -0,0 +1,5 @@ + +2048 9.42 11.82 14.81 22.9 +8192 8.25 16.04 19.24 23.12 +32768 11.69 14.7 23.82 30.75 +131072 12.7 18.21 30.25 44.95 diff --git a/ICCAD16_openram_paper/figs/timing_read.pdf b/ICCAD16_openram_paper/figs/timing_read.pdf new file mode 100644 index 00000000..5f69b5b3 Binary files /dev/null and b/ICCAD16_openram_paper/figs/timing_read.pdf differ diff --git a/ICCAD16_openram_paper/figs/timing_read.svg b/ICCAD16_openram_paper/figs/timing_read.svg new file mode 100644 index 00000000..835b6046 --- /dev/null +++ b/ICCAD16_openram_paper/figs/timing_read.svg @@ -0,0 +1,658 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + CLK + ADDR + CSb + OEb + WEb + DATA OUT + A0 + A1 + + + + + + + + + D0 + D1 + + + + + Setup + Hold + Setup + Hold + + Read Delay + Setup + + + SCLK + + + diff --git a/ICCAD16_openram_paper/figs/timing_write.pdf b/ICCAD16_openram_paper/figs/timing_write.pdf new file mode 100644 index 00000000..40d027a3 Binary files /dev/null and b/ICCAD16_openram_paper/figs/timing_write.pdf differ diff --git a/ICCAD16_openram_paper/figs/timing_write.svg b/ICCAD16_openram_paper/figs/timing_write.svg new file mode 100644 index 00000000..6ae8c816 --- /dev/null +++ b/ICCAD16_openram_paper/figs/timing_write.svg @@ -0,0 +1,858 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + CLK + ADDR + CSb + OEb + WEb + DATA IN + A0 + A1 + + + + + + + D0 + D1 + + + + + Setup + Hold + Setup + Hold + Setup + + + WD_EN + + + + + Setup + + Hold + + + + + + + D0 + X Mem Cell + + Write Delay + + + diff --git a/ICCAD16_openram_paper/implementation.tex b/ICCAD16_openram_paper/implementation.tex new file mode 100644 index 00000000..aadcb24a --- /dev/null +++ b/ICCAD16_openram_paper/implementation.tex @@ -0,0 +1,214 @@ +\section{Implementation} +\label{sec:implementation} + +% source langauge +OpenRAM's methodology is implemented using an object-oriented approach +in the Python programming language. Python is a simple, yet powerful +language that is easy to learn and very human-readable. Moreover, Python +enables portability to most operating systems. OpenRAM has no additional +dependencies except a DRC/LVS tool, but that is disabled with a +warning if the tools are unavailable. + +% portability of tools and technologies +In addition to system portability, OpenRAM is also translatable across +numerous process technologies. This is accomplished by using +generalized routines to generate the memory based on common features +across all technologies. To facilitate user modification and +technology interoperability, OpenRAM provides a reference implementation in +$45$nm FreePDK45~\cite{4231502} and a fabricable option using the +MOSIS Scalable CMOS (SCMOS) design rules~\cite{scmos}. FreePDK45 uses +many design rules found in modern technologies, but is non-fabricable, +while SCMOS enables fabrication of designs using the MOSIS foundry +services. SCMOS is not confidential and an implementation using it is +included, however, it does not include many advanced DSM design +rules. OpenRAM has also been ported to other commercial technologies, +but these are not directly included due to licensing issues. + +% methodology overview +OpenRAM's framework is divided into \enquote{front-end} and \enquote{back-end} +methodologies as shown in Figure~\ref{fig:methodology}. The front-end +has the compiler and the characterizer. The compiler generates +SPICE models and its GDSII layouts based on user inputs. The +characterizer calls a SPICE simulator to produce timing and power +results. The back-end uses a spice netlist extracted from the GDSII +layout using to generate annotated timing and power models. + +\begin{figure}[tb] +\centering +\includegraphics[width=8cm]{./figs/methodology.pdf} +\caption{Overall Compilation and Characterization Methodology} +\label{fig:methodology} +\end{figure} + +%\fixme{We actually dont have back end done yet.} + +\subsection{Base Data Structures} + +The design modules in OpenRAM are derived from the {\it design} class +(design.py). The design class has a name, a SPICE model (netlist), and +a layout. Both the SPICE model and the layout inherit their +capabilities from a hierarchical class. The design class also provides +inherited functions to perform DRC and LVS verification of any +sub-design for hierarchical debugging. + +The design class derives from the {\it spice} class +(hierarchy\_\allowbreak spice.py) which has a data structure to +maintain the circuit hierarchy. This class maintains the design +instances, their pins, and their connections as well as helper +functions to maintain the structure and connectivity of the circuit +hierarchy. + +The design class also derives from a {\it layout} class (hierarchy\_layout.py). +This class has a list of physical instances of sub-modules in the layout and +a structure for simple objects such as shapes and labels in the +current hierarchy level. In addition, there are helper functions that +maintain the physical layout structures. + +OpenRAM has an integrated, custom GDSII library to read, write, and +manipulate GDSII files. The library, originally called +GdsMill~\cite{gdsmill}, has been modified, debugged, and extended for +OpenRAM. Full rights were given to include the GdsMill source with +OpenRAM, but to make the interfacing easier and porting to other +physical layout databases possible, OpenRAM implements a {\it + geometry} wrapper class (geometry.py) that abstracts the GdsMill +library. + +\subsection{Technology and Tool Portability} + +% technology overview +OpenRAM is technology-independent by using a technology directory that +includes the technology's specific information, rules, and library +cells. Technology parameters such as the design rule check (DRC) rules +and the GDS layer map are required to ensure that the dynamically +generated designs are DRC clean. Custom designed library cells such as +the memory cell and the sense amplifier are also placed in this +directory. A very simple design rule parameter file has the most +important design rules for constructing basic interconnect and +transistor devices. FreePDK45 and SCMOS reference technologies are provided. + +% hand-optimized cells +OpenRAM uses some custom-designed library primitives as technology +input. Since density is extremely important, the following cells are +pre-designed in each technology: 6T cell, sense amplifier, +master-slave flip-flop, tri-state gate, and write driver. All other +cells are generated on-the-fly using parameterizable transistor and +gate primitives. + +% technology specific features +OpenRAM can be used for various technologies since it creates the +basic components of memory designs that are common over these +technologies. For technologies that have specific design requirements, +such as specialized well contacts, the user can include call-back +helper functions in the technology directory. This is done so that the +main compiler remains free of dependencies to specific technologies. + +% DRC and LVS +OpenRAM has two functions that provide a wrapper interface with DRC +and LVS tools. These two functions perform DRC and LVS using the GDSII +layout and SPICE netlist files. Since each DRC and LVS tool has +different output, this routine is customized per tool to parse DRC/LVS +reports and return the number of errors while also outputting debug +information. These routines allow flexibility of any DRC/LVS tool, +but the default implementation calls Calibre nmDRC and nmLVS. In +OpenRAM, both DRC and LVS are performed at all levels of the design +hierarchy to enhance bug tracking. DRC and LVS can be disabled for +improved run-time or if tool licenses are not available. + +\subsection{Class Hierarchy} + +\subsubsection{High-Level Classes} + +The {\it openram} class (openram.py) organizes execution and +instantiates a single memory design using the {\it sram} class. It +accepts user-provided parameters to generate the design, performs the +optional extraction, performs characterization, and saves the +resulting design files. + +The {\it sram} class (sram.py) decides the appropriate internal parameter +dependencies shown in Table~\ref{table:variables}. They are dependent +on the user-desired data word size, number of words, and number of banks. +It is responsible for instantiation of the single control logic module which +controls the SRAM banks. The control logic ensures that only one bank +is active in a given address range. + +The {\it bank} class (bank.py) does the bulk of the non-control memory layout. It +instantiates $1$, $2$, or $4$ bit-cell arrays and coordinates the row and column +address decoders along with their pre-charge, sense amplifiers, and input/output +data flops. + +\begin{table} +\centering +\caption{Dependencies required for sub-modules} +\begin{tabular}{|c|l|} \hline +Variable&Equation \\ \hline +\texttt{Total Bits} & $word\_size*num\_words$ \\ \hline +\texttt{Words Per Row} & $\sqrt(num\_words)/word\_size$ \\ \hline +\texttt{Num of Rows} & $num\_words/words\_per\_row$ \\ \hline +\texttt{Num of Cols} & $words\_per\_row*word\_size$ \\ \hline +\texttt{Col Addr Size} & $\log_2(words\_per\_row)$ \\ \hline +\texttt{Row Addr Size} & $\log_2(num\_of\_rows)$ \\ \hline +\texttt{Total Addr Size} & $row\_addr\_size + col\_addr\_size$ \\ \hline +\texttt{Data Size} & $word\_size$ \\ \hline +\texttt{Num of Bank} & $num\_banks$ \\ \hline +\end{tabular} +\label{table:variables} +\end{table} + +\subsubsection{Block Classes} + +Every other block in the memory design has a class for its base cell +(e.g., sense\_amplifier.py) and an array class (e.g., +sense\_amplifier\_array.py) that is responsible for tiling the base +cell. Each class is responsible for physically placing and logically +connecting its own sub-circuits while passing its dimensions and port +locations up to higher-level modules. + +\subsubsection{Low-Level Classes} + +OpenRAM provides parameterized transistor and logic gate +classes that help with technology portability. These classes generate +a technology-specific transistor and simple logic gate layouts so that +many modules do not rely on library cells. It is also used +when a module such as the write driver needs transistor sizing +to optimize performance. The parameterized transistor (ptx.py) generates a +basic transistor of specified type and size. The parameterized +transistor class is used to provide several parameterized gates +including pinv.py, nand2.py, nand3.py, and nor2.py. + +% FIXME +% crude fix to preven widow Section +%\clearpage +\subsection{Characterization} + +% overview +OpenRAM includes a memory characterizer that measures the timing and +power characteristics through SPICE simulation. The +characterizer has four main stages: generating the SPICE stimulus, +running the circuit simulations, parsing the simulator's output, and +producing the characteristics in a Liberty (.lib) file. + +% standard format of stimulus +The stimulus is written in standard SPICE format and can be used with +any simulator that supports this. The stimulus only uses the +interface of the memory (e.g., bi-directional data bus, address bus, +and control signals) to perform \enquote{black box} timing measurements. + +% what is measured and how +Results from simulations are used to produce the average power, +setup/hold times, and timing delay of the memory design. Setup and +hold times are obtained by analyzing the flip-flop library cell +because OpenRAM uses a completely synchronous input interface. The +setup time, hold time, and delay are found using a fast bisection +search. + +\subsection{Unit Tests} + +Probably the most important feature of OpenRAM is the set of thorough +regression tests implemented with the Python unit test framework. +These unit tests allow users to add features and easily verifying if +functionality is broken. The tests also work in multiple technologies +so they can guide users when porting to new technologies. Every module +has its own regression test and there are also regression tests for +memory functionality, verifying library cells, timing +characterization, and technology verification. + diff --git a/ICCAD16_openram_paper/introduction.tex b/ICCAD16_openram_paper/introduction.tex new file mode 100644 index 00000000..5d9ce448 --- /dev/null +++ b/ICCAD16_openram_paper/introduction.tex @@ -0,0 +1,68 @@ +\section{Introduction} +\label{sec:introduction} + +% why memory compilers are important +Static Random Access Memories (SRAMs) have become a standard component +embedded in all System-on-Chip (SoC), Application-Specific Integrated +Circuit (ASIC), and micro-processor designs. Their wide application +leads to a variety of requirements in circuit design and memory +configuration. However, manual design is +too time consuming. The +regular structure of memories leads well to automation that produces +size and configuration variations quickly, but developing this with +multiple technologies and tool methodologies is challenging. In +addition, memory designs play a significant role in overall system +performance and costs, so optimization is important. Thus, a memory +compiler is a critical tool. + +% why academics need memory compilers +Most academic ICs design methodologies are limited by the availability +of memories. Many standard-cell Process Design Kits (PDKs) are +available from foundries and vendors, but these PDKs frequently do not +come with memory arrays or memory compilers. If a memory compiler is +freely available, it often only supports a generic process technology +that is not fabricable. Due to academic funding restrictions, +commercial industry solutions are often not feasible for +researchers. In addition, these commercial solutions are limited in +customization of the memory sizes and specific components of the +memory. PDKs may have the options to request \enquote{black box} +memory models, but these are also not modifiable and have limited +available configurations. These restrictions and licensing issues make +comparison and experimentation with real world memories impossible. + +% manually designing is time consuming +Academic researchers are able to design their own custom memories, but +this can be a tedious and time-consuming task and may not be the intended +purpose of the research. Frequently, the memory design is the bare +minimum that the research project requires, +and, because of this, the memory designs are often inferior and are not +optimized. In memory research, peripheral circuits are often not +considered when comparing memory performance and density. The +lack of a customizable compiler makes it difficult for researchers to +prototype and verify circuits and methodologies beyond a single row or +column of memory cells. + +% what are the goals of OpenRAM +The OpenRAM project aims to provide an open-source memory compiler +development framework for memories. It provides reference circuit and +physical implementations in a generic $45$nm technology and fabricable +Scalable CMOS (SCMOS), but it has also been ported to several +commercial technology nodes using a simple technology file. OpenRAM +also includes a characterization methodology so that it can generate +the timing and power characterization results in addition to circuits and +layout while remaining independent of specific commercial tools. Most +importantly, OpenRAM is completely user-modifiable since all source +code is open source at: +\begin{center} +\url{https://openram.soe.ucsc.edu/} +\end{center} + +The remainder of this paper is organized as follows: +Section~\ref{sec:background} provides a background on previous memory +compilers. Section~\ref{sec:architecture} presents the reference +memory architecture in OpenRAM. Section~\ref{sec:implementation} +specifically introduces the implementation and main features of the +OpenRAM memory compiler. In Section~\ref{sec:results}, an analysis of +the area, timing and power is shown for different sizes and +technologies of memory. Finally, the paper is summarized in +Section~\ref{sec:conclusions}. diff --git a/ICCAD16_openram_paper/main.tex b/ICCAD16_openram_paper/main.tex new file mode 100644 index 00000000..de7d95de --- /dev/null +++ b/ICCAD16_openram_paper/main.tex @@ -0,0 +1,122 @@ +% This file should be compiled with V2.5 of "sig-alternate.cls" May 2012 +% This file has been modified by Brian Chen (bchen12@ucsc.edu) for the purpose of simplifying the sections +% +% This example file demonstrates the use of the 'sig-alternate.cls' +% V2.5 LaTeX2e document class file. It is for those submitting +% articles to ACM Conference Proceedings WHO DO NOT WISH TO +% STRICTLY ADHERE TO THE SIGS (PUBS-BOARD-ENDORSED) STYLE. +% The 'sig-alternate.cls' file will produce a similar-looking, +% albeit, 'tighter' paper resulting in, invariably, fewer pages. +% +% ---------------------------------------------------------------------------------------------------------------- +% This .tex file (and associated .cls V2.5) produces: +% 1) The Permission Statement +% 2) The Conference (location) Info information +% 3) The Copyright Line with ACM data +% 4) NO page numbers +% +% as against the acm_proc_article-sp.cls file which +% DOES NOT produce 1) thru' 3) above. +% +% Using 'sig-alternate.cls' you have control, however, from within +% the source .tex file, over both the CopyrightYear +% (defaulted to 200X) and the ACM Copyright Data +% (defaulted to X-XXXXX-XX-X/XX/XX). +% e.g. +% \CopyrightYear{2007} will cause 2007 to appear in the copyright line. +% \crdata{0-12345-67-8/90/12} will cause 0-12345-67-8/90/12 to appear in the copyright line. +% +% --------------------------------------------------------------------------------------------------------------- +% This .tex source is an example which *does* use +% the .bib file (from which the .bbl file % is produced). +% REMEMBER HOWEVER: After having produced the .bbl file, +% and prior to final submission, you *NEED* to 'insert' +% your .bbl file into your source .tex file so as to provide +% ONE 'self-contained' source file. +% +% ================= IF YOU HAVE QUESTIONS ======================= +% Questions regarding the SIGS styles, SIGS policies and +% procedures, Conferences etc. should be sent to +% Adrienne Griscti (griscti@acm.org) +% +% Technical questions _only_ to +% Gerald Murray (murray@hq.acm.org) +% =============================================================== +% +% For tracking purposes - this is V2.0 - May 2012 +% Custom Modified Version - November 2013 + +\documentclass{sig-alternate-05-2015} +%\RequirePackage[pdftex]{hyperref} +\usepackage{comment} +\usepackage{graphicx} +\usepackage[autostyle]{csquotes} +\usepackage{subfigure} + +\newcommand{\fixme}[1]{{\Large FIXME:} {\bf #1}} +\newcommand{\todo}[1]{{\bf TODO: {#1}}\\} +\newcommand{\note}[1]{{\bf Note:} \{#1\}\\} +\newcommand{\comm}[1]{\small{\it{ //{#1}}}} + + +% --- Author Metadata here --- +\conferenceinfo{ICCAD}{International Conference on Computer-Aided Design} +%\CopyrightYear{2007} % Allows default copyright year (20XX) to be over-ridden - IF NEED BE. +%\crdata{0-12345-67-8/90/01} % Allows default copyright data (0-89791-88-6/97/05) to be over-ridden - IF NEED BE. +% --- End of Author Metadata --- + +\title{OpenRAM: An Open-Source Memory Compiler\\ +\vspace{-0.5cm}\center{\normalsize{Invited Paper}}} +%\titlenote{Some Copyright info about OpenRAM??????}} + +\numberofauthors{1} +\author{ + %% TO DAC: Guthaus, Stine, Ataei, Chen, Wu, Sarwar +\alignauthor Matthew R. Guthaus$^1$, James E. Stine$^2$, Samira Ataei$^2$, \\Brian Chen$^1$, Bin Wu$^1$, Mehedi Sarwar$^2$ \\ +\affaddr{$^1$ Department of Computer Engineering, University of California Santa Cruz, Santa Cruz, CA 95064}\\ +\affaddr\{mrg, bchen12, bwu8\}@ucsc.edu \\ +\affaddr{$^2$ Electrical and Computer Engineering Department, Oklahoma State University, Stillwater, OK 74078}\\ +\affaddr\{james.stine, ataei, mehedis\}@okstate.edu} + + %% \alignauthor Matthew Guthaus, Brian Chen, Bin Wu \\ + %% \affaddr{Department of Computer Engineering} \\ + %% \affaddr{University of California Santa Cruz} \\ + %% \affaddr{Santa Cruz, CA 95064, USA} \\ + %% \affaddr{\{mrg,bchen12,bwu8\}@ucsc.edu} + %% \and + %% \alignauthor James Stine, Samira Ataei, Mehedi Sarwar \\ + %% \affaddr{Electrical and Computer Engineering Department} \\ + %% \affaddr{Oklahoma State University} \\ + %% \affaddr{Stillwater, OK 74078} \\ + %% \affaddr{\{james.stine,ataei,XXXX\}@okstate.edu} + %%} + + +\begin{document} + +\CopyrightYear{2016} +\setcopyright{acmlicensed} +\conferenceinfo{ICCAD '16,}{November 07 - 10, 2016, Austin, TX, USA} +\isbn{978-1-4503-4466-1/16/11}\acmPrice{\$15.00} +\doi{http://dx.doi.org/10.1145/2966986.2980098} +\maketitle + +\input{abstract} + +%\category{J.6}{COMPUTER-AIDED ENGINEERING}{\\Computer-aided design (CAD)} +%\terms{Design, Algorithms} +%\keywords{OpenRAM, Memory Compiler, Open-source} + +\input{introduction} +\input{background} +\input{architecture} +\input{implementation} +\input{results} +\input{conclusion} +\input{acknowledgments} + +\bibliographystyle{abbrv} +\bibliography{references} % Create bibliography using the file: references.bib + +%\input{appendix} +\end{document} diff --git a/ICCAD16_openram_paper/references.bib b/ICCAD16_openram_paper/references.bib new file mode 100644 index 00000000..f5c56f41 --- /dev/null +++ b/ICCAD16_openram_paper/references.bib @@ -0,0 +1,586 @@ +@Comment @string{DAC = "ACM/IEEE Design Automation Conference~(DAC)"} +@Comment @string{TDEV = "IEEE Transactions on Electron Devices"} +@Comment @string{DATE = "IEEE Design, Automation and Test in Europe~(DATE)"} +@Comment @string{ISSCC = "IEEE International Solid-State Circuits Conference~(ISSCC)"} +@Comment @string{TVLSI = "IEEE Transactions on Very Large Scale Integration~(VLSI) Systems"} +@Comment @string{JSSC = "IEEE Journal of Solid-State Circuits~(JSSC)"} +@Comment @string{ICCD = "International Conference on Computer Design~(ICCD)"} +@Comment @string{ISLPED = "IEEE International Symposium on Low Power Electronics and Design~(ISLPED)"} +@Comment @STRING{ICCAD = "IEEE/ACM International Conference on Computer-Aided Design~(ICCAD)"} +@Comment @string{ASP-DAC = "IEEE Asia and South Pacific Design Automation Conference~(ASP-DAC)"} +@Comment @string{ISCAS = "IEEE International Symposium on Circuits and Systems~(ISCAS)"} +@Comment @string{TCAD = "IEEE Transactions on Computer-Aided Design of Integrated Circuits and Systems~(TCAD)"} +@Comment @string{GLSVLSI = "ACM Great Lakes Symposium on VLSI~(GLSVLSI)"} +@Comment @string{TCASI = "IEEE Transactions on Circuits and Systems I~(TCAS-I)"} +@Comment @string{TCASII = "IEEE Transactions on Circuits and Systems II~(TCAS-II)"} +@Comment @string{TC = "IEEE Transactions on Computers"} +@Comment @string{ISPD = "IEEE International Symposium on Physical Design~(ISPD)"} +@Comment @string{TODAES = "ACM Transactions on Design Automation of Electronic Systems~(TODAES)"} +@Comment @string{ISVLSI = "IEEE International Symposium on Very Large Scale Integration~(ISVLSI)"} +@Comment @string{ISQED = "International Symposium on Quality Electronic Design~(ISQED)"} +@Comment @string{TNUKE = "IEEE Transactions on Nuclear Science"} +@Comment @string{MWSCAS = "IEEE Midwest Symposium on Circuits and Systems~(MWSCAS)"} +@Comment @string{MSE = "IEEE International Conference on Microelectronic Systems Education~(MSE)"} + +@string{DAC = "DAC"} +@string{TDEV = "TDEV"} +@string{DATE = "DATE"} +@string{ISSCC = "ISSCC"} +@string{TVLSI = "TVLSI"} +@string{JSSC = "JSSC"} +@string{ICCD = "ICCD"} +@string{ISLPED = "ISLPED"} +@STRING{ICCAD = "ICCAD"} +@string{ASP-DAC = "ASP-DAC"} +@string{ISCAS = "ISCAS"} +@string{TCAD = "TCAD"} +@string{GLSVLSI = "GLSVLSI"} +@string{TCASI = "TCAS-I"} +@string{TCASII = "TCAS-II"} +@string{TC = "TCOMP"} +@string{ISPD = "ISPD"} +@string{TODAES = "TODAES"} +@string{ISVLSI = "ISVLSI"} +@string{ISQED = "ISQED"} +@string{TNUKE = "Trans. on Nuclear Science"} +@string{MWSCAS = "MWSCAS"} +@string{MSE = "MSE"} + +@book{Rabaey:2003, +title = {Digital Integrated Circuits: A Design Perspective}, +author = {J. Rabaey and A. Chandrakasan and B. Nikolić}, +year = {2003}, +publisher = {Pearson Education, Inc.}, +edition = {2nd} +} + +@book{Chandrakasan:2001, +title = {Design of High Performance Microprocessor Circuits}, +booktitle = {Design of High Performance Microprocessor Circuits}, +author = {A. Chandrakasan and W.J. Bowhill and F. Fox}, +year = {2001}, +publisher = {IEEE Press} +} + +@manual{gdsmill, +title = {GDS Mill User Manual}, +author = {M. Wieckowski}, +year = {2010} +} + +%these are end of chapter references from Rabaey +%%%%%%%%%%% +@article{Amrutur:2001, +author = {B.S. Amrutur and M.A. Horowitz}, +journal = JSSC, +title = {Fast Low-Power Decoders for RAMs}, +number = {10}, +pages = {1506-1515}, +volume = {36}, +year = {2001}, +month = {Oct} +} + +@inbook{Preston:2001, +title = {Register Files and Caches}, +author = {R.P. Preston}, +crossref = {Chandrakasan:2001} +} + +@book{Itoh:2001, +title = {VLSI Memory Chip Design}, +author = {K. Itoh}, +publisher = {Springer-Verlag}, +year = {2001} +} + +@article{Itoh:1990, +author = {K. Itoh}, +journal = JSSC, +title = {Trends in Megabit DRAM Circuit Design}, +number = {3}, +pages = {778-798}, +volume = {25}, +year = {1990}, +month = {Jun} +} + +@article{May:1979, +author = {T. May and M. Woods}, +journal = TDEV, +title = {Aplha-Particle-Induced Soft Errors in Dynamic Memories}, +number = {1}, +pages = {2-9}, +volume = {26}, +year = {1979}, +month = {Jan} +} + +@ARTICLE{Tosaka:1997, +author={Y. Tosaka and S. Satoh and T. Itakura and K. Suzuki and T. Sugii and H. Ehara and G.A. Woffinden}, +journal=TDEV, +title={Cosmic Ray Neutron-Induced Soft Errors in Sub-Half Micron CMOS Circuits}, +year={1997}, +volume={18}, +number={3}, +pages={99-101} +} + +@ARTICLE{Regitz:1970, +author={W.M. Regitz and J. Karp}, +journal=JSSC, +title={Three-transistor-cell 1024-bit 500-ns MOS RAM}, +year={1970}, +volume={5}, +number={5}, +pages={181-186} +} + +@INPROCEEDINGS{Kim1:2011, +author={S. Kim and M. Guthaus}, +booktitle=DAC, +title={Leakage-aware redundancy for reliable sub-threshold memories}, +year={2011}, +pages={435-440} +} + +@INPROCEEDINGS{Kim2:2011, +author={S. Kim and M. Guthaus}, +booktitle=VLSISOC, +title={SNM-aware power reduction and reliability improvement in 45nm {SRAM}s}, +year={2011}, +pages={204-207} +} + +@INPROCEEDINGS{Kim3:2011, +author={S. Kim and M. Guthaus}, +booktitle=ICCAD, +title={Low-power multiple-bit upset tolerant memory optimization}, +year={2011}, +pages={577-581} +} + +@INPROCEEDINGS{Kim:2012, +author={S. Kim and M. Guthaus}, +booktitle=VLSISOC, +title={Dynamic voltage scaling for SEU-tolerance in low-power memories}, +year={2012}, +pages={207-212} +} + + +@ARTICLE{Rusu:2003, +author={S. Rusu and J. Stinson and S. Tam and J. Leung and H. Muljono and B. Cherkauer}, +journal=JSSC, +title={A 1.5-GHz 130-nm Itanium reg; 2 Processor with 6-MB on-die L3 cache}, +year={2003}, +volume={38}, +number={11}, +pages={1887-1895} +} + +@article{itrs:2012, +author = {International Technology Roadmap for Semiconductors}, +title = {2012 ITRS Report: System Drivers}, +howpublished = {www.itrs.net}, +year = {2012} +} + +@article{Kurdahi:2006, +author = {F.J. Kurdahi and A.M. Eltawil and Y.H. Park and R.N Kanj and S.R. Nassif}, +journal = ISQED, +title = {System-level {SRAM} Yield Enhancement}, +year = {2006}, +month = {Mar} +} + + +@misc{i7:2011, + author = {A. Shimpi}, + title = {Intel Core i7 3960X (Sandy Bridge) Review: Keeping the High-End Alive}, + howpublished = {\url{http://www.anandtech.com/show/5091/intel-core-i7-3960x-sandy-bridge-e-review-keeping-the-high-end-alive}}, + year = {2011}, + month = {Nov} +} + + + +@misc{calibre:2013, + author = {Mentor Graphics}, + title = {Calibre nmDRC and nmLVS}, + howpublished = {\url{http://www.mentor.com/products/ic_nanometer_design/verification-signoff/physical-verification/}}, + year = {2013} +} + +@misc{hspice:2013, + author = {Synopsis}, + title = {HSPICE}, + howpublished = {\url{http://www.synopsys.com/tools/Verification/AMSVerification/CircuitSimulation/HSPICE/Pages/default.aspx}}, + year = {2013} +} + +@INPROCEEDINGS{Athe:2009, +author={P. Athe and S. Dasgupta}, +booktitle={{ISIEA}}, +title={A comparative study of 6T, 8T and 9T decanano {SRAM} cell}, +year={2009}, +volume={2}, +pages={889-894} +} + +@ARTICLE{Calin:1996, +author={T. Calin and M. Nicolaidis and R. Velazco}, +journal=TNUKE, +title={Upset hardened memory design for submicron CMOS technology}, +year={1996}, +volume={43}, +number={6}, +pages={2874-2878} +} + +@INPROCEEDINGS{Jung:2012, +author={I. Jung and Y. Kim and F. Lombardi}, +booktitle=MWSCAS, +title={A novel sort error hardened 10T {SRAM} cells for low voltage operation}, +year={2012}, +pages={714-717} +} + +@ARTICLE{Goudarzi:2010, +author={M. Goudarzi and T. Ishihara}, +journal=TVLSI, +title={{SRAM} Leakage Reduction by Row/Column Redundancy Under Random Within-Die Delay Variation}, +year={2010}, +volume={18}, +number={12}, +pages={1660-1671} +} + +@techreport{ibm:1997, + author = {IBM}, + title = {Understanding Static RAM Operation}, + howpublished = {IBM Applications Note}, + year = {1997}, + month = {Mar} +} + + +@misc{python:2013, + author = {Python}, + title = {The Python Programming Language}, + howpublished = {\url{http://www.python.org}}, + year = {2013} +} + + +@misc{Wieckowski:2010, + author = {Michael Wieckowski}, + title = {GDS Mill}, + howpublished = {\url{http://michaelwieckowski.com/?page_id=190}}, + year = {2010} +} + +@misc{globalfoundries:2015, + author = {{Global Foundries}}, + title = {{ASICs}}, + howpublished = {\url{http://www.globalfoundries.com/technology-solutions/asics}}, + year = {2015} +} + +@misc{synopsys:2015, + author = {Synopsys}, + title = {DesignWare Memory Compilers}, + howpublished = {\url{http://www.synopsys.com/dw/ipdir.php?ds=dwc_sram_memory_compilers}}, + year = {2015} +} + +@misc{dolphin:2015, + author = {{Dolphin Technology}}, + title = {Memory Products}, + howpublished = {\url{http://www.dolphin-ic.com/memory-products.html}}, + year = {2015} +} + +@misc{faraday:2015, + author = {{Faraday Technologies}}, + title = {Memory Compiler Architecture}, + howpublished = {\url{http://www.faraday-tech.com/html/Product/IPProduct/LibraryMemoryCompiler/index.htm}}, + year = {2015} +} + +@misc{arm:2015, + author = {ARM}, + title = {Embedded Memory {IP}}, + howpublished = {\url{http://www.arm.com/products/physical-ip/embedded-memory-ip/index.php}}, + year = {2015} +} + + +@misc{scmos, + author = {MOSIS}, + title = {{MOSIS} Scalable {CMOS} ({SCMOS})}, + howpublished = {\url{https://www.mosis.com/files/scmos/scmos.pdf}}, + year = {2015} + } + +%%%look at this paper +@article{Hanson:2008, +author = {S. Hanson and M. Seok and D. Sylvester and D. Blaauw}, +journal = TDEV, +title = {Nanometer device scaling in subthreshold logic and {SRAM}}, +number = {1}, +pages = {175-185}, +volume = {55}, +year = {2008} +} + +%%%look at this paper +@article{Baeg:2009, +author={S. Baeg and S. Wen and R. Wong}, +journal=TNUKE, +title={{SRAM} Interleaving Distance Selection With a Soft Error Failure Model}, +year={2009}, +month={Aug.}, +volume={56}, +number={4}, +pages={2111-2118} +} + +%%%look at this paper +@article{Amrutur:2001, +author={B. Amrutur and M. Horowitz}, +journal=JSSC, +title={Fast low-power decoders for {RAMs}}, +year={2001}, +month={Oct}, +volume={36}, +number={10}, +pages={1506-1515} +} + +@inproceedings{Chen:2012, +author={Chen Ming and Bai Na}, +booktitle={{CyberC}}, +title={An Efficient and Flexible Embedded Memory {IP} Compiler}, +year={2012}, +month={Oct}, +pages={268-273}, +keywords={SRAM chips;embedded systems;interpolation;polynomials;circuit structure;efficient embedded memory IP compiler;flexible embedded memory IP compiler;polynomial interpolation algorithm;single-port SRAM compiler;Integrated circuit modeling;Interpolation;Mathematical model;Memory management;Random access memory;Tiles;Timing;SRAM;interpolation;memory compiler;modeling;tiling}, +doi={10.1109/CyberC.2012.52} +} + +@inproceedings{Wu:2010, +author={Sheng Wu and Xiang Zheng and Zhiqiang Gao and Xiangqing He}, +booktitle={{DDECS}}, +title={A 65nm embedded low power {SRAM} compiler}, +year={2010}, +month={April}, +pages={123-124}, +keywords={CMOS technology;Design methodology;Helium;Kernel;Layout;Libraries;Microelectronics;Program processors;Random access memory;SRAM chips;SRAM compiler;SoC IP;low power}, +doi={10.1109/DDECS.2010.5491802} +} + +@inproceedings{Xu:2007, +author={Yi Xu and Zhiqiang Gao and Xiangqing He}, +booktitle=ISCAS, +title={A Flexible Embedded {SRAM} {IP} Compiler}, +year={2007}, +month={May}, +pages={3756-3759}, +keywords={SRAM chips;circuit layout CAD;elemental semiconductors;embedded systems;logic design;program compilers;silicon;Si;atatic random access memory;block assembly techniques;embedded SRAM IP compiler;physical data syntax;silicon compiler;Assembly;Capacitance;Circuits;Energy consumption;Graphical user interfaces;Helium;Microelectronics;Random access memory;SRAM chips;Silicon compiler}, +doi={10.1109/ISCAS.2007.378778} +} + +%%%%Newest memory compiler on market in 2014 +@inproceedings{Goldman:2014, +author={Goldman, R. and Bartleson, K. and Wood, T. and Melikyan, V. and Babayan, E.}, +booktitle={{EWME}}, +title={Synopsys' Educational Generic Memory Compiler}, +year={2014}, +month={May}, +pages={89-92}, +keywords={SRAM chips;courseware;electronic engineering computing;electronic engineering education;GMC software tool;Synopsys educational generic memory compiler software tool;automatic SRAM cell generation;automatic static RAM cell generation;educational designs;educational process;intellectual property restrictions;Educational institutions;Layout;Memory management;Multiplexing;Ports (Computers);Random access memory;Software}, +doi={10.1109/EWME.2014.6877402} +} + +@mastersthesis{butera:2013, + author = {J. Butera}, + title = {OpenRAM: An Open-Source Memory Compiler}, + school = {University of California - Santa Cruz}, + year = {2013} +} + +@inproceedings{johannsen:blocks, + author = {D. Jahannsen}, + title = {Bristle Blocks: A Silicon Compiler}, + booktitle = DAC, + pages = {195-198}, + year = {1979} +} + +@book{broderson:sicompiler, + author = {R. Broderson}, + title = {Anatomy of a Silicon Compiler}, + publisher = {Springer}, + year = {1992} +} + +@inproceedings{poechmueller:array, + author = {P. Poechmueller and G.~K. Sharma and M. Glesner}, + title = {A {CAD} Tool for Designing Large, Fault-Tolerant {VLSI} Arrays}, + booktitle = GLSVLSI, + year = {1991} +} + +@inproceedings{huang:array, + author = {T.-H. Huang and C.-M. Liu and C.-W. Jen}, + title = {A High-Level Synthesizer for {VLSI} Array Architectures Dedicated to Digital Signal Processing}, + booktitle = {International Conference on Acoustics, Speech and Signal Processing}, + pages = {1221-1224}, + year = {1991} +} + + +@article{cabe:flexible, +author = {AC Cabe and Z Qi and W Huang and Y Zhang and MR Stan and GS Rose}, +journal = {Cadence CDNLive}, +title = {A flexible, technology adaptive memory generation tool}, +year = {2006}, +} + +@mastersthesis{fabmem:2010, + author = {T. Shah}, + title = {{FabMem}: A Multiported {RAM} and {CAM} Compiler for Superscalar Design Space Exploration}, + school = {North Carolina State University}, + year = {2010} +} + +@misc{virage:2015, + author = {{Virage Logic}}, + title = {{SiWare} Memory}, + howpublished = {\url{http://www.viragelogic.com}}, + year = {2015} +} + + +@article{RBL:1998, +author = {B. S. Amrutur and M. A. Horowitz}, +journal = JSSC, +title = {A Replica Technique for Wordline and Sense Control in Low-Power {SRAM}s}, +number = {8}, +pages = {1208-1219}, +volume = {33}, +year = {1998}, +month = {Aug} +} + + + + +% references for bit-density comparison + + +@article{Bit_Density_1, +author = {K. Kushida and others}, +journal = JSSC, +title = {A 0.7 {V} Single-Supply {SRAM} With 0.495 $um^2$ Cell in 65 nm Technology Utilizing Self-Write-Back Sense Amplifier and Cascaded Bit Line Scheme}, +number = {4}, +pages = {1192-1198}, +volume = {44}, +year = {2009}, +month = {Apr} +} + + +@article{Bit_Density_2, +author = {Sh. Miyano and others}, +journal = JSSC, +title = {Highly Energy-Efficient {SRAM} With Hierarchical Bit Line Charge-Sharing Method Using Non-Selected Bit Line Charges}, +number = {4}, +pages = {924-931}, +volume = {48}, +year = {2013}, +month = {Apr} +} + +@article{Bit_Density_3, +author = {S. O. Toh and Zh. Guo and T. K. Liu and B. Nikolic}, +journal = JSSC, +title = {Characterization of Dynamic {SRAM} Stability in 45 nm {CMOS}}, +number = {11}, +pages = {2702-2712}, +volume = {46}, +year = {2011}, +month = {Nov} +} + +@article{Bit_Density_4, +author = {K. Yamaguchi and others}, +journal = JSSC, +title = {A 1.5-ns Access Time, 78- $um^2$ Memory-Cell Size, 64-kb {ECL-CMOS SRAM}}, +number = {2}, +pages = {167-174}, +volume = {27}, +year = {1992}, +month = {Feb} +} + +@article{Bit_Density_5, +author = {N. Shibata and H. Morimura and M. Watanabe}, +journal = JSSC, +title = {A {1-V}, {10-MHz}, 3.5-mW, {1-Mb} {MTCMOS SRAM} with Charge-Recycling Input/Output Buffers}, +number = {6}, +pages = {866-877}, +volume = {34}, +year = {1999}, +month = {Jun} +} + +@article{Bit_Density_6, +author = {N. Tamba and others}, +journal = JSSC, +title = {A 1.5-ns 256-kb {BiCMOS SRAM} with 60-ps 11-K Logic Gates}, +number = {11}, +pages = {1344-1352}, +volume = {48}, +year = {1994}, +month = {Nov} +} + +%author={Yamaguchi, K. and Nambu, H. and Kanetani, K. and Idei, Y. and Homma, N. and Hiramoto, T. and Tamba, N. and Watanabe, K. and Odaka, Masanori and Ikeda, T. and Ohhata, K. and Sakurai, Y.}, +@ARTICLE{127339, +author={Yamaguchi, K. and others}, +journal=JSSC, +title={A $1.5$-ns access time, $78~um^2$ memory-cell size, $64$-kb {ECL-CMOS SRAM}}, +year={1992}, +volume={27}, +number={2}, +pages={167-174}, +doi={10.1109/4.127339}, +ISSN={0018-9200}, +month={Feb}, +} + +%author={Kushida, K. and Suzuki, A. and Fukano, G. and Kawasumi, A. and Hirabayashi, O. and Takeyama, Y. and Sasaki, T. and Katayama, A. and Fujimura, Y. and Yabe, T.}, +@INPROCEEDINGS{4585946, +author={Kushida, K. and others}, +booktitle=ISVLSI, +title={A $0.7$V single-supply {SRAM} with $0.495~um^2$ cell in $65$nm technology + utilizing self-write-back sense amplifier and cascaded bit + line scheme}, +year={2008}, +pages={46-47}, +doi={10.1109/VLSIC.2008.4585946}, +month={June} +} + +%author={Stine, J.E. and Castellanos, I. and Wood, M. and Henson, J. and Love, F. and Davis, W.R. and Franzon, P.D. and Bucher, M. and Basavarajaiah, S. and Julie Oh and Jenkal, R.}, +@INPROCEEDINGS{4231502, +author={J. E. Stine and others}, +booktitle=MSE, +title={{FreePDK}: An Open-Source Variation-Aware Design Kit}, +year={2007}, +pages={173-174}, +doi={10.1109/MSE.2007.44}, +month={June} +} \ No newline at end of file diff --git a/ICCAD16_openram_paper/results.tex b/ICCAD16_openram_paper/results.tex new file mode 100644 index 00000000..e6cd5380 --- /dev/null +++ b/ICCAD16_openram_paper/results.tex @@ -0,0 +1,115 @@ +\section{Results} +\label{sec:results} + +Figure~\ref{fig:layout} shows several different SRAM layouts +generated by OpenRAM in FreePDK45. OpenRAM can generate single +bank and multi-bank SRAM arrays. Banks are +symmetrically placed to have the same delay for data and address +while sharing peripheral blocks such as decoders. +\begin{figure}[tb] +\centering +\includegraphics[scale=.4]{./figs/layout.pdf} +\caption{Single bank and multi-bank SRAMs (not to scale) use + symmetrical bank placement to share peripheral circuitry and + equalize signal delays.} +\label{fig:layout} +\end{figure} + +Figure~\ref{fig:density_figure} shows the memory area of different +total size and data word width memories in both FreePDK45 and +SCMOS. As expected, the smaller process technology (45nm) has lower +total area overall but the trends are similar in both technologies. + +Figure~\ref{fig:density_figure} also shows the access time of +different size and data word width in FreePDK45 and SCMOS. Increasing +the memory size generally increases the access time; long bit-lines +and word-lines increase the access time by adding more parasitic +capacitance and resistance. Since OpenRAM uses multiple banks and +column muxing, it is possible to have a smaller access time for larger +memory designs, but this will sacrifice density. + +\begin{figure}[tb] +\begin{center} +\centering +%\includegraphics[width=8.5cm]{./figs/Results.pdf} +\includegraphics[width=7.5cm , height=14cm]{./figs/Results2.pdf} +% \subfigure[FreePDK45 memory area \label{fig:freepdk_area}]{ +% \includegraphics[scale=1]{./figs/Freepdk_Area.pdf}} +% \subfigure[SCMOS memory area \label{fig:scn3me_area}]{ +% \includegraphics[scale=.5]{./figs/Scn3me_Area.pdf}} + \caption{OpenRAM provides high-density memories in multiple + technologies and sizes with corresponding characterized + delays. \label{fig:density_figure}} + \vspace{-0.5cm} +\end{center} +\end{figure} + +%Table~\ref{table:bit-density-comparison} shows a comparison between bit +%density of OpenRAM's generated memory designs and other publications +%which are close in technology node with FreePDK45 and SCMOS. As shown +%in this table, OpenRAM provides very dense SRAM arrays in both technologies. + +\begin{table}[t] +\centering +\caption{OpenRAM has high density compared to other published memories in + similar technologies.} +\begin{tabular}{|c|c|c|c|l|l|l|l|l|} \hline +\texttt{Ref.} & \texttt{Feature} & \texttt{Tech.} & \texttt{Density} \\ + & \texttt{Size} & & [Mb/$mm^2$] \\ +\hline \hline +$~\cite{4585946}$ & $65$ nm & CMOS & $0.7700$ \\ \hline +$~\cite{Bit_Density_3}$ & $45$ nm & CMOS & $0.3300$ \\ \hline +$~\cite{Bit_Density_2}$ & $40$ nm & CMOS & $0.9400$ \\ \hline +\verb+OpenRAM+ & $45$ nm & FreePDK45 & $0.8260$ \\ \hline \hline +$~\cite{127339}$ & $0.5$ um & CMOS & $0.0036$ \\ \hline +$~\cite{Bit_Density_6}$ & $0.5$ um & BiCMOS & $0.0020$ \\ \hline +$~\cite{Bit_Density_5}$ & $0.5$ um & CMOS & $0.0050$ \\ \hline +\verb+OpenRAM+ & $0.5$ um & SCMOS & $0.0050$ \\ \hline +\end{tabular} +\label{table:bit-density-comparison} +\end{table} + +%\begin{table*} +%\centering +%\caption{OpenRAM has high density, fast access time and low power consumption compared to other published memories in similar technologies.} +%\begin{tabular}{|c|l|l|l|l|l|l|l|l|} \hline +%\texttt{Reference} & \texttt{Technology} & \texttt{Density (Mb/$mm^2$)}& \texttt{Access time (ns)}& \texttt{Power consumption} \\ \hline \hline +%$~\cite{Bit_Density_1}$ & $65 nm CMOS$ & $0.77$ & $28$ & $22$ $uW/MHz$ \\ \hline +%$~\cite{Bit_Density_2}$ & $40 nm CMOS$ & $0.94$ & $45$ & $13.8$ $pJ/access/Mbit$ \\ \hline +%$OpenRAM$ & $45 nm FreePDK45$ & $0.826$ & $9.86$ & $13.14$ $mW$ \\ \hline \hline +%$~\cite{Bit_Density_4}$ & $0.5 um CMOS$ & $0.0036$ & $1.5$ & $6$ $W$ \\ \hline +%$~\cite{Bit_Density_6}$ & $0.5 um BiCMOS$ & $0.002$ & $1.5$ & $35$ $W$ \\ \hline +%$~\cite{Bit_Density_5}$ & $0.5 um CMOS$ & $0.005$ & $75$ & $3.9$ $mW$ \\ \hline +%$OpenRAM$ & $0.5 um SCMOS$ & $0.005$ & $44.9$ & $115$ $mW$ \\ \hline +%\end{tabular} +%\label{table:bit-density-comparison} +%\end{table*} + +Comparison of power consumption and read access time of different +memories is a bit more complicated to make a conclusion, because there +are many trade-offs. Power and performance are highly dependent on +circuit style (CMOS, ECL, etc.), memory organization (more banks is +faster but sacrifices density), and the optimization goal: low-power +or high-performance. In general, OpenRAM has reasonable trade-off +between the two and can be customized by using an alternate sense +amplifiers, decoders, or overall dimensional organization. +Table~\ref{table:bit-density-comparison} compares the bit-density of +OpenRAM against published designs using similar technology nodes. The +results show the benefit of technology scaling and that OpenRAM has +very good density in both technologies. As a comparison, a 76ns SRAM +consumes 3.9mW~\cite{Bit_Density_5} while OpenRAM is much faster at +44.9ns but consumes 115mW for the same size. + +%Table~\ref{table:bit-density-comparison} shows a comparison between bit density, access +%time and power consumption of OpenRAM’s generated mem- +%ory designs and other publications which are close in tech- +%nology node with FreePDK45 and SCMOS. As shown in this +%table, OpenRAM provides very dense SRAM arrays in both +%technologies. There is no easy comparison on power con- +%sumption and read access time as these values vary with the +%array size and configuration. Therefore, we only try to com- +%pare the features of each work from a more general point of +%view. + + + diff --git a/ICCAD16_openram_paper/sig-alternate-05-2015.cls b/ICCAD16_openram_paper/sig-alternate-05-2015.cls new file mode 100755 index 00000000..b922ec29 --- /dev/null +++ b/ICCAD16_openram_paper/sig-alternate-05-2015.cls @@ -0,0 +1,1893 @@ +% SIG-ALTERNATE.CLS - VERSION 2.8 +% "COMPATIBLE" WITH THE "ACM_PROC_ARTICLE-SP.CLS" V3.2SP +% Gerald Murray - May 23rd 2012 +% Boris Veytsman - April 23 2013 +% Boris Veytsman - May 12 2013 +% Boris Veytsman - June 09 2013 +% Boris Veytsman - August 12 2013 +% +% ---- Start of 'updates' ---- +% Added new permission/copyright statement - BV +% Changed $10 fee to $15 -- May 2012 -- Gerry +% Changed $5 fee to $10 -- April 2009 -- Gerry +% April 22nd. 2009 - Fixed 'Natbib' incompatibility problem - Gerry +% April 22nd. 2009 - Fixed 'Babel' incompatibility problem - Gerry +% April 22nd. 2009 - Inserted various bug-fixes and improvements - Gerry +% +% To produce Type 1 fonts in the document plus allow for 'normal LaTeX accenting' in the critical areas; +% title, author block, section-heads, confname, etc. etc. +% i.e. the whole purpose of this version update is to NOT resort to 'inelegant accent patches'. +% After much research, three extra .sty packages were added to the the tail (ae, aecompl, aeguill) to solve, +% in particular, the accenting problem(s). We _could_ ask authors (via instructions/sample file) to 'include' these in +% the source .tex file - in the preamble - but if everything is already provided ('behind the scenes' - embedded IN the .cls) +% then this is less work for authors and also makes everything appear 'vanilla'. +% NOTE: all 'patchwork accenting" has been commented out (here) and is no longer 'used' in the sample .tex file (either). +% Gerry June 2007 +% +% Patch for accenting in conference name/location. Gerry May 3rd. 2007 +% Rule widths changed to .5, author count (>6) fixed, roll-back for Type 3 problem. Gerry March 20th. 2007 +% Changes made to 'modernize' the fontnames but esp. for MikTeX users V2.4/2.5 - Nov. 30th. 2006 +% Updated the \email definition to allow for its use inside of 'shared affiliations' - Nov. 30th. 2006 +% Fixed the 'section number depth value' - Nov. 30th. 2006 +% +% Footnotes inside table cells using \minipage (Oct. 2002) +% Georgia fixed bug in sub-sub-section numbering in paragraphs (July 29th. 2002) +% JS/GM fix to vertical spacing before Proofs (July 30th. 2002) +% +% Made the Permission Statement / Conference Info / Copyright Info +% 'user definable' in the source .tex file OR automatic if +% not specified. +% +% Allowance made to switch default fonts between those systems using +% normal/modern font names and those using 'Type 1' or 'Truetype' fonts. +% See LINE NUMBER 255 for details. +% Also provided for enumerated/annotated Corollaries 'surrounded' by +% enumerated Theorems (line 848). +% Gerry November 11th. 1999 +% +% ---- End of 'updates' ---- +% +\def\fileversion{v2.9} % for ACM's tracking purposes +\def\filedate{August 12, 2013} % Gerry Murray's tracking data +\def\docdate {\filedate} +\usepackage{epsfig} +\usepackage{amssymb} +\usepackage{amsmath} +\usepackage{amsfonts} +% Need this for accents in Arial/Helvetica +%\usepackage[T1]{fontenc} % Gerry March 12, 2007 - causes Type 3 problems (body text) +%\usepackage{textcomp} +% +% SIG-ALTERNATE DOCUMENT STYLE +% G.K.M. Tobin August-October 1999 +% adapted from ARTICLE document style by Ken Traub, Olin Shivers +% also using elements of esub2acm.cls +% HEAVILY MODIFIED, SUBSEQUENTLY, BY GERRY MURRAY 2000 +% ARTICLE DOCUMENT STYLE -- Released 16 March 1988 +% for LaTeX version 2.09 +% Copyright (C) 1988 by Leslie Lamport +% +% +%%% sig-alternate.cls is an 'ALTERNATE' document style for producing +%%% two-column camera-ready pages for ACM conferences. +%%% THIS FILE DOES NOT STRICTLY ADHERE TO THE SIGS (BOARD-ENDORSED) +%%% PROCEEDINGS STYLE. It has been designed to produce a 'tighter' +%%% paper in response to concerns over page budgets. +%%% The main features of this style are: +%%% +%%% 1) Two columns. +%%% 2) Side and top margins of 4.5pc, bottom margin of 6pc, column gutter of +%%% 2pc, hence columns are 20pc wide and 55.5pc tall. (6pc =3D 1in, approx) +%%% 3) First page has title information, and an extra 6pc of space at the +%%% bottom of the first column for the ACM copyright notice. +%%% 4) Text is 9pt on 10pt baselines; titles (except main) are 9pt bold. +%%% +%%% +%%% There are a few restrictions you must observe: +%%% +%%% 1) You cannot change the font size; ACM wants you to use 9pt. +%%% 3) You must start your paper with the \maketitle command. Prior to the +%%% \maketitle you must have \title and \author commands. If you have a +%%% \date command it will be ignored; no date appears on the paper, since +%%% the proceedings will have a date on the front cover. +%%% 4) Marginal paragraphs, tables of contents, lists of figures and tables, +%%% and page headings are all forbidden. +%%% 5) The `figure' environment will produce a figure one column wide; if you +%%% want one that is two columns wide, use `figure*'. +%%% +% +%%% Copyright Space: +%%% This style automatically reserves 1" blank space at the bottom of page 1/ +%%% column 1. This space can optionally be filled with some text using the +%%% \toappear{...} command. If used, this command must be BEFORE the \maketitle +%%% command. If this command is defined AND [preprint] is on, then the +%%% space is filled with the {...} text (at the bottom); otherwise, it is +%%% blank. If you use \toappearbox{...} instead of \toappear{...} then a +%%% box will be drawn around the text (if [preprint] is on). +%%% +%%% A typical usage looks like this: +%%% \toappear{To appear in the Ninth AES Conference on Medievil Lithuanian +%%% Embalming Technique, June 1991, Alfaretta, Georgia.} +%%% This will be included in the preprint, and left out of the conference +%%% version. +%%% +%%% WARNING: +%%% Some dvi-ps converters heuristically allow chars to drift from their +%%% true positions a few pixels. This may be noticeable with the 9pt sans-serif +%%% bold font used for section headers. +%%% You may turn this hackery off via the -e option: +%%% dvips -e 0 foo.dvi >foo.ps +%%% +\typeout{Document Class 'sig-alternate' <9th June '13>. Modified by + G.K.M. Tobin/Gerry Murray/Boris Veytsman} +\typeout{Based in part upon document Style `acmconf' <22 May 89>. Hacked 4/91 by} +\typeout{shivers@cs.cmu.edu, 4/93 by theobald@cs.mcgill.ca} +\typeout{Excerpts were taken from (Journal Style) 'esub2acm.cls'.} +\typeout{****** Bugs/comments/suggestions/technicalities to Gerry Murray -- murray@hq.acm.org ******} +\typeout{Questions on the style, SIGS policies, etc. to Adrienne Griscti griscti@acm.org} + +% New option code by BV + +\newcount\ACM@basesize +\ACM@basesize=9\relax +\DeclareOption{9pt}{\ACM@basesize=9\relax} +\DeclareOption{10pt}{\ACM@basesize=10\relax} +\DeclareOption{11pt}{\ClassError{sig-alternate}{The `11pt' option is + not allowed}{sig-alternate now exists in 9pt and 10pt versions only}} +\DeclareOption{12pt}{\ClassError{sig-alternate}{The `12pt' option is + not allowed}{sig-alternate now exists in 9pt and 10pt versions only}} + +\ExecuteOptions{9pt} +\ProcessOptions + + +\let\@concepts\@empty +% Support for CCSXML file +\RequirePackage{comment} +\excludecomment{CCSXML} + +% New concepts scheme +% +% The first argument is the significance, the +% second is the concept(s) +% +\newcommand\ccsdesc[2][100]{% + \ccsdesc@parse#1~#2~} +% +% The parser of the expression Significance~General~Specific +% +\def\ccsdesc@parse#1~#2~#3~{% + \expandafter\ifx\csname CCS@#2\endcsname\relax + \expandafter\gdef\csname CCS@#2\endcsname{\textbullet\textbf{#2} $\to$ }% + \g@addto@macro{\@concepts}{\csname CCS@#2\endcsname}\fi + \expandafter\g@addto@macro\expandafter{\csname CCS@#2\endcsname}{% + \ifnum#1>499\textbf{#3; }\else + \ifnum#1>299\textit{#3; }\else + #3; \fi\fi}} + +\newcommand\printccsdesc{% + \ifx\@concepts\@empty\else + \if@twocolumn + \section*{CCS Concepts} + \@concepts + \else \small + \quotation{\@concepts}% + \fi + \fi} + + + + +\def\doi#1{\def\@doi{#1}} +\doi{http://dx.doi.org/10.1145/0000000.0000000} + +\oddsidemargin 4.5pc +\evensidemargin 4.5pc +\advance\oddsidemargin by -1in % Correct for LaTeX gratuitousness +\advance\evensidemargin by -1in % Correct for LaTeX gratuitousness +\marginparwidth 0pt % Margin pars are not allowed. +\marginparsep 11pt % Horizontal space between outer margin and + % marginal note + + % Top of page: +\topmargin 4.5pc % Nominal distance from top of page to top of + % box containing running head. +\advance\topmargin by -1in % Correct for LaTeX gratuitousness +\headheight 0pt % Height of box containing running head. +\headsep 0pt % Space between running head and text. + % Bottom of page: +\footskip 30pt % Distance from baseline of box containing foot + % to baseline of last line of text. +\@ifundefined{footheight}{\newdimen\footheight}{}% this is for LaTeX2e +\footheight 12pt % Height of box containing running foot. + +%% Must redefine the top margin so there's room for headers and +%% page numbers if you are using the preprint option. Footers +%% are OK as is. Olin. +\advance\topmargin by -37pt % Leave 37pt above text for headers +\headheight 12pt % Height of box containing running head. +\headsep 25pt % Space between running head and text. + +\textheight 666pt % 9 1/4 column height +\textwidth 42pc % Width of text line. + % For two-column mode: +\columnsep 2pc % Space between columns +\columnseprule 0pt % Width of rule between columns. +\hfuzz 1pt % Allow some variation in column width, otherwise it's + % too hard to typeset in narrow columns. + +\ifnum\ACM@basesize=9\relax +\footnotesep 5.6pt % Height of strut placed at the beginning of every + % footnote =3D height of normal \footnotesize strut, + % so no extra space between footnotes. +\fi +\ifnum\ACM@basesize=10\relax +\footnotesep 6.22pt % Height of strut placed at the beginning of every + % footnote =3D height of normal \footnotesize strut, + % so no extra space between footnotes. +\fi + +\skip\footins 8.1pt plus 4pt minus 2pt % Space between last line of text and + % top of first footnote. +\floatsep 11pt plus 2pt minus 2pt % Space between adjacent floats moved + % to top or bottom of text page. +\textfloatsep 18pt plus 2pt minus 4pt % Space between main text and floats + % at top or bottom of page. +\intextsep 11pt plus 2pt minus 2pt % Space between in-text figures and + % text. +\@ifundefined{@maxsep}{\newdimen\@maxsep}{}% this is for LaTeX2e +\@maxsep 18pt % The maximum of \floatsep, + % \textfloatsep and \intextsep (minus + % the stretch and shrink). +\dblfloatsep 11pt plus 2pt minus 2pt % Same as \floatsep for double-column + % figures in two-column mode. +\dbltextfloatsep 18pt plus 2pt minus 4pt% \textfloatsep for double-column + % floats. +\@ifundefined{@dblmaxsep}{\newdimen\@dblmaxsep}{}% this is for LaTeX2e +\@dblmaxsep 18pt % The maximum of \dblfloatsep and + % \dbltexfloatsep. +\@fptop 0pt plus 1fil % Stretch at top of float page/column. (Must be + % 0pt plus ...) +\@fpsep 8pt plus 2fil % Space between floats on float page/column. +\@fpbot 0pt plus 1fil % Stretch at bottom of float page/column. (Must be + % 0pt plus ... ) +\@dblfptop 0pt plus 1fil % Stretch at top of float page. (Must be 0pt plus ...) +\@dblfpsep 8pt plus 2fil % Space between floats on float page. +\@dblfpbot 0pt plus 1fil % Stretch at bottom of float page. (Must be + % 0pt plus ... ) +\marginparpush 5pt % Minimum vertical separation between two marginal + % notes. + +\parskip 0pt plus 1pt % Extra vertical space between + % paragraphs. +\ifnum\ACM@basesize=9\relax +\parindent 9pt % GM July 2000 / was 0pt - width of paragraph + % indentation. +\fi +\ifnum\ACM@basesize=10\relax +\parindent 10pt % GM July 2000 / was 0pt - width of paragraph + % indentation. +\fi +\partopsep 2pt plus 1pt minus 1pt% Extra vertical space, in addition to + % \parskip and \topsep, added when user + % leaves blank line before environment. + +\@lowpenalty 51 % Produced by \nopagebreak[1] or \nolinebreak[1] +\@medpenalty 151 % Produced by \nopagebreak[2] or \nolinebreak[2] +\@highpenalty 301 % Produced by \nopagebreak[3] or \nolinebreak[3] + +\@beginparpenalty -\@lowpenalty % Before a list or paragraph environment. +\@endparpenalty -\@lowpenalty % After a list or paragraph environment. +\@itempenalty -\@lowpenalty % Between list items. + + +\RequirePackage{ifpdf}% +\ifpdf +\pdfpagewidth=8.5in +\pdfpageheight=11in +\fi + + +\lineskip 2pt % \lineskip is 1pt for all font sizes. +\normallineskip 2pt +\def\baselinestretch{1} + + +\ifnum\ACM@basesize=9\relax +\abovedisplayskip 9pt plus2pt minus4.5pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus3pt% +\belowdisplayshortskip 5.4pt plus3pt minus3pt% +\let\@listi\@listI % Setting of \@listi added 9 Jun 87 + +\def\small{\@setsize\small{9pt}\viiipt\@viiipt +\abovedisplayskip 7.6pt plus 3pt minus 4pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus2pt% +\belowdisplayshortskip 3.6pt plus2pt minus 2pt +\def\@listi{\leftmargin\leftmargini %% Added 22 Dec 87 +\topsep 4pt plus 2pt minus 2pt\parsep 2pt plus 1pt minus 1pt +\itemsep \parsep}} + +\def\footnotesize{\@setsize\footnotesize{9pt}\ixpt\@ixpt +\abovedisplayskip 6.4pt plus 2pt minus 4pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus 1pt% +\belowdisplayshortskip 2.7pt plus 1pt minus 2pt +\def\@listi{\leftmargin\leftmargini %% Added 22 Dec 87 +\topsep 3pt plus 1pt minus 1pt\parsep 2pt plus 1pt minus 1pt +\itemsep \parsep}} +\fi + +\ifnum\ACM@basesize=10\relax +\abovedisplayskip 10pt plus2pt minus4.5pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus3pt% +\belowdisplayshortskip 6pt plus3pt minus3pt% +\let\@listi\@listI % Setting of \@listi added 9 Jun 87 + +\def\small{\@setsize\small{10pt}\ixpt\@ixpt +\abovedisplayskip 8.5pt plus 3pt minus 4pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus2pt% +\belowdisplayshortskip 4pt plus2pt minus 2pt +\def\@listi{\leftmargin\leftmargini %% Added 22 Dec 87 +\topsep 4.5pt plus 2pt minus 2pt\parsep 2pt plus 1pt minus 1pt +\itemsep \parsep}} + +\def\footnotesize{\@setsize\footnotesize{10pt}\xpt\@xpt +\abovedisplayskip 7.6pt plus 2pt minus 4pt% +\belowdisplayskip \abovedisplayskip +\abovedisplayshortskip \z@ plus 1pt% +\belowdisplayshortskip 3.0pt plus 1pt minus 2pt +\def\@listi{\leftmargin\leftmargini %% Added 22 Dec 87 +\topsep 3.2pt plus 1pt minus 1pt\parsep 2pt plus 1pt minus 1pt +\itemsep \parsep}} +\fi + + +\newcount\aucount +\newcount\originalaucount +\newdimen\auwidth +\auwidth=\textwidth +\newdimen\auskip +\newcount\auskipcount +\newdimen\auskip +\global\auskip=1pc +\newdimen\allauboxes +\allauboxes=\auwidth +\newtoks\addauthors +\newcount\addauflag +\global\addauflag=0 %Haven't shown additional authors yet + +\newtoks\subtitletext +\gdef\subtitle#1{\subtitletext={#1}} + +\gdef\additionalauthors#1{\addauthors={#1}} + +\gdef\numberofauthors#1{\global\aucount=#1 +\ifnum\aucount>3\global\originalaucount=\aucount \global\aucount=3\fi %g} % 3 OK - Gerry March 2007 +\global\auskipcount=\aucount\global\advance\auskipcount by 1 +\global\multiply\auskipcount by 2 +\global\multiply\auskip by \auskipcount +\global\advance\auwidth by -\auskip +\global\divide\auwidth by \aucount} + +% \and was modified to count the number of authors. GKMT 12 Aug 1999 +\def\alignauthor{% % \begin{tabular} +\end{tabular}% + \begin{tabular}[t]{p{\auwidth}}\centering}% + +% *** NOTE *** NOTE *** NOTE *** NOTE *** +% If you have 'font problems' then you may need +% to change these, e.g. 'arialb' instead of "arialbd". +% Gerry Murray 11/11/1999 +% *** OR ** comment out block A and activate block B or vice versa. +% ********************************************** +% +% -- Start of block A -- (Type 1 or Truetype fonts) +%\newfont{\secfnt}{timesbd at 12pt} % was timenrb originally - now is timesbd +%\newfont{\secit}{timesbi at 12pt} %13 Jan 00 gkmt +%\newfont{\subsecfnt}{timesi at 11pt} % was timenrri originally - now is timesi +%\newfont{\subsecit}{timesbi at 11pt} % 13 Jan 00 gkmt -- was times changed to timesbi gm 2/4/2000 +% % because "normal" is italic, "italic" is Roman +%\newfont{\ttlfnt}{arialbd at 18pt} % was arialb originally - now is arialbd +%\newfont{\ttlit}{arialbi at 18pt} % 13 Jan 00 gkmt +%\newfont{\subttlfnt}{arial at 14pt} % was arialr originally - now is arial +%\newfont{\subttlit}{ariali at 14pt} % 13 Jan 00 gkmt +%\newfont{\subttlbf}{arialbd at 14pt} % 13 Jan 00 gkmt +%\newfont{\aufnt}{arial at 12pt} % was arialr originally - now is arial +%\newfont{\auit}{ariali at 12pt} % 13 Jan 00 gkmt +%\newfont{\affaddr}{arial at 10pt} % was arialr originally - now is arial +%\newfont{\affaddrit}{ariali at 10pt} %13 Jan 00 gkmt +%\newfont{\eaddfnt}{arial at 12pt} % was arialr originally - now is arial +%\newfont{\ixpt}{times at 9pt} % was timenrr originally - now is times +%\newfont{\confname}{timesi at 8pt} % was timenrri - now is timesi +%\newfont{\crnotice}{times at 8pt} % was timenrr originally - now is times +%\newfont{\ninept}{times at 9pt} % was timenrr originally - now is times + +% ********************************************* +% -- End of block A -- +% +% +% -- Start of block B -- UPDATED FONT NAMES +% ********************************************* +% Gerry Murray 11/30/2006 +% ********************************************* +\ifnum\ACM@basesize=9\relax +\newfont{\secfnt}{ptmb8t at 12pt} +\newfont{\secit}{ptmbi8t at 12pt} %13 Jan 00 gkmt +\newfont{\subsecfnt}{ptmri8t at 11pt} +\newfont{\subsecit}{ptmbi8t at 11pt} % +\newfont{\ttlfnt}{phvb8t at 18pt} +\newfont{\ttlit}{phvbo8t at 18pt} % GM 2/4/2000 +\newfont{\subttlfnt}{phvr8t at 14pt} +\newfont{\subttlit}{phvro8t at 14pt} % GM 2/4/2000 +\newfont{\subttlbf}{phvb8t at 14pt} % 13 Jan 00 gkmt +\newfont{\aufnt}{phvr8t at 12pt} +\newfont{\auit}{phvro8t at 12pt} % GM 2/4/2000 +\newfont{\affaddr}{phvr8t at 10pt} +\newfont{\affaddrit}{phvro8t at 10pt} % GM 2/4/2000 +\newfont{\eaddfnt}{phvr8t at 12pt} +\newfont{\ixpt}{ptmr8t at 9pt} +\newfont{\confname}{ptmri8t at 8pt} +\newfont{\crnotice}{ptmr8t at 8pt} +\newfont{\ninept}{ptmr8t at 9pt} +\fi +\ifnum\ACM@basesize=10\relax +\newfont{\secfnt}{ptmb8t at 13pt} +\newfont{\secit}{ptmbi8t at 13pt} %13 Jan 00 gkmt +\newfont{\subsecfnt}{ptmri8t at 12pt} +\newfont{\subsecit}{ptmbi8t at 12pt} % +\newfont{\ttlfnt}{phvb8t at 20pt} +\newfont{\ttlit}{phvbo8t at 20pt} % GM 2/4/2000 +\newfont{\subttlfnt}{phvr8t at 15pt} +\newfont{\subttlit}{phvro8t at 15pt} % GM 2/4/2000 +\newfont{\subttlbf}{phvb8t at 15pt} % 13 Jan 00 gkmt +\newfont{\aufnt}{phvr8t at 12pt} +\newfont{\auit}{phvro8t at 12pt} % GM 2/4/2000 +\newfont{\affaddr}{phvr8t at 11pt} +\newfont{\affaddrit}{phvro8t at 11pt} % GM 2/4/2000 +\newfont{\eaddfnt}{phvr8t at 12pt} +\newfont{\ixpt}{ptmr8t at 10pt} +\newfont{\confname}{ptmri8t at 9pt} +\newfont{\crnotice}{ptmr8t at 9pt} +\newfont{\ninept}{ptmr8t at 10pt} +\fi +% +++++++++++++++++++++++++++++++++++++++++++++ +% -- End of block B -- + +%\def\email#1{{{\eaddfnt{\vskip 4pt#1}}}} +% If we have an email, inside a "shared affiliation" then we need the following instead +\def\email#1{{{\eaddfnt{\par #1}}}} % revised - GM - 11/30/2006 + +\def\addauthorsection{\ifnum\originalaucount>6 % was 3 - Gerry March 2007 + \section{Additional Authors}\the\addauthors + \fi} + +\newcount\savesection +\newcount\sectioncntr +\global\sectioncntr=1 + +\setcounter{secnumdepth}{3} + +\def\appendix{\par +\section*{APPENDIX} +\setcounter{section}{0} + \setcounter{subsection}{0} + \def\thesection{\Alph{section}} } + +\leftmargini 22.5pt +\leftmarginii 19.8pt % > \labelsep + width of '(m)' +\leftmarginiii 16.8pt % > \labelsep + width of 'vii.' +\leftmarginiv 15.3pt % > \labelsep + width of 'M.' +\leftmarginv 9pt +\leftmarginvi 9pt + +\leftmargin\leftmargini +\labelsep 4.5pt +\labelwidth\leftmargini\advance\labelwidth-\labelsep + +\def\@listI{\leftmargin\leftmargini \parsep 3.6pt plus 2pt minus 1pt% +\topsep 7.2pt plus 2pt minus 4pt% +\itemsep 3.6pt plus 2pt minus 1pt} + +\let\@listi\@listI +\@listi + +\def\@listii{\leftmargin\leftmarginii + \labelwidth\leftmarginii\advance\labelwidth-\labelsep + \topsep 3.6pt plus 2pt minus 1pt + \parsep 1.8pt plus 0.9pt minus 0.9pt + \itemsep \parsep} + +\def\@listiii{\leftmargin\leftmarginiii + \labelwidth\leftmarginiii\advance\labelwidth-\labelsep + \topsep 1.8pt plus 0.9pt minus 0.9pt + \parsep \z@ \partopsep 1pt plus 0pt minus 1pt + \itemsep \topsep} + +\def\@listiv{\leftmargin\leftmarginiv + \labelwidth\leftmarginiv\advance\labelwidth-\labelsep} + +\def\@listv{\leftmargin\leftmarginv + \labelwidth\leftmarginv\advance\labelwidth-\labelsep} + +\def\@listvi{\leftmargin\leftmarginvi + \labelwidth\leftmarginvi\advance\labelwidth-\labelsep} + +\def\labelenumi{\theenumi.} +\def\theenumi{\arabic{enumi}} + +\def\labelenumii{(\theenumii)} +\def\theenumii{\alph{enumii}} +\def\p@enumii{\theenumi} + +\def\labelenumiii{\theenumiii.} +\def\theenumiii{\roman{enumiii}} +\def\p@enumiii{\theenumi(\theenumii)} + +\def\labelenumiv{\theenumiv.} +\def\theenumiv{\Alph{enumiv}} +\def\p@enumiv{\p@enumiii\theenumiii} + +\def\labelitemi{$\bullet$} +\def\labelitemii{\bf --} +\def\labelitemiii{$\ast$} +\def\labelitemiv{$\cdot$} + +\def\verse{\let\\=\@centercr + \list{}{\itemsep\z@ \itemindent -1.5em\listparindent \itemindent + \rightmargin\leftmargin\advance\leftmargin 1.5em}\item[]} +\let\endverse\endlist + +\def\quotation{\list{}{\listparindent 1.5em + \itemindent\listparindent + \rightmargin\leftmargin \parsep 0pt plus 1pt}\item[]} +\let\endquotation=\endlist + +\def\quote{\list{}{\rightmargin\leftmargin}\item[]} +\let\endquote=\endlist + +\def\descriptionlabel#1{\hspace\labelsep \bf #1} +\def\description{\list{}{\labelwidth\z@ \itemindent-\leftmargin + \let\makelabel\descriptionlabel}} + +\let\enddescription\endlist + +\def\theequation{\arabic{equation}} + + +\ifnum\ACM@basesize=9\relax +\arraycolsep 4.5pt % Half the space between columns in an array environment. +\tabcolsep 5.4pt % Half the space between columns in a tabular environment. +\arrayrulewidth .5pt % Width of rules in array and tabular environment. % (was .4) updated Gerry March 20 2007 +\doublerulesep 1.8pt % Space between adjacent rules in array or tabular env. + +\fi + +\ifnum\ACM@basesize=10\relax +\arraycolsep 5pt % Half the space between columns in an array environment. +\tabcolsep 6pt % Half the space between columns in a tabular environment. +\arrayrulewidth .5pt % Width of rules in array and tabular environment. % (was .4) updated Gerry March 20 2007 +\doublerulesep 1.8pt % Space between adjacent rules in array or tabular env. + +\fi + +\tabbingsep \labelsep % Space used by the \' command. (See LaTeX manual.) + +\skip\@mpfootins =\skip\footins + +\fboxsep =2.7pt % Space left between box and text by \fbox and \framebox. +\fboxrule =.5pt % Width of rules in box made by \fbox and \framebox. % (was .4) updated Gerry March 20 2007 + +\def\thepart{\Roman{part}} % Roman numeral part numbers. +\def\thesection {\arabic{section}} +\def\thesubsection {\thesection.\arabic{subsection}} +%\def\thesubsubsection {\thesubsection.\arabic{subsubsection}} % GM 7/30/2002 +%\def\theparagraph {\thesubsubsection.\arabic{paragraph}} % GM 7/30/2002 +\def\thesubparagraph {\theparagraph.\arabic{subparagraph}} + +\def\@pnumwidth{1.55em} +\def\@tocrmarg {2.55em} +\def\@dotsep{4.5} +\setcounter{tocdepth}{3} + +%\def\tableofcontents{\@latexerr{\tableofcontents: Tables of contents are not +% allowed in the `acmconf' document style.}\@eha} + +\def\tableofcontents{\ClassError{% + \string\tableofcontents\space is not allowed in the `acmconf' document % January 2008 + style}\@eha} + +\def\l@part#1#2{\addpenalty{\@secpenalty} + \addvspace{2.25em plus 1pt} % space above part line + \begingroup + \@tempdima 3em % width of box holding part number, used by + \parindent \z@ \rightskip \@pnumwidth %% \numberline + \parfillskip -\@pnumwidth + {\large \bf % set line in \large boldface + \leavevmode % TeX command to enter horizontal mode. + #1\hfil \hbox to\@pnumwidth{\hss #2}}\par + \nobreak % Never break after part entry + \endgroup} + +\def\l@section#1#2{\addpenalty{\@secpenalty} % good place for page break + \addvspace{1.0em plus 1pt} % space above toc entry + \@tempdima 1.5em % width of box holding section number + \begingroup + \parindent \z@ \rightskip \@pnumwidth + \parfillskip -\@pnumwidth + \bf % Boldface. + \leavevmode % TeX command to enter horizontal mode. + \advance\leftskip\@tempdima %% added 5 Feb 88 to conform to + \hskip -\leftskip %% 25 Jan 88 change to \numberline + #1\nobreak\hfil \nobreak\hbox to\@pnumwidth{\hss #2}\par + \endgroup} + + +\def\l@subsection{\@dottedtocline{2}{1.5em}{2.3em}} +\def\l@subsubsection{\@dottedtocline{3}{3.8em}{3.2em}} +\def\l@paragraph{\@dottedtocline{4}{7.0em}{4.1em}} +\def\l@subparagraph{\@dottedtocline{5}{10em}{5em}} + +%\def\listoffigures{\@latexerr{\listoffigures: Lists of figures are not +% allowed in the `acmconf' document style.}\@eha} + +\def\listoffigures{\ClassError{% + \string\listoffigures\space is not allowed in the `acmconf' document % January 2008 + style}\@eha} + +\def\l@figure{\@dottedtocline{1}{1.5em}{2.3em}} + +%\def\listoftables{\@latexerr{\listoftables: Lists of tables are not +% allowed in the `acmconf' document style.}\@eha} +%\let\l@table\l@figure + +\def\listoftables{\ClassError{% + \string\listoftables\space is not allowed in the `acmconf' document % January 2008 + style}\@eha} + \let\l@table\l@figure + +\def\footnoterule{\kern-3\p@ + \hrule width .5\columnwidth % (was .4) updated Gerry March 20 2007 + \kern 2.6\p@} % The \hrule has default height of .4pt % (was .4) updated Gerry March 20 2007 +% ------ +\long\def\@makefntext#1{\noindent +%\hbox to .5em{\hss$^{\@thefnmark}$}#1} % original +\hbox to .5em{\hss\textsuperscript{\@thefnmark}}#1} % C. Clifton / GM Oct. 2nd. 2002 +% ------- + +\long\def\@maketntext#1{\noindent +#1} + +\long\def\@maketitlenotetext#1#2{\noindent + \hbox to 1.8em{\hss$^{#1}$}#2} + +\setcounter{topnumber}{2} +\def\topfraction{.7} +\setcounter{bottomnumber}{1} +\def\bottomfraction{.3} +\setcounter{totalnumber}{3} +\def\textfraction{.2} +\def\floatpagefraction{.5} +\setcounter{dbltopnumber}{2} +\def\dbltopfraction{.7} +\def\dblfloatpagefraction{.5} + +% +\long\def\@makecaption#1#2{ + \vskip \baselineskip + \setbox\@tempboxa\hbox{\textbf{#1: #2}} + \ifdim \wd\@tempboxa >\hsize % IF longer than one line: + \textbf{#1: #2}\par % THEN set as ordinary paragraph. + \else % ELSE center. + \hbox to\hsize{\hfil\box\@tempboxa\hfil}\par + \fi} + +% + +\long\def\@makecaption#1#2{ + \vskip 10pt + \setbox\@tempboxa\hbox{\textbf{#1: #2}} + \ifdim \wd\@tempboxa >\hsize % IF longer than one line: + \textbf{#1: #2}\par % THEN set as ordinary paragraph. + \else % ELSE center. + \hbox to\hsize{\hfil\box\@tempboxa\hfil} + \fi} + +\@ifundefined{figure}{\newcounter {figure}} % this is for LaTeX2e + +\def\fps@figure{tbp} +\def\ftype@figure{1} +\def\ext@figure{lof} +\def\fnum@figure{Figure \thefigure} +\def\figure{\@float{figure}} +%\let\endfigure\end@float +\def\endfigure{\end@float} % Gerry January 2008 +\@namedef{figure*}{\@dblfloat{figure}} +\@namedef{endfigure*}{\end@dblfloat} + +\@ifundefined{table}{\newcounter {table}} % this is for LaTeX2e + +\def\fps@table{tbp} +\def\ftype@table{2} +\def\ext@table{lot} +\def\fnum@table{Table \thetable} +\def\table{\@float{table}} +%\let\endtable\end@float +\def\endtable{\end@float} % Gerry January 2008 +\@namedef{table*}{\@dblfloat{table}} +\@namedef{endtable*}{\end@dblfloat} + +\newtoks\titleboxnotes +\newcount\titleboxnoteflag + +\def\maketitle{\par + \begingroup + \def\thefootnote{\fnsymbol{footnote}} + \def\@makefnmark{\hbox + to 0pt{$^{\@thefnmark}$\hss}} + \twocolumn[\@maketitle] +\@thanks + \endgroup + \setcounter{footnote}{0} + \let\maketitle\relax + \let\@maketitle\relax + \gdef\@thanks{}\gdef\@author{}\gdef\@title{}\gdef\@subtitle{}\let\thanks\relax + \@copyrightspace} + +%% CHANGES ON NEXT LINES +\newif\if@ll % to record which version of LaTeX is in use + +\expandafter\ifx\csname LaTeXe\endcsname\relax % LaTeX2.09 is used +\else% LaTeX2e is used, so set ll to true +\global\@lltrue +\fi + +\if@ll + \NeedsTeXFormat{LaTeX2e} + \ProvidesClass{sig-alternate} [2013/05/12 v2.7 based on acmproc.cls V1.3 ] + \RequirePackage{latexsym}% QUERY: are these two really needed? + \let\dooptions\ProcessOptions +\else + \let\dooptions\@options +\fi +%% END CHANGES + +\def\@height{height} +\def\@width{width} +\def\@minus{minus} +\def\@plus{plus} +\def\hb@xt@{\hbox to} +\newif\if@faircopy +\@faircopyfalse +\def\ds@faircopy{\@faircopytrue} + +\def\ds@preprint{\@faircopyfalse} + +\@twosidetrue +\@mparswitchtrue +\def\ds@draft{\overfullrule 5\p@} +%% CHANGE ON NEXT LINE +\dooptions + +\lineskip \p@ +\normallineskip \p@ +\def\baselinestretch{1} +\def\@ptsize{0} %needed for amssymbols.sty + +%% CHANGES ON NEXT LINES +\if@ll% allow use of old-style font change commands in LaTeX2e +\@maxdepth\maxdepth +% +\DeclareOldFontCommand{\rm}{\ninept\rmfamily}{\mathrm} +\DeclareOldFontCommand{\sf}{\normalfont\sffamily}{\mathsf} +\DeclareOldFontCommand{\tt}{\normalfont\ttfamily}{\mathtt} +\DeclareOldFontCommand{\bf}{\normalfont\bfseries}{\mathbf} +\DeclareOldFontCommand{\it}{\normalfont\itshape}{\mathit} +\DeclareOldFontCommand{\sl}{\normalfont\slshape}{\@nomath\sl} +\DeclareOldFontCommand{\sc}{\normalfont\scshape}{\@nomath\sc} +\DeclareRobustCommand*{\cal}{\@fontswitch{\relax}{\mathcal}} +\DeclareRobustCommand*{\mit}{\@fontswitch{\relax}{\mathnormal}} +\fi +% +\if@ll + \renewcommand{\rmdefault}{cmr} % was 'ttm' +% Note! I have also found 'mvr' to work ESPECIALLY well. +% Gerry - October 1999 +% You may need to change your LV1times.fd file so that sc is +% mapped to cmcsc - -for smallcaps -- that is if you decide +% to change {cmr} to {times} above. (Not recommended) +\ifnum\ACM@basesize=9\relax + \renewcommand{\@ptsize}{} + \renewcommand{\normalsize}{% + \@setfontsize\normalsize\@ixpt{10.5\p@}%\ninept% + \abovedisplayskip 6\p@ \@plus2\p@ \@minus\p@ + \belowdisplayskip \abovedisplayskip + \abovedisplayshortskip 6\p@ \@minus 3\p@ + \belowdisplayshortskip 6\p@ \@minus 3\p@ + \let\@listi\@listI} +\fi +\ifnum\ACM@basesize=10\relax + \renewcommand{\@ptsize}{} + \renewcommand{\normalsize}{% + \@setfontsize\normalsize\@xpt{11.5\p@}%\ninept% + \abovedisplayskip 6.5\p@ \@plus2\p@ \@minus\p@ + \belowdisplayskip \abovedisplayskip + \abovedisplayshortskip 6.5\p@ \@minus 3\p@ + \belowdisplayshortskip 6.5\p@ \@minus 3\p@ + \let\@listi\@listI} +\fi +\else + \def\@normalsize{%changed next to 9 from 10 + \@setsize\normalsize{9\p@}\ixpt\@ixpt + \abovedisplayskip 6\p@ \@plus2\p@ \@minus\p@ + \belowdisplayskip \abovedisplayskip + \abovedisplayshortskip 6\p@ \@minus 3\p@ + \belowdisplayshortskip 6\p@ \@minus 3\p@ + \let\@listi\@listI + }% +\fi +\if@ll +\ifnum\ACM@basesize=9\relax + \newcommand\scriptsize{\@setfontsize\scriptsize\@viipt{8\p@}} + \newcommand\tiny{\@setfontsize\tiny\@vpt{6\p@}} + \newcommand\large{\@setfontsize\large\@xiipt{14\p@}} + \newcommand\Large{\@setfontsize\Large\@xivpt{18\p@}} + \newcommand\LARGE{\@setfontsize\LARGE\@xviipt{20\p@}} + \newcommand\huge{\@setfontsize\huge\@xxpt{25\p@}} + \newcommand\Huge{\@setfontsize\Huge\@xxvpt{30\p@}} +\fi +\ifnum\ACM@basesize=10\relax + \newcommand\scriptsize{\@setfontsize\scriptsize\@viiipt{9\p@}} + \newcommand\tiny{\@setfontsize\tiny\@vipt{7\p@}} + \newcommand\large{\@setfontsize\large\@xiiipt{15\p@}} + \newcommand\Large{\@setfontsize\Large\@xvpt{20\p@}} + \newcommand\LARGE{\@setfontsize\LARGE\@xixpt{22\p@}} + \newcommand\huge{\@setfontsize\huge\@xixpt{30\p@}} + \newcommand\Huge{\@setfontsize\Huge30pt{36\p@}} +\fi +\else + \def\scriptsize{\@setsize\scriptsize{8\p@}\viipt\@viipt} + \def\tiny{\@setsize\tiny{6\p@}\vpt\@vpt} + \def\large{\@setsize\large{14\p@}\xiipt\@xiipt} + \def\Large{\@setsize\Large{18\p@}\xivpt\@xivpt} + \def\LARGE{\@setsize\LARGE{20\p@}\xviipt\@xviipt} + \def\huge{\@setsize\huge{25\p@}\xxpt\@xxpt} + \def\Huge{\@setsize\Huge{30\p@}\xxvpt\@xxvpt} +\fi +\normalsize + +% make aubox hsize/number of authors up to 3, less gutter +% then showbox gutter showbox gutter showbox -- GKMT Aug 99 +\newbox\@acmtitlebox +\ifnum\ACM@basesize=9\relax +\def\@maketitle{\newpage + \null + \setbox\@acmtitlebox\vbox{% +\baselineskip 20pt +\vskip 2em % Vertical space above title. + \begin{center} + {\ttlfnt \@title\par} % Title set in 18pt Helvetica (Arial) bold size. + \vskip 1.5em % Vertical space after title. +%This should be the subtitle. +{\subttlfnt \the\subtitletext\par}\vskip 1.25em%\fi + {\baselineskip 16pt\aufnt % each author set in \12 pt Arial, in a + \lineskip .5em % tabular environment + \begin{tabular}[t]{c}\@author + \end{tabular}\par} + \vskip 1.5em % Vertical space after author. + \end{center}} + \dimen0=\ht\@acmtitlebox + \advance\dimen0 by -12.75pc\relax % Increased space for title box -- KBT + \unvbox\@acmtitlebox + \ifdim\dimen0<0.0pt\relax\vskip-\dimen0\fi} +\fi +\ifnum\ACM@basesize=10\relax +\def\@maketitle{\newpage + \null + \setbox\@acmtitlebox\vbox{% +\baselineskip 22pt +\vskip 2.2em % Vertical space above title. + \begin{center} + {\ttlfnt \@title\par} % Title set in 18pt Helvetica (Arial) bold size. + \vskip 2em % Vertical space after title. +%This should be the subtitle. +{\subttlfnt \the\subtitletext\par}\vskip 1.25em%\fi + {\baselineskip 18pt\aufnt % each author set in \12 pt Arial, in a + \lineskip .5em % tabular environment + \begin{tabular}[t]{c}\@author + \end{tabular}\par} + \vskip 2em % Vertical space after author. + \end{center}} + \dimen0=\ht\@acmtitlebox + \advance\dimen0 by -12.75pc\relax % Increased space for title box -- KBT + \unvbox\@acmtitlebox + \ifdim\dimen0<0.0pt\relax\vskip-\dimen0\fi} +\fi + +\newcount\titlenotecount +\global\titlenotecount=0 +\newtoks\tntoks +\newtoks\tntokstwo +\newtoks\tntoksthree +\newtoks\tntoksfour +\newtoks\tntoksfive + +\def\abstract{ +\ifnum\titlenotecount>0 % was =1 + \insert\footins{% + \reset@font\footnotesize + \interlinepenalty\interfootnotelinepenalty + \splittopskip\footnotesep + \splitmaxdepth \dp\strutbox \floatingpenalty \@MM + \hsize\columnwidth \@parboxrestore + \protected@edef\@currentlabel{% + }% + \color@begingroup +\ifnum\titlenotecount=1 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\@finalstrut\strutbox}% +\fi +\ifnum\titlenotecount=2 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\dagger$}\rule\z@\footnotesep\ignorespaces\the\tntokstwo\@finalstrut\strutbox}% +\fi +\ifnum\titlenotecount=3 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\dagger$}\rule\z@\footnotesep\ignorespaces\the\tntokstwo\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\ddagger$}\rule\z@\footnotesep\ignorespaces\the\tntoksthree\@finalstrut\strutbox}% +\fi +\ifnum\titlenotecount=4 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\dagger$}\rule\z@\footnotesep\ignorespaces\the\tntokstwo\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\ddagger$}\rule\z@\footnotesep\ignorespaces\the\tntoksthree\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\S$}\rule\z@\footnotesep\ignorespaces\the\tntoksfour\@finalstrut\strutbox}% +\fi +\ifnum\titlenotecount=5 + \@maketntext{% + \raisebox{4pt}{$\ast$}\rule\z@\footnotesep\ignorespaces\the\tntoks\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\dagger$}\rule\z@\footnotesep\ignorespaces\the\tntokstwo\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\ddagger$}\rule\z@\footnotesep\ignorespaces\the\tntoksthree\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\S$}\rule\z@\footnotesep\ignorespaces\the\tntoksfour\par\@finalstrut\strutbox}% +\@maketntext{% + \raisebox{4pt}{$\P$}\rule\z@\footnotesep\ignorespaces\the\tntoksfive\@finalstrut\strutbox}% +\fi + \color@endgroup} %g} +\fi +\setcounter{footnote}{0} +\section*{ABSTRACT}\normalsize%\ninept +} + +\def\endabstract{\if@twocolumn\else\endquotation\fi} + +\def\keywords{\if@twocolumn +\section*{Keywords} +\else \small +\quotation +\fi} + +\def\terms#1{% +%\if@twocolumn +%\section*{General Terms} +%\else \small +%\quotation +%\fi +} + +% -- Classification needs to be a bit smart due to optionals - Gerry/Georgia November 2nd. 1999 +\newcount\catcount +\global\catcount=1 + +\def\category#1#2#3{% +\ifnum\catcount=1 +\section*{Categories and Subject Descriptors} +\advance\catcount by 1\else{\unskip; }\fi + \@ifnextchar [{\@category{#1}{#2}{#3}}{\@category{#1}{#2}{#3}[]}% +} + + +\def\@category#1#2#3[#4]{% + \begingroup + \let\and\relax + #1 [\textbf{#2}]% + \if!#4!% + \if!#3!\else : #3\fi + \else + :\space + \if!#3!\else #3\kern\z@---\hskip\z@\fi + \textit{#4}% + \fi + \endgroup +} +% + + + + + + +%%% This section (written by KBT) handles the 1" box in the lower left +%%% corner of the left column of the first page by creating a picture, +%%% and inserting the predefined string at the bottom (with a negative +%%% displacement to offset the space allocated for a non-existent +%%% caption). +%%% +\newtoks\copyrightnotice +\def\ftype@copyrightbox{8} +\def\@copyrightspace{ +\@float{copyrightbox}[b] +\begin{center} +\setlength{\unitlength}{1pc} +\ifnum\ACM@basesize=9 +\begin{picture}(20,6) %Space for copyright notice +\put(0,-0.95){\crnotice{\@toappear}} +\end{picture} +\fi +\ifnum\ACM@basesize=10 +\begin{picture}(20,7) %Space for copyright notice +\put(0,-0.95){\crnotice{\@toappear}} +\end{picture} +\fi +\end{center} +\end@float} + +\def\@toappear{} % Default setting blank - commands below change this. +\long\def\toappear#1{\def\@toappear{\parbox[b]{20pc}{\baselineskip 9pt#1}}} +\def\toappearbox#1{\def\@toappear{\raisebox{5pt}{\framebox[20pc]{\parbox[b]{19pc}{#1}}}}} + +\newtoks\conf +\newtoks\confinfo +\def\conferenceinfo#1#2{\global\conf={#1}\global\confinfo{#2}} + + +%\def\marginpar{\@latexerr{The \marginpar command is not allowed in the +% `acmconf' document style.}\@eha} + +\def\marginpar{\ClassError{% + \string\marginpar\space is not allowed in the `acmconf' document % January 2008 + style}\@eha} + +\mark{{}{}} % Initializes TeX's marks + +\def\today{\ifcase\month\or + January\or February\or March\or April\or May\or June\or + July\or August\or September\or October\or November\or December\fi + \space\number\day, \number\year} + +\def\@begintheorem#1#2{% + \parskip 0pt % GM July 2000 (for tighter spacing) + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {{\sc #1}\hskip 5\p@\relax#2.}% + ] + \it +} +\def\@opargbegintheorem#1#2#3{% + \parskip 0pt % GM July 2000 (for tighter spacing) + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {\sc #1\ #2\ % This mod by Gerry to enumerate corollaries + \setbox\@tempboxa\hbox{(#3)} % and bracket the 'corollary title' + \ifdim \wd\@tempboxa>\z@ % and retain the correct numbering of e.g. theorems + \hskip 5\p@\relax % if they occur 'around' said corollaries. + \box\@tempboxa % Gerry - Nov. 1999. + \fi.}% + ] + \it +} +\newif\if@qeded +\global\@qededfalse + +% -- original +%\def\proof{% +% \vspace{-\parskip} % GM July 2000 (for tighter spacing) +% \global\@qededfalse +% \@ifnextchar[{\@xproof}{\@proof}% +%} +% -- end of original + +% (JSS) Fix for vertical spacing bug - Gerry Murray July 30th. 2002 +\def\proof{% +\vspace{-\lastskip}\vspace{-\parsep}\penalty-51% +\global\@qededfalse +\@ifnextchar[{\@xproof}{\@proof}% +} + +\def\endproof{% + \if@qeded\else\qed\fi + \endtrivlist +} +\def\@proof{% + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {\sc Proof.}% + ] + \ignorespaces +} +\def\@xproof[#1]{% + \trivlist + \item[\hskip 10\p@\hskip \labelsep{\sc Proof #1.}]% + \ignorespaces +} +\def\qed{% + \unskip + \kern 10\p@ + \begingroup + \unitlength\p@ + \linethickness{.4\p@}% + \framebox(6,6){}% + \endgroup + \global\@qededtrue +} + +\def\newdef#1#2{% + \expandafter\@ifdefinable\csname #1\endcsname + {\@definecounter{#1}% + \expandafter\xdef\csname the#1\endcsname{\@thmcounter{#1}}% + \global\@namedef{#1}{\@defthm{#1}{#2}}% + \global\@namedef{end#1}{\@endtheorem}% + }% +} +\def\@defthm#1#2{% + \refstepcounter{#1}% + \@ifnextchar[{\@ydefthm{#1}{#2}}{\@xdefthm{#1}{#2}}% +} +\def\@xdefthm#1#2{% + \@begindef{#2}{\csname the#1\endcsname}% + \ignorespaces +} +\def\@ydefthm#1#2[#3]{% + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {\it #2% +% \savebox\@tempboxa{#3}% + \saveb@x\@tempboxa{#3}% % January 2008 + \ifdim \wd\@tempboxa>\z@ + \ \box\@tempboxa + \fi.% + }]% + \ignorespaces +} +\def\@begindef#1#2{% + \trivlist + \item[% + \hskip 10\p@ + \hskip \labelsep + {\it #1\ \rm #2.}% + ]% +} +\def\theequation{\arabic{equation}} + +\newcounter{part} +\newcounter{section} +\newcounter{subsection}[section] +\newcounter{subsubsection}[subsection] +\newcounter{paragraph}[subsubsection] +\def\thepart{\Roman{part}} +\def\thesection{\arabic{section}} +\def\thesubsection{\thesection.\arabic{subsection}} +\def\thesubsubsection{\thesubsection.\arabic{subsubsection}} %removed \subsecfnt 29 July 2002 gkmt +\def\theparagraph{\thesubsubsection.\arabic{paragraph}} %removed \subsecfnt 29 July 2002 gkmt +\newif\if@uchead +\@ucheadfalse + +%% CHANGES: NEW NOTE +%% NOTE: OK to use old-style font commands below, since they were +%% suitably redefined for LaTeX2e +%% END CHANGES +\setcounter{secnumdepth}{3} +\def\part{% + \@startsection{part}{9}{\z@}{-10\p@ \@plus -4\p@ \@minus -2\p@} + {4\p@}{\normalsize\@ucheadtrue}% +} +\def\section{% + \@startsection{section}{1}{\z@}{-10\p@ \@plus -4\p@ \@minus -2\p@}% GM + {4\p@}{\baselineskip 14pt\secfnt\@ucheadtrue}% +} + +\def\subsection{% + \@startsection{subsection}{2}{\z@}{-8\p@ \@plus -2\p@ \@minus -\p@} + {4\p@}{\secfnt}% +} +\def\subsubsection{% + \@startsection{subsubsection}{3}{\z@}{-8\p@ \@plus -2\p@ \@minus -\p@}% + {4\p@}{\subsecfnt}% +} +%\def\paragraph{% +% \vskip 12pt\@startsection{paragraph}{3}{\z@}{6\p@ \@plus \p@}% original +% {-5\p@}{\subsecfnt}% +%} +% If one wants sections, subsections and subsubsections numbered, +% but not paragraphs, one usually sets secnumepth to 3. +% For that, the "depth" of paragraphs must be given correctly +% in the definition (``4'' instead of ``3'' as second argument +% of @startsection): +\def\paragraph{% + \vskip 12pt\@startsection{paragraph}{4}{\z@}{6\p@ \@plus \p@}% % GM and Wolfgang May - 11/30/06 + {-5\p@}{\subsecfnt}% +} +\let\@period=. +\def\@startsection#1#2#3#4#5#6{% + \if@noskipsec %gkmt, 11 aug 99 + \global\let\@period\@empty + \leavevmode + \global\let\@period.% + \fi + \par % + \@tempskipa #4\relax + \@afterindenttrue + \ifdim \@tempskipa <\z@ + \@tempskipa -\@tempskipa + \@afterindentfalse + \fi + \if@nobreak + \everypar{}% + \else + \addpenalty\@secpenalty + \addvspace\@tempskipa + \fi +\parskip=0pt % GM July 2000 (non numbered) section heads + \@ifstar + {\@ssect{#3}{#4}{#5}{#6}} + {\@dblarg{\@sect{#1}{#2}{#3}{#4}{#5}{#6}}}% +} +\def\@sect#1#2#3#4#5#6[#7]#8{% + \ifnum #2>\c@secnumdepth + \let\@svsec\@empty + \else + \refstepcounter{#1}% + \edef\@svsec{% + \begingroup + %\ifnum#2>2 \noexpand\rm \fi % changed to next 29 July 2002 gkmt + \ifnum#2>2 \noexpand#6 \fi + \csname the#1\endcsname + \endgroup + \ifnum #2=1\relax .\fi + \hskip 1em + }% + \fi + \@tempskipa #5\relax + \ifdim \@tempskipa>\z@ + \begingroup + #6\relax + \@hangfrom{\hskip #3\relax\@svsec}% + \begingroup + \interlinepenalty \@M + \if@uchead + \uppercase{#8}% + \else + #8% + \fi + \par + \endgroup + \endgroup + \csname #1mark\endcsname{#7}% + \vskip -12pt %gkmt, 11 aug 99 and GM July 2000 (was -14) - numbered section head spacing +\addcontentsline{toc}{#1}{% + \ifnum #2>\c@secnumdepth \else + \protect\numberline{\csname the#1\endcsname}% + \fi + #7% + }% + \else + \def\@svsechd{% + #6% + \hskip #3\relax + \@svsec + \if@uchead + \uppercase{#8}% + \else + #8% + \fi + \csname #1mark\endcsname{#7}% + \addcontentsline{toc}{#1}{% + \ifnum #2>\c@secnumdepth \else + \protect\numberline{\csname the#1\endcsname}% + \fi + #7% + }% + }% + \fi + \@xsect{#5}\hskip 1pt + \par +} +\def\@xsect#1{% + \@tempskipa #1\relax + \ifdim \@tempskipa>\z@ + \par + \nobreak + \vskip \@tempskipa + \@afterheading + \else + \global\@nobreakfalse + \global\@noskipsectrue + \everypar{% + \if@noskipsec + \global\@noskipsecfalse + \clubpenalty\@M + \hskip -\parindent + \begingroup + \@svsechd + \@period + \endgroup + \unskip + \@tempskipa #1\relax + \hskip -\@tempskipa + \else + \clubpenalty \@clubpenalty + \everypar{}% + \fi + }% + \fi + \ignorespaces +} +\def\@trivlist{% + \@topsepadd\topsep + \if@noskipsec + \global\let\@period\@empty + \leavevmode + \global\let\@period.% + \fi + \ifvmode + \advance\@topsepadd\partopsep + \else + \unskip + \par + \fi + \if@inlabel + \@noparitemtrue + \@noparlisttrue + \else + \@noparlistfalse + \@topsep\@topsepadd + \fi + \advance\@topsep \parskip + \leftskip\z@skip + \rightskip\@rightskip + \parfillskip\@flushglue + \@setpar{\if@newlist\else{\@@par}\fi} + \global\@newlisttrue + \@outerparskip\parskip +} + +%%% Actually, 'abbrev' works just fine as the default +%%% Bibliography style. + +\typeout{Using 'Abbrev' bibliography style} +\newcommand\bibyear[2]{% + \unskip\quad\ignorespaces#1\unskip + \if#2..\quad \else \quad#2 \fi +} +\newcommand{\bibemph}[1]{{\em#1}} +\newcommand{\bibemphic}[1]{{\em#1\/}} +\newcommand{\bibsc}[1]{{\sc#1}} +\def\@normalcite{% + \def\@cite##1##2{[##1\if@tempswa , ##2\fi]}% +} +\def\@citeNB{% + \def\@cite##1##2{##1\if@tempswa , ##2\fi}% +} +\def\@citeRB{% + \def\@cite##1##2{##1\if@tempswa , ##2\fi]}% +} +\def\start@cite#1#2{% + \edef\citeauthoryear##1##2##3{% + ###1% + \ifnum#2=\z@ \else\ ###2\fi + }% + \ifnum#1=\thr@@ + \let\@@cite\@citeyear + \else + \let\@@cite\@citenormal + \fi + \@ifstar{\@citeNB\@@cite}{\@normalcite\@@cite}% +} +%\def\cite{\start@cite23} +\DeclareRobustCommand\cite{\start@cite23} % January 2008 +\def\citeNP{\cite*} % No Parentheses e.g. 5 +%\def\citeA{\start@cite10} +\DeclareRobustCommand\citeA{\start@cite10} % January 2008 +\def\citeANP{\citeA*} +%\def\shortcite{\start@cite23} +\DeclareRobustCommand\shortcite{\start@cite23} % January 2008 +\def\shortciteNP{\shortcite*} +%\def\shortciteA{\start@cite20} +\DeclareRobustCommand\shortciteA{\start@cite20} % January 2008 +\def\shortciteANP{\shortciteA*} +%\def\citeyear{\start@cite30} +\DeclareRobustCommand\citeyear{\start@cite30} % January 2008 +\def\citeyearNP{\citeyear*} +%\def\citeN{% +\DeclareRobustCommand\citeN{% % January 2008 + \@citeRB + \def\citeauthoryear##1##2##3{##1\ [##3% + \def\reserved@a{##1}% + \def\citeauthoryear####1####2####3{% + \def\reserved@b{####1}% + \ifx\reserved@a\reserved@b + ####3% + \else + \errmessage{Package acmart Error: author mismatch + in \string\citeN^^J^^J% + See the acmart package documentation for explanation}% + \fi + }% + }% + \@ifstar\@citeyear\@citeyear +} +%\def\shortciteN{% +\DeclareRobustCommand\shortciteN{% % January 2008 + \@citeRB + \def\citeauthoryear##1##2##3{##2\ [##3% + \def\reserved@a{##2}% + \def\citeauthoryear####1####2####3{% + \def\reserved@b{####2}% + \ifx\reserved@a\reserved@b + ####3% + \else + \errmessage{Package acmart Error: author mismatch + in \string\shortciteN^^J^^J% + See the acmart package documentation for explanation}% + \fi + }% + }% + \@ifstar\@citeyear\@citeyear % GM July 2000 +} + +\def\@citenormal{% + \@ifnextchar [{\@tempswatrue\@citex;}% +% original {\@tempswafalse\@citex,[]}% was ; Gerry 2/24/00 +{\@tempswafalse\@citex[]}% % GERRY FIX FOR BABEL 3/20/2009 +} + +\def\@citeyear{% + \@ifnextchar [{\@tempswatrue\@citex,}% +% original {\@tempswafalse\@citex,[]}% +{\@tempswafalse\@citex[]}% % GERRY FIX FOR BABEL 3/20/2009 +} + +\def\@citex#1[#2]#3{% + \let\@citea\@empty + \@cite{% + \@for\@citeb:=#3\do{% + \@citea +% original \def\@citea{#1 }% + \def\@citea{#1, }% % GERRY FIX FOR BABEL 3/20/2009 -- SO THAT YOU GET [1, 2] IN THE BODY TEXT + \edef\@citeb{\expandafter\@iden\@citeb}% + \if@filesw + \immediate\write\@auxout{\string\citation{\@citeb}}% + \fi + \@ifundefined{b@\@citeb}{% + {\bf ?}% + \@warning{% + Citation `\@citeb' on page \thepage\space undefined% + }% + }% + {\csname b@\@citeb\endcsname}% + }% + }{#2}% +} +%\let\@biblabel\@gobble % Dec. 2008 - Gerry +% ---- +\def\@biblabelnum#1{[#1]} % Gerry's solution #1 - for Natbib -- April 2009 +\let\@biblabel=\@biblabelnum % Gerry's solution #1 - for Natbib -- April 2009 +\def\newblock{\relax} % Gerry Dec. 2008 +% --- +\newdimen\bibindent +\setcounter{enumi}{1} +\bibindent=0em +\def\thebibliography#1{% +\ifnum\addauflag=0\addauthorsection\global\addauflag=1\fi + \section[References]{% <=== OPTIONAL ARGUMENT ADDED HERE + {References} % was uppercased but this affects pdf bookmarks (SP/GM October 2004) + {\vskip -9pt plus 1pt} % GM Nov. 2006 / GM July 2000 (for somewhat tighter spacing) + \@mkboth{{\refname}}{{\refname}}% + }% + \list{[\arabic{enumi}]}{% + \settowidth\labelwidth{[#1]}% + \leftmargin\labelwidth + \advance\leftmargin\labelsep + \advance\leftmargin\bibindent + \parsep=0pt\itemsep=1pt % GM July 2000 + \itemindent -\bibindent + \listparindent \itemindent + \usecounter{enumi} + }% + \let\newblock\@empty + \raggedright % GM July 2000 + \sloppy + \sfcode`\.=1000\relax +} + + +\gdef\balancecolumns +{\vfill\eject +\global\@colht=\textheight +\global\ht\@cclv=\textheight +} + +\newcount\colcntr +\global\colcntr=0 +%\newbox\savebox +\newbox\saveb@x % January 2008 + +\gdef \@makecol {% +\global\advance\colcntr by 1 +\ifnum\colcntr>2 \global\colcntr=1\fi + \ifvoid\footins + \setbox\@outputbox \box\@cclv + \else + \setbox\@outputbox \vbox{% +\boxmaxdepth \@maxdepth + \@tempdima\dp\@cclv + \unvbox \@cclv + \vskip-\@tempdima + \vskip \skip\footins + \color@begingroup + \normalcolor + \footnoterule + \unvbox \footins + \color@endgroup + }% + \fi + \xdef\@freelist{\@freelist\@midlist}% + \global \let \@midlist \@empty + \@combinefloats + \ifvbox\@kludgeins + \@makespecialcolbox + \else + \setbox\@outputbox \vbox to\@colht {% +\@texttop + \dimen@ \dp\@outputbox + \unvbox \@outputbox + \vskip -\dimen@ + \@textbottom + }% + \fi + \global \maxdepth \@maxdepth +} +\def\titlenote{\@ifnextchar[\@xtitlenote{\stepcounter\@mpfn +\global\advance\titlenotecount by 1 +\ifnum\titlenotecount=1 + \raisebox{9pt}{$\ast$} +\fi +\ifnum\titlenotecount=2 + \raisebox{9pt}{$\dagger$} +\fi +\ifnum\titlenotecount=3 + \raisebox{9pt}{$\ddagger$} +\fi +\ifnum\titlenotecount=4 +\raisebox{9pt}{$\S$} +\fi +\ifnum\titlenotecount=5 +\raisebox{9pt}{$\P$} +\fi + \@titlenotetext +}} + +\long\def\@titlenotetext#1{\insert\footins{% +\ifnum\titlenotecount=1\global\tntoks={#1}\fi +\ifnum\titlenotecount=2\global\tntokstwo={#1}\fi +\ifnum\titlenotecount=3\global\tntoksthree={#1}\fi +\ifnum\titlenotecount=4\global\tntoksfour={#1}\fi +\ifnum\titlenotecount=5\global\tntoksfive={#1}\fi + \reset@font\footnotesize + \interlinepenalty\interfootnotelinepenalty + \splittopskip\footnotesep + \splitmaxdepth \dp\strutbox \floatingpenalty \@MM + \hsize\columnwidth \@parboxrestore + \protected@edef\@currentlabel{% + }% + \color@begingroup + \color@endgroup}} + +%%%%%%%%%%%%%%%%%%%%%%%%% +\ps@plain +\baselineskip=11pt +\let\thepage\relax % For NO page numbers - GM Nov. 30th. 1999 and July 2000 +\def\setpagenumber#1{\global\setcounter{page}{#1}} +%\pagenumbering{arabic} % Arabic page numbers GM July 2000 +\twocolumn % Double column. +\flushbottom % Even bottom -- alas, does not balance columns at end of document +\pagestyle{plain} + +% Need Copyright Year and Copyright Data to be user definable (in .tex file). +% Gerry Nov. 30th. 1999 +\newtoks\copyrtyr +\newtoks\acmcopyr +\newtoks\boilerplate +\global\acmcopyr={X-XXXXX-XX-X/XX/XX} % Default - 5/11/2001 *** Gerry +\global\copyrtyr={\the\year} % Default - 3/3/2003 *** Gerry +\def\acmPrice#1{\gdef\@acmPrice{#1}} +\acmPrice{} %article price % Changed to 15 - June 2012 - Gerry + + +\def\CopyrightYear#1{\global\copyrtyr{#1}} +\def\crdata#1{\global\acmcopyr{#1}} +\def\permission#1{\global\boilerplate{#1}} + +% ISBN +% +\def\isbn#1{\global\acmcopyr={#1}} +\isbn{978-1-4503-2138-9} + +\RequirePackage{url} +\urlstyle{rm} +\def\doi#1{\def\@doi{#1}} +\doi{10.1145/1235} +\def\printdoi#1{\url{#1}} + + + +% Copyright +\RequirePackage{acmcopyright} +\setcopyright{none} + +% +\global\boilerplate={\@copyrightpermission} + + + +\newtoks\copyrightetc +\ifnum\ACM@basesize=9\relax +\global\copyrightetc{% +{\noindent\confname\ \the\conf\ \the\confinfo}\par\smallskip + \if@printcopyright + \copyright\ \the\copyrtyr\ \@copyrightowner + \fi + \if@acmowned ISBN \else\ifnum\acm@copyrightmode=2 ISBN \else \par\smallskip ACM ISBN \fi\fi + \the\acmcopyr\ifx\@acmPrice\@empty.\else\dots\@acmPrice\fi\par\smallskip +{DOI: \small\expandafter\printdoi\expandafter{\@doi}}} +\toappear{\fontsize{7pt}{8pt}\fontfamily{ptm}\selectfont + \the\boilerplate\par\smallskip + \the\copyrightetc} +\fi +\ifnum\ACM@basesize=10\relax +\global\copyrightetc{% +{\noindent\confname\ \the\conf\ \the\confinfo}\par\smallskip + \if@printcopyright + \copyright\ \the\copyrtyr\ \@copyrightowner + \fi + \if@acmowned ISBN \else\ifnum\acm@copyrightmode=2 ISBN \else \par\smallskip ACM ISBN \fi\fi + \the\acmcopyr\ifx\@acmPrice\@empty.\else\dots\@acmPrice\fi\par\smallskip +{DOI: \small\expandafter\printdoi\expandafter{\@doi}}} +\toappear{\fontsize{7.5pt}{8.5pt}\fontfamily{ptm}\selectfont + \the\boilerplate\par\smallskip + \the\copyrightetc} +\fi +%\DeclareFixedFont{\altcrnotice}{OT1}{tmr}{m}{n}{8} % << patch needed for accenting e.g. Montreal - Gerry, May 2007 +%\DeclareFixedFont{\altconfname}{OT1}{tmr}{m}{it}{8} % << patch needed for accenting in italicized confname - Gerry, May 2007 +% +%{\altconfname{{\the\conf}}} {\altcrnotice\the\confinfo\par} \the\copyrightetc.} % << Gerry, May 2007 +% +% The following section (i.e. 3 .sty inclusions) was added in May 2007 so as to fix the problems that many +% authors were having with accents. Sometimes accents would occur, but the letter-character would be of a different +% font. Conversely the letter-character font would be correct but, e.g. a 'bar' would appear superimposed on the +% character instead of, say, an unlaut/diaresis. Sometimes the letter-character would NOT appear at all. +% Using [T1]{fontenc} outright was not an option as this caused 99% of the authors to 'produce' a Type-3 (bitmapped) +% PDF file - useless for production. +% +% For proper (font) accenting we NEED these packages to be part of the .cls file i.e. 'ae', 'aecompl' and 'aeguil' +% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +%% This is file `ae.sty' +\def\fileversion{1.3} +\def\filedate{2001/02/12} +\NeedsTeXFormat{LaTeX2e} +%\ProvidesPackage{ae}[\filedate\space\fileversion\space % GM +% Almost European Computer Modern] % GM - keeping the log file clean(er) +\newif\if@ae@slides \@ae@slidesfalse +\DeclareOption{slides}{\@ae@slidestrue} +\ProcessOptions +\fontfamily{aer} +\RequirePackage[T1]{fontenc} +\if@ae@slides + \renewcommand{\sfdefault}{laess} + \renewcommand{\rmdefault}{laess} % no roman + \renewcommand{\ttdefault}{laett} +\else + \renewcommand{\sfdefault}{aess} + \renewcommand{\rmdefault}{aer} + \renewcommand{\ttdefault}{aett} +\fi +\endinput +%% +%% End of file `ae.sty'. +% +% +\def\fileversion{0.9} +\def\filedate{1998/07/23} +\NeedsTeXFormat{LaTeX2e} +%\ProvidesPackage{aecompl}[\filedate\space\fileversion\space % GM +%T1 Complements for AE fonts (D. Roegel)] % GM -- keeping the log file clean(er) + +\def\@ae@compl#1{{\fontencoding{T1}\fontfamily{cmr}\selectfont\symbol{#1}}} +\def\guillemotleft{\@ae@compl{19}} +\def\guillemotright{\@ae@compl{20}} +\def\guilsinglleft{\@ae@compl{14}} +\def\guilsinglright{\@ae@compl{15}} +\def\TH{\@ae@compl{222}} +\def\NG{\@ae@compl{141}} +\def\ng{\@ae@compl{173}} +\def\th{\@ae@compl{254}} +\def\DJ{\@ae@compl{208}} +\def\dj{\@ae@compl{158}} +\def\DH{\@ae@compl{208}} +\def\dh{\@ae@compl{240}} +\def\@perthousandzero{\@ae@compl{24}} +\def\textperthousand{\%\@perthousandzero} +\def\textpertenthousand{\%\@perthousandzero\@perthousandzero} +\endinput +% +% +%% This is file `aeguill.sty' +% This file gives french guillemets (and not guillemots!) +% built with the Polish CMR fonts (default), WNCYR fonts, the LASY fonts +% or with the EC fonts. +% This is useful in conjunction with the ae package +% (this package loads the ae package in case it has not been loaded) +% and with or without the french(le) package. +% +% In order to get the guillemets, it is necessary to either type +% \guillemotleft and \guillemotright, or to use an 8 bit encoding +% (such as ISO-Latin1) which selects these two commands, +% or, if you use the french package (but not the frenchle package), +% to type << or >>. +% +% By default, you get the Polish CMR guillemets; if this package is loaded +% with the `cm' option, you get the LASY guillemets; with `ec,' you +% get the EC guillemets, and with `cyr,' you get the cyrillic guillemets. +% +% In verbatim mode, you always get the EC/TT guillemets. +% +% The default option is interesting in conjunction with PDF, +% because there is a Type 1 version of the Polish CMR fonts +% and these guillemets are very close in shape to the EC guillemets. +% There are no free Type 1 versions of the EC fonts. +% +% Support for Polish CMR guillemets was kindly provided by +% Rolf Niepraschk in version 0.99 (2000/05/22). +% Bernd Raichle provided extensive simplifications to the code +% for version 1.00. +% +% This package is released under the LPPL. +% +% Changes: +% Date version +% 2001/04/12 1.01 the frenchle and french package are now distinguished. +% +\def\fileversion{1.01} +\def\filedate{2001/04/12} +\NeedsTeXFormat{LaTeX2e} +%\ProvidesPackage{aeguill}[2001/04/12 1.01 % % GM +%AE fonts with french guillemets (D. Roegel)] % GM - keeping the log file clean(er) +%\RequirePackage{ae} % GM May 2007 - already embedded here + +\newcommand{\@ae@switch}[4]{#4} +\DeclareOption{ec}{\renewcommand\@ae@switch[4]{#1}} +\DeclareOption{cm}{\renewcommand\@ae@switch[4]{#2}} +\DeclareOption{cyr}{\renewcommand\@ae@switch[4]{#3}} +\DeclareOption{pl}{\renewcommand\@ae@switch[4]{#4}} + + +% +% Load necessary packages +% +\@ae@switch{% ec + % do nothing +}{% cm + \RequirePackage{latexsym}% GM - May 2007 - already 'mentioned as required' up above +}{% cyr + \RequirePackage[OT2,T1]{fontenc}% +}{% pl + \RequirePackage[OT4,T1]{fontenc}% +} + +% The following command will be compared to \frenchname, +% as defined in french.sty and frenchle.sty. +\def\aeguillfrenchdefault{french}% + +\let\guill@verbatim@font\verbatim@font +\def\verbatim@font{\guill@verbatim@font\ecguills{cmtt}% + \let\guillemotleft\@oguills\let\guillemotright\@fguills} + +\begingroup \catcode`\<=13 \catcode`\>=13 +\def\x{\endgroup + \def\ae@lfguill{<<}% + \def\ae@rfguill{>>}% +}\x + +\newcommand{\ecguills}[1]{% + \def\selectguillfont{\fontencoding{T1}\fontfamily{#1}\selectfont}% + \def\@oguills{{\selectguillfont\symbol{19}}}% + \def\@fguills{{\selectguillfont\symbol{20}}}% + } + +\newcommand{\aeguills}{% + \ae@guills + % We redefine \guillemotleft and \guillemotright + % in order to catch them when they are used + % with \DeclareInputText (in latin1.def for instance) + % We use \auxWARNINGi as a safe indicator that french.sty is used. + \gdef\guillemotleft{\ifx\auxWARNINGi\undefined + \@oguills % neither french.sty nor frenchle.sty + \else + \ifx\aeguillfrenchdefault\frenchname + \ae@lfguill % french.sty + \else + \@oguills % frenchle.sty + \fi + \fi}% + \gdef\guillemotright{\ifx\auxWARNINGi\undefined + \@fguills % neither french.sty nor frenchle.sty + \else + \ifx\aeguillfrenchdefault\frenchname + \ae@rfguill % french.sty + \else + \@fguills % frenchle.sty + \fi + \fi}% + } + +% +% Depending on the class option +% define the internal command \ae@guills +\@ae@switch{% ec + \newcommand{\ae@guills}{% + \ecguills{cmr}}% +}{% cm + \newcommand{\ae@guills}{% + \def\selectguillfont{\fontencoding{U}\fontfamily{lasy}% + \fontseries{m}\fontshape{n}\selectfont}% + \def\@oguills{\leavevmode\nobreak + \hbox{\selectguillfont (\kern-.20em(\kern.20em}\nobreak}% + \def\@fguills{\leavevmode\nobreak + \hbox{\selectguillfont \kern.20em)\kern-.2em)}% + \ifdim\fontdimen\@ne\font>\z@\/\fi}}% +}{% cyr + \newcommand{\ae@guills}{% + \def\selectguillfont{\fontencoding{OT2}\fontfamily{wncyr}\selectfont}% + \def\@oguills{{\selectguillfont\symbol{60}}}% + \def\@fguills{{\selectguillfont\symbol{62}}}} +}{% pl + \newcommand{\ae@guills}{% + \def\selectguillfont{\fontencoding{OT4}\fontfamily{cmr}\selectfont}% + \def\@oguills{{\selectguillfont\symbol{174}}}% + \def\@fguills{{\selectguillfont\symbol{175}}}} +} + + +\AtBeginDocument{% + \ifx\GOfrench\undefined + \aeguills + \else + \let\aeguill@GOfrench\GOfrench + \gdef\GOfrench{\aeguill@GOfrench \aeguills}% + \fi + } + +\endinput +% + diff --git a/ICCAD16_openram_paper/t.sh b/ICCAD16_openram_paper/t.sh new file mode 100755 index 00000000..851c32cd --- /dev/null +++ b/ICCAD16_openram_paper/t.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# This is a short script to simplify generating a PDF document from LaTeX and BibTeX code. +# The script cleans plausibly existing files, generates the PDF, then cleans furthur generated files. + +# Clean any latent files. +rm -rf *.aux *.bbl *.blg *.log #*.pdf + +# Generate the actual output. +pdflatex main.tex +bibtex main.aux +pdflatex main.tex +pdflatex main.tex +mv main.pdf openram.pdf + +# Clean all the generated files except for the .pdf +rm -rf *.aux *.bbl *.blg *.log *.lof *.lot *.out *.toc diff --git a/README b/README new file mode 100644 index 00000000..fa37917e --- /dev/null +++ b/README @@ -0,0 +1,107 @@ +############################################################################### +BASIC SETUP + +-The OpenRAM compiler has very few dependencies: + +1) ngspice v20 or later or HSpice I-2013.12-1 or later +2) Python 2.7 and higher (currently excludes Python 3 and up) +3) a setup script for each technology +4) a technology directory for each technology with the base cells + +- You must set two environment variables: OPENRAM_HOME should point to +the compiler source directory OPENERAM_TECH should point to a root +technology directory that contains subdirs of all other technologies. + +-All setup scripts should be in the setup_scripts directory under the +technology directory. Please look at the file +setup_openram_freepdk45.py for an example of what is needed for +OpenRAM. Each setup script should be named as: setup_openram_{tech +folder name}.py. + +-Each specific technology (e.g. freepdk45) should be a subdirectory + and include certain folders and files: + +1) gds_lib folder with all the .gds (premade) library cells. At a +minimum this includes: + ms_flop.gds + sense_amp.gds + write_driver.gds + cell_6t.gds + replica_cell_6t.gds + tri_gate.gds + +2) sp_lib folder with all the .sp (premade) library netlists for the above cells. + +3) layers.map + +4) References in tech.py to spice models that correspond to the +transistors in the cells + +5) OpenRAM tech file (tech.py) that contains library, DRC/LVS +information, layer information, etc. for the technology + +- In order to debug, it is useful to have a GDS viewer. In addition to +normal layout tools, we recommend the following viewers: + +LayoutEditor http://www.layouteditor.net/ +GLADE http://www.peardrop.co.uk/ +Magic http://opencircuitdesign.com/magic/ + +############################################################################### +DIRECTORY STRUCTURE + +compiler - openram compiler itself (pointed to by OPENRAM_HOME) +compiler/characterizer - timing characterization code +compiler/gdsMill - gds reader/writer +compiler/tests - unit tests +technology/freepdk45 - example configuration library for freepdk45 technology node +technology/scn3me_subm - example configuration library SCMOS technology node +technology/setup_scripts - setup scripts to customize your PDKs and OpenRAM technologies + +############################################################################### +Example to output/input .gds layout files from/to Cadence + +1) To create your component layouts, you should stream them to +individual gds files using our provided layermap and flatten +cells. For example, + + strmout -layerMap layers.map -library sram -topCell $i -view layout -flattenVias -flattenPcells -strmFile ../gds_lib/$i.gds + +2) To stream a layout back into Cadence, do this: + + strmin -layerMap layers.map -attachTechFileOfLib NCSU_TechLib_FreePDK45 -library sram_4_32 -strmFile sram_4_32.gds + +When you import a gds file, make sure to attach the correct tech lib +or you will get incorrect layers in the resulting library. + + + +############################################################################### +UNIT TESTS + +Regression testing performs a number of tests for all modules in OpenRAM. + +Steps to run regression testing: +1) First, ensure your setup_scripts is correctly setup. +2) Navigate to the compiler directory (trunk/compiler/tests) +3) Use the command: + python regress.py +4) To run a specific test: + python {unit test}.py + +The unit tests take the same arguments as openram.py itself. + +To increase the verbosity of the test, add one (or more) -v options: +python tests/00_code_format_check_test.py -v + +To specify a particular technology use "-t " such as +"-t scn3me_subm" + +A regression daemon script that can be used with cron is included: +regress_daemon.py +regress_daemon.sh + +This updates a git repository, checks out code, and sends an email +report with status information. + + diff --git a/compiler/TODO b/compiler/TODO new file mode 100644 index 00000000..e8744aaa --- /dev/null +++ b/compiler/TODO @@ -0,0 +1,34 @@ + +Develop set of vector/point manipulation functions and replace +everywhere. (Bin) + +Use signal names from the technology file. Right now they are hard +coded everywhere. For example: DATA, ADDR, etc. + +Cell name (ms_flop) is hard coded in characterizer, pin names are hard +coded too. This should come from the config file which dynamically +loads the module names. + +Autodetect ideal number of threads for hspice. + +vdd and gnd are hard coded in some places. The names should come from +the tech file. + +Some modules use upper/lower via layer instead of min width DRC rule +from tech file. + +Fix the size of labels. For some reason, they are HUGE. (Samira) + +Add the clock buffer internal to control logic. Simulation uses +1-4-8-16 inverters right now. Replace simulation with simple clock +buffer after fixing. + +Check out the multibank organization in sram.py and bank.py to see if +it can be reduced or made more readable. + +Move/modify similar functions in hierarchical_predecode2x4 and +hierarchical_predecode3x8 to hierarchical_predecode class + +Fix stimuli.py to be more readable. + +Add tests for bitcell, ms_flop, replica_bitcell, sens_amp, tri_gate, write_driver? diff --git a/compiler/bank.py b/compiler/bank.py new file mode 100644 index 00000000..df146da9 --- /dev/null +++ b/compiler/bank.py @@ -0,0 +1,1518 @@ +import sys +from tech import drc, parameter, cell +import debug +import design +import math +from math import log,sqrt,ceil +from contact import contact +from pinv import pinv +from nand_2 import nand_2 +from nor_2 import nor_2 +from vector import vector +from globals import OPTS + +class bank(design.design): + """ + Dynamically generated a single Bank including bitcell array, + hierarchical_decoder, precharge, column_mux, write driver and sense amplifiers. + """ + + def __init__(self, word_size, num_words, words_per_row, num_banks=1, name=""): + + mod_list = ["tri_gate", "bitcell", "decoder", "ms_flop_array", "wordline_driver", + "bitcell_array", "sense_amp_array", "precharge_array", + "column_mux_array","write_driver_array", "tri_gate_array"] + for mod_name in mod_list: + config_mod_name = getattr(OPTS.config, mod_name) + class_file = reload(__import__(config_mod_name)) + mod_class = getattr(class_file , config_mod_name) + setattr (self, "mod_"+mod_name, mod_class) + + self.bitcell_height = self.mod_bitcell.chars["height"] + self.tri_gate_chars = self.mod_tri_gate.chars + + if name == "": + self.name = "bank_{0}_{1}".format(word_size, num_words) + else: + self.name = name + design.design.__init__(self, self.name) + debug.info(2, "create sram of size {0} with {1} num of words".format(word_size,num_words)) + + self.word_size = word_size + self.num_words = num_words + self.words_per_row = words_per_row + self.num_banks = num_banks + + self.compute_sizes() + self.add_pins() + self.create_modules() + self.add_modules() + self.setup_layout_constraints() + + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + """ Adding pins for Bank module""" + for i in range(self.word_size): + self.add_pin("DATA[{0}]".format(i)) + for i in range(self.addr_size): + self.add_pin("ADDR[{0}]".format(i)) + + if(self.num_banks > 1): + self.add_pin("bank_select") + self.add_pin("gated_s_en") + self.add_pin("gated_w_en") + self.add_pin("gated_tri_en_bar") + self.add_pin("gated_tri_en") + self.add_pin("gated_clk_bar") + self.add_pin("gated_clk") + else: + self.add_pin("s_en") + self.add_pin("w_en") + self.add_pin("tri_en_bar") + self.add_pin("tri_en") + self.add_pin("clk_bar") + self.add_pin("clk") + self.add_pin("vdd") + self.add_pin("gnd") + + def create_layout(self): + """ Create routing amoung the modules """ + self.create_central_bus() + self.route_pre_charge_to_bitcell_array() + self.route_between_sense_amp_and_tri_gate() + self.route_tri_gate_out() + + self.route_between_wordline_driver_and_bitcell_array() + self.route_column_address_lines() + self.route_msf_address_to_row_decoder() + self.route_control_lines() + if(self.num_banks > 1): + self.route_bank_select_or2_gates() + self.route_power_rail_vdd() + self.route_power_rail_gnd() + + self.offset_all_coordinates() + + def add_modules(self): + """ Add modules. The order should be maintained.""" + self.add_bitcell_array() + self.add_precharge_array() + self.add_column_mux_array() + self.add_sense_amp_array() + self.add_write_driver_array() + self.add_msf_data_in() + self.add_tri_gate_array() + self.add_hierarchical_decoder() + self.add_wordline_driver() + self.add_msf_address() + self.add_column_line_decoder() + self.add_bank_select_or2_gates() + + def compute_sizes(self): + """ Computes the required sizes to create the bank """ + + self.num_cols = self.words_per_row*self.word_size + self.num_rows = self.num_words / self.words_per_row + + self.row_addr_size = int(log(self.num_rows, 2)) + self.col_addr_size = int(log(self.words_per_row, 2)) + self.addr_size = self.col_addr_size + self.row_addr_size + + assert self.num_rows*self.num_cols, self.word_size*self.num_words + assert self.addr_size, self.col_addr_size + self.row_addr_size + + self.power_rail_width = 10*drc["minwidth_metal1"] + + # Width for left gnd rail + self.power_rail_width = 10*drc["minwidth_metal2"] + self.left_gnd_rail_gap = 4*drc["minwidth_metal2"] + + # Number of control lines in the bus + self.number_of_control_lines = 6 + + self.num_central_bus = (2 * self.col_addr_size + self.row_addr_size + + self.number_of_control_lines) + + # bus gap is choosen 2 times the minimum width to eliminate drc between + # contact on one bus and the adjacent bus + self.width_central_bus = drc["minwidth_metal2"] + self.gap_central_bus = 2*drc["metal2_to_metal2"] + + # Overall central bus gap. It includes all the column mux lines, 6 + # control lines, address flop to decoder lines and a GND power rail in M2 + central_bus_gap = ((self.gap_central_bus + self.width_central_bus) + *(self.num_central_bus + 2)) + self.overall_central_bus_gap = (central_bus_gap + self.power_rail_width + + self.left_gnd_rail_gap) + + self.start_of_right_central_bus = self.gap_central_bus + control_gap = ((self.gap_central_bus + self.width_central_bus) + * self.number_of_control_lines) + self.start_of_left_central_bus = (control_gap + self.power_rail_width + + self.left_gnd_rail_gap + + self.start_of_right_central_bus) + + + # Array for control lines + self.control_bus = [] + self.control_signals = ["s_en", "w_en", + "clk_bar", + "tri_en", "tri_en_bar", + "clk"] + self.gated_control_signals = ["gated_s_en", "gated_w_en", + "gated_clk_bar", + "gated_tri_en", "gated_tri_en_bar", + "gated_clk"] + + # Array for bank address positions + self.address_positions = [] + + # Array for bank data positions + self.data_positions = [] + + def create_modules(self): + """ Create all the modules using the class loader """ + self.bitcell_array = self.mod_bitcell_array(name="bitcell_array", + cols=self.num_cols, + rows=self.num_rows) + self.add_mod(self.bitcell_array) + + self.precharge_array = self.mod_precharge_array(name="precharge_array", + columns=self.num_cols, + ptx_width=drc["minwidth_tx"]) + self.add_mod(self.precharge_array) + + if(self.col_addr_size > 0): + self.column_mux_array = self.mod_column_mux_array(rows=self.num_rows, + columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.column_mux_array) + + + self.sens_amp_array = self.mod_sense_amp_array(word_size=self.word_size, + words_per_row=self.words_per_row) + self.add_mod(self.sens_amp_array) + + self.write_driver_array = self.mod_write_driver_array(columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.write_driver_array) + + self.decoder = self.mod_decoder(nand2_nmos_width=2*drc["minwidth_tx"], + nand3_nmos_width=3*drc["minwidth_tx"], + rows=self.num_rows) + self.add_mod(self.decoder) + + self.msf_address = self.mod_ms_flop_array(name="msf_address", + array_type="address", + columns=self.row_addr_size+self.col_addr_size, + word_size=self.row_addr_size+self.col_addr_size) + self.add_mod(self.msf_address) + + self.msf_data_in = self.mod_ms_flop_array(name="msf_data_in", + array_type="data_in", + columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.msf_data_in) + + self.msf_data_out = self.mod_ms_flop_array(name="msf_data_out", + array_type="data_out", + columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.msf_data_out) + + self.tri_gate_array = self.mod_tri_gate_array(columns=self.num_cols, + word_size=self.word_size) + self.add_mod(self.tri_gate_array) + + self.wordline_driver = self.mod_wordline_driver(name="wordline_driver", + rows=self.num_rows) + self.add_mod(self.wordline_driver) + + self.inv = pinv(name="pinv", + nmos_width=drc["minwidth_tx"], + beta=parameter["pinv_beta"], + height=self.bitcell_height) + self.add_mod(self.inv) + + # 4x Inverter + self.inv4x = pinv(name="pinv4x", + nmos_width=4*drc["minwidth_tx"], + beta=parameter["pinv_beta"], + height=self.bitcell_height) + self.add_mod(self.inv4x) + + self.NAND2 = nand_2(name="pnand2_x1", + nmos_width=2*drc["minwidth_tx"], + height=self.bitcell_height) + self.add_mod(self.NAND2) + + self.NOR2 = nor_2(name="pnor2_x1", + nmos_width=drc["minwidth_tx"], + height=self.bitcell_height) + self.add_mod(self.NOR2) + + # These aren't for instantiating, but we use them to get the dimensions + self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) + + # Vertical metal rail gap definition + self.metal2_extend_contact = (self.m1m2_via.second_layer_height + - self.m1m2_via.contact_width) / 2 + self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"] + self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"] + self.via_shift = (self.m1m2_via.second_layer_width + - self.m1m2_via.first_layer_width) / 2 + + def add_bitcell_array(self): + """ Adding Bitcell Array """ + + self.module_offset = vector(0, 0) + self.bitcell_array_position = self.module_offset + self.add_inst(name="bitcell_array", + mod=self.bitcell_array, + offset=self.module_offset) + temp = [] + for i in range(self.num_cols): + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + for j in range(self.num_rows): + temp.append("wl[{0}]".format(j)) + temp = temp + ["vdd", "gnd"] + self.connect_inst(temp) + + def add_precharge_array(self): + """ Adding Pre-charge """ + + self.gap_between_precharge_and_bitcell = 5 * drc["minwidth_metal2"] + + y_off = self.bitcell_array.height + self.gap_between_precharge_and_bitcell + self.precharge_array_position = vector(0, y_off) + self.add_inst(name="precharge_array", + mod=self.precharge_array, + offset=self.precharge_array_position) + temp = [] + for i in range(self.num_cols): + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + temp = temp + ["clk_bar", "vdd"] + self.connect_inst(temp) + + def add_column_mux_array(self): + """ Adding Column Mux when words_per_row > 1 . """ + + if(self.col_addr_size != 0): + self.module_offset = vector(0, -self.column_mux_array.height) + self.column_mux_array_position = self.module_offset + self.add_inst(name="column_mux_array", + mod=self.column_mux_array, + offset=self.column_mux_array_position) + temp = [] + for i in range(self.num_cols): + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + for j in range(self.word_size): + temp.append("bl_out[{0}]".format( + j*self.words_per_row)) + temp.append("br_out[{0}]".format( + j*self.words_per_row)) + for k in range(self.words_per_row): + temp.append("sel[{0}]".format(k)) + temp.append("gnd") + self.connect_inst(temp) + + def add_sense_amp_array(self): + """ Adding Sense amp """ + + self.module_offset = vector(0, self.module_offset.y - self.sens_amp_array.height) + self.sens_amp_array_position = self.module_offset + self.add_inst(name="sense_amp_array", + mod=self.sens_amp_array, + offset=self.sens_amp_array_position) + temp = [] + if (self.words_per_row == 1): + for j in range(self.word_size): + temp.append("bl[{0}]".format(j*self.words_per_row)) + temp.append("br[{0}]".format(j*self.words_per_row)) + else: + for j in range(self.word_size): + temp.append("bl_out[{0}]".format(j*self.words_per_row)) + temp.append("br_out[{0}]".format(j*self.words_per_row)) + + for i in range(self.word_size): + temp.append("data_out[{0}]".format(i)) + temp = temp + ["s_en", "vdd", "gnd"] + self.connect_inst(temp) + + def add_write_driver_array(self): + """ Adding Write Driver """ + + self.module_offset = vector(0, self.module_offset.y - self.write_driver_array.height) + self.write_driver_array_position = self.module_offset + self.add_inst(name="write_driver_array", + mod=self.write_driver_array, + offset=self.write_driver_array_position) + + temp = [] + for i in range(self.word_size): + temp.append("data_in[{0}]".format(i)) + if (self.words_per_row == 1): + for j in range(self.word_size): + temp.append("bl[{0}]".format(j*self.words_per_row)) + temp.append("br[{0}]".format(j*self.words_per_row)) + else: + for j in range(self.word_size): + temp.append("bl_out[{0}]".format(j*self.words_per_row)) + temp.append("br_out[{0}]".format(j*self.words_per_row)) + temp = temp + ["w_en", "vdd", "gnd"] + self.connect_inst(temp) + + def add_msf_data_in(self): + """ data_in flip_flop """ + + self.module_offset = vector(0, self.module_offset.y - self.msf_data_in.height) + self.ms_flop_data_in_offset = self.module_offset + self.add_inst(name="data_in_flop_array", + mod=self.msf_data_in, + + offset=self.ms_flop_data_in_offset) + + temp = [] + for i in range(self.word_size): + temp.append("DATA[{0}]".format(i)) + for i in range(self.word_size): + temp.append("data_in[{0}]".format(i)) + temp.append("data_in_bar[{0}]".format(i)) + temp = temp + ["clk_bar", "vdd", "gnd"] + self.connect_inst(temp) + + def add_tri_gate_array(self): + """ data tri gate to drive the data bus """ + + self.module_offset = vector(0, self.module_offset.y) + self.tri_gate_array_offset = self.module_offset + self.add_inst(name="trigate_data_array", + mod=self.tri_gate_array, + offset=self.tri_gate_array_offset, + mirror="MX") + temp = [] + for i in range(self.word_size): + temp.append("data_out[{0}]".format(i)) + for i in range(self.word_size): + temp.append("DATA[{0}]".format(i)) + temp = temp + ["tri_en", "tri_en_bar", "vdd", "gnd"] + self.connect_inst(temp) + + def add_hierarchical_decoder(self): + """ Hierarchical Decoder """ + + """ creating space for address bus before we add Decoder. + The bus will be in between decoder and the main Memory array part + This bus will route decoder input and column mux inputs. + For convenient the space is created first so that placement of decoder and address FFs gets easier. + The wires are actually routed after we placed the stuffs on both side""" + + self.module_offset = vector(self.decoder.width + self.overall_central_bus_gap, + self.decoder.predecoder_height).scale(-1, -1) + self.decoder_position = self.module_offset + self.add_inst(name="address_decoder", + mod=self.decoder, + offset=self.decoder_position) + + temp = [] + for i in range(self.row_addr_size): + temp.append("A[{0}]".format(i)) + for j in range(self.num_rows): + temp.append("decode_out[{0}]".format(j)) + temp = temp + ["vdd", "gnd"] + self.connect_inst(temp) + + def add_wordline_driver(self): + """ Wordline Driver """ + + x_off = self.decoder_position.x + self.decoder.row_decoder_width + self.module_offset = vector(x_off, 0) + self.wordline_driver_position = self.module_offset + self.add_inst(name="wordline_driver", + mod=self.wordline_driver, + offset=self.wordline_driver_position) + + temp = [] + for i in range(self.num_rows): + temp.append("decode_out[{0}]".format(i)) + for i in range(self.num_rows): + temp.append("wl[{0}]".format(i)) + + if(self.num_banks > 1): + temp.append("gated_clk") + else: + temp.append("clk") + temp.append("vdd") + temp.append("gnd") + self.connect_inst(temp) + + def add_msf_address(self): + """ Adding address Flip-flops """ + + gap = max(drc["pwell_enclose_nwell"], + 2*drc["minwidth_metal2"]) + + self.module_offset = vector(-self.overall_central_bus_gap + - self.msf_address.height + - 4*drc["minwidth_metal2"], + self.decoder_position.y - gap + - drc["minwidth_metal2"]) + self.msf_address_offset = self.module_offset + self.add_inst(name="address_flop_array", + mod=self.msf_address, + offset=self.msf_address_offset, + mirror="R270") + if(self.col_addr_size == 1): + temp = [] + for i in range(self.row_addr_size): + temp.append("ADDR[{0}]".format(i)) + temp.append("ADDR[{0}]".format(self.row_addr_size)) + + for i in range(self.row_addr_size): + temp.append("A[{0}]".format(i)) + temp.append("A_bar[{0}]".format(i)) + temp.append("sel[1]") + temp.append("sel[0]") + if(self.num_banks > 1): + temp = temp + ["gated_clk", "vdd", "gnd"] + else: + temp = temp + ["clk", "vdd", "gnd"] + self.connect_inst(temp) + else: + temp = [] + for i in range(self.row_addr_size + self.col_addr_size): + temp.append("ADDR[{0}]".format(i)) + for i in range(self.row_addr_size + self.col_addr_size): + temp.append("A[{0}]".format(i)) + temp.append("A_bar[{0}]".format(i)) + if(self.num_banks > 1): + temp = temp + ["gated_clk", "vdd", "gnd"] + else: + temp = temp + ["clk", "vdd", "gnd"] + self.connect_inst(temp) + + # update the min_point + self.min_point = (self.msf_address_offset.y - self.msf_address.width + - 4*drc["minwidth_metal1"]) + + def add_column_line_decoder(self): + """ Create a 2:4 decoder to decode colum select lines if the col_addr_size = 4 """ + + if(self.col_addr_size == 2): + vertical_gap = max(drc["pwell_enclose_nwell"] + drc["minwidth_metal2"], + 3 * drc["minwidth_metal2"] + 3 * drc["metal2_to_metal2"]) + self.col_decoder = self.decoder.pre2_4 + x_off = (self.gap_central_bus + self.width_central_bus + + self.overall_central_bus_gap + + self.col_decoder.width) + y_off =(self.msf_address_offset.y - self.msf_address.width + - self.col_decoder.height - vertical_gap) + self.module_offset = vector(-x_off, y_off) + self.col_decoder_position = self.module_offset + self.add_inst(name="col_address_decoder", + mod=self.decoder.pre2_4, + offset=self.col_decoder_position) + addr_index = self.row_addr_size + temp = [] + for i in range(2): + temp.append("A[{0}]".format(i + self.row_addr_size)) + for j in range(4): + temp.append("sel[{0}]".format(j)) + temp = temp + ["vdd", "gnd"] + self.connect_inst(temp) + + # update the min_point + self.min_point = self.col_decoder_position.y + + def add_bank_select_or2_gates(self): + """ Create an array of and gates to gate the control signals in case + of multiple banks are created in upper level SRAM module """ + + if(self.num_banks > 1): + # update the min_point + self.min_point = (self.min_point - 3*drc["minwidth_metal1"] + - self.number_of_control_lines * self.bitcell_height) + xoffset_nor = (- self.start_of_left_central_bus - self.NOR2.width + - self.inv4x.width) + xoffset_inv = xoffset_nor + self.NOR2.width + self.bank_select_or_position = vector(xoffset_nor, self.min_point) + + # bank select inverter + self.bank_select_inv_position = vector(self.bank_select_or_position.x + - 5 * drc["minwidth_metal2"] + - self.inv4x.width, + self.min_point) + self.add_inst(name="bank_select_inv", + mod=self.inv4x, + offset=self.bank_select_inv_position) + self.connect_inst(["bank_select", "bank_select_bar", "vdd", "gnd"]) + + for i in range(self.number_of_control_lines): + # central control bus index + # 5 = clk,4 = tri_en_bar,3 = tri_en,2 = clk_bar,1 = w_en,0 = s_en + name_nor = "bank_selector_nor_{0}".format(i) + name_inv = "bank_selector_inv_{0}".format(i) + nor2_inv_connection_height = (self.inv4x.A_position.y + - self.NOR2.Z_position.y + + 0.5 * drc["minwidth_metal1"]) + + if (i % 2): + y_offset = self.min_point + self.inv.height*(i + 1) + mod_dir = "MX" + # nor2 output to inv input + y_correct = (self.NOR2.Z_position.y + nor2_inv_connection_height + - 0.5 * drc["minwidth_metal1"]) + else: + y_offset = self.min_point + self.inv.height*i + mod_dir = "R0" + # nor2 output to inv input + y_correct = 0.5 * drc["minwidth_metal1"] - self.NOR2.Z_position.y + connection = vector(xoffset_inv, y_offset - y_correct) + + if i == 3: + self.add_inst(name=name_nor, + mod=self.NOR2, + offset=[xoffset_nor, y_offset], + mirror=mod_dir) + self.connect_inst(["gated_tri_en_bar", + "bank_select_bar", + self.control_signals[i].format(i), + "vdd", + "gnd"]) + # connect the metal1 layer to connect to the old inv output + offset = connection - vector(0, 0.5*drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=offset, + width=self.inv4x.width, + height=drc["minwidth_metal1"]) + elif i == 5: + offset = [xoffset_nor, y_offset - self.NOR2.A_position.y + - 0.5*drc["minwidth_metal1"]] + self.add_rect(layer="metal1", + offset=offset, + width=self.NOR2.width + self.inv4x.width, + height=drc["minwidth_metal1"]) + else: + self.add_inst(name=name_nor, + mod=self.NOR2, + offset=[xoffset_nor, y_offset], + mirror=mod_dir) + self.connect_inst([self.gated_control_signals[i], + "bank_select_bar", + "net_block_nor_inv[{0}]".format(i), + "vdd", + "gnd"]) + + self.add_inst(name=name_inv, + mod=self.inv4x, + offset=[xoffset_inv, y_offset], + mirror=mod_dir) + self.connect_inst(["net_block_nor_inv[{0}]".format(i), + self.control_signals[i], + "vdd", + "gnd"]) + + # nor2 output to inv input + for i in range(self.number_of_control_lines -1) : + nor2_inv_connection_height = (self.inv4x.A_position.y + - self.NOR2.Z_position.y + + 0.5 * drc["minwidth_metal1"]) + + if (i % 2): + y_offset = self.min_point + self.inv.height * (i + 1) + mod_dir = "MX" + y_correct = (-self.NOR2.Z_position.y + 0.5 * drc["minwidth_metal1"] + - nor2_inv_connection_height) + else: + y_offset = self.min_point + self.inv.height*i + mod_dir = "R0" + y_correct = self.NOR2.Z_position.y - 0.5 * drc["minwidth_metal1"] + # nor2 output to inv input + connection = vector(xoffset_inv, y_offset + y_correct) + self.add_rect(layer="metal1", + offset=connection, + width=drc["minwidth_metal1"], + height=nor2_inv_connection_height) + + def setup_layout_constraints(self): + """ Calculating layout constraints, width, hwight etc """ + + tri_gate_min_point = (self.tri_gate_array_offset.y - 6 * drc["minwidth_metal3"] + - self.tri_gate_array.height) + + self.min_point = min(tri_gate_min_point, self.min_point) + self.max_point = self.precharge_array_position.y + self.precharge_array.height + + # VDD constraints + gap_between_bitcell_array_and_vdd = 3 * drc["minwidth_metal1"] + self.right_vdd_x_offset = self.bitcell_array.width + gap_between_bitcell_array_and_vdd + self.right_vdd_position = vector(self.right_vdd_x_offset, self.min_point) + self.add_layout_pin(text="vdd", + layer="metal1", + offset=[self.right_vdd_x_offset, self.min_point], + width=self.power_rail_width, + height=self.max_point - self.min_point) + # the width of the metal rail is 10 times minwidth metal1 and the gap + # from the edge of the decoder is another 2 times minwidth metal1 + + self.left_vdd_x_offset = (- 14 * drc["minwidth_metal1"] + + min(self.msf_address_offset.x, + self.decoder_position.x)) + self.left_vdd_position = vector(self.left_vdd_x_offset, self.min_point) + self.add_layout_pin(text="vdd", + layer="metal1", + offset=[self.left_vdd_x_offset, self.min_point], + width=self.power_rail_width, + height=self.max_point - self.min_point) + + self.left_gnd_x_offset = (self.left_gnd_rail_gap / 2 + - self.start_of_left_central_bus) + self.left_gnd_position = vector(self.left_gnd_x_offset, self.min_point) + self.add_layout_pin(text="gnd", + layer="metal2", + offset=self.left_gnd_position , + width=self.power_rail_width, + height=self.max_point - self.min_point) + + # Height and Width of the entire bank + self.height = self.max_point - self.min_point + self.width = (self.right_vdd_x_offset - self.left_vdd_x_offset + + self.power_rail_width) + + def create_central_bus(self): + """ Calculating the offset for placing VDD and GND power rails. + Here we determine the lowest point in the layout """ + + """ central Control lines central line connection 2*col_addr_size + number of connections for the column mux and row_addr_size number + of connections for the row address""" + + self.central_line_xoffset = [] + msf_to_central_line = (self.row_addr_size * self.msf_address.width + / (self.row_addr_size + self.col_addr_size)) + self.central_line_y_offset = self.msf_address_offset.y - msf_to_central_line + self.central_line_height = self.max_point - self.min_point + + # Creating the central bus + # Control lines + for i in range(self.number_of_control_lines): + x_offset = (i + 1) * (self.gap_central_bus + self.width_central_bus) + x_offset = -x_offset - self.start_of_right_central_bus + self.central_line_xoffset.append(x_offset) + self.control_bus.append(x_offset) + self.add_rect(layer="metal2", + offset=[x_offset, self.min_point], + width=self.width_central_bus, + height=self.central_line_height) + + # column mux lines if there is column mux [2 or 4 lines] + for i in range(2 * self.col_addr_size): + x_offset = (i + 1) * (self.gap_central_bus + self.width_central_bus) + x_offset = -x_offset - self.start_of_left_central_bus + self.central_line_xoffset.append(x_offset) + self.add_rect(layer="metal2", + offset=[x_offset, self.central_line_y_offset], + width=self.width_central_bus, + height=-self.central_line_y_offset - 4 * drc["minwidth_metal2"]) + + # row adress lines + for i in range(self.row_addr_size): + x_offset = ((self.gap_central_bus + self.width_central_bus) + * (i + 1 + 2*self.col_addr_size)) + x_offset = - x_offset - self.start_of_left_central_bus + self.central_line_xoffset.append(x_offset) + self.add_rect(layer="metal2", + offset=[x_offset, self.central_line_y_offset], + width=self.width_central_bus, + height=-self.central_line_y_offset - 4*drc["minwidth_metal2"]) + + def route_pre_charge_to_bitcell_array(self): + """ Routing of BL and BR between pre-charge and bitcell array """ + for i in range(self.num_cols): + BL_position = self.precharge_array_position + self.precharge_array.BL_positions[i] + BR_position = self.precharge_array_position + self.precharge_array.BR_positions[i] + correct = vector(0.5*drc["minwidth_metal2"], + self.gap_between_precharge_and_bitcell + -self.precharge_array_position.y) + # these two rectangles cannot be replaced with add_path. They are not connected together. + self.add_rect(layer="metal2", + offset=BL_position.scale(1,0) - correct, + width=drc["minwidth_metal2"], + height=self.gap_between_precharge_and_bitcell) + self.add_rect(layer="metal2", + offset=BR_position.scale(1,0) - correct, + width=drc["minwidth_metal2"], + height=self.gap_between_precharge_and_bitcell) + + def route_between_sense_amp_and_tri_gate(self): + """ Routing of sense amp output to tri_gate input """ + for i in range(self.word_size): + # Connection of data_out of sense amp to data_ in of msf_data_out + tri_gate_in_position = (self.tri_gate_array.tri_in_positions[i].scale(1,-1) + + self.tri_gate_array_offset) + sa_data_out_position = (self.sens_amp_array_position + + self.sens_amp_array.Data_out_positions[i]) + + startY = (self.tri_gate_array_offset.y - self.tri_gate_array.height + - 2 * drc["minwidth_metal3"] + + 0.5 * drc["minwidth_metal1"]) + start = vector(tri_gate_in_position.x - 3 * drc["minwidth_metal3"], + startY) + + m3_min = vector([drc["minwidth_metal3"]] * 2) + mid1 = (tri_gate_in_position.scale(1,0) + + sa_data_out_position.scale(0,1) + m3_min.scale(-3, 1)) + mid2 = sa_data_out_position + m3_min.scale(0.5, 1) + self.add_path("metal3", [start, mid1, mid2]) + + mid3 = [tri_gate_in_position.x, startY] + self.add_path("metal2", [start, mid3, tri_gate_in_position]) + + offset = start - vector([0.5*drc["minwidth_metal3"]] * 2) + self.add_via(("metal2", "via2", "metal3"),offset) + + def route_tri_gate_out(self): + """ Metal 3 routing of tri_gate output data """ + for i in range(self.word_size): + tri_gate_out_position = (self.tri_gate_array.DATA_positions[i].scale(1,-1) + + self.tri_gate_array_offset) + data_line_position = [tri_gate_out_position.x - 0.5 * drc["minwidth_metal3"], + self.min_point] + # save data line position + self.data_positions.append(data_line_position) + self.add_via(("metal2", "via2", "metal3"), data_line_position) + self.add_rect(layer="metal3", + offset=data_line_position, + width=drc["minwidth_metal3"], + height=tri_gate_out_position.y - self.min_point) + + def route_between_wordline_driver_and_bitcell_array(self): + """ Connecting Wordline driver output to Bitcell WL connection """ + WL_horizontal_distance = (- self.wordline_driver.WL_positions[0].x + - self.wordline_driver_position.x) + via_shift = (self.m1m2_via.second_layer_width + - self.m1m2_via.first_layer_width) / 2 + + for i in range(self.num_rows): + bitcell_WL_position = (self.bitcell_array.WL_positions[i] + + vector(0.5 * drc["minwidth_metal1"], 0)) + worldline_WL_position = (self.wordline_driver.WL_positions[i] + + self.wordline_driver_position) + worldline_decode_out_position = (self.wordline_driver.decode_out_positions[i] + + self.wordline_driver_position) + decoder_decode_out_position = (self.decoder.decode_out_positions[i] + + self.decoder_position) + + WL_vertical_distance = worldline_WL_position.y - bitcell_WL_position.y + decode_out_height = abs(worldline_decode_out_position.y + - decoder_decode_out_position.y) + drc["minwidth_metal1"] + + if(WL_vertical_distance > 0): + y_dir = 1 + else: + y_dir = -1 + WL_vertical_distance += 2 * y_dir * drc["minwidth_metal1"] + self.add_rect(layer="metal1", + offset=decoder_decode_out_position, + width=drc["minwidth_metal1"], + height=y_dir * decode_out_height) + + mid = bitcell_WL_position - vector(WL_horizontal_distance, 0) + target = bitcell_WL_position + vector(- WL_horizontal_distance, + WL_vertical_distance) + self.add_path("metal1", [bitcell_WL_position, mid, target]) + + # Connecting the vdd of the word line driver to the vdd of the bitcell array. + power_rail_width = (self.bitcell_array.vdd_positions[0].x + - self.wordline_driver.vdd_positions[0].x + - self.wordline_driver_position.x) + for i, offset in enumerate(self.wordline_driver.vdd_positions): + vdd_offset = self.wordline_driver_position + offset + if(i % 2 == 0): + self.add_rect(layer="metal1", + offset=vdd_offset, + width=power_rail_width, + height=drc["minwidth_metal1"]) + + def route_column_address_lines(self): + """ Connecting the select lines of column mux to the address bus """ + for i in range(2*self.col_addr_size): + line_index = i + self.number_of_control_lines + col_addr_line_position = (self.column_mux_array.addr_line_positions[i] + + self.column_mux_array_position) + + contact_offset = [self.central_line_xoffset[line_index], + col_addr_line_position.y] + connection_width = (col_addr_line_position.x + - self.central_line_xoffset[line_index]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=contact_offset) + self.add_rect(layer="metal1", + offset=contact_offset, + width=connection_width, + height=drc["minwidth_metal1"]) + + # Take care of the column address decoder routing + # If there is a 2:4 decoder for column select lines + if(self.col_addr_size == 2): + + # The snake connection between last two address flop to the input + # of the 2:4 column_mux line decoder + + for i in range(2): + ff_index = i + self.row_addr_size + current_dout = self.msf_address.dout_positions[ff_index] + msf_row_addr_line_position = (current_dout.rotate().scale(1,-1) + + self.msf_address_offset) + + line_index = self.num_central_bus - 2 + i + line_offset = self.central_line_xoffset[line_index] + y_offset = (self.col_decoder_position.y + self.col_decoder.height + + (i + 1) * drc["metal2_to_metal2"] + + i * drc["minwidth_metal2"]) + + gap = drc["minwidth_metal2"] + 2 * drc["metal2_to_metal2"] + input_rail_x = self.col_decoder_position.x - (i + 1) * gap + A_position = (self.col_decoder_position + + self.col_decoder.A_positions[i]) + offset = [input_rail_x - 0.5 * drc["minwidth_metal2"], + A_position.y] + self.add_rect(layer="metal1", + offset=offset, + width=A_position.x - input_rail_x, + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset) + + source = msf_row_addr_line_position + mid1 = [line_offset, msf_row_addr_line_position.y] + mid2 = [line_offset, y_offset] + mid3 = [input_rail_x, y_offset] + target = [input_rail_x, self.col_decoder_position.y] + self.add_path("metal2", [source, mid1, mid2, mid3, target]) + + # connections between outputs of 2:4 decoder to the extension of + # main address bus + for i in range(4): + line_index = i + self.number_of_control_lines + x_offset = self.central_line_xoffset[line_index] + y_offset = self.col_decoder_position.y + contact_offset = vector(x_offset,y_offset) + + col_decoder_out_position =(self.col_decoder_position + + self.col_decoder.decode_out_positions[i] + + vector(0, 0.5 * drc["minwidth_metal1"])) + connection_width = (x_offset - col_decoder_out_position.x + + 0.5 * drc["minwidth_metal2"]) + mid1 = col_decoder_out_position + vector(connection_width,0) + mid2 = col_decoder_out_position + vector(connection_width, + -self.central_line_y_offset) + + self.add_wire(layers=("metal2", "via1", "metal1"), + coordinates=[col_decoder_out_position,mid1,mid2], + offset=col_decoder_out_position) + + # if there are only two column select lines we just connect the dout_bar of the last FF + # to only select line and dout of that FF to the other select line + elif(self.col_addr_size == 1): + ff_index = self.row_addr_size + base = self.msf_address_offset - vector(0, 0.5 * drc["minwidth_metal3"]) + dout_position = (self.msf_address.dout_positions[ff_index].rotate().scale(1,-1) + + base) + dout_bar_position = (self.msf_address.dout_bar_positions[ff_index].rotate().scale(1,-1) + + base) + + y_offset = self.msf_address_offset.y - self.msf_address.width + height = self.central_line_y_offset - y_offset + + for i in range(2): + self.add_rect(layer="metal2", + offset=[self.central_line_xoffset[i + self.number_of_control_lines], + y_offset], + width=self.width_central_bus, + height=height) + + # dout connection to column select 1 + line_offset = self.central_line_xoffset[self.number_of_control_lines + 1] + connection_width = line_offset - dout_position.x + drc["minwidth_metal2"] + self.add_rect(layer="metal3", + offset=dout_position, + width=connection_width, + height=drc["minwidth_metal3"]) + # two m2m3_via contancts on both end + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[line_offset, + dout_position.y + drc["minwidth_metal3"]], + mirror="R270") + self.add_via(layers=("metal2", "via2", "metal3"), + offset=dout_position, + mirror="R90") + # dout_bar connection to column select 0 + line_offset = self.central_line_xoffset[self.number_of_control_lines] + connection_width = line_offset - dout_bar_position.x + drc["minwidth_metal2"] + self.add_rect(layer="metal3", + offset=dout_bar_position, + width=connection_width, + height=drc["minwidth_metal3"]) + # two m2m3_via contancts on both end + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[line_offset, dout_bar_position.y]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=(dout_bar_position + + vector(drc["minwidth_metal2"], 0)), + mirror="R90") + + def route_msf_address_to_row_decoder(self): + """ Routing the row address lines from the address ms-flop array to the row-decoder """ + for i in range(self.row_addr_size): + decoder_row_addr_line_position = (self.decoder_position + + self.decoder.A_positions[i]) + + line_index = i + 2*self.col_addr_size + self.number_of_control_lines + connection_width = (self.central_line_xoffset[line_index] + drc["minwidth_metal2"] + - decoder_row_addr_line_position.x) + first_contact_offset = [self.central_line_xoffset[line_index], + decoder_row_addr_line_position.y] + + self.add_rect(layer="metal1", + offset=decoder_row_addr_line_position, + width=connection_width, + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=first_contact_offset) + + # addres translation should take care of the 270 degree CCW rotation + # addres translation should take care of the 270 degree CCW rotation + msf_row_addr_line_position = (self.msf_address.dout_positions[i].rotate().scale(1,-1) + + self.msf_address_offset + - vector(0, 0.5 * drc["minwidth_metal3"])) + connection_width = (self.central_line_xoffset[line_index] + drc["minwidth_metal2"] + - msf_row_addr_line_position.x) + second_contact_offset = [self.central_line_xoffset[line_index], + msf_row_addr_line_position.y] + + self.add_rect(layer="metal3", + offset=msf_row_addr_line_position, + width=connection_width, + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=second_contact_offset) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=msf_row_addr_line_position, + mirror="R90") + + for i in range(self.addr_size): + # Route msf address inputs + msf_din_position = (self.msf_address.din_positions[i].rotate().scale(1,-1) + + self.msf_address_offset + - vector(0, 0.5 * drc["minwidth_metal3"])) + address_position = vector(self.left_vdd_x_offset, + msf_din_position.y) + self.address_positions.append(address_position) + self.add_rect(layer="metal3", + offset=address_position, + width=msf_din_position.x - self.left_vdd_x_offset, + height=drc["minwidth_metal3"]) + + def route_control_lines(self): + """ Routing of control lines """ + # 5 = clk, 4 = tri_en_bar, 3 = tri_en, 2 = clk_bar, 1 = w_en, 0 = s_en + + self.clk_position = [self.central_line_xoffset[5], 0] + self.tri_en_bar_position = [self.central_line_xoffset[4], 0] + self.tri_en_position = [self.central_line_xoffset[3], 0] + self.clk_bar_position = [self.central_line_xoffset[2], 0] + self.w_en_position = [self.central_line_xoffset[1], 0] + self.s_en_position = [self.central_line_xoffset[0], 0] + + right_hand_mapping = [2, 4, 3, 2, 1, 0] + + right_side = [] + right_side.append(self.ms_flop_data_in_offset + + self.msf_data_in.clk_positions[0] + - vector(0, 0.5 * drc["minwidth_metal1"])) + right_side.append(self.tri_gate_array_offset + + vector(1,-1).scale(self.tri_gate_chars["en_bar"]) + - vector(0, 0.5 * drc["minwidth_metal1"])) + right_side.append(self.tri_gate_array_offset + + vector(1,-1).scale(self.tri_gate_chars["en"]) + - vector(0, 0.5 * drc["minwidth_metal1"])) + right_side.append(self.precharge_array_position + + self.precharge_array.pclk_position) + right_side.append(self.write_driver_array_position + + self.write_driver_array.wen_positions[0]) + right_side.append(self.sens_amp_array_position + + self.sens_amp_array.SCLK_positions[0]) + + """ Routing control signals through the central bus. + Connection of control signal input to the central bus is in metal1 + Connection from the central bus to the main control block crosses + pre-decoder and this connections are in metal3""" + + control_line_offsets = [] + """ Connecting right hand side [sense amp. write_driver , tri state + gates, ffs] to the central bus""" + + for i in range(len(right_side)): + bus_line_index = right_hand_mapping[i] + + x_offset = self.central_line_xoffset[bus_line_index] + y_offset = self.tri_gate_array_offset.y + height = self.central_line_y_offset - y_offset + right_side_connection_width = right_side[i].x - self.central_line_xoffset[bus_line_index] + right_side_contact_offset = [self.central_line_xoffset[bus_line_index], + right_side[i].y] + self.add_rect(layer="metal1", + offset=right_side_contact_offset, + width=right_side_connection_width, + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=right_side_contact_offset) + + if(right_side[i].y > 0): + self.add_rect(layer="metal2", + offset=[self.central_line_xoffset[bus_line_index], 0], + width=drc["minwidth_metal2"], + height=right_side[i].y + 2*drc["minwidth_metal2"]) + + """ CLK connection from central bus to MSF address + should we move this somewhere else hard to find when modify""" + msf_address_clk_position = (self.msf_address_offset + + self.msf_address.clk_positions[0].rotate().scale(1,-1) + + vector(- 0.5 * drc["minwidth_metal1"], + 2 * drc["minwidth_metal2"])) + clk_connection_position = (self.msf_address_offset + + vector(self.msf_address.clk_positions[0].y, + 2 * drc["minwidth_metal3"])) + + connection_width = self.central_line_xoffset[5] - clk_connection_position.x + self.add_via(layers=("metal1", "via1", "metal2"), + offset=msf_address_clk_position, + mirror="R90") + self.add_via(layers=("metal2", "via2", "metal3"), + offset=msf_address_clk_position, + mirror="R90") + + mid_base = vector(msf_address_clk_position.x, clk_connection_position.y) + mid1 = mid_base + vector(0, 0.5 * drc["minwidth_metal3"]) + mid2 = (mid_base + vector([0.5 * drc["minwidth_metal3"]] * 2) + + vector(connection_width, 0)) + self.add_path(layer="metal3", + coordinates=[msf_address_clk_position,mid1,mid2], + width=drc["minwidth_metal3"]) + + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[self.central_line_xoffset[5], + clk_connection_position.y]) + + # Clk connection from central Bus to wordline_driver + wl_clk_position = (self.wordline_driver_position + + self.wordline_driver.clk_positions[0]) + connection_width = (self.central_line_xoffset[5] - wl_clk_position.x + + drc["minwidth_metal1"]) + y_off = self.max_point - 2.5 * drc["minwidth_metal1"] + start = wl_clk_position + vector(0.5 * drc["minwidth_metal1"], 0) + mid1 = [wl_clk_position.x, y_off] + mid2 = mid1 + vector(connection_width, 0) + self.add_path(layer="metal1", + coordinates=[wl_clk_position, mid1, mid2], + width=drc["minwidth_metal1"], + offset=start) + + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.central_line_xoffset[5], + self.max_point - 3*drc["minwidth_metal1"]]) + + def route_bank_select_or2_gates(self): + """ Route array of or gates to gate the control signals in case + of multiple banks are created in upper level SRAM module """ + bank_select_line_xoffset = (self.bank_select_or_position.x + - 3*drc["minwidth_metal2"]) + self.add_rect(layer="metal2", + offset=[bank_select_line_xoffset, + self.bank_select_or_position.y], + width=drc["minwidth_metal2"], + height=self.number_of_control_lines*self.inv.height) + + # bank select inverter routing + # output side + start = self.bank_select_inv_position + self.inv4x.Z_position + end = self.bank_select_or_position + self.NOR2.B_position + mid = vector(start.x, end.y) + self.add_path("metal1", [start, mid, end]) + + # input side + start = self.bank_select_inv_position + self.inv4x.A_position + end = vector(self.left_vdd_x_offset, start.y + 3 * drc["minwidth_metal3"]) + mid = vector(start.x, end.y) + self.add_wire(("metal1", "via1", "metal2"), [start, mid, end]) + + # save position + self.bank_select_position = end - vector(0, 0.5 * drc["minwidth_metal2"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=self.bank_select_position) + + x_offset = (self.bank_select_or_position.x + self.NOR2.width + + self.inv4x.width - drc["minwidth_metal1"]) + for i in range(self.number_of_control_lines): + base = self.bank_select_or_position.y + self.inv.height * i + if(i % 2): + Z_y_offset = (base + self.inv.height - self.inv4x.Z_position.y + - drc["minwidth_metal1"]) + B_y_offset = (base + self.inv.height - self.NOR2.B_position.y + - 0.5 * drc["minwidth_metal1"]) + A_y_offset = (base + self.inv.height - self.NOR2.A_position.y + - 0.5 * drc["minwidth_metal1"]) + else: + Z_y_offset = (base + self.inv4x.Z_position.y) + B_y_offset = (base + self.NOR2.B_position.y + - 0.5 * drc["minwidth_metal1"]) + A_y_offset = (base + self.NOR2.A_position.y + + 0.5 * drc["minwidth_metal1"] + - self.m1m2_via.width) + + # output + self.add_rect(layer="metal3", + offset=[x_offset, Z_y_offset], + width=self.central_line_xoffset[i] - x_offset, + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[x_offset, Z_y_offset]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[x_offset, Z_y_offset]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[self.central_line_xoffset[i], Z_y_offset]) + + # B_input + if i != 5: + self.add_rect(layer="metal1", + offset=[bank_select_line_xoffset, B_y_offset], + width=(self.bank_select_or_position.x + - bank_select_line_xoffset), + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[bank_select_line_xoffset, B_y_offset]) + + # A_input + if i != 3: + self.add_rect(layer="metal3", + offset=[self.left_vdd_x_offset, A_y_offset], + width=(self.bank_select_or_position.x + - self.left_vdd_x_offset), + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.bank_select_or_position.x + + drc["minwidth_metal1"], + A_y_offset], + mirror="R90") + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[self.bank_select_or_position.x + + drc["minwidth_metal1"], + A_y_offset], + mirror="R90") + else: + # connect A to last A, both are tri_en_bar + via_offset = vector(self.bank_select_or_position.x + + drc["minwidth_metal1"], + A_y_offset) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=via_offset, + mirror="R90") + self.add_via(layers=("metal2", "via2", "metal3"), + offset=via_offset, + mirror="R90") + + start = via_offset + vector(0, 0.5 * self.m1m2_via.width) + mid = [self.left_vdd_x_offset - self.left_vdd_x_offset + - drc["minwidth_metal2"] - drc["metal2_to_metal2"] + + bank_select_line_xoffset, + start.y] + correct_y = (2 * self.NOR2.A_position.y + drc["minwidth_metal1"] + - self.m1m2_via.width) + end = start + vector(0, correct_y) + self.add_wire(("metal2", "via2", "metal3"), [start, mid, end]) + + # Save position + setattr(self,"{0}_position".format(self.control_signals[i]), + [self.left_vdd_x_offset, A_y_offset]) + + def route_power_rail_vdd(self): + """ Routing of VDD for all modules """ + + # RIGHT HAND SIDE VDD RAIL CONNECTIONS + # Connecting Bitcell-array VDDs + for i,offset in enumerate(self.bitcell_array.vdd_positions): + if (i % 2 == 0): + self.add_rect(layer="metal1", + offset=offset - vector(0, 0.5 * drc["minwidth_metal1"]), + width=self.right_vdd_x_offset - offset.x, + height=drc["minwidth_metal1"]) + + # Connecting Pre-charge VDD + for offset in self.precharge_array.vdd_positions: + self.add_rect(layer="metal1", + offset=self.precharge_array_position + offset, + width=(self.right_vdd_x_offset - offset.x + - self.precharge_array_position.x), + height=drc["minwidth_metal1"]) + + # Connecting Sense Amp VDD + for offset in self.sens_amp_array.vdd_positions: + self.add_rect(layer="metal1", + offset=self.sens_amp_array_position + offset, + width=(self.right_vdd_x_offset - offset.x + - self.sens_amp_array_position.x), + height=drc["minwidth_metal1"]) + + # Connecting Write Driver VDD + for offset in self.write_driver_array.vdd_positions: + self.add_rect(layer="metal1", + offset=self.write_driver_array_position + offset, + width=(self.right_vdd_x_offset - offset.x + - self.write_driver_array_position.x), + height=drc["minwidth_metal1"]) + + # Connecting msf_data_in VDD + for offset in self.msf_data_in.vdd_positions: + self.add_rect(layer="metal1", + offset=(self.ms_flop_data_in_offset + offset + -vector(0, 0.5 * drc["minwidth_metal1"])), + width=self.right_vdd_x_offset \ + - (self.ms_flop_data_in_offset.x + offset.x), + height=drc["minwidth_metal1"]) + + # Connecting tri_gate VDD + for offset in self.tri_gate_array.vdd_positions: + self.add_rect(layer="metal1", + offset=(self.tri_gate_array_offset + offset.scale(1,-1) + - vector(0, 0.5 * drc["minwidth_metal1"])), + width=(self.right_vdd_x_offset - offset.x + - self.tri_gate_array_offset.x), + height=drc["minwidth_metal1"]) + + # LEFT HAND SIDE VDD RAIL CONNECTIONS + + # Connecting decoder VDD + for i, offset in enumerate(self.decoder.vdd_positions): + decoder_vdd_offset = self.decoder_position + offset + if(i % 2 == 0): + self.add_rect(layer="metal1", + offset=decoder_vdd_offset, + width=self.left_vdd_x_offset - decoder_vdd_offset.x, + height=drc["minwidth_metal1"]) + + # Connecting pre-decoder vdds + for offset in self.decoder.pre_decoder_vdd_positions: + preedecoder_vdd_offset = self.decoder_position + offset + self.add_rect(layer="metal1", + offset=[self.left_vdd_x_offset, + preedecoder_vdd_offset.y], + width=preedecoder_vdd_offset.x - self.left_vdd_x_offset, + height=drc["minwidth_metal1"]) + + # Connecting column_decoder vdd [Its the 2:4 decoder] + if(self.col_addr_size == 2): + col_vdd_offset = self.col_decoder_position + self.col_decoder.vdd_position + self.add_rect(layer="metal1", + offset=[self.left_vdd_x_offset, + col_vdd_offset.y], + width=col_vdd_offset.x - self.left_vdd_x_offset, + height=drc["minwidth_metal1"]) + + # Connecting address Flip-flop VDD + for offset in self.msf_address.vdd_positions: + ms_addres_gnd_y = (self.msf_address_offset.y - self.msf_address.width + - 0.5 * drc["minwidth_metal1"]) + y_offset = ms_addres_gnd_y - 2.5*drc["minwidth_metal1"] + vdd_connection = vector(self.left_vdd_x_offset, y_offset) + mid1 = vdd_connection - vector(0, 0.5 * drc["minwidth_metal1"]) + mid2 = vector(self.msf_address_offset.x + offset.y, + mid1.y) + mid3 = vector(mid2.x, ms_addres_gnd_y) + self.add_path(layer="metal1", + coordinates=[mid1, mid2, mid3], + width=drc["minwidth_metal1"], + offset = vdd_connection) + + # Connecting bank_select_and2_array vdd + if(self.num_banks > 1): + for i in range(self.number_of_control_lines): + if(i % 2): + self.add_rect(layer="metal1", + offset=[self.left_vdd_x_offset, + self.bank_select_or_position.y + + i * self.inv.height + - 0.5 * drc["minwidth_metal1"]], + width=(self.bank_select_or_position.x + - self.left_vdd_x_offset), + height=drc["minwidth_metal1"]) + + def route_power_rail_gnd(self): + """ Routing of GND for all modules """ + # FIRST HORIZONTAL GND RAIL BETWEEN PRECHARGE AND BITCELL + yoffset = self.bitcell_array.height + 2*drc["minwidth_metal1"] + # Add gnd via + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.left_gnd_x_offset, yoffset], + size=(2,1)) + self.add_rect(layer="metal1", + offset=[self.left_gnd_x_offset, yoffset], + width=self.bitcell_array.width - self.left_gnd_x_offset, + height=drc["minwidth_metal1"]) + + for offset in self.bitcell_array.gnd_positions: + #print self.bitcell_array.gnd_positions + self.add_rect(layer="metal2", + offset=[offset.x - 0.5*drc["minwidth_metal2"], + self.bitcell_array.height], + width=drc["minwidth_metal2"], + height= yoffset + drc["minwidth_metal1"] \ + - self.bitcell_array.height) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[offset.x + drc["minwidth_metal2"], yoffset], + mirror="R90") + + # GND connectiontions for the left side of bitcell-array + self.add_rect(layer="metal2", + offset=[-drc["minwidth_metal2"], 0], + width=drc["minwidth_metal2"], + height=yoffset + drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[0, yoffset], + mirror="R90") + + # LEFT HAND SIDE GND RAIL CONNECTIONS + # Connections of Tri_gate GND to the left hand GND rail + + # This is only used to compute teh sizes below + gnd_contact = contact(layer_stack=("metal1", "via1", "metal2"), + dimensions=(2, 1)) + + x_off = (self.left_gnd_x_offset + self.power_rail_width + - gnd_contact.width) + y_off = (self.tri_gate_array_offset.y - self.tri_gate_array.height + - drc["minwidth_metal1"]) + tri_gate_gnd_offset = vector(x_off, y_off) + self.add_rect(layer="metal1", + offset=tri_gate_gnd_offset, + width=(self.tri_gate_array_offset.x + + self.tri_gate_array.width + - tri_gate_gnd_offset.x), + height=drc["minwidth_metal1"]) + # Add gnd via + self.add_via(layers=("metal1", "via1", "metal2"), + offset=tri_gate_gnd_offset, + size=(2,1)) + + for offset in self.tri_gate_array.gnd_positions: + tri_gate_gnd_position = vector(self.tri_gate_array_offset.x + offset.x, + tri_gate_gnd_offset.y) + offset = tri_gate_gnd_position - vector(0.5 * self.m1m2_via.width, 0) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset) + + # Connecting decoder GND + for i,offset in enumerate(self.wordline_driver.gnd_positions): + wordline_driver_gnd_offset = self.wordline_driver_position + offset + even_row = (i % 2 == 0 and i != 0) + last_row = (i == self.num_rows - 1) + if even_row or last_row: + if even_row: + correct = vector(0,0) + # Connection of the last GND rail [The top most gnd of decoder] + if last_row: + correct = vector(0, drc["minwidth_metal1"]) + self.add_rect(layer="metal2", + offset=wordline_driver_gnd_offset - correct, + width=(self.left_gnd_x_offset + - wordline_driver_gnd_offset.x), + height=drc["minwidth_metal2"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.wordline_driver_position.x + + self.wordline_driver.width + + 0.5*drc["minwidth_metal2"], + wordline_driver_gnd_offset.y + - correct.y], + mirror="R90") + + # Connecting Pre-decoder gnd rail + for i in range(len(self.decoder.pre_decoder_gnd_positions)): + offset = self.decoder.pre_decoder_gnd_positions[i] + preedecoder_gnd_offset = self.decoder_position + offset + self.add_rect(layer="metal1", + offset=preedecoder_gnd_offset, + width=self.left_gnd_x_offset - preedecoder_gnd_offset.x, + height=drc["minwidth_metal1"]) + # Add gnd via + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.left_gnd_x_offset, + preedecoder_gnd_offset.y + drc["minwidth_metal1"]], + mirror="MX", + size=(2,1)) + + # Connecting column_decoder gnd [Its the 2:4 decoder] + if(self.col_addr_size == 2): + col_gnd_offset = self.col_decoder_position + self.col_decoder.gnd_position + self.add_rect(layer="metal1", + offset=col_gnd_offset, + width=self.left_gnd_x_offset - col_gnd_offset.x, + height=drc["minwidth_metal1"]) + # Add gnd via + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.left_gnd_x_offset, col_gnd_offset.y], + size=(2,1)) + + # Connecting address FF GND + for offset in self.msf_address.gnd_positions: + correct = vector(self.msf_address.height, + - offset.x - 0.5*drc["minwidth_metal1"]) + ms_addres_gnd_offset = self.msf_address_offset + correct + self.add_via(layers=("metal1", "via1", "metal2"), + offset=(ms_addres_gnd_offset + + vector(drc["minwidth_metal1"], 0)), + mirror="R90") + self.add_rect(layer="metal1", + offset=ms_addres_gnd_offset, + width=self.left_gnd_x_offset - ms_addres_gnd_offset.x, + height=drc["minwidth_metal1"]) + # Add gnd via + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.left_gnd_x_offset, + ms_addres_gnd_offset.y], + size=(2,1)) + + # Connecting bank_select_or2_array gnd + if(self.num_banks > 1): + self.bank_select_inv_position + self.add_rect(layer="metal1", + offset=(self.bank_select_inv_position + + self.inv4x.gnd_position), + width=(self.bank_select_or_position.x + - self.bank_select_inv_position.x), + height=drc["minwidth_metal1"]) + + x_offset = (self.bank_select_or_position.x + + self.NOR2.width + self.inv4x.width) + for i in range(self.number_of_control_lines): + if(i % 2 == 0): + y_offset = self.bank_select_or_position.y + i*self.inv.height \ + - 0.5*drc["minwidth_metal1"] + #both M1 & M2 are horizontal, cannot be replaced with wire + self.add_rect(layer="metal1", + offset=[x_offset, y_offset], + width=drc["minwidth_metal1"], + height=drc["minwidth_metal1"]) + self.add_rect(layer="metal2", + offset=[x_offset, y_offset], + width=self.left_gnd_x_offset \ + - x_offset + self.power_rail_width, + height=drc["minwidth_metal2"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[x_offset + drc["minwidth_metal1"], + y_offset], + mirror="R90") diff --git a/compiler/bitcell.py b/compiler/bitcell.py new file mode 100644 index 00000000..56509875 --- /dev/null +++ b/compiler/bitcell.py @@ -0,0 +1,23 @@ +import design +import debug +import utils +from tech import GDS,layer + +class bitcell(design.design): + """ + A single bit cell (6T, 8T, etc.) This module implements the + single memory cell used in the design. It is a hand-made cell, so + the layout and netlist should be available in the technology + library. + """ + + pins = ["BL", "BR", "WL", "vdd", "gnd"] + chars = utils.auto_measure_libcell(pins, "cell_6t", GDS["unit"], layer["boundary"]) + + def __init__(self, name="cell_6t"): + design.design.__init__(self, name) + debug.info(2, "Create bitcell object") + + self.width = bitcell.chars["width"] + self.height = bitcell.chars["height"] + diff --git a/compiler/bitcell_array.py b/compiler/bitcell_array.py new file mode 100644 index 00000000..c6a2265a --- /dev/null +++ b/compiler/bitcell_array.py @@ -0,0 +1,142 @@ +import debug +import design +from vector import vector +from globals import OPTS + + +class bitcell_array(design.design): + """ + Creates a rows x cols array of memory cells. Assumes bit-lines + and word line is connected by abutment. + Connects the word lines and bit lines. + """ + + def __init__(self, name, cols, rows): + design.design.__init__(self, name) + debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) + + + self.column_size = cols + self.row_size = rows + + c = reload(__import__(OPTS.config.bitcell)) + self.mod_bitcell = getattr(c, OPTS.config.bitcell) + self.bitcell_chars = self.mod_bitcell.chars + + self.add_pins() + self.create_layout() + self.add_labels() + self.DRC_LVS() + + def add_pins(self): + for col in range(self.column_size): + self.add_pin("bl[{0}]".format(col)) + self.add_pin("br[{0}]".format(col)) + for row in range(self.row_size): + self.add_pin("wl[{0}]".format(row)) + self.add_pin("vdd") + self.add_pin("gnd") + + def create_layout(self): + self.create_cell() + self.setup_layout_constants() + self.add_cells() + self.offset_all_coordinates() + + def setup_layout_constants(self): + self.vdd_positions = [] + self.gnd_positions = [] + self.BL_positions = [] + self.BR_positions = [] + self.WL_positions = [] + self.height = self.row_size * self.cell.height + self.width = self.column_size * self.cell.width + + def create_cell(self): + self.cell = self.mod_bitcell() + self.add_mod(self.cell) + + def add_cells(self): + xoffset = 0.0 + for col in range(self.column_size): + yoffset = 0.0 + for row in range(self.row_size): + name = "bit_r{0}_c{1}".format(row, col) + + if row % 2: + tempy = yoffset + self.cell.height + dir_key = "MX" + else: + tempy = yoffset + dir_key = "R0" + + if OPTS.trim_noncritical == True: + if row == self.row_size - 1: + self.add_inst(name=name, + mod=self.cell, + offset=[xoffset, tempy], + mirror=dir_key) + self.connect_inst(["bl[{0}]".format(col), + "br[{0}]".format(col), + "wl[{0}]".format(row), + "vdd", + "gnd"]) + else: + self.add_inst(name=name, + mod=self.cell, + offset=[xoffset, tempy], + mirror=dir_key) + self.connect_inst(["bl[{0}]".format(col), + "br[{0}]".format(col), + "wl[{0}]".format(row), + "vdd", + "gnd"]) + yoffset += self.cell.height + xoffset += self.cell.width + + def add_labels(self): + offset = vector(0.0, 0.0) + for col in range(self.column_size): + offset.y = 0.0 + self.add_label(text="bl[{0}]".format(col), + layer="metal2", + offset=offset + vector(self.bitcell_chars["BL"][0],0)) + self.add_label(text="br[{0}]".format(col), + layer="metal2", + offset=offset + vector(self.bitcell_chars["BR"][0],0)) + self.BL_positions.append(offset + vector(self.bitcell_chars["BL"][0],0)) + self.BR_positions.append(offset + vector(self.bitcell_chars["BR"][0],0)) + + # gnd offset is 0 in our cell, but it be non-zero + self.add_label(text="gnd", + layer="metal2", + offset=offset + vector(self.bitcell_chars["gnd"][0],0)) + self.gnd_positions.append(offset + vector(self.bitcell_chars["gnd"][0],0)) + + for row in range(self.row_size): + # only add row labels on the left most column + if col == 0: + # flipped row + if row % 2: + base_offset = offset + vector(0, self.cell.height) + vdd_offset = base_offset - vector(0,self.bitcell_chars["vdd"][1]) + wl_offset = base_offset - vector(0,self.bitcell_chars["WL"][1]) + # unflipped row + else: + vdd_offset = offset + vector(0,self.bitcell_chars["vdd"][1]) + wl_offset = offset + vector(0,self.bitcell_chars["WL"][1]) + # add vdd label and offset + self.add_label(text="vdd", + layer="metal1", + offset=vdd_offset) + self.vdd_positions.append(vdd_offset) + # add gnd label and offset + self.add_label(text="wl[{0}]".format(row), + layer="metal1", + offset=wl_offset) + self.WL_positions.append(wl_offset) + + # increments to the next row height + offset.y += self.cell.height + # increments to the next column width + offset.x += self.cell.width diff --git a/compiler/calibre.py b/compiler/calibre.py new file mode 100644 index 00000000..a028c6ce --- /dev/null +++ b/compiler/calibre.py @@ -0,0 +1,325 @@ +""" +This is a DRC/LVS interface for calibre. It implements completely +independently two functions: run_drc and run_lvs, that perform these +functions in batch mode and will return true/false if the result +passes. All of the setup (the rules, temp dirs, etc.) should be +contained in this file. Replacing with another DRC/LVS tool involves +rewriting this code to work properly. Porting to a new technology in +Calibre means pointing the code to the proper DRC and LVS rule files. + +A calibre DRC runset file contains, at the minimum, the following information: + +*drcRulesFile: /mada/software/techfiles/FreePDK45/ncsu_basekit/techfile/calibre/calibreDRC.rul +*drcRunDir: . +*drcLayoutPaths: ./cell_6t.gds +*drcLayoutPrimary: cell_6t +*drcLayoutSystem: GDSII +*drcResultsformat: ASCII +*drcResultsFile: cell_6t.drc.results +*drcSummaryFile: cell_6t.drc.summary +*cmnFDILayerMapFile: ./layer.map +*cmnFDIUseLayerMap: 1 + +This can be executed in "batch" mode with the following command: + +calibre -gui -drc example_drc_runset -batch + +To open the results, you can do this: + +calibre -rve cell_6t.drc.results + +*lvsRulesFile: /mada/software/techfiles/FreePDK45/ncsu_basekit/techfile/calibre/calibreLVS.rul +*lvsRunDir: . +*lvsLayoutPaths: ./cell_6t.gds +*lvsLayoutPrimary: cell_6t +*lvsSourcePath: ./cell_6t.sp +*lvsSourcePrimary: cell_6t +*lvsSourceSystem: SPICE +*lvsSpiceFile: extracted.sp +*lvsPowerNames: vdd +*lvsGroundNames: vss +*lvsIgnorePorts: 1 +*lvsERCDatabase: cell_6t.erc.results +*lvsERCSummaryFile: cell_6t.erc.summary +*lvsReportFile: cell_6t.lvs.report +*lvsMaskDBFile: cell_6t.maskdb +*cmnFDILayerMapFile: ./layer.map +*cmnFDIUseLayerMap: 1 + +To run and see results: + +calibre -gui -lvs example_lvs_runset -batch +more cell_6t.lvs.report +""" + + +import os +import re +import time +import debug +import globals +import subprocess + + +def run_drc(name, gds_name): + """Run DRC check on a given top-level name which is + implemented in gds_name.""" + OPTS = globals.get_opts() + + # the runset file contains all the options to run calibre + from tech import drc + drc_rules = drc["drc_rules"] + + drc_runset = { + 'drcRulesFile': drc_rules, + 'drcRunDir': OPTS.openram_temp, + 'drcLayoutPaths': gds_name, + 'drcLayoutPrimary': name, + 'drcLayoutSystem': 'GDSII', + 'drcResultsformat': 'ASCII', + 'drcResultsFile': OPTS.openram_temp + name + ".drc.results", + 'drcSummaryFile': OPTS.openram_temp + name + ".drc.summary", + 'cmnFDILayerMapFile': drc["layer_map"], + 'cmnFDIUseLayerMap': 1 + } + + # write the runset file + f = open(OPTS.openram_temp + "drc_runset", "w") + for k in sorted(drc_runset.iterkeys()): + f.write("*%s: %s\n" % (k, drc_runset[k])) + f.close() + + # run drc + os.chdir(OPTS.openram_temp) + errfile = "%s%s.drc.err" % (OPTS.openram_temp, name) + outfile = "%s%s.drc.out" % (OPTS.openram_temp, name) + + cmd = "{0} -gui -drc {1}drc_runset -batch 2> {2} 1> {3}".format( + OPTS.calibre_exe, OPTS.openram_temp, errfile, outfile) + debug.info(1, cmd) + os.system(cmd) + + # check the result for these lines in the summary: + # TOTAL Original Layer Geometries: 106 (157) + # TOTAL DRC RuleChecks Executed: 156 + # TOTAL DRC Results Generated: 0 (0) + f = open(drc_runset['drcSummaryFile'], "r") + results = f.readlines() + f.close() + # those lines should be the last 3 + results = results[-3:] + geometries = int(re.split("\W+", results[0])[5]) + rulechecks = int(re.split("\W+", results[1])[4]) + errors = int(re.split("\W+", results[2])[5]) + + # always display this summary + if errors > 0: + debug.error("%-25s\tGeometries: %d\tChecks: %d\tErrors: %d" % + (name, geometries, rulechecks, errors)) + else: + debug.info(1, "%-25s\tGeometries: %d\tChecks: %d\tErrors: %d" % + (name, geometries, rulechecks, errors)) + + return errors + + +def run_lvs(name, gds_name, sp_name): + """Run LVS check on a given top-level name which is + implemented in gds_name and sp_name. """ + OPTS = globals.get_opts() + from tech import drc + lvs_rules = drc["lvs_rules"] + lvs_runset = { + 'lvsRulesFile': lvs_rules, + 'lvsRunDir': OPTS.openram_temp, + 'lvsLayoutPaths': gds_name, + 'lvsLayoutPrimary': name, + 'lvsSourcePath': sp_name, + 'lvsSourcePrimary': name, + 'lvsSourceSystem': 'SPICE', + 'lvsSpiceFile': OPTS.openram_temp + "extracted.sp", + 'lvsPowerNames': 'vdd', + 'lvsGroundNames': 'gnd', + 'lvsIncludeSVRFCmds': 1, + 'lvsSVRFCmds': '{VIRTUAL CONNECT NAME VDD? GND? ?}', + 'lvsIgnorePorts': 1, + 'lvsERCDatabase': OPTS.openram_temp + name + ".erc.results", + 'lvsERCSummaryFile': OPTS.openram_temp + name + ".erc.summary", + 'lvsReportFile': OPTS.openram_temp + name + ".lvs.report", + 'lvsMaskDBFile': OPTS.openram_temp + name + ".maskdb", + 'cmnFDILayerMapFile': drc["layer_map"], + 'cmnFDIUseLayerMap': 1, + 'cmnVConnectNames': 'vdd, gnd', + #'cmnVConnectNamesState' : 'ALL', #connects all nets with the same name + } + + # write the runset file + f = open(OPTS.openram_temp + "lvs_runset", "w") + for k in sorted(lvs_runset.iterkeys()): + f.write("*%s: %s\n" % (k, lvs_runset[k])) + f.close() + + # run LVS + os.chdir(OPTS.openram_temp) + errfile = "%s%s.lvs.err" % (OPTS.openram_temp, name) + outfile = "%s%s.lvs.out" % (OPTS.openram_temp, name) + + cmd = "calibre -gui -lvs %slvs_runset -batch 2> %s 1> %s" % ( + OPTS.openram_temp, errfile, outfile) + debug.info(2, cmd) + os.system(cmd) + + # check the result for these lines in the summary: + f = open(lvs_runset['lvsReportFile'], "r") + results = f.readlines() + f.close() + + # NOT COMPARED + # CORRECT + # INCORRECT + test = re.compile("# CORRECT #") + correct = filter(test.search, results) + test = re.compile("NOT COMPARED") + notcompared = filter(test.search, results) + test = re.compile("# INCORRECT #") + incorrect = filter(test.search, results) + + # Errors begin with "Error:" + test = re.compile("\s+Error:") + errors = filter(test.search, results) + for e in errors: + debug.error(e.strip("\n")) + + summary_errors = len(notcompared) + len(incorrect) + len(errors) + + # also check the extraction summary file + f = open(lvs_runset['lvsReportFile'] + ".ext", "r") + results = f.readlines() + f.close() + + test = re.compile("ERROR:") + exterrors = filter(test.search, results) + for e in exterrors: + debug.error(e.strip("\n")) + + test = re.compile("WARNING:") + extwarnings = filter(test.search, results) + for e in extwarnings: + debug.error(e.strip("\n")) + + ext_errors = len(exterrors) + len(extwarnings) + + # also check the output file + f = open(outfile, "r") + results = f.readlines() + f.close() + + # Errors begin with "ERROR:" + test = re.compile("ERROR:") + stdouterrors = filter(test.search, results) + for e in stdouterrors: + debug.error(e.strip("\n")) + + out_errors = len(stdouterrors) + + return summary_errors + out_errors + ext_errors + + +def run_pex(name, gds_name, sp_name, output=None): + """Run pex on a given top-level name which is + implemented in gds_name and sp_name. """ + OPTS = globals.get_opts() + from tech import drc + if output == None: + output = name + ".pex.netlist" + + # check if lvs report has been done + # if not run drc and lvs + if not os.path.isfile(name + ".lvs.report"): + run_drc(name, gds_name) + run_lvs(name, gds_name, sp_name) + + pex_rules = drc["xrc_rules"] + pex_runset = { + 'pexRulesFile': pex_rules, + 'pexRunDir': OPTS.openram_temp, + 'pexLayoutPaths': gds_name, + 'pexLayoutPrimary': name, + #'pexSourcePath' : OPTS.openram_temp+"extracted.sp", + 'pexSourcePath': sp_name, + 'pexSourcePrimary': name, + 'pexReportFile': name + ".lvs.report", + 'pexPexNetlistFile': output, + 'pexPexReportFile': name + ".pex.report", + 'pexMaskDBFile': name + ".maskdb", + 'cmnFDIDEFLayoutPath': name + ".def", + } + + # write the runset file + f = open(OPTS.openram_temp + "pex_runset", "w") + for k in sorted(pex_runset.iterkeys()): + f.write("*{0}: {1}\n".format(k, pex_runset[k])) + f.close() + + # run pex + os.chdir(OPTS.openram_temp) + errfile = "{0}{1}.pex.err".format(OPTS.openram_temp, name) + outfile = "{0}{1}.pex.out".format(OPTS.openram_temp, name) + + cmd = "{0} -gui -pex {1}pex_runset -batch 2> {2} 1> {3}".format(OPTS.calibre_exe, + OPTS.openram_temp, + errfile, + outfile) + debug.info(2, cmd) + os.system(cmd) + + # also check the output file + f = open(outfile, "r") + results = f.readlines() + f.close() + + # Errors begin with "ERROR:" + test = re.compile("ERROR:") + stdouterrors = filter(test.search, results) + for e in stdouterrors: + debug.error(e.strip("\n")) + + out_errors = len(stdouterrors) + + assert(os.path.isfile(output)) + correct_port(name, output, sp_name) + + return out_errors + + +def correct_port(name, output_file_name, ref_file_name): + pex_file = open(output_file_name, "r") + contents = pex_file.read() + # locate the start of circuit definition line + match = re.search(".subckt " + str(name) + ".*", contents) + match_index_start = match.start() + pex_file.seek(match_index_start) + rest_text = pex_file.read() + # locate the end of circuit definition line + match = re.search("\* \n", rest_text) + match_index_end = match.start() + # store the unchanged part of pex file in memory + pex_file.seek(0) + part1 = pex_file.read(match_index_start) + pex_file.seek(match_index_start + match_index_end) + part2 = pex_file.read() + pex_file.close() + + # obatin the correct definition line from the original spice file + sp_file = open(ref_file_name, "r") + contents = sp_file.read() + circuit_title = re.search(".SUBCKT " + str(name) + ".*\n", contents) + circuit_title = circuit_title.group() + sp_file.close() + + # write the new pex file with info in the memory + output_file = open(output_file_name, "w") + output_file.write(part1) + output_file.write(circuit_title) + output_file.write(part2) + output_file.close() diff --git a/compiler/characterizer/charutils.py b/compiler/characterizer/charutils.py new file mode 100644 index 00000000..8f5952e7 --- /dev/null +++ b/compiler/characterizer/charutils.py @@ -0,0 +1,76 @@ +import globals +import re +import debug + +OPTS = globals.get_opts() + +# 0.1% is the relative tolerance for convergence +error_tolerance = 0.001 + +# times are in ns, so this is how many digits of precision +# 3 digits = 1ps +# 4 digits = 0.1ps +# etc. +time_precision = 3 +# voltages are in volts +# 3 digits = 1mv +# 4 digits = 0.1mv +# 5 digits = 0.01mv +# 6 digits = 1uv +# etc +voltage_precision = 5 + +def relative_compare(value1,value2): + """ This is used to compare relative values for convergence. """ + return (abs(value1 - value2) / max(value1,value2) <= error_tolerance) + + +def parse_output(filename, key): + """Parses a hspice output.lis file for a key value""" + f = open("{0}/{1}.lis".format(OPTS.openram_temp, filename), "r") + contents = f.read() + val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents) + if val != None: + debug.info(3, "Key = " + key + " Val = " + val.group(1)) + return val.group(1) + else: + return "Failed" + +def round_time(time): + return round(time,time_precision) + +def round_voltage(voltage): + return round(voltage,voltage_precision) + +def convert_to_float(number): + """Converts a string into a (float) number; also converts units(m,u,n,p)""" + if number == "Failed": + return False + + # start out with a binary value + float_value = False + try: + # checks if string is a float without letter units + float_value = float(number) + except ValueError: + # see if it is in scientific notation + unit = re.search(r"(-?\d+\.?\d*)e(\-?\+?\d+)", number) + if unit != None: + float_value=float(unit.group(1)) * (10 ^ float(unit.group(2))) + + # see if it is in spice notation + unit = re.search(r"(-?\d+\.?\d*)(m?u?n?p?f?)", number) + if unit != None: + float_value = { + 'm': lambda x: x * 0.001, # milli + 'u': lambda x: x * 0.000001, # micro + 'n': lambda x: x * 0.000000001, # nano + 'p': lambda x: x * 0.000000000001, # pico + 'f': lambda x: x * 0.000000000000001 # femto + }[unit.group(2)](float(unit.group(1))) + + # if we weren't able to convert it to a float then error out + if not type(float_value)==float: + debug.error("Invalid number: {0}".format(number),1) + + return float_value diff --git a/compiler/characterizer/delay.py b/compiler/characterizer/delay.py new file mode 100644 index 00000000..e140ab24 --- /dev/null +++ b/compiler/characterizer/delay.py @@ -0,0 +1,444 @@ +import sys +import re +import globals +import debug +import tech +import math +import stimuli +import charutils as ch + +OPTS = globals.get_opts() + +class delay(): + """ + Functions to measure the delay of the SRAM at a given address and + data bit. + """ + + def __init__(self,sram,spfile): + self.name = sram.name + self.num_words = sram.num_words + self.word_size = sram.word_size + self.addr_size = sram.addr_size + self.sram_sp_file = spfile + + + def check_arguments(self): + """Checks if arguments given for write_stimulus() meets requirements""" + try: + int(self.probe_address, 2) + except ValueError: + debug.error("Probe Address is not of binary form: {0}".format(self.probe_address),1) + + if len(self.probe_address) != self.addr_size: + debug.error("Probe Address's number of bits does not correspond to given SRAM",1) + + if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0: + debug.error("Given probe_data is not an integer to specify a data bit",1) + + + def write_stimulus(self, feasible_period, target_period, data_value): + """Creates a stimulus file for simulations to probe a certain bitcell, given an address and data-position of the data-word + (probe-address form: '111010000' LSB=0, MSB=1) + (probe_data form: number corresponding to the bit position of data-bus, begins with position 0) + """ + self.check_arguments() + + # obtains list of time-points for each rising clk edge + self.obtain_cycle_times(slow_period=feasible_period, + fast_period=target_period) + + # creates and opens stimulus file for writing + temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) + self.sf = open(temp_stim, "w") + self.sf.write("* Stimulus data value of {0} for target period of {1}n\n".format(data_value, target_period)) + self.sf.write("\n") + + # include files in stimulus file + model_list = tech.spice["fet_models"] + [self.sram_sp_file] + stimuli.write_include(stim_file=self.sf, models=model_list) + self.sf.write("\n") + + # add vdd/gnd statements + self.sf.write("* Global Power Supplies\n") + stimuli.write_supply(stim_file=self.sf, + vdd_name=tech.spice["vdd_name"], + gnd_name=tech.spice["gnd_name"], + vdd_voltage=tech.spice["supply_voltage"], + gnd_voltage=tech.spice["gnd_voltage"]) + self.sf.write("\n") + + # instantiate the sram + self.sf.write("* Instantiation of the SRAM\n") + stimuli.inst_sram(stim_file=self.sf, + abits=self.addr_size, + dbits=self.word_size, + sram_name=self.name) + self.sf.write("\n") + + # create a buffer and an inverter + self.sf.write("* Buffers and inverter Initialization\n") + # FIXME: We should replace the clock buffer with the same + # 2x buffer for control signals. This needs the buffer to be + # added to the control logic though. + stimuli.create_buffer(stim_file=self.sf, + buffer_name="clk1_buffer", + size=[1, 4]) + self.sf.write("\n") + stimuli.create_buffer(stim_file=self.sf, + buffer_name="clk2_buffer", + size=[8, 16]) + self.sf.write("\n") + + stimuli.create_buffer(stim_file=self.sf, + buffer_name="buffer", + size=[1, 2]) + self.sf.write("\n") + + stimuli.create_inverter(stim_file=self.sf) + self.sf.write("\n") + + # add a buffer for each signal and an inverter for WEb + signal_list = [] + for i in range(self.word_size): + signal_list.append("D[{0}]".format(i)) + for j in range(self.addr_size): + signal_list.append("A[{0}]".format(j)) + for k in tech.spice["control_signals"]: + signal_list.append(k) + self.sf.write("*Buffers for each generated signal and Inv for WEb\n") + stimuli.add_buffer(stim_file=self.sf, + buffer_name="buffer", + signal_list=signal_list) + stimuli.add_buffer(stim_file=self.sf, + buffer_name="clk1_buffer", + signal_list=["clk"]) + stimuli.add_buffer(stim_file=self.sf, + buffer_name="clk2_buffer", + signal_list=["clk_buf"]) + stimuli.add_buffer(stim_file=self.sf, + buffer_name="buffer", + signal_list=["WEb_trans"]) + stimuli.add_inverter(stim_file=self.sf, + signal_list=["WEb_trans"]) + self.sf.write("\n") + + # add access transistors for data-bus + self.sf.write("* Transmission Gates for data-bus\n") + stimuli.add_accesstx(stim_file=self.sf, dbits=self.word_size) + self.sf.write("\n") + + # generate data and addr signals + self.sf.write("*Generation of data and address signals\n") + if data_value == tech.spice["supply_voltage"]: + v_val = tech.spice["gnd_voltage"] + else: + v_val = tech.spice["supply_voltage"] + for i in range(self.word_size): + if i == self.probe_data: + stimuli.gen_data_pwl(stim_file=self.sf, + key_times=self.cycle_times, + sig_name="D[{0}]".format(i), + data_value=data_value, + feasible_period=feasible_period, + target_period=target_period, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + else: + stimuli.gen_constant(stim_file=self.sf, + sig_name="D[{0}]".format(i), + v_ref=tech.spice["gnd_voltage"], + v_val=v_val) + + stimuli.gen_addr_pwl(stim_file=self.sf, + key_times=self.cycle_times, + addr=self.probe_address, + feasible_period=feasible_period, + target_period=target_period, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + self.sf.write("\n") + + # generate control signals + self.sf.write("*Generation of control signals\n") + # CSb + (x_list, y_list) = stimuli.gen_csb_pwl(key_times=self.cycle_times, + feasible_period=feasible_period, + target_period=target_period, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + stimuli.gen_pwl(stim_file=self.sf, + sig_name="CSb", + x_list=x_list, + y_list=y_list) + # WEb + (x_list, y_list) = stimuli.gen_web_pwl(key_times=self.cycle_times, + feasible_period=feasible_period, + target_period=target_period, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + stimuli.gen_pwl(stim_file=self.sf, + sig_name="WEb", + x_list=x_list, + y_list=y_list) + # OEb + (x_list, y_list) = stimuli.gen_oeb_pwl(key_times=self.cycle_times, + feasible_period=feasible_period, + target_period=target_period, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + stimuli.gen_pwl(stim_file=self.sf, + sig_name="OEb", + x_list=x_list, + y_list=y_list) + # WEb_transmission_gate + (x_list, y_list) = stimuli.gen_web_trans_pwl(key_times=self.cycle_times, + feasible_period=feasible_period, + target_period=target_period, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + stimuli.gen_pwl(stim_file=self.sf, + sig_name="WEb_trans", + x_list=x_list, + y_list=y_list) + self.sf.write("\n") + + self.write_clock() + + self.write_measures(data_value) + + self.write_control() + + self.sf.close() + + def write_clock(self): + # generate clk PWL based on the clock periods + self.sf.write("* Generation of global clock signal\n") + stimuli.gen_clk_pwl(stim_file=self.sf, + cycle_times=self.cycle_times, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + self.sf.write("\n") + + def write_measures(self, data_value): + # meas statement for delay and power measurements + self.sf.write("* Measure statements for delay and power\n") + + # add measure statments for delay + trig_name = tech.spice["clk"] + "_buf_buf" + targ_name = "{0}".format("DATA[{0}]".format(self.probe_data)) + trig_val = targ_val = 0.5 * tech.spice["supply_voltage"] + trig_dir = self.read_cycle + targ_dir = "RISE" if data_value == tech.spice["supply_voltage"] else "FALL" + td = self.cycle_times[self.clear_bus_cycle] + stimuli.gen_meas_delay(stim_file=self.sf, + meas_name="DELAY", + trig_name=trig_name, + targ_name=targ_name, + trig_val=trig_val, + targ_val=targ_val, + trig_dir=trig_dir, + targ_dir=targ_dir, + td=td) + + # add measure statements for power + t_initial = self.cycle_times[self.write_cycle] + t_final = self.cycle_times[self.write_cycle+1] + stimuli.gen_meas_power(stim_file=self.sf, + meas_name="POWER_WRITE", + t_initial=t_initial, + t_final=t_final) + + t_initial = self.cycle_times[self.read_cycle] + t_final = self.cycle_times[self.read_cycle+1] + stimuli.gen_meas_power(stim_file=self.sf, + meas_name="POWER_READ", + t_initial=t_initial, + t_final=t_final) + self.sf.write("\n") + + + def write_control(self): + # run until the last cycle time + end_time = self.cycle_times[-1] + self.sf.write(".TRAN 5p {0}n\n".format(end_time)) + + if OPTS.spice_version == "hspice": + # create plots for all signals + self.sf.write(".OPTIONS POST=1 PROBE\n") + self.sf.write(".probe V(*)\n") + # end the stimulus file + self.sf.write(".end\n") + self.sf.close() + else: + self.sf.write(".control\n") + self.sf.write("run\n") + self.sf.write("quit\n") + self.sf.write(".endc\n") + self.sf.write(".end\n") + + + + def find_feasible_period(self,initial_period): + """Uses an initial period and finds a feasible period before we + run the binary search algorithm to find min period. We check if + the given clock period is valid and if it's not, we continue to + double the period until we find a valid period to use as a + starting point. """ + + feasible_period = initial_period + time_out = 8 + while True: + debug.info(1, "Finding feasible period: {0}ns".format(feasible_period)) + time_out -= 1 + + if (time_out <= 0): + debug.error("Timed out, could not find a feasible period.",2) + + (success, delay_out)=self.try_period(feasible_period,feasible_period,tech.spice["supply_voltage"]) + if not success: + feasible_period = 2 * feasible_period + continue + + (success, delay_out)=self.try_period(feasible_period,feasible_period,tech.spice["gnd_voltage"]) + if not success: + feasible_period = 2 * feasible_period + continue + + debug.info(1, "Starting Binary Search Algorithm with feasible_period: {0}ns".format(feasible_period)) + return feasible_period + + + def try_period(self, feasible_period, target_period, data_value): + """ This tries to simulate a period and checks if the result + works. If so, it returns True. If not, it it doubles the + period and returns False.""" + + # Checking from not data_value to data_value + self.write_stimulus(feasible_period, target_period, data_value) + stimuli.run_sim() + delay_value = ch.convert_to_float(ch.parse_output("timing", "delay")) + + # if it failed or the read was longer than a period + if type(delay_value)!=float or delay_value*1e9>target_period: + debug.info(2,"Infeasible period " + str(target_period) + " delay " + str(delay_value*1e9) + "ns") + return (False, "NA") + else: + debug.info(2,"Feasible period " + str(feasible_period) \ + + ", target period " + str(target_period) \ + + ", read/write of " + str(data_value) \ + + ", delay=" + str(delay_value*1e9) + "ns") + #key=raw_input("press return to continue") + + return (True, delay_value*1e9) + + + def find_min_period(self,data_value): + """Creates a spice test and instantiates a single SRAM for + testing. Returns a tuple of the lowest period and its + clk-to-q delay for the specified data_value.""" + + # Find a valid and feasible period before starting the binary search + # We just try 2.0ns here, but any value would work albeit slower. + feasible_period = self.find_feasible_period(tech.spice["feasible_period"]) + previous_period = ub_period = feasible_period + lb_period = 0.0 + + # Binary search algorithm to find the min period (max frequency) of design + time_out = 25 + while True: + time_out -= 1 + if (time_out <= 0): + debug.error("Timed out, could not converge on minimum period.",2) + + target_period = 0.5 * (ub_period + lb_period) + debug.info(1, "MinPeriod Search: {0}ns (ub: {1} lb: {2})".format(target_period, + ub_period, + lb_period)) + + (success, delay_out) = self.try_period(feasible_period, target_period, data_value) + if success: + if ch.relative_compare(ub_period, target_period): + # use the two values to compare, but only return the ub since it is guaranteed feasible + (success, delay_out) = self.try_period(feasible_period, ub_period, data_value) + return (ub_period, delay_out) + fail_flag = False + ub_period = target_period + else: + lb_period = target_period + + + self.error("Should not reach here.",-1) + return (target_period, delay_out) + + def set_probe(self,probe_address, probe_data): + """ Probe address and data can be set separately to utilize other + functions in this characterizer besides analyze.""" + self.probe_address = probe_address + self.probe_data = probe_data + + def analyze(self,probe_address, probe_data): + """main function to calculate the min period for a low_to_high + transistion and a high_to_low transistion returns a dictionary + that contains all both the min period and associated delays + Dictionary Keys: min_period1, delay1, min_period0, delay0 + """ + self.set_probe(probe_address, probe_data) + + (min_period1, delay1) = self.find_min_period(tech.spice["supply_voltage"]) + if (min_period1 == None) or (delay1 == None): + return None + debug.info(1, "Min Period for low_to_high transistion: {0}n with a delay of {1}".format(min_period1, delay1)) + (min_period0, delay0) = self.find_min_period(tech.spice["gnd_voltage"]) + if (min_period0 == None) or (delay0 == None): + return None + debug.info(1, "Min Period for high_to_low transistion: {0}n with a delay of {1}".format(min_period0, delay0)) + data = {"min_period1": min_period1, # period in ns + "delay1": delay1, # delay in s + "min_period0": min_period0, + "delay0": delay0 + } + return data + + + def obtain_cycle_times(self, slow_period, fast_period): + """Returns a list of key time-points [ns] of the waveform (each rising edge) + of the cycles to do a timing evaluation. The last time is the end of the simulation + and does not need a rising edge.""" + + # idle: half cycle, no operation + t_current = 0.5 * slow_period + + # slow cycle1: W data 1 address 1111 to initialize cell to a value + self.cycle_times = [t_current] + self.init_cycle=1 + t_current += slow_period + + # slow cycle2: R data 1 address 1111 (to ensure cell was written to opposite data value) + self.cycle_times.append(t_current) + self.verify_init_cycle=2 + t_current += slow_period + + # fast cycle3: W data 0 address 1111 (to ensure a write of opposite value works) + self.cycle_times.append(t_current) + self.write_cycle=3 + t_current += fast_period + + # fast cycle4: W data 1 address 0000 (to invert the bus cap from prev write) + self.cycle_times.append(t_current) + self.clear_bus_cycle=4 + t_current += fast_period + + # fast cycle5: R data 0 address 1111 (to ensure that a read works and gets the value) + self.cycle_times.append(t_current) + self.read_cycle=5 + t_current += fast_period + + # slow cycle 6: wait a slow clock period to end the simulation + self.cycle_times.append(t_current) + self.wait_cycle=6 + t_current += slow_period + + # end time of simulation + self.cycle_times.append(t_current) + diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py new file mode 100644 index 00000000..feb20570 --- /dev/null +++ b/compiler/characterizer/lib.py @@ -0,0 +1,294 @@ +import os +import sys +import re +import globals +import debug +import tech +import math +import setup_hold +import delay +import charutils as ch + + +OPTS = globals.get_opts() + +class lib: + """ lib file generation.""" + + def __init__(self, libname, sram, spfile): + self.name = sram.name + self.num_words = sram.num_words + self.word_size = sram.word_size + self.addr_size = sram.addr_size + + self.sh = setup_hold.setup_hold() + self.d = delay.delay(sram, spfile) + + debug.info(1,"Writing to {0}".format(libname)) + self.lib = open(libname, "w") + + self.lib.write("library ({0}_lib)".format(self.name)) + self.lib.write("{\n") + self.lib.write(" delay_model : \"table_lookup\";\n") + + self.write_units() + self.write_defaults() + self.write_LUT() + + self.lib.write(" default_operating_conditions : TT; \n") + + self.write_bus() + + self.lib.write("cell ({0})".format(self.name)) + self.lib.write("{\n") + self.lib.write(" memory(){ \n") + self.lib.write(" type : ram;\n") + self.lib.write(" address_width : {0};\n".format(self.addr_size)) + self.lib.write(" word_width : {0};\n".format(self.word_size)) + self.lib.write(" }\n") + self.lib.write(" interface_timing : true;\n") + self.lib.write(" dont_use : true;\n") + self.lib.write(" map_only : true;\n") + self.lib.write(" dont_touch : true;\n") + self.lib.write(" area : {0};\n\n".format(sram.width * sram.height)) + + times = self.sh.analyze() + + for i in times.keys(): + times[i] = ch.round_time(times[i]) + + + probe_address = "1" * self.addr_size + probe_data = self.word_size - 1 + + data = self.d.analyze(probe_address, probe_data) + for i in data.keys(): + data[i] = ch.round_time(data[i]) + + + self.write_data_bus(data, times) + self.write_addr_bus(times) + self.write_control_pins(times) + self.write_clk(data) + + self.lib.close() + + + def write_units(self): + """ Adds default units for time, voltage, current,...""" + + self.lib.write(" time_unit : \"1ns\" ;\n") + self.lib.write(" voltage_unit : \"1v\" ;\n") + self.lib.write(" current_unit : \"1mA\" ;\n") + self.lib.write(" resistance_unit : \"1kohm\" ;\n") + self.lib.write(" capacitive_load_unit(1 ,fF) ;\n") + self.lib.write(" leakage_power_unit : \"1uW\" ;\n") + self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n") + self.lib.write(" operating_conditions(TT){\n") + self.lib.write(" voltage : {0} ;\n".format(tech.spice["supply_voltage"])) + self.lib.write(" temperature : 25.000 ;\n") + self.lib.write(" }\n\n") + + def write_defaults(self): + """ Adds default values for slew and capacitance.""" + + self.lib.write(" input_threshold_pct_fall : 50.0 ;\n") + self.lib.write(" output_threshold_pct_fall : 50.0 ;\n") + self.lib.write(" input_threshold_pct_rise : 50.0 ;\n") + self.lib.write(" output_threshold_pct_rise : 50.0 ;\n") + self.lib.write(" slew_lower_threshold_pct_fall : 10.0 ;\n") + self.lib.write(" slew_upper_threshold_pct_fall : 90.0 ;\n") + self.lib.write(" slew_lower_threshold_pct_rise : 10.0 ;\n") + self.lib.write(" slew_upper_threshold_pct_rise : 90.0 ;\n\n") + + self.lib.write(" default_cell_leakage_power : 0.0 ;\n") + self.lib.write(" default_leakage_power_density : 0.0 ;\n") + self.lib.write(" default_input_pin_cap : 1.0 ;\n") + self.lib.write(" default_inout_pin_cap : 1.0 ;\n") + self.lib.write(" default_output_pin_cap : 0.0 ;\n") + self.lib.write(" default_max_transition : 0.5 ;\n") + self.lib.write(" default_fanout_load : 1.0 ;\n") + self.lib.write(" default_max_fanout : 4.0 ;\n") + self.lib.write(" default_connection_class : universal ;\n\n") + + def write_LUT(self): + """ Adds lookup_table format (A 1x1 lookup_table).""" + + Tran = ["CELL_UP_FOR_CLOCK" , "CELL_DN_FOR_CLOCK"] + for i in Tran: + self.lib.write(" lu_table_template({0})".format(i)) + self.lib.write("{\n") + self.lib.write(" variable_1 : input_net_transition;\n") + self.lib.write(" variable_2 : total_output_net_capacitance;\n") + self.lib.write(" index_1 (\"0.5\");\n") + self.lib.write(" index_2 (\"0.5\");\n") + self.lib.write(" }\n\n") + + CONS = ["CONSTRAINT_HIGH_POS" , "CONSTRAINT_LOW_POS"] + for i in CONS: + self.lib.write(" lu_table_template({0})".format(i)) + self.lib.write("{\n") + self.lib.write(" variable_1 : related_pin_transition;\n") + self.lib.write(" variable_2 : constrained_pin_transition;\n") + self.lib.write(" index_1 (\"0.5\");\n") + self.lib.write(" index_2 (\"0.5\");\n") + self.lib.write(" }\n\n") + + self.lib.write(" lu_table_template(CLK_TRAN) {\n") + self.lib.write(" variable_1 : constrained_pin_transition;\n") + self.lib.write(" index_1 (\"0.5\");\n") + self.lib.write(" }\n\n") + + self.lib.write(" lu_table_template(TRAN) {\n") + self.lib.write(" variable_1 : total_output_net_capacitance;\n") + self.lib.write(" index_1 (\"0.5\");\n") + self.lib.write(" }\n\n") + + + def write_bus(self): + """ Adds format of DATA and ADDR bus.""" + + self.lib.write("\n\n") + self.lib.write(" type (DATA){\n") + self.lib.write(" base_type : array;\n") + self.lib.write(" data_type : bit;\n") + self.lib.write(" bit_width : {0};\n".format(self.word_size)) + self.lib.write(" bit_from : 0;\n") + self.lib.write(" bit_to : {0};\n".format(self.word_size - 1)) + self.lib.write(" }\n\n") + + self.lib.write(" type (ADDR){\n") + self.lib.write(" base_type : array;\n") + self.lib.write(" data_type : bit;\n") + self.lib.write(" bit_width : {0};\n".format(self.addr_size)) + self.lib.write(" bit_from : 0;\n") + self.lib.write(" bit_to : {0};\n".format(self.addr_size - 1)) + self.lib.write(" }\n\n") + + + def write_timing(self, times): + """ Adds Setup and Hold timing results""" + + self.lib.write(" timing(){ \n") + self.lib.write(" timing_type : setup_rising; \n") + self.lib.write(" related_pin : \"clk\"; \n") + self.lib.write(" rise_constraint(CONSTRAINT_HIGH_POS) {\n") + self.lib.write(" values(\"{0}\"); \n".format(times["setup_time_one"])) + self.lib.write(" }\n") + self.lib.write(" fall_constraint(CONSTRAINT_LOW_POS) {\n") + self.lib.write(" values(\"{0}\"); \n".format(times["setup_time_zero"])) + self.lib.write(" }\n") + self.lib.write(" }\n") + self.lib.write(" timing(){ \n") + self.lib.write(" timing_type : hold_rising; \n") + self.lib.write(" related_pin : \"clk\"; \n") + self.lib.write(" rise_constraint(CONSTRAINT_HIGH_POS) {\n") + self.lib.write(" values(\"{0}\"); \n".format(times["hold_time_one"])) + self.lib.write(" }\n") + self.lib.write(" fall_constraint(CONSTRAINT_LOW_POS) {\n") + self.lib.write(" values(\"{0}\"); \n".format(times["hold_time_zero"])) + self.lib.write(" }\n") + self.lib.write(" }\n") + + + + def write_data_bus(self, data, times): + """ Adds data bus timing results.""" + self.lib.write(" bus(DATA){\n") + self.lib.write(" bus_type : DATA; \n") + self.lib.write(" direction : inout; \n") + self.lib.write(" max_capacitance : {0}; \n".format(tech.spice["FF_in_cap"] + tech.spice["tri_gate_out_cap"] )) + self.lib.write(" pin(DATA[{0}:0])".format(self.word_size - 1)) + self.lib.write("{\n") + self.lib.write(" }\n") + self.lib.write(" three_state : \"OEb & !clk\"; \n") + + self.lib.write(" memory_write(){ \n") + self.lib.write(" address : ADDR; \n") + self.lib.write(" clocked_on : clk; \n") + self.lib.write(" }\n") + self.write_timing(times) + self.lib.write(" memory_read(){ \n") + self.lib.write(" address : ADDR; \n") + self.lib.write(" }\n") + self.lib.write(" timing(){ \n") + self.lib.write(" timing_sense : non_unate; \n") + self.lib.write(" related_pin : \"clk\"; \n") + self.lib.write(" timing_type : rising_edge; \n") + self.lib.write(" cell_rise(CELL_UP_FOR_CLOCK) {\n") + self.lib.write(" values(\"{0}\"); \n".format(data["delay1"])) + self.lib.write(" }\n") + self.lib.write(" cell_fall(CELL_DN_FOR_CLOCK) {\n") + self.lib.write(" values(\"{0}\"); \n".format(data["delay0"])) + self.lib.write(" }\n") + self.lib.write(" rise_transition(TRAN) {\n") + self.lib.write(" values(\"{0}\"); \n".format(data["delay1"])) + self.lib.write(" }\n") + self.lib.write(" fall_transition(TRAN) {\n") + self.lib.write(" values(\"{0}\"); \n".format(data["delay0"])) + self.lib.write(" }\n") + self.lib.write(" }\n") + self.lib.write(" }\n\n") + + + def write_addr_bus(self, times): + """ Adds addr bus timing results.""" + + self.lib.write(" bus(ADDR){\n") + self.lib.write(" bus_type : ADDR; \n") + self.lib.write(" direction : input; \n") + self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) + self.lib.write(" max_transition : 0.5;\n") + self.lib.write(" fanout_load : 1.000000;\n") + self.lib.write(" pin(ADDR[{0}:0])".format(self.addr_size - 1)) + self.lib.write("{\n") + self.lib.write(" }\n") + self.write_timing(times) + self.lib.write(" }\n\n") + + + def write_control_pins(self, times): + """ Adds control pins timing results.""" + + ctrl_pin_names = ["CSb", "OEb", "WEb"] + for i in ctrl_pin_names: + self.lib.write(" pin({0})".format(i)) + self.lib.write("{\n") + self.lib.write(" direction : input; \n") + self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) + self.write_timing(times) + self.lib.write(" }\n\n") + + + def write_clk(self, data): + """ Adds clk pin timing results.""" + + self.lib.write(" pin(clk){\n") + self.lib.write(" clock : true;\n") + self.lib.write(" direction : input; \n") + self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"])) + self.lib.write(" min_pulse_width_high : {0} ; \n".format(ch.round_time(data["min_period1"]))) + self.lib.write(" min_pulse_width_low : {0} ; \n".format(ch.round_time(data["min_period0"]))) + self.lib.write(" timing(){ \n") + self.lib.write(" timing_type :\"min_pulse_width\"; \n") + self.lib.write(" related_pin : clk; \n") + self.lib.write(" rise_constraint(CLK_TRAN) {\n") + self.lib.write(" values(\"0\"); \n") + self.lib.write(" }\n") + self.lib.write(" fall_constraint(CLK_TRAN) {\n") + self.lib.write(" values(\"0\"); \n") + self.lib.write(" }\n") + self.lib.write(" }\n") + self.lib.write(" timing(){ \n") + self.lib.write(" timing_type :\"minimum_period\"; \n") + self.lib.write(" related_pin : clk; \n") + self.lib.write(" rise_constraint(CLK_TRAN) {\n") + self.lib.write(" values(\"0\"); \n") + self.lib.write(" }\n") + self.lib.write(" fall_constraint(CLK_TRAN) {\n") + self.lib.write(" values(\"0\"); \n") + self.lib.write(" }\n") + self.lib.write(" }\n") + self.lib.write(" }\n") + self.lib.write(" }\n") + self.lib.write("}\n") diff --git a/compiler/characterizer/setup_hold.py b/compiler/characterizer/setup_hold.py new file mode 100644 index 00000000..2ce38411 --- /dev/null +++ b/compiler/characterizer/setup_hold.py @@ -0,0 +1,328 @@ +import sys +import globals +import tech +import stimuli +import debug +import charutils as ch +import ms_flop + +OPTS = globals.get_opts() + +vdd = tech.spice["supply_voltage"] +gnd = tech.spice["gnd_voltage"] + + +class setup_hold(): + """ + Functions to calculate the setup and hold times of the SRAM + (Bisection Methodology) + """ + + def __init__(self): + # This must match the spice model order + self.pins = ["data_buf", "dout", "dout_bar", "clk_buf", "vdd", "gnd"] + self.output_name = "dout" + self.model_name = "ms_flop" + self.model_location = OPTS.openram_tech + "sp_lib/ms_flop.sp" + + + def check_arguments(self, correct_value, period): + """Checks if given arguments for write_stimulus() meets requirements""" + if not isinstance(correct_value, float): + if not isinstance(correct_value, int): + debug.error("Given correct_value is not a valid number",1) + + if not isinstance(period, float): + if not isinstance(period, int): + debug.error("Given period is not a valid number",1) + + + def write_stimulus(self, mode, target_time, correct_value, period, noise_margin): + """Creates a stimulus file for SRAM setup/hold time calculation""" + self.check_arguments(correct_value,period) + + # creates and opens the stimulus file for writing + temp_stim = OPTS.openram_temp + "stim.sp" + self.sf = open(temp_stim, "w") + + self.write_header(correct_value, period) + + # instantiate the master-slave d-flip-flop + self.sf.write("* Instantiation of the Master-Slave D-flip-flop\n") + stimuli.inst_model(stim_file=self.sf, + pins=self.pins, + model_name=self.model_name) + self.sf.write("\n") + + # create a buffer for the inputs + self.sf.write("* Buffer subckt\n") + stimuli.create_buffer(stim_file=self.sf, + buffer_name="buffer", + size=[1, 1]) + self.sf.write("\n") + + self.write_data(mode=mode, + target_time=target_time, + period=period, + correct_value=correct_value) + + self.write_clock(period) + + self.write_measures(mode=mode, + correct_value=correct_value, + noise_margin=noise_margin, + period=period) + + self.write_control(period=period) + + self.sf.close() + + def write_header(self, correct_value, period): + """ Write the header file with all the models and the power supplies. """ + self.sf.write("* Stimulus for setup/hold: data {0} period {1}n\n".format(correct_value, period)) + self.sf.write("\n") + + # include files in stimulus file + self.model_list = tech.spice["fet_models"] + [self.model_location] + stimuli.write_include(stim_file=self.sf, + models=self.model_list) + self.sf.write("\n") + + # add vdd/gnd statements + self.sf.write("* Global Power Supplies\n") + stimuli.write_supply(stim_file=self.sf, + vdd_name=tech.spice["vdd_name"], + gnd_name=tech.spice["gnd_name"], + vdd_voltage=vdd, + gnd_voltage=gnd) + self.sf.write("\n") + + + def write_data(self, mode, period, target_time, correct_value): + """ Create the buffered data signals for setup/hold analysis """ + self.sf.write("* Buffer for the DATA signal\n") + stimuli.add_buffer(stim_file=self.sf, + buffer_name="buffer", + signal_list=["DATA"]) + self.sf.write("* Generation of the data and clk signals\n") + incorrect_value = stimuli.get_inverse_value(correct_value) + if mode=="HOLD": + start_value = correct_value + end_value = incorrect_value + else: + start_value = incorrect_value + end_value = correct_value + + stimuli.gen_pulse(stim_file=self.sf, + sig_name="DATA", + v1=start_value, + v2=end_value, + offset=target_time, + period=2*period, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + self.sf.write("\n") + + def write_clock(self,period): + """ Create the buffered clock signal for setup/hold analysis """ + self.sf.write("* Buffer for the clk signal\n") + stimuli.add_buffer(stim_file=self.sf, + buffer_name="buffer", + signal_list=["clk"]) + self.sf.write("\n") + stimuli.gen_pulse(stim_file=self.sf, + sig_name="clk", + offset=period, + period=period, + t_rise=tech.spice["rise_time"], + t_fall=tech.spice["fall_time"]) + self.sf.write("\n") + + + def write_measures(self, mode, correct_value, noise_margin, period): + """ Measure statements for setup/hold with right phases. """ + + if correct_value == vdd: + max_or_min = "MAX" + rise_or_fall = "RISE" + else: + max_or_min = "MIN" + rise_or_fall = "FALL" + + incorrect_value = stimuli.get_inverse_value(correct_value) + + self.sf.write("* Measure statements for pass/fail verification\n") + self.sf.write(".IC v({0})={1}\n".format(self.output_name, incorrect_value)) + #self.sf.write(".MEASURE TRAN {0}VOUT {0} v({1}) GOAL={2}\n".format(max_or_min, output_name, noise_margin)) + # above is the old cmd for hspice, below is the one work for both + self.sf.write(".MEASURE TRAN {0}VOUT {0} v({1}) from ={2}n to ={3}n\n".format(max_or_min, + self.output_name, + 1.5*period, + 2*period)) + self.sf.write("\n") + + + def write_control(self, period): + # transient window + end_time = 2 * period + self.sf.write(".TRAN 1n {0}n\n".format(end_time)) + self.sf.write(".OPTIONS POST=1 PROBE\n") + + if OPTS.spice_version == "hspice": + self.sf.write(".probe V(*)\n") + # end the stimulus file + self.sf.write(".end\n") + else: + self.sf.write(".control\n") + self.sf.write("run\n") + self.sf.write("quit\n") + self.sf.write(".endc\n") + self.sf.write(".end\n") + + + def bidir_search(self, correct_value, noise_margin, measure_name, mode): + """ This will perform a bidirectional search for either setup or hold times. + It starts with the feasible priod and looks a half period beyond or before it + depending on whether we are doing setup or hold. + """ + period = tech.spice["feasible_period"] + + # The clock will start being offset by a period, so we want to look before and after + # theis time. + if mode == "HOLD": + target_time = 1.5 * period + lower_bound = 0.5*period + upper_bound = 1.5 * period + else: + target_time = 0.5 * period + lower_bound = 0.5 * period + upper_bound = 1.5*period + + previous_time = target_time + # Initial Check if reference setup time passes for correct_value + self.write_stimulus(mode=mode, + target_time=target_time, + correct_value=correct_value, + period=period, + noise_margin=noise_margin) + stimuli.run_sim() + output_value = ch.convert_to_float(ch.parse_output("timing", measure_name)) + debug.info(3,"Correct: {0} Output: {1} NM: {2}".format(correct_value,output_value,noise_margin)) + if mode == "HOLD": + setuphold_time = target_time - period + else: + setuphold_time = period - target_time + debug.info(3,"Target time: {0} Low: {1} Up: {2} Measured: {3}".format(target_time, + lower_bound, + upper_bound, + setuphold_time)) + if not self.pass_fail_test(output_value, correct_value, noise_margin): + debug.error("Initial period/target hold time fails for data value",2) + + # We already found it feasible, so advance one step first thing. + if mode == "HOLD": + target_time -= 0.5 * (upper_bound - lower_bound) + else: + target_time += 0.5 * (upper_bound - lower_bound) + while True: + self.write_stimulus(mode=mode, + target_time=target_time, + correct_value=correct_value, + period=period, + noise_margin=noise_margin) + if mode == "HOLD": + setuphold_time = target_time - period + else: + setuphold_time = period - target_time + debug.info(3,"Target time: {0} Low: {1} Up: {2} Measured: {3}".format(target_time, + lower_bound, + upper_bound, + setuphold_time)) + + stimuli.run_sim() + output_value = ch.convert_to_float(ch.parse_output("timing", measure_name)) + debug.info(3,"Correct: {0} Output: {1} NM: {2}".format(correct_value,output_value,noise_margin)) + if self.pass_fail_test(output_value,correct_value,noise_margin): + debug.info(3,"PASS") + if ch.relative_compare(target_time, previous_time): + debug.info(3,"CONVERGE " + str(target_time) + " " + str(previous_time)) + break + previous_time = target_time + if mode == "HOLD": + upper_bound = target_time + target_time -= 0.5 * (upper_bound - lower_bound) + else: + lower_bound = target_time + target_time += 0.5 * (upper_bound - lower_bound) + else: + debug.info(3,"FAIL") + if mode == "HOLD": + lower_bound = target_time + target_time += 0.5 * (upper_bound - lower_bound) + else: + upper_bound = target_time + target_time -= 0.5 * (upper_bound - lower_bound) + #raw_input("Press Enter to continue...") + # the clock starts offset by one clock period, + # so we always measure our setup or hold relative to this time + if mode == "HOLD": + setuphold_time = target_time - period + else: + setuphold_time = period - target_time + return setuphold_time + + + def setup_time(self): + """Calculates the setup time for low-to-high and high-to-low + transition for a D-flip-flop""" + + one_found = self.bidir_search(vdd, 0.9*vdd, "maxvout", "SETUP") + + zero_found = self.bidir_search(gnd, 0.1*vdd, "minvout", "SETUP") + + return [one_found, zero_found] + + def hold_time(self): + """Calculates the hold time for low-to-high and high-to-low + transition for a D-flip-flop""" + + one_found = self.bidir_search(vdd, 0.9*vdd, "maxvout", "HOLD") + + zero_found = self.bidir_search(gnd, 0.1*vdd, "minvout", "HOLD") + + return [one_found, zero_found] + + + def pass_fail_test(self,value,correct_value,noise_margin): + """Function to Test if the output value reached the + noise_margin to determine if it passed or failed""" + if correct_value == vdd: + return True if value >= noise_margin else False + else: + return True if value <= noise_margin else False + + + + + def analyze(self): + """main function to calculate both setup and hold time for the + d-flip-flop returns a dictionary that contains 4 times for both + setup/hold times for high_to_low and low_to_high transition + dictionary keys: setup_time_one (low_to_high), setup_time_zero + (high_to_low), hold_time_one (low_to_high), hold_time_zero + (high_to_low) + """ + + [one_setup_time, zero_setup_time] = self.setup_time() + [one_hold_time, zero_hold_time] = self.hold_time() + debug.info(1, "Setup Time for low_to_high transistion: {0}".format(one_setup_time)) + debug.info(1, "Setup Time for high_to_low transistion: {0}".format(zero_setup_time)) + debug.info(1, "Hold Time for low_to_high transistion: {0}".format(one_hold_time)) + debug.info(1, "Hold Time for high_to_low transistion: {0}".format(zero_hold_time)) + times = {"setup_time_one": one_setup_time, + "setup_time_zero": zero_setup_time, + "hold_time_one": one_hold_time, + "hold_time_zero": zero_hold_time + } + return times + diff --git a/compiler/characterizer/stimuli.py b/compiler/characterizer/stimuli.py new file mode 100644 index 00000000..52fd3350 --- /dev/null +++ b/compiler/characterizer/stimuli.py @@ -0,0 +1,467 @@ +""" +This file generates the test structure and stimulus for an sram +simulation. There are various functions that can be be used to +generate stimulus for other simulations as well. +""" + +import globals +import tech +import debug +import subprocess +import os +import sys + +OPTS = globals.get_opts() + +vdd = tech.spice["supply_voltage"] +gnd = tech.spice["gnd_voltage"] +vdd_name = tech.spice["vdd_name"] +gnd_name = tech.spice["gnd_name"] +pmos_name = tech.spice["pmos_name"] +nmos_name = tech.spice["nmos_name"] +tx_width = tech.spice["minwidth_tx"] +tx_length = tech.spice["channel"] + +def inst_sram(stim_file, abits, dbits, sram_name): + """function to instatiate the sram subckt""" + stim_file.write("Xsram ") + for i in range(dbits): + stim_file.write("DATA[{0}] ".format(i)) + for i in range(abits): + stim_file.write("A[{0}]_buf ".format(i)) + for i in tech.spice["control_signals"]: + stim_file.write("{0}_buf ".format(i)) + stim_file.write("{0}_buf_buf ".format(tech.spice["clk"])) + stim_file.write("{0} {1} ".format(vdd_name, gnd_name)) + stim_file.write("{0}\n".format(sram_name)) + + +def inst_model(stim_file, pins, model_name): + """function to instantiate a model""" + stim_file.write("X{0} ".format(model_name)) + for pin in pins: + stim_file.write("{0} ".format(pin)) + stim_file.write("{0}\n".format(model_name)) + + +def create_inverter(stim_file, size=1, beta=2.5): + """Generates inverter for the top level signals (only for sim purposes)""" + stim_file.write(".SUBCKT test_inv in out {0} {1}\n".format(vdd_name, gnd_name)) + stim_file.write("mpinv out in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name, + pmos_name, + beta * size * tx_width, + tx_length)) + stim_file.write("mninv out in {0} {0} {1} w={2}u l={3}u\n".format(gnd_name, + nmos_name, + size * tx_width, + tx_length)) + stim_file.write(".ENDS test_inv\n") + + +def create_buffer(stim_file, buffer_name, size=[1,3], beta=2.5): + """Generates buffer for top level signals (only for sim + purposes). Size is pair for PMOS, NMOS width multiple. It includes + a beta of 3.""" + + stim_file.write(".SUBCKT test_{2} in out {0} {1}\n".format(vdd_name, + gnd_name, + buffer_name)) + stim_file.write("mpinv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(vdd_name, + pmos_name, + beta * size[0] * tx_width, + tx_length)) + stim_file.write("mninv1 out_inv in {0} {0} {1} w={2}u l={3}u\n".format(gnd_name, + nmos_name, + size[0] * tx_width, + tx_length)) + stim_file.write("mpinv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(vdd_name, + pmos_name, + beta * size[1] * tx_width, + tx_length)) + stim_file.write("mninv2 out out_inv {0} {0} {1} w={2}u l={3}u\n".format(gnd_name, + nmos_name, + size[1] * tx_width, + tx_length)) + stim_file.write(".ENDS test_{0}\n".format(buffer_name)) + + +def add_buffer(stim_file, buffer_name, signal_list): + """Adds buffers to each top level signal that is in signal_list (only for sim purposes)""" + for signal in signal_list: + stim_file.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal, + "test"+vdd_name, + "test"+gnd_name, + buffer_name)) + + +def add_inverter(stim_file, signal_list): + """Adds inv for each signal that needs its inverted version (only for sim purposes)""" + for signal in signal_list: + stim_file.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal, + "test"+vdd_name, + "test"+gnd_name)) + + +def add_accesstx(stim_file, dbits): + """Adds transmission gate for inputs to data-bus (only for sim purposes)""" + stim_file.write("* Tx Pin-list: Drain Gate Source Body\n") + for i in range(dbits): + pmos_access_string="mp{0} DATA[{0}] WEb_trans_buf D[{0}]_buf {1} {2} w={3}u l={4}u\n" + stim_file.write(pmos_access_string.format(i, + "test"+vdd_name, + pmos_name, + 2 * tx_width, + tx_length)) + nmos_access_string="mn{0} DATA[{0}] WEb_trans_inv D[{0}]_buf {1} {2} w={3}u l={4}u\n" + stim_file.write(nmos_access_string.format(i, + "test"+gnd_name, + nmos_name, + 2 * tx_width, + tx_length)) + +def gen_pulse(stim_file, sig_name, v1=gnd, v2=vdd, offset=0, period=1, t_rise=0, t_fall=0): + """Generates a periodic signal with 50% duty cycle and slew rates. Period is measured + from 50% to 50%.""" + pulse_string="V{0} {0} 0 PULSE ({1} {2} {3}n {4}n {5}n {6}n {7}n)\n" + stim_file.write(pulse_string.format(sig_name, + v1, + v2, + offset, + t_rise, + t_fall, + 0.5*period-0.5*t_rise-0.5*t_fall, + period)) + + + +def gen_clk_pwl(stim_file, cycle_times, t_fall, t_rise): + """Generates a clk signal using pwl. The cycle times are the times of the + clock rising edge. It is assumed to start at time 0 with clock 0. Duty + cycle is assumed to be 50%. Rise/fall times are 0-100%.""" + stim_file.write("V{0} {0} 0 PWL (0n 0v ".format(tech.spice["clk"])) + for i in range(len(cycle_times)-1): + period = cycle_times[i+1] - cycle_times[i] + t_current = cycle_times[i] - 0.5*t_rise # 50% point is at cycle time + t_current2 = t_current + t_rise + # rising edge + stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, gnd, t_current2, vdd)) + t_current = t_current + 0.5*period - 0.5*t_fall # 50% point is at cycle time + t_current2 = t_current + t_fall + # falling edge + stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, vdd, t_current2, gnd)) + # end time doesn't need a rising edge + stim_file.write("{0}n {1}v)\n".format(cycle_times[-1], gnd)) + + +def gen_data_pwl(stim_file, key_times, sig_name, data_value, feasible_period, target_period, t_rise, t_fall): + """Generates the PWL data inputs for a simulation timing test.""" + data_value_invert = gnd if data_value == vdd else vdd + + t_current = 0.0 + stim_file.write("V{0} {0} 0 PWL ({1}n {2}v ".format(sig_name, t_current, data_value_invert)) + t_current = key_times[2] - 0.25 * target_period + t_current += (0.5 * target_period) # uses falling edge for ZBT mode + slew_time = t_rise if data_value_invert == gnd else t_fall + t_current2 = t_current + slew_time + stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, data_value_invert, t_current2, data_value)) + t_current = key_times[2] + 0.25 * target_period + t_current += (0.5 * target_period) # uses falling edge for ZBT mode + slew_time = t_rise if data_value == gnd else t_fall + t_current2 = t_current + slew_time + stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, data_value, t_current2, data_value_invert)) + t_current = key_times[5] + 0.25 * feasible_period + stim_file.write("{0}n {1}v)\n".format(t_current, data_value_invert)) + + +def gen_addr_pwl(stim_file, key_times, addr, feasible_period, target_period, t_rise, t_fall): + """Generates the PWL for address inputs for a simulation timing test""" + # reverse string + reversed_addr = addr[::-1] + # inverts all bits in address using intermediate value of 2 + invert_addr = reversed_addr.replace('1', '2').replace('0', '1').replace('2', '0') + + for i in range(len(reversed_addr)): + v_val = gnd if reversed_addr[i] == '0' else vdd + v_val_invert = gnd if invert_addr[i] == '0' else vdd + t_current = 0.0 + stim_file.write("V{0} {0} 0 PWL ({1}n {2}v ".format("A[{0}]".format(i), t_current, v_val)) + t_current = key_times[3] - 0.25 * target_period + slew_time = t_rise if v_val == gnd else t_fall + t_current2 = t_current + slew_time + stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, v_val, t_current2, v_val_invert)) + t_current = key_times[4] - 0.25 * target_period + slew_time = t_rise if v_val_invert == gnd else t_fall + t_current2 = t_current + slew_time + stim_file.write("{0}n {1}v {2}n {3}v ".format(t_current, v_val_invert, t_current2, v_val)) + t_current = key_times[5] + 0.25 * feasible_period + stim_file.write("{0}n {1}v)\n".format(t_current, v_val)) + + +def gen_constant(stim_file, sig_name, v_ref, v_val): + """Generates a constant signal with reference voltage and the voltage value""" + stim_file.write("V{0} {0} {1} DC {2}\n".format(sig_name, v_ref, v_val)) + +def gen_csb_pwl(key_times, feasible_period, target_period, t_rise, t_fall): + """Returns two lists for x,y coordinates for the generation of CSb pwl""" + t_current = 0.0 + x_list = [t_current] + y_list = [vdd] + + for key_time in key_times[:2]: + t_current = key_time - 0.25 * feasible_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current = key_time + 0.25 * feasible_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + for key_time in key_times[2:-1]: + t_current = key_time - 0.25 * target_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current = key_time + 0.25 * target_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + return (x_list, y_list) + + +def gen_web_pwl(key_times, feasible_period, target_period, t_rise, t_fall): + """Returns two lists for x,y coordinates for the generation of WEb pwl""" + + t_current = 0.0 + x_list = [t_current] + y_list = [vdd] + + t_current = key_times[0] - 0.25 * feasible_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current = key_times[0] + 0.25 * feasible_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + t_current = key_times[2] - 0.25 * target_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current = key_times[2] + 0.25 * target_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + t_current = key_times[3] - 0.25 * target_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current = key_times[3] + 0.25 * target_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + return (x_list, y_list) + + +def gen_oeb_pwl(key_times, feasible_period, target_period, t_rise, t_fall): + """Returns two lists for x,y coordinates for the generation of OEb pwl""" + + t_current = 0.0 + x_list = [t_current] + y_list = [vdd] + + t_current = key_times[1] - 0.25 * feasible_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current = key_times[1] + 0.25 * feasible_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + t_current = key_times[4] - 0.25 * target_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current = key_times[4] + 0.25 * target_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + return (x_list, y_list) + + +def gen_web_trans_pwl(key_times, feasible_period, target_period, t_rise, t_fall): + """Returns two lists for x,y coordinates for the generation of WEb_transmission_gate pwl""" + + t_current = 0.0 + x_list = [t_current] + y_list = [vdd] + + t_current = key_times[0] + 0.5 * feasible_period + t_current -= 0.25 * feasible_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current += 0.5 * feasible_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + t_current = key_times[2] + 0.5 * target_period + t_current -= 0.25 * target_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current += 0.5 * target_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + t_current = key_times[3] + 0.5 * target_period + t_current -= 0.25 * target_period + x_list.append(t_current) + y_list.append(vdd) + x_list.append(t_current + t_fall) + y_list.append(gnd) + + t_current += 0.5 * target_period + x_list.append(t_current) + y_list.append(gnd) + x_list.append(t_current + t_rise) + y_list.append(vdd) + + return (x_list, y_list) + + +def get_inverse_value(value): + if value > 0.5*vdd: + return gnd + elif value <= 0.5*vdd: + return vdd + else: + debug.error("Invalid value to get an inverse of: {0}".format(value)) + + +def gen_pwl(stim_file, sig_name, x_list, y_list): + """Generates an arbitrary pwl for a signal where xlist is times in + ns and ylist is voltage. """ + + t_current = 0.0 + stim_file.write("V{0} {0} 0 PWL (".format(sig_name)) + for p in zip(x_list,y_list): + stim_file.write("{0}n {1}v ".format(p[0],p[1])) + stim_file.write(")\n") + +def gen_trap_pwl(stim_file, sig_name, x_list, y_list, t_rise, t_fall): + """Generates a trapezoidal pwl for a signal where xlist is times in ns and ylist is voltage. + Transitions are assumed to ignore slew and the slew rates are generated automatically + using the provided 0-100% slew times and centering times at the 50% point..""" + + stim_file.write("V{0} {0} 0 PWL (".format(sig_name)) + for p in zip(x_list,y_list): + slew = t_rise if p[1]>0.5*vdd else t_fall + start = max(p[0]-0.5*slew,0) + end = p[0]+0.5*slew + stim_file.write("{0}n {1}v ".format(start, get_inverse_value(p[1]))) + stim_file.write("{0}n {1}v ".format(end, p[1])) + stim_file.write(")\n") + + + +def gen_meas_delay(stim_file, meas_name, trig_name, targ_name, trig_val, targ_val, trig_dir, targ_dir, td): + """Creates the .meas statement for the measurement of delay""" + measure_string=".meas tran {0} TRIG v({1}) VAL={2} RISE={3} TARG v({4}) VAL={5} TD={7}n {6}=1\n" + stim_file.write(measure_string.format(meas_name, + trig_name, + trig_val, + trig_dir, + targ_name, + targ_val, + targ_dir, + td)) + +def gen_meas_power(stim_file, meas_name, t_initial, t_final): + """Creates the .meas statement for the measurement of avg power""" + # power mea cmd is different in different spice: + if OPTS.spice_version == "hspice": + power_exp = "power" + else: + power_exp = "par('(-1*v(" + str(vdd_name) + ")*I(v" + str(vdd_name) + "))')" + stim_file.write(".meas tran {0} avg {1} from={2}n to={3}n\n".format(meas_name, + power_exp, + t_initial, + t_final)) + + +def write_include(stim_file, models): + """Writes include statements, inputs are lists of model files""" + for item in list(models): + stim_file.write(".include \"{0}\"\n".format(item)) + + +def write_supply(stim_file, vdd_name, gnd_name, vdd_voltage, gnd_voltage): + """Writes supply voltage statements""" + stim_file.write("V{0} {0} 0.0 {1}\n".format(vdd_name, vdd_voltage)) + stim_file.write("V{0} {0} 0.0 {1}\n".format(gnd_name, gnd_voltage)) + # This is for the test power supply + stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+vdd_name, vdd_voltage)) + stim_file.write("V{0} {0} 0.0 {1}\n".format("test"+gnd_name, gnd_voltage)) + + + + +def run_sim(): + """Run hspice in batch mode and output rawfile to parse.""" + temp_stim = "{0}stim.sp".format(OPTS.openram_temp) + if OPTS.spice_version == "hspice": + # TODO: Should make multithreading parameter a configuration option + cmd_args = "-mt 8 -i {1} -o {2}timing 2>&1 /dev/null".format(OPTS.spice_exe, + temp_stim, + OPTS.openram_temp) + else: + cmd_args = "-b -i {1} -o {2}timing.lis 2>&1 /dev/null".format(OPTS.spice_exe, + temp_stim, + OPTS.openram_temp) + + FNULL = open(os.devnull, 'w') + debug.info(2, OPTS.spice_exe + " " + cmd_args) + retcode = subprocess.call([OPTS.spice_exe, cmd_args], stdout=FNULL, stderr=FNULL) + FNULL.close() + + if (retcode > 0): + debug.error("Spice simulation error: " + OPTS.spice_exe + " " + cmd_args) + sys.exit(-1) + + diff --git a/compiler/contact.py b/compiler/contact.py new file mode 100644 index 00000000..4553ff91 --- /dev/null +++ b/compiler/contact.py @@ -0,0 +1,112 @@ +import design +import debug +from tech import drc +from vector import vector + +class contact(design.design): + """ + Object for a contact shape with its conductor enclosures + Creates a contact array minimum active or poly enclosure and metal1 enclosure. + This class has enclosure on multiple sides of the contact whereas a via may + have extension on two or four sides. + """ + unique_contact_id = 1 + + def __init__(self, layer_stack, dimensions=[1,1], offset=(0,0)): + name = "{0}_{1}x{2}_num{3}".format(layer_stack[1], + dimensions[0], + dimensions[1], + contact.unique_contact_id) + design.design.__init__(self, name) + debug.info(2, "create contact object {0}".format(name)) + contact.unique_contact_id += 1 + + self.layer_stack = layer_stack + self.dimensions = dimensions + self.offset = offset + self.pins = [] # used for matching parm lengths + self.create_layout() + + def create_layout(self): + self.setup_layers() + self.setup_layout_constants() + self.create_contact_array() + self.create_first_layer_enclosure() + self.create_second_layer_enclosure() + self.offset_all_coordinates() + + def offset_all_coordinates(self): + coordinate = self.find_lowest_coords() + self.offset_attributes(coordinate) + self.translate(coordinate) + + self.height = max(obj.offset[1] + obj.height for obj in self.objs) + self.width = max(obj.offset[0] + obj.width for obj in self.objs) + + def setup_layers(self): + (first_layer, via_layer, second_layer) = self.layer_stack + self.first_layer_name = first_layer + self.via_layer_name = via_layer + self.second_layer_name = second_layer + + def setup_layout_constants(self): + self.contact_width = drc["minwidth_{0}". format(self.via_layer_name)] + self.contact_to_contact = drc["{0}_to_{0}".format(self.via_layer_name)] + self.contact_pitch = self.contact_width + self.contact_to_contact + self.contact_array_width = self.contact_width \ + + (self.dimensions[0] - 1) * self.contact_pitch + self.contact_array_height = self.contact_width \ + + (self.dimensions[1] - 1) * self.contact_pitch + + # FIME break this up + self.first_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.first_layer_name)] - self.contact_array_width) / 2, + drc["{0}_enclosure_{1}".format(self.first_layer_name, self.via_layer_name)]) + self.first_layer_vertical_enclosure = max((drc["minarea_{0}".format(self.first_layer_name)] + / (self.contact_array_width + 2 * self.first_layer_horizontal_enclosure) - self.contact_array_height) / 2, + (drc["minheight_{0}".format( + self.first_layer_name)] - self.contact_array_height) / 2, + drc["{0}_extend_{1}".format(self.first_layer_name, self.via_layer_name)]) + + self.second_layer_horizontal_enclosure = max((drc["minwidth_{0}".format(self.second_layer_name)] - self.contact_array_width) / 2, + drc["{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name)]) + self.second_layer_vertical_enclosure = max((drc["minarea_{0}".format(self.second_layer_name)] + / (self.contact_array_width + 2 * self.second_layer_horizontal_enclosure) - self.contact_array_height) / 2, + (drc["minheight_{0}".format( + self.second_layer_name)] - self.contact_array_height) / 2, + drc["{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name)]) + + def create_contact_array(self): + """ Create the contact array at the origin""" + self.via_layer_position = vector(0, 0) + for i in range(self.dimensions[1]): + offset = [0, 0 + self.contact_pitch * i] + for j in range(self.dimensions[0]): + self.add_rect(layer=self.via_layer_name, + offset=offset, + width=self.contact_width, + height=self.contact_width) + offset = [offset[0] + self.contact_pitch, offset[1]] + + def create_first_layer_enclosure(self): + width = self.first_layer_width = self.contact_array_width \ + + 2 * self.first_layer_horizontal_enclosure + height = self.first_layer_height = self.contact_array_height \ + + 2 * self.first_layer_vertical_enclosure + offset = self.first_layer_position = vector(-self.first_layer_horizontal_enclosure, + -self.first_layer_vertical_enclosure) + self.add_rect(layer=self.first_layer_name, + offset=offset, + width=width, + height=height) + + def create_second_layer_enclosure(self): + width = self.second_layer_width = self.contact_array_width \ + + 2 * self.second_layer_horizontal_enclosure + height = self.second_layer_height = self.contact_array_height \ + + 2 * self.second_layer_vertical_enclosure + offset = self.second_layer_position = vector(-self.second_layer_horizontal_enclosure, + -self.second_layer_vertical_enclosure) + self.add_rect(layer=self.second_layer_name, + offset=offset, + width=width, + height=height) diff --git a/compiler/control_logic.py b/compiler/control_logic.py new file mode 100644 index 00000000..3cda5e85 --- /dev/null +++ b/compiler/control_logic.py @@ -0,0 +1,696 @@ +from math import log +import design +from tech import drc, parameter, cell +import debug +from ms_flop_array import ms_flop_array +from wordline_driver import wordline_driver +from contact import contact +from pinv import pinv +from nand_2 import nand_2 +from nand_3 import nand_3 +from nor_2 import nor_2 +from replica_bitline import replica_bitline +import math +from vector import vector +from globals import OPTS + +class control_logic(design.design): + """ + Dynamically generated Control logic for the total SRAM circuit. + """ + + def __init__(self, num_rows): + """ Constructor """ + design.design.__init__(self, "control_logic") + debug.info(1, "Creating %s" % self.name) + + self.num_rows = num_rows + self.create_layout() + self.DRC_LVS() + + def create_layout(self): + """ Create layout and route between modules """ + self.create_modules() + self.setup_layout_offsets() + self.add_modules() + self.add_routing() + self.add_pin_labels() + + def create_modules(self): + """ add all the required modules """ + c = reload(__import__(OPTS.config.ms_flop)) + self.mod_ms_flop = getattr(c, OPTS.config.ms_flop) + self.ms_flop = self.mod_ms_flop("ms_flop") + self.add_mod(self.ms_flop) + self.inv = pinv(name="pinv", + nmos_width=drc["minwidth_tx"], + beta=parameter["pinv_beta"]) + + self.add_mod(self.inv) + self.nand2 = nand_2(name="nand2", + nmos_width=2 * drc["minwidth_tx"]) + self.add_mod(self.nand2) + self.NAND3 = nand_3(name="NAND3", + nmos_width=3 * drc["minwidth_tx"]) + self.add_mod(self.NAND3) + + # Special gates: 4x Inverter + self.inv4 = pinv(name="pinv4", + nmos_width=4 * drc["minwidth_tx"], + beta=parameter["pinv_beta"]) + self.add_mod(self.inv4) + + self.nor2 = nor_2(name="nor2", + nmos_width=drc["minwidth_tx"]) + self.add_mod(self.nor2) + + self.msf_control = ms_flop_array(name="msf_control", + array_type="data_in", + columns=3, + word_size=3) + self.add_mod(self.msf_control) + + self.replica_bitline = replica_bitline("replica_bitline", + int(math.ceil(self.num_rows / 10.0))) + self.add_mod(self.replica_bitline) + + def add_pin_labels(self): + """ Add pins and labels after everything is done """ + input_lst =["CSb","WEb","OEb"] + output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"] + clk =["clk"] + rails = ["vdd", "gnd"] + pin_lst = input_lst + output_lst + clk + rails + for pin in pin_lst: + self.add_pin(pin) + + # add label of input, output and clk in metal3 layer + input_lst =["CSb","WEb","OEb"] + output_lst = ["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar"] + for pin in input_lst + output_lst + ["clk"]: + self.add_label(text=pin, + layer="metal3", + offset=getattr(self, pin+"_position")) + # add label of vdd and gnd manually cause non-uniformed names and layers + self.add_label(text="vdd", + layer="metal1", + offset=self.vdd1_position) + self.add_label(text="vdd", + layer="metal2", + offset=self.vdd2_position) + self.add_label(text="gnd", + layer="metal2", + offset=self.gnd_position) + + def setup_layout_offsets(self): + """ Setup layout offsets, determine the size of the busses etc """ + # This isn't for instantiating, but we use it to get the dimensions + m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) + + # Vertical metal rail gap definition + self.metal2_extend_contact = (m1m2_via.second_layer_height - m1m2_via.contact_width) / 2 + self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"] + self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"] + self.via_shift = (m1m2_via.second_layer_width - m1m2_via.first_layer_width) / 2 + + # used to shift contact when connecting to NAND3 C pin down + self.contact_shift = (m1m2_via.first_layer_width - m1m2_via.contact_width) / 2 + + # Common parameters for rails + self.rail_width = drc["minwidth_metal2"] + self.rail_gap = 2 * drc["metal2_to_metal2"] + self.rail_offset_gap = self.rail_width + self.rail_gap + + # First RAIL Parameters + self.num_rails_1 = 6 + self.overall_rail_1_gap = (self.num_rails_1 + 1) * self.rail_offset_gap + self.rail_1_x_offsets = [] + + # Second RAIL Parameters + self.num_rails_2 = 4 + self.overall_rail_2_gap = (self.num_rails_2 + 1) * self.rail_offset_gap + self.rail_2_x_offsets = [] + + # GAP between main control and REPLICA BITLINE + self.replica_bitline_gap = self.rail_offset_gap * 2 + + self.output_port_gap = 3 * drc["minwidth_metal3"] + self.logic_height = max(self.replica_bitline.width, 4 * self.inv.height) + + def add_modules(self): + """ Place all the modules """ + self.add_msf_control() + self.set_msf_control_pins() + self.add_1st_row(self.output_port_gap) + self.add_2nd_row(self.output_port_gap + 2 * self.inv.height) + + # Height and width + self.height = self.logic_height + self.output_port_gap + self.width = self.offset_replica_bitline[0] + self.replica_bitline.height + + def add_routing(self): + """ Routing between modules """ + self.add_msf_control_routing() + self.add_1st_row_routing() + self.add_2nd_row_routing() + self.add_vdd_routing() + self.add_gnd_routing() + self.add_input_routing() + self.add_output_routing() + + def add_msf_control(self): + """ ADD ARRAY OF MS_FLOP""" + self.offset_msf_control = vector(0, self.logic_height + self.output_port_gap) + self.add_inst(name="msf_control", + mod=self.msf_control, + offset=self.offset_msf_control, + mirror="R270") + # don't change this order. This pins are meant for internal connection of msf array inside the control logic. + # These pins are connecting the msf_array inside of control_logic. + temp = ["CSb", "WEb", "OEb", "CS_bar", "CS", "WE_bar", + "WE", "OE_bar", "OE", "clk", "vdd", "gnd"] + self.connect_inst(temp) + + def set_msf_control_pins(self): + # msf_control inputs + correct = vector(0, 0.5 * drc["minwidth_metal2"]) + def translate_inputs(pt1,pt2): + return pt1 + pt2.rotate().scale(1,-1) - correct + + # msf_control outputs + def translate_outputs(pt1,pt2): + return pt1 - correct + vector(self.msf_control.height,- pt2[0]) + + # set CSS WE OE signal groups(in, out, bar) + pt1 = self.offset_msf_control + pin_set = ["CSb","WEb","OEb"] + pt2in = self.msf_control.din_positions[0:len(pin_set)] + pt2out = self.msf_control.dout_positions[0:len(pin_set)] + pt2bar = self.msf_control.dout_bar_positions[0:len(pin_set)] + for i in range(len(pin_set)): + value = translate_inputs(pt1,pt2in[i]) + setattr(self,"msf_control_"+pin_set[i]+"_position",value) + value = translate_outputs(pt1,pt2out[i]) + setattr(self,"msf_control_"+pin_set[i][0:2]+"_bar_position",value) + value = translate_outputs(pt1,pt2bar[i]) + setattr(self,"msf_control_"+pin_set[i][0:2]+"_position",value) + + # clk , vdd + base = self.offset_msf_control - vector(0.5 * drc["minwidth_metal2"], 0) + msf_clk = self.msf_control.clk_positions[0].rotate().scale(1,-1) + self.msf_control_clk_position = base + msf_clk + msf_vdd = self.msf_control.vdd_positions[0].rotate().scale(1,-1) + self.msf_control_vdd_position = base + msf_vdd + + # gnd + self.msf_control_gnd_positions = [] + for gnd_offset in self.msf_control.gnd_positions: + offset = self.offset_msf_control + vector(self.msf_control.height, + - gnd_offset[0]) + self.msf_control_gnd_positions.append(offset - correct) + + def add_1st_row(self,y_off): + # inv1 with clk as gate input. + msf_control_rotate_x = self.offset_msf_control[0] + self.msf_control.height + self.offset_inv1 = vector(msf_control_rotate_x - self.inv4.width, y_off) + self.add_inst(name="clk_inverter", + mod=self.inv4, + offset=self.offset_inv1) + self.connect_inst(["clk", "clk_bar", "vdd", "gnd"]) + # set pin offset as attr + self.inv1_A_position = self.offset_inv1 + self.inv4.A_position.scale(0,1) + base = self.offset_inv1 + vector(self.inv4.width, 0) + for pin in ["Z_position", "vdd_position", "gnd_position"]: + setattr(self, "inv1_"+pin, base + getattr(self.inv4, pin).scale(0,1)) + + # nor2 + self.offset_nor2 = vector(self.nor2.width + 2 * drc["minwidth_metal3"], + y_off) + self.add_inst(name="nor2", + mod=self.nor2, + offset=self.offset_nor2, + mirror="MY") + self.connect_inst(["clk", "OE_bar", "tri_en", "vdd", "gnd"]) + self.set_nand2_nor2_pin("nor2",[-1,1]) + + x_off = msf_control_rotate_x + self.overall_rail_1_gap + self.nand_array_position = vector(x_off, y_off) + + # nand2_1 input: OE, clk_bar output: tri_en_bar + self.offset_nand2 = self.nand_array_position + self.add_inst(name="nand2_tri_en", + mod=self.nand2, + offset=self.offset_nand2) + self.connect_inst(["OE", "clk_bar", "tri_en_bar", "vdd", "gnd"]) + # set pin offset as attr + self.set_nand2_nor2_pin("nand2",[1,1]) + + # REPLICA BITLINE + base_x = self.nand_array_position[0] + self.NAND3.width + 3 * self.inv.width + total_rail_gap = self.rail_offset_gap + self.overall_rail_2_gap + x_off = base_x + total_rail_gap + self.replica_bitline_gap + self.offset_replica_bitline = vector(x_off, y_off) + self.add_inst(name="replica_bitline", + mod=self.replica_bitline, + offset=self.offset_replica_bitline, + mirror="MX", + rotate=90) + self.connect_inst(["rblk", "pre_s_en", "vdd", "gnd"]) + + # BUFFER INVERTERS FOR S_EN + # inv_4 input: input: pre_s_en_bar, output: s_en + self.offset_inv4 = vector(base_x - 2 * self.inv.width, y_off) + self.add_inst(name="inv_s_en1", + mod=self.inv, + offset=self.offset_inv4, + mirror="MY") + self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"]) + self.set_inv2345_pins(inv_name="inv4", inv_scale=[-1, 1]) + + # inv_5 input: pre_s_en, output: pre_s_en_bar + self.offset_inv5 = vector(base_x - self.inv.width, y_off) + self.add_inst(name="inv_s_en2", + mod=self.inv, + offset=self.offset_inv5, + mirror="MY") + self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"]) + self.set_inv2345_pins(inv_name="inv5", inv_scale=[-1, 1]) + + # set pin offset as attr + pin_offset = self.replica_bitline.en_input_offset.rotate() + self.replica_en_offset = self.offset_replica_bitline + pin_offset + pin_offset = self.replica_bitline.out_offset.rotate() + self.replica_out_offset = self.offset_replica_bitline + pin_offset + + def add_2nd_row(self, y_off): + # Nand3_1 input: OE, clk_bar,CS output: rblk_bar + self.offset_nand3_1 = vector(self.nand_array_position[0], y_off) + self.add_inst(name="NAND3_rblk_bar", + mod=self.NAND3, + offset=self.offset_nand3_1, + mirror="MX") + self.connect_inst(["clk_bar", "OE", "CS", "rblk_bar", "vdd", "gnd"]) + # set pin offset as attr + self.set_Nand3_pins(nand_name = "nand3_1",nand_scale = [0,-1]) + + # Nand3_2 input: WE, clk_bar,CS output: w_en_bar + self.offset_nand3_2 = vector(self.nand_array_position[0], y_off) + self.add_inst(name="NAND3_w_en_bar", + mod=self.NAND3, + offset=self.offset_nand3_2, + mirror="RO") + self.connect_inst(["clk_bar", "WE", "CS", "w_en_bar", "vdd", "gnd"]) + # set pin offset as attr + self.set_Nand3_pins(nand_name = "nand3_2",nand_scale = [0,1]) + + # connect nand2 and nand3 to inv + nand3_to_inv_connection_height = self.NAND3.Z_position[1] - self.inv.A_position[1] + drc["minwidth_metal1"] + self.add_rect(layer="metal1", + offset=self.nand3_1_Z_position, + width=drc["minwidth_metal1"], + height=nand3_to_inv_connection_height) + self.add_rect(layer="metal1", + offset=self.nand3_2_Z_position + vector(0,drc["minwidth_metal1"]), + width=drc["minwidth_metal1"], + height=-nand3_to_inv_connection_height) + + # inv_2 input: rblk_bar, output: rblk + x_off = self.nand_array_position[0] + self.NAND3.width + self.offset_inv2 = vector(x_off, y_off) + self.add_inst(name="inv_rblk", + mod=self.inv, + offset=self.offset_inv2, + mirror="MX") + self.connect_inst(["rblk_bar", "rblk", "vdd", "gnd"]) + # set pin offset as attr + self.set_inv2345_pins(inv_name="inv2", inv_scale=[1,-1]) + + # inv_3 input: w_en_bar, output: pre_w_en + self.offset_inv3 = self.offset_inv2 + self.add_inst(name="inv_w_en", + mod=self.inv, + offset=self.offset_inv3, + mirror="RO") + self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"]) + # set pin offset as attr + self.set_inv2345_pins(inv_name="inv3", inv_scale=[1, 1]) + + # BUFFER INVERTERS FOR W_EN + x_off = self.nand_array_position[0] + self.NAND3.width + self.inv.width + self.offset_inv6 = vector(x_off, y_off) + self.add_inst(name="inv_w_en1", + mod=self.inv, + offset=self.offset_inv6, + mirror="RO") + self.connect_inst(["pre_w_en", "pre_w_en1", "vdd", "gnd"]) + + x_off = self.nand_array_position[0] + self.NAND3.width + 2 * self.inv.width + self.offset_inv7 = [x_off, y_off] + self.add_inst(name="inv_w_en2", + mod=self.inv, + offset=self.offset_inv7, + mirror="RO") + self.connect_inst(["pre_w_en1", "w_en", "vdd", "gnd"]) + # set pin offset as attr + self.inv7_Z_position = self.offset_inv7 + vector(self.inv.width, + self.inv.Z_position[1]) + + def set_nand2_nor2_pin(self,mod,scale): + offset = getattr (self, "offset_"+mod) + for pin in ["A","B"]: + pin_xy = getattr(getattr(self, mod), pin+"_position") + setattr(self, mod+"_1_"+pin+"_position", offset + pin_xy.scale(0,1)) + base = offset + vector(getattr(self, mod).width,0).scale(scale[0],0) + for pin in ["Z","vdd","gnd"]: + pin_xy = getattr(getattr(self, mod), pin+"_position") + setattr(self, mod+"_1_"+pin+"_position", base + pin_xy.scale(0,1)) + + def set_Nand3_pins(self,nand_name,nand_scale): + base = getattr(self, "offset_"+nand_name) + extra = vector(0, drc["minwidth_metal1"]* (1 - nand_scale[1]) *0.5) + off1 = base - extra + off2 = base - extra + vector(self.NAND3.width, 0) + self.set_Nand3_pins_sub(nand_name,["A","B","C"],off1,[0,nand_scale[1]]) + self.set_Nand3_pins_sub(nand_name,["Z","vdd","gnd"],off2,[0,nand_scale[1]]) + + def set_Nand3_pins_sub(self,nand_name,pin_lst,base,nand_scale): + for pin in pin_lst: + pin_xy = getattr(self.NAND3, pin+"_position").scale(0,nand_scale[1]) + setattr(self, nand_name+"_"+pin+"_position", base + pin_xy) + + def set_inv2345_pins(self,inv_name,inv_scale): + base_xy = getattr(self, "offset_"+inv_name) + correct= vector(0, (1-inv_scale[1]) * 0.5 * drc["minwidth_metal1"]) + # pin A + pin_xy = vector(0, self.inv4.A_position.y).scale(0,inv_scale[1]) + setattr(self, inv_name+"_A_position", base_xy + pin_xy - correct) + # Z, vdd, gnd + for pin in ["Z_position", "vdd_position", "gnd_position"]: + pin_xy = getattr(self.inv, pin).scale(0,inv_scale[1]) + rotated_pin_xy = vector(self.inv.width * inv_scale[0], 0) + pin_xy + setattr(self, inv_name+"_"+pin, base_xy + rotated_pin_xy - correct) + + def add_msf_control_routing(self): + # FIRST RAIL : MSF_CONTROL OUTPUT RAIL + rail1_start = vector(self.msf_control_WE_position[0], + self.output_port_gap) + for i in range(self.num_rails_1): + correct = vector((i+1) * self.rail_offset_gap, 0) + offset = rail1_start + correct + self.add_rect(layer="metal2", + offset=offset, + width=drc["minwidth_metal2"], + height=self.logic_height) + self.rail_1_x_offsets.append(offset[0]) + + rail2_start_x = (self.nand_array_position[0] + self.NAND3.width + + 3 * self.inv.width + self.rail_offset_gap) + for i in range(self.num_rails_2): + offset = [rail2_start_x + i * self.rail_offset_gap, + self.output_port_gap] + self.add_rect(layer="metal2", + offset=offset, + width=drc["minwidth_metal2"], + height=self.logic_height) + self.rail_2_x_offsets.append(offset[0]) + + def add_1st_row_routing(self): + # First rail routing left + left_side = [] + for pin in ["OE_bar","OE","CS","WE"]: + left_side.append(getattr(self,"msf_control_"+pin+"_position")) + line_indices = [1, 2, 3, 4] + for i in range(len(left_side)): + offset = left_side[i] + line_x_offset = self.rail_1_x_offsets[line_indices[i]] + self.add_rect(layer="metal1", + offset=offset, + width=line_x_offset - offset[0] + drc["minwidth_metal2"], + height=drc["minwidth_metal1"]) + correct1 = vector(self.gap_between_rails, - self.via_shift) + correct2 = vector(self.contact_shift + drc["minwidth_metal2"],0) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset + correct1 - correct2, + rotate=90) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=vector(line_x_offset, offset[1]) + correct1, + rotate=90) + # First rail routing Right + right_side = [] + right_side.append(self.nand2_1_A_position) + right_side.append(self.nand2_1_B_position) + for size in ["1","2"]: + for pin in ["A","B","C"]: + right_side.append(getattr(self,"nand3_"+size+"_"+pin+"_position")) + + line_indices = [2, 5, 5, 2, 3, 5, 4, 3] + for i in range(len(right_side)): + offset = right_side[i] + line_x_offset = self.rail_1_x_offsets[line_indices[i]] + base = vector(line_x_offset, offset[1]) + self.add_rect(layer="metal1", + offset=base, + width=offset[0] - line_x_offset, + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=base + correct1, + rotate=90) + + # OE_bar [Bus # 1] to nor2 B input + layer_stack = ("metal2", "via1", "metal1") + start = self.nor2_1_B_position + mid1 = [self.nor2_1_B_position[0] + 2 * drc["minwidth_metal2"], start[1]] + mid2 = [mid1[0], self.nor2_1_gnd_position[1] - 2 * drc["minwidth_metal1"]] + mid3 = [self.rail_1_x_offsets[1] + 0.5 * drc["minwidth_metal2"], mid2[1]] + end = [mid3[0], self.output_port_gap] + self.add_wire(layer_stack, [start, mid1, mid2, mid3, end]) + + layer_stack = ("metal1") + start = [self.inv1_Z_position[0], self.inv1_Z_position[1] + 0.5 * drc["minwidth_metal1"]] + mid1 = [start[0] + drc["minwidth_metal2"], start[1]] + mid2 = [mid1[0], self.nand2_1_B_position + [1] + 0.5 * drc["minwidth_metal1"]] + end = [self.nand2_1_B_position[0], mid2[1]] + self.add_path(layer_stack, [start, mid1, mid2, end]) + + def add_2nd_row_routing(self): + # Second rail routing + left_side = [] + left_side.append(self.inv2_Z_position) + left_side.append(self.inv7_Z_position) + left_side.append(self.inv5_A_position) + line_indices = [1, 0, 2] + #line_indices = [1,2] + for i in range(len(left_side)): + offset = left_side[i] + line_x_offset = self.rail_2_x_offsets[line_indices[i]] + self.add_rect(layer="metal1", + offset=offset, + width=line_x_offset - offset[0] + drc["minwidth_metal2"], + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[line_x_offset + self.gap_between_rails, + offset[1] - self.via_shift], + rotate=90) + + # Replica bitline (rblk to replica bitline input) + layer_stack = ("metal2", "via1", "metal1") + start = [self.rail_2_x_offsets[1] + 0.5 * drc["minwidth_metal2"], + self.output_port_gap] + mid1 = [start[0], 0.5 * drc["minwidth_metal1"]] + end = [self.replica_en_offset[0], mid1[1]] + + self.add_wire(layer_stack, [start, mid1, end]) + + height = self.replica_en_offset[1] - end[1] + 0.5 * drc["minwidth_metal1"] + + self.add_rect(layer="metal1", + offset=end - vector([0.5 * drc["minwidth_metal1"]] * 2), + width=drc["minwidth_metal1"], + height=height) + + # Replica bitline (replica bitline output to the buffer [inv4,inv5]) + start = [self.rail_2_x_offsets[2], self.replica_out_offset[1]] + end = self.replica_out_offset - vector(0.5 * drc["minwidth_metal1"],0) + self.add_rect(layer="metal3", + offset=start, + width=self.replica_out_offset[0] - self.rail_2_x_offsets[2], + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=start) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=end) + + def add_vdd_routing(self): + """ VDD routing between modules """ + vdd_rail_index = self.num_rails_2 - 1 + rail_2_x = self.rail_2_x_offsets[vdd_rail_index] + + # Connection between nor2 vdd to nand3 vdd + self.add_rect(layer="metal1", + offset=self.nor2_1_vdd_position, + width=rail_2_x + drc["minwidth_metal2"], + height=drc["minwidth_metal1"]) + + # Connection between top AND_Array vdd to the last line on rail2 + self.add_rect(layer="metal1", + offset=self.nand3_2_vdd_position, + width=(rail_2_x + drc["minwidth_metal2"] + - self.nand3_2_vdd_position[0]), + height=drc["minwidth_metal1"]) + + # Connection in horizontal metal2 vdd rail + base = vector(rail_2_x + self.gap_between_rails, + - self.via_shift) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=base + self.nand2_1_vdd_position.scale(0, 1), + rotate=90) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=base + self.nand3_2_vdd_position.scale(0, 1), + rotate=90) + + # Connection of msf_vdd to inv1 vdd + self.add_rect(layer="metal1", + offset=[self.msf_control_vdd_position[0], + self.inv1_vdd_position[1]], + width=drc["minwidth_metal1"], + height=self.msf_control_vdd_position[1] - self.inv1_vdd_position[1]) + + vdd_offset = vector(self.replica_bitline.height,3 * drc["minwidth_metal1"]) + + self.vdd1_position = vdd_offset + self.offset_replica_bitline + self.vdd2_position = vector(rail_2_x, self.output_port_gap) + + def add_gnd_routing(self): + """ GND routing """ + self.gnd_position = self.offset_replica_bitline + + # Connection of msf_control gnds to the metal2 gnd rail + for gnd_offset in self.msf_control_gnd_positions: + self.add_rect(layer="metal2", + offset=gnd_offset, + width=(self.rail_1_x_offsets[0] - gnd_offset[0] + + drc["minwidth_metal2"]), + height=drc["minwidth_metal2"]) + + # Connect msf_control gnd to nand3 gnd + self.add_rect(layer="metal1", + offset=self.nor2_1_gnd_position, + width=self.offset_replica_bitline[0], + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.rail_1_x_offsets[0] + self.gap_between_rails, + self.nor2_1_gnd_position[1] - self.via_shift], + rotate=90) + + # nand3 gnd to replica bitline gnd + self.add_rect(layer="metal1", + offset=self.nand3_2_gnd_position, + width=(self.offset_replica_bitline[0] + - self.nand3_2_gnd_position[0]), + height=drc["minwidth_metal1"]) + + def add_input_routing(self): + """ Input pin routing """ + # WEb, CEb, OEb assign from msf_control pin + self.WEb_position = self.msf_control_WEb_position + self.CSb_position = self.msf_control_CSb_position + self.OEb_position = self.msf_control_OEb_position + + # Clk + clk_y = self.inv1_vdd_position[1] + 6 * drc["minwidth_metal1"] + self.clk_position = vector(0, clk_y) + + # clk port to inv1 A + layer_stack = ("metal2", "via1", "metal1") + start = self.inv1_A_position + vector(0, 0.5 * drc["minwidth_metal1"]) + mid1 = vector(self.inv1_A_position[0] - 2 * drc["minwidth_metal2"], + start.y) + mid2 = vector(mid1.x, clk_y) + self.clk_position = vector(0, mid2[1]) + + self.add_wire(layer_stack, [start, mid1, mid2, self.clk_position]) + + + # clk line to msf_control_clk + self.add_rect(layer="metal1", + offset=[self.msf_control_clk_position[0], + self.clk_position[1]], + width=drc["minwidth_metal1"], + height=(self.msf_control_clk_position[1] + - self.clk_position[1])) + + # clk connection to nor2 A input + start = [self.inv1_A_position[0] - 2 * drc["minwidth_metal2"], + self.inv1_A_position[1] + 0.5 * drc["minwidth_metal1"]] + mid1 = [start[0] - 3 * drc["minwidth_metal2"], start[1]] + mid2 = [mid1[0], self.nor2_1_A_position[1]] + + self.add_path("metal1", [start, mid1, mid2, self.nor2_1_A_position]) + + correct = vector(0, 0.5 * drc["minwidth_metal1"]) + + self.add_via(layers=("metal1", "via1", "metal2"), + offset=self.clk_position + correct, + rotate=270) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=self.clk_position + correct, + rotate=270) + + def add_output_routing(self): + """ Output pin routing """ + # clk_bar + self.clk_bar_position = vector(self.rail_1_x_offsets[self.num_rails_1 - 1], + 0) + self.add_rect(layer="metal2", + offset=self.clk_bar_position, + width=drc["minwidth_metal2"], + height=self.output_port_gap) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=self.clk_bar_position) + + + # tri_en + correct = vector (0, 0.5 * drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=self.nor2_1_Z_position + correct, + rotate=270) + self.add_rect(layer="metal2", + offset=self.nor2_1_Z_position.scale(1, 0), + width=drc["minwidth_metal2"], + height=self.nor2_1_Z_position.y + correct.y) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=self.nor2_1_Z_position.scale(1, 0)) + self.tri_en_position = vector(self.nor2_1_Z_position[0], 0) + + # tri_en_bar + correct = vector(drc["minwidth_metal2"], 0) + self.tri_en_bar_position = self.nand2_1_Z_position.scale(1, 0) - correct + self.add_via(layers=("metal1", "via1", "metal2"), + offset=self.nand2_1_Z_position - correct) + self.add_rect(layer="metal2", + offset=self.tri_en_bar_position, + width=drc["minwidth_metal2"], + height=self.nand2_1_Z_position[1] + drc["minwidth_metal1"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=self.tri_en_bar_position) + + # w_en + self.w_en_position = vector(self.rail_2_x_offsets[0], 0) + self.add_rect(layer="metal2", + offset=self.w_en_position, + width=drc["minwidth_metal2"], + height=self.output_port_gap) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=self.w_en_position) + + # s_en + self.s_en_position = self.inv4_Z_position.scale(1,0) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=self.inv4_Z_position) + self.add_rect(layer="metal2", + offset=self.s_en_position, + width=drc["minwidth_metal2"], + height=self.inv4_Z_position[1] + drc["minwidth_metal1"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=self.s_en_position) diff --git a/compiler/debug.py b/compiler/debug.py new file mode 100644 index 00000000..4bb7fab6 --- /dev/null +++ b/compiler/debug.py @@ -0,0 +1,33 @@ +import os +import inspect +import globals +import sys + +# the debug levels: +# 0 = minimum output (default) +# 1 = major stages +# 2 = verbose +# n = custom setting + + +def error(str,return_value=None): + (frame, filename, line_number, function_name, lines, + index) = inspect.getouterframes(inspect.currentframe())[1] + print "ERROR: file ", os.path.basename(filename), ": line ", line_number, ": ", str + if return_value: + sys.exit(return_value) + +def warning(str): + (frame, filename, line_number, function_name, lines, + index) = inspect.getouterframes(inspect.currentframe())[1] + print "WARNING: file ", os.path.basename(filename), ": line ", line_number, ": ", str + + +def info(lev, str): + OPTS = globals.get_opts() + if (OPTS.debug_level >= lev): + frm = inspect.stack()[1] + mod = inspect.getmodule(frm[0]) + print "\n[", frm[0].f_code.co_name, "]: ", str + # This sometimes gets a NoneType mod... + # print "[" , mod.__name__ , "]: ", str diff --git a/compiler/design.py b/compiler/design.py new file mode 100644 index 00000000..851f29c5 --- /dev/null +++ b/compiler/design.py @@ -0,0 +1,65 @@ +import hierarchy_layout +import hierarchy_spice +import globals +import calibre +import os + +OPTS = globals.get_opts() + +class design(hierarchy_spice.spice, hierarchy_layout.layout): + """ + Design Class for all modules to inherit the base features. + Class consisting of a set of modules and instances of these modules + """ + + def __init__(self, name): + self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds" + self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp" + + self.name = name + hierarchy_layout.layout.__init__(self, name) + hierarchy_spice.spice.__init__(self, name) + + def DRC_LVS(self): + """Checks both DRC and LVS for a module""" + if OPTS.check_lvsdrc: + tempspice = OPTS.openram_temp + "/temp.sp" + tempgds = OPTS.openram_temp + "/temp.gds" + self.sp_write(tempspice) + self.gds_write(tempgds) + assert calibre.run_drc(self.name, tempgds) == 0 + assert calibre.run_lvs(self.name, tempgds, tempspice) == 0 + os.remove(tempspice) + os.remove(tempgds) + + def DRC(self): + """Checks DRC for a module""" + if OPTS.check_lvsdrc: + tempgds = OPTS.openram_temp + "/temp.gds" + self.gds_write(tempgds) + assert calibre.run_drc(self.name, tempgds) == 0 + os.remove(tempgds) + + def LVS(self): + """Checks LVS for a module""" + if OPTS.check_lvsdrc: + tempspice = OPTS.openram_temp + "/temp.sp" + tempgds = OPTS.openram_temp + "/temp.gds" + self.sp_write(tempspice) + self.gds_write(tempgds) + assert calibre.run_lvs(self.name, tempgds, tempspice) == 0 + os.remove(tempspice) + os.remove(tempgds) + + def __str__(self): + """ override print function output """ + return "design: " + self.name + + def __repr__(self): + """ override print function output """ + text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n" + for i in self.objs: + text+=str(i)+",\n" + for i in self.insts: + text+=str(i)+",\n" + return text diff --git a/compiler/example_config.py b/compiler/example_config.py new file mode 100644 index 00000000..1217a677 --- /dev/null +++ b/compiler/example_config.py @@ -0,0 +1,23 @@ +word_size = 1 +num_words = 16 +num_banks = 1 + +tech_name = "freepdk45" + +decoder = "hierarchical_decoder" +ms_flop = "ms_flop" +ms_flop_array = "ms_flop_array" +control_logic = "control_logic" +bitcell_array = "bitcell_array" +sense_amp = "sense_amp" +sense_amp_array = "sense_amp_array" +precharge_array = "precharge_array" +column_mux_array = "single_level_column_mux_array" +write_driver = "write_driver" +write_driver_array = "write_driver_array" +tri_gate = "tri_gate" +tri_gate_array = "tri_gate_array" +wordline_driver = "wordline_driver" +replica_bitcell = "replica_bitcell" +bitcell = "bitcell" +delay_chain = "logic_effort_dc" diff --git a/compiler/gdsMill/LICENSE b/compiler/gdsMill/LICENSE new file mode 100644 index 00000000..6d8a8921 --- /dev/null +++ b/compiler/gdsMill/LICENSE @@ -0,0 +1,78 @@ +Delivered-To: mrg@ucsc.edu +Received: by 10.216.164.197 with SMTP id c47cs36474wel; + Sat, 19 Nov 2011 21:23:28 -0800 (PST) +Received: by 10.216.133.5 with SMTP id p5mr1106280wei.105.1321766513080; + Sat, 19 Nov 2011 21:21:53 -0800 (PST) +Received-SPF: softfail (google.com: best guess record for domain of transitioning wieckows@umich.edu does not designate 128.114.48.32 as permitted sender) client-ip=128.114.48.32; +Received: by 10.241.242.69 with POP3 id 5mf3470160wwf.5; + Sat, 19 Nov 2011 21:21:53 -0800 (PST) +X-Gmail-Fetch-Info: mguthaus@gmail.com 1 smtp.gmail.com 995 mguthaus +Delivered-To: mguthaus@gmail.com +Received: by 10.231.207.15 with SMTP id fw15cs52829ibb; + Thu, 14 Oct 2010 12:49:35 -0700 (PDT) +Received: by 10.142.224.8 with SMTP id w8mr3585480wfg.123.1287085774723; + Thu, 14 Oct 2010 12:49:34 -0700 (PDT) +Return-Path: +Received: from mail-01.cse.ucsc.edu (mail-01.cse.ucsc.edu [128.114.48.32]) + by mx.google.com with ESMTP id x31si25240170wfd.118.2010.10.14.12.49.34; + Thu, 14 Oct 2010 12:49:34 -0700 (PDT) +Received-SPF: neutral (google.com: 128.114.48.32 is neither permitted nor denied by best guess record for domain of wieckows@umich.edu) client-ip=128.114.48.32; +Authentication-Results: mx.google.com; spf=neutral (google.com: 128.114.48.32 is neither permitted nor denied by best guess record for domain of wieckows@umich.edu) smtp.mail=wieckows@umich.edu +Received: from services.cse.ucsc.edu (services.cse.ucsc.edu [128.114.48.10]) + by mail-01.cse.ucsc.edu (Postfix) with ESMTP id 60660100985C + for ; Thu, 14 Oct 2010 12:49:34 -0700 (PDT) +Received: from mailgw.soe.ucsc.edu (mailgw.cse.ucsc.edu [128.114.48.9]) + by services.cse.ucsc.edu (8.13.6/8.13.6) with ESMTP id o9EJnXcg026705 + (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) + for ; Thu, 14 Oct 2010 12:49:34 -0700 (PDT) +X-ASG-Debug-ID: 1287085773-35c0a0820001-k66d7V +Received: from hellskitchen.mr.itd.umich.edu (smtp.mail.umich.edu [141.211.14.82]) by mailgw.soe.ucsc.edu with ESMTP id iCFqYJFhVj1wHPNy for ; Thu, 14 Oct 2010 12:49:33 -0700 (PDT) +X-Barracuda-Envelope-From: wieckows@umich.edu +X-Barracuda-Apparent-Source-IP: 141.211.14.82 +Received: FROM [10.0.1.4] (adsl-99-67-97-169.dsl.sfldmi.sbcglobal.net [99.67.97.169]) + By hellskitchen.mr.itd.umich.edu ID 4CB75ECA.5F7DC.12653 ; + Authuser wieckows; + 14 Oct 2010 15:49:30 EDT +Content-Type: text/plain; charset=us-ascii +Mime-Version: 1.0 (Apple Message framework v1081) +Subject: Re: GDS Mill +From: Michael Wieckowski +X-ASG-Orig-Subj: Re: GDS Mill +In-Reply-To: +Date: Thu, 14 Oct 2010 15:49:29 -0400 +Content-Transfer-Encoding: quoted-printable +Message-Id: <7C1B8C49-7D87-4BF2-8ABF-6555CF7B37AD@umich.edu> +References: +To: Matthew Guthaus +X-Mailer: Apple Mail (2.1081) +X-Barracuda-Connect: smtp.mail.umich.edu[141.211.14.82] +X-Barracuda-Start-Time: 1287085773 +X-Barracuda-URL: http://mailgw.cse.ucsc.edu:8000/cgi-mod/mark.cgi +X-Virus-Scanned: by bsmtpd at soe.ucsc.edu + +Hi Matt, + +Feel free to use / modify / distribute the code as you like. + +-Mike + + +On Oct 14, 2010, at 3:07 PM, Matthew Guthaus wrote: + +> Hi Michael (& Dennis), +>=20 +> A student and I were looking at your GDS tools, but we noticed that = +there is no license. What is the license? +>=20 +> Thanks, +>=20 +> Matt +>=20 +> --- +> Matthew Guthaus +> Assistant Professor, Computer Engineering +> University of California Santa Cruz +> http://vlsida.soe.ucsc.edu/ +>=20 +>=20 +>=20 diff --git a/compiler/gdsMill/README b/compiler/gdsMill/README new file mode 100644 index 00000000..ce21dfdd --- /dev/null +++ b/compiler/gdsMill/README @@ -0,0 +1,30 @@ +README BY TOM GOLUBEV + +gdsMill was adapted from gdsMill by Michael Wieckowski. + +gdsMill allows one to work with GDS files, opening, reading, writing and altering. +It is a library and does not work by itself. To get started, refer to ExampleUserDir. +To look at sram compiler related example, refer to the sram_examples subdir. + +gdsMill takes a layermap, in a standard format (virtuoso .map files). This is necessary +for drawing text and rectangles, since those functions take layernames, and gdsMill needs layer numbers. + + +gdsMill funcionality: + +The main functions are from vlsilayout. They allow creating a new layout, fill in a box, text, and instancing other structures. + +Files: + + sram_examples: + + gdsMill.sh: Adds gdsMill to python path. + Source this before working + + printGDS.py: will dump a text version structures within the gds file. + usage: python printGDS.py file + + cell6tDemo.py: Will tile cell6t from sram_lib2.gds and output into layoutB.gds. All cells from source are copied into layoutB.gds. + usage: python ./cell6tDemo.py + + diff --git a/compiler/gdsMill/exampleUserDir/arrayDemo.py b/compiler/gdsMill/exampleUserDir/arrayDemo.py new file mode 100644 index 00000000..bdabd98b --- /dev/null +++ b/compiler/gdsMill/exampleUserDir/arrayDemo.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +import gdsMill +#we are going to make an array of instances of an existing layout +#assume that we designed the "base cell" in cadence +#step 1 is to stream it out of cadence into a GDS to work with +# creater a streamer object to interact with the cadence libraries +streamer = gdsMill.GdsStreamer() + +# use the streamer to take a cadence layout, and convert it to GDS 2 for us to work with +# the GDS will be named testLayoutA.gds +#streamer.streamFromCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", +# libraryName = "gdsMillTest", +# cellName = "testLayoutA", +# outputPath = "./gdsFiles") + +#next, load our base cell layout from the GDS generated above +arrayCellLayout = gdsMill.VlsiLayout() +reader = gdsMill.Gds2reader(arrayCellLayout) +reader.loadFromFile("./gdsFiles/testLayoutA.gds") + +##since we will be streaming into the same library that testLayout came from +#let's rename it here so that we don't overwrite accidentally later +arrayCellLayout.rename("arrayCell") + +#now create a new layout +#be sure to assign a name, since this will be the root object in our hierarchy to which +#all other objects are referenced +newLayout = gdsMill.VlsiLayout(name="arrayExample") + +#now place an instnace of our top level layout into the filled layout +#hierarchy looks like this: +# array example +# array cell layout +# layout elements +# layout elements +# layout elements +# cell instance +# cell instance +# cell instance +# connection elements ..... + +#now create the array of instances +for xIndex in range(0,10): + for yIndex in range(0,10): + if(yIndex%2 == 0): + mirror = "MX" + else: + mirror = "R0" + newLayout.addInstance(arrayCellLayout, + offsetInMicrons = (xIndex*10.0,yIndex*15.0), + mirror = mirror, + rotate = 0.0) + +#add a "wire" that in a real example might be a power rail, data bus, etc. +newLayout.addPath(layerNumber = newLayout.layerNumbersInUse[7], + coordinates = [(-20.0,0.0),(25.0,0),(25.0,10.0)], + width = 1.0) + +#add some text that in a real example might be an I/O pin +newLayout.addText(text = "Hello", + layerNumber = newLayout.layerNumbersInUse[5], + offsetInMicrons = (0,0), + magnification = 1, + rotate = None) + + +newLayout.prepareForWrite() + +#and now dump the filled layout to a new GDS file +writer = gdsMill.Gds2writer(newLayout) +writer.writeToFile("./gdsFiles/arrayLayout.gds") +#and stream it into cadence +#streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", +# libraryName = "gdsMillTest", +# inputPath = "./gdsFiles/arrayLayout.gds") diff --git a/compiler/gdsMill/exampleUserDir/fillerDemo.py b/compiler/gdsMill/exampleUserDir/fillerDemo.py new file mode 100644 index 00000000..0d581bff --- /dev/null +++ b/compiler/gdsMill/exampleUserDir/fillerDemo.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +import gdsMill + +#we will add the filler at a higher level of hiearchy +#so first, load our top level layout from GDS +myTopLevelLayout = gdsMill.VlsiLayout() +reader = gdsMill.Gds2reader(myTopLevelLayout) +reader.loadFromFile("./gdsFiles/testLayoutA.gds") + +#now create a new layout +#be sure to assign a name, since this will be the root object in our hierarchy to which +#all other objects are referenced +filledLayout = gdsMill.VlsiLayout(name="filledLayout") + +#now place an instnace of our top level layout into the filled layout +#hierarchy looks like this: +# filled layout +# top level layout +# layout elements +# layout elements +# layout elements +# fill elements +# fill elements ..... +filledLayout.addInstance(myTopLevelLayout, + offsetInMicrons = (0,0), + mirror = "", + rotate = 0.0) + +#now actaully add the fill - gds mill will create an array of boxes +# maintaining spacing from existing layout elements +#we'll do it once for two different layers +filledLayout.fillAreaDensity(layerToFill = myTopLevelLayout.layerNumbersInUse[5], + offsetInMicrons = (-10.0,-10.0), #this is where to start from + coverageWidth = 40.0, #size of the fill area in microns + coverageHeight = 40.0, + minSpacing = 0.5, #distance between fill blocks + blockSize = 2.0 #width and height of each filler block in microns + ) +filledLayout.fillAreaDensity(layerToFill = myTopLevelLayout.layerNumbersInUse[7], + offsetInMicrons = (-11.0,-11.0), #this is where to start from + coverageWidth = 40.0, #size of the fill area in microns + coverageHeight = 40.0, + minSpacing = 0.5, #distance between fill blocks + blockSize = 3.0 #width and height of each filler block in microns + ) +#and now dump the filled layout to a new GDS file +writer = gdsMill.Gds2writer(filledLayout) +writer.writeToFile("./gdsFiles/filledLayout.gds") +#and strea +streamer = gdsMill.GdsStreamer() +streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", + libraryName = "gdsMillTest", + inputPath = "./gdsFiles/filledLayout.gds") diff --git a/compiler/gdsMill/exampleUserDir/gdsFiles/testLayoutA.gds b/compiler/gdsMill/exampleUserDir/gdsFiles/testLayoutA.gds new file mode 100644 index 00000000..d3dc5d9f Binary files /dev/null and b/compiler/gdsMill/exampleUserDir/gdsFiles/testLayoutA.gds differ diff --git a/compiler/gdsMill/exampleUserDir/gdsMill.cshrc b/compiler/gdsMill/exampleUserDir/gdsMill.cshrc new file mode 100644 index 00000000..80bc52a8 --- /dev/null +++ b/compiler/gdsMill/exampleUserDir/gdsMill.cshrc @@ -0,0 +1,24 @@ +#----------------------------- +#Need a path to a recent python distribution +#If a recent one is already installed locally, just comment this out +#setenv PATH /some/path/to/python/Python2.6.1/bin\:$PATH + +#----------------------------- +#To do automatic stream IN/OUT, we need a path to PIPO (the cadence stream executable) +#PIPO is usually inside the dfII/bin directory of your cadence installation +#if not used, this can be commented out +#setenv PATH /some/path/to/cadence/ic-5.141_usr5/tools/dfII/bin\:/some/path/to/cadence/ic-5.141_usr5/tools/bin\:$PATH + +#----------------------------- +#This is the search path where Python will find GDSMill +if ( ! ($?PYTHONPATH) ) then + setenv PYTHONPATH /design/common/GDSMill +else + setenv PYTHONPATH /design/common/GDSMill\:$PYTHONPATH +endif + +#----------------------------- +#If you are going to use GDS Mill in conjunction with a particular Cadence work directory, it often makes sense to source the shell script +#required within that directory (for example, a shell script in my work directory sets up permissions to access the design kit files) +source ~/design/600nmAmi/600nmAmi.cshrc + diff --git a/compiler/gdsMill/exampleUserDir/quickStart.py b/compiler/gdsMill/exampleUserDir/quickStart.py new file mode 100644 index 00000000..4d9701ce --- /dev/null +++ b/compiler/gdsMill/exampleUserDir/quickStart.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +import gdsMill + +#creater a streamer object to interact with the cadence libraries +streamer = gdsMill.GdsStreamer() + +#use the streamer to take a cadence layout, and convert it to GDS 2 for us to work with +#the GDS will be named testLayoutA.gds +streamer.streamFromCadence(cadenceLibraryContainerPath = "/soe/bchen12/openram/branches/newptx/freepdk45/openram_temp", + libraryName = "gdsMillTest", + cellName = "testLayoutA", + outputPath = "./gdsFiles") + +#create a layout object - this object represents all of the elements within the layout +myLayout = gdsMill.VlsiLayout() + +#give our layout object to a gds reader object. The gds reader will look at the binary gds 2 file and +#populate the layout object with the file's contents +reader = gdsMill.Gds2reader(myLayout) + +#tell the reader object to process the gds file that we streamed in above +#un-comment the next line to see some details about the gds contents +#reader.debugToTerminal=1 +reader.loadFromFile("./gdsFiles/testLayoutA.gds") + +#our layout object now contains all of the elements of the layout +#let add a box to the layout +myLayout.addBox(layerNumber = myLayout.layerNumbersInUse[0], #pick some layer + offsetInMicrons = (-10,0), #location + width = 10.0, #units are microns + height = 5.0, + # updateInternalMap = True, #This is important for visualization - see note 1 below + center = True) #origin is in the center or the bottom left corner +#Note 1:the layout object keeps track of its elements in a 2D map (no hiearachy) +#this makes visualization more efficient. Therefore, to do a PDF output or some other +#flat output, you need to "update the internal map" of the layout object. Only do this +# ONE TIME after all the objects you are manipulating are fixed + +#let's take out new layout and stream it back into a cadence library +#first, create a new GDS +#we change the root structure name (this will be the cellview name upon stream in) +myLayout.rename("testLayoutB") +writer = gdsMill.Gds2writer(myLayout) +writer.writeToFile("./gdsFiles/testLayoutB.gds") + +streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", + libraryName = "gdsMillTest", + inputPath = "./gdsFiles/testLayoutB.gds") + +#let's create a PDF view of the layout object +#first, create the object to represent the visual output +visualizer = gdsMill.PdfLayout(myLayout) + +#since we have no knowledge of what the layer numbers mean for this particular technology +#we need to assign some colors to them + +#uncomment the following line if you want to actually see the layout numbers in use +#print myLayout.layerNumbersInUse + +#for each layer number used in the layout, we will asign it a layer color as a RGB Hex +visualizer.layerColors[myLayout.layerNumbersInUse[0]]="#219E1C" +visualizer.layerColors[myLayout.layerNumbersInUse[1]]="#271C9E" +visualizer.layerColors[myLayout.layerNumbersInUse[2]]="#CC54C8" +visualizer.layerColors[myLayout.layerNumbersInUse[3]]="#E9C514" +visualizer.layerColors[myLayout.layerNumbersInUse[4]]="#856F00" +visualizer.layerColors[myLayout.layerNumbersInUse[5]]="#BD1444" +visualizer.layerColors[myLayout.layerNumbersInUse[6]]="#FD1444" +visualizer.layerColors[myLayout.layerNumbersInUse[7]]="#FD1414" + +#set the scale so that our PDF isn't enormous +visualizer.setScale(500) +#tell the pdf layout object to draw everything in our layout +visualizer.drawLayout() +#and finally, dump it out to a file +visualizer.writeToFile("./gdsFiles/gdsOut.pdf") + diff --git a/compiler/gdsMill/gdsMill.sh b/compiler/gdsMill/gdsMill.sh new file mode 100755 index 00000000..462dc351 --- /dev/null +++ b/compiler/gdsMill/gdsMill.sh @@ -0,0 +1,10 @@ +#----------------------------- +#To do automatic stream IN/OUT, we need a path to PIPO (the cadence stream executable) +#PIPO is usually inside the dfII/bin directory of your cadence installation +#if not used, this can be commented out +#setenv PATH /some/path/to/cadence/ic-5.141_usr5/tools/dfII/bin\:/some/path/to/cadence/ic-5.141_usr5/tools/bin\:$PATH + +#----------------------------- +#This is the search path where Python will find GDSMill + +export PYTHONPATH=`pwd`/:$PYTHONPATH diff --git a/compiler/gdsMill/gdsMill/__init__.py b/compiler/gdsMill/gdsMill/__init__.py new file mode 100644 index 00000000..6fbb33ee --- /dev/null +++ b/compiler/gdsMill/gdsMill/__init__.py @@ -0,0 +1,13 @@ +""" +Python GDS Mill Package + +GDS Mill is a Python package for the creation and manipulation of binary GDS2 layout files. +""" + +from gds2reader import * +from gds2writer import * +from pdfLayout import * +from vlsiLayout import * +from gdsStreamer import * +from gdsPrimitives import * + diff --git a/compiler/gdsMill/gdsMill/gds2reader.py b/compiler/gdsMill/gdsMill/gds2reader.py new file mode 100644 index 00000000..ab0e94de --- /dev/null +++ b/compiler/gdsMill/gdsMill/gds2reader.py @@ -0,0 +1,820 @@ +#!/usr/bin/env python +import struct +from gdsPrimitives import * + +class Gds2reader: + """Class to read in a file in GDSII format and populate a layout class with it""" + ## Based on info from http://www.rulabinsky.com/cavd/text/chapc.html + global offset + offset=0 + + def __init__(self,layoutObject,debugToTerminal = 0): + self.fileHandle = None + self.layoutObject = layoutObject + self.debugToTerminal=debugToTerminal + + #do we dump debug data to the screen + + def print64AsBinary(self,number): + for index in range(0,64): + print (number>>(63-index))&0x1, + print "\n" + + def strip_non_ascii(self,string): + #''' Returns the string without non ASCII characters''' + stripped = (c for c in string if 0 < ord(c) < 127) + return "".join(stripped) + + def ieeeDoubleFromIbmData(self,ibmData): + #the GDS double is in IBM 370 format like this: + #(1)sign (7)exponent (56)mantissa + #exponent is excess 64, mantissa has no implied 1 + #a normal IEEE double is like this: + #(1)sign (11)exponent (52)mantissa + data = struct.unpack('>q',ibmData)[0] + sign = (data >> 63)&0x01 + exponent = (data >> 56) & 0x7f + mantissa = data<<8 #chop off sign and exponent + + if mantissa == 0: + newFloat = 0.0 + else: + exponent = ((exponent-64)*4)+1023 #convert to double exponent + #re normalize + while mantissa & 0x8000000000000000 == 0: + mantissa<<=1 + exponent-=1 + mantissa<<=1 #remove the assumed high bit + exponent-=1 + #check for underflow error -- should handle these properly! + if(exponent<=0): + print "Underflow Error" + elif(exponent == 2047): + print "Overflow Error" + #re assemble + newFloat=(sign<<63)|(exponent<<52)|((mantissa>>12)&0xfffffffffffff) + asciiDouble = struct.pack('>q',newFloat) + #convert back to double + newFloat = struct.unpack('>d',asciiDouble)[0] + return newFloat + + def ieeeFloatCheck(self,aFloat): + asciiDouble = struct.pack('>d',aFloat) + data = struct.unpack('>q',asciiDouble)[0] + sign = data >> 63 + exponent = ((data >> 52) & 0x7ff)-1023 + # BINWU: Cleanup + print exponent+1023 + mantissa = data << 12 #chop off sign and exponent + # BINWU: Cleanup + #self.print64AsBinary((sign<<63)|((exponent+1023)<<52)|(mantissa>>12)) + asciiDouble = struct.pack('>q',(sign<<63)|(exponent+1023<<52)|(mantissa>>12)) + newFloat = struct.unpack('>d',asciiDouble)[0] + print "Check:"+str(newFloat) + + def readNextRecord(self): + global offset + recordLengthAscii = self.fileHandle.read(2) #first 2 bytes tell us the length of the record + recordLength = struct.unpack(">h",recordLengthAscii) #gives us a tuple with a short int inside + offlist = list(recordLength) #change tuple to a list + offset += float(offlist[0]) #count offset + #print float(offlist[0]) + #print offset #print out the record numbers for de-bugging + record = self.fileHandle.read(recordLength[0]-2) #read the rest of it (first 2 bytes were already read) + return record + + def readHeader(self): + self.layoutObject.info.clear() + ## Header + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x00','\x02') and len(record)==4): + gdsVersion = struct.unpack(">h",record[2]+record[3])[0] + self.layoutObject.info["gdsVersion"]=gdsVersion + if(self.debugToTerminal==1): + print "GDS II Version "+str(gdsVersion) + else: + if(self.debugToTerminal==1): + print "Invalid GDSII Header" + return -1 + + #read records until we hit the UNITS section... this is the last part of the header + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + ## Modified Date + if(idBits==('\x01','\x02') and len(record)==26): + modYear = struct.unpack(">h",record[2]+record[3])[0] + modMonth = struct.unpack(">h",record[4]+record[5])[0] + modDay = struct.unpack(">h",record[6]+record[7])[0] + modHour = struct.unpack(">h",record[8]+record[9])[0] + modMinute = struct.unpack(">h",record[10]+record[11])[0] + modSecond = struct.unpack(">h",record[12]+record[13])[0] + lastAccessYear = struct.unpack(">h",record[14]+record[15])[0] + lastAccessMonth = struct.unpack(">h",record[16]+record[17])[0] + lastAccessDay = struct.unpack(">h",record[18]+record[19])[0] + lastAccessHour = struct.unpack(">h",record[20]+record[21])[0] + lastAccessMinute = struct.unpack(">h",record[22]+record[23])[0] + lastAccessSecond = struct.unpack(">h",record[24]+record[25])[0] + self.layoutObject.info["dates"]=(modYear,modMonth,modDay,modHour,modMinute,modSecond,\ + lastAccessYear,lastAccessMonth,lastAccessDay,lastAccessHour,lastAccessMinute,lastAccessSecond) + if(self.debugToTerminal==1): + print "Date Modified:"+str(modYear)+","+str(modMonth)+","+str(modDay)+","+str(modHour)+","+str(modMinute)+","+str(modSecond) + print "Date Last Accessed:"+str(lastAccessYear)+","+str(lastAccessMonth)+","+str(lastAccessDay)+\ + ","+str(lastAccessHour)+","+str(lastAccessMinute)+","+str(lastAccessSecond) + ## LibraryName + elif(idBits==('\x02','\x06')): + libraryName = record[2::] + self.layoutObject.info["libraryName"]=libraryName + if(self.debugToTerminal==1): + print "Library: "+libraryName + ## reference libraries + elif(idBits==('\x1F','\x06')): + referenceLibraryA = record[2:46] + referenceLibraryB = record[47:91] + self.layoutObject.info["referenceLibraries"]=(referenceLibraryA,referenceLibraryB) + if(self.debugToTerminal==1): + print "Reference Libraries:"+referenceLibraryA+","+referenceLibraryB + elif(idBits==('\x20','\x06')): + fontA = record[2:45] + fontB = record[46:89] + fontC = record[90:133] + fontD = record[134:177] + self.layoutObject.info["fonts"]=(fontA,fontB,fontC,fontD) + if(self.debugToTerminal==1): + print "Fonts:"+fontA+","+fontB+","+fontC+","+fontD + elif(idBits==('\x23','\x06')): + attributeTable = record[2:45] + self.layoutObject.info["attributeTable"]=attributeTable + if(self.debugToTerminal==1): + print "Attributes:"+attributeTable + elif(idBits==('\x22','\x02')): + generations = struct.unpack(">h",record[2]+record[3]) + self.layoutObject.info["generations"]=generations + if(self.debugToTerminal==1): + print "Generations:"+generations + elif(idBits==('\x36','\x02')): + fileFormat = struct.unpack(">h",record[2]+record[3]) + self.layoutObject.info["fileFormat"]=fileFormat + if(self.debugToTerminal==1): + print "File Format:"+fileFormat + elif(idBits==('\x37','\x06')): + mask = record[2::] + self.layoutObject.info["mask"] = mask + if(self.debugToTerminal==1): + print "Mask: "+mask + elif(idBits==('\x03','\x05')): #this is also wrong b/c python doesn't natively have an 8 byte float + userUnits=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9]) + dbUnits=self.ieeeDoubleFromIbmData + self.layoutObject.info["units"] = (userUnits,dbUnits) + + #print "userUnits %s"%((record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9])).encode("hex") + #print "dbUnits %s"%(record[10]+record[11]+record[12]+record[13]+record[14]+record[15]+record[16]+record[17]).encode("hex") + + if(self.debugToTerminal==1): + print "Units: 1 user unit="+str(userUnits)+" database units, 1 database unit="+str(dbUnits)+" meters." + break; + if(self.debugToTerminal==1): + print "End of GDSII Header Found" + return 1 + + def readBoundary(self): + ##reads in a boundary type structure = a filled polygon + thisBoundary=GdsBoundary() + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x26','\x01')): #ELFLAGS + elementFlags = struct.unpack(">h",record[2]+record[3])[0] + thisBoundary.elementFlags=elementFlags + if(self.debugToTerminal==1): + print "\t\tElement Flags: "+str(elementFlags) + elif(idBits==('\x2F','\x03')): #PLEX + plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisBoundary.plex=plex + if(self.debugToTerminal==1): + print "\t\tPLEX: "+str(plex) + elif(idBits==('\x0D','\x02')): #Layer + drawingLayer = struct.unpack(">h",record[2]+record[3])[0] + thisBoundary.drawingLayer=drawingLayer + if drawingLayer not in self.layoutObject.layerNumbersInUse: + self.layoutObject.layerNumbersInUse += [drawingLayer] + if(self.debugToTerminal==1): + print "\t\tDrawing Layer: "+str(drawingLayer) + elif(idBits==('\x16','\x02')): #Purpose + purposeLayer = struct.unpack(">h",record[2]+record[3])[0] + thisBoundary.purposeLayer=purposeLayer + if(self.debugToTerminal==1): + print "\t\tPurpose Layer: "+str(purposeLayer) + elif(idBits==('\x0E','\x02')): #DataType + dataType = struct.unpack(">h",record[2]+record[3])[0] + thisBoundary.dataType=dataType + if(self.debugToTerminal==1): + print "\t\t\tData Type: "+str(dataType) + elif(idBits==('\x10','\x03')): #XY Data Points + numDataPoints = len(record)-2 #packed as XY coordinates 4 bytes each + thisBoundary.coordinates=[] + for index in range(2,numDataPoints+2,8): #incorporate the 2 byte offset + x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0] + y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0] + thisBoundary.coordinates+=[(x,y)] + if(self.debugToTerminal==1): + print "\t\t\tXY Point: "+str(x)+","+str(y) + elif(idBits==('\x11','\x00')): #End Of Element + break; + return thisBoundary + + def readPath(self): #reads in a path structure + thisPath=GdsPath() + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x26','\x01')): #ELFLAGS + elementFlags = struct.unpack(">h",record[2]+record[3])[0] + thisPath.elementFlags=elementFlags + if(self.debugToTerminal==1): + print "\t\tElement Flags: "+str(elementFlags) + elif(idBits==('\x2F','\x03')): #PLEX + plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisPath.plex=plex + if(self.debugToTerminal==1): + print "\t\tPLEX: "+str(plex) + elif(idBits==('\x0D','\x02')): #Layer + drawingLayer = struct.unpack(">h",record[2]+record[3])[0] + thisPath.drawingLayer=drawingLayer + if drawingLayer not in self.layoutObject.layerNumbersInUse: + self.layoutObject.layerNumbersInUse += [drawingLayer] + if(self.debugToTerminal==1): + print "\t\t\tDrawing Layer: "+str(drawingLayer) + elif(idBits==('\x16','\x02')): #Purpose + purposeLayer = struct.unpack(">h",record[2]+record[3])[0] + thisPath.purposeLayer=purposeLayer + if(self.debugToTerminal==1): + print "\t\tPurpose Layer: "+str(purposeLayer) + elif(idBits==('\x21','\x02')): #Path type + pathType = struct.unpack(">h",record[2]+record[3])[0] + thisPath.pathType=pathType + if(self.debugToTerminal==1): + print "\t\t\tPath Type: "+str(pathType) + elif(idBits==('\x0F','\x03')): #Path width + pathWidth = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisPath.pathWidth=pathWidth + if(self.debugToTerminal==1): + print "\t\t\tPath Width: "+str(pathWidth) + elif(idBits==('\x10','\x03')): #XY Data Points + numDataPoints = len(record)-2 #packed as XY coordinates 4 bytes each + thisPath.coordinates=[] + for index in range(2,numDataPoints+2,8): #incorporate the 2 byte offset + x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0] + y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0] + thisPath.coordinates+=[(x,y)] + if(self.debugToTerminal==1): + print "\t\t\tXY Point: "+str(x)+","+str(y) + elif(idBits==('\x11','\x00')): #End Of Element + break; + return thisPath + + def readSref(self): #reads in a reference to another structure + thisSref=GdsSref() + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x26','\x01')): #ELFLAGS + elementFlags = struct.unpack(">h",record[2]+record[3])[0] + thisSref.elementFlags=elementFlags + if(self.debugToTerminal==1): + print "\t\tElement Flags: "+str(elementFlags) + elif(idBits==('\x2F','\x03')): #PLEX + plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisSref.plex=plex + if(self.debugToTerminal==1): + print "\t\tPLEX: "+str(plex) + elif(idBits==('\x12','\x06')): #Reference Name + sName = self.strip_non_ascii(record[2::]) + thisSref.sName=sName.rstrip() + if(self.debugToTerminal==1): + print "\t\tReference Name:"+sName + elif(idBits==('\x1A','\x01')): #Transformation + transFlags = struct.unpack(">H",record[2]+record[3])[0] + mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy + rotateFlag = bool(transFlags&0x0002) + magnifyFlag = bool(transFlags&0x0004) + thisSref.transFlags=(mirrorFlag,rotateFlag,magnifyFlag) + if(self.debugToTerminal==1): + print "\t\t\tMirror X:"+str(mirrorFlag) + print "\t\t\tRotate:"+str(rotateFlag) + print "\t\t\tMagnify:"+str(magnifyFlag) + elif(idBits==('\x1B','\x05')): #Magnify + magFactor=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9]) + thisSref.magFactor=magFactor + if(self.debugToTerminal==1): + print "\t\t\tMagnification:"+str(magFactor) + elif(idBits==('\x1C','\x05')): #Rotate Angle + rotateAngle=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9]) + thisSref.rotateAngle=rotateAngle + if(self.debugToTerminal==1): + print "\t\t\tRotate Angle (CCW):"+str(rotateAngle) + elif(idBits==('\x10','\x03')): #XY Data Points + index=2 + x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0] + y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0] + thisSref.coordinates=(x,y) + if(self.debugToTerminal==1): + print "\t\t\tXY Point: "+str(x)+","+str(y) + elif(idBits==('\x11','\x00')): #End Of Element + break; + return thisSref + + def readAref(self): #an array of references + thisAref = GdsAref() + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x26','\x01')): #ELFLAGS + elementFlags = struct.unpack(">h",record[2]+record[3])[0] + thisAref.elementFlags=elementFlags + if(self.debugToTerminal==1): + print "\t\tElement Flags: "+str(elementFlags) + elif(idBits==('\x2F','\x03')): #PLEX + plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisAref.plex=plex + if(self.debugToTerminal==1): + print "\t\tPLEX: "+str(plex) + elif(idBits==('\x12','\x06')): #Reference Name + aName = record[2::] + thisAref.aName=aName + if(self.debugToTerminal==1): + print "\t\tReference Name:"+aName + elif(idBits==('\x1A','\x01')): #Transformation + transFlags = struct.unpack(">H",record[2]+record[3])[0] + mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy + rotateFlag = bool(transFlags&0x0002) + magnifyFlag = bool(transFlags&0x0004) + thisAref.transFlags=(mirrorFlag,rotateFlag,magnifyFlag) + if(self.debugToTerminal==1): + print "\t\t\tMirror X:"+str(mirrorFlag) + print "\t\t\tRotate:"+str(rotateFlag) + print "\t\t\tMagnify:"+str(magnifyFlag) + elif(idBits==('\x1B','\x05')): #Magnify + magFactor=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9]) + thisAref.magFactor=magFactor + if(self.debugToTerminal==1): + print "\t\t\tMagnification:"+str(magFactor) + elif(idBits==('\x1C','\x05')): #Rotate Angle + rotateAngle=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9]) + thisAref.rotateAngle=rotateAngle + if(self.debugToTerminal==1): + print "\t\t\tRotate Angle (CCW):"+str(rotateAngle) + elif(idBits==('\x10','\x03')): #XY Data Points + index=2 + topLeftX=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0] + topLeftY=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0] + rightMostX=struct.unpack(">i",record[index+8]+record[index+9]+record[index+10]+record[index+11])[0] + bottomMostY=struct.unpack(">i",record[index+12]+record[index+13]+record[index+14]+record[index+15])[0] + thisAref.coordinates=[(topLeftX,topLeftY),(rightMostX,topLeftY),(topLeftX,bottomMostY)] + if(self.debugToTerminal==1): + print "\t\t\tTop Left Point: "+str(topLeftX)+","+str(topLeftY) + print "\t\t\t\tArray Width: "+str(rightMostX-topLeftX) + print "\t\t\t\tArray Height: "+str(topLeftY-bottomMostY) + elif(idBits==('\x11','\x00')): #End Of Element + break; + return thisAref + + def readText(self): + ##reads in a text structure + thisText=GdsText() + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x26','\x01')): #ELFLAGS + elementFlags = struct.unpack(">h",record[2]+record[3])[0] + thisText.elementFlags=elementFlags + if(self.debugToTerminal==1): + print "\t\tElement Flags: "+str(elementFlags) + elif(idBits==('\x2F','\x03')): #PLEX + plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisText.plex=plex + if(self.debugToTerminal==1): + print "\t\tPLEX: "+str(plex) + elif(idBits==('\x0D','\x02')): #Layer + drawingLayer = struct.unpack(">h",record[2]+record[3])[0] + thisText.drawingLayer=drawingLayer + if drawingLayer not in self.layoutObject.layerNumbersInUse: + self.layoutObject.layerNumbersInUse += [drawingLayer] + if(self.debugToTerminal==1): + print "\t\tDrawing Layer: "+str(drawingLayer) + elif(idBits==('\x16','\x02')): #Purpose + purposeLayer = struct.unpack(">h",record[2]+record[3])[0] + thisText.purposeLayer=purposeLayer + if(self.debugToTerminal==1): + print "\t\tPurpose Layer: "+str(purposeLayer) + elif(idBits==('\x1A','\x01')): #Transformation + transFlags = struct.unpack(">H",record[2]+record[3])[0] + mirrorFlag = bool(transFlags&0x8000) ##these flags are a bit sketchy + rotateFlag = bool(transFlags&0x0002) + magnifyFlag = bool(transFlags&0x0004) + thisText.transFlags=(mirrorFlag,rotateFlag,magnifyFlag) + if(self.debugToTerminal==1): + print "\t\t\tMirror X:"+str(mirrorFlag) + print "\t\t\tRotate:"+str(rotateFlag) + print "\t\t\tMagnify:"+str(magnifyFlag) + elif(idBits==('\x1B','\x05')): #Magnify + magFactor=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9]) + thisText.magFactor=magFactor + if(self.debugToTerminal==1): + print "\t\t\tMagnification:"+str(magFactor) + elif(idBits==('\x1C','\x05')): #Rotate Angle + rotateAngle=self.ieeeDoubleFromIbmData(record[2]+record[3]+record[4]+record[5]+record[6]+record[7]+record[8]+record[9]) + thisText.rotateAngle=rotateAngle + if(self.debugToTerminal==1): + print "\t\t\tRotate Angle (CCW):"+str(rotateAngle) + elif(idBits==('\x21','\x02')): #Path type + pathType = struct.unpack(">h",record[2]+record[3])[0] + thisText.pathType=pathType + if(self.debugToTerminal==1): + print "\t\t\tPath Type: "+str(pathType) + elif(idBits==('\x0F','\x03')): #Path width + pathWidth = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisText.pathWidth=pathWidth + if(self.debugToTerminal==1): + print "\t\t\tPath Width: "+str(pathWidth) + elif(idBits==('\x1A','\x01')): #Text Presentation + presentationFlags = struct.unpack(">H",record[2]+record[3])[0] + font = (presentationFlags&0x0030)>>4 ##these flags are a bit sketchy + verticalFlags = (presentationFlags&0x000C) + horizontalFlags = (presentationFlags&0x0003) + thisText.presentationFlags=(font,verticalFlags,horizontalFlags) + if(self.debugToTerminal==1): + print "\t\t\tFont:"+str(font) + if(verticalFlags==0): + if(self.debugToTerminal==1): + print "\t\t\tVertical: Top" + elif(verticalFlags==1): + if(self.debugToTerminal==1): + print "\t\t\tVertical: Middle" + elif(verticalFlags==2): + if(self.debugToTerminal==1): + print "\t\t\tVertical: Bottom" + if(horizontalFlags==0): + if(self.debugToTerminal==1): + print "\t\t\tHorizontal: Left" + elif(horizontalFlags==1): + if(self.debugToTerminal==1): + print "\t\t\tHorizontal: Center" + elif(horizontalFlags==2): + if(self.debugToTerminal==1): + print "\t\t\tHorizontal: Right" + elif(idBits==('\x10','\x03')): #XY Data Points + index=2 + x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0] + y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0] + thisText.coordinates=[(x,y)] + if(self.debugToTerminal==1): + print "\t\t\tXY Point: "+str(x)+","+str(y) + elif(idBits==('\x19','\x06')): #Text String - also the last record in this element + textString = record[2::] + thisText.textString=textString + if(self.debugToTerminal==1): + print "\t\t\tText String: "+textString + elif(idBits==('\x11','\x00')): #End Of Element + break; + return thisText + + def readNode(self): + ##reads in a node type structure = an electrical net + thisNode = GdsNode() + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x26','\x01')): #ELFLAGS + elementFlags = struct.unpack(">h",record[2]+record[3])[0] + thisNode.elementFlags=elementFlags + if(self.debugToTerminal==1): + print "\t\tElement Flags: "+str(elementFlags) + elif(idBits==('\x2F','\x03')): #PLEX + plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisNode.plex=plex + if(self.debugToTerminal==1): + print "\t\tPLEX: "+str(plex) + elif(idBits==('\x0D','\x02')): #Layer + drawingLayer = struct.unpack(">h",record[2]+record[3])[0] + thisNode.drawingLayer=drawingLayer + if drawingLayer not in self.layoutObject.layerNumbersInUse: + self.layoutObject.layerNumbersInUse += [drawingLayer] + if(self.debugToTerminal==1): + print "\t\tDrawing Layer: "+str(drawingLayer) + elif(idBits==('\x2A','\x02')): #Node Type + nodeType = struct.unpack(">h",record[2]+record[3])[0] + thisNode.nodeType=nodeType + if(self.debugToTerminal==1): + print "\t\tNode Type: "+str(nodeType) + elif(idBits==('\x10','\x03')): #XY Data Points + numDataPoints = len(record)-2 #packed as XY coordinates 4 bytes each + thisNode.coordinates=[] + for index in range(2,numDataPoints+2,8): #incorporate the 2 byte offset + x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0] + y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0] + thisNode.coordinates+=[(x,y)] + if(self.debugToTerminal==1): + print "\t\t\tXY Point: "+str(x)+","+str(y) + elif(idBits==('\x11','\x00')): #End Of Element + break; + return thisNode + + def readBox(self): + ##reads in a gds BOX structure + thisBox = GdsBox() + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x26','\x01')): #ELFLAGS + elementFlags = struct.unpack(">h",record[2]+record[3]) + thisBox.elementFlags=elementFlags + if(self.debugToTerminal==1): + print "\t\tElement Flags: "+str(elementFlags) + elif(idBits==('\x2F','\x03')): #PLEX + plex = struct.unpack(">i",record[2]+record[3]+record[4]+record[5])[0] + thisBox.plex=plex + if(self.debugToTerminal==1): + print "\t\tPLEX: "+str(plex) + elif(idBits==('\x0D','\x02')): #Layer + drawingLayer = struct.unpack(">h",record[2]+record[3])[0] + thisBox.drawingLayer=drawingLayer + if drawingLayer not in self.layoutObject.layerNumbersInUse: + self.layoutObject.layerNumbersInUse += [drawingLayer] + if(self.debugToTerminal==1): + print "\t\tDrawing Layer: "+str(drawingLayer) + elif(idBits==('\x16','\x02')): #Purpose + purposeLayer = struct.unpack(">h",record[2]+record[3])[0] + thisBox.purposeLayer=purposeLayer + if(self.debugToTerminal==1): + print "\t\tPurpose Layer: "+str(purposeLayer) + elif(idBits==('\x2D','\x00')): #Box + boxValue = struct.unpack(">h",record[2]+record[3])[0] + thisBox.boxValue=boxValue + if(self.debugToTerminal==1): + print "\t\tBox Value: "+str(boxValue) + elif(idBits==('\x10','\x03')): #XY Data Points that form a closed box + numDataPoints = len(record)-2 #packed as XY coordinates 4 bytes each + thisBox.coordinates=[] + for index in range(2,numDataPoints+2,8): #incorporate the 2 byte offset + x=struct.unpack(">i",record[index]+record[index+1]+record[index+2]+record[index+3])[0] + y=struct.unpack(">i",record[index+4]+record[index+5]+record[index+6]+record[index+7])[0] + thisBox.coordinates+=[(x,y)] + if(self.debugToTerminal==1): + print "\t\t\tXY Point: "+str(x)+","+str(y) + elif(idBits==('\x11','\x00')): #End Of Element + break; + return thisBox + + def readNextStructure(self): + thisStructure = GdsStructure() + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x05','\x02') and len(record)==26): + createYear = struct.unpack(">h",record[2]+record[3])[0] + createMonth = struct.unpack(">h",record[4]+record[5])[0] + createDay = struct.unpack(">h",record[6]+record[7])[0] + createHour = struct.unpack(">h",record[8]+record[9])[0] + createMinute = struct.unpack(">h",record[10]+record[11])[0] + createSecond = struct.unpack(">h",record[12]+record[13])[0] + modYear = struct.unpack(">h",record[14]+record[15])[0] + modMonth = struct.unpack(">h",record[16]+record[17])[0] + modDay = struct.unpack(">h",record[18]+record[19])[0] + modHour = struct.unpack(">h",record[20]+record[21])[0] + modMinute = struct.unpack(">h",record[22]+record[23])[0] + modSecond = struct.unpack(">h",record[24]+record[25])[0] + thisStructure.createDate=(createYear,createMonth,createDay,createHour,createMinute,createSecond) + thisStructure.modDate=(modYear,modMonth,modDay,modHour,modMinute,modSecond) + else: + #means we have hit the last structure, so return the record + #to whoever called us to do something with it + return record + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if idBits==('\x07','\x00'): break; #we've reached the end of the structure + elif(idBits==('\x06','\x06')): + structName = self.strip_non_ascii(record[2::]) #(record[2:1] + record[1::]).rstrip() +# print ''.[x for x in structName if ord(x) < 128] +# stripped = (c for c in structName if 0 < ord(c) < 127) +# structName = "".join(stripped) +# print self.strip_non_ascii(structName) ##FIXME: trimming by Tom g. ##could be an issue here with string trimming! + thisStructure.name = structName + if(self.debugToTerminal==1): + print "\tStructure Name: "+structName + elif(idBits==('\x08','\x00')): + thisStructure.boundaries+=[self.readBoundary()] + elif(idBits==('\x09','\x00')): + thisStructure.paths+=[self.readPath()] + elif(idBits==('\x0A','\x00')): + thisStructure.srefs+=[self.readSref()] + elif(idBits==('\x0B','\x00')): + thisStructure.arefs+=[self.readAref()] + elif(idBits==('\x0C','\x00')): + thisStructure.texts+=[self.readText()] + elif(idBits==('\x15','\x00')): + thisStructure.nodes+=[self.readNode()] + elif(idBits==('\x2E','\x02')): + thisStructure.boxes+=[self.readBox()] + if(self.debugToTerminal==1): + print "\tEnd of Structure." + self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object + return 1 + + def readGds2(self): + if(self.readHeader()): #did the header read ok? + record = self.readNextStructure() + while(record == 1): + record = self.readNextStructure() + #now we have fallen out of the while, which means we are out of structures + #so test for end of library + if(len(record)>1): + idBits = (record[0],record[1]) + if idBits==('\x04','\x00'): #we've reached the end of the library + if(self.debugToTerminal==1): + print "End of GDS Library." + else: + print "There was an error reading the structure list." + else: + print "There was an error parsing the GDS header. Aborting..." + + def loadFromFile(self, fileName): + self.fileHandle = open(fileName,"rb") + self.readGds2() + self.fileHandle.close() + self.layoutObject.initialize() + +############################################## + + def findStruct(self,fileName,findStructName): + #print"find struct" + self.fileHandle = open(fileName,"rb") + self.debugToTerminal=0 + if(self.readHeader()): #did the header read ok? + record = self.findStruct_readNextStruct(findStructName) + while(record == 1): + record = self.findStruct_readNextStruct(findStructName) + #now we have fallen out of the while, which means we are out of structures + #so test for end of library + else: + print "There was an error parsing the GDS header. Aborting..." + self.fileHandle.close() + #print "End the search of",findStructName + #self.layoutObject.initialize() + return record + + def findStruct_readNextStruct(self,findStructName): + self.debugToTerminal=0 + thisStructure = GdsStructure() + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x05','\x02') and len(record)==26): + createYear = struct.unpack(">h",record[2]+record[3])[0] + createMonth = struct.unpack(">h",record[4]+record[5])[0] + createDay = struct.unpack(">h",record[6]+record[7])[0] + createHour = struct.unpack(">h",record[8]+record[9])[0] + createMinute = struct.unpack(">h",record[10]+record[11])[0] + createSecond = struct.unpack(">h",record[12]+record[13])[0] + modYear = struct.unpack(">h",record[14]+record[15])[0] + modMonth = struct.unpack(">h",record[16]+record[17])[0] + modDay = struct.unpack(">h",record[18]+record[19])[0] + modHour = struct.unpack(">h",record[20]+record[21])[0] + modMinute = struct.unpack(">h",record[22]+record[23])[0] + modSecond = struct.unpack(">h",record[24]+record[25])[0] + thisStructure.createDate=(createYear,createMonth,createDay,createHour,createMinute,createSecond) + thisStructure.modDate=(modYear,modMonth,modDay,modHour,modMinute,modSecond) + else: + #means we have hit the last structure, so return the record + #to whoever called us to do something with it + return record + wantedStruct=0 + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if idBits==('\x07','\x00'): break; #we've reached the end of the structure + elif(idBits==('\x06','\x06')): + structName = self.strip_non_ascii(record[2::]) #(record[2:1] + record[1::]).rstrip() +# print ''.[x for x in structName if ord(x) < 128] +# stripped = (c for c in structName if 0 < ord(c) < 127) +# structName = "".join(stripped) +# print self.strip_non_ascii(structName) ##FIXME: trimming by Tom g. ##could be an issue here with string trimming! + thisStructure.name = structName + if(findStructName==thisStructure.name): + wantedStruct=1 + if(self.debugToTerminal==1): + print "\tStructure Name: "+structName + elif(idBits==('\x08','\x00')): + thisStructure.boundaries+=[self.readBoundary()] + elif(idBits==('\x09','\x00')): + thisStructure.paths+=[self.readPath()] + elif(idBits==('\x0A','\x00')): + thisStructure.srefs+=[self.readSref()] + elif(idBits==('\x0B','\x00')): + thisStructure.arefs+=[self.readAref()] + elif(idBits==('\x0C','\x00')): + thisStructure.texts+=[self.readText()] + elif(idBits==('\x15','\x00')): + thisStructure.nodes+=[self.readNode()] + elif(idBits==('\x2E','\x02')): + thisStructure.boxes+=[self.readBox()] + if(self.debugToTerminal==1): + print "\tEnd of Structure." + self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object + if(wantedStruct == 0): + return 1 + else: + #print "\tDone with collectting bound. Return" + return [0,thisStructure.boundaries] + + def findLabel(self,fileName,findLabelName): + #print"find Label" + self.fileHandle = open(fileName,"rb") + self.debugToTerminal=0 + if(self.readHeader()): #did the header read ok? + record = self.findLabel_readNextStruct(findLabelName) + while(record == 1): + record = self.findLabel_readNextStruct(findLabelName) + #now we have fallen out of the while, which means we are out of structures + #so test for end of library + else: + print "There was an error parsing the GDS header. Aborting..." + self.fileHandle.close() + #print "End the search of",findStructName + #self.layoutObject.initialize() + return record + + def findLabel_readNextStruct(self,findLabelName): + self.debugToTerminal=0 + thisStructure = GdsStructure() + record = self.readNextRecord() + idBits = (record[0],record[1]) + if(idBits==('\x05','\x02') and len(record)==26): + createYear = struct.unpack(">h",record[2]+record[3])[0] + createMonth = struct.unpack(">h",record[4]+record[5])[0] + createDay = struct.unpack(">h",record[6]+record[7])[0] + createHour = struct.unpack(">h",record[8]+record[9])[0] + createMinute = struct.unpack(">h",record[10]+record[11])[0] + createSecond = struct.unpack(">h",record[12]+record[13])[0] + modYear = struct.unpack(">h",record[14]+record[15])[0] + modMonth = struct.unpack(">h",record[16]+record[17])[0] + modDay = struct.unpack(">h",record[18]+record[19])[0] + modHour = struct.unpack(">h",record[20]+record[21])[0] + modMinute = struct.unpack(">h",record[22]+record[23])[0] + modSecond = struct.unpack(">h",record[24]+record[25])[0] + thisStructure.createDate=(createYear,createMonth,createDay,createHour,createMinute,createSecond) + thisStructure.modDate=(modYear,modMonth,modDay,modHour,modMinute,modSecond) + else: + #means we have hit the last structure, so return the record + #to whoever called us to do something with it + return record + wantedLabel=0 + wantedtexts=[GdsText()] + while 1: + record = self.readNextRecord() + idBits = (record[0],record[1]) + if idBits==('\x07','\x00'): break; #we've reached the end of the structure + elif(idBits==('\x06','\x06')): + structName = self.strip_non_ascii(record[2::]) #(record[2:1] + record[1::]).rstrip() +# print ''.[x for x in structName if ord(x) < 128] +# stripped = (c for c in structName if 0 < ord(c) < 127) +# structName = "".join(stripped) +# print self.strip_non_ascii(structName) ##FIXME: trimming by Tom g. ##could be an issue here with string trimming! + thisStructure.name = structName + if(self.debugToTerminal==1): + print "\tStructure Name: "+structName + elif(idBits==('\x08','\x00')): + thisStructure.boundaries+=[self.readBoundary()] + elif(idBits==('\x09','\x00')): + thisStructure.paths+=[self.readPath()] + elif(idBits==('\x0A','\x00')): + thisStructure.srefs+=[self.readSref()] + elif(idBits==('\x0B','\x00')): + thisStructure.arefs+=[self.readAref()] + elif(idBits==('\x0C','\x00')): + label=self.readText() + #Be careful: label.textString contains one space string in it. Delete that one before use it + if( findLabelName == label.textString[0:(len(label.textString)-1)] ): + wantedLabel=1 + # BINWU: Cleanup + #print"Find the Label",findLabelName + wantedtexts+=[label] + thisStructure.texts+=[label] + if(self.debugToTerminal == 1): + print label.textString[0:(len(label.textString)-1)],findLabelName,( findLabelName == label.textString[0:(len(label.textString)-1)] ) + # BINWU: Cleanup + #print thisStructure.name + #print thisStructure.texts + elif(idBits==('\x15','\x00')): + thisStructure.nodes+=[self.readNode()] + elif(idBits==('\x2E','\x02')): + thisStructure.boxes+=[self.readBox()] + if(self.debugToTerminal==1): + print "\tEnd of Structure." + self.layoutObject.structures[structName]=thisStructure #add this structure to the layout object + if(wantedLabel == 0): + return 1 + else: + #print "\tDone with collectting bound. Return" + return [0,wantedtexts] + diff --git a/compiler/gdsMill/gdsMill/gds2writer.py b/compiler/gdsMill/gdsMill/gds2writer.py new file mode 100644 index 00000000..d2b98f99 --- /dev/null +++ b/compiler/gdsMill/gdsMill/gds2writer.py @@ -0,0 +1,540 @@ +#!/usr/bin/env python +import struct +from gdsPrimitives import * + +class Gds2writer: + """Class to take a populated layout class and write it to a file in GDSII format""" + ## Based on info from http://www.rulabinsky.com/cavd/text/chapc.html + + def __init__(self,layoutObject): + self.fileHandle = 0 + self.layoutObject = layoutObject + self.debugToTerminal=0 #do we dump debug data to the screen + + def print64AsBinary(self,number): + #debugging method for binary inspection + for index in range(0,64): + print (number>>(63-index))&0x1, + print "\n" + + def ieeeDoubleFromIbmData(self,ibmData): + #the GDS double is in IBM 370 format like this: + #(1)sign (7)exponent (56)mantissa + #exponent is excess 64, mantissa has no implied 1 + #a normal IEEE double is like this: + #(1)sign (11)exponent (52)mantissa + data = struct.unpack('>q',ibmData)[0] + sign = (data >> 63)&0x01 + exponent = (data >> 56) & 0x7f + mantissa = data<<8 #chop off sign and exponent + + if mantissa == 0: + newFloat = 0.0 + else: + exponent = ((exponent-64)*4)+1023 #convert to double exponent + #re normalize + while mantissa & 0x8000000000000000 == 0: + mantissa<<=1 + exponent-=1 + mantissa<<=1 #remove the assumed high bit + exponent-=1 + #check for underflow error -- should handle these properly! + if(exponent<=0): + print "Underflow Error" + elif(exponent == 2047): + print "Overflow Error" + #re assemble + newFloat=(sign<<63)|(exponent<<52)|((mantissa>>12)&0xfffffffffffff) + asciiDouble = struct.pack('>q',newFloat) + #convert back to double + newFloat = struct.unpack('>d',asciiDouble)[0] + return newFloat + + def ibmDataFromIeeeDouble(self,ieeeDouble): + asciiDouble = struct.pack('>d',ieeeDouble) + data = struct.unpack('>q',asciiDouble)[0] + sign = (data >> 63) & 0x01 + exponent = ((data >> 52) & 0x7ff)-1023 + mantissa = data << 12 #chop off sign and exponent + if(ieeeDouble == 0): + mantissa = 0 + exponent = 0 + sign = 0 + else: + #add back the assumed digit + mantissa >>= 1 + mantissa = mantissa|0x8000000000000000 + exponent += 1 + #convert the exponent + #need to do this in a loop to prevent sign extension! + for index in range (0,-exponent&3): + mantissa >>= 1 + mantissa = mantissa & 0x7fffffffffffffff + + exponent = (exponent+3) >> 2 + exponent+=64 + + newFloat =(sign<<63)|(exponent<<56)|((mantissa>>8)&0xffffffffffffff) + asciiDouble = struct.pack('>q',newFloat) + return asciiDouble + + def ieeeFloatCheck(self,aFloat): + #debugging method for float construction + asciiDouble = struct.pack('>d',aFloat) + data = struct.unpack('>q',asciiDouble)[0] + sign = data >> 63 + exponent = ((data >> 52) & 0x7ff)-1023 + print exponent+1023 + mantissa = data << 12 #chop off sign and exponent + #self.print64AsBinary((sign<<63)|((exponent+1023)<<52)|(mantissa>>12)) + asciiDouble = struct.pack('>q',(sign<<63)|(exponent+1023<<52)|(mantissa>>12)) + newFloat = struct.unpack('>d',asciiDouble)[0] + print "Check:"+str(newFloat) + + def writeRecord(self,record): + recordLength = len(record)+2 #make sure to include this in the length + recordLengthAscii=struct.pack(">h",recordLength) + self.fileHandle.write(recordLengthAscii+record) + + def writeHeader(self): + ## Header + if("gdsVersion" in self.layoutObject.info): + idBits='\x00\x02' + gdsVersion = struct.pack(">h",self.layoutObject.info["gdsVersion"]) + self.writeRecord(idBits+gdsVersion) + ## Modified Date + if("dates" in self.layoutObject.info): + idBits='\x01\x02' + modYear = struct.pack(">h",self.layoutObject.info["dates"][0]) + modMonth = struct.pack(">h",self.layoutObject.info["dates"][1]) + modDay = struct.pack(">h",self.layoutObject.info["dates"][2]) + modHour = struct.pack(">h",self.layoutObject.info["dates"][3]) + modMinute = struct.pack(">h",self.layoutObject.info["dates"][4]) + modSecond = struct.pack(">h",self.layoutObject.info["dates"][5]) + lastAccessYear = struct.pack(">h",self.layoutObject.info["dates"][6]) + lastAccessMonth = struct.pack(">h",self.layoutObject.info["dates"][7]) + lastAccessDay = struct.pack(">h",self.layoutObject.info["dates"][8]) + lastAccessHour = struct.pack(">h",self.layoutObject.info["dates"][9]) + lastAccessMinute = struct.pack(">h",self.layoutObject.info["dates"][10]) + lastAccessSecond = struct.pack(">h",self.layoutObject.info["dates"][11]) + self.writeRecord(idBits+modYear+modMonth+modDay+modHour+modMinute+modSecond+\ + lastAccessYear+lastAccessMonth+lastAccessDay+lastAccessHour+\ + lastAccessMinute+lastAccessSecond) + ## LibraryName + if("libraryName" in self.layoutObject.info): + idBits='\x02\x06' + libraryName = self.layoutObject.info["libraryName"] + self.writeRecord(idBits+libraryName) + ## reference libraries + if("referenceLibraries" in self.layoutObject.info): + idBits='\x1F\x06' + referenceLibraryA = self.layoutObject.info["referenceLibraries"][0] + referenceLibraryB = self.layoutObject.info["referenceLibraries"][1] + self.writeRecord(idBits+referenceLibraryA+referenceLibraryB) + if("fonts" in self.layoutObject.info): + idBits='\x20\x06' + fontA = self.layoutObject.info["fonts"][0] + fontB = self.layoutObject.info["fonts"][1] + fontC = self.layoutObject.info["fonts"][2] + fontD = self.layoutObject.info["fonts"][3] + self.writeRecord(idBits+fontA+fontB+fontC+fontD) + if("attributeTable" in self.layoutObject.info): + idBits='\x23\x06' + attributeTable = self.layoutObject.info["attributeTable"] + self.writeRecord(idBits+attributeTable) + if("generations" in self.layoutObject.info): + idBits='\x22\x02' + generations = struct.pack(">h",self.layoutObject.info["generations"]) + self.writeRecord(idBits+generations) + if("fileFormat" in self.layoutObject.info): + idBits='\x36\x02' + fileFormat = struct.pack(">h",self.layoutObject.info["fileFormat"]) + self.writeRecord(idBits+fileFormat) + if("mask" in self.layoutObject.info): + idBits='\x37\x06' + mask = self.layoutObject.info["mask"] + self.writeRecord(idBits+mask) + if("units" in self.layoutObject.info): + idBits='\x03\x05' + userUnits=self.ibmDataFromIeeeDouble(self.layoutObject.info["units"][0]) + dbUnits=self.ibmDataFromIeeeDouble((self.layoutObject.info["units"][0]*1e-6/self.layoutObject.info["units"][1])*self.layoutObject.info["units"][1]) + + #User Units are hardcoded, since the floating point implementation of gdsMill is not adequate, + #resulting in a different value being written in output stream. Hardcoded to sram compiler's outputed gds units. + #db="39225c17d04dad2a" + #uu="3e20c49ba5e353f8" + + #userUnits="3e20c49ba5e353f8".decode("hex") + #dbUnits="39225c17d04dad2a".decode("hex") + + #dbUnits="39225c17d04dad2a".decode("hex") + #db=39225c17d04dad2a + + + self.writeRecord(idBits+userUnits+dbUnits) + if(self.debugToTerminal==1): + print "writer: userUnits %s"%(userUnits.encode("hex")) + print "writer: dbUnits %s"%(dbUnits.encode("hex")) + #self.ieeeFloatCheck(1.3e-6) + + print "End of GDSII Header Written" + return 1 + + def writeBoundary(self,thisBoundary): + idBits = '\x08\x00' #record Type + self.writeRecord(idBits) + if(thisBoundary.elementFlags!=""): + idBits='\x26\x01' #ELFLAGS + elementFlags = struct.pack(">h",thisBoundary.elementFlags) + self.writeRecord(idBits+elementFlags) + if(thisBoundary.plex!=""): + idBits='\x2F\x03' #PLEX + plex = struct.pack(">i",thisBoundary.plex) + self.writeRecord(idBits+plex) + if(thisBoundary.drawingLayer!=""): + idBits='\x0D\x02' #drawig layer + drawingLayer = struct.pack(">h",thisBoundary.drawingLayer) + self.writeRecord(idBits+drawingLayer) + if(thisBoundary.purposeLayer): + idBits='\x16\x02' #purpose layer + purposeLayer = struct.pack(">h",thisBoundary.purposeLayer) + self.writeRecord(idBits+purposeLayer) + if(thisBoundary.dataType!=""): + idBits='\x0E\x02'#DataType + dataType = struct.pack(">h",thisBoundary.dataType) + self.writeRecord(idBits+dataType) + if(thisBoundary.coordinates!=""): + idBits='\x10\x03' #XY Data Points + coordinateRecord = idBits + for coordinate in thisBoundary.coordinates: + x=struct.pack(">i",coordinate[0]) + y=struct.pack(">i",coordinate[1]) + coordinateRecord+=x + coordinateRecord+=y + self.writeRecord(coordinateRecord) + idBits='\x11\x00' #End Of Element + coordinateRecord = idBits + self.writeRecord(coordinateRecord) + + def writePath(self,thisPath): #writes out a path structure + idBits = '\x09\x00' #record Type + self.writeRecord(idBits) + if(thisPath.elementFlags != ""): + idBits='\x26\x01' #ELFLAGS + elementFlags = struct.pack(">h",thisPath.elementFlags) + self.writeRecord(idBits+elementFlags) + if(thisPath.plex!=""): + idBits='\x2F\x03' #PLEX + plex = struct.pack(">i",thisPath.plex) + self.writeRecord(idBits+plex) + if(thisPath.drawingLayer): + idBits='\x0D\x02' #drawig layer + drawingLayer = struct.pack(">h",thisPath.drawingLayer) + self.writeRecord(idBits+drawingLayer) + if(thisPath.purposeLayer): + idBits='\x16\x02' #purpose layer + purposeLayer = struct.pack(">h",thisPath.purposeLayer) + self.writeRecord(idBits+purposeLayer) + if(thisPath.pathType): + idBits='\x21\x02' #Path type + pathType = struct.pack(">h",thisPath.pathType) + self.writeRecord(idBits+pathType) + if(thisPath.pathWidth): + idBits='\x0F\x03' + pathWidth = struct.pack(">i",thisPath.pathWidth) + self.writeRecord(idBits+pathWidth) + if(thisPath.coordinates): + idBits='\x10\x03' #XY Data Points + coordinateRecord = idBits + for coordinate in thisPath.coordinates: + x=struct.pack(">i",coordinate[0]) + y=struct.pack(">i",coordinate[1]) + coordinateRecord+=x + coordinateRecord+=y + self.writeRecord(coordinateRecord) + idBits='\x11\x00' #End Of Element + coordinateRecord = idBits + self.writeRecord(coordinateRecord) + + def writeSref(self,thisSref): #reads in a reference to another structure + idBits = '\x0A\x00' #record Type + self.writeRecord(idBits) + if(thisSref.elementFlags != ""): + idBits='\x26\x01' #ELFLAGS + elementFlags = struct.pack(">h",thisSref.elementFlags) + self.writeRecord(idBits+elementFlags) + if(thisSref.plex!=""): + idBits='\x2F\x03' #PLEX + plex = struct.pack(">i",thisSref.plex) + self.writeRecord(idBits+plex) + if(thisSref.sName!=""): + idBits='\x12\x06' + sName = thisSref.sName + self.writeRecord(idBits+sName) + if(thisSref.transFlags!=""): + idBits='\x1A\x01' + mirrorFlag = int(thisSref.transFlags[0])<<15 + rotateFlag = int(thisSref.transFlags[1])<<1 + magnifyFlag = int(thisSref.transFlags[2])<<3 + transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) + self.writeRecord(idBits+transFlags) + if(thisSref.magFactor!=""): + idBits='\x1B\x05' + magFactor=self.ibmDataFromIeeeDouble(thisSref.magFactor) + self.writeRecord(idBits+magFactor) + if(thisSref.rotateAngle!=""): + idBits='\x1C\x05' + rotateAngle=self.ibmDataFromIeeeDouble(thisSref.rotateAngle) + self.writeRecord(idBits+rotateAngle) + if(thisSref.coordinates!=""): + idBits='\x10\x03' #XY Data Points + coordinateRecord = idBits + coordinate = thisSref.coordinates + x=struct.pack(">i",coordinate[0]) + y=struct.pack(">i",coordinate[1]) + coordinateRecord+=x + coordinateRecord+=y + #print thisSref.coordinates + self.writeRecord(coordinateRecord) + idBits='\x11\x00' #End Of Element + coordinateRecord = idBits + self.writeRecord(coordinateRecord) + + def writeAref(self,thisAref): #an array of references + idBits = '\x0B\x00' #record Type + self.writeRecord(idBits) + if(thisAref.elementFlags!=""): + idBits='\x26\x01' #ELFLAGS + elementFlags = struct.pack(">h",thisAref.elementFlags) + self.writeRecord(idBits+elementFlags) + if(thisAref.plex): + idBits='\x2F\x03' #PLEX + plex = struct.pack(">i",thisAref.plex) + self.writeRecord(idBits+plex) + if(thisAref.aName): + idBits='\x12\x06' + aName = thisAref.aName + self.writeRecord(idBits+aName) + if(thisAref.transFlags): + idBits='\x1A\x01' + mirrorFlag = int(thisAref.transFlags[0])<<15 + rotateFlag = int(thisAref.transFlags[1])<<1 + magnifyFlag = int(thisAref.transFlags[0])<<3 + transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) + self.writeRecord(idBits+transFlags) + if(thisAref.magFactor): + idBits='\x1B\x05' + magFactor=self.ibmDataFromIeeeDouble(thisAref.magFactor) + self.writeRecord(idBits+magFactor) + if(thisAref.rotateAngle): + idBits='\x1C\x05' + rotateAngle=self.ibmDataFromIeeeDouble(thisAref.rotateAngle) + self.writeRecord(idBits+rotateAngle) + if(thisAref.coordinates): + idBits='\x10\x03' #XY Data Points + coordinateRecord = idBits + for coordinate in thisAref.coordinates: + x=struct.pack(">i",coordinate[0]) + y=struct.pack(">i",coordinate[1]) + coordinateRecord+=x + coordinateRecord+=y + self.writeRecord(coordinateRecord) + idBits='\x11\x00' #End Of Element + coordinateRecord = idBits + self.writeRecord(coordinateRecord) + + def writeText(self,thisText): + idBits = '\x0C\x00' #record Type + self.writeRecord(idBits) + if(thisText.elementFlags!=""): + idBits='\x26\x01' #ELFLAGS + elementFlags = struct.pack(">h",thisText.elementFlags) + self.writeRecord(idBits+elementFlags) + if(thisText.plex !=""): + idBits='\x2F\x03' #PLEX + plex = struct.pack(">i",thisText.plex) + self.writeRecord(idBits+plex) + if(thisText.drawingLayer != ""): + idBits='\x0D\x02' #drawing layer + drawingLayer = struct.pack(">h",thisText.drawingLayer) + self.writeRecord(idBits+drawingLayer) + #if(thisText.purposeLayer): + idBits='\x16\x02' #purpose layer + purposeLayer = struct.pack(">h",thisText.purposeLayer) + self.writeRecord(idBits+purposeLayer) + if(thisText.transFlags != ""): + idBits='\x1A\x01' + mirrorFlag = int(thisText.transFlags[0])<<15 + rotateFlag = int(thisText.transFlags[1])<<1 + magnifyFlag = int(thisText.transFlags[0])<<3 + transFlags = struct.pack(">H",mirrorFlag|rotateFlag|magnifyFlag) + self.writeRecord(idBits+transFlags) + if(thisText.magFactor != ""): + idBits='\x1B\x05' + magFactor=self.ibmDataFromIeeeDouble(thisText.magFactor) + self.writeRecord(idBits+magFactor) + if(thisText.rotateAngle != ""): + idBits='\x1C\x05' + rotateAngle=self.ibmDataFromIeeeDouble(thisText.rotateAngle) + self.writeRecord(idBits+rotateAngle) + if(thisText.pathType !=""): + idBits='\x21\x02' #Path type + pathType = struct.pack(">h",thisText.pathType) + self.writeRecord(idBits+pathType) + if(thisText.pathWidth != ""): + idBits='\x0F\x03' + pathWidth = struct.pack(">i",thisText.pathWidth) + self.writeRecord(idBits+pathWidth) + if(thisText.presentationFlags!=""): + idBits='\x1A\x01' + font = thisText.presentationFlags[0]<<4 + verticalFlags = int(thisText.presentationFlags[1])<<2 + horizontalFlags = int(thisText.presentationFlags[2]) + presentationFlags = struct.pack(">H",font|verticalFlags|horizontalFlags) + self.writeRecord(idBits+transFlags) + if(thisText.coordinates!=""): + idBits='\x10\x03' #XY Data Points + coordinateRecord = idBits + for coordinate in thisText.coordinates: + x=struct.pack(">i",coordinate[0]) + y=struct.pack(">i",coordinate[1]) + coordinateRecord+=x + coordinateRecord+=y + self.writeRecord(coordinateRecord) + if(thisText.textString): + idBits='\x19\x06' + textString = thisText.textString + self.writeRecord(idBits+textString) + + idBits='\x11\x00' #End Of Element + coordinateRecord = idBits + self.writeRecord(coordinateRecord) + + def writeNode(self,thisNode): + idBits = '\x15\x00' #record Type + self.writeRecord(idBits) + if(thisNode.elementFlags!=""): + idBits='\x26\x01' #ELFLAGS + elementFlags = struct.pack(">h",thisNode.elementFlags) + self.writeRecord(idBits+elementFlags) + if(thisNode.plex!=""): + idBits='\x2F\x03' #PLEX + plex = struct.pack(">i",thisNode.plex) + self.writeRecord(idBits+plex) + if(thisNode.drawingLayer!=""): + idBits='\x0D\x02' #drawig layer + drawingLayer = struct.pack(">h",thisNode.drawingLayer) + self.writeRecord(idBits+drawingLayer) + if(thisNode.nodeType!=""): + idBits='\x2A\x02' + nodeType = struct.pack(">h",thisNode.nodeType) + self.writeRecord(idBits+nodeType) + if(thisText.coordinates!=""): + idBits='\x10\x03' #XY Data Points + coordinateRecord = idBits + for coordinate in thisText.coordinates: + x=struct.pack(">i",coordinate[0]) + y=struct.pack(">i",coordinate[1]) + coordinateRecord+=x + coordinateRecord+=y + self.writeRecord(coordinateRecord) + + idBits='\x11\x00' #End Of Element + coordinateRecord = idBits + self.writeRecord(coordinateRecord) + + def writeBox(self,thisBox): + idBits = '\x2E\x02' #record Type + self.writeRecord(idBits) + if(thisBox.elementFlags!=""): + idBits='\x26\x01' #ELFLAGS + elementFlags = struct.pack(">h",thisBox.elementFlags) + self.writeRecord(idBits+elementFlags) + if(thisBox.plex!=""): + idBits='\x2F\x03' #PLEX + plex = struct.pack(">i",thisBox.plex) + self.writeRecord(idBits+plex) + if(thisBox.drawingLayer!=""): + idBits='\x0D\x02' #drawig layer + drawingLayer = struct.pack(">h",thisBox.drawingLayer) + self.writeRecord(idBits+drawingLayer) + if(thisBox.purposeLayer): + idBits='\x16\x02' #purpose layer + purposeLayer = struct.pack(">h",thisBox.purposeLayer) + self.writeRecord(idBits+purposeLayer) + if(thisBox.boxValue!=""): + idBits='\x2D\x00' + boxValue = struct.pack(">h",thisBox.boxValue) + self.writeRecord(idBits+boxValue) + if(thisBox.coordinates!=""): + idBits='\x10\x03' #XY Data Points + coordinateRecord = idBits + for coordinate in thisBox.coordinates: + x=struct.pack(">i",coordinate[0]) + y=struct.pack(">i",coordinate[1]) + coordinateRecord+=x + coordinateRecord+=y + self.writeRecord(coordinateRecord) + + idBits='\x11\x00' #End Of Element + coordinateRecord = idBits + self.writeRecord(coordinateRecord) + + def writeNextStructure(self,structureName): + #first put in the structure head + thisStructure = self.layoutObject.structures[structureName] + idBits='\x05\x02' + createYear = struct.pack(">h",thisStructure.createDate[0]) + createMonth = struct.pack(">h",thisStructure.createDate[1]) + createDay = struct.pack(">h",thisStructure.createDate[2]) + createHour = struct.pack(">h",thisStructure.createDate[3]) + createMinute = struct.pack(">h",thisStructure.createDate[4]) + createSecond = struct.pack(">h",thisStructure.createDate[5]) + modYear = struct.pack(">h",thisStructure.modDate[0]) + modMonth = struct.pack(">h",thisStructure.modDate[1]) + modDay = struct.pack(">h",thisStructure.modDate[2]) + modHour = struct.pack(">h",thisStructure.modDate[3]) + modMinute = struct.pack(">h",thisStructure.modDate[4]) + modSecond = struct.pack(">h",thisStructure.modDate[5]) + self.writeRecord(idBits+createYear+createMonth+createDay+createHour+createMinute+createSecond\ + +modYear+modMonth+modDay+modHour+modMinute+modSecond) + #now the structure name + idBits='\x06\x06' + ##caveat: the name needs to be an EVEN number of characters + if(len(structureName)%2 == 1): + #pad with a zero + structureName = structureName + '\x00' + self.writeRecord(idBits+structureName) + #now go through all the structure elements and write them in + + for boundary in thisStructure.boundaries: + self.writeBoundary(boundary) + for path in thisStructure.paths: + self.writePath(path) + for sref in thisStructure.srefs: + self.writeSref(sref) + for aref in thisStructure.arefs: + self.writeAref(aref) + for text in thisStructure.texts: + self.writeText(text) + for node in thisStructure.nodes: + self.writeNode(node) + for box in thisStructure.boxes: + self.writeBox(box) + #put in the structure tail + idBits='\x07\x00' + self.writeRecord(idBits) + + def writeGds2(self): + self.writeHeader(); #first, put the header in + #go through each structure in the layout and write it to the file + for structureName in self.layoutObject.structures: + self.writeNextStructure(structureName) + #at the end, put in the END LIB record + idBits='\x04\x00' + self.writeRecord(idBits) + + def writeToFile(self,fileName): + self.fileHandle = open(fileName,"wb") + self.writeGds2() + self.fileHandle.close() diff --git a/compiler/gdsMill/gdsMill/gdsPrimitives.py b/compiler/gdsMill/gdsMill/gdsPrimitives.py new file mode 100644 index 00000000..c0981acd --- /dev/null +++ b/compiler/gdsMill/gdsMill/gdsPrimitives.py @@ -0,0 +1,170 @@ +import math + +class GdsStructure: + """Class represent a GDS Structure Object""" + def __init__(self): + self.name="" + self.createDate=() + self.modDate=() + #these are the primitives defined in GDS2, and we will maintain lists of them all + self.boundaries=[] + self.paths=[] + self.srefs=[] + self.arefs=[] + self.texts=[] + self.nodes=[] + self.boxes=[] + +class GdsBoundary: + """Class represent a GDS Boundary Object""" + def __init__(self): + self.elementFlags="" + self.plex="" + self.drawingLayer="" + self.purposeLayer = None + self.dataType="" + self.coordinates="" + +class GdsPath: + """Class represent a GDS Path Object""" + def __init__(self): + self.elementFlags="" + self.plex="" + self.drawingLayer="" + self.purposeLayer = None + self.pathType="" + self.pathWidth="" + self.coordinates="" + + def equivalentBoundaryCoordinates(self): + """Convert the path to a set of boundary coordinates that define it""" + halfWidth = (self.pathWidth/2) + lastX = lastY = None #start of the path + #go through every point + #always draw on the "left side of the line" + #this way, we can append two copies of the coordinates and just trace in order + # i.e. coordinates are (A,B,C,D) - we just make a new array (A,B,C,D,C,B,A) and trace with a fixed offset to the "left" + coordinatesCopy = self.coordinates[:] + coordinatesCopy.reverse() + coordinates=self.coordinates[:]+coordinatesCopy + boundaryEquivalent = [] + #create the first point + x=(coordinates[0][0]) + y=(coordinates[0][1]) + boundaryEquivalent += [(x, y)] + for index in range(0,len(coordinates)): + x=(coordinates[index][0]) + y=(coordinates[index][1]) + if index < len(coordinates)-1: + nextX=(coordinates[index+1][0]) + nextY=(coordinates[index+1][1]) + else: #end of the path b/c no next points + nextX = None; + nextY = None; + if lastX==None: #start of the path + if nextX>x:#moving right + boundaryEquivalent+=[(x,y+halfWidth)] + if nextXy:#moving up + boundaryEquivalent+=[(x-halfWidth,y)] + if nextY y)): #vertical line up + boundaryEquivalent+=[(x-halfWidth,y)] + if (x == lastX and nextX == x) and ((lastY > y) or (nextY < y)): #vertical line down + boundaryEquivalent+=[(x+halfWidth,y)] + if (y == lastY and nextY == y) and ((lastX < x) or (nextX > x)): #horizontal line right + boundaryEquivalent+=[(x,y+halfWidth)] + if (y == lastY and nextY == y) and ((lastX > x) or (nextX < x)): #horizontal line left + boundaryEquivalent+=[(x,y-halfWidth)] + ###### TAKE CARE OF THE CORNERS / BENDS HERE - there are 8 types of corners (4 angles * 2 directions) + if(y < nextY and x < lastX): + boundaryEquivalent+=[(x-halfWidth,y-halfWidth)] + if(y < lastY and x < nextX): + boundaryEquivalent+=[(x+halfWidth,y+halfWidth)] + if(y > nextY and x < lastX): + boundaryEquivalent+=[(x+halfWidth,y-halfWidth)] + if(y > lastY and x < nextX): + boundaryEquivalent+=[(x-halfWidth,y+halfWidth)] + if(y > nextY and x > lastX): + boundaryEquivalent+=[(x+halfWidth,y+halfWidth)] + if(y > lastY and x > nextX): + boundaryEquivalent+=[(x-halfWidth,y-halfWidth)] + if(y < nextY and x > lastX): + boundaryEquivalent+=[(x-halfWidth,y+halfWidth)] + if(y < lastY and x > nextX): + boundaryEquivalent+=[(x+halfWidth,y-halfWidth)] + + if nextX == None: #end of path, put in the last 2 points + if lastXx:#moving left + boundaryEquivalent+=[(x,y-halfWidth)] + if lastYy:#moving down + boundaryEquivalent+=[(x+halfWidth,y)] + #return to beginning + boundaryEquivalent+=[(x,y)] + lastX = x + lastY = y + return boundaryEquivalent + +class GdsSref: + """Class represent a GDS structure reference Object""" + def __init__(self): + self.elementFlags="" + self.plex="" + self.sName="" + self.transFlags=(False,False,False) + self.magFactor="" + self.rotateAngle="" + self.coordinates="" + +class GdsAref: + """Class represent a GDS array reference Object""" + def __init__(self): + self.elementFlags="" + self.plex="" + self.aName="" + self.transFlags=(False,False,False) + self.magFactor="" + self.rotateAngle="" + self.coordinates="" + +class GdsText: + """Class represent a GDS text Object""" + def __init__(self): + self.elementFlags="" + self.plex="" + self.drawingLayer="" + self.purposeLayer = None + self.transFlags=(False,False,False) + self.magFactor="" + self.rotateAngle="" + self.pathType="" + self.pathWidth="" + self.presentationFlags="" + self.coordinates="" + self.textString = "" + +class GdsNode: + """Class represent a GDS Node Object""" + def __init__(self): + self.elementFlags="" + self.plex="" + self.drawingLayer="" + self.nodeType="" + self.coordinates="" + +class GdsBox: + """Class represent a GDS Box Object""" + def __init__(self): + self.elementFlags="" + self.plex="" + self.drawingLayer="" + self.purposeLayer = None + self.boxValue="" + self.coordinates="" \ No newline at end of file diff --git a/compiler/gdsMill/gdsMill/gdsStreamer.py b/compiler/gdsMill/gdsMill/gdsStreamer.py new file mode 100644 index 00000000..b838dca4 --- /dev/null +++ b/compiler/gdsMill/gdsMill/gdsStreamer.py @@ -0,0 +1,154 @@ +import os + +class GdsStreamer: + """ + This class is used to stream GDS files in and out of the Cadence toolsuite. + """ + def __init__(self, workingDirectory = "."): + self.workingDirectory = os.path.abspath(workingDirectory) + + def createStreamOutTemplate(self, sourceLibraryName, sourceCellName, gdsDestinationPath): + templateFile = open(self.workingDirectory+"/partStreamOut.tmpl","w") + templateFile.write("streamOutKeys = list(nil\n") + templateFile.write("'runDir \".\"\n") + templateFile.write("'libName \""+sourceLibraryName+"\"\n") + templateFile.write("'primaryCell \""+sourceCellName+"\"\n") + templateFile.write("'viewName \"layout\"\n") + templateFile.write("'outFile \""+gdsDestinationPath+"/"+sourceCellName+".gds\"\n") + templateFile.write("'scale 0.001000\n") + templateFile.write("'units \"micron\"\n") + templateFile.write("'compression \"none\"\n") + templateFile.write("'hierDepth 32\n") + templateFile.write("'convertToGeo nil\n") + templateFile.write("'maxVertices 200\n") + templateFile.write("'refLib nil\n") + templateFile.write("'libVersion \"5.0\"\n") + templateFile.write("'checkPolygon nil\n") + templateFile.write("'snapToGrid nil\n") + templateFile.write("'simMosaicToArray t\n") + templateFile.write("'caseSensitivity \"preserve\"\n") + templateFile.write("'lineToZeroPath \"path\"\n") + templateFile.write("'convertDot \"ignore\"\n") + templateFile.write("'rectToBox nil\n") + templateFile.write("'convertPathToPoly nil\n") + templateFile.write("'keepPcell nil\n") + templateFile.write("'replaceBusBitChar nil\n") + templateFile.write("'useParentXYforText nil\n") + templateFile.write("'reportPrecision nil\n") + templateFile.write("'runQuiet nil\n") + templateFile.write("'comprehensiveLog nil\n") + templateFile.write("'ignorePcellEvalFail nil\n") + templateFile.write("'errFile \"PIPO.LOG\"\n") + templateFile.write("'NOUnmappingLayerWarning nil\n") + templateFile.write("'techFileChoice nil\n") + templateFile.write("'pcellSuffix \"DbId\"\n") + templateFile.write("'respectGDSIILimits nil\n") + templateFile.write("'dumpPcellInfo nil\n") + templateFile.write("'genListHier nil\n") + templateFile.write("'cellMapTable \"\"\n") + templateFile.write("'layerTable \"\"\n") + templateFile.write("'textFontTable \"\"\n") + templateFile.write("'convertPin \"geometry\"\n") + templateFile.write("'pinInfo 0\n") + templateFile.write("'pinTextMapTable \"\"\n") + templateFile.write("'propMapTable \"\"\n") + templateFile.write("'propSeparator \",\"\n") + templateFile.write("'userSkillFile \"\"\n") + templateFile.write("'rodDir \"\"\n") + templateFile.write("'refLibList \"\"\n") + templateFile.write(")\n") + templateFile.close() + + def createStreamInTemplate(self, sourceLibraryName = None,inputGdsPath = None, retainReferenceLibraries = True): + # retainReferenceLibraries added to tell PIPO whether it should import all SREFS as new cellviews or to + #look inside of existing libraries for cellviews with the same name. + templateParameters = dict() + if retainReferenceLibraries: + templateParameters["ref"] = "t" + else: + templateParameters["ref"] = "nil" + templateFile = open(self.workingDirectory+"/partStreamIn.tmpl","w") + templateFile.write("streamInKeys = list(nil\n") + templateFile.write("'runDir \".\"\n") + templateFile.write("'inFile \""+inputGdsPath+"\"\n") + templateFile.write("'primaryCell \"\"\n") + templateFile.write("'libName \""+sourceLibraryName+"\"\n") + templateFile.write("'techFileName \"\"\n") + templateFile.write("'scale 0.001000\n") + templateFile.write("'units \"micron\"\n") + templateFile.write("'errFile \"PIPO.LOG\"\n") + templateFile.write("'refLib "+templateParameters["ref"]+"\n") + templateFile.write("'hierDepth 32\n") + templateFile.write("'maxVertices 1024\n") + templateFile.write("'checkPolygon nil\n") + templateFile.write("'snapToGrid nil\n") + templateFile.write("'arrayToSimMosaic t\n") + templateFile.write("'caseSensitivity \"preserve\"\n") + templateFile.write("'zeroPathToLine \"path\"\n") + templateFile.write("'convertNode \"ignore\"\n") + templateFile.write("'keepPcell nil\n") + templateFile.write("'replaceBusBitChar nil\n") + templateFile.write("'skipUndefinedLPP nil\n") + templateFile.write("'ignoreBox nil\n") + templateFile.write("'mergeUndefPurposToDrawing nil\n") + templateFile.write("'reportPrecision nil\n") + templateFile.write("'keepStreamCells nil\n") + templateFile.write("'attachTechfileOfLib \"\"\n") + templateFile.write("'runQuiet nil\n") + templateFile.write("'noWriteExistCell nil\n") + templateFile.write("'NOUnmappingLayerWarning nil\n") + templateFile.write("'comprehensiveLog nil\n") + templateFile.write("'ignorePcellEvalFail nil\n") + templateFile.write("'appendDB nil\n") + templateFile.write("'genListHier nil\n") + templateFile.write("'cellMapTable \"\"\n") + templateFile.write("'layerTable \"\"\n") + templateFile.write("'textFontTable \"\"\n") + templateFile.write("'restorePin 0\n") + templateFile.write("'propMapTable \"\"\n") + templateFile.write("'propSeparator \",\"\n") + templateFile.write("'userSkillFile \"\"\n") + templateFile.write("'rodDir \"\"\n") + templateFile.write("'refLibOrder \"\"\n") + templateFile.write(")\n") + templateFile.close() + + def streamFromCadence(self, cadenceLibraryContainerPath, libraryName, cellName, outputPath): + #change into the cadence directory + outputPath = os.path.abspath(outputPath) + currentPath = os.path.abspath(".") + os.chdir(cadenceLibraryContainerPath) + self.createStreamOutTemplate(libraryName,cellName,outputPath) + #stream the gds out from cadence + worker = os.popen("pipo strmout "+self.workingDirectory+"/partStreamOut.tmpl") + #dump the outputs to the screen line by line + print "Streaming Out From Cadence......" + while 1: + line = worker.readline() + if not line: break #this means sim is finished so jump out + #else: print line #for debug only + worker.close() + #now remove the template file + os.remove(self.workingDirectory+"/partStreamOut.tmpl") + #and go back to whever it was we started from + os.chdir(currentPath) + + def streamToCadence(self,cadenceLibraryContainerPath, libraryName, inputPath): + #change into the cadence directory + inputPath = os.path.abspath(inputPath) + currentPath = os.path.abspath(".") + os.chdir(cadenceLibraryContainerPath) + self.createStreamInTemplate(libraryName,inputPath) + #stream the gds out from cadence + worker = os.popen("pipo strmin "+self.workingDirectory+"/partStreamIn.tmpl") + #dump the outputs to the screen line by line + print "Streaming In To Cadence......" + while 1: + line = worker.readline() + if not line: break #this means sim is finished so jump out + #else: print line #for debug only + worker.close() + #now remove the template file + os.remove(self.workingDirectory+"/partStreamIn.tmpl") + #and go back to whever it was we started from + os.chdir(currentPath) \ No newline at end of file diff --git a/compiler/gdsMill/gdsMill/pdfLayout.py b/compiler/gdsMill/gdsMill/pdfLayout.py new file mode 100644 index 00000000..af4dec8c --- /dev/null +++ b/compiler/gdsMill/gdsMill/pdfLayout.py @@ -0,0 +1,94 @@ +import pyx +import math +import mpmath +from gdsPrimitives import * +import random + +class pdfLayout: + """Class representing a view for a layout as a PDF""" + def __init__(self,theLayout): + self.canvas = pyx.canvas.canvas() + self.layout = theLayout + self.layerColors=dict() + self.scale = 1.0 + + def setScale(self,newScale): + self.scale = float(newScale) + + def hexToRgb(self,hexColor): + """ + Takes a hexadecimal color string i.e. "#219E1C" and converts it to an rgb float triplet ranging 0->1 + """ + red = int(hexColor[1:3],16) + green = int(hexColor[3:5],16) + blue = int(hexColor[5:7],16) + return (float(red)/255,float(green)/255,float(blue)/255) + + def randomHexColor(self): + """ + Generates a random color in hex using the format #ABC123 + """ + red = hex(random.randint(0,255)).lstrip("0x") + green = hex(random.randint(0,255)).lstrip("0x") + blue = hex(random.randint(0,255)).lstrip("0x") + return "#"+red+green+blue + + def transformCoordinates(self,uvCoordinates,origin,uVector,vVector): + """ + This helper method will convert coordinates from a UV space to the cartesian XY space + """ + xyCoordinates = [] + #setup a translation matrix + tMatrix = mpmath.matrix([[1.0,0.0,origin[0]],[0.0,1.0,origin[1]],[0.0,0.0,1.0]]) + #and a rotation matrix + rMatrix = mpmath.matrix([[uVector[0],vVector[0],0.0],[uVector[1],vVector[1],0.0],[0.0,0.0,1.0]]) + for coordinate in uvCoordinates: + #grab the point in UV space + uvPoint = mpmath.matrix([coordinate[0],coordinate[1],1.0]) + #now rotate and translate it back to XY space + xyPoint = rMatrix * uvPoint + xyPoint = tMatrix * xyPoint + xyCoordinates += [(xyPoint[0],xyPoint[1])] + return xyCoordinates + + def drawBoundary(self,boundary,origin,uVector,vVector): + #get the coordinates in the correct coordinate space + coordinates = self.transformCoordinates(boundary.coordinates,origin,uVector,vVector) + #method to draw a boundary with an XY offset + x=(coordinates[0][0])/self.scale + y=(coordinates[0][1])/self.scale + shape = pyx.path.path(pyx.path.moveto(x, y)) + for index in range(1,len(coordinates)): + x=(coordinates[index][0])/self.scale + y=(coordinates[index][1])/self.scale + shape.append(pyx.path.lineto(x,y)) + self.canvas.stroke(shape, [pyx.style.linewidth.thick]) + if(boundary.drawingLayer in self.layerColors): + layerColor = self.hexToRgb(self.layerColors[boundary.drawingLayer]) + self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)]) + + def drawPath(self,path,origin,uVector,vVector): + #method to draw a path with an XY offset + boundaryCoordinates = self.transformCoordinates(path.equivalentBoundaryCoordinates(),origin,uVector,vVector) + shape = pyx.path.path(pyx.path.moveto((boundaryCoordinates[0][0])/self.scale,(boundaryCoordinates[0][1])/self.scale)) + for coordinate in boundaryCoordinates[1::]: + shape.append(pyx.path.lineto((coordinate[0])/self.scale,(coordinate[1])/self.scale)) + self.canvas.stroke(shape, [pyx.style.linewidth.thick]) + if(path.drawingLayer in self.layerColors): + layerColor = self.hexToRgb(self.layerColors[path.drawingLayer]) + self.canvas.fill(shape, [pyx.color.rgb(layerColor[0],layerColor[1],layerColor[2]), pyx.color.transparency(0.5)]) + + def drawLayout(self): + #use the layout xyTree and structureList + #to draw ONLY the geometry in each structure + #SREFS and AREFS are handled in the tree + for element in self.layout.xyTree: + #each element is (name,offsetTuple,rotate) + structureToDraw = self.layout.structures[element[0]] + for boundary in structureToDraw.boundaries: + self.drawBoundary(boundary,element[1],element[2], element[3]) + for path in structureToDraw.paths: + self.drawPath(path,element[1],element[2], element[3]) + + def writeToFile(self,filename): + self.canvas.writePDFfile(filename) diff --git a/compiler/gdsMill/gdsMill/vlsiLayout.py b/compiler/gdsMill/gdsMill/vlsiLayout.py new file mode 100644 index 00000000..172e0f1c --- /dev/null +++ b/compiler/gdsMill/gdsMill/vlsiLayout.py @@ -0,0 +1,749 @@ +from gdsPrimitives import * +from datetime import * +import mpmath +import gdsPrimitives +import debug +debug_level=4 + +class VlsiLayout: + """Class represent a hierarchical layout""" + + def __init__(self, name=None, units=(0.001,1e-9), libraryName = "DEFAULT.DB", gdsVersion=5): + #keep a list of all the structures in this layout + self.units = units + #print units + modDate = datetime.now() + self.structures=dict() + self.layerNumbersInUse = [] + self.debug = debug + if name: + self.rootStructureName=name + #create the ROOT structure + self.structures[self.rootStructureName] = GdsStructure() + self.structures[self.rootStructureName].name = name + self.structures[self.rootStructureName].createDate = (modDate.year, + modDate.month, + modDate.day, + modDate.hour, + modDate.minute, + modDate.second) + self.structures[self.rootStructureName].modDate = (modDate.year, + modDate.month, + modDate.day, + modDate.hour, + modDate.minute, + modDate.second) + + self.info = dict() #information gathered from the GDSII header + self.info['units']=self.units + self.info['dates']=(modDate.year, + modDate.month, + modDate.day, + modDate.hour, + modDate.minute, + modDate.second, + modDate.year, + modDate.month, + modDate.day, + modDate.hour, + modDate.minute, + modDate.second) + self.info['libraryName']=libraryName + self.info['gdsVersion']=gdsVersion + + self.xyTree = [] #This will contain a list of all structure names + #expanded to include srefs / arefs separately. + #each structure will have an X,Y,offset, and rotate associated + #with it. Populate via traverseTheHierarchy method. + + #temp variables used in delegate functions + self.tempCoordinates=None + self.tempPassFail = True + + def strip_non_ascii(string): + ''' Returns the string without non ASCII characters''' + stripped = (c for c in string if 0 < ord(c) < 127) + return ''.join(stripped) + + + def rotatedCoordinates(self,coordinatesToRotate,rotateAngle): + #helper method to rotate a list of coordinates + angle=math.radians(float(0)) + if(rotateAngle): + angle = math.radians(float(repr(rotateAngle))) + + coordinatesRotate = [] #this will hold the rotated values + for coordinate in coordinatesToRotate: + newX = coordinate[0]*math.cos(angle) - coordinate[1]*math.sin(angle) + newY = coordinate[0]*math.sin(angle) + coordinate[1]*math.cos(angle) + coordinatesRotate += [(newX,newY)] + return coordinatesRotate + + def rename(self,newName): + #make sure the newName is a multiple of 2 characters + if(len(newName)%2 == 1): + #pad with a zero + newName = newName + '\x00' + #take the root structure and copy it to a new structure with the new name + self.structures[newName] = self.structures[self.rootStructureName] + self.structures[newName].name = newName + #and delete the old root + del self.structures[self.rootStructureName] + self.rootStructureName = newName + #repopulate the 2d map so drawing occurs correctly + del self.xyTree[:] + self.populateCoordinateMap() + + def newLayout(self,newName): + #if (newName == "" | newName == 0): + # print("ERROR: vlsiLayout.py:newLayout newName is null") + + #make sure the newName is a multiple of 2 characters + #if(len(newName)%2 == 1): + #pad with a zero + #newName = newName + '\x00' + #take the root structure and copy it to a new structure with the new name + #self.structures[newName] = self.structures[self.rootStructureName] + + modDate = datetime.now() + + self.structures[newName] = GdsStructure() + self.structures[newName].name = newName + + + + self.rootStructureName = newName + + self.rootStructureName=newName + + #create the ROOT structure + self.structures[self.rootStructureName] = GdsStructure() + #self.structures[self.rootStructureName].name = name + self.structures[self.rootStructureName].createDate = (modDate.year, + modDate.month, + modDate.day, + modDate.hour, + modDate.minute, + modDate.second) + self.structures[self.rootStructureName].modDate = (modDate.year, + modDate.month, + modDate.day, + modDate.hour, + modDate.minute, + modDate.second) + + + #repopulate the 2d map so drawing occurs correctly + self.prepareForWrite() + + def prepareForWrite(self): + del self.xyTree[:] + self.populateCoordinateMap() + + def deduceHierarchy(self): + #first, find the root of the tree. + #go through and get the name of every structure. + #then, go through and find which structure is not + #contained by any other structure. this is the root. + structureNames=[] + for name in self.structures: + #print "deduceHierarchy: structure.name[%s]",name //FIXME: Added By Tom G. + structureNames+=[name] + + for name in self.structures: + if(len(self.structures[name].srefs)>0): #does this structure reference any others? + for sref in self.structures[name].srefs: #go through each reference + if sref.sName in structureNames: #and compare to our list + structureNames.remove(sref.sName) + + self.rootStructureName = structureNames[0] + + def traverseTheHierarchy(self, startingStructureName=None, delegateFunction = None, + transformPath = [], rotateAngle = 0, transFlags = (0,0,0), coordinates = (0,0)): + #since this is a recursive function, must deal with the default + #parameters explicitly + if startingStructureName == None: + startingStructureName = self.rootStructureName + + #set up the rotation matrix + if(rotateAngle == None or rotateAngle == ""): + rotateAngle = 0 + else: + rotateAngle = math.radians(float(rotateAngle)) + mRotate = mpmath.matrix([[math.cos(rotateAngle),-math.sin(rotateAngle),0.0], + [math.sin(rotateAngle),math.cos(rotateAngle),0.0],[0.0,0.0,1.0],]) + #set up the translation matrix + translateX = float(coordinates[0]) + translateY = float(coordinates[1]) + mTranslate = mpmath.matrix([[1.0,0.0,translateX],[0.0,1.0,translateY],[0.0,0.0,1.0]]) + #set up the scale matrix (handles mirror X) + scaleX = 1.0 + if(transFlags[0]): + scaleY = -1.0 + else: + scaleY = 1.0 + mScale = mpmath.matrix([[scaleX,0.0,0.0],[0.0,scaleY,0.0],[0.0,0.0,1.0]]) + + #we need to keep track of all transforms in the hierarchy + #when we add an element to the xy tree, we apply all transforms from the bottom up + transformPath += [(mRotate,mScale,mTranslate)] + if delegateFunction != None: + delegateFunction(startingStructureName, transformPath) + #starting with a particular structure, we will recursively traverse the tree + #********might have to set the recursion level deeper for big layouts! + if(len(self.structures[startingStructureName].srefs)>0): #does this structure reference any others? + #if so, go through each and call this function again + #if not, return back to the caller (caller can be this function) + for sref in self.structures[startingStructureName].srefs: + #here, we are going to modify the sref coordinates based on the parent objects rotation +# if (sref.sName.count("via") == 0): + self.traverseTheHierarchy(startingStructureName = sref.sName, + delegateFunction = delegateFunction, + transformPath = transformPath, + rotateAngle = sref.rotateAngle, + transFlags = sref.transFlags, + coordinates = sref.coordinates) +# else: +# print "WARNING: via encountered, ignoring:", sref.sName + #MUST HANDLE AREFs HERE AS WELL + #when we return, drop the last transform from the transformPath + del transformPath[-1] + return + + def initialize(self): + self.deduceHierarchy() + #self.traverseTheHierarchy() + self.populateCoordinateMap() + + def populateCoordinateMap(self): + def addToXyTree(startingStructureName = None,transformPath = None): + #print"populateCoordinateMap" + uVector = mpmath.matrix([1.0,0.0,0.0]) #start with normal basis vectors + vVector = mpmath.matrix([0.0,1.0,0.0]) + origin = mpmath.matrix([0.0,0.0,1.0]) #and an origin (Z component is 1.0 to indicate position instead of vector) + #make a copy of all the transforms and reverse it + reverseTransformPath = transformPath[:] + if len(reverseTransformPath) > 1: + reverseTransformPath.reverse() + #now go through each transform and apply them to our basis and origin in succession + for transform in reverseTransformPath: + origin = transform[0] * origin #rotate + uVector = transform[0] * uVector #rotate + vVector = transform[0] * vVector #rotate + origin = transform[1] * origin #scale + uVector = transform[1] * uVector #rotate + vVector = transform[1] * vVector #rotate + origin = transform[2] * origin #translate + #we don't need to do a translation on the basis vectors + self.xyTree+=[(startingStructureName,origin,uVector,vVector)] #populate the xyTree with each + #structureName and coordinate space + self.traverseTheHierarchy(delegateFunction = addToXyTree) + + def microns(self,userUnits): + """Utility function to convert user units to microns""" + userUnit = self.units[1]/self.units[0] + userUnitsPerMicron = userUnit / (userunit) + layoutUnitsPerMicron = userUnitsPerMicron / self.units[0] + return userUnits / layoutUnitsPerMicron + + def userUnits(self,microns): + """Utility function to convert microns to user units""" + userUnit = self.units[1]/self.units[0] + #userUnitsPerMicron = userUnit / 1e-6 + userUnitsPerMicron = userUnit / (userUnit) + layoutUnitsPerMicron = userUnitsPerMicron / self.units[0] + #print "userUnit:",userUnit,"userUnitsPerMicron",userUnitsPerMicron,"layoutUnitsPerMicron",layoutUnitsPerMicron,[microns,microns*layoutUnitsPerMicron] + return round(microns*layoutUnitsPerMicron,0) + + def changeRoot(self,newRoot, create=False): + """ + Method to change the root pointer to another layout. + """ + + #if self.debug: print "DEBUG: GdsMill vlsiLayout: changeRoot: %s "%newRoot + debug.info(debug_level,"DEBUG: GdsMill vlsiLayout: changeRoot: %s "%newRoot) + + # Determine if newRoot exists + # layoutToAdd (default) or nameOfLayout + if (newRoot == 0 | ((newRoot not in self.structures) & ~create)): + #print "ERROR: vlsiLayout.changeRoot: Name of new root [%s] not found and create flag is false"%newRoot + debug.error(debug_level,"ERROR: vlsiLayout.changeRoot: Name of new root [%s] not found and create flag is false"+str(newRoot)) + exit(1) + else: + if ((newRoot not in self.structures) & create): + self.newLayout(newRoot) + self.rootStructureName = newRoot + + + + def addInstance(self,layoutToAdd,nameOfLayout=0,offsetInMicrons=(0,0),mirror=None,rotate=None): + """ + Method to insert one layout into another at a particular offset. + """ + offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1])) + #print "addInstance:offsetInLayoutUnits",offsetInLayoutUnits + #offsetInLayoutUnits = ((offsetInMicrons[0]),(offsetInMicrons[1])) + #print "DEBUG: addInstance offsetInLayoutUnits: %f, %f"%(self.userUnits(offsetInMicrons[0]), self.userUnits(offsetInMicrons[1])) + #if self.debug==1: + # print "DEBUG: GdsMill vlsiLayout: addInstance: type %s, nameOfLayout "%type(layoutToAdd),nameOfLayout + # print offsetInMicrons + # print offsetInLayoutUnits + debug.info(debug_level,"DEBUG: GdsMill vlsiLayout: addInstance: type "+str(layoutToAdd.rootStructureName)) + debug.info(debug_level,"offset In Microns:"+str(offsetInMicrons)+"offset In LayoutUnits:"+str(offsetInLayoutUnits)) + + + + # Determine if we are instantiating the root design of + # layoutToAdd (default) or nameOfLayout + if nameOfLayout == 0: + StructureFound = True + StructureName = layoutToAdd.rootStructureName + else: + StructureName = nameOfLayout #layoutToAdd + StructureFound = False + for structure in layoutToAdd.structures: + # if self.debug: print structure, "N","N" + if StructureName in structure: + debug.info(debug_level,"DEBUG: Structure %s Found"+str(StructureName)) + #if self.debug: print "DEBUG: Structure %s Found"%StructureName + StructureFound = True + + + + # If layoutToAdd is a unique object (not this), then copy heirarchy, + # otherwise, if it is a text name of an internal structure, use it. + + if layoutToAdd != self: + #first, we need to combine the structure dictionaries from both layouts + for structure in layoutToAdd.structures: + if structure not in self.structures: + self.structures[structure]=layoutToAdd.structures[structure] + #also combine the "layers in use" list + for layerNumber in layoutToAdd.layerNumbersInUse: + if layerNumber not in self.layerNumbersInUse: + self.layerNumbersInUse += [layerNumber] + #Also, check if the user units / microns is the same as this Layout + #if (layoutToAdd.units != self.units): + #print "WARNING: VlsiLayout: Units from design to be added do not match target Layout" + + # if debug: print "DEBUG: vlsilayout: Using %d layers" + + # If we can't find the structure, error + #if StructureFound == False: + #print "ERROR: vlsiLayout.addInstance: [%s] Name not found in local structures, "%(nameOfLayout) + #return #FIXME: remove! + #exit(1) + + + #add a reference to the new layout structure in this layout's root + layoutToAddSref = GdsSref() + layoutToAddSref.sName = StructureName + layoutToAddSref.coordinates = offsetInLayoutUnits + + if mirror or rotate: + ########flags = (mirror around x-axis, absolute rotation, absolute magnification) + layoutToAddSref.transFlags = (False,False,False) + #Below angles are angular angles(relative), not absolute + if mirror=="R90": + rotate = 90.0 + if mirror=="R180": + rotate = 180.0 + if mirror=="R270": + rotate = 270.0 + if rotate: + layoutToAddSref.rotateAngle = rotate + if mirror == "x" or mirror == "MX": + layoutToAddSref.transFlags = (True,False,False) + if mirror == "y" or mirror == "MY": #NOTE: "MY" option will override specified rotate angle + layoutToAddSref.transFlags = (True,False,False) + layoutToAddSref.rotateAngle = 180.0 + if mirror == "xy" or mirror == "XY": #NOTE: "XY" option will override specified rotate angle + layoutToAddSref.transFlags = (False,False,False) + layoutToAddSref.rotateAngle = 180.0 + + #add the sref to the root structure + self.structures[self.rootStructureName].srefs+=[layoutToAddSref] + + def addBox(self,layerNumber=0, purposeNumber=None, offsetInMicrons=(0,0), width=1.0, height=1.0,center=False): + """ + Method to add a box to a layout + """ + offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1])) + #print "addBox:offsetInLayoutUnits",offsetInLayoutUnits + widthInLayoutUnits = self.userUnits(width) + heightInLayoutUnits = self.userUnits(height) + #print "offsetInLayoutUnits",widthInLayoutUnits,"heightInLayoutUnits",heightInLayoutUnits + if not center: + coordinates=[offsetInLayoutUnits, + (offsetInLayoutUnits[0]+widthInLayoutUnits,offsetInLayoutUnits[1]), + (offsetInLayoutUnits[0]+widthInLayoutUnits,offsetInLayoutUnits[1]+heightInLayoutUnits), + (offsetInLayoutUnits[0],offsetInLayoutUnits[1]+heightInLayoutUnits), + offsetInLayoutUnits] + else: + + #is there where gdsmill is halving the coordinates??? + #if you printGDS of temp.gds, the header says 1 user unit = .0005 database units. By default user units = .001. + #something to do with the ieeedouble in gdswriter.py???? + startPoint = (offsetInLayoutUnits[0]-widthInLayoutUnits/2, offsetInLayoutUnits[1]-heightInLayoutUnits/2) #width/2 height/2 + coordinates=[startPoint, + (startPoint[0]+widthInLayoutUnits,startPoint[1]), + (startPoint[0]+widthInLayoutUnits,startPoint[1]+heightInLayoutUnits), + (startPoint[0],startPoint[1]+heightInLayoutUnits), + startPoint] + + boundaryToAdd = GdsBoundary() + boundaryToAdd.drawingLayer = layerNumber + boundaryToAdd.dataType = 0 + boundaryToAdd.coordinates = coordinates + boundaryToAdd.purposeLayer = purposeNumber + #add the sref to the root structure + self.structures[self.rootStructureName].boundaries+=[boundaryToAdd] + + def addPath(self, layerNumber=0, purposeNumber = None, coordinates=[(0,0)], width=1.0): + """ + Method to add a path to a layout + """ + widthInLayoutUnits = self.userUnits(width) + layoutUnitCoordinates = [] + #first convert to proper units + for coordinate in coordinates: + cX = self.userUnits(coordinate[0]) + cY = self.userUnits(coordinate[1]) + layoutUnitCoordinates += [(cX,cY)] + pathToAdd = GdsPath() + pathToAdd.drawingLayer=layerNumber + pathToAdd.purposeLayer = purposeNumber + pathToAdd.pathWidth=widthInLayoutUnits + pathToAdd.coordinates=layoutUnitCoordinates + #add the sref to the root structure + self.structures[self.rootStructureName].paths+=[pathToAdd] + + def addText(self, text, layerNumber=0, purposeNumber = None, offsetInMicrons=(0,0), magnification=0.1, rotate = None): + offsetInLayoutUnits = (self.userUnits(offsetInMicrons[0]),self.userUnits(offsetInMicrons[1])) + textToAdd = GdsText() + textToAdd.drawingLayer = layerNumber + textToAdd.purposeLayer = purposeNumber + textToAdd.dataType = 0 + textToAdd.coordinates = [offsetInLayoutUnits] + if(len(text)%2 == 1): + #pad with a zero + text = text + '\x00' + textToAdd.textString = text + textToAdd.transFlags = (False,False,True) + textToAdd.magFactor = magnification + if rotate: + textToAdd.transFlags = (False,True,True) + textToAdd.rotateAngle = rotate + #add the sref to the root structure + self.structures[self.rootStructureName].texts+=[textToAdd] + + def isBounded(self,testPoint,startPoint,endPoint): + #these arguments are touples of (x,y) coordinates + if testPoint == None: + return 0 + if(testPoint[0]<=max(endPoint[0],startPoint[0]) and \ + testPoint[0]>=min(endPoint[0],startPoint[0]) and \ + testPoint[1]<=max(endPoint[1],startPoint[1]) and \ + testPoint[1]>=min(endPoint[1],startPoint[1])): + return 1 + else: + return 0 + + def intersectionPoint(self,startPoint1,endPoint1,startPoint2,endPoint2): + if((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])!=0): + pSlope = (endPoint1[1]-startPoint1[1])/(endPoint1[0]-startPoint1[0]) + pIntercept = startPoint1[1]-pSlope*startPoint1[0] + qSlope = (endPoint2[1]-startPoint2[1])/(endPoint2[0]-startPoint2[0]) + qIntercept = startPoint2[1]-qSlope*startPoint2[0] + if(pSlope!=qSlope): + newX=(qIntercept-pIntercept)/(pSlope-qSlope) + newY=pSlope*newX+pIntercept + else: + #parallel lines can't intersect + newX=None + newY=None + elif((endPoint1[0]-startPoint1[0])==0 and (endPoint2[0]-startPoint2[0])==0): + #two vertical lines cannot intersect + newX = None + newY = None + elif((endPoint1[0]-startPoint1[0])==0 and (endPoint2[0]-startPoint2[0])!=0): + qSlope = (endPoint2[1]-startPoint2[1])/(endPoint2[0]-startPoint2[0]) + qIntercept = startPoint2[1]-qSlope*startPoint2[0] + newX=endPoint1[0] + newY=qSlope*newX+qIntercept + elif((endPoint1[0]-startPoint1[0])!=0 and (endPoint2[0]-startPoint2[0])==0): + pSlope = (endPoint1[1]-startPoint1[1])/(endPoint1[0]-startPoint1[0]) + pIntercept = startPoint1[1]-pSlope*startPoint1[0] + newX=endPoint2[0] + newY=pSlope*newX+pIntercept + return (newX,newY) + + def isCollinear(self,testPoint,point1,point2): + slope1 = (testPoint[1]-point1[1])/(testPoint[0]-point1[0]) + slope2 = (point2[1]-point1[1])/(point2[0]-point1[0]) + if slope1 == slope2: + return True + return False + + def doShapesIntersect(self,shape1Coordinates, shape2Coordinates): + """ + Utility function to determine if 2 arbitrary shapes intersect. + We define intersection by taking pairs of points in each shape (assuming they are in order) + and seeing if any of the lines formed by these pais intersect. + """ + for shape1Index in range(0,len(shape1Coordinates)-1): + for shape2Index in range(0,len(shape2Coordinates)-1): + startPoint1 = shape1Coordinates[shape1Index] + endPoint1 = shape1Coordinates[shape1Index+1] + startPoint2 = shape2Coordinates[shape2Index] + endPoint2 = shape2Coordinates[shape2Index+1] + intersect = self.intersectionPoint(startPoint1,endPoint1,startPoint2,endPoint2) + if(self.isBounded(intersect,startPoint1,endPoint1) and self.isBounded(intersect,startPoint2,endPoint2)): + return True #these shapes overlap! + return False #these shapes are ok + + def isPointInsideOfBox(self,pointCoordinates,boxCoordinates): + leftBound = boxCoordinates[0][0] + rightBound = boxCoordinates[0][0] + topBound = boxCoordinates[0][1] + bottomBound = boxCoordinates[0][1] + for point in boxCoordinates: + if point[0]rightBound: + rightBound = point[0] + if point[1]topBound: + topBound = point[1] + if(pointCoordinates[0]>rightBound or + pointCoordinates[0]topBound or + pointCoordinates[1]left_butt_X: + cellBoundary[0]=left_butt_X + if cellBoundary[1]>left_butt_Y: + cellBoundary[1]=left_butt_Y + if cellBoundary[2]=int(Rectangle[0]))&(label_coordinate[0]<=int(Rectangle[2])) + coordinate_In_Rectangle_y_range=(label_coordinate[1]>=int(Rectangle[1]))&(label_coordinate[1]<=int(Rectangle[3])) + if coordinate_In_Rectangle_x_range & coordinate_In_Rectangle_y_range: + return True + else: + return False + + def returnBiggerBoundary(self,comparedRectangle,label_boundary): + if label_boundary[0]== None: + label_boundary=comparedRectangle + debug.info(debug_level,"The label_boundary is initialized to "+str(label_boundary)) + else: + area_label_boundary=(label_boundary[2]-label_boundary[0])*(label_boundary[3]-label_boundary[1]) + area_comparedRectangle=(comparedRectangle[2]-comparedRectangle[0])*(comparedRectangle[3]-comparedRectangle[1]) + if area_label_boundary<=area_comparedRectangle: + label_boundary = comparedRectangle + debug.info(debug_level,"The label_boundary is updated to "+str(label_boundary)) + return label_boundary + diff --git a/compiler/gdsMill/mpmath/__init__.py b/compiler/gdsMill/mpmath/__init__.py new file mode 100644 index 00000000..8dea8385 --- /dev/null +++ b/compiler/gdsMill/mpmath/__init__.py @@ -0,0 +1,386 @@ +__version__ = '0.14' + +from usertools import monitor, timing + +from ctx_fp import FPContext +from ctx_mp import MPContext + +fp = FPContext() +mp = MPContext() + +fp._mp = mp +mp._mp = mp +mp._fp = fp +fp._fp = fp + +# XXX: extremely bad pickle hack +import ctx_mp as _ctx_mp +_ctx_mp._mpf_module.mpf = mp.mpf +_ctx_mp._mpf_module.mpc = mp.mpc + +make_mpf = mp.make_mpf +make_mpc = mp.make_mpc + +extraprec = mp.extraprec +extradps = mp.extradps +workprec = mp.workprec +workdps = mp.workdps + +mag = mp.mag + +bernfrac = mp.bernfrac + +jdn = mp.jdn +jsn = mp.jsn +jcn = mp.jcn +jtheta = mp.jtheta +calculate_nome = mp.calculate_nome + +nint_distance = mp.nint_distance + +plot = mp.plot +cplot = mp.cplot +splot = mp.splot + +odefun = mp.odefun + +jacobian = mp.jacobian +findroot = mp.findroot +multiplicity = mp.multiplicity + +isinf = mp.isinf +isnan = mp.isnan +isint = mp.isint +almosteq = mp.almosteq +nan = mp.nan +rand = mp.rand + +absmin = mp.absmin +absmax = mp.absmax + +fraction = mp.fraction + +linspace = mp.linspace +arange = mp.arange + +mpmathify = convert = mp.convert +mpc = mp.mpc +mpi = mp.mpi + +nstr = mp.nstr +nprint = mp.nprint +chop = mp.chop + +fneg = mp.fneg +fadd = mp.fadd +fsub = mp.fsub +fmul = mp.fmul +fdiv = mp.fdiv +fprod = mp.fprod + +quad = mp.quad +quadgl = mp.quadgl +quadts = mp.quadts +quadosc = mp.quadosc + +pslq = mp.pslq +identify = mp.identify +findpoly = mp.findpoly + +richardson = mp.richardson +shanks = mp.shanks +nsum = mp.nsum +nprod = mp.nprod +diff = mp.diff +diffs = mp.diffs +diffun = mp.diffun +differint = mp.differint +taylor = mp.taylor +pade = mp.pade +polyval = mp.polyval +polyroots = mp.polyroots +fourier = mp.fourier +fourierval = mp.fourierval +sumem = mp.sumem +chebyfit = mp.chebyfit +limit = mp.limit + +matrix = mp.matrix +eye = mp.eye +diag = mp.diag +zeros = mp.zeros +ones = mp.ones +hilbert = mp.hilbert +randmatrix = mp.randmatrix +swap_row = mp.swap_row +extend = mp.extend +norm = mp.norm +mnorm = mp.mnorm + +lu_solve = mp.lu_solve +lu = mp.lu +unitvector = mp.unitvector +inverse = mp.inverse +residual = mp.residual +qr_solve = mp.qr_solve +cholesky = mp.cholesky +cholesky_solve = mp.cholesky_solve +det = mp.det +cond = mp.cond + +expm = mp.expm +sqrtm = mp.sqrtm +powm = mp.powm +logm = mp.logm +sinm = mp.sinm +cosm = mp.cosm + +mpf = mp.mpf +j = mp.j +exp = mp.exp +expj = mp.expj +expjpi = mp.expjpi +ln = mp.ln +im = mp.im +re = mp.re +inf = mp.inf +ninf = mp.ninf +sign = mp.sign + +eps = mp.eps +pi = mp.pi +ln2 = mp.ln2 +ln10 = mp.ln10 +phi = mp.phi +e = mp.e +euler = mp.euler +catalan = mp.catalan +khinchin = mp.khinchin +glaisher = mp.glaisher +apery = mp.apery +degree = mp.degree +twinprime = mp.twinprime +mertens = mp.mertens + +ldexp = mp.ldexp +frexp = mp.frexp + +fsum = mp.fsum +fdot = mp.fdot + +sqrt = mp.sqrt +cbrt = mp.cbrt +exp = mp.exp +ln = mp.ln +log = mp.log +log10 = mp.log10 +power = mp.power +cos = mp.cos +sin = mp.sin +tan = mp.tan +cosh = mp.cosh +sinh = mp.sinh +tanh = mp.tanh +acos = mp.acos +asin = mp.asin +atan = mp.atan +asinh = mp.asinh +acosh = mp.acosh +atanh = mp.atanh +sec = mp.sec +csc = mp.csc +cot = mp.cot +sech = mp.sech +csch = mp.csch +coth = mp.coth +asec = mp.asec +acsc = mp.acsc +acot = mp.acot +asech = mp.asech +acsch = mp.acsch +acoth = mp.acoth +cospi = mp.cospi +sinpi = mp.sinpi +sinc = mp.sinc +sincpi = mp.sincpi +fabs = mp.fabs +re = mp.re +im = mp.im +conj = mp.conj +floor = mp.floor +ceil = mp.ceil +root = mp.root +nthroot = mp.nthroot +hypot = mp.hypot +modf = mp.modf +ldexp = mp.ldexp +frexp = mp.frexp +sign = mp.sign +arg = mp.arg +phase = mp.phase +polar = mp.polar +rect = mp.rect +degrees = mp.degrees +radians = mp.radians +atan2 = mp.atan2 +fib = mp.fib +fibonacci = mp.fibonacci +lambertw = mp.lambertw +zeta = mp.zeta +altzeta = mp.altzeta +gamma = mp.gamma +factorial = mp.factorial +fac = mp.fac +fac2 = mp.fac2 +beta = mp.beta +betainc = mp.betainc +psi = mp.psi +#psi0 = mp.psi0 +#psi1 = mp.psi1 +#psi2 = mp.psi2 +#psi3 = mp.psi3 +polygamma = mp.polygamma +digamma = mp.digamma +#trigamma = mp.trigamma +#tetragamma = mp.tetragamma +#pentagamma = mp.pentagamma +harmonic = mp.harmonic +bernoulli = mp.bernoulli +bernfrac = mp.bernfrac +stieltjes = mp.stieltjes +hurwitz = mp.hurwitz +dirichlet = mp.dirichlet +bernpoly = mp.bernpoly +eulerpoly = mp.eulerpoly +eulernum = mp.eulernum +polylog = mp.polylog +clsin = mp.clsin +clcos = mp.clcos +gammainc = mp.gammainc +gammaprod = mp.gammaprod +binomial = mp.binomial +rf = mp.rf +ff = mp.ff +hyper = mp.hyper +hyp0f1 = mp.hyp0f1 +hyp1f1 = mp.hyp1f1 +hyp1f2 = mp.hyp1f2 +hyp2f1 = mp.hyp2f1 +hyp2f2 = mp.hyp2f2 +hyp2f0 = mp.hyp2f0 +hyp2f3 = mp.hyp2f3 +hyp3f2 = mp.hyp3f2 +hyperu = mp.hyperu +hypercomb = mp.hypercomb +meijerg = mp.meijerg +appellf1 = mp.appellf1 +erf = mp.erf +erfc = mp.erfc +erfi = mp.erfi +erfinv = mp.erfinv +npdf = mp.npdf +ncdf = mp.ncdf +expint = mp.expint +e1 = mp.e1 +ei = mp.ei +li = mp.li +ci = mp.ci +si = mp.si +chi = mp.chi +shi = mp.shi +fresnels = mp.fresnels +fresnelc = mp.fresnelc +airyai = mp.airyai +airybi = mp.airybi +ellipe = mp.ellipe +ellipk = mp.ellipk +agm = mp.agm +jacobi = mp.jacobi +chebyt = mp.chebyt +chebyu = mp.chebyu +legendre = mp.legendre +legenp = mp.legenp +legenq = mp.legenq +hermite = mp.hermite +gegenbauer = mp.gegenbauer +laguerre = mp.laguerre +spherharm = mp.spherharm +besselj = mp.besselj +j0 = mp.j0 +j1 = mp.j1 +besseli = mp.besseli +bessely = mp.bessely +besselk = mp.besselk +hankel1 = mp.hankel1 +hankel2 = mp.hankel2 +struveh = mp.struveh +struvel = mp.struvel +whitm = mp.whitm +whitw = mp.whitw +ber = mp.ber +bei = mp.bei +ker = mp.ker +kei = mp.kei +coulombc = mp.coulombc +coulombf = mp.coulombf +coulombg = mp.coulombg +lambertw = mp.lambertw +barnesg = mp.barnesg +superfac = mp.superfac +hyperfac = mp.hyperfac +loggamma = mp.loggamma +siegeltheta = mp.siegeltheta +siegelz = mp.siegelz +grampoint = mp.grampoint +zetazero = mp.zetazero +riemannr = mp.riemannr +primepi = mp.primepi +primepi2 = mp.primepi2 +primezeta = mp.primezeta +bell = mp.bell +polyexp = mp.polyexp +expm1 = mp.expm1 +powm1 = mp.powm1 +unitroots = mp.unitroots +cyclotomic = mp.cyclotomic + + +# be careful when changing this name, don't use test*! +def runtests(): + """ + Run all mpmath tests and print output. + """ + import os.path + from inspect import getsourcefile + import tests.runtests as tests + testdir = os.path.dirname(os.path.abspath(getsourcefile(tests))) + importdir = os.path.abspath(testdir + '/../..') + tests.testit(importdir, testdir) + +def doctests(): + try: + import psyco; psyco.full() + except ImportError: + pass + import sys + from timeit import default_timer as clock + filter = [] + for i, arg in enumerate(sys.argv): + if '__init__.py' in arg: + filter = [sn for sn in sys.argv[i+1:] if not sn.startswith("-")] + break + import doctest + globs = globals().copy() + for obj in globs: #sorted(globs.keys()): + if filter: + if not sum([pat in obj for pat in filter]): + continue + print obj, + t1 = clock() + doctest.run_docstring_examples(globs[obj], {}, verbose=("-v" in sys.argv)) + t2 = clock() + print round(t2-t1, 3) + +if __name__ == '__main__': + doctests() + diff --git a/compiler/gdsMill/mpmath/calculus/__init__.py b/compiler/gdsMill/mpmath/calculus/__init__.py new file mode 100644 index 00000000..85f8919e --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/__init__.py @@ -0,0 +1,6 @@ +import calculus +# XXX: hack to set methods +import approximation +import differentiation +import extrapolation +import polynomials diff --git a/compiler/gdsMill/mpmath/calculus/approximation.py b/compiler/gdsMill/mpmath/calculus/approximation.py new file mode 100644 index 00000000..334e56d5 --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/approximation.py @@ -0,0 +1,246 @@ +from calculus import defun + +#----------------------------------------------------------------------------# +# Approximation methods # +#----------------------------------------------------------------------------# + +# The Chebyshev approximation formula is given at: +# http://mathworld.wolfram.com/ChebyshevApproximationFormula.html + +# The only major changes in the following code is that we return the +# expanded polynomial coefficients instead of Chebyshev coefficients, +# and that we automatically transform [a,b] -> [-1,1] and back +# for convenience. + +# Coefficient in Chebyshev approximation +def chebcoeff(ctx,f,a,b,j,N): + s = ctx.mpf(0) + h = ctx.mpf(0.5) + for k in range(1, N+1): + t = ctx.cos(ctx.pi*(k-h)/N) + s += f(t*(b-a)*h + (b+a)*h) * ctx.cos(ctx.pi*j*(k-h)/N) + return 2*s/N + +# Generate Chebyshev polynomials T_n(ax+b) in expanded form +def chebT(ctx, a=1, b=0): + Tb = [1] + yield Tb + Ta = [b, a] + while 1: + yield Ta + # Recurrence: T[n+1](ax+b) = 2*(ax+b)*T[n](ax+b) - T[n-1](ax+b) + Tmp = [0] + [2*a*t for t in Ta] + for i, c in enumerate(Ta): Tmp[i] += 2*b*c + for i, c in enumerate(Tb): Tmp[i] -= c + Ta, Tb = Tmp, Ta + +@defun +def chebyfit(ctx, f, interval, N, error=False): + r""" + Computes a polynomial of degree `N-1` that approximates the + given function `f` on the interval `[a, b]`. With ``error=True``, + :func:`chebyfit` also returns an accurate estimate of the + maximum absolute error; that is, the maximum value of + `|f(x) - P(x)|` for `x \in [a, b]`. + + :func:`chebyfit` uses the Chebyshev approximation formula, + which gives a nearly optimal solution: that is, the maximum + error of the approximating polynomial is very close to + the smallest possible for any polynomial of the same degree. + + Chebyshev approximation is very useful if one needs repeated + evaluation of an expensive function, such as function defined + implicitly by an integral or a differential equation. (For + example, it could be used to turn a slow mpmath function + into a fast machine-precision version of the same.) + + **Examples** + + Here we use :func:`chebyfit` to generate a low-degree approximation + of `f(x) = \cos(x)`, valid on the interval `[1, 2]`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> poly, err = chebyfit(cos, [1, 2], 5, error=True) + >>> nprint(poly) + [0.00291682, 0.146166, -0.732491, 0.174141, 0.949553] + >>> nprint(err, 12) + 1.61351758081e-5 + + The polynomial can be evaluated using ``polyval``:: + + >>> nprint(polyval(poly, 1.6), 12) + -0.0291858904138 + >>> nprint(cos(1.6), 12) + -0.0291995223013 + + Sampling the true error at 1000 points shows that the error + estimate generated by ``chebyfit`` is remarkably good:: + + >>> error = lambda x: abs(cos(x) - polyval(poly, x)) + >>> nprint(max([error(1+n/1000.) for n in range(1000)]), 12) + 1.61349954245e-5 + + **Choice of degree** + + The degree `N` can be set arbitrarily high, to obtain an + arbitrarily good approximation. As a rule of thumb, an + `N`-term Chebyshev approximation is good to `N/(b-a)` decimal + places on a unit interval (although this depends on how + well-behaved `f` is). The cost grows accordingly: ``chebyfit`` + evaluates the function `(N^2)/2` times to compute the + coefficients and an additional `N` times to estimate the error. + + **Possible issues** + + One should be careful to use a sufficiently high working + precision both when calling ``chebyfit`` and when evaluating + the resulting polynomial, as the polynomial is sometimes + ill-conditioned. It is for example difficult to reach + 15-digit accuracy when evaluating the polynomial using + machine precision floats, no matter the theoretical + accuracy of the polynomial. (The option to return the + coefficients in Chebyshev form should be made available + in the future.) + + It is important to note the Chebyshev approximation works + poorly if `f` is not smooth. A function containing singularities, + rapid oscillation, etc can be approximated more effectively by + multiplying it by a weight function that cancels out the + nonsmooth features, or by dividing the interval into several + segments. + """ + a, b = ctx._as_points(interval) + orig = ctx.prec + try: + ctx.prec = orig + int(N**0.5) + 20 + c = [chebcoeff(ctx,f,a,b,k,N) for k in range(N)] + d = [ctx.zero] * N + d[0] = -c[0]/2 + h = ctx.mpf(0.5) + T = chebT(ctx, ctx.mpf(2)/(b-a), ctx.mpf(-1)*(b+a)/(b-a)) + for k in range(N): + Tk = T.next() + for i in range(len(Tk)): + d[i] += c[k]*Tk[i] + d = d[::-1] + # Estimate maximum error + err = ctx.zero + for k in range(N): + x = ctx.cos(ctx.pi*k/N) * (b-a)*h + (b+a)*h + err = max(err, abs(f(x) - ctx.polyval(d, x))) + finally: + ctx.prec = orig + if error: + return d, +err + else: + return d + +@defun +def fourier(ctx, f, interval, N): + r""" + Computes the Fourier series of degree `N` of the given function + on the interval `[a, b]`. More precisely, :func:`fourier` returns + two lists `(c, s)` of coefficients (the cosine series and sine + series, respectively), such that + + .. math :: + + f(x) \sim \sum_{k=0}^N + c_k \cos(k m) + s_k \sin(k m) + + where `m = 2 \pi / (b-a)`. + + Note that many texts define the first coefficient as `2 c_0` instead + of `c_0`. The easiest way to evaluate the computed series correctly + is to pass it to :func:`fourierval`. + + **Examples** + + The function `f(x) = x` has a simple Fourier series on the standard + interval `[-\pi, \pi]`. The cosine coefficients are all zero (because + the function has odd symmetry), and the sine coefficients are + rational numbers:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> c, s = fourier(lambda x: x, [-pi, pi], 5) + >>> nprint(c) + [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + >>> nprint(s) + [0.0, 2.0, -1.0, 0.666667, -0.5, 0.4] + + This computes a Fourier series of a nonsymmetric function on + a nonstandard interval:: + + >>> I = [-1, 1.5] + >>> f = lambda x: x**2 - 4*x + 1 + >>> cs = fourier(f, I, 4) + >>> nprint(cs[0]) + [0.583333, 1.12479, -1.27552, 0.904708, -0.441296] + >>> nprint(cs[1]) + [0.0, -2.6255, 0.580905, 0.219974, -0.540057] + + It is instructive to plot a function along with its truncated + Fourier series:: + + >>> plot([f, lambda x: fourierval(cs, I, x)], I) #doctest: +SKIP + + Fourier series generally converge slowly (and may not converge + pointwise). For example, if `f(x) = \cosh(x)`, a 10-term Fourier + series gives an `L^2` error corresponding to 2-digit accuracy:: + + >>> I = [-1, 1] + >>> cs = fourier(cosh, I, 9) + >>> g = lambda x: (cosh(x) - fourierval(cs, I, x))**2 + >>> nprint(sqrt(quad(g, I))) + 0.00467963 + + :func:`fourier` uses numerical quadrature. For nonsmooth functions, + the accuracy (and speed) can be improved by including all singular + points in the interval specification:: + + >>> nprint(fourier(abs, [-1, 1], 0), 10) + ([0.5000441648], [0.0]) + >>> nprint(fourier(abs, [-1, 0, 1], 0), 10) + ([0.5], [0.0]) + + """ + interval = ctx._as_points(interval) + a = interval[0] + b = interval[-1] + L = b-a + cos_series = [] + sin_series = [] + cutoff = ctx.eps*10 + for n in xrange(N+1): + m = 2*n*ctx.pi/L + an = 2*ctx.quadgl(lambda t: f(t)*ctx.cos(m*t), interval)/L + bn = 2*ctx.quadgl(lambda t: f(t)*ctx.sin(m*t), interval)/L + if n == 0: + an /= 2 + if abs(an) < cutoff: an = ctx.zero + if abs(bn) < cutoff: bn = ctx.zero + cos_series.append(an) + sin_series.append(bn) + return cos_series, sin_series + +@defun +def fourierval(ctx, series, interval, x): + """ + Evaluates a Fourier series (in the format computed by + by :func:`fourier` for the given interval) at the point `x`. + + The series should be a pair `(c, s)` where `c` is the + cosine series and `s` is the sine series. The two lists + need not have the same length. + """ + cs, ss = series + ab = ctx._as_points(interval) + a = interval[0] + b = interval[-1] + m = 2*ctx.pi/(ab[-1]-ab[0]) + s = ctx.zero + s += ctx.fsum(cs[n]*ctx.cos(m*n*x) for n in xrange(len(cs)) if cs[n]) + s += ctx.fsum(ss[n]*ctx.sin(m*n*x) for n in xrange(len(ss)) if ss[n]) + return s \ No newline at end of file diff --git a/compiler/gdsMill/mpmath/calculus/calculus.py b/compiler/gdsMill/mpmath/calculus/calculus.py new file mode 100644 index 00000000..52fe4183 --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/calculus.py @@ -0,0 +1,5 @@ +class CalculusMethods(object): + pass + +def defun(f): + setattr(CalculusMethods, f.__name__, f) diff --git a/compiler/gdsMill/mpmath/calculus/differentiation.py b/compiler/gdsMill/mpmath/calculus/differentiation.py new file mode 100644 index 00000000..b0738970 --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/differentiation.py @@ -0,0 +1,438 @@ +from calculus import defun + +#----------------------------------------------------------------------------# +# Differentiation # +#----------------------------------------------------------------------------# + +@defun +def difference_delta(ctx, s, n): + r""" + Given a sequence `(s_k)` containing at least `n+1` items, returns the + `n`-th forward difference, + + .. math :: + + \Delta^n = \sum_{k=0}^{\infty} (-1)^{k+n} {n \choose k} s_k. + """ + n = int(n) + d = ctx.zero + b = (-1) ** (n & 1) + for k in xrange(n+1): + d += b * s[k] + b = (b * (k-n)) // (k+1) + return d + +@defun +def diff(ctx, f, x, n=1, method='step', scale=1, direction=0): + r""" + Numerically computes the derivative of `f`, `f'(x)`. Optionally, + computes the `n`-th derivative, `f^{(n)}(x)`, for any order `n`. + + **Basic examples** + + Derivatives of a simple function:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> diff(lambda x: x**2 + x, 1.0) + 3.0 + >>> diff(lambda x: x**2 + x, 1.0, 2) + 2.0 + >>> diff(lambda x: x**2 + x, 1.0, 3) + 0.0 + + The exponential function is invariant under differentiation:: + + >>> nprint([diff(exp, 3, n) for n in range(5)]) + [20.0855, 20.0855, 20.0855, 20.0855, 20.0855] + + **Method** + + One of two differentiation algorithms can be chosen with the + ``method`` keyword argument. The two options are ``'step'``, + and ``'quad'``. The default method is ``'step'``. + + ``'step'``: + + The derivative is computed using a finite difference + approximation, with a small step h. This requires n+1 function + evaluations and must be performed at (n+1) times the target + precision. Accordingly, f must support fast evaluation at high + precision. + + ``'quad'``: + + The derivative is computed using complex + numerical integration. This requires a larger number of function + evaluations, but the advantage is that not much extra precision + is required. For high order derivatives, this method may thus + be faster if f is very expensive to evaluate at high precision. + + With ``'quad'`` the result is likely to have a small imaginary + component even if the derivative is actually real:: + + >>> diff(sqrt, 1, method='quad') # doctest:+ELLIPSIS + (0.5 - 9.44...e-27j) + + **Scale** + + The scale option specifies the scale of variation of f. The step + size in the finite difference is taken to be approximately + eps*scale. Thus, for example if `f(x) = \cos(1000 x)`, the scale + should be set to 1/1000 and if `f(x) = \cos(x/1000)`, the scale + should be 1000. By default, scale = 1. + + (In practice, the default scale will work even for `\cos(1000 x)` or + `\cos(x/1000)`. Changing this parameter is a good idea if the scale + is something *preposterous*.) + + If numerical integration is used, the radius of integration is + taken to be equal to scale/2. Note that f must not have any + singularities within the circle of radius scale/2 centered around + x. If possible, a larger scale value is preferable because it + typically makes the integration faster and more accurate. + + **Direction** + + By default, :func:`diff` uses a central difference approximation. + This corresponds to direction=0. Alternatively, it can compute a + left difference (direction=-1) or right difference (direction=1). + This is useful for computing left- or right-sided derivatives + of nonsmooth functions: + + >>> diff(abs, 0, direction=0) + 0.0 + >>> diff(abs, 0, direction=1) + 1.0 + >>> diff(abs, 0, direction=-1) + -1.0 + + More generally, if the direction is nonzero, a right difference + is computed where the step size is multiplied by sign(direction). + For example, with direction=+j, the derivative from the positive + imaginary direction will be computed. + + This option only makes sense with method='step'. If integration + is used, it is assumed that f is analytic, implying that the + derivative is the same in all directions. + + """ + if n == 0: + return f(ctx.convert(x)) + orig = ctx.prec + try: + if method == 'step': + ctx.prec = (orig+20) * (n+1) + h = ctx.ldexp(scale, -orig-10) + # Applying the finite difference formula recursively n times, + # we get a step sum weighted by a row of binomial coefficients + # Directed: steps x, x+h, ... x+n*h + if direction: + h *= ctx.sign(direction) + steps = xrange(n+1) + norm = h**n + # Central: steps x-n*h, x-(n-2)*h ..., x, ..., x+(n-2)*h, x+n*h + else: + steps = xrange(-n, n+1, 2) + norm = (2*h)**n + v = ctx.difference_delta([f(x+k*h) for k in steps], n) + v = v / norm + elif method == 'quad': + ctx.prec += 10 + radius = ctx.mpf(scale)/2 + def g(t): + rei = radius*ctx.expj(t) + z = x + rei + return f(z) / rei**n + d = ctx.quadts(g, [0, 2*ctx.pi]) + v = d * ctx.factorial(n) / (2*ctx.pi) + else: + raise ValueError("unknown method: %r" % method) + finally: + ctx.prec = orig + return +v + +@defun +def diffs(ctx, f, x, n=None, method='step', scale=1, direction=0): + r""" + Returns a generator that yields the sequence of derivatives + + .. math :: + + f(x), f'(x), f''(x), \ldots, f^{(k)}(x), \ldots + + With ``method='step'``, :func:`diffs` uses only `O(k)` + function evaluations to generate the first `k` derivatives, + rather than the roughly `O(k^2)` evaluations + required if one calls :func:`diff` `k` separate times. + + With `n < \infty`, the generator stops as soon as the + `n`-th derivative has been generated. If the exact number of + needed derivatives is known in advance, this is further + slightly more efficient. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15 + >>> nprint(list(diffs(cos, 1, 5))) + [0.540302, -0.841471, -0.540302, 0.841471, 0.540302, -0.841471] + >>> for i, d in zip(range(6), diffs(cos, 1)): print i, d + ... + 0 0.54030230586814 + 1 -0.841470984807897 + 2 -0.54030230586814 + 3 0.841470984807897 + 4 0.54030230586814 + 5 -0.841470984807897 + + """ + if n is None: + n = ctx.inf + else: + n = int(n) + + if method != 'step': + k = 0 + while k < n: + yield ctx.diff(f, x, k) + k += 1 + return + + targetprec = ctx.prec + + def getvalues(m): + callprec = ctx.prec + try: + ctx.prec = workprec = (targetprec+20) * (m+1) + h = ctx.ldexp(scale, -targetprec-10) + if direction: + h *= ctx.sign(direction) + y = [f(x+h*k) for k in xrange(m+1)] + hnorm = h + else: + y = [f(x+h*k) for k in xrange(-m, m+1, 2)] + hnorm = 2*h + return y, hnorm, workprec + finally: + ctx.prec = callprec + + yield f(ctx.convert(x)) + if n < 1: + return + + if n == ctx.inf: + A, B = 1, 2 + else: + A, B = 1, n+1 + + while 1: + y, hnorm, workprec = getvalues(B) + for k in xrange(A, B): + try: + callprec = ctx.prec + ctx.prec = workprec + d = ctx.difference_delta(y, k) / hnorm**k + finally: + ctx.prec = callprec + yield +d + if k >= n: + return + A, B = B, int(A*1.4+1) + B = min(B, n) + +@defun +def differint(ctx, f, x, n=1, x0=0): + r""" + Calculates the Riemann-Liouville differintegral, or fractional + derivative, defined by + + .. math :: + + \,_{x_0}{\mathbb{D}}^n_xf(x) \frac{1}{\Gamma(m-n)} \frac{d^m}{dx^m} + \int_{x_0}^{x}(x-t)^{m-n-1}f(t)dt + + where `f` is a given (presumably well-behaved) function, + `x` is the evaluation point, `n` is the order, and `x_0` is + the reference point of integration (`m` is an arbitrary + parameter selected automatically). + + With `n = 1`, this is just the standard derivative `f'(x)`; with `n = 2`, + the second derivative `f''(x)`, etc. With `n = -1`, it gives + `\int_{x_0}^x f(t) dt`, with `n = -2` + it gives `\int_{x_0}^x \left( \int_{x_0}^t f(u) du \right) dt`, etc. + + As `n` is permitted to be any number, this operator generalizes + iterated differentiation and iterated integration to a single + operator with a continuous order parameter. + + **Examples** + + There is an exact formula for the fractional derivative of a + monomial `x^p`, which may be used as a reference. For example, + the following gives a half-derivative (order 0.5):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> x = mpf(3); p = 2; n = 0.5 + >>> differint(lambda t: t**p, x, n) + 7.81764019044672 + >>> gamma(p+1)/gamma(p-n+1) * x**(p-n) + 7.81764019044672 + + Another useful test function is the exponential function, whose + integration / differentiation formula easy generalizes + to arbitrary order. Here we first compute a third derivative, + and then a triply nested integral. (The reference point `x_0` + is set to `-\infty` to avoid nonzero endpoint terms.):: + + >>> differint(lambda x: exp(pi*x), -1.5, 3) + 0.278538406900792 + >>> exp(pi*-1.5) * pi**3 + 0.278538406900792 + >>> differint(lambda x: exp(pi*x), 3.5, -3, -inf) + 1922.50563031149 + >>> exp(pi*3.5) / pi**3 + 1922.50563031149 + + However, for noninteger `n`, the differentiation formula for the + exponential function must be modified to give the same result as the + Riemann-Liouville differintegral:: + + >>> x = mpf(3.5) + >>> c = pi + >>> n = 1+2*j + >>> differint(lambda x: exp(c*x), x, n) + (-123295.005390743 + 140955.117867654j) + >>> x**(-n) * exp(c)**x * (x*c)**n * gammainc(-n, 0, x*c) / gamma(-n) + (-123295.005390743 + 140955.117867654j) + + + """ + m = max(int(ctx.ceil(ctx.re(n)))+1, 1) + r = m-n-1 + g = lambda x: ctx.quad(lambda t: (x-t)**r * f(t), [x0, x]) + return ctx.diff(g, x, m) / ctx.gamma(m-n) + +@defun +def diffun(ctx, f, n=1, **options): + """ + Given a function f, returns a function g(x) that evaluates the nth + derivative f^(n)(x):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> cos2 = diffun(sin) + >>> sin2 = diffun(sin, 4) + >>> cos(1.3), cos2(1.3) + (0.267498828624587, 0.267498828624587) + >>> sin(1.3), sin2(1.3) + (0.963558185417193, 0.963558185417193) + + The function f must support arbitrary precision evaluation. + See :func:`diff` for additional details and supported + keyword options. + """ + if n == 0: + return f + def g(x): + return ctx.diff(f, x, n, **options) + return g + +@defun +def taylor(ctx, f, x, n, **options): + r""" + Produces a degree-`n` Taylor polynomial around the point `x` of the + given function `f`. The coefficients are returned as a list. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint(chop(taylor(sin, 0, 5))) + [0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333] + + The coefficients are computed using high-order numerical + differentiation. The function must be possible to evaluate + to arbitrary precision. See :func:`diff` for additional details + and supported keyword options. + + Note that to evaluate the Taylor polynomial as an approximation + of `f`, e.g. with :func:`polyval`, the coefficients must be reversed, + and the point of the Taylor expansion must be subtracted from + the argument: + + >>> p = taylor(exp, 2.0, 10) + >>> polyval(p[::-1], 2.5 - 2.0) + 12.1824939606092 + >>> exp(2.5) + 12.1824939607035 + + """ + return [d/ctx.factorial(i) for i, d in enumerate(ctx.diffs(f, x, n, **options))] + +@defun +def pade(ctx, a, L, M): + r""" + Computes a Pade approximation of degree `(L, M)` to a function. + Given at least `L+M+1` Taylor coefficients `a` approximating + a function `A(x)`, :func:`pade` returns coefficients of + polynomials `P, Q` satisfying + + .. math :: + + P = \sum_{k=0}^L p_k x^k + + Q = \sum_{k=0}^M q_k x^k + + Q_0 = 1 + + A(x) Q(x) = P(x) + O(x^{L+M+1}) + + `P(x)/Q(x)` can provide a good approximation to an analytic function + beyond the radius of convergence of its Taylor series (example + from G.A. Baker 'Essentials of Pade Approximants' Academic Press, + Ch.1A):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> one = mpf(1) + >>> def f(x): + ... return sqrt((one + 2*x)/(one + x)) + ... + >>> a = taylor(f, 0, 6) + >>> p, q = pade(a, 3, 3) + >>> x = 10 + >>> polyval(p[::-1], x)/polyval(q[::-1], x) + 1.38169105566806 + >>> f(x) + 1.38169855941551 + + """ + # To determine L+1 coefficients of P and M coefficients of Q + # L+M+1 coefficients of A must be provided + assert(len(a) >= L+M+1) + + if M == 0: + if L == 0: + return [ctx.one], [ctx.one] + else: + return a[:L+1], [ctx.one] + + # Solve first + # a[L]*q[1] + ... + a[L-M+1]*q[M] = -a[L+1] + # ... + # a[L+M-1]*q[1] + ... + a[L]*q[M] = -a[L+M] + A = ctx.matrix(M) + for j in range(M): + for i in range(min(M, L+j+1)): + A[j, i] = a[L+j-i] + v = -ctx.matrix(a[(L+1):(L+M+1)]) + x = ctx.lu_solve(A, v) + q = [ctx.one] + list(x) + # compute p + p = [0]*(L+1) + for i in range(L+1): + s = a[i] + for j in range(1, min(M,i) + 1): + s += q[j]*a[i-j] + p[i] = s + return p, q diff --git a/compiler/gdsMill/mpmath/calculus/extrapolation.py b/compiler/gdsMill/mpmath/calculus/extrapolation.py new file mode 100644 index 00000000..476c38e7 --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/extrapolation.py @@ -0,0 +1,1013 @@ +from calculus import defun +from itertools import izip + +@defun +def richardson(ctx, seq): + r""" + Given a list ``seq`` of the first `N` elements of a slowly convergent + infinite sequence, :func:`richardson` computes the `N`-term + Richardson extrapolate for the limit. + + :func:`richardson` returns `(v, c)` where `v` is the estimated + limit and `c` is the magnitude of the largest weight used during the + computation. The weight provides an estimate of the precision + lost to cancellation. Due to cancellation effects, the sequence must + be typically be computed at a much higher precision than the target + accuracy of the extrapolation. + + **Applicability and issues** + + The `N`-step Richardson extrapolation algorithm used by + :func:`richardson` is described in [1]. + + Richardson extrapolation only works for a specific type of sequence, + namely one converging like partial sums of + `P(1)/Q(1) + P(2)/Q(2) + \ldots` where `P` and `Q` are polynomials. + When the sequence does not convergence at such a rate + :func:`richardson` generally produces garbage. + + Richardson extrapolation has the advantage of being fast: the `N`-term + extrapolate requires only `O(N)` arithmetic operations, and usually + produces an estimate that is accurate to `O(N)` digits. Contrast with + the Shanks transformation (see :func:`shanks`), which requires + `O(N^2)` operations. + + :func:`richardson` is unable to produce an estimate for the + approximation error. One way to estimate the error is to perform + two extrapolations with slightly different `N` and comparing the + results. + + Richardson extrapolation does not work for oscillating sequences. + As a simple workaround, :func:`richardson` detects if the last + three elements do not differ monotonically, and in that case + applies extrapolation only to the even-index elements. + + **Example** + + Applying Richardson extrapolation to the Leibniz series for `\pi`:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m)) + ... for m in range(1,30)] + >>> v, c = richardson(S[:10]) + >>> v + 3.2126984126984126984126984127 + >>> nprint([v-pi, c]) + [0.0711058, 2.0] + + >>> v, c = richardson(S[:30]) + >>> v + 3.14159265468624052829954206226 + >>> nprint([v-pi, c]) + [1.09645e-9, 20833.3] + + **References** + + 1. C. M. Bender & S. A. Orszag, "Advanced Mathematical Methods for + Scientists and Engineers", Springer 1999, pp. 375-376 + + """ + assert len(seq) >= 3 + if ctx.sign(seq[-1]-seq[-2]) != ctx.sign(seq[-2]-seq[-3]): + seq = seq[::2] + N = len(seq)//2-1 + s = ctx.zero + # The general weight is c[k] = (N+k)**N * (-1)**(k+N) / k! / (N-k)! + # To avoid repeated factorials, we simplify the quotient + # of successive weights to obtain a recurrence relation + c = (-1)**N * N**N / ctx.mpf(ctx._ifac(N)) + maxc = 1 + for k in xrange(N+1): + s += c * seq[N+k] + maxc = max(abs(c), maxc) + c *= (k-N)*ctx.mpf(k+N+1)**N + c /= ((1+k)*ctx.mpf(k+N)**N) + return s, maxc + +@defun +def shanks(ctx, seq, table=None, randomized=False): + r""" + Given a list ``seq`` of the first `N` elements of a slowly + convergent infinite sequence `(A_k)`, :func:`shanks` computes the iterated + Shanks transformation `S(A), S(S(A)), \ldots, S^{N/2}(A)`. The Shanks + transformation often provides strong convergence acceleration, + especially if the sequence is oscillating. + + The iterated Shanks transformation is computed using the Wynn + epsilon algorithm (see [1]). :func:`shanks` returns the full + epsilon table generated by Wynn's algorithm, which can be read + off as follows: + + * The table is a list of lists forming a lower triangular matrix, + where higher row and column indices correspond to more accurate + values. + * The columns with even index hold dummy entries (required for the + computation) and the columns with odd index hold the actual + extrapolates. + * The last element in the last row is typically the most + accurate estimate of the limit. + * The difference to the third last element in the last row + provides an estimate of the approximation error. + * The magnitude of the second last element provides an estimate + of the numerical accuracy lost to cancellation. + + For convenience, so the extrapolation is stopped at an odd index + so that ``shanks(seq)[-1][-1]`` always gives an estimate of the + limit. + + Optionally, an existing table can be passed to :func:`shanks`. + This can be used to efficiently extend a previous computation after + new elements have been appended to the sequence. The table will + then be updated in-place. + + **The Shanks transformation** + + The Shanks transformation is defined as follows (see [2]): given + the input sequence `(A_0, A_1, \ldots)`, the transformed sequence is + given by + + .. math :: + + S(A_k) = \frac{A_{k+1}A_{k-1}-A_k^2}{A_{k+1}+A_{k-1}-2 A_k} + + The Shanks transformation gives the exact limit `A_{\infty}` in a + single step if `A_k = A + a q^k`. Note in particular that it + extrapolates the exact sum of a geometric series in a single step. + + Applying the Shanks transformation once often improves convergence + substantially for an arbitrary sequence, but the optimal effect is + obtained by applying it iteratively: + `S(S(A_k)), S(S(S(A_k))), \ldots`. + + Wynn's epsilon algorithm provides an efficient way to generate + the table of iterated Shanks transformations. It reduces the + computation of each element to essentially a single division, at + the cost of requiring dummy elements in the table. See [1] for + details. + + **Precision issues** + + Due to cancellation effects, the sequence must be typically be + computed at a much higher precision than the target accuracy + of the extrapolation. + + If the Shanks transformation converges to the exact limit (such + as if the sequence is a geometric series), then a division by + zero occurs. By default, :func:`shanks` handles this case by + terminating the iteration and returning the table it has + generated so far. With *randomized=True*, it will instead + replace the zero by a pseudorandom number close to zero. + (TODO: find a better solution to this problem.) + + **Examples** + + We illustrate by applying Shanks transformation to the Leibniz + series for `\pi`:: + + >>> from mpmath import * + >>> mp.dps = 50 + >>> S = [4*sum(mpf(-1)**n/(2*n+1) for n in range(m)) + ... for m in range(1,30)] + >>> + >>> T = shanks(S[:7]) + >>> for row in T: + ... nprint(row) + ... + [-0.75] + [1.25, 3.16667] + [-1.75, 3.13333, -28.75] + [2.25, 3.14524, 82.25, 3.14234] + [-2.75, 3.13968, -177.75, 3.14139, -969.937] + [3.25, 3.14271, 327.25, 3.14166, 3515.06, 3.14161] + + The extrapolated accuracy is about 4 digits, and about 4 digits + may have been lost due to cancellation:: + + >>> L = T[-1] + >>> nprint([abs(L[-1] - pi), abs(L[-1] - L[-3]), abs(L[-2])]) + [2.22532e-5, 4.78309e-5, 3515.06] + + Now we extend the computation:: + + >>> T = shanks(S[:25], T) + >>> L = T[-1] + >>> nprint([abs(L[-1] - pi), abs(L[-1] - L[-3]), abs(L[-2])]) + [3.75527e-19, 1.48478e-19, 2.96014e+17] + + The value for pi is now accurate to 18 digits. About 18 digits may + also have been lost to cancellation. + + Here is an example with a geometric series, where the convergence + is immediate (the sum is exactly 1):: + + >>> mp.dps = 15 + >>> for row in shanks([0.5, 0.75, 0.875, 0.9375, 0.96875]): + ... nprint(row) + [4.0] + [8.0, 1.0] + + **References** + + 1. P. R. Graves-Morris, D. E. Roberts, A. Salam, "The epsilon + algorithm and related topics", Journal of Computational and + Applied Mathematics, Volume 122, Issue 1-2 (October 2000) + + 2. C. M. Bender & S. A. Orszag, "Advanced Mathematical Methods for + Scientists and Engineers", Springer 1999, pp. 368-375 + + """ + assert len(seq) >= 2 + if table: + START = len(table) + else: + START = 0 + table = [] + STOP = len(seq) - 1 + if STOP & 1: + STOP -= 1 + one = ctx.one + eps = +ctx.eps + if randomized: + from random import Random + rnd = Random() + rnd.seed(START) + for i in xrange(START, STOP): + row = [] + for j in xrange(i+1): + if j == 0: + a, b = 0, seq[i+1]-seq[i] + else: + if j == 1: + a = seq[i] + else: + a = table[i-1][j-2] + b = row[j-1] - table[i-1][j-1] + if not b: + if randomized: + b = rnd.getrandbits(10)*eps + elif i & 1: + return table[:-1] + else: + return table + row.append(a + one/b) + table.append(row) + return table + +@defun +def sumem(ctx, f, interval, tol=None, reject=10, integral=None, + adiffs=None, bdiffs=None, verbose=False, error=False): + r""" + Uses the Euler-Maclaurin formula to compute an approximation accurate + to within ``tol`` (which defaults to the present epsilon) of the sum + + .. math :: + + S = \sum_{k=a}^b f(k) + + where `(a,b)` are given by ``interval`` and `a` or `b` may be + infinite. The approximation is + + .. math :: + + S \sim \int_a^b f(x) \,dx + \frac{f(a)+f(b)}{2} + + \sum_{k=1}^{\infty} \frac{B_{2k}}{(2k)!} + \left(f^{(2k-1)}(b)-f^{(2k-1)}(a)\right). + + The last sum in the Euler-Maclaurin formula is not generally + convergent (a notable exception is if `f` is a polynomial, in + which case Euler-Maclaurin actually gives an exact result). + + The summation is stopped as soon as the quotient between two + consecutive terms falls below *reject*. That is, by default + (*reject* = 10), the summation is continued as long as each + term adds at least one decimal. + + Although not convergent, convergence to a given tolerance can + often be "forced" if `b = \infty` by summing up to `a+N` and then + applying the Euler-Maclaurin formula to the sum over the range + `(a+N+1, \ldots, \infty)`. This procedure is implemented by + :func:`nsum`. + + By default numerical quadrature and differentiation is used. + If the symbolic values of the integral and endpoint derivatives + are known, it is more efficient to pass the value of the + integral explicitly as ``integral`` and the derivatives + explicitly as ``adiffs`` and ``bdiffs``. The derivatives + should be given as iterables that yield + `f(a), f'(a), f''(a), \ldots` (and the equivalent for `b`). + + **Examples** + + Summation of an infinite series, with automatic and symbolic + integral and derivative values (the second should be much faster):: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> sumem(lambda n: 1/n**2, [32, inf]) + 0.03174336652030209012658168043874142714132886413417 + >>> I = mpf(1)/32 + >>> D = adiffs=((-1)**n*fac(n+1)*32**(-2-n) for n in xrange(999)) + >>> sumem(lambda n: 1/n**2, [32, inf], integral=I, adiffs=D) + 0.03174336652030209012658168043874142714132886413417 + + An exact evaluation of a finite polynomial sum:: + + >>> sumem(lambda n: n**5-12*n**2+3*n, [-100000, 200000]) + 10500155000624963999742499550000.0 + >>> print sum(n**5-12*n**2+3*n for n in xrange(-100000, 200001)) + 10500155000624963999742499550000 + + """ + tol = tol or +ctx.eps + interval = ctx._as_points(interval) + a = ctx.convert(interval[0]) + b = ctx.convert(interval[-1]) + err = ctx.zero + prev = 0 + M = 10000 + if a == ctx.ninf: adiffs = (0 for n in xrange(M)) + else: adiffs = adiffs or ctx.diffs(f, a) + if b == ctx.inf: bdiffs = (0 for n in xrange(M)) + else: bdiffs = bdiffs or ctx.diffs(f, b) + orig = ctx.prec + #verbose = 1 + try: + ctx.prec += 10 + s = ctx.zero + for k, (da, db) in enumerate(izip(adiffs, bdiffs)): + if k & 1: + term = (db-da) * ctx.bernoulli(k+1) / ctx.factorial(k+1) + mag = abs(term) + if verbose: + print "term", k, "magnitude =", ctx.nstr(mag) + if k > 4 and mag < tol: + s += term + break + elif k > 4 and abs(prev) / mag < reject: + if verbose: + print "Failed to converge" + err += mag + break + else: + s += term + prev = term + # Endpoint correction + if a != ctx.ninf: s += f(a)/2 + if b != ctx.inf: s += f(b)/2 + # Tail integral + if verbose: + print "Integrating f(x) from x = %s to %s" % (ctx.nstr(a), ctx.nstr(b)) + if integral: + s += integral + else: + integral, ierr = ctx.quad(f, interval, error=True) + if verbose: + print "Integration error:", ierr + s += integral + err += ierr + finally: + ctx.prec = orig + if error: + return s, err + else: + return s + +@defun +def adaptive_extrapolation(ctx, update, emfun, kwargs): + option = kwargs.get + tol = option('tol', ctx.eps/2**10) + verbose = option('verbose', False) + maxterms = option('maxterms', ctx.dps*10) + method = option('method', 'r+s').split('+') + skip = option('skip', 0) + steps = iter(option('steps', xrange(10, 10**9, 10))) + strict = option('strict') + #steps = (10 for i in xrange(1000)) + if 'd' in method or 'direct' in method: + TRY_RICHARDSON = TRY_SHANKS = TRY_EULER_MACLAURIN = False + else: + TRY_RICHARDSON = ('r' in method) or ('richardson' in method) + TRY_SHANKS = ('s' in method) or ('shanks' in method) + TRY_EULER_MACLAURIN = ('e' in method) or \ + ('euler-maclaurin' in method) + + last_richardson_value = 0 + shanks_table = [] + index = 0 + step = 10 + partial = [] + best = ctx.zero + orig = ctx.prec + try: + if TRY_RICHARDSON or TRY_SHANKS: + ctx.prec *= 4 + else: + ctx.prec += 30 + while 1: + if index >= maxterms: + break + + # Get new batch of terms + try: + step = steps.next() + except StopIteration: + pass + if verbose: + print "-"*70 + print "Adding terms #%i-#%i" % (index, index+step) + update(partial, xrange(index, index+step)) + index += step + + # Check direct error + best = partial[-1] + error = abs(best - partial[-2]) + if verbose: + print "Direct error: %s" % ctx.nstr(error) + if error <= tol: + return best + + # Check each extrapolation method + if TRY_RICHARDSON: + value, maxc = ctx.richardson(partial) + # Convergence + richardson_error = abs(value - last_richardson_value) + if verbose: + print "Richardson error: %s" % ctx.nstr(richardson_error) + # Convergence + if richardson_error <= tol: + return value + last_richardson_value = value + # Unreliable due to cancellation + if ctx.eps*maxc > tol: + if verbose: + print "Ran out of precision for Richardson" + TRY_RICHARDSON = False + if richardson_error < error: + error = richardson_error + best = value + if TRY_SHANKS: + shanks_table = ctx.shanks(partial, shanks_table, randomized=True) + row = shanks_table[-1] + if len(row) == 2: + est1 = row[-1] + shanks_error = 0 + else: + est1, maxc, est2 = row[-1], abs(row[-2]), row[-3] + shanks_error = abs(est1-est2) + if verbose: + print "Shanks error: %s" % ctx.nstr(shanks_error) + if shanks_error <= tol: + return est1 + if ctx.eps*maxc > tol: + if verbose: + print "Ran out of precision for Shanks" + TRY_SHANKS = False + if shanks_error < error: + error = shanks_error + best = est1 + if TRY_EULER_MACLAURIN: + if ctx.mpc(ctx.sign(partial[-1]) / ctx.sign(partial[-2])).ae(-1): + if verbose: + print ("NOT using Euler-Maclaurin: the series appears" + " to be alternating, so numerical\n quadrature" + " will most likely fail") + TRY_EULER_MACLAURIN = False + else: + value, em_error = emfun(index, tol) + value += partial[-1] + if verbose: + print "Euler-Maclaurin error: %s" % ctx.nstr(em_error) + if em_error <= tol: + return value + if em_error < error: + best = value + finally: + ctx.prec = orig + if strict: + raise ctx.NoConvergence + if verbose: + print "Warning: failed to converge to target accuracy" + return best + +@defun +def nsum(ctx, f, interval, **kwargs): + r""" + Computes the sum + + .. math :: S = \sum_{k=a}^b f(k) + + where `(a, b)` = *interval*, and where `a = -\infty` and/or + `b = \infty` are allowed. Two examples of + infinite series that can be summed by :func:`nsum`, where the + first converges rapidly and the second converges slowly, are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nsum(lambda n: 1/fac(n), [0, inf]) + 2.71828182845905 + >>> nsum(lambda n: 1/n**2, [1, inf]) + 1.64493406684823 + + When appropriate, :func:`nsum` applies convergence acceleration to + accurately estimate the sums of slowly convergent series. + If the sum is finite, :func:`nsum` currently does not + attempt to perform any extrapolation, and simply calls + :func:`fsum`. + + **Options** + + *tol* + Desired maximum final error. Defaults roughly to the + epsilon of the working precision. + + *method* + Which summation algorithm to use (described below). + Default: ``'richardson+shanks'``. + + *maxterms* + Cancel after at most this many terms. Default: 10*dps. + + *steps* + An iterable giving the number of terms to add between + each extrapolation attempt. The default sequence is + [10, 20, 30, 40, ...]. For example, if you know that + approximately 100 terms will be required, efficiency might be + improved by setting this to [100, 10]. Then the first + extrapolation will be performed after 100 terms, the second + after 110, etc. + + *verbose* + Print details about progress. + + **Methods** + + Unfortunately, an algorithm that can efficiently sum any infinite + series does not exist. :func:`nsum` implements several different + algorithms that each work well in different cases. The *method* + keyword argument selects a method. + + The default method is ``'r+s'``, i.e. both Richardson extrapolation + and Shanks transformation is attempted. A slower method that + handles more cases is ``'r+s+e'``. For very high precision + summation, or if the summation needs to be fast (for example if + multiple sums need to be evaluated), it is a good idea to + investigate which one method works best and only use that. + + ``'richardson'`` / ``'r'``: + Uses Richardson extrapolation. Provides useful extrapolation + when `f(k) \sim P(k)/Q(k)` or when `f(k) \sim (-1)^k P(k)/Q(k)` + for polynomials `P` and `Q`. See :func:`richardson` for + additional information. + + ``'shanks'`` / ``'s'``: + Uses Shanks transformation. Typically provides useful + extrapolation when `f(k) \sim c^k` or when successive terms + alternate signs. Is able to sum some divergent series. + See :func:`shanks` for additional information. + + ``'euler-maclaurin'`` / ``'e'``: + Uses the Euler-Maclaurin summation formula to approximate + the remainder sum by an integral. This requires high-order + numerical derivatives and numerical integration. The advantage + of this algorithm is that it works regardless of the + decay rate of `f`, as long as `f` is sufficiently smooth. + See :func:`sumem` for additional information. + + ``'direct'`` / ``'d'``: + Does not perform any extrapolation. This can be used + (and should only be used for) rapidly convergent series. + The summation automatically stops when the terms + decrease below the target tolerance. + + **Basic examples** + + A finite sum:: + + >>> nsum(lambda k: 1/k, [1, 6]) + 2.45 + + Summation of a series going to negative infinity and a doubly + infinite series:: + + >>> nsum(lambda k: 1/k**2, [-inf, -1]) + 1.64493406684823 + >>> nsum(lambda k: 1/(1+k**2), [-inf, inf]) + 3.15334809493716 + + :func:`nsum` handles sums of complex numbers:: + + >>> nsum(lambda k: (0.5+0.25j)**k, [0, inf]) + (1.6 + 0.8j) + + The following sum converges very rapidly, so it is most + efficient to sum it by disabling convergence acceleration:: + + >>> mp.dps = 1000 + >>> a = nsum(lambda k: -(-1)**k * k**2 / fac(2*k), [1, inf], + ... method='direct') + >>> b = (cos(1)+sin(1))/4 + >>> abs(a-b) < mpf('1e-998') + True + + **Examples with Richardson extrapolation** + + Richardson extrapolation works well for sums over rational + functions, as well as their alternating counterparts:: + + >>> mp.dps = 50 + >>> nsum(lambda k: 1 / k**3, [1, inf], + ... method='richardson') + 1.2020569031595942853997381615114499907649862923405 + >>> zeta(3) + 1.2020569031595942853997381615114499907649862923405 + + >>> nsum(lambda n: (n + 3)/(n**3 + n**2), [1, inf], + ... method='richardson') + 2.9348022005446793094172454999380755676568497036204 + >>> pi**2/2-2 + 2.9348022005446793094172454999380755676568497036204 + + >>> nsum(lambda k: (-1)**k / k**3, [1, inf], + ... method='richardson') + -0.90154267736969571404980362113358749307373971925537 + >>> -3*zeta(3)/4 + -0.90154267736969571404980362113358749307373971925538 + + **Examples with Shanks transformation** + + The Shanks transformation works well for geometric series + and typically provides excellent acceleration for Taylor + series near the border of their disk of convergence. + Here we apply it to a series for `\log(2)`, which can be + seen as the Taylor series for `\log(1+x)` with `x = 1`:: + + >>> nsum(lambda k: -(-1)**k/k, [1, inf], + ... method='shanks') + 0.69314718055994530941723212145817656807550013436025 + >>> log(2) + 0.69314718055994530941723212145817656807550013436025 + + Here we apply it to a slowly convergent geometric series:: + + >>> nsum(lambda k: mpf('0.995')**k, [0, inf], + ... method='shanks') + 200.0 + + Finally, Shanks' method works very well for alternating series + where `f(k) = (-1)^k g(k)`, and often does so regardless of + the exact decay rate of `g(k)`:: + + >>> mp.dps = 15 + >>> nsum(lambda k: (-1)**(k+1) / k**1.5, [1, inf], + ... method='shanks') + 0.765147024625408 + >>> (2-sqrt(2))*zeta(1.5)/2 + 0.765147024625408 + + The following slowly convergent alternating series has no known + closed-form value. Evaluating the sum a second time at higher + precision indicates that the value is probably correct:: + + >>> nsum(lambda k: (-1)**k / log(k), [2, inf], + ... method='shanks') + 0.924299897222939 + >>> mp.dps = 30 + >>> nsum(lambda k: (-1)**k / log(k), [2, inf], + ... method='shanks') + 0.92429989722293885595957018136 + + **Examples with Euler-Maclaurin summation** + + The sum in the following example has the wrong rate of convergence + for either Richardson or Shanks to be effective. + + >>> f = lambda k: log(k)/k**2.5 + >>> mp.dps = 15 + >>> nsum(f, [1, inf], method='euler-maclaurin') + 0.38734195032621 + >>> -diff(zeta, 2.5) + 0.38734195032621 + + Increasing ``steps`` improves speed at higher precision:: + + >>> mp.dps = 50 + >>> nsum(f, [1, inf], method='euler-maclaurin', steps=[250]) + 0.38734195032620997271199237593105101319948228874688 + >>> -diff(zeta, 2.5) + 0.38734195032620997271199237593105101319948228874688 + + **Divergent series** + + The Shanks transformation is able to sum some *divergent* + series. In particular, it is often able to sum Taylor series + beyond their radius of convergence (this is due to a relation + between the Shanks transformation and Pade approximations; + see :func:`pade` for an alternative way to evaluate divergent + Taylor series). + + Here we apply it to `\log(1+x)` far outside the region of + convergence:: + + >>> mp.dps = 50 + >>> nsum(lambda k: -(-9)**k/k, [1, inf], + ... method='shanks') + 2.3025850929940456840179914546843642076011014886288 + >>> log(10) + 2.3025850929940456840179914546843642076011014886288 + + A particular type of divergent series that can be summed + using the Shanks transformation is geometric series. + The result is the same as using the closed-form formula + for an infinite geometric series:: + + >>> mp.dps = 15 + >>> for n in range(-8, 8): + ... if n == 1: + ... continue + ... print mpf(n), mpf(1)/(1-n), nsum(lambda k: n**k, [0, inf], + ... method='shanks') + ... + -8.0 0.111111111111111 0.111111111111111 + -7.0 0.125 0.125 + -6.0 0.142857142857143 0.142857142857143 + -5.0 0.166666666666667 0.166666666666667 + -4.0 0.2 0.2 + -3.0 0.25 0.25 + -2.0 0.333333333333333 0.333333333333333 + -1.0 0.5 0.5 + 0.0 1.0 1.0 + 2.0 -1.0 -1.0 + 3.0 -0.5 -0.5 + 4.0 -0.333333333333333 -0.333333333333333 + 5.0 -0.25 -0.25 + 6.0 -0.2 -0.2 + 7.0 -0.166666666666667 -0.166666666666667 + + """ + a, b = ctx._as_points(interval) + if a == ctx.ninf: + if b == ctx.inf: + return f(0) + ctx.nsum(lambda k: f(-k) + f(k), [1, ctx.inf], **kwargs) + return ctx.nsum(f, [-b, ctx.inf], **kwargs) + elif b != ctx.inf: + return ctx.fsum(f(ctx.mpf(k)) for k in xrange(int(a), int(b)+1)) + + a = int(a) + + def update(partial_sums, indices): + if partial_sums: + psum = partial_sums[-1] + else: + psum = ctx.zero + for k in indices: + psum = psum + f(a + ctx.mpf(k)) + partial_sums.append(psum) + + prec = ctx.prec + + def emfun(point, tol): + workprec = ctx.prec + ctx.prec = prec + 10 + v = ctx.sumem(f, [a+point, ctx.inf], tol, error=1) + ctx.prec = workprec + return v + + return +ctx.adaptive_extrapolation(update, emfun, kwargs) + +@defun +def nprod(ctx, f, interval, **kwargs): + """ + Computes the product + + .. math :: P = \prod_{k=a}^b f(k) + + where `(a, b)` = *interval*, and where `a = -\infty` and/or + `b = \infty` are allowed. + + This function is essentially equivalent to applying :func:`nsum` + to the logarithm of the product (which, of course, becomes a + series). All keyword arguments passed to :func:`nprod` are + forwarded verbatim to :func:`nsum`. + + **Examples** + + A simple finite product:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprod(lambda k: k, [1, 4]) + 24.0 + + A large number of infinite products have known exact values, + and can therefore be used as a reference. Most of the following + examples are taken from MathWorld [1]. + + A few infinite products with simple values are:: + + >>> 2*nprod(lambda k: (4*k**2)/(4*k**2-1), [1, inf]) + 3.14159265358979 + >>> nprod(lambda k: (1+1/k)**2/(1+2/k), [1, inf]) + 2.0 + >>> nprod(lambda k: (k**3-1)/(k**3+1), [2, inf]) + 0.666666666666667 + >>> nprod(lambda k: (1-1/k**2), [2, inf]) + 0.5 + + Next, several more infinite products with more complicated + values:: + + >>> nprod(lambda k: exp(1/k**2), [1, inf]) + 5.18066831789712 + >>> exp(pi**2/6) + 5.18066831789712 + + >>> nprod(lambda k: (k**2-1)/(k**2+1), [2, inf]) + 0.272029054982133 + >>> pi*csch(pi) + 0.272029054982133 + + >>> nprod(lambda k: (k**4-1)/(k**4+1), [2, inf]) + 0.8480540493529 + >>> pi*sinh(pi)/(cosh(sqrt(2)*pi)-cos(sqrt(2)*pi)) + 0.8480540493529 + + >>> nprod(lambda k: (1+1/k+1/k**2)**2/(1+2/k+3/k**2), [1, inf]) + 1.84893618285824 + >>> 3*sqrt(2)*cosh(pi*sqrt(3)/2)**2*csch(pi*sqrt(2))/pi + 1.84893618285824 + + >>> nprod(lambda k: (1-1/k**4), [2, inf]) + 0.919019477593744 + >>> sinh(pi)/(4*pi) + 0.919019477593744 + + >>> nprod(lambda k: (1-1/k**6), [2, inf]) + 0.982684277742192 + >>> (1+cosh(pi*sqrt(3)))/(12*pi**2) + 0.982684277742192 + + >>> nprod(lambda k: (1+1/k**2), [2, inf]) + 1.83803895518749 + >>> sinh(pi)/(2*pi) + 1.83803895518749 + + >>> nprod(lambda n: (1+1/n)**n * exp(1/(2*n)-1), [1, inf]) + 1.44725592689037 + >>> exp(1+euler/2)/sqrt(2*pi) + 1.44725592689037 + + The following two products are equivalent and can be evaluated in + terms of a Jacobi theta function. Pi can be replaced by any value + (as long as convergence is preserved):: + + >>> nprod(lambda k: (1-pi**-k)/(1+pi**-k), [1, inf]) + 0.383845120748167 + >>> nprod(lambda k: tanh(k*log(pi)/2), [1, inf]) + 0.383845120748167 + >>> jtheta(4,0,1/pi) + 0.383845120748167 + + This product does not have a known closed form value:: + + >>> nprod(lambda k: (1-1/2**k), [1, inf]) + 0.288788095086602 + + **References** + + 1. E. W. Weisstein, "Infinite Product", + http://mathworld.wolfram.com/InfiniteProduct.html, + MathWorld + + """ + a, b = ctx._as_points(interval) + if a != ctx.ninf and b != ctx.inf: + return ctx.fprod(f(ctx.mpf(k)) for k in xrange(int(a), int(b)+1)) + + orig = ctx.prec + try: + # TODO: we are evaluating log(1+eps) -> eps, which is + # inaccurate. This currently works because nsum greatly + # increases the working precision. But we should be + # more intelligent and handle the precision here. + ctx.prec += 10 + v = ctx.nsum(lambda n: ctx.ln(f(n)), interval, **kwargs) + finally: + ctx.prec = orig + return +ctx.exp(v) + +@defun +def limit(ctx, f, x, direction=1, exp=False, **kwargs): + r""" + Computes an estimate of the limit + + .. math :: + + \lim_{t \to x} f(t) + + where `x` may be finite or infinite. + + For finite `x`, :func:`limit` evaluates `f(x + d/n)` for + consecutive integer values of `n`, where the approach direction + `d` may be specified using the *direction* keyword argument. + For infinite `x`, :func:`limit` evaluates values of + `f(\mathrm{sign}(x) \cdot n)`. + + If the approach to the limit is not sufficiently fast to give + an accurate estimate directly, :func:`limit` attempts to find + the limit using Richardson extrapolation or the Shanks + transformation. You can select between these methods using + the *method* keyword (see documentation of :func:`nsum` for + more information). + + **Options** + + The following options are available with essentially the + same meaning as for :func:`nsum`: *tol*, *method*, *maxterms*, + *steps*, *verbose*. + + If the option *exp=True* is set, `f` will be + sampled at exponentially spaced points `n = 2^1, 2^2, 2^3, \ldots` + instead of the linearly spaced points `n = 1, 2, 3, \ldots`. + This can sometimes improve the rate of convergence so that + :func:`limit` may return a more accurate answer (and faster). + However, do note that this can only be used if `f` + supports fast and accurate evaluation for arguments that + are extremely close to the limit point (or if infinite, + very large arguments). + + **Examples** + + A basic evaluation of a removable singularity:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> limit(lambda x: (x-sin(x))/x**3, 0) + 0.166666666666666666666666666667 + + Computing the exponential function using its limit definition:: + + >>> limit(lambda n: (1+3/n)**n, inf) + 20.0855369231876677409285296546 + >>> exp(3) + 20.0855369231876677409285296546 + + A limit for `\pi`:: + + >>> f = lambda n: 2**(4*n+1)*fac(n)**4/(2*n+1)/fac(2*n)**2 + >>> limit(f, inf) + 3.14159265358979323846264338328 + + Calculating the coefficient in Stirling's formula:: + + >>> limit(lambda n: fac(n) / (sqrt(n)*(n/e)**n), inf) + 2.50662827463100050241576528481 + >>> sqrt(2*pi) + 2.50662827463100050241576528481 + + Evaluating Euler's constant `\gamma` using the limit representation + + .. math :: + + \gamma = \lim_{n \rightarrow \infty } \left[ \left( + \sum_{k=1}^n \frac{1}{k} \right) - \log(n) \right] + + (which converges notoriously slowly):: + + >>> f = lambda n: sum([mpf(1)/k for k in range(1,n+1)]) - log(n) + >>> limit(f, inf) + 0.577215664901532860606512090082 + >>> +euler + 0.577215664901532860606512090082 + + With default settings, the following limit converges too slowly + to be evaluated accurately. Changing to exponential sampling + however gives a perfect result:: + + >>> f = lambda x: sqrt(x**3+x**2)/(sqrt(x**3)+x) + >>> limit(f, inf) + 0.992518488562331431132360378669 + >>> limit(f, inf, exp=True) + 1.0 + + """ + + if ctx.isinf(x): + direction = ctx.sign(x) + g = lambda k: f(ctx.mpf(k+1)*direction) + else: + direction *= ctx.one + g = lambda k: f(x + direction/(k+1)) + if exp: + h = g + g = lambda k: h(2**k) + + def update(values, indices): + for k in indices: + values.append(g(k+1)) + + # XXX: steps used by nsum don't work well + if not 'steps' in kwargs: + kwargs['steps'] = [10] + + return +ctx.adaptive_extrapolation(update, None, kwargs) diff --git a/compiler/gdsMill/mpmath/calculus/odes.py b/compiler/gdsMill/mpmath/calculus/odes.py new file mode 100644 index 00000000..7ad5f15c --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/odes.py @@ -0,0 +1,287 @@ +from bisect import bisect + +class ODEMethods(object): + pass + +def ode_taylor(ctx, derivs, x0, y0, tol_prec, n): + h = tol = ctx.ldexp(1, -tol_prec) + dim = len(y0) + xs = [x0] + ys = [y0] + x = x0 + y = y0 + orig = ctx.prec + try: + ctx.prec = orig*(1+n) + # Use n steps with Euler's method to get + # evaluation points for derivatives + for i in range(n): + fxy = derivs(x, y) + y = [y[i]+h*fxy[i] for i in xrange(len(y))] + x += h + xs.append(x) + ys.append(y) + # Compute derivatives + ser = [[] for d in range(dim)] + for j in range(n+1): + s = [0]*dim + b = (-1) ** (j & 1) + k = 1 + for i in range(j+1): + for d in range(dim): + s[d] += b * ys[i][d] + b = (b * (j-k+1)) // (-k) + k += 1 + scale = h**(-j) / ctx.fac(j) + for d in range(dim): + s[d] = s[d] * scale + ser[d].append(s[d]) + finally: + ctx.prec = orig + # Estimate radius for which we can get full accuracy. + # XXX: do this right for zeros + radius = ctx.one + for ts in ser: + if ts[-1]: + radius = min(radius, ctx.nthroot(tol/abs(ts[-1]), n)) + radius /= 2 # XXX + return ser, x0+radius + +def odefun(ctx, F, x0, y0, tol=None, degree=None, method='taylor', verbose=False): + r""" + Returns a function `y(x) = [y_0(x), y_1(x), \ldots, y_n(x)]` + that is a numerical solution of the `n+1`-dimensional first-order + ordinary differential equation (ODE) system + + .. math :: + + y_0'(x) = F_0(x, [y_0(x), y_1(x), \ldots, y_n(x)]) + + y_1'(x) = F_1(x, [y_0(x), y_1(x), \ldots, y_n(x)]) + + \vdots + + y_n'(x) = F_n(x, [y_0(x), y_1(x), \ldots, y_n(x)]) + + The derivatives are specified by the vector-valued function + *F* that evaluates + `[y_0', \ldots, y_n'] = F(x, [y_0, \ldots, y_n])`. + The initial point `x_0` is specified by the scalar argument *x0*, + and the initial value `y(x_0) = [y_0(x_0), \ldots, y_n(x_0)]` is + specified by the vector argument *y0*. + + For convenience, if the system is one-dimensional, you may optionally + provide just a scalar value for *y0*. In this case, *F* should accept + a scalar *y* argument and return a scalar. The solution function + *y* will return scalar values instead of length-1 vectors. + + Evaluation of the solution function `y(x)` is permitted + for any `x \ge x_0`. + + A high-order ODE can be solved by transforming it into first-order + vector form. This transformation is described in standard texts + on ODEs. Examples will also be given below. + + **Options, speed and accuracy** + + By default, :func:`odefun` uses a high-order Taylor series + method. For reasonably well-behaved problems, the solution will + be fully accurate to within the working precision. Note that + *F* must be possible to evaluate to very high precision + for the generation of Taylor series to work. + + To get a faster but less accurate solution, you can set a large + value for *tol* (which defaults roughly to *eps*). If you just + want to plot the solution or perform a basic simulation, + *tol = 0.01* is likely sufficient. + + The *degree* argument controls the degree of the solver (with + *method='taylor'*, this is the degree of the Taylor series + expansion). A higher degree means that a longer step can be taken + before a new local solution must be generated from *F*, + meaning that fewer steps are required to get from `x_0` to a given + `x_1`. On the other hand, a higher degree also means that each + local solution becomes more expensive (i.e., more evaluations of + *F* are required per step, and at higher precision). + + The optimal setting therefore involves a tradeoff. Generally, + decreasing the *degree* for Taylor series is likely to give faster + solution at low precision, while increasing is likely to be better + at higher precision. + + The function + object returned by :func:`odefun` caches the solutions at all step + points and uses polynomial interpolation between step points. + Therefore, once `y(x_1)` has been evaluated for some `x_1`, + `y(x)` can be evaluated very quickly for any `x_0 \le x \le x_1`. + and continuing the evaluation up to `x_2 > x_1` is also fast. + + **Examples of first-order ODEs** + + We will solve the standard test problem `y'(x) = y(x), y(0) = 1` + which has explicit solution `y(x) = \exp(x)`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> f = odefun(lambda x, y: y, 0, 1) + >>> for x in [0, 1, 2.5]: + ... print f(x), exp(x) + ... + 1.0 1.0 + 2.71828182845905 2.71828182845905 + 12.1824939607035 12.1824939607035 + + The solution with high precision:: + + >>> mp.dps = 50 + >>> f = odefun(lambda x, y: y, 0, 1) + >>> f(1) + 2.7182818284590452353602874713526624977572470937 + >>> exp(1) + 2.7182818284590452353602874713526624977572470937 + + Using the more general vectorized form, the test problem + can be input as (note that *f* returns a 1-element vector):: + + >>> mp.dps = 15 + >>> f = odefun(lambda x, y: [y[0]], 0, [1]) + >>> f(1) + [2.71828182845905] + + :func:`odefun` can solve nonlinear ODEs, which are generally + impossible (and at best difficult) to solve analytically. As + an example of a nonlinear ODE, we will solve `y'(x) = x \sin(y(x))` + for `y(0) = \pi/2`. An exact solution happens to be known + for this problem, and is given by + `y(x) = 2 \tan^{-1}\left(\exp\left(x^2/2\right)\right)`:: + + >>> f = odefun(lambda x, y: x*sin(y), 0, pi/2) + >>> for x in [2, 5, 10]: + ... print f(x), 2*atan(exp(mpf(x)**2/2)) + ... + 2.87255666284091 2.87255666284091 + 3.14158520028345 3.14158520028345 + 3.14159265358979 3.14159265358979 + + If `F` is independent of `y`, an ODE can be solved using direct + integration. We can therefore obtain a reference solution with + :func:`quad`:: + + >>> f = lambda x: (1+x**2)/(1+x**3) + >>> g = odefun(lambda x, y: f(x), pi, 0) + >>> g(2*pi) + 0.72128263801696 + >>> quad(f, [pi, 2*pi]) + 0.72128263801696 + + **Examples of second-order ODEs** + + We will solve the harmonic oscillator equation `y''(x) + y(x) = 0`. + To do this, we introduce the helper functions `y_0 = y, y_1 = y_0'` + whereby the original equation can be written as `y_1' + y_0' = 0`. Put + together, we get the first-order, two-dimensional vector ODE + + .. math :: + + \begin{cases} + y_0' = y_1 \\ + y_1' = -y_0 + \end{cases} + + To get a well-defined IVP, we need two initial values. With + `y(0) = y_0(0) = 1` and `-y'(0) = y_1(0) = 0`, the problem will of + course be solved by `y(x) = y_0(x) = \cos(x)` and + `-y'(x) = y_1(x) = \sin(x)`. We check this:: + + >>> f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0]) + >>> for x in [0, 1, 2.5, 10]: + ... nprint(f(x), 15) + ... nprint([cos(x), sin(x)], 15) + ... print "---" + ... + [1.0, 0.0] + [1.0, 0.0] + --- + [0.54030230586814, 0.841470984807897] + [0.54030230586814, 0.841470984807897] + --- + [-0.801143615546934, 0.598472144103957] + [-0.801143615546934, 0.598472144103957] + --- + [-0.839071529076452, -0.54402111088937] + [-0.839071529076452, -0.54402111088937] + --- + + Note that we get both the sine and the cosine solutions + simultaneously. + + **TODO** + + * Better automatic choice of degree and step size + * Make determination of Taylor series convergence radius + more robust + * Allow solution for `x < x_0` + * Allow solution for complex `x` + * Test for difficult (ill-conditioned) problems + * Implement Runge-Kutta and other algorithms + + """ + if tol: + tol_prec = int(-ctx.log(tol, 2))+10 + else: + tol_prec = ctx.prec+10 + degree = degree or (3 + int(3*ctx.dps/2.)) + workprec = ctx.prec + 40 + try: + len(y0) + return_vector = True + except TypeError: + F_ = F + F = lambda x, y: [F_(x, y[0])] + y0 = [y0] + return_vector = False + ser, xb = ode_taylor(ctx, F, x0, y0, tol_prec, degree) + series_boundaries = [x0, xb] + series_data = [(ser, x0, xb)] + # We will be working with vectors of Taylor series + def mpolyval(ser, a): + return [ctx.polyval(s[::-1], a) for s in ser] + # Find nearest expansion point; compute if necessary + def get_series(x): + if x < x0: + raise ValueError + n = bisect(series_boundaries, x) + if n < len(series_boundaries): + return series_data[n-1] + while 1: + ser, xa, xb = series_data[-1] + if verbose: + print "Computing Taylor series for [%f, %f]" % (xa, xb) + y = mpolyval(ser, xb-xa) + xa = xb + ser, xb = ode_taylor(ctx, F, xb, y, tol_prec, degree) + series_boundaries.append(xb) + series_data.append((ser, xa, xb)) + if x <= xb: + return series_data[-1] + # Evaluation function + def interpolant(x): + x = ctx.convert(x) + orig = ctx.prec + try: + ctx.prec = workprec + ser, xa, xb = get_series(x) + y = mpolyval(ser, x-xa) + finally: + ctx.prec = orig + if return_vector: + return [+yk for yk in y] + else: + return +y[0] + return interpolant + +ODEMethods.odefun = odefun + +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/compiler/gdsMill/mpmath/calculus/optimization.py b/compiler/gdsMill/mpmath/calculus/optimization.py new file mode 100644 index 00000000..5873e9da --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/optimization.py @@ -0,0 +1,1087 @@ +from copy import copy + +class OptimizationMethods(object): + def __init__(ctx): + pass + +############## +# 1D-SOLVERS # +############## + +class Newton: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0 close to the root. + + Pro: + + * converges fast + * sometimes more robust than secant with bad second starting point + + Contra: + + * converges slowly for multiple roots + * needs first derivative + * 2 function evaluations per iteration + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + else: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + + def __iter__(self): + f = self.f + df = self.df + x0 = self.x0 + while True: + x1 = x0 - f(x0) / df(x0) + error = abs(x1 - x0) + x0 = x1 + yield (x1, error) + +class Secant: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0 and x1 close to the root. + x1 defaults to x0 + 0.25. + + Pro: + + * converges fast + + Contra: + + * converges slowly for multiple roots + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + self.x1 = self.x0 + 0.25 + elif len(x0) == 2: + self.x0 = x0[0] + self.x1 = x0[1] + else: + raise ValueError('expected 1 or 2 starting points, got %i' % len(x0)) + self.f = f + + def __iter__(self): + f = self.f + x0 = self.x0 + x1 = self.x1 + f0 = f(x0) + while True: + f1 = f(x1) + l = x1 - x0 + if not l: + break + s = (f1 - f0) / l + if not s: + break + x0, x1 = x1, x1 - f1/s + f0 = f1 + yield x1, abs(l) + +class MNewton: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting point x0 close to the root. + Uses modified Newton's method that converges fast regardless of the + multiplicity of the root. + + Pro: + + * converges fast for multiple roots + + Contra: + + * needs first and second derivative of f + * 3 function evaluations per iteration + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + if not 'd2f' in kwargs: + def d2f(x): + return self.ctx.diff(df, x) + else: + d2f = kwargs['df'] + self.d2f = d2f + + def __iter__(self): + x = self.x0 + f = self.f + df = self.df + d2f = self.d2f + while True: + prevx = x + fx = f(x) + if fx == 0: + break + dfx = df(x) + d2fx = d2f(x) + # x = x - F(x)/F'(x) with F(x) = f(x)/f'(x) + x -= fx / (dfx - fx * d2fx / dfx) + error = abs(x - prevx) + yield x, error + +class Halley: + """ + 1d-solver generating pairs of approximative root and error. + + Needs a starting point x0 close to the root. + Uses Halley's method with cubic convergence rate. + + Pro: + + * converges even faster the Newton's method + * useful when computing with *many* digits + + Contra: + + * needs first and second derivative of f + * 3 function evaluations per iteration + * converges slowly for multiple roots + """ + + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + if not 'd2f' in kwargs: + def d2f(x): + return self.ctx.diff(df, x) + else: + d2f = kwargs['df'] + self.d2f = d2f + + def __iter__(self): + x = self.x0 + f = self.f + df = self.df + d2f = self.d2f + while True: + prevx = x + fx = f(x) + dfx = df(x) + d2fx = d2f(x) + x -= 2*fx*dfx / (2*dfx**2 - fx*d2fx) + error = abs(x - prevx) + yield x, error + +class Muller: + """ + 1d-solver generating pairs of approximative root and error. + + Needs starting points x0, x1 and x2 close to the root. + x1 defaults to x0 + 0.25; x2 to x1 + 0.25. + Uses Muller's method that converges towards complex roots. + + Pro: + + * converges fast (somewhat faster than secant) + * can find complex roots + + Contra: + + * converges slowly for multiple roots + * may have complex values for real starting points and real roots + + http://en.wikipedia.org/wiki/Muller's_method + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) == 1: + self.x0 = x0[0] + self.x1 = self.x0 + 0.25 + self.x2 = self.x1 + 0.25 + elif len(x0) == 2: + self.x0 = x0[0] + self.x1 = x0[1] + self.x2 = self.x1 + 0.25 + elif len(x0) == 3: + self.x0 = x0[0] + self.x1 = x0[1] + self.x2 = x0[2] + else: + raise ValueError('expected 1, 2 or 3 starting points, got %i' + % len(x0)) + self.f = f + self.verbose = kwargs['verbose'] + + def __iter__(self): + f = self.f + x0 = self.x0 + x1 = self.x1 + x2 = self.x2 + fx0 = f(x0) + fx1 = f(x1) + fx2 = f(x2) + while True: + # TODO: maybe refactoring with function for divided differences + # calculate divided differences + fx2x1 = (fx1 - fx2) / (x1 - x2) + fx2x0 = (fx0 - fx2) / (x0 - x2) + fx1x0 = (fx0 - fx1) / (x0 - x1) + w = fx2x1 + fx2x0 - fx1x0 + fx2x1x0 = (fx1x0 - fx2x1) / (x0 - x2) + if w == 0 and fx2x1x0 == 0: + if self.verbose: + print 'canceled with' + print 'x0 =', x0, ', x1 =', x1, 'and x2 =', x2 + break + x0 = x1 + fx0 = fx1 + x1 = x2 + fx1 = fx2 + # denominator should be as large as possible => choose sign + r = self.ctx.sqrt(w**2 - 4*fx2*fx2x1x0) + if abs(w - r) > abs(w + r): + r = -r + x2 -= 2*fx2 / (w + r) + fx2 = f(x2) + error = abs(x2 - x1) + yield x2, error + +# TODO: consider raising a ValueError when there's no sign change in a and b +class Bisection: + """ + 1d-solver generating pairs of approximative root and error. + + Uses bisection method to find a root of f in [a, b]. + Might fail for multiple roots (needs sign change). + + Pro: + + * robust and reliable + + Contra: + + * converges slowly + * needs sign change + """ + maxsteps = 100 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.f = f + self.a = x0[0] + self.b = x0[1] + + def __iter__(self): + f = self.f + a = self.a + b = self.b + l = b - a + fb = f(b) + while True: + m = self.ctx.ldexp(a + b, -1) + fm = f(m) + if fm * fb < 0: + a = m + else: + b = m + fb = fm + l /= 2 + yield (a + b)/2, abs(l) + +def _getm(method): + """ + Return a function to calculate m for Illinois-like methods. + """ + if method == 'illinois': + def getm(fz, fb): + return 0.5 + elif method == 'pegasus': + def getm(fz, fb): + return fb/(fb + fz) + elif method == 'anderson': + def getm(fz, fb): + m = 1 - fz/fb + if m > 0: + return m + else: + return 0.5 + else: + raise ValueError, "method '%s' not recognized" % method + return getm + +class Illinois: + """ + 1d-solver generating pairs of approximative root and error. + + Uses Illinois method or similar to find a root of f in [a, b]. + Might fail for multiple roots (needs sign change). + Combines bisect with secant (improved regula falsi). + + The only difference between the methods is the scaling factor m, which is + used to ensure convergence (you can choose one using the 'method' keyword): + + Illinois method ('illinois'): + m = 0.5 + + Pegasus method ('pegasus'): + m = fb/(fb + fz) + + Anderson-Bjoerk method ('anderson'): + m = 1 - fz/fb if positive else 0.5 + + Pro: + + * converges very fast + + Contra: + + * has problems with multiple roots + * needs sign change + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.a = x0[0] + self.b = x0[1] + self.f = f + self.tol = kwargs['tol'] + self.verbose = kwargs['verbose'] + self.method = kwargs.get('method', 'illinois') + self.getm = _getm(self.method) + if self.verbose: + print 'using %s method' % self.method + + def __iter__(self): + method = self.method + f = self.f + a = self.a + b = self.b + fa = f(a) + fb = f(b) + m = None + while True: + l = b - a + if l == 0: + break + s = (fb - fa) / l + z = a - fa/s + fz = f(z) + if abs(fz) < self.tol: + # TODO: better condition (when f is very flat) + if self.verbose: + print 'canceled with z =', z + yield z, l + break + if fz * fb < 0: # root in [z, b] + a = b + fa = fb + b = z + fb = fz + else: # root in [a, z] + m = self.getm(fz, fb) + b = z + fb = fz + fa = m*fa # scale down to ensure convergence + if self.verbose and m and not method == 'illinois': + print 'm:', m + yield (a + b)/2, abs(l) + +def Pegasus(*args, **kwargs): + """ + 1d-solver generating pairs of approximative root and error. + + Uses Pegasus method to find a root of f in [a, b]. + Wrapper for illinois to use method='pegasus'. + """ + kwargs['method'] = 'pegasus' + return Illinois(*args, **kwargs) + +def Anderson(*args, **kwargs): + u""" + 1d-solver generating pairs of approximative root and error. + + Uses Anderson-Bjoerk method to find a root of f in [a, b]. + Wrapper for illinois to use method='pegasus'. + """ + kwargs['method'] = 'anderson' + return Illinois(*args, **kwargs) + +# TODO: check whether it's possible to combine it with Illinois stuff +class Ridder: + """ + 1d-solver generating pairs of approximative root and error. + + Ridders' method to find a root of f in [a, b]. + Is told to perform as well as Brent's method while being simpler. + + Pro: + + * very fast + * simpler than Brent's method + + Contra: + + * two function evaluations per step + * has problems with multiple roots + * needs sign change + + http://en.wikipedia.org/wiki/Ridders'_method + """ + maxsteps = 30 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + self.f = f + if len(x0) != 2: + raise ValueError('expected interval of 2 points, got %i' % len(x0)) + self.x1 = x0[0] + self.x2 = x0[1] + self.verbose = kwargs['verbose'] + self.tol = kwargs['tol'] + + def __iter__(self): + ctx = self.ctx + f = self.f + x1 = self.x1 + fx1 = f(x1) + x2 = self.x2 + fx2 = f(x2) + while True: + x3 = 0.5*(x1 + x2) + fx3 = f(x3) + x4 = x3 + (x3 - x1) * ctx.sign(fx1 - fx2) * fx3 / ctx.sqrt(fx3**2 - fx1*fx2) + fx4 = f(x4) + if abs(fx4) < self.tol: + # TODO: better condition (when f is very flat) + if self.verbose: + print 'canceled with f(x4) =', fx4 + yield x4, abs(x1 - x2) + break + if fx4 * fx2 < 0: # root in [x4, x2] + x1 = x4 + fx1 = fx4 + else: # root in [x1, x4] + x2 = x4 + fx2 = fx4 + error = abs(x1 - x2) + yield (x1 + x2)/2, error + +class ANewton: + """ + EXPERIMENTAL 1d-solver generating pairs of approximative root and error. + + Uses Newton's method modified to use Steffensens method when convergence is + slow. (I.e. for multiple roots.) + """ + maxsteps = 20 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + if not len(x0) == 1: + raise ValueError('expected 1 starting point, got %i' % len(x0)) + self.x0 = x0[0] + self.f = f + if not 'df' in kwargs: + def df(x): + return self.ctx.diff(f, x) + else: + df = kwargs['df'] + self.df = df + def phi(x): + return x - f(x) / df(x) + self.phi = phi + self.verbose = kwargs['verbose'] + + def __iter__(self): + x0 = self.x0 + f = self.f + df = self.df + phi = self.phi + error = 0 + counter = 0 + while True: + prevx = x0 + try: + x0 = phi(x0) + except ZeroDivisionError: + if self.verbose: + 'ZeroDivisionError: canceled with x =', x0 + break + preverror = error + error = abs(prevx - x0) + # TODO: decide not to use convergence acceleration + if error and abs(error - preverror) / error < 1: + if self.verbose: + print 'converging slowly' + counter += 1 + if counter >= 3: + # accelerate convergence + phi = steffensen(phi) + counter = 0 + if self.verbose: + print 'accelerating convergence' + yield x0, error + +# TODO: add Brent + +############################ +# MULTIDIMENSIONAL SOLVERS # +############################ + +def jacobian(ctx, f, x): + """ + Calculate the Jacobian matrix of a function at the point x0. + + This is the first derivative of a vectorial function: + + f : R^m -> R^n with m >= n + """ + x = ctx.matrix(x) + h = ctx.sqrt(ctx.eps) + fx = ctx.matrix(f(*x)) + m = len(fx) + n = len(x) + J = ctx.matrix(m, n) + for j in xrange(n): + xj = x.copy() + xj[j] += h + Jj = (ctx.matrix(f(*xj)) - fx) / h + for i in xrange(m): + J[i,j] = Jj[i] + return J + +# TODO: test with user-specified jacobian matrix, support force_type +class MDNewton: + """ + Find the root of a vector function numerically using Newton's method. + + f is a vector function representing a nonlinear equation system. + + x0 is the starting point close to the root. + + J is a function returning the Jacobian matrix for a point. + + Supports overdetermined systems. + + Use the 'norm' keyword to specify which norm to use. Defaults to max-norm. + The function to calculate the Jacobian matrix can be given using the + keyword 'J'. Otherwise it will be calculated numerically. + + Please note that this method converges only locally. Especially for high- + dimensional systems it is not trivial to find a good starting point being + close enough to the root. + + It is recommended to use a faster, low-precision solver from SciPy [1] or + OpenOpt [2] to get an initial guess. Afterwards you can use this method for + root-polishing to any precision. + + [1] http://scipy.org + + [2] http://openopt.org + """ + maxsteps = 10 + + def __init__(self, ctx, f, x0, **kwargs): + self.ctx = ctx + self.f = f + if isinstance(x0, (tuple, list)): + x0 = ctx.matrix(x0) + assert x0.cols == 1, 'need a vector' + self.x0 = x0 + if 'J' in kwargs: + self.J = kwargs['J'] + else: + def J(*x): + return ctx.jacobian(f, x) + self.J = J + self.norm = kwargs['norm'] + self.verbose = kwargs['verbose'] + + def __iter__(self): + f = self.f + x0 = self.x0 + norm = self.norm + J = self.J + fx = self.ctx.matrix(f(*x0)) + fxnorm = norm(fx) + cancel = False + while not cancel: + # get direction of descent + fxn = -fx + Jx = J(*x0) + s = self.ctx.lu_solve(Jx, fxn) + if self.verbose: + print 'Jx:' + print Jx + print 's:', s + # damping step size TODO: better strategy (hard task) + l = self.ctx.one + x1 = x0 + s + while True: + if x1 == x0: + if self.verbose: + print "canceled, won't get more excact" + cancel = True + break + fx = self.ctx.matrix(f(*x1)) + newnorm = norm(fx) + if newnorm < fxnorm: + # new x accepted + fxnorm = newnorm + x0 = x1 + break + l /= 2 + x1 = x0 + l*s + yield (x0, fxnorm) + +############# +# UTILITIES # +############# + +str2solver = {'newton':Newton, 'secant':Secant,'mnewton':MNewton, + 'halley':Halley, 'muller':Muller, 'bisect':Bisection, + 'illinois':Illinois, 'pegasus':Pegasus, 'anderson':Anderson, + 'ridder':Ridder, 'anewton':ANewton, 'mdnewton':MDNewton} + +def findroot(ctx, f, x0, solver=Secant, tol=None, verbose=False, verify=True, **kwargs): + r""" + Find a solution to `f(x) = 0`, using *x0* as starting point or + interval for *x*. + + Multidimensional overdetermined systems are supported. + You can specify them using a function or a list of functions. + + If the found root does not satisfy `|f(x)^2 < \mathrm{tol}|`, + an exception is raised (this can be disabled with *verify=False*). + + **Arguments** + + *f* + one dimensional function + *x0* + starting point, several starting points or interval (depends on solver) + *tol* + the returned solution has an error smaller than this + *verbose* + print additional information for each iteration if true + *verify* + verify the solution and raise a ValueError if `|f(x) > \mathrm{tol}|` + *solver* + a generator for *f* and *x0* returning approximative solution and error + *maxsteps* + after how many steps the solver will cancel + *df* + first derivative of *f* (used by some solvers) + *d2f* + second derivative of *f* (used by some solvers) + *multidimensional* + force multidimensional solving + *J* + Jacobian matrix of *f* (used by multidimensional solvers) + *norm* + used vector norm (used by multidimensional solvers) + + solver has to be callable with ``(f, x0, **kwargs)`` and return an generator + yielding pairs of approximative solution and estimated error (which is + expected to be positive). + You can use the following string aliases: + 'secant', 'mnewton', 'halley', 'muller', 'illinois', 'pegasus', 'anderson', + 'ridder', 'anewton', 'bisect' + + See mpmath.optimization for their documentation. + + **Examples** + + The function :func:`findroot` locates a root of a given function using the + secant method by default. A simple example use of the secant method is to + compute `\pi` as the root of `\sin x` closest to `x_0 = 3`:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> findroot(sin, 3) + 3.14159265358979323846264338328 + + The secant method can be used to find complex roots of analytic functions, + although it must in that case generally be given a nonreal starting value + (or else it will never leave the real line):: + + >>> mp.dps = 15 + >>> findroot(lambda x: x**3 + 2*x + 1, j) + (0.226698825758202 + 1.46771150871022j) + + A nice application is to compute nontrivial roots of the Riemann zeta + function with many digits (good initial values are needed for convergence):: + + >>> mp.dps = 30 + >>> findroot(zeta, 0.5+14j) + (0.5 + 14.1347251417346937904572519836j) + + The secant method can also be used as an optimization algorithm, by passing + it a derivative of a function. The following example locates the positive + minimum of the gamma function:: + + >>> mp.dps = 20 + >>> findroot(lambda x: diff(gamma, x), 1) + 1.4616321449683623413 + + Finally, a useful application is to compute inverse functions, such as the + Lambert W function which is the inverse of `w e^w`, given the first + term of the solution's asymptotic expansion as the initial value. In basic + cases, this gives identical results to mpmath's built-in ``lambertw`` + function:: + + >>> def lambert(x): + ... return findroot(lambda w: w*exp(w) - x, log(1+x)) + ... + >>> mp.dps = 15 + >>> lambert(1); lambertw(1) + 0.567143290409784 + 0.567143290409784 + >>> lambert(1000); lambert(1000) + 5.2496028524016 + 5.2496028524016 + + Multidimensional functions are also supported:: + + >>> f = [lambda x1, x2: x1**2 + x2, + ... lambda x1, x2: 5*x1**2 - 3*x1 + 2*x2 - 3] + >>> findroot(f, (0, 0)) + [-0.618033988749895] + [-0.381966011250105] + >>> findroot(f, (10, 10)) + [ 1.61803398874989] + [-2.61803398874989] + + You can verify this by solving the system manually. + + Please note that the following (more general) syntax also works:: + + >>> def f(x1, x2): + ... return x1**2 + x2, 5*x1**2 - 3*x1 + 2*x2 - 3 + ... + >>> findroot(f, (0, 0)) + [-0.618033988749895] + [-0.381966011250105] + + + **Multiple roots** + + For multiple roots all methods of the Newtonian family (including secant) + converge slowly. Consider this example:: + + >>> f = lambda x: (x - 1)**99 + >>> findroot(f, 0.9, verify=False) + 0.918073542444929 + + Even for a very close starting point the secant method converges very + slowly. Use ``verbose=True`` to illustrate this. + + It is possible to modify Newton's method to make it converge regardless of + the root's multiplicity:: + + >>> findroot(f, -10, solver='mnewton') + 1.0 + + This variant uses the first and second derivative of the function, which is + not very efficient. + + Alternatively you can use an experimental Newtonian solver that keeps track + of the speed of convergence and accelerates it using Steffensen's method if + necessary:: + + >>> findroot(f, -10, solver='anewton', verbose=True) + x: -9.88888888888888888889 + error: 0.111111111111111111111 + converging slowly + x: -9.77890011223344556678 + error: 0.10998877665544332211 + converging slowly + x: -9.67002233332199662166 + error: 0.108877778911448945119 + converging slowly + accelerating convergence + x: -9.5622443299551077669 + error: 0.107778003366888854764 + converging slowly + x: 0.99999999999999999214 + error: 10.562244329955107759 + x: 1.0 + error: 7.8598304758094664213e-18 + 1.0 + + + **Complex roots** + + For complex roots it's recommended to use Muller's method as it converges + even for real starting points very fast:: + + >>> findroot(lambda x: x**4 + x + 1, (0, 1, 2), solver='muller') + (0.727136084491197 + 0.934099289460529j) + + + **Intersection methods** + + When you need to find a root in a known interval, it's highly recommended to + use an intersection-based solver like ``'anderson'`` or ``'ridder'``. + Usually they converge faster and more reliable. They have however problems + with multiple roots and usually need a sign change to find a root:: + + >>> findroot(lambda x: x**3, (-1, 1), solver='anderson') + 0.0 + + Be careful with symmetric functions:: + + >>> findroot(lambda x: x**2, (-1, 1), solver='anderson') #doctest:+ELLIPSIS + Traceback (most recent call last): + ... + ZeroDivisionError + + It fails even for better starting points, because there is no sign change:: + + >>> findroot(lambda x: x**2, (-1, .5), solver='anderson') + Traceback (most recent call last): + ... + ValueError: Could not find root within given tolerance. (1 > 2.1684e-19) + Try another starting point or tweak arguments. + + """ + prec = ctx.prec + try: + ctx.prec += 20 + + # initialize arguments + if tol is None: + tol = ctx.eps * 2**10 + + kwargs['verbose'] = kwargs.get('verbose', verbose) + + if 'd1f' in kwargs: + kwargs['df'] = kwargs['d1f'] + + kwargs['tol'] = tol + if isinstance(x0, (list, tuple)): + x0 = [ctx.convert(x) for x in x0] + else: + x0 = [ctx.convert(x0)] + + if isinstance(solver, str): + try: + solver = str2solver[solver] + except KeyError: + raise ValueError('could not recognize solver') + + # accept list of functions + if isinstance(f, (list, tuple)): + f2 = copy(f) + def tmp(*args): + return [fn(*args) for fn in f2] + f = tmp + + # detect multidimensional functions + try: + fx = f(*x0) + multidimensional = isinstance(fx, (list, tuple, ctx.matrix)) + except TypeError: + fx = f(x0[0]) + multidimensional = False + if 'multidimensional' in kwargs: + multidimensional = kwargs['multidimensional'] + if multidimensional: + # only one multidimensional solver available at the moment + solver = MDNewton + if not 'norm' in kwargs: + norm = lambda x: ctx.norm(x, 'inf') + kwargs['norm'] = norm + else: + norm = kwargs['norm'] + else: + norm = abs + + # happily return starting point if it's a root + if norm(fx) == 0: + if multidimensional: + return ctx.matrix(x0) + else: + return x0[0] + + # use solver + iterations = solver(ctx, f, x0, **kwargs) + if 'maxsteps' in kwargs: + maxsteps = kwargs['maxsteps'] + else: + maxsteps = iterations.maxsteps + i = 0 + for x, error in iterations: + if verbose: + print 'x: ', x + print 'error:', error + i += 1 + if error < tol * max(1, norm(x)) or i >= maxsteps: + break + if not isinstance(x, (list, tuple, ctx.matrix)): + xl = [x] + else: + xl = x + if verify and norm(f(*xl))**2 > tol: # TODO: better condition? + raise ValueError('Could not find root within given tolerance. ' + '(%g > %g)\n' + 'Try another starting point or tweak arguments.' + % (norm(f(*xl))**2, tol)) + return x + finally: + ctx.prec = prec + + +def multiplicity(ctx, f, root, tol=None, maxsteps=10, **kwargs): + """ + Return the multiplicity of a given root of f. + + Internally, numerical derivatives are used. This might be inefficient for + higher order derviatives. Due to this, ``multiplicity`` cancels after + evaluating 10 derivatives by default. You can be specify the n-th derivative + using the dnf keyword. + + >>> from mpmath import * + >>> multiplicity(lambda x: sin(x) - 1, pi/2) + 2 + + """ + if tol is None: + tol = ctx.eps ** 0.8 + kwargs['d0f'] = f + for i in xrange(maxsteps): + dfstr = 'd' + str(i) + 'f' + if dfstr in kwargs: + df = kwargs[dfstr] + else: + df = lambda x: ctx.diff(f, x, i) + if not abs(df(root)) < tol: + break + return i + +def steffensen(f): + """ + linear convergent function -> quadratic convergent function + + Steffensen's method for quadratic convergence of a linear converging + sequence. + Don not use it for higher rates of convergence. + It may even work for divergent sequences. + + Definition: + F(x) = (x*f(f(x)) - f(x)**2) / (f(f(x)) - 2*f(x) + x) + + Example + ....... + + You can use Steffensen's method to accelerate a fixpoint iteration of linear + (or less) convergence. + + x* is a fixpoint of the iteration x_{k+1} = phi(x_k) if x* = phi(x*). For + phi(x) = x**2 there are two fixpoints: 0 and 1. + + Let's try Steffensen's method: + + >>> f = lambda x: x**2 + >>> from mpmath.optimization import steffensen + >>> F = steffensen(f) + >>> for x in [0.5, 0.9, 2.0]: + ... fx = Fx = x + ... for i in xrange(10): + ... try: + ... fx = f(fx) + ... except OverflowError: + ... pass + ... try: + ... Fx = F(Fx) + ... except ZeroDivisionError: + ... pass + ... print '%20g %20g' % (fx, Fx) + 0.25 -0.5 + 0.0625 0.1 + 0.00390625 -0.0011236 + 1.52588e-005 1.41691e-009 + 2.32831e-010 -2.84465e-027 + 5.42101e-020 2.30189e-080 + 2.93874e-039 -1.2197e-239 + 8.63617e-078 0 + 7.45834e-155 0 + 5.56268e-309 0 + 0.81 1.02676 + 0.6561 1.00134 + 0.430467 1 + 0.185302 1 + 0.0343368 1 + 0.00117902 1 + 1.39008e-006 1 + 1.93233e-012 1 + 3.73392e-024 1 + 1.39421e-047 1 + 4 1.6 + 16 1.2962 + 256 1.10194 + 65536 1.01659 + 4.29497e+009 1.00053 + 1.84467e+019 1 + 3.40282e+038 1 + 1.15792e+077 1 + 1.34078e+154 1 + 1.34078e+154 1 + + Unmodified, the iteration converges only towards 0. Modified it converges + not only much faster, it converges even to the repelling fixpoint 1. + """ + def F(x): + fx = f(x) + ffx = f(fx) + return (x*ffx - fx**2) / (ffx - 2*fx + x) + return F + +OptimizationMethods.jacobian = jacobian +OptimizationMethods.findroot = findroot +OptimizationMethods.multiplicity = multiplicity + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/compiler/gdsMill/mpmath/calculus/polynomials.py b/compiler/gdsMill/mpmath/calculus/polynomials.py new file mode 100644 index 00000000..7558b6be --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/polynomials.py @@ -0,0 +1,189 @@ +from calculus import defun + +#----------------------------------------------------------------------------# +# Polynomials # +#----------------------------------------------------------------------------# + +# XXX: extra precision +@defun +def polyval(ctx, coeffs, x, derivative=False): + r""" + Given coefficients `[c_n, \ldots, c_2, c_1, c_0]` and a number `x`, + :func:`polyval` evaluates the polynomial + + .. math :: + + P(x) = c_n x^n + \ldots + c_2 x^2 + c_1 x + c_0. + + If *derivative=True* is set, :func:`polyval` simultaneously + evaluates `P(x)` with the derivative, `P'(x)`, and returns the + tuple `(P(x), P'(x))`. + + >>> from mpmath import * + >>> mp.pretty = True + >>> polyval([3, 0, 2], 0.5) + 2.75 + >>> polyval([3, 0, 2], 0.5, derivative=True) + (2.75, 3.0) + + The coefficients and the evaluation point may be any combination + of real or complex numbers. + """ + if not coeffs: + return ctx.zero + p = ctx.convert(coeffs[0]) + q = ctx.zero + for c in coeffs[1:]: + if derivative: + q = p + x*q + p = c + x*p + if derivative: + return p, q + else: + return p + +@defun +def polyroots(ctx, coeffs, maxsteps=50, cleanup=True, extraprec=10, error=False): + """ + Computes all roots (real or complex) of a given polynomial. The roots are + returned as a sorted list, where real roots appear first followed by + complex conjugate roots as adjacent elements. The polynomial should be + given as a list of coefficients, in the format used by :func:`polyval`. + The leading coefficient must be nonzero. + + With *error=True*, :func:`polyroots` returns a tuple *(roots, err)* where + *err* is an estimate of the maximum error among the computed roots. + + **Examples** + + Finding the three real roots of `x^3 - x^2 - 14x + 24`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint(polyroots([1,-1,-14,24]), 4) + [-4.0, 2.0, 3.0] + + Finding the two complex conjugate roots of `4x^2 + 3x + 2`, with an + error estimate:: + + >>> roots, err = polyroots([4,3,2], error=True) + >>> for r in roots: + ... print r + ... + (-0.375 + 0.59947894041409j) + (-0.375 - 0.59947894041409j) + >>> + >>> err + 2.22044604925031e-16 + >>> + >>> polyval([4,3,2], roots[0]) + (2.22044604925031e-16 + 0.0j) + >>> polyval([4,3,2], roots[1]) + (2.22044604925031e-16 + 0.0j) + + The following example computes all the 5th roots of unity; that is, + the roots of `x^5 - 1`:: + + >>> mp.dps = 20 + >>> for r in polyroots([1, 0, 0, 0, 0, -1]): + ... print r + ... + 1.0 + (-0.8090169943749474241 + 0.58778525229247312917j) + (-0.8090169943749474241 - 0.58778525229247312917j) + (0.3090169943749474241 + 0.95105651629515357212j) + (0.3090169943749474241 - 0.95105651629515357212j) + + **Precision and conditioning** + + Provided there are no repeated roots, :func:`polyroots` can typically + compute all roots of an arbitrary polynomial to high precision:: + + >>> mp.dps = 60 + >>> for r in polyroots([1, 0, -10, 0, 1]): + ... print r + ... + -3.14626436994197234232913506571557044551247712918732870123249 + -0.317837245195782244725757617296174288373133378433432554879127 + 0.317837245195782244725757617296174288373133378433432554879127 + 3.14626436994197234232913506571557044551247712918732870123249 + >>> + >>> sqrt(3) + sqrt(2) + 3.14626436994197234232913506571557044551247712918732870123249 + >>> sqrt(3) - sqrt(2) + 0.317837245195782244725757617296174288373133378433432554879127 + + **Algorithm** + + :func:`polyroots` implements the Durand-Kerner method [1], which + uses complex arithmetic to locate all roots simultaneously. + The Durand-Kerner method can be viewed as approximately performing + simultaneous Newton iteration for all the roots. In particular, + the convergence to simple roots is quadratic, just like Newton's + method. + + Although all roots are internally calculated using complex arithmetic, + any root found to have an imaginary part smaller than the estimated + numerical error is truncated to a real number. Real roots are placed + first in the returned list, sorted by value. The remaining complex + roots are sorted by real their parts so that conjugate roots end up + next to each other. + + **References** + + 1. http://en.wikipedia.org/wiki/Durand-Kerner_method + + """ + if len(coeffs) <= 1: + if not coeffs or not coeffs[0]: + raise ValueError("Input to polyroots must not be the zero polynomial") + # Constant polynomial with no roots + return [] + + orig = ctx.prec + weps = +ctx.eps + try: + ctx.prec += 10 + tol = ctx.eps * 128 + deg = len(coeffs) - 1 + # Must be monic + lead = ctx.convert(coeffs[0]) + if lead == 1: + coeffs = map(ctx.convert, coeffs) + else: + coeffs = [c/lead for c in coeffs] + f = lambda x: ctx.polyval(coeffs, x) + roots = [ctx.mpc((0.4+0.9j)**n) for n in xrange(deg)] + err = [ctx.one for n in xrange(deg)] + # Durand-Kerner iteration until convergence + for step in xrange(maxsteps): + if abs(max(err)) < tol: + break + for i in xrange(deg): + if not abs(err[i]) < tol: + p = roots[i] + x = f(p) + for j in range(deg): + if i != j: + try: + x /= (p-roots[j]) + except ZeroDivisionError: + continue + roots[i] = p - x + err[i] = abs(x) + # Remove small imaginary parts + if cleanup: + for i in xrange(deg): + if abs(ctx._im(roots[i])) < weps: + roots[i] = roots[i].real + elif abs(ctx._re(roots[i])) < weps: + roots[i] = roots[i].imag * 1j + roots.sort(key=lambda x: (abs(ctx._im(x)), ctx._re(x))) + finally: + ctx.prec = orig + if error: + err = max(err) + err = max(err, ctx.ldexp(1, -orig+1)) + return [+r for r in roots], +err + else: + return [+r for r in roots] diff --git a/compiler/gdsMill/mpmath/calculus/quadrature.py b/compiler/gdsMill/mpmath/calculus/quadrature.py new file mode 100644 index 00000000..fa9b29a9 --- /dev/null +++ b/compiler/gdsMill/mpmath/calculus/quadrature.py @@ -0,0 +1,1002 @@ +import math + +class QuadratureRule(object): + """ + Quadrature rules are implemented using this class, in order to + simplify the code and provide a common infrastructure + for tasks such as error estimation and node caching. + + You can implement a custom quadrature rule by subclassing + :class:`QuadratureRule` and implementing the appropriate + methods. The subclass can then be used by :func:`quad` by + passing it as the *method* argument. + + :class:`QuadratureRule` instances are supposed to be singletons. + :class:`QuadratureRule` therefore implements instance caching + in :func:`__new__`. + """ + + def __init__(self, ctx): + self.ctx = ctx + self.standard_cache = {} + self.transformed_cache = {} + self.interval_count = {} + + def clear(self): + """ + Delete cached node data. + """ + self.standard_cache = {} + self.transformed_cache = {} + self.interval_count = {} + + def calc_nodes(self, degree, prec, verbose=False): + r""" + Compute nodes for the standard interval `[-1, 1]`. Subclasses + should probably implement only this method, and use + :func:`get_nodes` method to retrieve the nodes. + """ + raise NotImplementedError + + def get_nodes(self, a, b, degree, prec, verbose=False): + """ + Return nodes for given interval, degree and precision. The + nodes are retrieved from a cache if already computed; + otherwise they are computed by calling :func:`calc_nodes` + and are then cached. + + Subclasses should probably not implement this method, + but just implement :func:`calc_nodes` for the actual + node computation. + """ + key = (a, b, degree, prec) + if key in self.transformed_cache: + return self.transformed_cache[key] + orig = self.ctx.prec + try: + self.ctx.prec = prec+20 + # Get nodes on standard interval + if (degree, prec) in self.standard_cache: + nodes = self.standard_cache[degree, prec] + else: + nodes = self.calc_nodes(degree, prec, verbose) + self.standard_cache[degree, prec] = nodes + # Transform to general interval + nodes = self.transform_nodes(nodes, a, b, verbose) + if key in self.interval_count: + self.transformed_cache[key] = nodes + else: + self.interval_count[key] = True + finally: + self.ctx.prec = orig + return nodes + + def transform_nodes(self, nodes, a, b, verbose=False): + r""" + Rescale standardized nodes (for `[-1, 1]`) to a general + interval `[a, b]`. For a finite interval, a simple linear + change of variables is used. Otherwise, the following + transformations are used: + + .. math :: + + [a, \infty] : t = \frac{1}{x} + (a-1) + + [-\infty, b] : t = (b+1) - \frac{1}{x} + + [-\infty, \infty] : t = \frac{x}{\sqrt{1-x^2}} + + """ + ctx = self.ctx + a = ctx.convert(a) + b = ctx.convert(b) + one = ctx.one + if (a, b) == (-one, one): + return nodes + half = ctx.mpf(0.5) + new_nodes = [] + if (a, b) == (ctx.ninf, ctx.inf): + p05 = -half + for x, w in nodes: + x2 = x*x + px1 = one-x2 + spx1 = px1**p05 + x = x*spx1 + w *= spx1/px1 + new_nodes.append((x, w)) + elif a == ctx.ninf: + b1 = b+1 + for x, w in nodes: + u = 2/(x+one) + x = b1-u + w *= half*u**2 + new_nodes.append((x, w)) + elif b == ctx.inf: + a1 = a-1 + for x, w in nodes: + u = 2/(x+one) + x = a1+u + w *= half*u**2 + new_nodes.append((x, w)) + else: + # Simple linear change of variables + C = (b-a)/2 + D = (b+a)/2 + for x, w in nodes: + new_nodes.append((D+C*x, C*w)) + return new_nodes + + def guess_degree(self, prec): + """ + Given a desired precision `p` in bits, estimate the degree `m` + of the quadrature required to accomplish full accuracy for + typical integrals. By default, :func:`quad` will perform up + to `m` iterations. The value of `m` should be a slight + overestimate, so that "slightly bad" integrals can be dealt + with automatically using a few extra iterations. On the + other hand, it should not be too big, so :func:`quad` can + quit within a reasonable amount of time when it is given + an "unsolvable" integral. + + The default formula used by :func:`guess_degree` is tuned + for both :class:`TanhSinh` and :class:`GaussLegendre`. + The output is roughly as follows: + + +---------+---------+ + | `p` | `m` | + +=========+=========+ + | 50 | 6 | + +---------+---------+ + | 100 | 7 | + +---------+---------+ + | 500 | 10 | + +---------+---------+ + | 3000 | 12 | + +---------+---------+ + + This formula is based purely on a limited amount of + experimentation and will sometimes be wrong. + """ + # Expected degree + # XXX: use mag + g = int(4 + max(0, self.ctx.log(prec/30.0, 2))) + # Reasonable "worst case" + g += 2 + return g + + def estimate_error(self, results, prec, epsilon): + r""" + Given results from integrations `[I_1, I_2, \ldots, I_k]` done + with a quadrature of rule of degree `1, 2, \ldots, k`, estimate + the error of `I_k`. + + For `k = 2`, we estimate `|I_{\infty}-I_2|` as `|I_2-I_1|`. + + For `k > 2`, we extrapolate `|I_{\infty}-I_k| \approx |I_{k+1}-I_k|` + from `|I_k-I_{k-1}|` and `|I_k-I_{k-2}|` under the assumption + that each degree increment roughly doubles the accuracy of + the quadrature rule (this is true for both :class:`TanhSinh` + and :class:`GaussLegendre`). The extrapolation formula is given + by Borwein, Bailey & Girgensohn. Although not very conservative, + this method seems to be very robust in practice. + """ + if len(results) == 2: + return abs(results[0]-results[1]) + try: + if results[-1] == results[-2] == results[-3]: + return self.ctx.zero + D1 = self.ctx.log(abs(results[-1]-results[-2]), 10) + D2 = self.ctx.log(abs(results[-1]-results[-3]), 10) + except ValueError: + return epsilon + D3 = -prec + D4 = min(0, max(D1**2/D2, 2*D1, D3)) + return self.ctx.mpf(10) ** int(D4) + + def summation(self, f, points, prec, epsilon, max_degree, verbose=False): + """ + Main integration function. Computes the 1D integral over + the interval specified by *points*. For each subinterval, + performs quadrature of degree from 1 up to *max_degree* + until :func:`estimate_error` signals convergence. + + :func:`summation` transforms each subintegration to + the standard interval and then calls :func:`sum_next`. + """ + ctx = self.ctx + I = err = ctx.zero + for i in xrange(len(points)-1): + a, b = points[i], points[i+1] + if a == b: + continue + # XXX: we could use a single variable transformation, + # but this is not good in practice. We get better accuracy + # by having 0 as an endpoint. + if (a, b) == (ctx.ninf, ctx.inf): + _f = f + f = lambda x: _f(-x) + _f(x) + a, b = (ctx.zero, ctx.inf) + results = [] + for degree in xrange(1, max_degree+1): + nodes = self.get_nodes(a, b, degree, prec, verbose) + if verbose: + print "Integrating from %s to %s (degree %s of %s)" % \ + (ctx.nstr(a), ctx.nstr(b), degree, max_degree) + results.append(self.sum_next(f, nodes, degree, prec, results, verbose)) + if degree > 1: + err = self.estimate_error(results, prec, epsilon) + if err <= epsilon: + break + if verbose: + print "Estimated error:", ctx.nstr(err) + I += results[-1] + if err > epsilon: + if verbose: + print "Failed to reach full accuracy. Estimated error:", ctx.nstr(err) + return I, err + + def sum_next(self, f, nodes, degree, prec, previous, verbose=False): + r""" + Evaluates the step sum `\sum w_k f(x_k)` where the *nodes* list + contains the `(w_k, x_k)` pairs. + + :func:`summation` will supply the list *results* of + values computed by :func:`sum_next` at previous degrees, in + case the quadrature rule is able to reuse them. + """ + return self.ctx.fdot((w, f(x)) for (x,w) in nodes) + + +class TanhSinh(QuadratureRule): + r""" + This class implements "tanh-sinh" or "doubly exponential" + quadrature. This quadrature rule is based on the Euler-Maclaurin + integral formula. By performing a change of variables involving + nested exponentials / hyperbolic functions (hence the name), the + derivatives at the endpoints vanish rapidly. Since the error term + in the Euler-Maclaurin formula depends on the derivatives at the + endpoints, a simple step sum becomes extremely accurate. In + practice, this means that doubling the number of evaluation + points roughly doubles the number of accurate digits. + + Comparison to Gauss-Legendre: + * Initial computation of nodes is usually faster + * Handles endpoint singularities better + * Handles infinite integration intervals better + * Is slower for smooth integrands once nodes have been computed + + The implementation of the tanh-sinh algorithm is based on the + description given in Borwein, Bailey & Girgensohn, "Experimentation + in Mathematics - Computational Paths to Discovery", A K Peters, + 2003, pages 312-313. In the present implementation, a few + improvements have been made: + + * A more efficient scheme is used to compute nodes (exploiting + recurrence for the exponential function) + * The nodes are computed successively instead of all at once + + Various documents describing the algorithm are available online, e.g.: + + * http://crd.lbl.gov/~dhbailey/dhbpapers/dhb-tanh-sinh.pdf + * http://users.cs.dal.ca/~jborwein/tanh-sinh.pdf + """ + + def sum_next(self, f, nodes, degree, prec, previous, verbose=False): + """ + Step sum for tanh-sinh quadrature of degree `m`. We exploit the + fact that half of the abscissas at degree `m` are precisely the + abscissas from degree `m-1`. Thus reusing the result from + the previous level allows a 2x speedup. + """ + h = self.ctx.mpf(2)**(-degree) + # Abscissas overlap, so reusing saves half of the time + if previous: + S = previous[-1]/(h*2) + else: + S = self.ctx.zero + S += self.ctx.fdot((w,f(x)) for (x,w) in nodes) + return h*S + + def calc_nodes(self, degree, prec, verbose=False): + r""" + The abscissas and weights for tanh-sinh quadrature of degree + `m` are given by + + .. math:: + + x_k = \tanh(\pi/2 \sinh(t_k)) + + w_k = \pi/2 \cosh(t_k) / \cosh(\pi/2 \sinh(t_k))^2 + + where `t_k = t_0 + hk` for a step length `h \sim 2^{-m}`. The + list of nodes is actually infinite, but the weights die off so + rapidly that only a few are needed. + """ + ctx = self.ctx + nodes = [] + + extra = 20 + ctx.prec += extra + tol = ctx.ldexp(1, -prec-10) + pi4 = ctx.pi/4 + + # For simplicity, we work in steps h = 1/2^n, with the first point + # offset so that we can reuse the sum from the previous degree + + # We define degree 1 to include the "degree 0" steps, including + # the point x = 0. (It doesn't work well otherwise; not sure why.) + t0 = ctx.ldexp(1, -degree) + if degree == 1: + #nodes.append((mpf(0), pi4)) + #nodes.append((-mpf(0), pi4)) + nodes.append((ctx.zero, ctx.pi/2)) + h = t0 + else: + h = t0*2 + + # Since h is fixed, we can compute the next exponential + # by simply multiplying by exp(h) + expt0 = ctx.exp(t0) + a = pi4 * expt0 + b = pi4 / expt0 + udelta = ctx.exp(h) + urdelta = 1/udelta + + for k in xrange(0, 20*2**degree+1): + # Reference implementation: + # t = t0 + k*h + # x = tanh(pi/2 * sinh(t)) + # w = pi/2 * cosh(t) / cosh(pi/2 * sinh(t))**2 + + # Fast implementation. Note that c = exp(pi/2 * sinh(t)) + c = ctx.exp(a-b) + d = 1/c + co = (c+d)/2 + si = (c-d)/2 + x = si / co + w = (a+b) / co**2 + diff = abs(x-1) + if diff <= tol: + break + + nodes.append((x, w)) + nodes.append((-x, w)) + + a *= udelta + b *= urdelta + + if verbose and k % 300 == 150: + # Note: the number displayed is rather arbitrary. Should + # figure out how to print something that looks more like a + # percentage + print "Calculating nodes:", ctx.nstr(-ctx.log(diff, 10) / prec) + + ctx.prec -= extra + return nodes + + +class GaussLegendre(QuadratureRule): + """ + This class implements Gauss-Legendre quadrature, which is + exceptionally efficient for polynomials and polynomial-like (i.e. + very smooth) integrands. + + The abscissas and weights are given by roots and values of + Legendre polynomials, which are the orthogonal polynomials + on `[-1, 1]` with respect to the unit weight + (see :func:`legendre`). + + In this implementation, we take the "degree" `m` of the quadrature + to denote a Gauss-Legendre rule of degree `3 \cdot 2^m` (following + Borwein, Bailey & Girgensohn). This way we get quadratic, rather + than linear, convergence as the degree is incremented. + + Comparison to tanh-sinh quadrature: + * Is faster for smooth integrands once nodes have been computed + * Initial computation of nodes is usually slower + * Handles endpoint singularities worse + * Handles infinite integration intervals worse + + """ + + def calc_nodes(self, degree, prec, verbose=False): + """ + Calculates the abscissas and weights for Gauss-Legendre + quadrature of degree of given degree (actually `3 \cdot 2^m`). + """ + ctx = self.ctx + # It is important that the epsilon is set lower than the + # "real" epsilon + epsilon = ctx.ldexp(1, -prec-8) + # Fairly high precision might be required for accurate + # evaluation of the roots + orig = ctx.prec + ctx.prec = int(prec*1.5) + if degree == 1: + x = ctx.mpf(3)/5 + w = ctx.mpf(5)/9 + nodes = [(-x,w),(ctx.zero,ctx.mpf(8)/9),(x,w)] + ctx.prec = orig + return nodes + nodes = [] + n = 3*2**(degree-1) + upto = n//2 + 1 + for j in xrange(1, upto): + # Asymptotic formula for the roots + r = ctx.mpf(math.cos(math.pi*(j-0.25)/(n+0.5))) + # Newton iteration + while 1: + t1, t2 = 1, 0 + # Evaluates the Legendre polynomial using its defining + # recurrence relation + for j1 in xrange(1,n+1): + t3, t2, t1 = t2, t1, ((2*j1-1)*r*t1 - (j1-1)*t2)/j1 + t4 = n*(r*t1- t2)/(r**2-1) + t5 = r + a = t1/t4 + r = r - a + if abs(a) < epsilon: + break + x = r + w = 2/((1-r**2)*t4**2) + if verbose and j % 30 == 15: + print "Computing nodes (%i of %i)" % (j, upto) + nodes.append((x, w)) + nodes.append((-x, w)) + ctx.prec = orig + return nodes + +class QuadratureMethods: + + def __init__(ctx, *args, **kwargs): + ctx._gauss_legendre = GaussLegendre(ctx) + ctx._tanh_sinh = TanhSinh(ctx) + + def quad(ctx, f, *points, **kwargs): + r""" + Computes a single, double or triple integral over a given + 1D interval, 2D rectangle, or 3D cuboid. A basic example:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> quad(sin, [0, pi]) + 2.0 + + A basic 2D integral:: + + >>> f = lambda x, y: cos(x+y/2) + >>> quad(f, [-pi/2, pi/2], [0, pi]) + 4.0 + + **Interval format** + + The integration range for each dimension may be specified + using a list or tuple. Arguments are interpreted as follows: + + ``quad(f, [x1, x2])`` -- calculates + `\int_{x_1}^{x_2} f(x) \, dx` + + ``quad(f, [x1, x2], [y1, y2])`` -- calculates + `\int_{x_1}^{x_2} \int_{y_1}^{y_2} f(x,y) \, dy \, dx` + + ``quad(f, [x1, x2], [y1, y2], [z1, z2])`` -- calculates + `\int_{x_1}^{x_2} \int_{y_1}^{y_2} \int_{z_1}^{z_2} f(x,y,z) + \, dz \, dy \, dx` + + Endpoints may be finite or infinite. An interval descriptor + may also contain more than two points. In this + case, the integration is split into subintervals, between + each pair of consecutive points. This is useful for + dealing with mid-interval discontinuities, or integrating + over large intervals where the function is irregular or + oscillates. + + **Options** + + :func:`quad` recognizes the following keyword arguments: + + *method* + Chooses integration algorithm (described below). + *error* + If set to true, :func:`quad` returns `(v, e)` where `v` is the + integral and `e` is the estimated error. + *maxdegree* + Maximum degree of the quadrature rule to try before + quitting. + *verbose* + Print details about progress. + + **Algorithms** + + Mpmath presently implements two integration algorithms: tanh-sinh + quadrature and Gauss-Legendre quadrature. These can be selected + using *method='tanh-sinh'* or *method='gauss-legendre'* or by + passing the classes *method=TanhSinh*, *method=GaussLegendre*. + The functions :func:`quadts` and :func:`quadgl` are also available + as shortcuts. + + Both algorithms have the property that doubling the number of + evaluation points roughly doubles the accuracy, so both are ideal + for high precision quadrature (hundreds or thousands of digits). + + At high precision, computing the nodes and weights for the + integration can be expensive (more expensive than computing the + function values). To make repeated integrations fast, nodes + are automatically cached. + + The advantages of the tanh-sinh algorithm are that it tends to + handle endpoint singularities well, and that the nodes are cheap + to compute on the first run. For these reasons, it is used by + :func:`quad` as the default algorithm. + + Gauss-Legendre quadrature often requires fewer function + evaluations, and is therefore often faster for repeated use, but + the algorithm does not handle endpoint singularities as well and + the nodes are more expensive to compute. Gauss-Legendre quadrature + can be a better choice if the integrand is smooth and repeated + integrations are required (e.g. for multiple integrals). + + See the documentation for :class:`TanhSinh` and + :class:`GaussLegendre` for additional details. + + **Examples of 1D integrals** + + Intervals may be infinite or half-infinite. The following two + examples evaluate the limits of the inverse tangent function + (`\int 1/(1+x^2) = \tan^{-1} x`), and the Gaussian integral + `\int_{\infty}^{\infty} \exp(-x^2)\,dx = \sqrt{\pi}`:: + + >>> mp.dps = 15 + >>> quad(lambda x: 2/(x**2+1), [0, inf]) + 3.14159265358979 + >>> quad(lambda x: exp(-x**2), [-inf, inf])**2 + 3.14159265358979 + + Integrals can typically be resolved to high precision. + The following computes 50 digits of `\pi` by integrating the + area of the half-circle defined by `x^2 + y^2 \le 1`, + `-1 \le x \le 1`, `y \ge 0`:: + + >>> mp.dps = 50 + >>> 2*quad(lambda x: sqrt(1-x**2), [-1, 1]) + 3.1415926535897932384626433832795028841971693993751 + + One can just as well compute 1000 digits (output truncated):: + + >>> mp.dps = 1000 + >>> 2*quad(lambda x: sqrt(1-x**2), [-1, 1]) #doctest:+ELLIPSIS + 3.141592653589793238462643383279502884...216420198 + + Complex integrals are supported. The following computes + a residue at `z = 0` by integrating counterclockwise along the + diamond-shaped path from `1` to `+i` to `-1` to `-i` to `1`:: + + >>> mp.dps = 15 + >>> chop(quad(lambda z: 1/z, [1,j,-1,-j,1])) + (0.0 + 6.28318530717959j) + + **Examples of 2D and 3D integrals** + + Here are several nice examples of analytically solvable + 2D integrals (taken from MathWorld [1]) that can be evaluated + to high precision fairly rapidly by :func:`quad`:: + + >>> mp.dps = 30 + >>> f = lambda x, y: (x-1)/((1-x*y)*log(x*y)) + >>> quad(f, [0, 1], [0, 1]) + 0.577215664901532860606512090082 + >>> +euler + 0.577215664901532860606512090082 + + >>> f = lambda x, y: 1/sqrt(1+x**2+y**2) + >>> quad(f, [-1, 1], [-1, 1]) + 3.17343648530607134219175646705 + >>> 4*log(2+sqrt(3))-2*pi/3 + 3.17343648530607134219175646705 + + >>> f = lambda x, y: 1/(1-x**2 * y**2) + >>> quad(f, [0, 1], [0, 1]) + 1.23370055013616982735431137498 + >>> pi**2 / 8 + 1.23370055013616982735431137498 + + >>> quad(lambda x, y: 1/(1-x*y), [0, 1], [0, 1]) + 1.64493406684822643647241516665 + >>> pi**2 / 6 + 1.64493406684822643647241516665 + + Multiple integrals may be done over infinite ranges:: + + >>> mp.dps = 15 + >>> print quad(lambda x,y: exp(-x-y), [0, inf], [1, inf]) + 0.367879441171442 + >>> print 1/e + 0.367879441171442 + + For nonrectangular areas, one can call :func:`quad` recursively. + For example, we can replicate the earlier example of calculating + `\pi` by integrating over the unit-circle, and actually use double + quadrature to actually measure the area circle:: + + >>> f = lambda x: quad(lambda y: 1, [-sqrt(1-x**2), sqrt(1-x**2)]) + >>> quad(f, [-1, 1]) + 3.14159265358979 + + Here is a simple triple integral:: + + >>> mp.dps = 15 + >>> f = lambda x,y,z: x*y/(1+z) + >>> quad(f, [0,1], [0,1], [1,2], method='gauss-legendre') + 0.101366277027041 + >>> (log(3)-log(2))/4 + 0.101366277027041 + + **Singularities** + + Both tanh-sinh and Gauss-Legendre quadrature are designed to + integrate smooth (infinitely differentiable) functions. Neither + algorithm copes well with mid-interval singularities (such as + mid-interval discontinuities in `f(x)` or `f'(x)`). + The best solution is to split the integral into parts:: + + >>> mp.dps = 15 + >>> quad(lambda x: abs(sin(x)), [0, 2*pi]) # Bad + 3.99900894176779 + >>> quad(lambda x: abs(sin(x)), [0, pi, 2*pi]) # Good + 4.0 + + The tanh-sinh rule often works well for integrands having a + singularity at one or both endpoints:: + + >>> mp.dps = 15 + >>> quad(log, [0, 1], method='tanh-sinh') # Good + -1.0 + >>> quad(log, [0, 1], method='gauss-legendre') # Bad + -0.999932197413801 + + However, the result may still be inaccurate for some functions:: + + >>> quad(lambda x: 1/sqrt(x), [0, 1], method='tanh-sinh') + 1.99999999946942 + + This problem is not due to the quadrature rule per se, but to + numerical amplification of errors in the nodes. The problem can be + circumvented by temporarily increasing the precision:: + + >>> mp.dps = 30 + >>> a = quad(lambda x: 1/sqrt(x), [0, 1], method='tanh-sinh') + >>> mp.dps = 15 + >>> +a + 2.0 + + **Highly variable functions** + + For functions that are smooth (in the sense of being infinitely + differentiable) but contain sharp mid-interval peaks or many + "bumps", :func:`quad` may fail to provide full accuracy. For + example, with default settings, :func:`quad` is able to integrate + `\sin(x)` accurately over an interval of length 100 but not over + length 1000:: + + >>> quad(sin, [0, 100]); 1-cos(100) # Good + 0.137681127712316 + 0.137681127712316 + >>> quad(sin, [0, 1000]); 1-cos(1000) # Bad + -37.8587612408485 + 0.437620923709297 + + One solution is to break the integration into 10 intervals of + length 100:: + + >>> quad(sin, linspace(0, 1000, 10)) # Good + 0.437620923709297 + + Another is to increase the degree of the quadrature:: + + >>> quad(sin, [0, 1000], maxdegree=10) # Also good + 0.437620923709297 + + Whether splitting the interval or increasing the degree is + more efficient differs from case to case. Another example is the + function `1/(1+x^2)`, which has a sharp peak centered around + `x = 0`:: + + >>> f = lambda x: 1/(1+x**2) + >>> quad(f, [-100, 100]) # Bad + 3.64804647105268 + >>> quad(f, [-100, 100], maxdegree=10) # Good + 3.12159332021646 + >>> quad(f, [-100, 0, 100]) # Also good + 3.12159332021646 + + **References** + + 1. http://mathworld.wolfram.com/DoubleIntegral.html + + """ + rule = kwargs.get('method', 'tanh-sinh') + if type(rule) is str: + if rule == 'tanh-sinh': + rule = ctx._tanh_sinh + elif rule == 'gauss-legendre': + rule = ctx._gauss_legendre + else: + raise ValueError("unknown quadrature rule: %s" % rule) + else: + rule = rule(ctx) + verbose = kwargs.get('verbose') + dim = len(points) + orig = prec = ctx.prec + epsilon = ctx.eps/8 + m = kwargs.get('maxdegree') or rule.guess_degree(prec) + points = [ctx._as_points(p) for p in points] + try: + ctx.prec += 20 + if dim == 1: + v, err = rule.summation(f, points[0], prec, epsilon, m, verbose) + elif dim == 2: + v, err = rule.summation(lambda x: \ + rule.summation(lambda y: f(x,y), \ + points[1], prec, epsilon, m)[0], + points[0], prec, epsilon, m, verbose) + elif dim == 3: + v, err = rule.summation(lambda x: \ + rule.summation(lambda y: \ + rule.summation(lambda z: f(x,y,z), \ + points[2], prec, epsilon, m)[0], + points[1], prec, epsilon, m)[0], + points[0], prec, epsilon, m, verbose) + else: + raise NotImplementedError("quadrature must have dim 1, 2 or 3") + finally: + ctx.prec = orig + if kwargs.get("error"): + return +v, err + return +v + + def quadts(ctx, *args, **kwargs): + """ + Performs tanh-sinh quadrature. The call + + quadts(func, *points, ...) + + is simply a shortcut for: + + quad(func, *points, ..., method=TanhSinh) + + For example, a single integral and a double integral: + + quadts(lambda x: exp(cos(x)), [0, 1]) + quadts(lambda x, y: exp(cos(x+y)), [0, 1], [0, 1]) + + See the documentation for quad for information about how points + arguments and keyword arguments are parsed. + + See documentation for TanhSinh for algorithmic information about + tanh-sinh quadrature. + """ + kwargs['method'] = 'tanh-sinh' + return ctx.quad(*args, **kwargs) + + def quadgl(ctx, *args, **kwargs): + """ + Performs Gauss-Legendre quadrature. The call + + quadgl(func, *points, ...) + + is simply a shortcut for: + + quad(func, *points, ..., method=GaussLegendre) + + For example, a single integral and a double integral: + + quadgl(lambda x: exp(cos(x)), [0, 1]) + quadgl(lambda x, y: exp(cos(x+y)), [0, 1], [0, 1]) + + See the documentation for quad for information about how points + arguments and keyword arguments are parsed. + + See documentation for TanhSinh for algorithmic information about + tanh-sinh quadrature. + """ + kwargs['method'] = 'gauss-legendre' + return ctx.quad(*args, **kwargs) + + def quadosc(ctx, f, interval, omega=None, period=None, zeros=None): + r""" + Calculates + + .. math :: + + I = \int_a^b f(x) dx + + where at least one of `a` and `b` is infinite and where + `f(x) = g(x) \cos(\omega x + \phi)` for some slowly + decreasing function `g(x)`. With proper input, :func:`quadosc` + can also handle oscillatory integrals where the oscillation + rate is different from a pure sine or cosine wave. + + In the standard case when `|a| < \infty, b = \infty`, + :func:`quadosc` works by evaluating the infinite series + + .. math :: + + I = \int_a^{x_1} f(x) dx + + \sum_{k=1}^{\infty} \int_{x_k}^{x_{k+1}} f(x) dx + + where `x_k` are consecutive zeros (alternatively + some other periodic reference point) of `f(x)`. + Accordingly, :func:`quadosc` requires information about the + zeros of `f(x)`. For a periodic function, you can specify + the zeros by either providing the angular frequency `\omega` + (*omega*) or the *period* `2 \pi/\omega`. In general, you can + specify the `n`-th zero by providing the *zeros* arguments. + Below is an example of each:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> f = lambda x: sin(3*x)/(x**2+1) + >>> quadosc(f, [0,inf], omega=3) + 0.37833007080198 + >>> quadosc(f, [0,inf], period=2*pi/3) + 0.37833007080198 + >>> quadosc(f, [0,inf], zeros=lambda n: pi*n/3) + 0.37833007080198 + >>> (ei(3)*exp(-3)-exp(3)*ei(-3))/2 # Computed by Mathematica + 0.37833007080198 + + Note that *zeros* was specified to multiply `n` by the + *half-period*, not the full period. In theory, it does not matter + whether each partial integral is done over a half period or a full + period. However, if done over half-periods, the infinite series + passed to :func:`nsum` becomes an *alternating series* and this + typically makes the extrapolation much more efficient. + + Here is an example of an integration over the entire real line, + and a half-infinite integration starting at `-\infty`:: + + >>> quadosc(lambda x: cos(x)/(1+x**2), [-inf, inf], omega=1) + 1.15572734979092 + >>> pi/e + 1.15572734979092 + >>> quadosc(lambda x: cos(x)/x**2, [-inf, -1], period=2*pi) + -0.0844109505595739 + >>> cos(1)+si(1)-pi/2 + -0.0844109505595738 + + Of course, the integrand may contain a complex exponential just as + well as a real sine or cosine:: + + >>> quadosc(lambda x: exp(3*j*x)/(1+x**2), [-inf,inf], omega=3) + (0.156410688228254 + 0.0j) + >>> pi/e**3 + 0.156410688228254 + >>> quadosc(lambda x: exp(3*j*x)/(2+x+x**2), [-inf,inf], omega=3) + (0.00317486988463794 - 0.0447701735209082j) + >>> 2*pi/sqrt(7)/exp(3*(j+sqrt(7))/2) + (0.00317486988463794 - 0.0447701735209082j) + + **Non-periodic functions** + + If `f(x) = g(x) h(x)` for some function `h(x)` that is not + strictly periodic, *omega* or *period* might not work, and it might + be necessary to use *zeros*. + + A notable exception can be made for Bessel functions which, though not + periodic, are "asymptotically periodic" in a sufficiently strong sense + that the sum extrapolation will work out:: + + >>> quadosc(j0, [0, inf], period=2*pi) + 1.0 + >>> quadosc(j1, [0, inf], period=2*pi) + 1.0 + + More properly, one should provide the exact Bessel function zeros:: + + >>> j0zero = lambda n: findroot(j0, pi*(n-0.25)) + >>> quadosc(j0, [0, inf], zeros=j0zero) + 1.0 + + For an example where *zeros* becomes necessary, consider the + complete Fresnel integrals + + .. math :: + + \int_0^{\infty} \cos x^2\,dx = \int_0^{\infty} \sin x^2\,dx + = \sqrt{\frac{\pi}{8}}. + + Although the integrands do not decrease in magnitude as + `x \to \infty`, the integrals are convergent since the oscillation + rate increases (causing consecutive periods to asymptotically + cancel out). These integrals are virtually impossible to calculate + to any kind of accuracy using standard quadrature rules. However, + if one provides the correct asymptotic distribution of zeros + (`x_n \sim \sqrt{n}`), :func:`quadosc` works:: + + >>> mp.dps = 30 + >>> f = lambda x: cos(x**2) + >>> quadosc(f, [0,inf], zeros=lambda n:sqrt(pi*n)) + 0.626657068657750125603941321203 + >>> f = lambda x: sin(x**2) + >>> quadosc(f, [0,inf], zeros=lambda n:sqrt(pi*n)) + 0.626657068657750125603941321203 + >>> sqrt(pi/8) + 0.626657068657750125603941321203 + + (Interestingly, these integrals can still be evaluated if one + places some other constant than `\pi` in the square root sign.) + + In general, if `f(x) \sim g(x) \cos(h(x))`, the zeros follow + the inverse-function distribution `h^{-1}(x)`:: + + >>> mp.dps = 15 + >>> f = lambda x: sin(exp(x)) + >>> quadosc(f, [1,inf], zeros=lambda n: log(n)) + -0.25024394235267 + >>> pi/2-si(e) + -0.250243942352671 + + **Non-alternating functions** + + If the integrand oscillates around a positive value, without + alternating signs, the extrapolation might fail. A simple trick + that sometimes works is to multiply or divide the frequency by 2:: + + >>> f = lambda x: 1/x**2+sin(x)/x**4 + >>> quadosc(f, [1,inf], omega=1) # Bad + 1.28642190869921 + >>> quadosc(f, [1,inf], omega=0.5) # Perfect + 1.28652953559617 + >>> 1+(cos(1)+ci(1)+sin(1))/6 + 1.28652953559617 + + **Fast decay** + + :func:`quadosc` is primarily useful for slowly decaying + integrands. If the integrand decreases exponentially or faster, + :func:`quad` will likely handle it without trouble (and generally be + much faster than :func:`quadosc`):: + + >>> quadosc(lambda x: cos(x)/exp(x), [0, inf], omega=1) + 0.5 + >>> quad(lambda x: cos(x)/exp(x), [0, inf]) + 0.5 + + """ + a, b = ctx._as_points(interval) + a = ctx.convert(a) + b = ctx.convert(b) + if [omega, period, zeros].count(None) != 2: + raise ValueError( \ + "must specify exactly one of omega, period, zeros") + if a == ctx.ninf and b == ctx.inf: + s1 = ctx.quadosc(f, [a, 0], omega=omega, zeros=zeros, period=period) + s2 = ctx.quadosc(f, [0, b], omega=omega, zeros=zeros, period=period) + return s1 + s2 + if a == ctx.ninf: + if zeros: + return ctx.quadosc(lambda x:f(-x), [-b,-a], lambda n: zeros(-n)) + else: + return ctx.quadosc(lambda x:f(-x), [-b,-a], omega=omega, period=period) + if b != ctx.inf: + raise ValueError("quadosc requires an infinite integration interval") + if not zeros: + if omega: + period = 2*ctx.pi/omega + zeros = lambda n: n*period/2 + #for n in range(1,10): + # p = zeros(n) + # if p > a: + # break + #if n >= 9: + # raise ValueError("zeros do not appear to be correctly indexed") + n = 1 + s = ctx.quadgl(f, [a, zeros(n)]) + def term(k): + return ctx.quadgl(f, [zeros(k), zeros(k+1)]) + s += ctx.nsum(term, [n, ctx.inf]) + return s + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/compiler/gdsMill/mpmath/conftest.py b/compiler/gdsMill/mpmath/conftest.py new file mode 100644 index 00000000..6f4eb5c7 --- /dev/null +++ b/compiler/gdsMill/mpmath/conftest.py @@ -0,0 +1,8 @@ +# The py library is part of the "py.test" testing suite (python-codespeak-lib +# on Debian), see http://codespeak.net/py/ + +import py + +#this makes py.test put mpath directory into the sys.path, so that we can +#"import mpmath" from tests nicely +rootdir = py.magic.autopath().dirpath() diff --git a/compiler/gdsMill/mpmath/ctx_base.py b/compiler/gdsMill/mpmath/ctx_base.py new file mode 100644 index 00000000..38402cc6 --- /dev/null +++ b/compiler/gdsMill/mpmath/ctx_base.py @@ -0,0 +1,324 @@ +from operator import gt, lt + +from functions.functions import SpecialFunctions +from functions.rszeta import RSCache +from calculus.quadrature import QuadratureMethods +from calculus.calculus import CalculusMethods +from calculus.optimization import OptimizationMethods +from calculus.odes import ODEMethods +from matrices.matrices import MatrixMethods +from matrices.calculus import MatrixCalculusMethods +from matrices.linalg import LinearAlgebraMethods +from identification import IdentificationMethods +from visualization import VisualizationMethods + +import libmp + +class Context(object): + pass + +class StandardBaseContext(Context, + SpecialFunctions, + RSCache, + QuadratureMethods, + CalculusMethods, + MatrixMethods, + MatrixCalculusMethods, + LinearAlgebraMethods, + IdentificationMethods, + OptimizationMethods, + ODEMethods, + VisualizationMethods): + + NoConvergence = libmp.NoConvergence + ComplexResult = libmp.ComplexResult + + def __init__(ctx): + ctx._aliases = {} + # Call those that need preinitialization (e.g. for wrappers) + SpecialFunctions.__init__(ctx) + RSCache.__init__(ctx) + QuadratureMethods.__init__(ctx) + CalculusMethods.__init__(ctx) + MatrixMethods.__init__(ctx) + + def _init_aliases(ctx): + for alias, value in ctx._aliases.items(): + try: + setattr(ctx, alias, getattr(ctx, value)) + except AttributeError: + pass + + _fixed_precision = False + + # XXX + verbose = False + + def warn(ctx, msg): + print "Warning:", msg + + def bad_domain(ctx, msg): + raise ValueError(msg) + + def _re(ctx, x): + if hasattr(x, "real"): + return x.real + return x + + def _im(ctx, x): + if hasattr(x, "imag"): + return x.imag + return ctx.zero + + def chop(ctx, x, tol=None): + """ + Chops off small real or imaginary parts, or converts + numbers close to zero to exact zeros. The input can be a + single number or an iterable:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> chop(5+1e-10j, tol=1e-9) + mpf('5.0') + >>> nprint(chop([1.0, 1e-20, 3+1e-18j, -4, 2])) + [1.0, 0.0, 3.0, -4.0, 2.0] + + The tolerance defaults to ``100*eps``. + """ + if tol is None: + tol = 100*ctx.eps + try: + x = ctx.convert(x) + absx = abs(x) + if abs(x) < tol: + return ctx.zero + if ctx._is_complex_type(x): + if abs(x.imag) < min(tol, absx*tol): + return x.real + if abs(x.real) < min(tol, absx*tol): + return ctx.mpc(0, x.imag) + except TypeError: + if isinstance(x, ctx.matrix): + return x.apply(lambda a: ctx.chop(a, tol)) + if hasattr(x, "__iter__"): + return [ctx.chop(a, tol) for a in x] + return x + + def almosteq(ctx, s, t, rel_eps=None, abs_eps=None): + r""" + Determine whether the difference between `s` and `t` is smaller + than a given epsilon, either relatively or absolutely. + + Both a maximum relative difference and a maximum difference + ('epsilons') may be specified. The absolute difference is + defined as `|s-t|` and the relative difference is defined + as `|s-t|/\max(|s|, |t|)`. + + If only one epsilon is given, both are set to the same value. + If none is given, both epsilons are set to `2^{-p+m}` where + `p` is the current working precision and `m` is a small + integer. The default setting typically allows :func:`almosteq` + to be used to check for mathematical equality + in the presence of small rounding errors. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15 + >>> almosteq(3.141592653589793, 3.141592653589790) + True + >>> almosteq(3.141592653589793, 3.141592653589700) + False + >>> almosteq(3.141592653589793, 3.141592653589700, 1e-10) + True + >>> almosteq(1e-20, 2e-20) + True + >>> almosteq(1e-20, 2e-20, rel_eps=0, abs_eps=0) + False + + """ + t = ctx.convert(t) + if abs_eps is None and rel_eps is None: + rel_eps = abs_eps = ctx.ldexp(1, -ctx.prec+4) + if abs_eps is None: + abs_eps = rel_eps + elif rel_eps is None: + rel_eps = abs_eps + diff = abs(s-t) + if diff <= abs_eps: + return True + abss = abs(s) + abst = abs(t) + if abss < abst: + err = diff/abst + else: + err = diff/abss + return err <= rel_eps + + def arange(ctx, *args): + r""" + This is a generalized version of Python's :func:`range` function + that accepts fractional endpoints and step sizes and + returns a list of ``mpf`` instances. Like :func:`range`, + :func:`arange` can be called with 1, 2 or 3 arguments: + + ``arange(b)`` + `[0, 1, 2, \ldots, x]` + ``arange(a, b)`` + `[a, a+1, a+2, \ldots, x]` + ``arange(a, b, h)`` + `[a, a+h, a+h, \ldots, x]` + + where `b-1 \le x < b` (in the third case, `b-h \le x < b`). + + Like Python's :func:`range`, the endpoint is not included. To + produce ranges where the endpoint is included, :func:`linspace` + is more convenient. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> arange(4) + [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0')] + >>> arange(1, 2, 0.25) + [mpf('1.0'), mpf('1.25'), mpf('1.5'), mpf('1.75')] + >>> arange(1, -1, -0.75) + [mpf('1.0'), mpf('0.25'), mpf('-0.5')] + + """ + if not len(args) <= 3: + raise TypeError('arange expected at most 3 arguments, got %i' + % len(args)) + if not len(args) >= 1: + raise TypeError('arange expected at least 1 argument, got %i' + % len(args)) + # set default + a = 0 + dt = 1 + # interpret arguments + if len(args) == 1: + b = args[0] + elif len(args) >= 2: + a = args[0] + b = args[1] + if len(args) == 3: + dt = args[2] + a, b, dt = ctx.mpf(a), ctx.mpf(b), ctx.mpf(dt) + assert a + dt != a, 'dt is too small and would cause an infinite loop' + # adapt code for sign of dt + if a > b: + if dt > 0: + return [] + op = gt + else: + if dt < 0: + return [] + op = lt + # create list + result = [] + i = 0 + t = a + while 1: + t = a + dt*i + i += 1 + if op(t, b): + result.append(t) + else: + break + return result + + def linspace(ctx, *args, **kwargs): + """ + ``linspace(a, b, n)`` returns a list of `n` evenly spaced + samples from `a` to `b`. The syntax ``linspace(mpi(a,b), n)`` + is also valid. + + This function is often more convenient than :func:`arange` + for partitioning an interval into subintervals, since + the endpoint is included:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> linspace(1, 4, 4) + [mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] + >>> linspace(mpi(1,4), 4) + [mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] + + You may also provide the keyword argument ``endpoint=False``:: + + >>> linspace(1, 4, 4, endpoint=False) + [mpf('1.0'), mpf('1.75'), mpf('2.5'), mpf('3.25')] + + """ + if len(args) == 3: + a = ctx.mpf(args[0]) + b = ctx.mpf(args[1]) + n = int(args[2]) + elif len(args) == 2: + assert hasattr(args[0], '_mpi_') + a = args[0].a + b = args[0].b + n = int(args[1]) + else: + raise TypeError('linspace expected 2 or 3 arguments, got %i' \ + % len(args)) + if n < 1: + raise ValueError('n must be greater than 0') + if not 'endpoint' in kwargs or kwargs['endpoint']: + if n == 1: + return [ctx.mpf(a)] + step = (b - a) / ctx.mpf(n - 1) + y = [i*step + a for i in xrange(n)] + y[-1] = b + else: + step = (b - a) / ctx.mpf(n) + y = [i*step + a for i in xrange(n)] + return y + + def cos_sin(ctx, z, **kwargs): + return ctx.cos(z, **kwargs), ctx.sin(z, **kwargs) + + def _default_hyper_maxprec(ctx, p): + return int(1000 * p**0.25 + 4*p) + + _gcd = staticmethod(libmp.gcd) + list_primes = staticmethod(libmp.list_primes) + bernfrac = staticmethod(libmp.bernfrac) + moebius = staticmethod(libmp.moebius) + _ifac = staticmethod(libmp.ifac) + _eulernum = staticmethod(libmp.eulernum) + + def sum_accurately(ctx, terms, check_step=1): + prec = ctx.prec + try: + extraprec = 10 + while 1: + ctx.prec = prec + extraprec + 5 + max_mag = ctx.ninf + s = ctx.zero + k = 0 + for term in terms(): + s += term + if (not k % check_step) and term: + term_mag = ctx.mag(term) + max_mag = max(max_mag, term_mag) + sum_mag = ctx.mag(s) + if sum_mag - term_mag > ctx.prec: + break + k += 1 + cancellation = max_mag - sum_mag + if cancellation != cancellation: + break + if cancellation < extraprec or ctx._fixed_precision: + break + extraprec += min(ctx.prec, cancellation) + return s + finally: + ctx.prec = prec + + def power(ctx, x, y): + return ctx.convert(x) ** ctx.convert(y) + + def _zeta_int(ctx, n): + return ctx.zeta(n) diff --git a/compiler/gdsMill/mpmath/ctx_fp.py b/compiler/gdsMill/mpmath/ctx_fp.py new file mode 100644 index 00000000..97d80128 --- /dev/null +++ b/compiler/gdsMill/mpmath/ctx_fp.py @@ -0,0 +1,278 @@ +from ctx_base import StandardBaseContext + +import math +import cmath +import math2 + +import function_docs + +from libmp import mpf_bernoulli, to_float, int_types +import libmp + +class FPContext(StandardBaseContext): + """ + Context for fast low-precision arithmetic (53-bit precision, giving at most + about 15-digit accuracy), using Python's builtin float and complex. + """ + + def __init__(ctx): + StandardBaseContext.__init__(ctx) + + # Override SpecialFunctions implementation + ctx.loggamma = math2.loggamma + ctx._bernoulli_cache = {} + ctx.pretty = False + + ctx._init_aliases() + + _mpq = lambda cls, x: float(x[0])/x[1] + + NoConvergence = libmp.NoConvergence + + def _get_prec(ctx): return 53 + def _set_prec(ctx, p): return + def _get_dps(ctx): return 15 + def _set_dps(ctx, p): return + + _fixed_precision = True + + prec = property(_get_prec, _set_prec) + dps = property(_get_dps, _set_dps) + + zero = 0.0 + one = 1.0 + eps = math2.EPS + inf = math2.INF + ninf = math2.NINF + nan = math2.NAN + j = 1j + + # Called by SpecialFunctions.__init__() + @classmethod + def _wrap_specfun(cls, name, f, wrap): + if wrap: + def f_wrapped(ctx, *args, **kwargs): + convert = ctx.convert + args = [convert(a) for a in args] + return f(ctx, *args, **kwargs) + else: + f_wrapped = f + f_wrapped.__doc__ = function_docs.__dict__.get(name, "") + setattr(cls, name, f_wrapped) + + def bernoulli(ctx, n): + cache = ctx._bernoulli_cache + if n in cache: + return cache[n] + cache[n] = to_float(mpf_bernoulli(n, 53, 'n'), strict=True) + return cache[n] + + pi = math2.pi + e = math2.e + euler = math2.euler + sqrt2 = 1.4142135623730950488 + sqrt5 = 2.2360679774997896964 + phi = 1.6180339887498948482 + ln2 = 0.69314718055994530942 + ln10 = 2.302585092994045684 + euler = 0.57721566490153286061 + catalan = 0.91596559417721901505 + khinchin = 2.6854520010653064453 + apery = 1.2020569031595942854 + + absmin = absmax = abs + + def _as_points(ctx, x): + return x + + def fneg(ctx, x, **kwargs): + return -ctx.convert(x) + + def fadd(ctx, x, y, **kwargs): + return ctx.convert(x)+ctx.convert(y) + + def fsub(ctx, x, y, **kwargs): + return ctx.convert(x)-ctx.convert(y) + + def fmul(ctx, x, y, **kwargs): + return ctx.convert(x)*ctx.convert(y) + + def fdiv(ctx, x, y, **kwargs): + return ctx.convert(x)/ctx.convert(y) + + def fsum(ctx, args, absolute=False, squared=False): + if absolute: + if squared: + return sum((abs(x)**2 for x in args), ctx.zero) + return sum((abs(x) for x in args), ctx.zero) + if squared: + return sum((x**2 for x in args), ctx.zero) + return sum(args, ctx.zero) + + def fdot(ctx, xs, ys=None): + if ys is not None: + xs = zip(xs, ys) + return sum((x*y for (x,y) in xs), ctx.zero) + + def is_special(ctx, x): + return x - x != 0.0 + + def isnan(ctx, x): + return x != x + + def isinf(ctx, x): + return abs(x) == math2.INF + + def isnpint(ctx, x): + if type(x) is complex: + if x.imag: + return False + x = x.real + return x <= 0.0 and round(x) == x + + mpf = float + mpc = complex + + def convert(ctx, x): + try: + return float(x) + except: + return complex(x) + + power = staticmethod(math2.pow) + sqrt = staticmethod(math2.sqrt) + exp = staticmethod(math2.exp) + ln = log = staticmethod(math2.log) + cos = staticmethod(math2.cos) + sin = staticmethod(math2.sin) + tan = staticmethod(math2.tan) + cos_sin = staticmethod(math2.cos_sin) + acos = staticmethod(math2.acos) + asin = staticmethod(math2.asin) + atan = staticmethod(math2.atan) + cosh = staticmethod(math2.cosh) + sinh = staticmethod(math2.sinh) + tanh = staticmethod(math2.tanh) + gamma = staticmethod(math2.gamma) + fac = factorial = staticmethod(math2.factorial) + floor = staticmethod(math2.floor) + ceil = staticmethod(math2.ceil) + cospi = staticmethod(math2.cospi) + sinpi = staticmethod(math2.sinpi) + cbrt = staticmethod(math2.cbrt) + _nthroot = staticmethod(math2.nthroot) + _ei = staticmethod(math2.ei) + _e1 = staticmethod(math2.e1) + _zeta = _zeta_int = staticmethod(math2.zeta) + + # XXX: math2 + def arg(ctx, z): + z = complex(z) + return math.atan2(z.imag, z.real) + + def expj(ctx, x): + return ctx.exp(ctx.j*x) + + def expjpi(ctx, x): + return ctx.exp(ctx.j*ctx.pi*x) + + ldexp = math.ldexp + frexp = math.frexp + + def mag(ctx, z): + if z: + return ctx.frexp(abs(z))[1] + return ctx.ninf + + def isint(ctx, z): + if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 + if z.imag: + return False + z = z.real + try: + return z == int(z) + except: + return False + + def nint_distance(ctx, z): + if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 + n = round(z.real) + else: + n = round(z) + if n == z: + return n, ctx.ninf + return n, ctx.mag(abs(z-n)) + + def _convert_param(ctx, z): + if type(z) is tuple: + p, q = z + return ctx.mpf(p) / q, 'R' + if hasattr(z, "imag"): # float/int don't have .real/.imag in py2.5 + intz = int(z.real) + else: + intz = int(z) + if z == intz: + return intz, 'Z' + return z, 'R' + + def _is_real_type(ctx, z): + return isinstance(z, float) or isinstance(z, int_types) + + def _is_complex_type(ctx, z): + return isinstance(z, complex) + + def hypsum(ctx, p, q, types, coeffs, z, maxterms=6000, **kwargs): + coeffs = list(coeffs) + num = range(p) + den = range(p,p+q) + tol = ctx.eps + s = t = 1.0 + k = 0 + while 1: + for i in num: t *= (coeffs[i]+k) + for i in den: t /= (coeffs[i]+k) + k += 1; t /= k; t *= z; s += t + if abs(t) < tol: + return s + if k > maxterms: + raise ctx.NoConvergence + + def atan2(ctx, x, y): + return math.atan2(x, y) + + def psi(ctx, m, z): + m = int(m) + if m == 0: + return ctx.digamma(z) + return (-1)**(m+1) * ctx.fac(m) * ctx.zeta(m+1, z) + + digamma = staticmethod(math2.digamma) + + def harmonic(ctx, x): + x = ctx.convert(x) + if x == 0 or x == 1: + return x + return ctx.digamma(x+1) + ctx.euler + + nstr = str + + def to_fixed(ctx, x, prec): + return int(math.ldexp(x, prec)) + + def rand(ctx): + import random + return random.random() + + _erf = staticmethod(math2.erf) + _erfc = staticmethod(math2.erfc) + + def sum_accurately(ctx, terms, check_step=1): + s = ctx.zero + k = 0 + for term in terms(): + s += term + if (not k % check_step) and term: + if abs(term) <= 1e-18*abs(s): + break + k += 1 + return s diff --git a/compiler/gdsMill/mpmath/ctx_mp.py b/compiler/gdsMill/mpmath/ctx_mp.py new file mode 100644 index 00000000..36160715 --- /dev/null +++ b/compiler/gdsMill/mpmath/ctx_mp.py @@ -0,0 +1,1392 @@ +""" +This module defines the mpf, mpc classes, and standard functions for +operating with them. +""" +__docformat__ = 'plaintext' + +import re + +from string import strip + +from ctx_base import StandardBaseContext + +import libmp + +from libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps, + round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps, + ComplexResult, to_pickable, from_pickable, normalize, + from_int, from_float, from_str, to_int, to_float, to_str, + from_rational, from_man_exp, + fone, fzero, finf, fninf, fnan, + mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, + mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod, + mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge, + mpf_hash, mpf_rand, + mpf_sum, + bitcount, to_fixed, + mpc_to_str, + mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate, + mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf, + mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int, + mpc_mpf_div, + mpf_pow, + mpi_mid, mpi_delta, mpi_str, + mpi_abs, mpi_pos, mpi_neg, mpi_add, mpi_sub, + mpi_mul, mpi_div, mpi_pow_int, mpi_pow, + mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10, + mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin, + mpf_glaisher, mpf_twinprime, mpf_mertens, + int_types) + +import function_docs +import rational + +new = object.__new__ + +get_complex = re.compile(r'^\(?(?P[\+\-]?\d*\.?\d*(e[\+\-]?\d+)?)??' + r'(?P[\+\-]?\d*\.?\d*(e[\+\-]?\d+)?j)?\)?$') + + +try: + from sage.libs.mpmath.ext_main import Context as BaseMPContext + # pickle hack + import sage.libs.mpmath.ext_main as _mpf_module +except ImportError: + from ctx_mp_python import PythonMPContext as BaseMPContext + import ctx_mp_python as _mpf_module + +from ctx_mp_python import _mpf, _mpc, mpnumeric + + +class _mpi(mpnumeric): + """ + Interval arithmetic class. Precision is controlled by mp.prec. + """ + + def __new__(cls, a, b=None): + ctx = cls.context + if isinstance(a, ctx.mpi): + return a + if b is None: + b = a + a = ctx.mpf(a, rounding=round_floor) + b = ctx.mpf(b, rounding=round_ceiling) + if ctx.isnan(a) or ctx.isnan(b): + a, b = ctx.ninf, ctx.inf + assert a <= b, "endpoints must be properly ordered" + return ctx.make_mpi((a._mpf_, b._mpf_)) + + @property + def a(self): + return self.context.make_mpf(self._mpi_[0]) + + @property + def b(self): + return self.context.make_mpf(self._mpi_[1]) + + @property + def mid(self): + ctx = self.context + return ctx.make_mpf(mpi_mid(self._mpi_, ctx.prec)) + + @property + def delta(self): + ctx = self.context + return ctx.make_mpf(mpi_delta(self._mpi_, ctx.prec)) + + def _compare(*args): + raise TypeError("no ordering relation is defined for intervals") + + __gt__ = _compare + __le__ = _compare + __gt__ = _compare + __ge__ = _compare + + def __contains__(self, t): + t = self.context.mpi(t) + return (self.a <= t.a) and (t.b <= self.b) + + def __str__(self): + return mpi_str(self._mpi_, self.context.prec) + + def __repr__(self): + if self.context.pretty: + return str(self) + return "mpi(%r, %r)" % (self.a, self.b) + + def __eq__(self, other): + if not hasattr(other, "_mpi_"): + try: + other = self.context.mpi(other) + except: + return NotImplemented + return (self.a == other.a) and (self.b == other.b) + + def __ne__(self, other): + return not (self == other) + + def __abs__(self): + return self.context.make_mpi(mpi_abs(self._mpi_, self.context.prec)) + + def __pos__(self): + return self.context.make_mpi(mpi_pos(self._mpi_, self.context.prec)) + + def __neg__(self): + return self.context.make_mpi(mpi_neg(self._mpi_, self.context.prec)) + + def __add__(self, other): + if not hasattr(other, "_mpi_"): + other = self.context.mpi(other) + return self.context.make_mpi(mpi_add(self._mpi_, other._mpi_, + self.context.prec)) + + def __sub__(self, other): + if not hasattr(other, "_mpi_"): + other = self.context.mpi(other) + return self.context.make_mpi(mpi_sub(self._mpi_, other._mpi_, + self.context.prec)) + + def __mul__(self, other): + if not hasattr(other, "_mpi_"): + other = self.context.mpi(other) + return self.context.make_mpi(mpi_mul(self._mpi_, other._mpi_, + self.context.prec)) + + def __div__(self, other): + if not hasattr(other, "_mpi_"): + other = self.context.mpi(other) + return self.context.make_mpi(mpi_div(self._mpi_, other._mpi_, + self.context.prec)) + + def __pow__(self, other): + if isinstance(other, (int, long)): + return self.context.make_mpi(mpi_pow_int(self._mpi_, int(other), + self.context.prec)) + if not hasattr(other, "_mpi_"): + other = self.context.mpi(other) + return self.context.make_mpi(mpi_pow(self._mpi_, other._mpi_, + self.context.prec)) + + def __rsub__(s, t): + return s.context.mpi(t) - s + + def __rdiv__(s, t): + return s.context.mpi(t) / s + + def __rpow__(s, t): + return s.context.mpi(t) ** s + + __radd__ = __add__ + __rmul__ = __mul__ + __truediv__ = __div__ + __rtruediv__ = __rdiv__ + __floordiv__ = __div__ + __rfloordiv__ = __rdiv__ + +class MPContext(BaseMPContext, StandardBaseContext): + """ + Context for multiprecision arithmetic with a global precision. + """ + + def __init__(ctx): + BaseMPContext.__init__(ctx) + + ctx.trap_complex = False + ctx.pretty = False + ctx.mpi = type('mpi', (_mpi,), {}) + ctx.types = [ctx.mpf, ctx.mpc, ctx.mpi, ctx.constant] + # For fast access + ctx.mpi._ctxdata = [ctx.mpi, new, ctx._prec_rounding] + ctx.mpi.context = ctx + + ctx._mpq = rational.mpq + + ctx.default() + StandardBaseContext.__init__(ctx) + + ctx.mpq = rational.mpq + ctx.init_builtins() + + ctx.hyp_summators = {} + + ctx._init_aliases() + + # XXX: automate + ctx.bernoulli.im_func.func_doc = function_docs.bernoulli + ctx.primepi.im_func.func_doc = function_docs.primepi + ctx.psi.im_func.func_doc = function_docs.psi + ctx.atan2.im_func.func_doc = function_docs.atan2 + ctx.digamma.func_doc = function_docs.digamma + ctx.cospi.func_doc = function_docs.cospi + ctx.sinpi.func_doc = function_docs.sinpi + + def init_builtins(ctx): + + mpf = ctx.mpf + mpc = ctx.mpc + + # Exact constants + ctx.one = ctx.make_mpf(fone) + ctx.zero = ctx.make_mpf(fzero) + ctx.j = ctx.make_mpc((fzero,fone)) + ctx.inf = ctx.make_mpf(finf) + ctx.ninf = ctx.make_mpf(fninf) + ctx.nan = ctx.make_mpf(fnan) + + eps = ctx.constant(lambda prec, rnd: (0, MPZ_ONE, 1-prec, 1), + "epsilon of working precision", "eps") + ctx.eps = eps + + # Approximate constants + ctx.pi = ctx.constant(mpf_pi, "pi", "pi") + ctx.ln2 = ctx.constant(mpf_ln2, "ln(2)", "ln2") + ctx.ln10 = ctx.constant(mpf_ln10, "ln(10)", "ln10") + ctx.phi = ctx.constant(mpf_phi, "Golden ratio phi", "phi") + ctx.e = ctx.constant(mpf_e, "e = exp(1)", "e") + ctx.euler = ctx.constant(mpf_euler, "Euler's constant", "euler") + ctx.catalan = ctx.constant(mpf_catalan, "Catalan's constant", "catalan") + ctx.khinchin = ctx.constant(mpf_khinchin, "Khinchin's constant", "khinchin") + ctx.glaisher = ctx.constant(mpf_glaisher, "Glaisher's constant", "glaisher") + ctx.apery = ctx.constant(mpf_apery, "Apery's constant", "apery") + ctx.degree = ctx.constant(mpf_degree, "1 deg = pi / 180", "degree") + ctx.twinprime = ctx.constant(mpf_twinprime, "Twin prime constant", "twinprime") + ctx.mertens = ctx.constant(mpf_mertens, "Mertens' constant", "mertens") + + # Standard functions + ctx.sqrt = ctx._wrap_libmp_function(libmp.mpf_sqrt, libmp.mpc_sqrt, libmp.mpi_sqrt) + ctx.cbrt = ctx._wrap_libmp_function(libmp.mpf_cbrt, libmp.mpc_cbrt) + ctx.ln = ctx._wrap_libmp_function(libmp.mpf_log, libmp.mpc_log, libmp.mpi_log) + ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan) + ctx.exp = ctx._wrap_libmp_function(libmp.mpf_exp, libmp.mpc_exp, libmp.mpi_exp) + ctx.expj = ctx._wrap_libmp_function(libmp.mpf_expj, libmp.mpc_expj) + ctx.expjpi = ctx._wrap_libmp_function(libmp.mpf_expjpi, libmp.mpc_expjpi) + ctx.sin = ctx._wrap_libmp_function(libmp.mpf_sin, libmp.mpc_sin, libmp.mpi_sin) + ctx.cos = ctx._wrap_libmp_function(libmp.mpf_cos, libmp.mpc_cos, libmp.mpi_cos) + ctx.tan = ctx._wrap_libmp_function(libmp.mpf_tan, libmp.mpc_tan, libmp.mpi_tan) + ctx.sinh = ctx._wrap_libmp_function(libmp.mpf_sinh, libmp.mpc_sinh) + ctx.cosh = ctx._wrap_libmp_function(libmp.mpf_cosh, libmp.mpc_cosh) + ctx.tanh = ctx._wrap_libmp_function(libmp.mpf_tanh, libmp.mpc_tanh) + ctx.asin = ctx._wrap_libmp_function(libmp.mpf_asin, libmp.mpc_asin) + ctx.acos = ctx._wrap_libmp_function(libmp.mpf_acos, libmp.mpc_acos) + ctx.atan = ctx._wrap_libmp_function(libmp.mpf_atan, libmp.mpc_atan) + ctx.asinh = ctx._wrap_libmp_function(libmp.mpf_asinh, libmp.mpc_asinh) + ctx.acosh = ctx._wrap_libmp_function(libmp.mpf_acosh, libmp.mpc_acosh) + ctx.atanh = ctx._wrap_libmp_function(libmp.mpf_atanh, libmp.mpc_atanh) + ctx.sinpi = ctx._wrap_libmp_function(libmp.mpf_sin_pi, libmp.mpc_sin_pi) + ctx.cospi = ctx._wrap_libmp_function(libmp.mpf_cos_pi, libmp.mpc_cos_pi) + ctx.floor = ctx._wrap_libmp_function(libmp.mpf_floor, libmp.mpc_floor) + ctx.ceil = ctx._wrap_libmp_function(libmp.mpf_ceil, libmp.mpc_ceil) + ctx.fib = ctx.fibonacci = ctx._wrap_libmp_function(libmp.mpf_fibonacci, libmp.mpc_fibonacci) + ctx.gamma = ctx._wrap_libmp_function(libmp.mpf_gamma, libmp.mpc_gamma) + ctx.digamma = ctx._wrap_libmp_function(libmp.mpf_psi0, libmp.mpc_psi0) + ctx.fac = ctx.factorial = ctx._wrap_libmp_function(libmp.mpf_factorial, libmp.mpc_factorial) + ctx.harmonic = ctx._wrap_libmp_function(libmp.mpf_harmonic, libmp.mpc_harmonic) + ctx.ei = ctx._wrap_libmp_function(libmp.mpf_ei, libmp.mpc_ei) + ctx.e1 = ctx._wrap_libmp_function(libmp.mpf_e1, libmp.mpc_e1) + ctx._ci = ctx._wrap_libmp_function(libmp.mpf_ci, libmp.mpc_ci) + ctx._si = ctx._wrap_libmp_function(libmp.mpf_si, libmp.mpc_si) + ctx.ellipk = ctx._wrap_libmp_function(libmp.mpf_ellipk, libmp.mpc_ellipk) + ctx.ellipe = ctx._wrap_libmp_function(libmp.mpf_ellipe, libmp.mpc_ellipe) + ctx.agm1 = ctx._wrap_libmp_function(libmp.mpf_agm1, libmp.mpc_agm1) + ctx._erf = ctx._wrap_libmp_function(libmp.mpf_erf, None) + ctx._erfc = ctx._wrap_libmp_function(libmp.mpf_erfc, None) + ctx._zeta = ctx._wrap_libmp_function(libmp.mpf_zeta, libmp.mpc_zeta) + ctx._altzeta = ctx._wrap_libmp_function(libmp.mpf_altzeta, libmp.mpc_altzeta) + + def to_fixed(ctx, x, prec): + return x.to_fixed(prec) + + def hypot(ctx, x, y): + r""" + Computes the Euclidean norm of the vector `(x, y)`, equal + to `\sqrt{x^2 + y^2}`. Both `x` and `y` must be real.""" + x = ctx.convert(x) + y = ctx.convert(y) + return ctx.make_mpf(libmp.mpf_hypot(x._mpf_, y._mpf_, *ctx._prec_rounding)) + + def _gamma_upper_int(ctx, n, z): + n = int(n) + if n == 0: + return ctx.e1(z) + if not hasattr(z, '_mpf_'): + raise NotImplementedError + prec, rounding = ctx._prec_rounding + real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding, gamma=True) + if imag is None: + return ctx.make_mpf(real) + else: + return ctx.make_mpc((real, imag)) + + def _expint_int(ctx, n, z): + n = int(n) + if n == 1: + return ctx.e1(z) + if not hasattr(z, '_mpf_'): + raise NotImplementedError + prec, rounding = ctx._prec_rounding + real, imag = libmp.mpf_expint(n, z._mpf_, prec, rounding) + if imag is None: + return ctx.make_mpf(real) + else: + return ctx.make_mpc((real, imag)) + + def _nthroot(ctx, x, n): + if hasattr(x, '_mpf_'): + try: + return ctx.make_mpf(libmp.mpf_nthroot(x._mpf_, n, *ctx._prec_rounding)) + except ComplexResult: + if ctx.trap_complex: + raise + x = (x._mpf_, libmp.fzero) + else: + x = x._mpc_ + return ctx.make_mpc(libmp.mpc_nthroot(x, n, *ctx._prec_rounding)) + + def _besselj(ctx, n, z): + prec, rounding = ctx._prec_rounding + if hasattr(z, '_mpf_'): + return ctx.make_mpf(libmp.mpf_besseljn(n, z._mpf_, prec, rounding)) + elif hasattr(z, '_mpc_'): + return ctx.make_mpc(libmp.mpc_besseljn(n, z._mpc_, prec, rounding)) + + def _agm(ctx, a, b=1): + prec, rounding = ctx._prec_rounding + if hasattr(a, '_mpf_') and hasattr(b, '_mpf_'): + try: + v = libmp.mpf_agm(a._mpf_, b._mpf_, prec, rounding) + return ctx.make_mpf(v) + except ComplexResult: + pass + if hasattr(a, '_mpf_'): a = (a._mpf_, libmp.fzero) + else: a = a._mpc_ + if hasattr(b, '_mpf_'): b = (b._mpf_, libmp.fzero) + else: b = b._mpc_ + return ctx.make_mpc(libmp.mpc_agm(a, b, prec, rounding)) + + def bernoulli(ctx, n): + return ctx.make_mpf(libmp.mpf_bernoulli(int(n), *ctx._prec_rounding)) + + def _zeta_int(ctx, n): + return ctx.make_mpf(libmp.mpf_zeta_int(int(n), *ctx._prec_rounding)) + + def atan2(ctx, y, x): + x = ctx.convert(x) + y = ctx.convert(y) + return ctx.make_mpf(libmp.mpf_atan2(y._mpf_, x._mpf_, *ctx._prec_rounding)) + + def psi(ctx, m, z): + z = ctx.convert(z) + m = int(m) + if ctx._is_real_type(z): + return ctx.make_mpf(libmp.mpf_psi(m, z._mpf_, *ctx._prec_rounding)) + else: + return ctx.make_mpc(libmp.mpc_psi(m, z._mpc_, *ctx._prec_rounding)) + + def clone(ctx): + """ + Create a copy of the context, with the same working precision. + """ + a = ctx.__class__() + a.prec = ctx.prec + return a + + # Several helper methods + # TODO: add more of these, make consistent, write docstrings, ... + + def _is_real_type(ctx, x): + if hasattr(x, '_mpc_') or type(x) is complex: + return False + return True + + def _is_complex_type(ctx, x): + if hasattr(x, '_mpc_') or type(x) is complex: + return True + return False + + def make_mpi(ctx, v): + a = new(ctx.mpi) + a._mpi_ = v + return a + + def isnpint(ctx, x): + if not x: + return True + if hasattr(x, '_mpf_'): + sign, man, exp, bc = x._mpf_ + return sign and exp >= 0 + if hasattr(x, '_mpc_'): + return not x.imag and ctx.isnpint(x.real) + if type(x) in int_types: + return x <= 0 + if isinstance(x, ctx.mpq): + # XXX: WRONG + p, q = x + if not p: + return True + return (not (q % p)) and p <= 0 + return ctx.isnpint(ctx.convert(x)) + + def __str__(ctx): + lines = ["Mpmath settings:", + (" mp.prec = %s" % ctx.prec).ljust(30) + "[default: 53]", + (" mp.dps = %s" % ctx.dps).ljust(30) + "[default: 15]", + (" mp.trap_complex = %s" % ctx.trap_complex).ljust(30) + "[default: False]", + ] + return "\n".join(lines) + + @property + def _repr_digits(ctx): + return repr_dps(ctx._prec) + + @property + def _str_digits(ctx): + return ctx._dps + + def extraprec(ctx, n, normalize_output=False): + """ + The block + + with extraprec(n): + + + increases the precision n bits, executes , and then + restores the precision. + + extraprec(n)(f) returns a decorated version of the function f + that increases the working precision by n bits before execution, + and restores the parent precision afterwards. With + normalize_output=True, it rounds the return value to the parent + precision. + """ + return PrecisionManager(ctx, lambda p: p + n, None, normalize_output) + + def extradps(ctx, n, normalize_output=False): + """ + This function is analogous to extraprec (see documentation) + but changes the decimal precision instead of the number of bits. + """ + return PrecisionManager(ctx, None, lambda d: d + n, normalize_output) + + def workprec(ctx, n, normalize_output=False): + """ + The block + + with workprec(n): + + + sets the precision to n bits, executes , and then restores + the precision. + + workprec(n)(f) returns a decorated version of the function f + that sets the precision to n bits before execution, + and restores the precision afterwards. With normalize_output=True, + it rounds the return value to the parent precision. + """ + return PrecisionManager(ctx, lambda p: n, None, normalize_output) + + def workdps(ctx, n, normalize_output=False): + """ + This function is analogous to workprec (see documentation) + but changes the decimal precision instead of the number of bits. + """ + return PrecisionManager(ctx, None, lambda d: n, normalize_output) + + def nstr(ctx, x, n=6, **kwargs): + """ + Convert an ``mpf``, ``mpc`` or ``mpi`` to a decimal string literal with *n* + significant digits. The small default value for *n* is chosen to + make this function useful for printing collections of numbers + (lists, matrices, etc). + + If *x* is an ``mpi``, there are some extra options, notably *mode*, which + can be 'brackets', 'diff', 'plusminus' or 'percent'. See ``mpi_to_str`` for + a more complete documentation. + + If *x* is a list or tuple, :func:`nstr` is applied recursively + to each element. For unrecognized classes, :func:`nstr` + simply returns ``str(x)``. + + The companion function :func:`nprint` prints the result + instead of returning it. + + >>> from mpmath import * + >>> nstr([+pi, ldexp(1,-500)]) + '[3.14159, 3.05494e-151]' + >>> nprint([+pi, ldexp(1,-500)]) + [3.14159, 3.05494e-151] + """ + if isinstance(x, list): + return "[%s]" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x)) + if isinstance(x, tuple): + return "(%s)" % (", ".join(ctx.nstr(c, n, **kwargs) for c in x)) + if hasattr(x, '_mpf_'): + return to_str(x._mpf_, n, **kwargs) + if hasattr(x, '_mpc_'): + return "(" + mpc_to_str(x._mpc_, n, **kwargs) + ")" + if isinstance(x, basestring): + return repr(x) + if isinstance(x, ctx.matrix): + return x.__nstr__(n, **kwargs) + if hasattr(x, '_mpi_'): + return ctx.mpi_to_str(x, n, **kwargs) + return str(x) + + def nprint(ctx, x, n=6, **kwargs): + """ + Equivalent to ``print nstr(x, n)``. + """ + print ctx.nstr(x, n, **kwargs) + + def _convert_fallback(ctx, x, strings): + if strings and isinstance(x, basestring): + if 'j' in x.lower(): + x = x.lower().replace(' ', '') + match = get_complex.match(x) + re = match.group('re') + if not re: + re = 0 + im = match.group('im').rstrip('j') + return ctx.mpc(ctx.convert(re), ctx.convert(im)) + if '[' in x or '(' in x or '+-' in x: + # XXX + return ctx.mpi_from_str(x) + if type(x) in ctx.types: # XXX fix for mpi for Cython context + return x + raise TypeError("cannot create mpf from " + repr(x)) + + def mpmathify(ctx, *args, **kwargs): + return ctx.convert(*args, **kwargs) + + def _parse_prec(ctx, kwargs): + if kwargs: + if kwargs.get('exact'): + return 0, 'f' + prec, rounding = ctx._prec_rounding + if 'rounding' in kwargs: + rounding = kwargs['rounding'] + if 'prec' in kwargs: + prec = kwargs['prec'] + if prec == ctx.inf: + return 0, 'f' + else: + prec = int(prec) + elif 'dps' in kwargs: + dps = kwargs['dps'] + if dps == ctx.inf: + return 0, 'f' + prec = dps_to_prec(dps) + return prec, rounding + return ctx._prec_rounding + + _exact_overflow_msg = "the exact result does not fit in memory" + + _hypsum_msg = """hypsum() failed to converge to the requested %i bits of accuracy +using a working precision of %i bits. Try with a higher maxprec, +maxterms, or set zeroprec.""" + + def hypsum(ctx, p, q, flags, coeffs, z, accurate_small=True, **kwargs): + if hasattr(z, "_mpf_"): + key = p, q, flags, 'R' + v = z._mpf_ + elif hasattr(z, "_mpc_"): + key = p, q, flags, 'C' + v = z._mpc_ + if key not in ctx.hyp_summators: + ctx.hyp_summators[key] = libmp.make_hyp_summator(key)[1] + summator = ctx.hyp_summators[key] + prec = ctx.prec + maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(prec)) + extraprec = 50 + epsshift = 25 + # Jumps in magnitude occur when parameters are close to negative + # integers. We must ensure that these terms are included in + # the sum and added accurately + magnitude_check = {} + max_total_jump = 0 + for i, c in enumerate(coeffs): + if flags[i] == 'Z': + if i >= p and c <= 0: + ok = False + for ii, cc in enumerate(coeffs[:p]): + # Note: c <= cc or c < cc, depending on convention + if flags[ii] == 'Z' and cc <= 0 and c <= cc: + ok = True + if not ok: + raise ZeroDivisionError("pole in hypergeometric series") + continue + n, d = ctx.nint_distance(c) + n = -int(n) + d = -d + if i >= p and n >= 0 and d > 4: + if n in magnitude_check: + magnitude_check[n] += d + else: + magnitude_check[n] = d + extraprec = max(extraprec, d - prec + 60) + max_total_jump += abs(d) + while 1: + if extraprec > maxprec: + raise ValueError(ctx._hypsum_msg % (prec, prec+extraprec)) + wp = prec + extraprec + if magnitude_check: + mag_dict = dict((n,None) for n in magnitude_check) + else: + mag_dict = {} + zv, have_complex, magnitude = summator(coeffs, v, prec, wp, \ + epsshift, mag_dict, **kwargs) + cancel = -magnitude + jumps_resolved = True + if extraprec < max_total_jump: + for n in mag_dict.values(): + if (n is None) or (n < prec): + jumps_resolved = False + break + accurate = (cancel < extraprec-25-5 or not accurate_small) + if jumps_resolved: + if accurate: + break + # zero? + zeroprec = kwargs.get('zeroprec') + if zeroprec is not None: + if cancel > zeroprec: + if have_complex: + return ctx.mpc(0) + else: + return ctx.zero + + # Some near-singularities were not included, so increase + # precision and repeat until they are + extraprec *= 2 + # Possible workaround for bad roundoff in fixed-point arithmetic + epsshift += 5 + extraprec += 5 + + if have_complex: + z = ctx.make_mpc(zv) + else: + z = ctx.make_mpf(zv) + return z + + def ldexp(ctx, x, n): + r""" + Computes `x 2^n` efficiently. No rounding is performed. + The argument `x` must be a real floating-point number (or + possible to convert into one) and `n` must be a Python ``int``. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> ldexp(1, 10) + mpf('1024.0') + >>> ldexp(1, -3) + mpf('0.125') + + """ + x = ctx.convert(x) + return ctx.make_mpf(libmp.mpf_shift(x._mpf_, n)) + + def frexp(ctx, x): + r""" + Given a real number `x`, returns `(y, n)` with `y \in [0.5, 1)`, + `n` a Python integer, and such that `x = y 2^n`. No rounding is + performed. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> frexp(7.5) + (mpf('0.9375'), 3) + + """ + x = ctx.convert(x) + y, n = libmp.mpf_frexp(x._mpf_) + return ctx.make_mpf(y), n + + def fneg(ctx, x, **kwargs): + """ + Negates the number *x*, giving a floating-point result, optionally + using a custom precision and rounding mode. + + See the documentation of :func:`fadd` for a detailed description + of how to specify precision and rounding. + + **Examples** + + An mpmath number is returned:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fneg(2.5) + mpf('-2.5') + >>> fneg(-5+2j) + mpc(real='5.0', imag='-2.0') + + Precise control over rounding is possible:: + + >>> x = fadd(2, 1e-100, exact=True) + >>> fneg(x) + mpf('-2.0') + >>> fneg(x, rounding='f') + mpf('-2.0000000000000004') + + Negating with and without roundoff:: + + >>> n = 200000000000000000000001 + >>> print int(-mpf(n)) + -200000000000000016777216 + >>> print int(fneg(n)) + -200000000000000016777216 + >>> print int(fneg(n, prec=log(n,2)+1)) + -200000000000000000000001 + >>> print int(fneg(n, dps=log(n,10)+1)) + -200000000000000000000001 + >>> print int(fneg(n, prec=inf)) + -200000000000000000000001 + >>> print int(fneg(n, dps=inf)) + -200000000000000000000001 + >>> print int(fneg(n, exact=True)) + -200000000000000000000001 + + """ + prec, rounding = ctx._parse_prec(kwargs) + x = ctx.convert(x) + if hasattr(x, '_mpf_'): + return ctx.make_mpf(mpf_neg(x._mpf_, prec, rounding)) + if hasattr(x, '_mpc_'): + return ctx.make_mpc(mpc_neg(x._mpc_, prec, rounding)) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def fadd(ctx, x, y, **kwargs): + """ + Adds the numbers *x* and *y*, giving a floating-point result, + optionally using a custom precision and rounding mode. + + The default precision is the working precision of the context. + You can specify a custom precision in bits by passing the *prec* keyword + argument, or by providing an equivalent decimal precision with the *dps* + keyword argument. If the precision is set to ``+inf``, or if the flag + *exact=True* is passed, an exact addition with no rounding is performed. + + When the precision is finite, the optional *rounding* keyword argument + specifies the direction of rounding. Valid options are ``'n'`` for + nearest (default), ``'f'`` for floor, ``'c'`` for ceiling, ``'d'`` + for down, ``'u'`` for up. + + **Examples** + + Using :func:`fadd` with precision and rounding control:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fadd(2, 1e-20) + mpf('2.0') + >>> fadd(2, 1e-20, rounding='u') + mpf('2.0000000000000004') + >>> nprint(fadd(2, 1e-20, prec=100), 25) + 2.00000000000000000001 + >>> nprint(fadd(2, 1e-20, dps=15), 25) + 2.0 + >>> nprint(fadd(2, 1e-20, dps=25), 25) + 2.00000000000000000001 + >>> nprint(fadd(2, 1e-20, exact=True), 25) + 2.00000000000000000001 + + Exact addition avoids cancellation errors, enforcing familiar laws + of numbers such as `x+y-x = y`, which don't hold in floating-point + arithmetic with finite precision:: + + >>> x, y = mpf(2), mpf('1e-1000') + >>> print x + y - x + 0.0 + >>> print fadd(x, y, prec=inf) - x + 1.0e-1000 + >>> print fadd(x, y, exact=True) - x + 1.0e-1000 + + Exact addition can be inefficient and may be impossible to perform + with large magnitude differences:: + + >>> fadd(1, '1e-100000000000000000000', prec=inf) + Traceback (most recent call last): + ... + OverflowError: the exact result does not fit in memory + + """ + prec, rounding = ctx._parse_prec(kwargs) + x = ctx.convert(x) + y = ctx.convert(y) + try: + if hasattr(x, '_mpf_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpf(mpf_add(x._mpf_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_add_mpf(y._mpc_, x._mpf_, prec, rounding)) + if hasattr(x, '_mpc_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpc(mpc_add_mpf(x._mpc_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_add(x._mpc_, y._mpc_, prec, rounding)) + except (ValueError, OverflowError): + raise OverflowError(ctx._exact_overflow_msg) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def fsub(ctx, x, y, **kwargs): + """ + Subtracts the numbers *x* and *y*, giving a floating-point result, + optionally using a custom precision and rounding mode. + + See the documentation of :func:`fadd` for a detailed description + of how to specify precision and rounding. + + **Examples** + + Using :func:`fsub` with precision and rounding control:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fsub(2, 1e-20) + mpf('2.0') + >>> fsub(2, 1e-20, rounding='d') + mpf('1.9999999999999998') + >>> nprint(fsub(2, 1e-20, prec=100), 25) + 1.99999999999999999999 + >>> nprint(fsub(2, 1e-20, dps=15), 25) + 2.0 + >>> nprint(fsub(2, 1e-20, dps=25), 25) + 1.99999999999999999999 + >>> nprint(fsub(2, 1e-20, exact=True), 25) + 1.99999999999999999999 + + Exact subtraction avoids cancellation errors, enforcing familiar laws + of numbers such as `x-y+y = x`, which don't hold in floating-point + arithmetic with finite precision:: + + >>> x, y = mpf(2), mpf('1e1000') + >>> print x - y + y + 0.0 + >>> print fsub(x, y, prec=inf) + y + 2.0 + >>> print fsub(x, y, exact=True) + y + 2.0 + + Exact addition can be inefficient and may be impossible to perform + with large magnitude differences:: + + >>> fsub(1, '1e-100000000000000000000', prec=inf) + Traceback (most recent call last): + ... + OverflowError: the exact result does not fit in memory + + """ + prec, rounding = ctx._parse_prec(kwargs) + x = ctx.convert(x) + y = ctx.convert(y) + try: + if hasattr(x, '_mpf_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpf(mpf_sub(x._mpf_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_sub((x._mpf_, fzero), y._mpc_, prec, rounding)) + if hasattr(x, '_mpc_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpc(mpc_sub_mpf(x._mpc_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_sub(x._mpc_, y._mpc_, prec, rounding)) + except (ValueError, OverflowError): + raise OverflowError(ctx._exact_overflow_msg) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def fmul(ctx, x, y, **kwargs): + """ + Multiplies the numbers *x* and *y*, giving a floating-point result, + optionally using a custom precision and rounding mode. + + See the documentation of :func:`fadd` for a detailed description + of how to specify precision and rounding. + + **Examples** + + The result is an mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fmul(2, 5.0) + mpf('10.0') + >>> fmul(0.5j, 0.5) + mpc(real='0.0', imag='0.25') + + Avoiding roundoff:: + + >>> x, y = 10**10+1, 10**15+1 + >>> print x*y + 10000000001000010000000001 + >>> print mpf(x) * mpf(y) + 1.0000000001e+25 + >>> print int(mpf(x) * mpf(y)) + 10000000001000011026399232 + >>> print int(fmul(x, y)) + 10000000001000011026399232 + >>> print int(fmul(x, y, dps=25)) + 10000000001000010000000001 + >>> print int(fmul(x, y, exact=True)) + 10000000001000010000000001 + + Exact multiplication with complex numbers can be inefficient and may + be impossible to perform with large magnitude differences between + real and imaginary parts:: + + >>> x = 1+2j + >>> y = mpc(2, '1e-100000000000000000000') + >>> fmul(x, y) + mpc(real='2.0', imag='4.0') + >>> fmul(x, y, rounding='u') + mpc(real='2.0', imag='4.0000000000000009') + >>> fmul(x, y, exact=True) + Traceback (most recent call last): + ... + OverflowError: the exact result does not fit in memory + + """ + prec, rounding = ctx._parse_prec(kwargs) + x = ctx.convert(x) + y = ctx.convert(y) + try: + if hasattr(x, '_mpf_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpf(mpf_mul(x._mpf_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_mul_mpf(y._mpc_, x._mpf_, prec, rounding)) + if hasattr(x, '_mpc_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpc(mpc_mul_mpf(x._mpc_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_mul(x._mpc_, y._mpc_, prec, rounding)) + except (ValueError, OverflowError): + raise OverflowError(ctx._exact_overflow_msg) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def fdiv(ctx, x, y, **kwargs): + """ + Divides the numbers *x* and *y*, giving a floating-point result, + optionally using a custom precision and rounding mode. + + See the documentation of :func:`fadd` for a detailed description + of how to specify precision and rounding. + + **Examples** + + The result is an mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fdiv(3, 2) + mpf('1.5') + >>> fdiv(2, 3) + mpf('0.66666666666666663') + >>> fdiv(2+4j, 0.5) + mpc(real='4.0', imag='8.0') + + The rounding direction and precision can be controlled:: + + >>> fdiv(2, 3, dps=3) # Should be accurate to at least 3 digits + mpf('0.6666259765625') + >>> fdiv(2, 3, rounding='d') + mpf('0.66666666666666663') + >>> fdiv(2, 3, prec=60) + mpf('0.66666666666666667') + >>> fdiv(2, 3, rounding='u') + mpf('0.66666666666666674') + + Checking the error of a division by performing it at higher precision:: + + >>> fdiv(2, 3) - fdiv(2, 3, prec=100) + mpf('-3.7007434154172148e-17') + + Unlike :func:`fadd`, :func:`fmul`, etc., exact division is not + allowed since the quotient of two floating-point numbers generally + does not have an exact floating-point representation. (In the + future this might be changed to allow the case where the division + is actually exact.) + + >>> fdiv(2, 3, exact=True) + Traceback (most recent call last): + ... + ValueError: division is not an exact operation + + """ + prec, rounding = ctx._parse_prec(kwargs) + if not prec: + raise ValueError("division is not an exact operation") + x = ctx.convert(x) + y = ctx.convert(y) + if hasattr(x, '_mpf_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpf(mpf_div(x._mpf_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_div((x._mpf_, fzero), y._mpc_, prec, rounding)) + if hasattr(x, '_mpc_'): + if hasattr(y, '_mpf_'): + return ctx.make_mpc(mpc_div_mpf(x._mpc_, y._mpf_, prec, rounding)) + if hasattr(y, '_mpc_'): + return ctx.make_mpc(mpc_div(x._mpc_, y._mpc_, prec, rounding)) + raise ValueError("Arguments need to be mpf or mpc compatible numbers") + + def nint_distance(ctx, x): + """ + Returns (n, d) where n is the nearest integer to x and d is the + log-2 distance (i.e. distance in bits) of n from x. If d < 0, + (-d) gives the bits of cancellation when n is subtracted from x. + This function is intended to be used to check for cancellation + at poles. + """ + if hasattr(x, "_mpf_"): + re = x._mpf_ + im_dist = ctx.ninf + elif hasattr(x, "_mpc_"): + re, im = x._mpc_ + isign, iman, iexp, ibc = im + if iman: + im_dist = iexp + ibc + elif im == fzero: + im_dist = ctx.ninf + else: + raise ValueError("requires a finite number") + elif isinstance(x, int_types): + return int(x), ctx.ninf + elif isinstance(x, rational.mpq): + p, q = x + n, r = divmod(p, q) + if 2*r >= q: + n += 1 + elif not r: + return n, ctx.ninf + # log(p/q-n) = log((p-nq)/q) = log(p-nq) - log(q) + d = bitcount(abs(p-n*q)) - bitcount(q) + return n, d + else: + x = ctx.convert(x) + if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"): + return ctx.nint_distance(x) + else: + raise TypeError("requires an mpf/mpc") + sign, man, exp, bc = re + shift = exp+bc + if sign: + man = -man + if shift < -1: + n = 0 + re_dist = shift + elif man: + if exp >= 0: + n = man << exp + re_dist = ctx.ninf + else: + if shift >= 0: + xfixed = man << shift + else: + xfixed = man >> (-shift) + n1 = xfixed >> bc + n2 = -((-xfixed) >> bc) + dist1 = abs(xfixed - (n1<>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fprod([1, 2, 0.5, 7]) + mpf('7.0') + + """ + orig = ctx.prec + try: + v = ctx.one + for p in factors: + v *= p + finally: + ctx.prec = orig + return +v + + def rand(ctx): + """ + Returns an ``mpf`` with value chosen randomly from `[0, 1)`. + The number of randomly generated bits in the mantissa is equal + to the working precision. + """ + return ctx.make_mpf(mpf_rand(ctx._prec)) + + def fraction(ctx, p, q): + """ + Given Python integers `(p, q)`, returns a lazy ``mpf`` representing + the fraction `p/q`. The value is updated with the precision. + + >>> from mpmath import * + >>> mp.dps = 15 + >>> a = fraction(1,100) + >>> b = mpf(1)/100 + >>> print a; print b + 0.01 + 0.01 + >>> mp.dps = 30 + >>> print a; print b # a will be accurate + 0.01 + 0.0100000000000000002081668171172 + >>> mp.dps = 15 + """ + return ctx.constant(lambda prec, rnd: from_rational(p, q, prec, rnd), + '%s/%s' % (p, q)) + + def mpi_from_str(ctx, s): + """ + Parse an interval number given as a string. + + Allowed forms are + 1. 'a +- b' + 2. 'a (b%)' % sign is optional + 3. '[a, b]' + 4. 'x[y,z]e' + In 1, a is the midpoint of the interval and b is the half-width. + In 2, a is the midpoint of the interval and b is the half-width. + In 3, the interval is indicated directly. + In 4, x are shared digits, y and z are unequal digits, e is the exponent. + """ + e = ValueError("Improperly formed interval number '%s'" %s) + s = s.replace(" ", "") + if "+-" in s: + # case 1 + n = [ctx.mpf(strip(i)) for i in s.split("+-")] + return ctx.mpi(n[0] - n[1], n[0] + n[1]) + elif "(" in s: + # case 2 + if s[0] == "(": # Don't confuse with a complex number (x,y) + return None + if ")" not in s: + raise e + s = s.replace(")", "") + percent = False + if "%" in s: + if s[-1] != "%": + raise e + percent = True + s = s.replace("%", "") + a, p = [ctx.mpf(strip(i)) for i in s.split("(")] + d = p + if percent: + d = a*p / 100 + return ctx.mpi(a - d, a + d) + elif "," in s: + if ('[' not in s) or (']' not in s): + raise e + if s[0] == '[': + # case 3 + s = s.replace("[", "") + s = s.replace("]", "") + n = [ctx.mpf(strip(i)) for i in s.split(",")] + return ctx.mpi(n[0], n[1]) + else: + # case 4 + x, y = s.split('[') + y, z = y.split(',') + if 'e' in s: + z, e = z.split(']') + else: + z, e = z.rstrip(']'), '' + return ctx.mpi(x + y + e, x + z + e) + else: + return None + + def mpi_to_str(ctx, x, dps=None, use_spaces=True, brackets=('[', ']'), + mode='brackets', error_dps=4, **kwargs): + """ + Convert a mpi interval to a string. + + **Arguments** + + *dps* + decimal places to use for printing + *use_spaces* + use spaces for more readable output, defaults to true + *brackets* + tuple of two strings indicating the brackets to use + *mode* + mode of display: 'plusminus', 'percent', 'brackets' (default) or 'diff' + *error_dps* + limit the error to *error_dps* digits (mode 'plusminus and 'percent') + + **Examples** + + >>> from mpmath import mpi, mp + >>> mp.dps = 30 + >>> x = mpi(1, 2) + >>> mpi_to_str(x, mode='plusminus') + '1.5 +- 5.0e-1' + >>> mpi_to_str(x, mode='percent') + '1.5 (33.33%)' + >>> mpi_to_str(x, mode='brackets') + '[1.0, 2.0]' + >>> mpi_to_str(x, mode='brackets' , brackets=('<', '>')) + '<1.0, 2.0>' + >>> x = mpi('5.2582327113062393041', '5.2582327113062749951') + >>> mpi_to_str(x, mode='diff') + '5.2582327113062[4, 7]' + >>> mpi_to_str(mpi(0), mode='percent') + '0.0 (0%)' + + """ + if dps is None: + dps = ctx.dps # TODO: maybe choose a smaller default value + a = to_str(x.a._mpf_, dps, **kwargs) + b = to_str(x.b._mpf_, dps, **kwargs) + mid = to_str(x.mid._mpf_, dps, **kwargs) + delta = to_str((x.delta/2)._mpf_, error_dps, **kwargs) + sp = "" + if use_spaces: + sp = " " + br1, br2 = brackets + if mode == 'plusminus': + s = mid + sp + "+-" + sp + delta + elif mode == 'percent': + a = x.mid + if x.mid != 0: + b = 100*x.delta/(2*x.mid) + else: + b = MPZ_ZERO + m = str(a) + p = ctx.nstr(b, error_dps) + s = m + sp + "(" + p + "%)" + elif mode == 'brackets': + s = br1 + a.strip() + "," + sp + b + br2 + elif mode == 'diff': + # use more digits if str(x.a) and str(x.b) are equal + if a == b: + a = to_str(x.a._mpf_, repr_dps(ctx.prec), **kwargs) + b = to_str(x.b._mpf_, repr_dps(ctx.prec), **kwargs) + # separate mantissa and exponent + a = a.split('e') + if len(a) == 1: + a.append('') + b = b.split('e') + if len(b) == 1: + b.append('') + if a[1] == b[1]: + if a[0] != b[0]: + for i in xrange(len(a[0]) + 1): + if a[0][i] != b[0][i]: + break + s = (a[0][:i] + br1 + a[0][i:] + ',' + sp + b[0][i:] + br2 + + 'e'*min(len(a[1]), 1) + a[1]) + else: # no difference + s = a[0] + br1 + br2 + 'e'*min(len(a[1]), 1) + a[1] + else: + s = br1 + 'e'.join(a) + ',' + sp + 'e'.join(b) + br2 + else: + raise ValueError("'%s' is unknown mode for printing mpi" % mode) + return s + + def absmin(ctx, x): + """ + Returns ``abs(x).a`` for an interval, or ``abs(x)`` for anything else. + """ + if hasattr(x, '_mpi_'): + return abs(x).a + return abs(x) + + def absmax(ctx, x): + """ + Returns ``abs(x).b`` for an interval, or ``abs(x)`` for anything else. + """ + if hasattr(x, '_mpi_'): + return abs(x).b + return abs(x) + + def _as_points(ctx, x): + if hasattr(x, '_mpi_'): + return [x.a, x.b] + return x + + ''' + def _zetasum(ctx, s, a, b): + """ + Computes sum of k^(-s) for k = a, a+1, ..., b with a, b both small + integers. + """ + a = int(a) + b = int(b) + s = ctx.convert(s) + prec, rounding = ctx._prec_rounding + if hasattr(s, '_mpf_'): + v = ctx.make_mpf(libmp.mpf_zetasum(s._mpf_, a, b, prec)) + elif hasattr(s, '_mpc_'): + v = ctx.make_mpc(libmp.mpc_zetasum(s._mpc_, a, b, prec)) + return v + ''' + + def _zetasum_fast(ctx, s, a, n, derivatives=[0], reflect=False): + if not (ctx.isint(a) and hasattr(s, "_mpc_")): + raise NotImplementedError + a = int(a) + prec = ctx._prec + xs, ys = libmp.mpc_zetasum(s._mpc_, a, n, derivatives, reflect, prec) + xs = map(ctx.make_mpc, xs) + ys = map(ctx.make_mpc, ys) + return xs, ys + + +class PrecisionManager: + def __init__(self, ctx, precfun, dpsfun, normalize_output=False): + self.ctx = ctx + self.precfun = precfun + self.dpsfun = dpsfun + self.normalize_output = normalize_output + def __call__(self, f): + def g(*args, **kwargs): + orig = self.ctx.prec + try: + if self.precfun: + self.ctx.prec = self.precfun(self.ctx.prec) + else: + self.ctx.dps = self.dpsfun(self.ctx.dps) + if self.normalize_output: + v = f(*args, **kwargs) + if type(v) is tuple: + return tuple([+a for a in v]) + return +v + else: + return f(*args, **kwargs) + finally: + self.ctx.prec = orig + g.__name__ = f.__name__ + g.__doc__ = f.__doc__ + return g + def __enter__(self): + self.origp = self.ctx.prec + if self.precfun: + self.ctx.prec = self.precfun(self.ctx.prec) + else: + self.ctx.dps = self.dpsfun(self.ctx.dps) + def __exit__(self, exc_type, exc_val, exc_tb): + self.ctx.prec = self.origp + return False + + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/compiler/gdsMill/mpmath/ctx_mp_python.py b/compiler/gdsMill/mpmath/ctx_mp_python.py new file mode 100644 index 00000000..df3d3fce --- /dev/null +++ b/compiler/gdsMill/mpmath/ctx_mp_python.py @@ -0,0 +1,986 @@ +#from ctx_base import StandardBaseContext + +from libmp import (MPZ, MPZ_ZERO, MPZ_ONE, int_types, repr_dps, + round_floor, round_ceiling, dps_to_prec, round_nearest, prec_to_dps, + ComplexResult, to_pickable, from_pickable, normalize, + from_int, from_float, from_str, to_int, to_float, to_str, + from_rational, from_man_exp, + fone, fzero, finf, fninf, fnan, + mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_mul_int, + mpf_div, mpf_rdiv_int, mpf_pow_int, mpf_mod, + mpf_eq, mpf_cmp, mpf_lt, mpf_gt, mpf_le, mpf_ge, + mpf_hash, mpf_rand, + mpf_sum, + bitcount, to_fixed, + mpc_to_str, + mpc_to_complex, mpc_hash, mpc_pos, mpc_is_nonzero, mpc_neg, mpc_conjugate, + mpc_abs, mpc_add, mpc_add_mpf, mpc_sub, mpc_sub_mpf, mpc_mul, mpc_mul_mpf, + mpc_mul_int, mpc_div, mpc_div_mpf, mpc_pow, mpc_pow_mpf, mpc_pow_int, + mpc_mpf_div, + mpf_pow, + mpi_mid, mpi_delta, mpi_str, + mpi_abs, mpi_pos, mpi_neg, mpi_add, mpi_sub, + mpi_mul, mpi_div, mpi_pow_int, mpi_pow, + mpf_pi, mpf_degree, mpf_e, mpf_phi, mpf_ln2, mpf_ln10, + mpf_euler, mpf_catalan, mpf_apery, mpf_khinchin, + mpf_glaisher, mpf_twinprime, mpf_mertens, + int_types) + +import rational +import function_docs + +new = object.__new__ + +class mpnumeric(object): + """Base class for mpf and mpc.""" + __slots__ = [] + def __new__(cls, val): + raise NotImplementedError + +class _mpf(mpnumeric): + """ + An mpf instance holds a real-valued floating-point number. mpf:s + work analogously to Python floats, but support arbitrary-precision + arithmetic. + """ + __slots__ = ['_mpf_'] + + def __new__(cls, val=fzero, **kwargs): + """A new mpf can be created from a Python float, an int, a + or a decimal string representing a number in floating-point + format.""" + prec, rounding = cls.context._prec_rounding + if kwargs: + prec = kwargs.get('prec', prec) + if 'dps' in kwargs: + prec = dps_to_prec(kwargs['dps']) + rounding = kwargs.get('rounding', rounding) + if type(val) is cls: + sign, man, exp, bc = val._mpf_ + if (not man) and exp: + return val + v = new(cls) + v._mpf_ = normalize(sign, man, exp, bc, prec, rounding) + return v + elif type(val) is tuple: + if len(val) == 2: + v = new(cls) + v._mpf_ = from_man_exp(val[0], val[1], prec, rounding) + return v + if len(val) == 4: + sign, man, exp, bc = val + v = new(cls) + v._mpf_ = normalize(sign, MPZ(man), exp, bc, prec, rounding) + return v + raise ValueError + else: + v = new(cls) + v._mpf_ = mpf_pos(cls.mpf_convert_arg(val, prec, rounding), prec, rounding) + return v + + @classmethod + def mpf_convert_arg(cls, x, prec, rounding): + if isinstance(x, int_types): return from_int(x) + if isinstance(x, float): return from_float(x) + if isinstance(x, basestring): return from_str(x, prec, rounding) + if isinstance(x, cls.context.constant): return x.func(prec, rounding) + if hasattr(x, '_mpf_'): return x._mpf_ + if hasattr(x, '_mpmath_'): + t = cls.context.convert(x._mpmath_(prec, rounding)) + if hasattr(t, '_mpf_'): + return t._mpf_ + raise TypeError("cannot create mpf from " + repr(x)) + + @classmethod + def mpf_convert_rhs(cls, x): + if isinstance(x, int_types): return from_int(x) + if isinstance(x, float): return from_float(x) + if isinstance(x, complex_types): return cls.context.mpc(x) + if isinstance(x, rational.mpq): + p, q = x + return from_rational(p, q, cls.context.prec) + if hasattr(x, '_mpf_'): return x._mpf_ + if hasattr(x, '_mpmath_'): + t = cls.context.convert(x._mpmath_(*cls.context._prec_rounding)) + if hasattr(t, '_mpf_'): + return t._mpf_ + return t + return NotImplemented + + @classmethod + def mpf_convert_lhs(cls, x): + x = cls.mpf_convert_rhs(x) + if type(x) is tuple: + return cls.context.make_mpf(x) + return x + + man_exp = property(lambda self: self._mpf_[1:3]) + man = property(lambda self: self._mpf_[1]) + exp = property(lambda self: self._mpf_[2]) + bc = property(lambda self: self._mpf_[3]) + + real = property(lambda self: self) + imag = property(lambda self: self.context.zero) + + conjugate = lambda self: self + + def __getstate__(self): return to_pickable(self._mpf_) + def __setstate__(self, val): self._mpf_ = from_pickable(val) + + def __repr__(s): + if s.context.pretty: + return str(s) + return "mpf('%s')" % to_str(s._mpf_, s.context._repr_digits) + + def __str__(s): return to_str(s._mpf_, s.context._str_digits) + def __hash__(s): return mpf_hash(s._mpf_) + def __int__(s): return int(to_int(s._mpf_)) + def __long__(s): return long(to_int(s._mpf_)) + def __float__(s): return to_float(s._mpf_) + def __complex__(s): return complex(float(s)) + def __nonzero__(s): return s._mpf_ != fzero + + def __abs__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpf_ = mpf_abs(s._mpf_, prec, rounding) + return v + + def __pos__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpf_ = mpf_pos(s._mpf_, prec, rounding) + return v + + def __neg__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpf_ = mpf_neg(s._mpf_, prec, rounding) + return v + + def _cmp(s, t, func): + if hasattr(t, '_mpf_'): + t = t._mpf_ + else: + t = s.mpf_convert_rhs(t) + if t is NotImplemented: + return t + return func(s._mpf_, t) + + def __cmp__(s, t): return s._cmp(t, mpf_cmp) + def __lt__(s, t): return s._cmp(t, mpf_lt) + def __gt__(s, t): return s._cmp(t, mpf_gt) + def __le__(s, t): return s._cmp(t, mpf_le) + def __ge__(s, t): return s._cmp(t, mpf_ge) + + def __ne__(s, t): + v = s.__eq__(t) + if v is NotImplemented: + return v + return not v + + def __rsub__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if type(t) in int_types: + v = new(cls) + v._mpf_ = mpf_sub(from_int(t), s._mpf_, prec, rounding) + return v + t = s.mpf_convert_lhs(t) + if t is NotImplemented: + return t + return t - s + + def __rdiv__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if isinstance(t, int_types): + v = new(cls) + v._mpf_ = mpf_rdiv_int(t, s._mpf_, prec, rounding) + return v + t = s.mpf_convert_lhs(t) + if t is NotImplemented: + return t + return t / s + + def __rpow__(s, t): + t = s.mpf_convert_lhs(t) + if t is NotImplemented: + return t + return t ** s + + def __rmod__(s, t): + t = s.mpf_convert_lhs(t) + if t is NotImplemented: + return t + return t % s + + def sqrt(s): + return s.context.sqrt(s) + + def ae(s, t, rel_eps=None, abs_eps=None): + return s.context.almosteq(s, t, rel_eps, abs_eps) + + def to_fixed(self, prec): + return to_fixed(self._mpf_, prec) + + +mpf_binary_op = """ +def %NAME%(self, other): + mpf, new, (prec, rounding) = self._ctxdata + sval = self._mpf_ + if hasattr(other, '_mpf_'): + tval = other._mpf_ + %WITH_MPF% + ttype = type(other) + if ttype in int_types: + %WITH_INT% + elif ttype is float: + tval = from_float(other) + %WITH_MPF% + elif hasattr(other, '_mpc_'): + tval = other._mpc_ + mpc = type(other) + %WITH_MPC% + elif ttype is complex: + tval = from_float(other.real), from_float(other.imag) + mpc = self.context.mpc + %WITH_MPC% + if isinstance(other, mpnumeric): + return NotImplemented + try: + other = mpf.context.convert(other, strings=False) + except TypeError: + return NotImplemented + return self.%NAME%(other) +""" + +return_mpf = "; obj = new(mpf); obj._mpf_ = val; return obj" +return_mpc = "; obj = new(mpc); obj._mpc_ = val; return obj" + +mpf_pow_same = """ + try: + val = mpf_pow(sval, tval, prec, rounding) %s + except ComplexResult: + if mpf.context.trap_complex: + raise + mpc = mpf.context.mpc + val = mpc_pow((sval, fzero), (tval, fzero), prec, rounding) %s +""" % (return_mpf, return_mpc) + +def binary_op(name, with_mpf='', with_int='', with_mpc=''): + code = mpf_binary_op + code = code.replace("%WITH_INT%", with_int) + code = code.replace("%WITH_MPC%", with_mpc) + code = code.replace("%WITH_MPF%", with_mpf) + code = code.replace("%NAME%", name) + np = {} + exec code in globals(), np + return np[name] + +_mpf.__eq__ = binary_op('__eq__', + 'return mpf_eq(sval, tval)', + 'return mpf_eq(sval, from_int(other))', + 'return (tval[1] == fzero) and mpf_eq(tval[0], sval)') + +_mpf.__add__ = binary_op('__add__', + 'val = mpf_add(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_add(sval, from_int(other), prec, rounding)' + return_mpf, + 'val = mpc_add_mpf(tval, sval, prec, rounding)' + return_mpc) + +_mpf.__sub__ = binary_op('__sub__', + 'val = mpf_sub(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_sub(sval, from_int(other), prec, rounding)' + return_mpf, + 'val = mpc_sub((sval, fzero), tval, prec, rounding)' + return_mpc) + +_mpf.__mul__ = binary_op('__mul__', + 'val = mpf_mul(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_mul_int(sval, other, prec, rounding)' + return_mpf, + 'val = mpc_mul_mpf(tval, sval, prec, rounding)' + return_mpc) + +_mpf.__div__ = binary_op('__div__', + 'val = mpf_div(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_div(sval, from_int(other), prec, rounding)' + return_mpf, + 'val = mpc_mpf_div(sval, tval, prec, rounding)' + return_mpc) + +_mpf.__mod__ = binary_op('__mod__', + 'val = mpf_mod(sval, tval, prec, rounding)' + return_mpf, + 'val = mpf_mod(sval, from_int(other), prec, rounding)' + return_mpf, + 'raise NotImplementedError("complex modulo")') + +_mpf.__pow__ = binary_op('__pow__', + mpf_pow_same, + 'val = mpf_pow_int(sval, other, prec, rounding)' + return_mpf, + 'val = mpc_pow((sval, fzero), tval, prec, rounding)' + return_mpc) + +_mpf.__radd__ = _mpf.__add__ +_mpf.__rmul__ = _mpf.__mul__ +_mpf.__truediv__ = _mpf.__div__ +_mpf.__rtruediv__ = _mpf.__rdiv__ + + +class _constant(_mpf): + """Represents a mathematical constant with dynamic precision. + When printed or used in an arithmetic operation, a constant + is converted to a regular mpf at the working precision. A + regular mpf can also be obtained using the operation +x.""" + + def __new__(cls, func, name, docname=''): + a = object.__new__(cls) + a.name = name + a.func = func + a.__doc__ = getattr(function_docs, docname, '') + return a + + def __call__(self, prec=None, dps=None, rounding=None): + prec2, rounding2 = self.context._prec_rounding + if not prec: prec = prec2 + if not rounding: rounding = rounding2 + if dps: prec = dps_to_prec(dps) + return self.context.make_mpf(self.func(prec, rounding)) + + @property + def _mpf_(self): + prec, rounding = self.context._prec_rounding + return self.func(prec, rounding) + + def __repr__(self): + return "<%s: %s~>" % (self.name, self.context.nstr(self)) + + +class _mpc(mpnumeric): + """ + An mpc represents a complex number using a pair of mpf:s (one + for the real part and another for the imaginary part.) The mpc + class behaves fairly similarly to Python's complex type. + """ + + __slots__ = ['_mpc_'] + + def __new__(cls, real=0, imag=0): + s = object.__new__(cls) + if isinstance(real, complex_types): + real, imag = real.real, real.imag + elif hasattr(real, '_mpc_'): + s._mpc_ = real._mpc_ + return s + real = cls.context.mpf(real) + imag = cls.context.mpf(imag) + s._mpc_ = (real._mpf_, imag._mpf_) + return s + + real = property(lambda self: self.context.make_mpf(self._mpc_[0])) + imag = property(lambda self: self.context.make_mpf(self._mpc_[1])) + + def __getstate__(self): + return to_pickable(self._mpc_[0]), to_pickable(self._mpc_[1]) + + def __setstate__(self, val): + self._mpc_ = from_pickable(val[0]), from_pickable(val[1]) + + def __repr__(s): + if s.context.pretty: + return str(s) + r = repr(s.real)[4:-1] + i = repr(s.imag)[4:-1] + return "%s(real=%s, imag=%s)" % (type(s).__name__, r, i) + + def __str__(s): + return "(%s)" % mpc_to_str(s._mpc_, s.context._str_digits) + + def __complex__(s): + return mpc_to_complex(s._mpc_) + + def __pos__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpc_ = mpc_pos(s._mpc_, prec, rounding) + return v + + def __abs__(s): + prec, rounding = s.context._prec_rounding + v = new(s.context.mpf) + v._mpf_ = mpc_abs(s._mpc_, prec, rounding) + return v + + def __neg__(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpc_ = mpc_neg(s._mpc_, prec, rounding) + return v + + def conjugate(s): + cls, new, (prec, rounding) = s._ctxdata + v = new(cls) + v._mpc_ = mpc_conjugate(s._mpc_, prec, rounding) + return v + + def __nonzero__(s): + return mpc_is_nonzero(s._mpc_) + + def __hash__(s): + return mpc_hash(s._mpc_) + + @classmethod + def mpc_convert_lhs(cls, x): + try: + y = cls.context.convert(x) + return y + except TypeError: + return NotImplemented + + def __eq__(s, t): + if not hasattr(t, '_mpc_'): + if isinstance(t, str): + return False + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return s.real == t.real and s.imag == t.imag + + def __ne__(s, t): + b = s.__eq__(t) + if b is NotImplemented: + return b + return not b + + def _compare(*args): + raise TypeError("no ordering relation is defined for complex numbers") + + __gt__ = _compare + __le__ = _compare + __gt__ = _compare + __ge__ = _compare + + def __add__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if not hasattr(t, '_mpc_'): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + if hasattr(t, '_mpf_'): + v = new(cls) + v._mpc_ = mpc_add_mpf(s._mpc_, t._mpf_, prec, rounding) + return v + v = new(cls) + v._mpc_ = mpc_add(s._mpc_, t._mpc_, prec, rounding) + return v + + def __sub__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if not hasattr(t, '_mpc_'): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + if hasattr(t, '_mpf_'): + v = new(cls) + v._mpc_ = mpc_sub_mpf(s._mpc_, t._mpf_, prec, rounding) + return v + v = new(cls) + v._mpc_ = mpc_sub(s._mpc_, t._mpc_, prec, rounding) + return v + + def __mul__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if not hasattr(t, '_mpc_'): + if isinstance(t, int_types): + v = new(cls) + v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding) + return v + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + if hasattr(t, '_mpf_'): + v = new(cls) + v._mpc_ = mpc_mul_mpf(s._mpc_, t._mpf_, prec, rounding) + return v + t = s.mpc_convert_lhs(t) + v = new(cls) + v._mpc_ = mpc_mul(s._mpc_, t._mpc_, prec, rounding) + return v + + def __div__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if not hasattr(t, '_mpc_'): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + if hasattr(t, '_mpf_'): + v = new(cls) + v._mpc_ = mpc_div_mpf(s._mpc_, t._mpf_, prec, rounding) + return v + v = new(cls) + v._mpc_ = mpc_div(s._mpc_, t._mpc_, prec, rounding) + return v + + def __pow__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if isinstance(t, int_types): + v = new(cls) + v._mpc_ = mpc_pow_int(s._mpc_, t, prec, rounding) + return v + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + v = new(cls) + if hasattr(t, '_mpf_'): + v._mpc_ = mpc_pow_mpf(s._mpc_, t._mpf_, prec, rounding) + else: + v._mpc_ = mpc_pow(s._mpc_, t._mpc_, prec, rounding) + return v + + __radd__ = __add__ + + def __rsub__(s, t): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return t - s + + def __rmul__(s, t): + cls, new, (prec, rounding) = s._ctxdata + if isinstance(t, int_types): + v = new(cls) + v._mpc_ = mpc_mul_int(s._mpc_, t, prec, rounding) + return v + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return t * s + + def __rdiv__(s, t): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return t / s + + def __rpow__(s, t): + t = s.mpc_convert_lhs(t) + if t is NotImplemented: + return t + return t ** s + + __truediv__ = __div__ + __rtruediv__ = __rdiv__ + + def ae(s, t, rel_eps=None, abs_eps=None): + return s.context.almosteq(s, t, rel_eps, abs_eps) + + +complex_types = (complex, _mpc) + + +class PythonMPContext: + + def __init__(ctx): + ctx._prec_rounding = [53, round_nearest] + ctx.mpf = type('mpf', (_mpf,), {}) + ctx.mpc = type('mpc', (_mpc,), {}) + ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding] + ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding] + ctx.mpf.context = ctx + ctx.mpc.context = ctx + ctx.constant = type('constant', (_constant,), {}) + ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding] + ctx.constant.context = ctx + + def make_mpf(ctx, v): + a = new(ctx.mpf) + a._mpf_ = v + return a + + def make_mpc(ctx, v): + a = new(ctx.mpc) + a._mpc_ = v + return a + + def default(ctx): + ctx._prec = ctx._prec_rounding[0] = 53 + ctx._dps = 15 + ctx.trap_complex = False + + def _set_prec(ctx, n): + ctx._prec = ctx._prec_rounding[0] = max(1, int(n)) + ctx._dps = prec_to_dps(n) + + def _set_dps(ctx, n): + ctx._prec = ctx._prec_rounding[0] = dps_to_prec(n) + ctx._dps = max(1, int(n)) + + prec = property(lambda ctx: ctx._prec, _set_prec) + dps = property(lambda ctx: ctx._dps, _set_dps) + + def convert(ctx, x, strings=True): + """ + Converts *x* to an ``mpf``, ``mpc`` or ``mpi``. If *x* is of type ``mpf``, + ``mpc``, ``int``, ``float``, ``complex``, the conversion + will be performed losslessly. + + If *x* is a string, the result will be rounded to the present + working precision. Strings representing fractions or complex + numbers are permitted. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> mpmathify(3.5) + mpf('3.5') + >>> mpmathify('2.1') + mpf('2.1000000000000001') + >>> mpmathify('3/4') + mpf('0.75') + >>> mpmathify('2+3j') + mpc(real='2.0', imag='3.0') + + """ + if type(x) in ctx.types: return x + if isinstance(x, int_types): return ctx.make_mpf(from_int(x)) + if isinstance(x, float): return ctx.make_mpf(from_float(x)) + if isinstance(x, complex): + return ctx.make_mpc((from_float(x.real), from_float(x.imag))) + prec, rounding = ctx._prec_rounding + if isinstance(x, rational.mpq): + p, q = x + return ctx.make_mpf(from_rational(p, q, prec)) + if strings and isinstance(x, basestring): + try: + _mpf_ = from_str(x, prec, rounding) + return ctx.make_mpf(_mpf_) + except ValueError: + pass + if hasattr(x, '_mpf_'): return ctx.make_mpf(x._mpf_) + if hasattr(x, '_mpc_'): return ctx.make_mpc(x._mpc_) + if hasattr(x, '_mpmath_'): + return ctx.convert(x._mpmath_(prec, rounding)) + return ctx._convert_fallback(x, strings) + + def isnan(ctx, x): + """ + For an ``mpf`` *x*, determines whether *x* is not-a-number (nan):: + + >>> from mpmath import * + >>> isnan(nan), isnan(3) + (True, False) + """ + if not hasattr(x, '_mpf_'): + return False + return x._mpf_ == fnan + + def isinf(ctx, x): + """ + For an ``mpf`` *x*, determines whether *x* is infinite:: + + >>> from mpmath import * + >>> isinf(inf), isinf(-inf), isinf(3) + (True, True, False) + """ + if not hasattr(x, '_mpf_'): + return False + return x._mpf_ in (finf, fninf) + + def isint(ctx, x): + """ + For an ``mpf`` *x*, or any type that can be converted + to ``mpf``, determines whether *x* is exactly + integer-valued:: + + >>> from mpmath import * + >>> isint(3), isint(mpf(3)), isint(3.2) + (True, True, False) + """ + if isinstance(x, int_types): + return True + try: + x = ctx.convert(x) + except: + return False + if hasattr(x, '_mpf_'): + if ctx.isnan(x) or ctx.isinf(x): + return False + return x == int(x) + if isinstance(x, ctx.mpq): + p, q = x + return not (p % q) + return False + + def fsum(ctx, terms, absolute=False, squared=False): + """ + Calculates a sum containing a finite number of terms (for infinite + series, see :func:`nsum`). The terms will be converted to + mpmath numbers. For len(terms) > 2, this function is generally + faster and produces more accurate results than the builtin + Python function :func:`sum`. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fsum([1, 2, 0.5, 7]) + mpf('10.5') + + With squared=True each term is squared, and with absolute=True + the absolute value of each term is used. + """ + prec, rnd = ctx._prec_rounding + real = [] + imag = [] + other = 0 + for term in terms: + reval = imval = 0 + if hasattr(term, "_mpf_"): + reval = term._mpf_ + elif hasattr(term, "_mpc_"): + reval, imval = term._mpc_ + else: + term = ctx.convert(term) + if hasattr(term, "_mpf_"): + reval = term._mpf_ + elif hasattr(term, "_mpc_"): + reval, imval = term._mpc_ + else: + if absolute: term = ctx.absmax(term) + if squared: term = term**2 + other += term + continue + if imval: + if squared: + if absolute: + real.append(mpf_mul(reval,reval)) + real.append(mpf_mul(imval,imval)) + else: + reval, imval = mpc_pow_int((reval,imval),2,prec+10) + real.append(reval) + imag.append(imval) + elif absolute: + real.append(mpc_abs((reval,imval), prec)) + else: + real.append(reval) + imag.append(imval) + else: + if squared: + reval = mpf_mul(reval, reval) + elif absolute: + reval = mpf_abs(reval) + real.append(reval) + s = mpf_sum(real, prec, rnd, absolute) + if imag: + s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd))) + else: + s = ctx.make_mpf(s) + if other is 0: + return s + else: + return s + other + + def fdot(ctx, A, B=None): + r""" + Computes the dot product of the iterables `A` and `B`, + + .. math :: + + \sum_{k=0} A_k B_k. + + Alternatively, :func:`fdot` accepts a single iterable of pairs. + In other words, ``fdot(A,B)`` and ``fdot(zip(A,B))`` are equivalent. + + The elements are automatically converted to mpmath numbers. + + Examples:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> A = [2, 1.5, 3] + >>> B = [1, -1, 2] + >>> fdot(A, B) + mpf('6.5') + >>> zip(A, B) + [(2, 1), (1.5, -1), (3, 2)] + >>> fdot(_) + mpf('6.5') + + """ + if B: + A = zip(A, B) + prec, rnd = ctx._prec_rounding + real = [] + imag = [] + other = 0 + hasattr_ = hasattr + types = (ctx.mpf, ctx.mpc) + for a, b in A: + if type(a) not in types: a = ctx.convert(a) + if type(b) not in types: b = ctx.convert(b) + a_real = hasattr_(a, "_mpf_") + b_real = hasattr_(b, "_mpf_") + if a_real and b_real: + real.append(mpf_mul(a._mpf_, b._mpf_)) + continue + a_complex = hasattr_(a, "_mpc_") + b_complex = hasattr_(b, "_mpc_") + if a_real and b_complex: + aval = a._mpf_ + bre, bim = b._mpc_ + real.append(mpf_mul(aval, bre)) + imag.append(mpf_mul(aval, bim)) + elif b_real and a_complex: + are, aim = a._mpc_ + bval = b._mpf_ + real.append(mpf_mul(are, bval)) + imag.append(mpf_mul(aim, bval)) + elif a_complex and b_complex: + re, im = mpc_mul(a._mpc_, b._mpc_, prec+20) + real.append(re) + imag.append(im) + else: + other += a*b + s = mpf_sum(real, prec, rnd) + if imag: + s = ctx.make_mpc((s, mpf_sum(imag, prec, rnd))) + else: + s = ctx.make_mpf(s) + if other is 0: + return s + else: + return s + other + + def _wrap_libmp_function(ctx, mpf_f, mpc_f=None, mpi_f=None, doc=""): + """ + Given a low-level mpf_ function, and optionally similar functions + for mpc_ and mpi_, defines the function as a context method. + + It is assumed that the return type is the same as that of + the input; the exception is that propagation from mpf to mpc is possible + by raising ComplexResult. + + """ + def f(x, **kwargs): + if type(x) not in ctx.types: + x = ctx.convert(x) + prec, rounding = ctx._prec_rounding + if kwargs: + prec = kwargs.get('prec', prec) + if 'dps' in kwargs: + prec = dps_to_prec(kwargs['dps']) + rounding = kwargs.get('rounding', rounding) + if hasattr(x, '_mpf_'): + try: + return ctx.make_mpf(mpf_f(x._mpf_, prec, rounding)) + except ComplexResult: + # Handle propagation to complex + if ctx.trap_complex: + raise + return ctx.make_mpc(mpc_f((x._mpf_, fzero), prec, rounding)) + elif hasattr(x, '_mpc_'): + return ctx.make_mpc(mpc_f(x._mpc_, prec, rounding)) + elif hasattr(x, '_mpi_'): + if mpi_f: + return ctx.make_mpi(mpi_f(x._mpi_, prec)) + raise NotImplementedError("%s of a %s" % (name, type(x))) + name = mpf_f.__name__[4:] + f.__doc__ = function_docs.__dict__.get(name, "Computes the %s of x" % doc) + return f + + # Called by SpecialFunctions.__init__() + @classmethod + def _wrap_specfun(cls, name, f, wrap): + if wrap: + def f_wrapped(ctx, *args, **kwargs): + convert = ctx.convert + args = [convert(a) for a in args] + prec = ctx.prec + try: + ctx.prec += 10 + retval = f(ctx, *args, **kwargs) + finally: + ctx.prec = prec + return +retval + else: + f_wrapped = f + f_wrapped.__doc__ = function_docs.__dict__.get(name, "") + setattr(cls, name, f_wrapped) + + def _convert_param(ctx, x): + if hasattr(x, "_mpc_"): + v, im = x._mpc_ + if im != fzero: + return x, 'C' + elif hasattr(x, "_mpf_"): + v = x._mpf_ + else: + if type(x) in int_types: + return int(x), 'Z' + p = None + if isinstance(x, tuple): + p, q = x + elif isinstance(x, basestring) and '/' in x: + p, q = x.split('/') + p = int(p) + q = int(q) + if p is not None: + if not p % q: + return p // q, 'Z' + return ctx.mpq((p,q)), 'Q' + x = ctx.convert(x) + if hasattr(x, "_mpc_"): + v, im = x._mpc_ + if im != fzero: + return x, 'C' + elif hasattr(x, "_mpf_"): + v = x._mpf_ + else: + return x, 'U' + sign, man, exp, bc = v + if man: + if exp >= -4: + if sign: + man = -man + if exp >= 0: + return int(man) << exp, 'Z' + if exp >= -4: + p, q = int(man), (1<<(-exp)) + return ctx.mpq((p,q)), 'Q' + x = ctx.make_mpf(v) + return x, 'R' + elif not exp: + return 0, 'Z' + else: + return x, 'U' + + def _mpf_mag(ctx, x): + sign, man, exp, bc = x + if man: + return exp+bc + if x == fzero: + return ctx.ninf + if x == finf or x == fninf: + return ctx.inf + return ctx.nan + + def mag(ctx, x): + """ + Quick logarithmic magnitude estimate of a number. + Returns an integer or infinity `m` such that `|x| <= 2^m`. + It is not guaranteed that `m` is an optimal bound, + but it will never be off by more than 2 (and probably not + more than 1). + """ + if hasattr(x, "_mpf_"): + return ctx._mpf_mag(x._mpf_) + elif hasattr(x, "_mpc_"): + r, i = x._mpc_ + if r == fzero: + return ctx._mpf_mag(i) + if i == fzero: + return ctx._mpf_mag(r) + return 1+max(ctx._mpf_mag(r), ctx._mpf_mag(i)) + elif isinstance(x, int_types): + if x: + return bitcount(abs(x)) + return ctx.ninf + elif isinstance(x, rational.mpq): + p, q = x + if p: + return 1 + bitcount(abs(p)) - bitcount(abs(q)) + return ctx.ninf + else: + x = ctx.convert(x) + if hasattr(x, "_mpf_") or hasattr(x, "_mpc_"): + return ctx.mag(x) + else: + raise TypeError("requires an mpf/mpc") + diff --git a/compiler/gdsMill/mpmath/function_docs.py b/compiler/gdsMill/mpmath/function_docs.py new file mode 100644 index 00000000..1208abb3 --- /dev/null +++ b/compiler/gdsMill/mpmath/function_docs.py @@ -0,0 +1,8601 @@ +""" +Extended docstrings for functions.py +""" + + +pi = r""" +`\pi`, roughly equal to 3.141592654, represents the area of the unit +circle, the half-period of trigonometric functions, and many other +things in mathematics. + +Mpmath can evaluate `\pi` to arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +pi + 3.1415926535897932384626433832795028841971693993751 + +This shows digits 99991-100000 of `\pi`:: + + >>> mp.dps = 100000 + >>> str(pi)[-10:] + '5549362464' + +**Possible issues** + +:data:`pi` always rounds to the nearest floating-point +number when used. This means that exact mathematical identities +involving `\pi` will generally not be preserved in floating-point +arithmetic. In particular, multiples of :data:`pi` (except for +the trivial case ``0*pi``) are *not* the exact roots of +:func:`sin`, but differ roughly by the current epsilon:: + + >>> mp.dps = 15 + >>> sin(pi) + 1.22464679914735e-16 + +One solution is to use the :func:`sinpi` function instead:: + + >>> sinpi(1) + 0.0 + +See the documentation of trigonometric functions for additional +details. +""" + +degree = r""" +Represents one degree of angle, `1^{\circ} = \pi/180`, or +about 0.01745329. This constant may be evaluated to arbitrary +precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +degree + 0.017453292519943295769236907684886127134428718885417 + +The :data:`degree` object is convenient for conversion +to radians:: + + >>> sin(30 * degree) + 0.5 + >>> asin(0.5) / degree + 30.0 +""" + +e = r""" +The transcendental number `e` = 2.718281828... is the base of the +natural logarithm (:func:`ln`) and of the exponential function +(:func:`exp`). + +Mpmath can be evaluate `e` to arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +e + 2.7182818284590452353602874713526624977572470937 + +This shows digits 99991-100000 of `e`:: + + >>> mp.dps = 100000 + >>> str(e)[-10:] + '2100427165' + +**Possible issues** + +:data:`e` always rounds to the nearest floating-point number +when used, and mathematical identities involving `e` may not +hold in floating-point arithmetic. For example, ``ln(e)`` +might not evaluate exactly to 1. + +In particular, don't use ``e**x`` to compute the exponential +function. Use ``exp(x)`` instead; this is both faster and more +accurate. +""" + +phi = r""" +Represents the golden ratio `\phi = (1+\sqrt 5)/2`, +approximately equal to 1.6180339887. To high precision, +its value is:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +phi + 1.6180339887498948482045868343656381177203091798058 + +Formulas for the golden ratio include the following:: + + >>> (1+sqrt(5))/2 + 1.6180339887498948482045868343656381177203091798058 + >>> findroot(lambda x: x**2-x-1, 1) + 1.6180339887498948482045868343656381177203091798058 + >>> limit(lambda n: fib(n+1)/fib(n), inf) + 1.6180339887498948482045868343656381177203091798058 +""" + +euler = r""" +Euler's constant or the Euler-Mascheroni constant `\gamma` += 0.57721566... is a number of central importance to +number theory and special functions. It is defined as the limit + +.. math :: + + \gamma = \lim_{n\to\infty} H_n - \log n + +where `H_n = 1 + \frac{1}{2} + \ldots + \frac{1}{n}` is a harmonic +number (see :func:`harmonic`). + +Evaluation of `\gamma` is supported at arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +euler + 0.57721566490153286060651209008240243104215933593992 + +We can also compute `\gamma` directly from the definition, +although this is less efficient:: + + >>> limit(lambda n: harmonic(n)-log(n), inf) + 0.57721566490153286060651209008240243104215933593992 + +This shows digits 9991-10000 of `\gamma`:: + + >>> mp.dps = 10000 + >>> str(euler)[-10:] + '4679858165' + +Integrals, series, and representations for `\gamma` in terms of +special functions include the following (there are many others):: + + >>> mp.dps = 25 + >>> -quad(lambda x: exp(-x)*log(x), [0,inf]) + 0.5772156649015328606065121 + >>> quad(lambda x,y: (x-1)/(1-x*y)/log(x*y), [0,1], [0,1]) + 0.5772156649015328606065121 + >>> nsum(lambda k: 1/k-log(1+1/k), [1,inf]) + 0.5772156649015328606065121 + >>> nsum(lambda k: (-1)**k*zeta(k)/k, [2,inf]) + 0.5772156649015328606065121 + >>> -diff(gamma, 1) + 0.5772156649015328606065121 + >>> limit(lambda x: 1/x-gamma(x), 0) + 0.5772156649015328606065121 + >>> limit(lambda x: zeta(x)-1/(x-1), 1) + 0.5772156649015328606065121 + >>> (log(2*pi*nprod(lambda n: + ... exp(-2+2/n)*(1+2/n)**n, [1,inf]))-3)/2 + 0.5772156649015328606065121 + +For generalizations of the identities `\gamma = -\Gamma'(1)` +and `\gamma = \lim_{x\to1} \zeta(x)-1/(x-1)`, see +:func:`psi` and :func:`stieltjes` respectively. +""" + +catalan = r""" +Catalan's constant `K` = 0.91596559... is given by the infinite +series + +.. math :: + + K = \sum_{k=0}^{\infty} \frac{(-1)^k}{(2k+1)^2}. + +Mpmath can evaluate it to arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +catalan + 0.91596559417721901505460351493238411077414937428167 + +One can also compute `K` directly from the definition, although +this is significantly less efficient:: + + >>> nsum(lambda k: (-1)**k/(2*k+1)**2, [0, inf]) + 0.91596559417721901505460351493238411077414937428167 + +This shows digits 9991-10000 of `K`:: + + >>> mp.dps = 10000 + >>> str(catalan)[-10:] + '9537871503' + +Catalan's constant has numerous integral representations:: + + >>> mp.dps = 50 + >>> quad(lambda x: -log(x)/(1+x**2), [0, 1]) + 0.91596559417721901505460351493238411077414937428167 + >>> quad(lambda x: atan(x)/x, [0, 1]) + 0.91596559417721901505460351493238411077414937428167 + >>> quad(lambda x: ellipk(x**2)/2, [0, 1]) + 0.91596559417721901505460351493238411077414937428167 + >>> quad(lambda x,y: 1/(1+(x*y)**2), [0, 1], [0, 1]) + 0.91596559417721901505460351493238411077414937428167 + +As well as series representations:: + + >>> pi*log(sqrt(3)+2)/8 + 3*nsum(lambda n: + ... (fac(n)/(2*n+1))**2/fac(2*n), [0, inf])/8 + 0.91596559417721901505460351493238411077414937428167 + >>> 1-nsum(lambda n: n*zeta(2*n+1)/16**n, [1,inf]) + 0.91596559417721901505460351493238411077414937428167 +""" + +khinchin = r""" +Khinchin's constant `K` = 2.68542... is a number that +appears in the theory of continued fractions. Mpmath can evaluate +it to arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +khinchin + 2.6854520010653064453097148354817956938203822939945 + +An integral representation is:: + + >>> I = quad(lambda x: log((1-x**2)/sincpi(x))/x/(1+x), [0, 1]) + >>> 2*exp(1/log(2)*I) + 2.6854520010653064453097148354817956938203822939945 + +The computation of ``khinchin`` is based on an efficient +implementation of the following series:: + + >>> f = lambda n: (zeta(2*n)-1)/n*sum((-1)**(k+1)/mpf(k) + ... for k in range(1,2*n)) + >>> exp(nsum(f, [1,inf])/log(2)) + 2.6854520010653064453097148354817956938203822939945 +""" + +glaisher = r""" +Glaisher's constant `A`, also known as the Glaisher-Kinkelin +constant, is a number approximately equal to 1.282427129 that +sometimes appears in formulas related to gamma and zeta functions. +It is also related to the Barnes G-function (see :func:`barnesg`). + +The constant is defined as `A = \exp(1/12-\zeta'(-1))` where +`\zeta'(s)` denotes the derivative of the Riemann zeta function +(see :func:`zeta`). + +Mpmath can evaluate Glaisher's constant to arbitrary precision: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +glaisher + 1.282427129100622636875342568869791727767688927325 + +We can verify that the value computed by :data:`glaisher` is +correct using mpmath's facilities for numerical +differentiation and arbitrary evaluation of the zeta function: + + >>> exp(mpf(1)/12 - diff(zeta, -1)) + 1.282427129100622636875342568869791727767688927325 + +Here is an example of an integral that can be evaluated in +terms of Glaisher's constant: + + >>> mp.dps = 15 + >>> quad(lambda x: log(gamma(x)), [1, 1.5]) + -0.0428537406502909 + >>> -0.5 - 7*log(2)/24 + log(pi)/4 + 3*log(glaisher)/2 + -0.042853740650291 + +Mpmath computes Glaisher's constant by applying Euler-Maclaurin +summation to a slowly convergent series. The implementation is +reasonably efficient up to about 10,000 digits. See the source +code for additional details. + +References: +http://mathworld.wolfram.com/Glaisher-KinkelinConstant.html +""" + +apery = r""" +Represents Apery's constant, which is the irrational number +approximately equal to 1.2020569 given by + +.. math :: + + \zeta(3) = \sum_{k=1}^\infty\frac{1}{k^3}. + +The calculation is based on an efficient hypergeometric +series. To 50 decimal places, the value is given by:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +apery + 1.2020569031595942853997381615114499907649862923405 + +Other ways to evaluate Apery's constant using mpmath +include:: + + >>> zeta(3) + 1.2020569031595942853997381615114499907649862923405 + >>> -psi(2,1)/2 + 1.2020569031595942853997381615114499907649862923405 + >>> 8*nsum(lambda k: 1/(2*k+1)**3, [0,inf])/7 + 1.2020569031595942853997381615114499907649862923405 + >>> f = lambda k: 2/k**3/(exp(2*pi*k)-1) + >>> 7*pi**3/180 - nsum(f, [1,inf]) + 1.2020569031595942853997381615114499907649862923405 + +This shows digits 9991-10000 of Apery's constant:: + + >>> mp.dps = 10000 + >>> str(apery)[-10:] + '3189504235' +""" + +mertens = r""" +Represents the Mertens or Meissel-Mertens constant, which is the +prime number analog of Euler's constant: + +.. math :: + + B_1 = \lim_{N\to\infty} + \left(\sum_{p_k \le N} \frac{1}{p_k} - \log \log N \right) + +Here `p_k` denotes the `k`-th prime number. Other names for this +constant include the Hadamard-de la Vallee-Poussin constant or +the prime reciprocal constant. + +The following gives the Mertens constant to 50 digits:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +mertens + 0.2614972128476427837554268386086958590515666482612 + +References: +http://mathworld.wolfram.com/MertensConstant.html +""" + +twinprime = r""" +Represents the twin prime constant, which is the factor `C_2` +featuring in the Hardy-Littlewood conjecture for the growth of the +twin prime counting function, + +.. math :: + + \pi_2(n) \sim 2 C_2 \frac{n}{\log^2 n}. + +It is given by the product over primes + +.. math :: + + C_2 = \prod_{p\ge3} \frac{p(p-2)}{(p-1)^2} \approx 0.66016 + +Computing `C_2` to 50 digits:: + + >>> from mpmath import * + >>> mp.dps = 50; mp.pretty = True + >>> +twinprime + 0.66016181584686957392781211001455577843262336028473 + +References: +http://mathworld.wolfram.com/TwinPrimesConstant.html +""" + +ln = r""" +Computes the natural logarithm of `x`, `\ln x`. +See :func:`log` for additional documentation.""" + +sqrt = r""" +``sqrt(x)`` gives the principal square root of `x`, `\sqrt x`. +For positive real numbers, the principal root is simply the +positive square root. For arbitrary complex numbers, the principal +square root is defined to satisfy `\sqrt x = \exp(\log(x)/2)`. +The function thus has a branch cut along the negative half real axis. + +For all mpmath numbers ``x``, calling ``sqrt(x)`` is equivalent to +performing ``x**0.5``. + +**Examples** + +Basic examples and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> sqrt(10) + 3.16227766016838 + >>> sqrt(100) + 10.0 + >>> sqrt(-4) + (0.0 + 2.0j) + >>> sqrt(1+1j) + (1.09868411346781 + 0.455089860562227j) + >>> sqrt(inf) + +inf + +Square root evaluation is fast at huge precision:: + + >>> mp.dps = 50000 + >>> a = sqrt(3) + >>> str(a)[-10:] + '9329332814' + +:func:`sqrt` supports interval arguments:: + + >>> mp.dps = 15 + >>> sqrt(mpi(16, 100)) + [4.0, 10.0] + >>> sqrt(mpi(2)) + [1.4142135623730949234, 1.4142135623730951455] + >>> sqrt(mpi(2)) ** 2 + [1.9999999999999995559, 2.0000000000000004441] + +""" + +cbrt = r""" +``cbrt(x)`` computes the cube root of `x`, `x^{1/3}`. This +function is faster and more accurate than raising to a floating-point +fraction:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> 125**(mpf(1)/3) + mpf('4.9999999999999991') + >>> cbrt(125) + mpf('5.0') + +Every nonzero complex number has three cube roots. This function +returns the cube root defined by `\exp(\log(x)/3)` where the +principal branch of the natural logarithm is used. Note that this +does not give a real cube root for negative real numbers:: + + >>> mp.pretty = True + >>> cbrt(-1) + (0.5 + 0.866025403784439j) +""" + +exp = r""" +Computes the exponential function, + +.. math :: + + \exp(x) = e^x = \sum_{k=0}^{\infty} \frac{x^k}{k!}. + +For complex numbers, the exponential function also satisfies + +.. math :: + + \exp(x+yi) = e^x (\cos y + i \sin y). + +**Basic examples** + +Some values of the exponential function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> exp(0) + 1.0 + >>> exp(1) + 2.718281828459045235360287 + >>> exp(-1) + 0.3678794411714423215955238 + >>> exp(inf) + +inf + >>> exp(-inf) + 0.0 + +Arguments can be arbitrarily large:: + + >>> exp(10000) + 8.806818225662921587261496e+4342 + >>> exp(-10000) + 1.135483865314736098540939e-4343 + +Evaluation is supported for interval arguments:: + + >>> exp(mpi(-inf,0)) + [0.0, 1.0] + >>> exp(mpi(0,1)) + [1.0, 2.71828182845904523536028749558] + +The exponential function can be evaluated efficiently to arbitrary +precision:: + + >>> mp.dps = 10000 + >>> exp(pi) #doctest: +ELLIPSIS + 23.140692632779269005729...8984304016040616 + +**Functional properties** + +Numerical verification of Euler's identity for the complex +exponential function:: + + >>> mp.dps = 15 + >>> exp(j*pi)+1 + (0.0 + 1.22464679914735e-16j) + >>> chop(exp(j*pi)+1) + 0.0 + +This recovers the coefficients (reciprocal factorials) in the +Maclaurin series expansion of exp:: + + >>> nprint(taylor(exp, 0, 5)) + [1.0, 1.0, 0.5, 0.166667, 0.0416667, 0.00833333] + +The exponential function is its own derivative and antiderivative:: + + >>> exp(pi) + 23.1406926327793 + >>> diff(exp, pi) + 23.1406926327793 + >>> quad(exp, [-inf, pi]) + 23.1406926327793 + +The exponential function can be evaluated using various methods, +including direct summation of the series, limits, and solving +the defining differential equation:: + + >>> nsum(lambda k: pi**k/fac(k), [0,inf]) + 23.1406926327793 + >>> limit(lambda k: (1+pi/k)**k, inf) + 23.1406926327793 + >>> odefun(lambda t, x: x, 0, 1)(pi) + 23.1406926327793 +""" + +cosh = r""" +Computes the hyperbolic cosine of `x`, +`\cosh(x) = (e^x + e^{-x})/2`. Values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> cosh(0) + 1.0 + >>> cosh(1) + 1.543080634815243778477906 + >>> cosh(-inf), cosh(+inf) + (+inf, +inf) + +The hyperbolic cosine is an even, convex function with +a global minimum at `x = 0`, having a Maclaurin series +that starts:: + + >>> nprint(chop(taylor(cosh, 0, 5))) + [1.0, 0.0, 0.5, 0.0, 0.0416667, 0.0] + +Generalized to complex numbers, the hyperbolic cosine is +equivalent to a cosine with the argument rotated +in the imaginary direction, or `\cosh x = \cos ix`:: + + >>> cosh(2+3j) + (-3.724545504915322565473971 + 0.5118225699873846088344638j) + >>> cos(3-2j) + (-3.724545504915322565473971 + 0.5118225699873846088344638j) +""" + +sinh = r""" +Computes the hyperbolic sine of `x`, +`\sinh(x) = (e^x - e^{-x})/2`. Values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sinh(0) + 0.0 + >>> sinh(1) + 1.175201193643801456882382 + >>> sinh(-inf), sinh(+inf) + (-inf, +inf) + +The hyperbolic sine is an odd function, with a Maclaurin +series that starts:: + + >>> nprint(chop(taylor(sinh, 0, 5))) + [0.0, 1.0, 0.0, 0.166667, 0.0, 0.00833333] + +Generalized to complex numbers, the hyperbolic sine is +essentially a sine with a rotation `i` applied to +the argument; more precisely, `\sinh x = -i \sin ix`:: + + >>> sinh(2+3j) + (-3.590564589985779952012565 + 0.5309210862485198052670401j) + >>> j*sin(3-2j) + (-3.590564589985779952012565 + 0.5309210862485198052670401j) +""" + +tanh = r""" +Computes the hyperbolic tangent of `x`, +`\tanh(x) = \sinh(x)/\cosh(x)`. Values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> tanh(0) + 0.0 + >>> tanh(1) + 0.7615941559557648881194583 + >>> tanh(-inf), tanh(inf) + (-1.0, 1.0) + +The hyperbolic tangent is an odd, sigmoidal function, similar +to the inverse tangent and error function. Its Maclaurin +series is:: + + >>> nprint(chop(taylor(tanh, 0, 5))) + [0.0, 1.0, 0.0, -0.333333, 0.0, 0.133333] + +Generalized to complex numbers, the hyperbolic tangent is +essentially a tangent with a rotation `i` applied to +the argument; more precisely, `\tanh x = -i \tan ix`:: + + >>> tanh(2+3j) + (0.9653858790221331242784803 - 0.009884375038322493720314034j) + >>> j*tan(3-2j) + (0.9653858790221331242784803 - 0.009884375038322493720314034j) +""" + +cos = r""" +Computes the cosine of `x`, `\cos(x)`. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> cos(pi/3) + 0.5 + >>> cos(100000001) + -0.9802850113244713353133243 + >>> cos(2+3j) + (-4.189625690968807230132555 - 9.109227893755336597979197j) + >>> cos(inf) + nan + >>> nprint(chop(taylor(cos, 0, 6))) + [1.0, 0.0, -0.5, 0.0, 0.0416667, 0.0, -0.00138889] + >>> cos(mpi(0,1)) + [0.540302305868139717400936602301, 1.0] + >>> cos(mpi(0,2)) + [-0.41614683654714238699756823214, 1.0] +""" + +sin = r""" +Computes the sine of `x`, `\sin(x)`. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sin(pi/3) + 0.8660254037844386467637232 + >>> sin(100000001) + 0.1975887055794968911438743 + >>> sin(2+3j) + (9.1544991469114295734673 - 4.168906959966564350754813j) + >>> sin(inf) + nan + >>> nprint(chop(taylor(sin, 0, 6))) + [0.0, 1.0, 0.0, -0.166667, 0.0, 0.00833333, 0.0] + >>> sin(mpi(0,1)) + [0.0, 0.841470984807896506652502331201] + >>> sin(mpi(0,2)) + [0.0, 1.0] +""" + +tan = r""" +Computes the tangent of `x`, `\tan(x) = \frac{\sin(x)}{\cos(x)}`. +The tangent function is singular at `x = (n+1/2)\pi`, but +``tan(x)`` always returns a finite result since `(n+1/2)\pi` +cannot be represented exactly using floating-point arithmetic. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> tan(pi/3) + 1.732050807568877293527446 + >>> tan(100000001) + -0.2015625081449864533091058 + >>> tan(2+3j) + (-0.003764025641504248292751221 + 1.003238627353609801446359j) + >>> tan(inf) + nan + >>> nprint(chop(taylor(tan, 0, 6))) + [0.0, 1.0, 0.0, 0.333333, 0.0, 0.133333, 0.0] + >>> tan(mpi(0,1)) + [0.0, 1.55740772465490223050697482944] + >>> tan(mpi(0,2)) # Interval includes a singularity + [-inf, +inf] +""" + +sec = r""" +Computes the secant of `x`, `\mathrm{sec}(x) = \frac{1}{\cos(x)}`. +The secant function is singular at `x = (n+1/2)\pi`, but +``sec(x)`` always returns a finite result since `(n+1/2)\pi` +cannot be represented exactly using floating-point arithmetic. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> sec(pi/3) + 2.0 + >>> sec(10000001) + -1.184723164360392819100265 + >>> sec(2+3j) + (-0.04167496441114427004834991 + 0.0906111371962375965296612j) + >>> sec(inf) + nan + >>> nprint(chop(taylor(sec, 0, 6))) + [1.0, 0.0, 0.5, 0.0, 0.208333, 0.0, 0.0847222] + >>> sec(mpi(0,1)) + [1.0, 1.85081571768092561791175326276] + >>> sec(mpi(0,2)) # Interval includes a singularity + [-inf, +inf] +""" + +csc = r""" +Computes the cosecant of `x`, `\mathrm{csc}(x) = \frac{1}{\sin(x)}`. +This cosecant function is singular at `x = n \pi`, but with the +exception of the point `x = 0`, ``csc(x)`` returns a finite result +since `n \pi` cannot be represented exactly using floating-point +arithmetic. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> csc(pi/3) + 1.154700538379251529018298 + >>> csc(10000001) + -1.864910497503629858938891 + >>> csc(2+3j) + (0.09047320975320743980579048 + 0.04120098628857412646300981j) + >>> csc(inf) + nan + >>> csc(mpi(0,1)) # Interval includes a singularity + [1.18839510577812121626159943988, +inf] + >>> csc(mpi(0,2)) + [1.0, +inf] +""" + +cot = r""" +Computes the cotangent of `x`, +`\mathrm{cot}(x) = \frac{1}{\tan(x)} = \frac{\cos(x)}{\sin(x)}`. +This cotangent function is singular at `x = n \pi`, but with the +exception of the point `x = 0`, ``cot(x)`` returns a finite result +since `n \pi` cannot be represented exactly using floating-point +arithmetic. + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> cot(pi/3) + 0.5773502691896257645091488 + >>> cot(10000001) + 1.574131876209625656003562 + >>> cot(2+3j) + (-0.003739710376336956660117409 - 0.9967577965693583104609688j) + >>> cot(inf) + nan + >>> cot(mpi(0,1)) # Interval includes a singularity + [0.642092615934330703006419974862, +inf] + >>> cot(mpi(1,2)) + [-inf, +inf] +""" + +acos = r""" +Computes the inverse cosine or arccosine of `x`, `\cos^{-1}(x)`. +Since `-1 \le \cos(x) \le 1` for real `x`, the inverse +cosine is real-valued only for `-1 \le x \le 1`. On this interval, +:func:`acos` is defined to be a monotonically decreasing +function assuming values between `+\pi` and `0`. + +Basic values are:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> acos(-1) + 3.141592653589793238462643 + >>> acos(0) + 1.570796326794896619231322 + >>> acos(1) + 0.0 + >>> nprint(chop(taylor(acos, 0, 6))) + [1.5708, -1.0, 0.0, -0.166667, 0.0, -0.075, 0.0] + +:func:`acos` is defined so as to be a proper inverse function of +`\cos(\theta)` for `0 \le \theta < \pi`. +We have `\cos(\cos^{-1}(x)) = x` for all `x`, but +`\cos^{-1}(\cos(x)) = x` only for `0 \le \Re[x] < \pi`:: + + >>> for x in [1, 10, -1, 2+3j, 10+3j]: + ... print cos(acos(x)), acos(cos(x)) + ... + 1.0 1.0 + (10.0 + 0.0j) 2.566370614359172953850574 + -1.0 1.0 + (2.0 + 3.0j) (2.0 + 3.0j) + (10.0 + 3.0j) (2.566370614359172953850574 - 3.0j) + +The inverse cosine has two branch points: `x = \pm 1`. :func:`acos` +places the branch cuts along the line segments `(-\infty, -1)` and +`(+1, +\infty)`. In general, + +.. math :: + + \cos^{-1}(x) = \frac{\pi}{2} + i \log\left(ix + \sqrt{1-x^2} \right) + +where the principal-branch log and square root are implied. +""" + +asin = r""" +Computes the inverse sine or arcsine of `x`, `\sin^{-1}(x)`. +Since `-1 \le \sin(x) \le 1` for real `x`, the inverse +sine is real-valued only for `-1 \le x \le 1`. +On this interval, it is defined to be a monotonically increasing +function assuming values between `-\pi/2` and `\pi/2`. + +Basic values are:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> asin(-1) + -1.570796326794896619231322 + >>> asin(0) + 0.0 + >>> asin(1) + 1.570796326794896619231322 + >>> nprint(chop(taylor(asin, 0, 6))) + [0.0, 1.0, 0.0, 0.166667, 0.0, 0.075, 0.0] + +:func:`asin` is defined so as to be a proper inverse function of +`\sin(\theta)` for `-\pi/2 < \theta < \pi/2`. +We have `\sin(\sin^{-1}(x)) = x` for all `x`, but +`\sin^{-1}(\sin(x)) = x` only for `-\pi/2 < \Re[x] < \pi/2`:: + + >>> for x in [1, 10, -1, 1+3j, -2+3j]: + ... print chop(sin(asin(x))), asin(sin(x)) + ... + 1.0 1.0 + 10.0 -0.5752220392306202846120698 + -1.0 -1.0 + (1.0 + 3.0j) (1.0 + 3.0j) + (-2.0 + 3.0j) (-1.141592653589793238462643 - 3.0j) + +The inverse sine has two branch points: `x = \pm 1`. :func:`asin` +places the branch cuts along the line segments `(-\infty, -1)` and +`(+1, +\infty)`. In general, + +.. math :: + + \sin^{-1}(x) = -i \log\left(ix + \sqrt{1-x^2} \right) + +where the principal-branch log and square root are implied. +""" + +atan = r""" +Computes the inverse tangent or arctangent of `x`, `\tan^{-1}(x)`. +This is a real-valued function for all real `x`, with range +`(-\pi/2, \pi/2)`. + +Basic values are:: + + >>> from mpmath import * + >>> mp.dps = 25 + >>> atan(-inf); mp.pretty = True + -1.570796326794896619231322 + >>> atan(-1) + -0.7853981633974483096156609 + >>> atan(0) + 0.0 + >>> atan(1) + 0.7853981633974483096156609 + >>> atan(inf) + 1.570796326794896619231322 + >>> nprint(chop(taylor(atan, 0, 6))) + [0.0, 1.0, 0.0, -0.333333, 0.0, 0.2, 0.0] + +The inverse tangent is often used to compute angles. However, +the atan2 function is often better for this as it preserves sign +(see :func:`atan2`). + +:func:`atan` is defined so as to be a proper inverse function of +`\tan(\theta)` for `-\pi/2 < \theta < \pi/2`. +We have `\tan(\tan^{-1}(x)) = x` for all `x`, but +`\tan^{-1}(\tan(x)) = x` only for `-\pi/2 < \Re[x] < \pi/2`:: + + >>> mp.dps = 25 + >>> for x in [1, 10, -1, 1+3j, -2+3j]: + ... print tan(atan(x)), atan(tan(x)) + ... + 1.0 1.0 + 10.0 0.5752220392306202846120698 + -1.0 -1.0 + (1.0 + 3.0j) (1.000000000000000000000001 + 3.0j) + (-2.0 + 3.0j) (1.141592653589793238462644 + 3.0j) + +The inverse tangent has two branch points: `x = \pm i`. :func:`atan` +places the branch cuts along the line segments `(-i \infty, -i)` and +`(+i, +i \infty)`. In general, + +.. math :: + + \tan^{-1}(x) = \frac{i}{2}\left(\log(1-ix)-\log(1+ix)\right) + +where the principal-branch log is implied. +""" + +acot = r"""Computes the inverse cotangent of `x`, +`\mathrm{cot}^{-1}(x) = \tan^{-1}(1/x)`.""" + +asec = r"""Computes the inverse secant of `x`, +`\mathrm{sec}^{-1}(x) = \cos^{-1}(1/x)`.""" + +acsc = r"""Computes the inverse cosecant of `x`, +`\mathrm{csc}^{-1}(x) = \sin^{-1}(1/x)`.""" + +coth = r"""Computes the hyperbolic cotangent of `x`, +`\mathrm{coth}(x) = \frac{\cosh(x)}{\sinh(x)}`. +""" + +sech = r"""Computes the hyperbolic secant of `x`, +`\mathrm{sech}(x) = \frac{1}{\cosh(x)}`. +""" + +csch = r"""Computes the hyperbolic cosecant of `x`, +`\mathrm{csch}(x) = \frac{1}{\sinh(x)}`. +""" + +acosh = r"""Computes the inverse hyperbolic cosine of `x`, +`\mathrm{cosh}^{-1}(x) = \log(x+\sqrt{x+1}\sqrt{x-1})`. +""" + +asinh = r"""Computes the inverse hyperbolic sine of `x`, +`\mathrm{sinh}^{-1}(x) = \log(x+\sqrt{1+x^2})`. +""" + +atanh = r"""Computes the inverse hyperbolic tangent of `x`, +`\mathrm{tanh}^{-1}(x) = \frac{1}{2}\left(\log(1+x)-\log(1-x)\right)`. +""" + +acoth = r"""Computes the inverse hyperbolic cotangent of `x`, +`\mathrm{coth}^{-1}(x) = \tanh^{-1}(1/x)`.""" + +asech = r"""Computes the inverse hyperbolic secant of `x`, +`\mathrm{sech}^{-1}(x) = \cosh^{-1}(1/x)`.""" + +acsch = r"""Computes the inverse hyperbolic cosecant of `x`, +`\mathrm{csch}^{-1}(x) = \sinh^{-1}(1/x)`.""" + + + +sinpi = r""" +Computes `\sin(\pi x)`, more accurately than the expression +``sin(pi*x)``:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> sinpi(10**10), sin(pi*(10**10)) + (0.0, -2.23936276195592e-6) + >>> sinpi(10**10+0.5), sin(pi*(10**10+0.5)) + (1.0, 0.999999999998721) +""" + +cospi = r""" +Computes `\cos(\pi x)`, more accurately than the expression +``cos(pi*x)``:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> cospi(10**10), cos(pi*(10**10)) + (1.0, 0.999999999997493) + >>> cospi(10**10+0.5), cos(pi*(10**10+0.5)) + (0.0, 1.59960492420134e-6) +""" + +sinc = r""" +``sinc(x)`` computes the unnormalized sinc function, defined as + +.. math :: + + \mathrm{sinc}(x) = \begin{cases} + \sin(x)/x, & \mbox{if } x \ne 0 \\ + 1, & \mbox{if } x = 0. + \end{cases} + +See :func:`sincpi` for the normalized sinc function. + +Simple values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> sinc(0) + 1.0 + >>> sinc(1) + 0.841470984807897 + >>> sinc(inf) + 0.0 + +The integral of the sinc function is the sine integral Si:: + + >>> quad(sinc, [0, 1]) + 0.946083070367183 + >>> si(1) + 0.946083070367183 +""" + +sincpi = r""" +``sincpi(x)`` computes the normalized sinc function, defined as + +.. math :: + + \mathrm{sinc}_{\pi}(x) = \begin{cases} + \sin(\pi x)/(\pi x), & \mbox{if } x \ne 0 \\ + 1, & \mbox{if } x = 0. + \end{cases} + +Equivalently, we have +`\mathrm{sinc}_{\pi}(x) = \mathrm{sinc}(\pi x)`. + +The normalization entails that the function integrates +to unity over the entire real line:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> quadosc(sincpi, [-inf, inf], period=2.0) + 1.0 + +Like, :func:`sinpi`, :func:`sincpi` is evaluated accurately +at its roots:: + + >>> sincpi(10) + 0.0 +""" + +expj = r""" +Convenience function for computing `e^{ix}`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> expj(0) + (1.0 + 0.0j) + >>> expj(-1) + (0.5403023058681397174009366 - 0.8414709848078965066525023j) + >>> expj(j) + (0.3678794411714423215955238 + 0.0j) + >>> expj(1+j) + (0.1987661103464129406288032 + 0.3095598756531121984439128j) +""" + +expjpi = r""" +Convenience function for computing `e^{i \pi x}`. +Evaluation is accurate near zeros (see also :func:`cospi`, +:func:`sinpi`):: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> expjpi(0) + (1.0 + 0.0j) + >>> expjpi(1) + (-1.0 + 0.0j) + >>> expjpi(0.5) + (0.0 + 1.0j) + >>> expjpi(-1) + (-1.0 + 0.0j) + >>> expjpi(j) + (0.04321391826377224977441774 + 0.0j) + >>> expjpi(1+j) + (-0.04321391826377224977441774 + 0.0j) +""" + +floor = r""" +Computes the floor of `x`, `\lfloor x \rfloor`, defined as +the largest integer less than or equal to `x`:: + + >>> from mpmath import * + >>> mp.pretty = False + >>> floor(3.5) + mpf('3.0') + +Note: :func:`floor` returns a floating-point number, not a +Python ``int``. If `\lfloor x \rfloor` is too large to be +represented exactly at the present working precision, the +result will be rounded, not necessarily in the floor +direction.""" + +ceil = r""" +Computes the ceiling of `x`, `\lceil x \rceil`, defined as +the smallest integer greater than or equal to `x`:: + + >>> from mpmath import * + >>> mp.pretty = False + >>> ceil(3.5) + mpf('4.0') + +Note: :func:`ceil` returns a floating-point number, not a +Python ``int``. If `\lceil x \rceil` is too large to be +represented exactly at the present working precision, the +result will be rounded, not necessarily in the ceiling +direction.""" + +sign = r""" +Returns the sign of `x`, defined as `\mathrm{sign}(x) = x / |x|` +(with the special case `\mathrm{sign}(0) = 0`):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> sign(10) + mpf('1.0') + >>> sign(-10) + mpf('-1.0') + >>> sign(0) + mpf('0.0') + +Note that the sign function is also defined for complex numbers, +for which it gives the projection onto the unit circle:: + + >>> mp.dps = 15; mp.pretty = True + >>> sign(1+j) + (0.707106781186547 + 0.707106781186547j) + +""" + +arg = r""" +Computes the complex argument (phase) of `x`, defined as the +signed angle between the positive real axis and `x` in the +complex plane:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> arg(3) + 0.0 + >>> arg(3+3j) + 0.785398163397448 + >>> arg(3j) + 1.5707963267949 + >>> arg(-3) + 3.14159265358979 + >>> arg(-3j) + -1.5707963267949 + +The angle is defined to satisfy `-\pi < \arg(x) \le \pi` and +with the sign convention that a nonnegative imaginary part +results in a nonnegative argument. + +The value returned by :func:`arg` is an ``mpf`` instance. +""" + +fabs = r""" +Returns the absolute value of `x`, `|x|`. Unlike :func:`abs`, +:func:`fabs` converts non-mpmath numbers (such as ``int``) +into mpmath numbers:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> fabs(3) + mpf('3.0') + >>> fabs(-3) + mpf('3.0') + >>> fabs(3+4j) + mpf('5.0') +""" + +re = r""" +Returns the real part of `x`, `\Re(x)`. Unlike ``x.real``, +:func:`re` converts `x` to a mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> re(3) + mpf('3.0') + >>> re(-1+4j) + mpf('-1.0') +""" + +im = r""" +Returns the imaginary part of `x`, `\Im(x)`. Unlike ``x.imag``, +:func:`im` converts `x` to a mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> im(3) + mpf('0.0') + >>> im(-1+4j) + mpf('4.0') +""" + +conj = r""" +Returns the complex conjugate of `x`, `\overline{x}`. Unlike +``x.conjugate()``, :func:`im` converts `x` to a mpmath number:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> conj(3) + mpf('3.0') + >>> conj(-1+4j) + mpc(real='-1.0', imag='-4.0') +""" + +polar = r""" +Returns the polar representation of the complex number `z` +as a pair `(r, \phi)` such that `z = r e^{i \phi}`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> polar(-2) + (2.0, 3.14159265358979) + >>> polar(3-4j) + (5.0, -0.927295218001612) +""" + +rect = r""" +Returns the complex number represented by polar +coordinates `(r, \phi)`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> chop(rect(2, pi)) + -2.0 + >>> rect(sqrt(2), -pi/4) + (1.0 - 1.0j) +""" + +expm1 = r""" +Computes `e^x - 1`, accurately for small `x`. + +Unlike the expression ``exp(x) - 1``, ``expm1(x)`` does not suffer from +potentially catastrophic cancellation:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> exp(1e-10)-1; print expm1(1e-10) + 1.00000008274037e-10 + 1.00000000005e-10 + >>> exp(1e-20)-1; print expm1(1e-20) + 0.0 + 1.0e-20 + >>> 1/(exp(1e-20)-1) + Traceback (most recent call last): + ... + ZeroDivisionError + >>> 1/expm1(1e-20) + 1.0e+20 + +Evaluation works for extremely tiny values:: + + >>> expm1(0) + 0.0 + >>> expm1('1e-10000000') + 1.0e-10000000 + +""" + +powm1 = r""" +Computes `x^y - 1`, accurately when `x^y` is very close to 1. + +This avoids potentially catastrophic cancellation:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> power(0.99999995, 1e-10) - 1 + 0.0 + >>> powm1(0.99999995, 1e-10) + -5.00000012791934e-18 + +Powers exactly equal to 1, and only those powers, yield 0 exactly:: + + >>> powm1(-j, 4) + (0.0 + 0.0j) + >>> powm1(3, 0) + 0.0 + >>> powm1(fadd(-1, 1e-100, exact=True), 4) + -4.0e-100 + +Evaluation works for extremely tiny `y`:: + + >>> powm1(2, '1e-100000') + 6.93147180559945e-100001 + >>> powm1(j, '1e-1000') + (-1.23370055013617e-2000 + 1.5707963267949e-1000j) + +""" + +root = r""" +``root(z, n, k=0)`` computes an `n`-th root of `z`, i.e. returns a number +`r` that (up to possible approximation error) satisfies `r^n = z`. +(``nthroot`` is available as an alias for ``root``.) + +Every complex number `z \ne 0` has `n` distinct `n`-th roots, which are +equidistant points on a circle with radius `|z|^{1/n}`, centered around the +origin. A specific root may be selected using the optional index +`k`. The roots are indexed counterclockwise, starting with `k = 0` for the root +closest to the positive real half-axis. + +The `k = 0` root is the so-called principal `n`-th root, often denoted by +`\sqrt[n]{z}` or `z^{1/n}`, and also given by `\exp(\log(z) / n)`. If `z` is +a positive real number, the principal root is just the unique positive +`n`-th root of `z`. Under some circumstances, non-principal real roots exist: +for positive real `z`, `n` even, there is a negative root given by `k = n/2`; +for negative real `z`, `n` odd, there is a negative root given by `k = (n-1)/2`. + +To obtain all roots with a simple expression, use +``[root(z,n,k) for k in range(n)]``. + +An important special case, ``root(1, n, k)`` returns the `k`-th `n`-th root of +unity, `\zeta_k = e^{2 \pi i k / n}`. Alternatively, :func:`unitroots` +provides a slightly more convenient way to obtain the roots of unity, +including the option to compute only the primitive roots of unity. + +Both `k` and `n` should be integers; `k` outside of ``range(n)`` will be +reduced modulo `n`. If `n` is negative, `x^{-1/n} = 1/x^{1/n}` (or +the equivalent reciprocal for a non-principal root with `k \ne 0`) is computed. + +:func:`root` is implemented to use Newton's method for small +`n`. At high precision, this makes `x^{1/n}` not much more +expensive than the regular exponentiation, `x^n`. For very large +`n`, :func:`nthroot` falls back to use the exponential function. + +**Examples** + +:func:`nthroot`/:func:`root` is faster and more accurate than raising to a +floating-point fraction:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> 16807 ** (mpf(1)/5) + mpf('7.0000000000000009') + >>> root(16807, 5) + mpf('7.0') + >>> nthroot(16807, 5) # Alias + mpf('7.0') + +A high-precision root:: + + >>> mp.dps = 50; mp.pretty = True + >>> nthroot(10, 5) + 1.584893192461113485202101373391507013269442133825 + >>> nthroot(10, 5) ** 5 + 10.0 + +Computing principal and non-principal square and cube roots:: + + >>> mp.dps = 15 + >>> root(10, 2) + 3.16227766016838 + >>> root(10, 2, 1) + -3.16227766016838 + >>> root(-10, 3) + (1.07721734501594 + 1.86579517236206j) + >>> root(-10, 3, 1) + -2.15443469003188 + >>> root(-10, 3, 2) + (1.07721734501594 - 1.86579517236206j) + +All the 7th roots of a complex number:: + + >>> for r in [root(3+4j, 7, k) for k in range(7)]: + ... print r, r**7 + ... + (1.24747270589553 + 0.166227124177353j) (3.0 + 4.0j) + (0.647824911301003 + 1.07895435170559j) (3.0 + 4.0j) + (-0.439648254723098 + 1.17920694574172j) (3.0 + 4.0j) + (-1.19605731775069 + 0.391492658196305j) (3.0 + 4.0j) + (-1.05181082538903 - 0.691023585965793j) (3.0 + 4.0j) + (-0.115529328478668 - 1.25318497558335j) (3.0 + 4.0j) + (0.907748109144957 - 0.871672518271819j) (3.0 + 4.0j) + +Cube roots of unity:: + + >>> for k in range(3): print root(1, 3, k) + ... + 1.0 + (-0.5 + 0.866025403784439j) + (-0.5 - 0.866025403784439j) + +Some exact high order roots:: + + >>> root(75**210, 105) + 5625.0 + >>> root(1, 128, 96) + (0.0 - 1.0j) + >>> root(4**128, 128, 96) + (0.0 - 4.0j) + +""" + +unitroots = r""" +``unitroots(n)`` returns `\zeta_0, \zeta_1, \ldots, \zeta_{n-1}`, +all the distinct `n`-th roots of unity, as a list. If the option +*primitive=True* is passed, only the primitive roots are returned. + +Every `n`-th root of unity satisfies `(\zeta_k)^n = 1`. There are `n` distinct +roots for each `n` (`\zeta_k` and `\zeta_j` are the same when +`k = j \pmod n`), which form a regular polygon with vertices on the unit +circle. They are ordered counterclockwise with increasing `k`, starting +with `\zeta_0 = 1`. + +**Examples** + +The roots of unity up to `n = 4`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint(unitroots(1)) + [1.0] + >>> nprint(unitroots(2)) + [1.0, -1.0] + >>> nprint(unitroots(3)) + [1.0, (-0.5 + 0.866025j), (-0.5 - 0.866025j)] + >>> nprint(unitroots(4)) + [1.0, (0.0 + 1.0j), -1.0, (0.0 - 1.0j)] + +Roots of unity form a geometric series that sums to 0:: + + >>> mp.dps = 50 + >>> chop(fsum(unitroots(25))) + 0.0 + +Primitive roots up to `n = 4`:: + + >>> mp.dps = 15 + >>> nprint(unitroots(1, primitive=True)) + [1.0] + >>> nprint(unitroots(2, primitive=True)) + [-1.0] + >>> nprint(unitroots(3, primitive=True)) + [(-0.5 + 0.866025j), (-0.5 - 0.866025j)] + >>> nprint(unitroots(4, primitive=True)) + [(0.0 + 1.0j), (0.0 - 1.0j)] + +There are only four primitive 12th roots:: + + >>> nprint(unitroots(12, primitive=True)) + [(0.866025 + 0.5j), (-0.866025 + 0.5j), (-0.866025 - 0.5j), (0.866025 - 0.5j)] + +The `n`-th roots of unity form a group, the cyclic group of order `n`. +Any primitive root `r` is a generator for this group, meaning that +`r^0, r^1, \ldots, r^{n-1}` gives the whole set of unit roots (in +some permuted order):: + + >>> for r in unitroots(6): print r + ... + 1.0 + (0.5 + 0.866025403784439j) + (-0.5 + 0.866025403784439j) + -1.0 + (-0.5 - 0.866025403784439j) + (0.5 - 0.866025403784439j) + >>> r = unitroots(6, primitive=True)[1] + >>> for k in range(6): print chop(r**k) + ... + 1.0 + (0.5 - 0.866025403784439j) + (-0.5 - 0.866025403784439j) + -1.0 + (-0.5 + 0.866025403784438j) + (0.5 + 0.866025403784438j) + +The number of primitive roots equals the Euler totient function `\phi(n)`:: + + >>> [len(unitroots(n, primitive=True)) for n in range(1,20)] + [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18] + +""" + + +log = r""" +Computes the base-`b` logarithm of `x`, `\log_b(x)`. If `b` is +unspecified, :func:`log` computes the natural (base `e`) logarithm +and is equivalent to :func:`ln`. In general, the base `b` logarithm +is defined in terms of the natural logarithm as +`\log_b(x) = \ln(x)/\ln(b)`. + +By convention, we take `\log(0) = -\infty`. + +The natural logarithm is real if `x > 0` and complex if `x < 0` or if +`x` is complex. The principal branch of the complex logarithm is +used, meaning that `\Im(\ln(x)) = -\pi < \arg(x) \le \pi`. + +**Examples** + +Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> log(1) + 0.0 + >>> log(2) + 0.693147180559945 + >>> log(1000,10) + 3.0 + >>> log(4, 16) + 0.5 + >>> log(j) + (0.0 + 1.5707963267949j) + >>> log(-1) + (0.0 + 3.14159265358979j) + >>> log(0) + -inf + >>> log(inf) + +inf + +The natural logarithm is the antiderivative of `1/x`:: + + >>> quad(lambda x: 1/x, [1, 5]) + 1.6094379124341 + >>> log(5) + 1.6094379124341 + >>> diff(log, 10) + 0.1 + +The Taylor series expansion of the natural logarithm around +`x = 1` has coefficients `(-1)^{n+1}/n`:: + + >>> nprint(taylor(log, 1, 7)) + [0.0, 1.0, -0.5, 0.333333, -0.25, 0.2, -0.166667, 0.142857] + +:func:`log` supports arbitrary precision evaluation:: + + >>> mp.dps = 50 + >>> log(pi) + 1.1447298858494001741434273513530587116472948129153 + >>> log(pi, pi**3) + 0.33333333333333333333333333333333333333333333333333 + >>> mp.dps = 25 + >>> log(3+4j) + (1.609437912434100374600759 + 0.9272952180016122324285125j) +""" + +log10 = r""" +Computes the base-10 logarithm of `x`, `\log_{10}(x)`. ``log10(x)`` +is equivalent to ``log(x, 10)``. +""" + +power = r""" +Converts `x` and `y` to mpmath numbers and evaluates +`x^y = \exp(y \log(x))`:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> power(2, 0.5) + 1.41421356237309504880168872421 + +This shows the leading few digits of a large Mersenne prime +(performing the exact calculation ``2**43112609-1`` and +displaying the result in Python would be very slow):: + + >>> power(2, 43112609)-1 + 3.16470269330255923143453723949e+12978188 +""" + +modf = r""" +Converts `x` and `y` to mpmath numbers and returns `x \mod y`. +For mpmath numbers, this is equivalent to ``x % y``. + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> modf(100, pi) + 2.61062773871641 + +You can use :func:`modf` to compute fractional parts of numbers:: + + >>> modf(10.25, 1) + 0.25 + +""" + +radians = r""" +Converts the degree angle `x` to radians:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> radians(60) + 1.0471975511966 +""" + +degrees = r""" +Converts the radian angle `x` to a degree angle:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> degrees(pi/3) + 60.0 +""" + +atan2 = r""" +Computes the two-argument arctangent, `\mathrm{atan2}(y, x)`, +giving the signed angle between the positive `x`-axis and the +point `(x, y)` in the 2D plane. This function is defined for +real `x` and `y` only. + +The two-argument arctangent essentially computes +`\mathrm{atan}(y/x)`, but accounts for the signs of both +`x` and `y` to give the angle for the correct quadrant. The +following examples illustrate the difference:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> atan2(1,1), atan(1/1.) + (0.785398163397448, 0.785398163397448) + >>> atan2(1,-1), atan(1/-1.) + (2.35619449019234, -0.785398163397448) + >>> atan2(-1,1), atan(-1/1.) + (-0.785398163397448, -0.785398163397448) + >>> atan2(-1,-1), atan(-1/-1.) + (-2.35619449019234, 0.785398163397448) + +The angle convention is the same as that used for the complex +argument; see :func:`arg`. +""" + +fibonacci = r""" +``fibonacci(n)`` computes the `n`-th Fibonacci number, `F(n)`. The +Fibonacci numbers are defined by the recurrence `F(n) = F(n-1) + F(n-2)` +with the initial values `F(0) = 0`, `F(1) = 1`. :func:`fibonacci` +extends this definition to arbitrary real and complex arguments +using the formula + +.. math :: + + F(z) = \frac{\phi^z - \cos(\pi z) \phi^{-z}}{\sqrt 5} + +where `\phi` is the golden ratio. :func:`fibonacci` also uses this +continuous formula to compute `F(n)` for extremely large `n`, where +calculating the exact integer would be wasteful. + +For convenience, :func:`fib` is available as an alias for +:func:`fibonacci`. + +**Basic examples** + +Some small Fibonacci numbers are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for i in range(10): + ... print fibonacci(i), + ... + 0.0 1.0 1.0 2.0 3.0 5.0 8.0 13.0 21.0 34.0 + + >>> fibonacci(50) + 12586269025.0 + +The recurrence for `F(n)` extends backwards to negative `n`:: + + >>> for i in range(10): + ... print fibonacci(-i), + ... + 0.0 1.0 -1.0 2.0 -3.0 5.0 -8.0 13.0 -21.0 34.0 + +Large Fibonacci numbers will be computed approximately unless +the precision is set high enough:: + + >>> fib(200) + 2.8057117299251e+41 + >>> mp.dps = 45 + >>> fib(200) + 280571172992510140037611932413038677189525.0 + +:func:`fibonacci` can compute approximate Fibonacci numbers +of stupendous size:: + + >>> mp.dps = 15 + >>> fibonacci(10**25) + 3.49052338550226e+2089876402499787337692720 + +**Real and complex arguments** + +The extended Fibonacci function is an analytic function. The +property `F(z) = F(z-1) + F(z-2)` holds for arbitrary `z`:: + + >>> mp.dps = 15 + >>> fib(pi) + 2.1170270579161 + >>> fib(pi-1) + fib(pi-2) + 2.1170270579161 + >>> fib(3+4j) + (-5248.51130728372 - 14195.962288353j) + >>> fib(2+4j) + fib(1+4j) + (-5248.51130728372 - 14195.962288353j) + +The Fibonacci function has infinitely many roots on the +negative half-real axis. The first root is at 0, the second is +close to -0.18, and then there are infinitely many roots that +asymptotically approach `-n+1/2`:: + + >>> findroot(fib, -0.2) + -0.183802359692956 + >>> findroot(fib, -2) + -1.57077646820395 + >>> findroot(fib, -17) + -16.4999999596115 + >>> findroot(fib, -24) + -23.5000000000479 + +**Mathematical relationships** + +For large `n`, `F(n+1)/F(n)` approaches the golden ratio:: + + >>> mp.dps = 50 + >>> fibonacci(101)/fibonacci(100) + 1.6180339887498948482045868343656381177203127439638 + >>> +phi + 1.6180339887498948482045868343656381177203091798058 + +The sum of reciprocal Fibonacci numbers converges to an irrational +number for which no closed form expression is known:: + + >>> mp.dps = 15 + >>> nsum(lambda n: 1/fib(n), [1, inf]) + 3.35988566624318 + +Amazingly, however, the sum of odd-index reciprocal Fibonacci +numbers can be expressed in terms of a Jacobi theta function:: + + >>> nsum(lambda n: 1/fib(2*n+1), [0, inf]) + 1.82451515740692 + >>> sqrt(5)*jtheta(2,0,(3-sqrt(5))/2)**2/4 + 1.82451515740692 + +Some related sums can be done in closed form:: + + >>> nsum(lambda k: 1/(1+fib(2*k+1)), [0, inf]) + 1.11803398874989 + >>> phi - 0.5 + 1.11803398874989 + >>> f = lambda k:(-1)**(k+1) / sum(fib(n)**2 for n in range(1,k+1)) + >>> nsum(f, [1, inf]) + 0.618033988749895 + >>> phi-1 + 0.618033988749895 + +**References** + +1. http://mathworld.wolfram.com/FibonacciNumber.html +""" + +altzeta = r""" +Gives the Dirichlet eta function, `\eta(s)`, also known as the +alternating zeta function. This function is defined in analogy +with the Riemann zeta function as providing the sum of the +alternating series + +.. math :: + + \eta(s) = \sum_{k=0}^{\infty} \frac{(-1)^k}{k^s} + = 1-\frac{1}{2^s}+\frac{1}{3^s}-\frac{1}{4^s}+\ldots + +The eta function, unlike the Riemann zeta function, is an entire +function, having a finite value for all complex `s`. The special case +`\eta(1) = \log(2)` gives the value of the alternating harmonic series. + +The alternating zeta function may expressed using the Riemann zeta function +as `\eta(s) = (1 - 2^{1-s}) \zeta(s)`. It can also be expressed +in terms of the Hurwitz zeta function (:func:`hurwitz`), for example using +:func:`dirichlet` (see documentation for that function). + +**Examples** + +Some special values are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> altzeta(1) + 0.693147180559945 + >>> altzeta(0) + 0.5 + >>> altzeta(-1) + 0.25 + >>> altzeta(-2) + 0.0 + +An example of a sum that can be computed more accurately and +efficiently via :func:`altzeta` than via numerical summation:: + + >>> sum(-(-1)**n / n**2.5 for n in range(1, 100)) + 0.86720495150398402 + >>> altzeta(2.5) + 0.867199889012184 + +At positive even integers, the Dirichlet eta function +evaluates to a rational multiple of a power of `\pi`:: + + >>> altzeta(2) + 0.822467033424113 + >>> pi**2/12 + 0.822467033424113 + +Like the Riemann zeta function, `\eta(s)`, approaches 1 +as `s` approaches positive infinity, although it does +so from below rather than from above:: + + >>> altzeta(30) + 0.999999999068682 + >>> altzeta(inf) + 1.0 + >>> mp.pretty = False + >>> altzeta(1000, rounding='d') + mpf('0.99999999999999989') + >>> altzeta(1000, rounding='u') + mpf('1.0') + +**References** + +1. http://mathworld.wolfram.com/DirichletEtaFunction.html + +2. http://en.wikipedia.org/wiki/Dirichlet_eta_function +""" + +factorial = r""" +Computes the factorial, `x!`. For integers `n \ge 0`, we have +`n! = 1 \cdot 2 \cdots (n-1) \cdot n` and more generally the factorial +is defined for real or complex `x` by `x! = \Gamma(x+1)`. + +**Examples** + +Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for k in range(6): + ... print k, fac(k) + ... + 0 1.0 + 1 1.0 + 2 2.0 + 3 6.0 + 4 24.0 + 5 120.0 + >>> fac(inf) + +inf + >>> fac(0.5), sqrt(pi)/2 + (0.886226925452758, 0.886226925452758) + +For large positive `x`, `x!` can be approximated by +Stirling's formula:: + + >>> x = 10**10 + >>> fac(x) + 2.32579620567308e+95657055186 + >>> sqrt(2*pi*x)*(x/e)**x + 2.32579597597705e+95657055186 + +:func:`fac` supports evaluation for astronomically large values:: + + >>> fac(10**30) + 6.22311232304258e+29565705518096748172348871081098 + +Reciprocal factorials appear in the Taylor series of the +exponential function (among many other contexts):: + + >>> nsum(lambda k: 1/fac(k), [0, inf]), exp(1) + (2.71828182845905, 2.71828182845905) + >>> nsum(lambda k: pi**k/fac(k), [0, inf]), exp(pi) + (23.1406926327793, 23.1406926327793) + +""" + +gamma = r""" +Computes the gamma function, `\Gamma(x)`. The gamma function is a +shifted version of the ordinary factorial, satisfying +`\Gamma(n) = (n-1)!` for integers `n > 0`. More generally, it +is defined by + +.. math :: + + \Gamma(x) = \int_0^{\infty} t^{x-1} e^{-t}\, dt + +for any real or complex `x` with `\Re(x) > 0` and for `\Re(x) < 0` +by analytic continuation. + +**Examples** + +Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for k in range(1, 6): + ... print k, gamma(k) + ... + 1 1.0 + 2 1.0 + 3 2.0 + 4 6.0 + 5 24.0 + >>> gamma(inf) + +inf + >>> gamma(0) + Traceback (most recent call last): + ... + ValueError: gamma function pole + +The gamma function of a half-integer is a rational multiple of +`\sqrt{\pi}`:: + + >>> gamma(0.5), sqrt(pi) + (1.77245385090552, 1.77245385090552) + >>> gamma(1.5), sqrt(pi)/2 + (0.886226925452758, 0.886226925452758) + +We can check the integral definition:: + + >>> gamma(3.5) + 3.32335097044784 + >>> quad(lambda t: t**2.5*exp(-t), [0,inf]) + 3.32335097044784 + +:func:`gamma` supports arbitrary-precision evaluation and +complex arguments:: + + >>> mp.dps = 50 + >>> gamma(sqrt(3)) + 0.91510229697308632046045539308226554038315280564184 + >>> mp.dps = 25 + >>> gamma(2j) + (0.009902440080927490985955066 - 0.07595200133501806872408048j) + +Arguments can also be large. Note that the gamma function grows +very quickly:: + + >>> mp.dps = 15 + >>> gamma(10**20) + 1.9328495143101e+1956570551809674817225 + +""" + +psi = r""" +Gives the polygamma function of order `m` of `z`, `\psi^{(m)}(z)`. +Special cases are known as the *digamma function* (`\psi^{(0)}(z)`), +the *trigamma function* (`\psi^{(1)}(z)`), etc. The polygamma +functions are defined as the logarithmic derivatives of the gamma +function: + +.. math :: + + \psi^{(m)}(z) = \left(\frac{d}{dz}\right)^{m+1} \log \Gamma(z) + +In particular, `\psi^{(0)}(z) = \Gamma'(z)/\Gamma(z)`. In the +present implementation of :func:`psi`, the order `m` must be a +nonnegative integer, while the argument `z` may be an arbitrary +complex number (with exception for the polygamma function's poles +at `z = 0, -1, -2, \ldots`). + +**Examples** + +For various rational arguments, the polygamma function reduces to +a combination of standard mathematical constants:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> psi(0, 1), -euler + (-0.5772156649015328606065121, -0.5772156649015328606065121) + >>> psi(1, '1/4'), pi**2+8*catalan + (17.19732915450711073927132, 17.19732915450711073927132) + >>> psi(2, '1/2'), -14*apery + (-16.82879664423431999559633, -16.82879664423431999559633) + +The polygamma functions are derivatives of each other:: + + >>> diff(lambda x: psi(3, x), pi), psi(4, pi) + (-0.1105749312578862734526952, -0.1105749312578862734526952) + >>> quad(lambda x: psi(4, x), [2, 3]), psi(3,3)-psi(3,2) + (-0.375, -0.375) + +The digamma function diverges logarithmically as `z \to \infty`, +while higher orders tend to zero:: + + >>> psi(0,inf), psi(1,inf), psi(2,inf) + (+inf, 0.0, 0.0) + +Evaluation for a complex argument:: + + >>> psi(2, -1-2j) + (0.03902435405364952654838445 + 0.1574325240413029954685366j) + +Evaluation is supported for large orders `m` and/or large +arguments `z`:: + + >>> psi(3, 10**100) + 2.0e-300 + >>> psi(250, 10**30+10**20*j) + (-1.293142504363642687204865e-7010 + 3.232856260909107391513108e-7018j) + +**Application to infinite series** + +Any infinite series where the summand is a rational function of +the index `k` can be evaluated in closed form in terms of polygamma +functions of the roots and poles of the summand:: + + >>> a = sqrt(2) + >>> b = sqrt(3) + >>> nsum(lambda k: 1/((k+a)**2*(k+b)), [0, inf]) + 0.4049668927517857061917531 + >>> (psi(0,a)-psi(0,b)-a*psi(1,a)+b*psi(1,a))/(a-b)**2 + 0.4049668927517857061917531 + +This follows from the series representation (`m > 0`) + +.. math :: + + \psi^{(m)}(z) = (-1)^{m+1} m! \sum_{k=0}^{\infty} + \frac{1}{(z+k)^{m+1}}. + +Since the roots of a polynomial may be complex, it is sometimes +necessary to use the complex polygamma function to evaluate +an entirely real-valued sum:: + + >>> nsum(lambda k: 1/(k**2-2*k+3), [0, inf]) + 1.694361433907061256154665 + >>> nprint(polyroots([1,-2,3])) + [(1.0 - 1.41421j), (1.0 + 1.41421j)] + >>> r1 = 1-sqrt(2)*j + >>> r2 = r1.conjugate() + >>> (psi(0,-r2)-psi(0,-r1))/(r1-r2) + (1.694361433907061256154665 + 0.0j) + +""" + +digamma = r""" +Shortcut for ``psi(0,z)``. +""" + +harmonic = r""" +If `n` is an integer, ``harmonic(n)`` gives a floating-point +approximation of the `n`-th harmonic number `H(n)`, defined as + +.. math :: + + H(n) = 1 + \frac{1}{2} + \frac{1}{3} + \ldots + \frac{1}{n} + +The first few harmonic numbers are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(8): + ... print n, harmonic(n) + ... + 0 0.0 + 1 1.0 + 2 1.5 + 3 1.83333333333333 + 4 2.08333333333333 + 5 2.28333333333333 + 6 2.45 + 7 2.59285714285714 + +The infinite harmonic series `1 + 1/2 + 1/3 + \ldots` diverges:: + + >>> harmonic(inf) + +inf + +:func:`harmonic` is evaluated using the digamma function rather +than by summing the harmonic series term by term. It can therefore +be computed quickly for arbitrarily large `n`, and even for +nonintegral arguments:: + + >>> harmonic(10**100) + 230.835724964306 + >>> harmonic(0.5) + 0.613705638880109 + >>> harmonic(3+4j) + (2.24757548223494 + 0.850502209186044j) + +:func:`harmonic` supports arbitrary precision evaluation:: + + >>> mp.dps = 50 + >>> harmonic(11) + 3.0198773448773448773448773448773448773448773448773 + >>> harmonic(pi) + 1.8727388590273302654363491032336134987519132374152 + +The harmonic series diverges, but at a glacial pace. It is possible +to calculate the exact number of terms required before the sum +exceeds a given amount, say 100:: + + >>> mp.dps = 50 + >>> v = 10**findroot(lambda x: harmonic(10**x) - 100, 10) + >>> v + 15092688622113788323693563264538101449859496.864101 + >>> v = int(ceil(v)) + >>> print v + 15092688622113788323693563264538101449859497 + >>> harmonic(v-1) + 99.999999999999999999999999999999999999999999942747 + >>> harmonic(v) + 100.000000000000000000000000000000000000000000009 + +""" + +bernoulli = r""" +Computes the nth Bernoulli number, `B_n`, for any integer `n \ge 0`. + +The Bernoulli numbers are rational numbers, but this function +returns a floating-point approximation. To obtain an exact +fraction, use :func:`bernfrac` instead. + +**Examples** + +Numerical values of the first few Bernoulli numbers:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(15): + ... print n, bernoulli(n) + ... + 0 1.0 + 1 -0.5 + 2 0.166666666666667 + 3 0.0 + 4 -0.0333333333333333 + 5 0.0 + 6 0.0238095238095238 + 7 0.0 + 8 -0.0333333333333333 + 9 0.0 + 10 0.0757575757575758 + 11 0.0 + 12 -0.253113553113553 + 13 0.0 + 14 1.16666666666667 + +Bernoulli numbers can be approximated with arbitrary precision:: + + >>> mp.dps = 50 + >>> bernoulli(100) + -2.8382249570693706959264156336481764738284680928013e+78 + +Arbitrarily large `n` are supported:: + + >>> mp.dps = 15 + >>> bernoulli(10**20 + 2) + 3.09136296657021e+1876752564973863312327 + +The Bernoulli numbers are related to the Riemann zeta function +at integer arguments:: + + >>> -bernoulli(8) * (2*pi)**8 / (2*fac(8)) + 1.00407735619794 + >>> zeta(8) + 1.00407735619794 + +**Algorithm** + +For small `n` (`n < 3000`) :func:`bernoulli` uses a recurrence +formula due to Ramanujan. All results in this range are cached, +so sequential computation of small Bernoulli numbers is +guaranteed to be fast. + +For larger `n`, `B_n` is evaluated in terms of the Riemann zeta +function. +""" + +stieltjes = r""" +For a nonnegative integer `n`, ``stieltjes(n)`` computes the +`n`-th Stieltjes constant `\gamma_n`, defined as the +`n`-th coefficient in the Laurent series expansion of the +Riemann zeta function around the pole at `s = 1`. That is, +we have: + +.. math :: + + \zeta(s) = \frac{1}{s-1} \sum_{n=0}^{\infty} + \frac{(-1)^n}{n!} \gamma_n (s-1)^n + +More generally, ``stieltjes(n, a)`` gives the corresponding +coefficient `\gamma_n(a)` for the Hurwitz zeta function +`\zeta(s,a)` (with `\gamma_n = \gamma_n(1)`). + +**Examples** + +The zeroth Stieltjes constant is just Euler's constant `\gamma`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> stieltjes(0) + 0.577215664901533 + +Some more values are:: + + >>> stieltjes(1) + -0.0728158454836767 + >>> stieltjes(10) + 0.000205332814909065 + >>> stieltjes(30) + 0.00355772885557316 + >>> stieltjes(1000) + -1.57095384420474e+486 + >>> stieltjes(2000) + 2.680424678918e+1109 + >>> stieltjes(1, 2.5) + -0.23747539175716 + +An alternative way to compute `\gamma_1`:: + + >>> diff(extradps(15)(lambda x: 1/(x-1) - zeta(x)), 1) + -0.0728158454836767 + +:func:`stieltjes` supports arbitrary precision evaluation:: + + >>> mp.dps = 50 + >>> stieltjes(2) + -0.0096903631928723184845303860352125293590658061013408 + +**Algorithm** + +:func:`stieltjes` numerically evaluates the integral in +the following representation due to Ainsworth, Howell and +Coffey [1], [2]: + +.. math :: + + \gamma_n(a) = \frac{\log^n a}{2a} - \frac{\log^{n+1}(a)}{n+1} + + \frac{2}{a} \Re \int_0^{\infty} + \frac{(x/a-i)\log^n(a-ix)}{(1+x^2/a^2)(e^{2\pi x}-1)} dx. + +For some reference values with `a = 1`, see e.g. [4]. + +**References** + +1. O. R. Ainsworth & L. W. Howell, "An integral representation of + the generalized Euler-Mascheroni constants", NASA Technical + Paper 2456 (1985), + http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19850014994_1985014994.pdf + +2. M. W. Coffey, "The Stieltjes constants, their relation to the + `\eta_j` coefficients, and representation of the Hurwitz + zeta function", arXiv:0706.0343v1 http://arxiv.org/abs/0706.0343 + +3. http://mathworld.wolfram.com/StieltjesConstants.html + +4. http://pi.lacim.uqam.ca/piDATA/stieltjesgamma.txt + +""" + +gammaprod = r""" +Given iterables `a` and `b`, ``gammaprod(a, b)`` computes the +product / quotient of gamma functions: + +.. math :: + + \frac{\Gamma(a_0) \Gamma(a_1) \cdots \Gamma(a_p)} + {\Gamma(b_0) \Gamma(b_1) \cdots \Gamma(b_q)} + +Unlike direct calls to :func:`gamma`, :func:`gammaprod` considers +the entire product as a limit and evaluates this limit properly if +any of the numerator or denominator arguments are nonpositive +integers such that poles of the gamma function are encountered. +That is, :func:`gammaprod` evaluates + +.. math :: + + \lim_{\epsilon \to 0} + \frac{\Gamma(a_0+\epsilon) \Gamma(a_1+\epsilon) \cdots + \Gamma(a_p+\epsilon)} + {\Gamma(b_0+\epsilon) \Gamma(b_1+\epsilon) \cdots + \Gamma(b_q+\epsilon)} + +In particular: + +* If there are equally many poles in the numerator and the + denominator, the limit is a rational number times the remaining, + regular part of the product. + +* If there are more poles in the numerator, :func:`gammaprod` + returns ``+inf``. + +* If there are more poles in the denominator, :func:`gammaprod` + returns 0. + +**Examples** + +The reciprocal gamma function `1/\Gamma(x)` evaluated at `x = 0`:: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> gammaprod([], [0]) + 0.0 + +A limit:: + + >>> gammaprod([-4], [-3]) + -0.25 + >>> limit(lambda x: gamma(x-1)/gamma(x), -3, direction=1) + -0.25 + >>> limit(lambda x: gamma(x-1)/gamma(x), -3, direction=-1) + -0.25 + +""" + +beta = r""" +Computes the beta function, +`B(x,y) = \Gamma(x) \Gamma(y) / \Gamma(x+y)`. +The beta function is also commonly defined by the integral +representation + +.. math :: + + B(x,y) = \int_0^1 t^{x-1} (1-t)^{y-1} \, dt + +**Examples** + +For integer and half-integer arguments where all three gamma +functions are finite, the beta function becomes either rational +number or a rational multiple of `\pi`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> beta(5, 2) + 0.0333333333333333 + >>> beta(1.5, 2) + 0.266666666666667 + >>> 16*beta(2.5, 1.5) + 3.14159265358979 + +Where appropriate, :func:`beta` evaluates limits. A pole +of the beta function is taken to result in ``+inf``:: + + >>> beta(-0.5, 0.5) + 0.0 + >>> beta(-3, 3) + -0.333333333333333 + >>> beta(-2, 3) + +inf + >>> beta(inf, 1) + 0.0 + >>> beta(inf, 0) + nan + +:func:`beta` supports complex numbers and arbitrary precision +evaluation:: + + >>> beta(1, 2+j) + (0.4 - 0.2j) + >>> mp.dps = 25 + >>> beta(j,0.5) + (1.079424249270925780135675 - 1.410032405664160838288752j) + >>> mp.dps = 50 + >>> beta(pi, e) + 0.037890298781212201348153837138927165984170287886464 + +Various integrals can be computed by means of the +beta function:: + + >>> mp.dps = 15 + >>> quad(lambda t: t**2.5*(1-t)**2, [0, 1]) + 0.0230880230880231 + >>> beta(3.5, 3) + 0.0230880230880231 + >>> quad(lambda t: sin(t)**4 * sqrt(cos(t)), [0, pi/2]) + 0.319504062596158 + >>> beta(2.5, 0.75)/2 + 0.319504062596158 + +""" + +betainc = r""" +``betainc(a, b, x1=0, x2=1, regularized=False)`` gives the generalized +incomplete beta function, + +.. math :: + + I_{x_1}^{x_2}(a,b) = \int_{x_1}^{x_2} t^{a-1} (1-t)^{b-1} dt. + +When `x_1 = 0, x_2 = 1`, this reduces to the ordinary (complete) +beta function `B(a,b)`; see :func:`beta`. + +With the keyword argument ``regularized=True``, :func:`betainc` +computes the regularized incomplete beta function +`I_{x_1}^{x_2}(a,b) / B(a,b)`. This is the cumulative distribution of the +beta distribution with parameters `a`, `b`. + +Note: implementations of the incomplete beta function in some other +software uses a different argument order. For example, Mathematica uses the +reversed argument order ``Beta[x1,x2,a,b]``. For the equivalent of SciPy's +three-argument incomplete beta integral (implicitly with `x1 = 0`), use +``betainc(a,b,0,x2,regularized=True)``. + +**Examples** + +Verifying that :func:`betainc` computes the integral in the +definition:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> x,y,a,b = 3, 4, 0, 6 + >>> betainc(x, y, a, b) + -4010.4 + >>> quad(lambda t: t**(x-1) * (1-t)**(y-1), [a, b]) + -4010.4 + +The arguments may be arbitrary complex numbers:: + + >>> betainc(0.75, 1-4j, 0, 2+3j) + (0.2241657956955709603655887 + 0.3619619242700451992411724j) + +With regularization:: + + >>> betainc(1, 2, 0, 0.25, regularized=True) + 0.4375 + >>> betainc(pi, e, 0, 1, regularized=True) # Complete + 1.0 + +The beta integral satisfies some simple argument transformation +symmetries:: + + >>> mp.dps = 15 + >>> betainc(2,3,4,5), -betainc(2,3,5,4), betainc(3,2,1-5,1-4) + (56.0833333333333, 56.0833333333333, 56.0833333333333) + +The beta integral can often be evaluated analytically. For integer and +rational arguments, the incomplete beta function typically reduces to a +simple algebraic-logarithmic expression:: + + >>> mp.dps = 25 + >>> identify(chop(betainc(0, 0, 3, 4))) + '-(log((9/8)))' + >>> identify(betainc(2, 3, 4, 5)) + '(673/12)' + >>> identify(betainc(1.5, 1, 1, 2)) + '((-12+sqrt(1152))/18)' + +""" + +binomial = r""" +Computes the binomial coefficient + +.. math :: + + {n \choose k} = \frac{n!}{k!(n-k)!}. + +The binomial coefficient gives the number of ways that `k` items +can be chosen from a set of `n` items. More generally, the binomial +coefficient is a well-defined function of arbitrary real or +complex `n` and `k`, via the gamma function. + +**Examples** + +Generate Pascal's triangle:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint([binomial(n,k) for k in range(n+1)]) + ... + [1.0] + [1.0, 1.0] + [1.0, 2.0, 1.0] + [1.0, 3.0, 3.0, 1.0] + [1.0, 4.0, 6.0, 4.0, 1.0] + +There is 1 way to select 0 items from the empty set, and 0 ways to +select 1 item from the empty set:: + + >>> binomial(0, 0) + 1.0 + >>> binomial(0, 1) + 0.0 + +:func:`binomial` supports large arguments:: + + >>> binomial(10**20, 10**20-5) + 8.33333333333333e+97 + >>> binomial(10**20, 10**10) + 2.60784095465201e+104342944813 + +Nonintegral binomial coefficients find use in series +expansions:: + + >>> nprint(taylor(lambda x: (1+x)**0.25, 0, 4)) + [1.0, 0.25, -0.09375, 0.0546875, -0.0375977] + >>> nprint([binomial(0.25, k) for k in range(5)]) + [1.0, 0.25, -0.09375, 0.0546875, -0.0375977] + +An integral representation:: + + >>> n, k = 5, 3 + >>> f = lambda t: exp(-j*k*t)*(1+exp(j*t))**n + >>> chop(quad(f, [-pi,pi])/(2*pi)) + 10.0 + >>> binomial(n,k) + 10.0 + +""" + +rf = r""" +Computes the rising factorial or Pochhammer symbol, + +.. math :: + + x^{(n)} = x (x+1) \cdots (x+n-1) = \frac{\Gamma(x+n)}{\Gamma(x)} + +where the rightmost expression is valid for nonintegral `n`. + +**Examples** + +For integral `n`, the rising factorial is a polynomial:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint(taylor(lambda x: rf(x,n), 0, n)) + ... + [1.0] + [0.0, 1.0] + [0.0, 1.0, 1.0] + [0.0, 2.0, 3.0, 1.0] + [0.0, 6.0, 11.0, 6.0, 1.0] + +Evaluation is supported for arbitrary arguments:: + + >>> rf(2+3j, 5.5) + (-7202.03920483347 - 3777.58810701527j) +""" + +ff = r""" +Computes the falling factorial, + +.. math :: + + (x)_n = x (x-1) \cdots (x-n+1) = \frac{\Gamma(x+1)}{\Gamma(x-n+1)} + +where the rightmost expression is valid for nonintegral `n`. + +**Examples** + +For integral `n`, the falling factorial is a polynomial:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint(taylor(lambda x: ff(x,n), 0, n)) + ... + [1.0] + [0.0, 1.0] + [0.0, -1.0, 1.0] + [0.0, 2.0, -3.0, 1.0] + [0.0, -6.0, 11.0, -6.0, 1.0] + +Evaluation is supported for arbitrary arguments:: + + >>> ff(2+3j, 5.5) + (-720.41085888203 + 316.101124983878j) +""" + +fac2 = r""" +Computes the double factorial `x!!`, defined for integers +`x > 0` by + +.. math :: + + x!! = \begin{cases} + 1 \cdot 3 \cdots (x-2) \cdot x & x \;\mathrm{odd} \\ + 2 \cdot 4 \cdots (x-2) \cdot x & x \;\mathrm{even} + \end{cases} + +and more generally by [1] + +.. math :: + + x!! = 2^{x/2} \left(\frac{\pi}{2}\right)^{(\cos(\pi x)-1)/4} + \Gamma\left(\frac{x}{2}+1\right). + +**Examples** + +The integer sequence of double factorials begins:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint([fac2(n) for n in range(10)]) + [1.0, 1.0, 2.0, 3.0, 8.0, 15.0, 48.0, 105.0, 384.0, 945.0] + +For large `x`, double factorials follow a Stirling-like asymptotic +approximation:: + + >>> x = mpf(10000) + >>> fac2(x) + 5.97272691416282e+17830 + >>> sqrt(pi)*x**((x+1)/2)*exp(-x/2) + 5.97262736954392e+17830 + +The recurrence formula `x!! = x (x-2)!!` can be reversed to +define the double factorial of negative odd integers (but +not negative even integers):: + + >>> fac2(-1), fac2(-3), fac2(-5), fac2(-7) + (1.0, -1.0, 0.333333333333333, -0.0666666666666667) + >>> fac2(-2) + Traceback (most recent call last): + ... + ValueError: gamma function pole + +With the exception of the poles at negative even integers, +:func:`fac2` supports evaluation for arbitrary complex arguments. +The recurrence formula is valid generally:: + + >>> fac2(pi+2j) + (-1.3697207890154e-12 + 3.93665300979176e-12j) + >>> (pi+2j)*fac2(pi-2+2j) + (-1.3697207890154e-12 + 3.93665300979176e-12j) + +Double factorials should not be confused with nested factorials, +which are immensely larger:: + + >>> fac(fac(20)) + 5.13805976125208e+43675043585825292774 + >>> fac2(20) + 3715891200.0 + +Double factorials appear, among other things, in series expansions +of Gaussian functions and the error function. Infinite series +include:: + + >>> nsum(lambda k: 1/fac2(k), [0, inf]) + 3.05940740534258 + >>> sqrt(e)*(1+sqrt(pi/2)*erf(sqrt(2)/2)) + 3.05940740534258 + >>> nsum(lambda k: 2**k/fac2(2*k-1), [1, inf]) + 4.06015693855741 + >>> e * erf(1) * sqrt(pi) + 4.06015693855741 + +A beautiful Ramanujan sum:: + + >>> nsum(lambda k: (-1)**k*(fac2(2*k-1)/fac2(2*k))**3, [0,inf]) + 0.90917279454693 + >>> (gamma('9/8')/gamma('5/4')/gamma('7/8'))**2 + 0.90917279454693 + +**References** + +1. http://functions.wolfram.com/GammaBetaErf/Factorial2/27/01/0002/ + +2. http://mathworld.wolfram.com/DoubleFactorial.html + +""" + +hyper = r""" +Evaluates the generalized hypergeometric function + +.. math :: + + \,_pF_q(a_1,\ldots,a_p; b_1,\ldots,b_q; z) = + \sum_{n=0}^\infty \frac{(a_1)_n (a_2)_n \ldots (a_p)_n} + {(b_1)_n(b_2)_n\ldots(b_q)_n} \frac{z^n}{n!} + +where `(x)_n` denotes the rising factorial (see :func:`rf`). + +The parameters lists ``a_s`` and ``b_s`` may contain integers, +real numbers, complex numbers, as well as exact fractions given in +the form of tuples `(p, q)`. :func:`hyper` is optimized to handle +integers and fractions more efficiently than arbitrary +floating-point parameters (since rational parameters are by +far the most common). + +**Examples** + +Verifying that :func:`hyper` gives the sum in the definition, by +comparison with :func:`nsum`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a,b,c,d = 2,3,4,5 + >>> x = 0.25 + >>> hyper([a,b],[c,d],x) + 1.078903941164934876086237 + >>> fn = lambda n: rf(a,n)*rf(b,n)/rf(c,n)/rf(d,n)*x**n/fac(n) + >>> nsum(fn, [0, inf]) + 1.078903941164934876086237 + +The parameters can be any combination of integers, fractions, +floats and complex numbers:: + + >>> a, b, c, d, e = 1, (-1,2), pi, 3+4j, (2,3) + >>> x = 0.2j + >>> hyper([a,b],[c,d,e],x) + (0.9923571616434024810831887 - 0.005753848733883879742993122j) + >>> b, e = -0.5, mpf(2)/3 + >>> fn = lambda n: rf(a,n)*rf(b,n)/rf(c,n)/rf(d,n)/rf(e,n)*x**n/fac(n) + >>> nsum(fn, [0, inf]) + (0.9923571616434024810831887 - 0.005753848733883879742993122j) + +The `\,_0F_0` and `\,_1F_0` series are just elementary functions:: + + >>> a, z = sqrt(2), +pi + >>> hyper([],[],z) + 23.14069263277926900572909 + >>> exp(z) + 23.14069263277926900572909 + >>> hyper([a],[],z) + (-0.09069132879922920160334114 + 0.3283224323946162083579656j) + >>> (1-z)**(-a) + (-0.09069132879922920160334114 + 0.3283224323946162083579656j) + +If any `a_k` coefficient is a nonpositive integer, the series terminates +into a finite polynomial:: + + >>> hyper([1,1,1,-3],[2,5],1) + 0.7904761904761904761904762 + >>> identify(_) + '(83/105)' + +If any `b_k` is a nonpositive integer, the function is undefined (unless the +series terminates before the division by zero occurs):: + + >>> hyper([1,1,1,-3],[-2,5],1) + Traceback (most recent call last): + ... + ZeroDivisionError: pole in hypergeometric series + >>> hyper([1,1,1,-1],[-2,5],1) + 1.1 + +Except for polynomial cases, the radius of convergence `R` of the hypergeometric +series is either `R = \infty` (if `p \le q`), `R = 1` (if `p = q+1`), or +`R = 0` (if `p > q+1`). + +The analytic continuations of the functions with `p = q+1`, i.e. `\,_2F_1`, +`\,_3F_2`, `\,_4F_3`, etc, are all implemented and therefore these functions +can be evaluated for `|z| \ge 1`. The shortcuts :func:`hyp2f1`, :func:`hyp3f2` +are available to handle the most common cases (see their documentation), +but functions of higher degree are also supported via :func:`hyper`:: + + >>> hyper([1,2,3,4], [5,6,7], 1) # 4F3 at finite-valued branch point + 1.141783505526870731311423 + >>> hyper([4,5,6,7], [1,2,3], 1) # 4F3 at pole + +inf + >>> hyper([1,2,3,4,5], [6,7,8,9], 10) # 5F4 + (1.543998916527972259717257 - 0.5876309929580408028816365j) + >>> hyper([1,2,3,4,5,6], [7,8,9,10,11], 1j) # 6F5 + (0.9996565821853579063502466 + 0.0129721075905630604445669j) + +Please note that, as currently implemented, evaluation of `\,_pF_{p-1}` +with `p \ge 3` may be slow or inaccurate when `|z-1|` is small, +for some parameter values. + +When `p > q+1`, ``hyper`` computes the (iterated) Borel sum of the divergent +series. For `\,_2F_0` the Borel sum has an analytic solution and can be +computed efficiently (see :func:`hyp2f0`). For higher degrees, the functions +is evaluated first by attempting to sum it directly as an asymptotic +series (this only works for tiny `|z|`), and then by evaluating the Borel +regularized sum using numerical integration. Except for +special parameter combinations, this can be extremely slow. + + >>> hyper([1,1], [], 0.5) # regularization of 2F0 + (1.340965419580146562086448 + 0.8503366631752726568782447j) + >>> hyper([1,1,1,1], [1], 0.5) # regularization of 4F1 + (1.108287213689475145830699 + 0.5327107430640678181200491j) + +With the following magnitude of argument, the asymptotic series for `\,_3F1` +gives only a few digits. Using Borel summation, ``hyper`` can produce +a value with full accuracy:: + + >>> mp.dps = 15 + >>> hyper([2,0.5,4], [5.25], '0.08', force_series=True) + Traceback (most recent call last): + ... + NoConvergence: Hypergeometric series converges too slowly. Try increasing maxterms. + >>> hyper([2,0.5,4], [5.25], '0.08', asymp_tol=1e-4) + 1.0725535790737 + >>> hyper([2,0.5,4], [5.25], '0.08') + (1.07269542893559 + 5.54668863216891e-5j) + >>> hyper([2,0.5,4], [5.25], '-0.08', asymp_tol=1e-4) + 0.946344925484879 + >>> hyper([2,0.5,4], [5.25], '-0.08') + 0.946312503737771 + >>> mp.dps = 25 + >>> hyper([2,0.5,4], [5.25], '-0.08') + 0.9463125037377662296700858 + +Note that with the positive `z` value, there is a complex part in the +correct result, which falls below the tolerance of the asymptotic series. + +""" + +hypercomb = r""" +Computes a weighted combination of hypergeometric functions + +.. math :: + + \sum_{r=1}^N \left[ \prod_{k=1}^{l_r} {w_{r,k}}^{c_{r,k}} + \frac{\prod_{k=1}^{m_r} \Gamma(\alpha_{r,k})}{\prod_{k=1}^{n_r} + \Gamma(\beta_{r,k})} + \,_{p_r}F_{q_r}(a_{r,1},\ldots,a_{r,p}; b_{r,1}, + \ldots, b_{r,q}; z_r)\right]. + +Typically the parameters are linear combinations of a small set of base +parameters; :func:`hypercomb` permits computing a correct value in +the case that some of the `\alpha`, `\beta`, `b` turn out to be +nonpositive integers, or if division by zero occurs for some `w^c`, +assuming that there are opposing singularities that cancel out. +The limit is computed by evaluating the function with the base +parameters perturbed, at a higher working precision. + +The first argument should be a function that takes the perturbable +base parameters ``params`` as input and returns `N` tuples +``(w, c, alpha, beta, a, b, z)``, where the coefficients ``w``, ``c``, +gamma factors ``alpha``, ``beta``, and hypergeometric coefficients +``a``, ``b`` each should be lists of numbers, and ``z`` should be a single +number. + +**Examples** + +The following evaluates + +.. math :: + + (a-1) \frac{\Gamma(a-3)}{\Gamma(a-4)} \,_1F_1(a,a-1,z) = e^z(a-4)(a+z-1) + +with `a=1, z=3`. There is a zero factor, two gamma function poles, and +the 1F1 function is singular; all singularities cancel out to give a finite +value:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> hypercomb(lambda a: [([a-1],[1],[a-3],[a-4],[a],[a-1],3)], [1]) + -180.769832308689 + >>> -9*exp(3) + -180.769832308689 + +""" + +hyp0f1 = r""" +Gives the hypergeometric function `\,_0F_1`, sometimes known as the +confluent limit function, defined as + +.. math :: + + \,_0F_1(a,z) = \sum_{k=0}^{\infty} \frac{1}{(a)_k} \frac{z^k}{k!}. + +This function satisfies the differential equation `z f''(z) + a f'(z) = f(z)`, +and is related to the Bessel function of the first kind (see :func:`besselj`). + +``hyp0f1(a,z)`` is equivalent to ``hyper([],[a],z)``; see documentation for +:func:`hyper` for more information. + +**Examples** + +Evaluation for arbitrary arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp0f1(2, 0.25) + 1.130318207984970054415392 + >>> hyp0f1((1,2), 1234567) + 6.27287187546220705604627e+964 + >>> hyp0f1(3+4j, 1000000j) + (3.905169561300910030267132e+606 + 3.807708544441684513934213e+606j) + +Evaluation is supported for arbitrarily large values of `z`, +using asymptotic expansions:: + + >>> hyp0f1(1, 10**50) + 2.131705322874965310390701e+8685889638065036553022565 + >>> hyp0f1(1, -10**50) + 1.115945364792025420300208e-13 + +Verifying the differential equation:: + + >>> a = 2.5 + >>> f = lambda z: hyp0f1(a,z) + >>> for z in [0, 10, 3+4j]: + ... chop(z*diff(f,z,2) + a*diff(f,z) - f(z)) + ... + 0.0 + 0.0 + 0.0 + +""" + +hyp1f1 = r""" +Gives the confluent hypergeometric function of the first kind, + +.. math :: + + \,_1F_1(a,b,z) = \sum_{k=0}^{\infty} \frac{(a)_k}{(b)_k} \frac{z^k}{k!}, + +also known as Kummer's function and sometimes denoted by `M(a,b,z)`. This +function gives one solution to the confluent (Kummer's) differential equation + +.. math :: + + z f''(z) + (b-z) f'(z) - af(z) = 0. + +A second solution is given by the `U` function; see :func:`hyperu`. +Solutions are also given in an alternate form by the Whittaker +functions (:func:`whitm`, :func:`whitw`). + +``hyp1f1(a,b,z)`` is equivalent +to ``hyper([a],[b],z)``; see documentation for :func:`hyper` for more +information. + +**Examples** + +Evaluation for real and complex values of the argument `z`, with +fixed parameters `a = 2, b = -1/3`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp1f1(2, (-1,3), 3.25) + -2815.956856924817275640248 + >>> hyp1f1(2, (-1,3), -3.25) + -1.145036502407444445553107 + >>> hyp1f1(2, (-1,3), 1000) + -8.021799872770764149793693e+441 + >>> hyp1f1(2, (-1,3), -1000) + 0.000003131987633006813594535331 + >>> hyp1f1(2, (-1,3), 100+100j) + (-3.189190365227034385898282e+48 - 1.106169926814270418999315e+49j) + +Parameters may be complex:: + + >>> hyp1f1(2+3j, -1+j, 10j) + (261.8977905181045142673351 + 160.8930312845682213562172j) + +Arbitrarily large values of `z` are supported:: + + >>> hyp1f1(3, 4, 10**20) + 3.890569218254486878220752e+43429448190325182745 + >>> hyp1f1(3, 4, -10**20) + 6.0e-60 + >>> hyp1f1(3, 4, 10**20*j) + (-1.935753855797342532571597e-20 - 2.291911213325184901239155e-20j) + +Verifying the differential equation:: + + >>> a, b = 1.5, 2 + >>> f = lambda z: hyp1f1(a,b,z) + >>> for z in [0, -10, 3, 3+4j]: + ... chop(z*diff(f,z,2) + (b-z)*diff(f,z) - a*f(z)) + ... + 0.0 + 0.0 + 0.0 + 0.0 + +An integral representation:: + + >>> a, b = 1.5, 3 + >>> z = 1.5 + >>> hyp1f1(a,b,z) + 2.269381460919952778587441 + >>> g = lambda t: exp(z*t)*t**(a-1)*(1-t)**(b-a-1) + >>> gammaprod([b],[a,b-a])*quad(g, [0,1]) + 2.269381460919952778587441 + + +""" + +hyp1f2 = r""" +Gives the hypergeometric function `\,_1F_2(a_1,a_2;b_1,b_2; z)`. +The call ``hyp1f2(a1,b1,b2,z)`` is equivalent to +``hyper([a1],[b1,b2],z)``. + +Evaluation works for complex and arbitrarily large arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a, b, c = 1.5, (-1,3), 2.25 + >>> hyp1f2(a, b, c, 10**20) + -1.159388148811981535941434e+8685889639 + >>> hyp1f2(a, b, c, -10**20) + -12.60262607892655945795907 + >>> hyp1f2(a, b, c, 10**20*j) + (4.237220401382240876065501e+6141851464 - 2.950930337531768015892987e+6141851464j) + >>> hyp1f2(2+3j, -2j, 0.5j, 10-20j) + (135881.9905586966432662004 - 86681.95885418079535738828j) + +""" + +hyp2f2 = r""" +Gives the hypergeometric function `\,_2F_2(a_1,a_2;b_1,b_2; z)`. +The call ``hyp2f2(a1,a2,b1,b2,z)`` is equivalent to +``hyper([a1,a2],[b1,b2],z)``. + +Evaluation works for complex and arbitrarily large arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a, b, c, d = 1.5, (-1,3), 2.25, 4 + >>> hyp2f2(a, b, c, d, 10**20) + -5.275758229007902299823821e+43429448190325182663 + >>> hyp2f2(a, b, c, d, -10**20) + 2561445.079983207701073448 + >>> hyp2f2(a, b, c, d, 10**20*j) + (2218276.509664121194836667 - 1280722.539991603850462856j) + >>> hyp2f2(2+3j, -2j, 0.5j, 4j, 10-20j) + (80500.68321405666957342788 - 20346.82752982813540993502j) + +""" + +hyp2f3 = r""" +Gives the hypergeometric function `\,_2F_3(a_1,a_2;b_1,b_2,b_3; z)`. +The call ``hyp2f3(a1,a2,b1,b2,b3,z)`` is equivalent to +``hyper([a1,a2],[b1,b2,b3],z)``. + +Evaluation works for arbitrarily large arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a1,a2,b1,b2,b3 = 1.5, (-1,3), 2.25, 4, (1,5) + >>> hyp2f3(a1,a2,b1,b2,b3,10**20) + -4.169178177065714963568963e+8685889590 + >>> hyp2f3(a1,a2,b1,b2,b3,-10**20) + 7064472.587757755088178629 + >>> hyp2f3(a1,a2,b1,b2,b3,10**20*j) + (-5.163368465314934589818543e+6141851415 + 1.783578125755972803440364e+6141851416j) + >>> hyp2f3(2+3j, -2j, 0.5j, 4j, -1-j, 10-20j) + (-2280.938956687033150740228 + 13620.97336609573659199632j) + >>> hyp2f3(2+3j, -2j, 0.5j, 4j, -1-j, 10000000-20000000j) + (4.849835186175096516193e+3504 - 3.365981529122220091353633e+3504j) + +""" + +hyp2f1 = r""" +Gives the Gauss hypergeometric function `\,_2F_1` (often simply referred to as +*the* hypergeometric function), defined for `|z| < 1` as + +.. math :: + + \,_2F_1(a,b,c,z) = \sum_{k=0}^{\infty} + \frac{(a)_k (b)_k}{(c)_k} \frac{z^k}{k!}. + +and for `|z| \ge 1` by analytic continuation, with a branch cut on `(1, \infty)` +when necessary. + +Special cases of this function include many of the orthogonal polynomials as +well as the incomplete beta function and other functions. Properties of the +Gauss hypergeometric function are documented comprehensively in many references, +for example Abramowitz & Stegun, section 15. + +The implementation supports the analytic continuation as well as evaluation +close to the unit circle where `|z| \approx 1`. The syntax ``hyp2f1(a,b,c,z)`` +is equivalent to ``hyper([a,b],[c],z)``. + +**Examples** + +Evaluation with `z` inside, outside and on the unit circle, for +fixed parameters:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp2f1(2, (1,2), 4, 0.75) + 1.303703703703703703703704 + >>> hyp2f1(2, (1,2), 4, -1.75) + 0.7431290566046919177853916 + >>> hyp2f1(2, (1,2), 4, 1.75) + (1.418075801749271137026239 - 1.114976146679907015775102j) + >>> hyp2f1(2, (1,2), 4, 1) + 1.6 + >>> hyp2f1(2, (1,2), 4, -1) + 0.8235498012182875315037882 + >>> hyp2f1(2, (1,2), 4, j) + (0.9144026291433065674259078 + 0.2050415770437884900574923j) + >>> hyp2f1(2, (1,2), 4, 2+j) + (0.9274013540258103029011549 + 0.7455257875808100868984496j) + >>> hyp2f1(2, (1,2), 4, 0.25j) + (0.9931169055799728251931672 + 0.06154836525312066938147793j) + +Evaluation with complex parameter values:: + + >>> hyp2f1(1+j, 0.75, 10j, 1+5j) + (0.8834833319713479923389638 + 0.7053886880648105068343509j) + +Evaluation with `z = 1`:: + + >>> hyp2f1(-2.5, 3.5, 1.5, 1) + 0.0 + >>> hyp2f1(-2.5, 3, 4, 1) + 0.06926406926406926406926407 + >>> hyp2f1(2, 3, 4, 1) + +inf + +Evaluation for huge arguments:: + + >>> hyp2f1((-1,3), 1.75, 4, '1e100') + (7.883714220959876246415651e+32 + 1.365499358305579597618785e+33j) + >>> hyp2f1((-1,3), 1.75, 4, '1e1000000') + (7.883714220959876246415651e+333332 + 1.365499358305579597618785e+333333j) + >>> hyp2f1((-1,3), 1.75, 4, '1e1000000j') + (1.365499358305579597618785e+333333 - 7.883714220959876246415651e+333332j) + +An integral representation:: + + >>> a,b,c,z = -0.5, 1, 2.5, 0.25 + >>> g = lambda t: t**(b-1) * (1-t)**(c-b-1) * (1-t*z)**(-a) + >>> gammaprod([c],[b,c-b]) * quad(g, [0,1]) + 0.9480458814362824478852618 + >>> hyp2f1(a,b,c,z) + 0.9480458814362824478852618 + +Verifying the hypergeometric differential equation:: + + >>> f = lambda z: hyp2f1(a,b,c,z) + >>> chop(z*(1-z)*diff(f,z,2) + (c-(a+b+1)*z)*diff(f,z) - a*b*f(z)) + 0.0 + +""" + +hyp3f2 = r""" +Gives the generalized hypergeometric function `\,_3F_2`, defined for `|z| < 1` +as + +.. math :: + + \,_3F_2(a_1,a_2,a_3,b_1,b_2,z) = \sum_{k=0}^{\infty} + \frac{(a_1)_k (a_2)_k (a_3)_k}{(b_1)_k (b_2)_k} \frac{z^k}{k!}. + +and for `|z| \ge 1` by analytic continuation. The analytic structure of this +function is similar to that of `\,_2F_1`, generally with a singularity at +`z = 1` and a branch cut on `(1, \infty)`. + +Evaluation is supported inside, on, and outside +the circle of convergence `|z| = 1`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp3f2(1,2,3,4,5,0.25) + 1.083533123380934241548707 + >>> hyp3f2(1,2+2j,3,4,5,-10+10j) + (0.1574651066006004632914361 - 0.03194209021885226400892963j) + >>> hyp3f2(1,2,3,4,5,-10) + 0.3071141169208772603266489 + >>> hyp3f2(1,2,3,4,5,10) + (-0.4857045320523947050581423 - 0.5988311440454888436888028j) + >>> hyp3f2(0.25,1,1,2,1.5,1) + 1.157370995096772047567631 + >>> (8-pi-2*ln2)/3 + 1.157370995096772047567631 + >>> hyp3f2(1+j,0.5j,2,1,-2j,-1) + (1.74518490615029486475959 + 0.1454701525056682297614029j) + >>> hyp3f2(1+j,0.5j,2,1,-2j,sqrt(j)) + (0.9829816481834277511138055 - 0.4059040020276937085081127j) + >>> hyp3f2(-3,2,1,-5,4,1) + 1.41 + >>> hyp3f2(-3,2,1,-5,4,2) + 2.12 + +Evaluation very close to the unit circle:: + + >>> hyp3f2(1,2,3,4,5,'1.0001') + (1.564877796743282766872279 - 3.76821518787438186031973e-11j) + >>> hyp3f2(1,2,3,4,5,'1+0.0001j') + (1.564747153061671573212831 + 0.0001305757570366084557648482j) + >>> hyp3f2(1,2,3,4,5,'0.9999') + 1.564616644881686134983664 + >>> hyp3f2(1,2,3,4,5,'-0.9999') + 0.7823896253461678060196207 + +Note: evaluation for `|z-1|` small can currently be inaccurate or slow +for some parameter combinations. + +For various parameter combinations, `\,_3F_2` admits representation in terms +of hypergeometric functions of lower degree, or in terms of +simpler functions:: + + >>> for a, b, z in [(1,2,-1), (2,0.5,1)]: + ... hyp2f1(a,b,a+b+0.5,z)**2 + ... hyp3f2(2*a,a+b,2*b,a+b+0.5,2*a+2*b,z) + ... + 0.4246104461966439006086308 + 0.4246104461966439006086308 + 7.111111111111111111111111 + 7.111111111111111111111111 + + >>> z = 2+3j + >>> hyp3f2(0.5,1,1.5,2,2,z) + (0.7621440939243342419729144 + 0.4249117735058037649915723j) + >>> 4*(pi-2*ellipe(z))/(pi*z) + (0.7621440939243342419729144 + 0.4249117735058037649915723j) + +""" + +hyperu = r""" +Gives the Tricomi confluent hypergeometric function `U`, also known as +the Kummer or confluent hypergeometric function of the second kind. This +function gives a second linearly independent solution to the confluent +hypergeometric differential equation (the first is provided by `\,_1F_1` -- +see :func:`hyp1f1`). + +**Examples** + +Evaluation for arbitrary complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyperu(2,3,4) + 0.0625 + >>> hyperu(0.25, 5, 1000) + 0.1779949416140579573763523 + >>> hyperu(0.25, 5, -1000) + (0.1256256609322773150118907 - 0.1256256609322773150118907j) + +The `U` function may be singular at `z = 0`:: + + >>> hyperu(1.5, 2, 0) + +inf + >>> hyperu(1.5, -2, 0) + 0.1719434921288400112603671 + +Verifying the differential equation:: + + >>> a, b = 1.5, 2 + >>> f = lambda z: hyperu(a,b,z) + >>> for z in [-10, 3, 3+4j]: + ... chop(z*diff(f,z,2) + (b-z)*diff(f,z) - a*f(z)) + ... + 0.0 + 0.0 + 0.0 + +An integral representation:: + + >>> a,b,z = 2, 3.5, 4.25 + >>> hyperu(a,b,z) + 0.06674960718150520648014567 + >>> quad(lambda t: exp(-z*t)*t**(a-1)*(1+t)**(b-a-1),[0,inf]) / gamma(a) + 0.06674960718150520648014567 + + +[1] http://www.math.ucla.edu/~cbm/aands/page_504.htm +""" + +hyp2f0 = r""" +Gives the hypergeometric function `\,_2F_0`, defined formally by the +series + +.. math :: + + \,_2F_0(a,b;;z) = \sum_{n=0}^{\infty} (a)_n (b)_n \frac{z^n}{n!}. + +This series usually does not converge. For small enough `z`, it can be viewed +as an asymptotic series that may be summed directly with an appropriate +truncation. When this is not the case, :func:`hyp2f0` gives a regularized sum, +or equivalently, it uses a representation in terms of the +hypergeometric U function [1]. The series also converges when either `a` or `b` +is a nonpositive integer, as it then terminates into a polynomial +after `-a` or `-b` terms. + +**Examples** + +Evaluation is supported for arbitrary complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hyp2f0((2,3), 1.25, -100) + 0.07095851870980052763312791 + >>> hyp2f0((2,3), 1.25, 100) + (-0.03254379032170590665041131 + 0.07269254613282301012735797j) + >>> hyp2f0(-0.75, 1-j, 4j) + (-0.3579987031082732264862155 - 3.052951783922142735255881j) + +Even with real arguments, the regularized value of 2F0 is often complex-valued, +but the imaginary part decreases exponentially as `z \to 0`. In the following +example, the first call uses complex evaluation while the second has a small +enough `z` to evaluate using the direct series and thus the returned value +is strictly real (this should be taken to indicate that the imaginary +part is less than ``eps``):: + + >>> mp.dps = 15 + >>> hyp2f0(1.5, 0.5, 0.05) + (1.04166637647907 + 8.34584913683906e-8j) + >>> hyp2f0(1.5, 0.5, 0.0005) + 1.00037535207621 + +The imaginary part can be retrieved by increasing the working precision:: + + >>> mp.dps = 80 + >>> nprint(hyp2f0(1.5, 0.5, 0.009).imag) + 1.23828e-46 + +In the polynomial case (the series terminating), 2F0 can evaluate exactly:: + + >>> mp.dps = 15 + >>> hyp2f0(-6,-6,2) + 291793.0 + >>> identify(hyp2f0(-2,1,0.25)) + '(5/8)' + +The coefficients of the polynomials can be recovered using Taylor expansion:: + + >>> nprint(taylor(lambda x: hyp2f0(-3,0.5,x), 0, 10)) + [1.0, -1.5, 2.25, -1.875, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + >>> nprint(taylor(lambda x: hyp2f0(-4,0.5,x), 0, 10)) + [1.0, -2.0, 4.5, -7.5, 6.5625, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + + +[1] http://www.math.ucla.edu/~cbm/aands/page_504.htm +""" + + +gammainc = r""" +``gammainc(z, a=0, b=inf)`` computes the (generalized) incomplete +gamma function with integration limits `[a, b]`: + +.. math :: + + \Gamma(z,a,b) = \int_a^b t^{z-1} e^{-t} \, dt + +The generalized incomplete gamma function reduces to the +following special cases when one or both endpoints are fixed: + +* `\Gamma(z,0,\infty)` is the standard ("complete") + gamma function, `\Gamma(z)` (available directly + as the mpmath function :func:`gamma`) +* `\Gamma(z,a,\infty)` is the "upper" incomplete gamma + function, `\Gamma(z,a)` +* `\Gamma(z,0,b)` is the "lower" incomplete gamma + function, `\gamma(z,b)`. + +Of course, we have +`\Gamma(z,0,x) + \Gamma(z,x,\infty) = \Gamma(z)` +for all `z` and `x`. + +Note however that some authors reverse the order of the +arguments when defining the lower and upper incomplete +gamma function, so one should be careful to get the correct +definition. + +If also given the keyword argument ``regularized=True``, +:func:`gammainc` computes the "regularized" incomplete gamma +function + +.. math :: + + P(z,a,b) = \frac{\Gamma(z,a,b)}{\Gamma(z)}. + +**Examples** + +We can compare with numerical quadrature to verify that +:func:`gammainc` computes the integral in the definition:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> gammainc(2+3j, 4, 10) + (0.00977212668627705160602312 - 0.0770637306312989892451977j) + >>> quad(lambda t: t**(2+3j-1) * exp(-t), [4, 10]) + (0.00977212668627705160602312 - 0.0770637306312989892451977j) + +Argument symmetries follow directly from the integral definition:: + + >>> gammainc(3, 4, 5) + gammainc(3, 5, 4) + 0.0 + >>> gammainc(3,0,2) + gammainc(3,2,4); gammainc(3,0,4) + 1.523793388892911312363331 + 1.523793388892911312363331 + >>> findroot(lambda z: gammainc(2,z,3), 1) + 3.0 + +Evaluation for arbitrarily large arguments:: + + >>> gammainc(10, 100) + 4.083660630910611272288592e-26 + >>> gammainc(10, 10000000000000000) + 5.290402449901174752972486e-4342944819032375 + >>> gammainc(3+4j, 1000000+1000000j) + (-1.257913707524362408877881e-434284 + 2.556691003883483531962095e-434284j) + +Evaluation of a generalized incomplete gamma function automatically chooses +the representation that gives a more accurate result, depending on which +parameter is larger:: + + >>> gammainc(10000000, 3) - gammainc(10000000, 2) # Bad + 0.0 + >>> gammainc(10000000, 2, 3) # Good + 1.755146243738946045873491e+4771204 + >>> gammainc(2, 0, 100000001) - gammainc(2, 0, 100000000) # Bad + 0.0 + >>> gammainc(2, 100000000, 100000001) # Good + 4.078258353474186729184421e-43429441 + +The incomplete gamma functions satisfy simple recurrence +relations:: + + >>> mp.dps = 25 + >>> z, a = mpf(3.5), mpf(2) + >>> gammainc(z+1, a); z*gammainc(z,a) + a**z*exp(-a) + 10.60130296933533459267329 + 10.60130296933533459267329 + >>> gammainc(z+1,0,a); z*gammainc(z,0,a) - a**z*exp(-a) + 1.030425427232114336470932 + 1.030425427232114336470932 + +Evaluation at integers and poles:: + + >>> gammainc(-3, -4, -5) + (-0.2214577048967798566234192 + 0.0j) + >>> gammainc(-3, 0, 5) + +inf + +If `z` is an integer, the recurrence reduces the incomplete gamma +function to `P(a) \exp(-a) + Q(b) \exp(-b)` where `P` and +`Q` are polynomials:: + + >>> gammainc(1, 2); exp(-2) + 0.1353352832366126918939995 + 0.1353352832366126918939995 + >>> mp.dps = 50 + >>> identify(gammainc(6, 1, 2), ['exp(-1)', 'exp(-2)']) + '(326*exp(-1) + (-872)*exp(-2))' + +The incomplete gamma functions reduce to functions such as +the exponential integral Ei and the error function for special +arguments:: + + >>> mp.dps = 25 + >>> gammainc(0, 4); -ei(-4) + 0.00377935240984890647887486 + 0.00377935240984890647887486 + >>> gammainc(0.5, 0, 2); sqrt(pi)*erf(sqrt(2)) + 1.691806732945198336509541 + 1.691806732945198336509541 + +""" + +erf = r""" +Computes the error function, `\mathrm{erf}(x)`. The error +function is the normalized antiderivative of the Gaussian function +`\exp(-t^2)`. More precisely, + +.. math:: + + \mathrm{erf}(x) = \frac{2}{\sqrt \pi} \int_0^x \exp(-t^2) \,dt + +**Basic examples** + +Simple values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> erf(0) + 0.0 + >>> erf(1) + 0.842700792949715 + >>> erf(-1) + -0.842700792949715 + >>> erf(inf) + 1.0 + >>> erf(-inf) + -1.0 + +For large real `x`, `\mathrm{erf}(x)` approaches 1 very +rapidly:: + + >>> erf(3) + 0.999977909503001 + >>> erf(5) + 0.999999999998463 + +The error function is an odd function:: + + >>> nprint(chop(taylor(erf, 0, 5))) + [0.0, 1.12838, 0.0, -0.376126, 0.0, 0.112838] + +:func:`erf` implements arbitrary-precision evaluation and +supports complex numbers:: + + >>> mp.dps = 50 + >>> erf(0.5) + 0.52049987781304653768274665389196452873645157575796 + >>> mp.dps = 25 + >>> erf(1+j) + (1.316151281697947644880271 + 0.1904534692378346862841089j) + +Evaluation is supported for large arguments:: + + >>> mp.dps = 25 + >>> erf('1e1000') + 1.0 + >>> erf('-1e1000') + -1.0 + >>> erf('1e-1000') + 1.128379167095512573896159e-1000 + >>> erf('1e7j') + (0.0 + 8.593897639029319267398803e+43429448190317j) + >>> erf('1e7+1e7j') + (0.9999999858172446172631323 + 3.728805278735270407053139e-8j) + +**Related functions** + +See also :func:`erfc`, which is more accurate for large `x`, +and :func:`erfi` which gives the antiderivative of +`\exp(t^2)`. + +The Fresnel integrals :func:`fresnels` and :func:`fresnelc` +are also related to the error function. +""" + +erfc = r""" +Computes the complementary error function, +`\mathrm{erfc}(x) = 1-\mathrm{erf}(x)`. +This function avoids cancellation that occurs when naively +computing the complementary error function as ``1-erf(x)``:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> 1 - erf(10) + 0.0 + >>> erfc(10) + 2.08848758376254e-45 + +:func:`erfc` works accurately even for ludicrously large +arguments:: + + >>> erfc(10**10) + 4.3504398860243e-43429448190325182776 + +Complex arguments are supported:: + + >>> erfc(500+50j) + (1.19739830969552e-107492 + 1.46072418957528e-107491j) + +""" + + +erfi = r""" +Computes the imaginary error function, `\mathrm{erfi}(x)`. +The imaginary error function is defined in analogy with the +error function, but with a positive sign in the integrand: + +.. math :: + + \mathrm{erfi}(x) = \frac{2}{\sqrt \pi} \int_0^x \exp(t^2) \,dt + +Whereas the error function rapidly converges to 1 as `x` grows, +the imaginary error function rapidly diverges to infinity. +The functions are related as +`\mathrm{erfi}(x) = -i\,\mathrm{erf}(ix)` for all complex +numbers `x`. + +**Examples** + +Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> erfi(0) + 0.0 + >>> erfi(1) + 1.65042575879754 + >>> erfi(-1) + -1.65042575879754 + >>> erfi(inf) + +inf + >>> erfi(-inf) + -inf + +Note the symmetry between erf and erfi:: + + >>> erfi(3j) + (0.0 + 0.999977909503001j) + >>> erf(3) + 0.999977909503001 + >>> erf(1+2j) + (-0.536643565778565 - 5.04914370344703j) + >>> erfi(2+1j) + (-5.04914370344703 - 0.536643565778565j) + +Large arguments are supported:: + + >>> erfi(1000) + 1.71130938718796e+434291 + >>> erfi(10**10) + 7.3167287567024e+43429448190325182754 + >>> erfi(-10**10) + -7.3167287567024e+43429448190325182754 + >>> erfi(1000-500j) + (2.49895233563961e+325717 + 2.6846779342253e+325717j) + >>> erfi(100000j) + (0.0 + 1.0j) + >>> erfi(-100000j) + (0.0 - 1.0j) + + +""" + +erfinv = r""" +Computes the inverse error function, satisfying + +.. math :: + + \mathrm{erf}(\mathrm{erfinv}(x)) = + \mathrm{erfinv}(\mathrm{erf}(x)) = x. + +This function is defined only for `-1 \le x \le 1`. + +**Examples** + +Special values include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> erfinv(0) + 0.0 + >>> erfinv(1) + +inf + >>> erfinv(-1) + -inf + +The domain is limited to the standard interval:: + + >>> erfinv(2) + Traceback (most recent call last): + ... + ValueError: erfinv(x) is defined only for -1 <= x <= 1 + +It is simple to check that :func:`erfinv` computes inverse values of +:func:`erf` as promised:: + + >>> erf(erfinv(0.75)) + 0.75 + >>> erf(erfinv(-0.995)) + -0.995 + +:func:`erfinv` supports arbitrary-precision evaluation:: + + >>> mp.dps = 50 + >>> x = erf(2) + >>> x + 0.99532226501895273416206925636725292861089179704006 + >>> erfinv(x) + 2.0 + +A definite integral involving the inverse error function:: + + >>> mp.dps = 15 + >>> quad(erfinv, [0, 1]) + 0.564189583547756 + >>> 1/sqrt(pi) + 0.564189583547756 + +The inverse error function can be used to generate random numbers +with a Gaussian distribution (although this is a relatively +inefficient algorithm):: + + >>> nprint([erfinv(2*rand()-1) for n in range(6)]) # doctest: +SKIP + [-0.586747, 1.10233, -0.376796, 0.926037, -0.708142, -0.732012] + +""" + +npdf = r""" +``npdf(x, mu=0, sigma=1)`` evaluates the probability density +function of a normal distribution with mean value `\mu` +and variance `\sigma^2`. + +Elementary properties of the probability distribution can +be verified using numerical integration:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> quad(npdf, [-inf, inf]) + 1.0 + >>> quad(lambda x: npdf(x, 3), [3, inf]) + 0.5 + >>> quad(lambda x: npdf(x, 3, 2), [3, inf]) + 0.5 + +See also :func:`ncdf`, which gives the cumulative +distribution. +""" + +ncdf = r""" +``ncdf(x, mu=0, sigma=1)`` evaluates the cumulative distribution +function of a normal distribution with mean value `\mu` +and variance `\sigma^2`. + +See also :func:`npdf`, which gives the probability density. + +Elementary properties include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> ncdf(pi, mu=pi) + 0.5 + >>> ncdf(-inf) + 0.0 + >>> ncdf(+inf) + 1.0 + +The cumulative distribution is the integral of the density +function having identical mu and sigma:: + + >>> mp.dps = 15 + >>> diff(ncdf, 2) + 0.053990966513188 + >>> npdf(2) + 0.053990966513188 + >>> diff(lambda x: ncdf(x, 1, 0.5), 0) + 0.107981933026376 + >>> npdf(0, 1, 0.5) + 0.107981933026376 +""" + +expint = r""" +:func:`expint(n,z)` gives the generalized exponential integral +or En-function, + +.. math :: + + \mathrm{E}_n(z) = \int_1^{\infty} \frac{e^{-zt}}{t^n} dt, + +where `n` and `z` may both be complex numbers. The case with `n = 1` is +also given by :func:`e1`. + +**Examples** + +Evaluation at real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> expint(1, 6.25) + 0.0002704758872637179088496194 + >>> expint(-3, 2+3j) + (0.00299658467335472929656159 + 0.06100816202125885450319632j) + >>> expint(2+3j, 4-5j) + (0.001803529474663565056945248 - 0.002235061547756185403349091j) + +At negative integer values of `n`, `E_n(z)` reduces to a +rational-exponential function:: + + >>> f = lambda n, z: fac(n)*sum(z**k/fac(k-1) for k in range(1,n+2))/\ + ... exp(z)/z**(n+2) + >>> n = 3 + >>> z = 1/pi + >>> expint(-n,z) + 584.2604820613019908668219 + >>> f(n,z) + 584.2604820613019908668219 + >>> n = 5 + >>> expint(-n,z) + 115366.5762594725451811138 + >>> f(n,z) + 115366.5762594725451811138 +""" + +e1 = r""" +Computes the exponential integral `\mathrm{E}_1(z)`, given by + +.. math :: + + \mathrm{E}_1(z) = \int_z^{\infty} \frac{e^{-t}}{t} dt. + +This is equivalent to :func:`expint` with `n = 1`. + +**Examples** + +Two ways to evaluate this function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> e1(6.25) + 0.0002704758872637179088496194 + >>> expint(1,6.25) + 0.0002704758872637179088496194 + +The E1-function is essentially the same as the Ei-function (:func:`ei`) +with negated argument, except for an imaginary branch cut term:: + + >>> e1(2.5) + 0.02491491787026973549562801 + >>> -ei(-2.5) + 0.02491491787026973549562801 + >>> e1(-2.5) + (-7.073765894578600711923552 - 3.141592653589793238462643j) + >>> -ei(2.5) + -7.073765894578600711923552 + +""" + +ei = r""" +Computes the exponential integral or Ei-function, `\mathrm{Ei}(x)`. +The exponential integral is defined as + +.. math :: + + \mathrm{Ei}(x) = \int_{-\infty\,}^x \frac{e^t}{t} \, dt. + +When the integration range includes `t = 0`, the exponential +integral is interpreted as providing the Cauchy principal value. + +For real `x`, the Ei-function behaves roughly like +`\mathrm{Ei}(x) \approx \exp(x) + \log(|x|)`. + +The Ei-function is related to the more general family of exponential +integral functions denoted by `E_n`, which are available as :func:`expint`. + +**Basic examples** + +Some basic values and limits are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> ei(0) + -inf + >>> ei(1) + 1.89511781635594 + >>> ei(inf) + +inf + >>> ei(-inf) + 0.0 + +For `x < 0`, the defining integral can be evaluated +numerically as a reference:: + + >>> ei(-4) + -0.00377935240984891 + >>> quad(lambda t: exp(t)/t, [-inf, -4]) + -0.00377935240984891 + +:func:`ei` supports complex arguments and arbitrary +precision evaluation:: + + >>> mp.dps = 50 + >>> ei(pi) + 10.928374389331410348638445906907535171566338835056 + >>> mp.dps = 25 + >>> ei(3+4j) + (-4.154091651642689822535359 + 4.294418620024357476985535j) + +**Related functions** + +The exponential integral is closely related to the logarithmic +integral. See :func:`li` for additional information. + +The exponential integral is related to the hyperbolic +and trigonometric integrals (see :func:`chi`, :func:`shi`, +:func:`ci`, :func:`si`) similarly to how the ordinary +exponential function is related to the hyperbolic and +trigonometric functions:: + + >>> mp.dps = 15 + >>> ei(3) + 9.93383257062542 + >>> chi(3) + shi(3) + 9.93383257062542 + >>> chop(ci(3j) - j*si(3j) - pi*j/2) + 9.93383257062542 + +Beware that logarithmic corrections, as in the last example +above, are required to obtain the correct branch in general. +For details, see [1]. + +The exponential integral is also a special case of the +hypergeometric function `\,_2F_2`:: + + >>> z = 0.6 + >>> z*hyper([1,1],[2,2],z) + (ln(z)-ln(1/z))/2 + euler + 0.769881289937359 + >>> ei(z) + 0.769881289937359 + +**References** + +1. Relations between Ei and other functions: + http://functions.wolfram.com/GammaBetaErf/ExpIntegralEi/27/01/ + +2. Abramowitz & Stegun, section 5: + http://www.math.sfu.ca/~cbm/aands/page_228.htm + +3. Asymptotic expansion for Ei: + http://mathworld.wolfram.com/En-Function.html +""" + +li = r""" +Computes the logarithmic integral or li-function +`\mathrm{li}(x)`, defined by + +.. math :: + + \mathrm{li}(x) = \int_0^x \frac{1}{\log t} \, dt + +The logarithmic integral has a singularity at `x = 1`. + +Alternatively, ``li(x, offset=True)`` computes the offset +logarithmic integral (used in number theory) + +.. math :: + + \mathrm{Li}(x) = \int_2^x \frac{1}{\log t} \, dt. + +These two functions are related via the simple identity +`\mathrm{Li}(x) = \mathrm{li}(x) - \mathrm{li}(2)`. + +The logarithmic integral should also not be confused with +the polylogarithm (also denoted by Li), which is implemented +as :func:`polylog`. + +**Examples** + +Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> li(0) + 0.0 + >>> li(1) + -inf + >>> li(1) + -inf + >>> li(2) + 1.04516378011749278484458888919 + >>> findroot(li, 2) + 1.45136923488338105028396848589 + >>> li(inf) + +inf + >>> li(2, offset=True) + 0.0 + >>> li(1, offset=True) + -inf + >>> li(0, offset=True) + -1.04516378011749278484458888919 + >>> li(10, offset=True) + 5.12043572466980515267839286347 + +The logarithmic integral can be evaluated for arbitrary +complex arguments:: + + >>> mp.dps = 20 + >>> li(3+4j) + (3.1343755504645775265 + 2.6769247817778742392j) + +The logarithmic integral is related to the exponential integral:: + + >>> ei(log(3)) + 2.1635885946671919729 + >>> li(3) + 2.1635885946671919729 + +The logarithmic integral grows like `O(x/\log(x))`:: + + >>> mp.dps = 15 + >>> x = 10**100 + >>> x/log(x) + 4.34294481903252e+97 + >>> li(x) + 4.3619719871407e+97 + +The prime number theorem states that the number of primes less +than `x` is asymptotic to `\mathrm{Li}(x)` (equivalently +`\mathrm{li}(x)`). For example, it is known that there are +exactly 1,925,320,391,606,803,968,923 prime numbers less than +`10^{23}` [1]. The logarithmic integral provides a very +accurate estimate:: + + >>> li(10**23, offset=True) + 1.92532039161405e+21 + +A definite integral is:: + + >>> quad(li, [0, 1]) + -0.693147180559945 + >>> -ln(2) + -0.693147180559945 + +**References** + +1. http://mathworld.wolfram.com/PrimeCountingFunction.html + +2. http://mathworld.wolfram.com/LogarithmicIntegral.html + +""" + +ci = r""" +Computes the cosine integral, + +.. math :: + + \mathrm{Ci}(x) = -\int_x^{\infty} \frac{\cos t}{t}\,dt + = \gamma + \log x + \int_0^x \frac{\cos t - 1}{t}\,dt + +**Examples** + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ci(0) + -inf + >>> ci(1) + 0.3374039229009681346626462 + >>> ci(pi) + 0.07366791204642548599010096 + >>> ci(inf) + 0.0 + >>> ci(-inf) + (0.0 + 3.141592653589793238462643j) + >>> ci(2+3j) + (1.408292501520849518759125 - 2.983617742029605093121118j) + +The cosine integral behaves roughly like the sinc function +(see :func:`sinc`) for large real `x`:: + + >>> ci(10**10) + -4.875060251748226537857298e-11 + >>> sinc(10**10) + -4.875060250875106915277943e-11 + >>> chop(limit(ci, inf)) + 0.0 + +It has infinitely many roots on the positive real axis:: + + >>> findroot(ci, 1) + 0.6165054856207162337971104 + >>> findroot(ci, 2) + 3.384180422551186426397851 + +Evaluation is supported for `z` anywhere in the complex plane:: + + >>> ci(10**6*(1+j)) + (4.449410587611035724984376e+434287 + 9.75744874290013526417059e+434287j) + +We can evaluate the defining integral as a reference:: + + >>> mp.dps = 15 + >>> -quadosc(lambda t: cos(t)/t, [5, inf], omega=1) + -0.190029749656644 + >>> ci(5) + -0.190029749656644 + +Some infinite series can be evaluated using the +cosine integral:: + + >>> nsum(lambda k: (-1)**k/(fac(2*k)*(2*k)), [1,inf]) + -0.239811742000565 + >>> ci(1) - euler + -0.239811742000565 + +""" + +si = r""" +Computes the sine integral, + +.. math :: + + \mathrm{Si}(x) = \int_0^x \frac{\sin t}{t}\,dt. + +The sine integral is thus the antiderivative of the sinc +function (see :func:`sinc`). + +**Examples** + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> si(0) + 0.0 + >>> si(1) + 0.9460830703671830149413533 + >>> si(-1) + -0.9460830703671830149413533 + >>> si(pi) + 1.851937051982466170361053 + >>> si(inf) + 1.570796326794896619231322 + >>> si(-inf) + -1.570796326794896619231322 + >>> si(2+3j) + (4.547513889562289219853204 + 1.399196580646054789459839j) + +The sine integral approaches `\pi/2` for large real `x`:: + + >>> si(10**10) + 1.570796326707584656968511 + >>> pi/2 + 1.570796326794896619231322 + +Evaluation is supported for `z` anywhere in the complex plane:: + + >>> si(10**6*(1+j)) + (-9.75744874290013526417059e+434287 + 4.449410587611035724984376e+434287j) + +We can evaluate the defining integral as a reference:: + + >>> mp.dps = 15 + >>> quad(sinc, [0, 5]) + 1.54993124494467 + >>> si(5) + 1.54993124494467 + +Some infinite series can be evaluated using the +sine integral:: + + >>> nsum(lambda k: (-1)**k/(fac(2*k+1)*(2*k+1)), [0,inf]) + 0.946083070367183 + >>> si(1) + 0.946083070367183 + +""" + +chi = r""" +Computes the hyperbolic cosine integral, defined +in analogy with the cosine integral (see :func:`ci`) as + +.. math :: + + \mathrm{Chi}(x) = -\int_x^{\infty} \frac{\cosh t}{t}\,dt + = \gamma + \log x + \int_0^x \frac{\cosh t - 1}{t}\,dt + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> chi(0) + -inf + >>> chi(1) + 0.8378669409802082408946786 + >>> chi(inf) + +inf + >>> findroot(chi, 0.5) + 0.5238225713898644064509583 + >>> chi(2+3j) + (-0.1683628683277204662429321 + 2.625115880451325002151688j) + +Evaluation is supported for `z` anywhere in the complex plane:: + + >>> chi(10**6*(1+j)) + (4.449410587611035724984376e+434287 - 9.75744874290013526417059e+434287j) + +""" + +shi = r""" +Computes the hyperbolic sine integral, defined +in analogy with the sine integral (see :func:`si`) as + +.. math :: + + \mathrm{Shi}(x) = \int_0^x \frac{\sinh t}{t}\,dt. + +Some values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> shi(0) + 0.0 + >>> shi(1) + 1.057250875375728514571842 + >>> shi(-1) + -1.057250875375728514571842 + >>> shi(inf) + +inf + >>> shi(2+3j) + (-0.1931890762719198291678095 + 2.645432555362369624818525j) + +Evaluation is supported for `z` anywhere in the complex plane:: + + >>> shi(10**6*(1+j)) + (4.449410587611035724984376e+434287 - 9.75744874290013526417059e+434287j) + +""" + +fresnels = r""" +Computes the Fresnel sine integral + +.. math :: + + S(x) = \int_0^x \sin\left(\frac{\pi t^2}{2}\right) \,dt + +Note that some sources define this function +without the normalization factor `\pi/2`. + +**Examples** + +Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> fresnels(0) + 0.0 + >>> fresnels(inf) + 0.5 + >>> fresnels(-inf) + -0.5 + >>> fresnels(1) + 0.4382591473903547660767567 + >>> fresnels(1+2j) + (36.72546488399143842838788 + 15.58775110440458732748279j) + +Comparing with the definition:: + + >>> fresnels(3) + 0.4963129989673750360976123 + >>> quad(lambda t: sin(pi*t**2/2), [0,3]) + 0.4963129989673750360976123 +""" + +fresnelc = r""" +Computes the Fresnel cosine integral + +.. math :: + + C(x) = \int_0^x \cos\left(\frac{\pi t^2}{2}\right) \,dt + +Note that some sources define this function +without the normalization factor `\pi/2`. + +**Examples** + +Some basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> fresnelc(0) + 0.0 + >>> fresnelc(inf) + 0.5 + >>> fresnelc(-inf) + -0.5 + >>> fresnelc(1) + 0.7798934003768228294742064 + >>> fresnelc(1+2j) + (16.08787137412548041729489 - 36.22568799288165021578758j) + +Comparing with the definition:: + + >>> fresnelc(3) + 0.6057207892976856295561611 + >>> quad(lambda t: cos(pi*t**2/2), [0,3]) + 0.6057207892976856295561611 +""" + +airyai = r""" +Computes the Airy function `\mathrm{Ai}(x)`, which is +a solution of the Airy differential equation `y''-xy=0`. +The Ai-function behaves roughly like a slowly decaying +sine wave for `x < 0` and like a decreasing exponential for +`x > 0`. + +Limits and values include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> airyai(0), 1/(3**(2/3.)*gamma(2/3.)) + (0.355028053887817, 0.355028053887817) + >>> airyai(1) + 0.135292416312881 + >>> airyai(-1) + 0.535560883292352 + >>> airyai(inf) + 0.0 + >>> airyai(-inf) + 0.0 + +Evaluation is supported for large arguments:: + + >>> airyai(-100) + 0.176753393239553 + >>> airyai(100) + 2.63448215208818e-291 + >>> airyai(50+50j) + (-5.31790195707456e-68 - 1.16358800377071e-67j) + >>> airyai(-50+50j) + (1.04124253736317e+158 + 3.3475255449236e+157j) + +Huge arguments are also fine:: + + >>> airyai(10**10) + 1.16223597829874e-289529654602171 + >>> airyai(-10**10) + 0.000173620644815282 + >>> airyai(10**10*(1+j)) + (5.71150868372136e-186339621747698 + 1.86724550696231e-186339621747697j) + +The first negative root of the Ai function is:: + + >>> findroot(airyai, -2) + -2.33810741045977 + +We can verify the differential equation:: + + >>> for x in [-3.4, 0, 2.5, 1+2j]: + ... print abs(diff(airyai, x, 2) - x*airyai(x)) < eps + ... + True + True + True + True + +The Taylor series expansion around `x = 0` starts with +the following coefficients (note that every third term +is zero):: + + >>> nprint(chop(taylor(airyai, 0, 5))) + [0.355028, -0.258819, 0.0, 0.0591713, -0.0215683, 0.0] + +The Airy functions are a special case of Bessel functions. +For `x < 0`, we have:: + + >>> x = 3 + >>> airyai(-x) + -0.378814293677658 + >>> p = 2*(x**1.5)/3 + >>> sqrt(x)*(besselj(1/3.,p) + besselj(-1/3.,p))/3 + -0.378814293677658 + +""" + +airybi = r""" +Computes the Airy function `\mathrm{Bi}(x)`, which is +a solution of the Airy differential equation `y''-xy=0`. +The Bi-function behaves roughly like a slowly decaying +sine wave for `x < 0` and like an increasing exponential +for `x > 0`. + +Limits and values include:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> airybi(0), 1/(3**(1/6.)*gamma(2/3.)) + (0.614926627446001, 0.614926627446001) + >>> airybi(1) + 1.20742359495287 + >>> airybi(-1) + 0.103997389496945 + >>> airybi(inf) + +inf + >>> airybi(-inf) + 0.0 + +Evaluation is supported for large arguments:: + + >>> airybi(-100) + 0.0242738876801601 + >>> airybi(100) + 6.0412239966702e+288 + >>> airybi(50+50j) + (-5.32207626732144e+63 + 1.47845029116524e+65j) + >>> airybi(-50+50j) + (-3.3475255449236e+157 + 1.04124253736317e+158j) + +Huge arguments are also fine:: + + >>> mp.dps = 15 + >>> airybi(10**10) + 1.36938578794354e+289529654602165 + >>> airybi(-10**10) + 0.00177565614169293 + >>> airybi(10**10*(1+j)) + (-6.5599559310962e+186339621747689 - 6.82246272698136e+186339621747690j) + +The first negative root of the Bi function is:: + + >>> findroot(airybi, -1) + -1.17371322270913 + +We can verify the differential equation:: + + >>> for x in [-3.4, 0, 2.5, 1+2j]: + ... print abs(diff(airybi, x, 2) - x*airybi(x)) < eps + ... + True + True + True + True + +The Taylor series expansion around `x = 0` starts with +the following coefficients (note that every third term +is zero):: + + >>> nprint(chop(taylor(airybi, 0, 5))) + [0.614927, 0.448288, 0.0, 0.102488, 0.0373574, 0.0] + +The Airy functions are a special case of Bessel functions. +For `x < 0`, we have:: + + >>> x = 3 + >>> airybi(-x) + -0.198289626374927 + >>> p = 2*(x**1.5)/3 + >>> sqrt(x/3)*(besselj(-1/3.,p) - besselj(1/3.,p)) + -0.198289626374926 +""" + +ellipk = r""" +Evaluates the complete elliptic integral of the first kind, +`K(m)`, defined by + +.. math :: + + K(m) = \int_0^{\pi/2} \frac{1}{\sqrt{1-m \sin^2 t}} dt. + +Note that the argument is the parameter `m = k^2`, +not the modulus `k` which is sometimes used. + +Alternatively, in terms of a hypergeometric function, +we have: + +.. math :: + + K(m) = \frac{\pi}{2} \,_2F_1(1/2, 1/2, 1, m) + +**Examples** + +Values and limits include:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ellipk(0) + 1.570796326794896619231322 + >>> ellipk(inf) + (0.0 + 0.0j) + >>> ellipk(-inf) + 0.0 + >>> ellipk(1) + +inf + >>> ellipk(-1) + 1.31102877714605990523242 + >>> ellipk(2) + (1.31102877714605990523242 - 1.31102877714605990523242j) + +Verifying the defining integral and hypergeometric +representation:: + + >>> ellipk(0.5) + 1.85407467730137191843385 + >>> quad(lambda t: (1-0.5*sin(t)**2)**-0.5, [0, pi/2]) + 1.85407467730137191843385 + >>> pi/2*hyp2f1(0.5,0.5,1,0.5) + 1.85407467730137191843385 + +Evaluation is supported for arbitrary complex `m`:: + + >>> ellipk(3+4j) + (0.9111955638049650086562171 + 0.6313342832413452438845091j) + +A definite integral:: + + >>> quad(ellipk, [0, 1]) + 2.0 +""" + +ellipe = r""" +Evaluates the complete elliptic integral of the second kind, +`E(m)`, defined by + +.. math :: + + E(m) = \int_0^{\pi/2} \sqrt{1-m \sin^2 t} dt. + +Note that the argument is the parameter `m = k^2`, +not the modulus `k` which is sometimes used. + +Alternatively, in terms of a hypergeometric function, +we have: + +.. math :: + + E(m) = \frac{\pi}{2} \,_2F_1(1/2, -1/2, 1, m) + +**Examples** + +Basic values and limits:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> ellipe(0) + 1.570796326794896619231322 + >>> ellipe(1) + 1.0 + >>> ellipe(-1) + 1.910098894513856008952381 + >>> ellipe(2) + (0.5990701173677961037199612 + 0.5990701173677961037199612j) + >>> ellipe(inf) + (0.0 + +infj) + >>> ellipe(-inf) + +inf + +Verifying the defining integral and hypergeometric +representation:: + + >>> ellipe(0.5) + 1.350643881047675502520175 + >>> quad(lambda t: sqrt(1-0.5*sin(t)**2), [0, pi/2]) + 1.350643881047675502520175 + >>> pi/2*hyp2f1(0.5,-0.5,1,0.5) + 1.350643881047675502520175 + +Evaluation is supported for arbitrary complex `m`:: + + >>> ellipe(0.5+0.25j) + (1.360868682163129682716687 - 0.1238733442561786843557315j) + >>> ellipe(3+4j) + (1.499553520933346954333612 - 1.577879007912758274533309j) + +A definite integral:: + + >>> quad(ellipe, [0,1]) + 1.333333333333333333333333 + +""" + +agm = r""" +``agm(a, b)`` computes the arithmetic-geometric mean of `a` and +`b`, defined as the limit of the following iteration: + +.. math :: + + a_0 = a + + b_0 = b + + a_{n+1} = \frac{a_n+b_n}{2} + + b_{n+1} = \sqrt{a_n b_n} + +This function can be called with a single argument, computing +`\mathrm{agm}(a,1) = \mathrm{agm}(1,a)`. + +**Examples** + +It is a well-known theorem that the geometric mean of +two distinct positive numbers is less than the arithmetic +mean. It follows that the arithmetic-geometric mean lies +between the two means:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> a = mpf(3) + >>> b = mpf(4) + >>> sqrt(a*b) + 3.46410161513775 + >>> agm(a,b) + 3.48202767635957 + >>> (a+b)/2 + 3.5 + +The arithmetic-geometric mean is scale-invariant:: + + >>> agm(10*e, 10*pi) + 29.261085515723 + >>> 10*agm(e, pi) + 29.261085515723 + +As an order-of-magnitude estimate, `\mathrm{agm}(1,x) \approx x` +for large `x`:: + + >>> agm(10**10) + 643448704.760133 + >>> agm(10**50) + 1.34814309345871e+48 + +For tiny `x`, `\mathrm{agm}(1,x) \approx -\pi/(2 \log(x/4))`:: + + >>> agm('0.01') + 0.262166887202249 + >>> -pi/2/log('0.0025') + 0.262172347753122 + +The arithmetic-geometric mean can also be computed for complex +numbers:: + + >>> agm(3, 2+j) + (2.51055133276184 + 0.547394054060638j) + +The AGM iteration converges very quickly (each step doubles +the number of correct digits), so :func:`agm` supports efficient +high-precision evaluation:: + + >>> mp.dps = 10000 + >>> a = agm(1,2) + >>> str(a)[-10:] + '1679581912' + +**Mathematical relations** + +The arithmetic-geometric mean may be used to evaluate the +following two parametric definite integrals: + +.. math :: + + I_1 = \int_0^{\infty} + \frac{1}{\sqrt{(x^2+a^2)(x^2+b^2)}} \,dx + + I_2 = \int_0^{\pi/2} + \frac{1}{\sqrt{a^2 \cos^2(x) + b^2 \sin^2(x)}} \,dx + +We have:: + + >>> mp.dps = 15 + >>> a = 3 + >>> b = 4 + >>> f1 = lambda x: ((x**2+a**2)*(x**2+b**2))**-0.5 + >>> f2 = lambda x: ((a*cos(x))**2 + (b*sin(x))**2)**-0.5 + >>> quad(f1, [0, inf]) + 0.451115405388492 + >>> quad(f2, [0, pi/2]) + 0.451115405388492 + >>> pi/(2*agm(a,b)) + 0.451115405388492 + +A formula for `\Gamma(1/4)`:: + + >>> gamma(0.25) + 3.62560990822191 + >>> sqrt(2*sqrt(2*pi**3)/agm(1,sqrt(2))) + 3.62560990822191 + +**Possible issues** + +The branch cut chosen for complex `a` and `b` is somewhat +arbitrary. + +""" + +gegenbauer = r""" +Evaluates the Gegenbauer polynomial, or ultraspherical polynomial, + +.. math :: + + C_n^{(a)}(z) = {n+2a-1 \choose n} \,_2F_1\left(-n, n+2a; + a+\frac{1}{2}; \frac{1}{2}(1-z)\right). + +When `n` is a nonnegative integer, this formula gives a polynomial +in `z` of degree `n`, but all parameters are permitted to be +complex numbers. With `a = 1/2`, the Gegenbauer polynomial +reduces to a Legendre polynomial. + +**Examples** + +Evaluation for arbitrary arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> gegenbauer(3, 0.5, -10) + -2485.0 + >>> gegenbauer(1000, 10, 100) + 3.012757178975667428359374e+2322 + >>> gegenbauer(2+3j, -0.75, -1000j) + (-5038991.358609026523401901 + 9414549.285447104177860806j) + +Evaluation at negative integer orders:: + + >>> gegenbauer(-4, 2, 1.75) + -1.0 + >>> gegenbauer(-4, 3, 1.75) + 0.0 + >>> gegenbauer(-4, 2j, 1.75) + 0.0 + >>> gegenbauer(-7, 0.5, 3) + 8989.0 + +The Gegenbauer polynomials solve the differential equation:: + + >>> n, a = 4.5, 1+2j + >>> f = lambda z: gegenbauer(n, a, z) + >>> for z in [0, 0.75, -0.5j]: + ... chop((1-z**2)*diff(f,z,2) - (2*a+1)*z*diff(f,z) + n*(n+2*a)*f(z)) + ... + 0.0 + 0.0 + 0.0 + +The Gegenbauer polynomials have generating function +`(1-2zt+t^2)^{-a}`:: + + >>> a, z = 2.5, 1 + >>> taylor(lambda t: (1-2*z*t+t**2)**(-a), 0, 3) + [1.0, 5.0, 15.0, 35.0] + >>> [gegenbauer(n,a,z) for n in range(4)] + [1.0, 5.0, 15.0, 35.0] + +The Gegenbauer polynomials are orthogonal on `[-1, 1]` with respect +to the weight `(1-z^2)^{a-\frac{1}{2}}`:: + + >>> a, n, m = 2.5, 4, 5 + >>> Cn = lambda z: gegenbauer(n, a, z, zeroprec=1000) + >>> Cm = lambda z: gegenbauer(m, a, z, zeroprec=1000) + >>> chop(quad(lambda z: Cn(z)*Cm(z)*(1-z**2)*(a-0.5), [-1, 1])) + 0.0 +""" + +laguerre = r""" +Gives the generalized (associated) Laguerre polynomial, defined by + +.. math :: + + L_n^a(z) = \frac{\Gamma(n+b+1)}{\Gamma(b+1) \Gamma(n+1)} + \,_1F_1(-n, a+1, z). + +With `a = 0` and `n` a nonnegative integer, this reduces to an ordinary +Laguerre polynomial, the sequence of which begins +`L_0(z) = 1, L_1(z) = 1-z, L_2(z) = z^2-2z+1, \ldots`. + +The Laguerre polynomials are orthogonal with respect to the weight +`z^a e^{-z}` on `[0, \infty)`. + +**Examples** + +Evaluation for arbitrary arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> laguerre(5, 0, 0.25) + 0.03726399739583333333333333 + >>> laguerre(1+j, 0.5, 2+3j) + (4.474921610704496808379097 - 11.02058050372068958069241j) + >>> laguerre(2, 0, 10000) + 49980001.0 + >>> laguerre(2.5, 0, 10000) + -9.327764910194842158583189e+4328 + +The first few Laguerre polynomials, normalized to have integer +coefficients:: + + >>> for n in range(7): + ... chop(taylor(lambda z: fac(n)*laguerre(n, 0, z), 0, n)) + ... + [1.0] + [1.0, -1.0] + [2.0, -4.0, 1.0] + [6.0, -18.0, 9.0, -1.0] + [24.0, -96.0, 72.0, -16.0, 1.0] + [120.0, -600.0, 600.0, -200.0, 25.0, -1.0] + [720.0, -4320.0, 5400.0, -2400.0, 450.0, -36.0, 1.0] + +Verifying orthogonality:: + + >>> Lm = lambda t: laguerre(m,a,t) + >>> Ln = lambda t: laguerre(n,a,t) + >>> a, n, m = 2.5, 2, 3 + >>> chop(quad(lambda t: exp(-t)*t**a*Lm(t)*Ln(t), [0,inf])) + 0.0 + + +""" + +hermite = r""" +Evaluates the Hermite polynomial `H_n(z)`, which may be defined using +the recurrence + +.. math :: + + H_0(z) = 1 + + H_1(z) = 2z + + H_{n+1} = 2z H_n(z) - 2n H_{n-1}(z). + +The Hermite polynomials are orthogonal on `(-\infty, \infty)` with +respect to the weight `e^{-z^2}`. More generally, allowing arbitrary complex +values of `n`, the Hermite function `H_n(z)` is defined as + +.. math :: + + H_n(z) = (2z)^n \,_2F_0\left(-\frac{n}{2}, \frac{1-n}{2}, + -\frac{1}{z^2}\right) + +for `\Re{z} > 0`, or generally + +.. math :: + + H_n(z) = 2^n \sqrt{\pi} \left( + \frac{1}{\Gamma\left(\frac{1-n}{2}\right)} + \,_1F_1\left(-\frac{n}{2}, \frac{1}{2}, z^2\right) - + \frac{2z}{\Gamma\left(-\frac{n}{2}\right)} + \,_1F_1\left(\frac{1-n}{2}, \frac{3}{2}, z^2\right) + \right). + +**Examples** + +Evaluation for arbitrary arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hermite(0, 10) + 1.0 + >>> hermite(1, 10); hermite(2, 10) + 20.0 + 398.0 + >>> hermite(10000, 2) + 4.950440066552087387515653e+19334 + >>> hermite(3, -10**8) + -7999999999999998800000000.0 + >>> hermite(-3, -10**8) + 1.675159751729877682920301e+4342944819032534 + >>> hermite(2+3j, -1+2j) + (-0.076521306029935133894219 - 0.1084662449961914580276007j) + +Coefficients of the first few Hermite polynomials are:: + + >>> for n in range(7): + ... chop(taylor(lambda z: hermite(n, z), 0, n)) + ... + [1.0] + [0.0, 2.0] + [-2.0, 0.0, 4.0] + [0.0, -12.0, 0.0, 8.0] + [12.0, 0.0, -48.0, 0.0, 16.0] + [0.0, 120.0, 0.0, -160.0, 0.0, 32.0] + [-120.0, 0.0, 720.0, 0.0, -480.0, 0.0, 64.0] + +Values at `z = 0`:: + + >>> for n in range(-5, 9): + ... hermite(n, 0) + ... + 0.02769459142039868792653387 + 0.08333333333333333333333333 + 0.2215567313631895034122709 + 0.5 + 0.8862269254527580136490837 + 1.0 + 0.0 + -2.0 + 0.0 + 12.0 + 0.0 + -120.0 + 0.0 + 1680.0 + +Hermite functions satisfy the differential equation:: + + >>> n = 4 + >>> f = lambda z: hermite(n, z) + >>> z = 1.5 + >>> chop(diff(f,z,2) - 2*z*diff(f,z) + 2*n*f(z)) + 0.0 + +Verifying orthogonality:: + + >>> chop(quad(lambda t: hermite(2,t)*hermite(4,t)*exp(-t**2), [-inf,inf])) + 0.0 + +""" + +jacobi = r""" +``jacobi(n, a, b, x)`` evaluates the Jacobi polynomial +`P_n^{(a,b)}(x)`. The Jacobi polynomials are a special +case of the hypergeometric function `\,_2F_1` given by: + +.. math :: + + P_n^{(a,b)}(x) = {n+a \choose n} + \,_2F_1\left(-n,1+a+b+n,a+1,\frac{1-x}{2}\right). + +Note that this definition generalizes to nonintegral values +of `n`. When `n` is an integer, the hypergeometric series +terminates after a finite number of terms, giving +a polynomial in `x`. + +**Evaluation of Jacobi polynomials** + +A special evaluation is `P_n^{(a,b)}(1) = {n+a \choose n}`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> jacobi(4, 0.5, 0.25, 1) + 2.4609375 + >>> binomial(4+0.5, 4) + 2.4609375 + +A Jacobi polynomial of degree `n` is equal to its +Taylor polynomial of degree `n`. The explicit +coefficients of Jacobi polynomials can therefore +be recovered easily using :func:`taylor`:: + + >>> for n in range(5): + ... nprint(taylor(lambda x: jacobi(n,1,2,x), 0, n)) + ... + [1.0] + [-0.5, 2.5] + [-0.75, -1.5, 5.25] + [0.5, -3.5, -3.5, 10.5] + [0.625, 2.5, -11.25, -7.5, 20.625] + +For nonintegral `n`, the Jacobi "polynomial" is no longer +a polynomial:: + + >>> nprint(taylor(lambda x: jacobi(0.5,1,2,x), 0, 4)) + [0.309983, 1.84119, -1.26933, 1.26699, -1.34808] + +**Orthogonality** + +The Jacobi polynomials are orthogonal on the interval +`[-1, 1]` with respect to the weight function +`w(x) = (1-x)^a (1+x)^b`. That is, +`w(x) P_n^{(a,b)}(x) P_m^{(a,b)}(x)` integrates to +zero if `m \ne n` and to a nonzero number if `m = n`. + +The orthogonality is easy to verify using numerical +quadrature:: + + >>> P = jacobi + >>> f = lambda x: (1-x)**a * (1+x)**b * P(m,a,b,x) * P(n,a,b,x) + >>> a = 2 + >>> b = 3 + >>> m, n = 3, 4 + >>> chop(quad(f, [-1, 1]), 1) + 0.0 + >>> m, n = 4, 4 + >>> quad(f, [-1, 1]) + 1.9047619047619 + +**Differential equation** + +The Jacobi polynomials are solutions of the differential +equation + +.. math :: + + (1-x^2) y'' + (b-a-(a+b+2)x) y' + n (n+a+b+1) y = 0. + +We can verify that :func:`jacobi` approximately satisfies +this equation:: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> a = 2.5 + >>> b = 4 + >>> n = 3 + >>> y = lambda x: jacobi(n,a,b,x) + >>> x = pi + >>> A0 = n*(n+a+b+1)*y(x) + >>> A1 = (b-a-(a+b+2)*x)*diff(y,x) + >>> A2 = (1-x**2)*diff(y,x,2) + >>> nprint(A2 + A1 + A0, 1) + 4.0e-12 + +The difference of order `10^{-12}` is as close to zero as +it could be at 15-digit working precision, since the terms +are large:: + + >>> A0, A1, A2 + (26560.2328981879, -21503.7641037294, -5056.46879445852) + +""" + +legendre = r""" +``legendre(n, x)`` evaluates the Legendre polynomial `P_n(x)`. +The Legendre polynomials are given by the formula + +.. math :: + + P_n(x) = \frac{1}{2^n n!} \frac{d^n}{dx^n} (x^2 -1)^n. + +Alternatively, they can be computed recursively using + +.. math :: + + P_0(x) = 1 + + P_1(x) = x + + (n+1) P_{n+1}(x) = (2n+1) x P_n(x) - n P_{n-1}(x). + +A third definition is in terms of the hypergeometric function +`\,_2F_1`, whereby they can be generalized to arbitrary `n`: + +.. math :: + + P_n(x) = \,_2F_1\left(-n, n+1, 1, \frac{1-x}{2}\right) + +**Basic evaluation** + +The Legendre polynomials assume fixed values at the points +`x = -1` and `x = 1`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> nprint([legendre(n, 1) for n in range(6)]) + [1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + >>> nprint([legendre(n, -1) for n in range(6)]) + [1.0, -1.0, 1.0, -1.0, 1.0, -1.0] + +The coefficients of Legendre polynomials can be recovered +using degree-`n` Taylor expansion:: + + >>> for n in range(5): + ... nprint(chop(taylor(lambda x: legendre(n, x), 0, n))) + ... + [1.0] + [0.0, 1.0] + [-0.5, 0.0, 1.5] + [0.0, -1.5, 0.0, 2.5] + [0.375, 0.0, -3.75, 0.0, 4.375] + +The roots of Legendre polynomials are located symmetrically +on the interval `[-1, 1]`:: + + >>> for n in range(5): + ... nprint(polyroots(taylor(lambda x: legendre(n, x), 0, n)[::-1])) + ... + [] + [0.0] + [-0.57735, 0.57735] + [-0.774597, 0.0, 0.774597] + [-0.861136, -0.339981, 0.339981, 0.861136] + +An example of an evaluation for arbitrary `n`:: + + >>> legendre(0.75, 2+4j) + (1.94952805264875 + 2.1071073099422j) + +**Orthogonality** + +The Legendre polynomials are orthogonal on `[-1, 1]` with respect +to the trivial weight `w(x) = 1`. That is, `P_m(x) P_n(x)` +integrates to zero if `m \ne n` and to `2/(2n+1)` if `m = n`:: + + >>> m, n = 3, 4 + >>> quad(lambda x: legendre(m,x)*legendre(n,x), [-1, 1]) + 0.0 + >>> m, n = 4, 4 + >>> quad(lambda x: legendre(m,x)*legendre(n,x), [-1, 1]) + 0.222222222222222 + +**Differential equation** + +The Legendre polynomials satisfy the differential equation + +.. math :: + + ((1-x^2) y')' + n(n+1) y' = 0. + +We can verify this numerically:: + + >>> n = 3.6 + >>> x = 0.73 + >>> P = legendre + >>> A = diff(lambda t: (1-t**2)*diff(lambda u: P(n,u), t), x) + >>> B = n*(n+1)*P(n,x) + >>> nprint(A+B,1) + 9.0e-16 + +""" + + +legenp = r""" +Calculates the (associated) Legendre function of the first kind of +degree *n* and order *m*, `P_n^m(z)`. Taking `m = 0` gives the ordinary +Legendre function of the first kind, `P_n(z)`. The parameters may be +complex numbers. + +In terms of the Gauss hypergeometric function, the (associated) Legendre +function is defined as + +.. math :: + + P_n^m(z) = \frac{1}{\Gamma(1-m)} \frac{(1+z)^{m/2}}{(1-z)^{m/2}} + \,_2F_1\left(-n, n+1, 1-m, \frac{1-z}{2}\right). + +With *type=3* instead of *type=2*, the alternative +definition + +.. math :: + + \hat{P}_n^m(z) = \frac{1}{\Gamma(1-m)} \frac{(z+1)^{m/2}}{(z-1)^{m/2}} + \,_2F_1\left(-n, n+1, 1-m, \frac{1-z}{2}\right). + +is used. These functions correspond respectively to ``LegendreP[n,m,2,z]`` +and ``LegendreP[n,m,3,z]`` in Mathematica. + +The general solution of the (associated) Legendre differential equation + +.. math :: + + (1-z^2) f''(z) - 2zf'(z) + \left(n(n+1)-\frac{m^2}{1-z^2}\right)f(z) = 0 + +is given by `C_1 P_n^m(z) + C_2 Q_n^m(z)` for arbitrary constants +`C_1`, `C_2`, where `Q_n^m(z)` is a Legendre function of the +second kind as implemented by :func:`legenq`. + +**Examples** + +Evaluation for arbitrary parameters and arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> legenp(2, 0, 10); legendre(2, 10) + 149.5 + 149.5 + >>> legenp(-2, 0.5, 2.5) + (1.972260393822275434196053 - 1.972260393822275434196053j) + >>> legenp(2+3j, 1-j, -0.5+4j) + (-3.335677248386698208736542 - 5.663270217461022307645625j) + >>> chop(legenp(3, 2, -1.5, type=2)) + 28.125 + >>> chop(legenp(3, 2, -1.5, type=3)) + -28.125 + +Verifying the associated Legendre differential equation:: + + >>> n, m = 2, -0.5 + >>> C1, C2 = 1, -3 + >>> f = lambda z: C1*legenp(n,m,z) + C2*legenq(n,m,z) + >>> deq = lambda z: (1-z**2)*diff(f,z,2) - 2*z*diff(f,z) + \ + ... (n*(n+1)-m**2/(1-z**2))*f(z) + >>> for z in [0, 2, -1.5, 0.5+2j]: + ... chop(deq(mpmathify(z))) + ... + 0.0 + 0.0 + 0.0 + 0.0 +""" + +legenq = r""" +Calculates the (associated) Legendre function of the second kind of +degree *n* and order *m*, `Q_n^m(z)`. Taking `m = 0` gives the ordinary +Legendre function of the second kind, `Q_n(z)`. The parameters may +complex numbers. + +The Legendre functions of the second kind give a second set of +solutions to the (associated) Legendre differential equation. +(See :func:`legenp`.) +Unlike the Legendre functions of the first kind, they are not +polynomials of `z` for integer `n`, `m` but rational or logarithmic +functions with poles at `z = \pm 1`. + +There are various ways to define Legendre functions of +the second kind, giving rise to different complex structure. +A version can be selected using the *type* keyword argument. +The *type=2* and *type=3* functions are given respectively by + +.. math :: + + Q_n^m(z) = \frac{\pi}{2 \sin(\pi m)} + \left( \cos(\pi m) P_n^m(z) - + \frac{\Gamma(1+m+n)}{\Gamma(1-m+n)} P_n^{-m}(z)\right) + + \hat{Q}_n^m(z) = \frac{\pi}{2 \sin(\pi m)} e^{\pi i m} + \left( \hat{P}_n^m(z) - + \frac{\Gamma(1+m+n)}{\Gamma(1-m+n)} \hat{P}_n^{-m}(z)\right) + +where `P` and `\hat{P}` are the *type=2* and *type=3* Legendre functions +of the first kind. The formulas above should be understood as limits +when `m` is an integer. + +These functions correspond to ``LegendreQ[n,m,2,z]`` (or ``LegendreQ[n,m,z]``) +and ``LegendreQ[n,m,3,z]`` in Mathematica. The *type=3* function +is essentially the same as the function defined in +Abramowitz & Stegun (eq. 8.1.3) but with `(z+1)^{m/2}(z-1)^{m/2}` instead +of `(z^2-1)^{m/2}`, giving slightly different branches. + +**Examples** + +Evaluation for arbitrary parameters and arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> legenq(2, 0, 0.5) + -0.8186632680417568557122028 + >>> legenq(-1.5, -2, 2.5) + (0.6655964618250228714288277 + 0.3937692045497259717762649j) + >>> legenq(2-j, 3+4j, -6+5j) + (-10001.95256487468541686564 - 6011.691337610097577791134j) + +Different versions of the function:: + + >>> legenq(2, 1, 0.5) + 0.7298060598018049369381857 + >>> legenq(2, 1, 1.5) + (-7.902916572420817192300921 + 0.1998650072605976600724502j) + >>> legenq(2, 1, 0.5, type=3) + (2.040524284763495081918338 - 0.7298060598018049369381857j) + >>> chop(legenq(2, 1, 1.5, type=3)) + -0.1998650072605976600724502 + +""" + +chebyt = r""" +``chebyt(n, x)`` evaluates the Chebyshev polynomial of the first +kind `T_n(x)`, defined by the identity + +.. math :: + + T_n(\cos x) = \cos(n x). + +The Chebyshev polynomials of the first kind are a special +case of the Jacobi polynomials, and by extension of the +hypergeometric function `\,_2F_1`. They can thus also be +evaluated for nonintegral `n`. + +**Basic evaluation** + +The coefficients of the `n`-th polynomial can be recovered +using using degree-`n` Taylor expansion:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint(chop(taylor(lambda x: chebyt(n, x), 0, n))) + ... + [1.0] + [0.0, 1.0] + [-1.0, 0.0, 2.0] + [0.0, -3.0, 0.0, 4.0] + [1.0, 0.0, -8.0, 0.0, 8.0] + +**Orthogonality** + +The Chebyshev polynomials of the first kind are orthogonal +on the interval `[-1, 1]` with respect to the weight +function `w(x) = 1/\sqrt{1-x^2}`:: + + >>> f = lambda x: chebyt(m,x)*chebyt(n,x)/sqrt(1-x**2) + >>> m, n = 3, 4 + >>> nprint(quad(f, [-1, 1]),1) + 0.0 + >>> m, n = 4, 4 + >>> quad(f, [-1, 1]) + 1.57079632596448 + +""" + +chebyu = r""" +``chebyu(n, x)`` evaluates the Chebyshev polynomial of the second +kind `U_n(x)`, defined by the identity + +.. math :: + + U_n(\cos x) = \frac{\sin((n+1)x)}{\sin(x)}. + +The Chebyshev polynomials of the second kind are a special +case of the Jacobi polynomials, and by extension of the +hypergeometric function `\,_2F_1`. They can thus also be +evaluated for nonintegral `n`. + +**Basic evaluation** + +The coefficients of the `n`-th polynomial can be recovered +using using degree-`n` Taylor expansion:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(5): + ... nprint(chop(taylor(lambda x: chebyu(n, x), 0, n))) + ... + [1.0] + [0.0, 2.0] + [-1.0, 0.0, 4.0] + [0.0, -4.0, 0.0, 8.0] + [1.0, 0.0, -12.0, 0.0, 16.0] + +**Orthogonality** + +The Chebyshev polynomials of the second kind are orthogonal +on the interval `[-1, 1]` with respect to the weight +function `w(x) = \sqrt{1-x^2}`:: + + >>> f = lambda x: chebyu(m,x)*chebyu(n,x)*sqrt(1-x**2) + >>> m, n = 3, 4 + >>> quad(f, [-1, 1]) + 0.0 + >>> m, n = 4, 4 + >>> quad(f, [-1, 1]) + 1.5707963267949 +""" + +besselj = r""" +``besselj(n, x, derivative=0)`` gives the Bessel function of the first kind +`J_n(x)`. Bessel functions of the first kind are defined as +solutions of the differential equation + +.. math :: + + x^2 y'' + x y' + (x^2 - n^2) y = 0 + +which appears, among other things, when solving the radial +part of Laplace's equation in cylindrical coordinates. This +equation has two solutions for given `n`, where the +`J_n`-function is the solution that is nonsingular at `x = 0`. +For positive integer `n`, `J_n(x)` behaves roughly like a sine +(odd `n`) or cosine (even `n`) multiplied by a magnitude factor +that decays slowly as `x \to \pm\infty`. + +Generally, `J_n` is a special case of the hypergeometric +function `\,_0F_1`: + +.. math :: + + J_n(x) = \frac{x^n}{2^n \Gamma(n+1)} + \,_0F_1\left(n+1,-\frac{x^2}{4}\right) + +With *derivative* = `m \ne 0`, the `m`-th derivative + +.. math :: + + \frac{d^m}{dx^m} J_n(x) + +is computed. + +**Examples** + +Evaluation is supported for arbitrary arguments, and at +arbitrary precision:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> besselj(2, 1000) + -0.024777229528606 + >>> besselj(4, 0.75) + 0.000801070086542314 + >>> besselj(2, 1000j) + (-2.48071721019185e+432 + 6.41567059811949e-437j) + >>> mp.dps = 25 + >>> besselj(0.75j, 3+4j) + (-2.778118364828153309919653 - 1.5863603889018621585533j) + >>> mp.dps = 50 + >>> besselj(1, pi) + 0.28461534317975275734531059968613140570981118184947 + +Arguments may be large:: + + >>> mp.dps = 25 + >>> besselj(0, 10000) + -0.007096160353388801477265164 + >>> besselj(0, 10**10) + 0.000002175591750246891726859055 + >>> besselj(2, 10**100) + 7.337048736538615712436929e-51 + >>> besselj(2, 10**5*j) + (-3.540725411970948860173735e+43426 + 4.4949812409615803110051e-43433j) + +The Bessel functions of the first kind satisfy simple +symmetries around `x = 0`:: + + >>> mp.dps = 15 + >>> nprint([besselj(n,0) for n in range(5)]) + [1.0, 0.0, 0.0, 0.0, 0.0] + >>> nprint([besselj(n,pi) for n in range(5)]) + [-0.304242, 0.284615, 0.485434, 0.333458, 0.151425] + >>> nprint([besselj(n,-pi) for n in range(5)]) + [-0.304242, -0.284615, 0.485434, -0.333458, 0.151425] + +Roots of Bessel functions are often used:: + + >>> nprint([findroot(j0, k) for k in [2, 5, 8, 11, 14]]) + [2.40483, 5.52008, 8.65373, 11.7915, 14.9309] + >>> nprint([findroot(j1, k) for k in [3, 7, 10, 13, 16]]) + [3.83171, 7.01559, 10.1735, 13.3237, 16.4706] + +The roots are not periodic, but the distance between successive +roots asymptotically approaches `2 \pi`. Bessel functions of +the first kind have the following normalization:: + + >>> quadosc(j0, [0, inf], period=2*pi) + 1.0 + >>> quadosc(j1, [0, inf], period=2*pi) + 1.0 + +For `n = 1/2` or `n = -1/2`, the Bessel function reduces to a +trigonometric function:: + + >>> x = 10 + >>> besselj(0.5, x), sqrt(2/(pi*x))*sin(x) + (-0.13726373575505, -0.13726373575505) + >>> besselj(-0.5, x), sqrt(2/(pi*x))*cos(x) + (-0.211708866331398, -0.211708866331398) + +Derivatives of any order can be computed (negative orders +correspond to integration):: + + >>> mp.dps = 25 + >>> besselj(0, 7.5, 1) + -0.1352484275797055051822405 + >>> diff(lambda x: besselj(0,x), 7.5) + -0.1352484275797055051822405 + >>> besselj(0, 7.5, 10) + -0.1377811164763244890135677 + >>> diff(lambda x: besselj(0,x), 7.5, 10) + -0.1377811164763244890135677 + >>> besselj(0,7.5,-1) - besselj(0,3.5,-1) + -0.1241343240399987693521378 + >>> quad(j0, [3.5, 7.5]) + -0.1241343240399987693521378 + +Differentiation with a noninteger order gives the fractional derivative +in the sense of the Riemann-Liouville differintegral, as computed by +:func:`differint`:: + + >>> mp.dps = 15 + >>> besselj(1, 3.5, 0.75) + -0.385977722939384 + >>> differint(lambda x: besselj(1, x), 3.5, 0.75) + -0.385977722939384 + +""" + +besseli = r""" +``besseli(n, x, derivative=0)`` gives the modified Bessel function of the +first kind, + +.. math :: + + I_n(x) = i^{-n} J_n(ix). + +With *derivative* = `m \ne 0`, the `m`-th derivative + +.. math :: + + \frac{d^m}{dx^m} I_n(x) + +is computed. + +**Examples** + +Some values of `I_n(x)`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> besseli(0,0) + 1.0 + >>> besseli(1,0) + 0.0 + >>> besseli(0,1) + 1.266065877752008335598245 + >>> besseli(3.5, 2+3j) + (-0.2904369752642538144289025 - 0.4469098397654815837307006j) + +Arguments may be large:: + + >>> besseli(2, 1000) + 2.480717210191852440616782e+432 + >>> besseli(2, 10**10) + 4.299602851624027900335391e+4342944813 + >>> besseli(2, 6000+10000j) + (-2.114650753239580827144204e+2603 + 4.385040221241629041351886e+2602j) + +For integers `n`, the following integral representation holds:: + + >>> mp.dps = 15 + >>> n = 3 + >>> x = 2.3 + >>> quad(lambda t: exp(x*cos(t))*cos(n*t), [0,pi])/pi + 0.349223221159309 + >>> besseli(n,x) + 0.349223221159309 + +Derivatives and antiderivatives of any order can be computed:: + + >>> mp.dps = 25 + >>> besseli(2, 7.5, 1) + 195.8229038931399062565883 + >>> diff(lambda x: besseli(2,x), 7.5) + 195.8229038931399062565883 + >>> besseli(2, 7.5, 10) + 153.3296508971734525525176 + >>> diff(lambda x: besseli(2,x), 7.5, 10) + 153.3296508971734525525176 + >>> besseli(2,7.5,-1) - besseli(2,3.5,-1) + 202.5043900051930141956876 + >>> quad(lambda x: besseli(2,x), [3.5, 7.5]) + 202.5043900051930141956876 + +""" + +bessely = r""" +``bessely(n, x, derivative=0)`` gives the Bessel function of the second kind, + +.. math :: + + Y_n(x) = \frac{J_n(x) \cos(\pi n) - J_{-n}(x)}{\sin(\pi n)}. + +For `n` an integer, this formula should be understood as a +limit. With *derivative* = `m \ne 0`, the `m`-th derivative + +.. math :: + + \frac{d^m}{dx^m} Y_n(x) + +is computed. + +**Examples** + +Some values of `Y_n(x)`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> bessely(0,0), bessely(1,0), bessely(2,0) + (-inf, -inf, -inf) + >>> bessely(1, pi) + 0.3588729167767189594679827 + >>> bessely(0.5, 3+4j) + (9.242861436961450520325216 - 3.085042824915332562522402j) + +Arguments may be large:: + + >>> bessely(0, 10000) + 0.00364780555898660588668872 + >>> bessely(2.5, 10**50) + -4.8952500412050989295774e-26 + >>> bessely(2.5, -10**50) + (0.0 + 4.8952500412050989295774e-26j) + +Derivatives and antiderivatives of any order can be computed:: + + >>> bessely(2, 3.5, 1) + 0.3842618820422660066089231 + >>> diff(lambda x: bessely(2, x), 3.5) + 0.3842618820422660066089231 + >>> bessely(0.5, 3.5, 1) + -0.2066598304156764337900417 + >>> diff(lambda x: bessely(0.5, x), 3.5) + -0.2066598304156764337900417 + >>> diff(lambda x: bessely(2, x), 0.5, 10) + -208173867409.5547350101511 + >>> bessely(2, 0.5, 10) + -208173867409.5547350101511 + >>> bessely(2, 100.5, 100) + 0.02668487547301372334849043 + >>> quad(lambda x: bessely(2,x), [1,3]) + -1.377046859093181969213262 + >>> bessely(2,3,-1) - bessely(2,1,-1) + -1.377046859093181969213262 + +""" + +besselk = r""" +``besselk(n, x)`` gives the modified Bessel function of the +second kind, + +.. math :: + + K_n(x) = \frac{\pi}{2} \frac{I_{-n}(x)-I_{n}(x)}{\sin(\pi n)} + +For `n` an integer, this formula should be understood as a +limit. + +**Examples** + +Evaluation is supported for arbitrary complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> besselk(0,1) + 0.4210244382407083333356274 + >>> besselk(0, -1) + (0.4210244382407083333356274 - 3.97746326050642263725661j) + >>> besselk(3.5, 2+3j) + (-0.02090732889633760668464128 + 0.2464022641351420167819697j) + >>> besselk(2+3j, 0.5) + (0.9615816021726349402626083 + 0.1918250181801757416908224j) + +Arguments may be large:: + + >>> besselk(0, 100) + 4.656628229175902018939005e-45 + >>> besselk(1, 10**6) + 4.131967049321725588398296e-434298 + >>> besselk(1, 10**6*j) + (0.001140348428252385844876706 - 0.0005200017201681152909000961j) + >>> besselk(4.5, fmul(10**50, j, exact=True)) + (1.561034538142413947789221e-26 + 1.243554598118700063281496e-25j) + +The point `x = 0` is a singularity (logarithmic if `n = 0`):: + + >>> besselk(0,0) + +inf + >>> besselk(1,0) + +inf + >>> for n in range(-4, 5): + ... print besselk(n, '1e-1000') + ... + 4.8e+4001 + 8.0e+3000 + 2.0e+2000 + 1.0e+1000 + 2302.701024509704096466802 + 1.0e+1000 + 2.0e+2000 + 8.0e+3000 + 4.8e+4001 + +""" + +hankel1 = r""" +``hankel1(n,x)`` computes the Hankel function of the first kind, +which is the complex combination of Bessel functions given by + +.. math :: + + H_n^{(1)}(x) = J_n(x) + i Y_n(x). + +**Examples** + +The Hankel function is generally complex-valued:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hankel1(2, pi) + (0.4854339326315091097054957 - 0.0999007139290278787734903j) + >>> hankel1(3.5, pi) + (0.2340002029630507922628888 - 0.6419643823412927142424049j) +""" + +hankel2 = r""" +``hankel2(n,x)`` computes the Hankel function of the second kind, +which is the complex combination of Bessel functions given by + +.. math :: + + H_n^{(2)}(x) = J_n(x) - i Y_n(x). + +**Examples** + +The Hankel function is generally complex-valued:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> hankel2(2, pi) + (0.4854339326315091097054957 + 0.0999007139290278787734903j) + >>> hankel2(3.5, pi) + (0.2340002029630507922628888 + 0.6419643823412927142424049j) +""" + +lambertw = r""" +The Lambert W function `W(z)` is defined as the inverse function +of `w \exp(w)`. In other words, the value of `W(z)` is such that +`z = W(z) \exp(W(z))` for any complex number `z`. + +The Lambert W function is a multivalued function with infinitely +many branches. Each branch gives a separate solution of the +equation `w \exp(w)`. All branches are supported by +:func:`lambertw`: + +* ``lambertw(z)`` gives the principal solution (branch 0) + +* ``lambertw(z, k)`` gives the solution on branch `k` + +The Lambert W function has two partially real branches: the +principal branch (`k = 0`) is real for real `z > -1/e`, and the +`k = -1` branch is real for `-1/e < z < 0`. All branches except +`k = 0` have a logarithmic singularity at `z = 0`. + +**Basic examples** + +The Lambert W function is the inverse of `w \exp(w)`:: + + >>> from mpmath import * + >>> mp.dps = 35; mp.pretty = True + >>> w = lambertw(1) + >>> w + 0.56714329040978387299996866221035555 + >>> w*exp(w) + 1.0 + +Any branch gives a valid inverse:: + + >>> w = lambertw(1, k=3) + >>> w # doctest: +NORMALIZE_WHITESPACE + (-2.8535817554090378072068187234910812 + + 17.113535539412145912607826671159289j) + >>> w*exp(w) + (1.0 + 3.5075477124212226194278700785075126e-36j) + +**Applications to equation-solving** + +The Lambert W function may be used to solve various kinds of +equations, such as finding the value of the infinite power +tower `z^{z^{z^{\ldots}}}`:: + + >>> def tower(z, n): + ... if n == 0: + ... return z + ... return z ** tower(z, n-1) + ... + >>> tower(0.5, 100) + 0.641185744504986 + >>> mp.dps = 50 + >>> -lambertw(-log(0.5))/log(0.5) + 0.6411857445049859844862004821148236665628209571911 + +**Properties** + +The Lambert W function grows roughly like the natural logarithm +for large arguments:: + + >>> mp.dps = 15 + >>> lambertw(1000) + 5.2496028524016 + >>> log(1000) + 6.90775527898214 + >>> lambertw(10**100) + 224.843106445119 + >>> log(10**100) + 230.258509299405 + +The principal branch of the Lambert W function has a rational +Taylor series expansion around `z = 0`:: + + >>> nprint(taylor(lambertw, 0, 6), 10) + [0.0, 1.0, -1.0, 1.5, -2.666666667, 5.208333333, -10.8] + +Some special values and limits are:: + + >>> mp.dps = 15 + >>> lambertw(0) + 0.0 + >>> lambertw(1) + 0.567143290409784 + >>> lambertw(e) + 1.0 + >>> lambertw(inf) + +inf + >>> lambertw(0, k=-1) + -inf + >>> lambertw(0, k=3) + -inf + >>> lambertw(inf, k=3) + (+inf + 18.8495559215388j) + +The `k = 0` and `k = -1` branches join at `z = -1/e` where +`W(z) = -1` for both branches. Since `-1/e` can only be represented +approximately with mpmath numbers, evaluating the Lambert W function +at this point only gives `-1` approximately:: + + >>> mp.dps = 25 + >>> lambertw(-1/e, 0) + -0.999999999999837133022867 + >>> lambertw(-1/e, -1) + -1.00000000000016286697718 + +If `-1/e` happens to round in the negative direction, there might be +a small imaginary part:: + + >>> mp.dps = 15 + >>> lambertw(-1/e) + (-1.0 + 8.22007971511612e-9j) + +**Possible issues** + +The evaluation can become inaccurate very close to the branch point +at `-1/e`. In some corner cases, :func:`lambertw` might currently +fail to converge, or can end up on the wrong branch. + +**Algorithm** + +Halley's iteration is used to invert `w \exp(w)`, using a first-order +asymptotic approximation (`O(\log(w))` or `O(w)`) as the initial +estimate. + +The definition, implementation and choice of branches is based +on Corless et al, "On the Lambert W function", Adv. Comp. Math. 5 +(1996) 329-359, available online here: +http://www.apmaths.uwo.ca/~djeffrey/Offprints/W-adv-cm.pdf + +TODO: use a series expansion when extremely close to the branch point +at `-1/e` and make sure that the proper branch is chosen there +""" + +barnesg = r""" +Evaluates the Barnes G-function, which generalizes the +superfactorial (:func:`superfac`) and by extension also the +hyperfactorial (:func:`hyperfac`) to the complex numbers +in an analogous way to how the gamma function generalizes +the ordinary factorial. + +The Barnes G-function may be defined in terms of a Weierstrass +product: + +.. math :: + + G(z+1) = (2\pi)^{z/2} e^{-[z(z+1)+\gamma z^2]/2} + \prod_{n=1}^\infty + \left[\left(1+\frac{z}{n}\right)^ne^{-z+z^2/(2n)}\right] + +For positive integers `n`, we have have relation to superfactorials +`G(n) = \mathrm{sf}(n-2) = 0! \cdot 1! \cdots (n-2)!`. + +**Examples** + +Some elementary values and limits of the Barnes G-function:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> barnesg(1), barnesg(2), barnesg(3) + (1.0, 1.0, 1.0) + >>> barnesg(4) + 2.0 + >>> barnesg(5) + 12.0 + >>> barnesg(6) + 288.0 + >>> barnesg(7) + 34560.0 + >>> barnesg(8) + 24883200.0 + >>> barnesg(inf) + +inf + >>> barnesg(0), barnesg(-1), barnesg(-2) + (0.0, 0.0, 0.0) + +Closed-form values are known for some rational arguments:: + + >>> barnesg('1/2') + 0.603244281209446 + >>> sqrt(exp(0.25+log(2)/12)/sqrt(pi)/glaisher**3) + 0.603244281209446 + >>> barnesg('1/4') + 0.29375596533861 + >>> nthroot(exp('3/8')/exp(catalan/pi)/ + ... gamma(0.25)**3/sqrt(glaisher)**9, 4) + 0.29375596533861 + +The Barnes G-function satisfies the functional equation +`G(z+1) = \Gamma(z) G(z)`:: + + >>> z = pi + >>> barnesg(z+1) + 2.39292119327948 + >>> gamma(z)*barnesg(z) + 2.39292119327948 + +The asymptotic growth rate of the Barnes G-function is related to +the Glaisher-Kinkelin constant:: + + >>> limit(lambda n: barnesg(n+1)/(n**(n**2/2-mpf(1)/12)* + ... (2*pi)**(n/2)*exp(-3*n**2/4)), inf) + 0.847536694177301 + >>> exp('1/12')/glaisher + 0.847536694177301 + +The Barnes G-function can be differentiated in closed form:: + + >>> z = 3 + >>> diff(barnesg, z) + 0.264507203401607 + >>> barnesg(z)*((z-1)*psi(0,z)-z+(log(2*pi)+1)/2) + 0.264507203401607 + +Evaluation is supported for arbitrary arguments and at arbitrary +precision:: + + >>> barnesg(6.5) + 2548.7457695685 + >>> barnesg(-pi) + 0.00535976768353037 + >>> barnesg(3+4j) + (-0.000676375932234244 - 4.42236140124728e-5j) + >>> mp.dps = 50 + >>> barnesg(1/sqrt(2)) + 0.81305501090451340843586085064413533788206204124732 + >>> q = barnesg(10j) + >>> q.real + 0.000000000021852360840356557241543036724799812371995850552234 + >>> q.imag + -0.00000000000070035335320062304849020654215545839053210041457588 + >>> mp.dps = 15 + >>> barnesg(100) + 3.10361006263698e+6626 + >>> barnesg(-101) + 0.0 + >>> barnesg(-10.5) + 5.94463017605008e+25 + >>> barnesg(-10000.5) + -6.14322868174828e+167480422 + >>> barnesg(1000j) + (5.21133054865546e-1173597 + 4.27461836811016e-1173597j) + >>> barnesg(-1000+1000j) + (2.43114569750291e+1026623 + 2.24851410674842e+1026623j) + + +**References** + +1. Whittaker & Watson, *A Course of Modern Analysis*, + Cambridge University Press, 4th edition (1927), p.264 +2. http://en.wikipedia.org/wiki/Barnes_G-function +3. http://mathworld.wolfram.com/BarnesG-Function.html + +""" + +superfac = r""" +Computes the superfactorial, defined as the product of +consecutive factorials + +.. math :: + + \mathrm{sf}(n) = \prod_{k=1}^n k! + +For general complex `z`, `\mathrm{sf}(z)` is defined +in terms of the Barnes G-function (see :func:`barnesg`). + +**Examples** + +The first few superfactorials are (OEIS A000178):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(10): + ... print n, superfac(n) + ... + 0 1.0 + 1 1.0 + 2 2.0 + 3 12.0 + 4 288.0 + 5 34560.0 + 6 24883200.0 + 7 125411328000.0 + 8 5.05658474496e+15 + 9 1.83493347225108e+21 + +Superfactorials grow very rapidly:: + + >>> superfac(1000) + 3.24570818422368e+1177245 + >>> superfac(10**10) + 2.61398543581249e+467427913956904067453 + +Evaluation is supported for arbitrary arguments:: + + >>> mp.dps = 25 + >>> superfac(pi) + 17.20051550121297985285333 + >>> superfac(2+3j) + (-0.005915485633199789627466468 + 0.008156449464604044948738263j) + >>> diff(superfac, 1) + 0.2645072034016070205673056 + +**References** + +1. http://www.research.att.com/~njas/sequences/A000178 + +""" + + +hyperfac = r""" +Computes the hyperfactorial, defined for integers as the product + +.. math :: + + H(n) = \prod_{k=1}^n k^k. + + +The hyperfactorial satisfies the recurrence formula `H(z) = z^z H(z-1)`. +It can be defined more generally in terms of the Barnes G-function (see +:func:`barnesg`) and the gamma function by the formula + +.. math :: + + H(z) = \frac{\Gamma(z+1)^z}{G(z)}. + +The extension to complex numbers can also be done via +the integral representation + +.. math :: + + H(z) = (2\pi)^{-z/2} \exp \left[ + {z+1 \choose 2} + \int_0^z \log(t!)\,dt + \right]. + +**Examples** + +The rapidly-growing sequence of hyperfactorials begins +(OEIS A002109):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(10): + ... print n, hyperfac(n) + ... + 0 1.0 + 1 1.0 + 2 4.0 + 3 108.0 + 4 27648.0 + 5 86400000.0 + 6 4031078400000.0 + 7 3.3197663987712e+18 + 8 5.56964379417266e+25 + 9 2.15779412229419e+34 + +Some even larger hyperfactorials are:: + + >>> hyperfac(1000) + 5.46458120882585e+1392926 + >>> hyperfac(10**10) + 4.60408207642219e+489142638002418704309 + +The hyperfactorial can be evaluated for arbitrary arguments:: + + >>> hyperfac(0.5) + 0.880449235173423 + >>> diff(hyperfac, 1) + 0.581061466795327 + >>> hyperfac(pi) + 205.211134637462 + >>> hyperfac(-10+1j) + (3.01144471378225e+46 - 2.45285242480185e+46j) + +The recurrence property of the hyperfactorial holds +generally:: + + >>> z = 3-4*j + >>> hyperfac(z) + (-4.49795891462086e-7 - 6.33262283196162e-7j) + >>> z**z * hyperfac(z-1) + (-4.49795891462086e-7 - 6.33262283196162e-7j) + >>> z = mpf(-0.6) + >>> chop(z**z * hyperfac(z-1)) + 1.28170142849352 + >>> hyperfac(z) + 1.28170142849352 + +The hyperfactorial may also be computed using the integral +definition:: + + >>> z = 2.5 + >>> hyperfac(z) + 15.9842119922237 + >>> (2*pi)**(-z/2)*exp(binomial(z+1,2) + + ... quad(lambda t: loggamma(t+1), [0, z])) + 15.9842119922237 + +:func:`hyperfac` supports arbitrary-precision evaluation:: + + >>> mp.dps = 50 + >>> hyperfac(10) + 215779412229418562091680268288000000000000000.0 + >>> hyperfac(1/sqrt(2)) + 0.89404818005227001975423476035729076375705084390942 + +**References** + +1. http://www.research.att.com/~njas/sequences/A002109 +2. http://mathworld.wolfram.com/Hyperfactorial.html + +""" + + +loggamma = r""" +Computes the log-gamma function. Unlike `\ln(\Gamma(z))`, which +has infinitely many complex branch cuts, the log-gamma function +only has a single branch cut along the negative half-axis. +The functions are identical only on (and very close to) the positive +half-axis; elsewhere they differ by `2 n \pi i` (the real parts +agree):: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> loggamma(13.2), log(gamma(13.2)) + (20.494004194566, 20.494004194566) + >>> loggamma(3+4j) + (-1.75662678460378 + 4.74266443803466j) + >>> log(gamma(3+4j)) + (-1.75662678460378 - 1.54052086914493j) + +Note: this is a placeholder implementation. It is slower than +:func:`gamma`, and is in particular *not* faster than :func:`gamma` +for large arguments. +""" + +siegeltheta = r""" +Computes the Riemann-Siegel theta function, + +.. math :: + + \theta(t) = \frac{ + \log\Gamma\left(\frac{1+2it}{4}\right) - + \log\Gamma\left(\frac{1-2it}{4}\right) + }{2i} - \frac{\log \pi}{2} t. + +The Riemann-Siegel theta function is important in +providing the phase factor for the Z-function +(see :func:`siegelz`). Evaluation is supported for real and +complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> siegeltheta(0) + 0.0 + >>> siegeltheta(inf) + +inf + >>> siegeltheta(-inf) + -inf + >>> siegeltheta(1) + -1.767547952812290388302216 + >>> siegeltheta(10+0.25j) + (-3.068638039426838572528867 + 0.05804937947429712998395177j) + +The Riemann-Siegel theta function has odd symmetry around `t = 0`, +two local extreme points and three real roots including 0 (located +symmetrically):: + + >>> nprint(chop(taylor(siegeltheta, 0, 5))) + [0.0, -2.68609, 0.0, 2.69433, 0.0, -6.40218] + >>> findroot(diffun(siegeltheta), 7) + 6.28983598883690277966509 + >>> findroot(siegeltheta, 20) + 17.84559954041086081682634 + +For large `t`, there is a famous asymptotic formula +for `\theta(t)`, to first order given by:: + + >>> t = mpf(10**6) + >>> siegeltheta(t) + 5488816.353078403444882823 + >>> -t*log(2*pi/t)/2-t/2 + 5488816.745777464310273645 +""" + +grampoint = r""" +Gives the `n`-th Gram point `g_n`, defined as the solution +to the equation `\theta(g_n) = \pi n` where `\theta(t)` +is the Riemann-Siegel theta function (:func:`siegeltheta`). + +The first few Gram points are:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> grampoint(0) + 17.84559954041086081682634 + >>> grampoint(1) + 23.17028270124630927899664 + >>> grampoint(2) + 27.67018221781633796093849 + >>> grampoint(3) + 31.71797995476405317955149 + +Checking the definition:: + + >>> siegeltheta(grampoint(3)) + 9.42477796076937971538793 + >>> 3*pi + 9.42477796076937971538793 + +A large Gram point:: + + >>> grampoint(10**10) + 3293531632.728335454561153 + +Gram points are useful when studying the Z-function +(:func:`siegelz`). See the documentation of that function +for additional examples. + +:func:`grampoint` can solve the defining equation for +nonintegral `n`. There is a fixed point where `g(x) = x`:: + + >>> findroot(lambda x: grampoint(x) - x, 10000) + 9146.698193171459265866198 + +**References** + +1. http://mathworld.wolfram.com/GramPoint.html + +""" + +siegelz = r""" +Computes the Z-function, also known as the Riemann-Siegel Z function, + +.. math :: + + Z(t) = e^{i \theta(t)} \zeta(1/2+it) + +where `\zeta(s)` is the Riemann zeta function (:func:`zeta`) +and where `\theta(t)` denotes the Riemann-Siegel theta function +(see :func:`siegeltheta`). + +Evaluation is supported for real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> siegelz(1) + -0.7363054628673177346778998 + >>> siegelz(3+4j) + (-0.1852895764366314976003936 - 0.2773099198055652246992479j) + +The Z-function has a Maclaurin expansion:: + + >>> nprint(chop(taylor(siegelz, 0, 4))) + [-1.46035, 0.0, 2.73588, 0.0, -8.39357] + +The Z-function `Z(t)` is equal to `\pm |\zeta(s)|` on the +critical line `s = 1/2+it` (i.e. for real arguments `t` +to `Z`). Its zeros coincide with those of the Riemann zeta +function:: + + >>> findroot(siegelz, 14) + 14.13472514173469379045725 + >>> findroot(siegelz, 20) + 21.02203963877155499262848 + >>> findroot(zeta, 0.5+14j) + (0.5 + 14.13472514173469379045725j) + >>> findroot(zeta, 0.5+20j) + (0.5 + 21.02203963877155499262848j) + +Since the Z-function is real-valued on the critical line +(and unlike `|\zeta(s)|` analytic), it is useful for +investigating the zeros of the Riemann zeta function. +For example, one can use a root-finding algorithm based +on sign changes:: + + >>> findroot(siegelz, [100, 200], solver='bisect') + 176.4414342977104188888926 + +To locate roots, Gram points `g_n` which can be computed +by :func:`grampoint` are useful. If `(-1)^n Z(g_n)` is +positive for two consecutive `n`, then `Z(t)` must have +a zero between those points:: + + >>> g10 = grampoint(10) + >>> g11 = grampoint(11) + >>> (-1)**10 * siegelz(g10) > 0 + True + >>> (-1)**11 * siegelz(g11) > 0 + True + >>> findroot(siegelz, [g10, g11], solver='bisect') + 56.44624769706339480436776 + >>> g10, g11 + (54.67523744685325626632663, 57.54516517954725443703014) + +""" + +zetazero = r""" +Returns the `n`-th nontrivial zero of the Riemann zeta function. +The zero is computed using :func:`findroot`, using a table lookup +for the initial point. + +The zeros are located on the critical line with real part 1/2:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> zetazero(1) + (0.5 + 14.13472514173469379045725j) + >>> zetazero(2) + (0.5 + 21.02203963877155499262848j) + >>> zetazero(20) + (0.5 + 77.14484006887480537268266j) + +Negative indices give the conjugate zeros (`n = 0` is undefined):: + + >>> zetazero(-1) + (0.5 - 14.13472514173469379045725j) + +The default table only provides `n` up to 100. For larger `n` up to +100,000, :func:`zetazero` will automatically download a table +(1.8 MB) from the website of Andrew Odlyzko [1]. This requires a +fast connection to the internet. Alternatively, you can supply the +url to a custom table. The table should be a file listing the +imaginary parts as float literals, separated by line breaks. + +1. http://www.dtc.umn.edu/~odlyzko/zeta_tables/ +""" + +riemannr = r""" +Evaluates the Riemann R function, a smooth approximation of the +prime counting function `\pi(x)` (see :func:`primepi`). The Riemann +R function gives a fast numerical approximation useful e.g. to +roughly estimate the number of primes in a given interval. + +The Riemann R function is computed using the rapidly convergent Gram +series, + +.. math :: + + R(x) = 1 + \sum_{k=1}^{\infty} + \frac{\log^k x}{k k! \zeta(k+1)}. + +From the Gram series, one sees that the Riemann R function is a +well-defined analytic function (except for a branch cut along +the negative real half-axis); it can be evaluated for arbitrary +real or complex arguments. + +The Riemann R function gives a very accurate approximation +of the prime counting function. For example, it is wrong by at +most 2 for `x < 1000`, and for `x = 10^9` differs from the exact +value of `\pi(x)` by 79, or less than two parts in a million. +It is about 10 times more accurate than the logarithmic integral +estimate (see :func:`li`), which however is even faster to evaluate. +It is orders of magnitude more accurate than the extremely +fast `x/\log x` estimate. + +**Examples** + +For small arguments, the Riemann R function almost exactly +gives the prime counting function if rounded to the nearest +integer:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> primepi(50), riemannr(50) + (15, 14.9757023241462) + >>> max(abs(primepi(n)-round(riemannr(n))) for n in range(100)) + 1.0 + >>> max(abs(primepi(n)-round(riemannr(n))) for n in range(300)) + 2.0 + +The Riemann R function can be evaluated for arguments far too large +for exact determination of `\pi(x)` to be computationally +feasible with any presently known algorithm:: + + >>> riemannr(10**30) + 1.46923988977204e+28 + >>> riemannr(10**100) + 4.3619719871407e+97 + >>> riemannr(10**1000) + 4.3448325764012e+996 + +A comparison of the Riemann R function and logarithmic integral estimates +for `\pi(x)` using exact values of `\pi(10^n)` up to `n = 9`. +The fractional error is shown in parentheses:: + + >>> exact = [4,25,168,1229,9592,78498,664579,5761455,50847534] + >>> for n, p in enumerate(exact): + ... n += 1 + ... r, l = riemannr(10**n), li(10**n) + ... rerr, lerr = nstr((r-p)/p,3), nstr((l-p)/p,3) + ... print "%i %i %s(%s) %s(%s)" % (n, p, r, rerr, l, lerr) + ... + 1 4 4.56458314100509(0.141) 6.1655995047873(0.541) + 2 25 25.6616332669242(0.0265) 30.1261415840796(0.205) + 3 168 168.359446281167(0.00214) 177.609657990152(0.0572) + 4 1229 1226.93121834343(-0.00168) 1246.13721589939(0.0139) + 5 9592 9587.43173884197(-0.000476) 9629.8090010508(0.00394) + 6 78498 78527.3994291277(0.000375) 78627.5491594622(0.00165) + 7 664579 664667.447564748(0.000133) 664918.405048569(0.000511) + 8 5761455 5761551.86732017(1.68e-5) 5762209.37544803(0.000131) + 9 50847534 50847455.4277214(-1.55e-6) 50849234.9570018(3.35e-5) + +The derivative of the Riemann R function gives the approximate +probability for a number of magnitude `x` to be prime:: + + >>> diff(riemannr, 1000) + 0.141903028110784 + >>> mpf(primepi(1050) - primepi(950)) / 100 + 0.15 + +Evaluation is supported for arbitrary arguments and at arbitrary +precision:: + + >>> mp.dps = 30 + >>> riemannr(7.5) + 3.72934743264966261918857135136 + >>> riemannr(-4+2j) + (-0.551002208155486427591793957644 + 2.16966398138119450043195899746j) + +""" + +primepi = r""" +Evaluates the prime counting function, `\pi(x)`, which gives +the number of primes less than or equal to `x`. The argument +`x` may be fractional. + +The prime counting function is very expensive to evaluate +precisely for large `x`, and the present implementation is +not optimized in any way. For numerical approximation of the +prime counting function, it is better to use :func:`primepi2` +or :func:`riemannr`. + +Some values of the prime counting function:: + + >>> from mpmath import * + >>> [primepi(k) for k in range(20)] + [0, 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 8] + >>> primepi(3.5) + 2 + >>> primepi(100000) + 9592 + +""" + +primepi2 = r""" +Returns an interval (as an ``mpi`` instance) providing bounds +for the value of the prime counting function `\pi(x)`. For small +`x`, :func:`primepi2` returns an exact interval based on +the output of :func:`primepi`. For `x > 2656`, a loose interval +based on Schoenfeld's inequality + +.. math :: + + |\pi(x) - \mathrm{li}(x)| < \frac{\sqrt x \log x}{8 \pi} + +is returned. This estimate is rigorous assuming the truth of +the Riemann hypothesis, and can be computed very quickly. + +**Examples** + +Exact values of the prime counting function for small `x`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> primepi2(10) + [4.0, 4.0] + >>> primepi2(100) + [25.0, 25.0] + >>> primepi2(1000) + [168.0, 168.0] + +Loose intervals are generated for moderately large `x`: + + >>> primepi2(10000), primepi(10000) + ([1209.0, 1283.0], 1229) + >>> primepi2(50000), primepi(50000) + ([5070.0, 5263.0], 5133) + +As `x` increases, the absolute error gets worse while the relative +error improves. The exact value of `\pi(10^{23})` is +1925320391606803968923, and :func:`primepi2` gives 9 significant +digits:: + + >>> p = primepi2(10**23) + >>> p + [1.9253203909477020467e+21, 1.925320392280406229e+21] + >>> p.delta / p.a + 6.9219865355293e-10 + +A more precise, nonrigorous estimate for `\pi(x)` can be +obtained using the Riemann R function (:func:`riemannr`). +For large enough `x`, the value returned by :func:`primepi2` +essentially amounts to a small perturbation of the value returned by +:func:`riemannr`:: + + >>> primepi2(10**100) + [4.3619719871407024816e+97, 4.3619719871407032404e+97] + >>> riemannr(10**100) + 4.3619719871407e+97 +""" + +primezeta = r""" +Computes the prime zeta function, which is defined +in analogy with the Riemann zeta function (:func:`zeta`) +as + +.. math :: + + P(s) = \sum_p \frac{1}{p^s} + +where the sum is taken over all prime numbers `p`. Although +this sum only converges for `\mathrm{Re}(s) > 1`, the +function is defined by analytic continuation in the +half-plane `\mathrm{Re}(s) > 0`. + +**Examples** + +Arbitrary-precision evaluation for real and complex arguments is +supported:: + + >>> from mpmath import * + >>> mp.dps = 30; mp.pretty = True + >>> primezeta(2) + 0.452247420041065498506543364832 + >>> primezeta(pi) + 0.15483752698840284272036497397 + >>> mp.dps = 50 + >>> primezeta(3) + 0.17476263929944353642311331466570670097541212192615 + >>> mp.dps = 20 + >>> primezeta(3+4j) + (-0.12085382601645763295 - 0.013370403397787023602j) + +The prime zeta function has a logarithmic pole at `s = 1`, +with residue equal to the difference of the Mertens and +Euler constants:: + + >>> primezeta(1) + +inf + >>> extradps(25)(lambda x: primezeta(1+x)+log(x))(+eps) + -0.31571845205389007685 + >>> mertens-euler + -0.31571845205389007685 + +The analytic continuation to `0 < \mathrm{Re}(s) \le 1` +is implemented. In this strip the function exhibits +very complex behavior; on the unit interval, it has poles at +`1/n` for every squarefree integer `n`:: + + >>> primezeta(0.5) # Pole at s = 1/2 + (-inf + 3.1415926535897932385j) + >>> primezeta(0.25) + (-1.0416106801757269036 + 0.52359877559829887308j) + >>> primezeta(0.5+10j) + (0.54892423556409790529 + 0.45626803423487934264j) + +Although evaluation works in principle for any `\mathrm{Re}(s) > 0`, +it should be noted that the evaluation time increases exponentially +as `s` approaches the imaginary axis. + +For large `\mathrm{Re}(s)`, `P(s)` is asymptotic to `2^{-s}`:: + + >>> primezeta(inf) + 0.0 + >>> primezeta(10), mpf(2)**-10 + (0.00099360357443698021786, 0.0009765625) + >>> primezeta(1000) + 9.3326361850321887899e-302 + >>> primezeta(1000+1000j) + (-3.8565440833654995949e-302 - 8.4985390447553234305e-302j) + +**References** + +Carl-Erik Froberg, "On the prime zeta function", +BIT 8 (1968), pp. 187-202. + +""" + +bernpoly = r""" +Evaluates the Bernoulli polynomial `B_n(z)`. + +The first few Bernoulli polynomials are:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(6): + ... nprint(chop(taylor(lambda x: bernpoly(n,x), 0, n))) + ... + [1.0] + [-0.5, 1.0] + [0.166667, -1.0, 1.0] + [0.0, 0.5, -1.5, 1.0] + [-0.0333333, 0.0, 1.0, -2.0, 1.0] + [0.0, -0.166667, 0.0, 1.66667, -2.5, 1.0] + +At `z = 0`, the Bernoulli polynomial evaluates to a +Bernoulli number (see :func:`bernoulli`):: + + >>> bernpoly(12, 0), bernoulli(12) + (-0.253113553113553, -0.253113553113553) + >>> bernpoly(13, 0), bernoulli(13) + (0.0, 0.0) + +Evaluation is accurate for large `n` and small `z`:: + + >>> mp.dps = 25 + >>> bernpoly(100, 0.5) + 2.838224957069370695926416e+78 + >>> bernpoly(1000, 10.5) + 5.318704469415522036482914e+1769 + +""" + +polylog = r""" +Computes the polylogarithm, defined by the sum + +.. math :: + + \mathrm{Li}_s(z) = \sum_{k=1}^{\infty} \frac{z^k}{k^s}. + +This series is convergent only for `|z| < 1`, so elsewhere +the analytic continuation is implied. + +The polylogarithm should not be confused with the logarithmic +integral (also denoted by Li or li), which is implemented +as :func:`li`. + +**Examples** + +The polylogarithm satisfies a huge number of functional identities. +A sample of polylogarithm evaluations is shown below:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> polylog(1,0.5), log(2) + (0.693147180559945, 0.693147180559945) + >>> polylog(2,0.5), (pi**2-6*log(2)**2)/12 + (0.582240526465012, 0.582240526465012) + >>> polylog(2,-phi), -log(phi)**2-pi**2/10 + (-1.21852526068613, -1.21852526068613) + >>> polylog(3,0.5), 7*zeta(3)/8-pi**2*log(2)/12+log(2)**3/6 + (0.53721319360804, 0.53721319360804) + +:func:`polylog` can evaluate the analytic continuation of the +polylogarithm when `s` is an integer:: + + >>> polylog(2, 10) + (0.536301287357863 - 7.23378441241546j) + >>> polylog(2, -10) + -4.1982778868581 + >>> polylog(2, 10j) + (-3.05968879432873 + 3.71678149306807j) + >>> polylog(-2, 10) + -0.150891632373114 + >>> polylog(-2, -10) + 0.067618332081142 + >>> polylog(-2, 10j) + (0.0384353698579347 + 0.0912451798066779j) + +Some more examples, with arguments on the unit circle (note that +the series definition cannot be used for computation here):: + + >>> polylog(2,j) + (-0.205616758356028 + 0.915965594177219j) + >>> j*catalan-pi**2/48 + (-0.205616758356028 + 0.915965594177219j) + >>> polylog(3,exp(2*pi*j/3)) + (-0.534247512515375 + 0.765587078525922j) + >>> -4*zeta(3)/9 + 2*j*pi**3/81 + (-0.534247512515375 + 0.765587078525921j) + +Polylogarithms of different order are related by integration +and differentiation:: + + >>> s, z = 3, 0.5 + >>> polylog(s+1, z) + 0.517479061673899 + >>> quad(lambda t: polylog(s,t)/t, [0, z]) + 0.517479061673899 + >>> z*diff(lambda t: polylog(s+2,t), z) + 0.517479061673899 + +Taylor series expansions around `z = 0` are:: + + >>> for n in range(-3, 4): + ... nprint(taylor(lambda x: polylog(n,x), 0, 5)) + ... + [0.0, 1.0, 8.0, 27.0, 64.0, 125.0] + [0.0, 1.0, 4.0, 9.0, 16.0, 25.0] + [0.0, 1.0, 2.0, 3.0, 4.0, 5.0] + [0.0, 1.0, 1.0, 1.0, 1.0, 1.0] + [0.0, 1.0, 0.5, 0.333333, 0.25, 0.2] + [0.0, 1.0, 0.25, 0.111111, 0.0625, 0.04] + [0.0, 1.0, 0.125, 0.037037, 0.015625, 0.008] + +The series defining the polylogarithm is simultaneously +a Taylor series and an L-series. For certain values of `z`, the +polylogarithm reduces to a pure zeta function:: + + >>> polylog(pi, 1), zeta(pi) + (1.17624173838258, 1.17624173838258) + >>> polylog(pi, -1), -altzeta(pi) + (-0.909670702980385, -0.909670702980385) + +Evaluation for arbitrary, nonintegral `s` is supported +for `z` within the unit circle: + + >>> polylog(3+4j, 0.25) + (0.24258605789446 - 0.00222938275488344j) + >>> nsum(lambda k: 0.25**k / k**(3+4j), [1,inf]) + (0.24258605789446 - 0.00222938275488344j) + +It is also currently supported outside of the unit circle for `z` +not too large in magnitude:: + + >>> polylog(1+j, 20+40j) + (-7.1421172179728 - 3.92726697721369j) + >>> polylog(1+j, 200+400j) + Traceback (most recent call last): + ... + NotImplementedError: polylog for arbitrary s and z + +**References** + +1. Richard Crandall, "Note on fast polylogarithm computation" + http://people.reed.edu/~crandall/papers/Polylog.pdf +2. http://en.wikipedia.org/wiki/Polylogarithm +3. http://mathworld.wolfram.com/Polylogarithm.html + +""" + +bell = r""" +For `n` a nonnegative integer, ``bell(n,x)`` evaluates the Bell +polynomial `B_n(x)`, the first few of which are + +.. math :: + + B_0(x) = 1 + + B_1(x) = x + + B_2(x) = x^2+x + + B_3(x) = x^3+3x^2+x + +If `x = 1` or :func:`bell` is called with only one argument, it +gives the `n`-th Bell number `B_n`, which is the number of +partitions of a set with `n` elements. By setting the precision to +at least `\log_{10} B_n` digits, :func:`bell` provides fast +calculation of exact Bell numbers. + +In general, :func:`bell` computes + +.. math :: + + B_n(x) = e^{-x} \left(\mathrm{sinc}(\pi n) + E_n(x)\right) + +where `E_n(x)` is the generalized exponential function implemented +by :func:`polyexp`. This is an extension of Dobinski's formula [1], +where the modification is the sinc term ensuring that `B_n(x)` is +continuous in `n`; :func:`bell` can thus be evaluated, +differentiated, etc for arbitrary complex arguments. + +**Examples** + +Simple evaluations:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> bell(0, 2.5) + 1.0 + >>> bell(1, 2.5) + 2.5 + >>> bell(2, 2.5) + 8.75 + +Evaluation for arbitrary complex arguments:: + + >>> bell(5.75+1j, 2-3j) + (-10767.71345136587098445143 - 15449.55065599872579097221j) + +The first few Bell polynomials:: + + >>> for k in range(7): + ... nprint(taylor(lambda x: bell(k,x), 0, k)) + ... + [1.0] + [0.0, 1.0] + [0.0, 1.0, 1.0] + [0.0, 1.0, 3.0, 1.0] + [0.0, 1.0, 7.0, 6.0, 1.0] + [0.0, 1.0, 15.0, 25.0, 10.0, 1.0] + [0.0, 1.0, 31.0, 90.0, 65.0, 15.0, 1.0] + +The first few Bell numbers and complementary Bell numbers:: + + >>> [int(bell(k)) for k in range(10)] + [1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147] + >>> [int(bell(k,-1)) for k in range(10)] + [1, -1, 0, 1, 1, -2, -9, -9, 50, 267] + +Large Bell numbers:: + + >>> mp.dps = 50 + >>> bell(50) + 185724268771078270438257767181908917499221852770.0 + >>> bell(50,-1) + -29113173035759403920216141265491160286912.0 + +Some even larger values:: + + >>> mp.dps = 25 + >>> bell(1000,-1) + -1.237132026969293954162816e+1869 + >>> bell(1000) + 2.989901335682408421480422e+1927 + >>> bell(1000,2) + 6.591553486811969380442171e+1987 + >>> bell(1000,100.5) + 9.101014101401543575679639e+2529 + +A determinant identity satisfied by Bell numbers:: + + >>> mp.dps = 15 + >>> N = 8 + >>> det([[bell(k+j) for j in range(N)] for k in range(N)]) + 125411328000.0 + >>> superfac(N-1) + 125411328000.0 + +**References** + +1. http://mathworld.wolfram.com/DobinskisFormula.html + +""" + +polyexp = r""" +Evaluates the polyexponential function, defined for arbitrary +complex `s`, `z` by the series + +.. math :: + + E_s(z) = \sum_{k=1}^{\infty} \frac{k^s}{k!} z^k. + +`E_s(z)` is constructed from the exponential function analogously +to how the polylogarithm is constructed from the ordinary +logarithm; as a function of `s` (with `z` fixed), `E_s` is an L-series +It is an entire function of both `s` and `z`. + +The polyexponential function provides a generalization of the +Bell polynomials `B_n(x)` (see :func:`bell`) to noninteger orders `n`. +In terms of the Bell polynomials, + +.. math :: + + E_s(z) = e^z B_s(z) - \mathrm{sinc}(\pi s). + +Note that `B_n(x)` and `e^{-x} E_n(x)` are identical if `n` +is a nonzero integer, but not otherwise. In particular, they differ +at `n = 0`. + +**Examples** + +Evaluating a series:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> nsum(lambda k: sqrt(k)/fac(k), [1,inf]) + 2.101755547733791780315904 + >>> polyexp(0.5,1) + 2.101755547733791780315904 + +Evaluation for arbitrary arguments:: + + >>> polyexp(-3-4j, 2.5+2j) + (2.351660261190434618268706 + 1.202966666673054671364215j) + +Evaluation is accurate for tiny function values:: + + >>> polyexp(4, -100) + 3.499471750566824369520223e-36 + +If `n` is a nonpositive integer, `E_n` reduces to a special +instance of the hypergeometric function `\,_pF_q`:: + + >>> n = 3 + >>> x = pi + >>> polyexp(-n,x) + 4.042192318847986561771779 + >>> x*hyper([1]*(n+1), [2]*(n+1), x) + 4.042192318847986561771779 + +""" + +cyclotomic = r""" +Evaluates the cyclotomic polynomial `\Phi_n(x)`, defined by + +.. math :: + + \Phi_n(x) = \prod_{\zeta} (x - \zeta) + +where `\zeta` ranges over all primitive `n`-th roots of unity +(see :func:`unitroots`). An equivalent representation, used +for computation, is + +.. math :: + + \Phi_n(x) = \prod_{d\mid n}(x^d-1)^{\mu(n/d)} = \Phi_n(x) + +where `\mu(m)` denotes the Moebius function. The cyclotomic +polynomials are integer polynomials, the first of which can be +written explicitly as + +.. math :: + + \Phi_0(x) = 1 + + \Phi_1(x) = x - 1 + + \Phi_2(x) = x + 1 + + \Phi_3(x) = x^3 + x^2 + 1 + + \Phi_4(x) = x^2 + 1 + + \Phi_5(x) = x^4 + x^3 + x^2 + x + 1 + + \Phi_6(x) = x^2 - x + 1 + +**Examples** + +The coefficients of low-order cyclotomic polynomials can be recovered +using Taylor expansion:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> for n in range(9): + ... p = chop(taylor(lambda x: cyclotomic(n,x), 0, 10)) + ... print n,; nprint(p[:10+1-p[::-1].index(1)]) + ... + 0 [1.0] + 1 [-1.0, 1.0] + 2 [1.0, 1.0] + 3 [1.0, 1.0, 1.0] + 4 [1.0, 0.0, 1.0] + 5 [1.0, 1.0, 1.0, 1.0, 1.0] + 6 [1.0, -1.0, 1.0] + 7 [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0] + 8 [1.0, 0.0, 0.0, 0.0, 1.0] + +The definition as a product over primitive roots may be checked +by computing the product explicitly (for a real argument, this +method will generally introduce numerical noise in the imaginary +part):: + + >>> mp.dps = 25 + >>> z = 3+4j + >>> cyclotomic(10, z) + (-419.0 - 360.0j) + >>> fprod(z-r for r in unitroots(10, primitive=True)) + (-419.0 - 360.0j) + >>> z = 3 + >>> cyclotomic(10, z) + 61.0 + >>> fprod(z-r for r in unitroots(10, primitive=True)) + (61.0 - 3.146045605088568607055454e-25j) + +Up to permutation, the roots of a given cyclotomic polynomial +can be checked to agree with the list of primitive roots:: + + >>> p = taylor(lambda x: cyclotomic(6,x), 0, 6)[:3] + >>> for r in polyroots(p[::-1]): + ... print r + ... + (0.5 - 0.8660254037844386467637232j) + (0.5 + 0.8660254037844386467637232j) + >>> + >>> for r in unitroots(6, primitive=True): + ... print r + ... + (0.5 + 0.8660254037844386467637232j) + (0.5 - 0.8660254037844386467637232j) + +""" + +meijerg = r""" +Evaluates the Meijer G-function, defined as + +.. math :: + + G^{m,n}_{p,q} \left( \left. \begin{matrix} + a_1, \dots, a_n ; a_{n+1} \dots a_p \\ + b_1, \dots, b_m ; b_{m+1} \dots b_q + \end{matrix}\; \right| \; z ; r \right) = + \frac{1}{2 \pi i} \int_L + \frac{\prod_{j=1}^m \Gamma(b_j+s) \prod_{j=1}^n\Gamma(1-a_j-s)} + {\prod_{j=n+1}^{p}\Gamma(a_j+s) \prod_{j=m+1}^q \Gamma(1-b_j-s)} + z^{-s/r} ds + +for an appropriate choice of the contour `L` (see references). + +There are `p` elements `a_j`. +The argument *a_s* should be a pair of lists, the first containing the +`n` elements `a_1, \ldots, a_n` and the second containing +the `p-n` elements `a_{n+1}, \ldots a_p`. + +There are `q` elements `b_j`. +The argument *b_s* should be a pair of lists, the first containing the +`m` elements `b_1, \ldots, b_m` and the second containing +the `q-m` elements `b_{m+1}, \ldots b_q`. + +The implicit tuple `(m, n, p, q)` constitutes the order or degree of the +Meijer G-function, and is determined by the lengths of the coefficient +vectors. Confusingly, the indices in this tuple appear in a different order +from the coefficients, but this notation is standard. The many examples +given below should hopefully clear up any potential confusion. + +**Algorithm** + +The Meijer G-function is evaluated as a combination of hypergeometric series. +There are two versions of the function, which can be selected with +the optional *series* argument. + +*series=1* uses a sum of `m` `\,_pF_{q-1}` functions of `z` + +*series=2* uses a sum of `n` `\,_qF_{p-1}` functions of `1/z` + +The default series is chosen based on the degree and `|z|` in order +to be consistent with Mathematica's. This definition of the Meijer G-function +has a discontinuity at `|z| = 1` for some orders, which can +be avoided by explicitly specifying a series. + +Keyword arguments are forwarded to :func:`hypercomb`. + +**Examples** + +Many standard functions are special cases of the Meijer G-function +(possibly rescaled and/or with branch cut corrections). We define +some test parameters:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> a = mpf(0.75) + >>> b = mpf(1.5) + >>> z = mpf(2.25) + +The exponential function: +`e^z = G^{1,0}_{0,1} \left( \left. \begin{matrix} - \\ 0 \end{matrix} \; +\right| \; -z \right)` + + >>> meijerg([[],[]], [[0],[]], -z) + 9.487735836358525720550369 + >>> exp(z) + 9.487735836358525720550369 + +The natural logarithm: +`\log(1+z) = G^{1,2}_{2,2} \left( \left. \begin{matrix} 1, 1 \\ 1, 0 +\end{matrix} \; \right| \; -z \right)` + + >>> meijerg([[1,1],[]], [[1],[0]], z) + 1.178654996341646117219023 + >>> log(1+z) + 1.178654996341646117219023 + +A rational function: +`\frac{z}{z+1} = G^{1,2}_{2,2} \left( \left. \begin{matrix} 1, 1 \\ 1, 1 +\end{matrix} \; \right| \; z \right)` + + >>> meijerg([[1,1],[]], [[1],[1]], z) + 0.6923076923076923076923077 + >>> z/(z+1) + 0.6923076923076923076923077 + +The sine and cosine functions: + +`\frac{1}{\sqrt \pi} \sin(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} +- \\ \frac{1}{2}, 0 \end{matrix} \; \right| \; z \right)` + +`\frac{1}{\sqrt \pi} \cos(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. \begin{matrix} +- \\ 0, \frac{1}{2} \end{matrix} \; \right| \; z \right)` + + >>> meijerg([[],[]], [[0.5],[0]], (z/2)**2) + 0.4389807929218676682296453 + >>> sin(z)/sqrt(pi) + 0.4389807929218676682296453 + >>> meijerg([[],[]], [[0],[0.5]], (z/2)**2) + -0.3544090145996275423331762 + >>> cos(z)/sqrt(pi) + -0.3544090145996275423331762 + +Bessel functions: + +`J_a(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. +\begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} +\end{matrix} \; \right| \; z \right)` + +`Y_a(2 \sqrt z) = G^{2,0}_{1,3} \left( \left. +\begin{matrix} \frac{-a-1}{2} \\ \frac{a}{2}, -\frac{a}{2}, \frac{-a-1}{2} +\end{matrix} \; \right| \; z \right)` + +`(-z)^{a/2} z^{-a/2} I_a(2 \sqrt z) = G^{1,0}_{0,2} \left( \left. +\begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} +\end{matrix} \; \right| \; -z \right)` + +`2 K_a(2 \sqrt z) = G^{2,0}_{0,2} \left( \left. +\begin{matrix} - \\ \frac{a}{2}, -\frac{a}{2} +\end{matrix} \; \right| \; z \right)` + +As the example with the Bessel *I* function shows, a branch +factor is required for some arguments when inverting the square root. + + >>> meijerg([[],[]], [[a/2],[-a/2]], (z/2)**2) + 0.5059425789597154858527264 + >>> besselj(a,z) + 0.5059425789597154858527264 + >>> meijerg([[],[(-a-1)/2]], [[a/2,-a/2],[(-a-1)/2]], (z/2)**2) + 0.1853868950066556941442559 + >>> bessely(a, z) + 0.1853868950066556941442559 + >>> meijerg([[],[]], [[a/2],[-a/2]], -(z/2)**2) + (0.8685913322427653875717476 + 2.096964974460199200551738j) + >>> (-z)**(a/2) / z**(a/2) * besseli(a, z) + (0.8685913322427653875717476 + 2.096964974460199200551738j) + >>> 0.5*meijerg([[],[]], [[a/2,-a/2],[]], (z/2)**2) + 0.09334163695597828403796071 + >>> besselk(a,z) + 0.09334163695597828403796071 + +Error functions: + +`\sqrt{\pi} z^{2(a-1)} \mathrm{erfc}(z) = G^{2,0}_{1,2} \left( \left. +\begin{matrix} a \\ a-1, a-\frac{1}{2} +\end{matrix} \; \right| \; z, \frac{1}{2} \right)` + + >>> meijerg([[],[a]], [[a-1,a-0.5],[]], z, 0.5) + 0.00172839843123091957468712 + >>> sqrt(pi) * z**(2*a-2) * erfc(z) + 0.00172839843123091957468712 + +A Meijer G-function of higher degree, (1,1,2,3): + + >>> meijerg([[a],[b]], [[a],[b,a-1]], z) + 1.55984467443050210115617 + >>> sin((b-a)*pi)/pi*(exp(z)-1)*z**(a-1) + 1.55984467443050210115617 + +A Meijer G-function of still higher degree, (4,1,2,4), that can +be expanded as a messy combination of exponential integrals: + + >>> meijerg([[a],[2*b-a]], [[b,a,b-0.5,-1-a+2*b],[]], z) + 0.3323667133658557271898061 + >>> chop(4**(a-b+1)*sqrt(pi)*gamma(2*b-2*a)*z**a*\ + ... expint(2*b-2*a, -2*sqrt(-z))*expint(2*b-2*a, 2*sqrt(-z))) + 0.3323667133658557271898061 + +In the following case, different series give different values:: + + >>> chop(meijerg([[1],[0.25]],[[3],[0.5]],-2)) + -0.06417628097442437076207337 + >>> meijerg([[1],[0.25]],[[3],[0.5]],-2,series=1) + 0.1428699426155117511873047 + >>> chop(meijerg([[1],[0.25]],[[3],[0.5]],-2,series=2)) + -0.06417628097442437076207337 + +**References** + +1. http://en.wikipedia.org/wiki/Meijer_G-function + +2. http://mathworld.wolfram.com/MeijerG-Function.html + +3. http://functions.wolfram.com/HypergeometricFunctions/MeijerG/ + +4. http://functions.wolfram.com/HypergeometricFunctions/MeijerG1/ + +""" + +clsin = r""" +Computes the Clausen sine function, defined formally by the series + +.. math :: + + \mathrm{Cl}_s(z) = \sum_{k=1}^{\infty} \frac{\sin(kz)}{k^s}. + +The special case `\mathrm{Cl}_2(z)` (i.e. ``clsin(2,z)``) is the classical +"Clausen function". More generally, the Clausen function is defined for +complex `s` and `z`, even when the series does not converge. The +Clausen function is related to the polylogarithm (:func:`polylog`) as + +.. math :: + + \mathrm{Cl}_s(z) = \frac{1}{2i}\left(\mathrm{Li}_s\left(e^{iz}\right) - + \mathrm{Li}_s\left(e^{-iz}\right)\right) + + = \mathrm{Im}\left[\mathrm{Li}_s(e^{iz})\right] \quad (s, z \in \mathbb{R}), + +and this representation can be taken to provide the analytic continuation of the +series. The complementary function :func:`clcos` gives the corresponding +cosine sum. + +**Examples** + +Evaluation for arbitrarily chosen `s` and `z`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> s, z = 3, 4 + >>> clsin(s, z); nsum(lambda k: sin(z*k)/k**s, [1,inf]) + -0.6533010136329338746275795 + -0.6533010136329338746275795 + +Using `z + \pi` instead of `z` gives an alternating series:: + + >>> clsin(s, z+pi) + 0.8860032351260589402871624 + >>> nsum(lambda k: (-1)**k*sin(z*k)/k**s, [1,inf]) + 0.8860032351260589402871624 + +With `s = 1`, the sum can be expressed in closed form +using elementary functions:: + + >>> z = 1 + sqrt(3) + >>> clsin(1, z) + 0.2047709230104579724675985 + >>> chop((log(1-exp(-j*z)) - log(1-exp(j*z)))/(2*j)) + 0.2047709230104579724675985 + >>> nsum(lambda k: sin(k*z)/k, [1,inf]) + 0.2047709230104579724675985 + +The classical Clausen function `\mathrm{Cl}_2(\theta)` gives the +value of the integral `\int_0^{\theta} -\ln(2\sin(x/2)) dx` for +`0 < \theta < 2 \pi`:: + + >>> cl2 = lambda t: clsin(2, t) + >>> cl2(3.5) + -0.2465045302347694216534255 + >>> -quad(lambda x: ln(2*sin(0.5*x)), [0, 3.5]) + -0.2465045302347694216534255 + +This function is symmetric about `\theta = \pi` with zeros and extreme +points:: + + >>> cl2(0); cl2(pi/3); chop(cl2(pi)); cl2(5*pi/3); chop(cl2(2*pi)) + 0.0 + 1.014941606409653625021203 + 0.0 + -1.014941606409653625021203 + 0.0 + +Catalan's constant is a special value:: + + >>> cl2(pi/2) + 0.9159655941772190150546035 + >>> +catalan + 0.9159655941772190150546035 + +The Clausen sine function can be expressed in closed form when +`s` is an odd integer (becoming zero when `s` < 0):: + + >>> z = 1 + sqrt(2) + >>> clsin(1, z); (pi-z)/2 + 0.3636895456083490948304773 + 0.3636895456083490948304773 + >>> clsin(3, z); pi**2/6*z - pi*z**2/4 + z**3/12 + 0.5661751584451144991707161 + 0.5661751584451144991707161 + >>> clsin(-1, z) + 0.0 + >>> clsin(-3, z) + 0.0 + +It can also be expressed in closed form for even integer `s \le 0`, +providing a finite sum for series such as +`\sin(z) + \sin(2z) + \sin(3z) + \ldots`:: + + >>> z = 1 + sqrt(2) + >>> clsin(0, z) + 0.1903105029507513881275865 + >>> cot(z/2)/2 + 0.1903105029507513881275865 + >>> clsin(-2, z) + -0.1089406163841548817581392 + >>> -cot(z/2)*csc(z/2)**2/4 + -0.1089406163841548817581392 + +Call with ``pi=True`` to multiply `z` by `\pi` exactly:: + + >>> clsin(3, 3*pi) + -8.892316224968072424732898e-26 + >>> clsin(3, 3, pi=True) + 0.0 + +Evaluation for complex `s`, `z` in a nonconvergent case:: + + >>> s, z = -1-j, 1+2j + >>> clsin(s, z) + (-0.593079480117379002516034 + 0.9038644233367868273362446j) + >>> extraprec(20)(nsum)(lambda k: sin(k*z)/k**s, [1,inf]) + (-0.593079480117379002516034 + 0.9038644233367868273362446j) + +""" + +clcos = r""" +Computes the Clausen cosine function, defined formally by the series + +.. math :: + + \mathrm{\widetilde{Cl}}_s(z) = \sum_{k=1}^{\infty} \frac{\cos(kz)}{k^s}. + +This function is complementary to the Clausen sine function +:func:`clsin`. In terms of the polylogarithm, + +.. math :: + + \mathrm{\widetilde{Cl}}_s(z) = + \frac{1}{2}\left(\mathrm{Li}_s\left(e^{iz}\right) + + \mathrm{Li}_s\left(e^{-iz}\right)\right) + + = \mathrm{Re}\left[\mathrm{Li}_s(e^{iz})\right] \quad (s, z \in \mathbb{R}). + +**Examples** + +Evaluation for arbitrarily chosen `s` and `z`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> s, z = 3, 4 + >>> clcos(s, z); nsum(lambda k: cos(z*k)/k**s, [1,inf]) + -0.6518926267198991308332759 + -0.6518926267198991308332759 + +Using `z + \pi` instead of `z` gives an alternating series:: + + >>> s, z = 3, 0.5 + >>> clcos(s, z+pi) + -0.8155530586502260817855618 + >>> nsum(lambda k: (-1)**k*cos(z*k)/k**s, [1,inf]) + -0.8155530586502260817855618 + +With `s = 1`, the sum can be expressed in closed form +using elementary functions:: + + >>> z = 1 + sqrt(3) + >>> clcos(1, z) + -0.6720334373369714849797918 + >>> chop(-0.5*(log(1-exp(j*z))+log(1-exp(-j*z)))) + -0.6720334373369714849797918 + >>> -log(abs(2*sin(0.5*z))) # Equivalent to above when z is real + -0.6720334373369714849797918 + >>> nsum(lambda k: cos(k*z)/k, [1,inf]) + -0.6720334373369714849797918 + +It can also be expressed in closed form when `s` is an even integer. +For example, + + >>> clcos(2,z) + -0.7805359025135583118863007 + >>> pi**2/6 - pi*z/2 + z**2/4 + -0.7805359025135583118863007 + +The case `s = 0` gives the renormalized sum of +`\cos(z) + \cos(2z) + \cos(3z) + \ldots` (which happens to be the same for +any value of `z`):: + + >>> clcos(0, z) + -0.5 + >>> nsum(lambda k: cos(k*z), [1,inf]) + -0.5 + +Also the sums + +.. math :: + + \cos(z) + 2\cos(2z) + 3\cos(3z) + \ldots + +and + +.. math :: + + \cos(z) + 2^n \cos(2z) + 3^n \cos(3z) + \ldots + +for higher integer powers `n = -s` can be done in closed form. They are zero +when `n` is positive and even (`s` negative and even):: + + >>> clcos(-1, z); 1/(2*cos(z)-2) + -0.2607829375240542480694126 + -0.2607829375240542480694126 + >>> clcos(-3, z); (2+cos(z))*csc(z/2)**4/8 + 0.1472635054979944390848006 + 0.1472635054979944390848006 + >>> clcos(-2, z); clcos(-4, z); clcos(-6, z) + 0.0 + 0.0 + 0.0 + +With `z = \pi`, the series reduces to that of the Riemann zeta function +(more generally, if `z = p \pi/q`, it is a finite sum over Hurwitz zeta +function values):: + + >>> clcos(2.5, 0); zeta(2.5) + 1.34148725725091717975677 + 1.34148725725091717975677 + >>> clcos(2.5, pi); -altzeta(2.5) + -0.8671998890121841381913472 + -0.8671998890121841381913472 + +Call with ``pi=True`` to multiply `z` by `\pi` exactly:: + + >>> clcos(-3, 2*pi) + 2.997921055881167659267063e+102 + >>> clcos(-3, 2, pi=True) + 0.008333333333333333333333333 + +Evaluation for complex `s`, `z` in a nonconvergent case:: + + >>> s, z = -1-j, 1+2j + >>> clcos(s, z) + (0.9407430121562251476136807 + 0.715826296033590204557054j) + >>> extraprec(20)(nsum)(lambda k: cos(k*z)/k**s, [1,inf]) + (0.9407430121562251476136807 + 0.715826296033590204557054j) + +""" + +whitm = r""" +Evaluates the Whittaker function `M(k,m,z)`, which gives a solution +to the Whittaker differential equation + +.. math :: + + \frac{d^2f}{dz^2} + \left(-\frac{1}{4}+\frac{k}{z}+ + \frac{(\frac{1}{4}-m^2)}{z^2}\right) f = 0. + +A second solution is given by :func:`whitw`. + +The Whittaker functions are defined in Abramowitz & Stegun, section 13.1. +They are alternate forms of the confluent hypergeometric functions +`\,_1F_1` and `U`: + +.. math :: + + M(k,m,z) = e^{-\frac{1}{2}z} z^{\frac{1}{2}+m} + \,_1F_1(\tfrac{1}{2}+m-k, 1+2m, z) + + W(k,m,z) = e^{-\frac{1}{2}z} z^{\frac{1}{2}+m} + U(\tfrac{1}{2}+m-k, 1+2m, z). + +**Examples** + +Evaluation for arbitrary real and complex arguments is supported:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> whitm(1, 1, 1) + 0.7302596799460411820509668 + >>> whitm(1, 1, -1) + (0.0 - 1.417977827655098025684246j) + >>> whitm(j, j/2, 2+3j) + (3.245477713363581112736478 - 0.822879187542699127327782j) + >>> whitm(2, 3, 100000) + 4.303985255686378497193063e+21707 + +Evaluation at zero:: + + >>> whitm(1,-1,0); whitm(1,-0.5,0); whitm(1,0,0) + +inf + nan + 0.0 + +We can verify that :func:`whitm` numerically satisfies the +differential equation for arbitrarily chosen values:: + + >>> k = mpf(0.25) + >>> m = mpf(1.5) + >>> f = lambda z: whitm(k,m,z) + >>> for z in [-1, 2.5, 3, 1+2j]: + ... chop(diff(f,z,2) + (-0.25 + k/z + (0.25-m**2)/z**2)*f(z)) + ... + 0.0 + 0.0 + 0.0 + 0.0 + +An integral involving both :func:`whitm` and :func:`whitmw`, +verifying evaluation along the real axis:: + + >>> quad(lambda x: exp(-x)*whitm(3,2,x)*whitw(1,-2,x), [0,inf]) + 3.438869842576800225207341 + >>> 128/(21*sqrt(pi)) + 3.438869842576800225207341 + +""" + +whitw = r""" +Evaluates the Whittaker function `W(k,m,z)`, which gives a second +solution to the Whittaker differential equation. (See :func:`whitm`.) + +**Examples** + +Evaluation for arbitrary real and complex arguments is supported:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> whitw(1, 1, 1) + 1.19532063107581155661012 + >>> whitw(1, 1, -1) + (-0.9424875979222187313924639 - 0.2607738054097702293308689j) + >>> whitw(j, j/2, 2+3j) + (0.1782899315111033879430369 - 0.01609578360403649340169406j) + >>> whitw(2, 3, 100000) + 1.887705114889527446891274e-21705 + >>> whitw(-1, -1, 100) + 1.905250692824046162462058e-24 + +Evaluation at zero:: + + >>> for m in [-1, -0.5, 0, 0.5, 1]: + ... whitw(1, m, 0) + ... + +inf + nan + 0.0 + nan + +inf + +We can verify that :func:`whitw` numerically satisfies the +differential equation for arbitrarily chosen values:: + + >>> k = mpf(0.25) + >>> m = mpf(1.5) + >>> f = lambda z: whitw(k,m,z) + >>> for z in [-1, 2.5, 3, 1+2j]: + ... chop(diff(f,z,2) + (-0.25 + k/z + (0.25-m**2)/z**2)*f(z)) + ... + 0.0 + 0.0 + 0.0 + 0.0 + +""" + +ber = r""" +Computes the Kelvin function ber, which for real arguments gives the real part +of the Bessel J function of a rotated argument + +.. math :: + + J_n\left(x e^{3\pi i/4}\right) = \mathrm{ber}_n(x) + i \mathrm{bei}_n(x). + +The imaginary part is given by :func:`bei`. + +**Examples** + +Verifying the defining relation:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> n, x = 2, 3.5 + >>> ber(n,x) + 1.442338852571888752631129 + >>> bei(n,x) + -0.948359035324558320217678 + >>> besselj(n, x*root(1,8,3)) + (1.442338852571888752631129 - 0.948359035324558320217678j) + +The ber and bei functions are also defined by analytic continuation +for complex arguments:: + + >>> ber(1+j, 2+3j) + (4.675445984756614424069563 - 15.84901771719130765656316j) + >>> bei(1+j, 2+3j) + (15.83886679193707699364398 + 4.684053288183046528703611j) + +""" + +bei = r""" +Computes the Kelvin function bei, which for real arguments gives the +imaginary part of the Bessel J function of a rotated argument. +See :func:`ber`. +""" + +ker = r""" +Computes the Kelvin function ker, which for real arguments gives the real part +of the (rescaled) Bessel K function of a rotated argument + +.. math :: + + e^{-\pi i/2} K_n\left(x e^{3\pi i/4}\right) = \mathrm{ker}_n(x) + i \mathrm{kei}_n(x). + +The imaginary part is given by :func:`kei`. + +**Examples** + +Verifying the defining relation:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> n, x = 2, 4.5 + >>> ker(n,x) + 0.02542895201906369640249801 + >>> kei(n,x) + -0.02074960467222823237055351 + >>> exp(-n*pi*j/2) * besselk(n, x*root(1,8,1)) + (0.02542895201906369640249801 - 0.02074960467222823237055351j) + +The ker and kei functions are also defined by analytic continuation +for complex arguments:: + + >>> ker(1+j, 3+4j) + (1.586084268115490421090533 - 2.939717517906339193598719j) + >>> kei(1+j, 3+4j) + (-2.940403256319453402690132 - 1.585621643835618941044855j) + +""" + +kei = r""" +Computes the Kelvin function kei, which for real arguments gives the +imaginary part of the (rescaled) Bessel K function of a rotated argument. +See :func:`ker`. +""" + +struveh = r""" +Gives the Struve function + +.. math :: + + \,\mathbf{H}_n(z) = + \sum_{k=0}^\infty \frac{(-1)^k}{\Gamma(k+\frac{3}{2}) + \Gamma(k+n+\frac{3}{2})} {\left({\frac{z}{2}}\right)}^{2k+n+1} + +which is a solution to the Struve differential equation + +.. math :: + + z^2 f''(z) + z f'(z) + (z^2-n^2) f(z) = \frac{2 z^{n+1}}{\pi (2n-1)!!}. + +**Examples** + +Evaluation for arbitrary real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> struveh(0, 3.5) + 0.3608207733778295024977797 + >>> struveh(-1, 10) + -0.255212719726956768034732 + >>> struveh(1, -100.5) + 0.5819566816797362287502246 + >>> struveh(2.5, 10000000000000) + 3153915652525200060.308937 + >>> struveh(2.5, -10000000000000) + (0.0 - 3153915652525200060.308937j) + >>> struveh(1+j, 1000000+4000000j) + (-3.066421087689197632388731e+1737173 - 1.596619701076529803290973e+1737173j) + +A Struve function of half-integer order is elementary; for example: + + >>> z = 3 + >>> struveh(0.5, 3) + 0.9167076867564138178671595 + >>> sqrt(2/(pi*z))*(1-cos(z)) + 0.9167076867564138178671595 + +Numerically verifying the differential equation:: + + >>> z = mpf(4.5) + >>> n = 3 + >>> f = lambda z: struveh(n,z) + >>> lhs = z**2*diff(f,z,2) + z*diff(f,z) + (z**2-n**2)*f(z) + >>> rhs = 2*z**(n+1)/fac2(2*n-1)/pi + >>> lhs + 17.40359302709875496632744 + >>> rhs + 17.40359302709875496632744 + +""" + +struvel = r""" +Gives the modified Struve function + +.. math :: + + \,\mathbf{L}_n(z) = -i e^{-n\pi i/2} \mathbf{H}_n(i z) + +which solves to the modified Struve differential equation + +.. math :: + + z^2 f''(z) + z f'(z) - (z^2+n^2) f(z) = \frac{2 z^{n+1}}{\pi (2n-1)!!}. + +**Examples** + +Evaluation for arbitrary real and complex arguments:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> struvel(0, 3.5) + 7.180846515103737996249972 + >>> struvel(-1, 10) + 2670.994904980850550721511 + >>> struvel(1, -100.5) + 1.757089288053346261497686e+42 + >>> struvel(2.5, 10000000000000) + 4.160893281017115450519948e+4342944819025 + >>> struvel(2.5, -10000000000000) + (0.0 - 4.160893281017115450519948e+4342944819025j) + >>> struvel(1+j, 700j) + (-0.1721150049480079451246076 + 0.1240770953126831093464055j) + >>> struvel(1+j, 1000000+4000000j) + (-2.973341637511505389128708e+434290 - 5.164633059729968297147448e+434290j) + +Numerically verifying the differential equation:: + + >>> z = mpf(3.5) + >>> n = 3 + >>> f = lambda z: struvel(n,z) + >>> lhs = z**2*diff(f,z,2) + z*diff(f,z) - (z**2+n**2)*f(z) + >>> rhs = 2*z**(n+1)/fac2(2*n-1)/pi + >>> lhs + 6.368850306060678353018165 + >>> rhs + 6.368850306060678353018165 +""" + +appellf1 = r""" +Gives the Appell F1 hypergeometric function of two variables, + +.. math :: + + F_1(a,b_1,b_2,c,z_1,z_2) = \sum_{m=0}^{\infty} + \sum_{n=0}^{\infty} + \frac{(a)_{m+n} (b_1)_m (b_2)_n}{m! \,n! \,(c)_{m+n}} z_1^m z_2^n. + +This series is only generally convergent when `|z_1| < 1` and `|z_2| < 1`, +although :func:`appellf1` can evaluate the continuation +in many cases. + +**Examples** + +Evaluation is supported for real and complex parameters:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> appellf1(1,0,0.5,1,0.5,0.25) + 1.154700538379251529018298 + >>> appellf1(1,1+j,0.5,1,0.5,0.5j) + (1.138403860350148085179415 + 1.510544741058517621110615j) + +For some integer parameters, the F1 series reduces to a polynomial:: + + >>> appellf1(2,-4,-3,1,2,5) + -816.0 + >>> appellf1(-5,1,2,1,4,5) + -20528.0 + +The analytic continuation with respect to either `z_1` or `z_2`, +and sometimes with respect to both, can be evaluated:: + + >>> appellf1(2,3,4,5,100,0.5) + (0.0006231042714165329279738662 + 0.0000005769149277148425774499857j) + >>> appellf1('1.1', '0.3', '0.2+2j', '0.4', '0.2', 1.5+3j) + (-0.1782604566893954897128702 + 0.002472407104546216117161499j) + >>> appellf1(1,2,3,4,10,12) + -0.07122993830066776374929313 + +For certain arguments, F1 reduces to an ordinary hypergeometric function:: + + >>> appellf1(1,2,3,5,0.5,0.25) + 1.547902270302684019335555 + >>> 4*hyp2f1(1,2,5,'1/3')/3 + 1.547902270302684019335555 + >>> appellf1(1,2,3,4,0,1.5) + (-1.717202506168937502740238 - 2.792526803190927323077905j) + >>> hyp2f1(1,3,4,1.5) + (-1.717202506168937502740238 - 2.792526803190927323077905j) + +The Appell F1 function allows for closed-form evaluation of various +integrals, such as any integral of the form +`\int x^r (x+a)^p (x+b)^q dx`:: + + >>> def integral(a,b,p,q,r,x1,x2): + ... a,b,p,q,r,x1,x2 = map(mpmathify, [a,b,p,q,r,x1,x2]) + ... f = lambda x: x**r * (x+a)**p * (x+b)**q + ... def F(x): + ... v = x**(r+1)/(r+1) * (a+x)**p * (b+x)**q + ... v *= (1+x/a)**(-p) + ... v *= (1+x/b)**(-q) + ... v *= appellf1(r+1,-p,-q,2+r,-x/a,-x/b) + ... return v + ... print "Num. quad:", quad(f, [x1,x2]) + ... print "Appell F1:", F(x2)-F(x1) + ... + >>> integral('1/5','4/3','-2','3','1/2',0,1) + Num. quad: 9.073335358785776206576981 + Appell F1: 9.073335358785776206576981 + >>> integral('3/2','4/3','-2','3','1/2',0,1) + Num. quad: 1.092829171999626454344678 + Appell F1: 1.092829171999626454344678 + >>> integral('3/2','4/3','-2','3','1/2',12,25) + Num. quad: 1106.323225040235116498927 + Appell F1: 1106.323225040235116498927 + +Also incomplete elliptic integrals fall into this category [1]:: + + >>> def E(z, m): + ... if (pi/2).ae(z): + ... return ellipe(m) + ... return 2*round(re(z)/pi)*ellipe(m) + mpf(-1)**round(re(z)/pi)*\ + ... sin(z)*appellf1(0.5,0.5,-0.5,1.5,sin(z)**2,m*sin(z)**2) + ... + >>> z, m = 1, 0.5 + >>> E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z]) + 0.9273298836244400669659042 + 0.9273298836244400669659042 + >>> z, m = 3, 2 + >>> E(z,m); quad(lambda t: sqrt(1-m*sin(t)**2), [0,pi/4,3*pi/4,z]) + (1.057495752337234229715836 + 1.198140234735592207439922j) + (1.057495752337234229715836 + 1.198140234735592207439922j) + +**References** + +1. http://functions.wolfram.com/EllipticIntegrals/EllipticE2/26/01/ +""" + +zeta = r""" +Computes the Riemann zeta function + +.. math :: + + \zeta(s) = 1+\frac{1}{2^s}+\frac{1}{3^s}+\frac{1}{4^s}+\ldots + +or, with `a \ne 1`, the more general Hurwitz zeta function + +.. math :: + + \zeta(s,a) = \sum_{k=0}^\infty \frac{1}{(a+k)^s}. + +Optionally, ``zeta(s, a, n)`` computes the `n`-th derivative with +respect to `s`, + +.. math :: + + \zeta^{(n)}(s,a) = (-1)^n \sum_{k=0}^\infty \frac{\log^n(a+k)}{(a+k)^s}. + +Although these series only converge for `\Re(s) > 1`, the Riemann and Hurwitz +zeta functions are defined through analytic continuation for arbitrary +complex `s \ne 1` (`s = 1` is a pole). + +The implementation uses three algorithms: the Borwein algorithm for +the Riemann zeta function when `s` is close to the real line; +the Riemann-Siegel formula for the Riemann zeta function when `s` is +large imaginary, and Euler-Maclaurin summation in all other cases. +The reflection formula for `\Re(s) < 0` is implemented in some cases. +The algorithm can be chosen with ``method = 'borwein'``, +``method='riemann-siegel'`` or ``method = 'euler-maclaurin'``. + +The parameter `a` is usually a rational number `a = p/q`, and may be specified +as such by passing an integer tuple `(p, q)`. Evaluation is supported for +arbitrary complex `a`, but may be slow and/or inaccurate when `\Re(s) < 0` for +nonrational `a` or when computing derivatives. + +**Examples** + +Some values of the Riemann zeta function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> zeta(2); pi**2 / 6 + 1.644934066848226436472415 + 1.644934066848226436472415 + >>> zeta(0) + -0.5 + >>> zeta(-1) + -0.08333333333333333333333333 + >>> zeta(-2) + 0.0 + +For large positive `s`, `\zeta(s)` rapidly approaches 1:: + + >>> zeta(50) + 1.000000000000000888178421 + >>> zeta(100) + 1.0 + >>> zeta(inf) + 1.0 + >>> 1-sum((zeta(k)-1)/k for k in range(2,85)); +euler + 0.5772156649015328606065121 + 0.5772156649015328606065121 + >>> nsum(lambda k: zeta(k)-1, [2, inf]) + 1.0 + +Evaluation is supported for complex `s` and `a`: + + >>> zeta(-3+4j) + (-0.03373057338827757067584698 + 0.2774499251557093745297677j) + >>> zeta(2+3j, -1+j) + (389.6841230140842816370741 + 295.2674610150305334025962j) + +The Riemann zeta function has so-called nontrivial zeros on +the critical line `s = 1/2 + it`:: + + >>> findroot(zeta, 0.5+14j); zetazero(1) + (0.5 + 14.13472514173469379045725j) + (0.5 + 14.13472514173469379045725j) + >>> findroot(zeta, 0.5+21j); zetazero(2) + (0.5 + 21.02203963877155499262848j) + (0.5 + 21.02203963877155499262848j) + >>> findroot(zeta, 0.5+25j); zetazero(3) + (0.5 + 25.01085758014568876321379j) + (0.5 + 25.01085758014568876321379j) + >>> chop(zeta(zetazero(10))) + 0.0 + +Evaluation on and near the critical line is supported for large +heights `t` by means of the Riemann-Siegel formula (currently +for `a = 1`, `n \le 4`):: + + >>> zeta(0.5+100000j) + (1.073032014857753132114076 + 5.780848544363503984261041j) + >>> zeta(0.75+1000000j) + (0.9535316058375145020351559 + 0.9525945894834273060175651j) + >>> zeta(0.5+10000000j) + (11.45804061057709254500227 - 8.643437226836021723818215j) + >>> zeta(0.5+100000000j, derivative=1) + (51.12433106710194942681869 + 43.87221167872304520599418j) + >>> zeta(0.5+100000000j, derivative=2) + (-444.2760822795430400549229 - 896.3789978119185981665403j) + >>> zeta(0.5+100000000j, derivative=3) + (3230.72682687670422215339 + 14374.36950073615897616781j) + >>> zeta(0.5+100000000j, derivative=4) + (-11967.35573095046402130602 - 218945.7817789262839266148j) + >>> print zeta(1+10000000j) # off the line + (2.859846483332530337008882 + 0.491808047480981808903986j) + >>> print zeta(1+10000000j, derivative=1) + (-4.333835494679647915673205 - 0.08405337962602933636096103j) + >>> print zeta(1+10000000j, derivative=4) + (453.2764822702057701894278 - 581.963625832768189140995j) + +Note: for investigation of the zeta function zeros, the Riemann-Siegel +Z-function is often more convenient than working with the Riemann +zeta function directly (see :func:`siegelz`). + +Some values of the Hurwitz zeta function:: + + >>> zeta(2, 3); -5./4 + pi**2/6 + 0.3949340668482264364724152 + 0.3949340668482264364724152 + >>> zeta(2, (3,4)); pi**2 - 8*catalan + 2.541879647671606498397663 + 2.541879647671606498397663 + +For positive integer values of `s`, the Hurwitz zeta function is +equivalent to a polygamma function (except for a normalizing factor):: + + >>> zeta(4, (1,5)); psi(3, '1/5')/6 + 625.5408324774542966919938 + 625.5408324774542966919938 + +Evaluation of derivatives:: + + >>> zeta(0, 3+4j, 1); loggamma(3+4j) - ln(2*pi)/2 + (-2.675565317808456852310934 + 4.742664438034657928194889j) + (-2.675565317808456852310934 + 4.742664438034657928194889j) + >>> zeta(2, 1, 20) + 2432902008176640000.000242 + >>> zeta(3+4j, 5.5+2j, 4) + (-0.140075548947797130681075 - 0.3109263360275413251313634j) + >>> zeta(0.5+100000j, 1, 4) + (-10407.16081931495861539236 + 13777.78669862804508537384j) + +Generating a Taylor series at `s = 2` using derivatives:: + + >>> for k in range(11): print zeta(2,1,k)/fac(k), "*", "(s-2)^%i" % k + ... + 1.644934066848226436472415 * (s-2)^0 + -0.9375482543158437537025741 * (s-2)^1 + 0.9946401171494505117104293 * (s-2)^2 + -1.000024300473840810940657 * (s-2)^3 + 1.000061933072352565457512 * (s-2)^4 + -1.000006869443931806408941 * (s-2)^5 + 1.000000173233769531820592 * (s-2)^6 + -0.9999999569989868493432399 * (s-2)^7 + 0.9999999937218844508684206 * (s-2)^8 + -0.9999999996355013916608284 * (s-2)^9 + 1.000000000004610645020747 * (s-2)^10 + +Evaluation at zero and for negative integer `s`:: + + >>> zeta(0, 10) + -9.5 + >>> zeta(-2, (2,3)); mpf(1)/81 + 0.01234567901234567901234568 + 0.01234567901234567901234568 + >>> zeta(-3+4j, (5,4)) + (0.2899236037682695182085988 + 0.06561206166091757973112783j) + >>> zeta(-3.25, 1/pi) + -0.0005117269627574430494396877 + >>> extraprec(20)(zeta)(-3.5, pi, 1) # XXX: extra precision + 11.156360390440003294709 + >>> zeta(-100.5, (8,3)) + -4.68162300487989766727122e+77 + >>> zeta(-10.5, (-8,3)) + (-0.01521913704446246609237979 + 29907.72510874248161608216j) + >>> zeta(-1000.5, (-8,3)) + (1.031911949062334538202567e+1770 + 1.519555750556794218804724e+426j) + >>> zeta(-1+j, 3+4j) + (-16.32988355630802510888631 - 22.17706465801374033261383j) + >>> zeta(-1+j, 3+4j, 2) + (32.48985276392056641594055 - 51.11604466157397267043655j) + >>> diff(lambda s: zeta(s, 3+4j), -1+j, 2) + (32.48985276392056641594055 - 51.11604466157397267043655j) + +**References** + +1. http://mathworld.wolfram.com/RiemannZetaFunction.html + +2. http://mathworld.wolfram.com/HurwitzZetaFunction.html + +3. http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.pdf + +""" + +dirichlet = r""" +Evaluates the Dirichlet L-function + +.. math :: + + L(s,\chi) = \sum_{k=1}^\infty \frac{\chi(k)}{k^s}. + +where `\chi` is a periodic sequence of length `q` which should be supplied +in the form of a list `[\chi(0), \chi(1), \ldots, \chi(q-1)]`. +Strictly, `\chi` should be a Dirichlet character, but any periodic +sequence will work. + +For example, ``dirichlet(s, [1])`` gives the ordinary +Riemann zeta function and ``dirichlet(s, [-1,1])`` gives +the alternating zeta function (Dirichlet eta function). + +Also the derivative with respect to `s` (currently only a first +derivative) can be evaluated. + +**Examples** + +The ordinary Riemann zeta function:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> dirichlet(3, [1]); zeta(3) + 1.202056903159594285399738 + 1.202056903159594285399738 + >>> dirichlet(1, [1]) + +inf + +The alternating zeta function:: + + >>> dirichlet(1, [-1,1]); ln(2) + 0.6931471805599453094172321 + 0.6931471805599453094172321 + +The following defines the Dirichlet beta function +`\beta(s) = \sum_{k=0}^\infty \frac{(-1)^k}{(2k+1)^s}` and verifies +several values of this function:: + + >>> B = lambda s, d=0: dirichlet(s, [0, 1, 0, -1], d) + >>> B(0); 1./2 + 0.5 + 0.5 + >>> B(1); pi/4 + 0.7853981633974483096156609 + 0.7853981633974483096156609 + >>> B(2); +catalan + 0.9159655941772190150546035 + 0.9159655941772190150546035 + >>> B(2,1); diff(B, 2) + 0.08158073611659279510291217 + 0.08158073611659279510291217 + >>> B(-1,1); 2*catalan/pi + 0.5831218080616375602767689 + 0.5831218080616375602767689 + >>> B(0,1); log(gamma(0.25)**2/(2*pi*sqrt(2))) + 0.3915943927068367764719453 + 0.3915943927068367764719454 + >>> B(1,1); 0.25*pi*(euler+2*ln2+3*ln(pi)-4*ln(gamma(0.25))) + 0.1929013167969124293631898 + 0.1929013167969124293631898 + +A custom L-series of period 3:: + + >>> dirichlet(2, [2,0,1]) + 0.7059715047839078092146831 + >>> 2*nsum(lambda k: (3*k)**-2, [1,inf]) + \ + ... nsum(lambda k: (3*k+2)**-2, [0,inf]) + 0.7059715047839078092146831 + +""" + +coulombf = r""" +Calculates the regular Coulomb wave function + +.. math :: + + F_l(\eta,z) = C_l(\eta) z^{l+1} e^{-iz} \,_1F_1(l+1-i\eta, 2l+2, 2iz) + +where the normalization constant `C_l(\eta)` is as calculated by +:func:`coulombc`. This function solves the differential equation + +.. math :: + + f''(z) + \left(1-\frac{2\eta}{z}-\frac{l(l+1)}{z^2}\right) f(z) = 0. + +A second linearly independent solution is given by the irregular +Coulomb wave function `G_l(\eta,z)` (see :func:`coulombg`) +and thus the general solution is +`f(z) = C_1 F_l(\eta,z) + C_2 G_l(\eta,z)` for arbitrary +constants `C_1`, `C_2`. +Physically, the Coulomb wave functions give the radial solution +to the Schrodinger equation for a point particle in a `1/z` potential; `z` is +then the radius and `l`, `\eta` are quantum numbers. + +The Coulomb wave functions with real parameters are defined +in Abramowitz & Stegun, section 14. However, all parameters are permitted +to be complex in this implementation (see references). + +**Examples** + +Evaluation is supported for arbitrary magnitudes of `z`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> coulombf(2, 1.5, 3.5) + 0.4080998961088761187426445 + >>> coulombf(-2, 1.5, 3.5) + 0.7103040849492536747533465 + >>> coulombf(2, 1.5, '1e-10') + 4.143324917492256448770769e-33 + >>> coulombf(2, 1.5, 1000) + 0.4482623140325567050716179 + >>> coulombf(2, 1.5, 10**10) + -0.066804196437694360046619 + +Verifying the differential equation:: + + >>> l, eta, z = 2, 3, mpf(2.75) + >>> A, B = 1, 2 + >>> f = lambda z: A*coulombf(l,eta,z) + B*coulombg(l,eta,z) + >>> chop(diff(f,z,2) + (1-2*eta/z - l*(l+1)/z**2)*f(z)) + 0.0 + +A Wronskian relation satisfied by the Coulomb wave functions:: + + >>> l = 2 + >>> eta = 1.5 + >>> F = lambda z: coulombf(l,eta,z) + >>> G = lambda z: coulombg(l,eta,z) + >>> for z in [3.5, -1, 2+3j]: + ... chop(diff(F,z)*G(z) - F(z)*diff(G,z)) + ... + 1.0 + 1.0 + 1.0 + +Another Wronskian relation:: + + >>> F = coulombf + >>> G = coulombg + >>> for z in [3.5, -1, 2+3j]: + ... chop(F(l-1,eta,z)*G(l,eta,z)-F(l,eta,z)*G(l-1,eta,z) - l/sqrt(l**2+eta**2)) + ... + 0.0 + 0.0 + 0.0 + +An integral identity connecting the regular and irregular wave functions:: + + >>> l, eta, z = 4+j, 2-j, 5+2j + >>> coulombf(l,eta,z) + j*coulombg(l,eta,z) + (0.7997977752284033239714479 + 0.9294486669502295512503127j) + >>> g = lambda t: exp(-t)*t**(l-j*eta)*(t+2*j*z)**(l+j*eta) + >>> j*exp(-j*z)*z**(-l)/fac(2*l+1)/coulombc(l,eta)*quad(g, [0,inf]) + (0.7997977752284033239714479 + 0.9294486669502295512503127j) + +Some test case with complex parameters, taken from Michel [2]:: + + >>> mp.dps = 15 + >>> coulombf(1+0.1j, 50+50j, 100.156) + (-1.02107292320897e+15 - 2.83675545731519e+15j) + >>> coulombg(1+0.1j, 50+50j, 100.156) + (2.83675545731519e+15 - 1.02107292320897e+15j) + >>> coulombf(1e-5j, 10+1e-5j, 0.1+1e-6j) + (4.30566371247811e-14 - 9.03347835361657e-19j) + >>> coulombg(1e-5j, 10+1e-5j, 0.1+1e-6j) + (778709182061.134 + 18418936.2660553j) + +The following reproduces a table in Abramowitz & Stegun, at twice +the precision:: + + >>> mp.dps = 10 + >>> eta = 2; z = 5 + >>> for l in [5, 4, 3, 2, 1, 0]: + ... print l, coulombf(l,eta,z), diff(lambda z: coulombf(l,eta,z), z) + ... + 5 0.09079533488 0.1042553261 + 4 0.2148205331 0.2029591779 + 3 0.4313159311 0.320534053 + 2 0.7212774133 0.3952408216 + 1 0.9935056752 0.3708676452 + 0 1.143337392 0.2937960375 + +**References** + +1. I.J. Thompson & A.R. Barnett, "Coulomb and Bessel Functions of Complex + Arguments and Order", J. Comp. Phys., vol 64, no. 2, June 1986. + +2. N. Michel, "Precise Coulomb wave functions for a wide range of + complex `l`, `\eta` and `z`", http://arxiv.org/abs/physics/0702051v1 + +""" + +coulombg = r""" +Calculates the irregular Coulomb wave function + +.. math :: + + G_l(\eta,z) = \frac{F_l(\eta,z) \cos(\chi) - F_{-l-1}(\eta,z)}{\sin(\chi)} + +where `\chi = \sigma_l - \sigma_{-l-1} - (l+1/2) \pi` +and `\sigma_l(\eta) = (\ln \Gamma(1+l+i\eta)-\ln \Gamma(1+l-i\eta))/(2i)`. + +See :func:`coulombf` for additional information. + +**Examples** + +Evaluation is supported for arbitrary magnitudes of `z`:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> coulombg(-2, 1.5, 3.5) + 1.380011900612186346255524 + >>> coulombg(2, 1.5, 3.5) + 1.919153700722748795245926 + >>> coulombg(-2, 1.5, '1e-10') + 201126715824.7329115106793 + >>> coulombg(-2, 1.5, 1000) + 0.1802071520691149410425512 + >>> coulombg(-2, 1.5, 10**10) + 0.652103020061678070929794 + +The following reproduces a table in Abramowitz & Stegun, +at twice the precision:: + + >>> mp.dps = 10 + >>> eta = 2; z = 5 + >>> for l in [1, 2, 3, 4, 5]: + ... print l, coulombg(l,eta,z), -diff(lambda z: coulombg(l,eta,z), z) + ... + 1 1.08148276 0.6028279961 + 2 1.496877075 0.5661803178 + 3 2.048694714 0.7959909551 + 4 3.09408669 1.731802374 + 5 5.629840456 4.549343289 + +Evaluation close to the singularity at `z = 0`:: + + >>> mp.dps = 15 + >>> coulombg(0,10,1) + 3088184933.67358 + >>> coulombg(0,10,'1e-10') + 5554866000719.8 + >>> coulombg(0,10,'1e-100') + 5554866221524.1 + +Evaluation with a half-integer value for `l`:: + + >>> coulombg(1.5, 1, 10) + 0.852320038297334 +""" + +coulombc = r""" +Gives the normalizing Gamow constant for Coulomb wave functions, + +.. math :: + + C_l(\eta) = 2^l \exp\left(-\pi \eta/2 + [\ln \Gamma(1+l+i\eta) + + \ln \Gamma(1+l-i\eta)]/2 - \ln \Gamma(2l+2)\right), + +where the log gamma function with continuous imaginary part +away from the negative half axis (see :func:`loggamma`) is implied. + +This function is used internally for the calculation of +Coulomb wave functions, and automatically cached to make multiple +evaluations with fixed `l`, `\eta` fast. +""" + +jsn =r""" +Computes of the Jacobi elliptic sn function in terms +of Jacobi theta functions. +`u` is any complex number, `m` must be in the unit disk. + +The sn-function is doubly periodic in the complex plane with periods +`4 K(m)` and `2 i K(1-m)` (see :func:`ellipk`):: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> jsn(2, 0.25) + 0.9628981775982774425751399 + >>> jsn(2+4*ellipk(0.25), 0.25) + 0.9628981775982774425751399 + >>> chop(jsn(2+2*j*ellipk(1-0.25), 0.25)) + 0.9628981775982774425751399 +""" + +jcn = r""" +Computes of the Jacobi elliptic cn function in terms +of Jacobi theta functions. +`u` is any complex number, `m` must be in the unit disk + +The cn-function is doubly periodic in the complex +plane with periods `4 K(m)` and `4 i K(1-m)` +(see :func:`ellipk`):: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> jcn(2, 0.25) + -0.2698649654510865792581416 + >>> jcn(2+4*ellipk(0.25), 0.25) + -0.2698649654510865792581416 + >>> chop(jcn(2+4*j*ellipk(1-0.25), 0.25)) + -0.2698649654510865792581416 +""" + +jdn = r""" +Computes of the Jacobi elliptic dn function in terms +of Jacobi theta functions. +`u` is any complex number, `m` must be in the unit disk + +The dn-function is doubly periodic in the complex +plane with periods `2 K(m)` and `4 i K(1-m)` +(see :func:`ellipk`):: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> jdn(2, 0.25) + 0.8764740583123262286931578 + >>> jdn(2+2*ellipk(0.25), 0.25) + 0.8764740583123262286931578 + >>> chop(jdn(2+4*j*ellipk(1-0.25), 0.25)) + 0.8764740583123262286931578 +""" + +jtheta = r""" +Computes the Jacobi theta function `\vartheta_n(z, q)`, where +`n = 1, 2, 3, 4`. The theta functions are functions of two +variables: + +* `z` is the *argument*, an arbitrary real or complex number + +* `q` is the *nome*, which must be a real or complex number + in the unit disk (i.e. `|q| < 1`) + +One also commonly encounters the notation `\vartheta_n(z, \tau)` +in the literature. The variable `\tau` is called the *parameter* +and can be converted to a nome using the formula +`q = \exp(i \pi \tau)`. Note the condition `|q| < 1` requires +`\Im(\tau) > 0`; i.e. Jacobi theta functions are defined for +`\tau` in the upper half plane. + +Other notations are also in use. For example, some authors use +the single-argument form `\vartheta_n(x)`. Depending on context, +this can mean ``jtheta(n, 0, x)``, ``jtheta(n, x, q)``, or possibly +something else. Needless to say, it is a good idea to cross-check +the definitions when working with theta functions. + +Optionally, ``jtheta(n, z, q, derivative=d)`` with `d > 0` computes +a `d`-th derivative with respect to `z`. + +**Definition** + +The four Jacobi theta functions as implemented by :func:`jtheta` +are defined by the following infinite series: + +.. math :: + + \vartheta_1(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty} + (-1)^n q^{n^2+n\,} \sin((2n+1)z) + + \vartheta_2(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty} + q^{n^{2\,} + n} \cos((2n+1)z) + + \vartheta_3(z,q) = 1 + 2 \sum_{n=1}^{\infty} + q^{n^2\,} \cos(2 n z) + + \vartheta_4(z,q) = 1 + 2 \sum_{n=1}^{\infty} + (-q)^{n^2\,} \cos(2 n z) + +For `|q| \ll 1`, these series converge very quickly, so the +Jacobi theta functions can efficiently be evaluated to high +precision. + +**Examples and basic properties** + +Considered as functions of `z`, the Jacobi theta functions may be +viewed as generalizations of the ordinary trigonometric functions +cos and sin. They are periodic functions:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> jtheta(1, 0.25, '0.2') + 0.2945120798627300045053104 + >>> jtheta(1, 0.25 + 2*pi, '0.2') + 0.2945120798627300045053104 + +Indeed, the series defining the theta functions are essentially +trigonometric Fourier series. The coefficients can be retrieved +using :func:`fourier`:: + + >>> mp.dps = 10 + >>> nprint(fourier(lambda x: jtheta(2, x, 0.5), [-pi, pi], 4)) + ([0.0, 1.68179, 0.0, 0.420448, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0]) + +The Jacobi theta functions are also so-called quasiperiodic +functions of `z` and `\tau`, meaning that for fixed `\tau`, +`\vartheta_n(z, q)` and `\vartheta_n(z+\pi \tau, q)` are the same +except for an exponential factor:: + + >>> mp.dps = 25 + >>> tau = 3*j/10 + >>> q = exp(pi*j*tau) + >>> z = 10 + >>> jtheta(4, z+tau*pi, q) + (-0.682420280786034687520568 + 1.526683999721399103332021j) + >>> -exp(-2*j*z)/q * jtheta(4, z, q) + (-0.682420280786034687520568 + 1.526683999721399103332021j) + +The Jacobi theta functions satisfy a huge number of other +functional equations, such as the following identity (valid for +any `q`):: + + >>> q = mpf(3)/10 + >>> jtheta(3,0,q)**4 + 6.823744089352763305137427 + >>> jtheta(2,0,q)**4 + jtheta(4,0,q)**4 + 6.823744089352763305137427 + +Extensive listings of identities satisfied by the Jacobi theta +functions can be found in standard reference works. + +The Jacobi theta functions are related to the gamma function +for special arguments:: + + >>> jtheta(3, 0, exp(-pi)) + 1.086434811213308014575316 + >>> pi**(1/4.) / gamma(3/4.) + 1.086434811213308014575316 + +:func:`jtheta` supports arbitrary precision evaluation and complex +arguments:: + + >>> mp.dps = 50 + >>> jtheta(4, sqrt(2), 0.5) + 2.0549510717571539127004115835148878097035750653737 + >>> mp.dps = 25 + >>> jtheta(4, 1+2j, (1+j)/5) + (7.180331760146805926356634 - 1.634292858119162417301683j) + +Evaluation of derivatives:: + + >>> mp.dps = 25 + >>> jtheta(1, 7, 0.25, 1); diff(lambda z: jtheta(1, z, 0.25), 7) + 1.209857192844475388637236 + 1.209857192844475388637236 + >>> jtheta(1, 7, 0.25, 2); diff(lambda z: jtheta(1, z, 0.25), 7, 2) + -0.2598718791650217206533052 + -0.2598718791650217206533052 + >>> jtheta(2, 7, 0.25, 1); diff(lambda z: jtheta(2, z, 0.25), 7) + -1.150231437070259644461474 + -1.150231437070259644461474 + >>> jtheta(2, 7, 0.25, 2); diff(lambda z: jtheta(2, z, 0.25), 7, 2) + -0.6226636990043777445898114 + -0.6226636990043777445898114 + >>> jtheta(3, 7, 0.25, 1); diff(lambda z: jtheta(3, z, 0.25), 7) + -0.9990312046096634316587882 + -0.9990312046096634316587882 + >>> jtheta(3, 7, 0.25, 2); diff(lambda z: jtheta(3, z, 0.25), 7, 2) + -0.1530388693066334936151174 + -0.1530388693066334936151174 + >>> jtheta(4, 7, 0.25, 1); diff(lambda z: jtheta(4, z, 0.25), 7) + 0.9820995967262793943571139 + 0.9820995967262793943571139 + >>> jtheta(4, 7, 0.25, 2); diff(lambda z: jtheta(4, z, 0.25), 7, 2) + 0.3936902850291437081667755 + 0.3936902850291437081667755 + +**Possible issues** + +For `|q| \ge 1` or `\Im(\tau) \le 0`, :func:`jtheta` raises +``ValueError``. This exception is also raised for `|q|` extremely +close to 1 (or equivalently `\tau` very close to 0), since the +series would converge too slowly:: + + >>> jtheta(1, 10, 0.99999999 * exp(0.5*j)) + Traceback (most recent call last): + ... + ValueError: abs(q) > THETA_Q_LIM = 1.000000 + +""" + +eulernum = r""" +Gives the `n`-th Euler number, defined as the `n`-th derivative of +`\mathrm{sech}(t) = 1/\cosh(t)` evaluated at `t = 0`. Equivalently, the +Euler numbers give the coefficients of the Taylor series + +.. math :: + + \mathrm{sech}(t) = \sum_{n=0}^{\infty} \frac{E_n}{n!} t^n. + +The Euler numbers are closely related to Bernoulli numbers +and Bernoulli polynomials. They can also be evaluated in terms of +Euler polynomials (see :func:`eulerpoly`) as `E_n = 2^n E_n(1/2)`. + +**Examples** + +Computing the first few Euler numbers and verifying that they +agree with the Taylor series:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> [eulernum(n) for n in range(11)] + [1.0, 0.0, -1.0, 0.0, 5.0, 0.0, -61.0, 0.0, 1385.0, 0.0, -50521.0] + >>> chop(diffs(sech, 0, 10)) + [1.0, 0.0, -1.0, 0.0, 5.0, 0.0, -61.0, 0.0, 1385.0, 0.0, -50521.0] + +Euler numbers grow very rapidly. :func:`eulernum` efficiently +computes numerical approximations for large indices:: + + >>> eulernum(50) + -6.053285248188621896314384e+54 + >>> eulernum(1000) + 3.887561841253070615257336e+2371 + >>> eulernum(10**20) + 4.346791453661149089338186e+1936958564106659551331 + +Comparing with an asymptotic formula for the Euler numbers:: + + >>> n = 10**5 + >>> (-1)**(n//2) * 8 * sqrt(n/(2*pi)) * (2*n/(pi*e))**n + 3.69919063017432362805663e+436961 + >>> eulernum(n) + 3.699193712834466537941283e+436961 + +Pass ``exact=True`` to obtain exact values of Euler numbers as integers:: + + >>> print eulernum(50, exact=True) + -6053285248188621896314383785111649088103498225146815121 + >>> print eulernum(200, exact=True) % 10**10 + 1925859625 + >>> eulernum(1001, exact=True) + 0 +""" + +eulerpoly = r""" +Evaluates the Euler polynomial `E_n(z)`, defined by the generating function +representation + +.. math :: + + \frac{2e^{zt}}{e^t+1} = \sum_{n=0}^\infty E_n(z) \frac{t^n}{n!}. + +The Euler polynomials may also be represented in terms of +Bernoulli polynomials (see :func:`bernpoly`) using various formulas, for +example + +.. math :: + + E_n(z) = \frac{2}{n+1} \left( + B_n(z)-2^{n+1}B_n\left(\frac{z}{2}\right) + \right). + +Special values include the Euler numbers `E_n = 2^n E_n(1/2)` (see +:func:`eulernum`). + +**Examples** + +Computing the coefficients of the first few Euler polynomials:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> for n in range(6): + ... chop(taylor(lambda z: eulerpoly(n,z), 0, n)) + ... + [1.0] + [-0.5, 1.0] + [0.0, -1.0, 1.0] + [0.25, 0.0, -1.5, 1.0] + [0.0, 1.0, 0.0, -2.0, 1.0] + [-0.5, 0.0, 2.5, 0.0, -2.5, 1.0] + +Evaluation for arbitrary `z`:: + + >>> eulerpoly(2,3) + 6.0 + >>> eulerpoly(5,4) + 423.5 + >>> eulerpoly(35, 11111111112) + 3.994957561486776072734601e+351 + >>> eulerpoly(4, 10+20j) + (-47990.0 - 235980.0j) + >>> eulerpoly(2, '-3.5e-5') + 0.000035001225 + >>> eulerpoly(3, 0.5) + 0.0 + >>> eulerpoly(55, -10**80) + -1.0e+4400 + >>> eulerpoly(5, -inf) + -inf + >>> eulerpoly(6, -inf) + +inf + +Computing Euler numbers:: + + >>> 2**26 * eulerpoly(26,0.5) + -4087072509293123892361.0 + >>> eulernum(26) + -4087072509293123892361.0 + +Evaluation is accurate for large `n` and small `z`:: + + >>> eulerpoly(100, 0.5) + 2.29047999988194114177943e+108 + >>> eulerpoly(1000, 10.5) + 3.628120031122876847764566e+2070 + >>> eulerpoly(10000, 10.5) + 1.149364285543783412210773e+30688 +""" + +spherharm = r""" +Evaluates the spherical harmonic `Y_l^m(\theta,\phi)`, + +.. math :: + + Y_l^m(\theta,\phi) = \sqrt{\frac{2l+1}{4\pi}\frac{(l-m)!}{(l+m)!}} + P_l^m(\cos \theta) e^{i m \phi} + +where `P_l^m` is an associated Legendre function (see :func:`legenp`). + +Here `\theta \in [0, \pi]` denotes the polar coordinate (ranging +from the north pole to the south pole) and `\phi \in [0, 2 \pi]` denotes the +azimuthal coordinate on a sphere. Care should be used since many different +conventions for spherical coordinate variables are used. + +Usually spherical harmonics are considered for `l \in \mathbb{N}`, +`m \in \mathbb{Z}`, `|m| \le l`. More generally, `l,m,\theta,\phi` +are permitted to be complex numbers. + +Note: :func:`spherharm` returns a complex number, even the value is +purely real. + +**Examples** + +Some low-order spherical harmonics with reference values:: + + >>> from mpmath import * + >>> mp.dps = 25; mp.pretty = True + >>> theta = pi/4 + >>> phi = pi/3 + >>> spherharm(0,0,theta,phi); 0.5*sqrt(1/pi)*expj(0) + (0.2820947917738781434740397 + 0.0j) + (0.2820947917738781434740397 + 0.0j) + >>> spherharm(1,-1,theta,phi); 0.5*sqrt(3/(2*pi))*expj(-phi)*sin(theta) + (0.1221506279757299803965962 - 0.2115710938304086076055298j) + (0.1221506279757299803965962 - 0.2115710938304086076055298j) + >>> spherharm(1,0,theta,phi); 0.5*sqrt(3/pi)*cos(theta)*expj(0) + (0.3454941494713354792652446 + 0.0j) + (0.3454941494713354792652446 + 0.0j) + >>> spherharm(1,1,theta,phi); -0.5*sqrt(3/(2*pi))*expj(phi)*sin(theta) + (-0.1221506279757299803965962 - 0.2115710938304086076055298j) + (-0.1221506279757299803965962 - 0.2115710938304086076055298j) + +With the normalization convention used, the spherical harmonics are orthonormal +on the unit sphere:: + + >>> sphere = [0,pi], [0,2*pi] + >>> dS = lambda t,p: fp.sin(t) # differential element + >>> Y1 = lambda t,p: fp.spherharm(l1,m1,t,p) + >>> Y2 = lambda t,p: fp.conj(fp.spherharm(l2,m2,t,p)) + >>> l1 = l2 = 3; m1 = m2 = 2 + >>> print fp.quad(lambda t,p: Y1(t,p)*Y2(t,p)*dS(t,p), *sphere) + (1+0j) + >>> m2 = 1 # m1 != m2 + >>> fp.chop(fp.quad(lambda t,p: Y1(t,p)*Y2(t,p)*dS(t,p), *sphere)) + 0.0 + +Evaluation is accurate for large orders:: + + >>> spherharm(1000,750,0.5,0.25) + (3.776445785304252879026585e-102 - 5.82441278771834794493484e-102j) + +Evaluation works with complex parameter values:: + + >>> spherharm(1+j, 2j, 2+3j, -0.5j) + (64.44922331113759992154992 + 1981.693919841408089681743j) +""" diff --git a/compiler/gdsMill/mpmath/functions/__init__.py b/compiler/gdsMill/mpmath/functions/__init__.py new file mode 100644 index 00000000..7e3811b9 --- /dev/null +++ b/compiler/gdsMill/mpmath/functions/__init__.py @@ -0,0 +1,7 @@ +import functions +# Hack to update methods +import factorials +import hypergeometric +import elliptic +import zeta +import rszeta diff --git a/compiler/gdsMill/mpmath/functions/elliptic.py b/compiler/gdsMill/mpmath/functions/elliptic.py new file mode 100644 index 00000000..eedd1fc7 --- /dev/null +++ b/compiler/gdsMill/mpmath/functions/elliptic.py @@ -0,0 +1,1156 @@ +""" +elliptic.py + +Implements the Jacobi theta and Jacobi elliptic functions, using +arbitrary precision math library + +Author of the first version: M.T. Taschuk + +References: + +[1] Abramowitz & Stegun. 'Handbook of Mathematical Functions, 9th Ed.', + (Dover duplicate of 1972 edition) +[2] Whittaker 'A Course of Modern Analysis, 4th Ed.', 1946, + Cambridge University Press +""" + +from functions import defun, defun_wrapped + +@defun +def calculate_nome(ctx, k): + k = ctx.convert(k) + if abs(k) > ctx.one: # range error + raise ValueError + if k == ctx.zero: + return ctx.zero + elif k == ctx.one: + return ctx.one + else: + kprimesquared = ctx.one - k**2 + kprime = ctx.sqrt(kprimesquared) + top = ctx.ellipk(kprimesquared) + bottom = ctx.ellipk(k**2) + argument = -ctx.pi*top/bottom + nome = ctx.exp(argument) + return nome + +@defun +def _jacobi_theta2(ctx, z, q): + extra1 = 10 + extra2 = 20 + # the loops below break when the fixed precision quantities + # a and b go to zero; + # right shifting small negative numbers by wp one obtains -1, not zero, + # so the condition a**2 + b**2 > MIN is used to break the loops. + MIN = 2 + if z == ctx.zero: + if (not ctx._im(q)): + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + s = x2 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + s += a + s = (1 << (wp+1)) + (s << 1) + s = ctx.ldexp(s, -wp) + else: + wp = ctx.prec + extra1 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp-1) + are = bre = x2re + aim = bim = x2im + sre = (1< MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + sre += are + sim += aim + sre = (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + else: + if (not ctx._im(q)) and (not ctx._im(z)): + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) + cn = c1 = ctx.to_fixed(c1, wp) + sn = s1 = ctx.to_fixed(s1, wp) + c2 = (c1*c1 - s1*s1) >> wp + s2 = (c1 * s1) >> (wp - 1) + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + s = c1 + ((a * cn) >> wp) + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + s += (a * cn) >> wp + s = (s << 1) + s = ctx.ldexp(s, -wp) + s *= ctx.nthroot(q, 4) + return s + # case z real, q complex + elif not ctx._im(z): + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = x2re + aim = bim = x2im + c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) + cn = c1 = ctx.to_fixed(c1, wp) + sn = s1 = ctx.to_fixed(s1, wp) + c2 = (c1*c1 - s1*s1) >> wp + s2 = (c1 * s1) >> (wp - 1) + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + sre = c1 + ((are * cn) >> wp) + sim = ((aim * cn) >> wp) + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + sre += ((are * cn) >> wp) + sim += ((aim * cn) >> wp) + sre = (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + #case z complex, q real + elif not ctx._im(q): + wp = ctx.prec + extra2 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + prec0 = ctx.prec + ctx.prec = wp + c1 = ctx.cos(z) + s1 = ctx.sin(z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + #c2 = (c1*c1 - s1*s1) >> wp + c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp + c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) + #s2 = (c1 * s1) >> (wp - 1) + s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) + s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) + #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + sre = c1re + ((a * cnre) >> wp) + sim = c1im + ((a * cnim) >> wp) + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + sre += ((a * cnre) >> wp) + sim += ((a * cnim) >> wp) + sre = (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + # case z and q complex + else: + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = x2re + aim = bim = x2im + prec0 = ctx.prec + ctx.prec = wp + # cos(z), siz(z) with z complex + c1 = ctx.cos(z) + s1 = ctx.sin(z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp + c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) + s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) + s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + n = 1 + termre = c1re + termim = c1im + sre = c1re + ((are * cnre - aim * cnim) >> wp) + sim = c1im + ((are * cnim + aim * cnre) >> wp) + n = 3 + termre = ((are * cnre - aim * cnim) >> wp) + termim = ((are * cnim + aim * cnre) >> wp) + sre = c1re + ((are * cnre - aim * cnim) >> wp) + sim = c1im + ((are * cnim + aim * cnre) >> wp) + n = 5 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + termre = ((are * cnre - aim * cnim) >> wp) + termim = ((aim * cnre + are * cnim) >> wp) + sre += ((are * cnre - aim * cnim) >> wp) + sim += ((aim * cnre + are * cnim) >> wp) + n += 2 + sre = (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + s *= ctx.nthroot(q, 4) + return s + +@defun +def _djacobi_theta2(ctx, z, q, nd): + MIN = 2 + extra1 = 10 + extra2 = 20 + if (not ctx._im(q)) and (not ctx._im(z)): + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) + cn = c1 = ctx.to_fixed(c1, wp) + sn = s1 = ctx.to_fixed(s1, wp) + c2 = (c1*c1 - s1*s1) >> wp + s2 = (c1 * s1) >> (wp - 1) + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + if (nd&1): + s = s1 + ((a * sn * 3**nd) >> wp) + else: + s = c1 + ((a * cn * 3**nd) >> wp) + n = 2 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + if nd&1: + s += (a * sn * (2*n+1)**nd) >> wp + else: + s += (a * cn * (2*n+1)**nd) >> wp + n += 1 + s = -(s << 1) + s = ctx.ldexp(s, -wp) + # case z real, q complex + elif not ctx._im(z): + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = x2re + aim = bim = x2im + c1, s1 = ctx.cos_sin(ctx._re(z), prec=wp) + cn = c1 = ctx.to_fixed(c1, wp) + sn = s1 = ctx.to_fixed(s1, wp) + c2 = (c1*c1 - s1*s1) >> wp + s2 = (c1 * s1) >> (wp - 1) + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + if (nd&1): + sre = s1 + ((are * sn * 3**nd) >> wp) + sim = ((aim * sn * 3**nd) >> wp) + else: + sre = c1 + ((are * cn * 3**nd) >> wp) + sim = ((aim * cn * 3**nd) >> wp) + n = 5 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + + if (nd&1): + sre += ((are * sn * n**nd) >> wp) + sim += ((aim * sn * n**nd) >> wp) + else: + sre += ((are * cn * n**nd) >> wp) + sim += ((aim * cn * n**nd) >> wp) + n += 2 + sre = -(sre << 1) + sim = -(sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + #case z complex, q real + elif not ctx._im(q): + wp = ctx.prec + extra2 + x = ctx.to_fixed(ctx._re(q), wp) + x2 = (x*x) >> wp + a = b = x2 + prec0 = ctx.prec + ctx.prec = wp + c1 = ctx.cos(z) + s1 = ctx.sin(z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + #c2 = (c1*c1 - s1*s1) >> wp + c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp + c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) + #s2 = (c1 * s1) >> (wp - 1) + s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) + s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) + #cn, sn = (cn*c2 - sn*s2) >> wp, (sn*c2 + cn*s2) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre = s1re + ((a * snre * 3**nd) >> wp) + sim = s1im + ((a * snim * 3**nd) >> wp) + else: + sre = c1re + ((a * cnre * 3**nd) >> wp) + sim = c1im + ((a * cnim * 3**nd) >> wp) + n = 5 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre += ((a * snre * n**nd) >> wp) + sim += ((a * snim * n**nd) >> wp) + else: + sre += ((a * cnre * n**nd) >> wp) + sim += ((a * cnim * n**nd) >> wp) + n += 2 + sre = -(sre << 1) + sim = -(sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + # case z and q complex + else: + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = x2re + aim = bim = x2im + prec0 = ctx.prec + ctx.prec = wp + # cos(2*z), sin(2*z) with z complex + c1 = ctx.cos(z) + s1 = ctx.sin(z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + c2re = (c1re*c1re - c1im*c1im - s1re*s1re + s1im*s1im) >> wp + c2im = (c1re*c1im - s1re*s1im) >> (wp - 1) + s2re = (c1re*s1re - c1im*s1im) >> (wp - 1) + s2im = (c1re*s1im + c1im*s1re) >> (wp - 1) + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre = s1re + (((are * snre - aim * snim) * 3**nd) >> wp) + sim = s1im + (((are * snim + aim * snre)* 3**nd) >> wp) + else: + sre = c1re + (((are * cnre - aim * cnim) * 3**nd) >> wp) + sim = c1im + (((are * cnim + aim * cnre)* 3**nd) >> wp) + n = 5 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + #cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + t1 = (cnre*c2re - cnim*c2im - snre*s2re + snim*s2im) >> wp + t2 = (cnre*c2im + cnim*c2re - snre*s2im - snim*s2re) >> wp + t3 = (snre*c2re - snim*c2im + cnre*s2re - cnim*s2im) >> wp + t4 = (snre*c2im + snim*c2re + cnre*s2im + cnim*s2re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre += (((are * snre - aim * snim) * n**nd) >> wp) + sim += (((aim * snre + are * snim) * n**nd) >> wp) + else: + sre += (((are * cnre - aim * cnim) * n**nd) >> wp) + sim += (((aim * cnre + are * cnim) * n**nd) >> wp) + n += 2 + sre = -(sre << 1) + sim = -(sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + s *= ctx.nthroot(q, 4) + if (nd&1): + return (-1)**(nd//2) * s + else: + return (-1)**(1 + nd//2) * s + +@defun +def _jacobi_theta3(ctx, z, q): + extra1 = 10 + extra2 = 20 + MIN = 2 + if z == ctx.zero: + if not ctx._im(q): + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + s = x + a = b = x + x2 = (x*x) >> wp + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + s += a + s = (1 << wp) + (s << 1) + s = ctx.ldexp(s, -wp) + return s + else: + wp = ctx.prec + extra1 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + sre = are = bre = xre + sim = aim = bim = xim + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + sre += are + sim += aim + sre = (1 << wp) + (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + return s + else: + if (not ctx._im(q)) and (not ctx._im(z)): + s = 0 + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + a = b = x + x2 = (x*x) >> wp + c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) + c1 = ctx.to_fixed(c1, wp) + s1 = ctx.to_fixed(s1, wp) + cn = c1 + sn = s1 + s += (a * cn) >> wp + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + s += (a * cn) >> wp + s = (1 << wp) + (s << 1) + s = ctx.ldexp(s, -wp) + return s + # case z real, q complex + elif not ctx._im(z): + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = xre + aim = bim = xim + c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) + c1 = ctx.to_fixed(c1, wp) + s1 = ctx.to_fixed(s1, wp) + cn = c1 + sn = s1 + sre = (are * cn) >> wp + sim = (aim * cn) >> wp + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + sre += (are * cn) >> wp + sim += (aim * cn) >> wp + sre = (1 << wp) + (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + return s + #case z complex, q real + elif not ctx._im(q): + wp = ctx.prec + extra2 + x = ctx.to_fixed(ctx._re(q), wp) + a = b = x + x2 = (x*x) >> wp + prec0 = ctx.prec + ctx.prec = wp + c1 = ctx.cos(2*z) + s1 = ctx.sin(2*z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + sre = (a * cnre) >> wp + sim = (a * cnim) >> wp + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp + t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp + t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp + t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + sre += (a * cnre) >> wp + sim += (a * cnim) >> wp + sre = (1 << wp) + (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + return s + # case z and q complex + else: + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = xre + aim = bim = xim + prec0 = ctx.prec + ctx.prec = wp + # cos(2*z), sin(2*z) with z complex + c1 = ctx.cos(2*z) + s1 = ctx.sin(2*z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + sre = (are * cnre - aim * cnim) >> wp + sim = (aim * cnre + are * cnim) >> wp + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp + t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp + t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp + t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + sre += (are * cnre - aim * cnim) >> wp + sim += (aim * cnre + are * cnim) >> wp + sre = (1 << wp) + (sre << 1) + sim = (sim << 1) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + return s + +@defun +def _djacobi_theta3(ctx, z, q, nd): + """nd=1,2,3 order of the derivative with respect to z""" + MIN = 2 + extra1 = 10 + extra2 = 20 + if (not ctx._im(q)) and (not ctx._im(z)): + s = 0 + wp = ctx.prec + extra1 + x = ctx.to_fixed(ctx._re(q), wp) + a = b = x + x2 = (x*x) >> wp + c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) + c1 = ctx.to_fixed(c1, wp) + s1 = ctx.to_fixed(s1, wp) + cn = c1 + sn = s1 + if (nd&1): + s += (a * sn) >> wp + else: + s += (a * cn) >> wp + n = 2 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + if nd&1: + s += (a * sn * n**nd) >> wp + else: + s += (a * cn * n**nd) >> wp + n += 1 + s = -(s << (nd+1)) + s = ctx.ldexp(s, -wp) + # case z real, q complex + elif not ctx._im(z): + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = xre + aim = bim = xim + c1, s1 = ctx.cos_sin(ctx._re(z)*2, prec=wp) + c1 = ctx.to_fixed(c1, wp) + s1 = ctx.to_fixed(s1, wp) + cn = c1 + sn = s1 + if (nd&1): + sre = (are * sn) >> wp + sim = (aim * sn) >> wp + else: + sre = (are * cn) >> wp + sim = (aim * cn) >> wp + n = 2 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + cn, sn = (cn*c1 - sn*s1) >> wp, (sn*c1 + cn*s1) >> wp + if nd&1: + sre += (are * sn * n**nd) >> wp + sim += (aim * sn * n**nd) >> wp + else: + sre += (are * cn * n**nd) >> wp + sim += (aim * cn * n**nd) >> wp + n += 1 + sre = -(sre << (nd+1)) + sim = -(sim << (nd+1)) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + #case z complex, q real + elif not ctx._im(q): + wp = ctx.prec + extra2 + x = ctx.to_fixed(ctx._re(q), wp) + a = b = x + x2 = (x*x) >> wp + prec0 = ctx.prec + ctx.prec = wp + c1 = ctx.cos(2*z) + s1 = ctx.sin(2*z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + if (nd&1): + sre = (a * snre) >> wp + sim = (a * snim) >> wp + else: + sre = (a * cnre) >> wp + sim = (a * cnim) >> wp + n = 2 + while abs(a) > MIN: + b = (b*x2) >> wp + a = (a*b) >> wp + t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp + t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp + t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp + t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if (nd&1): + sre += (a * snre * n**nd) >> wp + sim += (a * snim * n**nd) >> wp + else: + sre += (a * cnre * n**nd) >> wp + sim += (a * cnim * n**nd) >> wp + n += 1 + sre = -(sre << (nd+1)) + sim = -(sim << (nd+1)) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + # case z and q complex + else: + wp = ctx.prec + extra2 + xre = ctx.to_fixed(ctx._re(q), wp) + xim = ctx.to_fixed(ctx._im(q), wp) + x2re = (xre*xre - xim*xim) >> wp + x2im = (xre*xim) >> (wp - 1) + are = bre = xre + aim = bim = xim + prec0 = ctx.prec + ctx.prec = wp + # cos(2*z), sin(2*z) with z complex + c1 = ctx.cos(2*z) + s1 = ctx.sin(2*z) + ctx.prec = prec0 + cnre = c1re = ctx.to_fixed(ctx._re(c1), wp) + cnim = c1im = ctx.to_fixed(ctx._im(c1), wp) + snre = s1re = ctx.to_fixed(ctx._re(s1), wp) + snim = s1im = ctx.to_fixed(ctx._im(s1), wp) + if (nd&1): + sre = (are * snre - aim * snim) >> wp + sim = (aim * snre + are * snim) >> wp + else: + sre = (are * cnre - aim * cnim) >> wp + sim = (aim * cnre + are * cnim) >> wp + n = 2 + while are**2 + aim**2 > MIN: + bre, bim = (bre * x2re - bim * x2im) >> wp, \ + (bre * x2im + bim * x2re) >> wp + are, aim = (are * bre - aim * bim) >> wp, \ + (are * bim + aim * bre) >> wp + t1 = (cnre*c1re - cnim*c1im - snre*s1re + snim*s1im) >> wp + t2 = (cnre*c1im + cnim*c1re - snre*s1im - snim*s1re) >> wp + t3 = (snre*c1re - snim*c1im + cnre*s1re - cnim*s1im) >> wp + t4 = (snre*c1im + snim*c1re + cnre*s1im + cnim*s1re) >> wp + cnre = t1 + cnim = t2 + snre = t3 + snim = t4 + if(nd&1): + sre += ((are * snre - aim * snim) * n**nd) >> wp + sim += ((aim * snre + are * snim) * n**nd) >> wp + else: + sre += ((are * cnre - aim * cnim) * n**nd) >> wp + sim += ((aim * cnre + are * cnim) * n**nd) >> wp + n += 1 + sre = -(sre << (nd+1)) + sim = -(sim << (nd+1)) + sre = ctx.ldexp(sre, -wp) + sim = ctx.ldexp(sim, -wp) + s = ctx.mpc(sre, sim) + if (nd&1): + return (-1)**(nd//2) * s + else: + return (-1)**(1 + nd//2) * s + +@defun +def _jacobi_theta2a(ctx, z, q): + """ + case ctx._im(z) != 0 + theta(2, z, q) = + q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=-inf, inf) + max term for minimum (2*n+1)*log(q).real - 2* ctx._im(z) + n0 = int(ctx._im(z)/log(q).real - 1/2) + theta(2, z, q) = + q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n=n0, inf) + + q**1/4 * Sum(q**(n*n + n) * exp(j*(2*n + 1)*z), n, n0-1, -inf) + """ + n = n0 = int(ctx._im(z)/ctx._re(ctx.log(q)) - 1/2) + e2 = ctx.expj(2*z) + e = e0 = ctx.expj((2*n+1)*z) + a = q**(n*n + n) + # leading term + term = a * e + s = term + eps1 = ctx.eps*abs(term) + while 1: + n += 1 + e = e * e2 + term = q**(n*n + n) * e + if abs(term) < eps1: + break + s += term + e = e0 + e2 = ctx.expj(-2*z) + n = n0 + while 1: + n -= 1 + e = e * e2 + term = q**(n*n + n) * e + if abs(term) < eps1: + break + s += term + s = s * ctx.nthroot(q, 4) + return s + +@defun +def _jacobi_theta3a(ctx, z, q): + """ + case ctx._im(z) != 0 + theta3(z, q) = Sum(q**(n*n) * exp(j*2*n*z), n, -inf, inf) + max term for n*abs(log(q).real) + ctx._im(z) ~= 0 + n0 = int(- ctx._im(z)/abs(log(q).real)) + """ + n = n0 = int(-ctx._im(z)/abs(ctx._re(ctx.log(q)))) + e2 = ctx.expj(2*z) + e = e0 = ctx.expj(2*n*z) + s = term = q**(n*n) * e + eps1 = ctx.eps*abs(term) + while 1: + n += 1 + e = e * e2 + term = q**(n*n) * e + if abs(term) < eps1: + break + s += term + e = e0 + e2 = ctx.expj(-2*z) + n = n0 + while 1: + n -= 1 + e = e * e2 + term = q**(n*n) * e + if abs(term) < eps1: + break + s += term + return s + +@defun +def _djacobi_theta2a(ctx, z, q, nd): + """ + case ctx._im(z) != 0 + dtheta(2, z, q, nd) = + j* q**1/4 * Sum(q**(n*n + n) * (2*n+1)*exp(j*(2*n + 1)*z), n=-inf, inf) + max term for (2*n0+1)*log(q).real - 2* ctx._im(z) ~= 0 + n0 = int(ctx._im(z)/log(q).real - 1/2) + """ + n = n0 = int(ctx._im(z)/ctx._re(ctx.log(q)) - 1/2) + e2 = ctx.expj(2*z) + e = e0 = ctx.expj((2*n + 1)*z) + a = q**(n*n + n) + # leading term + term = (2*n+1)**nd * a * e + s = term + eps1 = ctx.eps*abs(term) + while 1: + n += 1 + e = e * e2 + term = (2*n+1)**nd * q**(n*n + n) * e + if abs(term) < eps1: + break + s += term + e = e0 + e2 = ctx.expj(-2*z) + n = n0 + while 1: + n -= 1 + e = e * e2 + term = (2*n+1)**nd * q**(n*n + n) * e + if abs(term) < eps1: + break + s += term + return ctx.j**nd * s * ctx.nthroot(q, 4) + +@defun +def _djacobi_theta3a(ctx, z, q, nd): + """ + case ctx._im(z) != 0 + djtheta3(z, q, nd) = (2*j)**nd * + Sum(q**(n*n) * n**nd * exp(j*2*n*z), n, -inf, inf) + max term for minimum n*abs(log(q).real) + ctx._im(z) + """ + n = n0 = int(-ctx._im(z)/abs(ctx._re(ctx.log(q)))) + e2 = ctx.expj(2*z) + e = e0 = ctx.expj(2*n*z) + a = q**(n*n) * e + s = term = n**nd * a + if n != 0: + eps1 = ctx.eps*abs(term) + else: + eps1 = ctx.eps*abs(a) + while 1: + n += 1 + e = e * e2 + a = q**(n*n) * e + term = n**nd * a + if n != 0: + aterm = abs(term) + else: + aterm = abs(a) + if aterm < eps1: + break + s += term + e = e0 + e2 = ctx.expj(-2*z) + n = n0 + while 1: + n -= 1 + e = e * e2 + a = q**(n*n) * e + term = n**nd * a + if n != 0: + aterm = abs(term) + else: + aterm = abs(a) + if aterm < eps1: + break + s += term + return (2*ctx.j)**nd * s + +@defun +def jtheta(ctx, n, z, q, derivative=0): + if derivative: + return ctx._djtheta(n, z, q, derivative) + + z = ctx.convert(z) + q = ctx.convert(q) + + # Implementation note + # If ctx._im(z) is close to zero, _jacobi_theta2 and _jacobi_theta3 + # are used, + # which compute the series starting from n=0 using fixed precision + # numbers; + # otherwise _jacobi_theta2a and _jacobi_theta3a are used, which compute + # the series starting from n=n0, which is the largest term. + + # TODO: write _jacobi_theta2a and _jacobi_theta3a using fixed-point + + if abs(q) > ctx.THETA_Q_LIM: + raise ValueError('abs(q) > THETA_Q_LIM = %f' % ctx.THETA_Q_LIM) + + extra = 10 + if z: + M = ctx.mag(z) + if M > 5 or (n == 1 and M < -5): + extra += 2*abs(M) + cz = 0.5 + extra2 = 50 + prec0 = ctx.prec + try: + ctx.prec += extra + if n == 1: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._jacobi_theta2(z - ctx.pi/2, q) + else: + ctx.dps += 10 + res = ctx._jacobi_theta2a(z - ctx.pi/2, q) + else: + res = ctx._jacobi_theta2(z - ctx.pi/2, q) + elif n == 2: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._jacobi_theta2(z, q) + else: + ctx.dps += 10 + res = ctx._jacobi_theta2a(z, q) + else: + res = ctx._jacobi_theta2(z, q) + elif n == 3: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._jacobi_theta3(z, q) + else: + ctx.dps += 10 + res = ctx._jacobi_theta3a(z, q) + else: + res = ctx._jacobi_theta3(z, q) + elif n == 4: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._jacobi_theta3(z, -q) + else: + ctx.dps += 10 + res = ctx._jacobi_theta3a(z, -q) + else: + res = ctx._jacobi_theta3(z, -q) + else: + raise ValueError + finally: + ctx.prec = prec0 + return res + +@defun +def _djtheta(ctx, n, z, q, derivative=1): + z = ctx.convert(z) + q = ctx.convert(q) + nd = int(derivative) + + if abs(q) > ctx.THETA_Q_LIM: + raise ValueError('abs(q) > THETA_Q_LIM = %f' % ctx.THETA_Q_LIM) + extra = 10 + ctx.prec * nd // 10 + if z: + M = ctx.mag(z) + if M > 5 or (n != 1 and M < -5): + extra += 2*abs(M) + cz = 0.5 + extra2 = 50 + prec0 = ctx.prec + try: + ctx.prec += extra + if n == 1: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._djacobi_theta2(z - ctx.pi/2, q, nd) + else: + ctx.dps += 10 + res = ctx._djacobi_theta2a(z - ctx.pi/2, q, nd) + else: + res = ctx._djacobi_theta2(z - ctx.pi/2, q, nd) + elif n == 2: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._djacobi_theta2(z, q, nd) + else: + ctx.dps += 10 + res = ctx._djacobi_theta2a(z, q, nd) + else: + res = ctx._djacobi_theta2(z, q, nd) + elif n == 3: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._djacobi_theta3(z, q, nd) + else: + ctx.dps += 10 + res = ctx._djacobi_theta3a(z, q, nd) + else: + res = ctx._djacobi_theta3(z, q, nd) + elif n == 4: + if ctx._im(z): + if abs(ctx._im(z)) < cz * abs(ctx._re(ctx.log(q))): + ctx.dps += extra2 + res = ctx._djacobi_theta3(z, -q, nd) + else: + ctx.dps += 10 + res = ctx._djacobi_theta3a(z, -q, nd) + else: + res = ctx._djacobi_theta3(z, -q, nd) + else: + raise ValueError + finally: + ctx.prec = prec0 + return +res + +@defun +def jsn(ctx, u, m): + if abs(m) < ctx.eps: + return ctx.sin(u) + elif m == ctx.one: + return ctx.tanh(u) + else: + extra = 10 + try: + ctx.prec += extra + q = ctx.calculate_nome(ctx.sqrt(m)) + v3 = ctx.jtheta(3, 0, q) + v2 = ctx.jtheta(2, 0, q) # mathworld says v4 + arg1 = u / (v3*v3) + v1 = ctx.jtheta(1, arg1, q) + v4 = ctx.jtheta(4, arg1, q) + sn = (v3/v2)*(v1/v4) + finally: + ctx.prec -= extra + return sn + +@defun +def jcn(ctx, u, m): + if abs(m) < ctx.eps: + return ctx.cos(u) + elif m == ctx.one: + return ctx.sech(u) + else: + extra = 10 + try: + ctx.prec += extra + q = ctx.calculate_nome(ctx.sqrt(m)) + v3 = ctx.jtheta(3, 0, q) + v2 = ctx.jtheta(2, 0, q) + v04 = ctx.jtheta(4, 0, q) + arg1 = u / (v3*v3) + v1 = ctx.jtheta(2, arg1, q) + v4 = ctx.jtheta(4, arg1, q) + cn = (v04/v2)*(v1/v4) + finally: + ctx.prec -= extra + return +cn + +@defun +def jdn(ctx, u, m): + if m == ctx.zero: + return ctx.one + elif m == ctx.one: + return ctx.sech(u) + else: + extra = 10 + try: + ctx.prec += extra + q = ctx.calculate_nome(ctx.sqrt(m)) + v3 = ctx.jtheta(3, 0, q) + v2 = ctx.jtheta(2, 0, q) + v04 = ctx.jtheta(4, 0, q) + arg1 = u / (v3*v3) + v1 = ctx.jtheta(3, arg1, q) + v4 = ctx.jtheta(4, arg1, q) + cn = (v04/v3)*(v1/v4) + finally: + ctx.prec -= extra + return +cn diff --git a/compiler/gdsMill/mpmath/functions/factorials.py b/compiler/gdsMill/mpmath/functions/factorials.py new file mode 100644 index 00000000..4fabc601 --- /dev/null +++ b/compiler/gdsMill/mpmath/functions/factorials.py @@ -0,0 +1,196 @@ +from functions import defun, defun_wrapped + +@defun +def gammaprod(ctx, a, b, _infsign=False): + a = [ctx.convert(x) for x in a] + b = [ctx.convert(x) for x in b] + poles_num = [] + poles_den = [] + regular_num = [] + regular_den = [] + for x in a: [regular_num, poles_num][ctx.isnpint(x)].append(x) + for x in b: [regular_den, poles_den][ctx.isnpint(x)].append(x) + # One more pole in numerator or denominator gives 0 or inf + if len(poles_num) < len(poles_den): return ctx.zero + if len(poles_num) > len(poles_den): + # Get correct sign of infinity for x+h, h -> 0 from above + # XXX: hack, this should be done properly + if _infsign: + a = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_num] + b = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_den] + return ctx.sign(ctx.gammaprod(a+regular_num,b+regular_den)) * ctx.inf + else: + return ctx.inf + # All poles cancel + # lim G(i)/G(j) = (-1)**(i+j) * gamma(1-j) / gamma(1-i) + p = ctx.one + orig = ctx.prec + try: + ctx.prec = orig + 15 + while poles_num: + i = poles_num.pop() + j = poles_den.pop() + p *= (-1)**(i+j) * ctx.gamma(1-j) / ctx.gamma(1-i) + for x in regular_num: p *= ctx.gamma(x) + for x in regular_den: p /= ctx.gamma(x) + finally: + ctx.prec = orig + return +p + +@defun +def beta(ctx, x, y): + x = ctx.convert(x) + y = ctx.convert(y) + if ctx.isinf(y): + x, y = y, x + if ctx.isinf(x): + if x == ctx.inf and not ctx._im(y): + if y == ctx.ninf: + return ctx.nan + if y > 0: + return ctx.zero + if ctx.isint(y): + return ctx.nan + if y < 0: + return ctx.sign(ctx.gamma(y)) * ctx.inf + return ctx.nan + return ctx.gammaprod([x, y], [x+y]) + +@defun +def binomial(ctx, n, k): + return ctx.gammaprod([n+1], [k+1, n-k+1]) + +@defun +def rf(ctx, x, n): + return ctx.gammaprod([x+n], [x]) + +@defun +def ff(ctx, x, n): + return ctx.gammaprod([x+1], [x-n+1]) + +@defun_wrapped +def fac2(ctx, x): + if ctx.isinf(x): + if x == ctx.inf: + return x + return ctx.nan + return 2**(x/2)*(ctx.pi/2)**((ctx.cospi(x)-1)/4)*ctx.gamma(x/2+1) + +@defun_wrapped +def barnesg(ctx, z): + if ctx.isinf(z): + if z == ctx.inf: + return z + return ctx.nan + if ctx.isnan(z): + return z + if (not ctx._im(z)) and ctx._re(z) <= 0 and ctx.isint(ctx._re(z)): + return z*0 + # Account for size (would not be needed if computing log(G)) + if abs(z) > 5: + ctx.dps += 2*ctx.log(abs(z),2) + # Reflection formula + if ctx.re(z) < -ctx.dps: + w = 1-z + pi2 = 2*ctx.pi + u = ctx.expjpi(2*w) + v = ctx.j*ctx.pi/12 - ctx.j*ctx.pi*w**2/2 + w*ctx.ln(1-u) - \ + ctx.j*ctx.polylog(2, u)/pi2 + v = ctx.barnesg(2-z)*ctx.exp(v)/pi2**w + if ctx._is_real_type(z): + v = ctx._re(v) + return v + # Estimate terms for asymptotic expansion + # TODO: fixme, obviously + N = ctx.dps // 2 + 5 + G = 1 + while abs(z) < N or ctx.re(z) < 1: + G /= ctx.gamma(z) + z += 1 + z -= 1 + s = ctx.mpf(1)/12 + s -= ctx.log(ctx.glaisher) + s += z*ctx.log(2*ctx.pi)/2 + s += (z**2/2-ctx.mpf(1)/12)*ctx.log(z) + s -= 3*z**2/4 + z2k = z2 = z**2 + for k in xrange(1, N+1): + t = ctx.bernoulli(2*k+2) / (4*k*(k+1)*z2k) + if abs(t) < ctx.eps: + #print k, N # check how many terms were needed + break + z2k *= z2 + s += t + #if k == N: + # print "warning: series for barnesg failed to converge", ctx.dps + return G*ctx.exp(s) + +@defun +def superfac(ctx, z): + return ctx.barnesg(z+2) + +@defun_wrapped +def hyperfac(ctx, z): + # XXX: estimate needed extra bits accurately + if z == ctx.inf: + return z + if abs(z) > 5: + extra = 4*int(ctx.log(abs(z),2)) + else: + extra = 0 + ctx.prec += extra + if not ctx._im(z) and ctx._re(z) < 0 and ctx.isint(ctx._re(z)): + n = int(ctx.re(z)) + h = ctx.hyperfac(-n-1) + if ((n+1)//2) & 1: + h = -h + if ctx._is_complex_type(z): + return h + 0j + return h + zp1 = z+1 + # Wrong branch cut + #v = ctx.gamma(zp1)**z + #ctx.prec -= extra + #return v / ctx.barnesg(zp1) + v = ctx.exp(z*ctx.loggamma(zp1)) + ctx.prec -= extra + return v / ctx.barnesg(zp1) + +@defun_wrapped +def loggamma(ctx, z): + a = ctx._re(z) + b = ctx._im(z) + if not b and a > 0: + return ctx.ln(ctx.gamma(z)) + u = ctx.arg(z) + w = ctx.ln(ctx.gamma(z)) + if b: + gi = -b - u/2 + a*u + b*ctx.ln(abs(z)) + n = ctx.floor((gi-ctx._im(w))/(2*ctx.pi)+0.5) * (2*ctx.pi) + return w + n*ctx.j + elif a < 0: + n = int(ctx.floor(a)) + w += (n-(n%2))*ctx.pi*ctx.j + return w + +''' +@defun +def psi0(ctx, z): + """Shortcut for psi(0,z) (the digamma function)""" + return ctx.psi(0, z) + +@defun +def psi1(ctx, z): + """Shortcut for psi(1,z) (the trigamma function)""" + return ctx.psi(1, z) + +@defun +def psi2(ctx, z): + """Shortcut for psi(2,z) (the tetragamma function)""" + return ctx.psi(2, z) + +@defun +def psi3(ctx, z): + """Shortcut for psi(3,z) (the pentagamma function)""" + return ctx.psi(3, z) +''' diff --git a/compiler/gdsMill/mpmath/functions/functions.py b/compiler/gdsMill/mpmath/functions/functions.py new file mode 100644 index 00000000..e956bf29 --- /dev/null +++ b/compiler/gdsMill/mpmath/functions/functions.py @@ -0,0 +1,435 @@ +class SpecialFunctions(object): + """ + This class implements special functions using high-level code. + + Elementary and some other functions (e.g. gamma function, basecase + hypergeometric series) are assumed to be predefined by the context as + "builtins" or "low-level" functions. + """ + defined_functions = {} + + # The series for the Jacobi theta functions converge for |q| < 1; + # in the current implementation they throw a ValueError for + # abs(q) > THETA_Q_LIM + THETA_Q_LIM = 1 - 10**-7 + + def __init__(self): + cls = self.__class__ + for name in cls.defined_functions: + f, wrap = cls.defined_functions[name] + cls._wrap_specfun(name, f, wrap) + + self.mpq_1 = self._mpq((1,1)) + self.mpq_0 = self._mpq((0,1)) + self.mpq_1_2 = self._mpq((1,2)) + self.mpq_3_2 = self._mpq((3,2)) + self.mpq_1_4 = self._mpq((1,4)) + self.mpq_1_16 = self._mpq((1,16)) + self.mpq_3_16 = self._mpq((3,16)) + self.mpq_5_2 = self._mpq((5,2)) + self.mpq_3_4 = self._mpq((3,4)) + self.mpq_7_4 = self._mpq((7,4)) + self.mpq_5_4 = self._mpq((5,4)) + + self._aliases.update({ + 'phase' : 'arg', + 'conjugate' : 'conj', + 'nthroot' : 'root', + 'polygamma' : 'psi', + 'hurwitz' : 'zeta', + #'digamma' : 'psi0', + #'trigamma' : 'psi1', + #'tetragamma' : 'psi2', + #'pentagamma' : 'psi3', + 'fibonacci' : 'fib', + 'factorial' : 'fac', + }) + + # Default -- do nothing + @classmethod + def _wrap_specfun(cls, name, f, wrap): + setattr(cls, name, f) + + # Optional fast versions of common functions in common cases. + # If not overridden, default (generic hypergeometric series) + # implementations will be used + def _besselj(ctx, n, z): raise NotImplementedError + def _erf(ctx, z): raise NotImplementedError + def _erfc(ctx, z): raise NotImplementedError + def _gamma_upper_int(ctx, z, a): raise NotImplementedError + def _expint_int(ctx, n, z): raise NotImplementedError + def _zeta(ctx, s): raise NotImplementedError + def _zetasum_fast(ctx, s, a, n, derivatives, reflect): raise NotImplementedError + def _ei(ctx, z): raise NotImplementedError + def _e1(ctx, z): raise NotImplementedError + def _ci(ctx, z): raise NotImplementedError + def _si(ctx, z): raise NotImplementedError + def _altzeta(ctx, s): raise NotImplementedError + +def defun_wrapped(f): + SpecialFunctions.defined_functions[f.__name__] = f, True + +def defun(f): + SpecialFunctions.defined_functions[f.__name__] = f, False + +def defun_static(f): + setattr(SpecialFunctions, f.__name__, f) + +@defun_wrapped +def cot(ctx, z): return ctx.one / ctx.tan(z) + +@defun_wrapped +def sec(ctx, z): return ctx.one / ctx.cos(z) + +@defun_wrapped +def csc(ctx, z): return ctx.one / ctx.sin(z) + +@defun_wrapped +def coth(ctx, z): return ctx.one / ctx.tanh(z) + +@defun_wrapped +def sech(ctx, z): return ctx.one / ctx.cosh(z) + +@defun_wrapped +def csch(ctx, z): return ctx.one / ctx.sinh(z) + +@defun_wrapped +def acot(ctx, z): return ctx.atan(ctx.one / z) + +@defun_wrapped +def asec(ctx, z): return ctx.acos(ctx.one / z) + +@defun_wrapped +def acsc(ctx, z): return ctx.asin(ctx.one / z) + +@defun_wrapped +def acoth(ctx, z): return ctx.atanh(ctx.one / z) + +@defun_wrapped +def asech(ctx, z): return ctx.acosh(ctx.one / z) + +@defun_wrapped +def acsch(ctx, z): return ctx.asinh(ctx.one / z) + +@defun +def sign(ctx, x): + x = ctx.convert(x) + if not x or ctx.isnan(x): + return x + if ctx._is_real_type(x): + return ctx.mpf(cmp(x, 0)) + return x / abs(x) + +@defun +def agm(ctx, a, b=1): + if b == 1: + return ctx.agm1(a) + a = ctx.convert(a) + b = ctx.convert(b) + return ctx._agm(a, b) + +@defun_wrapped +def sinc(ctx, x): + if ctx.isinf(x): + return 1/x + if not x: + return x+1 + return ctx.sin(x)/x + +@defun_wrapped +def sincpi(ctx, x): + if ctx.isinf(x): + return 1/x + if not x: + return x+1 + return ctx.sinpi(x)/(ctx.pi*x) + +# TODO: tests; improve implementation +@defun_wrapped +def expm1(ctx, x): + if not x: + return ctx.zero + # exp(x) - 1 ~ x + if ctx.mag(x) < -ctx.prec: + return x + 0.5*x**2 + # TODO: accurately eval the smaller of the real/imag parts + return ctx.sum_accurately(lambda: iter([ctx.exp(x),-1]),1) + +@defun_wrapped +def powm1(ctx, x, y): + mag = ctx.mag + one = ctx.one + w = x**y - one + M = mag(w) + # Only moderate cancellation + if M > -8: + return w + # Check for the only possible exact cases + if not w: + if (not y) or (x in (1, -1, 1j, -1j) and ctx.isint(y)): + return w + x1 = x - one + magy = mag(y) + lnx = ctx.ln(x) + # Small y: x^y - 1 ~ log(x)*y + O(log(x)^2 * y^2) + if magy + mag(lnx) < -ctx.prec: + return lnx*y + (lnx*y)**2/2 + # TODO: accurately eval the smaller of the real/imag part + return ctx.sum_accurately(lambda: iter([x**y, -1]), 1) + +@defun +def _rootof1(ctx, k, n): + k = int(k) + n = int(n) + k %= n + if not k: + return ctx.one + elif 2*k == n: + return -ctx.one + elif 4*k == n: + return ctx.j + elif 4*k == 3*n: + return -ctx.j + return ctx.expjpi(2*ctx.mpf(k)/n) + +@defun +def root(ctx, x, n, k=0): + n = int(n) + x = ctx.convert(x) + if k: + # Special case: there is an exact real root + if (n & 1 and 2*k == n-1) and (not ctx.im(x)) and (ctx.re(x) < 0): + return -ctx.root(-x, n) + # Multiply by root of unity + prec = ctx.prec + try: + ctx.prec += 10 + v = ctx.root(x, n, 0) * ctx._rootof1(k, n) + finally: + ctx.prec = prec + return +v + return ctx._nthroot(x, n) + +@defun +def unitroots(ctx, n, primitive=False): + gcd = ctx._gcd + prec = ctx.prec + try: + ctx.prec += 10 + if primitive: + v = [ctx._rootof1(k,n) for k in range(n) if gcd(k,n) == 1] + else: + # TODO: this can be done *much* faster + v = [ctx._rootof1(k,n) for k in range(n)] + finally: + ctx.prec = prec + return [+x for x in v] + +@defun +def arg(ctx, x): + x = ctx.convert(x) + re = ctx._re(x) + im = ctx._im(x) + return ctx.atan2(im, re) + +@defun +def fabs(ctx, x): + return abs(ctx.convert(x)) + +@defun +def re(ctx, x): + x = ctx.convert(x) + if hasattr(x, "real"): # py2.5 doesn't have .real/.imag for all numbers + return x.real + return x + +@defun +def im(ctx, x): + x = ctx.convert(x) + if hasattr(x, "imag"): # py2.5 doesn't have .real/.imag for all numbers + return x.imag + return ctx.zero + +@defun +def conj(ctx, x): + return ctx.convert(x).conjugate() + +@defun +def polar(ctx, z): + return (ctx.fabs(z), ctx.arg(z)) + +@defun_wrapped +def rect(ctx, r, phi): + return r * ctx.mpc(*ctx.cos_sin(phi)) + +@defun +def log(ctx, x, b=None): + if b is None: + return ctx.ln(x) + wp = ctx.prec + 20 + return ctx.ln(x, prec=wp) / ctx.ln(b, prec=wp) + +@defun +def log10(ctx, x): + return ctx.log(x, 10) + +@defun +def modf(ctx, x, y): + return ctx.convert(x) % ctx.convert(y) + +@defun +def degrees(ctx, x): + return x / ctx.degree + +@defun +def radians(ctx, x): + return x * ctx.degree + +@defun_wrapped +def lambertw(ctx, z, k=0): + k = int(k) + if ctx.isnan(z): + return z + ctx.prec += 20 + mag = ctx.mag(z) + # Start from fp approximation + if ctx is ctx._mp and abs(mag) < 900 and abs(k) < 10000 and \ + abs(z+0.36787944117144) > 0.01: + w = ctx._fp.lambertw(z, k) + else: + absz = abs(z) + # We must be extremely careful near the singularities at -1/e and 0 + u = ctx.exp(-1) + if absz <= u: + if not z: + # w(0,0) = 0; for all other branches we hit the pole + if not k: + return z + return ctx.ninf + if not k: + w = z + # For small real z < 0, the -1 branch aves roughly like log(-z) + elif k == -1 and not ctx.im(z) and ctx.re(z) < 0: + w = ctx.ln(-z) + # Use a simple asymptotic approximation. + else: + w = ctx.ln(z) + # The branches are roughly logarithmic. This approximation + # gets better for large |k|; need to check that this always + # works for k ~= -1, 0, 1. + if k: w += k * 2*ctx.pi*ctx.j + elif k == 0 and ctx.im(z) and absz <= 0.7: + # Both the W(z) ~= z and W(z) ~= ln(z) approximations break + # down around z ~= -0.5 (converging to the wrong branch), so patch + # with a constant approximation (adjusted for sign) + if abs(z+0.5) < 0.1: + if ctx.im(z) > 0: + w = ctx.mpc(0.7+0.7j) + else: + w = ctx.mpc(0.7-0.7j) + else: + w = z + else: + if z == ctx.inf: + if k == 0: + return z + else: + return z + 2*k*ctx.pi*ctx.j + if z == ctx.ninf: + return (-z) + (2*k+1)*ctx.pi*ctx.j + # Simple asymptotic approximation as above + w = ctx.ln(z) + if k: + w += k * 2*ctx.pi*ctx.j + # Use Halley iteration to solve w*exp(w) = z + two = ctx.mpf(2) + weps = ctx.ldexp(ctx.eps, 15) + for i in xrange(100): + ew = ctx.exp(w) + wew = w*ew + wewz = wew-z + wn = w - wewz/(wew+ew-(w+two)*wewz/(two*w+two)) + if abs(wn-w) < weps*abs(wn): + return wn + else: + w = wn + ctx.warn("Lambert W iteration failed to converge for %s" % z) + return wn + +@defun_wrapped +def bell(ctx, n, x=1): + x = ctx.convert(x) + if not n: + if ctx.isnan(x): + return x + return type(x)(1) + if ctx.isinf(x) or ctx.isinf(n) or ctx.isnan(x) or ctx.isnan(n): + return x**n + if n == 1: return x + if n == 2: return x*(x+1) + if x == 0: return ctx.sincpi(n) + return _polyexp(ctx, n, x, True) / ctx.exp(x) + +def _polyexp(ctx, n, x, extra=False): + def _terms(): + if extra: + yield ctx.sincpi(n) + t = x + k = 1 + while 1: + yield k**n * t + k += 1 + t = t*x/k + return ctx.sum_accurately(_terms, check_step=4) + +@defun_wrapped +def polyexp(ctx, s, z): + if ctx.isinf(z) or ctx.isinf(s) or ctx.isnan(z) or ctx.isnan(s): + return z**s + if z == 0: return z*s + if s == 0: return ctx.expm1(z) + if s == 1: return ctx.exp(z)*z + if s == 2: return ctx.exp(z)*z*(z+1) + return _polyexp(ctx, s, z) + +@defun_wrapped +def cyclotomic(ctx, n, z): + n = int(n) + assert n >= 0 + p = ctx.one + if n == 0: + return p + if n == 1: + return z - p + if n == 2: + return z + p + # Use divisor product representation. Unfortunately, this sometimes + # includes singularities for roots of unity, which we have to cancel out. + # Matching zeros/poles pairwise, we have (1-z^a)/(1-z^b) ~ a/b + O(z-1). + a_prod = 1 + b_prod = 1 + num_zeros = 0 + num_poles = 0 + for d in range(1,n+1): + if not n % d: + w = ctx.moebius(n//d) + # Use powm1 because it is important that we get 0 only + # if it really is exactly 0 + b = -ctx.powm1(z, d) + if b: + p *= b**w + else: + if w == 1: + a_prod *= d + num_zeros += 1 + elif w == -1: + b_prod *= d + num_poles += 1 + #print n, num_zeros, num_poles + if num_zeros: + if num_zeros > num_poles: + p *= 0 + else: + p *= a_prod + p /= b_prod + return p diff --git a/compiler/gdsMill/mpmath/functions/hypergeometric.py b/compiler/gdsMill/mpmath/functions/hypergeometric.py new file mode 100644 index 00000000..915a9eb9 --- /dev/null +++ b/compiler/gdsMill/mpmath/functions/hypergeometric.py @@ -0,0 +1,2060 @@ +from functions import defun, defun_wrapped + +def _check_need_perturb(ctx, terms, prec, discard_known_zeros): + perturb = recompute = False + extraprec = 0 + discard = [] + for term_index, term in enumerate(terms): + w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term + have_singular_nongamma_weight = False + # Avoid division by zero in leading factors (TODO: + # also check for near division by zero?) + for k, w in enumerate(w_s): + if not w: + if ctx.re(c_s[k]) <= 0 and c_s[k]: + perturb = recompute = True + have_singular_nongamma_weight = True + pole_count = [0, 0, 0] + # Check for gamma and series poles and near-poles + for data_index, data in enumerate([alpha_s, beta_s, b_s]): + for i, x in enumerate(data): + n, d = ctx.nint_distance(x) + # Poles + if n > 0: + continue + if d == ctx.ninf: + # OK if we have a polynomial + # ------------------------------ + ok = False + if data_index == 2: + for u in a_s: + if ctx.isnpint(u) and u >= int(n): + ok = True + break + if ok: + continue + pole_count[data_index] += 1 + # ------------------------------ + #perturb = recompute = True + #return perturb, recompute, extraprec + elif d < -4: + extraprec += -d + recompute = True + if discard_known_zeros and pole_count[1] > pole_count[0] + pole_count[2] \ + and not have_singular_nongamma_weight: + discard.append(term_index) + elif sum(pole_count): + perturb = recompute = True + return perturb, recompute, extraprec, discard + +_hypercomb_msg = """ +hypercomb() failed to converge to the requested %i bits of accuracy +using a working precision of %i bits. The function value may be zero or +infinite; try passing zeroprec=N or infprec=M to bound finite values between +2^(-N) and 2^M. Otherwise try a higher maxprec or maxterms. +""" + +@defun +def hypercomb(ctx, function, params=[], discard_known_zeros=True, **kwargs): + orig = ctx.prec + sumvalue = ctx.zero + dist = ctx.nint_distance + ninf = ctx.ninf + orig_params = params[:] + verbose = kwargs.get('verbose', False) + maxprec = kwargs.get('maxprec', ctx._default_hyper_maxprec(orig)) + kwargs['maxprec'] = maxprec # For calls to hypsum + zeroprec = kwargs.get('zeroprec') + infprec = kwargs.get('infprec') + perturbed_reference_value = None + hextra = 0 + try: + while 1: + ctx.prec += 10 + if ctx.prec > maxprec: + raise ValueError(_hypercomb_msg % (orig, ctx.prec)) + orig2 = ctx.prec + params = orig_params[:] + terms = function(*params) + if verbose: + print + print "ENTERING hypercomb main loop" + print "prec =", ctx.prec + print "hextra", hextra + perturb, recompute, extraprec, discard = \ + _check_need_perturb(ctx, terms, orig, discard_known_zeros) + ctx.prec += extraprec + if perturb: + if "hmag" in kwargs: + hmag = kwargs["hmag"] + elif ctx._fixed_precision: + hmag = int(ctx.prec*0.3) + else: + hmag = orig + 10 + hextra + h = ctx.ldexp(ctx.one, -hmag) + ctx.prec = orig2 + 10 + hmag + 10 + for k in range(len(params)): + params[k] += h + # Heuristically ensure that the perturbations + # are "independent" so that two perturbations + # don't accidentally cancel each other out + # in a subtraction. + h += h/(k+1) + if recompute: + terms = function(*params) + if discard_known_zeros: + terms = [term for (i, term) in enumerate(terms) if i not in discard] + if not terms: + return ctx.zero + evaluated_terms = [] + for term_index, term_data in enumerate(terms): + w_s, c_s, alpha_s, beta_s, a_s, b_s, z = term_data + if verbose: + print + print " Evaluating term %i/%i : %iF%i" % \ + (term_index+1, len(terms), len(a_s), len(b_s)) + print " powers", ctx.nstr(w_s), ctx.nstr(c_s) + print " gamma", ctx.nstr(alpha_s), ctx.nstr(beta_s) + print " hyper", ctx.nstr(a_s), ctx.nstr(b_s) + print " z", ctx.nstr(z) + v = ctx.hyper(a_s, b_s, z, **kwargs) + for a in alpha_s: v *= ctx.gamma(a) + for b in beta_s: v /= ctx.gamma(b) + for w, c in zip(w_s, c_s): v *= ctx.power(w, c) + if verbose: + print " Value:", v + evaluated_terms.append(v) + + if len(terms) == 1 and (not perturb): + sumvalue = evaluated_terms[0] + break + + if ctx._fixed_precision: + sumvalue = ctx.fsum(evaluated_terms) + break + + sumvalue = ctx.fsum(evaluated_terms) + term_magnitudes = [ctx.mag(x) for x in evaluated_terms] + max_magnitude = max(term_magnitudes) + sum_magnitude = ctx.mag(sumvalue) + cancellation = max_magnitude - sum_magnitude + if verbose: + print + print " Cancellation:", cancellation, "bits" + print " Increased precision:", ctx.prec - orig, "bits" + + precision_ok = cancellation < ctx.prec - orig + + if zeroprec is None: + zero_ok = False + else: + zero_ok = max_magnitude - ctx.prec < -zeroprec + if infprec is None: + inf_ok = False + else: + inf_ok = max_magnitude > infprec + + if precision_ok and (not perturb) or ctx.isnan(cancellation): + break + elif precision_ok: + if perturbed_reference_value is None: + hextra += 20 + perturbed_reference_value = sumvalue + continue + elif ctx.mag(sumvalue - perturbed_reference_value) <= \ + ctx.mag(sumvalue) - orig: + break + elif zero_ok: + sumvalue = ctx.zero + break + elif inf_ok: + sumvalue = ctx.inf + break + elif 'hmag' in kwargs: + break + else: + hextra *= 2 + perturbed_reference_value = sumvalue + # Increase precision + else: + increment = min(max(cancellation, orig//2), max(extraprec,orig)) + ctx.prec += increment + if verbose: + print " Must start over with increased precision" + continue + finally: + ctx.prec = orig + return +sumvalue + +@defun +def hyper(ctx, a_s, b_s, z, **kwargs): + """ + Hypergeometric function, general case. + """ + z = ctx.convert(z) + p = len(a_s) + q = len(b_s) + a_s = map(ctx._convert_param, a_s) + b_s = map(ctx._convert_param, b_s) + # Reduce degree by eliminating common parameters + if kwargs.get('eliminate', True): + i = 0 + while i < q and a_s: + b = b_s[i] + if b in a_s: + a_s.remove(b) + b_s.remove(b) + p -= 1 + q -= 1 + else: + i += 1 + # Handle special cases + if p == 0: + if q == 1: return ctx._hyp0f1(b_s, z, **kwargs) + elif q == 0: return ctx.exp(z) + elif p == 1: + if q == 1: return ctx._hyp1f1(a_s, b_s, z, **kwargs) + elif q == 2: return ctx._hyp1f2(a_s, b_s, z, **kwargs) + elif q == 0: return ctx._hyp1f0(a_s[0][0], z) + elif p == 2: + if q == 1: return ctx._hyp2f1(a_s, b_s, z, **kwargs) + elif q == 2: return ctx._hyp2f2(a_s, b_s, z, **kwargs) + elif q == 3: return ctx._hyp2f3(a_s, b_s, z, **kwargs) + elif q == 0: return ctx._hyp2f0(a_s, b_s, z, **kwargs) + elif p == q+1: + return ctx._hypq1fq(p, q, a_s, b_s, z, **kwargs) + elif p > q+1 and not kwargs.get('force_series'): + return ctx._hyp_borel(p, q, a_s, b_s, z, **kwargs) + coeffs, types = zip(*(a_s+b_s)) + return ctx.hypsum(p, q, types, coeffs, z, **kwargs) + +@defun +def hyp0f1(ctx,b,z,**kwargs): + return ctx.hyper([],[b],z,**kwargs) + +@defun +def hyp1f1(ctx,a,b,z,**kwargs): + return ctx.hyper([a],[b],z,**kwargs) + +@defun +def hyp1f2(ctx,a1,b1,b2,z,**kwargs): + return ctx.hyper([a1],[b1,b2],z,**kwargs) + +@defun +def hyp2f1(ctx,a,b,c,z,**kwargs): + return ctx.hyper([a,b],[c],z,**kwargs) + +@defun +def hyp2f2(ctx,a1,a2,b1,b2,z,**kwargs): + return ctx.hyper([a1,a2],[b1,b2],z,**kwargs) + +@defun +def hyp2f3(ctx,a1,a2,b1,b2,b3,z,**kwargs): + return ctx.hyper([a1,a2],[b1,b2,b3],z,**kwargs) + +@defun +def hyp2f0(ctx,a,b,z,**kwargs): + return ctx.hyper([a,b],[],z,**kwargs) + +@defun +def hyp3f2(ctx,a1,a2,a3,b1,b2,z,**kwargs): + return ctx.hyper([a1,a2,a3],[b1,b2],z,**kwargs) + +@defun_wrapped +def _hyp1f0(ctx, a, z): + return (1-z) ** (-a) + +@defun +def _hyp0f1(ctx, b_s, z, **kwargs): + (b, btype), = b_s + if z: + magz = ctx.mag(z) + else: + magz = 0 + if magz >= 8 and not kwargs.get('force_series'): + try: + # http://functions.wolfram.com/HypergeometricFunctions/ + # Hypergeometric0F1/06/02/03/0004/ + # We don't need hypercomb because the only possible singularity + # occurs when the value is undefined. However, we should perhaps + # still check for cancellation... + # TODO: handle the all-real case more efficiently! + # TODO: figure out how much precision is needed (exponential growth) + orig = ctx.prec + try: + ctx.prec += 12 + magz//2 + w = ctx.sqrt(-z) + jw = ctx.j*w + u = 1/(4*jw) + c = ctx.mpq_1_2 - b + E = ctx.exp(2*jw) + H1 = (-jw)**c/E*ctx.hyp2f0(b-ctx.mpq_1_2, ctx.mpq_3_2-b, -u, + force_series=True) + H2 = (jw)**c*E*ctx.hyp2f0(b-ctx.mpq_1_2, ctx.mpq_3_2-b, u, + force_series=True) + v = ctx.gamma(b)/(2*ctx.sqrt(ctx.pi))*(H1 + H2) + finally: + ctx.prec = orig + if ctx._is_real_type(b) and ctx._is_real_type(z): + v = ctx._re(v) + return +v + except ctx.NoConvergence: + pass + return ctx.hypsum(0, 1, (btype,), [b], z, **kwargs) + +@defun +def _hyp1f1(ctx, a_s, b_s, z, **kwargs): + (a, atype), = a_s + (b, btype), = b_s + if not z: + return ctx.one+z + magz = ctx.mag(z) + if magz >= 7 and not (ctx.isint(a) and ctx.re(a) <= 0): + if ctx.isinf(z): + if ctx.sign(a) == ctx.sign(b) == ctx.sign(z) == 1: + return ctx.inf + return ctx.nan * z + try: + try: + ctx.prec += magz + sector = ctx._im(z) < 0 and ctx._re(z) <= 0 + def h(a,b): + if sector: + E = ctx.expjpi(ctx.fneg(a, exact=True)) + else: + E = ctx.expjpi(a) + rz = 1/z + T1 = ([E,z], [1,-a], [b], [b-a], [a, 1+a-b], [], -rz) + T2 = ([ctx.exp(z),z], [1,a-b], [b], [a], [b-a, 1-a], [], rz) + return T1, T2 + v = ctx.hypercomb(h, [a,b], force_series=True) + if ctx._is_real_type(a) and ctx._is_real_type(b) and ctx._is_real_type(z): + v = ctx._re(v) + return +v + except ctx.NoConvergence: + pass + finally: + ctx.prec -= magz + v = ctx.hypsum(1, 1, (atype, btype), [a, b], z, **kwargs) + return v + +def _hyp2f1_gosper(ctx,a,b,c,z,**kwargs): + # Use Gosper's recurrence + # See http://www.math.utexas.edu/pipermail/maxima/2006/000126.html + _a,_b,_c,_z = a, b, c, z + orig = ctx.prec + maxprec = kwargs.get('maxprec', 100*orig) + extra = 10 + while 1: + ctx.prec = orig + extra + #a = ctx.convert(_a) + #b = ctx.convert(_b) + #c = ctx.convert(_c) + z = ctx.convert(_z) + d = ctx.mpf(0) + e = ctx.mpf(1) + f = ctx.mpf(0) + k = 0 + # Common subexpression elimination, unfortunately making + # things a bit unreadable. The formula is quite messy to begin + # with, though... + abz = a*b*z + ch = c * ctx.mpq_1_2 + c1h = (c+1) * ctx.mpq_1_2 + nz = 1-z + g = z/nz + abg = a*b*g + cba = c-b-a + z2 = z-2 + tol = -ctx.prec - 10 + nstr = ctx.nstr + nprint = ctx.nprint + mag = ctx.mag + maxmag = ctx.ninf + while 1: + kch = k+ch + kakbz = (k+a)*(k+b)*z / (4*(k+1)*kch*(k+c1h)) + d1 = kakbz*(e-(k+cba)*d*g) + e1 = kakbz*(d*abg+(k+c)*e) + ft = d*(k*(cba*z+k*z2-c)-abz)/(2*kch*nz) + f1 = f + e - ft + maxmag = max(maxmag, mag(f1)) + if mag(f1-f) < tol: + break + d, e, f = d1, e1, f1 + k += 1 + cancellation = maxmag - mag(f1) + if cancellation < extra: + break + else: + extra += cancellation + if extra > maxprec: + raise ctx.NoConvergence + return f1 + +@defun +def _hyp2f1(ctx, a_s, b_s, z, **kwargs): + (a, atype), (b, btype) = a_s + (c, ctype), = b_s + if z == 1: + # TODO: the following logic can be simplified + convergent = ctx.re(c-a-b) > 0 + finite = (ctx.isint(a) and a <= 0) or (ctx.isint(b) and b <= 0) + zerodiv = ctx.isint(c) and c <= 0 and not \ + ((ctx.isint(a) and c <= a <= 0) or (ctx.isint(b) and c <= b <= 0)) + #print "bz", a, b, c, z, convergent, finite, zerodiv + # Gauss's theorem gives the value if convergent + if (convergent or finite) and not zerodiv: + return ctx.gammaprod([c, c-a-b], [c-a, c-b], _infsign=True) + # Otherwise, there is a pole and we take the + # sign to be that when approaching from below + # XXX: this evaluation is not necessarily correct in all cases + return ctx.hyp2f1(a,b,c,1-ctx.eps*2) * ctx.inf + + # Equal to 1 (first term), unless there is a subsequent + # division by zero + if not z: + # Division by zero but power of z is higher than + # first order so cancels + if c or a == 0 or b == 0: + return 1+z + # Indeterminate + return ctx.nan + + # Hit zero denominator unless numerator goes to 0 first + if ctx.isint(c) and c <= 0: + if (ctx.isint(a) and c <= a <= 0) or \ + (ctx.isint(b) and c <= b <= 0): + pass + else: + # Pole in series + return ctx.inf + + absz = abs(z) + + # Fast case: standard series converges rapidly, + # possibly in finitely many terms + if absz <= 0.8 or (ctx.isint(a) and a <= 0 and a >= -1000) or \ + (ctx.isint(b) and b <= 0 and b >= -1000): + return ctx.hypsum(2, 1, (atype, btype, ctype), [a, b, c], z, **kwargs) + + orig = ctx.prec + try: + ctx.prec += 10 + + # Use 1/z transformation + if absz >= 1.3: + def h(a,b): + t = ctx.mpq_1-c; ab = a-b; rz = 1/z + T1 = ([-z],[-a], [c,-ab],[b,c-a], [a,t+a],[ctx.mpq_1+ab], rz) + T2 = ([-z],[-b], [c,ab],[a,c-b], [b,t+b],[ctx.mpq_1-ab], rz) + return T1, T2 + v = ctx.hypercomb(h, [a,b], **kwargs) + + # Use 1-z transformation + elif abs(1-z) <= 0.75: + def h(a,b): + t = c-a-b; ca = c-a; cb = c-b; rz = 1-z + T1 = [], [], [c,t], [ca,cb], [a,b], [1-t], rz + T2 = [rz], [t], [c,a+b-c], [a,b], [ca,cb], [1+t], rz + return T1, T2 + v = ctx.hypercomb(h, [a,b], **kwargs) + + # Use z/(z-1) transformation + elif abs(z/(z-1)) <= 0.75: + v = ctx.hyp2f1(a, c-b, c, z/(z-1)) / (1-z)**a + + # Remaining part of unit circle + else: + v = _hyp2f1_gosper(ctx,a,b,c,z,**kwargs) + + finally: + ctx.prec = orig + return +v + +@defun +def _hypq1fq(ctx, p, q, a_s, b_s, z, **kwargs): + r""" + Evaluates 3F2, 4F3, 5F4, ... + """ + a_s, a_types = zip(*a_s) + b_s, b_types = zip(*b_s) + a_s = list(a_s) + b_s = list(b_s) + absz = abs(z) + ispoly = False + for a in a_s: + if ctx.isint(a) and a <= 0: + ispoly = True + break + # Direct summation + if absz < 1 or ispoly: + try: + return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs) + except ctx.NoConvergence: + if absz > 1.1 or ispoly: + raise + # Use expansion at |z-1| -> 0. + # Reference: Wolfgang Buhring, "Generalized Hypergeometric Functions at + # Unit Argument", Proc. Amer. Math. Soc., Vol. 114, No. 1 (Jan. 1992), + # pp.145-153 + # The current implementation has several problems: + # 1. We only implement it for 3F2. The expansion coefficients are + # given by extremely messy nested sums in the higher degree cases + # (see reference). Is efficient sequential generation of the coefficients + # possible in the > 3F2 case? + # 2. Although the series converges, it may do so slowly, so we need + # convergence acceleration. The acceleration implemented by + # nsum does not always help, so results returned are sometimes + # inaccurate! Can we do better? + # 3. We should check conditions for convergence, and possibly + # do a better job of cancelling out gamma poles if possible. + if z == 1: + # XXX: should also check for division by zero in the + # denominator of the series (cf. hyp2f1) + S = ctx.re(sum(b_s)-sum(a_s)) + if S <= 0: + #return ctx.hyper(a_s, b_s, 1-ctx.eps*2, **kwargs) * ctx.inf + return ctx.hyper(a_s, b_s, 0.9, **kwargs) * ctx.inf + if (p,q) == (3,2) and abs(z-1) < 0.05: # and kwargs.get('sum1') + #print "Using alternate summation (experimental)" + a1,a2,a3 = a_s + b1,b2 = b_s + u = b1+b2-a3 + initial = ctx.gammaprod([b2-a3,b1-a3,a1,a2],[b2-a3,b1-a3,1,u]) + def term(k, _cache={0:initial}): + u = b1+b2-a3+k + if k in _cache: + t = _cache[k] + else: + t = _cache[k-1] + t *= (b1+k-a3-1)*(b2+k-a3-1) + t /= k*(u-1) + _cache[k] = t + return t * ctx.hyp2f1(a1,a2,u,z) + try: + S = ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'), + strict=kwargs.get('strict', True)) + return S * ctx.gammaprod([b1,b2],[a1,a2,a3]) + except ctx.NoConvergence: + pass + # Try to use convergence acceleration on and close to the unit circle. + # Problem: the convergence acceleration degenerates as |z-1| -> 0, + # except for special cases. Everywhere else, the Shanks transformation + # is very efficient. + if absz < 1.1 and ctx._re(z) <= 1: + def term(k, _cache={0:ctx.one}): + k = int(k) + if k in _cache: + return _cache[k] + t = _cache[k-1] + m = k-1 + for j in xrange(p): t *= (a_s[j]+m) + for j in xrange(q): t /= (b_s[j]+m) + t *= z + t /= k + _cache[k] = t + return t + return ctx.nsum(term, [0,ctx.inf], verbose=kwargs.get('verbose'), + strict=kwargs.get('strict', True)) + # Use 1/z transformation + # http://functions.wolfram.com/HypergeometricFunctions/ + # HypergeometricPFQ/06/01/05/02/0004/ + def h(*args): + a_s = list(args[:p]) + b_s = list(args[p:]) + Ts = [] + recz = ctx.one/z + negz = ctx.fneg(z, exact=True) + for k in range(q+1): + ak = a_s[k] + C = [negz] + Cp = [-ak] + Gn = b_s + [ak] + [a_s[j]-ak for j in range(q+1) if j != k] + Gd = a_s + [b_s[j]-ak for j in range(q)] + Fn = [ak] + [ak-b_s[j]+1 for j in range(q)] + Fd = [1-a_s[j]+ak for j in range(q+1) if j != k] + Ts.append((C, Cp, Gn, Gd, Fn, Fd, recz)) + return Ts + return ctx.hypercomb(h, a_s+b_s, **kwargs) + +@defun +def _hyp_borel(ctx, p, q, a_s, b_s, z, **kwargs): + if a_s: + a_s, a_types = zip(*a_s) + a_s = list(a_s) + else: + a_s, a_types = [], () + if b_s: + b_s, b_types = zip(*b_s) + b_s = list(b_s) + else: + b_s, b_types = [], () + kwargs['maxterms'] = kwargs.get('maxterms', ctx.prec) + try: + return ctx.hypsum(p, q, a_types+b_types, a_s+b_s, z, **kwargs) + except ctx.NoConvergence: + pass + prec = ctx.prec + try: + tol = kwargs.get('asymp_tol', ctx.eps/4) + ctx.prec += 10 + # hypsum is has a conservative tolerance. So we try again: + def term(k, cache={0:ctx.one}): + if k in cache: + return cache[k] + t = term(k-1) + for a in a_s: t *= (a+(k-1)) + for b in b_s: t /= (b+(k-1)) + t *= z + t /= k + cache[k] = t + return t + s = ctx.one + for k in xrange(1, ctx.prec): + t = term(k) + s += t + if abs(t) <= tol: + return s + finally: + ctx.prec = prec + if p <= q+3: + contour = kwargs.get('contour') + if not contour: + if ctx.arg(z) < 0.25: + u = z / max(1, abs(z)) + if ctx.arg(z) >= 0: + contour = [0, 2j, (2j+2)/u, 2/u, ctx.inf] + else: + contour = [0, -2j, (-2j+2)/u, 2/u, ctx.inf] + #contour = [0, 2j/z, 2/z, ctx.inf] + #contour = [0, 2j, 2/z, ctx.inf] + #contour = [0, 2j, ctx.inf] + else: + contour = [0, ctx.inf] + quad_kwargs = kwargs.get('quad_kwargs', {}) + def g(t): + return ctx.exp(-t)*ctx.hyper(a_s, b_s+[1], t*z) + I, err = ctx.quad(g, contour, error=True, **quad_kwargs) + if err <= abs(I)*ctx.eps*8: + return I + raise ctx.NoConvergence + + +@defun +def _hyp2f2(ctx, a_s, b_s, z, **kwargs): + (a1, a1type), (a2, a2type) = a_s + (b1, b1type), (b2, b2type) = b_s + + absz = abs(z) + magz = ctx.mag(z) + orig = ctx.prec + + # Asymptotic expansion is ~ exp(z) + asymp_extraprec = magz + + # Asymptotic series is in terms of 3F1 + can_use_asymptotic = (not kwargs.get('force_series')) and \ + (ctx.mag(absz) > 3) + + # TODO: much of the following could be shared with 2F3 instead of + # copypasted + if can_use_asymptotic: + #print "using asymp" + try: + try: + ctx.prec += asymp_extraprec + # http://functions.wolfram.com/HypergeometricFunctions/ + # Hypergeometric2F2/06/02/02/0002/ + def h(a1,a2,b1,b2): + X = a1+a2-b1-b2 + A2 = a1+a2 + B2 = b1+b2 + c = {} + c[0] = ctx.one + c[1] = (A2-1)*X+b1*b2-a1*a2 + s1 = 0 + k = 0 + tprev = 0 + while 1: + if k not in c: + uu1 = 1-B2+2*a1+a1**2+2*a2+a2**2-A2*B2+a1*a2+b1*b2+(2*B2-3*(A2+1))*k+2*k**2 + uu2 = (k-A2+b1-1)*(k-A2+b2-1)*(k-X-2) + c[k] = ctx.one/k * (uu1*c[k-1]-uu2*c[k-2]) + t1 = c[k] * z**(-k) + if abs(t1) < 0.1*ctx.eps: + #print "Convergence :)" + break + # Quit if the series doesn't converge quickly enough + if k > 5 and abs(tprev) / abs(t1) < 1.5: + #print "No convergence :(" + raise ctx.NoConvergence + s1 += t1 + tprev = t1 + k += 1 + S = ctx.exp(z)*s1 + T1 = [z,S], [X,1], [b1,b2],[a1,a2],[],[],0 + T2 = [-z],[-a1],[b1,b2,a2-a1],[a2,b1-a1,b2-a1],[a1,a1-b1+1,a1-b2+1],[a1-a2+1],-1/z + T3 = [-z],[-a2],[b1,b2,a1-a2],[a1,b1-a2,b2-a2],[a2,a2-b1+1,a2-b2+1],[-a1+a2+1],-1/z + return T1, T2, T3 + v = ctx.hypercomb(h, [a1,a2,b1,b2], force_series=True, maxterms=4*ctx.prec) + if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,z]) == 5: + v = ctx.re(v) + return v + except ctx.NoConvergence: + pass + finally: + ctx.prec = orig + + return ctx.hypsum(2, 2, (a1type, a2type, b1type, b2type), [a1, a2, b1, b2], z, **kwargs) + + + +@defun +def _hyp1f2(ctx, a_s, b_s, z, **kwargs): + (a1, a1type), = a_s + (b1, b1type), (b2, b2type) = b_s + + absz = abs(z) + magz = ctx.mag(z) + orig = ctx.prec + + # Asymptotic expansion is ~ exp(sqrt(z)) + asymp_extraprec = z and magz//2 + + # Asymptotic series is in terms of 3F0 + can_use_asymptotic = (not kwargs.get('force_series')) and \ + (ctx.mag(absz) > 19) and \ + (ctx.sqrt(absz) > 1.5*orig) #and \ + #ctx._hyp_check_convergence([a1, a1-b1+1, a1-b2+1], [], + # 1/absz, orig+40+asymp_extraprec) + + # TODO: much of the following could be shared with 2F3 instead of + # copypasted + if can_use_asymptotic: + #print "using asymp" + try: + try: + ctx.prec += asymp_extraprec + # http://functions.wolfram.com/HypergeometricFunctions/ + # Hypergeometric1F2/06/02/03/ + def h(a1,b1,b2): + X = ctx.mpq_1_2*(a1-b1-b2+ctx.mpq_1_2) + c = {} + c[0] = ctx.one + c[1] = 2*(ctx.mpq_1_4*(3*a1+b1+b2-2)*(a1-b1-b2)+b1*b2-ctx.mpq_3_16) + c[2] = 2*(b1*b2+ctx.mpq_1_4*(a1-b1-b2)*(3*a1+b1+b2-2)-ctx.mpq_3_16)**2+\ + ctx.mpq_1_16*(-16*(2*a1-3)*b1*b2 + \ + 4*(a1-b1-b2)*(-8*a1**2+11*a1+b1+b2-2)-3) + s1 = 0 + s2 = 0 + k = 0 + tprev = 0 + while 1: + if k not in c: + uu1 = (3*k**2+(-6*a1+2*b1+2*b2-4)*k + 3*a1**2 - \ + (b1-b2)**2 - 2*a1*(b1+b2-2) + ctx.mpq_1_4) + uu2 = (k-a1+b1-b2-ctx.mpq_1_2)*(k-a1-b1+b2-ctx.mpq_1_2)*\ + (k-a1+b1+b2-ctx.mpq_5_2) + c[k] = ctx.one/(2*k)*(uu1*c[k-1]-uu2*c[k-2]) + w = c[k] * (-z)**(-0.5*k) + t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w + t2 = ctx.j**k * ctx.mpf(2)**(-k) * w + if abs(t1) < 0.1*ctx.eps: + #print "Convergence :)" + break + # Quit if the series doesn't converge quickly enough + if k > 5 and abs(tprev) / abs(t1) < 1.5: + #print "No convergence :(" + raise ctx.NoConvergence + s1 += t1 + s2 += t2 + tprev = t1 + k += 1 + S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \ + ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2 + T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2], [a1],\ + [], [], 0 + T2 = [-z], [-a1], [b1,b2],[b1-a1,b2-a1], \ + [a1,a1-b1+1,a1-b2+1], [], 1/z + return T1, T2 + v = ctx.hypercomb(h, [a1,b1,b2], force_series=True, maxterms=4*ctx.prec) + if sum(ctx._is_real_type(u) for u in [a1,b1,b2,z]) == 4: + v = ctx.re(v) + return v + except ctx.NoConvergence: + pass + finally: + ctx.prec = orig + + #print "not using asymp" + return ctx.hypsum(1, 2, (a1type, b1type, b2type), [a1, b1, b2], z, **kwargs) + + + +@defun +def _hyp2f3(ctx, a_s, b_s, z, **kwargs): + (a1, a1type), (a2, a2type) = a_s + (b1, b1type), (b2, b2type), (b3, b3type) = b_s + + absz = abs(z) + magz = ctx.mag(z) + + # Asymptotic expansion is ~ exp(sqrt(z)) + asymp_extraprec = z and magz//2 + orig = ctx.prec + + # Asymptotic series is in terms of 4F1 + # The square root below empirically provides a plausible criterion + # for the leading series to converge + can_use_asymptotic = (not kwargs.get('force_series')) and \ + (ctx.mag(absz) > 19) and (ctx.sqrt(absz) > 1.5*orig) + + if can_use_asymptotic: + #print "using asymp" + try: + try: + ctx.prec += asymp_extraprec + # http://functions.wolfram.com/HypergeometricFunctions/ + # Hypergeometric2F3/06/02/03/01/0002/ + def h(a1,a2,b1,b2,b3): + X = ctx.mpq_1_2*(a1+a2-b1-b2-b3+ctx.mpq_1_2) + A2 = a1+a2 + B3 = b1+b2+b3 + A = a1*a2 + B = b1*b2+b3*b2+b1*b3 + R = b1*b2*b3 + c = {} + c[0] = ctx.one + c[1] = 2*(B - A + ctx.mpq_1_4*(3*A2+B3-2)*(A2-B3) - ctx.mpq_3_16) + c[2] = ctx.mpq_1_2*c[1]**2 + ctx.mpq_1_16*(-16*(2*A2-3)*(B-A) + 32*R +\ + 4*(-8*A2**2 + 11*A2 + 8*A + B3 - 2)*(A2-B3)-3) + s1 = 0 + s2 = 0 + k = 0 + tprev = 0 + while 1: + if k not in c: + uu1 = (k-2*X-3)*(k-2*X-2*b1-1)*(k-2*X-2*b2-1)*\ + (k-2*X-2*b3-1) + uu2 = (4*(k-1)**3 - 6*(4*X+B3)*(k-1)**2 + \ + 2*(24*X**2+12*B3*X+4*B+B3-1)*(k-1) - 32*X**3 - \ + 24*B3*X**2 - 4*B - 8*R - 4*(4*B+B3-1)*X + 2*B3-1) + uu3 = (5*(k-1)**2+2*(-10*X+A2-3*B3+3)*(k-1)+2*c[1]) + c[k] = ctx.one/(2*k)*(uu1*c[k-3]-uu2*c[k-2]+uu3*c[k-1]) + w = c[k] * ctx.power(-z, -0.5*k) + t1 = (-ctx.j)**k * ctx.mpf(2)**(-k) * w + t2 = ctx.j**k * ctx.mpf(2)**(-k) * w + if abs(t1) < 0.1*ctx.eps: + break + # Quit if the series doesn't converge quickly enough + if k > 5 and abs(tprev) / abs(t1) < 1.5: + raise ctx.NoConvergence + s1 += t1 + s2 += t2 + tprev = t1 + k += 1 + S = ctx.expj(ctx.pi*X+2*ctx.sqrt(-z))*s1 + \ + ctx.expj(-(ctx.pi*X+2*ctx.sqrt(-z)))*s2 + T1 = [0.5*S, ctx.pi, -z], [1, -0.5, X], [b1, b2, b3], [a1, a2],\ + [], [], 0 + T2 = [-z], [-a1], [b1,b2,b3,a2-a1],[a2,b1-a1,b2-a1,b3-a1], \ + [a1,a1-b1+1,a1-b2+1,a1-b3+1], [a1-a2+1], 1/z + T3 = [-z], [-a2], [b1,b2,b3,a1-a2],[a1,b1-a2,b2-a2,b3-a2], \ + [a2,a2-b1+1,a2-b2+1,a2-b3+1],[-a1+a2+1], 1/z + return T1, T2, T3 + v = ctx.hypercomb(h, [a1,a2,b1,b2,b3], force_series=True, maxterms=4*ctx.prec) + if sum(ctx._is_real_type(u) for u in [a1,a2,b1,b2,b3,z]) == 6: + v = ctx.re(v) + return v + except ctx.NoConvergence: + pass + finally: + ctx.prec = orig + + return ctx.hypsum(2, 3, (a1type, a2type, b1type, b2type, b3type), [a1, a2, b1, b2, b3], z, **kwargs) + +@defun +def _hyp2f0(ctx, a_s, b_s, z, **kwargs): + (a, atype), (b, btype) = a_s + # We want to try aggressively to use the asymptotic expansion, + # and fall back only when absolutely necessary + try: + kwargsb = kwargs.copy() + kwargsb['maxterms'] = kwargsb.get('maxterms', ctx.prec) + return ctx.hypsum(2, 0, (atype,btype), [a,b], z, **kwargsb) + except ctx.NoConvergence: + if kwargs.get('force_series'): + raise + pass + def h(a, b): + w = ctx.sinpi(b) + rz = -1/z + T1 = ([ctx.pi,w,rz],[1,-1,a],[],[a-b+1,b],[a],[b],rz) + T2 = ([-ctx.pi,w,rz],[1,-1,1+a-b],[],[a,2-b],[a-b+1],[2-b],rz) + return T1, T2 + return ctx.hypercomb(h, [a, 1+a-b], **kwargs) + +@defun +def hyperu(ctx, a, b, z, **kwargs): + a, atype = ctx._convert_param(a) + b, btype = ctx._convert_param(b) + z = ctx.convert(z) + if not z: + if ctx.re(b) <= 1: + return ctx.gammaprod([1-b],[a-b+1]) + else: + return ctx.inf + z + bb = 1+a-b + bb, bbtype = ctx._convert_param(bb) + try: + orig = ctx.prec + try: + ctx.prec += 10 + v = ctx.hypsum(2, 0, (atype, bbtype), [a, bb], -1/z, maxterms=ctx.prec) + return v / z**a + finally: + ctx.prec = orig + except ctx.NoConvergence: + pass + def h(a,b): + w = ctx.sinpi(b) + T1 = ([ctx.pi,w],[1,-1],[],[a-b+1,b],[a],[b],z) + T2 = ([-ctx.pi,w,z],[1,-1,1-b],[],[a,2-b],[a-b+1],[2-b],z) + return T1, T2 + return ctx.hypercomb(h, [a,b], **kwargs) + +@defun_wrapped +def _erf_complex(ctx, z): + z2 = ctx.square_exp_arg(z, -1) + #z2 = -z**2 + v = (2/ctx.sqrt(ctx.pi))*z * ctx.hyp1f1((1,2),(3,2), z2) + if not ctx._re(z): + v = ctx._im(v)*ctx.j + return v + +@defun_wrapped +def _erfc_complex(ctx, z): + if ctx.re(z) > 2: + z2 = ctx.square_exp_arg(z) + nz2 = ctx.fneg(z2, exact=True) + v = ctx.exp(nz2)/ctx.sqrt(ctx.pi) * ctx.hyperu((1,2),(1,2), z2) + else: + v = 1 - ctx._erf_complex(z) + if not ctx._re(z): + v = 1+ctx._im(v)*ctx.j + return v + +@defun +def erf(ctx, z): + z = ctx.convert(z) + if ctx._is_real_type(z): + try: + return ctx._erf(z) + except NotImplementedError: + pass + if ctx._is_complex_type(z) and not z.imag: + try: + return type(z)(ctx._erf(z.real)) + except NotImplementedError: + pass + return ctx._erf_complex(z) + +@defun +def erfc(ctx, z): + z = ctx.convert(z) + if ctx._is_real_type(z): + try: + return ctx._erfc(z) + except NotImplementedError: + pass + if ctx._is_complex_type(z) and not z.imag: + try: + return type(z)(ctx._erfc(z.real)) + except NotImplementedError: + pass + return ctx._erfc_complex(z) + +@defun +def square_exp_arg(ctx, z, mult=1, reciprocal=False): + prec = ctx.prec*4+20 + if reciprocal: + z2 = ctx.fmul(z, z, prec=prec) + z2 = ctx.fdiv(ctx.one, z2, prec=prec) + else: + z2 = ctx.fmul(z, z, prec=prec) + if mult != 1: + z2 = ctx.fmul(z2, mult, exact=True) + return z2 + +@defun_wrapped +def erfi(ctx, z): + if not z: + return z + z2 = ctx.square_exp_arg(z) + v = (2/ctx.sqrt(ctx.pi)*z) * ctx.hyp1f1((1,2), (3,2), z2) + if not ctx._re(z): + v = ctx._im(v)*ctx.j + return v + +@defun_wrapped +def erfinv(ctx, x): + xre = ctx._re(x) + if (xre != x) or (xre < -1) or (xre > 1): + return ctx.bad_domain("erfinv(x) is defined only for -1 <= x <= 1") + x = xre + #if ctx.isnan(x): return x + if not x: return x + if x == 1: return ctx.inf + if x == -1: return ctx.ninf + if abs(x) < 0.9: + a = 0.53728*x**3 + 0.813198*x + else: + # An asymptotic formula + u = ctx.ln(2/ctx.pi/(abs(x)-1)**2) + a = ctx.sign(x) * ctx.sqrt(u - ctx.ln(u))/ctx.sqrt(2) + ctx.prec += 10 + return ctx.findroot(lambda t: ctx.erf(t)-x, a) + +@defun_wrapped +def npdf(ctx, x, mu=0, sigma=1): + sigma = ctx.convert(sigma) + return ctx.exp(-(x-mu)**2/(2*sigma**2)) / (sigma*ctx.sqrt(2*ctx.pi)) + +@defun_wrapped +def ncdf(ctx, x, mu=0, sigma=1): + a = (x-mu)/(sigma*ctx.sqrt(2)) + if a < 0: + return ctx.erfc(-a)/2 + else: + return (1+ctx.erf(a))/2 + +@defun_wrapped +def betainc(ctx, a, b, x1=0, x2=1, regularized=False): + if x1 == x2: + v = 0 + elif not x1: + if x1 == 0 and x2 == 1: + v = ctx.beta(a, b) + else: + v = x2**a * ctx.hyp2f1(a, 1-b, a+1, x2) / a + else: + m, d = ctx.nint_distance(a) + if m <= 0: + if d < -ctx.prec: + h = +ctx.eps + ctx.prec *= 2 + a += h + elif d < -4: + ctx.prec -= d + s1 = x2**a * ctx.hyp2f1(a,1-b,a+1,x2) + s2 = x1**a * ctx.hyp2f1(a,1-b,a+1,x1) + v = (s1 - s2) / a + if regularized: + v /= ctx.beta(a,b) + return v + +@defun +def gammainc(ctx, z, a=0, b=None, regularized=False): + regularized = bool(regularized) + z = ctx.convert(z) + if a is None: + a = ctx.zero + lower_modified = False + else: + a = ctx.convert(a) + lower_modified = a != ctx.zero + if b is None: + b = ctx.inf + upper_modified = False + else: + b = ctx.convert(b) + upper_modified = b != ctx.inf + # Complete gamma function + if not (upper_modified or lower_modified): + if regularized: + if ctx.re(z) < 0: + return ctx.inf + elif ctx.re(z) > 0: + return ctx.one + else: + return ctx.nan + return ctx.gamma(z) + if a == b: + return ctx.zero + # Standardize + if ctx.re(a) > ctx.re(b): + return -ctx.gammainc(z, b, a, regularized) + # Generalized gamma + if upper_modified and lower_modified: + return +ctx._gamma3(z, a, b, regularized) + # Upper gamma + elif lower_modified: + return ctx._upper_gamma(z, a, regularized) + # Lower gamma + elif upper_modified: + return ctx._lower_gamma(z, b, regularized) + +@defun +def _lower_gamma(ctx, z, b, regularized=False): + # Pole + if ctx.isnpint(z): + return type(z)(ctx.inf) + G = [z] * regularized + negb = ctx.fneg(b, exact=True) + def h(z): + T1 = [ctx.exp(negb), b, z], [1, z, -1], [], G, [1], [1+z], b + return (T1,) + return ctx.hypercomb(h, [z]) + +@defun +def _upper_gamma(ctx, z, a, regularized=False): + # Fast integer case, when available + if ctx.isint(z): + try: + if regularized: + # Gamma pole + if ctx.isnpint(z): + return type(z)(ctx.zero) + orig = ctx.prec + try: + ctx.prec += 10 + return ctx._gamma_upper_int(z, a) / ctx.gamma(z) + finally: + ctx.prec = orig + else: + return ctx._gamma_upper_int(z, a) + except NotImplementedError: + pass + nega = ctx.fneg(a, exact=True) + G = [z] * regularized + # Use 2F0 series when possible; fall back to lower gamma representation + try: + def h(z): + r = z-1 + return [([ctx.exp(nega), a], [1, r], [], G, [1, -r], [], 1/nega)] + return ctx.hypercomb(h, [z], force_series=True) + except ctx.NoConvergence: + def h(z): + T1 = [], [1, z-1], [z], G, [], [], 0 + T2 = [-ctx.exp(nega), a, z], [1, z, -1], [], G, [1], [1+z], a + return T1, T2 + return ctx.hypercomb(h, [z]) + +@defun +def _gamma3(ctx, z, a, b, regularized=False): + pole = ctx.isnpint(z) + if regularized and pole: + return ctx.zero + try: + ctx.prec += 15 + # We don't know in advance whether it's better to write as a difference + # of lower or upper gamma functions, so try both + T1 = ctx.gammainc(z, a, regularized=regularized) + T2 = ctx.gammainc(z, b, regularized=regularized) + R = T1 - T2 + if ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10: + return R + if not pole: + T1 = ctx.gammainc(z, 0, b, regularized=regularized) + T2 = ctx.gammainc(z, 0, a, regularized=regularized) + R = T1 - T2 + # May be ok, but should probably at least print a warning + # about possible cancellation + if 1: #ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10: + return R + finally: + ctx.prec -= 15 + raise NotImplementedError + +@defun_wrapped +def expint(ctx, n, z): + if ctx.isint(n) and ctx._is_real_type(z): + try: + return ctx._expint_int(n, z) + except NotImplementedError: + pass + if ctx.isnan(n) or ctx.isnan(z): + return z*n + if z == ctx.inf: + return 1/z + if z == 0: + # integral from 1 to infinity of t^n + if ctx.re(n) <= 1: + # TODO: reasonable sign of infinity + return type(z)(ctx.inf) + else: + return ctx.one/(n-1) + if n == 0: + return ctx.exp(-z)/z + if n == -1: + return ctx.exp(-z)*(z+1)/z**2 + return z**(n-1) * ctx.gammainc(1-n, z) + +@defun_wrapped +def li(ctx, z, offset=False): + if offset: + if z == 2: + return ctx.zero + return ctx.ei(ctx.ln(z)) - ctx.ei(ctx.ln2) + if not z: + return z + if z == 1: + return ctx.ninf + return ctx.ei(ctx.ln(z)) + +@defun +def ei(ctx, z): + try: + return ctx._ei(z) + except NotImplementedError: + return ctx._ei_generic(z) + +@defun_wrapped +def _ei_generic(ctx, z): + # Note: the following is currently untested because mp and fp + # both use special-case ei code + if z == ctx.inf: + return z + if z == ctx.ninf: + return ctx.zero + if ctx.mag(z) > 1: + try: + r = ctx.one/z + v = ctx.exp(z)*ctx.hyper([1,1],[],r, + maxterms=ctx.prec, force_series=True)/z + im = ctx._im(z) + if im > 0: + v += ctx.pi*ctx.j + if im < 0: + v -= ctx.pi*ctx.j + return v + except ctx.NoConvergence: + pass + v = z*ctx.hyp2f2(1,1,2,2,z) + ctx.euler + if ctx._im(z): + v += 0.5*(ctx.log(z) - ctx.log(ctx.one/z)) + else: + v += ctx.log(abs(z)) + return v + +@defun +def e1(ctx, z): + try: + return ctx._e1(z) + except NotImplementedError: + return ctx.expint(1, z) + +@defun +def ci(ctx, z): + try: + return ctx._ci(z) + except NotImplementedError: + return ctx._ci_generic(z) + +@defun_wrapped +def _ci_generic(ctx, z): + if ctx.isinf(z): + if z == ctx.inf: return ctx.zero + if z == ctx.ninf: return ctx.pi*1j + jz = ctx.fmul(ctx.j,z,exact=True) + njz = ctx.fneg(jz,exact=True) + v = 0.5*(ctx.ei(jz) + ctx.ei(njz)) + zreal = ctx._re(z) + zimag = ctx._im(z) + if zreal == 0: + if zimag > 0: v += ctx.pi*0.5j + if zimag < 0: v -= ctx.pi*0.5j + if zreal < 0: + if zimag >= 0: v += ctx.pi*1j + if zimag < 0: v -= ctx.pi*1j + if ctx._is_real_type(z) and zreal > 0: + v = ctx._re(v) + return v + +@defun +def si(ctx, z): + try: + return ctx._si(z) + except NotImplementedError: + return ctx._si_generic(z) + +@defun_wrapped +def _si_generic(ctx, z): + if ctx.isinf(z): + if z == ctx.inf: return 0.5*ctx.pi + if z == ctx.ninf: return -0.5*ctx.pi + # Suffers from cancellation near 0 + if ctx.mag(z) >= -1: + jz = ctx.fmul(ctx.j,z,exact=True) + njz = ctx.fneg(jz,exact=True) + v = (-0.5j)*(ctx.ei(jz) - ctx.ei(njz)) + zreal = ctx._re(z) + if zreal > 0: + v -= 0.5*ctx.pi + if zreal < 0: + v += 0.5*ctx.pi + if ctx._is_real_type(z): + v = ctx._re(v) + return v + else: + return z*ctx.hyp1f2((1,2),(3,2),(3,2),-0.25*z*z) + +@defun_wrapped +def chi(ctx, z): + nz = ctx.fneg(z, exact=True) + v = 0.5*(ctx.ei(z) + ctx.ei(nz)) + zreal = ctx._re(z) + zimag = ctx._im(z) + if zimag > 0: + v += ctx.pi*0.5j + elif zimag < 0: + v -= ctx.pi*0.5j + elif zreal < 0: + v += ctx.pi*1j + return v + +@defun_wrapped +def shi(ctx, z): + # Suffers from cancellation near 0 + if ctx.mag(z) >= -1: + nz = ctx.fneg(z, exact=True) + v = 0.5*(ctx.ei(z) - ctx.ei(nz)) + zimag = ctx._im(z) + if zimag > 0: v -= 0.5j*ctx.pi + if zimag < 0: v += 0.5j*ctx.pi + return v + else: + return z * ctx.hyp1f2((1,2),(3,2),(3,2),0.25*z*z) + +@defun_wrapped +def fresnels(ctx, z): + if z == ctx.inf: + return ctx.mpf(0.5) + if z == ctx.ninf: + return ctx.mpf(-0.5) + return ctx.pi*z**3/6*ctx.hyp1f2((3,4),(3,2),(7,4),-ctx.pi**2*z**4/16) + +@defun_wrapped +def fresnelc(ctx, z): + if z == ctx.inf: + return ctx.mpf(0.5) + if z == ctx.ninf: + return ctx.mpf(-0.5) + return z*ctx.hyp1f2((1,4),(1,2),(5,4),-ctx.pi**2*z**4/16) + +@defun_wrapped +def airyai(ctx, z): + if z == ctx.inf or z == ctx.ninf: + return ctx.zero + if z: + # Account for exponential scaling + ctx.prec += max(0, int(1.5*ctx.mag(z))) + if ctx._re(z) > 4: + # We could still use 1F1, but it results in huge cancellation; + # the following expansion is better + w = z**1.5 + r = -ctx.mpf(3)/(4*w) + v = ctx.exp(-2*w/3)/(2*ctx.sqrt(ctx.pi)*ctx.nthroot(z,4)) + v *= ctx.hyp2f0((1,6),(5,6),r) + return v + elif ctx._re(z) > 1: + # If not using asymptotic series: + # cancellation: both terms are ~ 2^(z^1.5), + # result is ~ 2^(-z^1.5), so need ~2*z^1.5 extra bits + ctx.prec += 2*int(ctx._re(z)**1.5) + z3 = z**3 / 9 + a = ctx.hyp0f1((2,3), z3) / (ctx.cbrt(9) * ctx.gamma(ctx.mpf(2)/3)) + b = z * ctx.hyp0f1((4,3), z3) / (ctx.cbrt(3) * ctx.gamma(ctx.mpf(1)/3)) + return a - b + +@defun_wrapped +def airybi(ctx, z): + if z == ctx.inf: + return z + if z == ctx.ninf: + return 1/z + if z: + # Account for exponential scaling + ctx.prec += max(0, int(1.5*ctx.mag(z))) + z3 = z**3 / 9 + rt = ctx.nthroot(3, 6) + a = ctx.hyp0f1((2,3), z3) / (rt * ctx.gamma(ctx.mpf(2)/3)) + b = z * rt * ctx.hyp0f1((4,3), z3) / ctx.gamma(ctx.mpf(1)/3) + return a + b + +@defun_wrapped +def hermite(ctx, n, z, **kwargs): + if not z: + try: + return 2**n * ctx.sqrt(ctx.pi) / ctx.gamma(0.5*(1-n)) + except ValueError: + return 0.0*(n+z) + if ctx.re(z) > 0 or (ctx.re(z) == 0 and ctx.im(z) > 0) or ctx.isnpint(-n): + prec = ctx.prec + ctx.prec = ctx.prec*4+20 + z2 = -z**(-2) + ctx.prec = prec + return (2*z)**n * ctx.hyp2f0(-0.5*n, -0.5*(n-1), z2, **kwargs) + else: + prec = ctx.prec + ctx.prec = ctx.prec*4+20 + z2 = z**2 + ctx.prec = prec + return ctx.hermite(n,-z) + 2**(n+2)*ctx.sqrt(ctx.pi) * (-z) / \ + ctx.gamma(-0.5*n) * ctx.hyp1f1((1-n)*0.5, 1.5, z2, **kwargs) + +@defun_wrapped +def gegenbauer(ctx, n, a, z, **kwargs): + # Special cases: a+0.5, a*2 poles + if ctx.isnpint(a): + return 0*(z+n) + if ctx.isnpint(a+0.5): + # TODO: something else is required here + # E.g.: gegenbauer(-2, -0.5, 3) == -12 + if ctx.isnpint(n+1): + raise NotImplementedError("Gegenbauer function with two limits") + def h(a): + a2 = 2*a + T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) + return [T] + return ctx.hypercomb(h, [a], **kwargs) + def h(n): + a2 = 2*a + T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z) + return [T] + return ctx.hypercomb(h, [n], **kwargs) + +@defun_wrapped +def jacobi(ctx, n, a, b, x, **kwargs): + if not ctx.isnpint(a): + def h(n): + return (([], [], [a+n+1], [n+1, a+1], [-n, a+b+n+1], [a+1], (1-x)*0.5),) + return ctx.hypercomb(h, [n], **kwargs) + if not ctx.isint(b): + def h(n, a): + return (([], [], [-b], [n+1, -b-n], [-n, a+b+n+1], [b+1], (x+1)*0.5),) + return ctx.hypercomb(h, [n, a], **kwargs) + # XXX: determine appropriate limit + return ctx.binomial(n+a,n) * ctx.hyp2f1(-n,1+n+a+b,a+1,(1-x)/2, **kwargs) + +@defun_wrapped +def laguerre(ctx, n, a, z, **kwargs): + # XXX: limits, poles + #if ctx.isnpint(n): + # return 0*(a+z) + def h(a): + return (([], [], [a+n+1], [a+1, n+1], [-n], [a+1], z),) + return ctx.hypercomb(h, [a], **kwargs) + +@defun_wrapped +def legendre(ctx, n, x, **kwargs): + if ctx.isint(n): + n = int(n) + # Accuracy near zeros + if (n + (n < 0)) & 1: + if not x: + return x + mag = ctx.mag(x) + if mag < -2*ctx.prec-10: + return x + if mag < -5: + ctx.prec += -mag + return ctx.hyp2f1(-n,n+1,1,(1-x)/2, **kwargs) + +@defun +def legenp(ctx, n, m, z, type=2, **kwargs): + # Legendre function, 1st kind + n = ctx.convert(n) + m = ctx.convert(m) + # Faster + if not m: + return ctx.legendre(n, z, **kwargs) + # TODO: correct evaluation at singularities + if type == 2: + def h(n,m): + g = m*0.5 + T = [1+z, 1-z], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) + return (T,) + return ctx.hypercomb(h, [n,m], **kwargs) + if type == 3: + def h(n,m): + g = m*0.5 + T = [z+1, z-1], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z) + return (T,) + return ctx.hypercomb(h, [n,m], **kwargs) + raise ValueError("requires type=2 or type=3") + +@defun +def legenq(ctx, n, m, z, type=2, **kwargs): + # Legendre function, 2nd kind + n = ctx.convert(n) + m = ctx.convert(m) + z = ctx.convert(z) + if z in (1, -1): + #if ctx.isint(m): + # return ctx.nan + #return ctx.inf # unsigned + return ctx.nan + if type == 2: + def h(n, m): + s = 2 * ctx.sinpi(m) / ctx.pi + c = ctx.cospi(m) + a = 1+z + b = 1-z + u = m/2 + w = (1-z)/2 + T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ + [-n, n+1], [1-m], w + T2 = [-s, a, b], [-1, -u, u], [n+m+1], [n-m+1, m+1], \ + [-n, n+1], [m+1], w + return T1, T2 + return ctx.hypercomb(h, [n, m], **kwargs) + if type == 3: + # The following is faster when there only is a single series + # Note: not valid for -1 < z < 0 (?) + if abs(z) > 1: + def h(n, m): + T1 = [ctx.expjpi(m), 2, ctx.pi, z, z-1, z+1], \ + [1, -n-1, 0.5, -n-m-1, 0.5*m, 0.5*m], \ + [n+m+1], [n+1.5], \ + [0.5*(2+n+m), 0.5*(1+n+m)], [n+1.5], z**(-2) + return [T1] + return ctx.hypercomb(h, [n, m], **kwargs) + else: + # not valid for 1 < z < inf ? + def h(n, m): + s = 2 * ctx.sinpi(m) / ctx.pi + c = ctx.expjpi(m) + a = 1+z + b = z-1 + u = m/2 + w = (1-z)/2 + T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \ + [-n, n+1], [1-m], w + T2 = [-s, c, a, b], [-1, 1, -u, u], [n+m+1], [n-m+1, m+1], \ + [-n, n+1], [m+1], w + return T1, T2 + return ctx.hypercomb(h, [n, m], **kwargs) + raise ValueError("requires type=2 or type=3") + +@defun_wrapped +def chebyt(ctx, n, x, **kwargs): + return ctx.hyp2f1(-n,n,(1,2),(1-x)/2, **kwargs) + +@defun_wrapped +def chebyu(ctx, n, x, **kwargs): + return (n+1) * ctx.hyp2f1(-n, n+2, (3,2), (1-x)/2, **kwargs) + +@defun +def j0(ctx, x): + """Computes the Bessel function `J_0(x)`. See :func:`besselj`.""" + return ctx.besselj(0, x) + +@defun +def j1(ctx, x): + """Computes the Bessel function `J_1(x)`. See :func:`besselj`.""" + return ctx.besselj(1, x) + +@defun +def besselj(ctx, n, z, derivative=0, **kwargs): + if type(n) is int: + n_isint = True + else: + n = ctx.convert(n) + n_isint = ctx.isint(n) + if n_isint: + n = int(n) + if n_isint and n < 0: + return (-1)**n * ctx.besselj(-n, z, derivative, **kwargs) + z = ctx.convert(z) + M = ctx.mag(z) + if derivative: + d = ctx.convert(derivative) + # TODO: the integer special-casing shouldn't be necessary. + # However, the hypergeometric series gets inaccurate for large d + # because of inaccurate pole cancellation at a pole far from + # zero (needs to be fixed in hypercomb or hypsum) + if ctx.isint(d) and d >= 0: + d = int(d) + orig = ctx.prec + try: + ctx.prec += 15 + v = ctx.fsum((-1)**k * ctx.binomial(d,k) * ctx.besselj(2*k+n-d,z) + for k in range(d+1)) + finally: + ctx.prec = orig + v *= ctx.mpf(2)**(-d) + else: + def h(n,d): + r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), -0.25, exact=True) + B = [0.5*(n-d+1), 0.5*(n-d+2)] + T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[],B,[(n+1)*0.5,(n+2)*0.5],B+[n+1],r)] + return T + v = ctx.hypercomb(h, [n,d], **kwargs) + else: + # Fast case: J_n(x), n int, appropriate magnitude for fixed-point calculation + if (not derivative) and n_isint and abs(M) < 10 and abs(n) < 20: + try: + return ctx._besselj(n, z) + except NotImplementedError: + pass + if not z: + if not n: + v = ctx.one + n+z + elif ctx.re(n) > 0: + v = n*z + else: + v = ctx.inf + z + n + else: + #v = 0 + orig = ctx.prec + try: + # XXX: workaround for accuracy in low level hypergeometric series + # when alternating, large arguments + ctx.prec += min(3*abs(M), ctx.prec) + w = ctx.fmul(z, 0.5, exact=True) + def h(n): + r = ctx.fneg(ctx.fmul(w, w, prec=max(0,ctx.prec+M)), exact=True) + return [([w], [n], [], [n+1], [], [n+1], r)] + v = ctx.hypercomb(h, [n], **kwargs) + finally: + ctx.prec = orig + v = +v + return v + +@defun +def besseli(ctx, n, z, derivative=0, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + if not z: + if derivative: + raise ValueError + if not n: + # I(0,0) = 1 + return 1+n+z + if ctx.isint(n): + return 0*(n+z) + r = ctx.re(n) + if r == 0: + return ctx.nan*(n+z) + elif r > 0: + return 0*(n+z) + else: + return ctx.inf+(n+z) + M = ctx.mag(z) + if derivative: + d = ctx.convert(derivative) + def h(n,d): + r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), 0.25, exact=True) + B = [0.5*(n-d+1), 0.5*(n-d+2), n+1] + T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[n+1],B,[(n+1)*0.5,(n+2)*0.5],B,r)] + return T + v = ctx.hypercomb(h, [n,d], **kwargs) + else: + def h(n): + w = ctx.fmul(z, 0.5, exact=True) + r = ctx.fmul(w, w, prec=max(0,ctx.prec+M)) + return [([w], [n], [], [n+1], [], [n+1], r)] + v = ctx.hypercomb(h, [n], **kwargs) + return v + +@defun_wrapped +def bessely(ctx, n, z, derivative=0, **kwargs): + if not z: + if derivative: + # Not implemented + raise ValueError + if not n: + # ~ log(z/2) + return -ctx.inf + (n+z) + if ctx.im(n): + return nan * (n+z) + r = ctx.re(n) + q = n+0.5 + if ctx.isint(q): + if n > 0: + return -ctx.inf + (n+z) + else: + return 0 * (n+z) + if r < 0 and int(ctx.floor(q)) % 2: + return ctx.inf + (n+z) + else: + return ctx.ninf + (n+z) + # XXX: use hypercomb + ctx.prec += 10 + m, d = ctx.nint_distance(n) + if d < -ctx.prec: + h = +ctx.eps + ctx.prec *= 2 + n += h + elif d < 0: + ctx.prec -= d + # TODO: avoid cancellation for imaginary arguments + return (ctx.besselj(n,z,derivative,**kwargs)*ctx.cospi(n) - \ + ctx.besselj(-n,z,derivative,**kwargs))/ctx.sinpi(n) + +@defun_wrapped +def besselk(ctx, n, z, **kwargs): + if not z: + return ctx.inf + M = ctx.mag(z) + if M < 1: + # Represent as limit definition + def h(n): + r = (z/2)**2 + T1 = [z, 2], [-n, n-1], [n], [], [], [1-n], r + T2 = [z, 2], [n, -n-1], [-n], [], [], [1+n], r + return T1, T2 + # We could use the limit definition always, but it leads + # to very bad cancellation (of exponentially large terms) + # for large real z + # Instead represent in terms of 2F0 + else: + ctx.prec += M + def h(n): + return [([ctx.pi/2, z, ctx.exp(-z)], [0.5,-0.5,1], [], [], \ + [n+0.5, 0.5-n], [], -1/(2*z))] + return ctx.hypercomb(h, [n], **kwargs) + +@defun_wrapped +def hankel1(ctx,n,x,**kwargs): + return ctx.besselj(n,x,**kwargs) + ctx.j*ctx.bessely(n,x,**kwargs) + +@defun_wrapped +def hankel2(ctx,n,x,**kwargs): + return ctx.besselj(n,x,**kwargs) - ctx.j*ctx.bessely(n,x,**kwargs) + +@defun_wrapped +def whitm(ctx,k,m,z,**kwargs): + if z == 0: + # M(k,m,z) = 0^(1/2+m) + if ctx.re(m) > -0.5: + return z + elif ctx.re(m) < -0.5: + return ctx.inf + z + else: + return ctx.nan * z + x = ctx.fmul(-0.5, z, exact=True) + y = 0.5+m + return ctx.exp(x) * z**y * ctx.hyp1f1(y-k, 1+2*m, z, **kwargs) + +@defun_wrapped +def whitw(ctx,k,m,z,**kwargs): + if z == 0: + g = abs(ctx.re(m)) + if g < 0.5: + return z + elif g > 0.5: + return ctx.inf + z + else: + return ctx.nan * z + x = ctx.fmul(-0.5, z, exact=True) + y = 0.5+m + return ctx.exp(x) * z**y * ctx.hyperu(y-k, 1+2*m, z, **kwargs) + +@defun +def struveh(ctx,n,z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/StruveH/26/01/02/ + def h(n): + return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], -(z/2)**2)] + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def struvel(ctx,n,z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/StruveL/26/01/02/ + def h(n): + return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], (z/2)**2)] + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def ber(ctx, n, z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBer2/26/01/02/0001/ + def h(n): + r = -(z/4)**4 + T1 = [ctx.cospi(0.75*n), z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r + T2 = [-ctx.sinpi(0.75*n), z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r + return T1, T2 + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def bei(ctx, n, z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBei2/26/01/02/0001/ + def h(n): + r = -(z/4)**4 + T1 = [ctx.cospi(0.75*n), z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r + T2 = [ctx.sinpi(0.75*n), z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r + return T1, T2 + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def ker(ctx, n, z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKer2/26/01/02/0001/ + def h(n): + r = -(z/4)**4 + T1 = [2, z, 4*ctx.cospi(0.25*n)], [-n-3, n, 1], [-n], [], [], [0.5, 0.5*(1+n), 0.5*(n+2)], r + T2 = [2, z, -ctx.sinpi(0.25*n)], [-n-3, 2+n, 1], [-n-1], [], [], [1.5, 0.5*(3+n), 0.5*(n+2)], r + T3 = [2, z, 4*ctx.cospi(0.75*n)], [n-3, -n, 1], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r + T4 = [2, z, -ctx.sinpi(0.75*n)], [n-3, 2-n, 1], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r + return T1, T2, T3, T4 + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def kei(ctx, n, z, **kwargs): + n = ctx.convert(n) + z = ctx.convert(z) + # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKei2/26/01/02/0001/ + def h(n): + r = -(z/4)**4 + T1 = [-ctx.cospi(0.75*n), 2, z], [1, n-3, 2-n], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r + T2 = [-ctx.sinpi(0.75*n), 2, z], [1, n-1, -n], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r + T3 = [-ctx.sinpi(0.25*n), 2, z], [1, -n-1, n], [-n], [], [], [0.5, 0.5*(n+1), 0.5*(n+2)], r + T4 = [-ctx.cospi(0.25*n), 2, z], [1, -n-3, n+2], [-n-1], [], [], [1.5, 0.5*(n+3), 0.5*(n+2)], r + return T1, T2, T3, T4 + return ctx.hypercomb(h, [n], **kwargs) + +@defun +def meijerg(ctx, a_s, b_s, z, r=1, series=None, **kwargs): + an, ap = a_s + bm, bq = b_s + n = len(an) + p = n + len(ap) + m = len(bm) + q = m + len(bq) + a = an+ap + b = bm+bq + a = map(ctx.convert, a) + b = map(ctx.convert, b) + z = ctx.convert(z) + if series is None: + if p < q: series = 1 + if p > q: series = 2 + if p == q: + if m+n == p and abs(z) > 1: + series = 2 + else: + series = 1 + if kwargs.get('verbose'): + print "Meijer G m,n,p,q,series =", m,n,p,q,series + if series == 1: + def h(*args): + a = args[:p] + b = args[p:] + terms = [] + for k in range(m): + bases = [z] + expts = [b[k]/r] + gn = [b[j]-b[k] for j in range(m) if j != k] + gn += [1-a[j]+b[k] for j in range(n)] + gd = [a[j]-b[k] for j in range(n,p)] + gd += [1-b[j]+b[k] for j in range(m,q)] + hn = [1-a[j]+b[k] for j in range(p)] + hd = [1-b[j]+b[k] for j in range(q) if j != k] + hz = (-ctx.one)**(p-m-n) * z**(ctx.one/r) + terms.append((bases, expts, gn, gd, hn, hd, hz)) + return terms + else: + def h(*args): + a = args[:p] + b = args[p:] + terms = [] + for k in range(n): + bases = [z] + if r == 1: + expts = [a[k]-1] + else: + expts = [(a[k]-1)/ctx.convert(r)] + gn = [a[k]-a[j] for j in range(n) if j != k] + gn += [1-a[k]+b[j] for j in range(m)] + gd = [a[k]-b[j] for j in range(m,q)] + gd += [1-a[k]+a[j] for j in range(n,p)] + hn = [1-a[k]+b[j] for j in range(q)] + hd = [1+a[j]-a[k] for j in range(p) if j != k] + hz = (-ctx.one)**(q-m-n) / z**(ctx.one/r) + terms.append((bases, expts, gn, gd, hn, hd, hz)) + return terms + return ctx.hypercomb(h, a+b, **kwargs) + +@defun_wrapped +def appellf1(ctx,a,b1,b2,c,z1,z2,**kwargs): + # Assume z1 smaller + # We will use z1 for the outer loop + if abs(z1) > abs(z2): + z1, z2 = z2, z1 + b1, b2 = b2, b1 + def ok(x): + return abs(x) < 0.99 + # Finite cases + if ctx.isnpint(a): + pass + elif ctx.isnpint(b1): + pass + elif ctx.isnpint(b2): + z1, z2, b1, b2 = z2, z1, b2, b1 + else: + #print z1, z2 + # Note: ok if |z2| > 1, because + # 2F1 implements analytic continuation + if not ok(z1): + u1 = (z1-z2)/(z1-1) + if not ok(u1): + raise ValueError("Analytic continuation not implemented") + #print "Using analytic continuation" + return (1-z1)**(-b1)*(1-z2)**(c-a-b2)*\ + ctx.appellf1(c-a,b1,c-b1-b2,c,u1,z2,**kwargs) + #print "inner is", a, b2, c + one = ctx.one + s = 0 + t = 1 + k = 0 + while 1: + h = ctx.hyp2f1(a,b2,c,z2,zeroprec=ctx.prec,**kwargs) + term = t * h + if abs(term) < ctx.eps and abs(h) > 10*ctx.eps: + break + s += term + k += 1 + t = (t*a*b1*z1) / (c*k) + c += one + a += one + b1 += one + return s + +@defun_wrapped +def coulombc(ctx, l, eta, _cache={}): + if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec: + return +_cache[l,eta][1] + G3 = ctx.loggamma(2*l+2) + G1 = ctx.loggamma(1+l+ctx.j*eta) + G2 = ctx.loggamma(1+l-ctx.j*eta) + v = 2**l * ctx.exp((-ctx.pi*eta+G1+G2)/2 - G3) + if not (ctx.im(l) or ctx.im(eta)): + v = ctx.re(v) + _cache[l,eta] = (ctx.prec, v) + return v + +@defun_wrapped +def coulombf(ctx, l, eta, z, w=1, chop=True, **kwargs): + # Regular Coulomb wave function + # Note: w can be either 1 or -1; the other may be better in some cases + # TODO: check that chop=True chops when and only when it should + #ctx.prec += 10 + def h(l, eta): + try: + jw = ctx.j*w + jwz = ctx.fmul(jw, z, exact=True) + jwz2 = ctx.fmul(jwz, -2, exact=True) + C = ctx.coulombc(l, eta) + T1 = [C, z, ctx.exp(jwz)], [1, l+1, 1], [], [], [1+l+jw*eta], \ + [2*l+2], jwz2 + except ValueError: + T1 = [0], [-1], [], [], [], [], 0 + return (T1,) + v = ctx.hypercomb(h, [l,eta], **kwargs) + if chop and (not ctx.im(l)) and (not ctx.im(eta)) and (not ctx.im(z)) and \ + (ctx.re(z) >= 0): + v = ctx.re(v) + return v + +@defun_wrapped +def _coulomb_chi(ctx, l, eta, _cache={}): + if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec: + return _cache[l,eta][1] + def terms(): + l2 = -l-1 + jeta = ctx.j*eta + return [ctx.loggamma(1+l+jeta) * (-0.5j), + ctx.loggamma(1+l-jeta) * (0.5j), + ctx.loggamma(1+l2+jeta) * (0.5j), + ctx.loggamma(1+l2-jeta) * (-0.5j), + -(l+0.5)*ctx.pi] + v = ctx.sum_accurately(terms, 1) + _cache[l,eta] = (ctx.prec, v) + return v + +@defun_wrapped +def coulombg(ctx, l, eta, z, w=1, chop=True, **kwargs): + # Irregular Coulomb wave function + # Note: w can be either 1 or -1; the other may be better in some cases + # TODO: check that chop=True chops when and only when it should + if not ctx._im(l): + l = ctx._re(l) # XXX: for isint + def h(l, eta): + # Force perturbation for integers and half-integers + if ctx.isint(l*2): + T1 = [0], [-1], [], [], [], [], 0 + return (T1,) + l2 = -l-1 + try: + chi = ctx._coulomb_chi(l, eta) + jw = ctx.j*w + s = ctx.sin(chi); c = ctx.cos(chi) + C1 = ctx.coulombc(l,eta) + C2 = ctx.coulombc(l2,eta) + u = ctx.exp(jw*z) + x = -2*jw*z + T1 = [s, C1, z, u, c], [-1, 1, l+1, 1, 1], [], [], \ + [1+l+jw*eta], [2*l+2], x + T2 = [-s, C2, z, u], [-1, 1, l2+1, 1], [], [], \ + [1+l2+jw*eta], [2*l2+2], x + return T1, T2 + except ValueError: + T1 = [0], [-1], [], [], [], [], 0 + return (T1,) + v = ctx.hypercomb(h, [l,eta], **kwargs) + if chop and (not ctx._im(l)) and (not ctx._im(eta)) and (not ctx._im(z)) and \ + (ctx._re(z) >= 0): + v = ctx._re(v) + return v + +@defun +def spherharm(ctx, l, m, theta, phi, **kwargs): + l = ctx.convert(l) + m = ctx.convert(m) + theta = ctx.convert(theta) + phi = ctx.convert(phi) + l_isint = ctx.isint(l) + l_natural = l_isint and l >= 0 + m_isint = ctx.isint(m) + if l_isint and l < 0 and m_isint: + return ctx.spherharm(-(l+1), m, theta, phi, **kwargs) + if theta == 0 and m_isint and m < 0: + return ctx.zero * 1j + if l_natural and m_isint: + if abs(m) > l: + return ctx.zero * 1j + # http://functions.wolfram.com/Polynomials/ + # SphericalHarmonicY/26/01/02/0004/ + def h(l,m): + C = [-1, ctx.expj(m*phi), + (2*l+1)*ctx.fac(l+abs(m))/ctx.pi/ctx.fac(l-abs(m)), + ctx.sin(theta)**2, + ctx.fac(abs(m)), 2] + P = [0.5*m*(ctx.sign(m)+1), 1, 0.5, 0.5*abs(m), -1, -abs(m)-1] + return ((C, P, [], [], [abs(m)-l, l+abs(m)+1], [abs(m)+1], + ctx.sin(0.5*theta)**2),) + else: + # http://functions.wolfram.com/HypergeometricFunctions/ + # SphericalHarmonicYGeneral/26/01/02/0001/ + def h(l,m): + if ctx.isnpint(l-m+1) or ctx.isnpint(l+m+1) or ctx.isnpint(1-m): + return (([0], [-1], [], [], [], [], 0),) + C = [0.5*ctx.expj(m*phi), + (2*l+1)/ctx.pi, + ctx.gamma(l-m+1), + ctx.gamma(l+m+1), + ctx.cos(0.5*theta)**2, + ctx.sin(0.5*theta)**2] + P = [1, 0.5, 0.5, -0.5, 0.5*m, -0.5*m] + return ((C, P, [], [1-m], [-l,l+1], [1-m], ctx.sin(0.5*theta)**2),) + return ctx.hypercomb(h, [l,m], **kwargs) diff --git a/compiler/gdsMill/mpmath/functions/rszeta.py b/compiler/gdsMill/mpmath/functions/rszeta.py new file mode 100644 index 00000000..69101355 --- /dev/null +++ b/compiler/gdsMill/mpmath/functions/rszeta.py @@ -0,0 +1,1412 @@ +""" +--------------------------------------------------------------------- +.. sectionauthor:: Juan Arias de Reyna + +This module implements zeta-related functions using the Riemann-Siegel +expansion: zeta_offline(s,k=0) + +* coef(J, eps): Need in the computation of Rzeta(s,k) + +* Rzeta_simul(s, der=0) computes Rzeta^(k)(s) and Rzeta^(k)(1-s) simultaneously + for 0 <= k <= der. Used by zeta_offline and z_offline + +* Rzeta_set(s, derivatives) computes Rzeta^(k)(s) for given derivatives, used by + z_half(t,k) and zeta_half + +* z_offline(w,k): Z(w) and its derivatives of order k <= 4 +* z_half(t,k): Z(t) (Riemann Siegel function) and its derivatives of order k <= 4 +* zeta_offline(s): zeta(s) and its derivatives of order k<= 4 +* zeta_half(1/2+it,k): zeta(s) and its derivatives of order k<= 4 + +* rs_zeta(s,k=0) Computes zeta^(k)(s) Unifies zeta_half and zeta_offline +* rs_z(w,k=0) Computes Z^(k)(w) Unifies z_offline and z_half +---------------------------------------------------------------------- + +This program uses Riemann-Siegel expansion even to compute +zeta(s) on points s = sigma + i t with sigma arbitrary not +necessarily equal to 1/2. + +It is founded on a new deduction of the formula, with rigorous +and sharp bounds for the terms and rest of this expansion. + +More information on the papers: + + J. Arias de Reyna, High Precision Computation of Riemann's + Zeta Function by the Riemann-Siegel Formula I, II + + We refer to them as I, II. + + In them we shall find detailed explanation of all the + procedure. + +The program uses Riemann-Siegel expansion. +This is useful when t is big, ( say t > 10000 ). +The precision is limited, roughly it can compute zeta(sigma+it) +with an error less than exp(-c t) for some constant c depending +on sigma. The program gives an error when the Riemann-Siegel +formula can not compute to the wanted precision. + +""" + +# XXX: currently the loggamma function can generate inaccurate values +# at high precision. As a temporary workaround, multiply the precision +# in affected places by the following factor +LOGGAMMA_BROKENNESS_FACTOR = 1.5 + + +import math + +class RSCache: + def __init__(ctx): + ctx._rs_cache = [0, 10, {}, {}] + +from functions import defun + +#-------------------------------------------------------------------------------# +# # +# coef(ctx, J, eps, _cache=[0, 10, {} ] ) # +# # +#-------------------------------------------------------------------------------# + +# This function computes the coefficients c[n] defined on (I, equation (47)) +# but see also (II, section 3.14). +# +# Since these coefficients are very difficult to compute we save the values +# in a cache. So if we compute several values of the functions Rzeta(s) for +# near values of s, we do not recompute these coefficients. +# +# c[n] are the Taylor coefficients of the function: +# +# F(z):= (exp(pi*j*(z*z/2+3/8))-j* sqrt(2) cos(pi*z/2))/(2*cos(pi *z)) +# +# + +def _coef(ctx, J, eps): + r""" + Computes the coefficients `c_n` for `0\le n\le 2J` with error less than eps + + **Definition** + + The coefficients c_n are defined by + + .. math :: + + \begin{equation} + F(z)=\frac{e^{\pi i + \bigl(\frac{z^2}{2}+\frac38\bigr)}-i\sqrt{2}\cos\frac{\pi}{2}z}{2\cos\pi + z}=\sum_{n=0}^\infty c_{2n} z^{2n} + \end{equation} + + they are computed applying the relation + + .. math :: + + \begin{multline} + c_{2n}=-\frac{i}{\sqrt{2}}\Bigl(\frac{\pi}{2}\Bigr)^{2n} + \sum_{k=0}^n\frac{(-1)^k}{(2k)!} + 2^{2n-2k}\frac{(-1)^{n-k}E_{2n-2k}}{(2n-2k)!}+\\ + +e^{3\pi i/8}\sum_{j=0}^n(-1)^j\frac{ + E_{2j}}{(2j)!}\frac{i^{n-j}\pi^{n+j}}{(n-j)!2^{n-j+1}}. + \end{multline} + """ + + newJ = J+2 # compute more coefficients that are needed + neweps6 = eps/2. # compute with a slight more precision + # that are needed + + # PREPARATION FOR THE COMPUTATION OF V(N) AND W(N) + # See II Section 3.16 + # + # Computing the exponent wpvw of the error II equation (81) + wpvw = max(ctx.mag(10*(newJ+3)), 4*newJ+5-ctx.mag(neweps6)) + + # Preparation of Euler numbers (we need until the 2*RS_NEWJ) + E = ctx._eulernum(2*newJ) + + # Now we have in the cache all the needed Euler numbers. + # + # Computing the powers of pi + # + # We need to compute the powers pi**n for 1<= n <= 2*J + # with relative error less than 2**(-wpvw) + # it is easy to show that this is obtained + # taking wppi as the least d with + # 2**d>40*J and 2**d> 4.24 *newJ + 2**wpvw + # In II Section 3.9 we need also that + # wppi > wptcoef[0], and that the powers + # here computed 0<= k <= 2*newJ are more + # than those needed there that are 2*L-2. + # so we need J >= L this will be checked + # before computing tcoef[] + wppi = max(ctx.mag(40*newJ), ctx.mag(newJ)+3 +wpvw) + ctx.prec = wppi + pipower = {} + pipower[0] = ctx.one + pipower[1] = ctx.pi + for n in range(2,2*newJ+1): + pipower[n] = pipower[n-1]*ctx.pi + + # COMPUTING THE COEFFICIENTS v(n) AND w(n) + # see II equation (61) and equations (81) and (82) + ctx.prec = wpvw+2 + v={} + w={} + for n in range(0,newJ+1): + va = (-1)**n * ctx._eulernum(2*n) + va = ctx.mpf(va)/ctx.fac(2*n) + v[n]=va*pipower[2*n] + for n in range(0,2*newJ+1): + wa = ctx.one/ctx.fac(n) + wa=wa/(2**n) + w[n]=wa*pipower[n] + + # COMPUTATION OF THE CONVOLUTIONS RS_P1 AND RS_P2 + # See II Section 3.16 + ctx.prec = 15 + wpp1a = 9 - ctx.mag(neweps6) + P1 = {} + for n in range(0,newJ+1): + ctx.prec = 15 + wpp1 = max(ctx.mag(10*(n+4)),4*n+wpp1a) + ctx.prec = wpp1 + sump = 0 + for k in range(0,n+1): + sump += ((-1)**k) * v[k]*w[2*n-2*k] + P1[n]=((-1)**(n+1))*ctx.j*sump + P2={} + for n in range(0,newJ+1): + ctx.prec = 15 + wpp2 = max(ctx.mag(10*(n+4)),4*n+wpp1a) + ctx.prec = wpp2 + sump = 0 + for k in range(0,n+1): + sump += (ctx.j**(n-k)) * v[k]*w[n-k] + P2[n]=sump + # COMPUTING THE COEFFICIENTS c[2n] + # See II Section 3.14 + ctx.prec = 15 + wpc0 = 5 - ctx.mag(neweps6) + wpc = max(6,4*newJ+wpc0) + ctx.prec = wpc + mu = ctx.sqrt(ctx.mpf('2'))/2 + nu = ctx.expjpi(3./8)/2 + c={} + for n in range(0,newJ): + ctx.prec = 15 + wpc = max(6,4*n+wpc0) + ctx.prec = wpc + c[2*n] = mu*P1[n]+nu*P2[n] + for n in range(1,2*newJ,2): + c[n] = 0 + return [newJ, neweps6, c, pipower] + +def coef(ctx, J, eps): + _cache = ctx._rs_cache + if J <= _cache[0] and eps >= _cache[1]: + return _cache[2], _cache[3] + orig = ctx._mp.prec + try: + data = _coef(ctx._mp, J, eps) + finally: + ctx._mp.prec = orig + if ctx is not ctx._mp: + data[2] = dict((k,ctx.convert(v)) for (k,v) in data[2].items()) + data[3] = dict((k,ctx.convert(v)) for (k,v) in data[3].items()) + ctx._rs_cache[:] = data + return ctx._rs_cache[2], ctx._rs_cache[3] + +#-------------------------------------------------------------------------------# +# # +# Rzeta_simul(s,k=0) # +# # +#-------------------------------------------------------------------------------# +# This function return a list with the values: +# Rzeta(sigma+it), conj(Rzeta(1-sigma+it)),Rzeta'(sigma+it), conj(Rzeta'(1-sigma+it)), +# .... , Rzeta^{(k)}(sigma+it), conj(Rzeta^{(k)}(1-sigma+it)) +# +# Useful to compute the function zeta(s) and Z(w) or its derivatives. +# + +def aux_M_Fp(ctx, xA, xeps4, a, xB1, xL): + # COMPUTING M NUMBER OF DERIVATIVES Fp[m] TO COMPUTE + # See II Section 3.11 equations (47) and (48) + aux1 = 126.0657606*xA/xeps4 # 126.06.. = 316/sqrt(2*pi) + aux1 = ctx.ln(aux1) + aux2 = (2*ctx.ln(ctx.pi)+ctx.ln(xB1)+ctx.ln(a))/3 -ctx.ln(2*ctx.pi)/2 + m = 3*xL-3 + aux3= (ctx.loggamma(m+1)-ctx.loggamma(m/3.0+2))/2 -ctx.loggamma((m+1)/2.) + while((aux1 < m*aux2+ aux3)and (m>1)): + m = m - 1 + aux3 = (ctx.loggamma(m+1)-ctx.loggamma(m/3.0+2))/2 -ctx.loggamma((m+1)/2.) + xM = m + return xM + +def aux_J_needed(ctx, xA, xeps4, a, xB1, xM): + # DETERMINATION OF J THE NUMBER OF TERMS NEEDED + # IN THE TAYLOR SERIES OF F. + # See II Section 3.11 equation (49)) + # Only determine one + h1 = xeps4/(632*xA) + h2 = xB1*a * 126.31337419529260248 # = pi^2*e^2*sqrt(3) + h2 = h1 * ctx.power((h2/xM**2),(xM-1)/3) / xM + h3 = min(h1,h2) + return h3 + +def Rzeta_simul(ctx, s, der=0): + # First we take the value of ctx.prec + wpinitial = ctx.prec + + # INITIALIZATION + # Take the real and imaginary part of s + t = ctx._im(s) + xsigma = ctx._re(s) + ysigma = 1 - xsigma + + # Now compute several parameter that appear on the program + ctx.prec = 15 + a = ctx.sqrt(t/(2*ctx.pi)) + xasigma = a ** xsigma + yasigma = a ** ysigma + + # We need a simple bound A1 < asigma (see II Section 3.1 and 3.3) + xA1=ctx.power(2, ctx.mag(xasigma)-1) + yA1=ctx.power(2, ctx.mag(yasigma)-1) + + # We compute various epsilon's (see II end of Section 3.1) + eps = ctx.power(2, -wpinitial) + eps1 = eps/6. + xeps2 = eps * xA1/3. + yeps2 = eps * yA1/3. + + # COMPUTING SOME COEFFICIENTS THAT DEPENDS + # ON sigma + # constant b and c (see I Theorem 2 formula (26) ) + # coefficients A and B1 (see I Section 6.1 equation (50)) + # + # here we not need high precision + ctx.prec = 15 + if xsigma > 0: + xb = 2. + xc = math.pow(9,xsigma)/4.44288 + # 4.44288 =(math.sqrt(2)*math.pi) + xA = math.pow(9,xsigma) + xB1 = 1 + else: + xb = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) + xc = math.pow(2,-xsigma)/4.44288 + xA = math.pow(2,-xsigma) + xB1 = 1.10789 # = 2*sqrt(1-log(2)) + + if(ysigma > 0): + yb = 2. + yc = math.pow(9,ysigma)/4.44288 + # 4.44288 =(math.sqrt(2)*math.pi) + yA = math.pow(9,ysigma) + yB1 = 1 + else: + yb = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) + yc = math.pow(2,-ysigma)/4.44288 + yA = math.pow(2,-ysigma) + yB1 = 1.10789 # = 2*sqrt(1-log(2)) + + # COMPUTING L THE NUMBER OF TERMS NEEDED IN THE RIEMANN-SIEGEL + # CORRECTION + # See II Section 3.2 + ctx.prec = 15 + xL = 1 + while 3*xc*ctx.gamma(xL*0.5) * ctx.power(xb*a,-xL) >= xeps2: + xL = xL+1 + xL = max(2,xL) + yL = 1 + while 3*yc*ctx.gamma(yL*0.5) * ctx.power(yb*a,-yL) >= yeps2: + yL = yL+1 + yL = max(2,yL) + + # The number L has to satify some conditions. + # If not RS can not compute Rzeta(s) with the prescribed precision + # (see II, Section 3.2 condition (20) ) and + # (II, Section 3.3 condition (22) ). Also we have added + # an additional technical condition in Section 3.17 Proposition 17 + if ((3*xL >= 2*a*a/25.) or (3*xL+2+xsigma<0) or (abs(xsigma) > a/2.) or \ + (3*yL >= 2*a*a/25.) or (3*yL+2+ysigma<0) or (abs(ysigma) > a/2.)): + ctx.prec = wpinitial + raise NotImplementedError("Riemann-Siegel can not compute with such precision") + + # We take the maximum of the two values + L = max(xL, yL) + + # INITIALIZATION (CONTINUATION) + # + # eps3 is the constant defined on (II, Section 3.5 equation (27) ) + # each term of the RS correction must be computed with error <= eps3 + xeps3 = xeps2/(4*xL) + yeps3 = yeps2/(4*yL) + + # eps4 is defined on (II Section 3.6 equation (30) ) + # each component of the formula (II Section 3.6 equation (29) ) + # must be computed with error <= eps4 + xeps4 = xeps3/(3*xL) + yeps4 = yeps3/(3*yL) + + # COMPUTING M NUMBER OF DERIVATIVES Fp[m] TO COMPUTE + xM = aux_M_Fp(ctx, xA, xeps4, a, xB1, xL) + yM = aux_M_Fp(ctx, yA, yeps4, a, yB1, yL) + M = max(xM, yM) + + # COMPUTING NUMBER OF TERMS J NEEDED + h3 = aux_J_needed(ctx, xA, xeps4, a, xB1, xM) + h4 = aux_J_needed(ctx, yA, yeps4, a, yB1, yM) + h3 = min(h3,h4) + J = 12 + jvalue = (2*ctx.pi)**J / ctx.gamma(J+1) + while jvalue > h3: + J = J+1 + jvalue = (2*ctx.pi)*jvalue/J + + # COMPUTING eps5[m] for 1 <= m <= 21 + # See II Section 10 equation (43) + # We choose the minimum of the two possibilities + eps5={} + xforeps5 = math.pi*math.pi*xB1*a + yforeps5 = math.pi*math.pi*yB1*a + for m in range(0,22): + xaux1 = math.pow(xforeps5, m/3)/(316.*xA) + yaux1 = math.pow(yforeps5, m/3)/(316.*yA) + aux1 = min(xaux1, yaux1) + aux2 = ctx.gamma(m+1)/ctx.gamma(m/3.0+0.5) + aux2 = math.sqrt(aux2) + eps5[m] = (aux1*aux2*min(xeps4,yeps4)) + + # COMPUTING wpfp + # See II Section 3.13 equation (59) + twenty = min(3*L-3, 21)+1 + aux = 6812*J + wpfp = ctx.mag(44*J) + for m in range(0,twenty): + wpfp = max(wpfp, ctx.mag(aux*ctx.gamma(m+1)/eps5[m])) + + # COMPUTING N AND p + # See II Section + ctx.prec = wpfp + ctx.mag(t)+20 + a = ctx.sqrt(t/(2*ctx.pi)) + N = ctx.floor(a) + p = 1-2*(a-N) + + # now we get a rounded version of p + # to the precision wpfp + # this possibly is not necessary + num=ctx.floor(p*(ctx.mpf('2')**wpfp)) + difference = p * (ctx.mpf('2')**wpfp)-num + if (difference < 0.5): + num = num + else: + num = num+1 + p = ctx.convert(num * (ctx.mpf('2')**(-wpfp))) + + # COMPUTING THE COEFFICIENTS c[n] = cc[n] + # We shall use the notation cc[n], since there is + # a constant that is called c + # See II Section 3.14 + # We compute the coefficients and also save then in a + # cache. The bulk of the computation is passed to + # the function coef() + # + # eps6 is defined in II Section 3.13 equation (58) + eps6 = ctx.power(ctx.convert(2*ctx.pi), J)/(ctx.gamma(J+1)*3*J) + + # Now we compute the coefficients + cc = {} + cont = {} + cont, pipowers = coef(ctx, J, eps6) + cc=cont.copy() # we need a copy since we have + # to change his values. + Fp={} # this is the adequate locus of this + for n in range(M, 3*L-2): + Fp[n] = 0 + Fp={} + ctx.prec = wpfp + for m in range(0,M+1): + sumP = 0 + for k in range(2*J-m-1,-1,-1): + sumP = (sumP * p)+ cc[k] + Fp[m] = sumP + # preparation of the new coefficients + for k in range(0,2*J-m-1): + cc[k] = (k+1)* cc[k+1] + + # COMPUTING THE NUMBERS xd[u,n,k], yd[u,n,k] + # See II Section 3.17 + # + # First we compute the working precisions xwpd[k] + # Se II equation (92) + xwpd={} + d1 = max(6,ctx.mag(40*L*L)) + xd2 = 13+ctx.mag((1+abs(xsigma))*xA)-ctx.mag(xeps4)-1 + xconst = ctx.ln(8/(ctx.pi*ctx.pi*a*a*xB1*xB1)) /2 + for n in range(0,L): + xd3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*xconst)+xd2 + xwpd[n]=max(xd3,d1) + + # procedure of II Section 3.17 + ctx.prec = xwpd[1]+10 + xpsigma = 1-(2*xsigma) + xd = {} + xd[0,0,-2]=0; xd[0,0,-1]=0; xd[0,0,0]=1; xd[0,0,1]=0 + xd[0,-1,-2]=0; xd[0,-1,-1]=0; xd[0,-1,0]=1; xd[0,-1,1]=0 + for n in range(1,L): + ctx.prec = xwpd[n]+10 + for k in range(0,3*n//2+1): + m = 3*n-2*k + if(m!=0): + m1 = ctx.one/m + c1= m1/4 + c2=(xpsigma*m1)/2 + c3=-(m+1) + xd[0,n,k]=c3*xd[0,n-1,k-2]+c1*xd[0,n-1,k]+c2*xd[0,n-1,k-1] + else: + xd[0,n,k]=0 + for r in range(0,k): + add=xd[0,n,r]*(ctx.mpf('1.0')*ctx.fac(2*k-2*r)/ctx.fac(k-r)) + xd[0,n,k] -= ((-1)**(k-r))*add + xd[0,n,-2]=0; xd[0,n,-1]=0; xd[0,n,3*n//2+1]=0 + for mu in range(-2,der+1): + for n in range(-2,L): + for k in range(-3,max(1,3*n//2+2)): + if( (mu<0)or (n<0) or(k<0)or (k>3*n//2)): + xd[mu,n,k] = 0 + for mu in range(1,der+1): + for n in range(0,L): + ctx.prec = xwpd[n]+10 + for k in range(0,3*n//2+1): + aux=(2*mu-2)*xd[mu-2,n-2,k-3]+2*(xsigma+n-2)*xd[mu-1,n-2,k-3] + xd[mu,n,k] = aux - xd[mu-1,n-1,k-1] + + # Now we compute the working precisions ywpd[k] + # Se II equation (92) + ywpd={} + d1 = max(6,ctx.mag(40*L*L)) + yd2 = 13+ctx.mag((1+abs(ysigma))*yA)-ctx.mag(yeps4)-1 + yconst = ctx.ln(8/(ctx.pi*ctx.pi*a*a*yB1*yB1)) /2 + for n in range(0,L): + yd3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*yconst)+yd2 + ywpd[n]=max(yd3,d1) + + # procedure of II Section 3.17 + ctx.prec = ywpd[1]+10 + ypsigma = 1-(2*ysigma) + yd = {} + yd[0,0,-2]=0; yd[0,0,-1]=0; yd[0,0,0]=1; yd[0,0,1]=0 + yd[0,-1,-2]=0; yd[0,-1,-1]=0; yd[0,-1,0]=1; yd[0,-1,1]=0 + for n in range(1,L): + ctx.prec = ywpd[n]+10 + for k in range(0,3*n//2+1): + m = 3*n-2*k + if(m!=0): + m1 = ctx.one/m + c1= m1/4 + c2=(ypsigma*m1)/2 + c3=-(m+1) + yd[0,n,k]=c3*yd[0,n-1,k-2]+c1*yd[0,n-1,k]+c2*yd[0,n-1,k-1] + else: + yd[0,n,k]=0 + for r in range(0,k): + add=yd[0,n,r]*(ctx.mpf('1.0')*ctx.fac(2*k-2*r)/ctx.fac(k-r)) + yd[0,n,k] -= ((-1)**(k-r))*add + yd[0,n,-2]=0; yd[0,n,-1]=0; yd[0,n,3*n//2+1]=0 + + for mu in range(-2,der+1): + for n in range(-2,L): + for k in range(-3,max(1,3*n//2+2)): + if( (mu<0)or (n<0) or(k<0)or (k>3*n//2)): + yd[mu,n,k] = 0 + for mu in range(1,der+1): + for n in range(0,L): + ctx.prec = ywpd[n]+10 + for k in range(0,3*n//2+1): + aux=(2*mu-2)*yd[mu-2,n-2,k-3]+2*(ysigma+n-2)*yd[mu-1,n-2,k-3] + yd[mu,n,k] = aux - yd[mu-1,n-1,k-1] + + # COMPUTING THE COEFFICIENTS xtcoef[k,l] + # See II Section 3.9 + # + # computing the needed wp + xwptcoef={} + xwpterm={} + ctx.prec = 15 + c1 = ctx.mag(40*(L+2)) + xc2 = ctx.mag(68*(L+2)*xA) + xc4 = ctx.mag(xB1*a*math.sqrt(ctx.pi))-1 + for k in range(0,L): + xc3 = xc2 - k*xc4+ctx.mag(ctx.fac(k+0.5))/2. + xwptcoef[k] = (max(c1,xc3-ctx.mag(xeps4)+1)+1 +20)*1.5 + xwpterm[k] = (max(c1,ctx.mag(L+2)+xc3-ctx.mag(xeps3)+1)+1 +20) + ywptcoef={} + ywpterm={} + ctx.prec = 15 + c1 = ctx.mag(40*(L+2)) + yc2 = ctx.mag(68*(L+2)*yA) + yc4 = ctx.mag(yB1*a*math.sqrt(ctx.pi))-1 + for k in range(0,L): + yc3 = yc2 - k*yc4+ctx.mag(ctx.fac(k+0.5))/2. + ywptcoef[k] = ((max(c1,yc3-ctx.mag(yeps4)+1))+10)*1.5 + ywpterm[k] = (max(c1,ctx.mag(L+2)+yc3-ctx.mag(yeps3)+1)+1)+10 + + # check of power of pi + # computing the fortcoef[mu,k,ell] + xfortcoef={} + for mu in range(0,der+1): + for k in range(0,L): + for ell in range(-2,3*k//2+1): + xfortcoef[mu,k,ell]=0 + for mu in range(0,der+1): + for k in range(0,L): + ctx.prec = xwptcoef[k] + for ell in range(0,3*k//2+1): + xfortcoef[mu,k,ell]=xd[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] + xfortcoef[mu,k,ell]=xfortcoef[mu,k,ell]/((2*ctx.j)**ell) + + def trunc_a(t): + wp = ctx.prec + ctx.prec = wp + 2 + aa = ctx.sqrt(t/(2*ctx.pi)) + ctx.prec = wp + return aa + + # computing the tcoef[k,ell] + xtcoef={} + for mu in range(0,der+1): + for k in range(0,L): + for ell in range(-2,3*k//2+1): + xtcoef[mu,k,ell]=0 + ctx.prec = max(xwptcoef[0],ywptcoef[0])+3 + aa= trunc_a(t) + la = -ctx.ln(aa) + + for chi in range(0,der+1): + for k in range(0,L): + ctx.prec = xwptcoef[k] + for ell in range(0,3*k//2+1): + xtcoef[chi,k,ell] =0 + for mu in range(0, chi+1): + tcoefter=ctx.binomial(chi,mu)*ctx.power(la,mu)*xfortcoef[chi-mu,k,ell] + xtcoef[chi,k,ell] += tcoefter + + # COMPUTING THE COEFFICIENTS ytcoef[k,l] + # See II Section 3.9 + # + # computing the needed wp + # check of power of pi + # computing the fortcoef[mu,k,ell] + yfortcoef={} + for mu in range(0,der+1): + for k in range(0,L): + for ell in range(-2,3*k//2+1): + yfortcoef[mu,k,ell]=0 + for mu in range(0,der+1): + for k in range(0,L): + ctx.prec = ywptcoef[k] + for ell in range(0,3*k//2+1): + yfortcoef[mu,k,ell]=yd[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] + yfortcoef[mu,k,ell]=yfortcoef[mu,k,ell]/((2*ctx.j)**ell) + # computing the tcoef[k,ell] + ytcoef={} + for chi in range(0,der+1): + for k in range(0,L): + for ell in range(-2,3*k//2+1): + ytcoef[chi,k,ell]=0 + for chi in range(0,der+1): + for k in range(0,L): + ctx.prec = ywptcoef[k] + for ell in range(0,3*k//2+1): + ytcoef[chi,k,ell] =0 + for mu in range(0, chi+1): + tcoefter=ctx.binomial(chi,mu)*ctx.power(la,mu)*yfortcoef[chi-mu,k,ell] + ytcoef[chi,k,ell] += tcoefter + + # COMPUTING tv[k,ell] + # See II Section 3.8 + # + # a has a good value + ctx.prec = max(xwptcoef[0], ywptcoef[0])+2 + av = {} + av[0] = 1 + av[1] = av[0]/a + + ctx.prec = max(xwptcoef[0],ywptcoef[0]) + for k in range(2,L): + av[k] = av[k-1] * av[1] + + # Computing the quotients + xtv = {} + for chi in range(0,der+1): + for k in range(0,L): + ctx.prec = xwptcoef[k] + for ell in range(0,3*k//2+1): + xtv[chi,k,ell] = xtcoef[chi,k,ell]* av[k] + # Computing the quotients + ytv = {} + for chi in range(0,der+1): + for k in range(0,L): + ctx.prec = ywptcoef[k] + for ell in range(0,3*k//2+1): + ytv[chi,k,ell] = ytcoef[chi,k,ell]* av[k] + + # COMPUTING THE TERMS xterm[k] + # See II Section 3.6 + xterm = {} + for chi in range(0,der+1): + for n in range(0,L): + ctx.prec = xwpterm[n] + te = 0 + for k in range(0, 3*n//2+1): + te += xtv[chi,n,k] + xterm[chi,n] = te + + # COMPUTING THE TERMS yterm[k] + # See II Section 3.6 + yterm = {} + for chi in range(0,der+1): + for n in range(0,L): + ctx.prec = ywpterm[n] + te = 0 + for k in range(0, 3*n//2+1): + te += ytv[chi,n,k] + yterm[chi,n] = te + + # COMPUTING rssum + # See II Section 3.5 + xrssum={} + ctx.prec=15 + xrsbound = math.sqrt(ctx.pi) * xc /(xb*a) + ctx.prec=15 + xwprssum = ctx.mag(4.4*((L+3)**2)*xrsbound / xeps2) + xwprssum = max(xwprssum, ctx.mag(10*(L+1))) + ctx.prec = xwprssum + for chi in range(0,der+1): + xrssum[chi] = 0 + for k in range(1,L+1): + xrssum[chi] += xterm[chi,L-k] + yrssum={} + ctx.prec=15 + yrsbound = math.sqrt(ctx.pi) * yc /(yb*a) + ctx.prec=15 + ywprssum = ctx.mag(4.4*((L+3)**2)*yrsbound / yeps2) + ywprssum = max(ywprssum, ctx.mag(10*(L+1))) + ctx.prec = ywprssum + for chi in range(0,der+1): + yrssum[chi] = 0 + for k in range(1,L+1): + yrssum[chi] += yterm[chi,L-k] + + # COMPUTING S3 + # See II Section 3.19 + ctx.prec = 15 + A2 = 2**(max(ctx.mag(abs(xrssum[0])), ctx.mag(abs(yrssum[0])))) + eps8 = eps/(3*A2) + T = t *ctx.ln(t/(2*ctx.pi)) + xwps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-xsigma))*T) + ywps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-ysigma))*T) + + ctx.prec = max(xwps3, ywps3) + + tpi = t/(2*ctx.pi) + arg = (t/2)*ctx.ln(tpi)-(t/2)-ctx.pi/8 + U = ctx.expj(-arg) + a = trunc_a(t) + xasigma = ctx.power(a, -xsigma) + yasigma = ctx.power(a, -ysigma) + xS3 = ((-1)**(N-1)) * xasigma * U + yS3 = ((-1)**(N-1)) * yasigma * U + + # COMPUTING S1 the zetasum + # See II Section 3.18 + ctx.prec = 15 + xwpsum = 4+ ctx.mag((N+ctx.power(N,1-xsigma))*ctx.ln(N) /eps1) + ywpsum = 4+ ctx.mag((N+ctx.power(N,1-ysigma))*ctx.ln(N) /eps1) + wpsum = max(xwpsum, ywpsum) + + ctx.prec = wpsum +10 + ''' + # This can be improved + xS1={} + yS1={} + for chi in range(0,der+1): + xS1[chi] = 0 + yS1[chi] = 0 + for n in range(1,int(N)+1): + ln = ctx.ln(n) + xexpn = ctx.exp(-ln*(xsigma+ctx.j*t)) + yexpn = ctx.conj(1/(n*xexpn)) + for chi in range(0,der+1): + pown = ctx.power(-ln, chi) + xterm = pown*xexpn + yterm = pown*yexpn + xS1[chi] += xterm + yS1[chi] += yterm + ''' + xS1, yS1 = ctx._zetasum(s, 1, int(N)-1, range(0,der+1), True) + + # END OF COMPUTATION of xrz, yrz + # See II Section 3.1 + ctx.prec = 15 + xabsS1 = abs(xS1[der]) + xabsS2 = abs(xrssum[der] * xS3) + xwpend = max(6, wpinitial+ctx.mag(6*(3*xabsS1+7*xabsS2) ) ) + + ctx.prec = xwpend + xrz={} + for chi in range(0,der+1): + xrz[chi] = xS1[chi]+xrssum[chi]*xS3 + + ctx.prec = 15 + yabsS1 = abs(yS1[der]) + yabsS2 = abs(yrssum[der] * yS3) + ywpend = max(6, wpinitial+ctx.mag(6*(3*yabsS1+7*yabsS2) ) ) + + ctx.prec = ywpend + yrz={} + for chi in range(0,der+1): + yrz[chi] = yS1[chi]+yrssum[chi]*yS3 + yrz[chi] = ctx.conj(yrz[chi]) + ctx.prec = wpinitial + return xrz, yrz + +def Rzeta_set(ctx, s, derivatives=[0]): + r""" + Computes several derivatives of the auxiliary function of Riemann `R(s)`. + + **Definition** + + The function is defined by + + .. math :: + + \begin{equation} + {\mathop{\mathcal R }\nolimits}(s)= + \int_{0\swarrow1}\frac{x^{-s} e^{\pi i x^2}}{e^{\pi i x}- + e^{-\pi i x}}\,dx + \end{equation} + + To this function we apply the Riemann-Siegel expansion. + """ + der = max(derivatives) + # First we take the value of ctx.prec + # During the computation we will change ctx.prec, and finally we will + # restaurate the initial value + wpinitial = ctx.prec + # Take the real and imaginary part of s + t = ctx._im(s) + sigma = ctx._re(s) + # Now compute several parameter that appear on the program + ctx.prec = 15 + a = ctx.sqrt(t/(2*ctx.pi)) # Careful + asigma = ctx.power(a, sigma) # Careful + # We need a simple bound A1 < asigma (see II Section 3.1 and 3.3) + A1 = ctx.power(2, ctx.mag(asigma)-1) + # We compute various epsilon's (see II end of Section 3.1) + eps = ctx.power(2, -wpinitial) + eps1 = eps/6. + eps2 = eps * A1/3. + # COMPUTING SOME COEFFICIENTS THAT DEPENDS + # ON sigma + # constant b and c (see I Theorem 2 formula (26) ) + # coefficients A and B1 (see I Section 6.1 equation (50)) + # here we not need high precision + ctx.prec = 15 + if sigma > 0: + b = 2. + c = math.pow(9,sigma)/4.44288 + # 4.44288 =(math.sqrt(2)*math.pi) + A = math.pow(9,sigma) + B1 = 1 + else: + b = 2.25158 # math.sqrt( (3-2* math.log(2))*math.pi ) + c = math.pow(2,-sigma)/4.44288 + A = math.pow(2,-sigma) + B1 = 1.10789 # = 2*sqrt(1-log(2)) + # COMPUTING L THE NUMBER OF TERMS NEEDED IN THE RIEMANN-SIEGEL + # CORRECTION + # See II Section 3.2 + ctx.prec = 15 + L = 1 + while 3*c*ctx.gamma(L*0.5) * ctx.power(b*a,-L) >= eps2: + L = L+1 + L = max(2,L) + # The number L has to satify some conditions. + # If not RS can not compute Rzeta(s) with the prescribed precision + # (see II, Section 3.2 condition (20) ) and + # (II, Section 3.3 condition (22) ). Also we have added + # an additional technical condition in Section 3.17 Proposition 17 + if ((3*L >= 2*a*a/25.) or (3*L+2+sigma<0) or (abs(sigma)> a/2.)): + #print 'Error Riemann-Siegel can not compute with such precision' + ctx.prec = wpinitial + raise NotImplementedError("Riemann-Siegel can not compute with such precision") + + # INITIALIZATION (CONTINUATION) + # + # eps3 is the constant defined on (II, Section 3.5 equation (27) ) + # each term of the RS correction must be computed with error <= eps3 + eps3 = eps2/(4*L) + + # eps4 is defined on (II Section 3.6 equation (30) ) + # each component of the formula (II Section 3.6 equation (29) ) + # must be computed with error <= eps4 + eps4 = eps3/(3*L) + + # COMPUTING M. NUMBER OF DERIVATIVES Fp[m] TO COMPUTE + M = aux_M_Fp(ctx, A, eps4, a, B1, L) + Fp = {} + for n in range(M, 3*L-2): + Fp[n] = 0 + + # But I have not seen an instance of M != 3*L-3 + # + # DETERMINATION OF J THE NUMBER OF TERMS NEEDED + # IN THE TAYLOR SERIES OF F. + # See II Section 3.11 equation (49)) + h1 = eps4/(632*A) + h2 = ctx.pi*ctx.pi*B1*a *ctx.sqrt(3)*math.e*math.e + h2 = h1 * ctx.power((h2/M**2),(M-1)/3) / M + h3 = min(h1,h2) + J=12 + jvalue = (2*ctx.pi)**J / ctx.gamma(J+1) + while jvalue > h3: + J = J+1 + jvalue = (2*ctx.pi)*jvalue/J + + # COMPUTING eps5[m] for 1 <= m <= 21 + # See II Section 10 equation (43) + eps5={} + foreps5 = math.pi*math.pi*B1*a + for m in range(0,22): + aux1 = math.pow(foreps5, m/3)/(316.*A) + aux2 = ctx.gamma(m+1)/ctx.gamma(m/3.0+0.5) + aux2 = math.sqrt(aux2) + eps5[m] = aux1*aux2*eps4 + + # COMPUTING wpfp + # See II Section 3.13 equation (59) + twenty = min(3*L-3, 21)+1 + aux = 6812*J + wpfp = ctx.mag(44*J) + for m in range(0, twenty): + wpfp = max(wpfp, ctx.mag(aux*ctx.gamma(m+1)/eps5[m])) + # COMPUTING N AND p + # See II Section + ctx.prec = wpfp + ctx.mag(t) + 20 + a = ctx.sqrt(t/(2*ctx.pi)) + N = ctx.floor(a) + p = 1-2*(a-N) + + # now we get a rounded version of p to the precision wpfp + # this possibly is not necessary + num = ctx.floor(p*(ctx.mpf(2)**wpfp)) + difference = p * (ctx.mpf(2)**wpfp)-num + if difference < 0.5: + num = num + else: + num = num+1 + p = ctx.convert(num * (ctx.mpf(2)**(-wpfp))) + + # COMPUTING THE COEFFICIENTS c[n] = cc[n] + # We shall use the notation cc[n], since there is + # a constant that is called c + # See II Section 3.14 + # We compute the coefficients and also save then in a + # cache. The bulk of the computation is passed to + # the function coef() + # + # eps6 is defined in II Section 3.13 equation (58) + eps6 = ctx.power(2*ctx.pi, J)/(ctx.gamma(J+1)*3*J) + + # Now we compute the coefficients + cc={} + cont={} + cont, pipowers = coef(ctx, J, eps6) + cc = cont.copy() # we need a copy since we have + Fp={} + for n in range(M, 3*L-2): + Fp[n] = 0 + ctx.prec = wpfp + for m in range(0,M+1): + sumP = 0 + for k in range(2*J-m-1,-1,-1): + sumP = (sumP * p) + cc[k] + Fp[m] = sumP + # preparation of the new coefficients + for k in range(0, 2*J-m-1): + cc[k] = (k+1) * cc[k+1] + + # COMPUTING THE NUMBERS d[n,k] + # See II Section 3.17 + + # First we compute the working precisions wpd[k] + # Se II equation (92) + wpd = {} + d1 = max(6, ctx.mag(40*L*L)) + d2 = 13+ctx.mag((1+abs(sigma))*A)-ctx.mag(eps4)-1 + const = ctx.ln(8/(ctx.pi*ctx.pi*a*a*B1*B1)) /2 + for n in range(0,L): + d3 = ctx.mag(ctx.sqrt(ctx.gamma(n-0.5)))-ctx.floor(n*const)+d2 + wpd[n] = max(d3,d1) + + # procedure of II Section 3.17 + ctx.prec = wpd[1]+10 + psigma = 1-(2*sigma) + d = {} + d[0,0,-2]=0; d[0,0,-1]=0; d[0,0,0]=1; d[0,0,1]=0 + d[0,-1,-2]=0; d[0,-1,-1]=0; d[0,-1,0]=1; d[0,-1,1]=0 + for n in range(1,L): + ctx.prec = wpd[n]+10 + for k in range(0,3*n//2+1): + m = 3*n-2*k + if (m!=0): + m1 = ctx.one/m + c1 = m1/4 + c2 = (psigma*m1)/2 + c3 = -(m+1) + d[0,n,k] = c3*d[0,n-1,k-2]+c1*d[0,n-1,k]+c2*d[0,n-1,k-1] + else: + d[0,n,k]=0 + for r in range(0,k): + add = d[0,n,r]*(ctx.one*ctx.fac(2*k-2*r)/ctx.fac(k-r)) + d[0,n,k] -= ((-1)**(k-r))*add + d[0,n,-2]=0; d[0,n,-1]=0; d[0,n,3*n//2+1]=0 + + for mu in range(-2,der+1): + for n in range(-2,L): + for k in range(-3,max(1,3*n//2+2)): + if ((mu<0)or (n<0) or(k<0)or (k>3*n//2)): + d[mu,n,k] = 0 + + for mu in range(1,der+1): + for n in range(0,L): + ctx.prec = wpd[n]+10 + for k in range(0,3*n//2+1): + aux=(2*mu-2)*d[mu-2,n-2,k-3]+2*(sigma+n-2)*d[mu-1,n-2,k-3] + d[mu,n,k] = aux - d[mu-1,n-1,k-1] + + # COMPUTING THE COEFFICIENTS t[k,l] + # See II Section 3.9 + # + # computing the needed wp + wptcoef = {} + wpterm = {} + ctx.prec = 15 + c1 = ctx.mag(40*(L+2)) + c2 = ctx.mag(68*(L+2)*A) + c4 = ctx.mag(B1*a*math.sqrt(ctx.pi))-1 + for k in range(0,L): + c3 = c2 - k*c4+ctx.mag(ctx.fac(k+0.5))/2. + wptcoef[k] = max(c1,c3-ctx.mag(eps4)+1)+1 +10 + wpterm[k] = max(c1,ctx.mag(L+2)+c3-ctx.mag(eps3)+1)+1 +10 + + # check of power of pi + + # computing the fortcoef[mu,k,ell] + fortcoef={} + for mu in derivatives: + for k in range(0,L): + for ell in range(-2,3*k//2+1): + fortcoef[mu,k,ell]=0 + + for mu in derivatives: + for k in range(0,L): + ctx.prec = wptcoef[k] + for ell in range(0,3*k//2+1): + fortcoef[mu,k,ell]=d[mu,k,ell]*Fp[3*k-2*ell]/pipowers[2*k-ell] + fortcoef[mu,k,ell]=fortcoef[mu,k,ell]/((2*ctx.j)**ell) + + def trunc_a(t): + wp = ctx.prec + ctx.prec = wp + 2 + aa = ctx.sqrt(t/(2*ctx.pi)) + ctx.prec = wp + return aa + + # computing the tcoef[chi,k,ell] + tcoef={} + for chi in derivatives: + for k in range(0,L): + for ell in range(-2,3*k//2+1): + tcoef[chi,k,ell]=0 + ctx.prec = wptcoef[0]+3 + aa = trunc_a(t) + la = -ctx.ln(aa) + + for chi in derivatives: + for k in range(0,L): + ctx.prec = wptcoef[k] + for ell in range(0,3*k//2+1): + tcoef[chi,k,ell] = 0 + for mu in range(0, chi+1): + tcoefter = ctx.binomial(chi,mu) * la**mu * \ + fortcoef[chi-mu,k,ell] + tcoef[chi,k,ell] += tcoefter + + # COMPUTING tv[k,ell] + # See II Section 3.8 + + # Computing the powers av[k] = a**(-k) + ctx.prec = wptcoef[0] + 2 + + # a has a good value of a. + # See II Section 3.6 + av = {} + av[0] = 1 + av[1] = av[0]/a + + ctx.prec = wptcoef[0] + for k in range(2,L): + av[k] = av[k-1] * av[1] + + # Computing the quotients + tv = {} + for chi in derivatives: + for k in range(0,L): + ctx.prec = wptcoef[k] + for ell in range(0,3*k//2+1): + tv[chi,k,ell] = tcoef[chi,k,ell]* av[k] + + # COMPUTING THE TERMS term[k] + # See II Section 3.6 + term = {} + for chi in derivatives: + for n in range(0,L): + ctx.prec = wpterm[n] + te = 0 + for k in range(0, 3*n//2+1): + te += tv[chi,n,k] + term[chi,n] = te + + # COMPUTING rssum + # See II Section 3.5 + rssum={} + ctx.prec=15 + rsbound = math.sqrt(ctx.pi) * c /(b*a) + ctx.prec=15 + wprssum = ctx.mag(4.4*((L+3)**2)*rsbound / eps2) + wprssum = max(wprssum, ctx.mag(10*(L+1))) + ctx.prec = wprssum + for chi in derivatives: + rssum[chi] = 0 + for k in range(1,L+1): + rssum[chi] += term[chi,L-k] + + # COMPUTING S3 + # See II Section 3.19 + ctx.prec = 15 + A2 = 2**(ctx.mag(rssum[0])) + eps8 = eps/(3* A2) + T = t * ctx.ln(t/(2*ctx.pi)) + wps3 = 5 + ctx.mag((1+(2/eps8)*ctx.power(a,-sigma))*T) + + ctx.prec = wps3 + tpi = t/(2*ctx.pi) + arg = (t/2)*ctx.ln(tpi)-(t/2)-ctx.pi/8 + U = ctx.expj(-arg) + a = trunc_a(t) + asigma = ctx.power(a, -sigma) + S3 = ((-1)**(N-1)) * asigma * U + + # COMPUTING S1 the zetasum + # See II Section 3.18 + ctx.prec = 15 + wpsum = 4 + ctx.mag((N+ctx.power(N,1-sigma))*ctx.ln(N)/eps1) + + ctx.prec = wpsum + 10 + ''' + # This can be improved + S1 = {} + for chi in derivatives: + S1[chi] = 0 + for n in range(1,int(N)+1): + ln = ctx.ln(n) + expn = ctx.exp(-ln*(sigma+ctx.j*t)) + for chi in derivatives: + term = ctx.power(-ln, chi)*expn + S1[chi] += term + ''' + S1 = ctx._zetasum(s, 1, int(N)-1, derivatives)[0] + + # END OF COMPUTATION + # See II Section 3.1 + ctx.prec = 15 + absS1 = abs(S1[der]) + absS2 = abs(rssum[der] * S3) + wpend = max(6, wpinitial + ctx.mag(6*(3*absS1+7*absS2))) + ctx.prec = wpend + rz = {} + for chi in derivatives: + rz[chi] = S1[chi]+rssum[chi]*S3 + ctx.prec = wpinitial + return rz + + +def z_half(ctx,t,der=0): + r""" + z_half(t,der=0) Computes Z^(der)(t) + """ + s=ctx.mpf('0.5')+ctx.j*t + wpinitial = ctx.prec + ctx.prec = 15 + tt = t/(2*ctx.pi) + wptheta = wpinitial +1 + ctx.mag(3*(tt**1.5)*ctx.ln(tt)) + wpz = wpinitial + 1 + ctx.mag(12*tt*ctx.ln(tt)) + ctx.prec = wptheta * LOGGAMMA_BROKENNESS_FACTOR + theta = ctx.siegeltheta(t) + ctx.prec = wpz + rz = Rzeta_set(ctx,s, range(der+1)) + if der > 0: ps1 = ctx._re(ctx.psi(0,s/2)/2 - ctx.ln(ctx.pi)/2) + if der > 1: ps2 = ctx._re(ctx.j*ctx.psi(1,s/2)/4) + if der > 2: ps3 = ctx._re(-ctx.psi(2,s/2)/8) + if der > 3: ps4 = ctx._re(-ctx.j*ctx.psi(3,s/2)/16) + exptheta = ctx.expj(theta) + if der == 0: + z = 2*exptheta*rz[0] + if der == 1: + zf = 2j*exptheta + z = zf*(ps1*rz[0]+rz[1]) + if der == 2: + zf = 2 * exptheta + z = -zf*(2*rz[1]*ps1+rz[0]*ps1**2+rz[2]-ctx.j*rz[0]*ps2) + if der == 3: + zf = -2j*exptheta + z = 3*rz[1]*ps1**2+rz[0]*ps1**3+3*ps1*rz[2] + z = zf*(z-3j*rz[1]*ps2-3j*rz[0]*ps1*ps2+rz[3]-rz[0]*ps3) + if der == 4: + zf = 2*exptheta + z = 4*rz[1]*ps1**3+rz[0]*ps1**4+6*ps1**2*rz[2] + z = z-12j*rz[1]*ps1*ps2-6j*rz[0]*ps1**2*ps2-6j*rz[2]*ps2-3*rz[0]*ps2*ps2 + z = z + 4*ps1*rz[3]-4*rz[1]*ps3-4*rz[0]*ps1*ps3+rz[4]+ctx.j*rz[0]*ps4 + z = zf*z + ctx.prec = wpinitial + return ctx._re(z) + +def zeta_half(ctx, s, k=0): + """ + zeta_half(s,k=0) Computes zeta^(k)(s) when Re s = 0.5 + """ + wpinitial = ctx.prec + sigma = ctx._re(s) + t = ctx._im(s) + #--- compute wptheta, wpR, wpbasic --- + ctx.prec = 53 + # X see II Section 3.21 (109) and (110) + if sigma > 0: + X = ctx.sqrt(abs(s)) + else: + X = (2*ctx.pi)**(sigma-1) * abs(1-s)**(0.5-sigma) + # M1 see II Section 3.21 (111) and (112) + if sigma > 0: + M1 = 2*ctx.sqrt(t/(2*ctx.pi)) + else: + M1 = 4 * t * X + # T see II Section 3.21 (113) + abst = abs(0.5-s) + T = 2* abst*math.log(abst) + # computing wpbasic, wptheta, wpR see II Section 3.21 + wpbasic = max(6,3+ctx.mag(t)) + wpbasic2 = 2+ctx.mag(2.12*M1+21.2*M1*X+1.3*M1*X*T)+wpinitial+1 + wpbasic = max(wpbasic, wpbasic2) + wptheta = max(4, 3+ctx.mag(2.7*M1*X)+wpinitial+1) + wpR = 3+ctx.mag(1.1+2*X)+wpinitial+1 + ctx.prec = wptheta * LOGGAMMA_BROKENNESS_FACTOR + theta = ctx.siegeltheta(t-ctx.j*(sigma-ctx.mpf('0.5'))) + if k > 0: ps1 = (ctx._re(ctx.psi(0,s/2)))/2 - ctx.ln(ctx.pi)/2 + if k > 1: ps2 = -(ctx._im(ctx.psi(1,s/2)))/4 + if k > 2: ps3 = -(ctx._re(ctx.psi(2,s/2)))/8 + if k > 3: ps4 = (ctx._im(ctx.psi(3,s/2)))/16 + ctx.prec = wpR + xrz = Rzeta_set(ctx,s,range(k+1)) + yrz={} + for chi in range(0,k+1): + yrz[chi] = ctx.conj(xrz[chi]) + ctx.prec = wpbasic + exptheta = ctx.expj(-2*theta) + if k==0: + zv = xrz[0]+exptheta*yrz[0] + if k==1: + zv1 = -yrz[1] - 2*yrz[0]*ps1 + zv = xrz[1] + exptheta*zv1 + if k==2: + zv1 = 4*yrz[1]*ps1+4*yrz[0]*(ps1**2)+yrz[2]+2j*yrz[0]*ps2 + zv = xrz[2]+exptheta*zv1 + if k==3: + zv1 = -12*yrz[1]*ps1**2-8*yrz[0]*ps1**3-6*yrz[2]*ps1-6j*yrz[1]*ps2 + zv1 = zv1 - 12j*yrz[0]*ps1*ps2-yrz[3]+2*yrz[0]*ps3 + zv = xrz[3]+exptheta*zv1 + if k == 4: + zv1 = 32*yrz[1]*ps1**3 +16*yrz[0]*ps1**4+24*yrz[2]*ps1**2 + zv1 = zv1 +48j*yrz[1]*ps1*ps2+48j*yrz[0]*(ps1**2)*ps2 + zv1 = zv1+12j*yrz[2]*ps2-12*yrz[0]*ps2**2+8*yrz[3]*ps1-8*yrz[1]*ps3 + zv1 = zv1-16*yrz[0]*ps1*ps3+yrz[4]-2j*yrz[0]*ps4 + zv = xrz[4]+exptheta*zv1 + ctx.prec = wpinitial + return zv + +def zeta_offline(ctx, s, k=0): + """ + Computes zeta^(k)(s) off the line + """ + wpinitial = ctx.prec + sigma = ctx._re(s) + t = ctx._im(s) + #--- compute wptheta, wpR, wpbasic --- + ctx.prec = 53 + # X see II Section 3.21 (109) and (110) + if sigma > 0: + X = ctx.power(abs(s), 0.5) + else: + X = ctx.power(2*ctx.pi, sigma-1)*ctx.power(abs(1-s),0.5-sigma) + # M1 see II Section 3.21 (111) and (112) + if (sigma > 0): + M1 = 2*ctx.sqrt(t/(2*ctx.pi)) + else: + M1 = 4 * t * X + # M2 see II Section 3.21 (111) and (112) + if (1-sigma > 0): + M2 = 2*ctx.sqrt(t/(2*ctx.pi)) + else: + M2 = 4*t*ctx.power(2*ctx.pi, -sigma)*ctx.power(abs(s),sigma-0.5) + # T see II Section 3.21 (113) + abst = abs(0.5-s) + T = 2* abst*math.log(abst) + # computing wpbasic, wptheta, wpR see II Section 3.21 + wpbasic = max(6,3+ctx.mag(t)) + wpbasic2 = 2+ctx.mag(2.12*M1+21.2*M2*X+1.3*M2*X*T)+wpinitial+1 + wpbasic = max(wpbasic, wpbasic2) + wptheta = max(4, 3+ctx.mag(2.7*M2*X)+wpinitial+1) + wpR = 3+ctx.mag(1.1+2*X)+wpinitial+1 + ctx.prec = wptheta * LOGGAMMA_BROKENNESS_FACTOR + theta = ctx.siegeltheta(t-ctx.j*(sigma-ctx.mpf('0.5'))) + s1 = s + s2 = ctx.conj(1-s1) + ctx.prec = wpR + xrz, yrz = Rzeta_simul(ctx, s, k) + if k > 0: ps1 = (ctx.psi(0,s1/2)+ctx.psi(0,(1-s1)/2))/4 - ctx.ln(ctx.pi)/2 + if k > 1: ps2 = ctx.j*(ctx.psi(1,s1/2)-ctx.psi(1,(1-s1)/2))/8 + if k > 2: ps3 = -(ctx.psi(2,s1/2)+ctx.psi(2,(1-s1)/2))/16 + if k > 3: ps4 = -ctx.j*(ctx.psi(3,s1/2)-ctx.psi(3,(1-s1)/2))/32 + ctx.prec = wpbasic + exptheta = ctx.expj(-2*theta) + if k == 0: + zv = xrz[0]+exptheta*yrz[0] + if k == 1: + zv1 = -yrz[1]-2*yrz[0]*ps1 + zv = xrz[1]+exptheta*zv1 + if k == 2: + zv1 = 4*yrz[1]*ps1+4*yrz[0]*(ps1**2) +yrz[2]+2j*yrz[0]*ps2 + zv = xrz[2]+exptheta*zv1 + if k == 3: + zv1 = -12*yrz[1]*ps1**2 -8*yrz[0]*ps1**3-6*yrz[2]*ps1-6j*yrz[1]*ps2 + zv1 = zv1 - 12j*yrz[0]*ps1*ps2-yrz[3]+2*yrz[0]*ps3 + zv = xrz[3]+exptheta*zv1 + if k == 4: + zv1 = 32*yrz[1]*ps1**3 +16*yrz[0]*ps1**4+24*yrz[2]*ps1**2 + zv1 = zv1 +48j*yrz[1]*ps1*ps2+48j*yrz[0]*(ps1**2)*ps2 + zv1 = zv1+12j*yrz[2]*ps2-12*yrz[0]*ps2**2+8*yrz[3]*ps1-8*yrz[1]*ps3 + zv1 = zv1-16*yrz[0]*ps1*ps3+yrz[4]-2j*yrz[0]*ps4 + zv = xrz[4]+exptheta*zv1 + ctx.prec = wpinitial + return zv + +def z_offline(ctx, w, k=0): + r""" + Computes Z(w) and its derivatives off the line + """ + s = ctx.mpf('0.5')+ctx.j*w + s1 = s + s2 = ctx.conj(1-s1) + wpinitial = ctx.prec + ctx.prec = 35 + # X see II Section 3.21 (109) and (110) + # M1 see II Section 3.21 (111) and (112) + if (ctx._re(s1) >= 0): + M1 = 2*ctx.sqrt(ctx._im(s1)/(2 * ctx.pi)) + X = ctx.sqrt(abs(s1)) + else: + X = (2*ctx.pi)**(ctx._re(s1)-1) * abs(1-s1)**(0.5-ctx._re(s1)) + M1 = 4 * ctx._im(s1)*X + # M2 see II Section 3.21 (111) and (112) + if (ctx._re(s2) >= 0): + M2 = 2*ctx.sqrt(ctx._im(s2)/(2 * ctx.pi)) + else: + M2 = 4 * ctx._im(s2)*(2*ctx.pi)**(ctx._re(s2)-1)*abs(1-s2)**(0.5-ctx._re(s2)) + # T see II Section 3.21 Prop. 27 + T = 2*abs(ctx.siegeltheta(w)) + # defining some precisions + # see II Section 3.22 (115), (116), (117) + aux1 = ctx.sqrt(X) + aux2 = aux1*(M1+M2) + aux3 = 3 +wpinitial + wpbasic = max(6, 3+ctx.mag(T), ctx.mag(aux2*(26+2*T))+aux3) + wptheta = max(4,ctx.mag(2.04*aux2)+aux3) + wpR = ctx.mag(4*aux1)+aux3 + # now the computations + ctx.prec = wptheta * LOGGAMMA_BROKENNESS_FACTOR + theta = ctx.siegeltheta(w) + ctx.prec = wpR + xrz, yrz = Rzeta_simul(ctx,s,k) + pta = 0.25 + 0.5j*w + ptb = 0.25 - 0.5j*w + if k > 0: ps1 = 0.25*(ctx.psi(0,pta)+ctx.psi(0,ptb)) - ctx.ln(ctx.pi)/2 + if k > 1: ps2 = (1j/8)*(ctx.psi(1,pta)-ctx.psi(1,ptb)) + if k > 2: ps3 = (-1./16)*(ctx.psi(2,pta)+ctx.psi(2,ptb)) + if k > 3: ps4 = (-1j/32)*(ctx.psi(3,pta)-ctx.psi(3,ptb)) + ctx.prec = wpbasic + exptheta = ctx.expj(theta) + if k == 0: + zv = exptheta*xrz[0]+yrz[0]/exptheta + j = ctx.j + if k == 1: + zv = j*exptheta*(xrz[1]+xrz[0]*ps1)-j*(yrz[1]+yrz[0]*ps1)/exptheta + if k == 2: + zv = exptheta*(-2*xrz[1]*ps1-xrz[0]*ps1**2-xrz[2]+j*xrz[0]*ps2) + zv =zv + (-2*yrz[1]*ps1-yrz[0]*ps1**2-yrz[2]-j*yrz[0]*ps2)/exptheta + if k == 3: + zv1 = -3*xrz[1]*ps1**2-xrz[0]*ps1**3-3*xrz[2]*ps1+j*3*xrz[1]*ps2 + zv1 = (zv1+ 3j*xrz[0]*ps1*ps2-xrz[3]+xrz[0]*ps3)*j*exptheta + zv2 = 3*yrz[1]*ps1**2+yrz[0]*ps1**3+3*yrz[2]*ps1+j*3*yrz[1]*ps2 + zv2 = j*(zv2 + 3j*yrz[0]*ps1*ps2+ yrz[3]-yrz[0]*ps3)/exptheta + zv = zv1+zv2 + if k == 4: + zv1 = 4*xrz[1]*ps1**3+xrz[0]*ps1**4 + 6*xrz[2]*ps1**2 + zv1 = zv1-12j*xrz[1]*ps1*ps2-6j*xrz[0]*ps1**2*ps2-6j*xrz[2]*ps2 + zv1 = zv1-3*xrz[0]*ps2*ps2+4*xrz[3]*ps1-4*xrz[1]*ps3-4*xrz[0]*ps1*ps3 + zv1 = zv1+xrz[4]+j*xrz[0]*ps4 + zv2 = 4*yrz[1]*ps1**3+yrz[0]*ps1**4 + 6*yrz[2]*ps1**2 + zv2 = zv2+12j*yrz[1]*ps1*ps2+6j*yrz[0]*ps1**2*ps2+6j*yrz[2]*ps2 + zv2 = zv2-3*yrz[0]*ps2*ps2+4*yrz[3]*ps1-4*yrz[1]*ps3-4*yrz[0]*ps1*ps3 + zv2 = zv2+yrz[4]-j*yrz[0]*ps4 + zv = exptheta*zv1+zv2/exptheta + ctx.prec = wpinitial + return zv + +@defun +def rs_zeta(ctx, s, derivative=0, **kwargs): + if derivative > 4: + raise NotImplementedError + s = ctx.convert(s) + re = ctx._re(s); im = ctx._im(s) + if im < 0: + z = ctx.conj(ctx.rs_zeta(ctx.conj(s), derivative)) + return z + critical_line = (re == 0.5) + if critical_line: + return zeta_half(ctx, s, derivative) + else: + return zeta_offline(ctx, s, derivative) + +@defun +def rs_z(ctx, w, derivative=0): + w = ctx.convert(w) + re = ctx._re(w); im = ctx._im(w) + if re < 0: + return rs_z(ctx, -w, derivative) + critical_line = (im == 0) + if critical_line : + return z_half(ctx, w, derivative) + else: + return z_offline(ctx, w, derivative) + diff --git a/compiler/gdsMill/mpmath/functions/zeta.py b/compiler/gdsMill/mpmath/functions/zeta.py new file mode 100644 index 00000000..2a4f307b --- /dev/null +++ b/compiler/gdsMill/mpmath/functions/zeta.py @@ -0,0 +1,693 @@ +from functions import defun, defun_wrapped, defun_static + +@defun +def stieltjes(ctx, n, a=1): + n = ctx.convert(n) + a = ctx.convert(a) + if n < 0: + return ctx.bad_domain("Stieltjes constants defined for n >= 0") + if hasattr(ctx, "stieltjes_cache"): + stieltjes_cache = ctx.stieltjes_cache + else: + stieltjes_cache = ctx.stieltjes_cache = {} + if a == 1: + if n == 0: + return +ctx.euler + if n in stieltjes_cache: + prec, s = stieltjes_cache[n] + if prec >= ctx.prec: + return +s + mag = 1 + def f(x): + xa = x/a + v = (xa-ctx.j)*ctx.ln(a-ctx.j*x)**n/(1+xa**2)/(ctx.exp(2*ctx.pi*x)-1) + return ctx._re(v) / mag + orig = ctx.prec + try: + # Normalize integrand by approx. magnitude to + # speed up quadrature (which uses absolute error) + if n > 50: + ctx.prec = 20 + mag = ctx.quad(f, [0,ctx.inf], maxdegree=3) + ctx.prec = orig + 10 + int(n**0.5) + s = ctx.quad(f, [0,ctx.inf], maxdegree=20) + v = ctx.ln(a)**n/(2*a) - ctx.ln(a)**(n+1)/(n+1) + 2*s/a*mag + finally: + ctx.prec = orig + if a == 1 and ctx.isint(n): + stieltjes_cache[n] = (ctx.prec, v) + return +v + +@defun_wrapped +def siegeltheta(ctx, t): + if ctx._im(t): + # XXX: cancellation occurs + a = ctx.loggamma(0.25+0.5j*t) + b = ctx.loggamma(0.25-0.5j*t) + return -ctx.ln(ctx.pi)/2*t - 0.5j*(a-b) + else: + if ctx.isinf(t): + return t + return ctx._im(ctx.loggamma(0.25+0.5j*t)) - ctx.ln(ctx.pi)/2*t + +@defun_wrapped +def grampoint(ctx, n): + # asymptotic expansion, from + # http://mathworld.wolfram.com/GramPoint.html + g = 2*ctx.pi*ctx.exp(1+ctx.lambertw((8*n+1)/(8*ctx.e))) + return ctx.findroot(lambda t: ctx.siegeltheta(t)-ctx.pi*n, g) + +@defun_wrapped +def siegelz(ctx, t): + v = ctx.expj(ctx.siegeltheta(t))*ctx.zeta(0.5+ctx.j*t) + if ctx._is_real_type(t): + return ctx._re(v) + return v + +_zeta_zeros = [ +14.134725142,21.022039639,25.010857580,30.424876126,32.935061588, +37.586178159,40.918719012,43.327073281,48.005150881,49.773832478, +52.970321478,56.446247697,59.347044003,60.831778525,65.112544048, +67.079810529,69.546401711,72.067157674,75.704690699,77.144840069, +79.337375020,82.910380854,84.735492981,87.425274613,88.809111208, +92.491899271,94.651344041,95.870634228,98.831194218,101.317851006, +103.725538040,105.446623052,107.168611184,111.029535543,111.874659177, +114.320220915,116.226680321,118.790782866,121.370125002,122.946829294, +124.256818554,127.516683880,129.578704200,131.087688531,133.497737203, +134.756509753,138.116042055,139.736208952,141.123707404,143.111845808, +146.000982487,147.422765343,150.053520421,150.925257612,153.024693811, +156.112909294,157.597591818,158.849988171,161.188964138,163.030709687, +165.537069188,167.184439978,169.094515416,169.911976479,173.411536520, +174.754191523,176.441434298,178.377407776,179.916484020,182.207078484, +184.874467848,185.598783678,187.228922584,189.416158656,192.026656361, +193.079726604,195.265396680,196.876481841,198.015309676,201.264751944, +202.493594514,204.189671803,205.394697202,207.906258888,209.576509717, +211.690862595,213.347919360,214.547044783,216.169538508,219.067596349, +220.714918839,221.430705555,224.007000255,224.983324670,227.421444280, +229.337413306,231.250188700,231.987235253,233.693404179,236.524229666, +] + +def _load_zeta_zeros(url): + import urllib + d = urllib.urlopen(url) + L = [float(x) for x in d.readlines()] + # Sanity check + assert round(L[0]) == 14 + _zeta_zeros[:] = L + +@defun +def zetazero(ctx, n, url='http://www.dtc.umn.edu/~odlyzko/zeta_tables/zeros1'): + n = int(n) + if n < 0: + return ctx.zetazero(-n).conjugate() + if n == 0: + raise ValueError("n must be nonzero") + if n > len(_zeta_zeros) and n <= 100000: + _load_zeta_zeros(url) + if n > len(_zeta_zeros): + raise NotImplementedError("n too large for zetazeros") + return ctx.mpc(0.5, ctx.findroot(ctx.siegelz, _zeta_zeros[n-1])) + +@defun_wrapped +def riemannr(ctx, x): + if x == 0: + return ctx.zero + # Check if a simple asymptotic estimate is accurate enough + if abs(x) > 1000: + a = ctx.li(x) + b = 0.5*ctx.li(ctx.sqrt(x)) + if abs(b) < abs(a)*ctx.eps: + return a + if abs(x) < 0.01: + # XXX + ctx.prec += int(-ctx.log(abs(x),2)) + # Sum Gram's series + s = t = ctx.one + u = ctx.ln(x) + k = 1 + while abs(t) > abs(s)*ctx.eps: + t = t * u / k + s += t / (k * ctx._zeta_int(k+1)) + k += 1 + return s + +@defun_static +def primepi(ctx, x): + x = int(x) + if x < 2: + return 0 + return len(ctx.list_primes(x)) + +@defun_wrapped +def primepi2(ctx, x): + x = int(x) + if x < 2: + return ctx.mpi(0,0) + if x < 2657: + return ctx.mpi(ctx.primepi(x)) + mid = ctx.li(x) + # Schoenfeld's estimate for x >= 2657, assuming RH + err = ctx.sqrt(x,rounding='u')*ctx.ln(x,rounding='u')/8/ctx.pi(rounding='d') + a = ctx.floor((ctx.mpi(mid)-err).a, rounding='d') + b = ctx.ceil((ctx.mpi(mid)+err).b, rounding='u') + return ctx.mpi(a, b) + +@defun_wrapped +def primezeta(ctx, s): + if ctx.isnan(s): + return s + if ctx.re(s) <= 0: + raise ValueError("prime zeta function defined only for re(s) > 0") + if s == 1: + return ctx.inf + if s == 0.5: + return ctx.mpc(ctx.ninf, ctx.pi) + r = ctx.re(s) + if r > ctx.prec: + return 0.5**s + else: + wp = ctx.prec + int(r) + def terms(): + orig = ctx.prec + # zeta ~ 1+eps; need to set precision + # to get logarithm accurately + k = 0 + while 1: + k += 1 + u = ctx.moebius(k) + if not u: + continue + ctx.prec = wp + t = u*ctx.ln(ctx.zeta(k*s))/k + if not t: + return + #print ctx.prec, ctx.nstr(t) + ctx.prec = orig + yield t + return ctx.sum_accurately(terms) + +# TODO: for bernpoly and eulerpoly, ensure that all exact zeros are covered + +@defun_wrapped +def bernpoly(ctx, n, z): + # Slow implementation: + #return sum(ctx.binomial(n,k)*ctx.bernoulli(k)*z**(n-k) for k in xrange(0,n+1)) + n = int(n) + if n < 0: + raise ValueError("Bernoulli polynomials only defined for n >= 0") + if z == 0 or (z == 1 and n > 1): + return ctx.bernoulli(n) + if z == 0.5: + return (ctx.ldexp(1,1-n)-1)*ctx.bernoulli(n) + if n <= 3: + if n == 0: return z ** 0 + if n == 1: return z - 0.5 + if n == 2: return (6*z*(z-1)+1)/6 + if n == 3: return z*(z*(z-1.5)+0.5) + if abs(z) == ctx.inf: + return z ** n + if z != z: + return z + if abs(z) > 2: + def terms(): + t = ctx.one + yield t + r = ctx.one/z + k = 1 + while k <= n: + t = t*(n+1-k)/k*r + if not (k > 2 and k & 1): + yield t*ctx.bernoulli(k) + k += 1 + return ctx.sum_accurately(terms) * z**n + else: + def terms(): + yield ctx.bernoulli(n) + t = ctx.one + k = 1 + while k <= n: + t = t*(n+1-k)/k * z + m = n-k + if not (m > 2 and m & 1): + yield t*ctx.bernoulli(m) + k += 1 + return ctx.sum_accurately(terms) + +@defun_wrapped +def eulerpoly(ctx, n, z): + n = int(n) + if n < 0: + raise ValueError("Euler polynomials only defined for n >= 0") + if n <= 2: + if n == 0: return z ** 0 + if n == 1: return z - 0.5 + if n == 2: return z*(z-1) + if abs(z) == ctx.inf: + return z**n + if z != z: + return z + m = n+1 + if z == 0: + return -2*(ctx.ldexp(1,m)-1)*ctx.bernoulli(m)/m * z**0 + if z == 1: + return 2*(ctx.ldexp(1,m)-1)*ctx.bernoulli(m)/m * z**0 + if z == 0.5: + if n % 2: + return ctx.zero + # Use exact code for Euler numbers + if n < 100 or n*ctx.mag(0.46839865*n) < ctx.prec*0.25: + return ctx.ldexp(ctx._eulernum(n), -n) + # http://functions.wolfram.com/Polynomials/EulerE2/06/01/02/01/0002/ + def terms(): + t = ctx.one + k = 0 + w = ctx.ldexp(1,n+2) + while 1: + v = n-k+1 + if not (v > 2 and v & 1): + yield (2-w)*ctx.bernoulli(v)*t + k += 1 + if k > n: + break + t = t*z*(n-k+2)/k + w *= 0.5 + return ctx.sum_accurately(terms) / m + +@defun +def eulernum(ctx, n, exact=False): + n = int(n) + if exact: + return int(ctx._eulernum(n)) + if n < 100: + return ctx.mpf(ctx._eulernum(n)) + if n % 2: + return ctx.zero + return ctx.ldexp(ctx.eulerpoly(n,0.5), n) + +# TODO: this should be implemented low-level +def polylog_series(ctx, s, z): + tol = +ctx.eps + l = ctx.zero + k = 1 + zk = z + while 1: + term = zk / k**s + l += term + if abs(term) < tol: + break + zk *= z + k += 1 + return l + +def polylog_continuation(ctx, n, z): + if n < 0: + return z*0 + twopij = 2j * ctx.pi + a = -twopij**n/ctx.fac(n) * ctx.bernpoly(n, ctx.ln(z)/twopij) + if ctx._is_real_type(z) and z < 0: + a = ctx._re(a) + if ctx._im(z) < 0 or (ctx._im(z) == 0 and ctx._re(z) >= 1): + a -= twopij*ctx.ln(z)**(n-1)/ctx.fac(n-1) + return a + +def polylog_unitcircle(ctx, n, z): + tol = +ctx.eps + if n > 1: + l = ctx.zero + logz = ctx.ln(z) + logmz = ctx.one + m = 0 + while 1: + if (n-m) != 1: + term = ctx.zeta(n-m) * logmz / ctx.fac(m) + if term and abs(term) < tol: + break + l += term + logmz *= logz + m += 1 + l += ctx.ln(z)**(n-1)/ctx.fac(n-1)*(ctx.harmonic(n-1)-ctx.ln(-ctx.ln(z))) + elif n < 1: # else + l = ctx.fac(-n)*(-ctx.ln(z))**(n-1) + logz = ctx.ln(z) + logkz = ctx.one + k = 0 + while 1: + b = ctx.bernoulli(k-n+1) + if b: + term = b*logkz/(ctx.fac(k)*(k-n+1)) + if abs(term) < tol: + break + l -= term + logkz *= logz + k += 1 + else: + raise ValueError + if ctx._is_real_type(z) and z < 0: + l = ctx._re(l) + return l + +def polylog_general(ctx, s, z): + v = ctx.zero + u = ctx.ln(z) + if not abs(u) < 5: # theoretically |u| < 2*pi + raise NotImplementedError("polylog for arbitrary s and z") + t = 1 + k = 0 + while 1: + term = ctx.zeta(s-k) * t + if abs(term) < ctx.eps: + break + v += term + k += 1 + t *= u + t /= k + return ctx.gamma(1-s)*(-u)**(s-1) + v + +@defun_wrapped +def polylog(ctx, s, z): + s = ctx.convert(s) + z = ctx.convert(z) + if z == 1: + return ctx.zeta(s) + if z == -1: + return -ctx.altzeta(s) + if s == 0: + return z/(1-z) + if s == 1: + return -ctx.ln(1-z) + if s == -1: + return z/(1-z)**2 + if abs(z) <= 0.75 or (not ctx.isint(s) and abs(z) < 0.9): + return polylog_series(ctx, s, z) + if abs(z) >= 1.4 and ctx.isint(s): + return (-1)**(s+1)*polylog_series(ctx, s, 1/z) + polylog_continuation(ctx, s, z) + if ctx.isint(s): + return polylog_unitcircle(ctx, int(s), z) + return polylog_general(ctx, s, z) + + #raise NotImplementedError("polylog for arbitrary s and z") + # This could perhaps be used in some cases + #from quadrature import quad + #return quad(lambda t: t**(s-1)/(exp(t)/z-1),[0,inf])/gamma(s) + +@defun_wrapped +def clsin(ctx, s, z, pi=False): + if ctx.isint(s) and s < 0 and int(s) % 2 == 1: + return z*0 + if pi: + a = ctx.expjpi(z) + else: + a = ctx.expj(z) + if ctx._is_real_type(z) and ctx._is_real_type(s): + return ctx.im(ctx.polylog(s,a)) + b = 1/a + return (-0.5j)*(ctx.polylog(s,a) - ctx.polylog(s,b)) + +@defun_wrapped +def clcos(ctx, s, z, pi=False): + if ctx.isint(s) and s < 0 and int(s) % 2 == 0: + return z*0 + if pi: + a = ctx.expjpi(z) + else: + a = ctx.expj(z) + if ctx._is_real_type(z) and ctx._is_real_type(s): + return ctx.re(ctx.polylog(s,a)) + b = 1/a + return 0.5*(ctx.polylog(s,a) + ctx.polylog(s,b)) + +@defun +def altzeta(ctx, s, **kwargs): + try: + return ctx._altzeta(s, **kwargs) + except NotImplementedError: + return ctx._altzeta_generic(s) + +@defun_wrapped +def _altzeta_generic(ctx, s): + if s == 1: + return ctx.ln2 + 0*s + return -ctx.powm1(2, 1-s) * ctx.zeta(s) + +@defun +def zeta(ctx, s, a=1, derivative=0, method=None, **kwargs): + d = int(derivative) + if a == 1 and not (d or method): + try: + return ctx._zeta(s, **kwargs) + except NotImplementedError: + pass + s = ctx.convert(s) + prec = ctx.prec + method = kwargs.get('method') + verbose = kwargs.get('verbose') + if a == 1 and method != 'euler-maclaurin': + im = abs(ctx._im(s)) + re = abs(ctx._re(s)) + #if (im < prec or method == 'borwein') and not derivative: + # try: + # if verbose: + # print "zeta: Attempting to use the Borwein algorithm" + # return ctx._zeta(s, **kwargs) + # except NotImplementedError: + # if verbose: + # print "zeta: Could not use the Borwein algorithm" + # pass + if abs(im) > 60*prec and 10*re < prec and derivative <= 4 or \ + method == 'riemann-siegel': + try: # py2.4 compatible try block + try: + if verbose: + print "zeta: Attempting to use the Riemann-Siegel algorithm" + return ctx.rs_zeta(s, derivative, **kwargs) + except NotImplementedError: + if verbose: + print "zeta: Could not use the Riemann-Siegel algorithm" + pass + finally: + ctx.prec = prec + if s == 1: + return ctx.inf + abss = abs(s) + if abss == ctx.inf: + if ctx.re(s) == ctx.inf: + if d == 0: + return ctx.one + return ctx.zero + return s*0 + elif ctx.isnan(abss): + return 1/s + if ctx.re(s) > 2*ctx.prec and a == 1 and not derivative: + return ctx.one + ctx.power(2, -s) + if verbose: + print "zeta: Using the Euler-Maclaurin algorithm" + prec = ctx.prec + try: + ctx.prec += 10 + v = ctx._hurwitz(s, a, d) + finally: + ctx.prec = prec + return +v + +@defun +def _hurwitz(ctx, s, a=1, d=0): + # We strongly want to special-case rational a + a, atype = ctx._convert_param(a) + prec = ctx.prec + # TODO: implement reflection for derivatives + res = ctx.re(s) + negs = -s + try: + if res < 0 and not d: + # Integer reflection formula + if ctx.isnpint(s): + n = int(res) + if n <= 0: + return ctx.bernpoly(1-n, a) / (n-1) + t = 1-s + # We now require a to be standardized + v = 0 + shift = 0 + b = a + while ctx.re(b) > 1: + b -= 1 + v -= b**negs + shift -= 1 + while ctx.re(b) <= 0: + v += b**negs + b += 1 + shift += 1 + # Rational reflection formula + if atype == 'Q' or atype == 'Z': + try: + p, q = a + except: + assert a == int(a) + p = int(a) + q = 1 + p += shift*q + assert 1 <= p <= q + g = ctx.fsum(ctx.cospi(t/2-2*k*b)*ctx._hurwitz(t,(k,q)) \ + for k in range(1,q+1)) + g *= 2*ctx.gamma(t)/(2*ctx.pi*q)**t + v += g + return v + # General reflection formula + else: + C1 = ctx.cospi(t/2) + C2 = ctx.sinpi(t/2) + # Clausen functions; could maybe use polylog directly + if C1: C1 *= ctx.clcos(t, 2*a, pi=True) + if C2: C2 *= ctx.clsin(t, 2*a, pi=True) + v += 2*ctx.gamma(t)/(2*ctx.pi)**t*(C1+C2) + return v + except NotImplementedError: + pass + a = ctx.convert(a) + tol = -prec + # Estimate number of terms for Euler-Maclaurin summation; could be improved + M1 = 0 + M2 = prec // 3 + N = M2 + lsum = 0 + # This speeds up the recurrence for derivatives + if ctx.isint(s): + s = int(ctx._re(s)) + s1 = s-1 + while 1: + # Truncated L-series + l = ctx._zetasum(s, M1+a, M2-M1-1, [d])[0][0] + #if d: + # l = ctx.fsum((-ctx.ln(n+a))**d * (n+a)**negs for n in range(M1,M2)) + #else: + # l = ctx.fsum((n+a)**negs for n in range(M1,M2)) + lsum += l + M2a = M2+a + logM2a = ctx.ln(M2a) + logM2ad = logM2a**d + logs = [logM2ad] + logr = 1/logM2a + rM2a = 1/M2a + M2as = rM2a**s + if d: + tailsum = ctx.gammainc(d+1, s1*logM2a) / s1**(d+1) + else: + tailsum = 1/((s1)*(M2a)**s1) + tailsum += 0.5 * logM2ad * M2as + U = [1] + r = M2as + fact = 2 + for j in range(1, N+1): + # TODO: the following could perhaps be tidied a bit + j2 = 2*j + if j == 1: + upds = [1] + else: + upds = [j2-2, j2-1] + for m in upds: + D = min(m,d+1) + if m <= d: + logs.append(logs[-1] * logr) + Un = [0]*(D+1) + for i in xrange(D): Un[i] = (1-m-s)*U[i] + for i in xrange(1,D+1): Un[i] += (d-(i-1))*U[i-1] + U = Un + r *= rM2a + t = ctx.fdot(U, logs) * r * ctx.bernoulli(j2)/(-fact) + tailsum += t + if ctx.mag(t) < tol: + return lsum + (-1)**d * tailsum + fact *= (j2+1)*(j2+2) + M1, M2 = M2, M2*2 + +@defun +def _zetasum(ctx, s, a, n, derivatives=[0], reflect=False): + """ + Returns [xd0,xd1,...,xdr], [yd0,yd1,...ydr] where + + xdk = D^k ( 1/a^s + 1/(a+1)^s + ... + 1/(a+n)^s ) + ydk = D^k conj( 1/a^(1-s) + 1/(a+1)^(1-s) + ... + 1/(a+n)^(1-s) ) + + D^k = kth derivative with respect to s, k ranges over the given list of + derivatives (which should consist of either a single element + or a range 0,1,...r). If reflect=False, the ydks are not computed. + """ + try: + return ctx._zetasum_fast(s, a, n, derivatives, reflect) + except NotImplementedError: + pass + negs = ctx.fneg(s, exact=True) + have_derivatives = derivatives != [0] + have_one_derivative = len(derivatives) == 1 + if not reflect: + if not have_derivatives: + return [ctx.fsum((a+k)**negs for k in xrange(n+1))], [] + if have_one_derivative: + d = derivatives[0] + x = ctx.fsum(ctx.ln(a+k)**d * (a+k)**negs for k in xrange(n+1)) + return [(-1)**d * x], [] + maxd = max(derivatives) + if not have_one_derivative: + derivatives = range(maxd+1) + xs = [ctx.zero for d in derivatives] + if reflect: + ys = [ctx.zero for d in derivatives] + else: + ys = [] + for k in xrange(n+1): + w = a + k + xterm = w ** negs + if reflect: + yterm = ctx.conj(ctx.one / (w * xterm)) + if have_derivatives: + logw = -ctx.ln(w) + if have_one_derivative: + logw = logw ** maxd + xs[0] += xterm * logw + if reflect: + ys[0] += yterm * logw + else: + t = ctx.one + for d in derivatives: + xs[d] += xterm * t + if reflect: + ys[d] += yterm * t + t *= logw + else: + xs[0] += xterm + if reflect: + ys[0] += yterm + return xs, ys + +@defun +def dirichlet(ctx, s, chi=[1], derivative=0): + s = ctx.convert(s) + q = len(chi) + d = int(derivative) + if d > 2: + raise NotImplementedError("arbitrary order derivatives") + prec = ctx.prec + try: + ctx.prec += 10 + if s == 1: + have_pole = True + for x in chi: + if x and x != 1: + have_pole = False + h = +ctx.eps + ctx.prec *= 2*(d+1) + s += h + if have_pole: + return +ctx.inf + z = ctx.zero + for p in range(1,q+1): + if chi[p%q]: + if d == 1: + z += chi[p%q] * (ctx.zeta(s, (p,q), 1) - \ + ctx.zeta(s, (p,q))*ctx.log(q)) + else: + z += chi[p%q] * ctx.zeta(s, (p,q)) + z /= q**s + finally: + ctx.prec = prec + return +z diff --git a/compiler/gdsMill/mpmath/identification.py b/compiler/gdsMill/mpmath/identification.py new file mode 100644 index 00000000..cd6c2ce3 --- /dev/null +++ b/compiler/gdsMill/mpmath/identification.py @@ -0,0 +1,840 @@ +""" +Implements the PSLQ algorithm for integer relation detection, +and derivative algorithms for constant recognition. +""" + +from libmp import int_types, sqrt_fixed + +# round to nearest integer (can be done more elegantly...) +def round_fixed(x, prec): + return ((x + (1<<(prec-1))) >> prec) << prec + +class IdentificationMethods(object): + pass + + +def pslq(ctx, x, tol=None, maxcoeff=1000, maxsteps=100, verbose=False): + r""" + Given a vector of real numbers `x = [x_0, x_1, ..., x_n]`, ``pslq(x)`` + uses the PSLQ algorithm to find a list of integers + `[c_0, c_1, ..., c_n]` such that + + .. math :: + + |c_1 x_1 + c_2 x_2 + ... + c_n x_n| < \mathrm{tol} + + and such that `\max |c_k| < \mathrm{maxcoeff}`. If no such vector + exists, :func:`pslq` returns ``None``. The tolerance defaults to + 3/4 of the working precision. + + **Examples** + + Find rational approximations for `\pi`:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> pslq([-1, pi], tol=0.01) + [22, 7] + >>> pslq([-1, pi], tol=0.001) + [355, 113] + >>> mpf(22)/7; mpf(355)/113; +pi + 3.14285714285714 + 3.14159292035398 + 3.14159265358979 + + Pi is not a rational number with denominator less than 1000:: + + >>> pslq([-1, pi]) + >>> + + To within the standard precision, it can however be approximated + by at least one rational number with denominator less than `10^{12}`:: + + >>> p, q = pslq([-1, pi], maxcoeff=10**12) + >>> print p, q + 238410049439 75888275702 + >>> mpf(p)/q + 3.14159265358979 + + The PSLQ algorithm can be applied to long vectors. For example, + we can investigate the rational (in)dependence of integer square + roots:: + + >>> mp.dps = 30 + >>> pslq([sqrt(n) for n in range(2, 5+1)]) + >>> + >>> pslq([sqrt(n) for n in range(2, 6+1)]) + >>> + >>> pslq([sqrt(n) for n in range(2, 8+1)]) + [2, 0, 0, 0, 0, 0, -1] + + **Machin formulas** + + A famous formula for `\pi` is Machin's, + + .. math :: + + \frac{\pi}{4} = 4 \operatorname{acot} 5 - \operatorname{acot} 239 + + There are actually infinitely many formulas of this type. Two + others are + + .. math :: + + \frac{\pi}{4} = \operatorname{acot} 1 + + \frac{\pi}{4} = 12 \operatorname{acot} 49 + 32 \operatorname{acot} 57 + + 5 \operatorname{acot} 239 + 12 \operatorname{acot} 110443 + + We can easily verify the formulas using the PSLQ algorithm:: + + >>> mp.dps = 30 + >>> pslq([pi/4, acot(1)]) + [1, -1] + >>> pslq([pi/4, acot(5), acot(239)]) + [1, -4, 1] + >>> pslq([pi/4, acot(49), acot(57), acot(239), acot(110443)]) + [1, -12, -32, 5, -12] + + We could try to generate a custom Machin-like formula by running + the PSLQ algorithm with a few inverse cotangent values, for example + acot(2), acot(3) ... acot(10). Unfortunately, there is a linear + dependence among these values, resulting in only that dependence + being detected, with a zero coefficient for `\pi`:: + + >>> pslq([pi] + [acot(n) for n in range(2,11)]) + [0, 1, -1, 0, 0, 0, -1, 0, 0, 0] + + We get better luck by removing linearly dependent terms:: + + >>> pslq([pi] + [acot(n) for n in range(2,11) if n not in (3, 5)]) + [1, -8, 0, 0, 4, 0, 0, 0] + + In other words, we found the following formula:: + + >>> 8*acot(2) - 4*acot(7) + 3.14159265358979323846264338328 + >>> +pi + 3.14159265358979323846264338328 + + **Algorithm** + + This is a fairly direct translation to Python of the pseudocode given by + David Bailey, "The PSLQ Integer Relation Algorithm": + http://www.cecm.sfu.ca/organics/papers/bailey/paper/html/node3.html + + The present implementation uses fixed-point instead of floating-point + arithmetic, since this is significantly (about 7x) faster. + """ + + n = len(x) + assert n >= 2 + + # At too low precision, the algorithm becomes meaningless + prec = ctx.prec + assert prec >= 53 + + if verbose and prec // max(2,n) < 5: + print "Warning: precision for PSLQ may be too low" + + target = int(prec * 0.75) + + if tol is None: + tol = ctx.mpf(2)**(-target) + else: + tol = ctx.convert(tol) + + extra = 60 + prec += extra + + if verbose: + print "PSLQ using prec %i and tol %s" % (prec, ctx.nstr(tol)) + + tol = ctx.to_fixed(tol, prec) + assert tol + + # Convert to fixed-point numbers. The dummy None is added so we can + # use 1-based indexing. (This just allows us to be consistent with + # Bailey's indexing. The algorithm is 100 lines long, so debugging + # a single wrong index can be painful.) + x = [None] + [ctx.to_fixed(ctx.mpf(xk), prec) for xk in x] + + # Sanity check on magnitudes + minx = min(abs(xx) for xx in x[1:]) + if not minx: + raise ValueError("PSLQ requires a vector of nonzero numbers") + if minx < tol//100: + if verbose: + print "STOPPING: (one number is too small)" + return None + + g = sqrt_fixed((4<> prec) + s[k] = sqrt_fixed(t, prec) + t = s[1] + y = x[:] + for k in xrange(1, n+1): + y[k] = (x[k] << prec) // t + s[k] = (s[k] << prec) // t + # step 3 + for i in xrange(1, n+1): + for j in xrange(i+1, n): + H[i,j] = 0 + if i <= n-1: + if s[i]: + H[i,i] = (s[i+1] << prec) // s[i] + else: + H[i,i] = 0 + for j in range(1, i): + sjj1 = s[j]*s[j+1] + if sjj1: + H[i,j] = ((-y[i]*y[j])<> prec) + for k in xrange(1, j+1): + H[i,k] = H[i,k] - (t*H[j,k] >> prec) + for k in xrange(1, n+1): + A[i,k] = A[i,k] - (t*A[j,k] >> prec) + B[k,j] = B[k,j] + (t*B[k,i] >> prec) + # Main algorithm + for REP in range(maxsteps): + # Step 1 + m = -1 + szmax = -1 + for i in range(1, n): + h = H[i,i] + sz = (g**i * abs(h)) >> (prec*(i-1)) + if sz > szmax: + m = i + szmax = sz + # Step 2 + y[m], y[m+1] = y[m+1], y[m] + tmp = {} + for i in xrange(1,n+1): H[m,i], H[m+1,i] = H[m+1,i], H[m,i] + for i in xrange(1,n+1): A[m,i], A[m+1,i] = A[m+1,i], A[m,i] + for i in xrange(1,n+1): B[i,m], B[i,m+1] = B[i,m+1], B[i,m] + # Step 3 + if m <= n - 2: + t0 = sqrt_fixed((H[m,m]**2 + H[m,m+1]**2)>>prec, prec) + # A zero element probably indicates that the precision has + # been exhausted. XXX: this could be spurious, due to + # using fixed-point arithmetic + if not t0: + break + t1 = (H[m,m] << prec) // t0 + t2 = (H[m,m+1] << prec) // t0 + for i in xrange(m, n+1): + t3 = H[i,m] + t4 = H[i,m+1] + H[i,m] = (t1*t3+t2*t4) >> prec + H[i,m+1] = (-t2*t3+t1*t4) >> prec + # Step 4 + for i in xrange(m+1, n+1): + for j in xrange(min(i-1, m+1), 0, -1): + try: + t = round_fixed((H[i,j] << prec)//H[j,j], prec) + # Precision probably exhausted + except ZeroDivisionError: + break + y[j] = y[j] + ((t*y[i]) >> prec) + for k in xrange(1, j+1): + H[i,k] = H[i,k] - (t*H[j,k] >> prec) + for k in xrange(1, n+1): + A[i,k] = A[i,k] - (t*A[j,k] >> prec) + B[k,j] = B[k,j] + (t*B[k,i] >> prec) + # Until a relation is found, the error typically decreases + # slowly (e.g. a factor 1-10) with each step TODO: we could + # compare err from two successive iterations. If there is a + # large drop (several orders of magnitude), that indicates a + # "high quality" relation was detected. Reporting this to + # the user somehow might be useful. + best_err = maxcoeff<> prec) for j in \ + range(1,n+1)] + if max(abs(v) for v in vec) < maxcoeff: + if verbose: + print "FOUND relation at iter %i/%i, error: %s" % \ + (REP, maxsteps, ctx.nstr(err / ctx.mpf(2)**prec, 1)) + return vec + best_err = min(err, best_err) + # Calculate a lower bound for the norm. We could do this + # more exactly (using the Euclidean norm) but there is probably + # no practical benefit. + recnorm = max(abs(h) for h in H.values()) + if recnorm: + norm = ((1 << (2*prec)) // recnorm) >> prec + norm //= 100 + else: + norm = ctx.inf + if verbose: + print "%i/%i: Error: %8s Norm: %s" % \ + (REP, maxsteps, ctx.nstr(best_err / ctx.mpf(2)**prec, 1), norm) + if norm >= maxcoeff: + break + if verbose: + print "CANCELLING after step %i/%i." % (REP, maxsteps) + print "Could not find an integer relation. Norm bound: %s" % norm + return None + +def findpoly(ctx, x, n=1, **kwargs): + r""" + ``findpoly(x, n)`` returns the coefficients of an integer + polynomial `P` of degree at most `n` such that `P(x) \approx 0`. + If no polynomial having `x` as a root can be found, + :func:`findpoly` returns ``None``. + + :func:`findpoly` works by successively calling :func:`pslq` with + the vectors `[1, x]`, `[1, x, x^2]`, `[1, x, x^2, x^3]`, ..., + `[1, x, x^2, .., x^n]` as input. Keyword arguments given to + :func:`findpoly` are forwarded verbatim to :func:`pslq`. In + particular, you can specify a tolerance for `P(x)` with ``tol`` + and a maximum permitted coefficient size with ``maxcoeff``. + + For large values of `n`, it is recommended to run :func:`findpoly` + at high precision; preferably 50 digits or more. + + **Examples** + + By default (degree `n = 1`), :func:`findpoly` simply finds a linear + polynomial with a rational root:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> findpoly(0.7) + [-10, 7] + + The generated coefficient list is valid input to ``polyval`` and + ``polyroots``:: + + >>> nprint(polyval(findpoly(phi, 2), phi), 1) + -2.0e-16 + >>> for r in polyroots(findpoly(phi, 2)): + ... print r + ... + -0.618033988749895 + 1.61803398874989 + + Numbers of the form `m + n \sqrt p` for integers `(m, n, p)` are + solutions to quadratic equations. As we find here, `1+\sqrt 2` + is a root of the polynomial `x^2 - 2x - 1`:: + + >>> findpoly(1+sqrt(2), 2) + [1, -2, -1] + >>> findroot(lambda x: x**2 - 2*x - 1, 1) + 2.4142135623731 + + Despite only containing square roots, the following number results + in a polynomial of degree 4:: + + >>> findpoly(sqrt(2)+sqrt(3), 4) + [1, 0, -10, 0, 1] + + In fact, `x^4 - 10x^2 + 1` is the *minimal polynomial* of + `r = \sqrt 2 + \sqrt 3`, meaning that a rational polynomial of + lower degree having `r` as a root does not exist. Given sufficient + precision, :func:`findpoly` will usually find the correct + minimal polynomial of a given algebraic number. + + **Non-algebraic numbers** + + If :func:`findpoly` fails to find a polynomial with given + coefficient size and tolerance constraints, that means no such + polynomial exists. + + We can verify that `\pi` is not an algebraic number of degree 3 with + coefficients less than 1000:: + + >>> mp.dps = 15 + >>> findpoly(pi, 3) + >>> + + It is always possible to find an algebraic approximation of a number + using one (or several) of the following methods: + + 1. Increasing the permitted degree + 2. Allowing larger coefficients + 3. Reducing the tolerance + + One example of each method is shown below:: + + >>> mp.dps = 15 + >>> findpoly(pi, 4) + [95, -545, 863, -183, -298] + >>> findpoly(pi, 3, maxcoeff=10000) + [836, -1734, -2658, -457] + >>> findpoly(pi, 3, tol=1e-7) + [-4, 22, -29, -2] + + It is unknown whether Euler's constant is transcendental (or even + irrational). We can use :func:`findpoly` to check that if is + an algebraic number, its minimal polynomial must have degree + at least 7 and a coefficient of magnitude at least 1000000:: + + >>> mp.dps = 200 + >>> findpoly(euler, 6, maxcoeff=10**6, tol=1e-100, maxsteps=1000) + >>> + + Note that the high precision and strict tolerance is necessary + for such high-degree runs, since otherwise unwanted low-accuracy + approximations will be detected. It may also be necessary to set + maxsteps high to prevent a premature exit (before the coefficient + bound has been reached). Running with ``verbose=True`` to get an + idea what is happening can be useful. + """ + x = ctx.mpf(x) + assert n >= 1 + if x == 0: + return [1, 0] + xs = [ctx.mpf(1)] + for i in range(1,n+1): + xs.append(x**i) + a = ctx.pslq(xs, **kwargs) + if a is not None: + return a[::-1] + +def fracgcd(p, q): + x, y = p, q + while y: + x, y = y, x % y + if x != 1: + p //= x + q //= x + if q == 1: + return p + return p, q + +def pslqstring(r, constants): + q = r[0] + r = r[1:] + s = [] + for i in range(len(r)): + p = r[i] + if p: + z = fracgcd(-p,q) + cs = constants[i][1] + if cs == '1': + cs = '' + else: + cs = '*' + cs + if isinstance(z, int_types): + if z > 0: term = str(z) + cs + else: term = ("(%s)" % z) + cs + else: + term = ("(%s/%s)" % z) + cs + s.append(term) + s = ' + '.join(s) + if '+' in s or '*' in s: + s = '(' + s + ')' + return s or '0' + +def prodstring(r, constants): + q = r[0] + r = r[1:] + num = [] + den = [] + for i in range(len(r)): + p = r[i] + if p: + z = fracgcd(-p,q) + cs = constants[i][1] + if isinstance(z, int_types): + if abs(z) == 1: t = cs + else: t = '%s**%s' % (cs, abs(z)) + ([num,den][z<0]).append(t) + else: + t = '%s**(%s/%s)' % (cs, abs(z[0]), z[1]) + ([num,den][z[0]<0]).append(t) + num = '*'.join(num) + den = '*'.join(den) + if num and den: return "(%s)/(%s)" % (num, den) + if num: return num + if den: return "1/(%s)" % den + +def quadraticstring(ctx,t,a,b,c): + if c < 0: + a,b,c = -a,-b,-c + u1 = (-b+ctx.sqrt(b**2-4*a*c))/(2*c) + u2 = (-b-ctx.sqrt(b**2-4*a*c))/(2*c) + if abs(u1-t) < abs(u2-t): + if b: s = '((%s+sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c) + else: s = '(sqrt(%s)/%s)' % (-4*a*c,2*c) + else: + if b: s = '((%s-sqrt(%s))/%s)' % (-b,b**2-4*a*c,2*c) + else: s = '(-sqrt(%s)/%s)' % (-4*a*c,2*c) + return s + +# Transformation y = f(x,c), with inverse function x = f(y,c) +# The third entry indicates whether the transformation is +# redundant when c = 1 +transforms = [ + (lambda ctx,x,c: x*c, '$y/$c', 0), + (lambda ctx,x,c: x/c, '$c*$y', 1), + (lambda ctx,x,c: c/x, '$c/$y', 0), + (lambda ctx,x,c: (x*c)**2, 'sqrt($y)/$c', 0), + (lambda ctx,x,c: (x/c)**2, '$c*sqrt($y)', 1), + (lambda ctx,x,c: (c/x)**2, '$c/sqrt($y)', 0), + (lambda ctx,x,c: c*x**2, 'sqrt($y)/sqrt($c)', 1), + (lambda ctx,x,c: x**2/c, 'sqrt($c)*sqrt($y)', 1), + (lambda ctx,x,c: c/x**2, 'sqrt($c)/sqrt($y)', 1), + (lambda ctx,x,c: ctx.sqrt(x*c), '$y**2/$c', 0), + (lambda ctx,x,c: ctx.sqrt(x/c), '$c*$y**2', 1), + (lambda ctx,x,c: ctx.sqrt(c/x), '$c/$y**2', 0), + (lambda ctx,x,c: c*ctx.sqrt(x), '$y**2/$c**2', 1), + (lambda ctx,x,c: ctx.sqrt(x)/c, '$c**2*$y**2', 1), + (lambda ctx,x,c: c/ctx.sqrt(x), '$c**2/$y**2', 1), + (lambda ctx,x,c: ctx.exp(x*c), 'log($y)/$c', 0), + (lambda ctx,x,c: ctx.exp(x/c), '$c*log($y)', 1), + (lambda ctx,x,c: ctx.exp(c/x), '$c/log($y)', 0), + (lambda ctx,x,c: c*ctx.exp(x), 'log($y/$c)', 1), + (lambda ctx,x,c: ctx.exp(x)/c, 'log($c*$y)', 1), + (lambda ctx,x,c: c/ctx.exp(x), 'log($c/$y)', 0), + (lambda ctx,x,c: ctx.ln(x*c), 'exp($y)/$c', 0), + (lambda ctx,x,c: ctx.ln(x/c), '$c*exp($y)', 1), + (lambda ctx,x,c: ctx.ln(c/x), '$c/exp($y)', 0), + (lambda ctx,x,c: c*ctx.ln(x), 'exp($y/$c)', 1), + (lambda ctx,x,c: ctx.ln(x)/c, 'exp($c*$y)', 1), + (lambda ctx,x,c: c/ctx.ln(x), 'exp($c/$y)', 0), +] + +def identify(ctx, x, constants=[], tol=None, maxcoeff=1000, full=False, + verbose=False): + """ + Given a real number `x`, ``identify(x)`` attempts to find an exact + formula for `x`. This formula is returned as a string. If no match + is found, ``None`` is returned. With ``full=True``, a list of + matching formulas is returned. + + As a simple example, :func:`identify` will find an algebraic + formula for the golden ratio:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> identify(phi) + '((1+sqrt(5))/2)' + + :func:`identify` can identify simple algebraic numbers and simple + combinations of given base constants, as well as certain basic + transformations thereof. More specifically, :func:`identify` + looks for the following: + + 1. Fractions + 2. Quadratic algebraic numbers + 3. Rational linear combinations of the base constants + 4. Any of the above after first transforming `x` into `f(x)` where + `f(x)` is `1/x`, `\sqrt x`, `x^2`, `\log x` or `\exp x`, either + directly or with `x` or `f(x)` multiplied or divided by one of + the base constants + 5. Products of fractional powers of the base constants and + small integers + + Base constants can be given as a list of strings representing mpmath + expressions (:func:`identify` will ``eval`` the strings to numerical + values and use the original strings for the output), or as a dict of + formula:value pairs. + + In order not to produce spurious results, :func:`identify` should + be used with high precision; preferrably 50 digits or more. + + **Examples** + + Simple identifications can be performed safely at standard + precision. Here the default recognition of rational, algebraic, + and exp/log of algebraic numbers is demonstrated:: + + >>> mp.dps = 15 + >>> identify(0.22222222222222222) + '(2/9)' + >>> identify(1.9662210973805663) + 'sqrt(((24+sqrt(48))/8))' + >>> identify(4.1132503787829275) + 'exp((sqrt(8)/2))' + >>> identify(0.881373587019543) + 'log(((2+sqrt(8))/2))' + + By default, :func:`identify` does not recognize `\pi`. At standard + precision it finds a not too useful approximation. At slightly + increased precision, this approximation is no longer accurate + enough and :func:`identify` more correctly returns ``None``:: + + >>> identify(pi) + '(2**(176/117)*3**(20/117)*5**(35/39))/(7**(92/117))' + >>> mp.dps = 30 + >>> identify(pi) + >>> + + Numbers such as `\pi`, and simple combinations of user-defined + constants, can be identified if they are provided explicitly:: + + >>> identify(3*pi-2*e, ['pi', 'e']) + '(3*pi + (-2)*e)' + + Here is an example using a dict of constants. Note that the + constants need not be "atomic"; :func:`identify` can just + as well express the given number in terms of expressions + given by formulas:: + + >>> identify(pi+e, {'a':pi+2, 'b':2*e}) + '((-2) + 1*a + (1/2)*b)' + + Next, we attempt some identifications with a set of base constants. + It is necessary to increase the precision a bit. + + >>> mp.dps = 50 + >>> base = ['sqrt(2)','pi','log(2)'] + >>> identify(0.25, base) + '(1/4)' + >>> identify(3*pi + 2*sqrt(2) + 5*log(2)/7, base) + '(2*sqrt(2) + 3*pi + (5/7)*log(2))' + >>> identify(exp(pi+2), base) + 'exp((2 + 1*pi))' + >>> identify(1/(3+sqrt(2)), base) + '((3/7) + (-1/7)*sqrt(2))' + >>> identify(sqrt(2)/(3*pi+4), base) + 'sqrt(2)/(4 + 3*pi)' + >>> identify(5**(mpf(1)/3)*pi*log(2)**2, base) + '5**(1/3)*pi*log(2)**2' + + An example of an erroneous solution being found when too low + precision is used:: + + >>> mp.dps = 15 + >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)']) + '((11/25) + (-158/75)*pi + (76/75)*e + (44/15)*sqrt(2))' + >>> mp.dps = 50 + >>> identify(1/(3*pi-4*e+sqrt(8)), ['pi', 'e', 'sqrt(2)']) + '1/(3*pi + (-4)*e + 2*sqrt(2))' + + **Finding approximate solutions** + + The tolerance ``tol`` defaults to 3/4 of the working precision. + Lowering the tolerance is useful for finding approximate matches. + We can for example try to generate approximations for pi:: + + >>> mp.dps = 15 + >>> identify(pi, tol=1e-2) + '(22/7)' + >>> identify(pi, tol=1e-3) + '(355/113)' + >>> identify(pi, tol=1e-10) + '(5**(339/269))/(2**(64/269)*3**(13/269)*7**(92/269))' + + With ``full=True``, and by supplying a few base constants, + ``identify`` can generate almost endless lists of approximations + for any number (the output below has been truncated to show only + the first few):: + + >>> for p in identify(pi, ['e', 'catalan'], tol=1e-5, full=True): + ... print p + ... # doctest: +ELLIPSIS + e/log((6 + (-4/3)*e)) + (3**3*5*e*catalan**2)/(2*7**2) + sqrt(((-13) + 1*e + 22*catalan)) + log(((-6) + 24*e + 4*catalan)/e) + exp(catalan*((-1/5) + (8/15)*e)) + catalan*(6 + (-6)*e + 15*catalan) + sqrt((5 + 26*e + (-3)*catalan))/e + e*sqrt(((-27) + 2*e + 25*catalan)) + log(((-1) + (-11)*e + 59*catalan)) + ((3/20) + (21/20)*e + (3/20)*catalan) + ... + + The numerical values are roughly as close to pi as permitted by the + specified tolerance: + + >>> e/log(6-4*e/3) + 3.14157719846001 + >>> 135*e*catalan**2/98 + 3.14166950419369 + >>> sqrt(e-13+22*catalan) + 3.14158000062992 + >>> log(24*e-6+4*catalan)-1 + 3.14158791577159 + + **Symbolic processing** + + The output formula can be evaluated as a Python expression. + Note however that if fractions (like '2/3') are present in + the formula, Python's :func:`eval()` may erroneously perform + integer division. Note also that the output is not necessarily + in the algebraically simplest form:: + + >>> identify(sqrt(2)) + '(sqrt(8)/2)' + + As a solution to both problems, consider using SymPy's + :func:`sympify` to convert the formula into a symbolic expression. + SymPy can be used to pretty-print or further simplify the formula + symbolically:: + + >>> from sympy import sympify + >>> sympify(identify(sqrt(2))) + 2**(1/2) + + Sometimes :func:`identify` can simplify an expression further than + a symbolic algorithm:: + + >>> from sympy import simplify + >>> x = sympify('-1/(-3/2+(1/2)*5**(1/2))*(3/2-1/2*5**(1/2))**(1/2)') + >>> x + (3/2 - 5**(1/2)/2)**(-1/2) + >>> x = simplify(x) + >>> x + 2/(6 - 2*5**(1/2))**(1/2) + >>> mp.dps = 30 + >>> x = sympify(identify(x.evalf(30))) + >>> x + 1/2 + 5**(1/2)/2 + + (In fact, this functionality is available directly in SymPy as the + function :func:`nsimplify`, which is essentially a wrapper for + :func:`identify`.) + + **Miscellaneous issues and limitations** + + The input `x` must be a real number. All base constants must be + positive real numbers and must not be rationals or rational linear + combinations of each other. + + The worst-case computation time grows quickly with the number of + base constants. Already with 3 or 4 base constants, + :func:`identify` may require several seconds to finish. To search + for relations among a large number of constants, you should + consider using :func:`pslq` directly. + + The extended transformations are applied to x, not the constants + separately. As a result, ``identify`` will for example be able to + recognize ``exp(2*pi+3)`` with ``pi`` given as a base constant, but + not ``2*exp(pi)+3``. It will be able to recognize the latter if + ``exp(pi)`` is given explicitly as a base constant. + + """ + + solutions = [] + + def addsolution(s): + if verbose: print "Found: ", s + solutions.append(s) + + x = ctx.mpf(x) + + # Further along, x will be assumed positive + if x == 0: + if full: return ['0'] + else: return '0' + if x < 0: + sol = ctx.identify(-x, constants, tol, maxcoeff, full, verbose) + if sol is None: + return sol + if full: + return ["-(%s)"%s for s in sol] + else: + return "-(%s)" % sol + + if tol: + tol = ctx.mpf(tol) + else: + tol = ctx.eps**0.7 + M = maxcoeff + + if constants: + if isinstance(constants, dict): + constants = [(ctx.mpf(v), name) for (name, v) in constants.items()] + else: + namespace = dict((name, getattr(ctx,name)) for name in dir(ctx)) + constants = [(eval(p, namespace), p) for p in constants] + else: + constants = [] + + # We always want to find at least rational terms + if 1 not in [value for (name, value) in constants]: + constants = [(ctx.mpf(1), '1')] + constants + + # PSLQ with simple algebraic and functional transformations + for ft, ftn, red in transforms: + for c, cn in constants: + if red and cn == '1': + continue + t = ft(ctx,x,c) + # Prevent exponential transforms from wreaking havoc + if abs(t) > M**2 or abs(t) < tol: + continue + # Linear combination of base constants + r = ctx.pslq([t] + [a[0] for a in constants], tol, M) + s = None + if r is not None and max(abs(uw) for uw in r) <= M and r[0]: + s = pslqstring(r, constants) + # Quadratic algebraic numbers + else: + q = ctx.pslq([ctx.one, t, t**2], tol, M) + if q is not None and len(q) == 3 and q[2]: + aa, bb, cc = q + if max(abs(aa),abs(bb),abs(cc)) <= M: + s = quadraticstring(ctx,t,aa,bb,cc) + if s: + if cn == '1' and ('/$c' in ftn): + s = ftn.replace('$y', s).replace('/$c', '') + else: + s = ftn.replace('$y', s).replace('$c', cn) + addsolution(s) + if not full: return solutions[0] + + if verbose: + print "." + + # Check for a direct multiplicative formula + if x != 1: + # Allow fractional powers of fractions + ilogs = [2,3,5,7] + # Watch out for existing fractional powers of fractions + logs = [] + for a, s in constants: + if not sum(bool(ctx.findpoly(ctx.ln(a)/ctx.ln(i),1)) for i in ilogs): + logs.append((ctx.ln(a), s)) + logs = [(ctx.ln(i),str(i)) for i in ilogs] + logs + r = ctx.pslq([ctx.ln(x)] + [a[0] for a in logs], tol, M) + if r is not None and max(abs(uw) for uw in r) <= M and r[0]: + addsolution(prodstring(r, logs)) + if not full: return solutions[0] + + if full: + return sorted(solutions, key=len) + else: + return None + +IdentificationMethods.pslq = pslq +IdentificationMethods.findpoly = findpoly +IdentificationMethods.identify = identify + + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/compiler/gdsMill/mpmath/libmp/__init__.py b/compiler/gdsMill/mpmath/libmp/__init__.py new file mode 100644 index 00000000..862b8eb2 --- /dev/null +++ b/compiler/gdsMill/mpmath/libmp/__init__.py @@ -0,0 +1,64 @@ +from libmpf import (prec_to_dps, dps_to_prec, repr_dps, + round_down, round_up, round_floor, round_ceiling, round_nearest, + to_pickable, from_pickable, ComplexResult, + fzero, fnzero, fone, fnone, ftwo, ften, fhalf, fnan, finf, fninf, + math_float_inf, round_int, normalize, normalize1, + from_man_exp, from_int, to_man_exp, to_int, mpf_ceil, mpf_floor, + from_float, to_float, from_rational, to_rational, to_fixed, + mpf_rand, mpf_eq, mpf_hash, mpf_cmp, mpf_lt, mpf_le, mpf_gt, mpf_ge, + mpf_pos, mpf_neg, mpf_abs, mpf_sign, mpf_add, mpf_sub, mpf_sum, + mpf_mul, mpf_mul_int, mpf_shift, mpf_frexp, + mpf_div, mpf_rdiv_int, mpf_mod, mpf_pow_int, + mpf_perturb, + to_digits_exp, to_str, str_to_man_exp, from_str, from_bstr, to_bstr, + mpf_sqrt, mpf_hypot) + +from libmpc import (mpc_one, mpc_zero, mpc_two, mpc_half, + mpc_is_inf, mpc_is_infnan, mpc_to_str, mpc_to_complex, mpc_hash, + mpc_conjugate, mpc_is_nonzero, mpc_add, mpc_add_mpf, + mpc_sub, mpc_sub_mpf, mpc_pos, mpc_neg, mpc_shift, mpc_abs, + mpc_arg, mpc_floor, mpc_ceil, mpc_mul, mpc_square, + mpc_mul_mpf, mpc_mul_imag_mpf, mpc_mul_int, + mpc_div, mpc_div_mpf, mpc_reciprocal, mpc_mpf_div, + complex_int_pow, mpc_pow, mpc_pow_mpf, mpc_pow_int, + mpc_sqrt, mpc_nthroot, mpc_cbrt, mpc_exp, mpc_log, mpc_cos, mpc_sin, + mpc_tan, mpc_cos_pi, mpc_sin_pi, mpc_cosh, mpc_sinh, mpc_tanh, + mpc_atan, mpc_acos, mpc_asin, mpc_asinh, mpc_acosh, mpc_atanh, + mpc_fibonacci, mpf_expj, mpf_expjpi, mpc_expj, mpc_expjpi) + +from libelefun import (ln2_fixed, mpf_ln2, ln10_fixed, mpf_ln10, + pi_fixed, mpf_pi, e_fixed, mpf_e, phi_fixed, mpf_phi, + degree_fixed, mpf_degree, + mpf_pow, mpf_nthroot, mpf_cbrt, log_int_fixed, agm_fixed, + mpf_log, mpf_log_hypot, mpf_exp, mpf_cos_sin, mpf_cos, mpf_sin, mpf_tan, + mpf_cos_sin_pi, mpf_cos_pi, mpf_sin_pi, mpf_cosh_sinh, + mpf_cosh, mpf_sinh, mpf_tanh, mpf_atan, mpf_atan2, mpf_asin, + mpf_acos, mpf_asinh, mpf_acosh, mpf_atanh, mpf_fibonacci) + +from libhyper import (NoConvergence, make_hyp_summator, + mpf_erf, mpf_erfc, mpf_ei, mpc_ei, mpf_e1, mpc_e1, mpf_expint, + mpf_ci_si, mpf_ci, mpf_si, mpc_ci, mpc_si, mpf_besseljn, + mpc_besseljn, mpf_agm, mpf_agm1, mpc_agm, mpc_agm1, + mpf_ellipk, mpc_ellipk, mpf_ellipe, mpc_ellipe) + +from gammazeta import (catalan_fixed, mpf_catalan, + khinchin_fixed, mpf_khinchin, glaisher_fixed, mpf_glaisher, + apery_fixed, mpf_apery, euler_fixed, mpf_euler, mertens_fixed, + mpf_mertens, twinprime_fixed, mpf_twinprime, + mpf_bernoulli, bernfrac, mpf_gamma_int, + mpf_factorial, mpc_factorial, mpf_gamma, mpc_gamma, + mpf_harmonic, mpc_harmonic, mpf_psi0, mpc_psi0, + mpf_psi, mpc_psi, mpf_zeta_int, mpf_zeta, mpc_zeta, + mpf_altzeta, mpc_altzeta, mpf_zetasum, mpc_zetasum) + +from libmpi import (mpi_str, mpi_add, mpi_sub, mpi_delta, mpi_mid, + mpi_pos, mpi_neg, mpi_abs, mpi_mul, mpi_div, mpi_exp, + mpi_log, mpi_sqrt, mpi_pow_int, mpi_pow, mpi_cos_sin, + mpi_cos, mpi_sin, mpi_tan, mpi_cot) + +from libintmath import (trailing, bitcount, numeral, bin_to_radix, + isqrt, isqrt_small, isqrt_fast, sqrt_fixed, sqrtrem, ifib, ifac, + list_primes, moebius, gcd, eulernum) + +from backend import (gmpy, sage, BACKEND, STRICT, MPZ, MPZ_TYPE, + MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_THREE, MPZ_FIVE, int_types) diff --git a/compiler/gdsMill/mpmath/libmp/backend.py b/compiler/gdsMill/mpmath/libmp/backend.py new file mode 100644 index 00000000..af275675 --- /dev/null +++ b/compiler/gdsMill/mpmath/libmp/backend.py @@ -0,0 +1,64 @@ +import os +import sys + +#----------------------------------------------------------------------------# +# Support GMPY for high-speed large integer arithmetic. # +# # +# To allow an external module to handle arithmetic, we need to make sure # +# that all high-precision variables are declared of the correct type. MPZ # +# is the constructor for the high-precision type. It defaults to Python's # +# long type but can be assinged another type, typically gmpy.mpz. # +# # +# MPZ must be used for the mantissa component of an mpf and must be used # +# for internal fixed-point operations. # +# # +# Side-effects # +# 1) "is" cannot be used to test for special values. Must use "==". # +# 2) There are bugs in GMPY prior to v1.02 so we must use v1.03 or later. # +#----------------------------------------------------------------------------# + +# So we can import it from this module +gmpy = None +sage = None +sage_utils = None + +BACKEND = 'python' +MPZ = long + +if 'MPMATH_NOGMPY' not in os.environ: + try: + import gmpy + if gmpy.version() >= '1.03': + BACKEND = 'gmpy' + MPZ = gmpy.mpz + except: + pass + +if 'MPMATH_NOSAGE' not in os.environ: + try: + import sage.all + import sage.libs.mpmath.utils as _sage_utils + sage = sage.all + sage_utils = _sage_utils + BACKEND = 'sage' + MPZ = sage.Integer + except: + pass + +if 'MPMATH_STRICT' in os.environ: + STRICT = True +else: + STRICT = False + +MPZ_TYPE = type(MPZ(0)) +MPZ_ZERO = MPZ(0) +MPZ_ONE = MPZ(1) +MPZ_TWO = MPZ(2) +MPZ_THREE = MPZ(3) +MPZ_FIVE = MPZ(5) + +if BACKEND == 'python': + int_types = (int, long) +else: + int_types = (int, long, MPZ_TYPE) + diff --git a/compiler/gdsMill/mpmath/libmp/gammazeta.py b/compiler/gdsMill/mpmath/libmp/gammazeta.py new file mode 100644 index 00000000..f09a8f5b --- /dev/null +++ b/compiler/gdsMill/mpmath/libmp/gammazeta.py @@ -0,0 +1,1476 @@ +""" +----------------------------------------------------------------------- +This module implements gamma- and zeta-related functions: + +* Bernoulli numbers +* Factorials +* The gamma function +* Polygamma functions +* Harmonic numbers +* The Riemann zeta function +* Constants related to these functions + +----------------------------------------------------------------------- +""" + +import math + +from backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_THREE, gmpy + +from libintmath import list_primes, ifac, moebius + +from libmpf import (\ + round_floor, round_ceiling, round_down, round_up, + round_nearest, round_fast, + lshift, sqrt_fixed, + fzero, fone, fnone, fhalf, ftwo, finf, fninf, fnan, + from_int, to_int, to_fixed, from_man_exp, from_rational, + mpf_pos, mpf_neg, mpf_abs, mpf_add, mpf_sub, + mpf_mul, mpf_mul_int, mpf_div, mpf_sqrt, mpf_pow_int, + mpf_rdiv_int, + mpf_perturb, mpf_le, mpf_lt, mpf_gt, mpf_shift, + negative_rnd, reciprocal_rnd, +) + +from libelefun import (\ + constant_memo, + def_mpf_constant, + mpf_pi, pi_fixed, ln2_fixed, log_int_fixed, mpf_ln2, + mpf_exp, mpf_log, mpf_pow, mpf_cosh, + mpf_cos_sin, mpf_cosh_sinh, mpf_cos_sin_pi, mpf_cos_pi, mpf_sin_pi, +) + +from libmpc import (\ + mpc_zero, mpc_one, mpc_half, mpc_two, + mpc_abs, mpc_shift, mpc_pos, mpc_neg, + mpc_add, mpc_sub, mpc_mul, mpc_div, + mpc_add_mpf, mpc_mul_mpf, mpc_div_mpf, mpc_mpf_div, + mpc_mul_int, mpc_pow_int, + mpc_log, mpc_exp, mpc_pow, + mpc_cos_pi, mpc_sin_pi, + mpc_reciprocal, mpc_square +) + +# Catalan's constant is computed using Lupas's rapidly convergent series +# (listed on http://mathworld.wolfram.com/CatalansConstant.html) +# oo +# ___ n-1 8n 2 3 2 +# 1 \ (-1) 2 (40n - 24n + 3) [(2n)!] (n!) +# K = --- ) ----------------------------------------- +# 64 /___ 3 2 +# n (2n-1) [(4n)!] +# n = 1 + +@constant_memo +def catalan_fixed(prec): + prec = prec + 20 + a = one = MPZ_ONE << prec + s, t, n = 0, 1, 1 + while t: + a *= 32 * n**3 * (2*n-1) + a //= (3-16*n+16*n**2)**2 + t = a * (-1)**(n-1) * (40*n**2-24*n+3) // (n**3 * (2*n-1)) + s += t + n += 1 + return s >> (20 + 6) + +# Khinchin's constant is relatively difficult to compute. Here +# we use the rational zeta series + +# oo 2*n-1 +# ___ ___ +# \ ` zeta(2*n)-1 \ ` (-1)^(k+1) +# log(K)*log(2) = ) ------------ ) ---------- +# /___. n /___. k +# n = 1 k = 1 + +# which adds half a digit per term. The essential trick for achieving +# reasonable efficiency is to recycle both the values of the zeta +# function (essentially Bernoulli numbers) and the partial terms of +# the inner sum. + +# An alternative might be to use K = 2*exp[1/log(2) X] where + +# / 1 1 [ pi*x*(1-x^2) ] +# X = | ------ log [ ------------ ]. +# / 0 x(1+x) [ sin(pi*x) ] + +# and integrate numerically. In practice, this seems to be slightly +# slower than the zeta series at high precision. + +@constant_memo +def khinchin_fixed(prec): + wp = int(prec + prec**0.5 + 15) + s = MPZ_ZERO + fac = from_int(4) + t = ONE = MPZ_ONE << wp + pi = mpf_pi(wp) + pipow = twopi2 = mpf_shift(mpf_mul(pi, pi, wp), 2) + n = 1 + while 1: + zeta2n = mpf_abs(mpf_bernoulli(2*n, wp)) + zeta2n = mpf_mul(zeta2n, pipow, wp) + zeta2n = mpf_div(zeta2n, fac, wp) + zeta2n = to_fixed(zeta2n, wp) + term = (((zeta2n - ONE) * t) // n) >> wp + if term < 100: + break + #if not n % 10: + # print n, math.log(int(abs(term))) + s += term + t += ONE//(2*n+1) - ONE//(2*n) + n += 1 + fac = mpf_mul_int(fac, (2*n)*(2*n-1), wp) + pipow = mpf_mul(pipow, twopi2, wp) + s = (s << wp) // ln2_fixed(wp) + K = mpf_exp(from_man_exp(s, -wp), wp) + K = to_fixed(K, prec) + return K + + +# Glaisher's constant is defined as A = exp(1/2 - zeta'(-1)). +# One way to compute it would be to perform direct numerical +# differentiation, but computing arbitrary Riemann zeta function +# values at high precision is expensive. We instead use the formula + +# A = exp((6 (-zeta'(2))/pi^2 + log 2 pi + gamma)/12) + +# and compute zeta'(2) from the series representation + +# oo +# ___ +# \ log k +# -zeta'(2) = ) ----- +# /___ 2 +# k +# k = 2 + +# This series converges exceptionally slowly, but can be accelerated +# using Euler-Maclaurin formula. The important insight is that the +# E-M integral can be done in closed form and that the high order +# are given by + +# n / \ +# d | log x | a + b log x +# --- | ----- | = ----------- +# n | 2 | 2 + n +# dx \ x / x + +# where a and b are integers given by a simple recurrence. Note +# that just one logarithm is needed. However, lots of integer +# logarithms are required for the initial summation. + +# This algorithm could possibly be turned into a faster algorithm +# for general evaluation of zeta(s) or zeta'(s); this should be +# looked into. + +@constant_memo +def glaisher_fixed(prec): + wp = prec + 30 + # Number of direct terms to sum before applying the Euler-Maclaurin + # formula to the tail. TODO: choose more intelligently + N = int(0.33*prec + 5) + ONE = MPZ_ONE << wp + # Euler-Maclaurin, step 1: sum log(k)/k**2 for k from 2 to N-1 + s = MPZ_ZERO + for k in range(2, N): + #print k, N + s += log_int_fixed(k, wp) // k**2 + logN = log_int_fixed(N, wp) + #logN = to_fixed(mpf_log(from_int(N), wp+20), wp) + # E-M step 2: integral of log(x)/x**2 from N to inf + s += (ONE + logN) // N + # E-M step 3: endpoint correction term f(N)/2 + s += logN // (N**2 * 2) + # E-M step 4: the series of derivatives + pN = N**3 + a = 1 + b = -2 + j = 3 + fac = from_int(2) + k = 1 + while 1: + # D(2*k-1) * B(2*k) / fac(2*k) [D(n) = nth derivative] + D = ((a << wp) + b*logN) // pN + D = from_man_exp(D, -wp) + B = mpf_bernoulli(2*k, wp) + term = mpf_mul(B, D, wp) + term = mpf_div(term, fac, wp) + term = to_fixed(term, wp) + if abs(term) < 100: + break + #if not k % 10: + # print k, math.log(int(abs(term)), 10) + s -= term + # Advance derivative twice + a, b, pN, j = b-a*j, -j*b, pN*N, j+1 + a, b, pN, j = b-a*j, -j*b, pN*N, j+1 + k += 1 + fac = mpf_mul_int(fac, (2*k)*(2*k-1), wp) + # A = exp((6*s/pi**2 + log(2*pi) + euler)/12) + pi = pi_fixed(wp) + s *= 6 + s = (s << wp) // (pi**2 >> wp) + s += euler_fixed(wp) + s += to_fixed(mpf_log(from_man_exp(2*pi, -wp), wp), wp) + s //= 12 + A = mpf_exp(from_man_exp(s, -wp), wp) + return to_fixed(A, prec) + +# Apery's constant can be computed using the very rapidly convergent +# series +# oo +# ___ 2 10 +# \ n 205 n + 250 n + 77 (n!) +# zeta(3) = ) (-1) ------------------- ---------- +# /___ 64 5 +# n = 0 ((2n+1)!) + +@constant_memo +def apery_fixed(prec): + prec += 20 + d = MPZ_ONE << prec + term = MPZ(77) << prec + n = 1 + s = MPZ_ZERO + while term: + s += term + d *= (n**10) + d //= (((2*n+1)**5) * (2*n)**5) + term = (-1)**n * (205*(n**2) + 250*n + 77) * d + n += 1 + return s >> (20 + 6) + +""" +Euler's constant (gamma) is computed using the Brent-McMillan formula, +gamma ~= I(n)/J(n) - log(n), where + + I(n) = sum_{k=0,1,2,...} (n**k / k!)**2 * H(k) + J(n) = sum_{k=0,1,2,...} (n**k / k!)**2 + H(k) = 1 + 1/2 + 1/3 + ... + 1/k + +The error is bounded by O(exp(-4n)). Choosing n to be a power +of two, 2**p, the logarithm becomes particularly easy to calculate.[1] + +We use the formulation of Algorithm 3.9 in [2] to make the summation +more efficient. + +Reference: +[1] Xavier Gourdon & Pascal Sebah, The Euler constant: gamma +http://numbers.computation.free.fr/Constants/Gamma/gamma.pdf + +[2] Jonathan Borwein & David Bailey, Mathematics by Experiment, +A K Peters, 2003 +""" + +@constant_memo +def euler_fixed(prec): + extra = 30 + prec += extra + # choose p such that exp(-4*(2**p)) < 2**-n + p = int(math.log((prec/4) * math.log(2), 2)) + 1 + n = 2**p + A = U = -p*ln2_fixed(prec) + B = V = MPZ_ONE << prec + k = 1 + while 1: + B = B*n**2//k**2 + A = (A*n**2//k + B)//k + U += A + V += B + if max(abs(A), abs(B)) < 100: + break + k += 1 + return (U<<(prec-extra))//V + +# Use zeta accelerated formulas for the Mertens and twin +# prime constants; see +# http://mathworld.wolfram.com/MertensConstant.html +# http://mathworld.wolfram.com/TwinPrimesConstant.html + +@constant_memo +def mertens_fixed(prec): + wp = prec + 20 + m = 2 + s = mpf_euler(wp) + while 1: + t = mpf_zeta_int(m, wp) + if t == fone: + break + t = mpf_log(t, wp) + t = mpf_mul_int(t, moebius(m), wp) + t = mpf_div(t, from_int(m), wp) + s = mpf_add(s, t) + m += 1 + return to_fixed(s, prec) + +@constant_memo +def twinprime_fixed(prec): + def I(n): + return sum(moebius(d)<<(n//d) for d in xrange(1,n+1) if not n%d)//n + wp = 2*prec + 30 + res = fone + primes = [from_rational(1,p,wp) for p in [2,3,5,7]] + ppowers = [mpf_mul(p,p,wp) for p in primes] + n = 2 + while 1: + a = mpf_zeta_int(n, wp) + for i in range(4): + a = mpf_mul(a, mpf_sub(fone, ppowers[i]), wp) + ppowers[i] = mpf_mul(ppowers[i], primes[i], wp) + a = mpf_pow_int(a, -I(n), wp) + if mpf_pos(a, prec+10, 'n') == fone: + break + #from libmpf import to_str + #print n, to_str(mpf_sub(fone, a), 6) + res = mpf_mul(res, a, wp) + n += 1 + res = mpf_mul(res, from_int(3*15*35), wp) + res = mpf_div(res, from_int(4*16*36), wp) + return to_fixed(res, prec) + + +mpf_euler = def_mpf_constant(euler_fixed) +mpf_apery = def_mpf_constant(apery_fixed) +mpf_khinchin = def_mpf_constant(khinchin_fixed) +mpf_glaisher = def_mpf_constant(glaisher_fixed) +mpf_catalan = def_mpf_constant(catalan_fixed) +mpf_mertens = def_mpf_constant(mertens_fixed) +mpf_twinprime = def_mpf_constant(twinprime_fixed) + + +#-----------------------------------------------------------------------# +# # +# Bernoulli numbers # +# # +#-----------------------------------------------------------------------# + +MAX_BERNOULLI_CACHE = 3000 + + +""" +Small Bernoulli numbers and factorials are used in numerous summations, +so it is critical for speed that sequential computation is fast and that +values are cached up to a fairly high threshold. + +On the other hand, we also want to support fast computation of isolated +large numbers. Currently, no such acceleration is provided for integer +factorials (though it is for large floating-point factorials, which are +computed via gamma if the precision is low enough). + +For sequential computation of Bernoulli numbers, we use Ramanujan's formula + + / n + 3 \ + B = (A(n) - S(n)) / | | + n \ n / + +where A(n) = (n+3)/3 when n = 0 or 2 (mod 6), A(n) = -(n+3)/6 +when n = 4 (mod 6), and + + [n/6] + ___ + \ / n + 3 \ + S(n) = ) | | * B + /___ \ n - 6*k / n-6*k + k = 1 + +For isolated large Bernoulli numbers, we use the Riemann zeta function +to calculate a numerical value for B_n. The von Staudt-Clausen theorem +can then be used to optionally find the exact value of the +numerator and denominator. +""" + +bernoulli_cache = {} +f3 = from_int(3) +f6 = from_int(6) + +def bernoulli_size(n): + """Accurately estimate the size of B_n (even n > 2 only)""" + lgn = math.log(n,2) + return int(2.326 + 0.5*lgn + n*(lgn - 4.094)) + +BERNOULLI_PREC_CUTOFF = bernoulli_size(MAX_BERNOULLI_CACHE) + +def mpf_bernoulli(n, prec, rnd=None): + """Computation of Bernoulli numbers (numerically)""" + if n < 2: + if n < 0: + raise ValueError("Bernoulli numbers only defined for n >= 0") + if n == 0: + return fone + if n == 1: + return mpf_neg(fhalf) + # For odd n > 1, the Bernoulli numbers are zero + if n & 1: + return fzero + # If precision is extremely high, we can save time by computing + # the Bernoulli number at a lower precision that is sufficient to + # obtain the exact fraction, round to the exact fraction, and + # convert the fraction back to an mpf value at the original precision + if prec > BERNOULLI_PREC_CUTOFF and prec > bernoulli_size(n)*1.1 + 1000: + p, q = bernfrac(n) + return from_rational(p, q, prec, rnd or round_floor) + if n > MAX_BERNOULLI_CACHE: + return mpf_bernoulli_huge(n, prec, rnd) + wp = prec + 30 + # Reuse nearby precisions + wp += 32 - (prec & 31) + cached = bernoulli_cache.get(wp) + if cached: + numbers, state = cached + if n in numbers: + if not rnd: + return numbers[n] + return mpf_pos(numbers[n], prec, rnd) + m, bin, bin1 = state + if n - m > 10: + return mpf_bernoulli_huge(n, prec, rnd) + else: + if n > 10: + return mpf_bernoulli_huge(n, prec, rnd) + numbers = {0:fone} + m, bin, bin1 = state = [2, MPZ(10), MPZ_ONE] + bernoulli_cache[wp] = (numbers, state) + while m <= n: + #print m + case = m % 6 + # Accurately estimate size of B_m so we can use + # fixed point math without using too much precision + szbm = bernoulli_size(m) + s = 0 + sexp = max(0, szbm) - wp + if m < 6: + a = MPZ_ZERO + else: + a = bin1 + for j in xrange(1, m//6+1): + usign, uman, uexp, ubc = u = numbers[m-6*j] + if usign: + uman = -uman + s += lshift(a*uman, uexp-sexp) + # Update inner binomial coefficient + j6 = 6*j + a *= ((m-5-j6)*(m-4-j6)*(m-3-j6)*(m-2-j6)*(m-1-j6)*(m-j6)) + a //= ((4+j6)*(5+j6)*(6+j6)*(7+j6)*(8+j6)*(9+j6)) + if case == 0: b = mpf_rdiv_int(m+3, f3, wp) + if case == 2: b = mpf_rdiv_int(m+3, f3, wp) + if case == 4: b = mpf_rdiv_int(-m-3, f6, wp) + s = from_man_exp(s, sexp, wp) + b = mpf_div(mpf_sub(b, s, wp), from_int(bin), wp) + numbers[m] = b + m += 2 + # Update outer binomial coefficient + bin = bin * ((m+2)*(m+3)) // (m*(m-1)) + if m > 6: + bin1 = bin1 * ((2+m)*(3+m)) // ((m-7)*(m-6)) + state[:] = [m, bin, bin1] + return numbers[n] + +def mpf_bernoulli_huge(n, prec, rnd=None): + wp = prec + 10 + piprec = wp + int(math.log(n,2)) + v = mpf_gamma_int(n+1, wp) + v = mpf_mul(v, mpf_zeta_int(n, wp), wp) + v = mpf_mul(v, mpf_pow_int(mpf_pi(piprec), -n, wp)) + v = mpf_shift(v, 1-n) + if not n & 3: + v = mpf_neg(v) + return mpf_pos(v, prec, rnd or round_fast) + +def bernfrac(n): + r""" + Returns a tuple of integers `(p, q)` such that `p/q = B_n` exactly, + where `B_n` denotes the `n`-th Bernoulli number. The fraction is + always reduced to lowest terms. Note that for `n > 1` and `n` odd, + `B_n = 0`, and `(0, 1)` is returned. + + **Examples** + + The first few Bernoulli numbers are exactly:: + + >>> from mpmath import * + >>> for n in range(15): + ... p, q = bernfrac(n) + ... print n, "%s/%s" % (p, q) + ... + 0 1/1 + 1 -1/2 + 2 1/6 + 3 0/1 + 4 -1/30 + 5 0/1 + 6 1/42 + 7 0/1 + 8 -1/30 + 9 0/1 + 10 5/66 + 11 0/1 + 12 -691/2730 + 13 0/1 + 14 7/6 + + This function works for arbitrarily large `n`:: + + >>> p, q = bernfrac(10**4) + >>> print q + 2338224387510 + >>> print len(str(p)) + 27692 + >>> mp.dps = 15 + >>> print mpf(p) / q + -9.04942396360948e+27677 + >>> print bernoulli(10**4) + -9.04942396360948e+27677 + + Note: :func:`bernoulli` computes a floating-point approximation + directly, without computing the exact fraction first. + This is much faster for large `n`. + + **Algorithm** + + :func:`bernfrac` works by computing the value of `B_n` numerically + and then using the von Staudt-Clausen theorem [1] to reconstruct + the exact fraction. For large `n`, this is significantly faster than + computing `B_1, B_2, \ldots, B_2` recursively with exact arithmetic. + The implementation has been tested for `n = 10^m` up to `m = 6`. + + In practice, :func:`bernfrac` appears to be about three times + slower than the specialized program calcbn.exe [2] + + **References** + + 1. MathWorld, von Staudt-Clausen Theorem: + http://mathworld.wolfram.com/vonStaudt-ClausenTheorem.html + + 2. The Bernoulli Number Page: + http://www.bernoulli.org/ + + """ + n = int(n) + if n < 3: + return [(1, 1), (-1, 2), (1, 6)][n] + if n & 1: + return (0, 1) + q = 1 + for k in list_primes(n+1): + if not (n % (k-1)): + q *= k + prec = bernoulli_size(n) + int(math.log(q,2)) + 20 + b = mpf_bernoulli(n, prec) + p = mpf_mul(b, from_int(q)) + pint = to_int(p, round_nearest) + return (pint, q) + + +#-----------------------------------------------------------------------# +# # +# The gamma function # +# # +#-----------------------------------------------------------------------# + + +""" +We compute the real factorial / gamma function using Spouge's approximation + + x! = (x+a)**(x+1/2) * exp(-x-a) * [c_0 + S(x) + eps] + +where S(x) is the sum of c_k/(x+k) from k = 1 to a-1 and the coefficients +are given by + + c_0 = sqrt(2*pi) + + (-1)**(k-1) + c_k = ----------- (a-k)**(k-1/2) exp(-k+a), k = 1,2,...,a-1 + (k - 1)! + +As proved by Spouge, if we choose a = log(2)/log(2*pi)*n = 0.38*n, the +relative error eps is less than 2^(-n) for any x in the right complex +half-plane (assuming a > 2). In practice, it seems that a can be chosen +quite a bit lower still (30-50%); this possibility should be investigated. + +For negative x, we use the reflection formula. + +References: +----------- + +John L. Spouge, "Computation of the gamma, digamma, and trigamma +functions", SIAM Journal on Numerical Analysis 31 (1994), no. 3, 931-944. +""" + +spouge_cache = {} + +def calc_spouge_coefficients(a, prec): + wp = prec + int(a*1.4) + c = [0] * a + # b = exp(a-1) + b = mpf_exp(from_int(a-1), wp) + # e = exp(1) + e = mpf_exp(fone, wp) + # sqrt(2*pi) + sq2pi = mpf_sqrt(mpf_shift(mpf_pi(wp), 1), wp) + c[0] = to_fixed(sq2pi, prec) + for k in xrange(1, a): + # c[k] = ((-1)**(k-1) * (a-k)**k) * b / sqrt(a-k) + term = mpf_mul_int(b, ((-1)**(k-1) * (a-k)**k), wp) + term = mpf_div(term, mpf_sqrt(from_int(a-k), wp), wp) + c[k] = to_fixed(term, prec) + # b = b / (e * k) + b = mpf_div(b, mpf_mul(e, from_int(k), wp), wp) + return c + +# Cached lookup of coefficients +def get_spouge_coefficients(prec): + # This exact precision has been used before + if prec in spouge_cache: + return spouge_cache[prec] + for p in spouge_cache: + if 0.8 <= prec/float(p) < 1: + return spouge_cache[p] + # Here we estimate the value of a based on Spouge's inequality for + # the relative error + a = max(3, int(0.38*prec)) # 0.38 = log(2)/log(2*pi), ~= 1.26*n + coefs = calc_spouge_coefficients(a, prec) + spouge_cache[prec] = (prec, a, coefs) + return spouge_cache[prec] + +def spouge_sum_real(x, prec, a, c): + x = to_fixed(x, prec) + s = c[0] + for k in xrange(1, a): + s += (c[k] << prec) // (x + (k << prec)) + return from_man_exp(s, -prec, prec, round_floor) + +# Unused: for fast computation of gamma(p/q) +def spouge_sum_rational(p, q, prec, a, c): + s = c[0] + for k in xrange(1, a): + s += c[k] * q // (p+q*k) + return from_man_exp(s, -prec, prec, round_floor) + +# For a complex number a + b*I, we have +# +# c_k (a+k)*c_k b * c_k +# ------------- = --------- - ------- * I +# (a + b*I) + k M M +# +# 2 2 2 2 2 +# where M = (a+k) + b = (a + b ) + (2*a*k + k ) + +def spouge_sum_complex(re, im, prec, a, c): + re = to_fixed(re, prec) + im = to_fixed(im, prec) + sre, sim = c[0], 0 + mag = ((re**2)>>prec) + ((im**2)>>prec) + for k in xrange(1, a): + M = mag + re*(2*k) + ((k**2) << prec) + sre += (c[k] * (re + (k << prec))) // M + sim -= (c[k] * im) // M + re = from_man_exp(sre, -prec, prec, round_floor) + im = from_man_exp(sim, -prec, prec, round_floor) + return re, im + +# XXX: currently this falls back to mpf_gamma. It should +# be the other way around: this function should handle +# all sizes/precisions efficiently, and gamma should fall +# back here +def mpf_gamma_int(n, prec, rounding=round_fast): + if n < 1000: + return from_int(ifac(n-1), prec, rounding) + # XXX: choose the cutoff less arbitrarily + size = int(n*math.log(n,2)) + if prec > size/20.0: + return from_int(ifac(n-1), prec, rounding) + return mpf_gamma(from_int(n), prec, rounding) + +def mpf_factorial(x, prec, rounding=round_fast): + return mpf_gamma(x, prec, rounding, p1=0) + +def mpc_factorial(x, prec, rounding=round_fast): + return mpc_gamma(x, prec, rounding, p1=0) + +def mpf_gamma(x, prec, rounding=round_fast, p1=1): + """ + Computes the gamma function of a real floating-point argument. + With p1=0, computes a factorial instead. + """ + sign, man, exp, bc = x + if not man: + if x == finf: + return finf + if x == fninf or x == fnan: + return fnan + # More precision is needed for enormous x. TODO: + # use Stirling's formula + Euler-Maclaurin summation + size = exp + bc + if size > 5: + size = int(size * math.log(size,2)) + wp = prec + max(0, size) + 15 + if exp >= 0: + if sign or (p1 and not man): + raise ValueError("gamma function pole") + # A direct factorial is fastest + if exp + bc <= 10: + return from_int(ifac((man< 5: + size = int(size * math.log(size,2)) + reflect = sign or (exp+bc < -1) + wp = prec + max(0, size) + 25 + # Near x = 0 pole (TODO: other poles) + if p1: + if size < -prec-5: + return mpc_add_mpf(mpc_div(mpc_one, x, 2*prec+10), \ + mpf_neg(mpf_euler(2*prec+10)), prec, rounding) + elif size < -5: + wp += (-2*size) + if p1: + # Should be done exactly! + re_orig = re + re = mpf_sub(re, fone, bc+abs(exp)+2) + x = re, im + if reflect: + # Reflection formula + wp += 15 + pi = mpf_pi(wp), fzero + pix = mpc_mul(x, pi, wp) + t = mpc_sin_pi(x, wp) + u = mpc_sub(mpc_one, x, wp) + g = mpc_gamma(u, wp) + w = mpc_mul(t, g, wp) + return mpc_div(pix, w, wp) + # Extremely close to the real line? + # XXX: reflection formula + if iexp+ibc < -wp: + a = mpf_gamma(re_orig, wp) + b = mpf_psi0(re_orig, wp) + gamma_diff = mpf_div(a, b, wp) + return mpf_pos(a, prec, rounding), mpf_mul(gamma_diff, im, prec, rounding) + sprec, a, c = get_spouge_coefficients(wp) + s = spouge_sum_complex(re, im, sprec, a, c) + # gamma = exp(log(x+a)*(x+0.5) - xpa) * s + repa = mpf_add(re, from_int(a), wp) + logxpa = mpc_log((repa, im), wp) + reph = mpf_add(re, fhalf, wp) + t = mpc_sub(mpc_mul(logxpa, (reph, im), wp), (repa, im), wp) + t = mpc_mul(mpc_exp(t, wp), s, prec, rounding) + return t + + +#-----------------------------------------------------------------------# +# # +# Polygamma functions # +# # +#-----------------------------------------------------------------------# + +""" +For all polygamma (psi) functions, we use the Euler-Maclaurin summation +formula. It looks slightly different in the m = 0 and m > 0 cases. + +For m = 0, we have + oo + ___ B + (0) 1 \ 2 k -2 k + psi (z) ~ log z + --- - ) ------ z + 2 z /___ (2 k)! + k = 1 + +Experiment shows that the minimum term of the asymptotic series +reaches 2^(-p) when Re(z) > 0.11*p. So we simply use the recurrence +for psi (equivalent, in fact, to summing to the first few terms +directly before applying E-M) to obtain z large enough. + +Since, very crudely, log z ~= 1 for Re(z) > 1, we can use +fixed-point arithmetic (if z is extremely large, log(z) itself +is a sufficient approximation, so we can stop there already). + +For Re(z) << 0, we could use recurrence, but this is of course +inefficient for large negative z, so there we use the +reflection formula instead. + +For m > 0, we have + + N - 1 + ___ + ~~~(m) [ \ 1 ] 1 1 + psi (z) ~ [ ) -------- ] + ---------- + -------- + + [ /___ m+1 ] m+1 m + k = 1 (z+k) ] 2 (z+N) m (z+N) + + oo + ___ B + \ 2 k (m+1) (m+2) ... (m+2k-1) + + ) ------ ------------------------ + /___ (2 k)! m + 2 k + k = 1 (z+N) + +where ~~~ denotes the function rescaled by 1/((-1)^(m+1) m!). + +Here again N is chosen to make z+N large enough for the minimum +term in the last series to become smaller than eps. + +TODO: the current estimation of N for m > 0 is *very suboptimal*. + +TODO: implement the reflection formula for m > 0, Re(z) << 0. +It is generally a combination of multiple cotangents. Need to +figure out a reasonably simple way to generate these formulas +on the fly. + +TODO: maybe use exact algorithms to compute psi for integral +and certain rational arguments, as this can be much more +efficient. (On the other hand, the availability of these +special values provides a convenient way to test the general +algorithm.) +""" + +# Harmonic numbers are just shifted digamma functions +# We should calculate these exactly when x is an integer +# and when doing so is faster. + +def mpf_harmonic(x, prec, rnd): + if x in (fzero, fnan, finf): + return x + a = mpf_psi0(mpf_add(fone, x, prec+5), prec) + return mpf_add(a, mpf_euler(prec+5, rnd), prec, rnd) + +def mpc_harmonic(z, prec, rnd): + if z[1] == fzero: + return (mpf_harmonic(z[0], prec, rnd), fzero) + a = mpc_psi0(mpc_add_mpf(z, fone, prec+5), prec) + return mpc_add_mpf(a, mpf_euler(prec+5, rnd), prec, rnd) + +def mpf_psi0(x, prec, rnd=round_fast): + """ + Computation of the digamma function (psi function of order 0) + of a real argument. + """ + sign, man, exp, bc = x + wp = prec + 10 + if not man: + if x == finf: return x + if x == fninf or x == fnan: return fnan + if x == fzero or (exp >= 0 and sign): + raise ValueError("polygamma pole") + # Reflection formula + if sign and exp+bc > 3: + c, s = mpf_cos_sin_pi(x, wp) + q = mpf_mul(mpf_div(c, s, wp), mpf_pi(wp), wp) + p = mpf_psi0(mpf_sub(fone, x, wp), wp) + return mpf_sub(p, q, prec, rnd) + # The logarithmic term is accurate enough + if (not sign) and bc + exp > wp: + return mpf_log(mpf_sub(x, fone, wp), prec, rnd) + # Initial recurrence to obtain a large enough x + m = to_int(x) + n = int(0.11*wp) + 2 + s = MPZ_ZERO + x = to_fixed(x, wp) + one = MPZ_ONE << wp + if m < n: + for k in xrange(m, n): + s -= (one << wp) // x + x += one + x -= one + # Logarithmic term + s += to_fixed(mpf_log(from_man_exp(x, -wp, wp), wp), wp) + # Endpoint term in Euler-Maclaurin expansion + s += (one << wp) // (2*x) + # Euler-Maclaurin remainder sum + x2 = (x*x) >> wp + t = one + prev = 0 + k = 1 + while 1: + t = (t*x2) >> wp + bsign, bman, bexp, bbc = mpf_bernoulli(2*k, wp) + offset = (bexp + 2*wp) + if offset >= 0: term = (bman << offset) // (t*(2*k)) + else: term = (bman >> (-offset)) // (t*(2*k)) + if k & 1: s -= term + else: s += term + if k > 2 and term >= prev: + break + prev = term + k += 1 + return from_man_exp(s, -wp, wp, rnd) + +def mpc_psi0(z, prec, rnd=round_fast): + """ + Computation of the digamma function (psi function of order 0) + of a complex argument. + """ + re, im = z + # Fall back to the real case + if im == fzero: + return (mpf_psi0(re, prec, rnd), fzero) + wp = prec + 20 + sign, man, exp, bc = re + # Reflection formula + if sign and exp+bc > 3: + c = mpc_cos_pi(z, wp) + s = mpc_sin_pi(z, wp) + q = mpc_mul_mpf(mpc_div(c, s, wp), mpf_pi(wp), wp) + p = mpc_psi0(mpc_sub(mpc_one, z, wp), wp) + return mpc_sub(p, q, prec, rnd) + # Just the logarithmic term + if (not sign) and bc + exp > wp: + return mpc_log(mpc_sub(z, mpc_one, wp), prec, rnd) + # Initial recurrence to obtain a large enough z + w = to_int(re) + n = int(0.11*wp) + 2 + s = mpc_zero + if w < n: + for k in xrange(w, n): + s = mpc_sub(s, mpc_reciprocal(z, wp), wp) + z = mpc_add_mpf(z, fone, wp) + z = mpc_sub(z, mpc_one, wp) + # Logarithmic and endpoint term + s = mpc_add(s, mpc_log(z, wp), wp) + s = mpc_add(s, mpc_div(mpc_half, z, wp), wp) + # Euler-Maclaurin remainder sum + z2 = mpc_square(z, wp) + t = mpc_one + prev = mpc_zero + k = 1 + eps = mpf_shift(fone, -wp+2) + while 1: + t = mpc_mul(t, z2, wp) + bern = mpf_bernoulli(2*k, wp) + term = mpc_mpf_div(bern, mpc_mul_int(t, 2*k, wp), wp) + s = mpc_sub(s, term, wp) + szterm = mpc_abs(term, 10) + if k > 2 and mpf_le(szterm, eps): + break + prev = term + k += 1 + return s + +# Currently unoptimized +def mpf_psi(m, x, prec, rnd=round_fast): + """ + Computation of the polygamma function of arbitrary integer order + m >= 0, for a real argument x. + """ + if m == 0: + return mpf_psi0(x, prec, rnd=round_fast) + return mpc_psi(m, (x, fzero), prec, rnd)[0] + +def mpc_psi(m, z, prec, rnd=round_fast): + """ + Computation of the polygamma function of arbitrary integer order + m >= 0, for a complex argument z. + """ + if m == 0: + return mpc_psi0(z, prec, rnd) + re, im = z + wp = prec + 20 + sign, man, exp, bc = re + if not man: + if re == finf and im == fzero: + return (fzero, fzero) + if re == fnan: + return fnan + # Recurrence + w = to_int(re) + n = int(0.4*wp + 4*m) + s = mpc_zero + if w < n: + for k in xrange(w, n): + t = mpc_pow_int(z, -m-1, wp) + s = mpc_add(s, t, wp) + z = mpc_add_mpf(z, fone, wp) + zm = mpc_pow_int(z, -m, wp) + z2 = mpc_pow_int(z, -2, wp) + # 1/m*(z+N)^m + integral_term = mpc_div_mpf(zm, from_int(m), wp) + s = mpc_add(s, integral_term, wp) + # 1/2*(z+N)^(-(m+1)) + s = mpc_add(s, mpc_mul_mpf(mpc_div(zm, z, wp), fhalf, wp), wp) + a = m + 1 + b = 2 + k = 1 + # Important: we want to sum up to the *relative* error, + # not the absolute error, because psi^(m)(z) might be tiny + magn = mpc_abs(s, 10) + magn = magn[2]+magn[3] + eps = mpf_shift(fone, magn-wp+2) + while 1: + zm = mpc_mul(zm, z2, wp) + bern = mpf_bernoulli(2*k, wp) + scal = mpf_mul_int(bern, a, wp) + scal = mpf_div(scal, from_int(b), wp) + term = mpc_mul_mpf(zm, scal, wp) + s = mpc_add(s, term, wp) + szterm = mpc_abs(term, 10) + if k > 2 and mpf_le(szterm, eps): + break + #print k, to_str(szterm, 10), to_str(eps, 10) + a *= (m+2*k)*(m+2*k+1) + b *= (2*k+1)*(2*k+2) + k += 1 + # Scale and sign factor + v = mpc_mul_mpf(s, mpf_gamma(from_int(m+1), wp), prec, rnd) + if not (m & 1): + v = mpf_neg(v[0]), mpf_neg(v[1]) + return v + + +#-----------------------------------------------------------------------# +# # +# Riemann zeta function # +# # +#-----------------------------------------------------------------------# + +""" +We use zeta(s) = eta(s) / (1 - 2**(1-s)) and Borwein's approximation + + n-1 + ___ k + -1 \ (-1) (d_k - d_n) + eta(s) ~= ---- ) ------------------ + d_n /___ s + k = 0 (k + 1) +where + k + ___ i + \ (n + i - 1)! 4 + d_k = n ) ---------------. + /___ (n - i)! (2i)! + i = 0 + +If s = a + b*I, the absolute error for eta(s) is bounded by + + 3 (1 + 2|b|) + ------------ * exp(|b| pi/2) + n + (3+sqrt(8)) + +Disregarding the linear term, we have approximately, + + log(err) ~= log(exp(1.58*|b|)) - log(5.8**n) + log(err) ~= 1.58*|b| - log(5.8)*n + log(err) ~= 1.58*|b| - 1.76*n + log2(err) ~= 2.28*|b| - 2.54*n + +So for p bits, we should choose n > (p + 2.28*|b|) / 2.54. + +References: +----------- + +Peter Borwein, "An Efficient Algorithm for the Riemann Zeta Function" +http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P117.ps + +http://en.wikipedia.org/wiki/Dirichlet_eta_function +""" + +borwein_cache = {} + +def borwein_coefficients(n): + if n in borwein_cache: + return borwein_cache[n] + ds = [MPZ_ZERO] * (n+1) + d = MPZ_ONE + s = ds[0] = MPZ_ONE + for i in range(1, n+1): + d = d * 4 * (n+i-1) * (n-i+1) + d //= ((2*i) * ((2*i)-1)) + s += d + ds[i] = s + borwein_cache[n] = ds + return ds + +ZETA_INT_CACHE_MAX_PREC = 1000 +zeta_int_cache = {} + +def mpf_zeta_int(s, prec, rnd=round_fast): + """ + Optimized computation of zeta(s) for an integer s. + """ + wp = prec + 20 + s = int(s) + if s in zeta_int_cache and zeta_int_cache[s][0] >= wp: + return mpf_pos(zeta_int_cache[s][1], prec, rnd) + if s < 2: + if s == 1: + raise ValueError("zeta(1) pole") + if not s: + return mpf_neg(fhalf) + return mpf_div(mpf_bernoulli(-s+1, wp), from_int(s-1), prec, rnd) + # 2^-s term vanishes? + if s >= wp: + return mpf_perturb(fone, 0, prec, rnd) + # 5^-s term vanishes? + elif s >= wp*0.431: + t = one = 1 << wp + t += 1 << (wp - s) + t += one // (MPZ_THREE ** s) + t += 1 << max(0, wp - s*2) + return from_man_exp(t, -wp, prec, rnd) + else: + # Fast enough to sum directly? + # Even better, we use the Euler product (idea stolen from pari) + m = (float(wp)/(s-1) + 1) + if m < 30: + needed_terms = int(2.0**m + 1) + if needed_terms < int(wp/2.54 + 5) / 10: + t = fone + for k in list_primes(needed_terms): + #print k, needed_terms + powprec = int(wp - s*math.log(k,2)) + if powprec < 2: + break + a = mpf_sub(fone, mpf_pow_int(from_int(k), -s, powprec), wp) + t = mpf_mul(t, a, wp) + return mpf_div(fone, t, wp) + # Use Borwein's algorithm + n = int(wp/2.54 + 5) + d = borwein_coefficients(n) + t = MPZ_ZERO + s = MPZ(s) + for k in xrange(n): + t += (((-1)**k * (d[k] - d[n])) << wp) // (k+1)**s + t = (t << wp) // (-d[n]) + t = (t << wp) // ((1 << wp) - (1 << (wp+1-s))) + if (s in zeta_int_cache and zeta_int_cache[s][0] < wp) or (s not in zeta_int_cache): + zeta_int_cache[s] = (wp, from_man_exp(t, -wp-wp)) + return from_man_exp(t, -wp-wp, prec, rnd) + +def mpf_zeta(s, prec, rnd=round_fast, alt=0): + sign, man, exp, bc = s + if not man: + if s == fzero: + if alt: + return fhalf + else: + return mpf_neg(fhalf) + if s == finf: + return fone + return fnan + wp = prec + 20 + # First term vanishes? + if (not sign) and (exp + bc > (math.log(wp,2) + 2)): + return mpf_perturb(fone, alt, prec, rnd) + # Optimize for integer arguments + elif exp >= 0: + if alt: + if s == fone: + return mpf_ln2(prec, rnd) + z = mpf_zeta_int(to_int(s), wp, negative_rnd[rnd]) + q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) + return mpf_mul(z, q, prec, rnd) + else: + return mpf_zeta_int(to_int(s), prec, rnd) + # Negative: use the reflection formula + # Borwein only proves the accuracy bound for x >= 1/2. However, based on + # tests, the accuracy without reflection is quite good even some distance + # to the left of 1/2. XXX: verify this. + if sign: + # XXX: could use the separate refl. formula for Dirichlet eta + if alt: + q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) + return mpf_mul(mpf_zeta(s, wp), q, prec, rnd) + # XXX: -1 should be done exactly + y = mpf_sub(fone, s, 10*wp) + a = mpf_gamma(y, wp) + b = mpf_zeta(y, wp) + c = mpf_sin_pi(mpf_shift(s, -1), wp) + wp2 = wp + (exp+bc) + pi = mpf_pi(wp+wp2) + d = mpf_div(mpf_pow(mpf_shift(pi, 1), s, wp2), pi, wp2) + return mpf_mul(a,mpf_mul(b,mpf_mul(c,d,wp),wp),prec,rnd) + + # Near pole + r = mpf_sub(fone, s, wp) + asign, aman, aexp, abc = mpf_abs(r) + pole_dist = -2*(aexp+abc) + if pole_dist > wp: + if alt: + return mpf_ln2(prec, rnd) + else: + q = mpf_neg(mpf_div(fone, r, wp)) + return mpf_add(q, mpf_euler(wp), prec, rnd) + else: + wp += max(0, pole_dist) + + t = MPZ_ZERO + #wp += 16 - (prec & 15) + # Use Borwein's algorithm + n = int(wp/2.54 + 5) + d = borwein_coefficients(n) + t = MPZ_ZERO + sf = to_fixed(s, wp) + for k in xrange(n): + u = from_man_exp(-sf*log_int_fixed(k+1, wp), -2*wp, wp) + esign, eman, eexp, ebc = mpf_exp(u, wp) + offset = eexp + wp + if offset >= 0: + w = ((d[k] - d[n]) * eman) << offset + else: + w = ((d[k] - d[n]) * eman) >> (-offset) + if k & 1: + t -= w + else: + t += w + t = t // (-d[n]) + t = from_man_exp(t, -wp, wp) + if alt: + return mpf_pos(t, prec, rnd) + else: + q = mpf_sub(fone, mpf_pow(ftwo, mpf_sub(fone, s, wp), wp), wp) + return mpf_div(t, q, prec, rnd) + +def mpc_zeta(s, prec, rnd=round_fast, alt=0, force=False): + re, im = s + if im == fzero: + return mpf_zeta(re, prec, rnd, alt), fzero + + # slow for large s + if (not force) and mpf_gt(mpc_abs(s, 10), from_int(prec)): + raise NotImplementedError + + wp = prec + 20 + + # Near pole + r = mpc_sub(mpc_one, s, wp) + asign, aman, aexp, abc = mpc_abs(r, 10) + pole_dist = -2*(aexp+abc) + if pole_dist > wp: + if alt: + q = mpf_ln2(wp) + y = mpf_mul(q, mpf_euler(wp), wp) + g = mpf_shift(mpf_mul(q, q, wp), -1) + g = mpf_sub(y, g) + z = mpc_mul_mpf(r, mpf_neg(g), wp) + z = mpc_add_mpf(z, q, wp) + return mpc_pos(z, prec, rnd) + else: + q = mpc_neg(mpc_div(mpc_one, r, wp)) + q = mpc_add_mpf(q, mpf_euler(wp), wp) + return mpc_pos(q, prec, rnd) + else: + wp += max(0, pole_dist) + + # Reflection formula. To be rigorous, we should reflect to the left of + # re = 1/2 (see comments for mpf_zeta), but this leads to unnecessary + # slowdown for interesting values of s + if mpf_lt(re, fzero): + # XXX: could use the separate refl. formula for Dirichlet eta + if alt: + q = mpc_sub(mpc_one, mpc_pow(mpc_two, mpc_sub(mpc_one, s, wp), + wp), wp) + return mpc_mul(mpc_zeta(s, wp), q, prec, rnd) + # XXX: -1 should be done exactly + y = mpc_sub(mpc_one, s, 10*wp) + a = mpc_gamma(y, wp) + b = mpc_zeta(y, wp) + c = mpc_sin_pi(mpc_shift(s, -1), wp) + rsign, rman, rexp, rbc = re + isign, iman, iexp, ibc = im + mag = max(rexp+rbc, iexp+ibc) + wp2 = wp + mag + pi = mpf_pi(wp+wp2) + pi2 = (mpf_shift(pi, 1), fzero) + d = mpc_div_mpf(mpc_pow(pi2, s, wp2), pi, wp2) + return mpc_mul(a,mpc_mul(b,mpc_mul(c,d,wp),wp),prec,rnd) + n = int(wp/2.54 + 5) + n += int(0.9*abs(to_int(im))) + d = borwein_coefficients(n) + ref = to_fixed(re, wp) + imf = to_fixed(im, wp) + tre = MPZ_ZERO + tim = MPZ_ZERO + one = MPZ_ONE << wp + one_2wp = MPZ_ONE << (2*wp) + critical_line = re == fhalf + for k in xrange(n): + log = log_int_fixed(k+1, wp) + # A square root is much cheaper than an exp + if critical_line: + w = one_2wp // sqrt_fixed((k+1) << wp, wp) + else: + w = to_fixed(mpf_exp(from_man_exp(-ref*log, -2*wp), wp), wp) + if k & 1: + w *= (d[n] - d[k]) + else: + w *= (d[k] - d[n]) + wre, wim = mpf_cos_sin(from_man_exp(-imf * log, -2*wp), wp) + tre += (w * to_fixed(wre, wp)) >> wp + tim += (w * to_fixed(wim, wp)) >> wp + tre //= (-d[n]) + tim //= (-d[n]) + tre = from_man_exp(tre, -wp, wp) + tim = from_man_exp(tim, -wp, wp) + if alt: + return mpc_pos((tre, tim), prec, rnd) + else: + q = mpc_sub(mpc_one, mpc_pow(mpc_two, r, wp), wp) + return mpc_div((tre, tim), q, prec, rnd) + +def mpf_altzeta(s, prec, rnd=round_fast): + return mpf_zeta(s, prec, rnd, 1) + +def mpc_altzeta(s, prec, rnd=round_fast): + return mpc_zeta(s, prec, rnd, 1) + +# Not optimized currently +mpf_zetasum = None + +def exp_fixed_prod(x, wp): + u = from_man_exp(x, -2*wp, wp) + esign, eman, eexp, ebc = mpf_exp(u, wp) + offset = eexp + wp + if offset >= 0: + return eman << offset + else: + return eman >> (-offset) + +def cos_sin_fixed_prod(x, wp): + cos, sin = mpf_cos_sin(from_man_exp(x, -2*wp), wp) + sign, man, exp, bc = cos + if sign: + man = -man + offset = exp + wp + if offset >= 0: + cos = man << offset + else: + cos = man >> (-offset) + sign, man, exp, bc = sin + if sign: + man = -man + offset = exp + wp + if offset >= 0: + sin = man << offset + else: + sin = man >> (-offset) + return cos, sin + +def pow_fixed(x, n, wp): + if n == 1: + return x + y = MPZ_ONE << wp + while n: + if n & 1: + y = (y*x) >> wp + n -= 1 + x = (x*x) >> wp + n //= 2 + return y + +def mpc_zetasum(s, a, n, derivatives, reflect, prec): + """ + Fast version of mp._zetasum, assuming s = complex, a = integer. + """ + + wp = prec + 10 + have_derivatives = derivatives != [0] + have_one_derivative = len(derivatives) == 1 + + # parse s + sre, sim = s + critical_line = (sre == fhalf) + sre = to_fixed(sre, wp) + sim = to_fixed(sim, wp) + + maxd = max(derivatives) + if not have_one_derivative: + derivatives = range(maxd+1) + + # x_d = 0, y_d = 0 + xre = [MPZ_ZERO for d in derivatives] + xim = [MPZ_ZERO for d in derivatives] + if reflect: + yre = [MPZ_ZERO for d in derivatives] + yim = [MPZ_ZERO for d in derivatives] + else: + yre = yim = [] + + one = MPZ_ONE << wp + one_2wp = MPZ_ONE << (2*wp) + + for w in xrange(a, a+n+1): + log = log_int_fixed(w, wp) + cos, sin = cos_sin_fixed_prod(-sim*log, wp) + if critical_line: + u = one_2wp // sqrt_fixed(w << wp, wp) + else: + u = exp_fixed_prod(-sre*log, wp) + xterm_re = (u * cos) >> wp + xterm_im = (u * sin) >> wp + if reflect: + reciprocal = (one_2wp // (u*w)) + yterm_re = (reciprocal * cos) >> wp + yterm_im = (reciprocal * sin) >> wp + + if have_derivatives: + if have_one_derivative: + log = pow_fixed(log, maxd, wp) + xre[0] += (xterm_re * log) >> wp + xim[0] += (xterm_im * log) >> wp + if reflect: + yre[0] += (yterm_re * log) >> wp + yim[0] += (yterm_im * log) >> wp + else: + t = MPZ_ONE << wp + for d in derivatives: + xre[d] += (xterm_re * t) >> wp + xim[d] += (xterm_im * t) >> wp + if reflect: + yre[d] += (yterm_re * t) >> wp + yim[d] += (yterm_im * t) >> wp + t = (t * log) >> wp + else: + xre[0] += xterm_re + xim[0] += xterm_im + if reflect: + yre[0] += yterm_re + yim[0] += yterm_im + if have_derivatives: + if have_one_derivative: + if maxd % 2: + xre[0] = -xre[0] + xim[0] = -xim[0] + if reflect: + yre[0] = -yre[0] + yim[0] = -yim[0] + else: + xre = [(-1)**d * xre[d] for d in derivatives] + xim = [(-1)**d * xim[d] for d in derivatives] + if reflect: + yre = [(-1)**d * yre[d] for d in derivatives] + yim = [(-1)**d * yim[d] for d in derivatives] + xs = [(from_man_exp(xa, -wp, prec, 'n'), from_man_exp(xb, -wp, prec, 'n')) + for (xa, xb) in zip(xre, xim)] + ys = [(from_man_exp(ya, -wp, prec, 'n'), from_man_exp(yb, -wp, prec, 'n')) + for (ya, yb) in zip(yre, yim)] + return xs, ys diff --git a/compiler/gdsMill/mpmath/libmp/libelefun.py b/compiler/gdsMill/mpmath/libmp/libelefun.py new file mode 100644 index 00000000..32e6eefd --- /dev/null +++ b/compiler/gdsMill/mpmath/libmp/libelefun.py @@ -0,0 +1,1595 @@ +""" +This module implements computation of elementary transcendental +functions (powers, logarithms, trigonometric and hyperbolic +functions, inverse trigonometric and hyperbolic) for real +floating-point numbers. + +For complex and interval implementations of the same functions, +see libmpc and libmpi. + +""" + +import math +from bisect import bisect + +from backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_FIVE + +from libmpf import ( + round_floor, round_ceiling, round_down, round_up, + round_nearest, round_fast, + ComplexResult, + bitcount, bctable, lshift, rshift, giant_steps, sqrt_fixed, + from_int, to_int, from_man_exp, to_fixed, to_float, from_float, + normalize, + fzero, fone, fnone, fhalf, finf, fninf, fnan, + mpf_cmp, mpf_sign, mpf_abs, + mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, mpf_div, mpf_shift, + mpf_rdiv_int, mpf_pow_int, mpf_sqrt, + reciprocal_rnd, negative_rnd, mpf_perturb, + isqrt_fast +) + +from libintmath import ifib + + +#----------------------------------------------------------------------------# +# # +# Elementary mathematical constants # +# # +#----------------------------------------------------------------------------# + +def constant_memo(f): + """ + Decorator for caching computed values of mathematical + constants. This decorator should be applied to a + function taking a single argument prec as input and + returning a fixed-point value with the given precision. + """ + f.memo_prec = -1 + f.memo_val = None + def g(prec, **kwargs): + memo_prec = f.memo_prec + if prec <= memo_prec: + return f.memo_val >> (memo_prec-prec) + newprec = int(prec*1.05+10) + f.memo_val = f(newprec, **kwargs) + f.memo_prec = newprec + return f.memo_val >> (newprec-prec) + g.__name__ = f.__name__ + g.__doc__ = f.__doc__ + return g + +def def_mpf_constant(fixed): + """ + Create a function that computes the mpf value for a mathematical + constant, given a function that computes the fixed-point value. + + Assumptions: the constant is positive and has magnitude ~= 1; + the fixed-point function rounds to floor. + """ + def f(prec, rnd=round_fast): + wp = prec + 20 + v = fixed(wp) + if rnd in (round_up, round_ceiling): + v += 1 + return normalize(0, v, -wp, bitcount(v), prec, rnd) + f.__doc__ = fixed.__doc__ + return f + +def bsp_acot(q, a, b, hyperbolic): + if b - a == 1: + a1 = MPZ(2*a + 3) + if hyperbolic or a&1: + return MPZ_ONE, a1 * q**2, a1 + else: + return -MPZ_ONE, a1 * q**2, a1 + m = (a+b)//2 + p1, q1, r1 = bsp_acot(q, a, m, hyperbolic) + p2, q2, r2 = bsp_acot(q, m, b, hyperbolic) + return q2*p1 + r1*p2, q1*q2, r1*r2 + +# the acoth(x) series converges like the geometric series for x^2 +# N = ceil(p*log(2)/(2*log(x))) +def acot_fixed(a, prec, hyperbolic): + """ + Compute acot(a) or acoth(a) for an integer a with binary splitting; see + http://numbers.computation.free.fr/Constants/Algorithms/splitting.html + """ + N = int(0.35 * prec/math.log(a) + 20) + p, q, r = bsp_acot(a, 0,N, hyperbolic) + return ((p+q)<> extraprec) + +# Logarithms of integers are needed for various computations involving +# logarithms, powers, radix conversion, etc + +@constant_memo +def ln2_fixed(prec): + """ + Computes ln(2). This is done with a hyperbolic Machin-type formula, + with binary splitting at high precision. + """ + return machin([(18, 26), (-2, 4801), (8, 8749)], prec, True) + +@constant_memo +def ln10_fixed(prec): + """ + Computes ln(10). This is done with a hyperbolic Machin-type formula. + """ + return machin([(46, 31), (34, 49), (20, 161)], prec, True) + + +""" +For computation of pi, we use the Chudnovsky series: + + oo + ___ k + 1 \ (-1) (6 k)! (A + B k) + ----- = ) ----------------------- + 12 pi /___ 3 3k+3/2 + (3 k)! (k!) C + k = 0 + +where A, B, and C are certain integer constants. This series adds roughly +14 digits per term. Note that C^(3/2) can be extracted so that the +series contains only rational terms. This makes binary splitting very +efficient. + +The recurrence formulas for the binary splitting were taken from +ftp://ftp.gmplib.org/pub/src/gmp-chudnovsky.c + +Previously, Machin's formula was used at low precision and the AGM iteration +was used at high precision. However, the Chudnovsky series is essentially as +fast as the Machin formula at low precision and in practice about 3x faster +than the AGM at high precision (despite theoretically having a worse +asymptotic complexity), so there is no reason not to use it in all cases. + +""" + +# Constants in Chudnovsky's series +CHUD_A = MPZ(13591409) +CHUD_B = MPZ(545140134) +CHUD_C = MPZ(640320) +CHUD_D = MPZ(12) + +def bs_chudnovsky(a, b, level, verbose): + """ + Computes the sum from a to b of the series in the Chudnovsky + formula. Returns g, p, q where p/q is the sum as an exact + fraction and g is a temporary value used to save work + for recursive calls. + """ + if b-a == 1: + g = MPZ((6*b-5)*(2*b-1)*(6*b-1)) + p = b**3 * CHUD_C**3 // 24 + q = (-1)**b * g * (CHUD_A+CHUD_B*b) + else: + if verbose and level < 4: + print " binary splitting", a, b + mid = (a+b)//2 + g1, p1, q1 = bs_chudnovsky(a, mid, level+1, verbose) + g2, p2, q2 = bs_chudnovsky(mid, b, level+1, verbose) + p = p1*p2 + g = g1*g2 + q = q1*p2 + q2*g1 + return g, p, q + +@constant_memo +def pi_fixed(prec, verbose=False, verbose_base=None): + """ + Compute floor(pi * 2**prec) as a big integer. + + This is done using Chudnovsky's series (see comments in + libelefun.py for details). + """ + # The Chudnovsky series gives 14.18 digits per term + N = int(prec/3.3219280948/14.181647462 + 2) + if verbose: + print "binary splitting with N =", N + g, p, q = bs_chudnovsky(0, N, 0, verbose) + sqrtC = isqrt_fast(CHUD_C<<(2*prec)) + v = p*CHUD_C*sqrtC//((q+CHUD_A*p)*CHUD_D) + return v + +def degree_fixed(prec): + return pi_fixed(prec)//180 + +def bspe(a, b): + """ + Sum series for exp(1)-1 between a, b, returning the result + as an exact fraction (p, q). + """ + if b-a == 1: + return MPZ_ONE, MPZ(b) + m = (a+b)//2 + p1, q1 = bspe(a, m) + p2, q2 = bspe(m, b) + return p1*q2+p2, q1*q2 + +@constant_memo +def e_fixed(prec): + """ + Computes exp(1). This is done using the ordinary Taylor series for + exp, with binary splitting. For a description of the algorithm, + see: + + http://numbers.computation.free.fr/Constants/ + Algorithms/splitting.html + """ + # Slight overestimate of N needed for 1/N! < 2**(-prec) + # This could be tightened for large N. + N = int(1.1*prec/math.log(prec) + 20) + p, q = bspe(0,N) + return ((p+q)<> 11 + +mpf_phi = def_mpf_constant(phi_fixed) +mpf_pi = def_mpf_constant(pi_fixed) +mpf_e = def_mpf_constant(e_fixed) +mpf_degree = def_mpf_constant(degree_fixed) +mpf_ln2 = def_mpf_constant(ln2_fixed) +mpf_ln10 = def_mpf_constant(ln10_fixed) + + +#----------------------------------------------------------------------------# +# # +# Powers # +# # +#----------------------------------------------------------------------------# + +def mpf_pow(s, t, prec, rnd=round_fast): + """ + Compute s**t. Raises ComplexResult if s is negative and t is + fractional. + """ + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + if ssign and texp < 0: + raise ComplexResult("negative number raised to a fractional power") + if texp >= 0: + return mpf_pow_int(s, (-1)**tsign * (tman<> pbc)] + if pbc > workprec: + pm = pm >> (pbc-workprec) + pe += pbc - workprec + pbc = workprec + n -= 1 + if not n: + break + y = y*y + exp = exp+exp + bc = bc + bc - 2 + bc = bc + bctable[int(y >> bc)] + if bc > workprec: + y = y >> (bc-workprec) + exp += bc - workprec + bc = workprec + n = n // 2 + return pm, pe + +# froot(s, n, prec, rnd) computes the real n-th root of a +# positive mpf tuple s. +# To compute the root we start from a 50-bit estimate for r +# generated with ordinary floating-point arithmetic, and then refine +# the value to full accuracy using the iteration + +# 1 / y \ +# r = --- | (n-1) * r + ---------- | +# n+1 n \ n r_n**(n-1) / + +# which is simply Newton's method applied to the equation r**n = y. +# With giant_steps(start, prec+extra) = [p0,...,pm, prec+extra] +# and y = man * 2**-shift one has +# (man * 2**exp)**(1/n) = +# y**(1/n) * 2**(start-prec/n) * 2**(p0-start) * ... * 2**(prec+extra-pm) * +# 2**((exp+shift-(n-1)*prec)/n -extra)) +# The last factor is accounted for in the last line of froot. + +def nthroot_fixed(y, n, prec, exp1): + start = 50 + try: + y1 = rshift(y, prec - n*start) + r = MPZ(int(y1**(1.0/n))) + except OverflowError: + y1 = from_int(y1, start) + fn = from_int(n) + fn = mpf_rdiv_int(1, fn, start) + r = mpf_pow(y1, fn, start) + r = to_int(r) + extra = 10 + extra1 = n + prevp = start + for p in giant_steps(start, prec+extra): + pm, pe = int_pow_fixed(r, n-1, prevp) + r2 = rshift(pm, (n-1)*prevp - p - pe - extra1) + B = lshift(y, 2*p-prec+extra1)//r2 + r = (B + (n-1) * lshift(r, p-prevp))//n + prevp = p + return r + +def mpf_nthroot(s, n, prec, rnd=round_fast): + """nth-root of a positive number + + Use the Newton method when faster, otherwise use x**(1/n) + """ + sign, man, exp, bc = s + if sign: + raise ComplexResult("nth root of a negative number") + if not man: + if s == fnan: + return fnan + if s == fzero: + if n > 0: + return fzero + if n == 0: + return fone + return finf + # Infinity + if not n: + return fnan + if n < 0: + return fzero + return finf + flag_inverse = False + if n < 2: + if n == 0: + return fone + if n == 1: + return mpf_pos(s, prec, rnd) + if n == -1: + return mpf_div(fone, s, prec, rnd) + # n < 0 + rnd = reciprocal_rnd[rnd] + flag_inverse = True + extra_inverse = 5 + prec += extra_inverse + n = -n + if n > 20 and (n >= 20000 or prec < int(233 + 28.3 * n**0.62)): + prec2 = prec + 10 + fn = from_int(n) + nth = mpf_rdiv_int(1, fn, prec2) + r = mpf_pow(s, nth, prec2, rnd) + s = normalize(r[0], r[1], r[2], r[3], prec, rnd) + if flag_inverse: + return mpf_div(fone, s, prec-extra_inverse, rnd) + else: + return s + # Convert to a fixed-point number with prec2 bits. + prec2 = prec + 2*n - (prec%n) + # a few tests indicate that + # for 10 < n < 10**4 a bit more precision is needed + if n > 10: + prec2 += prec2//10 + prec2 = prec2 - prec2%n + # Mantissa may have more bits than we need. Trim it down. + shift = bc - prec2 + # Adjust exponents to make prec2 and exp+shift multiples of n. + sign1 = 0 + es = exp+shift + if es < 0: + sign1 = 1 + es = -es + if sign1: + shift += es%n + else: + shift -= es%n + man = rshift(man, shift) + extra = 10 + exp1 = ((exp+shift-(n-1)*prec2)//n) - extra + rnd_shift = 0 + if flag_inverse: + if rnd == 'u' or rnd == 'c': + rnd_shift = 1 + else: + if rnd == 'd' or rnd == 'f': + rnd_shift = 1 + man = nthroot_fixed(man+rnd_shift, n, prec2, exp1) + s = from_man_exp(man, exp1, prec, rnd) + if flag_inverse: + return mpf_div(fone, s, prec-extra_inverse, rnd) + else: + return s + +def mpf_cbrt(s, prec, rnd=round_fast): + """cubic root of a positive number""" + return mpf_nthroot(s, 3, prec, rnd) + +#----------------------------------------------------------------------------# +# # +# Logarithms # +# # +#----------------------------------------------------------------------------# + +# Fast sequential integer logarithms are required for various series +# computations related to zeta functions, so we cache them +# TODO: can this be done better? +MAX_LOG_INT_CACHE = 2000 + +log_int_cache = {} + +def log_int_fixed(n, prec): + if n in log_int_cache: + value, vprec = log_int_cache[n] + if vprec >= prec: + return value >> (vprec - prec) + extra = 30 + vprec = prec + extra + v = to_fixed(mpf_log(from_int(n), vprec+5), vprec) + if n < MAX_LOG_INT_CACHE: + log_int_cache[n] = (v, vprec) + return v >> extra + +# Use Taylor series with caching up to this prec +LOG_TAYLOR_PREC = 2500 + +# Cache log values in steps of size 2^-N +LOG_TAYLOR_SHIFT = 9 + +# prec/size ratio of x for fastest convergence in AGM formula +LOG_AGM_MAG_PREC_RATIO = 20 + +log_taylor_cache = {} + +# ~= next power of two + 20 +cache_prec_steps = [22,22] +for k in xrange(1, bitcount(LOG_TAYLOR_PREC)+1): + cache_prec_steps += [min(2**k,LOG_TAYLOR_PREC)+20] * 2**(k-1) + +def agm_fixed(a, b, prec): + """ + Fixed-point computation of agm(a,b), assuming + a, b both close to unit magnitude. + """ + i = 0 + while 1: + anew = (a+b)>>1 + if i > 4 and abs(a-anew) < 8: + return a + b = isqrt_fast(a*b) + a = anew + i += 1 + return a + +def log_agm(x, prec): + """ + Fixed-point computation of -log(x) = log(1/x), suitable + for large precision. It is required that 0 < x < 1. The + algorithm used is the Sasaki-Kanada formula + + -log(x) = pi/agm(theta2(x)^2,theta3(x)^2). [1] + + For faster convergence in the theta functions, x should + be chosen closer to 0. + + Guard bits must be added by the caller. + + HYPOTHESIS: if x = 2^(-n), n bits need to be added to + account for the truncation to a fixed-point number, + and this is the only significant cancellation error. + + The number of bits lost to roundoff is small and can be + considered constant. + + [1] Richard P. Brent, "Fast Algorithms for High-Precision + Computation of Elementary Functions (extended abstract)", + http://wwwmaths.anu.edu.au/~brent/pd/RNC7-Brent.pdf + + """ + x2 = (x*x) >> prec + # Compute jtheta2(x)**2 + s = a = b = x2 + while a: + b = (b*x2) >> prec + a = (a*b) >> prec + s += a + s += (MPZ_ONE<>(prec-2) + s = (s*isqrt_fast(x<>prec + # Compute jtheta3(x)**2 + t = a = b = x + while a: + b = (b*x2) >> prec + a = (a*b) >> prec + t += a + t = (MPZ_ONE<>prec + # Final formula + p = agm_fixed(s, t, prec) + return (pi_fixed(prec) << prec) // p + +def log_taylor(x, prec, r=0): + """ + Fixed-point calculation of log(x). It is assumed that x is close + enough to 1 for the Taylor series to converge quickly. Convergence + can be improved by specifying r > 0 to compute + log(x^(1/2^r))*2^r, at the cost of performing r square roots. + + The caller must provide sufficient guard bits. + """ + for i in xrange(r): + x = isqrt_fast(x<> prec + v4 = (v2*v2) >> prec + s0 = v + s1 = v//3 + v = (v*v4) >> prec + k = 5 + while v: + s0 += v // k + k += 2 + s1 += v // k + v = (v*v4) >> prec + k += 2 + s1 = (s1*v2) >> prec + s = (s0+s1) << (1+r) + if sign: + return -s + return s + +def log_taylor_cached(x, prec): + """ + Fixed-point computation of log(x), assuming x in (0.5, 2) + and prec <= LOG_TAYLOR_PREC. + """ + n = x >> (prec-LOG_TAYLOR_SHIFT) + cached_prec = cache_prec_steps[prec] + dprec = cached_prec - prec + if (n, cached_prec) in log_taylor_cache: + a, log_a = log_taylor_cache[n, cached_prec] + else: + a = n << (cached_prec - LOG_TAYLOR_SHIFT) + log_a = log_taylor(a, cached_prec, 8) + log_taylor_cache[n, cached_prec] = (a, log_a) + a >>= dprec + log_a >>= dprec + u = ((x - a) << prec) // a + v = (u << prec) // ((MPZ_TWO << prec) + u) + v2 = (v*v) >> prec + v4 = (v2*v2) >> prec + s0 = v + s1 = v//3 + v = (v*v4) >> prec + k = 5 + while v: + s0 += v//k + k += 2 + s1 += v//k + v = (v*v4) >> prec + k += 2 + s1 = (s1*v2) >> prec + s = (s0+s1) << 1 + return log_a + s + +def mpf_log(x, prec, rnd=round_fast): + """ + Compute the natural logarithm of the mpf value x. If x is negative, + ComplexResult is raised. + """ + sign, man, exp, bc = x + #------------------------------------------------------------------ + # Handle special values + if not man: + if x == fzero: return fninf + if x == finf: return finf + if x == fnan: return fnan + if sign: + raise ComplexResult("logarithm of a negative number") + wp = prec + 20 + #------------------------------------------------------------------ + # Handle log(2^n) = log(n)*2. + # Here we catch the only possible exact value, log(1) = 0 + if man == 1: + if not exp: + return fzero + return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) + mag = exp+bc + abs_mag = abs(mag) + #------------------------------------------------------------------ + # Handle x = 1+eps, where log(x) ~ x. We need to check for + # cancellation when moving to fixed-point math and compensate + # by increasing the precision. Note that abs_mag in (0, 1) <=> + # 0.5 < x < 2 and x != 1 + if abs_mag <= 1: + # Calculate t = x-1 to measure distance from 1 in bits + tsign = 1-abs_mag + if tsign: + tman = (MPZ_ONE< wp: + t = normalize(tsign, tman, abs_mag-bc, tbc, tbc, 'n') + return mpf_perturb(t, tsign, prec, rnd) + else: + wp += cancellation + # TODO: if close enough to 1, we could use Taylor series + # even in the AGM precision range, since the Taylor series + # converges rapidly + #------------------------------------------------------------------ + # Another special case: + # n*log(2) is a good enough approximation + if abs_mag > 10000: + if bitcount(abs_mag) > wp: + return from_man_exp(exp*ln2_fixed(wp), -wp, prec, rnd) + #------------------------------------------------------------------ + # General case. + # Perform argument reduction using log(x) = log(x*2^n) - n*log(2): + # If we are in the Taylor precision range, choose magnitude 0 or 1. + # If we are in the AGM precision range, choose magnitude -m for + # some large m; benchmarking on one machine showed m = prec/20 to be + # optimal between 1000 and 100,000 digits. + if wp <= LOG_TAYLOR_PREC: + m = log_taylor_cached(lshift(man, wp-bc), wp) + if mag: + m += mag*ln2_fixed(wp) + else: + optimal_mag = -wp//LOG_AGM_MAG_PREC_RATIO + n = optimal_mag - mag + x = mpf_shift(x, n) + wp += (-optimal_mag) + m = -log_agm(to_fixed(x, wp), wp) + m -= n*ln2_fixed(wp) + return from_man_exp(m, -wp, prec, rnd) + +def mpf_log_hypot(a, b, prec, rnd): + """ + Computes log(sqrt(a^2+b^2)) accurately. + """ + # If either a or b is inf/nan/0, assume it to be a + if not b[1]: + a, b = b, a + # a is inf/nan/0 + if not a[1]: + # both are inf/nan/0 + if not b[1]: + if a == b == fzero: + return fninf + if fnan in (a, b): + return fnan + # at least one term is (+/- inf)^2 + return finf + # only a is inf/nan/0 + if a == fzero: + # log(sqrt(0+b^2)) = log(|b|) + return mpf_log(mpf_abs(b), prec, rnd) + if a == fnan: + return fnan + return finf + # Exact + a2 = mpf_mul(a,a) + b2 = mpf_mul(b,b) + extra = 20 + # Not exact + h2 = mpf_add(a2, b2, prec+extra) + cancelled = mpf_add(h2, fnone, 10) + mag_cancelled = cancelled[2]+cancelled[3] + # Just redo the sum exactly if necessary (could be smarter + # and avoid memory allocation when a or b is precisely 1 + # and the other is tiny...) + if cancelled == fzero or mag_cancelled < -extra//2: + h2 = mpf_add(a2, b2, prec+extra-min(a2[2],b2[2])) + return mpf_shift(mpf_log(h2, prec, rnd), -1) + + +#----------------------------------------------------------------------------# +# # +# Exponential function # +# # +#----------------------------------------------------------------------------# + +# The exponential function has a rapidly convergent Maclaurin series: +# +# exp(x) = 1 + x + x**2/2! + x**3/3! + x**4/4! + ... +# +# The series can be summed very easily using fixed-point arithmetic. +# The convergence can be improved further, using a trick due to +# Richard P. Brent: instead of computing exp(x) directly, we choose a +# small integer r (say, r=10) and compute exp(x/2**r)**(2**r). + +# The optimal value for r depends on the Python platform, the magnitude +# of x and the target precision, and has to be estimated from +# experimental timings. One test with x ~= 0.3 showed that +# r = 2.2*prec**0.42 gave a good fit to the optimal values for r for +# prec between 1 and 10000 bits, on one particular machine. + +# This optimization makes the summation about twice as fast at +# low precision levels and much faster at high precision +# (roughly five times faster at 1000 decimal digits). + +# If |x| is very large, we first rewrite it as t + n*log(2) with the +# integer n chosen such that |t| <= log(2), and then calculate +# exp(x) as exp(t)*(2**n), using the Maclaurin series for exp(t) +# (the multiplication by 2**n just amounts to shifting the exponent). + +# For very high precision use the newton method to compute exp from +# log_agm; for |x| very large or very small use +# exp(x + m) = exp(x) * e**m, m = int(n * math.log(2)) + +# Input: x * 2**prec +# Output: exp(x) * 2**(prec + r) +def exp_series(x, prec, r): + x >>= r + # 1 + x + x^2/2! + x^3/3! + x^4/4! + ... = + # (1 + x^2/2! + ...) + x * (1 + x^2/3! + ...) + s0 = s1 = (MPZ_ONE << prec) + k = 2 + a = x2 = (x * x) >> prec + while a: + a = a // k + s0 += a + k += 1 + a = a // k + s1 += a + a = (a * x2) >> prec + k += 1 + # Calculate s**(2**r) by repeated squaring + s1 = (s1 * x) >> prec + s = s0 + s1 + while r: + s = (s*s) >> prec + r -= 1 + return s + +def exp_series2(x, prec, r): + x >>= r + sign = 0 + if x < 0: + sign = 1 + x = -x + x2 = (x*x) >> prec + if prec < 1500: + s1 = a = x + k = 3 + while a: + a = ((a * x2) >> prec) // (k*(k-1)) + s1 += a + k += 2 + else: + # use Smith's method: + # reduce the number of multiplication summing concurrently J series + # J=4 + # Sinh(x) = + # (x + x^9/9! + ...) + x^2 * (x/3! + x^9/11! + ...) + + # x^4 * (x/5! + x^9/13! + ...) + x^6 * (x/7! + x^9/15! + ...) + J = 4 + ax = [MPZ_ONE << prec, x2] + px = x2 + asum = [x, x//6] + fact = 6 + k = 4 + for j in range(2, J): + px = (px * x2) >> prec + ax.append(px) + fact *= k*(k+1) + asum.append(x//fact) + k += 2 + lx = (ax[-1]*x2) >> prec + p = asum[-1] + while p: + p = (p * lx) >> prec + for j in range(J): + p = p//(k*(k+1)) + asum[j] += p + k += 2 + s1 = 0 + for i in range(1, J): + s1 += ax[i]*asum[i] + s1 = asum[0] + (s1 >> prec) + c1 = isqrt_fast((s1*s1) + (MPZ_ONE<<(2*prec))) + if sign: + s = c1 - s1 + else: + s = c1 + s1 + # Calculate s**(2**r) by repeated squaring + while r: + s = (s*s) >> prec + r -= 1 + return s + +# use the fourth order newton method, with step +# r = r + r * (h + h^2/2 + h^3/6 + h$/24) +# at each step the precision is quadrupled. + +def exp_newton(x, prec): + extra = 10 + r = mpf_exp(x, 60) + start = 50 + prevp = start + for p in giant_steps(start, prec+extra, 4): + h = mpf_sub(x, mpf_log(r, p), p) + h2 = mpf_mul(h, h, p) + h3 = mpf_mul(h2, h, p) + h4 = mpf_mul(h2, h2, p) + t = mpf_add(h, mpf_shift(h2, -1), p) + t = mpf_add(t, mpf_div(h3, from_int(6, p), p), p) + t = mpf_add(t, mpf_div(h4, from_int(24, p), p), p) + t = mpf_mul(r, t, p) + r = mpf_add(r, t, p) + return r + +# for precision larger than this limit, for x > 1, use the newton method +LIM_EXP_SERIES2 = 10000 +# when the newton method is used, if x has mag=exp+bc larger than LIM_MAG +# shift it +LIM_MAG = 5 + +# table of values to determine if exp_series2 or exp_newton is faster, +# determined with benchmarking on a PC, with gmpy +ns_exp = [8,9,10,11,12,13,33,66,83,99,132,166,199,232,265,298,332,664] +precs_exp = [43000, 63000, 64000, 64000, 65000, 66000, 72000, 82000, 99000, + 115000, 148000, 190000, 218000, 307000, 363000, 528000, 594000, 1650000] + + +def mpf_exp(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if not exp: + return fone + if x == fninf: + return fzero + return x + mag = bc+exp + # Fast handling e**n. TODO: the best cutoff depends on both the + # size of n and the precision. + if prec > 600 and exp >= 0: + e = mpf_e(prec+10+int(1.45*mag)) + return mpf_pow_int(e, (-1)**sign *(man< 1? + if mag > 1: + lg2 = ln2_fixed(wp) + n, t = divmod(t, lg2) + else: + n = 0 + man = exp_series(t, wp, r) + else: + use_newton = False + # put a bound on exp to avoid infinite recursion in exp_newton + # TODO find a good bound + if wp > LIM_EXP_SERIES2 and exp < 1000: + if mag > 0: + use_newton = True + elif mag <= 0 and -mag <= ns_exp[-1]: + i = bisect(ns_exp, -mag-1) + if i < len(ns_exp): + wp0 = precs_exp[i] + if wp > wp0: + use_newton = True + + if not use_newton: + r = int(0.7 * wp**0.5) + if mag < 0: + r = max(1, r + mag) + wp += r + 20 + t = to_fixed(x, wp) + if mag > 1: + lg2 = ln2_fixed(wp) + n, t = divmod(t, lg2) + else: + n = 0 + man = exp_series2(t, wp, r) + else: + # if x is very small or very large use + # exp(x + m) = exp(x) * e**m + if mag > LIM_MAG: + wp += mag*10 + 100 + n = int(mag * math.log(2)) + 1 + x = mpf_sub(x, from_int(n, wp), wp) + elif mag <= 0: + wp += -mag*10 + 100 + if mag < 0: + n = int(-mag * math.log(2)) + 1 + x = mpf_add(x, from_int(n, wp), wp) + res = exp_newton(x, wp) + sign, man, exp, bc = res + if mag < 0: + t = mpf_pow_int(mpf_e(wp), n, wp) + res = mpf_div(res, t, wp) + sign, man, exp, bc = res + if mag > LIM_MAG: + t = mpf_pow_int(mpf_e(wp), n, wp) + res = mpf_mul(res, t, wp) + sign, man, exp, bc = res + return normalize(sign, man, exp, bc, prec, rnd) + bc = bitcount(man) + return normalize(0, man, int(-wp+n), bc, prec, rnd) + + +#----------------------------------------------------------------------------# +# # +# Trigonometric functions # +# # +#----------------------------------------------------------------------------# + +def sin_taylor(x, prec): + x = MPZ(x) + x2 = (x*x) >> prec + s = a = x + k = 3 + while a: + a = ((a * x2) >> prec) // (k*(1-k)) + s += a + k += 2 + return s + +def cos_taylor(x, prec): + x = MPZ(x) + x2 = (x*x) >> prec + a = c = (MPZ_ONE<> prec) // (k*(1-k)) + c += a + k += 2 + return c + +# Input: x * 2**prec +# Output: c * 2**(prec + r), s * 2**(prec + r) +def expi_series(x, prec, r): + x >>= r + one = MPZ_ONE << prec + x2 = (x*x) >> prec + s = x + a = x + k = 2 + while a: + a = ((a * x2) >> prec) // (-k*(k+1)) + s += a + k += 2 + c = isqrt_fast((MPZ_ONE<<(2*prec)) - (s*s)) + # Calculate (c + j*s)**(2**r) by repeated squaring + for j in range(r): + c, s = (c*c-s*s) >> prec, (2*c*s ) >> prec + return c, s + +def reduce_angle(x, prec): + """ + Let x be a nonzero, finite mpf value defining angle (measured in + radians). Then reduce_trig(x, prec) returns (y, swaps, n) where: + + y = (man, wp) is the reduced angle as a scaled fixed-point + number with precision wp, i.e. a floating-point number with + exponent -wp. The mantissa is positive and has width ~equal + to the input prec. + + swaps = (swap_cos_sin, cos_sign, sin_sign) + Flags indicating the swaps that need to be applied + to (cos(y), sin(y)) to obtain (cos(x), sin(x)) + + n is an integer giving the original quadrant of x + + Calculation of the quadrant + =========================== + + The integer n indices the quadrant of x. That is: + + ... + -pi < x < -pi/2 n = -2 + -pi/2 < x < 0 n = -1 + 0 < x < pi/2 n = 0 + pi/2 < x < pi n = 1 + pi < x < 3*pi/2 n = 2 + 3*pi/2 < x < 2*pi n = 3 + 2*pi < x < 5*pi/2 n = 4 + ... + + Note that n does not wrap around. A quadrant index normalized to + lie in [0, 1, 2, 3] can be found easily later on by computing + n % 4. Keeping the extended information in n is crucial for + interval arithmetic, as it allows one to distinguish between + whether two points of a sine wave lie next to each other on + a monotonic segment or are actually separated by a full + period (or several periods). + + Note also that because is x is guaranteed to be rational, and + all roots of the sine/cosine are irrational, all inequalities are + strict. That is, we can always compute the correct quadrant. + Care is required to do ensure that this is done right. + + Swaps + ===== + + The number y is a reduction of x to the first quadrant. This is + essentially x mod pi/2. In fact, we reduce y further, to the first + octant, by computing pi/2-x if x > pi/4. + + Due to the translation and mirror symmetries of trigonometric + functions, this allows us to compute sin(x) or cos(x) by computing + +/-sin(y) or +/-cos(y). The point, of course, is that if x + is large, the Taylor series for y converges much more quickly + than the one for x. + + """ + sign, man, exp, bc = x + magnitude = exp + bc + + if not man: + return (0, 0), (0, 0, 0), 0 + + # Here we have abs(x) < 0.5. In this case no reduction is necessary. + # TODO: could also handle abs(x) < 1 + if magnitude < 0: + # Quadrant is 0 or -1 + n = -sign + swaps = (0, 0, sign) + fixed_exp = exp + bc - prec + delta = fixed_exp - exp + if delta < 0: + man <<= (-delta) + elif delta > 0: + man >>= delta + y = (man, -fixed_exp) + return y, swaps, n + + i = 0 + while 1: + cancellation_prec = 20 * 2**i + wp = prec + abs(magnitude) + cancellation_prec + pi1 = pi_fixed(wp) + pi2 = pi1 >> 1 + pi4 = pi1 >> 2 + # Find nearest multiple + n, y = divmod(to_fixed(x, wp), pi2) + # Interchange cos/sin ? + if y > pi4: + swap_cos_sin = 1 + y = pi2 - y + else: + swap_cos_sin = 0 + # Now, the catch is that x might be extremely close to a + # multiple of pi/2. This means accuracy is lost, and we may + # even end up in the wrong quadrant, which is bad news + # for interval arithmetic. This effect manifests by the + # fixed-point value of y becoming small. This is easy to check for. + if y >> (prec + magnitude - 10): + n = int(n) + swaps = swap_table[swap_cos_sin^(n%2)][n%4] + return (y>>magnitude, wp-magnitude), swaps, n + i += 1 + +swap_table = ((0,0,0),(0,1,0),(0,1,1),(0,0,1)), ((1,0,0),(1,1,0),(1,1,1),(1,0,1)) + +def calc_cos_sin(which, y, swaps, prec, cos_rnd, sin_rnd): + """ + Simultaneous computation of cos and sin (internal function). + """ + y, wp = y + swap_cos_sin, cos_sign, sin_sign = swaps + + if swap_cos_sin: + which_compute = -which + else: + which_compute = which + + # XXX: assumes no swaps + if not y: + return fone, fzero + + # Tiny nonzero argument + if wp > prec*2 + 30: + y = from_man_exp(y, -wp) + + if swap_cos_sin: + cos_rnd, sin_rnd = sin_rnd, cos_rnd + cos_sign, sin_sign = sin_sign, cos_sign + + if cos_sign: cos = mpf_perturb(fnone, 0, prec, cos_rnd) + else: cos = mpf_perturb(fone, 1, prec, cos_rnd) + if sin_sign: sin = mpf_perturb(mpf_neg(y), 0, prec, sin_rnd) + else: sin = mpf_perturb(y, 1, prec, sin_rnd) + + if swap_cos_sin: + cos, sin = sin, cos + return cos, sin + + # Use standard Taylor series + if prec < 600: + if which_compute == 0: + sin = sin_taylor(y, wp) + # only need to evaluate one of the series + cos = isqrt_fast((MPZ_ONE<<(2*wp)) - sin*sin) + elif which_compute == 1: + sin = 0 + cos = cos_taylor(y, wp) + elif which_compute == -1: + sin = sin_taylor(y, wp) + cos = 0 + # Use exp(i*x) with Brent's trick + else: + r = int(0.137 * prec**0.579) + ep = r+20 + cos, sin = expi_series(y<>= ep + sin >>= ep + + if swap_cos_sin: + cos, sin = sin, cos + + if cos_rnd is not round_nearest: + # Round and set correct signs + # XXX: this logic needs a second look + ONE = MPZ_ONE << wp + if cos_sign: + cos += (-1)**(cos_rnd in (round_ceiling, round_down)) + cos = min(ONE, cos) + else: + cos += (-1)**(cos_rnd in (round_ceiling, round_up)) + cos = min(ONE, cos) + if sin_sign: + sin += (-1)**(sin_rnd in (round_ceiling, round_down)) + sin = min(ONE, sin) + else: + sin += (-1)**(sin_rnd in (round_ceiling, round_up)) + sin = min(ONE, sin) + + if which != -1: + cos = normalize(cos_sign, cos, -wp, bitcount(cos), prec, cos_rnd) + if which != 1: + sin = normalize(sin_sign, sin, -wp, bitcount(sin), prec, sin_rnd) + + return cos, sin + +def mpf_cos_sin(x, prec, rnd=round_fast, which=0): + """ + Computes (cos(x), sin(x)). The parameter 'which' can disable + evaluation of either cos or sin: + + 0 -- return (cos(x), sin(x), n) + 1 -- return (cos(x), -, n) + -1 -- return (-, sin(x), n) + + If only one function is wanted, this is slightly + faster at low precision. + """ + sign, man, exp, bc = x + # Exact (or special) cases + if not man: + if exp: + return (fnan, fnan) + else: + return (fone, fzero) + y, swaps, n = reduce_angle(x, prec+10) + return calc_cos_sin(which, y, swaps, prec, rnd, rnd) + +def mpf_cos(x, prec, rnd=round_fast): + return mpf_cos_sin(x, prec, rnd, 1)[0] + +def mpf_sin(x, prec, rnd=round_fast): + return mpf_cos_sin(x, prec, rnd, -1)[1] + +def mpf_tan(x, prec, rnd=round_fast): + c, s = mpf_cos_sin(x, prec+20) + return mpf_div(s, c, prec, rnd) + +# Accurate computation of cos(pi*x) and sin(pi*x) is needed by +# reflection formulas for gamma, polygamma, zeta, etc + +def mpf_cos_sin_pi(x, prec, rnd=round_fast): + """Accurate computation of (cos(pi*x), sin(pi*x)) + for x close to an integer""" + sign, man, exp, bc = x + if not man: + return mpf_cos_sin(x, prec, rnd) + # Exactly an integer or half-integer? + if exp >= -1: + if exp == -1: + c = fzero + s = (fone, fnone)[bool(man & 2) ^ sign] + elif exp == 0: + c, s = (fnone, fzero) + else: + c, s = (fone, fzero) + return c, s + # Close to 0 ? + size = exp + bc + if size < -(prec+5): + c = mpf_perturb(fone, 1, prec, rnd) + s = mpf_perturb(mpf_mul(x, mpf_pi(prec)), sign, prec, rnd) + return c, s + if sign: + man = -man + # Subtract nearest half-integer (= modulo pi/2) + nhint = ((man >> (-exp-2)) + 1) >> 1 + man = man - (nhint << (-exp-1)) + x = from_man_exp(man, exp, prec) + x = mpf_mul(x, mpf_pi(prec), prec) + # XXX: with some more work, could call calc_cos_sin, + # to save some time and to get rounding right + case = nhint % 4 + if case == 0: + c, s = mpf_cos_sin(x, prec, rnd) + elif case == 1: + s, c = mpf_cos_sin(x, prec, rnd) + c = mpf_neg(c) + elif case == 2: + c, s = mpf_cos_sin(x, prec, rnd) + c = mpf_neg(c) + s = mpf_neg(s) + else: + s, c = mpf_cos_sin(x, prec, rnd) + s = mpf_neg(s) + return c, s + +def mpf_cos_pi(x, prec, rnd=round_fast): + return mpf_cos_sin_pi(x, prec, rnd)[0] + +def mpf_sin_pi(x, prec, rnd=round_fast): + return mpf_cos_sin_pi(x, prec, rnd)[1] + + +#---------------------------------------------------------------------- +# Hyperbolic functions +# + +def sinh_taylor(x, prec): + x = MPZ(x) + x2 = (x*x) >> prec + s = a = x + k = 3 + while a: + a = ((a * x2) >> prec) // (k*(k-1)) + s += a + k += 2 + return s + +def mpf_cosh_sinh(x, prec, rnd=round_fast, tanh=0): + """Simultaneously compute (cosh(x), sinh(x)) for real x""" + sign, man, exp, bc = x + if (not man) and exp: + if tanh: + if x == finf: return fone + if x == fninf: return fnone + return fnan + if x == finf: return (finf, finf) + if x == fninf: return (finf, fninf) + return fnan, fnan + + if sign: + man = -man + + mag = exp + bc + prec2 = prec + 20 + + if mag < -3: + # Extremely close to 0, sinh(x) ~= x and cosh(x) ~= 1 + if mag < -prec-2: + if tanh: + return mpf_perturb(x, 1-sign, prec, rnd) + cosh = mpf_perturb(fone, 0, prec, rnd) + sinh = mpf_perturb(x, sign, prec, rnd) + return cosh, sinh + + # Avoid cancellation when computing sinh + # TODO: might be faster to use sinh series directly + prec2 += (-mag) + 4 + + # In the general case, we use + # cosh(x) = (exp(x) + exp(-x))/2 + # sinh(x) = (exp(x) - exp(-x))/2 + # and note that the exponential only needs to be computed once. + ep = mpf_exp(x, prec2) + em = mpf_div(fone, ep, prec2) + if tanh: + ch = mpf_add(ep, em, prec2, rnd) + sh = mpf_sub(ep, em, prec2, rnd) + return mpf_div(sh, ch, prec, rnd) + else: + ch = mpf_shift(mpf_add(ep, em, prec, rnd), -1) + sh = mpf_shift(mpf_sub(ep, em, prec, rnd), -1) + return ch, sh + +def mpf_cosh(x, prec, rnd=round_fast): + """Compute cosh(x) for a real argument x""" + return mpf_cosh_sinh(x, prec, rnd)[0] + +def mpf_sinh(x, prec, rnd=round_fast): + """Compute sinh(x) for a real argument x""" + return mpf_cosh_sinh(x, prec, rnd)[1] + +def mpf_tanh(x, prec, rnd=round_fast): + """Compute tanh(x) for a real argument x""" + return mpf_cosh_sinh(x, prec, rnd, tanh=1) + + +#---------------------------------------------------------------------- +# Inverse tangent +# + +def atan_newton(x, prec): + if prec >= 100: + r = math.atan((x>>(prec-53))/2.0**53) + else: + r = math.atan(x/2.0**prec) + prevp = 50 + r = int(r * 2.0**53) >> (53-prevp) + extra_p = 100 + for p in giant_steps(prevp, prec): + s = int(0.137 * p**0.579) + p += s + 50 + r = r << (p-prevp) + cos, sin = expi_series(r, p, s) + tan = (sin << p) // cos + a = ((tan - rshift(x, prec-p)) << p) // ((MPZ_ONE<>p)) + r = r - a + prevp = p + return rshift(r, prevp-prec) + + +ATAN_TAYLOR_PREC = 3000 +ATAN_TAYLOR_SHIFT = 7 # steps of size 2^-N + +atan_taylor_cache = {} + +def atan_taylor_get_cached(n, prec): + # Taylor series with caching wins up to huge precisions + # To avoid unnecessary precomputation at low precision, we + # do it in steps + # Round to next power of 2 + prec2 = (1<<(bitcount(prec-1))) + 20 + dprec = prec2 - prec + if (n, prec2) in atan_taylor_cache: + a, atan_a = atan_taylor_cache[n, prec2] + else: + a = n << (prec2 - ATAN_TAYLOR_SHIFT) + atan_a = atan_newton(a, prec2) + atan_taylor_cache[n, prec2] = (a, atan_a) + return (a >> dprec), (atan_a >> dprec) + +def atan_taylor(x, prec): + n = (x >> (prec-ATAN_TAYLOR_SHIFT)) + a, atan_a = atan_taylor_get_cached(n, prec) + d = x - a + s0 = v = (d << prec) // ((a**2 >> prec) + (a*d >> prec) + (MPZ_ONE << prec)) + v2 = (v**2 >> prec) + v4 = (v2 * v2) >> prec + s1 = v//3 + v = (v * v4) >> prec + k = 5 + while v: + s0 += v // k + k += 2 + s1 += v // k + v = (v * v4) >> prec + k += 2 + s1 = (s1 * v2) >> prec + s = s0 - s1 + return atan_a + s + +def atan_inf(sign, prec, rnd): + if not sign: + return mpf_shift(mpf_pi(prec, rnd), -1) + return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) + +def mpf_atan(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if x == fzero: return fzero + if x == finf: return atan_inf(0, prec, rnd) + if x == fninf: return atan_inf(1, prec, rnd) + return fnan + mag = exp + bc + # Essentially infinity + if mag > prec+20: + return atan_inf(sign, prec, rnd) + # Essentially ~ x + if -mag > prec+20: + return mpf_perturb(x, 1-sign, prec, rnd) + wp = prec + 30 + abs(mag) + # For large x, use atan(x) = pi/2 - atan(1/x) + if mag >= 2: + x = mpf_rdiv_int(1, x, wp) + reciprocal = True + else: + reciprocal = False + t = to_fixed(x, wp) + if sign: + t = -t + if wp < ATAN_TAYLOR_PREC: + a = atan_taylor(t, wp) + else: + a = atan_newton(t, wp) + if reciprocal: + a = ((pi_fixed(wp)>>1)+1) - a + if sign: + a = -a + return from_man_exp(a, -wp, prec, rnd) + +# TODO: cleanup the special cases +def mpf_atan2(y, x, prec, rnd=round_fast): + xsign, xman, xexp, xbc = x + ysign, yman, yexp, ybc = y + if not yman: + if y == fzero and x != fnan: + if mpf_sign(x) >= 0: + return fzero + return mpf_pi(prec, rnd) + if y in (finf, fninf): + if x in (finf, fninf): + return fnan + # pi/2 + if y == finf: + return mpf_shift(mpf_pi(prec, rnd), -1) + # -pi/2 + return mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) + return fnan + if ysign: + return mpf_neg(mpf_atan2(mpf_neg(y), x, prec, negative_rnd[rnd])) + if not xman: + if x == fnan: + return fnan + if x == finf: + return fzero + if x == fninf: + return mpf_pi(prec, rnd) + if y == fzero: + return fzero + return mpf_shift(mpf_pi(prec, rnd), -1) + tquo = mpf_atan(mpf_div(y, x, prec+4), prec+4) + if xsign: + return mpf_add(mpf_pi(prec+4), tquo, prec, rnd) + else: + return mpf_pos(tquo, prec, rnd) + +def mpf_asin(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if bc+exp > 0 and x not in (fone, fnone): + raise ComplexResult("asin(x) is real only for -1 <= x <= 1") + # asin(x) = 2*atan(x/(1+sqrt(1-x**2))) + wp = prec + 15 + a = mpf_mul(x, x) + b = mpf_add(fone, mpf_sqrt(mpf_sub(fone, a, wp), wp), wp) + c = mpf_div(x, b, wp) + return mpf_shift(mpf_atan(c, prec, rnd), 1) + +def mpf_acos(x, prec, rnd=round_fast): + # acos(x) = 2*atan(sqrt(1-x**2)/(1+x)) + sign, man, exp, bc = x + if bc + exp > 0: + if x not in (fone, fnone): + raise ComplexResult("acos(x) is real only for -1 <= x <= 1") + if x == fnone: + return mpf_pi(prec, rnd) + wp = prec + 15 + a = mpf_mul(x, x) + b = mpf_sqrt(mpf_sub(fone, a, wp), wp) + c = mpf_div(b, mpf_add(fone, x, wp), wp) + return mpf_shift(mpf_atan(c, prec, rnd), 1) + +def mpf_asinh(x, prec, rnd=round_fast): + wp = prec + 20 + sign, man, exp, bc = x + mag = exp+bc + if mag < -8: + if mag < -wp: + return mpf_perturb(x, 1-sign, prec, rnd) + wp += (-mag) + # asinh(x) = log(x+sqrt(x**2+1)) + # use reflection symmetry to avoid cancellation + q = mpf_sqrt(mpf_add(mpf_mul(x, x), fone, wp), wp) + q = mpf_add(mpf_abs(x), q, wp) + if sign: + return mpf_neg(mpf_log(q, prec, negative_rnd[rnd])) + else: + return mpf_log(q, prec, rnd) + +def mpf_acosh(x, prec, rnd=round_fast): + # acosh(x) = log(x+sqrt(x**2-1)) + wp = prec + 15 + if mpf_cmp(x, fone) == -1: + raise ComplexResult("acosh(x) is real only for x >= 1") + q = mpf_sqrt(mpf_add(mpf_mul(x,x), fnone, wp), wp) + return mpf_log(mpf_add(x, q, wp), prec, rnd) + +def mpf_atanh(x, prec, rnd=round_fast): + # atanh(x) = log((1+x)/(1-x))/2 + sign, man, exp, bc = x + if (not man) and exp: + if x in (fzero, fnan): + return x + raise ComplexResult("atanh(x) is real only for -1 <= x <= 1") + mag = bc + exp + if mag > 0: + if mag == 1 and man == 1: + return [finf, fninf][sign] + raise ComplexResult("atanh(x) is real only for -1 <= x <= 1") + wp = prec + 15 + if mag < -8: + if mag < -wp: + return mpf_perturb(x, sign, prec, rnd) + wp += (-mag) + a = mpf_add(x, fone, wp) + b = mpf_sub(fone, x, wp) + return mpf_shift(mpf_log(mpf_div(a, b, wp), prec, rnd), -1) + +def mpf_fibonacci(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if x == fninf: + return fnan + return x + # F(2^n) ~= 2^(2^n) + size = abs(exp+bc) + if exp >= 0: + # Exact + if size < 10 or size <= bitcount(prec): + return from_int(ifib(to_int(x)), prec, rnd) + # Use the modified Binet formula + wp = prec + size + 20 + a = mpf_phi(wp) + b = mpf_add(mpf_shift(a, 1), fnone, wp) + u = mpf_pow(a, x, wp) + v = mpf_cos_pi(x, wp) + v = mpf_div(v, u, wp) + u = mpf_sub(u, v, wp) + u = mpf_div(u, b, prec, rnd) + return u diff --git a/compiler/gdsMill/mpmath/libmp/libhyper.py b/compiler/gdsMill/mpmath/libmp/libhyper.py new file mode 100644 index 00000000..e7b1b823 --- /dev/null +++ b/compiler/gdsMill/mpmath/libmp/libhyper.py @@ -0,0 +1,1133 @@ +""" +This module implements computation of hypergeometric and related +functions. In particular, it provides code for generic summation +of hypergeometric series. Optimized versions for various special +cases are also provided. +""" + +import operator +import math + +from backend import MPZ_ZERO, MPZ_ONE + +from libintmath import gcd + +from libmpf import (\ + ComplexResult, round_fast, round_nearest, + negative_rnd, bitcount, to_fixed, from_man_exp, from_int, to_int, + from_rational, + fzero, fone, fnone, ftwo, finf, fninf, fnan, + mpf_sign, mpf_add, mpf_abs, mpf_pos, + mpf_cmp, mpf_lt, mpf_le, mpf_gt, + mpf_perturb, mpf_neg, mpf_shift, mpf_sub, mpf_mul, mpf_div, + sqrt_fixed, mpf_sqrt, mpf_rdiv_int, mpf_pow_int, + to_rational, +) + +from libelefun import (\ + mpf_pi, mpf_exp, mpf_log, pi_fixed, mpf_cos_sin, mpf_cos, mpf_sin, + mpf_sqrt, agm_fixed, +) + +from libmpc import (\ + mpc_one, mpc_sub, mpc_mul_mpf, mpc_mul, mpc_neg, complex_int_pow, + mpc_div, mpc_add_mpf, mpc_sub_mpf, + mpc_log, mpc_add, mpc_pos, mpc_shift, + mpc_is_infnan, mpc_zero, mpc_sqrt, mpc_abs, + mpc_mpf_div, mpc_square, mpc_exp +) + +from libintmath import ifac +from gammazeta import mpf_gamma_int, mpf_euler, euler_fixed + +class NoConvergence(Exception): + pass + + +#-----------------------------------------------------------------------# +# # +# Generic hypergeometric series # +# # +#-----------------------------------------------------------------------# + +""" +TODO: + +1. proper mpq parsing +2. imaginary z special-cased (also: rational, integer?) +3. more clever handling of series that don't converge because of stupid + upwards rounding +4. checking for cancellation + +""" + +def make_hyp_summator(key): + """ + Returns a function that sums a generalized hypergeometric series, + for given parameter types (integer, rational, real, complex). + + """ + p, q, param_types, ztype = key + + pstring = "".join(param_types) + fname = "hypsum_%i_%i_%s_%s_%s" % (p, q, pstring[:p], pstring[p:], ztype) + #print "generating hypsum", fname + + have_complex_param = 'C' in param_types + have_complex_arg = ztype == 'C' + have_complex = have_complex_param or have_complex_arg + + source = [] + add = source.append + + aint = [] + arat = [] + bint = [] + brat = [] + areal = [] + breal = [] + acomplex = [] + bcomplex = [] + + #add("wp = prec + 40") + add("MAX = kwargs.get('maxterms', wp*100)") + add("HIGH = MPZ_ONE<= 0:") + add(" ZRE = xm << offset") + add("else:") + add(" ZRE = xm >> (-offset)") + if have_complex_arg: + add("offset = ye + wp") + add("if offset >= 0:") + add(" ZIM = ym << offset") + add("else:") + add(" ZIM = ym >> (-offset)") + + for i, flag in enumerate(param_types): + W = ["A", "B"][i >= p] + if flag == 'Z': + ([aint,bint][i >= p]).append(i) + add("%sINT_%i = coeffs[%i]" % (W, i, i)) + elif flag == 'Q': + ([arat,brat][i >= p]).append(i) + add("%sP_%i, %sQ_%i = coeffs[%i]" % (W, i, W, i, i)) + elif flag == 'R': + ([areal,breal][i >= p]).append(i) + add("xsign, xm, xe, xbc = coeffs[%i]._mpf_" % i) + add("if xsign: xm = -xm") + add("offset = xe + wp") + add("if offset >= 0:") + add(" %sREAL_%i = xm << offset" % (W, i)) + add("else:") + add(" %sREAL_%i = xm >> (-offset)" % (W, i)) + elif flag == 'C': + ([acomplex,bcomplex][i >= p]).append(i) + add("__re, __im = coeffs[%i]._mpc_" % i) + add("xsign, xm, xe, xbc = __re") + add("if xsign: xm = -xm") + add("ysign, ym, ye, ybc = __im") + add("if ysign: ym = -ym") + + add("offset = xe + wp") + add("if offset >= 0:") + add(" %sCRE_%i = xm << offset" % (W, i)) + add("else:") + add(" %sCRE_%i = xm >> (-offset)" % (W, i)) + add("offset = ye + wp") + add("if offset >= 0:") + add(" %sCIM_%i = ym << offset" % (W, i)) + add("else:") + add(" %sCIM_%i = ym >> (-offset)" % (W, i)) + else: + raise ValueError + + l_areal = len(areal) + l_breal = len(breal) + cancellable_real = min(l_areal, l_breal) + noncancellable_real_num = areal[cancellable_real:] + noncancellable_real_den = breal[cancellable_real:] + + # LOOP + add("for n in xrange(1,10**8):") + + add(" if n in magnitude_check:") + add(" p_mag = bitcount(abs(PRE))") + if have_complex: + add(" p_mag = max(p_mag, bitcount(abs(PIM)))") + add(" magnitude_check[n] = wp-p_mag") + + # Real factors + multiplier = " * ".join(["AINT_#".replace("#", str(i)) for i in aint] + \ + ["AP_#".replace("#", str(i)) for i in arat] + \ + ["BQ_#".replace("#", str(i)) for i in brat]) + + divisor = " * ".join(["BINT_#".replace("#", str(i)) for i in bint] + \ + ["BP_#".replace("#", str(i)) for i in brat] + \ + ["AQ_#".replace("#", str(i)) for i in arat] + ["n"]) + + if multiplier: + add(" mul = " + multiplier) + add(" div = " + divisor) + + # Check for singular terms + add(" if not div:") + if multiplier: + add(" if not mul:") + add(" break") + add(" raise ZeroDivisionError") + + # Update product + if have_complex: + + # TODO: when there are several real parameters and just a few complex + # (maybe just the complex argument), we only need to do about + # half as many ops if we accumulate the real factor in a single real variable + for k in range(cancellable_real): add(" PRE = PRE * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) + for i in noncancellable_real_num: add(" PRE = (PRE * AREAL_#) >> wp".replace("#", str(i))) + for i in noncancellable_real_den: add(" PRE = (PRE << wp) // BREAL_#".replace("#", str(i))) + for k in range(cancellable_real): add(" PIM = PIM * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) + for i in noncancellable_real_num: add(" PIM = (PIM * AREAL_#) >> wp".replace("#", str(i))) + for i in noncancellable_real_den: add(" PIM = (PIM << wp) // BREAL_#".replace("#", str(i))) + + if multiplier: + if have_complex_arg: + add(" PRE, PIM = (mul*(PRE*ZRE-PIM*ZIM))//div, (mul*(PIM*ZRE+PRE*ZIM))//div") + add(" PRE >>= wp") + add(" PIM >>= wp") + else: + add(" PRE = ((mul * PRE * ZRE) >> wp) // div") + add(" PIM = ((mul * PIM * ZRE) >> wp) // div") + else: + if have_complex_arg: + add(" PRE, PIM = (PRE*ZRE-PIM*ZIM)//div, (PIM*ZRE+PRE*ZIM)//div") + add(" PRE >>= wp") + add(" PIM >>= wp") + else: + add(" PRE = ((PRE * ZRE) >> wp) // div") + add(" PIM = ((PIM * ZRE) >> wp) // div") + + for i in acomplex: + add(" PRE, PIM = PRE*ACRE_#-PIM*ACIM_#, PIM*ACRE_#+PRE*ACIM_#".replace("#", str(i))) + add(" PRE >>= wp") + add(" PIM >>= wp") + + for i in bcomplex: + add(" mag = BCRE_#*BCRE_#+BCIM_#*BCIM_#".replace("#", str(i))) + add(" re = PRE*BCRE_# + PIM*BCIM_#".replace("#", str(i))) + add(" im = PIM*BCRE_# - PRE*BCIM_#".replace("#", str(i))) + add(" PRE = (re << wp) // mag".replace("#", str(i))) + add(" PIM = (im << wp) // mag".replace("#", str(i))) + + else: + for k in range(cancellable_real): add(" PRE = PRE * AREAL_%i // BREAL_%i" % (areal[k], breal[k])) + for i in noncancellable_real_num: add(" PRE = (PRE * AREAL_#) >> wp".replace("#", str(i))) + for i in noncancellable_real_den: add(" PRE = (PRE << wp) // BREAL_#".replace("#", str(i))) + if multiplier: + add(" PRE = ((PRE * mul * ZRE) >> wp) // div") + else: + add(" PRE = ((PRE * ZRE) >> wp) // div") + + # Add product to sum + if have_complex: + add(" SRE += PRE") + add(" SIM += PIM") + add(" if (HIGH > PRE > LOW) and (HIGH > PIM > LOW):") + add(" break") + else: + add(" SRE += PRE") + add(" if HIGH > PRE > LOW:") + add(" break") + + #add(" from mpmath import nprint, log, ldexp") + #add(" nprint([n, log(abs(PRE),2), ldexp(PRE,-wp)])") + + add(" if n > MAX:") + add(" raise NoConvergence('Hypergeometric series converges too slowly. Try increasing maxterms.')") + + # +1 all parameters for next loop + for i in aint: add(" AINT_# += 1".replace("#", str(i))) + for i in bint: add(" BINT_# += 1".replace("#", str(i))) + for i in arat: add(" AP_# += AQ_#".replace("#", str(i))) + for i in brat: add(" BP_# += BQ_#".replace("#", str(i))) + for i in areal: add(" AREAL_# += one".replace("#", str(i))) + for i in breal: add(" BREAL_# += one".replace("#", str(i))) + for i in acomplex: add(" ACRE_# += one".replace("#", str(i))) + for i in bcomplex: add(" BCRE_# += one".replace("#", str(i))) + + if have_complex: + add("a = from_man_exp(SRE, -wp, prec, 'n')") + add("b = from_man_exp(SIM, -wp, prec, 'n')") + + add("if SRE:") + add(" if SIM:") + add(" magn = max(a[2]+a[3], b[2]+b[3])") + add(" else:") + add(" magn = a[2]+a[3]") + add("elif SIM:") + add(" magn = b[2]+b[3]") + add("else:") + add(" magn = -prec") + + add("return (a, b), True, magn") + else: + add("a = from_man_exp(SRE, -wp, prec, 'n')") + + add("if SRE:") + add(" magn = a[2]+a[3]") + add("else:") + add(" magn = -prec") + + add("return a, False, magn") + + source = "\n".join((" " + line) for line in source) + source = ("def %s(coeffs, z, prec, wp, epsshift, magnitude_check, **kwargs):\n" % fname) + source + + namespace = {} + + exec source in globals(), namespace + #print source + + return source, namespace[fname] + + + +#-----------------------------------------------------------------------# +# # +# Error functions # +# # +#-----------------------------------------------------------------------# + +# TODO: mpf_erf should call mpf_erfc when appropriate (currently +# only the converse delegation is implemented) + +def mpf_erf(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if x == fzero: return fzero + if x == finf: return fone + if x== fninf: return fnone + return fnan + size = exp + bc + lg = math.log + # The approximation erf(x) = 1 is accurate to > x^2 * log(e,2) bits + if size > 3 and 2*(size-1) + 0.528766 > lg(prec,2): + if sign: + return mpf_perturb(fnone, 0, prec, rnd) + else: + return mpf_perturb(fone, 1, prec, rnd) + # erf(x) ~ 2*x/sqrt(pi) close to 0 + if size < -prec: + # 2*x + x = mpf_shift(x,1) + c = mpf_sqrt(mpf_pi(prec+20), prec+20) + # TODO: interval rounding + return mpf_div(x, c, prec, rnd) + wp = prec + abs(size) + 25 + # Taylor series for erf, fixed-point summation + t = abs(to_fixed(x, wp)) + t2 = (t*t) >> wp + s, term, k = t, 12345, 1 + while term: + t = ((t * t2) >> wp) // k + term = t // (2*k+1) + if k & 1: + s -= term + else: + s += term + k += 1 + s = (s << (wp+1)) // sqrt_fixed(pi_fixed(wp), wp) + if sign: + s = -s + return from_man_exp(s, -wp, prec, rnd) + +# If possible, we use the asymptotic series for erfc. +# This is an alternating divergent asymptotic series, so +# the error is at most equal to the first omitted term. +# Here we check if the smallest term is small enough +# for a given x and precision +def erfc_check_series(x, prec): + n = to_int(x) + if n**2 * 1.44 > prec: + return True + return False + +def mpf_erfc(x, prec, rnd=round_fast): + sign, man, exp, bc = x + if not man: + if x == fzero: return fone + if x == finf: return fzero + if x == fninf: return ftwo + return fnan + wp = prec + 20 + mag = bc+exp + # Preserve full accuracy when exponent grows huge + wp += max(0, 2*mag) + regular_erf = sign or mag < 2 + if regular_erf or not erfc_check_series(x, wp): + if regular_erf: + return mpf_sub(fone, mpf_erf(x, prec+10, negative_rnd[rnd]), prec, rnd) + # 1-erf(x) ~ exp(-x^2), increase prec to deal with cancellation + n = to_int(x)+1 + return mpf_sub(fone, mpf_erf(x, prec + int(n**2*1.44) + 10), prec, rnd) + s = term = MPZ_ONE << wp + term_prev = 0 + t = (2 * to_fixed(x, wp) ** 2) >> wp + k = 1 + while 1: + term = ((term * (2*k - 1)) << wp) // t + if k > 4 and term > term_prev or not term: + break + if k & 1: + s -= term + else: + s += term + term_prev = term + #print k, to_str(from_man_exp(term, -wp, 50), 10) + k += 1 + s = (s << wp) // sqrt_fixed(pi_fixed(wp), wp) + s = from_man_exp(s, -wp, wp) + z = mpf_exp(mpf_neg(mpf_mul(x,x,wp),wp),wp) + y = mpf_div(mpf_mul(z, s, wp), x, prec, rnd) + return y + + +#-----------------------------------------------------------------------# +# # +# Exponential integrals # +# # +#-----------------------------------------------------------------------# + +def ei_taylor(x, prec): + s = t = x + k = 2 + while t: + t = ((t*x) >> prec) // k + s += t // k + k += 1 + return s + +def complex_ei_taylor(zre, zim, prec): + _abs = abs + sre = tre = zre + sim = tim = zim + k = 2 + while _abs(tre) + _abs(tim) > 5: + tre, tim = ((tre*zre-tim*zim)//k)>>prec, ((tre*zim+tim*zre)//k)>>prec + sre += tre // k + sim += tim // k + k += 1 + return sre, sim + +def ei_asymptotic(x, prec): + one = MPZ_ONE << prec + x = t = ((one << prec) // x) + s = one + x + k = 2 + while t: + t = (k*t*x) >> prec + s += t + k += 1 + return s + +def complex_ei_asymptotic(zre, zim, prec): + _abs = abs + one = MPZ_ONE << prec + M = (zim*zim + zre*zre) >> prec + # 1 / z + xre = tre = (zre << prec) // M + xim = tim = ((-zim) << prec) // M + sre = one + xre + sim = xim + k = 2 + while _abs(tre) + _abs(tim) > 1000: + #print tre, tim + tre, tim = ((tre*xre-tim*xim)*k)>>prec, ((tre*xim+tim*xre)*k)>>prec + sre += tre + sim += tim + k += 1 + if k > prec: + raise NoConvergence + return sre, sim + +def mpf_ei(x, prec, rnd=round_fast, e1=False): + if e1: + x = mpf_neg(x) + sign, man, exp, bc = x + if e1 and not sign: + if x == fzero: + return finf + raise ComplexResult("E1(x) for x < 0") + if man: + xabs = 0, man, exp, bc + xmag = exp+bc + wp = prec + 20 + can_use_asymp = xmag > wp + if not can_use_asymp: + if exp >= 0: + xabsint = man << exp + else: + xabsint = man >> (-exp) + can_use_asymp = xabsint > int(wp*0.693) + 10 + if can_use_asymp: + if xmag > wp: + v = fone + else: + v = from_man_exp(ei_asymptotic(to_fixed(x, wp), wp), -wp) + v = mpf_mul(v, mpf_exp(x, wp), wp) + v = mpf_div(v, x, prec, rnd) + else: + wp += 2*int(to_int(xabs)) + u = to_fixed(x, wp) + v = ei_taylor(u, wp) + euler_fixed(wp) + t1 = from_man_exp(v,-wp) + t2 = mpf_log(xabs,wp) + v = mpf_add(t1, t2, prec, rnd) + else: + if x == fzero: v = fninf + elif x == finf: v = finf + elif x == fninf: v = fzero + else: v = fnan + if e1: + v = mpf_neg(v) + return v + +def mpc_ei(z, prec, rnd=round_fast, e1=False): + if e1: + z = mpc_neg(z) + a, b = z + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + if b == fzero: + if e1: + x = mpf_neg(mpf_ei(a, prec, rnd)) + if not asign: + y = mpf_neg(mpf_pi(prec, rnd)) + else: + y = fzero + return x, y + else: + return mpf_ei(a, prec, rnd), fzero + if a != fzero: + if not aman or not bman: + return (fnan, fnan) + wp = prec + 40 + amag = aexp+abc + bmag = bexp+bbc + zmag = max(amag, bmag) + can_use_asymp = zmag > wp + if not can_use_asymp: + zabsint = abs(to_int(a)) + abs(to_int(b)) + can_use_asymp = zabsint > int(wp*0.693) + 20 + try: + if can_use_asymp: + if zmag > wp: + v = fone, fzero + else: + zre = to_fixed(a, wp) + zim = to_fixed(b, wp) + vre, vim = complex_ei_asymptotic(zre, zim, wp) + v = from_man_exp(vre, -wp), from_man_exp(vim, -wp) + v = mpc_mul(v, mpc_exp(z, wp), wp) + v = mpc_div(v, z, wp) + if e1: + v = mpc_neg(v, prec, rnd) + else: + x, y = v + if bsign: + v = mpf_pos(x, prec, rnd), mpf_sub(y, mpf_pi(wp), prec, rnd) + else: + v = mpf_pos(x, prec, rnd), mpf_add(y, mpf_pi(wp), prec, rnd) + return v + except NoConvergence: + pass + #wp += 2*max(0,zmag) + wp += 2*int(to_int(mpc_abs(z, 5))) + zre = to_fixed(a, wp) + zim = to_fixed(b, wp) + vre, vim = complex_ei_taylor(zre, zim, wp) + vre += euler_fixed(wp) + v = from_man_exp(vre,-wp), from_man_exp(vim,-wp) + if e1: + u = mpc_log(mpc_neg(z),wp) + else: + u = mpc_log(z,wp) + v = mpc_add(v, u, prec, rnd) + if e1: + v = mpc_neg(v) + return v + +def mpf_e1(x, prec, rnd=round_fast): + return mpf_ei(x, prec, rnd, True) + +def mpc_e1(x, prec, rnd=round_fast): + return mpc_ei(x, prec, rnd, True) + +def mpf_expint(n, x, prec, rnd=round_fast, gamma=False): + """ + E_n(x), n an integer, x real + + With gamma=True, computes Gamma(n,x) (upper incomplete gamma function) + + Returns (real, None) if real, otherwise (real, imag) + The imaginary part is an optional branch cut term + + """ + sign, man, exp, bc = x + if not man: + if gamma: + if x == fzero: + # Actually gamma function pole + if n <= 0: + return finf, None + return mpf_gamma_int(n, prec, rnd), None + if x == finf: + return fzero, None + # TODO: could return finite imaginary value at -inf + return fnan, fnan + else: + if x == fzero: + if n > 1: + return from_rational(1, n-1, prec, rnd), None + else: + return finf, None + if x == finf: + return fzero, None + return fnan, fnan + n_orig = n + if gamma: + n = 1-n + wp = prec + 20 + xmag = exp + bc + # Beware of near-poles + if xmag < -10: + raise NotImplementedError + nmag = bitcount(abs(n)) + have_imag = n > 0 and sign + negx = mpf_neg(x) + # Skip series if direct convergence + if n == 0 or 2*nmag - xmag < -wp: + if gamma: + v = mpf_exp(negx, wp) + re = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), prec, rnd) + else: + v = mpf_exp(negx, wp) + re = mpf_div(v, x, prec, rnd) + else: + # Finite number of terms, or... + can_use_asymptotic_series = -3*wp < n <= 0 + # ...large enough? + if not can_use_asymptotic_series: + xi = abs(to_int(x)) + m = min(max(1, xi-n), 2*wp) + siz = -n*nmag + (m+n)*bitcount(abs(m+n)) - m*xmag - (144*m//100) + tol = -wp-10 + can_use_asymptotic_series = siz < tol + if can_use_asymptotic_series: + r = ((-MPZ_ONE) << (wp+wp)) // to_fixed(x, wp) + m = n + t = r*m + s = MPZ_ONE << wp + while m and t: + s += t + m += 1 + t = (m*r*t) >> wp + v = mpf_exp(negx, wp) + if gamma: + # ~ exp(-x) * x^(n-1) * (1 + ...) + v = mpf_mul(v, mpf_pow_int(x, n_orig-1, wp), wp) + else: + # ~ exp(-x)/x * (1 + ...) + v = mpf_div(v, x, wp) + re = mpf_mul(v, from_man_exp(s, -wp), prec, rnd) + elif n == 1: + re = mpf_neg(mpf_ei(negx, prec, rnd)) + elif n > 0 and n < 3*wp: + T1 = mpf_neg(mpf_ei(negx, wp)) + if gamma: + if n_orig & 1: + T1 = mpf_neg(T1) + else: + T1 = mpf_mul(T1, mpf_pow_int(negx, n-1, wp), wp) + r = t = to_fixed(x, wp) + facs = [1] * (n-1) + for k in range(1,n-1): + facs[k] = facs[k-1] * k + facs = facs[::-1] + s = facs[0] << wp + for k in range(1, n-1): + if k & 1: + s -= facs[k] * t + else: + s += facs[k] * t + t = (t*r) >> wp + T2 = from_man_exp(s, -wp, wp) + T2 = mpf_mul(T2, mpf_exp(negx, wp)) + if gamma: + T2 = mpf_mul(T2, mpf_pow_int(x, n_orig, wp), wp) + R = mpf_add(T1, T2) + re = mpf_div(R, from_int(ifac(n-1)), prec, rnd) + else: + raise NotImplementedError + if have_imag: + M = from_int(-ifac(n-1)) + if gamma: + im = mpf_div(mpf_pi(wp), M, prec, rnd) + else: + im = mpf_div(mpf_mul(mpf_pi(wp), mpf_pow_int(negx, n_orig-1, wp), wp), M, prec, rnd) + return re, im + else: + return re, None + +def mpf_ci_si_taylor(x, wp, which=0): + """ + 0 - Ci(x) - (euler+log(x)) + 1 - Si(x) + """ + x = to_fixed(x, wp) + x2 = -(x*x) >> wp + if which == 0: + s, t, k = 0, (MPZ_ONE<>wp + s += t//k + k += 2 + return from_man_exp(s, -wp) + +def mpc_ci_si_taylor(re, im, wp, which=0): + # The following code is only designed for small arguments, + # and not too small arguments (for relative accuracy) + if re[1]: + mag = re[2]+re[3] + elif im[1]: + mag = im[2]+im[3] + if im[1]: + mag = max(mag, im[2]+im[3]) + if mag > 2 or mag < -wp: + raise NotImplementedError + wp += (2-mag) + zre = to_fixed(re, wp) + zim = to_fixed(im, wp) + z2re = (zim*zim-zre*zre)>>wp + z2im = (-2*zre*zim)>>wp + tre = zre + tim = zim + one = MPZ_ONE< 2: + f = k*(k-1) + tre, tim = ((tre*z2re-tim*z2im)//f)>>wp, ((tre*z2im+tim*z2re)//f)>>wp + sre += tre//k + sim += tim//k + k += 2 + return from_man_exp(sre, -wp), from_man_exp(sim, -wp) + +def mpf_ci_si(x, prec, rnd=round_fast, which=2): + """ + Calculation of Ci(x), Si(x) for real x. + + which = 0 -- returns (Ci(x), -) + which = 1 -- returns (Si(x), -) + which = 2 -- returns (Ci(x), Si(x)) + + Note: if x < 0, Ci(x) needs an additional imaginary term, pi*i. + """ + wp = prec + 20 + sign, man, exp, bc = x + ci, si = None, None + if not man: + if x == fzero: + return (fninf, fzero) + if x == fnan: + return (x, x) + ci = fzero + if which != 0: + if x == finf: + si = mpf_shift(mpf_pi(prec, rnd), -1) + if x == fninf: + si = mpf_neg(mpf_shift(mpf_pi(prec, negative_rnd[rnd]), -1)) + return (ci, si) + # For small x: Ci(x) ~ euler + log(x), Si(x) ~ x + mag = exp+bc + if mag < -wp: + if which != 0: + si = mpf_perturb(x, 1-sign, prec, rnd) + if which != 1: + y = mpf_euler(wp) + xabs = mpf_abs(x) + ci = mpf_add(y, mpf_log(xabs, wp), prec, rnd) + return ci, si + # For huge x: Ci(x) ~ sin(x)/x, Si(x) ~ pi/2 + elif mag > wp: + if which != 0: + if sign: + si = mpf_neg(mpf_pi(prec, negative_rnd[rnd])) + else: + si = mpf_pi(prec, rnd) + si = mpf_shift(si, -1) + if which != 1: + ci = mpf_div(mpf_sin(x, wp), x, prec, rnd) + return ci, si + else: + wp += abs(mag) + # Use an asymptotic series? The smallest value of n!/x^n + # occurs for n ~ x, where the magnitude is ~ exp(-x). + asymptotic = mag-1 > math.log(wp, 2) + # Case 1: convergent series near 0 + if not asymptotic: + if which != 0: + si = mpf_pos(mpf_ci_si_taylor(x, wp, 1), prec, rnd) + if which != 1: + ci = mpf_ci_si_taylor(x, wp, 0) + ci = mpf_add(ci, mpf_euler(wp), wp) + ci = mpf_add(ci, mpf_log(mpf_abs(x), wp), prec, rnd) + return ci, si + x = mpf_abs(x) + # Case 2: asymptotic series for x >> 1 + xf = to_fixed(x, wp) + xr = (MPZ_ONE<<(2*wp)) // xf # 1/x + s1 = (MPZ_ONE << wp) + s2 = xr + t = xr + k = 2 + while t: + t = -t + t = (t*xr*k)>>wp + k += 1 + s1 += t + t = (t*xr*k)>>wp + k += 1 + s2 += t + s1 = from_man_exp(s1, -wp) + s2 = from_man_exp(s2, -wp) + s1 = mpf_div(s1, x, wp) + s2 = mpf_div(s2, x, wp) + cos, sin = mpf_cos_sin(x, wp) + # Ci(x) = sin(x)*s1-cos(x)*s2 + # Si(x) = pi/2-cos(x)*s1-sin(x)*s2 + if which != 0: + si = mpf_add(mpf_mul(cos, s1), mpf_mul(sin, s2), wp) + si = mpf_sub(mpf_shift(mpf_pi(wp), -1), si, wp) + if sign: + si = mpf_neg(si) + si = mpf_pos(si, prec, rnd) + if which != 1: + ci = mpf_sub(mpf_mul(sin, s1), mpf_mul(cos, s2), prec, rnd) + return ci, si + +def mpf_ci(x, prec, rnd=round_fast): + if mpf_sign(x) < 0: + raise ComplexResult + return mpf_ci_si(x, prec, rnd, 0)[0] + +def mpf_si(x, prec, rnd=round_fast): + return mpf_ci_si(x, prec, rnd, 1)[1] + +def mpc_ci(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + ci = mpf_ci_si(re, prec, rnd, 0)[0] + if mpf_sign(re) < 0: + return (ci, mpf_pi(prec, rnd)) + return (ci, fzero) + wp = prec + 20 + cre, cim = mpc_ci_si_taylor(re, im, wp, 0) + cre = mpf_add(cre, mpf_euler(wp), wp) + ci = mpc_add((cre, cim), mpc_log(z, wp), prec, rnd) + return ci + +def mpc_si(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + return (mpf_ci_si(re, prec, rnd, 1)[1], fzero) + wp = prec + 20 + z = mpc_ci_si_taylor(re, im, wp, 1) + return mpc_pos(z, prec, rnd) + + +#-----------------------------------------------------------------------# +# # +# Bessel functions # +# # +#-----------------------------------------------------------------------# + +# A Bessel function of the first kind of integer order, J_n(x), is +# given by the power series + +# oo +# ___ k 2 k + n +# \ (-1) / x \ +# J_n(x) = ) ----------- | - | +# /___ k! (k + n)! \ 2 / +# k = 0 + +# Simplifying the quotient between two successive terms gives the +# ratio x^2 / (-4*k*(k+n)). Hence, we only need one full-precision +# multiplication and one division by a small integer per term. +# The complex version is very similar, the only difference being +# that the multiplication is actually 4 multiplies. + +# In the general case, we have +# J_v(x) = (x/2)**v / v! * 0F1(v+1, (-1/4)*z**2) + +# TODO: for extremely large x, we could use an asymptotic +# trigonometric approximation. + +# TODO: recompute at higher precision if the fixed-point mantissa +# is very small + +def mpf_besseljn(n, x, prec, rounding=round_fast): + prec += 50 + negate = n < 0 and n & 1 + mag = x[2]+x[3] + n = abs(n) + wp = prec + 20 + n*bitcount(n) + if mag < 0: + wp -= n * mag + x = to_fixed(x, wp) + x2 = (x**2) >> wp + if not n: + s = t = MPZ_ONE << wp + else: + s = t = (x**n // ifac(n)) >> ((n-1)*wp + n) + k = 1 + while t: + t = ((t * x2) // (-4*k*(k+n))) >> wp + s += t + k += 1 + if negate: + s = -s + return from_man_exp(s, -wp, prec, rounding) + +def mpc_besseljn(n, z, prec, rounding=round_fast): + negate = n < 0 and n & 1 + n = abs(n) + origprec = prec + zre, zim = z + mag = max(zre[2]+zre[3], zim[2]+zim[3]) + prec += 20 + n*bitcount(n) + abs(mag) + if mag < 0: + prec -= n * mag + zre = to_fixed(zre, prec) + zim = to_fixed(zim, prec) + z2re = (zre**2 - zim**2) >> prec + z2im = (zre*zim) >> (prec-1) + if not n: + sre = tre = MPZ_ONE << prec + sim = tim = MPZ_ZERO + else: + re, im = complex_int_pow(zre, zim, n) + sre = tre = (re // ifac(n)) >> ((n-1)*prec + n) + sim = tim = (im // ifac(n)) >> ((n-1)*prec + n) + k = 1 + while abs(tre) + abs(tim) > 3: + p = -4*k*(k+n) + tre, tim = tre*z2re - tim*z2im, tim*z2re + tre*z2im + tre = (tre // p) >> prec + tim = (tim // p) >> prec + sre += tre + sim += tim + k += 1 + if negate: + sre = -sre + sim = -sim + re = from_man_exp(sre, -prec, origprec, rounding) + im = from_man_exp(sim, -prec, origprec, rounding) + return (re, im) + +def mpf_agm(a, b, prec, rnd=round_fast): + """ + Computes the arithmetic-geometric mean agm(a,b) for + nonnegative mpf values a, b. + """ + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + if asign or bsign: + raise ComplexResult("agm of a negative number") + # Handle inf, nan or zero in either operand + if not (aman and bman): + if a == fnan or b == fnan: + return fnan + if a == finf: + if b == fzero: + return fnan + return finf + if b == finf: + if a == fzero: + return fnan + return finf + # agm(0,x) = agm(x,0) = 0 + return fzero + wp = prec + 20 + amag = aexp+abc + bmag = bexp+bbc + mag_delta = amag - bmag + # Reduce to roughly the same magnitude using floating-point AGM + abs_mag_delta = abs(mag_delta) + if abs_mag_delta > 10: + while abs_mag_delta > 10: + a, b = mpf_shift(mpf_add(a,b,wp),-1), \ + mpf_sqrt(mpf_mul(a,b,wp),wp) + abs_mag_delta //= 2 + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + amag = aexp+abc + bmag = bexp+bbc + mag_delta = amag - bmag + #print to_float(a), to_float(b) + # Use agm(a,b) = agm(x*a,x*b)/x to obtain a, b ~= 1 + min_mag = min(amag,bmag) + max_mag = max(amag,bmag) + n = 0 + # If too small, we lose precision when going to fixed-point + if min_mag < -8: + n = -min_mag + # If too large, we waste time using fixed-point with large numbers + elif max_mag > 20: + n = -max_mag + if n: + a = mpf_shift(a, n) + b = mpf_shift(b, n) + #print to_float(a), to_float(b) + af = to_fixed(a, wp) + bf = to_fixed(b, wp) + g = agm_fixed(af, bf, wp) + return from_man_exp(g, -wp-n, prec, rnd) + +def mpf_agm1(a, prec, rnd=round_fast): + """ + Computes the arithmetic-geometric mean agm(1,a) for a nonnegative + mpf value a. + """ + return mpf_agm(fone, a, prec, rnd) + +def mpc_agm(a, b, prec, rnd=round_fast): + """ + Complex AGM. + + TODO: + * check that convergence works as intended + * optimize + * select a nonarbitrary branch + """ + if mpc_is_infnan(a) or mpc_is_infnan(b): + return fnan, fnan + if mpc_zero in (a, b): + return fzero, fzero + if mpc_neg(a) == b: + return fzero, fzero + wp = prec+20 + eps = mpf_shift(fone, -wp+10) + while 1: + a1 = mpc_shift(mpc_add(a, b, wp), -1) + b1 = mpc_sqrt(mpc_mul(a, b, wp), wp) + a, b = a1, b1 + size = sorted([mpc_abs(a,10), mpc_abs(a,10)], cmp=mpf_cmp)[1] + err = mpc_abs(mpc_sub(a, b, 10), 10) + if size == fzero or mpf_lt(err, mpf_mul(eps, size)): + return a + +def mpc_agm1(a, prec, rnd=round_fast): + return mpc_agm(mpc_one, a, prec, rnd) + +def mpf_ellipk(x, prec, rnd=round_fast): + if not x[1]: + if x == fzero: + return mpf_shift(mpf_pi(prec, rnd), -1) + if x == fninf: + return fzero + if x == fnan: + return x + if x == fone: + return finf + # TODO: for |x| << 1/2, one could use fall back to + # pi/2 * hyp2f1_rat((1,2),(1,2),(1,1), x) + wp = prec + 15 + # Use K(x) = pi/2/agm(1,a) where a = sqrt(1-x) + # The sqrt raises ComplexResult if x > 0 + a = mpf_sqrt(mpf_sub(fone, x, wp), wp) + v = mpf_agm1(a, wp) + r = mpf_div(mpf_pi(wp), v, prec, rnd) + return mpf_shift(r, -1) + +def mpc_ellipk(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + if re == finf: + return mpc_zero + if mpf_le(re, fone): + return mpf_ellipk(re, prec, rnd), fzero + wp = prec + 15 + a = mpc_sqrt(mpc_sub(mpc_one, z, wp), wp) + v = mpc_agm1(a, wp) + r = mpc_mpf_div(mpf_pi(wp), v, prec, rnd) + return mpc_shift(r, -1) + +def mpf_ellipe(x, prec, rnd=round_fast): + # http://functions.wolfram.com/EllipticIntegrals/ + # EllipticK/20/01/0001/ + # E = (1-m)*(K'(m)*2*m + K(m)) + sign, man, exp, bc = x + if not man: + if x == fzero: + return mpf_shift(mpf_pi(prec, rnd), -1) + if x == fninf: + return finf + if x == fnan: + return x + if x == finf: + raise ComplexResult + if x == fone: + return fone + wp = prec+20 + mag = exp+bc + if mag < -wp: + return mpf_shift(mpf_pi(prec, rnd), -1) + # Compute a finite difference for K' + p = max(mag, 0) - wp + h = mpf_shift(fone, p) + K = mpf_ellipk(x, 2*wp) + Kh = mpf_ellipk(mpf_sub(x, h), 2*wp) + Kdiff = mpf_shift(mpf_sub(K, Kh), -p) + t = mpf_sub(fone, x) + b = mpf_mul(Kdiff, mpf_shift(x,1), wp) + return mpf_mul(t, mpf_add(K, b), prec, rnd) + +def mpc_ellipe(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + if re == finf: + return (fzero, finf) + if mpf_le(re, fone): + return mpf_ellipe(re, prec, rnd), fzero + wp = prec + 15 + mag = mpc_abs(z, 1) + p = max(mag[2]+mag[3], 0) - wp + h = mpf_shift(fone, p) + K = mpc_ellipk(z, 2*wp) + Kh = mpc_ellipk(mpc_add_mpf(z, h, 2*wp), 2*wp) + Kdiff = mpc_shift(mpc_sub(Kh, K, wp), -p) + t = mpc_sub(mpc_one, z, wp) + b = mpc_mul(Kdiff, mpc_shift(z,1), wp) + return mpc_mul(t, mpc_add(K, b, wp), prec, rnd) diff --git a/compiler/gdsMill/mpmath/libmp/libintmath.py b/compiler/gdsMill/mpmath/libmp/libintmath.py new file mode 100644 index 00000000..47cd666c --- /dev/null +++ b/compiler/gdsMill/mpmath/libmp/libintmath.py @@ -0,0 +1,461 @@ +""" +Utility functions for integer math. + +TODO: rename, cleanup, perhaps move the gmpy wrapper code +here from settings.py + +""" + +import math +from bisect import bisect + +from backend import BACKEND, gmpy, sage, sage_utils, MPZ, MPZ_ONE, MPZ_ZERO + +def giant_steps(start, target, n=2): + """ + Return a list of integers ~= + + [start, n*start, ..., target/n^2, target/n, target] + + but conservatively rounded so that the quotient between two + successive elements is actually slightly less than n. + + With n = 2, this describes suitable precision steps for a + quadratically convergent algorithm such as Newton's method; + with n = 3 steps for cubic convergence (Halley's method), etc. + + >>> giant_steps(50,1000) + [66, 128, 253, 502, 1000] + >>> giant_steps(50,1000,4) + [65, 252, 1000] + + """ + L = [target] + while L[-1] > start*n: + L = L + [L[-1]//n + 2] + return L[::-1] + +def rshift(x, n): + """For an integer x, calculate x >> n with the fastest (floor) + rounding. Unlike the plain Python expression (x >> n), n is + allowed to be negative, in which case a left shift is performed.""" + if n >= 0: return x >> n + else: return x << (-n) + +def lshift(x, n): + """For an integer x, calculate x << n. Unlike the plain Python + expression (x << n), n is allowed to be negative, in which case a + right shift with default (floor) rounding is performed.""" + if n >= 0: return x << n + else: return x >> (-n) + +if BACKEND == 'sage': + import operator + rshift = operator.rshift + lshift = operator.lshift + +def python_trailing(n): + """Count the number of trailing zero bits in abs(n).""" + if not n: + return 0 + t = 0 + while not n & 1: + n >>= 1 + t += 1 + return t + +def gmpy_trailing(n): + """Count the number of trailing zero bits in abs(n) using gmpy.""" + if n: return MPZ(n).scan1() + else: return 0 + +# Small powers of 2 +powers = [1<<_ for _ in range(300)] + +def python_bitcount(n): + """Calculate bit size of the nonnegative integer n.""" + bc = bisect(powers, n) + if bc != 300: + return bc + bc = int(math.log(n, 2)) - 4 + return bc + bctable[n>>bc] + +def gmpy_bitcount(n): + """Calculate bit size of the nonnegative integer n.""" + if n: return MPZ(n).numdigits(2) + else: return 0 + +#def sage_bitcount(n): +# if n: return MPZ(n).nbits() +# else: return 0 + +def sage_trailing(n): + return MPZ(n).trailing_zero_bits() + +if BACKEND == 'gmpy': + bitcount = gmpy_bitcount + trailing = gmpy_trailing +elif BACKEND == 'sage': + sage_bitcount = sage_utils.bitcount + bitcount = sage_bitcount + trailing = sage_trailing +else: + bitcount = python_bitcount + trailing = python_trailing + +if BACKEND == 'gmpy' and 'bit_length' in dir(gmpy): + bitcount = gmpy.bit_length + +# Used to avoid slow function calls as far as possible +trailtable = map(trailing, range(256)) +bctable = map(bitcount, range(1024)) + +# TODO: speed up for bases 2, 4, 8, 16, ... + +def bin_to_radix(x, xbits, base, bdigits): + """Changes radix of a fixed-point number; i.e., converts + x * 2**xbits to floor(x * 10**bdigits).""" + return x * (MPZ(base)**bdigits) >> xbits + +stddigits = '0123456789abcdefghijklmnopqrstuvwxyz' + +def small_numeral(n, base=10, digits=stddigits): + """Return the string numeral of a positive integer in an arbitrary + base. Most efficient for small input.""" + if base == 10: + return str(n) + digs = [] + while n: + n, digit = divmod(n, base) + digs.append(digits[digit]) + return "".join(digs[::-1]) + +def numeral_python(n, base=10, size=0, digits=stddigits): + """Represent the integer n as a string of digits in the given base. + Recursive division is used to make this function about 3x faster + than Python's str() for converting integers to decimal strings. + + The 'size' parameters specifies the number of digits in n; this + number is only used to determine splitting points and need not be + exact.""" + if n <= 0: + if not n: + return "0" + return "-" + numeral(-n, base, size, digits) + # Fast enough to do directly + if size < 250: + return small_numeral(n, base, digits) + # Divide in half + half = (size // 2) + (size & 1) + A, B = divmod(n, base**half) + ad = numeral(A, base, half, digits) + bd = numeral(B, base, half, digits).rjust(half, "0") + return ad + bd + +def numeral_gmpy(n, base=10, size=0, digits=stddigits): + """Represent the integer n as a string of digits in the given base. + Recursive division is used to make this function about 3x faster + than Python's str() for converting integers to decimal strings. + + The 'size' parameters specifies the number of digits in n; this + number is only used to determine splitting points and need not be + exact.""" + if n < 0: + return "-" + numeral(-n, base, size, digits) + # gmpy.digits() may cause a segmentation fault when trying to convert + # extremely large values to a string. The size limit may need to be + # adjusted on some platforms, but 1500000 works on Windows and Linux. + if size < 1500000: + return gmpy.digits(n, base) + # Divide in half + half = (size // 2) + (size & 1) + A, B = divmod(n, MPZ(base)**half) + ad = numeral(A, base, half, digits) + bd = numeral(B, base, half, digits).rjust(half, "0") + return ad + bd + +if BACKEND == "gmpy": + numeral = numeral_gmpy +else: + numeral = numeral_python + +_1_800 = 1<<800 +_1_600 = 1<<600 +_1_400 = 1<<400 +_1_200 = 1<<200 +_1_100 = 1<<100 +_1_50 = 1<<50 + +def isqrt_small_python(x): + """ + Correctly (floor) rounded integer square root, using + division. Fast up to ~200 digits. + """ + if not x: + return x + if x < _1_800: + # Exact with IEEE double precision arithmetic + if x < _1_50: + return int(x**0.5) + # Initial estimate can be any integer >= the true root; round up + r = int(x**0.5 * 1.00000000000001) + 1 + else: + bc = bitcount(x) + n = bc//2 + r = int((x>>(2*n-100))**0.5+2)<<(n-50) # +2 is to round up + # The following iteration now precisely computes floor(sqrt(x)) + # See e.g. Crandall & Pomerance, "Prime Numbers: A Computational + # Perspective" + while 1: + y = (r+x//r)>>1 + if y >= r: + return r + r = y + +def isqrt_fast_python(x): + """ + Fast approximate integer square root, computed using division-free + Newton iteration for large x. For random integers the result is almost + always correct (floor(sqrt(x))), but is 1 ulp too small with a roughly + 0.1% probability. If x is very close to an exact square, the answer is + 1 ulp wrong with high probability. + + With 0 guard bits, the largest error over a set of 10^5 random + inputs of size 1-10^5 bits was 3 ulp. The use of 10 guard bits + almost certainly guarantees a max 1 ulp error. + """ + # Use direct division-based iteration if sqrt(x) < 2^400 + # Assume floating-point square root accurate to within 1 ulp, then: + # 0 Newton iterations good to 52 bits + # 1 Newton iterations good to 104 bits + # 2 Newton iterations good to 208 bits + # 3 Newton iterations good to 416 bits + if x < _1_800: + y = int(x**0.5) + if x >= _1_100: + y = (y + x//y) >> 1 + if x >= _1_200: + y = (y + x//y) >> 1 + if x >= _1_400: + y = (y + x//y) >> 1 + return y + bc = bitcount(x) + guard_bits = 10 + x <<= 2*guard_bits + bc += 2*guard_bits + bc += (bc&1) + hbc = bc//2 + startprec = min(50, hbc) + # Newton iteration for 1/sqrt(x), with floating-point starting value + r = int(2.0**(2*startprec) * (x >> (bc-2*startprec)) ** -0.5) + pp = startprec + for p in giant_steps(startprec, hbc): + # r**2, scaled from real size 2**(-bc) to 2**p + r2 = (r*r) >> (2*pp - p) + # x*r**2, scaled from real size ~1.0 to 2**p + xr2 = ((x >> (bc-p)) * r2) >> p + # New value of r, scaled from real size 2**(-bc/2) to 2**p + r = (r * ((3<> (pp+1) + pp = p + # (1/sqrt(x))*x = sqrt(x) + return (r*(x>>hbc)) >> (p+guard_bits) + +def sqrtrem_python(x): + """Correctly rounded integer (floor) square root with remainder.""" + # to check cutoff: + # plot(lambda x: timing(isqrt, 2**int(x)), [0,2000]) + if x < _1_600: + y = isqrt_small_python(x) + return y, x - y*y + y = isqrt_fast_python(x) + 1 + rem = x - y*y + # Correct remainder + while rem < 0: + y -= 1 + rem += (1+2*y) + else: + if rem: + while rem > 2*(1+y): + y += 1 + rem -= (1+2*y) + return y, rem + +def isqrt_python(x): + """Integer square root with correct (floor) rounding.""" + return sqrtrem_python(x)[0] + +def sqrt_fixed(x, prec): + return isqrt_fast(x<>= 1 + if m < 250: + _cache[m] = b + return b + +MAX_FACTORIAL_CACHE = 1000 + +def ifac(n, memo={0:1, 1:1}): + """Return n factorial (for integers n >= 0 only).""" + f = memo.get(n) + if f: + return f + k = len(memo) + p = memo[k-1] + MAX = MAX_FACTORIAL_CACHE + while k <= n: + p *= k + if k <= MAX: + memo[k] = p + k += 1 + return p + +if BACKEND == 'gmpy': + ifac = gmpy.fac +elif BACKEND == 'sage': + ifac = lambda n: int(sage.factorial(n)) + ifib = sage.fibonacci + +def list_primes(n): + n = n + 1 + sieve = range(n) + sieve[:2] = [0, 0] + for i in xrange(2, int(n**0.5)+1): + if sieve[i]: + for j in xrange(i**2, n, i): + sieve[j] = 0 + return [p for p in sieve if p] + +if BACKEND == 'sage': + def list_primes(n): + return list(sage.primes(n+1)) + +def moebius(n): + """ + Evaluates the Moebius function which is `mu(n) = (-1)^k` if `n` + is a product of `k` distinct primes and `mu(n) = 0` otherwise. + + TODO: speed up using factorization + """ + n = abs(int(n)) + if n < 2: + return n + factors = [] + for p in xrange(2, n+1): + if not (n % p): + if not (n % p**2): + return 0 + if not sum(p % f for f in factors): + factors.append(p) + return (-1)**len(factors) + +def gcd(*args): + a = 0 + for b in args: + if a: + while b: + a, b = b, a % b + else: + a = b + return a + + +# Comment by Juan Arias de Reyna: +# +# I learn this method to compute EulerE[2n] from van de Lune. +# +# We apply the formula EulerE[2n] = (-1)^n 2**(-2n) sum_{j=0}^n a(2n,2j+1) +# +# where the numbers a(n,j) vanish for j > n+1 or j <= -1 and satisfies +# +# a(0,-1) = a(0,0) = 0; a(0,1)= 1; a(0,2) = a(0,3) = 0 +# +# a(n,j) = a(n-1,j) when n+j is even +# a(n,j) = (j-1) a(n-1,j-1) + (j+1) a(n-1,j+1) when n+j is odd +# +# +# But we can use only one array unidimensional a(j) since to compute +# a(n,j) we only need to know a(n-1,k) where k and j are of different parity +# and we have not to conserve the used values. +# +# We cached up the values of Euler numbers to sufficiently high order. +# +# Important Observation: If we pretend to use the numbers +# EulerE[1], EulerE[2], ... , EulerE[n] +# it is convenient to compute first EulerE[n], since the algorithm +# computes first all +# the previous ones, and keeps them in the CACHE + +MAX_EULER_CACHE = 500 + +def eulernum(m, _cache={0:MPZ_ONE}): + r""" + Computes the Euler numbers `E(n)`, which can be defined as + coefficients of the Taylor expansion of `1/cosh x`: + + .. math :: + + \frac{1}{\cosh x} = \sum_{n=0}^\infty \frac{E_n}{n!} x^n + + Example:: + + >>> [int(eulernum(n)) for n in range(11)] + [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521] + >>> [int(eulernum(n)) for n in range(11)] # test cache + [1, 0, -1, 0, 5, 0, -61, 0, 1385, 0, -50521] + + """ + # for odd m > 1, the Euler numbers are zero + if m & 1: + return MPZ_ZERO + f = _cache.get(m) + if f: + return f + MAX = MAX_EULER_CACHE + n = m + a = map(MPZ, [0,0,1,0,0,0]) + for n in range(1, m+1): + for j in range(n+1, -1, -2): + a[j+1] = (j-1)*a[j] + (j+1)*a[j+2] + a.append(0) + suma = 0 + for k in range(n+1, -1, -2): + suma += a[k+1] + if n <= MAX: + _cache[n] = ((-1)**(n//2))*(suma // 2**n) + if n == m: + return ((-1)**(n//2))*suma // 2**n diff --git a/compiler/gdsMill/mpmath/libmp/libmpc.py b/compiler/gdsMill/mpmath/libmp/libmpc.py new file mode 100644 index 00000000..4683bc5f --- /dev/null +++ b/compiler/gdsMill/mpmath/libmp/libmpc.py @@ -0,0 +1,754 @@ +""" +Low-level functions for complex arithmetic. +""" + +from backend import MPZ, MPZ_ZERO, MPZ_ONE, MPZ_TWO + +from libmpf import (\ + round_floor, round_ceiling, round_down, round_up, + round_nearest, round_fast, bitcount, + bctable, normalize, normalize1, reciprocal_rnd, rshift, lshift, giant_steps, + negative_rnd, + to_str, to_fixed, from_man_exp, from_float, to_float, from_int, to_int, + fzero, fone, ftwo, fhalf, finf, fninf, fnan, fnone, + mpf_abs, mpf_pos, mpf_neg, mpf_add, mpf_sub, mpf_mul, + mpf_div, mpf_mul_int, mpf_shift, mpf_sqrt, mpf_hypot, + mpf_rdiv_int, mpf_floor, mpf_ceil, + mpf_sign, + ComplexResult +) + +from libelefun import (\ + mpf_pi, mpf_exp, mpf_log, mpf_cos_sin, mpf_cosh_sinh, mpf_tan, mpf_pow_int, + mpf_log_hypot, + mpf_cos_sin_pi, mpf_phi, + mpf_atan, mpf_atan2, mpf_cosh, mpf_sinh, mpf_tanh, + mpf_asin, mpf_acos, mpf_acosh, mpf_nthroot, mpf_fibonacci +) + +# An mpc value is a (real, imag) tuple +mpc_one = fone, fzero +mpc_zero = fzero, fzero +mpc_two = ftwo, fzero +mpc_half = (fhalf, fzero) + +_infs = (finf, fninf) +_infs_nan = (finf, fninf, fnan) + +def mpc_is_inf(z): + """Check if either real or imaginary part is infinite""" + re, im = z + if re in _infs: return True + if im in _infs: return True + return False + +def mpc_is_infnan(z): + """Check if either real or imaginary part is infinite or nan""" + re, im = z + if re in _infs_nan: return True + if im in _infs_nan: return True + return False + +def mpc_to_str(z, dps, **kwargs): + re, im = z + rs = to_str(re, dps) + if im[0]: + return rs + " - " + to_str(mpf_neg(im), dps, **kwargs) + "j" + else: + return rs + " + " + to_str(im, dps, **kwargs) + "j" + +def mpc_to_complex(z, strict=False): + re, im = z + return complex(to_float(re, strict), to_float(im, strict)) + +def mpc_hash(z): + try: + return hash(mpc_to_complex(z, strict=True)) + except OverflowError: + return hash(z) + +def mpc_conjugate(z, prec, rnd=round_fast): + re, im = z + return re, mpf_neg(im, prec, rnd) + +def mpc_is_nonzero(z): + return z != mpc_zero + +def mpc_add(z, w, prec, rnd=round_fast): + a, b = z + c, d = w + return mpf_add(a, c, prec, rnd), mpf_add(b, d, prec, rnd) + +def mpc_add_mpf(z, x, prec, rnd=round_fast): + a, b = z + return mpf_add(a, x, prec, rnd), b + +def mpc_sub(z, w, prec, rnd=round_fast): + a, b = z + c, d = w + return mpf_sub(a, c, prec, rnd), mpf_sub(b, d, prec, rnd) + +def mpc_sub_mpf(z, p, prec, rnd=round_fast): + a, b = z + return mpf_sub(a, p, prec, rnd), b + +def mpc_pos(z, prec, rnd=round_fast): + a, b = z + return mpf_pos(a, prec, rnd), mpf_pos(b, prec, rnd) + +def mpc_neg(z, prec=None, rnd=round_fast): + a, b = z + return mpf_neg(a, prec, rnd), mpf_neg(b, prec, rnd) + +def mpc_shift(z, n): + a, b = z + return mpf_shift(a, n), mpf_shift(b, n) + +def mpc_abs(z, prec, rnd=round_fast): + """Absolute value of a complex number, |a+bi|. + Returns an mpf value.""" + a, b = z + return mpf_hypot(a, b, prec, rnd) + +def mpc_arg(z, prec, rnd=round_fast): + """Argument of a complex number. Returns an mpf value.""" + a, b = z + return mpf_atan2(b, a, prec, rnd) + +def mpc_floor(z, prec, rnd=round_fast): + a, b = z + return mpf_floor(a, prec, rnd), mpf_floor(b, prec, rnd) + +def mpc_ceil(z, prec, rnd=round_fast): + a, b = z + return mpf_ceil(a, prec, rnd), mpf_ceil(b, prec, rnd) + +def mpc_mul(z, w, prec, rnd=round_fast): + """ + Complex multiplication. + + Returns the real and imaginary part of (a+bi)*(c+di), rounded to + the specified precision. The rounding mode applies to the real and + imaginary parts separately. + """ + a, b = z + c, d = w + p = mpf_mul(a, c) + q = mpf_mul(b, d) + r = mpf_mul(a, d) + s = mpf_mul(b, c) + re = mpf_sub(p, q, prec, rnd) + im = mpf_add(r, s, prec, rnd) + return re, im + +def mpc_square(z, prec, rnd=round_fast): + # (a+b*I)**2 == a**2 - b**2 + 2*I*a*b + a, b = z + p = mpf_mul(a,a) + q = mpf_mul(b,b) + r = mpf_mul(a,b, prec, rnd) + re = mpf_sub(p, q, prec, rnd) + im = mpf_shift(r, 1) + return re, im + +def mpc_mul_mpf(z, p, prec, rnd=round_fast): + a, b = z + re = mpf_mul(a, p, prec, rnd) + im = mpf_mul(b, p, prec, rnd) + return re, im + +def mpc_mul_imag_mpf(z, x, prec, rnd=round_fast): + """ + Multiply the mpc value z by I*x where x is an mpf value. + """ + a, b = z + re = mpf_neg(mpf_mul(b, x, prec, rnd)) + im = mpf_mul(a, x, prec, rnd) + return re, im + +def mpc_mul_int(z, n, prec, rnd=round_fast): + a, b = z + re = mpf_mul_int(a, n, prec, rnd) + im = mpf_mul_int(b, n, prec, rnd) + return re, im + +def mpc_div(z, w, prec, rnd=round_fast): + a, b = z + c, d = w + wp = prec + 10 + # mag = c*c + d*d + mag = mpf_add(mpf_mul(c, c), mpf_mul(d, d), wp) + # (a*c+b*d)/mag, (b*c-a*d)/mag + t = mpf_add(mpf_mul(a,c), mpf_mul(b,d), wp) + u = mpf_sub(mpf_mul(b,c), mpf_mul(a,d), wp) + return mpf_div(t,mag,prec,rnd), mpf_div(u,mag,prec,rnd) + +def mpc_div_mpf(z, p, prec, rnd=round_fast): + """Calculate z/p where p is real""" + a, b = z + re = mpf_div(a, p, prec, rnd) + im = mpf_div(b, p, prec, rnd) + return re, im + +def mpc_reciprocal(z, prec, rnd=round_fast): + """Calculate 1/z efficiently""" + a, b = z + m = mpf_add(mpf_mul(a,a),mpf_mul(b,b),prec+10) + re = mpf_div(a, m, prec, rnd) + im = mpf_neg(mpf_div(b, m, prec, rnd)) + return re, im + +def mpc_mpf_div(p, z, prec, rnd=round_fast): + """Calculate p/z where p is real efficiently""" + a, b = z + m = mpf_add(mpf_mul(a,a),mpf_mul(b,b), prec+10) + re = mpf_div(mpf_mul(a,p), m, prec, rnd) + im = mpf_div(mpf_neg(mpf_mul(b,p)), m, prec, rnd) + return re, im + +def complex_int_pow(a, b, n): + """Complex integer power: computes (a+b*I)**n exactly for + nonnegative n (a and b must be Python ints).""" + wre = 1 + wim = 0 + while n: + if n & 1: + wre, wim = wre*a - wim*b, wim*a + wre*b + n -= 1 + a, b = a*a - b*b, 2*a*b + n //= 2 + return wre, wim + +def mpc_pow(z, w, prec, rnd=round_fast): + if w[1] == fzero: + return mpc_pow_mpf(z, w[0], prec, rnd) + return mpc_exp(mpc_mul(mpc_log(z, prec+10), w, prec+10), prec, rnd) + +def mpc_pow_mpf(z, p, prec, rnd=round_fast): + psign, pman, pexp, pbc = p + if pexp >= 0: + return mpc_pow_int(z, (-1)**psign * (pman< 0: + aman <<= de + aexp = bexp + else: + bman <<= (-de) + bexp = aexp + re, im = complex_int_pow(aman, bman, n) + re = from_man_exp(re, int(n*aexp), prec, rnd) + im = from_man_exp(im, int(n*bexp), prec, rnd) + return re, im + return mpc_exp(mpc_mul_int(mpc_log(z, prec+10), n, prec+10), prec, rnd) + +def mpc_sqrt(z, prec, rnd=round_fast): + """Complex square root (principal branch). + + We have sqrt(a+bi) = sqrt((r+a)/2) + b/sqrt(2*(r+a))*i where + r = abs(a+bi), when a+bi is not a negative real number.""" + a, b = z + if b == fzero: + if a == fzero: + return (a, b) + # When a+bi is a negative real number, we get a real sqrt times i + if a[0]: + im = mpf_sqrt(mpf_neg(a), prec, rnd) + return (fzero, im) + else: + re = mpf_sqrt(a, prec, rnd) + return (re, fzero) + wp = prec+20 + if not a[0]: # case a positive + t = mpf_add(mpc_abs((a, b), wp), a, wp) # t = abs(a+bi) + a + u = mpf_shift(t, -1) # u = t/2 + re = mpf_sqrt(u, prec, rnd) # re = sqrt(u) + v = mpf_shift(t, 1) # v = 2*t + w = mpf_sqrt(v, wp) # w = sqrt(v) + im = mpf_div(b, w, prec, rnd) # im = b / w + else: # case a negative + t = mpf_sub(mpc_abs((a, b), wp), a, wp) # t = abs(a+bi) - a + u = mpf_shift(t, -1) # u = t/2 + im = mpf_sqrt(u, prec, rnd) # im = sqrt(u) + v = mpf_shift(t, 1) # v = 2*t + w = mpf_sqrt(v, wp) # w = sqrt(v) + re = mpf_div(b, w, prec, rnd) # re = b/w + if b[0]: + re = mpf_neg(re) + im = mpf_neg(im) + return re, im + +def mpc_nthroot_fixed(a, b, n, prec): + # a, b signed integers at fixed precision prec + start = 50 + a1 = int(rshift(a, prec - n*start)) + b1 = int(rshift(b, prec - n*start)) + try: + r = (a1 + 1j * b1)**(1.0/n) + re = r.real + im = r.imag + re = MPZ(int(re)) + im = MPZ(int(im)) + except OverflowError: + a1 = from_int(a1, start) + b1 = from_int(b1, start) + fn = from_int(n) + nth = mpf_rdiv_int(1, fn, start) + re, im = mpc_pow((a1, b1), (nth, fzero), start) + re = to_int(re) + im = to_int(im) + extra = 10 + prevp = start + extra1 = n + for p in giant_steps(start, prec+extra): + # this is slow for large n, unlike int_pow_fixed + re2, im2 = complex_int_pow(re, im, n-1) + re2 = rshift(re2, (n-1)*prevp - p - extra1) + im2 = rshift(im2, (n-1)*prevp - p - extra1) + r4 = (re2*re2 + im2*im2) >> (p + extra1) + ap = rshift(a, prec - p) + bp = rshift(b, prec - p) + rec = (ap * re2 + bp * im2) >> p + imc = (-ap * im2 + bp * re2) >> p + reb = (rec << p) // r4 + imb = (imc << p) // r4 + re = (reb + (n-1)*lshift(re, p-prevp))//n + im = (imb + (n-1)*lshift(im, p-prevp))//n + prevp = p + return re, im + +def mpc_nthroot(z, n, prec, rnd=round_fast): + """ + Complex n-th root. + + Use Newton method as in the real case when it is faster, + otherwise use z**(1/n) + """ + a, b = z + if a[0] == 0 and b == fzero: + re = mpf_nthroot(a, n, prec, rnd) + return (re, fzero) + if n < 2: + if n == 0: + return mpc_one + if n == 1: + return mpc_pos((a, b), prec, rnd) + if n == -1: + return mpc_div(mpc_one, (a, b), prec, rnd) + inverse = mpc_nthroot((a, b), -n, prec+5, reciprocal_rnd[rnd]) + return mpc_div(mpc_one, inverse, prec, rnd) + if n <= 20: + prec2 = int(1.2 * (prec + 10)) + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + pf = mpc_abs((a,b), prec) + if pf[-2] + pf[-1] > -10 and pf[-2] + pf[-1] < prec: + af = to_fixed(a, prec2) + bf = to_fixed(b, prec2) + re, im = mpc_nthroot_fixed(af, bf, n, prec2) + extra = 10 + re = from_man_exp(re, -prec2-extra, prec2, rnd) + im = from_man_exp(im, -prec2-extra, prec2, rnd) + return re, im + fn = from_int(n) + prec2 = prec+10 + 10 + nth = mpf_rdiv_int(1, fn, prec2) + re, im = mpc_pow((a, b), (nth, fzero), prec2, rnd) + re = normalize(re[0], re[1], re[2], re[3], prec, rnd) + im = normalize(im[0], im[1], im[2], im[3], prec, rnd) + return re, im + +def mpc_cbrt((a, b), prec, rnd=round_fast): + """ + Complex cubic root. + """ + return mpc_nthroot((a, b), 3, prec, rnd) + +def mpc_exp((a, b), prec, rnd=round_fast): + """ + Complex exponential function. + + We use the direct formula exp(a+bi) = exp(a) * (cos(b) + sin(b)*i) + for the computation. This formula is very nice because it is + pefectly stable; since we just do real multiplications, the only + numerical errors that can creep in are single-ulp rounding errors. + + The formula is efficient since mpmath's real exp is quite fast and + since we can compute cos and sin simultaneously. + + It is no problem if a and b are large; if the implementations of + exp/cos/sin are accurate and efficient for all real numbers, then + so is this function for all complex numbers. + """ + if a == fzero: + return mpf_cos_sin(b, prec, rnd) + mag = mpf_exp(a, prec+4, rnd) + c, s = mpf_cos_sin(b, prec+4, rnd) + re = mpf_mul(mag, c, prec, rnd) + im = mpf_mul(mag, s, prec, rnd) + return re, im + +def mpc_log(z, prec, rnd=round_fast): + re = mpf_log_hypot(z[0], z[1], prec, rnd) + im = mpc_arg(z, prec, rnd) + return re, im + +def mpc_cos((a, b), prec, rnd=round_fast): + """Complex cosine. The formula used is cos(a+bi) = cos(a)*cosh(b) - + sin(a)*sinh(b)*i. + + The same comments apply as for the complex exp: only real + multiplications are pewrormed, so no cancellation errors are + possible. The formula is also efficient since we can compute both + pairs (cos, sin) and (cosh, sinh) in single stwps.""" + if a == fzero: + return mpf_cosh(b, prec, rnd), fzero + wp = prec + 6 + c, s = mpf_cos_sin(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + re = mpf_mul(c, ch, prec, rnd) + im = mpf_mul(s, sh, prec, rnd) + return re, mpf_neg(im) + +def mpc_sin((a, b), prec, rnd=round_fast): + """Complex sine. We have sin(a+bi) = sin(a)*cosh(b) + + cos(a)*sinh(b)*i. See the docstring for mpc_cos for additional + comments.""" + if a == fzero: + return fzero, mpf_sinh(b, prec, rnd) + wp = prec + 6 + c, s = mpf_cos_sin(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + re = mpf_mul(s, ch, prec, rnd) + im = mpf_mul(c, sh, prec, rnd) + return re, im + +def mpc_tan(z, prec, rnd=round_fast): + """Complex tangent. Computed as tan(a+bi) = sin(2a)/M + sinh(2b)/M*i + where M = cos(2a) + cosh(2b).""" + a, b = z + asign, aman, aexp, abc = a + bsign, bman, bexp, bbc = b + if b == fzero: return mpf_tan(a, prec, rnd), fzero + if a == fzero: return fzero, mpf_tanh(b, prec, rnd) + wp = prec + 15 + a = mpf_shift(a, 1) + b = mpf_shift(b, 1) + c, s = mpf_cos_sin(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + # TODO: handle cancellation when c ~= -1 and ch ~= 1 + mag = mpf_add(c, ch, wp) + re = mpf_div(s, mag, prec, rnd) + im = mpf_div(sh, mag, prec, rnd) + return re, im + +def mpc_cos_pi((a, b), prec, rnd=round_fast): + b = mpf_mul(b, mpf_pi(prec+5), prec+5) + if a == fzero: + return mpf_cosh(b, prec, rnd), fzero + wp = prec + 6 + c, s = mpf_cos_sin_pi(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + re = mpf_mul(c, ch, prec, rnd) + im = mpf_mul(s, sh, prec, rnd) + return re, mpf_neg(im) + +def mpc_sin_pi((a, b), prec, rnd=round_fast): + b = mpf_mul(b, mpf_pi(prec+5), prec+5) + if a == fzero: + return fzero, mpf_sinh(b, prec, rnd) + wp = prec + 6 + c, s = mpf_cos_sin_pi(a, wp) + ch, sh = mpf_cosh_sinh(b, wp) + re = mpf_mul(s, ch, prec, rnd) + im = mpf_mul(c, sh, prec, rnd) + return re, im + +def mpc_cosh((a, b), prec, rnd=round_fast): + """Complex hyperbolic cosine. Computed as cosh(z) = cos(z*i).""" + return mpc_cos((b, mpf_neg(a)), prec, rnd) + +def mpc_sinh((a, b), prec, rnd=round_fast): + """Complex hyperbolic sine. Computed as sinh(z) = -i*sin(z*i).""" + b, a = mpc_sin((b, a), prec, rnd) + return a, b + +def mpc_tanh((a, b), prec, rnd=round_fast): + """Complex hyperbolic tangent. Computed as tanh(z) = -i*tan(z*i).""" + b, a = mpc_tan((b, a), prec, rnd) + return a, b + +# TODO: avoid loss of accuracy +def mpc_atan(z, prec, rnd=round_fast): + a, b = z + # atan(z) = (I/2)*(log(1-I*z) - log(1+I*z)) + # x = 1-I*z = 1 + b - I*a + # y = 1+I*z = 1 - b + I*a + wp = prec + 15 + x = mpf_add(fone, b, wp), mpf_neg(a) + y = mpf_sub(fone, b, wp), a + l1 = mpc_log(x, wp) + l2 = mpc_log(y, wp) + a, b = mpc_sub(l1, l2, prec, rnd) + # (I/2) * (a+b*I) = (-b/2 + a/2*I) + v = mpf_neg(mpf_shift(b,-1)), mpf_shift(a,-1) + # Subtraction at infinity gives correct real part but + # wrong imaginary part (should be zero) + if v[1] == fnan and mpc_is_inf(z): + v = (v[0], fzero) + return v + +beta_crossover = from_float(0.6417) +alpha_crossover = from_float(1.5) + +def acos_asin(z, prec, rnd, n): + """ complex acos for n = 0, asin for n = 1 + The algorithm is described in + T.E. Hull, T.F. Fairgrieve and P.T.P. Tang + 'Implementing the Complex Arcsine and Arcosine Functions + using Exception Handling', + ACM Trans. on Math. Software Vol. 23 (1997), p299 + The complex acos and asin can be defined as + acos(z) = acos(beta) - I*sign(a)* log(alpha + sqrt(alpha**2 -1)) + asin(z) = asin(beta) + I*sign(a)* log(alpha + sqrt(alpha**2 -1)) + where z = a + I*b + alpha = (1/2)*(r + s); beta = (1/2)*(r - s) = a/alpha + r = sqrt((a+1)**2 + y**2); s = sqrt((a-1)**2 + y**2) + These expressions are rewritten in different ways in different + regions, delimited by two crossovers alpha_crossover and beta_crossover, + and by abs(a) <= 1, in order to improve the numerical accuracy. + """ + a, b = z + wp = prec + 10 + # special cases with real argument + if b == fzero: + am = mpf_sub(fone, mpf_abs(a), wp) + # case abs(a) <= 1 + if not am[0]: + if n == 0: + return mpf_acos(a, prec, rnd), fzero + else: + return mpf_asin(a, prec, rnd), fzero + # cases abs(a) > 1 + else: + # case a < -1 + if a[0]: + pi = mpf_pi(prec, rnd) + c = mpf_acosh(mpf_neg(a), prec, rnd) + if n == 0: + return pi, mpf_neg(c) + else: + return mpf_neg(mpf_shift(pi, -1)), c + # case a > 1 + else: + c = mpf_acosh(a, prec, rnd) + if n == 0: + return fzero, c + else: + pi = mpf_pi(prec, rnd) + return mpf_shift(pi, -1), mpf_neg(c) + asign = bsign = 0 + if a[0]: + a = mpf_neg(a) + asign = 1 + if b[0]: + b = mpf_neg(b) + bsign = 1 + am = mpf_sub(fone, a, wp) + ap = mpf_add(fone, a, wp) + r = mpf_hypot(ap, b, wp) + s = mpf_hypot(am, b, wp) + alpha = mpf_shift(mpf_add(r, s, wp), -1) + beta = mpf_div(a, alpha, wp) + b2 = mpf_mul(b,b, wp) + # case beta <= beta_crossover + if not mpf_sub(beta_crossover, beta, wp)[0]: + if n == 0: + re = mpf_acos(beta, wp) + else: + re = mpf_asin(beta, wp) + else: + # to compute the real part in this region use the identity + # asin(beta) = atan(beta/sqrt(1-beta**2)) + # beta/sqrt(1-beta**2) = (alpha + a) * (alpha - a) + # alpha + a is numerically accurate; alpha - a can have + # cancellations leading to numerical inaccuracies, so rewrite + # it in differente ways according to the region + Ax = mpf_add(alpha, a, wp) + # case a <= 1 + if not am[0]: + # c = b*b/(r + (a+1)); d = (s + (1-a)) + # alpha - a = (1/2)*(c + d) + # case n=0: re = atan(sqrt((1/2) * Ax * (c + d))/a) + # case n=1: re = atan(a/sqrt((1/2) * Ax * (c + d))) + c = mpf_div(b2, mpf_add(r, ap, wp), wp) + d = mpf_add(s, am, wp) + re = mpf_shift(mpf_mul(Ax, mpf_add(c, d, wp), wp), -1) + if n == 0: + re = mpf_atan(mpf_div(mpf_sqrt(re, wp), a, wp), wp) + else: + re = mpf_atan(mpf_div(a, mpf_sqrt(re, wp), wp), wp) + else: + # c = Ax/(r + (a+1)); d = Ax/(s - (1-a)) + # alpha - a = (1/2)*(c + d) + # case n = 0: re = atan(b*sqrt(c + d)/2/a) + # case n = 1: re = atan(a/(b*sqrt(c + d)/2) + c = mpf_div(Ax, mpf_add(r, ap, wp), wp) + d = mpf_div(Ax, mpf_sub(s, am, wp), wp) + re = mpf_shift(mpf_add(c, d, wp), -1) + re = mpf_mul(b, mpf_sqrt(re, wp), wp) + if n == 0: + re = mpf_atan(mpf_div(re, a, wp), wp) + else: + re = mpf_atan(mpf_div(a, re, wp), wp) + # to compute alpha + sqrt(alpha**2 - 1), if alpha <= alpha_crossover + # replace it with 1 + Am1 + sqrt(Am1*(alpha+1))) + # where Am1 = alpha -1 + # if alpha <= alpha_crossover: + if not mpf_sub(alpha_crossover, alpha, wp)[0]: + c1 = mpf_div(b2, mpf_add(r, ap, wp), wp) + # case a < 1 + if mpf_neg(am)[0]: + # Am1 = (1/2) * (b*b/(r + (a+1)) + b*b/(s + (1-a)) + c2 = mpf_add(s, am, wp) + c2 = mpf_div(b2, c2, wp) + Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) + else: + # Am1 = (1/2) * (b*b/(r + (a+1)) + (s - (1-a))) + c2 = mpf_sub(s, am, wp) + Am1 = mpf_shift(mpf_add(c1, c2, wp), -1) + # im = log(1 + Am1 + sqrt(Am1*(alpha+1))) + im = mpf_mul(Am1, mpf_add(alpha, fone, wp), wp) + im = mpf_log(mpf_add(fone, mpf_add(Am1, mpf_sqrt(im, wp), wp), wp), wp) + else: + # im = log(alpha + sqrt(alpha*alpha - 1)) + im = mpf_sqrt(mpf_sub(mpf_mul(alpha, alpha, wp), fone, wp), wp) + im = mpf_log(mpf_add(alpha, im, wp), wp) + if asign: + if n == 0: + re = mpf_sub(mpf_pi(wp), re, wp) + else: + re = mpf_neg(re) + if not bsign and n == 0: + im = mpf_neg(im) + if bsign and n == 1: + im = mpf_neg(im) + re = normalize(re[0], re[1], re[2], re[3], prec, rnd) + im = normalize(im[0], im[1], im[2], im[3], prec, rnd) + return re, im + +def mpc_acos(z, prec, rnd=round_fast): + return acos_asin(z, prec, rnd, 0) + +def mpc_asin(z, prec, rnd=round_fast): + return acos_asin(z, prec, rnd, 1) + +def mpc_asinh(z, prec, rnd=round_fast): + # asinh(z) = I * asin(-I z) + a, b = z + a, b = mpc_asin((b, mpf_neg(a)), prec, rnd) + return mpf_neg(b), a + +def mpc_acosh(z, prec, rnd=round_fast): + # acosh(z) = -I * acos(z) for Im(acos(z)) <= 0 + # +I * acos(z) otherwise + a, b = mpc_acos(z, prec, rnd) + if b[0] or b == fzero: + return mpf_neg(b), a + else: + return b, mpf_neg(a) + +def mpc_atanh(z, prec, rnd=round_fast): + # atanh(z) = (log(1+z)-log(1-z))/2 + wp = prec + 15 + a = mpc_add(z, mpc_one, wp) + b = mpc_sub(mpc_one, z, wp) + a = mpc_log(a, wp) + b = mpc_log(b, wp) + v = mpc_shift(mpc_sub(a, b, wp), -1) + # Subtraction at infinity gives correct imaginary part but + # wrong real part (should be zero) + if v[0] == fnan and mpc_is_inf(z): + v = (fzero, v[1]) + return v + +def mpc_fibonacci(z, prec, rnd=round_fast): + re, im = z + if im == fzero: + return (mpf_fibonacci(re, prec, rnd), fzero) + size = max(abs(re[2]+re[3]), abs(re[2]+re[3])) + wp = prec + size + 20 + a = mpf_phi(wp) + b = mpf_add(mpf_shift(a, 1), fnone, wp) + u = mpc_pow((a, fzero), z, wp) + v = mpc_cos_pi(z, wp) + v = mpc_div(v, u, wp) + u = mpc_sub(u, v, wp) + u = mpc_div_mpf(u, b, prec, rnd) + return u + +def mpf_expj(x, prec, rnd='f'): + raise ComplexResult + +def mpc_expj(z, prec, rnd='f'): + re, im = z + if im == fzero: + return mpf_cos_sin(re, prec, rnd) + if re == fzero: + return mpf_exp(mpf_neg(im), prec, rnd), fzero + ey = mpf_exp(mpf_neg(im), prec+10) + c, s = mpf_cos_sin(re, prec+10) + re = mpf_mul(ey, c, prec, rnd) + im = mpf_mul(ey, s, prec, rnd) + return re, im + +def mpf_expjpi(x, prec, rnd='f'): + raise ComplexResult + +def mpc_expjpi(z, prec, rnd='f'): + re, im = z + if im == fzero: + return mpf_cos_sin_pi(re, prec, rnd) + sign, man, exp, bc = im + wp = prec+10 + if man: + wp += max(0, exp+bc) + im = mpf_neg(mpf_mul(mpf_pi(wp), im, wp)) + if re == fzero: + return mpf_exp(im, prec, rnd), fzero + ey = mpf_exp(im, prec+10) + c, s = mpf_cos_sin_pi(re, prec+10) + re = mpf_mul(ey, c, prec, rnd) + im = mpf_mul(ey, s, prec, rnd) + return re, im diff --git a/compiler/gdsMill/mpmath/libmp/libmpf.py b/compiler/gdsMill/mpmath/libmp/libmpf.py new file mode 100644 index 00000000..ee517454 --- /dev/null +++ b/compiler/gdsMill/mpmath/libmp/libmpf.py @@ -0,0 +1,1317 @@ +""" +Low-level functions for arbitrary-precision floating-point arithmetic. +""" + +__docformat__ = 'plaintext' + +import math + +from bisect import bisect + +# Importing random is slow +#from random import getrandbits +getrandbits = None + +from backend import (MPZ, MPZ_TYPE, MPZ_ZERO, MPZ_ONE, MPZ_TWO, MPZ_FIVE, + BACKEND, STRICT, gmpy, sage, sage_utils) + +from libintmath import (giant_steps, + trailtable, bctable, lshift, rshift, bitcount, trailing, + sqrt_fixed, numeral, isqrt, isqrt_fast, sqrtrem, + bin_to_radix) + +# We don't pickle tuples directly for the following reasons: +# 1: pickle uses str() for ints, which is inefficient when they are large +# 2: pickle doesn't work for gmpy mpzs +# Both problems are solved by using hex() + +if BACKEND == 'sage': + def to_pickable(x): + sign, man, exp, bc = x + return sign, hex(man), exp, bc +else: + def to_pickable(x): + sign, man, exp, bc = x + return sign, hex(man)[2:], exp, bc + +def from_pickable(x): + sign, man, exp, bc = x + return (sign, MPZ(man, 16), exp, bc) + +class ComplexResult(ValueError): + pass + +# All supported rounding modes +round_nearest = intern('n') +round_floor = intern('f') +round_ceiling = intern('c') +round_up = intern('u') +round_down = intern('d') +round_fast = round_down + +def prec_to_dps(n): + """Return number of accurate decimals that can be represented + with a precision of n bits.""" + return max(1, int(round(int(n)/3.3219280948873626)-1)) + +def dps_to_prec(n): + """Return the number of bits required to represent n decimals + accurately.""" + return max(1, int(round((int(n)+1)*3.3219280948873626))) + +def repr_dps(n): + """Return the number of decimal digits required to represent + a number with n-bit precision so that it can be uniquely + reconstructed from the representation.""" + dps = prec_to_dps(n) + if dps == 15: + return 17 + return dps + 3 + +#----------------------------------------------------------------------------# +# Some commonly needed float values # +#----------------------------------------------------------------------------# + +# Regular number format: +# (-1)**sign * mantissa * 2**exponent, plus bitcount of mantissa +fzero = (0, MPZ_ZERO, 0, 0) +fnzero = (1, MPZ_ZERO, 0, 0) +fone = (0, MPZ_ONE, 0, 1) +fnone = (1, MPZ_ONE, 0, 1) +ftwo = (0, MPZ_ONE, 1, 1) +ften = (0, MPZ_FIVE, 1, 3) +fhalf = (0, MPZ_ONE, -1, 1) + +# Arbitrary encoding for special numbers: zero mantissa, nonzero exponent +fnan = (0, MPZ_ZERO, -123, -1) +finf = (0, MPZ_ZERO, -456, -2) +fninf = (1, MPZ_ZERO, -789, -3) + +# Was 1e1000; this is broken in Python 2.4 +math_float_inf = 1e300 * 1e300 + + +#----------------------------------------------------------------------------# +# Rounding # +#----------------------------------------------------------------------------# + +# This function can be used to round a mantissa generally. However, +# we will try to do most rounding inline for efficiency. +def round_int(x, n, rnd): + if rnd is round_nearest: + if x >= 0: + t = x >> (n-1) + if t & 1 and ((t & 2) or (x & h_mask[n<300][n])): + return (t>>1)+1 + else: + return t>>1 + else: + return -round_int(-x, n, rnd) + if rnd is round_floor: + return x >> n + if rnd is round_ceiling: + return -((-x) >> n) + if rnd is round_down: + if x >= 0: + return x >> n + return -((-x) >> n) + if rnd is round_up: + if x >= 0: + return -((-x) >> n) + return x >> n + +# These masks are used to pick out segments of numbers to determine +# which direction to round when rounding to nearest. +class h_mask_big: + def __getitem__(self, n): + return (MPZ_ONE<<(n-1))-1 + +h_mask_small = [0]+[((MPZ_ONE<<(_-1))-1) for _ in range(1, 300)] +h_mask = [h_mask_big(), h_mask_small] + +# The >> operator rounds to floor. shifts_down[rnd][sign] +# tells whether this is the right direction to use, or if the +# number should be negated before shifting +shifts_down = {round_floor:(1,0), round_ceiling:(0,1), + round_down:(1,1), round_up:(0,0)} + + +#----------------------------------------------------------------------------# +# Normalization of raw mpfs # +#----------------------------------------------------------------------------# + +# This function is called almost every time an mpf is created. +# It has been optimized accordingly. + +def _normalize(sign, man, exp, bc, prec, rnd): + """ + Create a raw mpf tuple with value (-1)**sign * man * 2**exp and + normalized mantissa. The mantissa is rounded in the specified + direction if its size exceeds the precision. Trailing zero bits + are also stripped from the mantissa to ensure that the + representation is canonical. + + Conditions on the input: + * The input must represent a regular (finite) number + * The sign bit must be 0 or 1 + * The mantissa must be positive + * The exponent must be an integer + * The bitcount must be exact + + If these conditions are not met, use from_man_exp, mpf_pos, or any + of the conversion functions to create normalized raw mpf tuples. + """ + if not man: + return fzero + # Cut mantissa down to size if larger than target precision + n = bc - prec + if n > 0: + if rnd is round_nearest: + t = man >> (n-1) + if t & 1 and ((t & 2) or (man & h_mask[n<300][n])): + man = (t>>1)+1 + else: + man = t>>1 + elif shifts_down[rnd][sign]: + man >>= n + else: + man = -((-man)>>n) + exp += n + bc = prec + # Strip trailing bits + if not man & 1: + t = trailtable[int(man & 255)] + if not t: + while not man & 255: + man >>= 8 + exp += 8 + bc -= 8 + t = trailtable[int(man & 255)] + man >>= t + exp += t + bc -= t + # Bit count can be wrong if the input mantissa was 1 less than + # a power of 2 and got rounded up, thereby adding an extra bit. + # With trailing bits removed, all powers of two have mantissa 1, + # so this is easy to check for. + if man == 1: + bc = 1 + return sign, man, exp, bc + +def _normalize1(sign, man, exp, bc, prec, rnd): + """same as normalize, but with the added condition that + man is odd or zero + """ + if not man: + return fzero + if bc <= prec: + return sign, man, exp, bc + n = bc - prec + if rnd is round_nearest: + t = man >> (n-1) + if t & 1 and ((t & 2) or (man & h_mask[n<300][n])): + man = (t>>1)+1 + else: + man = t>>1 + elif shifts_down[rnd][sign]: + man >>= n + else: + man = -((-man)>>n) + exp += n + bc = prec + # Strip trailing bits + if not man & 1: + t = trailtable[int(man & 255)] + if not t: + while not man & 255: + man >>= 8 + exp += 8 + bc -= 8 + t = trailtable[int(man & 255)] + man >>= t + exp += t + bc -= t + # Bit count can be wrong if the input mantissa was 1 less than + # a power of 2 and got rounded up, thereby adding an extra bit. + # With trailing bits removed, all powers of two have mantissa 1, + # so this is easy to check for. + if man == 1: + bc = 1 + return sign, man, exp, bc + +def strict_normalize(sign, man, exp, bc, prec, rnd): + """Additional checks on the components of an mpf. Enable tests by setting + the environment variable MPMATH_STRICT to Y.""" + assert type(man) == MPZ_TYPE + assert type(bc) in (int, long) + assert type(exp) in (int, long) + assert bc == bitcount(man) + return _normalize(sign, man, exp, bc, prec, rnd) + +def strict_normalize1(sign, man, exp, bc, prec, rnd): + """Additional checks on the components of an mpf. Enable tests by setting + the environment variable MPMATH_STRICT to Y.""" + assert type(man) == MPZ_TYPE + assert type(bc) in (int, long) + assert type(exp) in (int, long) + assert bc == bitcount(man) + assert (not man) or (man & 1) + return _normalize1(sign, man, exp, bc, prec, rnd) + +if BACKEND == 'gmpy' and '_mpmath_normalize' in dir(gmpy): + _normalize = gmpy._mpmath_normalize + _normalize1 = gmpy._mpmath_normalize + +if BACKEND == 'sage': + _normalize = _normalize1 = sage_utils.normalize + +if STRICT: + normalize = strict_normalize + normalize1 = strict_normalize1 +else: + normalize = _normalize + normalize1 = _normalize1 + +#----------------------------------------------------------------------------# +# Conversion functions # +#----------------------------------------------------------------------------# + +def from_man_exp(man, exp, prec=None, rnd=round_fast): + """Create raw mpf from (man, exp) pair. The mantissa may be signed. + If no precision is specified, the mantissa is stored exactly.""" + man = MPZ(man) + sign = 0 + if man < 0: + sign = 1 + man = -man + if man < 1024: + bc = bctable[int(man)] + else: + bc = bitcount(man) + if not prec: + if not man: + return fzero + if not man & 1: + if man & 2: + return (sign, man >> 1, exp + 1, bc - 1) + t = trailtable[int(man & 255)] + if not t: + while not man & 255: + man >>= 8 + exp += 8 + bc -= 8 + t = trailtable[int(man & 255)] + man >>= t + exp += t + bc -= t + return (sign, man, exp, bc) + return normalize(sign, man, exp, bc, prec, rnd) + +int_cache = dict((n, from_man_exp(n, 0)) for n in range(-10, 257)) + +if BACKEND == 'gmpy' and '_mpmath_create' in dir(gmpy): + from_man_exp = gmpy._mpmath_create + +if BACKEND == 'sage': + from_man_exp = sage_utils.from_man_exp + +def from_int(n, prec=0, rnd=round_fast): + """Create a raw mpf from an integer. If no precision is specified, + the mantissa is stored exactly.""" + if not prec: + if n in int_cache: + return int_cache[n] + return from_man_exp(n, 0, prec, rnd) + +def to_man_exp(s): + """Return (man, exp) of a raw mpf. Raise an error if inf/nan.""" + sign, man, exp, bc = s + if (not man) and exp: + raise ValueError("mantissa and exponent are undefined for %s" % man) + return man, exp + +def to_int(s, rnd=None): + """Convert a raw mpf to the nearest int. Rounding is done down by + default (same as int(float) in Python), but can be changed. If the + input is inf/nan, an exception is raised.""" + sign, man, exp, bc = s + if (not man) and exp: + raise ValueError("cannot convert %s to int" % man) + if exp >= 0: + if sign: + return (-man) << exp + return man << exp + # Make default rounding fast + if not rnd: + if sign: + return -(man >> (-exp)) + else: + return man >> (-exp) + if sign: + return round_int(-man, -exp, rnd) + else: + return round_int(man, -exp, rnd) + +def mpf_ceil(s, prec, rnd=round_fast): + """Calculate ceil of a raw mpf, and round the result in the given + direction (not necessarily ceiling). Note: returns a raw mpf + representing an integer, not a Python int.""" + sign, man, exp, bc = s + if (not man) and exp: + return s + if exp > 0: + return mpf_pos(s, prec, rnd) + return from_int(to_int(s, round_ceiling), prec, rnd) + +def mpf_floor(s, prec, rnd=round_fast): + """Calculate floor of a raw mpf, and round the result in the given + direction (not necessarily floor). Note: returns a raw mpf + representing an integer, not a Python int.""" + sign, man, exp, bc = s + if (not man) and exp: + return s + if exp > 0: + return mpf_pos(s, prec, rnd) + return from_int(to_int(s, round_floor), prec, rnd) + +def from_float(x, prec=53, rnd=round_fast): + """Create a raw mpf from a Python float, rounding if necessary. + If prec >= 53, the result is guaranteed to represent exactly the + same number as the input. If prec is not specified, use prec=53.""" + # frexp only raises an exception for nan on some platforms + if x != x: + return fnan + # in Python2.5 math.frexp gives an exception for float infinity + # in Python2.6 it returns (float infinity, 0) + try: + m, e = math.frexp(x) + except: + if x == math_float_inf: return finf + if x == -math_float_inf: return fninf + return fnan + if x == math_float_inf: return finf + if x == -math_float_inf: return fninf + return from_man_exp(int(m*(1<<53)), e-53, prec, rnd) + +def to_float(s, strict=False): + """ + Convert a raw mpf to a Python float. The result is exact if the + bitcount of s is <= 53 and no underflow/overflow occurs. + + If the number is too large or too small to represent as a regular + float, it will be converted to inf or 0.0. Setting strict=True + forces an OverflowError to be raised instead. + """ + sign, man, exp, bc = s + if not man: + if s == fzero: return 0.0 + if s == finf: return math_float_inf + if s == fninf: return -math_float_inf + return math_float_inf/math_float_inf + if sign: + man = -man + try: + if bc < 100: + return math.ldexp(man, exp) + # Try resizing the mantissa. Overflow may still happen here. + n = bc - 53 + m = man >> n + return math.ldexp(m, exp + n) + except OverflowError: + if strict: + raise + # Overflow to infinity + if exp + bc > 0: + if sign: + return -math_float_inf + else: + return math_float_inf + # Underflow to zero + return 0.0 + +def from_rational(p, q, prec, rnd=round_fast): + """Create a raw mpf from a rational number p/q, round if + necessary.""" + return mpf_div(from_int(p), from_int(q), prec, rnd) + +def to_rational(s): + """Convert a raw mpf to a rational number. Return integers (p, q) + such that s = p/q exactly.""" + sign, man, exp, bc = s + if sign: + man = -man + if bc == -1: + raise ValueError("cannot convert %s to a rational number" % man) + if exp >= 0: + return man * (1<= 0: return (-man) << offset + else: return (-man) >> (-offset) + else: + if offset >= 0: return man << offset + else: return man >> (-offset) + + +############################################################################## +############################################################################## + +#----------------------------------------------------------------------------# +# Arithmetic operations, etc. # +#----------------------------------------------------------------------------# + +def mpf_rand(prec): + """Return a raw mpf chosen randomly from [0, 1), with prec bits + in the mantissa.""" + global getrandbits + if not getrandbits: + import random + getrandbits = random.getrandbits + return from_man_exp(getrandbits(prec), -prec, prec, round_floor) + +def mpf_eq(s, t): + """Test equality of two raw mpfs. This is simply tuple comparion + unless either number is nan, in which case the result is False.""" + if not s[1] or not t[1]: + if s == fnan or t == fnan: + return False + return s == t + +def mpf_hash(s): + try: + # Try to be compatible with hash values for floats and ints + return hash(to_float(s, strict=1)) + except OverflowError: + # We must unfortunately sacrifice compatibility with ints here. We + # could do hash(man << exp) when the exponent is positive, but + # this would cause unreasonable inefficiency for large numbers. + return hash(s) + +def mpf_cmp(s, t): + """Compare the raw mpfs s and t. Return -1 if s < t, 0 if s == t, + and 1 if s > t. (Same convention as Python's cmp() function.)""" + + # In principle, a comparison amounts to determining the sign of s-t. + # A full subtraction is relatively slow, however, so we first try to + # look at the components. + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + + # Handle zeros and special numbers + if not sman or not tman: + if s == fzero: return -mpf_sign(t) + if t == fzero: return mpf_sign(s) + if s == t: return 0 + # Follow same convention as Python's cmp for float nan + if t == fnan: return 1 + if s == finf: return 1 + if t == fninf: return 1 + return -1 + # Different sides of zero + if ssign != tsign: + if not ssign: return 1 + return -1 + # This reduces to direct integer comparison + if sexp == texp: + if ssign: return -cmp(sman, tman) + else: return cmp(sman, tman) + # Check position of the highest set bit in each number. If + # different, there is certainly an inequality. + a = sbc + sexp + b = tbc + texp + if ssign: + if a < b: return 1 + if a > b: return -1 + else: + if a < b: return -1 + if a > b: return 1 + + # Both numbers have the same highest bit. Subtract to find + # how the lower bits compare. + delta = mpf_sub(s, t, 5, round_floor) + if delta[0]: + return -1 + return 1 + +def mpf_lt(s, t): + if s == fnan or t == fnan: + return False + return mpf_cmp(s, t) < 0 + +def mpf_le(s, t): + if s == fnan or t == fnan: + return False + return mpf_cmp(s, t) <= 0 + +def mpf_gt(s, t): + if s == fnan or t == fnan: + return False + return mpf_cmp(s, t) > 0 + +def mpf_ge(s, t): + if s == fnan or t == fnan: + return False + return mpf_cmp(s, t) >= 0 + +def mpf_pos(s, prec, rnd=round_fast): + """Calculate 0+s for a raw mpf (i.e., just round s to the specified + precision).""" + sign, man, exp, bc = s + if (not man) and exp: + return s + return normalize1(sign, man, exp, bc, prec, rnd) + +def mpf_neg(s, prec=None, rnd=round_fast): + """Negate a raw mpf (return -s), rounding the result to the + specified precision. The prec argument can be omitted to do the + operation exactly.""" + sign, man, exp, bc = s + if not man: + if exp: + if s == finf: return fninf + if s == fninf: return finf + return s + if not prec: + return (1-sign, man, exp, bc) + return normalize1(1-sign, man, exp, bc, prec, rnd) + +def mpf_abs(s, prec=None, rnd=round_fast): + """Return abs(s) of the raw mpf s, rounded to the specified + precision. The prec argument can be omitted to generate an + exact result.""" + sign, man, exp, bc = s + if (not man) and exp: + if s == fninf: + return finf + return s + if not prec: + if sign: + return (0, man, exp, bc) + return s + return normalize1(0, man, exp, bc, prec, rnd) + +def mpf_sign(s): + """Return -1, 0, or 1 (as a Python int, not a raw mpf) depending on + whether s is negative, zero, or positive. (Nan is taken to give 0.)""" + sign, man, exp, bc = s + if not man: + if s == finf: return 1 + if s == fninf: return -1 + return 0 + return (-1) ** sign + +def mpf_add(s, t, prec=0, rnd=round_fast, _sub=0): + """ + Add the two raw mpf values s and t. + + With prec=0, no rounding is performed. Note that this can + produce a very large mantissa (potentially too large to fit + in memory) if exponents are far apart. + """ + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + tsign ^= _sub + # Standard case: two nonzero, regular numbers + if sman and tman: + offset = sexp - texp + if offset: + if offset > 0: + # Outside precision range; only need to perturb + if offset > 100 and prec: + delta = sbc + sexp - tbc - texp + if delta > prec + 4: + offset = prec + 4 + sman <<= offset + if tsign == ssign: sman += 1 + else: sman -= 1 + return normalize1(ssign, sman, sexp-offset, + bitcount(sman), prec, rnd) + # Add + if ssign == tsign: + man = tman + (sman << offset) + # Subtract + else: + if ssign: man = tman - (sman << offset) + else: man = (sman << offset) - tman + if man >= 0: + ssign = 0 + else: + man = -man + ssign = 1 + bc = bitcount(man) + return normalize1(ssign, man, texp, bc, prec or bc, rnd) + elif offset < 0: + # Outside precision range; only need to perturb + if offset < -100 and prec: + delta = tbc + texp - sbc - sexp + if delta > prec + 4: + offset = prec + 4 + tman <<= offset + if ssign == tsign: tman += 1 + else: tman -= 1 + return normalize1(tsign, tman, texp-offset, + bitcount(tman), prec, rnd) + # Add + if ssign == tsign: + man = sman + (tman << -offset) + # Subtract + else: + if tsign: man = sman - (tman << -offset) + else: man = (tman << -offset) - sman + if man >= 0: + ssign = 0 + else: + man = -man + ssign = 1 + bc = bitcount(man) + return normalize1(ssign, man, sexp, bc, prec or bc, rnd) + # Equal exponents; no shifting necessary + if ssign == tsign: + man = tman + sman + else: + if ssign: man = tman - sman + else: man = sman - tman + if man >= 0: + ssign = 0 + else: + man = -man + ssign = 1 + bc = bitcount(man) + return normalize(ssign, man, texp, bc, prec or bc, rnd) + # Handle zeros and special numbers + if _sub: + t = mpf_neg(t) + if not sman: + if sexp: + if s == t or tman or not texp: + return s + return fnan + if tman: + return normalize1(tsign, tman, texp, tbc, prec or tbc, rnd) + return t + if texp: + return t + if sman: + return normalize1(ssign, sman, sexp, sbc, prec or sbc, rnd) + return s + +def mpf_sub(s, t, prec=0, rnd=round_fast): + """Return the difference of two raw mpfs, s-t. This function is + simply a wrapper of mpf_add that changes the sign of t.""" + return mpf_add(s, t, prec, rnd, 1) + +def mpf_sum(xs, prec=0, rnd=round_fast, absolute=False): + """ + Sum a list of mpf values efficiently and accurately + (typically no temporary roundoff occurs). If prec=0, + the final result will not be rounded either. + + There may be roundoff error or cancellation if extremely + large exponent differences occur. + + With absolute=True, sums the absolute values. + """ + man = 0 + exp = 0 + max_extra_prec = prec*2 or 1000000 # XXX + special = None + for x in xs: + xsign, xman, xexp, xbc = x + if xman: + if xsign and not absolute: + xman = -xman + delta = xexp - exp + if xexp >= exp: + # x much larger than existing sum? + # first: quick test + if (delta > max_extra_prec) and \ + ((not man) or delta-bitcount(abs(man)) > max_extra_prec): + man = xman + exp = xexp + else: + man += (xman << delta) + else: + delta = -delta + # x much smaller than existing sum? + if delta-xbc > max_extra_prec: + if not man: + man, exp = xman, xexp + else: + man = (man << delta) + xman + exp = xexp + elif xexp: + if absolute: + x = mpf_abs(x) + special = mpf_add(special or fzero, x, 1) + # Will be inf or nan + if special: + return special + return from_man_exp(man, exp, prec, rnd) + +def gmpy_mpf_mul(s, t, prec=0, rnd=round_fast): + """Multiply two raw mpfs""" + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + sign = ssign ^ tsign + man = sman*tman + if man: + bc = bitcount(man) + if prec: + return normalize1(sign, man, sexp+texp, bc, prec, rnd) + else: + return (sign, man, sexp+texp, bc) + s_special = (not sman) and sexp + t_special = (not tman) and texp + if not s_special and not t_special: + return fzero + if fnan in (s, t): return fnan + if (not tman) and texp: s, t = t, s + if t == fzero: return fnan + return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] + +def gmpy_mpf_mul_int(s, n, prec, rnd=round_fast): + """Multiply by a Python integer.""" + sign, man, exp, bc = s + if not man: + return mpf_mul(s, from_int(n), prec, rnd) + if not n: + return fzero + if n < 0: + sign ^= 1 + n = -n + man *= n + return normalize(sign, man, exp, bitcount(man), prec, rnd) + +def python_mpf_mul(s, t, prec=0, rnd=round_fast): + """Multiply two raw mpfs""" + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + sign = ssign ^ tsign + man = sman*tman + if man: + bc = sbc + tbc - 1 + bc += int(man>>bc) + if prec: + return normalize1(sign, man, sexp+texp, bc, prec, rnd) + else: + return (sign, man, sexp+texp, bc) + s_special = (not sman) and sexp + t_special = (not tman) and texp + if not s_special and not t_special: + return fzero + if fnan in (s, t): return fnan + if (not tman) and texp: s, t = t, s + if t == fzero: return fnan + return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] + +def python_mpf_mul_int(s, n, prec, rnd=round_fast): + """Multiply by a Python integer.""" + sign, man, exp, bc = s + if not man: + return mpf_mul(s, from_int(n), prec, rnd) + if not n: + return fzero + if n < 0: + sign ^= 1 + n = -n + man *= n + # Generally n will be small + if n < 1024: + bc += bctable[int(n)] - 1 + else: + bc += bitcount(n) - 1 + bc += int(man>>bc) + return normalize(sign, man, exp, bc, prec, rnd) + + +if BACKEND == 'gmpy': + mpf_mul = gmpy_mpf_mul + mpf_mul_int = gmpy_mpf_mul_int +else: + mpf_mul = python_mpf_mul + mpf_mul_int = python_mpf_mul_int + +def mpf_shift(s, n): + """Quickly multiply the raw mpf s by 2**n without rounding.""" + sign, man, exp, bc = s + if not man: + return s + return sign, man, exp+n, bc + +def mpf_frexp(x): + """Convert x = y*2**n to (y, n) with abs(y) in [0.5, 1) if nonzero""" + sign, man, exp, bc = x + if not man: + if x == fzero: + return (fzero, 0) + else: + raise ValueError + return mpf_shift(x, -bc-exp), bc+exp + +def mpf_div(s, t, prec, rnd=round_fast): + """Floating-point division""" + ssign, sman, sexp, sbc = s + tsign, tman, texp, tbc = t + if not sman or not tman: + if s == fzero: + if t == fzero: raise ZeroDivisionError + if t == fnan: return fnan + return fzero + if t == fzero: + raise ZeroDivisionError + s_special = (not sman) and sexp + t_special = (not tman) and texp + if s_special and t_special: + return fnan + if s == fnan or t == fnan: + return fnan + if not t_special: + if t == fzero: + return fnan + return {1:finf, -1:fninf}[mpf_sign(s) * mpf_sign(t)] + return fzero + sign = ssign ^ tsign + if tman == 1: + return normalize1(sign, sman, sexp-texp, sbc, prec, rnd) + # Same strategy as for addition: if there is a remainder, perturb + # the result a few bits outside the precision range before rounding + extra = prec - sbc + tbc + 5 + if extra < 5: + extra = 5 + quot, rem = divmod(sman< sexp+sbc: + return s + # Another important special case: this allows us to do e.g. x % 1.0 + # to find the fractional part of x, and it will work when x is huge. + if tman == 1 and sexp > texp+tbc: + return fzero + base = min(sexp, texp) + sman = (-1)**ssign * sman + tman = (-1)**tsign * tman + man = (sman << (sexp-base)) % (tman << (texp-base)) + if man >= 0: + sign = 0 + else: + man = -man + sign = 1 + return normalize(sign, man, base, bitcount(man), prec, rnd) + +reciprocal_rnd = { + round_down : round_up, + round_up : round_down, + round_floor : round_ceiling, + round_ceiling : round_floor, + round_nearest : round_nearest +} + +negative_rnd = { + round_down : round_down, + round_up : round_up, + round_floor : round_ceiling, + round_ceiling : round_floor, + round_nearest : round_nearest +} + +def mpf_pow_int(s, n, prec, rnd=round_fast): + """Compute s**n, where s is a raw mpf and n is a Python integer.""" + sign, man, exp, bc = s + + if (not man) and exp: + if s == finf: + if n > 0: return s + if n == 0: return fnan + return fzero + if s == fninf: + if n > 0: return [finf, fninf][n & 1] + if n == 0: return fnan + return fzero + return fnan + + n = int(n) + if n == 0: return fone + if n == 1: return mpf_pos(s, prec, rnd) + if n == 2: + _, man, exp, bc = s + if not man: + return fzero + man = man*man + if man == 1: + return (0, MPZ_ONE, exp+exp, 1) + bc = bc + bc - 2 + bc += bctable[int(man>>bc)] + return normalize1(0, man, exp+exp, bc, prec, rnd) + if n == -1: return mpf_div(fone, s, prec, rnd) + if n < 0: + inverse = mpf_pow_int(s, -n, prec+5, reciprocal_rnd[rnd]) + return mpf_div(fone, inverse, prec, rnd) + + result_sign = sign & n + + # Use exact integer power when the exact mantissa is small + if man == 1: + return (result_sign, MPZ_ONE, exp*n, 1) + if bc*n < 1000: + man **= n + return normalize1(result_sign, man, exp*n, bitcount(man), prec, rnd) + + # Use directed rounding all the way through to maintain rigorous + # bounds for interval arithmetic + rounds_down = (rnd is round_nearest) or \ + shifts_down[rnd][result_sign] + + # Now we perform binary exponentiation. Need to estimate precision + # to avoid rounding errors from temporary operations. Roughly log_2(n) + # operations are performed. + workprec = prec + 4*bitcount(n) + 4 + _, pm, pe, pbc = fone + while 1: + if n & 1: + pm = pm*man + pe = pe+exp + pbc += bc - 2 + pbc = pbc + bctable[int(pm >> pbc)] + if pbc > workprec: + if rounds_down: + pm = pm >> (pbc-workprec) + else: + pm = -((-pm) >> (pbc-workprec)) + pe += pbc - workprec + pbc = workprec + n -= 1 + if not n: + break + man = man*man + exp = exp+exp + bc = bc + bc - 2 + bc = bc + bctable[int(man >> bc)] + if bc > workprec: + if rounds_down: + man = man >> (bc-workprec) + else: + man = -((-man) >> (bc-workprec)) + exp += bc - workprec + bc = workprec + n = n // 2 + + return normalize(result_sign, pm, pe, pbc, prec, rnd) + + +def mpf_perturb(x, eps_sign, prec, rnd): + """ + For nonzero x, calculate x + eps with directed rounding, where + eps < prec relatively and eps has the given sign (0 for + positive, 1 for negative). + + With rounding to nearest, this is taken to simply normalize + x to the given precision. + """ + if rnd is round_nearest: + return mpf_pos(x, prec, rnd) + sign, man, exp, bc = x + eps = (eps_sign, MPZ_ONE, exp+bc-prec-1, 1) + if sign: + away = (rnd in (round_down, round_ceiling)) ^ eps_sign + else: + away = (rnd in (round_up, round_ceiling)) ^ eps_sign + if away: + return mpf_add(x, eps, prec, rnd) + else: + return mpf_pos(x, prec, rnd) + + +#----------------------------------------------------------------------------# +# Radix conversion # +#----------------------------------------------------------------------------# + +def to_digits_exp(s, dps): + """Helper function for representing the floating-point number s as + a decimal with dps digits. Returns (sign, string, exponent) where + sign is '' or '-', string is the digit string, and exponent is + the decimal exponent as an int. + + If inexact, the decimal representation is rounded toward zero.""" + + # Extract sign first so it doesn't mess up the string digit count + if s[0]: + sign = '-' + s = mpf_neg(s) + else: + sign = '' + _sign, man, exp, bc = s + + if not man: + return '', '0', 0 + + bitprec = int(dps * math.log(10,2)) + 10 + + # Cut down to size + # TODO: account for precision when doing this + exp_from_1 = exp + bc + if abs(exp_from_1) > 3500: + from libelefun import mpf_ln2, mpf_ln10 + # Set b = int(exp * log(2)/log(10)) + # If exp is huge, we must use high-precision arithmetic to + # find the nearest power of ten + expprec = bitcount(abs(exp)) + 5 + tmp = from_int(exp) + tmp = mpf_mul(tmp, mpf_ln2(expprec)) + tmp = mpf_div(tmp, mpf_ln10(expprec), expprec) + b = to_int(tmp) + s = mpf_div(s, mpf_pow_int(ften, b, bitprec), bitprec) + _sign, man, exp, bc = s + exponent = b + else: + exponent = 0 + + # First, calculate mantissa digits by converting to a binary + # fixed-point number and then converting that number to + # a decimal fixed-point number. + fixprec = max(bitprec - exp - bc, 0) + fixdps = int(fixprec / math.log(10,2) + 0.5) + sf = to_fixed(s, fixprec) + sd = bin_to_radix(sf, fixprec, 10, fixdps) + digits = numeral(sd, base=10, size=dps) + + exponent += len(digits) - fixdps - 1 + return sign, digits, exponent + +def to_str(s, dps, strip_zeros=True, min_fixed=None, max_fixed=None, + show_zero_exponent=False): + """ + Convert a raw mpf to a decimal floating-point literal with at + most `dps` decimal digits in the mantissa (not counting extra zeros + that may be inserted for visual purposes). + + The number will be printed in fixed-point format if the position + of the leading digit is strictly between min_fixed + (default = min(-dps/3,-5)) and max_fixed (default = dps). + + To force fixed-point format always, set min_fixed = -inf, + max_fixed = +inf. To force floating-point format, set + min_fixed >= max_fixed. + + The literal is formatted so that it can be parsed back to a number + by to_str, float() or Decimal(). + """ + + # Special numbers + if not s[1]: + if s == fzero: + if dps: t = '0.0' + else: t = '.0' + if show_zero_exponent: + t += 'e+0' + return t + if s == finf: return '+inf' + if s == fninf: return '-inf' + if s == fnan: return 'nan' + raise ValueError + + if min_fixed is None: min_fixed = min(-(dps//3), -5) + if max_fixed is None: max_fixed = dps + + # to_digits_exp rounds to floor. + # This sometimes kills some instances of "...00001" + sign, digits, exponent = to_digits_exp(s, dps+3) + + # No digits: show only .0; round exponent to nearest + if not dps: + if digits[0] in '56789': + exponent += 1 + digits = ".0" + + else: + # Rounding up kills some instances of "...99999" + if len(digits) > dps and digits[dps] in '56789' and \ + (dps < 500 or digits[dps-4:dps] == '9999'): + digits2 = str(int(digits[:dps]) + 1) + if len(digits2) > dps: + digits2 = digits2[:dps] + exponent += 1 + digits = digits2 + else: + digits = digits[:dps] + + # Prettify numbers close to unit magnitude + if min_fixed < exponent < max_fixed: + if exponent < 0: + digits = ("0"*int(-exponent)) + digits + split = 1 + else: + split = exponent + 1 + if split > dps: + digits += "0"*(split-dps) + exponent = 0 + else: + split = 1 + + digits = (digits[:split] + "." + digits[split:]) + + if strip_zeros: + # Clean up trailing zeros + digits = digits.rstrip('0') + if digits[-1] == ".": + digits += "0" + + if exponent == 0 and dps and not show_zero_exponent: return sign + digits + if exponent >= 0: return sign + digits + "e+" + str(exponent) + if exponent < 0: return sign + digits + "e" + str(exponent) + +def str_to_man_exp(x, base=10): + """Helper function for from_str.""" + # Verify that the input is a valid float literal + float(x) + # Split into mantissa, exponent + x = x.lower() + parts = x.split('e') + if len(parts) == 1: + exp = 0 + else: # == 2 + x = parts[0] + exp = int(parts[1]) + # Look for radix point in mantissa + parts = x.split('.') + if len(parts) == 2: + a, b = parts[0], parts[1].rstrip('0') + exp -= len(b) + x = a + b + x = MPZ(int(x, base)) + return x, exp + +special_str = {'inf':finf, '+inf':finf, '-inf':fninf, 'nan':fnan} + +def from_str(x, prec, rnd=round_fast): + """Create a raw mpf from a decimal literal, rounding in the + specified direction if the input number cannot be represented + exactly as a binary floating-point number with the given number of + bits. The literal syntax accepted is the same as for Python + floats. + + TODO: the rounding does not work properly for large exponents. + """ + x = x.strip() + if x in special_str: + return special_str[x] + + if '/' in x: + p, q = x.split('/') + return from_rational(int(p), int(q), prec, rnd) + + man, exp = str_to_man_exp(x, base=10) + + # XXX: appropriate cutoffs & track direction + # note no factors of 5 + if abs(exp) > 400: + s = from_int(man, prec+10) + s = mpf_mul(s, mpf_pow_int(ften, exp, prec+10), prec, rnd) + else: + if exp >= 0: + s = from_int(man * 10**exp, prec, rnd) + else: + s = from_rational(man, 10**-exp, prec, rnd) + return s + +# Binary string conversion. These are currently mainly used for debugging +# and could use some improvement in the future + +def from_bstr(x): + man, exp = str_to_man_exp(x, base=2) + man = MPZ(man) + sign = 0 + if man < 0: + man = -man + sign = 1 + bc = bitcount(man) + return normalize(sign, man, exp, bc, bc, round_floor) + +def to_bstr(x): + sign, man, exp, bc = x + return ['','-'][sign] + numeral(man, size=bitcount(man), base=2) + ("e%i" % exp) + + +#----------------------------------------------------------------------------# +# Square roots # +#----------------------------------------------------------------------------# + + +def mpf_sqrt(s, prec, rnd=round_fast): + """ + Compute the square root of a nonnegative mpf value. The + result is correctly rounded. + """ + sign, man, exp, bc = s + if sign: + raise ComplexResult("square root of a negative number") + if not man: + return s + if exp & 1: + exp -= 1 + man <<= 1 + bc += 1 + elif man == 1: + return normalize1(sign, man, exp//2, bc, prec, rnd) + shift = max(4, 2*prec-bc+4) + shift += shift & 1 + if rnd in 'fd': + man = isqrt(man<= 0: + a = mpf_pos(sa, prec, round_floor) + b = mpf_pos(sb, prec, round_ceiling) + # Upper point nonnegative? + elif sbs >= 0: + a = fzero + negsa = mpf_neg(sa) + if mpf_lt(negsa, sb): + b = mpf_pos(sb, prec, round_ceiling) + else: + b = mpf_pos(negsa, prec, round_ceiling) + # Both negative? + else: + a = mpf_neg(sb, prec, round_floor) + b = mpf_neg(sa, prec, round_ceiling) + return a, b + +def mpi_mul(s, t, prec): + sa, sb = s + ta, tb = t + sas = mpf_sign(sa) + sbs = mpf_sign(sb) + tas = mpf_sign(ta) + tbs = mpf_sign(tb) + if sas == sbs == 0: + # Should maybe be undefined + if ta == fninf or tb == finf: + return fninf, finf + return fzero, fzero + if tas == tbs == 0: + # Should maybe be undefined + if sa == fninf or sb == finf: + return fninf, finf + return fzero, fzero + if sas >= 0: + # positive * positive + if tas >= 0: + a = mpf_mul(sa, ta, prec, round_floor) + b = mpf_mul(sb, tb, prec, round_ceiling) + if a == fnan: a = fzero + if b == fnan: b = finf + # positive * negative + elif tbs <= 0: + a = mpf_mul(sb, ta, prec, round_floor) + b = mpf_mul(sa, tb, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = fzero + # positive * both signs + else: + a = mpf_mul(sb, ta, prec, round_floor) + b = mpf_mul(sb, tb, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = finf + elif sbs <= 0: + # negative * positive + if tas >= 0: + a = mpf_mul(sa, tb, prec, round_floor) + b = mpf_mul(sb, ta, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = fzero + # negative * negative + elif tbs <= 0: + a = mpf_mul(sb, tb, prec, round_floor) + b = mpf_mul(sa, ta, prec, round_ceiling) + if a == fnan: a = fzero + if b == fnan: b = finf + # negative * both signs + else: + a = mpf_mul(sa, tb, prec, round_floor) + b = mpf_mul(sa, ta, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = finf + else: + # General case: perform all cross-multiplications and compare + # Since the multiplications can be done exactly, we need only + # do 4 (instead of 8: two for each rounding mode) + cases = [mpf_mul(sa, ta), mpf_mul(sa, tb), mpf_mul(sb, ta), mpf_mul(sb, tb)] + if fnan in cases: + a, b = (fninf, finf) + else: + cases = sorted(cases, cmp=mpf_cmp) + a = mpf_pos(cases[0], prec, round_floor) + b = mpf_pos(cases[-1], prec, round_ceiling) + return a, b + +def mpi_div(s, t, prec): + sa, sb = s + ta, tb = t + sas = mpf_sign(sa) + sbs = mpf_sign(sb) + tas = mpf_sign(ta) + tbs = mpf_sign(tb) + # 0 / X + if sas == sbs == 0: + # 0 / + if (tas < 0 and tbs > 0) or (tas == 0 or tbs == 0): + return fninf, finf + return fzero, fzero + # Denominator contains both negative and positive numbers; + # this should properly be a multi-interval, but the closest + # match is the entire (extended) real line + if tas < 0 and tbs > 0: + return fninf, finf + # Assume denominator to be nonnegative + if tas < 0: + return mpi_div(mpi_neg(s), mpi_neg(t), prec) + # Division by zero + # XXX: make sure all results make sense + if tas == 0: + # Numerator contains both signs? + if sas < 0 and sbs > 0: + return fninf, finf + if tas == tbs: + return fninf, finf + # Numerator positive? + if sas >= 0: + a = mpf_div(sa, tb, prec, round_floor) + b = finf + if sbs <= 0: + a = fninf + b = mpf_div(sb, tb, prec, round_ceiling) + # Division with positive denominator + # We still have to handle nans resulting from inf/0 or inf/inf + else: + # Nonnegative numerator + if sas >= 0: + a = mpf_div(sa, tb, prec, round_floor) + b = mpf_div(sb, ta, prec, round_ceiling) + if a == fnan: a = fzero + if b == fnan: b = finf + # Nonpositive numerator + elif sbs <= 0: + a = mpf_div(sa, ta, prec, round_floor) + b = mpf_div(sb, tb, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = fzero + # Numerator contains both signs? + else: + a = mpf_div(sa, ta, prec, round_floor) + b = mpf_div(sb, ta, prec, round_ceiling) + if a == fnan: a = fninf + if b == fnan: b = finf + return a, b + +def mpi_exp(s, prec): + sa, sb = s + # exp is monotonous + a = mpf_exp(sa, prec, round_floor) + b = mpf_exp(sb, prec, round_ceiling) + return a, b + +def mpi_log(s, prec): + sa, sb = s + # log is monotonous + a = mpf_log(sa, prec, round_floor) + b = mpf_log(sb, prec, round_ceiling) + return a, b + +def mpi_sqrt(s, prec): + sa, sb = s + # sqrt is monotonous + a = mpf_sqrt(sa, prec, round_floor) + b = mpf_sqrt(sb, prec, round_ceiling) + return a, b + +def mpi_pow_int(s, n, prec): + sa, sb = s + if n < 0: + return mpi_div((fone, fone), mpi_pow_int(s, -n, prec+20), prec) + if n == 0: + return (fone, fone) + if n == 1: + return s + # Odd -- signs are preserved + if n & 1: + a = mpf_pow_int(sa, n, prec, round_floor) + b = mpf_pow_int(sb, n, prec, round_ceiling) + # Even -- important to ensure positivity + else: + sas = mpf_sign(sa) + sbs = mpf_sign(sb) + # Nonnegative? + if sas >= 0: + a = mpf_pow_int(sa, n, prec, round_floor) + b = mpf_pow_int(sb, n, prec, round_ceiling) + # Nonpositive? + elif sbs <= 0: + a = mpf_pow_int(sb, n, prec, round_floor) + b = mpf_pow_int(sa, n, prec, round_ceiling) + # Mixed signs? + else: + a = fzero + # max(-a,b)**n + sa = mpf_neg(sa) + if mpf_ge(sa, sb): + b = mpf_pow_int(sa, n, prec, round_ceiling) + else: + b = mpf_pow_int(sb, n, prec, round_ceiling) + return a, b + +def mpi_pow(s, t, prec): + ta, tb = t + if ta == tb and ta not in (finf, fninf): + if ta == from_int(to_int(ta)): + return mpi_pow_int(s, to_int(ta), prec) + if ta == fhalf: + return mpi_sqrt(s, prec) + u = mpi_log(s, prec + 20) + v = mpi_mul(u, t, prec + 20) + return mpi_exp(v, prec) + +def MIN(x, y): + if mpf_le(x, y): + return x + return y + +def MAX(x, y): + if mpf_ge(x, y): + return x + return y + +def mpi_cos_sin(x, prec): + a, b = x + # Guaranteed to contain both -1 and 1 + if finf in (a, b) or fninf in (a, b): + return (fnone, fone), (fnone, fone) + y, yswaps, yn = reduce_angle(a, prec+20) + z, zswaps, zn = reduce_angle(b, prec+20) + # Guaranteed to contain both -1 and 1 + if zn - yn >= 4: + return (fnone, fone), (fnone, fone) + # Both points in the same quadrant -- cos and sin both strictly monotonous + if yn == zn: + m = yn % 4 + if m == 0: + cb, sa = calc_cos_sin(0, y, yswaps, prec, round_ceiling, round_floor) + ca, sb = calc_cos_sin(0, z, zswaps, prec, round_floor, round_ceiling) + if m == 1: + cb, sb = calc_cos_sin(0, y, yswaps, prec, round_ceiling, round_ceiling) + ca, sa = calc_cos_sin(0, z, zswaps, prec, round_floor, round_ceiling) + if m == 2: + ca, sb = calc_cos_sin(0, y, yswaps, prec, round_floor, round_ceiling) + cb, sa = calc_cos_sin(0, z, zswaps, prec, round_ceiling, round_floor) + if m == 3: + ca, sa = calc_cos_sin(0, y, yswaps, prec, round_floor, round_floor) + cb, sb = calc_cos_sin(0, z, zswaps, prec, round_ceiling, round_ceiling) + return (ca, cb), (sa, sb) + # Intervals spanning multiple quadrants + yn %= 4 + zn %= 4 + case = (yn, zn) + if case == (0, 1): + cb, sy = calc_cos_sin(0, y, yswaps, prec, round_ceiling, round_floor) + ca, sz = calc_cos_sin(0, z, zswaps, prec, round_floor, round_floor) + return (ca, cb), (MIN(sy, sz), fone) + if case == (3, 0): + cy, sa = calc_cos_sin(0, y, yswaps, prec, round_floor, round_floor) + cz, sb = calc_cos_sin(0, z, zswaps, prec, round_floor, round_ceiling) + return (MIN(cy, cz), fone), (sa, sb) + + + raise NotImplementedError("cos/sin spanning multiple quadrants") + +def mpi_cos(x, prec): + return mpi_cos_sin(x, prec)[0] + +def mpi_sin(x, prec): + return mpi_cos_sin(x, prec)[1] + +def mpi_tan(x, prec): + cos, sin = mpi_cos_sin(x, prec+20) + return mpi_div(sin, cos, prec) + +def mpi_cot(x, prec): + cos, sin = mpi_cos_sin(x, prec+20) + return mpi_div(cos, sin, prec) diff --git a/compiler/gdsMill/mpmath/math2.py b/compiler/gdsMill/mpmath/math2.py new file mode 100644 index 00000000..abe46242 --- /dev/null +++ b/compiler/gdsMill/mpmath/math2.py @@ -0,0 +1,645 @@ +""" +This module complements the math and cmath builtin modules by providing +fast machine precision versions of some additional functions (gamma, ...) +and wrapping math/cmath functions so that they can be called with either +real or complex arguments. +""" + +import operator +import math +import cmath + +# Irrational (?) constants +pi = 3.1415926535897932385 +e = 2.7182818284590452354 +sqrt2 = 1.4142135623730950488 +sqrt5 = 2.2360679774997896964 +phi = 1.6180339887498948482 +ln2 = 0.69314718055994530942 +ln10 = 2.302585092994045684 +euler = 0.57721566490153286061 +catalan = 0.91596559417721901505 +khinchin = 2.6854520010653064453 +apery = 1.2020569031595942854 + +logpi = 1.1447298858494001741 + +def _mathfun_real(f_real, f_complex): + def f(x, **kwargs): + if type(x) is float: + return f_real(x) + if type(x) is complex: + return f_complex(x) + try: + x = float(x) + return f_real(x) + except (TypeError, ValueError): + x = complex(x) + return f_complex(x) + f.__name__ = f_real.__name__ + return f + +def _mathfun(f_real, f_complex): + def f(x, **kwargs): + if type(x) is complex: + return f_complex(x) + try: + return f_real(float(x)) + except (TypeError, ValueError): + return f_complex(complex(x)) + f.__name__ = f_real.__name__ + return f + +def _mathfun_n(f_real, f_complex): + def f(*args, **kwargs): + try: + return f_real(*(float(x) for x in args)) + except (TypeError, ValueError): + return f_complex(*(complex(x) for x in args)) + f.__name__ = f_real.__name__ + return f + +pow = _mathfun_n(operator.pow, lambda x, y: complex(x)**y) +log = _mathfun_n(math.log, cmath.log) +sqrt = _mathfun(math.sqrt, cmath.sqrt) +exp = _mathfun_real(math.exp, cmath.exp) + +cos = _mathfun_real(math.cos, cmath.cos) +sin = _mathfun_real(math.sin, cmath.sin) +tan = _mathfun_real(math.tan, cmath.tan) + +acos = _mathfun(math.acos, cmath.acos) +asin = _mathfun(math.asin, cmath.asin) +atan = _mathfun_real(math.atan, cmath.atan) + +cosh = _mathfun_real(math.cosh, cmath.cosh) +sinh = _mathfun_real(math.sinh, cmath.sinh) +tanh = _mathfun_real(math.tanh, cmath.tanh) + +floor = _mathfun_real(math.floor, + lambda z: complex(math.floor(z.real), math.floor(z.imag))) +ceil = _mathfun_real(math.ceil, + lambda z: complex(math.ceil(z.real), math.ceil(z.imag))) + + +cos_sin = _mathfun_real(lambda x: (math.cos(x), math.sin(x)), + lambda z: (cmath.cos(z), cmath.sin(z))) + +cbrt = _mathfun(lambda x: x**(1./3), lambda z: z**(1./3)) + +def nthroot(x, n): + r = 1./n + try: + return float(x) ** r + except (ValueError, TypeError): + return complex(x) ** r + +def _sinpi_real(x): + if x < 0: + return -_sinpi_real(-x) + n, r = divmod(x, 0.5) + r *= pi + n %= 4 + if n == 0: return math.sin(r) + if n == 1: return math.cos(r) + if n == 2: return -math.sin(r) + if n == 3: return -math.cos(r) + +def _cospi_real(x): + if x < 0: + x = -x + n, r = divmod(x, 0.5) + r *= pi + n %= 4 + if n == 0: return math.cos(r) + if n == 1: return -math.sin(r) + if n == 2: return -math.cos(r) + if n == 3: return math.sin(r) + +def _sinpi_complex(z): + if z.real < 0: + return -_sinpi_complex(-z) + n, r = divmod(z.real, 0.5) + z = pi*complex(r, z.imag) + n %= 4 + if n == 0: return cmath.sin(z) + if n == 1: return cmath.cos(z) + if n == 2: return -cmath.sin(z) + if n == 3: return -cmath.cos(z) + +def _cospi_complex(z): + if z.real < 0: + z = -z + n, r = divmod(z.real, 0.5) + z = pi*complex(r, z.imag) + n %= 4 + if n == 0: return cmath.cos(z) + if n == 1: return -cmath.sin(z) + if n == 2: return -cmath.cos(z) + if n == 3: return cmath.sin(z) + +cospi = _mathfun_real(_cospi_real, _cospi_complex) +sinpi = _mathfun_real(_sinpi_real, _sinpi_complex) + +def tanpi(x): + try: + return sinpi(x) / cospi(x) + except OverflowError: + if complex(x).imag > 10: + return 1j + if complex(x).imag < 10: + return -1j + raise + +def cotpi(x): + try: + return cospi(x) / sinpi(x) + except OverflowError: + if complex(x).imag > 10: + return -1j + if complex(x).imag < 10: + return 1j + raise + +INF = 1e300*1e300 +NINF = -INF +NAN = INF-INF +EPS = 2.2204460492503131e-16 + +_exact_gamma = (INF, 1.0, 1.0, 2.0, 6.0, 24.0, 120.0, 720.0, 5040.0, 40320.0, + 362880.0, 3628800.0, 39916800.0, 479001600.0, 6227020800.0, 87178291200.0, + 1307674368000.0, 20922789888000.0, 355687428096000.0, 6402373705728000.0, + 121645100408832000.0, 2432902008176640000.0) + +_max_exact_gamma = len(_exact_gamma)-1 + +# Lanczos coefficients used by the GNU Scientific Library +_lanczos_g = 7 +_lanczos_p = (0.99999999999980993, 676.5203681218851, -1259.1392167224028, + 771.32342877765313, -176.61502916214059, 12.507343278686905, + -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7) + +def _gamma_real(x): + _intx = int(x) + if _intx == x: + if _intx <= 0: + #return (-1)**_intx * INF + raise ZeroDivisionError("gamma function pole") + if _intx <= _max_exact_gamma: + return _exact_gamma[_intx] + if x < 0.5: + # TODO: sinpi + return pi / (_sinpi_real(x)*_gamma_real(1-x)) + else: + x -= 1.0 + r = _lanczos_p[0] + for i in range(1, _lanczos_g+2): + r += _lanczos_p[i]/(x+i) + t = x + _lanczos_g + 0.5 + return 2.506628274631000502417 * t**(x+0.5) * math.exp(-t) * r + +def _gamma_complex(x): + if not x.imag: + return complex(_gamma_real(x.real)) + if x.real < 0.5: + # TODO: sinpi + return pi / (_sinpi_complex(x)*_gamma_complex(1-x)) + else: + x -= 1.0 + r = _lanczos_p[0] + for i in range(1, _lanczos_g+2): + r += _lanczos_p[i]/(x+i) + t = x + _lanczos_g + 0.5 + return 2.506628274631000502417 * t**(x+0.5) * cmath.exp(-t) * r + +gamma = _mathfun_real(_gamma_real, _gamma_complex) + +def factorial(x): + return gamma(x+1.0) + +def arg(x): + if type(x) is float: + return math.atan2(0.0,x) + return math.atan2(x.imag,x.real) + +# XXX: broken for negatives +def loggamma(x): + if type(x) not in (float, complex): + try: + x = float(x) + except (ValueError, TypeError): + x = complex(x) + try: + xreal = x.real + ximag = x.imag + except AttributeError: # py2.5 + xreal = x + ximag = 0.0 + # Reflection formula + # http://functions.wolfram.com/GammaBetaErf/LogGamma/16/01/01/0003/ + if xreal < 0.0: + if abs(x) < 0.5: + v = log(gamma(x)) + if ximag == 0: + v = v.conjugate() + return v + z = 1-x + try: + re = z.real + im = z.imag + except AttributeError: # py2.5 + re = z + im = 0.0 + refloor = floor(re) + imsign = cmp(im, 0) + return (-pi*1j)*abs(refloor)*(1-abs(imsign)) + logpi - \ + log(sinpi(z-refloor)) - loggamma(z) + 1j*pi*refloor*imsign + if x == 1.0 or x == 2.0: + return x*0 + p = 0. + while abs(x) < 11: + p -= log(x) + x += 1.0 + s = 0.918938533204672742 + (x-0.5)*log(x) - x + r = 1./x + r2 = r*r + s += 0.083333333333333333333*r; r *= r2 + s += -0.0027777777777777777778*r; r *= r2 + s += 0.00079365079365079365079*r; r *= r2 + s += -0.0005952380952380952381*r; r *= r2 + s += 0.00084175084175084175084*r; r *= r2 + s += -0.0019175269175269175269*r; r *= r2 + s += 0.0064102564102564102564*r; r *= r2 + s += -0.02955065359477124183*r + return s + p + +_psi_coeff = [ +0.083333333333333333333, +-0.0083333333333333333333, +0.003968253968253968254, +-0.0041666666666666666667, +0.0075757575757575757576, +-0.021092796092796092796, +0.083333333333333333333, +-0.44325980392156862745, +3.0539543302701197438, +-26.456212121212121212] + +def _digamma_real(x): + _intx = int(x) + if _intx == x: + if _intx <= 0: + raise ZeroDivisionError("polygamma pole") + if x < 0.5: + x = 1.0-x + s = pi*cotpi(x) + else: + s = 0.0 + while x < 10.0: + s -= 1.0/x + x += 1.0 + x2 = x**-2 + t = x2 + for c in _psi_coeff: + s -= c*t + if t < 1e-20: + break + t *= x2 + return s + math.log(x) - 0.5/x + +def _digamma_complex(x): + if not x.imag: + return complex(_digamma_real(x.real)) + if x.real < 0.5: + x = 1.0-x + s = pi*cotpi(x) + else: + s = 0.0 + while abs(x) < 10.0: + s -= 1.0/x + x += 1.0 + x2 = x**-2 + t = x2 + for c in _psi_coeff: + s -= c*t + if abs(t) < 1e-20: + break + t *= x2 + return s + cmath.log(x) - 0.5/x + +digamma = _mathfun_real(_digamma_real, _digamma_complex) + +# TODO: could implement complex erf and erfc here. Need +# to find an accurate method (avoiding cancellation) +# for approx. 1 < abs(x) < 9. + +_erfc_coeff_P = [ + 1.0000000161203922312, + 2.1275306946297962644, + 2.2280433377390253297, + 1.4695509105618423961, + 0.66275911699770787537, + 0.20924776504163751585, + 0.045459713768411264339, + 0.0063065951710717791934, + 0.00044560259661560421715][::-1] + +_erfc_coeff_Q = [ + 1.0000000000000000000, + 3.2559100272784894318, + 4.9019435608903239131, + 4.4971472894498014205, + 2.7845640601891186528, + 1.2146026030046904138, + 0.37647108453729465912, + 0.080970149639040548613, + 0.011178148899483545902, + 0.00078981003831980423513][::-1] + +def _polyval(coeffs, x): + p = coeffs[0] + for c in coeffs[1:]: + p = c + x*p + return p + +def _erf_taylor(x): + # Taylor series assuming 0 <= x <= 1 + x2 = x*x + s = t = x + n = 1 + while abs(t) > 1e-17: + t *= x2/n + s -= t/(n+n+1) + n += 1 + t *= x2/n + s += t/(n+n+1) + n += 1 + return 1.1283791670955125739*s + +def _erfc_mid(x): + # Rational approximation assuming 0 <= x <= 9 + return exp(-x*x)*_polyval(_erfc_coeff_P,x)/_polyval(_erfc_coeff_Q,x) + +def _erfc_asymp(x): + # Asymptotic expansion assuming x >= 9 + x2 = x*x + v = exp(-x2)/x*0.56418958354775628695 + r = t = 0.5 / x2 + s = 1.0 + for n in range(1,22,4): + s -= t + t *= r * (n+2) + s += t + t *= r * (n+4) + if abs(t) < 1e-17: + break + return s * v + +def erf(x): + """ + erf of a real number. + """ + x = float(x) + if x != x: + return x + if x < 0.0: + return -erf(-x) + if x >= 1.0: + if x >= 6.0: + return 1.0 + return 1.0 - _erfc_mid(x) + return _erf_taylor(x) + +def erfc(x): + """ + erfc of a real number. + """ + x = float(x) + if x != x: + return x + if x < 0.0: + if x < -6.0: + return 2.0 + return 2.0-erfc(-x) + if x > 9.0: + return _erfc_asymp(x) + if x >= 1.0: + return _erfc_mid(x) + return 1.0 - _erf_taylor(x) + +gauss42 = [\ +(0.99839961899006235, 0.0041059986046490839), +(-0.99839961899006235, 0.0041059986046490839), +(0.9915772883408609, 0.009536220301748501), +(-0.9915772883408609,0.009536220301748501), +(0.97934250806374812, 0.014922443697357493), +(-0.97934250806374812, 0.014922443697357493), +(0.96175936533820439,0.020227869569052644), +(-0.96175936533820439, 0.020227869569052644), +(0.93892355735498811, 0.025422959526113047), +(-0.93892355735498811,0.025422959526113047), +(0.91095972490412735, 0.030479240699603467), +(-0.91095972490412735, 0.030479240699603467), +(0.87802056981217269,0.03536907109759211), +(-0.87802056981217269, 0.03536907109759211), +(0.8402859832618168, 0.040065735180692258), +(-0.8402859832618168,0.040065735180692258), +(0.7979620532554873, 0.044543577771965874), +(-0.7979620532554873, 0.044543577771965874), +(0.75127993568948048,0.048778140792803244), +(-0.75127993568948048, 0.048778140792803244), +(0.70049459055617114, 0.052746295699174064), +(-0.70049459055617114,0.052746295699174064), +(0.64588338886924779, 0.056426369358018376), +(-0.64588338886924779, 0.056426369358018376), +(0.58774459748510932, 0.059798262227586649), +(-0.58774459748510932, 0.059798262227586649), +(0.5263957499311922, 0.062843558045002565), +(-0.5263957499311922, 0.062843558045002565), +(0.46217191207042191, 0.065545624364908975), +(-0.46217191207042191, 0.065545624364908975), +(0.39542385204297503, 0.067889703376521934), +(-0.39542385204297503, 0.067889703376521934), +(0.32651612446541151, 0.069862992492594159), +(-0.32651612446541151, 0.069862992492594159), +(0.25582507934287907, 0.071454714265170971), +(-0.25582507934287907, 0.071454714265170971), +(0.18373680656485453, 0.072656175243804091), +(-0.18373680656485453, 0.072656175243804091), +(0.11064502720851986, 0.073460813453467527), +(-0.11064502720851986, 0.073460813453467527), +(0.036948943165351772, 0.073864234232172879), +(-0.036948943165351772, 0.073864234232172879)] + +EI_ASYMP_CONVERGENCE_RADIUS = 40.0 + +def ei_asymp(z, _e1=False): + r = 1./z + s = t = 1.0 + k = 1 + while 1: + t *= k*r + s += t + if abs(t) < 1e-16: + break + k += 1 + v = s*exp(z)/z + if _e1: + if type(z) is complex: + zreal = z.real + zimag = z.imag + else: + zreal = z + zimag = 0.0 + if zimag == 0.0 and zreal > 0.0: + v += pi*1j + else: + if type(z) is complex: + if z.imag > 0: + v += pi*1j + if z.imag < 0: + v -= pi*1j + return v + +def ei_taylor(z, _e1=False): + s = t = z + k = 2 + while 1: + t = t*z/k + term = t/k + if abs(term) < 1e-17: + break + s += term + k += 1 + s += euler + if _e1: + s += log(-z) + else: + if type(z) is float or z.imag == 0.0: + s += math.log(abs(z)) + else: + s += cmath.log(z) + return s + +def ei(z, _e1=False): + typez = type(z) + if typez not in (float, complex): + try: + z = float(z) + typez = float + except (TypeError, ValueError): + z = complex(z) + typez = complex + if not z: + return -INF + absz = abs(z) + if absz > EI_ASYMP_CONVERGENCE_RADIUS: + return ei_asymp(z, _e1) + elif absz <= 2.0 or (typez is float and z > 0.0): + return ei_taylor(z, _e1) + # Integrate, starting from whichever is smaller of a Taylor + # series value or an asymptotic series value + if typez is complex and z.real > 0.0: + zref = z / absz + ref = ei_taylor(zref, _e1) + else: + zref = EI_ASYMP_CONVERGENCE_RADIUS * z / absz + ref = ei_asymp(zref, _e1) + C = (zref-z)*0.5 + D = (zref+z)*0.5 + s = 0.0 + if type(z) is complex: + _exp = cmath.exp + else: + _exp = math.exp + for x,w in gauss42: + t = C*x+D + s += w*_exp(t)/t + ref -= C*s + return ref + +def e1(z): + # hack to get consistent signs if the imaginary part if 0 + # and signed + typez = type(z) + if type(z) not in (float, complex): + try: + z = float(z) + typez = float + except (TypeError, ValueError): + z = complex(z) + typez = complex + if typez is complex and not z.imag: + z = complex(z.real, 0.0) + # end hack + return -ei(-z, _e1=True) + +_zeta_int = [\ +-0.5, +0.0, +1.6449340668482264365,1.2020569031595942854,1.0823232337111381915, +1.0369277551433699263,1.0173430619844491397,1.0083492773819228268, +1.0040773561979443394,1.0020083928260822144,1.0009945751278180853, +1.0004941886041194646,1.0002460865533080483,1.0001227133475784891, +1.0000612481350587048,1.0000305882363070205,1.0000152822594086519, +1.0000076371976378998,1.0000038172932649998,1.0000019082127165539, +1.0000009539620338728,1.0000004769329867878,1.0000002384505027277, +1.0000001192199259653,1.0000000596081890513,1.0000000298035035147, +1.0000000149015548284] + +_zeta_P = [-3.50000000087575873, -0.701274355654678147, +-0.0672313458590012612, -0.00398731457954257841, +-0.000160948723019303141, -4.67633010038383371e-6, +-1.02078104417700585e-7, -1.68030037095896287e-9, +-1.85231868742346722e-11][::-1] + +_zeta_Q = [1.00000000000000000, -0.936552848762465319, +-0.0588835413263763741, -0.00441498861482948666, +-0.000143416758067432622, -5.10691659585090782e-6, +-9.58813053268913799e-8, -1.72963791443181972e-9, +-1.83527919681474132e-11][::-1] + +_zeta_1 = [3.03768838606128127e-10, -1.21924525236601262e-8, +2.01201845887608893e-7, -1.53917240683468381e-6, +-5.09890411005967954e-7, 0.000122464707271619326, +-0.000905721539353130232, -0.00239315326074843037, +0.084239750013159168, 0.418938517907442414, 0.500000001921884009] + +_zeta_0 = [-3.46092485016748794e-10, -6.42610089468292485e-9, +1.76409071536679773e-7, -1.47141263991560698e-6, -6.38880222546167613e-7, +0.000122641099800668209, -0.000905894913516772796, -0.00239303348507992713, +0.0842396947501199816, 0.418938533204660256, 0.500000000000000052] + +def zeta(s): + """ + Riemann zeta function, real argument + """ + if not isinstance(s, (float, int)): + try: + s = float(s) + except (ValueError, TypeError): + try: + s = complex(s) + if not s.imag: + return complex(zeta(s.real)) + except (ValueError, TypeError): + pass + raise NotImplementedError + if s == 1: + raise ValueError("zeta(1) pole") + if s >= 27: + return 1.0 + 2.0**(-s) + 3.0**(-s) + n = int(s) + if n == s: + if n >= 0: + return _zeta_int[n] + if not (n % 2): + return 0.0 + if s <= 0.0: + return 2.**s*pi**(s-1)*_sinpi_real(0.5*s)*_gamma_real(1-s)*zeta(1-s) + if s <= 2.0: + if s <= 1.0: + return _polyval(_zeta_0,s)/(s-1) + return _polyval(_zeta_1,s)/(s-1) + z = _polyval(_zeta_P,s) / _polyval(_zeta_Q,s) + return 1.0 + 2.0**(-s) + 3.0**(-s) + 4.0**(-s)*z diff --git a/compiler/gdsMill/mpmath/matrices/__init__.py b/compiler/gdsMill/mpmath/matrices/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/compiler/gdsMill/mpmath/matrices/calculus.py b/compiler/gdsMill/mpmath/matrices/calculus.py new file mode 100644 index 00000000..0c7acdb9 --- /dev/null +++ b/compiler/gdsMill/mpmath/matrices/calculus.py @@ -0,0 +1,522 @@ +# TODO: should use diagonalization-based algorithms + +class MatrixCalculusMethods: + + def _exp_pade(ctx, a): + """ + Exponential of a matrix using Pade approximants. + + See G. H. Golub, C. F. van Loan 'Matrix Computations', + third Ed., page 572 + + TODO: + - find a good estimate for q + - reduce the number of matrix multiplications to improve + performance + """ + def eps_pade(p): + return ctx.mpf(2)**(3-2*p) * \ + ctx.factorial(p)**2/(ctx.factorial(2*p)**2 * (2*p + 1)) + q = 4 + extraq = 8 + while 1: + if eps_pade(q) < ctx.eps: + break + q += 1 + q += extraq + j = int(max(1, ctx.mag(ctx.mnorm(a,'inf')))) + extra = q + prec = ctx.prec + ctx.dps += extra + 3 + try: + a = a/2**j + na = a.rows + den = ctx.eye(na) + num = ctx.eye(na) + x = ctx.eye(na) + c = ctx.mpf(1) + for k in range(1, q+1): + c *= ctx.mpf(q - k + 1)/((2*q - k + 1) * k) + x = a*x + cx = c*x + num += cx + den += (-1)**k * cx + f = ctx.lu_solve_mat(den, num) + for k in range(j): + f = f*f + finally: + ctx.prec = prec + return f*1 + + def expm(ctx, A, method='taylor'): + r""" + Computes the matrix exponential of a square matrix `A`, which is defined + by the power series + + .. math :: + + \exp(A) = I + A + \frac{A^2}{2!} + \frac{A^3}{3!} + \ldots + + With method='taylor', the matrix exponential is computed + using the Taylor series. With method='pade', Pade approximants + are used instead. + + **Examples** + + Basic examples:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> expm(zeros(3)) + [1.0 0.0 0.0] + [0.0 1.0 0.0] + [0.0 0.0 1.0] + >>> expm(eye(3)) + [2.71828182845905 0.0 0.0] + [ 0.0 2.71828182845905 0.0] + [ 0.0 0.0 2.71828182845905] + >>> expm([[1,1,0],[1,0,1],[0,1,0]]) + [ 3.86814500615414 2.26812870852145 0.841130841230196] + [ 2.26812870852145 2.44114713886289 1.42699786729125] + [0.841130841230196 1.42699786729125 1.6000162976327] + >>> expm([[1,1,0],[1,0,1],[0,1,0]], method='pade') + [ 3.86814500615414 2.26812870852145 0.841130841230196] + [ 2.26812870852145 2.44114713886289 1.42699786729125] + [0.841130841230196 1.42699786729125 1.6000162976327] + >>> expm([[1+j, 0], [1+j,1]]) + [(1.46869393991589 + 2.28735528717884j) 0.0] + [ (1.03776739863568 + 3.536943175722j) (2.71828182845905 + 0.0j)] + + Matrices with large entries are allowed:: + + >>> expm(matrix([[1,2],[2,3]])**25) + [5.65024064048415e+2050488462815550 9.14228140091932e+2050488462815550] + [9.14228140091932e+2050488462815550 1.47925220414035e+2050488462815551] + + The identity `\exp(A+B) = \exp(A) \exp(B)` does not hold for + noncommuting matrices:: + + >>> A = hilbert(3) + >>> B = A + eye(3) + >>> chop(mnorm(A*B - B*A)) + 0.0 + >>> chop(mnorm(expm(A+B) - expm(A)*expm(B))) + 0.0 + >>> B = A + ones(3) + >>> mnorm(A*B - B*A) + 1.8 + >>> mnorm(expm(A+B) - expm(A)*expm(B)) + 42.0927851137247 + + """ + A = ctx.matrix(A) + if method == 'pade': + return ctx._exp_pade(A) + prec = ctx.prec + j = int(max(1, ctx.mag(ctx.mnorm(A,'inf')))) + j += int(0.5*prec**0.5) + try: + ctx.prec += 10 + 2*j + tol = +ctx.eps + A = A/2**j + T = A + Y = A**0 + A + k = 2 + while 1: + T *= A * (1/ctx.mpf(k)) + if ctx.mnorm(T, 'inf') < tol: + break + Y += T + k += 1 + for k in xrange(j): + Y = Y*Y + finally: + ctx.prec = prec + Y *= 1 + return Y + + def cosm(ctx, A): + r""" + Gives the cosine of a square matrix `A`, defined in analogy + with the matrix exponential. + + Examples:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> X = eye(3) + >>> cosm(X) + [0.54030230586814 0.0 0.0] + [ 0.0 0.54030230586814 0.0] + [ 0.0 0.0 0.54030230586814] + >>> X = hilbert(3) + >>> cosm(X) + [ 0.424403834569555 -0.316643413047167 -0.221474945949293] + [-0.316643413047167 0.820646708837824 -0.127183694770039] + [-0.221474945949293 -0.127183694770039 0.909236687217541] + >>> X = matrix([[1+j,-2],[0,-j]]) + >>> cosm(X) + [(0.833730025131149 - 0.988897705762865j) (1.07485840848393 - 0.17192140544213j)] + [ 0.0 (1.54308063481524 + 0.0j)] + """ + B = 0.5 * (ctx.expm(A*ctx.j) + ctx.expm(A*(-ctx.j))) + if not sum(A.apply(ctx.im).apply(abs)): + B = B.apply(ctx.re) + return B + + def sinm(ctx, A): + r""" + Gives the sine of a square matrix `A`, defined in analogy + with the matrix exponential. + + Examples:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> X = eye(3) + >>> sinm(X) + [0.841470984807897 0.0 0.0] + [ 0.0 0.841470984807897 0.0] + [ 0.0 0.0 0.841470984807897] + >>> X = hilbert(3) + >>> sinm(X) + [0.711608512150994 0.339783913247439 0.220742837314741] + [0.339783913247439 0.244113865695532 0.187231271174372] + [0.220742837314741 0.187231271174372 0.155816730769635] + >>> X = matrix([[1+j,-2],[0,-j]]) + >>> sinm(X) + [(1.29845758141598 + 0.634963914784736j) (-1.96751511930922 + 0.314700021761367j)] + [ 0.0 (0.0 - 1.1752011936438j)] + """ + B = (-0.5j) * (ctx.expm(A*ctx.j) - ctx.expm(A*(-ctx.j))) + if not sum(A.apply(ctx.im).apply(abs)): + B = B.apply(ctx.re) + return B + + def _sqrtm_rot(ctx, A, _may_rotate): + # If the iteration fails to converge, cheat by performing + # a rotation by a complex number + u = ctx.j**0.3 + return ctx.sqrtm(u*A, _may_rotate) / ctx.sqrt(u) + + def sqrtm(ctx, A, _may_rotate=2): + r""" + Computes a square root of the square matrix `A`, i.e. returns + a matrix `B = A^{1/2}` such that `B^2 = A`. The square root + of a matrix, if it exists, is not unique. + + **Examples** + + Square roots of some simple matrices:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> sqrtm([[1,0], [0,1]]) + [1.0 0.0] + [0.0 1.0] + >>> sqrtm([[0,0], [0,0]]) + [0.0 0.0] + [0.0 0.0] + >>> sqrtm([[2,0],[0,1]]) + [1.4142135623731 0.0] + [ 0.0 1.0] + >>> sqrtm([[1,1],[1,0]]) + [ (0.920442065259926 - 0.21728689675164j) (0.568864481005783 + 0.351577584254143j)] + [(0.568864481005783 + 0.351577584254143j) (0.351577584254143 - 0.568864481005783j)] + >>> sqrtm([[1,0],[0,1]]) + [1.0 0.0] + [0.0 1.0] + >>> sqrtm([[-1,0],[0,1]]) + [(0.0 - 1.0j) 0.0] + [ 0.0 (1.0 + 0.0j)] + >>> sqrtm([[j,0],[0,j]]) + [(0.707106781186547 + 0.707106781186547j) 0.0] + [ 0.0 (0.707106781186547 + 0.707106781186547j)] + + A square root of a rotation matrix, giving the corresponding + half-angle rotation matrix:: + + >>> t1 = 0.75 + >>> t2 = t1 * 0.5 + >>> A1 = matrix([[cos(t1), -sin(t1)], [sin(t1), cos(t1)]]) + >>> A2 = matrix([[cos(t2), -sin(t2)], [sin(t2), cos(t2)]]) + >>> sqrtm(A1) + [0.930507621912314 -0.366272529086048] + [0.366272529086048 0.930507621912314] + >>> A2 + [0.930507621912314 -0.366272529086048] + [0.366272529086048 0.930507621912314] + + The identity `(A^2)^{1/2} = A` does not necessarily hold:: + + >>> A = matrix([[4,1,4],[7,8,9],[10,2,11]]) + >>> sqrtm(A**2) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> sqrtm(A)**2 + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> A = matrix([[-4,1,4],[7,-8,9],[10,2,11]]) + >>> sqrtm(A**2) + [ 7.43715112194995 -0.324127569985474 1.8481718827526] + [-0.251549715716942 9.32699765900402 2.48221180985147] + [ 4.11609388833616 0.775751877098258 13.017955697342] + >>> chop(sqrtm(A)**2) + [-4.0 1.0 4.0] + [ 7.0 -8.0 9.0] + [10.0 2.0 11.0] + + For some matrices, a square root does not exist:: + + >>> sqrtm([[0,1], [0,0]]) + Traceback (most recent call last): + ... + ZeroDivisionError: matrix is numerically singular + + Two examples from the documentation for Matlab's ``sqrtm``:: + + >>> mp.dps = 15; mp.pretty = True + >>> sqrtm([[7,10],[15,22]]) + [1.56669890360128 1.74077655955698] + [2.61116483933547 4.17786374293675] + >>> + >>> X = matrix(\ + ... [[5,-4,1,0,0], + ... [-4,6,-4,1,0], + ... [1,-4,6,-4,1], + ... [0,1,-4,6,-4], + ... [0,0,1,-4,5]]) + >>> Y = matrix(\ + ... [[2,-1,-0,-0,-0], + ... [-1,2,-1,0,-0], + ... [0,-1,2,-1,0], + ... [-0,0,-1,2,-1], + ... [-0,-0,-0,-1,2]]) + >>> mnorm(sqrtm(X) - Y) + 4.53155328326114e-19 + + """ + A = ctx.matrix(A) + # Trivial + if A*0 == A: + return A + prec = ctx.prec + if _may_rotate: + d = ctx.det(A) + if abs(ctx.im(d)) < 16*ctx.eps and ctx.re(d) < 0: + return ctx._sqrtm_rot(A, _may_rotate-1) + try: + ctx.prec += 10 + tol = ctx.eps * 128 + Y = A + Z = I = A**0 + k = 0 + # Denman-Beavers iteration + while 1: + Yprev = Y + try: + Y, Z = 0.5*(Y+ctx.inverse(Z)), 0.5*(Z+ctx.inverse(Y)) + except ZeroDivisionError: + if _may_rotate: + Y = ctx._sqrtm_rot(A, _may_rotate-1) + break + else: + raise + mag1 = ctx.mnorm(Y-Yprev, 'inf') + mag2 = ctx.mnorm(Y, 'inf') + if mag1 <= mag2*tol: + break + if _may_rotate and k > 6 and not mag1 < mag2 * 0.001: + return ctx._sqrtm_rot(A, _may_rotate-1) + k += 1 + if k > ctx.prec: + raise ctx.NoConvergence + finally: + ctx.prec = prec + Y *= 1 + return Y + + def logm(ctx, A): + r""" + Computes a logarithm of the square matrix `A`, i.e. returns + a matrix `B = \log(A)` such that `\exp(B) = A`. The logarithm + of a matrix, if it exists, is not unique. + + **Examples** + + Logarithms of some simple matrices:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> X = eye(3) + >>> logm(X) + [0.0 0.0 0.0] + [0.0 0.0 0.0] + [0.0 0.0 0.0] + >>> logm(2*X) + [0.693147180559945 0.0 0.0] + [ 0.0 0.693147180559945 0.0] + [ 0.0 0.0 0.693147180559945] + >>> logm(expm(X)) + [1.0 0.0 0.0] + [0.0 1.0 0.0] + [0.0 0.0 1.0] + + A logarithm of a complex matrix:: + + >>> X = matrix([[2+j, 1, 3], [1-j, 1-2*j, 1], [-4, -5, j]]) + >>> B = logm(X) + >>> nprint(B) + [ (0.808757 + 0.107759j) (2.20752 + 0.202762j) (1.07376 - 0.773874j)] + [ (0.905709 - 0.107795j) (0.0287395 - 0.824993j) (0.111619 + 0.514272j)] + [(-0.930151 + 0.399512j) (-2.06266 - 0.674397j) (0.791552 + 0.519839j)] + >>> chop(expm(B)) + [(2.0 + 1.0j) 1.0 3.0] + [(1.0 - 1.0j) (1.0 - 2.0j) 1.0] + [ -4.0 -5.0 (0.0 + 1.0j)] + + A matrix `X` close to the identity matrix, for which + `\log(\exp(X)) = \exp(\log(X)) = X` holds:: + + >>> X = eye(3) + hilbert(3)/4 + >>> X + [ 1.25 0.125 0.0833333333333333] + [ 0.125 1.08333333333333 0.0625] + [0.0833333333333333 0.0625 1.05] + >>> logm(expm(X)) + [ 1.25 0.125 0.0833333333333333] + [ 0.125 1.08333333333333 0.0625] + [0.0833333333333333 0.0625 1.05] + >>> expm(logm(X)) + [ 1.25 0.125 0.0833333333333333] + [ 0.125 1.08333333333333 0.0625] + [0.0833333333333333 0.0625 1.05] + + A logarithm of a rotation matrix, giving back the angle of + the rotation:: + + >>> t = 3.7 + >>> A = matrix([[cos(t),sin(t)],[-sin(t),cos(t)]]) + >>> chop(logm(A)) + [ 0.0 -2.58318530717959] + [2.58318530717959 0.0] + >>> (2*pi-t) + 2.58318530717959 + + For some matrices, a logarithm does not exist:: + + >>> logm([[1,0], [0,0]]) + Traceback (most recent call last): + ... + ZeroDivisionError: matrix is numerically singular + + Logarithm of a matrix with large entries:: + + >>> logm(hilbert(3) * 10**20).apply(re) + [ 45.5597513593433 1.27721006042799 0.317662687717978] + [ 1.27721006042799 42.5222778973542 2.24003708791604] + [0.317662687717978 2.24003708791604 42.395212822267] + + """ + A = ctx.matrix(A) + prec = ctx.prec + try: + ctx.prec += 10 + tol = ctx.eps * 128 + I = A**0 + B = A + n = 0 + while 1: + B = ctx.sqrtm(B) + n += 1 + if ctx.mnorm(B-I, 'inf') < 0.125: + break + T = X = B-I + L = X*0 + k = 1 + while 1: + if k & 1: + L += T / k + else: + L -= T / k + T *= X + if ctx.mnorm(T, 'inf') < tol: + break + k += 1 + if k > ctx.prec: + raise ctx.NoConvergence + finally: + ctx.prec = prec + L *= 2**n + return L + + def powm(ctx, A, r): + r""" + Computes `A^r = \exp(A \log r)` for a matrix `A` and complex + number `r`. + + **Examples** + + Powers and inverse powers of a matrix:: + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = True + >>> A = matrix([[4,1,4],[7,8,9],[10,2,11]]) + >>> powm(A, 2) + [ 63.0 20.0 69.0] + [174.0 89.0 199.0] + [164.0 48.0 179.0] + >>> chop(powm(powm(A, 4), 1/4.)) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> powm(extraprec(20)(powm)(A, -4), -1/4.) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> chop(powm(powm(A, 1+0.5j), 1/(1+0.5j))) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + >>> powm(extraprec(5)(powm)(A, -1.5), -1/(1.5)) + [ 4.0 1.0 4.0] + [ 7.0 8.0 9.0] + [10.0 2.0 11.0] + + A Fibonacci-generating matrix:: + + >>> powm([[1,1],[1,0]], 10) + [89.0 55.0] + [55.0 34.0] + >>> fib(10) + 55.0 + >>> powm([[1,1],[1,0]], 6.5) + [(16.5166626964253 - 0.0121089837381789j) (10.2078589271083 + 0.0195927472575932j)] + [(10.2078589271083 + 0.0195927472575932j) (6.30880376931698 - 0.0317017309957721j)] + >>> (phi**6.5 - (1-phi)**6.5)/sqrt(5) + (10.2078589271083 - 0.0195927472575932j) + >>> powm([[1,1],[1,0]], 6.2) + [ (14.3076953002666 - 0.008222855781077j) (8.81733464837593 + 0.0133048601383712j)] + [(8.81733464837593 + 0.0133048601383712j) (5.49036065189071 - 0.0215277159194482j)] + >>> (phi**6.2 - (1-phi)**6.2)/sqrt(5) + (8.81733464837593 - 0.0133048601383712j) + + """ + A = ctx.matrix(A) + r = ctx.convert(r) + prec = ctx.prec + try: + ctx.prec += 10 + if ctx.isint(r): + v = A ** int(r) + elif ctx.isint(r*2): + y = int(r*2) + v = ctx.sqrtm(A) ** y + else: + v = ctx.expm(r*ctx.logm(A)) + finally: + ctx.prec = prec + v *= 1 + return v diff --git a/compiler/gdsMill/mpmath/matrices/linalg.py b/compiler/gdsMill/mpmath/matrices/linalg.py new file mode 100644 index 00000000..708c6254 --- /dev/null +++ b/compiler/gdsMill/mpmath/matrices/linalg.py @@ -0,0 +1,516 @@ +""" +Linear algebra +-------------- + +Linear equations +................ + +Basic linear algebra is implemented; you can for example solve the linear +equation system:: + + x + 2*y = -10 + 3*x + 4*y = 10 + +using ``lu_solve``:: + + >>> A = matrix([[1, 2], [3, 4]]) + >>> b = matrix([-10, 10]) + >>> x = lu_solve(A, b) + >>> x + matrix( + [['30.0'], + ['-20.0']]) + +If you don't trust the result, use ``residual`` to calculate the residual ||A*x-b||:: + + >>> residual(A, x, b) + matrix( + [['3.46944695195361e-18'], + ['3.46944695195361e-18']]) + >>> str(eps) + '2.22044604925031e-16' + +As you can see, the solution is quite accurate. The error is caused by the +inaccuracy of the internal floating point arithmetic. Though, it's even smaller +than the current machine epsilon, which basically means you can trust the +result. + +If you need more speed, use NumPy. Or choose a faster data type using the +keyword ``force_type``:: + + >>> lu_solve(A, b, force_type=float) + matrix( + [[29.999999999999996], + [-19.999999999999996]]) + +``lu_solve`` accepts overdetermined systems. It is usually not possible to solve +such systems, so the residual is minimized instead. Internally this is done +using Cholesky decomposition to compute a least squares approximation. This means +that that ``lu_solve`` will square the errors. If you can't afford this, use +``qr_solve`` instead. It is twice as slow but more accurate, and it calculates +the residual automatically. + + +Matrix factorization +.................... + +The function ``lu`` computes an explicit LU factorization of a matrix:: + + >>> P, L, U = lu(matrix([[0,2,3],[4,5,6],[7,8,9]])) + >>> print P + [0.0 0.0 1.0] + [1.0 0.0 0.0] + [0.0 1.0 0.0] + >>> print L + [ 1.0 0.0 0.0] + [ 0.0 1.0 0.0] + [0.571428571428571 0.214285714285714 1.0] + >>> print U + [7.0 8.0 9.0] + [0.0 2.0 3.0] + [0.0 0.0 0.214285714285714] + >>> print P.T*L*U + [0.0 2.0 3.0] + [4.0 5.0 6.0] + [7.0 8.0 9.0] + +Interval matrices +----------------- + +Matrices may contain interval elements. This allows one to perform +basic linear algebra operations such as matrix multiplication +and equation solving with rigorous error bounds:: + + >>> a = matrix([['0.1','0.3','1.0'], + ... ['7.1','5.5','4.8'], + ... ['3.2','4.4','5.6']], force_type=mpi) + >>> + >>> b = matrix(['4','0.6','0.5'], force_type=mpi) + >>> c = lu_solve(a, b) + >>> c + matrix( + [[[5.2582327113062393041, 5.2582327113062749951]], + [[-13.155049396267856583, -13.155049396267821167]], + [[7.4206915477497212555, 7.4206915477497310922]]]) + >>> print a*c + [ [3.9999999999999866773, 4.0000000000000133227]] + [[0.59999999999972430942, 0.60000000000027142733]] + [[0.49999999999982236432, 0.50000000000018474111]] +""" + +# TODO: +# *implement high-level qr() +# *test unitvector +# *iterative solving + +from copy import copy + +class LinearAlgebraMethods(object): + + def LU_decomp(ctx, A, overwrite=False, use_cache=True): + """ + LU-factorization of a n*n matrix using the Gauss algorithm. + Returns L and U in one matrix and the pivot indices. + + Use overwrite to specify whether A will be overwritten with L and U. + """ + if not A.rows == A.cols: + raise ValueError('need n*n matrix') + # get from cache if possible + if use_cache and isinstance(A, ctx.matrix) and A._LU: + return A._LU + if not overwrite: + orig = A + A = A.copy() + tol = ctx.absmin(ctx.mnorm(A,1) * ctx.eps) # each pivot element has to be bigger + n = A.rows + p = [None]*(n - 1) + for j in xrange(n - 1): + # pivoting, choose max(abs(reciprocal row sum)*abs(pivot element)) + biggest = 0 + for k in xrange(j, n): + s = ctx.fsum([ctx.absmin(A[k,l]) for l in xrange(j, n)]) + if ctx.absmin(s) <= tol: + raise ZeroDivisionError('matrix is numerically singular') + current = 1/s * ctx.absmin(A[k,j]) + if current > biggest: # TODO: what if equal? + biggest = current + p[j] = k + # swap rows according to p + ctx.swap_row(A, j, p[j]) + if ctx.absmin(A[j,j]) <= tol: + raise ZeroDivisionError('matrix is numerically singular') + # calculate elimination factors and add rows + for i in xrange(j + 1, n): + A[i,j] /= A[j,j] + for k in xrange(j + 1, n): + A[i,k] -= A[i,j]*A[j,k] + if ctx.absmin(A[n - 1,n - 1]) <= tol: + raise ZeroDivisionError('matrix is numerically singular') + # cache decomposition + if not overwrite and isinstance(orig, ctx.matrix): + orig._LU = (A, p) + return A, p + + def L_solve(ctx, L, b, p=None): + """ + Solve the lower part of a LU factorized matrix for y. + """ + assert L.rows == L.cols, 'need n*n matrix' + n = L.rows + assert len(b) == n + b = copy(b) + if p: # swap b according to p + for k in xrange(0, len(p)): + ctx.swap_row(b, k, p[k]) + # solve + for i in xrange(1, n): + for j in xrange(i): + b[i] -= L[i,j] * b[j] + return b + + def U_solve(ctx, U, y): + """ + Solve the upper part of a LU factorized matrix for x. + """ + assert U.rows == U.cols, 'need n*n matrix' + n = U.rows + assert len(y) == n + x = copy(y) + for i in xrange(n - 1, -1, -1): + for j in xrange(i + 1, n): + x[i] -= U[i,j] * x[j] + x[i] /= U[i,i] + return x + + def lu_solve(ctx, A, b, **kwargs): + """ + Ax = b => x + + Solve a determined or overdetermined linear equations system. + Fast LU decomposition is used, which is less accurate than QR decomposition + (especially for overdetermined systems), but it's twice as efficient. + Use qr_solve if you want more precision or have to solve a very ill- + conditioned system. + + If you specify real=True, it does not check for overdeterminded complex + systems. + """ + prec = ctx.prec + try: + ctx.prec += 10 + # do not overwrite A nor b + A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() + if A.rows < A.cols: + raise ValueError('cannot solve underdetermined system') + if A.rows > A.cols: + # use least-squares method if overdetermined + # (this increases errors) + AH = A.H + A = AH * A + b = AH * b + if (kwargs.get('real', False) or + not sum(type(i) is ctx.mpc for i in A)): + # TODO: necessary to check also b? + x = ctx.cholesky_solve(A, b) + else: + x = ctx.lu_solve(A, b) + else: + # LU factorization + A, p = ctx.LU_decomp(A) + b = ctx.L_solve(A, b, p) + x = ctx.U_solve(A, b) + finally: + ctx.prec = prec + return x + + def improve_solution(ctx, A, x, b, maxsteps=1): + """ + Improve a solution to a linear equation system iteratively. + + This re-uses the LU decomposition and is thus cheap. + Usually 3 up to 4 iterations are giving the maximal improvement. + """ + assert A.rows == A.cols, 'need n*n matrix' # TODO: really? + for _ in xrange(maxsteps): + r = ctx.residual(A, x, b) + if ctx.norm(r, 2) < 10*ctx.eps: + break + # this uses cached LU decomposition and is thus cheap + dx = ctx.lu_solve(A, -r) + x += dx + return x + + def lu(ctx, A): + """ + A -> P, L, U + + LU factorisation of a square matrix A. L is the lower, U the upper part. + P is the permutation matrix indicating the row swaps. + + P*A = L*U + + If you need efficiency, use the low-level method LU_decomp instead, it's + much more memory efficient. + """ + # get factorization + A, p = ctx.LU_decomp(A) + n = A.rows + L = ctx.matrix(n) + U = ctx.matrix(n) + for i in xrange(n): + for j in xrange(n): + if i > j: + L[i,j] = A[i,j] + elif i == j: + L[i,j] = 1 + U[i,j] = A[i,j] + else: + U[i,j] = A[i,j] + # calculate permutation matrix + P = ctx.eye(n) + for k in xrange(len(p)): + ctx.swap_row(P, k, p[k]) + return P, L, U + + def unitvector(ctx, n, i): + """ + Return the i-th n-dimensional unit vector. + """ + assert 0 < i <= n, 'this unit vector does not exist' + return [ctx.zero]*(i-1) + [ctx.one] + [ctx.zero]*(n-i) + + def inverse(ctx, A, **kwargs): + """ + Calculate the inverse of a matrix. + + If you want to solve an equation system Ax = b, it's recommended to use + solve(A, b) instead, it's about 3 times more efficient. + """ + prec = ctx.prec + try: + ctx.prec += 10 + # do not overwrite A + A = ctx.matrix(A, **kwargs).copy() + n = A.rows + # get LU factorisation + A, p = ctx.LU_decomp(A) + cols = [] + # calculate unit vectors and solve corresponding system to get columns + for i in xrange(1, n + 1): + e = ctx.unitvector(n, i) + y = ctx.L_solve(A, e, p) + cols.append(ctx.U_solve(A, y)) + # convert columns to matrix + inv = [] + for i in xrange(n): + row = [] + for j in xrange(n): + row.append(cols[j][i]) + inv.append(row) + result = ctx.matrix(inv, **kwargs) + finally: + ctx.prec = prec + return result + + def householder(ctx, A): + """ + (A|b) -> H, p, x, res + + (A|b) is the coefficient matrix with left hand side of an optionally + overdetermined linear equation system. + H and p contain all information about the transformation matrices. + x is the solution, res the residual. + """ + assert isinstance(A, ctx.matrix) + m = A.rows + n = A.cols + assert m >= n - 1 + # calculate Householder matrix + p = [] + for j in xrange(0, n - 1): + s = ctx.fsum((A[i,j])**2 for i in xrange(j, m)) + if not abs(s) > ctx.eps: + raise ValueError('matrix is numerically singular') + p.append(-ctx.sign(A[j,j]) * ctx.sqrt(s)) + kappa = ctx.one / (s - p[j] * A[j,j]) + A[j,j] -= p[j] + for k in xrange(j+1, n): + y = ctx.fsum(A[i,j] * A[i,k] for i in xrange(j, m)) * kappa + for i in xrange(j, m): + A[i,k] -= A[i,j] * y + # solve Rx = c1 + x = [A[i,n - 1] for i in xrange(n - 1)] + for i in xrange(n - 2, -1, -1): + x[i] -= ctx.fsum(A[i,j] * x[j] for j in xrange(i + 1, n - 1)) + x[i] /= p[i] + # calculate residual + if not m == n - 1: + r = [A[m-1-i, n-1] for i in xrange(m - n + 1)] + else: + # determined system, residual should be 0 + r = [0]*m # maybe a bad idea, changing r[i] will change all elements + return A, p, x, r + + #def qr(ctx, A): + # """ + # A -> Q, R + # + # QR factorisation of a square matrix A using Householder decomposition. + # Q is orthogonal, this leads to very few numerical errors. + # + # A = Q*R + # """ + # H, p, x, res = householder(A) + # TODO: implement this + + def residual(ctx, A, x, b, **kwargs): + """ + Calculate the residual of a solution to a linear equation system. + + r = A*x - b for A*x = b + """ + oldprec = ctx.prec + try: + ctx.prec *= 2 + A, x, b = ctx.matrix(A, **kwargs), ctx.matrix(x, **kwargs), ctx.matrix(b, **kwargs) + return A*x - b + finally: + ctx.prec = oldprec + + def qr_solve(ctx, A, b, norm=None, **kwargs): + """ + Ax = b => x, ||Ax - b|| + + Solve a determined or overdetermined linear equations system and + calculate the norm of the residual (error). + QR decomposition using Householder factorization is applied, which gives very + accurate results even for ill-conditioned matrices. qr_solve is twice as + efficient. + """ + if norm is None: + norm = ctx.norm + prec = ctx.prec + try: + prec += 10 + # do not overwrite A nor b + A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() + if A.rows < A.cols: + raise ValueError('cannot solve underdetermined system') + H, p, x, r = ctx.householder(ctx.extend(A, b)) + res = ctx.norm(r) + # calculate residual "manually" for determined systems + if res == 0: + res = ctx.norm(ctx.residual(A, x, b)) + return ctx.matrix(x, **kwargs), res + finally: + ctx.prec = prec + + # TODO: possible for complex matrices? -> have a look at GSL + def cholesky(ctx, A): + """ + Cholesky decomposition of a symmetric positive-definite matrix. + + Can be used to solve linear equation systems twice as efficient compared + to LU decomposition or to test whether A is positive-definite. + + A = L * L.T + Only L (the lower part) is returned. + """ + assert isinstance(A, ctx.matrix) + if not A.rows == A.cols: + raise ValueError('need n*n matrix') + n = A.rows + L = ctx.matrix(n) + for j in xrange(n): + s = A[j,j] - ctx.fsum(L[j,k]**2 for k in xrange(j)) + if s < ctx.eps: + raise ValueError('matrix not positive-definite') + L[j,j] = ctx.sqrt(s) + for i in xrange(j, n): + L[i,j] = (A[i,j] - ctx.fsum(L[i,k] * L[j,k] for k in xrange(j))) \ + / L[j,j] + return L + + def cholesky_solve(ctx, A, b, **kwargs): + """ + Ax = b => x + + Solve a symmetric positive-definite linear equation system. + This is twice as efficient as lu_solve. + + Typical use cases: + * A.T*A + * Hessian matrix + * differential equations + """ + prec = ctx.prec + try: + prec += 10 + # do not overwrite A nor b + A, b = ctx.matrix(A, **kwargs).copy(), ctx.matrix(b, **kwargs).copy() + if A.rows != A.cols: + raise ValueError('can only solve determined system') + # Cholesky factorization + L = ctx.cholesky(A) + # solve + n = L.rows + assert len(b) == n + for i in xrange(n): + b[i] -= ctx.fsum(L[i,j] * b[j] for j in xrange(i)) + b[i] /= L[i,i] + x = ctx.U_solve(L.T, b) + return x + finally: + ctx.prec = prec + + def det(ctx, A): + """ + Calculate the determinant of a matrix. + """ + prec = ctx.prec + try: + # do not overwrite A + A = ctx.matrix(A).copy() + # use LU factorization to calculate determinant + try: + R, p = ctx.LU_decomp(A) + except ZeroDivisionError: + return 0 + z = 1 + for i, e in enumerate(p): + if i != e: + z *= -1 + for i in xrange(A.rows): + z *= R[i,i] + return z + finally: + ctx.prec = prec + + def cond(ctx, A, norm=None): + """ + Calculate the condition number of a matrix using a specified matrix norm. + + The condition number estimates the sensitivity of a matrix to errors. + Example: small input errors for ill-conditioned coefficient matrices + alter the solution of the system dramatically. + + For ill-conditioned matrices it's recommended to use qr_solve() instead + of lu_solve(). This does not help with input errors however, it just avoids + to add additional errors. + + Definition: cond(A) = ||A|| * ||A**-1|| + """ + if norm is None: + norm = lambda x: ctx.mnorm(x,1) + return norm(A) * norm(ctx.inverse(A)) + + def lu_solve_mat(ctx, a, b): + """Solve a * x = b where a and b are matrices.""" + r = ctx.matrix(a.rows, b.cols) + for i in range(b.cols): + c = ctx.lu_solve(a, b.column(i)) + for j in range(len(c)): + r[j, i] = c[j] + return r + diff --git a/compiler/gdsMill/mpmath/matrices/matrices.py b/compiler/gdsMill/mpmath/matrices/matrices.py new file mode 100644 index 00000000..d962c74a --- /dev/null +++ b/compiler/gdsMill/mpmath/matrices/matrices.py @@ -0,0 +1,858 @@ +# TODO: interpret list as vectors (for multiplication) + +rowsep = '\n' +colsep = ' ' + +class _matrix(object): + """ + Numerical matrix. + + Specify the dimensions or the data as a nested list. + Elements default to zero. + Use a flat list to create a column vector easily. + + By default, only mpf is used to store the data. You can specify another type + using force_type=type. It's possible to specify None. + Make sure force_type(force_type()) is fast. + + Creating matrices + ----------------- + + Matrices in mpmath are implemented using dictionaries. Only non-zero values + are stored, so it is cheap to represent sparse matrices. + + The most basic way to create one is to use the ``matrix`` class directly. + You can create an empty matrix specifying the dimensions: + + >>> from mpmath import * + >>> mp.dps = 15 + >>> matrix(2) + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + >>> matrix(2, 3) + matrix( + [['0.0', '0.0', '0.0'], + ['0.0', '0.0', '0.0']]) + + Calling ``matrix`` with one dimension will create a square matrix. + + To access the dimensions of a matrix, use the ``rows`` or ``cols`` keyword: + + >>> A = matrix(3, 2) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0'], + ['0.0', '0.0']]) + >>> A.rows + 3 + >>> A.cols + 2 + + You can also change the dimension of an existing matrix. This will set the + new elements to 0. If the new dimension is smaller than before, the + concerning elements are discarded: + + >>> A.rows = 2 + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + + Internally ``mpmathify`` is used every time an element is set. This + is done using the syntax A[row,column], counting from 0: + + >>> A = matrix(2) + >>> A[1,1] = 1 + 1j + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '(1.0 + 1.0j)']]) + + You can use the keyword ``force_type`` to change the function which is + called on every new element: + + >>> matrix(2, 5, force_type=int) + matrix( + [[0, 0, 0, 0, 0], + [0, 0, 0, 0, 0]]) + + A more comfortable way to create a matrix lets you use nested lists: + + >>> matrix([[1, 2], [3, 4]]) + matrix( + [['1.0', '2.0'], + ['3.0', '4.0']]) + + If you want to preserve the type of the elements you can use + ``force_type=None``: + + >>> matrix([[1, 2.5], [1j, mpf(2)]], force_type=None) + matrix( + [[1, 2.5], + [1j, '2.0']]) + + Convenient advanced functions are available for creating various standard + matrices, see ``zeros``, ``ones``, ``diag``, ``eye``, ``randmatrix`` and + ``hilbert``. + + Vectors + ....... + + Vectors may also be represented by the ``matrix`` class (with rows = 1 or cols = 1). + For vectors there are some things which make life easier. A column vector can + be created using a flat list, a row vectors using an almost flat nested list:: + + >>> matrix([1, 2, 3]) + matrix( + [['1.0'], + ['2.0'], + ['3.0']]) + >>> matrix([[1, 2, 3]]) + matrix( + [['1.0', '2.0', '3.0']]) + + Optionally vectors can be accessed like lists, using only a single index:: + + >>> x = matrix([1, 2, 3]) + >>> x[1] + mpf('2.0') + >>> x[1,0] + mpf('2.0') + + Other + ..... + + Like you probably expected, matrices can be printed:: + + >>> print randmatrix(3) # doctest:+SKIP + [ 0.782963853573023 0.802057689719883 0.427895717335467] + [0.0541876859348597 0.708243266653103 0.615134039977379] + [ 0.856151514955773 0.544759264818486 0.686210904770947] + + Use ``nstr`` or ``nprint`` to specify the number of digits to print:: + + >>> nprint(randmatrix(5), 3) # doctest:+SKIP + [2.07e-1 1.66e-1 5.06e-1 1.89e-1 8.29e-1] + [6.62e-1 6.55e-1 4.47e-1 4.82e-1 2.06e-2] + [4.33e-1 7.75e-1 6.93e-2 2.86e-1 5.71e-1] + [1.01e-1 2.53e-1 6.13e-1 3.32e-1 2.59e-1] + [1.56e-1 7.27e-2 6.05e-1 6.67e-2 2.79e-1] + + As matrices are mutable, you will need to copy them sometimes:: + + >>> A = matrix(2) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + >>> B = A.copy() + >>> B[0,0] = 1 + >>> B + matrix( + [['1.0', '0.0'], + ['0.0', '0.0']]) + >>> A + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + + Finally, it is possible to convert a matrix to a nested list. This is very useful, + as most Python libraries involving matrices or arrays (namely NumPy or SymPy) + support this format:: + + >>> B.tolist() + [[mpf('1.0'), mpf('0.0')], [mpf('0.0'), mpf('0.0')]] + + + Matrix operations + ----------------- + + You can add and subtract matrices of compatible dimensions:: + + >>> A = matrix([[1, 2], [3, 4]]) + >>> B = matrix([[-2, 4], [5, 9]]) + >>> A + B + matrix( + [['-1.0', '6.0'], + ['8.0', '13.0']]) + >>> A - B + matrix( + [['3.0', '-2.0'], + ['-2.0', '-5.0']]) + >>> A + ones(3) # doctest:+ELLIPSIS + Traceback (most recent call last): + ... + ValueError: incompatible dimensions for addition + + It is possible to multiply or add matrices and scalars. In the latter case the + operation will be done element-wise:: + + >>> A * 2 + matrix( + [['2.0', '4.0'], + ['6.0', '8.0']]) + >>> A / 4 + matrix( + [['0.25', '0.5'], + ['0.75', '1.0']]) + >>> A - 1 + matrix( + [['0.0', '1.0'], + ['2.0', '3.0']]) + + Of course you can perform matrix multiplication, if the dimensions are + compatible:: + + >>> A * B + matrix( + [['8.0', '22.0'], + ['14.0', '48.0']]) + >>> matrix([[1, 2, 3]]) * matrix([[-6], [7], [-2]]) + matrix( + [['2.0']]) + + You can raise powers of square matrices:: + + >>> A**2 + matrix( + [['7.0', '10.0'], + ['15.0', '22.0']]) + + Negative powers will calculate the inverse:: + + >>> A**-1 + matrix( + [['-2.0', '1.0'], + ['1.5', '-0.5']]) + >>> A * A**-1 + matrix( + [['1.0', '1.0842021724855e-19'], + ['-2.16840434497101e-19', '1.0']]) + + Matrix transposition is straightforward:: + + >>> A = ones(2, 3) + >>> A + matrix( + [['1.0', '1.0', '1.0'], + ['1.0', '1.0', '1.0']]) + >>> A.T + matrix( + [['1.0', '1.0'], + ['1.0', '1.0'], + ['1.0', '1.0']]) + + Norms + ..... + + Sometimes you need to know how "large" a matrix or vector is. Due to their + multidimensional nature it's not possible to compare them, but there are + several functions to map a matrix or a vector to a positive real number, the + so called norms. + + For vectors the p-norm is intended, usually the 1-, the 2- and the oo-norm are + used. + + >>> x = matrix([-10, 2, 100]) + >>> norm(x, 1) + mpf('112.0') + >>> norm(x, 2) + mpf('100.5186549850325') + >>> norm(x, inf) + mpf('100.0') + + Please note that the 2-norm is the most used one, though it is more expensive + to calculate than the 1- or oo-norm. + + It is possible to generalize some vector norms to matrix norm:: + + >>> A = matrix([[1, -1000], [100, 50]]) + >>> mnorm(A, 1) + mpf('1050.0') + >>> mnorm(A, inf) + mpf('1001.0') + >>> mnorm(A, 'F') + mpf('1006.2310867787777') + + The last norm (the "Frobenius-norm") is an approximation for the 2-norm, which + is hard to calculate and not available. The Frobenius-norm lacks some + mathematical properties you might expect from a norm. + """ + + def __init__(self, *args, **kwargs): + self.__data = {} + # LU decompostion cache, this is useful when solving the same system + # multiple times, when calculating the inverse and when calculating the + # determinant + self._LU = None + convert = kwargs.get('force_type', self.ctx.convert) + if isinstance(args[0], (list, tuple)): + if isinstance(args[0][0], (list, tuple)): + # interpret nested list as matrix + A = args[0] + self.__rows = len(A) + self.__cols = len(A[0]) + for i, row in enumerate(A): + for j, a in enumerate(row): + self[i, j] = convert(a) + else: + # interpret list as row vector + v = args[0] + self.__rows = len(v) + self.__cols = 1 + for i, e in enumerate(v): + self[i, 0] = e + elif isinstance(args[0], int): + # create empty matrix of given dimensions + if len(args) == 1: + self.__rows = self.__cols = args[0] + else: + assert isinstance(args[1], int), 'expected int' + self.__rows = args[0] + self.__cols = args[1] + elif isinstance(args[0], _matrix): + A = args[0].copy() + self.__data = A._matrix__data + self.__rows = A._matrix__rows + self.__cols = A._matrix__cols + convert = kwargs.get('force_type', self.ctx.convert) + for i in xrange(A.__rows): + for j in xrange(A.__cols): + A[i,j] = convert(A[i,j]) + elif hasattr(args[0], 'tolist'): + A = self.ctx.matrix(args[0].tolist()) + self.__data = A._matrix__data + self.__rows = A._matrix__rows + self.__cols = A._matrix__cols + else: + raise TypeError('could not interpret given arguments') + + def apply(self, f): + """ + Return a copy of self with the function `f` applied elementwise. + """ + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i,j] = f(self[i,j]) + return new + + def __nstr__(self, n=None, **kwargs): + # Build table of string representations of the elements + res = [] + # Track per-column max lengths for pretty alignment + maxlen = [0] * self.cols + for i in range(self.rows): + res.append([]) + for j in range(self.cols): + if n: + string = self.ctx.nstr(self[i,j], n, **kwargs) + else: + string = str(self[i,j]) + res[-1].append(string) + maxlen[j] = max(len(string), maxlen[j]) + # Patch strings together + for i, row in enumerate(res): + for j, elem in enumerate(row): + # Pad each element up to maxlen so the columns line up + row[j] = elem.rjust(maxlen[j]) + res[i] = "[" + colsep.join(row) + "]" + return rowsep.join(res) + + def __str__(self): + return self.__nstr__() + + def _toliststr(self, avoid_type=False): + """ + Create a list string from a matrix. + + If avoid_type: avoid multiple 'mpf's. + """ + # XXX: should be something like self.ctx._types + typ = self.ctx.mpf + s = '[' + for i in xrange(self.__rows): + s += '[' + for j in xrange(self.__cols): + if not avoid_type or not isinstance(self[i,j], typ): + a = repr(self[i,j]) + else: + a = "'" + str(self[i,j]) + "'" + s += a + ', ' + s = s[:-2] + s += '],\n ' + s = s[:-3] + s += ']' + return s + + def tolist(self): + """ + Convert the matrix to a nested list. + """ + return [[self[i,j] for j in range(self.__cols)] for i in range(self.__rows)] + + def __repr__(self): + if self.ctx.pretty: + return self.__str__() + s = 'matrix(\n' + s += self._toliststr(avoid_type=True) + ')' + return s + + def __getitem__(self, key): + if type(key) is int: + # only sufficent for vectors + if self.__rows == 1: + key = (0, key) + elif self.__cols == 1: + key = (key, 0) + else: + raise IndexError('insufficient indices for matrix') + if key in self.__data: + return self.__data[key] + else: + if key[0] >= self.__rows or key[1] >= self.__cols: + raise IndexError('matrix index out of range') + return self.ctx.zero + + def __setitem__(self, key, value): + if type(key) is int: + # only sufficent for vectors + if self.__rows == 1: + key = (0, key) + elif self.__cols == 1: + key = (key, 0) + else: + raise IndexError('insufficient indices for matrix') + if key[0] >= self.__rows or key[1] >= self.__cols: + raise IndexError('matrix index out of range') + value = self.ctx.convert(value) + if value: # only store non-zeros + self.__data[key] = value + elif key in self.__data: + del self.__data[key] + # TODO: maybe do this better, if the performance impact is significant + if self._LU: + self._LU = None + + def __iter__(self): + for i in xrange(self.__rows): + for j in xrange(self.__cols): + yield self[i,j] + + def __mul__(self, other): + if isinstance(other, self.ctx.matrix): + # dot multiplication TODO: use Strassen's method? + if self.__cols != other.__rows: + raise ValueError('dimensions not compatible for multiplication') + new = self.ctx.matrix(self.__rows, other.__cols) + for i in xrange(self.__rows): + for j in xrange(other.__cols): + new[i, j] = self.ctx.fdot((self[i,k], other[k,j]) + for k in xrange(other.__rows)) + return new + else: + # try scalar multiplication + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i, j] = other * self[i, j] + return new + + def __rmul__(self, other): + # assume other is scalar and thus commutative + assert not isinstance(other, self.ctx.matrix) + return self.__mul__(other) + + def __pow__(self, other): + # avoid cyclic import problems + #from linalg import inverse + if not isinstance(other, int): + raise ValueError('only integer exponents are supported') + if not self.__rows == self.__cols: + raise ValueError('only powers of square matrices are defined') + n = other + if n == 0: + return self.ctx.eye(self.__rows) + if n < 0: + n = -n + neg = True + else: + neg = False + i = n + y = 1 + z = self.copy() + while i != 0: + if i % 2 == 1: + y = y * z + z = z*z + i = i // 2 + if neg: + y = self.ctx.inverse(y) + return y + + def __div__(self, other): + # assume other is scalar and do element-wise divison + assert not isinstance(other, self.ctx.matrix) + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i,j] = self[i,j] / other + return new + + __truediv__ = __div__ + + def __add__(self, other): + if isinstance(other, self.ctx.matrix): + if not (self.__rows == other.__rows and self.__cols == other.__cols): + raise ValueError('incompatible dimensions for addition') + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i,j] = self[i,j] + other[i,j] + return new + else: + # assume other is scalar and add element-wise + new = self.ctx.matrix(self.__rows, self.__cols) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[i,j] += self[i,j] + other + return new + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + if isinstance(other, self.ctx.matrix) and not (self.__rows == other.__rows + and self.__cols == other.__cols): + raise ValueError('incompatible dimensions for substraction') + return self.__add__(other * (-1)) + + def __neg__(self): + return (-1) * self + + def __rsub__(self, other): + return -self + other + + def __eq__(self, other): + return self.__rows == other.__rows and self.__cols == other.__cols \ + and self.__data == other.__data + + def __len__(self): + if self.rows == 1: + return self.cols + elif self.cols == 1: + return self.rows + else: + return self.rows # do it like numpy + + def __getrows(self): + return self.__rows + + def __setrows(self, value): + for key in self.__data.copy().iterkeys(): + if key[0] >= value: + del self.__data[key] + self.__rows = value + + rows = property(__getrows, __setrows, doc='number of rows') + + def __getcols(self): + return self.__cols + + def __setcols(self, value): + for key in self.__data.copy().iterkeys(): + if key[1] >= value: + del self.__data[key] + self.__cols = value + + cols = property(__getcols, __setcols, doc='number of columns') + + def transpose(self): + new = self.ctx.matrix(self.__cols, self.__rows) + for i in xrange(self.__rows): + for j in xrange(self.__cols): + new[j,i] = self[i,j] + return new + + T = property(transpose) + + def conjugate(self): + return self.apply(self.ctx.conj) + + def transpose_conj(self): + return self.conjugate().transpose() + + H = property(transpose_conj) + + def copy(self): + new = self.ctx.matrix(self.__rows, self.__cols) + new.__data = self.__data.copy() + return new + + __copy__ = copy + + def column(self, n): + m = self.ctx.matrix(self.rows, 1) + for i in range(self.rows): + m[i] = self[i,n] + return m + +class MatrixMethods(object): + + def __init__(ctx): + # XXX: subclass + ctx.matrix = type('matrix', (_matrix,), {}) + ctx.matrix.ctx = ctx + ctx.matrix.convert = ctx.convert + + def eye(ctx, n, **kwargs): + """ + Create square identity matrix n x n. + """ + A = ctx.matrix(n, **kwargs) + for i in xrange(n): + A[i,i] = 1 + return A + + def diag(ctx, diagonal, **kwargs): + """ + Create square diagonal matrix using given list. + + Example: + >>> from mpmath import diag, mp + >>> mp.pretty = False + >>> diag([1, 2, 3]) + matrix( + [['1.0', '0.0', '0.0'], + ['0.0', '2.0', '0.0'], + ['0.0', '0.0', '3.0']]) + """ + A = ctx.matrix(len(diagonal), **kwargs) + for i in xrange(len(diagonal)): + A[i,i] = diagonal[i] + return A + + def zeros(ctx, *args, **kwargs): + """ + Create matrix m x n filled with zeros. + One given dimension will create square matrix n x n. + + Example: + >>> from mpmath import zeros, mp + >>> mp.pretty = False + >>> zeros(2) + matrix( + [['0.0', '0.0'], + ['0.0', '0.0']]) + """ + if len(args) == 1: + m = n = args[0] + elif len(args) == 2: + m = args[0] + n = args[1] + else: + raise TypeError('zeros expected at most 2 arguments, got %i' % len(args)) + A = ctx.matrix(m, n, **kwargs) + for i in xrange(m): + for j in xrange(n): + A[i,j] = 0 + return A + + def ones(ctx, *args, **kwargs): + """ + Create matrix m x n filled with ones. + One given dimension will create square matrix n x n. + + Example: + >>> from mpmath import ones, mp + >>> mp.pretty = False + >>> ones(2) + matrix( + [['1.0', '1.0'], + ['1.0', '1.0']]) + """ + if len(args) == 1: + m = n = args[0] + elif len(args) == 2: + m = args[0] + n = args[1] + else: + raise TypeError('ones expected at most 2 arguments, got %i' % len(args)) + A = ctx.matrix(m, n, **kwargs) + for i in xrange(m): + for j in xrange(n): + A[i,j] = 1 + return A + + def hilbert(ctx, m, n=None): + """ + Create (pseudo) hilbert matrix m x n. + One given dimension will create hilbert matrix n x n. + + The matrix is very ill-conditioned and symmetric, positive definite if + square. + """ + if n is None: + n = m + A = ctx.matrix(m, n) + for i in xrange(m): + for j in xrange(n): + A[i,j] = ctx.one / (i + j + 1) + return A + + def randmatrix(ctx, m, n=None, min=0, max=1, **kwargs): + """ + Create a random m x n matrix. + + All values are >= min and >> from mpmath import randmatrix + >>> randmatrix(2) # doctest:+SKIP + matrix( + [['0.53491598236191806', '0.57195669543302752'], + ['0.85589992269513615', '0.82444367501382143']]) + """ + if not n: + n = m + A = ctx.matrix(m, n, **kwargs) + for i in xrange(m): + for j in xrange(n): + A[i,j] = ctx.rand() * (max - min) + min + return A + + def swap_row(ctx, A, i, j): + """ + Swap row i with row j. + """ + if i == j: + return + if isinstance(A, ctx.matrix): + for k in xrange(A.cols): + A[i,k], A[j,k] = A[j,k], A[i,k] + elif isinstance(A, list): + A[i], A[j] = A[j], A[i] + else: + raise TypeError('could not interpret type') + + def extend(ctx, A, b): + """ + Extend matrix A with column b and return result. + """ + assert isinstance(A, ctx.matrix) + assert A.rows == len(b) + A = A.copy() + A.cols += 1 + for i in xrange(A.rows): + A[i, A.cols-1] = b[i] + return A + + def norm(ctx, x, p=2): + r""" + Gives the entrywise `p`-norm of an iterable *x*, i.e. the vector norm + `\left(\sum_k |x_k|^p\right)^{1/p}`, for any given `1 \le p \le \infty`. + + Special cases: + + If *x* is not iterable, this just returns ``absmax(x)``. + + ``p=1`` gives the sum of absolute values. + + ``p=2`` is the standard Euclidean vector norm. + + ``p=inf`` gives the magnitude of the largest element. + + For *x* a matrix, ``p=2`` is the Frobenius norm. + For operator matrix norms, use :func:`mnorm` instead. + + You can use the string 'inf' as well as float('inf') or mpf('inf') + to specify the infinity norm. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> x = matrix([-10, 2, 100]) + >>> norm(x, 1) + mpf('112.0') + >>> norm(x, 2) + mpf('100.5186549850325') + >>> norm(x, inf) + mpf('100.0') + + """ + try: + iter(x) + except TypeError: + return ctx.absmax(x) + if type(p) is not int: + p = ctx.convert(p) + if p == ctx.inf: + return max(ctx.absmax(i) for i in x) + elif p == 1: + return ctx.fsum(x, absolute=1) + elif p == 2: + return ctx.sqrt(ctx.fsum(x, absolute=1, squared=1)) + elif p > 1: + return ctx.nthroot(ctx.fsum(abs(i)**p for i in x), p) + else: + raise ValueError('p has to be >= 1') + + def mnorm(ctx, A, p=1): + r""" + Gives the matrix (operator) `p`-norm of A. Currently ``p=1`` and ``p=inf`` + are supported: + + ``p=1`` gives the 1-norm (maximal column sum) + + ``p=inf`` gives the `\infty`-norm (maximal row sum). + You can use the string 'inf' as well as float('inf') or mpf('inf') + + ``p=2`` (not implemented) for a square matrix is the usual spectral + matrix norm, i.e. the largest singular value. + + ``p='f'`` (or 'F', 'fro', 'Frobenius, 'frobenius') gives the + Frobenius norm, which is the elementwise 2-norm. The Frobenius norm is an + approximation of the spectral norm and satisfies + + .. math :: + + \frac{1}{\sqrt{\mathrm{rank}(A)}} \|A\|_F \le \|A\|_2 \le \|A\|_F + + The Frobenius norm lacks some mathematical properties that might + be expected of a norm. + + For general elementwise `p`-norms, use :func:`norm` instead. + + **Examples** + + >>> from mpmath import * + >>> mp.dps = 15; mp.pretty = False + >>> A = matrix([[1, -1000], [100, 50]]) + >>> mnorm(A, 1) + mpf('1050.0') + >>> mnorm(A, inf) + mpf('1001.0') + >>> mnorm(A, 'F') + mpf('1006.2310867787777') + + """ + A = ctx.matrix(A) + if type(p) is not int: + if type(p) is str and 'frobenius'.startswith(p.lower()): + return ctx.norm(A, 2) + p = ctx.convert(p) + m, n = A.rows, A.cols + if p == 1: + return max(ctx.fsum((A[i,j] for i in xrange(m)), absolute=1) for j in xrange(n)) + elif p == ctx.inf: + return max(ctx.fsum((A[i,j] for j in xrange(n)), absolute=1) for i in xrange(m)) + else: + raise NotImplementedError("matrix p-norm for arbitrary p") + +if __name__ == '__main__': + import doctest + doctest.testmod() diff --git a/compiler/gdsMill/mpmath/rational.py b/compiler/gdsMill/mpmath/rational.py new file mode 100644 index 00000000..e767c7da --- /dev/null +++ b/compiler/gdsMill/mpmath/rational.py @@ -0,0 +1,106 @@ +# TODO: use gmpy.mpq when available? + +class mpq(tuple): + """ + Rational number type, only intended for internal use. + """ + + """ + def _mpmath_(self, prec, rounding): + # XXX + return mp.make_mpf(from_rational(self[0], self[1], prec, rounding)) + #(mpf(self[0])/self[1])._mpf_ + + """ + + def __int__(self): + a, b = self + return a // b + + def __abs__(self): + a, b = self + return mpq((abs(a), b)) + + def __neg__(self): + a, b = self + return mpq((-a, b)) + + def __nonzero__(self): + return bool(self[0]) + + def __cmp__(self, other): + if type(other) is int and self[1] == 1: + return cmp(self[0], other) + return NotImplemented + + def __add__(self, other): + if isinstance(other, mpq): + a, b = self + c, d = other + return mpq((a*d+b*c, b*d)) + if isinstance(other, (int, long)): + a, b = self + return mpq((a+b*other, b)) + return NotImplemented + + __radd__ = __add__ + + def __sub__(self, other): + if isinstance(other, mpq): + a, b = self + c, d = other + return mpq((a*d-b*c, b*d)) + if isinstance(other, (int, long)): + a, b = self + return mpq((a-b*other, b)) + return NotImplemented + + def __rsub__(self, other): + if isinstance(other, mpq): + a, b = self + c, d = other + return mpq((b*c-a*d, b*d)) + if isinstance(other, (int, long)): + a, b = self + return mpq((b*other-a, b)) + return NotImplemented + + def __mul__(self, other): + if isinstance(other, mpq): + a, b = self + c, d = other + return mpq((a*c, b*d)) + if isinstance(other, (int, long)): + a, b = self + return mpq((a*other, b)) + return NotImplemented + + def __div__(self, other): + if isinstance(other, (int, long)): + if other: + a, b = self + return mpq((a, b*other)) + raise ZeroDivisionError + return NotImplemented + + def __pow__(self, other): + if type(other) is int: + a, b = self + return mpq((a**other, b**other)) + return NotImplemented + + __rmul__ = __mul__ + + +mpq_1 = mpq((1,1)) +mpq_0 = mpq((0,1)) +mpq_1_2 = mpq((1,2)) +mpq_3_2 = mpq((3,2)) +mpq_1_4 = mpq((1,4)) +mpq_1_16 = mpq((1,16)) +mpq_3_16 = mpq((3,16)) +mpq_5_2 = mpq((5,2)) +mpq_3_4 = mpq((3,4)) +mpq_7_4 = mpq((7,4)) +mpq_5_4 = mpq((5,4)) + diff --git a/compiler/gdsMill/mpmath/tests/__init__.py b/compiler/gdsMill/mpmath/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/compiler/gdsMill/mpmath/tests/runtests.py b/compiler/gdsMill/mpmath/tests/runtests.py new file mode 100644 index 00000000..8c1afaec --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/runtests.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python + +""" +python runtests.py -py + Use py.test to run tests (more useful for debugging) + +python runtests.py -psyco + Enable psyco to make tests run about 50% faster + +python runtests.py -coverage + Generate test coverage report. Statistics are written to /tmp + +python runtests.py -profile + Generate profile stats (this is much slower) + +python runtests.py -nogmpy + Run tests without using GMPY even if it exists + +python runtests.py -strict + Enforce extra tests in normalize() + +python runtests.py -local + Insert '../..' at the beginning of sys.path to use local mpmath + +Additional arguments are used to filter the tests to run. Only files that have +one of the arguments in their name are executed. + +""" + +import sys, os, traceback + +if "-psyco" in sys.argv: + sys.argv.remove('-psyco') + import psyco + psyco.full() + +profile = False +if "-profile" in sys.argv: + sys.argv.remove('-profile') + profile = True + +coverage = False +if "-coverage" in sys.argv: + sys.argv.remove('-coverage') + coverage = True + +if "-nogmpy" in sys.argv: + sys.argv.remove('-nogmpy') + os.environ['MPMATH_NOGMPY'] = 'Y' + +if "-strict" in sys.argv: + sys.argv.remove('-strict') + os.environ['MPMATH_STRICT'] = 'Y' + +if "-local" in sys.argv: + sys.argv.remove('-local') + importdir = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), + '../..')) +else: + importdir = '' + +# TODO: add a flag for this +testdir = '' + +def testit(importdir='', testdir=''): + """Run all tests in testdir while importing from importdir.""" + if importdir: + sys.path.insert(1, importdir) + if testdir: + sys.path.insert(1, testdir) + import os.path + import mpmath + print "mpmath imported from", os.path.dirname(mpmath.__file__) + print "mpmath backend:", mpmath.libmp.backend.BACKEND + print "mpmath mp class:", repr(mpmath.mp) + print "mpmath version:", mpmath.__version__ + print "Python version:", sys.version + print + if "-py" in sys.argv: + sys.argv.remove('-py') + import py + py.test.cmdline.main() + else: + import glob + from timeit import default_timer as clock + modules = [] + args = sys.argv[1:] + # search for tests in directory of this file if not otherwise specified + if not testdir: + pattern = os.path.dirname(sys.argv[0]) + else: + pattern = testdir + if pattern: + pattern += '/' + pattern += 'test*.py' + # look for tests (respecting specified filter) + for f in glob.glob(pattern): + name = os.path.splitext(os.path.basename(f))[0] + # If run as a script, only run tests given as args, if any are given + if args and __name__ == "__main__": + ok = False + for arg in args: + if arg in name: + ok = True + break + if not ok: + continue + module = __import__(name) + priority = module.__dict__.get('priority', 100) + if priority == 666: + modules = [[priority, name, module]] + break + modules.append([priority, name, module]) + # execute tests + modules.sort() + tstart = clock() + for priority, name, module in modules: + print name + for f in sorted(module.__dict__.keys()): + if f.startswith('test_'): + if coverage and ('numpy' in f): + continue + print " ", f[5:].ljust(25), + t1 = clock() + try: + module.__dict__[f]() + except: + etype, evalue, trb = sys.exc_info() + if etype in (KeyboardInterrupt, SystemExit): + raise + print + print "TEST FAILED!" + print + traceback.print_exc() + t2 = clock() + print "ok", " ", ("%.7f" % (t2-t1)), "s" + tend = clock() + print + print "finished tests in", ("%.2f" % (tend-tstart)), "seconds" + # clean sys.path + if importdir: + sys.path.remove(importdir) + if testdir: + sys.path.remove(testdir) + +if __name__ == '__main__': + if profile: + import cProfile + cProfile.run("testit('%s', '%s')" % (importdir, testdir), sort=1) + elif coverage: + import trace + tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix], + trace=0, count=1) + tracer.run('testit(importdir, testdir)') + r = tracer.results() + r.write_results(show_missing=True, summary=True, coverdir="/tmp") + else: + testit(importdir, testdir) + diff --git a/compiler/gdsMill/mpmath/tests/test_basic_ops.py b/compiler/gdsMill/mpmath/tests/test_basic_ops.py new file mode 100644 index 00000000..b2c42c6b --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_basic_ops.py @@ -0,0 +1,161 @@ +from mpmath import * + +def test_type_compare(): + assert mpf(2) == mpc(2,0) + assert mpf(0) == mpc(0) + assert mpf(2) != mpc(2, 0.00001) + assert mpf(2) == 2.0 + assert mpf(2) != 3.0 + assert mpf(2) == 2 + assert mpf(2) != '2.0' + assert mpc(2) != '2.0' + +def test_add(): + assert mpf(2.5) + mpf(3) == 5.5 + assert mpf(2.5) + 3 == 5.5 + assert mpf(2.5) + 3.0 == 5.5 + assert 3 + mpf(2.5) == 5.5 + assert 3.0 + mpf(2.5) == 5.5 + assert (3+0j) + mpf(2.5) == 5.5 + assert mpc(2.5) + mpf(3) == 5.5 + assert mpc(2.5) + 3 == 5.5 + assert mpc(2.5) + 3.0 == 5.5 + assert mpc(2.5) + (3+0j) == 5.5 + assert 3 + mpc(2.5) == 5.5 + assert 3.0 + mpc(2.5) == 5.5 + assert (3+0j) + mpc(2.5) == 5.5 + +def test_sub(): + assert mpf(2.5) - mpf(3) == -0.5 + assert mpf(2.5) - 3 == -0.5 + assert mpf(2.5) - 3.0 == -0.5 + assert 3 - mpf(2.5) == 0.5 + assert 3.0 - mpf(2.5) == 0.5 + assert (3+0j) - mpf(2.5) == 0.5 + assert mpc(2.5) - mpf(3) == -0.5 + assert mpc(2.5) - 3 == -0.5 + assert mpc(2.5) - 3.0 == -0.5 + assert mpc(2.5) - (3+0j) == -0.5 + assert 3 - mpc(2.5) == 0.5 + assert 3.0 - mpc(2.5) == 0.5 + assert (3+0j) - mpc(2.5) == 0.5 + +def test_mul(): + assert mpf(2.5) * mpf(3) == 7.5 + assert mpf(2.5) * 3 == 7.5 + assert mpf(2.5) * 3.0 == 7.5 + assert 3 * mpf(2.5) == 7.5 + assert 3.0 * mpf(2.5) == 7.5 + assert (3+0j) * mpf(2.5) == 7.5 + assert mpc(2.5) * mpf(3) == 7.5 + assert mpc(2.5) * 3 == 7.5 + assert mpc(2.5) * 3.0 == 7.5 + assert mpc(2.5) * (3+0j) == 7.5 + assert 3 * mpc(2.5) == 7.5 + assert 3.0 * mpc(2.5) == 7.5 + assert (3+0j) * mpc(2.5) == 7.5 + +def test_div(): + assert mpf(6) / mpf(3) == 2.0 + assert mpf(6) / 3 == 2.0 + assert mpf(6) / 3.0 == 2.0 + assert 6 / mpf(3) == 2.0 + assert 6.0 / mpf(3) == 2.0 + assert (6+0j) / mpf(3.0) == 2.0 + assert mpc(6) / mpf(3) == 2.0 + assert mpc(6) / 3 == 2.0 + assert mpc(6) / 3.0 == 2.0 + assert mpc(6) / (3+0j) == 2.0 + assert 6 / mpc(3) == 2.0 + assert 6.0 / mpc(3) == 2.0 + assert (6+0j) / mpc(3) == 2.0 + +def test_pow(): + assert mpf(6) ** mpf(3) == 216.0 + assert mpf(6) ** 3 == 216.0 + assert mpf(6) ** 3.0 == 216.0 + assert 6 ** mpf(3) == 216.0 + assert 6.0 ** mpf(3) == 216.0 + assert (6+0j) ** mpf(3.0) == 216.0 + assert mpc(6) ** mpf(3) == 216.0 + assert mpc(6) ** 3 == 216.0 + assert mpc(6) ** 3.0 == 216.0 + assert mpc(6) ** (3+0j) == 216.0 + assert 6 ** mpc(3) == 216.0 + assert 6.0 ** mpc(3) == 216.0 + assert (6+0j) ** mpc(3) == 216.0 + +def test_mixed_misc(): + assert 1 + mpf(3) == mpf(3) + 1 == 4 + assert 1 - mpf(3) == -(mpf(3) - 1) == -2 + assert 3 * mpf(2) == mpf(2) * 3 == 6 + assert 6 / mpf(2) == mpf(6) / 2 == 3 + assert 1.0 + mpf(3) == mpf(3) + 1.0 == 4 + assert 1.0 - mpf(3) == -(mpf(3) - 1.0) == -2 + assert 3.0 * mpf(2) == mpf(2) * 3.0 == 6 + assert 6.0 / mpf(2) == mpf(6) / 2.0 == 3 + +def test_add_misc(): + mp.dps = 15 + assert mpf(4) + mpf(-70) == -66 + assert mpf(1) + mpf(1.1)/80 == 1 + 1.1/80 + assert mpf((1, 10000000000)) + mpf(3) == mpf((1, 10000000000)) + assert mpf(3) + mpf((1, 10000000000)) == mpf((1, 10000000000)) + assert mpf((1, -10000000000)) + mpf(3) == mpf(3) + assert mpf(3) + mpf((1, -10000000000)) == mpf(3) + assert mpf(1) + 1e-15 != 1 + assert mpf(1) + 1e-20 == 1 + assert mpf(1.07e-22) + 0 == mpf(1.07e-22) + assert mpf(0) + mpf(1.07e-22) == mpf(1.07e-22) + +def test_complex_misc(): + # many more tests needed + assert 1 + mpc(2) == 3 + assert not mpc(2).ae(2 + 1e-13) + assert mpc(2+1e-15j).ae(2) + +def test_complex_zeros(): + for a in [0,2]: + for b in [0,3]: + for c in [0,4]: + for d in [0,5]: + assert mpc(a,b)*mpc(c,d) == complex(a,b)*complex(c,d) + +def test_hash(): + for i in range(-256, 256): + assert hash(mpf(i)) == hash(i) + assert hash(mpf(0.5)) == hash(0.5) + assert hash(mpc(2,3)) == hash(2+3j) + # Check that this doesn't fail + assert hash(inf) + # Check that overflow doesn't assign equal hashes to large numbers + assert hash(mpf('1e1000')) != hash('1e10000') + assert hash(mpc(100,'1e1000')) != hash(mpc(200,'1e1000')) + +def test_arithmetic_functions(): + import operator + ops = [(operator.add, fadd), (operator.sub, fsub), (operator.mul, fmul), + (operator.div, fdiv)] + a = mpf(0.27) + b = mpf(1.13) + c = mpc(0.51+2.16j) + d = mpc(1.08-0.99j) + for x in [a,b,c,d]: + for y in [a,b,c,d]: + for op, fop in ops: + if fop is not fdiv: + mp.prec = 200 + z0 = op(x,y) + mp.prec = 60 + z1 = op(x,y) + mp.prec = 53 + z2 = op(x,y) + assert fop(x, y, prec=60) == z1 + assert fop(x, y) == z2 + if fop is not fdiv: + assert fop(x, y, prec=inf) == z0 + assert fop(x, y, dps=inf) == z0 + assert fop(x, y, exact=True) == z0 + assert fneg(fneg(z1, exact=True), prec=inf) == z1 + assert fneg(z1) == -(+z1) + mp.dps = 15 diff --git a/compiler/gdsMill/mpmath/tests/test_bitwise.py b/compiler/gdsMill/mpmath/tests/test_bitwise.py new file mode 100644 index 00000000..125c0d58 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_bitwise.py @@ -0,0 +1,172 @@ +""" +Test bit-level integer and mpf operations +""" + +from mpmath import * +from mpmath.libmp import * + +def test_bitcount(): + assert bitcount(0) == 0 + assert bitcount(1) == 1 + assert bitcount(7) == 3 + assert bitcount(8) == 4 + assert bitcount(2**100) == 101 + assert bitcount(2**100-1) == 100 + +def test_trailing(): + assert trailing(0) == 0 + assert trailing(1) == 0 + assert trailing(2) == 1 + assert trailing(7) == 0 + assert trailing(8) == 3 + assert trailing(2**100) == 100 + assert trailing(2**100-1) == 0 + +def test_round_down(): + assert from_man_exp(0, -4, 4, round_down)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_down)[:3] == (0, 15, 0) + assert from_man_exp(0xf1, -4, 4, round_down)[:3] == (0, 15, 0) + assert from_man_exp(0xff, -4, 4, round_down)[:3] == (0, 15, 0) + assert from_man_exp(-0xf0, -4, 4, round_down)[:3] == (1, 15, 0) + assert from_man_exp(-0xf1, -4, 4, round_down)[:3] == (1, 15, 0) + assert from_man_exp(-0xff, -4, 4, round_down)[:3] == (1, 15, 0) + +def test_round_up(): + assert from_man_exp(0, -4, 4, round_up)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_up)[:3] == (0, 15, 0) + assert from_man_exp(0xf1, -4, 4, round_up)[:3] == (0, 1, 4) + assert from_man_exp(0xff, -4, 4, round_up)[:3] == (0, 1, 4) + assert from_man_exp(-0xf0, -4, 4, round_up)[:3] == (1, 15, 0) + assert from_man_exp(-0xf1, -4, 4, round_up)[:3] == (1, 1, 4) + assert from_man_exp(-0xff, -4, 4, round_up)[:3] == (1, 1, 4) + +def test_round_floor(): + assert from_man_exp(0, -4, 4, round_floor)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_floor)[:3] == (0, 15, 0) + assert from_man_exp(0xf1, -4, 4, round_floor)[:3] == (0, 15, 0) + assert from_man_exp(0xff, -4, 4, round_floor)[:3] == (0, 15, 0) + assert from_man_exp(-0xf0, -4, 4, round_floor)[:3] == (1, 15, 0) + assert from_man_exp(-0xf1, -4, 4, round_floor)[:3] == (1, 1, 4) + assert from_man_exp(-0xff, -4, 4, round_floor)[:3] == (1, 1, 4) + +def test_round_ceiling(): + assert from_man_exp(0, -4, 4, round_ceiling)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_ceiling)[:3] == (0, 15, 0) + assert from_man_exp(0xf1, -4, 4, round_ceiling)[:3] == (0, 1, 4) + assert from_man_exp(0xff, -4, 4, round_ceiling)[:3] == (0, 1, 4) + assert from_man_exp(-0xf0, -4, 4, round_ceiling)[:3] == (1, 15, 0) + assert from_man_exp(-0xf1, -4, 4, round_ceiling)[:3] == (1, 15, 0) + assert from_man_exp(-0xff, -4, 4, round_ceiling)[:3] == (1, 15, 0) + +def test_round_nearest(): + assert from_man_exp(0, -4, 4, round_nearest)[:3] == (0, 0, 0) + assert from_man_exp(0xf0, -4, 4, round_nearest)[:3] == (0, 15, 0) + assert from_man_exp(0xf7, -4, 4, round_nearest)[:3] == (0, 15, 0) + assert from_man_exp(0xf8, -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1000 -> 10000.0 + assert from_man_exp(0xf9, -4, 4, round_nearest)[:3] == (0, 1, 4) # 1111.1001 -> 10000.0 + assert from_man_exp(0xe8, -4, 4, round_nearest)[:3] == (0, 7, 1) # 1110.1000 -> 1110.0 + assert from_man_exp(0xe9, -4, 4, round_nearest)[:3] == (0, 15, 0) # 1110.1001 -> 1111.0 + assert from_man_exp(-0xf0, -4, 4, round_nearest)[:3] == (1, 15, 0) + assert from_man_exp(-0xf7, -4, 4, round_nearest)[:3] == (1, 15, 0) + assert from_man_exp(-0xf8, -4, 4, round_nearest)[:3] == (1, 1, 4) + assert from_man_exp(-0xf9, -4, 4, round_nearest)[:3] == (1, 1, 4) + assert from_man_exp(-0xe8, -4, 4, round_nearest)[:3] == (1, 7, 1) + assert from_man_exp(-0xe9, -4, 4, round_nearest)[:3] == (1, 15, 0) + +def test_rounding_bugs(): + # 1 less than power-of-two cases + assert from_man_exp(72057594037927935, -56, 53, round_up) == (0, 1, 0, 1) + assert from_man_exp(73786976294838205979l, -65, 53, round_nearest) == (0, 1, 1, 1) + assert from_man_exp(31, 0, 4, round_up) == (0, 1, 5, 1) + assert from_man_exp(-31, 0, 4, round_floor) == (1, 1, 5, 1) + assert from_man_exp(255, 0, 7, round_up) == (0, 1, 8, 1) + assert from_man_exp(-255, 0, 7, round_floor) == (1, 1, 8, 1) + +def test_rounding_issue160(): + a = from_man_exp(9867,-100) + b = from_man_exp(9867,-200) + c = from_man_exp(-1,0) + z = (1, 1023, -10, 10) + assert mpf_add(a, c, 10, 'd') == z + assert mpf_add(b, c, 10, 'd') == z + assert mpf_add(c, a, 10, 'd') == z + assert mpf_add(c, b, 10, 'd') == z + +def test_perturb(): + a = fone + b = from_float(0.99999999999999989) + c = from_float(1.0000000000000002) + assert mpf_perturb(a, 0, 53, round_nearest) == a + assert mpf_perturb(a, 1, 53, round_nearest) == a + assert mpf_perturb(a, 0, 53, round_up) == c + assert mpf_perturb(a, 0, 53, round_ceiling) == c + assert mpf_perturb(a, 0, 53, round_down) == a + assert mpf_perturb(a, 0, 53, round_floor) == a + assert mpf_perturb(a, 1, 53, round_up) == a + assert mpf_perturb(a, 1, 53, round_ceiling) == a + assert mpf_perturb(a, 1, 53, round_down) == b + assert mpf_perturb(a, 1, 53, round_floor) == b + a = mpf_neg(a) + b = mpf_neg(b) + c = mpf_neg(c) + assert mpf_perturb(a, 0, 53, round_nearest) == a + assert mpf_perturb(a, 1, 53, round_nearest) == a + assert mpf_perturb(a, 0, 53, round_up) == a + assert mpf_perturb(a, 0, 53, round_floor) == a + assert mpf_perturb(a, 0, 53, round_down) == b + assert mpf_perturb(a, 0, 53, round_ceiling) == b + assert mpf_perturb(a, 1, 53, round_up) == c + assert mpf_perturb(a, 1, 53, round_floor) == c + assert mpf_perturb(a, 1, 53, round_down) == a + assert mpf_perturb(a, 1, 53, round_ceiling) == a + +def test_add_exact(): + ff = from_float + assert mpf_add(ff(3.0), ff(2.5)) == ff(5.5) + assert mpf_add(ff(3.0), ff(-2.5)) == ff(0.5) + assert mpf_add(ff(-3.0), ff(2.5)) == ff(-0.5) + assert mpf_add(ff(-3.0), ff(-2.5)) == ff(-5.5) + assert mpf_sub(mpf_add(fone, ff(1e-100)), fone) == ff(1e-100) + assert mpf_sub(mpf_add(ff(1e-100), fone), fone) == ff(1e-100) + assert mpf_sub(mpf_add(fone, ff(-1e-100)), fone) == ff(-1e-100) + assert mpf_sub(mpf_add(ff(-1e-100), fone), fone) == ff(-1e-100) + assert mpf_add(fone, fzero) == fone + assert mpf_add(fzero, fone) == fone + assert mpf_add(fzero, fzero) == fzero + +def test_long_exponent_shifts(): + mp.dps = 15 + # Check for possible bugs due to exponent arithmetic overflow + # in a C implementation + x = mpf(1) + for p in [32, 64]: + a = ldexp(1,2**(p-1)) + b = ldexp(1,2**p) + c = ldexp(1,2**(p+1)) + d = ldexp(1,-2**(p-1)) + e = ldexp(1,-2**p) + f = ldexp(1,-2**(p+1)) + assert (x+a) == a + assert (x+b) == b + assert (x+c) == c + assert (x+d) == x + assert (x+e) == x + assert (x+f) == x + assert (a+x) == a + assert (b+x) == b + assert (c+x) == c + assert (d+x) == x + assert (e+x) == x + assert (f+x) == x + assert (x-a) == -a + assert (x-b) == -b + assert (x-c) == -c + assert (x-d) == x + assert (x-e) == x + assert (x-f) == x + assert (a-x) == a + assert (b-x) == b + assert (c-x) == c + assert (d-x) == -x + assert (e-x) == -x + assert (f-x) == -x diff --git a/compiler/gdsMill/mpmath/tests/test_calculus.py b/compiler/gdsMill/mpmath/tests/test_calculus.py new file mode 100644 index 00000000..9110cb1c --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_calculus.py @@ -0,0 +1,69 @@ +from mpmath import * + +def test_approximation(): + mp.dps = 15 + f = lambda x: cos(2-2*x)/x + p, err = chebyfit(f, [2, 4], 8, error=True) + assert err < 1e-5 + for i in range(10): + x = 2 + i/5. + assert abs(polyval(p, x) - f(x)) < err + +def test_limits(): + mp.dps = 15 + assert limit(lambda x: (x-sin(x))/x**3, 0).ae(mpf(1)/6) + assert limit(lambda n: (1+1/n)**n, inf).ae(e) + +def test_polyval(): + assert polyval([], 3) == 0 + assert polyval([0], 3) == 0 + assert polyval([5], 3) == 5 + # 4x^3 - 2x + 5 + p = [4, 0, -2, 5] + assert polyval(p,4) == 253 + assert polyval(p,4,derivative=True) == (253, 190) + +def test_polyroots(): + p = polyroots([1,-4]) + assert p[0].ae(4) + p, q = polyroots([1,2,3]) + assert p.ae(-1 - sqrt(2)*j) + assert q.ae(-1 + sqrt(2)*j) + #this is not a real test, it only tests a specific case + assert polyroots([1]) == [] + try: + polyroots([0]) + assert False + except ValueError: + pass + +def test_pade(): + one = mpf(1) + mp.dps = 20 + N = 10 + a = [one] + k = 1 + for i in range(1, N+1): + k *= i + a.append(one/k) + p, q = pade(a, N//2, N//2) + for x in arange(0, 1, 0.1): + r = polyval(p[::-1], x)/polyval(q[::-1], x) + assert(r.ae(exp(x), 1.0e-10)) + mp.dps = 15 + +def test_fourier(): + mp.dps = 15 + c, s = fourier(lambda x: x+1, [-1, 2], 2) + #plot([lambda x: x+1, lambda x: fourierval((c, s), [-1, 2], x)], [-1, 2]) + assert c[0].ae(1.5) + assert c[1].ae(-3*sqrt(3)/(2*pi)) + assert c[2].ae(3*sqrt(3)/(4*pi)) + assert s[0] == 0 + assert s[1].ae(3/(2*pi)) + assert s[2].ae(3/(4*pi)) + assert fourierval((c, s), [-1, 2], 1).ae(1.9134966715663442) + +def test_differint(): + mp.dps = 15 + assert differint(lambda t: t, 2, -0.5).ae(8*sqrt(2/pi)/3) diff --git a/compiler/gdsMill/mpmath/tests/test_compatibility.py b/compiler/gdsMill/mpmath/tests/test_compatibility.py new file mode 100644 index 00000000..8b2b1e9a --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_compatibility.py @@ -0,0 +1,77 @@ +from mpmath import * +from random import seed, randint, random +import math + +# Test compatibility with Python floats, which are +# IEEE doubles (53-bit) + +N = 5000 +seed(1) + +# Choosing exponents between roughly -140, 140 ensures that +# the Python floats don't overflow or underflow +xs = [(random()-1) * 10**randint(-140, 140) for x in range(N)] +ys = [(random()-1) * 10**randint(-140, 140) for x in range(N)] + +# include some equal values +ys[int(N*0.8):] = xs[int(N*0.8):] + +# Detect whether Python is compiled to use 80-bit floating-point +# instructions, in which case the double compatibility test breaks +uses_x87 = -4.1974624032366689e+117 / -8.4657370748010221e-47 \ + == 4.9581771393902231e+163 + +def test_double_compatibility(): + mp.prec = 53 + for x, y in zip(xs, ys): + mpx = mpf(x) + mpy = mpf(y) + assert mpf(x) == x + assert (mpx < mpy) == (x < y) + assert (mpx > mpy) == (x > y) + assert (mpx == mpy) == (x == y) + assert (mpx != mpy) == (x != y) + assert (mpx <= mpy) == (x <= y) + assert (mpx >= mpy) == (x >= y) + assert mpx == mpx + if uses_x87: + mp.prec = 64 + a = mpx + mpy + b = mpx * mpy + c = mpx / mpy + d = mpx % mpy + mp.prec = 53 + assert +a == x + y + assert +b == x * y + assert +c == x / y + assert +d == x % y + else: + assert mpx + mpy == x + y + assert mpx * mpy == x * y + assert mpx / mpy == x / y + assert mpx % mpy == x % y + assert abs(mpx) == abs(x) + assert mpf(repr(x)) == x + assert ceil(mpx) == math.ceil(x) + assert floor(mpx) == math.floor(x) + +def test_sqrt(): + # this fails quite often. it appers to be float + # that rounds the wrong way, not mpf + fail = 0 + mp.prec = 53 + for x in xs: + x = abs(x) + mp.prec = 100 + mp_high = mpf(x)**0.5 + mp.prec = 53 + mp_low = mpf(x)**0.5 + fp = x**0.5 + assert abs(mp_low-mp_high) <= abs(fp-mp_high) + fail += mp_low != fp + assert fail < N/10 + +def test_bugs(): + # particular bugs + assert mpf(4.4408920985006262E-16) < mpf(1.7763568394002505E-15) + assert mpf(-4.4408920985006262E-16) > mpf(-1.7763568394002505E-15) diff --git a/compiler/gdsMill/mpmath/tests/test_convert.py b/compiler/gdsMill/mpmath/tests/test_convert.py new file mode 100644 index 00000000..7790b090 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_convert.py @@ -0,0 +1,186 @@ +import random +from mpmath import * +from mpmath.libmp import * + + +def test_basic_string(): + """ + Test basic string conversion + """ + mp.dps = 15 + assert mpf('3') == mpf('3.0') == mpf('0003.') == mpf('0.03e2') == mpf(3.0) + assert mpf('30') == mpf('30.0') == mpf('00030.') == mpf(30.0) + for i in range(10): + for j in range(10): + assert mpf('%ie%i' % (i,j)) == i * 10**j + assert str(mpf('25000.0')) == '25000.0' + assert str(mpf('2500.0')) == '2500.0' + assert str(mpf('250.0')) == '250.0' + assert str(mpf('25.0')) == '25.0' + assert str(mpf('2.5')) == '2.5' + assert str(mpf('0.25')) == '0.25' + assert str(mpf('0.025')) == '0.025' + assert str(mpf('0.0025')) == '0.0025' + assert str(mpf('0.00025')) == '0.00025' + assert str(mpf('0.000025')) == '2.5e-5' + assert str(mpf(0)) == '0.0' + assert str(mpf('2.5e1000000000000000000000')) == '2.5e+1000000000000000000000' + assert str(mpf('2.6e-1000000000000000000000')) == '2.6e-1000000000000000000000' + assert str(mpf(1.23402834e-15)) == '1.23402834e-15' + assert str(mpf(-1.23402834e-15)) == '-1.23402834e-15' + assert str(mpf(-1.2344e-15)) == '-1.2344e-15' + assert repr(mpf(-1.2344e-15)) == "mpf('-1.2343999999999999e-15')" + +def test_pretty(): + mp.pretty = True + assert repr(mpf(2.5)) == '2.5' + assert repr(mpc(2.5,3.5)) == '(2.5 + 3.5j)' + assert repr(mpi(2.5,3.5)) == '[2.5, 3.5]' + mp.pretty = False + +def test_str_whitespace(): + assert mpf('1.26 ') == 1.26 + +def test_unicode(): + mp.dps = 15 + assert mpf(u'2.76') == 2.76 + assert mpf(u'inf') == inf + +def test_str_format(): + assert to_str(from_float(0.1),15,strip_zeros=False) == '0.100000000000000' + assert to_str(from_float(0.0),15,show_zero_exponent=True) == '0.0e+0' + assert to_str(from_float(0.0),0,show_zero_exponent=True) == '.0e+0' + assert to_str(from_float(0.0),0,show_zero_exponent=False) == '.0' + assert to_str(from_float(0.0),1,show_zero_exponent=True) == '0.0e+0' + assert to_str(from_float(0.0),1,show_zero_exponent=False) == '0.0' + assert to_str(from_float(1.23),3,show_zero_exponent=True) == '1.23e+0' + assert to_str(from_float(1.23456789000000e-2),15,strip_zeros=False,min_fixed=0,max_fixed=0) == '1.23456789000000e-2' + assert to_str(from_float(1.23456789000000e+2),15,strip_zeros=False,min_fixed=0,max_fixed=0) == '1.23456789000000e+2' + assert to_str(from_float(2.1287e14), 15, max_fixed=1000) == '212870000000000.0' + assert to_str(from_float(2.1287e15), 15, max_fixed=1000) == '2128700000000000.0' + assert to_str(from_float(2.1287e16), 15, max_fixed=1000) == '21287000000000000.0' + assert to_str(from_float(2.1287e30), 15, max_fixed=1000) == '2128700000000000000000000000000.0' + +def test_tight_string_conversion(): + mp.dps = 15 + # In an old version, '0.5' wasn't recognized as representing + # an exact binary number and was erroneously rounded up or down + assert from_str('0.5', 10, round_floor) == fhalf + assert from_str('0.5', 10, round_ceiling) == fhalf + +def test_eval_repr_invariant(): + """Test that eval(repr(x)) == x""" + random.seed(123) + for dps in [10, 15, 20, 50, 100]: + mp.dps = dps + for i in xrange(1000): + a = mpf(random.random())**0.5 * 10**random.randint(-100, 100) + assert eval(repr(a)) == a + mp.dps = 15 + +def test_str_bugs(): + mp.dps = 15 + # Decimal rounding used to give the wrong exponent in some cases + assert str(mpf('1e600')) == '1.0e+600' + assert str(mpf('1e10000')) == '1.0e+10000' + +def test_str_prec0(): + assert to_str(from_float(1.234), 0) == '.0e+0' + assert to_str(from_float(1e-15), 0) == '.0e-15' + assert to_str(from_float(1e+15), 0) == '.0e+15' + assert to_str(from_float(-1e-15), 0) == '-.0e-15' + assert to_str(from_float(-1e+15), 0) == '-.0e+15' + +def test_convert_rational(): + mp.dps = 15 + assert from_rational(30, 5, 53, round_nearest) == (0, 3, 1, 2) + assert from_rational(-7, 4, 53, round_nearest) == (1, 7, -2, 3) + assert to_rational((0, 1, -1, 1)) == (1, 2) + +def test_custom_class(): + class mympf: + @property + def _mpf_(self): + return mpf(3.5)._mpf_ + class mympc: + @property + def _mpc_(self): + return mpf(3.5)._mpf_, mpf(2.5)._mpf_ + assert mpf(2) + mympf() == 5.5 + assert mympf() + mpf(2) == 5.5 + assert mpf(mympf()) == 3.5 + assert mympc() + mpc(2) == mpc(5.5, 2.5) + assert mpc(2) + mympc() == mpc(5.5, 2.5) + assert mpc(mympc()) == (3.5+2.5j) + +def test_conversion_methods(): + class SomethingRandom: + pass + class SomethingReal: + def _mpmath_(self, prec, rounding): + return mp.make_mpf(from_str('1.3', prec, rounding)) + class SomethingComplex: + def _mpmath_(self, prec, rounding): + return mp.make_mpc((from_str('1.3', prec, rounding), \ + from_str('1.7', prec, rounding))) + x = mpf(3) + z = mpc(3) + a = SomethingRandom() + y = SomethingReal() + w = SomethingComplex() + for d in [15, 45]: + mp.dps = d + assert (x+y).ae(mpf('4.3')) + assert (y+x).ae(mpf('4.3')) + assert (x+w).ae(mpc('4.3', '1.7')) + assert (w+x).ae(mpc('4.3', '1.7')) + assert (z+y).ae(mpc('4.3')) + assert (y+z).ae(mpc('4.3')) + assert (z+w).ae(mpc('4.3', '1.7')) + assert (w+z).ae(mpc('4.3', '1.7')) + x-y; y-x; x-w; w-x; z-y; y-z; z-w; w-z + x*y; y*x; x*w; w*x; z*y; y*z; z*w; w*z + x/y; y/x; x/w; w/x; z/y; y/z; z/w; w/z + x**y; y**x; x**w; w**x; z**y; y**z; z**w; w**z + x==y; y==x; x==w; w==x; z==y; y==z; z==w; w==z + mp.dps = 15 + assert x.__add__(a) is NotImplemented + assert x.__radd__(a) is NotImplemented + assert x.__lt__(a) is NotImplemented + assert x.__gt__(a) is NotImplemented + assert x.__le__(a) is NotImplemented + assert x.__ge__(a) is NotImplemented + assert x.__eq__(a) is NotImplemented + assert x.__ne__(a) is NotImplemented + # implementation detail + if hasattr(x, "__cmp__"): + assert x.__cmp__(a) is NotImplemented + assert x.__sub__(a) is NotImplemented + assert x.__rsub__(a) is NotImplemented + assert x.__mul__(a) is NotImplemented + assert x.__rmul__(a) is NotImplemented + assert x.__div__(a) is NotImplemented + assert x.__rdiv__(a) is NotImplemented + assert x.__mod__(a) is NotImplemented + assert x.__rmod__(a) is NotImplemented + assert x.__pow__(a) is NotImplemented + assert x.__rpow__(a) is NotImplemented + assert z.__add__(a) is NotImplemented + assert z.__radd__(a) is NotImplemented + assert z.__eq__(a) is NotImplemented + assert z.__ne__(a) is NotImplemented + assert z.__sub__(a) is NotImplemented + assert z.__rsub__(a) is NotImplemented + assert z.__mul__(a) is NotImplemented + assert z.__rmul__(a) is NotImplemented + assert z.__div__(a) is NotImplemented + assert z.__rdiv__(a) is NotImplemented + assert z.__pow__(a) is NotImplemented + assert z.__rpow__(a) is NotImplemented + +def test_mpmathify(): + assert mpmathify('1/2') == 0.5 + assert mpmathify('(1.0+1.0j)') == mpc(1, 1) + assert mpmathify('(1.2e-10 - 3.4e5j)') == mpc('1.2e-10', '-3.4e5') + assert mpmathify('1j') == mpc(1j) + diff --git a/compiler/gdsMill/mpmath/tests/test_diff.py b/compiler/gdsMill/mpmath/tests/test_diff.py new file mode 100644 index 00000000..a9531512 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_diff.py @@ -0,0 +1,20 @@ +from mpmath import * + +def test_diff(): + assert diff(log, 2.0, n=0).ae(log(2)) + assert diff(cos, 1.0).ae(-sin(1)) + assert diff(abs, 0.0) == 0 + assert diff(abs, 0.0, direction=1) == 1 + assert diff(abs, 0.0, direction=-1) == -1 + assert diff(exp, 1.0).ae(e) + assert diff(exp, 1.0, n=5).ae(e) + assert diff(exp, 2.0, n=5, direction=3*j).ae(e**2) + assert diff(lambda x: x**2, 3.0, method='quad').ae(6) + assert diff(lambda x: 3+x**5, 3.0, n=2, method='quad').ae(540) + assert diff(lambda x: 3+x**5, 3.0, n=2, method='step').ae(540) + assert diffun(sin)(2).ae(cos(2)) + assert diffun(sin, n=2)(2).ae(-sin(2)) + +def test_taylor(): + # Easy to test since the coefficients are exact in floating-point + assert taylor(sqrt, 1, 4) == [1, 0.5, -0.125, 0.0625, -0.0390625] diff --git a/compiler/gdsMill/mpmath/tests/test_division.py b/compiler/gdsMill/mpmath/tests/test_division.py new file mode 100644 index 00000000..565dac25 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_division.py @@ -0,0 +1,143 @@ +from mpmath.libmp import * +from mpmath import mpf, mp + +from random import randint, choice, seed + +all_modes = [round_floor, round_ceiling, round_down, round_up, round_nearest] + +fb = from_bstr +fi = from_int +ff = from_float + + +def test_div_1_3(): + a = fi(1) + b = fi(3) + c = fi(-1) + + # floor rounds down, ceiling rounds up + assert mpf_div(a, b, 7, round_floor) == fb('0.01010101') + assert mpf_div(a, b, 7, round_ceiling) == fb('0.01010110') + assert mpf_div(a, b, 7, round_down) == fb('0.01010101') + assert mpf_div(a, b, 7, round_up) == fb('0.01010110') + assert mpf_div(a, b, 7, round_nearest) == fb('0.01010101') + + # floor rounds up, ceiling rounds down + assert mpf_div(c, b, 7, round_floor) == fb('-0.01010110') + assert mpf_div(c, b, 7, round_ceiling) == fb('-0.01010101') + assert mpf_div(c, b, 7, round_down) == fb('-0.01010101') + assert mpf_div(c, b, 7, round_up) == fb('-0.01010110') + assert mpf_div(c, b, 7, round_nearest) == fb('-0.01010101') + +def test_mpf_divi_1_3(): + a = 1 + b = fi(3) + c = -1 + assert mpf_rdiv_int(a, b, 7, round_floor) == fb('0.01010101') + assert mpf_rdiv_int(a, b, 7, round_ceiling) == fb('0.01010110') + assert mpf_rdiv_int(a, b, 7, round_down) == fb('0.01010101') + assert mpf_rdiv_int(a, b, 7, round_up) == fb('0.01010110') + assert mpf_rdiv_int(a, b, 7, round_nearest) == fb('0.01010101') + assert mpf_rdiv_int(c, b, 7, round_floor) == fb('-0.01010110') + assert mpf_rdiv_int(c, b, 7, round_ceiling) == fb('-0.01010101') + assert mpf_rdiv_int(c, b, 7, round_down) == fb('-0.01010101') + assert mpf_rdiv_int(c, b, 7, round_up) == fb('-0.01010110') + assert mpf_rdiv_int(c, b, 7, round_nearest) == fb('-0.01010101') + + +def test_div_300(): + + q = fi(1000000) + a = fi(300499999) # a/q is a little less than a half-integer + b = fi(300500000) # b/q exactly a half-integer + c = fi(300500001) # c/q is a little more than a half-integer + + # Check nearest integer rounding (prec=9 as 2**8 < 300 < 2**9) + + assert mpf_div(a, q, 9, round_down) == fi(300) + assert mpf_div(b, q, 9, round_down) == fi(300) + assert mpf_div(c, q, 9, round_down) == fi(300) + assert mpf_div(a, q, 9, round_up) == fi(301) + assert mpf_div(b, q, 9, round_up) == fi(301) + assert mpf_div(c, q, 9, round_up) == fi(301) + + # Nearest even integer is down + assert mpf_div(a, q, 9, round_nearest) == fi(300) + assert mpf_div(b, q, 9, round_nearest) == fi(300) + assert mpf_div(c, q, 9, round_nearest) == fi(301) + + # Nearest even integer is up + a = fi(301499999) + b = fi(301500000) + c = fi(301500001) + assert mpf_div(a, q, 9, round_nearest) == fi(301) + assert mpf_div(b, q, 9, round_nearest) == fi(302) + assert mpf_div(c, q, 9, round_nearest) == fi(302) + + +def test_tight_integer_division(): + # Test that integer division at tightest possible precision is exact + N = 100 + seed(1) + for i in range(N): + a = choice([1, -1]) * randint(1, 1< Q_LIM the theta functions raise ValueError + mp.dps = 30 + mp.dps += 30 + q = mpf(6)/10 - one/10**6 - mpf(8)/10 * j + mp.dps -= 30 + # Mathematica run first + # N[EllipticTheta[3, 1, 6/10 - 10^-6 - 8/10*I], 2000] + # then it works: + # N[EllipticTheta[3, 1, 6/10 - 10^-6 - 8/10*I], 30] + res = mpf('32.0031009628901652627099524264') + \ + mpf('16.6153027998236087899308935624') * j + result = jtheta(3, 1, q) + # check that for abs(q) > Q_LIM a ValueError exception is raised + mp.dps += 30 + q = mpf(6)/10 - one/10**7 - mpf(8)/10 * j + mp.dps -= 30 + try: + result = jtheta(3, 1, q) + except ValueError: + pass + else: + assert(False) + + # bug reported in issue39 + mp.dps = 100 + z = (1+j)/3 + q = mpf(368983957219251)/10**15 + mpf(636363636363636)/10**15 * j + # Mathematica N[EllipticTheta[1, z, q], 35] + res = mpf('2.4439389177990737589761828991467471') + \ + mpf('0.5446453005688226915290954851851490') *j + mp.dps = 30 + result = jtheta(1, z, q) + assert(result.ae(res)) + mp.dps = 80 + z = 3 + 4*j + q = 0.5 + 0.5*j + r1 = jtheta(1, z, q) + mp.dps = 15 + r2 = jtheta(1, z, q) + assert r1.ae(r2) + mp.dps = 80 + z = 3 + j + q1 = exp(j*3) + # longer test + # for n in range(1, 6) + for n in range(1, 2): + mp.dps = 80 + q = q1*(1 - mpf(1)/10**n) + r1 = jtheta(1, z, q) + mp.dps = 15 + r2 = jtheta(1, z, q) + assert r1.ae(r2) + mp.dps = 15 + # issue 39 about high derivatives + assert jtheta(3, 4.5, 0.25, 9).ae(1359.04892680683) + assert jtheta(3, 4.5, 0.25, 50).ae(-6.14832772630905e+33) + mp.dps = 50 + r = jtheta(3, 4.5, 0.25, 9) + assert r.ae('1359.048926806828939547859396600218966947753213803') + r = jtheta(3, 4.5, 0.25, 50) + assert r.ae('-6148327726309051673317975084654262.4119215720343656') + +def test_jtheta_identities(): + """ + Tests the some of the jacobi identidies found in Abramowitz, + Sec. 16.28, Pg. 576. The identities are tested to 1 part in 10^98. + """ + mp.dps = 110 + eps1 = ldexp(eps, 30) + + for i in range(10): + qstring = str(random.random()) + q = mpf(qstring) + + zstring = str(10*random.random()) + z = mpf(zstring) + # Abramowitz 16.28.1 + # v_1(z, q)**2 * v_4(0, q)**2 = v_3(z, q)**2 * v_2(0, q)**2 + # - v_2(z, q)**2 * v_3(0, q)**2 + term1 = (jtheta(1, z, q)**2) * (jtheta(4, zero, q)**2) + term2 = (jtheta(3, z, q)**2) * (jtheta(2, zero, q)**2) + term3 = (jtheta(2, z, q)**2) * (jtheta(3, zero, q)**2) + equality = term1 - term2 + term3 + assert(equality.ae(0, eps1)) + + zstring = str(100*random.random()) + z = mpf(zstring) + # Abramowitz 16.28.2 + # v_2(z, q)**2 * v_4(0, q)**2 = v_4(z, q)**2 * v_2(0, q)**2 + # - v_1(z, q)**2 * v_3(0, q)**2 + term1 = (jtheta(2, z, q)**2) * (jtheta(4, zero, q)**2) + term2 = (jtheta(4, z, q)**2) * (jtheta(2, zero, q)**2) + term3 = (jtheta(1, z, q)**2) * (jtheta(3, zero, q)**2) + equality = term1 - term2 + term3 + assert(equality.ae(0, eps1)) + + # Abramowitz 16.28.3 + # v_3(z, q)**2 * v_4(0, q)**2 = v_4(z, q)**2 * v_3(0, q)**2 + # - v_1(z, q)**2 * v_2(0, q)**2 + term1 = (jtheta(3, z, q)**2) * (jtheta(4, zero, q)**2) + term2 = (jtheta(4, z, q)**2) * (jtheta(3, zero, q)**2) + term3 = (jtheta(1, z, q)**2) * (jtheta(2, zero, q)**2) + equality = term1 - term2 + term3 + assert(equality.ae(0, eps1)) + + # Abramowitz 16.28.4 + # v_4(z, q)**2 * v_4(0, q)**2 = v_3(z, q)**2 * v_3(0, q)**2 + # - v_2(z, q)**2 * v_2(0, q)**2 + term1 = (jtheta(4, z, q)**2) * (jtheta(4, zero, q)**2) + term2 = (jtheta(3, z, q)**2) * (jtheta(3, zero, q)**2) + term3 = (jtheta(2, z, q)**2) * (jtheta(2, zero, q)**2) + equality = term1 - term2 + term3 + assert(equality.ae(0, eps1)) + + # Abramowitz 16.28.5 + # v_2(0, q)**4 + v_4(0, q)**4 == v_3(0, q)**4 + term1 = (jtheta(2, zero, q))**4 + term2 = (jtheta(4, zero, q))**4 + term3 = (jtheta(3, zero, q))**4 + equality = term1 + term2 - term3 + assert(equality.ae(0, eps1)) + mp.dps = 15 + +def test_jtheta_complex(): + mp.dps = 30 + z = mpf(1)/4 + j/8 + q = mpf(1)/3 + j/7 + # Mathematica N[EllipticTheta[1, 1/4 + I/8, 1/3 + I/7], 35] + res = mpf('0.31618034835986160705729105731678285') + \ + mpf('0.07542013825835103435142515194358975') * j + r = jtheta(1, z, q) + assert(mpc_ae(r, res)) + + # Mathematica N[EllipticTheta[2, 1/4 + I/8, 1/3 + I/7], 35] + res = mpf('1.6530986428239765928634711417951828') + \ + mpf('0.2015344864707197230526742145361455') * j + r = jtheta(2, z, q) + assert(mpc_ae(r, res)) + + # Mathematica N[EllipticTheta[3, 1/4 + I/8, 1/3 + I/7], 35] + res = mpf('1.6520564411784228184326012700348340') + \ + mpf('0.1998129119671271328684690067401823') * j + r = jtheta(3, z, q) + assert(mpc_ae(r, res)) + + # Mathematica N[EllipticTheta[4, 1/4 + I/8, 1/3 + I/7], 35] + res = mpf('0.37619082382228348252047624089973824') - \ + mpf('0.15623022130983652972686227200681074') * j + r = jtheta(4, z, q) + assert(mpc_ae(r, res)) + + # check some theta function identities + mp.dos = 100 + z = mpf(1)/4 + j/8 + q = mpf(1)/3 + j/7 + mp.dps += 10 + a = [0,0, jtheta(2, 0, q), jtheta(3, 0, q), jtheta(4, 0, q)] + t = [0, jtheta(1, z, q), jtheta(2, z, q), jtheta(3, z, q), jtheta(4, z, q)] + r = [(t[2]*a[4])**2 - (t[4]*a[2])**2 + (t[1] *a[3])**2, + (t[3]*a[4])**2 - (t[4]*a[3])**2 + (t[1] *a[2])**2, + (t[1]*a[4])**2 - (t[3]*a[2])**2 + (t[2] *a[3])**2, + (t[4]*a[4])**2 - (t[3]*a[3])**2 + (t[2] *a[2])**2, + a[2]**4 + a[4]**4 - a[3]**4] + mp.dps -= 10 + for x in r: + assert(mpc_ae(x, mpc(0))) + mp.dps = 15 + +def test_djtheta(): + mp.dps = 30 + + z = one/7 + j/3 + q = one/8 + j/5 + # Mathematica N[EllipticThetaPrime[1, 1/7 + I/3, 1/8 + I/5], 35] + res = mpf('1.5555195883277196036090928995803201') - \ + mpf('0.02439761276895463494054149673076275') * j + result = jtheta(1, z, q, 1) + assert(mpc_ae(result, res)) + + # Mathematica N[EllipticThetaPrime[2, 1/7 + I/3, 1/8 + I/5], 35] + res = mpf('0.19825296689470982332701283509685662') - \ + mpf('0.46038135182282106983251742935250009') * j + result = jtheta(2, z, q, 1) + assert(mpc_ae(result, res)) + + # Mathematica N[EllipticThetaPrime[3, 1/7 + I/3, 1/8 + I/5], 35] + res = mpf('0.36492498415476212680896699407390026') - \ + mpf('0.57743812698666990209897034525640369') * j + result = jtheta(3, z, q, 1) + assert(mpc_ae(result, res)) + + # Mathematica N[EllipticThetaPrime[4, 1/7 + I/3, 1/8 + I/5], 35] + res = mpf('-0.38936892528126996010818803742007352') + \ + mpf('0.66549886179739128256269617407313625') * j + result = jtheta(4, z, q, 1) + assert(mpc_ae(result, res)) + + for i in range(10): + q = (one*random.random() + j*random.random())/2 + # identity in Wittaker, Watson &21.41 + a = jtheta(1, 0, q, 1) + b = jtheta(2, 0, q)*jtheta(3, 0, q)*jtheta(4, 0, q) + assert(a.ae(b)) + + # test higher derivatives + mp.dps = 20 + for q,z in [(one/3, one/5), (one/3 + j/8, one/5), + (one/3, one/5 + j/8), (one/3 + j/7, one/5 + j/8)]: + for n in [1, 2, 3, 4]: + r = jtheta(n, z, q, 2) + r1 = diff(lambda zz: jtheta(n, zz, q), z, n=2) + assert r.ae(r1) + r = jtheta(n, z, q, 3) + r1 = diff(lambda zz: jtheta(n, zz, q), z, n=3) + assert r.ae(r1) + + # identity in Wittaker, Watson &21.41 + q = one/3 + z = zero + a = [0]*5 + a[1] = jtheta(1, z, q, 3)/jtheta(1, z, q, 1) + for n in [2,3,4]: + a[n] = jtheta(n, z, q, 2)/jtheta(n, z, q) + equality = a[2] + a[3] + a[4] - a[1] + assert(equality.ae(0)) + mp.dps = 15 + +def test_jsn(): + """ + Test some special cases of the sn(z, q) function. + """ + mp.dps = 100 + + # trival case + result = jsn(zero, zero) + assert(result == zero) + + # Abramowitz Table 16.5 + # + # sn(0, m) = 0 + + for i in range(10): + qstring = str(random.random()) + q = mpf(qstring) + + equality = jsn(zero, q) + assert(equality.ae(0)) + + # Abramowitz Table 16.6.1 + # + # sn(z, 0) = sin(z), m == 0 + # + # sn(z, 1) = tanh(z), m == 1 + # + # It would be nice to test these, but I find that they run + # in to numerical trouble. I'm currently treating as a boundary + # case for sn function. + + mp.dps = 25 + arg = one/10 + #N[JacobiSN[1/10, 2^-100], 25] + res = mpf('0.09983341664682815230681420') + m = ldexp(one, -100) + result = jsn(arg, m) + assert(result.ae(res)) + + # N[JacobiSN[1/10, 1/10], 25] + res = mpf('0.09981686718599080096451168') + result = jsn(arg, arg) + assert(result.ae(res)) + mp.dps = 15 + +def test_jcn(): + """ + Test some special cases of the cn(z, q) function. + """ + mp.dps = 100 + + # Abramowitz Table 16.5 + # cn(0, q) = 1 + qstring = str(random.random()) + q = mpf(qstring) + cn = jcn(zero, q) + assert(cn.ae(one)) + + # Abramowitz Table 16.6.2 + # + # cn(u, 0) = cos(u), m == 0 + # + # cn(u, 1) = sech(z), m == 1 + # + # It would be nice to test these, but I find that they run + # in to numerical trouble. I'm currently treating as a boundary + # case for cn function. + + mp.dps = 25 + arg = one/10 + m = ldexp(one, -100) + #N[JacobiCN[1/10, 2^-100], 25] + res = mpf('0.9950041652780257660955620') + result = jcn(arg, m) + assert(result.ae(res)) + + # N[JacobiCN[1/10, 1/10], 25] + res = mpf('0.9950058256237368748520459') + result = jcn(arg, arg) + assert(result.ae(res)) + mp.dps = 15 + +def test_jdn(): + """ + Test some special cases of the dn(z, q) function. + """ + mp.dps = 100 + + # Abramowitz Table 16.5 + # dn(0, q) = 1 + mstring = str(random.random()) + m = mpf(mstring) + + dn = jdn(zero, m) + assert(dn.ae(one)) + + mp.dps = 25 + # N[JacobiDN[1/10, 1/10], 25] + res = mpf('0.9995017055025556219713297') + arg = one/10 + result = jdn(arg, arg) + assert(result.ae(res)) + mp.dps = 15 + + +def test_sn_cn_dn_identities(): + """ + Tests the some of the jacobi elliptic function identities found + on Mathworld. Haven't found in Abramowitz. + """ + mp.dps = 100 + N = 5 + for i in range(N): + qstring = str(random.random()) + q = mpf(qstring) + zstring = str(100*random.random()) + z = mpf(zstring) + + # MathWorld + # sn(z, q)**2 + cn(z, q)**2 == 1 + term1 = jsn(z, q)**2 + term2 = jcn(z, q)**2 + equality = one - term1 - term2 + assert(equality.ae(0)) + + # MathWorld + # k**2 * sn(z, m)**2 + dn(z, m)**2 == 1 + for i in range(N): + mstring = str(random.random()) + m = mpf(qstring) + k = m.sqrt() + zstring = str(10*random.random()) + z = mpf(zstring) + term1 = k**2 * jsn(z, m)**2 + term2 = jdn(z, m)**2 + equality = one - term1 - term2 + assert(equality.ae(0)) + + + for i in range(N): + mstring = str(random.random()) + m = mpf(mstring) + k = m.sqrt() + zstring = str(random.random()) + z = mpf(zstring) + + # MathWorld + # k**2 * cn(z, m)**2 + (1 - k**2) = dn(z, m)**2 + term1 = k**2 * jcn(z, m)**2 + term2 = 1 - k**2 + term3 = jdn(z, m)**2 + equality = term3 - term1 - term2 + assert(equality.ae(0)) + + K = ellipk(k**2) + # Abramowitz Table 16.5 + # sn(K, m) = 1; K is K(k), first complete elliptic integral + r = jsn(K, m) + assert(r.ae(one)) + + # Abramowitz Table 16.5 + # cn(K, q) = 0; K is K(k), first complete elliptic integral + equality = jcn(K, m) + assert(equality.ae(0)) + + # Abramowitz Table 16.6.3 + # dn(z, 0) = 1, m == 0 + z = m + value = jdn(z, zero) + assert(value.ae(one)) + + mp.dps = 15 + +def test_sn_cn_dn_complex(): + mp.dps = 30 + # N[JacobiSN[1/4 + I/8, 1/3 + I/7], 35] in Mathematica + res = mpf('0.2495674401066275492326652143537') + \ + mpf('0.12017344422863833381301051702823') * j + u = mpf(1)/4 + j/8 + m = mpf(1)/3 + j/7 + r = jsn(u, m) + assert(mpc_ae(r, res)) + + #N[JacobiCN[1/4 + I/8, 1/3 + I/7], 35] + res = mpf('0.9762691700944007312693721148331') - \ + mpf('0.0307203994181623243583169154824')*j + r = jcn(u, m) + #assert r.real.ae(res.real) + #assert r.imag.ae(res.imag) + assert(mpc_ae(r, res)) + + #N[JacobiDN[1/4 + I/8, 1/3 + I/7], 35] + res = mpf('0.99639490163039577560547478589753039') - \ + mpf('0.01346296520008176393432491077244994')*j + r = jdn(u, m) + assert(mpc_ae(r, res)) + mp.dps = 15 diff --git a/compiler/gdsMill/mpmath/tests/test_fp.py b/compiler/gdsMill/mpmath/tests/test_fp.py new file mode 100644 index 00000000..f65eb4de --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_fp.py @@ -0,0 +1,1666 @@ +""" +Easy-to-use test-generating code: + +cases = ''' +exp 2.25 +log 2.25 +''' + +from mpmath import * +mp.dps = 20 +for test in cases.splitlines(): + if not test: + continue + words = test.split() + fname = words[0] + args = words[1:] + argstr = ", ".join(args) + testline = "%s(%s)" % (fname, argstr) + ans = str(eval(testline)) + print " assert ae(fp.%s, %s)" % (testline, ans) + +""" + +from mpmath import fp + +def ae(x, y, tol=1e-12): + if x == y: + return True + return abs(x-y) <= tol*abs(y) + +def test_fp_number_parts(): + assert ae(fp.arg(3), 0.0) + assert ae(fp.arg(-3), 3.1415926535897932385) + assert ae(fp.arg(3j), 1.5707963267948966192) + assert ae(fp.arg(-3j), -1.5707963267948966192) + assert ae(fp.arg(2+3j), 0.98279372324732906799) + assert ae(fp.arg(-1-1j), -2.3561944901923449288) + assert ae(fp.re(2.5), 2.5) + assert ae(fp.re(2.5+3j), 2.5) + assert ae(fp.im(2.5), 0.0) + assert ae(fp.im(2.5+3j), 3.0) + assert ae(fp.floor(2.5), 2.0) + assert ae(fp.floor(2), 2.0) + assert ae(fp.floor(2.0+0j), (2.0 + 0.0j)) + assert ae(fp.floor(-1.5-0.5j), (-2.0 - 1.0j)) + assert ae(fp.ceil(2.5), 3.0) + assert ae(fp.ceil(2), 2.0) + assert ae(fp.ceil(2.0+0j), (2.0 + 0.0j)) + assert ae(fp.ceil(-1.5-0.5j), (-1.0 + 0.0j)) + +def test_fp_cospi_sinpi(): + assert ae(fp.sinpi(0), 0.0) + assert ae(fp.sinpi(0.25), 0.7071067811865475244) + assert ae(fp.sinpi(0.5), 1.0) + assert ae(fp.sinpi(0.75), 0.7071067811865475244) + assert ae(fp.sinpi(1), 0.0) + assert ae(fp.sinpi(1.25), -0.7071067811865475244) + assert ae(fp.sinpi(1.5), -1.0) + assert ae(fp.sinpi(1.75), -0.7071067811865475244) + assert ae(fp.sinpi(2), 0.0) + assert ae(fp.sinpi(2.25), 0.7071067811865475244) + assert ae(fp.sinpi(0+3j), (0.0 + 6195.8238636085899556j)) + assert ae(fp.sinpi(0.25+3j), (4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.sinpi(0.5+3j), (6195.8239443081075259 + 0.0j)) + assert ae(fp.sinpi(0.75+3j), (4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.sinpi(1+3j), (0.0 - 6195.8238636085899556j)) + assert ae(fp.sinpi(1.25+3j), (-4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.sinpi(1.5+3j), (-6195.8239443081075259 + 0.0j)) + assert ae(fp.sinpi(1.75+3j), (-4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.sinpi(2+3j), (0.0 + 6195.8238636085899556j)) + assert ae(fp.sinpi(2.25+3j), (4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.sinpi(-0.75), -0.7071067811865475244) + assert ae(fp.sinpi(-1e-10), -3.1415926535897933529e-10) + assert ae(fp.sinpi(1e-10), 3.1415926535897933529e-10) + assert ae(fp.sinpi(1e-10+1e-10j), (3.141592653589793353e-10 + 3.1415926535897933528e-10j)) + assert ae(fp.sinpi(1e-10-1e-10j), (3.141592653589793353e-10 - 3.1415926535897933528e-10j)) + assert ae(fp.sinpi(-1e-10+1e-10j), (-3.141592653589793353e-10 + 3.1415926535897933528e-10j)) + assert ae(fp.sinpi(-1e-10-1e-10j), (-3.141592653589793353e-10 - 3.1415926535897933528e-10j)) + assert ae(fp.cospi(0), 1.0) + assert ae(fp.cospi(0.25), 0.7071067811865475244) + assert ae(fp.cospi(0.5), 0.0) + assert ae(fp.cospi(0.75), -0.7071067811865475244) + assert ae(fp.cospi(1), -1.0) + assert ae(fp.cospi(1.25), -0.7071067811865475244) + assert ae(fp.cospi(1.5), 0.0) + assert ae(fp.cospi(1.75), 0.7071067811865475244) + assert ae(fp.cospi(2), 1.0) + assert ae(fp.cospi(2.25), 0.7071067811865475244) + assert ae(fp.cospi(0+3j), (6195.8239443081075259 + 0.0j)) + assert ae(fp.cospi(0.25+3j), (4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.cospi(0.5+3j), (0.0 - 6195.8238636085899556j)) + assert ae(fp.cospi(0.75+3j), (-4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.cospi(1+3j), (-6195.8239443081075259 + 0.0j)) + assert ae(fp.cospi(1.25+3j), (-4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.cospi(1.5+3j), (0.0 + 6195.8238636085899556j)) + assert ae(fp.cospi(1.75+3j), (4381.1091260582448033 + 4381.1090689950686908j)) + assert ae(fp.cospi(2+3j), (6195.8239443081075259 + 0.0j)) + assert ae(fp.cospi(2.25+3j), (4381.1091260582448033 - 4381.1090689950686908j)) + assert ae(fp.cospi(-0.75), -0.7071067811865475244) + assert ae(fp.sinpi(-0.7), -0.80901699437494750611) + assert ae(fp.cospi(-0.7), -0.5877852522924730163) + assert ae(fp.cospi(-3+2j), (-267.74676148374822225 + 0.0j)) + assert ae(fp.sinpi(-3+2j), (0.0 - 267.74489404101651426j)) + assert ae(fp.sinpi(-0.7+2j), (-216.6116802292079471 - 157.37650009392034693j)) + assert ae(fp.cospi(-0.7+2j), (-157.37759774921754565 + 216.61016943630197336j)) + +def test_fp_expj(): + assert ae(fp.expj(0), (1.0 + 0.0j)) + assert ae(fp.expj(1), (0.5403023058681397174 + 0.84147098480789650665j)) + assert ae(fp.expj(2), (-0.416146836547142387 + 0.9092974268256816954j)) + assert ae(fp.expj(0.75), (0.73168886887382088631 + 0.68163876002333416673j)) + assert ae(fp.expj(2+3j), (-0.020718731002242879378 + 0.045271253156092975488j)) + assert ae(fp.expjpi(0), (1.0 + 0.0j)) + assert ae(fp.expjpi(1), (-1.0 + 0.0j)) + assert ae(fp.expjpi(2), (1.0 + 0.0j)) + assert ae(fp.expjpi(0.75), (-0.7071067811865475244 + 0.7071067811865475244j)) + assert ae(fp.expjpi(2+3j), (0.000080699517570304599239 + 0.0j)) + +def test_fp_bernoulli(): + assert ae(fp.bernoulli(0), 1.0) + assert ae(fp.bernoulli(1), -0.5) + assert ae(fp.bernoulli(2), 0.16666666666666666667) + assert ae(fp.bernoulli(10), 0.075757575757575757576) + assert ae(fp.bernoulli(11), 0.0) + +def test_fp_gamma(): + assert ae(fp.gamma(1), 1.0) + assert ae(fp.gamma(1.5), 0.88622692545275801365) + assert ae(fp.gamma(10), 362880.0) + assert ae(fp.gamma(-0.5), -3.5449077018110320546) + assert ae(fp.gamma(-7.1), 0.0016478244570263333622) + assert ae(fp.gamma(12.3), 83385367.899970000963) + assert ae(fp.gamma(2+0j), (1.0 + 0.0j)) + assert ae(fp.gamma(-2.5+0j), (-0.94530872048294188123 + 0.0j)) + assert ae(fp.gamma(3+4j), (0.0052255384713692141947 - 0.17254707929430018772j)) + assert ae(fp.gamma(-3-4j), (0.00001460997305874775607 - 0.000020760733311509070396j)) + assert ae(fp.fac(0), 1.0) + assert ae(fp.fac(1), 1.0) + assert ae(fp.fac(20), 2432902008176640000.0) + assert ae(fp.fac(-3.5), -0.94530872048294188123) + assert ae(fp.fac(2+3j), (-0.44011340763700171113 - 0.06363724312631702183j)) + assert ae(fp.loggamma(1.0), 0.0) + assert ae(fp.loggamma(2.0), 0.0) + assert ae(fp.loggamma(3.0), 0.69314718055994530942) + assert ae(fp.loggamma(7.25), 7.0521854507385394449) + assert ae(fp.loggamma(1000.0), 5905.2204232091812118) + assert ae(fp.loggamma(1e50), 1.1412925464970229298e+52) + assert ae(fp.loggamma(1e25+1e25j), (5.6125802751733671621e+26 + 5.7696599078528568383e+26j)) + assert ae(fp.loggamma(3+4j), (-1.7566267846037841105 + 4.7426644380346579282j)) + assert ae(fp.loggamma(-0.5), (1.2655121234846453965 - 3.1415926535897932385j)) + assert ae(fp.loggamma(-1.25), (1.3664317612369762346 - 6.2831853071795864769j)) + assert ae(fp.loggamma(-2.75), (0.0044878975359557733115 - 9.4247779607693797154j)) + assert ae(fp.loggamma(-3.5), (-1.3090066849930420464 - 12.566370614359172954j)) + assert ae(fp.loggamma(-4.5), (-2.8130840817693161197 - 15.707963267948966192j)) + assert ae(fp.loggamma(-2+3j), (-6.776523813485657093 - 4.568791367260286402j)) + assert ae(fp.loggamma(-1000.3), (-5912.8440347785205041 - 3144.7342462433830317j)) + assert ae(fp.loggamma(-100-100j), (-632.35117666833135562 - 158.37641469650352462j)) + assert ae(fp.loggamma(1e-10), 23.025850929882735237) + assert ae(fp.loggamma(-1e-10), (23.02585092999817837 - 3.1415926535897932385j)) + assert ae(fp.loggamma(1e-10j), (23.025850929940456804 - 1.5707963268526181857j)) + assert ae(fp.loggamma(1e-10j-1e-10), (22.679277339718205716 - 2.3561944902500664954j)) + +def test_fp_psi(): + assert ae(fp.psi(0, 3.7), 1.1671535393615114409) + assert ae(fp.psi(0, 0.5), -1.9635100260214234794) + assert ae(fp.psi(0, 1), -0.57721566490153286061) + assert ae(fp.psi(0, -2.5), 1.1031566406452431872) + assert ae(fp.psi(0, 12.9), 2.5179671503279156347) + assert ae(fp.psi(0, 100), 4.6001618527380874002) + assert ae(fp.psi(0, 2500.3), 7.8239660143238547877) + assert ae(fp.psi(0, 1e40), 92.103403719761827391) + assert ae(fp.psi(0, 1e200), 460.51701859880913677) + assert ae(fp.psi(0, 3.7+0j), (1.1671535393615114409 + 0.0j)) + assert ae(fp.psi(1, 3), 0.39493406684822643647) + assert ae(fp.psi(3, 2+3j), (-0.05383196209159972116 + 0.0076890935247364805218j)) + assert ae(fp.psi(4, -0.5+1j), (1.2719531355492328195 - 18.211833410936276774j)) + assert ae(fp.harmonic(0), 0.0) + assert ae(fp.harmonic(1), 1.0) + assert ae(fp.harmonic(2), 1.5) + assert ae(fp.harmonic(100), 5.1873775176396202608) + assert ae(fp.harmonic(-2.5), 1.2803723055467760478) + assert ae(fp.harmonic(2+3j), (1.9390425294578375875 + 0.87336044981834544043j)) + assert ae(fp.harmonic(-5-4j), (2.3725754822349437733 - 2.4160904444801621j)) + +def test_fp_zeta(): + assert ae(fp.zeta(1e100), 1.0) + assert ae(fp.zeta(3), 1.2020569031595942854) + assert ae(fp.zeta(2+0j), (1.6449340668482264365 + 0.0j)) + assert ae(fp.zeta(0.93), -13.713619351638164784) + assert ae(fp.zeta(1.74), 1.9796863545771774095) + assert ae(fp.zeta(0.0), -0.5) + assert ae(fp.zeta(-1.0), -0.083333333333333333333) + assert ae(fp.zeta(-2.0), 0.0) + assert ae(fp.zeta(-3.0), 0.0083333333333333333333) + assert ae(fp.zeta(-500.0), 0.0) + assert ae(fp.zeta(-7.4), 0.0036537321227995882447) + assert ae(fp.zeta(2.1), 1.5602165335033620158) + assert ae(fp.zeta(26.9), 1.0000000079854809935) + assert ae(fp.zeta(26), 1.0000000149015548284) + assert ae(fp.zeta(27), 1.0000000074507117898) + assert ae(fp.zeta(28), 1.0000000037253340248) + assert ae(fp.zeta(27.1), 1.000000006951755045) + assert ae(fp.zeta(32.7), 1.0000000001433243232) + assert ae(fp.zeta(100), 1.0) + assert ae(fp.altzeta(3.5), 0.92755357777394803511) + assert ae(fp.altzeta(1), 0.69314718055994530942) + assert ae(fp.altzeta(2), 0.82246703342411321824) + assert ae(fp.altzeta(0), 0.5) + assert ae(fp.zeta(-2+3j, 1), (0.13297115587929864827 + 0.12305330040458776494j)) + assert ae(fp.zeta(-2+3j, 5), (18.384866151867576927 - 11.377015110597711009j)) + assert ae(fp.zeta(1.0000000001), 9999999173.1735741337) + assert ae(fp.zeta(0.9999999999), -9999999172.0191428039) + assert ae(fp.zeta(1+0.000000001j), (0.57721566490153286061 - 999999999.99999993765j)) + assert ae(fp.primezeta(2.5+4j), (-0.16922458243438033385 - 0.010847965298387727811j)) + assert ae(fp.primezeta(4), 0.076993139764246844943) + assert ae(fp.riemannr(3.7), 2.3034079839110855717) + assert ae(fp.riemannr(8), 3.9011860449341499474) + assert ae(fp.riemannr(3+4j), (2.2369653314259991796 + 1.6339943856990281694j)) + +def test_fp_hyp2f1(): + assert ae(fp.hyp2f1(1, (3,2), 3.25, 5.0), (-0.46600275923108143059 - 0.74393667908854842325j)) + assert ae(fp.hyp2f1(1+1j, (3,2), 3.25, 5.0), (-5.9208875603806515987 - 2.3813557707889590686j)) + assert ae(fp.hyp2f1(1+1j, (3,2), 3.25, 2+3j), (0.17174552030925080445 + 0.19589781970539389999j)) + +def test_fp_erf(): + assert fp.erf(2) == fp.erf(2.0) == fp.erf(2.0+0.0j) + assert fp.erf(fp.inf) == 1.0 + assert fp.erf(fp.ninf) == -1.0 + assert ae(fp.erf(0), 0.0) + assert ae(fp.erf(-0), -0.0) + assert ae(fp.erf(0.3), 0.32862675945912741619) + assert ae(fp.erf(-0.3), -0.32862675945912741619) + assert ae(fp.erf(0.9), 0.79690821242283213966) + assert ae(fp.erf(-0.9), -0.79690821242283213966) + assert ae(fp.erf(1.0), 0.84270079294971486934) + assert ae(fp.erf(-1.0), -0.84270079294971486934) + assert ae(fp.erf(1.1), 0.88020506957408172966) + assert ae(fp.erf(-1.1), -0.88020506957408172966) + assert ae(fp.erf(8.5), 1.0) + assert ae(fp.erf(-8.5), -1.0) + assert ae(fp.erf(9.1), 1.0) + assert ae(fp.erf(-9.1), -1.0) + assert ae(fp.erf(20.0), 1.0) + assert ae(fp.erf(-20.0), -1.0) + assert ae(fp.erf(10000.0), 1.0) + assert ae(fp.erf(-10000.0), -1.0) + assert ae(fp.erf(1e+50), 1.0) + assert ae(fp.erf(-1e+50), -1.0) + assert ae(fp.erf(1j), 1.650425758797542876j) + assert ae(fp.erf(-1j), -1.650425758797542876j) + assert ae(fp.erf((2+3j)), (-20.829461427614568389 + 8.6873182714701631444j)) + assert ae(fp.erf(-(2+3j)), -(-20.829461427614568389 + 8.6873182714701631444j)) + assert ae(fp.erf((8+9j)), (-1072006.2525062051158 + 364149.91954310255423j)) + assert ae(fp.erf(-(8+9j)), -(-1072006.2525062051158 + 364149.91954310255423j)) + assert fp.erfc(fp.inf) == 0.0 + assert fp.erfc(fp.ninf) == 2.0 + assert fp.erfc(0) == 1 + assert fp.erfc(-0.0) == 1 + assert fp.erfc(0+0j) == 1 + assert ae(fp.erfc(0.3), 0.67137324054087258381) + assert ae(fp.erfc(-0.3), 1.3286267594591274162) + assert ae(fp.erfc(0.9), 0.20309178757716786034) + assert ae(fp.erfc(-0.9), 1.7969082124228321397) + assert ae(fp.erfc(1.0), 0.15729920705028513066) + assert ae(fp.erfc(-1.0), 1.8427007929497148693) + assert ae(fp.erfc(1.1), 0.11979493042591827034) + assert ae(fp.erfc(-1.1), 1.8802050695740817297) + assert ae(fp.erfc(8.5), 2.7623240713337714461e-33) + assert ae(fp.erfc(-8.5), 2.0) + assert ae(fp.erfc(9.1), 6.6969004279886077452e-38) + assert ae(fp.erfc(-9.1), 2.0) + assert ae(fp.erfc(20.0), 5.3958656116079009289e-176) + assert ae(fp.erfc(-20.0), 2.0) + assert ae(fp.erfc(10000.0), 0.0) + assert ae(fp.erfc(-10000.0), 2.0) + assert ae(fp.erfc(1e+50), 0.0) + assert ae(fp.erfc(-1e+50), 2.0) + assert ae(fp.erfc(1j), (1.0 - 1.650425758797542876j)) + assert ae(fp.erfc(-1j), (1.0 + 1.650425758797542876j)) + assert ae(fp.erfc((2+3j)), (21.829461427614568389 - 8.6873182714701631444j), 1e-13) + assert ae(fp.erfc(-(2+3j)), (-19.829461427614568389 + 8.6873182714701631444j), 1e-13) + assert ae(fp.erfc((8+9j)), (1072005.2525062051158 - 364149.91954310255423j)) + assert ae(fp.erfc(-(8+9j)), (-1072005.2525062051158 + 364149.91954310255423j)) + assert ae(fp.erfc(20+0j), (5.3958656116079009289e-176 + 0.0j)) + +def test_fp_lambertw(): + assert ae(fp.lambertw(0.0), 0.0) + assert ae(fp.lambertw(1.0), 0.567143290409783873) + assert ae(fp.lambertw(7.5), 1.5662309537823875394) + assert ae(fp.lambertw(-0.25), -0.35740295618138890307) + assert ae(fp.lambertw(-10.0), (1.3699809685212708156 + 2.140194527074713196j)) + assert ae(fp.lambertw(0+0j), (0.0 + 0.0j)) + assert ae(fp.lambertw(4+0j), (1.2021678731970429392 + 0.0j)) + assert ae(fp.lambertw(1000.5), 5.2500227450408980127) + assert ae(fp.lambertw(1e100), 224.84310644511850156) + assert ae(fp.lambertw(-1000.0), (5.1501630246362515223 + 2.6641981432905204596j)) + assert ae(fp.lambertw(1e-10), 9.9999999990000003645e-11) + assert ae(fp.lambertw(1e-10j), (1.0000000000000000728e-20 + 1.0000000000000000364e-10j)) + assert ae(fp.lambertw(3+4j), (1.2815618061237758782 + 0.53309522202097107131j)) + assert ae(fp.lambertw(-3-4j), (1.0750730665692549276 - 1.3251023817343588823j)) + assert ae(fp.lambertw(10000+1000j), (7.2361526563371602186 + 0.087567810943839352034j)) + assert ae(fp.lambertw(0.0, -1), -fp.inf) + assert ae(fp.lambertw(1.0, -1), (-1.5339133197935745079 - 4.3751851530618983855j)) + assert ae(fp.lambertw(7.5, -1), (0.44125668415098614999 - 4.8039842008452390179j)) + assert ae(fp.lambertw(-0.25, -1), -2.1532923641103496492) + assert ae(fp.lambertw(-10.0, -1), (1.3699809685212708156 - 2.140194527074713196j)) + assert ae(fp.lambertw(0+0j, -1), -fp.inf) + assert ae(fp.lambertw(4+0j, -1), (-0.15730793189620765317 - 4.6787800704666656212j)) + assert ae(fp.lambertw(1000.5, -1), (4.9153765415404024736 - 5.4465682700815159569j)) + assert ae(fp.lambertw(1e100, -1), (224.84272130101601052 - 6.2553713838167244141j)) + assert ae(fp.lambertw(-1000.0, -1), (5.1501630246362515223 - 2.6641981432905204596j)) + assert ae(fp.lambertw(1e-10, -1), (-26.303186778379041521 - 3.2650939117038283975j)) + assert ae(fp.lambertw(1e-10j, -1), (-26.297238779529035028 - 1.6328071613455765135j)) + assert ae(fp.lambertw(3+4j, -1), (0.25856740686699741676 - 3.8521166861614355895j)) + assert ae(fp.lambertw(-3-4j, -1), (-0.32028750204310768396 - 6.8801677192091972343j)) + assert ae(fp.lambertw(10000+1000j, -1), (7.0255308742285435567 - 5.5177506835734067601j)) + assert ae(fp.lambertw(0.0, 2), -fp.inf) + assert ae(fp.lambertw(1.0, 2), (-2.4015851048680028842 + 10.776299516115070898j)) + assert ae(fp.lambertw(7.5, 2), (-0.38003357962843791529 + 10.960916473368746184j)) + assert ae(fp.lambertw(-0.25, 2), (-4.0558735269061511898 + 13.852334658567271386j)) + assert ae(fp.lambertw(-10.0, 2), (-0.34479123764318858696 + 14.112740596763592363j)) + assert ae(fp.lambertw(0+0j, 2), -fp.inf) + assert ae(fp.lambertw(4+0j, 2), (-1.0070343323804262788 + 10.903476551861683082j)) + assert ae(fp.lambertw(1000.5, 2), (4.4076185165459395295 + 11.365524591091402177j)) + assert ae(fp.lambertw(1e100, 2), (224.84156762724875878 + 12.510785262632255672j)) + assert ae(fp.lambertw(-1000.0, 2), (4.1984245610246530756 + 14.420478573754313845j)) + assert ae(fp.lambertw(1e-10, 2), (-26.362258095445866488 + 9.7800247407031482519j)) + assert ae(fp.lambertw(1e-10j, 2), (-26.384250801683084252 + 11.403535950607739763j)) + assert ae(fp.lambertw(3+4j, 2), (-0.86554679943333993562 + 11.849956798331992027j)) + assert ae(fp.lambertw(-3-4j, 2), (-0.55792273874679112639 + 8.7173627024159324811j)) + assert ae(fp.lambertw(10000+1000j, 2), (6.6223802254585662734 + 11.61348646825020766j)) + +def test_fp_stress_ei_e1(): + # Can be tightened on recent Pythons with more accurate math/cmath + ATOL = 1e-13 + PTOL = 1e-12 + v = fp.e1(1.1641532182693481445e-10) + assert ae(v, 22.296641293693077672, tol=ATOL) + assert type(v) is float + v = fp.e1(0.25) + assert ae(v, 1.0442826344437381945, tol=ATOL) + assert type(v) is float + v = fp.e1(1.0) + assert ae(v, 0.21938393439552027368, tol=ATOL) + assert type(v) is float + v = fp.e1(2.0) + assert ae(v, 0.048900510708061119567, tol=ATOL) + assert type(v) is float + v = fp.e1(5.0) + assert ae(v, 0.0011482955912753257973, tol=ATOL) + assert type(v) is float + v = fp.e1(20.0) + assert ae(v, 9.8355252906498816904e-11, tol=ATOL) + assert type(v) is float + v = fp.e1(30.0) + assert ae(v, 3.0215520106888125448e-15, tol=ATOL) + assert type(v) is float + v = fp.e1(40.0) + assert ae(v, 1.0367732614516569722e-19, tol=ATOL) + assert type(v) is float + v = fp.e1(50.0) + assert ae(v, 3.7832640295504590187e-24, tol=ATOL) + assert type(v) is float + v = fp.e1(80.0) + assert ae(v, 2.2285432586884729112e-37, tol=ATOL) + assert type(v) is float + v = fp.e1((1.1641532182693481445e-10 + 0.0j)) + assert ae(v, (22.296641293693077672 + 0.0j), tol=ATOL) + assert ae(v.real, 22.296641293693077672, tol=PTOL) + assert v.imag == 0 + v = fp.e1((0.25 + 0.0j)) + assert ae(v, (1.0442826344437381945 + 0.0j), tol=ATOL) + assert ae(v.real, 1.0442826344437381945, tol=PTOL) + assert v.imag == 0 + v = fp.e1((1.0 + 0.0j)) + assert ae(v, (0.21938393439552027368 + 0.0j), tol=ATOL) + assert ae(v.real, 0.21938393439552027368, tol=PTOL) + assert v.imag == 0 + v = fp.e1((2.0 + 0.0j)) + assert ae(v, (0.048900510708061119567 + 0.0j), tol=ATOL) + assert ae(v.real, 0.048900510708061119567, tol=PTOL) + assert v.imag == 0 + v = fp.e1((5.0 + 0.0j)) + assert ae(v, (0.0011482955912753257973 + 0.0j), tol=ATOL) + assert ae(v.real, 0.0011482955912753257973, tol=PTOL) + assert v.imag == 0 + v = fp.e1((20.0 + 0.0j)) + assert ae(v, (9.8355252906498816904e-11 + 0.0j), tol=ATOL) + assert ae(v.real, 9.8355252906498816904e-11, tol=PTOL) + assert v.imag == 0 + v = fp.e1((30.0 + 0.0j)) + assert ae(v, (3.0215520106888125448e-15 + 0.0j), tol=ATOL) + assert ae(v.real, 3.0215520106888125448e-15, tol=PTOL) + assert v.imag == 0 + v = fp.e1((40.0 + 0.0j)) + assert ae(v, (1.0367732614516569722e-19 + 0.0j), tol=ATOL) + assert ae(v.real, 1.0367732614516569722e-19, tol=PTOL) + assert v.imag == 0 + v = fp.e1((50.0 + 0.0j)) + assert ae(v, (3.7832640295504590187e-24 + 0.0j), tol=ATOL) + assert ae(v.real, 3.7832640295504590187e-24, tol=PTOL) + assert v.imag == 0 + v = fp.e1((80.0 + 0.0j)) + assert ae(v, (2.2285432586884729112e-37 + 0.0j), tol=ATOL) + assert ae(v.real, 2.2285432586884729112e-37, tol=PTOL) + assert v.imag == 0 + v = fp.e1((4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (20.880034622014215597 - 0.24497866301044883237j), tol=ATOL) + assert ae(v.real, 20.880034622014215597, tol=PTOL) + assert ae(v.imag, -0.24497866301044883237, tol=PTOL) + v = fp.e1((1.0 + 0.25j)) + assert ae(v, (0.19731063945004229095 - 0.087366045774299963672j), tol=ATOL) + assert ae(v.real, 0.19731063945004229095, tol=PTOL) + assert ae(v.imag, -0.087366045774299963672, tol=PTOL) + v = fp.e1((4.0 + 1.0j)) + assert ae(v, (0.0013106173980145506944 - 0.0034542480199350626699j), tol=ATOL) + assert ae(v.real, 0.0013106173980145506944, tol=PTOL) + assert ae(v.imag, -0.0034542480199350626699, tol=PTOL) + v = fp.e1((8.0 + 2.0j)) + assert ae(v, (-0.000022278049065270225945 - 0.000029191940456521555288j), tol=ATOL) + assert ae(v.real, -0.000022278049065270225945, tol=PTOL) + assert ae(v.imag, -0.000029191940456521555288, tol=PTOL) + v = fp.e1((20.0 + 5.0j)) + assert ae(v, (4.7711374515765346894e-11 + 8.2902652405126947359e-11j), tol=ATOL) + assert ae(v.real, 4.7711374515765346894e-11, tol=PTOL) + assert ae(v.imag, 8.2902652405126947359e-11, tol=PTOL) + v = fp.e1((80.0 + 20.0j)) + assert ae(v, (3.8353473865788235787e-38 - 2.129247592349605139e-37j), tol=ATOL) + assert ae(v.real, 3.8353473865788235787e-38, tol=PTOL) + assert ae(v.imag, -2.129247592349605139e-37, tol=PTOL) + v = fp.e1((120.0 + 30.0j)) + assert ae(v, (2.3836002337480334716e-55 + 5.6704043587126198306e-55j), tol=ATOL) + assert ae(v.real, 2.3836002337480334716e-55, tol=PTOL) + assert ae(v.imag, 5.6704043587126198306e-55, tol=PTOL) + v = fp.e1((160.0 + 40.0j)) + assert ae(v, (-1.6238022898654510661e-72 - 1.104172355572287367e-72j), tol=ATOL) + assert ae(v.real, -1.6238022898654510661e-72, tol=PTOL) + assert ae(v.imag, -1.104172355572287367e-72, tol=PTOL) + v = fp.e1((200.0 + 50.0j)) + assert ae(v, (6.6800061461666228487e-90 + 1.4473816083541016115e-91j), tol=ATOL) + assert ae(v.real, 6.6800061461666228487e-90, tol=PTOL) + assert ae(v.imag, 1.4473816083541016115e-91, tol=PTOL) + v = fp.e1((320.0 + 80.0j)) + assert ae(v, (4.2737871527778786157e-143 + 3.1789935525785660314e-142j), tol=ATOL) + assert ae(v.real, 4.2737871527778786157e-143, tol=PTOL) + assert ae(v.imag, 3.1789935525785660314e-142, tol=PTOL) + v = fp.e1((1.1641532182693481445e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (21.950067703413105017 - 0.7853981632810329878j), tol=ATOL) + assert ae(v.real, 21.950067703413105017, tol=PTOL) + assert ae(v.imag, -0.7853981632810329878, tol=PTOL) + v = fp.e1((0.25 + 0.25j)) + assert ae(v, (0.71092525792923287894 - 0.56491812441304194711j), tol=ATOL) + assert ae(v.real, 0.71092525792923287894, tol=PTOL) + assert ae(v.imag, -0.56491812441304194711, tol=PTOL) + v = fp.e1((1.0 + 1.0j)) + assert ae(v, (0.00028162445198141832551 - 0.17932453503935894015j), tol=ATOL) + assert ae(v.real, 0.00028162445198141832551, tol=PTOL) + assert ae(v.imag, -0.17932453503935894015, tol=PTOL) + v = fp.e1((2.0 + 2.0j)) + assert ae(v, (-0.033767089606562004246 - 0.018599414169750541925j), tol=ATOL) + assert ae(v.real, -0.033767089606562004246, tol=PTOL) + assert ae(v.imag, -0.018599414169750541925, tol=PTOL) + v = fp.e1((5.0 + 5.0j)) + assert ae(v, (0.0007266506660356393891 + 0.00047102780163522245054j), tol=ATOL) + assert ae(v.real, 0.0007266506660356393891, tol=PTOL) + assert ae(v.imag, 0.00047102780163522245054, tol=PTOL) + v = fp.e1((20.0 + 20.0j)) + assert ae(v, (-2.3824537449367396579e-11 - 6.6969873156525615158e-11j), tol=ATOL) + assert ae(v.real, -2.3824537449367396579e-11, tol=PTOL) + assert ae(v.imag, -6.6969873156525615158e-11, tol=PTOL) + v = fp.e1((30.0 + 30.0j)) + assert ae(v, (1.7316045841744061617e-15 + 1.3065678019487308689e-15j), tol=ATOL) + assert ae(v.real, 1.7316045841744061617e-15, tol=PTOL) + assert ae(v.imag, 1.3065678019487308689e-15, tol=PTOL) + v = fp.e1((40.0 + 40.0j)) + assert ae(v, (-7.4001043002899232182e-20 - 4.991847855336816304e-21j), tol=ATOL) + assert ae(v.real, -7.4001043002899232182e-20, tol=PTOL) + assert ae(v.imag, -4.991847855336816304e-21, tol=PTOL) + v = fp.e1((50.0 + 50.0j)) + assert ae(v, (2.3566128324644641219e-24 - 1.3188326726201614778e-24j), tol=ATOL) + assert ae(v.real, 2.3566128324644641219e-24, tol=PTOL) + assert ae(v.imag, -1.3188326726201614778e-24, tol=PTOL) + v = fp.e1((80.0 + 80.0j)) + assert ae(v, (9.8279750572186526673e-38 + 1.243952841288868831e-37j), tol=ATOL) + assert ae(v.real, 9.8279750572186526673e-38, tol=PTOL) + assert ae(v.imag, 1.243952841288868831e-37, tol=PTOL) + v = fp.e1((1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) + assert ae(v, (20.880034621664969632 - 1.3258176632023711778j), tol=ATOL) + assert ae(v.real, 20.880034621664969632, tol=PTOL) + assert ae(v.imag, -1.3258176632023711778, tol=PTOL) + v = fp.e1((0.25 + 1.0j)) + assert ae(v, (-0.16868306393667788761 - 0.4858011885947426971j), tol=ATOL) + assert ae(v.real, -0.16868306393667788761, tol=PTOL) + assert ae(v.imag, -0.4858011885947426971, tol=PTOL) + v = fp.e1((1.0 + 4.0j)) + assert ae(v, (0.03373591813926547318 + 0.073523452241083821877j), tol=ATOL) + assert ae(v.real, 0.03373591813926547318, tol=PTOL) + assert ae(v.imag, 0.073523452241083821877, tol=PTOL) + v = fp.e1((2.0 + 8.0j)) + assert ae(v, (-0.015392833434733785143 - 0.0031747121557605415914j), tol=ATOL) + assert ae(v.real, -0.015392833434733785143, tol=PTOL) + assert ae(v.imag, -0.0031747121557605415914, tol=PTOL) + v = fp.e1((5.0 + 20.0j)) + assert ae(v, (-0.00024419662286542966525 - 0.00021008322966152755674j), tol=ATOL) + assert ae(v.real, -0.00024419662286542966525, tol=PTOL) + assert ae(v.imag, -0.00021008322966152755674, tol=PTOL) + v = fp.e1((20.0 + 80.0j)) + assert ae(v, (2.3255552781051330088e-11 + 8.9463918891349438007e-12j), tol=ATOL) + assert ae(v.real, 2.3255552781051330088e-11, tol=PTOL) + assert ae(v.imag, 8.9463918891349438007e-12, tol=PTOL) + v = fp.e1((30.0 + 120.0j)) + assert ae(v, (-2.7068919097124652332e-16 - 7.0477762411705130239e-16j), tol=ATOL) + assert ae(v.real, -2.7068919097124652332e-16, tol=PTOL) + assert ae(v.imag, -7.0477762411705130239e-16, tol=PTOL) + v = fp.e1((40.0 + 160.0j)) + assert ae(v, (-1.1695597827678024687e-20 + 2.2907401455645736661e-20j), tol=ATOL) + assert ae(v.real, -1.1695597827678024687e-20, tol=PTOL) + assert ae(v.imag, 2.2907401455645736661e-20, tol=PTOL) + v = fp.e1((50.0 + 200.0j)) + assert ae(v, (9.0323746914410162531e-25 - 2.3950601790033530935e-25j), tol=ATOL) + assert ae(v.real, 9.0323746914410162531e-25, tol=PTOL) + assert ae(v.imag, -2.3950601790033530935e-25, tol=PTOL) + v = fp.e1((80.0 + 320.0j)) + assert ae(v, (3.4819106748728063576e-38 - 4.215653005615772724e-38j), tol=ATOL) + assert ae(v.real, 3.4819106748728063576e-38, tol=PTOL) + assert ae(v.imag, -4.215653005615772724e-38, tol=PTOL) + v = fp.e1((0.0 + 1.1641532182693481445e-10j)) + assert ae(v, (22.29664129357666235 - 1.5707963266784812974j), tol=ATOL) + assert ae(v.real, 22.29664129357666235, tol=PTOL) + assert ae(v.imag, -1.5707963266784812974, tol=PTOL) + v = fp.e1((0.0 + 0.25j)) + assert ae(v, (0.82466306258094565309 - 1.3216627564751394551j), tol=ATOL) + assert ae(v.real, 0.82466306258094565309, tol=PTOL) + assert ae(v.imag, -1.3216627564751394551, tol=PTOL) + v = fp.e1((0.0 + 1.0j)) + assert ae(v, (-0.33740392290096813466 - 0.62471325642771360429j), tol=ATOL) + assert ae(v.real, -0.33740392290096813466, tol=PTOL) + assert ae(v.imag, -0.62471325642771360429, tol=PTOL) + v = fp.e1((0.0 + 2.0j)) + assert ae(v, (-0.4229808287748649957 + 0.034616650007798229345j), tol=ATOL) + assert ae(v.real, -0.4229808287748649957, tol=PTOL) + assert ae(v.imag, 0.034616650007798229345, tol=PTOL) + v = fp.e1((0.0 + 5.0j)) + assert ae(v, (0.19002974965664387862 - 0.020865081850222481957j), tol=ATOL) + assert ae(v.real, 0.19002974965664387862, tol=PTOL) + assert ae(v.imag, -0.020865081850222481957, tol=PTOL) + v = fp.e1((0.0 + 20.0j)) + assert ae(v, (-0.04441982084535331654 - 0.022554625751456779068j), tol=ATOL) + assert ae(v.real, -0.04441982084535331654, tol=PTOL) + assert ae(v.imag, -0.022554625751456779068, tol=PTOL) + v = fp.e1((0.0 + 30.0j)) + assert ae(v, (0.033032417282071143779 - 0.0040397867645455082476j), tol=ATOL) + assert ae(v.real, 0.033032417282071143779, tol=PTOL) + assert ae(v.imag, -0.0040397867645455082476, tol=PTOL) + v = fp.e1((0.0 + 40.0j)) + assert ae(v, (-0.019020007896208766962 + 0.016188792559887887544j), tol=ATOL) + assert ae(v.real, -0.019020007896208766962, tol=PTOL) + assert ae(v.imag, 0.016188792559887887544, tol=PTOL) + v = fp.e1((0.0 + 50.0j)) + assert ae(v, (0.0056283863241163054402 - 0.019179254308960724503j), tol=ATOL) + assert ae(v.real, 0.0056283863241163054402, tol=PTOL) + assert ae(v.imag, -0.019179254308960724503, tol=PTOL) + v = fp.e1((0.0 + 80.0j)) + assert ae(v, (0.012402501155070958192 + 0.0015345601175906961199j), tol=ATOL) + assert ae(v.real, 0.012402501155070958192, tol=PTOL) + assert ae(v.imag, 0.0015345601175906961199, tol=PTOL) + v = fp.e1((-1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) + assert ae(v, (20.880034621432138988 - 1.8157749894560994861j), tol=ATOL) + assert ae(v.real, 20.880034621432138988, tol=PTOL) + assert ae(v.imag, -1.8157749894560994861, tol=PTOL) + v = fp.e1((-0.25 + 1.0j)) + assert ae(v, (-0.59066621214766308594 - 0.74474454765205036972j), tol=ATOL) + assert ae(v.real, -0.59066621214766308594, tol=PTOL) + assert ae(v.imag, -0.74474454765205036972, tol=PTOL) + v = fp.e1((-1.0 + 4.0j)) + assert ae(v, (0.49739047283060471093 + 0.41543605404038863174j), tol=ATOL) + assert ae(v.real, 0.49739047283060471093, tol=PTOL) + assert ae(v.imag, 0.41543605404038863174, tol=PTOL) + v = fp.e1((-2.0 + 8.0j)) + assert ae(v, (-0.8705211147733730969 + 0.24099328498605539667j), tol=ATOL) + assert ae(v.real, -0.8705211147733730969, tol=PTOL) + assert ae(v.imag, 0.24099328498605539667, tol=PTOL) + v = fp.e1((-5.0 + 20.0j)) + assert ae(v, (-7.0789514293925893007 - 1.6102177171960790536j), tol=ATOL) + assert ae(v.real, -7.0789514293925893007, tol=PTOL) + assert ae(v.imag, -1.6102177171960790536, tol=PTOL) + v = fp.e1((-20.0 + 80.0j)) + assert ae(v, (5855431.4907298084434 - 720920.93315409165707j), tol=ATOL) + assert ae(v.real, 5855431.4907298084434, tol=PTOL) + assert ae(v.imag, -720920.93315409165707, tol=PTOL) + v = fp.e1((-30.0 + 120.0j)) + assert ae(v, (-65402491644.703470747 - 56697658399.657460294j), tol=ATOL) + assert ae(v.real, -65402491644.703470747, tol=PTOL) + assert ae(v.imag, -56697658399.657460294, tol=PTOL) + v = fp.e1((-40.0 + 160.0j)) + assert ae(v, (25504929379604.776769 + 1429035198630573.2463j), tol=ATOL) + assert ae(v.real, 25504929379604.776769, tol=PTOL) + assert ae(v.imag, 1429035198630573.2463, tol=PTOL) + v = fp.e1((-50.0 + 200.0j)) + assert ae(v, (18437746526988116954.0 - 17146362239046152345.0j), tol=ATOL) + assert ae(v.real, 18437746526988116954.0, tol=PTOL) + assert ae(v.imag, -17146362239046152345.0, tol=PTOL) + v = fp.e1((-80.0 + 320.0j)) + assert ae(v, (3.3464697299634526706e+31 - 1.6473152633843023919e+32j), tol=ATOL) + assert ae(v.real, 3.3464697299634526706e+31, tol=PTOL) + assert ae(v.imag, -1.6473152633843023919e+32, tol=PTOL) + v = fp.e1((-4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (20.880034621082893023 - 2.8966139903465137624j), tol=ATOL) + assert ae(v.real, 20.880034621082893023, tol=PTOL) + assert ae(v.imag, -2.8966139903465137624, tol=PTOL) + v = fp.e1((-1.0 + 0.25j)) + assert ae(v, (-1.8942716983721074932 - 2.4689102827070540799j), tol=ATOL) + assert ae(v.real, -1.8942716983721074932, tol=PTOL) + assert ae(v.imag, -2.4689102827070540799, tol=PTOL) + v = fp.e1((-4.0 + 1.0j)) + assert ae(v, (-14.806699492675420438 + 9.1384225230837893776j), tol=ATOL) + assert ae(v.real, -14.806699492675420438, tol=PTOL) + assert ae(v.imag, 9.1384225230837893776, tol=PTOL) + v = fp.e1((-8.0 + 2.0j)) + assert ae(v, (54.633252667426386294 + 413.20318163814670688j), tol=ATOL) + assert ae(v.real, 54.633252667426386294, tol=PTOL) + assert ae(v.imag, 413.20318163814670688, tol=PTOL) + v = fp.e1((-20.0 + 5.0j)) + assert ae(v, (-711836.97165402624643 - 24745250.939695900956j), tol=ATOL) + assert ae(v.real, -711836.97165402624643, tol=PTOL) + assert ae(v.imag, -24745250.939695900956, tol=PTOL) + v = fp.e1((-80.0 + 20.0j)) + assert ae(v, (-4.2139911108612653091e+32 + 5.3367124741918251637e+32j), tol=ATOL) + assert ae(v.real, -4.2139911108612653091e+32, tol=PTOL) + assert ae(v.imag, 5.3367124741918251637e+32, tol=PTOL) + v = fp.e1((-120.0 + 30.0j)) + assert ae(v, (9.7760616203707508892e+48 - 1.058257682317195792e+50j), tol=ATOL) + assert ae(v.real, 9.7760616203707508892e+48, tol=PTOL) + assert ae(v.imag, -1.058257682317195792e+50, tol=PTOL) + v = fp.e1((-160.0 + 40.0j)) + assert ae(v, (8.7065541466623638861e+66 + 1.6577106725141739889e+67j), tol=ATOL) + assert ae(v.real, 8.7065541466623638861e+66, tol=PTOL) + assert ae(v.imag, 1.6577106725141739889e+67, tol=PTOL) + v = fp.e1((-200.0 + 50.0j)) + assert ae(v, (-3.070744996327018106e+84 - 1.7243244846769415903e+84j), tol=ATOL) + assert ae(v.real, -3.070744996327018106e+84, tol=PTOL) + assert ae(v.imag, -1.7243244846769415903e+84, tol=PTOL) + v = fp.e1((-320.0 + 80.0j)) + assert ae(v, (9.9960598637998647276e+135 - 2.6855081527595608863e+136j), tol=ATOL) + assert ae(v.real, 9.9960598637998647276e+135, tol=PTOL) + assert ae(v.imag, -2.6855081527595608863e+136, tol=PTOL) + v = fp.e1(-1.1641532182693481445e-10) + assert ae(v, (22.296641293460247028 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 22.296641293460247028, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-0.25) + assert ae(v, (0.54254326466191372953 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 0.54254326466191372953, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-1.0) + assert ae(v, (-1.8951178163559367555 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -1.8951178163559367555, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-2.0) + assert ae(v, (-4.9542343560018901634 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -4.9542343560018901634, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-5.0) + assert ae(v, (-40.185275355803177455 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -40.185275355803177455, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-20.0) + assert ae(v, (-25615652.66405658882 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -25615652.66405658882, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-30.0) + assert ae(v, (-368973209407.27419706 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -368973209407.27419706, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-40.0) + assert ae(v, (-6039718263611241.5784 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -6039718263611241.5784, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-50.0) + assert ae(v, (-1.0585636897131690963e+20 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -1.0585636897131690963e+20, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1(-80.0) + assert ae(v, (-7.0146000049047999696e+32 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -7.0146000049047999696e+32, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-1.1641532182693481445e-10 + 0.0j)) + assert ae(v, (22.296641293460247028 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 22.296641293460247028, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-0.25 + 0.0j)) + assert ae(v, (0.54254326466191372953 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 0.54254326466191372953, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-1.0 + 0.0j)) + assert ae(v, (-1.8951178163559367555 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -1.8951178163559367555, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-2.0 + 0.0j)) + assert ae(v, (-4.9542343560018901634 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -4.9542343560018901634, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-5.0 + 0.0j)) + assert ae(v, (-40.185275355803177455 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -40.185275355803177455, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-20.0 + 0.0j)) + assert ae(v, (-25615652.66405658882 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -25615652.66405658882, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-30.0 + 0.0j)) + assert ae(v, (-368973209407.27419706 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -368973209407.27419706, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-40.0 + 0.0j)) + assert ae(v, (-6039718263611241.5784 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -6039718263611241.5784, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-50.0 + 0.0j)) + assert ae(v, (-1.0585636897131690963e+20 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -1.0585636897131690963e+20, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-80.0 + 0.0j)) + assert ae(v, (-7.0146000049047999696e+32 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -7.0146000049047999696e+32, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.e1((-4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (20.880034621082893023 + 2.8966139903465137624j), tol=ATOL) + assert ae(v.real, 20.880034621082893023, tol=PTOL) + assert ae(v.imag, 2.8966139903465137624, tol=PTOL) + v = fp.e1((-1.0 - 0.25j)) + assert ae(v, (-1.8942716983721074932 + 2.4689102827070540799j), tol=ATOL) + assert ae(v.real, -1.8942716983721074932, tol=PTOL) + assert ae(v.imag, 2.4689102827070540799, tol=PTOL) + v = fp.e1((-4.0 - 1.0j)) + assert ae(v, (-14.806699492675420438 - 9.1384225230837893776j), tol=ATOL) + assert ae(v.real, -14.806699492675420438, tol=PTOL) + assert ae(v.imag, -9.1384225230837893776, tol=PTOL) + v = fp.e1((-8.0 - 2.0j)) + assert ae(v, (54.633252667426386294 - 413.20318163814670688j), tol=ATOL) + assert ae(v.real, 54.633252667426386294, tol=PTOL) + assert ae(v.imag, -413.20318163814670688, tol=PTOL) + v = fp.e1((-20.0 - 5.0j)) + assert ae(v, (-711836.97165402624643 + 24745250.939695900956j), tol=ATOL) + assert ae(v.real, -711836.97165402624643, tol=PTOL) + assert ae(v.imag, 24745250.939695900956, tol=PTOL) + v = fp.e1((-80.0 - 20.0j)) + assert ae(v, (-4.2139911108612653091e+32 - 5.3367124741918251637e+32j), tol=ATOL) + assert ae(v.real, -4.2139911108612653091e+32, tol=PTOL) + assert ae(v.imag, -5.3367124741918251637e+32, tol=PTOL) + v = fp.e1((-120.0 - 30.0j)) + assert ae(v, (9.7760616203707508892e+48 + 1.058257682317195792e+50j), tol=ATOL) + assert ae(v.real, 9.7760616203707508892e+48, tol=PTOL) + assert ae(v.imag, 1.058257682317195792e+50, tol=PTOL) + v = fp.e1((-160.0 - 40.0j)) + assert ae(v, (8.7065541466623638861e+66 - 1.6577106725141739889e+67j), tol=ATOL) + assert ae(v.real, 8.7065541466623638861e+66, tol=PTOL) + assert ae(v.imag, -1.6577106725141739889e+67, tol=PTOL) + v = fp.e1((-200.0 - 50.0j)) + assert ae(v, (-3.070744996327018106e+84 + 1.7243244846769415903e+84j), tol=ATOL) + assert ae(v.real, -3.070744996327018106e+84, tol=PTOL) + assert ae(v.imag, 1.7243244846769415903e+84, tol=PTOL) + v = fp.e1((-320.0 - 80.0j)) + assert ae(v, (9.9960598637998647276e+135 + 2.6855081527595608863e+136j), tol=ATOL) + assert ae(v.real, 9.9960598637998647276e+135, tol=PTOL) + assert ae(v.imag, 2.6855081527595608863e+136, tol=PTOL) + v = fp.e1((-1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (21.950067703180274374 + 2.356194490075929607j), tol=ATOL) + assert ae(v.real, 21.950067703180274374, tol=PTOL) + assert ae(v.imag, 2.356194490075929607, tol=PTOL) + v = fp.e1((-0.25 - 0.25j)) + assert ae(v, (0.21441047326710323254 + 2.0732153554307936389j), tol=ATOL) + assert ae(v.real, 0.21441047326710323254, tol=PTOL) + assert ae(v.imag, 2.0732153554307936389, tol=PTOL) + v = fp.e1((-1.0 - 1.0j)) + assert ae(v, (-1.7646259855638540684 + 0.7538228020792708192j), tol=ATOL) + assert ae(v.real, -1.7646259855638540684, tol=PTOL) + assert ae(v.imag, 0.7538228020792708192, tol=PTOL) + v = fp.e1((-2.0 - 2.0j)) + assert ae(v, (-1.8920781621855474089 - 2.1753697842428647236j), tol=ATOL) + assert ae(v.real, -1.8920781621855474089, tol=PTOL) + assert ae(v.imag, -2.1753697842428647236, tol=PTOL) + v = fp.e1((-5.0 - 5.0j)) + assert ae(v, (13.470936071475245856 + 18.464085049321024206j), tol=ATOL) + assert ae(v.real, 13.470936071475245856, tol=PTOL) + assert ae(v.imag, 18.464085049321024206, tol=PTOL) + v = fp.e1((-20.0 - 20.0j)) + assert ae(v, (-16589317.398788971896 - 5831702.3296441771206j), tol=ATOL) + assert ae(v.real, -16589317.398788971896, tol=PTOL) + assert ae(v.imag, -5831702.3296441771206, tol=PTOL) + v = fp.e1((-30.0 - 30.0j)) + assert ae(v, (154596484273.69322527 + 204179357837.41389696j), tol=ATOL) + assert ae(v.real, 154596484273.69322527, tol=PTOL) + assert ae(v.imag, 204179357837.41389696, tol=PTOL) + v = fp.e1((-40.0 - 40.0j)) + assert ae(v, (-287512180321448.45408 - 4203502407932314.974j), tol=ATOL) + assert ae(v.real, -287512180321448.45408, tol=PTOL) + assert ae(v.imag, -4203502407932314.974, tol=PTOL) + v = fp.e1((-50.0 - 50.0j)) + assert ae(v, (-36128528616649268826.0 + 64648801861338741963.0j), tol=ATOL) + assert ae(v.real, -36128528616649268826.0, tol=PTOL) + assert ae(v.imag, 64648801861338741963.0, tol=PTOL) + v = fp.e1((-80.0 - 80.0j)) + assert ae(v, (3.8674816337930010217e+32 + 3.0540709639658071041e+32j), tol=ATOL) + assert ae(v.real, 3.8674816337930010217e+32, tol=PTOL) + assert ae(v.imag, 3.0540709639658071041e+32, tol=PTOL) + v = fp.e1((-1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) + assert ae(v, (20.880034621432138988 + 1.8157749894560994861j), tol=ATOL) + assert ae(v.real, 20.880034621432138988, tol=PTOL) + assert ae(v.imag, 1.8157749894560994861, tol=PTOL) + v = fp.e1((-0.25 - 1.0j)) + assert ae(v, (-0.59066621214766308594 + 0.74474454765205036972j), tol=ATOL) + assert ae(v.real, -0.59066621214766308594, tol=PTOL) + assert ae(v.imag, 0.74474454765205036972, tol=PTOL) + v = fp.e1((-1.0 - 4.0j)) + assert ae(v, (0.49739047283060471093 - 0.41543605404038863174j), tol=ATOL) + assert ae(v.real, 0.49739047283060471093, tol=PTOL) + assert ae(v.imag, -0.41543605404038863174, tol=PTOL) + v = fp.e1((-2.0 - 8.0j)) + assert ae(v, (-0.8705211147733730969 - 0.24099328498605539667j), tol=ATOL) + assert ae(v.real, -0.8705211147733730969, tol=PTOL) + assert ae(v.imag, -0.24099328498605539667, tol=PTOL) + v = fp.e1((-5.0 - 20.0j)) + assert ae(v, (-7.0789514293925893007 + 1.6102177171960790536j), tol=ATOL) + assert ae(v.real, -7.0789514293925893007, tol=PTOL) + assert ae(v.imag, 1.6102177171960790536, tol=PTOL) + v = fp.e1((-20.0 - 80.0j)) + assert ae(v, (5855431.4907298084434 + 720920.93315409165707j), tol=ATOL) + assert ae(v.real, 5855431.4907298084434, tol=PTOL) + assert ae(v.imag, 720920.93315409165707, tol=PTOL) + v = fp.e1((-30.0 - 120.0j)) + assert ae(v, (-65402491644.703470747 + 56697658399.657460294j), tol=ATOL) + assert ae(v.real, -65402491644.703470747, tol=PTOL) + assert ae(v.imag, 56697658399.657460294, tol=PTOL) + v = fp.e1((-40.0 - 160.0j)) + assert ae(v, (25504929379604.776769 - 1429035198630573.2463j), tol=ATOL) + assert ae(v.real, 25504929379604.776769, tol=PTOL) + assert ae(v.imag, -1429035198630573.2463, tol=PTOL) + v = fp.e1((-50.0 - 200.0j)) + assert ae(v, (18437746526988116954.0 + 17146362239046152345.0j), tol=ATOL) + assert ae(v.real, 18437746526988116954.0, tol=PTOL) + assert ae(v.imag, 17146362239046152345.0, tol=PTOL) + v = fp.e1((-80.0 - 320.0j)) + assert ae(v, (3.3464697299634526706e+31 + 1.6473152633843023919e+32j), tol=ATOL) + assert ae(v.real, 3.3464697299634526706e+31, tol=PTOL) + assert ae(v.imag, 1.6473152633843023919e+32, tol=PTOL) + v = fp.e1((0.0 - 1.1641532182693481445e-10j)) + assert ae(v, (22.29664129357666235 + 1.5707963266784812974j), tol=ATOL) + assert ae(v.real, 22.29664129357666235, tol=PTOL) + assert ae(v.imag, 1.5707963266784812974, tol=PTOL) + v = fp.e1((0.0 - 0.25j)) + assert ae(v, (0.82466306258094565309 + 1.3216627564751394551j), tol=ATOL) + assert ae(v.real, 0.82466306258094565309, tol=PTOL) + assert ae(v.imag, 1.3216627564751394551, tol=PTOL) + v = fp.e1((0.0 - 1.0j)) + assert ae(v, (-0.33740392290096813466 + 0.62471325642771360429j), tol=ATOL) + assert ae(v.real, -0.33740392290096813466, tol=PTOL) + assert ae(v.imag, 0.62471325642771360429, tol=PTOL) + v = fp.e1((0.0 - 2.0j)) + assert ae(v, (-0.4229808287748649957 - 0.034616650007798229345j), tol=ATOL) + assert ae(v.real, -0.4229808287748649957, tol=PTOL) + assert ae(v.imag, -0.034616650007798229345, tol=PTOL) + v = fp.e1((0.0 - 5.0j)) + assert ae(v, (0.19002974965664387862 + 0.020865081850222481957j), tol=ATOL) + assert ae(v.real, 0.19002974965664387862, tol=PTOL) + assert ae(v.imag, 0.020865081850222481957, tol=PTOL) + v = fp.e1((0.0 - 20.0j)) + assert ae(v, (-0.04441982084535331654 + 0.022554625751456779068j), tol=ATOL) + assert ae(v.real, -0.04441982084535331654, tol=PTOL) + assert ae(v.imag, 0.022554625751456779068, tol=PTOL) + v = fp.e1((0.0 - 30.0j)) + assert ae(v, (0.033032417282071143779 + 0.0040397867645455082476j), tol=ATOL) + assert ae(v.real, 0.033032417282071143779, tol=PTOL) + assert ae(v.imag, 0.0040397867645455082476, tol=PTOL) + v = fp.e1((0.0 - 40.0j)) + assert ae(v, (-0.019020007896208766962 - 0.016188792559887887544j), tol=ATOL) + assert ae(v.real, -0.019020007896208766962, tol=PTOL) + assert ae(v.imag, -0.016188792559887887544, tol=PTOL) + v = fp.e1((0.0 - 50.0j)) + assert ae(v, (0.0056283863241163054402 + 0.019179254308960724503j), tol=ATOL) + assert ae(v.real, 0.0056283863241163054402, tol=PTOL) + assert ae(v.imag, 0.019179254308960724503, tol=PTOL) + v = fp.e1((0.0 - 80.0j)) + assert ae(v, (0.012402501155070958192 - 0.0015345601175906961199j), tol=ATOL) + assert ae(v.real, 0.012402501155070958192, tol=PTOL) + assert ae(v.imag, -0.0015345601175906961199, tol=PTOL) + v = fp.e1((1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) + assert ae(v, (20.880034621664969632 + 1.3258176632023711778j), tol=ATOL) + assert ae(v.real, 20.880034621664969632, tol=PTOL) + assert ae(v.imag, 1.3258176632023711778, tol=PTOL) + v = fp.e1((0.25 - 1.0j)) + assert ae(v, (-0.16868306393667788761 + 0.4858011885947426971j), tol=ATOL) + assert ae(v.real, -0.16868306393667788761, tol=PTOL) + assert ae(v.imag, 0.4858011885947426971, tol=PTOL) + v = fp.e1((1.0 - 4.0j)) + assert ae(v, (0.03373591813926547318 - 0.073523452241083821877j), tol=ATOL) + assert ae(v.real, 0.03373591813926547318, tol=PTOL) + assert ae(v.imag, -0.073523452241083821877, tol=PTOL) + v = fp.e1((2.0 - 8.0j)) + assert ae(v, (-0.015392833434733785143 + 0.0031747121557605415914j), tol=ATOL) + assert ae(v.real, -0.015392833434733785143, tol=PTOL) + assert ae(v.imag, 0.0031747121557605415914, tol=PTOL) + v = fp.e1((5.0 - 20.0j)) + assert ae(v, (-0.00024419662286542966525 + 0.00021008322966152755674j), tol=ATOL) + assert ae(v.real, -0.00024419662286542966525, tol=PTOL) + assert ae(v.imag, 0.00021008322966152755674, tol=PTOL) + v = fp.e1((20.0 - 80.0j)) + assert ae(v, (2.3255552781051330088e-11 - 8.9463918891349438007e-12j), tol=ATOL) + assert ae(v.real, 2.3255552781051330088e-11, tol=PTOL) + assert ae(v.imag, -8.9463918891349438007e-12, tol=PTOL) + v = fp.e1((30.0 - 120.0j)) + assert ae(v, (-2.7068919097124652332e-16 + 7.0477762411705130239e-16j), tol=ATOL) + assert ae(v.real, -2.7068919097124652332e-16, tol=PTOL) + assert ae(v.imag, 7.0477762411705130239e-16, tol=PTOL) + v = fp.e1((40.0 - 160.0j)) + assert ae(v, (-1.1695597827678024687e-20 - 2.2907401455645736661e-20j), tol=ATOL) + assert ae(v.real, -1.1695597827678024687e-20, tol=PTOL) + assert ae(v.imag, -2.2907401455645736661e-20, tol=PTOL) + v = fp.e1((50.0 - 200.0j)) + assert ae(v, (9.0323746914410162531e-25 + 2.3950601790033530935e-25j), tol=ATOL) + assert ae(v.real, 9.0323746914410162531e-25, tol=PTOL) + assert ae(v.imag, 2.3950601790033530935e-25, tol=PTOL) + v = fp.e1((80.0 - 320.0j)) + assert ae(v, (3.4819106748728063576e-38 + 4.215653005615772724e-38j), tol=ATOL) + assert ae(v.real, 3.4819106748728063576e-38, tol=PTOL) + assert ae(v.imag, 4.215653005615772724e-38, tol=PTOL) + v = fp.e1((1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (21.950067703413105017 + 0.7853981632810329878j), tol=ATOL) + assert ae(v.real, 21.950067703413105017, tol=PTOL) + assert ae(v.imag, 0.7853981632810329878, tol=PTOL) + v = fp.e1((0.25 - 0.25j)) + assert ae(v, (0.71092525792923287894 + 0.56491812441304194711j), tol=ATOL) + assert ae(v.real, 0.71092525792923287894, tol=PTOL) + assert ae(v.imag, 0.56491812441304194711, tol=PTOL) + v = fp.e1((1.0 - 1.0j)) + assert ae(v, (0.00028162445198141832551 + 0.17932453503935894015j), tol=ATOL) + assert ae(v.real, 0.00028162445198141832551, tol=PTOL) + assert ae(v.imag, 0.17932453503935894015, tol=PTOL) + v = fp.e1((2.0 - 2.0j)) + assert ae(v, (-0.033767089606562004246 + 0.018599414169750541925j), tol=ATOL) + assert ae(v.real, -0.033767089606562004246, tol=PTOL) + assert ae(v.imag, 0.018599414169750541925, tol=PTOL) + v = fp.e1((5.0 - 5.0j)) + assert ae(v, (0.0007266506660356393891 - 0.00047102780163522245054j), tol=ATOL) + assert ae(v.real, 0.0007266506660356393891, tol=PTOL) + assert ae(v.imag, -0.00047102780163522245054, tol=PTOL) + v = fp.e1((20.0 - 20.0j)) + assert ae(v, (-2.3824537449367396579e-11 + 6.6969873156525615158e-11j), tol=ATOL) + assert ae(v.real, -2.3824537449367396579e-11, tol=PTOL) + assert ae(v.imag, 6.6969873156525615158e-11, tol=PTOL) + v = fp.e1((30.0 - 30.0j)) + assert ae(v, (1.7316045841744061617e-15 - 1.3065678019487308689e-15j), tol=ATOL) + assert ae(v.real, 1.7316045841744061617e-15, tol=PTOL) + assert ae(v.imag, -1.3065678019487308689e-15, tol=PTOL) + v = fp.e1((40.0 - 40.0j)) + assert ae(v, (-7.4001043002899232182e-20 + 4.991847855336816304e-21j), tol=ATOL) + assert ae(v.real, -7.4001043002899232182e-20, tol=PTOL) + assert ae(v.imag, 4.991847855336816304e-21, tol=PTOL) + v = fp.e1((50.0 - 50.0j)) + assert ae(v, (2.3566128324644641219e-24 + 1.3188326726201614778e-24j), tol=ATOL) + assert ae(v.real, 2.3566128324644641219e-24, tol=PTOL) + assert ae(v.imag, 1.3188326726201614778e-24, tol=PTOL) + v = fp.e1((80.0 - 80.0j)) + assert ae(v, (9.8279750572186526673e-38 - 1.243952841288868831e-37j), tol=ATOL) + assert ae(v.real, 9.8279750572186526673e-38, tol=PTOL) + assert ae(v.imag, -1.243952841288868831e-37, tol=PTOL) + v = fp.e1((4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (20.880034622014215597 + 0.24497866301044883237j), tol=ATOL) + assert ae(v.real, 20.880034622014215597, tol=PTOL) + assert ae(v.imag, 0.24497866301044883237, tol=PTOL) + v = fp.e1((1.0 - 0.25j)) + assert ae(v, (0.19731063945004229095 + 0.087366045774299963672j), tol=ATOL) + assert ae(v.real, 0.19731063945004229095, tol=PTOL) + assert ae(v.imag, 0.087366045774299963672, tol=PTOL) + v = fp.e1((4.0 - 1.0j)) + assert ae(v, (0.0013106173980145506944 + 0.0034542480199350626699j), tol=ATOL) + assert ae(v.real, 0.0013106173980145506944, tol=PTOL) + assert ae(v.imag, 0.0034542480199350626699, tol=PTOL) + v = fp.e1((8.0 - 2.0j)) + assert ae(v, (-0.000022278049065270225945 + 0.000029191940456521555288j), tol=ATOL) + assert ae(v.real, -0.000022278049065270225945, tol=PTOL) + assert ae(v.imag, 0.000029191940456521555288, tol=PTOL) + v = fp.e1((20.0 - 5.0j)) + assert ae(v, (4.7711374515765346894e-11 - 8.2902652405126947359e-11j), tol=ATOL) + assert ae(v.real, 4.7711374515765346894e-11, tol=PTOL) + assert ae(v.imag, -8.2902652405126947359e-11, tol=PTOL) + v = fp.e1((80.0 - 20.0j)) + assert ae(v, (3.8353473865788235787e-38 + 2.129247592349605139e-37j), tol=ATOL) + assert ae(v.real, 3.8353473865788235787e-38, tol=PTOL) + assert ae(v.imag, 2.129247592349605139e-37, tol=PTOL) + v = fp.e1((120.0 - 30.0j)) + assert ae(v, (2.3836002337480334716e-55 - 5.6704043587126198306e-55j), tol=ATOL) + assert ae(v.real, 2.3836002337480334716e-55, tol=PTOL) + assert ae(v.imag, -5.6704043587126198306e-55, tol=PTOL) + v = fp.e1((160.0 - 40.0j)) + assert ae(v, (-1.6238022898654510661e-72 + 1.104172355572287367e-72j), tol=ATOL) + assert ae(v.real, -1.6238022898654510661e-72, tol=PTOL) + assert ae(v.imag, 1.104172355572287367e-72, tol=PTOL) + v = fp.e1((200.0 - 50.0j)) + assert ae(v, (6.6800061461666228487e-90 - 1.4473816083541016115e-91j), tol=ATOL) + assert ae(v.real, 6.6800061461666228487e-90, tol=PTOL) + assert ae(v.imag, -1.4473816083541016115e-91, tol=PTOL) + v = fp.e1((320.0 - 80.0j)) + assert ae(v, (4.2737871527778786157e-143 - 3.1789935525785660314e-142j), tol=ATOL) + assert ae(v.real, 4.2737871527778786157e-143, tol=PTOL) + assert ae(v.imag, -3.1789935525785660314e-142, tol=PTOL) + v = fp.ei(1.1641532182693481445e-10) + assert ae(v, -22.296641293460247028, tol=ATOL) + assert type(v) is float + v = fp.ei(0.25) + assert ae(v, -0.54254326466191372953, tol=ATOL) + assert type(v) is float + v = fp.ei(1.0) + assert ae(v, 1.8951178163559367555, tol=ATOL) + assert type(v) is float + v = fp.ei(2.0) + assert ae(v, 4.9542343560018901634, tol=ATOL) + assert type(v) is float + v = fp.ei(5.0) + assert ae(v, 40.185275355803177455, tol=ATOL) + assert type(v) is float + v = fp.ei(20.0) + assert ae(v, 25615652.66405658882, tol=ATOL) + assert type(v) is float + v = fp.ei(30.0) + assert ae(v, 368973209407.27419706, tol=ATOL) + assert type(v) is float + v = fp.ei(40.0) + assert ae(v, 6039718263611241.5784, tol=ATOL) + assert type(v) is float + v = fp.ei(50.0) + assert ae(v, 1.0585636897131690963e+20, tol=ATOL) + assert type(v) is float + v = fp.ei(80.0) + assert ae(v, 7.0146000049047999696e+32, tol=ATOL) + assert type(v) is float + v = fp.ei((1.1641532182693481445e-10 + 0.0j)) + assert ae(v, (-22.296641293460247028 + 0.0j), tol=ATOL) + assert ae(v.real, -22.296641293460247028, tol=PTOL) + assert v.imag == 0 + v = fp.ei((0.25 + 0.0j)) + assert ae(v, (-0.54254326466191372953 + 0.0j), tol=ATOL) + assert ae(v.real, -0.54254326466191372953, tol=PTOL) + assert v.imag == 0 + v = fp.ei((1.0 + 0.0j)) + assert ae(v, (1.8951178163559367555 + 0.0j), tol=ATOL) + assert ae(v.real, 1.8951178163559367555, tol=PTOL) + assert v.imag == 0 + v = fp.ei((2.0 + 0.0j)) + assert ae(v, (4.9542343560018901634 + 0.0j), tol=ATOL) + assert ae(v.real, 4.9542343560018901634, tol=PTOL) + assert v.imag == 0 + v = fp.ei((5.0 + 0.0j)) + assert ae(v, (40.185275355803177455 + 0.0j), tol=ATOL) + assert ae(v.real, 40.185275355803177455, tol=PTOL) + assert v.imag == 0 + v = fp.ei((20.0 + 0.0j)) + assert ae(v, (25615652.66405658882 + 0.0j), tol=ATOL) + assert ae(v.real, 25615652.66405658882, tol=PTOL) + assert v.imag == 0 + v = fp.ei((30.0 + 0.0j)) + assert ae(v, (368973209407.27419706 + 0.0j), tol=ATOL) + assert ae(v.real, 368973209407.27419706, tol=PTOL) + assert v.imag == 0 + v = fp.ei((40.0 + 0.0j)) + assert ae(v, (6039718263611241.5784 + 0.0j), tol=ATOL) + assert ae(v.real, 6039718263611241.5784, tol=PTOL) + assert v.imag == 0 + v = fp.ei((50.0 + 0.0j)) + assert ae(v, (1.0585636897131690963e+20 + 0.0j), tol=ATOL) + assert ae(v.real, 1.0585636897131690963e+20, tol=PTOL) + assert v.imag == 0 + v = fp.ei((80.0 + 0.0j)) + assert ae(v, (7.0146000049047999696e+32 + 0.0j), tol=ATOL) + assert ae(v.real, 7.0146000049047999696e+32, tol=PTOL) + assert v.imag == 0 + v = fp.ei((4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (-20.880034621082893023 + 0.24497866324327947603j), tol=ATOL) + assert ae(v.real, -20.880034621082893023, tol=PTOL) + assert ae(v.imag, 0.24497866324327947603, tol=PTOL) + v = fp.ei((1.0 + 0.25j)) + assert ae(v, (1.8942716983721074932 + 0.67268237088273915854j), tol=ATOL) + assert ae(v.real, 1.8942716983721074932, tol=PTOL) + assert ae(v.imag, 0.67268237088273915854, tol=PTOL) + v = fp.ei((4.0 + 1.0j)) + assert ae(v, (14.806699492675420438 + 12.280015176673582616j), tol=ATOL) + assert ae(v.real, 14.806699492675420438, tol=PTOL) + assert ae(v.imag, 12.280015176673582616, tol=PTOL) + v = fp.ei((8.0 + 2.0j)) + assert ae(v, (-54.633252667426386294 + 416.34477429173650012j), tol=ATOL) + assert ae(v.real, -54.633252667426386294, tol=PTOL) + assert ae(v.imag, 416.34477429173650012, tol=PTOL) + v = fp.ei((20.0 + 5.0j)) + assert ae(v, (711836.97165402624643 - 24745247.798103247366j), tol=ATOL) + assert ae(v.real, 711836.97165402624643, tol=PTOL) + assert ae(v.imag, -24745247.798103247366, tol=PTOL) + v = fp.ei((80.0 + 20.0j)) + assert ae(v, (4.2139911108612653091e+32 + 5.3367124741918251637e+32j), tol=ATOL) + assert ae(v.real, 4.2139911108612653091e+32, tol=PTOL) + assert ae(v.imag, 5.3367124741918251637e+32, tol=PTOL) + v = fp.ei((120.0 + 30.0j)) + assert ae(v, (-9.7760616203707508892e+48 - 1.058257682317195792e+50j), tol=ATOL) + assert ae(v.real, -9.7760616203707508892e+48, tol=PTOL) + assert ae(v.imag, -1.058257682317195792e+50, tol=PTOL) + v = fp.ei((160.0 + 40.0j)) + assert ae(v, (-8.7065541466623638861e+66 + 1.6577106725141739889e+67j), tol=ATOL) + assert ae(v.real, -8.7065541466623638861e+66, tol=PTOL) + assert ae(v.imag, 1.6577106725141739889e+67, tol=PTOL) + v = fp.ei((200.0 + 50.0j)) + assert ae(v, (3.070744996327018106e+84 - 1.7243244846769415903e+84j), tol=ATOL) + assert ae(v.real, 3.070744996327018106e+84, tol=PTOL) + assert ae(v.imag, -1.7243244846769415903e+84, tol=PTOL) + v = fp.ei((320.0 + 80.0j)) + assert ae(v, (-9.9960598637998647276e+135 - 2.6855081527595608863e+136j), tol=ATOL) + assert ae(v.real, -9.9960598637998647276e+135, tol=PTOL) + assert ae(v.imag, -2.6855081527595608863e+136, tol=PTOL) + v = fp.ei((1.1641532182693481445e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (-21.950067703180274374 + 0.78539816351386363145j), tol=ATOL) + assert ae(v.real, -21.950067703180274374, tol=PTOL) + assert ae(v.imag, 0.78539816351386363145, tol=PTOL) + v = fp.ei((0.25 + 0.25j)) + assert ae(v, (-0.21441047326710323254 + 1.0683772981589995996j), tol=ATOL) + assert ae(v.real, -0.21441047326710323254, tol=PTOL) + assert ae(v.imag, 1.0683772981589995996, tol=PTOL) + v = fp.ei((1.0 + 1.0j)) + assert ae(v, (1.7646259855638540684 + 2.3877698515105224193j), tol=ATOL) + assert ae(v.real, 1.7646259855638540684, tol=PTOL) + assert ae(v.imag, 2.3877698515105224193, tol=PTOL) + v = fp.ei((2.0 + 2.0j)) + assert ae(v, (1.8920781621855474089 + 5.3169624378326579621j), tol=ATOL) + assert ae(v.real, 1.8920781621855474089, tol=PTOL) + assert ae(v.imag, 5.3169624378326579621, tol=PTOL) + v = fp.ei((5.0 + 5.0j)) + assert ae(v, (-13.470936071475245856 - 15.322492395731230968j), tol=ATOL) + assert ae(v.real, -13.470936071475245856, tol=PTOL) + assert ae(v.imag, -15.322492395731230968, tol=PTOL) + v = fp.ei((20.0 + 20.0j)) + assert ae(v, (16589317.398788971896 + 5831705.4712368307104j), tol=ATOL) + assert ae(v.real, 16589317.398788971896, tol=PTOL) + assert ae(v.imag, 5831705.4712368307104, tol=PTOL) + v = fp.ei((30.0 + 30.0j)) + assert ae(v, (-154596484273.69322527 - 204179357834.2723043j), tol=ATOL) + assert ae(v.real, -154596484273.69322527, tol=PTOL) + assert ae(v.imag, -204179357834.2723043, tol=PTOL) + v = fp.ei((40.0 + 40.0j)) + assert ae(v, (287512180321448.45408 + 4203502407932318.1156j), tol=ATOL) + assert ae(v.real, 287512180321448.45408, tol=PTOL) + assert ae(v.imag, 4203502407932318.1156, tol=PTOL) + v = fp.ei((50.0 + 50.0j)) + assert ae(v, (36128528616649268826.0 - 64648801861338741960.0j), tol=ATOL) + assert ae(v.real, 36128528616649268826.0, tol=PTOL) + assert ae(v.imag, -64648801861338741960.0, tol=PTOL) + v = fp.ei((80.0 + 80.0j)) + assert ae(v, (-3.8674816337930010217e+32 - 3.0540709639658071041e+32j), tol=ATOL) + assert ae(v.real, -3.8674816337930010217e+32, tol=PTOL) + assert ae(v.imag, -3.0540709639658071041e+32, tol=PTOL) + v = fp.ei((1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) + assert ae(v, (-20.880034621432138988 + 1.3258176641336937524j), tol=ATOL) + assert ae(v.real, -20.880034621432138988, tol=PTOL) + assert ae(v.imag, 1.3258176641336937524, tol=PTOL) + v = fp.ei((0.25 + 1.0j)) + assert ae(v, (0.59066621214766308594 + 2.3968481059377428687j), tol=ATOL) + assert ae(v.real, 0.59066621214766308594, tol=PTOL) + assert ae(v.imag, 2.3968481059377428687, tol=PTOL) + v = fp.ei((1.0 + 4.0j)) + assert ae(v, (-0.49739047283060471093 + 3.5570287076301818702j), tol=ATOL) + assert ae(v.real, -0.49739047283060471093, tol=PTOL) + assert ae(v.imag, 3.5570287076301818702, tol=PTOL) + v = fp.ei((2.0 + 8.0j)) + assert ae(v, (0.8705211147733730969 + 3.3825859385758486351j), tol=ATOL) + assert ae(v.real, 0.8705211147733730969, tol=PTOL) + assert ae(v.imag, 3.3825859385758486351, tol=PTOL) + v = fp.ei((5.0 + 20.0j)) + assert ae(v, (7.0789514293925893007 + 1.5313749363937141849j), tol=ATOL) + assert ae(v.real, 7.0789514293925893007, tol=PTOL) + assert ae(v.imag, 1.5313749363937141849, tol=PTOL) + v = fp.ei((20.0 + 80.0j)) + assert ae(v, (-5855431.4907298084434 - 720917.79156143806727j), tol=ATOL) + assert ae(v.real, -5855431.4907298084434, tol=PTOL) + assert ae(v.imag, -720917.79156143806727, tol=PTOL) + v = fp.ei((30.0 + 120.0j)) + assert ae(v, (65402491644.703470747 - 56697658396.51586764j), tol=ATOL) + assert ae(v.real, 65402491644.703470747, tol=PTOL) + assert ae(v.imag, -56697658396.51586764, tol=PTOL) + v = fp.ei((40.0 + 160.0j)) + assert ae(v, (-25504929379604.776769 + 1429035198630576.3879j), tol=ATOL) + assert ae(v.real, -25504929379604.776769, tol=PTOL) + assert ae(v.imag, 1429035198630576.3879, tol=PTOL) + v = fp.ei((50.0 + 200.0j)) + assert ae(v, (-18437746526988116954.0 - 17146362239046152342.0j), tol=ATOL) + assert ae(v.real, -18437746526988116954.0, tol=PTOL) + assert ae(v.imag, -17146362239046152342.0, tol=PTOL) + v = fp.ei((80.0 + 320.0j)) + assert ae(v, (-3.3464697299634526706e+31 - 1.6473152633843023919e+32j), tol=ATOL) + assert ae(v.real, -3.3464697299634526706e+31, tol=PTOL) + assert ae(v.imag, -1.6473152633843023919e+32, tol=PTOL) + v = fp.ei((0.0 + 1.1641532182693481445e-10j)) + assert ae(v, (-22.29664129357666235 + 1.5707963269113119411j), tol=ATOL) + assert ae(v.real, -22.29664129357666235, tol=PTOL) + assert ae(v.imag, 1.5707963269113119411, tol=PTOL) + v = fp.ei((0.0 + 0.25j)) + assert ae(v, (-0.82466306258094565309 + 1.8199298971146537833j), tol=ATOL) + assert ae(v.real, -0.82466306258094565309, tol=PTOL) + assert ae(v.imag, 1.8199298971146537833, tol=PTOL) + v = fp.ei((0.0 + 1.0j)) + assert ae(v, (0.33740392290096813466 + 2.5168793971620796342j), tol=ATOL) + assert ae(v.real, 0.33740392290096813466, tol=PTOL) + assert ae(v.imag, 2.5168793971620796342, tol=PTOL) + v = fp.ei((0.0 + 2.0j)) + assert ae(v, (0.4229808287748649957 + 3.1762093035975914678j), tol=ATOL) + assert ae(v.real, 0.4229808287748649957, tol=PTOL) + assert ae(v.imag, 3.1762093035975914678, tol=PTOL) + v = fp.ei((0.0 + 5.0j)) + assert ae(v, (-0.19002974965664387862 + 3.1207275717395707565j), tol=ATOL) + assert ae(v.real, -0.19002974965664387862, tol=PTOL) + assert ae(v.imag, 3.1207275717395707565, tol=PTOL) + v = fp.ei((0.0 + 20.0j)) + assert ae(v, (0.04441982084535331654 + 3.1190380278383364594j), tol=ATOL) + assert ae(v.real, 0.04441982084535331654, tol=PTOL) + assert ae(v.imag, 3.1190380278383364594, tol=PTOL) + v = fp.ei((0.0 + 30.0j)) + assert ae(v, (-0.033032417282071143779 + 3.1375528668252477302j), tol=ATOL) + assert ae(v.real, -0.033032417282071143779, tol=PTOL) + assert ae(v.imag, 3.1375528668252477302, tol=PTOL) + v = fp.ei((0.0 + 40.0j)) + assert ae(v, (0.019020007896208766962 + 3.157781446149681126j), tol=ATOL) + assert ae(v.real, 0.019020007896208766962, tol=PTOL) + assert ae(v.imag, 3.157781446149681126, tol=PTOL) + v = fp.ei((0.0 + 50.0j)) + assert ae(v, (-0.0056283863241163054402 + 3.122413399280832514j), tol=ATOL) + assert ae(v.real, -0.0056283863241163054402, tol=PTOL) + assert ae(v.imag, 3.122413399280832514, tol=PTOL) + v = fp.ei((0.0 + 80.0j)) + assert ae(v, (-0.012402501155070958192 + 3.1431272137073839346j), tol=ATOL) + assert ae(v.real, -0.012402501155070958192, tol=PTOL) + assert ae(v.imag, 3.1431272137073839346, tol=PTOL) + v = fp.ei((-1.1641532182693481445e-10 + 4.6566128730773925781e-10j)) + assert ae(v, (-20.880034621664969632 + 1.8157749903874220607j), tol=ATOL) + assert ae(v.real, -20.880034621664969632, tol=PTOL) + assert ae(v.imag, 1.8157749903874220607, tol=PTOL) + v = fp.ei((-0.25 + 1.0j)) + assert ae(v, (0.16868306393667788761 + 2.6557914649950505414j), tol=ATOL) + assert ae(v.real, 0.16868306393667788761, tol=PTOL) + assert ae(v.imag, 2.6557914649950505414, tol=PTOL) + v = fp.ei((-1.0 + 4.0j)) + assert ae(v, (-0.03373591813926547318 + 3.2151161058308770603j), tol=ATOL) + assert ae(v.real, -0.03373591813926547318, tol=PTOL) + assert ae(v.imag, 3.2151161058308770603, tol=PTOL) + v = fp.ei((-2.0 + 8.0j)) + assert ae(v, (0.015392833434733785143 + 3.1384179414340326969j), tol=ATOL) + assert ae(v.real, 0.015392833434733785143, tol=PTOL) + assert ae(v.imag, 3.1384179414340326969, tol=PTOL) + v = fp.ei((-5.0 + 20.0j)) + assert ae(v, (0.00024419662286542966525 + 3.1413825703601317109j), tol=ATOL) + assert ae(v.real, 0.00024419662286542966525, tol=PTOL) + assert ae(v.imag, 3.1413825703601317109, tol=PTOL) + v = fp.ei((-20.0 + 80.0j)) + assert ae(v, (-2.3255552781051330088e-11 + 3.1415926535987396304j), tol=ATOL) + assert ae(v.real, -2.3255552781051330088e-11, tol=PTOL) + assert ae(v.imag, 3.1415926535987396304, tol=PTOL) + v = fp.ei((-30.0 + 120.0j)) + assert ae(v, (2.7068919097124652332e-16 + 3.1415926535897925337j), tol=ATOL) + assert ae(v.real, 2.7068919097124652332e-16, tol=PTOL) + assert ae(v.imag, 3.1415926535897925337, tol=PTOL) + v = fp.ei((-40.0 + 160.0j)) + assert ae(v, (1.1695597827678024687e-20 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 1.1695597827678024687e-20, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-50.0 + 200.0j)) + assert ae(v, (-9.0323746914410162531e-25 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -9.0323746914410162531e-25, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-80.0 + 320.0j)) + assert ae(v, (-3.4819106748728063576e-38 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -3.4819106748728063576e-38, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-4.6566128730773925781e-10 + 1.1641532182693481445e-10j)) + assert ae(v, (-20.880034622014215597 + 2.8966139905793444061j), tol=ATOL) + assert ae(v.real, -20.880034622014215597, tol=PTOL) + assert ae(v.imag, 2.8966139905793444061, tol=PTOL) + v = fp.ei((-1.0 + 0.25j)) + assert ae(v, (-0.19731063945004229095 + 3.0542266078154932748j), tol=ATOL) + assert ae(v.real, -0.19731063945004229095, tol=PTOL) + assert ae(v.imag, 3.0542266078154932748, tol=PTOL) + v = fp.ei((-4.0 + 1.0j)) + assert ae(v, (-0.0013106173980145506944 + 3.1381384055698581758j), tol=ATOL) + assert ae(v.real, -0.0013106173980145506944, tol=PTOL) + assert ae(v.imag, 3.1381384055698581758, tol=PTOL) + v = fp.ei((-8.0 + 2.0j)) + assert ae(v, (0.000022278049065270225945 + 3.1415634616493367169j), tol=ATOL) + assert ae(v.real, 0.000022278049065270225945, tol=PTOL) + assert ae(v.imag, 3.1415634616493367169, tol=PTOL) + v = fp.ei((-20.0 + 5.0j)) + assert ae(v, (-4.7711374515765346894e-11 + 3.1415926536726958909j), tol=ATOL) + assert ae(v.real, -4.7711374515765346894e-11, tol=PTOL) + assert ae(v.imag, 3.1415926536726958909, tol=PTOL) + v = fp.ei((-80.0 + 20.0j)) + assert ae(v, (-3.8353473865788235787e-38 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -3.8353473865788235787e-38, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-120.0 + 30.0j)) + assert ae(v, (-2.3836002337480334716e-55 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -2.3836002337480334716e-55, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-160.0 + 40.0j)) + assert ae(v, (1.6238022898654510661e-72 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 1.6238022898654510661e-72, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-200.0 + 50.0j)) + assert ae(v, (-6.6800061461666228487e-90 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -6.6800061461666228487e-90, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei((-320.0 + 80.0j)) + assert ae(v, (-4.2737871527778786157e-143 + 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -4.2737871527778786157e-143, tol=PTOL) + assert ae(v.imag, 3.1415926535897932385, tol=PTOL) + v = fp.ei(-1.1641532182693481445e-10) + assert ae(v, -22.296641293693077672, tol=ATOL) + assert type(v) is float + v = fp.ei(-0.25) + assert ae(v, -1.0442826344437381945, tol=ATOL) + assert type(v) is float + v = fp.ei(-1.0) + assert ae(v, -0.21938393439552027368, tol=ATOL) + assert type(v) is float + v = fp.ei(-2.0) + assert ae(v, -0.048900510708061119567, tol=ATOL) + assert type(v) is float + v = fp.ei(-5.0) + assert ae(v, -0.0011482955912753257973, tol=ATOL) + assert type(v) is float + v = fp.ei(-20.0) + assert ae(v, -9.8355252906498816904e-11, tol=ATOL) + assert type(v) is float + v = fp.ei(-30.0) + assert ae(v, -3.0215520106888125448e-15, tol=ATOL) + assert type(v) is float + v = fp.ei(-40.0) + assert ae(v, -1.0367732614516569722e-19, tol=ATOL) + assert type(v) is float + v = fp.ei(-50.0) + assert ae(v, -3.7832640295504590187e-24, tol=ATOL) + assert type(v) is float + v = fp.ei(-80.0) + assert ae(v, -2.2285432586884729112e-37, tol=ATOL) + assert type(v) is float + v = fp.ei((-1.1641532182693481445e-10 + 0.0j)) + assert ae(v, (-22.296641293693077672 + 0.0j), tol=ATOL) + assert ae(v.real, -22.296641293693077672, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-0.25 + 0.0j)) + assert ae(v, (-1.0442826344437381945 + 0.0j), tol=ATOL) + assert ae(v.real, -1.0442826344437381945, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-1.0 + 0.0j)) + assert ae(v, (-0.21938393439552027368 + 0.0j), tol=ATOL) + assert ae(v.real, -0.21938393439552027368, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-2.0 + 0.0j)) + assert ae(v, (-0.048900510708061119567 + 0.0j), tol=ATOL) + assert ae(v.real, -0.048900510708061119567, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-5.0 + 0.0j)) + assert ae(v, (-0.0011482955912753257973 + 0.0j), tol=ATOL) + assert ae(v.real, -0.0011482955912753257973, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-20.0 + 0.0j)) + assert ae(v, (-9.8355252906498816904e-11 + 0.0j), tol=ATOL) + assert ae(v.real, -9.8355252906498816904e-11, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-30.0 + 0.0j)) + assert ae(v, (-3.0215520106888125448e-15 + 0.0j), tol=ATOL) + assert ae(v.real, -3.0215520106888125448e-15, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-40.0 + 0.0j)) + assert ae(v, (-1.0367732614516569722e-19 + 0.0j), tol=ATOL) + assert ae(v.real, -1.0367732614516569722e-19, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-50.0 + 0.0j)) + assert ae(v, (-3.7832640295504590187e-24 + 0.0j), tol=ATOL) + assert ae(v.real, -3.7832640295504590187e-24, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-80.0 + 0.0j)) + assert ae(v, (-2.2285432586884729112e-37 + 0.0j), tol=ATOL) + assert ae(v.real, -2.2285432586884729112e-37, tol=PTOL) + assert v.imag == 0 + v = fp.ei((-4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (-20.880034622014215597 - 2.8966139905793444061j), tol=ATOL) + assert ae(v.real, -20.880034622014215597, tol=PTOL) + assert ae(v.imag, -2.8966139905793444061, tol=PTOL) + v = fp.ei((-1.0 - 0.25j)) + assert ae(v, (-0.19731063945004229095 - 3.0542266078154932748j), tol=ATOL) + assert ae(v.real, -0.19731063945004229095, tol=PTOL) + assert ae(v.imag, -3.0542266078154932748, tol=PTOL) + v = fp.ei((-4.0 - 1.0j)) + assert ae(v, (-0.0013106173980145506944 - 3.1381384055698581758j), tol=ATOL) + assert ae(v.real, -0.0013106173980145506944, tol=PTOL) + assert ae(v.imag, -3.1381384055698581758, tol=PTOL) + v = fp.ei((-8.0 - 2.0j)) + assert ae(v, (0.000022278049065270225945 - 3.1415634616493367169j), tol=ATOL) + assert ae(v.real, 0.000022278049065270225945, tol=PTOL) + assert ae(v.imag, -3.1415634616493367169, tol=PTOL) + v = fp.ei((-20.0 - 5.0j)) + assert ae(v, (-4.7711374515765346894e-11 - 3.1415926536726958909j), tol=ATOL) + assert ae(v.real, -4.7711374515765346894e-11, tol=PTOL) + assert ae(v.imag, -3.1415926536726958909, tol=PTOL) + v = fp.ei((-80.0 - 20.0j)) + assert ae(v, (-3.8353473865788235787e-38 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -3.8353473865788235787e-38, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-120.0 - 30.0j)) + assert ae(v, (-2.3836002337480334716e-55 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -2.3836002337480334716e-55, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-160.0 - 40.0j)) + assert ae(v, (1.6238022898654510661e-72 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 1.6238022898654510661e-72, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-200.0 - 50.0j)) + assert ae(v, (-6.6800061461666228487e-90 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -6.6800061461666228487e-90, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-320.0 - 80.0j)) + assert ae(v, (-4.2737871527778786157e-143 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -4.2737871527778786157e-143, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (-21.950067703413105017 - 2.3561944903087602507j), tol=ATOL) + assert ae(v.real, -21.950067703413105017, tol=PTOL) + assert ae(v.imag, -2.3561944903087602507, tol=PTOL) + v = fp.ei((-0.25 - 0.25j)) + assert ae(v, (-0.71092525792923287894 - 2.5766745291767512913j), tol=ATOL) + assert ae(v.real, -0.71092525792923287894, tol=PTOL) + assert ae(v.imag, -2.5766745291767512913, tol=PTOL) + v = fp.ei((-1.0 - 1.0j)) + assert ae(v, (-0.00028162445198141832551 - 2.9622681185504342983j), tol=ATOL) + assert ae(v.real, -0.00028162445198141832551, tol=PTOL) + assert ae(v.imag, -2.9622681185504342983, tol=PTOL) + v = fp.ei((-2.0 - 2.0j)) + assert ae(v, (0.033767089606562004246 - 3.1229932394200426965j), tol=ATOL) + assert ae(v.real, 0.033767089606562004246, tol=PTOL) + assert ae(v.imag, -3.1229932394200426965, tol=PTOL) + v = fp.ei((-5.0 - 5.0j)) + assert ae(v, (-0.0007266506660356393891 - 3.1420636813914284609j), tol=ATOL) + assert ae(v.real, -0.0007266506660356393891, tol=PTOL) + assert ae(v.imag, -3.1420636813914284609, tol=PTOL) + v = fp.ei((-20.0 - 20.0j)) + assert ae(v, (2.3824537449367396579e-11 - 3.1415926535228233653j), tol=ATOL) + assert ae(v.real, 2.3824537449367396579e-11, tol=PTOL) + assert ae(v.imag, -3.1415926535228233653, tol=PTOL) + v = fp.ei((-30.0 - 30.0j)) + assert ae(v, (-1.7316045841744061617e-15 - 3.141592653589794545j), tol=ATOL) + assert ae(v.real, -1.7316045841744061617e-15, tol=PTOL) + assert ae(v.imag, -3.141592653589794545, tol=PTOL) + v = fp.ei((-40.0 - 40.0j)) + assert ae(v, (7.4001043002899232182e-20 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 7.4001043002899232182e-20, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-50.0 - 50.0j)) + assert ae(v, (-2.3566128324644641219e-24 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -2.3566128324644641219e-24, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-80.0 - 80.0j)) + assert ae(v, (-9.8279750572186526673e-38 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -9.8279750572186526673e-38, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) + assert ae(v, (-20.880034621664969632 - 1.8157749903874220607j), tol=ATOL) + assert ae(v.real, -20.880034621664969632, tol=PTOL) + assert ae(v.imag, -1.8157749903874220607, tol=PTOL) + v = fp.ei((-0.25 - 1.0j)) + assert ae(v, (0.16868306393667788761 - 2.6557914649950505414j), tol=ATOL) + assert ae(v.real, 0.16868306393667788761, tol=PTOL) + assert ae(v.imag, -2.6557914649950505414, tol=PTOL) + v = fp.ei((-1.0 - 4.0j)) + assert ae(v, (-0.03373591813926547318 - 3.2151161058308770603j), tol=ATOL) + assert ae(v.real, -0.03373591813926547318, tol=PTOL) + assert ae(v.imag, -3.2151161058308770603, tol=PTOL) + v = fp.ei((-2.0 - 8.0j)) + assert ae(v, (0.015392833434733785143 - 3.1384179414340326969j), tol=ATOL) + assert ae(v.real, 0.015392833434733785143, tol=PTOL) + assert ae(v.imag, -3.1384179414340326969, tol=PTOL) + v = fp.ei((-5.0 - 20.0j)) + assert ae(v, (0.00024419662286542966525 - 3.1413825703601317109j), tol=ATOL) + assert ae(v.real, 0.00024419662286542966525, tol=PTOL) + assert ae(v.imag, -3.1413825703601317109, tol=PTOL) + v = fp.ei((-20.0 - 80.0j)) + assert ae(v, (-2.3255552781051330088e-11 - 3.1415926535987396304j), tol=ATOL) + assert ae(v.real, -2.3255552781051330088e-11, tol=PTOL) + assert ae(v.imag, -3.1415926535987396304, tol=PTOL) + v = fp.ei((-30.0 - 120.0j)) + assert ae(v, (2.7068919097124652332e-16 - 3.1415926535897925337j), tol=ATOL) + assert ae(v.real, 2.7068919097124652332e-16, tol=PTOL) + assert ae(v.imag, -3.1415926535897925337, tol=PTOL) + v = fp.ei((-40.0 - 160.0j)) + assert ae(v, (1.1695597827678024687e-20 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, 1.1695597827678024687e-20, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-50.0 - 200.0j)) + assert ae(v, (-9.0323746914410162531e-25 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -9.0323746914410162531e-25, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((-80.0 - 320.0j)) + assert ae(v, (-3.4819106748728063576e-38 - 3.1415926535897932385j), tol=ATOL) + assert ae(v.real, -3.4819106748728063576e-38, tol=PTOL) + assert ae(v.imag, -3.1415926535897932385, tol=PTOL) + v = fp.ei((0.0 - 1.1641532182693481445e-10j)) + assert ae(v, (-22.29664129357666235 - 1.5707963269113119411j), tol=ATOL) + assert ae(v.real, -22.29664129357666235, tol=PTOL) + assert ae(v.imag, -1.5707963269113119411, tol=PTOL) + v = fp.ei((0.0 - 0.25j)) + assert ae(v, (-0.82466306258094565309 - 1.8199298971146537833j), tol=ATOL) + assert ae(v.real, -0.82466306258094565309, tol=PTOL) + assert ae(v.imag, -1.8199298971146537833, tol=PTOL) + v = fp.ei((0.0 - 1.0j)) + assert ae(v, (0.33740392290096813466 - 2.5168793971620796342j), tol=ATOL) + assert ae(v.real, 0.33740392290096813466, tol=PTOL) + assert ae(v.imag, -2.5168793971620796342, tol=PTOL) + v = fp.ei((0.0 - 2.0j)) + assert ae(v, (0.4229808287748649957 - 3.1762093035975914678j), tol=ATOL) + assert ae(v.real, 0.4229808287748649957, tol=PTOL) + assert ae(v.imag, -3.1762093035975914678, tol=PTOL) + v = fp.ei((0.0 - 5.0j)) + assert ae(v, (-0.19002974965664387862 - 3.1207275717395707565j), tol=ATOL) + assert ae(v.real, -0.19002974965664387862, tol=PTOL) + assert ae(v.imag, -3.1207275717395707565, tol=PTOL) + v = fp.ei((0.0 - 20.0j)) + assert ae(v, (0.04441982084535331654 - 3.1190380278383364594j), tol=ATOL) + assert ae(v.real, 0.04441982084535331654, tol=PTOL) + assert ae(v.imag, -3.1190380278383364594, tol=PTOL) + v = fp.ei((0.0 - 30.0j)) + assert ae(v, (-0.033032417282071143779 - 3.1375528668252477302j), tol=ATOL) + assert ae(v.real, -0.033032417282071143779, tol=PTOL) + assert ae(v.imag, -3.1375528668252477302, tol=PTOL) + v = fp.ei((0.0 - 40.0j)) + assert ae(v, (0.019020007896208766962 - 3.157781446149681126j), tol=ATOL) + assert ae(v.real, 0.019020007896208766962, tol=PTOL) + assert ae(v.imag, -3.157781446149681126, tol=PTOL) + v = fp.ei((0.0 - 50.0j)) + assert ae(v, (-0.0056283863241163054402 - 3.122413399280832514j), tol=ATOL) + assert ae(v.real, -0.0056283863241163054402, tol=PTOL) + assert ae(v.imag, -3.122413399280832514, tol=PTOL) + v = fp.ei((0.0 - 80.0j)) + assert ae(v, (-0.012402501155070958192 - 3.1431272137073839346j), tol=ATOL) + assert ae(v.real, -0.012402501155070958192, tol=PTOL) + assert ae(v.imag, -3.1431272137073839346, tol=PTOL) + v = fp.ei((1.1641532182693481445e-10 - 4.6566128730773925781e-10j)) + assert ae(v, (-20.880034621432138988 - 1.3258176641336937524j), tol=ATOL) + assert ae(v.real, -20.880034621432138988, tol=PTOL) + assert ae(v.imag, -1.3258176641336937524, tol=PTOL) + v = fp.ei((0.25 - 1.0j)) + assert ae(v, (0.59066621214766308594 - 2.3968481059377428687j), tol=ATOL) + assert ae(v.real, 0.59066621214766308594, tol=PTOL) + assert ae(v.imag, -2.3968481059377428687, tol=PTOL) + v = fp.ei((1.0 - 4.0j)) + assert ae(v, (-0.49739047283060471093 - 3.5570287076301818702j), tol=ATOL) + assert ae(v.real, -0.49739047283060471093, tol=PTOL) + assert ae(v.imag, -3.5570287076301818702, tol=PTOL) + v = fp.ei((2.0 - 8.0j)) + assert ae(v, (0.8705211147733730969 - 3.3825859385758486351j), tol=ATOL) + assert ae(v.real, 0.8705211147733730969, tol=PTOL) + assert ae(v.imag, -3.3825859385758486351, tol=PTOL) + v = fp.ei((5.0 - 20.0j)) + assert ae(v, (7.0789514293925893007 - 1.5313749363937141849j), tol=ATOL) + assert ae(v.real, 7.0789514293925893007, tol=PTOL) + assert ae(v.imag, -1.5313749363937141849, tol=PTOL) + v = fp.ei((20.0 - 80.0j)) + assert ae(v, (-5855431.4907298084434 + 720917.79156143806727j), tol=ATOL) + assert ae(v.real, -5855431.4907298084434, tol=PTOL) + assert ae(v.imag, 720917.79156143806727, tol=PTOL) + v = fp.ei((30.0 - 120.0j)) + assert ae(v, (65402491644.703470747 + 56697658396.51586764j), tol=ATOL) + assert ae(v.real, 65402491644.703470747, tol=PTOL) + assert ae(v.imag, 56697658396.51586764, tol=PTOL) + v = fp.ei((40.0 - 160.0j)) + assert ae(v, (-25504929379604.776769 - 1429035198630576.3879j), tol=ATOL) + assert ae(v.real, -25504929379604.776769, tol=PTOL) + assert ae(v.imag, -1429035198630576.3879, tol=PTOL) + v = fp.ei((50.0 - 200.0j)) + assert ae(v, (-18437746526988116954.0 + 17146362239046152342.0j), tol=ATOL) + assert ae(v.real, -18437746526988116954.0, tol=PTOL) + assert ae(v.imag, 17146362239046152342.0, tol=PTOL) + v = fp.ei((80.0 - 320.0j)) + assert ae(v, (-3.3464697299634526706e+31 + 1.6473152633843023919e+32j), tol=ATOL) + assert ae(v.real, -3.3464697299634526706e+31, tol=PTOL) + assert ae(v.imag, 1.6473152633843023919e+32, tol=PTOL) + v = fp.ei((1.1641532182693481445e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (-21.950067703180274374 - 0.78539816351386363145j), tol=ATOL) + assert ae(v.real, -21.950067703180274374, tol=PTOL) + assert ae(v.imag, -0.78539816351386363145, tol=PTOL) + v = fp.ei((0.25 - 0.25j)) + assert ae(v, (-0.21441047326710323254 - 1.0683772981589995996j), tol=ATOL) + assert ae(v.real, -0.21441047326710323254, tol=PTOL) + assert ae(v.imag, -1.0683772981589995996, tol=PTOL) + v = fp.ei((1.0 - 1.0j)) + assert ae(v, (1.7646259855638540684 - 2.3877698515105224193j), tol=ATOL) + assert ae(v.real, 1.7646259855638540684, tol=PTOL) + assert ae(v.imag, -2.3877698515105224193, tol=PTOL) + v = fp.ei((2.0 - 2.0j)) + assert ae(v, (1.8920781621855474089 - 5.3169624378326579621j), tol=ATOL) + assert ae(v.real, 1.8920781621855474089, tol=PTOL) + assert ae(v.imag, -5.3169624378326579621, tol=PTOL) + v = fp.ei((5.0 - 5.0j)) + assert ae(v, (-13.470936071475245856 + 15.322492395731230968j), tol=ATOL) + assert ae(v.real, -13.470936071475245856, tol=PTOL) + assert ae(v.imag, 15.322492395731230968, tol=PTOL) + v = fp.ei((20.0 - 20.0j)) + assert ae(v, (16589317.398788971896 - 5831705.4712368307104j), tol=ATOL) + assert ae(v.real, 16589317.398788971896, tol=PTOL) + assert ae(v.imag, -5831705.4712368307104, tol=PTOL) + v = fp.ei((30.0 - 30.0j)) + assert ae(v, (-154596484273.69322527 + 204179357834.2723043j), tol=ATOL) + assert ae(v.real, -154596484273.69322527, tol=PTOL) + assert ae(v.imag, 204179357834.2723043, tol=PTOL) + v = fp.ei((40.0 - 40.0j)) + assert ae(v, (287512180321448.45408 - 4203502407932318.1156j), tol=ATOL) + assert ae(v.real, 287512180321448.45408, tol=PTOL) + assert ae(v.imag, -4203502407932318.1156, tol=PTOL) + v = fp.ei((50.0 - 50.0j)) + assert ae(v, (36128528616649268826.0 + 64648801861338741960.0j), tol=ATOL) + assert ae(v.real, 36128528616649268826.0, tol=PTOL) + assert ae(v.imag, 64648801861338741960.0, tol=PTOL) + v = fp.ei((80.0 - 80.0j)) + assert ae(v, (-3.8674816337930010217e+32 + 3.0540709639658071041e+32j), tol=ATOL) + assert ae(v.real, -3.8674816337930010217e+32, tol=PTOL) + assert ae(v.imag, 3.0540709639658071041e+32, tol=PTOL) + v = fp.ei((4.6566128730773925781e-10 - 1.1641532182693481445e-10j)) + assert ae(v, (-20.880034621082893023 - 0.24497866324327947603j), tol=ATOL) + assert ae(v.real, -20.880034621082893023, tol=PTOL) + assert ae(v.imag, -0.24497866324327947603, tol=PTOL) + v = fp.ei((1.0 - 0.25j)) + assert ae(v, (1.8942716983721074932 - 0.67268237088273915854j), tol=ATOL) + assert ae(v.real, 1.8942716983721074932, tol=PTOL) + assert ae(v.imag, -0.67268237088273915854, tol=PTOL) + v = fp.ei((4.0 - 1.0j)) + assert ae(v, (14.806699492675420438 - 12.280015176673582616j), tol=ATOL) + assert ae(v.real, 14.806699492675420438, tol=PTOL) + assert ae(v.imag, -12.280015176673582616, tol=PTOL) + v = fp.ei((8.0 - 2.0j)) + assert ae(v, (-54.633252667426386294 - 416.34477429173650012j), tol=ATOL) + assert ae(v.real, -54.633252667426386294, tol=PTOL) + assert ae(v.imag, -416.34477429173650012, tol=PTOL) + v = fp.ei((20.0 - 5.0j)) + assert ae(v, (711836.97165402624643 + 24745247.798103247366j), tol=ATOL) + assert ae(v.real, 711836.97165402624643, tol=PTOL) + assert ae(v.imag, 24745247.798103247366, tol=PTOL) + v = fp.ei((80.0 - 20.0j)) + assert ae(v, (4.2139911108612653091e+32 - 5.3367124741918251637e+32j), tol=ATOL) + assert ae(v.real, 4.2139911108612653091e+32, tol=PTOL) + assert ae(v.imag, -5.3367124741918251637e+32, tol=PTOL) + v = fp.ei((120.0 - 30.0j)) + assert ae(v, (-9.7760616203707508892e+48 + 1.058257682317195792e+50j), tol=ATOL) + assert ae(v.real, -9.7760616203707508892e+48, tol=PTOL) + assert ae(v.imag, 1.058257682317195792e+50, tol=PTOL) + v = fp.ei((160.0 - 40.0j)) + assert ae(v, (-8.7065541466623638861e+66 - 1.6577106725141739889e+67j), tol=ATOL) + assert ae(v.real, -8.7065541466623638861e+66, tol=PTOL) + assert ae(v.imag, -1.6577106725141739889e+67, tol=PTOL) + v = fp.ei((200.0 - 50.0j)) + assert ae(v, (3.070744996327018106e+84 + 1.7243244846769415903e+84j), tol=ATOL) + assert ae(v.real, 3.070744996327018106e+84, tol=PTOL) + assert ae(v.imag, 1.7243244846769415903e+84, tol=PTOL) + v = fp.ei((320.0 - 80.0j)) + assert ae(v, (-9.9960598637998647276e+135 + 2.6855081527595608863e+136j), tol=ATOL) + assert ae(v.real, -9.9960598637998647276e+135, tol=PTOL) + assert ae(v.imag, 2.6855081527595608863e+136, tol=PTOL) diff --git a/compiler/gdsMill/mpmath/tests/test_functions.py b/compiler/gdsMill/mpmath/tests/test_functions.py new file mode 100644 index 00000000..637e0b7d --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_functions.py @@ -0,0 +1,882 @@ +from mpmath.libmp import * +from mpmath import * +import random +import time +import math +import cmath + +def mpc_ae(a, b, eps=eps): + res = True + res = res and a.real.ae(b.real, eps) + res = res and a.imag.ae(b.imag, eps) + return res + +#---------------------------------------------------------------------------- +# Constants and functions +# + +tpi = "3.1415926535897932384626433832795028841971693993751058209749445923078\ +1640628620899862803482534211706798" +te = "2.71828182845904523536028747135266249775724709369995957496696762772407\ +663035354759457138217852516642743" +tdegree = "0.017453292519943295769236907684886127134428718885417254560971914\ +4017100911460344944368224156963450948221" +teuler = "0.5772156649015328606065120900824024310421593359399235988057672348\ +84867726777664670936947063291746749516" +tln2 = "0.693147180559945309417232121458176568075500134360255254120680009493\ +393621969694715605863326996418687542" +tln10 = "2.30258509299404568401799145468436420760110148862877297603332790096\ +757260967735248023599720508959829834" +tcatalan = "0.91596559417721901505460351493238411077414937428167213426649811\ +9621763019776254769479356512926115106249" +tkhinchin = "2.6854520010653064453097148354817956938203822939944629530511523\ +4555721885953715200280114117493184769800" +tglaisher = "1.2824271291006226368753425688697917277676889273250011920637400\ +2174040630885882646112973649195820237439420646" +tapery = "1.2020569031595942853997381615114499907649862923404988817922715553\ +4183820578631309018645587360933525815" +tphi = "1.618033988749894848204586834365638117720309179805762862135448622705\ +26046281890244970720720418939113748475" +tmertens = "0.26149721284764278375542683860869585905156664826119920619206421\ +3924924510897368209714142631434246651052" +ttwinprime = "0.660161815846869573927812110014555778432623360284733413319448\ +423335405642304495277143760031413839867912" + +def test_constants(): + for prec in [3, 7, 10, 15, 20, 37, 80, 100, 29]: + mp.dps = prec + assert pi == mpf(tpi) + assert e == mpf(te) + assert degree == mpf(tdegree) + assert euler == mpf(teuler) + assert ln2 == mpf(tln2) + assert ln10 == mpf(tln10) + assert catalan == mpf(tcatalan) + assert khinchin == mpf(tkhinchin) + assert glaisher == mpf(tglaisher) + assert phi == mpf(tphi) + if prec < 50: + assert mertens == mpf(tmertens) + assert twinprime == mpf(ttwinprime) + mp.dps = 15 + assert pi >= -1 + assert pi > 2 + assert pi > 3 + assert pi < 4 + +def test_exact_sqrts(): + for i in range(20000): + assert sqrt(mpf(i*i)) == i + random.seed(1) + for prec in [100, 300, 1000, 10000]: + mp.dps = prec + for i in range(20): + A = random.randint(10**(prec//2-2), 10**(prec//2-1)) + assert sqrt(mpf(A*A)) == A + mp.dps = 15 + for i in range(100): + for a in [1, 8, 25, 112307]: + assert sqrt(mpf((a*a, 2*i))) == mpf((a, i)) + assert sqrt(mpf((a*a, -2*i))) == mpf((a, -i)) + +def test_sqrt_rounding(): + for i in [2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15]: + i = from_int(i) + for dps in [7, 15, 83, 106, 2000]: + mp.dps = dps + a = mpf_pow_int(mpf_sqrt(i, mp.prec, round_down), 2, mp.prec, round_down) + b = mpf_pow_int(mpf_sqrt(i, mp.prec, round_up), 2, mp.prec, round_up) + assert mpf_lt(a, i) + assert mpf_gt(b, i) + random.seed(1234) + prec = 100 + for rnd in [round_down, round_nearest, round_ceiling]: + for i in range(100): + a = mpf_rand(prec) + b = mpf_mul(a, a) + assert mpf_sqrt(b, prec, rnd) == a + # Test some extreme cases + mp.dps = 100 + a = mpf(9) + 1e-90 + b = mpf(9) - 1e-90 + mp.dps = 15 + assert sqrt(a, rounding='d') == 3 + assert sqrt(a, rounding='n') == 3 + assert sqrt(a, rounding='u') > 3 + assert sqrt(b, rounding='d') < 3 + assert sqrt(b, rounding='n') == 3 + assert sqrt(b, rounding='u') == 3 + # A worst case, from the MPFR test suite + assert sqrt(mpf('7.0503726185518891')) == mpf('2.655253776675949') + +def test_float_sqrt(): + mp.dps = 15 + # These should round identically + for x in [0, 1e-7, 0.1, 0.5, 1, 2, 3, 4, 5, 0.333, 76.19]: + assert sqrt(mpf(x)) == float(x)**0.5 + assert sqrt(-1) == 1j + assert sqrt(-2).ae(cmath.sqrt(-2)) + assert sqrt(-3).ae(cmath.sqrt(-3)) + assert sqrt(-100).ae(cmath.sqrt(-100)) + assert sqrt(1j).ae(cmath.sqrt(1j)) + assert sqrt(-1j).ae(cmath.sqrt(-1j)) + assert sqrt(math.pi + math.e*1j).ae(cmath.sqrt(math.pi + math.e*1j)) + assert sqrt(math.pi - math.e*1j).ae(cmath.sqrt(math.pi - math.e*1j)) + +def test_hypot(): + assert hypot(0, 0) == 0 + assert hypot(0, 0.33) == mpf(0.33) + assert hypot(0.33, 0) == mpf(0.33) + assert hypot(-0.33, 0) == mpf(0.33) + assert hypot(3, 4) == mpf(5) + +def test_exact_cbrt(): + for i in range(0, 20000, 200): + assert cbrt(mpf(i*i*i)) == i + random.seed(1) + for prec in [100, 300, 1000, 10000]: + mp.dps = prec + A = random.randint(10**(prec//2-2), 10**(prec//2-1)) + assert cbrt(mpf(A*A*A)) == A + mp.dps = 15 + +def test_exp(): + assert exp(0) == 1 + assert exp(10000).ae(mpf('8.8068182256629215873e4342')) + assert exp(-10000).ae(mpf('1.1354838653147360985e-4343')) + a = exp(mpf((1, 8198646019315405L, -53, 53))) + assert(a.bc == bitcount(a.man)) + mp.prec = 67 + a = exp(mpf((1, 1781864658064754565L, -60, 61))) + assert(a.bc == bitcount(a.man)) + mp.prec = 53 + assert exp(ln2 * 10).ae(1024) + assert exp(2+2j).ae(cmath.exp(2+2j)) + +def test_issue_33(): + mp.dps = 512 + a = exp(-1) + b = exp(1) + mp.dps = 15 + assert (+a).ae(0.36787944117144233) + assert (+b).ae(2.7182818284590451) + +def test_log(): + mp.dps = 15 + assert log(1) == 0 + for x in [0.5, 1.5, 2.0, 3.0, 100, 10**50, 1e-50]: + assert log(x).ae(math.log(x)) + assert log(x, x) == 1 + assert log(1024, 2) == 10 + assert log(10**1234, 10) == 1234 + assert log(2+2j).ae(cmath.log(2+2j)) + # Accuracy near 1 + assert (log(0.6+0.8j).real*10**17).ae(2.2204460492503131) + assert (log(0.6-0.8j).real*10**17).ae(2.2204460492503131) + assert (log(0.8-0.6j).real*10**17).ae(2.2204460492503131) + assert (log(1+1e-8j).real*10**16).ae(0.5) + assert (log(1-1e-8j).real*10**16).ae(0.5) + assert (log(-1+1e-8j).real*10**16).ae(0.5) + assert (log(-1-1e-8j).real*10**16).ae(0.5) + assert (log(1j+1e-8).real*10**16).ae(0.5) + assert (log(1j-1e-8).real*10**16).ae(0.5) + assert (log(-1j+1e-8).real*10**16).ae(0.5) + assert (log(-1j-1e-8).real*10**16).ae(0.5) + assert (log(1+1e-40j).real*10**80).ae(0.5) + assert (log(1j+1e-40).real*10**80).ae(0.5) + # Huge + assert log(ldexp(1.234,10**20)).ae(log(2)*1e20) + assert log(ldexp(1.234,10**200)).ae(log(2)*1e200) + # Some special values + assert log(mpc(0,0)) == mpc(-inf,0) + assert isnan(log(mpc(nan,0)).real) + assert isnan(log(mpc(nan,0)).imag) + assert isnan(log(mpc(0,nan)).real) + assert isnan(log(mpc(0,nan)).imag) + assert isnan(log(mpc(nan,1)).real) + assert isnan(log(mpc(nan,1)).imag) + assert isnan(log(mpc(1,nan)).real) + assert isnan(log(mpc(1,nan)).imag) + +def test_trig_hyperb_basic(): + for x in (range(100) + range(-100,0)): + t = x / 4.1 + assert cos(mpf(t)).ae(math.cos(t)) + assert sin(mpf(t)).ae(math.sin(t)) + assert tan(mpf(t)).ae(math.tan(t)) + assert cosh(mpf(t)).ae(math.cosh(t)) + assert sinh(mpf(t)).ae(math.sinh(t)) + assert tanh(mpf(t)).ae(math.tanh(t)) + assert sin(1+1j).ae(cmath.sin(1+1j)) + assert sin(-4-3.6j).ae(cmath.sin(-4-3.6j)) + assert cos(1+1j).ae(cmath.cos(1+1j)) + assert cos(-4-3.6j).ae(cmath.cos(-4-3.6j)) + +def test_degrees(): + assert cos(0*degree) == 1 + assert cos(90*degree).ae(0) + assert cos(180*degree).ae(-1) + assert cos(270*degree).ae(0) + assert cos(360*degree).ae(1) + assert sin(0*degree) == 0 + assert sin(90*degree).ae(1) + assert sin(180*degree).ae(0) + assert sin(270*degree).ae(-1) + assert sin(360*degree).ae(0) + +def random_complexes(N): + random.seed(1) + a = [] + for i in range(N): + x1 = random.uniform(-10, 10) + y1 = random.uniform(-10, 10) + x2 = random.uniform(-10, 10) + y2 = random.uniform(-10, 10) + z1 = complex(x1, y1) + z2 = complex(x2, y2) + a.append((z1, z2)) + return a + +def test_complex_powers(): + for dps in [15, 30, 100]: + # Check accuracy for complex square root + mp.dps = dps + a = mpc(1j)**0.5 + assert a.real == a.imag == mpf(2)**0.5 / 2 + mp.dps = 15 + random.seed(1) + for (z1, z2) in random_complexes(100): + assert (mpc(z1)**mpc(z2)).ae(z1**z2, 1e-12) + assert (e**(-pi*1j)).ae(-1) + mp.dps = 50 + assert (e**(-pi*1j)).ae(-1) + mp.dps = 15 + +def test_complex_sqrt_accuracy(): + def test_mpc_sqrt(lst): + for a, b in lst: + z = mpc(a + j*b) + assert mpc_ae(sqrt(z*z), z) + z = mpc(-a + j*b) + assert mpc_ae(sqrt(z*z), -z) + z = mpc(a - j*b) + assert mpc_ae(sqrt(z*z), z) + z = mpc(-a - j*b) + assert mpc_ae(sqrt(z*z), -z) + random.seed(2) + N = 10 + mp.dps = 30 + dps = mp.dps + test_mpc_sqrt([(random.uniform(0, 10),random.uniform(0, 10)) for i in range(N)]) + test_mpc_sqrt([(i + 0.1, (i + 0.2)*10**i) for i in range(N)]) + mp.dps = 15 + +def test_atan(): + mp.dps = 15 + assert atan(-2.3).ae(math.atan(-2.3)) + assert atan(1e-50) == 1e-50 + assert atan(1e50).ae(pi/2) + assert atan(-1e-50) == -1e-50 + assert atan(-1e50).ae(-pi/2) + assert atan(10**1000).ae(pi/2) + for dps in [25, 70, 100, 300, 1000]: + mp.dps = dps + assert (4*atan(1)).ae(pi) + mp.dps = 15 + pi2 = pi/2 + assert atan(mpc(inf,-1)).ae(pi2) + assert atan(mpc(inf,0)).ae(pi2) + assert atan(mpc(inf,1)).ae(pi2) + assert atan(mpc(1,inf)).ae(pi2) + assert atan(mpc(0,inf)).ae(pi2) + assert atan(mpc(-1,inf)).ae(-pi2) + assert atan(mpc(-inf,1)).ae(-pi2) + assert atan(mpc(-inf,0)).ae(-pi2) + assert atan(mpc(-inf,-1)).ae(-pi2) + assert atan(mpc(-1,-inf)).ae(-pi2) + assert atan(mpc(0,-inf)).ae(-pi2) + assert atan(mpc(1,-inf)).ae(pi2) + +def test_atan2(): + mp.dps = 15 + assert atan2(1,1).ae(pi/4) + assert atan2(1,-1).ae(3*pi/4) + assert atan2(-1,-1).ae(-3*pi/4) + assert atan2(-1,1).ae(-pi/4) + assert atan2(-1,0).ae(-pi/2) + assert atan2(1,0).ae(pi/2) + assert atan2(0,0) == 0 + assert atan2(inf,0).ae(pi/2) + assert atan2(-inf,0).ae(-pi/2) + assert isnan(atan2(inf,inf)) + assert isnan(atan2(-inf,inf)) + assert isnan(atan2(inf,-inf)) + assert isnan(atan2(3,nan)) + assert isnan(atan2(nan,3)) + assert isnan(atan2(0,nan)) + assert isnan(atan2(nan,0)) + assert atan2(0,inf) == 0 + assert atan2(0,-inf).ae(pi) + assert atan2(10,inf) == 0 + assert atan2(-10,inf) == 0 + assert atan2(-10,-inf).ae(-pi) + assert atan2(10,-inf).ae(pi) + assert atan2(inf,10).ae(pi/2) + assert atan2(inf,-10).ae(pi/2) + assert atan2(-inf,10).ae(-pi/2) + assert atan2(-inf,-10).ae(-pi/2) + +def test_areal_inverses(): + assert asin(mpf(0)) == 0 + assert asinh(mpf(0)) == 0 + assert acosh(mpf(1)) == 0 + assert isinstance(asin(mpf(0.5)), mpf) + assert isinstance(asin(mpf(2.0)), mpc) + assert isinstance(acos(mpf(0.5)), mpf) + assert isinstance(acos(mpf(2.0)), mpc) + assert isinstance(atanh(mpf(0.1)), mpf) + assert isinstance(atanh(mpf(1.1)), mpc) + + random.seed(1) + for i in range(50): + x = random.uniform(0, 1) + assert asin(mpf(x)).ae(math.asin(x)) + assert acos(mpf(x)).ae(math.acos(x)) + + x = random.uniform(-10, 10) + assert asinh(mpf(x)).ae(cmath.asinh(x).real) + assert isinstance(asinh(mpf(x)), mpf) + x = random.uniform(1, 10) + assert acosh(mpf(x)).ae(cmath.acosh(x).real) + assert isinstance(acosh(mpf(x)), mpf) + x = random.uniform(-10, 0.999) + assert isinstance(acosh(mpf(x)), mpc) + + x = random.uniform(-1, 1) + assert atanh(mpf(x)).ae(cmath.atanh(x).real) + assert isinstance(atanh(mpf(x)), mpf) + + dps = mp.dps + mp.dps = 300 + assert isinstance(asin(0.5), mpf) + mp.dps = 1000 + assert asin(1).ae(pi/2) + assert asin(-1).ae(-pi/2) + mp.dps = dps + +def test_invhyperb_inaccuracy(): + mp.dps = 15 + assert (asinh(1e-5)*10**5).ae(0.99999999998333333) + assert (asinh(1e-10)*10**10).ae(1) + assert (asinh(1e-50)*10**50).ae(1) + assert (asinh(-1e-5)*10**5).ae(-0.99999999998333333) + assert (asinh(-1e-10)*10**10).ae(-1) + assert (asinh(-1e-50)*10**50).ae(-1) + assert asinh(10**20).ae(46.744849040440862) + assert asinh(-10**20).ae(-46.744849040440862) + assert (tanh(1e-10)*10**10).ae(1) + assert (tanh(-1e-10)*10**10).ae(-1) + assert (atanh(1e-10)*10**10).ae(1) + assert (atanh(-1e-10)*10**10).ae(-1) + +def test_complex_functions(): + for x in (range(10) + range(-10,0)): + for y in (range(10) + range(-10,0)): + z = complex(x, y)/4.3 + 0.01j + assert exp(mpc(z)).ae(cmath.exp(z)) + assert log(mpc(z)).ae(cmath.log(z)) + assert cos(mpc(z)).ae(cmath.cos(z)) + assert sin(mpc(z)).ae(cmath.sin(z)) + assert tan(mpc(z)).ae(cmath.tan(z)) + assert sinh(mpc(z)).ae(cmath.sinh(z)) + assert cosh(mpc(z)).ae(cmath.cosh(z)) + assert tanh(mpc(z)).ae(cmath.tanh(z)) + +def test_complex_inverse_functions(): + for (z1, z2) in random_complexes(30): + # apparently cmath uses a different branch, so we + # can't use it for comparison + assert sinh(asinh(z1)).ae(z1) + # + assert acosh(z1).ae(cmath.acosh(z1)) + assert atanh(z1).ae(cmath.atanh(z1)) + assert atan(z1).ae(cmath.atan(z1)) + # the reason we set a big eps here is that the cmath + # functions are inaccurate + assert asin(z1).ae(cmath.asin(z1), rel_eps=1e-12) + assert acos(z1).ae(cmath.acos(z1), rel_eps=1e-12) + one = mpf(1) + for i in range(-9, 10, 3): + for k in range(-9, 10, 3): + a = 0.9*j*10**k + 0.8*one*10**i + b = cos(acos(a)) + assert b.ae(a) + b = sin(asin(a)) + assert b.ae(a) + one = mpf(1) + err = 2*10**-15 + for i in range(-9, 9, 3): + for k in range(-9, 9, 3): + a = -0.9*10**k + j*0.8*one*10**i + b = cosh(acosh(a)) + assert b.ae(a, err) + b = sinh(asinh(a)) + assert b.ae(a, err) + +def test_reciprocal_functions(): + assert sec(3).ae(-1.01010866590799375) + assert csc(3).ae(7.08616739573718592) + assert cot(3).ae(-7.01525255143453347) + assert sech(3).ae(0.0993279274194332078) + assert csch(3).ae(0.0998215696688227329) + assert coth(3).ae(1.00496982331368917) + assert asec(3).ae(1.23095941734077468) + assert acsc(3).ae(0.339836909454121937) + assert acot(3).ae(0.321750554396642193) + assert asech(0.5).ae(1.31695789692481671) + assert acsch(3).ae(0.327450150237258443) + assert acoth(3).ae(0.346573590279972655) + +def test_ldexp(): + mp.dps = 15 + assert ldexp(mpf(2.5), 0) == 2.5 + assert ldexp(mpf(2.5), -1) == 1.25 + assert ldexp(mpf(2.5), 2) == 10 + assert ldexp(mpf('inf'), 3) == mpf('inf') + +def test_frexp(): + mp.dps = 15 + assert frexp(0) == (0.0, 0) + assert frexp(9) == (0.5625, 4) + assert frexp(1) == (0.5, 1) + assert frexp(0.2) == (0.8, -2) + assert frexp(1000) == (0.9765625, 10) + +def test_aliases(): + assert ln(7) == log(7) + assert log10(3.75) == log(3.75,10) + assert degrees(5.6) == 5.6 / degree + assert radians(5.6) == 5.6 * degree + assert power(-1,0.5) == j + assert modf(25,7) == 4.0 and isinstance(modf(25,7), mpf) + +def test_arg_sign(): + assert arg(3) == 0 + assert arg(-3).ae(pi) + assert arg(j).ae(pi/2) + assert arg(-j).ae(-pi/2) + assert arg(0) == 0 + assert isnan(atan2(3,nan)) + assert isnan(atan2(nan,3)) + assert isnan(atan2(0,nan)) + assert isnan(atan2(nan,0)) + assert isnan(atan2(nan,nan)) + assert arg(inf) == 0 + assert arg(-inf).ae(pi) + assert isnan(arg(nan)) + #assert arg(inf*j).ae(pi/2) + assert sign(0) == 0 + assert sign(3) == 1 + assert sign(-3) == -1 + assert sign(inf) == 1 + assert sign(-inf) == -1 + assert isnan(sign(nan)) + assert sign(j) == j + assert sign(-3*j) == -j + assert sign(1+j).ae((1+j)/sqrt(2)) + +def test_misc_bugs(): + # test that this doesn't raise an exception + mp.dps = 1000 + log(1302) + mp.dps = 15 + +def test_arange(): + assert arange(10) == [mpf('0.0'), mpf('1.0'), mpf('2.0'), mpf('3.0'), + mpf('4.0'), mpf('5.0'), mpf('6.0'), mpf('7.0'), + mpf('8.0'), mpf('9.0')] + assert arange(-5, 5) == [mpf('-5.0'), mpf('-4.0'), mpf('-3.0'), + mpf('-2.0'), mpf('-1.0'), mpf('0.0'), + mpf('1.0'), mpf('2.0'), mpf('3.0'), mpf('4.0')] + assert arange(0, 1, 0.1) == [mpf('0.0'), mpf('0.10000000000000001'), + mpf('0.20000000000000001'), + mpf('0.30000000000000004'), + mpf('0.40000000000000002'), + mpf('0.5'), mpf('0.60000000000000009'), + mpf('0.70000000000000007'), + mpf('0.80000000000000004'), + mpf('0.90000000000000002')] + assert arange(17, -9, -3) == [mpf('17.0'), mpf('14.0'), mpf('11.0'), + mpf('8.0'), mpf('5.0'), mpf('2.0'), + mpf('-1.0'), mpf('-4.0'), mpf('-7.0')] + assert arange(0.2, 0.1, -0.1) == [mpf('0.20000000000000001')] + assert arange(0) == [] + assert arange(1000, -1) == [] + assert arange(-1.23, 3.21, -0.0000001) == [] + +def test_linspace(): + assert linspace(2, 9, 7) == [mpf('2.0'), mpf('3.166666666666667'), + mpf('4.3333333333333339'), mpf('5.5'), mpf('6.666666666666667'), + mpf('7.8333333333333339'), mpf('9.0')] == linspace(mpi(2, 9), 7) + assert linspace(2, 9, 7, endpoint=0) == [mpf('2.0'), mpf('3.0'), mpf('4.0'), + mpf('5.0'), mpf('6.0'), mpf('7.0'), mpf('8.0')] + assert linspace(2, 7, 1) == [mpf(2)] + +def test_float_cbrt(): + mp.dps = 30 + for a in arange(0,10,0.1): + assert cbrt(a*a*a).ae(a, eps) + assert cbrt(-1).ae(0.5 + j*sqrt(3)/2) + one_third = mpf(1)/3 + for a in arange(0,10,2.7) + [0.1 + 10**5]: + a = mpc(a + 1.1j) + r1 = cbrt(a) + mp.dps += 10 + r2 = pow(a, one_third) + mp.dps -= 10 + assert r1.ae(r2, eps) + mp.dps = 100 + for n in range(100, 301, 100): + w = 10**n + j*10**-3 + z = w*w*w + r = cbrt(z) + assert mpc_ae(r, w, eps) + mp.dps = 15 + +def test_root(): + mp.dps = 30 + random.seed(1) + a = random.randint(0, 10000) + p = a*a*a + r = nthroot(mpf(p), 3) + assert r == a + for n in range(4, 10): + p = p*a + assert nthroot(mpf(p), n) == a + mp.dps = 40 + for n in range(10, 5000, 100): + for a in [random.random()*10000, random.random()*10**100]: + r = nthroot(a, n) + r1 = pow(a, mpf(1)/n) + assert r.ae(r1) + r = nthroot(a, -n) + r1 = pow(a, -mpf(1)/n) + assert r.ae(r1) + # XXX: this is broken right now + # tests for nthroot rounding + for rnd in ['nearest', 'up', 'down']: + mp.rounding = rnd + for n in [-5, -3, 3, 5]: + prec = 50 + for i in xrange(10): + mp.prec = prec + a = rand() + mp.prec = 2*prec + b = a**n + mp.prec = prec + r = nthroot(b, n) + assert r == a + mp.dps = 30 + for n in range(3, 21): + a = (random.random() + j*random.random()) + assert nthroot(a, n).ae(pow(a, mpf(1)/n)) + assert mpc_ae(nthroot(a, n), pow(a, mpf(1)/n)) + a = (random.random()*10**100 + j*random.random()) + r = nthroot(a, n) + mp.dps += 4 + r1 = pow(a, mpf(1)/n) + mp.dps -= 4 + assert r.ae(r1) + assert mpc_ae(r, r1, eps) + r = nthroot(a, -n) + mp.dps += 4 + r1 = pow(a, -mpf(1)/n) + mp.dps -= 4 + assert r.ae(r1) + assert mpc_ae(r, r1, eps) + mp.dps = 15 + assert nthroot(4, 1) == 4 + assert nthroot(4, 0) == 1 + assert nthroot(4, -1) == 0.25 + assert nthroot(inf, 1) == inf + assert nthroot(inf, 2) == inf + assert nthroot(inf, 3) == inf + assert nthroot(inf, -1) == 0 + assert nthroot(inf, -2) == 0 + assert nthroot(inf, -3) == 0 + assert nthroot(j, 1) == j + assert nthroot(j, 0) == 1 + assert nthroot(j, -1) == -j + assert isnan(nthroot(nan, 1)) + assert isnan(nthroot(nan, 0)) + assert isnan(nthroot(nan, -1)) + assert isnan(nthroot(inf, 0)) + assert root(2,3) == nthroot(2,3) + assert root(16,4,0) == 2 + assert root(16,4,1) == 2j + assert root(16,4,2) == -2 + assert root(16,4,3) == -2j + assert root(16,4,4) == 2 + assert root(-125,3,1) == -5 + +def test_issue_96(): + for dps in [20, 80]: + mp.dps = dps + r = nthroot(mpf('-1e-20'), 4) + assert r.ae(mpf(10)**(-5) * (1 + j) * mpf(2)**(-0.5)) + mp.dps = 80 + assert nthroot('-1e-3', 4).ae(mpf(10)**(-3./4) * (1 + j)/sqrt(2)) + assert nthroot('-1e-6', 4).ae((1 + j)/(10 * sqrt(20))) + # Check that this doesn't take eternity to compute + mp.dps = 20 + assert nthroot('-1e100000000', 4).ae((1+j)*mpf('1e25000000')/sqrt(2)) + mp.dps = 15 + +def test_perturbation_rounding(): + mp.dps = 100 + a = pi/10**50 + b = -pi/10**50 + c = 1 + a + d = 1 + b + mp.dps = 15 + assert exp(a) == 1 + assert exp(a, rounding='c') > 1 + assert exp(b, rounding='c') == 1 + assert exp(a, rounding='f') == 1 + assert exp(b, rounding='f') < 1 + assert cos(a) == 1 + assert cos(a, rounding='c') == 1 + assert cos(b, rounding='c') == 1 + assert cos(a, rounding='f') < 1 + assert cos(b, rounding='f') < 1 + for f in [sin, atan, asinh, tanh]: + assert f(a) == +a + assert f(a, rounding='c') > a + assert f(a, rounding='f') < a + assert f(b) == +b + assert f(b, rounding='c') > b + assert f(b, rounding='f') < b + for f in [asin, tan, sinh, atanh]: + assert f(a) == +a + assert f(b) == +b + assert f(a, rounding='c') > a + assert f(b, rounding='c') > b + assert f(a, rounding='f') < a + assert f(b, rounding='f') < b + assert ln(c) == +a + assert ln(d) == +b + assert ln(c, rounding='c') > a + assert ln(c, rounding='f') < a + assert ln(d, rounding='c') > b + assert ln(d, rounding='f') < b + assert cosh(a) == 1 + assert cosh(b) == 1 + assert cosh(a, rounding='c') > 1 + assert cosh(b, rounding='c') > 1 + assert cosh(a, rounding='f') == 1 + assert cosh(b, rounding='f') == 1 + +def test_integer_parts(): + assert floor(3.2) == 3 + assert ceil(3.2) == 4 + assert floor(3.2+5j) == 3+5j + assert ceil(3.2+5j) == 4+5j + +def test_complex_parts(): + assert fabs('3') == 3 + assert fabs(3+4j) == 5 + assert re(3) == 3 + assert re(1+4j) == 1 + assert im(3) == 0 + assert im(1+4j) == 4 + assert conj(3) == 3 + assert conj(3+4j) == 3-4j + assert mpf(3).conjugate() == 3 + +def test_cospi_sinpi(): + assert sinpi(0) == 0 + assert sinpi(0.5) == 1 + assert sinpi(1) == 0 + assert sinpi(1.5) == -1 + assert sinpi(2) == 0 + assert sinpi(2.5) == 1 + assert sinpi(-0.5) == -1 + assert cospi(0) == 1 + assert cospi(0.5) == 0 + assert cospi(1) == -1 + assert cospi(1.5) == 0 + assert cospi(2) == 1 + assert cospi(2.5) == 0 + assert cospi(-0.5) == 0 + assert cospi(100000000000.25).ae(sqrt(2)/2) + a = cospi(2+3j) + assert a.real.ae(cos((2+3j)*pi).real) + assert a.imag == 0 + b = sinpi(2+3j) + assert b.imag.ae(sin((2+3j)*pi).imag) + assert b.real == 0 + mp.dps = 35 + x1 = mpf(10000) - mpf('1e-15') + x2 = mpf(10000) + mpf('1e-15') + x3 = mpf(10000.5) - mpf('1e-15') + x4 = mpf(10000.5) + mpf('1e-15') + x5 = mpf(10001) - mpf('1e-15') + x6 = mpf(10001) + mpf('1e-15') + x7 = mpf(10001.5) - mpf('1e-15') + x8 = mpf(10001.5) + mpf('1e-15') + mp.dps = 15 + M = 10**15 + assert (sinpi(x1)*M).ae(-pi) + assert (sinpi(x2)*M).ae(pi) + assert (cospi(x3)*M).ae(pi) + assert (cospi(x4)*M).ae(-pi) + assert (sinpi(x5)*M).ae(pi) + assert (sinpi(x6)*M).ae(-pi) + assert (cospi(x7)*M).ae(-pi) + assert (cospi(x8)*M).ae(pi) + assert 0.999 < cospi(x1, rounding='d') < 1 + assert 0.999 < cospi(x2, rounding='d') < 1 + assert 0.999 < sinpi(x3, rounding='d') < 1 + assert 0.999 < sinpi(x4, rounding='d') < 1 + assert -1 < cospi(x5, rounding='d') < -0.999 + assert -1 < cospi(x6, rounding='d') < -0.999 + assert -1 < sinpi(x7, rounding='d') < -0.999 + assert -1 < sinpi(x8, rounding='d') < -0.999 + assert (sinpi(1e-15)*M).ae(pi) + assert (sinpi(-1e-15)*M).ae(-pi) + assert cospi(1e-15) == 1 + assert cospi(1e-15, rounding='d') < 1 + +def test_expj(): + assert expj(0) == 1 + assert expj(1).ae(exp(j)) + assert expj(j).ae(exp(-1)) + assert expj(1+j).ae(exp(j*(1+j))) + assert expjpi(0) == 1 + assert expjpi(1).ae(exp(j*pi)) + assert expjpi(j).ae(exp(-pi)) + assert expjpi(1+j).ae(exp(j*pi*(1+j))) + assert expjpi(-10**15 * j).ae('2.22579818340535731e+1364376353841841') + +def test_sinc(): + assert sinc(0) == sincpi(0) == 1 + assert sinc(inf) == sincpi(inf) == 0 + assert sinc(-inf) == sincpi(-inf) == 0 + assert sinc(2).ae(0.45464871341284084770) + assert sinc(2+3j).ae(0.4463290318402435457-2.7539470277436474940j) + assert sincpi(2) == 0 + assert sincpi(1.5).ae(-0.212206590789193781) + +def test_fibonacci(): + mp.dps = 15 + assert [fibonacci(n) for n in range(-5, 10)] == \ + [5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + assert fib(2.5).ae(1.4893065462657091) + assert fib(3+4j).ae(-5248.51130728372 - 14195.962288353j) + assert fib(1000).ae(4.3466557686937455e+208) + assert str(fib(10**100)) == '6.24499112864607e+2089876402499787337692720892375554168224592399182109535392875613974104853496745963277658556235103534' + mp.dps = 2100 + a = fib(10000) + assert a % 10**10 == 9947366875 + mp.dps = 15 + assert fibonacci(inf) == inf + assert fib(3+0j) == 2 + +def test_call_with_dps(): + mp.dps = 15 + assert abs(exp(1, dps=30)-e(dps=35)) < 1e-29 + +def test_tanh(): + mp.dps = 15 + assert tanh(0) == 0 + assert tanh(inf) == 1 + assert tanh(-inf) == -1 + assert isnan(tanh(nan)) + assert tanh(mpc('inf', '0')) == 1 + +def test_atanh(): + mp.dps = 15 + assert atanh(0) == 0 + assert atanh(0.5).ae(0.54930614433405484570) + assert atanh(-0.5).ae(-0.54930614433405484570) + assert atanh(1) == inf + assert atanh(-1) == -inf + assert isnan(atanh(nan)) + assert isinstance(atanh(1), mpf) + assert isinstance(atanh(-1), mpf) + # Limits at infinity + jpi2 = j*pi/2 + assert atanh(inf).ae(-jpi2) + assert atanh(-inf).ae(jpi2) + assert atanh(mpc(inf,-1)).ae(-jpi2) + assert atanh(mpc(inf,0)).ae(-jpi2) + assert atanh(mpc(inf,1)).ae(jpi2) + assert atanh(mpc(1,inf)).ae(jpi2) + assert atanh(mpc(0,inf)).ae(jpi2) + assert atanh(mpc(-1,inf)).ae(jpi2) + assert atanh(mpc(-inf,1)).ae(jpi2) + assert atanh(mpc(-inf,0)).ae(jpi2) + assert atanh(mpc(-inf,-1)).ae(-jpi2) + assert atanh(mpc(-1,-inf)).ae(-jpi2) + assert atanh(mpc(0,-inf)).ae(-jpi2) + assert atanh(mpc(1,-inf)).ae(-jpi2) + +def test_expm1(): + mp.dps = 15 + assert expm1(0) == 0 + assert expm1(3).ae(exp(3)-1) + assert expm1(inf) == inf + assert expm1(1e-10)*1e10 + assert expm1(1e-50).ae(1e-50) + assert (expm1(1e-10)*1e10).ae(1.00000000005) + +def test_powm1(): + mp.dps = 15 + assert powm1(2,3) == 7 + assert powm1(-1,2) == 0 + assert powm1(-1,0) == 0 + assert powm1(-2,0) == 0 + assert powm1(3+4j,0) == 0 + assert powm1(0,1) == -1 + assert powm1(0,0) == 0 + assert powm1(1,0) == 0 + assert powm1(1,2) == 0 + assert powm1(1,3+4j) == 0 + assert powm1(1,5) == 0 + assert powm1(j,4) == 0 + assert powm1(-j,4) == 0 + assert (powm1(2,1e-100)*1e100).ae(ln2) + assert powm1(2,'1e-100000000000') != 0 + assert (powm1(fadd(1,1e-100,exact=True), 5)*1e100).ae(5) + +def test_unitroots(): + assert unitroots(1) == [1] + assert unitroots(2) == [1, -1] + a, b, c = unitroots(3) + assert a == 1 + assert b.ae(-0.5 + 0.86602540378443864676j) + assert c.ae(-0.5 - 0.86602540378443864676j) + assert unitroots(1, primitive=True) == [1] + assert unitroots(2, primitive=True) == [-1] + assert unitroots(3, primitive=True) == unitroots(3)[1:] + assert unitroots(4, primitive=True) == [j, -j] + assert len(unitroots(17, primitive=True)) == 16 + assert len(unitroots(16, primitive=True)) == 8 + +def test_cyclotomic(): + mp.dps = 15 + assert [cyclotomic(n,1) for n in range(31)] == [1,0,2,3,2,5,1,7,2,3,1,11,1,13,1,1,2,17,1,19,1,1,1,23,1,5,1,3,1,29,1] + assert [cyclotomic(n,-1) for n in range(31)] == [1,-2,0,1,2,1,3,1,2,1,5,1,1,1,7,1,2,1,3,1,1,1,11,1,1,1,13,1,1,1,1] + assert [cyclotomic(n,j) for n in range(21)] == [1,-1+j,1+j,j,0,1,-j,j,2,-j,1,j,3,1,-j,1,2,1,j,j,5] + assert [cyclotomic(n,-j) for n in range(21)] == [1,-1-j,1-j,-j,0,1,j,-j,2,j,1,-j,3,1,j,1,2,1,-j,-j,5] + assert cyclotomic(1624,j) == 1 + assert cyclotomic(33600,j) == 1 + u = sqrt(j, prec=500) + assert cyclotomic(8, u).ae(0) + assert cyclotomic(30, u).ae(5.8284271247461900976) + assert cyclotomic(2040, u).ae(1) + assert cyclotomic(0,2.5) == 1 + assert cyclotomic(1,2.5) == 2.5-1 + assert cyclotomic(2,2.5) == 2.5+1 + assert cyclotomic(3,2.5) == 2.5**2 + 2.5 + 1 + assert cyclotomic(7,2.5) == 406.234375 diff --git a/compiler/gdsMill/mpmath/tests/test_functions2.py b/compiler/gdsMill/mpmath/tests/test_functions2.py new file mode 100644 index 00000000..2883ede2 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_functions2.py @@ -0,0 +1,1272 @@ +import math +from mpmath import * + +def test_bessel(): + mp.dps = 15 + assert j0(1).ae(0.765197686557966551) + assert j0(pi).ae(-0.304242177644093864) + assert j0(1000).ae(0.0247866861524201746) + assert j0(-25).ae(0.0962667832759581162) + assert j1(1).ae(0.440050585744933516) + assert j1(pi).ae(0.284615343179752757) + assert j1(1000).ae(0.00472831190708952392) + assert j1(-25).ae(0.125350249580289905) + assert besselj(5,1).ae(0.000249757730211234431) + assert besselj(5,pi).ae(0.0521411843671184747) + assert besselj(5,1000).ae(0.00502540694523318607) + assert besselj(5,-25).ae(0.0660079953984229934) + assert besselj(-3,2).ae(-0.128943249474402051) + assert besselj(-4,2).ae(0.0339957198075684341) + assert besselj(3,3+2j).ae(0.424718794929639595942 + 0.625665327745785804812j) + assert besselj(0.25,4).ae(-0.374760630804249715) + assert besselj(1+2j,3+4j).ae(0.319247428741872131 - 0.669557748880365678j) + assert (besselj(3, 10**10) * 10**5).ae(0.76765081748139204023) + assert bessely(-0.5, 0) == 0 + assert bessely(0.5, 0) == -inf + assert bessely(1.5, 0) == -inf + assert bessely(0,0) == -inf + assert bessely(-0.4, 0) == -inf + assert bessely(-0.6, 0) == inf + assert bessely(-1, 0) == inf + assert bessely(-1.4, 0) == inf + assert bessely(-1.6, 0) == -inf + assert bessely(-1, 0) == inf + assert bessely(-2, 0) == -inf + assert bessely(-3, 0) == inf + assert bessely(0.5, 0) == -inf + assert bessely(1, 0) == -inf + assert bessely(1.5, 0) == -inf + assert bessely(2, 0) == -inf + assert bessely(2.5, 0) == -inf + assert bessely(3, 0) == -inf + assert bessely(0,0.5).ae(-0.44451873350670655715) + assert bessely(1,0.5).ae(-1.4714723926702430692) + assert bessely(-1,0.5).ae(1.4714723926702430692) + assert bessely(3.5,0.5).ae(-138.86400867242488443) + assert bessely(0,3+4j).ae(4.6047596915010138655-8.8110771408232264208j) + assert bessely(0,j).ae(-0.26803248203398854876+1.26606587775200833560j) + assert (bessely(3, 10**10) * 10**5).ae(0.21755917537013204058) + assert besseli(0,0) == 1 + assert besseli(1,0) == 0 + assert besseli(2,0) == 0 + assert besseli(-1,0) == 0 + assert besseli(-2,0) == 0 + assert besseli(0,0.5).ae(1.0634833707413235193) + assert besseli(1,0.5).ae(0.25789430539089631636) + assert besseli(-1,0.5).ae(0.25789430539089631636) + assert besseli(3.5,0.5).ae(0.00068103597085793815863) + assert besseli(0,3+4j).ae(-3.3924877882755196097-1.3239458916287264815j) + assert besseli(0,j).ae(besselj(0,1)) + assert (besseli(3, 10**10) * mpf(10)**(-4342944813)).ae(4.2996028505491271875) + assert besselk(0,0) == inf + assert besselk(1,0) == inf + assert besselk(2,0) == inf + assert besselk(-1,0) == inf + assert besselk(-2,0) == inf + assert besselk(0,0.5).ae(0.92441907122766586178) + assert besselk(1,0.5).ae(1.6564411200033008937) + assert besselk(-1,0.5).ae(1.6564411200033008937) + assert besselk(3.5,0.5).ae(207.48418747548460607) + assert besselk(0,3+4j).ae(-0.007239051213570155013+0.026510418350267677215j) + assert besselk(0,j).ae(-0.13863371520405399968-1.20196971531720649914j) + assert (besselk(3, 10**10) * mpf(10)**4342944824).ae(1.1628981033356187851) + +def test_hankel(): + mp.dps = 15 + assert hankel1(0,0.5).ae(0.93846980724081290423-0.44451873350670655715j) + assert hankel1(1,0.5).ae(0.2422684576748738864-1.4714723926702430692j) + assert hankel1(-1,0.5).ae(-0.2422684576748738864+1.4714723926702430692j) + assert hankel1(1.5,0.5).ae(0.0917016996256513026-2.5214655504213378514j) + assert hankel1(1.5,3+4j).ae(0.0066806866476728165382-0.0036684231610839127106j) + assert hankel2(0,0.5).ae(0.93846980724081290423+0.44451873350670655715j) + assert hankel2(1,0.5).ae(0.2422684576748738864+1.4714723926702430692j) + assert hankel2(-1,0.5).ae(-0.2422684576748738864-1.4714723926702430692j) + assert hankel2(1.5,0.5).ae(0.0917016996256513026+2.5214655504213378514j) + assert hankel2(1.5,3+4j).ae(14.783528526098567526-7.397390270853446512j) + +def test_struve(): + mp.dps = 15 + assert struveh(2,3).ae(0.74238666967748318564) + assert struveh(-2.5,3).ae(0.41271003220971599344) + assert struvel(2,3).ae(1.7476573277362782744) + assert struvel(-2.5,3).ae(1.5153394466819651377) + +def test_whittaker(): + mp.dps = 15 + assert whitm(2,3,4).ae(49.753745589025246591) + assert whitw(2,3,4).ae(14.111656223052932215) + +def test_kelvin(): + mp.dps = 15 + assert ber(2,3).ae(0.80836846563726819091) + assert ber(3,4).ae(-0.28262680167242600233) + assert ber(-3,2).ae(-0.085611448496796363669) + assert bei(2,3).ae(-0.89102236377977331571) + assert bei(-3,2).ae(-0.14420994155731828415) + assert ker(2,3).ae(0.12839126695733458928) + assert ker(-3,2).ae(-0.29802153400559142783) + assert ker(0.5,3).ae(-0.085662378535217097524) + assert kei(2,3).ae(0.036804426134164634000) + assert kei(-3,2).ae(0.88682069845786731114) + assert kei(0.5,3).ae(0.013633041571314302948) + +def test_hyper_misc(): + mp.dps = 15 + assert hyp0f1(1,0) == 1 + assert hyp1f1(1,2,0) == 1 + assert hyp1f2(1,2,3,0) == 1 + assert hyp2f1(1,2,3,0) == 1 + assert hyp2f2(1,2,3,4,0) == 1 + assert hyp2f3(1,2,3,4,5,0) == 1 + # Degenerate case: 0F0 + assert hyper([],[],0) == 1 + assert hyper([],[],-2).ae(exp(-2)) + # Degenerate case: 1F0 + assert hyper([2],[],1.5) == 4 + # + assert hyp2f1((1,3),(2,3),(5,6),mpf(27)/32).ae(1.6) + assert hyp2f1((1,4),(1,2),(3,4),mpf(80)/81).ae(1.8) + assert hyp2f1((2,3),(1,1),(3,2),(2+j)/3).ae(1.327531603558679093+0.439585080092769253j) + mp.dps = 25 + v = mpc('1.2282306665029814734863026', '-0.1225033830118305184672133') + assert hyper([(3,4),2+j,1],[1,5,j/3],mpf(1)/5+j/8).ae(v) + mp.dps = 15 + +def test_elliptic_integrals(): + mp.dps = 15 + assert ellipk(0).ae(pi/2) + assert ellipk(0.5).ae(gamma(0.25)**2/(4*sqrt(pi))) + assert ellipk(1) == inf + assert ellipk(1+0j) == inf + assert ellipk(-1).ae('1.3110287771460599052') + assert ellipk(-2).ae('1.1714200841467698589') + assert isinstance(ellipk(-2), mpf) + assert isinstance(ellipe(-2), mpf) + assert ellipk(-50).ae('0.47103424540873331679') + mp.dps = 30 + n1 = +fraction(99999,100000) + n2 = +fraction(100001,100000) + mp.dps = 15 + assert ellipk(n1).ae('7.1427724505817781901') + assert ellipk(n2).ae(mpc('7.1427417367963090109', '-1.5707923998261688019')) + assert ellipe(n1).ae('1.0000332138990829170') + v = ellipe(n2) + assert v.real.ae('0.999966786328145474069137') + assert (v.imag*10**6).ae('7.853952181727432') + assert ellipk(2).ae(mpc('1.3110287771460599052', '-1.3110287771460599052')) + assert ellipk(50).ae(mpc('0.22326753950210985451', '-0.47434723226254522087')) + assert ellipk(3+4j).ae(mpc('0.91119556380496500866', '0.63133428324134524388')) + assert ellipk(3-4j).ae(mpc('0.91119556380496500866', '-0.63133428324134524388')) + assert ellipk(-3+4j).ae(mpc('0.95357894880405122483', '0.23093044503746114444')) + assert ellipk(-3-4j).ae(mpc('0.95357894880405122483', '-0.23093044503746114444')) + assert isnan(ellipk(nan)) + assert isnan(ellipe(nan)) + assert ellipk(inf) == 0 + assert isinstance(ellipk(inf), mpc) + assert ellipk(-inf) == 0 + assert ellipk(1+0j) == inf + assert ellipe(0).ae(pi/2) + assert ellipe(0.5).ae(pi**(mpf(3)/2)/gamma(0.25)**2 +gamma(0.25)**2/(8*sqrt(pi))) + assert ellipe(1) == 1 + assert ellipe(1+0j) == 1 + assert ellipe(inf) == mpc(0,inf) + assert ellipe(-inf) == inf + assert ellipe(3+4j).ae(1.4995535209333469543-1.5778790079127582745j) + assert ellipe(3-4j).ae(1.4995535209333469543+1.5778790079127582745j) + assert ellipe(-3+4j).ae(2.5804237855343377803-0.8306096791000413778j) + assert ellipe(-3-4j).ae(2.5804237855343377803+0.8306096791000413778j) + assert ellipe(2).ae(0.59907011736779610372+0.59907011736779610372j) + assert ellipe('1e-1000000000').ae(pi/2) + assert ellipk('1e-1000000000').ae(pi/2) + assert ellipe(-pi).ae(2.4535865983838923) + mp.dps = 50 + assert ellipk(1/pi).ae('1.724756270009501831744438120951614673874904182624739673') + assert ellipe(1/pi).ae('1.437129808135123030101542922290970050337425479058225712') + assert ellipk(-10*pi).ae('0.5519067523886233967683646782286965823151896970015484512') + assert ellipe(-10*pi).ae('5.926192483740483797854383268707108012328213431657645509') + v = ellipk(pi) + assert v.real.ae('0.973089521698042334840454592642137667227167622330325225') + assert v.imag.ae('-1.156151296372835303836814390793087600271609993858798016') + v = ellipe(pi) + assert v.real.ae('0.4632848917264710404078033487934663562998345622611263332') + assert v.imag.ae('1.0637961621753130852473300451583414489944099504180510966') + mp.dps = 15 + +def test_exp_integrals(): + mp.dps = 15 + x = +e + z = e + sqrt(3)*j + assert ei(x).ae(8.21168165538361560) + assert li(x).ae(1.89511781635593676) + assert si(x).ae(1.82104026914756705) + assert ci(x).ae(0.213958001340379779) + assert shi(x).ae(4.11520706247846193) + assert chi(x).ae(4.09647459290515367) + assert fresnels(x).ae(0.437189718149787643) + assert fresnelc(x).ae(0.401777759590243012) + assert airyai(x).ae(0.0108502401568586681) + assert airybi(x).ae(8.98245748585468627) + assert ei(z).ae(3.72597969491314951 + 7.34213212314224421j) + assert li(z).ae(2.28662658112562502 + 1.50427225297269364j) + assert si(z).ae(2.48122029237669054 + 0.12684703275254834j) + assert ci(z).ae(0.169255590269456633 - 0.892020751420780353j) + assert shi(z).ae(1.85810366559344468 + 3.66435842914920263j) + assert chi(z).ae(1.86787602931970484 + 3.67777369399304159j) + assert fresnels(z/3).ae(0.034534397197008182 + 0.754859844188218737j) + assert fresnelc(z/3).ae(1.261581645990027372 + 0.417949198775061893j) + assert airyai(z).ae(-0.0162552579839056062 - 0.0018045715700210556j) + assert airybi(z).ae(-4.98856113282883371 + 2.08558537872180623j) + assert li(0) == 0.0 + assert li(1) == -inf + assert li(inf) == inf + assert isinstance(li(0.7), mpf) + assert si(inf).ae(pi/2) + assert si(-inf).ae(-pi/2) + assert ci(inf) == 0 + assert ci(0) == -inf + assert isinstance(ei(-0.7), mpf) + assert airyai(inf) == 0 + assert airybi(inf) == inf + assert airyai(-inf) == 0 + assert airybi(-inf) == 0 + assert fresnels(inf) == 0.5 + assert fresnelc(inf) == 0.5 + assert fresnels(-inf) == -0.5 + assert fresnelc(-inf) == -0.5 + assert shi(0) == 0 + assert shi(inf) == inf + assert shi(-inf) == -inf + assert chi(0) == -inf + assert chi(inf) == inf + +def test_ei(): + mp.dps = 15 + assert ei(0) == -inf + assert ei(inf) == inf + assert ei(-inf) == -0.0 + assert ei(20+70j).ae(6.1041351911152984397e6 - 2.7324109310519928872e6j) + # tests for the asymptotic expansion + # values checked with Mathematica ExpIntegralEi + mp.dps = 50 + r = ei(20000) + s = '3.8781962825045010930273870085501819470698476975019e+8681' + assert str(r) == s + r = ei(-200) + s = '-6.8852261063076355977108174824557929738368086933303e-90' + assert str(r) == s + r =ei(20000 + 10*j) + sre = '-3.255138234032069402493850638874410725961401274106e+8681' + sim = '-2.1081929993474403520785942429469187647767369645423e+8681' + assert str(r.real) == sre and str(r.imag) == sim + mp.dps = 15 + # More asymptotic expansions + assert chi(-10**6+100j).ae('1.3077239389562548386e+434288 + 7.6808956999707408158e+434287j') + assert shi(-10**6+100j).ae('-1.3077239389562548386e+434288 - 7.6808956999707408158e+434287j') + mp.dps = 15 + assert ei(10j).ae(-0.0454564330044553726+3.2291439210137706686j) + assert ei(100j).ae(-0.0051488251426104921+3.1330217936839529126j) + u = ei(fmul(10**20, j, exact=True)) + assert u.real.ae(-6.4525128526578084421345e-21, abs_eps=0, rel_eps=8*eps) + assert u.imag.ae(pi) + assert ei(-10j).ae(-0.0454564330044553726-3.2291439210137706686j) + assert ei(-100j).ae(-0.0051488251426104921-3.1330217936839529126j) + u = ei(fmul(-10**20, j, exact=True)) + assert u.real.ae(-6.4525128526578084421345e-21, abs_eps=0, rel_eps=8*eps) + assert u.imag.ae(-pi) + assert ei(10+10j).ae(-1576.1504265768517448+436.9192317011328140j) + u = ei(-10+10j) + assert u.real.ae(7.6698978415553488362543e-7, abs_eps=0, rel_eps=8*eps) + assert u.imag.ae(3.141595611735621062025) + +def test_e1(): + mp.dps = 15 + assert e1(0) == inf + assert e1(inf) == 0 + assert e1(-inf) == mpc(-inf, -pi) + assert e1(10j).ae(0.045456433004455372635 + 0.087551267423977430100j) + assert e1(100j).ae(0.0051488251426104921444 - 0.0085708599058403258790j) + assert e1(fmul(10**20, j, exact=True)).ae(6.4525128526578084421e-21 - 7.6397040444172830039e-21j, abs_eps=0, rel_eps=8*eps) + assert e1(-10j).ae(0.045456433004455372635 - 0.087551267423977430100j) + assert e1(-100j).ae(0.0051488251426104921444 + 0.0085708599058403258790j) + assert e1(fmul(-10**20, j, exact=True)).ae(6.4525128526578084421e-21 + 7.6397040444172830039e-21j, abs_eps=0, rel_eps=8*eps) + +def test_expint(): + mp.dps = 15 + assert expint(0,0) == inf + assert expint(0,1).ae(1/e) + assert expint(0,1.5).ae(2/exp(1.5)/3) + assert expint(1,1).ae(-ei(-1)) + assert expint(2,0).ae(1) + assert expint(3,0).ae(1/2.) + assert expint(4,0).ae(1/3.) + assert expint(-2, 0.5).ae(26/sqrt(e)) + assert expint(-1,-1) == 0 + assert expint(-2,-1).ae(-e) + assert expint(5.5, 0).ae(2/9.) + assert expint(2.00000001,0).ae(100000000./100000001) + assert expint(2+3j,4-j).ae(0.0023461179581675065414+0.0020395540604713669262j) + assert expint('1.01', '1e-1000').ae(99.9999999899412802) + assert expint('1.000000000001', 3.5).ae(0.00697013985754701819446) + assert expint(2,3).ae(3*ei(-3)+exp(-3)) + assert (expint(10,20)*10**10).ae(0.694439055541231353) + assert expint(3,inf) == 0 + assert expint(3.2,inf) == 0 + assert expint(3.2+2j,inf) == 0 + assert expint(1,3j).ae(-0.11962978600800032763 + 0.27785620120457163717j) + assert expint(1,3).ae(0.013048381094197037413) + assert expint(1,-3).ae(-ei(3)-pi*j) + #assert expint(3) == expint(1,3) + assert expint(1,-20).ae(-25615652.66405658882 - 3.1415926535897932385j) + assert expint(1000000,0).ae(1./999999) + assert expint(0,2+3j).ae(-0.025019798357114678171 + 0.027980439405104419040j) + assert expint(-1,2+3j).ae(-0.022411973626262070419 + 0.038058922011377716932j) + assert expint(-1.5,0) == inf + +def test_trig_integrals(): + mp.dps = 30 + assert si(mpf(1)/1000000).ae('0.000000999999999999944444444444446111') + assert ci(mpf(1)/1000000).ae('-13.2382948930629912435014366276') + assert si(10**10).ae('1.5707963267075846569685111517747537') + assert ci(10**10).ae('-4.87506025174822653785729773959e-11') + assert si(10**100).ae(pi/2) + assert (ci(10**100)*10**100).ae('-0.372376123661276688262086695553') + assert si(-3) == -si(3) + assert ci(-3).ae(ci(3) + pi*j) + # Test complex structure + mp.dps = 15 + assert mp.ci(50).ae(-0.0056283863241163054402) + assert mp.ci(50+2j).ae(-0.018378282946133067149+0.070352808023688336193j) + assert mp.ci(20j).ae(1.28078263320282943611e7+1.5707963267949j) + assert mp.ci(-2+20j).ae(-4.050116856873293505e6+1.207476188206989909e7j) + assert mp.ci(-50+2j).ae(-0.0183782829461330671+3.0712398455661049023j) + assert mp.ci(-50).ae(-0.0056283863241163054+3.1415926535897932385j) + assert mp.ci(-50-2j).ae(-0.0183782829461330671-3.0712398455661049023j) + assert mp.ci(-2-20j).ae(-4.050116856873293505e6-1.207476188206989909e7j) + assert mp.ci(-20j).ae(1.28078263320282943611e7-1.5707963267949j) + assert mp.ci(50-2j).ae(-0.018378282946133067149-0.070352808023688336193j) + assert mp.si(50).ae(1.5516170724859358947) + assert mp.si(50+2j).ae(1.497884414277228461-0.017515007378437448j) + assert mp.si(20j).ae(1.2807826332028294459e7j) + assert mp.si(-2+20j).ae(-1.20747603112735722103e7-4.050116856873293554e6j) + assert mp.si(-50+2j).ae(-1.497884414277228461-0.017515007378437448j) + assert mp.si(-50).ae(-1.5516170724859358947) + assert mp.si(-50-2j).ae(-1.497884414277228461+0.017515007378437448j) + assert mp.si(-2-20j).ae(-1.20747603112735722103e7+4.050116856873293554e6j) + assert mp.si(-20j).ae(-1.2807826332028294459e7j) + assert mp.si(50-2j).ae(1.497884414277228461+0.017515007378437448j) + assert mp.chi(50j).ae(-0.0056283863241163054+1.5707963267948966192j) + assert mp.chi(-2+50j).ae(-0.0183782829461330671+1.6411491348185849554j) + assert mp.chi(-20).ae(1.28078263320282943611e7+3.1415926535898j) + assert mp.chi(-20-2j).ae(-4.050116856873293505e6+1.20747571696809187053e7j) + assert mp.chi(-2-50j).ae(-0.0183782829461330671-1.6411491348185849554j) + assert mp.chi(-50j).ae(-0.0056283863241163054-1.5707963267948966192j) + assert mp.chi(2-50j).ae(-0.0183782829461330671-1.500443518771208283j) + assert mp.chi(20-2j).ae(-4.050116856873293505e6-1.20747603112735722951e7j) + assert mp.chi(20).ae(1.2807826332028294361e7) + assert mp.chi(2+50j).ae(-0.0183782829461330671+1.500443518771208283j) + assert mp.shi(50j).ae(1.5516170724859358947j) + assert mp.shi(-2+50j).ae(0.017515007378437448+1.497884414277228461j) + assert mp.shi(-20).ae(-1.2807826332028294459e7) + assert mp.shi(-20-2j).ae(4.050116856873293554e6-1.20747603112735722103e7j) + assert mp.shi(-2-50j).ae(0.017515007378437448-1.497884414277228461j) + assert mp.shi(-50j).ae(-1.5516170724859358947j) + assert mp.shi(2-50j).ae(-0.017515007378437448-1.497884414277228461j) + assert mp.shi(20-2j).ae(-4.050116856873293554e6-1.20747603112735722103e7j) + assert mp.shi(20).ae(1.2807826332028294459e7) + assert mp.shi(2+50j).ae(-0.017515007378437448+1.497884414277228461j) + def ae(x,y,tol=1e-12): + return abs(x-y) <= abs(y)*tol + assert fp.ci(fp.inf) == 0 + assert ae(fp.ci(fp.ninf), fp.pi*1j) + assert ae(fp.si(fp.inf), fp.pi/2) + assert ae(fp.si(fp.ninf), -fp.pi/2) + assert fp.si(0) == 0 + assert ae(fp.ci(50), -0.0056283863241163054402) + assert ae(fp.ci(50+2j), -0.018378282946133067149+0.070352808023688336193j) + assert ae(fp.ci(20j), 1.28078263320282943611e7+1.5707963267949j) + assert ae(fp.ci(-2+20j), -4.050116856873293505e6+1.207476188206989909e7j) + assert ae(fp.ci(-50+2j), -0.0183782829461330671+3.0712398455661049023j) + assert ae(fp.ci(-50), -0.0056283863241163054+3.1415926535897932385j) + assert ae(fp.ci(-50-2j), -0.0183782829461330671-3.0712398455661049023j) + assert ae(fp.ci(-2-20j), -4.050116856873293505e6-1.207476188206989909e7j) + assert ae(fp.ci(-20j), 1.28078263320282943611e7-1.5707963267949j) + assert ae(fp.ci(50-2j), -0.018378282946133067149-0.070352808023688336193j) + assert ae(fp.si(50), 1.5516170724859358947) + assert ae(fp.si(50+2j), 1.497884414277228461-0.017515007378437448j) + assert ae(fp.si(20j), 1.2807826332028294459e7j) + assert ae(fp.si(-2+20j), -1.20747603112735722103e7-4.050116856873293554e6j) + assert ae(fp.si(-50+2j), -1.497884414277228461-0.017515007378437448j) + assert ae(fp.si(-50), -1.5516170724859358947) + assert ae(fp.si(-50-2j), -1.497884414277228461+0.017515007378437448j) + assert ae(fp.si(-2-20j), -1.20747603112735722103e7+4.050116856873293554e6j) + assert ae(fp.si(-20j), -1.2807826332028294459e7j) + assert ae(fp.si(50-2j), 1.497884414277228461+0.017515007378437448j) + assert ae(fp.chi(50j), -0.0056283863241163054+1.5707963267948966192j) + assert ae(fp.chi(-2+50j), -0.0183782829461330671+1.6411491348185849554j) + assert ae(fp.chi(-20), 1.28078263320282943611e7+3.1415926535898j) + assert ae(fp.chi(-20-2j), -4.050116856873293505e6+1.20747571696809187053e7j) + assert ae(fp.chi(-2-50j), -0.0183782829461330671-1.6411491348185849554j) + assert ae(fp.chi(-50j), -0.0056283863241163054-1.5707963267948966192j) + assert ae(fp.chi(2-50j), -0.0183782829461330671-1.500443518771208283j) + assert ae(fp.chi(20-2j), -4.050116856873293505e6-1.20747603112735722951e7j) + assert ae(fp.chi(20), 1.2807826332028294361e7) + assert ae(fp.chi(2+50j), -0.0183782829461330671+1.500443518771208283j) + assert ae(fp.shi(50j), 1.5516170724859358947j) + assert ae(fp.shi(-2+50j), 0.017515007378437448+1.497884414277228461j) + assert ae(fp.shi(-20), -1.2807826332028294459e7) + assert ae(fp.shi(-20-2j), 4.050116856873293554e6-1.20747603112735722103e7j) + assert ae(fp.shi(-2-50j), 0.017515007378437448-1.497884414277228461j) + assert ae(fp.shi(-50j), -1.5516170724859358947j) + assert ae(fp.shi(2-50j), -0.017515007378437448-1.497884414277228461j) + assert ae(fp.shi(20-2j), -4.050116856873293554e6-1.20747603112735722103e7j) + assert ae(fp.shi(20), 1.2807826332028294459e7) + assert ae(fp.shi(2+50j), -0.017515007378437448+1.497884414277228461j) + +def test_airy(): + mp.dps = 15 + assert (airyai(10)*10**10).ae(1.1047532552898687) + assert (airybi(10)/10**9).ae(0.45564115354822515) + assert (airyai(1000)*10**9158).ae(9.306933063179556004) + assert (airybi(1000)/10**9154).ae(5.4077118391949465477) + assert airyai(-1000).ae(0.055971895773019918842) + assert airybi(-1000).ae(-0.083264574117080633012) + assert (airyai(100+100j)*10**188).ae(2.9099582462207032076 + 2.353013591706178756j) + assert (airybi(100+100j)/10**185).ae(1.7086751714463652039 - 3.1416590020830804578j) + +def test_hyper_0f1(): + mp.dps = 15 + v = 8.63911136507950465 + assert hyper([],[(1,3)],1.5).ae(v) + assert hyper([],[1/3.],1.5).ae(v) + assert hyp0f1(1/3.,1.5).ae(v) + assert hyp0f1((1,3),1.5).ae(v) + # Asymptotic expansion + assert hyp0f1(3,1e9).ae('4.9679055380347771271e+27455') + assert hyp0f1(3,1e9j).ae('-2.1222788784457702157e+19410 + 5.0840597555401854116e+19410j') + +def test_hyper_1f1(): + mp.dps = 15 + v = 1.2917526488617656673 + assert hyper([(1,2)],[(3,2)],0.7).ae(v) + assert hyper([(1,2)],[(3,2)],0.7+0j).ae(v) + assert hyper([0.5],[(3,2)],0.7).ae(v) + assert hyper([0.5],[1.5],0.7).ae(v) + assert hyper([0.5],[(3,2)],0.7+0j).ae(v) + assert hyper([0.5],[1.5],0.7+0j).ae(v) + assert hyper([(1,2)],[1.5+0j],0.7).ae(v) + assert hyper([0.5+0j],[1.5],0.7).ae(v) + assert hyper([0.5+0j],[1.5+0j],0.7+0j).ae(v) + assert hyp1f1(0.5,1.5,0.7).ae(v) + assert hyp1f1((1,2),1.5,0.7).ae(v) + # Asymptotic expansion + assert hyp1f1(2,3,1e10).ae('2.1555012157015796988e+4342944809') + assert (hyp1f1(2,3,1e10j)*10**10).ae(-0.97501205020039745852 - 1.7462392454512132074j) + # Shouldn't use asymptotic expansion + assert hyp1f1(-2, 1, 10000).ae(49980001) + +def test_hyper_2f1(): + mp.dps = 15 + v = 1.0652207633823291032 + assert hyper([(1,2), (3,4)], [2], 0.3).ae(v) + assert hyper([(1,2), 0.75], [2], 0.3).ae(v) + assert hyper([0.5, 0.75], [2.0], 0.3).ae(v) + assert hyper([0.5, 0.75], [2.0], 0.3+0j).ae(v) + assert hyper([0.5+0j, (3,4)], [2.0], 0.3+0j).ae(v) + assert hyper([0.5+0j, (3,4)], [2.0], 0.3).ae(v) + assert hyper([0.5, (3,4)], [2.0+0j], 0.3).ae(v) + assert hyper([0.5+0j, 0.75+0j], [2.0+0j], 0.3+0j).ae(v) + v = 1.09234681096223231717 + 0.18104859169479360380j + assert hyper([(1,2),0.75+j], [2], 0.5).ae(v) + assert hyper([0.5,0.75+j], [2.0], 0.5).ae(v) + assert hyper([0.5,0.75+j], [2.0], 0.5+0j).ae(v) + assert hyper([0.5,0.75+j], [2.0+0j], 0.5+0j).ae(v) + v = 0.9625 - 0.125j + assert hyper([(3,2),-1],[4], 0.1+j/3).ae(v) + assert hyper([1.5,-1.0],[4], 0.1+j/3).ae(v) + assert hyper([1.5,-1.0],[4+0j], 0.1+j/3).ae(v) + assert hyper([1.5+0j,-1.0+0j],[4+0j], 0.1+j/3).ae(v) + v = 1.02111069501693445001 - 0.50402252613466859521j + assert hyper([(2,10),(3,10)],[(4,10)],1.5).ae(v) + assert hyper([0.2,(3,10)],[0.4+0j],1.5).ae(v) + assert hyper([0.2,(3,10)],[0.4+0j],1.5+0j).ae(v) + v = 0.76922501362865848528 + 0.32640579593235886194j + assert hyper([(2,10),(3,10)],[(4,10)],4+2j).ae(v) + assert hyper([0.2,(3,10)],[0.4+0j],4+2j).ae(v) + assert hyper([0.2,(3,10)],[(4,10)],4+2j).ae(v) + +def test_hyper_2f1_hard(): + mp.dps = 15 + # Singular cases + assert hyp2f1(2,-1,-1,3).ae(0.25) + assert hyp2f1(2,-2,-2,3).ae(0.25) + assert hyp2f1(2,-1,-1,3,eliminate=False) == 7 + assert hyp2f1(2,-2,-2,3,eliminate=False) == 34 + assert hyp2f1(2,-2,-3,3) == 14 + assert hyp2f1(2,-3,-2,3) == inf + assert hyp2f1(2,-1.5,-1.5,3) == 0.25 + assert hyp2f1(1,2,3,0) == 1 + assert hyp2f1(0,1,0,0) == 1 + assert hyp2f1(0,0,0,0) == 1 + assert isnan(hyp2f1(1,1,0,0)) + assert hyp2f1(2,-1,-5, 0.25+0.25j).ae(1.1+0.1j) + assert hyp2f1(2,-5,-5, 0.25+0.25j, eliminate=False).ae(163./128 + 125./128*j) + assert hyp2f1(0.7235, -1, -5, 0.3).ae(1.04341) + assert hyp2f1(0.7235, -5, -5, 0.3, eliminate=False).ae(1.2939225017815903812) + assert hyp2f1(-1,-2,4,1) == 1.5 + assert hyp2f1(1,2,-3,1) == inf + assert hyp2f1(-2,-2,1,1) == 6 + assert hyp2f1(1,-2,-4,1).ae(5./3) + assert hyp2f1(0,-6,-4,1) == 1 + assert hyp2f1(0,-3,-4,1) == 1 + assert hyp2f1(0,0,0,1) == 1 + assert hyp2f1(1,0,0,1,eliminate=False) == 1 + assert hyp2f1(1,1,0,1) == inf + assert hyp2f1(1,-6,-4,1) == inf + assert hyp2f1(-7.2,-0.5,-4.5,1) == 0 + assert hyp2f1(-7.2,-1,-2,1).ae(-2.6) + assert hyp2f1(1,-0.5,-4.5, 1) == inf + assert hyp2f1(1,0.5,-4.5, 1) == -inf + # Check evaluation on / close to unit circle + z = exp(j*pi/3) + w = (nthroot(2,3)+1)*exp(j*pi/12)/nthroot(3,4)**3 + assert hyp2f1('1/2','1/6','1/3', z).ae(w) + assert hyp2f1('1/2','1/6','1/3', z.conjugate()).ae(w.conjugate()) + assert hyp2f1(0.25, (1,3), 2, '0.999').ae(1.06826449496030635) + assert hyp2f1(0.25, (1,3), 2, '1.001').ae(1.06867299254830309446-0.00001446586793975874j) + assert hyp2f1(0.25, (1,3), 2, -1).ae(0.96656584492524351673) + assert hyp2f1(0.25, (1,3), 2, j).ae(0.99041766248982072266+0.03777135604180735522j) + assert hyp2f1(2,3,5,'0.99').ae(27.699347904322690602) + assert hyp2f1((3,2),-0.5,3,'0.99').ae(0.68403036843911661388) + assert hyp2f1(2,3,5,1j).ae(0.37290667145974386127+0.59210004902748285917j) + assert fsum([hyp2f1((7,10),(2,3),(-1,2), 0.95*exp(j*k)) for k in range(1,15)]).ae(52.851400204289452922+6.244285013912953225j) + assert fsum([hyp2f1((7,10),(2,3),(-1,2), 1.05*exp(j*k)) for k in range(1,15)]).ae(54.506013786220655330-3.000118813413217097j) + assert fsum([hyp2f1((7,10),(2,3),(-1,2), exp(j*k)) for k in range(1,15)]).ae(55.792077935955314887+1.731986485778500241j) + assert hyp2f1(2,2.5,-3.25,0.999).ae(218373932801217082543180041.33) + # Branches + assert hyp2f1(1,1,2,1.01).ae(4.5595744415723676911-3.1104877758314784539j) + assert hyp2f1(1,1,2,1.01+0.1j).ae(2.4149427480552782484+1.4148224796836938829j) + assert hyp2f1(1,1,2,3+4j).ae(0.14576709331407297807+0.48379185417980360773j) + assert hyp2f1(1,1,2,4).ae(-0.27465307216702742285 - 0.78539816339744830962j) + assert hyp2f1(1,1,2,-4).ae(0.40235947810852509365) + # Other: + # Cancellation with a large parameter involved (bug reported on sage-devel) + assert hyp2f1(112, (51,10), (-9,10), -0.99999).ae(-1.6241361047970862961e-24, abs_eps=0, rel_eps=eps*16) + +def test_hyper_3f2_etc(): + assert hyper([1,2,3],[1.5,8],-1).ae(0.67108992351533333030) + assert hyper([1,2,3,4],[5,6,7], -1).ae(0.90232988035425506008) + assert hyper([1,2,3],[1.25,5], 1).ae(28.924181329701905701) + assert hyper([1,2,3,4],[5,6,7],5).ae(1.5192307344006649499-1.1529845225075537461j) + assert hyper([1,2,3,4,5],[6,7,8,9],-1).ae(0.96288759462882357253) + assert hyper([1,2,3,4,5],[6,7,8,9],1).ae(1.0428697385885855841) + assert hyper([1,2,3,4,5],[6,7,8,9],5).ae(1.33980653631074769423-0.07143405251029226699j) + assert hyper([1,2.79,3.08,4.37],[5.2,6.1,7.3],5).ae(1.0996321464692607231-1.7748052293979985001j) + assert hyper([1,1,1],[1,2],1) == inf + assert hyper([1,1,1],[2,(101,100)],1).ae(100.01621213528313220) + # slow -- covered by doctests + #assert hyper([1,1,1],[2,3],0.9999).ae(1.2897972005319693905) + +def test_hyper_u(): + mp.dps = 15 + assert hyperu(2,-3,0).ae(0.05) + assert hyperu(2,-3.5,0).ae(4./99) + assert hyperu(2,0,0) == 0.5 + assert hyperu(-5,1,0) == -120 + assert hyperu(-5,2,0) == inf + assert hyperu(-5,-2,0) == 0 + assert hyperu(7,7,3).ae(0.00014681269365593503986) #exp(3)*gammainc(-6,3) + assert hyperu(2,-3,4).ae(0.011836478100271995559) + assert hyperu(3,4,5).ae(1./125) + assert hyperu(2,3,0.0625) == 256 + assert hyperu(-1,2,0.25+0.5j) == -1.75+0.5j + assert hyperu(0.5,1.5,7.25).ae(2/sqrt(29)) + assert hyperu(2,6,pi).ae(0.55804439825913399130) + assert (hyperu((3,2),8,100+201j)*10**4).ae(-0.3797318333856738798 - 2.9974928453561707782j) + assert (hyperu((5,2),(-1,2),-5000)*10**10).ae(-5.6681877926881664678j) + # XXX: fails because of undetected cancellation in low level series code + # Alternatively: could use asymptotic series here, if convergence test + # tweaked back to recognize this one + #assert (hyperu((5,2),(-1,2),-500)*10**7).ae(-1.82526906001593252847j) + +def test_hyper_2f0(): + mp.dps = 15 + assert hyper([1,2],[],3) == hyp2f0(1,2,3) + assert hyp2f0(2,3,7).ae(0.0116108068639728714668 - 0.0073727413865865802130j) + assert hyp2f0(2,3,0) == 1 + assert hyp2f0(0,0,0) == 1 + assert hyp2f0(-1,-1,1).ae(2) + assert hyp2f0(-4,1,1.5).ae(62.5) + assert hyp2f0(-4,1,50).ae(147029801) + assert hyp2f0(-4,1,0.0001).ae(0.99960011997600240000) + assert hyp2f0(0.5,0.25,0.001).ae(1.0001251174078538115) + assert hyp2f0(0.5,0.25,3+4j).ae(0.85548875824755163518 + 0.21636041283392292973j) + # Important: cancellation check + assert hyp2f0((1,6),(5,6),-0.02371708245126284498).ae(0.996785723120804309) + # Should be exact; polynomial case + assert hyp2f0(-2,1,0.5+0.5j) == 0 + assert hyp2f0(1,-2,0.5+0.5j) == 0 + # There used to be a bug in thresholds that made one of the following hang + for d in [15, 50, 80]: + mp.dps = d + assert hyp2f0(1.5, 0.5, 0.009).ae('1.006867007239309717945323585695344927904000945829843527398772456281301440034218290443367270629519483 + 1.238277162240704919639384945859073461954721356062919829456053965502443570466701567100438048602352623e-46j') + +def test_hyper_1f2(): + mp.dps = 15 + assert hyper([1],[2,3],4) == hyp1f2(1,2,3,4) + a1,b1,b2 = (1,10),(2,3),1./16 + assert hyp1f2(a1,b1,b2,10).ae(298.7482725554557568) + assert hyp1f2(a1,b1,b2,100).ae(224128961.48602947604) + assert hyp1f2(a1,b1,b2,1000).ae(1.1669528298622675109e+27) + assert hyp1f2(a1,b1,b2,10000).ae(2.4780514622487212192e+86) + assert hyp1f2(a1,b1,b2,100000).ae(1.3885391458871523997e+274) + assert hyp1f2(a1,b1,b2,1000000).ae('9.8851796978960318255e+867') + assert hyp1f2(a1,b1,b2,10**7).ae('1.1505659189516303646e+2746') + assert hyp1f2(a1,b1,b2,10**8).ae('1.4672005404314334081e+8685') + assert hyp1f2(a1,b1,b2,10**20).ae('3.6888217332150976493e+8685889636') + assert hyp1f2(a1,b1,b2,10*j).ae(-16.163252524618572878 - 44.321567896480184312j) + assert hyp1f2(a1,b1,b2,100*j).ae(61938.155294517848171 + 637349.45215942348739j) + assert hyp1f2(a1,b1,b2,1000*j).ae(8455057657257695958.7 + 6261969266997571510.6j) + assert hyp1f2(a1,b1,b2,10000*j).ae(-8.9771211184008593089e+60 + 4.6550528111731631456e+59j) + assert hyp1f2(a1,b1,b2,100000*j).ae(2.6398091437239324225e+193 + 4.1658080666870618332e+193j) + assert hyp1f2(a1,b1,b2,1000000*j).ae('3.5999042951925965458e+613 + 1.5026014707128947992e+613j') + assert hyp1f2(a1,b1,b2,10**7*j).ae('-8.3208715051623234801e+1939 - 3.6752883490851869429e+1941j') + assert hyp1f2(a1,b1,b2,10**8*j).ae('2.0724195707891484454e+6140 - 1.3276619482724266387e+6141j') + assert hyp1f2(a1,b1,b2,10**20*j).ae('-1.1734497974795488504e+6141851462 + 1.1498106965385471542e+6141851462j') + +def test_hyper_2f3(): + mp.dps = 15 + assert hyper([1,2],[3,4,5],6) == hyp2f3(1,2,3,4,5,6) + a1,a2,b1,b2,b3 = (1,10),(2,3),(3,10), 2, 1./16 + # Check asymptotic expansion + assert hyp2f3(a1,a2,b1,b2,b3,10).ae(128.98207160698659976) + assert hyp2f3(a1,a2,b1,b2,b3,1000).ae(6.6309632883131273141e25) + assert hyp2f3(a1,a2,b1,b2,b3,10000).ae(4.6863639362713340539e84) + assert hyp2f3(a1,a2,b1,b2,b3,100000).ae(8.6632451236103084119e271) + assert hyp2f3(a1,a2,b1,b2,b3,10**6).ae('2.0291718386574980641e865') + assert hyp2f3(a1,a2,b1,b2,b3,10**7).ae('7.7639836665710030977e2742') + assert hyp2f3(a1,a2,b1,b2,b3,10**8).ae('3.2537462584071268759e8681') + assert hyp2f3(a1,a2,b1,b2,b3,10**20).ae('1.2966030542911614163e+8685889627') + assert hyp2f3(a1,a2,b1,b2,b3,10*j).ae(-18.551602185587547854 - 13.348031097874113552j) + assert hyp2f3(a1,a2,b1,b2,b3,100*j).ae(78634.359124504488695 + 74459.535945281973996j) + assert hyp2f3(a1,a2,b1,b2,b3,1000*j).ae(597682550276527901.59 - 65136194809352613.078j) + assert hyp2f3(a1,a2,b1,b2,b3,10000*j).ae(-1.1779696326238582496e+59 + 1.2297607505213133872e+59j) + assert hyp2f3(a1,a2,b1,b2,b3,100000*j).ae(2.9844228969804380301e+191 + 7.5587163231490273296e+190j) + assert hyp2f3(a1,a2,b1,b2,b3,1000000*j).ae('7.4859161049322370311e+610 - 2.8467477015940090189e+610j') + assert hyp2f3(a1,a2,b1,b2,b3,10**7*j).ae('-1.7477645579418800826e+1938 - 1.7606522995808116405e+1938j') + assert hyp2f3(a1,a2,b1,b2,b3,10**8*j).ae('-1.6932731942958401784e+6137 - 2.4521909113114629368e+6137j') + assert hyp2f3(a1,a2,b1,b2,b3,10**20*j).ae('-2.0988815677627225449e+6141851451 + 5.7708223542739208681e+6141851452j') + +def test_hyper_2f2(): + mp.dps = 15 + assert hyper([1,2],[3,4],5) == hyp2f2(1,2,3,4,5) + a1,a2,b1,b2 = (3,10),4,(1,2),1./16 + assert hyp2f2(a1,a2,b1,b2,10).ae(448225936.3377556696) + assert hyp2f2(a1,a2,b1,b2,10000).ae('1.2012553712966636711e+4358') + assert hyp2f2(a1,a2,b1,b2,-20000).ae(-0.04182343755661214626) + assert hyp2f2(a1,a2,b1,b2,10**20).ae('1.1148680024303263661e+43429448190325182840') + +def test_orthpoly(): + mp.dps = 15 + assert jacobi(-4,2,3,0.7).ae(22800./4913) + assert jacobi(3,2,4,5.5) == 4133.125 + assert jacobi(1.5,5/6.,4,0).ae(-1.0851951434075508417) + assert jacobi(-2, 1, 2, 4).ae(-0.16) + assert jacobi(2, -1, 2.5, 4).ae(34.59375) + #assert jacobi(2, -1, 2, 4) == 28.5 + assert legendre(5, 7) == 129367 + assert legendre(0.5,0).ae(0.53935260118837935667) + assert legendre(-1,-1) == 1 + assert legendre(0,-1) == 1 + assert legendre(0, 1) == 1 + assert legendre(1, -1) == -1 + assert legendre(7, 1) == 1 + assert legendre(7, -1) == -1 + assert legendre(8,1.5).ae(15457523./32768) + assert legendre(j,-j).ae(2.4448182735671431011 + 0.6928881737669934843j) + assert chebyu(5,1) == 6 + assert chebyt(3,2) == 26 + assert legendre(3.5,-1) == inf + assert legendre(4.5,-1) == -inf + assert legendre(3.5+1j,-1) == mpc(inf,inf) + assert legendre(4.5+1j,-1) == mpc(-inf,-inf) + assert laguerre(4, -2, 3).ae(-1.125) + assert laguerre(3, 1+j, 0.5).ae(0.2291666666666666667 + 2.5416666666666666667j) + +def test_hermite(): + mp.dps = 15 + assert hermite(-2, 0).ae(0.5) + assert hermite(-1, 0).ae(0.88622692545275801365) + assert hermite(0, 0).ae(1) + assert hermite(1, 0) == 0 + assert hermite(2, 0).ae(-2) + assert hermite(0, 2).ae(1) + assert hermite(1, 2).ae(4) + assert hermite(1, -2).ae(-4) + assert hermite(2, -2).ae(14) + assert hermite(0.5, 0).ae(0.69136733903629335053) + assert hermite(9, 0) == 0 + assert hermite(4,4).ae(3340) + assert hermite(3,4).ae(464) + assert hermite(-4,4).ae(0.00018623860287512396181) + assert hermite(-3,4).ae(0.0016540169879668766270) + assert hermite(9, 2.5j).ae(13638725j) + assert hermite(9, -2.5j).ae(-13638725j) + assert hermite(9, 100).ae(511078883759363024000) + assert hermite(9, -100).ae(-511078883759363024000) + assert hermite(9, 100j).ae(512922083920643024000j) + assert hermite(9, -100j).ae(-512922083920643024000j) + assert hermite(-9.5, 2.5j).ae(-2.9004951258126778174e-6 + 1.7601372934039951100e-6j) + assert hermite(-9.5, -2.5j).ae(-2.9004951258126778174e-6 - 1.7601372934039951100e-6j) + assert hermite(-9.5, 100).ae(1.3776300722767084162e-22, abs_eps=0, rel_eps=eps) + assert hermite(-9.5, -100).ae('1.3106082028470671626e4355') + assert hermite(-9.5, 100j).ae(-9.7900218581864768430e-23 - 9.7900218581864768430e-23j, abs_eps=0, rel_eps=eps) + assert hermite(-9.5, -100j).ae(-9.7900218581864768430e-23 + 9.7900218581864768430e-23j, abs_eps=0, rel_eps=eps) + assert hermite(2+3j, -1-j).ae(851.3677063883687676 - 1496.4373467871007997j) + +def test_gegenbauer(): + mp.dps = 15 + assert gegenbauer(1,2,3).ae(12) + assert gegenbauer(2,3,4).ae(381) + assert gegenbauer(0,0,0) == 0 + assert gegenbauer(2,-1,3) == 0 + assert gegenbauer(-7, 0.5, 3).ae(8989) + assert gegenbauer(1, -0.5, 3).ae(-3) + assert gegenbauer(1, -1.5, 3).ae(-9) + assert gegenbauer(1, -0.5, 3).ae(-3) + assert gegenbauer(-0.5, -0.5, 3).ae(-2.6383553159023906245) + assert gegenbauer(2+3j, 1-j, 3+4j).ae(14.880536623203696780 + 20.022029711598032898j) + #assert gegenbauer(-2, -0.5, 3).ae(-12) + +def test_legenp(): + mp.dps = 15 + assert legenp(2,0,4) == legendre(2,4) + assert legenp(-2, -1, 0.5).ae(0.43301270189221932338) + assert legenp(-2, -1, 0.5, type=3).ae(0.43301270189221932338j) + assert legenp(-2, 1, 0.5).ae(-0.86602540378443864676) + assert legenp(2+j, 3+4j, -j).ae(134742.98773236786148 + 429782.72924463851745j) + assert legenp(2+j, 3+4j, -j, type=3).ae(802.59463394152268507 - 251.62481308942906447j) + assert legenp(2,4,3).ae(0) + assert legenp(2,4,3,type=3).ae(0) + assert legenp(2,1,0.5).ae(-1.2990381056766579701) + assert legenp(2,1,0.5,type=3).ae(1.2990381056766579701j) + assert legenp(3,2,3).ae(-360) + assert legenp(3,3,3).ae(240j*2**0.5) + assert legenp(3,4,3).ae(0) + assert legenp(0,0.5,2).ae(0.52503756790433198939 - 0.52503756790433198939j) + assert legenp(-1,-0.5,2).ae(0.60626116232846498110 + 0.60626116232846498110j) + assert legenp(-2,0.5,2).ae(1.5751127037129959682 - 1.5751127037129959682j) + assert legenp(-2,0.5,-0.5).ae(-0.85738275810499171286) + +def test_legenq(): + mp.dps = 15 + f = legenq + # Evaluation at poles + assert isnan(f(3,2,1)) + assert isnan(f(3,2,-1)) + assert isnan(f(3,2,1,type=3)) + assert isnan(f(3,2,-1,type=3)) + # Evaluation at 0 + assert f(0,1,0,type=2).ae(-1) + assert f(-2,2,0,type=2,zeroprec=200).ae(0) + assert f(1.5,3,0,type=2).ae(-2.2239343475841951023) + assert f(0,1,0,type=3).ae(j) + assert f(-2,2,0,type=3,zeroprec=200).ae(0) + assert f(1.5,3,0,type=3).ae(2.2239343475841951022*(1-1j)) + # Standard case, degree 0 + assert f(0,0,-1.5).ae(-0.8047189562170501873 + 1.5707963267948966192j) + assert f(0,0,-0.5).ae(-0.54930614433405484570) + assert f(0,0,0,zeroprec=200).ae(0) + assert f(0,0,0.5).ae(0.54930614433405484570) + assert f(0,0,1.5).ae(0.8047189562170501873 - 1.5707963267948966192j) + assert f(0,0,-1.5,type=3).ae(-0.80471895621705018730) + assert f(0,0,-0.5,type=3).ae(-0.5493061443340548457 - 1.5707963267948966192j) + assert f(0,0,0,type=3).ae(-1.5707963267948966192j) + assert f(0,0,0.5,type=3).ae(0.5493061443340548457 - 1.5707963267948966192j) + assert f(0,0,1.5,type=3).ae(0.80471895621705018730) + # Standard case, degree 1 + assert f(1,0,-1.5).ae(0.2070784343255752810 - 2.3561944901923449288j) + assert f(1,0,-0.5).ae(-0.72534692783297257715) + assert f(1,0,0).ae(-1) + assert f(1,0,0.5).ae(-0.72534692783297257715) + assert f(1,0,1.5).ae(0.2070784343255752810 - 2.3561944901923449288j) + # Standard case, degree 2 + assert f(2,0,-1.5).ae(-0.0635669991240192885 + 4.5160394395353277803j) + assert f(2,0,-0.5).ae(0.81866326804175685571) + assert f(2,0,0,zeroprec=200).ae(0) + assert f(2,0,0.5).ae(-0.81866326804175685571) + assert f(2,0,1.5).ae(0.0635669991240192885 - 4.5160394395353277803j) + # Misc orders and degrees + assert f(2,3,1.5,type=2).ae(-5.7243340223994616228j) + assert f(2,3,1.5,type=3).ae(-5.7243340223994616228) + assert f(2,3,0.5,type=2).ae(-12.316805742712016310) + assert f(2,3,0.5,type=3).ae(-12.316805742712016310j) + assert f(2,3,-1.5,type=2).ae(-5.7243340223994616228j) + assert f(2,3,-1.5,type=3).ae(5.7243340223994616228) + assert f(2,3,-0.5,type=2).ae(-12.316805742712016310) + assert f(2,3,-0.5,type=3).ae(-12.316805742712016310j) + assert f(2+3j, 3+4j, 0.5, type=3).ae(0.0016119404873235186807 - 0.0005885900510718119836j) + assert f(2+3j, 3+4j, -1.5, type=3).ae(0.008451400254138808670 + 0.020645193304593235298j) + assert f(-2.5,1,-1.5).ae(3.9553395527435335749j) + assert f(-2.5,1,-0.5).ae(1.9290561746445456908) + assert f(-2.5,1,0).ae(1.2708196271909686299) + assert f(-2.5,1,0.5).ae(-0.31584812990742202869) + assert f(-2.5,1,1.5).ae(-3.9553395527435335742 + 0.2993235655044701706j) + assert f(-2.5,1,-1.5,type=3).ae(0.29932356550447017254j) + assert f(-2.5,1,-0.5,type=3).ae(-0.3158481299074220287 - 1.9290561746445456908j) + assert f(-2.5,1,0,type=3).ae(1.2708196271909686292 - 1.2708196271909686299j) + assert f(-2.5,1,0.5,type=3).ae(1.9290561746445456907 + 0.3158481299074220287j) + assert f(-2.5,1,1.5,type=3).ae(-0.29932356550447017254) + +def test_agm(): + mp.dps = 15 + assert agm(0,0) == 0 + assert agm(0,1) == 0 + assert agm(1,1) == 1 + assert agm(7,7) == 7 + assert agm(j,j) == j + assert (1/agm(1,sqrt(2))).ae(0.834626841674073186) + assert agm(1,2).ae(1.4567910310469068692) + assert agm(1,3).ae(1.8636167832448965424) + assert agm(1,j).ae(0.599070117367796104+0.599070117367796104j) + assert agm(2) == agm(1,2) + assert agm(-3,4).ae(0.63468509766550907+1.3443087080896272j) + +def test_gammainc(): + mp.dps = 15 + assert gammainc(2,5).ae(6*exp(-5)) + assert gammainc(2,0,5).ae(1-6*exp(-5)) + assert gammainc(2,3,5).ae(-6*exp(-5)+4*exp(-3)) + assert gammainc(-2.5,-0.5).ae(-0.9453087204829418812-5.3164237738936178621j) + assert gammainc(0,2,4).ae(0.045121158298212213088) + assert gammainc(0,3).ae(0.013048381094197037413) + assert gammainc(0,2+j,1-j).ae(0.00910653685850304839-0.22378752918074432574j) + assert gammainc(0,1-j).ae(0.00028162445198141833+0.17932453503935894015j) + assert gammainc(3,4,5,True).ae(0.11345128607046320253) + assert gammainc(3.5,0,inf).ae(gamma(3.5)) + assert gammainc(-150.5,500).ae('6.9825435345798951153e-627') + assert gammainc(-150.5,800).ae('4.6885137549474089431e-788') + assert gammainc(-3.5, -20.5).ae(0.27008820585226911 - 1310.31447140574997636j) + assert gammainc(-3.5, -200.5).ae(0.27008820585226911 - 5.3264597096208368435e76j) # XXX real part + assert gammainc(0,0,2) == inf + assert gammainc(1,b=1).ae(0.6321205588285576784) + assert gammainc(3,2,2) == 0 + assert gammainc(2,3+j,3-j).ae(-0.28135485191849314194j) + # Regularized upper gamma + assert isnan(gammainc(0, 0, regularized=True)) + assert gammainc(-1, 0, regularized=True) == inf + assert gammainc(1, 0, regularized=True) == 1 + assert gammainc(0, 5, regularized=True) == 0 + assert gammainc(0, 2+3j, regularized=True) == 0 + assert gammainc(0, 5000, regularized=True) == 0 + assert gammainc(0, 10**30, regularized=True) == 0 + assert gammainc(-1, 5, regularized=True) == 0 + assert gammainc(-1, 5000, regularized=True) == 0 + assert gammainc(-1, 10**30, regularized=True) == 0 + assert gammainc(-1, -5, regularized=True) == 0 + assert gammainc(-1, -5000, regularized=True) == 0 + assert gammainc(-1, -10**30, regularized=True) == 0 + assert gammainc(-1, 3+4j, regularized=True) == 0 + assert gammainc(1, 5, regularized=True).ae(exp(-5)) + assert gammainc(1, 5000, regularized=True).ae(exp(-5000)) + assert gammainc(1, 10**30, regularized=True).ae(exp(-10**30)) + assert gammainc(1, 3+4j, regularized=True).ae(exp(-3-4j)) + assert gammainc(-1000000,2).ae('1.3669297209397347754e-301037', abs_eps=0, rel_eps=8*eps) + assert gammainc(-1000000,2,regularized=True) == 0 + assert gammainc(-1000000,3+4j).ae('-1.322575609404222361e-698979 - 4.9274570591854533273e-698978j', abs_eps=0, rel_eps=8*eps) + assert gammainc(-1000000,3+4j,regularized=True) == 0 + assert gammainc(2+3j, 4+5j, regularized=True).ae(0.085422013530993285774-0.052595379150390078503j) + assert gammainc(1000j, 1000j, regularized=True).ae(0.49702647628921131761 + 0.00297355675013575341j) + # Generalized + assert gammainc(3,4,2) == -gammainc(3,2,4) + assert gammainc(4, 2, 3).ae(1.2593494302978947396) + assert gammainc(4, 2, 3, regularized=True).ae(0.20989157171631578993) + assert gammainc(0, 2, 3).ae(0.035852129613864082155) + assert gammainc(0, 2, 3, regularized=True) == 0 + assert gammainc(-1, 2, 3).ae(0.015219822548487616132) + assert gammainc(-1, 2, 3, regularized=True) == 0 + assert gammainc(0, 2, 3).ae(0.035852129613864082155) + assert gammainc(0, 2, 3, regularized=True) == 0 + # Should use upper gammas + assert gammainc(5, 10000, 12000).ae('1.1359381951461801687e-4327', abs_eps=0, rel_eps=8*eps) + # Should use lower gammas + assert gammainc(10000, 2, 3).ae('8.1244514125995785934e4765') + +def test_gammainc_expint_n(): + # These tests are intended to check all cases of the low-level code + # for upper gamma and expint with small integer index. + # Need to cover positive/negative arguments; small/large/huge arguments + # for both positive and negative indices, as well as indices 0 and 1 + # which may be special-cased + mp.dps = 15 + assert expint(-3,3.5).ae(0.021456366563296693987) + assert expint(-2,3.5).ae(0.014966633183073309405) + assert expint(-1,3.5).ae(0.011092916359219041088) + assert expint(0,3.5).ae(0.0086278238349481430685) + assert expint(1,3.5).ae(0.0069701398575483929193) + assert expint(2,3.5).ae(0.0058018939208991255223) + assert expint(3,3.5).ae(0.0049453773495857807058) + assert expint(-3,-3.5).ae(-4.6618170604073311319) + assert expint(-2,-3.5).ae(-5.5996974157555515963) + assert expint(-1,-3.5).ae(-6.7582555017739415818) + assert expint(0,-3.5).ae(-9.4615577024835182145) + assert expint(1,-3.5).ae(-13.925353995152335292 - 3.1415926535897932385j) + assert expint(2,-3.5).ae(-15.62328702434085977 - 10.995574287564276335j) + assert expint(3,-3.5).ae(-10.783026313250347722 - 19.242255003237483586j) + assert expint(-3,350).ae(2.8614825451252838069e-155, abs_eps=0, rel_eps=8*eps) + assert expint(-2,350).ae(2.8532837224504675901e-155, abs_eps=0, rel_eps=8*eps) + assert expint(-1,350).ae(2.8451316155828634555e-155, abs_eps=0, rel_eps=8*eps) + assert expint(0,350).ae(2.8370258275042797989e-155, abs_eps=0, rel_eps=8*eps) + assert expint(1,350).ae(2.8289659656701459404e-155, abs_eps=0, rel_eps=8*eps) + assert expint(2,350).ae(2.8209516419468505006e-155, abs_eps=0, rel_eps=8*eps) + assert expint(3,350).ae(2.8129824725501272171e-155, abs_eps=0, rel_eps=8*eps) + assert expint(-3,-350).ae(-2.8528796154044839443e+149) + assert expint(-2,-350).ae(-2.8610072121701264351e+149) + assert expint(-1,-350).ae(-2.8691813842677537647e+149) + assert expint(0,-350).ae(-2.8774025343659421709e+149) + u = expint(1,-350) + assert u.ae(-2.8856710698020863568e+149) + assert u.imag.ae(-3.1415926535897932385) + u = expint(2,-350) + assert u.ae(-2.8939874026504650534e+149) + assert u.imag.ae(-1099.5574287564276335) + u = expint(3,-350) + assert u.ae(-2.9023519497915044349e+149) + assert u.imag.ae(-192422.55003237483586) + assert expint(-3,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(-2,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(-1,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(0,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(1,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(2,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(3,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert expint(-3,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') + assert expint(-2,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') + assert expint(-1,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') + assert expint(0,-350000000000000000000000).ae('-3.7805306852415755699e+152003068666138139677871') + u = expint(1,-350000000000000000000000) + assert u.ae('-3.7805306852415755699e+152003068666138139677871') + assert u.imag.ae(-3.1415926535897932385) + u = expint(2,-350000000000000000000000) + assert u.imag.ae(-1.0995574287564276335e+24) + assert u.ae('-3.7805306852415755699e+152003068666138139677871') + u = expint(3,-350000000000000000000000) + assert u.imag.ae(-1.9242255003237483586e+47) + assert u.ae('-3.7805306852415755699e+152003068666138139677871') + # Small case; no branch cut + assert gammainc(-3,3.5).ae(0.00010020262545203707109) + assert gammainc(-2,3.5).ae(0.00040370427343557393517) + assert gammainc(-1,3.5).ae(0.0016576839773997501492) + assert gammainc(0,3.5).ae(0.0069701398575483929193) + assert gammainc(1,3.5).ae(0.03019738342231850074) + assert gammainc(2,3.5).ae(0.13588822540043325333) + assert gammainc(3,3.5).ae(0.64169439772426814072) + # Small case; with branch cut + assert gammainc(-3,-3.5).ae(0.03595832954467563286 - 0.52359877559829887308j) + assert gammainc(-2,-3.5).ae(-0.88024704597962022221 - 1.5707963267948966192j) + assert gammainc(-1,-3.5).ae(4.4637962926688170771 - 3.1415926535897932385j) + assert gammainc(0,-3.5).ae(-13.925353995152335292 - 3.1415926535897932385j) + assert gammainc(1,-3.5).ae(33.115451958692313751) + assert gammainc(2,-3.5).ae(-82.788629896730784377) + assert gammainc(3,-3.5).ae(240.08702670051927469) + # Asymptotic case; no branch cut + assert gammainc(-3,350).ae(6.5424095113340358813e-163, abs_eps=0, rel_eps=8*eps) + assert gammainc(-2,350).ae(2.296312222489899769e-160, abs_eps=0, rel_eps=8*eps) + assert gammainc(-1,350).ae(8.059861834133858573e-158, abs_eps=0, rel_eps=8*eps) + assert gammainc(0,350).ae(2.8289659656701459404e-155, abs_eps=0, rel_eps=8*eps) + assert gammainc(1,350).ae(9.9295903962649792963e-153, abs_eps=0, rel_eps=8*eps) + assert gammainc(2,350).ae(3.485286229089007733e-150, abs_eps=0, rel_eps=8*eps) + assert gammainc(3,350).ae(1.2233453960006379793e-147, abs_eps=0, rel_eps=8*eps) + # Asymptotic case; branch cut + u = gammainc(-3,-350) + assert u.ae(6.7889565783842895085e+141) + assert u.imag.ae(-0.52359877559829887308) + u = gammainc(-2,-350) + assert u.ae(-2.3692668977889832121e+144) + assert u.imag.ae(-1.5707963267948966192) + u = gammainc(-1,-350) + assert u.ae(8.2685354361441858669e+146) + assert u.imag.ae(-3.1415926535897932385) + u = gammainc(0,-350) + assert u.ae(-2.8856710698020863568e+149) + assert u.imag.ae(-3.1415926535897932385) + u = gammainc(1,-350) + assert u.ae(1.0070908870280797598e+152) + assert u.imag == 0 + u = gammainc(2,-350) + assert u.ae(-3.5147471957279983618e+154) + assert u.imag == 0 + u = gammainc(3,-350) + assert u.ae(1.2266568422179417091e+157) + assert u.imag == 0 + # Extreme asymptotic case + assert gammainc(-3,350000000000000000000000).ae('5.0362468738874738859e-152003068666138139677990', abs_eps=0, rel_eps=8*eps) + assert gammainc(-2,350000000000000000000000).ae('1.7626864058606158601e-152003068666138139677966', abs_eps=0, rel_eps=8*eps) + assert gammainc(-1,350000000000000000000000).ae('6.1694024205121555102e-152003068666138139677943', abs_eps=0, rel_eps=8*eps) + assert gammainc(0,350000000000000000000000).ae('2.1592908471792544286e-152003068666138139677919', abs_eps=0, rel_eps=8*eps) + assert gammainc(1,350000000000000000000000).ae('7.5575179651273905e-152003068666138139677896', abs_eps=0, rel_eps=8*eps) + assert gammainc(2,350000000000000000000000).ae('2.645131287794586675e-152003068666138139677872', abs_eps=0, rel_eps=8*eps) + assert gammainc(3,350000000000000000000000).ae('9.2579595072810533625e-152003068666138139677849', abs_eps=0, rel_eps=8*eps) + u = gammainc(-3,-350000000000000000000000) + assert u.ae('8.8175642804468234866e+152003068666138139677800') + assert u.imag.ae(-0.52359877559829887308) + u = gammainc(-2,-350000000000000000000000) + assert u.ae('-3.0861474981563882203e+152003068666138139677824') + assert u.imag.ae(-1.5707963267948966192) + u = gammainc(-1,-350000000000000000000000) + assert u.ae('1.0801516243547358771e+152003068666138139677848') + assert u.imag.ae(-3.1415926535897932385) + u = gammainc(0,-350000000000000000000000) + assert u.ae('-3.7805306852415755699e+152003068666138139677871') + assert u.imag.ae(-3.1415926535897932385) + assert gammainc(1,-350000000000000000000000).ae('1.3231857398345514495e+152003068666138139677895') + assert gammainc(2,-350000000000000000000000).ae('-4.6311500894209300731e+152003068666138139677918') + assert gammainc(3,-350000000000000000000000).ae('1.6209025312973255256e+152003068666138139677942') + +def test_incomplete_beta(): + mp.dps = 15 + assert betainc(-2,-3,0.5,0.75).ae(63.4305673311255413583969) + assert betainc(4.5,0.5+2j,2.5,6).ae(0.2628801146130621387903065 + 0.5162565234467020592855378j) + assert betainc(4,5,0,6).ae(90747.77142857142857142857) + +def test_erf(): + mp.dps = 15 + assert erf(0) == 0 + assert erf(1).ae(0.84270079294971486934) + assert erf(3+4j).ae(-120.186991395079444098 - 27.750337293623902498j) + assert erf(-4-3j).ae(-0.99991066178539168236 + 0.00004972026054496604j) + assert erf(pi).ae(0.99999112385363235839) + assert erf(1j).ae(1.6504257587975428760j) + assert erf(-1j).ae(-1.6504257587975428760j) + assert isinstance(erf(1), mpf) + assert isinstance(erf(-1), mpf) + assert isinstance(erf(0), mpf) + assert isinstance(erf(0j), mpc) + assert erf(inf) == 1 + assert erf(-inf) == -1 + assert erfi(0) == 0 + assert erfi(1/pi).ae(0.371682698493894314) + assert erfi(inf) == inf + assert erfi(-inf) == -inf + assert erf(1+0j) == erf(1) + assert erfc(1+0j) == erfc(1) + assert erf(0.2+0.5j).ae(1 - erfc(0.2+0.5j)) + assert erfc(0) == 1 + assert erfc(1).ae(1-erf(1)) + assert erfc(-1).ae(1-erf(-1)) + assert erfc(1/pi).ae(1-erf(1/pi)) + assert erfc(-10) == 2 + assert erfc(-1000000) == 2 + assert erfc(-inf) == 2 + assert erfc(inf) == 0 + assert isnan(erfc(nan)) + assert (erfc(10**4)*mpf(10)**43429453).ae('3.63998738656420') + mp.dps = 50 + # This one does not use the asymptotic series + assert (erfc(10)*10**45).ae('2.0884875837625447570007862949577886115608181193212') + # This one does + assert (erfc(50)*10**1088).ae('2.0709207788416560484484478751657887929322509209954') + mp.dps = 15 + assert str(erfc(10**50)) == '3.66744826532555e-4342944819032518276511289189166050822943970058036665661144537831658646492088707747292249493384317534' + assert erfinv(0) == 0 + assert erfinv(0.5).ae(0.47693627620446987338) + assert erfinv(-0.5).ae(-0.47693627620446987338) + assert erfinv(1) == inf + assert erfinv(-1) == -inf + assert erf(erfinv(0.95)).ae(0.95) + assert erf(erfinv(0.999999999995)).ae(0.999999999995) + assert erf(erfinv(-0.999999999995)).ae(-0.999999999995) + mp.dps = 50 + assert erf(erfinv('0.99999999999999999999999999999995')).ae('0.99999999999999999999999999999995') + assert erf(erfinv('0.999999999999999999999999999999995')).ae('0.999999999999999999999999999999995') + assert erf(erfinv('-0.999999999999999999999999999999995')).ae('-0.999999999999999999999999999999995') + mp.dps = 15 + # Complex asymptotic expansions + v = erfc(50j) + assert v.real == 1 + assert v.imag.ae('-6.1481820666053078736e+1083') + assert erfc(-100+5j).ae(2) + assert (erfc(100+5j)*10**4335).ae(2.3973567853824133572 - 3.9339259530609420597j) + assert erfc(100+100j).ae(0.00065234366376857698698 - 0.0039357263629214118437j) + +def test_pdf(): + mp.dps = 15 + assert npdf(-inf) == 0 + assert npdf(inf) == 0 + assert npdf(5,0,2).ae(npdf(5+4,4,2)) + assert quadts(lambda x: npdf(x,-0.5,0.8), [-inf, inf]) == 1 + assert ncdf(0) == 0.5 + assert ncdf(3,3) == 0.5 + assert ncdf(-inf) == 0 + assert ncdf(inf) == 1 + assert ncdf(10) == 1 + # Verify that this is computed accurately + assert (ncdf(-10)*10**24).ae(7.619853024160526) + +def test_lambertw(): + mp.dps = 15 + assert lambertw(0) == 0 + assert lambertw(0+0j) == 0 + assert lambertw(inf) == inf + assert isnan(lambertw(nan)) + assert lambertw(inf,1).real == inf + assert lambertw(inf,1).imag.ae(2*pi) + assert lambertw(-inf,1).real == inf + assert lambertw(-inf,1).imag.ae(3*pi) + assert lambertw(0,-1) == -inf + assert lambertw(0,1) == -inf + assert lambertw(0,3) == -inf + assert lambertw(e).ae(1) + assert lambertw(1).ae(0.567143290409783873) + assert lambertw(-pi/2).ae(j*pi/2) + assert lambertw(-log(2)/2).ae(-log(2)) + assert lambertw(0.25).ae(0.203888354702240164) + assert lambertw(-0.25).ae(-0.357402956181388903) + assert lambertw(-1./10000,0).ae(-0.000100010001500266719) + assert lambertw(-0.25,-1).ae(-2.15329236411034965) + assert lambertw(0.25,-1).ae(-3.00899800997004620-4.07652978899159763j) + assert lambertw(-0.25,-1).ae(-2.15329236411034965) + assert lambertw(0.25,1).ae(-3.00899800997004620+4.07652978899159763j) + assert lambertw(-0.25,1).ae(-3.48973228422959210+7.41405453009603664j) + assert lambertw(-4).ae(0.67881197132094523+1.91195078174339937j) + assert lambertw(-4,1).ae(-0.66743107129800988+7.76827456802783084j) + assert lambertw(-4,-1).ae(0.67881197132094523-1.91195078174339937j) + assert lambertw(1000).ae(5.24960285240159623) + assert lambertw(1000,1).ae(4.91492239981054535+5.44652615979447070j) + assert lambertw(1000,-1).ae(4.91492239981054535-5.44652615979447070j) + assert lambertw(1000,5).ae(3.5010625305312892+29.9614548941181328j) + assert lambertw(3+4j).ae(1.281561806123775878+0.533095222020971071j) + assert lambertw(-0.4+0.4j).ae(-0.10396515323290657+0.61899273315171632j) + assert lambertw(3+4j,1).ae(-0.11691092896595324+5.61888039871282334j) + assert lambertw(3+4j,-1).ae(0.25856740686699742-3.85211668616143559j) + assert lambertw(-0.5,-1).ae(-0.794023632344689368-0.770111750510379110j) + assert lambertw(-1./10000,1).ae(-11.82350837248724344+6.80546081842002101j) + assert lambertw(-1./10000,-1).ae(-11.6671145325663544) + assert lambertw(-1./10000,-2).ae(-11.82350837248724344-6.80546081842002101j) + assert lambertw(-1./100000,4).ae(-14.9186890769540539+26.1856750178782046j) + assert lambertw(-1./100000,5).ae(-15.0931437726379218666+32.5525721210262290086j) + assert lambertw((2+j)/10).ae(0.173704503762911669+0.071781336752835511j) + assert lambertw((2+j)/10,1).ae(-3.21746028349820063+4.56175438896292539j) + assert lambertw((2+j)/10,-1).ae(-3.03781405002993088-3.53946629633505737j) + assert lambertw((2+j)/10,4).ae(-4.6878509692773249+23.8313630697683291j) + assert lambertw(-(2+j)/10).ae(-0.226933772515757933-0.164986470020154580j) + assert lambertw(-(2+j)/10,1).ae(-2.43569517046110001+0.76974067544756289j) + assert lambertw(-(2+j)/10,-1).ae(-3.54858738151989450-6.91627921869943589j) + assert lambertw(-(2+j)/10,4).ae(-4.5500846928118151+20.6672982215434637j) + mp.dps = 50 + assert lambertw(pi).ae('1.073658194796149172092178407024821347547745350410314531') + mp.dps = 15 + # Former bug in generated branch + assert lambertw(-0.5+0.002j).ae(-0.78917138132659918344 + 0.76743539379990327749j) + assert lambertw(-0.5-0.002j).ae(-0.78917138132659918344 - 0.76743539379990327749j) + assert lambertw(-0.448+0.4j).ae(-0.11855133765652382241 + 0.66570534313583423116j) + assert lambertw(-0.448-0.4j).ae(-0.11855133765652382241 - 0.66570534313583423116j) + +def test_meijerg(): + mp.dps = 15 + assert meijerg([[2,3],[1]],[[0.5,2],[3,4]], 2.5).ae(4.2181028074787439386) + assert meijerg([[],[1+j]],[[1],[1]], 3+4j).ae(271.46290321152464592 - 703.03330399954820169j) + assert meijerg([[0.25],[1]],[[0.5],[2]],0) == 0 + assert meijerg([[0],[]],[[0,0,'1/3','2/3'], []], '2/27').ae(2.2019391389653314120) + # Verify 1/z series being used + assert meijerg([[-3],[-0.5]], [[-1],[-2.5]], -0.5).ae(-1.338096165935754898687431) + assert meijerg([[1-(-1)],[1-(-2.5)]], [[1-(-3)],[1-(-0.5)]], -2.0).ae(-1.338096165935754898687431) + assert meijerg([[-3],[-0.5]], [[-1],[-2.5]], -1).ae(-(pi+4)/(4*pi)) + a = 2.5 + b = 1.25 + for z in [mpf(0.25), mpf(2)]: + x1 = hyp1f1(a,b,z) + x2 = gamma(b)/gamma(a)*meijerg([[1-a],[]],[[0],[1-b]],-z) + x3 = gamma(b)/gamma(a)*meijerg([[1-0],[1-(1-b)]],[[1-(1-a)],[]],-1/z) + assert x1.ae(x2) + assert x1.ae(x3) + +def test_appellf1(): + mp.dps = 15 + assert appellf1(2,-2,1,1,2,3).ae(-1.75) + assert appellf1(2,1,-2,1,2,3).ae(-8) + assert appellf1(2,1,-2,1,0.5,0.25).ae(1.5) + assert appellf1(-2,1,3,2,3,3).ae(19) + assert appellf1(1,2,3,4,0.5,0.125).ae( 1.53843285792549786518) + +def test_coulomb(): + # Note: most tests are doctests + # Test for a bug: + mp.dps = 15 + assert coulombg(mpc(-5,0),2,3).ae(20.087729487721430394) + +def test_hyper_param_accuracy(): + mp.dps = 15 + As = [n+1e-10 for n in range(-5,-1)] + Bs = [n+1e-10 for n in range(-12,-5)] + assert hyper(As,Bs,10).ae(-381757055858.652671927) + assert legenp(0.5, 100, 0.25).ae(-2.4124576567211311755e+144) + assert (hyp1f1(1000,1,-100)*10**24).ae(5.2589445437370169113) + assert (hyp2f1(10, -900, 10.5, 0.99)*10**24).ae(1.9185370579660768203) + assert (hyp2f1(1000,1.5,-3.5,-1.5)*10**385).ae(-2.7367529051334000764) + assert hyp2f1(-5, 10, 3, 0.5, zeroprec=500) == 0 + assert (hyp1f1(-10000, 1000, 100)*10**424).ae(-3.1046080515824859974) + assert (hyp2f1(1000,1.5,-3.5,-0.75,maxterms=100000)*10**231).ae(-4.0534790813913998643) + assert legenp(2, 3, 0.25) == 0 + try: + hypercomb(lambda a: [([],[],[],[],[a],[-a],0.5)], [3]) + assert 0 + except ValueError: + pass + assert hypercomb(lambda a: [([],[],[],[],[a],[-a],0.5)], [3], infprec=200) == inf + assert meijerg([[],[]],[[0,0,0,0],[]],0.1).ae(1.5680822343832351418) + assert (besselk(400,400)*10**94).ae(1.4387057277018550583) + mp.dps = 5 + (hyp1f1(-5000.5, 1500, 100)*10**185).ae(8.5185229673381935522) + (hyp1f1(-5000, 1500, 100)*10**185).ae(9.1501213424563944311) + mp.dps = 15 + (hyp1f1(-5000.5, 1500, 100)*10**185).ae(8.5185229673381935522) + (hyp1f1(-5000, 1500, 100)*10**185).ae(9.1501213424563944311) + assert hyp0f1(fadd(-20,'1e-100',exact=True), 0.25).ae(1.85014429040102783e+49) + assert hyp0f1((-20*10**100+1, 10**100), 0.25).ae(1.85014429040102783e+49) + +def test_hypercomb_zero_pow(): + # check that 0^0 = 1 + assert hypercomb(lambda a: (([0],[a],[],[],[],[],0),), [0]) == 1 + assert meijerg([[-1.5],[]],[[0],[-0.75]],0).ae(1.4464090846320771425) + +def test_spherharm(): + mp.dps = 15 + t = 0.5; r = 0.25 + assert spherharm(0,0,t,r).ae(0.28209479177387814347) + assert spherharm(1,-1,t,r).ae(0.16048941205971996369 - 0.04097967481096344271j) + assert spherharm(1,0,t,r).ae(0.42878904414183579379) + assert spherharm(1,1,t,r).ae(-0.16048941205971996369 - 0.04097967481096344271j) + assert spherharm(2,-2,t,r).ae(0.077915886919031181734 - 0.042565643022253962264j) + assert spherharm(2,-1,t,r).ae(0.31493387233497459884 - 0.08041582001959297689j) + assert spherharm(2,0,t,r).ae(0.41330596756220761898) + assert spherharm(2,1,t,r).ae(-0.31493387233497459884 - 0.08041582001959297689j) + assert spherharm(2,2,t,r).ae(0.077915886919031181734 + 0.042565643022253962264j) + assert spherharm(3,-3,t,r).ae(0.033640236589690881646 - 0.031339125318637082197j) + assert spherharm(3,-2,t,r).ae(0.18091018743101461963 - 0.09883168583167010241j) + assert spherharm(3,-1,t,r).ae(0.42796713930907320351 - 0.10927795157064962317j) + assert spherharm(3,0,t,r).ae(0.27861659336351639787) + assert spherharm(3,1,t,r).ae(-0.42796713930907320351 - 0.10927795157064962317j) + assert spherharm(3,2,t,r).ae(0.18091018743101461963 + 0.09883168583167010241j) + assert spherharm(3,3,t,r).ae(-0.033640236589690881646 - 0.031339125318637082197j) + assert spherharm(0,-1,t,r) == 0 + assert spherharm(0,-2,t,r) == 0 + assert spherharm(0,1,t,r) == 0 + assert spherharm(0,2,t,r) == 0 + assert spherharm(1,2,t,r) == 0 + assert spherharm(1,3,t,r) == 0 + assert spherharm(1,-2,t,r) == 0 + assert spherharm(1,-3,t,r) == 0 + assert spherharm(2,3,t,r) == 0 + assert spherharm(2,4,t,r) == 0 + assert spherharm(2,-3,t,r) == 0 + assert spherharm(2,-4,t,r) == 0 + assert spherharm(3,4.5,0.5,0.25).ae(-22.831053442240790148 + 10.910526059510013757j) + assert spherharm(2+3j, 1-j, 1+j, 3+4j).ae(-2.6582752037810116935 - 1.0909214905642160211j) + assert spherharm(-6,2.5,t,r).ae(0.39383644983851448178 + 0.28414687085358299021j) + assert spherharm(-3.5, 3, 0.5, 0.25).ae(0.014516852987544698924 - 0.015582769591477628495j) + assert spherharm(-3, 3, 0.5, 0.25) == 0 + assert spherharm(-6, 3, 0.5, 0.25).ae(-0.16544349818782275459 - 0.15412657723253924562j) + assert spherharm(-6, 1.5, 0.5, 0.25).ae(0.032208193499767402477 + 0.012678000924063664921j) + assert spherharm(3,0,0,1).ae(0.74635266518023078283) + assert spherharm(3,-2,0,1) == 0 + assert spherharm(3,-2,1,1).ae(-0.16270707338254028971 - 0.35552144137546777097j) diff --git a/compiler/gdsMill/mpmath/tests/test_gammazeta.py b/compiler/gdsMill/mpmath/tests/test_gammazeta.py new file mode 100644 index 00000000..b91bbd4a --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_gammazeta.py @@ -0,0 +1,658 @@ +from mpmath import * +from mpmath.libmp import round_up, from_float, mpf_zeta_int + +def test_zeta_int_bug(): + assert mpf_zeta_int(0, 10) == from_float(-0.5) + +def test_bernoulli(): + assert bernfrac(0) == (1,1) + assert bernfrac(1) == (-1,2) + assert bernfrac(2) == (1,6) + assert bernfrac(3) == (0,1) + assert bernfrac(4) == (-1,30) + assert bernfrac(5) == (0,1) + assert bernfrac(6) == (1,42) + assert bernfrac(8) == (-1,30) + assert bernfrac(10) == (5,66) + assert bernfrac(12) == (-691,2730) + assert bernfrac(18) == (43867,798) + p, q = bernfrac(228) + assert p % 10**10 == 164918161 + assert q == 625170 + p, q = bernfrac(1000) + assert p % 10**10 == 7950421099 + assert q == 342999030 + mp.dps = 15 + assert bernoulli(0) == 1 + assert bernoulli(1) == -0.5 + assert bernoulli(2).ae(1./6) + assert bernoulli(3) == 0 + assert bernoulli(4).ae(-1./30) + assert bernoulli(5) == 0 + assert bernoulli(6).ae(1./42) + assert str(bernoulli(10)) == '0.0757575757575758' + assert str(bernoulli(234)) == '7.62772793964344e+267' + assert str(bernoulli(10**5)) == '-5.82229431461335e+376755' + assert str(bernoulli(10**8+2)) == '1.19570355039953e+676752584' + + mp.dps = 50 + assert str(bernoulli(10)) == '0.075757575757575757575757575757575757575757575757576' + assert str(bernoulli(234)) == '7.6277279396434392486994969020496121553385863373331e+267' + assert str(bernoulli(10**5)) == '-5.8222943146133508236497045360612887555320691004308e+376755' + assert str(bernoulli(10**8+2)) == '1.1957035503995297272263047884604346914602088317782e+676752584' + + mp.dps = 1000 + assert bernoulli(10).ae(mpf(5)/66) + + mp.dps = 50000 + assert bernoulli(10).ae(mpf(5)/66) + + mp.dps = 15 + +def test_bernpoly_eulerpoly(): + mp.dps = 15 + assert bernpoly(0,-1).ae(1) + assert bernpoly(0,0).ae(1) + assert bernpoly(0,'1/2').ae(1) + assert bernpoly(0,'3/4').ae(1) + assert bernpoly(0,1).ae(1) + assert bernpoly(0,2).ae(1) + assert bernpoly(1,-1).ae('-3/2') + assert bernpoly(1,0).ae('-1/2') + assert bernpoly(1,'1/2').ae(0) + assert bernpoly(1,'3/4').ae('1/4') + assert bernpoly(1,1).ae('1/2') + assert bernpoly(1,2).ae('3/2') + assert bernpoly(2,-1).ae('13/6') + assert bernpoly(2,0).ae('1/6') + assert bernpoly(2,'1/2').ae('-1/12') + assert bernpoly(2,'3/4').ae('-1/48') + assert bernpoly(2,1).ae('1/6') + assert bernpoly(2,2).ae('13/6') + assert bernpoly(3,-1).ae(-3) + assert bernpoly(3,0).ae(0) + assert bernpoly(3,'1/2').ae(0) + assert bernpoly(3,'3/4').ae('-3/64') + assert bernpoly(3,1).ae(0) + assert bernpoly(3,2).ae(3) + assert bernpoly(4,-1).ae('119/30') + assert bernpoly(4,0).ae('-1/30') + assert bernpoly(4,'1/2').ae('7/240') + assert bernpoly(4,'3/4').ae('7/3840') + assert bernpoly(4,1).ae('-1/30') + assert bernpoly(4,2).ae('119/30') + assert bernpoly(5,-1).ae(-5) + assert bernpoly(5,0).ae(0) + assert bernpoly(5,'1/2').ae(0) + assert bernpoly(5,'3/4').ae('25/1024') + assert bernpoly(5,1).ae(0) + assert bernpoly(5,2).ae(5) + assert bernpoly(10,-1).ae('665/66') + assert bernpoly(10,0).ae('5/66') + assert bernpoly(10,'1/2').ae('-2555/33792') + assert bernpoly(10,'3/4').ae('-2555/34603008') + assert bernpoly(10,1).ae('5/66') + assert bernpoly(10,2).ae('665/66') + assert bernpoly(11,-1).ae(-11) + assert bernpoly(11,0).ae(0) + assert bernpoly(11,'1/2').ae(0) + assert bernpoly(11,'3/4').ae('-555731/4194304') + assert bernpoly(11,1).ae(0) + assert bernpoly(11,2).ae(11) + assert eulerpoly(0,-1).ae(1) + assert eulerpoly(0,0).ae(1) + assert eulerpoly(0,'1/2').ae(1) + assert eulerpoly(0,'3/4').ae(1) + assert eulerpoly(0,1).ae(1) + assert eulerpoly(0,2).ae(1) + assert eulerpoly(1,-1).ae('-3/2') + assert eulerpoly(1,0).ae('-1/2') + assert eulerpoly(1,'1/2').ae(0) + assert eulerpoly(1,'3/4').ae('1/4') + assert eulerpoly(1,1).ae('1/2') + assert eulerpoly(1,2).ae('3/2') + assert eulerpoly(2,-1).ae(2) + assert eulerpoly(2,0).ae(0) + assert eulerpoly(2,'1/2').ae('-1/4') + assert eulerpoly(2,'3/4').ae('-3/16') + assert eulerpoly(2,1).ae(0) + assert eulerpoly(2,2).ae(2) + assert eulerpoly(3,-1).ae('-9/4') + assert eulerpoly(3,0).ae('1/4') + assert eulerpoly(3,'1/2').ae(0) + assert eulerpoly(3,'3/4').ae('-11/64') + assert eulerpoly(3,1).ae('-1/4') + assert eulerpoly(3,2).ae('9/4') + assert eulerpoly(4,-1).ae(2) + assert eulerpoly(4,0).ae(0) + assert eulerpoly(4,'1/2').ae('5/16') + assert eulerpoly(4,'3/4').ae('57/256') + assert eulerpoly(4,1).ae(0) + assert eulerpoly(4,2).ae(2) + assert eulerpoly(5,-1).ae('-3/2') + assert eulerpoly(5,0).ae('-1/2') + assert eulerpoly(5,'1/2').ae(0) + assert eulerpoly(5,'3/4').ae('361/1024') + assert eulerpoly(5,1).ae('1/2') + assert eulerpoly(5,2).ae('3/2') + assert eulerpoly(10,-1).ae(2) + assert eulerpoly(10,0).ae(0) + assert eulerpoly(10,'1/2').ae('-50521/1024') + assert eulerpoly(10,'3/4').ae('-36581523/1048576') + assert eulerpoly(10,1).ae(0) + assert eulerpoly(10,2).ae(2) + assert eulerpoly(11,-1).ae('-699/4') + assert eulerpoly(11,0).ae('691/4') + assert eulerpoly(11,'1/2').ae(0) + assert eulerpoly(11,'3/4').ae('-512343611/4194304') + assert eulerpoly(11,1).ae('-691/4') + assert eulerpoly(11,2).ae('699/4') + # Potential accuracy issues + assert bernpoly(10000,10000).ae('5.8196915936323387117e+39999') + assert bernpoly(200,17.5).ae(3.8048418524583064909e244) + assert eulerpoly(200,17.5).ae(-3.7309911582655785929e275) + +def test_gamma(): + mp.dps = 15 + assert gamma(0.25).ae(3.6256099082219083119) + assert gamma(0.0001).ae(9999.4228832316241908) + assert gamma(300).ae('1.0201917073881354535e612') + assert gamma(-0.5).ae(-3.5449077018110320546) + assert gamma(-7.43).ae(0.00026524416464197007186) + #assert gamma(Rational(1,2)) == gamma(0.5) + #assert gamma(Rational(-7,3)).ae(gamma(mpf(-7)/3)) + assert gamma(1+1j).ae(0.49801566811835604271 - 0.15494982830181068512j) + assert gamma(-1+0.01j).ae(-0.422733904013474115 + 99.985883082635367436j) + assert gamma(20+30j).ae(-1453876687.5534810 + 1163777777.8031573j) + # Should always give exact factorials when they can + # be represented as mpfs under the current working precision + fact = 1 + for i in range(1, 18): + assert gamma(i) == fact + fact *= i + for dps in [170, 600]: + fact = 1 + mp.dps = dps + for i in range(1, 105): + assert gamma(i) == fact + fact *= i + mp.dps = 100 + assert gamma(0.5).ae(sqrt(pi)) + mp.dps = 15 + assert factorial(0) == fac(0) == 1 + assert factorial(3) == 6 + assert isnan(gamma(nan)) + assert gamma(1100).ae('4.8579168073569433667e2866') + +def test_fac2(): + mp.dps = 15 + assert [fac2(n) for n in range(10)] == [1,1,2,3,8,15,48,105,384,945] + assert fac2(-5).ae(1./3) + assert fac2(-11).ae(-1./945) + assert fac2(50).ae(5.20469842636666623e32) + assert fac2(0.5+0.75j).ae(0.81546769394688069176-0.34901016085573266889j) + assert fac2(inf) == inf + assert isnan(fac2(-inf)) + +def test_gamma_quotients(): + mp.dps = 15 + h = 1e-8 + ep = 1e-4 + G = gamma + assert gammaprod([-1],[-3,-4]) == 0 + assert gammaprod([-1,0],[-5]) == inf + assert abs(gammaprod([-1],[-2]) - G(-1+h)/G(-2+h)) < 1e-4 + assert abs(gammaprod([-4,-3],[-2,0]) - G(-4+h)*G(-3+h)/G(-2+h)/G(0+h)) < 1e-4 + assert rf(3,0) == 1 + assert rf(2.5,1) == 2.5 + assert rf(-5,2) == 20 + assert rf(j,j).ae(gamma(2*j)/gamma(j)) + assert ff(-2,0) == 1 + assert ff(-2,1) == -2 + assert ff(4,3) == 24 + assert ff(3,4) == 0 + assert binomial(0,0) == 1 + assert binomial(1,0) == 1 + assert binomial(0,-1) == 0 + assert binomial(3,2) == 3 + assert binomial(5,2) == 10 + assert binomial(5,3) == 10 + assert binomial(5,5) == 1 + assert binomial(-1,0) == 1 + assert binomial(-2,-4) == 3 + assert binomial(4.5, 1.5) == 6.5625 + assert binomial(1100,1) == 1100 + assert binomial(1100,2) == 604450 + assert beta(1,1) == 1 + assert beta(0,0) == inf + assert beta(3,0) == inf + assert beta(-1,-1) == inf + assert beta(1.5,1).ae(2/3.) + assert beta(1.5,2.5).ae(pi/16) + assert (10**15*beta(10,100)).ae(2.3455339739604649879) + assert beta(inf,inf) == 0 + assert isnan(beta(-inf,inf)) + assert isnan(beta(-3,inf)) + assert isnan(beta(0,inf)) + assert beta(inf,0.5) == beta(0.5,inf) == 0 + assert beta(inf,-1.5) == inf + assert beta(inf,-0.5) == -inf + assert beta(1+2j,-1-j/2).ae(1.16396542451069943086+0.08511695947832914640j) + assert beta(-0.5,0.5) == 0 + assert beta(-3,3).ae(-1/3.) + +def test_zeta(): + mp.dps = 15 + assert zeta(2).ae(pi**2 / 6) + assert zeta(2.0).ae(pi**2 / 6) + assert zeta(mpc(2)).ae(pi**2 / 6) + assert zeta(100).ae(1) + assert zeta(0).ae(-0.5) + assert zeta(0.5).ae(-1.46035450880958681) + assert zeta(-1).ae(-mpf(1)/12) + assert zeta(-2) == 0 + assert zeta(-3).ae(mpf(1)/120) + assert zeta(-4) == 0 + assert zeta(-100) == 0 + assert isnan(zeta(nan)) + # Zeros in the critical strip + assert zeta(mpc(0.5, 14.1347251417346937904)).ae(0) + assert zeta(mpc(0.5, 21.0220396387715549926)).ae(0) + assert zeta(mpc(0.5, 25.0108575801456887632)).ae(0) + mp.dps = 50 + im = '236.5242296658162058024755079556629786895294952121891237' + assert zeta(mpc(0.5, im)).ae(0, 1e-46) + mp.dps = 15 + # Complex reflection formula + assert (zeta(-60+3j) / 10**34).ae(8.6270183987866146+15.337398548226238j) + +def test_altzeta(): + mp.dps = 15 + assert altzeta(-2) == 0 + assert altzeta(-4) == 0 + assert altzeta(-100) == 0 + assert altzeta(0) == 0.5 + assert altzeta(-1) == 0.25 + assert altzeta(-3) == -0.125 + assert altzeta(-5) == 0.25 + assert altzeta(-21) == 1180529130.25 + assert altzeta(1).ae(log(2)) + assert altzeta(2).ae(pi**2/12) + assert altzeta(10).ae(73*pi**10/6842880) + assert altzeta(50) < 1 + assert altzeta(60, rounding='d') < 1 + assert altzeta(60, rounding='u') == 1 + assert altzeta(10000, rounding='d') < 1 + assert altzeta(10000, rounding='u') == 1 + assert altzeta(3+0j) == altzeta(3) + s = 3+4j + assert altzeta(s).ae((1-2**(1-s))*zeta(s)) + s = -3+4j + assert altzeta(s).ae((1-2**(1-s))*zeta(s)) + assert altzeta(-100.5).ae(4.58595480083585913e+108) + assert altzeta(1.3).ae(0.73821404216623045) + +def test_zeta_huge(): + mp.dps = 15 + assert zeta(inf) == 1 + mp.dps = 50 + assert zeta(100).ae('1.0000000000000000000000000000007888609052210118073522') + assert zeta(40*pi).ae('1.0000000000000000000000000000000000000148407238666182') + mp.dps = 10000 + v = zeta(33000) + mp.dps = 15 + assert str(v-1) == '1.02363019598118e-9934' + assert zeta(pi*1000, rounding=round_up) > 1 + assert zeta(3000, rounding=round_up) > 1 + assert zeta(pi*1000) == 1 + assert zeta(3000) == 1 + +def test_zeta_negative(): + mp.dps = 150 + a = -pi*10**40 + mp.dps = 15 + assert str(zeta(a)) == '2.55880492708712e+1233536161668617575553892558646631323374078' + mp.dps = 50 + assert str(zeta(a)) == '2.5588049270871154960875033337384432038436330847333e+1233536161668617575553892558646631323374078' + mp.dps = 15 + +def test_polygamma(): + mp.dps = 15 + psi0 = lambda z: psi(0,z) + psi1 = lambda z: psi(1,z) + assert psi0(3) == psi(0,3) == digamma(3) + #assert psi2(3) == psi(2,3) == tetragamma(3) + #assert psi3(3) == psi(3,3) == pentagamma(3) + assert psi0(pi).ae(0.97721330794200673) + assert psi0(-pi).ae(7.8859523853854902) + assert psi0(-pi+1).ae(7.5676424992016996) + assert psi0(pi+j).ae(1.04224048313859376 + 0.35853686544063749j) + assert psi0(-pi-j).ae(1.3404026194821986 - 2.8824392476809402j) + assert findroot(psi0, 1).ae(1.4616321449683622) + assert psi0(inf) == inf + assert psi1(inf) == 0 + assert psi(2,inf) == 0 + assert psi1(pi).ae(0.37424376965420049) + assert psi1(-pi).ae(53.030438740085385) + assert psi1(pi+j).ae(0.32935710377142464 - 0.12222163911221135j) + assert psi1(-pi-j).ae(-0.30065008356019703 + 0.01149892486928227j) + assert (10**6*psi(4,1+10*pi*j)).ae(-6.1491803479004446 - 0.3921316371664063j) + assert psi0(1+10*pi*j).ae(3.4473994217222650 + 1.5548808324857071j) + assert isnan(psi0(nan)) + assert isnan(psi0(-inf)) + assert psi0(-100.5).ae(4.615124601338064) + assert psi0(3+0j).ae(psi0(3)) + assert psi0(-100+3j).ae(4.6106071768714086321+3.1117510556817394626j) + +def test_polygamma_high_prec(): + mp.dps = 100 + assert str(psi(0,pi)) == "0.9772133079420067332920694864061823436408346099943256380095232865318105924777141317302075654362928734" + assert str(psi(10,pi)) == "-12.98876181434889529310283769414222588307175962213707170773803550518307617769657562747174101900659238" + +def test_polygamma_identities(): + mp.dps = 15 + psi0 = lambda z: psi(0,z) + psi1 = lambda z: psi(1,z) + psi2 = lambda z: psi(2,z) + assert psi0(0.5).ae(-euler-2*log(2)) + assert psi0(1).ae(-euler) + assert psi1(0.5).ae(0.5*pi**2) + assert psi1(1).ae(pi**2/6) + assert psi1(0.25).ae(pi**2 + 8*catalan) + assert psi2(1).ae(-2*apery) + mp.dps = 20 + u = -182*apery+4*sqrt(3)*pi**3 + mp.dps = 15 + assert psi(2,5/6.).ae(u) + assert psi(3,0.5).ae(pi**4) + +def test_foxtrot_identity(): + # A test of the complex digamma function. + # See http://mathworld.wolfram.com/FoxTrotSeries.html and + # http://mathworld.wolfram.com/DigammaFunction.html + psi0 = lambda z: psi(0,z) + mp.dps = 50 + a = (-1)**fraction(1,3) + b = (-1)**fraction(2,3) + x = -psi0(0.5*a) - psi0(-0.5*b) + psi0(0.5*(1+a)) + psi0(0.5*(1-b)) + y = 2*pi*sech(0.5*sqrt(3)*pi) + assert x.ae(y) + mp.dps = 15 + +def test_polygamma_high_order(): + mp.dps = 100 + assert str(psi(50, pi)) == "-1344100348958402765749252447726432491812.641985273160531055707095989227897753035823152397679626136483" + assert str(psi(50, pi + 14*e)) == "-0.00000000000000000189793739550804321623512073101895801993019919886375952881053090844591920308111549337295143780341396" + assert str(psi(50, pi + 14*e*j)) == ("(-0.0000000000000000522516941152169248975225472155683565752375889510631513244785" + "9377385233700094871256507814151956624433 - 0.00000000000000001813157041407010184" + "702414110218205348527862196327980417757665282244728963891298080199341480881811613j)") + mp.dps = 15 + assert str(psi(50, pi)) == "-1.34410034895841e+39" + assert str(psi(50, pi + 14*e)) == "-1.89793739550804e-18" + assert str(psi(50, pi + 14*e*j)) == "(-5.2251694115217e-17 - 1.81315704140701e-17j)" + +def test_harmonic(): + mp.dps = 15 + assert harmonic(0) == 0 + assert harmonic(1) == 1 + assert harmonic(2) == 1.5 + assert harmonic(3).ae(1. + 1./2 + 1./3) + assert harmonic(10**10).ae(23.603066594891989701) + assert harmonic(10**1000).ae(2303.162308658947) + assert harmonic(0.5).ae(2-2*log(2)) + assert harmonic(inf) == inf + assert harmonic(2+0j) == 1.5+0j + assert harmonic(1+2j).ae(1.4918071802755104+0.92080728264223022j) + +def test_gamma_huge_1(): + mp.dps = 500 + x = mpf(10**10) / 7 + mp.dps = 15 + assert str(gamma(x)) == "6.26075321389519e+12458010678" + mp.dps = 50 + assert str(gamma(x)) == "6.2607532138951929201303779291707455874010420783933e+12458010678" + mp.dps = 15 + +def test_gamma_huge_2(): + mp.dps = 500 + x = mpf(10**100) / 19 + mp.dps = 15 + assert str(gamma(x)) == (\ + "1.82341134776679e+5172997469323364168990133558175077136829182824042201886051511" + "9656908623426021308685461258226190190661") + mp.dps = 50 + assert str(gamma(x)) == (\ + "1.82341134776678875374414910350027596939980412984e+5172997469323364168990133558" + "1750771368291828240422018860515119656908623426021308685461258226190190661") + +def test_gamma_huge_3(): + mp.dps = 500 + x = 10**80 // 3 + 10**70*j / 7 + mp.dps = 15 + y = gamma(x) + assert str(y.real) == (\ + "-6.82925203918106e+2636286142112569524501781477865238132302397236429627932441916" + "056964386399485392600") + assert str(y.imag) == (\ + "8.54647143678418e+26362861421125695245017814778652381323023972364296279324419160" + "56964386399485392600") + mp.dps = 50 + y = gamma(x) + assert str(y.real) == (\ + "-6.8292520391810548460682736226799637356016538421817e+26362861421125695245017814" + "77865238132302397236429627932441916056964386399485392600") + assert str(y.imag) == (\ + "8.5464714367841748507479306948130687511711420234015e+263628614211256952450178147" + "7865238132302397236429627932441916056964386399485392600") + +def test_gamma_huge_4(): + x = 3200+11500j + mp.dps = 15 + assert str(gamma(x)) == \ + "(8.95783268539713e+5164 - 1.94678798329735e+5164j)" + mp.dps = 50 + assert str(gamma(x)) == (\ + "(8.9578326853971339570292952697675570822206567327092e+5164" + " - 1.9467879832973509568895402139429643650329524144794e+51" + "64j)") + mp.dps = 15 + +def test_gamma_huge_5(): + mp.dps = 500 + x = 10**60 * j / 3 + mp.dps = 15 + y = gamma(x) + assert str(y.real) == "-3.27753899634941e-227396058973640224580963937571892628368354580620654233316839" + assert str(y.imag) == "-7.1519888950416e-227396058973640224580963937571892628368354580620654233316841" + mp.dps = 50 + y = gamma(x) + assert str(y.real) == (\ + "-3.2775389963494132168950056995974690946983219123935e-22739605897364022458096393" + "7571892628368354580620654233316839") + assert str(y.imag) == (\ + "-7.1519888950415979749736749222530209713136588885897e-22739605897364022458096393" + "7571892628368354580620654233316841") + mp.dps = 15 + +""" +XXX: fails +def test_gamma_huge_6(): + return + mp.dps = 500 + x = -10**10 + mpf(10)**(-175)*j + mp.dps = 15 + assert str(gamma(x)) == \ + "(1.86729378905343e-95657055178 - 4.29960285282433e-95657055002j)" + mp.dps = 50 + assert str(gamma(x)) == (\ + "(1.8672937890534298925763143275474177736153484820662e-9565705517" + "8 - 4.2996028528243336966001185406200082244961757496106e-9565705" + "5002j)") + mp.dps = 15 +""" + +def test_gamma_huge_7(): + mp.dps = 100 + a = 3 + j/mpf(10)**1000 + mp.dps = 15 + y = gamma(a) + assert str(y.real) == "2.0" + assert str(y.imag) == "2.16735365342606e-1000" + mp.dps = 50 + y = gamma(a) + assert str(y.real) == "2.0" + assert str(y.imag) == "2.1673536534260596065418805612488708028522563689298e-1000" + +def test_stieltjes(): + mp.dps = 15 + assert stieltjes(0).ae(+euler) + mp.dps = 25 + assert stieltjes(1).ae('-0.07281584548367672486058637587') + assert stieltjes(2).ae('-0.009690363192872318484530386035') + assert stieltjes(3).ae('0.002053834420303345866160046543') + assert stieltjes(4).ae('0.002325370065467300057468170178') + mp.dps = 15 + assert stieltjes(1).ae(-0.07281584548367672486058637587) + assert stieltjes(2).ae(-0.009690363192872318484530386035) + assert stieltjes(3).ae(0.002053834420303345866160046543) + assert stieltjes(4).ae(0.0023253700654673000574681701775) + +def test_barnesg(): + mp.dps = 15 + assert barnesg(0) == barnesg(-1) == 0 + assert [superfac(i) for i in range(8)] == [1, 1, 2, 12, 288, 34560, 24883200, 125411328000] + assert str(superfac(1000)) == '3.24570818422368e+1177245' + assert isnan(barnesg(nan)) + assert isnan(superfac(nan)) + assert isnan(hyperfac(nan)) + assert barnesg(inf) == inf + assert superfac(inf) == inf + assert hyperfac(inf) == inf + assert isnan(superfac(-inf)) + assert barnesg(0.7).ae(0.8068722730141471) + assert barnesg(2+3j).ae(-0.17810213864082169+0.04504542715447838j) + assert [hyperfac(n) for n in range(7)] == [1, 1, 4, 108, 27648, 86400000, 4031078400000] + assert [hyperfac(n) for n in range(0,-7,-1)] == [1,1,-1,-4,108,27648,-86400000] + a = barnesg(-3+0j) + assert a == 0 and isinstance(a, mpc) + a = hyperfac(-3+0j) + assert a == -4 and isinstance(a, mpc) + +def test_polylog(): + mp.dps = 15 + zs = [mpmathify(z) for z in [0, 0.5, 0.99, 4, -0.5, -4, 1j, 3+4j]] + for z in zs: assert polylog(1, z).ae(-log(1-z)) + for z in zs: assert polylog(0, z).ae(z/(1-z)) + for z in zs: assert polylog(-1, z).ae(z/(1-z)**2) + for z in zs: assert polylog(-2, z).ae(z*(1+z)/(1-z)**3) + for z in zs: assert polylog(-3, z).ae(z*(1+4*z+z**2)/(1-z)**4) + assert polylog(3, 7).ae(5.3192579921456754382-5.9479244480803301023j) + assert polylog(3, -7).ae(-4.5693548977219423182) + assert polylog(2, 0.9).ae(1.2997147230049587252) + assert polylog(2, -0.9).ae(-0.75216317921726162037) + assert polylog(2, 0.9j).ae(-0.17177943786580149299+0.83598828572550503226j) + assert polylog(2, 1.1).ae(1.9619991013055685931-0.2994257606855892575j) + assert polylog(2, -1.1).ae(-0.89083809026228260587) + assert polylog(2, 1.1*sqrt(j)).ae(0.58841571107611387722+1.09962542118827026011j) + assert polylog(-2, 0.9).ae(1710) + assert polylog(-2, -0.9).ae(-90/6859.) + assert polylog(3, 0.9).ae(1.0496589501864398696) + assert polylog(-3, 0.9).ae(48690) + assert polylog(-3, -4).ae(-0.0064) + assert polylog(0.5+j/3, 0.5+j/2).ae(0.31739144796565650535 + 0.99255390416556261437j) + assert polylog(3+4j,1).ae(zeta(3+4j)) + assert polylog(3+4j,-1).ae(-altzeta(3+4j)) + +def test_bell_polyexp(): + mp.dps = 15 + # TODO: more tests for polyexp + assert (polyexp(0,1e-10)*10**10).ae(1.00000000005) + assert (polyexp(1,1e-10)*10**10).ae(1.0000000001) + assert polyexp(5,3j).ae(-607.7044517476176454+519.962786482001476087j) + assert polyexp(-1,3.5).ae(12.09537536175543444) + # bell(0,x) = 1 + assert bell(0,0) == 1 + assert bell(0,1) == 1 + assert bell(0,2) == 1 + assert bell(0,inf) == 1 + assert bell(0,-inf) == 1 + assert isnan(bell(0,nan)) + # bell(1,x) = x + assert bell(1,4) == 4 + assert bell(1,0) == 0 + assert bell(1,inf) == inf + assert bell(1,-inf) == -inf + assert isnan(bell(1,nan)) + # bell(2,x) = x*(1+x) + assert bell(2,-1) == 0 + assert bell(2,0) == 0 + # large orders / arguments + assert bell(10) == 115975 + assert bell(10,1) == 115975 + assert bell(10, -8) == 11054008 + assert bell(5,-50) == -253087550 + assert bell(50,-50).ae('3.4746902914629720259e74') + mp.dps = 80 + assert bell(50,-50) == 347469029146297202586097646631767227177164818163463279814268368579055777450 + assert bell(40,50) == 5575520134721105844739265207408344706846955281965031698187656176321717550 + assert bell(74) == 5006908024247925379707076470957722220463116781409659160159536981161298714301202 + mp.dps = 15 + assert bell(10,20j) == 7504528595600+15649605360020j + # continuity of the generalization + assert bell(0.5,0).ae(sinc(pi*0.5)) + +def test_primezeta(): + mp.dps = 15 + assert primezeta(0.9).ae(1.8388316154446882243 + 3.1415926535897932385j) + assert primezeta(4).ae(0.076993139764246844943) + assert primezeta(1) == inf + assert primezeta(inf) == 0 + assert isnan(primezeta(nan)) + +def test_rs_zeta(): + mp.dps = 15 + assert zeta(0.5+100000j).ae(1.0730320148577531321 + 5.7808485443635039843j) + assert zeta(0.75+100000j).ae(1.837852337251873704 + 1.9988492668661145358j) + assert zeta(0.5+1000000j, derivative=3).ae(1647.7744105852674733 - 1423.1270943036622097j) + assert zeta(1+1000000j, derivative=3).ae(3.4085866124523582894 - 18.179184721525947301j) + assert zeta(1+1000000j, derivative=1).ae(-0.10423479366985452134 - 0.74728992803359056244j) + assert zeta(0.5-1000000j, derivative=1).ae(11.636804066002521459 + 17.127254072212996004j) + # Additional sanity tests using fp arithmetic. + # Some more high-precision tests are found in the docstrings + def ae(x, y, tol=1e-6): + return abs(x-y) < tol*abs(y) + assert ae(fp.zeta(0.5-100000j), 1.0730320148577531321 - 5.7808485443635039843j) + assert ae(fp.zeta(0.75-100000j), 1.837852337251873704 - 1.9988492668661145358j) + assert ae(fp.zeta(0.5+1e6j), 0.076089069738227100006 + 2.8051021010192989554j) + assert ae(fp.zeta(0.5+1e6j, derivative=1), 11.636804066002521459 - 17.127254072212996004j) + assert ae(fp.zeta(1+1e6j), 0.94738726251047891048 + 0.59421999312091832833j) + assert ae(fp.zeta(1+1e6j, derivative=1), -0.10423479366985452134 - 0.74728992803359056244j) + assert ae(fp.zeta(0.5+100000j, derivative=1), 10.766962036817482375 - 30.92705282105996714j) + assert ae(fp.zeta(0.5+100000j, derivative=2), -119.40515625740538429 + 217.14780631141830251j) + assert ae(fp.zeta(0.5+100000j, derivative=3), 1129.7550282628460881 - 1685.4736895169690346j) + assert ae(fp.zeta(0.5+100000j, derivative=4), -10407.160819314958615 + 13777.786698628045085j) + assert ae(fp.zeta(0.75+100000j, derivative=1), -0.41742276699594321475 - 6.4453816275049955949j) + assert ae(fp.zeta(0.75+100000j, derivative=2), -9.214314279161977266 + 35.07290795337967899j) + assert ae(fp.zeta(0.75+100000j, derivative=3), 110.61331857820103469 - 236.87847130518129926j) + assert ae(fp.zeta(0.75+100000j, derivative=4), -1054.334275898559401 + 1769.9177890161596383j) + +def test_zeta_near_1(): + # Test for a former bug in mpf_zeta and mpc_zeta + mp.dps = 15 + s1 = fadd(1, '1e-10', exact=True) + s2 = fadd(1, '-1e-10', exact=True) + s3 = fadd(1, '1e-10j', exact=True) + assert zeta(s1).ae(1.000000000057721566490881444e10) + assert zeta(s2).ae(-9.99999999942278433510574872e9) + z = zeta(s3) + assert z.real.ae(0.57721566490153286060) + assert z.imag.ae(-9.9999999999999999999927184e9) + mp.dps = 30 + s1 = fadd(1, '1e-50', exact=True) + s2 = fadd(1, '-1e-50', exact=True) + s3 = fadd(1, '1e-50j', exact=True) + assert zeta(s1).ae('1e50') + assert zeta(s2).ae('-1e50') + z = zeta(s3) + assert z.real.ae('0.57721566490153286060651209008240243104215933593992') + assert z.imag.ae('-1e50') diff --git a/compiler/gdsMill/mpmath/tests/test_hp.py b/compiler/gdsMill/mpmath/tests/test_hp.py new file mode 100644 index 00000000..756608be --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_hp.py @@ -0,0 +1,292 @@ +""" +Check that the output from irrational functions is accurate for +high-precision input, from 5 to 200 digits. The reference values were +verified with Mathematica. +""" + +import time +from mpmath import * + +precs = [5, 15, 28, 35, 57, 80, 100, 150, 200] + +# sqrt(3) + pi/2 +a = \ +"3.302847134363773912758768033145623809041389953497933538543279275605"\ +"841220051904536395163599428307109666700184672047856353516867399774243594"\ +"67433521615861420725323528325327484262075464241255915238845599752675" + +# e + 1/euler**2 +b = \ +"5.719681166601007617111261398629939965860873957353320734275716220045750"\ +"31474116300529519620938123730851145473473708966080207482581266469342214"\ +"824842256999042984813905047895479210702109260221361437411947323431" + +# sqrt(a) +sqrt_a = \ +"1.817373691447021556327498239690365674922395036495564333152483422755"\ +"144321726165582817927383239308173567921345318453306994746434073691275094"\ +"484777905906961689902608644112196725896908619756404253109722911487" + +# sqrt(a+b*i).real +sqrt_abi_real = \ +"2.225720098415113027729407777066107959851146508557282707197601407276"\ +"89160998185797504198062911768240808839104987021515555650875977724230130"\ +"3584116233925658621288393930286871862273400475179312570274423840384" + +# sqrt(a+b*i).imag +sqrt_abi_imag = \ +"1.2849057639084690902371581529110949983261182430040898147672052833653668"\ +"0629534491275114877090834296831373498336559849050755848611854282001250"\ +"1924311019152914021365263161630765255610885489295778894976075186" + +# log(a) +log_a = \ +"1.194784864491089550288313512105715261520511949410072046160598707069"\ +"4336653155025770546309137440687056366757650909754708302115204338077595203"\ +"83005773986664564927027147084436553262269459110211221152925732612" + +# log(a+b*i).real +log_abi_real = \ +"1.8877985921697018111624077550443297276844736840853590212962006811663"\ +"04949387789489704203167470111267581371396245317618589339274243008242708"\ +"014251531496104028712866224020066439049377679709216784954509456421" + +# log(a+b*i).imag +log_abi_imag = \ +"1.0471204952840802663567714297078763189256357109769672185219334169734948"\ +"4265809854092437285294686651806426649541504240470168212723133326542181"\ +"8300136462287639956713914482701017346851009323172531601894918640" + +# exp(a) +exp_a = \ +"27.18994224087168661137253262213293847994194869430518354305430976149"\ +"382792035050358791398632888885200049857986258414049540376323785711941636"\ +"100358982497583832083513086941635049329804685212200507288797531143" + +# exp(a+b*i).real +exp_abi_real = \ +"22.98606617170543596386921087657586890620262522816912505151109385026"\ +"40160179326569526152851983847133513990281518417211964710397233157168852"\ +"4963130831190142571659948419307628119985383887599493378056639916701" + +# exp(a+b*i).imag +exp_abi_imag = \ +"-14.523557450291489727214750571590272774669907424478129280902375851196283"\ +"3377162379031724734050088565710975758824441845278120105728824497308303"\ +"6065619788140201636218705414429933685889542661364184694108251449" + +# a**b +pow_a_b = \ +"928.7025342285568142947391505837660251004990092821305668257284426997"\ +"361966028275685583421197860603126498884545336686124793155581311527995550"\ +"580229264427202446131740932666832138634013168125809402143796691154" + +# (a**(a+b*i)).real +pow_a_abi_real = \ +"44.09156071394489511956058111704382592976814280267142206420038656267"\ +"67707916510652790502399193109819563864568986234654864462095231138500505"\ +"8197456514795059492120303477512711977915544927440682508821426093455" + +# (a**(a+b*i)).imag +pow_a_abi_imag = \ +"27.069371511573224750478105146737852141664955461266218367212527612279886"\ +"9322304536553254659049205414427707675802193810711302947536332040474573"\ +"8166261217563960235014674118610092944307893857862518964990092301" + +# ((a+b*i)**(a+b*i)).real +pow_abi_abi_real = \ +"-0.15171310677859590091001057734676423076527145052787388589334350524"\ +"8084195882019497779202452975350579073716811284169068082670778986235179"\ +"0813026562962084477640470612184016755250592698408112493759742219150452"\ + +# ((a+b*i)**(a+b*i)).imag +pow_abi_abi_imag = \ +"1.2697592504953448936553147870155987153192995316950583150964099070426"\ +"4736837932577176947632535475040521749162383347758827307504526525647759"\ +"97547638617201824468382194146854367480471892602963428122896045019902" + +# sin(a) +sin_a = \ +"-0.16055653857469062740274792907968048154164433772938156243509084009"\ +"38437090841460493108570147191289893388608611542655654723437248152535114"\ +"528368009465836614227575701220612124204622383149391870684288862269631" + +# sin(1000*a) +sin_1000a = \ +"-0.85897040577443833776358106803777589664322997794126153477060795801"\ +"09151695416961724733492511852267067419573754315098042850381158563024337"\ +"216458577140500488715469780315833217177634490142748614625281171216863" + +# sin(a+b*i) +sin_abi_real = \ +"-24.4696999681556977743346798696005278716053366404081910969773939630"\ +"7149215135459794473448465734589287491880563183624997435193637389884206"\ +"02151395451271809790360963144464736839412254746645151672423256977064" + +sin_abi_imag = \ +"-150.42505378241784671801405965872972765595073690984080160750785565810981"\ +"8314482499135443827055399655645954830931316357243750839088113122816583"\ +"7169201254329464271121058839499197583056427233866320456505060735" + +# cos +cos_a = \ +"-0.98702664499035378399332439243967038895709261414476495730788864004"\ +"05406821549361039745258003422386169330787395654908532996287293003581554"\ +"257037193284199198069707141161341820684198547572456183525659969145501" + +cos_1000a = \ +"-0.51202523570982001856195696460663971099692261342827540426136215533"\ +"52686662667660613179619804463250686852463876088694806607652218586060613"\ +"951310588158830695735537073667299449753951774916401887657320950496820" + +# tan +tan_a = \ +"0.162666873675188117341401059858835168007137819495998960250142156848"\ +"639654718809412181543343168174807985559916643549174530459883826451064966"\ +"7996119428949951351938178809444268785629011625179962457123195557310" + +tan_abi_real = \ +"6.822696615947538488826586186310162599974827139564433912601918442911"\ +"1026830824380070400102213741875804368044342309515353631134074491271890"\ +"467615882710035471686578162073677173148647065131872116479947620E-6" + +tan_abi_imag = \ +"0.9999795833048243692245661011298447587046967777739649018690797625964167"\ +"1446419978852235960862841608081413169601038230073129482874832053357571"\ +"62702259309150715669026865777947502665936317953101462202542168429" + + +def test_hp(): + for dps in precs: + mp.dps = dps + 8 + aa = mpf(a) + bb = mpf(b) + a1000 = 1000*mpf(a) + abi = mpc(aa, bb) + mp.dps = dps + assert (sqrt(3) + pi/2).ae(aa) + assert (e + 1/euler**2).ae(bb) + + assert sqrt(aa).ae(mpf(sqrt_a)) + assert sqrt(abi).ae(mpc(sqrt_abi_real, sqrt_abi_imag)) + + assert log(aa).ae(mpf(log_a)) + assert log(abi).ae(mpc(log_abi_real, log_abi_imag)) + + assert exp(aa).ae(mpf(exp_a)) + assert exp(abi).ae(mpc(exp_abi_real, exp_abi_imag)) + + assert (aa**bb).ae(mpf(pow_a_b)) + assert (aa**abi).ae(mpc(pow_a_abi_real, pow_a_abi_imag)) + assert (abi**abi).ae(mpc(pow_abi_abi_real, pow_abi_abi_imag)) + + assert sin(a).ae(mpf(sin_a)) + assert sin(a1000).ae(mpf(sin_1000a)) + assert sin(abi).ae(mpc(sin_abi_real, sin_abi_imag)) + + assert cos(a).ae(mpf(cos_a)) + assert cos(a1000).ae(mpf(cos_1000a)) + + assert tan(a).ae(mpf(tan_a)) + assert tan(abi).ae(mpc(tan_abi_real, tan_abi_imag)) + + # check that complex cancellation is avoided so that both + # real and imaginary parts have high relative accuracy. + # abs_eps should be 0, but has to be set to 1e-205 to pass the + # 200-digit case, probably due to slight inaccuracy in the + # precomputed input + assert (tan(abi).real).ae(mpf(tan_abi_real), abs_eps=1e-205) + assert (tan(abi).imag).ae(mpf(tan_abi_imag), abs_eps=1e-205) + mp.dps = 460 + assert str(log(3))[-20:] == '02166121184001409826' + mp.dps = 15 + +# Since str(a) can differ in the last digit from rounded a, and I want +# to compare the last digits of big numbers with the results in Mathematica, +# I made this hack to get the last 20 digits of rounded a + +def last_digits(a): + r = repr(a) + s = str(a) + #dps = mp.dps + #mp.dps += 3 + m = 10 + r = r.replace(s[:-m],'') + r = r.replace("mpf('",'').replace("')",'') + num0 = 0 + for c in r: + if c == '0': + num0 += 1 + else: + break + b = float(int(r))/10**(len(r) - m) + if b >= 10**m - 0.5: + raise NotImplementedError + n = int(round(b)) + sn = str(n) + s = s[:-m] + '0'*num0 + sn + return s[-20:] + +# values checked with Mathematica +def test_log_hp(): + mp.dps = 2000 + a = mpf(10)**15000/3 + r = log(a) + res = last_digits(r) + # Mathematica N[Log[10^15000/3], 2000] + # ...7443804441768333470331 + assert res == '44380444176833347033' + + # see issue 105 + r = log(mpf(3)/2) + # Mathematica N[Log[3/2], 2000] + # ...69653749808140753263288 + res = last_digits(r) + assert res == '53749808140753263288' + + mp.dps = 10000 + r = log(2) + res = last_digits(r) + # Mathematica N[Log[2], 10000] + # ...695615913401856601359655561 + assert res == '91340185660135965556' + r = log(mpf(10)**10/3) + res = last_digits(r) + # Mathematica N[Log[10^10/3], 10000] + # ...587087654020631943060007154 + assert res == '54020631943060007154', res + r = log(mpf(10)**100/3) + res = last_digits(r) + # Mathematica N[Log[10^100/3], 10000] + # ,,,59246336539088351652334666 + assert res == '36539088351652334666', res + mp.dps += 10 + a = 1 - mpf(1)/10**10 + mp.dps -= 10 + r = log(a) + res = last_digits(r) + # ...3310334360482956137216724048322957404 + # 372167240483229574038733026370 + # Mathematica N[Log[1 - 10^-10]*10^10, 10000] + # ...60482956137216724048322957404 + assert res == '37216724048322957404', res + mp.dps = 10000 + mp.dps += 100 + a = 1 + mpf(1)/10**100 + mp.dps -= 100 + + r = log(a) + res = last_digits(+r) + # Mathematica N[Log[1 + 10^-100]*10^10, 10030] + # ...3994733877377412241546890854692521568292338268273 10^-91 + assert res == '39947338773774122415', res + + mp.dps = 15 + +def test_exp_hp(): + mp.dps = 4000 + r = exp(mpf(1)/10) + # IntegerPart[N[Exp[1/10] * 10^4000, 4000]] + # ...92167105162069688129 + assert int(r * 10**mp.dps) % 10**20 == 92167105162069688129 + diff --git a/compiler/gdsMill/mpmath/tests/test_identify.py b/compiler/gdsMill/mpmath/tests/test_identify.py new file mode 100644 index 00000000..90b7de70 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_identify.py @@ -0,0 +1,19 @@ +from mpmath import * + +def test_pslq(): + mp.dps = 15 + assert pslq([3*pi+4*e/7, pi, e, log(2)]) == [7, -21, -4, 0] + assert pslq([4.9999999999999991, 1]) == [1, -5] + assert pslq([2,1]) == [1, -2] + +def test_identify(): + mp.dps = 20 + assert identify(zeta(4), ['log(2)', 'pi**4']) == '((1/90)*pi**4)' + mp.dps = 15 + assert identify(exp(5)) == 'exp(5)' + assert identify(exp(4)) == 'exp(4)' + assert identify(log(5)) == 'log(5)' + assert identify(exp(3*pi), ['pi']) == 'exp((3*pi))' + assert identify(3, full=True) == ['3', '3', '1/(1/3)', 'sqrt(9)', + '1/sqrt((1/9))', '(sqrt(12)/2)**2', '1/(sqrt(12)/6)**2'] + assert identify(pi+1, {'a':+pi}) == '(1 + 1*a)' diff --git a/compiler/gdsMill/mpmath/tests/test_interval.py b/compiler/gdsMill/mpmath/tests/test_interval.py new file mode 100644 index 00000000..9db109e5 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_interval.py @@ -0,0 +1,264 @@ +from mpmath import * + +mpi_to_str = mp.mpi_to_str +mpi_from_str = mp.mpi_from_str + +def test_interval_identity(): + mp.dps = 15 + assert mpi(2) == mpi(2, 2) + assert mpi(2) != mpi(-2, 2) + assert not (mpi(2) != mpi(2, 2)) + assert mpi(-1, 1) == mpi(-1, 1) + assert str(mpi('0.1')) == "[0.099999999999999991673, 0.10000000000000000555]" + assert repr(mpi('0.1')) == "mpi(mpf('0.099999999999999992'), mpf('0.10000000000000001'))" + u = mpi(-1, 3) + assert -1 in u + assert 2 in u + assert 3 in u + assert -1.1 not in u + assert 3.1 not in u + assert mpi(-1, 3) in u + assert mpi(0, 1) in u + assert mpi(-1.1, 2) not in u + assert mpi(2.5, 3.1) not in u + w = mpi(-inf, inf) + assert mpi(-5, 5) in w + assert mpi(2, inf) in w + assert mpi(0, 2) in mpi(0, 10) + assert not (3 in mpi(-inf, 0)) + +def test_interval_arithmetic(): + mp.dps = 15 + assert mpi(2) + mpi(3,4) == mpi(5,6) + assert mpi(1, 2)**2 == mpi(1, 4) + assert mpi(1) + mpi(0, 1e-50) == mpi(1, mpf('1.0000000000000002')) + x = 1 / (1 / mpi(3)) + assert x.a < 3 < x.b + x = mpi(2) ** mpi(0.5) + mp.dps += 5 + sq = sqrt(2) + mp.dps -= 5 + assert x.a < sq < x.b + assert mpi(1) / mpi(1, inf) + assert mpi(2, 3) / inf == mpi(0, 0) + assert mpi(0) / inf == 0 + assert mpi(0) / 0 == mpi(-inf, inf) + assert mpi(inf) / 0 == mpi(-inf, inf) + assert mpi(0) * inf == mpi(-inf, inf) + assert 1 / mpi(2, inf) == mpi(0, 0.5) + assert str((mpi(50, 50) * mpi(-10, -10)) / 3) == \ + '[-166.66666666666668561, -166.66666666666665719]' + assert mpi(0, 4) ** 3 == mpi(0, 64) + assert mpi(2,4).mid == 3 + mp.dps = 30 + a = mpi(pi) + mp.dps = 15 + b = +a + assert b.a < a.a + assert b.b > a.b + a = mpi(pi) + assert a == +a + assert abs(mpi(-1,2)) == mpi(0,2) + assert abs(mpi(0.5,2)) == mpi(0.5,2) + assert abs(mpi(-3,2)) == mpi(0,3) + assert abs(mpi(-3,-0.5)) == mpi(0.5,3) + assert mpi(0) * mpi(2,3) == mpi(0) + assert mpi(2,3) * mpi(0) == mpi(0) + assert mpi(1,3).delta == 2 + assert mpi(1,2) - mpi(3,4) == mpi(-3,-1) + assert mpi(-inf,0) - mpi(0,inf) == mpi(-inf,0) + assert mpi(-inf,0) - mpi(-inf,inf) == mpi(-inf,inf) + assert mpi(0,inf) - mpi(-inf,1) == mpi(-1,inf) + +def test_interval_mul(): + assert mpi(-1, 0) * inf == mpi(-inf, 0) + assert mpi(-1, 0) * -inf == mpi(0, inf) + assert mpi(0, 1) * inf == mpi(0, inf) + assert mpi(0, 1) * mpi(0, inf) == mpi(0, inf) + assert mpi(-1, 1) * inf == mpi(-inf, inf) + assert mpi(-1, 1) * mpi(0, inf) == mpi(-inf, inf) + assert mpi(-1, 1) * mpi(-inf, inf) == mpi(-inf, inf) + assert mpi(-inf, 0) * mpi(0, 1) == mpi(-inf, 0) + assert mpi(-inf, 0) * mpi(0, 0) * mpi(-inf, 0) + assert mpi(-inf, 0) * mpi(-inf, inf) == mpi(-inf, inf) + assert mpi(-5,0)*mpi(-32,28) == mpi(-140,160) + assert mpi(2,3) * mpi(-1,2) == mpi(-3,6) + # Should be undefined? + assert mpi(inf, inf) * 0 == mpi(-inf, inf) + assert mpi(-inf, -inf) * 0 == mpi(-inf, inf) + assert mpi(0) * mpi(-inf,2) == mpi(-inf,inf) + assert mpi(0) * mpi(-2,inf) == mpi(-inf,inf) + assert mpi(-2,inf) * mpi(0) == mpi(-inf,inf) + assert mpi(-inf,2) * mpi(0) == mpi(-inf,inf) + +def test_interval_pow(): + assert mpi(3)**2 == mpi(9, 9) + assert mpi(-3)**2 == mpi(9, 9) + assert mpi(-3, 1)**2 == mpi(0, 9) + assert mpi(-3, -1)**2 == mpi(1, 9) + assert mpi(-3, -1)**3 == mpi(-27, -1) + assert mpi(-3, 1)**3 == mpi(-27, 1) + assert mpi(-2, 3)**2 == mpi(0, 9) + assert mpi(-3, 2)**2 == mpi(0, 9) + assert mpi(4) ** -1 == mpi(0.25, 0.25) + assert mpi(-4) ** -1 == mpi(-0.25, -0.25) + assert mpi(4) ** -2 == mpi(0.0625, 0.0625) + assert mpi(-4) ** -2 == mpi(0.0625, 0.0625) + assert mpi(0, 1) ** inf == mpi(0, 1) + assert mpi(0, 1) ** -inf == mpi(1, inf) + assert mpi(0, inf) ** inf == mpi(0, inf) + assert mpi(0, inf) ** -inf == mpi(0, inf) + assert mpi(1, inf) ** inf == mpi(1, inf) + assert mpi(1, inf) ** -inf == mpi(0, 1) + assert mpi(2, 3) ** 1 == mpi(2, 3) + assert mpi(2, 3) ** 0 == 1 + assert mpi(1,3) ** mpi(2) == mpi(1,9) + +def test_interval_sqrt(): + assert mpi(4) ** 0.5 == mpi(2) + +def test_interval_div(): + assert mpi(0.5, 1) / mpi(-1, 0) == mpi(-inf, -0.5) + assert mpi(0, 1) / mpi(0, 1) == mpi(0, inf) + assert mpi(inf, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(inf, inf) / mpi(2, inf) == mpi(0, inf) + assert mpi(inf, inf) / mpi(2, 2) == mpi(inf, inf) + assert mpi(0, inf) / mpi(2, inf) == mpi(0, inf) + assert mpi(0, inf) / mpi(2, 2) == mpi(0, inf) + assert mpi(2, inf) / mpi(2, 2) == mpi(1, inf) + assert mpi(2, inf) / mpi(2, inf) == mpi(0, inf) + assert mpi(-4, 8) / mpi(1, inf) == mpi(-4, 8) + assert mpi(-4, 8) / mpi(0.5, inf) == mpi(-8, 16) + assert mpi(-inf, 8) / mpi(0.5, inf) == mpi(-inf, 16) + assert mpi(-inf, inf) / mpi(0.5, inf) == mpi(-inf, inf) + assert mpi(8, inf) / mpi(0.5, inf) == mpi(0, inf) + assert mpi(-8, inf) / mpi(0.5, inf) == mpi(-16, inf) + assert mpi(-4, 8) / mpi(inf, inf) == mpi(0, 0) + assert mpi(0, 8) / mpi(inf, inf) == mpi(0, 0) + assert mpi(0, 0) / mpi(inf, inf) == mpi(0, 0) + assert mpi(-inf, 0) / mpi(inf, inf) == mpi(-inf, 0) + assert mpi(-inf, 8) / mpi(inf, inf) == mpi(-inf, 0) + assert mpi(-inf, inf) / mpi(inf, inf) == mpi(-inf, inf) + assert mpi(-8, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(0, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(8, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(inf, inf) / mpi(inf, inf) == mpi(0, inf) + assert mpi(-1, 2) / mpi(0, 1) == mpi(-inf, +inf) + assert mpi(0, 1) / mpi(0, 1) == mpi(0.0, +inf) + assert mpi(-1, 0) / mpi(0, 1) == mpi(-inf, 0.0) + assert mpi(-0.5, -0.25) / mpi(0, 1) == mpi(-inf, -0.25) + assert mpi(0.5, 1) / mpi(0, 1) == mpi(0.5, +inf) + assert mpi(0.5, 4) / mpi(0, 1) == mpi(0.5, +inf) + assert mpi(-1, -0.5) / mpi(0, 1) == mpi(-inf, -0.5) + assert mpi(-4, -0.5) / mpi(0, 1) == mpi(-inf, -0.5) + assert mpi(-1, 2) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(0, 1) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-1, 0) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-0.5, -0.25) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(0.5, 1) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(0.5, 4) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-1, -0.5) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-4, -0.5) / mpi(-2, 0.5) == mpi(-inf, +inf) + assert mpi(-1, 2) / mpi(-1, 0) == mpi(-inf, +inf) + assert mpi(0, 1) / mpi(-1, 0) == mpi(-inf, 0.0) + assert mpi(-1, 0) / mpi(-1, 0) == mpi(0.0, +inf) + assert mpi(-0.5, -0.25) / mpi(-1, 0) == mpi(0.25, +inf) + assert mpi(0.5, 1) / mpi(-1, 0) == mpi(-inf, -0.5) + assert mpi(0.5, 4) / mpi(-1, 0) == mpi(-inf, -0.5) + assert mpi(-1, -0.5) / mpi(-1, 0) == mpi(0.5, +inf) + assert mpi(-4, -0.5) / mpi(-1, 0) == mpi(0.5, +inf) + assert mpi(-1, 2) / mpi(0.5, 1) == mpi(-2.0, 4.0) + assert mpi(0, 1) / mpi(0.5, 1) == mpi(0.0, 2.0) + assert mpi(-1, 0) / mpi(0.5, 1) == mpi(-2.0, 0.0) + assert mpi(-0.5, -0.25) / mpi(0.5, 1) == mpi(-1.0, -0.25) + assert mpi(0.5, 1) / mpi(0.5, 1) == mpi(0.5, 2.0) + assert mpi(0.5, 4) / mpi(0.5, 1) == mpi(0.5, 8.0) + assert mpi(-1, -0.5) / mpi(0.5, 1) == mpi(-2.0, -0.5) + assert mpi(-4, -0.5) / mpi(0.5, 1) == mpi(-8.0, -0.5) + assert mpi(-1, 2) / mpi(-2, -0.5) == mpi(-4.0, 2.0) + assert mpi(0, 1) / mpi(-2, -0.5) == mpi(-2.0, 0.0) + assert mpi(-1, 0) / mpi(-2, -0.5) == mpi(0.0, 2.0) + assert mpi(-0.5, -0.25) / mpi(-2, -0.5) == mpi(0.125, 1.0) + assert mpi(0.5, 1) / mpi(-2, -0.5) == mpi(-2.0, -0.25) + assert mpi(0.5, 4) / mpi(-2, -0.5) == mpi(-8.0, -0.25) + assert mpi(-1, -0.5) / mpi(-2, -0.5) == mpi(0.25, 2.0) + assert mpi(-4, -0.5) / mpi(-2, -0.5) == mpi(0.25, 8.0) + # Should be undefined? + assert mpi(0, 0) / mpi(0, 0) == mpi(-inf, inf) + assert mpi(0, 0) / mpi(0, 1) == mpi(-inf, inf) + +def test_interval_cos_sin(): + mp.dps = 15 + # Around 0 + assert cos(mpi(0)) == 1 + assert sin(mpi(0)) == 0 + assert cos(mpi(0,1)) == mpi(0.54030230586813965399, 1.0) + assert sin(mpi(0,1)) == mpi(0, 0.8414709848078966159) + assert cos(mpi(1,2)) == mpi(-0.4161468365471424069, 0.54030230586813976501) + assert sin(mpi(1,2)) == mpi(0.84147098480789650488, 1.0) + assert sin(mpi(1,2.5)) == mpi(0.59847214410395643824, 1.0) + assert cos(mpi(-1, 1)) == mpi(0.54030230586813965399, 1.0) + assert cos(mpi(-1, 0.5)) == mpi(0.54030230586813965399, 1.0) + assert cos(mpi(-1, 1.5)) == mpi(0.070737201667702906405, 1.0) + assert sin(mpi(-1,1)) == mpi(-0.8414709848078966159, 0.8414709848078966159) + assert sin(mpi(-1,0.5)) == mpi(-0.8414709848078966159, 0.47942553860420300538) + assert sin(mpi(-1,1e-100)) == mpi(-0.8414709848078966159, 1.00000000000000002e-100) + assert sin(mpi(-2e-100,1e-100)) == mpi(-2.00000000000000004e-100, 1.00000000000000002e-100) + # Same interval + assert cos(mpi(2, 2.5)) == mpi(-0.80114361554693380718, -0.41614683654714235139) + assert cos(mpi(3.5, 4)) == mpi(-0.93645668729079634129, -0.65364362086361182946) + assert cos(mpi(5, 5.5)) == mpi(0.28366218546322624627, 0.70866977429126010168) + assert sin(mpi(2, 2.5)) == mpi(0.59847214410395654927, 0.90929742682568170942) + assert sin(mpi(3.5, 4)) == mpi(-0.75680249530792831347, -0.35078322768961983646) + assert sin(mpi(5, 5.5)) == mpi(-0.95892427466313856499, -0.70554032557039181306) + # Higher roots + mp.dps = 55 + w = 4*10**50 + mpf(0.5) + for p in [15, 40, 80]: + mp.dps = p + assert 0 in sin(4*mpi(pi)) + assert 0 in sin(4*10**50*mpi(pi)) + assert 0 in cos((4+0.5)*mpi(pi)) + assert 0 in cos(w*mpi(pi)) + assert 1 in cos(4*mpi(pi)) + assert 1 in cos(4*10**50*mpi(pi)) + mp.dps = 15 + assert cos(mpi(2,inf)) == mpi(-1,1) + assert sin(mpi(2,inf)) == mpi(-1,1) + assert cos(mpi(-inf,2)) == mpi(-1,1) + assert sin(mpi(-inf,2)) == mpi(-1,1) + u = tan(mpi(0.5,1)) + assert u.a.ae(tan(0.5)) + assert u.b.ae(tan(1)) + v = cot(mpi(0.5,1)) + assert v.a.ae(cot(1)) + assert v.b.ae(cot(0.5)) + +def test_mpi_to_str(): + mp.dps = 30 + x = mpi(1, 2) + # FIXME: error_dps should not be necessary + assert mpi_to_str(x, mode='plusminus', error_dps=6) == '1.5 +- 0.5' + assert mpi_to_str(x, mode='plusminus', use_spaces=False, error_dps=6 + ) == '1.5+-0.5' + assert mpi_to_str(x, mode='percent') == '1.5 (33.33%)' + assert mpi_to_str(x, mode='brackets', use_spaces=False) == '[1.0,2.0]' + assert mpi_to_str(x, mode='brackets' , brackets=('<', '>')) == '<1.0, 2.0>' + x = mpi('5.2582327113062393041', '5.2582327113062749951') + assert (mpi_to_str(x, mode='diff') == + '5.2582327113062[393041, 749951]') + assert (mpi_to_str(cos(mpi(1)), mode='diff', use_spaces=False) == + '0.54030230586813971740093660744[2955,3053]') + assert (mpi_to_str(mpi('1e123', '1e129'), mode='diff') == + '[1.0e+123, 1.0e+129]') + assert (mpi_to_str(exp(mpi('5000.1')), mode='diff') == + '3.2797365856787867069110487[0926, 1191]e+2171') + +def test_mpi_from_str(): + assert mpi_from_str('1.5 +- 0.5') == mpi(mpf('1.0'), mpf('2.0')) + assert (mpi_from_str('1.5 (33.33333333333333333333333333333%)') == + mpi(mpf(1), mpf(2))) + assert mpi_from_str('[1, 2]') == mpi(1, 2) + assert mpi_from_str('1[2, 3]') == mpi(12, 13) + assert mpi_from_str('1.[23,46]e-8') == mpi('1.23e-8', '1.46e-8') + assert mpi_from_str('12[3.4,5.9]e4') == mpi('123.4e+4', '125.9e4') diff --git a/compiler/gdsMill/mpmath/tests/test_linalg.py b/compiler/gdsMill/mpmath/tests/test_linalg.py new file mode 100644 index 00000000..23be1fca --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_linalg.py @@ -0,0 +1,243 @@ +# TODO: don't use round + +from __future__ import division + +from mpmath import * + +# XXX: these shouldn't be visible(?) +LU_decomp = mp.LU_decomp +L_solve = mp.L_solve +U_solve = mp.U_solve +householder = mp.householder +improve_solution = mp.improve_solution + +A1 = matrix([[3, 1, 6], + [2, 1, 3], + [1, 1, 1]]) +b1 = [2, 7, 4] + +A2 = matrix([[ 2, -1, -1, 2], + [ 6, -2, 3, -1], + [-4, 2, 3, -2], + [ 2, 0, 4, -3]]) +b2 = [3, -3, -2, -1] + +A3 = matrix([[ 1, 0, -1, -1, 0], + [ 0, 1, 1, 0, -1], + [ 4, -5, 2, 0, 0], + [ 0, 0, -2, 9,-12], + [ 0, 5, 0, 0, 12]]) +b3 = [0, 0, 0, 0, 50] + +A4 = matrix([[10.235, -4.56, 0., -0.035, 5.67], + [-2.463, 1.27, 3.97, -8.63, 1.08], + [-6.58, 0.86, -0.257, 9.32, -43.6 ], + [ 9.83, 7.39, -17.25, 0.036, 24.86], + [-9.31, 34.9, 78.56, 1.07, 65.8 ]]) +b4 = [8.95, 20.54, 7.42, 5.60, 58.43] + +A5 = matrix([[ 1, 2, -4], + [-2, -3, 5], + [ 3, 5, -8]]) + +A6 = matrix([[ 1.377360, 2.481400, 5.359190], + [ 2.679280, -1.229560, 25.560210], + [-1.225280+1.e6, 9.910180, -35.049900-1.e6]]) +b6 = [23.500000, -15.760000, 2.340000] + +A7 = matrix([[1, -0.5], + [2, 1], + [-2, 6]]) +b7 = [3, 2, -4] + +A8 = matrix([[1, 2, 3], + [-1, 0, 1], + [-1, -2, -1], + [1, 0, -1]]) +b8 = [1, 2, 3, 4] + +A9 = matrix([[ 4, 2, -2], + [ 2, 5, -4], + [-2, -4, 5.5]]) +b9 = [10, 16, -15.5] + +A10 = matrix([[1.0 + 1.0j, 2.0, 2.0], + [4.0, 5.0, 6.0], + [7.0, 8.0, 9.0]]) +b10 = [1.0, 1.0 + 1.0j, 1.0] + + +def test_LU_decomp(): + A = A3.copy() + b = b3 + A, p = LU_decomp(A) + y = L_solve(A, b, p) + x = U_solve(A, y) + assert p == [2, 1, 2, 3] + assert [round(i, 14) for i in x] == [3.78953107960742, 2.9989094874591098, + -0.081788440567070006, 3.8713195201744801, 2.9171210468920399] + A = A4.copy() + b = b4 + A, p = LU_decomp(A) + y = L_solve(A, b, p) + x = U_solve(A, y) + assert p == [0, 3, 4, 3] + assert [round(i, 14) for i in x] == [2.6383625899619201, 2.6643834462368399, + 0.79208015947958998, -2.5088376454101899, -1.0567657691375001] + A = randmatrix(3) + bak = A.copy() + LU_decomp(A, overwrite=1) + assert A != bak + +def test_inverse(): + for A in [A1, A2, A5]: + inv = inverse(A) + assert mnorm(A*inv - eye(A.rows), 1) < 1.e-14 + +def test_householder(): + mp.dps = 15 + A, b = A8, b8 + H, p, x, r = householder(extend(A, b)) + assert H == matrix( + [[mpf('3.0'), mpf('-2.0'), mpf('-1.0'), 0], + [-1.0,mpf('3.333333333333333'),mpf('-2.9999999999999991'),mpf('2.0')], + [-1.0, mpf('-0.66666666666666674'),mpf('2.8142135623730948'), + mpf('-2.8284271247461898')], + [1.0, mpf('-1.3333333333333333'),mpf('-0.20000000000000018'), + mpf('4.2426406871192857')]]) + assert p == [-2, -2, mpf('-1.4142135623730949')] + assert round(norm(r, 2), 10) == 4.2426406870999998 + + y = [102.102, 58.344, 36.463, 24.310, 17.017, 12.376, 9.282, 7.140, 5.610, + 4.488, 3.6465, 3.003] + + def coeff(n): + # similiar to Hilbert matrix + A = [] + for i in xrange(1, 13): + A.append([1. / (i + j - 1) for j in xrange(1, n + 1)]) + return matrix(A) + + residuals = [] + refres = [] + for n in xrange(2, 7): + A = coeff(n) + H, p, x, r = householder(extend(A, y)) + x = matrix(x) + y = matrix(y) + residuals.append(norm(r, 2)) + refres.append(norm(residual(A, x, y), 2)) + assert [round(res, 10) for res in residuals] == [15.1733888877, + 0.82378073210000002, 0.302645887, 0.0260109244, + 0.00058653999999999998] + assert norm(matrix(residuals) - matrix(refres), inf) < 1.e-13 + +def test_factorization(): + A = randmatrix(5) + P, L, U = lu(A) + assert mnorm(P*A - L*U, 1) < 1.e-15 + +def test_solve(): + assert norm(residual(A6, lu_solve(A6, b6), b6), inf) < 1.e-10 + assert norm(residual(A7, lu_solve(A7, b7), b7), inf) < 1.5 + assert norm(residual(A8, lu_solve(A8, b8), b8), inf) <= 3 + 1.e-10 + assert norm(residual(A6, qr_solve(A6, b6)[0], b6), inf) < 1.e-10 + assert norm(residual(A7, qr_solve(A7, b7)[0], b7), inf) < 1.5 + assert norm(residual(A8, qr_solve(A8, b8)[0], b8), 2) <= 4.3 + assert norm(residual(A10, lu_solve(A10, b10), b10), 2) < 1.e-10 + assert norm(residual(A10, qr_solve(A10, b10)[0], b10), 2) < 1.e-10 + +def test_solve_overdet_complex(): + A = matrix([[1, 2j], [3, 4j], [5, 6]]) + b = matrix([1 + j, 2, -j]) + assert norm(residual(A, lu_solve(A, b), b)) < 1.0208 + +def test_singular(): + mp.dps = 15 + A = [[5.6, 1.2], [7./15, .1]] + B = repr(zeros(2)) + b = [1, 2] + def _assert_ZeroDivisionError(statement): + try: + eval(statement) + assert False + except (ZeroDivisionError, ValueError): + pass + for i in ['lu_solve(%s, %s)' % (A, b), 'lu_solve(%s, %s)' % (B, b), + 'qr_solve(%s, %s)' % (A, b), 'qr_solve(%s, %s)' % (B, b)]: + _assert_ZeroDivisionError(i) + +def test_cholesky(): + assert fp.cholesky(fp.matrix(A9)) == fp.matrix([[2, 0, 0], [1, 2, 0], [-1, -3/2, 3/2]]) + x = fp.cholesky_solve(A9, b9) + assert fp.norm(fp.residual(A9, x, b9), fp.inf) == 0 + +def test_det(): + assert det(A1) == 1 + assert round(det(A2), 14) == 8 + assert round(det(A3)) == 1834 + assert round(det(A4)) == 4443376 + assert det(A5) == 1 + assert round(det(A6)) == 78356463 + assert det(zeros(3)) == 0 + +def test_cond(): + mp.dps = 15 + A = matrix([[1.2969, 0.8648], [0.2161, 0.1441]]) + assert cond(A, lambda x: mnorm(x,1)) == mpf('327065209.73817754') + assert cond(A, lambda x: mnorm(x,inf)) == mpf('327065209.73817754') + assert cond(A, lambda x: mnorm(x,'F')) == mpf('249729266.80008656') + +@extradps(50) +def test_precision(): + A = randmatrix(10, 10) + assert mnorm(inverse(inverse(A)) - A, 1) < 1.e-45 + +def test_interval_matrix(): + a = matrix([['0.1','0.3','1.0'],['7.1','5.5','4.8'],['3.2','4.4','5.6']], + force_type=mpi) + b = matrix(['4','0.6','0.5'], force_type=mpi) + c = lu_solve(a, b) + assert c[0].delta < 1e-13 + assert c[1].delta < 1e-13 + assert c[2].delta < 1e-13 + assert 5.25823271130625686059275 in c[0] + assert -13.155049396267837541163 in c[1] + assert 7.42069154774972557628979 in c[2] + +def test_LU_cache(): + A = randmatrix(3) + LU = LU_decomp(A) + assert A._LU == LU_decomp(A) + A[0,0] = -1000 + assert A._LU is None + +def test_improve_solution(): + A = randmatrix(5, min=1e-20, max=1e20) + b = randmatrix(5, 1, min=-1000, max=1000) + x1 = lu_solve(A, b) + randmatrix(5, 1, min=-1e-5, max=1.e-5) + x2 = improve_solution(A, x1, b) + assert norm(residual(A, x2, b), 2) < norm(residual(A, x1, b), 2) + +def test_exp_pade(): + for i in range(3): + dps = 15 + extra = 5 + mp.dps = dps + extra + dm = 0 + while not dm: + m = randmatrix(3) + dm = det(m) + m = m/dm + a = diag([1,2,3]) + a1 = m**-1 * a * m + mp.dps = dps + e1 = expm(a1, method='pade') + mp.dps = dps + extra + e2 = m * a1 * m**-1 + d = e2 - a + #print d + mp.dps = dps + assert norm(d, inf).ae(0) + mp.dps = 15 + diff --git a/compiler/gdsMill/mpmath/tests/test_matrices.py b/compiler/gdsMill/mpmath/tests/test_matrices.py new file mode 100644 index 00000000..e3f174ca --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_matrices.py @@ -0,0 +1,144 @@ +from mpmath import * + +def test_matrix_basic(): + A1 = matrix(3) + for i in xrange(3): + A1[i,i] = 1 + assert A1 == eye(3) + assert A1 == matrix(A1) + A2 = matrix(3, 2) + assert not A2._matrix__data + A3 = matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + assert list(A3) == range(1, 10) + A3[1,1] = 0 + assert not (1, 1) in A3._matrix__data + A4 = matrix([[1, 2, 3], [4, 5, 6]]) + A5 = matrix([[6, -1], [3, 2], [0, -3]]) + assert A4 * A5 == matrix([[12, -6], [39, -12]]) + assert A1 * A3 == A3 * A1 == A3 + try: + A2 * A2 + assert False + except ValueError: + pass + l = [[10, 20, 30], [40, 0, 60], [70, 80, 90]] + A6 = matrix(l) + assert A6.tolist() == l + assert A6 == eval(repr(A6)) + A6 = matrix(A6, force_type=float) + assert A6 == eval(repr(A6)) + assert A6*1j == eval(repr(A6*1j)) + assert A3 * 10 == 10 * A3 == A6 + assert A2.rows == 3 + assert A2.cols == 2 + A3.rows = 2 + A3.cols = 2 + assert len(A3._matrix__data) == 3 + assert A4 + A4 == 2*A4 + try: + A4 + A2 + except ValueError: + pass + assert sum(A1 - A1) == 0 + A7 = matrix([[1, 2], [3, 4], [5, 6], [7, 8]]) + x = matrix([10, -10]) + assert A7*x == matrix([-10, -10, -10, -10]) + A8 = ones(5) + assert sum((A8 + 1) - (2 - zeros(5))) == 0 + assert (1 + ones(4)) / 2 - 1 == zeros(4) + assert eye(3)**10 == eye(3) + try: + A7**2 + assert False + except ValueError: + pass + A9 = randmatrix(3) + A10 = matrix(A9) + A9[0,0] = -100 + assert A9 != A10 + A11 = matrix(randmatrix(2, 3), force_type=mpi) + for a in A11: + assert isinstance(a, mpi) + assert nstr(A9) + +def test_matrix_power(): + A = matrix([[1, 2], [3, 4]]) + assert A**2 == A*A + assert A**3 == A*A*A + assert A**-1 == inverse(A) + assert A**-2 == inverse(A*A) + +def test_matrix_transform(): + A = matrix([[1, 2], [3, 4], [5, 6]]) + assert A.T == A.transpose() == matrix([[1, 3, 5], [2, 4, 6]]) + swap_row(A, 1, 2) + assert A == matrix([[1, 2], [5, 6], [3, 4]]) + l = [1, 2] + swap_row(l, 0, 1) + assert l == [2, 1] + assert extend(eye(3), [1,2,3]) == matrix([[1,0,0,1],[0,1,0,2],[0,0,1,3]]) + +def test_matrix_conjugate(): + A = matrix([[1 + j, 0], [2, j]]) + assert A.conjugate() == matrix([[mpc(1, -1), 0], [2, mpc(0, -1)]]) + assert A.transpose_conj() == A.H == matrix([[mpc(1, -1), 2], + [0, mpc(0, -1)]]) + +def test_matrix_creation(): + assert diag([1, 2, 3]) == matrix([[1, 0, 0], [0, 2, 0], [0, 0, 3]]) + A1 = ones(2, 3) + assert A1.rows == 2 and A1.cols == 3 + for a in A1: + assert a == 1 + A2 = zeros(3, 2) + assert A2.rows == 3 and A2.cols == 2 + for a in A2: + assert a == 0 + assert randmatrix(10) != randmatrix(10) + one = mpf(1) + assert hilbert(3) == matrix([[one, one/2, one/3], + [one/2, one/3, one/4], + [one/3, one/4, one/5]]) + +def test_norms(): + # matrix norms + A = matrix([[1, -2], [-3, -1], [2, 1]]) + assert mnorm(A,1) == 6 + assert mnorm(A,inf) == 4 + assert mnorm(A,'F') == sqrt(20) + # vector norms + assert norm(-3) == 3 + x = [1, -2, 7, -12] + assert norm(x, 1) == 22 + assert round(norm(x, 2), 10) == 14.0712472795 + assert round(norm(x, 10), 10) == 12.0054633727 + assert norm(x, inf) == 12 + +def test_vector(): + x = matrix([0, 1, 2, 3, 4]) + assert x == matrix([[0], [1], [2], [3], [4]]) + assert x[3] == 3 + assert len(x._matrix__data) == 4 + assert list(x) == range(5) + x[0] = -10 + x[4] = 0 + assert x[0] == -10 + assert len(x) == len(x.T) == 5 + assert x.T*x == matrix([[114]]) + +def test_matrix_copy(): + A = ones(6) + B = A.copy() + assert A == B + B[0,0] = 0 + assert A != B + +def test_matrix_numpy(): + try: + import numpy + except ImportError: + return + l = [[1, 2], [3, 4], [5, 6]] + a = numpy.matrix(l) + assert matrix(l) == matrix(a) + diff --git a/compiler/gdsMill/mpmath/tests/test_mpmath.py b/compiler/gdsMill/mpmath/tests/test_mpmath.py new file mode 100644 index 00000000..1f34f4d8 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_mpmath.py @@ -0,0 +1,98 @@ +from mpmath.libmp import * +from mpmath import * +import random + + +#---------------------------------------------------------------------------- +# Low-level tests +# + +# Advanced rounding test +def test_add_rounding(): + mp.dps = 15 + a = from_float(1e-50) + assert mpf_sub(mpf_add(fone, a, 53, round_up), fone, 53, round_up) == from_float(2.2204460492503131e-16) + assert mpf_sub(fone, a, 53, round_up) == fone + assert mpf_sub(fone, mpf_sub(fone, a, 53, round_down), 53, round_down) == from_float(1.1102230246251565e-16) + assert mpf_add(fone, a, 53, round_down) == fone + +def test_almost_equal(): + assert mpf(1.2).ae(mpf(1.20000001), 1e-7) + assert not mpf(1.2).ae(mpf(1.20000001), 1e-9) + assert not mpf(-0.7818314824680298).ae(mpf(-0.774695868667929)) + + +#---------------------------------------------------------------------------- +# Test basic arithmetic +# + +# Test that integer arithmetic is exact +def test_aintegers(): + # XXX: re-fix this so that all operations are tested with all rounding modes + random.seed(0) + for prec in [6, 10, 25, 40, 100, 250, 725]: + for rounding in ['d', 'u', 'f', 'c', 'n']: + mp.dps = prec + M = 10**(prec-2) + M2 = 10**(prec//2-2) + for i in range(10): + a = random.randint(-M, M) + b = random.randint(-M, M) + assert mpf(a, rounding=rounding) == a + assert int(mpf(a, rounding=rounding)) == a + assert int(mpf(str(a), rounding=rounding)) == a + assert mpf(a) + mpf(b) == a + b + assert mpf(a) - mpf(b) == a - b + assert -mpf(a) == -a + a = random.randint(-M2, M2) + b = random.randint(-M2, M2) + assert mpf(a) * mpf(b) == a*b + assert mpf_mul(from_int(a), from_int(b), mp.prec, rounding) == from_int(a*b) + mp.dps = 15 + +def test_odd_int_bug(): + assert to_int(from_int(3), round_nearest) == 3 + +def test_str_1000_digits(): + mp.dps = 1001 + # last digit may be wrong + assert str(mpf(2)**0.5)[-10:-1] == '9518488472'[:9] + assert str(pi)[-10:-1] == '2164201989'[:9] + mp.dps = 15 + +def test_str_10000_digits(): + mp.dps = 10001 + # last digit may be wrong + assert str(mpf(2)**0.5)[-10:-1] == '5873258351'[:9] + assert str(pi)[-10:-1] == '5256375678'[:9] + mp.dps = 15 + +def test_monitor(): + f = lambda x: x**2 + a = [] + b = [] + g = monitor(f, a.append, b.append) + assert g(3) == 9 + assert g(4) == 16 + assert a[0] == ((3,), {}) + assert b[0] == 9 + +def test_nint_distance(): + nint_distance(mpf(-3)) == (-3, -inf) + nint_distance(mpc(-3)) == (-3, -inf) + nint_distance(mpf(-3.1)) == (-3, -3) + nint_distance(mpf(-3.01)) == (-3, -6) + nint_distance(mpf(-3.001)) == (-3, -9) + nint_distance(mpf(-3.0001)) == (-3, -13) + nint_distance(mpf(-2.9)) == (-3, -3) + nint_distance(mpf(-2.99)) == (-3, -6) + nint_distance(mpf(-2.999)) == (-3, -9) + nint_distance(mpf(-2.9999)) == (-3, -13) + nint_distance(mpc(-3+0.1j)) == (-3, -3) + nint_distance(mpc(-3+0.01j)) == (-3, -6) + nint_distance(mpc(-3.1+0.1j)) == (-3, -3) + nint_distance(mpc(-3.01+0.01j)) == (-3, -6) + nint_distance(mpc(-3.001+0.001j)) == (-3, -9) + nint_distance(mpf(0)) == (0, -inf) + nint_distance(mpf(0.01)) == (0, -6) + nint_distance(mpf('1e-100')) == (0, -332) diff --git a/compiler/gdsMill/mpmath/tests/test_ode.py b/compiler/gdsMill/mpmath/tests/test_ode.py new file mode 100644 index 00000000..c8439028 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_ode.py @@ -0,0 +1,73 @@ +#from mpmath.calculus import ODE_step_euler, ODE_step_rk4, odeint, arange +from mpmath import odefun, cos, sin, mpf, sinc, mp + +''' +solvers = [ODE_step_euler, ODE_step_rk4] + +def test_ode1(): + """ + Let's solve: + + x'' + w**2 * x = 0 + + i.e. x1 = x, x2 = x1': + + x1' = x2 + x2' = -x1 + """ + def derivs((x1, x2), t): + return x2, -x1 + + for solver in solvers: + t = arange(0, 3.1415926, 0.005) + sol = odeint(derivs, (0., 1.), t, solver) + x1 = [a[0] for a in sol] + x2 = [a[1] for a in sol] + # the result is x1 = sin(t), x2 = cos(t) + # let's just check the end points for t = pi + assert abs(x1[-1]) < 1e-2 + assert abs(x2[-1] - (-1)) < 1e-2 + +def test_ode2(): + """ + Let's solve: + + x' - x = 0 + + i.e. x = exp(x) + + """ + def derivs((x), t): + return x + + for solver in solvers: + t = arange(0, 1, 1e-3) + sol = odeint(derivs, (1.,), t, solver) + x = [a[0] for a in sol] + # the result is x = exp(t) + # let's just check the end point for t = 1, i.e. x = e + assert abs(x[-1] - 2.718281828) < 1e-2 +''' + +def test_odefun_rational(): + mp.dps = 15 + # A rational function + f = lambda t: 1/(1+mpf(t)**2) + g = odefun(lambda x, y: [-2*x*y[0]**2], 0, [f(0)]) + assert f(2).ae(g(2)[0]) + +def test_odefun_sinc_large(): + mp.dps = 15 + # Sinc function; test for large x + f = sinc + g = odefun(lambda x, y: [(cos(x)-y[0])/x], 1, [f(1)], tol=0.01, degree=5) + assert abs(f(100) - g(100)[0])/f(100) < 0.01 + +def test_odefun_harmonic(): + mp.dps = 15 + # Harmonic oscillator + f = odefun(lambda x, y: [-y[1], y[0]], 0, [1, 0]) + for x in [0, 1, 2.5, 8, 3.7]: # we go back to 3.7 to check caching + c, s = f(x) + assert c.ae(cos(x)) + assert s.ae(sin(x)) diff --git a/compiler/gdsMill/mpmath/tests/test_pickle.py b/compiler/gdsMill/mpmath/tests/test_pickle.py new file mode 100644 index 00000000..44d4b808 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_pickle.py @@ -0,0 +1,27 @@ +import os +import tempfile +import pickle + +from mpmath import * + +def pickler(obj): + fn = tempfile.mktemp() + + f = open(fn, 'wb') + pickle.dump(obj, f) + f.close() + + f = open(fn, 'rb') + obj2 = pickle.load(f) + f.close() + os.remove(fn) + + return obj2 + +def test_pickle(): + + obj = mpf('0.5') + assert obj == pickler(obj) + + obj = mpc('0.5','0.2') + assert obj == pickler(obj) diff --git a/compiler/gdsMill/mpmath/tests/test_power.py b/compiler/gdsMill/mpmath/tests/test_power.py new file mode 100644 index 00000000..a1c24d3c --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_power.py @@ -0,0 +1,155 @@ +from mpmath import * +from mpmath.libmp import * + +import random + +def test_fractional_pow(): + assert mpf(16) ** 2.5 == 1024 + assert mpf(64) ** 0.5 == 8 + assert mpf(64) ** -0.5 == 0.125 + assert mpf(16) ** -2.5 == 0.0009765625 + assert (mpf(10) ** 0.5).ae(3.1622776601683791) + assert (mpf(10) ** 2.5).ae(316.2277660168379) + assert (mpf(10) ** -0.5).ae(0.31622776601683794) + assert (mpf(10) ** -2.5).ae(0.0031622776601683794) + assert (mpf(10) ** 0.3).ae(1.9952623149688795) + assert (mpf(10) ** -0.3).ae(0.50118723362727224) + +def test_pow_integer_direction(): + """ + Test that inexact integer powers are rounded in the right + direction. + """ + random.seed(1234) + for prec in [10, 53, 200]: + for i in range(50): + a = random.randint(1<<(prec-1), 1< ab + + +def test_pow_epsilon_rounding(): + """ + Stress test directed rounding for powers with integer exponents. + Basically, we look at the following cases: + + >>> 1.0001 ** -5 + 0.99950014996500702 + >>> 0.9999 ** -5 + 1.000500150035007 + >>> (-1.0001) ** -5 + -0.99950014996500702 + >>> (-0.9999) ** -5 + -1.000500150035007 + + >>> 1.0001 ** -6 + 0.99940020994401269 + >>> 0.9999 ** -6 + 1.0006002100560125 + >>> (-1.0001) ** -6 + 0.99940020994401269 + >>> (-0.9999) ** -6 + 1.0006002100560125 + + etc. + + We run the tests with values a very small epsilon away from 1: + small enough that the result is indistinguishable from 1 when + rounded to nearest at the output precision. We check that the + result is not erroneously rounded to 1 in cases where the + rounding should be done strictly away from 1. + """ + + def powr(x, n, r): + return make_mpf(mpf_pow_int(x._mpf_, n, mp.prec, r)) + + for (inprec, outprec) in [(100, 20), (5000, 3000)]: + + mp.prec = inprec + + pos10001 = mpf(1) + mpf(2)**(-inprec+5) + pos09999 = mpf(1) - mpf(2)**(-inprec+5) + neg10001 = -pos10001 + neg09999 = -pos09999 + + mp.prec = outprec + r = round_up + assert powr(pos10001, 5, r) > 1 + assert powr(pos09999, 5, r) == 1 + assert powr(neg10001, 5, r) < -1 + assert powr(neg09999, 5, r) == -1 + assert powr(pos10001, 6, r) > 1 + assert powr(pos09999, 6, r) == 1 + assert powr(neg10001, 6, r) > 1 + assert powr(neg09999, 6, r) == 1 + + assert powr(pos10001, -5, r) == 1 + assert powr(pos09999, -5, r) > 1 + assert powr(neg10001, -5, r) == -1 + assert powr(neg09999, -5, r) < -1 + assert powr(pos10001, -6, r) == 1 + assert powr(pos09999, -6, r) > 1 + assert powr(neg10001, -6, r) == 1 + assert powr(neg09999, -6, r) > 1 + + r = round_down + assert powr(pos10001, 5, r) == 1 + assert powr(pos09999, 5, r) < 1 + assert powr(neg10001, 5, r) == -1 + assert powr(neg09999, 5, r) > -1 + assert powr(pos10001, 6, r) == 1 + assert powr(pos09999, 6, r) < 1 + assert powr(neg10001, 6, r) == 1 + assert powr(neg09999, 6, r) < 1 + + assert powr(pos10001, -5, r) < 1 + assert powr(pos09999, -5, r) == 1 + assert powr(neg10001, -5, r) > -1 + assert powr(neg09999, -5, r) == -1 + assert powr(pos10001, -6, r) < 1 + assert powr(pos09999, -6, r) == 1 + assert powr(neg10001, -6, r) < 1 + assert powr(neg09999, -6, r) == 1 + + r = round_ceiling + assert powr(pos10001, 5, r) > 1 + assert powr(pos09999, 5, r) == 1 + assert powr(neg10001, 5, r) == -1 + assert powr(neg09999, 5, r) > -1 + assert powr(pos10001, 6, r) > 1 + assert powr(pos09999, 6, r) == 1 + assert powr(neg10001, 6, r) > 1 + assert powr(neg09999, 6, r) == 1 + + assert powr(pos10001, -5, r) == 1 + assert powr(pos09999, -5, r) > 1 + assert powr(neg10001, -5, r) > -1 + assert powr(neg09999, -5, r) == -1 + assert powr(pos10001, -6, r) == 1 + assert powr(pos09999, -6, r) > 1 + assert powr(neg10001, -6, r) == 1 + assert powr(neg09999, -6, r) > 1 + + r = round_floor + assert powr(pos10001, 5, r) == 1 + assert powr(pos09999, 5, r) < 1 + assert powr(neg10001, 5, r) < -1 + assert powr(neg09999, 5, r) == -1 + assert powr(pos10001, 6, r) == 1 + assert powr(pos09999, 6, r) < 1 + assert powr(neg10001, 6, r) == 1 + assert powr(neg09999, 6, r) < 1 + + assert powr(pos10001, -5, r) < 1 + assert powr(pos09999, -5, r) == 1 + assert powr(neg10001, -5, r) == -1 + assert powr(neg09999, -5, r) < -1 + assert powr(pos10001, -6, r) < 1 + assert powr(pos09999, -6, r) == 1 + assert powr(neg10001, -6, r) < 1 + assert powr(neg09999, -6, r) == 1 + + mp.dps = 15 diff --git a/compiler/gdsMill/mpmath/tests/test_quad.py b/compiler/gdsMill/mpmath/tests/test_quad.py new file mode 100644 index 00000000..3fd2cdee --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_quad.py @@ -0,0 +1,85 @@ +from mpmath import * + +def ae(a, b): + return abs(a-b) < 10**(-mp.dps+5) + +def test_basic_integrals(): + for prec in [15, 30, 100]: + mp.dps = prec + assert ae(quadts(lambda x: x**3 - 3*x**2, [-2, 4]), -12) + assert ae(quadgl(lambda x: x**3 - 3*x**2, [-2, 4]), -12) + assert ae(quadts(sin, [0, pi]), 2) + assert ae(quadts(sin, [0, 2*pi]), 0) + assert ae(quadts(exp, [-inf, -1]), 1/e) + assert ae(quadts(lambda x: exp(-x), [0, inf]), 1) + assert ae(quadts(lambda x: exp(-x*x), [-inf, inf]), sqrt(pi)) + assert ae(quadts(lambda x: 1/(1+x*x), [-1, 1]), pi/2) + assert ae(quadts(lambda x: 1/(1+x*x), [-inf, inf]), pi) + assert ae(quadts(lambda x: 2*sqrt(1-x*x), [-1, 1]), pi) + mp.dps = 15 + +def test_quad_symmetry(): + assert quadts(sin, [-1, 1]) == 0 + assert quadgl(sin, [-1, 1]) == 0 + +def test_quadgl_linear(): + assert quadgl(lambda x: x, [0, 1], maxdegree=1).ae(0.5) + +def test_complex_integration(): + assert quadts(lambda x: x, [0, 1+j]).ae(j) + +def test_quadosc(): + mp.dps = 15 + assert quadosc(lambda x: sin(x)/x, [0, inf], period=2*pi).ae(pi/2) + +# Double integrals +def test_double_trivial(): + assert ae(quadts(lambda x, y: x, [0, 1], [0, 1]), 0.5) + assert ae(quadts(lambda x, y: x, [-1, 1], [-1, 1]), 0.0) + +def test_double_1(): + assert ae(quadts(lambda x, y: cos(x+y/2), [-pi/2, pi/2], [0, pi]), 4) + +def test_double_2(): + assert ae(quadts(lambda x, y: (x-1)/((1-x*y)*log(x*y)), [0, 1], [0, 1]), euler) + +def test_double_3(): + assert ae(quadts(lambda x, y: 1/sqrt(1+x*x+y*y), [-1, 1], [-1, 1]), 4*log(2+sqrt(3))-2*pi/3) + +def test_double_4(): + assert ae(quadts(lambda x, y: 1/(1-x*x * y*y), [0, 1], [0, 1]), pi**2 / 8) + +def test_double_5(): + assert ae(quadts(lambda x, y: 1/(1-x*y), [0, 1], [0, 1]), pi**2 / 6) + +def test_double_6(): + assert ae(quadts(lambda x, y: exp(-(x+y)), [0, inf], [0, inf]), 1) + +# fails +def xtest_double_7(): + assert ae(quadts(lambda x, y: exp(-x*x-y*y), [-inf, inf], [-inf, inf]), pi) + + +# Test integrals from "Experimentation in Mathematics" by Borwein, +# Bailey & Girgensohn +def test_expmath_integrals(): + for prec in [15, 30, 50]: + mp.dps = prec + assert ae(quadts(lambda x: x/sinh(x), [0, inf]), pi**2 / 4) + assert ae(quadts(lambda x: log(x)**2 / (1+x**2), [0, inf]), pi**3 / 8) + assert ae(quadts(lambda x: (1+x**2)/(1+x**4), [0, inf]), pi/sqrt(2)) + assert ae(quadts(lambda x: log(x)/cosh(x)**2, [0, inf]), log(pi)-2*log(2)-euler) + assert ae(quadts(lambda x: log(1+x**3)/(1-x+x**2), [0, inf]), 2*pi*log(3)/sqrt(3)) + assert ae(quadts(lambda x: log(x)**2 / (x**2+x+1), [0, 1]), 8*pi**3 / (81*sqrt(3))) + assert ae(quadts(lambda x: log(cos(x))**2, [0, pi/2]), pi/2 * (log(2)**2+pi**2/12)) + assert ae(quadts(lambda x: x**2 / sin(x)**2, [0, pi/2]), pi*log(2)) + assert ae(quadts(lambda x: x**2/sqrt(exp(x)-1), [0, inf]), 4*pi*(log(2)**2 + pi**2/12)) + assert ae(quadts(lambda x: x*exp(-x)*sqrt(1-exp(-2*x)), [0, inf]), pi*(1+2*log(2))/8) + mp.dps = 15 + +# Do not reach full accuracy +def xtest_expmath_fail(): + assert ae(quadts(lambda x: sqrt(tan(x)), [0, pi/2]), pi*sqrt(2)/2) + assert ae(quadts(lambda x: atan(x)/(x*sqrt(1-x**2)), [0, 1]), pi*log(1+sqrt(2))/2) + assert ae(quadts(lambda x: log(1+x**2)/x**2, [0, 1]), pi/2-log(2)) + assert ae(quadts(lambda x: x**2/((1+x**4)*sqrt(1-x**4)), [0, 1]), pi/8) diff --git a/compiler/gdsMill/mpmath/tests/test_rootfinding.py b/compiler/gdsMill/mpmath/tests/test_rootfinding.py new file mode 100644 index 00000000..fa5fa171 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_rootfinding.py @@ -0,0 +1,75 @@ +from mpmath import * +from mpmath.calculus.optimization import Secant, Muller, Bisection, Illinois, \ + Pegasus, Anderson, Ridder, ANewton, Newton, MNewton, MDNewton + +def test_findroot(): + # old tests, assuming secant + mp.dps = 15 + assert findroot(lambda x: 4*x-3, mpf(5)).ae(0.75) + assert findroot(sin, mpf(3)).ae(pi) + assert findroot(sin, (mpf(3), mpf(3.14))).ae(pi) + assert findroot(lambda x: x*x+1, mpc(2+2j)).ae(1j) + # test all solvers with 1 starting point + f = lambda x: cos(x) + for solver in [Newton, Secant, MNewton, Muller, ANewton]: + x = findroot(f, 2., solver=solver) + assert abs(f(x)) < eps + # test all solvers with interval of 2 points + for solver in [Secant, Muller, Bisection, Illinois, Pegasus, Anderson, + Ridder]: + x = findroot(f, (1., 2.), solver=solver) + assert abs(f(x)) < eps + # test types + f = lambda x: (x - 2)**2 + + #assert isinstance(findroot(f, 1, force_type=mpf, tol=1e-10), mpf) + #assert isinstance(findroot(f, 1., force_type=None, tol=1e-10), float) + #assert isinstance(findroot(f, 1, force_type=complex, tol=1e-10), complex) + assert isinstance(fp.findroot(f, 1, tol=1e-10), float) + assert isinstance(fp.findroot(f, 1+0j, tol=1e-10), complex) + +def test_mnewton(): + f = lambda x: polyval([1,3,3,1],x) + x = findroot(f, -0.9, solver='mnewton') + assert abs(f(x)) < eps + +def test_anewton(): + f = lambda x: (x - 2)**100 + x = findroot(f, 1., solver=ANewton) + assert abs(f(x)) < eps + +def test_muller(): + f = lambda x: (2 + x)**3 + 2 + x = findroot(f, 1., solver=Muller) + assert abs(f(x)) < eps + +def test_multiplicity(): + for i in xrange(1, 5): + assert multiplicity(lambda x: (x - 1)**i, 1) == i + assert multiplicity(lambda x: x**2, 1) == 0 + +def test_multidimensional(): + def f(*x): + return [3*x[0]**2-2*x[1]**2-1, x[0]**2-2*x[0]+x[1]**2+2*x[1]-8] + assert mnorm(jacobian(f, (1,-2)) - matrix([[6,8],[0,-2]]),1) < 1.e-7 + for x, error in MDNewton(mp, f, (1,-2), verbose=0, + norm=lambda x: norm(x, inf)): + pass + assert norm(f(*x), 2) < 1e-14 + # The Chinese mathematician Zhu Shijie was the very first to solve this + # nonlinear system 700 years ago + f1 = lambda x, y: -x + 2*y + f2 = lambda x, y: (x**2 + x*(y**2 - 2) - 4*y) / (x + 4) + f3 = lambda x, y: sqrt(x**2 + y**2) + def f(x, y): + f1x = f1(x, y) + return (f2(x, y) - f1x, f3(x, y) - f1x) + x = findroot(f, (10, 10)) + assert [int(round(i)) for i in x] == [3, 4] + +def test_trivial(): + assert findroot(lambda x: 0, 1) == 1 + assert findroot(lambda x: x, 0) == 0 + #assert findroot(lambda x, y: x + y, (1, -1)) == (1, -1) + + diff --git a/compiler/gdsMill/mpmath/tests/test_special.py b/compiler/gdsMill/mpmath/tests/test_special.py new file mode 100644 index 00000000..c8a39260 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_special.py @@ -0,0 +1,112 @@ +from mpmath import * + +def test_special(): + assert inf == inf + assert inf != -inf + assert -inf == -inf + assert inf != nan + assert nan != nan + assert isnan(nan) + assert --inf == inf + assert abs(inf) == inf + assert abs(-inf) == inf + assert abs(nan) != abs(nan) + + assert isnan(inf - inf) + assert isnan(inf + (-inf)) + assert isnan(-inf - (-inf)) + + assert isnan(inf + nan) + assert isnan(-inf + nan) + + assert mpf(2) + inf == inf + assert 2 + inf == inf + assert mpf(2) - inf == -inf + assert 2 - inf == -inf + + assert inf > 3 + assert 3 < inf + assert 3 > -inf + assert -inf < 3 + assert inf > mpf(3) + assert mpf(3) < inf + assert mpf(3) > -inf + assert -inf < mpf(3) + + assert not (nan < 3) + assert not (nan > 3) + + assert isnan(inf * 0) + assert isnan(-inf * 0) + assert inf * 3 == inf + assert inf * -3 == -inf + assert -inf * 3 == -inf + assert -inf * -3 == inf + assert inf * inf == inf + assert -inf * -inf == inf + + assert isnan(nan / 3) + assert inf / -3 == -inf + assert inf / 3 == inf + assert 3 / inf == 0 + assert -3 / inf == 0 + assert 0 / inf == 0 + assert isnan(inf / inf) + assert isnan(inf / -inf) + assert isnan(inf / nan) + + assert mpf('inf') == mpf('+inf') == inf + assert mpf('-inf') == -inf + assert isnan(mpf('nan')) + + assert isinf(inf) + assert isinf(-inf) + assert not isinf(mpf(0)) + assert not isinf(nan) + +def test_special_powers(): + assert inf**3 == inf + assert isnan(inf**0) + assert inf**-3 == 0 + assert (-inf)**2 == inf + assert (-inf)**3 == -inf + assert isnan((-inf)**0) + assert (-inf)**-2 == 0 + assert (-inf)**-3 == 0 + assert isnan(nan**5) + assert isnan(nan**0) + +def test_functions_special(): + assert exp(inf) == inf + assert exp(-inf) == 0 + assert isnan(exp(nan)) + assert log(inf) == inf + assert isnan(sin(inf)) + assert isnan(sin(nan)) + assert atan(inf).ae(pi/2) + assert atan(-inf).ae(-pi/2) + assert isnan(sqrt(nan)) + assert sqrt(inf) == inf + +def test_convert_special(): + float_inf = 1e300 * 1e300 + float_ninf = -float_inf + float_nan = float_inf/float_ninf + assert mpf(3) * float_inf == inf + assert mpf(3) * float_ninf == -inf + assert isnan(mpf(3) * float_nan) + assert not (mpf(3) < float_nan) + assert not (mpf(3) > float_nan) + assert not (mpf(3) <= float_nan) + assert not (mpf(3) >= float_nan) + assert float(mpf('1e1000')) == float_inf + assert float(mpf('-1e1000')) == float_ninf + assert float(mpf('1e100000000000000000')) == float_inf + assert float(mpf('-1e100000000000000000')) == float_ninf + assert float(mpf('1e-100000000000000000')) == 0.0 + +def test_div_bug(): + assert isnan(nan/1) + assert isnan(nan/2) + assert inf/2 == inf + assert (-inf)/2 == -inf diff --git a/compiler/gdsMill/mpmath/tests/test_str.py b/compiler/gdsMill/mpmath/tests/test_str.py new file mode 100644 index 00000000..372082aa --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_str.py @@ -0,0 +1,15 @@ +from mpmath import nstr, matrix, inf + +def test_nstr(): + m = matrix([[0.75, 0.190940654, -0.0299195971], + [0.190940654, 0.65625, 0.205663228], + [-0.0299195971, 0.205663228, 0.64453125e-20]]) + assert nstr(m, 4, min_fixed=-inf) == \ + '''[ 0.75 0.1909 -0.02992] +[ 0.1909 0.6563 0.2057] +[-0.02992 0.2057 0.000000000000000000006445]''' + assert nstr(m, 4) == \ + '''[ 0.75 0.1909 -0.02992] +[ 0.1909 0.6563 0.2057] +[-0.02992 0.2057 6.445e-21]''' + diff --git a/compiler/gdsMill/mpmath/tests/test_summation.py b/compiler/gdsMill/mpmath/tests/test_summation.py new file mode 100644 index 00000000..ea063777 --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_summation.py @@ -0,0 +1,51 @@ +from mpmath import * + +def test_sumem(): + mp.dps = 15 + assert sumem(lambda k: 1/k**2.5, [50, 100]).ae(0.0012524505324784962) + assert sumem(lambda k: k**4 + 3*k + 1, [10, 100]).ae(2050333103) + +def test_nsum(): + mp.dps = 15 + assert nsum(lambda x: x**2, [1, 3]) == 14 + assert nsum(lambda k: 1/factorial(k), [0, inf]).ae(e) + assert nsum(lambda k: (-1)**(k+1) / k, [1, inf]).ae(log(2)) + assert nsum(lambda k: (-1)**(k+1) / k**2, [1, inf]).ae(pi**2 / 12) + assert nsum(lambda k: (-1)**k / log(k), [2, inf]).ae(0.9242998972229388) + assert nsum(lambda k: 1/k**2, [1, inf]).ae(pi**2 / 6) + assert nsum(lambda k: 2**k/fac(k), [0, inf]).ae(exp(2)) + assert nsum(lambda k: 1/k**2, [4, inf], method='e').ae(0.2838229557371153) + +def test_nprod(): + mp.dps = 15 + assert nprod(lambda k: exp(1/k**2), [1,inf], method='r').ae(exp(pi**2/6)) + assert nprod(lambda x: x**2, [1, 3]) == 36 + +def test_fsum(): + mp.dps = 15 + assert fsum([]) == 0 + assert fsum([-4]) == -4 + assert fsum([2,3]) == 5 + assert fsum([1e-100,1]) == 1 + assert fsum([1,1e-100]) == 1 + assert fsum([1e100,1]) == 1e100 + assert fsum([1,1e100]) == 1e100 + assert fsum([1e-100,0]) == 1e-100 + assert fsum([1e-100,1e100,1e-100]) == 1e100 + assert fsum([2,1+1j,1]) == 4+1j + assert fsum([1,mpi(2,3)]) == mpi(3,4) + assert fsum([2,inf,3]) == inf + assert fsum([2,-1], absolute=1) == 3 + assert fsum([2,-1], squared=1) == 5 + assert fsum([1,1+j], squared=1) == 1+2j + assert fsum([1,3+4j], absolute=1) == 6 + assert fsum([1,2+3j], absolute=1, squared=1) == 14 + assert isnan(fsum([inf,-inf])) + assert fsum([inf,-inf], absolute=1) == inf + assert fsum([inf,-inf], squared=1) == inf + assert fsum([inf,-inf], absolute=1, squared=1) == inf + +def test_fprod(): + mp.dps = 15 + assert fprod([]) == 1 + assert fprod([2,3]) == 6 diff --git a/compiler/gdsMill/mpmath/tests/test_trig.py b/compiler/gdsMill/mpmath/tests/test_trig.py new file mode 100644 index 00000000..34db2acb --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_trig.py @@ -0,0 +1,142 @@ +from mpmath import * +from mpmath.libmp import * + +def test_trig_misc_hard(): + mp.prec = 53 + # Worst-case input for an IEEE double, from a paper by Kahan + x = ldexp(6381956970095103,797) + assert cos(x) == mpf('-4.6871659242546277e-19') + assert sin(x) == 1 + + mp.prec = 150 + a = mpf(10**50) + mp.prec = 53 + assert sin(a).ae(-0.7896724934293100827) + assert cos(a).ae(-0.6135286082336635622) + + # Check relative accuracy close to x = zero + assert sin(1e-100) == 1e-100 # when rounding to nearest + assert sin(1e-6).ae(9.999999999998333e-007, rel_eps=2e-15, abs_eps=0) + assert sin(1e-6j).ae(1.0000000000001666e-006j, rel_eps=2e-15, abs_eps=0) + assert sin(-1e-6j).ae(-1.0000000000001666e-006j, rel_eps=2e-15, abs_eps=0) + assert cos(1e-100) == 1 + assert cos(1e-6).ae(0.9999999999995) + assert cos(-1e-6j).ae(1.0000000000005) + assert tan(1e-100) == 1e-100 + assert tan(1e-6).ae(1.0000000000003335e-006, rel_eps=2e-15, abs_eps=0) + assert tan(1e-6j).ae(9.9999999999966644e-007j, rel_eps=2e-15, abs_eps=0) + assert tan(-1e-6j).ae(-9.9999999999966644e-007j, rel_eps=2e-15, abs_eps=0) + +def test_trig_near_zero(): + mp.dps = 15 + + for r in [round_nearest, round_down, round_up, round_floor, round_ceiling]: + assert sin(0, rounding=r) == 0 + assert cos(0, rounding=r) == 1 + + a = mpf('1e-100') + b = mpf('-1e-100') + + assert sin(a, rounding=round_nearest) == a + assert sin(a, rounding=round_down) < a + assert sin(a, rounding=round_floor) < a + assert sin(a, rounding=round_up) >= a + assert sin(a, rounding=round_ceiling) >= a + assert sin(b, rounding=round_nearest) == b + assert sin(b, rounding=round_down) > b + assert sin(b, rounding=round_floor) <= b + assert sin(b, rounding=round_up) <= b + assert sin(b, rounding=round_ceiling) > b + + assert cos(a, rounding=round_nearest) == 1 + assert cos(a, rounding=round_down) < 1 + assert cos(a, rounding=round_floor) < 1 + assert cos(a, rounding=round_up) == 1 + assert cos(a, rounding=round_ceiling) == 1 + assert cos(b, rounding=round_nearest) == 1 + assert cos(b, rounding=round_down) < 1 + assert cos(b, rounding=round_floor) < 1 + assert cos(b, rounding=round_up) == 1 + assert cos(b, rounding=round_ceiling) == 1 + + +def test_trig_near_n_pi(): + + mp.dps = 15 + a = [n*pi for n in [1, 2, 6, 11, 100, 1001, 10000, 100001]] + mp.dps = 135 + a.append(10**100 * pi) + mp.dps = 15 + + assert sin(a[0]) == mpf('1.2246467991473531772e-16') + assert sin(a[1]) == mpf('-2.4492935982947063545e-16') + assert sin(a[2]) == mpf('-7.3478807948841190634e-16') + assert sin(a[3]) == mpf('4.8998251578625894243e-15') + assert sin(a[4]) == mpf('1.9643867237284719452e-15') + assert sin(a[5]) == mpf('-8.8632615209684813458e-15') + assert sin(a[6]) == mpf('-4.8568235395684898392e-13') + assert sin(a[7]) == mpf('3.9087342299491231029e-11') + assert sin(a[8]) == mpf('-1.369235466754566993528e-36') + + r = round_nearest + assert cos(a[0], rounding=r) == -1 + assert cos(a[1], rounding=r) == 1 + assert cos(a[2], rounding=r) == 1 + assert cos(a[3], rounding=r) == -1 + assert cos(a[4], rounding=r) == 1 + assert cos(a[5], rounding=r) == -1 + assert cos(a[6], rounding=r) == 1 + assert cos(a[7], rounding=r) == -1 + assert cos(a[8], rounding=r) == 1 + + r = round_up + assert cos(a[0], rounding=r) == -1 + assert cos(a[1], rounding=r) == 1 + assert cos(a[2], rounding=r) == 1 + assert cos(a[3], rounding=r) == -1 + assert cos(a[4], rounding=r) == 1 + assert cos(a[5], rounding=r) == -1 + assert cos(a[6], rounding=r) == 1 + assert cos(a[7], rounding=r) == -1 + assert cos(a[8], rounding=r) == 1 + + r = round_down + assert cos(a[0], rounding=r) > -1 + assert cos(a[1], rounding=r) < 1 + assert cos(a[2], rounding=r) < 1 + assert cos(a[3], rounding=r) > -1 + assert cos(a[4], rounding=r) < 1 + assert cos(a[5], rounding=r) > -1 + assert cos(a[6], rounding=r) < 1 + assert cos(a[7], rounding=r) > -1 + assert cos(a[8], rounding=r) < 1 + + r = round_floor + assert cos(a[0], rounding=r) == -1 + assert cos(a[1], rounding=r) < 1 + assert cos(a[2], rounding=r) < 1 + assert cos(a[3], rounding=r) == -1 + assert cos(a[4], rounding=r) < 1 + assert cos(a[5], rounding=r) == -1 + assert cos(a[6], rounding=r) < 1 + assert cos(a[7], rounding=r) == -1 + assert cos(a[8], rounding=r) < 1 + + r = round_ceiling + assert cos(a[0], rounding=r) > -1 + assert cos(a[1], rounding=r) == 1 + assert cos(a[2], rounding=r) == 1 + assert cos(a[3], rounding=r) > -1 + assert cos(a[4], rounding=r) == 1 + assert cos(a[5], rounding=r) > -1 + assert cos(a[6], rounding=r) == 1 + assert cos(a[7], rounding=r) > -1 + assert cos(a[8], rounding=r) == 1 + + mp.dps = 15 + +if __name__ == '__main__': + for f in globals().keys(): + if f.startswith("test_"): + print f + globals()[f]() diff --git a/compiler/gdsMill/mpmath/tests/test_visualization.py b/compiler/gdsMill/mpmath/tests/test_visualization.py new file mode 100644 index 00000000..953b493c --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/test_visualization.py @@ -0,0 +1,27 @@ +""" +Limited tests of the visualization module. Right now it just makes +sure that passing custom Axes works. + +""" + +from mpmath import mp, fp + +def test_axes(): + try: + import pylab + except ImportError: + print "\nSkipping test (pylab not available)\n" + return + fig = pylab.figure() + axes = fig.add_subplot(111) + for ctx in [mp, fp]: + ctx.plot(lambda x: x**2, [0, 3], axes=axes) + assert axes.get_xlabel() == 'x' + assert axes.get_ylabel() == 'f(x)' + + fig = pylab.figure() + axes = fig.add_subplot(111) + for ctx in [mp, fp]: + ctx.cplot(lambda z: z, [-2, 2], [-10, 10], axes=axes) + assert axes.get_xlabel() == 'Re(z)' + assert axes.get_ylabel() == 'Im(z)' diff --git a/compiler/gdsMill/mpmath/tests/torture.py b/compiler/gdsMill/mpmath/tests/torture.py new file mode 100644 index 00000000..4cde012b --- /dev/null +++ b/compiler/gdsMill/mpmath/tests/torture.py @@ -0,0 +1,229 @@ +""" +Torture tests for asymptotics and high precision evaluation of +special functions. + +(Other torture tests may also be placed here.) + +Running this file (gmpy and psyco recommended!) takes several CPU minutes. +With Python 2.6+, multiprocessing is used automatically to run tests +in parallel if many cores are available. (A single test may take between +a second and several minutes; possibly more.) + +The idea: + +* We evaluate functions at positive, negative, imaginary, 45- and 135-degree + complex values with magnitudes between 10^-20 to 10^20, at precisions between + 5 and 150 digits (we can go even higher for fast functions). + +* Comparing the result from two different precision levels provides + a strong consistency check (particularly for functions that use + different algorithms at different precision levels). + +* That the computation finishes at all (without failure), within reasonable + time, provides a check that evaluation works at all: that the code runs, + that it doesn't get stuck in an infinite loop, and that it doesn't use + some extremely slowly algorithm where it could use a faster one. + +TODO: + +* Speed up those functions that take long to finish! +* Generalize to test more cases; more options. +* Implement a timeout mechanism. +* Some functions are notably absent, including the following: + * inverse trigonometric functions (some become inaccurate for complex arguments) + * ci, si (not implemented properly for large complex arguments) + * zeta functions (need to modify test not to try too large imaginary values) + * and others... + +""" + + +import sys, os +from timeit import default_timer as clock + +if "-psyco" in sys.argv: + sys.argv.remove('-psyco') + import psyco + psyco.full() + +if "-nogmpy" in sys.argv: + sys.argv.remove('-nogmpy') + os.environ['MPMATH_NOGMPY'] = 'Y' + +filt = '' +if not sys.argv[-1].endswith(".py"): + filt = sys.argv[-1] + +from mpmath import * + +def test_asymp(f, maxdps=150, verbose=False, huge_range=False): + dps = [5,15,25,50,90,150,500,1500,5000,10000] + dps = [p for p in dps if p <= maxdps] + def check(x,y,p,inpt): + if abs(x-y)/abs(y) < workprec(20)(power)(10, -p+1): + return + print + print "Error!" + print "Input:", inpt + print "dps =", p + print "Result 1:", x + print "Result 2:", y + print "Absolute error:", abs(x-y) + print "Relative error:", abs(x-y)/abs(y) + raise AssertionError + exponents = range(-20,20) + if huge_range: + exponents += [-1000, -100, -50, 50, 100, 1000] + for n in exponents: + if verbose: + print ".", + mp.dps = 25 + xpos = mpf(10)**n / 1.1287 + xneg = -xpos + ximag = xpos*j + xcomplex1 = xpos*(1+j) + xcomplex2 = xpos*(-1+j) + for i in range(len(dps)): + if verbose: + print "Testing dps = %s" % dps[i] + mp.dps = dps[i] + new = f(xpos), f(xneg), f(ximag), f(xcomplex1), f(xcomplex2) + if i != 0: + p = dps[i-1] + check(prev[0], new[0], p, xpos) + check(prev[1], new[1], p, xneg) + check(prev[2], new[2], p, ximag) + check(prev[3], new[3], p, xcomplex1) + check(prev[4], new[4], p, xcomplex2) + prev = new + if verbose: + print + +a1, a2, a3, a4, a5 = 1.5, -2.25, 3.125, 4, 2 + +def test_bernoulli_huge(): + p, q = bernfrac(9000) + assert p % 10**10 == 9636701091 + assert q == 4091851784687571609141381951327092757255270 + mp.dps = 15 + assert str(bernoulli(10**100)) == '-2.58183325604736e+987675256497386331227838638980680030172857347883537824464410652557820800494271520411283004120790908623' + mp.dps = 50 + assert str(bernoulli(10**100)) == '-2.5818332560473632073252488656039475548106223822913e+987675256497386331227838638980680030172857347883537824464410652557820800494271520411283004120790908623' + mp.dps = 15 + +cases = """\ +test_bernoulli_huge() +test_asymp(lambda z: +pi, maxdps=10000) +test_asymp(lambda z: +e, maxdps=10000) +test_asymp(lambda z: +ln2, maxdps=10000) +test_asymp(lambda z: +ln10, maxdps=10000) +test_asymp(lambda z: +phi, maxdps=10000) +test_asymp(lambda z: +catalan, maxdps=5000) +test_asymp(lambda z: +euler, maxdps=5000) +test_asymp(lambda z: +glaisher, maxdps=1000) +test_asymp(lambda z: +khinchin, maxdps=1000) +test_asymp(lambda z: +twinprime, maxdps=150) +test_asymp(lambda z: stieltjes(2), maxdps=150) +test_asymp(lambda z: +mertens, maxdps=150) +test_asymp(lambda z: +apery, maxdps=5000) +test_asymp(sqrt, maxdps=10000, huge_range=True) +test_asymp(cbrt, maxdps=5000, huge_range=True) +test_asymp(lambda z: root(z,4), maxdps=5000, huge_range=True) +test_asymp(lambda z: root(z,-5), maxdps=5000, huge_range=True) +test_asymp(exp, maxdps=5000, huge_range=True) +test_asymp(expm1, maxdps=1500) +test_asymp(ln, maxdps=5000, huge_range=True) +test_asymp(cosh, maxdps=5000) +test_asymp(sinh, maxdps=5000) +test_asymp(tanh, maxdps=1500) +test_asymp(sin, maxdps=5000, huge_range=True) +test_asymp(cos, maxdps=5000, huge_range=True) +test_asymp(tan, maxdps=1500) +test_asymp(agm, maxdps=1500, huge_range=True) +test_asymp(ellipk, maxdps=1500) +test_asymp(ellipe, maxdps=1500) +test_asymp(lambertw, huge_range=True) +test_asymp(lambda z: lambertw(z,-1)) +test_asymp(lambda z: lambertw(z,1)) +test_asymp(lambda z: lambertw(z,4)) +test_asymp(gamma) +test_asymp(loggamma) # huge_range=True ? +test_asymp(ei) +test_asymp(e1) +test_asymp(li, huge_range=True) +test_asymp(ci) +test_asymp(si) +test_asymp(chi) +test_asymp(shi) +test_asymp(erf) +test_asymp(erfc) +test_asymp(erfi) +test_asymp(lambda z: besselj(2, z)) +test_asymp(lambda z: bessely(2, z)) +test_asymp(lambda z: besseli(2, z)) +test_asymp(lambda z: besselk(2, z)) +test_asymp(lambda z: besselj(-2.25, z)) +test_asymp(lambda z: bessely(-2.25, z)) +test_asymp(lambda z: besseli(-2.25, z)) +test_asymp(lambda z: besselk(-2.25, z)) +test_asymp(airyai) +test_asymp(airybi) +test_asymp(lambda z: hyp0f1(a1, z)) +test_asymp(lambda z: hyp1f1(a1, a2, z)) +test_asymp(lambda z: hyp1f2(a1, a2, a3, z)) +test_asymp(lambda z: hyp2f0(a1, a2, z)) +test_asymp(lambda z: hyperu(a1, a2, z)) +test_asymp(lambda z: hyp2f1(a1, a2, a3, z)) +test_asymp(lambda z: hyp2f2(a1, a2, a3, a4, z)) +test_asymp(lambda z: hyp2f3(a1, a2, a3, a4, a5, z)) +test_asymp(lambda z: coulombf(a1, a2, z)) +test_asymp(lambda z: coulombg(a1, a2, z)) +test_asymp(lambda z: polylog(2,z)) +test_asymp(lambda z: polylog(3,z)) +test_asymp(lambda z: polylog(-2,z)) +test_asymp(lambda z: expint(4, z)) +test_asymp(lambda z: expint(-4, z)) +test_asymp(lambda z: expint(2.25, z)) +test_asymp(lambda z: gammainc(2.5, z, 5)) +test_asymp(lambda z: gammainc(2.5, 5, z)) +test_asymp(lambda z: hermite(3, z)) +test_asymp(lambda z: hermite(2.5, z)) +test_asymp(lambda z: legendre(3, z)) +test_asymp(lambda z: legendre(4, z)) +test_asymp(lambda z: legendre(2.5, z)) +test_asymp(lambda z: legenp(a1, a2, z)) +test_asymp(lambda z: legenq(a1, a2, z), maxdps=90) # abnormally slow +test_asymp(lambda z: jtheta(1, z, 0.5)) +test_asymp(lambda z: jtheta(2, z, 0.5)) +test_asymp(lambda z: jtheta(3, z, 0.5)) +test_asymp(lambda z: jtheta(4, z, 0.5)) +test_asymp(lambda z: jtheta(1, z, 0.5, 1)) +test_asymp(lambda z: jtheta(2, z, 0.5, 1)) +test_asymp(lambda z: jtheta(3, z, 0.5, 1)) +test_asymp(lambda z: jtheta(4, z, 0.5, 1)) +test_asymp(barnesg, maxdps=90) +""" + +def testit(line): + if filt in line: + print line + t1 = clock() + exec line + t2 = clock() + elapsed = t2-t1 + print "Time:", elapsed, "for", line, "(OK)" + +if __name__ == '__main__': + try: + from multiprocessing import Pool + mapf = Pool(None).map + print "Running tests with multiprocessing" + except ImportError: + print "Not using multiprocessing" + mapf = map + t1 = clock() + tasks = cases.splitlines() + mapf(testit, tasks) + t2 = clock() + print "Cumulative wall time:", t2-t1 + diff --git a/compiler/gdsMill/mpmath/usertools.py b/compiler/gdsMill/mpmath/usertools.py new file mode 100644 index 00000000..71917f68 --- /dev/null +++ b/compiler/gdsMill/mpmath/usertools.py @@ -0,0 +1,91 @@ + +def monitor(f, input='print', output='print'): + """ + Returns a wrapped copy of *f* that monitors evaluation by calling + *input* with every input (*args*, *kwargs*) passed to *f* and + *output* with every value returned from *f*. The default action + (specify using the special string value ``'print'``) is to print + inputs and outputs to stdout, along with the total evaluation + count:: + + >>> from mpmath import * + >>> mp.dps = 5; mp.pretty = False + >>> diff(monitor(exp), 1) # diff will eval f(x-h) and f(x+h) + in 0 (mpf('0.99999999906867742538452148'),) {} + out 0 mpf('2.7182818259274480055282064') + in 1 (mpf('1.0000000009313225746154785'),) {} + out 1 mpf('2.7182818309906424675501024') + mpf('2.7182808') + + To disable either the input or the output handler, you may + pass *None* as argument. + + Custom input and output handlers may be used e.g. to store + results for later analysis:: + + >>> mp.dps = 15 + >>> input = [] + >>> output = [] + >>> findroot(monitor(sin, input.append, output.append), 3.0) + mpf('3.1415926535897932') + >>> len(input) # Count number of evaluations + 9 + >>> print input[3], output[3] + ((mpf('3.1415076583334066'),), {}) 8.49952562843408e-5 + >>> print input[4], output[4] + ((mpf('3.1415928201669122'),), {}) -1.66577118985331e-7 + + """ + if not input: + input = lambda v: None + elif input == 'print': + incount = [0] + def input(value): + args, kwargs = value + print "in %s %r %r" % (incount[0], args, kwargs) + incount[0] += 1 + if not output: + output = lambda v: None + elif output == 'print': + outcount = [0] + def output(value): + print "out %s %r" % (outcount[0], value) + outcount[0] += 1 + def f_monitored(*args, **kwargs): + input((args, kwargs)) + v = f(*args, **kwargs) + output(v) + return v + return f_monitored + +def timing(f, *args, **kwargs): + """ + Returns time elapsed for evaluating ``f()``. Optionally arguments + may be passed to time the execution of ``f(*args, **kwargs)``. + + If the first call is very quick, ``f`` is called + repeatedly and the best time is returned. + """ + once = kwargs.get('once') + if 'once' in kwargs: + del kwargs['once'] + if args or kwargs: + if len(args) == 1 and not kwargs: + arg = args[0] + g = lambda: f(arg) + else: + g = lambda: f(*args, **kwargs) + else: + g = f + from timeit import default_timer as clock + t1=clock(); v=g(); t2=clock(); t=t2-t1 + if t > 0.05 or once: + return t + for i in range(3): + t1=clock(); + # Evaluate multiple times because the timer function + # has a significant overhead + g();g();g();g();g();g();g();g();g();g() + t2=clock() + t=min(t,(t2-t1)/10) + return t diff --git a/compiler/gdsMill/mpmath/visualization.py b/compiler/gdsMill/mpmath/visualization.py new file mode 100644 index 00000000..8e56d0f1 --- /dev/null +++ b/compiler/gdsMill/mpmath/visualization.py @@ -0,0 +1,270 @@ +""" +Plotting (requires matplotlib) +""" + +from colorsys import hsv_to_rgb, hls_to_rgb + +class VisualizationMethods(object): + plot_ignore = (ValueError, ArithmeticError, ZeroDivisionError) + +def plot(ctx, f, xlim=[-5,5], ylim=None, points=200, file=None, dpi=None, + singularities=[], axes=None): + r""" + Shows a simple 2D plot of a function `f(x)` or list of functions + `[f_0(x), f_1(x), \ldots, f_n(x)]` over a given interval + specified by *xlim*. Some examples:: + + plot(lambda x: exp(x)*li(x), [1, 4]) + plot([cos, sin], [-4, 4]) + plot([fresnels, fresnelc], [-4, 4]) + plot([sqrt, cbrt], [-4, 4]) + plot(lambda t: zeta(0.5+t*j), [-20, 20]) + plot([floor, ceil, abs, sign], [-5, 5]) + + Points where the function raises a numerical exception or + returns an infinite value are removed from the graph. + Singularities can also be excluded explicitly + as follows (useful for removing erroneous vertical lines):: + + plot(cot, ylim=[-5, 5]) # bad + plot(cot, ylim=[-5, 5], singularities=[-pi, 0, pi]) # good + + For parts where the function assumes complex values, the + real part is plotted with dashes and the imaginary part + is plotted with dots. + + NOTE: This function requires matplotlib (pylab). + """ + if file: + axes = None + fig = None + if not axes: + import pylab + fig = pylab.figure() + axes = fig.add_subplot(111) + if not isinstance(f, (tuple, list)): + f = [f] + a, b = xlim + colors = ['b', 'r', 'g', 'm', 'k'] + for n, func in enumerate(f): + x = ctx.arange(a, b, (b-a)/float(points)) + segments = [] + segment = [] + in_complex = False + for i in xrange(len(x)): + try: + if i != 0: + for sing in singularities: + if x[i-1] <= sing and x[i] >= sing: + raise ValueError + v = func(x[i]) + if ctx.isnan(v) or abs(v) > 1e300: + raise ValueError + if hasattr(v, "imag") and v.imag: + re = float(v.real) + im = float(v.imag) + if not in_complex: + in_complex = True + segments.append(segment) + segment = [] + segment.append((float(x[i]), re, im)) + else: + if in_complex: + in_complex = False + segments.append(segment) + segment = [] + segment.append((float(x[i]), v)) + except ctx.plot_ignore: + if segment: + segments.append(segment) + segment = [] + if segment: + segments.append(segment) + for segment in segments: + x = [s[0] for s in segment] + y = [s[1] for s in segment] + if not x: + continue + c = colors[n % len(colors)] + if len(segment[0]) == 3: + z = [s[2] for s in segment] + axes.plot(x, y, '--'+c, linewidth=3) + axes.plot(x, z, ':'+c, linewidth=3) + else: + axes.plot(x, y, c, linewidth=3) + axes.set_xlim(xlim) + if ylim: + axes.set_ylim(ylim) + axes.set_xlabel('x') + axes.set_ylabel('f(x)') + axes.grid(True) + if fig: + if file: + pylab.savefig(file, dpi=dpi) + else: + pylab.show() + +def default_color_function(ctx, z): + if ctx.isinf(z): + return (1.0, 1.0, 1.0) + if ctx.isnan(z): + return (0.5, 0.5, 0.5) + pi = 3.1415926535898 + a = (float(ctx.arg(z)) + ctx.pi) / (2*ctx.pi) + a = (a + 0.5) % 1.0 + b = 1.0 - float(1/(1.0+abs(z)**0.3)) + return hls_to_rgb(a, b, 0.8) + +def cplot(ctx, f, re=[-5,5], im=[-5,5], points=2000, color=None, + verbose=False, file=None, dpi=None, axes=None): + """ + Plots the given complex-valued function *f* over a rectangular part + of the complex plane specified by the pairs of intervals *re* and *im*. + For example:: + + cplot(lambda z: z, [-2, 2], [-10, 10]) + cplot(exp) + cplot(zeta, [0, 1], [0, 50]) + + By default, the complex argument (phase) is shown as color (hue) and + the magnitude is show as brightness. You can also supply a + custom color function (*color*). This function should take a + complex number as input and return an RGB 3-tuple containing + floats in the range 0.0-1.0. + + To obtain a sharp image, the number of points may need to be + increased to 100,000 or thereabout. Since evaluating the + function that many times is likely to be slow, the 'verbose' + option is useful to display progress. + + NOTE: This function requires matplotlib (pylab). + """ + if color is None: + color = ctx.default_color_function + import pylab + if file: + axes = None + fig = None + if not axes: + fig = pylab.figure() + axes = fig.add_subplot(111) + rea, reb = re + ima, imb = im + dre = reb - rea + dim = imb - ima + M = int(ctx.sqrt(points*dre/dim)+1) + N = int(ctx.sqrt(points*dim/dre)+1) + x = pylab.linspace(rea, reb, M) + y = pylab.linspace(ima, imb, N) + # Note: we have to be careful to get the right rotation. + # Test with these plots: + # cplot(lambda z: z if z.real < 0 else 0) + # cplot(lambda z: z if z.imag < 0 else 0) + w = pylab.zeros((N, M, 3)) + for n in xrange(N): + for m in xrange(M): + z = ctx.mpc(x[m], y[n]) + try: + v = color(f(z)) + except ctx.plot_ignore: + v = (0.5, 0.5, 0.5) + w[n,m] = v + if verbose: + print n, "of", N + axes.imshow(w, extent=(rea, reb, ima, imb), origin='lower') + axes.set_xlabel('Re(z)') + axes.set_ylabel('Im(z)') + if fig: + if file: + pylab.savefig(file, dpi=dpi) + else: + pylab.show() + +def splot(ctx, f, u=[-5,5], v=[-5,5], points=100, keep_aspect=True, \ + wireframe=False, file=None, dpi=None, axes=None): + """ + Plots the surface defined by `f`. + + If `f` returns a single component, then this plots the surface + defined by `z = f(x,y)` over the rectangular domain with + `x = u` and `y = v`. + + If `f` returns three components, then this plots the parametric + surface `x, y, z = f(u,v)` over the pairs of intervals `u` and `v`. + + For example, to plot a simple function:: + + >>> from mpmath import * + >>> f = lambda x, y: sin(x+y)*cos(y) + >>> splot(f, [-pi,pi], [-pi,pi]) # doctest: +SKIP + + Plotting a donut:: + + >>> r, R = 1, 2.5 + >>> f = lambda u, v: [r*cos(u), (R+r*sin(u))*cos(v), (R+r*sin(u))*sin(v)] + >>> splot(f, [0, 2*pi], [0, 2*pi]) # doctest: +SKIP + + NOTE: This function requires matplotlib (pylab) 0.98.5.3 or higher. + """ + import pylab + import mpl_toolkits.mplot3d as mplot3d + if file: + axes = None + fig = None + if not axes: + fig = pylab.figure() + axes = mplot3d.axes3d.Axes3D(fig) + ua, ub = u + va, vb = v + du = ub - ua + dv = vb - va + if not isinstance(points, (list, tuple)): + points = [points, points] + M, N = points + u = pylab.linspace(ua, ub, M) + v = pylab.linspace(va, vb, N) + x, y, z = [pylab.zeros((M, N)) for i in xrange(3)] + xab, yab, zab = [[0, 0] for i in xrange(3)] + for n in xrange(N): + for m in xrange(M): + fdata = f(ctx.convert(u[m]), ctx.convert(v[n])) + try: + x[m,n], y[m,n], z[m,n] = fdata + except TypeError: + x[m,n], y[m,n], z[m,n] = u[m], v[n], fdata + for c, cab in [(x[m,n], xab), (y[m,n], yab), (z[m,n], zab)]: + if c < cab[0]: + cab[0] = c + if c > cab[1]: + cab[1] = c + if wireframe: + axes.plot_wireframe(x, y, z, rstride=4, cstride=4) + else: + axes.plot_surface(x, y, z, rstride=4, cstride=4) + axes.set_xlabel('x') + axes.set_ylabel('y') + axes.set_zlabel('z') + if keep_aspect: + dx, dy, dz = [cab[1] - cab[0] for cab in [xab, yab, zab]] + maxd = max(dx, dy, dz) + if dx < maxd: + delta = maxd - dx + axes.set_xlim3d(xab[0] - delta / 2.0, xab[1] + delta / 2.0) + if dy < maxd: + delta = maxd - dy + axes.set_ylim3d(yab[0] - delta / 2.0, yab[1] + delta / 2.0) + if dz < maxd: + delta = maxd - dz + axes.set_zlim3d(zab[0] - delta / 2.0, zab[1] + delta / 2.0) + if fig: + if file: + pylab.savefig(file, dpi=dpi) + else: + pylab.show() + + +VisualizationMethods.plot = plot +VisualizationMethods.default_color_function = default_color_function +VisualizationMethods.cplot = cplot +VisualizationMethods.splot = splot + diff --git a/compiler/gdsMill/pyx/__init__.py b/compiler/gdsMill/pyx/__init__.py new file mode 100644 index 00000000..6bf2b886 --- /dev/null +++ b/compiler/gdsMill/pyx/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2005 Jörg Lehmann +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +"""Python graphics package + +PyX is a Python package for the creation of PostScript and PDF files. It +combines an abstraction of the PostScript drawing model with a TeX/LaTeX +interface. Complex tasks like 2d and 3d plots in publication-ready quality are +built out of these primitives. +""" + +import version +__version__ = version.version + +__all__ = ["attr", "box", "bitmap", "canvas", "color", "connector", "deco", "deformer", "document", + "epsfile", "graph", "mesh", "path", "pattern", "style", "trafo", "text", "unit"] + + +# automatically import main modules into pyx namespace +for module in __all__: + __import__(module, globals(), locals(), []) diff --git a/compiler/gdsMill/pyx/attr.py b/compiler/gdsMill/pyx/attr.py new file mode 100644 index 00000000..778b4720 --- /dev/null +++ b/compiler/gdsMill/pyx/attr.py @@ -0,0 +1,270 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2003-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2003-2004 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# check for an isinstance which accepts both a class and a sequence of classes +# as second argument and emulate this behaviour if necessary +try: + isinstance(1, (int, float)) +except TypeError: + # workaround for Python 2.1 + _isinstance = isinstance + def isinstance(instance, clsarg): + import types + if _isinstance(clsarg, types.ClassType): + return _isinstance(instance, clsarg) + for cls in clsarg: + if _isinstance(instance, cls): + return 1 + return 0 + +# +# some helper functions for the attribute handling +# + +def mergeattrs(attrs): + """perform merging of the attribute list attrs as defined by the + merge methods of the attributes""" + newattrs = [] + for a in attrs: + # XXX Do we really need this test? + if isinstance(a, attr): + newattrs = a.merge(newattrs) + else: + raise TypeError("only instances of class attr.attr are allowed") + return newattrs + + +def getattrs(attrs, getclasses): + """return all attributes in the attribute list attrs, which are + instances of one of the classes in getclasses""" + return [attr for attr in attrs if isinstance(attr, tuple(getclasses))] + + +def checkattrs(attrs, allowedclasses): + """check whether only attributes which are instances of classes in + allowedclasses are present in the attribute list attrs; if not it + raises a TypeError""" + if len(attrs) != len(getattrs(attrs, allowedclasses)): + for attr1, attr2 in zip(attrs, getattrs(attrs, allowedclasses)): + if attr1 is not attr2: + raise TypeError("instance %r not allowed" % attr1) + else: + raise TypeError("instance %r not allowed" % attrs[len(getattrs(attrs, allowedclasses))]) + +# +# attr class and simple descendants +# + +class attr: + + """ attr is the base class of all attributes, i.e., colors, decorators, + styles, text attributes and trafos""" + + def merge(self, attrs): + """merge self into list of attrs + + self may either be appended to attrs or inserted at a proper position + immediately before a dependent attribute. Attributes of the same type + should be removed, if redundant. Note that it is safe to modify + attrs.""" + + attrs.append(self) + return attrs + + +class exclusiveattr(attr): + + """an attribute which swallows all but the last of the same type (specified + by the exlusiveclass argument to the constructor) in an attribute list""" + + def __init__(self, exclusiveclass): + self.exclusiveclass = exclusiveclass + + def merge(self, attrs): + attrs = [attr for attr in attrs if not isinstance(attr, self.exclusiveclass)] + attrs.append(self) + return attrs + + +class sortbeforeattr(attr): + + """an attribute which places itself previous to all attributes given + in the beforetheclasses argument to the constructor""" + + def __init__(self, beforetheclasses): + self.beforetheclasses = tuple(beforetheclasses) + + def merge(self, attrs): + first = 1 + result = [] + for attr in attrs: + if first and isinstance(attr, self.beforetheclasses): + result.append(self) + first = 0 + result.append(attr) + if first: + result.append(self) + return result + + +class sortbeforeexclusiveattr(attr): + + """an attribute which swallows all but the last of the same type (specified + by the exlusiveclass argument to the constructor) in an attribute list and + places itself previous to all attributes given in the beforetheclasses + argument to the constructor""" + + def __init__(self, exclusiveclass, beforetheclasses): + self.exclusiveclass = exclusiveclass + self.beforetheclasses = tuple(beforetheclasses) + + def merge(self, attrs): + first = 1 + result = [] + for attr in attrs: + if first and isinstance(attr, self.beforetheclasses): + result.append(self) + first = 0 + if not isinstance(attr, self.exclusiveclass): + result.append(attr) + if first: + result.append(self) + return result + + +class clearclass(attr): + + """a special attribute which allows to remove all predecessing attributes of + the same type in an attribute list""" + + def __init__(self, clearclass): + self.clearclass = clearclass + + def merge(self, attrs): + return [attr for attr in attrs if not isinstance(attr, self.clearclass)] + + +class _clear(attr): + + """a special attribute which removes all predecessing attributes + in an attribute list""" + + def merge(self, attrs): + return [] + +# we define the attribute "clear", an instance of "_clear", +# which can be used to remove all predecessing attributes +# in an attribute list + +clear = _clear() + +# +# changeable attrs +# + +def selectattrs(attrs, index, total): + """performs select calls for all changeable attributes and + returns the resulting attribute list + - attrs should be a list containing attributes and changeable + attributes + - index should be an unsigned integer + - total should be a positive number + - valid sections fullfill 0<=index +# Copyright (C) 2002-2004 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import math +import unit + +# +# classes representing bounding boxes +# + +class bbox_pt: + + """class for bounding boxes + + This variant requires points in the constructor, and is used for internal + purposes. + + A bbox for which llx_pt is None represents an empty bbox, i.e., one containing + no points. + """ + + def __init__(self, llx_pt, lly_pt, urx_pt, ury_pt): + self.llx_pt = llx_pt + self.lly_pt = lly_pt + self.urx_pt = urx_pt + self.ury_pt = ury_pt + + def __nonzero__(self): + return self.llx_pt is not None + + def __add__(self, other): + """join two bboxes""" + if self.llx_pt is not None: + if other.llx_pt is not None: + return bbox_pt(min(self.llx_pt, other.llx_pt), min(self.lly_pt, other.lly_pt), + max(self.urx_pt, other.urx_pt), max(self.ury_pt, other.ury_pt)) + else: + return bbox_pt(self.llx_pt, self.lly_pt, self.urx_pt, self.ury_pt) + else: + return bbox_pt(other.llx_pt, other.lly_pt, other.urx_pt, other.ury_pt) + + def __iadd__(self, other): + """join two bboxes inplace""" + if self.llx_pt is not None: + if other.llx_pt is not None: + self.llx_pt = min(self.llx_pt, other.llx_pt) + self.lly_pt = min(self.lly_pt, other.lly_pt) + self.urx_pt = max(self.urx_pt, other.urx_pt) + self.ury_pt = max(self.ury_pt, other.ury_pt) + else: + self.llx_pt = other.llx_pt + self.lly_pt = other.lly_pt + self.urx_pt = other.urx_pt + self.ury_pt = other.ury_pt + return self + + def __mul__(self, other): + """return intersection of two bboxes""" + if self.llx_pt is not None and other.llx_pt is not None: + return bbox_pt(max(self.llx_pt, other.llx_pt), max(self.lly_pt, other.lly_pt), + min(self.urx_pt, other.urx_pt), min(self.ury_pt, other.ury_pt)) + else: + return empty() + + def __imul__(self, other): + """intersect two bboxes in place""" + if self.llx_pt is not None and other.llx_pt is not None: + self.llx_pt = max(self.llx_pt, other.llx_pt) + self.lly_pt = max(self.lly_pt, other.lly_pt) + self.urx_pt = min(self.urx_pt, other.urx_pt) + self.ury_pt = min(self.ury_pt, other.ury_pt) + elif other.llx_pt is None: + self.llx_pt = None + return self + + def copy(self): + return bbox_pt(self.llx_pt, self.lly_pt, self.urx_pt, self.ury_pt) + + def set(self, other): + self.llx_pt = other.llx_pt + self.lly_pt = other.lly_pt + self.urx_pt = other.urx_pt + self.ury_pt = other.ury_pt + + def lowrestuple_pt(self): + if self.llx_pt is None: + raise ValueError("Cannot return low-res tuple for empty bbox") + return (math.floor(self.llx_pt), math.floor(self.lly_pt), + math.ceil(self.urx_pt), math.ceil(self.ury_pt)) + + def highrestuple_pt(self): + if self.llx_pt is None: + raise ValueError("Cannot return high-res tuple for empty bbox") + return (self.llx_pt, self.lly_pt, self.urx_pt, self.ury_pt) + + def intersects(self, other): + """check, if two bboxes intersect eachother""" + if self.llx_pt is None or other.llx_pt is None: + return 0 + else: + return not (self.llx_pt > other.urx_pt or + self.lly_pt > other.ury_pt or + self.urx_pt < other.llx_pt or + self.ury_pt < other.lly_pt) + + def includepoint_pt(self, x_pt, y_pt): + if self.llx_pt is None: + self.llx_pt = self.urx_pt = x_pt + self.ury_pt = self.ury_pt = y_pt + else: + self.llx_pt = min(self.llx_pt, x_pt) + self.lly_pt = min(self.lly_pt, y_pt) + self.urx_pt = max(self.urx_pt, x_pt) + self.ury_pt = max(self.ury_pt, y_pt) + + def transform(self, trafo): + """transform bbox in place by trafo""" + if self.llx_pt is None: + return + # we have to transform all four corner points of the bbox + llx_pt, lly_pt = trafo.apply_pt(self.llx_pt, self.lly_pt) + lrx_pt, lry_pt = trafo.apply_pt(self.urx_pt, self.lly_pt) + urx_pt, ury_pt = trafo.apply_pt(self.urx_pt, self.ury_pt) + ulx_pt, uly_pt = trafo.apply_pt(self.llx_pt, self.ury_pt) + + # Now, by sorting, we obtain the lower left and upper right corner + # of the new bounding box. + self.llx_pt = min(llx_pt, lrx_pt, urx_pt, ulx_pt) + self.lly_pt = min(lly_pt, lry_pt, ury_pt, uly_pt) + self.urx_pt = max(llx_pt, lrx_pt, urx_pt, ulx_pt) + self.ury_pt = max(lly_pt, lry_pt, ury_pt, uly_pt) + + def transformed(self, trafo): + """return bbox transformed by trafo""" + if self.llx_pt is None: + return empty() + # we have to transform all four corner points of the bbox + llx_pt, lly_pt = trafo.apply_pt(self.llx_pt, self.lly_pt) + lrx_pt, lry_pt = trafo.apply_pt(self.urx_pt, self.lly_pt) + urx_pt, ury_pt = trafo.apply_pt(self.urx_pt, self.ury_pt) + ulx_pt, uly_pt = trafo.apply_pt(self.llx_pt, self.ury_pt) + + # Now, by sorting, we obtain the lower left and upper right corner + # of the new bounding box. + return bbox_pt(min(llx_pt, lrx_pt, urx_pt, ulx_pt), min(lly_pt, lry_pt, ury_pt, uly_pt), + max(llx_pt, lrx_pt, urx_pt, ulx_pt), max(lly_pt, lry_pt, ury_pt, uly_pt)) + + def enlarge_pt(self, all_pt=0, bottom_pt=None, left_pt=None, top_pt=None, right_pt=None): + """enlarge bbox in place by the given amounts in pts + + all is used, if bottom, left, top and/or right are not given. + + """ + if self.llx_pt is None: + return + if bottom_pt is None: + bottom_pt = all_pt + if left_pt is None: + left_pt = all_pt + if top_pt is None: + top_pt = all_pt + if right_pt is None: + right_pt = all_pt + self.llx_pt -= left_pt + self.lly_pt -= bottom_pt + self.urx_pt += right_pt + self.ury_pt += top_pt + + def enlarged_pt(self, all_pt=0, bottom_pt=None, left_pt=None, top_pt=None, right_pt=None): + """return bbox enlarged by the given amounts in pts + + all is used, if bottom, left, top and/or right are not given. + + """ + if self.llx_pt is None: + return empty() + if bottom_pt is None: + bottom_pt = all_pt + if left_pt is None: + left_pt = all_pt + if top_pt is None: + top_pt = all_pt + if right_pt is None: + right_pt = all_pt + return bbox_pt(self.llx_pt-left_pt, self.lly_pt-bottom_pt, self.urx_pt+right_pt, self.ury_pt+top_pt) + + def enlarge(self, all=0, bottom=None, left=None, top=None, right=None): + """enlarge bbox in place + + all is used, if bottom, left, top and/or right are not given. + + """ + if self.llx_pt is None: + return + bottom_pt = left_pt = top_pt = right_pt = unit.topt(all) + if bottom is not None: + bottom_pt = unit.topt(bottom) + if left is not None: + left_pt = unit.topt(left) + if top is not None: + top_pt = unit.topt(top) + if right is not None: + right_pt = unit.topt(right) + self.llx_pt -= left_pt + self.lly_pt -= bottom_pt + self.urx_pt += right_pt + self.ury_pt += top_pt + + def enlarged(self, all=0, bottom=None, left=None, top=None, right=None): + """return bbox enlarged + + all is used, if bottom, left, top and/or right are not given. + + """ + if self.llx_pt is None: + return empty() + bottom_pt = left_pt = top_pt = right_pt = unit.topt(all) + if bottom is not None: + bottom_pt = unit.topt(bottom) + if left is not None: + left_pt = unit.topt(left) + if top is not None: + top_pt = unit.topt(top) + if right is not None: + right_pt = unit.topt(right) + return bbox_pt(self.llx_pt-left_pt, self.lly_pt-bottom_pt, self.urx_pt+right_pt, self.ury_pt+top_pt) + + def rect(self): + """return rectangle corresponding to bbox""" + if self.llx_pt is None: + raise ValueError("Cannot return path for empty bbox") + import path + return path.rect_pt(self.llx_pt, self.lly_pt, self.urx_pt-self.llx_pt, self.ury_pt-self.lly_pt) + + path = rect + + def height_pt(self): + """return height of bbox in pts""" + if self.llx_pt is None: + raise ValueError("Cannot return heigth of empty bbox") + return self.ury_pt-self.lly_pt + + def width_pt(self): + """return width of bbox in pts""" + if self.llx_pt is None: + raise ValueError("Cannot return width of empty bbox") + return self.urx_pt-self.llx_pt + + def top_pt(self): + """return top coordinate of bbox in pts""" + if self.llx_pt is None: + raise ValueError("Cannot return top coordinate of empty bbox") + return self.ury_pt + + def bottom_pt(self): + """return bottom coordinate of bbox in pts""" + if self.llx_pt is None: + raise ValueError("Cannot return bottom coordinate of empty bbox") + return self.lly_pt + + def left_pt(self): + """return left coordinate of bbox in pts""" + if self.llx_pt is None: + raise ValueError("Cannot return left coordinate of empty bbox") + return self.llx_pt + + def right_pt(self): + """return right coordinate of bbox in pts""" + if self.llx_pt is None: + raise ValueError("Cannot return right coordinate of empty bbox") + return self.urx_pt + + def center_pt(self): + """return coordinates of the center of the bbox in pts""" + if self.llx_pt is None: + raise ValueError("Cannot return center coordinates of empty bbox") + return 0.5 * (self.llx_pt+self.urx_pt), 0.5 * (self.lly_pt+self.ury_pt) + + def height(self): + """return height of bbox""" + return self.height_pt() * unit.t_pt + + def width(self): + """return width of bbox""" + return self.width_pt() * unit.t_pt + + def top(self): + """return top coordinate of bbox""" + return self.ury_pt * unit.t_pt + + def bottom(self): + """return bottom coordinate of bbox""" + return self.lly_pt * unit.t_pt + + def left(self): + """return left coordinate of bbox""" + return self.llx_pt * unit.t_pt + + def right(self): + """return right coordinate of bbox""" + return self.urx_pt * unit.t_pt + + def center(self): + """return coordinates of the center of the bbox""" + centerx_pt, centery_pt = self.center_pt() + return centerx_pt * unit.t_pt, centery_pt * unit.t_pt + + +class bbox(bbox_pt): + + """class for bounding boxes""" + + def __init__(self, llx_pt, lly_pt, urx_pt, ury_pt): + llx_pt = unit.topt(llx_pt) + lly_pt = unit.topt(lly_pt) + urx_pt = unit.topt(urx_pt) + ury_pt = unit.topt(ury_pt) + bbox_pt.__init__(self, llx_pt, lly_pt, urx_pt, ury_pt) + + +class empty(bbox_pt): + + """empty bounding box, i.e., one containing no point""" + def __init__(self): + bbox_pt.__init__(self, None, None, None, None) diff --git a/compiler/gdsMill/pyx/bitmap.py b/compiler/gdsMill/pyx/bitmap.py new file mode 100644 index 00000000..0ae8d9b2 --- /dev/null +++ b/compiler/gdsMill/pyx/bitmap.py @@ -0,0 +1,452 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2004-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import struct, warnings, binascii +try: + import zlib + haszlib = 1 +except: + haszlib = 0 + +import bbox, canvas, pswriter, pdfwriter, trafo, unit + +def ascii85lines(datalen): + if datalen < 4: + return 1 + return (datalen + 56)/60 + +def ascii85stream(file, data): + """Encodes the string data in ASCII85 and writes it to + the stream file. The number of lines written to the stream + is known just from the length of the data by means of the + ascii85lines function. Note that the tailing newline character + of the last line is not added by this function, but it is taken + into account in the ascii85lines function.""" + i = 3 # go on smoothly in case of data length equals zero + l = 0 + l = [None, None, None, None] + for i in range(len(data)): + c = data[i] + l[i%4] = ord(c) + if i%4 == 3: + if i%60 == 3 and i != 3: + file.write("\n") + if l: + # instead of + # l[3], c5 = divmod(256*256*256*l[0]+256*256*l[1]+256*l[2]+l[3], 85) + # l[2], c4 = divmod(l[3], 85) + # we have to avoid number > 2**31 by + l[3], c5 = divmod(256*256*l[0]+256*256*l[1]+256*l[2]+l[3], 85) + l[2], c4 = divmod(256*256*3*l[0]+l[3], 85) + l[1], c3 = divmod(l[2], 85) + c1 , c2 = divmod(l[1], 85) + file.write(struct.pack('BBBBB', c1+33, c2+33, c3+33, c4+33, c5+33)) + else: + file.write("z") + if i%4 != 3: + for j in range((i%4) + 1, 4): + l[j] = 0 + l[3], c5 = divmod(256*256*l[0]+256*256*l[1]+256*l[2]+l[3], 85) + l[2], c4 = divmod(256*256*3*l[0]+l[3], 85) + l[1], c3 = divmod(l[2], 85) + c1 , c2 = divmod(l[1], 85) + file.write(struct.pack('BBBB', c1+33, c2+33, c3+33, c4+33)[:(i%4)+2]) + +_asciihexlinelength = 64 +def asciihexlines(datalen): + return (datalen*2 + _asciihexlinelength - 1) / _asciihexlinelength + +def asciihexstream(file, data): + hexdata = binascii.b2a_hex(data) + for i in range((len(hexdata)-1)/_asciihexlinelength + 1): + file.write(hexdata[i*_asciihexlinelength: i*_asciihexlinelength+_asciihexlinelength]) + file.write("\n") + + +class image: + + def __init__(self, width, height, mode, data, compressed=None): + if width <= 0 or height <= 0: + raise ValueError("valid image size") + if mode not in ["L", "RGB", "CMYK"]: + raise ValueError("invalid mode") + if compressed is None and len(mode)*width*height != len(data): + raise ValueError("wrong size of uncompressed data") + self.size = width, height + self.mode = mode + self.data = data + self.compressed = compressed + + def tostring(self, *args): + if len(args): + raise RuntimeError("encoding not supported in this implementation") + return self.data + + def convert(self, model): + raise RuntimeError("color model conversion not supported in this implementation") + + +class jpegimage(image): + + def __init__(self, file): + try: + data = file.read() + except: + data = open(file, "rb").read() + pos = 0 + nestinglevel = 0 + try: + while 1: + if data[pos] == "\377" and data[pos+1] not in ["\0", "\377"]: + # print "marker: 0x%02x \\%03o" % (ord(data[pos+1]), ord(data[pos+1])) + if data[pos+1] == "\330": + if not nestinglevel: + begin = pos + nestinglevel += 1 + elif not nestinglevel: + raise ValueError("begin marker expected") + elif data[pos+1] == "\331": + nestinglevel -= 1 + if not nestinglevel: + end = pos + 2 + break + elif data[pos+1] in ["\300", "\301"]: + l, bits, height, width, components = struct.unpack(">HBHHB", data[pos+2:pos+10]) + if bits != 8: + raise ValueError("implementation limited to 8 bit per component only") + try: + mode = {1: "L", 3: "RGB", 4: "CMYK"}[components] + except KeyError: + raise ValueError("invalid number of components") + pos += l+1 + elif data[pos+1] == "\340": + l, id, major, minor, dpikind, xdpi, ydpi = struct.unpack(">H5sBBBHH", data[pos+2:pos+16]) + if dpikind == 1: + self.info = {"dpi": (xdpi, ydpi)} + elif dpikind == 2: + self.info = {"dpi": (xdpi*2.54, ydpi*2.45)} + # else do not provide dpi information + pos += l+1 + pos += 1 + except IndexError: + raise ValueError("end marker expected") + image.__init__(self, width, height, mode, data[begin:end], compressed="DCT") + + +class PSimagedata(pswriter.PSresource): + + def __init__(self, name, data, singlestring, maxstrlen): + pswriter.PSresource.__init__(self, "imagedata", name) + self.data = data + self.singlestring = singlestring + self.maxstrlen = maxstrlen + + def output(self, file, writer, registry): + file.write("%%%%BeginRessource: %s\n" % self.id) + if self.singlestring: + file.write("%%%%BeginData: %i ASCII Lines\n" + "<~" % ascii85lines(len(self.data))) + ascii85stream(file, self.data) + file.write("~>\n" + "%%EndData\n") + else: + datalen = len(self.data) + tailpos = datalen - datalen % self.maxstrlen + file.write("%%%%BeginData: %i ASCII Lines\n" % + ((tailpos/self.maxstrlen) * ascii85lines(self.maxstrlen) + + ascii85lines(datalen-tailpos))) + file.write("[ ") + for i in xrange(0, tailpos, self.maxstrlen): + file.write("<~") + ascii85stream(file, self.data[i: i+self.maxstrlen]) + file.write("~>\n") + if datalen != tailpos: + file.write("<~") + ascii85stream(file, self.data[tailpos:]) + file.write("~>") + file.write("]\n" + "%%EndData\n") + file.write("/%s exch def\n" % self.id) + file.write("%%EndRessource\n") + + +class PDFimagepalettedata(pdfwriter.PDFobject): + + def __init__(self, name, data): + pdfwriter.PDFobject.__init__(self, "imagepalettedata", name) + self.data = data + + def write(self, file, writer, registry): + file.write("<<\n" + "/Length %d\n" % len(self.data)) + file.write(">>\n" + "stream\n") + file.write(self.data) + file.write("\n" + "endstream\n") + + +class PDFimage(pdfwriter.PDFobject): + + def __init__(self, name, width, height, palettecolorspace, palettedata, colorspace, + bitspercomponent, compressmode, data, registry): + if palettedata is not None: + procset = "ImageI" + elif colorspace == "/DeviceGray": + procset = "ImageB" + else: + procset = "ImageC" + pdfwriter.PDFobject.__init__(self, "image", name) + registry.addresource("XObject", name, self, procset=procset) + if palettedata is not None: + # acrobat wants a palette to be an object + self.PDFpalettedata = PDFimagepalettedata(name, palettedata) + registry.add(self.PDFpalettedata) + self.name = name + self.width = width + self.height = height + self.palettecolorspace = palettecolorspace + self.palettedata = palettedata + self.colorspace = colorspace + self.bitspercomponent = bitspercomponent + self.compressmode = compressmode + self.data = data + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /XObject\n" + "/Subtype /Image\n" + "/Width %d\n" % self.width) + file.write("/Height %d\n" % self.height) + if self.palettedata is not None: + file.write("/ColorSpace [ /Indexed %s %i\n" % (self.palettecolorspace, len(self.palettedata)/3-1)) + file.write("%d 0 R\n" % registry.getrefno(self.PDFpalettedata)) + file.write("]\n") + else: + file.write("/ColorSpace %s\n" % self.colorspace) + file.write("/BitsPerComponent %d\n" % self.bitspercomponent) + file.write("/Length %d\n" % len(self.data)) + if self.compressmode: + file.write("/Filter /%sDecode\n" % self.compressmode) + file.write(">>\n" + "stream\n") + file.write(self.data) + file.write("\n" + "endstream\n") + + +class bitmap(canvas.canvasitem): + + def __init__(self, xpos, ypos, image, width=None, height=None, ratio=None, + PSstoreimage=0, PSmaxstrlen=4093, PSbinexpand=1, + compressmode="Flate", flatecompresslevel=6, + dctquality=75, dctoptimize=0, dctprogression=0): + # keep a copy of the image instance to ensure different id's + self.image = image + + self.xpos = xpos + self.ypos = ypos + self.imagewidth, self.imageheight = image.size + self.PSstoreimage = PSstoreimage + self.PSmaxstrlen = PSmaxstrlen + self.PSbinexpand = PSbinexpand + + if width is not None or height is not None: + self.width = width + self.height = height + if self.width is None: + if ratio is None: + self.width = self.height * self.imagewidth / float(self.imageheight) + else: + self.width = ratio * self.height + elif self.height is None: + if ratio is None: + self.height = self.width * self.imageheight / float(self.imagewidth) + else: + self.height = (1.0/ratio) * self.width + elif ratio is not None: + raise ValueError("can't specify a ratio when setting width and height") + else: + if ratio is not None: + raise ValueError("must specify width or height to set a ratio") + widthdpi, heightdpi = image.info["dpi"] # fails when no dpi information available + self.width = self.imagewidth / float(widthdpi) * unit.t_inch + self.height = self.imageheight / float(heightdpi) * unit.t_inch + + self.xpos_pt = unit.topt(self.xpos) + self.ypos_pt = unit.topt(self.ypos) + self.width_pt = unit.topt(self.width) + self.height_pt = unit.topt(self.height) + + # create decode and colorspace + self.colorspace = self.palettecolorspace = self.palettedata = None + if image.mode == "P": + palettemode, self.palettedata = image.palette.getdata() + self.decode = "[0 255]" + try: + self.palettecolorspace = {"L": "/DeviceGray", + "RGB": "/DeviceRGB", + "CMYK": "/DeviceCMYK"}[palettemode] + except KeyError: + warnings.warn("image with unknown palette mode '%s' converted to rgb image" % palettemode) + image = image.convert("RGB") + self.decode = "[0 1 0 1 0 1]" + self.palettedata = None + self.colorspace = "/DeviceRGB" + elif len(image.mode) == 1: + if image.mode != "L": + image = image.convert("L") + warnings.warn("specific single channel image mode not natively supported, converted to regular grayscale") + self.decode = "[0 1]" + self.colorspace = "/DeviceGray" + elif image.mode == "CMYK": + self.decode = "[0 1 0 1 0 1 0 1]" + self.colorspace = "/DeviceCMYK" + else: + if image.mode != "RGB": + image = image.convert("RGB") + warnings.warn("image with unknown mode converted to rgb") + self.decode = "[0 1 0 1 0 1]" + self.colorspace = "/DeviceRGB" + + # create imagematrix + self.imagematrixPS = (trafo.mirror(0) + .translated_pt(-self.xpos_pt, self.ypos_pt+self.height_pt) + .scaled_pt(self.imagewidth/self.width_pt, self.imageheight/self.height_pt)) + self.imagematrixPDF = (trafo.scale_pt(self.width_pt, self.height_pt) + .translated_pt(self.xpos_pt, self.ypos_pt)) + + # check whether imagedata is compressed or not + try: + imagecompressed = image.compressed + except: + imagecompressed = None + if compressmode != None and imagecompressed != None: + raise ValueError("compression of a compressed image not supported") + self.compressmode = compressmode + if compressmode is not None and compressmode not in ["Flate", "DCT"]: + raise ValueError("invalid compressmode '%s'" % compressmode) + if imagecompressed is not None: + self.compressmode = imagecompressed + if imagecompressed not in ["Flate", "DCT"]: + raise ValueError("invalid compressed image '%s'" % imagecompressed) + if not haszlib and compressmode == "Flate": + warnings.warn("zlib module not available, disable compression") + self.compressmode = compressmode = None + + # create data + if compressmode == "Flate": + self.data = zlib.compress(image.tostring(), flatecompresslevel) + elif compressmode == "DCT": + self.data = image.tostring("jpeg", image.mode, + dctquality, dctoptimize, dctprogression) + else: + self.data = image.tostring() + + self.PSsinglestring = self.PSstoreimage and len(self.data) < self.PSmaxstrlen + if self.PSsinglestring: + self.PSimagename = "image-%d-%s-singlestring" % (id(image), compressmode) + else: + self.PSimagename = "image-%d-%s-stringarray" % (id(image), compressmode) + self.PDFimagename = "image-%d-%s" % (id(image), compressmode) + + def bbox(self): + return bbox.bbox_pt(self.xpos_pt, self.ypos_pt, + self.xpos_pt+self.width_pt, self.ypos_pt+self.height_pt) + + def processPS(self, file, writer, context, registry, bbox): + if self.PSstoreimage and not self.PSsinglestring: + registry.add(pswriter.PSdefinition("imagedataaccess", + "{ /imagedataindex load " # get list index + "dup 1 add /imagedataindex exch store " # store increased index + "/imagedataid load exch get }")) # select string from array + if self.PSstoreimage: + registry.add(PSimagedata(self.PSimagename, self.data, self.PSsinglestring, self.PSmaxstrlen)) + bbox += self.bbox() + + file.write("gsave\n") + if self.palettedata is not None: + file.write("[ /Indexed %s %i\n" % (self.palettecolorspace, len(self.palettedata)/3-1)) + file.write("%%%%BeginData: %i ASCII Lines\n" % ascii85lines(len(self.palettedata))) + file.write("<~") + ascii85stream(file, self.palettedata) + file.write("~>\n" + "%%EndData\n") + file.write("] setcolorspace\n") + else: + file.write("%s setcolorspace\n" % self.colorspace) + + if self.PSstoreimage and not self.PSsinglestring: + file.write("/imagedataindex 0 store\n" # not use the stack since interpreters differ in their stack usage + "/imagedataid %s store\n" % self.PSimagename) + + file.write("<<\n" + "/ImageType 1\n" + "/Width %i\n" % self.imagewidth) + file.write("/Height %i\n" % self.imageheight) + file.write("/BitsPerComponent 8\n" + "/ImageMatrix %s\n" % self.imagematrixPS) + file.write("/Decode %s\n" % self.decode) + + file.write("/DataSource ") + if self.PSstoreimage: + if self.PSsinglestring: + file.write("/%s load" % self.PSimagename) + else: + file.write("/imagedataaccess load") # some printers do not allow for inline code here -> we store it in a resource + else: + if self.PSbinexpand == 2: + file.write("currentfile /ASCIIHexDecode filter") + else: + file.write("currentfile /ASCII85Decode filter") + if self.compressmode: + file.write(" /%sDecode filter" % self.compressmode) + file.write("\n") + + file.write(">>\n") + + if self.PSstoreimage: + file.write("image\n") + else: + if self.PSbinexpand == 2: + file.write("%%%%BeginData: %i ASCII Lines\n" + "image\n" % (asciihexlines(len(self.data)) + 1)) + asciihexstream(file, self.data) + else: + # the datasource is currentstream (plus some filters) + file.write("%%%%BeginData: %i ASCII Lines\n" + "image\n" % (ascii85lines(len(self.data)) + 1)) + ascii85stream(file, self.data) + file.write("~>\n") + file.write("%%EndData\n") + + file.write("grestore\n") + + def processPDF(self, file, writer, context, registry, bbox): + registry.add(PDFimage(self.PDFimagename, self.imagewidth, self.imageheight, + self.palettecolorspace, self.palettedata, self.colorspace, + 8, self.compressmode, self.data, registry)) + bbox += self.bbox() + + file.write("q\n") + self.imagematrixPDF.processPDF(file, writer, context, registry, bbox) + file.write("/%s Do\n" % self.PDFimagename) + file.write("Q\n") diff --git a/compiler/gdsMill/pyx/box.py b/compiler/gdsMill/pyx/box.py new file mode 100644 index 00000000..a05eb218 --- /dev/null +++ b/compiler/gdsMill/pyx/box.py @@ -0,0 +1,325 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2004 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +import types, math +import bbox, path, unit, trafo + +class _marker: pass + +class BoxCrossError(Exception): pass + +class polygon_pt: + + def __init__(self, corners=None, center=None): + self.corners = corners + self.center = center + if self.center is None: + self._ensurecenter() + + def _ensurecenter(self): + if self.center is None: + self.center = 0, 0 + for corn in self.corners: + self.center = self.center[0] + corn[0], self.center[1] + corn[1] + self.center = self.center[0]/len(self.corners), self.center[1]/len(self.corners) + + def path(self, centerradius=None, bezierradius=None, beziersoftness=None): + pathitems = [] + if centerradius is not None and self.center is not None: + r = unit.topt(centerradius) + pathitems.append(path.arc_pt(self.center[0], self.center[1], r, 0, 360)) + pathitems.append(path.closepath()) + if bezierradius is not None or beziersoftness is not None: + raise ValueError("smooth functionality removed; apply smooth deformer on path") + pathitems.append(path.moveto_pt(self.corners[0][0], self.corners[0][1])) + for x, y in self.corners[1:]: + pathitems.append(path.lineto_pt(x, y)) + pathitems.append(path.closepath()) + return path.path(*pathitems) + + def transform(self, *trafos): + for trafo in trafos: + if self.center is not None: + self.center = trafo.apply_pt(*self.center) + self.corners = [trafo.apply_pt(*point) for point in self.corners] + + def reltransform(self, *trafos): + if self.center is not None: + trafos = ([trafo.translate_pt(-self.center[0], -self.center[1])] + + list(trafos) + + [trafo.translate_pt(self.center[0], self.center[1])]) + self.transform(*trafos) + + def successivepointnumbers(self): + return [i and (i - 1, i) or (len(self.corners) - 1, 0) for i in range(len(self.corners))] + + def successivepoints(self): + return [(self.corners[i], self.corners[j]) for i, j in self.successivepointnumbers()] + + def circlealignlinevector_pt(self, a, dx, dy, ex, ey, fx, fy, epsilon=1e-10): + cx, cy = self.center + gx, gy = ex - fx, ey - fy # direction vector + if gx*gx + gy*gy < epsilon: # zero line length + return None # no solution -> return None + rsplit = (dx*gx + dy*gy) * 1.0 / (gx*gx + gy*gy) + bx, by = dx - gx * rsplit, dy - gy * rsplit + if bx*bx + by*by < epsilon: # zero projection + return None # no solution -> return None + if bx*gy - by*gx < 0: # half space + return None # no solution -> return None + sfactor = math.sqrt((dx*dx + dy*dy) / (bx*bx + by*by)) + bx, by = a * bx * sfactor, a * by * sfactor + alpha = ((bx+cx-ex)*dy - (by+cy-ey)*dx) * 1.0 / (gy*dx - gx*dy) + if alpha > 0 - epsilon and alpha < 1 + epsilon: + beta = ((ex-bx-cx)*gy - (ey-by-cy)*gx) * 1.0 / (gx*dy - gy*dx) + return beta*dx, beta*dy # valid solution -> return align tuple + # crossing point at the line, but outside a valid range + if alpha < 0: + return 0 # crossing point outside e + return 1 # crossing point outside f + + def linealignlinevector_pt(self, a, dx, dy, ex, ey, fx, fy, epsilon=1e-10): + cx, cy = self.center + gx, gy = ex - fx, ey - fy # direction vector + if gx*gx + gy*gy < epsilon: # zero line length + return None # no solution -> return None + if gy*dx - gx*dy < -epsilon: # half space + return None # no solution -> return None + if dx*gx + dy*gy > epsilon or dx*gx + dy*gy < -epsilon: + if dx*gx + dy*gy < 0: # angle bigger 90 degree + return 0 # use point e + return 1 # use point f + # a and g are othorgonal + alpha = ((a*dx+cx-ex)*dy - (a*dy+cy-ey)*dx) * 1.0 / (gy*dx - gx*dy) + if alpha > 0 - epsilon and alpha < 1 + epsilon: + beta = ((ex-a*dx-cx)*gy - (ey-a*dy-cy)*gx) * 1.0 / (gx*dy - gy*dx) + return beta*dx, beta*dy # valid solution -> return align tuple + # crossing point at the line, but outside a valid range + if alpha < 0: + return 0 # crossing point outside e + return 1 # crossing point outside f + + def circlealignpointvector_pt(self, a, dx, dy, px, py, epsilon=1e-10): + if a*a < epsilon: + return None + cx, cy = self.center + p = 2 * ((px-cx)*dx + (py-cy)*dy) + q = ((px-cx)*(px-cx) + (py-cy)*(py-cy) - a*a) + if p*p/4 - q < 0: + return None + if a > 0: + alpha = - p / 2 + math.sqrt(p*p/4 - q) + else: + alpha = - p / 2 - math.sqrt(p*p/4 - q) + return alpha*dx, alpha*dy + + def linealignpointvector_pt(self, a, dx, dy, px, py): + cx, cy = self.center + beta = (a*dx+cx-px)*dy - (a*dy+cy-py)*dx + return a*dx - beta*dy - px + cx, a*dy + beta*dx - py + cy + + def alignvector_pt(self, a, dx, dy, alignlinevector, alignpointvector): + n = math.hypot(dx, dy) + dx, dy = dx / n, dy / n + linevectors = map(lambda (p1, p2), self=self, a=a, dx=dx, dy=dy, alignlinevector=alignlinevector: + alignlinevector(a, dx, dy, *(p1 + p2)), self.successivepoints()) + for linevector in linevectors: + if type(linevector) is types.TupleType: + return linevector + for i, j in self.successivepointnumbers(): + l1, l2 = linevectors[i], linevectors[j] + if (l1 is not None or l2 is not None) and (l1 == 1 or l1 is None) and (l2 == 0 or l2 is None): + return alignpointvector(a, dx, dy, *self.successivepoints()[j][0]) + return a*dx, a*dy + + def circlealignvector_pt(self, a, dx, dy): + return self.alignvector_pt(a, dx, dy, self.circlealignlinevector_pt, self.circlealignpointvector_pt) + + def linealignvector_pt(self, a, dx, dy): + return self.alignvector_pt(a, dx, dy, self.linealignlinevector_pt, self.linealignpointvector_pt) + + def circlealignvector(self, a, dx, dy): + ndx, ndy = self.circlealignvector_pt(unit.topt(a), dx, dy) + return ndx * unit.t_pt, ndy * unit.t_pt + + def linealignvector(self, a, dx, dy): + ndx, ndy = self.linealignvector_pt(unit.topt(a), dx, dy) + return ndx * unit.t_pt, ndy * unit.t_pt + + def circlealign_pt(self, *args): + self.transform(trafo.translate_pt(*self.circlealignvector_pt(*args))) + return self + + def linealign_pt(self, *args): + self.transform(trafo.translate_pt(*self.linealignvector_pt(*args))) + return self + + def circlealign(self, *args): + self.transform(trafo.translate(*self.circlealignvector(*args))) + return self + + def linealign(self, *args): + self.transform(trafo.translate(*self.linealignvector(*args))) + return self + + def extent_pt(self, dx, dy): + n = math.hypot(dx, dy) + dx, dy = dx / n, dy / n + oldcenter = self.center + if self.center is None: + self.center = 0, 0 + x1, y1 = self.linealignvector_pt(0, dx, dy) + x2, y2 = self.linealignvector_pt(0, -dx, -dy) + self.center = oldcenter + return (x1-x2)*dx + (y1-y2)*dy + + def extent(self, dx, dy): + return self.extent_pt(dx, dy) * unit.t_pt + + def pointdistance_pt(self, x, y): + result = None + for p1, p2 in self.successivepoints(): + gx, gy = p2[0] - p1[0], p2[1] - p1[1] + if gx * gx + gy * gy < 1e-10: + dx, dy = p1[0] - x, p1[1] - y + else: + a = (gx * (x - p1[0]) + gy * (y - p1[1])) / (gx * gx + gy * gy) + if a < 0: + dx, dy = p1[0] - x, p1[1] - y + elif a > 1: + dx, dy = p2[0] - x, p2[1] - y + else: + dx, dy = x - p1[0] - a * gx, y - p1[1] - a * gy + new = math.hypot(dx, dy) + if result is None or new < result: + result = new + return result + + def pointdistance(self, x, y): + return self.pointdistance_pt(unit.topt(x), unit.topt(y)) * unit.t_pt + + def boxdistance_pt(self, other, epsilon=1e-10): + # XXX: boxes crossing and distance calculation is O(N^2) + for p1, p2 in self.successivepoints(): + for p3, p4 in other.successivepoints(): + a = (p4[1] - p3[1]) * (p3[0] - p1[0]) - (p4[0] - p3[0]) * (p3[1] - p1[1]) + b = (p2[1] - p1[1]) * (p3[0] - p1[0]) - (p2[0] - p1[0]) * (p3[1] - p1[1]) + c = (p2[0] - p1[0]) * (p4[1] - p3[1]) - (p2[1] - p1[1]) * (p4[0] - p3[0]) + if (abs(c) > 1e-10 and + a / c > -epsilon and a / c < 1 + epsilon and + b / c > -epsilon and b / c < 1 + epsilon): + raise BoxCrossError + result = None + for x, y in other.corners: + new = self.pointdistance_pt(x, y) + if result is None or new < result: + result = new + for x, y in self.corners: + new = other.pointdistance_pt(x, y) + if result is None or new < result: + result = new + return result + + def boxdistance(self, other): + return self.boxdistance_pt(other) * unit.t_pt + + def bbox(self): + return bbox.bbox_pt(min([x[0] for x in self.corners]), + min([x[1] for x in self.corners]), + max([x[0] for x in self.corners]), + max([x[1] for x in self.corners])) + + +def genericalignequal_pt(method, polygons, a, dx, dy): + vec = None + for p in polygons: + v = method(p, a, dx, dy) + if vec is None or vec[0] * dx + vec[1] * dy < v[0] * dx + v[1] * dy: + vec = v + for p in polygons: + p.transform(trafo.translate_pt(*vec)) + + +def circlealignequal_pt(polygons, *args): + genericalignequal_pt(polygon_pt.circlealignvector_pt, polygons, *args) + +def linealignequal_pt(polygons, *args): + genericalignequal_pt(polygon_pt.linealignvector_pt, polygons, *args) + +def circlealignequal(polygons, a, *args): + circlealignequal_pt(polygons, unit.topt(a), *args) + +def linealignequal(polygons, a, *args): + linealignequal_pt(polygons, unit.topt(a), *args) + + +def tile_pt(polygons, a, dx, dy): + maxextent = polygons[0].extent_pt(dx, dy) + for p in polygons[1:]: + extent = p.extent_pt(dx, dy) + if extent > maxextent: + maxextent = extent + delta = maxextent + a + d = 0 + for p in polygons: + p.transform(trafo.translate_pt(d*dx, d*dy)) + d += delta + return delta + + +def tile(polygons, a, dx, dy): + return tile_pt(polygons, unit.topt(a), dx, dy) * unit.t_pt + + +class polygon(polygon_pt): + + def __init__(self, corners=None, center=None, **args): + corners = [[unit.topt(x) for x in corner] for corner in corners] + if center is not None: + center = unit.topt(center[0]), unit.topt(center[1]) + polygon_pt.__init__(self, corners=corners, center=center, **args) + + +class rect_pt(polygon_pt): + + def __init__(self, x, y, width, height, relcenter=(0, 0), abscenter=(0, 0), + corners=_marker, center=_marker, **args): + if corners != _marker or center != _marker: + raise ValueError + polygon_pt.__init__(self, corners=((x, y), + (x + width, y), + (x + width, y + height), + (x, y + height)), + center=(x + relcenter[0] * width + abscenter[0], + y + relcenter[1] * height + abscenter[1]), + **args) + + +class rect(rect_pt): + + def __init__(self, x, y, width, height, relcenter=(0, 0), abscenter=(0, 0), **args): + rect_pt.__init__(self, unit.topt(x), unit.topt(y), unit.topt(width), unit.topt(height), + relcenter=relcenter, + abscenter=(unit.topt(abscenter[0]), unit.topt(abscenter[1])), **args) + diff --git a/compiler/gdsMill/pyx/canvas.py b/compiler/gdsMill/pyx/canvas.py new file mode 100644 index 00000000..6110af17 --- /dev/null +++ b/compiler/gdsMill/pyx/canvas.py @@ -0,0 +1,361 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +"""The canvas module provides a PostScript canvas class and related classes + +A canvas holds a collection of all elements and corresponding attributes to be +displayed. """ + +# +# canvas item +# + +from __future__ import nested_scopes +import os + +class canvasitem: + + """Base class for everything which can be inserted into a canvas""" + + def bbox(self): + """return bounding box of canvasitem""" + pass + + def processPS(self, file, writer, context, registry, bbox): + """process canvasitem by writing the corresponding PS code to file and + by updating context, registry as well as bbox + + - the PS code corresponding to the canvasitem has to be written in the + stream file, which provides a write(string) method + - writer is the PSwriter used for the output + - context is an instance of pswriter.context which is used for keeping + track of the graphics state (current linewidth, colorspace and font)) + - registry is used for tracking resources needed by the canvasitem + - bbox has to be updated to include the bounding box of the canvasitem + """ + raise NotImplementedError() + + def processPDF(self, file, writer, context, registry, bbox): + """process canvasitem by writing the corresponding PDF code to file and + by updating context, registry as well as bbox + + - the PDF code corresponding to the canvasitem has to be written in the + stream file, which provides a write(string) method + - writer is the PDFwriter used for the output, which contains properties + like whether streamcompression is used + - context is an instance of pdfwriter.context which is used for keeping + track of the graphics state, in particular for the emulation of PS + behaviour regarding fill and stroke styles, for keeping track of the + currently selected font as well as of text regions. + - registry is used for tracking resources needed by the canvasitem + - bbox has to be updated to include the bounding box of the canvasitem + """ + raise NotImplementedError() + + +import attr, deco, deformer, document, style, trafo, type1font +import bbox as bboxmodule + + +# +# clipping class +# + +class clip(canvasitem): + + """class for use in canvas constructor which clips to a path""" + + def __init__(self, path): + """construct a clip instance for a given path""" + self.path = path + + def bbox(self): + # as a canvasitem a clipping path has NO influence on the bbox... + return bboxmodule.empty() + + def clipbbox(self): + # ... but for clipping, we nevertheless need the bbox + return self.path.bbox() + + def processPS(self, file, writer, context, registry, bbox): + file.write("newpath\n") + self.path.outputPS(file, writer) + file.write("clip\n") + + def processPDF(self, file, writer, context, registry, bbox): + self.path.outputPDF(file, writer) + file.write("W n\n") + + +# +# general canvas class +# + +class _canvas(canvasitem): + + """a canvas holds a collection of canvasitems""" + + def __init__(self, attrs=[], texrunner=None): + + """construct a canvas + + The canvas can be modfied by supplying args, which have + to be instances of one of the following classes: + - trafo.trafo (leading to a global transformation of the canvas) + - canvas.clip (clips the canvas) + - style.strokestyle, style.fillstyle (sets some global attributes of the canvas) + + Note that, while the first two properties are fixed for the + whole canvas, the last one can be changed via canvas.set(). + + The texrunner instance used for the text method can be specified + using the texrunner argument. It defaults to text.defaulttexrunner + + """ + + self.items = [] + self.trafo = trafo.trafo() + self.clipbbox = None + if texrunner is not None: + self.texrunner = texrunner + else: + # prevent cyclic imports + import text + self.texrunner = text.defaulttexrunner + + attr.checkattrs(attrs, [trafo.trafo_pt, clip, style.strokestyle, style.fillstyle]) + # We have to reverse the trafos such that the PostScript concat operators + # are in the right order. Correspondingly, we below multiply the current self.trafo + # from the right. + # Note that while for the stroke and fill styles the order doesn't matter at all, + # this is not true for the clip operation. + attrs = attrs[:] + attrs.reverse() + for aattr in attrs: + if isinstance(aattr, trafo.trafo_pt): + self.trafo = self.trafo * aattr + elif isinstance(aattr, clip): + if self.clipbbox is None: + self.clipbbox = aattr.clipbbox().transformed(self.trafo) + else: + self.clippbox *= aattr.clipbbox().transformed(self.trafo) + self.items.append(aattr) + + def __len__(self): + return len(self.items) + + def __getitem__(self, i): + return self.items[i] + + def bbox(self): + """returns bounding box of canvas + + Note that this bounding box doesn't take into account the linewidths, so + is less accurate than the one used when writing the output to a file. + """ + obbox = bboxmodule.empty() + for cmd in self.items: + obbox += cmd.bbox() + + # transform according to our global transformation and + # intersect with clipping bounding box (which has already been + # transformed in canvas.__init__()) + obbox.transform(self.trafo) + if self.clipbbox is not None: + obbox *= self.clipbbox + return obbox + + def processPS(self, file, writer, context, registry, bbox): + context = context() + if self.items: + file.write("gsave\n") + nbbox = bboxmodule.empty() + for item in self.items: + item.processPS(file, writer, context, registry, nbbox) + # update bounding bbox + nbbox.transform(self.trafo) + if self.clipbbox is not None: + nbbox *= self.clipbbox + bbox += nbbox + file.write("grestore\n") + + def processPDF(self, file, writer, context, registry, bbox): + context = context() + if self.items: + file.write("q\n") # gsave + nbbox = bboxmodule.empty() + for item in self.items: + if isinstance(item, type1font.text_pt): + if not context.textregion: + file.write("BT\n") + context.textregion = 1 + else: + if context.textregion: + file.write("ET\n") + context.textregion = 0 + context.font = None + item.processPDF(file, writer, context, registry, nbbox) + if context.textregion: + file.write("ET\n") + context.textregion = 0 + context.font = None + # update bounding bbox + nbbox.transform(self.trafo) + if self.clipbbox is not None: + nbbox *= self.clipbbox + bbox += nbbox + file.write("Q\n") # grestore + + def insert(self, item, attrs=None): + """insert item in the canvas. + + If attrs are passed, a canvas containing the item is inserted applying attrs. + + returns the item + + """ + + if not isinstance(item, canvasitem): + raise RuntimeError("only instances of base.canvasitem can be inserted into a canvas") + + if attrs: + sc = _canvas(attrs) + sc.insert(item) + self.items.append(sc) + else: + self.items.append(item) + return item + + def draw(self, path, attrs): + """draw path on canvas using the style given by args + + The argument attrs consists of PathStyles, which modify + the appearance of the path, PathDecos, which add some new + visual elements to the path, or trafos, which are applied + before drawing the path. + + """ + + attrs = attr.mergeattrs(attrs) + attr.checkattrs(attrs, [deco.deco, deformer.deformer, style.fillstyle, style.strokestyle]) + + for adeformer in attr.getattrs(attrs, [deformer.deformer]): + path = adeformer.deform(path) + + styles = attr.getattrs(attrs, [style.fillstyle, style.strokestyle]) + dp = deco.decoratedpath(path, styles=styles) + + # add path decorations and modify path accordingly + for adeco in attr.getattrs(attrs, [deco.deco]): + adeco.decorate(dp, self.texrunner) + + self.insert(dp) + + def stroke(self, path, attrs=[]): + """stroke path on canvas using the style given by args + + The argument attrs consists of PathStyles, which modify + the appearance of the path, PathDecos, which add some new + visual elements to the path, or trafos, which are applied + before drawing the path. + + """ + + self.draw(path, [deco.stroked]+list(attrs)) + + def fill(self, path, attrs=[]): + """fill path on canvas using the style given by args + + The argument attrs consists of PathStyles, which modify + the appearance of the path, PathDecos, which add some new + visual elements to the path, or trafos, which are applied + before drawing the path. + + """ + + self.draw(path, [deco.filled]+list(attrs)) + + def settexrunner(self, texrunner): + """sets the texrunner to be used to within the text and text_pt methods""" + + self.texrunner = texrunner + + def text(self, x, y, atext, *args, **kwargs): + """insert a text into the canvas + + inserts a textbox created by self.texrunner.text into the canvas + + returns the inserted textbox""" + + return self.insert(self.texrunner.text(x, y, atext, *args, **kwargs)) + + + def text_pt(self, x, y, atext, *args): + """insert a text into the canvas + + inserts a textbox created by self.texrunner.text_pt into the canvas + + returns the inserted textbox""" + + return self.insert(self.texrunner.text_pt(x, y, atext, *args)) + +# +# user canvas class which adds a few convenience methods for single page output +# + +def _wrappedindocument(method): + def wrappedindocument(self, file, *args, **kwargs): + d = document.document([document.page(self, *args, **kwargs)]) + self.__name__ = method.__name__ + self.__doc__ = method.__doc__ + return method(d, file) + return wrappedindocument + + +class canvas(_canvas): + + """a canvas holds a collection of canvasitems""" + + writeEPSfile = _wrappedindocument(document.document.writeEPSfile) + writePSfile = _wrappedindocument(document.document.writePSfile) + writePDFfile = _wrappedindocument(document.document.writePDFfile) + writetofile = _wrappedindocument(document.document.writetofile) + + def pipeGS(self, filename="-", device=None, resolution=100, + gscommand="gs", gsoptions="", + textalphabits=4, graphicsalphabits=4, + **kwargs): + if device is None: + if filename.endswith(".png"): + device = "png16m" + if filename.endswith(".jpg"): + device = "jpeg" + gscommand += " -dEPSCrop -dNOPAUSE -dQUIET -dBATCH -r%i -sDEVICE=%s -sOutputFile=%s" % (resolution, device, filename) + if gsoptions: + gscommand += " %s" % gsoptions + if textalphabits is not None: + gscommand += " -dTextAlphaBits=%i" % textalphabits + if graphicsalphabits is not None: + gscommand += " -dGraphicsAlphaBits=%i" % graphicsalphabits + gscommand += " -" + input = os.popen(gscommand, "w") + self.writeEPSfile(input, **kwargs) diff --git a/compiler/gdsMill/pyx/color.py b/compiler/gdsMill/pyx/color.py new file mode 100644 index 00000000..8b7d1609 --- /dev/null +++ b/compiler/gdsMill/pyx/color.py @@ -0,0 +1,493 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004, 2006 Jörg Lehmann +# Copyright (C) 2003-2006 Michael Schindler +# Copyright (C) 2002-2007 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import binascii, colorsys, math, struct, warnings +import attr, style, pdfwriter + +# device-dependend (nonlinear) functions for color conversion +# UCRx : [0,1] -> [-1, 1] UnderColorRemoval (removes black from c, y, m) +# BG : [0,1] -> [0, 1] BlackGeneration (generate the black from the nominal k-value) +# as long as we have no further knowledge we define them linearly with constants 1 +def _UCRc(x): return x +def _UCRm(x): return x +def _UCRy(x): return x +def _BG(x): return x + +def set(UCRc=None, UCRm=None, UCRy=None, BG=None): + global _UCRc + global _UCRm + global _UCRy + global _BG + + if UCRc is not None: + _UCRc = UCRc + if UCRm is not None: + _UCRm = UCRm + if UCRy is not None: + _UCRy = UCRy + if BG is not None: + _BG = BG + + +class color(attr.exclusiveattr, style.strokestyle, style.fillstyle): + + """base class for all colors""" + + def __init__(self): + attr.exclusiveattr.__init__(self, color) + + +clear = attr.clearclass(color) + + +class grey(color): + + """grey tones""" + + def __init__(self, gray): + color.__init__(self) + if gray<0 or gray>1: raise ValueError + self.color = {"gray": gray} + + def processPS(self, file, writer, context, registry, bbox): + file.write("%(gray)g setgray\n" % self.color) + + def processPDF(self, file, writer, context, registry, bbox): + if context.strokeattr: + file.write("%(gray)f G\n" % self.color) + if context.fillattr: + file.write("%(gray)f g\n" % self.color) + + def cmyk(self): + return cmyk(0, 0, 0, 1 - self.color["gray"]) + + def grey(self): + return grey(**self.color) + gray = grey + + def hsb(self): + return hsb(0, 0, self.color["gray"]) + + def rgb(self): + return rgb(self.color["gray"], self.color["gray"], self.color["gray"]) + + def colorspacestring(self): + return "/DeviceGray" + + def tostring8bit(self): + return chr(int(self.color["gray"]*255)) + +grey.black = grey(0.0) +grey.white = grey(1.0) +gray = grey + + +class rgb(color): + + """rgb colors""" + + def __init__(self, r=0.0, g=0.0, b=0.0): + color.__init__(self) + if r<0 or r>1 or g<0 or g>1 or b<0 or b>1: raise ValueError + self.color = {"r": r, "g": g, "b": b} + + def processPS(self, file, writer, context, registry, bbox): + file.write("%(r)g %(g)g %(b)g setrgbcolor\n" % self.color) + + def processPDF(self, file, writer, context, registry, bbox): + if context.strokeattr: + file.write("%(r)f %(g)f %(b)f RG\n" % self.color) + if context.fillattr: + file.write("%(r)f %(g)f %(b)f rg\n" % self.color) + + def cmyk(self): + # conversion to cmy + c, m, y = 1 - self.color["r"], 1 - self.color["g"], 1 - self.color["b"] + # conversion from cmy to cmyk with device-dependent functions + k = min([c, m, y]) + return cmyk(min(1, max(0, c - _UCRc(k))), + min(1, max(0, m - _UCRm(k))), + min(1, max(0, y - _UCRy(k))), + _BG(k)) + + def grey(self): + return grey(0.3*self.color["r"] + 0.59*self.color["g"] + 0.11*self.color["b"]) + gray = grey + + def hsb(self): + + values = self.color.values() + values.sort() + z, y, x = values + r, g, b = self.color["r"], self.color["g"], self.color["b"] + try: + if r == x and g == z: + return hsb((5 + (x-b)/(x-z)) / 6.0, (x - z) / x, x) + elif r == x and g > z: + return hsb((1 - (x-g)/(x-z)) / 6.0, (x - z) / x, x) + elif g == x and b == z: + return hsb((1 + (x-r)/(x-z)) / 6.0, (x - z) / x, x) + elif g == x and b > z: + return hsb((3 - (x-b)/(x-z)) / 6.0, (x - z) / x, x) + elif b == x and r == z: + return hsb((3 + (x-g)/(x-z)) / 6.0, (x - z) / x, x) + elif b == x and r > z: + return hsb((5 - (x-r)/(x-z)) / 6.0, (x - z) / x, x) + else: + raise ValueError + except ZeroDivisionError: + return hsb(0, 0, x) + + def rgb(self): + return rgb(**self.color) + + def colorspacestring(self): + return "/DeviceRGB" + + def tostring8bit(self): + return struct.pack("BBB", int(self.color["r"]*255), int(self.color["g"]*255), int(self.color["b"]*255)) + + def tohexstring(self, cssstrip=1, addhash=1): + hexstring = binascii.b2a_hex(self.to8bitstring()) + if cssstrip and hexstring[0] == hexstring[1] and hexstring[2] == hexstring[3] and hexstring[4] == hexstring[5]: + hexstring = "".join([hexstring[0], hexstring[1], hexstring[2]]) + if addhash: + hexstring = "#" + hexstring + return hexstring + + +def rgbfromhexstring(hexstring): + hexstring = hexstring.strip().lstrip("#") + if len(hexstring) == 3: + hexstring = "".join([hexstring[0], hexstring[0], hexstring[1], hexstring[1], hexstring[2], hexstring[2]]) + elif len(hexstring) != 6: + raise ValueError("3 or 6 digit hex number expected (with optional leading hash character)") + return rgb(*[value/255.0 for value in struct.unpack("BBB", binascii.a2b_hex(hexstring))]) + +rgb.red = rgb(1, 0, 0) +rgb.green = rgb(0, 1, 0) +rgb.blue = rgb(0, 0, 1) +rgb.white = rgb(1, 1, 1) +rgb.black = rgb(0, 0, 0) + + +class hsb(color): + + """hsb colors""" + + def __init__(self, h=0.0, s=0.0, b=0.0): + color.__init__(self) + if h<0 or h>1 or s<0 or s>1 or b<0 or b>1: raise ValueError + self.color = {"h": h, "s": s, "b": b} + + def processPS(self, file, writer, context, registry, bbox): + file.write("%(h)g %(s)g %(b)g sethsbcolor\n" % self.color) + + def processPDF(self, file, writer, context, registry, bbox): + r, g, b = colorsys.hsv_to_rgb(self.color["h"], self.color["s"], self.color["b"]) + rgb(r, g, b).processPDF(file, writer, context, registry, bbox) + + def cmyk(self): + return self.rgb().cmyk() + + def grey(self): + return self.rgb().grey() + gray = grey + + def hsb(self): + return hsb(**self.color) + + def rgb(self): + h, s, b = self.color["h"], self.color["s"], self.color["b"] + i = int(6*h) + f = 6*h - i + m, n, k = 1 - s, 1 - s*f, 1 - s*(1-f) + if i == 1: + return rgb(b*n, b, b*m) + elif i == 2: + return rgb(b*m, b, b*k) + elif i == 3: + return rgb(b*m, b*n, b) + elif i == 4: + return rgb(b*k, b*m, b) + elif i == 5: + return rgb(b, b*m, b*n) + else: + return rgb(b, b*k, b*m) + + def colorspacestring(self): + raise RuntimeError("colorspace string not available for hsb colors") + + +class cmyk(color): + + """cmyk colors""" + + def __init__(self, c=0.0, m=0.0, y=0.0, k=0.0): + color.__init__(self) + if c<0 or c>1 or m<0 or m>1 or y<0 or y>1 or k<0 or k>1: raise ValueError + self.color = {"c": c, "m": m, "y": y, "k": k} + + def processPS(self, file, writer, context, registry, bbox): + file.write("%(c)g %(m)g %(y)g %(k)g setcmykcolor\n" % self.color) + + def processPDF(self, file, writer, context, registry, bbox): + if context.strokeattr: + file.write("%(c)f %(m)f %(y)f %(k)f K\n" % self.color) + if context.fillattr: + file.write("%(c)f %(m)f %(y)f %(k)f k\n" % self.color) + + def cmyk(self): + return cmyk(**self.color) + + def grey(self): + return grey(1 - min([1, 0.3*self.color["c"] + 0.59*self.color["m"] + + 0.11*self.color["y"] + self.color["k"]])) + gray = grey + + def hsb(self): + return self.rgb().hsb() + + def rgb(self): + # conversion to cmy: + c = min(1, self.color["c"] + self.color["k"]) + m = min(1, self.color["m"] + self.color["k"]) + y = min(1, self.color["y"] + self.color["k"]) + # conversion from cmy to rgb: + return rgb(1 - c, 1 - m, 1 - y) + + def colorspacestring(self): + return "/DeviceCMYK" + + def tostring8bit(self): + return struct.pack("BBBB", int(self.color["c"]*255), int(self.color["m"]*255), int(self.color["y"]*255), int(self.color["k"]*255)) + +cmyk.GreenYellow = cmyk(0.15, 0, 0.69, 0) +cmyk.Yellow = cmyk(0, 0, 1, 0) +cmyk.Goldenrod = cmyk(0, 0.10, 0.84, 0) +cmyk.Dandelion = cmyk(0, 0.29, 0.84, 0) +cmyk.Apricot = cmyk(0, 0.32, 0.52, 0) +cmyk.Peach = cmyk(0, 0.50, 0.70, 0) +cmyk.Melon = cmyk(0, 0.46, 0.50, 0) +cmyk.YellowOrange = cmyk(0, 0.42, 1, 0) +cmyk.Orange = cmyk(0, 0.61, 0.87, 0) +cmyk.BurntOrange = cmyk(0, 0.51, 1, 0) +cmyk.Bittersweet = cmyk(0, 0.75, 1, 0.24) +cmyk.RedOrange = cmyk(0, 0.77, 0.87, 0) +cmyk.Mahogany = cmyk(0, 0.85, 0.87, 0.35) +cmyk.Maroon = cmyk(0, 0.87, 0.68, 0.32) +cmyk.BrickRed = cmyk(0, 0.89, 0.94, 0.28) +cmyk.Red = cmyk(0, 1, 1, 0) +cmyk.OrangeRed = cmyk(0, 1, 0.50, 0) +cmyk.RubineRed = cmyk(0, 1, 0.13, 0) +cmyk.WildStrawberry = cmyk(0, 0.96, 0.39, 0) +cmyk.Salmon = cmyk(0, 0.53, 0.38, 0) +cmyk.CarnationPink = cmyk(0, 0.63, 0, 0) +cmyk.Magenta = cmyk(0, 1, 0, 0) +cmyk.VioletRed = cmyk(0, 0.81, 0, 0) +cmyk.Rhodamine = cmyk(0, 0.82, 0, 0) +cmyk.Mulberry = cmyk(0.34, 0.90, 0, 0.02) +cmyk.RedViolet = cmyk(0.07, 0.90, 0, 0.34) +cmyk.Fuchsia = cmyk(0.47, 0.91, 0, 0.08) +cmyk.Lavender = cmyk(0, 0.48, 0, 0) +cmyk.Thistle = cmyk(0.12, 0.59, 0, 0) +cmyk.Orchid = cmyk(0.32, 0.64, 0, 0) +cmyk.DarkOrchid = cmyk(0.40, 0.80, 0.20, 0) +cmyk.Purple = cmyk(0.45, 0.86, 0, 0) +cmyk.Plum = cmyk(0.50, 1, 0, 0) +cmyk.Violet = cmyk(0.79, 0.88, 0, 0) +cmyk.RoyalPurple = cmyk(0.75, 0.90, 0, 0) +cmyk.BlueViolet = cmyk(0.86, 0.91, 0, 0.04) +cmyk.Periwinkle = cmyk(0.57, 0.55, 0, 0) +cmyk.CadetBlue = cmyk(0.62, 0.57, 0.23, 0) +cmyk.CornflowerBlue = cmyk(0.65, 0.13, 0, 0) +cmyk.MidnightBlue = cmyk(0.98, 0.13, 0, 0.43) +cmyk.NavyBlue = cmyk(0.94, 0.54, 0, 0) +cmyk.RoyalBlue = cmyk(1, 0.50, 0, 0) +cmyk.Blue = cmyk(1, 1, 0, 0) +cmyk.Cerulean = cmyk(0.94, 0.11, 0, 0) +cmyk.Cyan = cmyk(1, 0, 0, 0) +cmyk.ProcessBlue = cmyk(0.96, 0, 0, 0) +cmyk.SkyBlue = cmyk(0.62, 0, 0.12, 0) +cmyk.Turquoise = cmyk(0.85, 0, 0.20, 0) +cmyk.TealBlue = cmyk(0.86, 0, 0.34, 0.02) +cmyk.Aquamarine = cmyk(0.82, 0, 0.30, 0) +cmyk.BlueGreen = cmyk(0.85, 0, 0.33, 0) +cmyk.Emerald = cmyk(1, 0, 0.50, 0) +cmyk.JungleGreen = cmyk(0.99, 0, 0.52, 0) +cmyk.SeaGreen = cmyk(0.69, 0, 0.50, 0) +cmyk.Green = cmyk(1, 0, 1, 0) +cmyk.ForestGreen = cmyk(0.91, 0, 0.88, 0.12) +cmyk.PineGreen = cmyk(0.92, 0, 0.59, 0.25) +cmyk.LimeGreen = cmyk(0.50, 0, 1, 0) +cmyk.YellowGreen = cmyk(0.44, 0, 0.74, 0) +cmyk.SpringGreen = cmyk(0.26, 0, 0.76, 0) +cmyk.OliveGreen = cmyk(0.64, 0, 0.95, 0.40) +cmyk.RawSienna = cmyk(0, 0.72, 1, 0.45) +cmyk.Sepia = cmyk(0, 0.83, 1, 0.70) +cmyk.Brown = cmyk(0, 0.81, 1, 0.60) +cmyk.Tan = cmyk(0.14, 0.42, 0.56, 0) +cmyk.Gray = cmyk(0, 0, 0, 0.50) +cmyk.Grey = cmyk.Gray +cmyk.Black = cmyk(0, 0, 0, 1) +cmyk.White = cmyk(0, 0, 0, 0) +cmyk.white = cmyk.White +cmyk.black = cmyk.Black + +class palette(attr.changelist): + """color palettes + + A color palette is a discrete, ordered list of colors""" + +palette.clear = attr.clearclass(palette) + + +class gradient(attr.changeattr): + + """base class for color gradients + + A gradient is a continuous collection of colors with a single parameter ranging from 0 to 1 + to address them""" + + def getcolor(self, param): + """return color corresponding to param""" + pass + + def select(self, index, n_indices): + """return a color corresponding to an index out of n_indices""" + if n_indices == 1: + param = 0 + else: + param = index / (n_indices - 1.0) + return self.getcolor(param) + +gradient.clear = attr.clearclass(gradient) + + +class lineargradient(gradient): + + """collection of two colors for a linear transition between them""" + + def __init__(self, mincolor, maxcolor): + if mincolor.__class__ != maxcolor.__class__: + raise ValueError + self.colorclass = mincolor.__class__ + self.mincolor = mincolor + self.maxcolor = maxcolor + + def getcolor(self, param): + colordict = {} + for key in self.mincolor.color.keys(): + colordict[key] = param * self.maxcolor.color[key] + (1 - param) * self.mincolor.color[key] + return self.colorclass(**colordict) + + +class functiongradient(gradient): + + """collection of colors for an arbitray non-linear transition between them + + parameters: + functions: a dictionary for the color values + type: a string indicating the color class + """ + + def __init__(self, functions, cls): + self.functions = functions + self.cls = cls + + def getcolor(self, param): + colordict = {} + for key in self.functions.keys(): + colordict[key] = self.functions[key](param) + return self.cls(**colordict) + + +gradient.Gray = lineargradient(gray.white, gray.black) +gradient.Grey = gradient.Gray +gradient.ReverseGray = lineargradient(gray.black, gray.white) +gradient.ReverseGrey = gradient.ReverseGray +gradient.BlackYellow = functiongradient({ # compare this with reversegray above + "r":(lambda x: 2*x*(1-x)**5 + 3.5*x**2*(1-x)**3 + 2.1*x*x*(1-x)**2 + 3.0*x**3*(1-x)**2 + x**0.5*(1-(1-x)**2)), + "g":(lambda x: 1.5*x**2*(1-x)**3 - 0.8*x**3*(1-x)**2 + 2.0*x**4*(1-x) + x**4), + "b":(lambda x: 5*x*(1-x)**5 - 0.5*x**2*(1-x)**3 + 0.3*x*x*(1-x)**2 + 5*x**3*(1-x)**2 + 0.5*x**6)}, + rgb) +gradient.RedGreen = lineargradient(rgb.red, rgb.green) +gradient.RedBlue = lineargradient(rgb.red, rgb.blue) +gradient.GreenRed = lineargradient(rgb.green, rgb.red) +gradient.GreenBlue = lineargradient(rgb.green, rgb.blue) +gradient.BlueRed = lineargradient(rgb.blue, rgb.red) +gradient.BlueGreen = lineargradient(rgb.blue, rgb.green) +gradient.RedBlack = lineargradient(rgb.red, rgb.black) +gradient.BlackRed = lineargradient(rgb.black, rgb.red) +gradient.RedWhite = lineargradient(rgb.red, rgb.white) +gradient.WhiteRed = lineargradient(rgb.white, rgb.red) +gradient.GreenBlack = lineargradient(rgb.green, rgb.black) +gradient.BlackGreen = lineargradient(rgb.black, rgb.green) +gradient.GreenWhite = lineargradient(rgb.green, rgb.white) +gradient.WhiteGreen = lineargradient(rgb.white, rgb.green) +gradient.BlueBlack = lineargradient(rgb.blue, rgb.black) +gradient.BlackBlue = lineargradient(rgb.black, rgb.blue) +gradient.BlueWhite = lineargradient(rgb.blue, rgb.white) +gradient.WhiteBlue = lineargradient(rgb.white, rgb.blue) +gradient.Rainbow = lineargradient(hsb(0, 1, 1), hsb(2.0/3.0, 1, 1)) +gradient.ReverseRainbow = lineargradient(hsb(2.0/3.0, 1, 1), hsb(0, 1, 1)) +gradient.Hue = lineargradient(hsb(0, 1, 1), hsb(1, 1, 1)) +gradient.ReverseHue = lineargradient(hsb(1, 1, 1), hsb(0, 1, 1)) + + +class PDFextgstate(pdfwriter.PDFobject): + + def __init__(self, name, extgstate, registry): + pdfwriter.PDFobject.__init__(self, "extgstate", name) + registry.addresource("ExtGState", name, self) + self.name = name + self.extgstate = extgstate + + def write(self, file, writer, registry): + file.write("%s\n" % self.extgstate) + + +class transparency(attr.exclusiveattr, style.strokestyle, style.fillstyle): + + def __init__(self, value): + self.value = 1-value + attr.exclusiveattr.__init__(self, transparency) + + def processPS(self, file, writer, context, registry, bbox): + warnings.warn("Transparency not available in PostScript, proprietary ghostscript extension code inserted.") + file.write("%f .setshapealpha\n" % self.value) + + def processPDF(self, file, writer, context, registry, bbox): + if context.strokeattr and context.fillattr: + registry.add(PDFextgstate("Transparency-%f" % self.value, + "<< /Type /ExtGState /CA %f /ca %f >>" % (self.value, self.value), registry)) + file.write("/Transparency-%f gs\n" % self.value) + elif context.strokeattr: + registry.add(PDFextgstate("Transparency-Stroke-%f" % self.value, + "<< /Type /ExtGState /CA %f >>" % self.value, registry)) + file.write("/Transparency-Stroke-%f gs\n" % self.value) + elif context.fillattr: + registry.add(PDFextgstate("Transparency-Fill-%f" % self.value, + "<< /Type /ExtGState /ca %f >>" % self.value, registry)) + file.write("/Transparency-Fill-%f gs\n" % self.value) + diff --git a/compiler/gdsMill/pyx/config.py b/compiler/gdsMill/pyx/config.py new file mode 100644 index 00000000..611ab70e --- /dev/null +++ b/compiler/gdsMill/pyx/config.py @@ -0,0 +1,54 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2003-2004 Jörg Lehmann +# Copyright (C) 2003-2005 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import ConfigParser, os.path +import siteconfig + +cflist = [os.path.join(siteconfig.pyxrcdir, "pyxrc"), os.path.expanduser("~/.pyxrc")] + +config = ConfigParser.ConfigParser() +config.read(cflist) + +def get(section, option, default): + try: + return config.get(section, option) + except: + return default + +def getint(section, option, default): + try: + return config.getint(section, option) + except: + return default + +def getfloat(section, option, default): + try: + return config.getfloat(section, option) + except: + return default + +def getboolean(section, option, default): + try: + return config.getboolean(section, option) + except: + return default + diff --git a/compiler/gdsMill/pyx/connector.py b/compiler/gdsMill/pyx/connector.py new file mode 100644 index 00000000..53a52445 --- /dev/null +++ b/compiler/gdsMill/pyx/connector.py @@ -0,0 +1,408 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2003-2006 Michael Schindler +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +import math +from math import pi, sin, cos, atan2, tan, hypot, acos, sqrt +import path, unit, mathutils, normpath +try: + from math import radians, degrees +except ImportError: + # fallback implementation for Python 2.1 and below + def radians(x): return x*pi/180 + def degrees(x): return x*180/pi + + +######################### +## helpers +######################### + +class connector_pt(normpath.normpath): + + def omitends(self, box1, box2): + """intersects a path with the boxes' paths""" + + # cut off the start of self + # XXX how can decoration of this box1.path() be handled? + sp = self.intersect(box1.path())[0] + if sp: + self.normsubpaths = self.split(sp[-1:])[1].normsubpaths + + # cut off the end of self + sp = self.intersect(box2.path())[0] + if sp: + self.normsubpaths = self.split(sp[:1])[0].normsubpaths + + def shortenpath(self, dists): + """shortens a path by the given distances""" + + # XXX later, this should be done by extended boxes instead of intersecting with circles + # cut off the start of self + center = self.atbegin_pt() + cutpath = path.circle_pt(center[0], center[1], dists[0]) + try: + cutpath = cutpath.normpath() + except normpath.NormpathException: + pass + else: + sp = self.intersect(cutpath)[0] + self.normsubpaths = self.split(sp[-1:])[1].normsubpaths + + # cut off the end of self + center = self.atend_pt() + cutpath = path.circle_pt(center[0], center[1], dists[1]) + try: + cutpath = cutpath.normpath() + except normpath.NormpathException: + pass + else: + sp = self.intersect(cutpath)[0] + if sp: + self.normsubpaths = self.split(sp[:1])[0].normsubpaths + + +################ +## classes +################ + + +class line_pt(connector_pt): + + def __init__(self, box1, box2, boxdists=[0,0]): + + self.box1 = box1 + self.box2 = box2 + + connector_pt.__init__(self, + [path.normsubpath([path.normline_pt(self.box1.center[0], self.box1.center[1], + self.box2.center[0], self.box2.center[1])], closed=0)]) + + self.omitends(box1, box2) + self.shortenpath(boxdists) + + +class arc_pt(connector_pt): + + def __init__(self, box1, box2, relangle=45, + absbulge=None, relbulge=None, boxdists=[0,0]): + + # the deviation of arc from the straight line can be specified: + # 1. By an angle between a straight line and the arc + # This angle is measured at the centers of the box. + # 2. By the largest normal distance between line and arc: absbulge + # or, equivalently, by the bulge relative to the length of the + # straight line from center to center. + # Only one can be used. + + self.box1 = box1 + self.box2 = box2 + + tangent = (self.box2.center[0] - self.box1.center[0], + self.box2.center[1] - self.box1.center[1]) + distance = hypot(*tangent) + tangent = tangent[0] / distance, tangent[1] / distance + + if relbulge is not None or absbulge is not None: + # usage of bulge overrides the relangle parameter + bulge = 0 + if absbulge is not None: + bulge += absbulge + if relbulge is not None: + bulge += relbulge*distance + else: + # otherwise use relangle, which should be present + bulge = 0.5 * distance * math.tan(0.5*radians(relangle)) + + if abs(bulge) < normpath._epsilon: + # fallback solution for too straight arcs + connector_pt.__init__(self, + [path.normsubpath([path.normline_pt(*(self.box1.center+self.box2.center))], closed=0)]) + else: + radius = abs(0.5 * (bulge + 0.25 * distance**2 / bulge)) + centerdist = mathutils.sign(bulge) * (radius - abs(bulge)) + center = (0.5 * (self.box1.center[0] + self.box2.center[0]) + tangent[1]*centerdist, + 0.5 * (self.box1.center[1] + self.box2.center[1]) - tangent[0]*centerdist) + angle1 = atan2(self.box1.center[1] - center[1], self.box1.center[0] - center[0]) + angle2 = atan2(self.box2.center[1] - center[1], self.box2.center[0] - center[0]) + + if bulge > 0: + connectorpath = path.path(path.moveto_pt(*self.box1.center), + path.arcn_pt(center[0], center[1], radius, degrees(angle1), degrees(angle2))) + connector_pt.__init__(self, connectorpath.normpath().normsubpaths) + else: + connectorpath = path.path(path.moveto_pt(*self.box1.center), + path.arc_pt(center[0], center[1], radius, degrees(angle1), degrees(angle2))) + connector_pt.__init__(self, connectorpath.normpath().normsubpaths) + + self.omitends(box1, box2) + self.shortenpath(boxdists) + + +class curve_pt(connector_pt): + + def __init__(self, box1, box2, + relangle1=45, relangle2=45, + absangle1=None, absangle2=None, + absbulge=0, relbulge=0.39, boxdists=[0,0]): + + # The deviation of the curve from a straight line can be specified: + # A. By an angle at each center + # These angles are either absolute angles with origin at the positive x-axis + # or the relative angle with origin at the straight connection line + # B. By the (expected) largest normal distance between line and arc: absbulge + # and/or by the (expected) bulge relative to the length of the + # straight line from center to center. + # Here, we need both informations. + # + # a curve with relbulge=0.39 and relangle1,2=45 leads + # approximately to the arc with angle=45 + + self.box1 = box1 + self.box2 = box2 + + rel = (self.box2.center[0] - self.box1.center[0], + self.box2.center[1] - self.box1.center[1]) + distance = hypot(*rel) + # absolute angle of the straight connection + dangle = atan2(rel[1], rel[0]) + + # calculate the armlength and absolute angles for the control points: + # absolute and relative bulges are added + bulge = abs(distance*relbulge + absbulge) + + if absangle1 is not None: + angle1 = radians(absangle1) + else: + angle1 = dangle + radians(relangle1) + if absangle2 is not None: + angle2 = radians(absangle2) + else: + angle2 = dangle + radians(relangle2) + + # get the control points + control1 = (cos(angle1), sin(angle1)) + control2 = (cos(angle2), sin(angle2)) + control1 = (self.box1.center[0] + control1[0] * bulge, self.box1.center[1] + control1[1] * bulge) + control2 = (self.box2.center[0] - control2[0] * bulge, self.box2.center[1] - control2[1] * bulge) + + connector_pt.__init__(self, + [path.normsubpath([path.normcurve_pt(*(self.box1.center + + control1 + + control2 + self.box2.center))], 0)]) + + self.omitends(box1, box2) + self.shortenpath(boxdists) + + +class twolines_pt(connector_pt): + + def __init__(self, box1, box2, + absangle1=None, absangle2=None, + relangle1=None, relangle2=None, relangleM=None, + length1=None, length2=None, + bezierradius=None, beziersoftness=1, + arcradius=None, + boxdists=[0,0]): + + # The connection with two lines can be done in the following ways: + # 1. an angle at each box-center + # 2. two armlengths (if they are long enough) + # 3. angle and armlength at the same box + # 4. angle and armlength at different boxes + # 5. one armlength and the angle between the arms + # + # Angles at the box-centers can be relative or absolute + # The angle in the middle is always relative + # lengths are always absolute + + self.box1 = box1 + self.box2 = box2 + + begin = self.box1.center + end = self.box2.center + rel = (self.box2.center[0] - self.box1.center[0], + self.box2.center[1] - self.box1.center[1]) + distance = hypot(*rel) + dangle = atan2(rel[1], rel[0]) + + # find out what arguments are given: + if relangle1 is not None: relangle1 = radians(relangle1) + if relangle2 is not None: relangle2 = radians(relangle2) + if relangleM is not None: relangleM = radians(relangleM) + # absangle has priority over relangle: + if absangle1 is not None: relangle1 = dangle - radians(absangle1) + if absangle2 is not None: relangle2 = math.pi - dangle + radians(absangle2) + + # check integrity of arguments + no_angles, no_lengths=0,0 + for anangle in (relangle1, relangle2, relangleM): + if anangle is not None: no_angles += 1 + for alength in (length1, length2): + if alength is not None: no_lengths += 1 + + if no_angles + no_lengths != 2: + raise NotImplementedError, "Please specify exactly two angles or lengths" + + # calculate necessary angles and armlengths + # always length1 and relangle1 + + # the case with two given angles + # use the "sine-theorem" for calculating length1 + if no_angles == 2: + if relangle1 is None: relangle1 = math.pi - relangle2 - relangleM + elif relangle2 is None: relangle2 = math.pi - relangle1 - relangleM + elif relangleM is None: relangleM = math.pi - relangle1 - relangle2 + length1 = distance * abs(sin(relangle2)/sin(relangleM)) + middle = self._middle_a(begin, dangle, length1, relangle1) + # the case with two given lengths + # uses the "cosine-theorem" for calculating length1 + elif no_lengths == 2: + relangle1 = acos((distance**2 + length1**2 - length2**2) / (2.0*distance*length1)) + middle = self._middle_a(begin, dangle, length1, relangle1) + # the case with one length and one angle + else: + if relangle1 is not None: + if length1 is not None: + middle = self._middle_a(begin, dangle, length1, relangle1) + elif length2 is not None: + length1 = self._missinglength(length2, distance, relangle1) + middle = self._middle_a(begin, dangle, length1, relangle1) + elif relangle2 is not None: + if length1 is not None: + length2 = self._missinglength(length1, distance, relangle2) + middle = self._middle_b(end, dangle, length2, relangle2) + elif length2 is not None: + middle = self._middle_b(end, dangle, length2, relangle2) + elif relangleM is not None: + if length1 is not None: + length2 = self._missinglength(distance, length1, relangleM) + relangle1 = acos((distance**2 + length1**2 - length2**2) / (2.0*distance*length1)) + middle = self._middle_a(begin, dangle, length1, relangle1) + elif length2 is not None: + length1 = self._missinglength(distance, length2, relangleM) + relangle1 = acos((distance**2 + length1**2 - length2**2) / (2.0*distance*length1)) + middle = self._middle_a(begin, dangle, length1, relangle1) + else: + raise NotImplementedError, "I found a strange combination of arguments" + + connectorpath = path.path(path.moveto_pt(*self.box1.center), + path.lineto_pt(*middle), + path.lineto_pt(*self.box2.center)) + connector_pt.__init__(self, connectorpath.normpath().normsubpaths) + + self.omitends(box1, box2) + self.shortenpath(boxdists) + + def _middle_a(self, begin, dangle, length1, angle1): + a = dangle - angle1 + dir = cos(a), sin(a) + return begin[0] + length1*dir[0], begin[1] + length1*dir[1] + + def _middle_b(self, end, dangle, length2, angle2): + # a = -math.pi + dangle + angle2 + return self._middle_a(end, -math.pi+dangle, length2, -angle2) + + def _missinglength(self, lenA, lenB, angleA): + # calculate lenC, where side A and angleA are opposite + tmp1 = lenB * cos(angleA) + tmp2 = sqrt(tmp1**2 - lenB**2 + lenA**2) + if tmp1 > tmp2: return tmp1 - tmp2 + return tmp1 + tmp2 + + + +class line(line_pt): + + """a line is the straight connector between the centers of two boxes""" + + def __init__(self, box1, box2, boxdists=(0,0)): + line_pt.__init__(self, box1, box2, boxdists=map(unit.topt, boxdists)) + + +class curve(curve_pt): + + """a curve is the curved connector between the centers of two boxes. + The constructor needs both angle and bulge""" + + + def __init__(self, box1, box2, + relangle1=45, relangle2=45, + absangle1=None, absangle2=None, + absbulge=0, relbulge=0.39, + boxdists=[0,0]): + curve_pt.__init__(self, box1, box2, + relangle1=relangle1, relangle2=relangle2, + absangle1=absangle1, absangle2=absangle2, + absbulge=unit.topt(absbulge), relbulge=relbulge, + boxdists=map(unit.topt, boxdists)) + +class arc(arc_pt): + + """an arc is a round connector between the centers of two boxes. + The constructor gets + either an angle in (-pi,pi) + or a bulge parameter in (-distance, distance) + (relbulge and absbulge are added)""" + + def __init__(self, box1, box2, relangle=45, + absbulge=None, relbulge=None, boxdists=[0,0]): + if absbulge is not None: + absbulge = unit.topt(absbulge) + arc_pt.__init__(self, box1, box2, + relangle=relangle, + absbulge=absbulge, relbulge=relbulge, + boxdists=map(unit.topt, boxdists)) + + +class twolines(twolines_pt): + + """a twolines is a connector consisting of two straight lines. + The construcor takes a combination of angles and lengths: + either two angles (relative or absolute) + or two lenghts + or one length and one angle""" + + def __init__(self, box1, box2, + absangle1=None, absangle2=None, + relangle1=None, relangle2=None, relangleM=None, + length1=None, length2=None, + bezierradius=None, beziersoftness=1, + arcradius=None, + boxdists=[0,0]): + if length1 is not None: + length1 = unit.topt(length1) + if length2 is not None: + length2 = unit.topt(length2) + if bezierradius is not None: + bezierradius = unit.topt(bezierradius) + if arcradius is not None: + arcradius = unit.topt(arcradius) + twolines_pt.__init__(self, box1, box2, + absangle1=absangle1, absangle2=absangle2, + relangle1=relangle1, relangle2=relangle2, + relangleM=relangleM, + length1=length1, length2=length2, + bezierradius=bezierradius, beziersoftness=1, + arcradius=arcradius, + boxdists=map(unit.topt, boxdists)) + + + diff --git a/compiler/gdsMill/pyx/deco.py b/compiler/gdsMill/pyx/deco.py new file mode 100644 index 00000000..72c18a3b --- /dev/null +++ b/compiler/gdsMill/pyx/deco.py @@ -0,0 +1,577 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# TODO: +# - should we improve on the arc length -> arg parametrization routine or +# should we at least factor it out? + +from __future__ import nested_scopes + +import sys, math +import attr, canvas, color, path, normpath, style, trafo, unit + +try: + from math import radians +except ImportError: + # fallback implementation for Python 2.1 and below + def radians(x): return x*math.pi/180 + +class _marker: pass + +# +# Decorated path +# + +class decoratedpath(canvas.canvasitem): + """Decorated path + + The main purpose of this class is during the drawing + (stroking/filling) of a path. It collects attributes for the + stroke and/or fill operations. + """ + + def __init__(self, path, strokepath=None, fillpath=None, + styles=None, strokestyles=None, fillstyles=None, + ornaments=None): + + self.path = path + + # global style for stroking and filling and subdps + self.styles = styles + + # styles which apply only for stroking and filling + self.strokestyles = strokestyles + self.fillstyles = fillstyles + + # the decoratedpath can contain additional elements of the + # path (ornaments), e.g., arrowheads. + if ornaments is None: + self.ornaments = canvas.canvas() + else: + self.ornaments = ornaments + + self.nostrokeranges = None + + def ensurenormpath(self): + """convert self.path into a normpath""" + assert self.nostrokeranges is None or isinstance(self.path, path.normpath), "you don't understand what you are doing" + self.path = self.path.normpath() + + def excluderange(self, begin, end): + assert isinstance(self.path, path.normpath), "you don't understand what this is about" + if self.nostrokeranges is None: + self.nostrokeranges = [(begin, end)] + else: + ibegin = 0 + while ibegin < len(self.nostrokeranges) and self.nostrokeranges[ibegin][1] < begin: + ibegin += 1 + + if ibegin == len(self.nostrokeranges): + self.nostrokeranges.append((begin, end)) + return + + iend = len(self.nostrokeranges) - 1 + while 0 <= iend and end < self.nostrokeranges[iend][0]: + iend -= 1 + + if iend == -1: + self.nostrokeranges.insert(0, (begin, end)) + return + + if self.nostrokeranges[ibegin][0] < begin: + begin = self.nostrokeranges[ibegin][0] + if end < self.nostrokeranges[iend][1]: + end = self.nostrokeranges[iend][1] + + self.nostrokeranges[ibegin:iend+1] = [(begin, end)] + + def bbox(self): + pathbbox = self.path.bbox() + ornamentsbbox = self.ornaments.bbox() + if ornamentsbbox is not None: + return ornamentsbbox + pathbbox + else: + return pathbbox + + def strokepath(self): + if self.nostrokeranges: + splitlist = [] + for begin, end in self.nostrokeranges: + splitlist.append(begin) + splitlist.append(end) + split = self.path.split(splitlist) + # XXX properly handle closed paths? + result = split[0] + for i in range(2, len(split), 2): + result += split[i] + return result + else: + return self.path + + def processPS(self, file, writer, context, registry, bbox): + # draw (stroke and/or fill) the decoratedpath on the canvas + # while trying to produce an efficient output, e.g., by + # not writing one path two times + + # small helper + def _writestyles(styles, context, registry, bbox): + for style in styles: + style.processPS(file, writer, context, registry, bbox) + + if self.strokestyles is None and self.fillstyles is None: + if not len(self.ornaments): + raise RuntimeError("Path neither to be stroked nor filled nor decorated in another way") + # just draw additional elements of decoratedpath + self.ornaments.processPS(file, writer, context, registry, bbox) + return + + strokepath = self.strokepath() + fillpath = self.path + + # apply global styles + if self.styles: + file.write("gsave\n") + context = context() + _writestyles(self.styles, context, registry, bbox) + + if self.fillstyles is not None: + file.write("newpath\n") + fillpath.outputPS(file, writer) + + if self.strokestyles is not None and strokepath is fillpath: + # do efficient stroking + filling if respective paths are identical + file.write("gsave\n") + + if self.fillstyles: + _writestyles(self.fillstyles, context(), registry, bbox) + + file.write("fill\n") + file.write("grestore\n") + + acontext = context() + if self.strokestyles: + file.write("gsave\n") + _writestyles(self.strokestyles, acontext, registry, bbox) + + file.write("stroke\n") + # take linewidth into account for bbox when stroking a path + bbox += strokepath.bbox().enlarged_pt(0.5*acontext.linewidth_pt) + + if self.strokestyles: + file.write("grestore\n") + else: + # only fill fillpath - for the moment + if self.fillstyles: + file.write("gsave\n") + _writestyles(self.fillstyles, context(), registry, bbox) + + file.write("fill\n") + bbox += fillpath.bbox() + + if self.fillstyles: + file.write("grestore\n") + + if self.strokestyles is not None and (strokepath is not fillpath or self.fillstyles is None): + # this is the only relevant case still left + # Note that a possible stroking has already been done. + acontext = context() + if self.strokestyles: + file.write("gsave\n") + _writestyles(self.strokestyles, acontext, registry, bbox) + + file.write("newpath\n") + strokepath.outputPS(file, writer) + file.write("stroke\n") + # take linewidth into account for bbox when stroking a path + bbox += strokepath.bbox().enlarged_pt(0.5*acontext.linewidth_pt) + + if self.strokestyles: + file.write("grestore\n") + + # now, draw additional elements of decoratedpath + self.ornaments.processPS(file, writer, context, registry, bbox) + + # restore global styles + if self.styles: + file.write("grestore\n") + + def processPDF(self, file, writer, context, registry, bbox): + # draw (stroke and/or fill) the decoratedpath on the canvas + + def _writestyles(styles, context, registry, bbox): + for style in styles: + style.processPDF(file, writer, context, registry, bbox) + + def _writestrokestyles(strokestyles, context, registry, bbox): + context.fillattr = 0 + for style in strokestyles: + style.processPDF(file, writer, context, registry, bbox) + context.fillattr = 1 + + def _writefillstyles(fillstyles, context, registry, bbox): + context.strokeattr = 0 + for style in fillstyles: + style.processPDF(file, writer, context, registry, bbox) + context.strokeattr = 1 + + if self.strokestyles is None and self.fillstyles is None: + if not len(self.ornaments): + raise RuntimeError("Path neither to be stroked nor filled nor decorated in another way") + # just draw additional elements of decoratedpath + self.ornaments.processPDF(file, writer, context, registry, bbox) + return + + strokepath = self.strokepath() + fillpath = self.path + + # apply global styles + if self.styles: + file.write("q\n") # gsave + context = context() + _writestyles(self.styles, context, registry, bbox) + + if self.fillstyles is not None: + fillpath.outputPDF(file, writer) + + if self.strokestyles is not None and strokepath is fillpath: + # do efficient stroking + filling + file.write("q\n") # gsave + acontext = context() + + if self.fillstyles: + _writefillstyles(self.fillstyles, acontext, registry, bbox) + if self.strokestyles: + _writestrokestyles(self.strokestyles, acontext, registry, bbox) + + file.write("B\n") # both stroke and fill + # take linewidth into account for bbox when stroking a path + bbox += strokepath.bbox().enlarged_pt(0.5*acontext.linewidth_pt) + + file.write("Q\n") # grestore + else: + # only fill fillpath - for the moment + if self.fillstyles: + file.write("q\n") # gsave + _writefillstyles(self.fillstyles, context(), registry, bbox) + + file.write("f\n") # fill + bbox += fillpath.bbox() + + if self.fillstyles: + file.write("Q\n") # grestore + + if self.strokestyles is not None and (strokepath is not fillpath or self.fillstyles is None): + # this is the only relevant case still left + # Note that a possible stroking has already been done. + acontext = context() + + if self.strokestyles: + file.write("q\n") # gsave + _writestrokestyles(self.strokestyles, acontext, registry, bbox) + + strokepath.outputPDF(file, writer) + file.write("S\n") # stroke + # take linewidth into account for bbox when stroking a path + bbox += strokepath.bbox().enlarged_pt(0.5*acontext.linewidth_pt) + + if self.strokestyles: + file.write("Q\n") # grestore + + # now, draw additional elements of decoratedpath + self.ornaments.processPDF(file, writer, context, registry, bbox) + + # restore global styles + if self.styles: + file.write("Q\n") # grestore + +# +# Path decorators +# + +class deco: + + """decorators + + In contrast to path styles, path decorators depend on the concrete + path to which they are applied. In particular, they don't make + sense without any path and can thus not be used in canvas.set! + + """ + + def decorate(self, dp, texrunner): + """apply a style to a given decoratedpath object dp + + decorate accepts a decoratedpath object dp, applies PathStyle + by modifying dp in place. + """ + + pass + +# +# stroked and filled: basic decos which stroked and fill, +# respectively the path +# + +class _stroked(deco, attr.exclusiveattr): + + """stroked is a decorator, which draws the outline of the path""" + + def __init__(self, styles=[]): + attr.exclusiveattr.__init__(self, _stroked) + self.styles = attr.mergeattrs(styles) + attr.checkattrs(self.styles, [style.strokestyle]) + + def __call__(self, styles=[]): + # XXX or should we also merge self.styles + return _stroked(styles) + + def decorate(self, dp, texrunner): + if dp.strokestyles is not None: + raise RuntimeError("Cannot stroke an already stroked path") + dp.strokestyles = self.styles + +stroked = _stroked() +stroked.clear = attr.clearclass(_stroked) + + +class _filled(deco, attr.exclusiveattr): + + """filled is a decorator, which fills the interior of the path""" + + def __init__(self, styles=[]): + attr.exclusiveattr.__init__(self, _filled) + self.styles = attr.mergeattrs(styles) + attr.checkattrs(self.styles, [style.fillstyle]) + + def __call__(self, styles=[]): + # XXX or should we also merge self.styles + return _filled(styles) + + def decorate(self, dp, texrunner): + if dp.fillstyles is not None: + raise RuntimeError("Cannot fill an already filled path") + dp.fillstyles = self.styles + +filled = _filled() +filled.clear = attr.clearclass(_filled) + +# +# Arrows +# + +# helper function which constructs the arrowhead + +def _arrowhead(anormpath, arclenfrombegin, direction, size, angle, constrictionlen): + + """helper routine, which returns an arrowhead from a given anormpath + + - arclenfrombegin: position of arrow in arc length from the start of the path + - direction: +1 for an arrow pointing along the direction of anormpath or + -1 for an arrow pointing opposite to the direction of normpath + - size: size of the arrow as arc length + - angle. opening angle + - constrictionlen: None (no constriction) or arc length of constriction. + """ + + # arc length and coordinates of tip + tx, ty = anormpath.at(arclenfrombegin) + + # construct the template for the arrow by cutting the path at the + # corresponding length + arrowtemplate = anormpath.split([arclenfrombegin, arclenfrombegin - direction * size])[1] + + # from this template, we construct the two outer curves of the arrow + arrowl = arrowtemplate.transformed(trafo.rotate(-angle/2.0, tx, ty)) + arrowr = arrowtemplate.transformed(trafo.rotate( angle/2.0, tx, ty)) + + # now come the joining backward parts + if constrictionlen is not None: + # constriction point (cx, cy) lies on path + cx, cy = anormpath.at(arclenfrombegin - direction * constrictionlen) + arrowcr= path.line(*(arrowr.atend() + (cx,cy))) + arrow = arrowl.reversed() << arrowr << arrowcr + else: + arrow = arrowl.reversed() << arrowr + + arrow[-1].close() + + return arrow + + +_base = 6 * unit.v_pt + +class arrow(deco, attr.attr): + + """arrow is a decorator which adds an arrow to either side of the path""" + + def __init__(self, attrs=[], pos=1, reversed=0, size=_base, angle=45, constriction=0.8): + self.attrs = attr.mergeattrs([style.linestyle.solid, filled] + attrs) + attr.checkattrs(self.attrs, [deco, style.fillstyle, style.strokestyle]) + self.pos = pos + self.reversed = reversed + self.size = size + self.angle = angle + self.constriction = constriction + + def __call__(self, attrs=None, pos=None, reversed=None, size=None, angle=None, constriction=_marker): + if attrs is None: + attrs = self.attrs + if pos is None: + pos = self.pos + if reversed is None: + reversed = self.reversed + if size is None: + size = self.size + if angle is None: + angle = self.angle + if constriction is _marker: + constriction = self.constriction + return arrow(attrs=attrs, pos=pos, reversed=reversed, size=size, angle=angle, constriction=constriction) + + def decorate(self, dp, texrunner): + dp.ensurenormpath() + anormpath = dp.path + + # calculate absolute arc length of constricition + # Note that we have to correct this length because the arrowtemplates are rotated + # by self.angle/2 to the left and right. Hence, if we want no constriction, i.e., for + # self.constriction = 1, we actually have a length which is approximately shorter + # by the given geometrical factor. + if self.constriction is not None: + constrictionlen = arrowheadconstrictionlen = self.size * self.constriction * math.cos(radians(self.angle/2.0)) + else: + # if we do not want a constriction, i.e. constriction is None, we still + # need constrictionlen for cutting the path + constrictionlen = self.size * 1 * math.cos(radians(self.angle/2.0)) + arrowheadconstrictionlen = None + + arclenfrombegin = self.pos * anormpath.arclen() + direction = self.reversed and -1 or 1 + arrowhead = _arrowhead(anormpath, arclenfrombegin, direction, self.size, self.angle, arrowheadconstrictionlen) + + # add arrowhead to decoratedpath + dp.ornaments.draw(arrowhead, self.attrs) + + # exlude part of the path from stroking when the arrow is strictly at the begin or the end + if self.pos == 0 and self.reversed: + dp.excluderange(0, min(self.size, constrictionlen)) + elif self.pos == 1 and not self.reversed: + dp.excluderange(anormpath.end() - min(self.size, constrictionlen), anormpath.end()) + +arrow.clear = attr.clearclass(arrow) + +# arrows at begin of path +barrow = arrow(pos=0, reversed=1) +barrow.SMALL = barrow(size=_base/math.sqrt(64)) +barrow.SMALl = barrow(size=_base/math.sqrt(32)) +barrow.SMAll = barrow(size=_base/math.sqrt(16)) +barrow.SMall = barrow(size=_base/math.sqrt(8)) +barrow.Small = barrow(size=_base/math.sqrt(4)) +barrow.small = barrow(size=_base/math.sqrt(2)) +barrow.normal = barrow(size=_base) +barrow.large = barrow(size=_base*math.sqrt(2)) +barrow.Large = barrow(size=_base*math.sqrt(4)) +barrow.LArge = barrow(size=_base*math.sqrt(8)) +barrow.LARge = barrow(size=_base*math.sqrt(16)) +barrow.LARGe = barrow(size=_base*math.sqrt(32)) +barrow.LARGE = barrow(size=_base*math.sqrt(64)) + +# arrows at end of path +earrow = arrow() +earrow.SMALL = earrow(size=_base/math.sqrt(64)) +earrow.SMALl = earrow(size=_base/math.sqrt(32)) +earrow.SMAll = earrow(size=_base/math.sqrt(16)) +earrow.SMall = earrow(size=_base/math.sqrt(8)) +earrow.Small = earrow(size=_base/math.sqrt(4)) +earrow.small = earrow(size=_base/math.sqrt(2)) +earrow.normal = earrow(size=_base) +earrow.large = earrow(size=_base*math.sqrt(2)) +earrow.Large = earrow(size=_base*math.sqrt(4)) +earrow.LArge = earrow(size=_base*math.sqrt(8)) +earrow.LARge = earrow(size=_base*math.sqrt(16)) +earrow.LARGe = earrow(size=_base*math.sqrt(32)) +earrow.LARGE = earrow(size=_base*math.sqrt(64)) + + +class text(deco, attr.attr): + """a simple text decorator""" + + def __init__(self, text, textattrs=[], angle=0, textdist=0.2, + relarclenpos=0.5, arclenfrombegin=None, arclenfromend=None, + texrunner=None): + if arclenfrombegin is not None and arclenfromend is not None: + raise ValueError("either set arclenfrombegin or arclenfromend") + self.text = text + self.textattrs = textattrs + self.angle = angle + self.textdist = textdist + self.relarclenpos = relarclenpos + self.arclenfrombegin = arclenfrombegin + self.arclenfromend = arclenfromend + self.texrunner = texrunner + + def decorate(self, dp, texrunner): + if self.texrunner: + texrunner = self.texrunner + import text as textmodule + textattrs = attr.mergeattrs([textmodule.halign.center, textmodule.vshift.mathaxis] + self.textattrs) + + dp.ensurenormpath() + if self.arclenfrombegin is not None: + x, y = dp.path.at(dp.path.begin() + self.arclenfrombegin) + elif self.arclenfromend is not None: + x, y = dp.path.at(dp.path.end() - self.arclenfromend) + else: + # relarcpos is used, when neither arcfrombegin nor arcfromend is given + x, y = dp.path.at(self.relarclenpos * dp.path.arclen()) + + t = texrunner.text(x, y, self.text, textattrs) + t.linealign(self.textdist, math.cos(self.angle*math.pi/180), math.sin(self.angle*math.pi/180)) + dp.ornaments.insert(t) + + +class shownormpath(deco, attr.attr): + + def decorate(self, dp, texrunner): + r_pt = 2 + dp.ensurenormpath() + for normsubpath in dp.path.normsubpaths: + for i, normsubpathitem in enumerate(normsubpath.normsubpathitems): + if isinstance(normsubpathitem, normpath.normcurve_pt): + dp.ornaments.stroke(normpath.normpath([normpath.normsubpath([normsubpathitem])]), [color.rgb.green]) + else: + dp.ornaments.stroke(normpath.normpath([normpath.normsubpath([normsubpathitem])]), [color.rgb.blue]) + for normsubpath in dp.path.normsubpaths: + for i, normsubpathitem in enumerate(normsubpath.normsubpathitems): + if isinstance(normsubpathitem, normpath.normcurve_pt): + dp.ornaments.stroke(path.line_pt(normsubpathitem.x0_pt, normsubpathitem.y0_pt, normsubpathitem.x1_pt, normsubpathitem.y1_pt), [style.linestyle.dashed, color.rgb.red]) + dp.ornaments.stroke(path.line_pt(normsubpathitem.x2_pt, normsubpathitem.y2_pt, normsubpathitem.x3_pt, normsubpathitem.y3_pt), [style.linestyle.dashed, color.rgb.red]) + dp.ornaments.draw(path.circle_pt(normsubpathitem.x1_pt, normsubpathitem.y1_pt, r_pt), [filled([color.rgb.red])]) + dp.ornaments.draw(path.circle_pt(normsubpathitem.x2_pt, normsubpathitem.y2_pt, r_pt), [filled([color.rgb.red])]) + for normsubpath in dp.path.normsubpaths: + for i, normsubpathitem in enumerate(normsubpath.normsubpathitems): + if not i: + x_pt, y_pt = normsubpathitem.atbegin_pt() + dp.ornaments.draw(path.circle_pt(x_pt, y_pt, r_pt), [filled]) + x_pt, y_pt = normsubpathitem.atend_pt() + dp.ornaments.draw(path.circle_pt(x_pt, y_pt, r_pt), [filled]) diff --git a/compiler/gdsMill/pyx/deformer.py b/compiler/gdsMill/pyx/deformer.py new file mode 100644 index 00000000..fdde6b67 --- /dev/null +++ b/compiler/gdsMill/pyx/deformer.py @@ -0,0 +1,1371 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2003-2006 Michael Schindler +# Copyright (C) 2003-2005 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import nested_scopes + +import math, warnings +import attr, mathutils, path, normpath, unit, color +from path import degrees, radians + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + + +# specific exception for an invalid parameterization point +# used in parallel +class InvalidParamException(Exception): + + def __init__(self, param): + self.normsubpathitemparam = param + +def curvescontrols_from_endlines_pt(B, tangent1, tangent2, r1, r2, softness): # <<< + # calculates the parameters for two bezier curves connecting two lines (curvature=0) + # starting at B - r1*tangent1 + # ending at B + r2*tangent2 + # + # Takes the corner B + # and two tangent vectors heading to and from B + # and two radii r1 and r2: + # All arguments must be in Points + # Returns the seven control points of the two bezier curves: + # - start d1 + # - control points g1 and f1 + # - midpoint e + # - control points f2 and g2 + # - endpoint d2 + + # make direction vectors d1: from B to A + # d2: from B to C + d1 = -tangent1[0] / math.hypot(*tangent1), -tangent1[1] / math.hypot(*tangent1) + d2 = tangent2[0] / math.hypot(*tangent2), tangent2[1] / math.hypot(*tangent2) + + # 0.3192 has turned out to be the maximum softness available + # for straight lines ;-) + f = 0.3192 * softness + g = (15.0 * f + math.sqrt(-15.0*f*f + 24.0*f))/12.0 + + # make the control points of the two bezier curves + f1 = B[0] + f * r1 * d1[0], B[1] + f * r1 * d1[1] + f2 = B[0] + f * r2 * d2[0], B[1] + f * r2 * d2[1] + g1 = B[0] + g * r1 * d1[0], B[1] + g * r1 * d1[1] + g2 = B[0] + g * r2 * d2[0], B[1] + g * r2 * d2[1] + d1 = B[0] + r1 * d1[0], B[1] + r1 * d1[1] + d2 = B[0] + r2 * d2[0], B[1] + r2 * d2[1] + e = 0.5 * (f1[0] + f2[0]), 0.5 * (f1[1] + f2[1]) + + return (d1, g1, f1, e, f2, g2, d2) +# >>> + +def controldists_from_endgeometry_pt(A, B, tangA, tangB, curvA, curvB, allownegative=0): # <<< + + """For a curve with given tangents and curvatures at the endpoints this gives the distances between the controlpoints + + This helper routine returns a list of two distances between the endpoints and the + corresponding control points of a (cubic) bezier curve that has + prescribed tangents tangentA, tangentB and curvatures curvA, curvB at the + end points. + + Note: The returned distances are not always positive. + But only positive values are geometrically correct, so please check! + The outcome is sorted so that the first entry is expected to be the + most reasonable one + """ + debug = 0 + + def test_divisions(T, D, E, AB, curvA, curvB, debug):# <<< + + def is_zero(x): + try: + 1.0 / x + except ZeroDivisionError: + return 1 + return 0 + + T_is_zero = is_zero(T) + curvA_is_zero = is_zero(curvA) + curvB_is_zero = is_zero(curvB) + + if T_is_zero: + if curvA_is_zero: + assert abs(D) < 1.0e-10 + a = AB / 3.0 + if curvB_is_zero: + assert abs(E) < 1.0e-10 + b = AB / 3.0 + else: + b = math.sqrt(abs(E / (1.5 * curvB))) * mathutils.sign(E*curvB) + else: + a = math.sqrt(abs(D / (1.5 * curvA))) * mathutils.sign(D*curvA) + if curvB_is_zero: + assert abs(E) < 1.0e-10 + b = AB / 3.0 + else: + b = math.sqrt(abs(E / (1.5 * curvB))) * mathutils.sign(E*curvB) + else: + if curvA_is_zero: + b = D / T + a = (E - 1.5*curvB*b*abs(b)) / T + elif curvB_is_zero: + a = E / T + b = (D - 1.5*curvA*a*abs(a)) / T + else: + return [] + + if debug: + print "fallback with exact zero value" + return [(a, b)] + # >>> + def fallback_smallT(T, D, E, AB, curvA, curvB, threshold, debug):# <<< + a = math.sqrt(abs(D / (1.5 * curvA))) * mathutils.sign(D*curvA) + b = math.sqrt(abs(E / (1.5 * curvB))) * mathutils.sign(E*curvB) + q1 = min(abs(1.5*a*a*curvA), abs(D)) + q2 = min(abs(1.5*b*b*curvB), abs(E)) + if (a >= 0 and b >= 0 and + abs(b*T) < threshold * q1 and abs(1.5*a*abs(a)*curvA - D) < threshold * q1 and + abs(a*T) < threshold * q2 and abs(1.5*b*abs(b)*curvB - E) < threshold * q2): + if debug: + print "fallback with T approx 0" + return [(a, b)] + return [] + # >>> + def fallback_smallcurv(T, D, E, AB, curvA, curvB, threshold, debug):# <<< + result = [] + + # is curvB approx zero? + a = E / T + b = (D - 1.5*curvA*a*abs(a)) / T + if (a >= 0 and b >= 0 and + abs(1.5*b*b*curvB) < threshold * min(abs(a*T), abs(E)) and + abs(a*T - E) < threshold * min(abs(a*T), abs(E))): + if debug: + print "fallback with curvB approx 0" + result.append((a, b)) + + # is curvA approx zero? + b = D / T + a = (E - 1.5*curvB*b*abs(b)) / T + if (a >= 0 and b >= 0 and + abs(1.5*a*a*curvA) < threshold * min(abs(b*T), abs(D)) and + abs(b*T - D) < threshold * min(abs(b*T), abs(D))): + if debug: + print "fallback with curvA approx 0" + result.append((a, b)) + + return result + # >>> + def findnearest(x, ys): # <<< + I = 0 + Y = ys[I] + mindist = abs(x - Y) + + # find the value in ys which is nearest to x + for i, y in enumerate(ys[1:]): + dist = abs(x - y) + if dist < mindist: + I, Y, mindist = i, y, dist + + return I, Y + # >>> + + # some shortcuts + T = tangA[0] * tangB[1] - tangA[1] * tangB[0] + D = tangA[0] * (B[1]-A[1]) - tangA[1] * (B[0]-A[0]) + E = tangB[0] * (A[1]-B[1]) - tangB[1] * (A[0]-B[0]) + AB = math.hypot(A[0] - B[0], A[1] - B[1]) + + # try if one of the prefactors is exactly zero + testsols = test_divisions(T, D, E, AB, curvA, curvB, debug) + if testsols: + return testsols + + # The general case: + # we try to find all the zeros of the decoupled 4th order problem + # for the combined problem: + # The control points of a cubic Bezier curve are given by a, b: + # A, A + a*tangA, B - b*tangB, B + # for the derivation see /design/beziers.tex + # 0 = 1.5 a |a| curvA + b * T - D + # 0 = 1.5 b |b| curvB + a * T - E + # because of the absolute values we get several possibilities for the signs + # in the equation. We test all signs, also the invalid ones! + if allownegative: + signs = [(+1, +1), (-1, +1), (+1, -1), (-1, -1)] + else: + signs = [(+1, +1)] + + candidates_a = [] + candidates_b = [] + for sign_a, sign_b in signs: + coeffs_a = (sign_b*3.375*curvA*curvA*curvB, 0.0, -sign_b*sign_a*4.5*curvA*curvB*D, T**3, sign_b*1.5*curvB*D*D - T*T*E) + coeffs_b = (sign_a*3.375*curvA*curvB*curvB, 0.0, -sign_a*sign_b*4.5*curvA*curvB*E, T**3, sign_a*1.5*curvA*E*E - T*T*D) + candidates_a += [root for root in mathutils.realpolyroots(*coeffs_a) if sign_a*root >= 0] + candidates_b += [root for root in mathutils.realpolyroots(*coeffs_b) if sign_b*root >= 0] + solutions = [] + if candidates_a and candidates_b: + for a in candidates_a: + i, b = findnearest((D - 1.5*curvA*a*abs(a))/T, candidates_b) + solutions.append((a, b)) + + # try if there is an approximate solution + for thr in [1.0e-2, 1.0e-1]: + if not solutions: + solutions = fallback_smallT(T, D, E, AB, curvA, curvB, thr, debug) + if not solutions: + solutions = fallback_smallcurv(T, D, E, AB, curvA, curvB, thr, debug) + + # sort the solutions: the more reasonable values at the beginning + def mycmp(x,y): # <<< + # first the pairs that are purely positive, then all the pairs with some negative signs + # inside the two sets: sort by magnitude + sx = (x[0] > 0 and x[1] > 0) + sy = (y[0] > 0 and y[1] > 0) + + # experimental stuff: + # what criterion should be used for sorting ? + # + #errx = abs(1.5*curvA*x[0]*abs(x[0]) + x[1]*T - D) + abs(1.5*curvB*x[1]*abs(x[1]) + x[0]*T - E) + #erry = abs(1.5*curvA*y[0]*abs(y[0]) + y[1]*T - D) + abs(1.5*curvB*y[1]*abs(y[1]) + y[0]*T - E) + # # For each equation, a value like + # # abs(1.5*curvA*y[0]*abs(y[0]) + y[1]*T - D) / abs(curvA*(D - y[1]*T)) + # # indicates how good the solution is. In order to avoid the division, + # # we here multiply with all four denominators: + # errx = max(abs( (1.5*curvA*y[0]*abs(y[0]) + y[1]*T - D) * (curvB*(E - y[0]*T))*(curvA*(D - x[1]*T))*(curvB*(E - x[0]*T)) ), + # abs( (1.5*curvB*y[1]*abs(y[1]) + y[0]*T - E) * (curvA*(D - y[1]*T))*(curvA*(D - x[1]*T))*(curvB*(E - x[0]*T)) )) + # errx = max(abs( (1.5*curvA*x[0]*abs(x[0]) + x[1]*T - D) * (curvA*(D - y[1]*T))*(curvB*(E - y[0]*T))*(curvB*(E - x[0]*T)) ), + # abs( (1.5*curvB*x[1]*abs(x[1]) + x[0]*T - E) * (curvA*(D - y[1]*T))*(curvB*(E - y[0]*T))*(curvA*(D - x[1]*T)) )) + #errx = (abs(curvA*x[0]) - 1.0)**2 + (abs(curvB*x[1]) - 1.0)**2 + #erry = (abs(curvA*y[0]) - 1.0)**2 + (abs(curvB*y[1]) - 1.0)**2 + + errx = x[0]**2 + x[1]**2 + erry = y[0]**2 + y[1]**2 + + if sx == 1 and sy == 1: + # try to use longer solutions if there are any crossings in the control-arms + # the following combination yielded fewest sorting errors in test_bezier.py + t, s = intersection(A, B, tangA, tangB) + t, s = abs(t), abs(s) + if (t > 0 and t < x[0] and s > 0 and s < x[1]): + if (t > 0 and t < y[0] and s > 0 and s < y[1]): + # use the shorter one + return cmp(errx, erry) + else: + # use the longer one + return -1 + else: + if (t > 0 and t < y[0] and s > 0 and s < y[1]): + # use the longer one + return 1 + else: + # use the shorter one + return cmp(errx, erry) + #return cmp(x[0]**2 + x[1]**2, y[0]**2 + y[1]**2) + else: + return cmp(sy, sx) + # >>> + solutions.sort(mycmp) + + return solutions +# >>> + +def normcurve_from_endgeometry_pt(A, B, tangA, tangB, curvA, curvB): # <<< + a, b = controldists_from_endgeometry_pt(A, B, tangA, tangB, curvA, curvB)[0] + return normpath.normcurve_pt(A[0], A[1], + A[0] + a * tangA[0], A[1] + a * tangA[1], + B[0] - b * tangB[0], B[1] - b * tangB[1], B[0], B[1]) + # >>> + +def intersection(A, D, tangA, tangD): # <<< + + """returns the intersection parameters of two evens + + they are defined by: + x(t) = A + t * tangA + x(s) = D + s * tangD + """ + det = -tangA[0] * tangD[1] + tangA[1] * tangD[0] + try: + 1.0 / det + except ArithmeticError: + return None, None + + DA = D[0] - A[0], D[1] - A[1] + + t = (-tangD[1]*DA[0] + tangD[0]*DA[1]) / det + s = (-tangA[1]*DA[0] + tangA[0]*DA[1]) / det + + return t, s +# >>> + +class deformer(attr.attr): + + def deform (self, basepath): + return basepath + +class cycloid(deformer): # <<< + """Wraps a cycloid around a path. + + The outcome looks like a spring with the originalpath as the axis. + radius: radius of the cycloid + halfloops: number of halfloops + skipfirst/skiplast: undeformed end lines of the original path + curvesperhloop: + sign: start left (1) or right (-1) with the first halfloop + turnangle: angle of perspective on a (3D) spring + turnangle=0 will produce a sinus-like cycloid, + turnangle=90 will procude a row of connected circles + + """ + + def __init__(self, radius=0.5*unit.t_cm, halfloops=10, + skipfirst=1*unit.t_cm, skiplast=1*unit.t_cm, curvesperhloop=3, sign=1, turnangle=45): + self.skipfirst = skipfirst + self.skiplast = skiplast + self.radius = radius + self.halfloops = halfloops + self.curvesperhloop = curvesperhloop + self.sign = sign + self.turnangle = turnangle + + def __call__(self, radius=None, halfloops=None, + skipfirst=None, skiplast=None, curvesperhloop=None, sign=None, turnangle=None): + if radius is None: + radius = self.radius + if halfloops is None: + halfloops = self.halfloops + if skipfirst is None: + skipfirst = self.skipfirst + if skiplast is None: + skiplast = self.skiplast + if curvesperhloop is None: + curvesperhloop = self.curvesperhloop + if sign is None: + sign = self.sign + if turnangle is None: + turnangle = self.turnangle + + return cycloid(radius=radius, halfloops=halfloops, skipfirst=skipfirst, skiplast=skiplast, + curvesperhloop=curvesperhloop, sign=sign, turnangle=turnangle) + + def deform(self, basepath): + resultnormsubpaths = [self.deformsubpath(nsp) for nsp in basepath.normpath().normsubpaths] + return normpath.normpath(resultnormsubpaths) + + def deformsubpath(self, normsubpath): + + skipfirst = abs(unit.topt(self.skipfirst)) + skiplast = abs(unit.topt(self.skiplast)) + radius = abs(unit.topt(self.radius)) + turnangle = degrees(self.turnangle) + sign = mathutils.sign(self.sign) + + cosTurn = math.cos(turnangle) + sinTurn = math.sin(turnangle) + + # make list of the lengths and parameters at points on normsubpath + # where we will add cycloid-points + totlength = normsubpath.arclen_pt() + if totlength <= skipfirst + skiplast + 2*radius*sinTurn: + warnings.warn("normsubpath is too short for deformation with cycloid -- skipping...") + return normsubpath + + # parameterization is in rotation-angle around the basepath + # differences in length, angle ... between two basepoints + # and between basepoints and controlpoints + Dphi = math.pi / self.curvesperhloop + phis = [i * Dphi for i in range(self.halfloops * self.curvesperhloop + 1)] + DzDphi = (totlength - skipfirst - skiplast - 2*radius*sinTurn) * 1.0 / (self.halfloops * math.pi * cosTurn) + # Dz = (totlength - skipfirst - skiplast - 2*radius*sinTurn) * 1.0 / (self.halfloops * self.curvesperhloop * cosTurn) + # zs = [i * Dz for i in range(self.halfloops * self.curvesperhloop + 1)] + # from path._arctobcurve: + # optimal relative distance along tangent for second and third control point + L = 4 * radius * (1 - math.cos(Dphi/2)) / (3 * math.sin(Dphi/2)) + + # Now the transformation of z into the turned coordinate system + Zs = [ skipfirst + radius*sinTurn # here the coordinate z starts + - sinTurn*radius*math.cos(phi) + cosTurn*DzDphi*phi # the transformed z-coordinate + for phi in phis] + params = normsubpath._arclentoparam_pt(Zs)[0] + + # get the positions of the splitpoints in the cycloid + points = [] + for phi, param in zip(phis, params): + # the cycloid is a circle that is stretched along the normsubpath + # here are the points of that circle + basetrafo = normsubpath.trafo([param])[0] + + # The point on the cycloid, in the basepath's local coordinate system + baseZ, baseY = 0, radius*math.sin(phi) + + # The tangent there, also in local coords + tangentX = -cosTurn*radius*math.sin(phi) + sinTurn*DzDphi + tangentY = radius*math.cos(phi) + tangentZ = sinTurn*radius*math.sin(phi) + DzDphi*cosTurn + norm = math.sqrt(tangentX*tangentX + tangentY*tangentY + tangentZ*tangentZ) + tangentY, tangentZ = tangentY/norm, tangentZ/norm + + # Respect the curvature of the basepath for the cycloid's curvature + # XXX this is only a heuristic, not a "true" expression for + # the curvature in curved coordinate systems + pathradius = normsubpath.curveradius_pt([param])[0] + if pathradius is not normpath.invalid: + factor = (pathradius - baseY) / pathradius + factor = abs(factor) + else: + factor = 1 + l = L * factor + + # The control points prior and after the point on the cycloid + preeZ, preeY = baseZ - l * tangentZ, baseY - l * tangentY + postZ, postY = baseZ + l * tangentZ, baseY + l * tangentY + + # Now put everything at the proper place + points.append(basetrafo.apply_pt(preeZ, sign * preeY) + + basetrafo.apply_pt(baseZ, sign * baseY) + + basetrafo.apply_pt(postZ, sign * postY)) + + if len(points) <= 1: + warnings.warn("normsubpath is too short for deformation with cycloid -- skipping...") + return normsubpath + + # Build the path from the pointlist + # containing (control x 2, base x 2, control x 2) + if skipfirst > normsubpath.epsilon: + normsubpathitems = normsubpath.segments([0, params[0]])[0] + normsubpathitems.append(normpath.normcurve_pt(*(points[0][2:6] + points[1][0:4]))) + else: + normsubpathitems = [normpath.normcurve_pt(*(points[0][2:6] + points[1][0:4]))] + for i in range(1, len(points)-1): + normsubpathitems.append(normpath.normcurve_pt(*(points[i][2:6] + points[i+1][0:4]))) + if skiplast > normsubpath.epsilon: + for nsp in normsubpath.segments([params[-1], len(normsubpath)]): + normsubpathitems.extend(nsp.normsubpathitems) + + # That's it + return normpath.normsubpath(normsubpathitems, epsilon=normsubpath.epsilon) +# >>> + +cycloid.clear = attr.clearclass(cycloid) + +class smoothed(deformer): # <<< + + """Bends corners in a normpath. + + This decorator replaces corners in a normpath with bezier curves. There are two cases: + - If the corner lies between two lines, _two_ bezier curves will be used + that are highly optimized to look good (their curvature is to be zero at the ends + and has to have zero derivative in the middle). + Additionally, it can controlled by the softness-parameter. + - If the corner lies between curves then _one_ bezier is used that is (except in some + special cases) uniquely determined by the tangents and curvatures at its end-points. + In some cases it is necessary to use only the absolute value of the curvature to avoid a + cusp-shaped connection of the new bezier to the old path. In this case the use of + "obeycurv=0" allows the sign of the curvature to switch. + - The radius argument gives the arclength-distance of the corner to the points where the + old path is cut and the beziers are inserted. + - Path elements that are too short (shorter than the radius) are skipped + """ + + def __init__(self, radius, softness=1, obeycurv=0, relskipthres=0.01): + self.radius = radius + self.softness = softness + self.obeycurv = obeycurv + self.relskipthres = relskipthres + + def __call__(self, radius=None, softness=None, obeycurv=None, relskipthres=None): + if radius is None: + radius = self.radius + if softness is None: + softness = self.softness + if obeycurv is None: + obeycurv = self.obeycurv + if relskipthres is None: + relskipthres = self.relskipthres + return smoothed(radius=radius, softness=softness, obeycurv=obeycurv, relskipthres=relskipthres) + + def deform(self, basepath): + return normpath.normpath([self.deformsubpath(normsubpath) + for normsubpath in basepath.normpath().normsubpaths]) + + def deformsubpath(self, normsubpath): + radius_pt = unit.topt(self.radius) + epsilon = normsubpath.epsilon + + # remove too short normsubpath items (shorter than self.relskipthres*radius_pt or epsilon) + pertinentepsilon = max(epsilon, self.relskipthres*radius_pt) + pertinentnormsubpath = normpath.normsubpath(normsubpath.normsubpathitems, + epsilon=pertinentepsilon) + pertinentnormsubpath.flushskippedline() + pertinentnormsubpathitems = pertinentnormsubpath.normsubpathitems + + # calculate the splitting parameters for the pertinentnormsubpathitems + arclens_pt = [] + params = [] + for pertinentnormsubpathitem in pertinentnormsubpathitems: + arclen_pt = pertinentnormsubpathitem.arclen_pt(epsilon) + arclens_pt.append(arclen_pt) + l1_pt = min(radius_pt, 0.5*arclen_pt) + l2_pt = max(0.5*arclen_pt, arclen_pt - radius_pt) + params.append(pertinentnormsubpathitem.arclentoparam_pt([l1_pt, l2_pt], epsilon)) + + # handle the first and last pertinentnormsubpathitems for a non-closed normsubpath + if not normsubpath.closed: + l1_pt = 0 + l2_pt = max(0, arclens_pt[0] - radius_pt) + params[0] = pertinentnormsubpathitems[0].arclentoparam_pt([l1_pt, l2_pt], epsilon) + l1_pt = min(radius_pt, arclens_pt[-1]) + l2_pt = arclens_pt[-1] + params[-1] = pertinentnormsubpathitems[-1].arclentoparam_pt([l1_pt, l2_pt], epsilon) + + newnormsubpath = normpath.normsubpath(epsilon=normsubpath.epsilon) + for i in range(len(pertinentnormsubpathitems)): + this = i + next = (i+1) % len(pertinentnormsubpathitems) + thisparams = params[this] + nextparams = params[next] + thisnormsubpathitem = pertinentnormsubpathitems[this] + nextnormsubpathitem = pertinentnormsubpathitems[next] + thisarclen_pt = arclens_pt[this] + nextarclen_pt = arclens_pt[next] + + # insert the middle segment + newnormsubpath.append(thisnormsubpathitem.segments(thisparams)[0]) + + # insert replacement curves for the corners + if next or normsubpath.closed: + + t1 = thisnormsubpathitem.rotation([thisparams[1]])[0].apply_pt(1, 0) + t2 = nextnormsubpathitem.rotation([nextparams[0]])[0].apply_pt(1, 0) + # TODO: normpath.invalid + + if (isinstance(thisnormsubpathitem, normpath.normline_pt) and + isinstance(nextnormsubpathitem, normpath.normline_pt)): + + # case of two lines -> replace by two curves + d1, g1, f1, e, f2, g2, d2 = curvescontrols_from_endlines_pt( + thisnormsubpathitem.atend_pt(), t1, t2, + thisarclen_pt*(1-thisparams[1]), nextarclen_pt*(nextparams[0]), softness=self.softness) + + p1 = thisnormsubpathitem.at_pt([thisparams[1]])[0] + p2 = nextnormsubpathitem.at_pt([nextparams[0]])[0] + + newnormsubpath.append(normpath.normcurve_pt(*(d1 + g1 + f1 + e))) + newnormsubpath.append(normpath.normcurve_pt(*(e + f2 + g2 + d2))) + + else: + + # generic case -> replace by a single curve with prescribed tangents and curvatures + p1 = thisnormsubpathitem.at_pt([thisparams[1]])[0] + p2 = nextnormsubpathitem.at_pt([nextparams[0]])[0] + c1 = thisnormsubpathitem.curvature_pt([thisparams[1]])[0] + c2 = nextnormsubpathitem.curvature_pt([nextparams[0]])[0] + # TODO: normpath.invalid + + # TODO: more intelligent fallbacks: + # circle -> line + # circle -> circle + + if not self.obeycurv: + # do not obey the sign of the curvature but + # make the sign such that the curve smoothly passes to the next point + # this results in a discontinuous curvature + # (but the absolute value is still continuous) + s1 = +mathutils.sign(t1[0] * (p2[1]-p1[1]) - t1[1] * (p2[0]-p1[0])) + s2 = -mathutils.sign(t2[0] * (p2[1]-p1[1]) - t2[1] * (p2[0]-p1[0])) + c1 = s1 * abs(c1) + c2 = s2 * abs(c2) + + # get the length of the control "arms" + controldists = controldists_from_endgeometry_pt(p1, p2, t1, t2, c1, c2) + + if controldists and (controldists[0][0] >= 0 and controldists[0][1] >= 0): + # use the first entry in the controldists + # this should be the "smallest" pair + a, d = controldists[0] + # avoid curves with invalid parameterization + a = max(a, epsilon) + d = max(d, epsilon) + + # avoid overshooting at the corners: + # this changes not only the sign of the curvature + # but also the magnitude + if not self.obeycurv: + t, s = intersection(p1, p2, t1, t2) + if (t is not None and s is not None and + t > 0 and s < 0): + a = min(a, abs(t)) + d = min(d, abs(s)) + + else: + # use a fallback + t, s = intersection(p1, p2, t1, t2) + if t is not None and s is not None: + a = 0.65 * abs(t) + d = 0.65 * abs(s) + else: + # if there is no useful result: + # take an arbitrary smoothing curve that does not obey + # the curvature constraints + dist = math.hypot(p1[0] - p2[0], p1[1] - p2[1]) + a = dist / (3.0 * math.hypot(*t1)) + d = dist / (3.0 * math.hypot(*t2)) + + # calculate the two missing control points + q1 = p1[0] + a * t1[0], p1[1] + a * t1[1] + q2 = p2[0] - d * t2[0], p2[1] - d * t2[1] + + newnormsubpath.append(normpath.normcurve_pt(*(p1 + q1 + q2 + p2))) + + if normsubpath.closed: + newnormsubpath.close() + return newnormsubpath + +# >>> + +smoothed.clear = attr.clearclass(smoothed) + +class parallel(deformer): # <<< + + """creates a parallel normpath with constant distance to the original normpath + + A positive 'distance' results in a curve left of the original one -- and a + negative 'distance' in a curve at the right. Left/Right are understood in + terms of the parameterization of the original curve. For each path element + a parallel curve/line is constructed. At corners, either a circular arc is + drawn around the corner, or, if possible, the parallel curve is cut in + order to also exhibit a corner. + + distance: the distance of the parallel normpath + relerr: distance*relerr is the maximal allowed error in the parallel distance + sharpoutercorners: make the outer corners not round but sharp. + The inner corners (corners after inflection points) will stay round + dointersection: boolean for doing the intersection step (default: 1). + Set this value to 0 if you want the whole parallel path + checkdistanceparams: a list of parameter values in the interval (0,1) where the + parallel distance is checked on each normpathitem + lookforcurvatures: number of points per normpathitem where is looked for + a critical value of the curvature + """ + + # TODO: + # * do testing for curv=0, T=0, D=0, E=0 cases + # * do testing for several random curves + # -- does the recursive deformnicecurve converge? + + + def __init__(self, distance, relerr=0.05, sharpoutercorners=0, dointersection=1, + checkdistanceparams=[0.5], lookforcurvatures=11, debug=None): + self.distance = distance + self.relerr = relerr + self.sharpoutercorners = sharpoutercorners + self.checkdistanceparams = checkdistanceparams + self.lookforcurvatures = lookforcurvatures + self.dointersection = dointersection + self.debug = debug + + def __call__(self, distance=None, relerr=None, sharpoutercorners=None, dointersection=None, + checkdistanceparams=None, lookforcurvatures=None, debug=None): + # returns a copy of the deformer with different parameters + if distance is None: + distance = self.distance + if relerr is None: + relerr = self.relerr + if sharpoutercorners is None: + sharpoutercorners = self.sharpoutercorners + if dointersection is None: + dointersection = self.dointersection + if checkdistanceparams is None: + checkdistanceparams = self.checkdistanceparams + if lookforcurvatures is None: + lookforcurvatures = self.lookforcurvatures + if debug is None: + debug = self.debug + + return parallel(distance=distance, relerr=relerr, + sharpoutercorners=sharpoutercorners, + dointersection=dointersection, + checkdistanceparams=checkdistanceparams, + lookforcurvatures=lookforcurvatures, + debug=debug) + + def deform(self, basepath): + self.dist_pt = unit.topt(self.distance) + resultnormsubpaths = [] + for nsp in basepath.normpath().normsubpaths: + parallel_normpath = self.deformsubpath(nsp) + resultnormsubpaths += parallel_normpath.normsubpaths + result = normpath.normpath(resultnormsubpaths) + return result + + def deformsubpath(self, orig_nsp): # <<< + + """returns a list of normsubpaths building the parallel curve""" + + dist = self.dist_pt + epsilon = orig_nsp.epsilon + + # avoid too small dists: we would run into instabilities + if abs(dist) < abs(epsilon): + return orig_nsp + + result = normpath.normpath() + + # iterate over the normsubpath in the following manner: + # * for each item first append the additional arc / intersect + # and then add the next parallel piece + # * for the first item only add the parallel piece + # (because this is done for next_orig_nspitem, we need to start with next=0) + for i in range(len(orig_nsp.normsubpathitems)): + prev = i-1 + next = i + prev_orig_nspitem = orig_nsp.normsubpathitems[prev] + next_orig_nspitem = orig_nsp.normsubpathitems[next] + + stepsize = 0.01 + prev_param, prev_rotation = self.valid_near_rotation(prev_orig_nspitem, 1, 0, stepsize, 0.5*epsilon) + next_param, next_rotation = self.valid_near_rotation(next_orig_nspitem, 0, 1, stepsize, 0.5*epsilon) + # TODO: eventually shorten next_orig_nspitem + + prev_tangent = prev_rotation.apply_pt(1, 0) + next_tangent = next_rotation.apply_pt(1, 0) + + # get the next parallel piece for the normpath + try: + next_parallel_normpath = self.deformsubpathitem(next_orig_nspitem, epsilon) + except InvalidParamException, e: + invalid_nspitem_param = e.normsubpathitemparam + # split the nspitem apart and continue with pieces that do not contain + # the invalid point anymore. At the end, simply take one piece, otherwise two. + stepsize = 0.01 + if self.length_pt(next_orig_nspitem, invalid_nspitem_param, 0) > epsilon: + if self.length_pt(next_orig_nspitem, invalid_nspitem_param, 1) > epsilon: + p1, foo = self.valid_near_rotation(next_orig_nspitem, invalid_nspitem_param, 0, stepsize, 0.5*epsilon) + p2, foo = self.valid_near_rotation(next_orig_nspitem, invalid_nspitem_param, 1, stepsize, 0.5*epsilon) + segments = next_orig_nspitem.segments([0, p1, p2, 1]) + segments = segments[0], segments[2].modifiedbegin_pt(*(segments[0].atend_pt())) + else: + p1, foo = self.valid_near_rotation(next_orig_nspitem, invalid_nspitem_param, 0, stepsize, 0.5*epsilon) + segments = next_orig_nspitem.segments([0, p1]) + else: + p2, foo = self.valid_near_rotation(next_orig_nspitem, invalid_nspitem_param, 1, stepsize, 0.5*epsilon) + segments = next_orig_nspitem.segments([p2, 1]) + + next_parallel_normpath = self.deformsubpath(normpath.normsubpath(segments, epsilon=epsilon)) + + if not (next_parallel_normpath.normsubpaths and next_parallel_normpath[0].normsubpathitems): + continue + + # this starts the whole normpath + if not result.normsubpaths: + result = next_parallel_normpath + continue + + # sinus of the angle between the tangents + # sinangle > 0 for a left-turning nexttangent + # sinangle < 0 for a right-turning nexttangent + sinangle = prev_tangent[0]*next_tangent[1] - prev_tangent[1]*next_tangent[0] + cosangle = prev_tangent[0]*next_tangent[0] + prev_tangent[1]*next_tangent[1] + if cosangle < 0 or abs(dist*math.asin(sinangle)) >= epsilon: + if self.sharpoutercorners and dist*sinangle < 0: + A1, A2 = result.atend_pt(), next_parallel_normpath.atbegin_pt() + t1, t2 = intersection(A1, A2, prev_tangent, next_tangent) + B = A1[0] + t1 * prev_tangent[0], A1[1] + t1 * prev_tangent[1] + arc_normpath = normpath.normpath([normpath.normsubpath([ + normpath.normline_pt(A1[0], A1[1], B[0], B[1]), + normpath.normline_pt(B[0], B[1], A2[0], A2[1]) + ])]) + else: + # We must append an arc around the corner + arccenter = next_orig_nspitem.atbegin_pt() + arcbeg = result.atend_pt() + arcend = next_parallel_normpath.atbegin_pt() + angle1 = math.atan2(arcbeg[1] - arccenter[1], arcbeg[0] - arccenter[0]) + angle2 = math.atan2(arcend[1] - arccenter[1], arcend[0] - arccenter[0]) + + # depending on the direction we have to use arc or arcn + if dist > 0: + arcclass = path.arcn_pt + else: + arcclass = path.arc_pt + arc_normpath = path.path(arcclass( + arccenter[0], arccenter[1], abs(dist), + degrees(angle1), degrees(angle2))).normpath(epsilon=epsilon) + + # append the arc to the parallel path + result.join(arc_normpath) + # append the next parallel piece to the path + result.join(next_parallel_normpath) + else: + # The path is quite straight between prev and next item: + # normpath.normpath.join adds a straight line if necessary + result.join(next_parallel_normpath) + + + # end here if nothing has been found so far + if not (result.normsubpaths and result[-1].normsubpathitems): + return result + + # the curve around the closing corner may still be missing + if orig_nsp.closed: + # TODO: normpath.invalid + stepsize = 0.01 + prev_param, prev_rotation = self.valid_near_rotation(result[-1][-1], 1, 0, stepsize, 0.5*epsilon) + next_param, next_rotation = self.valid_near_rotation(result[0][0], 0, 1, stepsize, 0.5*epsilon) + # TODO: eventually shorten next_orig_nspitem + + prev_tangent = prev_rotation.apply_pt(1, 0) + next_tangent = next_rotation.apply_pt(1, 0) + sinangle = prev_tangent[0]*next_tangent[1] - prev_tangent[1]*next_tangent[0] + cosangle = prev_tangent[0]*next_tangent[0] + prev_tangent[1]*next_tangent[1] + + if cosangle < 0 or abs(dist*math.asin(sinangle)) >= epsilon: + # We must append an arc around the corner + # TODO: avoid the code dublication + if self.sharpoutercorners and dist*sinangle < 0: + A1, A2 = result.atend_pt(), result.atbegin_pt() + t1, t2 = intersection(A1, A2, prev_tangent, next_tangent) + B = A1[0] + t1 * prev_tangent[0], A1[1] + t1 * prev_tangent[1] + arc_normpath = normpath.normpath([normpath.normsubpath([ + normpath.normline_pt(A1[0], A1[1], B[0], B[1]), + normpath.normline_pt(B[0], B[1], A2[0], A2[1]) + ])]) + else: + arccenter = orig_nsp.atend_pt() + arcbeg = result.atend_pt() + arcend = result.atbegin_pt() + angle1 = math.atan2(arcbeg[1] - arccenter[1], arcbeg[0] - arccenter[0]) + angle2 = math.atan2(arcend[1] - arccenter[1], arcend[0] - arccenter[0]) + + # depending on the direction we have to use arc or arcn + if dist > 0: + arcclass = path.arcn_pt + else: + arcclass = path.arc_pt + arc_normpath = path.path(arcclass( + arccenter[0], arccenter[1], abs(dist), + degrees(angle1), degrees(angle2))).normpath(epsilon=epsilon) + + # append the arc to the parallel path + if (result.normsubpaths and result[-1].normsubpathitems and + arc_normpath.normsubpaths and arc_normpath[-1].normsubpathitems): + result.join(arc_normpath) + + if len(result) == 1: + result[0].close() + else: + # if the parallel normpath is split into several subpaths anyway, + # then use the natural beginning and ending + # closing is not possible anymore + for nspitem in result[0]: + result[-1].append(nspitem) + result.normsubpaths = result.normsubpaths[1:] + + if self.dointersection: + result = self.rebuild_intersected_normpath(result, normpath.normpath([orig_nsp]), epsilon) + + return result + # >>> + def deformsubpathitem(self, nspitem, epsilon): # <<< + + """Returns a parallel normpath for a single normsubpathitem + + Analyzes the curvature of a normsubpathitem and returns a normpath with + the appropriate number of normsubpaths. This must be a normpath because + a normcurve can be strongly curved, such that the parallel path must + contain a hole""" + + dist = self.dist_pt + + # for a simple line we return immediately + if isinstance(nspitem, normpath.normline_pt): + normal = nspitem.rotation([0])[0].apply_pt(0, 1) + start = nspitem.atbegin_pt() + end = nspitem.atend_pt() + return path.line_pt( + start[0] + dist * normal[0], start[1] + dist * normal[1], + end[0] + dist * normal[0], end[1] + dist * normal[1]).normpath(epsilon=epsilon) + + # for a curve we have to check if the curvatures + # cross the singular value 1/dist + crossings = self.distcrossingparameters(nspitem, epsilon) + + # depending on the number of crossings we must consider + # three different cases: + if crossings: + # The curvature crosses the borderline 1/dist + # the parallel curve contains points with infinite curvature! + result = normpath.normpath() + + # we need the endpoints of the nspitem + if self.length_pt(nspitem, crossings[0], 0) > epsilon: + crossings.insert(0, 0) + if self.length_pt(nspitem, crossings[-1], 1) > epsilon: + crossings.append(1) + + for i in range(len(crossings) - 1): + middleparam = 0.5*(crossings[i] + crossings[i+1]) + middlecurv = nspitem.curvature_pt([middleparam])[0] + if middlecurv is normpath.invalid: + raise InvalidParamException(middleparam) + # the radius is good if + # - middlecurv and dist have opposite signs or + # - middlecurv is "smaller" than 1/dist + if middlecurv*dist < 0 or abs(dist*middlecurv) < 1: + parallel_nsp = self.deformnicecurve(nspitem.segments(crossings[i:i+2])[0], epsilon) + # never append empty normsubpaths + if parallel_nsp.normsubpathitems: + result.append(parallel_nsp) + + return result + + else: + # the curvature is either bigger or smaller than 1/dist + middlecurv = nspitem.curvature_pt([0.5])[0] + if dist*middlecurv < 0 or abs(dist*middlecurv) < 1: + # The curve is everywhere less curved than 1/dist + # We can proceed finding the parallel curve for the whole piece + parallel_nsp = self.deformnicecurve(nspitem, epsilon) + # never append empty normsubpaths + if parallel_nsp.normsubpathitems: + return normpath.normpath([parallel_nsp]) + else: + return normpath.normpath() + else: + # the curve is everywhere stronger curved than 1/dist + # There is nothing to be returned. + return normpath.normpath() + + # >>> + def deformnicecurve(self, normcurve, epsilon, startparam=0.0, endparam=1.0): # <<< + + """Returns a parallel normsubpath for the normcurve. + + This routine assumes that the normcurve is everywhere + 'less' curved than 1/dist and contains no point with an + invalid parameterization + """ + dist = self.dist_pt + T_threshold = 1.0e-5 + + # normalized tangent directions + tangA, tangD = normcurve.rotation([startparam, endparam]) + # if we find an unexpected normpath.invalid we have to + # parallelise this normcurve on the level of split normsubpaths + if tangA is normpath.invalid: + raise InvalidParamException(startparam) + if tangD is normpath.invalid: + raise InvalidParamException(endparam) + tangA = tangA.apply_pt(1, 0) + tangD = tangD.apply_pt(1, 0) + + # the new starting points + orig_A, orig_D = normcurve.at_pt([startparam, endparam]) + A = orig_A[0] - dist * tangA[1], orig_A[1] + dist * tangA[0] + D = orig_D[0] - dist * tangD[1], orig_D[1] + dist * tangD[0] + + # we need to end this _before_ we will run into epsilon-problems + # when creating curves we do not want to calculate the length of + # or even split it for recursive calls + if (math.hypot(A[0] - D[0], A[1] - D[1]) < epsilon and + math.hypot(tangA[0] - tangD[0], tangA[1] - tangD[1]) < T_threshold): + return normpath.normsubpath([normpath.normline_pt(A[0], A[1], D[0], D[1])]) + + result = normpath.normsubpath(epsilon=epsilon) + # is there enough space on the normals before they intersect? + a, d = intersection(orig_A, orig_D, (-tangA[1], tangA[0]), (-tangD[1], tangD[0])) + # a,d are the lengths to the intersection points: + # for a (and equally for b) we can proceed in one of the following cases: + # a is None (means parallel normals) + # a and dist have opposite signs (and the same for b) + # a has the same sign but is bigger + if ( (a is None or a*dist < 0 or abs(a) > abs(dist) + epsilon) or + (d is None or d*dist < 0 or abs(d) > abs(dist) + epsilon) ): + # the original path is long enough to draw a parallel piece + # this is the generic case. Get the parallel curves + orig_curvA, orig_curvD = normcurve.curvature_pt([startparam, endparam]) + # normpath.invalid may not appear here because we have asked + # for this already at the tangents + assert orig_curvA is not normpath.invalid + assert orig_curvD is not normpath.invalid + curvA = orig_curvA / (1.0 - dist*orig_curvA) + curvD = orig_curvD / (1.0 - dist*orig_curvD) + + # first try to approximate the normcurve with a single item + controldistpairs = controldists_from_endgeometry_pt(A, D, tangA, tangD, curvA, curvD) + + if controldistpairs: + # TODO: is it good enough to get the first entry here? + # from testing: this fails if there are loops in the original curve + a, d = controldistpairs[0] + if a >= 0 and d >= 0: + if a < epsilon and d < epsilon: + result = normpath.normsubpath([normpath.normline_pt(A[0], A[1], D[0], D[1])], epsilon=epsilon) + else: + # we avoid curves with invalid parameterization + a = max(a, epsilon) + d = max(d, epsilon) + result = normpath.normsubpath([normpath.normcurve_pt( + A[0], A[1], + A[0] + a * tangA[0], A[1] + a * tangA[1], + D[0] - d * tangD[0], D[1] - d * tangD[1], + D[0], D[1])], epsilon=epsilon) + + # then try with two items, recursive call + if ((not result.normsubpathitems) or + (self.checkdistanceparams and result.normsubpathitems + and not self.distchecked(normcurve, result, epsilon, startparam, endparam))): + # TODO: does this ever converge? + # TODO: what if this hits epsilon? + firstnsp = self.deformnicecurve(normcurve, epsilon, startparam, 0.5*(startparam+endparam)) + secondnsp = self.deformnicecurve(normcurve, epsilon, 0.5*(startparam+endparam), endparam) + if not (firstnsp.normsubpathitems and secondnsp.normsubpathitems): + result = normpath.normsubpath( + [normpath.normline_pt(A[0], A[1], D[0], D[1])], epsilon=epsilon) + else: + # we will get problems if the curves are too short: + result = firstnsp.joined(secondnsp) + + return result + # >>> + + def distchecked(self, orig_normcurve, parallel_normsubpath, epsilon, tstart, tend): # <<< + + """Checks the distances between orig_normcurve and parallel_normsubpath + + The checking is done at parameters self.checkdistanceparams of orig_normcurve.""" + + dist = self.dist_pt + # do not look closer than epsilon: + dist_relerr = mathutils.sign(dist) * max(abs(self.relerr*dist), epsilon) + + checkdistanceparams = [tstart + (tend-tstart)*t for t in self.checkdistanceparams] + + for param, P, rotation in zip(checkdistanceparams, + orig_normcurve.at_pt(checkdistanceparams), + orig_normcurve.rotation(checkdistanceparams)): + # check if the distance is really the wanted distance + # measure the distance in the "middle" of the original curve + if rotation is normpath.invalid: + raise InvalidParamException(param) + + normal = rotation.apply_pt(0, 1) + + # create a short cutline for intersection only: + cutline = normpath.normsubpath([normpath.normline_pt ( + P[0] + (dist - 2*dist_relerr) * normal[0], + P[1] + (dist - 2*dist_relerr) * normal[1], + P[0] + (dist + 2*dist_relerr) * normal[0], + P[1] + (dist + 2*dist_relerr) * normal[1])], epsilon=epsilon) + + cutparams = parallel_normsubpath.intersect(cutline) + distances = [math.hypot(P[0] - cutpoint[0], P[1] - cutpoint[1]) + for cutpoint in cutline.at_pt(cutparams[1])] + + if (not distances) or (abs(min(distances) - abs(dist)) > abs(dist_relerr)): + return 0 + + return 1 + # >>> + def distcrossingparameters(self, normcurve, epsilon, tstart=0, tend=1): # <<< + + """Returns a list of parameters where the curvature is 1/distance""" + + dist = self.dist_pt + + # we _need_ to do this with the curvature, not with the radius + # because the curvature is continuous at the straight line and the radius is not: + # when passing from one slightly curved curve to the other with opposite curvature sign, + # via the straight line, then the curvature changes its sign at curv=0, while the + # radius changes its sign at +/-infinity + # this causes instabilities for nearly straight curves + + # include tstart and tend + params = [tstart + i * (tend - tstart) * 1.0 / (self.lookforcurvatures - 1) + for i in range(self.lookforcurvatures)] + curvs = normcurve.curvature_pt(params) + + # break everything at invalid curvatures + for param, curv in zip(params, curvs): + if curv is normpath.invalid: + raise InvalidParamException(param) + + parampairs = zip(params[:-1], params[1:]) + curvpairs = zip(curvs[:-1], curvs[1:]) + + crossingparams = [] + for parampair, curvpair in zip(parampairs, curvpairs): + begparam, endparam = parampair + begcurv, endcurv = curvpair + if (endcurv*dist - 1)*(begcurv*dist - 1) < 0: + # the curvature crosses the value 1/dist + # get the parmeter value by linear interpolation: + middleparam = ( + (begparam * abs(begcurv*dist - 1) + endparam * abs(endcurv*dist - 1)) / + (abs(begcurv*dist - 1) + abs(endcurv*dist - 1))) + middleradius = normcurve.curveradius_pt([middleparam])[0] + + if middleradius is normpath.invalid: + raise InvalidParamException(middleparam) + + if abs(middleradius - dist) < epsilon: + # get the parmeter value by linear interpolation: + crossingparams.append(middleparam) + else: + # call recursively: + cps = self.distcrossingparameters(normcurve, epsilon, tstart=begparam, tend=endparam) + crossingparams += cps + + return crossingparams + # >>> + def valid_near_rotation(self, nspitem, param, otherparam, stepsize, epsilon): # <<< + p = param + rot = nspitem.rotation([p])[0] + # run towards otherparam searching for a valid rotation + while rot is normpath.invalid: + p = (1-stepsize)*p + stepsize*otherparam + rot = nspitem.rotation([p])[0] + # walk back to param until near enough + # but do not go further if an invalid point is hit + end, new = nspitem.at_pt([param, p]) + far = math.hypot(end[0]-new[0], end[1]-new[1]) + pnew = p + while far > epsilon: + pnew = (1-stepsize)*pnew + stepsize*param + end, new = nspitem.at_pt([param, pnew]) + far = math.hypot(end[0]-new[0], end[1]-new[1]) + if nspitem.rotation([pnew])[0] is normpath.invalid: + break + else: + p = pnew + return p, nspitem.rotation([p])[0] + # >>> + def length_pt(self, path, param1, param2): # <<< + point1, point2 = path.at_pt([param1, param2]) + return math.hypot(point1[0] - point2[0], point1[1] - point2[1]) + # >>> + + def normpath_selfintersections(self, np, epsilon): # <<< + + """return all self-intersection points of normpath np. + + This does not include the intersections of a single normcurve with itself, + but all intersections of one normpathitem with a different one in the path""" + + n = len(np) + linearparams = [] + parampairs = [] + paramsriap = {} + for nsp_i in range(n): + for nsp_j in range(nsp_i, n): + for nspitem_i in range(len(np[nsp_i])): + if nsp_j == nsp_i: + nspitem_j_range = range(nspitem_i+1, len(np[nsp_j])) + else: + nspitem_j_range = range(len(np[nsp_j])) + for nspitem_j in nspitem_j_range: + intsparams = np[nsp_i][nspitem_i].intersect(np[nsp_j][nspitem_j], epsilon) + if intsparams: + for intsparam_i, intsparam_j in intsparams: + if ( (abs(intsparam_i) < epsilon and abs(1-intsparam_j) < epsilon) or + (abs(intsparam_j) < epsilon and abs(1-intsparam_i) < epsilon) ): + continue + npp_i = normpath.normpathparam(np, nsp_i, float(nspitem_i)+intsparam_i) + npp_j = normpath.normpathparam(np, nsp_j, float(nspitem_j)+intsparam_j) + linearparams.append(npp_i) + linearparams.append(npp_j) + paramsriap[id(npp_i)] = len(parampairs) + paramsriap[id(npp_j)] = len(parampairs) + parampairs.append((npp_i, npp_j)) + linearparams.sort() + return linearparams, parampairs, paramsriap + + # >>> + def can_continue(self, par_np, param1, param2): # <<< + dist = self.dist_pt + + rot1, rot2 = par_np.rotation([param1, param2]) + if rot1 is normpath.invalid or rot2 is normpath.invalid: + return 0 + curv1, curv2 = par_np.curvature_pt([param1, param2]) + tang2 = rot2.apply_pt(1, 0) + norm1 = rot1.apply_pt(0, -1) + norm1 = (dist*norm1[0], dist*norm1[1]) + + # the self-intersection is valid if the tangents + # point into the correct direction or, for parallel tangents, + # if the curvature is such that the on-going path does not + # enter the region defined by dist + mult12 = norm1[0]*tang2[0] + norm1[1]*tang2[1] + eps = 1.0e-6 + if abs(mult12) > eps: + return (mult12 < 0) + else: + # tang1 and tang2 are parallel + if curv2 is normpath.invalid or curv1 is normpath.invalid: + return 0 + if dist > 0: + return (curv2 <= curv1) + else: + return (curv2 >= curv1) + # >>> + def rebuild_intersected_normpath(self, par_np, orig_np, epsilon): # <<< + + dist = self.dist_pt + + # calculate the self-intersections of the par_np + selfintparams, selfintpairs, selfintsriap = self.normpath_selfintersections(par_np, epsilon) + # calculate the intersections of the par_np with the original path + origintparams = par_np.intersect(orig_np)[0] + + # visualize the intersection points: # <<< + if self.debug is not None: + for param1, param2 in selfintpairs: + point1, point2 = par_np.at([param1, param2]) + self.debug.fill(path.circle(point1[0], point1[1], 0.05), [color.rgb.red]) + self.debug.fill(path.circle(point2[0], point2[1], 0.03), [color.rgb.black]) + for param in origintparams: + point = par_np.at([param])[0] + self.debug.fill(path.circle(point[0], point[1], 0.05), [color.rgb.green]) + # >>> + + result = normpath.normpath() + if not selfintparams: + if origintparams: + return result + else: + return par_np + + beginparams = [] + endparams = [] + for i in range(len(par_np)): + beginparams.append(normpath.normpathparam(par_np, i, 0)) + endparams.append(normpath.normpathparam(par_np, i, len(par_np[i]))) + + allparams = selfintparams + origintparams + beginparams + endparams + allparams.sort() + allparamindices = {} + for i, param in enumerate(allparams): + allparamindices[id(param)] = i + + done = {} + for param in allparams: + done[id(param)] = 0 + + def otherparam(p): # <<< + pair = selfintpairs[selfintsriap[id(p)]] + if (p is pair[0]): + return pair[1] + else: + return pair[0] + # >>> + def trial_parampairs(startp): # <<< + tried = {} + for param in allparams: + tried[id(param)] = done[id(param)] + + lastp = startp + currentp = allparams[allparamindices[id(startp)] + 1] + result = [] + + while 1: + if currentp is startp: + result.append((lastp, currentp)) + return result + if currentp in selfintparams and otherparam(currentp) is startp: + result.append((lastp, currentp)) + return result + if currentp in endparams: + result.append((lastp, currentp)) + return result + if tried[id(currentp)]: + return [] + if currentp in origintparams: + return [] + # follow the crossings on valid startpairs until + # the normsubpath is closed or the end is reached + if (currentp in selfintparams and + self.can_continue(par_np, currentp, otherparam(currentp))): + # go to the next pair on the curve, seen from currentpair[1] + result.append((lastp, currentp)) + lastp = otherparam(currentp) + tried[id(currentp)] = 1 + tried[id(otherparam(currentp))] = 1 + currentp = allparams[allparamindices[id(otherparam(currentp))] + 1] + else: + # go to the next pair on the curve, seen from currentpair[0] + tried[id(currentp)] = 1 + tried[id(otherparam(currentp))] = 1 + currentp = allparams[allparamindices[id(currentp)] + 1] + assert 0 + # >>> + + # first the paths that start at the beginning of a subnormpath: + for startp in beginparams + selfintparams: + if done[id(startp)]: + continue + + parampairs = trial_parampairs(startp) + if not parampairs: + continue + + # collect all the pieces between parampairs + add_nsp = normpath.normsubpath(epsilon=epsilon) + for begin, end in parampairs: + # check that trial_parampairs works correctly + assert begin is not end + # we do not cross the border of a normsubpath here + assert begin.normsubpathindex is end.normsubpathindex + for item in par_np[begin.normsubpathindex].segments( + [begin.normsubpathparam, end.normsubpathparam])[0].normsubpathitems: + # TODO: this should be obsolete with an improved intersection algorithm + # guaranteeing epsilon + if add_nsp.normsubpathitems: + item = item.modifiedbegin_pt(*(add_nsp.atend_pt())) + add_nsp.append(item) + + if begin in selfintparams: + done[id(begin)] = 1 + #done[otherparam(begin)] = 1 + if end in selfintparams: + done[id(end)] = 1 + #done[otherparam(end)] = 1 + + # eventually close the path + if add_nsp and (parampairs[0][0] is parampairs[-1][-1] or + (parampairs[0][0] in selfintparams and otherparam(parampairs[0][0]) is parampairs[-1][-1])): + add_nsp.normsubpathitems[-1] = add_nsp.normsubpathitems[-1].modifiedend_pt(*add_nsp.atbegin_pt()) + add_nsp.close() + + result.extend([add_nsp]) + + return result + + # >>> + +# >>> + +parallel.clear = attr.clearclass(parallel) + +# vim:foldmethod=marker:foldmarker=<<<,>>> diff --git a/compiler/gdsMill/pyx/document.py b/compiler/gdsMill/pyx/document.py new file mode 100644 index 00000000..9b7e26e0 --- /dev/null +++ b/compiler/gdsMill/pyx/document.py @@ -0,0 +1,171 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 Jörg Lehmann +# Copyright (C) 2005-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import cStringIO, warnings +import bbox, pswriter, pdfwriter, trafo, style, unit + + +class paperformat: + + def __init__(self, width, height, name=None): + self.width = width + self.height = height + self.name = name + +paperformat.A4 = paperformat(210 * unit.t_mm, 297 * unit.t_mm, "A4") +paperformat.A3 = paperformat(297 * unit.t_mm, 420 * unit.t_mm, "A3") +paperformat.A2 = paperformat(420 * unit.t_mm, 594 * unit.t_mm, "A2") +paperformat.A1 = paperformat(594 * unit.t_mm, 840 * unit.t_mm, "A1") +paperformat.A0 = paperformat(840 * unit.t_mm, 1188 * unit.t_mm, "A0") +paperformat.A0b = paperformat(910 * unit.t_mm, 1370 * unit.t_mm, None) # dedicated to our friends in Augsburg +paperformat.Letter = paperformat(8.5 * unit.t_inch, 11 * unit.t_inch, "Letter") +paperformat.Legal = paperformat(8.5 * unit.t_inch, 14 * unit.t_inch, "Legal") + +def _paperformatfromstring(name): + return getattr(paperformat, name.capitalize()) + + +class page: + + def __init__(self, canvas, pagename=None, paperformat=None, rotated=0, centered=1, fittosize=0, + margin=1*unit.t_cm, bboxenlarge=1*unit.t_pt, bbox=None): + self.canvas = canvas + self.pagename = pagename + # support for depricated string specification of paper formats + try: + paperformat + "" + except: + self.paperformat = paperformat + else: + self.paperformat = _paperformatfromstring(paperformat) + warnings.warn("specification of paperformat by string is deprecated, use document.paperformat.%s instead" % paperformat.capitalize(), DeprecationWarning) + + self.rotated = rotated + self.centered = centered + self.fittosize = fittosize + self.margin = margin + self.bboxenlarge = bboxenlarge + self.pagebbox = bbox + + def _process(self, processMethod, contentfile, writer, context, registry, bbox): + assert not bbox + + # check whether we expect a page trafo and use a temporary canvasfile to insert the + # pagetrafo in front after the bbox was calculated + expectpagetrafo = self.paperformat and (self.rotated or self.centered or self.fittosize) + if expectpagetrafo: + canvasfile = cStringIO.StringIO() + else: + canvasfile = contentfile + + getattr(style.linewidth.normal, processMethod)(canvasfile, writer, context, registry, bbox) + getattr(self.canvas, processMethod)(canvasfile, writer, context, registry, bbox) + + # usually its the bbox of the canvas enlarged by self.bboxenlarge, but + # it might be a different bbox as specified in the page constructor + if self.pagebbox: + bbox.set(self.pagebbox) + elif bbox: + bbox.enlarge(self.bboxenlarge) + + if expectpagetrafo: + + if bbox: + # calculate the pagetrafo + paperwidth, paperheight = self.paperformat.width, self.paperformat.height + + # center (optionally rotated) output on page + if self.rotated: + pagetrafo = trafo.rotate(90).translated(paperwidth, 0) + if self.centered or self.fittosize: + if not self.fittosize and (bbox.height() > paperwidth or bbox.width() > paperheight): + warnings.warn("content exceeds the papersize") + pagetrafo = pagetrafo.translated(-0.5*(paperwidth - bbox.height()) + bbox.bottom(), + 0.5*(paperheight - bbox.width()) - bbox.left()) + else: + if not self.fittosize and (bbox.width() > paperwidth or bbox.height() > paperheight): + warnings.warn("content exceeds the papersize") + pagetrafo = trafo.translate(0.5*(paperwidth - bbox.width()) - bbox.left(), + 0.5*(paperheight - bbox.height()) - bbox.bottom()) + + if self.fittosize: + + if 2*self.margin > paperwidth or 2*self.margin > paperheight: + raise ValueError("Margins too broad for selected paperformat. Aborting.") + + paperwidth -= 2 * self.margin + paperheight -= 2 * self.margin + + # scale output to pagesize - margins + if self.rotated: + sfactor = min(unit.topt(paperheight)/bbox.width_pt(), unit.topt(paperwidth)/bbox.height_pt()) + else: + sfactor = min(unit.topt(paperwidth)/bbox.width_pt(), unit.topt(paperheight)/bbox.height_pt()) + + pagetrafo = pagetrafo.scaled(sfactor, sfactor, self.margin + 0.5*paperwidth, self.margin + 0.5*paperheight) + + # apply the pagetrafo and write it to the contentfile + bbox.transform(pagetrafo) + pagetrafofile = cStringIO.StringIO() + # context, bbox, registry are just passed as stubs (the trafo should not touch them) + getattr(pagetrafo, processMethod)(pagetrafofile, writer, context, registry, bbox) + contentfile.write(pagetrafofile.getvalue()) + pagetrafofile.close() + + contentfile.write(canvasfile.getvalue()) + canvasfile.close() + + def processPS(self, *args): + self._process("processPS", *args) + + def processPDF(self, *args): + self._process("processPDF", *args) + + +class document: + + """holds a collection of page instances which are output as pages of a document""" + + def __init__(self, pages=[]): + self.pages = pages + + def append(self, page): + self.pages.append(page) + + def writeEPSfile(self, file, *args, **kwargs): + pswriter.epswriter(self, file, *args, **kwargs) + + def writePSfile(self, file, *args, **kwargs): + pswriter.pswriter(self, file, *args, **kwargs) + + def writePDFfile(self, file, *args, **kwargs): + pdfwriter.PDFwriter(self, file, *args, **kwargs) + + def writetofile(self, filename, *args, **kwargs): + if filename.endswith(".eps"): + self.writeEPSfile(filename, *args, **kwargs) + elif filename.endswith(".ps"): + self.writePSfile(filename, *args, **kwargs) + elif filename.endswith(".pdf"): + self.writePDFfile(filename, *args, **kwargs) + else: + raise ValueError("unknown file extension") diff --git a/compiler/gdsMill/pyx/dvifile.py b/compiler/gdsMill/pyx/dvifile.py new file mode 100644 index 00000000..06248c89 --- /dev/null +++ b/compiler/gdsMill/pyx/dvifile.py @@ -0,0 +1,1308 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2003-2004,2006,2007 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import cStringIO, exceptions, re, struct, string, sys, warnings, math +import unit, epsfile, bbox, canvas, color, trafo, path, pykpathsea, type1font + + +class binfile: + + def __init__(self, filename, mode="r"): + self.file = open(filename, mode) + + def close(self): + self.file.close() + + def tell(self): + return self.file.tell() + + def eof(self): + return self.file.eof() + + def read(self, bytes): + return self.file.read(bytes) + + def readint(self, bytes=4, signed=0): + first = 1 + result = 0 + while bytes: + value = ord(self.file.read(1)) + if first and signed and value > 127: + value -= 256 + first = 0 + result = 256 * result + value + bytes -= 1 + return result + + def readint32(self): + return struct.unpack(">l", self.file.read(4))[0] + + def readuint32(self): + return struct.unpack(">L", self.file.read(4))[0] + + def readint24(self): + # XXX: checkme + return struct.unpack(">l", "\0"+self.file.read(3))[0] + + def readuint24(self): + # XXX: checkme + return struct.unpack(">L", "\0"+self.file.read(3))[0] + + def readint16(self): + return struct.unpack(">h", self.file.read(2))[0] + + def readuint16(self): + return struct.unpack(">H", self.file.read(2))[0] + + def readchar(self): + return struct.unpack("b", self.file.read(1))[0] + + def readuchar(self): + return struct.unpack("B", self.file.read(1))[0] + + def readstring(self, bytes): + l = self.readuchar() + assert l <= bytes-1, "inconsistency in file: string too long" + return self.file.read(bytes-1)[:l] + +class stringbinfile(binfile): + + def __init__(self, s): + self.file = cStringIO.StringIO(s) + + + + +############################################################################## +# TFM file handling +############################################################################## + +class TFMError(exceptions.Exception): pass + + +class char_info_word: + def __init__(self, word): + self.width_index = int((word & 0xFF000000L) >> 24) #make sign-safe + self.height_index = (word & 0x00F00000) >> 20 + self.depth_index = (word & 0x000F0000) >> 16 + self.italic_index = (word & 0x0000FC00) >> 10 + self.tag = (word & 0x00000300) >> 8 + self.remainder = (word & 0x000000FF) + + +class tfmfile: + def __init__(self, name, debug=0): + self.file = binfile(name, "rb") + + # + # read pre header + # + + self.lf = self.file.readint16() + self.lh = self.file.readint16() + self.bc = self.file.readint16() + self.ec = self.file.readint16() + self.nw = self.file.readint16() + self.nh = self.file.readint16() + self.nd = self.file.readint16() + self.ni = self.file.readint16() + self.nl = self.file.readint16() + self.nk = self.file.readint16() + self.ne = self.file.readint16() + self.np = self.file.readint16() + + if not (self.bc-1 <= self.ec <= 255 and + self.ne <= 256 and + self.lf == 6+self.lh+(self.ec-self.bc+1)+self.nw+self.nh+self.nd + +self.ni+self.nl+self.nk+self.ne+self.np): + raise TFMError, "error in TFM pre-header" + + if debug: + print "lh=%d" % self.lh + + # + # read header + # + + self.checksum = self.file.readint32() + self.designsize = self.file.readint32() + assert self.designsize > 0, "invald design size" + if self.lh > 2: + assert self.lh > 11, "inconsistency in TFM file: incomplete field" + self.charcoding = self.file.readstring(40) + else: + self.charcoding = None + + if self.lh > 12: + assert self.lh > 16, "inconsistency in TFM file: incomplete field" + self.fontfamily = self.file.readstring(20) + else: + self.fontfamily = None + + if debug: + print "(FAMILY %s)" % self.fontfamily + print "(CODINGSCHEME %s)" % self.charcoding + print "(DESINGSIZE R %f)" % 16.0*self.designsize/16777216L + + if self.lh > 17: + self.sevenbitsave = self.file.readuchar() + # ignore the following two bytes + self.file.readint16() + facechar = self.file.readuchar() + # decode ugly face specification into the Knuth suggested string + if facechar < 18: + if facechar >= 12: + self.face = "E" + facechar -= 12 + elif facechar >= 6: + self.face = "C" + facechar -= 6 + else: + self.face = "R" + + if facechar >= 4: + self.face = "L" + self.face + facechar -= 4 + elif facechar >= 2: + self.face = "B" + self.face + facechar -= 2 + else: + self.face = "M" + self.face + + if facechar == 1: + self.face = self.face[0] + "I" + self.face[1] + else: + self.face = self.face[0] + "R" + self.face[1] + + else: + self.face = None + else: + self.sevenbitsave = self.face = None + + if self.lh > 18: + # just ignore the rest + print self.file.read((self.lh-18)*4) + + # + # read char_info + # + + self.char_info = [None]*(self.ec+1) + for charcode in range(self.bc, self.ec+1): + self.char_info[charcode] = char_info_word(self.file.readint32()) + if self.char_info[charcode].width_index == 0: + # disable character if width_index is zero + self.char_info[charcode] = None + + # + # read widths + # + + self.width = [None for width_index in range(self.nw)] + for width_index in range(self.nw): + self.width[width_index] = self.file.readint32() + + # + # read heights + # + + self.height = [None for height_index in range(self.nh)] + for height_index in range(self.nh): + self.height[height_index] = self.file.readint32() + + # + # read depths + # + + self.depth = [None for depth_index in range(self.nd)] + for depth_index in range(self.nd): + self.depth[depth_index] = self.file.readint32() + + # + # read italic + # + + self.italic = [None for italic_index in range(self.ni)] + for italic_index in range(self.ni): + self.italic[italic_index] = self.file.readint32() + + # + # read lig_kern + # + + # XXX decode to lig_kern_command + + self.lig_kern = [None for lig_kern_index in range(self.nl)] + for lig_kern_index in range(self.nl): + self.lig_kern[lig_kern_index] = self.file.readint32() + + # + # read kern + # + + self.kern = [None for kern_index in range(self.nk)] + for kern_index in range(self.nk): + self.kern[kern_index] = self.file.readint32() + + # + # read exten + # + + # XXX decode to extensible_recipe + + self.exten = [None for exten_index in range(self.ne)] + for exten_index in range(self.ne): + self.exten[exten_index] = self.file.readint32() + + # + # read param + # + + # XXX decode + + self.param = [None for param_index in range(self.np)] + for param_index in range(self.np): + self.param[param_index] = self.file.readint32() + + self.file.close() + + + +############################################################################## +# Font handling +############################################################################## + +# +# PostScript font selection and output primitives +# + +class UnsupportedFontFormat(Exception): + pass + +class UnsupportedPSFragment(Exception): + pass + +class fontmapping: + + tokenpattern = re.compile(r'"(.*?)("\s+|"$|$)|(.*?)(\s+|$)') + + def __init__(self, s): + """ construct font mapping from line s of font mapping file """ + self.texname = self.basepsname = self.fontfile = None + + # standard encoding + self.encodingfile = None + + # supported postscript fragments occuring in psfonts.map + self.reencodefont = self.extendfont = self.slantfont = None + + tokens = [] + while len(s): + match = self.tokenpattern.match(s) + if match: + if match.groups()[0] is not None: + tokens.append('"%s"' % match.groups()[0]) + else: + tokens.append(match.groups()[2]) + s = s[match.end():] + else: + raise RuntimeError("Cannot tokenize string '%s'" % s) + + for token in tokens: + if token.startswith("<"): + if token.startswith("<<"): + # XXX: support non-partial download here + self.fontfile = token[2:] + elif token.startswith("<["): + self.encodingfile = token[2:] + elif token.endswith(".pfa") or token.endswith(".pfb"): + self.fontfile = token[1:] + elif token.endswith(".enc"): + self.encodingfile = token[1:] + elif token.endswith(".ttf"): + raise UnsupportedFontFormat("TrueType font") + else: + raise RuntimeError("Unknown token '%s'" % token) + elif token.startswith('"'): + pscode = token[1:-1].split() + # parse standard postscript code fragments + while pscode: + try: + arg, cmd = pscode[:2] + except: + raise UnsupportedPSFragment("Unsupported Postscript fragment '%s'" % pscode) + pscode = pscode[2:] + if cmd == "ReEncodeFont": + self.reencodefont = arg + elif cmd == "ExtendFont": + self.extendfont = arg + elif cmd == "SlantFont": + self.slantfont = arg + else: + raise UnsupportedPSFragment("Unsupported Postscript fragment '%s %s'" % (arg, cmd)) + else: + if self.texname is None: + self.texname = token + else: + self.basepsname = token + if self.basepsname is None: + self.basepsname = self.texname + + def __str__(self): + return ("'%s' is '%s' read from '%s' encoded as '%s'" % + (self.texname, self.basepsname, self.fontfile, repr(self.encodingfile))) + +# generate fontmap + +def readfontmap(filenames): + """ read font map from filename (without path) """ + fontmap = {} + for filename in filenames: + mappath = pykpathsea.find_file(filename, pykpathsea.kpse_fontmap_format) + # try also the oft-used registration as dvips config file + if not mappath: + mappath = pykpathsea.find_file(filename, pykpathsea.kpse_dvips_config_format) + if not mappath: + raise RuntimeError("cannot find font mapping file '%s'" % filename) + mapfile = open(mappath, "rU") + lineno = 0 + for line in mapfile.readlines(): + lineno += 1 + line = line.rstrip() + if not (line=="" or line[0] in (" ", "%", "*", ";" , "#")): + try: + fm = fontmapping(line) + except (RuntimeError, UnsupportedPSFragment), e: + warnings.warn("Ignoring line %i in mapping file '%s': %s" % (lineno, mappath, e)) + except UnsupportedFontFormat, e: + pass + else: + fontmap[fm.texname] = fm + mapfile.close() + return fontmap + + +class font: + + def __init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug=0): + self.name = name + self.q = q # desired size of font (fix_word) in TeX points + self.d = d # design size of font (fix_word) in TeX points + self.tfmconv = tfmconv # conversion factor from tfm units to dvi units + self.pyxconv = pyxconv # conversion factor from dvi units to PostScript points + self.fontmap = fontmap + tfmpath = pykpathsea.find_file("%s.tfm" % self.name, pykpathsea.kpse_tfm_format) + if not tfmpath: + raise TFMError("cannot find %s.tfm" % self.name) + self.tfmfile = tfmfile(tfmpath, debug) + + # We only check for equality of font checksums if none of them + # is zero. The case c == 0 happend in some VF files and + # according to the VFtoVP documentation, paragraph 40, a check + # is only performed if tfmfile.checksum > 0. Anyhow, being + # more generous here seems to be reasonable + if self.tfmfile.checksum != c and self.tfmfile.checksum != 0 and c != 0: + raise DVIError("check sums do not agree: %d vs. %d" % + (self.tfmfile.checksum, c)) + + # Check whether the given design size matches the one defined in the tfm file + if abs(self.tfmfile.designsize - d) > 2: + raise DVIError("design sizes do not agree: %d vs. %d" % (self.tfmfile.designsize, d)) + #if q < 0 or q > 134217728: + # raise DVIError("font '%s' not loaded: bad scale" % self.name) + if d < 0 or d > 134217728: + raise DVIError("font '%s' not loaded: bad design size" % self.name) + + self.scale = 1.0*q/d + + def fontinfo(self): + class fontinfo: + pass + + # The following code is a very crude way to obtain the information + # required for the PDF font descritor. (TODO: The correct way would + # be to read the information from the AFM file.) + fontinfo = fontinfo() + try: + fontinfo.fontbbox = (0, + -self.getdepth_ds(ord("y")), + self.getwidth_ds(ord("W")), + self.getheight_ds(ord("H"))) + except: + fontinfo.fontbbox = (0, -10, 100, 100) + try: + fontinfo.italicangle = -180/math.pi*math.atan(self.tfmfile.param[0]/65536.0) + except IndexError: + fontinfo.italicangle = 0 + fontinfo.ascent = fontinfo.fontbbox[3] + fontinfo.descent = fontinfo.fontbbox[1] + try: + fontinfo.capheight = self.getheight_ds(ord("h")) + except: + fontinfo.capheight = 100 + try: + fontinfo.vstem = self.getwidth_ds(ord("."))/3 + except: + fontinfo.vstem = 5 + return fontinfo + + def __str__(self): + return "font %s designed at %g TeX pts used at %g TeX pts" % (self.name, + 16.0*self.d/16777216L, + 16.0*self.q/16777216L) + __repr__ = __str__ + + def getsize_pt(self): + """ return size of font in (PS) points """ + # The factor 16L/16777216L=2**(-20) converts a fix_word (here self.q) + # to the corresponding float. Furthermore, we have to convert from TeX + # points to points, hence the factor 72/72.27. + return 16L*self.q/16777216L*72/72.27 + + def _convert_tfm_to_dvi(self, length): + # doing the integer math with long integers will lead to different roundings + # return 16*length*int(round(self.q*self.tfmconv))/16777216 + + # Knuth instead suggests the following algorithm based on 4 byte integer logic only + # z = int(round(self.q*self.tfmconv)) + # b0, b1, b2, b3 = [ord(c) for c in struct.pack(">L", length)] + # assert b0 == 0 or b0 == 255 + # shift = 4 + # while z >= 8388608: + # z >>= 1 + # shift -= 1 + # assert shift >= 0 + # result = ( ( ( ( ( b3 * z ) >> 8 ) + ( b2 * z ) ) >> 8 ) + ( b1 * z ) ) >> shift + # if b0 == 255: + # result = result - (z << (8-shift)) + + # however, we can simplify this using a single long integer multiplication, + # but take into account the transformation of z + z = int(round(self.q*self.tfmconv)) + assert -16777216 <= length < 16777216 # -(1 << 24) <= length < (1 << 24) + assert z < 134217728 # 1 << 27 + shift = 20 # 1 << 20 + while z >= 8388608: # 1 << 23 + z >>= 1 + shift -= 1 + # length*z is a long integer, but the result will be a regular integer + return int(length*long(z) >> shift) + + def _convert_tfm_to_ds(self, length): + return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv * 1000 / self.getsize_pt() + + def _convert_tfm_to_pt(self, length): + return (16*long(round(length*float(self.q)*self.tfmconv))/16777216) * self.pyxconv + + # routines returning lengths as integers in dvi units + + def getwidth_dvi(self, charcode): + return self._convert_tfm_to_dvi(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index]) + + def getheight_dvi(self, charcode): + return self._convert_tfm_to_dvi(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index]) + + def getdepth_dvi(self, charcode): + return self._convert_tfm_to_dvi(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index]) + + def getitalic_dvi(self, charcode): + return self._convert_tfm_to_dvi(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index]) + + # routines returning lengths as integers in design size (AFM) units + + def getwidth_ds(self, charcode): + return self._convert_tfm_to_ds(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index]) + + def getheight_ds(self, charcode): + return self._convert_tfm_to_ds(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index]) + + def getdepth_ds(self, charcode): + return self._convert_tfm_to_ds(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index]) + + def getitalic_ds(self, charcode): + return self._convert_tfm_to_ds(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index]) + + # routines returning lengths as floats in PostScript points + + def getwidth_pt(self, charcode): + return self._convert_tfm_to_pt(self.tfmfile.width[self.tfmfile.char_info[charcode].width_index]) + + def getheight_pt(self, charcode): + return self._convert_tfm_to_pt(self.tfmfile.height[self.tfmfile.char_info[charcode].height_index]) + + def getdepth_pt(self, charcode): + return self._convert_tfm_to_pt(self.tfmfile.depth[self.tfmfile.char_info[charcode].depth_index]) + + def getitalic_pt(self, charcode): + return self._convert_tfm_to_pt(self.tfmfile.italic[self.tfmfile.char_info[charcode].italic_index]) + + +class virtualfont(font): + def __init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug=0): + fontpath = pykpathsea.find_file(name, pykpathsea.kpse_vf_format) + if fontpath is None or not len(fontpath): + raise RuntimeError + font.__init__(self, name, c, q, d, tfmconv, pyxconv, fontmap, debug) + self.vffile = vffile(fontpath, self.scale, tfmconv, pyxconv, fontmap, debug > 1) + + def getfonts(self): + """ return fonts used in virtual font itself """ + return self.vffile.getfonts() + + def getchar(self, cc): + """ return dvi chunk corresponding to char code cc """ + return self.vffile.getchar(cc) + + +############################################################################## +# DVI file handling +############################################################################## + +_DVI_CHARMIN = 0 # typeset a character and move right (range min) +_DVI_CHARMAX = 127 # typeset a character and move right (range max) +_DVI_SET1234 = 128 # typeset a character and move right +_DVI_SETRULE = 132 # typeset a rule and move right +_DVI_PUT1234 = 133 # typeset a character +_DVI_PUTRULE = 137 # typeset a rule +_DVI_NOP = 138 # no operation +_DVI_BOP = 139 # beginning of page +_DVI_EOP = 140 # ending of page +_DVI_PUSH = 141 # save the current positions (h, v, w, x, y, z) +_DVI_POP = 142 # restore positions (h, v, w, x, y, z) +_DVI_RIGHT1234 = 143 # move right +_DVI_W0 = 147 # move right by w +_DVI_W1234 = 148 # move right and set w +_DVI_X0 = 152 # move right by x +_DVI_X1234 = 153 # move right and set x +_DVI_DOWN1234 = 157 # move down +_DVI_Y0 = 161 # move down by y +_DVI_Y1234 = 162 # move down and set y +_DVI_Z0 = 166 # move down by z +_DVI_Z1234 = 167 # move down and set z +_DVI_FNTNUMMIN = 171 # set current font (range min) +_DVI_FNTNUMMAX = 234 # set current font (range max) +_DVI_FNT1234 = 235 # set current font +_DVI_SPECIAL1234 = 239 # special (dvi extention) +_DVI_FNTDEF1234 = 243 # define the meaning of a font number +_DVI_PRE = 247 # preamble +_DVI_POST = 248 # postamble beginning +_DVI_POSTPOST = 249 # postamble ending + +_DVI_VERSION = 2 # dvi version + +# position variable indices +_POS_H = 0 +_POS_V = 1 +_POS_W = 2 +_POS_X = 3 +_POS_Y = 4 +_POS_Z = 5 + +# reader states +_READ_PRE = 1 +_READ_NOPAGE = 2 +_READ_PAGE = 3 +_READ_POST = 4 # XXX not used +_READ_POSTPOST = 5 # XXX not used +_READ_DONE = 6 + + +class DVIError(exceptions.Exception): pass + +# save and restore colors + +class _savecolor(canvas.canvasitem): + def processPS(self, file, writer, context, registry, bbox): + file.write("currentcolor currentcolorspace\n") + + def processPDF(self, file, writer, context, registry, bbox): + file.write("q\n") + + +class _restorecolor(canvas.canvasitem): + def processPS(self, file, writer, context, registry, bbox): + file.write("setcolorspace setcolor\n") + + def processPDF(self, file, writer, context, registry, bbox): + file.write("Q\n") + +class _savetrafo(canvas.canvasitem): + def processPS(self, file, writer, context, registry, bbox): + file.write("matrix currentmatrix\n") + + def processPDF(self, file, writer, context, registry, bbox): + file.write("q\n") + + +class _restoretrafo(canvas.canvasitem): + def processPS(self, file, writer, context, registry, bbox): + file.write("setmatrix\n") + + def processPDF(self, file, writer, context, registry, bbox): + file.write("Q\n") + + +class dvifile: + + def __init__(self, filename, fontmap, debug=0, debugfile=sys.stdout): + """ opens the dvi file and reads the preamble """ + self.filename = filename + self.fontmap = fontmap + self.debug = debug + self.debugfile = debugfile + self.debugstack = [] + + self.fonts = {} + self.activefont = None + + # stack of fonts and fontscale currently used (used for VFs) + self.fontstack = [] + self.stack = [] + + # pointer to currently active page + self.actpage = None + + # stack for self.file, self.fonts and self.stack, needed for VF inclusion + self.statestack = [] + + self.file = binfile(self.filename, "rb") + + # currently read byte in file (for debugging output) + self.filepos = None + + self._read_pre() + + # helper routines + + def flushtext(self): + """ finish currently active text object """ + if self.debug and self.activetext: + self.debugfile.write("[%s]\n" % "".join([chr(char) for char in self.activetext.chars])) + self.activetext = None + + def putrule(self, height, width, advancepos=1): + self.flushtext() + x1 = self.pos[_POS_H] * self.pyxconv + y1 = -self.pos[_POS_V] * self.pyxconv + w = width * self.pyxconv + h = height * self.pyxconv + + if height > 0 and width > 0: + if self.debug: + self.debugfile.write("%d: %srule height %d, width %d (???x??? pixels)\n" % + (self.filepos, advancepos and "set" or "put", height, width)) + self.actpage.fill(path.rect_pt(x1, y1, w, h)) + else: + if self.debug: + self.debugfile.write("%d: %srule height %d, width %d (invisible)\n" % + (self.filepos, advancepos and "set" or "put", height, width)) + + if advancepos: + if self.debug: + self.debugfile.write(" h:=%d+%d=%d, hh:=???\n" % + (self.pos[_POS_H], width, self.pos[_POS_H]+width)) + self.pos[_POS_H] += width + + def putchar(self, char, advancepos=1, id1234=0): + dx = advancepos and self.activefont.getwidth_dvi(char) or 0 + + if self.debug: + self.debugfile.write("%d: %s%s%d h:=%d+%d=%d, hh:=???\n" % + (self.filepos, + advancepos and "set" or "put", + id1234 and "%i " % id1234 or "char", + char, + self.pos[_POS_H], dx, self.pos[_POS_H]+dx)) + + if isinstance(self.activefont, virtualfont): + # virtual font handling + afterpos = list(self.pos) + afterpos[_POS_H] += dx + self._push_dvistring(self.activefont.getchar(char), self.activefont.getfonts(), afterpos, + self.activefont.getsize_pt()) + else: + if self.activetext is None: + if not self.fontmap.has_key(self.activefont.name): + raise RuntimeError("missing font information for '%s'; check fontmapping file(s)" % self.activefont.name) + fontmapinfo = self.fontmap[self.activefont.name] + + encodingname = fontmapinfo.reencodefont + if encodingname is not None: + encodingfilename = pykpathsea.find_file(fontmapinfo.encodingfile, pykpathsea.kpse_tex_ps_header_format) + if not encodingfilename: + raise RuntimeError("cannot find font encoding file %s" % fontmapinfo.encodingfile) + fontencoding = type1font.encoding(encodingname, encodingfilename) + else: + fontencoding = None + + fontbasefontname = fontmapinfo.basepsname + if fontmapinfo.fontfile is not None: + fontfilename = pykpathsea.find_file(fontmapinfo.fontfile, pykpathsea.kpse_type1_format) + if not fontfilename: + raise RuntimeError("cannot find type 1 font %s" % fontmapinfo.fontfile) + else: + fontfilename = None + + fontslant = fontmapinfo.slantfont + if fontslant is not None: + fontslant = float(fontslant) + + # XXX we currently misuse use self.activefont as metric + font = type1font.font(fontbasefontname, fontfilename, fontencoding, fontslant, self.activefont) + + self.activetext = type1font.text_pt(self.pos[_POS_H] * self.pyxconv, -self.pos[_POS_V] * self.pyxconv, font) + self.actpage.insert(self.activetext) + self.activetext.addchar(char) + self.pos[_POS_H] += dx + + if not advancepos: + self.flushtext() + + def usefont(self, fontnum, id1234=0): + self.flushtext() + self.activefont = self.fonts[fontnum] + if self.debug: + self.debugfile.write("%d: fnt%s%i current font is %s\n" % + (self.filepos, + id1234 and "%i " % id1234 or "num", + fontnum, + self.fonts[fontnum].name)) + + + def definefont(self, cmdnr, num, c, q, d, fontname): + # cmdnr: type of fontdef command (only used for debugging output) + # c: checksum + # q: scaling factor (fix_word) + # Note that q is actually s in large parts of the documentation. + # d: design size (fix_word) + + try: + afont = virtualfont(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1) + except (TypeError, RuntimeError): + afont = font(fontname, c, q/self.tfmconv, d/self.tfmconv, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1) + + self.fonts[num] = afont + + if self.debug: + self.debugfile.write("%d: fntdef%d %i: %s\n" % (self.filepos, cmdnr, num, fontname)) + +# scale = round((1000.0*self.conv*q)/(self.trueconv*d)) +# m = 1.0*q/d +# scalestring = scale!=1000 and " scaled %d" % scale or "" +# print ("Font %i: %s%s---loaded at size %d DVI units" % +# (num, fontname, scalestring, q)) +# if scale!=1000: +# print " (this font is magnified %d%%)" % round(scale/10) + + def special(self, s): + x = self.pos[_POS_H] * self.pyxconv + y = -self.pos[_POS_V] * self.pyxconv + if self.debug: + self.debugfile.write("%d: xxx '%s'\n" % (self.filepos, s)) + if not s.startswith("PyX:"): + warnings.warn("ignoring special '%s'" % s) + return + + # it is in general not safe to continue using the currently active font because + # the specials may involve some gsave/grestore operations + self.flushtext() + + command, args = s[4:].split()[0], s[4:].split()[1:] + if command == "color_begin": + if args[0] == "cmyk": + c = color.cmyk(float(args[1]), float(args[2]), float(args[3]), float(args[4])) + elif args[0] == "gray": + c = color.gray(float(args[1])) + elif args[0] == "hsb": + c = color.hsb(float(args[1]), float(args[2]), float(args[3])) + elif args[0] == "rgb": + c = color.rgb(float(args[1]), float(args[2]), float(args[3])) + elif args[0] == "RGB": + c = color.rgb(int(args[1])/255.0, int(args[2])/255.0, int(args[3])/255.0) + elif args[0] == "texnamed": + try: + c = getattr(color.cmyk, args[1]) + except AttributeError: + raise RuntimeError("unknown TeX color '%s', aborting" % args[1]) + elif args[0] == "pyxcolor": + # pyx.color.cmyk.PineGreen or + # pyx.color.cmyk(0,0,0,0.0) + pat = re.compile(r"(pyx\.)?(color\.)?(?P(cmyk)|(rgb)|(grey)|(gray)|(hsb))[\.]?(?P.*)") + sd = pat.match(" ".join(args[1:])) + if sd: + sd = sd.groupdict() + if sd["arg"][0] == "(": + numpat = re.compile(r"[+-]?((\d+\.\d*)|(\d*\.\d+)|(\d+))([eE][+-]\d+)?") + arg = tuple([float(x[0]) for x in numpat.findall(sd["arg"])]) + try: + c = getattr(color, sd["model"])(*arg) + except TypeError or AttributeError: + raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:])) + else: + try: + c = getattr(getattr(color, sd["model"]), sd["arg"]) + except AttributeError: + raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:])) + else: + raise RuntimeError("cannot access PyX color '%s' in TeX, aborting" % " ".join(args[1:])) + else: + raise RuntimeError("color model '%s' cannot be handled by PyX, aborting" % args[0]) + self.actpage.insert(_savecolor()) + self.actpage.insert(c) + elif command == "color_end": + self.actpage.insert(_restorecolor()) + elif command == "rotate_begin": + self.actpage.insert(_savetrafo()) + self.actpage.insert(trafo.rotate_pt(float(args[0]), x, y)) + elif command == "rotate_end": + self.actpage.insert(_restoretrafo()) + elif command == "scale_begin": + self.actpage.insert(_savetrafo()) + self.actpage.insert(trafo.scale_pt(float(args[0]), float(args[1]), x, y)) + elif command == "scale_end": + self.actpage.insert(_restoretrafo()) + elif command == "epsinclude": + # parse arguments + argdict = {} + for arg in args: + name, value = arg.split("=") + argdict[name] = value + + # construct kwargs for epsfile constructor + epskwargs = {} + epskwargs["filename"] = argdict["file"] + epskwargs["bbox"] = bbox.bbox_pt(float(argdict["llx"]), float(argdict["lly"]), + float(argdict["urx"]), float(argdict["ury"])) + if argdict.has_key("width"): + epskwargs["width"] = float(argdict["width"]) * unit.t_pt + if argdict.has_key("height"): + epskwargs["height"] = float(argdict["height"]) * unit.t_pt + if argdict.has_key("clip"): + epskwargs["clip"] = int(argdict["clip"]) + self.actpage.insert(epsfile.epsfile(x * unit.t_pt, y * unit.t_pt, **epskwargs)) + elif command == "marker": + if len(args) != 1: + raise RuntimeError("marker contains spaces") + for c in args[0]: + if c not in string.digits + string.letters + "@": + raise RuntimeError("marker contains invalid characters") + if self.actpage.markers.has_key(args[0]): + raise RuntimeError("marker name occurred several times") + self.actpage.markers[args[0]] = x * unit.t_pt, y * unit.t_pt + else: + raise RuntimeError("unknown PyX special '%s', aborting" % command) + + # routines for pushing and popping different dvi chunks on the reader + + def _push_dvistring(self, dvi, fonts, afterpos, fontsize): + """ push dvi string with defined fonts on top of reader + stack. Every positions gets scaled relatively by the factor + scale. After the interpreting of the dvi chunk has been finished, + continue with self.pos=afterpos. The designsize of the virtual + font is passed as a fix_word + + """ + + #if self.debug: + # self.debugfile.write("executing new dvi chunk\n") + self.debugstack.append(self.debug) + self.debug = 0 + + self.statestack.append((self.file, self.fonts, self.activefont, afterpos, self.stack, self.pyxconv, self.tfmconv)) + + # units in vf files are relative to the size of the font and given as fix_words + # which can be converted to floats by diving by 2**20 + oldpyxconv = self.pyxconv + self.pyxconv = fontsize/2**20 + rescale = self.pyxconv/oldpyxconv + + self.file = stringbinfile(dvi) + self.fonts = fonts + self.stack = [] + self.filepos = 0 + + # rescale self.pos in order to be consistent with the new scaling + self.pos = map(lambda x, rescale=rescale:1.0*x/rescale, self.pos) + + # since tfmconv converts from tfm units to dvi units, rescale it as well + self.tfmconv /= rescale + + self.usefont(0) + + def _pop_dvistring(self): + self.flushtext() + #if self.debug: + # self.debugfile.write("finished executing dvi chunk\n") + self.debug = self.debugstack.pop() + + self.file.close() + self.file, self.fonts, self.activefont, self.pos, self.stack, self.pyxconv, self.tfmconv = self.statestack.pop() + + # routines corresponding to the different reader states of the dvi maschine + + def _read_pre(self): + afile = self.file + while 1: + self.filepos = afile.tell() + cmd = afile.readuchar() + if cmd == _DVI_NOP: + pass + elif cmd == _DVI_PRE: + if afile.readuchar() != _DVI_VERSION: raise DVIError + num = afile.readuint32() + den = afile.readuint32() + self.mag = afile.readuint32() + + # For the interpretation of the lengths in dvi and tfm files, + # three conversion factors are relevant: + # - self.tfmconv: tfm units -> dvi units + # - self.pyxconv: dvi units -> (PostScript) points + # - self.conv: dvi units -> pixels + self.tfmconv = (25400000.0/num)*(den/473628672.0)/16.0 + + # calculate conv as described in the DVIType docu using + # a given resolution in dpi + self.resolution = 300.0 + self.conv = (num/254000.0)*(self.resolution/den) + + # self.pyxconv is the conversion factor from the dvi units + # to (PostScript) points. It consists of + # - self.mag/1000.0: magstep scaling + # - self.conv: conversion from dvi units to pixels + # - 1/self.resolution: conversion from pixels to inch + # - 72 : conversion from inch to points + self.pyxconv = self.mag/1000.0*self.conv/self.resolution*72 + + comment = afile.read(afile.readuchar()) + return + else: + raise DVIError + + def readpage(self, pageid=None): + """ reads a page from the dvi file + + This routine reads a page from the dvi file which is + returned as a canvas. When there is no page left in the + dvifile, None is returned and the file is closed properly.""" + + while 1: + self.filepos = self.file.tell() + cmd = self.file.readuchar() + if cmd == _DVI_NOP: + pass + elif cmd == _DVI_BOP: + ispageid = [self.file.readuint32() for i in range(10)] + if pageid is not None and ispageid != pageid: + raise DVIError("invalid pageid") + if self.debug: + self.debugfile.write("%d: beginning of page %i\n" % (self.filepos, ispageid[0])) + self.file.readuint32() + break + elif cmd == _DVI_POST: + self.file.close() + return None # nothing left + else: + raise DVIError + + self.actpage = canvas.canvas() + self.actpage.markers = {} + self.pos = [0, 0, 0, 0, 0, 0] + + # currently active output: text instance currently used + self.activetext = None + + while 1: + afile = self.file + self.filepos = afile.tell() + try: + cmd = afile.readuchar() + except struct.error: + # we most probably (if the dvi file is not corrupt) hit the end of a dvi chunk, + # so we have to continue with the rest of the dvi file + self._pop_dvistring() + continue + if cmd == _DVI_NOP: + pass + if cmd >= _DVI_CHARMIN and cmd <= _DVI_CHARMAX: + self.putchar(cmd) + elif cmd >= _DVI_SET1234 and cmd < _DVI_SET1234 + 4: + self.putchar(afile.readint(cmd - _DVI_SET1234 + 1), id1234=cmd-_DVI_SET1234+1) + elif cmd == _DVI_SETRULE: + self.putrule(afile.readint32(), afile.readint32()) + elif cmd >= _DVI_PUT1234 and cmd < _DVI_PUT1234 + 4: + self.putchar(afile.readint(cmd - _DVI_PUT1234 + 1), advancepos=0, id1234=cmd-_DVI_SET1234+1) + elif cmd == _DVI_PUTRULE: + self.putrule(afile.readint32(), afile.readint32(), 0) + elif cmd == _DVI_EOP: + self.flushtext() + if self.debug: + self.debugfile.write("%d: eop\n \n" % self.filepos) + return self.actpage + elif cmd == _DVI_PUSH: + self.stack.append(list(self.pos)) + if self.debug: + self.debugfile.write("%s: push\n" + "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" % + ((self.filepos, len(self.stack)-1) + tuple(self.pos))) + elif cmd == _DVI_POP: + self.flushtext() + self.pos = self.stack.pop() + if self.debug: + self.debugfile.write("%s: pop\n" + "level %d:(h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=???,vv=???)\n" % + ((self.filepos, len(self.stack)) + tuple(self.pos))) + elif cmd >= _DVI_RIGHT1234 and cmd < _DVI_RIGHT1234 + 4: + self.flushtext() + dh = afile.readint(cmd - _DVI_RIGHT1234 + 1, 1) + if self.debug: + self.debugfile.write("%d: right%d %d h:=%d%+d=%d, hh:=???\n" % + (self.filepos, + cmd - _DVI_RIGHT1234 + 1, + dh, + self.pos[_POS_H], + dh, + self.pos[_POS_H]+dh)) + self.pos[_POS_H] += dh + elif cmd == _DVI_W0: + self.flushtext() + if self.debug: + self.debugfile.write("%d: w0 %d h:=%d%+d=%d, hh:=???\n" % + (self.filepos, + self.pos[_POS_W], + self.pos[_POS_H], + self.pos[_POS_W], + self.pos[_POS_H]+self.pos[_POS_W])) + self.pos[_POS_H] += self.pos[_POS_W] + elif cmd >= _DVI_W1234 and cmd < _DVI_W1234 + 4: + self.flushtext() + self.pos[_POS_W] = afile.readint(cmd - _DVI_W1234 + 1, 1) + if self.debug: + self.debugfile.write("%d: w%d %d h:=%d%+d=%d, hh:=???\n" % + (self.filepos, + cmd - _DVI_W1234 + 1, + self.pos[_POS_W], + self.pos[_POS_H], + self.pos[_POS_W], + self.pos[_POS_H]+self.pos[_POS_W])) + self.pos[_POS_H] += self.pos[_POS_W] + elif cmd == _DVI_X0: + self.flushtext() + if self.debug: + self.debugfile.write("%d: x0 %d h:=%d%+d=%d, hh:=???\n" % + (self.filepos, + self.pos[_POS_X], + self.pos[_POS_H], + self.pos[_POS_X], + self.pos[_POS_H]+self.pos[_POS_X])) + self.pos[_POS_H] += self.pos[_POS_X] + elif cmd >= _DVI_X1234 and cmd < _DVI_X1234 + 4: + self.flushtext() + self.pos[_POS_X] = afile.readint(cmd - _DVI_X1234 + 1, 1) + if self.debug: + self.debugfile.write("%d: x%d %d h:=%d%+d=%d, hh:=???\n" % + (self.filepos, + cmd - _DVI_X1234 + 1, + self.pos[_POS_X], + self.pos[_POS_H], + self.pos[_POS_X], + self.pos[_POS_H]+self.pos[_POS_X])) + self.pos[_POS_H] += self.pos[_POS_X] + elif cmd >= _DVI_DOWN1234 and cmd < _DVI_DOWN1234 + 4: + self.flushtext() + dv = afile.readint(cmd - _DVI_DOWN1234 + 1, 1) + if self.debug: + self.debugfile.write("%d: down%d %d v:=%d%+d=%d, vv:=???\n" % + (self.filepos, + cmd - _DVI_DOWN1234 + 1, + dv, + self.pos[_POS_V], + dv, + self.pos[_POS_V]+dv)) + self.pos[_POS_V] += dv + elif cmd == _DVI_Y0: + self.flushtext() + if self.debug: + self.debugfile.write("%d: y0 %d v:=%d%+d=%d, vv:=???\n" % + (self.filepos, + self.pos[_POS_Y], + self.pos[_POS_V], + self.pos[_POS_Y], + self.pos[_POS_V]+self.pos[_POS_Y])) + self.pos[_POS_V] += self.pos[_POS_Y] + elif cmd >= _DVI_Y1234 and cmd < _DVI_Y1234 + 4: + self.flushtext() + self.pos[_POS_Y] = afile.readint(cmd - _DVI_Y1234 + 1, 1) + if self.debug: + self.debugfile.write("%d: y%d %d v:=%d%+d=%d, vv:=???\n" % + (self.filepos, + cmd - _DVI_Y1234 + 1, + self.pos[_POS_Y], + self.pos[_POS_V], + self.pos[_POS_Y], + self.pos[_POS_V]+self.pos[_POS_Y])) + self.pos[_POS_V] += self.pos[_POS_Y] + elif cmd == _DVI_Z0: + self.flushtext() + if self.debug: + self.debugfile.write("%d: z0 %d v:=%d%+d=%d, vv:=???\n" % + (self.filepos, + self.pos[_POS_Z], + self.pos[_POS_V], + self.pos[_POS_Z], + self.pos[_POS_V]+self.pos[_POS_Z])) + self.pos[_POS_V] += self.pos[_POS_Z] + elif cmd >= _DVI_Z1234 and cmd < _DVI_Z1234 + 4: + self.flushtext() + self.pos[_POS_Z] = afile.readint(cmd - _DVI_Z1234 + 1, 1) + if self.debug: + self.debugfile.write("%d: z%d %d v:=%d%+d=%d, vv:=???\n" % + (self.filepos, + cmd - _DVI_Z1234 + 1, + self.pos[_POS_Z], + self.pos[_POS_V], + self.pos[_POS_Z], + self.pos[_POS_V]+self.pos[_POS_Z])) + self.pos[_POS_V] += self.pos[_POS_Z] + elif cmd >= _DVI_FNTNUMMIN and cmd <= _DVI_FNTNUMMAX: + self.usefont(cmd - _DVI_FNTNUMMIN, 0) + elif cmd >= _DVI_FNT1234 and cmd < _DVI_FNT1234 + 4: + # note that according to the DVI docs, for four byte font numbers, + # the font number is signed. Don't ask why! + fntnum = afile.readint(cmd - _DVI_FNT1234 + 1, cmd == _DVI_FNT1234 + 3) + self.usefont(fntnum, id1234=cmd-_DVI_FNT1234+1) + elif cmd >= _DVI_SPECIAL1234 and cmd < _DVI_SPECIAL1234 + 4: + self.special(afile.read(afile.readint(cmd - _DVI_SPECIAL1234 + 1))) + elif cmd >= _DVI_FNTDEF1234 and cmd < _DVI_FNTDEF1234 + 4: + if cmd == _DVI_FNTDEF1234: + num = afile.readuchar() + elif cmd == _DVI_FNTDEF1234+1: + num = afile.readuint16() + elif cmd == _DVI_FNTDEF1234+2: + num = afile.readuint24() + elif cmd == _DVI_FNTDEF1234+3: + # Cool, here we have according to docu a signed int. Why? + num = afile.readint32() + self.definefont(cmd-_DVI_FNTDEF1234+1, + num, + afile.readint32(), + afile.readint32(), + afile.readint32(), + afile.read(afile.readuchar()+afile.readuchar())) + else: + raise DVIError + + +############################################################################## +# VF file handling +############################################################################## + +_VF_LONG_CHAR = 242 # character packet (long version) +_VF_FNTDEF1234 = _DVI_FNTDEF1234 # font definition +_VF_PRE = _DVI_PRE # preamble +_VF_POST = _DVI_POST # postamble + +_VF_ID = 202 # VF id byte + +class VFError(exceptions.Exception): pass + +class vffile: + def __init__(self, filename, scale, tfmconv, pyxconv, fontmap, debug=0): + self.filename = filename + self.scale = scale + self.tfmconv = tfmconv + self.pyxconv = pyxconv + self.fontmap = fontmap + self.debug = debug + self.fonts = {} # used fonts + self.widths = {} # widths of defined chars + self.chardefs = {} # dvi chunks for defined chars + + afile = binfile(self.filename, "rb") + + cmd = afile.readuchar() + if cmd == _VF_PRE: + if afile.readuchar() != _VF_ID: raise VFError + comment = afile.read(afile.readuchar()) + self.cs = afile.readuint32() + self.ds = afile.readuint32() + else: + raise VFError + + while 1: + cmd = afile.readuchar() + if cmd >= _VF_FNTDEF1234 and cmd < _VF_FNTDEF1234 + 4: + # font definition + if cmd == _VF_FNTDEF1234: + num = afile.readuchar() + elif cmd == _VF_FNTDEF1234+1: + num = afile.readuint16() + elif cmd == _VF_FNTDEF1234+2: + num = afile.readuint24() + elif cmd == _VF_FNTDEF1234+3: + num = afile.readint32() + c = afile.readint32() + s = afile.readint32() # relative scaling used for font (fix_word) + d = afile.readint32() # design size of font + fontname = afile.read(afile.readuchar()+afile.readuchar()) + + # rescaled size of font: s is relative to the scaling + # of the virtual font itself. Note that realscale has + # to be a fix_word (like s) + # XXX: check rounding + reals = int(round(self.scale * (16*self.ds/16777216L) * s)) + + # print ("defining font %s -- VF scale: %g, VF design size: %d, relative font size: %d => real size: %d" % + # (fontname, self.scale, self.ds, s, reals) + # ) + + # XXX allow for virtual fonts here too + self.fonts[num] = font(fontname, c, reals, d, self.tfmconv, self.pyxconv, self.fontmap, self.debug > 1) + elif cmd == _VF_LONG_CHAR: + # character packet (long form) + pl = afile.readuint32() # packet length + cc = afile.readuint32() # char code (assumed unsigned, but anyhow only 0 <= cc < 255 is actually used) + tfm = afile.readuint24() # character width + dvi = afile.read(pl) # dvi code of character + self.widths[cc] = tfm + self.chardefs[cc] = dvi + elif cmd < _VF_LONG_CHAR: + # character packet (short form) + cc = afile.readuchar() # char code + tfm = afile.readuint24() # character width + dvi = afile.read(cmd) + self.widths[cc] = tfm + self.chardefs[cc] = dvi + elif cmd == _VF_POST: + break + else: + raise VFError + + afile.close() + + def getfonts(self): + return self.fonts + + def getchar(self, cc): + return self.chardefs[cc] + + diff --git a/compiler/gdsMill/pyx/epsfile.py b/compiler/gdsMill/pyx/epsfile.py new file mode 100644 index 00000000..b008d68f --- /dev/null +++ b/compiler/gdsMill/pyx/epsfile.py @@ -0,0 +1,338 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import string +import canvas, bbox, pykpathsea, unit, trafo, pswriter + +# PostScript-procedure definitions (cf. 5002.EPSF_Spec_v3.0.pdf) +# with important correction in EndEPSF: +# end operator is missing in the spec! + +_BeginEPSF = pswriter.PSdefinition("BeginEPSF", """{ + /b4_Inc_state save def + /dict_count countdictstack def + /op_count count 1 sub def + userdict begin + /showpage { } def + 0 setgray 0 setlinecap + 1 setlinewidth 0 setlinejoin + 10 setmiterlimit [ ] 0 setdash newpath + /languagelevel where + {pop languagelevel + 1 ne + {false setstrokeadjust false setoverprint + } if + } if +} bind""") + +_EndEPSF = pswriter.PSdefinition("EndEPSF", """{ + end + count op_count sub {pop} repeat + countdictstack dict_count sub {end} repeat + b4_Inc_state restore +} bind""") + + +class linefilereader: + """a line by line file reader + + This line by line file reader allows for '\n', '\r' and + '\r\n' as line separation characters. Line separation + characters are not modified (binary mode). It implements + a readline, a read and a close method similar to a regular + file object.""" + + # note: '\n\r' is not considered to be a linebreak as its documented + # in the DSC spec #5001, while '\n\r' *is* a *single* linebreak + # according to the EPSF spec #5002 + + def __init__(self, filename, typicallinelen=257): + """Opens the file filename for reading. + + typicallinelen defines the default buffer increase + to find the next linebreak.""" + + # note: The maximal line size in an EPS is 255 plus the + # linebreak characters. However, we also handle + # lines longer than that. + + self.file = open(filename, "rb") + self.buffer = "" + self.typicallinelen = typicallinelen + + def read(self, count=None, EOFmsg="unexpected end of file"): + """read bytes from the file + + count is the number of bytes to be read when set. Then count + is unset, the rest of the file is returned. EOFmsg is used + to raise a IOError, when the end of the file is reached while + reading count bytes or when the rest of the file is empty when + count is unset. When EOFmsg is set to None, less than the + requested number of bytes might be returned.""" + if count is not None: + if count > len(self.buffer): + self.buffer += self.file.read(count - len(self.buffer)) + if EOFmsg is not None and len(self.buffer) < count: + raise IOError(EOFmsg) + result = self.buffer[:count] + self.buffer = self.buffer[count:] + return result + else: + self.buffer += self.file.read() + if EOFmsg is not None and not len(self.buffer): + raise IOError(EOFmsg) + result = self.buffer + self.buffer = "" + return result + + def readline(self, EOFmsg="unexpected end of file"): + """reads a line from the file + + Lines are separated by '\n', '\r' or '\r\n'. The line separation + strings are included in the return value. The last line might not + end with an line separation string. Reading beyond the file generates + an IOError with the EOFmsg message. When EOFmsg is None, an empty + string is returned when reading beyond the end of the file.""" + EOF = 0 + while 1: + crpos = self.buffer.find("\r") + nlpos = self.buffer.find("\n") + if nlpos == -1 and (crpos == -1 or crpos == len(self.buffer) - 1) and not EOF: + newbuffer = self.file.read(self.typicallinelen) + if not len(newbuffer): + EOF = 1 + self.buffer += newbuffer + else: + eol = len(self.buffer) + if not eol and EOFmsg is not None: + raise IOError(EOFmsg) + if nlpos != -1: + eol = nlpos + 1 + if crpos != -1 and (nlpos == -1 or crpos < nlpos - 1): + eol = crpos + 1 + result = self.buffer[:eol] + self.buffer = self.buffer[eol:] + return result + + def close(self): + "closes the file" + self.file.close() + + +def _readbbox(filename): + """returns bounding box of EPS file filename""" + + file = linefilereader(filename) + + # check the %! header comment + if not file.readline().startswith("%!"): + raise IOError("file doesn't start with a '%!' header comment") + + bboxatend = 0 + # parse the header (use the first BoundingBox) + while 1: + line = file.readline() + if not line: + break + if line.startswith("%%BoundingBox:") and not bboxatend: + values = line.split(":", 1)[1].split() + if values == ["(atend)"]: + bboxatend = 1 + else: + if len(values) != 4: + raise IOError("invalid number of bounding box values") + return bbox.bbox_pt(*map(int, values)) + elif (line.rstrip() == "%%EndComments" or + (len(line) >= 2 and line[0] != "%" and line[1] not in string.whitespace)): + # implicit end of comments section + break + if not bboxatend: + raise IOError("no bounding box information found") + + # parse the body + nesting = 0 # allow for nested documents + while 1: + line = file.readline() + if line.startswith("%%BeginData:"): + values = line.split(":", 1)[1].split() + if len(values) > 3: + raise IOError("invalid number of arguments") + if len(values) == 3: + if values[2] == "Lines": + for i in xrange(int(values[0])): + file.readline() + elif values[2] != "Bytes": + raise IOError("invalid bytesorlines-value") + else: + file.read(int(values[0])) + else: + file.read(int(values[0])) + line = file.readline() + # ignore tailing whitespace/newline for binary data + if (len(values) < 3 or values[2] != "Lines") and not len(line.strip()): + line = file.readline() + if line.rstrip() != "%%EndData": + raise IOError("missing EndData") + elif line.startswith("%%BeginBinary:"): + file.read(int(line.split(":", 1)[1])) + line = file.readline() + # ignore tailing whitespace/newline + if not len(line.strip()): + line = file.readline() + if line.rstrip() != "%%EndBinary": + raise IOError("missing EndBinary") + elif line.startswith("%%BeginDocument:"): + nesting += 1 + elif line.rstrip() == "%%EndDocument": + if nesting < 1: + raise IOError("unmatched EndDocument") + nesting -= 1 + elif not nesting and line.rstrip() == "%%Trailer": + break + + usebbox = None + # parse the trailer (use the last BoundingBox) + line = True + while line: + line = file.readline(EOFmsg=None) + if line.startswith("%%BoundingBox:"): + values = line.split(":", 1)[1].split() + if len(values) != 4: + raise IOError("invalid number of bounding box values") + usebbox = bbox.bbox_pt(*map(int, values)) + if not usebbox: + raise IOError("missing bounding box information in document trailer") + return usebbox + + +class epsfile(canvas.canvasitem): + + """class for epsfiles""" + + def __init__(self, + x, y, filename, + width=None, height=None, scale=None, align="bl", + clip=1, translatebbox=1, bbox=None, + kpsearch=0): + """inserts epsfile + + Object for an EPS file named filename at position (x,y). Width, height, + scale and aligment can be adjusted by the corresponding parameters. If + clip is set, the result gets clipped to the bbox of the EPS file. If + translatebbox is not set, the EPS graphics is not translated to the + corresponding origin. If bbox is not None, it overrides the bounding + box in the epsfile itself. If kpsearch is set then filename is searched + using the kpathsea library. + """ + + self.x_pt = unit.topt(x) + self.y_pt = unit.topt(y) + if kpsearch: + self.filename = pykpathsea.find_file(filename, pykpathsea.kpse_pict_format) + else: + self.filename = filename + self.mybbox = bbox or _readbbox(self.filename) + + # determine scaling in x and y direction + self.scalex = self.scaley = scale + + if width is not None or height is not None: + if scale is not None: + raise ValueError("cannot set both width and/or height and scale simultaneously") + if height is not None: + self.scaley = unit.topt(height)/(self.mybbox.ury_pt-self.mybbox.lly_pt) + if width is not None: + self.scalex = unit.topt(width)/(self.mybbox.urx_pt-self.mybbox.llx_pt) + + if self.scalex is None: + self.scalex = self.scaley + if self.scaley is None: + self.scaley = self.scalex + + # set the actual width and height of the eps file (after a + # possible scaling) + self.width_pt = self.mybbox.urx_pt-self.mybbox.llx_pt + if self.scalex: + self.width_pt *= self.scalex + + self.height_pt = self.mybbox.ury_pt-self.mybbox.lly_pt + if self.scaley: + self.height_pt *= self.scaley + + # take alignment into account + self.align = align + if self.align[0]=="b": + pass + elif self.align[0]=="c": + self.y_pt -= self.height_pt/2.0 + elif self.align[0]=="t": + self.y_pt -= self.height_pt + else: + raise ValueError("vertical alignment can only be b (bottom), c (center), or t (top)") + + if self.align[1]=="l": + pass + elif self.align[1]=="c": + self.x_pt -= self.width_pt/2.0 + elif self.align[1]=="r": + self.x_pt -= self.width_pt + else: + raise ValueError("horizontal alignment can only be l (left), c (center), or r (right)") + + self.clip = clip + self.translatebbox = translatebbox + + self.trafo = trafo.translate_pt(self.x_pt, self.y_pt) + + if self.scalex is not None: + self.trafo = self.trafo * trafo.scale_pt(self.scalex, self.scaley) + + if translatebbox: + self.trafo = self.trafo * trafo.translate_pt(-self.mybbox.llx_pt, -self.mybbox.lly_pt) + + def bbox(self): + return self.mybbox.transformed(self.trafo) + + def processPS(self, file, writer, context, registry, bbox): + registry.add(_BeginEPSF) + registry.add(_EndEPSF) + bbox += self.bbox() + try: + epsfile=open(self.filename,"rb") + except: + raise IOError, "cannot open EPS file '%s'" % self.filename + + file.write("BeginEPSF\n") + + if self.clip: + llx_pt, lly_pt, urx_pt, ury_pt = self.mybbox.transformed(self.trafo).highrestuple_pt() + file.write("%g %g %g %g rectclip\n" % (llx_pt, lly_pt, urx_pt-llx_pt, ury_pt-lly_pt)) + + self.trafo.processPS(file, writer, context, registry, bbox) + + file.write("%%%%BeginDocument: %s\n" % self.filename) + file.write(epsfile.read()) + file.write("%%EndDocument\n") + file.write("EndEPSF\n") + + def processPDF(self, file, writer, context, registry, bbox): + raise RuntimeError("Including EPS files in PDF files not supported") diff --git a/compiler/gdsMill/pyx/font/__init__.py b/compiler/gdsMill/pyx/font/__init__.py new file mode 100644 index 00000000..f65434e6 --- /dev/null +++ b/compiler/gdsMill/pyx/font/__init__.py @@ -0,0 +1,20 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA diff --git a/compiler/gdsMill/pyx/font/_t1code.c b/compiler/gdsMill/pyx/font/_t1code.c new file mode 100644 index 00000000..f806fed2 --- /dev/null +++ b/compiler/gdsMill/pyx/font/_t1code.c @@ -0,0 +1,118 @@ +/* t1code.c: Copyright 2005 Jörg Lehmann + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +#define C1 52845 +#define C2 22719 + +/* +def decoder(code, r, n): + c1 = 52845 + c2 = 22719 + data = array.array("B") + for x in array.array("B", code): + data.append(x ^ (r >> 8)) + r = ((x + r) * c1 + c2) & 0xffff + return data.tostring()[n:] + +*/ + +static PyObject *py_decoder(PyObject *self, PyObject *args) +{ + unsigned char *code; + int lcode, pr, n; + + if (PyArg_ParseTuple(args, "s#ii", (char **) &code, &lcode, &pr, &n)) { + unsigned char *data; + int i; + unsigned char x; + uint16_t r=pr; + PyObject *result; + + if (! (data = (unsigned char *) malloc(lcode)) ) + return NULL; + + for (i=0; i> 8); + r = (x + r) * C1 + C2; + } + + /* convert result to string stripping first n chars */ + result = PyString_FromStringAndSize((const char *)data + n, lcode - n); + free(data); + return result; + } + else return NULL; + +} + +/* +def encoder(data, r, random): + c1 = 52845 + c2 = 22719 + code = array.array("B") + for x in array.array("B", random+data): + code.append(x ^ (r >> 8)) + r = ((code[-1] + r) * c1 + c2) & 0xffff; + return code.tostring() +*/ + +static PyObject *py_encoder(PyObject *self, PyObject *args) +{ + unsigned char *data; + unsigned char *random; + int ldata, pr, lrandom; + + if (PyArg_ParseTuple(args, "s#is#", (char **) &data, &ldata, &pr, (char **) &random, &lrandom)) { + unsigned char *code; + int i; + uint16_t r=pr; + PyObject *result; + + if (! (code = (unsigned char *) malloc(ldata + lrandom)) ) + return NULL; + + for (i=0; i> 8); + r = (code[i] + r) * C1 + C2; + } + + for (i=0; i> 8); + r = (code[i+lrandom] + r) * C1 + C2; + } + + result = PyString_FromStringAndSize((const char *)code, ldata + lrandom); + free(code); + return result; + } + else return NULL; + +} + + + +/* exported methods */ + +static PyMethodDef t1code_methods[] = { + {"decoder", py_decoder, METH_VARARGS}, + {"encoder", py_encoder, METH_VARARGS}, + {NULL, NULL} +}; + +void init_t1code(void) { + (void) Py_InitModule("_t1code", t1code_methods); +} diff --git a/compiler/gdsMill/pyx/font/afm.py b/compiler/gdsMill/pyx/font/afm.py new file mode 100644 index 00000000..2e95c867 --- /dev/null +++ b/compiler/gdsMill/pyx/font/afm.py @@ -0,0 +1,489 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2006 Jörg Lehmann +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import string + +class AFMError(Exception): + pass + +# reader states +_READ_START = 0 +_READ_MAIN = 1 +_READ_DIRECTION = 2 +_READ_CHARMETRICS = 3 +_READ_KERNDATA = 4 +_READ_TRACKKERN = 5 +_READ_KERNPAIRS = 6 +_READ_COMPOSITES = 7 +_READ_END = 8 + +# various parsing functions +def _parseint(s): + try: + return int(s) + except: + raise AFMError("Expecting int, got '%s'" % s) + +def _parsehex(s): + try: + if s[0] != "<" or s[-1] != ">": + raise AFMError() + return int(s[1:-1], 16) + except: + raise AFMError("Expecting hexadecimal int, got '%s'" % s) + +def _parsefloat(s): + try: + return float(s) + except: + raise AFMError("Expecting float, got '%s'" % s) + +def _parsefloats(s, nos): + try: + numbers = s.split() + result = map(float, numbers) + if len(result) != nos: + raise AFMError() + except: + raise AFMError("Expecting list of %d numbers, got '%s'" % (s, nos)) + return result + +def _parsestr(s): + # XXX: check for invalid characters in s + return s + +def _parsebool(s): + s = s.rstrip() + if s == "true": + return 1 + elif s == "false": + return 0 + else: + raise AFMError("Expecting boolean, got '%s'" % s) + + +class AFMcharmetrics: + def __init__(self, code, widths=None, vvector=None, name=None, bbox=None, ligatures=None): + self.code = code + if widths is None: + self.widths = [None] * 2 + else: + self.widths = widths + self.vvector = vvector + self.name = name + self.bbox = bbox + if ligatures is None: + self.ligatures = [] + else: + self.ligatures = ligatures + + +class AFMtrackkern: + def __init__(self, degree, min_ptsize, min_kern, max_ptsize, max_kern): + self.degree = degree + self.min_ptsize = min_ptsize + self.min_kern = min_kern + self.max_ptsize = max_ptsize + self.max_kern = max_kern + + +class AFMkernpair: + def __init__(self, name1, name2, x, y): + self.name1 = name1 + self.name2 = name2 + self.x = x + self.y = y + + +class AFMcomposite: + def __init__(self, name, parts): + self.name = name + self.parts = parts + + +class AFMfile: + + def __init__(self, filename): + self.filename = filename + self.metricssets = 0 # int, optional + self.fontname = None # str, required + self.fullname = None # str, optional + self.familyname = None # str, optional + self.weight = None # str, optional + self.fontbbox = None # 4 floats, required + self.version = None # str, optional + self.notice = None # str, optional + self.encodingscheme = None # str, optional + self.mappingscheme = None # int, optional (not present in base font programs) + self.escchar = None # int, required if mappingscheme == 3 + self.characterset = None # str, optional + self.characters = None # int, optional + self.isbasefont = 1 # bool, optional + self.vvector = None # 2 floats, required if metricssets == 2 + self.isfixedv = None # bool, default: true if vvector present, false otherwise + self.capheight = None # float, optional + self.xheight = None # float, optional + self.ascender = None # float, optional + self.descender = None # float, optional + self.underlinepositions = [None] * 2 # int, optional (for each direction) + self.underlinethicknesses = [None] * 2 # float, optional (for each direction) + self.italicangles = [None] * 2 # float, optional (for each direction) + self.charwidths = [None] * 2 # 2 floats, optional (for each direction) + self.isfixedpitchs = [None] * 2 # bool, optional (for each direction) + self.charmetrics = None # list of character metrics information, optional + self.trackkerns = None # list of track kernings, optional + self.kernpairs = [None] * 2 # list of list of kerning pairs (for each direction), optional + self.composites = None # list of composite character data sets, optional + self.parse() + if self.isfixedv is None: + self.isfixedv = self.vvector is not None + # XXX we should check the constraints on some parameters + + # the following methods process a line when the reader is in the corresponding + # state and return the new state + def _processline_start(self, line): + key, args = line.split(None, 1) + if key != "StartFontMetrics": + raise AFMError("Expecting StartFontMetrics, no found") + return _READ_MAIN, None + + def _processline_main(self, line): + try: + key, args = line.split(None, 1) + except ValueError: + key = line.rstrip() + if key == "Comment": + return _READ_MAIN, None + elif key == "MetricsSets": + self.metricssets = _parseint(args) + if direction is not None: + raise AFMError("MetricsSets not allowed after first (implicit) StartDirection") + elif key == "FontName": + self.fontname = _parsestr(args) + elif key == "FullName": + self.fullname = _parsestr(args) + elif key == "FamilyName": + self.familyname = _parsestr(args) + elif key == "Weight": + self.weight = _parsestr(args) + elif key == "FontBBox": + self.fontbbox = _parsefloats(args, 4) + elif key == "Version": + self.version = _parsestr(args) + elif key == "Notice": + self.notice = _parsestr(args) + elif key == "EncodingScheme": + self.encodingscheme = _parsestr(args) + elif key == "MappingScheme": + self.mappingscheme = _parseint(args) + elif key == "EscChar": + self.escchar = _parseint(args) + elif key == "CharacterSet": + self.characterset = _parsestr(args) + elif key == "Characters": + self.characters = _parseint(args) + elif key == "IsBaseFont": + self.isbasefont = _parsebool(args) + elif key == "VVector": + self.vvector = _parsefloats(args, 2) + elif key == "IsFixedV": + self.isfixedv = _parsebool(args) + elif key == "CapHeight": + self.capheight = _parsefloat(args) + elif key == "XHeight": + self.xheight = _parsefloat(args) + elif key == "Ascender": + self.ascender = _parsefloat(args) + elif key == "Descender": + self.descender = _parsefloat(args) + elif key == "StartDirection": + direction = _parseint(args) + if 0 <= direction <= 2: + return _READ_DIRECTION, direction + else: + raise AFMError("Wrong direction number %d" % direction) + elif (key == "UnderLinePosition" or key == "UnderlineThickness" or key == "ItalicAngle" or + key == "Charwidth" or key == "IsFixedPitch"): + # we implicitly entered a direction section, so we should process the line again + return self._processline_direction(line, 0) + elif key == "StartCharMetrics": + if self.charmetrics is not None: + raise AFMError("Multiple character metrics sections") + self.charmetrics = [None] * _parseint(args) + return _READ_CHARMETRICS, 0 + elif key == "StartKernData": + return _READ_KERNDATA, None + elif key == "StartComposites": + if self.composites is not None: + raise AFMError("Multiple composite character data sections") + self.composites = [None] * _parseint(args) + return _READ_COMPOSITES, 0 + elif key == "EndFontMetrics": + return _READ_END, None + elif key[0] in string.lowercase: + # ignoring private commands + pass + return _READ_MAIN, None + + def _processline_direction(self, line, direction): + try: + key, args = line.split(None, 1) + except ValueError: + key = line.rstrip() + if key == "UnderLinePosition": + self.underlinepositions[direction] = _parseint(args) + elif key == "UnderlineThickness": + self.underlinethicknesses[direction] = _parsefloat(args) + elif key == "ItalicAngle": + self.italicangles[direction] = _parsefloat(args) + elif key == "Charwidth": + self.charwidths[direction] = _parsefloats(args, 2) + elif key == "IsFixedPitch": + self.isfixedpitchs[direction] = _parsebool(args) + elif key == "EndDirection": + return _READ_MAIN, None + else: + # we assume that we are implicitly leaving the direction section again, + # so try to reprocess the line in the header reader state + return self._processline_main(line) + return _READ_DIRECTION, direction + + def _processline_charmetrics(self, line, charno): + if line.rstrip() == "EndCharMetrics": + if charno != len(self.charmetrics): + raise AFMError("Fewer character metrics than expected") + return _READ_MAIN, None + if charno >= len(self.charmetrics): + raise AFMError("More character metrics than expected") + + char = None + for s in line.split(";"): + s = s.strip() + if not s: + continue + key, args = s.split(None, 1) + if key == "C": + if char is not None: + raise AFMError("Cannot define char code twice") + char = AFMcharmetrics(_parseint(args)) + elif key == "CH": + if char is not None: + raise AFMError("Cannot define char code twice") + char = AFMcharmetrics(_parsehex(args)) + elif key == "WX" or key == "W0X": + char.widths[0] = _parsefloat(args), 0 + elif key == "W1X": + char.widths[1] = _parsefloat(args), 0 + elif key == "WY" or key == "W0Y": + char.widths[0] = 0, _parsefloat(args) + elif key == "W1Y": + char.widths[1] = 0, _parsefloat(args) + elif key == "W" or key == "W0": + char.widths[0] = _parsefloats(args, 2) + elif key == "W1": + char.widths[1] = _parsefloats(args, 2) + elif key == "VV": + char.vvector = _parsefloats(args, 2) + elif key == "N": + # XXX: we should check that name is valid (no whitespcae, etc.) + char.name = _parsestr(args) + elif key == "B": + char.bbox = _parsefloats(args, 4) + elif key == "L": + successor, ligature = args.split(None, 1) + char.ligatures.append((_parsestr(successor), ligature)) + else: + raise AFMError("Undefined command in character widths specification: '%s'", s) + if char is None: + raise AFMError("Character metrics not defined") + + self.charmetrics[charno] = char + return _READ_CHARMETRICS, charno+1 + + def _processline_kerndata(self, line): + try: + key, args = line.split(None, 1) + except ValueError: + key = line.rstrip() + if key == "Comment": + return _READ_KERNDATA, None + if key == "StartTrackKern": + if self.trackkerns is not None: + raise AFMError("Multiple track kernings data sections") + self.trackkerns = [None] * _parseint(args) + return _READ_TRACKKERN, 0 + elif key == "StartKernPairs" or key == "StartKernPairs0": + if self.kernpairs[0] is not None: + raise AFMError("Multiple kerning pairs data sections for direction 0") + self.kernpairs[0] = [None] * _parseint(args) + return _READ_KERNPAIRS, (0, 0) + elif key == "StartKernPairs1": + if self.kernpairs[1] is not None: + raise AFMError("Multiple kerning pairs data sections for direction 0") + self.kernpairs[1] = [None] * _parseint(args) + return _READ_KERNPAIRS, (1, 0) + elif key == "EndKernData": + return _READ_MAIN, None + else: + raise AFMError("Unsupported key %s in kerning data section" % key) + + def _processline_trackkern(self, line, i): + try: + key, args = line.split(None, 1) + except ValueError: + key = line.rstrip() + if key == "Comment": + return _READ_TRACKKERN, i + elif key == "TrackKern": + if i >= len(self.trackkerns): + raise AFMError("More track kerning data sets than expected") + degrees, args = args.split(None, 1) + self.trackkerns[i] = AFMtrackkern(int(degrees), *_parsefloats(args, 4)) + return _READ_TRACKKERN, i+1 + elif key == "EndTrackKern": + if i < len(self.trackkerns): + raise AFMError("Fewer track kerning data sets than expected") + return _READ_KERNDATA, None + else: + raise AFMError("Unsupported key %s in kerning data section" % key) + + def _processline_kernpairs(self, line, (direction, i)): + try: + key, args = line.split(None, 1) + except ValueError: + key = line.rstrip() + if key == "Comment": + return _READ_KERNPAIRS, (direction, i) + elif key == "EndKernPairs": + if i < len(self.kernpairs[direction]): + raise AFMError("Fewer kerning pairs than expected") + return _READ_KERNDATA, None + else: + if i >= len(self.kernpairs[direction]): + raise AFMError("More kerning pairs than expected") + if key == "KP": + try: + name1, name2, x, y = args.split() + except: + raise AFMError("Expecting name1, name2, x, y, got '%s'" % args) + self.kernpairs[direction][i] = AFMkernpair(name1, name2, _parsefloat(x), _parsefloat(y)) + elif key == "KPH": + try: + hex1, hex2, x, y = args.split() + except: + raise AFMError("Expecting , , x, y, got '%s'" % args) + self.kernpairs[direction][i] = AFMkernpair(_parsehex(hex1), _parsehex(hex2), + _parsefloat(x), _parsefloat(y)) + elif key == "KPX": + try: + name1, name2, x = args.split() + except: + raise AFMError("Expecting name1, name2, x, got '%s'" % args) + self.kernpairs[direction][i] = AFMkernpair(name1, name2, _parsefloat(x), 0) + elif key == "KPY": + try: + name1, name2, y = args.split() + except: + raise AFMError("Expecting name1, name2, x, got '%s'" % args) + self.kernpairs[direction][i] = AFMkernpair(name1, name2, 0, _parsefloat(y)) + else: + raise AFMError("Unknown key '%s' in kern pair section" % key) + return _READ_KERNPAIRS, (direction, i+1) + + def _processline_composites(self, line, i): + if line.rstrip() == "EndComposites": + if i < len(self.composites): + raise AFMError("Fewer composite character data sets than expected") + return _READ_MAIN, None + if i >= len(self.composites): + raise AFMError("More composite character data sets than expected") + + name = None + no = None + parts = [] + for s in line.split(";"): + s = s.strip() + if not s: + continue + key, args = s.split(None, 1) + if key == "CC": + try: + name, no = args.split() + except: + raise AFMError("Expecting name number, got '%s'" % args) + no = _parseint(no) + elif key == "PCC": + try: + name1, x, y = args.split() + except: + raise AFMError("Expecting name x y, got '%s'" % args) + parts.append((name1, _parsefloat(x), _parsefloat(y))) + else: + raise AFMError("Unknown key '%s' in composite character data section" % key) + if len(parts) != no: + raise AFMError("Wrong number of composite characters") + self.composites[i] = AFMcomposite(name, parts) + return _READ_COMPOSITES, i+1 + + def parse(self): + f = open(self.filename, "r") + try: + # state of the reader, consisting of + # - the main state, i.e. the type of the section + # - a parameter sstate + state = _READ_START, None + # Note that we do a line by line processing here, since one + # of the states (_READ_DIRECTION) can be entered implicitly, i.e. + # without a corresponding StartDirection section and we thus + # may need to reprocess a line in the context of the new state + for line in f: + line = line[:-1] + mstate, sstate = state + if mstate == _READ_START: + state = self._processline_start(line) + else: + # except for the first line, any empty will be ignored + if not line.strip(): + continue + if mstate == _READ_MAIN: + state = self._processline_main(line) + elif mstate == _READ_DIRECTION: + state = self._processline_direction(line, sstate) + elif mstate == _READ_CHARMETRICS: + state = self._processline_charmetrics(line, sstate) + elif mstate == _READ_KERNDATA: + state = self._processline_kerndata(line) + elif mstate == _READ_TRACKKERN: + state = self._processline_trackkern(line, sstate) + elif mstate == _READ_KERNPAIRS: + state = self._processline_kernpairs(line, sstate) + elif mstate == _READ_COMPOSITES: + state = self._processline_composites(line, sstate) + else: + raise RuntimeError("Undefined state in AFM reader") + finally: + f.close() + +if __name__ == "__main__": + a = AFMfile("/opt/local/share/texmf-dist/fonts/afm/yandy/lucida/lbc.afm") + print a.charmetrics[0].name + a = AFMfile("/usr/share/enscript/hv.afm") + print a.charmetrics[32].name diff --git a/compiler/gdsMill/pyx/font/encoding.py b/compiler/gdsMill/pyx/font/encoding.py new file mode 100644 index 00000000..0503d4fe --- /dev/null +++ b/compiler/gdsMill/pyx/font/encoding.py @@ -0,0 +1,90 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 Jörg Lehmann +# Copyright (C) 2005-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +class encoding: + + def __init__(self, encvector): + self.encvector = encvector + + def decode(self, char): + return self.encvector[char] + + # XXX why do we need to pass the name during the outputPS call + def outputPS(self, file, writer, name): + file.write("%%%%BeginProcSet: %s\n" % name) + file.write("/%s\n" + "[" % self.name) + for i, glyphname in enumerate(self.encvector): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + file.write(glyphname) + file.write(" ] def\n" + "%%EndProcSet\n") + + def outputPDF(self, file, writer): + file.write("<<\n" + "/Type /Encoding\n" + "/Differences\n" + "[ 0") + for i, glyphname in enumerate(self.encvector): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + file.write(glyphname) + file.write(" ]\n" + ">>\n") + +adobestandardencoding = encoding([None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", + "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", + "zero", "one", "two", "three", "four", "five", "six", "seven", + "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", + "at", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "asciicircum", "underscore", + "quoteleft", "a", "b", "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "braceleft", "bar", "braceright", "asciitilde", None, + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + None, "exclamdown", "cent", "sterling", "fraction", "yen", "florin", "section", + "currency", "quotesingle", "quotedblleft", "guillemotleft", "guilsinglleft", "guilsinglright", "fi", "fl", + None, "endash", "dagger", "daggerdbl", "periodcentered", None, "paragraph", "bullet", + "quotesinglbase", "quotedblbase", "quotedblright", "guillemotright", "ellipsis", "perthousand", None, "questiondown", + None, "grave", "acute", "circumflex", "tilde", "macron", "breve", "dotaccent", + "dieresis", None, "ring", "cedilla", None, "hungarumlaut", "ogonek", "caron", + "emdash", None, None, None, None, None, None, None, + None, None, None, None, None, None, None, None, + None, "AE", None, "ordfeminine", None, None, None, None, + "Lslash", "Oslash", "OE", "ordmasculine", None, None, None, None, + None, "ae", None, None, None, "dotlessi", None, None, + "lslash", "oslash", "oe", "germandbls", None, None, None, None]) diff --git a/compiler/gdsMill/pyx/font/t1code.py b/compiler/gdsMill/pyx/font/t1code.py new file mode 100644 index 00000000..b16b8283 --- /dev/null +++ b/compiler/gdsMill/pyx/font/t1code.py @@ -0,0 +1,42 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import array + +c1 = 52845 +c1_16, c1_8 = divmod(c1, 0x100) # to avoid overflow (or conversion to the slow long integers) +c2 = 22719 + +def decoder(code, r, n): + plain = array.array("B") + for x in array.array("B", code): + plain.append(x ^ (r >> 8)) + # r = ((x + r) * c1 + c2) & 0xffff # this might overflow + r = ((((x + r) * c1_16) & 0xff) * 0x100 + (x + r) * c1_8 + c2) & 0xffff + return plain.tostring()[n:] + +def encoder(data, r, random): + code = array.array("B") + for x in array.array("B", random+data): + code.append(x ^ (r>>8)) + # r = ((code[-1] + r) * c1 + c2) & 0xffff # this might overflow + r = ((((code[-1] + r) * c1_16) & 0xff) * 0x100 + (code[-1] + r) * c1_8 + c2) & 0xffff + return code.tostring() diff --git a/compiler/gdsMill/pyx/font/t1font.py b/compiler/gdsMill/pyx/font/t1font.py new file mode 100644 index 00000000..f2b34982 --- /dev/null +++ b/compiler/gdsMill/pyx/font/t1font.py @@ -0,0 +1,1151 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 André Wobst +# Copyright (C) 2006 Jörg Lehmann +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import nested_scopes + +import array, binascii, re +try: + import zlib + haszlib = 1 +except ImportError: + haszlib = 0 + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + +from pyx import trafo +from pyx.path import path, moveto_pt, lineto_pt, curveto_pt, closepath +import encoding + +try: + from _t1code import * +except: + from t1code import * + + +class T1context: + + def __init__(self, t1font): + """context for T1cmd evaluation""" + self.t1font = t1font + + # state description + self.x = None + self.y = None + self.wx = None + self.wy = None + self.t1stack = [] + self.psstack = [] + + +###################################################################### +# T1 commands +# Note, that the T1 commands are variable-free except for plain number, +# which are stored as integers. All other T1 commands exist as a single +# instance only + +T1cmds = {} +T1subcmds = {} + +class T1cmd: + + def __init__(self, code, subcmd=0): + self.code = code + self.subcmd = subcmd + if subcmd: + T1subcmds[code] = self + else: + T1cmds[code] = self + + def __str__(self): + """returns a string representation of the T1 command""" + raise NotImplementedError + + def updatepath(self, path, trafo, context): + """update path instance applying trafo to the points""" + raise NotImplementedError + + def gathercalls(self, seacglyphs, subrs, othersubrs, context): + """gather dependancy information + + subrs is the "called-subrs" dictionary. gathercalls will insert the + subr number as key having the value 1, i.e. subrs.keys() will become the + numbers of used subrs. Similar seacglyphs will contain all glyphs in + composite characters (subrs and othersubrs for those glyphs will also + already be included) and othersubrs the othersubrs called. + + This method might will not properly update all information in the + context (especially consuming values from the stack) and will also skip + various tests for performance reasons. For most T1 commands it just + doesn't need to do anything. + """ + pass + + +# commands for starting and finishing + +class _T1endchar(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 14) + + def __str__(self): + return "endchar" + + def updatepath(self, path, trafo, context): + pass + +T1endchar = _T1endchar() + + +class _T1hsbw(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 13) + + def __str__(self): + return "hsbw" + + def updatepath(self, path, trafo, context): + sbx = context.t1stack.pop(0) + wx = context.t1stack.pop(0) + path.append(moveto_pt(*trafo.apply_pt(sbx, 0))) + context.x = sbx + context.y = 0 + context.wx = wx + context.wy = 0 + +T1hsbw = _T1hsbw() + + +class _T1seac(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 6, subcmd=1) + + def __str__(self): + return "seac" + + def updatepath(self, path, atrafo, context): + sab = context.t1stack.pop(0) + adx = context.t1stack.pop(0) + ady = context.t1stack.pop(0) + bchar = context.t1stack.pop(0) + achar = context.t1stack.pop(0) + aglyph = encoding.adobestandardencoding.decode(achar) + bglyph = encoding.adobestandardencoding.decode(bchar) + context.t1font.updateglyphpath(bglyph, path, atrafo, context) + atrafo = atrafo * trafo.translate_pt(adx-sab, ady) + context.t1font.updateglyphpath(aglyph, path, atrafo, context) + + def gathercalls(self, seacglyphs, subrs, othersubrs, context): + bchar = context.t1stack.pop() + achar = context.t1stack.pop() + aglyph = encoding.adobestandardencoding.decode(achar) + bglyph = encoding.adobestandardencoding.decode(bchar) + seacglyphs[aglyph] = 1 + seacglyphs[bglyph] = 1 + context.t1font.gatherglyphcalls(bglyph, seacglyphs, subrs, othersubrs, context) + context.t1font.gatherglyphcalls(aglyph, seacglyphs, subrs, othersubrs, context) + +T1seac = _T1seac() + + +class _T1sbw(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 7, subcmd=1) + + def __str__(self): + return "sbw" + + def updatepath(self, path, trafo, context): + sbx = context.t1stack.pop(0) + sby = context.t1stack.pop(0) + wx = context.t1stack.pop(0) + wy = context.t1stack.pop(0) + path.append(moveto_pt(*trafo.apply_pt(sbx, sby))) + context.x = sbx + context.y = sby + context.wx = wx + context.wy = wy + +T1sbw = _T1sbw() + + +# path construction commands + +class _T1closepath(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 9) + + def __str__(self): + return "closepath" + + def updatepath(self, path, trafo, context): + path.append(closepath()) + # The closepath in T1 is different from PostScripts in that it does + # *not* modify the current position; hence we need to add an additional + # moveto here ... + path.append(moveto_pt(*trafo.apply_pt(context.x, context.y))) + +T1closepath = _T1closepath() + + +class _T1hlineto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 6) + + def __str__(self): + return "hlineto" + + def updatepath(self, path, trafo, context): + dx = context.t1stack.pop(0) + path.append(lineto_pt(*trafo.apply_pt(context.x + dx, context.y))) + context.x += dx + +T1hlineto = _T1hlineto() + + +class _T1hmoveto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 22) + + def __str__(self): + return "hmoveto" + + def updatepath(self, path, trafo, context): + dx = context.t1stack.pop(0) + path.append(moveto_pt(*trafo.apply_pt(context.x + dx, context.y))) + context.x += dx + +T1hmoveto = _T1hmoveto() + + +class _T1hvcurveto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 31) + + def __str__(self): + return "hvcurveto" + + def updatepath(self, path, trafo, context): + dx1 = context.t1stack.pop(0) + dx2 = context.t1stack.pop(0) + dy2 = context.t1stack.pop(0) + dy3 = context.t1stack.pop(0) + path.append(curveto_pt(*(trafo.apply_pt(context.x + dx1, context.y) + + trafo.apply_pt(context.x + dx1 + dx2, context.y + dy2) + + trafo.apply_pt(context.x + dx1 + dx2, context.y + dy2 + dy3)))) + context.x += dx1+dx2 + context.y += dy2+dy3 + +T1hvcurveto = _T1hvcurveto() + + +class _T1rlineto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 5) + + def __str__(self): + return "rlineto" + + def updatepath(self, path, trafo, context): + dx = context.t1stack.pop(0) + dy = context.t1stack.pop(0) + path.append(lineto_pt(*trafo.apply_pt(context.x + dx, context.y + dy))) + context.x += dx + context.y += dy + +T1rlineto = _T1rlineto() + + +class _T1rmoveto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 21) + + def __str__(self): + return "rmoveto" + + def updatepath(self, path, trafo, context): + dx = context.t1stack.pop(0) + dy = context.t1stack.pop(0) + path.append(moveto_pt(*trafo.apply_pt(context.x + dx, context.y + dy))) + context.x += dx + context.y += dy + +T1rmoveto = _T1rmoveto() + + +class _T1rrcurveto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 8) + + def __str__(self): + return "rrcurveto" + + def updatepath(self, path, trafo, context): + dx1 = context.t1stack.pop(0) + dy1 = context.t1stack.pop(0) + dx2 = context.t1stack.pop(0) + dy2 = context.t1stack.pop(0) + dx3 = context.t1stack.pop(0) + dy3 = context.t1stack.pop(0) + path.append(curveto_pt(*(trafo.apply_pt(context.x + dx1, context.y + dy1) + + trafo.apply_pt(context.x + dx1 + dx2, context.y + dy1 + dy2) + + trafo.apply_pt(context.x + dx1 + dx2 + dx3, context.y + dy1 + dy2 + dy3)))) + context.x += dx1+dx2+dx3 + context.y += dy1+dy2+dy3 + +T1rrcurveto = _T1rrcurveto() + + +class _T1vlineto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 7) + + def __str__(self): + return "vlineto" + + def updatepath(self, path, trafo, context): + dy = context.t1stack.pop(0) + path.append(lineto_pt(*trafo.apply_pt(context.x, context.y + dy))) + context.y += dy + +T1vlineto = _T1vlineto() + + +class _T1vmoveto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 4) + + def __str__(self): + return "vmoveto" + + def updatepath(self, path, trafo, context): + dy = context.t1stack.pop(0) + path.append(moveto_pt(*trafo.apply_pt(context.x, context.y + dy))) + context.y += dy + +T1vmoveto = _T1vmoveto() + + +class _T1vhcurveto(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 30) + + def __str__(self): + return "vhcurveto" + + def updatepath(self, path, trafo, context): + dy1 = context.t1stack.pop(0) + dx2 = context.t1stack.pop(0) + dy2 = context.t1stack.pop(0) + dx3 = context.t1stack.pop(0) + path.append(curveto_pt(*(trafo.apply_pt(context.x, context.y + dy1) + + trafo.apply_pt(context.x + dx2, context.y + dy1 + dy2) + + trafo.apply_pt(context.x + dx2 + dx3, context.y + dy1 + dy2)))) + context.x += dx2+dx3 + context.y += dy1+dy2 + +T1vhcurveto = _T1vhcurveto() + + +# hint commands + +class _T1dotsection(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 0, subcmd=1) + + def __str__(self): + return "dotsection" + + def updatepath(self, path, trafo, context): + pass + +T1dotsection = _T1dotsection() + + +class _T1hstem(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 1) + + def __str__(self): + return "hstem" + + def updatepath(self, path, trafo, context): + y = context.t1stack.pop(0) + dy = context.t1stack.pop(0) + +T1hstem = _T1hstem() + + +class _T1hstem3(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 2, subcmd=1) + + def __str__(self): + return "hstem3" + + def updatepath(self, path, trafo, context): + y0 = context.t1stack.pop(0) + dy0 = context.t1stack.pop(0) + y1 = context.t1stack.pop(0) + dy1 = context.t1stack.pop(0) + y2 = context.t1stack.pop(0) + dy2 = context.t1stack.pop(0) + +T1hstem3 = _T1hstem3() + + +class _T1vstem(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 3) + + def __str__(self): + return "vstem" + + def updatepath(self, path, trafo, context): + x = context.t1stack.pop(0) + dx = context.t1stack.pop(0) + +T1vstem = _T1vstem() + + +class _T1vstem3(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 1, subcmd=1) + + def __str__(self): + return "vstem3" + + def updatepath(self, path, trafo, context): + self.x0 = context.t1stack.pop(0) + self.dx0 = context.t1stack.pop(0) + self.x1 = context.t1stack.pop(0) + self.dx1 = context.t1stack.pop(0) + self.x2 = context.t1stack.pop(0) + self.dx2 = context.t1stack.pop(0) + +T1vstem3 = _T1vstem3() + + +# arithmetic command + +class _T1div(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 12, subcmd=1) + + def __str__(self): + return "div" + + def updatepath(self, path, trafo, context): + num2 = context.t1stack.pop() + num1 = context.t1stack.pop() + context.t1stack.append(divmod(num1, num2)[0]) + + def gathercalls(self, seacglyphs, subrs, othersubrs, context): + num2 = context.t1stack.pop() + num1 = context.t1stack.pop() + context.t1stack.append(divmod(num1, num2)[0]) + +T1div = _T1div() + + +# subroutine commands + +class _T1callothersubr(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 16, subcmd=1) + + def __str__(self): + return "callothersubr" + + def updatepath(self, path, trafo, context): + othersubrnumber = context.t1stack.pop() + n = context.t1stack.pop() + for i in range(n): + context.psstack.append(context.t1stack.pop()) + + def gathercalls(self, seacglyphs, subrs, othersubrs, context): + othersubrnumber = context.t1stack.pop() + othersubrs[othersubrnumber] = 1 + n = context.t1stack.pop() + for i in range(n): + context.psstack.append(context.t1stack.pop()) + +T1callothersubr = _T1callothersubr() + + +class _T1callsubr(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 10) + + def __str__(self): + return "callsubr" + + def updatepath(self, path, trafo, context): + subr = context.t1stack.pop() + context.t1font.updatesubrpath(subr, path, trafo, context) + + def gathercalls(self, seacglyphs, subrs, othersubrs, context): + subr = context.t1stack.pop() + subrs[subr] = 1 + context.t1font.gathersubrcalls(subr, seacglyphs, subrs, othersubrs, context) + +T1callsubr = _T1callsubr() + + +class _T1pop(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 17, subcmd=1) + + def __str__(self): + return "pop" + + def updatepath(self, path, trafo, context): + context.t1stack.append(context.psstack.pop()) + + def gathercalls(self, seacglyphs, subrs, othersubrs, context): + context.t1stack.append(context.psstack.pop()) + +T1pop = _T1pop() + + +class _T1return(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 11) + + def __str__(self): + return "return" + + def updatepath(self, path, trafo, context): + pass + +T1return = _T1return() + + +class _T1setcurrentpoint(T1cmd): + + def __init__(self): + T1cmd.__init__(self, 33, subcmd=1) + + def __str__(self): + return "setcurrentpoint" % self.x, self.y + + def updatepath(self, path, trafo, context): + x = context.t1stack.pop(0) + y = context.t1stack.pop(0) + path.append(moveto_pt(*trafo.apply_pt(x, y))) + context.x = x + context.y = y + +T1setcurrentpoint = _T1setcurrentpoint() + + +###################################################################### + +class cursor: + """cursor to read a string token by token""" + + def __init__(self, data, startstring, eattokensep=1, tokenseps=" \t\r\n", tokenstarts="()<>[]{}/%"): + """creates a cursor for the string data + + startstring is a string at which the cursor should start at. The first + ocurance of startstring is used. When startstring is not in data, an + exception is raised, otherwise the cursor is set to the position right + after the startstring. When eattokenseps is set, startstring must be + followed by a tokensep and this first tokensep is also consumed. + tokenseps is a string containing characters to be used as token + separators. tokenstarts is a string containing characters which + directly (even without intermediate token separator) start a new token. + """ + self.data = data + self.pos = self.data.index(startstring) + len(startstring) + self.tokenseps = tokenseps + self.tokenstarts = tokenstarts + if eattokensep: + if self.data[self.pos] not in self.tokenstarts: + if self.data[self.pos] not in self.tokenseps: + raise ValueError("cursor initialization string is not followed by a token separator") + self.pos += 1 + + def gettoken(self): + """get the next token + + Leading token separators and comments are silently consumed. The first token + separator after the token is also silently consumed.""" + while self.data[self.pos] in self.tokenseps: + self.pos += 1 + # ignore comments including subsequent whitespace characters + while self.data[self.pos] == "%": + while self.data[self.pos] not in "\r\n": + self.pos += 1 + while self.data[self.pos] in self.tokenseps: + self.pos += 1 + startpos = self.pos + while self.data[self.pos] not in self.tokenseps: + # any character in self.tokenstarts ends the token + if self.pos>startpos and self.data[self.pos] in self.tokenstarts: + break + self.pos += 1 + result = self.data[startpos:self.pos] + if self.data[self.pos] in self.tokenseps: + self.pos += 1 # consume a single tokensep + return result + + def getint(self): + """get the next token as an integer""" + return int(self.gettoken()) + + def getbytes(self, count): + """get the next count bytes""" + startpos = self.pos + self.pos += count + return self.data[startpos: self.pos] + + +class T1font: + + eexecr = 55665 + charstringr = 4330 + + def __init__(self, data1, data2eexec, data3): + """initializes a t1font instance + + data1 and data3 are the two clear text data parts and data2 is + the binary data part""" + self.data1 = data1 + self._data2eexec = data2eexec + self.data3 = data3 + + # marker and value for decoded data + self._data2 = None + # note that data2eexec is set to none by setsubrcmds and setglyphcmds + # this *also* denotes, that data2 is out-of-date; hence they are both + # marked by an _ and getdata2 and getdata2eexec will properly resolve + # the current state of decoding ... + + # marker and value for standard encoding check + self.encoding = None + + def _eexecdecode(self, code): + """eexec decoding of code""" + return decoder(code, self.eexecr, 4) + + def _charstringdecode(self, code): + """charstring decoding of code""" + return decoder(code, self.charstringr, self.lenIV) + + def _eexecencode(self, data): + """eexec encoding of data""" + return encoder(data, self.eexecr, "PyX!") + + def _charstringencode(self, data): + """eexec encoding of data""" + return encoder(data, self.charstringr, "PyX!"[:self.lenIV]) + + lenIVpattern = re.compile("/lenIV\s+(\d+)\s+def\s+") + flexhintsubrs = [[3, 0, T1callothersubr, T1pop, T1pop, T1setcurrentpoint, T1return], + [0, 1, T1callothersubr, T1return], + [0, 2, T1callothersubr, T1return], + [T1return]] + + def _encoding(self): + """helper method to lookup the encoding in the font""" + c = cursor(self.data1, "/Encoding") + token1 = c.gettoken() + token2 = c.gettoken() + if token1 == "StandardEncoding" and token2 == "def": + self.encoding = encoding.adobestandardencoding + else: + encvector = [None]*256 + while 1: + self.encodingstart = c.pos + if c.gettoken() == "dup": + break + while 1: + i = c.getint() + glyph = c.gettoken() + if 0 <= i < 256: + encvector[i] = glyph[1:] + token = c.gettoken(); assert token == "put" + self.encodingend = c.pos + token = c.gettoken() + if token == "readonly" or token == "def": + break + assert token == "dup" + self.encoding = encoding.encoding(encvector) + + def _data2decode(self): + """decodes data2eexec to the data2 string and the subr and glyphs dictionary + + It doesn't make sense to call this method twice -- check the content of + data2 before calling. The method also keeps the subrs and charstrings + start and end positions for later use.""" + self._data2 = self._eexecdecode(self._data2eexec) + + m = self.lenIVpattern.search(self._data2) + if m: + self.lenIV = int(m.group(1)) + else: + self.lenIV = 4 + self.emptysubr = self._charstringencode(chr(11)) + + # extract Subrs + c = cursor(self._data2, "/Subrs") + self.subrsstart = c.pos + arraycount = c.getint() + token = c.gettoken(); assert token == "array" + self.subrs = [] + for i in range(arraycount): + token = c.gettoken(); assert token == "dup" + token = c.getint(); assert token == i + size = c.getint() + if not i: + self.subrrdtoken = c.gettoken() + else: + token = c.gettoken(); assert token == self.subrrdtoken + self.subrs.append(c.getbytes(size)) + token = c.gettoken() + if token == "noaccess": + token = "%s %s" % (token, c.gettoken()) + if not i: + self.subrnptoken = token + else: + assert token == self.subrnptoken + self.subrsend = c.pos + + # hasflexhintsubrs is a boolean indicating that the font uses flex or + # hint replacement subrs as specified by Adobe (tm). When it does, the + # first 4 subrs should all be copied except when none of them are used + # in the stripped version of the font since we than get a font not + # using flex or hint replacement subrs at all. + self.hasflexhintsubrs = (arraycount >= len(self.flexhintsubrs) and + [self.getsubrcmds(i) + for i in range(len(self.flexhintsubrs))] == self.flexhintsubrs) + + # extract glyphs + self.glyphs = {} + self.glyphlist = [] # we want to keep the order of the glyph names + c = cursor(self._data2, "/CharStrings") + self.charstringsstart = c.pos + c.getint() + token = c.gettoken(); assert token == "dict" + token = c.gettoken(); assert token == "dup" + token = c.gettoken(); assert token == "begin" + first = 1 + while 1: + chartoken = c.gettoken() + if chartoken == "end": + break + assert chartoken[0] == "/" + size = c.getint() + if first: + self.glyphrdtoken = c.gettoken() + else: + token = c.gettoken(); assert token == self.glyphrdtoken + self.glyphlist.append(chartoken[1:]) + self.glyphs[chartoken[1:]] = c.getbytes(size) + if first: + self.glyphndtoken = c.gettoken() + else: + token = c.gettoken(); assert token == self.glyphndtoken + first = 0 + self.charstringsend = c.pos + assert not self.subrs or self.subrrdtoken == self.glyphrdtoken + + def _cmds(self, code): + """return a list of T1cmd's for encoded charstring data in code""" + code = array.array("B", self._charstringdecode(code)) + cmds = [] + while code: + x = code.pop(0) + if x == 12: # this starts an escaped cmd + cmds.append(T1subcmds[code.pop(0)]) + elif 0 <= x < 32: # those are cmd's + cmds.append(T1cmds[x]) + elif 32 <= x <= 246: # short ints + cmds.append(x-139) + elif 247 <= x <= 250: # mid size ints + cmds.append(((x - 247)*256) + code.pop(0) + 108) + elif 251 <= x <= 254: # mid size ints + cmds.append(-((x - 251)*256) - code.pop(0) - 108) + else: # x = 255, i.e. full size ints + y = ((code.pop(0)*256l+code.pop(0))*256+code.pop(0))*256+code.pop(0) + if y > (1l << 31): + cmds.append(y - (1l << 32)) + else: + cmds.append(y) + return cmds + + def _code(self, cmds): + """return an encoded charstring data for list of T1cmd's in cmds""" + code = array.array("B") + for cmd in cmds: + try: + if cmd.subcmd: + code.append(12) + code.append(cmd.code) + except AttributeError: + if -107 <= cmd <= 107: + code.append(cmd+139) + elif 108 <= cmd <= 1131: + a, b = divmod(cmd-108, 256) + code.append(a+247) + code.append(b) + elif -1131 <= cmd <= -108: + a, b = divmod(-cmd-108, 256) + code.append(a+251) + code.append(b) + else: + if cmd < 0: + cmd += 1l << 32 + cmd, x4 = divmod(cmd, 256) + cmd, x3 = divmod(cmd, 256) + x1, x2 = divmod(cmd, 256) + code.append(255) + code.append(x1) + code.append(x2) + code.append(x3) + code.append(x4) + return self._charstringencode(code.tostring()) + + def getsubrcmds(self, subr): + """return a list of T1cmd's for subr subr""" + if not self._data2: + self._data2decode() + return self._cmds(self.subrs[subr]) + + def getglyphcmds(self, glyph): + """return a list of T1cmd's for glyph glyph""" + if not self._data2: + self._data2decode() + return self._cmds(self.glyphs[glyph]) + + def setsubrcmds(self, subr, cmds): + """replaces the T1cmd's by the list cmds for subr subr""" + if not self._data2: + self._data2decode() + self._data2eexec = None + self.subrs[subr] = self._code(cmds) + + def setglyphcmds(self, glyph, cmds): + """replaces the T1cmd's by the list cmds for glyph glyph""" + if not self._data2: + self._data2decode() + self._data2eexec = None + self.glyphs[glyph] = self._code(cmds) + + def updatepath(self, cmds, path, trafo, context): + for cmd in cmds: + if isinstance(cmd, T1cmd): + cmd.updatepath(path, trafo, context) + else: + context.t1stack.append(cmd) + + def updatesubrpath(self, subr, path, trafo, context): + self.updatepath(self.getsubrcmds(subr), path, trafo, context) + + def updateglyphpath(self, glyph, path, trafo, context): + self.updatepath(self.getglyphcmds(glyph), path, trafo, context) + + def gathercalls(self, cmds, seacglyphs, subrs, othersubrs, context): + for cmd in cmds: + if isinstance(cmd, T1cmd): + cmd.gathercalls(seacglyphs, subrs, othersubrs, context) + else: + context.t1stack.append(cmd) + + def gathersubrcalls(self, subr, seacglyphs, subrs, othersubrs, context): + self.gathercalls(self.getsubrcmds(subr), seacglyphs, subrs, othersubrs, context) + + def gatherglyphcalls(self, glyph, seacglyphs, subrs, othersubrs, context): + self.gathercalls(self.getglyphcmds(glyph), seacglyphs, subrs, othersubrs, context) + + fontmatrixpattern = re.compile("/FontMatrix\s*\[\s*(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s+(-?[0-9.]+)\s*\]\s*(readonly\s+)?def") + + def getglyphpathwxwy_pt(self, glyph, size): + m = self.fontmatrixpattern.search(self.data1) + m11, m12, m21, m22, v1, v2 = map(float, m.groups()[:6]) + t = trafo.trafo_pt(matrix=((m11, m12), (m21, m22)), vector=(v1, v2)).scaled(size) + context = T1context(self) + p = path() + self.updateglyphpath(glyph, p, t, context) + wx, wy = t.apply_pt(context.wx, context.wy) + return p, wx, wy + + def getglyphpath(self, glyph, size): + """return a PyX path for glyph named glyph""" + return self.getglyphpathwxwy_pt(glyph, size)[0] + + def getglyphwxwy_pt(self, glyph, size): + return self.getglyphpathwxwy_pt(glyph, size)[1:] + + def getdata2(self, subrs=None, glyphs=None): + """makes a data2 string + + subrs is a dict containing those subrs numbers as keys, + which are to be contained in the subrsstring to be created. + If subrs is None, all subrs in self.subrs will be used. + The subrs dict might be modified *in place*. + + glyphs is a dict containing those glyph names as keys, + which are to be contained in the charstringsstring to be created. + If glyphs is None, all glyphs in self.glyphs will be used.""" + def addsubrs(subrs, result): + if subrs is not None: + # some adjustments to the subrs dict + if subrs: + subrsindices = subrs.keys() + subrsmin = min(subrsindices) + subrsmax = max(subrsindices) + if self.hasflexhintsubrs and subrsmin < len(self.flexhintsubrs): + # According to the spec we need to keep all the flex and hint subrs + # as long as any of it is used. + for subr in range(len(self.flexhintsubrs)): + subrs[subr] = 1 + else: + subrsmax = -1 + else: + # build a new subrs dict containing all subrs + subrs = dict([(subr, 1) for subr in range(len(self.subrs))]) + subrsmax = len(self.subrs) - 1 + + # build the string from all selected subrs + result.append("%d array\n" % (subrsmax + 1)) + for subr in range(subrsmax+1): + if subrs.has_key(subr): + code = self.subrs[subr] + else: + code = self.emptysubr + result.append("dup %d %d %s %s %s\n" % (subr, len(code), self.subrrdtoken, code, self.subrnptoken)) + + def addcharstrings(glyphs, result): + result.append("%d dict dup begin\n" % (glyphs is None and len(self.glyphlist) or len(glyphs))) + for glyph in self.glyphlist: + if glyphs is None or glyphs.has_key(glyph): + result.append("/%s %d %s %s %s\n" % (glyph, len(self.glyphs[glyph]), self.glyphrdtoken, self.glyphs[glyph], self.glyphndtoken)) + result.append("end\n") + + if self.subrsstart < self.charstringsstart: + result = [self._data2[:self.subrsstart]] + addsubrs(subrs, result) + result.append(self._data2[self.subrsend:self.charstringsstart]) + addcharstrings(glyphs, result) + result.append(self._data2[self.charstringsend:]) + else: + result = [self._data2[:self.charstringsstart]] + addcharstrings(glyphs, result) + result.append(self._data2[self.charstringsend:self.subrsstart]) + addsubrs(subrs, result) + result.append(self._data2[self.subrsend:]) + return "".join(result) + + def getdata2eexec(self): + if self._data2eexec: + return self._data2eexec + # note that self._data2 is out-of-date here too, hence we need to call getdata2 + return self._eexecencode(self.getdata2()) + + newlinepattern = re.compile("\s*[\r\n]\s*") + uniqueidpattern = re.compile("/UniqueID\s+\d+\s+def\s+") + + def getstrippedfont(self, glyphs): + """create a T1font instance containing only certain glyphs + + glyphs is a dict having the glyph names to be contained as keys. + The glyphs dict might be modified *in place*. + """ + # TODO: we could also strip othersubrs to those actually used + + # collect information about used glyphs and subrs + seacglyphs = {} + subrs = {} + othersubrs = {} + for glyph in glyphs.keys(): + self.gatherglyphcalls(glyph, seacglyphs, subrs, othersubrs, T1context(self)) + # while we have gathered all subrs for the seacglyphs alreadys, we + # might have missed the glyphs themself (when they are not used stand-alone) + glyphs.update(seacglyphs) + glyphs[".notdef"] = 1 + + # strip data1 + if not self.encoding: + self._encoding() + if self.encoding is encoding.adobestandardencoding: + data1 = self.data1 + else: + encodingstrings = [] + for char, glyph in enumerate(self.encoding.encvector): + if glyph in glyphs.keys(): + encodingstrings.append("dup %i /%s put\n" % (char, glyph)) + data1 = self.data1[:self.encodingstart] + "".join(encodingstrings) + self.data1[self.encodingend:] + data1 = self.newlinepattern.subn("\n", data1)[0] + data1 = self.uniqueidpattern.subn("", data1)[0] + + # strip data2 + data2 = self.uniqueidpattern.subn("", self.getdata2(subrs, glyphs))[0] + + # strip data3 + data3 = self.newlinepattern.subn("\n", self.data3)[0] + + # create and return the new font instance + return T1font(data1.rstrip() + "\n", self._eexecencode(data2), data3.rstrip() + "\n") + + def getflags(self): + # As a simple heuristics we assume non-symbolic fonts if and only + # if the Adobe standard encoding is used. All other font flags are + # not specified here. + if not self.encoding: + self._encoding() + if self.encoding is encoding.adobestandardencoding: + return 32 + return 4 + + def outputPFA(self, file): + """output the T1font in PFA format""" + file.write(self.data1) + data2eexechex = binascii.b2a_hex(self.getdata2eexec()) + linelength = 64 + for i in range((len(data2eexechex)-1)/linelength + 1): + file.write(data2eexechex[i*linelength: i*linelength+linelength]) + file.write("\n") + file.write(self.data3) + + def outputPFB(self, file): + """output the T1font in PFB format""" + data2eexec = self.getdata2eexec() + def pfblength(data): + l = len(data) + l, x1 = divmod(l, 256) + l, x2 = divmod(l, 256) + x4, x3 = divmod(l, 256) + return chr(x1) + chr(x2) + chr(x3) + chr(x4) + file.write("\200\1") + file.write(pfblength(self.data1)) + file.write(self.data1) + file.write("\200\2") + file.write(pfblength(data2eexec)) + file.write(data2eexec) + file.write("\200\1") + file.write(pfblength(self.data3)) + file.write(self.data3) + file.write("\200\3") + + def outputPS(self, file, writer): + """output the PostScript code for the T1font to the file file""" + self.outputPFA(file) + + def outputPDF(self, file, writer): + data2eexec = self.getdata2eexec() + data3 = self.data3 + # we might be allowed to skip the third part ... + if (data3.replace("\n", "") + .replace("\r", "") + .replace("\t", "") + .replace(" ", "")) == "0"*512 + "cleartomark": + data3 = "" + + data = self.data1 + data2eexec + data3 + if writer.compress and haszlib: + data = zlib.compress(data) + + file.write("<<\n" + "/Length %d\n" + "/Length1 %d\n" + "/Length2 %d\n" + "/Length3 %d\n" % (len(data), len(self.data1), len(data2eexec), len(data3))) + if writer.compress and haszlib: + file.write("/Filter /FlateDecode\n") + file.write(">>\n" + "stream\n") + file.write(data) + file.write("\n" + "endstream\n") + + +class T1pfafont(T1font): + + """create a T1font instance from a pfa font file""" + + def __init__(self, filename): + d = open(filename, "rb").read() + # hey, that's quick'n'dirty + m1 = d.index("eexec") + 6 + m2 = d.index("0"*40) + data1 = d[:m1] + data2 = binascii.a2b_hex(d[m1: m2].replace(" ", "").replace("\r", "").replace("\n", "")) + data3 = d[m2:] + T1font.__init__(self, data1, data2, data3) + + +class T1pfbfont(T1font): + + """create a T1font instance from a pfb font file""" + + def __init__(self, filename): + def pfblength(s): + if len(s) != 4: + raise ValueError("invalid string length") + return (ord(s[0]) + + ord(s[1])*256 + + ord(s[2])*256*256 + + ord(s[3])*256*256*256) + f = open(filename, "rb") + mark = f.read(2); assert mark == "\200\1" + data1 = f.read(pfblength(f.read(4))) + mark = f.read(2); assert mark == "\200\2" + data2 = "" + while mark == "\200\2": + data2 = data2 + f.read(pfblength(f.read(4))) + mark = f.read(2) + assert mark == "\200\1" + data3 = f.read(pfblength(f.read(4))) + mark = f.read(2); assert mark == "\200\3" + assert not f.read(1) + T1font.__init__(self, data1, data2, data3) + diff --git a/compiler/gdsMill/pyx/graph/__init__.py b/compiler/gdsMill/pyx/graph/__init__.py new file mode 100644 index 00000000..5e2c65ce --- /dev/null +++ b/compiler/gdsMill/pyx/graph/__init__.py @@ -0,0 +1,32 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2004-2005 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +__allmodules__ = ["data", "key", "style", "axis"] +for module in __allmodules__: + __import__(module, globals(), locals(), []) + +import graph +__allgraph__ = ["graphxy", "graphxyz"] +for importfromgraph in __allgraph__: + locals()[importfromgraph] = getattr(graph, importfromgraph) + +__all__ = __allmodules__ + __allgraph__ diff --git a/compiler/gdsMill/pyx/graph/axis/__init__.py b/compiler/gdsMill/pyx/graph/axis/__init__.py new file mode 100644 index 00000000..930a277d --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/__init__.py @@ -0,0 +1,36 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2004-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +__allmodules__ = ["painter", "parter", "rater", "texter", "tick"] +for module in __allmodules__: + __import__(module, globals(), locals(), []) + +import axis +__allaxis__ = ["linear", "lin", "logarithmic", "log", + "bar", "nestedbar", "split", + "sizedlinear", "sizedlin", "autosizedlinear", "autosizedlin", + "anchoredaxis", "linkedaxis", + "anchoredpathaxis", "pathaxis"] +for importfromaxis in __allaxis__: + locals()[importfromaxis] = getattr(axis, importfromaxis) + +__all__ = __allmodules__ + __allaxis__ diff --git a/compiler/gdsMill/pyx/graph/axis/axis.py b/compiler/gdsMill/pyx/graph/axis/axis.py new file mode 100644 index 00000000..867600fc --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/axis.py @@ -0,0 +1,610 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import nested_scopes + +import math, warnings +from pyx import attr, unit, text +from pyx.graph.axis import painter, parter, positioner, rater, texter, tick + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + +class _marker: pass + + +class axisdata: + """axis data storage class + + Instances of this class are used to store axis data local to the + graph. It will always contain an axispos instance provided by the + graph during initialization.""" + + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + +class _axis: + """axis""" + + def createlinked(self, data, positioner, graphtexrunner, errorname, linkpainter): + canvas = painter.axiscanvas(self.painter, graphtexrunner) + if linkpainter is not None: + linkpainter.paint(canvas, data, self, positioner) + return canvas + + +class NoValidPartitionError(RuntimeError): + + pass + + +class _regularaxis(_axis): + """base implementation a regular axis + + Regular axis have a continuous variable like linear axes, + logarithmic axes, time axes etc.""" + + def __init__(self, min=None, max=None, reverse=0, divisor=None, title=None, + painter=painter.regular(), texter=texter.mixed(), linkpainter=painter.linked(), + density=1, maxworse=2, manualticks=[]): + if min is not None and max is not None and min > max: + min, max, reverse = max, min, not reverse + self.min = min + self.max = max + self.reverse = reverse + self.divisor = divisor + self.title = title + self.painter = painter + self.texter = texter + self.linkpainter = linkpainter + self.density = density + self.maxworse = maxworse + self.manualticks = self.checkfraclist(manualticks) + + def createdata(self, errorname): + return axisdata(min=self.min, max=self.max) + + zero = 0.0 + + def adjustaxis(self, data, columndata, graphtexrunner, errorname): + if self.min is None or self.max is None: + for value in columndata: + try: + value = value + self.zero + except: + pass + else: + if self.min is None and (data.min is None or value < data.min): + data.min = value + if self.max is None and (data.max is None or value > data.max): + data.max = value + + def checkfraclist(self, fracs): + "orders a list of fracs, equal entries are not allowed" + if not len(fracs): return [] + sorted = list(fracs) + sorted.sort() + last = sorted[0] + for item in sorted[1:]: + if last == item: + raise ValueError("duplicate entry found") + last = item + return sorted + + def _create(self, data, positioner, graphtexrunner, parter, rater, errorname): + errorname = " for axis %s" % errorname + if data.min is None or data.max is None: + raise RuntimeError("incomplete axis range%s" % errorname) + + def layout(data): + self.adjustaxis(data, data.ticks, graphtexrunner, errorname) + self.texter.labels(data.ticks) + if self.divisor: + for t in data.ticks: + t *= tick.rational(self.divisor) + canvas = painter.axiscanvas(self.painter, graphtexrunner) + if self.painter is not None: + self.painter.paint(canvas, data, self, positioner) + return canvas + + if parter is None: + data.ticks = self.manualticks + return layout(data) + + # a variant is a data copy with local modifications to test several partitions + class variant: + def __init__(self, data, **kwargs): + self.data = data + for key, value in kwargs.items(): + setattr(self, key, value) + + def __getattr__(self, key): + return getattr(data, key) + + def __cmp__(self, other): + # we can also sort variants by their rate + return cmp(self.rate, other.rate) + + # build a list of variants + bestrate = None + if self.divisor is not None: + partfunctions = parter.partfunctions(data.min/self.divisor, data.max/self.divisor, + self.min is None, self.max is None) + else: + partfunctions = parter.partfunctions(data.min, data.max, + self.min is None, self.max is None) + variants = [] + for partfunction in partfunctions: + worse = 0 + while worse < self.maxworse: + worse += 1 + ticks = partfunction() + if ticks is None: + break + ticks = tick.mergeticklists(self.manualticks, ticks, mergeequal=0) + if ticks: + rate = rater.rateticks(self, ticks, self.density) + if self.reverse: + rate += rater.raterange(self.convert(data, ticks[0]) - + self.convert(data, ticks[-1]), 1) + else: + rate += rater.raterange(self.convert(data, ticks[-1]) - + self.convert(data, ticks[0]), 1) + if bestrate is None or rate < bestrate: + bestrate = rate + worse = 0 + variants.append(variant(data, rate=rate, ticks=ticks)) + + if not variants: + raise RuntimeError("no axis partitioning found%s" % errorname) + + if len(variants) == 1 or self.painter is None: + # When the painter is None, we could sort the variants here by their rating. + # However, we didn't did this so far and there is no real reason to change that. + data.ticks = variants[0].ticks + return layout(data) + + # build the layout for best variants + for variant in variants: + variant.storedcanvas = None + variants.sort() + while not variants[0].storedcanvas: + variants[0].storedcanvas = layout(variants[0]) + ratelayout = rater.ratelayout(variants[0].storedcanvas, self.density) + if ratelayout is None: + del variants[0] + if not variants: + raise NoValidPartitionError("no valid axis partitioning found%s" % errorname) + else: + variants[0].rate += ratelayout + variants.sort() + self.adjustaxis(data, variants[0].ticks, graphtexrunner, errorname) + data.ticks = variants[0].ticks + return variants[0].storedcanvas + + +class linear(_regularaxis): + """linear axis""" + + def __init__(self, parter=parter.autolinear(), rater=rater.linear(), **args): + _regularaxis.__init__(self, **args) + self.parter = parter + self.rater = rater + + def convert(self, data, value): + """axis coordinates -> graph coordinates""" + if self.reverse: + return (data.max - float(value)) / (data.max - data.min) + else: + return (float(value) - data.min) / (data.max - data.min) + + def create(self, data, positioner, graphtexrunner, errorname): + return _regularaxis._create(self, data, positioner, graphtexrunner, self.parter, self.rater, errorname) + +lin = linear + + +class logarithmic(_regularaxis): + """logarithmic axis""" + + def __init__(self, parter=parter.autologarithmic(), rater=rater.logarithmic(), + linearparter=parter.autolinear(extendtick=None), **args): + _regularaxis.__init__(self, **args) + self.parter = parter + self.rater = rater + self.linearparter = linearparter + + def convert(self, data, value): + """axis coordinates -> graph coordinates""" + # TODO: store log(data.min) and log(data.max) + if self.reverse: + return (math.log(data.max) - math.log(float(value))) / (math.log(data.max) - math.log(data.min)) + else: + return (math.log(float(value)) - math.log(data.min)) / (math.log(data.max) - math.log(data.min)) + + def create(self, data, positioner, graphtexrunner, errorname): + try: + return _regularaxis._create(self, data, positioner, graphtexrunner, self.parter, self.rater, errorname) + except NoValidPartitionError: + if self.linearparter: + warnings.warn("no valid logarithmic partitioning found for axis %s, switch to linear partitioning" % errorname) + return _regularaxis._create(self, data, positioner, graphtexrunner, self.linearparter, self.rater, errorname) + raise + +log = logarithmic + + +class subaxispositioner(positioner._positioner): + """a subaxis positioner""" + + def __init__(self, basepositioner, subaxis): + self.basepositioner = basepositioner + self.vmin = subaxis.vmin + self.vmax = subaxis.vmax + self.vminover = subaxis.vminover + self.vmaxover = subaxis.vmaxover + + def vbasepath(self, v1=None, v2=None): + if v1 is not None: + v1 = self.vmin+v1*(self.vmax-self.vmin) + else: + v1 = self.vminover + if v2 is not None: + v2 = self.vmin+v2*(self.vmax-self.vmin) + else: + v2 = self.vmaxover + return self.basepositioner.vbasepath(v1, v2) + + def vgridpath(self, v): + return self.basepositioner.vgridpath(self.vmin+v*(self.vmax-self.vmin)) + + def vtickpoint_pt(self, v, axis=None): + return self.basepositioner.vtickpoint_pt(self.vmin+v*(self.vmax-self.vmin)) + + def vtickdirection(self, v, axis=None): + return self.basepositioner.vtickdirection(self.vmin+v*(self.vmax-self.vmin)) + + +class bar(_axis): + + def __init__(self, subaxes=None, defaultsubaxis=linear(painter=None, linkpainter=None, parter=None), + dist=0.5, firstdist=None, lastdist=None, title=None, reverse=0, + painter=painter.bar(), linkpainter=painter.linkedbar()): + self.subaxes = subaxes + self.defaultsubaxis = defaultsubaxis + self.dist = dist + if firstdist is not None: + self.firstdist = firstdist + else: + self.firstdist = 0.5 * dist + if lastdist is not None: + self.lastdist = lastdist + else: + self.lastdist = 0.5 * dist + self.title = title + self.reverse = reverse + self.painter = painter + self.linkpainter = linkpainter + + def createdata(self, errorname): + data = axisdata(size=self.firstdist+self.lastdist-self.dist, subaxes={}, names=[]) + return data + + def addsubaxis(self, data, name, subaxis, graphtexrunner, errorname): + subaxis = anchoredaxis(subaxis, graphtexrunner, "%s, subaxis %s" % (errorname, name)) + subaxis.setcreatecall(lambda: None) + subaxis.sized = hasattr(subaxis.data, "size") + if subaxis.sized: + data.size += subaxis.data.size + else: + data.size += 1 + data.size += self.dist + data.subaxes[name] = subaxis + if self.reverse: + data.names.insert(0, name) + else: + data.names.append(name) + + def adjustaxis(self, data, columndata, graphtexrunner, errorname): + for value in columndata: + + # some checks and error messages + try: + len(value) + except: + raise ValueError("tuple expected by bar axis '%s'" % errorname) + try: + value + "" + except: + pass + else: + raise ValueError("tuple expected by bar axis '%s'" % errorname) + assert len(value) == 2, "tuple of size two expected by bar axis '%s'" % errorname + + name = value[0] + if name is not None and name not in data.names: + if self.subaxes: + if self.subaxes[name] is not None: + self.addsubaxis(data, name, self.subaxes[name], graphtexrunner, errorname) + else: + self.addsubaxis(data, name, self.defaultsubaxis, graphtexrunner, errorname) + for name in data.names: + subaxis = data.subaxes[name] + if subaxis.sized: + data.size -= subaxis.data.size + subaxis.axis.adjustaxis(subaxis.data, + [value[1] for value in columndata if value[0] == name], + graphtexrunner, + "%s, subaxis %s" % (errorname, name)) + if subaxis.sized: + data.size += subaxis.data.size + + def convert(self, data, value): + if value[0] is None: + return None + axis = data.subaxes[value[0]] + vmin = axis.vmin + vmax = axis.vmax + return axis.vmin + axis.convert(value[1]) * (axis.vmax - axis.vmin) + + def create(self, data, positioner, graphtexrunner, errorname): + canvas = painter.axiscanvas(self.painter, graphtexrunner) + v = 0 + position = self.firstdist + for name in data.names: + subaxis = data.subaxes[name] + subaxis.vmin = position / float(data.size) + if subaxis.sized: + position += subaxis.data.size + else: + position += 1 + subaxis.vmax = position / float(data.size) + position += 0.5*self.dist + subaxis.vminover = v + if name == data.names[-1]: + subaxis.vmaxover = 1 + else: + subaxis.vmaxover = position / float(data.size) + subaxis.setpositioner(subaxispositioner(positioner, subaxis)) + subaxis.create() + canvas.insert(subaxis.canvas) + if canvas.extent_pt < subaxis.canvas.extent_pt: + canvas.extent_pt = subaxis.canvas.extent_pt + position += 0.5*self.dist + v = subaxis.vmaxover + if self.painter is not None: + self.painter.paint(canvas, data, self, positioner) + return canvas + + def createlinked(self, data, positioner, graphtexrunner, errorname, linkpainter): + canvas = painter.axiscanvas(self.painter, graphtexrunner) + for name in data.names: + subaxis = data.subaxes[name] + subaxis = linkedaxis(subaxis, name) + subaxis.setpositioner(subaxispositioner(positioner, data.subaxes[name])) + subaxis.create() + canvas.insert(subaxis.canvas) + if canvas.extent_pt < subaxis.canvas.extent_pt: + canvas.extent_pt = subaxis.canvas.extent_pt + if linkpainter is not None: + linkpainter.paint(canvas, data, self, positioner) + return canvas + + +class nestedbar(bar): + + def __init__(self, defaultsubaxis=bar(dist=0, painter=None, linkpainter=None), **kwargs): + bar.__init__(self, defaultsubaxis=defaultsubaxis, **kwargs) + + +class split(bar): + + def __init__(self, defaultsubaxis=linear(), + firstdist=0, lastdist=0, + painter=painter.split(), linkpainter=painter.linkedsplit(), **kwargs): + bar.__init__(self, defaultsubaxis=defaultsubaxis, + firstdist=firstdist, lastdist=lastdist, + painter=painter, linkpainter=linkpainter, **kwargs) + + +class sizedlinear(linear): + + def __init__(self, size=1, **kwargs): + linear.__init__(self, **kwargs) + self.size = size + + def createdata(self, errorname): + data = linear.createdata(self, errorname) + data.size = self.size + return data + +sizedlin = sizedlinear + + +class autosizedlinear(linear): + + def __init__(self, parter=parter.autolinear(extendtick=None), **kwargs): + linear.__init__(self, parter=parter, **kwargs) + + def createdata(self, errorname): + data = linear.createdata(self, errorname) + try: + data.size = data.max - data.min + except: + data.size = 0 + return data + + def adjustaxis(self, data, columndata, graphtexrunner, errorname): + linear.adjustaxis(self, data, columndata, graphtexrunner, errorname) + try: + data.size = data.max - data.min + except: + data.size = 0 + + def create(self, data, positioner, graphtexrunner, errorname): + min = data.min + max = data.max + canvas = linear.create(self, data, positioner, graphtexrunner, errorname) + if min != data.min or max != data.max: + raise RuntimeError("range change during axis creation of autosized linear axis") + return canvas + +autosizedlin = autosizedlinear + + +class anchoredaxis: + + def __init__(self, axis, graphtexrunner, errorname): + assert not isinstance(axis, anchoredaxis), errorname + self.axis = axis + self.errorname = errorname + self.graphtexrunner = graphtexrunner + self.data = axis.createdata(errorname) + self.canvas = None + self.positioner = None + + def setcreatecall(self, function, *args, **kwargs): + self._createfunction = function + self._createargs = args + self._createkwargs = kwargs + + def docreate(self): + if not self.canvas: + self._createfunction(*self._createargs, **self._createkwargs) + + def setpositioner(self, positioner): + assert positioner is not None, self.errorname + assert self.positioner is None, self.errorname + self.positioner = positioner + + def convert(self, x): + self.docreate() + return self.axis.convert(self.data, x) + + def adjustaxis(self, columndata): + if self.canvas is None: + self.axis.adjustaxis(self.data, columndata, self.graphtexrunner, self.errorname) + else: + warnings.warn("ignore axis range adjustment of already created axis '%s'" % self.errorname) + + def vbasepath(self, v1=None, v2=None): + return self.positioner.vbasepath(v1=v1, v2=v2) + + def basepath(self, x1=None, x2=None): + self.docreate() + if x1 is None: + if x2 is None: + return self.positioner.vbasepath() + else: + return self.positioner.vbasepath(v2=self.axis.convert(self.data, x2)) + else: + if x2 is None: + return self.positioner.vbasepath(v1=self.axis.convert(self.data, x1)) + else: + return self.positioner.vbasepath(v1=self.axis.convert(self.data, x1), + v2=self.axis.convert(self.data, x2)) + + def vgridpath(self, v): + return self.positioner.vgridpath(v) + + def gridpath(self, x): + self.docreate() + return self.positioner.vgridpath(self.axis.convert(self.data, x)) + + def vtickpoint_pt(self, v): + return self.positioner.vtickpoint_pt(v) + + def vtickpoint(self, v): + return self.positioner.vtickpoint_pt(v) * unit.t_pt + + def tickpoint_pt(self, x): + self.docreate() + return self.positioner.vtickpoint_pt(self.axis.convert(self.data, x)) + + def tickpoint(self, x): + self.docreate() + x_pt, y_pt = self.positioner.vtickpoint_pt(self.axis.convert(self.data, x)) + return x_pt * unit.t_pt, y_pt * unit.t_pt + + def vtickdirection(self, v): + return self.positioner.vtickdirection(v) + + def tickdirection(self, x): + self.docreate() + return self.positioner.vtickdirection(self.axis.convert(self.data, x)) + + def create(self): + if self.canvas is None: + assert self.positioner is not None, self.errorname + self.canvas = self.axis.create(self.data, self.positioner, self.graphtexrunner, self.errorname) + return self.canvas + + +class linkedaxis(anchoredaxis): + + def __init__(self, linkedaxis=None, errorname="manual-linked", painter=_marker): + self.painter = painter + self.linkedto = None + self.errorname = errorname + self.canvas = None + self.positioner = None + if linkedaxis: + self.setlinkedaxis(linkedaxis) + + def setlinkedaxis(self, linkedaxis): + assert isinstance(linkedaxis, anchoredaxis), errorname + self.linkedto = linkedaxis + self.axis = linkedaxis.axis + self.graphtexrunner = self.linkedto.graphtexrunner + self.errorname = "%s (linked to %s)" % (self.errorname, linkedaxis.errorname) + self.data = linkedaxis.data + if self.painter is _marker: + self.painter = linkedaxis.axis.linkpainter + + def create(self): + assert self.linkedto is not None, self.errorname + assert self.positioner is not None, self.errorname + if self.canvas is None: + self.linkedto.docreate() + self.canvas = self.axis.createlinked(self.data, self.positioner, self.graphtexrunner, self.errorname, self.painter) + return self.canvas + + +class anchoredpathaxis(anchoredaxis): + """an anchored axis along a path""" + + def __init__(self, path, axis, **kwargs): + anchoredaxis.__init__(self, axis, text.defaulttexrunner, "pathaxis") + self.setpositioner(positioner.pathpositioner(path, **kwargs)) + self.create() + +def pathaxis(*args, **kwargs): + """creates an axiscanvas for an axis along a path""" + return anchoredpathaxis(*args, **kwargs).canvas + diff --git a/compiler/gdsMill/pyx/graph/axis/painter.py b/compiler/gdsMill/pyx/graph/axis/painter.py new file mode 100644 index 00000000..2391e1bb --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/painter.py @@ -0,0 +1,422 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +import math +from pyx import canvas, color, attr, text, style, unit, box, path +from pyx import trafo as trafomodule +from pyx.graph.axis import tick + + +goldenmean = 0.5 * (math.sqrt(5) + 1) + + +class axiscanvas(canvas.canvas): + """axis canvas""" + + def __init__(self, painter, graphtexrunner): + """initializes the instance + - sets extent to zero + - sets labels to an empty list""" + canvas._canvas.__init__(self) + self.extent_pt = 0 + self.labels = [] + if isinstance(painter, _text) and painter.texrunner: + self.settexrunner(painter.texrunner) + else: + self.settexrunner(graphtexrunner) + + +class rotatetext: + """create rotations accordingly to tick directions""" + + def __init__(self, direction, epsilon=1e-10): + self.direction = direction + self.epsilon = epsilon + + def trafo(self, dx, dy): + direction = self.direction + math.atan2(dy, dx) * 180 / math.pi + while (direction > 180 + self.epsilon): + direction -= 360 + while (direction < -180 - self.epsilon): + direction += 360 + while (direction > 90 + self.epsilon): + direction -= 180 + while (direction < -90 - self.epsilon): + direction += 180 + return trafomodule.rotate(direction) + + +rotatetext.parallel = rotatetext(90) +rotatetext.orthogonal = rotatetext(180) + + +class _text: + """a painter with a texrunner""" + + def __init__(self, texrunner=None): + self.texrunner = texrunner + + +class _title(_text): + """class for painting an axis title""" + + defaulttitleattrs = [text.halign.center, text.vshift.mathaxis] + + def __init__(self, titledist=0.3*unit.v_cm, + titleattrs=[], + titledirection=rotatetext.parallel, + titlepos=0.5, + **kwargs): + self.titledist = titledist + self.titleattrs = titleattrs + self.titledirection = titledirection + self.titlepos = titlepos + _text.__init__(self, **kwargs) + + def paint(self, canvas, data, axis, axispos): + if axis.title is not None and self.titleattrs is not None: + x, y = axispos.vtickpoint_pt(self.titlepos) + dx, dy = axispos.vtickdirection(self.titlepos) + titleattrs = self.defaulttitleattrs + self.titleattrs + if self.titledirection is not None: + titleattrs.append(self.titledirection.trafo(dx, dy)) + title = canvas.text_pt(x, y, axis.title, titleattrs) + canvas.extent_pt += unit.topt(self.titledist) + title.linealign_pt(canvas.extent_pt, -dx, -dy) + canvas.extent_pt += title.extent_pt(dx, dy) + + +class geometricseries(attr.changeattr): + + def __init__(self, initial, factor): + self.initial = initial + self.factor = factor + + def select(self, index, total): + return self.initial * (self.factor ** index) + + +class ticklength(geometricseries): pass + +_base = 0.12 * unit.v_cm + +ticklength.SHORT = ticklength(_base/math.sqrt(64), 1/goldenmean) +ticklength.SHORt = ticklength(_base/math.sqrt(32), 1/goldenmean) +ticklength.SHOrt = ticklength(_base/math.sqrt(16), 1/goldenmean) +ticklength.SHort = ticklength(_base/math.sqrt(8), 1/goldenmean) +ticklength.Short = ticklength(_base/math.sqrt(4), 1/goldenmean) +ticklength.short = ticklength(_base/math.sqrt(2), 1/goldenmean) +ticklength.normal = ticklength(_base, 1/goldenmean) +ticklength.long = ticklength(_base*math.sqrt(2), 1/goldenmean) +ticklength.Long = ticklength(_base*math.sqrt(4), 1/goldenmean) +ticklength.LOng = ticklength(_base*math.sqrt(8), 1/goldenmean) +ticklength.LONg = ticklength(_base*math.sqrt(16), 1/goldenmean) +ticklength.LONG = ticklength(_base*math.sqrt(32), 1/goldenmean) + + +class regular(_title): + """class for painting the ticks and labels of an axis""" + + defaulttickattrs = [] + defaultgridattrs = [] + defaultbasepathattrs = [style.linecap.square] + defaultlabelattrs = [text.halign.center, text.vshift.mathaxis] + + def __init__(self, innerticklength=ticklength.normal, + outerticklength=None, + tickattrs=[], + gridattrs=None, + basepathattrs=[], + labeldist=0.3*unit.v_cm, + labelattrs=[], + labeldirection=None, + labelhequalize=0, + labelvequalize=1, + **kwargs): + self.innerticklength = innerticklength + self.outerticklength = outerticklength + self.tickattrs = tickattrs + self.gridattrs = gridattrs + self.basepathattrs = basepathattrs + self.labeldist = labeldist + self.labelattrs = labelattrs + self.labeldirection = labeldirection + self.labelhequalize = labelhequalize + self.labelvequalize = labelvequalize + _title.__init__(self, **kwargs) + + def paint(self, canvas, data, axis, axispos): + for t in data.ticks: + t.temp_v = axis.convert(data, t) + t.temp_x_pt, t.temp_y_pt = axispos.vtickpoint_pt(t.temp_v) + t.temp_dx, t.temp_dy = axispos.vtickdirection(t.temp_v) + maxticklevel, maxlabellevel = tick.maxlevels(data.ticks) + labeldist_pt = unit.topt(self.labeldist) + + # create & align t.temp_labelbox + for t in data.ticks: + if t.labellevel is not None: + labelattrs = attr.selectattrs(self.labelattrs, t.labellevel, maxlabellevel) + if labelattrs is not None: + labelattrs = self.defaultlabelattrs + labelattrs + if self.labeldirection is not None: + labelattrs.append(self.labeldirection.trafo(t.temp_dx, t.temp_dy)) + if t.labelattrs is not None: + labelattrs.extend(t.labelattrs) + t.temp_labelbox = canvas.texrunner.text_pt(t.temp_x_pt, t.temp_y_pt, t.label, labelattrs) + if len(data.ticks) > 1: + equaldirection = 1 + for t in data.ticks[1:]: + if t.temp_dx != data.ticks[0].temp_dx or t.temp_dy != data.ticks[0].temp_dy: + equaldirection = 0 + else: + equaldirection = 0 + if equaldirection and ((not data.ticks[0].temp_dx and self.labelvequalize) or + (not data.ticks[0].temp_dy and self.labelhequalize)): + if self.labelattrs is not None: + box.linealignequal_pt([t.temp_labelbox for t in data.ticks if t.labellevel is not None], + labeldist_pt, -data.ticks[0].temp_dx, -data.ticks[0].temp_dy) + else: + for t in data.ticks: + if t.labellevel is not None and self.labelattrs is not None: + t.temp_labelbox.linealign_pt(labeldist_pt, -t.temp_dx, -t.temp_dy) + + for t in data.ticks: + if t.ticklevel is not None and self.tickattrs is not None: + tickattrs = attr.selectattrs(self.defaulttickattrs + self.tickattrs, t.ticklevel, maxticklevel) + if tickattrs is not None: + innerticklength = attr.selectattr(self.innerticklength, t.ticklevel, maxticklevel) + outerticklength = attr.selectattr(self.outerticklength, t.ticklevel, maxticklevel) + if innerticklength is not None or outerticklength is not None: + if innerticklength is None: + innerticklength = 0 + if outerticklength is None: + outerticklength = 0 + innerticklength_pt = unit.topt(innerticklength) + outerticklength_pt = unit.topt(outerticklength) + x1 = t.temp_x_pt + t.temp_dx * innerticklength_pt + y1 = t.temp_y_pt + t.temp_dy * innerticklength_pt + x2 = t.temp_x_pt - t.temp_dx * outerticklength_pt + y2 = t.temp_y_pt - t.temp_dy * outerticklength_pt + canvas.stroke(path.line_pt(x1, y1, x2, y2), tickattrs) + if outerticklength_pt > canvas.extent_pt: + canvas.extent_pt = outerticklength_pt + if -innerticklength_pt > canvas.extent_pt: + canvas.extent_pt = -innerticklength_pt + if self.gridattrs is not None: + gridattrs = attr.selectattrs(self.defaultgridattrs + self.gridattrs, t.ticklevel, maxticklevel) + if gridattrs is not None: + canvas.stroke(axispos.vgridpath(t.temp_v), gridattrs) + if t.labellevel is not None and self.labelattrs is not None: + canvas.insert(t.temp_labelbox) + canvas.labels.append(t.temp_labelbox) + extent_pt = t.temp_labelbox.extent_pt(t.temp_dx, t.temp_dy) + labeldist_pt + if extent_pt > canvas.extent_pt: + canvas.extent_pt = extent_pt + + if self.labelattrs is None: + canvas.labels = None + + if self.basepathattrs is not None: + canvas.stroke(axispos.vbasepath(), self.defaultbasepathattrs + self.basepathattrs) + + # for t in data.ticks: + # del t.temp_v # we've inserted those temporary variables ... and do not care any longer about them + # del t.temp_x_pt + # del t.temp_y_pt + # del t.temp_dx + # del t.temp_dy + # if t.labellevel is not None and self.labelattrs is not None: + # del t.temp_labelbox + + _title.paint(self, canvas, data, axis, axispos) + + +class linked(regular): + """class for painting a linked axis""" + + def __init__(self, labelattrs=None, # turn off labels and title + titleattrs=None, + **kwargs): + regular.__init__(self, labelattrs=labelattrs, + titleattrs=titleattrs, + **kwargs) + + +class bar(_title): + """class for painting a baraxis""" + + defaulttickattrs = [] + defaultbasepathattrs = [style.linecap.square] + defaultnameattrs = [text.halign.center, text.vshift.mathaxis] + + def __init__(self, innerticklength=None, + outerticklength=None, + tickattrs=[], + basepathattrs=[], + namedist=0.3*unit.v_cm, + nameattrs=[], + namedirection=None, + namepos=0.5, + namehequalize=0, + namevequalize=1, + **args): + self.innerticklength = innerticklength + self.outerticklength = outerticklength + self.tickattrs = tickattrs + self.basepathattrs = basepathattrs + self.namedist = namedist + self.nameattrs = nameattrs + self.namedirection = namedirection + self.namepos = namepos + self.namehequalize = namehequalize + self.namevequalize = namevequalize + _title.__init__(self, **args) + + def paint(self, canvas, data, axis, positioner): + namepos = [] + for name in data.names: + subaxis = data.subaxes[name] + v = subaxis.vmin + self.namepos * (subaxis.vmax - subaxis.vmin) + x, y = positioner.vtickpoint_pt(v) + dx, dy = positioner.vtickdirection(v) + namepos.append((v, x, y, dx, dy)) + nameboxes = [] + if self.nameattrs is not None: + for (v, x, y, dx, dy), name in zip(namepos, data.names): + nameattrs = self.defaultnameattrs + self.nameattrs + if self.namedirection is not None: + nameattrs.append(self.namedirection.trafo(tick.temp_dx, tick.temp_dy)) + nameboxes.append(canvas.texrunner.text_pt(x, y, str(name), nameattrs)) + labeldist_pt = canvas.extent_pt + unit.topt(self.namedist) + if len(namepos) > 1: + equaldirection = 1 + for np in namepos[1:]: + if np[3] != namepos[0][3] or np[4] != namepos[0][4]: + equaldirection = 0 + else: + equaldirection = 0 + if equaldirection and ((not namepos[0][3] and self.namevequalize) or + (not namepos[0][4] and self.namehequalize)): + box.linealignequal_pt(nameboxes, labeldist_pt, -namepos[0][3], -namepos[0][4]) + else: + for namebox, np in zip(nameboxes, namepos): + namebox.linealign_pt(labeldist_pt, -np[3], -np[4]) + if self.basepathattrs is not None: + p = positioner.vbasepath() + if p is not None: + canvas.stroke(p, self.defaultbasepathattrs + self.basepathattrs) + if ( self.tickattrs is not None and + (self.innerticklength is not None or self.outerticklength is not None) ): + if self.innerticklength is not None: + innerticklength_pt = unit.topt(self.innerticklength) + if canvas.extent_pt < -innerticklength_pt: + canvas.extent_pt = -innerticklength_pt + elif self.outerticklength is not None: + innerticklength_pt = 0 + if self.outerticklength is not None: + outerticklength_pt = unit.topt(self.outerticklength) + if canvas.extent_pt < outerticklength_pt: + canvas.extent_pt = outerticklength_pt + elif innerticklength_pt is not None: + outerticklength_pt = 0 + for v in [data.subaxes[name].vminover for name in data.names] + [1]: + x, y = positioner.vtickpoint_pt(v) + dx, dy = positioner.vtickdirection(v) + x1 = x + dx * innerticklength_pt + y1 = y + dy * innerticklength_pt + x2 = x - dx * outerticklength_pt + y2 = y - dy * outerticklength_pt + canvas.stroke(path.line_pt(x1, y1, x2, y2), self.defaulttickattrs + self.tickattrs) + for (v, x, y, dx, dy), namebox in zip(namepos, nameboxes): + newextent_pt = namebox.extent_pt(dx, dy) + labeldist_pt + if canvas.extent_pt < newextent_pt: + canvas.extent_pt = newextent_pt + for namebox in nameboxes: + canvas.insert(namebox) + _title.paint(self, canvas, data, axis, positioner) + + +class linkedbar(bar): + """class for painting a linked baraxis""" + + def __init__(self, nameattrs=None, titleattrs=None, **kwargs): + bar.__init__(self, nameattrs=nameattrs, titleattrs=titleattrs, **kwargs) + + def getsubaxis(self, subaxis, name): + from pyx.graph.axis import linkedaxis + return linkedaxis(subaxis, name) + + +class split(_title): + """class for painting a splitaxis""" + + defaultbreaklinesattrs = [] + + def __init__(self, breaklinesdist=0.05*unit.v_cm, + breaklineslength=0.5*unit.v_cm, + breaklinesangle=-60, + breaklinesattrs=[], + **args): + self.breaklinesdist = breaklinesdist + self.breaklineslength = breaklineslength + self.breaklinesangle = breaklinesangle + self.breaklinesattrs = breaklinesattrs + self.sin = math.sin(self.breaklinesangle*math.pi/180.0) + self.cos = math.cos(self.breaklinesangle*math.pi/180.0) + _title.__init__(self, **args) + + def paint(self, canvas, data, axis, axispos): + if self.breaklinesattrs is not None: + breaklinesdist_pt = unit.topt(self.breaklinesdist) + breaklineslength_pt = unit.topt(self.breaklineslength) + breaklinesextent_pt = (0.5*breaklinesdist_pt*math.fabs(self.cos) + + 0.5*breaklineslength_pt*math.fabs(self.sin)) + if canvas.extent_pt < breaklinesextent_pt: + canvas.extent_pt = breaklinesextent_pt + for v in [data.subaxes[name].vminover for name in data.names[1:]]: + # use a tangent of the basepath (this is independent of the tickdirection) + p = axispos.vbasepath(v, None).normpath() + breakline = p.tangent(0, length=self.breaklineslength) + widthline = p.tangent(0, length=self.breaklinesdist).transformed(trafomodule.rotate(self.breaklinesangle+90, *breakline.atbegin())) + # XXX Uiiii + tocenter = map(lambda x: 0.5*(x[0]-x[1]), zip(breakline.atbegin(), breakline.atend())) + towidth = map(lambda x: 0.5*(x[0]-x[1]), zip(widthline.atbegin(), widthline.atend())) + breakline = breakline.transformed(trafomodule.translate(*tocenter).rotated(self.breaklinesangle, *breakline.atbegin())) + breakline1 = breakline.transformed(trafomodule.translate(*towidth)) + breakline2 = breakline.transformed(trafomodule.translate(-towidth[0], -towidth[1])) + canvas.fill(path.path(path.moveto_pt(*breakline1.atbegin_pt()), + path.lineto_pt(*breakline1.atend_pt()), + path.lineto_pt(*breakline2.atend_pt()), + path.lineto_pt(*breakline2.atbegin_pt()), + path.closepath()), [color.gray.white]) + canvas.stroke(breakline1, self.defaultbreaklinesattrs + self.breaklinesattrs) + canvas.stroke(breakline2, self.defaultbreaklinesattrs + self.breaklinesattrs) + _title.paint(self, canvas, data, axis, axispos) + + +class linkedsplit(split): + + def __init__(self, titleattrs=None, **kwargs): + split.__init__(self, titleattrs=titleattrs, **kwargs) diff --git a/compiler/gdsMill/pyx/graph/axis/parter.py b/compiler/gdsMill/pyx/graph/axis/parter.py new file mode 100644 index 00000000..f01e73d6 --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/parter.py @@ -0,0 +1,309 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import nested_scopes + +import math +from pyx.graph.axis import tick + + +# Note: A partition is a list of ticks. + +class _partdata: + """state storage class for a partfunction + + partdata is used to keep local data and a current state to emulate + generators. In the future we might use yield statements within a + partfunction. Currently we add partdata by a lambda construct and + do inplace modifications within partdata to keep track of the state. + """ + + def __init__(self, **kwargs): + for key, value in kwargs.items(): + setattr(self, key, value) + + +class _parter: + """interface of a partitioner""" + + def partfunctions(self, min, max, extendmin, extendmax): + """returns a list of partfunctions + + A partfunction can be called without further arguments and + it will return a new partition each time, or None. Several + partfunctions are used to walk in different "directions" + (like more and less partitions). + + Note that we do not alternate walking in different directions + (i.e. alternate the partfunction calls). Instead we first walk + into one direction (which should give less and less ticks) until + the rating becomes bad and when try more ticks. We want to keep + the number of ticks small compared to a simple alternate search. + """ + # This is a (useless) empty partitioner. + return [] + + +class linear(_parter): + """partitioner to create a single linear parition""" + + def __init__(self, tickdists=None, labeldists=None, extendtick=0, extendlabel=None, epsilon=1e-10): + if tickdists is None and labeldists is not None: + self.ticklist = [tick.rational(labeldists[0])] + else: + self.ticklist = map(tick.rational, tickdists) + if labeldists is None and tickdists is not None: + self.labellist = [tick.rational(tickdists[0])] + else: + self.labellist = map(tick.rational, labeldists) + self.extendtick = extendtick + self.extendlabel = extendlabel + self.epsilon = epsilon + + def extendminmax(self, min, max, dist, extendmin, extendmax): + """return new min, max tuple extending the range min, max + - dist is the tick distance to be used + - extendmin and extendmax are booleans to allow for the extension""" + if extendmin: + min = float(dist) * math.floor(min / float(dist) + self.epsilon) + if extendmax: + max = float(dist) * math.ceil(max / float(dist) - self.epsilon) + return min, max + + def getticks(self, min, max, dist, ticklevel=None, labellevel=None): + """return a list of equal spaced ticks + - the tick distance is dist, the ticklevel is set to ticklevel and + the labellevel is set to labellevel + - min, max is the range where ticks should be placed""" + imin = int(math.ceil(min/float(dist) - 0.5*self.epsilon)) + imax = int(math.floor(max/float(dist) + 0.5*self.epsilon)) + ticks = [] + for i in range(imin, imax + 1): + ticks.append(tick.tick((i*dist.num, dist.denom), ticklevel=ticklevel, labellevel=labellevel)) + return ticks + + def partfunction(self, data): + if data.first: + data.first = 0 + min = data.min + max = data.max + if self.extendtick is not None and len(self.ticklist) > self.extendtick: + min, max = self.extendminmax(min, max, self.ticklist[self.extendtick], data.extendmin, data.extendmax) + if self.extendlabel is not None and len(self.labellist) > self.extendlabel: + min, max = self.extendminmax(min, max, self.labellist[self.extendlabel], data.extendmin, data.extendmax) + + ticks = [] + for i in range(len(self.ticklist)): + ticks = tick.mergeticklists(ticks, self.getticks(min, max, self.ticklist[i], ticklevel=i)) + for i in range(len(self.labellist)): + ticks = tick.mergeticklists(ticks, self.getticks(min, max, self.labellist[i], labellevel=i)) + + return ticks + + return None + + def partfunctions(self, min, max, extendmin, extendmax): + return [lambda d=_partdata(first=1, min=min, max=max, extendmin=extendmin, extendmax=extendmax): + self.partfunction(d)] + +lin = linear + + +class autolinear(_parter): + """partitioner to create an arbitrary number of linear paritions""" + + defaultvariants = [[tick.rational((1, 1)), tick.rational((1, 2))], + [tick.rational((2, 1)), tick.rational((1, 1))], + [tick.rational((5, 2)), tick.rational((5, 4))], + [tick.rational((5, 1)), tick.rational((5, 2))]] + + def __init__(self, variants=defaultvariants, extendtick=0, epsilon=1e-10): + self.variants = variants + self.extendtick = extendtick + self.epsilon = epsilon + + def partfunctions(self, min, max, extendmin, extendmax): + try: + logmm = math.log(max - min) / math.log(10) + except ArithmeticError: + raise RuntimeError("partitioning failed due to empty or invalid axis range") + if logmm < 0: # correction for rounding towards zero of the int routine + base = tick.rational((10, 1), power=int(logmm-1)) + else: + base = tick.rational((10, 1), power=int(logmm)) + ticks = map(tick.rational, self.variants[0]) + useticks = [t * base for t in ticks] + + return [lambda d=_partdata(min=min, max=max, extendmin=extendmin, extendmax=extendmax, + sign=1, tickindex=-1, base=tick.rational(base)): + self.partfunction(d), + lambda d=_partdata(min=min, max=max, extendmin=extendmin, extendmax=extendmax, + sign=-1, tickindex=0, base=tick.rational(base)): + self.partfunction(d)] + + def partfunction(self, data): + if data.sign == 1: + if data.tickindex < len(self.variants) - 1: + data.tickindex += 1 + else: + data.tickindex = 0 + data.base.num *= 10 + else: + if data.tickindex: + data.tickindex -= 1 + else: + data.tickindex = len(self.variants) - 1 + data.base.denom *= 10 + tickdists = [tick.rational(t) * data.base for t in self.variants[data.tickindex]] + linearparter = linear(tickdists=tickdists, extendtick=self.extendtick, epsilon=self.epsilon) + return linearparter.partfunctions(min=data.min, max=data.max, extendmin=data.extendmin, extendmax=data.extendmax)[0]() + +autolin = autolinear + + +class preexp: + """definition of a logarithmic partition + + exp is an integer, which defines multiplicator (usually 10). + pres are a list of tick positions (rational numbers, e.g. + instances of rational). possible positions are the tick + positions and arbitrary divisions and multiplications of + the tick positions by exp.""" + + def __init__(self, pres, exp): + self.pres = pres + self.exp = exp + + +class logarithmic(linear): + """partitioner to create a single logarithmic parition""" + + # define some useful constants + pre1exp = preexp([tick.rational((1, 1))], 10) + pre125exp = preexp([tick.rational((1, 1)), tick.rational((2, 1)), tick.rational((5, 1))], 10) + pre1to9exp = preexp([tick.rational((x, 1)) for x in range(1, 10)], 10) + # ^- we always include 1 in order to get extendto(tick|label)level to work as expected + + def __init__(self, tickpreexps=None, labelpreexps=None, extendtick=0, extendlabel=None, epsilon=1e-10): + if tickpreexps is None and labelpreexps is not None: + self.ticklist = [labelpreexps[0]] + else: + self.ticklist = tickpreexps + + if labelpreexps is None and tickpreexps is not None: + self.labellist = [tickpreexps[0]] + else: + self.labellist = labelpreexps + self.extendtick = extendtick + self.extendlabel = extendlabel + self.epsilon = epsilon + + def extendminmax(self, min, max, preexp, extendmin, extendmax): + minpower = None + maxpower = None + for i in xrange(len(preexp.pres)): + imin = int(math.floor(math.log(min / float(preexp.pres[i])) / + math.log(preexp.exp) + self.epsilon)) + 1 + imax = int(math.ceil(math.log(max / float(preexp.pres[i])) / + math.log(preexp.exp) - self.epsilon)) - 1 + if minpower is None or imin < minpower: + minpower, minindex = imin, i + if maxpower is None or imax >= maxpower: + maxpower, maxindex = imax, i + if minindex: + minrational = preexp.pres[minindex - 1] + else: + minrational = preexp.pres[-1] + minpower -= 1 + if maxindex != len(preexp.pres) - 1: + maxrational = preexp.pres[maxindex + 1] + else: + maxrational = preexp.pres[0] + maxpower += 1 + if extendmin: + min = float(minrational) * float(preexp.exp) ** minpower + if extendmax: + max = float(maxrational) * float(preexp.exp) ** maxpower + return min, max + + def getticks(self, min, max, preexp, ticklevel=None, labellevel=None): + ticks = [] + minimin = 0 + maximax = 0 + for f in preexp.pres: + thisticks = [] + imin = int(math.ceil(math.log(min / float(f)) / + math.log(preexp.exp) - 0.5 * self.epsilon)) + imax = int(math.floor(math.log(max / float(f)) / + math.log(preexp.exp) + 0.5 * self.epsilon)) + for i in range(imin, imax + 1): + pos = f * tick.rational((preexp.exp, 1), power=i) + thisticks.append(tick.tick((pos.num, pos.denom), ticklevel = ticklevel, labellevel = labellevel)) + ticks = tick.mergeticklists(ticks, thisticks) + return ticks + +log = logarithmic + + +class autologarithmic(logarithmic): + """partitioner to create several logarithmic paritions""" + + defaultvariants = [([logarithmic.pre1exp, # ticks + logarithmic.pre1to9exp], # subticks + [logarithmic.pre1exp, # labels + logarithmic.pre125exp]), # sublevels + + ([logarithmic.pre1exp, # ticks + logarithmic.pre1to9exp], # subticks + None)] # labels like ticks + + def __init__(self, variants=defaultvariants, extendtick=0, extendlabel=None, autoexponent=10, epsilon=1e-10): + self.variants = variants + self.extendtick = extendtick + self.extendlabel = extendlabel + self.autoexponent = autoexponent + self.epsilon = epsilon + + def partfunctions(self, min, max, extendmin, extendmax): + return [lambda d=_partdata(min=min, max=max, extendmin=extendmin, extendmax=extendmax, + variantsindex=len(self.variants)): + self.variantspartfunction(d), + lambda d=_partdata(min=min, max=max, extendmin=extendmin, extendmax=extendmax, + exponent=self.autoexponent): + self.autopartfunction(d)] + + def variantspartfunction(self, data): + data.variantsindex -= 1 + if 0 <= data.variantsindex: + logarithmicparter= logarithmic(tickpreexps=self.variants[data.variantsindex][0], labelpreexps=self.variants[data.variantsindex][1], + extendtick=self.extendtick, extendlabel=self.extendlabel, epsilon=self.epsilon) + return logarithmicparter.partfunctions(min=data.min, max=data.max, extendmin=data.extendmin, extendmax=data.extendmax)[0]() + return None + + def autopartfunction(self, data): + data.exponent *= self.autoexponent + logarithmicparter= logarithmic(tickpreexps=[preexp([tick.rational((1, 1))], data.exponent), logarithmic.pre1exp], + extendtick=self.extendtick, extendlabel=self.extendlabel, epsilon=self.epsilon) + return logarithmicparter.partfunctions(min=data.min, max=data.max, extendmin=data.extendmin, extendmax=data.extendmax)[0]() + +autolog = autologarithmic diff --git a/compiler/gdsMill/pyx/graph/axis/positioner.py b/compiler/gdsMill/pyx/graph/axis/positioner.py new file mode 100644 index 00000000..bdee12db --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/positioner.py @@ -0,0 +1,122 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import math +from pyx import path, unit + + +class _positioner: + """interface definition of axis tick position methods + - these methods are used for the postitioning of the ticks + when painting an axis""" + # TODO: should we add a local transformation (for label text etc?) + # (this might replace tickdirection (and even tickposition?)) + + def vbasepath(self, v1=None, v2=None): + """return the basepath as a path + - like basepath, but for graph coordinates""" + + def vgridpath(self, v): + """return the gridpath as a path for a given position v + in graph coordinates + - might return None when no gridpath is available""" + return None + + def vtickpoint_pt(self, v): + """return tick position (x, y) in pts for a tick at position v in graph coordinates""" + + def vtickdirection(self, v): + """return direction tuple (dx, dy) for a tick at position v in graph coordinates""" + + +class pathpositioner(_positioner): + """axis tick position methods along an arbitrary path""" + + def __init__(self, p, direction=1): + self.path = p + self.normpath = p.normpath() + self.arclen_pt = self.normpath.arclen_pt() + self.arclen = self.arclen_pt * unit.t_pt + self.direction = direction + + def vbasepath(self, v1=None, v2=None): + if v1 is None: + if v2 is None: + return self.path + else: + return self.normpath.split(self.normpath.arclentoparam(v2 * self.arclen))[0] + else: + if v2 is None: + return self.normpath.split(self.normpath.arclentoparam(v1 * self.arclen))[1] + else: + return self.normpath.split(*self.normpath.arclentoparam([v1 * self.arclen, v2 * self.arclen]))[1] + + def vtickpoint_pt(self, v): + return self.normpath.at_pt(self.normpath.arclentoparam(v * self.arclen)) + + def vtickdirection(self, v): + return self.normpath.rotation(self.normpath.arclentoparam(v * self.arclen)).apply_pt(0, self.direction) + + +class lineaxispos_pt: + """an axispos linear along a line with a fix direction for the ticks""" + + def __init__(self, x1_pt, y1_pt, x2_pt, y2_pt, fixtickdirection, vgridpath): + self.x1_pt = x1_pt + self.y1_pt = y1_pt + self.x2_pt = x2_pt + self.y2_pt = y2_pt + self.fixtickdirection = fixtickdirection + self.vgridpath = vgridpath + + def vbasepath(self, v1=None, v2=None): + if v1 is None: + v1 = 0 + if v2 is None: + v2 = 1 + return path.line_pt((1-v1)*self.x1_pt+v1*self.x2_pt, + (1-v1)*self.y1_pt+v1*self.y2_pt, + (1-v2)*self.x1_pt+v2*self.x2_pt, + (1-v2)*self.y1_pt+v2*self.y2_pt) + + def vtickpoint_pt(self, v): + return (1-v)*self.x1_pt+v*self.x2_pt, (1-v)*self.y1_pt+v*self.y2_pt + + def vtickdirection(self, v): + return self.fixtickdirection + + +class flexlineaxispos_pt(lineaxispos_pt): + """an axispos linear along a line with flexible direction for the ticks""" + + def __init__(self, vtickpoint_pt, vtickdirection, vgridpath): + self.vtickpoint_pt = vtickpoint_pt + self.vtickdirection = vtickdirection + self.vgridpath = vgridpath + + def vbasepath(self, v1=None, v2=None): + if v1 is None: + v1 = 0 + if v2 is None: + v2 = 1 + x1_pt, y1_pt = self.vtickpoint_pt(v1) + x2_pt, y2_pt = self.vtickpoint_pt(v2) + return path.line_pt(x1_pt, y1_pt, x2_pt, y2_pt) diff --git a/compiler/gdsMill/pyx/graph/axis/rater.py b/compiler/gdsMill/pyx/graph/axis/rater.py new file mode 100644 index 00000000..0daf6806 --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/rater.py @@ -0,0 +1,242 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +from pyx import unit, box +from pyx.graph.axis import tick + + +# rater +# conseptional remarks: +# - raters are used to calculate a rating for a realization of something +# - a rating means a positive floating point value +# - ratings are used to order those realizations by their suitability +# (small ratings are better) +# - a rating of None means not suitable at all (those realizations should be +# thrown out) + + +class cube: + """a value rater + - a cube rater has an optimal value, where the rate becomes zero + - for a left (below the optimum) and a right value (above the optimum), + the rating is value is set to 1 (modified by an overall weight factor + for the rating) + - the analytic form of the rating is cubic for both, the left and + the right side of the rater, independently""" + + def __init__(self, opt, left=None, right=None, weight=1): + """initializes the rater + - by default, left is set to zero, right is set to 3*opt + - left should be smaller than opt, right should be bigger than opt + - weight should be positive and is a factor multiplicated to the rates""" + if left is None: + left = 0 + if right is None: + right = 3*opt + self.opt = opt + self.left = left + self.right = right + self.weight = weight + + def rate(self, value, density): + """returns a rating for a value + - the density lineary rescales the rater (the optimum etc.), + e.g. a value bigger than one increases the optimum (when it is + positive) and a value lower than one decreases the optimum (when + it is positive); the density itself should be positive""" + opt = self.opt * density + if value < opt: + other = self.left * density + elif value > opt: + other = self.right * density + else: + return 0 + factor = (value - opt) / float(other - opt) + return self.weight * (factor ** 3) + + +class distance: + # TODO: update docstring + """a distance rater (rates a list of distances) + - the distance rater rates a list of distances by rating each independently + and returning the average rate + - there is an optimal value, where the rate becomes zero + - the analytic form is linary for values above the optimal value + (twice the optimal value has the rating one, three times the optimal + value has the rating two, etc.) + - the analytic form is reciprocal subtracting one for values below the + optimal value (halve the optimal value has the rating one, one third of + the optimal value has the rating two, etc.)""" + + def __init__(self, opt, weight=0.1): + """inititializes the rater + - opt is the optimal length (a visual PyX length) + - weight should be positive and is a factor multiplicated to the rates""" + self.opt = opt + self.weight = weight + + def rate(self, distances, density): + """rate distances + - the distances are a list of positive floats in PostScript points + - the density lineary rescales the rater (the optimum etc.), + e.g. a value bigger than one increases the optimum (when it is + positive) and a value lower than one decreases the optimum (when + it is positive); the density itself should be positive""" + if len(distances): + opt = unit.topt(self.opt) / density + rate = 0 + for distance in distances: + if distance < opt: + rate += self.weight * (opt / distance - 1) + else: + rate += self.weight * (distance / opt - 1) + return rate / float(len(distances)) + + +class rater: + """a rater for ticks + - the rating of axes is splited into two separate parts: + - rating of the ticks in terms of the number of ticks, subticks, + labels, etc. + - rating of the label distances + - in the end, a rate for ticks is the sum of these rates + - it is useful to first just rate the number of ticks etc. + and selecting those partitions, where this fits well -> as soon + as an complete rate (the sum of both parts from the list above) + of a first ticks is below a rate of just the number of ticks, + subticks labels etc. of other ticks, those other ticks will never + be better than the first one -> we gain speed by minimizing the + number of ticks, where label distances have to be taken into account) + - both parts of the rating are shifted into instances of raters + defined above --- right now, there is not yet a strict interface + for this delegation (should be done as soon as it is needed)""" + + def __init__(self, ticks, labels, range, distance): + """initializes the axis rater + - ticks and labels are lists of instances of a value rater + - the first entry in ticks rate the number of ticks, the + second the number of subticks, etc.; when there are no + ticks of a level or there is not rater for a level, the + level is just ignored + - labels is analogous, but for labels + - within the rating, all ticks with a higher level are + considered as ticks for a given level + - range is a value rater instance, which rates the covering + of an axis range by the ticks (as a relative value of the + tick range vs. the axis range), ticks might cover less or + more than the axis range (for the standard automatic axis + partition schemes an extention of the axis range is normal + and should get some penalty) + - distance is an distance rater instance""" + self.ticks = ticks + self.labels = labels + self.range = range + self.distance = distance + + def rateticks(self, axis, ticks, density): + """rates ticks by the number of ticks, subticks, labels etc. + - takes into account the number of ticks, subticks, labels + etc. and the coverage of the axis range by the ticks + - when there are no ticks of a level or there was not rater + given in the constructor for a level, the level is just + ignored + - the method returns the sum of the rating results divided + by the sum of the weights of the raters + - within the rating, all ticks with a higher level are + considered as ticks for a given level""" + maxticklevel, maxlabellevel = tick.maxlevels(ticks) + numticks = [0]*maxticklevel + numlabels = [0]*maxlabellevel + for t in ticks: + if t.ticklevel is not None: + for level in range(t.ticklevel, maxticklevel): + numticks[level] += 1 + if t.labellevel is not None: + for level in range(t.labellevel, maxlabellevel): + numlabels[level] += 1 + rate = 0 + weight = 0 + for numtick, rater in zip(numticks, self.ticks): + rate += rater.rate(numtick, density) + weight += rater.weight + for numlabel, rater in zip(numlabels, self.labels): + rate += rater.rate(numlabel, density) + weight += rater.weight + return rate/weight + + def raterange(self, tickrange, datarange): + """rate the range covered by the ticks compared to the range + of the data + - tickrange and datarange are the ranges covered by the ticks + and the data in graph coordinates + - usually, the datarange is 1 (ticks are calculated for a + given datarange) + - the ticks might cover less or more than the data range (for + the standard automatic axis partition schemes an extention + of the axis range is normal and should get some penalty)""" + return self.range.rate(tickrange, datarange) + + def ratelayout(self, axiscanvas, density): + """rate distances of the labels in an axis canvas + - the distances should be collected as box distances of + subsequent labels + - the axiscanvas provides a labels attribute for easy + access to the labels whose distances have to be taken + into account + - the density is used within the distancerate instance""" + if axiscanvas.labels is None: # to disable any layout rating + return 0 + if len(axiscanvas.labels) > 1: + try: + distances = [axiscanvas.labels[i].boxdistance_pt(axiscanvas.labels[i+1]) + for i in range(len(axiscanvas.labels) - 1)] + except box.BoxCrossError: + return None + return self.distance.rate(distances, density) + else: + return None + + +class linear(rater): + """a rater with predefined constructor arguments suitable for a linear axis""" + + def __init__(self, ticks=[cube(4), cube(10, weight=0.5)], + labels=[cube(4)], + range=cube(1, weight=2), + distance=distance(1*unit.v_cm)): + rater.__init__(self, ticks, labels, range, distance) + +lin = linear + + +class logarithmic(rater): + """a rater with predefined constructor arguments suitable for a logarithmic axis""" + + def __init__(self, ticks=[cube(5, right=20), cube(20, right=100, weight=0.5)], + labels=[cube(5, right=20), cube(5, right=20, weight=0.5)], + range=cube(1, weight=2), + distance=distance(1*unit.v_cm)): + rater.__init__(self, ticks, labels, range, distance) + +log = logarithmic diff --git a/compiler/gdsMill/pyx/graph/axis/texter.py b/compiler/gdsMill/pyx/graph/axis/texter.py new file mode 100644 index 00000000..267460bd --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/texter.py @@ -0,0 +1,448 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2004 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +from pyx import text +from pyx.graph.axis import tick + + +class _Itexter: + + def labels(self, ticks): + """fill the label attribute of ticks + - ticks is a list of instances of tick + - for each element of ticks the value of the attribute label is set to + a string appropriate to the attributes num and denom of that tick + instance + - label attributes of the tick instances are just kept, whenever they + are not equal to None + - the method might modify the labelattrs attribute of the ticks; be sure + to not modify it in-place!""" + + +class decimal: + "a texter creating decimal labels (e.g. '1.234' or even '0.\overline{3}')" + + __implements__ = _Itexter + + def __init__(self, prefix="", infix="", suffix="", equalprecision=0, + decimalsep=".", thousandsep="", thousandthpartsep="", + plus="", minus="-", period=r"\overline{%s}", + labelattrs=[text.mathmode]): + r"""initializes the instance + - prefix, infix, and suffix (strings) are added at the begin, + immediately after the minus, and at the end of the label, + respectively + - decimalsep, thousandsep, and thousandthpartsep (strings) + are used as separators + - plus or minus (string) is inserted for non-negative or negative numbers + - period (string) is taken as a format string generating a period; + it has to contain exactly one string insert operators "%s" for the + period; usually it should be r"\overline{%s}" + - labelattrs is a list of attributes to be added to the label attributes + given in the painter""" + self.prefix = prefix + self.infix = infix + self.suffix = suffix + self.equalprecision = equalprecision + self.decimalsep = decimalsep + self.thousandsep = thousandsep + self.thousandthpartsep = thousandthpartsep + self.plus = plus + self.minus = minus + self.period = period + self.labelattrs = labelattrs + + def labels(self, ticks): + labeledticks = [] + maxdecprecision = 0 + for tick in ticks: + if tick.label is None and tick.labellevel is not None: + labeledticks.append(tick) + m, n = tick.num, tick.denom + if m < 0: m = -m + if n < 0: n = -n + quotient, remainder = divmod(m, n) + quotient = str(quotient) + if len(self.thousandsep): + l = len(quotient) + tick.label = "" + for i in range(l): + tick.label += quotient[i] + if not ((l-i-1) % 3) and l > i+1: + tick.label += self.thousandsep + else: + tick.label = quotient + if remainder: + tick.label += self.decimalsep + oldremainders = [] + tick.temp_decprecision = 0 + while (remainder): + tick.temp_decprecision += 1 + if remainder in oldremainders: + tick.temp_decprecision = None + periodstart = len(tick.label) - (len(oldremainders) - oldremainders.index(remainder)) + tick.label = tick.label[:periodstart] + self.period % tick.label[periodstart:] + break + oldremainders += [remainder] + remainder *= 10 + quotient, remainder = divmod(remainder, n) + if not ((tick.temp_decprecision - 1) % 3) and tick.temp_decprecision > 1: + tick.label += self.thousandthpartsep + tick.label += str(quotient) + if maxdecprecision < tick.temp_decprecision: + maxdecprecision = tick.temp_decprecision + if self.equalprecision: + for tick in labeledticks: + if tick.temp_decprecision is not None: + if tick.temp_decprecision == 0 and maxdecprecision > 0: + tick.label += self.decimalsep + for i in range(tick.temp_decprecision, maxdecprecision): + if not ((i - 1) % 3) and i > 1: + tick.label += self.thousandthpartsep + tick.label += "0" + for tick in labeledticks: + if tick.num * tick.denom < 0: + plusminus = self.minus + else: + plusminus = self.plus + tick.label = "%s%s%s%s%s" % (self.prefix, plusminus, self.infix, tick.label, self.suffix) + tick.labelattrs = tick.labelattrs + self.labelattrs + + # del tick.temp_decprecision # we've inserted this temporary variable ... and do not care any longer about it + + +class exponential: + "a texter creating labels with exponentials (e.g. '2\cdot10^5')" + + __implements__ = _Itexter + + def __init__(self, plus="", minus="-", + mantissaexp=r"{{%s}\cdot10^{%s}}", + skipexp0=r"{%s}", + skipexp1=None, + nomantissaexp=r"{10^{%s}}", + minusnomantissaexp=r"{-10^{%s}}", + mantissamin=tick.rational((1, 1)), mantissamax=tick.rational((10L, 1)), + skipmantissa1=0, skipallmantissa1=1, + mantissatexter=decimal()): + r"""initializes the instance + - plus or minus (string) is inserted for non-negative or negative exponents + - mantissaexp (string) is taken as a format string generating the exponent; + it has to contain exactly two string insert operators "%s" -- + the first for the mantissa and the second for the exponent; + examples are r"{{%s}\cdot10^{%s}}" and r"{{%s}{\rm e}{%s}}" + - skipexp0 (string) is taken as a format string used for exponent 0; + exactly one string insert operators "%s" for the mantissa; + None turns off the special handling of exponent 0; + an example is r"{%s}" + - skipexp1 (string) is taken as a format string used for exponent 1; + exactly one string insert operators "%s" for the mantissa; + None turns off the special handling of exponent 1; + an example is r"{{%s}\cdot10}" + - nomantissaexp (string) is taken as a format string generating the exponent + when the mantissa is one and should be skipped; it has to contain + exactly one string insert operators "%s" for the exponent; + an examples is r"{10^{%s}}" + - minusnomantissaexp (string) is taken as a format string generating the exponent + when the mantissa is minus one and should be skipped; it has to contain + exactly one string insert operators "%s" for the exponent; + None turns off the special handling of mantissa -1; + an examples is r"{-10^{%s}}" + - mantissamin and mantissamax are the minimum and maximum of the mantissa; + they are rational instances greater than zero and mantissamin < mantissamax; + the sign of the tick is ignored here + - skipmantissa1 (boolean) turns on skipping of any mantissa equals one + (and minus when minusnomantissaexp is set) + - skipallmantissa1 (boolean) as above, but all mantissas must be 1 (or -1) + - mantissatexter is the texter for the mantissa + - the skipping of a mantissa is stronger than the skipping of an exponent""" + self.plus = plus + self.minus = minus + self.mantissaexp = mantissaexp + self.skipexp0 = skipexp0 + self.skipexp1 = skipexp1 + self.nomantissaexp = nomantissaexp + self.minusnomantissaexp = minusnomantissaexp + self.mantissamin = mantissamin + self.mantissamax = mantissamax + self.mantissamindivmax = self.mantissamin / self.mantissamax + self.mantissamaxdivmin = self.mantissamax / self.mantissamin + self.skipmantissa1 = skipmantissa1 + self.skipallmantissa1 = skipallmantissa1 + self.mantissatexter = mantissatexter + + def labels(self, ticks): + labeledticks = [] + for tick in ticks: + if tick.label is None and tick.labellevel is not None: + tick.temp_orgnum, tick.temp_orgdenom = tick.num, tick.denom + labeledticks.append(tick) + tick.temp_exp = 0 + if tick.num: + while abs(tick) >= self.mantissamax: + tick.temp_exp += 1 + x = tick * self.mantissamindivmax + tick.num, tick.denom = x.num, x.denom + while abs(tick) < self.mantissamin: + tick.temp_exp -= 1 + x = tick * self.mantissamaxdivmin + tick.num, tick.denom = x.num, x.denom + if tick.temp_exp < 0: + tick.temp_exp = "%s%i" % (self.minus, -tick.temp_exp) + else: + tick.temp_exp = "%s%i" % (self.plus, tick.temp_exp) + self.mantissatexter.labels(labeledticks) + if self.minusnomantissaexp is not None: + allmantissa1 = len(labeledticks) == len([tick for tick in labeledticks if abs(tick.num) == abs(tick.denom)]) + else: + allmantissa1 = len(labeledticks) == len([tick for tick in labeledticks if tick.num == tick.denom]) + for tick in labeledticks: + if (self.skipallmantissa1 and allmantissa1 or + (self.skipmantissa1 and (tick.num == tick.denom or + (tick.num == -tick.denom and self.minusnomantissaexp is not None)))): + if tick.num == tick.denom: + tick.label = self.nomantissaexp % tick.temp_exp + else: + tick.label = self.minusnomantissaexp % tick.temp_exp + else: + if tick.temp_exp == "0" and self.skipexp0 is not None: + tick.label = self.skipexp0 % tick.label + elif tick.temp_exp == "1" and self.skipexp1 is not None: + tick.label = self.skipexp1 % tick.label + else: + tick.label = self.mantissaexp % (tick.label, tick.temp_exp) + tick.num, tick.denom = tick.temp_orgnum, tick.temp_orgdenom + + # del tick.temp_orgnum # we've inserted those temporary variables ... and do not care any longer about them + # del tick.temp_orgdenom + # del tick.temp_exp + + +class mixed: + "a texter creating decimal or exponential labels" + + __implements__ = _Itexter + + def __init__(self, smallestdecimal=tick.rational((1, 1000)), + biggestdecimal=tick.rational((9999, 1)), + equaldecision=1, + decimal=decimal(), + exponential=exponential()): + """initializes the instance + - smallestdecimal and biggestdecimal are the smallest and + biggest decimal values, where the decimal texter should be used; + they are rational instances; the sign of the tick is ignored here; + a tick at zero is considered for the decimal texter as well + - equaldecision (boolean) uses decimal texter or exponential texter + globaly (set) or for each tick separately (unset) + - decimal and exponential are texters to be used""" + self.smallestdecimal = smallestdecimal + self.biggestdecimal = biggestdecimal + self.equaldecision = equaldecision + self.decimal = decimal + self.exponential = exponential + + def labels(self, ticks): + decticks = [] + expticks = [] + for tick in ticks: + if tick.label is None and tick.labellevel is not None: + if not tick.num or (abs(tick) >= self.smallestdecimal and abs(tick) <= self.biggestdecimal): + decticks.append(tick) + else: + expticks.append(tick) + if self.equaldecision: + if len(expticks): + self.exponential.labels(ticks) + else: + self.decimal.labels(ticks) + else: + for tick in decticks: + self.decimal.labels([tick]) + for tick in expticks: + self.exponential.labels([tick]) + + +class rational: + "a texter creating rational labels (e.g. 'a/b' or even 'a \over b')" + # XXX: we use divmod here to be more expicit + + __implements__ = _Itexter + + def __init__(self, prefix="", infix="", suffix="", + numprefix="", numinfix="", numsuffix="", + denomprefix="", denominfix="", denomsuffix="", + plus="", minus="-", minuspos=0, over=r"{{%s}\over{%s}}", + equaldenom=0, skip1=1, skipnum0=1, skipnum1=1, skipdenom1=1, + labelattrs=[text.mathmode]): + r"""initializes the instance + - prefix, infix, and suffix (strings) are added at the begin, + immediately after the minus, and at the end of the label, + respectively + - prefixnum, infixnum, and suffixnum (strings) are added + to the labels numerator correspondingly + - prefixdenom, infixdenom, and suffixdenom (strings) are added + to the labels denominator correspondingly + - plus or minus (string) is inserted for non-negative or negative numbers + - minuspos is an integer, which determines the position, where the + plus or minus sign has to be placed; the following values are allowed: + 1 - writes the plus or minus in front of the numerator + 0 - writes the plus or minus in front of the hole fraction + -1 - writes the plus or minus in front of the denominator + - over (string) is taken as a format string generating the + fraction bar; it has to contain exactly two string insert + operators "%s" -- the first for the numerator and the second + for the denominator; by far the most common examples are + r"{{%s}\over{%s}}" and "{{%s}/{%s}}" + - usually the numerator and denominator are canceled; however, + when equaldenom is set, the least common multiple of all + denominators is used + - skip1 (boolean) just prints the prefix, the plus or minus, + the infix and the suffix, when the value is plus or minus one + and at least one of prefix, infix and the suffix is present + - skipnum0 (boolean) just prints a zero instead of + the hole fraction, when the numerator is zero; + no prefixes, infixes, and suffixes are taken into account + - skipnum1 (boolean) just prints the numprefix, the plus or minus, + the numinfix and the numsuffix, when the num value is plus or minus one + and at least one of numprefix, numinfix and the numsuffix is present + - skipdenom1 (boolean) just prints the numerator instead of + the hole fraction, when the denominator is one and none of the parameters + denomprefix, denominfix and denomsuffix are set and minuspos is not -1 or the + fraction is positive + - labelattrs is a list of attributes for a texrunners text method; + None is considered as an empty list; labelattrs might be changed + in the painter as well""" + self.prefix = prefix + self.infix = infix + self.suffix = suffix + self.numprefix = numprefix + self.numinfix = numinfix + self.numsuffix = numsuffix + self.denomprefix = denomprefix + self.denominfix = denominfix + self.denomsuffix = denomsuffix + self.plus = plus + self.minus = minus + self.minuspos = minuspos + self.over = over + self.equaldenom = equaldenom + self.skip1 = skip1 + self.skipnum0 = skipnum0 + self.skipnum1 = skipnum1 + self.skipdenom1 = skipdenom1 + self.labelattrs = labelattrs + + def gcd(self, *n): + """returns the greates common divisor of all elements in n + - the elements of n must be non-negative integers + - return None if the number of elements is zero + - the greates common divisor is not affected when some + of the elements are zero, but it becomes zero when + all elements are zero""" + if len(n) == 2: + i, j = n + if i < j: + i, j = j, i + while j > 0: + i, (dummy, j) = j, divmod(i, j) + return i + if len(n): + res = n[0] + for i in n[1:]: + res = self.gcd(res, i) + return res + + def lcm(self, *n): + """returns the least common multiple of all elements in n + - the elements of n must be non-negative integers + - return None if the number of elements is zero + - the least common multiple is zero when some of the + elements are zero""" + if len(n): + res = n[0] + for i in n[1:]: + res = divmod(res * i, self.gcd(res, i))[0] + return res + + def labels(self, ticks): + labeledticks = [] + for tick in ticks: + if tick.label is None and tick.labellevel is not None: + labeledticks.append(tick) + tick.temp_rationalnum = tick.num + tick.temp_rationaldenom = tick.denom + tick.temp_rationalminus = 1 + if tick.temp_rationalnum < 0: + tick.temp_rationalminus = -tick.temp_rationalminus + tick.temp_rationalnum = -tick.temp_rationalnum + if tick.temp_rationaldenom < 0: + tick.temp_rationalminus = -tick.temp_rationalminus + tick.temp_rationaldenom = -tick.temp_rationaldenom + gcd = self.gcd(tick.temp_rationalnum, tick.temp_rationaldenom) + (tick.temp_rationalnum, dummy1), (tick.temp_rationaldenom, dummy2) = divmod(tick.temp_rationalnum, gcd), divmod(tick.temp_rationaldenom, gcd) + if self.equaldenom: + equaldenom = self.lcm(*[tick.temp_rationaldenom for tick in ticks if tick.label is None]) + if equaldenom is not None: + for tick in labeledticks: + factor, dummy = divmod(equaldenom, tick.temp_rationaldenom) + tick.temp_rationalnum, tick.temp_rationaldenom = factor * tick.temp_rationalnum, factor * tick.temp_rationaldenom + for tick in labeledticks: + rationalminus = rationalnumminus = rationaldenomminus = "" + if tick.temp_rationalminus == -1: + plusminus = self.minus + else: + plusminus = self.plus + if self.minuspos == 0: + rationalminus = plusminus + elif self.minuspos == 1: + rationalnumminus = plusminus + elif self.minuspos == -1: + rationaldenomminus = plusminus + else: + raise RuntimeError("invalid minuspos") + if self.skipnum0 and tick.temp_rationalnum == 0: + tick.label = "0" + elif (self.skip1 and self.skipdenom1 and tick.temp_rationalnum == 1 and tick.temp_rationaldenom == 1 and + (len(self.prefix) or len(self.infix) or len(self.suffix)) and + not len(rationalnumminus) and not len(self.numprefix) and not len(self.numinfix) and not len(self.numsuffix) and + not len(rationaldenomminus) and not len(self.denomprefix) and not len(self.denominfix) and not len(self.denomsuffix)): + tick.label = "%s%s%s%s" % (self.prefix, rationalminus, self.infix, self.suffix) + else: + if self.skipnum1 and tick.temp_rationalnum == 1 and (len(self.numprefix) or len(self.numinfix) or len(self.numsuffix)): + tick.temp_rationalnum = "%s%s%s%s" % (self.numprefix, rationalnumminus, self.numinfix, self.numsuffix) + else: + tick.temp_rationalnum = "%s%s%s%i%s" % (self.numprefix, rationalnumminus, self.numinfix, tick.temp_rationalnum, self.numsuffix) + if self.skipdenom1 and tick.temp_rationaldenom == 1 and not len(rationaldenomminus) and not len(self.denomprefix) and not len(self.denominfix) and not len(self.denomsuffix): + rational = tick.temp_rationalnum + else: + tick.temp_rationaldenom = "%s%s%s%i%s" % (self.denomprefix, rationaldenomminus, self.denominfix, tick.temp_rationaldenom, self.denomsuffix) + rational = self.over % (tick.temp_rationalnum, tick.temp_rationaldenom) + tick.label = "%s%s%s%s%s" % (self.prefix, rationalminus, self.infix, rational, self.suffix) + tick.labelattrs = tick.labelattrs + self.labelattrs + + # del tick.temp_rationalnum # we've inserted those temporary variables ... and do not care any longer about them + # del tick.temp_rationaldenom + # del tick.temp_rationalminus + diff --git a/compiler/gdsMill/pyx/graph/axis/tick.py b/compiler/gdsMill/pyx/graph/axis/tick.py new file mode 100644 index 00000000..dc59ac3f --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/tick.py @@ -0,0 +1,267 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2005 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +import sys + +# test automatic long conversion +try: + sys.maxint+1 + autolong = 1 +except OverflowError: + autolong = 0 + + +class rational: + """rational class performing some basic rational arithmetics + the axis partitioning uses rational arithmetics (with infinite accuracy) + basically it contains self.num and self.denom""" + + def initfromstring(self, s): + "converts a string 0.123 into a rational" + expparts = s.strip().replace("E", "e").split("e") + if len(expparts) > 2: + raise ValueError("multiple 'e' found in '%s'" % s) + commaparts = expparts[0].split(".") + if len(commaparts) > 2: + raise ValueError("multiple '.' found in '%s'" % expparts[0]) + if len(commaparts) == 1: + commaparts = [commaparts[0], ""] + self.num = 1 + if autolong: + self.denom = 10 ** len(commaparts[1]) + else: + self.denom = 10L ** len(commaparts[1]) + neg = len(commaparts[0]) and commaparts[0][0] == "-" + if neg: + commaparts[0] = commaparts[0][1:] + elif len(commaparts[0]) and commaparts[0][0] == "+": + commaparts[0] = commaparts[0][1:] + if len(commaparts[0]): + if not commaparts[0].isdigit(): + raise ValueError("unrecognized characters in '%s'" % s) + try: + x = int(commaparts[0]) + except: + x = long(commaparts[0]) + else: + x = 0 + if len(commaparts[1]): + if not commaparts[1].isdigit(): + raise ValueError("unrecognized characters in '%s'" % s) + try: + y = int(commaparts[1]) + except: + y = long(commaparts[1]) + else: + y = 0 + self.num = x*self.denom + y + if neg: + self.num = -self.num + if len(expparts) == 2: + neg = expparts[1][0] == "-" + if neg: + expparts[1] = expparts[1][1:] + elif expparts[1][0] == "+": + expparts[1] = expparts[1][1:] + if not expparts[1].isdigit(): + raise ValueError("unrecognized characters in '%s'" % s) + if neg: + if autolong: + self.denom *= 10 ** int(expparts[1]) + else: + self.denom *= 10L ** int(expparts[1]) + else: + if autolong: + self.num *= 10 ** int(expparts[1]) + else: + self.num *= 10L ** int(expparts[1]) + + def initfromfloat(self, x, floatprecision): + "converts a float into a rational with finite resolution" + if floatprecision < 0: + raise RuntimeError("float resolution must be non-negative") + self.initfromstring(("%%.%ig" % floatprecision) % x) + + def __init__(self, x, power=1, floatprecision=10): + """initializes a rational + - rational=(num/denom)**power + - x must be one of: + - a string (like "1.2", "1.2e3", "1.2/3.4", etc.) + - a float (converted using floatprecision) + - a sequence of two integers + - a rational instance""" + if power == 0: + self.num = 1 + self.denom = 1 + return + try: + # does x behave like a number + x + 0 + except: + try: + # does x behave like a string + x + "" + except: + try: + # x might be a tuple + self.num, self.denom = x + except: + # otherwise it should have a num and denom + self.num, self.denom = x.num, x.denom + else: + # x is a string + fraction = x.split("/") + if len(fraction) > 2: + raise ValueError("multiple '/' found in '%s'" % x) + self.initfromstring(fraction[0]) + if len(fraction) == 2: + self /= rational(fraction[1]) + else: + # x is a number + self.initfromfloat(x, floatprecision) + if not self.denom: raise ZeroDivisionError("zero denominator") + if power == -1: + self.num, self.denom = self.denom, self.num + elif power < -1: + if autolong: + self.num, self.denom = self.denom ** (-power), self.num ** (-power) + else: + self.num, self.denom = long(self.denom) ** (-power), long(self.num) ** (-power) + elif power > 1: + if autolong: + self.num = self.num ** power + self.denom = self.denom ** power + else: + self.num = long(self.num) ** power + self.denom = long(self.denom) ** power + + def __cmp__(self, other): + try: + return cmp(self.num * other.denom, other.num * self.denom) + except: + return cmp(float(self), other) + + def __abs__(self): + return rational((abs(self.num), abs(self.denom))) + + def __add__(self, other): + assert abs(other) < 1e-10 + return float(self) + + def __mul__(self, other): + return rational((self.num * other.num, self.denom * other.denom)) + + def __imul__(self, other): + self.num *= other.num + self.denom *= other.denom + return self + + def __div__(self, other): + return rational((self.num * other.denom, self.denom * other.num)) + + __truediv__ = __div__ + + def __idiv__(self, other): + self.num *= other.denom + self.denom *= other.num + return self + + def __float__(self): + "caution: avoid final precision of floats" + return float(self.num) / self.denom + + def __str__(self): + return "%i/%i" % (self.num, self.denom) + + +class tick(rational): + """tick class + a tick is a rational enhanced by + - self.ticklevel (0 = tick, 1 = subtick, etc.) + - self.labellevel (0 = label, 1 = sublabel, etc.) + - self.label (a string) and self.labelattrs (a list, defaults to []) + When ticklevel or labellevel is None, no tick or label is present at that value. + When label is None, it should be automatically created (and stored), once the + an axis painter needs it. Classes, which implement _Itexter do precisely that.""" + + def __init__(self, x, ticklevel=0, labellevel=0, label=None, labelattrs=[], **kwargs): + """initializes the instance + - see class description for the parameter description + - **kwargs are passed to the rational constructor""" + rational.__init__(self, x, **kwargs) + self.ticklevel = ticklevel + self.labellevel = labellevel + self.label = label + self.labelattrs = labelattrs + + def merge(self, other): + """merges two ticks together: + - the lower ticklevel/labellevel wins + - the ticks should be at the same position (otherwise it doesn't make sense) + -> this is NOT checked""" + if self.ticklevel is None or (other.ticklevel is not None and other.ticklevel < self.ticklevel): + self.ticklevel = other.ticklevel + if self.labellevel is None or (other.labellevel is not None and other.labellevel < self.labellevel): + self.labellevel = other.labellevel + if self.label is None: + self.label = other.label + + +def mergeticklists(list1, list2, mergeequal=1): + """helper function to merge tick lists + - return a merged list of ticks out of list1 and list2 + - CAUTION: original lists have to be ordered + (the returned list is also ordered)""" + # TODO: improve along the lines of http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305269 + + # do not destroy original lists + list1 = list1[:] + i = 0 + j = 0 + try: + while 1: # we keep on going until we reach an index error + while list2[j] < list1[i]: # insert tick + list1.insert(i, list2[j]) + i += 1 + j += 1 + if list2[j] == list1[i]: # merge tick + if mergeequal: + list1[i].merge(list2[j]) + j += 1 + i += 1 + except IndexError: + if j < len(list2): + list1 += list2[j:] + return list1 + + +def maxlevels(ticks): + "returns a tuple maxticklevel, maxlabellevel from a list of tick instances" + maxticklevel = maxlabellevel = 0 + for tick in ticks: + if tick.ticklevel is not None and tick.ticklevel >= maxticklevel: + maxticklevel = tick.ticklevel + 1 + if tick.labellevel is not None and tick.labellevel >= maxlabellevel: + maxlabellevel = tick.labellevel + 1 + return maxticklevel, maxlabellevel diff --git a/compiler/gdsMill/pyx/graph/axis/timeaxis.py b/compiler/gdsMill/pyx/graph/axis/timeaxis.py new file mode 100644 index 00000000..4b036fc2 --- /dev/null +++ b/compiler/gdsMill/pyx/graph/axis/timeaxis.py @@ -0,0 +1,60 @@ +import datetime +from pyx.graph import style +from pyx.graph.axis import axis, rater + +"""some experimental code for creating a time axis +- it needs python 2.3 to be used (it is based on the new datetime data type) +- a timeaxis is always based on the datetime data type (there is no distinction between times and dates) +""" + +class timeaxis(axis.linear): + "time axis mapping based " + + # TODO: how to deal with reversed timeaxis? + + def __init__(self, parter=None, rater=rater.linear(), **args): + axis._regularaxis.__init__(self, divisor=None, **args) + self.parter = parter + self.rater = rater + + def convert(self, data, x): + # XXX float division of timedelta instances + def mstimedelta(td): + "return the timedelta in microseconds" + return td.microseconds + 1000000*(td.seconds + 3600*24*td.days) + return mstimedelta(x - data.min) / float(mstimedelta(data.max - data.min)) + # we could store float(mstimedelta(self.dx)) instead of self.dx, but + # I prefer a different solution (not based on huge integers) for the + # future + + zero = datetime.timedelta(0) + + +class timetick(datetime.datetime): + + def __init__(self, year, month, day, ticklevel=0, labellevel=0, label=None, labelattrs=[], **kwargs): + datetime.datetime.__init__(self, year, month, day, **kwargs) + self.ticklevel = ticklevel + self.labellevel = labellevel + self.label = label + self.labelattrs = labelattrs[:] + + def merge(self, other): + if self.ticklevel is None or (other.ticklevel is not None and other.ticklevel < self.ticklevel): + self.ticklevel = other.ticklevel + if self.labellevel is None or (other.labellevel is not None and other.labellevel < self.labellevel): + self.labellevel = other.labellevel + + +class timetexter: + + def __init__(self, format="%c"): + self.format = format + + def labels(self, ticks): + for tick in ticks: + if tick.labellevel is not None and tick.label is None: + tick.label = tick.strftime(self.format) + + + diff --git a/compiler/gdsMill/pyx/graph/data.py b/compiler/gdsMill/pyx/graph/data.py new file mode 100644 index 00000000..9e525ba2 --- /dev/null +++ b/compiler/gdsMill/pyx/graph/data.py @@ -0,0 +1,612 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import nested_scopes + +import math, re, ConfigParser, struct, warnings +from pyx import text +from pyx.style import linestyle +from pyx.graph import style + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + +try: + dict() +except NameError: + # fallback implementation for Python 2.1 + def dict(items): + result = {} + for key, value in items: + result[key] = value + return result + + +def splitatvalue(value, *splitpoints): + section = 0 + while section < len(splitpoints) and splitpoints[section] < value: + section += 1 + if len(splitpoints) > 1: + if section % 2: + section = None + else: + section >>= 1 + return (section, value) + + +_mathglobals = {"neg": lambda x: -x, + "abs": lambda x: x < 0 and -x or x, + "sgn": lambda x: x < 0 and -1 or 1, + "sqrt": math.sqrt, + "exp": math.exp, + "log": math.log, + "sin": math.sin, + "cos": math.cos, + "tan": math.tan, + "asin": math.asin, + "acos": math.acos, + "atan": math.atan, + "sind": lambda x: math.sin(math.pi/180*x), + "cosd": lambda x: math.cos(math.pi/180*x), + "tand": lambda x: math.tan(math.pi/180*x), + "asind": lambda x: 180/math.pi*math.asin(x), + "acosd": lambda x: 180/math.pi*math.acos(x), + "atand": lambda x: 180/math.pi*math.atan(x), + "norm": lambda x, y: math.hypot(x, y), + "splitatvalue": splitatvalue, + "pi": math.pi, + "e": math.e} + + +class _data: + """graph data interface + + Graph data consists in columns, where each column might be identified by a + string or an integer. Each row in the resulting table refers to a data + point. + + All methods except for the constructor should consider self and its + attributes to be readonly, since the data instance might be shared between + several graphs simultaniously. + + The instance variable columns is a dictionary mapping column names to the + data of the column (i.e. to a list). Only static columns (known at + construction time) are contained in that dictionary. For data with numbered + columns the column data is also available via the list columndata. + Otherwise the columndata list should be missing and an access to a column + number will fail. + + The names of all columns (static and dynamic) must be fixed at the constructor + and stated in the columnnames dictionary. + + The instance variable title and defaultstyles contain the data title and + the default styles (a list of styles), respectively. + """ + + def dynamiccolumns(self, graph): + """create and return dynamic columns data + + Returns dynamic data matching the given axes (the axes range and other + data might be used). The return value is a dictionary similar to the + columns instance variable. + """ + return {} + + +defaultsymbols = [style.symbol()] +defaultlines = [style.line()] + + +class values(_data): + + defaultstyles = defaultsymbols + + def __init__(self, title="user provided values", **columns): + for i, values in enumerate(columns.values()): + if i and len(values) != l: + raise ValueError("different number of values") + else: + l = len(values) + self.columns = columns + self.columnnames = columns.keys() + self.title = title + + +class points(_data): + "Graph data from a list of points" + + defaultstyles = defaultsymbols + + def __init__(self, points, title="user provided points", addlinenumbers=1, **columns): + if len(points): + l = len(points[0]) + self.columndata = [[x] for x in points[0]] + for point in points[1:]: + if l != len(point): + raise ValueError("different number of columns per point") + for i, x in enumerate(point): + self.columndata[i].append(x) + for v in columns.values(): + if abs(v) > l or (not addlinenumbers and abs(v) == l): + raise ValueError("column number bigger than number of columns") + if addlinenumbers: + self.columndata = [range(1, len(points) + 1)] + self.columndata + self.columns = dict([(key, self.columndata[i]) for key, i in columns.items()]) + else: + self.columns = dict([(key, []) for key, i in columns]) + self.columnnames = self.columns.keys() + self.title = title + + +def list(*args, **kwargs): + warnings.warn("graph.data.list is deprecated. Use graph.data.points instead.") + return points(*args, **kwargs) + + +class _notitle: + pass + +_columnintref = re.compile(r"\$(-?\d+)", re.IGNORECASE) + +class data(_data): + "creates a new data set out of an existing data set" + + def __init__(self, data, title=_notitle, context={}, copy=1, + replacedollar=1, columncallback="__column__", **columns): + # build a nice title + if title is _notitle: + items = columns.items() + items.sort() # we want sorted items (otherwise they would be unpredictable scrambled) + self.title = "%s: %s" % (text.escapestring(data.title or "unkown source"), + ", ".join(["%s=%s" % (text.escapestring(key), + text.escapestring(str(value))) + for key, value in items])) + else: + self.title = title + + self.orgdata = data + self.defaultstyles = self.orgdata.defaultstyles + + # analyse the **columns argument + self.columns = {} + for columnname, value in columns.items(): + # search in the columns dictionary + try: + self.columns[columnname] = self.orgdata.columns[value] + except KeyError: + # search in the columndata list + try: + self.columns[columnname] = self.orgdata.columndata[value] + except (AttributeError, TypeError): + # value was not an valid column identifier + # i.e. take it as a mathematical expression + if replacedollar: + m = _columnintref.search(value) + while m: + value = "%s%s(%s)%s" % (value[:m.start()], columncallback, m.groups()[0], value[m.end():]) + m = _columnintref.search(value) + value = value.replace("$", columncallback) + expression = compile(value.strip(), __file__, "eval") + context = context.copy() + context[columncallback] = self.columncallback + if self.orgdata.columns: + key, columndata = self.orgdata.columns.items()[0] + count = len(columndata) + elif self.orgdata.columndata: + count = len(self.orgdata.columndata[0]) + else: + count = 0 + newdata = [] + for i in xrange(count): + self.columncallbackcount = i + for key, values in self.orgdata.columns.items(): + context[key] = values[i] + try: + newdata.append(eval(expression, _mathglobals, context)) + except (ArithmeticError, ValueError): + newdata.append(None) + self.columns[columnname] = newdata + + if copy: + # copy other, non-conflicting column names + for columnname, columndata in self.orgdata.columns.items(): + if not self.columns.has_key(columnname): + self.columns[columnname] = columndata + + self.columnnames = self.columns.keys() + + def columncallback(self, value): + try: + return self.orgdata.columndata[value][self.columncallbackcount] + except: + return self.orgdata.columns[value][self.columncallbackcount] + + +filecache = {} + +class file(data): + + defaultcommentpattern = re.compile(r"(#+|!+|%+)\s*") + defaultstringpattern = re.compile(r"\"(.*?)\"(\s+|$)") + defaultcolumnpattern = re.compile(r"(.*?)(\s+|$)") + + def splitline(self, line, stringpattern, columnpattern, tofloat=1): + """returns a tuple created out of the string line + - matches stringpattern and columnpattern, adds the first group of that + match to the result and and removes those matches until the line is empty + - when stringpattern matched, the result is always kept as a string + - when columnpattern matched and tofloat is true, a conversion to a float + is tried; when this conversion fails, the string is kept""" + result = [] + # try to gain speed by skip matching regular expressions + if line.find('"')!=-1 or \ + stringpattern is not self.defaultstringpattern or \ + columnpattern is not self.defaultcolumnpattern: + while len(line): + match = stringpattern.match(line) + if match: + result.append(match.groups()[0]) + line = line[match.end():] + else: + match = columnpattern.match(line) + if tofloat: + try: + result.append(float(match.groups()[0])) + except (TypeError, ValueError): + result.append(match.groups()[0]) + else: + result.append(match.groups()[0]) + line = line[match.end():] + else: + if tofloat: + try: + return map(float, line.split()) + except (TypeError, ValueError): + result = [] + for r in line.split(): + try: + result.append(float(r)) + except (TypeError, ValueError): + result.append(r) + else: + return line.split() + return result + + def getcachekey(self, *args): + return ":".join([str(x) for x in args]) + + def __init__(self, filename, + commentpattern=defaultcommentpattern, + stringpattern=defaultstringpattern, + columnpattern=defaultcolumnpattern, + skiphead=0, skiptail=0, every=1, + **kwargs): + + def readfile(file, title, self=self, commentpattern=commentpattern, stringpattern=stringpattern, columnpattern=columnpattern, skiphead=skiphead, skiptail=skiptail, every=every): + columns = [] + columndata = [] + linenumber = 0 + maxcolumns = 0 + for line in file.readlines(): + line = line.strip() + match = commentpattern.match(line) + if match: + if not len(columndata): + columns = self.splitline(line[match.end():], stringpattern, columnpattern, tofloat=0) + else: + linedata = [] + for value in self.splitline(line, stringpattern, columnpattern, tofloat=1): + linedata.append(value) + if len(linedata): + if linenumber >= skiphead and not ((linenumber - skiphead) % every): + linedata = [linenumber + 1] + linedata + if len(linedata) > maxcolumns: + maxcolumns = len(linedata) + columndata.append(linedata) + linenumber += 1 + if skiptail >= every: + skip, x = divmod(skiptail, every) + del columndata[-skip:] + for i in xrange(len(columndata)): + if len(columndata[i]) != maxcolumns: + columndata[i].extend([None]*(maxcolumns-len(columndata[i]))) + return points(columndata, title=title, addlinenumbers=0, + **dict([(column, i+1) for i, column in enumerate(columns[:maxcolumns-1])])) + + try: + filename.readlines + except: + # not a file-like object -> open it + cachekey = self.getcachekey(filename, commentpattern, stringpattern, columnpattern, skiphead, skiptail, every) + if not filecache.has_key(cachekey): + filecache[cachekey] = readfile(open(filename), filename) + data.__init__(self, filecache[cachekey], **kwargs) + else: + data.__init__(self, readfile(filename, "user provided file-like object"), **kwargs) + + +conffilecache = {} + +class conffile(data): + + def __init__(self, filename, **kwargs): + """read data from a config-like file + - filename is a string + - each row is defined by a section in the config-like file (see + config module description) + - the columns for each row are defined by lines in the section file; + the option entries identify and name the columns + - further keyword arguments are passed to the constructor of data, + keyword arguments data and titles excluded""" + + def readfile(file, title): + config = ConfigParser.ConfigParser() + config.optionxform = str + config.readfp(file) + sections = config.sections() + sections.sort() + columndata = [None]*len(sections) + maxcolumns = 1 + columns = {} + for i in xrange(len(sections)): + point = [sections[i]] + [None]*(maxcolumns-1) + for option in config.options(sections[i]): + value = config.get(sections[i], option) + try: + value = float(value) + except: + pass + try: + index = columns[option] + except KeyError: + columns[option] = maxcolumns + point.append(value) + maxcolumns += 1 + else: + point[index] = value + columndata[i] = point + # wrap result into a data instance to remove column numbers + result = data(points(columndata, addlinenumbers=0, **columns), title=title) + # ... but reinsert sections as linenumbers + result.columndata = [[x[0] for x in columndata]] + return result + + try: + filename.readlines + except: + # not a file-like object -> open it + if not filecache.has_key(filename): + filecache[filename] = readfile(open(filename), filename) + data.__init__(self, filecache[filename], **kwargs) + else: + data.__init__(self, readfile(filename, "user provided file-like object"), **kwargs) + + +cbdfilecache = {} + +class cbdfile(data): + + defaultstyles = defaultlines + + def getcachekey(self, *args): + return ":".join([str(x) for x in args]) + + def __init__(self, filename, minrank=None, maxrank=None, **kwargs): + + class cbdhead: + + def __init__(self, file): + (self.magic, + self.dictaddr, + self.segcount, + self.segsize, + self.segmax, + self.fill) = struct.unpack("<5i20s", file.read(40)) + if self.magic != 0x20770002: + raise ValueError("bad magic number") + + class segdict: + + def __init__(self, file, i): + self.index = i + (self.segid, + self.maxlat, + self.minlat, + self.maxlong, + self.minlong, + self.absaddr, + self.nbytes, + self.rank) = struct.unpack("<6i2h", file.read(28)) + + class segment: + + def __init__(self, file, sd): + file.seek(sd.absaddr) + (self.orgx, + self.orgy, + self.id, + self.nstrokes, + self.dummy) = struct.unpack("<3i2h", file.read(16)) + oln, olt = self.orgx, self.orgy + self.points = [(olt, oln)] + for i in range(self.nstrokes): + c1, c2 = struct.unpack("2c", file.read(2)) + if ord(c2) & 0x40: + if c1 > "\177": + dy = ord(c1) - 256 + else: + dy = ord(c1) + if c2 > "\177": + dx = ord(c2) - 256 + else: + dx = ord(c2) - 64 + else: + c3, c4, c5, c6, c7, c8 = struct.unpack("6c", file.read(6)) + if c2 > "\177": + c2 = chr(ord(c2) | 0x40) + dx, dy = struct.unpack("<2i", c3+c4+c1+c2+c7+c8+c5+c6) + oln += dx + olt += dy + self.points.append((olt, oln)) + sd.nstrokes = self.nstrokes + + def readfile(file, title): + h = cbdhead(file) + file.seek(h.dictaddr) + sds = [segdict(file, i+1) for i in range(h.segcount)] + sbs = [segment(file, sd) for sd in sds] + + # remove jumps at long +/- 180 + for sd, sb in zip(sds, sbs): + if sd.minlong < -150*3600 and sd.maxlong > 150*3600: + for i, (lat, long) in enumerate(sb.points): + if long < 0: + sb.points[i] = lat, long + 360*3600 + + columndata = [] + for sd, sb in zip(sds, sbs): + if ((minrank is None or sd.rank >= minrank) and + (maxrank is None or sd.rank <= maxrank)): + if columndata: + columndata.append((None, None)) + columndata.extend([(long/3600.0, lat/3600.0) + for lat, long in sb.points]) + + result = points(columndata, title=title) + result.defaultstyles = self.defaultstyles + return result + + + try: + filename.readlines + except: + # not a file-like object -> open it + cachekey = self.getcachekey(filename, minrank, maxrank) + if not cbdfilecache.has_key(cachekey): + cbdfilecache[cachekey] = readfile(open(filename, "rb"), filename) + data.__init__(self, cbdfilecache[cachekey], **kwargs) + else: + data.__init__(self, readfile(filename, "user provided file-like object"), **kwargs) + + +class function(_data): + + defaultstyles = defaultlines + + assignmentpattern = re.compile(r"\s*([a-z_][a-z0-9_]*)\s*\(\s*([a-z_][a-z0-9_]*)\s*\)\s*=", re.IGNORECASE) + + def __init__(self, expression, title=_notitle, min=None, max=None, + points=100, context={}): + + if title is _notitle: + self.title = expression + else: + self.title = title + self.min = min + self.max = max + self.numberofpoints = points + self.context = context.copy() # be safe on late evaluations + m = self.assignmentpattern.match(expression) + if m: + self.yname, self.xname = m.groups() + expression = expression[m.end():] + else: + raise ValueError("y(x)=... or similar expected") + if context.has_key(self.xname): + raise ValueError("xname in context") + self.expression = compile(expression.strip(), __file__, "eval") + self.columns = {} + self.columnnames = [self.xname, self.yname] + + def dynamiccolumns(self, graph): + dynamiccolumns = {self.xname: [], self.yname: []} + + xaxis = graph.axes[self.xname] + from pyx.graph.axis import logarithmic + logaxis = isinstance(xaxis.axis, logarithmic) + if self.min is not None: + min = self.min + else: + min = xaxis.data.min + if self.max is not None: + max = self.max + else: + max = xaxis.data.max + if logaxis: + min = math.log(min) + max = math.log(max) + for i in range(self.numberofpoints): + x = min + (max-min)*i / (self.numberofpoints-1.0) + if logaxis: + x = math.exp(x) + dynamiccolumns[self.xname].append(x) + self.context[self.xname] = x + try: + y = eval(self.expression, _mathglobals, self.context) + except (ArithmeticError, ValueError): + y = None + dynamiccolumns[self.yname].append(y) + return dynamiccolumns + + +class functionxy(function): + + def __init__(self, f, min=None, max=None, **kwargs): + function.__init__(self, "y(x)=f(x)", context={"f": f}, min=min, max=max, **kwargs) + + +class paramfunction(_data): + + defaultstyles = defaultlines + + def __init__(self, varname, min, max, expression, title=_notitle, points=100, context={}): + if context.has_key(varname): + raise ValueError("varname in context") + if title is _notitle: + self.title = expression + else: + self.title = title + varlist, expression = expression.split("=") + expression = compile(expression.strip(), __file__, "eval") + keys = [key.strip() for key in varlist.split(",")] + self.columns = dict([(key, []) for key in keys]) + context = context.copy() + for i in range(points): + param = min + (max-min)*i / (points-1.0) + context[varname] = param + values = eval(expression, _mathglobals, context) + for key, value in zip(keys, values): + self.columns[key].append(value) + if len(keys) != len(values): + raise ValueError("unpack tuple of wrong size") + self.columnnames = self.columns.keys() + + +class paramfunctionxy(paramfunction): + + def __init__(self, f, min, max, **kwargs): + paramfunction.__init__(self, "t", min, max, "x, y = f(t)", context={"f": f}, **kwargs) diff --git a/compiler/gdsMill/pyx/graph/graph.py b/compiler/gdsMill/pyx/graph/graph.py new file mode 100644 index 00000000..8b4a319e --- /dev/null +++ b/compiler/gdsMill/pyx/graph/graph.py @@ -0,0 +1,893 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +from __future__ import nested_scopes + +import math, re, string, warnings +from pyx import canvas, path, trafo, unit +from pyx.graph import style +from pyx.graph.axis import axis, positioner + + +goldenmean = 0.5 * (math.sqrt(5) + 1) + + +class styledata: + """style data storage class + + Instances of this class are used to store data from the styles + and to pass point data to the styles by instances named privatedata + and sharedata. sharedata is shared between all the style(s) in use + by a data instance, while privatedata is private to each style and + used as a storage place instead of self to prevent side effects when + using a style several times.""" + pass + + +class plotitem: + + def __init__(self, graph, data, styles): + self.data = data + self.title = data.title + + addstyles = [None] + while addstyles: + # add styles to ensure all needs of the given styles + provided = [] # already provided sharedata variables + addstyles = [] # a list of style instances to be added in front + for s in styles: + for n in s.needsdata: + if n not in provided: + defaultprovider = style.getdefaultprovider(n) + addstyles.append(defaultprovider) + provided.extend(defaultprovider.providesdata) + provided.extend(s.providesdata) + styles = addstyles + styles + + self.styles = styles + self.sharedata = styledata() + self.privatedatalist = [styledata() for s in self.styles] + + # perform setcolumns to all styles + self.usedcolumnnames = [] + for privatedata, s in zip(self.privatedatalist, self.styles): + self.usedcolumnnames.extend(s.columnnames(privatedata, self.sharedata, graph, self.data.columnnames)) + + def selectstyles(self, graph, selectindex, selecttotal): + for privatedata, style in zip(self.privatedatalist, self.styles): + style.selectstyle(privatedata, self.sharedata, graph, selectindex, selecttotal) + + def adjustaxesstatic(self, graph): + for columnname, data in self.data.columns.items(): + for privatedata, style in zip(self.privatedatalist, self.styles): + style.adjustaxis(privatedata, self.sharedata, graph, columnname, data) + + def makedynamicdata(self, graph): + self.dynamiccolumns = self.data.dynamiccolumns(graph) + + def adjustaxesdynamic(self, graph): + for columnname, data in self.dynamiccolumns.items(): + for privatedata, style in zip(self.privatedatalist, self.styles): + style.adjustaxis(privatedata, self.sharedata, graph, columnname, data) + + def draw(self, graph): + for privatedata, style in zip(self.privatedatalist, self.styles): + style.initdrawpoints(privatedata, self.sharedata, graph) + point = {} + useitems = [] + for columnname in self.usedcolumnnames: + try: + useitems.append((columnname, self.dynamiccolumns[columnname])) + except KeyError: + useitems.append((columnname, self.data.columns[columnname])) + if not useitems: + raise ValueError("cannot draw empty data") + for i in xrange(len(useitems[0][1])): + for columnname, data in useitems: + point[columnname] = data[i] + for privatedata, style in zip(self.privatedatalist, self.styles): + style.drawpoint(privatedata, self.sharedata, graph, point) + for privatedata, style in zip(self.privatedatalist, self.styles): + style.donedrawpoints(privatedata, self.sharedata, graph) + + def key_pt(self, graph, x_pt, y_pt, width_pt, height_pt): + for privatedata, style in zip(self.privatedatalist, self.styles): + style.key_pt(privatedata, self.sharedata, graph, x_pt, y_pt, width_pt, height_pt) + + def __getattr__(self, attr): + # read only access to the styles privatedata + stylesdata = [getattr(styledata, attr) + for styledata in self.privatedatalist + if hasattr(styledata, attr)] + if len(stylesdata) > 1: + return stylesdata + elif len(stylesdata) == 1: + return stylesdata[0] + raise AttributeError("access to styledata attribute '%s' failed" % attr) + + +class graph(canvas.canvas): + + def __init__(self): + canvas.canvas.__init__(self) + self.axes = {} + self.plotitems = [] + self._calls = {} + self.didranges = 0 + self.didstyles = 0 + + def did(self, method, *args, **kwargs): + if not self._calls.has_key(method): + self._calls[method] = [] + for callargs in self._calls[method]: + if callargs == (args, kwargs): + return 1 + self._calls[method].append((args, kwargs)) + return 0 + + def bbox(self): + self.finish() + return canvas.canvas.bbox(self) + + def registerPS(self, registry): + self.finish() + canvas.canvas.registerPS(self, registry) + + def registerPDF(self, registry): + self.finish() + canvas.canvas.registerPDF(self, registry) + + def processPS(self, file, writer, context, registry, bbox): + self.finish() + canvas.canvas.processPS(self, file, writer, context, registry, bbox) + + def processPDF(self, file, writer, context, registry, bbox): + self.finish() + canvas.canvas.processPDF(self, file, writer, context, registry, bbox) + + def plot(self, data, styles=None, rangewarning=1): + if self.didranges and rangewarning: + warnings.warn("axes ranges have already been analysed; no further adjustments will be performed") + if self.didstyles: + raise RuntimeError("can't plot further data after dostyles() has been executed") + singledata = 0 + try: + for d in data: + pass + except: + usedata = [data] + singledata = 1 + else: + usedata = data + if styles is None: + for d in usedata: + if styles is None: + styles = d.defaultstyles + elif styles != d.defaultstyles: + raise RuntimeError("defaultstyles differ") + plotitems = [] + for d in usedata: + plotitems.append(plotitem(self, d, styles)) + self.plotitems.extend(plotitems) + if self.didranges: + for aplotitem in plotitems: + aplotitem.makedynamicdata(self) + if singledata: + return plotitems[0] + else: + return plotitems + + def doranges(self): + if self.did(self.doranges): + return + for plotitem in self.plotitems: + plotitem.adjustaxesstatic(self) + for plotitem in self.plotitems: + plotitem.makedynamicdata(self) + for plotitem in self.plotitems: + plotitem.adjustaxesdynamic(self) + self.didranges = 1 + + def doaxiscreate(self, axisname): + if self.did(self.doaxiscreate, axisname): + return + self.doaxispositioner(axisname) + self.axes[axisname].create() + + def dolayout(self): + raise NotImplementedError + + def dobackground(self): + pass + + def doaxes(self): + raise NotImplementedError + + def dostyles(self): + if self.did(self.dostyles): + return + self.dolayout() + self.dobackground() + + # count the usage of styles and perform selects + styletotal = {} + def stylesid(styles): + return ":".join([str(id(style)) for style in styles]) + for plotitem in self.plotitems: + try: + styletotal[stylesid(plotitem.styles)] += 1 + except: + styletotal[stylesid(plotitem.styles)] = 1 + styleindex = {} + for plotitem in self.plotitems: + try: + styleindex[stylesid(plotitem.styles)] += 1 + except: + styleindex[stylesid(plotitem.styles)] = 0 + plotitem.selectstyles(self, styleindex[stylesid(plotitem.styles)], + styletotal[stylesid(plotitem.styles)]) + + self.didstyles = 1 + + def doplot(self, plotitem): + if self.did(self.doplot, plotitem): + return + self.dostyles() + plotitem.draw(self) + + def dodata(self): + for plotitem in self.plotitems: + self.doplot(plotitem) + + def dokey(self): + raise NotImplementedError + + def finish(self): + self.dobackground() + self.doaxes() + self.dodata() + self.dokey() + + +class graphxy(graph): + + def __init__(self, xpos=0, ypos=0, width=None, height=None, ratio=goldenmean, + key=None, backgroundattrs=None, axesdist=0.8*unit.v_cm, + xaxisat=None, yaxisat=None, **axes): + graph.__init__(self) + + self.xpos = xpos + self.ypos = ypos + self.xpos_pt = unit.topt(self.xpos) + self.ypos_pt = unit.topt(self.ypos) + self.xaxisat = xaxisat + self.yaxisat = yaxisat + self.key = key + self.backgroundattrs = backgroundattrs + self.axesdist_pt = unit.topt(axesdist) + + self.width = width + self.height = height + if width is None: + if height is None: + raise ValueError("specify width and/or height") + else: + self.width = ratio * self.height + elif height is None: + self.height = (1.0/ratio) * self.width + self.width_pt = unit.topt(self.width) + self.height_pt = unit.topt(self.height) + + for axisname, aaxis in axes.items(): + if aaxis is not None: + if not isinstance(aaxis, axis.linkedaxis): + self.axes[axisname] = axis.anchoredaxis(aaxis, self.texrunner, axisname) + else: + self.axes[axisname] = aaxis + for axisname, axisat in [("x", xaxisat), ("y", yaxisat)]: + okey = axisname + "2" + if not axes.has_key(axisname): + if not axes.has_key(okey) or axes[okey] is None: + self.axes[axisname] = axis.anchoredaxis(axis.linear(), self.texrunner, axisname) + if not axes.has_key(okey): + self.axes[okey] = axis.linkedaxis(self.axes[axisname], okey) + else: + self.axes[axisname] = axis.linkedaxis(self.axes[okey], axisname) + elif not axes.has_key(okey) and axisat is None: + self.axes[okey] = axis.linkedaxis(self.axes[axisname], okey) + + if self.axes.has_key("x"): + self.xbasepath = self.axes["x"].basepath + self.xvbasepath = self.axes["x"].vbasepath + self.xgridpath = self.axes["x"].gridpath + self.xtickpoint_pt = self.axes["x"].tickpoint_pt + self.xtickpoint = self.axes["x"].tickpoint + self.xvtickpoint_pt = self.axes["x"].vtickpoint_pt + self.xvtickpoint = self.axes["x"].tickpoint + self.xtickdirection = self.axes["x"].tickdirection + self.xvtickdirection = self.axes["x"].vtickdirection + + if self.axes.has_key("y"): + self.ybasepath = self.axes["y"].basepath + self.yvbasepath = self.axes["y"].vbasepath + self.ygridpath = self.axes["y"].gridpath + self.ytickpoint_pt = self.axes["y"].tickpoint_pt + self.ytickpoint = self.axes["y"].tickpoint + self.yvtickpoint_pt = self.axes["y"].vtickpoint_pt + self.yvtickpoint = self.axes["y"].vtickpoint + self.ytickdirection = self.axes["y"].tickdirection + self.yvtickdirection = self.axes["y"].vtickdirection + + self.axesnames = ([], []) + for axisname, aaxis in self.axes.items(): + if axisname[0] not in "xy" or (len(axisname) != 1 and (not axisname[1:].isdigit() or + axisname[1:] == "1")): + raise ValueError("invalid axis name") + if axisname[0] == "x": + self.axesnames[0].append(axisname) + else: + self.axesnames[1].append(axisname) + aaxis.setcreatecall(self.doaxiscreate, axisname) + + + def pos_pt(self, x, y, xaxis=None, yaxis=None): + if xaxis is None: + xaxis = self.axes["x"] + if yaxis is None: + yaxis = self.axes["y"] + return (self.xpos_pt + xaxis.convert(x)*self.width_pt, + self.ypos_pt + yaxis.convert(y)*self.height_pt) + + def pos(self, x, y, xaxis=None, yaxis=None): + if xaxis is None: + xaxis = self.axes["x"] + if yaxis is None: + yaxis = self.axes["y"] + return (self.xpos + xaxis.convert(x)*self.width, + self.ypos + yaxis.convert(y)*self.height) + + def vpos_pt(self, vx, vy): + return (self.xpos_pt + vx*self.width_pt, + self.ypos_pt + vy*self.height_pt) + + def vpos(self, vx, vy): + return (self.xpos + vx*self.width, + self.ypos + vy*self.height) + + def vzindex(self, vx, vy): + return 0 + + def vangle(self, vx1, vy1, vx2, vy2, vx3, vy3): + return 1 + + def vgeodesic(self, vx1, vy1, vx2, vy2): + """returns a geodesic path between two points in graph coordinates""" + return path.line_pt(self.xpos_pt + vx1*self.width_pt, + self.ypos_pt + vy1*self.height_pt, + self.xpos_pt + vx2*self.width_pt, + self.ypos_pt + vy2*self.height_pt) + + def vgeodesic_el(self, vx1, vy1, vx2, vy2): + """returns a geodesic path element between two points in graph coordinates""" + return path.lineto_pt(self.xpos_pt + vx2*self.width_pt, + self.ypos_pt + vy2*self.height_pt) + + def vcap_pt(self, coordinate, length_pt, vx, vy): + """returns an error cap path for a given coordinate, lengths and + point in graph coordinates""" + if coordinate == 0: + return path.line_pt(self.xpos_pt + vx*self.width_pt - 0.5*length_pt, + self.ypos_pt + vy*self.height_pt, + self.xpos_pt + vx*self.width_pt + 0.5*length_pt, + self.ypos_pt + vy*self.height_pt) + elif coordinate == 1: + return path.line_pt(self.xpos_pt + vx*self.width_pt, + self.ypos_pt + vy*self.height_pt - 0.5*length_pt, + self.xpos_pt + vx*self.width_pt, + self.ypos_pt + vy*self.height_pt + 0.5*length_pt) + else: + raise ValueError("direction invalid") + + def xvgridpath(self, vx): + return path.line_pt(self.xpos_pt + vx*self.width_pt, self.ypos_pt, + self.xpos_pt + vx*self.width_pt, self.ypos_pt + self.height_pt) + + def yvgridpath(self, vy): + return path.line_pt(self.xpos_pt, self.ypos_pt + vy*self.height_pt, + self.xpos_pt + self.width_pt, self.ypos_pt + vy*self.height_pt) + + def axistrafo(self, axis, t): + c = canvas.canvas([t]) + c.insert(axis.canvas) + axis.canvas = c + + def axisatv(self, axis, v): + if axis.positioner.fixtickdirection[0]: + # it is a y-axis + self.axistrafo(axis, trafo.translate_pt(self.xpos_pt + v*self.width_pt - axis.positioner.x1_pt, 0)) + else: + # it is an x-axis + self.axistrafo(axis, trafo.translate_pt(0, self.ypos_pt + v*self.height_pt - axis.positioner.y1_pt)) + + def doaxispositioner(self, axisname): + if self.did(self.doaxispositioner, axisname): + return + self.doranges() + if axisname == "x": + self.axes["x"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt, + self.xpos_pt + self.width_pt, self.ypos_pt, + (0, 1), self.xvgridpath)) + elif axisname == "x2": + self.axes["x2"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt + self.height_pt, + self.xpos_pt + self.width_pt, self.ypos_pt + self.height_pt, + (0, -1), self.xvgridpath)) + elif axisname == "y": + self.axes["y"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, self.ypos_pt, + self.xpos_pt, self.ypos_pt + self.height_pt, + (1, 0), self.yvgridpath)) + elif axisname == "y2": + self.axes["y2"].setpositioner(positioner.lineaxispos_pt(self.xpos_pt + self.width_pt, self.ypos_pt, + self.xpos_pt + self.width_pt, self.ypos_pt + self.height_pt, + (-1, 0), self.yvgridpath)) + else: + if axisname[1:] == "3": + dependsonaxisname = axisname[0] + else: + dependsonaxisname = "%s%d" % (axisname[0], int(axisname[1:]) - 2) + self.doaxiscreate(dependsonaxisname) + sign = 2*(int(axisname[1:]) % 2) - 1 + if axisname[0] == "x": + y_pt = self.axes[dependsonaxisname].positioner.y1_pt - sign * (self.axes[dependsonaxisname].canvas.extent_pt + self.axesdist_pt) + self.axes[axisname].setpositioner(positioner.lineaxispos_pt(self.xpos_pt, y_pt, + self.xpos_pt + self.width_pt, y_pt, + (0, sign), self.xvgridpath)) + else: + x_pt = self.axes[dependsonaxisname].positioner.x1_pt - sign * (self.axes[dependsonaxisname].canvas.extent_pt + self.axesdist_pt) + self.axes[axisname].setpositioner(positioner.lineaxispos_pt(x_pt, self.ypos_pt, + x_pt, self.ypos_pt + self.height_pt, + (sign, 0), self.yvgridpath)) + + def dolayout(self): + if self.did(self.dolayout): + return + for axisname in self.axes.keys(): + self.doaxiscreate(axisname) + if self.xaxisat is not None: + self.axisatv(self.axes["x"], self.axes["y"].convert(self.xaxisat)) + if self.yaxisat is not None: + self.axisatv(self.axes["y"], self.axes["x"].convert(self.yaxisat)) + + def dobackground(self): + if self.did(self.dobackground): + return + if self.backgroundattrs is not None: + self.draw(path.rect_pt(self.xpos_pt, self.ypos_pt, self.width_pt, self.height_pt), + self.backgroundattrs) + + def doaxes(self): + if self.did(self.doaxes): + return + self.dolayout() + self.dobackground() + for axis in self.axes.values(): + self.insert(axis.canvas) + + def dokey(self): + if self.did(self.dokey): + return + self.dobackground() + self.dostyles() + if self.key is not None: + c = self.key.paint(self.plotitems) + bbox = c.bbox() + def parentchildalign(pmin, pmax, cmin, cmax, pos, dist, inside): + ppos = pmin+0.5*(cmax-cmin)+dist+pos*(pmax-pmin-cmax+cmin-2*dist) + cpos = 0.5*(cmin+cmax)+(1-inside)*(1-2*pos)*(cmax-cmin+2*dist) + return ppos-cpos + if bbox: + x = parentchildalign(self.xpos_pt, self.xpos_pt+self.width_pt, + bbox.llx_pt, bbox.urx_pt, + self.key.hpos, unit.topt(self.key.hdist), self.key.hinside) + y = parentchildalign(self.ypos_pt, self.ypos_pt+self.height_pt, + bbox.lly_pt, bbox.ury_pt, + self.key.vpos, unit.topt(self.key.vdist), self.key.vinside) + self.insert(c, [trafo.translate_pt(x, y)]) + + +class graphxyz(graphxy): + + class central: + + def __init__(self, distance, phi, theta, anglefactor=math.pi/180): + phi *= anglefactor + theta *= anglefactor + self.distance = distance + + self.a = (-math.sin(phi), math.cos(phi), 0) + self.b = (-math.cos(phi)*math.sin(theta), + -math.sin(phi)*math.sin(theta), + math.cos(theta)) + self.eye = (distance*math.cos(phi)*math.cos(theta), + distance*math.sin(phi)*math.cos(theta), + distance*math.sin(theta)) + + def point(self, x, y, z): + d0 = (self.a[0]*self.b[1]*(z-self.eye[2]) + + self.a[2]*self.b[0]*(y-self.eye[1]) + + self.a[1]*self.b[2]*(x-self.eye[0]) + - self.a[2]*self.b[1]*(x-self.eye[0]) + - self.a[0]*self.b[2]*(y-self.eye[1]) + - self.a[1]*self.b[0]*(z-self.eye[2])) + da = (self.eye[0]*self.b[1]*(z-self.eye[2]) + + self.eye[2]*self.b[0]*(y-self.eye[1]) + + self.eye[1]*self.b[2]*(x-self.eye[0]) + - self.eye[2]*self.b[1]*(x-self.eye[0]) + - self.eye[0]*self.b[2]*(y-self.eye[1]) + - self.eye[1]*self.b[0]*(z-self.eye[2])) + db = (self.a[0]*self.eye[1]*(z-self.eye[2]) + + self.a[2]*self.eye[0]*(y-self.eye[1]) + + self.a[1]*self.eye[2]*(x-self.eye[0]) + - self.a[2]*self.eye[1]*(x-self.eye[0]) + - self.a[0]*self.eye[2]*(y-self.eye[1]) + - self.a[1]*self.eye[0]*(z-self.eye[2])) + return da/d0, db/d0 + + def zindex(self, x, y, z): + return math.sqrt((x-self.eye[0])*(x-self.eye[0])+(y-self.eye[1])*(y-self.eye[1])+(z-self.eye[2])*(z-self.eye[2]))-self.distance + + def angle(self, x1, y1, z1, x2, y2, z2, x3, y3, z3): + sx = (x1-self.eye[0]) + sy = (y1-self.eye[1]) + sz = (z1-self.eye[2]) + nx = (y2-y1)*(z3-z1)-(z2-z1)*(y3-y1) + ny = (z2-z1)*(x3-x1)-(x2-x1)*(z3-z1) + nz = (x2-x1)*(y3-y1)-(y2-y1)*(x3-x1) + return (sx*nx+sy*ny+sz*nz)/math.sqrt(nx*nx+ny*ny+nz*nz)/math.sqrt(sx*sx+sy*sy+sz*sz) + + + class parallel: + + def __init__(self, phi, theta, anglefactor=math.pi/180): + phi *= anglefactor + theta *= anglefactor + + self.a = (-math.sin(phi), math.cos(phi), 0) + self.b = (-math.cos(phi)*math.sin(theta), + -math.sin(phi)*math.sin(theta), + math.cos(theta)) + self.c = (-math.cos(phi)*math.cos(theta), + -math.sin(phi)*math.cos(theta), + -math.sin(theta)) + + def point(self, x, y, z): + return self.a[0]*x+self.a[1]*y+self.a[2]*z, self.b[0]*x+self.b[1]*y+self.b[2]*z + + def zindex(self, x, y, z): + return self.c[0]*x+self.c[1]*y+self.c[2]*z + + def angle(self, x1, y1, z1, x2, y2, z2, x3, y3, z3): + nx = (y2-y1)*(z3-z1)-(z2-z1)*(y3-y1) + ny = (z2-z1)*(x3-x1)-(x2-x1)*(z3-z1) + nz = (x2-x1)*(y3-y1)-(y2-y1)*(x3-x1) + return (self.c[0]*nx+self.c[1]*ny+self.c[2]*nz)/math.sqrt(nx*nx+ny*ny+nz*nz) + + + def __init__(self, xpos=0, ypos=0, size=None, + xscale=1, yscale=1, zscale=1/goldenmean, + projector=central(10, -30, 30), key=None, + **axes): + graph.__init__(self) + + self.xpos = xpos + self.ypos = ypos + self.size = size + self.xpos_pt = unit.topt(xpos) + self.ypos_pt = unit.topt(ypos) + self.size_pt = unit.topt(size) + self.xscale = xscale + self.yscale = yscale + self.zscale = zscale + self.projector = projector + self.key = key + + self.xorder = projector.zindex(0, -1, 0) > projector.zindex(0, 1, 0) and 1 or 0 + self.yorder = projector.zindex(-1, 0, 0) > projector.zindex(1, 0, 0) and 1 or 0 + self.zindexscale = math.sqrt(xscale*xscale+yscale*yscale+zscale*zscale) + + for axisname, aaxis in axes.items(): + if aaxis is not None: + if not isinstance(aaxis, axis.linkedaxis): + self.axes[axisname] = axis.anchoredaxis(aaxis, self.texrunner, axisname) + else: + self.axes[axisname] = aaxis + for axisname in ["x", "y"]: + okey = axisname + "2" + if not axes.has_key(axisname): + if not axes.has_key(okey) or axes[okey] is None: + self.axes[axisname] = axis.anchoredaxis(axis.linear(), self.texrunner, axisname) + if not axes.has_key(okey): + self.axes[okey] = axis.linkedaxis(self.axes[axisname], okey) + else: + self.axes[axisname] = axis.linkedaxis(self.axes[okey], axisname) + if not axes.has_key("z"): + self.axes["z"] = axis.anchoredaxis(axis.linear(), self.texrunner, axisname) + + if self.axes.has_key("x"): + self.xbasepath = self.axes["x"].basepath + self.xvbasepath = self.axes["x"].vbasepath + self.xgridpath = self.axes["x"].gridpath + self.xtickpoint_pt = self.axes["x"].tickpoint_pt + self.xtickpoint = self.axes["x"].tickpoint + self.xvtickpoint_pt = self.axes["x"].vtickpoint_pt + self.xvtickpoint = self.axes["x"].tickpoint + self.xtickdirection = self.axes["x"].tickdirection + self.xvtickdirection = self.axes["x"].vtickdirection + + if self.axes.has_key("y"): + self.ybasepath = self.axes["y"].basepath + self.yvbasepath = self.axes["y"].vbasepath + self.ygridpath = self.axes["y"].gridpath + self.ytickpoint_pt = self.axes["y"].tickpoint_pt + self.ytickpoint = self.axes["y"].tickpoint + self.yvtickpoint_pt = self.axes["y"].vtickpoint_pt + self.yvtickpoint = self.axes["y"].vtickpoint + self.ytickdirection = self.axes["y"].tickdirection + self.yvtickdirection = self.axes["y"].vtickdirection + + if self.axes.has_key("z"): + self.zbasepath = self.axes["z"].basepath + self.zvbasepath = self.axes["z"].vbasepath + self.zgridpath = self.axes["z"].gridpath + self.ztickpoint_pt = self.axes["z"].tickpoint_pt + self.ztickpoint = self.axes["z"].tickpoint + self.zvtickpoint_pt = self.axes["z"].vtickpoint + self.zvtickpoint = self.axes["z"].vtickpoint + self.ztickdirection = self.axes["z"].tickdirection + self.zvtickdirection = self.axes["z"].vtickdirection + + self.axesnames = ([], [], []) + for axisname, aaxis in self.axes.items(): + if axisname[0] not in "xyz" or (len(axisname) != 1 and (not axisname[1:].isdigit() or + axisname[1:] == "1")): + raise ValueError("invalid axis name") + if axisname[0] == "x": + self.axesnames[0].append(axisname) + elif axisname[0] == "y": + self.axesnames[1].append(axisname) + else: + self.axesnames[2].append(axisname) + aaxis.setcreatecall(self.doaxiscreate, axisname) + + def pos_pt(self, x, y, z, xaxis=None, yaxis=None, zaxis=None): + if xaxis is None: + xaxis = self.axes["x"] + if yaxis is None: + yaxis = self.axes["y"] + if zaxis is None: + zaxis = self.axes["z"] + return self.vpos_pt(xaxis.convert(x), yaxis.convert(y), zaxis.convert(y)) + + def pos(self, x, y, z, xaxis=None, yaxis=None, zaxis=None): + if xaxis is None: + xaxis = self.axes["x"] + if yaxis is None: + yaxis = self.axes["y"] + if zaxis is None: + zaxis = self.axes["z"] + return self.vpos(xaxis.convert(x), yaxis.convert(y), zaxis.convert(y)) + + def vpos_pt(self, vx, vy, vz): + x, y = self.projector.point(2*self.xscale*(vx - 0.5), + 2*self.yscale*(vy - 0.5), + 2*self.zscale*(vz - 0.5)) + return self.xpos_pt+x*self.size_pt, self.ypos_pt+y*self.size_pt + + def vpos(self, vx, vy, vz): + x, y = self.projector.point(2*self.xscale*(vx - 0.5), + 2*self.yscale*(vy - 0.5), + 2*self.zscale*(vz - 0.5)) + return self.xpos+x*self.size, self.ypos+y*self.size + + def vzindex(self, vx, vy, vz): + return self.projector.zindex(2*self.xscale*(vx - 0.5), + 2*self.yscale*(vy - 0.5), + 2*self.zscale*(vz - 0.5))/self.zindexscale + + def vangle(self, vx1, vy1, vz1, vx2, vy2, vz2, vx3, vy3, vz3): + return self.projector.angle(2*self.xscale*(vx1 - 0.5), + 2*self.yscale*(vy1 - 0.5), + 2*self.zscale*(vz1 - 0.5), + 2*self.xscale*(vx2 - 0.5), + 2*self.yscale*(vy2 - 0.5), + 2*self.zscale*(vz2 - 0.5), + 2*self.xscale*(vx3 - 0.5), + 2*self.yscale*(vy3 - 0.5), + 2*self.zscale*(vz3 - 0.5)) + + def vgeodesic(self, vx1, vy1, vz1, vx2, vy2, vz2): + """returns a geodesic path between two points in graph coordinates""" + return path.line_pt(*(self.vpos_pt(vx1, vy1, vz1) + self.vpos_pt(vx2, vy2, vz2))) + + def vgeodesic_el(self, vx1, vy1, vz1, vx2, vy2, vz2): + """returns a geodesic path element between two points in graph coordinates""" + return path.lineto_pt(*(self.vpos_pt(vx1, vy1, vz1) + self.vpos_pt(vx2, vy2, vz2))) + + def vcap_pt(self, coordinate, length_pt, vx, vy, vz): + """returns an error cap path for a given coordinate, lengths and + point in graph coordinates""" + if coordinate == 0: + return self.vgeodesic(vx-0.5*length_pt/self.size_pt, vy, vz, vx+0.5*length_pt/self.size_pt, vy, vz) + elif coordinate == 1: + return self.vgeodesic(vx, vy-0.5*length_pt/self.size_pt, vz, vx, vy+0.5*length_pt/self.size_pt, vz) + elif coordinate == 2: + return self.vgeodesic(vx, vy, vz-0.5*length_pt/self.size_pt, vx, vy, vz+0.5*length_pt/self.size_pt) + else: + raise ValueError("direction invalid") + + def xvtickdirection(self, vx): + if self.xorder: + x1_pt, y1_pt = self.vpos_pt(vx, 1, 0) + x2_pt, y2_pt = self.vpos_pt(vx, 0, 0) + else: + x1_pt, y1_pt = self.vpos_pt(vx, 0, 0) + x2_pt, y2_pt = self.vpos_pt(vx, 1, 0) + dx_pt = x2_pt - x1_pt + dy_pt = y2_pt - y1_pt + norm = math.hypot(dx_pt, dy_pt) + return dx_pt/norm, dy_pt/norm + + def yvtickdirection(self, vy): + if self.yorder: + x1_pt, y1_pt = self.vpos_pt(1, vy, 0) + x2_pt, y2_pt = self.vpos_pt(0, vy, 0) + else: + x1_pt, y1_pt = self.vpos_pt(0, vy, 0) + x2_pt, y2_pt = self.vpos_pt(1, vy, 0) + dx_pt = x2_pt - x1_pt + dy_pt = y2_pt - y1_pt + norm = math.hypot(dx_pt, dy_pt) + return dx_pt/norm, dy_pt/norm + + def vtickdirection(self, vx1, vy1, vz1, vx2, vy2, vz2): + x1_pt, y1_pt = self.vpos_pt(vx1, vy1, vz1) + x2_pt, y2_pt = self.vpos_pt(vx2, vy2, vz2) + dx_pt = x2_pt - x1_pt + dy_pt = y2_pt - y1_pt + norm = math.hypot(dx_pt, dy_pt) + return dx_pt/norm, dy_pt/norm + + def xvgridpath(self, vx): + return path.path(path.moveto_pt(*self.vpos_pt(vx, 0, 0)), + path.lineto_pt(*self.vpos_pt(vx, 1, 0)), + path.lineto_pt(*self.vpos_pt(vx, 1, 1)), + path.lineto_pt(*self.vpos_pt(vx, 0, 1)), + path.closepath()) + + def yvgridpath(self, vy): + return path.path(path.moveto_pt(*self.vpos_pt(0, vy, 0)), + path.lineto_pt(*self.vpos_pt(1, vy, 0)), + path.lineto_pt(*self.vpos_pt(1, vy, 1)), + path.lineto_pt(*self.vpos_pt(0, vy, 1)), + path.closepath()) + + def zvgridpath(self, vz): + return path.path(path.moveto_pt(*self.vpos_pt(0, 0, vz)), + path.lineto_pt(*self.vpos_pt(1, 0, vz)), + path.lineto_pt(*self.vpos_pt(1, 1, vz)), + path.lineto_pt(*self.vpos_pt(0, 1, vz)), + path.closepath()) + + def doaxispositioner(self, axisname): + if self.did(self.doaxispositioner, axisname): + return + self.doranges() + if axisname == "x": + self.axes["x"].setpositioner(positioner.flexlineaxispos_pt(lambda vx: self.vpos_pt(vx, self.xorder, 0), + lambda vx: self.vtickdirection(vx, self.xorder, 0, vx, 1-self.xorder, 0), + self.xvgridpath)) + elif axisname == "x2": + self.axes["x2"].setpositioner(positioner.flexlineaxispos_pt(lambda vx: self.vpos_pt(vx, 1-self.xorder, 0), + lambda vx: self.vtickdirection(vx, 1-self.xorder, 0, vx, self.xorder, 0), + self.xvgridpath)) + elif axisname == "x3": + self.axes["x3"].setpositioner(positioner.flexlineaxispos_pt(lambda vx: self.vpos_pt(vx, self.xorder, 1), + lambda vx: self.vtickdirection(vx, self.xorder, 1, vx, 1-self.xorder, 1), + self.xvgridpath)) + elif axisname == "x4": + self.axes["x4"].setpositioner(positioner.flexlineaxispos_pt(lambda vx: self.vpos_pt(vx, 1-self.xorder, 1), + lambda vx: self.vtickdirection(vx, 1-self.xorder, 1, vx, self.xorder, 1), + self.xvgridpath)) + elif axisname == "y": + self.axes["y"].setpositioner(positioner.flexlineaxispos_pt(lambda vy: self.vpos_pt(self.yorder, vy, 0), + lambda vy: self.vtickdirection(self.yorder, vy, 0, 1-self.yorder, vy, 0), + self.yvgridpath)) + elif axisname == "y2": + self.axes["y2"].setpositioner(positioner.flexlineaxispos_pt(lambda vy: self.vpos_pt(1-self.yorder, vy, 0), + lambda vy: self.vtickdirection(1-self.yorder, vy, 0, self.yorder, vy, 0), + self.yvgridpath)) + elif axisname == "y3": + self.axes["y3"].setpositioner(positioner.flexlineaxispos_pt(lambda vy: self.vpos_pt(self.yorder, vy, 1), + lambda vy: self.vtickdirection(self.yorder, vy, 1, 1-self.yorder, vy, 1), + self.yvgridpath)) + elif axisname == "y4": + self.axes["y4"].setpositioner(positioner.flexlineaxispos_pt(lambda vy: self.vpos_pt(1-self.yorder, vy, 1), + lambda vy: self.vtickdirection(1-self.yorder, vy, 1, self.yorder, vy, 1), + self.yvgridpath)) + elif axisname == "z": + self.axes["z"].setpositioner(positioner.flexlineaxispos_pt(lambda vz: self.vpos_pt(0, 0, vz), + lambda vz: self.vtickdirection(0, 0, vz, 1, 1, vz), + self.zvgridpath)) + elif axisname == "z2": + self.axes["z2"].setpositioner(positioner.flexlineaxispos_pt(lambda vz: self.vpos_pt(1, 0, vz), + lambda vz: self.vtickdirection(1, 0, vz, 0, 1, vz), + self.zvgridpath)) + elif axisname == "z3": + self.axes["z3"].setpositioner(positioner.flexlineaxispos_pt(lambda vz: self.vpos_pt(0, 1, vz), + lambda vz: self.vtickdirection(0, 1, vz, 1, 0, vz), + self.zvgridpath)) + elif axisname == "z4": + self.axes["z4"].setpositioner(positioner.flexlineaxispos_pt(lambda vz: self.vpos_pt(0, 0, vz), + lambda vz: self.vtickdirection(1, 1, vz, 0, 0, vz), + self.zvgridpath)) + else: + raise NotImplementedError("4 axis per dimension supported only") + + def dolayout(self): + if self.did(self.dolayout): + return + for axisname in self.axes.keys(): + self.doaxiscreate(axisname) + + def dobackground(self): + if self.did(self.dobackground): + return + + def doaxes(self): + if self.did(self.doaxes): + return + self.dolayout() + self.dobackground() + for axis in self.axes.values(): + self.insert(axis.canvas) + + def dokey(self): + if self.did(self.dokey): + return + self.dobackground() + self.dostyles() + if self.key is not None: + c = self.key.paint(self.plotitems) + bbox = c.bbox() + def parentchildalign(pmin, pmax, cmin, cmax, pos, dist, inside): + ppos = pmin+0.5*(cmax-cmin)+dist+pos*(pmax-pmin-cmax+cmin-2*dist) + cpos = 0.5*(cmin+cmax)+(1-inside)*(1-2*pos)*(cmax-cmin+2*dist) + return ppos-cpos + if bbox: + x = parentchildalign(self.xpos_pt, self.xpos_pt+self.size_pt, + bbox.llx_pt, bbox.urx_pt, + self.key.hpos, unit.topt(self.key.hdist), self.key.hinside) + y = parentchildalign(self.ypos_pt, self.ypos_pt+self.size_pt, + bbox.lly_pt, bbox.ury_pt, + self.key.vpos, unit.topt(self.key.vdist), self.key.vinside) + self.insert(c, [trafo.translate_pt(x, y)]) diff --git a/compiler/gdsMill/pyx/graph/key.py b/compiler/gdsMill/pyx/graph/key.py new file mode 100644 index 00000000..515d203a --- /dev/null +++ b/compiler/gdsMill/pyx/graph/key.py @@ -0,0 +1,115 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2005 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +from pyx import box, canvas, text, trafo, unit + + +class key: + + defaulttextattrs = [text.vshift.mathaxis] + + def __init__(self, dist=0.2*unit.v_cm, pos="tr", hpos=None, vpos=None, + hinside=1, vinside=1, hdist=0.6*unit.v_cm, vdist=0.4*unit.v_cm, + symbolwidth=0.5*unit.v_cm, symbolheight=0.25*unit.v_cm, symbolspace=0.2*unit.v_cm, + textattrs=[], columns=1, columndist=0.5*unit.v_cm, + border=0.3*unit.v_cm, keyattrs=None): + self.dist = dist + self.hinside = hinside + self.vinside = vinside + self.hdist = hdist + self.vdist = vdist + self.symbolwidth = symbolwidth + self.symbolheight = symbolheight + self.symbolspace = symbolspace + self.textattrs = textattrs + self.columns = columns + self.columndist = columndist + self.border = border + self.keyattrs = keyattrs + if pos is not None: + if vpos is not None or hpos is not None: + raise ValueError("either specify pos or a combination of hpos, vpos") + for poslist, hpos, vpos in [(["tr", "rt"], 1, 1), + (["tc", "ct"], 0.5, 1), + (["tl", "lt"], 0, 1), + (["mr", "rm"], 1, 0.5), + (["mc", "cm"], 0.5, 0.5), + (["ml", "lm"], 0, 0.5), + (["br", "rb"], 1, 0), + (["bc", "cb"], 0.5, 0), + (["bl", "lb"], 0, 0)]: + if pos in poslist: + self.hpos = hpos + self.vpos = vpos + break + else: + raise ValueError("invalid pos") + else: + if vpos is None or hpos is None: + raise ValueError("either specify pos or a combination of hpos, vpos") + self.hpos = hpos + self.vpos = vpos + + def paintcolumn(self, plotitems): + "creates the layout of a key column" + c = canvas.canvas() + self.dist_pt = unit.topt(self.dist) + self.hdist_pt = unit.topt(self.hdist) + self.vdist_pt = unit.topt(self.vdist) + self.symbolwidth_pt = unit.topt(self.symbolwidth) + self.symbolheight_pt = unit.topt(self.symbolheight) + self.symbolspace_pt = unit.topt(self.symbolspace) + titleboxes = [] + for plotitem in plotitems: + titlebox = c.texrunner.text_pt(0, 0, plotitem.title, self.defaulttextattrs + self.textattrs) + titlebox.plotitem = plotitem + titleboxes.append(titlebox) + dy_pt = box.tile_pt(titleboxes, self.dist_pt, 0, -1) + box.linealignequal_pt(titleboxes, self.symbolwidth_pt + self.symbolspace_pt, 1, 0) + y_pt = -0.5 * self.symbolheight_pt + titleboxes[0].center[1] + for titlebox in titleboxes: + titlebox.plotitem.key_pt(c, 0, y_pt, self.symbolwidth_pt, self.symbolheight_pt) + y_pt -= dy_pt + for titlebox in titleboxes: + c.insert(titlebox) + return c + + def paint(self, plotitems): + "creates the layout of the key" + columndist_pt = unit.topt(self.columndist) + c = canvas.canvas() + plotitems = [plotitem for plotitem in plotitems if plotitem.title is not None] + itemspercolumn = (len(plotitems) + self.columns - 1) / self.columns # integer division + x_pt = 0 + while plotitems: + subc = self.paintcolumn(plotitems[:itemspercolumn]) + c.insert(subc, [trafo.translate_pt(x_pt, 0)]) + x_pt += unit.topt(subc.bbox().width()) + columndist_pt + del plotitems[:itemspercolumn] + if self.keyattrs is not None: + newc = canvas.canvas() + newc.draw(c.bbox().enlarged(self.border).path(), self.keyattrs) + newc.insert(c) + c = newc + return c diff --git a/compiler/gdsMill/pyx/graph/style.py b/compiler/gdsMill/pyx/graph/style.py new file mode 100644 index 00000000..cc43df10 --- /dev/null +++ b/compiler/gdsMill/pyx/graph/style.py @@ -0,0 +1,1875 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +from __future__ import nested_scopes + +import math, warnings +from pyx import attr, deco, style, color, unit, canvas, path, mesh +from pyx import text as textmodule + +builtinrange = range + +try: + sum([]) +except NameError: + # fallback implementation for Python 2.2 and below + def sum(list): + return reduce(lambda x, y: x+y, list, 0) + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2. and below + def enumerate(list): + return zip(xrange(len(list)), list) + +class _style: + """Interface class for graph styles + + Each graph style must support the methods described in this + class. However, since a graph style might not need to perform + actions on all the various events, it does not need to overwrite + all methods of this base class (e.g. this class is not an abstract + class in any respect). + + A style should never store private data by istance variables + (i.e. accessing self), but it should use the sharedata and privatedata + instances instead. A style instance can be used multiple times with + different sharedata and privatedata instances at the very same time. + The sharedata and privatedata instances act as data containers and + sharedata allows for sharing information across several styles. + + Every style contains two class variables, which are not to be + modified: + - providesdata is a list of variable names a style offers via + the sharedata instance. This list is used to determine whether + all needs of subsequent styles are fullfilled. Otherwise + getdefaultprovider should return a proper style to be used. + - needsdata is a list of variable names the style needs to access in the + sharedata instance. + """ + + providesdata = [] # by default, we provide nothing + needsdata = [] # and do not depend on anything + + def columnnames(self, privatedata, sharedata, graph, columnnames): + """Set column information + + This method is used setup the column name information to be + accessible to the style later on. The style should analyse + the list of column names. The method should return a list of + column names which the style will make use of.""" + return [] + + def adjustaxis(self, privatedata, sharedata, graph, columnname, data): + """Adjust axis range + + This method is called in order to adjust the axis range to + the provided data. columnname is the column name (each style + is subsequently called for all column names).""" + pass + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + """Select stroke/fill attributes + + This method is called to allow for the selection of + changable attributes of a style.""" + pass + + def initdrawpoints(self, privatedata, sharedata, graph): + """Initialize drawing of data + + This method might be used to initialize the drawing of data.""" + pass + + def drawpoint(self, privatedata, sharedata, graph, point): + """Draw data + + This method is called for each data point. The data is + available in the dictionary point. The dictionary + keys are the column names.""" + pass + + def donedrawpoints(self, privatedata, sharedata, graph): + """Finalize drawing of data + + This method is called after the last data point was + drawn using the drawpoint method above.""" + pass + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + """Draw graph key""" + + +# The following two methods are used to register and get a default provider +# for keys. A key is a variable name in sharedata. A provider is a style +# which creates variables in sharedata. + +_defaultprovider = {} + +def registerdefaultprovider(style, keys): + """sets a style as a default creator for sharedata variables 'keys'""" + for key in keys: + assert key in style.providesdata, "key not provided by style" + # we might allow for overwriting the defaults, i.e. the following is not checked: + # assert key in _defaultprovider.keys(), "default provider already registered for key" + _defaultprovider[key] = style + +def getdefaultprovider(key): + """returns a style, which acts as a default creator for the + sharedata variable 'key'""" + return _defaultprovider[key] + + +class pos(_style): + + providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"] + + def __init__(self, epsilon=1e-10): + self.epsilon = epsilon + + def columnnames(self, privatedata, sharedata, graph, columnnames): + sharedata.poscolumnnames = [] + sharedata.vposmissing = [] + for count, axisnames in enumerate(graph.axesnames): + for axisname in axisnames: + for columnname in columnnames: + if axisname == columnname: + sharedata.poscolumnnames.append(columnname) + if len(sharedata.poscolumnnames) > count+1: + raise ValueError("multiple axes per graph dimension") + elif len(sharedata.poscolumnnames) < count+1: + sharedata.vposmissing.append(count) + sharedata.poscolumnnames.append(None) + return [columnname for columnname in sharedata.poscolumnnames if columnname is not None] + + def adjustaxis(self, privatedata, sharedata, graph, columnname, data): + if columnname in sharedata.poscolumnnames: + graph.axes[columnname].adjustaxis(data) + + def initdrawpoints(self, privatedata, sharedata, graph): + sharedata.vpos = [None]*(len(graph.axesnames)) + privatedata.pointpostmplist = [[columnname, index, graph.axes[columnname]] # temporarily used by drawpoint only + for index, columnname in enumerate([columnname for columnname in sharedata.poscolumnnames if columnname is not None])] + for missing in sharedata.vposmissing: + for pointpostmp in privatedata.pointpostmplist: + if pointpostmp[1] >= missing: + pointpostmp[1] += 1 + + def drawpoint(self, privatedata, sharedata, graph, point): + sharedata.vposavailable = 1 # valid position (but might be outside of the graph) + sharedata.vposvalid = 1 # valid position inside the graph + for columnname, index, axis in privatedata.pointpostmplist: + try: + v = axis.convert(point[columnname]) + except (ArithmeticError, ValueError, TypeError): + sharedata.vposavailable = sharedata.vposvalid = 0 + sharedata.vpos[index] = None + else: + if v < -self.epsilon or v > 1+self.epsilon: + sharedata.vposvalid = 0 + sharedata.vpos[index] = v + + +registerdefaultprovider(pos(), pos.providesdata) + + +class range(_style): + + providesdata = ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"] + + # internal bit masks + mask_value = 1 + mask_min = 2 + mask_max = 4 + mask_dmin = 8 + mask_dmax = 16 + mask_d = 32 + + def __init__(self, usenames={}, epsilon=1e-10): + self.usenames = usenames + self.epsilon = epsilon + + def _numberofbits(self, mask): + if not mask: + return 0 + if mask & 1: + return self._numberofbits(mask >> 1) + 1 + else: + return self._numberofbits(mask >> 1) + + def columnnames(self, privatedata, sharedata, graph, columnnames): + usecolumns = [] + privatedata.rangeposcolumns = [] + sharedata.vrangemissing = [] + sharedata.vrangeminmissing = [] + sharedata.vrangemaxmissing = [] + privatedata.rangeposdeltacolumns = {} # temporarily used by adjustaxis only + for count, axisnames in enumerate(graph.axesnames): + for axisname in axisnames: + try: + usename = self.usenames[axisname] + except KeyError: + usename = axisname + mask = 0 + for columnname in columnnames: + addusecolumns = 1 + if usename == columnname: + mask += self.mask_value + elif usename + "min" == columnname: + mask += self.mask_min + elif usename + "max" == columnname: + mask += self.mask_max + elif "d" + usename + "min" == columnname: + mask += self.mask_dmin + elif "d" + usename + "max" == columnname: + mask += self.mask_dmax + elif "d" + usename == columnname: + mask += self.mask_d + else: + addusecolumns = 0 + if addusecolumns: + usecolumns.append(columnname) + if mask & (self.mask_min | self.mask_max | self.mask_dmin | self.mask_dmax | self.mask_d): + if (self._numberofbits(mask & (self.mask_min | self.mask_dmin | self.mask_d)) > 1 or + self._numberofbits(mask & (self.mask_max | self.mask_dmax | self.mask_d)) > 1): + raise ValueError("multiple range definition") + if mask & (self.mask_dmin | self.mask_dmax | self.mask_d): + if not (mask & self.mask_value): + raise ValueError("missing value for delta") + privatedata.rangeposdeltacolumns[axisname] = {} + privatedata.rangeposcolumns.append((axisname, usename, mask)) + elif mask == self.mask_value: + usecolumns = usecolumns[:-1] + if len(privatedata.rangeposcolumns) + len(sharedata.vrangemissing) > count+1: + raise ValueError("multiple axes per graph dimension") + elif len(privatedata.rangeposcolumns) + len(sharedata.vrangemissing) < count+1: + sharedata.vrangemissing.append(count) + sharedata.vrangeminmissing.append(count) + sharedata.vrangemaxmissing.append(count) + else: + if not (privatedata.rangeposcolumns[-1][2] & (self.mask_min | self.mask_dmin | self.mask_d)): + sharedata.vrangeminmissing.append(count) + if not (privatedata.rangeposcolumns[-1][2] & (self.mask_max | self.mask_dmax | self.mask_d)): + sharedata.vrangemaxmissing.append(count) + return usecolumns + + def adjustaxis(self, privatedata, sharedata, graph, columnname, data): + if columnname in [c + "min" for a, c, m in privatedata.rangeposcolumns if m & self.mask_min]: + graph.axes[columnname[:-3]].adjustaxis(data) + if columnname in [c + "max" for a, c, m in privatedata.rangeposcolumns if m & self.mask_max]: + graph.axes[columnname[:-3]].adjustaxis(data) + + # delta handling: fill rangeposdeltacolumns + for axisname, usename, mask in privatedata.rangeposcolumns: + if columnname == usename and mask & (self.mask_dmin | self.mask_dmax | self.mask_d): + privatedata.rangeposdeltacolumns[axisname][self.mask_value] = data + if columnname == "d" + usename + "min" and mask & self.mask_dmin: + privatedata.rangeposdeltacolumns[axisname][self.mask_dmin] = data + if columnname == "d" + usename + "max" and mask & self.mask_dmax: + privatedata.rangeposdeltacolumns[axisname][self.mask_dmax] = data + if columnname == "d" + usename and mask & self.mask_d: + privatedata.rangeposdeltacolumns[axisname][self.mask_d] = data + + # delta handling: process rangeposdeltacolumns + for a, d in privatedata.rangeposdeltacolumns.items(): + if d.has_key(self.mask_value): + for k in d.keys(): + if k != self.mask_value: + if k & (self.mask_dmin | self.mask_d): + mindata = [] + for value, delta in zip(d[self.mask_value], d[k]): + try: + mindata.append(value-delta) + except: + pass + graph.axes[a].adjustaxis(mindata) + if k & (self.mask_dmax | self.mask_d): + maxdata = [] + for value, delta in zip(d[self.mask_value], d[k]): + try: + maxdata.append(value+delta) + except: + pass + graph.axes[a].adjustaxis(maxdata) + del d[k] + + def initdrawpoints(self, privatedata, sharedata, graph): + sharedata.vrange = [[None for x in xrange(2)] for y in privatedata.rangeposcolumns + sharedata.vrangemissing] + privatedata.rangepostmplist = [[usename, mask, index, graph.axes[axisname]] # temporarily used by drawpoint only + for index, (axisname, usename, mask) in enumerate(privatedata.rangeposcolumns)] + for missing in sharedata.vrangemissing: + for rangepostmp in privatedata.rangepostmplist: + if rangepostmp[2] >= missing: + rangepostmp[2] += 1 + + def drawpoint(self, privatedata, sharedata, graph, point): + for usename, mask, index, axis in privatedata.rangepostmplist: + try: + if mask & self.mask_min: + sharedata.vrange[index][0] = axis.convert(point[usename + "min"]) + if mask & self.mask_dmin: + sharedata.vrange[index][0] = axis.convert(point[usename] - point["d" + usename + "min"]) + if mask & self.mask_d: + sharedata.vrange[index][0] = axis.convert(point[usename] - point["d" + usename]) + except (ArithmeticError, ValueError, TypeError): + sharedata.vrange[index][0] = None + try: + if mask & self.mask_max: + sharedata.vrange[index][1] = axis.convert(point[usename + "max"]) + if mask & self.mask_dmax: + sharedata.vrange[index][1] = axis.convert(point[usename] + point["d" + usename + "max"]) + if mask & self.mask_d: + sharedata.vrange[index][1] = axis.convert(point[usename] + point["d" + usename]) + except (ArithmeticError, ValueError, TypeError): + sharedata.vrange[index][1] = None + + # some range checks for data consistency + if (sharedata.vrange[index][0] is not None and sharedata.vrange[index][1] is not None and + sharedata.vrange[index][0] > sharedata.vrange[index][1] + self.epsilon): + raise ValueError("inverse range") + # disabled due to missing vpos access: + # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and + # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon): + # raise ValueError("negative minimum errorbar") + # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and + # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon): + # raise ValueError("negative maximum errorbar") + + +registerdefaultprovider(range(), range.providesdata) + + +def _crosssymbol(c, x_pt, y_pt, size_pt, attrs): + c.draw(path.path(path.moveto_pt(x_pt-0.5*size_pt, y_pt-0.5*size_pt), + path.lineto_pt(x_pt+0.5*size_pt, y_pt+0.5*size_pt), + path.moveto_pt(x_pt-0.5*size_pt, y_pt+0.5*size_pt), + path.lineto_pt(x_pt+0.5*size_pt, y_pt-0.5*size_pt)), attrs) + +def _plussymbol(c, x_pt, y_pt, size_pt, attrs): + c.draw(path.path(path.moveto_pt(x_pt-0.707106781*size_pt, y_pt), + path.lineto_pt(x_pt+0.707106781*size_pt, y_pt), + path.moveto_pt(x_pt, y_pt-0.707106781*size_pt), + path.lineto_pt(x_pt, y_pt+0.707106781*size_pt)), attrs) + +def _squaresymbol(c, x_pt, y_pt, size_pt, attrs): + c.draw(path.path(path.moveto_pt(x_pt-0.5*size_pt, y_pt-0.5*size_pt), + path.lineto_pt(x_pt+0.5*size_pt, y_pt-0.5*size_pt), + path.lineto_pt(x_pt+0.5*size_pt, y_pt+0.5*size_pt), + path.lineto_pt(x_pt-0.5*size_pt, y_pt+0.5*size_pt), + path.closepath()), attrs) + +def _trianglesymbol(c, x_pt, y_pt, size_pt, attrs): + c.draw(path.path(path.moveto_pt(x_pt-0.759835685*size_pt, y_pt-0.438691337*size_pt), + path.lineto_pt(x_pt+0.759835685*size_pt, y_pt-0.438691337*size_pt), + path.lineto_pt(x_pt, y_pt+0.877382675*size_pt), + path.closepath()), attrs) + +def _circlesymbol(c, x_pt, y_pt, size_pt, attrs): + c.draw(path.path(path.arc_pt(x_pt, y_pt, 0.564189583*size_pt, 0, 360), + path.closepath()), attrs) + +def _diamondsymbol(c, x_pt, y_pt, size_pt, attrs): + c.draw(path.path(path.moveto_pt(x_pt-0.537284965*size_pt, y_pt), + path.lineto_pt(x_pt, y_pt-0.930604859*size_pt), + path.lineto_pt(x_pt+0.537284965*size_pt, y_pt), + path.lineto_pt(x_pt, y_pt+0.930604859*size_pt), + path.closepath()), attrs) + + +class _styleneedingpointpos(_style): + + needsdata = ["vposmissing"] + + def columnnames(self, privatedata, sharedata, graph, columnnames): + if len(sharedata.vposmissing): + raise ValueError("incomplete position information") + return [] + + +class symbol(_styleneedingpointpos): + + needsdata = ["vpos", "vposmissing", "vposvalid"] + + # "inject" the predefinied symbols into the class: + # + # Note, that statements like cross = _crosssymbol are + # invalid, since the would lead to unbound methods, but + # a single entry changeable list does the trick. + # + # Once we require Python 2.2+ we should use staticmethods + # to implement the default symbols inplace. + + cross = attr.changelist([_crosssymbol]) + plus = attr.changelist([_plussymbol]) + square = attr.changelist([_squaresymbol]) + triangle = attr.changelist([_trianglesymbol]) + circle = attr.changelist([_circlesymbol]) + diamond = attr.changelist([_diamondsymbol]) + + changecross = attr.changelist([_crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol]) + changeplus = attr.changelist([_plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol]) + changesquare = attr.changelist([_squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol]) + changetriangle = attr.changelist([_trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol]) + changecircle = attr.changelist([_circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol]) + changediamond = attr.changelist([_diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol]) + changesquaretwice = attr.changelist([_squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol]) + changetriangletwice = attr.changelist([_trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol]) + changecircletwice = attr.changelist([_circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol]) + changediamondtwice = attr.changelist([_diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol]) + + changestrokedfilled = attr.changelist([deco.stroked, deco.filled]) + changefilledstroked = attr.changelist([deco.filled, deco.stroked]) + + defaultsymbolattrs = [deco.stroked] + + def __init__(self, symbol=changecross, size=0.2*unit.v_cm, symbolattrs=[]): + self.symbol = symbol + self.size = size + self.symbolattrs = symbolattrs + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + privatedata.symbol = attr.selectattr(self.symbol, selectindex, selecttotal) + privatedata.size_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal)) + if self.symbolattrs is not None: + privatedata.symbolattrs = attr.selectattrs(self.defaultsymbolattrs + self.symbolattrs, selectindex, selecttotal) + else: + privatedata.symbolattrs = None + + def initdrawpoints(self, privatedata, sharedata, graph): + privatedata.symbolcanvas = canvas.canvas() + + def drawpoint(self, privatedata, sharedata, graph, point): + if sharedata.vposvalid and privatedata.symbolattrs is not None: + x_pt, y_pt = graph.vpos_pt(*sharedata.vpos) + privatedata.symbol(privatedata.symbolcanvas, x_pt, y_pt, privatedata.size_pt, privatedata.symbolattrs) + + def donedrawpoints(self, privatedata, sharedata, graph): + graph.insert(privatedata.symbolcanvas) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + if privatedata.symbolattrs is not None: + privatedata.symbol(graph, x_pt+0.5*width_pt, y_pt+0.5*height_pt, privatedata.size_pt, privatedata.symbolattrs) + + +class _line(_styleneedingpointpos): + + # this style is not a complete style, but it provides the basic functionality to + # create a line, which is cut at the graph boundaries (or at otherwise invalid points) + + def initpointstopath(self, privatedata): + privatedata.path = path.path() + privatedata.linebasepoints = [] + privatedata.lastvpos = None + + def addpointstopath(self, privatedata): + # add baselinepoints to privatedata.path + if len(privatedata.linebasepoints) > 1: + privatedata.path.append(path.moveto_pt(*privatedata.linebasepoints[0])) + if len(privatedata.linebasepoints) > 2: + privatedata.path.append(path.multilineto_pt(privatedata.linebasepoints[1:])) + else: + privatedata.path.append(path.lineto_pt(*privatedata.linebasepoints[1])) + privatedata.linebasepoints = [] + + def addpoint(self, privatedata, graphvpos_pt, vposavailable, vposvalid, vpos): + # append linebasepoints + if vposavailable: + if len(privatedata.linebasepoints): + # the last point was inside the graph + if vposvalid: # shortcut for the common case + privatedata.linebasepoints.append(graphvpos_pt(*vpos)) + else: + # cut end + cut = 1 + for vstart, vend in zip(privatedata.lastvpos, vpos): + newcut = None + if vend > 1: + # 1 = vstart + (vend - vstart) * cut + try: + newcut = (1 - vstart)/(vend - vstart) + except (ArithmeticError, TypeError): + break + if vend < 0: + # 0 = vstart + (vend - vstart) * cut + try: + newcut = - vstart/(vend - vstart) + except (ArithmeticError, TypeError): + break + if newcut is not None and newcut < cut: + cut = newcut + else: + cutvpos = [] + for vstart, vend in zip(privatedata.lastvpos, vpos): + cutvpos.append(vstart + (vend - vstart) * cut) + privatedata.linebasepoints.append(graphvpos_pt(*cutvpos)) + self.addpointstopath(privatedata) + else: + # the last point was outside the graph + if privatedata.lastvpos is not None: + if vposvalid: + # cut beginning + cut = 0 + for vstart, vend in zip(privatedata.lastvpos, vpos): + newcut = None + if vstart > 1: + # 1 = vstart + (vend - vstart) * cut + try: + newcut = (1 - vstart)/(vend - vstart) + except (ArithmeticError, TypeError): + break + if vstart < 0: + # 0 = vstart + (vend - vstart) * cut + try: + newcut = - vstart/(vend - vstart) + except (ArithmeticError, TypeError): + break + if newcut is not None and newcut > cut: + cut = newcut + else: + cutvpos = [] + for vstart, vend in zip(privatedata.lastvpos, vpos): + cutvpos.append(vstart + (vend - vstart) * cut) + privatedata.linebasepoints.append(graphvpos_pt(*cutvpos)) + privatedata.linebasepoints.append(graphvpos_pt(*vpos)) + else: + # sometimes cut beginning and end + cutfrom = 0 + cutto = 1 + for vstart, vend in zip(privatedata.lastvpos, vpos): + newcutfrom = None + if vstart > 1: + if vend > 1: + break + # 1 = vstart + (vend - vstart) * cutfrom + try: + newcutfrom = (1 - vstart)/(vend - vstart) + except (ArithmeticError, TypeError): + break + if vstart < 0: + if vend < 0: + break + # 0 = vstart + (vend - vstart) * cutfrom + try: + newcutfrom = - vstart/(vend - vstart) + except (ArithmeticError, TypeError): + break + if newcutfrom is not None and newcutfrom > cutfrom: + cutfrom = newcutfrom + newcutto = None + if vend > 1: + # 1 = vstart + (vend - vstart) * cutto + try: + newcutto = (1 - vstart)/(vend - vstart) + except (ArithmeticError, TypeError): + break + if vend < 0: + # 0 = vstart + (vend - vstart) * cutto + try: + newcutto = - vstart/(vend - vstart) + except (ArithmeticError, TypeError): + break + if newcutto is not None and newcutto < cutto: + cutto = newcutto + else: + if cutfrom < cutto: + cutfromvpos = [] + cuttovpos = [] + for vstart, vend in zip(privatedata.lastvpos, vpos): + cutfromvpos.append(vstart + (vend - vstart) * cutfrom) + cuttovpos.append(vstart + (vend - vstart) * cutto) + privatedata.linebasepoints.append(graphvpos_pt(*cutfromvpos)) + privatedata.linebasepoints.append(graphvpos_pt(*cuttovpos)) + self.addpointstopath(privatedata) + privatedata.lastvpos = vpos[:] + else: + if len(privatedata.linebasepoints) > 1: + self.addpointstopath(privatedata) + privatedata.lastvpos = None + + def addinvalid(self, privatedata): + if len(privatedata.linebasepoints) > 1: + self.addpointstopath(privatedata) + privatedata.lastvpos = None + + def donepointstopath(self, privatedata): + if len(privatedata.linebasepoints) > 1: + self.addpointstopath(privatedata) + return privatedata.path + + +class line(_line): + + needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid"] + + changelinestyle = attr.changelist([style.linestyle.solid, + style.linestyle.dashed, + style.linestyle.dotted, + style.linestyle.dashdotted]) + + defaultlineattrs = [changelinestyle] + + def __init__(self, lineattrs=[]): + self.lineattrs = lineattrs + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + if self.lineattrs is not None: + privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal) + else: + privatedata.lineattrs = None + + def initdrawpoints(self, privatedata, sharedata, graph): + self.initpointstopath(privatedata) + + def drawpoint(self, privatedata, sharedata, graph, point): + self.addpoint(privatedata, graph.vpos_pt, sharedata.vposavailable, sharedata.vposvalid, sharedata.vpos) + + def donedrawpoints(self, privatedata, sharedata, graph): + path = self.donepointstopath(privatedata) + if privatedata.lineattrs is not None and len(path): + graph.stroke(path, privatedata.lineattrs) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + if privatedata.lineattrs is not None: + graph.stroke(path.line_pt(x_pt, y_pt+0.5*height_pt, x_pt+width_pt, y_pt+0.5*height_pt), privatedata.lineattrs) + + +class impulses(_styleneedingpointpos): + + needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"] + + defaultlineattrs = [line.changelinestyle] + defaultfrompathattrs = [] + + def __init__(self, lineattrs=[], fromvalue=0, frompathattrs=[], valueaxisindex=1): + self.lineattrs = lineattrs + self.fromvalue = fromvalue + self.frompathattrs = frompathattrs + self.valueaxisindex = valueaxisindex + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + privatedata.insertfrompath = selectindex == 0 + if self.lineattrs is not None: + privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal) + else: + privatedata.lineattrs = None + + def adjustaxis(self, privatedata, sharedata, graph, columnname, data): + if self.fromvalue is not None: + try: + i = sharedata.poscolumnnames.index(columnname) + except ValueError: + pass + else: + if i == self.valueaxisindex: + graph.axes[sharedata.poscolumnnames[i]].adjustaxis([self.fromvalue]) + + def initdrawpoints(self, privatedata, sharedata, graph): + privatedata.impulsescanvas = canvas.canvas() + if self.fromvalue is not None: + valueaxisname = sharedata.poscolumnnames[self.valueaxisindex] + privatedata.vfromvalue = graph.axes[valueaxisname].convert(self.fromvalue) + privatedata.vfromvaluecut = 0 + if privatedata.vfromvalue < 0: + privatedata.vfromvalue = 0 + if privatedata.vfromvalue > 1: + privatedata.vfromvalue = 1 + if self.frompathattrs is not None and privatedata.insertfrompath: + graph.stroke(graph.axes[valueaxisname].vgridpath(privatedata.vfromvalue), + self.defaultfrompathattrs + self.frompathattrs) + else: + privatedata.vfromvalue = 0 + + def drawpoint(self, privatedata, sharedata, graph, point): + if sharedata.vposvalid and privatedata.lineattrs is not None: + vpos = sharedata.vpos[:] + vpos[self.valueaxisindex] = privatedata.vfromvalue + privatedata.impulsescanvas.stroke(graph.vgeodesic(*(vpos + sharedata.vpos)), privatedata.lineattrs) + + def donedrawpoints(self, privatedata, sharedata, graph): + graph.insert(privatedata.impulsescanvas) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + if privatedata.lineattrs is not None: + graph.stroke(path.line_pt(x_pt, y_pt+0.5*height_pt, x_pt+width_pt, y_pt+0.5*height_pt), privatedata.lineattrs) + + +class errorbar(_style): + + needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"] + + defaulterrorbarattrs = [] + + def __init__(self, size=0.1*unit.v_cm, + errorbarattrs=[], + epsilon=1e-10): + self.size = size + self.errorbarattrs = errorbarattrs + self.epsilon = epsilon + + def columnnames(self, privatedata, sharedata, graph, columnnames): + for i in sharedata.vposmissing: + if i in sharedata.vrangeminmissing and i in sharedata.vrangemaxmissing: + raise ValueError("position and range for a graph dimension missing") + return [] + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + privatedata.errorsize_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal)) + privatedata.errorbarattrs = attr.selectattrs(self.defaulterrorbarattrs + self.errorbarattrs, selectindex, selecttotal) + + def initdrawpoints(self, privatedata, sharedata, graph): + if privatedata.errorbarattrs is not None: + privatedata.errorbarcanvas = canvas.canvas(privatedata.errorbarattrs) + privatedata.dimensionlist = list(xrange(len(sharedata.vpos))) + + def drawpoint(self, privatedata, sharedata, graph, point): + if privatedata.errorbarattrs is not None: + for i in privatedata.dimensionlist: + for j in privatedata.dimensionlist: + if (i != j and + (sharedata.vpos[j] is None or + sharedata.vpos[j] < -self.epsilon or + sharedata.vpos[j] > 1+self.epsilon)): + break + else: + if ((sharedata.vrange[i][0] is None and sharedata.vpos[i] is None) or + (sharedata.vrange[i][1] is None and sharedata.vpos[i] is None) or + (sharedata.vrange[i][0] is None and sharedata.vrange[i][1] is None)): + continue + vminpos = sharedata.vpos[:] + if sharedata.vrange[i][0] is not None: + vminpos[i] = sharedata.vrange[i][0] + mincap = 1 + else: + mincap = 0 + if vminpos[i] > 1+self.epsilon: + continue + if vminpos[i] < -self.epsilon: + vminpos[i] = 0 + mincap = 0 + vmaxpos = sharedata.vpos[:] + if sharedata.vrange[i][1] is not None: + vmaxpos[i] = sharedata.vrange[i][1] + maxcap = 1 + else: + maxcap = 0 + if vmaxpos[i] < -self.epsilon: + continue + if vmaxpos[i] > 1+self.epsilon: + vmaxpos[i] = 1 + maxcap = 0 + privatedata.errorbarcanvas.stroke(graph.vgeodesic(*(vminpos + vmaxpos))) + for j in privatedata.dimensionlist: + if i != j: + if mincap: + privatedata.errorbarcanvas.stroke(graph.vcap_pt(j, privatedata.errorsize_pt, *vminpos)) + if maxcap: + privatedata.errorbarcanvas.stroke(graph.vcap_pt(j, privatedata.errorsize_pt, *vmaxpos)) + + def donedrawpoints(self, privatedata, sharedata, graph): + if privatedata.errorbarattrs is not None: + graph.insert(privatedata.errorbarcanvas) + + +class text(_styleneedingpointpos): + + needsdata = ["vpos", "vposmissing", "vposvalid"] + + defaulttextattrs = [textmodule.halign.center, textmodule.vshift.mathaxis] + + def __init__(self, textname="text", dxname=None, dyname=None, + dxunit=0.3*unit.v_cm, dyunit=0.3*unit.v_cm, + textdx=0*unit.v_cm, textdy=0.3*unit.v_cm, textattrs=[]): + self.textname = textname + self.dxname = dxname + self.dyname = dyname + self.dxunit = dxunit + self.dyunit = dyunit + self.textdx = textdx + self.textdy = textdy + self.textattrs = textattrs + + def columnnames(self, privatedata, sharedata, graph, columnnames): + if self.textname not in columnnames: + raise ValueError("column '%s' missing" % self.textname) + names = [self.textname] + if self.dxname is not None: + if self.dxname not in columnnames: + raise ValueError("column '%s' missing" % self.dxname) + names.append(self.dxname) + if self.dyname is not None: + if self.dyname not in columnnames: + raise ValueError("column '%s' missing" % self.dyname) + names.append(self.dyname) + return names + _styleneedingpointpos.columnnames(self, privatedata, sharedata, graph, columnnames) + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + if self.textattrs is not None: + privatedata.textattrs = attr.selectattrs(self.defaulttextattrs + self.textattrs, selectindex, selecttotal) + else: + privatedata.textattrs = None + + def initdrawpoints(self, privatedata, sharedata, grap): + if self.dxname is None: + privatedata.textdx_pt = unit.topt(self.textdx) + else: + privatedata.dxunit_pt = unit.topt(self.dxunit) + if self.dyname is None: + privatedata.textdy_pt = unit.topt(self.textdy) + else: + privatedata.dyunit_pt = unit.topt(self.dyunit) + + def drawpoint(self, privatedata, sharedata, graph, point): + if privatedata.textattrs is not None and sharedata.vposvalid: + x_pt, y_pt = graph.vpos_pt(*sharedata.vpos) + try: + text = str(point[self.textname]) + except: + pass + else: + if self.dxname is None: + dx_pt = privatedata.textdx_pt + else: + dx_pt = float(point[self.dxname]) * privatedata.dxunit_pt + if self.dyname is None: + dy_pt = privatedata.textdy_pt + else: + dy_pt = float(point[self.dyname]) * privatedata.dyunit_pt + graph.text_pt(x_pt + dx_pt, y_pt + dy_pt, text, privatedata.textattrs) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + raise RuntimeError("Style currently doesn't provide a graph key") + + +class arrow(_styleneedingpointpos): + + needsdata = ["vpos", "vposmissing", "vposvalid"] + + defaultlineattrs = [] + defaultarrowattrs = [] + + def __init__(self, linelength=0.25*unit.v_cm, arrowsize=0.15*unit.v_cm, lineattrs=[], arrowattrs=[], arrowpos=0.5, epsilon=1e-5): + self.linelength = linelength + self.arrowsize = arrowsize + self.lineattrs = lineattrs + self.arrowattrs = arrowattrs + self.arrowpos = arrowpos + self.epsilon = epsilon + + def columnnames(self, privatedata, sharedata, graph, columnnames): + if len(graph.axesnames) != 2: + raise ValueError("arrow style restricted on two-dimensional graphs") + if "size" not in columnnames: + raise ValueError("size missing") + if "angle" not in columnnames: + raise ValueError("angle missing") + return ["size", "angle"] + _styleneedingpointpos.columnnames(self, privatedata, sharedata, graph, columnnames) + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + if self.lineattrs is not None: + privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal) + else: + privatedata.lineattrs = None + if self.arrowattrs is not None: + privatedata.arrowattrs = attr.selectattrs(self.defaultarrowattrs + self.arrowattrs, selectindex, selecttotal) + else: + privatedata.arrowattrs = None + + def initdrawpoints(self, privatedata, sharedata, graph): + privatedata.arrowcanvas = canvas.canvas() + + def drawpoint(self, privatedata, sharedata, graph, point): + if privatedata.lineattrs is not None and privatedata.arrowattrs is not None and sharedata.vposvalid: + linelength_pt = unit.topt(self.linelength) + x_pt, y_pt = graph.vpos_pt(*sharedata.vpos) + try: + angle = point["angle"] + 0.0 + size = point["size"] + 0.0 + except: + pass + else: + if point["size"] > self.epsilon: + dx = math.cos(angle*math.pi/180) + dy = math.sin(angle*math.pi/180) + x1 = x_pt-self.arrowpos*dx*linelength_pt*size + y1 = y_pt-self.arrowpos*dy*linelength_pt*size + x2 = x_pt+(1-self.arrowpos)*dx*linelength_pt*size + y2 = y_pt+(1-self.arrowpos)*dy*linelength_pt*size + privatedata.arrowcanvas.stroke(path.line_pt(x1, y1, x2, y2), privatedata.lineattrs + + [deco.earrow(privatedata.arrowattrs, size=self.arrowsize*size)]) + + def donedrawpoints(self, privatedata, sharedata, graph): + graph.insert(privatedata.arrowcanvas) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + raise RuntimeError("Style currently doesn't provide a graph key") + + +class rect(_style): + + needsdata = ["vrange", "vrangeminmissing", "vrangemaxmissing"] + + def __init__(self, gradient=color.gradient.Grey): + self.gradient = gradient + + def columnnames(self, privatedata, sharedata, graph, columnnames): + if len(graph.axesnames) != 2: + raise TypeError("arrow style restricted on two-dimensional graphs") + if "color" not in columnnames: + raise ValueError("color missing") + if len(sharedata.vrangeminmissing) + len(sharedata.vrangemaxmissing): + raise ValueError("incomplete range") + return ["color"] + + def initdrawpoints(self, privatedata, sharedata, graph): + privatedata.rectcanvas = graph.insert(canvas.canvas()) + + def drawpoint(self, privatedata, sharedata, graph, point): + xvmin = sharedata.vrange[0][0] + xvmax = sharedata.vrange[0][1] + yvmin = sharedata.vrange[1][0] + yvmax = sharedata.vrange[1][1] + if (xvmin is not None and xvmin < 1 and + xvmax is not None and xvmax > 0 and + yvmin is not None and yvmin < 1 and + yvmax is not None and yvmax > 0): + if xvmin < 0: + xvmin = 0 + elif xvmax > 1: + xvmax = 1 + if yvmin < 0: + yvmin = 0 + elif yvmax > 1: + yvmax = 1 + p = graph.vgeodesic(xvmin, yvmin, xvmax, yvmin) + p.append(graph.vgeodesic_el(xvmax, yvmin, xvmax, yvmax)) + p.append(graph.vgeodesic_el(xvmax, yvmax, xvmin, yvmax)) + p.append(graph.vgeodesic_el(xvmin, yvmax, xvmin, yvmin)) + p.append(path.closepath()) + privatedata.rectcanvas.fill(p, [self.gradient.getcolor(point["color"])]) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + raise RuntimeError("Style currently doesn't provide a graph key") + + +class histogram(_style): + + needsdata = ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"] + + defaultlineattrs = [deco.stroked] + defaultfrompathattrs = [] + + def __init__(self, lineattrs=[], steps=0, fromvalue=0, frompathattrs=[], fillable=0, rectkey=0, + autohistogramaxisindex=0, autohistogrampointpos=0.5, epsilon=1e-10): + self.lineattrs = lineattrs + self.steps = steps + self.fromvalue = fromvalue + self.frompathattrs = frompathattrs + self.fillable = fillable # TODO: fillable paths might not properly be closed by straight lines on curved graph geometries + self.rectkey = rectkey + self.autohistogramaxisindex = autohistogramaxisindex + self.autohistogrampointpos = autohistogrampointpos + self.epsilon = epsilon + + def columnnames(self, privatedata, sharedata, graph, columnnames): + if len(graph.axesnames) != 2: + raise TypeError("histogram style restricted on two-dimensional graphs") + privatedata.rangeaxisindex = None + for i in builtinrange(len(graph.axesnames)): + if i in sharedata.vrangeminmissing or i in sharedata.vrangemaxmissing: + if i in sharedata.vposmissing: + raise ValueError("pos and range missing") + else: + if privatedata.rangeaxisindex is not None: + raise ValueError("multiple ranges") + privatedata.rangeaxisindex = i + if privatedata.rangeaxisindex is None: + privatedata.rangeaxisindex = self.autohistogramaxisindex + privatedata.autohistogram = 1 + else: + privatedata.autohistogram = 0 + return [] + + def adjustaxis(self, privatedata, sharedata, graph, columnname, data): + if privatedata.autohistogram and columnname == sharedata.poscolumnnames[privatedata.rangeaxisindex]: + if len(data) == 1: + raise ValueError("several data points needed for automatic histogram width calculation") + if data: + delta = data[1] - data[0] + min = data[0] - self.autohistogrampointpos * delta + max = data[-1] + (1-self.autohistogrampointpos) * delta + graph.axes[columnname].adjustaxis([min, max]) + elif self.fromvalue is not None and columnname == sharedata.poscolumnnames[1-privatedata.rangeaxisindex]: + graph.axes[columnname].adjustaxis([self.fromvalue]) + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + privatedata.insertfrompath = selectindex == 0 + if self.lineattrs is not None: + privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal) + else: + privatedata.lineattrs = None + + def vmoveto(self, privatedata, sharedata, graph, vpos, vvalue): + if -self.epsilon < vpos < 1+self.epsilon and -self.epsilon < vvalue < 1+self.epsilon: + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vpos))) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos, vvalue))) + + def vposline(self, privatedata, sharedata, graph, vpos, vvalue1, vvalue2): + if -self.epsilon < vpos < 1+self.epsilon: + vvalue1cut = 0 + if vvalue1 < 0: + vvalue1 = 0 + vvalue1cut = -1 + elif vvalue1 > 1: + vvalue1 = 1 + vvalue1cut = 1 + vvalue2cut = 0 + if vvalue2 < 0: + vvalue2 = 0 + vvalue2cut = -1 + elif vvalue2 > 1: + vvalue2 = 1 + vvalue2cut = 1 + if abs(vvalue1cut + vvalue2cut) <= 1: + if vvalue1cut and not self.fillable: + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue1, vpos))) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos, vvalue1))) + if privatedata.rangeaxisindex: + privatedata.path.append(graph.vgeodesic_el(vvalue1, vpos, vvalue2, vpos)) + else: + privatedata.path.append(graph.vgeodesic_el(vpos, vvalue1, vpos, vvalue2)) + + def vvalueline(self, privatedata, sharedata, graph, vvalue, vpos1, vpos2): + if self.fillable: + if vvalue < -self.epsilon: + vvalue = 0 + warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path") + if vvalue > 1+self.epsilon: + vvalue = 1 + warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path") + if self.fillable or (-self.epsilon < vvalue < 1+self.epsilon): + vpos1cut = 0 + if vpos1 < 0: + vpos1 = 0 + vpos1cut = -1 + elif vpos1 > 1: + vpos1 = 1 + vpos1cut = 1 + vpos2cut = 0 + if vpos2 < 0: + vpos2 = 0 + vpos2cut = -1 + elif vpos2 > 1: + vpos2 = 1 + vpos2cut = 1 + if abs(vpos1cut + vpos2cut) <= 1: + if vpos1cut: + if self.fillable: + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vpos1))) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vpos1, vvalue, vpos1)) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos1, privatedata.vfromvalue))) + privatedata.path.append(graph.vgeodesic_el(vpos1, privatedata.vfromvalue, vpos1, vvalue)) + else: + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vpos1))) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos1, vvalue))) + if privatedata.rangeaxisindex: + privatedata.path.append(graph.vgeodesic_el(vvalue, vpos1, vvalue, vpos2)) + else: + privatedata.path.append(graph.vgeodesic_el(vpos1, vvalue, vpos2, vvalue)) + if self.fillable and vpos2cut: + warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path") + if privatedata.rangeaxisindex: + privatedata.path.append(graph.vgeodesic_el(vvalue, vpos2, privatedata.vfromvalue, vpos2)) + else: + privatedata.path.append(graph.vgeodesic_el(vpos2, vvalue, vpos2, privatedata.vfromvalue)) + + def drawvalue(self, privatedata, sharedata, graph, vmin, vmax, vvalue): + currentvalid = vmin is not None and vmax is not None and vvalue is not None + if self.fillable and not self.steps: + if not currentvalid: + return + vmincut = 0 + if vmin < -self.epsilon: + vmin = 0 + vmincut = -1 + elif vmin > 1+self.epsilon: + vmin = 1 + vmincut = 1 + vmaxcut = 0 + if vmax < -self.epsilon: + vmax = 0 + vmaxcut = -1 + if vmax > 1+self.epsilon: + vmax = 1 + vmaxcut = 1 + vvaluecut = 0 + if vvalue < -self.epsilon: + vvalue = 0 + vvaluecut = -1 + if vvalue > 1+self.epsilon: + vvalue = 1 + vvaluecut = 1 + done = 0 + if abs(vmincut) + abs(vmaxcut) + abs(vvaluecut) + abs(privatedata.vfromvaluecut) > 1: + if abs(vmincut + vmaxcut) > 1 or abs(vvaluecut+privatedata.vfromvaluecut) > 1: + done = 1 + else: + warnings.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path") + elif vmincut: + done = 1 + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmin))) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax)) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax)) + privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin)) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, privatedata.vfromvalue))) + privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue)) + privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue)) + privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue)) + elif vmaxcut: + done = 1 + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vmax))) + privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin)) + privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin)) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax)) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmax, vvalue))) + privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue)) + privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue)) + privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue)) + elif privatedata.vfromvaluecut: + done = 1 + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmax))) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax)) + privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin)) + privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin)) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmax, privatedata.vfromvalue))) + privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue)) + privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue)) + privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue)) + elif vvaluecut: + done = 1 + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vmin))) + privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin)) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax)) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax)) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, vvalue))) + privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue)) + privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue)) + privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue)) + if not done: + if privatedata.rangeaxisindex: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmin))) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax)) + privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax)) + privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin)) + privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin)) + privatedata.path.append(path.closepath()) + else: + privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, privatedata.vfromvalue))) + privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue)) + privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue)) + privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue)) + privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue)) + privatedata.path.append(path.closepath()) + else: + try: + gap = abs(vmin - privatedata.lastvmax) > self.epsilon + except (ArithmeticError, ValueError, TypeError): + gap = 1 + if (privatedata.lastvvalue is not None and currentvalid and not gap and + (self.steps or (privatedata.lastvvalue-privatedata.vfromvalue)*(vvalue-privatedata.vfromvalue) < 0)): + self.vposline(privatedata, sharedata, graph, + vmin, privatedata.lastvvalue, vvalue) + else: + if privatedata.lastvvalue is not None and currentvalid: + currentbigger = abs(privatedata.lastvvalue-privatedata.vfromvalue) < abs(vvalue-privatedata.vfromvalue) + if privatedata.lastvvalue is not None and (not currentvalid or not currentbigger or gap): + self.vposline(privatedata, sharedata, graph, + privatedata.lastvmax, privatedata.lastvvalue, privatedata.vfromvalue) + if currentvalid: + self.vmoveto(privatedata, sharedata, graph, + vmin, vvalue) + if currentvalid and (privatedata.lastvvalue is None or currentbigger or gap): + self.vmoveto(privatedata, sharedata, graph, + vmin, privatedata.vfromvalue) + self.vposline(privatedata, sharedata, graph, + vmin, privatedata.vfromvalue, vvalue) + if currentvalid: + self.vvalueline(privatedata, sharedata, graph, + vvalue, vmin, vmax) + privatedata.lastvvalue = vvalue + privatedata.lastvmax = vmax + else: + privatedata.lastvvalue = privatedata.lastvmax = None + + def initdrawpoints(self, privatedata, sharedata, graph): + privatedata.path = path.path() + privatedata.lastvvalue = privatedata.lastvmax = None + privatedata.vcurrentpoint = None + privatedata.count = 0 + if self.fromvalue is not None: + valueaxisname = sharedata.poscolumnnames[1-privatedata.rangeaxisindex] + privatedata.vfromvalue = graph.axes[valueaxisname].convert(self.fromvalue) + privatedata.vfromvaluecut = 0 + if privatedata.vfromvalue < 0: + privatedata.vfromvalue = 0 + privatedata.vfromvaluecut = -1 + if privatedata.vfromvalue > 1: + privatedata.vfromvalue = 1 + privatedata.vfromvaluecut = 1 + if self.frompathattrs is not None and privatedata.insertfrompath: + graph.stroke(graph.axes[valueaxisname].vgridpath(privatedata.vfromvalue), + self.defaultfrompathattrs + self.frompathattrs) + else: + privatedata.vfromvalue = 0 + + def drawpoint(self, privatedata, sharedata, graph, point): + if privatedata.autohistogram: + # automatic range handling + privatedata.count += 1 + if privatedata.count == 2: + if privatedata.rangeaxisindex: + privatedata.vrange = sharedata.vpos[1] - privatedata.lastvpos[1] + self.drawvalue(privatedata, sharedata, graph, + privatedata.lastvpos[1] - self.autohistogrampointpos*privatedata.vrange, + privatedata.lastvpos[1] + (1-self.autohistogrampointpos)*privatedata.vrange, + privatedata.lastvpos[0]) + else: + privatedata.vrange = sharedata.vpos[0] - privatedata.lastvpos[0] + self.drawvalue(privatedata, sharedata, graph, + privatedata.lastvpos[0] - self.autohistogrampointpos*privatedata.vrange, + privatedata.lastvpos[0] + (1-self.autohistogrampointpos)*privatedata.vrange, + privatedata.lastvpos[1]) + elif privatedata.count > 2: + if privatedata.rangeaxisindex: + vrange = sharedata.vpos[1] - privatedata.lastvpos[1] + else: + vrange = sharedata.vpos[0] - privatedata.lastvpos[0] + if abs(privatedata.vrange - vrange) > self.epsilon: + raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation") + if privatedata.count > 1: + if privatedata.rangeaxisindex: + self.drawvalue(privatedata, sharedata, graph, + sharedata.vpos[1] - self.autohistogrampointpos*privatedata.vrange, + sharedata.vpos[1] + (1-self.autohistogrampointpos)*privatedata.vrange, + sharedata.vpos[0]) + else: + self.drawvalue(privatedata, sharedata, graph, + sharedata.vpos[0] - self.autohistogrampointpos*privatedata.vrange, + sharedata.vpos[0] + (1-self.autohistogrampointpos)*privatedata.vrange, + sharedata.vpos[1]) + privatedata.lastvpos = sharedata.vpos[:] + else: + if privatedata.rangeaxisindex: + self.drawvalue(privatedata, sharedata, graph, + sharedata.vrange[1][0], sharedata.vrange[1][1], sharedata.vpos[0]) + else: + self.drawvalue(privatedata, sharedata, graph, + sharedata.vrange[0][0], sharedata.vrange[0][1], sharedata.vpos[1]) + + def donedrawpoints(self, privatedata, sharedata, graph): + self.drawvalue(privatedata, sharedata, graph, None, None, None) + if privatedata.lineattrs is not None and len(privatedata.path): + graph.draw(privatedata.path, privatedata.lineattrs) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + if privatedata.lineattrs is not None: + if self.rectkey: + p = path.rect_pt(x_pt, y_pt, width_pt, height_pt) + else: + p = path.line_pt(x_pt, y_pt+0.5*height_pt, x_pt+width_pt, y_pt+0.5*height_pt) + graph.draw(p, privatedata.lineattrs) + + +class barpos(_style): + + providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"] + + defaultfrompathattrs = [] + + def __init__(self, fromvalue=None, frompathattrs=[], epsilon=1e-10): + self.fromvalue = fromvalue + self.frompathattrs = frompathattrs + self.epsilon = epsilon + + def columnnames(self, privatedata, sharedata, graph, columnnames): + sharedata.barposcolumnnames = [] + sharedata.barvalueindex = None + for dimension, axisnames in enumerate(graph.axesnames): + found = 0 + for axisname in axisnames: + if axisname in columnnames: + if sharedata.barvalueindex is not None: + raise ValueError("multiple values") + sharedata.barvalueindex = dimension + sharedata.barposcolumnnames.append(axisname) + found += 1 + if (axisname + "name") in columnnames: + sharedata.barposcolumnnames.append(axisname + "name") + found += 1 + if found > 1: + raise ValueError("multiple names and value") + if not found: + raise ValueError("value/name missing") + if sharedata.barvalueindex is None: + raise ValueError("missing value") + sharedata.vposmissing = [] + return sharedata.barposcolumnnames + + def addsubvalue(self, value, subvalue): + try: + value + "" + except: + try: + return value[0], self.addsubvalue(value[1], subvalue) + except: + return value, subvalue + else: + return value, subvalue + + def adjustaxis(self, privatedata, sharedata, graph, columnname, data): + try: + i = sharedata.barposcolumnnames.index(columnname) + except ValueError: + pass + else: + if i == sharedata.barvalueindex: + if self.fromvalue is not None: + graph.axes[sharedata.barposcolumnnames[i]].adjustaxis([self.fromvalue]) + graph.axes[sharedata.barposcolumnnames[i]].adjustaxis(data) + else: + graph.axes[sharedata.barposcolumnnames[i][:-4]].adjustaxis([self.addsubvalue(x, 0) for x in data]) + graph.axes[sharedata.barposcolumnnames[i][:-4]].adjustaxis([self.addsubvalue(x, 1) for x in data]) + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + privatedata.insertfrompath = selectindex == 0 + + def initdrawpoints(self, privatedata, sharedata, graph): + sharedata.vpos = [None]*(len(sharedata.barposcolumnnames)) + sharedata.vbarrange = [[None for i in xrange(2)] for x in sharedata.barposcolumnnames] + sharedata.stackedbar = sharedata.stackedbardraw = 0 + + if self.fromvalue is not None: + privatedata.vfromvalue = graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex][0]].convert(self.fromvalue) + if privatedata.vfromvalue < 0: + privatedata.vfromvalue = 0 + if privatedata.vfromvalue > 1: + privatedata.vfromvalue = 1 + if self.frompathattrs is not None and privatedata.insertfrompath: + graph.stroke(graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex][0]].vgridpath(privatedata.vfromvalue), + self.defaultfrompathattrs + self.frompathattrs) + else: + privatedata.vfromvalue = 0 + + def drawpoint(self, privatedata, sharedata, graph, point): + sharedata.vposavailable = sharedata.vposvalid = 1 + for i, barname in enumerate(sharedata.barposcolumnnames): + if i == sharedata.barvalueindex: + sharedata.vbarrange[i][0] = privatedata.vfromvalue + sharedata.lastbarvalue = point[barname] + try: + sharedata.vpos[i] = sharedata.vbarrange[i][1] = graph.axes[barname].convert(sharedata.lastbarvalue) + except (ArithmeticError, ValueError, TypeError): + sharedata.vpos[i] = sharedata.vbarrange[i][1] = None + else: + for j in xrange(2): + try: + sharedata.vbarrange[i][j] = graph.axes[barname[:-4]].convert(self.addsubvalue(point[barname], j)) + except (ArithmeticError, ValueError, TypeError): + sharedata.vbarrange[i][j] = None + try: + sharedata.vpos[i] = 0.5*(sharedata.vbarrange[i][0]+sharedata.vbarrange[i][1]) + except (ArithmeticError, ValueError, TypeError): + sharedata.vpos[i] = None + if sharedata.vpos[i] is None: + sharedata.vposavailable = sharedata.vposvalid = 0 + elif sharedata.vpos[i] < -self.epsilon or sharedata.vpos[i] > 1+self.epsilon: + sharedata.vposvalid = 0 + +registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]) + + +class stackedbarpos(_style): + + # provides no additional data, but needs some data (and modifies some of them) + needsdata = ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"] + + def __init__(self, stackname, addontop=0, epsilon=1e-10): + self.stackname = stackname + self.epsilon = epsilon + self.addontop = addontop + + def columnnames(self, privatedata, sharedata, graph, columnnames): + if self.stackname not in columnnames: + raise ValueError("column '%s' missing" % self.stackname) + return [self.stackname] + + def adjustaxis(self, privatedata, sharedata, graph, columnname, data): + if columnname == self.stackname: + graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex]].adjustaxis(data) + + def initdrawpoints(self, privatedata, sharedata, graph): + if sharedata.stackedbardraw: # do not count the start bar when not gets painted + sharedata.stackedbar += 1 + + def drawpoint(self, privatedata, sharedata, graph, point): + sharedata.vbarrange[sharedata.barvalueindex][0] = sharedata.vbarrange[sharedata.barvalueindex][1] + if self.addontop: + try: + sharedata.lastbarvalue += point[self.stackname] + except (ArithmeticError, ValueError, TypeError): + sharedata.lastbarvalue = None + else: + sharedata.lastbarvalue = point[self.stackname] + try: + sharedata.vpos[sharedata.barvalueindex] = sharedata.vbarrange[sharedata.barvalueindex][1] = graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex]].convert(sharedata.lastbarvalue) + except (ArithmeticError, ValueError, TypeError): + sharedata.vpos[sharedata.barvalueindex] = sharedata.vbarrange[sharedata.barvalueindex][1] = None + sharedata.vposavailable = sharedata.vposvalid = 0 + else: + if not sharedata.vposavailable or not sharedata.vposvalid: + sharedata.vposavailable = sharedata.vposvalid = 1 + for v in sharedata.vpos: + if v is None: + sharedata.vposavailable = sharedata.vposvalid = 0 + break + if v < -self.epsilon or v > 1+self.epsilon: + sharedata.vposvalid = 0 + + +class bar(_style): + + needsdata = ["vbarrange"] + + defaultbarattrs = [color.gradient.Rainbow, deco.stroked([color.grey.black])] + + def __init__(self, barattrs=[]): + self.barattrs = barattrs + + def columnnames(self, privatedata, sharedata, graph, columnnames): + if len(graph.axesnames) != 2: + raise TypeError("bar style restricted on two-dimensional graphs") + return [] + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + privatedata.barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal) + + def initdrawpoints(self, privatedata, sharedata, graph): + privatedata.rectcanvas = graph.insert(canvas.canvas()) + sharedata.stackedbardraw = 1 + privatedata.stackedbar = sharedata.stackedbar + + def drawpointfill(self, privatedata, p): + if p: + privatedata.rectcanvas.fill(p, privatedata.barattrs) + + def drawpoint(self, privatedata, sharedata, graph, point): + xvmin = sharedata.vbarrange[0][0] + xvmax = sharedata.vbarrange[0][1] + yvmin = sharedata.vbarrange[1][0] + yvmax = sharedata.vbarrange[1][1] + try: + if xvmin > xvmax: + xvmin, xvmax = xvmax, xvmin + except: + pass + try: + if yvmin > yvmax: + yvmin, yvmax = yvmax, yvmin + except: + pass + if (xvmin is not None and xvmin < 1 and + xvmax is not None and xvmax > 0 and + yvmin is not None and yvmin < 1 and + yvmax is not None and yvmax > 0): + if xvmin < 0: + xvmin = 0 + elif xvmax > 1: + xvmax = 1 + if yvmin < 0: + yvmin = 0 + elif yvmax > 1: + yvmax = 1 + p = graph.vgeodesic(xvmin, yvmin, xvmax, yvmin) + p.append(graph.vgeodesic_el(xvmax, yvmin, xvmax, yvmax)) + p.append(graph.vgeodesic_el(xvmax, yvmax, xvmin, yvmax)) + p.append(graph.vgeodesic_el(xvmin, yvmax, xvmin, yvmin)) + p.append(path.closepath()) + self.drawpointfill(privatedata, p) + else: + self.drawpointfill(privatedata, None) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + selectindex = privatedata.stackedbar + selecttotal = sharedata.stackedbar + 1 + graph.fill(path.rect_pt(x_pt + width_pt*selectindex/float(selecttotal), y_pt, width_pt/float(selecttotal), height_pt), privatedata.barattrs) + + +class changebar(bar): + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + if selecttotal != 1: + raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.") + + def initdrawpoints(self, privatedata, sharedata, graph): + bar.initdrawpoints(self, privatedata, sharedata, graph) + privatedata.bars = [] + + def drawpointfill(self, privatedata, p): + privatedata.bars.append(p) + + def donedrawpoints(self, privatedata, sharedata, graph): + selecttotal = len(privatedata.bars) + for selectindex, p in enumerate(privatedata.bars): + if p: + barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal) + privatedata.rectcanvas.fill(p, barattrs) + + def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt): + raise RuntimeError("Style currently doesn't provide a graph key") + + +class gridpos(_style): + + needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid"] + providesdata = ["values1", "values2", "data12", "data21", "index1", "index2"] + + def __init__(self, index1=0, index2=1, epsilon=1e-10): + self.index1 = index1 + self.index2 = index2 + self.epsilon = epsilon + + def initdrawpoints(self, privatedata, sharedata, graph): + sharedata.index1 = self.index1 + sharedata.index2 = self.index2 + sharedata.values1 = {} + sharedata.values2 = {} + sharedata.data12 = {} + sharedata.data21 = {} + + def drawpoint(self, privatedata, sharedata, graph, point): + if sharedata.vposavailable: + sharedata.value1 = sharedata.vpos[self.index1] + sharedata.value2 = sharedata.vpos[self.index2] + if not sharedata.values1.has_key(sharedata.value1): + for hasvalue in sharedata.values1.keys(): + if hasvalue - self.epsilon <= sharedata.value1 <= hasvalue + self.epsilon: + sharedata.value1 = hasvalue + break + else: + sharedata.values1[sharedata.value1] = 1 + if not sharedata.values2.has_key(sharedata.value2): + for hasvalue in sharedata.values2.keys(): + if hasvalue - self.epsilon <= sharedata.value2 <= hasvalue + self.epsilon: + sharedata.value2 = hasvalue + break + else: + sharedata.values2[sharedata.value2] = 1 + data = sharedata.vposavailable, sharedata.vposvalid, sharedata.vpos[:] + sharedata.data12.setdefault(sharedata.value1, {})[sharedata.value2] = data + sharedata.data21.setdefault(sharedata.value2, {})[sharedata.value1] = data + +registerdefaultprovider(gridpos(), gridpos.providesdata) + + +class grid(_line, _style): + + needsdata = ["values1", "values2", "data12", "data21"] + + defaultgridattrs = [line.changelinestyle] + + def __init__(self, gridlines1=1, gridlines2=1, gridattrs=[]): + self.gridlines1 = gridlines1 + self.gridlines2 = gridlines2 + self.gridattrs = gridattrs + + def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal): + if self.gridattrs is not None: + privatedata.gridattrs = attr.selectattrs(self.defaultgridattrs + self.gridattrs, selectindex, selecttotal) + else: + privatedata.gridattrs = None + + def donedrawpoints(self, privatedata, sharedata, graph): + values1 = sharedata.values1.keys() + values1.sort() + values2 = sharedata.values2.keys() + values2.sort() + if self.gridlines1: + for value2 in values2: + data1 = sharedata.data21[value2] + self.initpointstopath(privatedata) + for value1 in values1: + try: + data = data1[value1] + except KeyError: + self.addinvalid(privatedata) + else: + self.addpoint(privatedata, graph.vpos_pt, *data) + p = self.donepointstopath(privatedata) + if len(p): + graph.stroke(p, privatedata.gridattrs) + if self.gridlines2: + for value1 in values1: + data2 = sharedata.data12[value1] + self.initpointstopath(privatedata) + for value2 in values2: + try: + data = data2[value2] + except KeyError: + self.addinvalid(privatedata) + else: + self.addpoint(privatedata, graph.vpos_pt, *data) + p = self.donepointstopath(privatedata) + if len(p): + graph.stroke(p, privatedata.gridattrs) + + +class surface(_style): + + needsdata = ["values1", "values2", "data12", "data21"] + + def __init__(self, colorname="color", gradient=color.gradient.Grey, mincolor=None, maxcolor=None, + gridlines1=0.05, gridlines2=0.05, gridcolor=None, + backcolor=color.gray.black): + self.colorname = colorname + self.gradient = gradient + self.mincolor = mincolor + self.maxcolor = maxcolor + self.gridlines1 = gridlines1 + self.gridlines2 = gridlines2 + self.gridcolor = gridcolor + self.backcolor = backcolor + + colorspacestring = gradient.getcolor(0).colorspacestring() + if self.gridcolor is not None and self.gridcolor.colorspacestring() != colorspacestring: + raise RuntimeError("colorspace mismatch (gradient/grid)") + if self.backcolor is not None and self.backcolor.colorspacestring() != colorspacestring: + raise RuntimeError("colorspace mismatch (gradient/back)") + + def midvalue(self, v1, v2, v3, v4): + return [0.25*sum(values) for values in zip(v1, v2, v3, v4)] + + def midcolor(self, c1, c2, c3, c4): + return 0.25*(c1+c2+c3+c4) + + def lightning(self, angle, zindex): + if angle < 0 and self.backcolor is not None: + return self.backcolor + return self.gradient.getcolor(0.7-0.4*abs(angle)+0.1*zindex) + + def columnnames(self, privatedata, sharedata, graph, columnnames): + privatedata.colorize = self.colorname in columnnames + if privatedata.colorize: + return [self.colorname] + return [] + + def initdrawpoints(self, privatedata, sharedata, graph): + privatedata.colors = {} + privatedata.mincolor = privatedata.maxcolor = None + + def drawpoint(self, privatedata, sharedata, graph, point): + if privatedata.colorize: + try: + color = point[self.colorname] + 0 + except: + pass + else: + privatedata.colors.setdefault(sharedata.value1, {})[sharedata.value2] = color + if privatedata.mincolor is None or color < privatedata.mincolor: + privatedata.mincolor = color + if privatedata.mincolor is None or privatedata.maxcolor < color: + privatedata.maxcolor = color + + def donedrawpoints(self, privatedata, sharedata, graph): + v1 = [0]*len(graph.axesnames) + v2 = [0]*len(graph.axesnames) + v3 = [0]*len(graph.axesnames) + v4 = [0]*len(graph.axesnames) + v1[sharedata.index2] = 0.5 + v2[sharedata.index1] = 0.5 + v3[sharedata.index1] = 0.5 + v3[sharedata.index2] = 1 + v4[sharedata.index1] = 1 + v4[sharedata.index2] = 0.5 + sortElements = [-graph.vzindex(*v1), + -graph.vzindex(*v2), + -graph.vzindex(*v3), + -graph.vzindex(*v4)] + + values1 = sharedata.values1.keys() + values1.sort() + v1 = [0]*len(graph.axesnames) + v2 = [0]*len(graph.axesnames) + v1[sharedata.index1] = -1 + v2[sharedata.index1] = 1 + sign = 1 + if graph.vzindex(*v1) < graph.vzindex(*v2): + values1.reverse() + sign *= -1 + sortElements = [sortElements[3], sortElements[1], sortElements[2], sortElements[0]] + + values2 = sharedata.values2.keys() + values2.sort() + v1 = [0]*len(graph.axesnames) + v2 = [0]*len(graph.axesnames) + v1[sharedata.index2] = -1 + v2[sharedata.index2] = 1 + if graph.vzindex(*v1) < graph.vzindex(*v2): + values2.reverse() + sign *= -1 + sortElements = [sortElements[0], sortElements[2], sortElements[1], sortElements[3]] + + sortElements = [(zindex, i) for i, zindex in enumerate(sortElements)] + sortElements.sort() + + if self.mincolor is not None: + mincolor = self.mincolor + if self.maxcolor is not None: + maxcolor = self.maxcolor + nodes = [] + elements = [] + for value1a, value1b in zip(values1[:-1], values1[1:]): + for value2a, value2b in zip(values2[:-1], values2[1:]): + try: + available1, valid1, v1 = sharedata.data12[value1a][value2a] + available2, valid2, v2 = sharedata.data12[value1a][value2b] + available3, valid3, v3 = sharedata.data12[value1b][value2a] + available4, valid4, v4 = sharedata.data12[value1b][value2b] + except KeyError: + continue + if not available1 or not available2 or not available3 or not available4: + continue + if not valid1 or not valid2 or not valid3 or not valid4: + warnings.warn("surface elements partially outside of the graph are (currently) skipped completely") + continue + def shrink(index, v1, v2, by): + v1 = v1[:] + v2 = v2[:] + for i in builtinrange(3): + if i != index: + v1[i], v2[i] = v1[i] + by*(v2[i]-v1[i]), v2[i] + by*(v1[i]-v2[i]) + return v1, v2 + v1f, v2f, v3f, v4f = v1, v2, v3, v4 + if self.gridcolor is not None and self.gridlines1: + v1, v2 = shrink(sharedata.index1, v1, v2, self.gridlines1) + v3, v4 = shrink(sharedata.index1, v3, v4, self.gridlines1) + if self.gridcolor is not None and self.gridlines2: + v1, v3 = shrink(sharedata.index2, v1, v3, self.gridlines2) + v2, v4 = shrink(sharedata.index2, v2, v4, self.gridlines2) + v5 = self.midvalue(v1, v2, v3, v4) + x1_pt, y1_pt = graph.vpos_pt(*v1) + x2_pt, y2_pt = graph.vpos_pt(*v2) + x3_pt, y3_pt = graph.vpos_pt(*v3) + x4_pt, y4_pt = graph.vpos_pt(*v4) + x5_pt, y5_pt = graph.vpos_pt(*v5) + if privatedata.colorize: + def colorfromgradient(c): + return self.gradient.getcolor((c - privatedata.mincolor) / + float(privatedata.maxcolor - privatedata.mincolor)) + c1 = privatedata.colors[value1a][value2a] + c2 = privatedata.colors[value1a][value2b] + c3 = privatedata.colors[value1b][value2a] + c4 = privatedata.colors[value1b][value2b] + c5 = self.midcolor(c1, c2, c3, c4) + c1a = c1b = colorfromgradient(c1) + c2a = c2c = colorfromgradient(c2) + c3b = c3d = colorfromgradient(c3) + c4c = c4d = colorfromgradient(c4) + c5a = c5b = c5c = c5d = colorfromgradient(c5) + if self.backcolor is not None and sign*graph.vangle(*(v1+v2+v5)) < 0: + c1a = c2a = c5a = self.backcolor + if self.backcolor is not None and sign*graph.vangle(*(v3+v1+v5)) < 0: + c3b = c1b = c5b = self.backcolor + if self.backcolor is not None and sign*graph.vangle(*(v2+v4+v5)) < 0: + c2c = c4c = c5c = self.backcolor + if self.backcolor is not None and sign*graph.vangle(*(v4+v3+v5)) < 0: + c4d = c3d = c5d = self.backcolor + else: + zindex = graph.vzindex(*v5) + c1a = c2a = c5a = self.lightning(sign*graph.vangle(*(v1+v2+v5)), zindex) + c3b = c1b = c5b = self.lightning(sign*graph.vangle(*(v3+v1+v5)), zindex) + c2c = c4c = c5c = self.lightning(sign*graph.vangle(*(v2+v4+v5)), zindex) + c4d = c3d = c5d = self.lightning(sign*graph.vangle(*(v4+v3+v5)), zindex) + for zindex, i in sortElements: + if i == 0: + elements.append(mesh.element((mesh.node_pt((x1_pt, y1_pt), c1a), + mesh.node_pt((x2_pt, y2_pt), c2a), + mesh.node_pt((x5_pt, y5_pt), c5a)))) + if self.gridcolor is not None and self.gridlines2: + elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v1f), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v2), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v1), self.gridcolor)))) + elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v1f), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v2), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v2f), self.gridcolor)))) + elif i == 1: + elements.append(mesh.element((mesh.node_pt((x3_pt, y3_pt), c3b), + mesh.node_pt((x1_pt, y1_pt), c1b), + mesh.node_pt((x5_pt, y5_pt), c5b)))) + if self.gridcolor is not None and self.gridlines1: + elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v1f), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v3), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v1), self.gridcolor)))) + elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v1f), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v3), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v3f), self.gridcolor)))) + elif i == 2: + elements.append(mesh.element((mesh.node_pt((x2_pt, y2_pt), c2c), + mesh.node_pt((x4_pt, y4_pt), c4c), + mesh.node_pt((x5_pt, y5_pt), c5c)))) + if self.gridcolor is not None and self.gridlines1: + elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v2f), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v4), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v2), self.gridcolor)))) + elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v2f), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v4), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v4f), self.gridcolor)))) + elif i == 3: + elements.append(mesh.element((mesh.node_pt((x4_pt, y4_pt), c4d), + mesh.node_pt((x3_pt, y3_pt), c3d), + mesh.node_pt((x5_pt, y5_pt), c5d)))) + if self.gridcolor is not None and self.gridlines2: + elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v3f), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v4), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v3), self.gridcolor)))) + elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v3f), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v4), self.gridcolor), + mesh.node_pt(graph.vpos_pt(*v4f), self.gridcolor)))) + m = mesh.mesh(elements, check=0) + graph.insert(m) diff --git a/compiler/gdsMill/pyx/lfs/10pt.lfs b/compiler/gdsMill/pyx/lfs/10pt.lfs new file mode 100644 index 00000000..2a4f4bed --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/10pt.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="10pt" +% \latexclassname="article" +% \latexclassopt="10pt" +% \latexinit="" +\def\tiny{\font\pyxfont=cmr5\pyxfont\font\pyxfonttfa=cmr5\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr5\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi5\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi5\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy5\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy5\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmr7\pyxfont\font\pyxfonttfa=cmr7\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr5\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi5\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy5\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmr8\pyxfont\font\pyxfonttfa=cmr8\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi8\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy8\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmr9\pyxfont\font\pyxfonttfa=cmr9\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi9\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy9\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmr10\pyxfont\font\pyxfonttfa=cmr10\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmr12\pyxfont\font\pyxfonttfa=cmr12\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 12.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmr12 at 14.4pt\pyxfont\font\pyxfonttfa=cmr12 at 14.4pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 14.4pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 14.4pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmr17\pyxfont\font\pyxfonttfa=cmr17\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 17.28pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 17.28pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 12.0pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmr17 at 20.74pt\pyxfont\font\pyxfonttfa=cmr17 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12 at 14.4pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr12\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 14.4pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 14.4pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 12.0pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmr17 at 24.88pt\pyxfont\font\pyxfonttfa=cmr17 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr17 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr17\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12 at 17.28pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 17.28pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/10ptex.lfs b/compiler/gdsMill/pyx/lfs/10ptex.lfs new file mode 100644 index 00000000..77eaca8e --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/10ptex.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="10ptex" +% \latexclassname="article" +% \latexclassopt="10pt" +% \latexinit="\usepackage{exscale}" +\def\tiny{\font\pyxfont=cmr5\pyxfont\font\pyxfonttfa=cmr5\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr5\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi5\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi5\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy5\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy5\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmr7\pyxfont\font\pyxfonttfa=cmr7\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr5\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi5\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy5\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmr8\pyxfont\font\pyxfonttfa=cmr8\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi8\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy8\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex8\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmr9\pyxfont\font\pyxfonttfa=cmr9\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi9\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy9\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex9\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmr10\pyxfont\font\pyxfonttfa=cmr10\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmr12\pyxfont\font\pyxfonttfa=cmr12\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 12.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 12.0pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex8\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmr12 at 14.4pt\pyxfont\font\pyxfonttfa=cmr12 at 14.4pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 14.4pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 14.4pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 14.4pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmr17\pyxfont\font\pyxfonttfa=cmr17\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 17.28pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 17.28pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 12.0pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 17.28pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 12.0pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmr17 at 20.74pt\pyxfont\font\pyxfonttfa=cmr17 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12 at 14.4pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr12\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 14.4pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 14.4pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 12.0pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 20.74pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 14.4pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 12.0pt\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmr17 at 24.88pt\pyxfont\font\pyxfonttfa=cmr17 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr17 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr17\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12 at 17.28pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 17.28pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 24.88pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 20.74pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 17.28pt\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/11pt.lfs b/compiler/gdsMill/pyx/lfs/11pt.lfs new file mode 100644 index 00000000..2d0bc9c0 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/11pt.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="11pt" +% \latexclassname="article" +% \latexclassopt="11pt" +% \latexinit="" +\def\tiny{\font\pyxfont=cmr6\pyxfont\font\pyxfonttfa=cmr6\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr5\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi6\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi5\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy6\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy5\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmr8\pyxfont\font\pyxfonttfa=cmr8\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi8\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy8\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmr9\pyxfont\font\pyxfonttfa=cmr9\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi9\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy9\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmr10\pyxfont\font\pyxfonttfa=cmr10\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmr10 at 10.95pt\pyxfont\font\pyxfonttfa=cmr10 at 10.95pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 10.95pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 10.95pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmr12\pyxfont\font\pyxfonttfa=cmr12\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 12.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmr12 at 14.4pt\pyxfont\font\pyxfonttfa=cmr12 at 14.4pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 14.4pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 14.4pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmr17\pyxfont\font\pyxfonttfa=cmr17\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 17.28pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 17.28pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 12.0pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmr17 at 20.74pt\pyxfont\font\pyxfonttfa=cmr17 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12 at 14.4pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr12\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 14.4pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 14.4pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 12.0pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmr17 at 24.88pt\pyxfont\font\pyxfonttfa=cmr17 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr17 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr17\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12 at 17.28pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 17.28pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/11ptex.lfs b/compiler/gdsMill/pyx/lfs/11ptex.lfs new file mode 100644 index 00000000..507361a5 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/11ptex.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="11ptex" +% \latexclassname="article" +% \latexclassopt="11pt" +% \latexinit="\usepackage{exscale}" +\def\tiny{\font\pyxfont=cmr6\pyxfont\font\pyxfonttfa=cmr6\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr5\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi6\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi5\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy6\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy5\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmr8\pyxfont\font\pyxfonttfa=cmr8\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi8\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy8\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex8\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmr9\pyxfont\font\pyxfonttfa=cmr9\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi9\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy9\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex9\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmr10\pyxfont\font\pyxfonttfa=cmr10\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmr10 at 10.95pt\pyxfont\font\pyxfonttfa=cmr10 at 10.95pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 10.95pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 10.95pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 10.95pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex8\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmr12\pyxfont\font\pyxfonttfa=cmr12\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 12.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 12.0pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex8\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmr12 at 14.4pt\pyxfont\font\pyxfonttfa=cmr12 at 14.4pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 14.4pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 14.4pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 14.4pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmr17\pyxfont\font\pyxfonttfa=cmr17\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 17.28pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 17.28pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 12.0pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 17.28pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 12.0pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmr17 at 20.74pt\pyxfont\font\pyxfonttfa=cmr17 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12 at 14.4pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr12\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 14.4pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 14.4pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 12.0pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 20.74pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 14.4pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 12.0pt\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmr17 at 24.88pt\pyxfont\font\pyxfonttfa=cmr17 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr17 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr17\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12 at 17.28pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 17.28pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 24.88pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 20.74pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 17.28pt\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/12pt.lfs b/compiler/gdsMill/pyx/lfs/12pt.lfs new file mode 100644 index 00000000..c06109d8 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/12pt.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="12pt" +% \latexclassname="article" +% \latexclassopt="12pt" +% \latexinit="" +\def\tiny{\font\pyxfont=cmr6\pyxfont\font\pyxfonttfa=cmr6\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr5\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi6\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi5\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy6\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy5\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmr8\pyxfont\font\pyxfonttfa=cmr8\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi8\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy8\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmr10\pyxfont\font\pyxfonttfa=cmr10\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmr10 at 10.95pt\pyxfont\font\pyxfonttfa=cmr10 at 10.95pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 10.95pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 10.95pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmr12\pyxfont\font\pyxfonttfa=cmr12\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 12.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmr12 at 14.4pt\pyxfont\font\pyxfonttfa=cmr12 at 14.4pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 14.4pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 14.4pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmr17\pyxfont\font\pyxfonttfa=cmr17\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 17.28pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 17.28pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 12.0pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmr17 at 20.74pt\pyxfont\font\pyxfonttfa=cmr17 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12 at 14.4pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr12\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 14.4pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 14.4pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 12.0pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmr17 at 24.88pt\pyxfont\font\pyxfonttfa=cmr17 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr17 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr17\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12 at 17.28pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 17.28pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmr17 at 24.88pt\pyxfont\font\pyxfonttfa=cmr17 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr17 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr17\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12 at 17.28pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 17.28pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/12ptex.lfs b/compiler/gdsMill/pyx/lfs/12ptex.lfs new file mode 100644 index 00000000..d97d9996 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/12ptex.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="12ptex" +% \latexclassname="article" +% \latexclassopt="12pt" +% \latexinit="\usepackage{exscale}" +\def\tiny{\font\pyxfont=cmr6\pyxfont\font\pyxfonttfa=cmr6\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr5\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi6\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi5\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy6\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy5\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmr8\pyxfont\font\pyxfonttfa=cmr8\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr6\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi8\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi6\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy8\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy6\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex8\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmr10\pyxfont\font\pyxfonttfa=cmr10\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr5\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi5\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy5\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmr10 at 10.95pt\pyxfont\font\pyxfonttfa=cmr10 at 10.95pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 10.95pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 10.95pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 10.95pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex8\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmr12\pyxfont\font\pyxfonttfa=cmr12\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr8\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr6\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi8\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi6\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 12.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy8\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy6\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 12.0pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex8\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmr12 at 14.4pt\pyxfont\font\pyxfonttfa=cmr12 at 14.4pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 14.4pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 14.4pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 14.4pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmr17\pyxfont\font\pyxfonttfa=cmr17\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 17.28pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 17.28pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 12.0pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 17.28pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 12.0pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmr17 at 20.74pt\pyxfont\font\pyxfonttfa=cmr17 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr12 at 14.4pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr12\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 14.4pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 14.4pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 12.0pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 20.74pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 14.4pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 12.0pt\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmr17 at 24.88pt\pyxfont\font\pyxfonttfa=cmr17 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr17 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr17\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12 at 17.28pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 17.28pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 24.88pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 20.74pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 17.28pt\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmr17 at 24.88pt\pyxfont\font\pyxfonttfa=cmr17 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr17 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr17\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi12 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi12 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi12 at 17.28pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 17.28pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 24.88pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 20.74pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 17.28pt\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/createlfs.py b/compiler/gdsMill/pyx/lfs/createlfs.py new file mode 100644 index 00000000..a1c474f9 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/createlfs.py @@ -0,0 +1,37 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002 Jörg Lehmann +# Copyright (C) 2002 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import os + +styles = (("10pt", "article", "10pt", "" , ), + ("11pt", "article", "11pt", "" , ), + ("12pt", "article", "12pt", "" , ), + ("10ptex", "article", "10pt", "\\usepackage{exscale}" , ), + ("11ptex", "article", "11pt", "\\usepackage{exscale}" , ), + ("12ptex", "article", "12pt", "\\usepackage{exscale}" , ), + ("foils17pt", "foils", "17pt", "" , ), + ("foils20pt", "foils", "20pt", "" , ), + ("foils25pt", "foils", "25pt", "" , ), + ("foils30pt", "foils", "30pt", "" , ), ) + +for style in styles: + os.system("echo \'%s\n%s\n%s\n%s\'|latex createlfs.tex" % style) diff --git a/compiler/gdsMill/pyx/lfs/createlfs.tex b/compiler/gdsMill/pyx/lfs/createlfs.tex new file mode 100644 index 00000000..2bc6038e --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/createlfs.tex @@ -0,0 +1,98 @@ +% Copyright (C) 2002 Jörg Lehmann +% Copyright (C) 2002 André Wobst +% +% This file is part of PyX (http://pyx.sourceforge.net/). +% +% PyX 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. +% +% PyX 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 PyX; if not, write to the Free Software +% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +\endlinechar=-1 % don't add tailing space while \read + +\message{latex style name (e.g. 12ptex)? } +\read-1 to\latexstylename + +\message{latex class name (e.g. article)? } +\read-1 to\latexclassname + +\message{latex class options (e.g. 12pt)? } +\read-1 to\latexclassopt + +\message{initial commands (e.g. \string\usepackage\string{exscale\string})? } +\read-1 to\latexinit + +\endlinechar=13 + +\newwrite\myfile + +\newbox\mybox + +\documentclass[\latexclassopt]{\latexclassname} + +\latexinit + +\newcommand{\writefontsize}[1]{ + \setbox\mybox=\hbox{#1 + a $a$ + \immediate\write\myfile{% + \string\def\string#1{% + \string\font\string\pyxfont=\fontname\font + \string\pyxfont + \string\font\string\pyxfonttfa=\fontname\textfont0 + \string\textfont0=\string\pyxfonttfa + \string\font\string\pyxfontsfa=\fontname\scriptfont0 + \string\scriptfont0=\string\pyxfontsfa + \string\font\string\pyxfontssfa=\fontname\scriptscriptfont0 + \string\scriptscriptfont0=\string\pyxfontssfa + \string\font\string\pyxfonttfb=\fontname\textfont1 + \string\textfont1=\string\pyxfonttfb + \string\font\string\pyxfontsfb=\fontname\scriptfont1 + \string\scriptfont1=\string\pyxfontsfb + \string\font\string\pyxfontssfb=\fontname\scriptscriptfont1 + \string\scriptscriptfont1=\string\pyxfontssfb + \string\font\string\pyxfonttfc=\fontname\textfont2 + \string\textfont2=\string\pyxfonttfc + \string\font\string\pyxfontsfc=\fontname\scriptfont2 + \string\scriptfont2=\string\pyxfontsfc + \string\font\string\pyxfontssfc=\fontname\scriptscriptfont2 + \string\scriptscriptfont2=\string\pyxfontssfc + \string\font\string\pyxfonttfd=\fontname\textfont3 + \string\textfont3=\string\pyxfonttfd + \string\font\string\pyxfontsfd=\fontname\scriptfont3 + \string\scriptfont3=\string\pyxfontsfd + \string\font\string\pyxfontssfd=\fontname\scriptscriptfont3 + \string\scriptscriptfont3=\string\pyxfontssfd + }% + } + } +} + +\begin{document} +\immediate\openout\myfile=\latexstylename.lfs +{\catcode`\%=12\immediate\write\myfile{% This automatically generated file is part of PyX (http://pyx.sourceforge.net/).}} +{\catcode`\%=12\immediate\write\myfile{% \string\latexstylename="\expandafter\string\latexstylename"}} +{\catcode`\%=12\immediate\write\myfile{% \string\latexclassname="\expandafter\string\latexclassname"}} +{\catcode`\%=12\immediate\write\myfile{% \string\latexclassopt="\expandafter\string\latexclassopt"}} +{\catcode`\%=12\immediate\write\myfile{% \string\latexinit="\expandafter\string\latexinit"}} +\writefontsize{\tiny} +\writefontsize{\scriptsize} +\writefontsize{\footnotesize} +\writefontsize{\small} +\writefontsize{\normalsize} +\writefontsize{\large} +\writefontsize{\Large} +\writefontsize{\LARGE} +\writefontsize{\huge} +\writefontsize{\Huge} +\immediate\closeout\myfile +\end{document} diff --git a/compiler/gdsMill/pyx/lfs/foils17pt.lfs b/compiler/gdsMill/pyx/lfs/foils17pt.lfs new file mode 100644 index 00000000..b8a3b8e6 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/foils17pt.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="foils17pt" +% \latexclassname="foils" +% \latexclassopt="17pt" +% \latexinit="" +\def\tiny{\font\pyxfont=cmss10 at 12.0pt\pyxfont\font\pyxfonttfa=cmr7 at 12.1pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 12.1pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 12.1pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 12.1pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmss10 at 12.0pt\pyxfont\font\pyxfonttfa=cmr7 at 12.1pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 12.1pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 12.1pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 12.1pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmss10 at 12.0pt\pyxfont\font\pyxfonttfa=cmr7 at 12.1pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 12.1pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 12.1pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 12.1pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmss10 at 14.4pt\pyxfont\font\pyxfonttfa=cmr7 at 14.5pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 14.5pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 14.5pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 14.5pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmss10 at 17.28pt\pyxfont\font\pyxfonttfa=cmr7 at 17.38pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 17.38pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 17.38pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 17.38pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmss10 at 20.74pt\pyxfont\font\pyxfonttfa=cmr10 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 14.5pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 14.5pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 14.5pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 20.74pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 14.5pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmss10 at 24.88pt\pyxfont\font\pyxfonttfa=cmr10 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 17.38pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 14.5pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 17.38pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 14.5pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 17.38pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 14.5pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 24.88pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 17.38pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 14.5pt\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmss10 at 29.86pt\pyxfont\font\pyxfonttfa=cmr10 at 29.86pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 17.38pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 29.86pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 17.38pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 29.86pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 17.38pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 29.86pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 20.74pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 17.38pt\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmss10 at 35.83pt\pyxfont\font\pyxfonttfa=cmr10 at 35.83pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 24.88pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 20.74pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 35.83pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 24.88pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 20.74pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 35.83pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 24.88pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 20.74pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 35.83pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 24.88pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 20.74pt\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmss10 at 43.0pt\pyxfont\font\pyxfonttfa=cmr10 at 43.0pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 29.86pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 24.88pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 43.0pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 29.86pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 24.88pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 43.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 29.86pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 24.88pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 43.0pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 29.86pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 24.88pt\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/foils20pt.lfs b/compiler/gdsMill/pyx/lfs/foils20pt.lfs new file mode 100644 index 00000000..9f597126 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/foils20pt.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="foils20pt" +% \latexclassname="foils" +% \latexclassopt="20pt" +% \latexinit="" +\def\tiny{\font\pyxfont=cmss10 at 12.0pt\pyxfont\font\pyxfonttfa=cmr7 at 12.1pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 12.1pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 12.1pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 12.1pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmss10 at 12.0pt\pyxfont\font\pyxfonttfa=cmr7 at 12.1pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 12.1pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 12.1pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 12.1pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmss10 at 14.4pt\pyxfont\font\pyxfonttfa=cmr7 at 14.5pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 14.5pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 14.5pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 14.5pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmss10 at 17.28pt\pyxfont\font\pyxfonttfa=cmr7 at 17.38pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 17.38pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 17.38pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 17.38pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmss10 at 20.74pt\pyxfont\font\pyxfonttfa=cmr10 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 14.5pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 14.5pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 14.5pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 20.74pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 14.5pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmss10 at 24.88pt\pyxfont\font\pyxfonttfa=cmr10 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 17.38pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 14.5pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 17.38pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 14.5pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 17.38pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 14.5pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 24.88pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 17.38pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 14.5pt\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmss10 at 29.86pt\pyxfont\font\pyxfonttfa=cmr10 at 29.86pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 17.38pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 29.86pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 17.38pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 29.86pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 17.38pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 29.86pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 20.74pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 17.38pt\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmss10 at 35.83pt\pyxfont\font\pyxfonttfa=cmr10 at 35.83pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 24.88pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 20.74pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 35.83pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 24.88pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 20.74pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 35.83pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 24.88pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 20.74pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 35.83pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 24.88pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 20.74pt\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmss10 at 43.0pt\pyxfont\font\pyxfonttfa=cmr10 at 43.0pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 29.86pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 24.88pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 43.0pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 29.86pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 24.88pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 43.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 29.86pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 24.88pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 43.0pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 29.86pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 24.88pt\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmss10 at 51.6pt\pyxfont\font\pyxfonttfa=cmr10 at 51.6pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 35.83pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 29.86pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 51.6pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 35.83pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 29.86pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 51.6pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 35.83pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 29.86pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 51.6pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 35.83pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 29.86pt\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/foils25pt.lfs b/compiler/gdsMill/pyx/lfs/foils25pt.lfs new file mode 100644 index 00000000..828cf8a2 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/foils25pt.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="foils25pt" +% \latexclassname="foils" +% \latexclassopt="25pt" +% \latexinit="" +\def\tiny{\font\pyxfont=cmss10 at 12.0pt\pyxfont\font\pyxfonttfa=cmr7 at 12.1pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 12.1pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 12.1pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 12.1pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmss10 at 14.4pt\pyxfont\font\pyxfonttfa=cmr7 at 14.5pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 14.5pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 14.5pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 14.5pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmss10 at 17.28pt\pyxfont\font\pyxfonttfa=cmr7 at 17.38pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 17.38pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 17.38pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 17.38pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmss10 at 20.74pt\pyxfont\font\pyxfonttfa=cmr10 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 14.5pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 14.5pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 14.5pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 20.74pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 14.5pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmss10 at 24.88pt\pyxfont\font\pyxfonttfa=cmr10 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 17.38pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 14.5pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 17.38pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 14.5pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 17.38pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 14.5pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 24.88pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 17.38pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 14.5pt\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmss10 at 29.86pt\pyxfont\font\pyxfonttfa=cmr10 at 29.86pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 17.38pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 29.86pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 17.38pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 29.86pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 17.38pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 29.86pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 20.74pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 17.38pt\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmss10 at 35.83pt\pyxfont\font\pyxfonttfa=cmr10 at 35.83pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 24.88pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 20.74pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 35.83pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 24.88pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 20.74pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 35.83pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 24.88pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 20.74pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 35.83pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 24.88pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 20.74pt\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmss10 at 43.0pt\pyxfont\font\pyxfonttfa=cmr10 at 43.0pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 29.86pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 24.88pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 43.0pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 29.86pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 24.88pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 43.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 29.86pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 24.88pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 43.0pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 29.86pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 24.88pt\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmss10 at 51.6pt\pyxfont\font\pyxfonttfa=cmr10 at 51.6pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 35.83pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 29.86pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 51.6pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 35.83pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 29.86pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 51.6pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 35.83pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 29.86pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 51.6pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 35.83pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 29.86pt\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmss10 at 51.6pt\pyxfont\font\pyxfonttfa=cmr10 at 51.6pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 35.83pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 29.86pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 51.6pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 35.83pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 29.86pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 51.6pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 35.83pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 29.86pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 51.6pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 35.83pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 29.86pt\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/lfs/foils30pt.lfs b/compiler/gdsMill/pyx/lfs/foils30pt.lfs new file mode 100644 index 00000000..fcc05d90 --- /dev/null +++ b/compiler/gdsMill/pyx/lfs/foils30pt.lfs @@ -0,0 +1,15 @@ +% This automatically generated file is part of PyX (http://pyx.sourceforge.net/). +% \latexstylename="foils30pt" +% \latexclassname="foils" +% \latexclassopt="30pt" +% \latexinit="" +\def\tiny{\font\pyxfont=cmss10 at 14.4pt\pyxfont\font\pyxfonttfa=cmr7 at 14.5pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 14.5pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 14.5pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 14.5pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\scriptsize{\font\pyxfont=cmss10 at 17.28pt\pyxfont\font\pyxfonttfa=cmr7 at 17.38pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 12.1pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi7 at 17.38pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 12.1pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy7 at 17.38pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 12.1pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex7 at 17.38pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 12.1pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\footnotesize{\font\pyxfont=cmss10 at 20.74pt\pyxfont\font\pyxfonttfa=cmr10 at 20.74pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 14.5pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 12.1pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 20.74pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 14.5pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 12.1pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 20.74pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 14.5pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 12.1pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 20.74pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 14.5pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 12.1pt\scriptscriptfont3=\pyxfontssfd} +\def\small{\font\pyxfont=cmss10 at 24.88pt\pyxfont\font\pyxfonttfa=cmr10 at 24.88pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr7 at 17.38pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 14.5pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 24.88pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi7 at 17.38pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 14.5pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 24.88pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy7 at 17.38pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 14.5pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 24.88pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex7 at 17.38pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 14.5pt\scriptscriptfont3=\pyxfontssfd} +\def\normalsize{\font\pyxfont=cmss10 at 29.86pt\pyxfont\font\pyxfonttfa=cmr10 at 29.86pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 20.74pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr7 at 17.38pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 29.86pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 20.74pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi7 at 17.38pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 29.86pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 20.74pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy7 at 17.38pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 29.86pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 20.74pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex7 at 17.38pt\scriptscriptfont3=\pyxfontssfd} +\def\large{\font\pyxfont=cmss10 at 35.83pt\pyxfont\font\pyxfonttfa=cmr10 at 35.83pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 24.88pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 20.74pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 35.83pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 24.88pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 20.74pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 35.83pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 24.88pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 20.74pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 35.83pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 24.88pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 20.74pt\scriptscriptfont3=\pyxfontssfd} +\def\Large{\font\pyxfont=cmss10 at 43.0pt\pyxfont\font\pyxfonttfa=cmr10 at 43.0pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 29.86pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 24.88pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 43.0pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 29.86pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 24.88pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 43.0pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 29.86pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 24.88pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 43.0pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 29.86pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 24.88pt\scriptscriptfont3=\pyxfontssfd} +\def\LARGE{\font\pyxfont=cmss10 at 51.6pt\pyxfont\font\pyxfonttfa=cmr10 at 51.6pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 35.83pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 29.86pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 51.6pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 35.83pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 29.86pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 51.6pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 35.83pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 29.86pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 51.6pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 35.83pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 29.86pt\scriptscriptfont3=\pyxfontssfd} +\def\huge{\font\pyxfont=cmss10 at 51.6pt\pyxfont\font\pyxfonttfa=cmr10 at 51.6pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 35.83pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 29.86pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 51.6pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 35.83pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 29.86pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 51.6pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 35.83pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 29.86pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 51.6pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 35.83pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 29.86pt\scriptscriptfont3=\pyxfontssfd} +\def\Huge{\font\pyxfont=cmss10 at 51.6pt\pyxfont\font\pyxfonttfa=cmr10 at 51.6pt\textfont0=\pyxfonttfa\font\pyxfontsfa=cmr10 at 35.83pt\scriptfont0=\pyxfontsfa\font\pyxfontssfa=cmr10 at 29.86pt\scriptscriptfont0=\pyxfontssfa\font\pyxfonttfb=cmmi10 at 51.6pt\textfont1=\pyxfonttfb\font\pyxfontsfb=cmmi10 at 35.83pt\scriptfont1=\pyxfontsfb\font\pyxfontssfb=cmmi10 at 29.86pt\scriptscriptfont1=\pyxfontssfb\font\pyxfonttfc=cmsy10 at 51.6pt\textfont2=\pyxfonttfc\font\pyxfontsfc=cmsy10 at 35.83pt\scriptfont2=\pyxfontsfc\font\pyxfontssfc=cmsy10 at 29.86pt\scriptscriptfont2=\pyxfontssfc\font\pyxfonttfd=cmex10 at 51.6pt\textfont3=\pyxfonttfd\font\pyxfontsfd=cmex10 at 35.83pt\scriptfont3=\pyxfontsfd\font\pyxfontssfd=cmex10 at 29.86pt\scriptscriptfont3=\pyxfontssfd} diff --git a/compiler/gdsMill/pyx/mathutils.py b/compiler/gdsMill/pyx/mathutils.py new file mode 100644 index 00000000..1b47af12 --- /dev/null +++ b/compiler/gdsMill/pyx/mathutils.py @@ -0,0 +1,169 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 Michael Schindler +# Copyright (C) 2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import math, types + +# try: +# import Numeric, LinearAlgebra +# _has_numeric = 1 +# except: +# _has_numeric = 0 + + +def sign(x): + """sign of x, i.e. +1 or -1; returns 1 for x == 0""" + if x >= 0: + return 1 + return -1 + + +def asinh(x): + """Return the arc hyperbolic sine of x.""" + return math.log(x+math.sqrt(x*x+1)) + + +def acosh(x): + """Return the arc hyperbolic cosine of x.""" + return math.log(x+math.sqrt(x*x-1)) + + +def _realroots_quadratic(a1, a0): + """gives the real roots of x**2 + a1 * x + a0 = 0""" + D = a1*a1 - 4*a0 + if D < 0: + return [] + SD = math.sqrt(D) + return [0.5 * (-a1 + SD), 0.5 * (-a1 - SD)] + + +def _realroots_cubic(a2, a1, a0): + """gives the real roots of x**3 + a2 * x**2 + a1 * x + a0 = 0""" + # see http://mathworld.wolfram.com/CubicFormula.html for details + + Q = (3*a1 - a2*a2) / 9.0 + R = (9*a2*a1 - 27*a0 - 2*a2*a2*a2) / 54.0 + D = Q*Q*Q + R*R + + if D > 0: # one real and two complex roots + SD = math.sqrt(D) + if R + SD >= 0: + S = (R + SD)**(1/3.0) + else: + S = -(-R - SD)**(1/3.0) + if R - SD >= 0: + T = (R - SD)**(1/3.0) + else: + T = -(SD - R)**(1/3.0) + return [S + T - a2/3.0] + elif D == 0: + if Q == 0: # one real root (R==0) + return [-a2/3.0] + else: # two real roots (R>0, Q<0) + S = -math.sqrt(-Q) + return [2*S - a2/3.0, -S - a2/3.0] + else: # three real roots (Q<0) + SQ = math.sqrt(-Q) + arg = R / (SQ**3) + if arg >= 1: + theta = 0 + elif arg <= -1: + theta = math.pi + else: + theta = math.acos(R/(SQ**3)) + return [2 * SQ * math.cos((theta + 2*2*i*math.pi)/3.0) - a2/3.0 for i in range(3)] + + +def _realroots_quartic(a3, a2, a1, a0): + """gives the real roots of x**4 + a3 * x**3 + a2 * x**2 + a1 * x + a0 = 0""" + # see http://mathworld.wolfram.com/QuarticEquation.html for details + ys = _realroots_cubic(-a2, a1*a3 - 4*a0, 4*a0*a2 - a1*a1 - a0*a3*a3) + ys = [y for y in ys if a3*a3-4*a2+4*y >= 0 and y*y-4*a0 >= 0] + if not ys: + return [] + y1 = min(ys) + if a3*y1-2*a1 < 0: + return (_realroots_quadratic(0.5*(a3+math.sqrt(a3*a3-4*a2+4*y1)), 0.5*(y1-math.sqrt(y1*y1-4*a0))) + + _realroots_quadratic(0.5*(a3-math.sqrt(a3*a3-4*a2+4*y1)), 0.5*(y1+math.sqrt(y1*y1-4*a0)))) + else: + return (_realroots_quadratic(0.5*(a3+math.sqrt(a3*a3-4*a2+4*y1)), 0.5*(y1+math.sqrt(y1*y1-4*a0))) + + _realroots_quadratic(0.5*(a3-math.sqrt(a3*a3-4*a2+4*y1)), 0.5*(y1-math.sqrt(y1*y1-4*a0)))) + + +def realpolyroots(*cs): + """returns the roots of a polynom with given coefficients + + polynomial with coefficients given in cs: + 0 = \sum_i cs[i] * x^(len(cs)-i-1) + """ + if not cs: + return [0] + try: + f = 1.0/cs[0] + cs = [f*c for c in cs[1:]] + except ArithmeticError: + return realpolyroots(*cs[1:]) + else: + n = len(cs) + if n == 0: + return [] + elif n == 1: + return [-cs[0]] + elif n == 2: + return _realroots_quadratic(*cs) + elif n == 3: + return _realroots_cubic(*cs) + elif n == 4: + return _realroots_quartic(*cs) + else: + raise RuntimeError("realpolyroots solver currently limited to polynoms up to the power of 4") + + +# def realpolyroots_eigenvalue(*cs): +# # as realpolyroots but using an equivalent eigenvalue problem +# # (this code is currently used for functional tests only) +# if not _has_numeric: +# raise RuntimeError("realpolyroots_eigenvalue depends on Numeric") +# if not cs: +# return [0] +# try: +# f = 1.0/cs[0] +# cs = [f*c for c in cs[1:]] +# except ArithmeticError: +# return realpolyroots_eigenvalue(*cs[1:]) +# else: +# if not cs: +# return [] +# n = len(cs) +# a = Numeric.zeros((n, n), Numeric.Float) +# for i in range(n-1): +# a[i+1][i] = 1 +# for i in range(n): +# a[0][i] = -cs[i] +# rs = [] +# for r in LinearAlgebra.eigenvalues(a): +# if type(r) == types.ComplexType: +# if not r.imag: +# rs.append(r.real) +# else: +# rs.append(r) +# return rs +# diff --git a/compiler/gdsMill/pyx/mesh.py b/compiler/gdsMill/pyx/mesh.py new file mode 100644 index 00000000..d29d0b51 --- /dev/null +++ b/compiler/gdsMill/pyx/mesh.py @@ -0,0 +1,152 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2006, 2007 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +# Just a quick'n'dirty ascii art (I'll do a nice PyX plot later on): +# +# +# node1 * +# | \ +# | \ neighbor2 +# | \ +# | \ +# neighbor3 |element * node3 +# | / +# | / +# | / neighbor1 +# | / +# node2 * + + +import struct, binascii, zlib +import bbox, canvas, color, pdfwriter, unit + + +class node_pt: + + def __init__(self, coords_pt, value): + self.coords_pt = coords_pt + self.value = value + + +class node(node_pt): + + def __init__(self, coords, value): + node_pt.__init__(self, [unit.topt(coord) for coord in coords], value) + + +class element: + + def __init__(self, nodes, neighbors=None): + self.nodes = nodes + self.neighbors = neighbors + + +def coords24bit_pt(coords_pt, min_pt, max_pt): + return struct.pack(">I", int((coords_pt-min_pt)*16777215.0/(max_pt-min_pt)))[1:] + + +class PDFGenericResource(pdfwriter.PDFobject): + + def __init__(self, type, name, content): + pdfwriter.PDFobject.__init__(self, type, name) + self.content = content + + def write(self, file, writer, registry): + file.write(self.content) + + +class mesh(canvas.canvasitem): + + def __init__(self, elements, check=1): + self.elements = elements + if check: + colorspacestring = "" + for element in elements: + if len(element.nodes) != 3: + raise ValueError("triangular mesh expected") + try: + for node in element.nodes: + if not colorspacestring: + colorspacestring = node.value.colorspacestring() + elif node.value.colorspacestring() != colorspacestring: + raise ValueError("color space mismatch") + except AttributeError: + raise ValueError("gray, rgb or cmyk color values expected") + for node in element.nodes: + if len(node.coords_pt) != 2: + raise ValueError("two dimensional coordinates expected") + + def bbox(self): + return bbox.bbox_pt(min([node.coords_pt[0] for element in self.elements for node in element.nodes]), + min([node.coords_pt[1] for element in self.elements for node in element.nodes]), + max([node.coords_pt[0] for element in self.elements for node in element.nodes]), + max([node.coords_pt[1] for element in self.elements for node in element.nodes])) + + def data(self, bbox): + return "".join(["\000%s%s%s" % (coords24bit_pt(node.coords_pt[0], bbox.llx_pt, bbox.urx_pt), + coords24bit_pt(node.coords_pt[1], bbox.lly_pt, bbox.ury_pt), + node.value.tostring8bit()) + for element in self.elements for node in element.nodes]) + + def processPS(self, file, writer, context, registry, bbox): + thisbbox = self.bbox() + bbox += thisbbox + file.write("""<< /ShadingType 4 +/ColorSpace %s +/BitsPerCoordinate 24 +/BitsPerComponent 8 +/BitsPerFlag 8 +/Decode [%f %f %f %f %s] +/DataSource currentfile /ASCIIHexDecode filter /FlateDecode filter +>> shfill\n""" % (self.elements[0].nodes[0].value.colorspacestring(), + thisbbox.llx_pt, thisbbox.urx_pt, thisbbox.lly_pt, thisbbox.ury_pt, + " ".join(["0 1" for value in self.elements[0].nodes[0].value.tostring8bit()]))) + file.write(binascii.b2a_hex(zlib.compress(self.data(thisbbox)))) + file.write("\n") + + def processPDF(self, file, writer, context, registry, bbox): + thisbbox = self.bbox() + bbox += thisbbox + d = self.data(thisbbox) + if writer.compress: + filter = "/Filter /FlateDecode\n" + d = zlib.compress(d) + else: + filter = "" + name = "shading-%s" % id(self) + shading = PDFGenericResource("shading", name, """<< /ShadingType 4 +/ColorSpace %s +/BitsPerCoordinate 24 +/BitsPerComponent 8 +/BitsPerFlag 8 +/Decode [%f %f %f %f %s] +/Length %i +%s>> +stream +%s +endstream\n""" % (self.elements[0].nodes[0].value.colorspacestring(), + thisbbox.llx_pt, thisbbox.urx_pt, thisbbox.lly_pt, thisbbox.ury_pt, + " ".join(["0 1" for value in self.elements[0].nodes[0].value.tostring8bit()]), + len(d), filter, d)) + registry.add(shading) + registry.addresource("Shading", name, shading) + file.write("/%s sh\n" % name) diff --git a/compiler/gdsMill/pyx/normpath.py b/compiler/gdsMill/pyx/normpath.py new file mode 100644 index 00000000..6056a4c8 --- /dev/null +++ b/compiler/gdsMill/pyx/normpath.py @@ -0,0 +1,1935 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2003-2006 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import nested_scopes + +import math +try: + from math import radians, degrees +except ImportError: + # fallback implementation for Python 2.1 + def radians(x): return x*math.pi/180 + def degrees(x): return x*180/math.pi + +import mathutils, path, trafo, unit +import bbox as bboxmodule + +try: + sum([]) +except NameError: + # fallback implementation for Python 2.2 and below + def sum(list): + return reduce(lambda x, y: x+y, list, 0) + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + +# use new style classes when possible +__metaclass__ = type + +class _marker: pass + +################################################################################ + +# specific exception for normpath-related problems +class NormpathException(Exception): pass + +# invalid result marker +class _invalid: + + """invalid result marker class + + The following norm(sub)path(item) methods: + - trafo + - rotation + - tangent_pt + - tangent + - curvature_pt + - curvradius_pt + return list of result values, which might contain the invalid instance + defined below to signal points, where the result is undefined due to + properties of the norm(sub)path(item). Accessing invalid leads to an + NormpathException, but you can test the result values by "is invalid". + """ + + def invalid1(self): + raise NormpathException("invalid result (the requested value is undefined due to path properties)") + __str__ = __repr__ = __neg__ = invalid1 + + def invalid2(self, other): + self.invalid1() + __cmp__ = __add__ = __iadd__ = __sub__ = __isub__ = __mul__ = __imul__ = __div__ = __truediv__ = __idiv__ = invalid2 + +invalid = _invalid() + +################################################################################ + +# global epsilon (default precision of normsubpaths) +_epsilon = 1e-5 +# minimal relative speed (abort condition for tangent information) +_minrelspeed = 1e-5 + +def set(epsilon=None, minrelspeed=None): + global _epsilon + global _minrelspeed + if epsilon is not None: + _epsilon = epsilon + if minrelspeed is not None: + _minrelspeed = minrelspeed + + +################################################################################ +# normsubpathitems +################################################################################ + +class normsubpathitem: + + """element of a normalized sub path + + Various operations on normsubpathitems might be subject of + approximitions. Those methods get the finite precision epsilon, + which is the accuracy needed expressed as a length in pts. + + normsubpathitems should never be modified inplace, since references + might be shared between several normsubpaths. + """ + + def arclen_pt(self, epsilon): + """return arc length in pts""" + pass + + def _arclentoparam_pt(self, lengths_pt, epsilon): + """return a tuple of params and the total length arc length in pts""" + pass + + def arclentoparam_pt(self, lengths_pt, epsilon): + """return a tuple of params""" + pass + + def at_pt(self, params): + """return coordinates at params in pts""" + pass + + def atbegin_pt(self): + """return coordinates of first point in pts""" + pass + + def atend_pt(self): + """return coordinates of last point in pts""" + pass + + def bbox(self): + """return bounding box of normsubpathitem""" + pass + + def cbox(self): + """return control box of normsubpathitem + + The control box also fully encloses the normsubpathitem but in the case of a Bezier + curve it is not the minimal box doing so. On the other hand, it is much faster + to calculate. + """ + pass + + def curvature_pt(self, params): + """return the curvature at params in 1/pts + + The result contains the invalid instance at positions, where the + curvature is undefined.""" + pass + + def curveradius_pt(self, params): + """return the curvature radius at params in pts + + The curvature radius is the inverse of the curvature. Where the + curvature is undefined, the invalid instance is returned. Note that + this radius can be negative or positive, depending on the sign of the + curvature.""" + pass + + def intersect(self, other, epsilon): + """intersect self with other normsubpathitem""" + pass + + def modifiedbegin_pt(self, x_pt, y_pt): + """return a normsubpathitem with a modified beginning point""" + pass + + def modifiedend_pt(self, x_pt, y_pt): + """return a normsubpathitem with a modified end point""" + pass + + def _paramtoarclen_pt(self, param, epsilon): + """return a tuple of arc lengths and the total arc length in pts""" + pass + + def pathitem(self): + """return pathitem corresponding to normsubpathitem""" + + def reversed(self): + """return reversed normsubpathitem""" + pass + + def rotation(self, params): + """return rotation trafos (i.e. trafos without translations) at params""" + pass + + def segments(self, params): + """return segments of the normsubpathitem + + The returned list of normsubpathitems for the segments between + the params. params needs to contain at least two values. + """ + pass + + def trafo(self, params): + """return transformations at params""" + + def transformed(self, trafo): + """return transformed normsubpathitem according to trafo""" + pass + + def outputPS(self, file, writer): + """write PS code corresponding to normsubpathitem to file""" + pass + + def outputPDF(self, file, writer): + """write PDF code corresponding to normsubpathitem to file""" + pass + + +class normline_pt(normsubpathitem): + + """Straight line from (x0_pt, y0_pt) to (x1_pt, y1_pt) (coordinates in pts)""" + + __slots__ = "x0_pt", "y0_pt", "x1_pt", "y1_pt" + + def __init__(self, x0_pt, y0_pt, x1_pt, y1_pt): + self.x0_pt = x0_pt + self.y0_pt = y0_pt + self.x1_pt = x1_pt + self.y1_pt = y1_pt + + def __str__(self): + return "normline_pt(%g, %g, %g, %g)" % (self.x0_pt, self.y0_pt, self.x1_pt, self.y1_pt) + + def _arclentoparam_pt(self, lengths_pt, epsilon): + # do self.arclen_pt inplace for performance reasons + l_pt = math.hypot(self.x0_pt-self.x1_pt, self.y0_pt-self.y1_pt) + return [length_pt/l_pt for length_pt in lengths_pt], l_pt + + def arclentoparam_pt(self, lengths_pt, epsilon): + """return a tuple of params""" + return self._arclentoparam_pt(lengths_pt, epsilon)[0] + + def arclen_pt(self, epsilon): + return math.hypot(self.x0_pt-self.x1_pt, self.y0_pt-self.y1_pt) + + def at_pt(self, params): + return [(self.x0_pt+(self.x1_pt-self.x0_pt)*t, self.y0_pt+(self.y1_pt-self.y0_pt)*t) + for t in params] + + def atbegin_pt(self): + return self.x0_pt, self.y0_pt + + def atend_pt(self): + return self.x1_pt, self.y1_pt + + def bbox(self): + return bboxmodule.bbox_pt(min(self.x0_pt, self.x1_pt), min(self.y0_pt, self.y1_pt), + max(self.x0_pt, self.x1_pt), max(self.y0_pt, self.y1_pt)) + + cbox = bbox + + def curvature_pt(self, params): + return [0] * len(params) + + def curveradius_pt(self, params): + return [invalid] * len(params) + + def intersect(self, other, epsilon): + if isinstance(other, normline_pt): + a_deltax_pt = self.x1_pt - self.x0_pt + a_deltay_pt = self.y1_pt - self.y0_pt + + b_deltax_pt = other.x1_pt - other.x0_pt + b_deltay_pt = other.y1_pt - other.y0_pt + try: + det = 1.0 / (b_deltax_pt * a_deltay_pt - b_deltay_pt * a_deltax_pt) + except ArithmeticError: + return [] + + ba_deltax0_pt = other.x0_pt - self.x0_pt + ba_deltay0_pt = other.y0_pt - self.y0_pt + + a_t = (b_deltax_pt * ba_deltay0_pt - b_deltay_pt * ba_deltax0_pt) * det + b_t = (a_deltax_pt * ba_deltay0_pt - a_deltay_pt * ba_deltax0_pt) * det + + # check for intersections out of bound + # TODO: we might allow for a small out of bound errors. + if not (0<=a_t<=1 and 0<=b_t<=1): + return [] + + # return parameters of intersection + return [(a_t, b_t)] + else: + return [(s_t, o_t) for o_t, s_t in other.intersect(self, epsilon)] + + def modifiedbegin_pt(self, x_pt, y_pt): + return normline_pt(x_pt, y_pt, self.x1_pt, self.y1_pt) + + def modifiedend_pt(self, x_pt, y_pt): + return normline_pt(self.x0_pt, self.y0_pt, x_pt, y_pt) + + def _paramtoarclen_pt(self, params, epsilon): + totalarclen_pt = self.arclen_pt(epsilon) + arclens_pt = [totalarclen_pt * param for param in params + [1]] + return arclens_pt[:-1], arclens_pt[-1] + + def pathitem(self): + return path.lineto_pt(self.x1_pt, self.y1_pt) + + def reversed(self): + return normline_pt(self.x1_pt, self.y1_pt, self.x0_pt, self.y0_pt) + + def rotation(self, params): + return [trafo.rotate(degrees(math.atan2(self.y1_pt-self.y0_pt, self.x1_pt-self.x0_pt)))]*len(params) + + def segments(self, params): + if len(params) < 2: + raise ValueError("at least two parameters needed in segments") + result = [] + xl_pt = yl_pt = None + for t in params: + xr_pt = self.x0_pt + (self.x1_pt-self.x0_pt)*t + yr_pt = self.y0_pt + (self.y1_pt-self.y0_pt)*t + if xl_pt is not None: + result.append(normline_pt(xl_pt, yl_pt, xr_pt, yr_pt)) + xl_pt = xr_pt + yl_pt = yr_pt + return result + + def trafo(self, params): + rotate = trafo.rotate(degrees(math.atan2(self.y1_pt-self.y0_pt, self.x1_pt-self.x0_pt))) + return [trafo.translate_pt(*at_pt) * rotate + for param, at_pt in zip(params, self.at_pt(params))] + + def transformed(self, trafo): + return normline_pt(*(trafo.apply_pt(self.x0_pt, self.y0_pt) + trafo.apply_pt(self.x1_pt, self.y1_pt))) + + def outputPS(self, file, writer): + file.write("%g %g lineto\n" % (self.x1_pt, self.y1_pt)) + + def outputPDF(self, file, writer): + file.write("%f %f l\n" % (self.x1_pt, self.y1_pt)) + + +class normcurve_pt(normsubpathitem): + + """Bezier curve with control points x0_pt, y0_pt, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt (coordinates in pts)""" + + __slots__ = "x0_pt", "y0_pt", "x1_pt", "y1_pt", "x2_pt", "y2_pt", "x3_pt", "y3_pt" + + def __init__(self, x0_pt, y0_pt, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt): + self.x0_pt = x0_pt + self.y0_pt = y0_pt + self.x1_pt = x1_pt + self.y1_pt = y1_pt + self.x2_pt = x2_pt + self.y2_pt = y2_pt + self.x3_pt = x3_pt + self.y3_pt = y3_pt + + def __str__(self): + return "normcurve_pt(%g, %g, %g, %g, %g, %g, %g, %g)" % (self.x0_pt, self.y0_pt, self.x1_pt, self.y1_pt, + self.x2_pt, self.y2_pt, self.x3_pt, self.y3_pt) + + def _midpointsplit(self, epsilon): + """split curve into two parts + + Helper method to reduce the complexity of a problem by turning + a normcurve_pt into several normline_pt segments. This method + returns normcurve_pt instances only, when they are not yet straight + enough to be replaceable by normcurve_pt instances. Thus a recursive + midpointsplitting will turn a curve into line segments with the + given precision epsilon. + """ + + # first, we have to calculate the midpoints between adjacent + # control points + x01_pt = 0.5*(self.x0_pt + self.x1_pt) + y01_pt = 0.5*(self.y0_pt + self.y1_pt) + x12_pt = 0.5*(self.x1_pt + self.x2_pt) + y12_pt = 0.5*(self.y1_pt + self.y2_pt) + x23_pt = 0.5*(self.x2_pt + self.x3_pt) + y23_pt = 0.5*(self.y2_pt + self.y3_pt) + + # In the next iterative step, we need the midpoints between 01 and 12 + # and between 12 and 23 + x01_12_pt = 0.5*(x01_pt + x12_pt) + y01_12_pt = 0.5*(y01_pt + y12_pt) + x12_23_pt = 0.5*(x12_pt + x23_pt) + y12_23_pt = 0.5*(y12_pt + y23_pt) + + # Finally the midpoint is given by + xmidpoint_pt = 0.5*(x01_12_pt + x12_23_pt) + ymidpoint_pt = 0.5*(y01_12_pt + y12_23_pt) + + # Before returning the normcurves we check whether we can + # replace them by normlines within an error of epsilon pts. + # The maximal error value is given by the modulus of the + # difference between the length of the control polygon + # (i.e. |P1-P0|+|P2-P1|+|P3-P2|), which consitutes an upper + # bound for the length, and the length of the straight line + # between start and end point of the normcurve (i.e. |P3-P1|), + # which represents a lower bound. + l0_pt = math.hypot(xmidpoint_pt - self.x0_pt, ymidpoint_pt - self.y0_pt) + l1_pt = math.hypot(x01_pt - self.x0_pt, y01_pt - self.y0_pt) + l2_pt = math.hypot(x01_12_pt - x01_pt, y01_12_pt - y01_pt) + l3_pt = math.hypot(xmidpoint_pt - x01_12_pt, ymidpoint_pt - y01_12_pt) + if l1_pt+l2_pt+l3_pt-l0_pt < epsilon: + a = _leftnormline_pt(self.x0_pt, self.y0_pt, xmidpoint_pt, ymidpoint_pt, l1_pt, l2_pt, l3_pt) + else: + a = _leftnormcurve_pt(self.x0_pt, self.y0_pt, + x01_pt, y01_pt, + x01_12_pt, y01_12_pt, + xmidpoint_pt, ymidpoint_pt) + + l0_pt = math.hypot(self.x3_pt - xmidpoint_pt, self.y3_pt - ymidpoint_pt) + l1_pt = math.hypot(x12_23_pt - xmidpoint_pt, y12_23_pt - ymidpoint_pt) + l2_pt = math.hypot(x23_pt - x12_23_pt, y23_pt - y12_23_pt) + l3_pt = math.hypot(self.x3_pt - x23_pt, self.y3_pt - y23_pt) + if l1_pt+l2_pt+l3_pt-l0_pt < epsilon: + b = _rightnormline_pt(xmidpoint_pt, ymidpoint_pt, self.x3_pt, self.y3_pt, l1_pt, l2_pt, l3_pt) + else: + b = _rightnormcurve_pt(xmidpoint_pt, ymidpoint_pt, + x12_23_pt, y12_23_pt, + x23_pt, y23_pt, + self.x3_pt, self.y3_pt) + + return a, b + + def _arclentoparam_pt(self, lengths_pt, epsilon): + a, b = self._midpointsplit(epsilon) + params_a, arclen_a_pt = a._arclentoparam_pt(lengths_pt, epsilon) + params_b, arclen_b_pt = b._arclentoparam_pt([length_pt - arclen_a_pt for length_pt in lengths_pt], epsilon) + params = [] + for param_a, param_b, length_pt in zip(params_a, params_b, lengths_pt): + if length_pt > arclen_a_pt: + params.append(b.subparamtoparam(param_b)) + else: + params.append(a.subparamtoparam(param_a)) + return params, arclen_a_pt + arclen_b_pt + + def arclentoparam_pt(self, lengths_pt, epsilon): + """return a tuple of params""" + return self._arclentoparam_pt(lengths_pt, epsilon)[0] + + def arclen_pt(self, epsilon): + a, b = self._midpointsplit(epsilon) + return a.arclen_pt(epsilon) + b.arclen_pt(epsilon) + + def at_pt(self, params): + return [( (-self.x0_pt+3*self.x1_pt-3*self.x2_pt+self.x3_pt)*t*t*t + + (3*self.x0_pt-6*self.x1_pt+3*self.x2_pt )*t*t + + (-3*self.x0_pt+3*self.x1_pt )*t + + self.x0_pt, + (-self.y0_pt+3*self.y1_pt-3*self.y2_pt+self.y3_pt)*t*t*t + + (3*self.y0_pt-6*self.y1_pt+3*self.y2_pt )*t*t + + (-3*self.y0_pt+3*self.y1_pt )*t + + self.y0_pt ) + for t in params] + + def atbegin_pt(self): + return self.x0_pt, self.y0_pt + + def atend_pt(self): + return self.x3_pt, self.y3_pt + + def bbox(self): + xmin_pt, xmax_pt = path._bezierpolyrange(self.x0_pt, self.x1_pt, self.x2_pt, self.x3_pt) + ymin_pt, ymax_pt = path._bezierpolyrange(self.y0_pt, self.y1_pt, self.y2_pt, self.y3_pt) + return bboxmodule.bbox_pt(xmin_pt, ymin_pt, xmax_pt, ymax_pt) + + def cbox(self): + return bboxmodule.bbox_pt(min(self.x0_pt, self.x1_pt, self.x2_pt, self.x3_pt), + min(self.y0_pt, self.y1_pt, self.y2_pt, self.y3_pt), + max(self.x0_pt, self.x1_pt, self.x2_pt, self.x3_pt), + max(self.y0_pt, self.y1_pt, self.y2_pt, self.y3_pt)) + + def curvature_pt(self, params): + result = [] + # see notes in rotation + approxarclen = (math.hypot(self.x1_pt-self.x0_pt, self.y1_pt-self.y0_pt) + + math.hypot(self.x2_pt-self.x1_pt, self.y2_pt-self.y1_pt) + + math.hypot(self.x3_pt-self.x2_pt, self.y3_pt-self.y2_pt)) + for param in params: + xdot = ( 3 * (1-param)*(1-param) * (-self.x0_pt + self.x1_pt) + + 6 * (1-param)*param * (-self.x1_pt + self.x2_pt) + + 3 * param*param * (-self.x2_pt + self.x3_pt) ) + ydot = ( 3 * (1-param)*(1-param) * (-self.y0_pt + self.y1_pt) + + 6 * (1-param)*param * (-self.y1_pt + self.y2_pt) + + 3 * param*param * (-self.y2_pt + self.y3_pt) ) + xddot = ( 6 * (1-param) * (self.x0_pt - 2*self.x1_pt + self.x2_pt) + + 6 * param * (self.x1_pt - 2*self.x2_pt + self.x3_pt) ) + yddot = ( 6 * (1-param) * (self.y0_pt - 2*self.y1_pt + self.y2_pt) + + 6 * param * (self.y1_pt - 2*self.y2_pt + self.y3_pt) ) + + hypot = math.hypot(xdot, ydot) + if hypot/approxarclen > _minrelspeed: + result.append((xdot*yddot - ydot*xddot) / hypot**3) + else: + result.append(invalid) + return result + + def curveradius_pt(self, params): + result = [] + # see notes in rotation + approxarclen = (math.hypot(self.x1_pt-self.x0_pt, self.y1_pt-self.y0_pt) + + math.hypot(self.x2_pt-self.x1_pt, self.y2_pt-self.y1_pt) + + math.hypot(self.x3_pt-self.x2_pt, self.y3_pt-self.y2_pt)) + for param in params: + xdot = ( 3 * (1-param)*(1-param) * (-self.x0_pt + self.x1_pt) + + 6 * (1-param)*param * (-self.x1_pt + self.x2_pt) + + 3 * param*param * (-self.x2_pt + self.x3_pt) ) + ydot = ( 3 * (1-param)*(1-param) * (-self.y0_pt + self.y1_pt) + + 6 * (1-param)*param * (-self.y1_pt + self.y2_pt) + + 3 * param*param * (-self.y2_pt + self.y3_pt) ) + xddot = ( 6 * (1-param) * (self.x0_pt - 2*self.x1_pt + self.x2_pt) + + 6 * param * (self.x1_pt - 2*self.x2_pt + self.x3_pt) ) + yddot = ( 6 * (1-param) * (self.y0_pt - 2*self.y1_pt + self.y2_pt) + + 6 * param * (self.y1_pt - 2*self.y2_pt + self.y3_pt) ) + + hypot = math.hypot(xdot, ydot) + if hypot/approxarclen > _minrelspeed: + result.append(hypot**3 / (xdot*yddot - ydot*xddot)) + else: + result.append(invalid) + return result + + def intersect(self, other, epsilon): + # There can be no intersection point, when the control boxes are not + # overlapping. Note that we use the control box instead of the bounding + # box here, because the former can be calculated more efficiently for + # Bezier curves. + if not self.cbox().intersects(other.cbox()): + return [] + a, b = self._midpointsplit(epsilon) + # To improve the performance in the general case we alternate the + # splitting process between the two normsubpathitems + return ( [(a.subparamtoparam(a_t), o_t) for o_t, a_t in other.intersect(a, epsilon)] + + [(b.subparamtoparam(b_t), o_t) for o_t, b_t in other.intersect(b, epsilon)] ) + + def modifiedbegin_pt(self, x_pt, y_pt): + return normcurve_pt(x_pt, y_pt, + self.x1_pt, self.y1_pt, + self.x2_pt, self.y2_pt, + self.x3_pt, self.y3_pt) + + def modifiedend_pt(self, x_pt, y_pt): + return normcurve_pt(self.x0_pt, self.y0_pt, + self.x1_pt, self.y1_pt, + self.x2_pt, self.y2_pt, + x_pt, y_pt) + + def _paramtoarclen_pt(self, params, epsilon): + arclens_pt = [segment.arclen_pt(epsilon) for segment in self.segments([0] + list(params) + [1])] + for i in range(1, len(arclens_pt)): + arclens_pt[i] += arclens_pt[i-1] + return arclens_pt[:-1], arclens_pt[-1] + + def pathitem(self): + return path.curveto_pt(self.x1_pt, self.y1_pt, self.x2_pt, self.y2_pt, self.x3_pt, self.y3_pt) + + def reversed(self): + return normcurve_pt(self.x3_pt, self.y3_pt, self.x2_pt, self.y2_pt, self.x1_pt, self.y1_pt, self.x0_pt, self.y0_pt) + + def rotation(self, params): + result = [] + # We need to take care of the case of tdx_pt and tdy_pt close to zero. + # We should not compare those values to epsilon (which is a length) directly. + # Furthermore we want this "speed" in general and it's abort condition in + # particular to be invariant on the actual size of the normcurve. Hence we + # first calculate a crude approximation for the arclen. + approxarclen = (math.hypot(self.x1_pt-self.x0_pt, self.y1_pt-self.y0_pt) + + math.hypot(self.x2_pt-self.x1_pt, self.y2_pt-self.y1_pt) + + math.hypot(self.x3_pt-self.x2_pt, self.y3_pt-self.y2_pt)) + for param in params: + tdx_pt = (3*( -self.x0_pt+3*self.x1_pt-3*self.x2_pt+self.x3_pt)*param*param + + 2*( 3*self.x0_pt-6*self.x1_pt+3*self.x2_pt )*param + + (-3*self.x0_pt+3*self.x1_pt )) + tdy_pt = (3*( -self.y0_pt+3*self.y1_pt-3*self.y2_pt+self.y3_pt)*param*param + + 2*( 3*self.y0_pt-6*self.y1_pt+3*self.y2_pt )*param + + (-3*self.y0_pt+3*self.y1_pt )) + # We scale the speed such the "relative speed" of a line is 1 independend of + # the length of the line. For curves we want this "relative speed" to be higher than + # _minrelspeed: + if math.hypot(tdx_pt, tdy_pt)/approxarclen > _minrelspeed: + result.append(trafo.rotate(degrees(math.atan2(tdy_pt, tdx_pt)))) + else: + # Note that we can't use the rule of l'Hopital here, since it would + # not provide us with a sign for the tangent. Hence we wouldn't + # notice whether the sign changes (which is a typical case at cusps). + result.append(invalid) + return result + + def segments(self, params): + if len(params) < 2: + raise ValueError("at least two parameters needed in segments") + + # first, we calculate the coefficients corresponding to our + # original bezier curve. These represent a useful starting + # point for the following change of the polynomial parameter + a0x_pt = self.x0_pt + a0y_pt = self.y0_pt + a1x_pt = 3*(-self.x0_pt+self.x1_pt) + a1y_pt = 3*(-self.y0_pt+self.y1_pt) + a2x_pt = 3*(self.x0_pt-2*self.x1_pt+self.x2_pt) + a2y_pt = 3*(self.y0_pt-2*self.y1_pt+self.y2_pt) + a3x_pt = -self.x0_pt+3*(self.x1_pt-self.x2_pt)+self.x3_pt + a3y_pt = -self.y0_pt+3*(self.y1_pt-self.y2_pt)+self.y3_pt + + result = [] + + for i in range(len(params)-1): + t1 = params[i] + dt = params[i+1]-t1 + + # [t1,t2] part + # + # the new coefficients of the [t1,t1+dt] part of the bezier curve + # are then given by expanding + # a0 + a1*(t1+dt*u) + a2*(t1+dt*u)**2 + + # a3*(t1+dt*u)**3 in u, yielding + # + # a0 + a1*t1 + a2*t1**2 + a3*t1**3 + + # ( a1 + 2*a2 + 3*a3*t1**2 )*dt * u + + # ( a2 + 3*a3*t1 )*dt**2 * u**2 + + # a3*dt**3 * u**3 + # + # from this values we obtain the new control points by inversion + # + # TODO: we could do this more efficiently by reusing for + # (x0_pt, y0_pt) the control point (x3_pt, y3_pt) from the previous + # Bezier curve + + x0_pt = a0x_pt + a1x_pt*t1 + a2x_pt*t1*t1 + a3x_pt*t1*t1*t1 + y0_pt = a0y_pt + a1y_pt*t1 + a2y_pt*t1*t1 + a3y_pt*t1*t1*t1 + x1_pt = (a1x_pt+2*a2x_pt*t1+3*a3x_pt*t1*t1)*dt/3.0 + x0_pt + y1_pt = (a1y_pt+2*a2y_pt*t1+3*a3y_pt*t1*t1)*dt/3.0 + y0_pt + x2_pt = (a2x_pt+3*a3x_pt*t1)*dt*dt/3.0 - x0_pt + 2*x1_pt + y2_pt = (a2y_pt+3*a3y_pt*t1)*dt*dt/3.0 - y0_pt + 2*y1_pt + x3_pt = a3x_pt*dt*dt*dt + x0_pt - 3*x1_pt + 3*x2_pt + y3_pt = a3y_pt*dt*dt*dt + y0_pt - 3*y1_pt + 3*y2_pt + + result.append(normcurve_pt(x0_pt, y0_pt, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt)) + + return result + + def trafo(self, params): + result = [] + for rotation, at_pt in zip(self.rotation(params), self.at_pt(params)): + if rotation is invalid: + result.append(rotation) + else: + result.append(trafo.translate_pt(*at_pt) * rotation) + return result + + def transformed(self, trafo): + x0_pt, y0_pt = trafo.apply_pt(self.x0_pt, self.y0_pt) + x1_pt, y1_pt = trafo.apply_pt(self.x1_pt, self.y1_pt) + x2_pt, y2_pt = trafo.apply_pt(self.x2_pt, self.y2_pt) + x3_pt, y3_pt = trafo.apply_pt(self.x3_pt, self.y3_pt) + return normcurve_pt(x0_pt, y0_pt, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt) + + def outputPS(self, file, writer): + file.write("%g %g %g %g %g %g curveto\n" % (self.x1_pt, self.y1_pt, self.x2_pt, self.y2_pt, self.x3_pt, self.y3_pt)) + + def outputPDF(self, file, writer): + file.write("%f %f %f %f %f %f c\n" % (self.x1_pt, self.y1_pt, self.x2_pt, self.y2_pt, self.x3_pt, self.y3_pt)) + + def x_pt(self, t): + return ((( self.x3_pt-3*self.x2_pt+3*self.x1_pt-self.x0_pt)*t + + 3*self.x0_pt-6*self.x1_pt+3*self.x2_pt)*t + + 3*self.x1_pt-3*self.x0_pt)*t + self.x0_pt + + def xdot_pt(self, t): + return ((3*self.x3_pt-9*self.x2_pt+9*self.x1_pt-3*self.x0_pt)*t + + 6*self.x0_pt-12*self.x1_pt+6*self.x2_pt)*t + 3*self.x1_pt - 3*self.x0_pt + + def xddot_pt(self, t): + return (6*self.x3_pt-18*self.x2_pt+18*self.x1_pt-6*self.x0_pt)*t + 6*self.x0_pt - 12*self.x1_pt + 6*self.x2_pt + + def xdddot_pt(self, t): + return 6*self.x3_pt-18*self.x2_pt+18*self.x1_pt-6*self.x0_pt + + def y_pt(self, t): + return ((( self.y3_pt-3*self.y2_pt+3*self.y1_pt-self.y0_pt)*t + + 3*self.y0_pt-6*self.y1_pt+3*self.y2_pt)*t + + 3*self.y1_pt-3*self.y0_pt)*t + self.y0_pt + + def ydot_pt(self, t): + return ((3*self.y3_pt-9*self.y2_pt+9*self.y1_pt-3*self.y0_pt)*t + + 6*self.y0_pt-12*self.y1_pt+6*self.y2_pt)*t + 3*self.y1_pt - 3*self.y0_pt + + def yddot_pt(self, t): + return (6*self.y3_pt-18*self.y2_pt+18*self.y1_pt-6*self.y0_pt)*t + 6*self.y0_pt - 12*self.y1_pt + 6*self.y2_pt + + def ydddot_pt(self, t): + return 6*self.y3_pt-18*self.y2_pt+18*self.y1_pt-6*self.y0_pt + + +# curve replacements used by midpointsplit: +# The replacements are normline_pt and normcurve_pt instances with an +# additional subparamtoparam function for proper conversion of the +# parametrization. Note that we only one direction (when a parameter +# gets calculated), since the other way around direction midpointsplit +# is not needed at all + +class _leftnormline_pt(normline_pt): + + __slots__ = "x0_pt", "y0_pt", "x1_pt", "y1_pt", "l1_pt", "l2_pt", "l3_pt" + + def __init__(self, x0_pt, y0_pt, x1_pt, y1_pt, l1_pt, l2_pt, l3_pt): + normline_pt.__init__(self, x0_pt, y0_pt, x1_pt, y1_pt) + self.l1_pt = l1_pt + self.l2_pt = l2_pt + self.l3_pt = l3_pt + + def subparamtoparam(self, param): + if 0 <= param <= 1: + params = mathutils.realpolyroots(self.l1_pt-2*self.l2_pt+self.l3_pt, + -3*self.l1_pt+3*self.l2_pt, + 3*self.l1_pt, + -param*(self.l1_pt+self.l2_pt+self.l3_pt)) + # we might get several solutions and choose the one closest to 0.5 + # (we want the solution to be in the range 0 <= param <= 1; in case + # we get several solutions in this range, they all will be close to + # each other since l1_pt+l2_pt+l3_pt-l0_pt < epsilon) + params.sort(lambda t1, t2: cmp(abs(t1-0.5), abs(t2-0.5))) + return 0.5*params[0] + else: + # when we are outside the proper parameter range, we skip the non-linear + # transformation, since it becomes slow and it might even start to be + # numerically instable + return 0.5*param + + +class _rightnormline_pt(_leftnormline_pt): + + __slots__ = "x0_pt", "y0_pt", "x1_pt", "y1_pt", "l1_pt", "l2_pt", "l3_pt" + + def subparamtoparam(self, param): + return 0.5+_leftnormline_pt.subparamtoparam(self, param) + + +class _leftnormcurve_pt(normcurve_pt): + + __slots__ = "x0_pt", "y0_pt", "x1_pt", "y1_pt", "x2_pt", "y2_pt", "x3_pt", "y3_pt" + + def subparamtoparam(self, param): + return 0.5*param + + +class _rightnormcurve_pt(normcurve_pt): + + __slots__ = "x0_pt", "y0_pt", "x1_pt", "y1_pt", "x2_pt", "y2_pt", "x3_pt", "y3_pt" + + def subparamtoparam(self, param): + return 0.5+0.5*param + + +################################################################################ +# normsubpath +################################################################################ + +class normsubpath: + + """sub path of a normalized path + + A subpath consists of a list of normsubpathitems, i.e., normlines_pt and + normcurves_pt and can either be closed or not. + + Some invariants, which have to be obeyed: + - All normsubpathitems have to be longer than epsilon pts. + - At the end there may be a normline (stored in self.skippedline) whose + length is shorter than epsilon -- it has to be taken into account + when adding further normsubpathitems + - The last point of a normsubpathitem and the first point of the next + element have to be equal. + - When the path is closed, the last point of last normsubpathitem has + to be equal to the first point of the first normsubpathitem. + - epsilon might be none, disallowing any numerics, but allowing for + arbitrary short paths. This is used in pdf output, where all paths need + to be transformed to normpaths. + """ + + __slots__ = "normsubpathitems", "closed", "epsilon", "skippedline" + + def __init__(self, normsubpathitems=[], closed=0, epsilon=_marker): + """construct a normsubpath""" + if epsilon is _marker: + epsilon = _epsilon + self.epsilon = epsilon + # If one or more items appended to the normsubpath have been + # skipped (because their total length was shorter than epsilon), + # we remember this fact by a line because we have to take it + # properly into account when appending further normsubpathitems + self.skippedline = None + + self.normsubpathitems = [] + self.closed = 0 + + # a test (might be temporary) + for anormsubpathitem in normsubpathitems: + assert isinstance(anormsubpathitem, normsubpathitem), "only list of normsubpathitem instances allowed" + + self.extend(normsubpathitems) + + if closed: + self.close() + + def __getitem__(self, i): + """return normsubpathitem i""" + return self.normsubpathitems[i] + + def __len__(self): + """return number of normsubpathitems""" + return len(self.normsubpathitems) + + def __str__(self): + l = ", ".join(map(str, self.normsubpathitems)) + if self.closed: + return "normsubpath([%s], closed=1)" % l + else: + return "normsubpath([%s])" % l + + def _distributeparams(self, params): + """return a dictionary mapping normsubpathitemindices to a tuple + of a paramindices and normsubpathitemparams. + + normsubpathitemindex specifies a normsubpathitem containing + one or several positions. paramindex specify the index of the + param in the original list and normsubpathitemparam is the + parameter value in the normsubpathitem. + """ + + result = {} + for i, param in enumerate(params): + if param > 0: + index = int(param) + if index > len(self.normsubpathitems) - 1: + index = len(self.normsubpathitems) - 1 + else: + index = 0 + result.setdefault(index, ([], [])) + result[index][0].append(i) + result[index][1].append(param - index) + return result + + def append(self, anormsubpathitem): + """append normsubpathitem + + Fails on closed normsubpath. + """ + if self.epsilon is None: + self.normsubpathitems.append(anormsubpathitem) + else: + # consitency tests (might be temporary) + assert isinstance(anormsubpathitem, normsubpathitem), "only normsubpathitem instances allowed" + if self.skippedline: + assert math.hypot(*[x-y for x, y in zip(self.skippedline.atend_pt(), anormsubpathitem.atbegin_pt())]) < self.epsilon, "normsubpathitems do not match" + elif self.normsubpathitems: + assert math.hypot(*[x-y for x, y in zip(self.normsubpathitems[-1].atend_pt(), anormsubpathitem.atbegin_pt())]) < self.epsilon, "normsubpathitems do not match" + + if self.closed: + raise NormpathException("Cannot append to closed normsubpath") + + if self.skippedline: + xs_pt, ys_pt = self.skippedline.atbegin_pt() + else: + xs_pt, ys_pt = anormsubpathitem.atbegin_pt() + xe_pt, ye_pt = anormsubpathitem.atend_pt() + + if (math.hypot(xe_pt-xs_pt, ye_pt-ys_pt) >= self.epsilon or + anormsubpathitem.arclen_pt(self.epsilon) >= self.epsilon): + if self.skippedline: + anormsubpathitem = anormsubpathitem.modifiedbegin_pt(xs_pt, ys_pt) + self.normsubpathitems.append(anormsubpathitem) + self.skippedline = None + else: + self.skippedline = normline_pt(xs_pt, ys_pt, xe_pt, ye_pt) + + def arclen_pt(self): + """return arc length in pts""" + return sum([npitem.arclen_pt(self.epsilon) for npitem in self.normsubpathitems]) + + def _arclentoparam_pt(self, lengths_pt): + """return a tuple of params and the total length arc length in pts""" + # work on a copy which is counted down to negative values + lengths_pt = lengths_pt[:] + results = [None] * len(lengths_pt) + + totalarclen = 0 + for normsubpathindex, normsubpathitem in enumerate(self.normsubpathitems): + params, arclen = normsubpathitem._arclentoparam_pt(lengths_pt, self.epsilon) + for i in range(len(results)): + if results[i] is None: + lengths_pt[i] -= arclen + if lengths_pt[i] < 0 or normsubpathindex == len(self.normsubpathitems) - 1: + # overwrite the results until the length has become negative + results[i] = normsubpathindex + params[i] + totalarclen += arclen + + return results, totalarclen + + def arclentoparam_pt(self, lengths_pt): + """return a tuple of params""" + return self._arclentoparam_pt(lengths_pt)[0] + + def at_pt(self, params): + """return coordinates at params in pts""" + if not self.normsubpathitems and self.skippedline: + return [self.skippedline.atbegin_pt()]*len(params) + result = [None] * len(params) + for normsubpathitemindex, (indices, params) in self._distributeparams(params).items(): + for index, point_pt in zip(indices, self.normsubpathitems[normsubpathitemindex].at_pt(params)): + result[index] = point_pt + return result + + def atbegin_pt(self): + """return coordinates of first point in pts""" + if not self.normsubpathitems and self.skippedline: + return self.skippedline.atbegin_pt() + return self.normsubpathitems[0].atbegin_pt() + + def atend_pt(self): + """return coordinates of last point in pts""" + if self.skippedline: + return self.skippedline.atend_pt() + return self.normsubpathitems[-1].atend_pt() + + def bbox(self): + """return bounding box of normsubpath""" + if self.normsubpathitems: + abbox = self.normsubpathitems[0].bbox() + for anormpathitem in self.normsubpathitems[1:]: + abbox += anormpathitem.bbox() + return abbox + else: + return bboxmodule.empty() + + def close(self): + """close subnormpath + + Fails on closed normsubpath. + """ + if self.closed: + raise NormpathException("Cannot close already closed normsubpath") + if not self.normsubpathitems: + if self.skippedline is None: + raise NormpathException("Cannot close empty normsubpath") + else: + raise NormpathException("Normsubpath too short, cannot be closed") + + xs_pt, ys_pt = self.normsubpathitems[-1].atend_pt() + xe_pt, ye_pt = self.normsubpathitems[0].atbegin_pt() + self.append(normline_pt(xs_pt, ys_pt, xe_pt, ye_pt)) + self.flushskippedline() + self.closed = 1 + + def copy(self): + """return copy of normsubpath""" + # Since normsubpathitems are never modified inplace, we just + # need to copy the normsubpathitems list. We do not pass the + # normsubpathitems to the constructor to not repeat the checks + # for minimal length of each normsubpathitem. + result = normsubpath(epsilon=self.epsilon) + result.normsubpathitems = self.normsubpathitems[:] + result.closed = self.closed + + # We can share the reference to skippedline, since it is a + # normsubpathitem as well and thus not modified in place either. + result.skippedline = self.skippedline + + return result + + def curvature_pt(self, params): + """return the curvature at params in 1/pts + + The result contain the invalid instance at positions, where the + curvature is undefined.""" + result = [None] * len(params) + for normsubpathitemindex, (indices, params) in self._distributeparams(params).items(): + for index, curvature_pt in zip(indices, self.normsubpathitems[normsubpathitemindex].curvature_pt(params)): + result[index] = curvature_pt + return result + + def curveradius_pt(self, params): + """return the curvature radius at params in pts + + The curvature radius is the inverse of the curvature. When the + curvature is 0, the invalid instance is returned. Note that this radius can be negative + or positive, depending on the sign of the curvature.""" + result = [None] * len(params) + for normsubpathitemindex, (indices, params) in self._distributeparams(params).items(): + for index, radius_pt in zip(indices, self.normsubpathitems[normsubpathitemindex].curveradius_pt(params)): + result[index] = radius_pt + return result + + def extend(self, normsubpathitems): + """extend path by normsubpathitems + + Fails on closed normsubpath. + """ + for normsubpathitem in normsubpathitems: + self.append(normsubpathitem) + + def flushskippedline(self): + """flush the skippedline, i.e. apply it to the normsubpath + + remove the skippedline by modifying the end point of the existing normsubpath + """ + while self.skippedline: + try: + lastnormsubpathitem = self.normsubpathitems.pop() + except IndexError: + raise ValueError("normsubpath too short to flush the skippedline") + lastnormsubpathitem = lastnormsubpathitem.modifiedend_pt(*self.skippedline.atend_pt()) + self.skippedline = None + self.append(lastnormsubpathitem) + + def intersect(self, other): + """intersect self with other normsubpath + + Returns a tuple of lists consisting of the parameter values + of the intersection points of the corresponding normsubpath. + """ + intersections_a = [] + intersections_b = [] + epsilon = min(self.epsilon, other.epsilon) + # Intersect all subpaths of self with the subpaths of other, possibly including + # one intersection point several times + for t_a, pitem_a in enumerate(self.normsubpathitems): + for t_b, pitem_b in enumerate(other.normsubpathitems): + for intersection_a, intersection_b in pitem_a.intersect(pitem_b, epsilon): + intersections_a.append(intersection_a + t_a) + intersections_b.append(intersection_b + t_b) + + # although intersectipns_a are sorted for the different normsubpathitems, + # within a normsubpathitem, the ordering has to be ensured separately: + intersections = zip(intersections_a, intersections_b) + intersections.sort() + intersections_a = [a for a, b in intersections] + intersections_b = [b for a, b in intersections] + + # for symmetry reasons we enumerate intersections_a as well, although + # they are already sorted (note we do not need to sort intersections_a) + intersections_a = zip(intersections_a, range(len(intersections_a))) + intersections_b = zip(intersections_b, range(len(intersections_b))) + intersections_b.sort() + + # now we search for intersections points which are closer together than epsilon + # This task is handled by the following function + def closepoints(normsubpath, intersections): + split = normsubpath.segments([0] + [intersection for intersection, index in intersections] + [len(normsubpath)]) + result = [] + if normsubpath.closed: + # note that the number of segments of a closed path is off by one + # compared to an open path + i = 0 + while i < len(split): + splitnormsubpath = split[i] + j = i + while not splitnormsubpath.normsubpathitems: # i.e. while "is short" + ip1, ip2 = intersections[i-1][1], intersections[j][1] + if ip1 0: + index = int(param) + if index > len(self.normsubpathitems) - 1: + index = len(self.normsubpathitems) - 1 + param -= index + else: + index = 0 + if index != collectindex: + if collectindex is not None: + # append end point depening on the forthcoming index + if index > collectindex: + collectparams.append(1) + else: + collectparams.append(0) + # get segments of the normsubpathitem and add them to the result + segments = self.normsubpathitems[collectindex].segments(collectparams) + result[-1].append(segments[0]) + result.extend([normsubpath([segment], epsilon=self.epsilon) for segment in segments[1:]]) + # add normsubpathitems and first segment parameter to close the + # gap to the forthcoming index + if index > collectindex: + for i in range(collectindex+1, index): + result[-1].append(self.normsubpathitems[i]) + collectparams = [0] + else: + for i in range(collectindex-1, index, -1): + result[-1].append(self.normsubpathitems[i].reversed()) + collectparams = [1] + collectindex = index + collectparams.append(param) + # add remaining collectparams to the result + segments = self.normsubpathitems[collectindex].segments(collectparams) + result[-1].append(segments[0]) + result.extend([normsubpath([segment], epsilon=self.epsilon) for segment in segments[1:]]) + + if self.closed: + # join last and first segment together if the normsubpath was + # originally closed and first and the last parameters are the + # beginning and end points of the normsubpath + if ( ( params[0] == 0 and params[-1] == len(self.normsubpathitems) ) or + ( params[-1] == 0 and params[0] == len(self.normsubpathitems) ) ): + result[-1].normsubpathitems.extend(result[0].normsubpathitems) + result = result[-1:] + result[1:-1] + + return result + + def trafo(self, params): + """return transformations at params""" + result = [None] * len(params) + for normsubpathitemindex, (indices, params) in self._distributeparams(params).items(): + for index, trafo in zip(indices, self.normsubpathitems[normsubpathitemindex].trafo(params)): + result[index] = trafo + return result + + def transformed(self, trafo): + """return transformed path""" + nnormsubpath = normsubpath(epsilon=self.epsilon) + for pitem in self.normsubpathitems: + nnormsubpath.append(pitem.transformed(trafo)) + if self.closed: + nnormsubpath.close() + elif self.skippedline is not None: + nnormsubpath.append(self.skippedline.transformed(trafo)) + return nnormsubpath + + def outputPS(self, file, writer): + # if the normsubpath is closed, we must not output a normline at + # the end + if not self.normsubpathitems: + return + if self.closed and isinstance(self.normsubpathitems[-1], normline_pt): + assert len(self.normsubpathitems) > 1, "a closed normsubpath should contain more than a single normline_pt" + normsubpathitems = self.normsubpathitems[:-1] + else: + normsubpathitems = self.normsubpathitems + file.write("%g %g moveto\n" % self.atbegin_pt()) + for anormsubpathitem in normsubpathitems: + anormsubpathitem.outputPS(file, writer) + if self.closed: + file.write("closepath\n") + + def outputPDF(self, file, writer): + # if the normsubpath is closed, we must not output a normline at + # the end + if not self.normsubpathitems: + return + if self.closed and isinstance(self.normsubpathitems[-1], normline_pt): + assert len(self.normsubpathitems) > 1, "a closed normsubpath should contain more than a single normline_pt" + normsubpathitems = self.normsubpathitems[:-1] + else: + normsubpathitems = self.normsubpathitems + file.write("%f %f m\n" % self.atbegin_pt()) + for anormsubpathitem in normsubpathitems: + anormsubpathitem.outputPDF(file, writer) + if self.closed: + file.write("h\n") + + +################################################################################ +# normpath +################################################################################ + +class normpathparam: + + """parameter of a certain point along a normpath""" + + __slots__ = "normpath", "normsubpathindex", "normsubpathparam" + + def __init__(self, normpath, normsubpathindex, normsubpathparam): + self.normpath = normpath + self.normsubpathindex = normsubpathindex + self.normsubpathparam = normsubpathparam + float(normsubpathparam) + + def __str__(self): + return "normpathparam(%s, %s, %s)" % (self.normpath, self.normsubpathindex, self.normsubpathparam) + + def __add__(self, other): + if isinstance(other, normpathparam): + assert self.normpath is other.normpath, "normpathparams have to belong to the same normpath" + return self.normpath.arclentoparam_pt(self.normpath.paramtoarclen_pt(self) + + other.normpath.paramtoarclen_pt(other)) + else: + return self.normpath.arclentoparam_pt(self.normpath.paramtoarclen_pt(self) + unit.topt(other)) + + __radd__ = __add__ + + def __sub__(self, other): + if isinstance(other, normpathparam): + assert self.normpath is other.normpath, "normpathparams have to belong to the same normpath" + return self.normpath.arclentoparam_pt(self.normpath.paramtoarclen_pt(self) - + other.normpath.paramtoarclen_pt(other)) + else: + return self.normpath.arclentoparam_pt(self.normpath.paramtoarclen_pt(self) - unit.topt(other)) + + def __rsub__(self, other): + # other has to be a length in this case + return self.normpath.arclentoparam_pt(-self.normpath.paramtoarclen_pt(self) + unit.topt(other)) + + def __mul__(self, factor): + return self.normpath.arclentoparam_pt(self.normpath.paramtoarclen_pt(self) * factor) + + __rmul__ = __mul__ + + def __div__(self, divisor): + return self.normpath.arclentoparam_pt(self.normpath.paramtoarclen_pt(self) / divisor) + + def __neg__(self): + return self.normpath.arclentoparam_pt(-self.normpath.paramtoarclen_pt(self)) + + def __cmp__(self, other): + if isinstance(other, normpathparam): + assert self.normpath is other.normpath, "normpathparams have to belong to the same normpath" + return cmp((self.normsubpathindex, self.normsubpathparam), (other.normsubpathindex, other.normsubpathparam)) + else: + return cmp(self.normpath.paramtoarclen_pt(self), unit.topt(other)) + + def arclen_pt(self): + """return arc length in pts corresponding to the normpathparam """ + return self.normpath.paramtoarclen_pt(self) + + def arclen(self): + """return arc length corresponding to the normpathparam """ + return self.normpath.paramtoarclen(self) + + +def _valueorlistmethod(method): + """Creates a method which takes a single argument or a list and + returns a single value or a list out of method, which always + works on lists.""" + + def wrappedmethod(self, valueorlist, *args, **kwargs): + try: + for item in valueorlist: + break + except: + return method(self, [valueorlist], *args, **kwargs)[0] + return method(self, valueorlist, *args, **kwargs) + return wrappedmethod + + +class normpath: + + """normalized path + + A normalized path consists of a list of normsubpaths. + """ + + def __init__(self, normsubpaths=None): + """construct a normpath from a list of normsubpaths""" + + if normsubpaths is None: + self.normsubpaths = [] # make a fresh list + else: + self.normsubpaths = normsubpaths + for subpath in normsubpaths: + assert isinstance(subpath, normsubpath), "only list of normsubpath instances allowed" + + def __add__(self, other): + """create new normpath out of self and other""" + result = self.copy() + result += other + return result + + def __iadd__(self, other): + """add other inplace""" + for normsubpath in other.normpath().normsubpaths: + self.normsubpaths.append(normsubpath.copy()) + return self + + def __getitem__(self, i): + """return normsubpath i""" + return self.normsubpaths[i] + + def __len__(self): + """return the number of normsubpaths""" + return len(self.normsubpaths) + + def __str__(self): + return "normpath([%s])" % ", ".join(map(str, self.normsubpaths)) + + def _convertparams(self, params, convertmethod): + """return params with all non-normpathparam arguments converted by convertmethod + + usecases: + - self._convertparams(params, self.arclentoparam_pt) + - self._convertparams(params, self.arclentoparam) + """ + + converttoparams = [] + convertparamindices = [] + for i, param in enumerate(params): + if not isinstance(param, normpathparam): + converttoparams.append(param) + convertparamindices.append(i) + if converttoparams: + params = params[:] + for i, param in zip(convertparamindices, convertmethod(converttoparams)): + params[i] = param + return params + + def _distributeparams(self, params): + """return a dictionary mapping subpathindices to a tuple of a paramindices and subpathparams + + subpathindex specifies a subpath containing one or several positions. + paramindex specify the index of the normpathparam in the original list and + subpathparam is the parameter value in the subpath. + """ + + result = {} + for i, param in enumerate(params): + assert param.normpath is self, "normpathparam has to belong to this path" + result.setdefault(param.normsubpathindex, ([], [])) + result[param.normsubpathindex][0].append(i) + result[param.normsubpathindex][1].append(param.normsubpathparam) + return result + + def append(self, item): + """append a normpath by a normsubpath or a pathitem""" + if isinstance(item, normsubpath): + # the normsubpaths list can be appended by a normsubpath only + self.normsubpaths.append(item) + elif isinstance(item, path.pathitem): + # ... but we are kind and allow for regular path items as well + # in order to make a normpath to behave more like a regular path + if self.normsubpaths: + context = path.context(*(self.normsubpaths[-1].atend_pt() + + self.normsubpaths[-1].atbegin_pt())) + item.updatenormpath(self, context) + else: + self.normsubpaths = item.createnormpath(self).normsubpaths + + def arclen_pt(self): + """return arc length in pts""" + return sum([normsubpath.arclen_pt() for normsubpath in self.normsubpaths]) + + def arclen(self): + """return arc length""" + return self.arclen_pt() * unit.t_pt + + def _arclentoparam_pt(self, lengths_pt): + """return the params matching the given lengths_pt""" + # work on a copy which is counted down to negative values + lengths_pt = lengths_pt[:] + results = [None] * len(lengths_pt) + + for normsubpathindex, normsubpath in enumerate(self.normsubpaths): + params, arclen = normsubpath._arclentoparam_pt(lengths_pt) + done = 1 + for i, result in enumerate(results): + if results[i] is None: + lengths_pt[i] -= arclen + if lengths_pt[i] < 0 or normsubpathindex == len(self.normsubpaths) - 1: + # overwrite the results until the length has become negative + results[i] = normpathparam(self, normsubpathindex, params[i]) + done = 0 + if done: + break + + return results + + def arclentoparam_pt(self, lengths_pt): + """return the param(s) matching the given length(s)_pt in pts""" + pass + arclentoparam_pt = _valueorlistmethod(_arclentoparam_pt) + + def arclentoparam(self, lengths): + """return the param(s) matching the given length(s)""" + return self._arclentoparam_pt([unit.topt(l) for l in lengths]) + arclentoparam = _valueorlistmethod(arclentoparam) + + def _at_pt(self, params): + """return coordinates of normpath in pts at params""" + result = [None] * len(params) + for normsubpathindex, (indices, params) in self._distributeparams(params).items(): + for index, point_pt in zip(indices, self.normsubpaths[normsubpathindex].at_pt(params)): + result[index] = point_pt + return result + + def at_pt(self, params): + """return coordinates of normpath in pts at param(s) or lengths in pts""" + return self._at_pt(self._convertparams(params, self.arclentoparam_pt)) + at_pt = _valueorlistmethod(at_pt) + + def at(self, params): + """return coordinates of normpath at param(s) or arc lengths""" + return [(x_pt * unit.t_pt, y_pt * unit.t_pt) + for x_pt, y_pt in self._at_pt(self._convertparams(params, self.arclentoparam))] + at = _valueorlistmethod(at) + + def atbegin_pt(self): + """return coordinates of the beginning of first subpath in normpath in pts""" + if self.normsubpaths: + return self.normsubpaths[0].atbegin_pt() + else: + raise NormpathException("cannot return first point of empty path") + + def atbegin(self): + """return coordinates of the beginning of first subpath in normpath""" + x, y = self.atbegin_pt() + return x * unit.t_pt, y * unit.t_pt + + def atend_pt(self): + """return coordinates of the end of last subpath in normpath in pts""" + if self.normsubpaths: + return self.normsubpaths[-1].atend_pt() + else: + raise NormpathException("cannot return last point of empty path") + + def atend(self): + """return coordinates of the end of last subpath in normpath""" + x, y = self.atend_pt() + return x * unit.t_pt, y * unit.t_pt + + def bbox(self): + """return bbox of normpath""" + abbox = bboxmodule.empty() + for normsubpath in self.normsubpaths: + abbox += normsubpath.bbox() + return abbox + + def begin(self): + """return param corresponding of the beginning of the normpath""" + if self.normsubpaths: + return normpathparam(self, 0, 0) + else: + raise NormpathException("empty path") + + def copy(self): + """return copy of normpath""" + result = normpath() + for normsubpath in self.normsubpaths: + result.append(normsubpath.copy()) + return result + + def _curvature_pt(self, params): + """return the curvature in 1/pts at params + + When the curvature is undefined, the invalid instance is returned.""" + + result = [None] * len(params) + for normsubpathindex, (indices, params) in self._distributeparams(params).items(): + for index, curvature_pt in zip(indices, self.normsubpaths[normsubpathindex].curvature_pt(params)): + result[index] = curvature_pt + return result + + def curvature_pt(self, params): + """return the curvature in 1/pt at params + + The curvature radius is the inverse of the curvature. When the + curvature is undefined, the invalid instance is returned. Note that + this radius can be negative or positive, depending on the sign of the + curvature.""" + + result = [None] * len(params) + for normsubpathindex, (indices, params) in self._distributeparams(params).items(): + for index, curv_pt in zip(indices, self.normsubpaths[normsubpathindex].curvature_pt(params)): + result[index] = curv_pt + return result + curvature_pt = _valueorlistmethod(curvature_pt) + + def _curveradius_pt(self, params): + """return the curvature radius at params in pts + + The curvature radius is the inverse of the curvature. When the + curvature is 0, None is returned. Note that this radius can be negative + or positive, depending on the sign of the curvature.""" + + result = [None] * len(params) + for normsubpathindex, (indices, params) in self._distributeparams(params).items(): + for index, radius_pt in zip(indices, self.normsubpaths[normsubpathindex].curveradius_pt(params)): + result[index] = radius_pt + return result + + def curveradius_pt(self, params): + """return the curvature radius in pts at param(s) or arc length(s) in pts + + The curvature radius is the inverse of the curvature. When the + curvature is 0, None is returned. Note that this radius can be negative + or positive, depending on the sign of the curvature.""" + + return self._curveradius_pt(self._convertparams(params, self.arclentoparam_pt)) + curveradius_pt = _valueorlistmethod(curveradius_pt) + + def curveradius(self, params): + """return the curvature radius at param(s) or arc length(s) + + The curvature radius is the inverse of the curvature. When the + curvature is 0, None is returned. Note that this radius can be negative + or positive, depending on the sign of the curvature.""" + + result = [] + for radius_pt in self._curveradius_pt(self._convertparams(params, self.arclentoparam)): + if radius_pt is not invalid: + result.append(radius_pt * unit.t_pt) + else: + result.append(invalid) + return result + curveradius = _valueorlistmethod(curveradius) + + def end(self): + """return param corresponding of the end of the path""" + if self.normsubpaths: + return normpathparam(self, len(self)-1, len(self.normsubpaths[-1])) + else: + raise NormpathException("empty path") + + def extend(self, normsubpaths): + """extend path by normsubpaths or pathitems""" + for anormsubpath in normsubpaths: + # use append to properly handle regular path items as well as normsubpaths + self.append(anormsubpath) + + def intersect(self, other): + """intersect self with other path + + Returns a tuple of lists consisting of the parameter values + of the intersection points of the corresponding normpath. + """ + other = other.normpath() + + # here we build up the result + intersections = ([], []) + + # Intersect all normsubpaths of self with the normsubpaths of + # other. + for ia, normsubpath_a in enumerate(self.normsubpaths): + for ib, normsubpath_b in enumerate(other.normsubpaths): + for intersection in zip(*normsubpath_a.intersect(normsubpath_b)): + intersections[0].append(normpathparam(self, ia, intersection[0])) + intersections[1].append(normpathparam(other, ib, intersection[1])) + return intersections + + def join(self, other): + """join other normsubpath inplace + + Both normpaths must contain at least one normsubpath. + The last normsubpath of self will be joined to the first + normsubpath of other. + """ + other = other.normpath() + + if not self.normsubpaths: + raise NormpathException("cannot join to empty path") + if not other.normsubpaths: + raise PathException("cannot join empty path") + self.normsubpaths[-1].join(other.normsubpaths[0]) + self.normsubpaths.extend(other.normsubpaths[1:]) + + def joined(self, other): + """return joined self and other + + Both normpaths must contain at least one normsubpath. + The last normsubpath of self will be joined to the first + normsubpath of other. + """ + result = self.copy() + result.join(other.normpath()) + return result + + # << operator also designates joining + __lshift__ = joined + + def normpath(self): + """return a normpath, i.e. self""" + return self + + def _paramtoarclen_pt(self, params): + """return arc lengths in pts matching the given params""" + result = [None] * len(params) + totalarclen_pt = 0 + distributeparams = self._distributeparams(params) + for normsubpathindex in range(max(distributeparams.keys()) + 1): + if distributeparams.has_key(normsubpathindex): + indices, params = distributeparams[normsubpathindex] + arclens_pt, normsubpatharclen_pt = self.normsubpaths[normsubpathindex]._paramtoarclen_pt(params) + for index, arclen_pt in zip(indices, arclens_pt): + result[index] = totalarclen_pt + arclen_pt + totalarclen_pt += normsubpatharclen_pt + else: + totalarclen_pt += self.normsubpaths[normsubpathindex].arclen_pt() + return result + + def paramtoarclen_pt(self, params): + """return arc length(s) in pts matching the given param(s)""" + paramtoarclen_pt = _valueorlistmethod(_paramtoarclen_pt) + + def paramtoarclen(self, params): + """return arc length(s) matching the given param(s)""" + return [arclen_pt * unit.t_pt for arclen_pt in self._paramtoarclen_pt(params)] + paramtoarclen = _valueorlistmethod(paramtoarclen) + + def path(self): + """return path corresponding to normpath""" + pathitems = [] + for normsubpath in self.normsubpaths: + pathitems.extend(normsubpath.pathitems()) + return path.path(*pathitems) + + def reversed(self): + """return reversed path""" + nnormpath = normpath() + for i in range(len(self.normsubpaths)): + nnormpath.normsubpaths.append(self.normsubpaths[-(i+1)].reversed()) + return nnormpath + + def _rotation(self, params): + """return rotation at params""" + result = [None] * len(params) + for normsubpathindex, (indices, params) in self._distributeparams(params).items(): + for index, rotation in zip(indices, self.normsubpaths[normsubpathindex].rotation(params)): + result[index] = rotation + return result + + def rotation_pt(self, params): + """return rotation at param(s) or arc length(s) in pts""" + return self._rotation(self._convertparams(params, self.arclentoparam_pt)) + rotation_pt = _valueorlistmethod(rotation_pt) + + def rotation(self, params): + """return rotation at param(s) or arc length(s)""" + return self._rotation(self._convertparams(params, self.arclentoparam)) + rotation = _valueorlistmethod(rotation) + + def _split_pt(self, params): + """split path at params and return list of normpaths""" + if not params: + return [self.copy()] + + # instead of distributing the parameters, we need to keep their + # order and collect parameters for splitting of normsubpathitem + # with index collectindex + collectindex = None + for param in params: + if param.normsubpathindex != collectindex: + if collectindex is not None: + # append end point depening on the forthcoming index + if param.normsubpathindex > collectindex: + collectparams.append(len(self.normsubpaths[collectindex])) + else: + collectparams.append(0) + # get segments of the normsubpath and add them to the result + segments = self.normsubpaths[collectindex].segments(collectparams) + result[-1].append(segments[0]) + result.extend([normpath([segment]) for segment in segments[1:]]) + # add normsubpathitems and first segment parameter to close the + # gap to the forthcoming index + if param.normsubpathindex > collectindex: + for i in range(collectindex+1, param.normsubpathindex): + result[-1].append(self.normsubpaths[i]) + collectparams = [0] + else: + for i in range(collectindex-1, param.normsubpathindex, -1): + result[-1].append(self.normsubpaths[i].reversed()) + collectparams = [len(self.normsubpaths[param.normsubpathindex])] + else: + result = [normpath(self.normsubpaths[:param.normsubpathindex])] + collectparams = [0] + collectindex = param.normsubpathindex + collectparams.append(param.normsubpathparam) + # add remaining collectparams to the result + collectparams.append(len(self.normsubpaths[collectindex])) + segments = self.normsubpaths[collectindex].segments(collectparams) + result[-1].append(segments[0]) + result.extend([normpath([segment]) for segment in segments[1:]]) + result[-1].extend(self.normsubpaths[collectindex+1:]) + return result + + def split_pt(self, params): + """split path at param(s) or arc length(s) in pts and return list of normpaths""" + try: + for param in params: + break + except: + params = [params] + return self._split_pt(self._convertparams(params, self.arclentoparam_pt)) + + def split(self, params): + """split path at param(s) or arc length(s) and return list of normpaths""" + try: + for param in params: + break + except: + params = [params] + return self._split_pt(self._convertparams(params, self.arclentoparam)) + + def _tangent(self, params, length_pt): + """return tangent vector of path at params + + If length_pt in pts is not None, the tangent vector will be scaled to + the desired length. + """ + + result = [None] * len(params) + tangenttemplate = path.line_pt(0, 0, length_pt, 0).normpath() + for normsubpathindex, (indices, params) in self._distributeparams(params).items(): + for index, atrafo in zip(indices, self.normsubpaths[normsubpathindex].trafo(params)): + if atrafo is invalid: + result[index] = invalid + else: + result[index] = tangenttemplate.transformed(atrafo) + return result + + def tangent_pt(self, params, length_pt): + """return tangent vector of path at param(s) or arc length(s) in pts + + If length in pts is not None, the tangent vector will be scaled to + the desired length. + """ + return self._tangent(self._convertparams(params, self.arclentoparam_pt), length_pt) + tangent_pt = _valueorlistmethod(tangent_pt) + + def tangent(self, params, length): + """return tangent vector of path at param(s) or arc length(s) + + If length is not None, the tangent vector will be scaled to + the desired length. + """ + return self._tangent(self._convertparams(params, self.arclentoparam), unit.topt(length)) + tangent = _valueorlistmethod(tangent) + + def _trafo(self, params): + """return transformation at params""" + result = [None] * len(params) + for normsubpathindex, (indices, params) in self._distributeparams(params).items(): + for index, trafo in zip(indices, self.normsubpaths[normsubpathindex].trafo(params)): + result[index] = trafo + return result + + def trafo_pt(self, params): + """return transformation at param(s) or arc length(s) in pts""" + return self._trafo(self._convertparams(params, self.arclentoparam_pt)) + trafo_pt = _valueorlistmethod(trafo_pt) + + def trafo(self, params): + """return transformation at param(s) or arc length(s)""" + return self._trafo(self._convertparams(params, self.arclentoparam)) + trafo = _valueorlistmethod(trafo) + + def transformed(self, trafo): + """return transformed normpath""" + return normpath([normsubpath.transformed(trafo) for normsubpath in self.normsubpaths]) + + def outputPS(self, file, writer): + for normsubpath in self.normsubpaths: + normsubpath.outputPS(file, writer) + + def outputPDF(self, file, writer): + for normsubpath in self.normsubpaths: + normsubpath.outputPDF(file, writer) diff --git a/compiler/gdsMill/pyx/path.py b/compiler/gdsMill/pyx/path.py new file mode 100644 index 00000000..6352ac59 --- /dev/null +++ b/compiler/gdsMill/pyx/path.py @@ -0,0 +1,1290 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2003-2005 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import nested_scopes + +import math +from math import cos, sin, tan, acos, pi +try: + from math import radians, degrees +except ImportError: + # fallback implementation for Python 2.1 + def radians(x): return x*pi/180 + def degrees(x): return x*180/pi + +import trafo, unit +from normpath import NormpathException, normpath, normsubpath, normline_pt, normcurve_pt +import bbox as bboxmodule + +# set is available as an external interface to the normpath.set method +from normpath import set +# normpath's invalid is available as an external interface +from normpath import invalid + +try: + sum([]) +except NameError: + # fallback implementation for Python 2.2 and below + def sum(list): + return reduce(lambda x, y: x+y, list, 0) + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + +# use new style classes when possible +__metaclass__ = type + +class _marker: pass + +################################################################################ + +# specific exception for path-related problems +class PathException(Exception): pass + +################################################################################ +# Bezier helper functions +################################################################################ + +def _bezierpolyrange(x0, x1, x2, x3): + tc = [0, 1] + + a = x3 - 3*x2 + 3*x1 - x0 + b = 2*x0 - 4*x1 + 2*x2 + c = x1 - x0 + + s = b*b - 4*a*c + if s >= 0: + if b >= 0: + q = -0.5*(b+math.sqrt(s)) + else: + q = -0.5*(b-math.sqrt(s)) + + try: + t = q*1.0/a + except ZeroDivisionError: + pass + else: + if 0 < t < 1: + tc.append(t) + + try: + t = c*1.0/q + except ZeroDivisionError: + pass + else: + if 0 < t < 1: + tc.append(t) + + p = [(((a*t + 1.5*b)*t + 3*c)*t + x0) for t in tc] + + return min(*p), max(*p) + + +def _arctobcurve(x_pt, y_pt, r_pt, phi1, phi2): + """generate the best bezier curve corresponding to an arc segment""" + + dphi = phi2-phi1 + + if dphi==0: return None + + # the two endpoints should be clear + x0_pt, y0_pt = x_pt+r_pt*cos(phi1), y_pt+r_pt*sin(phi1) + x3_pt, y3_pt = x_pt+r_pt*cos(phi2), y_pt+r_pt*sin(phi2) + + # optimal relative distance along tangent for second and third + # control point + l = r_pt*4*(1-cos(dphi/2))/(3*sin(dphi/2)) + + x1_pt, y1_pt = x0_pt-l*sin(phi1), y0_pt+l*cos(phi1) + x2_pt, y2_pt = x3_pt+l*sin(phi2), y3_pt-l*cos(phi2) + + return normcurve_pt(x0_pt, y0_pt, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt) + + +def _arctobezierpath(x_pt, y_pt, r_pt, phi1, phi2, dphimax=45): + apath = [] + + phi1 = radians(phi1) + phi2 = radians(phi2) + dphimax = radians(dphimax) + + if phi2phi1 ... + phi2 = phi2 + (math.floor((phi1-phi2)/(2*pi))+1)*2*pi + elif phi2>phi1+2*pi: + # ... or remove unnecessary multiples of 2*pi + phi2 = phi2 - (math.floor((phi2-phi1)/(2*pi))-1)*2*pi + + if r_pt == 0 or phi1-phi2 == 0: return [] + + subdivisions = abs(int((1.0*(phi1-phi2))/dphimax))+1 + + dphi = (1.0*(phi2-phi1))/subdivisions + + for i in range(subdivisions): + apath.append(_arctobcurve(x_pt, y_pt, r_pt, phi1+i*dphi, phi1+(i+1)*dphi)) + + return apath + +def _arcpoint(x_pt, y_pt, r_pt, angle): + """return starting point of arc segment""" + return x_pt+r_pt*cos(radians(angle)), y_pt+r_pt*sin(radians(angle)) + +def _arcbboxdata(x_pt, y_pt, r_pt, angle1, angle2): + phi1 = radians(angle1) + phi2 = radians(angle2) + + # starting end end point of arc segment + sarcx_pt, sarcy_pt = _arcpoint(x_pt, y_pt, r_pt, angle1) + earcx_pt, earcy_pt = _arcpoint(x_pt, y_pt, r_pt, angle2) + + # Now, we have to determine the corners of the bbox for the + # arc segment, i.e. global maxima/mimima of cos(phi) and sin(phi) + # in the interval [phi1, phi2]. These can either be located + # on the borders of this interval or in the interior. + + if phi2 < phi1: + # guarantee that phi2>phi1 + phi2 = phi2 + (math.floor((phi1-phi2)/(2*pi))+1)*2*pi + + # next minimum of cos(phi) looking from phi1 in counterclockwise + # direction: 2*pi*floor((phi1-pi)/(2*pi)) + 3*pi + + if phi2 < (2*math.floor((phi1-pi)/(2*pi))+3)*pi: + minarcx_pt = min(sarcx_pt, earcx_pt) + else: + minarcx_pt = x_pt-r_pt + + # next minimum of sin(phi) looking from phi1 in counterclockwise + # direction: 2*pi*floor((phi1-3*pi/2)/(2*pi)) + 7/2*pi + + if phi2 < (2*math.floor((phi1-3.0*pi/2)/(2*pi))+7.0/2)*pi: + minarcy_pt = min(sarcy_pt, earcy_pt) + else: + minarcy_pt = y_pt-r_pt + + # next maximum of cos(phi) looking from phi1 in counterclockwise + # direction: 2*pi*floor((phi1)/(2*pi))+2*pi + + if phi2 < (2*math.floor((phi1)/(2*pi))+2)*pi: + maxarcx_pt = max(sarcx_pt, earcx_pt) + else: + maxarcx_pt = x_pt+r_pt + + # next maximum of sin(phi) looking from phi1 in counterclockwise + # direction: 2*pi*floor((phi1-pi/2)/(2*pi)) + 1/2*pi + + if phi2 < (2*math.floor((phi1-pi/2)/(2*pi))+5.0/2)*pi: + maxarcy_pt = max(sarcy_pt, earcy_pt) + else: + maxarcy_pt = y_pt+r_pt + + return minarcx_pt, minarcy_pt, maxarcx_pt, maxarcy_pt + + +################################################################################ +# path context and pathitem base class +################################################################################ + +class context: + + """context for pathitem""" + + def __init__(self, x_pt, y_pt, subfirstx_pt, subfirsty_pt): + """initializes a context for path items + + x_pt, y_pt are the currentpoint. subfirstx_pt, subfirsty_pt + are the starting point of the current subpath. There are no + invalid contexts, i.e. all variables need to be set to integer + or float numbers. + """ + self.x_pt = x_pt + self.y_pt = y_pt + self.subfirstx_pt = subfirstx_pt + self.subfirsty_pt = subfirsty_pt + + +class pathitem: + + """element of a PS style path""" + + def __str__(self): + raise NotImplementedError() + + def createcontext(self): + """creates a context from the current pathitem + + Returns a context instance. Is called, when no context has yet + been defined, i.e. for the very first pathitem. Most of the + pathitems do not provide this method. Note, that you should pass + the context created by createcontext to updatebbox and updatenormpath + of successive pathitems only; use the context-free createbbox and + createnormpath for the first pathitem instead. + """ + raise PathException("path must start with moveto or the like (%r)" % self) + + def createbbox(self): + """creates a bbox from the current pathitem + + Returns a bbox instance. Is called, when a bbox has to be + created instead of updating it, i.e. for the very first + pathitem. Most pathitems do not provide this method. + updatebbox must not be called for the created instance and the + same pathitem. + """ + raise PathException("path must start with moveto or the like (%r)" % self) + + def createnormpath(self, epsilon=_marker): + """create a normpath from the current pathitem + + Return a normpath instance. Is called, when a normpath has to + be created instead of updating it, i.e. for the very first + pathitem. Most pathitems do not provide this method. + updatenormpath must not be called for the created instance and + the same pathitem. + """ + raise PathException("path must start with moveto or the like (%r)" % self) + + def updatebbox(self, bbox, context): + """updates the bbox to contain the pathitem for the given + context + + Is called for all subsequent pathitems in a path to complete + the bbox information. Both, the bbox and context are updated + inplace. Does not return anything. + """ + raise NotImplementedError() + + def updatenormpath(self, normpath, context): + """update the normpath to contain the pathitem for the given + context + + Is called for all subsequent pathitems in a path to complete + the normpath. Both the normpath and the context are updated + inplace. Most pathitem implementations will use + normpath.normsubpath[-1].append to add normsubpathitem(s). + Does not return anything. + """ + raise NotImplementedError() + + def outputPS(self, file, writer): + """write PS representation of pathitem to file""" + + + +################################################################################ +# various pathitems +################################################################################ +# Each one comes in two variants: +# - one with suffix _pt. This one requires the coordinates +# to be already in pts (mainly used for internal purposes) +# - another which accepts arbitrary units + + +class closepath(pathitem): + + """Connect subpath back to its starting point""" + + __slots__ = () + + def __str__(self): + return "closepath()" + + def updatebbox(self, bbox, context): + context.x_pt = context.subfirstx_pt + context.y_pt = context.subfirsty_pt + + def updatenormpath(self, normpath, context): + normpath.normsubpaths[-1].close() + context.x_pt = context.subfirstx_pt + context.y_pt = context.subfirsty_pt + + def outputPS(self, file, writer): + file.write("closepath\n") + + +class moveto_pt(pathitem): + + """Start a new subpath and set current point to (x_pt, y_pt) (coordinates in pts)""" + + __slots__ = "x_pt", "y_pt" + + def __init__(self, x_pt, y_pt): + self.x_pt = x_pt + self.y_pt = y_pt + + def __str__(self): + return "moveto_pt(%g, %g)" % (self.x_pt, self.y_pt) + + def createcontext(self): + return context(self.x_pt, self.y_pt, self.x_pt, self.y_pt) + + def createbbox(self): + return bboxmodule.bbox_pt(self.x_pt, self.y_pt, self.x_pt, self.y_pt) + + def createnormpath(self, epsilon=_marker): + if epsilon is _marker: + return normpath([normsubpath([normline_pt(self.x_pt, self.y_pt, self.x_pt, self.y_pt)])]) + else: + return normpath([normsubpath([normline_pt(self.x_pt, self.y_pt, self.x_pt, self.y_pt)], + epsilon=epsilon)]) + + def updatebbox(self, bbox, context): + bbox.includepoint_pt(self.x_pt, self.y_pt) + context.x_pt = context.subfirstx_pt = self.x_pt + context.y_pt = context.subfirsty_pt = self.y_pt + + def updatenormpath(self, normpath, context): + if normpath.normsubpaths[-1].epsilon is not None: + normpath.append(normsubpath([normline_pt(self.x_pt, self.y_pt, self.x_pt, self.y_pt)], + epsilon=normpath.normsubpaths[-1].epsilon)) + else: + normpath.append(normsubpath(epsilon=normpath.normsubpaths[-1].epsilon)) + context.x_pt = context.subfirstx_pt = self.x_pt + context.y_pt = context.subfirsty_pt = self.y_pt + + def outputPS(self, file, writer): + file.write("%g %g moveto\n" % (self.x_pt, self.y_pt) ) + + +class lineto_pt(pathitem): + + """Append straight line to (x_pt, y_pt) (coordinates in pts)""" + + __slots__ = "x_pt", "y_pt" + + def __init__(self, x_pt, y_pt): + self.x_pt = x_pt + self.y_pt = y_pt + + def __str__(self): + return "lineto_pt(%g, %g)" % (self.x_pt, self.y_pt) + + def updatebbox(self, bbox, context): + bbox.includepoint_pt(self.x_pt, self.y_pt) + context.x_pt = self.x_pt + context.y_pt = self.y_pt + + def updatenormpath(self, normpath, context): + normpath.normsubpaths[-1].append(normline_pt(context.x_pt, context.y_pt, + self.x_pt, self.y_pt)) + context.x_pt = self.x_pt + context.y_pt = self.y_pt + + def outputPS(self, file, writer): + file.write("%g %g lineto\n" % (self.x_pt, self.y_pt) ) + + +class curveto_pt(pathitem): + + """Append curveto (coordinates in pts)""" + + __slots__ = "x1_pt", "y1_pt", "x2_pt", "y2_pt", "x3_pt", "y3_pt" + + def __init__(self, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt): + self.x1_pt = x1_pt + self.y1_pt = y1_pt + self.x2_pt = x2_pt + self.y2_pt = y2_pt + self.x3_pt = x3_pt + self.y3_pt = y3_pt + + def __str__(self): + return "curveto_pt(%g, %g, %g, %g, %g, %g)" % (self.x1_pt, self.y1_pt, + self.x2_pt, self.y2_pt, + self.x3_pt, self.y3_pt) + + def updatebbox(self, bbox, context): + xmin_pt, xmax_pt = _bezierpolyrange(context.x_pt, self.x1_pt, self.x2_pt, self.x3_pt) + ymin_pt, ymax_pt = _bezierpolyrange(context.y_pt, self.y1_pt, self.y2_pt, self.y3_pt) + bbox.includepoint_pt(xmin_pt, ymin_pt) + bbox.includepoint_pt(xmax_pt, ymax_pt) + context.x_pt = self.x3_pt + context.y_pt = self.y3_pt + + def updatenormpath(self, normpath, context): + normpath.normsubpaths[-1].append(normcurve_pt(context.x_pt, context.y_pt, + self.x1_pt, self.y1_pt, + self.x2_pt, self.y2_pt, + self.x3_pt, self.y3_pt)) + context.x_pt = self.x3_pt + context.y_pt = self.y3_pt + + def outputPS(self, file, writer): + file.write("%g %g %g %g %g %g curveto\n" % (self.x1_pt, self.y1_pt, + self.x2_pt, self.y2_pt, + self.x3_pt, self.y3_pt)) + + +class rmoveto_pt(pathitem): + + """Perform relative moveto (coordinates in pts)""" + + __slots__ = "dx_pt", "dy_pt" + + def __init__(self, dx_pt, dy_pt): + self.dx_pt = dx_pt + self.dy_pt = dy_pt + + def __str__(self): + return "rmoveto_pt(%g, %g)" % (self.dx_pt, self.dy_pt) + + def updatebbox(self, bbox, context): + bbox.includepoint_pt(context.x_pt + self.dx_pt, context.y_pt + self.dy_pt) + context.x_pt += self.dx_pt + context.y_pt += self.dy_pt + context.subfirstx_pt = context.x_pt + context.subfirsty_pt = context.y_pt + + def updatenormpath(self, normpath, context): + context.x_pt += self.dx_pt + context.y_pt += self.dy_pt + context.subfirstx_pt = context.x_pt + context.subfirsty_pt = context.y_pt + if normpath.normsubpaths[-1].epsilon is not None: + normpath.append(normsubpath([normline_pt(context.x_pt, context.y_pt, + context.x_pt, context.y_pt)], + epsilon=normpath.normsubpaths[-1].epsilon)) + else: + normpath.append(normsubpath(epsilon=normpath.normsubpaths[-1].epsilon)) + + def outputPS(self, file, writer): + file.write("%g %g rmoveto\n" % (self.dx_pt, self.dy_pt) ) + + +class rlineto_pt(pathitem): + + """Perform relative lineto (coordinates in pts)""" + + __slots__ = "dx_pt", "dy_pt" + + def __init__(self, dx_pt, dy_pt): + self.dx_pt = dx_pt + self.dy_pt = dy_pt + + def __str__(self): + return "rlineto_pt(%g %g)" % (self.dx_pt, self.dy_pt) + + def updatebbox(self, bbox, context): + bbox.includepoint_pt(context.x_pt + self.dx_pt, context.y_pt + self.dy_pt) + context.x_pt += self.dx_pt + context.y_pt += self.dy_pt + + def updatenormpath(self, normpath, context): + normpath.normsubpaths[-1].append(normline_pt(context.x_pt, context.y_pt, + context.x_pt + self.dx_pt, context.y_pt + self.dy_pt)) + context.x_pt += self.dx_pt + context.y_pt += self.dy_pt + + def outputPS(self, file, writer): + file.write("%g %g rlineto\n" % (self.dx_pt, self.dy_pt) ) + + +class rcurveto_pt(pathitem): + + """Append rcurveto (coordinates in pts)""" + + __slots__ = "dx1_pt", "dy1_pt", "dx2_pt", "dy2_pt", "dx3_pt", "dy3_pt" + + def __init__(self, dx1_pt, dy1_pt, dx2_pt, dy2_pt, dx3_pt, dy3_pt): + self.dx1_pt = dx1_pt + self.dy1_pt = dy1_pt + self.dx2_pt = dx2_pt + self.dy2_pt = dy2_pt + self.dx3_pt = dx3_pt + self.dy3_pt = dy3_pt + + def __str__(self): + return "rcurveto_pt(%g, %g, %g, %g, %g, %g)" % (self.dx1_pt, self.dy1_pt, + self.dx2_pt, self.dy2_pt, + self.dx3_pt, self.dy3_pt) + + def updatebbox(self, bbox, context): + xmin_pt, xmax_pt = _bezierpolyrange(context.x_pt, + context.x_pt+self.dx1_pt, + context.x_pt+self.dx2_pt, + context.x_pt+self.dx3_pt) + ymin_pt, ymax_pt = _bezierpolyrange(context.y_pt, + context.y_pt+self.dy1_pt, + context.y_pt+self.dy2_pt, + context.y_pt+self.dy3_pt) + bbox.includepoint_pt(xmin_pt, ymin_pt) + bbox.includepoint_pt(xmax_pt, ymax_pt) + context.x_pt += self.dx3_pt + context.y_pt += self.dy3_pt + + def updatenormpath(self, normpath, context): + normpath.normsubpaths[-1].append(normcurve_pt(context.x_pt, context.y_pt, + context.x_pt + self.dx1_pt, context.y_pt + self.dy1_pt, + context.x_pt + self.dx2_pt, context.y_pt + self.dy2_pt, + context.x_pt + self.dx3_pt, context.y_pt + self.dy3_pt)) + context.x_pt += self.dx3_pt + context.y_pt += self.dy3_pt + + def outputPS(self, file, writer): + file.write("%g %g %g %g %g %g rcurveto\n" % (self.dx1_pt, self.dy1_pt, + self.dx2_pt, self.dy2_pt, + self.dx3_pt, self.dy3_pt)) + + +class arc_pt(pathitem): + + """Append counterclockwise arc (coordinates in pts)""" + + __slots__ = "x_pt", "y_pt", "r_pt", "angle1", "angle2" + + def __init__(self, x_pt, y_pt, r_pt, angle1, angle2): + self.x_pt = x_pt + self.y_pt = y_pt + self.r_pt = r_pt + self.angle1 = angle1 + self.angle2 = angle2 + + def __str__(self): + return "arc_pt(%g, %g, %g, %g, %g)" % (self.x_pt, self.y_pt, self.r_pt, + self.angle1, self.angle2) + + def createcontext(self): + x_pt, y_pt = _arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle2) + return context(x_pt, y_pt, x_pt, y_pt) + + def createbbox(self): + return bboxmodule.bbox_pt(*_arcbboxdata(self.x_pt, self.y_pt, self.r_pt, + self.angle1, self.angle2)) + + def createnormpath(self, epsilon=_marker): + if epsilon is _marker: + return normpath([normsubpath(_arctobezierpath(self.x_pt, self.y_pt, self.r_pt, self.angle1, self.angle2))]) + else: + return normpath([normsubpath(_arctobezierpath(self.x_pt, self.y_pt, self.r_pt, self.angle1, self.angle2), + epsilon=epsilon)]) + + def updatebbox(self, bbox, context): + minarcx_pt, minarcy_pt, maxarcx_pt, maxarcy_pt = _arcbboxdata(self.x_pt, self.y_pt, self.r_pt, + self.angle1, self.angle2) + bbox.includepoint_pt(minarcx_pt, minarcy_pt) + bbox.includepoint_pt(maxarcx_pt, maxarcy_pt) + context.x_pt, context.y_pt = _arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle2) + + def updatenormpath(self, normpath, context): + if normpath.normsubpaths[-1].closed: + normpath.append(normsubpath([normline_pt(context.x_pt, context.y_pt, + *_arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle1))], + epsilon=normpath.normsubpaths[-1].epsilon)) + else: + normpath.normsubpaths[-1].append(normline_pt(context.x_pt, context.y_pt, + *_arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle1))) + normpath.normsubpaths[-1].extend(_arctobezierpath(self.x_pt, self.y_pt, self.r_pt, self.angle1, self.angle2)) + context.x_pt, context.y_pt = _arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle2) + + def outputPS(self, file, writer): + file.write("%g %g %g %g %g arc\n" % (self.x_pt, self.y_pt, + self.r_pt, + self.angle1, + self.angle2)) + + +class arcn_pt(pathitem): + + """Append clockwise arc (coordinates in pts)""" + + __slots__ = "x_pt", "y_pt", "r_pt", "angle1", "angle2" + + def __init__(self, x_pt, y_pt, r_pt, angle1, angle2): + self.x_pt = x_pt + self.y_pt = y_pt + self.r_pt = r_pt + self.angle1 = angle1 + self.angle2 = angle2 + + def __str__(self): + return "arcn_pt(%g, %g, %g, %g, %g)" % (self.x_pt, self.y_pt, self.r_pt, + self.angle1, self.angle2) + + def createcontext(self): + x_pt, y_pt = _arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle2) + return context(x_pt, y_pt, x_pt, y_pt) + + def createbbox(self): + return bboxmodule.bbox_pt(*_arcbboxdata(self.x_pt, self.y_pt, self.r_pt, + self.angle2, self.angle1)) + + def createnormpath(self, epsilon=_marker): + if epsilon is _marker: + return normpath([normsubpath(_arctobezierpath(self.x_pt, self.y_pt, self.r_pt, self.angle2, self.angle1))]).reversed() + else: + return normpath([normsubpath(_arctobezierpath(self.x_pt, self.y_pt, self.r_pt, self.angle2, self.angle1), + epsilon=epsilon)]).reversed() + + def updatebbox(self, bbox, context): + minarcx_pt, minarcy_pt, maxarcx_pt, maxarcy_pt = _arcbboxdata(self.x_pt, self.y_pt, self.r_pt, + self.angle2, self.angle1) + bbox.includepoint_pt(minarcx_pt, minarcy_pt) + bbox.includepoint_pt(maxarcx_pt, maxarcy_pt) + context.x_pt, context.y_pt = _arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle2) + + def updatenormpath(self, normpath, context): + if normpath.normsubpaths[-1].closed: + normpath.append(normsubpath([normline_pt(context.x_pt, context.y_pt, + *_arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle1))], + epsilon=normpath.normsubpaths[-1].epsilon)) + else: + normpath.normsubpaths[-1].append(normline_pt(context.x_pt, context.y_pt, + *_arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle1))) + bpathitems = _arctobezierpath(self.x_pt, self.y_pt, self.r_pt, self.angle2, self.angle1) + bpathitems.reverse() + for bpathitem in bpathitems: + normpath.normsubpaths[-1].append(bpathitem.reversed()) + context.x_pt, context.y_pt = _arcpoint(self.x_pt, self.y_pt, self.r_pt, self.angle2) + + def outputPS(self, file, writer): + file.write("%g %g %g %g %g arcn\n" % (self.x_pt, self.y_pt, + self.r_pt, + self.angle1, + self.angle2)) + + +class arct_pt(pathitem): + + """Append tangent arc (coordinates in pts)""" + + __slots__ = "x1_pt", "y1_pt", "x2_pt", "y2_pt", "r_pt" + + def __init__(self, x1_pt, y1_pt, x2_pt, y2_pt, r_pt): + self.x1_pt = x1_pt + self.y1_pt = y1_pt + self.x2_pt = x2_pt + self.y2_pt = y2_pt + self.r_pt = r_pt + + def __str__(self): + return "arct_pt(%g, %g, %g, %g, %g)" % (self.x1_pt, self.y1_pt, + self.x2_pt, self.y2_pt, + self.r_pt) + + def _pathitems(self, x_pt, y_pt): + """return pathitems corresponding to arct for given currentpoint x_pt, y_pt. + + The return is a list containing line_pt, arc_pt, a arcn_pt instances. + + This is a helper routine for updatebbox and updatenormpath, + which will delegate the work to the constructed pathitem. + """ + + # direction of tangent 1 + dx1_pt, dy1_pt = self.x1_pt-x_pt, self.y1_pt-y_pt + l1_pt = math.hypot(dx1_pt, dy1_pt) + dx1, dy1 = dx1_pt/l1_pt, dy1_pt/l1_pt + + # direction of tangent 2 + dx2_pt, dy2_pt = self.x2_pt-self.x1_pt, self.y2_pt-self.y1_pt + l2_pt = math.hypot(dx2_pt, dy2_pt) + dx2, dy2 = dx2_pt/l2_pt, dy2_pt/l2_pt + + # intersection angle between two tangents in the range (-pi, pi). + # We take the orientation from the sign of the vector product. + # Negative (positive) angles alpha corresponds to a turn to the right (left) + # as seen from currentpoint. + if dx1*dy2-dy1*dx2 > 0: + alpha = acos(dx1*dx2+dy1*dy2) + else: + alpha = -acos(dx1*dx2+dy1*dy2) + + try: + # two tangent points + xt1_pt = self.x1_pt - dx1*self.r_pt*tan(abs(alpha)/2) + yt1_pt = self.y1_pt - dy1*self.r_pt*tan(abs(alpha)/2) + xt2_pt = self.x1_pt + dx2*self.r_pt*tan(abs(alpha)/2) + yt2_pt = self.y1_pt + dy2*self.r_pt*tan(abs(alpha)/2) + + # direction point 1 -> center of arc + dmx_pt = 0.5*(xt1_pt+xt2_pt) - self.x1_pt + dmy_pt = 0.5*(yt1_pt+yt2_pt) - self.y1_pt + lm_pt = math.hypot(dmx_pt, dmy_pt) + dmx, dmy = dmx_pt/lm_pt, dmy_pt/lm_pt + + # center of arc + mx_pt = self.x1_pt + dmx*self.r_pt/cos(alpha/2) + my_pt = self.y1_pt + dmy*self.r_pt/cos(alpha/2) + + # angle around which arc is centered + phi = degrees(math.atan2(-dmy, -dmx)) + + # half angular width of arc + deltaphi = degrees(alpha)/2 + + line = lineto_pt(*_arcpoint(mx_pt, my_pt, self.r_pt, phi-deltaphi)) + if alpha > 0: + return [line, arc_pt(mx_pt, my_pt, self.r_pt, phi-deltaphi, phi+deltaphi)] + else: + return [line, arcn_pt(mx_pt, my_pt, self.r_pt, phi-deltaphi, phi+deltaphi)] + + except ZeroDivisionError: + # in the degenerate case, we just return a line as specified by the PS + # language reference + return [lineto_pt(self.x1_pt, self.y1_pt)] + + def updatebbox(self, bbox, context): + for pathitem in self._pathitems(context.x_pt, context.y_pt): + pathitem.updatebbox(bbox, context) + + def updatenormpath(self, normpath, context): + for pathitem in self._pathitems(context.x_pt, context.y_pt): + pathitem.updatenormpath(normpath, context) + + def outputPS(self, file, writer): + file.write("%g %g %g %g %g arct\n" % (self.x1_pt, self.y1_pt, + self.x2_pt, self.y2_pt, + self.r_pt)) + +# +# now the pathitems that convert from user coordinates to pts +# + +class moveto(moveto_pt): + + """Set current point to (x, y)""" + + __slots__ = "x_pt", "y_pt" + + def __init__(self, x, y): + moveto_pt.__init__(self, unit.topt(x), unit.topt(y)) + + +class lineto(lineto_pt): + + """Append straight line to (x, y)""" + + __slots__ = "x_pt", "y_pt" + + def __init__(self, x, y): + lineto_pt.__init__(self, unit.topt(x), unit.topt(y)) + + +class curveto(curveto_pt): + + """Append curveto""" + + __slots__ = "x1_pt", "y1_pt", "x2_pt", "y2_pt", "x3_pt", "y3_pt" + + def __init__(self, x1, y1, x2, y2, x3, y3): + curveto_pt.__init__(self, + unit.topt(x1), unit.topt(y1), + unit.topt(x2), unit.topt(y2), + unit.topt(x3), unit.topt(y3)) + +class rmoveto(rmoveto_pt): + + """Perform relative moveto""" + + __slots__ = "dx_pt", "dy_pt" + + def __init__(self, dx, dy): + rmoveto_pt.__init__(self, unit.topt(dx), unit.topt(dy)) + + +class rlineto(rlineto_pt): + + """Perform relative lineto""" + + __slots__ = "dx_pt", "dy_pt" + + def __init__(self, dx, dy): + rlineto_pt.__init__(self, unit.topt(dx), unit.topt(dy)) + + +class rcurveto(rcurveto_pt): + + """Append rcurveto""" + + __slots__ = "dx1_pt", "dy1_pt", "dx2_pt", "dy2_pt", "dx3_pt", "dy3_pt" + + def __init__(self, dx1, dy1, dx2, dy2, dx3, dy3): + rcurveto_pt.__init__(self, + unit.topt(dx1), unit.topt(dy1), + unit.topt(dx2), unit.topt(dy2), + unit.topt(dx3), unit.topt(dy3)) + + +class arcn(arcn_pt): + + """Append clockwise arc""" + + __slots__ = "x_pt", "y_pt", "r_pt", "angle1", "angle2" + + def __init__(self, x, y, r, angle1, angle2): + arcn_pt.__init__(self, unit.topt(x), unit.topt(y), unit.topt(r), angle1, angle2) + + +class arc(arc_pt): + + """Append counterclockwise arc""" + + __slots__ = "x_pt", "y_pt", "r_pt", "angle1", "angle2" + + def __init__(self, x, y, r, angle1, angle2): + arc_pt.__init__(self, unit.topt(x), unit.topt(y), unit.topt(r), angle1, angle2) + + +class arct(arct_pt): + + """Append tangent arc""" + + __slots__ = "x1_pt", "y1_pt", "x2_pt", "y2_pt", "r_pt" + + def __init__(self, x1, y1, x2, y2, r): + arct_pt.__init__(self, unit.topt(x1), unit.topt(y1), + unit.topt(x2), unit.topt(y2), unit.topt(r)) + +# +# "combined" pathitems provided for performance reasons +# + +class multilineto_pt(pathitem): + + """Perform multiple linetos (coordinates in pts)""" + + __slots__ = "points_pt" + + def __init__(self, points_pt): + self.points_pt = points_pt + + def __str__(self): + result = [] + for point_pt in self.points_pt: + result.append("(%g, %g)" % point_pt ) + return "multilineto_pt([%s])" % (", ".join(result)) + + def updatebbox(self, bbox, context): + for point_pt in self.points_pt: + bbox.includepoint_pt(*point_pt) + if self.points_pt: + context.x_pt, context.y_pt = self.points_pt[-1] + + def updatenormpath(self, normpath, context): + x0_pt, y0_pt = context.x_pt, context.y_pt + for point_pt in self.points_pt: + normpath.normsubpaths[-1].append(normline_pt(x0_pt, y0_pt, *point_pt)) + x0_pt, y0_pt = point_pt + context.x_pt, context.y_pt = x0_pt, y0_pt + + def outputPS(self, file, writer): + for point_pt in self.points_pt: + file.write("%g %g lineto\n" % point_pt ) + + +class multicurveto_pt(pathitem): + + """Perform multiple curvetos (coordinates in pts)""" + + __slots__ = "points_pt" + + def __init__(self, points_pt): + self.points_pt = points_pt + + def __str__(self): + result = [] + for point_pt in self.points_pt: + result.append("(%g, %g, %g, %g, %g, %g)" % point_pt ) + return "multicurveto_pt([%s])" % (", ".join(result)) + + def updatebbox(self, bbox, context): + for point_pt in self.points_pt: + bbox.includepoint_pt(*point_pt[0: 2]) + bbox.includepoint_pt(*point_pt[2: 4]) + bbox.includepoint_pt(*point_pt[4: 6]) + if self.points_pt: + context.x_pt, context.y_pt = self.points_pt[-1][4:] + + def updatenormpath(self, normpath, context): + x0_pt, y0_pt = context.x_pt, context.y_pt + for point_pt in self.points_pt: + normpath.normsubpaths[-1].append(normcurve_pt(x0_pt, y0_pt, *point_pt)) + x0_pt, y0_pt = point_pt[4:] + context.x_pt, context.y_pt = x0_pt, y0_pt + + def outputPS(self, file, writer): + for point_pt in self.points_pt: + file.write("%g %g %g %g %g %g curveto\n" % point_pt) + + +################################################################################ +# path: PS style path +################################################################################ + +class path: + + """PS style path""" + + __slots__ = "pathitems", "_normpath" + + def __init__(self, *pathitems): + """construct a path from pathitems *args""" + + for apathitem in pathitems: + assert isinstance(apathitem, pathitem), "only pathitem instances allowed" + + self.pathitems = list(pathitems) + # normpath cache (when no epsilon is set) + self._normpath = None + + def __add__(self, other): + """create new path out of self and other""" + return path(*(self.pathitems + other.path().pathitems)) + + def __iadd__(self, other): + """add other inplace + + If other is a normpath instance, it is converted to a path before + being added. + """ + self.pathitems += other.path().pathitems + self._normpath = None + return self + + def __getitem__(self, i): + """return path item i""" + return self.pathitems[i] + + def __len__(self): + """return the number of path items""" + return len(self.pathitems) + + def __str__(self): + l = ", ".join(map(str, self.pathitems)) + return "path(%s)" % l + + def append(self, apathitem): + """append a path item""" + assert isinstance(apathitem, pathitem), "only pathitem instance allowed" + self.pathitems.append(apathitem) + self._normpath = None + + def arclen_pt(self): + """return arc length in pts""" + return self.normpath().arclen_pt() + + def arclen(self): + """return arc length""" + return self.normpath().arclen() + + def arclentoparam_pt(self, lengths_pt): + """return the param(s) matching the given length(s)_pt in pts""" + return self.normpath().arclentoparam_pt(lengths_pt) + + def arclentoparam(self, lengths): + """return the param(s) matching the given length(s)""" + return self.normpath().arclentoparam(lengths) + + def at_pt(self, params): + """return coordinates of path in pts at param(s) or arc length(s) in pts""" + return self.normpath().at_pt(params) + + def at(self, params): + """return coordinates of path at param(s) or arc length(s)""" + return self.normpath().at(params) + + def atbegin_pt(self): + """return coordinates of the beginning of first subpath in path in pts""" + return self.normpath().atbegin_pt() + + def atbegin(self): + """return coordinates of the beginning of first subpath in path""" + return self.normpath().atbegin() + + def atend_pt(self): + """return coordinates of the end of last subpath in path in pts""" + return self.normpath().atend_pt() + + def atend(self): + """return coordinates of the end of last subpath in path""" + return self.normpath().atend() + + def bbox(self): + """return bbox of path""" + if self.pathitems: + bbox = self.pathitems[0].createbbox() + context = self.pathitems[0].createcontext() + for pathitem in self.pathitems[1:]: + pathitem.updatebbox(bbox, context) + return bbox + else: + return bboxmodule.empty() + + def begin(self): + """return param corresponding of the beginning of the path""" + return self.normpath().begin() + + def curveradius_pt(self, params): + """return the curvature radius in pts at param(s) or arc length(s) in pts + + The curvature radius is the inverse of the curvature. When the + curvature is 0, None is returned. Note that this radius can be negative + or positive, depending on the sign of the curvature.""" + return self.normpath().curveradius_pt(params) + + def curveradius(self, params): + """return the curvature radius at param(s) or arc length(s) + + The curvature radius is the inverse of the curvature. When the + curvature is 0, None is returned. Note that this radius can be negative + or positive, depending on the sign of the curvature.""" + return self.normpath().curveradius(params) + + def end(self): + """return param corresponding of the end of the path""" + return self.normpath().end() + + def extend(self, pathitems): + """extend path by pathitems""" + for apathitem in pathitems: + assert isinstance(apathitem, pathitem), "only pathitem instance allowed" + self.pathitems.extend(pathitems) + self._normpath = None + + def intersect(self, other): + """intersect self with other path + + Returns a tuple of lists consisting of the parameter values + of the intersection points of the corresponding normpath. + """ + return self.normpath().intersect(other) + + def join(self, other): + """join other path/normpath inplace + + If other is a normpath instance, it is converted to a path before + being joined. + """ + self.pathitems = self.joined(other).path().pathitems + self._normpath = None + return self + + def joined(self, other): + """return path consisting of self and other joined together""" + return self.normpath().joined(other).path() + + # << operator also designates joining + __lshift__ = joined + + def normpath(self, epsilon=_marker): + """convert the path into a normpath""" + # use cached value if existent and epsilon is _marker + if self._normpath is not None and epsilon is _marker: + return self._normpath + if self.pathitems: + if epsilon is _marker: + normpath = self.pathitems[0].createnormpath() + else: + normpath = self.pathitems[0].createnormpath(epsilon) + context = self.pathitems[0].createcontext() + for pathitem in self.pathitems[1:]: + pathitem.updatenormpath(normpath, context) + else: + if epsilon is _marker: + normpath = normpath([]) + else: + normpath = normpath(epsilon=epsilon) + if epsilon is _marker: + self._normpath = normpath + return normpath + + def paramtoarclen_pt(self, params): + """return arc lenght(s) in pts matching the given param(s)""" + return self.normpath().paramtoarclen_pt(params) + + def paramtoarclen(self, params): + """return arc lenght(s) matching the given param(s)""" + return self.normpath().paramtoarclen(params) + + def path(self): + """return corresponding path, i.e., self""" + return self + + def reversed(self): + """return reversed normpath""" + # TODO: couldn't we try to return a path instead of converting it + # to a normpath (but this might not be worth the trouble) + return self.normpath().reversed() + + def rotation_pt(self, params): + """return rotation at param(s) or arc length(s) in pts""" + return self.normpath().rotation(params) + + def rotation(self, params): + """return rotation at param(s) or arc length(s)""" + return self.normpath().rotation(params) + + def split_pt(self, params): + """split normpath at param(s) or arc length(s) in pts and return list of normpaths""" + return self.normpath().split(params) + + def split(self, params): + """split normpath at param(s) or arc length(s) and return list of normpaths""" + return self.normpath().split(params) + + def tangent_pt(self, params, length): + """return tangent vector of path at param(s) or arc length(s) in pts + + If length in pts is not None, the tangent vector will be scaled to + the desired length. + """ + return self.normpath().tangent_pt(params, length) + + def tangent(self, params, length=1): + """return tangent vector of path at param(s) or arc length(s) + + If length is not None, the tangent vector will be scaled to + the desired length. + """ + return self.normpath().tangent(params, length) + + def trafo_pt(self, params): + """return transformation at param(s) or arc length(s) in pts""" + return self.normpath().trafo(params) + + def trafo(self, params): + """return transformation at param(s) or arc length(s)""" + return self.normpath().trafo(params) + + def transformed(self, trafo): + """return transformed path""" + return self.normpath().transformed(trafo) + + def outputPS(self, file, writer): + """write PS code to file""" + for pitem in self.pathitems: + pitem.outputPS(file, writer) + + def outputPDF(self, file, writer): + """write PDF code to file""" + # PDF only supports normsubpathitems; we need to use a normpath + # with epsilon equals None to prevent failure for paths shorter + # than epsilon + self.normpath(epsilon=None).outputPDF(file, writer) + + +# +# some special kinds of path, again in two variants +# + +class line_pt(path): + + """straight line from (x1_pt, y1_pt) to (x2_pt, y2_pt) in pts""" + + def __init__(self, x1_pt, y1_pt, x2_pt, y2_pt): + path.__init__(self, moveto_pt(x1_pt, y1_pt), lineto_pt(x2_pt, y2_pt)) + + +class curve_pt(path): + + """bezier curve with control points (x0_pt, y1_pt),..., (x3_pt, y3_pt) in pts""" + + def __init__(self, x0_pt, y0_pt, x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt): + path.__init__(self, + moveto_pt(x0_pt, y0_pt), + curveto_pt(x1_pt, y1_pt, x2_pt, y2_pt, x3_pt, y3_pt)) + + +class rect_pt(path): + + """rectangle at position (x_pt, y_pt) with width_pt and height_pt in pts""" + + def __init__(self, x_pt, y_pt, width_pt, height_pt): + path.__init__(self, moveto_pt(x_pt, y_pt), + lineto_pt(x_pt+width_pt, y_pt), + lineto_pt(x_pt+width_pt, y_pt+height_pt), + lineto_pt(x_pt, y_pt+height_pt), + closepath()) + + +class circle_pt(path): + + """circle with center (x_pt, y_pt) and radius_pt in pts""" + + def __init__(self, x_pt, y_pt, radius_pt, arcepsilon=0.1): + path.__init__(self, moveto_pt(x_pt+radius_pt, y_pt), + arc_pt(x_pt, y_pt, radius_pt, arcepsilon, 360-arcepsilon), + closepath()) + + +class ellipse_pt(path): + + """ellipse with center (x_pt, y_pt) in pts, + the two axes (a_pt, b_pt) in pts, + and the angle angle of the first axis""" + + def __init__(self, x_pt, y_pt, a_pt, b_pt, angle, **kwargs): + t = trafo.scale(a_pt, b_pt, epsilon=None).rotated(angle).translated_pt(x_pt, y_pt) + p = circle_pt(0, 0, 1, **kwargs).normpath(epsilon=None).transformed(t).path() + path.__init__(self, *p.pathitems) + + +class line(line_pt): + + """straight line from (x1, y1) to (x2, y2)""" + + def __init__(self, x1, y1, x2, y2): + line_pt.__init__(self, unit.topt(x1), unit.topt(y1), + unit.topt(x2), unit.topt(y2)) + + +class curve(curve_pt): + + """bezier curve with control points (x0, y1),..., (x3, y3)""" + + def __init__(self, x0, y0, x1, y1, x2, y2, x3, y3): + curve_pt.__init__(self, unit.topt(x0), unit.topt(y0), + unit.topt(x1), unit.topt(y1), + unit.topt(x2), unit.topt(y2), + unit.topt(x3), unit.topt(y3)) + + +class rect(rect_pt): + + """rectangle at position (x,y) with width and height""" + + def __init__(self, x, y, width, height): + rect_pt.__init__(self, unit.topt(x), unit.topt(y), + unit.topt(width), unit.topt(height)) + + +class circle(circle_pt): + + """circle with center (x,y) and radius""" + + def __init__(self, x, y, radius, **kwargs): + circle_pt.__init__(self, unit.topt(x), unit.topt(y), unit.topt(radius), **kwargs) + + +class ellipse(ellipse_pt): + + """ellipse with center (x, y), the two axes (a, b), + and the angle angle of the first axis""" + + def __init__(self, x, y, a, b, angle, **kwargs): + ellipse_pt.__init__(self, unit.topt(x), unit.topt(y), unit.topt(a), unit.topt(b), angle, **kwargs) diff --git a/compiler/gdsMill/pyx/pattern.py b/compiler/gdsMill/pyx/pattern.py new file mode 100644 index 00000000..a2b0cc1a --- /dev/null +++ b/compiler/gdsMill/pyx/pattern.py @@ -0,0 +1,328 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import nested_scopes + +import cStringIO, math, warnings +import attr, canvas, path, pdfwriter, pswriter, style, unit, trafo +import bbox as bboxmodule + +class _marker: pass + +# TODO: pattern should not derive from canvas but wrap a canvas + +class pattern(canvas._canvas, attr.exclusiveattr, style.fillstyle): + + def __init__(self, painttype=1, tilingtype=1, xstep=None, ystep=None, bbox=None, trafo=None, **kwargs): + canvas._canvas.__init__(self, **kwargs) + attr.exclusiveattr.__init__(self, pattern) + self.id = "pattern%d" % id(self) + self.patterntype = 1 + if painttype not in (1,2): + raise ValueError("painttype must be 1 or 2") + self.painttype = painttype + if tilingtype not in (1,2,3): + raise ValueError("tilingtype must be 1, 2 or 3") + self.tilingtype = tilingtype + self.xstep = xstep + self.ystep = ystep + self.patternbbox = bbox + self.patterntrafo = trafo + + def __call__(self, painttype=_marker, tilingtype=_marker, xstep=_marker, ystep=_marker, + bbox=_marker, trafo=_marker): + if painttype is _marker: + painttype = self.painttype + if tilingtype is _marker: + tilingtype = self.tilingtype + if xstep is _marker: + xstep = self.xstep + if ystep is _marker: + ystep = self.ystep + if bbox is _marker: + bbox = self.bbox + if trafo is _marker: + trafo = self.trafo + return pattern(painttype, tilingtype, xstep, ystep, bbox, trafo) + + def bbox(self): + return bboxmodule.empty() + + def processPS(self, file, writer, context, registry, bbox): + # process pattern, letting it register its resources and calculate the bbox of the pattern + patternfile = cStringIO.StringIO() + realpatternbbox = bboxmodule.empty() + canvas._canvas.processPS(self, patternfile, writer, pswriter.context(), registry, realpatternbbox) + patternproc = patternfile.getvalue() + patternfile.close() + + if self.xstep is None: + xstep = unit.topt(realpatternbbox.width()) + else: + xstep = unit.topt(self.xstep) + if self.ystep is None: + ystep = unit.topt(realpatternbbox.height()) + else: + ystep = unit.topt(self.ystep) + if not xstep: + raise ValueError("xstep in pattern cannot be zero") + if not ystep: + raise ValueError("ystep in pattern cannot be zero") + patternbbox = self.patternbbox or realpatternbbox.enlarged(5*unit.pt) + + patternprefix = "\n".join(("<<", + "/PatternType %d" % self.patterntype, + "/PaintType %d" % self.painttype, + "/TilingType %d" % self.tilingtype, + "/BBox[%d %d %d %d]" % patternbbox.lowrestuple_pt(), + "/XStep %g" % xstep, + "/YStep %g" % ystep, + "/PaintProc {\nbegin\n")) + patterntrafostring = self.patterntrafo is None and "matrix" or str(self.patterntrafo) + patternsuffix = "end\n} bind\n>>\n%s\nmakepattern" % patterntrafostring + + registry.add(pswriter.PSdefinition(self.id, "".join((patternprefix, patternproc, patternsuffix)))) + + # activate pattern + file.write("%s setpattern\n" % self.id) + + def processPDF(self, file, writer, context, registry, bbox): + # we need to keep track of the resources used by the pattern, hence + # we create our own registry, which we merge immediately in the main registry + patternregistry = pdfwriter.PDFregistry() + + patternfile = cStringIO.StringIO() + realpatternbbox = bboxmodule.empty() + canvas._canvas.processPDF(self, patternfile, writer, pdfwriter.context(), patternregistry, realpatternbbox) + patternproc = patternfile.getvalue() + patternfile.close() + + registry.mergeregistry(patternregistry) + + if self.xstep is None: + xstep = unit.topt(realpatternbbox.width()) + else: + xstep = unit.topt(self.xstep) + if self.ystep is None: + ystep = unit.topt(realpatternbbox.height()) + else: + ystep = unit.topt(self.ystep) + if not xstep: + raise ValueError("xstep in pattern cannot be zero") + if not ystep: + raise ValueError("ystep in pattern cannot be zero") + patternbbox = self.patternbbox or realpatternbbox.enlarged(5*unit.pt) + patterntrafo = self.patterntrafo or trafo.trafo() + + registry.add(PDFpattern(self.id, self.patterntype, self.painttype, self.tilingtype, + patternbbox, xstep, ystep, patterntrafo, patternproc, writer, registry, patternregistry)) + + # activate pattern + if context.colorspace != "Pattern": + # we only set the fill color space (see next comment) + file.write("/Pattern cs\n") + context.colorspace = "Pattern" + if context.strokeattr: + # using patterns as stroke colors doesn't seem to work, so + # we just don't do this... + warnings.warn("ignoring stroke color for patterns in PDF") + if context.fillattr: + file.write("/%s scn\n"% self.id) + + +pattern.clear = attr.clearclass(pattern) + + +_base = 0.1 * unit.v_cm + +class hatched(pattern): + def __init__(self, dist, angle, strokestyles=[]): + pattern.__init__(self, painttype=1, tilingtype=1, xstep=dist, ystep=100*unit.t_pt, bbox=None, trafo=trafo.rotate(angle)) + self.strokestyles = attr.mergeattrs([style.linewidth.THIN] + strokestyles) + attr.checkattrs(self.strokestyles, [style.strokestyle]) + self.dist = dist + self.angle = angle + self.stroke(path.line_pt(0, -50, 0, 50), self.strokestyles) + + def __call__(self, dist=None, angle=None, strokestyles=None): + if dist is None: + dist = self.dist + if angle is None: + angle = self.angle + if strokestyles is None: + strokestyles = self.strokestyles + return hatched(dist, angle, strokestyles) + +hatched0 = hatched(_base, 0) +hatched0.SMALL = hatched0(_base/math.sqrt(64)) +hatched0.SMALL = hatched0(_base/math.sqrt(64)) +hatched0.SMALl = hatched0(_base/math.sqrt(32)) +hatched0.SMAll = hatched0(_base/math.sqrt(16)) +hatched0.SMall = hatched0(_base/math.sqrt(8)) +hatched0.Small = hatched0(_base/math.sqrt(4)) +hatched0.small = hatched0(_base/math.sqrt(2)) +hatched0.normal = hatched0(_base) +hatched0.large = hatched0(_base*math.sqrt(2)) +hatched0.Large = hatched0(_base*math.sqrt(4)) +hatched0.LArge = hatched0(_base*math.sqrt(8)) +hatched0.LARge = hatched0(_base*math.sqrt(16)) +hatched0.LARGe = hatched0(_base*math.sqrt(32)) +hatched0.LARGE = hatched0(_base*math.sqrt(64)) + +hatched45 = hatched(_base, 45) +hatched45.SMALL = hatched45(_base/math.sqrt(64)) +hatched45.SMALl = hatched45(_base/math.sqrt(32)) +hatched45.SMAll = hatched45(_base/math.sqrt(16)) +hatched45.SMall = hatched45(_base/math.sqrt(8)) +hatched45.Small = hatched45(_base/math.sqrt(4)) +hatched45.small = hatched45(_base/math.sqrt(2)) +hatched45.normal = hatched45(_base) +hatched45.large = hatched45(_base*math.sqrt(2)) +hatched45.Large = hatched45(_base*math.sqrt(4)) +hatched45.LArge = hatched45(_base*math.sqrt(8)) +hatched45.LARge = hatched45(_base*math.sqrt(16)) +hatched45.LARGe = hatched45(_base*math.sqrt(32)) +hatched45.LARGE = hatched45(_base*math.sqrt(64)) + +hatched90 = hatched(_base, 90) +hatched90.SMALL = hatched90(_base/math.sqrt(64)) +hatched90.SMALl = hatched90(_base/math.sqrt(32)) +hatched90.SMAll = hatched90(_base/math.sqrt(16)) +hatched90.SMall = hatched90(_base/math.sqrt(8)) +hatched90.Small = hatched90(_base/math.sqrt(4)) +hatched90.small = hatched90(_base/math.sqrt(2)) +hatched90.normal = hatched90(_base) +hatched90.large = hatched90(_base*math.sqrt(2)) +hatched90.Large = hatched90(_base*math.sqrt(4)) +hatched90.LArge = hatched90(_base*math.sqrt(8)) +hatched90.LARge = hatched90(_base*math.sqrt(16)) +hatched90.LARGe = hatched90(_base*math.sqrt(32)) +hatched90.LARGE = hatched90(_base*math.sqrt(64)) + +hatched135 = hatched(_base, 135) +hatched135.SMALL = hatched135(_base/math.sqrt(64)) +hatched135.SMALl = hatched135(_base/math.sqrt(32)) +hatched135.SMAll = hatched135(_base/math.sqrt(16)) +hatched135.SMall = hatched135(_base/math.sqrt(8)) +hatched135.Small = hatched135(_base/math.sqrt(4)) +hatched135.small = hatched135(_base/math.sqrt(2)) +hatched135.normal = hatched135(_base) +hatched135.large = hatched135(_base*math.sqrt(2)) +hatched135.Large = hatched135(_base*math.sqrt(4)) +hatched135.LArge = hatched135(_base*math.sqrt(8)) +hatched135.LARge = hatched135(_base*math.sqrt(16)) +hatched135.LARGe = hatched135(_base*math.sqrt(32)) +hatched135.LARGE = hatched135(_base*math.sqrt(64)) + + +class crosshatched(pattern): + def __init__(self, dist, angle, strokestyles=[]): + pattern.__init__(self, painttype=1, tilingtype=1, xstep=dist, ystep=dist, bbox=None, trafo=trafo.rotate(angle)) + self.strokestyles = attr.mergeattrs([style.linewidth.THIN] + strokestyles) + attr.checkattrs(self.strokestyles, [style.strokestyle]) + self.dist = dist + self.angle = angle + self.stroke(path.line_pt(0, 0, 0, unit.topt(dist)), self.strokestyles) + self.stroke(path.line_pt(0, 0, unit.topt(dist), 0), self.strokestyles) + + def __call__(self, dist=None, angle=None, strokestyles=None): + if dist is None: + dist = self.dist + if angle is None: + angle = self.angle + if strokestyles is None: + strokestyles = self.strokestyles + return crosshatched(dist, angle, strokestyles) + +crosshatched0 = crosshatched(_base, 0) +crosshatched0.SMALL = crosshatched0(_base/math.sqrt(64)) +crosshatched0.SMALl = crosshatched0(_base/math.sqrt(32)) +crosshatched0.SMAll = crosshatched0(_base/math.sqrt(16)) +crosshatched0.SMall = crosshatched0(_base/math.sqrt(8)) +crosshatched0.Small = crosshatched0(_base/math.sqrt(4)) +crosshatched0.small = crosshatched0(_base/math.sqrt(2)) +crosshatched0.normal = crosshatched0 +crosshatched0.large = crosshatched0(_base*math.sqrt(2)) +crosshatched0.Large = crosshatched0(_base*math.sqrt(4)) +crosshatched0.LArge = crosshatched0(_base*math.sqrt(8)) +crosshatched0.LARge = crosshatched0(_base*math.sqrt(16)) +crosshatched0.LARGe = crosshatched0(_base*math.sqrt(32)) +crosshatched0.LARGE = crosshatched0(_base*math.sqrt(64)) + +crosshatched45 = crosshatched(_base, 45) +crosshatched45.SMALL = crosshatched45(_base/math.sqrt(64)) +crosshatched45.SMALl = crosshatched45(_base/math.sqrt(32)) +crosshatched45.SMAll = crosshatched45(_base/math.sqrt(16)) +crosshatched45.SMall = crosshatched45(_base/math.sqrt(8)) +crosshatched45.Small = crosshatched45(_base/math.sqrt(4)) +crosshatched45.small = crosshatched45(_base/math.sqrt(2)) +crosshatched45.normal = crosshatched45 +crosshatched45.large = crosshatched45(_base*math.sqrt(2)) +crosshatched45.Large = crosshatched45(_base*math.sqrt(4)) +crosshatched45.LArge = crosshatched45(_base*math.sqrt(8)) +crosshatched45.LARge = crosshatched45(_base*math.sqrt(16)) +crosshatched45.LARGe = crosshatched45(_base*math.sqrt(32)) +crosshatched45.LARGE = crosshatched45(_base*math.sqrt(64)) + + +class PDFpattern(pdfwriter.PDFobject): + + def __init__(self, name, patterntype, painttype, tilingtype, bbox, xstep, ystep, trafo, + patternproc, writer, registry, patternregistry): + self.patternregistry = patternregistry + pdfwriter.PDFobject.__init__(self, "pattern", name) + registry.addresource("Pattern", name, self) + + self.name = name + self.patterntype = patterntype + self.painttype = painttype + self.tilingtype = tilingtype + self.bbox = bbox + self.xstep = xstep + self.ystep = ystep + self.trafo = trafo + self.patternproc = patternproc + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /Pattern\n" + "/PatternType %d\n" % self.patterntype) + file.write("/PaintType %d\n" % self.painttype) + file.write("/TilingType %d\n" % self.tilingtype) + file.write("/BBox [%d %d %d %d]\n" % self.bbox.lowrestuple_pt()) + file.write("/XStep %f\n" % self.xstep) + file.write("/YStep %f\n" % self.ystep) + file.write("/Matrix %s\n" % str(self.trafo)) + self.patternregistry.writeresources(file) + if writer.compress: + import zlib + content = zlib.compress(self.patternproc) + else: + content = self.patternproc + + file.write("/Length %i\n" % len(content)) + if writer.compress: + file.write("/Filter /FlateDecode\n") + file.write(">>\n" + "stream\n") + file.write(content) + file.write("endstream\n") diff --git a/compiler/gdsMill/pyx/pdfwriter.py b/compiler/gdsMill/pyx/pdfwriter.py new file mode 100644 index 00000000..25ac3f7f --- /dev/null +++ b/compiler/gdsMill/pyx/pdfwriter.py @@ -0,0 +1,498 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 Jörg Lehmann +# Copyright (C) 2005-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import cStringIO, copy, warnings, time +try: + import zlib + haszlib = 1 +except: + haszlib = 0 + +import bbox, unit, style, type1font, version + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + +try: + dict([]) +except NameError: + # fallback implementation for Python 2.1 + def dict(list): + result = {} + for key, value in list: + result[key] = value + return result + + +class PDFregistry: + + def __init__(self): + self.types = {} + # we want to keep the order of the resources + self.objects = [] + self.resources = {} + self.procsets = {"PDF": 1} + self.merged = None + + def add(self, object): + """ register object, merging it with an already registered object of the same type and id """ + sameobjects = self.types.setdefault(object.type, {}) + if sameobjects.has_key(object.id): + sameobjects[object.id].merge(object) + else: + self.objects.append(object) + sameobjects[object.id] = object + + def getrefno(self, object): + if self.merged: + return self.merged.getrefno(object) + else: + return self.types[object.type][object.id].refno + + def mergeregistry(self, registry): + for object in registry.objects: + self.add(object) + registry.merged = self + + def write(self, file, writer, catalog): + # first we set all refnos + refno = 1 + for object in self.objects: + object.refno = refno + refno += 1 + + # second, all objects are written, keeping the positions in the output file + fileposes = [] + for object in self.objects: + fileposes.append(file.tell()) + file.write("%i 0 obj\n" % object.refno) + object.write(file, writer, self) + file.write("endobj\n") + + # xref + xrefpos = file.tell() + file.write("xref\n" + "0 %d\n" + "0000000000 65535 f \n" % refno) + + for filepos in fileposes: + file.write("%010i 00000 n \n" % filepos) + + # trailer + file.write("trailer\n" + "<<\n" + "/Size %i\n" % refno) + file.write("/Root %i 0 R\n" % self.getrefno(catalog)) + file.write("/Info %i 0 R\n" % self.getrefno(catalog.PDFinfo)) + file.write(">>\n" + "startxref\n" + "%i\n" % xrefpos) + file.write("%%EOF\n") + + def addresource(self, resourcetype, resourcename, object, procset=None): + self.resources.setdefault(resourcetype, {})[resourcename] = object + if procset: + self.procsets[procset] = 1 + + def writeresources(self, file): + file.write("/Resources <<\n") + file.write("/ProcSet [ %s ]\n" % " ".join(["/%s" % p for p in self.procsets.keys()])) + if self.resources: + for resourcetype, resources in self.resources.items(): + file.write("/%s <<\n%s\n>>\n" % (resourcetype, "\n".join(["/%s %i 0 R" % (name, self.getrefno(object)) + for name, object in resources.items()]))) + file.write(">>\n") + + +class PDFobject: + + def __init__(self, type, _id=None): + """create a PDFobject + - type has to be a string describing the type of the object + - _id is a unique identification used for the object if it is not None. + Otherwise id(self) is used + """ + self.type = type + if _id is None: + self.id = id(self) + else: + self.id = _id + + def merge(self, other): + pass + + def write(self, file, writer, registry): + raise NotImplementedError("write method has to be provided by PDFobject subclass") + + +class PDFcatalog(PDFobject): + + def __init__(self, document, writer, registry): + PDFobject.__init__(self, "catalog") + self.PDFpages = PDFpages(document, writer, registry) + registry.add(self.PDFpages) + self.PDFinfo = PDFinfo() + registry.add(self.PDFinfo) + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /Catalog\n" + "/Pages %i 0 R\n" % registry.getrefno(self.PDFpages)) + if writer.fullscreen: + file.write("/PageMode /FullScreen\n") + file.write(">>\n") + + +class PDFinfo(PDFobject): + + def __init__(self): + PDFobject.__init__(self, "info") + + def write(self, file, writer, registry): + if time.timezone < 0: + # divmod on positive numbers, otherwise the minutes have a different sign from the hours + timezone = "-%02i'%02i'" % divmod(-time.timezone/60, 60) + elif time.timezone > 0: + timezone = "+%02i'%02i'" % divmod(time.timezone/60, 60) + else: + timezone = "Z00'00'" + + def pdfstring(s): + r = "" + for c in s: + if 32 <= ord(c) <= 127 and c not in "()[]<>\\": + r += c + else: + r += "\\%03o" % ord(c) + return r + + file.write("<<\n") + if writer.title: + file.write("/Title (%s)\n" % pdfstring(writer.title)) + if writer.author: + file.write("/Author (%s)\n" % pdfstring(writer.author)) + if writer.subject: + file.write("/Subject (%s)\n" % pdfstring(writer.subject)) + if writer.keywords: + file.write("/Keywords (%s)\n" % pdfstring(writer.keywords)) + file.write("/Creator (PyX %s)\n" % version.version) + file.write("/CreationDate (D:%s%s)\n" % (time.strftime("%Y%m%d%H%M"), timezone)) + file.write(">>\n") + + +class PDFpages(PDFobject): + + def __init__(self, document, writer, registry): + PDFobject.__init__(self, "pages") + self.PDFpagelist = [] + for pageno, page in enumerate(document.pages): + page = PDFpage(page, pageno, self, writer, registry) + registry.add(page) + self.PDFpagelist.append(page) + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /Pages\n" + "/Kids [%s]\n" + "/Count %i\n" + ">>\n" % (" ".join(["%i 0 R" % registry.getrefno(page) + for page in self.PDFpagelist]), + len(self.PDFpagelist))) + + +class PDFpage(PDFobject): + + def __init__(self, page, pageno, PDFpages, writer, registry): + PDFobject.__init__(self, "page") + self.PDFpages = PDFpages + self.page = page + + # every page uses its own registry in order to find out which + # resources are used within the page. However, the + # pageregistry is also merged in the global registry + self.pageregistry = PDFregistry() + + self.PDFcontent = PDFcontent(page, writer, self.pageregistry) + self.pageregistry.add(self.PDFcontent) + registry.mergeregistry(self.pageregistry) + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /Page\n" + "/Parent %i 0 R\n" % registry.getrefno(self.PDFpages)) + paperformat = self.page.paperformat + if paperformat: + file.write("/MediaBox [0 0 %f %f]\n" % (unit.topt(paperformat.width), unit.topt(paperformat.height))) + else: + file.write("/MediaBox [%f %f %f %f]\n" % self.PDFcontent.bbox.highrestuple_pt()) + if self.PDFcontent.bbox and writer.writebbox: + file.write("/CropBox [%f %f %f %f]\n" % self.PDFcontent.bbox.highrestuple_pt()) + if self.page.rotated: + file.write("/Rotate 90\n") + file.write("/Contents %i 0 R\n" % registry.getrefno(self.PDFcontent)) + self.pageregistry.writeresources(file) + file.write(">>\n") + + +class PDFcontent(PDFobject): + + def __init__(self, page, writer, registry): + PDFobject.__init__(self, registry, "content") + contentfile = cStringIO.StringIO() + self.bbox = bbox.empty() + acontext = context() + page.processPDF(contentfile, writer, acontext, registry, self.bbox) + self.content = contentfile.getvalue() + contentfile.close() + + def write(self, file, writer, registry): + if writer.compress: + content = zlib.compress(self.content) + else: + content = self.content + file.write("<<\n" + "/Length %i\n" % len(content)) + if writer.compress: + file.write("/Filter /FlateDecode\n") + file.write(">>\n" + "stream\n") + file.write(content) + file.write("endstream\n") + + +class PDFfont(PDFobject): + + def __init__(self, font, chars, writer, registry): + PDFobject.__init__(self, "font", font.name) + registry.addresource("Font", font.name, self, procset="Text") + + self.fontdescriptor = PDFfontdescriptor(font, chars, writer, registry) + registry.add(self.fontdescriptor) + + if font.encoding: + self.encoding = PDFencoding(font.encoding, writer, registry) + registry.add(self.encoding) + else: + self.encoding = None + + self.name = font.name + self.basefontname = font.basefontname + self.metric = font.metric + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /Font\n" + "/Subtype /Type1\n") + file.write("/Name /%s\n" % self.name) + file.write("/BaseFont /%s\n" % self.basefontname) + if self.fontdescriptor.fontfile is not None and self.fontdescriptor.fontfile.usedchars is not None: + usedchars = self.fontdescriptor.fontfile.usedchars + firstchar = min(usedchars.keys()) + lastchar = max(usedchars.keys()) + file.write("/FirstChar %d\n" % firstchar) + file.write("/LastChar %d\n" % lastchar) + file.write("/Widths\n" + "[") + for i in range(firstchar, lastchar+1): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + if usedchars.has_key(i): + file.write("%f" % self.metric.getwidth_ds(i)) + else: + file.write("0") + file.write(" ]\n") + else: + file.write("/FirstChar 0\n" + "/LastChar 255\n" + "/Widths\n" + "[") + for i in range(256): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + try: + width = self.metric.getwidth_ds(i) + except (IndexError, AttributeError): + width = 0 + file.write("%f" % width) + file.write(" ]\n") + file.write("/FontDescriptor %d 0 R\n" % registry.getrefno(self.fontdescriptor)) + if self.encoding: + file.write("/Encoding %d 0 R\n" % registry.getrefno(self.encoding)) + file.write(">>\n") + + +class PDFfontdescriptor(PDFobject): + + def __init__(self, font, chars, writer, registry): + PDFobject.__init__(self, "fontdescriptor", font.basefontname) + + if font.filename is None: + self.fontfile = None + else: + self.fontfile = PDFfontfile(font.basefontname, font.filename, font.encoding, chars, writer, registry) + registry.add(self.fontfile) + + self.name = font.basefontname + self.fontinfo = font.metric.fontinfo() + + def write(self, file, writer, registry): + file.write("<<\n" + "/Type /FontDescriptor\n" + "/FontName /%s\n" % self.name) + if self.fontfile is None: + file.write("/Flags 32\n") + else: + file.write("/Flags %d\n" % self.fontfile.getflags()) + file.write("/FontBBox [%d %d %d %d]\n" % self.fontinfo.fontbbox) + file.write("/ItalicAngle %d\n" % self.fontinfo.italicangle) + file.write("/Ascent %d\n" % self.fontinfo.ascent) + file.write("/Descent %d\n" % self.fontinfo.descent) + file.write("/CapHeight %d\n" % self.fontinfo.capheight) + file.write("/StemV %d\n" % self.fontinfo.vstem) + if self.fontfile is not None: + file.write("/FontFile %d 0 R\n" % registry.getrefno(self.fontfile)) + file.write(">>\n") + + +class PDFfontfile(PDFobject): + + def __init__(self, name, filename, encoding, chars, writer, registry): + PDFobject.__init__(self, "fontfile", filename) + self.name = name + self.filename = filename + if encoding is None: + self.encodingfilename = None + else: + self.encodingfilename = encoding.filename + self.usedchars = {} + for char in chars: + self.usedchars[char] = 1 + + self.strip = 1 + self.font = None + + def merge(self, other): + if self.encodingfilename == other.encodingfilename: + self.usedchars.update(other.usedchars) + else: + # TODO: need to resolve the encoding when several encodings are in the play + self.strip = 0 + + def mkfontfile(self): + import font.t1font + self.font = font.t1font.T1pfbfont(self.filename) + + def getflags(self): + if self.font is None: + self.mkfontfile() + return self.font.getflags() + + def write(self, file, writer, registry): + if self.font is None: + self.mkfontfile() + if self.strip: + # XXX: access to the encoding file + if self.encodingfilename: + encodingfile = type1font.encodingfile(self.encodingfilename, self.encodingfilename) + usedglyphs = dict([(encodingfile.decode(char)[1:], 1) for char in self.usedchars.keys()]) + else: + self.font._encoding() + usedglyphs = dict([(self.font.encoding.decode(char), 1) for char in self.usedchars.keys()]) + strippedfont = self.font.getstrippedfont(usedglyphs) + else: + strippedfont = self.font + strippedfont.outputPDF(file, writer) + + +class PDFencoding(PDFobject): + + def __init__(self, encoding, writer, registry): + PDFobject.__init__(self, "encoding", encoding.name) + self.encoding = encoding + + def write(self, file, writer, registry): + encodingfile = type1font.encodingfile(self.encoding.name, self.encoding.filename) + encodingfile.outputPDF(file, writer) + + +class PDFwriter: + + def __init__(self, document, file, + title=None, author=None, subject=None, keywords=None, + fullscreen=0, writebbox=0, compress=1, compresslevel=6): + try: + file.write("") + except: + filename = file + if not filename.endswith(".pdf"): + filename += ".pdf" + try: + file = open(filename, "wb") + except IOError: + raise IOError("cannot open output file") + + self.title = title + self.author = author + self.subject = subject + self.keywords = keywords + self.fullscreen = fullscreen + self.writebbox = writebbox + if compress and not haszlib: + compress = 0 + warnings.warn("compression disabled due to missing zlib module") + self.compress = compress + self.compresslevel = compresslevel + + # the PDFcatalog class automatically builds up the pdfobjects from a document + registry = PDFregistry() + catalog = PDFcatalog(document, self, registry) + registry.add(catalog) + + file.write("%%PDF-1.4\n%%%s%s%s%s\n" % (chr(195), chr(182), chr(195), chr(169))) + registry.write(file, self, catalog) + file.close() + + +class context: + + def __init__(self): + self.linewidth_pt = None + # XXX there are both stroke and fill color spaces + self.colorspace = None + self.strokeattr = 1 + self.fillattr = 1 + self.font = None + self.textregion = 0 + + def __call__(self, **kwargs): + newcontext = copy.copy(self) + for key, value in kwargs.items(): + setattr(newcontext, key, value) + return newcontext diff --git a/compiler/gdsMill/pyx/pswriter.py b/compiler/gdsMill/pyx/pswriter.py new file mode 100644 index 00000000..e9f838ad --- /dev/null +++ b/compiler/gdsMill/pyx/pswriter.py @@ -0,0 +1,469 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 Jörg Lehmann +# Copyright (C) 2005-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import cStringIO, copy, time, math +import bbox, style, version, type1font, unit, trafo + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + +try: + dict([]) +except NameError: + # fallback implementation for Python 2.1 + def dict(list): + result = {} + for key, value in list: + result[key] = value + return result + + +class PSregistry: + + def __init__(self): + # in order to keep a consistent order of the registered resources we + # not only store them in a hash but also keep an ordered list (up to a + # possible merging of resources, in which case the first instance is + # kept) + self.resourceshash = {} + self.resourceslist = [] + + def add(self, resource): + rkey = (resource.type, resource.id) + if self.resourceshash.has_key(rkey): + self.resourceshash[rkey].merge(resource) + else: + self.resourceshash[rkey] = resource + self.resourceslist.append(resource) + + def mergeregistry(self, registry): + for resource in registry.resources: + self.add(resource) + + def output(self, file, writer): + """ write all PostScript code of the prolog resources """ + for resource in self.resourceslist: + resource.output(file, writer, self) + +# +# Abstract base class +# + +class PSresource: + + """ a PostScript resource """ + + def __init__(self, type, id): + # Every PSresource has to have a type and a unique id. + # Resources with the same type and id will be merged + # when they are registered in the PSregistry + self.type = type + self.id = id + + def merge(self, other): + """ merge self with other, which has to be a resource of the same type and with + the same id""" + pass + + def output(self, file, writer, registry): + raise NotImplementedError("output not implemented for %s" % repr(self)) + +# +# Different variants of prolog items +# + +class PSdefinition(PSresource): + + """ PostScript function definition included in the prolog """ + + def __init__(self, id, body): + self.type = "definition" + self.id = id + self.body = body + + def output(self, file, writer, registry): + file.write("%%%%BeginRessource: %s\n" % self.id) + file.write("%(body)s /%(id)s exch def\n" % self.__dict__) + file.write("%%EndRessource\n") + + +class PSfont: + + def __init__(self, font, chars, registry): + if font.filename: + registry.add(PSfontfile(font.basefontname, + font.filename, + font.encoding, + chars)) + if font.encoding and font.slant: + assert font.encname + # do first the reencoding and then the slanting: + enc_basename, enc_finalname = font.basefontname, font.encname + slt_basename, slt_finalname = tfont.encname, font.name + elif font.encoding: + enc_basename, enc_finalname = font.basefontname, font.name + elif font.slant: + slt_basename, slt_finalname = font.basefontname, font.name + + if font.encoding: + registry.add(_ReEncodeFont) + registry.add(PSfontencoding(font.encoding)) + registry.add(PSfontreencoding(enc_finalname, enc_basename, font.encoding.name)) + + if font.slant: + # we need the current fontmatrix in order to manipulate it: + # for this we need to re-read the fontfile as below in + # PSfontfile.ouput: + # XXX Is there a better way to do this? + t = trafo.trafo_pt(matrix=((1, font.slant), (0, 1))) + if font.filename: + # for the builtin fonts, we assume a trivial fontmatrix + import font.t1font as t1fontmodule + t1font = t1fontmodule.T1pfbfont(font.filename) + m = t1font.fontmatrixpattern.search(t1font.data1) + m11, m12, m21, m22, v1, v2 = map(float, m.groups()[:6]) + t *= trafo.trafo_pt(matrix=((m11, m12), (m21, m22)), vector=(v1, v2)) + else: + raise NotImplementedError( + "cannot slant unembedded fonts -- try to include \"download35.map\" in fontmaps") + registry.add(PSfontslanting(slt_finalname, slt_basename, t.__str__())) + + +class PSfontfile(PSresource): + + """ PostScript font definition included in the prolog """ + + def __init__(self, name, filename, encoding, chars): + """ include type 1 font defined by the following parameters + + - name: name of the PostScript font + - filename: name (without path) of file containing the font definition + - encfilename: name (without path) of file containing used encoding of font + or None (if no encoding file used) + - chars: character list to fill usedchars + + """ + + # Note that here we only need the encoding for selecting the used glyphs! + + self.type = "fontfile" + self.id = self.name = name + self.filename = filename + if encoding is None: + self.encodingfilename = None + else: + self.encodingfilename = encoding.filename + self.usedchars = {} + for char in chars: + self.usedchars[char] = 1 + + self.strip = 1 + + def merge(self, other): + if self.encodingfilename == other.encodingfilename: + self.usedchars.update(other.usedchars) + else: + # TODO: need to resolve the encoding when several encodings are in the play + self.strip = 0 + + def output(self, file, writer, registry): + import font.t1font + font = font.t1font.T1pfbfont(self.filename) + + file.write("%%%%BeginFont: %s\n" % self.name) + # file.write("%%Included glyphs: %s\n" % " ".join(usedglyphs)) + if self.strip: + # XXX: access to the encoding file + if self.encodingfilename: + encodingfile = type1font.encodingfile(self.encodingfilename, self.encodingfilename) + usedglyphs = dict([(encodingfile.decode(char)[1:], 1) for char in self.usedchars.keys()]) + else: + font._encoding() + usedglyphs = dict([(font.encoding.decode(char), 1) for char in self.usedchars.keys()]) + strippedfont = font.getstrippedfont(usedglyphs) + else: + strippedfont = font + strippedfont.outputPS(file, writer) + file.write("\n%%EndFont\n") + + +class PSfontencoding(PSresource): + + """ PostScript font encoding vector included in the prolog """ + + def __init__(self, encoding): + """ include font encoding vector specified by encoding """ + + self.type = "fontencoding" + self.id = encoding.name + self.encoding = encoding + + def output(self, file, writer, registry): + encodingfile = type1font.encodingfile(self.encoding.name, self.encoding.filename) + encodingfile.outputPS(file, writer) + + +class PSfontslanting(PSresource): + + """ PostScript font slanting directive included in the prolog """ + + def __init__(self, fontname, basefontname, matrixstring): + """ include transformed font directive specified by + + - fontname: PostScript FontName of the new slanted font + - basefontname: PostScript FontName of the original font + - slant: the value of slanting + """ + + self.type = "fontslanting" + self.id = self.fontname = fontname + self.basefontname = basefontname + self.matrixstring = matrixstring + + def output(self, file, writer, registry): + file.write("%%%%BeginProcSet: %s\n" % self.fontname) + file.write("/%s findfont\n" % self.basefontname) + file.write("dup length dict begin\n") + file.write("{ 1 index /FID ne {def} {pop pop} ifelse } forall\n") + file.write("/FontMatrix %s readonly def\n" % self.matrixstring) + file.write("currentdict\n") + file.write("end\n") + file.write("/%s exch definefont pop\n" % self.fontname) + file.write("%%EndProcSet\n") + +class PSfontreencoding(PSresource): + + """ PostScript font re-encoding directive included in the prolog """ + + def __init__(self, fontname, basefontname, encodingname): + """ include font re-encoding directive specified by + + - fontname: PostScript FontName of the new reencoded font + - basefontname: PostScript FontName of the original font + - encname: name of the encoding + + Before being able to reencode a font, you have to include the + encoding via a fontencoding prolog item with name=encname + + """ + + self.type = "fontreencoding" + self.id = self.fontname = fontname + self.basefontname = basefontname + self.encodingname = encodingname + + def output(self, file, writer, registry): + file.write("%%%%BeginProcSet: %s\n" % self.fontname) + file.write("/%s /%s %s ReEncodeFont\n" % (self.basefontname, self.fontname, self.encodingname)) + file.write("%%EndProcSet\n") + + +_ReEncodeFont = PSdefinition("ReEncodeFont", """{ + 5 dict + begin + /newencoding exch def + /newfontname exch def + /basefontname exch def + /basefontdict basefontname findfont def + /newfontdict basefontdict maxlength dict def + basefontdict { + exch dup dup /FID ne exch /Encoding ne and + { exch newfontdict 3 1 roll put } + { pop pop } + ifelse + } forall + newfontdict /FontName newfontname put + newfontdict /Encoding newencoding put + newfontname newfontdict definefont pop + end +}""") + + +class epswriter: + + def __init__(self, document, file): + if len(document.pages) != 1: + raise ValueError("EPS file can be constructed out of a single page document only") + page = document.pages[0] + canvas = page.canvas + + try: + file.write("") + except: + filename = file + if not filename.endswith(".eps"): + filename += ".eps" + try: + file = open(filename, "w") + except IOError: + raise IOError("cannot open output file") + else: + filename = "stream" + + pagefile = cStringIO.StringIO() + registry = PSregistry() + acontext = context() + pagebbox = bbox.empty() + + page.processPS(pagefile, self, acontext, registry, pagebbox) + + file.write("%!PS-Adobe-3.0 EPSF-3.0\n") + if pagebbox: + file.write("%%%%BoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt()) + file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % pagebbox.highrestuple_pt()) + file.write("%%%%Creator: PyX %s\n" % version.version) + file.write("%%%%Title: %s\n" % filename) + file.write("%%%%CreationDate: %s\n" % + time.asctime(time.localtime(time.time()))) + file.write("%%EndComments\n") + + file.write("%%BeginProlog\n") + registry.output(file, self) + file.write("%%EndProlog\n") + + file.write(pagefile.getvalue()) + pagefile.close() + + file.write("showpage\n") + file.write("%%Trailer\n") + file.write("%%EOF\n") + + +class pswriter: + + def __init__(self, document, file, writebbox=0): + try: + file.write("") + except: + filename = file + if not filename.endswith(".ps"): + filename += ".ps" + try: + file = open(filename, "w") + except IOError: + raise IOError("cannot open output file") + else: + filename = "stream" + + # We first have to process the content of the pages, writing them into the stream pagesfile + # Doing so, we fill the registry and also calculate the page bounding boxes, which are + # stored in page._bbox for every page + pagesfile = cStringIO.StringIO() + registry = PSregistry() + + # calculated bounding boxes of the whole document + documentbbox = bbox.empty() + + for nr, page in enumerate(document.pages): + # process contents of page + pagefile = cStringIO.StringIO() + acontext = context() + pagebbox = bbox.empty() + page.processPS(pagefile, self, acontext, registry, pagebbox) + + documentbbox += pagebbox + + pagesfile.write("%%%%Page: %s %d\n" % (page.pagename is None and str(nr+1) or page.pagename, nr+1)) + if page.paperformat: + pagesfile.write("%%%%PageMedia: %s\n" % page.paperformat.name) + pagesfile.write("%%%%PageOrientation: %s\n" % (page.rotated and "Landscape" or "Portrait")) + if pagebbox and writebbox: + pagesfile.write("%%%%PageBoundingBox: %d %d %d %d\n" % pagebbox.lowrestuple_pt()) + + # page setup section + pagesfile.write("%%BeginPageSetup\n") + pagesfile.write("/pgsave save def\n") + + pagesfile.write("%%EndPageSetup\n") + pagesfile.write(pagefile.getvalue()) + pagefile.close() + pagesfile.write("pgsave restore\n") + pagesfile.write("showpage\n") + pagesfile.write("%%PageTrailer\n") + + file.write("%!PS-Adobe-3.0\n") + if documentbbox and writebbox: + file.write("%%%%BoundingBox: %d %d %d %d\n" % documentbbox.lowrestuple_pt()) + file.write("%%%%HiResBoundingBox: %g %g %g %g\n" % documentbbox.highrestuple_pt()) + file.write("%%%%Creator: PyX %s\n" % version.version) + file.write("%%%%Title: %s\n" % filename) + file.write("%%%%CreationDate: %s\n" % + time.asctime(time.localtime(time.time()))) + + # required paper formats + paperformats = {} + for page in document.pages: + if page.paperformat: + paperformats[page.paperformat] = page.paperformat + + first = 1 + for paperformat in paperformats.values(): + if first: + file.write("%%DocumentMedia: ") + first = 0 + else: + file.write("%%+ ") + file.write("%s %d %d 75 white ()\n" % (paperformat.name, + unit.topt(paperformat.width), + unit.topt(paperformat.height))) + + # file.write(%%DocumentNeededResources: ") # register not downloaded fonts here + + file.write("%%%%Pages: %d\n" % len(document.pages)) + file.write("%%PageOrder: Ascend\n") + file.write("%%EndComments\n") + + # document defaults section + #file.write("%%BeginDefaults\n") + #file.write("%%EndDefaults\n") + + # document prolog section + file.write("%%BeginProlog\n") + registry.output(file, self) + file.write("%%EndProlog\n") + + # document setup section + #file.write("%%BeginSetup\n") + #file.write("%%EndSetup\n") + + file.write(pagesfile.getvalue()) + pagesfile.close() + + file.write("%%Trailer\n") + file.write("%%EOF\n") + +class context: + + def __init__(self): + self.linewidth_pt = None + self.colorspace = None + self.font = None + + def __call__(self, **kwargs): + newcontext = copy.copy(self) + for key, value in kwargs.items(): + setattr(newcontext, key, value) + return newcontext diff --git a/compiler/gdsMill/pyx/pykpathsea/__init__.py b/compiler/gdsMill/pyx/pykpathsea/__init__.py new file mode 100644 index 00000000..6a9c0eee --- /dev/null +++ b/compiler/gdsMill/pyx/pykpathsea/__init__.py @@ -0,0 +1,77 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2003-2004 Jörg Lehmann +# Copyright (C) 2003-2004 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +try: + from _pykpathsea import * +except: + import os + find_file_cache = {} + def find_file(filename, kpse_file_format): + command = 'kpsewhich --format="%s" %s' % (kpse_file_format, filename) + if not find_file_cache.has_key(command): + find_file_cache[command] = os.popen(command, "r").readline().strip() + return find_file_cache[command] + kpse_gf_format = "gf" + kpse_pk_format = "pk" + kpse_any_glyph_format = "bitmap font" + kpse_tfm_format = "tfm" + kpse_afm_format = "afm" + kpse_base_format = "base" + kpse_bib_format = "bib" + kpse_bst_format = "bst" + kpse_cnf_format = "cnf" + kpse_db_format = "ls-R" + kpse_fmt_format = "fmt" + kpse_fontmap_format = "map" + kpse_mem_format = "mem" + kpse_mf_format = "mf" + kpse_mfpool_format = "mfpool" + kpse_mft_format = "mft" + kpse_mp_format = "mp" + kpse_mppool_format = "mppool" + kpse_mpsupport_format = "MetaPost support" + kpse_ocp_format = "ocp" + kpse_ofm_format = "ofm" + kpse_opl_format = "opl" + kpse_otp_format = "otp" + kpse_ovf_format = "ovf" + kpse_ovp_format = "ovp" + kpse_pict_format = "graphics/figure" + kpse_tex_format = "tex" + kpse_texdoc_format = "TeX system documentation" + kpse_texpool_format = "texpool" + kpse_texsource_format = "TeX system sources" + kpse_tex_ps_header_format = "PostScript header" + kpse_troff_font_format = "Troff fonts" + kpse_type1_format = "type1 fonts" + kpse_vf_format = "vf" + kpse_dvips_config_format = "dvips config" + kpse_ist_format = "ist" + kpse_truetype_format = "truetype fonts" + kpse_type42_format = "type42 fonts" + kpse_web2c_format = "web2c" + kpse_program_text_format = "other text files" + kpse_program_binary_format = "other binary files" + kpse_miscfonts_format = "misc fonts" + kpse_web_format = "web" + kpse_cweb_format = "cweb" diff --git a/compiler/gdsMill/pyx/pykpathsea/pykpathsea.c b/compiler/gdsMill/pyx/pykpathsea/pykpathsea.c new file mode 100644 index 00000000..02c8a493 --- /dev/null +++ b/compiler/gdsMill/pyx/pykpathsea/pykpathsea.c @@ -0,0 +1,104 @@ +/* pykpathsea.c: Copyright 2003 Jörg Lehmann, André Wobst + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include +#include +#include + +static PyObject *py_kpse_find_file(PyObject *self, PyObject *args) +{ + char *filename; + int kpse_file_format; + char *completefilename; + PyObject *returnvalue; + + if (PyArg_ParseTuple(args, "si", &filename, &kpse_file_format)) { + completefilename = kpse_find_file(filename, (kpse_file_format_type) kpse_file_format, 1); + + returnvalue = Py_BuildValue("s", completefilename); + /* XXX: free(completefilename); */ + return returnvalue; + } + + return NULL; + +} + +/* exported methods */ + +static PyMethodDef pykpathsea_methods[] = { + {"find_file", py_kpse_find_file, METH_VARARGS}, + {NULL, NULL} +}; + +#define AddInt(x) PyDict_SetItemString(dict, #x, PyInt_FromLong(x)) + +void init_pykpathsea(void) +{ + PyObject *module = Py_InitModule("_pykpathsea", pykpathsea_methods); + PyObject *dict = PyModule_GetDict(module); + kpse_set_program_name("dvips", "dvips"); + + AddInt(kpse_gf_format); + AddInt(kpse_pk_format); + AddInt(kpse_any_glyph_format); + AddInt(kpse_tfm_format); + AddInt(kpse_afm_format); + AddInt(kpse_base_format); + AddInt(kpse_bib_format); + AddInt(kpse_bst_format); + AddInt(kpse_cnf_format); + AddInt(kpse_db_format); + AddInt(kpse_fmt_format); + AddInt(kpse_fontmap_format); + AddInt(kpse_mem_format); + AddInt(kpse_mf_format); + AddInt(kpse_mfpool_format); + AddInt(kpse_mft_format); + AddInt(kpse_mp_format); + AddInt(kpse_mppool_format); + AddInt(kpse_mpsupport_format); + AddInt(kpse_ocp_format); + AddInt(kpse_ofm_format); + AddInt(kpse_opl_format); + AddInt(kpse_otp_format); + AddInt(kpse_ovf_format); + AddInt(kpse_ovp_format); + AddInt(kpse_pict_format); + AddInt(kpse_tex_format); + AddInt(kpse_texdoc_format); + AddInt(kpse_texpool_format); + AddInt(kpse_texsource_format); + AddInt(kpse_tex_ps_header_format); + AddInt(kpse_troff_font_format); + AddInt(kpse_type1_format); + AddInt(kpse_vf_format); + AddInt(kpse_dvips_config_format); + AddInt(kpse_ist_format); + AddInt(kpse_truetype_format); + AddInt(kpse_type42_format); + AddInt(kpse_web2c_format); + AddInt(kpse_program_text_format); + AddInt(kpse_program_binary_format); + AddInt(kpse_miscfonts_format); + /* + AddInt(kpse_web_format); + AddInt(kpse_cweb_format); + */ + +} diff --git a/compiler/gdsMill/pyx/siteconfig.py b/compiler/gdsMill/pyx/siteconfig.py new file mode 100644 index 00000000..fb8a879e --- /dev/null +++ b/compiler/gdsMill/pyx/siteconfig.py @@ -0,0 +1,35 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2004-2005 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# This file configures PyX search paths relative to the current +# position, e.g. for local usage. When installing PyX via distutils +# the contents of this file is not copied to the PyX installation. +# Instead the correct information about the paths from the installation +# process are used. +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +import os + +lfsdir = os.path.join(os.path.dirname(__file__), "lfs") +sharedir = os.path.join(os.path.dirname(__file__), "..", "contrib") +pyxrcdir = os.path.join(os.path.dirname(__file__), "..") + diff --git a/compiler/gdsMill/pyx/style.py b/compiler/gdsMill/pyx/style.py new file mode 100644 index 00000000..1b036573 --- /dev/null +++ b/compiler/gdsMill/pyx/style.py @@ -0,0 +1,190 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2003-2004,2006 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import math +import attr, unit, canvas + +# +# base classes for stroke and fill styles +# + +class strokestyle(canvas.canvasitem): pass + +class fillstyle(canvas.canvasitem): pass + +# +# common stroke styles +# + + +class linecap(attr.exclusiveattr, strokestyle): + + """linecap of paths""" + + def __init__(self, value=0): + attr.exclusiveattr.__init__(self, linecap) + self.value = value + + def processPS(self, file, writer, context, registry, bbox): + file.write("%d setlinecap\n" % self.value) + + def processPDF(self, file, writer, context, registry, bbox): + file.write("%d J\n" % self.value) + +linecap.butt = linecap(0) +linecap.round = linecap(1) +linecap.square = linecap(2) +linecap.clear = attr.clearclass(linecap) + + +class linejoin(attr.exclusiveattr, strokestyle): + + """linejoin of paths""" + + def __init__(self, value=0): + attr.exclusiveattr.__init__(self, linejoin) + self.value = value + + def processPS(self, file, writer, context, registry, bbox): + file.write("%d setlinejoin\n" % self.value) + + def processPDF(self, file, writer, context, registry, bbox): + file.write("%d j\n" % self.value) + +linejoin.miter = linejoin(0) +linejoin.round = linejoin(1) +linejoin.bevel = linejoin(2) +linejoin.clear = attr.clearclass(linejoin) + + +class miterlimit(attr.exclusiveattr, strokestyle): + + """miterlimit of paths""" + + def __init__(self, value=10.0): + attr.exclusiveattr.__init__(self, miterlimit) + self.value = value + + def processPS(self, file, writer, context, registry, bbox): + file.write("%f setmiterlimit\n" % self.value) + + def processPDF(self, file, writer, context, registry, bbox): + file.write("%f M\n" % self.value) + +miterlimit.lessthan180deg = miterlimit(1/math.sin(math.pi*180/360)) +miterlimit.lessthan90deg = miterlimit(1/math.sin(math.pi*90/360)) +miterlimit.lessthan60deg = miterlimit(1/math.sin(math.pi*60/360)) +miterlimit.lessthan45deg = miterlimit(1/math.sin(math.pi*45/360)) +miterlimit.lessthan11deg = miterlimit(10) # the default, approximately 11.4783 degress +miterlimit.clear = attr.clearclass(miterlimit) + + +_defaultlinewidth = 0.02 * unit.w_cm +_defaultlinewidth_pt = unit.topt(_defaultlinewidth) + +class dash(attr.exclusiveattr, strokestyle): + + """dash of paths""" + + def __init__(self, pattern=[], offset=0, rellengths=1): + """set pattern with offset. + + If rellengths is True, interpret all dash lengths relative to current linewidth. + """ + attr.exclusiveattr.__init__(self, dash) + self.pattern = pattern + self.offset = offset + self.rellengths = rellengths + + def processPS(self, file, writer, context, registry, bbox): + if self.rellengths: + patternstring = " ".join(["%f" % (element * context.linewidth_pt/_defaultlinewidth_pt) for element in self.pattern]) + else: + patternstring = " ".join(["%f" % element for element in self.pattern]) + file.write("[%s] %d setdash\n" % (patternstring, self.offset)) + + def processPDF(self, file, writer, context, registry, bbox): + if self.rellengths: + patternstring = " ".join(["%f" % (element * context.linewidth_pt/_defaultlinewidth_pt) for element in self.pattern]) + else: + patternstring = " ".join(["%f" % element for element in self.pattern]) + file.write("[%s] %d d\n" % (patternstring, self.offset)) + +dash.clear = attr.clearclass(dash) + + +class linestyle(attr.exclusiveattr, strokestyle): + + """linestyle (linecap together with dash) of paths""" + + def __init__(self, c=linecap.butt, d=dash([])): + # XXX better, but at the moment not supported by attr.exlusiveattr would be: + # XXX attr.exclusiveattr.__init__(self, [linestyle, linecap, dash]) + attr.exclusiveattr.__init__(self, linestyle) + self.c = c + self.d = d + + def processPS(self, file, writer, context, registry, bbox): + self.c.processPS(file, writer, context, registry, bbox) + self.d.processPS(file, writer, context, registry, bbox) + + def processPDF(self, file, writer, context, registry, bbox): + self.c.processPDF(file, writer, context, registry, bbox) + self.d.processPDF(file, writer, context, registry, bbox) + +linestyle.solid = linestyle(linecap.butt, dash([])) +linestyle.dashed = linestyle(linecap.butt, dash([2])) +linestyle.dotted = linestyle(linecap.round, dash([0, 2])) +linestyle.dashdotted = linestyle(linecap.round, dash([0, 2, 2, 2])) +linestyle.clear = attr.clearclass(linestyle) + + +class linewidth(attr.sortbeforeexclusiveattr, strokestyle): + + """linewidth of paths""" + + def __init__(self, width): + attr.sortbeforeexclusiveattr.__init__(self, linewidth, [dash, linestyle]) + self.width = width + + def processPS(self, file, writer, context, registry, bbox): + file.write("%f setlinewidth\n" % unit.topt(self.width)) + context.linewidth_pt = unit.topt(self.width) + + def processPDF(self, file, writer, context, registry, bbox): + file.write("%f w\n" % unit.topt(self.width)) + context.linewidth_pt = unit.topt(self.width) + +linewidth.THIN = linewidth(_defaultlinewidth/math.sqrt(32)) +linewidth.THIn = linewidth(_defaultlinewidth/math.sqrt(16)) +linewidth.THin = linewidth(_defaultlinewidth/math.sqrt(8)) +linewidth.Thin = linewidth(_defaultlinewidth/math.sqrt(4)) +linewidth.thin = linewidth(_defaultlinewidth/math.sqrt(2)) +linewidth.normal = linewidth(_defaultlinewidth) +linewidth.thick = linewidth(_defaultlinewidth*math.sqrt(2)) +linewidth.Thick = linewidth(_defaultlinewidth*math.sqrt(4)) +linewidth.THick = linewidth(_defaultlinewidth*math.sqrt(8)) +linewidth.THIck = linewidth(_defaultlinewidth*math.sqrt(16)) +linewidth.THICk = linewidth(_defaultlinewidth*math.sqrt(32)) +linewidth.THICK = linewidth(_defaultlinewidth*math.sqrt(64)) +linewidth.clear = attr.clearclass(linewidth) diff --git a/compiler/gdsMill/pyx/text.py b/compiler/gdsMill/pyx/text.py new file mode 100644 index 00000000..8e51a695 --- /dev/null +++ b/compiler/gdsMill/pyx/text.py @@ -0,0 +1,1361 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2007 Michael Schindler +# Copyright (C) 2002-2006 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import glob, os, threading, Queue, re, tempfile, atexit, time, warnings +import config, siteconfig, unit, box, canvas, trafo, version, attr, style, dvifile +import bbox as bboxmodule + +############################################################################### +# texmessages +# - please don't get confused: +# - there is a texmessage (and a texmessageparsed) attribute within the +# texrunner; it contains TeX/LaTeX response from the last command execution +# - instances of classes derived from the class texmessage are used to +# parse the TeX/LaTeX response as it is stored in the texmessageparsed +# attribute of a texrunner instance +# - the multiple usage of the name texmessage might be removed in the future +# - texmessage instances should implement _Itexmessage +############################################################################### + +class TexResultError(RuntimeError): + """specialized texrunner exception class + - it is raised by texmessage instances, when a texmessage indicates an error + - it is raised by the texrunner itself, whenever there is a texmessage left + after all parsing of this message (by texmessage instances) + prints a detailed report about the problem + - the verbose level is controlled by texrunner.errordebug""" + + def __init__(self, description, texrunner): + if texrunner.errordebug >= 2: + self.description = ("%s\n" % description + + "The expression passed to TeX was:\n" + " %s\n" % texrunner.expr.replace("\n", "\n ").rstrip() + + "The return message from TeX was:\n" + " %s\n" % texrunner.texmessage.replace("\n", "\n ").rstrip() + + "After parsing this message, the following was left:\n" + " %s" % texrunner.texmessageparsed.replace("\n", "\n ").rstrip()) + elif texrunner.errordebug == 1: + firstlines = texrunner.texmessageparsed.split("\n") + if len(firstlines) > 5: + firstlines = firstlines[:5] + ["(cut after 5 lines, increase errordebug for more output)"] + self.description = ("%s\n" % description + + "The expression passed to TeX was:\n" + " %s\n" % texrunner.expr.replace("\n", "\n ").rstrip() + + "After parsing the return message from TeX, the following was left:\n" + + reduce(lambda x, y: "%s %s\n" % (x,y), firstlines, "").rstrip()) + else: + self.description = description + + def __str__(self): + return self.description + + +class _Itexmessage: + """validates/invalidates TeX/LaTeX response""" + + def check(self, texrunner): + """check a Tex/LaTeX response and respond appropriate + - read the texrunners texmessageparsed attribute + - if there is an problem found, raise TexResultError + - remove any valid and identified TeX/LaTeX response + from the texrunners texmessageparsed attribute + -> finally, there should be nothing left in there, + otherwise it is interpreted as an error""" + + +class texmessage(attr.attr): pass + + +class _texmessagestart(texmessage): + """validates TeX/LaTeX startup""" + + __implements__ = _Itexmessage + + startpattern = re.compile(r"This is [-0-9a-zA-Z\s_]*TeX") + + def check(self, texrunner): + # check for "This is e-TeX" + m = self.startpattern.search(texrunner.texmessageparsed) + if not m: + raise TexResultError("TeX startup failed", texrunner) + texrunner.texmessageparsed = texrunner.texmessageparsed[m.end():] + + # check for filename to be processed + try: + texrunner.texmessageparsed = texrunner.texmessageparsed.split("%s.tex" % texrunner.texfilename, 1)[1] + except (IndexError, ValueError): + raise TexResultError("TeX running startup file failed", texrunner) + + # check for \raiseerror -- just to be sure that communication works + try: + texrunner.texmessageparsed = texrunner.texmessageparsed.split("*! Undefined control sequence.\n<*> \\raiseerror\n %\n", 1)[1] + except (IndexError, ValueError): + raise TexResultError("TeX scrollmode check failed", texrunner) + + +class _texmessagenofile(texmessage): + """allows for LaTeXs no-file warning""" + + __implements__ = _Itexmessage + + def __init__(self, fileending): + self.fileending = fileending + + def check(self, texrunner): + try: + s1, s2 = texrunner.texmessageparsed.split("No file %s.%s." % (texrunner.texfilename, self.fileending), 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + try: + s1, s2 = texrunner.texmessageparsed.split("No file %s%s%s.%s." % (os.curdir, + os.sep, + texrunner.texfilename, + self.fileending), 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + pass + + +class _texmessageinputmarker(texmessage): + """validates the PyXInputMarker""" + + __implements__ = _Itexmessage + + def check(self, texrunner): + try: + s1, s2 = texrunner.texmessageparsed.split("PyXInputMarker:executeid=%s:" % texrunner.executeid, 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + raise TexResultError("PyXInputMarker expected", texrunner) + + +class _texmessagepyxbox(texmessage): + """validates the PyXBox output""" + + __implements__ = _Itexmessage + + pattern = re.compile(r"PyXBox:page=(?P\d+),lt=-?\d*((\d\.?)|(\.?\d))\d*pt,rt=-?\d*((\d\.?)|(\.?\d))\d*pt,ht=-?\d*((\d\.?)|(\.?\d))\d*pt,dp=-?\d*((\d\.?)|(\.?\d))\d*pt:") + + def check(self, texrunner): + m = self.pattern.search(texrunner.texmessageparsed) + if m and m.group("page") == str(texrunner.page): + texrunner.texmessageparsed = texrunner.texmessageparsed[:m.start()] + texrunner.texmessageparsed[m.end():] + else: + raise TexResultError("PyXBox expected", texrunner) + + +class _texmessagepyxpageout(texmessage): + """validates the dvi shipout message (writing a page to the dvi file)""" + + __implements__ = _Itexmessage + + def check(self, texrunner): + try: + s1, s2 = texrunner.texmessageparsed.split("[80.121.88.%s]" % texrunner.page, 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + raise TexResultError("PyXPageOutMarker expected", texrunner) + + +class _texmessageend(texmessage): + """validates TeX/LaTeX finish""" + + __implements__ = _Itexmessage + + def check(self, texrunner): + try: + s1, s2 = texrunner.texmessageparsed.split("(%s.aux)" % texrunner.texfilename, 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + try: + s1, s2 = texrunner.texmessageparsed.split("(%s%s%s.aux)" % (os.curdir, + os.sep, + texrunner.texfilename), 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + pass + + # check for "(see the transcript file for additional information)" + try: + s1, s2 = texrunner.texmessageparsed.split("(see the transcript file for additional information)", 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + pass + + # check for "Output written on ...dvi (1 page, 220 bytes)." + dvipattern = re.compile(r"Output written on %s\.dvi \((?P\d+) pages?, \d+ bytes\)\." % texrunner.texfilename) + m = dvipattern.search(texrunner.texmessageparsed) + if texrunner.page: + if not m: + raise TexResultError("TeX dvifile messages expected", texrunner) + if m.group("page") != str(texrunner.page): + raise TexResultError("wrong number of pages reported", texrunner) + texrunner.texmessageparsed = texrunner.texmessageparsed[:m.start()] + texrunner.texmessageparsed[m.end():] + else: + try: + s1, s2 = texrunner.texmessageparsed.split("No pages of output.", 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + raise TexResultError("no dvifile expected", texrunner) + + # check for "Transcript written on ...log." + try: + s1, s2 = texrunner.texmessageparsed.split("Transcript written on %s.log." % texrunner.texfilename, 1) + texrunner.texmessageparsed = s1 + s2 + except (IndexError, ValueError): + raise TexResultError("TeX logfile message expected", texrunner) + + +class _texmessageemptylines(texmessage): + """validates "*-only" (TeX/LaTeX input marker in interactive mode) and empty lines + also clear TeX interactive mode warning (Please type a command or say `\\end') + """ + + __implements__ = _Itexmessage + + def check(self, texrunner): + texrunner.texmessageparsed = texrunner.texmessageparsed.replace(r"(Please type a command or say `\end')", "") + texrunner.texmessageparsed = texrunner.texmessageparsed.replace(" ", "") + texrunner.texmessageparsed = texrunner.texmessageparsed.replace("*\n", "") + texrunner.texmessageparsed = texrunner.texmessageparsed.replace("\n", "") + + +class _texmessageload(texmessage): + """validates inclusion of arbitrary files + - the matched pattern is "( )", where + is a readable file and other stuff can be anything + - If the filename is enclosed in double quotes, it may contain blank space. + - "(" and ")" must be used consistent (otherwise this validator just does nothing) + - this is not always wanted, but we just assume that file inclusion is fine""" + + __implements__ = _Itexmessage + + pattern = re.compile(r"\([\"]?(?P(?:(?[^()]*)\)") + + def baselevels(self, s, maxlevel=1, brackets="()"): + """strip parts of a string above a given bracket level + - return a modified (some parts might be removed) version of the string s + where all parts inside brackets with level higher than maxlevel are + removed + - if brackets do not match (number of left and right brackets is wrong + or at some points there were more right brackets than left brackets) + just return the unmodified string""" + level = 0 + highestlevel = 0 + res = "" + for c in s: + if c == brackets[0]: + level += 1 + if level > highestlevel: + highestlevel = level + if level <= maxlevel: + res += c + if c == brackets[1]: + level -= 1 + if level == 0 and highestlevel > 0: + return res + + def check(self, texrunner): + lowestbracketlevel = self.baselevels(texrunner.texmessageparsed) + if lowestbracketlevel is not None: + m = self.pattern.search(lowestbracketlevel) + while m: + filename = m.group("filename").replace("\n", "") + try: + additional = m.group("additional") + except IndexError: + additional = "" + if (os.access(filename, os.R_OK) or + len(additional) and additional[0] == "\n" and os.access(filename+additional.split()[0], os.R_OK)): + lowestbracketlevel = lowestbracketlevel[:m.start()] + lowestbracketlevel[m.end():] + else: + break + m = self.pattern.search(lowestbracketlevel) + else: + texrunner.texmessageparsed = lowestbracketlevel + + +class _texmessageloaddef(_texmessageload): + """validates the inclusion of font description files (fd-files) + - works like _texmessageload + - filename must end with .def or .fd and no further text is allowed""" + + pattern = re.compile(r"\((?P[^)]+(\.fd|\.def))\)") + + def baselevels(self, s, **kwargs): + return s + + +class _texmessagegraphicsload(_texmessageload): + """validates the inclusion of files as the graphics packages writes it + - works like _texmessageload, but using "<" and ">" as delimiters + - filename must end with .eps and no further text is allowed""" + + pattern = re.compile(r"<(?P[^>]+.eps)>") + + def baselevels(self, s, **kwargs): + return s + + +class _texmessageignore(_texmessageload): + """validates any TeX/LaTeX response + - this might be used, when the expression is ok, but no suitable texmessage + parser is available + - PLEASE: - consider writing suitable tex message parsers + - share your ideas/problems/solutions with others (use the PyX mailing lists)""" + + __implements__ = _Itexmessage + + def check(self, texrunner): + texrunner.texmessageparsed = "" + + +texmessage.start = _texmessagestart() +texmessage.noaux = _texmessagenofile("aux") +texmessage.nonav = _texmessagenofile("nav") +texmessage.end = _texmessageend() +texmessage.load = _texmessageload() +texmessage.loaddef = _texmessageloaddef() +texmessage.graphicsload = _texmessagegraphicsload() +texmessage.ignore = _texmessageignore() + +# for internal use: +texmessage.inputmarker = _texmessageinputmarker() +texmessage.pyxbox = _texmessagepyxbox() +texmessage.pyxpageout = _texmessagepyxpageout() +texmessage.emptylines = _texmessageemptylines() + + +class _texmessageallwarning(texmessage): + """validates a given pattern 'pattern' as a warning 'warning'""" + + def check(self, texrunner): + if texrunner.texmessageparsed: + warnings.warn("ignoring all warnings:\n%s" % texrunner.texmessageparsed) + texrunner.texmessageparsed = "" + +texmessage.allwarning = _texmessageallwarning() + + +class texmessagepattern(texmessage): + """validates a given pattern and issue a warning (when set)""" + + def __init__(self, pattern, warning=None): + self.pattern = pattern + self.warning = warning + + def check(self, texrunner): + m = self.pattern.search(texrunner.texmessageparsed) + while m: + texrunner.texmessageparsed = texrunner.texmessageparsed[:m.start()] + texrunner.texmessageparsed[m.end():] + if self.warning: + warnings.warn("%s:\n%s" % (self.warning, m.string[m.start(): m.end()].rstrip())) + m = self.pattern.search(texrunner.texmessageparsed) + +texmessage.fontwarning = texmessagepattern(re.compile(r"^LaTeX Font Warning: .*$(\n^\(Font\).*$)*", re.MULTILINE), "ignoring font warning") +texmessage.boxwarning = texmessagepattern(re.compile(r"^(Overfull|Underfull) \\[hv]box.*$(\n^..*$)*\n^$\n", re.MULTILINE), "ignoring overfull/underfull box warning") +texmessage.rerunwarning = texmessagepattern(re.compile(r"^(LaTeX Warning: Label\(s\) may have changed\. Rerun to get cross-references right\s*\.)$", re.MULTILINE), "ignoring rerun warning") + + + +############################################################################### +# textattrs +############################################################################### + +_textattrspreamble = "" + +class textattr: + "a textattr defines a apply method, which modifies a (La)TeX expression" + +class _localattr: pass + +_textattrspreamble += r"""\gdef\PyXFlushHAlign{0}% +\def\PyXragged{% +\leftskip=0pt plus \PyXFlushHAlign fil% +\rightskip=0pt plus 1fil% +\advance\rightskip0pt plus -\PyXFlushHAlign fil% +\parfillskip=0pt% +\pretolerance=9999% +\tolerance=9999% +\parindent=0pt% +\hyphenpenalty=9999% +\exhyphenpenalty=9999}% +""" + +class boxhalign(attr.exclusiveattr, textattr, _localattr): + + def __init__(self, aboxhalign): + self.boxhalign = aboxhalign + attr.exclusiveattr.__init__(self, boxhalign) + + def apply(self, expr): + return r"\gdef\PyXBoxHAlign{%.5f}%s" % (self.boxhalign, expr) + +boxhalign.left = boxhalign(0) +boxhalign.center = boxhalign(0.5) +boxhalign.right = boxhalign(1) +# boxhalign.clear = attr.clearclass(boxhalign) # we can't defined a clearclass for boxhalign since it can't clear a halign's boxhalign + + +class flushhalign(attr.exclusiveattr, textattr, _localattr): + + def __init__(self, aflushhalign): + self.flushhalign = aflushhalign + attr.exclusiveattr.__init__(self, flushhalign) + + def apply(self, expr): + return r"\gdef\PyXFlushHAlign{%.5f}\PyXragged{}%s" % (self.flushhalign, expr) + +flushhalign.left = flushhalign(0) +flushhalign.center = flushhalign(0.5) +flushhalign.right = flushhalign(1) +# flushhalign.clear = attr.clearclass(flushhalign) # we can't defined a clearclass for flushhalign since it couldn't clear a halign's flushhalign + + +class halign(attr.exclusiveattr, textattr, boxhalign, flushhalign, _localattr): + + def __init__(self, aboxhalign, aflushhalign): + self.boxhalign = aboxhalign + self.flushhalign = aflushhalign + attr.exclusiveattr.__init__(self, halign) + + def apply(self, expr): + return r"\gdef\PyXBoxHAlign{%.5f}\gdef\PyXFlushHAlign{%.5f}\PyXragged{}%s" % (self.boxhalign, self.flushhalign, expr) + +halign.left = halign(0, 0) +halign.center = halign(0.5, 0.5) +halign.right = halign(1, 1) +halign.clear = attr.clearclass(halign) +halign.boxleft = boxhalign.left +halign.boxcenter = boxhalign.center +halign.boxright = boxhalign.right +halign.flushleft = halign.raggedright = flushhalign.left +halign.flushcenter = halign.raggedcenter = flushhalign.center +halign.flushright = halign.raggedleft = flushhalign.right + + +class _mathmode(attr.attr, textattr, _localattr): + "math mode" + + def apply(self, expr): + return r"$\displaystyle{%s}$" % expr + +mathmode = _mathmode() +clearmathmode = attr.clearclass(_mathmode) + + +class _phantom(attr.attr, textattr, _localattr): + "phantom text" + + def apply(self, expr): + return r"\phantom{%s}" % expr + +phantom = _phantom() +clearphantom = attr.clearclass(_phantom) + + +_textattrspreamble += "\\newbox\\PyXBoxVBox%\n\\newdimen\\PyXDimenVBox%\n" + +class parbox_pt(attr.sortbeforeexclusiveattr, textattr): + + top = 1 + middle = 2 + bottom = 3 + + def __init__(self, width, baseline=top): + self.width = width * 72.27 / (unit.scale["x"] * 72) + self.baseline = baseline + attr.sortbeforeexclusiveattr.__init__(self, parbox_pt, [_localattr]) + + def apply(self, expr): + if self.baseline == self.top: + return r"\linewidth=%.5ftruept\vtop{\hsize=\linewidth\textwidth=\linewidth{}%s}" % (self.width, expr) + elif self.baseline == self.middle: + return r"\linewidth=%.5ftruept\setbox\PyXBoxVBox=\hbox{{\vtop{\hsize=\linewidth\textwidth=\linewidth{}%s}}}\PyXDimenVBox=0.5\dp\PyXBoxVBox\setbox\PyXBoxVBox=\hbox{{\vbox{\hsize=\linewidth\textwidth=\linewidth{}%s}}}\advance\PyXDimenVBox by -0.5\dp\PyXBoxVBox\lower\PyXDimenVBox\box\PyXBoxVBox" % (self.width, expr, expr) + elif self.baseline == self.bottom: + return r"\linewidth=%.5ftruept\vbox{\hsize=\linewidth\textwidth=\linewidth{}%s}" % (self.width, expr) + else: + RuntimeError("invalid baseline argument") + +parbox_pt.clear = attr.clearclass(parbox_pt) + +class parbox(parbox_pt): + + def __init__(self, width, **kwargs): + parbox_pt.__init__(self, unit.topt(width), **kwargs) + +parbox.clear = parbox_pt.clear + + +_textattrspreamble += "\\newbox\\PyXBoxVAlign%\n\\newdimen\\PyXDimenVAlign%\n" + +class valign(attr.sortbeforeexclusiveattr, textattr): + + def __init__(self, avalign): + self.valign = avalign + attr.sortbeforeexclusiveattr.__init__(self, valign, [parbox_pt, _localattr]) + + def apply(self, expr): + return r"\setbox\PyXBoxVAlign=\hbox{{%s}}\PyXDimenVAlign=%.5f\ht\PyXBoxVAlign\advance\PyXDimenVAlign by -%.5f\dp\PyXBoxVAlign\lower\PyXDimenVAlign\box\PyXBoxVAlign" % (expr, 1-self.valign, self.valign) + +valign.top = valign(0) +valign.middle = valign(0.5) +valign.bottom = valign(1) +valign.clear = valign.baseline = attr.clearclass(valign) + + +_textattrspreamble += "\\newdimen\\PyXDimenVShift%\n" + +class _vshift(attr.sortbeforeattr, textattr): + + def __init__(self): + attr.sortbeforeattr.__init__(self, [valign, parbox_pt, _localattr]) + + def apply(self, expr): + return r"%s\setbox0\hbox{{%s}}\lower\PyXDimenVShift\box0" % (self.setheightexpr(), expr) + +class vshift(_vshift): + "vertical down shift by a fraction of a character height" + + def __init__(self, lowerratio, heightstr="0"): + _vshift.__init__(self) + self.lowerratio = lowerratio + self.heightstr = heightstr + + def setheightexpr(self): + return r"\setbox0\hbox{{%s}}\PyXDimenVShift=%.5f\ht0" % (self.heightstr, self.lowerratio) + +class _vshiftmathaxis(_vshift): + "vertical down shift by the height of the math axis" + + def setheightexpr(self): + return r"\setbox0\hbox{$\vcenter{\vrule width0pt}$}\PyXDimenVShift=\ht0" + + +vshift.bottomzero = vshift(0) +vshift.middlezero = vshift(0.5) +vshift.topzero = vshift(1) +vshift.mathaxis = _vshiftmathaxis() +vshift.clear = attr.clearclass(_vshift) + + +defaultsizelist = ["normalsize", "large", "Large", "LARGE", "huge", "Huge", +None, "tiny", "scriptsize", "footnotesize", "small"] + +class size(attr.sortbeforeattr, textattr): + "font size" + + def __init__(self, sizeindex=None, sizename=None, sizelist=defaultsizelist): + if (sizeindex is None and sizename is None) or (sizeindex is not None and sizename is not None): + raise RuntimeError("either specify sizeindex or sizename") + attr.sortbeforeattr.__init__(self, [_mathmode, _vshift]) + if sizeindex is not None: + if sizeindex >= 0 and sizeindex < sizelist.index(None): + self.size = sizelist[sizeindex] + elif sizeindex < 0 and sizeindex + len(sizelist) > sizelist.index(None): + self.size = sizelist[sizeindex] + else: + raise IndexError("index out of sizelist range") + else: + self.size = sizename + + def apply(self, expr): + return r"\%s{}%s" % (self.size, expr) + +size.tiny = size(-4) +size.scriptsize = size.script = size(-3) +size.footnotesize = size.footnote = size(-2) +size.small = size(-1) +size.normalsize = size.normal = size(0) +size.large = size(1) +size.Large = size(2) +size.LARGE = size(3) +size.huge = size(4) +size.Huge = size(5) +size.clear = attr.clearclass(size) + + +############################################################################### +# texrunner +############################################################################### + + +class _readpipe(threading.Thread): + """threaded reader of TeX/LaTeX output + - sets an event, when a specific string in the programs output is found + - sets an event, when the terminal ends""" + + def __init__(self, pipe, expectqueue, gotevent, gotqueue, quitevent): + """initialize the reader + - pipe: file to be read from + - expectqueue: keeps the next InputMarker to be wait for + - gotevent: the "got InputMarker" event + - gotqueue: a queue containing the lines recieved from TeX/LaTeX + - quitevent: the "end of terminal" event""" + threading.Thread.__init__(self) + self.setDaemon(1) # don't care if the output might not be finished (nevertheless, it shouldn't happen) + self.pipe = pipe + self.expectqueue = expectqueue + self.gotevent = gotevent + self.gotqueue = gotqueue + self.quitevent = quitevent + self.expect = None + self.start() + + def run(self): + """thread routine""" + read = self.pipe.readline() # read, what comes in + try: + self.expect = self.expectqueue.get_nowait() # read, what should be expected + except Queue.Empty: + pass + while len(read): + # universal EOL handling (convert everything into unix like EOLs) + # XXX is this necessary on pipes? + read = read.replace("\r", "").replace("\n", "") + "\n" + self.gotqueue.put(read) # report, whats read + if self.expect is not None and read.find(self.expect) != -1: + self.gotevent.set() # raise the got event, when the output was expected (XXX: within a single line) + read = self.pipe.readline() # read again + try: + self.expect = self.expectqueue.get_nowait() + except Queue.Empty: + pass + # EOF reached + self.pipe.close() + if self.expect is not None and self.expect.find("PyXInputMarker") != -1: + raise RuntimeError("TeX/LaTeX finished unexpectedly") + self.quitevent.set() + + +class textbox(box.rect, canvas._canvas): + """basically a box.rect, but it contains a text created by the texrunner + - texrunner._text and texrunner.text return such an object + - _textbox instances can be inserted into a canvas + - the output is contained in a page of the dvifile available thru the texrunner""" + # TODO: shouldn't all boxes become canvases? how about inserts then? + + def __init__(self, x, y, left, right, height, depth, finishdvi, attrs): + """ + - finishdvi is a method to be called to get the dvicanvas + (e.g. the finishdvi calls the setdvicanvas method) + - attrs are fillstyles""" + self.left = left + self.right = right + self.width = left + right + self.height = height + self.depth = depth + self.texttrafo = trafo.scale(unit.scale["x"]).translated(x, y) + box.rect.__init__(self, x - left, y - depth, left + right, depth + height, abscenter = (left, depth)) + canvas._canvas.__init__(self, attrs) + self.finishdvi = finishdvi + self.dvicanvas = None + self.insertdvicanvas = 0 + + def transform(self, *trafos): + if self.insertdvicanvas: + raise RuntimeError("can't apply transformation after dvicanvas was inserted") + box.rect.transform(self, *trafos) + for trafo in trafos: + self.texttrafo = trafo * self.texttrafo + + def setdvicanvas(self, dvicanvas): + if self.dvicanvas is not None: + raise RuntimeError("multiple call to setdvicanvas") + self.dvicanvas = dvicanvas + + def ensuredvicanvas(self): + if self.dvicanvas is None: + self.finishdvi() + assert self.dvicanvas is not None, "finishdvi is broken" + if not self.insertdvicanvas: + self.insert(self.dvicanvas, [self.texttrafo]) + self.insertdvicanvas = 1 + + def marker(self, marker): + self.ensuredvicanvas() + return self.texttrafo.apply(*self.dvicanvas.markers[marker]) + + def processPS(self, file, writer, context, registry, bbox): + self.ensuredvicanvas() + abbox = bboxmodule.empty() + canvas._canvas.processPS(self, file, writer, context, registry, abbox) + bbox += box.rect.bbox(self) + + def processPDF(self, file, writer, context, registry, bbox): + self.ensuredvicanvas() + abbox = bboxmodule.empty() + canvas._canvas.processPDF(self, file, writer, context, registry, abbox) + bbox += box.rect.bbox(self) + + +def _cleantmp(texrunner): + """get rid of temporary files + - function to be registered by atexit + - files contained in usefiles are kept""" + if texrunner.texruns: # cleanup while TeX is still running? + texrunner.expectqueue.put_nowait(None) # do not expect any output anymore + if texrunner.mode == "latex": # try to immediately quit from TeX or LaTeX + texrunner.texinput.write("\n\\catcode`\\@11\\relax\\@@end\n") + else: + texrunner.texinput.write("\n\\end\n") + texrunner.texinput.close() # close the input queue and + if not texrunner.waitforevent(texrunner.quitevent): # wait for finish of the output + return # didn't got a quit from TeX -> we can't do much more + texrunner.texruns = 0 + texrunner.texdone = 1 + for usefile in texrunner.usefiles: + extpos = usefile.rfind(".") + try: + os.rename(texrunner.texfilename + usefile[extpos:], usefile) + except OSError: + pass + for file in glob.glob("%s.*" % texrunner.texfilename): + try: + os.unlink(file) + except OSError: + pass + if texrunner.texdebug is not None: + try: + texrunner.texdebug.close() + texrunner.texdebug = None + except IOError: + pass + + +class _unset: + pass + +class texrunner: + """TeX/LaTeX interface + - runs TeX/LaTeX expressions instantly + - checks TeX/LaTeX response + - the instance variable texmessage stores the last TeX + response as a string + - the instance variable texmessageparsed stores a parsed + version of texmessage; it should be empty after + texmessage.check was called, otherwise a TexResultError + is raised + - the instance variable errordebug controls the verbose + level of TexResultError""" + + defaulttexmessagesstart = [texmessage.start] + defaulttexmessagesdocclass = [texmessage.load] + defaulttexmessagesbegindoc = [texmessage.load, texmessage.noaux] + defaulttexmessagesend = [texmessage.end, texmessage.fontwarning, texmessage.rerunwarning] + defaulttexmessagesdefaultpreamble = [texmessage.load] + defaulttexmessagesdefaultrun = [texmessage.loaddef, texmessage.graphicsload, + texmessage.fontwarning, texmessage.boxwarning] + + def __init__(self, mode="tex", + lfs="10pt", + docclass="article", + docopt=None, + usefiles=[], + fontmaps=config.get("text", "fontmaps", "psfonts.map"), + waitfortex=config.getint("text", "waitfortex", 60), + showwaitfortex=config.getint("text", "showwaitfortex", 5), + texipc=config.getboolean("text", "texipc", 0), + texdebug=None, + dvidebug=0, + errordebug=1, + pyxgraphics=1, + texmessagesstart=[], + texmessagesdocclass=[], + texmessagesbegindoc=[], + texmessagesend=[], + texmessagesdefaultpreamble=[], + texmessagesdefaultrun=[]): + mode = mode.lower() + if mode != "tex" and mode != "latex": + raise ValueError("mode \"TeX\" or \"LaTeX\" expected") + self.mode = mode + self.lfs = lfs + self.docclass = docclass + self.docopt = docopt + self.usefiles = usefiles[:] + self.fontmaps = fontmaps + self.waitfortex = waitfortex + self.showwaitfortex = showwaitfortex + self.texipc = texipc + if texdebug is not None: + if texdebug[-4:] == ".tex": + self.texdebug = open(texdebug, "w") + else: + self.texdebug = open("%s.tex" % texdebug, "w") + else: + self.texdebug = None + self.dvidebug = dvidebug + self.errordebug = errordebug + self.pyxgraphics = pyxgraphics + self.texmessagesstart = texmessagesstart[:] + self.texmessagesdocclass = texmessagesdocclass[:] + self.texmessagesbegindoc = texmessagesbegindoc[:] + self.texmessagesend = texmessagesend[:] + self.texmessagesdefaultpreamble = texmessagesdefaultpreamble[:] + self.texmessagesdefaultrun = texmessagesdefaultrun[:] + + self.texruns = 0 + self.texdone = 0 + self.preamblemode = 1 + self.executeid = 0 + self.page = 0 + self.preambles = [] + self.needdvitextboxes = [] # when texipc-mode off + self.dvifile = None + self.textboxesincluded = 0 + savetempdir = tempfile.tempdir + tempfile.tempdir = os.curdir + self.texfilename = os.path.basename(tempfile.mktemp()) + tempfile.tempdir = savetempdir + + def waitforevent(self, event): + """waits verbosely with an timeout for an event + - observes an event while periodly while printing messages + - returns the status of the event (isSet) + - does not clear the event""" + if self.showwaitfortex: + waited = 0 + hasevent = 0 + while waited < self.waitfortex and not hasevent: + if self.waitfortex - waited > self.showwaitfortex: + event.wait(self.showwaitfortex) + waited += self.showwaitfortex + else: + event.wait(self.waitfortex - waited) + waited += self.waitfortex - waited + hasevent = event.isSet() + if not hasevent: + if waited < self.waitfortex: + warnings.warn("still waiting for %s after %i (of %i) seconds..." % (self.mode, waited, self.waitfortex)) + else: + warnings.warn("the timeout of %i seconds expired and %s did not respond." % (waited, self.mode)) + return hasevent + else: + event.wait(self.waitfortex) + return event.isSet() + + def execute(self, expr, texmessages): + """executes expr within TeX/LaTeX + - if self.texruns is not yet set, TeX/LaTeX is initialized, + self.texruns is set and self.preamblemode is set + - the method must not be called, when self.texdone is already set + - expr should be a string or None + - when expr is None, TeX/LaTeX is stopped, self.texruns is unset and + self.texdone becomes set + - when self.preamblemode is set, the expr is passed directly to TeX/LaTeX + - when self.preamblemode is unset, the expr is passed to \ProcessPyXBox + - texmessages is a list of texmessage instances""" + if not self.texruns: + if self.texdebug is not None: + self.texdebug.write("%% PyX %s texdebug file\n" % version.version) + self.texdebug.write("%% mode: %s\n" % self.mode) + self.texdebug.write("%% date: %s\n" % time.asctime(time.localtime(time.time()))) + for usefile in self.usefiles: + extpos = usefile.rfind(".") + try: + os.rename(usefile, self.texfilename + usefile[extpos:]) + except OSError: + pass + texfile = open("%s.tex" % self.texfilename, "w") # start with filename -> creates dvi file with that name + texfile.write("\\relax%\n") + texfile.close() + if self.texipc: + ipcflag = " --ipc" + else: + ipcflag = "" + try: + self.texinput, self.texoutput = os.popen4("%s%s %s" % (self.mode, ipcflag, self.texfilename), "t", 0) + except ValueError: + # XXX: workaround for MS Windows (bufsize = 0 makes trouble!?) + self.texinput, self.texoutput = os.popen4("%s%s %s" % (self.mode, ipcflag, self.texfilename), "t") + atexit.register(_cleantmp, self) + self.expectqueue = Queue.Queue(1) # allow for a single entry only -> keeps the next InputMarker to be wait for + self.gotevent = threading.Event() # keeps the got inputmarker event + self.gotqueue = Queue.Queue(0) # allow arbitrary number of entries + self.quitevent = threading.Event() # keeps for end of terminal event + self.readoutput = _readpipe(self.texoutput, self.expectqueue, self.gotevent, self.gotqueue, self.quitevent) + self.texruns = 1 + self.fontmap = dvifile.readfontmap(self.fontmaps.split()) + oldpreamblemode = self.preamblemode + self.preamblemode = 1 + self.execute("\\scrollmode\n\\raiseerror%\n" # switch to and check scrollmode + "\\def\\PyX{P\\kern-.3em\\lower.5ex\hbox{Y}\kern-.18em X}%\n" # just the PyX Logo + "\\gdef\\PyXBoxHAlign{0}%\n" # global PyXBoxHAlign (0.0-1.0) for the horizontal alignment, default to 0 + "\\newbox\\PyXBox%\n" # PyXBox will contain the output + "\\newbox\\PyXBoxHAligned%\n" # PyXBox will contain the horizontal aligned output + "\\newdimen\\PyXDimenHAlignLT%\n" # PyXDimenHAlignLT/RT will contain the left/right extent + "\\newdimen\\PyXDimenHAlignRT%\n" + + _textattrspreamble + # insert preambles for textattrs macros + "\\long\\def\\ProcessPyXBox#1#2{%\n" # the ProcessPyXBox definition (#1 is expr, #2 is page number) + "\\setbox\\PyXBox=\\hbox{{#1}}%\n" # push expression into PyXBox + "\\PyXDimenHAlignLT=\\PyXBoxHAlign\\wd\\PyXBox%\n" # calculate the left/right extent + "\\PyXDimenHAlignRT=\\wd\\PyXBox%\n" + "\\advance\\PyXDimenHAlignRT by -\\PyXDimenHAlignLT%\n" + "\\gdef\\PyXBoxHAlign{0}%\n" # reset the PyXBoxHAlign to the default 0 + "\\immediate\\write16{PyXBox:page=#2," # write page and extents of this box to stdout + "lt=\\the\\PyXDimenHAlignLT," + "rt=\\the\\PyXDimenHAlignRT," + "ht=\\the\\ht\\PyXBox," + "dp=\\the\\dp\\PyXBox:}%\n" + "\\setbox\\PyXBoxHAligned=\\hbox{\\kern-\\PyXDimenHAlignLT\\box\\PyXBox}%\n" # align horizontally + "\\ht\\PyXBoxHAligned0pt%\n" # baseline alignment (hight to zero) + "{\\count0=80\\count1=121\\count2=88\\count3=#2\\shipout\\box\\PyXBoxHAligned}}%\n" # shipout PyXBox to Page 80.121.88. + "\\def\\PyXInput#1{\\immediate\\write16{PyXInputMarker:executeid=#1:}}%\n" # write PyXInputMarker to stdout + "\\def\\PyXMarker#1{\\hskip0pt\\special{PyX:marker #1}}%", # write PyXMarker special into the dvi-file + self.defaulttexmessagesstart + self.texmessagesstart) + os.remove("%s.tex" % self.texfilename) + if self.mode == "tex": + if self.lfs: + lfserror = None + if len(self.lfs) > 4 and self.lfs[-4:] == ".lfs": + lfsname = self.lfs + else: + lfsname = "%s.lfs" % self.lfs + for fulllfsname in [lfsname, + os.path.join(siteconfig.lfsdir, lfsname)]: + try: + lfsfile = open(fulllfsname, "r") + lfsdef = lfsfile.read() + lfsfile.close() + break + except IOError: + pass + else: + lfserror = "File '%s' is not available or not readable. " % lfsname + else: + lfserror = "" + if lfserror is not None: + allfiles = (glob.glob("*.lfs") + + glob.glob(os.path.join(siteconfig.lfsdir, "*.lfs"))) + lfsnames = [] + for f in allfiles: + try: + open(f, "r").close() + lfsnames.append(os.path.basename(f)[:-4]) + except IOError: + pass + lfsnames.sort() + if len(lfsnames): + raise IOError("%sAvailable LaTeX font size files (*.lfs): %s" % (lfserror, lfsnames)) + else: + raise IOError("%sNo LaTeX font size files (*.lfs) available. Check your installation." % lfserror) + self.execute(lfsdef, []) + self.execute("\\normalsize%\n", []) + self.execute("\\newdimen\\linewidth\\newdimen\\textwidth%\n", []) + elif self.mode == "latex": + if self.pyxgraphics: + pyxdef = os.path.join(siteconfig.sharedir, "pyx.def") + try: + open(pyxdef, "r").close() + except IOError: + IOError("file 'pyx.def' is not available or not readable. Check your installation or turn off the pyxgraphics option.") + pyxdef = os.path.abspath(pyxdef).replace(os.sep, "/") + self.execute("\\makeatletter%\n" + "\\let\\saveProcessOptions=\\ProcessOptions%\n" + "\\def\\ProcessOptions{%\n" + "\\def\\Gin@driver{" + pyxdef + "}%\n" + "\\def\\c@lor@namefile{dvipsnam.def}%\n" + "\\saveProcessOptions}%\n" + "\\makeatother", + []) + if self.docopt is not None: + self.execute("\\documentclass[%s]{%s}" % (self.docopt, self.docclass), + self.defaulttexmessagesdocclass + self.texmessagesdocclass) + else: + self.execute("\\documentclass{%s}" % self.docclass, + self.defaulttexmessagesdocclass + self.texmessagesdocclass) + self.preamblemode = oldpreamblemode + self.executeid += 1 + if expr is not None: # TeX/LaTeX should process expr + self.expectqueue.put_nowait("PyXInputMarker:executeid=%i:" % self.executeid) + if self.preamblemode: + self.expr = ("%s%%\n" % expr + + "\\PyXInput{%i}%%\n" % self.executeid) + else: + self.page += 1 + self.expr = ("\\ProcessPyXBox{%s%%\n}{%i}%%\n" % (expr, self.page) + + "\\PyXInput{%i}%%\n" % self.executeid) + else: # TeX/LaTeX should be finished + self.expectqueue.put_nowait("Transcript written on %s.log" % self.texfilename) + if self.mode == "latex": + self.expr = "\\end{document}%\n" + else: + self.expr = "\\end%\n" + if self.texdebug is not None: + self.texdebug.write(self.expr) + self.texinput.write(self.expr) + gotevent = self.waitforevent(self.gotevent) + self.gotevent.clear() + if expr is None and gotevent: # TeX/LaTeX should have finished + self.texruns = 0 + self.texdone = 1 + self.texinput.close() # close the input queue and + gotevent = self.waitforevent(self.quitevent) # wait for finish of the output + try: + self.texmessage = "" + while 1: + self.texmessage += self.gotqueue.get_nowait() + except Queue.Empty: + pass + self.texmessage = self.texmessage.replace("\r\n", "\n").replace("\r", "\n") + self.texmessageparsed = self.texmessage + if gotevent: + if expr is not None: + texmessage.inputmarker.check(self) + if not self.preamblemode: + texmessage.pyxbox.check(self) + texmessage.pyxpageout.check(self) + texmessages = attr.mergeattrs(texmessages) + for t in texmessages: + t.check(self) + keeptexmessageparsed = self.texmessageparsed + texmessage.emptylines.check(self) + if len(self.texmessageparsed): + self.texmessageparsed = keeptexmessageparsed + raise TexResultError("unhandled TeX response (might be an error)", self) + else: + raise TexResultError("TeX didn't respond as expected within the timeout period (%i seconds)." % self.waitfortex, self) + + def finishdvi(self, ignoretail=0): + """finish TeX/LaTeX and read the dvifile + - this method ensures that all textboxes can access their + dvicanvas""" + self.execute(None, self.defaulttexmessagesend + self.texmessagesend) + dvifilename = "%s.dvi" % self.texfilename + if not self.texipc: + self.dvifile = dvifile.dvifile(dvifilename, self.fontmap, debug=self.dvidebug) + page = 1 + for box in self.needdvitextboxes: + box.setdvicanvas(self.dvifile.readpage([ord("P"), ord("y"), ord("X"), page, 0, 0, 0, 0, 0, 0])) + page += 1 + if not ignoretail and self.dvifile.readpage(None) is not None: + raise RuntimeError("end of dvifile expected") + self.dvifile = None + self.needdvitextboxes = [] + + def reset(self, reinit=0): + "resets the tex runner to its initial state (upto its record to old dvi file(s))" + if self.texruns: + self.finishdvi() + if self.texdebug is not None: + self.texdebug.write("%s\n%% preparing restart of %s\n" % ("%"*80, self.mode)) + self.executeid = 0 + self.page = 0 + self.texdone = 0 + if reinit: + self.preamblemode = 1 + for expr, texmessages in self.preambles: + self.execute(expr, texmessages) + if self.mode == "latex": + self.execute("\\begin{document}", self.defaulttexmessagesbegindoc + self.texmessagesbegindoc) + self.preamblemode = 0 + else: + self.preambles = [] + self.preamblemode = 1 + + def set(self, mode=_unset, + lfs=_unset, + docclass=_unset, + docopt=_unset, + usefiles=_unset, + fontmaps=_unset, + waitfortex=_unset, + showwaitfortex=_unset, + texipc=_unset, + texdebug=_unset, + dvidebug=_unset, + errordebug=_unset, + pyxgraphics=_unset, + texmessagesstart=_unset, + texmessagesdocclass=_unset, + texmessagesbegindoc=_unset, + texmessagesend=_unset, + texmessagesdefaultpreamble=_unset, + texmessagesdefaultrun=_unset): + """provide a set command for TeX/LaTeX settings + - TeX/LaTeX must not yet been started + - especially needed for the defaultrunner, where no access to + the constructor is available""" + if self.texruns: + raise RuntimeError("set not allowed -- TeX/LaTeX already started") + if mode is not _unset: + mode = mode.lower() + if mode != "tex" and mode != "latex": + raise ValueError("mode \"TeX\" or \"LaTeX\" expected") + self.mode = mode + if lfs is not _unset: + self.lfs = lfs + if docclass is not _unset: + self.docclass = docclass + if docopt is not _unset: + self.docopt = docopt + if usefiles is not _unset: + self.usefiles = usefiles + if fontmaps is not _unset: + self.fontmaps = fontmaps + if waitfortex is not _unset: + self.waitfortex = waitfortex + if showwaitfortex is not _unset: + self.showwaitfortex = showwaitfortex + if texipc is not _unset: + self.texipc = texipc + if texdebug is not _unset: + if self.texdebug is not None: + self.texdebug.close() + if texdebug[-4:] == ".tex": + self.texdebug = open(texdebug, "w") + else: + self.texdebug = open("%s.tex" % texdebug, "w") + if dvidebug is not _unset: + self.dvidebug = dvidebug + if errordebug is not _unset: + self.errordebug = errordebug + if pyxgraphics is not _unset: + self.pyxgraphics = pyxgraphics + if errordebug is not _unset: + self.errordebug = errordebug + if texmessagesstart is not _unset: + self.texmessagesstart = texmessagesstart + if texmessagesdocclass is not _unset: + self.texmessagesdocclass = texmessagesdocclass + if texmessagesbegindoc is not _unset: + self.texmessagesbegindoc = texmessagesbegindoc + if texmessagesend is not _unset: + self.texmessagesend = texmessagesend + if texmessagesdefaultpreamble is not _unset: + self.texmessagesdefaultpreamble = texmessagesdefaultpreamble + if texmessagesdefaultrun is not _unset: + self.texmessagesdefaultrun = texmessagesdefaultrun + + def preamble(self, expr, texmessages=[]): + r"""put something into the TeX/LaTeX preamble + - in LaTeX, this is done before the \begin{document} + (you might use \AtBeginDocument, when you're in need for) + - it is not allowed to call preamble after calling the + text method for the first time (for LaTeX this is needed + due to \begin{document}; in TeX it is forced for compatibility + (you should be able to switch from TeX to LaTeX, if you want, + without breaking something) + - preamble expressions must not create any dvi output + - args might contain texmessage instances""" + if self.texdone or not self.preamblemode: + raise RuntimeError("preamble calls disabled due to previous text calls") + texmessages = self.defaulttexmessagesdefaultpreamble + self.texmessagesdefaultpreamble + texmessages + self.execute(expr, texmessages) + self.preambles.append((expr, texmessages)) + + PyXBoxPattern = re.compile(r"PyXBox:page=(?P\d+),lt=(?P-?\d*((\d\.?)|(\.?\d))\d*)pt,rt=(?P-?\d*((\d\.?)|(\.?\d))\d*)pt,ht=(?P-?\d*((\d\.?)|(\.?\d))\d*)pt,dp=(?P-?\d*((\d\.?)|(\.?\d))\d*)pt:") + + def text(self, x, y, expr, textattrs=[], texmessages=[]): + """create text by passing expr to TeX/LaTeX + - returns a textbox containing the result from running expr thru TeX/LaTeX + - the box center is set to x, y + - *args may contain attr parameters, namely: + - textattr instances + - texmessage instances + - trafo._trafo instances + - style.fillstyle instances""" + if expr is None: + raise ValueError("None expression is invalid") + if self.texdone: + self.reset(reinit=1) + first = 0 + if self.preamblemode: + if self.mode == "latex": + self.execute("\\begin{document}", self.defaulttexmessagesbegindoc + self.texmessagesbegindoc) + self.preamblemode = 0 + first = 1 + textattrs = attr.mergeattrs(textattrs) # perform cleans + attr.checkattrs(textattrs, [textattr, trafo.trafo_pt, style.fillstyle]) + trafos = attr.getattrs(textattrs, [trafo.trafo_pt]) + fillstyles = attr.getattrs(textattrs, [style.fillstyle]) + textattrs = attr.getattrs(textattrs, [textattr]) + # reverse loop over the merged textattrs (last is applied first) + lentextattrs = len(textattrs) + for i in range(lentextattrs): + expr = textattrs[lentextattrs-1-i].apply(expr) + try: + self.execute(expr, self.defaulttexmessagesdefaultrun + self.texmessagesdefaultrun + texmessages) + except TexResultError: + self.finishdvi(ignoretail=1) + raise + if self.texipc: + if first: + self.dvifile = dvifile.dvifile("%s.dvi" % self.texfilename, self.fontmap, debug=self.dvidebug) + match = self.PyXBoxPattern.search(self.texmessage) + if not match or int(match.group("page")) != self.page: + raise TexResultError("box extents not found", self) + left, right, height, depth = [float(xxx)*72/72.27*unit.x_pt for xxx in match.group("lt", "rt", "ht", "dp")] + box = textbox(x, y, left, right, height, depth, self.finishdvi, fillstyles) + for t in trafos: + box.reltransform(t) # TODO: should trafos really use reltransform??? + # this is quite different from what we do elsewhere!!! + # see https://sourceforge.net/mailarchive/forum.php?thread_id=9137692&forum_id=23700 + if self.texipc: + box.setdvicanvas(self.dvifile.readpage([ord("P"), ord("y"), ord("X"), self.page, 0, 0, 0, 0, 0, 0])) + else: + self.needdvitextboxes.append(box) + return box + + def text_pt(self, x, y, expr, *args, **kwargs): + return self.text(x * unit.t_pt, y * unit.t_pt, expr, *args, **kwargs) + + PyXVariableBoxPattern = re.compile(r"PyXVariableBox:page=(?P\d+),par=(?P\d+),prevgraf=(?P\d+):") + + def textboxes(self, text, pageshapes): + # this is some experimental code to put text into several boxes + # while the bounding shape changes from box to box (rectangles only) + # first we load sev.tex + if not self.textboxesincluded: + self.execute(r"\input textboxes.tex", [texmessage.load]) + self.textboxesincluded = 1 + # define page shapes + pageshapes_str = "\\hsize=%.5ftruept%%\n\\vsize=%.5ftruept%%\n" % (72.27/72*unit.topt(pageshapes[0][0]), 72.27/72*unit.topt(pageshapes[0][1])) + pageshapes_str += "\\lohsizes={%\n" + for hsize, vsize in pageshapes[1:]: + pageshapes_str += "{\\global\\hsize=%.5ftruept}%%\n" % (72.27/72*unit.topt(hsize)) + pageshapes_str += "{\\relax}%\n}%\n" + pageshapes_str += "\\lovsizes={%\n" + for hsize, vsize in pageshapes[1:]: + pageshapes_str += "{\\global\\vsize=%.5ftruept}%%\n" % (72.27/72*unit.topt(vsize)) + pageshapes_str += "{\\relax}%\n}%\n" + page = 0 + parnos = [] + parshapes = [] + loop = 0 + while 1: + self.execute(pageshapes_str, []) + parnos_str = "}{".join(parnos) + if parnos_str: + parnos_str = "{%s}" % parnos_str + parnos_str = "\\parnos={%s{\\relax}}%%\n" % parnos_str + self.execute(parnos_str, []) + parshapes_str = "\\parshapes={%%\n%s%%\n{\\relax}%%\n}%%\n" % "%\n".join(parshapes) + self.execute(parshapes_str, []) + self.execute("\\global\\count0=1%%\n" + "\\global\\parno=0%%\n" + "\\global\\myprevgraf=0%%\n" + "\\global\\showprevgraf=0%%\n" + "\\global\\outputtype=0%%\n" + "\\global\\leastcost=10000000%%\n" + "%s%%\n" + "\\vfill\\supereject%%\n" % text, [texmessage.ignore]) + if self.texipc: + if self.dvifile is None: + self.dvifile = dvifile.dvifile("%s.dvi" % self.texfilename, self.fontmap, debug=self.dvidebug) + else: + raise RuntimeError("textboxes currently needs texipc") + lastparnos = parnos + parnos = [] + lastparshapes = parshapes + parshapes = [] + pages = 0 + lastpar = prevgraf = -1 + m = self.PyXVariableBoxPattern.search(self.texmessage) + while m: + pages += 1 + page = int(m.group("page")) + assert page == pages + par = int(m.group("par")) + prevgraf = int(m.group("prevgraf")) + if page <= len(pageshapes): + width = 72.27/72*unit.topt(pageshapes[page-1][0]) + else: + width = 72.27/72*unit.topt(pageshapes[-1][0]) + if page < len(pageshapes): + nextwidth = 72.27/72*unit.topt(pageshapes[page][0]) + else: + nextwidth = 72.27/72*unit.topt(pageshapes[-1][0]) + + if par != lastpar: + # a new paragraph is to be broken + parnos.append(str(par)) + parshape = " 0pt ".join(["%.5ftruept" % width for i in range(prevgraf)]) + if len(parshape): + parshape = " 0pt " + parshape + parshapes.append("{\\parshape %i%s 0pt %.5ftruept}" % (prevgraf + 1, parshape, nextwidth)) + elif prevgraf == lastprevgraf: + pass + else: + # we have to append the breaking of the previous paragraph + oldparshape = " ".join(parshapes[-1].split(' ')[2:2+2*lastprevgraf]) + oldparshape = oldparshape.split('}')[0] + if len(parshape): + oldparshape = " " + oldparshape + parshape = " 0pt ".join(["%.5ftruept" % width for i in range(prevgraf - lastprevgraf)]) + if len(parshape): + parshape = " 0pt " + parshape + else: + parshape = " " + parshapes[-1] = "{\\parshape %i%s%s 0pt %.5ftruept}" % (prevgraf + 1, oldparshape, parshape, nextwidth) + lastpar = par + lastprevgraf = prevgraf + nextpos = m.end() + m = self.PyXVariableBoxPattern.search(self.texmessage, nextpos) + result = [] + for i in range(pages): + result.append(self.dvifile.readpage([i + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])) + if parnos == lastparnos and parshapes == lastparshapes: + return result + loop += 1 + if loop > 100: + raise TexResultError("Too many loops in textboxes ", texrunner) + + +# the module provides an default texrunner and methods for direct access +defaulttexrunner = texrunner() +reset = defaulttexrunner.reset +set = defaulttexrunner.set +preamble = defaulttexrunner.preamble +text = defaulttexrunner.text +text_pt = defaulttexrunner.text_pt + +def escapestring(s, replace={" ": "~", + "$": "\\$", + "&": "\\&", + "#": "\\#", + "_": "\\_", + "%": "\\%", + "^": "\\string^", + "~": "\\string~", + "<": "{$<$}", + ">": "{$>$}", + "{": "{$\{$}", + "}": "{$\}$}", + "\\": "{$\setminus$}", + "|": "{$\mid$}"}): + "escape all ascii characters such that they are printable by TeX/LaTeX" + i = 0 + while i < len(s): + if not 32 <= ord(s[i]) < 127: + raise ValueError("escapestring function handles ascii strings only") + c = s[i] + try: + r = replace[c] + except KeyError: + i += 1 + else: + s = s[:i] + r + s[i+1:] + i += len(r) + return s diff --git a/compiler/gdsMill/pyx/trafo.py b/compiler/gdsMill/pyx/trafo.py new file mode 100644 index 00000000..b9509502 --- /dev/null +++ b/compiler/gdsMill/pyx/trafo.py @@ -0,0 +1,278 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2006 Jörg Lehmann +# Copyright (C) 2002-2004 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import math +import attr, canvas, deformer, unit +import bbox as bboxmodule + +# global epsilon (used to judge whether a matrix is singular) +_epsilon = (1e-5)**2 + +def set(epsilon=None): + global _epsilon + if epsilon is not None: + _epsilon = epsilon + + +# some helper routines + +def _rmatrix(angle): + phi = math.pi*angle/180.0 + + return ((math.cos(phi), -math.sin(phi)), + (math.sin(phi), math.cos(phi))) + +def _rvector(angle, x, y): + phi = math.pi*angle/180.0 + + return ((1-math.cos(phi))*x + math.sin(phi) *y, + -math.sin(phi) *x + (1-math.cos(phi))*y) + + +def _mmatrix(angle): + phi = math.pi*angle/180.0 + + return ( (math.cos(phi)*math.cos(phi)-math.sin(phi)*math.sin(phi), + -2*math.sin(phi)*math.cos(phi) ), + (-2*math.sin(phi)*math.cos(phi), + math.sin(phi)*math.sin(phi)-math.cos(phi)*math.cos(phi) ) ) + +class _marker: pass + +# Exception + +class TrafoException(Exception): + pass + +# trafo: affine transformations + +class trafo_pt(canvas.canvasitem, deformer.deformer): + + """affine transformation (coordinates in constructor in pts) + + Note that though the coordinates in the constructor are in + pts (which is useful for internal purposes), all other + methods only accept units in the standard user notation. + + """ + + def __init__(self, matrix=((1, 0), (0, 1)), vector=(0, 0), epsilon=_marker): + """Return trafo with given transformation matrix and vector. If epsilon + is passed it is used instead of the global epsilon defined in the module to + check whether the matrix is singular or not. Use epsilon=None to turn of this + checking. + """ + if epsilon is _marker: + epsilon = _epsilon + self.epsilon = epsilon + if epsilon is not None and abs(matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]) < epsilon: + raise TrafoException("transformation matrix must not be singular") + else: + self.matrix = matrix + self.vector = vector + + def __mul__(self, other): + if isinstance(other, trafo_pt): + if self.epsilon is None or other.epsilon is None: + epsilon = None + elif self.epsilon <= other.epsilon: + epsilon = self.epsilon + else: + epsilon = other.epsilon + matrix = ( ( self.matrix[0][0]*other.matrix[0][0] + + self.matrix[0][1]*other.matrix[1][0], + self.matrix[0][0]*other.matrix[0][1] + + self.matrix[0][1]*other.matrix[1][1] ), + ( self.matrix[1][0]*other.matrix[0][0] + + self.matrix[1][1]*other.matrix[1][0], + self.matrix[1][0]*other.matrix[0][1] + + self.matrix[1][1]*other.matrix[1][1] ) + ) + + vector = ( self.matrix[0][0]*other.vector[0] + + self.matrix[0][1]*other.vector[1] + + self.vector[0], + self.matrix[1][0]*other.vector[0] + + self.matrix[1][1]*other.vector[1] + + self.vector[1] ) + + return trafo_pt(matrix=matrix, vector=vector, epsilon=epsilon) + else: + raise NotImplementedError("can only multiply two transformations") + + def __str__(self): + return "[%f %f %f %f %f %f]" % \ + ( self.matrix[0][0], self.matrix[1][0], + self.matrix[0][1], self.matrix[1][1], + self.vector[0], self.vector[1] ) + + def processPS(self, file, writer, context, registry, bbox): + file.write("[%f %f %f %f %f %f] concat\n" % \ + ( self.matrix[0][0], self.matrix[1][0], + self.matrix[0][1], self.matrix[1][1], + self.vector[0], self.vector[1] ) ) + + def processPDF(self, file, writer, context, registry, bbox): + file.write("%f %f %f %f %f %f cm\n" % \ + ( self.matrix[0][0], self.matrix[1][0], + self.matrix[0][1], self.matrix[1][1], + self.vector[0], self.vector[1] ) ) + + def bbox(self): + return bboxmodule.empty() + + def apply_pt(self, x_pt, y_pt): + """apply transformation to point (x_pt, y_pt) in pts""" + return ( self.matrix[0][0]*x_pt + self.matrix[0][1]*y_pt + self.vector[0], + self.matrix[1][0]*x_pt + self.matrix[1][1]*y_pt + self.vector[1] ) + + def apply(self, x, y): + # for the transformation we have to convert to points + tx, ty = self.apply_pt(unit.topt(x), unit.topt(y)) + return tx * unit.t_pt, ty * unit.t_pt + + def deform(self, path): + return path.transformed(self) + + def inverse(self): + det = 1.0*(self.matrix[0][0]*self.matrix[1][1] - self.matrix[0][1]*self.matrix[1][0]) + matrix = ( ( self.matrix[1][1]/det, -self.matrix[0][1]/det), + (-self.matrix[1][0]/det, self.matrix[0][0]/det) ) + return ( trafo_pt(matrix=matrix, epsilon=self.epsilon) * + trafo_pt(vector=(-self.vector[0], -self.vector[1]), epsilon=self.epsilon) ) + + def mirrored(self, angle): + return mirror(angle, epsilon=self.epsilon) * self + + def rotated_pt(self, angle, x=None, y=None): + return rotate_pt(angle, x, y, epsilon=self.epsilon) * self + + def rotated(self, angle, x=None, y=None): + return rotate(angle, x, y, epsilon=self.epsilon) * self + + def scaled_pt(self, sx, sy=None, x=None, y=None): + return scale_pt(sx, sy, x, y, epsilon=self.epsilon) * self + + def scaled(self, sx, sy=None, x=None, y=None): + return scale(sx, sy, x, y, epsilon=self.epsilon) * self + + def slanted_pt(self, a, angle=0, x=None, y=None): + return slant_pt(a, angle, x, y, epsilon=self.epsilon) * self + + def slanted(self, a, angle=0, x=None, y=None): + return slant(a, angle, x, y, epsilon=self.epsilon) * self + + def translated_pt(self, x, y): + return translate_pt(x, y, epsilon=self.epsilon) * self + + def translated(self, x, y): + return translate(x, y, epsilon=self.epsilon) * self + + +class trafo(trafo_pt): + + """affine transformation""" + + def __init__(self, matrix=((1,0), (0,1)), vector=(0, 0), epsilon=_marker): + trafo_pt.__init__(self, + matrix, (unit.topt(vector[0]), unit.topt(vector[1])), + epsilon=epsilon) + +# +# some standard transformations +# + +class mirror(trafo): + def __init__(self, angle=0, epsilon=_marker): + trafo.__init__(self, matrix=_mmatrix(angle), epsilon=epsilon) + + +class rotate_pt(trafo_pt): + def __init__(self, angle, x=None, y=None, epsilon=_marker): + vector = 0, 0 + if x is not None or y is not None: + if x is None or y is None: + raise TrafoException("either specify both x and y or none of them") + vector=_rvector(angle, x, y) + + trafo_pt.__init__(self, matrix=_rmatrix(angle), vector=vector, epsilon=epsilon) + + +class rotate(trafo_pt): + def __init__(self, angle, x=None, y=None, epsilon=_marker): + vector = 0, 0 + if x is not None or y is not None: + if x is None or y is None: + raise TrafoException("either specify both x and y or none of them") + vector=_rvector(angle, unit.topt(x), unit.topt(y)) + + trafo_pt.__init__(self, matrix=_rmatrix(angle), vector=vector, epsilon=epsilon) + + +class scale_pt(trafo_pt): + def __init__(self, sx, sy=None, x=None, y=None, epsilon=_marker): + if sy is None: + sy = sx + vector = 0, 0 + if x is not None or y is not None: + if x is None or y is None: + raise TrafoException("either specify both x and y or none of them") + vector = (1-sx)*x, (1-sy)*y + trafo_pt.__init__(self, matrix=((sx, 0), (0, sy)), vector=vector, epsilon=epsilon) + + +class scale(trafo): + def __init__(self, sx, sy=None, x=None, y=None, epsilon=_marker): + if sy is None: + sy = sx + vector = 0, 0 + if x is not None or y is not None: + if x is None or y is None: + raise TrafoException("either specify both x and y or none of them") + vector = (1-sx)*x, (1-sy)*y + trafo.__init__(self, matrix=((sx, 0), (0, sy)), vector=vector, epsilon=epsilon) + + +class slant_pt(trafo_pt): + def __init__(self, a, angle=0, x=None, y=None, epsilon=_marker): + t = ( rotate_pt(-angle, x, y, epsilon=epsilon) * + trafo(matrix=((1, a), (0, 1)), epsilon=epsilon) * + rotate_pt(angle, x, y, epsilon=epsilon) ) + trafo_pt.__init__(self, t.matrix, t.vector, epsilon=epsilon) + + +class slant(trafo): + def __init__(self, a, angle=0, x=None, y=None, epsilon=_marker): + t = ( rotate(-angle, x, y, epsilon=epsilon) * + trafo(matrix=((1, a), (0, 1)), epsilon=epsilon) * + rotate(angle, x, y, epsilon=epsilon) ) + trafo.__init__(self, t.matrix, t.vector, epsilon=epsilon) + + +class translate_pt(trafo_pt): + def __init__(self, x, y, epsilon=_marker): + trafo_pt.__init__(self, vector=(x, y), epsilon=epsilon) + + +class translate(trafo): + def __init__(self, x, y, epsilon=_marker): + trafo.__init__(self, vector=(x, y), epsilon=epsilon) diff --git a/compiler/gdsMill/pyx/type1font.py b/compiler/gdsMill/pyx/type1font.py new file mode 100644 index 00000000..bdf86ca3 --- /dev/null +++ b/compiler/gdsMill/pyx/type1font.py @@ -0,0 +1,186 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2005-2006 Jörg Lehmann +# Copyright (C) 2005-2006 André Wobst +# Copyright (C) 2007 Michael Schindler +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import bbox, canvas, pswriter, pdfwriter + +try: + enumerate([]) +except NameError: + # fallback implementation for Python 2.2 and below + def enumerate(list): + return zip(xrange(len(list)), list) + + +class encoding: + + def __init__(self, name, filename): + """ font encoding contained in filename """ + self.name = name + self.filename = filename + + +class encodingfile: + + def __init__(self, name, filename): + # XXX move the cursor to a module of its own + from font.t1font import cursor + self.name = name + encfile = open(filename, "r") + c = cursor(encfile.read(), "") + encfile.close() + + # name of encoding + self.encname = c.gettoken() + token = c.gettoken() + if token != "[": + raise RuntimeError("cannot parse encoding file '%s', expecting '[' got '%s'" % (filename, token)) + self.encvector = [] + for i in range(256): + token = c.gettoken() + if token == "]": + raise RuntimeError("not enough charcodes in encoding file '%s'" % filename) + self.encvector.append(token) + if c.gettoken() != "]": + raise RuntimeError("too many charcodes in encoding file '%s'" % filename) + token = c.gettoken() + if token != "def": + raise RuntimeError("cannot parse encoding file '%s', expecting 'def' got '%s'" % (filename, token)) + + def decode(self, charcode): + return self.encvector[charcode] + + def outputPS(self, file, writer): + file.write("%%%%BeginProcSet: %s\n" % self.name) + file.write("/%s\n" + "[" % self.name) + for i, glyphname in enumerate(self.encvector): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + file.write(glyphname) + file.write(" ] def\n" + "%%EndProcSet\n") + + def outputPDF(self, file, writer): + file.write("<<\n" + "/Type /Encoding\n" + "/Differences\n" + "[ 0") + for i, glyphname in enumerate(self.encvector): + if i and not (i % 8): + file.write("\n") + else: + file.write(" ") + file.write(glyphname) + file.write(" ]\n" + ">>\n") + + +class font: + + def __init__(self, basefontname, filename, encoding, slant, metric): + self.basefontname = basefontname + self.filename = filename + self.encoding = encoding + self.slant = slant # None or a float + self.metric = metric + + if encoding is not None and slant is not None: + self.encname = "%s-%s" % (basefontname, encoding.name) + self.name = "%s-slant%f" % (self.encname, self.slant) + elif encoding is not None: + self.name = "%s-%s" % (basefontname, encoding.name) + elif slant is not None: + self.name = "%s-slant%f" % (basefontname, self.slant) + else: + self.name = basefontname + + +class text_pt(canvas.canvasitem): + + def __init__(self, x_pt, y_pt, font): + self.font = font + self.x_pt = x_pt + self.y_pt = y_pt + self.width_pt = 0 + self.height_pt = 0 + self.depth_pt = 0 + self.chars = [] + + def addchar(self, char): + metric = self.font.metric + self.width_pt += metric.getwidth_pt(char) + cheight_pt = metric.getwidth_pt(char) + if cheight_pt > self.height_pt: + self.height_pt = cheight_pt + cdepth_pt = metric.getdepth_pt(char) + if cdepth_pt > self.depth_pt: + self.depth_pt = cdepth_pt + self.chars.append(char) + + def bbox(self): + return bbox.bbox_pt(self.x_pt, self.y_pt-self.depth_pt, self.x_pt+self.width_pt, self.y_pt+self.height_pt) + + def processPS(self, file, writer, context, registry, bbox): + # note that we don't register PSfont as it is just a helper resource + # which registers the needed components + pswriter.PSfont(self.font, self.chars, registry) + bbox += self.bbox() + + if ( context.font is None or + context.font.name != self.font.name or + context.font.metric.getsize_pt() != self.font.metric.getsize_pt() ): + file.write("/%s %f selectfont\n" % (self.font.name, self.font.metric.getsize_pt())) + context.font = self.font + outstring = "" + for char in self.chars: + if char > 32 and char < 127 and chr(char) not in "()[]<>\\": + ascii = "%s" % chr(char) + else: + ascii = "\\%03o" % char + outstring += ascii + file.write("%g %g moveto (%s) show\n" % (self.x_pt, self.y_pt, outstring)) + + def processPDF(self, file, writer, context, registry, bbox): + registry.add(pdfwriter.PDFfont(self.font, self.chars, writer, registry)) + bbox += self.bbox() + + if ( context.font is None or + context.font.name != self.font.name or + context.font.metric.getsize_pt() != self.font.metric.getsize_pt() ): + file.write("/%s %f Tf\n" % (self.font.name, self.font.metric.getsize_pt())) + context.font = self.font + outstring = "" + for char in self.chars: + if 32 <= char <= 127 and chr(char) not in "()[]<>\\": + ascii = "%s" % chr(char) + else: + ascii = "\\%03o" % char + outstring += ascii + if self.font.slant is None: + slantvalue = 0 + else: + slantvalue = self.font.slant + file.write("1 0 %f 1 %f %f Tm (%s) Tj\n" % (slantvalue, self.x_pt, self.y_pt, outstring)) + diff --git a/compiler/gdsMill/pyx/unit.py b/compiler/gdsMill/pyx/unit.py new file mode 100644 index 00000000..932eaabb --- /dev/null +++ b/compiler/gdsMill/pyx/unit.py @@ -0,0 +1,237 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2002-2004 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +import types + +scale = { 't':1, 'u':1, 'v':1, 'w':1, 'x':1 } + +_default_unit = "cm" + +_m = { + 'm' : 1, + 'cm': 0.01, + 'mm': 0.001, + 'inch': 0.01*2.54, + 'pt': 0.01*2.54/72, + } + +def set(uscale=None, vscale=None, wscale=None, xscale=None, defaultunit=None): + if uscale is not None: + scale['u'] = uscale + if vscale is not None: + scale['v'] = vscale + if wscale is not None: + scale['w'] = wscale + if xscale is not None: + scale['x'] = xscale + if defaultunit is not None: + global _default_unit + _default_unit = defaultunit + + +def _convert_to(l, dest_unit="m"): + if type(l) in (types.IntType, types.LongType, types.FloatType): + return l * _m[_default_unit] * scale['u'] / _m[dest_unit] + elif not isinstance(l, length): + l = length(l) # convert to length instance if necessary + + return (l.t + l.u*scale['u'] + l.v*scale['v'] + l.w*scale['w'] + l.x*scale['x']) / _m[dest_unit] + +def tom(l): + return _convert_to(l, "m") + +def tocm(l): + return _convert_to(l, "cm") + +def tomm(l): + return _convert_to(l, "mm") + +def toinch(l): + return _convert_to(l, "inch") + +def topt(l): + return _convert_to(l, "pt") + +################################################################################ +# class for generic length +################################################################################ + +class length: + """ PyX lengths + + PyX lengths are composed of five components (t=true, u=user, v=visual, + w=width, and x=TeX) which can be scaled separately (except for the true + component, which is always unscaled). Lengths can be constructed in units + of "pt", "mm", "cm", "m" and "inch". When no unit is given, a module + default is used, which can be changed with the help of the module level function + set(). + """ + + def __init__(self, f=0, type="u", unit=None): + """ create a length instance of the given type with a length f + in the given unit. If unit is not set, the currently set default unit is used. + """ + self.t = self.u = self.v = self.w = self.x = 0 + l = float(f) * _m[unit or _default_unit] + if type == "t": + self.t = l + elif type == "u": + self.u = l + elif type == "v": + self.v = l + elif type == "w": + self.w = l + elif type == "x": + self.x = l + + def __cmp__(self, other): + # we try to convert self and other into meters and + # if this fails, we give other a chance to do the comparison + try: + return cmp(tom(self), tom(other)) + except: + # why does -cmp(other, self) not work? + return -other.__cmp__(self) + + def __mul__(self, factor): + result = length() + result.t = factor * self.t + result.u = factor * self.u + result.v = factor * self.v + result.w = factor * self.w + result.x = factor * self.x + return result + + __rmul__=__mul__ + + def __div__(self, divisor): + if isinstance(divisor, length): + return tom(self) / tom(divisor) + result = length() + result.t = self.t / divisor + result.u = self.u / divisor + result.v = self.v / divisor + result.w = self.w / divisor + result.x = self.x / divisor + return result + + __truediv__ = __div__ + + def __add__(self, other): + # convert to length if necessary + if not isinstance(other, length): + # if other is not a length, we try to convert it into a length and + # if this fails, we give other a chance to do the addition + try: + other = length(other) + except: + return other + self + result = length() + result.t = self.t + other.t + result.u = self.u + other.u + result.v = self.v + other.v + result.w = self.w + other.w + result.x = self.x + other.x + return result + + __radd__ = __add__ + + def __sub__(self, other): + # convert to length if necessary + if not isinstance(other, length): + # if other is not a length, we try to convert it into a length and + # if this fails, we give other a chance to do the subtraction + try: + other = length(other) + except: + return -other + self + result = length() + result.t = self.t - other.t + result.u = self.u - other.u + result.v = self.v - other.v + result.w = self.w - other.w + result.x = self.x - other.x + return result + + def __rsub__(self, other): + # convert to length if necessary + if not isinstance(other, length): + other = length(other) + result = length() + result.t = other.t - self.t + result.u = other.u - self.u + result.v = other.v - self.v + result.w = other.w - self.w + result.x = other.x - self.x + return result + + def __neg__(self): + result = length() + result.t = -self.t + result.u = -self.u + result.v = -self.v + result.w = -self.w + result.x = -self.x + return result + + def __str__(self): + return "(%(t)f t + %(u)f u + %(v)f v + %(w)f w + %(x)f x) m" % self.__dict__ + + +################################################################################ +# predefined instances which can be used as length units +################################################################################ + +# user lengths and unqualified length which are also user length +u_pt = pt = length(1, type="u", unit="pt") +u_m = m = length(1, type="u", unit="m") +u_mm = mm = length(1, type="u", unit="mm") +u_cm = cm = length(1, type="u", unit="cm") +u_inch = inch = length(1, type="u", unit="inch") + +# true lengths +t_pt = length(1, type="t", unit="pt") +t_m = length(1, type="t", unit="m") +t_mm = length(1, type="t", unit="mm") +t_cm = length(1, type="t", unit="cm") +t_inch = length(1, type="t", unit="inch") + +# visual lengths +v_pt = length(1, type="v", unit="pt") +v_m = length(1, type="v", unit="m") +v_mm = length(1, type="v", unit="mm") +v_cm = length(1, type="v", unit="cm") +v_inch = length(1, type="v", unit="inch") + +# width lengths +w_pt = length(1, type="w", unit="pt") +w_m = length(1, type="w", unit="m") +w_mm = length(1, type="w", unit="mm") +w_cm = length(1, type="w", unit="cm") +w_inch = length(1, type="w", unit="inch") + +# TeX lengths +x_pt = length(1, type="x", unit="pt") +x_m = length(1, type="x", unit="m") +x_mm = length(1, type="x", unit="mm") +x_cm = length(1, type="x", unit="cm") +x_inch = length(1, type="x", unit="inch") diff --git a/compiler/gdsMill/pyx/version.py b/compiler/gdsMill/pyx/version.py new file mode 100644 index 00000000..9cf13e3a --- /dev/null +++ b/compiler/gdsMill/pyx/version.py @@ -0,0 +1,25 @@ +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2007 Jörg Lehmann +# Copyright (C) 2002-2007 André Wobst +# +# This file is part of PyX (http://pyx.sourceforge.net/). +# +# PyX 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. +# +# PyX 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 PyX; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + + +version = "0.10" +date = "2007/10/03" diff --git a/compiler/gdsMill/sram_examples/cell6tDemo.py b/compiler/gdsMill/sram_examples/cell6tDemo.py new file mode 100644 index 00000000..689b986c --- /dev/null +++ b/compiler/gdsMill/sram_examples/cell6tDemo.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +import gdsMill +#we are going to make an array of instances of an existing layout +#assume that we designed the "base cell" in cadence +#step 1 is to stream it out of cadence into a GDS to work with +# creater a streamer object to interact with the cadence libraries + +gds_file_in = "sram_lib2.gds" #"sram_cell_6t.gds" #"gds_sram_tgate2.gds" +gds_file_out = "layoutB.gds" +debug = 0 + + +streamer = gdsMill.GdsStreamer() + +# use the streamer to take a cadence layout, and convert it to GDS 2 for us to work with +# the GDS will be named testLayoutA.gds +#streamer.streamFromCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", +# libraryName = "gdsMillTest", +# cellName = "testLayoutA", +# outputPath = "./gdsFiles") + +#next, load our base cell layout from the GDS generated above +arrayCellLayout = gdsMill.VlsiLayout() +reader = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal = debug) +reader.loadFromFile(gds_file_in) + +##since we will be streaming into the same library that testLayout came from +#let's rename it here so that we don't overwrite accidentally later +arrayCellLayout.rename("arrayCell") + +#now create a new layout +#be sure to assign a name, since this will be the root object in our hierarchy to which +#all other objects are referenced +newLayout = gdsMill.VlsiLayout(name="arrayExample", debug=1, units=(0.001,2.0000000000000000e-09)) #units=(5e-4,5e-10)) # + +#now place an instnace of our top level layout into the filled layout +#hierarchy looks like this: +# array example +# array cell layout +# layout elements +# layout elements +# layout elements +# cell instance +# cell instance +# cell instance +# connection elements ..... + +#now create the array of instances +for xIndex in range(0,2): + for yIndex in range(0,2): + if(yIndex%2 == 0): + mirror = "MX" + else: + mirror = "R0" + newLayout.addInstance(arrayCellLayout, + nameOfLayout = "cell_6t", + offsetInMicrons = (xIndex*1.25250,yIndex*(0.91000)), + mirror = mirror, + rotate = 0.0) + +#add a "wire" that in a real example might be a power rail, data bus, etc. +#newLayout.addPath(layerNumber = newLayout.layerNumbersInUse[7], +# coordinates = [(-20.0,0.0),(25.0,0),(25.0,10.0)], +# width = 1.0, +# updateInternalMap = False) +#add some text that in a real example might be an I/O pin +#newLayout.addText(text = "Hello", +# layerNumber = newLayout.layerNumbersInUse[5], +# offsetInMicrons = (0,0), +# magnification = 1, +# rotate = None, +# updateInternalMap=True) + +#and now dump the filled layout to a new GDS file +writer = gdsMill.Gds2writer(newLayout) +writer.writeToFile(gds_file_out) + + +#and stream it into cadence +#streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", +# libraryName = "gdsMillTest", +# inputPath = "./gdsFiles/arrayLayout.gds") + + +print "LIB: %s" , gds_file_in +print "\nCompleted ", gds_file_out diff --git a/compiler/gdsMill/sram_examples/fillerDemo.py b/compiler/gdsMill/sram_examples/fillerDemo.py new file mode 100644 index 00000000..0d581bff --- /dev/null +++ b/compiler/gdsMill/sram_examples/fillerDemo.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +import gdsMill + +#we will add the filler at a higher level of hiearchy +#so first, load our top level layout from GDS +myTopLevelLayout = gdsMill.VlsiLayout() +reader = gdsMill.Gds2reader(myTopLevelLayout) +reader.loadFromFile("./gdsFiles/testLayoutA.gds") + +#now create a new layout +#be sure to assign a name, since this will be the root object in our hierarchy to which +#all other objects are referenced +filledLayout = gdsMill.VlsiLayout(name="filledLayout") + +#now place an instnace of our top level layout into the filled layout +#hierarchy looks like this: +# filled layout +# top level layout +# layout elements +# layout elements +# layout elements +# fill elements +# fill elements ..... +filledLayout.addInstance(myTopLevelLayout, + offsetInMicrons = (0,0), + mirror = "", + rotate = 0.0) + +#now actaully add the fill - gds mill will create an array of boxes +# maintaining spacing from existing layout elements +#we'll do it once for two different layers +filledLayout.fillAreaDensity(layerToFill = myTopLevelLayout.layerNumbersInUse[5], + offsetInMicrons = (-10.0,-10.0), #this is where to start from + coverageWidth = 40.0, #size of the fill area in microns + coverageHeight = 40.0, + minSpacing = 0.5, #distance between fill blocks + blockSize = 2.0 #width and height of each filler block in microns + ) +filledLayout.fillAreaDensity(layerToFill = myTopLevelLayout.layerNumbersInUse[7], + offsetInMicrons = (-11.0,-11.0), #this is where to start from + coverageWidth = 40.0, #size of the fill area in microns + coverageHeight = 40.0, + minSpacing = 0.5, #distance between fill blocks + blockSize = 3.0 #width and height of each filler block in microns + ) +#and now dump the filled layout to a new GDS file +writer = gdsMill.Gds2writer(filledLayout) +writer.writeToFile("./gdsFiles/filledLayout.gds") +#and strea +streamer = gdsMill.GdsStreamer() +streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", + libraryName = "gdsMillTest", + inputPath = "./gdsFiles/filledLayout.gds") diff --git a/compiler/gdsMill/sram_examples/gdsMill.sh b/compiler/gdsMill/sram_examples/gdsMill.sh new file mode 100644 index 00000000..6c28a08e --- /dev/null +++ b/compiler/gdsMill/sram_examples/gdsMill.sh @@ -0,0 +1,10 @@ +#----------------------------- +#To do automatic stream IN/OUT, we need a path to PIPO (the cadence stream executable) +#PIPO is usually inside the dfII/bin directory of your cadence installation +#if not used, this can be commented out +#setenv PATH /some/path/to/cadence/ic-5.141_usr5/tools/dfII/bin\:/some/path/to/cadence/ic-5.141_usr5/tools/bin\:$PATH + +#----------------------------- +#This is the search path where Python will find GDSMill + +export PYTHONPATH=`pwd`/../:$PYTHONPATH diff --git a/compiler/gdsMill/sram_examples/layoutB.gds b/compiler/gdsMill/sram_examples/layoutB.gds new file mode 100644 index 00000000..ed32a547 Binary files /dev/null and b/compiler/gdsMill/sram_examples/layoutB.gds differ diff --git a/compiler/gdsMill/sram_examples/newcell.gds b/compiler/gdsMill/sram_examples/newcell.gds new file mode 100644 index 00000000..dd6c43b7 Binary files /dev/null and b/compiler/gdsMill/sram_examples/newcell.gds differ diff --git a/compiler/gdsMill/sram_examples/newcell.py b/compiler/gdsMill/sram_examples/newcell.py new file mode 100644 index 00000000..92f3a257 --- /dev/null +++ b/compiler/gdsMill/sram_examples/newcell.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +import gdsMill +#we are going to make an array of instances of an existing layout +#assume that we designed the "base cell" in cadence +#step 1 is to stream it out of cadence into a GDS to work with +# creater a streamer object to interact with the cadence libraries + +gds_file_in = "sram_lib2.gds" #"sram_cell_6t.gds" #"gds_sram_tgate2.gds" +gds_file_out = "newcell.gds" +debug = 0 + + +streamer = gdsMill.GdsStreamer() + +# use the streamer to take a cadence layout, and convert it to GDS 2 for us to work with +# the GDS will be named testLayoutA.gds +#streamer.streamFromCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", +# libraryName = "gdsMillTest", +# cellName = "testLayoutA", +# outputPath = "./gdsFiles") + +#next, load our base cell layout from the GDS generated above +arrayCellLayout = gdsMill.VlsiLayout() +reader = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal = debug) +reader.loadFromFile(gds_file_in) + +##since we will be streaming into the same library that testLayout came from +#let's rename it here so that we don't overwrite accidentally later +#arrayCellLayout.rename("tom_2x2") + +#now create a new layout +#be sure to assign a name, since this will be the root object in our hierarchy to which +#all other objects are referenced +#newLayout = gdsMill.VlsiLayout(name="arrayExample", debug=1, units=(5e-4,5e-10)) # +newLayout = gdsMill.VlsiLayout(name="tom_2x2", debug=0, units=(0.001,1.0000000000000001e-09)) # + +#now place an instnace of our top level layout into the filled layout +#hierarchy looks like this: +# array example +# array cell layout +# layout elements +# layout elements +# layout elements +# cell instance +# cell instance +# cell instance +# connection elements ..... + +#now create the array of instances +for xIndex in range(0,2): + for yIndex in range(0,2): + if(yIndex%2 == 0): + mirror = "MX" + else: + mirror = "R0" + newLayout.addInstance(arrayCellLayout, + nameOfLayout = "cell_6t", + offsetInMicrons = (xIndex*1.25250,yIndex*1.820), + mirror = mirror, + rotate = 0.0) + + +#newLayout.addInstance(arrayCellLayout, +# nameOfLayout = "precharge", +# offsetInMicrons = (0*1.25250,1*3.640000), +# mirror = "R0", +# rotate = 0.0) +#newLayout.addInstance(arrayCellLayout, +# nameOfLayout = "precharge", +# offsetInMicrons = (1*1.25250,1*3.640000), +# mirror = "R0", +# rotate = 0.0) + +#add a "wire" that in a real example might be a power rail, data bus, etc. +#newLayout.addPath(layerNumber = newLayout.layerNumbersInUse[7], +# coordinates = [(-20.0,0.0),(25.0,0),(25.0,10.0)], +# width = 1.0, +# updateInternalMap = False) +#add some text that in a real example might be an I/O pin +#newLayout.addText(text = "Hello", +# layerNumber = newLayout.layerNumbersInUse[5], +# offsetInMicrons = (0,0), +# magnification = 1, +# rotate = None, +# updateInternalMap=True) + +#and now dump the filled layout to a new GDS file +writer = gdsMill.Gds2writer(newLayout) +writer.writeToFile(gds_file_out) + + +#and stream it into cadence +#streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", +# libraryName = "gdsMillTest", +# inputPath = "./gdsFiles/arrayLayout.gds") + + +print "LIB: %s" % gds_file_in +print "\nCompleted ", gds_file_out diff --git a/compiler/gdsMill/sram_examples/printGDS.py b/compiler/gdsMill/sram_examples/printGDS.py new file mode 100644 index 00000000..2522bf9e --- /dev/null +++ b/compiler/gdsMill/sram_examples/printGDS.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import gdsMill, sys + +gds_file = sys.argv[1] #"layoutB.gds" #"sram_cell_6t.gds" #"gds_sram_tgate2.gds" +#streamer = gdsMill.GdsStreamer() + +arrayCellLayout = gdsMill.VlsiLayout() +reader = gdsMill.Gds2reader(arrayCellLayout,debugToTerminal = 1) +reader.loadFromFile(gds_file) + diff --git a/compiler/gdsMill/sram_examples/quickStart.py b/compiler/gdsMill/sram_examples/quickStart.py new file mode 100644 index 00000000..5d893c9f --- /dev/null +++ b/compiler/gdsMill/sram_examples/quickStart.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +import gdsMill + +gds_file = "sram_lib2.gds" #"gds_sram_tgate2.gds" +#creater a streamer object to interact with the cadence libraries +#streamer = gdsMill.GdsStreamer() + +#use the streamer to take a cadence layout, and convert it to GDS 2 for us to work with +#the GDS will be named testLayoutA.gds +#streamer.streamFromCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", +# libraryName = "gdsMillTest", +# cellName = "testLayoutA", +# outputPath = "./gdsFiles") + +#create a layout object - this object represents all of the elements within the layout +myLayout = gdsMill.VlsiLayout() + +#give our layout object to a gds reader object. The gds reader will look at the binary gds 2 file and +#populate the layout object with the file's contents +reader = gdsMill.Gds2reader(myLayout) + +#tell the reader object to process the gds file that we streamed in above +#un-comment the next line to see some details about the gds contents +#reader.debugToTerminal=1 +reader.loadFromFile(gds_file) + +#our layout object now contains all of the elements of the layout +#let add a box to the layout +#myLayout.addBox(layerNumber = myLayout.layerNumbersInUse[0], #pick some layer +# offsetInMicrons = (-10,0), #location +# width = 1.0, #units are microns +# height = 2.0, +# updateInternalMap = True, #This is important for visualization - see note 1 below +# center = True) #origin is in the center or the bottom left corner +#Note 1:the layout object keeps track of its elements in a 2D map (no hiearachy) +#this makes visualization more efficient. Therefore, to do a PDF output or some other +#flat output, you need to "update the internal map" of the layout object. Only do this +# ONE TIME after all the objects you are manipulating are fixed + +#let's take out new layout and stream it back into a cadence library +#first, create a new GDS +#we change the root structure name (this will be the cellview name upon stream in) +myLayout.rename("testLayoutB") +writer = gdsMill.Gds2writer(myLayout) +writer.writeToFile("testLayoutB.gds") + +streamer.streamToCadence(cadenceLibraryContainerPath = "~/design/600nmAmi", + libraryName = "gdsMillTest", + inputPath = "./gdsFiles/testLayoutB.gds") + +#let's create a PDF view of the layout object +#first, create the object to represent the visual output +visualizer = gdsMill.PdfLayout(myLayout) + +#since we have no knowledge of what the layer numbers mean for this particular technology +#we need to assign some colors to them + +#uncomment the following line if you want to actually see the layout numbers in use +#print myLayout.layerNumbersInUse + +#for each layer number used in the layout, we will asign it a layer color as a RGB Hex +visualizer.layerColors[myLayout.layerNumbersInUse[0]]="#219E1C" +visualizer.layerColors[myLayout.layerNumbersInUse[1]]="#271C9E" +visualizer.layerColors[myLayout.layerNumbersInUse[2]]="#CC54C8" +visualizer.layerColors[myLayout.layerNumbersInUse[3]]="#E9C514" +visualizer.layerColors[myLayout.layerNumbersInUse[4]]="#856F00" +#visualizer.layerColors[myLayout.layerNumbersInUse[5]]="#BD1444" +#visualizer.layerColors[myLayout.layerNumbersInUse[6]]="#FD1444" +#visualizer.layerColors[myLayout.layerNumbersInUse[7]]="#FD1414" + +#set the scale so that our PDF isn't enormous +visualizer.setScale(500) +#tell the pdf layout object to draw everything in our layout +visualizer.drawLayout() +#and finally, dump it out to a file +visualizer.writeToFile("./gdsFiles/gdsOut.pdf") + diff --git a/compiler/gdsMill/sram_examples/sram_lib16.gds b/compiler/gdsMill/sram_examples/sram_lib16.gds new file mode 100644 index 00000000..bfdde642 Binary files /dev/null and b/compiler/gdsMill/sram_examples/sram_lib16.gds differ diff --git a/compiler/gdsMill/sram_examples/sram_lib2.gds b/compiler/gdsMill/sram_examples/sram_lib2.gds new file mode 100644 index 00000000..f5bcbdab Binary files /dev/null and b/compiler/gdsMill/sram_examples/sram_lib2.gds differ diff --git a/compiler/geometry.py b/compiler/geometry.py new file mode 100644 index 00000000..1585e4a4 --- /dev/null +++ b/compiler/geometry.py @@ -0,0 +1,160 @@ +""" +This provides a set of useful generic types for the gdsMill interface. +""" + +import tech +import debug +from utils import snap_to_grid + +class geometry: + """ + A specific path, shape, or text geometry. Base class for shared + items. + """ + def __init__(self): + """ By default, everything has no size. """ + self.width = 0 + self.height = 0 + + def __str__(self): + """ override print function output """ + debug.error("__str__ must be overridden by all geometry types.",1) + + def __repr__(self): + """ override print function output """ + debug.error("__repr__ must be overridden by all geometry types.",1) + + +class instance(geometry): + """ + An instance of an instance/module with a specified location and + rotation + """ + def __init__(self, name, mod, offset, mirror, rotate): + """Initializes an instance to represent a module""" + geometry.__init__(self) + self.name = name + self.mod = mod + self.gds = mod.gds + self.rotate = rotate + self.offset = snap_to_grid(offset) + self.mirror = mirror + + + + def gds_write_file(self, newLayout): + """Recursively writes all the sub-modules in this instance""" + debug.info(3, "writing instance:" + self.name) + # make sure to write out my module/structure + # (it will only be written the first time though) + self.mod.gds_write_file(self.gds) + # now write an instance of my module/structure + newLayout.addInstance(self.gds, + offsetInMicrons=self.offset, + mirror=self.mirror, + rotate=self.rotate) + + def __str__(self): + """ override print function output """ + return "inst: " + self.name + " mod=" + self.mod.name + + def __repr__(self): + """ override print function output """ + return "( inst: " + self.name + " @" + str(self.offset) + " mod=" + self.mod.name + " " + self.mirror + " R=" + str(self.rotate) + ")" + +class path(geometry): + """Represents a Path""" + + def __init__(self, layerNumber, coordinates, path_width): + """Initializes a path for the specified layer""" + geometry.__init__(self) + self.name = "path" + self.layerNumber = layerNumber + self.coordinates = map(lambda x: [x[0], x[1]], coordinates) + self.coordinates = snap_to_grid(self.coordinates) + self.path_width = path_width + + # FIXME figure out the width/height. This type of path is not + # supported right now. It might not work in gdsMill. + assert(0) + + def gds_write_file(self, newLayout): + """Writes the path to GDS""" + debug.info(3, "writing path (" + str(self.layerNumber) + "):" + self.coordinates) + newLayout.addPath(layerNumber=self.layerNumber, + purposeNumber=0, + coordinates=self.coordinates, + width=self.path_width) + + def __str__(self): + """ override print function output """ + return "path: layer=" + self.layerNumber + " w=" + self.width + + def __repr__(self): + """ override print function output """ + return "( path: layer=" + self.layerNumber + " w=" + self.width + " coords=" + str(self.coordinates) + " )" + + +class label(geometry): + """Represents a text label""" + + def __init__(self, text, layerNumber, offset, zoom=1): + """Initializes a text label for specified layer""" + geometry.__init__(self) + self.name = "label" + self.text = text + self.layerNumber = layerNumber + self.offset = snap_to_grid(offset) + self.zoom = zoom + self.size = 0 + + def gds_write_file(self, newLayout): + """Writes the text label to GDS""" + debug.info(3, "writing label (" + str(self.layerNumber) + "):" + self.text) + newLayout.addText(text=self.text, + layerNumber=self.layerNumber, + purposeNumber=0, + offsetInMicrons=self.offset, + magnification=self.zoom, + rotate=None) + + def __str__(self): + """ override print function output """ + return "label: " + self.text + " layer=" + str(self.layerNumber) + + def __repr__(self): + """ override print function output """ + return "( label: " + self.text + " @" + str(self.offset) + " layer=" + self.layerNumber + " )" + +class rectangle(geometry): + """Represents a rectangular shape""" + + def __init__(self, layerNumber, offset, width, height): + """Initializes a rectangular shape for specified layer""" + geometry.__init__(self) + self.name = "rect" + self.layerNumber = layerNumber + self.offset = snap_to_grid(offset) + self.size = snap_to_grid([width, height]) + self.width = self.size[0] + self.height = self.size[1] + + def gds_write_file(self, newLayout): + """Writes the rectangular shape to GDS""" + snapped_offset=snap_to_grid(self.offset) + debug.info(3, "writing rectangle (" + str(self.layerNumber) + "):" + + str(self.width) + "x" + str(self.height) + " @ " + str(snapped_offset)) + newLayout.addBox(layerNumber=self.layerNumber, + purposeNumber=0, + offsetInMicrons=snapped_offset, + width=self.width, + height=self.height, + center=False) + + def __str__(self): + """ override print function output """ + return "rect: @" + str(self.offset) + " " + str(self.width) + "x" + str(self.height) + " layer=" +str(self.layerNumber) + + def __repr__(self): + """ override print function output """ + return "( rect: @" + str(self.offset) + " " + str(self.width) + "x" + str(self.height) + " layer=" + str(self.layerNumber) + " )" diff --git a/compiler/globals.py b/compiler/globals.py new file mode 100644 index 00000000..713efb07 --- /dev/null +++ b/compiler/globals.py @@ -0,0 +1,227 @@ +""" +This is called globals.py, but it actually parses all the arguments and performs +the global OpenRAM setup as well. +""" +import os +import debug +import shutil +import optparse +import options +import sys +import re +import importlib + +# Current version of OpenRAM. +VERSION = "1.0" + +# Output banner for file output and program execution. +BANNER = """\ +############################################################## +# # +# OpenRAM Compiler v""" + VERSION + """ # +# # +# VLSI Design Automation Lab # +# UCSC CE Department # +# # +# VLSI Computer Architecture Research Group # +# Oklahoma State University ECE Department # +# # +##############################################################\n""" + +USAGE = "usage: openram.py [options] \n" + +# Anonymous object that will be the options +OPTS = options.options() + +def is_exe(fpath): + return os.path.exists(fpath) and os.access(fpath, os.X_OK) + +# parse the optional arguments +# this only does the optional arguments + + +def parse_args(): + """Parse the arguments and initialize openram""" + + global OPTS + + option_list = { + optparse.make_option("-b", "--backannotated", dest="run_pex", + help="back annotated simulation for characterizer"), + optparse.make_option("-o", "--output", dest="out_name", + help="Base output file name.", metavar="FILE"), + optparse.make_option("-p", "--outpath", dest="out_path", + help="output file location."), + optparse.make_option("-n", "--nocheck", action="store_false", + help="Disable inline LVS/DRC checks", dest="check_lvsdrc"), + optparse.make_option("-q", "--quiet", action="store_false", dest="print_banner", + help="Don\'t display banner"), + optparse.make_option("-v", "--verbose", action="count", dest="debug_level", + help="Increase the verbosity level"), + optparse.make_option("-t", "--tech", dest="tech_name", + help="Technology name"), + optparse.make_option("-s", "--spiceversion", dest="spice_version", + help="Spice simulator name"), + # TODO: Why is this -f? + optparse.make_option("-f", "--trim_noncritical", dest="trim_noncritical", + help="Trim noncritical memory cells during simulation") + } +# -h --help is implicit. + + parser = optparse.OptionParser(option_list=option_list, + description="Compile and/or characterize an SRAM.", + usage=USAGE, + version="sramc v" + VERSION) + + (options, args) = parser.parse_args(values=OPTS) + + return (options, args) + + +def get_opts(): + return(OPTS) + + +def init_openram(config_file): + """Initialize the technology, paths, simulators, etc.""" + + debug.info(1,"Initializing OpenRAM...") + + setup_paths() + + read_config(config_file) + + import_tech() + + set_spice() + + set_calibre() + +def read_config(config_file): + global OPTS + + OPTS.config_file = config_file + OPTS.config_file = re.sub(r'\.py$', "", OPTS.config_file) + + # dynamically import the configuration file of which modules to use + debug.info(1, "Configuration file is " + OPTS.config_file + ".py") + try: + OPTS.config = importlib.import_module(OPTS.config_file) + except: + debug.error("Unable to read configuration file: {0}".format(OPTS.config_file+".py. Did you specify the technology?"),2) + + +def set_calibre(): + debug.info(2,"Finding calibre...") + global OPTS + + # check if calibre is installed, if so, we should be running LVS/DRC on + # everything. + if not OPTS.check_lvsdrc: + # over-ride the check LVS/DRC option + debug.info(0,"Over-riding LVS/DRC. Not performing inline LVS/DRC.") + else: + # see if calibre is in the path (extend to other tools later) + for path in os.environ["PATH"].split(os.pathsep): + OPTS.calibre_exe = os.path.join(path, "calibre") + # if it is found, do inline LVS/DRC + if is_exe(OPTS.calibre_exe): + OPTS.check_lvsdrc = True + debug.info(1, "Using calibre: " + OPTS.calibre_exe) + break + else: + # otherwise, give warning and procede + debug.warning("Calibre not found. Not performing inline LVS/DRC.") + OPTS.check_lvsdrc = False + + +def setup_paths(): + """ Set up the non-tech related paths. """ + debug.info(2,"Setting up paths...") + + global OPTS + + OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) + sys.path.append("{0}".format(OPENRAM_HOME)) + sys.path.append("{0}/gdsMill".format(OPENRAM_HOME)) + sys.path.append("{0}/tests".format(OPENRAM_HOME)) + sys.path.append("{0}/characterizer".format(OPENRAM_HOME)) + + + if not OPTS.openram_temp.endswith('/'): + OPTS.openram_temp += "/" + debug.info(1, "Temporary files saved in " + OPTS.openram_temp) + + # we should clean up this temp directory after execution... + if os.path.exists(OPTS.openram_temp): + shutil.rmtree(OPTS.openram_temp, ignore_errors=True) + + # make the directory if it doesn't exist + try: + os.makedirs(OPTS.openram_temp, 0750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(OPTS.openram_temp, 0750) + + # Don't delete the output dir, it may have other files! + # make the directory if it doesn't exist + try: + os.makedirs(OPTS.out_path, 0750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(OPTS.out_path, 0750) + + if OPTS.out_path=="": + OPTS.out_path="." + if not OPTS.out_path.endswith('/'): + OPTS.out_path += "/" + debug.info(1, "Output saved in " + OPTS.out_path) + + +def set_spice(): + debug.info(2,"Finding spice...") + global OPTS + + # set the input dir for spice files if using ngspice (not needed for + # hspice) + if OPTS.spice_version == "ngspice": + os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp) + + # search for calibre in the path + for path in os.environ["PATH"].split(os.pathsep): + OPTS.spice_exe = os.path.join(path, OPTS.spice_version) + # if it is found, then break and use first version + if is_exe(OPTS.spice_exe): + debug.info(1, "Using spice: " + OPTS.spice_exe) + break + else: + # otherwise, give warning and procede + debug.warning("Spice not found. Unable to perform characterization.") + + +# imports correct technology directories for testing +def import_tech(): + global OPTS + + debug.info(2,"Importing technology: " + OPTS.tech_name) + + if OPTS.tech_name == "": + OPTS.tech_name = OPTS.config.tech_name + + # environment variable should point to the technology dir + OPTS.openram_tech = os.path.abspath(os.environ.get("OPENRAM_TECH")) + "/" + OPTS.tech_name + if not OPTS.openram_tech.endswith('/'): + OPTS.openram_tech += "/" + debug.info(1, "Technology path is " + OPTS.openram_tech) + + try: + filename = "setup_openram_{0}".format(OPTS.tech_name) + # we assume that the setup scripts (and tech dirs) are located at the + # same level as the compielr itself, probably not a good idea though. + path = "{0}/setup_scripts".format(os.environ.get("OPENRAM_TECH")) + sys.path.append(os.path.abspath(path)) + __import__(filename) + except ImportError: + debug.error("Nonexistent technology_setup_file: {0}.py".format(filename)) + sys.exit(1) + diff --git a/compiler/hierarchical_decoder.py b/compiler/hierarchical_decoder.py new file mode 100644 index 00000000..5b4c590f --- /dev/null +++ b/compiler/hierarchical_decoder.py @@ -0,0 +1,603 @@ +from tech import drc, cell +import debug +import design +from math import log +from math import sqrt +import math +from contact import contact +from nand_2 import nand_2 +from nand_3 import nand_3 +from pinv import pinv +from hierarchical_predecode2x4 import hierarchical_predecode2x4 as pre2x4 +from hierarchical_predecode3x8 import hierarchical_predecode3x8 as pre3x8 +from vector import vector +from globals import OPTS + +class hierarchical_decoder(design.design): + """ + Dynamically generated hierarchical decoder. + """ + + def __init__(self, nand2_nmos_width, nand3_nmos_width, rows): + design.design.__init__(self, "hierarchical_decoder") + + c = reload(__import__(OPTS.config.bitcell)) + self.mod_bitcell = getattr(c, OPTS.config.bitcell) + self.bitcell_height = self.mod_bitcell.chars["height"] + + self.rows = rows + self.nand2_nmos_width = nand2_nmos_width + self.nand3_nmos_width = nand3_nmos_width + self.num_inputs = int(math.log(self.rows, 2)) + self.create_layout() + self.DRC_LVS() + + def create_layout(self): + self.add_modules() + self.setup_layout_offsets() + self.setup_layout_constants() + self.create_decoder() + # We only need to call the offset_all_coordinate function when there + # are vertical metal rails. + if (self.num_inputs >= 4): + self.offset_all_coordinates() + + def create_decoder(self): + self.add_pins() + self.dimensions_hierarchy_decoder() + self.create_pre_decoder() + self.create_row_decoder() + self.create_vertical_rail() + + def add_modules(self): + self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) + # Vertical metal rail gap definition + self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2 + self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"] + self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"] + self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2 + # used to shift contact when connecting to NAND3 C pin down + self.contact_shift = (self.m1m2_via.first_layer_width - self.m1m2_via.contact_width) / 2 + + self.inv = pinv(name="pinverter", + nmos_width=drc["minwidth_tx"], + beta=2, + height=self.bitcell_height) + self.add_mod(self.inv) + self.nand2 = nand_2(name="pnand2", + nmos_width=self.nand2_nmos_width, + height=self.bitcell_height) + self.add_mod(self.nand2) + self.nand3 = nand_3(name="pnand3", + nmos_width=self.nand3_nmos_width, + height=self.bitcell_height) + self.add_mod(self.nand3) + + # CREATION OF PRE-DECODER + self.pre2_4 = pre2x4(self.nand2_nmos_width, + "pre2x4") + self.add_mod(self.pre2_4) + self.pre3_8 = pre3x8(self.nand3_nmos_width, + "pre3x8") + self.add_mod(self.pre3_8) + + def setup_layout_offsets(self): + self.vdd_positions = [] + self.gnd_positions = [] + self.decode_out_positions = [] + self.A_positions = [] + + self.pre_decoder_vdd_positions = [] + self.pre_decoder_gnd_positions = [] + + def determine_predecodes(self,num_inputs): + # Determines the number of 2:4 pre-decoder and 3:8 pre-decoder needed + # based on the number of inputs + if (num_inputs == 2): + return (1,0) + elif (num_inputs == 3): + return(0,1) + elif (num_inputs == 4): + return(2,0) + elif (num_inputs == 5): + return(1,1) + elif (num_inputs == 6): + return(3,0) + elif (num_inputs == 7): + return(2,1) + elif (num_inputs == 8): + return(1,2) + elif (num_inputs == 9): + return(0,3) + else: + debug.error("Invalid number of inputs for hierarchical decoder") + + def setup_layout_constants(self): + (p2x4,p3x8)=self.determine_predecodes(self.num_inputs) + self.no_of_pre2x4=p2x4 + self.no_of_pre3x8=p3x8 + + # Stromg the index of the vertical rails in different groups. These + # vertical lines is used to connect pre-decoder to row-decoder + self.predecoder_output_groups = [] # This array is a 2D array. + self.group_sizes = [] + + # Distributing vertical rails to different groups. One group belongs to one pre-decoder. + # For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will + # have total 16 output lines out of these 3 pre-decoders and they will + # be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ] + # in self.predecoder_output_groups + index = 0 + for i in range(self.no_of_pre2x4): + lines = [] + for j in range(4): + lines.append(index) + index = index + 1 + self.predecoder_output_groups.append(lines) + self.group_sizes.append(4) + + for i in range(self.no_of_pre3x8): + lines = [] + for j in range(8): + lines.append(index) + index = index + 1 + self.predecoder_output_groups.append(lines) + self.group_sizes.append(8) + + def add_pins(self): + for i in range(self.num_inputs): + self.add_pin("A[{0}]".format(i)) + + if(self.num_inputs >= 4): + for j in range(self.rows): + self.add_pin("decode_out[{0}]".format(j)) + else: + for j in range(self.rows): + self.add_pin("out[{0}]".format(j)) + self.add_pin("vdd") + self.add_pin("gnd") + + def dimensions_hierarchy_decoder(self): + self.total_number_of_predecoder_outputs = (4 * self.no_of_pre2x4 + + 8 * self.no_of_pre3x8) + + # Calculates height and width of pre-decoder, + if(self.no_of_pre3x8 > 0): + self.predecoder_width = self.pre3_8.width + else: + self.predecoder_width = self.pre2_4.width + self.predecoder_height = (self.pre2_4.height * self.no_of_pre2x4 + + self.pre3_8.height * self.no_of_pre3x8) + + # Calculates height and width of row-decoder + if (self.num_inputs == 4 or self.num_inputs == 5): + nand_width = self.nand2.width + else: + nand_width = self.nand3.width + total_gap = (self.gap_between_rail_offset + * self.total_number_of_predecoder_outputs) + self.row_decoder_width = (nand_width + total_gap + + self.inv.width) + self.row_decoder_height = self.inv.height * self.rows + + # Calculates height and width of hierarchical decoder + self.height = (self.predecoder_height + + self.row_decoder_height) + self.width = self.predecoder_width + total_gap + + def create_pre_decoder(self): + """ Creates pre-decoder and places labels input address [A] """ + for i in range(self.no_of_pre2x4): + self.add_pre2x4(i) + self.add_lables_pre2x4(i) + for j in range(self.no_of_pre3x8): + pre3x8_yoffset=self.add_pre3x8(j) + self.add_lables_pre3x8(j,pre3x8_yoffset) + + def add_pre2x4(self,i): + if (self.num_inputs == 2): + base = vector(0,0) + mod_dir = vector(1,1) + mirror = "RO" + index_off1 = index_off2 = 0 + else: + base= vector(self.pre2_4.width, i * self.pre2_4.height) + mod_dir = vector(-1,1) + mirror = "MY" + index_off1 = i * 2 + index_off2 = i * 4 + + pins = [] + for input_index in range(2): + pins.append("A[{0}]".format(input_index + index_off1)) + for output_index in range(4): + pins.append("out[{0}]".format(output_index + index_off2)) + pins = pins + ["vdd", "gnd"] + + self.add_inst(name="pre[{0}]".format(i), + mod=self.pre2_4, + offset=base, + mirror=mirror) + self.connect_inst(pins) + + vdd_offset = base + self.pre2_4.vdd_position.scale(mod_dir) + self.pre_decoder_vdd_positions.append(vdd_offset) + self.add_label(text="vdd", + layer="metal1", + offset=vdd_offset) + + gnd_offset = base + self.pre2_4.gnd_position.scale(mod_dir) + self.pre_decoder_gnd_positions.append(gnd_offset) + self.add_label(text="gnd", + layer="metal1", + offset=gnd_offset) + + def add_lables_pre2x4(self,i): + pre2_4_base = i * self.pre2_4.height + # ADDING LABELS FOR INPUT SIDE OF THE 2:4 PRE-DECODER + if (self.num_inputs == 2): + xoffset = self.pre2_4.x_off_inv_1 + else: + xoffset = self.pre2_4.width - self.pre2_4.x_off_inv_1 + for inv_2x4 in range(2): + if (inv_2x4 % 2 == 0): + pin_y = self.inv.A_position.y + else: + pin_y = (self.inv.height - self.inv.A_position.y + - drc["metal1_to_metal1"]) + yoffset = pre2_4_base + inv_2x4 * self.inv.height + pin_y + self.add_label(text="A[{0}]".format(inv_2x4 + i * 2), + layer="metal1", + offset=[xoffset, yoffset]) + self.A_positions.append(vector(xoffset, yoffset)) + + # ADDING LABELS FOR OUTPUT SIDE OF THE 2:4 PRE-DECODER + for inv_2x4 in range(4): + if (self.num_inputs == 2): + xoffset = self.pre2_4.x_off_inv_2 + self.inv.Z_position.x + else: + xoffset = 0 + if (inv_2x4 % 2 == 0): + pin_y = self.inv.Z_position.y + else: + pin_y = self.inv.height - self.inv.Z_position.y + yoffset = pre2_4_base + inv_2x4 * self.inv.height + pin_y + self.add_label(text="out[{0}]".format(inv_2x4 + i * 4), + layer="metal1", + offset=[xoffset, yoffset]) + + def add_pre3x8(self,j): + if (self.num_inputs == 3): + offset = vector(0,0) + mirror ="R0" + mod_dir = vector(1,1) + index_off1 = index_off2 = 0 + else: + offset = vector(self.pre3_8.width, + self.no_of_pre2x4 * self.pre2_4.height + + j * self.pre3_8.height) + mirror="MY" + mod_dir = vector(-1,1) + index_off1 = j * 3 + self.no_of_pre2x4 * 2 + index_off2 = j * 8 + self.no_of_pre2x4 * 4 + + pins = [] + for input_index in range(3): + pins.append("A[{0}]".format(input_index + index_off1)) + for output_index in range(8): + pins.append("out[{0}]".format(output_index + index_off2)) + pins = pins + ["vdd", "gnd"] + + self.add_inst(name="pre3x8[{0}]".format(j), + mod=self.pre3_8, + offset=offset, + mirror=mirror) + self.connect_inst(pins) + + vdd_offset = offset + self.pre3_8.vdd_position.scale(mod_dir) + self.pre_decoder_vdd_positions.append(vdd_offset) + self.add_label(text="vdd", + layer="metal1", + offset=vdd_offset) + + gnd_offset = offset + self.pre3_8.gnd_position.scale(mod_dir) + self.pre_decoder_gnd_positions.append(gnd_offset) + self.add_label(text="gnd", + layer="metal1", + offset=gnd_offset) + return offset.y + + def add_lables_pre3x8(self,j,pre3x8_yoffset): + # ADDING LABELS FOR INPUT SIDE OF THE 3:8 PRE-DECODER + if (self.num_inputs == 3): + xoffset = self.pre3_8.x_off_inv_1 + else: + xoffset = self.pre3_8.width - self.pre3_8.x_off_inv_1 + for inv_3x8 in range(3): + if (inv_3x8 % 2 == 0): + pin_y = self.inv.A_position.y + else: + pin_y = (self.inv.height - self.inv.Z_position.y + -drc["minwidth_metal1"]) + yoffset = pre3x8_yoffset + inv_3x8 * (self.inv.height) + pin_y + A_index = self.no_of_pre2x4 * 2 + inv_3x8 + j * 3 + self.add_label(text="A[{0}]".format(A_index), + layer="metal1", + offset=[xoffset, yoffset]) + self.A_positions.append(vector(xoffset, yoffset)) + + # ADDING LABELS FOR OUTPUT SIDE OF THE 3:8 PRE-DECODER + for inv_3x8 in range(8): + if (self.num_inputs == 3): + xoffset = self.pre3_8.x_off_inv_2 + self.inv.Z_position[0] + else: + xoffset = 0 + + if (inv_3x8 % 2 == 0): + pin_y = self.inv.Z_position.y + else: + pin_y = self.inv.height - self.inv.Z_position.y + yoffset = pre3x8_yoffset + inv_3x8 * self.inv.height + pin_y + out_index = self.no_of_pre2x4 * 4 + inv_3x8 + j * 8 + self.add_label(text="out[{0}]".format(out_index), + layer="metal1", + offset=[xoffset, yoffset]) + + def create_row_decoder(self): + # Create the row-decoder using NAND2/NAND3 and Inverter and places the + # output labels [out/decode_out] + if (self.num_inputs >= 4): + self.add_decoder_nand_array_and_labels() + self.add_decoder_inv_array_and_labels() + + def add_decoder_nand_array_and_labels(self): + # Row Decoder NAND GATE array for address inputs <5. + if (self.num_inputs == 4 or self.num_inputs == 5): + nand = self.nand2 + correct = 0 + nand_name ="NAND2" + self.add_nand_array(nand,correct,nand_name) + # FIXME: Can we convert this to the connect_inst with checks? + for i in range(self.group_sizes[0]): + for j in range(self.group_sizes[1]): + pins =["out[{0}]".format(i), + "out[{0}]".format(j + self.group_sizes[0]), + "Z[{0}]".format(self.group_sizes[1] * i + j), + "vdd", "gnd"] + self.connect_inst(args=pins, check=False) + + # Row Decoder NAND GATE array for address inputs >5. + elif (self.num_inputs > 5): + nand = self.nand3 + correct = drc["minwidth_metal1"] + nand_name ="NAND3" + self.add_nand_array(nand,correct,nand_name) + # This will not check that the inst connections match. + for i in range(self.group_sizes[0]): + for j in range(self.group_sizes[1]): + for k in range(self.group_sizes[2]): + Z_index = (self.group_sizes[1] * self.group_sizes[2]* i + + self.group_sizes[2] * j + k) + pins = ["out[{0}]".format(i), + "out[{0}]".format(j + self.group_sizes[0]), + "out[{0}]".format(k + self.group_sizes[0] + self.group_sizes[1]), + "Z[{0}]".format(Z_index), + "vdd", "gnd"] + self.connect_inst(args=pins, check=False) + + def add_nand_array(self,nand,correct,nand_name): + for row in range(self.rows): + name = nand_name+"_[{0}]".format(row) + if ((row % 2) == 0): + y_off = self.predecoder_height + (nand.height) * (row) + y_dir = 1 + mirror = "R0" + else: + y_off = self.predecoder_height + (nand.height) * (row + 1) + y_dir = - 1 + mirror = "MX" + + self.add_inst(name=name, + mod=nand, + offset=[0, y_off], + mirror=mirror) + self.add_rect(layer="metal1", + offset=[nand.width - correct, + y_off + y_dir * (nand.Z_position[1]-correct)], + width=drc["minwidth_metal1"], + height=y_dir * drc["minwidth_metal1"]) + + def add_decoder_inv_array_and_labels(self): + # Row Decoder INVERTER array insts. + if (self.num_inputs == 4 or self.num_inputs == 5): + x_off = self.nand2.width + else: + x_off = self.nand3.width + for row in range(self.rows): + name = "INVERTER_[{0}]".format(row) + if ((row % 2) == 0): + inv_row_height = self.inv.height * row + mirror = "R0" + else: + inv_row_height = self.inv.height * (row + 1) + mirror = "MX" + y_off = self.predecoder_height + inv_row_height + + self.add_inst(name=name, + mod=self.inv, + offset=[x_off, y_off], + mirror=mirror) + # This will not check that the inst connections match. + self.connect_inst(args=["Z[{0}]".format(row), + "decode_out[{0}]".format(row), + "vdd", "gnd"], + check=False) + + # add vdd and gnd label + for row in range(self.rows): + if ((row % 2) == 0): + offset = vector(0, self.predecoder_height + row*(self.inv.height)) + vdd_offset = offset + self.inv.vdd_position.scale(0,1) + gnd_offset = offset + self.inv.gnd_position.scale(0,1) + else: + offset = vector(0, self.predecoder_height + (row+1)*(self.inv.height)) + vdd_offset = offset + self.inv.vdd_position.scale(0, -1) + gnd_offset = offset + self.inv.gnd_position.scale(0, -1) + self.vdd_positions.append(vdd_offset) + self.add_label(text="vdd", + layer="metal1", + offset=vdd_offset) + self.gnd_positions.append(gnd_offset) + self.add_label(text="gnd", + layer="metal1", + offset=gnd_offset) + + # add output label for Row Decoder INVERTER array. + if (self.num_inputs == 4 or self.num_inputs == 5): + x_off = self.nand2.width + self.inv.Z_position[0] + else: + x_off = self.nand3.width + self.inv.Z_position[0] + + for row in range(self.rows): + if ((row % 2) == 0): + pin_y = row * self.inv.height + self.inv.Z_position.y + else: + pin_y = (row+1)*self.inv.height - self.inv.Z_position.y + y_off = self.predecoder_height + pin_y + + self.add_label(text="decode_out[{0}]".format(row), + layer="metal1", + offset=[x_off, y_off]) + self.decode_out_positions.append(vector(x_off, y_off)) + + def create_vertical_rail(self): + # VERTICAL METAL RAILS TO CONNECT PREDECODER TO DECODE STAGE + if (self.num_inputs >= 4): + # Array for saving the X offsets of the vertical rails. These rail + # offsets are accessed with indices. + vertical_rail_x_offsets = [] + for i in range(self.total_number_of_predecoder_outputs): + vertical_rail_x_offsets.append(-self.gap_between_rail_offset \ + * (self.total_number_of_predecoder_outputs - i)) + self.add_rect(layer="metal2", + offset=[-self.gap_between_rail_offset * (i + 1), + 0], + width=drc["minwidth_metal2"], + height=self.height) + + # Horizontal metal extensions from pre-decoder 2x4ouput. + for i in range(self.no_of_pre2x4): + self.extend_horizontal_to_pre2x4(i,vertical_rail_x_offsets) + + # Horizontal metal extensions from pre-decoder 3x8 ouput. + for i in range(self.no_of_pre3x8): + self.extend_horizontal_to_pre3x8(i,vertical_rail_x_offsets) + + self.connect_vertial_rails_to_decoder(vertical_rail_x_offsets) + + def extend_horizontal_to_pre2x4(self, output_index, vertical_rail_x_offsets): + for inv_2x4 in range(4): + line_index = output_index * 4 + inv_2x4 + current_inv_height = (output_index * self.pre2_4.height + + inv_2x4 * (self.inv.height)) + + if (inv_2x4 % 2 == 0): + pin_y = self.inv.Z_position[1] + else: + pin_y = (self.inv.height - drc["minwidth_metal1"] + - self.inv.Z_position[1]) + yoffset = current_inv_height + pin_y + + self.add_extend_rails(yoffset = yoffset, + xoffset = vertical_rail_x_offsets[line_index]) + + def extend_horizontal_to_pre3x8(self, output_index, vertical_rail_x_offsets): + for inv_3x8 in range(8): + line_index = output_index * 8 + inv_3x8 + self.no_of_pre2x4 * 4 + current_inv_height = output_index * (self.pre3_8.height) \ + + inv_3x8 * (self.inv.height) \ + + self.no_of_pre2x4 * self.pre2_4.height + + if (inv_3x8 % 2 == 0): + pin_y = self.inv.Z_position[1] + else: + pin_y = (self.inv.height - drc["minwidth_metal1"] + - self.inv.Z_position[1]) + yoffset = current_inv_height + pin_y + + self.add_extend_rails(yoffset = yoffset, + xoffset = vertical_rail_x_offsets[line_index]) + + def connect_vertial_rails_to_decoder(self, vertical_rail_x_offsets): + # METAL CONNECTION FROM THE VERTICAL RAIL TOWARDS THE DECODER. + # PRE-DECODER OUTPUT ARE CONNECTED TO THIS SAME RAIL ALSO + # To makes these connections groups of line index that was stored in + # self.predecoder_output_groups are used + # Inputs of NAND2/NAND3 gates come from diffrent groups. + # For example for these groups [ [0,1,2,3] ,[4,5,6,7], + # [8,9,10,11,12,13,14,15] ] the first NAND3 inputs are connected to + # [0,4,8] and second NAND3 is connected to [0,4,9] ........... and the + # 128th NAND3 is connected to [3,7,15] + row_index = 0 + if (self.num_inputs == 4 or self.num_inputs == 5): + for line_index_A in self.predecoder_output_groups[0]: + for line_index_B in self.predecoder_output_groups[1]: + + current_inv_height = self.predecoder_height + row_index * (self.inv.height) + if (row_index % 2 == 0): + yoffset_A = current_inv_height + self.nand2.A_position[1] + yoffset_B = current_inv_height + self.nand2.B_position[1] + + else: + base = current_inv_height + self.inv.height - drc["minwidth_metal1"] + yoffset_A = base - self.nand2.A_position[1] + yoffset_B = base - self.nand2.B_position[1] + + row_index = row_index + 1 + self.add_extend_rails(yoffset =yoffset_A, + xoffset =vertical_rail_x_offsets[line_index_A]) + self.add_extend_rails(yoffset =yoffset_B, + xoffset =vertical_rail_x_offsets[line_index_B]) + + elif (self.num_inputs > 5): + for line_index_A in self.predecoder_output_groups[0]: + for line_index_B in self.predecoder_output_groups[1]: + for line_index_C in self.predecoder_output_groups[2]: + + current_inv_height = self.predecoder_height + row_index * (self.inv.height) + + if (row_index % 2 == 0): + yoffset_A = current_inv_height + self.nand3.A_position[1] + yoffset_B = current_inv_height + self.nand3.B_position[1] + yoffset_C = current_inv_height + self.nand3.C_position[1] + contact_C_yoffset = yoffset_C - self.contact_shift + else: + base = current_inv_height + self.inv.height - drc["minwidth_metal1"] + yoffset_A = base - self.nand3.A_position[1] + yoffset_B = base - self.nand3.B_position[1] + yoffset_C = base - self.nand3.C_position[1] + contact_C_yoffset = yoffset_C + + row_index = row_index + 1 + + self.add_extend_rails(yoffset =yoffset_A, + xoffset =vertical_rail_x_offsets[line_index_A]) + self.add_extend_rails(yoffset =yoffset_B, + xoffset =vertical_rail_x_offsets[line_index_B]) + self.add_extend_rails(yoffset =yoffset_C, + xoffset =vertical_rail_x_offsets[line_index_C], + contact_yoffset = contact_C_yoffset) + + def add_extend_rails(self, yoffset, xoffset, contact_yoffset=0): + self.add_rect(layer="metal1", + offset=[xoffset, yoffset], + width=-xoffset, + height=drc["minwidth_metal1"]) + + if contact_yoffset!=0: + yoffset = contact_yoffset + + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[xoffset + self.gap_between_rails, + yoffset - self.via_shift], + rotate=90) diff --git a/compiler/hierarchical_predecode.py b/compiler/hierarchical_predecode.py new file mode 100644 index 00000000..65a7fd2d --- /dev/null +++ b/compiler/hierarchical_predecode.py @@ -0,0 +1,196 @@ +import debug +import design +import math +from tech import drc, cell +from contact import contact +from nand_2 import nand_2 +from nand_3 import nand_3 +from pinv import pinv +from vector import vector +from globals import OPTS + + +class hierarchical_predecode(design.design): + """ + Pre 2x4 and 3x8 decoder shared code. + """ + def __init__(self, nmos_width, cellname, input_number): + design.design.__init__(self, cellname) + + c = reload(__import__(OPTS.config.bitcell)) + self.mod_bitcell = getattr(c, OPTS.config.bitcell) + self.bitcell_height = self.mod_bitcell.chars["height"] + + self.nmos_width = nmos_width + self.number_of_inputs = input_number + self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) + + def add_pins(self): + for k in range(self.number_of_inputs): + self.add_pin("A[{0}]".format(k)) + for i in range(self.number_of_outputs): + self.add_pin("out[{0}]".format(i)) + self.add_pin("vdd") + self.add_pin("gnd") + + def create_modules(self): + layer_stack = ("metal1", "via1", "metal2") + self.m1m2_via = contact(layer_stack=layer_stack) + self.inv = pinv(name="a_inv_1", + nmos_width=drc["minwidth_tx"], + beta=2, + height=self.bitcell_height) + self.add_mod(self.inv) + if self.number_of_inputs ==2: + self.nand = nand_2(name="a_nand_2", + nmos_width=self.nmos_width, + height=self.bitcell_height) + elif self.number_of_inputs ==3: + self.nand = nand_3(name="a_nand_3", + nmos_width=self.nmos_width, + height=self.bitcell_height) + self.add_mod(self.nand) + + def set_up_constrain(self): + self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2 + self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2 + self.via_shift = (self.m1m2_via.second_layer_width + - self.m1m2_via.first_layer_width) / 2 + self.metal2_extend_contact = (self.m1m2_via.second_layer_height + - self.m1m2_via.contact_width) / 2 + + self.gap_between_rails = (self.metal2_extend_contact + + drc["metal2_to_metal2"]) + self.gap_between_rail_offset = (self.gap_between_rails + + drc["minwidth_metal2"]) + + def setup_constrains(self): + self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2 + self.metal2_extend_contact = (self.m1m2_via.second_layer_height - self.m1m2_via.contact_width) / 2 + + # Contact shift used connecting NAND3 inputs to the rail + self.contact_shift = (self.m1m2_via.first_layer_width - self.m1m2_via.contact_width) / 2 + + self.gap_between_rails = self.metal2_extend_contact + drc["metal2_to_metal2"] + self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"] + + self.rails_x_offset = [] + if self.number_of_inputs == 2: + self.rail_height = (self.number_of_outputs * self.nand.height + - (self.number_of_outputs - 1) * drc["minwidth_metal2"]) + elif self.number_of_inputs == 3: + self.rail_height = (self.number_of_outputs * self.nand.height + - 1.5 * drc["minwidth_metal2"]) + # Creating the left hand side metal2 rails for input connections + for hrail_1 in range(self.number_of_inputs): + xoffset_1 = (self.metal2_extend_contact + + (hrail_1 * self.gap_between_rail_offset)) + self.rails_x_offset.append(xoffset_1) + # x offset for Xpre2x4_inv + self.x_off_inv_1 = self.rails_x_offset[-1] + self.gap_between_rail_offset + # Creating the right hand side metal2 rails for output connections + for hrail_2 in range(2 * self.number_of_inputs + 2): + xoffset_2 = self.x_off_inv_1 + self.inv.width + self.gap_between_rails + (hrail_2 * self.gap_between_rail_offset) + self.rails_x_offset.append(xoffset_2) + self.xoffset_2=self.rails_x_offset[-1] + + self.x_off_nand = self.xoffset_2 + self.gap_between_rail_offset + self.x_off_inv_2 = self.x_off_nand + self.nand.width + self.update_size() + + def update_size(self): + self.width = self.x_off_inv_2 + self.inv.width + if self.number_of_inputs ==2: + self.height = 4 * self.nand.height + elif self.number_of_inputs ==3: + self.height = 8 * self.nand.height + self.size = vector(self.width, self.height) + correct =vector(0, 0.5 * drc["minwidth_metal1"]) + self.vdd_position = self.size - correct - vector(0, self.inv.height) + self.gnd_position = self.size - correct + + def create_rails(self): + for x_off in self.rails_x_offset: + self.add_rect(layer="metal2", + offset=[x_off, 0], + width=drc["minwidth_metal2"], + height=self.rail_height) + + def add_output_inverters(self): + self.decode_out_positions = [] + for inv_2x4 in range(self.number_of_outputs): + name = "Xpre2x4_nand_inv[{0}]".format(inv_2x4) + if (inv_2x4 % 2 == 0): + y_factor = inv_2x4 + mirror = "R0" + correct = self.inv.Z_position + else: + y_factor =inv_2x4 + 1 + mirror = "MX" + correct = self.inv.Z_position.scale(1,-1) - vector(0, + drc["minwidth_metal1"]) + base = vector(self.x_off_inv_2, self.inv.height * y_factor) + self.add_inst(name=name, + mod=self.inv, + offset=base, + mirror=mirror) + self.connect_inst(["Z[{0}]".format(inv_2x4), + "out[{0}]".format(inv_2x4), + "vdd", "gnd"]) + output_inv_out_offset = base + correct + self.decode_out_positions.append(output_inv_out_offset) + + def add_nand(self,connections): + for nand_input in range(self.number_of_outputs): + if self.number_of_inputs ==2: + name = "Xpre2x4_nand[{0}]".format(nand_input) + elif self.number_of_inputs ==3: + name = "Xpre3x8_nand[{0}]".format(nand_input) + if (nand_input % 2 == 0): + y_off = nand_input * (self.nand.height) + mirror = "R0" + offset = [self.x_off_nand + self.nand.width, + y_off + self.nand.Z_position[1]] + else: + y_off = (nand_input + 1) * (self.nand.height) + mirror = "MX" + offset =[self.x_off_nand + self.nand.width, + y_off - self.nand.Z_position[1] - drc["minwidth_metal1"]] + self.add_inst(name=name, + mod=self.nand, + offset=[self.x_off_nand, y_off], + mirror=mirror) + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=drc["minwidth_metal1"]) + self.connect_inst(connections[nand_input]) + + def route(self): + # route sub funtions need to be redfined in sub class + self.route_input_inverters() + self.route_nand_to_rails() + self.route_vdd_gnd_from_rails_to_gates() + + def route_input_inverters_input(self,inv_rout,inv_in_offset): + self.add_rect(layer="metal1", + offset=[self.rails_x_offset[inv_rout], + inv_in_offset[1]], + width=inv_in_offset[0] - self.rails_x_offset[inv_rout] + drc["minwidth_metal2"], + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.rails_x_offset[inv_rout] + self.gap_between_rails, + inv_in_offset[1] - self.via_shift], + rotate=90) + + def route_input_inverters_vdd(self,inv_vdd_offset): + self.add_rect(layer="metal1", + offset=inv_vdd_offset, + width=self.rails_x_offset[self.number_of_inputs] - inv_vdd_offset[0] + drc["minwidth_metal2"], + height=drc["minwidth_metal1"]) + + def route_input_inverters_gnd(self,inv_gnd_offset): + self.add_rect(layer="metal1", + offset=inv_gnd_offset, + width=self.rails_x_offset[self.number_of_inputs+1] - inv_gnd_offset[0] + drc["minwidth_metal2"], + height=drc["minwidth_metal1"]) diff --git a/compiler/hierarchical_predecode2x4.py b/compiler/hierarchical_predecode2x4.py new file mode 100644 index 00000000..33065db8 --- /dev/null +++ b/compiler/hierarchical_predecode2x4.py @@ -0,0 +1,159 @@ +from tech import drc +import debug +import design +from vector import vector +from hierarchical_predecode import hierarchical_predecode + +class hierarchical_predecode2x4(hierarchical_predecode): + """ + Pre 2x4 decoder used in hierarchical_decoder. + """ + + def __init__(self, nmos_width, cellname): + hierarchical_predecode.__init__(self, nmos_width, cellname, 2) + + self.add_pins() + self.create_modules() + self.setup_constrains() + self.create_layout() + self.route() + + def create_layout(self): + self.create_rails() + self.add_inv2x4() + self.add_output_inverters() + connections =[["A[0]", "A[1]", "Z[3]", "vdd", "gnd"], + ["B[0]", "A[1]", "Z[2]", "vdd", "gnd"], + ["A[0]", "B[1]", "Z[1]", "vdd", "gnd"], + ["B[0]", "B[1]", "Z[0]", "vdd", "gnd"]] + self.add_nand(connections) + + def add_inv2x4(self): + self.A_positions = [] + for inv_2x4 in range(self.number_of_inputs): + name = "Xpre2x4_inv[{0}]".format(inv_2x4) + if (inv_2x4 % 2 == 0): + y_off = inv_2x4 * (self.inv.height) + offset = vector(self.x_off_inv_1, y_off) + mirror = "R0" + A_off = self.inv.A_position.scale(0, 1) + else: + y_off = (inv_2x4 + 1) * (self.inv.height) + offset = vector(self.x_off_inv_1, y_off) + mirror="MX" + A_off = vector(0, - self.inv.A_position.y - drc["minwidth_metal1"]) + self.A_positions.append(offset + A_off) + self.add_inst(name=name, + mod=self.inv, + offset=offset, + mirror=mirror) + self.connect_inst(["A[{0}]".format(inv_2x4), + "B[{0}]".format(inv_2x4), + "vdd", "gnd"]) + + def route_input_inverters(self): + # All conections of the inputs inverters [Inputs, outputs, vdd, gnd] + output_shift = 2 * drc["minwidth_metal1"] + for inv_rout in range(self.number_of_inputs): + if (inv_rout % 2 == 0): + y_dir= 1 + else: + y_dir= -1 + base = vector(self.x_off_inv_1, + (1-y_dir) * (self.inv.height - 0.5 * drc["minwidth_metal1"])) + inv_out_offset = base + self.inv.Z_position.scale(1,y_dir) + inv_in_offset = base + self.inv.A_position.scale(1,y_dir) + inv_vdd_offset = base + self.inv.vdd_position.scale(1,y_dir) + inv_gnd_offset = base + self.inv.gnd_position.scale(1,y_dir) + out_y_mirrored = inv_vdd_offset[1] + output_shift + drc["minwidth_metal1"] + out_offset = [inv_out_offset[0], + inv_out_offset[1] * (1 + y_dir) / 2 + + out_y_mirrored * (1 - y_dir) / 2] + # output connection + correct = y_dir * (output_shift + drc["minwidth_metal1"]) + off_via = [self.rails_x_offset[inv_rout + 4] + self.gap_between_rails, + inv_vdd_offset[1] - self.via_shift - correct] + self.add_rect(layer="metal1", + offset=out_offset, + width=drc["minwidth_metal1"], + height=(inv_vdd_offset[1] - inv_out_offset[1]) * y_dir - output_shift) + self.add_rect(layer="metal1", + offset=[inv_out_offset[0], + inv_vdd_offset[1] - correct], + width=self.rails_x_offset[inv_rout + 4] - inv_out_offset[0] + drc["minwidth_metal2"], + height=drc["minwidth_metal1"]) + self.add_via(layers = ("metal1", "via1", "metal2"), + offset=off_via, + rotate=90) + self.route_input_inverters_input(inv_rout,inv_in_offset) + self.route_input_inverters_vdd(inv_vdd_offset) + self.route_input_inverters_gnd(inv_gnd_offset) + + def route_nand_to_rails(self): + # This 2D array defines the connection mapping + nand2_input_line_combination = [[4, 5], [6, 5], [4, 7], [6, 7]] + for k in range(self.number_of_outputs): + # create x offset list + x_index = nand2_input_line_combination[k] + line_x_offset = [self.rails_x_offset[x_index[0]], + self.rails_x_offset[x_index[1]]] + # create y offset list + if (k % 2 == 0): + y_off = k * (self.nand.height) + direct = 1 + else: + y_off = (k + 1) * (self.nand.height) - drc["minwidth_metal1"] + direct = - 1 + list_connect = [y_off + direct * self.nand.A_position.y, + y_off + direct * self.nand.B_position.y] + # connect based on the two list + for connect in list_connect: + x_offset = line_x_offset[list_connect.index(connect)] + self.add_rect(layer="metal1", + offset=[x_offset, connect], + width=self.x_off_nand - x_offset, + height=drc["minwidth_metal1"]) + self.add_via(layers = ("metal1", "via1", "metal2"), + offset=[x_offset + self.gap_between_rails, + connect - self.via_shift], + rotate=90) + # Extended of the top NAND2 to the left hand side input rails + if(k == self.number_of_outputs - 1): + x_offset = self.rails_x_offset[list_connect.index(connect)] + self.add_rect(layer="metal1", + offset=[x_offset, connect], + width=self.x_off_nand - x_offset, + height=drc["minwidth_metal1"]) + self.add_via(layers = ("metal1", "via1", "metal2"), + offset=[x_offset + self.gap_between_rails, + connect - self.via_shift], + rotate=90) + + def route_vdd_gnd_from_rails_to_gates(self): + for k in range(self.number_of_outputs): + power_line_index = 3 - (k%2) + yoffset = k * self.inv.height - 0.5 * drc["minwidth_metal1"] + self.add_rect(layer="metal1", + offset=[self.rails_x_offset[power_line_index], + yoffset], + width=self.x_off_nand - self.rails_x_offset[power_line_index], + height=drc["minwidth_metal1"]) + self.add_via(layers = ("metal1", "via1", "metal2"), + offset=[self.rails_x_offset[power_line_index] + self.gap_between_rails, + yoffset - self.via_shift], + rotate=90) + + yoffset = (self.number_of_outputs * self.inv.height + - 0.5 * drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=[self.rails_x_offset[3], yoffset], + width=self.x_off_nand - self.rails_x_offset[3], + height=drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=[self.rails_x_offset[3], self.rail_height], + width=drc["minwidth_metal1"], + height=yoffset - self.rail_height) + self.add_via(layers = ("metal1", "via1", "metal2"), + offset=[self.rails_x_offset[3] + self.gap_between_rails, + self.rail_height - self.via_shift], + rotate=90) diff --git a/compiler/hierarchical_predecode3x8.py b/compiler/hierarchical_predecode3x8.py new file mode 100644 index 00000000..4d6f4c0d --- /dev/null +++ b/compiler/hierarchical_predecode3x8.py @@ -0,0 +1,141 @@ +from tech import drc +import debug +import design +from vector import vector +from hierarchical_predecode import hierarchical_predecode + + +class hierarchical_predecode3x8(hierarchical_predecode): + """ + Pre 3x8 decoder used in hierarchical_decoder. + """ + def __init__(self, nmos_width, cellname): + hierarchical_predecode.__init__(self, nmos_width, cellname, 3) + + self.add_pins() + self.create_modules() + self.setup_constrains() + self.create_layout() + self.route() + + def create_layout(self): + self.create_rails() + self.add_output_inverters() + connections=[["A[0]", "A[1]", "A[2]", "Z[7]", "vdd", "gnd"], + ["A[0]", "A[1]", "B[2]", "Z[6]", "vdd", "gnd"], + ["A[0]", "B[1]", "A[2]", "Z[5]", "vdd", "gnd"], + ["A[0]", "B[1]", "B[2]", "Z[4]", "vdd", "gnd"], + ["B[0]", "A[1]", "A[2]", "Z[3]", "vdd", "gnd"], + ["B[0]", "A[1]", "B[2]", "Z[2]", "vdd", "gnd"], + ["B[0]", "B[1]", "A[2]", "Z[1]", "vdd", "gnd"], + ["B[0]", "B[1]", "B[2]", "Z[0]", "vdd", "gnd"]] + self.add_nand(connections) + + def route_input_inverters(self): + # All conections of the inputs inverters [Inputs, outputs, vdd, gnd] + for inv_rout in range(self.number_of_inputs): + output_shift = 1.5 * drc["minwidth_metal1"] + + if (inv_rout % 2 == 0): + base_offset=[self.x_off_inv_1, inv_rout * self.inv.height ] + y_dir = 1 + else: + base_offset=[self.x_off_inv_1, 2 * self.inv.height - drc["minwidth_metal1"]] + y_dir = -1 + inv_out_offset = base_offset+self.inv.Z_position.scale(1,y_dir) + inv_in_offset = base_offset+self.inv.A_position.scale(1,y_dir) + inv_vdd_offset = base_offset+self.inv.vdd_position.scale(1,y_dir) + inv_gnd_offset = base_offset+self.inv.gnd_position.scale(1,y_dir) + # output connection + correct = y_dir * (output_shift + drc["minwidth_metal1"]) + off_via = [self.rails_x_offset[inv_rout + 5] + self.gap_between_rails, + inv_vdd_offset[1] - self.via_shift - correct] + path1 = vector(inv_out_offset[0] + 0.5*drc["minwidth_metal1"], + inv_out_offset[1]- 1.5*drc["minwidth_metal1"] - correct) + path2 = vector(path1.x, + inv_vdd_offset[1] + 0.5 * drc["minwidth_metal1"] - correct) + path3 = vector(self.rails_x_offset[inv_rout + 5] + drc["minwidth_metal2"], + path2.y) + self.add_path("metal1", [path1,path2,path3]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=off_via, + rotate=90) + self.route_input_inverters_input(inv_rout,inv_in_offset) + self.route_input_inverters_vdd(inv_vdd_offset) + self.route_input_inverters_gnd(inv_gnd_offset) + + def route_nand_to_rails(self): + # This 2D array defines the connection mapping of the Nand3 gates to + # the rail + nand3_input_line_combination = [[5, 6, 7], [5, 6, 10], + [5, 9, 7], [5, 9, 10], + [8, 6, 7], [8, 6, 10], + [8, 9, 7], [8, 9, 10]] + for k in range(self.number_of_outputs): + index_lst = nand3_input_line_combination[k] + line_x_offset = [] + for index in index_lst: + line_x_offset.append(self.rails_x_offset[index]) + + if (k % 2 == 0): + y_off = k * (self.nand.height) + y_dir =1 + correct = [0,0,self.contact_shift] + else: + y_off = 2 * self.inv.height - drc["minwidth_metal1"] + (k - 1) * (self.nand.height) + y_dir = -1 + correct = [0,self.contact_shift,0] + yoffset_nand_in = [y_off + y_dir*self.nand.A_position[1], + y_off + y_dir*self.nand.B_position[1], + y_off + y_dir*self.nand.C_position[1]] + + for i in range(self.number_of_inputs): + # Connecting the i-th input of Nand3 gate + self.add_rect(layer="metal1", + offset=[line_x_offset[i], yoffset_nand_in[i]], + width=self.x_off_nand - line_x_offset[i], + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[line_x_offset[i]+ self.gap_between_rails, + yoffset_nand_in[i] - self.via_shift - correct[i]], + rotate=90) + #Extended of the top NAND2 to the left hand side input rails + if(k == self.number_of_outputs - 1): + for i in range(self.number_of_inputs): + self.add_rect(layer="metal1", + offset=[self.rails_x_offset[i], yoffset_nand_in[i]], + width=self.x_off_nand - self.rails_x_offset[i], + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.rails_x_offset[i] + self.gap_between_rails, + yoffset_nand_in[i] - self.via_shift], + rotate=90) + + def route_vdd_gnd_from_rails_to_gates(self): + for k in range(self.number_of_outputs): + power_line_index = 4 - (k%2) + yoffset = k * self.inv.height - 0.5 * drc["minwidth_metal1"] + self.add_rect(layer="metal1", + offset=[self.rails_x_offset[power_line_index], yoffset], + width=self.x_off_nand - self.rails_x_offset[power_line_index], + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.rails_x_offset[power_line_index] + self.gap_between_rails, + yoffset - self.via_shift - self.contact_shift], + rotate=90) + + yoffset = (self.number_of_outputs * self.inv.height + - 0.5 * drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=[self.rails_x_offset[4], yoffset], + width=self.x_off_nand - self.rails_x_offset[4], + height=drc["minwidth_metal1"]) + + self.add_rect(layer="metal2", + offset=[self.rails_x_offset[4], self.rail_height], + width=drc["minwidth_metal2"], + height=yoffset - self.rail_height) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.rails_x_offset[4] + self.gap_between_rails - self.via_shift, + yoffset - self.via_shift - self.contact_shift], + rotate=90) diff --git a/compiler/hierarchy_layout.py b/compiler/hierarchy_layout.py new file mode 100644 index 00000000..420df8cd --- /dev/null +++ b/compiler/hierarchy_layout.py @@ -0,0 +1,340 @@ +import itertools +import geometry +import gdsMill +import debug +from tech import drc, GDS +from tech import layer as techlayer +import os +from utils import snap_to_grid +from vector import vector + +class layout: + """ + Class consisting of a set of objs and instances for a module + This provides a set of useful generic types for hierarchy + management. If a module is a custom designed cell, it will read from + the GDS and spice files and perform LVS/DRC. If it is dynamically + generated, it should implement a constructor to create the + layout/netlist and perform LVS/DRC. + """ + + def __init__(self, name): + self.name = name + self.width = None + self.height = None + self.insts = [] # Holds module/cell layout instances + self.objs = [] # Holds all other objects (labels, geometries, etc) + + self.visited = False # Flag for traversing the hierarchy + + self.gds_read() + + ############################################################ + # GDS layout + ############################################################ + def offset_all_coordinates(self): + """ This function is called after everything is placed to + shift the origin in the lowest left corner """ + coordinate = self.find_lowest_coords() + self.offset_attributes(coordinate) + self.translate(coordinate) + + + + def find_lowest_coords(self): + """Finds the lowest set of 2d cartesian coordinates within + this layout""" + #***1,000,000 number is used to avoid empty sequences errors*** + # FIXME Is this hard coded value ok?? + try: + lowestx1 = min(rect.offset[0] for rect in self.objs) + lowesty1 = min(rect.offset[1] for rect in self.objs) + except: + [lowestx1, lowesty1] = [1000000.0, 1000000.0] + try: + lowestx2 = min(inst.offset[0] for inst in self.insts) + lowesty2 = min(inst.offset[1] for inst in self.insts) + except: + [lowestx2, lowesty2] = [1000000.0, 1000000.0] + return vector(min(lowestx1, lowestx2), min(lowesty1, lowesty2)) + + def offset_attributes(self, coordinate): + """Translates all stored 2d cartesian coordinates within the + attr dictionary""" + # FIXME: This is dangerous. I think we should not do this, but explicitly + # offset the necessary coordinates. + + #for attr_key, attr_val in self.attr.items(): + for attr_key in dir(self): + attr_val = getattr(self,attr_key) + + # skip the list of things as these will be offset separately + if (attr_key in ['objs','insts','mods','pins','conns']): continue + + # if is a list + if isinstance(attr_val, list): + + for i in range(len(attr_val)): + # each unit in the list is a list coordinates + if isinstance(attr_val[i], (list,vector)): + attr_val[i] = vector([attr_val[i][0] - coordinate.x, + attr_val[i][1] - coordinate.y]) + # the list itself is a coordinate + else: + if len(attr_val)!=2: continue + for val in attr_val: + if not isinstance(val, (int, long, float)): continue + setattr(self,attr_key, vector([attr_val[0] - coordinate.x, + attr_val[1] - coordinate.y])) + break + + # if is a vector coordinate + if isinstance(attr_val, vector): + setattr(self, attr_key, vector(attr_val[0] - coordinate.x, + attr_val[1] - coordinate.y)) + + + + def translate(self, coordinate): + """Translates all 2d cartesian coordinates in a layout given + the (x,y) offset""" + for obj in self.objs: + obj.offset = snap_to_grid([obj.offset[0] - coordinate.x, + obj.offset[1] - coordinate.y]) + for inst in self.insts: + inst.offset = snap_to_grid([inst.offset[0] - coordinate.x, + inst.offset[1] - coordinate.y]) + + # FIXME: Make name optional and pick a random one if not specified + def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0): + """Adds an instance of a mod to this module""" + self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) + message = [] + for x in self.insts: + message.append(x.name) + debug.info(4, "adding instance" + ",".join(message)) + + def add_rect(self, layer, offset, width, height): + """Adds a rectangle on a given layer,offset with width and height""" + debug.info(3, "adding rectangle (" + str(layer) + ") :" + + str(width) + "x" + str(height) + " @ " + str(offset)) + + # negative layers indicate "unused" layers in a given technology + layerNumber = techlayer[layer] + if layerNumber >= 0: + self.objs.append(geometry.rectangle(layerNumber, offset, width, height)) + + def add_layout_pin(self, text, layer, offset, width, height): + """Create a labeled pin""" + self.add_rect(layer=layer, + offset=offset, + width=width, + height=height) + self.add_label(text=text, + layer=layer, + offset=offset) + + + def add_label(self, text, layer, offset=[0,0],zoom=1): + """Adds a text label on the given layer,offset, and zoom level""" + debug.info(3,"add label " + text + " " + str(layer) + " " + str(offset)) + # negative layers indicate "unused" layers in a given technology + layerNumber = techlayer[layer] + if layerNumber >= 0: + self.objs.append(geometry.label(text, layerNumber, offset, zoom)) + + + def add_path(self, layer, coordinates, width=None, offset=None): + """Connects a routing path on given layer,coordinates,width.""" + debug.info(3,"add path " + str(layer) + " " + str(coordinates)) + import path + # NOTE: (UNTESTED) add_path(...) is currently not used + # negative layers indicate "unused" layers in a given technology + #layerNumber = techlayer[layer] + #if layerNumber >= 0: + # self.objs.append(geometry.path(layerNumber, coordinates, width)) + + # add an instance of our path that breaks down into rectangles + if width==None: + self.layer_width = drc["minwidth_{0}".format(layer)] + else: + self.layer_width = width + # Wires/paths are created so that the first point is (0,0) + # therefore we offset the instantiation to the first point + # however, we can override this + if offset==None: + inst_offset = coordinates[0] + else: + inst_offset = offset + + route = path.path(layer=layer, + position_list=coordinates, + width=self.layer_width) + self.add_mod(route) + self.add_inst(name=route.name, + mod=route, + offset=inst_offset) + # We don't model the logical connectivity of wires/paths + self.connect_inst([]) + return route + + def add_wire(self, layers, coordinates, offset=None): + """Connects a routing path on given layer,coordinates,width. + The layers are the (vertical, via, horizontal). """ + import wire + debug.info(3,"add wire " + str(layers) + " " + str(coordinates)) + # Wires/paths are created so that the first point is (0,0) + # therefore we offset the instantiation to the first point + # however, we can override this + if offset==None: + inst_offset=coordinates[0] + else: + inst_offset=offset + # add an instance of our path that breaks down into rectangles and contacts + route = wire.wire(layer_stack=layers, + position_list=coordinates) + self.add_mod(route) + self.add_inst(name=route.name, + mod=route, + offset=inst_offset) + # We don't model the logical connectivity of wires/paths + self.connect_inst([]) + return route + + def add_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0): + """ This is just an alias for a via.""" + return self.add_via(layers=layers, + offset=offset, + size=size, + mirror=mirror,rotate=rotate) + + def add_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0): + """ Add a three layer via structure. """ + import contact + via = contact.contact(layer_stack=layers, + dimensions=size, + offset=offset) + self.add_mod(via) + self.add_inst(name=via.name, + mod=via, + offset=offset, + mirror=mirror, + rotate=rotate) + # We don't model the logical connectivity of wires/paths + self.connect_inst([]) + return via + + def add_ptx(self, name, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): + """Adds a ptx module to the design.""" + import ptx + mos = ptx.ptx(name=name, + width=width, + mults=mults, + tx_type=tx_type) + self.add_mod(mos) + self.add_inst(name=mos.name, + mod=mos, + offset=offset, + mirror=mirror, + rotate=rotate) + return mos + + + def gds_read(self): + """Reads a GDSII file in the library and checks if it exists + Otherwise, start a new layout for dynamic generation.""" + # open the gds file if it exists or else create a blank layout + if os.path.isfile(self.gds_file): + debug.info(2, "opening %s" % self.gds_file) + self.gds = gdsMill.VlsiLayout(units=GDS["unit"]) + reader = gdsMill.Gds2reader(self.gds) + reader.loadFromFile(self.gds_file) + # TODO: parse the width/height + # TODO: parse the pin locations + else: + debug.info(2, "creating structure %s" % self.name) + self.gds = gdsMill.VlsiLayout( + name=self.name, units=GDS["unit"]) + + def print_gds(self, gds_file=None): + """Print the gds file (not the vlsi class) to the terminal """ + if gds_file == None: + gds_file = self.gds_file + debug.info(3, "Printing %s" % gds_file) + arrayCellLayout = gdsMill.VlsiLayout() + reader = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal=1) + reader.loadFromFile(gds_file) + + def clear_visited(self): + """ Recursively clear the visited flag """ + if not self.visited: + for i in self.insts: + i.mod.clear_visited() + self.visited = False + + def gds_write_file(self, newLayout): + """Recursive GDS write function""" + if self.visited: + return + for i in self.insts: + i.gds_write_file(newLayout) + for i in self.objs: + i.gds_write_file(newLayout) + self.visited = True + + def gds_write(self, gds_name): + """Write the entire gds of the object to the file.""" + debug.info(3, "Writing to {0}".format(gds_name)) + + #self.gds = gdsMill.VlsiLayout(name=self.name,units=GDS["unit"]) + writer = gdsMill.Gds2writer(self.gds) + # clear the visited flag for the traversal + self.clear_visited() + # recursively create all the remaining objects + self.gds_write_file(self.gds) + # populates the xyTree data structure for gds + # self.gds.prepareForWrite() + writer.writeToFile(gds_name) + + def pdf_write(self, pdf_name): + # NOTE: Currently does not work (Needs further research) + #self.pdf_name = self.name + ".pdf" + debug.info(0, "Writing to %s" % pdf_name) + pdf = gdsMill.pdfLayout(self.gds) + + return + pdf.layerColors[self.gds.layerNumbersInUse[0]] = "#219E1C" + pdf.layerColors[self.gds.layerNumbersInUse[1]] = "#271C9E" + pdf.layerColors[self.gds.layerNumbersInUse[2]] = "#CC54C8" + pdf.layerColors[self.gds.layerNumbersInUse[3]] = "#E9C514" + pdf.layerColors[self.gds.layerNumbersInUse[4]] = "#856F00" + pdf.layerColors[self.gds.layerNumbersInUse[5]] = "#BD1444" + pdf.layerColors[self.gds.layerNumbersInUse[6]] = "#FD1444" + pdf.layerColors[self.gds.layerNumbersInUse[7]] = "#FD1414" + + pdf.setScale(500) + pdf.drawLayout() + pdf.writeToFile(pdf_name) + + def print_attr(self): + """Prints a list of attributes for the current layout object""" + debug.info(0, + "|==============================================================================|") + debug.info(0, + "|========= LIST OF OBJECTS (Rects) FOR: " + self.attr["name"]) + debug.info(0, + "|==============================================================================|") + for obj in self.objs: + debug.info(0, "layer={0} : offset={1} : size={2}".format( + obj.layerNumber, obj.offset, obj.size)) + + debug.info(0, + "|==============================================================================|") + debug.info(0, + "|========= LIST OF INSTANCES FOR: " + + self.attr["name"]) + debug.info(0, + "|==============================================================================|") + for inst in self.insts: + debug.info(0, "name={0} : mod={1} : offset={2}".format( + inst.name, inst.mod.name, inst.offset)) diff --git a/compiler/hierarchy_spice.py b/compiler/hierarchy_spice.py new file mode 100644 index 00000000..857b5ec7 --- /dev/null +++ b/compiler/hierarchy_spice.py @@ -0,0 +1,148 @@ +import debug +import re +import os + + +class spice: + """ + This provides a set of useful generic types for hierarchy + management. If a module is a custom designed cell, it will read from + the GDS and spice files and perform LVS/DRC. If it is dynamically + generated, it should implement a constructor to create the + layout/netlist and perform LVS/DRC. + Class consisting of a set of modules and instances of these modules + """ + + def __init__(self, name): + self.name = name + + self.mods = [] # Holds subckts/mods for this module + self.pins = [] # Holds the pins for this module + + # for each instance, this is the set of nets/nodes that map to the pins for this instance + # THIS MUST MATCH THE ORDER OF THE PINS (restriction imposed by the + # Spice format) + self.conns = [] + + self.sp_read() + +############################################################ +# Spice circuit +############################################################ + + def add_pin(self, name): + """Adds a pin to the pins list""" + self.pins.append(name) + + def add_pin_list(self, pin_list): + """Adds a pin_list to the pins list""" + self.pins = self.pins + pin_list + + def add_mod(self, mod): + """Adds a subckt/submodule to the subckt hierarchy""" + self.mods.append(mod) + + def connect_inst(self, args, check=True): + """Connects the pins of the last instance added + It is preferred to use the other function with the check to find if + there is a problem. The check otion can be set to false + where we dynamically generate groups of connections after a + group of modules are generated.""" + + if (check and (len(self.insts[-1].mod.pins) != len(args))): + debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins), + len(args)), 1) + self.conns.append(args) + + if check and (len(self.insts)!=len(self.conns)): + debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, + len(self.insts), + len(self.conns))) + debug.error("Instances: \n"+str(self.insts)) + debug.error("-----") + debug.error("Connections: \n"+str(self.conns),1) + + + + def sp_read(self): + """Reads the sp file (and parse the pins) from the library + Otherwise, initialize it to null for dynamic generation""" + if os.path.isfile(self.sp_file): + debug.info(2, "opening {0}".format(self.sp_file)) + f = open(self.sp_file) + self.spice = f.readlines() + for i in range(len(self.spice)): + self.spice[i] = self.spice[i].rstrip(" \n") + + # find first subckt line in the file + subckt = re.compile("^.subckt", re.IGNORECASE) + subckt_line = filter(subckt.search, self.spice)[0] + # parses line into ports and remove subckt + self.pins = subckt_line.split(" ")[2:] + else: + self.spice = [] + + def contains(self, mod, modlist): + for x in modlist: + if x.name == mod.name: + return True + return False + + def sp_write_file(self, sp, usedMODS): + """ Recursive spice subcircuit write; + Writes the spice subcircuit from the library or the dynamically generated one""" + if not self.spice: + # recursively write the modules + for i in self.mods: + if self.contains(i, usedMODS): + continue + usedMODS.append(i) + i.sp_write_file(sp, usedMODS) + + if len(self.insts) == 0: + return + if self.pins == []: + return + # write out the first spice line (the subcircuit) + sp.write("\n.SUBCKT {0} {1}\n".format(self.name, + " ".join(self.pins))) + + # every instance must have a set of connections, even if it is empty. + if len(self.insts)!=len(self.conns): + debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, + len(self.insts), + len(self.conns))) + debug.error("Instances: \n"+str(self.insts)) + debug.error("-----") + debug.error("Connections: \n"+str(self.conns),1) + + + + for i in range(len(self.insts)): + # we don't need to output connections of empty instances. + # these are wires and paths + if self.conns[i] == []: + continue + sp.write("X{0} {1} {2}\n".format(self.insts[i].name, + " ".join(self.conns[i]), + self.insts[i].mod.name)) + + sp.write(".ENDS {0}\n".format(self.name)) + + else: + # write the subcircuit itself + # Including the file path makes the unit test fail for other users. + #if os.path.isfile(self.sp_file): + # sp.write("\n* {0}\n".format(self.sp_file)) + sp.write("\n".join(self.spice)) + sp.write("\n") + + def sp_write(self, spname): + """Writes the spice to files""" + debug.info(3, "Writing to {0}".format(spname)) + spfile = open(spname, 'w') + spfile.write("*FIRST LINE IS A COMMENT\n") + usedMODS = list() + self.sp_write_file(spfile, usedMODS) + del usedMODS + spfile.close() diff --git a/compiler/lef.py b/compiler/lef.py new file mode 100644 index 00000000..e8911e54 --- /dev/null +++ b/compiler/lef.py @@ -0,0 +1,255 @@ +import gdsMill +import tech +import globals +import math +import debug +from collections import defaultdict + +class lef: + """ + SRAM LEF Class open GDS file, read pins information, obstruction + and write them to LEF file + """ + def __init__(self, gdsName, lefName, sr): + self.gdsName = gdsName + self.lef = open(lefName,"w") + self.sr = sr + self.myLayout = gdsMill.VlsiLayout(units=tech.GDS["unit"]) + self.reader = gdsMill.Gds2reader(self.myLayout) + self.reader.loadFromFile(gdsName) + self.unit = float(self.myLayout.info['units'][0]) + self.layer = ["metal1", "via1", "metal2", "via2", "metal3"] + + self.create() + + self.lef.close() + + def create(self): + """Write to LEF file""" + power_pin_name = self.powerPinName() + ground_pin_name = self.groundPinName() + input_pin_name = self.inputPinName() + inout_pin_name = self.inoutPinName() + + self.writeLefHeader() + + for pin in power_pin_name: + self.writePin(pin, 1) + + for pin in ground_pin_name: + self.writePin(pin, 2) + + for pin in inout_pin_name: + self.writePin(pin, 3) + + for pin in input_pin_name: + self.writePin(pin,4) + + self.lef.write(" OBS \n") + for lay in self.layer: + self.lef.write(" Layer {0} ; \n".format(lay)) + self.writeObstruct(self.sr.name, lay, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)) + self.lef.write(" END \n") + + self.writeLefFooter() + + def coordinatesTranslate(self, coord, mirr, angle, xyShift): + """Calculate coordinates after flip, rotate, and shift""" + coordinate = [] + for item in coord: + x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0]) + y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1]) + coordinate += [(x, y)] + return coordinate + + def writeObstruct(self, sr, lay, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0)): + """Recursive write boundaries on each Structure in GDS file to LEF""" + for boundary in self.myLayout.structures[sr].boundaries: + coordTrans = self.coordinatesTranslate(boundary.coordinates, mirr, angle, xyShift) + rect = self.minMaxCoord(coordTrans) + lay_convert = tech.layer[lay] + if boundary.drawingLayer == lay_convert: + self.lef.write(" RECT ") + for item in rect: + self.lef.write(" {0} {1}".format(item[0]*self.unit, item[1]*self.unit)) + self.lef.write(" ;\n") + + for sref in self.myLayout.structures[sr].srefs: + sMirr = 1 + if sref.transFlags[0] == True: + sMirr = -1 + sAngle = math.radians(float(0)) + if sref.rotateAngle: + sAngle = math.radians(float(sref.rotateAngle)) + sAngle += angle + x = sref.coordinates[0] + y = sref.coordinates[1] + newX = (x)*math.cos(angle) - mirr*(y)*math.sin(angle) + xyShift[0] + newY = (x)*math.sin(angle) + mirr*(y)*math.cos(angle) + xyShift[1] + sxyShift = (newX, newY) + self.writeObstruct(sref.sName, lay,sMirr, sAngle, sxyShift) + + def writePinCoord(self, sr, pinName, pinLayer, pinCoord, mirr = 1, + angle = math.radians(float(0)), xyShift = (0, 0)): + """Write PIN information to LEF""" + for boundary in self.myLayout.structures[sr].boundaries: + if (pinLayer == boundary.drawingLayer): + coordTrans = self.coordinatesTranslate(boundary.coordinates, mirr, angle, xyShift) + rect = self.minMaxCoord(coordTrans) + if self.pointInsideRect(pinCoord, rect): + self.lef.write(" RECT ") + for item in rect: + self.lef.write(" {0} {1}".format(item[0]*self.unit, item[1]*self.unit)) + self.lef.write(" ;\n") + + for sref in self.myLayout.structures[sr].srefs: + sMirr = 1 + if sref.transFlags[0] == True: + sMirr = -1 + sAngle = math.radians(float(0)) + if sref.rotateAngle: + sAngle = math.radians(float(sref.rotateAngle)) + sAngle += angle + x = sref.coordinates[0] + y = sref.coordinates[1] + newX = (x*math.cos(angle) - mirr*y*math.sin(angle)) + xyShift[0] + newY = (x*math.sin(angle) + mirr*y*math.cos(angle)) + xyShift[1] + sxyShift = (newX, newY) + self.writePinCoord(sref.sName, pinName, pinLayer, pinCoord, sMirr, sAngle, sxyShift) + + def pinLayerCoord(self, sr, pinName): + """Get Pin Layer and Coordinates {layer:[coord1, coord2, ...]}""" + layCoord = defaultdict(list) + for text in self.myLayout.structures[self.sr.name].texts: + if text.textString.strip('\x00') == pinName: + k = text.drawingLayer + v = text.coordinates + d = {k: v} + layCoord.setdefault(k, []).append(v) + return layCoord + + def minMaxCoord(self, coordTrans): + """Find the lowest and highest conner of a Rectangle""" + coordinate = [] + minx = min(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0]) + maxx = max(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0]) + miny = min(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1]) + maxy = max(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1]) + coordinate += [(minx, miny)] + coordinate += [(maxx, maxy)] + return coordinate + + def pointInsideRect(self, p, rect): + """Check if a point is inside a rectangle""" + inside = False + if ((p[0][0] >= rect[0][0])& (p[0][0] <= rect[1][0]) + & (p[0][1] >= rect[0][1]) &(p[0][1] <= rect[1][1])): + inside = not inside + return inside + + def writeLefHeader(self): + """Heafer of LEF file""" + coord = self.lowestLeftCorner(self.sr.name, 1, 0.0, (0, 0), [], [], [], []) + self.lef.write("MACRO {0}\n".format(self.sr.name)) + self.lef.write(" CLASS RING ;\n") + self.lef.write(" ORIGIN {0} {1} ;\n".format(-coord[0][0]*self.unit, coord[0][1]*self.unit)) + self.lef.write(" FOREIGN sram {0} {1} ;\n" + .format(0.0, 0.0)) + self.lef.write(" SIZE {0} BY {1} ;\n" + .format(self.sr.width, self.sr.height)) + self.lef.write(" SYMMETRY X Y R90 ;\n") + + def writeLefFooter(self): + self.lef.write("END {0} \n".format(self.sr.name)) + self.lef.write("END LIBRARY \n") + + def powerPinName(self): + return ["vdd"] + + def groundPinName(self): + return ["gnd"] + + def inputPinName(self): + input_pin_name = [] + for i in range(self.sr.addr_size + int(math.log(self.sr.num_banks, 2))): + input_pin_name.append("ADDR[{0}]".format(i)) + input_pin_name.append("CSb") + input_pin_name.append("OEb") + input_pin_name.append("WEb") + input_pin_name.append("clk") + return input_pin_name + + def inoutPinName(self): + inout_pin_name = [] + for i in range(self.sr.word_size): + inout_pin_name.append("DATA[{0}]".format(i)) + + return inout_pin_name + + def writePin(self, pinName, typ): + self.lef.write(" PIN {0} \n".format(pinName)) + if typ == 1: + self.lef.write(" DIRECTION INOUT ; \n") + self.lef.write(" USE POWER ; \n") + self.lef.write(" SHAPE ABUTMENT ; \n") + self.lef.write(" PORT \n") + elif typ == 2: + self.lef.write(" DIRECTION INOUT ; \n") + self.lef.write(" USE GROUND ; \n") + self.lef.write(" SHAPE ABUTMENT ; \n") + self.lef.write(" PORT \n") + elif typ == 3: + self.lef.write(" DIRECTION INOUT ; \n") + self.lef.write(" PORT \n") + elif typ == 4: + self.lef.write(" DIRECTION INPUT ; \n") + self.lef.write(" PORT \n") + else: + debug.error("Invalid pin type on pin {0}".format(pinName)) + + pin_layer_coord = self.pinLayerCoord(self.sr.name, pinName) + for pinLayer in pin_layer_coord: + lay = [key for key, value in tech.layer.iteritems() if value == pinLayer][0] + self.lef.write(" Layer {0} ; \n".format(lay)) + for pinCoord in pin_layer_coord[pinLayer]: + self.writePinCoord(self.sr.name, pinName, pinLayer, pinCoord, + mirr = 1,angle = math.radians(float(0)), xyShift = (0, 0)) + self.lef.write(" END \n") + self.lef.write(" END {0} \n".format(pinName)) + + def lowestLeftCorner(self, sr, mirr = 1, angle = math.radians(float(0)), xyShift = (0, 0), listMinX = [], listMinY = [], listMaxX = [], listMaxY =[]): + """Recursive find a lowest left conner on each Structure in GDS file""" + for boundary in self.myLayout.structures[sr].boundaries: + coordTrans = self.coordinatesTranslate(boundary.coordinates, mirr, angle, xyShift) + minX = min(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0]) + minY = min(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1]) + maxX = max(coordTrans[0][0], coordTrans[1][0], coordTrans[2][0], coordTrans[3][0]) + maxY = max(coordTrans[0][1], coordTrans[1][1], coordTrans[2][1], coordTrans[3][1]) + listMinX.append(minX) + listMinY.append(minY) + listMaxX.append(maxX) + listMaxY.append(maxY) + + for sref in self.myLayout.structures[sr].srefs: + sMirr = 1 + if sref.transFlags[0] == True: + sMirr = -1 + sAngle = math.radians(float(0)) + if sref.rotateAngle: + sAngle = math.radians(float(sref.rotateAngle)) + sAngle += angle + x = sref.coordinates[0] + y = sref.coordinates[1] + newX = (x*math.cos(angle) - mirr*y*math.sin(angle)) + xyShift[0] + newY = (x*math.sin(angle) + mirr*y*math.cos(angle)) + xyShift[1] + sxyShift = (newX, newY) + self.lowestLeftCorner(sref.sName, sMirr, sAngle, sxyShift, listMinX, listMinY, listMaxX, listMaxY) + coordinate = [] + lowestX = min(listMinX) + lowestY = min(float(item) for item in listMinY) + highestX = max(float(item) for item in listMaxX) + highestY = max(float(item) for item in listMaxY) + coordinate.append((lowestX, lowestY)) + coordinate.append((highestX, highestY)) + return coordinate + diff --git a/compiler/logic_effort_dc.py b/compiler/logic_effort_dc.py new file mode 100644 index 00000000..115ff8a7 --- /dev/null +++ b/compiler/logic_effort_dc.py @@ -0,0 +1,189 @@ +import debug +import design +from tech import drc, cell +from pinv import pinv +from contact import contact +from vector import vector +from globals import OPTS + +class logic_effort_dc(design.design): + """ + Generate a logic effort based delay chain. + Input is a list contains the electrical effort of each stage. + """ + + def __init__(self, name, stage_list): + """init function""" + design.design.__init__(self, "delay_chain") + #fix me: input should be logic effort value + # and there should be functions to get + # area effecient inverter stage list + + # chain_length is number of inverters in the load + # plus 1 for the input + chain_length = 1 + sum(stage_list) + # half chain length is the width of the layeout + # invs are stacked into 2 levels so input/output are close + half_length = round(chain_length / 2.0) + + c = reload(__import__(OPTS.config.bitcell)) + self.mod_bitcell = getattr(c, OPTS.config.bitcell) + self.bitcell_height = self.mod_bitcell.chars["height"] + + self.add_pins() + self.create_module() + self.cal_cell_size(half_length) + self.create_inv_stage_lst(stage_list) + self.add_inv_lst(chain_length) + self.route_inv(stage_list) + self.add_vddgnd_label() + self.DRC_LVS() + + def add_pins(self): + """add the pins of the delay chain""" + self.add_pin("clk_in") + self.add_pin("clk_out") + self.add_pin("vdd") + self.add_pin("gnd") + + def cal_cell_size(self, half_length): + """ calculate the width and height of the cell""" + self.width = half_length * self.inv.width + self.height = 2 * self.bitcell_height + + def create_module(self): + """add the inverters""" + self.inv = pinv(name="delay_chain_inv", + nmos_width=drc["minwidth_tx"], + route_output=False) + self.add_mod(self.inv) + + + def create_inv_stage_lst(self, stage_list): + """ Generate a list to indicate what stage each inv is in """ + self.inv_stage_lst = [[0, True]] + stage_num = 0 + for stage in stage_list: + stage_num = stage_num + 1 + repeat_times = stage + for i in range(repeat_times): + if i == repeat_times - 1: + # the first one need to connected to the next stage + self.inv_stage_lst.append([stage_num, True]) + else: + # the rest should not drive any thing + self.inv_stage_lst.append([stage_num, False]) + + def add_inv_lst(self, chain_length): + """add the inverter and connect them based on the stage list """ + half_length = round(chain_length / 2.0) + self.inv_positions = [] + for i in range(chain_length): + if i < half_length: + # add top level + inv_offset = [i * self.inv.width, + 2 * self.inv.height] + inv_mirror="MX" + self.inv_positions.append(inv_offset) + offset = inv_offset + \ + self.inv.input_position.scale(1,-1) + m1m2_via_rotate=270 + if i == 0: + self.clk_in_offset = offset + else: + # add bottom level + inv_offset = [(chain_length - i) * self.inv.width, + 0] + inv_mirror="MY" + self.inv_positions.append(inv_offset) + offset = inv_offset + \ + self.inv.input_position.scale(-1,1) + m1m2_via_rotate=90 + if i == chain_length - 1: + self.clk_out_offset = inv_offset + \ + self.inv.output_position.scale(-1,1) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=m1m2_via_rotate) + self.add_inst(name="inv_chain%d" % i, + mod=self.inv, + offset=inv_offset, + mirror=inv_mirror) + + # connecting spice + if i == 0: + self.connect_inst(args=["clk_in", "s" + str(self.inv_stage_lst[i][0] + 1), + "vdd", "gnd"], + check=False) + spare_node_counter = 1 + elif i == chain_length - 1: + self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]), "clk_out", "vdd", "gnd"], + check=False) + else: + if self.inv_stage_lst[i][1] == True: + self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]), + "s" + str(self.inv_stage_lst[i][0] + 1), "vdd", "gnd"], + check=False) + spare_node_counter = 1 + else: + self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]), "s" \ + + str(self.inv_stage_lst[i][0] + 1) + "n" \ + + str(spare_node_counter), "vdd", "gnd"], + check=False) + spare_node_counter += 1 + + def route_inv(self, stage_list): + """add metal routing based on the stage list """ + half_length = round((sum(stage_list) + 1) / 2.0) + start_inv = end_inv = 0 + for stage in stage_list: + # end inv number depends on the fan out number + end_inv = start_inv + stage + start_inv_offset = self.inv_positions[start_inv] + end_inv_offset = self.inv_positions[end_inv] + + if start_inv < half_length: + start_o_offset = start_inv_offset + \ + self.inv.output_position.scale(1,-1) + m1m2_via_rotate =270 + m1m2_via_vc = vector(1,-.5) + else: + start_o_offset = start_inv_offset + \ + self.inv.output_position.scale(-1,1) + m1m2_via_rotate =90 + m1m2_via_vc = vector(1,.5) + M2_start = start_o_offset + vector(0,drc["minwidth_metal2"]).scale(m1m2_via_vc) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=start_o_offset, + rotate=m1m2_via_rotate) + + if end_inv < half_length: + end_i_offset = end_inv_offset + \ + self.inv.input_position.scale(1,-1) + M2_end = [end_i_offset[0], end_i_offset[1] - 0.5 * drc["minwidth_metal2"]] + else: + end_i_offset = end_inv_offset + \ + self.inv.input_position.scale(-1,1) + M2_end = [end_i_offset[0], end_i_offset[1] + 0.5 * drc["minwidth_metal2"]] + + if start_inv < half_length and end_inv >= half_length: + mid = [half_length * self.inv.width \ + - 0.5 * drc["minwidth_metal2"], M2_start[1]] + self.add_wire(("metal3", "via2", "metal2"), + [M2_start, mid, M2_end]) + else: + self.add_path(("metal2"), [M2_start, M2_end]) + # set the start of next one after current end + start_inv = end_inv + + def add_vddgnd_label(self): + """add vdd and gnd labels""" + for i in range(3): + if i % 2: + self.add_label(text="vdd", + layer="metal1", + offset=[0, i * self.bitcell_height]) + else: + self.add_label(text="gnd", + layer="metal1", + offset=[0, i * self.bitcell_height]) diff --git a/compiler/ms_flop.py b/compiler/ms_flop.py new file mode 100644 index 00000000..05763bbe --- /dev/null +++ b/compiler/ms_flop.py @@ -0,0 +1,25 @@ +import globals +import design +from math import log +import design +from tech import GDS,layer +import utils + +class ms_flop(design.design): + """ + Memory address flip-flop + """ + + pins = ["din", "dout", "dout_bar", "clk", "vdd", "gnd"] + chars = utils.auto_measure_libcell(pins, "ms_flop", GDS["unit"], layer["boundary"]) + + def __init__(self, name): + design.design.__init__(self, name) + + self.width = ms_flop.chars["width"] + self.height = ms_flop.chars["height"] + + self.clk_offset = ms_flop.chars["clk"] + self.din_offset = ms_flop.chars["din"] + self.dout_offset = ms_flop.chars["dout"] + self.dout_bar_offset = ms_flop.chars["dout_bar"] diff --git a/compiler/ms_flop_array.py b/compiler/ms_flop_array.py new file mode 100644 index 00000000..904eebd1 --- /dev/null +++ b/compiler/ms_flop_array.py @@ -0,0 +1,165 @@ +import debug +import design +from tech import drc +from math import log +from vector import vector +from globals import OPTS + +class ms_flop_array(design.design): + """ + An Array of D-Flipflops used for to store Data_in & Data_out of + Write_driver & Sense_amp, address inputs of column_mux & + hierdecoder + """ + + def __init__(self, name, array_type, columns, word_size): + self.array_type = array_type + self.columns = columns + self.word_size = word_size + design.design.__init__(self, name) + debug.info(1, "Creating %s" % self.name) + + c = reload(__import__(OPTS.config.ms_flop)) + self.mod_ms_flop = getattr(c, OPTS.config.ms_flop) + self.ms_flop_chars = self.mod_ms_flop.chars + + self.create_layout() + + def create_layout(self): + self.add_modules() + self.setup_layout_constants() + self.add_pins() + self.create_ms_flop_array() + self.add_labels() + self.DRC_LVS() + + def add_modules(self): + self.ms_flop = self.mod_ms_flop("ms_flop") + self.add_mod(self.ms_flop) + + def setup_layout_constants(self): + self.width = self.columns * self.ms_flop.width + self.height = self.ms_flop.height + self.words_per_row = self.columns / self.word_size + + self.flop_positions = [] + self.vdd_positions = [] + self.gnd_positions = [] + self.clk_positions = [] + self.dout_positions = [] + self.dout_bar_positions = [] + self.din_positions = [] + + def add_pins(self): + if (self.array_type == "data_in"): + pins = {"data_in": 1, "din": "DATA", "dout": "data_in", "dout_bar": "data_in_bar", "clk": "clk"} + elif (self.array_type == "data_out"): + pins = {"data_out": 1, "din": "data_out", "dout": "tri_in", "dout_bar": "tri_in_bar", "clk": "sclk"} + elif (self.array_type == "address"): + pins = {"address": 1, "din": "ADDR", "dout": "A", "dout_bar": "A_bar", "clk": "addr_clk"} + self.input_output_pins = pins + assert(self.array_type in pins), "Invalid input for array_type" + + for i in range(self.word_size): + self.add_pin(self.input_output_pins["din"] + "[{0}]".format(i)) + for i in range(self.word_size): + self.add_pin(self.input_output_pins["dout"] + "[{0}]".format(i)) + self.add_pin(self.input_output_pins["dout_bar"] + "[{0}]".format(i)) + self.add_pin(self.input_output_pins["clk"]) + self.add_pin("vdd") + self.add_pin("gnd") + + def create_ms_flop_array(self): + for i in range(self.word_size): + name = "Xdff%d" % i + if (i % 2 == 0): + x_off = i * self.ms_flop.width * self.words_per_row + mirror = "None" + else: + if (self.words_per_row == 1): + x_off = (i + 1) * self.ms_flop.width + mirror="MY" + else: + x_off = i * self.ms_flop.width * self.words_per_row + self.add_inst(name=name, + mod=self.ms_flop, + offset=[x_off, 0], + mirror=mirror) + self.connect_inst([self.input_output_pins["din"] + "[{0}]".format(i), + self.input_output_pins["dout"] + "[{0}]".format(i), + self.input_output_pins["dout_bar"] + "[{0}]".format(i), + self.input_output_pins["clk"], + "vdd", "gnd"]) + self.flop_positions.append(vector(x_off, 0)) + + def add_labels(self): + for i in range(self.word_size): + i_str = "[{0}]".format(i) + if (i % 2 == 0 or self.words_per_row > 1): + base = vector(i * self.ms_flop.width * self.words_per_row, 0) + self.add_label(text="gnd", + layer="metal2", + offset=base + self.ms_flop_chars["gnd"]) + self.add_label(text=self.input_output_pins["din"] + i_str, + layer="metal2", + offset=base + self.ms_flop_chars["din"]) + self.add_label(text=self.input_output_pins["dout"] + i_str, + layer="metal2", + offset=base + self.ms_flop_chars["dout"]) + self.add_label(text=self.input_output_pins["dout_bar"] + i_str, + layer="metal2", + offset=base + self.ms_flop_chars["dout_bar"]) + + self.din_positions.append(base + self.ms_flop_chars["din"]) + self.dout_positions.append(base + self.ms_flop_chars["dout"]) + self.dout_bar_positions.append(base + self.ms_flop_chars["dout_bar"]) + self.gnd_positions.append(base + self.ms_flop_chars["gnd"]) + else: + base = vector((i + 1) * self.ms_flop.width, 0) + gnd_offset = base + vector(self.ms_flop_chars["gnd"]).scale(-1,1) + din_offset = base + vector(self.ms_flop_chars["din"]).scale(-1,1) + dout_offset = base + vector(self.ms_flop_chars["dout"]).scale(-1,1) + dout_bar_offset = base + vector(self.ms_flop_chars["dout_bar"]).scale(-1,1) + + self.add_label(text="gnd", + layer="metal2", + offset=gnd_offset) + self.add_label(text=self.input_output_pins["din"] + i_str, + layer="metal2", + offset=din_offset) + self.add_label(text=self.input_output_pins["dout"] + i_str, + layer="metal2", + offset=dout_offset) + self.add_label(text=self.input_output_pins["dout_bar"] + i_str, + layer="metal2", + offset=dout_bar_offset) + + self.gnd_positions.append(gnd_offset) + self.din_positions.append(din_offset) + self.dout_positions.append(dout_offset) + self.dout_bar_positions.append(dout_bar_offset) + + # Continous "clk" rail along with label. + self.add_rect(layer="metal1", + offset=[0, self.ms_flop_chars["clk"][1]], + width=self.width, + height=-drc["minwidth_metal1"]) + self.add_label(text=self.input_output_pins["clk"], + layer="metal1", + offset=self.ms_flop_chars["clk"]) + self.clk_positions.append(vector(self.ms_flop_chars["clk"])) + + # Continous "Vdd" rail along with label. + self.add_rect(layer="metal1", + offset=[0, self.ms_flop_chars["vdd"][1] - 0.5 * drc["minwidth_metal1"]], + width=self.width, + height=drc["minwidth_metal1"]) + self.add_label(text="vdd", + layer="metal1", + offset=vector(self.ms_flop_chars["vdd"]).scale(0, 1)) + self.vdd_positions.append(vector(self.ms_flop_chars["vdd"]).scale(0, 1)) + + self.add_label(text=self.array_type + "ms_flop", + layer="text", + offset=[self.width / 2.0, + self.height / 2.0]) diff --git a/compiler/nand_2.py b/compiler/nand_2.py new file mode 100644 index 00000000..1780c552 --- /dev/null +++ b/compiler/nand_2.py @@ -0,0 +1,444 @@ +import contact +import design +import debug +from tech import drc, cell +from ptx import ptx +from vector import vector +from globals import OPTS + +class nand_2(design.design): + """ + This module generates gds of a parametrically sized 2_input nand. + This model use ptx to generate a 2_input nand within a cetrain height. + The 2_input nand's cell_height should be the same as the 6t library cell + This module doesn't generate multi_finger 2_input nand gate, + It generate only the minimum size 2_input nand gate. + Creates a pcell for a simple 2_input nand + """ + + c = reload(__import__(OPTS.config.bitcell)) + bitcell = getattr(c, OPTS.config.bitcell) + + def __init__(self, name, nmos_width=1, height=bitcell.chars["height"]): + """ Constructor """ + design.design.__init__(self, name) + debug.info(2, "create nand_2 strcuture {0} with size of {1}".format( + name, nmos_width)) + + self.nmos_width = nmos_width + self.height = height + + self.add_pins() + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + """ Add pins """ + self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) + + def create_layout(self): + """ Layout """ + self.determine_sizes() + self.create_ptx() + self.setup_layout_constants() + self.add_rails() + self.add_ptx() + self.add_well_contacts() + + # This isn't instantiated, but we use it to get the dimensions + self.poly_contact = contact.contact(("poly", "contact", "metal1")) + + self.connect_well_contacts() + self.connect_rails() + self.connect_tx() + self.route_pins() + self.extend_wells() + self.extend_active() + self.setup_layout_offsets() + + # Determine transistor size + def determine_sizes(self): + """ Determine the size of the transistors """ + self.nmos_size = self.nmos_width + self.pmos_size = self.nmos_width + self.tx_mults = 1 + + # transistors are created here but not yet placed or added as a module + def create_ptx(self): + """ Add required modules """ + self.nmos1 = ptx(name="nand_2_nmos1", + width=self.nmos_size, + mults=self.tx_mults, + tx_type="nmos") + self.add_mod(self.nmos1) + self.nmos2 = ptx(name="nand_2_nmos2", + width=self.nmos_size, + mults=self.tx_mults, + tx_type="nmos") + self.add_mod(self.nmos2) + + self.pmos1 = ptx(name="nand_2_pmos1", + width=self.pmos_size, + mults=self.tx_mults, + tx_type="pmos") + self.add_mod(self.pmos1) + self.pmos2 = ptx(name="nand_2_pmos2", + width=self.pmos_size, + mults=self.tx_mults, + tx_type="pmos") + self.add_mod(self.pmos2) + + def setup_layout_constants(self): + """ Calculate the layout constraints """ + self.well_width = self.pmos1.active_position[0] \ + + 2 * self.pmos1.active_width \ + + drc["active_to_body_active"] + \ + drc["well_enclosure_active"] + + self.width = self.well_width + + def add_rails(self): + """ add power rails """ + rail_width = self.width + rail_height = drc["minwidth_metal1"] + self.rail_height = rail_height + # Relocate the origin + self.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=self.gnd_position, + width=rail_width, + height=rail_height) + self.add_label(text="gnd", + layer="metal1", + offset=self.gnd_position) + + self.vdd_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=self.vdd_position, + width=rail_width, + height=rail_height) + self.add_label(text="vdd", + layer="metal1", + offset=self.vdd_position) + + def add_ptx(self): + """ transistors are added and placed inside the layout """ + + # determines the spacing between the edge and nmos (rail to active + # metal or poly_to_poly spacing) + edge_to_nmos = max(drc["metal1_to_metal1"] + - self.nmos1.active_contact_positions[0][1], + 0.5 * (drc["poly_to_poly"] - drc["minwidth_metal1"]) + - self.nmos1.poly_positions[0][1]) + + # determine the position of the first transistor from the left + self.nmos_position1 = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos) + offset = self.nmos_position1+ vector(0,self.nmos1.height) + self.add_inst(name="nmos1", + mod=self.nmos1, + offset=offset, + mirror="MX") + self.connect_inst(["Z", "A", "net1", "gnd"]) + + self.nmos_position2 = vector(self.nmos2.active_width - self.nmos2.active_contact.width, + self.nmos_position1[1]) + offset = self.nmos_position2 + vector(0,self.nmos2.height) + self.add_inst(name="nmos2", + mod=self.nmos2, + offset=offset, + mirror="MX") + self.connect_inst(["net1", "B", "gnd", "gnd"]) + + # determines the spacing between the edge and pmos + edge_to_pmos = max(drc["metal1_to_metal1"] \ + - self.pmos1.active_contact_positions[0][1], + 0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"] \ + - self.pmos1.poly_positions[0][1]) + + self.pmos_position1 = vector(0, self.height - 0.5 * drc["minwidth_metal1"] + - edge_to_pmos - self.pmos1.height) + self.add_inst(name="pmos1", + mod=self.pmos1, + offset=self.pmos_position1) + self.connect_inst(["vdd", "A", "Z", "vdd"]) + + self.pmos_position2 = vector(self.nmos_position2.x, self.pmos_position1.y) + self.add_inst(name="pmos2", + mod=self.pmos2, + offset=self.pmos_position2) + self.connect_inst(["Z", "B", "vdd", "vdd"]) + + def add_well_contacts(self): + """ Create and add well copntacts """ + # create well contacts + layer_stack = ("active", "contact", "metal1") + + xoffset = (self.nmos_position2.x + self.pmos1.active_position.x + + self.pmos1.active_width + drc["active_to_body_active"]) + yoffset = (self.pmos_position1.y + + self.pmos1.active_contact_positions[0].y) + offset = self.nwell_contact_position = vector(xoffset, yoffset) + self.pwell_contact=self.add_contact(layer_stack,offset,(1,self.nmos1.num_of_tacts)) + + xoffset = (self.nmos_position2.x + self.nmos1.active_position.x + + self.nmos1.active_width + drc["active_to_body_active"]) + yoffset = (self.nmos_position1.y + self.nmos1.height + - self.nmos1.active_contact_positions[0].y + - self.nmos1.active_contact.height) + offset = self.pwell_contact_position = vector(xoffset, yoffset) + self.nwell_contact=self.add_contact(layer_stack,offset,(1,self.pmos1.num_of_tacts)) + + def connect_well_contacts(self): + """ Connect well contacts to vdd and gnd rail """ + well_tap_length = (self.height - self.nwell_contact_position.y) + offset = vector(self.nwell_contact_position.x + + self.nwell_contact.second_layer_position.x + - self.nwell_contact.first_layer_position.x, + self.nwell_contact_position.y) + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=well_tap_length) + + well_tap_length = self.nmos1.active_height + offset = (self.pwell_contact_position.scale(1,0) + + self.pwell_contact.second_layer_position.scale(1,0) + - self.pwell_contact.first_layer_position.scale(1,0)) + self.add_rect(layer="metal1", + offset=offset, width=drc["minwidth_metal1"], + height=well_tap_length) + + def connect_rails(self): + """ Connect transistor pmos drains to vdd and nmos drains to gnd rail """ + correct = vector(self.pmos1.active_contact.width - drc["minwidth_metal1"], + 0).scale(.5,0) + poffset = self.pmos_position1 + self.pmos1.active_contact_positions[0] + correct + temp_height = self.height - poffset[1] + self.add_rect(layer="metal1", + offset=poffset, width=drc["minwidth_metal1"], + height=temp_height) + + poffset = vector(2 * self.pmos_position2.x + correct.x + + self.pmos2.active_contact_positions[0].x , poffset[1]) + self.add_rect(layer="metal1", + offset=poffset, + width=drc["minwidth_metal1"], + height=temp_height) + + poffset = self.nmos_position1 + self.nmos1.active_contact_positions[0] + correct + self.add_rect(layer="metal1", + offset=poffset.scale(1,0), + width=drc["minwidth_metal1"], + height=temp_height) + + def connect_tx(self): + """ Connect tx poly znd drains """ + self.connect_poly() + self.connect_drains() + + def connect_poly(self): + """ poly connection """ + yoffset_nmos1 = (self.nmos_position1.y + + self.nmos1.poly_positions[0].y + + self.nmos1.poly_height) + poly_length = (self.pmos_position1.y + self.pmos1.poly_positions[0].y + - yoffset_nmos1 + drc["minwidth_poly"]) + for position in self.pmos1.poly_positions: + offset = [position[0], + yoffset_nmos1 - 0.5 * drc["minwidth_poly"]] + self.add_rect(layer="poly", + offset=offset, width=drc["minwidth_poly"], + height=poly_length) + self.add_rect(layer="poly", + offset=[offset[0] + self.pmos1.active_contact.width + 2 * drc["minwidth_poly"], + offset[1]], + width=drc["minwidth_poly"], + height=poly_length) + + def connect_drains(self): + """ Connect pmos and nmos drains. The output will be routed to this connection point. """ + yoffset = self.nmos_position1.y + self.nmos1.active_contact_positions[0].y + drain_length = (self.height + self.pmos1.active_contact_positions[0].y + - yoffset - self.pmos1.height + 0.5 * drc["minwidth_metal2"]) + + for position in self.pmos1.active_contact_positions[1:][::2]: + start = self.drain_position = [position[0] + 0.5 * drc["minwidth_metal1"] + + self.pmos_position2[0] + + self.pmos2.active_contact.first_layer_position[0] + + self.pmos2.active_contact.width / 2, + yoffset] + mid1 = [start[0], + self.height - drc["minwidth_metal2"] - drc["metal2_to_metal2"] - + self.pmos_size - drc["metal1_to_metal1"] - 0.5 * drc["minwidth_metal1"]] + end = [position[0] + 0.5 * drc["minwidth_metal1"] + + self.pmos2.active_contact.second_layer_position[0], + self.pmos_position1[1] + self.pmos1.active_contact_positions[0][1]] + mid2 = [end[0], mid1[1]] + + self.add_path("metal1",[start, mid1, mid2, end]) + + def route_pins(self): + """ Routing """ + self.route_input_gate() + self.route_output() + + def route_input_gate(self): + """ Gate routing """ + self.route_input_gate_A() + self.route_input_gate_B() + + def route_input_gate_A(self): + """ routing for input A """ + + xoffset = self.pmos1.poly_positions[0][0] + yoffset = (self.height + - (drc["minwidth_metal1"] + + drc["metal1_to_metal1"] + + self.pmos2.active_height + + drc["metal1_to_metal1"] + + self.pmos2.active_contact.second_layer_width)) + if (self.nmos_width == drc["minwidth_tx"]): + yoffset = (self.pmos_position1[1] + + self.pmos1.poly_positions[0][1] + + drc["poly_extend_active"] + - (self.pmos1.active_contact.height + - self.pmos1.active_height) / 2 + - drc["metal1_to_metal1"] + - self.poly_contact.width) + + offset = [xoffset, yoffset] + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + size=(1,1), + rotate=90) + + offset = offset - self.poly_contact.first_layer_position.rotate().scale(1,0) + self.add_rect(layer="poly", + offset=offset, + width=self.poly_contact.first_layer_position[1] + drc["minwidth_poly"], + height=self.poly_contact.first_layer_width) + + input_length = (self.pmos1.poly_positions[0][0] + - self.poly_contact.height) + yoffset += self.poly_contact.via_layer_position[0] + offset = self.input_position1 = vector(0, yoffset) + self.add_rect(layer="metal1", + offset=offset, + width=input_length, + height=drc["minwidth_metal1"]) + self.add_label(text="A", + layer="metal1", + offset=offset) + + def route_input_gate_B(self): + """ routing for input B """ + xoffset = (self.pmos2.poly_positions[0][0] + + self.pmos_position2[0] + drc["minwidth_poly"]) + yoffset = (drc["minwidth_metal1"] + + drc["metal1_to_metal1"] + + self.nmos2.active_height + + drc["minwidth_metal1"]) + if (self.nmos_width == drc["minwidth_tx"]): + yoffset = (self.nmos_position1[1] + + self.nmos1.poly_positions[0][1] + + self.nmos1.poly_height + + drc["metal1_to_metal1"]) + offset = [xoffset, yoffset] + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + size=(1,1), + rotate=90) + + input_length = self.pmos2.poly_positions[0].x - self.poly_contact.height + self.input_position2 = vector(xoffset - self.poly_contact.width, + yoffset + self.poly_contact.via_layer_position[0]) + self.add_layout_pin(text="B", + layer="metal1", + offset=self.input_position2.scale(0,1), + width=(input_length + self.pmos_position2[0] + drc["minwidth_poly"]), + height=drc["minwidth_metal1"]) + + def route_output(self): + """ routing for output Z """ + yoffset = (self.nmos1.height - 2 * drc["minwidth_metal1"] / 3 + + (self.height - self.pmos1.height - self.nmos1.height - drc["minwidth_metal1"]) / 2 ) + xoffset = self.drain_position[0] + offset = self.output_position = vector(xoffset, yoffset) + output_length = self.width - xoffset + self.add_layout_pin(text="Z", + layer="metal1", + offset=offset, + width=output_length, + height=drc["minwidth_metal1"]) + + def extend_wells(self): + """ Extension of well """ + middle_point = (self.nmos_position1.y + self.nmos1.pwell_position.y + + self.nmos1.well_height + + (self.pmos_position1.y + self.pmos1.nwell_position.y + - self.nmos_position1.y - self.nmos1.pwell_position.y + - self.nmos1.well_height) / 2) + offset = self.nwell_position = vector(0, middle_point) + self.nwell_height = self.height - middle_point + self.add_rect(layer="nwell", + offset=offset, + width=self.well_width, + height=self.nwell_height) + self.add_rect(layer="vtg", + offset=offset, + width=self.well_width, + height=self.nwell_height) + + offset = self.pwell_position = vector(0, 0) + self.pwell_height = middle_point + self.add_rect(layer="pwell", + offset=offset, + width=self.well_width, + height=self.pwell_height) + self.add_rect(layer="vtg", + offset=offset, + width=self.well_width, + height=self.pwell_height) + + def extend_active(self): + """ Extension of active region """ + self.active_width = (self.pmos1.active_width + + drc["active_to_body_active"] + + self.pmos1.active_contact.width) + offset = (self.pmos1.active_position + + self.pmos_position2.scale(1,0) + + self.pmos_position1.scale(0,1)) + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.pmos1.active_height) + + offset = offset + vector(self.pmos1.active_width, 0) + width = self.active_width - self.pmos1.active_width + self.add_rect(layer="nimplant", + offset=offset, + width=width, + height=self.pmos1.active_height) + + offset = vector(self.nmos_position2[0] + self.nmos1.active_position[0], + self.nmos_position1[1] - self.nmos1.active_height + - self.nmos1.active_position[1] + self.nmos1.height) + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.nmos1.active_height) + + offset = offset + vector(self.nmos1.active_width,0) + width = self.active_width - self.nmos1.active_width + self.add_rect(layer="pimplant", + offset=offset, + width=width, + height=self.nmos1.active_height) + + def setup_layout_offsets(self): + """ Defining the position of i/o pins for the two input nand gate """ + self.A_position = self.A_position = self.input_position1 + self.B_position = self.B_position = self.input_position2 + self.Z_position = self.Z_position = self.output_position + self.vdd_position = self.vdd_position + self.gnd_position = self.gnd_position diff --git a/compiler/nand_3.py b/compiler/nand_3.py new file mode 100644 index 00000000..ede069c9 --- /dev/null +++ b/compiler/nand_3.py @@ -0,0 +1,533 @@ +import contact +import design +import debug +from tech import drc, cell +from ptx import ptx +from vector import vector +from globals import OPTS + +class nand_3(design.design): + """ + This module generates gds of a parametrically sized 3_input nand. + + This model use ptx to generate a 3_input nand within a cetrain height. + The 3_input nand's cell_height should be the same as the 6t library cell. + This module doesn't generate multi_finger 3_input nand gate, + It generate only the minimum size 3_input nand gate + """ + c = reload(__import__(OPTS.config.bitcell)) + bitcell = getattr(c, OPTS.config.bitcell) + + + def __init__(self, name, nmos_width=1, height=bitcell.chars["height"]): + """Constructor : Creates a pcell for a simple 3_input nand""" + + design.design.__init__(self, name) + debug.info(2, "create nand_3 strcuture {0} with size of {1}".format(name, nmos_width)) + + self.nmos_width = nmos_width + self.height = height + + self.add_pins() + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + """ add pics for this module """ + self.add_pin_list(["A", "B", "C", "Z", "vdd", "gnd"]) + + def create_layout(self): + """ create layout """ + self.determine_sizes() + self.create_ptx() + self.setup_layout_constants() + self.add_rails() + self.add_ptx() + self.add_well_contacts() + + # These aren't for instantiating, but we use them to get the dimensions + self.poly_contact = contact.contact(("poly", "contact", "metal1")) + self.m1m2_via = contact.contact(("metal1", "via1", "metal2")) + + self.connect_tx() + self.connect_well_contacts() + self.extend_wells() + self.extend_active() + self.connect_rails() + self.route_pins() + self.setup_layout_offsets() + + def determine_sizes(self): + """ Determine the size of the transistors used in this module """ + self.nmos_size = self.nmos_width + self.pmos_size = 2 * self.nmos_width / 3 + self.tx_mults = 1 + + def create_ptx(self): + """ Create ptx but not yet placed""" + self.nmos1 = ptx(name="nand_3_nmos1", + width=self.nmos_size, + mults=self.tx_mults, + tx_type="nmos") + self.add_mod(self.nmos1) + self.nmos2 = ptx(name="nand_3_nmos2", + width=self.nmos_size, + mults=self.tx_mults, + tx_type="nmos") + self.add_mod(self.nmos2) + self.nmos3 = ptx(name="nand_3_nmos3", + width=self.nmos_size, + mults=self.tx_mults, + tx_type="nmos") + self.add_mod(self.nmos3) + + self.pmos1 = ptx(name="nand_3_pmos1", + width=self.pmos_size, + mults=self.tx_mults, + tx_type="pmos") + self.add_mod(self.pmos1) + self.pmos2 = ptx(name="nand_3_pmos2", + width=self.pmos_size, + mults=self.tx_mults, + tx_type="pmos") + self.add_mod(self.pmos2) + self.pmos3 = ptx(name="nand_3_pmos3", + width=self.pmos_size, + mults=self.tx_mults, + tx_type="pmos") + self.add_mod(self.pmos3) + + def setup_layout_constants(self): + """ setup layout constraints """ + self.well_width = self.nmos1.active_position[0] \ + + 3 * self.pmos1.active_width + drc["active_to_body_active"] \ + + drc["well_enclosure_active"] - self.nmos3.active_contact.width \ + + self.pmos1.active_contact.height + drc["minwidth_metal1"] \ + + (drc["metal1_to_metal1"] - drc["well_enclosure_active"]) + self.width = self.width = self.well_width + + def add_rails(self): + """ Add VDD and GND rails """ + rail_width = self.width + self.rail_height = rail_height = drc["minwidth_metal1"] + + # Relocate the origin + self.gnd_position = vector(0 , - 0.5 * drc["minwidth_metal1"]) + self.add_layout_pin(text="gnd", + layer="metal1", + offset=self.gnd_position, + width=rail_width, + height=rail_height) + + self.vdd_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"]) + self.add_layout_pin(text="vdd", + layer="metal1", + offset=self.vdd_position, + width=rail_width, + height=rail_height) + + def add_ptx(self): + """ transistors are added and placed inside the layout """ + # determines the spacing between the edge and nmos (rail to active + # metal or poly_to_poly spacing) + self.edge_to_nmos = max(drc["metal1_to_metal1"] + - self.nmos1.active_contact_positions[0].y, + 0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"] + - self.nmos1.poly_positions[0].y) + + # Extra offset is calculated to make room for B and C contancts + xoffset = (self.nmos1.active_contact.width + + drc["minwidth_metal1"] + + (drc["metal1_to_metal1"] - drc["well_enclosure_active"])) + + # determine the position of the first transistor from the left + self.nmos_position1 = vector(xoffset, + 0.5 * drc["minwidth_metal1"] + self.edge_to_nmos) + offset = self.nmos_position1 + vector(0,self.nmos1.height) + self.add_inst(name="nmos1", + mod=self.nmos1, + offset=offset, + mirror="MX") + self.connect_inst(["net2", "A", "gnd", "gnd"]) + + self.nmos_position2 = (self.nmos_position1 + + vector(self.nmos2.active_width,0) + - vector(self.nmos2.active_contact.width,0)) + offset = self.nmos_position2 + vector(0, self.nmos2.height) + self.add_inst(name="nmos2", + mod=self.nmos2, + offset=offset, + mirror="MX") + self.connect_inst(["net1", "B", "net2", "gnd"]) + + p2tp3 = vector(self.nmos3.active_width - self.nmos3.active_contact.width, + self.nmos3.height) + self.nmos_position3 = self.nmos_position2 + p2tp3 + self.add_inst(name="nmos3", + mod=self.nmos3, + offset=self.nmos_position3, + mirror="MX") + self.connect_inst(["Z", "C", "net1", "gnd"]) + + # determines the spacing between the edge and pmos + self.edge_to_pmos = max(drc["metal1_to_metal1"] + - self.pmos1.active_contact_positions[0][1], + 0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"] + - self.pmos1.poly_positions[0][1]) + + self.pmos_position1 = vector(self.nmos_position1[0], + self.height - 0.5 * drc["minwidth_metal1"] + - self.pmos1.height - self.edge_to_pmos) + self.add_inst(name="pmos1", + mod=self.pmos1, + offset=self.pmos_position1) + self.connect_inst(["Z", "A", "vdd", "vdd"]) + + self.pmos_position2 = vector(self.nmos_position2.x, self.pmos_position1.y) + self.add_inst(name="pmos2", + mod=self.pmos2, + offset=self.pmos_position2) + self.connect_inst(["vdd", "B", "Z", "vdd"]) + + self.pmos_position3 = vector(self.nmos_position3.x, self.pmos_position1.y) + self.add_inst(name="pmos3", + mod=self.pmos3, + offset=self.pmos_position3) + self.connect_inst(["Z", "C", "vdd", "vdd"]) + + def add_well_contacts(self): + """ create well contacts """ + layer_stack = ("active", "contact", "metal1") + + xoffset = (self.nmos_position3.x + self.pmos1.active_position.x + + self.pmos1.active_width + drc["active_to_body_active"]) + yoffset = self.pmos_position1.y + self.pmos1.active_contact_positions[0].y + self.nwell_contact_position = vector(xoffset, yoffset) + self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos1.num_of_tacts)) + + xoffset = self.nmos_position3.x + (self.nmos1.active_position.x + + self.nmos1.active_width + + drc["active_to_body_active"]) + yoffset = self.nmos_position1.y + (self.nmos1.height + - self.nmos1.active_contact_positions[0].y + - self.nmos1.active_contact.height) + self.pwell_contact_position = vector(xoffset, yoffset) + self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos1.num_of_tacts)) + + def connect_well_contacts(self): + """ Connect well contacts to vdd and gnd rail """ + well_tap_length = self.height - self.nwell_contact_position[1] + xoffset = (self.nwell_contact_position[0] + + self.nwell_contact.second_layer_position[0] + - self.nwell_contact.first_layer_position[0]) + offset = [xoffset, self.nwell_contact_position.y] + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=well_tap_length) + + well_tap_length = self.nmos1.active_height + offset = vector(self.pwell_contact_position + + self.pwell_contact.second_layer_position + - self.pwell_contact.first_layer_position).scale(1,0) + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=well_tap_length) + + def connect_rails(self): + """ Connect transistor pmos drains to vdd and nmos drains to gnd rail """ + correct = vector(self.pmos1.active_contact.width - drc["minwidth_metal1"], + 0).scale(0.5,0) + poffset = self.pmos_position1 + self.pmos1.active_contact_positions[0] + correct + temp_height = self.height - poffset[1] + self.add_rect(layer="metal1", + offset=poffset, + width=drc["minwidth_metal1"], + height=temp_height) + + poffset = [self.pmos_position3.x + self.pmos3.active_contact_positions[0].x + correct.x, + poffset[1]] + self.add_rect(layer="metal1", + offset=poffset, + width=drc["minwidth_metal1"], + height=temp_height) + + poffset = self.nmos_position1 + self.nmos1.active_contact_positions[0] + correct + self.add_rect(layer="metal1", + offset=poffset.scale(1,0), + width=drc["minwidth_metal1"], + height=temp_height) + + def connect_tx(self): + """ poly and drain connections """ + self.connect_poly() + self.connect_drains() + + def connect_poly(self): + """ Connect poly """ + yoffset_nmos1 = (self.nmos_position1.y + self.nmos1.poly_positions[0].y + + self.nmos1.poly_height) + poly_length = (self.pmos_position1.y + self.pmos1.poly_positions[0].y + - yoffset_nmos1 + drc["minwidth_poly"]) + + offset = [self.nmos_position1[0] + self.nmos1.poly_positions[0][0], + yoffset_nmos1 - drc["minwidth_poly"]] + self.add_rect(layer="poly", + offset=offset, + width=drc["minwidth_poly"], + height=poly_length) + self.add_rect(layer="poly", + offset=[offset[0] + self.pmos1.active_contact.width + 2 * drc["minwidth_poly"], + offset[1]], + width=drc["minwidth_poly"], + height=poly_length) + self.add_rect(layer="poly", + offset=[offset[0] + 2 * self.pmos1.active_contact.width + 4 * drc["minwidth_poly"], + offset[1]], + width=drc["minwidth_poly"], + height=poly_length) + + def connect_drains(self): + """ Connect pmos and nmos drains. The output will be routed to this connection point. """ + yoffset = drc["minwidth_metal1"] + (self.nmos1.active_contact_positions[0].y + - drc["well_enclosure_active"] + + drc["metal1_to_metal1"]) + drain_length = (self.height - yoffset + 0.5 * drc["minwidth_metal1"] + - (self.pmos1.height - self.pmos1.active_contact_positions[0][1])) + layer_stack = ("metal1", "via1", "metal2") + for position in self.pmos1.active_contact_positions[1:][::2]: + diff_active_via = self.pmos2.active_contact.width - self.m1m2_via.second_layer_width + offset = (self.pmos_position2 + self.pmos2.active_contact_positions[0] + + vector(diff_active_via / 2,0)) + self.add_via(layer_stack,offset) + + width = (2 * self.pmos3.active_width + - self.pmos3.active_contact.width + - (self.pmos2.active_contact.width + - self.m1m2_via.second_layer_width)) + self.add_rect(layer="metal2", + offset=offset, + width=width, + height=self.m1m2_via.second_layer_width) + + xoffset = (self.pmos_position3.x + self.pmos3.active_contact_positions[1].x + + diff_active_via / 2) + self.add_via(layer_stack,[xoffset,offset.y]) + + xoffset = (self.nmos_position3[0] + self.nmos3.active_position[0] + + self.nmos3.active_width - self.nmos3.active_contact.width / 2) + self.drain_position = vector(xoffset, + drc["minwidth_metal1"] + drc["metal1_to_metal1"]) + length = self.height - 2 * (drc["minwidth_metal1"] + drc["metal1_to_metal1"]) + self.add_rect(layer="metal1", + offset=self.drain_position, + width=drc["minwidth_metal1"], + height=length) + + def route_pins(self): + """ input anbd output routing """ + self.route_input_gate_A() + self.route_input_gate_B() + self.route_input_gate_C() + self.route_output() + + def route_input_gate_A(self): + """ routing for input A """ + + offset = (self.pmos_position1 + + self.pmos1.poly_positions[0] + - vector(drc["minwidth_poly"] / 2, self.poly_contact.width)) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + self.add_rect(layer="poly", + offset=offset + vector(drc["minwidth_poly"] / 2,0), + width=-(self.poly_contact.first_layer_position[1] + drc["minwidth_poly"]), + height=self.poly_contact.first_layer_width) + + offset = vector(offset.x, + self.pmos_position1[1] + self.pmos1.poly_positions[0][1]) + self.add_layout_pin(text="A", + layer="metal1", + offset=offset, + width=-offset.x, + height=-drc["minwidth_metal1"]) + self.A_position = vector(0, offset.y - drc["minwidth_metal1"]) + + + def route_input_gate_B(self): + """ routing for input B """ + xoffset = self.pmos2.poly_positions[0][0] \ + + self.pmos_position2[0] - drc["minwidth_poly"] + yoffset = self.nmos_position1[1] + self.nmos1.height \ + - drc["well_enclosure_active"] + (self.nmos1.active_contact.height \ + - self.nmos1.active_height) / 2 \ + + drc["metal1_to_metal1"] + self.add_contact(layers=("poly", "contact", "metal1"), + offset=[xoffset,yoffset]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[xoffset,yoffset]) + + xoffset = self.pmos2.poly_positions[0][0] + self.pmos_position2[0] \ + - drc["minwidth_poly"] + self.m1m2_via.width + length = -xoffset + self.m1m2_via.width + self.add_rect(layer="metal2", + offset=[xoffset, yoffset], + width=length, + height=-drc["minwidth_metal2"]) + + self.B_position = vector(0, yoffset - drc["minwidth_metal1"]) + self.add_label(text="B", + layer="metal1", + offset=self.B_position) + + xoffset = self.pmos_position1[0] + self.pmos1.active_position[0] \ + - drc["metal1_to_metal1"] + (self.pmos1.active_contact.width \ + - self.m1m2_via.second_layer_width) / 2 + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[xoffset,yoffset - drc["minwidth_metal2"]], + rotate=90) + self.add_rect(layer="metal1", + offset=[xoffset, yoffset], + width=-xoffset, + height=-drc["minwidth_metal1"]) + + def route_input_gate_C(self): + """ routing for input A """ + xoffset = self.pmos3.poly_positions[0][0] \ + + self.pmos_position3[0] - drc["minwidth_poly"] + yoffset = self.nmos_position1[1] + self.nmos1.height \ + - drc["well_enclosure_active"] + (self.nmos1.active_contact.height \ + - self.nmos1.active_height) / 2 + drc["metal1_to_metal1"] + + self.add_contact(layers=("poly", "contact", "metal1"), + offset=[xoffset,yoffset]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[xoffset,yoffset]) + + xoffset = self.pmos3.poly_positions[0][0] \ + + self.pmos_position3[0] - drc["minwidth_poly"] \ + + self.m1m2_via.width + length = -xoffset + self.m1m2_via.width + self.add_rect(layer="metal2", + offset=[xoffset, + yoffset - drc["minwidth_metal2"] - drc["metal2_to_metal2"]], + width=length, + height=-drc["minwidth_metal2"]) + + # FIXME: Convert to add_layout_pin? + self.add_rect(layer="metal2", + offset=[xoffset - self.m1m2_via.width, + yoffset], + width=self.m1m2_via.width, + height=-drc["minwidth_metal2"] - drc["metal2_to_metal2"]) + self.C_position = vector(0, + self.B_position[1] - drc["metal2_to_metal2"] - drc["minwidth_metal1"] \ + - (self.m1m2_via.second_layer_width + - self.m1m2_via.first_layer_width)) + self.add_label(text="C", + layer="metal1", + offset=self.C_position) + + xoffset = self.pmos_position1[0] + self.pmos1.active_position[0] \ + - drc["metal1_to_metal1"] + (self.pmos1.active_contact.width \ + - self.m1m2_via.second_layer_width) / 2 + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[xoffset, + yoffset - 2 * drc["minwidth_metal2"] - self.m1m2_via.width], + rotate=90) + self.add_rect(layer="metal1", + offset=[xoffset, + yoffset - 2 * drc["minwidth_metal2"]], + width=-xoffset, + height=-drc["minwidth_metal1"]) + + def route_output(self): + """ routing for output Z """ + xoffset = self.nmos_position3[0] + self.nmos3.active_position[0] \ + + self.nmos3.active_width - self.nmos3.active_contact.width / 2 + yoffset = (self.nmos1.height + (self.height - drc["minwidth_metal1"] + - self.pmos1.height - self.nmos1.height) / 2 + - (drc["minwidth_metal1"] / 2)) + # FIXME Convert to add_layout_pin? + self.add_rect(layer="metal1", + offset=[xoffset, yoffset], + width=self.well_width - xoffset, + height=drc["minwidth_metal1"]) + + self.Z_position = vector(self.well_width, yoffset) + self.add_label(text="Z", + layer="metal1", + offset=self.Z_position) + + def extend_wells(self): + """ extension of well """ + middle_point = self.nmos_position1[1] + self.nmos1.pwell_position[1] \ + + self.nmos1.well_height + (self.pmos_position1[1] + + self.pmos1.nwell_position[1] + - self.nmos_position1[1] + - self.nmos1.pwell_position[1] + - self.nmos1.well_height) / 2 + offset = self.nwell_position = vector(0, middle_point) + self.nwell_height = self.height - middle_point + self.add_rect(layer="nwell", + offset=offset, + width=self.well_width, + height=self.nwell_height) + self.add_rect(layer="vtg", + offset=offset, + width=self.well_width, + height=self.nwell_height) + + offset = self.pwell_position = vector(0, 0) + self.pwell_height = middle_point + self.add_rect(layer="pwell", + offset=offset, width=self.well_width, + height=self.pwell_height) + self.add_rect(layer="vtg", + offset=offset, + width=self.well_width, + height=self.pwell_height) + + def extend_active(self): + """ extension of active region """ + self.active_width = self.pmos1.active_width \ + + drc["active_to_body_active"] + self.pmos1.active_contact.width + offset = (self.pmos1.active_position+self.pmos_position3.scale(1,0) + + self.pmos_position1.scale(0,1)) + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.pmos1.active_height) + + offset = offset + vector(self.pmos1.active_width,0) + width = self.active_width - self.pmos1.active_width + self.add_rect(layer="nimplant", + offset=offset, + width=width, + height=self.pmos1.active_height) + + offset = [self.nmos_position3[0] + self.nmos1.active_position[0], + self.nmos_position1[1] + self.nmos1.height + - self.nmos1.active_position[1] - self.nmos1.active_height] + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.nmos1.active_height) + + offset = offset + vector(self.nmos1.active_width,0) + width = self.active_width - self.nmos1.active_width + self.add_rect(layer="pimplant", + offset=offset, + width=width, + height=self.nmos1.active_height) + + def setup_layout_offsets(self): + """ Defining the position of i/o pins for the three input nand gate """ + self.A_position = self.A_position + self.B_position = self.B_position + self.C_position = self.C_position + self.Z_position = self.Z_position + self.vdd_position = self.vdd_position + self.gnd_position = self.gnd_position diff --git a/compiler/nor_2.py b/compiler/nor_2.py new file mode 100644 index 00000000..9138c8eb --- /dev/null +++ b/compiler/nor_2.py @@ -0,0 +1,483 @@ +import contact +import design +import debug +from tech import drc, cell +from ptx import ptx +from vector import vector +from globals import OPTS + +class nor_2(design.design): + """ + This module generates gds of a parametrically sized 2_input nor. + + This model use ptx to generate a 2_input nand within a cetrain height. + The 2_input nor cell_height should be the same as the 6t library cell. + If pmos can not fit in the given vertical space, it will be folded + based so that it takes minmium horiztonal space. + """ + c = reload(__import__(OPTS.config.bitcell)) + bitcell = getattr(c, OPTS.config.bitcell) + + def __init__(self, name, nmos_width=1, height=bitcell.chars["height"]): + """init function""" + design.design.__init__(self, name) + debug.info(2, "create nand_2 strcuture {0} with size of {1}".format(name, nmos_width)) + + self.nmos_width = nmos_width + self.height = height + + self.add_pins() + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + self.add_pin_list(["A", "B", "Z", "vdd", "gnd"]) + + def create_layout(self): + # These aren't for instantiating, but we use them to get the dimensions + self.poly_contact = contact.contact(("poly", "contact", "metal1")) + self.m1m2_via = contact.contact(("metal1", "via1", "metal2")) + + self.determine_sizes() + self.create_modules() + + # These aren't for instantiating, but we use them to get the dimensions + self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"), + dimensions=(1, self.pmos1.num_of_tacts)) + self.pwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"), + dimensions=(1, self.nmos1.num_of_tacts)) + + self.setup_layout_constants() + self.add_rails() + self.add_ptx() + self.add_well_contacts() + self.extend_wells() + self.extend_active() + self.route() + + def determine_sizes(self, beta=4): + """Determine transistor size""" + nmos_mults = 1 + for pmos_mults in range(1, 5): + nmos_size = self.nmos_width + pmos_size = 4 * self.nmos_width / pmos_mults + test_nmos = ptx(name="testp", + width=nmos_size, + mults=nmos_mults, + tx_type="nmos") + test_pmos = ptx(name="testn", + width=pmos_size, + mults=pmos_mults, + tx_type="nmos") + + # this how the position is done for now + # postion noms and pmos and put A at 0.3 of the margin of it and put B and the 3rd m1 track above it + # if there is no left space then it means the nmos is too big, we + # need to increase the mults number + gate_to_gate = drc["poly_to_poly"] - drc["minwidth_metal1"] + edge_to_nmos = max(drc["metal1_to_metal1"] - test_nmos.active_contact_positions[0].y, + 0.5 * gate_to_gate - test_nmos.poly_positions[0].y) + edge_to_pmos = max(drc["metal1_to_metal1"] - test_pmos.active_contact_positions[0].y, + 0.5 * gate_to_gate - test_pmos.poly_positions[0].y) + route_margin = .5 * (self.poly_contact.second_layer_width + + drc["minwidth_metal1"]) + drc["metal1_to_metal1"] + pmos_position = [0, self.height - 0.5 * drc["minwidth_metal1"] + - edge_to_pmos - test_pmos.height] + nmos_position = [0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos] + leftspace = (0.7 * (pmos_position[1] - nmos_position[1]) + - 3 * route_margin - 2 * drc["metal1_to_metal1"]) + if leftspace >= 0: + break + + self.nmos_size = nmos_size + self.pmos_size = pmos_size + self.nmos_mults = nmos_mults + self.pmos_mults = pmos_mults + + def create_modules(self): + """transistors are created as modules""" + self.nmos1 = ptx(name="nor_2_nmos1", + width=self.nmos_size, + mults=self.nmos_mults, + tx_type="nmos") + self.add_mod(self.nmos1) + self.nmos2 = ptx(name="nor_2_nmos2", + width=self.nmos_size, + mults=self.nmos_mults, + tx_type="nmos") + self.add_mod(self.nmos2) + + self.pmos1 = ptx(name="nor_2_pmos1", + width=self.pmos_size, + mults=self.pmos_mults, + tx_type="pmos") + self.add_mod(self.pmos1) + self.pmos2 = ptx(name="nor_2_pmos2", + width=self.pmos_size, + mults=self.pmos_mults, + tx_type="pmos") + self.add_mod(self.pmos2) + + + def setup_layout_constants(self): + """ calcuate the transistor spacing and cell size""" + # determines the spacing between the edge and nmos (rail to active + # metal or poly_to_poly spacing) + half_gate_to_gate = 0.5 * (drc["poly_to_poly"] - drc["minwidth_metal1"]) + edge_to_nmos = max(drc["metal1_to_metal1"] - self.nmos1.active_contact_positions[0].y, + half_gate_to_gate - self.nmos1.poly_positions[0].y) + + # determine the position of the first transistor from the left + self.nmos_position1 = vector(0, + 0.5 * drc["minwidth_metal1"] + edge_to_nmos) + offset = self.nmos_position1 + vector(0,self.nmos1.height) + + x = vector(self.nmos2.active_width - self.nmos2.active_contact.width, 0) + self.nmos_position2 = x + self.nmos_position1.scale(0,1) + + # determines the spacing between the edge and pmos + edge_to_pmos = max(drc["metal1_to_metal1"] - self.pmos1.active_contact_positions[0].y, + half_gate_to_gate - self.pmos1.poly_positions[0].y) + self.pmos_position1 = vector(0, + self.height - 0.5 * drc["minwidth_metal1"] + - edge_to_pmos - self.pmos1.height) + self.pmos_position2 = self.pmos_position1 + vector(self.pmos1.width,0) + + self.well_width = max(self.pmos_position2.x + self.pmos2.active_position.x + + self.pmos2.active_width + + drc["active_to_body_active"] + self.nwell_contact.width + + drc["well_enclosure_active"], + self.nmos_position2.x + self.nmos2.active_position.x + + self.nmos2.active_width + + drc["active_to_body_active"] + drc["well_enclosure_active"]) + self.width = self.well_width + + def add_rails(self): + rail_width = self.width + rail_height = drc["minwidth_metal1"] + self.rail_height = rail_height + # Relocate the origin + self.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"]) + self.add_layout_pin(text="gnd", + layer="metal1", + offset=self.gnd_position, + width=rail_width, + height=rail_height) + + self.vdd_position = self.gnd_position + vector(0, self.height) + self.add_layout_pin(text="vdd", + layer="metal1", + offset=self.vdd_position, + width=rail_width, + height=rail_height) + + def add_ptx(self): + """ transistors are placed in the layout""" + offset = self.nmos_position1 + vector(0, self.nmos1.height) + self.add_inst(name="nmos1", + mod=self.nmos1, + offset=offset, + mirror="MX") + self.connect_inst(["Z", "A", "gnd", "gnd"]) + + offset = self.nmos_position2 + vector(0, self.nmos2.height) + self.add_inst(name="nmos2", + mod=self.nmos2, + offset=offset, + mirror="MX") + self.connect_inst(["Z", "B", "gnd", "gnd"]) + + offset = self.pmos_position1 + self.add_inst(name="pmos1", + mod=self.pmos1, + offset=offset) + self.connect_inst(["vdd", "A", "net1", "vdd"]) + + offset = self.pmos_position2 + self.add_inst(name="pmos2", + mod=self.pmos2, + offset=offset) + self.connect_inst(["net1", "B", "Z", "vdd"]) + + def add_well_contacts(self): + layer_stack = ("active", "contact", "metal1") + + xoffset = (self.pmos_position2.x + self.pmos1.active_position.x + + self.pmos1.active_width + drc["active_to_body_active"]) + yoffset = (self.pmos_position1.y + + self.pmos1.active_contact_positions[0].y) + self.nwell_contact_position = vector(xoffset, yoffset) + self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos1.num_of_tacts)) + + xoffset = self.nmos_position2[0] + (self.nmos1.active_position[0] + + self.nmos1.active_width + + drc["active_to_body_active"]) + yoffset = (self.nmos_position1[1] + self.nmos1.height + - self.nmos1.active_contact_positions[0].y + - self.nmos1.active_contact.height) + self.pwell_contact_position = vector(xoffset, yoffset) + self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos1.num_of_tacts)) + + + def route(self): + self.route_pins() + self.connect_well_contacts() + M1_track = (self.B_position[1] + drc["metal1_to_metal1"] + + .5 * (self.poly_contact.second_layer_width + + drc["minwidth_metal1"])) + self.connect_tx(M1_track) + self.connect_poly() + + def connect_well_contacts(self): + """ Connect well contacts to vdd and gnd rail """ + well_tap_length = self.height - self.nwell_contact_position[1] + xoffset = (self.nwell_contact_position.x + + self.nwell_contact.second_layer_position.x + - self.nwell_contact.first_layer_position.x) + offset = [xoffset, self.nwell_contact_position[1]] + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=well_tap_length) + + offset = (self.pwell_contact_position.scale(1,0) + + self.pwell_contact.second_layer_position.scale(1,0) + - self.pwell_contact.first_layer_position.scale(1,0)) + well_tap_length = self.pwell_contact_position[1] + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=well_tap_length) + + def connect_tx(self, M1_track): + """Connect transistor pmos drains to vdd and nmos drains to gnd rail""" + # the first pmos drain to Vdd + for i in range(len(self.pmos1.active_contact_positions)): + contact_pos = self.pmos_position1 + self.pmos1.active_contact_positions[i] + if i % 2 == 0: + correct = self.pmos1.active_contact.second_layer_position.scale(1,0) + drain_posistion = contact_pos + correct + height = self.vdd_position[1] - drain_posistion[1] + self.add_rect(layer="metal1", + offset=drain_posistion, + width=drc["minwidth_metal1"], + height=height) + else: + # source to pmos2 + correct = (self.pmos1.active_contact.second_layer_position.scale(1,0) + + vector(self.pmos1.active_contact.second_layer_width, + 0).scale(.5,0)) + source_position = contact_pos + correct + mid = [self.pmos_position2[0], M1_track] + self.add_path("metal1", [source_position, mid]) + + # the second pmos + for i in range(len(self.pmos2.active_contact_positions)): + if i % 2 == 0: + # source to pmos2 + pmos_active =self.pmos_position2+self.pmos2.active_contact_positions[i] + correct= (self.pmos2.active_contact.second_layer_position.scale(1,0) + + vector(0.5 * self.pmos2.active_contact.second_layer_width,0)) + source_position = pmos_active + correct + mid = [self.pmos_position2[0], M1_track] + self.add_path("metal1", [source_position, mid]) + # two nmos source to gnd + source_posistion1 = (self.nmos_position1 + + self.nmos1.active_contact_positions[0] + + self.nmos1.active_contact.second_layer_position.scale(1,0)) + height = self.gnd_position[1] - source_posistion1[1] + self.add_rect(layer="metal1", + offset=source_posistion1, + width=drc["minwidth_metal1"], + height=height) + + source_posistion2 = (self.nmos_position2 + + self.nmos2.active_contact_positions[1] + + self.nmos2.active_contact.second_layer_position.scale(1,0)) + height = self.gnd_position[1] - source_posistion2[1] + self.add_rect(layer="metal1", + offset=source_posistion2, + width=drc["minwidth_metal1"], + height=height) + + def connect_poly(self): + """connect connect poly between nmos and pmos""" + # connect pmos1 poly + nmos_gate = (self.nmos_position1 + + self.nmos1.poly_positions[0] + + vector(0.5 * drc["minwidth_poly"], 0)) + for i in range(len(self.pmos1.poly_positions)): + pmos_gate = (self.pmos_position1 + + self.pmos1.poly_positions[i] + + vector(0.5 * drc["minwidth_poly"], 0)) + mid1 = [pmos_gate[0], pmos_gate[1] - drc["poly_to_active"]] + self.add_path("poly", [nmos_gate, mid1, pmos_gate]) + + # connect pmos2 poly + nmos_gate = [self.nmos_position2[0] \ + + self.nmos2.poly_positions[0][0] + + 0.5 * drc["minwidth_poly"], \ + self.nmos_position1[1] \ + + self.nmos1.poly_positions[0][1]] + for i in range(len(self.pmos2.poly_positions)): + pmos_gate = (self.pmos_position2 + + self.pmos2.poly_positions[i] + + vector(0.5 * drc["minwidth_poly"], 0)) + mid1 = [pmos_gate[0], + nmos_gate[1] + self.nmos2.height \ + + drc["poly_to_active"]] + self.add_path("poly", [nmos_gate, mid1, pmos_gate]) + + def route_pins(self): + self.route_input_gate() + self.route_output() + + def route_input_gate(self): + self.route_input_A() + self.route_input_B() + + def route_input_A(self): + """create input A layout""" + xoffset = self.nmos1.poly_positions[0].x + yoffset = self.nmos_position1.y + self.nmos1.height \ + + 0.3 * (self.pmos_position1.y - self.nmos_position1.y \ + - self.nmos1.height) + self.A_position = vector(xoffset, yoffset) + # gate input + offset = self.A_position - vector(0, 0.5 * self.poly_contact.width) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + + # connect gate input to tx gate + offset = self.A_position - vector(self.poly_contact.first_layer_position[1], + 0.5 * self.poly_contact.width) + self.add_rect(layer="poly", + offset=offset, + width=self.poly_contact.first_layer_position[1] + drc["minwidth_poly"], + height=self.poly_contact.first_layer_width) + # extend the metal to the boundary of the cell + input_length = self.A_position[0] + offset = [0, self.A_position[1] - 0.5 * drc["minwidth_metal1"]] + self.add_layout_pin(text="A", + layer="metal1", + offset=offset, + width=input_length, + height=drc["minwidth_metal1"]) + + def route_input_B(self): + """create input B layout """ + xoffset = self.pmos2.poly_positions[0][0] \ + + self.pmos_position2[0] + yoffset = self.A_position[1] \ + + 0.5 * (self.poly_contact.second_layer_width \ + + drc["minwidth_metal1"]) + drc["metal1_to_metal1"] + self.B_position = vector(xoffset, yoffset) + offset = self.B_position - vector(0, 0.5 * self.poly_contact.width) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + + self.add_rect(layer="poly", + offset=offset, + width=-(self.poly_contact.first_layer_position[1] + drc["minwidth_poly"]), + height=self.poly_contact.first_layer_width) + self.add_layout_pin(text="B", + layer="metal1", + offset=[0, + self.B_position[1] - 0.5 * drc["minwidth_metal1"]], + width=self.B_position[0], + height=drc["minwidth_metal1"]) + + def route_output(self): + """route the output to nmos pmos """ + self.Z_position = vector(self.width, self.A_position.y) + # route nmos drain to Z + nmos_contact = (self.nmos_position1 + + self.nmos1.active_contact_positions[1] + + self.nmos1.active_contact.second_layer_position + + vector(self.nmos1.active_contact.second_layer_width, + 0).scale(0.5, 0)) + mid = [nmos_contact[0], self.A_position[1]] + self.add_path("metal1", [self.Z_position, mid, nmos_contact]) + + for i in range(len(self.pmos2.poly_positions) + 1): + if i % 2 == 1: + # pmos2 drain to Z + pmos_contact = (self.pmos_position2 + + self.pmos1.active_contact_positions[i] + + self.pmos2.active_contact.second_layer_position.scale(1, 0) + + vector(self.pmos2.active_contact.second_layer_width, + 0).scale(0.5, 0)) + offset = pmos_contact - vector(0.5 * self.m1m2_via.width, 0) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset) + mid = [pmos_contact[0], self.Z_position[1]] + self.add_wire(("metal2", "via1", "metal1"), + [self.Z_position, mid, pmos_contact]) + + def extend_wells(self): + """ extend well for well contact""" + middle_point = (self.nmos_position1[1] + + self.nmos1.pwell_position[1] + + self.nmos1.well_height + + (self.pmos_position1[1] + + self.pmos1.nwell_position[1] + - self.nmos_position1[1] + - self.nmos1.pwell_position[1] + - self.nmos1.well_height) / 2 ) + self.nwell_position = vector(0, middle_point) + self.nwell_height = self.height - middle_point + self.add_rect(layer="nwell", + offset=self.nwell_position, + width=self.well_width, + height=self.nwell_height) + self.add_rect(layer="vtg", + offset=self.nwell_position, + width=self.well_width, + height=self.nwell_height) + + self.pwell_position = vector(0, 0) + self.pwell_height = middle_point + self.add_rect(layer="pwell", + offset=self.pwell_position, + width=self.well_width, + height=self.pwell_height) + self.add_rect(layer="vtg", + offset=self.pwell_position, + width=self.well_width, + height=self.pwell_height) + + def extend_active(self): + """ extend active for well contact""" + self.active_width = self.pmos1.active_width \ + + drc["active_to_body_active"] \ + + self.pmos1.active_contact.width + offset = (self.pmos_position2.scale(1,0) + + self.pmos_position1.scale(0,1) + + self.pmos1.active_position) + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.pmos1.active_height) + offset = offset + vector(self.pmos1.active_width, 0) + width = self.active_width - self.pmos1.active_width + self.add_rect(layer="nimplant", + offset=offset, + width=width, + height=self.pmos1.active_height) + + offset = (self.nmos1.active_position.scale(1,-1) + + self.nmos_position2.scale(1,0) + + self.nmos_position1.scale(0,1) + + vector(0, self.nmos1.height - self.nmos1.active_height)) + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.nmos1.active_height) + offset = offset + vector(self.nmos1.active_width,0) + width = self.active_width - self.nmos1.active_width + self.add_rect(layer="pimplant", + offset=offset, + width=width, + height=self.nmos1.active_height) diff --git a/compiler/openram.py b/compiler/openram.py new file mode 100755 index 00000000..dace6489 --- /dev/null +++ b/compiler/openram.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python2.7 +""" +SRAM Compiler + +The output files append the given suffixes to the output name: +a spice (.sp) file for circuit simulation +a GDS2 (.gds) file containing the layout +a LEF (.lef) file for preliminary P&R (real one should be from layout) +a Liberty (.lib) file for timing analysis/optimization + +""" + +__author__ = "Matthew Guthaus (mrg@ucsc.edu) and numerous others" +__version__ = "$Revision: 0.9 $" +__copyright__ = "Copyright (c) 2015 UCSC and OSU" +__license__ = "This is not currently licensed for use outside of UCSC's VLSI-DA and OSU's VLSI group." + + +import sys,os +import datetime +import re +import importlib +import globals + +global OPTS + +(OPTS, args) = globals.parse_args() + +# These depend on arguments, so don't load them until now. +import debug + +# required positional args for using openram main exe +if len(args) < 1: + print globals.USAGE + sys.exit(2) + +if OPTS.print_banner: + print globals.BANNER + +globals.init_openram(args[0]) + +# Check if all arguments are integers for bits, size, banks +if type(OPTS.config.word_size)!=int: + debug.error("{0} is not an integer in config file.".format(OPTS.config.word_size)) +if type(OPTS.config.num_words)!=int: + debug.error("{0} is not an integer in config file.".format(OPTS.config.sram_size)) +if type(OPTS.config.num_banks)!=int: + debug.error("{0} is not an integer in config file.".format(OPTS.config.num_banks)) + +if not OPTS.config.tech_name: + debug.error("Tech name must be specified in config file.") + +word_size = OPTS.config.word_size +num_words = OPTS.config.num_words +num_banks = OPTS.config.num_banks + +if (OPTS.out_name == ""): + OPTS.out_name = "sram_{0}_{1}_{2}_{3}".format(word_size, + num_words, + num_banks, + OPTS.tech_name) + +debug.info(1, "Output file is " + OPTS.out_name + ".(sp|gds|v|lib|lef)") + +print "Technology: %s" % (OPTS.tech_name) +print "Word size: {0}\nWords: {1}\nBanks: {2}".format(word_size,num_words,num_banks) + +# only start importing modules after we have the config file +import calibre +import sram + +print "Start: ", datetime.datetime.now() + +# import SRAM test generation +s = sram.sram(word_size=word_size, + num_words=num_words, + num_banks=num_banks, + name=OPTS.out_name) + +# Measure design area +# Not working? +#cell_size = s.gds.measureSize(s.name) +#print "Area:", cell_size[0] * cell_size[1] + +# Output the files for the resulting SRAM + +spname = OPTS.out_path + s.name + ".sp" +print "SP: Writing to {0}".format(spname) +s.sp_write(spname) + +gdsname = OPTS.out_path + s.name + ".gds" +print "GDS: Writing to {0}".format(gdsname) +s.gds_write(gdsname) + +# Run Characterizer on the design +sram_file = spname +if OPTS.run_pex: + sram_file = OPTS.out_path + "temp_pex.sp" + calibre.run_pex(s.name, gdsname, spname, output=sram_file) + + +# geenrate verilog +import verilog +vname = OPTS.out_path + s.name + ".v" +print "Verilog: Writing to {0}".format(vname) +verilog.verilog(vname,s) + +# generate LEF +import lef +lefname = OPTS.out_path + s.name + ".lef" +print "LEF: Writing to {0}".format(lefname) +lef.lef(gdsname,lefname,s) + +# generate lib +import lib +libname = OPTS.out_path + s.name + ".lib" +print "LIB: Writing to {0}".format(libname) +lib.lib(libname,s,sram_file) + + +print "End: ", datetime.datetime.now() diff --git a/compiler/options.py b/compiler/options.py new file mode 100644 index 00000000..616630a0 --- /dev/null +++ b/compiler/options.py @@ -0,0 +1,34 @@ +import optparse +import getpass + +class options(optparse.Values): + """ + Class for holding all of the OpenRAM options. + """ + # This is the technology directory. + openram_tech = "" + # This is the name of the technology. + tech_name = "" + # This is the temp directory where all intermediate results are stored. + openram_temp = "/tmp/openram_{0}_temp/".format(getpass.getuser()) + # This is the verbosity level to control debug information. 0 is none, 1 + # is minimal, etc. + debug_level = 0 + # This determines whether LVS and DRC is checked for each submodule. + check_lvsdrc = True + # Variable to select the variant of spice (hspice or ngspice right now) + spice_version = "hspice" + # Should we print out the banner at startup + print_banner = True + # The Calibre executable being used which is derived from the user PATH. + calibre_exe = "" + # The spice executable being used which is derived from the user PATH. + spice_exe = "" + # Run with extracted parasitics + run_pex = False + # Trim noncritical memory cells for simulation speed-up + trim_noncritical = False + # Define the output file paths + out_path = "" + # Define the output file base name + out_name = "" diff --git a/compiler/path.py b/compiler/path.py new file mode 100644 index 00000000..57409f50 --- /dev/null +++ b/compiler/path.py @@ -0,0 +1,183 @@ +from tech import drc +from tech import layer as techlayer +import debug +import design +from vector import vector +from utils import snap_to_grid + +class path(design.design): + """ + Object metal path; given the layer type + Add a path of minimium metal width between a set of points. + The points should be rectilinear to control the bend points. If + not, it will always go down first. The points are the center of the path. + If width is not given, it uses minimum layer width. + """ + + unique_path_id = 1 + + def __init__(self, layer, position_list, width=None): + name = "path_{0}".format(path.unique_path_id) + path.unique_path_id += 1 + design.design.__init__(self, name) + debug.info(2, "create path obj {0}".format(name)) + + self.name = name + self.layer_name = layer + self.layer_id = techlayer[layer] + if width==None: + self.layer_width = drc["minwidth_{0}".format(layer)] + else: + self.layer_width = width + self.position_list = position_list + self.pins = [] # used for matching parm lengths + self.switch_pos_list = [] + self.create_layout() + + + def create_layout(self): + self.create_rectilinear_route() + self.connect_corner() + self.create_rectangles() + # wires and paths should not be offset to (0,0) + + def create_rectilinear_route(self): + """ Add intermediate nodes if it isn't rectilinear. Also skip + repeated nodes. Also, convert to vector if the aren't.""" + pl = self.position_list + + self.position_list = [] + for index in range(len(pl) - 1): + if pl[index] != pl[index + 1]: + self.position_list.append(vector(pl[index])) + if (pl[index][0] != pl[index + 1][0]) and (pl[index][1] != pl[index + 1][1]): + self.position_list.append(vector(pl[index][0], pl[index + 1][1])) + self.position_list.append(vector(pl[-1])) + + + + def pairwise(self, iterable): + """s -> (s0,s1), (s1,s2), (s2, s3), ...""" + from itertools import tee, izip + a, b = tee(iterable) + next(b, None) + temp = [] + for v in izip(a, b): + temp.append(list(v)) + return temp + + def connect_corner(self): + """ Add a corner square at every corner of the path.""" + pl = self.pairwise(self.position_list) + from itertools import izip + orient = None # orientation toggler + offset = [0, 0] + + for (v, w), index in izip(pl, range(len(pl))): + if index != 0: + if pl[index][1] == pl[index - 1][0]: + if v[0] != w[0]: + offset = [(offset[0] + (w[0] - v[0])), offset[1]] + else: + offset = [offset[0], (offset[1] + w[1] - v[1])] + orient = not orient + continue + if v[0] != w[0]: + if (orient == None): + orient = True + if not orient: + orient = not orient + temp_offset = offset + self.switch_pos_list.append(temp_offset) + via_offset = self.switch_pos_list[-1] + corner_offset = [via_offset[0] - 0.5 * self.layer_width, + via_offset[1] - 0.5 * self.layer_width] + self.draw_corner_wire(corner_offset) + offset = [(offset[0] + (w[0] - v[0])), offset[1]] + elif v[1] != w[1]: + if (orient == None): + orient = False + if orient: + orient = not orient + temp_offset = offset + self.switch_pos_list.append(temp_offset) + via_offset = self.switch_pos_list[-1] + corner_offset = [via_offset[0] - 0.5 * self.layer_width, + via_offset[1] - 0.5 * self.layer_width] + self.draw_corner_wire(corner_offset) + offset = [offset[0], (offset[1] + w[1] - v[1])] + + def draw_corner_wire(self, offset): + """ This function adds the corner squares since the center + line convention only draws to the center of the corner.""" + self.add_rect(layer=self.layer_name, + offset=offset, + width=self.layer_width, + height=self.layer_width) + + def create_rectangles(self): + """ Create the actual rectangles on teh appropriate layers + using the position list of the corners. """ + offset = [0, 0] + # FIXME: These should not be hard coded limits. + xval = [1000000, -1000000] + yval = [1000000, -1000000] + pl = self.position_list # position list + for index in range(len(pl) - 1): + temp_offset = offset + if temp_offset[0] < xval[0]: + xval[0] = temp_offset[0] + if temp_offset[0] > xval[1]: + xval[1] = temp_offset[0] + if temp_offset[1] < yval[0]: + yval[0] = temp_offset[1] + if temp_offset[1] > yval[1]: + yval[1] = temp_offset[1] + if pl[index][0] != pl[index + 1][0]: + line_length = pl[index + 1][0] - pl[index][0] + temp_offset = [temp_offset[0], + temp_offset[1] - 0.5 * self.layer_width] + if line_length < 0: + temp_offset = [temp_offset[0] + line_length, + temp_offset[1]] + self.add_line(layer_name=self.layer_name, + length=abs(line_length), + offset=temp_offset, + orientation="horizontal") + offset = [offset[0] + line_length, + offset[1]] + elif pl[index][1] != pl[index + 1][1]: + line_length = pl[index + 1][1] - pl[index][1] + temp_offset = [temp_offset[0] - 0.5 * self.layer_width, + temp_offset[1]] + if line_length < 0: + temp_offset = [temp_offset[0], + temp_offset[1] + line_length] + self.add_line(layer_name=self.layer_name, + length=abs(line_length), + offset=temp_offset, + orientation="vertical") + + offset = [offset[0], + offset[1] + line_length] + self.width = abs(xval[0] - xval[1]) + self.height = abs(yval[0] - yval[1]) + + + def add_line(self, layer_name, length, offset, orientation): + """ + straight line object with layer_minwidth + (orientation: "vertical" or "horizontal") default is vertical + """ + + layer_width = drc["minwidth_{0}".format(layer_name)] + width = layer_width + height = length + + if orientation == "horizontal": + width = length + height = layer_width + self.add_rect(layer=layer_name, + offset=offset, + width=width, + height=height) diff --git a/compiler/pinv.py b/compiler/pinv.py new file mode 100644 index 00000000..935304bd --- /dev/null +++ b/compiler/pinv.py @@ -0,0 +1,409 @@ +import contact +import design +import debug +from tech import drc, parameter, cell +from ptx import ptx +from vector import vector +from math import ceil +from globals import OPTS + +class pinv(design.design): + """ + This module generates gds of a parametrically sized inverter. + This model use ptx to generate a inverter within a cetrain height. + The inverter's cell_height should be the same as the 6t library cell. + """ + c = reload(__import__(OPTS.config.bitcell)) + bitcell = getattr(c, OPTS.config.bitcell) + + def __init__(self, name, nmos_width=1, beta=3, height=bitcell.chars["height"], route_output=True): + design.design.__init__(self, name) + debug.info(2, "create pinv strcuture {0} with size of {1}".format(name, nmos_width)) + + self.nmos_width = nmos_width + self.beta = beta + self.height = height + self.route_output = route_output + + self.add_pins() + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + """Adds pins for spice netlist processing""" + self.add_pin_list(["A", "Z", "vdd", "gnd"]) + + def create_layout(self): + """Calls all functions related to the generation of the layout(gds)""" + + # These aren't for instantiating, but we use them to get the dimensions + self.poly_contact = contact.contact(("poly", "contact", "metal1")) + self.m1m2_via = contact.contact(("metal1", "via1", "metal2")) + + self.determine_tx_mults() + self.create_ptx() + self.setup_layout_constants() + self.add_rails() + self.add_ptx() + + # These aren't for instantiating, but we use them to get the dimensions + self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"), + dimensions=(1, self.pmos.num_of_tacts)) + self.pwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"), + dimensions=(1, self.nmos.num_of_tacts)) + + self.extend_wells() + self.extend_active() + self.add_well_contacts() + self.connect_well_contacts() + self.connect_rails() + self.connect_tx() + self.route_pins() + self.setup_layout_offsets() + + def determine_tx_mults(self): + """Determines the number of fingers needed to achieve same size with a height constraint""" + # check minimum distance between well + minwidth_poly_contact = drc["minwidth_contact"] \ + + 2 * drc["poly_enclosure_contact"] + + # this should be 2*poly extension beyond active? + minwidth_box_poly = 2 * drc["minwidth_poly"] \ + + drc["poly_to_poly"] + well_to_well = max(drc["pwell_enclose_nwell"], + minwidth_poly_contact, + minwidth_box_poly) + + # determine both mos enclosure sizes + bot_mos_enclosure = 2 * (drc["well_enclosure_active"]) + top_mos_enclosure = 2 * max(drc["well_enclosure_active"], + drc["metal1_to_metal1"] + 0.5 * drc["minwidth_metal1"]) + + self.nmos_size = parameter["min_tx_size"] + self.pmos_size = parameter["min_tx_size"] * self.beta + + # use multi finger if the cell is not big enough + if self.nmos_size <= self.nmos_width: + self.tx_mults = int(ceil(self.nmos_width / self.nmos_size)) + else: + self.tx_mults = 1 + + def create_ptx(self): + """Intiializes a ptx object""" + self.nmos = ptx(name="inv_nmos1", + width=self.nmos_size, + mults=self.tx_mults, + tx_type="nmos") + self.nmos.connect_fingered_poly() + self.nmos.connect_fingered_active() + self.add_mod(self.nmos) + self.pmos = ptx(name="inv_pmos1", + width=self.pmos_size, + mults=self.tx_mults, + tx_type="pmos") + self.pmos.connect_fingered_poly() + self.pmos.connect_fingered_active() + self.add_mod(self.pmos) + + def setup_layout_constants(self): + """Sets up constant variables""" + # the well width is determined the multi-finger PMOS device width plus + # the well contact width and enclosure + self.well_width = self.pmos.active_position[0] \ + + self.pmos.active_width \ + + drc["active_to_body_active"] \ + + self.pmos.active_contact.width \ + + drc["well_enclosure_active"] + self.width = self.well_width + + def add_rails(self): + """Adds vdd/gnd rails to the layout""" + rail_width = self.width + rail_height = self.rail_height = drc["minwidth_metal1"] + + self.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"]) # for tiling purposes + self.add_layout_pin(text="gnd", + layer="metal1", + offset=self.gnd_position, + width=rail_width, + height=rail_height) + + self.vdd_position = vector(0, self.height - 0.5 * drc["minwidth_metal1"]) + self.add_layout_pin(text="vdd", + layer="metal1", + offset=self.vdd_position, + width=rail_width, + height=rail_height) + + def add_ptx(self): + """Adds pmos and nmos to the layout""" + # determines the spacing between the edge and nmos (rail to active + # metal or poly_to_poly spacing) + edge_to_nmos = max(drc["metal1_to_metal1"] \ + - self.nmos.active_contact_positions[0][1], + 0.5 * drc["poly_to_poly"] \ + - 0.5 * drc["minwidth_metal1"] \ + - self.nmos.poly_positions[0][1]) + self.nmos_position = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos) + offset = self.nmos_position + vector(0,self.nmos.height) + self.add_inst(name="pinv_nmos", + mod=self.nmos, + offset=offset, + mirror="MX") + self.connect_inst(["Z", "A", "gnd", "gnd"]) + + # determines the spacing between the edge and pmos + edge_to_pmos = max(drc["metal1_to_metal1"] \ + - self.pmos.active_contact_positions[0][1], + 0.5 * drc["poly_to_poly"] \ + - 0.5 * drc["minwidth_metal1"] \ + - self.pmos.poly_positions[0][1]) + self.pmos_position = vector(0, + self.height - 0.5 * drc["minwidth_metal1"] + - edge_to_pmos - self.pmos.height) + self.add_inst(name="pinv_pmos", + mod=self.pmos, + offset=self.pmos_position) + self.connect_inst(["Z", "A", "vdd", "vdd"]) + + def extend_wells(self): + """Extends the n/p wells to cover whole layout""" + nmos_top_yposition = self.nmos_position[1] + self.nmos.height + # calculates the length between the pmos and nmos + middle_length = self.pmos_position[1] - nmos_top_yposition + # calculate the middle point between the pmos and nmos + middle_yposition = nmos_top_yposition + 0.5 * middle_length + + self.nwell_position = vector(0, middle_yposition) + self.nwell_height = self.height - middle_yposition + self.add_rect(layer="nwell", + offset=self.nwell_position, + width=self.well_width, + height=self.nwell_height) + self.add_rect(layer="vtg", + offset=self.nwell_position, + width=self.well_width, + height=self.nwell_height) + + self.pwell_position = [0, 0] + self.pwell_height = middle_yposition + self.add_rect(layer="pwell", + offset=self.pwell_position, width=self.well_width, + height=self.pwell_height) + self.add_rect(layer="vtg", + offset=self.pwell_position, + width=self.well_width, + height=self.pwell_height) + + def extend_active(self): + """Extends the active area for n/p mos for the addition of the n/p well taps""" + # calculates the new active width that includes the well_taps + self.active_width = self.pmos.active_width \ + + drc["active_to_body_active"] \ + + self.pmos.active_contact.width + + # Calculates the coordinates of the bottom left corner of active area + # of the pmos + offset = self.pmos_position + self.pmos.active_position + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.pmos.active_height) + + # Determines where the active of the well portion starts to add the + # implant + offset = offset + vector(self.pmos.active_width,0) + implant_width = self.active_width - self.pmos.active_width + self.add_rect(layer="nimplant", + offset=offset, + width=implant_width, + height=self.pmos.active_height) + + # Calculates the coordinates of the bottom left corner of active area + # of the nmos + offset = self.nmos_position + self.nmos.active_position + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.nmos.active_height) + + # Determines where the active of the well portion starts to add the + # implant + offset = offset + vector(self.pmos.active_width,0) + implant_width = self.active_width - self.nmos.active_width + self.add_rect(layer="pimplant", + offset=offset, + width=implant_width, + height=self.nmos.active_height) + + def connect_poly(self): + """Connects the poly from nmos to pmos (as well if it is multi-fingered)""" + # Calculates the y-coordinate of the top of the poly of the nmos + nmos_top_poly_yposition = self.nmos_position[1] \ + + self.nmos.height \ + - self.nmos.poly_positions[0][1] + + poly_length = self.pmos_position[1] + self.pmos.poly_positions[0][1] \ + - nmos_top_poly_yposition + for position in self.pmos.poly_positions: + offset = [position[0], nmos_top_poly_yposition] + self.add_rect(layer="poly", + offset=offset, + width=drc["minwidth_poly"], + height=poly_length) + + def connect_drains(self): + """Connects the drains of the nmos/pmos together""" + # Determines the top y-coordinate of the nmos drain metal layer + yoffset = self.nmos.height \ + - 0.5 * drc["minwidth_metal1"] \ + - self.nmos.active_contact_positions[0][1] + drain_length = self.height - yoffset + drc["minwidth_metal1"] \ + - (self.pmos.height + - self.pmos.active_contact_positions[0][1]) + for position in self.pmos.active_contact_positions[1:][::2]: + offset = [position[0] + self.pmos.active_contact.second_layer_position[0], + yoffset] + self.drain_position = offset + self.add_rect(layer="metal1", + offset=offset, + width=self.nmos.active_contact.second_layer_width, + height=drain_length) + + def route_input_gate(self): + """Routes the input gate to the left side of the cell for access""" + + xoffset = self.pmos.poly_positions[0][0] + # Determines the y-coordinate of where to place the gate input poly pin + # (middle in between the pmos and nmos) + yoffset = self.nmos.height + (self.height + - self.pmos.height - self.nmos.height + - self.poly_contact.width) / 2 + offset = [xoffset, yoffset] + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + + # Determines the poly coordinate to connect to the poly contact + offset = offset - self.poly_contact.first_layer_position.rotate().scale(1,0) + self.add_rect(layer="poly", + offset=offset, + width=self.poly_contact.first_layer_position[1] + drc["minwidth_poly"], + height=self.poly_contact.first_layer_width) + + input_length = self.pmos.poly_positions[0][0] \ + - self.poly_contact.height + # Determine the y-coordinate for the placement of the metal1 via + self.input_position = vector(0, .5*(self.height - drc["minwidth_metal1"] + + self.nmos.height - self.pmos.height)) + self.add_layout_pin(text="A", + layer="metal1", + offset=self.input_position, + width=input_length, + height=drc["minwidth_metal1"]) + + def route_output_drain(self): + """Routes the output (drain) to the right side of the cell for access""" + # Determines the y-coordinate of the output metal1 via pin + offset = vector(self.drain_position[0] + + self.nmos.active_contact.second_layer_width, + self.input_position[1]) + output_length = self.width - offset.x + if self.route_output == True: + self.output_position = offset + vector(output_length,0) + self.add_rect(layer="metal1", + offset=offset, + width=output_length, + height=drc["minwidth_metal1"]) + else: + self.output_position = offset + self.add_label(text="Z", + layer="metal1", + offset=offset) + + def add_well_contacts(self): + """Adds n/p well taps to the layout""" + layer_stack = ("active", "contact", "metal1") + # Same y-positions of the drain/source metals as the n/p mos + nwell_tap_xposition = self.pmos_position[0] \ + + self.pmos.active_position[0] \ + + self.active_width \ + - self.nwell_contact.width + nwell_tap_yposition = self.pmos_position[1] \ + + self.pmos.active_contact_positions[0][1] + self.nwell_contact_position = [nwell_tap_xposition, nwell_tap_yposition] + self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos.num_of_tacts)) + + pwell_tap_xposition = self.nmos_position[0] \ + + self.nmos.active_position[0] \ + + self.active_width \ + - self.pwell_contact.width + pwell_tap_yposition = self.nmos_position[1] \ + + self.nmos.active_contact_positions[0][1] + self.pwell_contact_position = [pwell_tap_xposition, pwell_tap_yposition] + self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos.num_of_tacts)) + + def connect_well_contacts(self): + """Connects the well taps to its respective power rails""" + # calculates the length needed to connect the nwell_tap to vdd + nwell_tap_length = self.height \ + - 0.5 * drc["minwidth_metal1"] \ + - self.nwell_contact_position[1] + # obtains the position for the metal 1 layer in the nwell_tap + offset = self.nwell_contact_position + \ + self.nwell_contact.second_layer_position.scale(1,0) + self.add_rect(layer="metal1", + offset=offset, width=self.nwell_contact.second_layer_width, + height=nwell_tap_length) + + pwell_tap_length = self.pwell_contact_position[1] \ + + 0.5 * drc["minwidth_metal1"] + offset = [self.pwell_contact_position[0] + + self.pwell_contact.second_layer_position[0], + 0.5 * drc["minwidth_metal1"]] + self.add_rect(layer="metal1", + offset=offset, + width=self.pwell_contact.second_layer_width, + height=pwell_tap_length) + + def connect_rails(self): + """Connects the n/p mos to its respective power rails""" + if self.tx_mults != 1: + return + # corrects offset to obtain real coordinates of the layer in question + # in a contact object + correct = vector(self.nmos.active_contact.width + - self.nmos.active_contact.width, + - drc["minwidth_metal1"]).scale(.5,.5) + # nmos position of the source metals + noffset = self.nmos_position + self.nmos.active_contact_positions[0] + correct + offset = [self.nmos.active_contact.second_layer_position[0] + noffset[0], + 0.5 * drc["minwidth_metal1"]] + self.add_rect(layer="metal1", offset=offset, + width=self.nmos.active_contact.second_layer_width, + height=noffset.y) + # corrects offset to obtain real coordinates of the layer in question + # in a contact object + correct = vector(self.pmos.active_contact.width + - self.pmos.active_contact.width, + self.pmos.active_contact.second_layer_height).scale(.5,1) + # pmos position of the source metals + offset = self.pmos_position + self.pmos.active_contact_positions[0]\ + + correct + self.pmos.active_contact.second_layer_position + temp_height = self.height - offset[1] - 0.5 * drc["minwidth_metal1"] + self.add_rect(layer="metal1", + offset=offset, + width=self.pmos.active_contact.second_layer_width, + height=temp_height) + + def connect_tx(self): + self.connect_poly() + self.connect_drains() + + def route_pins(self): + self.route_input_gate() + self.route_output_drain() + + def setup_layout_offsets(self): + self.A_position = self.input_position + self.Z_position = self.output_position diff --git a/compiler/precharge.py b/compiler/precharge.py new file mode 100644 index 00000000..4241abc5 --- /dev/null +++ b/compiler/precharge.py @@ -0,0 +1,300 @@ +from contact import contact +import design +import debug +from tech import drc, cell +from ptx import ptx +from vector import vector +from globals import OPTS + +class precharge(design.design): + """ + Creates a single precharge cell + This module implements the precharge bitline cell used in the design. + """ + + def __init__(self, name, ptx_width, beta=2): + design.design.__init__(self, name) + debug.info(2, "create single precharge cell: {0}".format(name)) + + c = reload(__import__(OPTS.config.bitcell)) + self.mod_bitcell = getattr(c, OPTS.config.bitcell) + self.bitcell_chars = self.mod_bitcell.chars + + self.ptx_width = ptx_width + self.beta = beta + + self.add_pins() + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + self.add_pin_list(["bl", "br", "clk", "vdd"]) + + def create_layout(self): + self.create_ptx() + self.create_contacts() + self.setup_layout_constants() + self.add_ptx() + self.connect_poly() + self.add_pclk() + self.add_nwell_contact() + self.extend_nwell() + self.add_vdd_rail() + self.add_bitlines() + self.connect_to_bitlines() + + def create_ptx(self): + """Initializes the upper and lower pmos""" + self.lower_pmos = ptx(name="lower_pmos", + width=self.ptx_width, + mults=1, + tx_type="pmos") + self.add_mod(self.lower_pmos) + self.upper_pmos = ptx(name="upper_pmos", + width=self.beta * self.ptx_width, + mults=1, + tx_type="pmos") + self.upper_pmos = self.upper_pmos + self.add_mod(self.upper_pmos) + self.temp_pmos = ptx(name="temp_upper_pmos", + width=self.beta * self.ptx_width, + mults=2, + tx_type="pmos") + self.temp_pmos.remove_source_connect() + self.temp_pmos.remove_drain_connect() + + def create_contacts(self): + """Initializes all required contacts/vias for this module""" + # These aren't for instantiating, but we use them to get the dimensions + self.nwell_contact = contact(layer_stack=("active", "contact", "metal1")) + self.poly_contact = contact(layer_stack=("poly", "contact", "metal1")) + self.upper_dimensions = self.upper_pmos.active_contact.dimensions + self.lower_dimensions = self.lower_pmos.active_contact.dimensions + self.upper_contact = contact(layer_stack=("metal1", "via1", "metal2"), + dimensions=self.upper_dimensions) + self.lower_contact = contact(layer_stack=("metal1", "via1", "metal2"), + dimensions=self.lower_dimensions) + + def setup_layout_constants(self): + self.width = self.bitcell_chars["width"] + self.BL_position = vector(self.bitcell_chars["BL"][0], 0) + self.BR_position = vector(self.bitcell_chars["BR"][0], 0) + + def add_ptx(self): + """Adds both the upper_pmos and lower_pmos to the module""" + # adds the lower pmos to layout + base = vector(self.width - self.temp_pmos.width, 0).scale(0.5,0) + self.lower_pmos_position = base + vector([drc["metal1_to_metal1"]]*2) + self.add_inst(name="lower_pmos", + mod=self.lower_pmos, + offset=self.lower_pmos_position) + self.connect_inst(["bl", "clk", "br", "vdd"]) + + # adds the upper pmos(s) to layout + ydiff = (self.lower_pmos.height + + 2 * drc["metal1_to_metal1"] + + self.poly_contact.width) + self.upper_pmos_position = self.lower_pmos_position + vector(0, ydiff) + self.add_inst(name="upper_pmos1", + mod=self.upper_pmos, + offset=self.upper_pmos_position) + self.connect_inst(["bl", "clk", "vdd", "vdd"]) + + xdiff =(self.upper_pmos.active_contact_positions[-1].x + - self.upper_pmos.active_contact_positions[0].x) + self.upper_pmos_position2 = self.upper_pmos_position + vector(xdiff, 0) + self.add_inst(name="upper_pmos2", + mod=self.upper_pmos, + offset=self.upper_pmos_position2) + self.connect_inst(["br", "clk", "vdd", "vdd"]) + + def connect_poly(self): + """Connects the upper and lower pmos together""" + offset = (self.lower_pmos_position + + self.lower_pmos.poly_positions[0] + + vector(0,self.lower_pmos.poly_height)) + # connects the top and bottom pmos' gates together + ylength = (self.upper_pmos_position.y + + self.upper_pmos.poly_positions[0].y + - offset.y) + self.add_rect(layer="poly", + offset=offset, + width=drc["minwidth_poly"], + height=ylength) + + # connects the two poly for the two upper pmos(s) + offset = offset + vector(0, ylength - drc["minwidth_poly"]) + xlength = (self.temp_pmos.poly_positions[-1].x + - self.temp_pmos.poly_positions[0].x + + drc["minwidth_poly"]) + self.add_rect(layer="poly", + offset=offset, + width=xlength, + height=drc["minwidth_poly"]) + + def add_pclk(self): + """Adds the pclk input rail, pclk contact/vias, and connects to the pmos""" + # adds the pclk contact to connect the gates to the pclk rail on metal1 + offset = vector(self.lower_pmos_position.x + self.lower_pmos.poly_positions[0].x + + 0.5 * self.poly_contact.height, + self.upper_pmos_position.y - drc["metal1_to_metal1"] \ + - self.poly_contact.width) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset, + rotate=90) + + # adds the pclk rail on metal1 + offset.y= offset.y + self.poly_contact.second_layer_position.x + self.pclk_position = vector(0, offset.y) + self.add_layout_pin(text="clk", + layer="metal1", + offset=self.pclk_position, + width=self.width, + height=drc["minwidth_metal1"]) + + def add_nwell_contact(self): + """Adds a nwell tap to connect to the vdd rail""" + # adds the contact from active to metal1 + offset = vector(self.temp_pmos.active_contact_positions[1].x, + self.upper_pmos.height + drc["well_extend_active"] + - self.nwell_contact.first_layer_position.y) + self.nwell_contact_position = offset + self.upper_pmos_position + self.add_contact(layers=("active", "contact", "metal1"), + offset=self.nwell_contact_position) + + # adds the implant to turn the contact into a nwell tap + offset = self.nwell_contact_position + self.nwell_contact.first_layer_position + xlength = self.nwell_contact.first_layer_width + ylength = self.nwell_contact.first_layer_height + self.add_rect(layer="nimplant", + offset=offset, + width=xlength, + height=ylength) + + def extend_nwell(self): + """Extends the nwell for the whole module""" + self.nwell_position = self.pclk_position.scale(1,0) + self.height = (self.nwell_contact_position.y + self.nwell_contact.height + - self.nwell_contact.first_layer_position.y + + drc["well_extend_active"]) + self.add_rect(layer="nwell", + offset=self.nwell_position, + width=self.width, + height=self.height) + + def add_vdd_rail(self): + """Adds a vdd rail at the top of the cell""" + # adds the rail across the width of the cell + self.vdd_position = vector(self.pclk_position[0], + self.height - drc["minwidth_metal1"]) + self.add_layout_pin(text="vdd", + layer="metal1", + offset=self.vdd_position, + width=self.width, + height=drc["minwidth_metal1"]) + + # connects the upper pmos(s) to the vdd rail + upper_pmos_contact = (self.upper_pmos.active_contact_positions[1] + + self.upper_pmos_position) + ylength = self.height - upper_pmos_contact.y + offset = vector(self.nwell_contact_position.x + + self.nwell_contact.second_layer_position.x, + self.upper_pmos.active_contact.second_layer_position.y + + upper_pmos_contact.y) + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=ylength) + + def add_bitlines(self): + """Adds both bit-line and bit-line-bar to the module""" + # adds the BL on metal 2 + offset = self.BL_position - vector(0.5 * drc["minwidth_metal2"],0) + self.add_layout_pin(text="bl", + layer="metal2", + offset=offset, + width=drc['minwidth_metal2'], + height=self.height) + + # adds the BR on metal 2 + offset = self.BR_position - vector(0.5 * drc["minwidth_metal2"],0) + self.add_layout_pin(text="br", + layer="metal2", + offset=offset, + width=drc['minwidth_metal2'], + height=self.height) + + def connect_to_bitlines(self): + self.add_bitline_contacts() + dest =[self.upper_pmos,self.upper_pmos_position,self.upper_contact] + correct_x = self.xcorrect_upper + self.upper_contact.second_layer_position.x + self.connect_pmos_to_BL(correct_x,dest) + correct_x = correct_x + self.temp_pmos.active_contact_positions[-1].x + self.connect_pmos_to_BR(correct_x,dest) + + dest =[self.lower_pmos,self.lower_pmos_position,self.lower_contact] + correct_x = self.xcorrect_lower + self.lower_contact.first_layer_position.x + self.connect_pmos_to_BL(correct_x,dest) + correct_x = correct_x + self.lower_pmos.active_contact_positions[-1].x + self.connect_pmos_to_BR(correct_x,dest) + + def add_bitline_contacts(self): + """Adds contacts/via from metal1 to metal2 for bit-lines""" + # helps centers the via over the underneath contact + self.xcorrect_upper = 0.5 * abs(self.upper_contact.width + - self.upper_pmos.active_contact.width) + self.xcorrect_lower = 0.5 * abs(self.lower_contact.width + - self.lower_pmos.active_contact.width) + + # adds contacts from metal1 to metal2 over the active contacts for the + # upper pmos(s) + offset = (self.upper_pmos_position + + self.upper_pmos.active_contact_positions[0] + + vector(self.xcorrect_upper,0)) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + size=self.upper_dimensions) + offset.x = (self.upper_pmos_position.x + + self.temp_pmos.active_contact_positions[-1].x + + self.xcorrect_upper) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + size=self.upper_dimensions) + + # adds contacts from metal1 to metal 2 over active contacts for the + # lower pmos + offset = (self.lower_pmos_position + + self.lower_pmos.active_contact_positions[0] + + vector(self.xcorrect_upper,0)) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + size=self.lower_dimensions) + offset.x = (self.lower_pmos_position.x + + self.lower_pmos.active_contact_positions[-1].x + + self.xcorrect_lower) + self.add_contact(layers=("metal1", "via1", "metal2"), + offset=offset, + size=self.lower_dimensions) + + def connect_pmos_to_BL(self,correct_x,dest): + """Connects bit-lines to lower_pmos""" + mos,mos_pos,contact = dest + mos_active = (mos_pos + mos.active_contact_positions[0]) + offset = vector(self.BL_position.x, mos_active.y) + xlength = (mos_active.x + correct_x - self.BL_position.x + + 0.5 * drc["minwidth_metal2"]) + self.add_rect(layer="metal2", + offset=offset, + width=xlength, + height=contact.height) + + def connect_pmos_to_BR(self,correct_x,dest): + """Connects bit-lines to the upper_pmos""" + mos,mos_pos,contact = dest + offset = mos_pos + vector(correct_x, + mos.active_contact_positions[0].y) + xlength = self.BR_position.x - offset.x - 0.5 * drc["minwidth_metal2"] + self.add_rect(layer="metal2", + offset=offset, + width=xlength, + height=contact.height) diff --git a/compiler/precharge_array.py b/compiler/precharge_array.py new file mode 100644 index 00000000..1578eca8 --- /dev/null +++ b/compiler/precharge_array.py @@ -0,0 +1,97 @@ +import design +import debug +from tech import drc +from vector import vector +from precharge import precharge + + +class precharge_array(design.design): + """ + Dynamically generated precharge array of all bitlines. Cols is number + of bit line columns, height is the height of the bit-cell array. + """ + + def __init__(self, name, columns, ptx_width, beta=2): + design.design.__init__(self, name) + debug.info(1, "Creating {0}".format(name)) + + self.columns = columns + self.ptx_width = ptx_width + self.beta = beta + + self.add_pins() + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + """Adds pins for spice file""" + for i in range(self.columns): + self.add_pin("bl[{0}]".format(i)) + self.add_pin("br[{0}]".format(i)) + self.add_pin("clk") + self.add_pin("vdd") + + def create_layout(self): + self.create_pc_cell() + self.setup_layout_constants() + self.add_pc() + self.add_rails() + self.offset_all_coordinates() + + def setup_layout_constants(self): + self.vdd_positions = [] + self.BL_positions = [] + self.BR_positions = [] + + self.width = self.columns * self.pc_cell.width + self.height = self.pc_cell.height + + def add_rails(self): + self.add_vdd_rail() + self.add_pclk_rail() + + def add_vdd_rail(self): + offset = self.pc_cell.vdd_position + self.add_layout_pin(text="vdd", + layer="metal1", + offset=offset, + width=self.width, + height=drc["minwidth_metal1"]) + self.vdd_positions.append(offset) + + def add_pclk_rail(self): + self.pclk_position = self.pc_cell.pclk_position + self.add_layout_pin(text="clk", + layer="metal1", + offset=self.pclk_position, + width=self.width, + height=drc["minwidth_metal1"]) + + def create_pc_cell(self): + """Initializes a single precharge cell""" + self.pc_cell = precharge(name="precharge_cell", + ptx_width=self.ptx_width, + beta=self.beta) + self.add_mod(self.pc_cell) + + def add_pc(self): + """Creates a precharge array by horizontally tiling the precharge cell""" + self.pc_cell_positions = [] + for i in range(self.columns): + name = "pre_column_{0}".format(i) + offset = vector(self.pc_cell.width * i, 0) + self.pc_cell_positions.append(offset) + self.add_inst(name=name, + mod=self.pc_cell, + offset=offset) + self.add_label(text="bl[{0}]".format(i), + layer="metal2", + offset=offset+ self.pc_cell.BL_position.scale(1,0)) + self.add_label(text="br[{0}]".format(i), + layer="metal2", + offset=offset+ self.pc_cell.BR_position.scale(1,0)) + self.connect_inst(["bl[{0}]".format(i), "br[{0}]".format(i), + "clk", "vdd"]) + + self.BL_positions.append(offset + self.pc_cell.BL_position.scale(1,0)) + self.BR_positions.append(offset + self.pc_cell.BR_position.scale(1,0)) diff --git a/compiler/ptx.py b/compiler/ptx.py new file mode 100644 index 00000000..f6d29027 --- /dev/null +++ b/compiler/ptx.py @@ -0,0 +1,306 @@ +import design +import debug +from tech import drc, info, spice +from vector import vector +from contact import contact + +class ptx(design.design): + """ + This module generates gds and spice of a parametrically NMOS or PMOS sized transistor. + Creates a simple MOS transistor + """ + # This is used to create a unique MOS ID name by avoiding collisions + unique_mos_id = 1 + + def __init__(self, name, width=1, mults=1, tx_type="nmos"): + name = "{0}{1}".format(name, ptx.unique_mos_id) + ptx.unique_mos_id += 1 + design.design.__init__(self, name) + debug.info(2, "create ptx structure {0}".format(name)) + + self.tx_type = tx_type + self.mults = mults + self.gate_width = width + + self.add_pins() + self.create_layout() + self.create_spice() + self.DRC() + + def add_pins(self): + self.add_pin_list(["D", "G", "S", "B"]) + + def create_layout(self): + self.setup_layout_constants() + + # This is not actually instantiated but used for calculations + self.num_of_tacts = self.calculate_num_of_tacts() + self.active_contact = contact(layer_stack=("active", "contact", "metal1"), + dimensions=(1, self.num_of_tacts)) + + self.add_active() + self.add_implants() + self.add_poly() + self.add_active_contacts() + # rather than connect these, we let the user of the ptx + # decide to call them. + # self.determine_active_wire_location() + # self.connect_fingered_active() + # self.connect_fingered_poly() + self.offset_all_coordinates() + + def offset_all_coordinates(self): + coordinate = self.find_lowest_coords() + self.offset_attributes(coordinate) + self.translate(coordinate) + + # We can do this in ptx because we have offset all modules it uses. + # Is this really true considering the paths that connect the src/drain? + self.height = max(max(obj.offset[1] + obj.height for obj in self.objs), + max(inst.offset[1] + inst.mod.height for inst in self.insts)) + self.width = max(max(obj.offset[0] + obj.width for obj in self.objs), + max(inst.offset[0] + inst.mod.width for inst in self.insts)) + + def create_spice(self): + self.spice.append("\n.SUBCKT {0} {1}".format(self.name, + " ".join(self.pins))) + self.spice.append("M{0} {1} {2} m={3} w={4}u l={5}u".format(self.tx_type, + " ".join(self.pins), + spice[self.tx_type], + self.mults, + self.gate_width, + drc["minwidth_poly"])) + self.spice.append(".ENDS {0}".format(self.name)) + + def setup_layout_constants(self): + # usually contacted poly will limit the spacing, but it could be poly + # spacing in some weird technology. + self.mults_poly_to_poly = max(2 * drc["contact_to_poly"] + drc["minwidth_contact"], + drc["poly_to_poly"]) + outeractive_to_contact = max(drc["active_enclosure_contact"], + (drc["minwidth_active"] - drc["minwidth_contact"]) / 2) + self.active_width = 2 * (outeractive_to_contact \ + + drc["minwidth_contact"] \ + + drc["contact_to_poly"]) \ + + drc["minwidth_poly"] \ + + (self.mults - 1) * ( + self.mults_poly_to_poly + drc["minwidth_poly"]) + self.active_height = max(drc["minarea_active"] / self.active_width, + self.gate_width) + self.poly_width = drc["minwidth_poly"] # horizontal + self.poly_height = max(drc["minarea_poly"] / self.poly_width, + self.gate_width \ + + 2 * drc["poly_extend_active"]) # vertical + self.well_width = self.active_width \ + + 2 * (drc["well_enclosure_active"]) + self.well_height = max(self.gate_width + 2 * (drc["well_enclosure_active"]), + drc["minwidth_well"]) + + def connect_fingered_poly(self): + poly_connect_length = self.poly_positions[-1][0] + self.poly_width \ + - self.poly_positions[0][0] + poly_connect_position = [self.poly_positions[0][0], + self.poly_positions[0][1] - self.poly_width] + if len(self.poly_positions) > 1: + self.add_rect(layer="poly", + offset=poly_connect_position, + width=poly_connect_length, + height=drc["minwidth_poly"]) + self.poly_connect_index = len(self.objs) - 1 + + def pairwise(self, iterable): + #"s -> (s0,s1), (s1,s2), (s2, s3), ..." + from itertools import tee, izip + a, b = tee(iterable) + next(b, None) + return izip(a, b) + + def determine_active_wire_location(self): + self.determine_source_wire() + self.determine_drain_wire() + + def determine_source_wire(self): + self.source_positions = [] + source_contact_pos = self.active_contact_positions[0:][::2] # even indexes + for a, b in self.pairwise(source_contact_pos): + correct=vector(0.5 * (self.active_contact.width - + drc["minwidth_metal1"] + drc["minwidth_contact"]), + 0.5 * (self.active_contact.height - drc["minwidth_contact"]) + - drc["metal1_extend_contact"]) + connected=vector(b[0] + drc["minwidth_metal1"], + a[1] + self.active_contact.height + drc["metal1_to_metal1"]) + self.source_positions.append(a + correct) + self.source_positions.append(vector(a.x + correct.x, connected.y)) + self.source_positions.append(vector(b.x + correct.x, + connected.y + 0.5 * drc["minwidth_metal2"])) + self.source_positions.append(b + correct) + + def determine_drain_wire(self): + self.drain_positions = [] + drain_contact_pos = self.active_contact_positions[1:][::2] # odd indexes + for c, d in self.pairwise(drain_contact_pos): + correct = vector(0.5*(self.active_contact.width + - drc["minwidth_metal1"] + + drc["minwidth_contact"]), + 0.5*(self.active_contact.height - drc["minwidth_contact"]) + - drc["metal1_extend_contact"]) + connected = vector(d[0] + drc["minwidth_metal1"], c[1] - drc["metal1_to_metal1"]) + self.drain_positions.append(vector(c + correct)) + self.drain_positions.append(vector(c[0] + correct.x, connected.y)) + self.drain_positions.append(vector(d[0] + correct.x, + connected.y - 0.5 * drc["minwidth_metal1"])) + self.drain_positions.append(vector(d + correct)) + + def connect_fingered_active(self): + self.determine_active_wire_location() + # allows one to connect the source and drains + self.source_connect_index = None + if self.source_positions: + self.add_path(("metal1"), self.source_positions) + self.source_connect_index = len(self.insts) - 1 + self.drain_connect_index = None + if self.drain_positions: + self.add_path(("metal1"), self.drain_positions) + self.drain_connect_index = len(self.insts) - 1 + + def add_poly(self): + # left_most poly + poly_xoffset = self.active_contact.via_layer_position[0] \ + + drc["minwidth_contact"] + drc["contact_to_poly"] + poly_yoffset = -drc["poly_extend_active"] + self.poly_positions = [] + # following poly(s) + for i in range(0, self.mults): + self.add_rect(layer="poly", + offset=[poly_xoffset, poly_yoffset], + width=self.poly_width, + height=self.poly_height) + self.poly_positions.append(vector(poly_xoffset, poly_yoffset)) + poly_xoffset += self.mults_poly_to_poly + drc["minwidth_poly"] + + def add_active(self): + """Adding the diffusion (active region = diffusion region)""" + offset = self.active_position = [0, 0] + self.add_rect(layer="active", + offset=offset, + width=self.active_width, + height=self.active_height) + + def add_implants(self): + if self.tx_type == "nmos": + self.add_nmos_implants() + elif self.tx_type == "pmos": + self.add_pmos_implants() + + def add_nmos_implants(self): + offset = self.pwell_position = [-drc["well_enclosure_active"], -drc["well_enclosure_active"]] + if info["has_pwell"]: + self.add_rect(layer="pwell", + offset=offset, + width=self.well_width, + height=self.well_height) + self.add_rect(layer="vtg", + offset=offset, + width=self.well_width, + height=self.well_height) + xlength = self.active_width + ylength = self.active_height + self.add_rect(layer="nimplant", + offset=self.active_position, + width=xlength, + height=ylength) + + def add_pmos_implants(self): + offset = self.nwell_position = [-drc["well_enclosure_active"], -drc["well_enclosure_active"]] + if info["has_nwell"]: + self.add_rect(layer="nwell", + offset=offset, + width=self.well_width, + height=self.well_height) + self.add_rect(layer="vtg", + offset=offset, + width=self.well_width, + height=self.well_height) + xlength = self.active_width + ylength = self.active_height + self.add_rect(layer="pimplant", + offset=self.active_position, + width=xlength, + height=ylength) + + def calculate_num_of_tacts(self): + """ Calculates the possible number of source/drain contacts in a column """ + possible_length = self.active_height \ + - 2 * drc["active_extend_contact"] + y = 1 + while True: + temp_length = (y * drc["minwidth_contact"]) \ + + ((y - 1) * drc["contact_to_contact"]) + if round(possible_length - temp_length, 6) < 0: + return y - 1 + y += 1 + + def add_active_contacts(self): + self.active_contact_positions = [] + + # left_most contact column + contact_xoffset = 0 + contact_yoffset = (self.active_height \ + - self.active_contact.height) / 2 + offset = vector(contact_xoffset, contact_yoffset) + self.add_contact(layers=("active", "contact", "metal1"), + offset=offset, + size=(1, self.num_of_tacts)) + self.active_contact_positions.append(offset) + + # middle contact columns + for i in range(self.mults - 1): + contact_xoffset = self.poly_positions[i][0] + self.poly_width \ + + (self.mults_poly_to_poly / 2) \ + - (drc["minwidth_contact"] / 2) - \ + self.active_contact.via_layer_position[0] + offset = vector(contact_xoffset, contact_yoffset) + self.add_contact(layers=("active", "contact", "metal1"), + offset=offset, + size=(1, self.num_of_tacts)) + + self.active_contact_positions.append(offset) + + # right_most contact column + contact_xoffset = self.poly_positions[-1][0] \ + + self.poly_width + drc["contact_to_poly"] - \ + self.active_contact.via_layer_position[0] + offset = vector(contact_xoffset, contact_yoffset) + self.add_contact(layers=("active", "contact", "metal1"), + offset=offset, + size=(1, self.num_of_tacts)) + self.active_contact_positions.append(offset) + + + def remove_drain_connect(self): + # FIXME: This is horrible exception handling! + try: + del self.insts[self.drain_connect_index] + del self.drain_connect_index + self.offset_all_coordinates() + except: + pass + + def remove_source_connect(self): + # FIXME: This is horrible exception handling! + try: + del self.insts[self.source_connect_index] + del self.source_connect_index + if isinstance(self.drain_connect_index, int): + self.drain_connect_index -= 1 + self.offset_all_coordinates() + except: + pass + + def remove_poly_connect(self): + # FIXME: This is horrible exception handling! + try: + del self.objs[self.poly_connect_index] + self.offset_all_coordinates() + except: + pass diff --git a/compiler/replica_bitcell.py b/compiler/replica_bitcell.py new file mode 100644 index 00000000..dd02a167 --- /dev/null +++ b/compiler/replica_bitcell.py @@ -0,0 +1,22 @@ +import design +import debug +import utils +from tech import GDS,layer + +class replica_bitcell(design.design): + """ + A single bit cell (6T, 8T, etc.) + This module implements the single memory cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. """ + + pins = ["BL", "BR", "WL", "vdd", "gnd"] + chars = utils.auto_measure_libcell(pins, "replica_cell_6t", GDS["unit"], layer["boundary"]) + + def __init__(self, name="replica_cell_6t"): + design.design.__init__(self, name) + debug.info(2, "Create bitcell object") + + + self.width = replica_bitcell.chars["width"] + self.height = replica_bitcell.chars["height"] diff --git a/compiler/replica_bitline.py b/compiler/replica_bitline.py new file mode 100644 index 00000000..9e3e70b9 --- /dev/null +++ b/compiler/replica_bitline.py @@ -0,0 +1,427 @@ +import debug +import design +from tech import drc, cell +from pinv import pinv +from contact import contact +from bitcell_array import bitcell_array +from nor_2 import nor_2 +from ptx import ptx +from vector import vector +from globals import OPTS + +class replica_bitline(design.design): + """ + Generate a module that simulate the delay of control logic + and bit line charging. + Used for memory timing control + """ + + def __init__(self, name, rows): + design.design.__init__(self, "replica_bitline") + + g = reload(__import__(OPTS.config.delay_chain)) + self.mod_delay_chain = getattr(g, OPTS.config.delay_chain) + + g = reload(__import__(OPTS.config.replica_bitcell)) + self.mod_replica_bitcell = getattr(g, OPTS.config.replica_bitcell) + + c = reload(__import__(OPTS.config.bitcell)) + self.mod_bitcell = getattr(c, OPTS.config.bitcell) + self.bitcell_chars = self.mod_bitcell.chars + + for pin in ["en", "out", "vdd", "gnd"]: + self.add_pin(pin) + self.rows = rows + + self.create_modules() + self.cal_modules_offset() + self.add_modules() + self.route() + self.offset_all_coordinates() + + self.DRC_LVS() + + def cal_modules_offset(self): + pinv_error_offset = 0.025 + # leave some room for metal1 routing + margin = 3 * drc["minwidth_metal1"] + # witdth + min_spacing of M1 & M2 + m1rail_space = drc["minwidth_metal1"] + drc["metal1_to_metal1"] + m2rail_space = drc["minwidth_metal2"] + drc["metal2_to_metal2"] + # leave some margin as bit cell layout exceeds its own orgin + route_margin = 8 * m2rail_space + well_margin = 2 * drc["pwell_enclose_nwell"] + bitcell_array_spacing = max(route_margin, well_margin) + # now extra space for BL and WL of RBC + gnd_route_margin = 5 * m2rail_space + + y_off = (self.inv.height * 2 + pinv_error_offset + + max(drc["pwell_enclose_nwell"], + m1rail_space * 4)) + self.delay_chain_offset = vector(self.delay_chain.height,y_off) + self.en_input_offset = vector(0, y_off - m2rail_space) + self.en_nor_offset = vector(self.nor.width + margin, + self.inv.height * 2) + self.BL_inv_offset = vector(self.en_nor_offset.x - self.inv.width, 0) + self.access_tx_offset = vector(self.en_nor_offset.x - self.nor.width + + self.access_tx.height + margin, + self.inv.height * 0.5) + self.replica_bitline_offset = vector(self.delay_chain_offset.x + + bitcell_array_spacing, + self.bitcell_chars["height"] + gnd_route_margin) + self.delay_inv_offset = vector(self.delay_chain_offset.x - self.inv.width, + self.inv.height * 2) + + self.height = m1rail_space + max(self.delay_chain_offset.y + self.inv.height, + self.replica_bitline_offset.y + + self.bitline_load.height + + 0.5 * self.bitcell_chars["height"]) + self.width = (self.replica_bitline_offset.x + self.replica_bitcell.width) + + + def create_modules(self): + """ create module """ + self.replica_bitcell = self.mod_replica_bitcell() + self.add_mod(self.replica_bitcell) + + # This is the replica bitline load column that is the same height as our array + self.bitline_load = bitcell_array(name="bitline_load", + cols=1, + rows=self.rows) + self.add_mod(self.bitline_load) + + # FIXME: This just creates 3 1x inverters + self.delay_chain = self.mod_delay_chain("delay_chain", + [1, 1, 1]) + self.add_mod(self.delay_chain) + + self.inv = pinv(name="RBL_inv", + nmos_width=drc["minwidth_tx"]) + self.add_mod(self.inv) + + # These aren't for instantiating, but we use them to get the dimensions + self.poly_contact = contact(layer_stack=("poly", "contact", "metal1")) + self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) + self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3")) + + self.nor = nor_2(name="replica_bitline_nor2", + nmos_width=drc["minwidth_tx"]) + self.add_mod(self.nor) + + self.access_tx = ptx(name="access_tx", + width=drc["minwidth_tx"], + mults=1, + tx_type="pmos") + self.add_mod(self.access_tx) + + def add_modules(self): + """add mod instance in layout """ + self.add_inst(name="BL_inv", + mod=self.inv, + offset=self.BL_inv_offset) + self.connect_inst(["bl[0]", "out", "vdd", "gnd"]) + + self.add_inst(name="BL_access_tx", + mod=self.access_tx, + offset=self.access_tx_offset, + rotate=90) + # D, G, S, B + self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"]) + + self.add_inst(name="delay_chain", + mod=self.delay_chain, + offset=self.delay_chain_offset, + rotate=90) + self.connect_inst(["en", "delayed_en", "vdd", "gnd"]) + + self.add_inst(name="bitcell", + mod=self.replica_bitcell, + offset=self.replica_bitline_offset, + mirror="MX") + self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"]) + + self.add_loads() + self.expan_the_well_to_BL_inv() + + def expan_the_well_to_BL_inv(self): + width = self.BL_inv_offset.x - self.access_tx_offset.x + self.inv.width + well_offset = self.access_tx_offset - vector(self.access_tx.width, 0) + for layer in ["nwell", "vtg"]: + self.add_rect(layer=layer, + offset=well_offset, + width=width, + height= 2*self.access_tx.width) + + def add_loads(self): + self.add_inst(name="load", + mod=self.bitline_load, + offset=self.replica_bitline_offset) + temp = [] + for i in range(1): + temp.append("bl[{0}]".format(i)) + temp.append("br[{0}]".format(i)) + for j in range(self.rows): + temp.append("gnd".format(j)) + temp = temp + ["vdd", "gnd"] + self.connect_inst(temp) + + def route(self): + """connect modules together""" + # calculate pin offset + correct = vector(0, 0.5 * drc["minwidth_metal1"]) + self.out_offset = self.BL_inv_offset + self.inv.Z_position + correct + self.add_via(layers=("metal1", "via1", "metal2"), + offset=self.out_offset) + m1_pin_offset = self.out_offset - correct + self.add_rect(layer="metal1", + offset=m1_pin_offset, + width=self.m1m2_via.width, + height=self.m1m2_via.height) + self.add_rect(layer="metal2", + offset=m1_pin_offset, + width=self.m2m3_via.width, + height=self.m2m3_via.height) + + BL_inv_in = self.BL_inv_offset + self.inv.A_position + correct + BL_offset = self.replica_bitline_offset + vector(1,0).scale(self.bitcell_chars["BL"]) + pin_offset = self.delay_chain.clk_out_offset.rotate().scale(-1,1) + delay_chain_output = self.delay_chain_offset + pin_offset + + self.create_input() + + self.route_BL_t_BL_inv(BL_offset, BL_inv_in) + self.route_access_tx(delay_chain_output, BL_inv_in) + self.route_vdd() + self.route_gnd() + # route loads after gnd and vdd created + self.route_loads() + self.route_RC() + + def create_input(self): + # create routing module based on module offset + correct = vector(0.5 * drc["minwidth_metal1"], 0) + pin_offset = self.delay_chain.clk_in_offset.rotate().scale(-1,1) + input_offset = self.delay_chain_offset + pin_offset + correct + mid1 = [input_offset[0], self.en_input_offset[1]] + self.add_path("metal1", [self.en_input_offset, mid1, input_offset]) + + self.add_label(text="en", + layer="metal1", + offset=self.en_input_offset) + + def route_nor2A_t_dc(self, nor_A, delayed_en_offset): + # delay chain output to m2 + dc_offset = [delayed_en_offset[0], delayed_en_offset[1]] + mid1 = [dc_offset[0], self.en_nor_offset + [1] + 3 * drc["minwidth_metal2"]] + mid2 = [self.delay_chain_offset[0] + 3*drc["minwidth_metal2"], + dc_offset[1]] + mid3 = [mid2[0], nor_A[1]] + self.add_wire(("metal2", "via1", "metal1"), + [dc_offset, mid2, mid3, nor_A]) + + def route_nor2B_t_BL_inv(self, nor_B, BL_inv_out): + mid1 = [nor_B[0] + 0.5 * drc["metal2_to_metal2"], nor_B[1]] + self.add_wire(("metal2", "via1", "metal1"), + [nor_B, mid1, BL_inv_out]) + + def route_BL_t_BL_inv(self, BL_offset, BL_inv_in): + # BL_inv input to M3 + mid1 = [BL_inv_in[0], + BL_inv_in[1] - drc["metal2_to_metal2"] - self.m1m2_via.width] + mid2 = [self.en_nor_offset[0] + 3*drc["metal1_to_metal1"], + mid1[1]] + mid3 = [mid2[0], + self.replica_bitline_offset[1] - self.replica_bitcell.height + - 0.5 * (self.m1m2_via.height + drc["metal1_to_metal1"]) + - 2 * drc["metal1_to_metal1"]] + self.add_wire(("metal1", "via1", "metal2"), + [BL_inv_in, mid1, mid2, mid3]) + + # need to fix the mid point as this is done with two wire + # this seems to cover the metal1 error of the wire + offset = mid3 - vector( [0.5 * drc["minwidth_metal1"]] * 2) + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=drc["minwidth_metal1"]) + + mid4 = [BL_offset[0], mid3[1]] + self.add_wire(("metal2", "via1", "metal1"), + [BL_offset, mid4, mid3]) + + def route_access_tx(self, delay_chain_output, BL_inv_in): + self.route_tx_gate(delay_chain_output) + self.route_tx_drain() + self.route_tx_source(BL_inv_in) + + def route_tx_gate(self, delay_chain_output): + # gate input for access tx + offset = (self.access_tx.poly_positions[0].rotate().scale(0,1) + + self.access_tx_offset) + width = -6 * drc["minwidth_metal1"] + self.add_rect(layer="poly", + offset=offset, + width=width, + height=drc["minwidth_poly"]) + y_off = 0.5 * (drc["minwidth_poly"] - self.poly_contact.height) + offset = offset + vector(width, y_off) + self.add_contact(layers=("poly", "contact", "metal1"), + offset=offset) + # route gate to delay_chain output + gate_offset = offset + vector(0.5 * drc["minwidth_metal1"], + 0.5 * self.poly_contact.width) + self.route_access_tx_t_delay_chain(gate_offset, delay_chain_output) + self.route_access_tx_t_WL(gate_offset) + + def route_access_tx_t_delay_chain(self, offset, delay_chain_output): + m2rail_space = (drc["minwidth_metal2"] + drc["metal2_to_metal2"]) + mid1 = [offset[0], self.delay_chain_offset[1] - 3 * m2rail_space] + mid2 = [delay_chain_output[0], mid1[1]] + # Note the inverted wire stack + self.add_wire(("metal2", "via1", "metal1"), + [offset, mid1, mid2, delay_chain_output]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=delay_chain_output, + mirror="MX") + + def route_access_tx_t_WL(self, offset): + m1m2_via_offset = offset - vector(0.5 * self.m1m2_via.width, + 0.5 * self.m1m2_via.height) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=m1m2_via_offset) + # route gate to RC WL + RC_WL = self.replica_bitline_offset - vector(0,1).scale(self.bitcell_chars["WL"]) + mid1 = [offset[0], 0] + mid2 = [self.en_nor_offset[0] + 3 * drc["metal1_to_metal1"], + mid1[1]] + mid3 = [RC_WL[0] - drc["minwidth_metal1"] - self.m1m2_via.height, + mid1[1]] + mid4 = [mid3[0], RC_WL[1]] + self.add_path("metal2", [offset, mid1, mid2, mid3, mid4]) + + offset = mid4 - vector([0.5 * drc["minwidth_metal1"]]*2) + width = RC_WL[0] - offset[0] + # enter the bit line array with metal1 + via_offset = [mid4[0] - 0.5 * self.m1m2_via.width, + offset[1] - 0.5 * (self.m1m2_via.height + - drc["minwidth_metal1"])] + self.add_via(layers=("metal1", "via1", "metal2"), + offset=via_offset) + self.add_rect(layer="metal1", + offset=offset, + width=width, + height=drc["minwidth_metal1"]) + + def route_tx_drain(self): + # route drain to Vdd + active_offset = self.access_tx.active_contact_positions[1].rotate().scale(-1,1) + correct = vector(drc["minwidth_metal1"], + self.access_tx.active_contact.width).scale(-0.5, 0.5) + drain_offset = self.access_tx_offset + active_offset + correct + vdd_rail = [self.delay_chain_offset[0] + 9 * drc["minwidth_metal2"], + self.height] + close_Vdd_offset = self.BL_inv_offset + vector(0, self.inv.height) + self.add_path("metal1", [drain_offset, close_Vdd_offset]) + + mid = [vdd_rail[0], close_Vdd_offset[1]] + self.add_wire(("metal2", "via1", "metal1"), + [close_Vdd_offset, mid, vdd_rail]) + + def route_tx_source(self, BL_inv_in): + # route source to BL inv input which is connected to BL + active_offset = self.access_tx.active_contact_positions[0].rotate().scale(-1,1) + correct = vector(drc["minwidth_metal1"], + self.access_tx.active_contact.width).scale(-0.5, 0.5) + source_offset = self.access_tx_offset + active_offset + correct + self.add_path("metal1", [source_offset, BL_inv_in]) + + def route_vdd(self): + vdd_offset = vector(0, self.height) + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_offset, + width=self.width, + height=drc["minwidth_metal1"]) + # delay chain vdd to vertical vdd rail and + start = self.delay_chain_offset - vector(0.5 * self.delay_chain.height, 0) + m1rail_space = (drc["minwidth_metal1"] + drc["metal1_to_metal1"]) + mid1 = start - vector(0, m1rail_space) + mid2 = [self.delay_chain_offset[0] + 9 * drc["minwidth_metal2"], + mid1[1]] + end = [mid2[0], vdd_offset[1]] + self.add_path(("metal1"), [start, mid1, mid2]) + self.add_wire(("metal2", "via1", "metal1"), [mid1, mid2, end]) + + def route_gnd(self): + """route gnd of delay chain, en_nor, en_inv and BL_inv""" + # route delay chain gnd to BL_inv gnd + # gnd Node between BL_inv access tx and delay chain, and is below + # en_input + self.gnd_position = self.delay_chain_offset + BL_gnd_offset = self.BL_inv_offset + mid1 = vector(0, self.BL_inv_offset.y) + rail2_space = drc["minwidth_metal2"] + drc["metal2_to_metal2"] + y_off = self.gnd_position.y + self.delay_chain.width + rail2_space + mid2 = vector(mid1.x, y_off) + share_gnd = vector(self.gnd_position.x, mid2.y) + # Note the inverted stacks + self.add_wire(("metal2", "via1", "metal1"), + [BL_gnd_offset, mid1, mid2, share_gnd, self.gnd_position]) + self.add_label(text="gnd", + layer="metal1", + offset=self.gnd_position) + # connect to the metal1 gnd of delay chain + offset = mid2 - vector(0.5 * drc["minwidth_metal1"], 0) + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=-self.delay_chain.width) + offset = [offset[0] + self.delay_chain.height, + mid2[1]] + self.add_rect(layer="metal1", + offset=offset, + width=drc["minwidth_metal1"], + height=-self.delay_chain.width) + + def route_loads(self): + """connect all the loads word line to gnd""" + vdd_offset = [self.delay_chain_offset[0] + 9*drc["minwidth_metal2"], + self.height] + self.add_via(layers=("metal1", "via1", "metal2"), + offset=vdd_offset, + mirror="MX") + gnd_offset = (self.delay_chain_offset + + vector([drc["minwidth_metal1"]]*2).scale(-.5,.5)) + for i in range(self.rows): + WL_offset = (self.replica_bitline_offset + + self.bitline_load.WL_positions[i].scale(0,1)) + mid = [self.delay_chain_offset[0] + 6 * drc["minwidth_metal2"], + gnd_offset[1]] + self.add_wire(("metal2", "via1", "metal1"), [gnd_offset, mid, WL_offset]) + if i % 2 == 0: + load_vdd_offset = (self.replica_bitline_offset + + self.bitline_load.vdd_positions[i]) + mid = [vdd_offset[0], load_vdd_offset[1]] + self.add_wire(("metal2", "via1", "metal1"), [vdd_offset, mid, load_vdd_offset]) + + def route_RC(self): + """route vdd gnd to the replica cell """ + # connect vdd + RC_vdd = self.replica_bitline_offset + vector(1,-1).scale(self.bitcell_chars["vdd"]) + vdd_offset = [self.delay_chain_offset[0] + 9 * drc["minwidth_metal2"], + self.height] + mid = [vdd_offset[0], RC_vdd[1]] + # Note the inverted stacks + self.add_wire(("metal2", "via1", "metal1"), [vdd_offset, mid, RC_vdd]) + + gnd_offset = self.BL_inv_offset - vector(self.inv.width, 0) + load_gnd = (self.replica_bitline_offset + + vector(self.bitcell_chars["gnd"][0], self.bitline_load.height)) + mid = [load_gnd[0], gnd_offset[1]] + self.add_wire(("metal2", "via1", "metal1"), [gnd_offset, mid, load_gnd]) + + load_gnd = (self.replica_bitline_offset + + vector(0, self.bitline_load.height)) + mid = [load_gnd[0], gnd_offset[1]] + self.add_wire(("metal2", "via1", "metal1"), [gnd_offset, mid, load_gnd]) diff --git a/compiler/sense_amp.py b/compiler/sense_amp.py new file mode 100644 index 00000000..1f440e4f --- /dev/null +++ b/compiler/sense_amp.py @@ -0,0 +1,23 @@ +import design +import debug +import utils +from tech import GDS,layer + +class sense_amp(design.design): + """ + This module implements the single sense amp cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. + Sense amplifier to read a pair of bit-lines. + """ + + pins = ["BL", "BR", "Dout", "SCLK", "vdd", "gnd"] + chars = utils.auto_measure_libcell(pins, "sense_amp", GDS["unit"], layer["boundary"]) + + def __init__(self, name): + design.design.__init__(self, name) + debug.info(2, "Create Sense Amp object") + + self.width = sense_amp.chars["width"] + self.height = sense_amp.chars["height"] + diff --git a/compiler/sense_amp_array.py b/compiler/sense_amp_array.py new file mode 100644 index 00000000..c843cca1 --- /dev/null +++ b/compiler/sense_amp_array.py @@ -0,0 +1,133 @@ +import design +from tech import drc +from vector import vector +import debug +from globals import OPTS + +class sense_amp_array(design.design): + """ + Array of sense amplifiers to read the bitlines through the column mux. + Dynamically generated sense amp array for all bitlines. + """ + + def __init__(self, word_size, words_per_row): + design.design.__init__(self, "sense_amp_array") + debug.info(1, "Creating {0}".format(self.name)) + + c = reload(__import__(OPTS.config.sense_amp)) + self.mod_sense_amp = getattr(c, OPTS.config.sense_amp) + self.sense_amp_chars = self.mod_sense_amp.chars + + self.word_size = word_size + self.words_per_row = words_per_row + + self.add_pins() + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + + if (self.words_per_row == 1): + for i in range(self.word_size): + self.add_pin("bl[{0}]".format(i)) + self.add_pin("br[{0}]".format(i)) + else: + for i in range(self.word_size): + index = i * self.words_per_row + self.add_pin("bl_out[{0}]".format(index)) + self.add_pin("br_out[{0}]".format(index)) + + for i in range(self.word_size): + self.add_pin("data_out[{0}]".format(i)) + + self.add_pin("sclk") + self.add_pin("vdd") + self.add_pin("gnd") + + def create_layout(self): + self.create_sense_amp() + self.setup_layout_constants() + self.add_sense_amp() + self.connect_rails() + self.offset_all_coordinates() + + def setup_layout_constants(self): + self.vdd_positions = [] + self.gnd_positions = [] + self.SCLK_positions = [] + self.amp_positions = [] + self.Data_out_positions = [] + self.height = self.amp.height + self.width = self.amp.width * self.word_size * self.words_per_row + + def create_sense_amp(self): + self.amp = self.mod_sense_amp("sense_amp") + self.add_mod(self.amp) + + def add_sense_amp(self): + for i in range(self.word_size): + name = "sa_d{0}".format(i) + index = i * self.words_per_row + amp_position = vector(self.amp.width * index, 0) + BL_offset = amp_position + vector(self.sense_amp_chars["BL"][0], 0) + BR_offset = amp_position + vector(self.sense_amp_chars["BR"][0], 0) + + self.add_inst(name=name, + mod=self.amp, + offset=amp_position) + self.amp_positions.append(amp_position) + if (self.words_per_row == 1): + self.add_label(text="bl[{0}]".format(i), + layer="metal2", + offset=BL_offset) + self.add_label(text="br[{0}]".format(i), + layer="metal2", + offset=BR_offset) + self.connect_inst(["bl[{0}]".format(i),"br[{0}]".format(i), + "data_out[{0}]".format(i), + "sclk", "vdd", "gnd"]) + else: + self.add_label(text="bl_out[{0}]".format(index), + layer="metal2", + offset=BL_offset) + self.add_label(text="br_out[{0}]".format(index), + layer="metal2", + offset=BR_offset) + self.connect_inst(["bl_out[{0}]".format(index), "br_out[{0}]".format(index), + "data_out[{0}]".format(i), + "sclk", "vdd", "gnd"]) + + self.add_label(text="data_out[{0}]".format(i), + layer="metal2", + offset=amp_position + self.sense_amp_chars["Dout"]) + self.Data_out_positions.append(amp_position + self.sense_amp_chars["Dout"]) + + def connect_rails(self): + base_offset = vector(0, - 0.5 * drc["minwidth_metal1"]) + # add vdd rail across entire array + vdd_offset = base_offset + vector(self.sense_amp_chars["vdd"]).scale(0,1) + self.add_layout_pin(text="vdd", + layer="metal1", + offset=vdd_offset, + width=self.width, + height=drc["minwidth_metal1"]) + self.vdd_positions.append(vdd_offset) + + # NOTE:the gnd rails are vertical so it is not connected horizontally + # add gnd rail across entire array + gnd_offset = base_offset + vector(self.sense_amp_chars["gnd"]).scale(0,1) + self.add_layout_pin(text="gnd", + layer="metal1", + offset=gnd_offset, + width=self.width, + height=drc["minwidth_metal1"]) + self.gnd_positions.append(gnd_offset) + + # add sclk rail across entire array + sclk_offset = base_offset + vector(self.sense_amp_chars["SCLK"]).scale(0,1) + self.add_layout_pin(text="sclk", + layer="metal1", + offset=sclk_offset, + width=self.width, + height=drc["minwidth_metal1"]) + self.SCLK_positions.append(sclk_offset) diff --git a/compiler/single_level_column_mux.py b/compiler/single_level_column_mux.py new file mode 100644 index 00000000..005ce54c --- /dev/null +++ b/compiler/single_level_column_mux.py @@ -0,0 +1,224 @@ +import design +import debug +from tech import drc, cell +from vector import vector +from contact import contact +from ptx import ptx +from globals import OPTS + +class single_level_column_mux(design.design): + """ + This module implements the columnmux bitline cell used in the design. + Creates a single columnmux cell. + """ + + def __init__(self, name, tx_size): + design.design.__init__(self, name) + debug.info(2, "create single columnmux cell: {0}".format(name)) + + c = reload(__import__(OPTS.config.bitcell)) + self.mod_bitcell = getattr(c, OPTS.config.bitcell) + self.bitcell_chars = self.mod_bitcell.chars + + self.tx_size = tx_size + self.ptx_width = self.tx_size * drc["minwidth_tx"] + self.add_pins() + self.create_layout() + + def add_pins(self): + self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) + + def create_layout(self): + + # This is not instantiated and used for calculations only. + self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) + self.pwell_contact = contact(layer_stack=("active", "contact", "metal1")) + + self.create_ptx() + self.add_ptx() + self.connect_poly() + self.connect_to_bitlines() + self.add_gnd_rail() + self.add_well_contacts() + self.setup_layout_constants() + + def create_ptx(self): + """Initializes the nmos1 and nmos2 transistors""" + self.nmos1 = ptx(name="nmos1", + width=self.ptx_width, + mults=1, + tx_type="nmos") + self.add_mod(self.nmos1) + self.nmos2 = ptx(name="nmos2", + width=self.ptx_width, + mults=1, + tx_type="nmos") + self.nmos2 = self.nmos2 + self.add_mod(self.nmos2) + + def add_ptx(self): + # Adds nmos1,nmos2 to the module + self.nmos1_position = (vector(drc["minwidth_metal1"], + drc["poly_extend_active"]) + - vector([drc["well_enclosure_active"]]*2)) + self.add_inst(name="M_1", + mod=self.nmos1, + offset=self.nmos1_position) + self.connect_inst(["bl", "sel", "bl_out", "gnd"]) + + nmos2_to_nmos1 = vector(self.nmos1.active_width, + self.nmos1.active_height + drc["minwidth_poly"] + + 2* drc["poly_extend_active"]) + self.nmos2_position = self.nmos1_position + nmos2_to_nmos1 + self.add_inst(name="M_2", + mod=self.nmos2, + offset=self.nmos2_position) + self.connect_inst(["br", "sel", "br_out", "gnd"]) + + def connect_poly(self): + self.poly_offset = (self.nmos1_position + + self.nmos1.poly_positions[0] + + vector(0,self.nmos1.poly_height)) + width=self.nmos2_position[0] - self.nmos1_position[0] + drc["minwidth_poly"] + self.poly = self.add_rect(layer="poly", + offset=self.poly_offset, + width=width, + height=drc["minwidth_poly"]) + self.add_label(text="col_addr", + layer="poly", + offset=self.poly_offset) + + def connect_to_bitlines(self): + offset = [self.nmos1.active_contact_positions[0].x + self.m1m2_via.contact_width / 2 + + 3 * (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2, + self.nmos1.active_position[1] + self.nmos1.active_height] + offset = self.nmos1_position + offset + connection = vector(0, + self.nmos2.active_height+ 2 * drc["poly_extend_active"] \ + + drc["minwidth_poly"] + drc["minwidth_metal2"]) + self.add_rect(layer="metal2", + offset=offset, + width=drc["minwidth_metal2"], + height=connection.y - drc["minwidth_metal2"]) + + self.BL_position = (vector(self.bitcell_chars["BL"][0] - 0.5 * self.m1m2_via.width, + offset.y) + + connection) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset, + mirror="MX") + self.add_label(text="bl", + layer="metal2", + offset=self.BL_position) + + self.add_rect(layer="metal2", + offset=self.BL_position - vector(0, 2 * drc["minwidth_metal2"]), + width=drc["minwidth_metal2"], + height=2 * drc["minwidth_metal2"]) + + width = self.bitcell_chars["BL"][0] - 0.5 * self.m1m2_via.width - offset[0] + drc["minwidth_metal2"] + self.add_rect(layer="metal2", + offset=[offset[0], + self.BL_position[1] - 2*drc["minwidth_metal2"]], + width=width, + height=drc["minwidth_metal2"]) + + offset = self.nmos1_position + self.nmos1.active_contact_positions[1] + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset) + self.add_rect(layer="metal2", + offset=[self.bitcell_chars["BL"][0] - 0.5 * self.m1m2_via.width, + 0], + width=drc["minwidth_metal2"], + height=(drc["minwidth_metal2"] + offset[1])) + self.add_rect(layer="metal2", + offset=[self.bitcell_chars["BL"][0] - 0.5 * self.m1m2_via.width, + offset[1]], + width=(offset[0] - self.bitcell_chars["BL"][0] - 0.5 * self.m1m2_via.width + + 2 * drc["minwidth_metal2"]), + height=drc["minwidth_metal2"]) + self.BL_out_position = vector(self.bitcell_chars["BL"][0] - 0.5* self.m1m2_via.width, + 0) + self.add_label(text="bl_out", + layer="metal2", + offset=self.BL_out_position) + + offset = [self.nmos2.active_contact_positions[1].x - self.m1m2_via.contact_width / 2, + self.nmos2.active_position[1] + self.nmos2.active_height] + offset = self.nmos2_position + offset + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset, + mirror="MX") + mid = offset + vector(drc["minwidth_metal2"],0) + self.add_rect(layer="metal2", + offset= mid, + width= (self.bitcell_chars["BR"][0] - mid[0] + 0.5*self.m1m2_via.width), + height=-drc["minwidth_metal2"]) + self.add_rect(layer="metal2", + offset=[self.bitcell_chars["BR"][0] - 0.5*self.m1m2_via.width, + offset[1] - drc["metal1_to_metal1"]], + width=drc["minwidth_metal2"], + height=2*drc["minwidth_metal2"]) + self.BR_position = vector(self.bitcell_chars["BR"][0] - 0.5 * self.m1m2_via.width, + self.BL_position.y) + self.add_label(text="br", + layer="metal2", + offset=self.BR_position) + + offset = self.nmos2_position + self.nmos2.active_contact_positions[0] + self.BR_out_position = vector(self.bitcell_chars["BR"][0] - 0.5 * self.m1m2_via.width, + 0) + self.add_label(text="br_out", + layer="metal2", + offset=self.BR_out_position) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset) + self.add_rect(layer="metal2", + offset=offset, + width=self.BR_out_position.x - offset[0], + height=drc["minwidth_metal2"]) + self.add_rect(layer="metal2", + offset=[self.BR_out_position.x, + offset[1] + drc["minwidth_metal2"]], + width=drc["minwidth_metal2"], + height=-(offset[1] + drc["minwidth_metal2"])) + + def add_gnd_rail(self): + self.gnd_position = vector(self.bitcell_chars["gnd"][0] - 0.5 * self.m1m2_via.width, + 0) + self.add_layout_pin(text="gnd", + layer="metal2", + offset=self.gnd_position, + width=drc["minwidth_metal2"], + height=self.BL_position[1]) + + def add_well_contacts(self): + offset = vector(self.gnd_position[0] + drc["minwidth_metal2"], + self.nmos1.poly_height / 2) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset - vector(self.m1m2_via.width / 2, 0), + mirror="MY") + self.add_contact(layers=("active", "contact", "metal1"), + offset=offset - vector(self.m1m2_via.width, 0), + mirror="MY") + temp = vector(self.m1m2_via.width, + (self.pwell_contact.first_layer_height - self.pwell_contact.second_layer_height) / 2) + offset_implant = offset - temp + vector([drc["implant_to_contact"]]*2).scale(1,-1) + self.add_rect(layer="pimplant", + offset=offset_implant, + width=-(2*drc["implant_to_contact"] + self.pwell_contact.first_layer_width), + height=2*drc["implant_to_contact"] + self.pwell_contact.width) + + offset_well = self.nmos1_position + vector(self.nmos1.width, 0) + self.add_rect(layer="pwell", + offset=offset_well, + width=self.gnd_position[0] + drc["minwidth_metal2"] - offset_well[0], + height=self.nmos1.height + drc["minwidth_poly"]) + self.add_rect(layer="vtg", + offset=offset_well, + width=self.gnd_position[0] + drc["minwidth_metal2"] - offset_well[0], + height=self.nmos1.height + drc["minwidth_poly"]) + + def setup_layout_constants(self): + self.width = self.width = self.bitcell_chars["width"] + self.height = self.height = self.BL_position[1] diff --git a/compiler/single_level_column_mux_array.py b/compiler/single_level_column_mux_array.py new file mode 100644 index 00000000..019cfcda --- /dev/null +++ b/compiler/single_level_column_mux_array.py @@ -0,0 +1,272 @@ +from math import log +import design +from single_level_column_mux import single_level_column_mux +from contact import contact +from tech import drc +import debug +import math +from vector import vector + + +class single_level_column_mux_array(design.design): + """ + Dynamically generated column mux array. + Array of column mux to read the bitlines through the 6T. + """ + + def __init__(self, rows, columns, word_size): + design.design.__init__(self, "columnmux_array") + debug.info(1, "Creating {0}".format(self.name)) + self.rows = rows + self.columns = columns + self.word_size = word_size + self.words_per_row = self.columns / self.word_size + self.row_addr_size = self.decoder_inputs = int(math.log(self.rows, 2)) + self.add_pins() + self.create_layout() + self.offset_all_coordinates() + self.DRC_LVS() + + def add_pins(self): + for i in range(self.columns): + self.add_pin("bl[{0}]".format(i)) + self.add_pin("br[{0}]".format(i)) + for i in range(self.columns / self.words_per_row): + self.add_pin("bl_out[{0}]".format(i * self.words_per_row)) + self.add_pin("br_out[{0}]".format(i * self.words_per_row)) + for i in range(self.words_per_row): + self.add_pin("sel[{0}]".format(i)) + self.add_pin("gnd") + + def create_layout(self): + self.add_modules() + self.setup_layout_constants() + self.create_array() + self.add_routing() + + def add_modules(self): + self.mux = single_level_column_mux(name="single_level_column_mux", + tx_size=8) + self.single_mux = self.mux + self.add_mod(self.mux) + + # This is not instantiated and used for calculations only. + self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) + + + def setup_layout_constants(self): + self.column_addr_size = num_of_inputs = int(self.words_per_row / 2) + self.width = (self.columns * self.mux.width) + self.gnd_positions = [] + self.BL_out_positions = [] + self.BR_out_positions = [] + self.BL_positions = [] + self.BR_positions = [] + self.addr_line_positions = [] + + spacing = self.m1m2_via.width + drc['metal1_to_metal1'] + self.height = self.mux.height + spacing + 4 * drc['metal2_to_metal2'] + if (self.words_per_row > 1): + # 1 for BL and another for BR + self.height = self.height + (self.words_per_row + 1) * spacing + + def create_array(self): + for i in range(self.columns): + name = "XMUX{0}".format(i) + x_off = vector(i * self.mux.width, 0) + self.add_inst(name=name, + mod=self.mux, + offset=x_off) + + """ draw a vertical m2 rail to extend BL BR & gnd on top of the cell """ + # FIXME: These are just min metal squares, are they needed? + self.add_rect(layer="metal2", + offset=x_off + self.mux.BL_position, + width=drc['minwidth_metal2'], + height=drc['minwidth_metal2']) + self.add_rect(layer="metal2", + offset=x_off + self.mux.BR_position, + width=drc['minwidth_metal2'], + height=drc['minwidth_metal2']) + self.add_rect(layer="metal2", + offset=x_off + self.mux.gnd_position, + width=drc['minwidth_metal2'], + height=drc['minwidth_metal2']) + + """ add labels for the column_mux array """ + BL = self.mux.BL_position + vector(i * self.mux.width, 0) + self.BL_positions.append(BL) + self.add_label(text="bl[{0}]".format(i), + layer="metal2", + offset=BL) + + BR = self.mux.BR_position + vector(i * self.mux.width, 0) + self.BR_positions.append(BR) + self.add_label(text="br[{0}]".format(i), + layer="metal2", + offset=BR) + + gnd = self.mux.gnd_position + vector(i * self.mux.width, 0) + self.gnd_positions.append(gnd) + self.add_label(text="gnd", + layer="metal2", + offset=gnd) + + for i in range(self.word_size): + base =vector(i * self.words_per_row * self.mux.width, 0) + BL_out = base + self.mux.BL_out_position + BR_out = base + self.mux.BR_out_position + self.add_label(text="bl_out[{0}]".format(i * self.words_per_row), + layer="metal2", + offset=BL_out) + self.add_label(text="br_out[{0}]".format(i * self.words_per_row), + layer="metal2", + offset=BR_out) + self.BL_out_positions.append(BL_out) + self.BR_out_positions.append(BR_out) + + if(self.words_per_row == 2): + for i in range(self.columns / 2): + # This will not check that the inst connections match. + self.connect_inst(args=["bl[{0}]".format(2 * i), + "br[{0}]".format(2 * i), + "bl_out[{0}]".format(2 * i), + "br_out[{0}]".format(2 * i), + "sel[{0}]".format(0), "gnd"], + check=False) + # This will not check that the inst connections match. + self.connect_inst(args=["bl[{0}]".format(2 * i + 1), + "br[{0}]".format(2 * i + 1), + "bl_out[{0}]".format(2 * i), + "br_out[{0}]".format(2 * i), + "sel[{0}]".format(1), "gnd"], + check=False) + if(self.words_per_row == 4): + for i in range(self.columns / 4): + # This will not check that the inst connections match. + self.connect_inst(args=["bl[{0}]".format(4 * i), + "br[{0}]".format(4 * i), + "bl_out[{0}]".format(4 * i), + "br_out[{0}]".format(4 * i), + "sel[{0}]".format(0), "gnd"], + check=False) + # This will not check that the inst connections match. + self.connect_inst(args=["bl[{0}]".format(4 * i + 1), + "br[{0}]".format(4 * i + 1), + "bl_out[{0}]".format(4 * i), + "br_out[{0}]".format(4 * i), + "sel[{0}]".format(1), "gnd"], + check=False) + # This will not check that the inst connections match. + self.connect_inst(args=["bl[{0}]".format(4 * i + 2), + "br[{0}]".format(4 * i + 2), + "bl_out[{0}]".format(4 * i), + "br_out[{0}]".format(4 * i), + "sel[{0}]".format(2), "gnd"], + check=False) + # This will not check that the inst connections match. + self.connect_inst(args=["bl[{0}]".format(4 * i + 3), + "br[{0}]".format(4 * i + 3), + "bl_out[{0}]".format(4 * i), + "br_out[{0}]".format(4 * i), + "sel[{0}]".format(3), "gnd"], + check=False) + + def add_routing(self): + self.add_horizontal_input_rail() + self.add_vertical_poly_rail() + self.routing_BL_BR() + + def add_horizontal_input_rail(self): + """ HORIZONTAL ADDRESS INPUTS TO THE COLUMN MUX ARRAY """ + if (self.words_per_row > 1): + for j in range(self.words_per_row): + offset = vector(0, -(j + 1) * self.m1m2_via.width + - j * drc['metal1_to_metal1']) + self.add_rect(layer="metal1", + offset=offset, + width=self.mux.width * self.columns, + height=self.m1m2_via.width) + self.addr_line_positions.append(offset) + + def add_vertical_poly_rail(self): + """ VERTICAL POLY METAL EXTENSION AND POLY CONTACT """ + for j1 in range(self.columns): + pattern = math.floor(j1 / self.words_per_row) * self.words_per_row + height = ((self.m1m2_via.width + drc['metal1_to_metal1']) + *(pattern - j1)) + nmos1_poly = self.mux.nmos1_position + self.mux.nmos1.poly_positions[0] + offset = nmos1_poly.scale(1, 0) + vector(j1 * self.mux.width, 0) + self.add_rect(layer="poly", + offset=offset, + width=drc["minwidth_poly"], + height= height -self.m1m2_via.width) + + # This is not instantiated and used for calculations only. + poly_contact = contact(layer_stack=("metal1", "contact", "poly")) + offset = offset.scale(1, 0) + vector(0, height - poly_contact.width) + self.add_contact(layers=("metal1", "contact", "poly"), + offset=offset, + mirror="MX", + rotate=90) + + def routing_BL_BR(self): + """ OUTPUT BIT-LINE CONNECTIONS (BL_OUT, BR_OUT) """ + if (self.words_per_row > 1): + for j in range(self.columns / self.words_per_row): + base = vector(self.mux.width * self.words_per_row * j, + self.m1m2_via.width + drc['metal1_to_metal1']) + width = self.m1m2_via.width + self.mux.width * (self.words_per_row - 1) + self.add_rect(layer="metal1", + offset=base.scale(1,-self.words_per_row) + self.mux.BL_position.scale(1,0), + width=width, + height=-self.m1m2_via.width) + self.add_rect(layer="metal1", + offset=base.scale(1,-self.words_per_row-1) + self.mux.BR_position.scale(1,0), + width=width, + height=-self.m1m2_via.width) + + height = base.y * (self.words_per_row + 2) + 3 * drc['metal2_to_metal2'] + base = vector(base.x, - height) + self.add_rect(layer="metal2", + offset=base + self.mux.BL_position.scale(1,0), + width=drc['minwidth_metal2'], + height=height) + self.add_rect(layer="metal2", + offset=base + self.mux.BR_position.scale(1,0), + width=drc['minwidth_metal2'], + height=height) + self.add_rect(layer="metal2", + offset=base + self.mux.gnd_position.scale(1,0), + width=drc['minwidth_metal2'], + height=height) + + for j in range(self.columns): + """ adding vertical metal rails to route BL_out and BR_out vertical rails """ + contact_spacing = self.m1m2_via.width + drc['metal1_to_metal1'] + height = self.words_per_row * contact_spacing + self.m1m2_via.width + offset = vector(self.mux.BL_position.x + self.mux.width * j, 0) + self.add_rect(layer="metal2", + offset=offset, + width=drc['minwidth_metal2'], + height=-height) + offset = offset + vector(self.m1m2_via.height, - height) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=offset, + rotate=90) + + offset = vector(self.mux.BR_position.x + self.mux.width * j, 0) + height = height + contact_spacing + self.add_rect(layer="metal2", + offset=offset, + width=drc['minwidth_metal2'], + height= - height) + offset = offset + vector(self.m1m2_via.height/2, - height) + layer_diff = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) + self.add_via(layers=("metal1", "via1", "metal2"), + offset= offset + vector(layer_diff, 0), + rotate=90) + + self.add_label(text="COLUMN_MUX", + layer="text", + offset=[self.width / 2.0, self.height / 2.0]) diff --git a/compiler/sram.py b/compiler/sram.py new file mode 100644 index 00000000..ffa70454 --- /dev/null +++ b/compiler/sram.py @@ -0,0 +1,1172 @@ +import math +import sys +from tech import drc, spice, cell +import debug +import design +from math import log,sqrt,ceil +from contact import contact +from bank import bank +import datetime +import getpass +from vector import vector +from globals import OPTS + + +class sram(design.design): + """ + Dynamically generated SRAM by connecting banks to control logic. The + number of banks should be 1 , 2 or 4 + """ + + def __init__(self, word_size, num_words, num_banks, name): + mod_list = ["control_logic", "ms_flop_array", "ms_flop", "bitcell"] + for mod_name in mod_list: + config_mod_name = getattr(OPTS.config, mod_name) + class_file = reload(__import__(config_mod_name)) + mod_class = getattr(class_file , config_mod_name) + setattr (self, "mod_"+mod_name, mod_class) + + + self.ms_flop_chars = self.mod_ms_flop.chars + self.bitcell_chars = self.mod_bitcell.chars + + self.word_size = word_size + self.num_words = num_words + self.num_banks = num_banks + + debug.info(2, "create sram of size {0} with {1} num of words".format(self.word_size, + self.num_words)) + + design.design.__init__(self, name) + self.ctrl_positions = {} + + self.compute_sizes() + self.add_pins() + + self.create_layout() + self.DRC_LVS() + + def compute_sizes(self): + """ Computes the required sizes to create the memory """ + self.check_num_banks(self.num_banks) + + self.num_words_per_bank = self.num_words/self.num_banks + self.num_bits_per_bank = self.word_size*self.num_words_per_bank + + self.bank_area = self.bitcell_chars["width"]*\ + self.bitcell_chars["height"]*self.num_bits_per_bank + self.bank_side_length = math.sqrt(self.bank_area) + + self.tentative_num_cols = int(self.bank_side_length/self.bitcell_chars["width"]) + self.words_per_row = self.cal_words_per_row(self.tentative_num_cols, + self.word_size) + self.tentative_num_rows = self.num_bits_per_bank \ + /(self.words_per_row \ + *self.word_size) + self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, + self.words_per_row) + + self.num_cols = self.words_per_row*self.word_size + self.num_rows = self.num_words_per_bank/self.words_per_row + + self.row_addr_size = int(log(self.num_rows, 2)) + self.col_addr_size = int(log(self.words_per_row, 2)) + self.bank_addr_size = self.col_addr_size \ + + self.row_addr_size + self.addr_size = self.bank_addr_size + \ + int(math.log(self.num_banks, 2)) + + self.control_size = 6 + + self.bank_to_bus_distance = 5*drc["minwidth_metal3"] + + def check_num_banks(self,num_banks): + if(num_banks != 1 and num_banks != 2 and num_banks != 4): + debug.error("Valid number of banks are 1 , 2 and 4.") + sys.exit(-1) + + def cal_words_per_row(self,tentative_num_cols, word_size): + if(tentative_num_cols < 1.5*word_size): + words_per_row = 1 + elif(tentative_num_cols > 3*word_size): + words_per_row = 4 + else: + words_per_row = 2 + return words_per_row + + def amend_words_per_row(self,tentative_num_rows, words_per_row): + if(tentative_num_rows > 512): + if(tentative_num_rows*words_per_row > 2048): + debug.error("Number of rows exceeds 512") + sys.exit(-1) + words_per_row = words_per_row*tentative_num_rows/512 + + if(tentative_num_rows < 16): + if(tentative_num_rows*words_per_row < 16): + debug.error("minimum number of rows is 16, but given {0}".format( + tentative_num_rows)) + sys.exit(-1) + words_per_row = words_per_row*tentative_num_rows/16 + return words_per_row + + def add_pins(self): + """ app pins """ + for i in range(self.word_size): + self.add_pin("DATA[{0}]".format(i)) + for i in range(self.addr_size): + self.add_pin("ADDR[{0}]".format(i)) + for pin in ["CSb","WEb","OEb", + "clk","vdd","gnd"]: + self.add_pin(pin) + + def create_layout(self): + """ Layout creation """ + self.create_modules() + self.add_modules() + self.add_routing() + + def add_routing(self): + """ Route all of the modules """ + if (self.num_banks == 2 or self.num_banks == 4): + self.route_2or4_banks() + if (self.num_banks == 4): + self.route_4_banks() + self.route_bank_and_control() + self.route_supplies() + + def create_multibank_modules(self): + """ Add the multibank address flops and bank decoder """ + self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_address", + array_type="address", + columns=self.num_banks/2, + word_size=self.num_banks/2) + self.add_mod(self.msf_msb_address) + + self.msb_decoder = self.bank.decoder.pre2_4 + self.add_mod(self.msb_decoder) + + def create_modules(self): + """ Create all the modules that will be used """ + + # Create the control logic module + self.control = self.mod_control_logic(num_rows=self.num_rows) + self.add_mod(self.control) + + # Create the bank module (up to four are instantiated) + self.bank = bank(word_size=self.word_size, + num_words=self.num_words_per_bank, + words_per_row=self.words_per_row, + num_banks=self.num_banks, + name="test_bank1") + self.add_mod(self.bank) + + # Conditionally create the + if(self.num_banks > 1): + self.create_multibank_modules() + + # These aren't for instantiating, but we use them to get the dimensions + self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2")) + self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3")) + + self.bank_count = 0 + + self.sram_property = ["bank_clk_positions", + "bank_clk_bar_positions", + "bank_tri_en_positions", + "bank_tri_en_bar_positions", + "bank_w_en_positions", + "bank_s_en_positions"] + self.bank_property = ["clk_position", + "clk_bar_position", + "tri_en_position", + "tri_en_bar_position", + "w_en_position", + "s_en_position", ] + + self.bank_positions = [] + + self.bank_clk_positions = [] + self.bank_clk_bar_positions = [] + self.bank_tri_en_positions = [] + self.bank_tri_en_bar_positions = [] + self.bank_w_en_positions = [] + self.bank_s_en_positions = [] + + # SRAM bank address 3D array + # 2 keys will return a x,y position pair + # example key1 = bank_index , key2 = addr_line_index will return [x,y] + self.sram_bank_adress_positions = [] + + # SRAM data lines 3D array + # 2 keys will return a x,y position pair + # example key1 = bank_index , key2 = data_line_index will return [x,y] + self.sram_bank_data_positions = [] + + # 2D array for bank_select position of banks + self.sram_bank_select_positions = [] + + # Bank power rail positions + self.sram_bank_right_vdd_positions = [] + self.sram_bank_left_vdd_positions = [] + self.sram_bank_left_gnd_positions = [] + + self.power_rail_width = self.bank.power_rail_width + self.sram_power_rail_gap = 4*self.power_rail_width + + self.vdd_position = vector(0, 2*self.power_rail_width) + self.gnd_position = vector(0, 0) + + + + def add_bank(self, position, x_flip, y_flip): + """ add and place bank. All the pin position is also + translated and saved for later use""" + + # x_flip == 1 --> no flip in x_axis + # x_flip == -1 --> flip in x_axis + # y_flip == 1 --> no flip in y_axis + # y_flip == -1 --> flip in y_axis + + # x_flip and y_flip are used for position translation + + bank_rotation = 180 if (x_flip == -1 and y_flip == -1) else 0 + bank_mirror = "R0" + + if(x_flip == y_flip): + bank_mirror = "R0" + elif(x_flip == -1): + bank_mirror = "MX" + elif(y_flip == -1): + bank_mirror = "MY" + + yMetalShift = drc["minwidth_metal3"] if (x_flip == -1) else 0 + xMetalShift = drc["minwidth_metal3"] if (y_flip == -1) else 0 + + position=vector(position) + self.add_inst(name="bank{0}".format(self.bank_count), + mod=self.bank, + offset=position, + mirror=bank_mirror, + rotate=bank_rotation) + self.bank_positions.append(position) + + temp = [] + for i in range(self.word_size): + temp.append("DATA[{0}]".format(i)) + for i in range(self.bank_addr_size): + temp.append("ADDR[{0}]".format(i)) + if(self.num_banks > 1): + temp.append("bank_select[{0}]".format(self.bank_count)) + temp = temp + ["s_en" , "w_en", "tri_en_bar", "tri_en", + "clk_bar", "clk" , "vdd" , "gnd" ] + self.connect_inst(temp) + + # Saving control line properties + for i in range(len(self.sram_property)): + sub_mod_offset = getattr(self.bank,self.bank_property[i]) + new=(position + vector(y_flip,x_flip).scale(sub_mod_offset) + - vector(xMetalShift,yMetalShift)) + + pos_list=getattr(self,self.sram_property[i]) + if pos_list is None: + pos_list=[] + pos_list.append(new) + setattr(self,self.sram_property[i],pos_list) + + # Address input lines + bank_address_positions = [] + for addr_position in self.bank.address_positions: + new=(position + vector(y_flip,x_flip).scale(addr_position) + - vector(xMetalShift,yMetalShift)) + bank_address_positions.append(new) + self.sram_bank_adress_positions.append(bank_address_positions) + + # Bank select + if (self.num_banks > 1): + new=(position + vector(y_flip,x_flip).scale(self.bank.bank_select_position) + - vector(xMetalShift,yMetalShift)) + self.sram_bank_select_positions.append(new) + + # Data input lines + bank_data_positions = [] + for data_position in self.bank.data_positions: + new=(position + vector(y_flip,x_flip).scale(data_position) + - vector(xMetalShift,yMetalShift)) + bank_data_positions.append(new) + self.sram_bank_data_positions.append(bank_data_positions) + + # VDD rails + + yPowerShift = self.power_rail_width if(x_flip == -1) else 0 + xPowerShift = self.power_rail_width if(y_flip == -1) else 0 + + # Right vdd + new=(position + vector(y_flip,x_flip).scale(self.bank.right_vdd_position) + - vector(xPowerShift,yPowerShift)) + self.sram_bank_right_vdd_positions.append(new) + # left vdd + new=(position + vector(y_flip,x_flip).scale(self.bank.left_vdd_position) + - vector(xPowerShift,yPowerShift)) + self.sram_bank_left_vdd_positions.append(new) + # left gnd + new=(position + vector(y_flip,x_flip).scale(self.bank.left_gnd_position) + - vector(xPowerShift,yPowerShift)) + self.sram_bank_left_gnd_positions.append(new) + + self.bank_count = self.bank_count + 1 + + # FIXME: This should be in geometry.py or it's own class since it is + # reusable + def create_bus(self, layer, offset, bits, height, rotate): + """ Create a bus and place it according to rotate and + return an array of line positions """ + + minwidth = "minwidth_{0}".format(layer) + m2m = "{0}_to_{0}".format(layer) + line_width = drc[minwidth] + line_gap = 2*drc[m2m] + + line_positions = [] + bus_width = bits*(line_width + line_gap) + if(rotate == 0): + for i in range(bits): + line_offset = offset + vector(i*(line_width + line_gap),0) + self.add_rect(layer=layer, + offset=line_offset, + width=line_width, + height=height) + line_positions.append(line_offset) + elif(rotate == 270): + for i in range(bits): + line_offset = offset - vector(0, (i+1)*line_width + i*line_gap) + self.add_rect(layer=layer, + offset=line_offset, + width=height, + height=line_width) + line_positions.append(line_offset) + else: + debug.error("Unimplemented rotation for create_bus") + + return line_positions + + def calculate_bus_width(self, layer, bits): + """ Calculate the bus width """ + minwidth = "minwidth_{0}".format(layer) + m2m = "{0}_to_{0}".format(layer) + line_width = drc[minwidth] + line_gap = 2*drc[m2m] + return bits*(line_width + line_gap) - line_gap + + def add_control_logic(self, position, mirror): + """ Add and place control logic """ + self.control_position = position + self.add_inst(name="control", + mod=self.control, + offset=self.control_position, + mirror=mirror) + temp = ["CSb", "WEb", "OEb", "s_en", "w_en", "tri_en", + "tri_en_bar", "clk_bar", "clk", "vdd", "gnd"] + self.connect_inst(temp) + + def add_singlebank_modules(self): + """ This adds the moduels for a single bank SRAM with control + logic. """ + self.add_bank([0, 0], 1, 1) + # FIXME: document + loc = vector(- 2 * drc["minwidth_metal3"], + self.bank_positions[0].y + self.bank.decoder_position.y + + 2 * drc["minwidth_metal3"]) + self.add_control_logic(loc, "R90") + + self.width = self.bank.width + self.control.height + 2*drc["minwidth_metal3"] + self.height = self.bank.height + self.control.CSb_position.rotate().scale(-1,1) + self.CSb_position = (self.control.CSb_position.rotate().scale(-1,1) + +self.control_position) + self.OEb_position = (self.control.OEb_position.rotate().scale(-1,1) + +self.control_position) + self.WEb_position = (self.control.WEb_position.rotate().scale(-1,1) + +self.control_position) + self.clk_position = (self.control.clk_position.rotate().scale(-1,1) + +self.control_position) + for i in range(0, self.word_size): + self.add_label(text="DATA[{0}]".format(i), + layer="metal3", + offset=self.bank.data_positions[i]) + + def add_multibank_modules(self): + """ This creates either a 2 or 4 bank SRAM with control logic + and bank selection logic.""" + + self.bank_h = self.bank.height + self.bank_w = self.bank.width + + self.num_vertical_line = self.bank_addr_size + self.control_size \ + + self.num_banks + self.num_banks/2 + self.num_horizontal_line = self.word_size + + self.vertical_bus_width = self.calculate_bus_width("metal2", + self.num_vertical_line) + self.horizontal_bus_width = self.calculate_bus_width("metal3", + self.num_horizontal_line) + + self.vertical_bus_height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \ + + self.horizontal_bus_width + self.horizontal_bus_height = (2 * (self.bank_w + self.bank_to_bus_distance) + + self.vertical_bus_width) + + self.vertical_bus_offset = vector(self.bank_w + self.bank_to_bus_distance, + self.sram_power_rail_gap) + self.horizontal_bus_offset = vector(0, + self.bank_h + self.bank_to_bus_distance + + self.sram_power_rail_gap + + self.horizontal_bus_width) + + # Vertical bus + self.vertical_line_positions = self.create_bus(layer="metal2", + offset=self.vertical_bus_offset, + bits=self.num_vertical_line, + height=self.vertical_bus_height, + rotate=0) + + # Horizontal bus + self.horizontal_line_positions = self.create_bus(layer="metal3", + offset=self.horizontal_bus_offset, + bits=self.num_horizontal_line, + height=self.horizontal_bus_height, + rotate=270) + for i in range(0, self.word_size): + self.add_label(text="DATA[{0}]".format(i), + layer="metal3", + offset=self.horizontal_line_positions[i]) + + self.width = 2*(self.bank_w + self.bank_to_bus_distance) + self.vertical_bus_width + self.height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \ + + self.horizontal_bus_width + self.sram_power_rail_gap + + # Add Control logic for Bank = 2 and Bank =4 + + control_bus_width = self.calculate_bus_width("metal1", + self.control_size + 2) + control_bus_height = (self.vertical_line_positions[self.control_size - 1].x + + drc["minwidth_metal2"]) + + control_bus_offset = vector(0, self.height + control_bus_width + + 4*drc["minwidth_metal3"]) + self.control_bus_line_positions = self.create_bus(layer="metal1", + offset=control_bus_offset, + bits=self.control_size + 2, + height=control_bus_height, + rotate=270) + + if (self.num_banks == 2): + self.control_position = vector(0, control_bus_offset.y + + self.ms_flop_chars["width"]) + self.add_control_logic(self.control_position, "R0") + + self.CSb_position = self.control_position + self.control.CSb_position + self.OEb_position = self.control_position + self.control.OEb_position + self.WEb_position = self.control_position + self.control.WEb_position + self.clk_position = self.control_position + self.control.clk_position + # Max point + self.max_point = self.control_position.y + self.ms_flop_chars["width"] + + # MSB address + x_off = (self.bank_w + self.vertical_bus_width + + 2 * self.bank_to_bus_distance + + self.power_rail_width + + 4 * drc["minwidth_metal3"]) + y_off = self.height + 2 * self.ms_flop_chars["width"] + 4 * drc["minwidth_metal3"] + self.msf_msb_address_position = vector(x_off, y_off) + self.add_inst(name="msf_msb_address", + mod=self.msf_msb_address, + offset=self.msf_msb_address_position, + mirror="RO", + rotate=270) + + temp = [] + for i in range(self.num_banks/2): + temp.append("ADDR[{0}]".format(self.bank.addr_size + i)) + if(self.num_banks == 4): + for i in range(self.num_banks/2): + temp.append("msb{0}".format(i)) + temp.append("msb{0}_bar".format(i)) + else: + temp = temp + ["bank_select[1]", "bank_select[0]"] + temp = temp + ["clk", "vdd", "gnd"] + self.connect_inst(temp) + + self.add_banks_0and1() + + if (self.num_banks == 4): + self.add_banks_2and3() + + # Extension of Vertical Rail + self.create_bus(layer="metal2", + offset=[self.vertical_bus_offset.x, + self.height], + bits=self.num_vertical_line, + height=self.max_point - self.height, + rotate=0) + + # Add ADDRESS labels to vertical line + for i in range(self.addr_size - int(math.log(self.num_banks, 2))): + index = self.control_size + int(math.log(self.num_banks, 2)) + i + self.add_label(text="ADDR[{}]".format(i), + layer="metal2", + offset=[self.vertical_line_positions[index].x, + self.max_point]) + + for i in range(int(math.log(self.num_banks, 2))): + self.add_label(text="ADDR[{}]".format(self.addr_size - i - 1), + layer="metal2", + offset=[self.vertical_line_positions[self.control_size + i].x, + self.max_point]) + + def add_modules(self): + """ add all the modules """ + if (self.num_banks == 1): + self.add_singlebank_modules() + elif (self.num_banks == 2 or self.num_banks == 4): + self.add_multibank_modules() + + self.add_labels() + + def add_banks_0and1(self): + # Placement of bank 0 + self.bank_position_0 = vector(self.bank_w, + self.bank_h + self.sram_power_rail_gap) + self.add_bank(self.bank_position_0, -1, -1) + + # Placement of bank 1 + x_off = self.bank_w + self.vertical_bus_width + 2*self.bank_to_bus_distance + self.bank_position_1 = vector(x_off, self.bank_position_0.y) + self.add_bank(self.bank_position_1, -1, 1) + + def add_banks_2and3(self): + # Placement of bank 2 + y_off = (self.bank_h + self.horizontal_bus_width + +2 * self.bank_to_bus_distance + + self.sram_power_rail_gap) + bank_position_2 = vector(self.bank_position_0.x, y_off) + self.add_bank(bank_position_2, 1, -1) + + # Placement of bank 3 + bank_position_3 = vector(self.bank_position_1.x, bank_position_2.y) + self.add_bank(bank_position_3, 1, 1) + + self.msb_decoder_position = vector(bank_position_3.x + self.power_rail_width + + 4 * drc["minwidth_metal3"] + + self.msb_decoder.width, + self.msf_msb_address_position.y + + 4 * drc["minwidth_metal3"]) + + self.add_inst(name="msb_decoder", + mod=self.msb_decoder, + offset=self.msb_decoder_position, + mirror="MY") + temp = ["msb0", "msb1", "bank_select[{0}]".format(0), + "bank_select[{0}]".format(1), "bank_select[{0}]".format(2), + "bank_select[{0}]".format(3), + "vdd", "gnd"] + self.connect_inst(temp) + + self.control_position = vector(0, self.msb_decoder_position.y + + self.msb_decoder.height) + self.add_control_logic(self.control_position, "R0") + + self.CSb_position = self.control_position + self.control.CSb_position + self.OEb_position = self.control_position + self.control.OEb_position + self.WEb_position = self.control_position + self.control.WEb_position + self.clk_position = self.control_position + self.control.clk_position + + # Max point + self.max_point = self.msb_decoder_position.y + self.msb_decoder.height + + def add_labels(self): + """ Add the top-level labels for control and address """ + for label in ["CSb", "OEb", "WEb", "clk"]: + offset = getattr(self, label+"_position") + self.add_label(text=label, + layer="metal3", + offset=offset) + + # add address label + for addr_pos_lst in self.sram_bank_adress_positions: + for address, address_positions in enumerate(addr_pos_lst): + self.add_label(text="ADDR[{0}]".format(address), + layer="metal3", + offset=address_positions) + + def route_2or4_banks(self): + """ Routing between bank 2 or 4 bank modules """ + addr_start_index = len(self.sram_property) + (self.num_banks / 2) + bank_select_index = addr_start_index + self.bank.addr_size + + # control, data , address and bank_select connection + for i in range(self.num_banks / 2): + left_bank_index = 2 * i + right_bank_index = 2 * i + 1 + + for attr_index in range(len(self.sram_property)): + bank_attr = self.sram_property[attr_index] + self.add_rect(layer="metal3", + offset=getattr(self,bank_attr)[left_bank_index], + width=getattr(self,bank_attr)[right_bank_index].x - getattr(self,bank_attr)[left_bank_index][0], + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[self.vertical_line_positions[attr_index].x, + getattr(self,bank_attr)[left_bank_index].y]) + + for addr_index in range(self.bank.addr_size): + line_index = addr_start_index + addr_index + self.add_rect(layer="metal3", + offset=self.sram_bank_adress_positions[left_bank_index][addr_index], + width=self.sram_bank_adress_positions[right_bank_index][addr_index].x \ + - self.sram_bank_adress_positions[left_bank_index][addr_index].x, + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[self.vertical_line_positions[line_index].x, + self.sram_bank_adress_positions[left_bank_index][addr_index].y]) + + # left bank_select + self.add_rect(layer="metal3", + offset=self.sram_bank_select_positions[left_bank_index], + width=self.vertical_line_positions[bank_select_index + left_bank_index].x \ + - self.sram_bank_select_positions[left_bank_index].x, + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[self.vertical_line_positions[bank_select_index + left_bank_index].x, + self.sram_bank_select_positions[left_bank_index].y]) + + # right bank select + x_off = self.vertical_line_positions[bank_select_index + right_bank_index].x + contact_offset = vector(x_off, + self.sram_bank_select_positions[right_bank_index].y) + + self.add_rect(layer="metal3", + offset=contact_offset, + width=self.sram_bank_select_positions[right_bank_index].x \ + - contact_offset.x, + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=contact_offset) + + # Data connection on the horizontal bus + if (self.num_banks == 4): + data_connection_top = self.sram_bank_data_positions[2][0].y + self.m2m3_via.height + else: + data_connection_top=self.horizontal_bus_offset.y + + data_connection_height = data_connection_top - self.sram_bank_data_positions[0][0].y + + for i in range(2): + lower_bank_index = i + upper_bank_index = i + 2 + + for data_index in range(self.bank.word_size): + line_index = addr_start_index + addr_index + self.add_rect(layer="metal2", + offset=self.sram_bank_data_positions[lower_bank_index][data_index], + width=drc["minwidth_metal2"], + height=data_connection_height) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[self.sram_bank_data_positions[lower_bank_index][data_index].x, + self.horizontal_line_positions[data_index].y]) + + def route_4_banks(self): + for i in range(2): + lower_bank_index = i + upper_bank_index = i + 2 + + # Power rail connections + self.add_rect(layer="metal1", + offset=self.sram_bank_right_vdd_positions[lower_bank_index], + width=self.power_rail_width, + height=self.sram_bank_right_vdd_positions[upper_bank_index].y \ + - self.sram_bank_right_vdd_positions[lower_bank_index].y) + self.add_rect(layer="metal1", + offset=self.sram_bank_left_vdd_positions[lower_bank_index], + width=self.power_rail_width, + height=self.sram_bank_left_vdd_positions[upper_bank_index].y \ + - self.sram_bank_left_vdd_positions[lower_bank_index].y) + self.add_rect(layer="metal2", + offset=self.sram_bank_left_gnd_positions[lower_bank_index], + width=self.power_rail_width, + height=self.sram_bank_left_gnd_positions[upper_bank_index].y \ + - self.sram_bank_left_gnd_positions[lower_bank_index].y) + + def route_bank_and_control(self): + """ Routing between banks and control """ + + if (self.num_banks == 1): + + # FIXME what is this? add comments + # 5 = clk + # 4 = tri_en_bar + # 3 = tri_en + # 2 = clk_bar + # 1 = w_en + # 0 = s_en + + control_side = [] + control_side.append(self.control.clk_position.rotate().scale(-1, 1) + + self.control_position) + control_side.append(self.control.clk_bar_position.rotate().scale(-1, 1) + + self.control_position) + control_side.append(self.control.tri_en_position.rotate().scale(-1, 1) + + self.control_position) + control_side.append(self.control.tri_en_bar_position.rotate().scale(-1, 1) + + self.control_position) + control_side.append(self.control.w_en_position.rotate().scale(-1, 1) + + self.control_position) + control_side.append(self.control.s_en_position.rotate().scale(-1, 1) + + self.control_position) + + bank_side = [] + + for attr_name in (self.sram_property): + bank_side.append(getattr(self,attr_name)[0]) + + for i in range(len(control_side)): + self.add_rect(layer="metal3", + offset=control_side[i], + width=bank_side[i].x - control_side[i].x, + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[bank_side[i].x + drc["minwidth_metal2"], + control_side[i].y], + mirror="R90") + elif (self.num_banks == 2 or self.num_banks == 4): + for i in range(self.control_size): + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.vertical_line_positions[i].x + drc["minwidth_metal2"], + self.control_bus_line_positions[i].y], + mirror="R90") + control_attr = self.bank_property[i] + control_side_line_position = (getattr(self.control,control_attr) + +self.control_position) + + self.add_rect(layer="metal2", + offset=[control_side_line_position.x, + self.control_bus_line_positions[i].y], + width=drc["minwidth_metal2"], + height=control_side_line_position.y + - self.control_bus_line_positions[i].y) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[control_side_line_position.x + + drc["minwidth_metal2"], + self.control_bus_line_positions[i].y], + mirror="R90") + for i in range(self.num_banks/2): + # MSB line connections + msb_line = self.control_size + self.num_banks/2 - 1 - i + bank_select_start_line = msb_line + 2 + self.bank_addr_size + + msf_msb_din = (self.msf_msb_address.din_positions[i].rotate().scale(1, -1) + + self.msf_msb_address_position) + + contact_pos = [self.vertical_line_positions[msb_line].x, + msf_msb_din.y - 0.5*self.m2m3_via.width] + self.add_rect(layer="metal3", + offset=contact_pos, + width=msf_msb_din[0] - contact_pos[0], + height=drc["minwidth_metal3"]) + self.add_via(layers=("metal2", "via2", "metal3"), + offset=contact_pos) + + # msf_msb_address clk connection + self.add_rect(layer="metal1", + offset=[self.vertical_line_positions[0].x, + self.control_bus_line_positions[0].y], + width=self.msf_msb_address_position.x + + self.msf_msb_address.clk_positions[0].y + - self.vertical_line_positions[0].x, + height=drc["minwidth_metal1"]) + + if(self.num_banks == 2): + msb_msf_dout_position = (self.msf_msb_address.dout_positions[i].rotate().scale(1, -1) + + self.msf_msb_address_position) + msb_msf_dout_bar_position = (self.msf_msb_address.dout_bar_positions[i].rotate().scale(1, -1) + + self.msf_msb_address_position) + starts = [msb_msf_dout_bar_position, + msb_msf_dout_position] + + for i in range(2): + bank_select_line = (self.control_size + 1 + + self.bank_addr_size + i) + + start = starts[i] + mid1 = vector(self.msf_msb_address_position.x + + self.msf_msb_address.height + + 4 * (i + 1) * drc["minwidth_metal2"], + start.y) + end = vector(mid1.x, self.msf_msb_address_position[1] + + 4 * (i + 1) * drc["minwidth_metal2"]) + self.add_wire(("metal1", "via1", "metal2"), [start, mid1, end]) + + x_off = self.vertical_line_positions[bank_select_line].x + contact_pos = vector(x_off, + end.y - drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=contact_pos, + width=end.x - contact_pos.x + + 0.5 * drc["minwidth_metal1"], + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=contact_pos) + + if(self.num_banks == 4): + for i in range(2): + msb_msf_out_position = (self.msf_msb_address.dout_positions[i].rotate().scale(1, -1) + + self.msf_msb_address_position) + msb_decoder_in_position =(self.msb_decoder.A_positions[i].scale(-1, 1) + + self.msb_decoder_position + + vector(0, 0.5 * drc["minwidth_metal1"])) + + start = msb_msf_out_position + mid1 = start + vector(4 * (i + 1) * drc["minwidth_metal1"], 0) + mid2 = vector(mid1.x, msb_decoder_in_position.y) + end = vector(self.msb_decoder_position[0] + + 3*drc["minwidth_metal3"], + mid2.y) + + layer_stack = ("metal1", "via1", "metal2") + self.add_wire(layer_stack, [start, mid1, mid2, end]) + + self.add_rect(layer="metal1", + offset=[msb_decoder_in_position.x, + msb_decoder_in_position.y - 0.5 * drc["minwidth_metal1"]], + width=end.x - msb_decoder_in_position.x, + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=end - vector(0, 0.5 * self.m1m2_via.width), + mirror="R90") + for i in range(4): + bank_select_line = self.control_size + 2 + self.bank_addr_size + i + msb_decoder_out = (self.msb_decoder_position + + self.msb_decoder.decode_out_positions[i].scale(-1, 1) + + vector(0, 0.5*drc["minwidth_metal1"])) + + x_off = self.vertical_line_positions[bank_select_line].x + contact_pos = vector(x_off, + msb_decoder_out.y - 0.5*drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=contact_pos, + width=msf_msb_din.x - contact_pos.x, + height=drc["minwidth_metal1"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=contact_pos) + + def route_vdd_singlebank(self): + """ Route the vdd for 1 bank SRAMs """ + + # left vdd rail of bank + self.vdd_offset = self.bank.left_vdd_position + self.add_label(text="vdd", + layer="metal1", + offset=self.vdd_offset) + + # Add label for right vdd rail bank + self.add_label(text="vdd", + layer="metal1", + offset=self.sram_bank_right_vdd_positions[0]) + + # control logic + self.control_vdd1_position = (self.control_position + + self.control.vdd1_position.rotate().scale(-1, 1)) + self.control_vdd2_position = (self.control_position + + self.control.vdd2_position.rotate().scale(-1, 1)) + + self.add_rect(layer="metal1", + offset=self.control_vdd1_position, + width=self.vdd_offset.x + - self.control_vdd1_position.x, + height=drc["minwidth_metal1"]) + + self.add_rect(layer="metal2", + offset=self.control_vdd2_position, + width=self.vdd_offset.x + - self.control_vdd2_position.x, + height=drc["minwidth_metal2"]) + + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.vdd_offset.x, + self.control_vdd2_position.y], + size=(2, 1)) + + def route_vdd_multibank(self): + """ Route the vdd for 2 and 4 bank SRAMs """ + # VDD routing between banks + self.add_rect(layer="metal1", + offset=self.vdd_position, + width=self.width, + height=self.power_rail_width) + for bank_index in range(2): + self.add_rect(layer="metal1", + offset=[self.sram_bank_right_vdd_positions[bank_index].x, + self.vdd_position.y], + width=self.power_rail_width, + height=self.sram_bank_right_vdd_positions[bank_index].y + - self.vdd_position.y) + self.add_rect(layer="metal1", + offset=[self.sram_bank_left_vdd_positions[bank_index].x, + self.vdd_position.y], + width=self.power_rail_width, + height=self.sram_bank_left_vdd_positions[bank_index].y + - self.vdd_position.y) + + # VDD routing to control + control_vdd_supply = self.control_bus_line_positions[self.control_size + 1] + control_vdd1_position = self.control_position + self.control.vdd1_position + control_vdd2_position = self.control_position + self.control.vdd2_position + + # rail extension + self.add_rect(layer="metal1", + offset=self.sram_bank_right_vdd_positions[0], + width=self.power_rail_width, + height=control_vdd_supply.y + - self.sram_bank_right_vdd_positions[0].y) + + # Control vdd1 + if (self.control.width <= self.bank.width): + self.add_rect(layer="metal2", + offset=[control_vdd1_position.x, + control_vdd_supply.y], + width=drc["minwidth_metal2"], + height=control_vdd1_position.y + - control_vdd_supply.y) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=control_vdd1_position) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[control_vdd1_position.x + drc["minwidth_metal2"], + control_vdd_supply.y], + mirror="R90") + + if (self.control.width > self.bank.width): + last_bank = self.num_banks - 1 + self.add_rect(layer="metal1", + offset=control_vdd1_position, + width=self.sram_bank_right_vdd_positions[last_bank].x + - control_vdd1_position.x, + height=drc["minwidth_metal2"]) + self.add_rect(layer="metal1", + offset=self.sram_bank_right_vdd_positions[last_bank], + width=10*drc["minwidth_metal2"], + height=control_vdd1_position.y + - self.sram_bank_right_vdd_positions[last_bank].y + + drc["minwidth_metal2"]) + + # Control vdd2 + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[control_vdd2_position.x + + drc["minwidth_metal2"], + control_vdd_supply.y], + mirror="R90") + + self.add_layout_pin(text="vdd", + layer="metal2", + offset=[control_vdd2_position.x, + control_vdd_supply.y], + width=drc["minwidth_metal2"], + height=control_vdd2_position.y + - control_vdd_supply.y) + + # msf_msb_address + start = msf_address_vdd_position = (self.msf_msb_address_position + + self.msf_msb_address.vdd_positions[0].rotate().scale(1,-1)) + mid1 = vector(start.x, + self.msf_msb_address_position.y + - self.msf_msb_address.width + - 2*drc["minwidth_metal3"]) + end = vector(self.sram_bank_left_vdd_positions[1].x, + mid1.y) + + self.add_path("metal1", [start, mid1, end]) + + # rail extension + self.add_rect(layer="metal1", + offset=self.sram_bank_left_vdd_positions[1], + width=self.power_rail_width, + height=end.y - self.sram_bank_left_vdd_positions[1].y) + + if(self.num_banks == 4): + # msf_msb and msb_decoder VDD + start = (self.msb_decoder_position + + self.msb_decoder.vdd_position.scale(-1, 1) + + vector(0, 0.5*drc["minwidth_metal1"])) + mid1 = vector(msf_address_vdd_position.x, + start.y) + end = msf_address_vdd_position + + self.add_path("metal1", [start, mid1, end]) + + # Add vdd labels to horizotal and vertical lines + self.add_label(text="vdd", + layer="metal1", + offset=self.vdd_position) + self.add_label(text="vdd", + layer="metal1", + offset=[self.sram_bank_left_vdd_positions[0].x, + self.vdd_position.y]) + self.add_label(text="vdd", + layer="metal1", + offset=[self.sram_bank_left_vdd_positions[1].x, + self.vdd_position.y]) + self.add_label(text="vdd", + layer="metal1", + offset=[self.sram_bank_right_vdd_positions[1].x, + self.vdd_position.y]) + + + def route_gnd_singlebank(self): + """ Route the gnd for 1 bank SRAMs """ + + # left gnd rail of bank + self.gnd_offset = self.bank.left_gnd_position + self.add_label(text="gnd", + layer="metal2", + offset=self.gnd_offset) + + self.control_gnd_position = (self.control_position + + self.control.gnd_position.rotate().scale(-1,1) + + vector(drc["minwidth_metal2"],0)) + + self.add_rect(layer="metal3", + offset=self.control_gnd_position, + width=self.gnd_offset.x - self.control_gnd_position.x, + height=drc["minwidth_metal3"]) + + self.add_via(layers=("metal2", "via2", "metal3"), + offset=[self.gnd_offset.x, + self.control_gnd_position.y], + size=(2,1)) + + self.add_via(layers=("metal2", "via2", "metal3"), + offset=self.control_gnd_position, + rotate=90) + + + def route_gnd_multibank(self): + """ Route the gnd for 2 and 4 bank SRAMs """ + self.add_rect(layer="metal2", + offset=self.gnd_position, + width=self.width, + height=self.power_rail_width) + + for bank_index in range(2): + self.add_rect(layer="metal2", + offset=[self.sram_bank_left_gnd_positions[bank_index].x, + self.gnd_position.y], + width=self.power_rail_width, + height=self.sram_bank_left_gnd_positions[bank_index].y + - self.gnd_position.y) + + # gnd routing to control + control_gnd_supply = self.control_bus_line_positions[self.control_size] + control_gnd_position = self.control_position + self.control.gnd_position + + # rail extension + self.add_rect(layer="metal2", + offset=self.sram_bank_left_gnd_positions[0], + width=drc["minwidth_metal2"], + height=control_gnd_supply[1] + drc["minwidth_metal1"] + - self.sram_bank_left_gnd_positions[0].y) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.sram_bank_left_gnd_positions[0].x + + drc["minwidth_metal2"], + control_gnd_supply[1]], + mirror="R90") + # Control gnd + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[control_gnd_position.x + drc["minwidth_metal2"], + control_gnd_supply.y], + mirror="R90") + self.add_layout_pin(text="gnd", + layer="metal2", + offset=[control_gnd_position.x, + control_gnd_supply.y], + width=drc["minwidth_metal2"], + height=control_gnd_position.y + - control_gnd_supply.y) + + # msf_msb_address , msb_decoder gnd + + # left gnd rail extension of bank3 + self.add_rect(layer="metal2", + offset=self.sram_bank_left_gnd_positions[1], + width=self.power_rail_width, + height=self.max_point + - self.sram_bank_left_gnd_positions[1].y) + + for p in self.msf_msb_address.gnd_positions: + gnd_position = vector(self.msf_msb_address_position.x + + self.msf_msb_address.height, + self.msf_msb_address_position.y + - p.x - 0.5*drc["minwidth_metal2"]) + self.add_rect(layer="metal2", + offset=gnd_position, + width=(self.sram_bank_left_gnd_positions[1].x + - gnd_position.x), + height=drc["minwidth_metal2"]) + + if(self.num_banks == 4): + # msb Decoder + msb_decoder_gnd_position = (self.msb_decoder_position + + self.msb_decoder.gnd_position.scale(-1,1)) + self.add_rect(layer="metal1", + offset=msb_decoder_gnd_position, + width=self.sram_bank_left_gnd_positions[3].x \ + - msb_decoder_gnd_position.x \ + + self.power_rail_width, + height=drc["minwidth_metal1"]) + + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.sram_bank_left_gnd_positions[3].x, + msb_decoder_gnd_position.y + drc["minwidth_metal1"]], + mirror="MX", + size=(2,1)) + + # Add gnd labels to horizotal and vertical lines + self.add_label(text="gnd", + layer="metal2", + offset=self.gnd_position) + self.add_label(text="gnd", + layer="metal2", + offset=[self.sram_bank_left_gnd_positions[0].x, + 0]) + self.add_label(text="gnd", + layer="metal2", + offset=[self.sram_bank_left_gnd_positions[1].x, + 0]) + + def route_supplies(self): + """ vdd/gnd routing of all modules """ + + if (self.num_banks == 1): + self.route_vdd_singlebank() + self.route_gnd_singlebank() + elif (self.num_banks == 2 or self.num_banks == 4): + self.route_vdd_multibank() + self.route_gnd_multibank() + else: + debug.error("Incorrect number of banks.") + + + def sp_write(self, sp_name): + # Write the entire spice of the object to the file + ############################################################ + # Spice circuit + ############################################################ + sp = open(sp_name, 'w') + + sp.write("* OpenRAM generated memory.\n") + # This causes unit test mismatch + #sp.write("* Created: {0}\n".format(datetime.datetime.now())) + sp.write("* User: {0}\n".format(getpass.getuser())) + sp.write(".global {0} {1}\n".format(spice["vdd_name"], + spice["gnd_name"])) + usedMODS = list() + self.sp_write_file(sp, usedMODS) + del usedMODS + sp.close() diff --git a/compiler/tests/00_code_format_check_test.py b/compiler/tests/00_code_format_check_test.py new file mode 100644 index 00000000..b732edde --- /dev/null +++ b/compiler/tests/00_code_format_check_test.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python2.7 + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre +import re + +#@unittest.skip("SKIPPING 00_format check test") + + +class code_format_test(unittest.TestCase): + "Run a test to check for tabs instead of spaces in the all source files." + + def runTest(self): + source_code_dir = os.environ["OPENRAM_HOME"] + "/compiler" + source_codes = setup_files(source_code_dir) + errors = 0 + + for code in source_codes: + if re.search("gdsMill", code): + continue + errors += check_file_format_tab(code) + + for code in source_codes: + if re.search("gdsMill", code): + continue + if re.search("options.py$", code): + continue + if re.search("debug.py$", code): + continue + if re.search("header.py$", code): + continue + if re.search("openram.py$", code): + continue + errors += check_print_output(code) + + # fails if there are any tabs in any files + self.assertEqual(errors, 0) + + +def setup_files(path): + files = [] + for (dir, _, current_files) in os.walk(path): + for f in current_files: + files.append(os.path.join(dir, f)) + nametest = re.compile("\.py$", re.IGNORECASE) + select_files = filter(nametest.search, files) + return select_files + + +def check_file_format_tab(file_name): + """Check if any files contain tabs and return the number of tabs.""" + f = open(file_name, "r+b") + key_positions = [] + for num, line in enumerate(f, 1): + if '\t' in line: + key_positions.append(num) + if len(key_positions) > 0: + debug.info(0, '\nFound ' + str(len(key_positions)) + ' tabs in ' + + str(file_name) + ' (line ' + str(key_positions[0]) + ')') + return len(key_positions) + + +def check_print_output(file_name): + """Check if any files (except debug.py) call the _print_ function. We should + use the debug output with verbosity instead!""" + file = open(file_name, "r+b") + line = file.read() + # skip comments with a hash + line = re.sub(r'#.*', '', line) + # skip doc string comments + line=re.sub(r'\"\"\"[^\"]*\"\"\"', '', line, flags=re.S|re.M) + count = len(re.findall("\s*print\s+", line)) + if count > 0: + debug.info(0, "\nFound " + str(count) + + " _print_ calls " + str(file_name)) + + return(count) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py new file mode 100644 index 00000000..63f7f90e --- /dev/null +++ b/compiler/tests/01_library_drc_test.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python2.7 +"Run a regresion test the library cells for DRC" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre +import re + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 01_library_drc_test") + + +class library_drc_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + (gds_dir, gds_files) = setup_files() + drc_errors = 0 + debug.info(1, "\nPerforming DRC on: " + ", ".join(gds_files)) + for f in gds_files: + name = re.sub('\.gds$', '', f) + gds_name = "{0}/{1}".format(gds_dir, f) + if not os.path.isfile(gds_name): + drc_errors += 1 + debug.error("Missing GDS file: {}".format(gds_name)) + drc_errors += calibre.run_drc(name, gds_name) + + # fails if there are any DRC errors on any cells + self.assertEqual(drc_errors, 0) + + +def setup_files(): + gds_dir = OPTS.openram_tech + "/gds_lib" + files = os.listdir(gds_dir) + nametest = re.compile("\.gds$", re.IGNORECASE) + gds_files = filter(nametest.search, files) + return (gds_dir, gds_files) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py new file mode 100644 index 00000000..356ad075 --- /dev/null +++ b/compiler/tests/02_library_lvs_test.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python2.7 +"Run a regresion test the library cells for LVS" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre +import re + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 02_lvs_test") + + +class library_lvs_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + (gds_dir, sp_dir, allnames) = setup_files() + lvs_errors = 0 + debug.info(1, "Performing LVS on: " + ", ".join(allnames)) + + for f in allnames: + gds_name = "{0}/{1}.gds".format(gds_dir, f) + sp_name = "{0}/{1}.sp".format(sp_dir, f) + if not os.path.isfile(gds_name): + lvs_errors += 1 + debug.error("Missing GDS file {}".format(gds_name)) + if not os.path.isfile(sp_name): + lvs_errors += 1 + debug.error("Missing SPICE file {}".format(gds_name)) + lvs_errors += calibre.run_lvs(f, gds_name, sp_name) + + # fail if the error count is not zero + self.assertEqual(lvs_errors, 0) + + +def setup_files(): + gds_dir = OPTS.openram_tech + "/gds_lib" + sp_dir = OPTS.openram_tech + "/sp_lib" + files = os.listdir(gds_dir) + nametest = re.compile("\.gds$", re.IGNORECASE) + gds_files = filter(nametest.search, files) + files = os.listdir(sp_dir) + nametest = re.compile("\.sp$", re.IGNORECASE) + sp_files = filter(nametest.search, files) + + # make a list of all the gds and spice files + tempnames = gds_files + tempnames.extend(sp_files) + + # remove the .gds and .sp suffixes + for i in range(len(tempnames)): + tempnames[i] = re.sub('\.gds$', '', tempnames[i]) + tempnames[i] = re.sub('\.sp$', '', tempnames[i]) + + # remove duplicate base names + nameset = set(tempnames) + allnames = list(nameset) + + return (gds_dir, sp_dir, allnames) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/03_contact_test.py b/compiler/tests/03_contact_test.py new file mode 100644 index 00000000..502a0e2f --- /dev/null +++ b/compiler/tests/03_contact_test.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python2.7 +"Run a regresion test for DRC on basic contacts of different array sizes" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 03_contact_test") + + +class contact_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import contact + + + for layer_stack in [("poly", "contact", "metal1"), ("metal1", "via1", "metal2")]: + stack_name = ":".join(map(str, layer_stack)) + + # Check single 1 x 1 contact" + debug.info(2, "1 x 1 {} test".format(stack_name)) + OPTS.check_lvsdrc = False + c = contact.contact(layer_stack, (1, 1)) + OPTS.check_lvsdrc = True + self.local_check(c) + + # check vertical array with one in the middle and two ends + debug.info(2, "1 x 3 {} test".format(stack_name)) + OPTS.check_lvsdrc = False + c = contact.contact(layer_stack, (1, 3)) + OPTS.check_lvsdrc = True + self.local_check(c) + + # check horizontal array with one in the middle and two ends + debug.info(2, "3 x 1 {} test".format(stack_name)) + OPTS.check_lvsdrc = False + c = contact.contact(layer_stack, (3, 1)) + OPTS.check_lvsdrc = True + self.local_check(c) + + # check 3x3 array for all possible neighbors + debug.info(2, "3 x 3 {} test".format(stack_name)) + OPTS.check_lvsdrc = False + c = contact.contact(layer_stack, (3, 3)) + OPTS.check_lvsdrc = True + self.local_check(c) + + OPTS.check_lvsdrc = True + + def local_check(self, c): + tempgds = OPTS.openram_temp + "temp.gds" + c.gds_write(tempgds) + self.assertFalse(calibre.run_drc(c.name, tempgds)) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/03_path_test.py b/compiler/tests/03_path_test.py new file mode 100644 index 00000000..dd622a13 --- /dev/null +++ b/compiler/tests/03_path_test.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python2.7 +"Run a regresion test on a basic path" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 03_path_test") + + +class path_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import path + import tech + + min_space = 2 * tech.drc["minwidth_metal1"] + layer_stack = ("metal1") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + OPTS.check_lvsdrc = False + w = path.path(layer_stack, position_list) + self.local_check(w) + OPTS.check_lvsdrc = True + + min_space = 2 * tech.drc["minwidth_metal2"] + layer_stack = ("metal2") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + OPTS.check_lvsdrc = False + w = path.path(layer_stack, position_list) + self.local_check(w) + OPTS.check_lvsdrc = True + + min_space = 2 * tech.drc["minwidth_metal3"] + layer_stack = ("metal3") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + # run on the reverse list + position_list.reverse() + OPTS.check_lvsdrc = False + w = path.path(layer_stack, position_list) + OPTS.check_lvsdrc = True + + self.local_check(w) + + # return it back to it's normal state + OPTS.check_lvsdrc = True + + def local_check(self, w): + tempgds = OPTS.openram_temp + "temp.gds" + w.gds_write(tempgds) + self.assertFalse(calibre.run_drc(w.name, tempgds)) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/03_ptx_1finger_nmos_test.py b/compiler/tests/03_ptx_1finger_nmos_test.py new file mode 100644 index 00000000..07d233e6 --- /dev/null +++ b/compiler/tests/03_ptx_1finger_nmos_test.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python2.7 +"Run a regresion test on a basic parameterized transistors" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 03_ptx_test") + + +class ptx_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import ptx + import tech + + debug.info(2, "Checking min size NMOS with 1 finger") + fet = ptx.ptx(name="nmos_1_finger", + width=tech.drc["minwidth_tx"], + mults=1, + tx_type="nmos") + # return it back to it's normal state + OPTS.check_lvsdrc = True + + self.local_check(fet) + + + + def add_mods(self, fet): + self.create_contacts() + self.add_well_extension(fet) + self.add_wire_extension(fet) + self.add_well_tiedown(fet) + self.add_poly_tiedown(fet) + + def create_contacts(self): + layer_stack = ("active", "contact", "metal1") + self.well_contact = contact.contact(layer_stack) + + layer_stack = ("poly", "contact", "metal1") + self.poly_contact = contact.contact(layer_stack) + + def add_well_tiedown(self, fet): + offset = [fet.active_contact_positions[0][0], + fet.active_contact_positions[0][1] + fet.well_height] + fet.add_inst(name="well_tap", + mod=self.well_contact, + offset=offset, + mirror="R0", + rotate=0) + fet.well_contact = self.well_contact + fet.well_tiedown_location = offset + + def add_well_extension(self, fet): + well_define = {"pmos": "nwell", + "nmos": "pwell"} + well_type = well_define[fet.tx_type] + offset = getattr(fet,"{}_position".format(well_type)) + if tech.info["has_{0}".format(well_type)]: + fet.add_rect(layerNumber=tech.layer[well_type], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["{0}implant".format(fet.tx_type[0])], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["vtg"], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + + well_type = "{0}well".format(fet.tx_type[0]) + offset[1] = offset[1] - 3 * fet.well_height + if tech.info["has_{0}".format(well_type)]: + fet.add_rect(layerNumber=tech.layer[well_type], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["{0}implant".format(well_define[fet.tx_type][ + 0])], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["vtg"], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + + def add_wire_extension(self, fet): + xcorrect = (fet.active_contact.width / 2) - (tech.drc["minwidth_metal1"] / 2) + offset = [fet.active_contact_positions[0][0] + xcorrect, + fet.active_contact_positions[0][1]] + fet.add_rect(layerNumber=tech.layer["metal1"], + offset=offset, + width=tech.drc["minwidth_metal1"], + height=fet.well_height) + + offset = [fet.active_contact_positions[-1][0] + xcorrect, + fet.active_contact_positions[-1][1] - 2 * fet.well_height] + fet.add_rect(layerNumber=tech.layer["metal1"], + offset=offset, + width=tech.drc["minwidth_metal1"], + height=2 * fet.well_height) + + offset = [fet.poly_positions[-1][0], + fet.poly_positions[-1][1] - (fet.well_height)] + fet.add_rect(layerNumber=tech.layer["poly"], + offset=offset, + width=tech.drc["minwidth_poly"], + height=fet.well_height) + + def add_poly_tiedown(self, fet): + xcorrect = abs(self.poly_contact.upper_layer_vertical_enclosure - + self.poly_contact.lower_layer_vertical_enclosure) + offset = [fet.poly_positions[-1][0] - xcorrect, + fet.poly_positions[-1][1] - (fet.well_height)] + fet.add_inst(name="poly_contact", + mod=self.poly_contact, + offset=offset, + mirror="R270") + + + offset = [fet.active_contact_positions[-1][0], fet.active_contact_positions + [-1][1] - 2 * fet.well_height - self.well_contact.height] + fet.poly_tiedown_location = offset + fet.add_inst(name="n_tiedown", + mod=self.well_contact, + offset=offset) + tech.ptx_port.add_custom_layer(fet) + + def local_check(self, fet): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + fet.sp_write(tempspice) + fet.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(fet.name, tempgds)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/03_ptx_1finger_pmos_test.py b/compiler/tests/03_ptx_1finger_pmos_test.py new file mode 100644 index 00000000..7c9a012a --- /dev/null +++ b/compiler/tests/03_ptx_1finger_pmos_test.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python2.7 +"Run a regresion test on a basic parameterized transistors" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 03_ptx_test") + + +class ptx_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import ptx + import tech + + debug.info(2, "Checking min size PMOS with 1 finger") + fet = ptx.ptx(name="pmos_1_finger", + width=tech.drc["minwidth_tx"], + mults=1, + tx_type="pmos") + # return it back to it's normal state + OPTS.check_lvsdrc = True + + self.local_check(fet) + + + + def add_mods(self, fet): + self.create_contacts() + self.add_well_extension(fet) + self.add_wire_extension(fet) + self.add_well_tiedown(fet) + self.add_poly_tiedown(fet) + + def create_contacts(self): + layer_stack = ("active", "contact", "metal1") + self.well_contact = contact.contact(layer_stack) + + layer_stack = ("poly", "contact", "metal1") + self.poly_contact = contact.contact(layer_stack) + + def add_well_tiedown(self, fet): + offset = [fet.active_contact_positions[0][0], + fet.active_contact_positions[0][1] + fet.well_height] + fet.add_inst(name="well_tap", + mod=self.well_contact, + offset=offset, + mirror="R0", + rotate=0) + fet.well_contact = self.well_contact + fet.well_tiedown_location = offset + + def add_well_extension(self, fet): + well_define = {"pmos": "nwell", + "nmos": "pwell"} + well_type = well_define[fet.tx_type] + offset = getattr(fet,"{}_position".format(well_type)) + if tech.info["has_{0}".format(well_type)]: + fet.add_rect(layerNumber=tech.layer[well_type], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["{0}implant".format(fet.tx_type[0])], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["vtg"], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + + well_type = "{0}well".format(fet.tx_type[0]) + offset[1] = offset[1] - 3 * fet.well_height + if tech.info["has_{0}".format(well_type)]: + fet.add_rect(layerNumber=tech.layer[well_type], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["{0}implant".format(well_define[fet.tx_type][ + 0])], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["vtg"], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + + def add_wire_extension(self, fet): + xcorrect = (fet.active_contact.width / 2) - (tech.drc["minwidth_metal1"] / 2) + offset = [fet.active_contact_positions[0][0] + xcorrect, + fet.active_contact_positions[0][1]] + fet.add_rect(layerNumber=tech.layer["metal1"], + offset=offset, + width=tech.drc["minwidth_metal1"], + height=fet.well_height) + + offset = [fet.active_contact_positions[-1][0] + xcorrect, + fet.active_contact_positions[-1][1] - 2 * fet.well_height] + fet.add_rect(layerNumber=tech.layer["metal1"], + offset=offset, + width=tech.drc["minwidth_metal1"], + height=2 * fet.well_height) + + offset = [fet.poly_positions[-1][0], + fet.poly_positions[-1][1] - (fet.well_height)] + fet.add_rect(layerNumber=tech.layer["poly"], + offset=offset, + width=tech.drc["minwidth_poly"], + height=fet.well_height) + + def add_poly_tiedown(self, fet): + xcorrect = abs(self.poly_contact.upper_layer_vertical_enclosure - + self.poly_contact.lower_layer_vertical_enclosure) + offset = [fet.poly_positions[-1][0] - xcorrect, + fet.poly_positions[-1][1] - (fet.well_height)] + fet.add_inst(name="poly_contact", + mod=self.poly_contact, + offset=offset, + mirror="R270") + + + offset = [fet.active_contact_positions[-1][0], fet.active_contact_positions + [-1][1] - 2 * fet.well_height - self.well_contact.height] + fet.poly_tiedown_location = offset + fet.add_inst(name="n_tiedown", + mod=self.well_contact, + offset=offset) + tech.ptx_port.add_custom_layer(fet) + + def local_check(self, fet): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + fet.sp_write(tempspice) + fet.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(fet.name, tempgds)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/03_ptx_3finger_nmos_test.py b/compiler/tests/03_ptx_3finger_nmos_test.py new file mode 100644 index 00000000..d146cea7 --- /dev/null +++ b/compiler/tests/03_ptx_3finger_nmos_test.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python2.7 +"Run a regresion test on a basic parameterized transistors" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 03_ptx_test") + + +class ptx_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import ptx + import tech + + debug.info(2, "Checking three fingers NMOS") + fet = ptx.ptx(name="nmos_3_fingers", + width=tech.drc["minwidth_tx"], + mults=3, + tx_type="nmos") + # return it back to it's normal state + OPTS.check_lvsdrc = True + + self.local_check(fet) + + + def add_mods(self, fet): + self.create_contacts() + self.add_well_extension(fet) + self.add_wire_extension(fet) + self.add_well_tiedown(fet) + self.add_poly_tiedown(fet) + + def create_contacts(self): + layer_stack = ("active", "contact", "metal1") + self.well_contact = contact.contact(layer_stack) + + layer_stack = ("poly", "contact", "metal1") + self.poly_contact = contact.contact(layer_stack) + + def add_well_tiedown(self, fet): + offset = [fet.active_contact_positions[0][0], + fet.active_contact_positions[0][1] + fet.well_height] + fet.add_inst(name="well_tap", + mod=self.well_contact, + offset=offset, + mirror="R0", + rotate=0) + fet.well_contact = self.well_contact + fet.well_tiedown_location = offset + + def add_well_extension(self, fet): + well_define = {"pmos": "nwell", + "nmos": "pwell"} + well_type = well_define[fet.tx_type] + offset = getattr(fet,"{}_position".format(well_type)) + if tech.info["has_{0}".format(well_type)]: + fet.add_rect(layerNumber=tech.layer[well_type], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["{0}implant".format(fet.tx_type[0])], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["vtg"], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + + well_type = "{0}well".format(fet.tx_type[0]) + offset[1] = offset[1] - 3 * fet.well_height + if tech.info["has_{0}".format(well_type)]: + fet.add_rect(layerNumber=tech.layer[well_type], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["{0}implant".format(well_define[fet.tx_type][ + 0])], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["vtg"], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + + def add_wire_extension(self, fet): + xcorrect = (fet.active_contact.width / 2) - (tech.drc["minwidth_metal1"] / 2) + offset = [fet.active_contact_positions[0][0] + xcorrect, + fet.active_contact_positions[0][1]] + fet.add_rect(layerNumber=tech.layer["metal1"], + offset=offset, + width=tech.drc["minwidth_metal1"], + height=fet.well_height) + + offset = [fet.active_contact_positions[-1][0] + xcorrect, + fet.active_contact_positions[-1][1] - 2 * fet.well_height] + fet.add_rect(layerNumber=tech.layer["metal1"], + offset=offset, + width=tech.drc["minwidth_metal1"], + height=2 * fet.well_height) + + offset = [fet.poly_positions[-1][0], + fet.poly_positions[-1][1] - (fet.well_height)] + fet.add_rect(layerNumber=tech.layer["poly"], + offset=offset, + width=tech.drc["minwidth_poly"], + height=fet.well_height) + + def add_poly_tiedown(self, fet): + xcorrect = abs(self.poly_contact.upper_layer_vertical_enclosure - + self.poly_contact.lower_layer_vertical_enclosure) + offset = [fet.poly_positions[-1][0] - xcorrect, + fet.poly_positions[-1][1] - (fet.well_height)] + fet.add_inst(name="poly_contact", + mod=self.poly_contact, + offset=offset, + mirror="R270") + + + offset = [fet.active_contact_positions[-1][0], fet.active_contact_positions + [-1][1] - 2 * fet.well_height - self.well_contact.height] + fet.poly_tiedown_location = offset + fet.add_inst(name="n_tiedown", + mod=self.well_contact, + offset=offset) + tech.ptx_port.add_custom_layer(fet) + + def local_check(self, fet): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + fet.sp_write(tempspice) + fet.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(fet.name, tempgds)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/03_ptx_3finger_pmos_test.py b/compiler/tests/03_ptx_3finger_pmos_test.py new file mode 100644 index 00000000..603e0b64 --- /dev/null +++ b/compiler/tests/03_ptx_3finger_pmos_test.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python2.7 +"Run a regresion test on a basic parameterized transistors" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 03_ptx_test") + + +class ptx_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import ptx + import tech + + debug.info(2, "Checking three fingers PMOS") + fet = ptx.ptx(name="pmos_3_fingers", + width=tech.drc["minwidth_tx"], + mults=3, + tx_type="pmos") + # return it back to it's normal state + OPTS.check_lvsdrc = True + + self.local_check(fet) + + def add_mods(self, fet): + self.create_contacts() + self.add_well_extension(fet) + self.add_wire_extension(fet) + self.add_well_tiedown(fet) + self.add_poly_tiedown(fet) + + def create_contacts(self): + layer_stack = ("active", "contact", "metal1") + self.well_contact = contact.contact(layer_stack) + + layer_stack = ("poly", "contact", "metal1") + self.poly_contact = contact.contact(layer_stack) + + def add_well_tiedown(self, fet): + offset = [fet.active_contact_positions[0][0], + fet.active_contact_positions[0][1] + fet.well_height] + fet.add_inst(name="well_tap", + mod=self.well_contact, + offset=offset, + mirror="R0", + rotate=0) + fet.well_contact = self.well_contact + fet.well_tiedown_location = offset + + def add_well_extension(self, fet): + well_define = {"pmos": "nwell", + "nmos": "pwell"} + well_type = well_define[fet.tx_type] + offset = getattr(fet,"{}_position".format(well_type)) + if tech.info["has_{0}".format(well_type)]: + fet.add_rect(layerNumber=tech.layer[well_type], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["{0}implant".format(fet.tx_type[0])], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["vtg"], + offset=offset, + width=fet.well_width, + height=2 * fet.well_height) + + well_type = "{0}well".format(fet.tx_type[0]) + offset[1] = offset[1] - 3 * fet.well_height + if tech.info["has_{0}".format(well_type)]: + fet.add_rect(layerNumber=tech.layer[well_type], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["{0}implant".format(well_define[fet.tx_type][ + 0])], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + fet.add_rect(layerNumber=tech.layer["vtg"], + offset=offset, + width=fet.well_width, + height=3 * fet.well_height) + + def add_wire_extension(self, fet): + xcorrect = (fet.active_contact.width / 2) - (tech.drc["minwidth_metal1"] / 2) + offset = [fet.active_contact_positions[0][0] + xcorrect, + fet.active_contact_positions[0][1]] + fet.add_rect(layerNumber=tech.layer["metal1"], + offset=offset, + width=tech.drc["minwidth_metal1"], + height=fet.well_height) + + offset = [fet.active_contact_positions[-1][0] + xcorrect, + fet.active_contact_positions[-1][1] - 2 * fet.well_height] + fet.add_rect(layerNumber=tech.layer["metal1"], + offset=offset, + width=tech.drc["minwidth_metal1"], + height=2 * fet.well_height) + + offset = [fet.poly_positions[-1][0], + fet.poly_positions[-1][1] - (fet.well_height)] + fet.add_rect(layerNumber=tech.layer["poly"], + offset=offset, + width=tech.drc["minwidth_poly"], + height=fet.well_height) + + def add_poly_tiedown(self, fet): + xcorrect = abs(self.poly_contact.upper_layer_vertical_enclosure - + self.poly_contact.lower_layer_vertical_enclosure) + offset = [fet.poly_positions[-1][0] - xcorrect, + fet.poly_positions[-1][1] - (fet.well_height)] + fet.add_inst(name="poly_contact", + mod=self.poly_contact, + offset=offset, + mirror="R270") + + + offset = [fet.active_contact_positions[-1][0], fet.active_contact_positions + [-1][1] - 2 * fet.well_height - self.well_contact.height] + fet.poly_tiedown_location = offset + fet.add_inst(name="n_tiedown", + mod=self.well_contact, + offset=offset) + tech.ptx_port.add_custom_layer(fet) + + def local_check(self, fet): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + fet.sp_write(tempspice) + fet.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(fet.name, tempgds)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/03_wire_test.py b/compiler/tests/03_wire_test.py new file mode 100644 index 00000000..b458d458 --- /dev/null +++ b/compiler/tests/03_wire_test.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python2.7 +"Run a regresion test on a basic wire" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 03_wire_test") + + +class wire_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import wire + import tech + min_space = 2 * (tech.drc["minwidth_poly"] + + tech.drc["minwidth_metal1"]) + layer_stack = ("poly", "contact", "metal1") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + OPTS.check_lvsdrc = False + w = wire.wire(layer_stack, position_list) + OPTS.check_lvsdrc = True + self.local_check(w) + + min_space = 2 * (tech.drc["minwidth_poly"] + + tech.drc["minwidth_metal1"]) + layer_stack = ("metal1", "contact", "poly") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + OPTS.check_lvsdrc = False + w = wire.wire(layer_stack, position_list) + OPTS.check_lvsdrc = True + self.local_check(w) + + min_space = 2 * (tech.drc["minwidth_metal2"] + + tech.drc["minwidth_metal1"]) + layer_stack = ("metal1", "via1", "metal2") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + OPTS.check_lvsdrc = False + w = wire.wire(layer_stack, position_list) + OPTS.check_lvsdrc = True + self.local_check(w) + + + min_space = 2 * (tech.drc["minwidth_metal2"] + + tech.drc["minwidth_metal1"]) + layer_stack = ("metal2", "via1", "metal1") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + OPTS.check_lvsdrc = False + w = wire.wire(layer_stack, position_list) + OPTS.check_lvsdrc = True + self.local_check(w) + + min_space = 2 * (tech.drc["minwidth_metal2"] + + tech.drc["minwidth_metal3"]) + layer_stack = ("metal2", "via2", "metal3") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + position_list.reverse() + OPTS.check_lvsdrc = False + w = wire.wire(layer_stack, position_list) + OPTS.check_lvsdrc = True + self.local_check(w) + + min_space = 2 * (tech.drc["minwidth_metal2"] + + tech.drc["minwidth_metal3"]) + layer_stack = ("metal3", "via2", "metal2") + position_list = [[0, 0], + [0, 3 * min_space], + [1 * min_space, 3 * min_space], + [4 * min_space, 3 * min_space], + [4 * min_space, 0], + [7 * min_space, 0], + [7 * min_space, 4 * min_space], + [-1 * min_space, 4 * min_space], + [-1 * min_space, 0]] + position_list.reverse() + OPTS.check_lvsdrc = False + w = wire.wire(layer_stack, position_list) + OPTS.check_lvsdrc = True + self.local_check(w) + + # return it back to it's normal state + OPTS.check_lvsdrc = True + + def local_check(self, w): + tempgds = OPTS.openram_temp + "temp.gds" + w.gds_write(tempgds) + self.assertFalse(calibre.run_drc(w.name, tempgds)) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/04_nand_2_test.py b/compiler/tests/04_nand_2_test.py new file mode 100644 index 00000000..1472cb0d --- /dev/null +++ b/compiler/tests/04_nand_2_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized nand_2. This module doesn't +generate multi_finger 2_input nand gate. It generate only the minimum +size 2_input nand gate that is nmos_width=2*tech.drc[minwidth_tx]. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre +import sys + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_nand_2_test") + + +class nand_2_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import nand_2 + import tech + + debug.info(2, "Checking 2-input nand gate") + tx = nand_2.nand_2(name="a_nand_1", nmos_width=2 * tech.drc["minwidth_tx"]) + OPTS.check_lvsdrc = True + self.local_check(tx) + + + def local_check(self, tx): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(tx.name, tempgds)) + self.assertFalse(calibre.run_lvs(tx.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/04_nand_3_test.py b/compiler/tests/04_nand_3_test.py new file mode 100644 index 00000000..d751a8a3 --- /dev/null +++ b/compiler/tests/04_nand_3_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized nand_3. +This module doesn't generate multi_finger 3_input nand gate. +It generate only the minimum size 3_input nand gate that is nmos_width=3*tech.drc[minwidth_tx]. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_nand_3_test") +class nand_3_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import nand_3 + import tech + + debug.info(2, "Checking 3-input nand gate") + tx = nand_3.nand_3(name="nand_3", nmos_width=3 * tech.drc["minwidth_tx"]) + + OPTS.check_lvsdrc = True + self.local_check(tx) + + def local_check(self, tx): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(tx.name, tempgds)) + self.assertFalse(calibre.run_lvs(tx.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/04_nor_2_test.py b/compiler/tests/04_nor_2_test.py new file mode 100644 index 00000000..746891eb --- /dev/null +++ b/compiler/tests/04_nor_2_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized nor_2 +This module doesn't generate multi_finger 2_input nor gate +It generate only the minimum size 2_input nor gate that is nmos_width=2*tech.drc[minwidth_tx] +""" +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre +import sys + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_nor_2_test") + + +class nor_2_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import nor_2 + import tech + + debug.info(2, "Checking 2-input nor gate") + tx = nor_2.nor_2(name="a_nor_1", nmos_width=2 * tech.drc["minwidth_tx"]) + OPTS.check_lvsdrc = True + self.local_check(tx) + + + def local_check(self, tx): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(tx.name, tempgds)) + self.assertFalse(calibre.run_lvs(tx.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/04_pinv_test.py b/compiler/tests/04_pinv_test.py new file mode 100644 index 00000000..ecbbb7e6 --- /dev/null +++ b/compiler/tests/04_pinv_test.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python2.7 +""" +Run regresion tests on a parameterized inverter +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_pinv_test") + + +class pinv_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import pinv + import tech + + debug.info(2, "Checking min size inverter") + OPTS.check_lvsdrc = False + tx = pinv.pinv(name="a_inv_1", nmos_width=tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"]) + OPTS.check_lvsdrc = True + self.local_check(tx) + + debug.info(2, "Checking 2x min size inverter") + OPTS.check_lvsdrc = False + tx = pinv.pinv(name="a_inv_2", nmos_width=2 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"]) + OPTS.check_lvsdrc = True + self.local_check(tx) + + debug.info(2, "Checking 5x min size inverter") + OPTS.check_lvsdrc = False + tx = pinv.pinv(name="a_inv_5", nmos_width=5 * tech.drc["minwidth_tx"], beta=tech.parameter["pinv_beta"]) + OPTS.check_lvsdrc = True + self.local_check(tx) + + + def local_check(self, tx): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(tx.name, tempgds)) + self.assertFalse(calibre.run_lvs(tx.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/04_wordline_driver_test.py b/compiler/tests/04_wordline_driver_test.py new file mode 100644 index 00000000..d5f6a7c4 --- /dev/null +++ b/compiler/tests/04_wordline_driver_test.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a wordline_driver array +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre +import sys + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 04_driver_test") + + +class wordline_driver_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import wordline_driver + import tech + + debug.info(2, "Checking driver") + tx = wordline_driver.wordline_driver(name="Wordline_driver", rows=8) + + OPTS.check_lvsdrc = True + + self.local_check(tx) + + def local_check(self, tx): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + tx.sp_write(tempspice) + tx.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(tx.name, tempgds)) + self.assertFalse(calibre.run_lvs(tx.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/05_bitcell_array_test.py b/compiler/tests/05_bitcell_array_test.py new file mode 100644 index 00000000..e794c72c --- /dev/null +++ b/compiler/tests/05_bitcell_array_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a basic array +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + +#@unittest.skip("SKIPPING 05_array_test") + + +class array_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import bitcell_array + + debug.info(2, "Testing 3x3 array for 6t_cell") + a = bitcell_array.bitcell_array(name="bitcell_array", cols=3, rows=3) + + OPTS.check_lvsdrc = True + + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + temppdf = OPTS.openram_temp + "temp.pdf" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/06_hierarchical_decoder_test.py b/compiler/tests/06_hierarchical_decoder_test.py new file mode 100644 index 00000000..a087c5f3 --- /dev/null +++ b/compiler/tests/06_hierarchical_decoder_test.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a thierarchy_decoder. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + + +class hierarchical_decoder_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import hierarchical_decoder + import tech + + debug.info(1, "Testing sample for hierarchy_decoder") + OPTS.check_lvsdrc = False + a = hierarchical_decoder.hierarchical_decoder( + nand2_nmos_width=2 * tech.drc["minwidth_tx"], nand3_nmos_width=3 * tech.drc["minwidth_tx"], rows=4) + OPTS.check_lvsdrc = True + self.local_check(a) + + debug.info(1, "Testing sample for hierarchy_decoder") + OPTS.check_lvsdrc = False + a = hierarchical_decoder.hierarchical_decoder( + nand2_nmos_width=2 * tech.drc["minwidth_tx"], nand3_nmos_width=3 * tech.drc["minwidth_tx"], rows=8) + OPTS.check_lvsdrc = True + self.local_check(a) + + debug.info(1, "Testing sample for hierarchy_decoder") + OPTS.check_lvsdrc = False + a = hierarchical_decoder.hierarchical_decoder( + nand2_nmos_width=2 * tech.drc["minwidth_tx"], nand3_nmos_width=3 * tech.drc["minwidth_tx"], rows=32) + OPTS.check_lvsdrc = True + self.local_check(a) + + debug.info(1, "Testing sample for hierarchy_decoder") + OPTS.check_lvsdrc = False + a = hierarchical_decoder.hierarchical_decoder( + nand2_nmos_width=2 * tech.drc["minwidth_tx"], nand3_nmos_width=3 * tech.drc["minwidth_tx"], rows=128) + OPTS.check_lvsdrc = True + self.local_check(a) + + debug.info(1, "Testing sample for hierarchy_decoder") + OPTS.check_lvsdrc = False + a = hierarchical_decoder.hierarchical_decoder( + nand2_nmos_width=2 * tech.drc["minwidth_tx"], nand3_nmos_width=3 * tech.drc["minwidth_tx"], rows=512) + OPTS.check_lvsdrc = True + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/06_hierarchical_predecode2x4_test.py b/compiler/tests/06_hierarchical_predecode2x4_test.py new file mode 100644 index 00000000..0114728e --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode2x4_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a hierarchical_predecode2x4. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + + +class hierarchical_predecode2x4_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import hierarchical_predecode2x4 as pre + import tech + + debug.info(1, "Testing sample for hierarchy_decoder") + a = pre.hierarchical_predecode2x4(nmos_width=2 * tech.drc["minwidth_tx"], + cellname="test_pre2x4") + OPTS.check_lvsdrc = True + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/06_hierarchical_predecode3x8_test.py b/compiler/tests/06_hierarchical_predecode3x8_test.py new file mode 100644 index 00000000..6458513e --- /dev/null +++ b/compiler/tests/06_hierarchical_predecode3x8_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a hierarchical_predecode3x8. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.OPTS + + +class hierarchical_predecode3x8_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import hierarchical_predecode3x8 as pre + import tech + + debug.info(1, "Testing sample for hierarchy_decoder") + a = pre.hierarchical_predecode3x8(nmos_width=3 * tech.drc["minwidth_tx"], + cellname="test_pre3x8") + OPTS.check_lvsdrc = True + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/07_single_level_column_mux_test.py b/compiler/tests/07_single_level_column_mux_test.py new file mode 100644 index 00000000..c810b865 --- /dev/null +++ b/compiler/tests/07_single_level_column_mux_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a single transistor column_mux. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + + +class single_level_column_mux_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import single_level_column_mux_array + + debug.info(1, "Testing sample for columnmux_array") + a = single_level_column_mux_array.single_level_column_mux_array( + rows=32, columns=32, word_size=16) + OPTS.check_lvsdrc = True + self.local_check(a) + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + OPTS.check_lvsdrc = True + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/08_precharge_array_test.py b/compiler/tests/08_precharge_array_test.py new file mode 100644 index 00000000..66fcb193 --- /dev/null +++ b/compiler/tests/08_precharge_array_test.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a precharge array +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 08_precharge_test") + + +class precharge_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import precharge_array + import tech + + debug.info(2, "Checking 1 column precharge") + OPTS.check_lvsdrc = False + pc = precharge_array.precharge_array( + name="precharge_array", columns=1, ptx_width=tech.drc["minwidth_tx"], beta=2) + OPTS.check_lvsdrc = True + self.local_check(pc) + + debug.info(2, "Checking 3 column precharge") + OPTS.check_lvsdrc = False + pc = precharge_array.precharge_array( + name="precharge_array", columns=3, ptx_width=tech.drc["minwidth_tx"], beta=2) + OPTS.check_lvsdrc = True + self.local_check(pc) + + + def local_check(self, pc): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + pc.sp_write(tempspice) + pc.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(pc.name, tempgds)) + self.assertFalse(calibre.run_lvs(pc.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/09_sense_amp_array_test.py b/compiler/tests/09_sense_amp_array_test.py new file mode 100644 index 00000000..2974712e --- /dev/null +++ b/compiler/tests/09_sense_amp_array_test.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a sense amp array +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 09_sense_amp_test") + + +class sense_amp_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import sense_amp_array + + debug.info(2, "Testing sense_amp_array for word_size=1, words_per_row=1") + OPTS.check_lvsdrc = False + a = sense_amp_array.sense_amp_array(word_size=1, words_per_row=1) + OPTS.check_lvsdrc = True + self.local_check(a) + + debug.info(2, "Testing sense_amp_array for word_size=4, words_per_row=2") + OPTS.check_lvsdrc = False + a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=2) + OPTS.check_lvsdrc = True + self.local_check(a) + + debug.info(2, "Testing sense_amp_array for word_size=3, words_per_row=1") + OPTS.check_lvsdrc = False + a = sense_amp_array.sense_amp_array(word_size=3, words_per_row=1) + OPTS.check_lvsdrc = True + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/10_write_driver_array_test.py b/compiler/tests/10_write_driver_array_test.py new file mode 100644 index 00000000..559107f8 --- /dev/null +++ b/compiler/tests/10_write_driver_array_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a write driver array +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 10_write_driver_test") + + +class write_driver_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import write_driver_array + + debug.info(2, "Testing write_driver_array for columns=16, word_size=4") + OPTS.check_lvsdrc = False + a = write_driver_array.write_driver_array(columns=16, word_size=4) + OPTS.check_lvsdrc = True + self.local_check(a) + + debug.info(2, "Testing write_driver_array for columns=128, word_size=128") + OPTS.check_lvsdrc = False + a = write_driver_array.write_driver_array(columns=16, word_size=16) + OPTS.check_lvsdrc = True + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/11_ms_flop_array_test.py b/compiler/tests/11_ms_flop_array_test.py new file mode 100644 index 00000000..33680a53 --- /dev/null +++ b/compiler/tests/11_ms_flop_array_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a dff_array. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre +import importlib + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 20_sram_test") + + +class dff_array_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + import ms_flop_array + + debug.info(1, "Testing sample for dff_array") + OPTS.check_lvsdrc = False + a = ms_flop_array.ms_flop_array( + name="test1", array_type="address", columns=64, word_size=32) + OPTS.check_lvsdrc = True + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/13_control_logic_test.py b/compiler/tests/13_control_logic_test.py new file mode 100644 index 00000000..37c39ca1 --- /dev/null +++ b/compiler/tests/13_control_logic_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a control_logic +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + + +class control_logic_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import control_logic + import tech + + debug.info(1, "Testing sample for control_logic") + a = control_logic.control_logic(num_rows=128) + OPTS.check_lvsdrc = True + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/14_logic_effort_dc_test.py b/compiler/tests/14_logic_effort_dc_test.py new file mode 100644 index 00000000..48e8b768 --- /dev/null +++ b/compiler/tests/14_logic_effort_dc_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2.7 +""" +Run a test on a delay chain +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 14_delay_chain_test") + + +class logic_effort_dc_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import logic_effort_dc + + debug.info(2, "Testing delay_chain") + a = logic_effort_dc.logic_effort_dc( + "chain", stage_list=[4, 4, 4, 4, 4]) + OPTS.check_lvsdrc = True + self.local_check(a) + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/15_tri_gate_array_test.py b/compiler/tests/15_tri_gate_array_test.py new file mode 100644 index 00000000..ef388696 --- /dev/null +++ b/compiler/tests/15_tri_gate_array_test.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a tri_gate_array. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + + +class tri_gate_array_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import tri_gate_array + + debug.info(1, "Testing sample for tri_gate_array") + a = tri_gate_array.tri_gate_array(columns=16, word_size=16) + OPTS.check_lvsdrc = True + self.local_check(a) + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/16_replica_bitline_test.py b/compiler/tests/16_replica_bitline_test.py new file mode 100644 index 00000000..2b0aaa9a --- /dev/null +++ b/compiler/tests/16_replica_bitline_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2.7 +""" +Run a test on a delay chain +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre +import importlib + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 14_delay_chain_test") + + +class replica_bitline_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import replica_bitline + + debug.info(2, "Testing RBL") + a = replica_bitline.replica_bitline("chain", 13) + OPTS.check_lvsdrc = True + self.local_check(a) + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/19_bank_test.py b/compiler/tests/19_bank_test.py new file mode 100644 index 00000000..aef9068b --- /dev/null +++ b/compiler/tests/19_bank_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on various srams +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 20_sram_test") + + +class bank_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import bank + + # override these from the config file + OPTS.word_size=8 + OPTS.num_words=128 + OPTS.num_banks=1 + + debug.info(1, "Testing sample 8bit, 64word BANK") + a = bank.bank(word_size=OPTS.num_words, num_words=OPTS.num_words, words_per_row=2, num_banks=OPTS.num_banks, name="test_sram1") + OPTS.check_lvsdrc = True + self.local_check(a) + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/20_sram_1bank_test.py b/compiler/tests/20_sram_1bank_test.py new file mode 100644 index 00000000..d1b553ae --- /dev/null +++ b/compiler/tests/20_sram_1bank_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a 1 bank SRAM +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 20_sram_test") + + +class sram_1bank_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import sram + + debug.info(1, "Testing sample 8bit, 64word SRAM, 1 bank") + a = sram.sram(word_size=8, num_words=128, num_banks=1, name="test_sram1") + OPTS.check_lvsdrc = True + self.local_check(a) + + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + #self.assertFalse(calibre.run_pex(a.name, tempgds, tempspice, output=OPTS.openram_temp+"temp_pex.sp")) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/20_sram_2bank_test.py b/compiler/tests/20_sram_2bank_test.py new file mode 100644 index 00000000..b07bf1a5 --- /dev/null +++ b/compiler/tests/20_sram_2bank_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a 2 bank SRAM +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 20_sram_test") + + +class sram_2bank_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import sram + + debug.info(1, "Testing sample 8bit, 128word SRAM, 2 banks") + a = sram.sram(word_size=8, num_words=128, num_banks=2, name="test_sram1") + OPTS.check_lvsdrc = True + self.local_check(a) + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + #self.assertFalse(calibre.run_pex(a.name, tempgds, tempspice, output=OPTS.openram_temp+"temp_pex.sp")) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/20_sram_4bank_test.py b/compiler/tests/20_sram_4bank_test.py new file mode 100644 index 00000000..beacc5e2 --- /dev/null +++ b/compiler/tests/20_sram_4bank_test.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on a 4 bank SRAM +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 20_sram_test") + + +class sram_4bank_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import sram + + debug.info(1, "Testing sample 8bit, 128word SRAM, 4 banks") + a = sram.sram(word_size=8, num_words=128, num_banks=4, name="test_sram1") + OPTS.check_lvsdrc = True + self.local_check(a) + + def local_check(self, a): + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + a.sp_write(tempspice) + a.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(a.name, tempgds)) + self.assertFalse(calibre.run_lvs(a.name, tempgds, tempspice)) + #self.assertFalse(calibre.run_pex(a.name, tempgds, tempspice, output=OPTS.openram_temp+"temp_pex.sp")) + + os.remove(tempspice) + os.remove(tempgds) + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/21_timing_delay_test.py b/compiler/tests/21_timing_delay_test.py new file mode 100644 index 00000000..8a129203 --- /dev/null +++ b/compiler/tests/21_timing_delay_test.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on various srams +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 21_timing_sram_test") + + +class timing_sram_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + OPTS.use_pex = False + + import sram + + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = sram.sram(word_size=OPTS.config.word_size, + num_words=OPTS.config.num_words, + num_banks=OPTS.config.num_banks, + name="test_sram1") + + OPTS.check_lvsdrc = True + + import delay + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + probe_address = "1" * s.addr_size + probe_data = s.word_size - 1 + debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) + + d = delay.delay(s,tempspice) + data = d.analyze(probe_address, probe_data) + + if OPTS.tech_name == "freepdk45": + self.assertTrue(isclose(data['delay1'],0.013649)) + self.assertTrue(isclose(data['delay0'],0.22893)) + self.assertTrue(isclose(data['min_period1'],0.078582763671875)) + self.assertTrue(isclose(data['min_period0'],0.25543212890625)) + elif OPTS.tech_name == "scn3me_subm": + self.assertTrue(isclose(data['delay1'],1.5335)) + self.assertTrue(isclose(data['delay0'],2.2635000000000005)) + self.assertTrue(isclose(data['min_period1'],1.53564453125)) + self.assertTrue(isclose(data['min_period0'],2.998046875)) + else: + self.assertTrue(False) # other techs fail + + os.remove(tempspice) + +def isclose(value1,value2): + """ This is used to compare relative values for convergence. """ + return (abs(value1 - value2) / max(value1,value2) <= 1e-2) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/21_timing_hold_test.py b/compiler/tests/21_timing_hold_test.py new file mode 100644 index 00000000..78d4fe5b --- /dev/null +++ b/compiler/tests/21_timing_hold_test.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on various srams +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 21_timing_sram_test") + + +class timing_setup_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + OPTS.use_pex = False + + import sram + import setup_hold + + sh = setup_hold.setup_hold() + [one_setup_time, zero_setup_time] = sh.hold_time() + + OPTS.check_lvsdrc = True + + if OPTS.tech_name == "freepdk45": + self.assertTrue(isclose(one_setup_time,-0.0048828125)) + self.assertTrue(isclose(zero_setup_time,-0.010986328125)) + elif OPTS.tech_name == "scn3me_subm": + self.assertTrue(isclose(one_setup_time,0.04638671875)) + self.assertTrue(isclose(zero_setup_time,-0.0830078125)) + else: + self.assertTrue(False) # other techs fail + +def isclose(value1,value2): + """ This is used to compare relative values for convergence. """ + return (abs(value1 - value2) / max(value1,value2) <= 1e-2) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/21_timing_setup_test.py b/compiler/tests/21_timing_setup_test.py new file mode 100644 index 00000000..4011d6ad --- /dev/null +++ b/compiler/tests/21_timing_setup_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on various srams +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 21_timing_sram_test") + + +class timing_setup_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + OPTS.use_pex = False + + import sram + import setup_hold + + sh = setup_hold.setup_hold() + [one_setup_time, zero_setup_time] = sh.setup_time() + + OPTS.check_lvsdrc = True + if OPTS.tech_name == "freepdk45": + self.assertTrue(isclose(one_setup_time,0.0146484375)) + self.assertTrue(isclose(zero_setup_time,0.008544921875)) + elif OPTS.tech_name == "scn3me_subm": + self.assertTrue(isclose(one_setup_time,0.0927734375)) + self.assertTrue(isclose(zero_setup_time,-0.0244140625)) + else: + self.assertTrue(False) # other techs fail + +def isclose(value1,value2): + """ This is used to compare relative values for convergence. """ + return (abs(value1 - value2) / max(value1,value2) <= 1e-2) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_pex_func_test_with_pinv.py b/compiler/tests/22_pex_func_test_with_pinv.py new file mode 100644 index 00000000..58c40e18 --- /dev/null +++ b/compiler/tests/22_pex_func_test_with_pinv.py @@ -0,0 +1,309 @@ +#!/usr/bin/env python2.7 +""" +Run a regression test on an extracted SRAM to ensure functionality. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + + +@unittest.skip("SKIPPING 22_sram_func_test") +class sram_func_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + self.func_test(bank_num=1) + self.func_test(bank_num=2) + self.func_test(bank_num=4) + + def func_test(self, bank_num): + + import sram + import tech + + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + OPTS.check_lvsdrc = False + OPTS.use_pex = True + s = sram.sram(word_size=OPTS.config.word_size, + num_words=OPTS.config.num_words, + num_banks=OPTS.config.num_banks, + name="test_sram1") + OPTS.check_lvsdrc = True + OPTS.use_pex = False + + tempspice = OPTS.openram_temp + "temp.sp" + tempgds = OPTS.openram_temp + "temp.gds" + + s.sp_write(tempspice) + s.gds_write(tempgds) + + self.assertFalse(calibre.run_drc(s.name, tempgds)) + self.assertFalse(calibre.run_lvs(s.name, tempgds, tempspice)) + self.assertFalse(calibre.run_pex(s.name, tempgds, + tempspice, output=OPTS.openram_temp + "temp_pex.sp")) + + import sp_file + stimulus_file = OPTS.openram_temp + "stimulus.sp" + a_stimulus = sp_file.sp_file(stimulus_file) + self.write_stimulus(a_stimulus) + + simulator_file = OPTS.openram_temp + "simulator.sp" + a_simulator = sp_file.sp_file(simulator_file) + self.write_simulator(a_simulator) + + result_file = OPTS.openram_temp + "result" + + import os + + if OPTS.spice_version == "hspice": + cmd = "hspice -mt 36 -i {0} > {1} ".format( + simulator_file, result_file) + else: + cmd = "ngspice -b -i {0} > {1} ".format( + simulator_file, result_file) + os.system(cmd) + + import re + sp_result = open(result_file, "r") + contents = sp_result.read() + key = "vr1" + val = re.search( + r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents) + val = val.group(3) + value1 = float(self.convert_voltage_unit(val)) + + key = "vr2" + val = re.search( + r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents) + val = val.group(3) + value2 = float(self.convert_voltage_unit(val)) + + self.assertTrue(round(value1) > 0.5 * tech.spice["supply_voltage"]) + self.assertTrue(round(value2) < 0.5 * tech.spice["supply_voltage"]) + + OPTS.check_lvsdrc = True + + def convert_voltage_unit(self, string): + newstring = "" + for letter in string: + if letter == "m": + letter = "10e-3" + elif letter == "u": + letter = "10e-6" + else: + letter = letter + newstring = str(newstring) + str(letter) + return newstring + + def convert_time_unit(self, string): + newstring = "" + for letter in string: + if letter == "f": + letter = "10e-15" + elif letter == "p": + letter = "10e-12" + elif letter == "n": + letter = "10e-9" + elif letter == "u": + letter = "10e-6" + elif letter == "m": + letter = "10e-3" + else: + letter = letter + newstring = str(newstring) + str(letter) + return newstring + + def write_simulator(self, sim_file): + sim_file.write("\n") + import tech + time_step = tech.spice["clock_period"] + for model in tech.spice["fet_models"]: + sim_file.write(".inc " + str(model) + "\n") + sim_file.write(".inc stimulus.sp\n") + sim_file.write(".inc temp_pex.sp\n") + sim_file.write(".options post runlvl=6\n") + sim_file.write("\n") + + sim_file.write( + "Xsource DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss source\n") + sim_file.write( + "Xsram DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd vss test_sram1\n") + sim_file.write("\n") + + sim_file.write(".MEASURE TRAN vr1 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format( + 4.5 * tech.spice["clock_period"], 5 * tech.spice["clock_period"])) + sim_file.write(".MEASURE TRAN vr2 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format( + 9.5 * tech.spice["clock_period"], 10 * tech.spice["clock_period"])) + sim_file.write("\n") + + if OPTS.spice_version == "hspice": + sim_file.write(".probe v(x*.*)\n") + sim_file.write(".tran 0.1ns {0}ns\n".format( + 10 * tech.spice["clock_period"])) + sim_file.write(".end\n") + else: + sim_file.write( + ".meas tran DELAY1.0 TRIG v(clk) VAL=0.5 RISE=6 TARG v(DATA[0]) VAL=0.5 TD=0.5n RISE=1\n") + sim_file.write(".tran 0.1ns {0}ns\n".format( + 10 * tech.spice["clock_period"])) + sim_file.write(".control\n") + sim_file.write("run\n") + #sim_file.write("plot CSb WEb OEb \n") + #sim_file.write("plot clk DATA0 \n") + sim_file.write("quit\n") + sim_file.write(".endc\n") + sim_file.write(".end\n") + sim_file.file.close() + + def write_stimulus(self, sti_file): + import tech + import sp_file + sti_file.write( + ".subckt source DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss\n") + + time_step = tech.spice["clock_period"] + + clk = sp_file.PWL(name="clk", port=["clk", "0"]) + for i in range(0, 11): + clk.write_pulse(i * time_step, time_step, "UP") + clk.write_to_sp(sti_file) + + WEB_inv = sp_file.PWL(name="WEb_inv", port=["WEb_inv", "0"]) + WEB = sp_file.PWL(name="WEB", port=["WEb", "0"]) + OEb = sp_file.PWL(name="OEb", port=["OEb", "0"]) + CSb = sp_file.PWL(name="CSb", port=["CSb", "0"]) + + # write + CSb.write_pulse(0.75 * time_step, time_step, "DN") + WEB.write_pulse(0.75 * time_step, time_step, "DN") + WEB_inv.write_pulse(0.75 * time_step, time_step, "UP") + CSb.write_pulse(1.75 * time_step, time_step, "DN") + WEB.write_pulse(1.75 * time_step, time_step, "DN") + WEB_inv.write_pulse(1.75 * time_step, time_step, "UP") + + # read + OEb.write_pulse(3.75 * time_step, time_step, "DN") + CSb.write_pulse(3.75 * time_step, time_step, "DN") + + # write + CSb.write_pulse(5.75 * time_step, time_step, "DN") + WEB.write_pulse(5.75 * time_step, time_step, "DN") + WEB_inv.write_pulse(5.75 * time_step, time_step, "UP") + CSb.write_pulse(6.75 * time_step, time_step, "DN") + WEB.write_pulse(6.75 * time_step, time_step, "DN") + WEB_inv.write_pulse(6.75 * time_step, time_step, "UP") + + # read + OEb.write_pulse(8.75 * time_step, time_step, "DN") + CSb.write_pulse(8.75 * time_step, time_step, "DN") + + CSb.write_to_sp(sti_file) + WEB.write_to_sp(sti_file) + WEB_inv.write_to_sp(sti_file) + OEb.write_to_sp(sti_file) + + sti_file.write("VA[0] A[0] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[ + "clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"])) + sti_file.write("VA[1] A[1] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[ + "clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"])) + sti_file.write("VA[2] A[2] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[ + "clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"])) + sti_file.write("VA[3] A[3] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[ + "clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"])) + + sti_file.write( + "xA[0]_buff A[0] ADDR[0]_inv ADDR[0] vdd vss test_buf\n") + sti_file.write( + "xA[1]_buff A[1] ADDR[1]_inv ADDR[1] vdd vss test_buf\n") + sti_file.write( + "xA[2]_buff A[2] ADDR[2]_inv ADDR[2] vdd vss test_buf\n") + sti_file.write( + "xA[3]_buff A[3] ADDR[3]_inv ADDR[3] vdd vss test_buf\n") + + VD_0 = sp_file.PWL(name="VD[0]", port=["D[0]", "0"]) + VD_0.write_pulse(0, 5 * time_step, "S1") + VD_0.write_pulse(5 * time_step, 5 * time_step, "S0") + VD_0.write_to_sp(sti_file) + + sti_file.write( + "xD[0]_buff D[0] DATA[0]_inv DATA[0]s vdd vss test_buf\n") + sti_file.write( + "xD[0]_gate DATA[0]s WEb WEb_inv DATA[0] vdd vss tran_gate\n") + sti_file.write("mp[0]_gate_vdd vdd write_v DATA[0] vdd " + str(tech.spice["pmos"]) + + " w=" + str(2 * tech.parameter["min_tx_size"]) + "u" + + " l=" + str(tech.drc["minlength_channel"]) + "u" + + "\n") + sti_file.write("mn[0]_gate_vss vss write_g DATA[0] vss " + str(tech.spice["nmos"]) + + " w=" + str(tech.parameter["min_tx_size"]) + "u" + + " l=" + str(tech.drc["minlength_channel"]) + "u" + + "\n") + + Vwrite_v = sp_file.PWL(name="write_v", port=["write_vs", "0"]) + Vwrite_v.write_pulse(0, 0.5 * time_step, "S1") + Vwrite_v.write_pulse(7.5 * time_step, time_step, "DN") + Vwrite_v.write_to_sp(sti_file) + sti_file.write( + "xwrite_v write_vs write_v_inv write_v vdd vss test_buf\n") + + Vwrite_g = sp_file.PWL(name="write_g", port=["write_gs", "0"]) + Vwrite_g.write_pulse(0, 0.5 * time_step, "S0") + Vwrite_g.write_pulse(3 * time_step, time_step, "UP") + Vwrite_g.write_to_sp(sti_file) + sti_file.write( + "xwrite_g write_gs write_g_inv write_g vdd vss test_buf\n") + + sti_file.write("Vdd vdd 0 DC " + + str(tech.spice["supply_voltage"]) + "\n") + sti_file.write("Vvss vss 0 DC 0\n") + sti_file.write(".ENDS source\n") + sti_file.write("\n") + + sti_file.write(".SUBCKT tran_gate in gate gate_inv out vdd vss\n") + sti_file.write("mp0 in gate out vdd " + str(tech.spice["pmos"]) + + " w=" + str(2 * tech.parameter["min_tx_size"]) + "u" + + " l=" + str(tech.drc["minlength_channel"]) + "u" + + "\n") + sti_file.write("mn0 in gate_inv out vss " + str(tech.spice["nmos"]) + + " w=" + str(tech.parameter["min_tx_size"]) + "u" + + " l=" + str(tech.drc["minlength_channel"]) + "u" + + "\n") + sti_file.write(".ENDS tran_gate\n") + sti_file.write("\n") + + sti_file.write(".SUBCKT test_buf in out_inv out_buf vdd vss\n") + sti_file.write("mpinv1 out_inv in vdd vdd " + str(tech.spice["pmos"]) + + " w=" + str(2 * tech.parameter["min_tx_size"]) + "u" + + " l=" + str(tech.drc["minlength_channel"]) + "u" + + "\n") + sti_file.write("mninv1 out_inv in vss vss " + str(tech.spice["nmos"]) + + " w=" + str(tech.parameter["min_tx_size"]) + "u" + + " l=" + str(tech.drc["minlength_channel"]) + "u" + + "\n") + sti_file.write("mpinv2 out_buf out_inv vdd vdd " + str(tech.spice["pmos"]) + + " w=" + str(2 * tech.parameter["min_tx_size"]) + "u" + + " l=" + str(tech.drc["minlength_channel"]) + "u" + + "\n") + sti_file.write("mninv2 out_buf out_inv vss vss " + str(tech.spice["nmos"]) + + " w=" + str(tech.parameter["min_tx_size"]) + "u" + + " l=" + str(tech.drc["minlength_channel"]) + "u" + + "\n") + sti_file.write(".ENDS test_buf\n") + sti_file.write("\n") + + sti_file.file.close() + + +# instantiate a copy of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/22_sram_func_test.py b/compiler/tests/22_sram_func_test.py new file mode 100644 index 00000000..5c7bc6c3 --- /dev/null +++ b/compiler/tests/22_sram_func_test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on various srams +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 21_timing_sram_test") + + +class sram_func_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + OPTS.use_pex = False + + import sram + + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = sram.sram(word_size=OPTS.config.word_size, + num_words=OPTS.config.num_words, + num_banks=OPTS.config.num_banks, + name="test_sram1") + + OPTS.check_lvsdrc = True + + import delay + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + probe_address = "1" * s.addr_size + probe_data = s.word_size - 1 + debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) + + d = delay.delay(s,tempspice) + d.set_probe(probe_address,probe_data) + + # This will exit if it doesn't find a feasible period + feasible_period = d.find_feasible_period(2.0) + + os.remove(tempspice) + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/23_lib_sram_test.py b/compiler/tests/23_lib_sram_test.py new file mode 100644 index 00000000..3c2d226c --- /dev/null +++ b/compiler/tests/23_lib_sram_test.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python2.7 +""" +Check the .lib file for an SRAM +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + + +class lib_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + OPTS.use_pex = False + + import sram + import lib + import filecmp + + debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank") + s = sram.sram(word_size=2, + num_words=OPTS.config.num_words, + num_banks=OPTS.config.num_banks, + name="sram_2_16_1_{0}".format(OPTS.tech_name)) + OPTS.check_lvsdrc = True + + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + curpath=os.path.dirname(os.path.realpath(__file__)) + "/" + filename = s.name + ".lib" + libname = curpath + filename + lib.lib(libname,s,tempspice) + + # let's diff the result with a golden model + golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename) + self.assertEqual(filecmp.cmp(libname,golden),True) + + os.system("rm {0}".format(libname)) + + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() + + + + + + diff --git a/compiler/tests/24_lef_sram_test.py b/compiler/tests/24_lef_sram_test.py new file mode 100644 index 00000000..2a349502 --- /dev/null +++ b/compiler/tests/24_lef_sram_test.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python2.7 +""" +Check the LEF file for an SRMA +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + + +class lef_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import sram + import lef + import filecmp + + debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank") + s = sram.sram(word_size=2, + num_words=OPTS.config.num_words, + num_banks=OPTS.config.num_banks, + name="sram_2_16_1_{0}".format(OPTS.tech_name)) + + OPTS.check_lvsdrc = True + + curpath=os.path.dirname(os.path.realpath(__file__)) + "/" + gdsfile = s.name + ".gds" + leffile = s.name + ".lef" + gdsname = curpath + gdsfile + lefname = curpath + leffile + s.gds_write(gdsname) + lef.lef(gdsname,lefname,s) + + # let's diff the result with a golden model + golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),leffile) + self.assertEqual(filecmp.cmp(lefname,golden),True) + + os.system("rm {0}".format(gdsname)) + os.system("rm {0}".format(lefname)) + + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/25_verilog_sram_test.py b/compiler/tests/25_verilog_sram_test.py new file mode 100644 index 00000000..dc628081 --- /dev/null +++ b/compiler/tests/25_verilog_sram_test.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python2.7 +""" +Check the .v file for an SRAM +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + + +class verilog_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + + import sram + import verilog + import filecmp + + debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank") + s = sram.sram(word_size=2, + num_words=OPTS.config.num_words, + num_banks=OPTS.config.num_banks, + name="sram_2_16_1_{0}".format(OPTS.tech_name)) + + OPTS.check_lvsdrc = True + + curpath=os.path.dirname(os.path.realpath(__file__)) + "/" + vfile = s.name + ".v" + vname = curpath + vfile + verilog.verilog(vname,s) + + + # let's diff the result with a golden model + golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),vfile) + self.assertEqual(filecmp.cmp(vname,golden),True) + + os.system("rm {0}".format(vname)) + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py new file mode 100644 index 00000000..ce0a2a05 --- /dev/null +++ b/compiler/tests/30_openram_test.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python2.7 +""" +This tests the top-level executable. It checks that it generates the +appropriate files: .lef, .lib, .sp, .gds, .v. It DOES NOT, however, +check that these files are right. +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import os +import re +import shutil + +OPTS = globals.get_opts() + +class openram_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + + debug.info(1, "Testing top-level openram.py with 2-bit, 16 word SRAM.") + out_file = "testsram" + # get the directory under the test modules + out_path=os.path.dirname(os.path.realpath(__file__)) + # make a temp directory for output + out_path += "/testsram" + + # make sure we start without the files existing + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path),False) + + try: + os.makedirs(out_path, 0750) + except OSError as e: + if e.errno == 17: # errno.EEXIST + os.chmod(out_path, 0750) + + # specify the same verbosity for the system call + verbosity = "" + for i in range(OPTS.debug_level): + verbosity += " -v" + + + OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME")) + + cmd = "python2.7 {0}/openram.py -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(OPENRAM_HOME, + out_file, + out_path, + verbosity, + OPTS.tech_name, + out_path) + debug.info(1, cmd) + os.system(cmd) + + # assert an error until we actually check a resul + for extension in ["gds", "v", "lef", "lib", "sp"]: + filename = "{0}/{1}.{2}".format(out_path,out_file,extension) + debug.info(1,"Checking for file: " + filename) + self.assertEqual(os.path.exists(filename),True) + + # grep any errors from the output + output = open("{0}/output.log".format(out_path),"r").read() + self.assertEqual(len(re.findall('ERROR',output)),0) + self.assertEqual(len(re.findall('WARNING',output)),0) + + # now clean up the directory + if os.path.exists(out_path): + shutil.rmtree(out_path, ignore_errors=True) + self.assertEqual(os.path.exists(out_path),False) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/README b/compiler/tests/README new file mode 100644 index 00000000..be7f42aa --- /dev/null +++ b/compiler/tests/README @@ -0,0 +1,3 @@ +Note that the tests turn off DRC/LVS when they perform their own check +for performance improvement. However, it must be turned back on before +the test runs an assert. \ No newline at end of file diff --git a/compiler/tests/config_20_freepdk45.py b/compiler/tests/config_20_freepdk45.py new file mode 100644 index 00000000..1217a677 --- /dev/null +++ b/compiler/tests/config_20_freepdk45.py @@ -0,0 +1,23 @@ +word_size = 1 +num_words = 16 +num_banks = 1 + +tech_name = "freepdk45" + +decoder = "hierarchical_decoder" +ms_flop = "ms_flop" +ms_flop_array = "ms_flop_array" +control_logic = "control_logic" +bitcell_array = "bitcell_array" +sense_amp = "sense_amp" +sense_amp_array = "sense_amp_array" +precharge_array = "precharge_array" +column_mux_array = "single_level_column_mux_array" +write_driver = "write_driver" +write_driver_array = "write_driver_array" +tri_gate = "tri_gate" +tri_gate_array = "tri_gate_array" +wordline_driver = "wordline_driver" +replica_bitcell = "replica_bitcell" +bitcell = "bitcell" +delay_chain = "logic_effort_dc" diff --git a/compiler/tests/config_20_scn3me_subm.py b/compiler/tests/config_20_scn3me_subm.py new file mode 100644 index 00000000..d7618847 --- /dev/null +++ b/compiler/tests/config_20_scn3me_subm.py @@ -0,0 +1,23 @@ +word_size = 1 +num_words = 16 +num_banks = 1 + +tech_name = "scn3me_subm" + +decoder = "hierarchical_decoder" +ms_flop = "ms_flop" +ms_flop_array = "ms_flop_array" +control_logic = "control_logic" +bitcell_array = "bitcell_array" +sense_amp = "sense_amp" +sense_amp_array = "sense_amp_array" +precharge_array = "precharge_array" +column_mux_array = "single_level_column_mux_array" +write_driver = "write_driver" +write_driver_array = "write_driver_array" +tri_gate = "tri_gate" +tri_gate_array = "tri_gate_array" +wordline_driver = "wordline_driver" +replica_bitcell = "replica_bitcell" +bitcell = "bitcell" +delay_chain = "logic_effort_dc" diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.lef b/compiler/tests/golden/sram_2_16_1_freepdk45.lef new file mode 100644 index 00000000..60fb341c --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.lef @@ -0,0 +1,11978 @@ +MACRO sram_2_16_1_freepdk45 + CLASS RING ; + ORIGIN 5.765 0.0 ; + FOREIGN sram 0.0 0.0 ; + SIZE 19.165 BY 41.725 ; + SYMMETRY X Y R90 ; + PIN vdd + DIRECTION INOUT ; + USE POWER ; + SHAPE ABUTMENT ; + PORT + Layer metal1 ; + RECT 0.0 0.0 0.7 41.725 ; + RECT 12.735 0.0 13.435 41.725 ; + END + END vdd + PIN gnd + DIRECTION INOUT ; + USE GROUND ; + SHAPE ABUTMENT ; + PORT + Layer metal2 ; + RECT 8.89 0.0 9.59 41.725 ; + END + END gnd + PIN DATA[0] + DIRECTION INOUT ; + PORT + Layer metal3 ; + RECT 11.4475 0.0 11.5175 3.22 ; + RECT 11.4475 0.0 11.5175 0.135 ; + END + END DATA[0] + PIN DATA[1] + DIRECTION INOUT ; + PORT + Layer metal3 ; + RECT 12.1525 0.0 12.2225 3.22 ; + RECT 12.1525 0.0 12.2225 0.135 ; + END + END DATA[1] + PIN ADDR[0] + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT 0.0 7.4525 0.9825 7.5225 ; + END + END ADDR[0] + PIN ADDR[1] + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT 0.0 6.7475 0.9825 6.8175 ; + END + END ADDR[1] + PIN ADDR[2] + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT 0.0 6.0425 0.9825 6.1125 ; + END + END ADDR[2] + PIN ADDR[3] + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT 0.0 5.3375 0.9825 5.4075 ; + END + END ADDR[3] + PIN CSb + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT -5.4125 8.275 -5.3425 8.415 ; + END + END CSb + PIN OEb + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT -4.0025 8.275 -3.9325 8.415 ; + END + END OEb + PIN WEb + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT -4.7075 8.275 -4.6375 8.415 ; + END + END WEb + PIN clk + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT -2.0525 8.275 9.73 8.345 ; + RECT -2.085 8.275 -2.015 8.41 ; + END + END clk + OBS + Layer metal1 ; + RECT -0.545 26.2325 0.0 26.2975 ; + RECT 12.735 0.0 13.435 41.725 ; + RECT 0.0 0.0 0.7 41.725 ; + RECT 5.53 19.49 5.595 19.87 ; + RECT 5.53 20.61 5.595 20.99 ; + RECT 5.53 22.18 5.595 22.56 ; + RECT 5.53 23.3 5.595 23.68 ; + RECT 5.53 24.87 5.595 25.25 ; + RECT 5.53 25.99 5.595 26.37 ; + RECT 5.53 27.56 5.595 27.94 ; + RECT 5.53 28.68 5.595 29.06 ; + RECT 5.53 30.25 5.595 30.63 ; + RECT 5.53 31.37 5.595 31.75 ; + RECT 5.53 32.94 5.595 33.32 ; + RECT 5.53 34.06 5.595 34.44 ; + RECT 5.53 35.63 5.595 36.01 ; + RECT 5.53 36.75 5.595 37.13 ; + RECT 5.53 38.32 5.595 38.7 ; + RECT 5.53 39.44 5.595 39.82 ; + RECT 7.855 20.2075 11.13 20.2725 ; + RECT 7.855 22.8975 11.13 22.9625 ; + RECT 7.855 25.5875 11.13 25.6525 ; + RECT 7.855 28.2775 11.13 28.3425 ; + RECT 7.855 30.9675 11.13 31.0325 ; + RECT 7.855 33.6575 11.13 33.7225 ; + RECT 7.855 36.3475 11.13 36.4125 ; + RECT 7.855 39.0375 11.13 39.1025 ; + RECT 7.245 8.73 8.61 8.795 ; + RECT 7.245 10.165 8.4 10.23 ; + RECT 7.245 14.11 8.19 14.175 ; + RECT 7.245 15.545 7.98 15.61 ; + RECT 10.36 3.6 11.4825 3.665 ; + RECT 9.94 1.415 11.5 1.48 ; + RECT 10.15 2.9625 11.5 3.0275 ; + RECT 10.36 41.1 11.13 41.165 ; + RECT 10.57 10.1025 11.13 10.1675 ; + RECT 10.78 14.1275 11.13 14.1925 ; + RECT 11.13 20.2075 12.735 20.2725 ; + RECT 11.13 22.8975 12.735 22.9625 ; + RECT 11.13 25.5875 12.735 25.6525 ; + RECT 11.13 28.2775 12.735 28.3425 ; + RECT 11.13 30.9675 12.735 31.0325 ; + RECT 11.13 33.6575 12.735 33.7225 ; + RECT 11.13 36.3475 12.735 36.4125 ; + RECT 11.13 39.0375 12.735 39.1025 ; + RECT 11.13 41.66 12.735 41.725 ; + RECT 11.13 18.7 12.735 18.765 ; + RECT 11.13 10.2325 12.735 10.2975 ; + RECT 11.13 9.565 12.735 9.63 ; + RECT 11.5 1.545 12.735 1.61 ; + RECT 12.17 1.545 12.735 1.61 ; + RECT 0.0 20.2075 4.22 20.2725 ; + RECT 0.0 22.8975 4.22 22.9625 ; + RECT 0.0 25.5875 4.22 25.6525 ; + RECT 0.0 28.2775 4.22 28.3425 ; + RECT 0.0 30.9675 4.22 31.0325 ; + RECT 0.0 33.6575 4.22 33.7225 ; + RECT 0.0 36.3475 4.22 36.4125 ; + RECT 0.0 39.0375 4.22 39.1025 ; + RECT 0.0 12.1375 4.22 12.2025 ; + RECT 0.0 17.5175 4.22 17.5825 ; + RECT 8.89 40.545 12.54 40.61 ; + RECT 9.385 0.355 12.54 0.42 ; + RECT 4.22 13.4825 8.89 13.5475 ; + RECT 4.22 18.8625 8.89 18.9275 ; + RECT 7.35 7.8075 8.89 7.8725 ; + RECT 7.35 6.3975 8.89 6.4625 ; + RECT 7.35 6.3975 8.89 6.4625 ; + RECT 7.35 4.9875 8.89 5.0525 ; + RECT 11.8 20.0075 11.865 20.1425 ; + RECT 11.615 20.0075 11.68 20.1425 ; + RECT 11.1 20.0075 11.165 20.1425 ; + RECT 11.285 20.0075 11.35 20.1425 ; + RECT 11.615 19.5425 11.68 19.6775 ; + RECT 11.8 19.5425 11.865 19.6775 ; + RECT 11.285 19.5425 11.35 19.6775 ; + RECT 11.1 19.5425 11.165 19.6775 ; + RECT 11.72 19.1525 11.785 19.2875 ; + RECT 11.535 19.1525 11.6 19.2875 ; + RECT 11.365 19.1525 11.43 19.2875 ; + RECT 11.18 19.1525 11.245 19.2875 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.41 20.2075 11.545 20.2725 ; + RECT 11.0625 18.8625 11.1975 18.9275 ; + RECT 11.7675 18.8625 11.9025 18.9275 ; + RECT 11.3975 19.0025 11.5325 19.0675 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.3025 19.8925 11.4375 19.9575 ; + RECT 11.3025 19.8925 11.4375 19.9575 ; + RECT 11.5275 19.7425 11.6625 19.8075 ; + RECT 11.5275 19.7425 11.6625 19.8075 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.3525 19.1525 11.4175 19.2875 ; + RECT 11.0975 19.645 11.1625 19.78 ; + RECT 11.0975 19.645 11.1625 19.78 ; + RECT 11.0975 19.645 11.1625 19.78 ; + RECT 11.0975 19.645 11.1625 19.78 ; + RECT 11.0975 19.645 11.1625 19.78 ; + RECT 11.0975 19.645 11.1625 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.5475 19.1525 11.6125 19.2875 ; + RECT 11.415 18.8625 11.55 18.9275 ; + RECT 11.415 18.8625 11.55 18.9275 ; + RECT 11.7675 18.8625 11.9025 18.9275 ; + RECT 11.41 20.2075 11.545 20.2725 ; + RECT 11.7675 18.8625 11.9025 18.9275 ; + RECT 11.7675 18.8625 11.9025 18.9275 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.0625 18.8625 11.1975 18.9275 ; + RECT 11.4225 20.2075 11.5225 20.27 ; + RECT 11.4225 20.21 11.5225 20.2725 ; + RECT 11.75 19.005 11.8025 19.0675 ; + RECT 11.4225 20.2075 11.5225 20.27 ; + RECT 11.8 20.0075 11.87 20.2075 ; + RECT 11.8 19.5425 11.87 19.6775 ; + RECT 11.8 19.5425 11.87 19.6775 ; + RECT 11.04 18.8625 11.925 18.9275 ; + RECT 11.615 19.3775 11.79 19.4425 ; + RECT 11.095 19.5425 11.165 19.6775 ; + RECT 11.285 19.3775 11.35 20.1175 ; + RECT 11.4225 20.21 11.5225 20.2725 ; + RECT 11.045 19.005 11.0975 19.0675 ; + RECT 11.8 19.5425 11.87 19.6775 ; + RECT 11.04 20.2075 11.925 20.2725 ; + RECT 11.615 19.3775 11.68 20.0075 ; + RECT 11.8 19.5425 11.87 19.6775 ; + RECT 11.8 19.5425 11.87 19.6775 ; + RECT 11.18 19.1525 11.25 19.4425 ; + RECT 11.095 19.5425 11.165 19.6775 ; + RECT 11.04 19.0025 11.925 19.0675 ; + RECT 11.8 19.5425 11.87 19.6775 ; + RECT 11.8 20.0075 11.87 20.2075 ; + RECT 11.095 20.0075 11.165 20.2075 ; + RECT 11.72 19.1525 11.79 19.4425 ; + RECT 11.18 19.3775 11.35 19.4425 ; + RECT 11.8 20.3375 11.865 20.4725 ; + RECT 11.615 20.3375 11.68 20.4725 ; + RECT 11.1 20.3375 11.165 20.4725 ; + RECT 11.285 20.3375 11.35 20.4725 ; + RECT 11.615 20.8025 11.68 20.9375 ; + RECT 11.8 20.8025 11.865 20.9375 ; + RECT 11.285 20.8025 11.35 20.9375 ; + RECT 11.1 20.8025 11.165 20.9375 ; + RECT 11.72 21.1925 11.785 21.3275 ; + RECT 11.535 21.1925 11.6 21.3275 ; + RECT 11.365 21.1925 11.43 21.3275 ; + RECT 11.18 21.1925 11.245 21.3275 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.41 20.2075 11.545 20.2725 ; + RECT 11.0625 21.5525 11.1975 21.6175 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 11.3975 21.4125 11.5325 21.4775 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.3025 20.5225 11.4375 20.5875 ; + RECT 11.3025 20.5225 11.4375 20.5875 ; + RECT 11.5275 20.6725 11.6625 20.7375 ; + RECT 11.5275 20.6725 11.6625 20.7375 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.3525 21.1925 11.4175 21.3275 ; + RECT 11.0975 20.7 11.1625 20.835 ; + RECT 11.0975 20.7 11.1625 20.835 ; + RECT 11.0975 20.7 11.1625 20.835 ; + RECT 11.0975 20.7 11.1625 20.835 ; + RECT 11.0975 20.7 11.1625 20.835 ; + RECT 11.0975 20.7 11.1625 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.5475 21.1925 11.6125 21.3275 ; + RECT 11.415 21.5525 11.55 21.6175 ; + RECT 11.415 21.5525 11.55 21.6175 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 11.41 20.2075 11.545 20.2725 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.0625 21.5525 11.1975 21.6175 ; + RECT 11.4225 20.21 11.5225 20.2725 ; + RECT 11.4225 20.2075 11.5225 20.27 ; + RECT 11.75 21.4125 11.8025 21.475 ; + RECT 11.4225 20.21 11.5225 20.2725 ; + RECT 11.8 20.2725 11.87 20.4725 ; + RECT 11.8 20.8025 11.87 20.9375 ; + RECT 11.8 20.8025 11.87 20.9375 ; + RECT 11.04 21.5525 11.925 21.6175 ; + RECT 11.615 21.0375 11.79 21.1025 ; + RECT 11.095 20.8025 11.165 20.9375 ; + RECT 11.285 20.3625 11.35 21.1025 ; + RECT 11.4225 20.2075 11.5225 20.27 ; + RECT 11.045 21.4125 11.0975 21.475 ; + RECT 11.8 20.8025 11.87 20.9375 ; + RECT 11.04 20.2075 11.925 20.2725 ; + RECT 11.615 20.4725 11.68 21.1025 ; + RECT 11.8 20.8025 11.87 20.9375 ; + RECT 11.8 20.8025 11.87 20.9375 ; + RECT 11.18 21.0375 11.25 21.3275 ; + RECT 11.095 20.8025 11.165 20.9375 ; + RECT 11.04 21.4125 11.925 21.4775 ; + RECT 11.8 20.8025 11.87 20.9375 ; + RECT 11.8 20.2725 11.87 20.4725 ; + RECT 11.095 20.2725 11.165 20.4725 ; + RECT 11.72 21.0375 11.79 21.3275 ; + RECT 11.18 21.0375 11.35 21.1025 ; + RECT 11.8 22.6975 11.865 22.8325 ; + RECT 11.615 22.6975 11.68 22.8325 ; + RECT 11.1 22.6975 11.165 22.8325 ; + RECT 11.285 22.6975 11.35 22.8325 ; + RECT 11.615 22.2325 11.68 22.3675 ; + RECT 11.8 22.2325 11.865 22.3675 ; + RECT 11.285 22.2325 11.35 22.3675 ; + RECT 11.1 22.2325 11.165 22.3675 ; + RECT 11.72 21.8425 11.785 21.9775 ; + RECT 11.535 21.8425 11.6 21.9775 ; + RECT 11.365 21.8425 11.43 21.9775 ; + RECT 11.18 21.8425 11.245 21.9775 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.41 22.8975 11.545 22.9625 ; + RECT 11.0625 21.5525 11.1975 21.6175 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 11.3975 21.6925 11.5325 21.7575 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.3025 22.5825 11.4375 22.6475 ; + RECT 11.3025 22.5825 11.4375 22.6475 ; + RECT 11.5275 22.4325 11.6625 22.4975 ; + RECT 11.5275 22.4325 11.6625 22.4975 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.3525 21.8425 11.4175 21.9775 ; + RECT 11.0975 22.335 11.1625 22.47 ; + RECT 11.0975 22.335 11.1625 22.47 ; + RECT 11.0975 22.335 11.1625 22.47 ; + RECT 11.0975 22.335 11.1625 22.47 ; + RECT 11.0975 22.335 11.1625 22.47 ; + RECT 11.0975 22.335 11.1625 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.5475 21.8425 11.6125 21.9775 ; + RECT 11.415 21.5525 11.55 21.6175 ; + RECT 11.415 21.5525 11.55 21.6175 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 11.41 22.8975 11.545 22.9625 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.0625 21.5525 11.1975 21.6175 ; + RECT 11.4225 22.8975 11.5225 22.96 ; + RECT 11.4225 22.9 11.5225 22.9625 ; + RECT 11.75 21.695 11.8025 21.7575 ; + RECT 11.4225 22.8975 11.5225 22.96 ; + RECT 11.8 22.6975 11.87 22.8975 ; + RECT 11.8 22.2325 11.87 22.3675 ; + RECT 11.8 22.2325 11.87 22.3675 ; + RECT 11.04 21.5525 11.925 21.6175 ; + RECT 11.615 22.0675 11.79 22.1325 ; + RECT 11.095 22.2325 11.165 22.3675 ; + RECT 11.285 22.0675 11.35 22.8075 ; + RECT 11.4225 22.9 11.5225 22.9625 ; + RECT 11.045 21.695 11.0975 21.7575 ; + RECT 11.8 22.2325 11.87 22.3675 ; + RECT 11.04 22.8975 11.925 22.9625 ; + RECT 11.615 22.0675 11.68 22.6975 ; + RECT 11.8 22.2325 11.87 22.3675 ; + RECT 11.8 22.2325 11.87 22.3675 ; + RECT 11.18 21.8425 11.25 22.1325 ; + RECT 11.095 22.2325 11.165 22.3675 ; + RECT 11.04 21.6925 11.925 21.7575 ; + RECT 11.8 22.2325 11.87 22.3675 ; + RECT 11.8 22.6975 11.87 22.8975 ; + RECT 11.095 22.6975 11.165 22.8975 ; + RECT 11.72 21.8425 11.79 22.1325 ; + RECT 11.18 22.0675 11.35 22.1325 ; + RECT 11.8 23.0275 11.865 23.1625 ; + RECT 11.615 23.0275 11.68 23.1625 ; + RECT 11.1 23.0275 11.165 23.1625 ; + RECT 11.285 23.0275 11.35 23.1625 ; + RECT 11.615 23.4925 11.68 23.6275 ; + RECT 11.8 23.4925 11.865 23.6275 ; + RECT 11.285 23.4925 11.35 23.6275 ; + RECT 11.1 23.4925 11.165 23.6275 ; + RECT 11.72 23.8825 11.785 24.0175 ; + RECT 11.535 23.8825 11.6 24.0175 ; + RECT 11.365 23.8825 11.43 24.0175 ; + RECT 11.18 23.8825 11.245 24.0175 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.41 22.8975 11.545 22.9625 ; + RECT 11.0625 24.2425 11.1975 24.3075 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 11.3975 24.1025 11.5325 24.1675 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.3025 23.2125 11.4375 23.2775 ; + RECT 11.3025 23.2125 11.4375 23.2775 ; + RECT 11.5275 23.3625 11.6625 23.4275 ; + RECT 11.5275 23.3625 11.6625 23.4275 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.3525 23.8825 11.4175 24.0175 ; + RECT 11.0975 23.39 11.1625 23.525 ; + RECT 11.0975 23.39 11.1625 23.525 ; + RECT 11.0975 23.39 11.1625 23.525 ; + RECT 11.0975 23.39 11.1625 23.525 ; + RECT 11.0975 23.39 11.1625 23.525 ; + RECT 11.0975 23.39 11.1625 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.5475 23.8825 11.6125 24.0175 ; + RECT 11.415 24.2425 11.55 24.3075 ; + RECT 11.415 24.2425 11.55 24.3075 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 11.41 22.8975 11.545 22.9625 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.0625 24.2425 11.1975 24.3075 ; + RECT 11.4225 22.9 11.5225 22.9625 ; + RECT 11.4225 22.8975 11.5225 22.96 ; + RECT 11.75 24.1025 11.8025 24.165 ; + RECT 11.4225 22.9 11.5225 22.9625 ; + RECT 11.8 22.9625 11.87 23.1625 ; + RECT 11.8 23.4925 11.87 23.6275 ; + RECT 11.8 23.4925 11.87 23.6275 ; + RECT 11.04 24.2425 11.925 24.3075 ; + RECT 11.615 23.7275 11.79 23.7925 ; + RECT 11.095 23.4925 11.165 23.6275 ; + RECT 11.285 23.0525 11.35 23.7925 ; + RECT 11.4225 22.8975 11.5225 22.96 ; + RECT 11.045 24.1025 11.0975 24.165 ; + RECT 11.8 23.4925 11.87 23.6275 ; + RECT 11.04 22.8975 11.925 22.9625 ; + RECT 11.615 23.1625 11.68 23.7925 ; + RECT 11.8 23.4925 11.87 23.6275 ; + RECT 11.8 23.4925 11.87 23.6275 ; + RECT 11.18 23.7275 11.25 24.0175 ; + RECT 11.095 23.4925 11.165 23.6275 ; + RECT 11.04 24.1025 11.925 24.1675 ; + RECT 11.8 23.4925 11.87 23.6275 ; + RECT 11.8 22.9625 11.87 23.1625 ; + RECT 11.095 22.9625 11.165 23.1625 ; + RECT 11.72 23.7275 11.79 24.0175 ; + RECT 11.18 23.7275 11.35 23.7925 ; + RECT 11.8 25.3875 11.865 25.5225 ; + RECT 11.615 25.3875 11.68 25.5225 ; + RECT 11.1 25.3875 11.165 25.5225 ; + RECT 11.285 25.3875 11.35 25.5225 ; + RECT 11.615 24.9225 11.68 25.0575 ; + RECT 11.8 24.9225 11.865 25.0575 ; + RECT 11.285 24.9225 11.35 25.0575 ; + RECT 11.1 24.9225 11.165 25.0575 ; + RECT 11.72 24.5325 11.785 24.6675 ; + RECT 11.535 24.5325 11.6 24.6675 ; + RECT 11.365 24.5325 11.43 24.6675 ; + RECT 11.18 24.5325 11.245 24.6675 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.41 25.5875 11.545 25.6525 ; + RECT 11.0625 24.2425 11.1975 24.3075 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 11.3975 24.3825 11.5325 24.4475 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.3025 25.2725 11.4375 25.3375 ; + RECT 11.3025 25.2725 11.4375 25.3375 ; + RECT 11.5275 25.1225 11.6625 25.1875 ; + RECT 11.5275 25.1225 11.6625 25.1875 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.3525 24.5325 11.4175 24.6675 ; + RECT 11.0975 25.025 11.1625 25.16 ; + RECT 11.0975 25.025 11.1625 25.16 ; + RECT 11.0975 25.025 11.1625 25.16 ; + RECT 11.0975 25.025 11.1625 25.16 ; + RECT 11.0975 25.025 11.1625 25.16 ; + RECT 11.0975 25.025 11.1625 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.5475 24.5325 11.6125 24.6675 ; + RECT 11.415 24.2425 11.55 24.3075 ; + RECT 11.415 24.2425 11.55 24.3075 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 11.41 25.5875 11.545 25.6525 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.0625 24.2425 11.1975 24.3075 ; + RECT 11.4225 25.5875 11.5225 25.65 ; + RECT 11.4225 25.59 11.5225 25.6525 ; + RECT 11.75 24.385 11.8025 24.4475 ; + RECT 11.4225 25.5875 11.5225 25.65 ; + RECT 11.8 25.3875 11.87 25.5875 ; + RECT 11.8 24.9225 11.87 25.0575 ; + RECT 11.8 24.9225 11.87 25.0575 ; + RECT 11.04 24.2425 11.925 24.3075 ; + RECT 11.615 24.7575 11.79 24.8225 ; + RECT 11.095 24.9225 11.165 25.0575 ; + RECT 11.285 24.7575 11.35 25.4975 ; + RECT 11.4225 25.59 11.5225 25.6525 ; + RECT 11.045 24.385 11.0975 24.4475 ; + RECT 11.8 24.9225 11.87 25.0575 ; + RECT 11.04 25.5875 11.925 25.6525 ; + RECT 11.615 24.7575 11.68 25.3875 ; + RECT 11.8 24.9225 11.87 25.0575 ; + RECT 11.8 24.9225 11.87 25.0575 ; + RECT 11.18 24.5325 11.25 24.8225 ; + RECT 11.095 24.9225 11.165 25.0575 ; + RECT 11.04 24.3825 11.925 24.4475 ; + RECT 11.8 24.9225 11.87 25.0575 ; + RECT 11.8 25.3875 11.87 25.5875 ; + RECT 11.095 25.3875 11.165 25.5875 ; + RECT 11.72 24.5325 11.79 24.8225 ; + RECT 11.18 24.7575 11.35 24.8225 ; + RECT 11.8 25.7175 11.865 25.8525 ; + RECT 11.615 25.7175 11.68 25.8525 ; + RECT 11.1 25.7175 11.165 25.8525 ; + RECT 11.285 25.7175 11.35 25.8525 ; + RECT 11.615 26.1825 11.68 26.3175 ; + RECT 11.8 26.1825 11.865 26.3175 ; + RECT 11.285 26.1825 11.35 26.3175 ; + RECT 11.1 26.1825 11.165 26.3175 ; + RECT 11.72 26.5725 11.785 26.7075 ; + RECT 11.535 26.5725 11.6 26.7075 ; + RECT 11.365 26.5725 11.43 26.7075 ; + RECT 11.18 26.5725 11.245 26.7075 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.41 25.5875 11.545 25.6525 ; + RECT 11.0625 26.9325 11.1975 26.9975 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 11.3975 26.7925 11.5325 26.8575 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.3025 25.9025 11.4375 25.9675 ; + RECT 11.3025 25.9025 11.4375 25.9675 ; + RECT 11.5275 26.0525 11.6625 26.1175 ; + RECT 11.5275 26.0525 11.6625 26.1175 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.3525 26.5725 11.4175 26.7075 ; + RECT 11.0975 26.08 11.1625 26.215 ; + RECT 11.0975 26.08 11.1625 26.215 ; + RECT 11.0975 26.08 11.1625 26.215 ; + RECT 11.0975 26.08 11.1625 26.215 ; + RECT 11.0975 26.08 11.1625 26.215 ; + RECT 11.0975 26.08 11.1625 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.5475 26.5725 11.6125 26.7075 ; + RECT 11.415 26.9325 11.55 26.9975 ; + RECT 11.415 26.9325 11.55 26.9975 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 11.41 25.5875 11.545 25.6525 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.0625 26.9325 11.1975 26.9975 ; + RECT 11.4225 25.59 11.5225 25.6525 ; + RECT 11.4225 25.5875 11.5225 25.65 ; + RECT 11.75 26.7925 11.8025 26.855 ; + RECT 11.4225 25.59 11.5225 25.6525 ; + RECT 11.8 25.6525 11.87 25.8525 ; + RECT 11.8 26.1825 11.87 26.3175 ; + RECT 11.8 26.1825 11.87 26.3175 ; + RECT 11.04 26.9325 11.925 26.9975 ; + RECT 11.615 26.4175 11.79 26.4825 ; + RECT 11.095 26.1825 11.165 26.3175 ; + RECT 11.285 25.7425 11.35 26.4825 ; + RECT 11.4225 25.5875 11.5225 25.65 ; + RECT 11.045 26.7925 11.0975 26.855 ; + RECT 11.8 26.1825 11.87 26.3175 ; + RECT 11.04 25.5875 11.925 25.6525 ; + RECT 11.615 25.8525 11.68 26.4825 ; + RECT 11.8 26.1825 11.87 26.3175 ; + RECT 11.8 26.1825 11.87 26.3175 ; + RECT 11.18 26.4175 11.25 26.7075 ; + RECT 11.095 26.1825 11.165 26.3175 ; + RECT 11.04 26.7925 11.925 26.8575 ; + RECT 11.8 26.1825 11.87 26.3175 ; + RECT 11.8 25.6525 11.87 25.8525 ; + RECT 11.095 25.6525 11.165 25.8525 ; + RECT 11.72 26.4175 11.79 26.7075 ; + RECT 11.18 26.4175 11.35 26.4825 ; + RECT 11.8 28.0775 11.865 28.2125 ; + RECT 11.615 28.0775 11.68 28.2125 ; + RECT 11.1 28.0775 11.165 28.2125 ; + RECT 11.285 28.0775 11.35 28.2125 ; + RECT 11.615 27.6125 11.68 27.7475 ; + RECT 11.8 27.6125 11.865 27.7475 ; + RECT 11.285 27.6125 11.35 27.7475 ; + RECT 11.1 27.6125 11.165 27.7475 ; + RECT 11.72 27.2225 11.785 27.3575 ; + RECT 11.535 27.2225 11.6 27.3575 ; + RECT 11.365 27.2225 11.43 27.3575 ; + RECT 11.18 27.2225 11.245 27.3575 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.41 28.2775 11.545 28.3425 ; + RECT 11.0625 26.9325 11.1975 26.9975 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 11.3975 27.0725 11.5325 27.1375 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.3025 27.9625 11.4375 28.0275 ; + RECT 11.3025 27.9625 11.4375 28.0275 ; + RECT 11.5275 27.8125 11.6625 27.8775 ; + RECT 11.5275 27.8125 11.6625 27.8775 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.3525 27.2225 11.4175 27.3575 ; + RECT 11.0975 27.715 11.1625 27.85 ; + RECT 11.0975 27.715 11.1625 27.85 ; + RECT 11.0975 27.715 11.1625 27.85 ; + RECT 11.0975 27.715 11.1625 27.85 ; + RECT 11.0975 27.715 11.1625 27.85 ; + RECT 11.0975 27.715 11.1625 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.5475 27.2225 11.6125 27.3575 ; + RECT 11.415 26.9325 11.55 26.9975 ; + RECT 11.415 26.9325 11.55 26.9975 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 11.41 28.2775 11.545 28.3425 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.0625 26.9325 11.1975 26.9975 ; + RECT 11.4225 28.2775 11.5225 28.34 ; + RECT 11.4225 28.28 11.5225 28.3425 ; + RECT 11.75 27.075 11.8025 27.1375 ; + RECT 11.4225 28.2775 11.5225 28.34 ; + RECT 11.8 28.0775 11.87 28.2775 ; + RECT 11.8 27.6125 11.87 27.7475 ; + RECT 11.8 27.6125 11.87 27.7475 ; + RECT 11.04 26.9325 11.925 26.9975 ; + RECT 11.615 27.4475 11.79 27.5125 ; + RECT 11.095 27.6125 11.165 27.7475 ; + RECT 11.285 27.4475 11.35 28.1875 ; + RECT 11.4225 28.28 11.5225 28.3425 ; + RECT 11.045 27.075 11.0975 27.1375 ; + RECT 11.8 27.6125 11.87 27.7475 ; + RECT 11.04 28.2775 11.925 28.3425 ; + RECT 11.615 27.4475 11.68 28.0775 ; + RECT 11.8 27.6125 11.87 27.7475 ; + RECT 11.8 27.6125 11.87 27.7475 ; + RECT 11.18 27.2225 11.25 27.5125 ; + RECT 11.095 27.6125 11.165 27.7475 ; + RECT 11.04 27.0725 11.925 27.1375 ; + RECT 11.8 27.6125 11.87 27.7475 ; + RECT 11.8 28.0775 11.87 28.2775 ; + RECT 11.095 28.0775 11.165 28.2775 ; + RECT 11.72 27.2225 11.79 27.5125 ; + RECT 11.18 27.4475 11.35 27.5125 ; + RECT 11.8 28.4075 11.865 28.5425 ; + RECT 11.615 28.4075 11.68 28.5425 ; + RECT 11.1 28.4075 11.165 28.5425 ; + RECT 11.285 28.4075 11.35 28.5425 ; + RECT 11.615 28.8725 11.68 29.0075 ; + RECT 11.8 28.8725 11.865 29.0075 ; + RECT 11.285 28.8725 11.35 29.0075 ; + RECT 11.1 28.8725 11.165 29.0075 ; + RECT 11.72 29.2625 11.785 29.3975 ; + RECT 11.535 29.2625 11.6 29.3975 ; + RECT 11.365 29.2625 11.43 29.3975 ; + RECT 11.18 29.2625 11.245 29.3975 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.41 28.2775 11.545 28.3425 ; + RECT 11.0625 29.6225 11.1975 29.6875 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 11.3975 29.4825 11.5325 29.5475 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.3025 28.5925 11.4375 28.6575 ; + RECT 11.3025 28.5925 11.4375 28.6575 ; + RECT 11.5275 28.7425 11.6625 28.8075 ; + RECT 11.5275 28.7425 11.6625 28.8075 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.3525 29.2625 11.4175 29.3975 ; + RECT 11.0975 28.77 11.1625 28.905 ; + RECT 11.0975 28.77 11.1625 28.905 ; + RECT 11.0975 28.77 11.1625 28.905 ; + RECT 11.0975 28.77 11.1625 28.905 ; + RECT 11.0975 28.77 11.1625 28.905 ; + RECT 11.0975 28.77 11.1625 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.5475 29.2625 11.6125 29.3975 ; + RECT 11.415 29.6225 11.55 29.6875 ; + RECT 11.415 29.6225 11.55 29.6875 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 11.41 28.2775 11.545 28.3425 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.0625 29.6225 11.1975 29.6875 ; + RECT 11.4225 28.28 11.5225 28.3425 ; + RECT 11.4225 28.2775 11.5225 28.34 ; + RECT 11.75 29.4825 11.8025 29.545 ; + RECT 11.4225 28.28 11.5225 28.3425 ; + RECT 11.8 28.3425 11.87 28.5425 ; + RECT 11.8 28.8725 11.87 29.0075 ; + RECT 11.8 28.8725 11.87 29.0075 ; + RECT 11.04 29.6225 11.925 29.6875 ; + RECT 11.615 29.1075 11.79 29.1725 ; + RECT 11.095 28.8725 11.165 29.0075 ; + RECT 11.285 28.4325 11.35 29.1725 ; + RECT 11.4225 28.2775 11.5225 28.34 ; + RECT 11.045 29.4825 11.0975 29.545 ; + RECT 11.8 28.8725 11.87 29.0075 ; + RECT 11.04 28.2775 11.925 28.3425 ; + RECT 11.615 28.5425 11.68 29.1725 ; + RECT 11.8 28.8725 11.87 29.0075 ; + RECT 11.8 28.8725 11.87 29.0075 ; + RECT 11.18 29.1075 11.25 29.3975 ; + RECT 11.095 28.8725 11.165 29.0075 ; + RECT 11.04 29.4825 11.925 29.5475 ; + RECT 11.8 28.8725 11.87 29.0075 ; + RECT 11.8 28.3425 11.87 28.5425 ; + RECT 11.095 28.3425 11.165 28.5425 ; + RECT 11.72 29.1075 11.79 29.3975 ; + RECT 11.18 29.1075 11.35 29.1725 ; + RECT 11.8 30.7675 11.865 30.9025 ; + RECT 11.615 30.7675 11.68 30.9025 ; + RECT 11.1 30.7675 11.165 30.9025 ; + RECT 11.285 30.7675 11.35 30.9025 ; + RECT 11.615 30.3025 11.68 30.4375 ; + RECT 11.8 30.3025 11.865 30.4375 ; + RECT 11.285 30.3025 11.35 30.4375 ; + RECT 11.1 30.3025 11.165 30.4375 ; + RECT 11.72 29.9125 11.785 30.0475 ; + RECT 11.535 29.9125 11.6 30.0475 ; + RECT 11.365 29.9125 11.43 30.0475 ; + RECT 11.18 29.9125 11.245 30.0475 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.41 30.9675 11.545 31.0325 ; + RECT 11.0625 29.6225 11.1975 29.6875 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 11.3975 29.7625 11.5325 29.8275 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.3025 30.6525 11.4375 30.7175 ; + RECT 11.3025 30.6525 11.4375 30.7175 ; + RECT 11.5275 30.5025 11.6625 30.5675 ; + RECT 11.5275 30.5025 11.6625 30.5675 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.3525 29.9125 11.4175 30.0475 ; + RECT 11.0975 30.405 11.1625 30.54 ; + RECT 11.0975 30.405 11.1625 30.54 ; + RECT 11.0975 30.405 11.1625 30.54 ; + RECT 11.0975 30.405 11.1625 30.54 ; + RECT 11.0975 30.405 11.1625 30.54 ; + RECT 11.0975 30.405 11.1625 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.5475 29.9125 11.6125 30.0475 ; + RECT 11.415 29.6225 11.55 29.6875 ; + RECT 11.415 29.6225 11.55 29.6875 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 11.41 30.9675 11.545 31.0325 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.0625 29.6225 11.1975 29.6875 ; + RECT 11.4225 30.9675 11.5225 31.03 ; + RECT 11.4225 30.97 11.5225 31.0325 ; + RECT 11.75 29.765 11.8025 29.8275 ; + RECT 11.4225 30.9675 11.5225 31.03 ; + RECT 11.8 30.7675 11.87 30.9675 ; + RECT 11.8 30.3025 11.87 30.4375 ; + RECT 11.8 30.3025 11.87 30.4375 ; + RECT 11.04 29.6225 11.925 29.6875 ; + RECT 11.615 30.1375 11.79 30.2025 ; + RECT 11.095 30.3025 11.165 30.4375 ; + RECT 11.285 30.1375 11.35 30.8775 ; + RECT 11.4225 30.97 11.5225 31.0325 ; + RECT 11.045 29.765 11.0975 29.8275 ; + RECT 11.8 30.3025 11.87 30.4375 ; + RECT 11.04 30.9675 11.925 31.0325 ; + RECT 11.615 30.1375 11.68 30.7675 ; + RECT 11.8 30.3025 11.87 30.4375 ; + RECT 11.8 30.3025 11.87 30.4375 ; + RECT 11.18 29.9125 11.25 30.2025 ; + RECT 11.095 30.3025 11.165 30.4375 ; + RECT 11.04 29.7625 11.925 29.8275 ; + RECT 11.8 30.3025 11.87 30.4375 ; + RECT 11.8 30.7675 11.87 30.9675 ; + RECT 11.095 30.7675 11.165 30.9675 ; + RECT 11.72 29.9125 11.79 30.2025 ; + RECT 11.18 30.1375 11.35 30.2025 ; + RECT 11.8 31.0975 11.865 31.2325 ; + RECT 11.615 31.0975 11.68 31.2325 ; + RECT 11.1 31.0975 11.165 31.2325 ; + RECT 11.285 31.0975 11.35 31.2325 ; + RECT 11.615 31.5625 11.68 31.6975 ; + RECT 11.8 31.5625 11.865 31.6975 ; + RECT 11.285 31.5625 11.35 31.6975 ; + RECT 11.1 31.5625 11.165 31.6975 ; + RECT 11.72 31.9525 11.785 32.0875 ; + RECT 11.535 31.9525 11.6 32.0875 ; + RECT 11.365 31.9525 11.43 32.0875 ; + RECT 11.18 31.9525 11.245 32.0875 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.41 30.9675 11.545 31.0325 ; + RECT 11.0625 32.3125 11.1975 32.3775 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 11.3975 32.1725 11.5325 32.2375 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.3025 31.2825 11.4375 31.3475 ; + RECT 11.3025 31.2825 11.4375 31.3475 ; + RECT 11.5275 31.4325 11.6625 31.4975 ; + RECT 11.5275 31.4325 11.6625 31.4975 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.3525 31.9525 11.4175 32.0875 ; + RECT 11.0975 31.46 11.1625 31.595 ; + RECT 11.0975 31.46 11.1625 31.595 ; + RECT 11.0975 31.46 11.1625 31.595 ; + RECT 11.0975 31.46 11.1625 31.595 ; + RECT 11.0975 31.46 11.1625 31.595 ; + RECT 11.0975 31.46 11.1625 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.5475 31.9525 11.6125 32.0875 ; + RECT 11.415 32.3125 11.55 32.3775 ; + RECT 11.415 32.3125 11.55 32.3775 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 11.41 30.9675 11.545 31.0325 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.0625 32.3125 11.1975 32.3775 ; + RECT 11.4225 30.97 11.5225 31.0325 ; + RECT 11.4225 30.9675 11.5225 31.03 ; + RECT 11.75 32.1725 11.8025 32.235 ; + RECT 11.4225 30.97 11.5225 31.0325 ; + RECT 11.8 31.0325 11.87 31.2325 ; + RECT 11.8 31.5625 11.87 31.6975 ; + RECT 11.8 31.5625 11.87 31.6975 ; + RECT 11.04 32.3125 11.925 32.3775 ; + RECT 11.615 31.7975 11.79 31.8625 ; + RECT 11.095 31.5625 11.165 31.6975 ; + RECT 11.285 31.1225 11.35 31.8625 ; + RECT 11.4225 30.9675 11.5225 31.03 ; + RECT 11.045 32.1725 11.0975 32.235 ; + RECT 11.8 31.5625 11.87 31.6975 ; + RECT 11.04 30.9675 11.925 31.0325 ; + RECT 11.615 31.2325 11.68 31.8625 ; + RECT 11.8 31.5625 11.87 31.6975 ; + RECT 11.8 31.5625 11.87 31.6975 ; + RECT 11.18 31.7975 11.25 32.0875 ; + RECT 11.095 31.5625 11.165 31.6975 ; + RECT 11.04 32.1725 11.925 32.2375 ; + RECT 11.8 31.5625 11.87 31.6975 ; + RECT 11.8 31.0325 11.87 31.2325 ; + RECT 11.095 31.0325 11.165 31.2325 ; + RECT 11.72 31.7975 11.79 32.0875 ; + RECT 11.18 31.7975 11.35 31.8625 ; + RECT 11.8 33.4575 11.865 33.5925 ; + RECT 11.615 33.4575 11.68 33.5925 ; + RECT 11.1 33.4575 11.165 33.5925 ; + RECT 11.285 33.4575 11.35 33.5925 ; + RECT 11.615 32.9925 11.68 33.1275 ; + RECT 11.8 32.9925 11.865 33.1275 ; + RECT 11.285 32.9925 11.35 33.1275 ; + RECT 11.1 32.9925 11.165 33.1275 ; + RECT 11.72 32.6025 11.785 32.7375 ; + RECT 11.535 32.6025 11.6 32.7375 ; + RECT 11.365 32.6025 11.43 32.7375 ; + RECT 11.18 32.6025 11.245 32.7375 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.41 33.6575 11.545 33.7225 ; + RECT 11.0625 32.3125 11.1975 32.3775 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 11.3975 32.4525 11.5325 32.5175 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.3025 33.3425 11.4375 33.4075 ; + RECT 11.3025 33.3425 11.4375 33.4075 ; + RECT 11.5275 33.1925 11.6625 33.2575 ; + RECT 11.5275 33.1925 11.6625 33.2575 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.3525 32.6025 11.4175 32.7375 ; + RECT 11.0975 33.095 11.1625 33.23 ; + RECT 11.0975 33.095 11.1625 33.23 ; + RECT 11.0975 33.095 11.1625 33.23 ; + RECT 11.0975 33.095 11.1625 33.23 ; + RECT 11.0975 33.095 11.1625 33.23 ; + RECT 11.0975 33.095 11.1625 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.5475 32.6025 11.6125 32.7375 ; + RECT 11.415 32.3125 11.55 32.3775 ; + RECT 11.415 32.3125 11.55 32.3775 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 11.41 33.6575 11.545 33.7225 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.0625 32.3125 11.1975 32.3775 ; + RECT 11.4225 33.6575 11.5225 33.72 ; + RECT 11.4225 33.66 11.5225 33.7225 ; + RECT 11.75 32.455 11.8025 32.5175 ; + RECT 11.4225 33.6575 11.5225 33.72 ; + RECT 11.8 33.4575 11.87 33.6575 ; + RECT 11.8 32.9925 11.87 33.1275 ; + RECT 11.8 32.9925 11.87 33.1275 ; + RECT 11.04 32.3125 11.925 32.3775 ; + RECT 11.615 32.8275 11.79 32.8925 ; + RECT 11.095 32.9925 11.165 33.1275 ; + RECT 11.285 32.8275 11.35 33.5675 ; + RECT 11.4225 33.66 11.5225 33.7225 ; + RECT 11.045 32.455 11.0975 32.5175 ; + RECT 11.8 32.9925 11.87 33.1275 ; + RECT 11.04 33.6575 11.925 33.7225 ; + RECT 11.615 32.8275 11.68 33.4575 ; + RECT 11.8 32.9925 11.87 33.1275 ; + RECT 11.8 32.9925 11.87 33.1275 ; + RECT 11.18 32.6025 11.25 32.8925 ; + RECT 11.095 32.9925 11.165 33.1275 ; + RECT 11.04 32.4525 11.925 32.5175 ; + RECT 11.8 32.9925 11.87 33.1275 ; + RECT 11.8 33.4575 11.87 33.6575 ; + RECT 11.095 33.4575 11.165 33.6575 ; + RECT 11.72 32.6025 11.79 32.8925 ; + RECT 11.18 32.8275 11.35 32.8925 ; + RECT 11.8 33.7875 11.865 33.9225 ; + RECT 11.615 33.7875 11.68 33.9225 ; + RECT 11.1 33.7875 11.165 33.9225 ; + RECT 11.285 33.7875 11.35 33.9225 ; + RECT 11.615 34.2525 11.68 34.3875 ; + RECT 11.8 34.2525 11.865 34.3875 ; + RECT 11.285 34.2525 11.35 34.3875 ; + RECT 11.1 34.2525 11.165 34.3875 ; + RECT 11.72 34.6425 11.785 34.7775 ; + RECT 11.535 34.6425 11.6 34.7775 ; + RECT 11.365 34.6425 11.43 34.7775 ; + RECT 11.18 34.6425 11.245 34.7775 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.41 33.6575 11.545 33.7225 ; + RECT 11.0625 35.0025 11.1975 35.0675 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 11.3975 34.8625 11.5325 34.9275 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.3025 33.9725 11.4375 34.0375 ; + RECT 11.3025 33.9725 11.4375 34.0375 ; + RECT 11.5275 34.1225 11.6625 34.1875 ; + RECT 11.5275 34.1225 11.6625 34.1875 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.3525 34.6425 11.4175 34.7775 ; + RECT 11.0975 34.15 11.1625 34.285 ; + RECT 11.0975 34.15 11.1625 34.285 ; + RECT 11.0975 34.15 11.1625 34.285 ; + RECT 11.0975 34.15 11.1625 34.285 ; + RECT 11.0975 34.15 11.1625 34.285 ; + RECT 11.0975 34.15 11.1625 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.5475 34.6425 11.6125 34.7775 ; + RECT 11.415 35.0025 11.55 35.0675 ; + RECT 11.415 35.0025 11.55 35.0675 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 11.41 33.6575 11.545 33.7225 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.0625 35.0025 11.1975 35.0675 ; + RECT 11.4225 33.66 11.5225 33.7225 ; + RECT 11.4225 33.6575 11.5225 33.72 ; + RECT 11.75 34.8625 11.8025 34.925 ; + RECT 11.4225 33.66 11.5225 33.7225 ; + RECT 11.8 33.7225 11.87 33.9225 ; + RECT 11.8 34.2525 11.87 34.3875 ; + RECT 11.8 34.2525 11.87 34.3875 ; + RECT 11.04 35.0025 11.925 35.0675 ; + RECT 11.615 34.4875 11.79 34.5525 ; + RECT 11.095 34.2525 11.165 34.3875 ; + RECT 11.285 33.8125 11.35 34.5525 ; + RECT 11.4225 33.6575 11.5225 33.72 ; + RECT 11.045 34.8625 11.0975 34.925 ; + RECT 11.8 34.2525 11.87 34.3875 ; + RECT 11.04 33.6575 11.925 33.7225 ; + RECT 11.615 33.9225 11.68 34.5525 ; + RECT 11.8 34.2525 11.87 34.3875 ; + RECT 11.8 34.2525 11.87 34.3875 ; + RECT 11.18 34.4875 11.25 34.7775 ; + RECT 11.095 34.2525 11.165 34.3875 ; + RECT 11.04 34.8625 11.925 34.9275 ; + RECT 11.8 34.2525 11.87 34.3875 ; + RECT 11.8 33.7225 11.87 33.9225 ; + RECT 11.095 33.7225 11.165 33.9225 ; + RECT 11.72 34.4875 11.79 34.7775 ; + RECT 11.18 34.4875 11.35 34.5525 ; + RECT 11.8 36.1475 11.865 36.2825 ; + RECT 11.615 36.1475 11.68 36.2825 ; + RECT 11.1 36.1475 11.165 36.2825 ; + RECT 11.285 36.1475 11.35 36.2825 ; + RECT 11.615 35.6825 11.68 35.8175 ; + RECT 11.8 35.6825 11.865 35.8175 ; + RECT 11.285 35.6825 11.35 35.8175 ; + RECT 11.1 35.6825 11.165 35.8175 ; + RECT 11.72 35.2925 11.785 35.4275 ; + RECT 11.535 35.2925 11.6 35.4275 ; + RECT 11.365 35.2925 11.43 35.4275 ; + RECT 11.18 35.2925 11.245 35.4275 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.41 36.3475 11.545 36.4125 ; + RECT 11.0625 35.0025 11.1975 35.0675 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 11.3975 35.1425 11.5325 35.2075 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.3025 36.0325 11.4375 36.0975 ; + RECT 11.3025 36.0325 11.4375 36.0975 ; + RECT 11.5275 35.8825 11.6625 35.9475 ; + RECT 11.5275 35.8825 11.6625 35.9475 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.3525 35.2925 11.4175 35.4275 ; + RECT 11.0975 35.785 11.1625 35.92 ; + RECT 11.0975 35.785 11.1625 35.92 ; + RECT 11.0975 35.785 11.1625 35.92 ; + RECT 11.0975 35.785 11.1625 35.92 ; + RECT 11.0975 35.785 11.1625 35.92 ; + RECT 11.0975 35.785 11.1625 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.5475 35.2925 11.6125 35.4275 ; + RECT 11.415 35.0025 11.55 35.0675 ; + RECT 11.415 35.0025 11.55 35.0675 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 11.41 36.3475 11.545 36.4125 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.0625 35.0025 11.1975 35.0675 ; + RECT 11.4225 36.3475 11.5225 36.41 ; + RECT 11.4225 36.35 11.5225 36.4125 ; + RECT 11.75 35.145 11.8025 35.2075 ; + RECT 11.4225 36.3475 11.5225 36.41 ; + RECT 11.8 36.1475 11.87 36.3475 ; + RECT 11.8 35.6825 11.87 35.8175 ; + RECT 11.8 35.6825 11.87 35.8175 ; + RECT 11.04 35.0025 11.925 35.0675 ; + RECT 11.615 35.5175 11.79 35.5825 ; + RECT 11.095 35.6825 11.165 35.8175 ; + RECT 11.285 35.5175 11.35 36.2575 ; + RECT 11.4225 36.35 11.5225 36.4125 ; + RECT 11.045 35.145 11.0975 35.2075 ; + RECT 11.8 35.6825 11.87 35.8175 ; + RECT 11.04 36.3475 11.925 36.4125 ; + RECT 11.615 35.5175 11.68 36.1475 ; + RECT 11.8 35.6825 11.87 35.8175 ; + RECT 11.8 35.6825 11.87 35.8175 ; + RECT 11.18 35.2925 11.25 35.5825 ; + RECT 11.095 35.6825 11.165 35.8175 ; + RECT 11.04 35.1425 11.925 35.2075 ; + RECT 11.8 35.6825 11.87 35.8175 ; + RECT 11.8 36.1475 11.87 36.3475 ; + RECT 11.095 36.1475 11.165 36.3475 ; + RECT 11.72 35.2925 11.79 35.5825 ; + RECT 11.18 35.5175 11.35 35.5825 ; + RECT 11.8 36.4775 11.865 36.6125 ; + RECT 11.615 36.4775 11.68 36.6125 ; + RECT 11.1 36.4775 11.165 36.6125 ; + RECT 11.285 36.4775 11.35 36.6125 ; + RECT 11.615 36.9425 11.68 37.0775 ; + RECT 11.8 36.9425 11.865 37.0775 ; + RECT 11.285 36.9425 11.35 37.0775 ; + RECT 11.1 36.9425 11.165 37.0775 ; + RECT 11.72 37.3325 11.785 37.4675 ; + RECT 11.535 37.3325 11.6 37.4675 ; + RECT 11.365 37.3325 11.43 37.4675 ; + RECT 11.18 37.3325 11.245 37.4675 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.41 36.3475 11.545 36.4125 ; + RECT 11.0625 37.6925 11.1975 37.7575 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 11.3975 37.5525 11.5325 37.6175 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.3025 36.6625 11.4375 36.7275 ; + RECT 11.3025 36.6625 11.4375 36.7275 ; + RECT 11.5275 36.8125 11.6625 36.8775 ; + RECT 11.5275 36.8125 11.6625 36.8775 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.3525 37.3325 11.4175 37.4675 ; + RECT 11.0975 36.84 11.1625 36.975 ; + RECT 11.0975 36.84 11.1625 36.975 ; + RECT 11.0975 36.84 11.1625 36.975 ; + RECT 11.0975 36.84 11.1625 36.975 ; + RECT 11.0975 36.84 11.1625 36.975 ; + RECT 11.0975 36.84 11.1625 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.5475 37.3325 11.6125 37.4675 ; + RECT 11.415 37.6925 11.55 37.7575 ; + RECT 11.415 37.6925 11.55 37.7575 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 11.41 36.3475 11.545 36.4125 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.0625 37.6925 11.1975 37.7575 ; + RECT 11.4225 36.35 11.5225 36.4125 ; + RECT 11.4225 36.3475 11.5225 36.41 ; + RECT 11.75 37.5525 11.8025 37.615 ; + RECT 11.4225 36.35 11.5225 36.4125 ; + RECT 11.8 36.4125 11.87 36.6125 ; + RECT 11.8 36.9425 11.87 37.0775 ; + RECT 11.8 36.9425 11.87 37.0775 ; + RECT 11.04 37.6925 11.925 37.7575 ; + RECT 11.615 37.1775 11.79 37.2425 ; + RECT 11.095 36.9425 11.165 37.0775 ; + RECT 11.285 36.5025 11.35 37.2425 ; + RECT 11.4225 36.3475 11.5225 36.41 ; + RECT 11.045 37.5525 11.0975 37.615 ; + RECT 11.8 36.9425 11.87 37.0775 ; + RECT 11.04 36.3475 11.925 36.4125 ; + RECT 11.615 36.6125 11.68 37.2425 ; + RECT 11.8 36.9425 11.87 37.0775 ; + RECT 11.8 36.9425 11.87 37.0775 ; + RECT 11.18 37.1775 11.25 37.4675 ; + RECT 11.095 36.9425 11.165 37.0775 ; + RECT 11.04 37.5525 11.925 37.6175 ; + RECT 11.8 36.9425 11.87 37.0775 ; + RECT 11.8 36.4125 11.87 36.6125 ; + RECT 11.095 36.4125 11.165 36.6125 ; + RECT 11.72 37.1775 11.79 37.4675 ; + RECT 11.18 37.1775 11.35 37.2425 ; + RECT 11.8 38.8375 11.865 38.9725 ; + RECT 11.615 38.8375 11.68 38.9725 ; + RECT 11.1 38.8375 11.165 38.9725 ; + RECT 11.285 38.8375 11.35 38.9725 ; + RECT 11.615 38.3725 11.68 38.5075 ; + RECT 11.8 38.3725 11.865 38.5075 ; + RECT 11.285 38.3725 11.35 38.5075 ; + RECT 11.1 38.3725 11.165 38.5075 ; + RECT 11.72 37.9825 11.785 38.1175 ; + RECT 11.535 37.9825 11.6 38.1175 ; + RECT 11.365 37.9825 11.43 38.1175 ; + RECT 11.18 37.9825 11.245 38.1175 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.41 39.0375 11.545 39.1025 ; + RECT 11.0625 37.6925 11.1975 37.7575 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 11.3975 37.8325 11.5325 37.8975 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.3025 38.7225 11.4375 38.7875 ; + RECT 11.3025 38.7225 11.4375 38.7875 ; + RECT 11.5275 38.5725 11.6625 38.6375 ; + RECT 11.5275 38.5725 11.6625 38.6375 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.3525 37.9825 11.4175 38.1175 ; + RECT 11.0975 38.475 11.1625 38.61 ; + RECT 11.0975 38.475 11.1625 38.61 ; + RECT 11.0975 38.475 11.1625 38.61 ; + RECT 11.0975 38.475 11.1625 38.61 ; + RECT 11.0975 38.475 11.1625 38.61 ; + RECT 11.0975 38.475 11.1625 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.5475 37.9825 11.6125 38.1175 ; + RECT 11.415 37.6925 11.55 37.7575 ; + RECT 11.415 37.6925 11.55 37.7575 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 11.41 39.0375 11.545 39.1025 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.0625 37.6925 11.1975 37.7575 ; + RECT 11.4225 39.0375 11.5225 39.1 ; + RECT 11.4225 39.04 11.5225 39.1025 ; + RECT 11.75 37.835 11.8025 37.8975 ; + RECT 11.4225 39.0375 11.5225 39.1 ; + RECT 11.8 38.8375 11.87 39.0375 ; + RECT 11.8 38.3725 11.87 38.5075 ; + RECT 11.8 38.3725 11.87 38.5075 ; + RECT 11.04 37.6925 11.925 37.7575 ; + RECT 11.615 38.2075 11.79 38.2725 ; + RECT 11.095 38.3725 11.165 38.5075 ; + RECT 11.285 38.2075 11.35 38.9475 ; + RECT 11.4225 39.04 11.5225 39.1025 ; + RECT 11.045 37.835 11.0975 37.8975 ; + RECT 11.8 38.3725 11.87 38.5075 ; + RECT 11.04 39.0375 11.925 39.1025 ; + RECT 11.615 38.2075 11.68 38.8375 ; + RECT 11.8 38.3725 11.87 38.5075 ; + RECT 11.8 38.3725 11.87 38.5075 ; + RECT 11.18 37.9825 11.25 38.2725 ; + RECT 11.095 38.3725 11.165 38.5075 ; + RECT 11.04 37.8325 11.925 37.8975 ; + RECT 11.8 38.3725 11.87 38.5075 ; + RECT 11.8 38.8375 11.87 39.0375 ; + RECT 11.095 38.8375 11.165 39.0375 ; + RECT 11.72 37.9825 11.79 38.2725 ; + RECT 11.18 38.2075 11.35 38.2725 ; + RECT 11.8 39.1675 11.865 39.3025 ; + RECT 11.615 39.1675 11.68 39.3025 ; + RECT 11.1 39.1675 11.165 39.3025 ; + RECT 11.285 39.1675 11.35 39.3025 ; + RECT 11.615 39.6325 11.68 39.7675 ; + RECT 11.8 39.6325 11.865 39.7675 ; + RECT 11.285 39.6325 11.35 39.7675 ; + RECT 11.1 39.6325 11.165 39.7675 ; + RECT 11.72 40.0225 11.785 40.1575 ; + RECT 11.535 40.0225 11.6 40.1575 ; + RECT 11.365 40.0225 11.43 40.1575 ; + RECT 11.18 40.0225 11.245 40.1575 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.41 39.0375 11.545 39.1025 ; + RECT 11.0625 40.3825 11.1975 40.4475 ; + RECT 11.7675 40.3825 11.9025 40.4475 ; + RECT 11.3975 40.2425 11.5325 40.3075 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.3025 39.3525 11.4375 39.4175 ; + RECT 11.3025 39.3525 11.4375 39.4175 ; + RECT 11.5275 39.5025 11.6625 39.5675 ; + RECT 11.5275 39.5025 11.6625 39.5675 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.3525 40.0225 11.4175 40.1575 ; + RECT 11.0975 39.53 11.1625 39.665 ; + RECT 11.0975 39.53 11.1625 39.665 ; + RECT 11.0975 39.53 11.1625 39.665 ; + RECT 11.0975 39.53 11.1625 39.665 ; + RECT 11.0975 39.53 11.1625 39.665 ; + RECT 11.0975 39.53 11.1625 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.5475 40.0225 11.6125 40.1575 ; + RECT 11.415 40.3825 11.55 40.4475 ; + RECT 11.415 40.3825 11.55 40.4475 ; + RECT 11.7675 40.3825 11.9025 40.4475 ; + RECT 11.41 39.0375 11.545 39.1025 ; + RECT 11.7675 40.3825 11.9025 40.4475 ; + RECT 11.7675 40.3825 11.9025 40.4475 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.0625 40.3825 11.1975 40.4475 ; + RECT 11.4225 39.04 11.5225 39.1025 ; + RECT 11.4225 39.0375 11.5225 39.1 ; + RECT 11.75 40.2425 11.8025 40.305 ; + RECT 11.4225 39.04 11.5225 39.1025 ; + RECT 11.8 39.1025 11.87 39.3025 ; + RECT 11.8 39.6325 11.87 39.7675 ; + RECT 11.8 39.6325 11.87 39.7675 ; + RECT 11.04 40.3825 11.925 40.4475 ; + RECT 11.615 39.8675 11.79 39.9325 ; + RECT 11.095 39.6325 11.165 39.7675 ; + RECT 11.285 39.1925 11.35 39.9325 ; + RECT 11.4225 39.0375 11.5225 39.1 ; + RECT 11.045 40.2425 11.0975 40.305 ; + RECT 11.8 39.6325 11.87 39.7675 ; + RECT 11.04 39.0375 11.925 39.1025 ; + RECT 11.615 39.3025 11.68 39.9325 ; + RECT 11.8 39.6325 11.87 39.7675 ; + RECT 11.8 39.6325 11.87 39.7675 ; + RECT 11.18 39.8675 11.25 40.1575 ; + RECT 11.095 39.6325 11.165 39.7675 ; + RECT 11.04 40.2425 11.925 40.3075 ; + RECT 11.8 39.6325 11.87 39.7675 ; + RECT 11.8 39.1025 11.87 39.3025 ; + RECT 11.095 39.1025 11.165 39.3025 ; + RECT 11.72 39.8675 11.79 40.1575 ; + RECT 11.18 39.8675 11.35 39.9325 ; + RECT 12.505 20.0075 12.57 20.1425 ; + RECT 12.32 20.0075 12.385 20.1425 ; + RECT 11.805 20.0075 11.87 20.1425 ; + RECT 11.99 20.0075 12.055 20.1425 ; + RECT 12.32 19.5425 12.385 19.6775 ; + RECT 12.505 19.5425 12.57 19.6775 ; + RECT 11.99 19.5425 12.055 19.6775 ; + RECT 11.805 19.5425 11.87 19.6775 ; + RECT 12.425 19.1525 12.49 19.2875 ; + RECT 12.24 19.1525 12.305 19.2875 ; + RECT 12.07 19.1525 12.135 19.2875 ; + RECT 11.885 19.1525 11.95 19.2875 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.115 20.2075 12.25 20.2725 ; + RECT 11.7675 18.8625 11.9025 18.9275 ; + RECT 12.4725 18.8625 12.6075 18.9275 ; + RECT 12.1025 19.0025 12.2375 19.0675 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.0075 19.8925 12.1425 19.9575 ; + RECT 12.0075 19.8925 12.1425 19.9575 ; + RECT 12.2325 19.7425 12.3675 19.8075 ; + RECT 12.2325 19.7425 12.3675 19.8075 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.0575 19.1525 12.1225 19.2875 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 11.8025 19.645 11.8675 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.2525 19.1525 12.3175 19.2875 ; + RECT 12.12 18.8625 12.255 18.9275 ; + RECT 12.12 18.8625 12.255 18.9275 ; + RECT 12.4725 18.8625 12.6075 18.9275 ; + RECT 12.115 20.2075 12.25 20.2725 ; + RECT 12.4725 18.8625 12.6075 18.9275 ; + RECT 12.4725 18.8625 12.6075 18.9275 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 12.5075 19.645 12.5725 19.78 ; + RECT 11.7675 18.8625 11.9025 18.9275 ; + RECT 12.1275 20.2075 12.2275 20.27 ; + RECT 12.1275 20.21 12.2275 20.2725 ; + RECT 12.455 19.005 12.5075 19.0675 ; + RECT 12.1275 20.2075 12.2275 20.27 ; + RECT 12.505 20.0075 12.575 20.2075 ; + RECT 12.505 19.5425 12.575 19.6775 ; + RECT 12.505 19.5425 12.575 19.6775 ; + RECT 11.745 18.8625 12.63 18.9275 ; + RECT 12.32 19.3775 12.495 19.4425 ; + RECT 11.8 19.5425 11.87 19.6775 ; + RECT 11.99 19.3775 12.055 20.1175 ; + RECT 12.1275 20.21 12.2275 20.2725 ; + RECT 11.75 19.005 11.8025 19.0675 ; + RECT 12.505 19.5425 12.575 19.6775 ; + RECT 11.745 20.2075 12.63 20.2725 ; + RECT 12.32 19.3775 12.385 20.0075 ; + RECT 12.505 19.5425 12.575 19.6775 ; + RECT 12.505 19.5425 12.575 19.6775 ; + RECT 11.885 19.1525 11.955 19.4425 ; + RECT 11.8 19.5425 11.87 19.6775 ; + RECT 11.745 19.0025 12.63 19.0675 ; + RECT 12.505 19.5425 12.575 19.6775 ; + RECT 12.505 20.0075 12.575 20.2075 ; + RECT 11.8 20.0075 11.87 20.2075 ; + RECT 12.425 19.1525 12.495 19.4425 ; + RECT 11.885 19.3775 12.055 19.4425 ; + RECT 12.505 20.3375 12.57 20.4725 ; + RECT 12.32 20.3375 12.385 20.4725 ; + RECT 11.805 20.3375 11.87 20.4725 ; + RECT 11.99 20.3375 12.055 20.4725 ; + RECT 12.32 20.8025 12.385 20.9375 ; + RECT 12.505 20.8025 12.57 20.9375 ; + RECT 11.99 20.8025 12.055 20.9375 ; + RECT 11.805 20.8025 11.87 20.9375 ; + RECT 12.425 21.1925 12.49 21.3275 ; + RECT 12.24 21.1925 12.305 21.3275 ; + RECT 12.07 21.1925 12.135 21.3275 ; + RECT 11.885 21.1925 11.95 21.3275 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.115 20.2075 12.25 20.2725 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 12.4725 21.5525 12.6075 21.6175 ; + RECT 12.1025 21.4125 12.2375 21.4775 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.0075 20.5225 12.1425 20.5875 ; + RECT 12.0075 20.5225 12.1425 20.5875 ; + RECT 12.2325 20.6725 12.3675 20.7375 ; + RECT 12.2325 20.6725 12.3675 20.7375 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.0575 21.1925 12.1225 21.3275 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 11.8025 20.7 11.8675 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.2525 21.1925 12.3175 21.3275 ; + RECT 12.12 21.5525 12.255 21.6175 ; + RECT 12.12 21.5525 12.255 21.6175 ; + RECT 12.4725 21.5525 12.6075 21.6175 ; + RECT 12.115 20.2075 12.25 20.2725 ; + RECT 12.4725 21.5525 12.6075 21.6175 ; + RECT 12.4725 21.5525 12.6075 21.6175 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 12.5075 20.7 12.5725 20.835 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 12.1275 20.21 12.2275 20.2725 ; + RECT 12.1275 20.2075 12.2275 20.27 ; + RECT 12.455 21.4125 12.5075 21.475 ; + RECT 12.1275 20.21 12.2275 20.2725 ; + RECT 12.505 20.2725 12.575 20.4725 ; + RECT 12.505 20.8025 12.575 20.9375 ; + RECT 12.505 20.8025 12.575 20.9375 ; + RECT 11.745 21.5525 12.63 21.6175 ; + RECT 12.32 21.0375 12.495 21.1025 ; + RECT 11.8 20.8025 11.87 20.9375 ; + RECT 11.99 20.3625 12.055 21.1025 ; + RECT 12.1275 20.2075 12.2275 20.27 ; + RECT 11.75 21.4125 11.8025 21.475 ; + RECT 12.505 20.8025 12.575 20.9375 ; + RECT 11.745 20.2075 12.63 20.2725 ; + RECT 12.32 20.4725 12.385 21.1025 ; + RECT 12.505 20.8025 12.575 20.9375 ; + RECT 12.505 20.8025 12.575 20.9375 ; + RECT 11.885 21.0375 11.955 21.3275 ; + RECT 11.8 20.8025 11.87 20.9375 ; + RECT 11.745 21.4125 12.63 21.4775 ; + RECT 12.505 20.8025 12.575 20.9375 ; + RECT 12.505 20.2725 12.575 20.4725 ; + RECT 11.8 20.2725 11.87 20.4725 ; + RECT 12.425 21.0375 12.495 21.3275 ; + RECT 11.885 21.0375 12.055 21.1025 ; + RECT 12.505 22.6975 12.57 22.8325 ; + RECT 12.32 22.6975 12.385 22.8325 ; + RECT 11.805 22.6975 11.87 22.8325 ; + RECT 11.99 22.6975 12.055 22.8325 ; + RECT 12.32 22.2325 12.385 22.3675 ; + RECT 12.505 22.2325 12.57 22.3675 ; + RECT 11.99 22.2325 12.055 22.3675 ; + RECT 11.805 22.2325 11.87 22.3675 ; + RECT 12.425 21.8425 12.49 21.9775 ; + RECT 12.24 21.8425 12.305 21.9775 ; + RECT 12.07 21.8425 12.135 21.9775 ; + RECT 11.885 21.8425 11.95 21.9775 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.115 22.8975 12.25 22.9625 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 12.4725 21.5525 12.6075 21.6175 ; + RECT 12.1025 21.6925 12.2375 21.7575 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.0075 22.5825 12.1425 22.6475 ; + RECT 12.0075 22.5825 12.1425 22.6475 ; + RECT 12.2325 22.4325 12.3675 22.4975 ; + RECT 12.2325 22.4325 12.3675 22.4975 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.0575 21.8425 12.1225 21.9775 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 11.8025 22.335 11.8675 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.2525 21.8425 12.3175 21.9775 ; + RECT 12.12 21.5525 12.255 21.6175 ; + RECT 12.12 21.5525 12.255 21.6175 ; + RECT 12.4725 21.5525 12.6075 21.6175 ; + RECT 12.115 22.8975 12.25 22.9625 ; + RECT 12.4725 21.5525 12.6075 21.6175 ; + RECT 12.4725 21.5525 12.6075 21.6175 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 12.5075 22.335 12.5725 22.47 ; + RECT 11.7675 21.5525 11.9025 21.6175 ; + RECT 12.1275 22.8975 12.2275 22.96 ; + RECT 12.1275 22.9 12.2275 22.9625 ; + RECT 12.455 21.695 12.5075 21.7575 ; + RECT 12.1275 22.8975 12.2275 22.96 ; + RECT 12.505 22.6975 12.575 22.8975 ; + RECT 12.505 22.2325 12.575 22.3675 ; + RECT 12.505 22.2325 12.575 22.3675 ; + RECT 11.745 21.5525 12.63 21.6175 ; + RECT 12.32 22.0675 12.495 22.1325 ; + RECT 11.8 22.2325 11.87 22.3675 ; + RECT 11.99 22.0675 12.055 22.8075 ; + RECT 12.1275 22.9 12.2275 22.9625 ; + RECT 11.75 21.695 11.8025 21.7575 ; + RECT 12.505 22.2325 12.575 22.3675 ; + RECT 11.745 22.8975 12.63 22.9625 ; + RECT 12.32 22.0675 12.385 22.6975 ; + RECT 12.505 22.2325 12.575 22.3675 ; + RECT 12.505 22.2325 12.575 22.3675 ; + RECT 11.885 21.8425 11.955 22.1325 ; + RECT 11.8 22.2325 11.87 22.3675 ; + RECT 11.745 21.6925 12.63 21.7575 ; + RECT 12.505 22.2325 12.575 22.3675 ; + RECT 12.505 22.6975 12.575 22.8975 ; + RECT 11.8 22.6975 11.87 22.8975 ; + RECT 12.425 21.8425 12.495 22.1325 ; + RECT 11.885 22.0675 12.055 22.1325 ; + RECT 12.505 23.0275 12.57 23.1625 ; + RECT 12.32 23.0275 12.385 23.1625 ; + RECT 11.805 23.0275 11.87 23.1625 ; + RECT 11.99 23.0275 12.055 23.1625 ; + RECT 12.32 23.4925 12.385 23.6275 ; + RECT 12.505 23.4925 12.57 23.6275 ; + RECT 11.99 23.4925 12.055 23.6275 ; + RECT 11.805 23.4925 11.87 23.6275 ; + RECT 12.425 23.8825 12.49 24.0175 ; + RECT 12.24 23.8825 12.305 24.0175 ; + RECT 12.07 23.8825 12.135 24.0175 ; + RECT 11.885 23.8825 11.95 24.0175 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.115 22.8975 12.25 22.9625 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 12.4725 24.2425 12.6075 24.3075 ; + RECT 12.1025 24.1025 12.2375 24.1675 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.0075 23.2125 12.1425 23.2775 ; + RECT 12.0075 23.2125 12.1425 23.2775 ; + RECT 12.2325 23.3625 12.3675 23.4275 ; + RECT 12.2325 23.3625 12.3675 23.4275 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.0575 23.8825 12.1225 24.0175 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 11.8025 23.39 11.8675 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.2525 23.8825 12.3175 24.0175 ; + RECT 12.12 24.2425 12.255 24.3075 ; + RECT 12.12 24.2425 12.255 24.3075 ; + RECT 12.4725 24.2425 12.6075 24.3075 ; + RECT 12.115 22.8975 12.25 22.9625 ; + RECT 12.4725 24.2425 12.6075 24.3075 ; + RECT 12.4725 24.2425 12.6075 24.3075 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 12.5075 23.39 12.5725 23.525 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 12.1275 22.9 12.2275 22.9625 ; + RECT 12.1275 22.8975 12.2275 22.96 ; + RECT 12.455 24.1025 12.5075 24.165 ; + RECT 12.1275 22.9 12.2275 22.9625 ; + RECT 12.505 22.9625 12.575 23.1625 ; + RECT 12.505 23.4925 12.575 23.6275 ; + RECT 12.505 23.4925 12.575 23.6275 ; + RECT 11.745 24.2425 12.63 24.3075 ; + RECT 12.32 23.7275 12.495 23.7925 ; + RECT 11.8 23.4925 11.87 23.6275 ; + RECT 11.99 23.0525 12.055 23.7925 ; + RECT 12.1275 22.8975 12.2275 22.96 ; + RECT 11.75 24.1025 11.8025 24.165 ; + RECT 12.505 23.4925 12.575 23.6275 ; + RECT 11.745 22.8975 12.63 22.9625 ; + RECT 12.32 23.1625 12.385 23.7925 ; + RECT 12.505 23.4925 12.575 23.6275 ; + RECT 12.505 23.4925 12.575 23.6275 ; + RECT 11.885 23.7275 11.955 24.0175 ; + RECT 11.8 23.4925 11.87 23.6275 ; + RECT 11.745 24.1025 12.63 24.1675 ; + RECT 12.505 23.4925 12.575 23.6275 ; + RECT 12.505 22.9625 12.575 23.1625 ; + RECT 11.8 22.9625 11.87 23.1625 ; + RECT 12.425 23.7275 12.495 24.0175 ; + RECT 11.885 23.7275 12.055 23.7925 ; + RECT 12.505 25.3875 12.57 25.5225 ; + RECT 12.32 25.3875 12.385 25.5225 ; + RECT 11.805 25.3875 11.87 25.5225 ; + RECT 11.99 25.3875 12.055 25.5225 ; + RECT 12.32 24.9225 12.385 25.0575 ; + RECT 12.505 24.9225 12.57 25.0575 ; + RECT 11.99 24.9225 12.055 25.0575 ; + RECT 11.805 24.9225 11.87 25.0575 ; + RECT 12.425 24.5325 12.49 24.6675 ; + RECT 12.24 24.5325 12.305 24.6675 ; + RECT 12.07 24.5325 12.135 24.6675 ; + RECT 11.885 24.5325 11.95 24.6675 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.115 25.5875 12.25 25.6525 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 12.4725 24.2425 12.6075 24.3075 ; + RECT 12.1025 24.3825 12.2375 24.4475 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.0075 25.2725 12.1425 25.3375 ; + RECT 12.0075 25.2725 12.1425 25.3375 ; + RECT 12.2325 25.1225 12.3675 25.1875 ; + RECT 12.2325 25.1225 12.3675 25.1875 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.0575 24.5325 12.1225 24.6675 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 11.8025 25.025 11.8675 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.2525 24.5325 12.3175 24.6675 ; + RECT 12.12 24.2425 12.255 24.3075 ; + RECT 12.12 24.2425 12.255 24.3075 ; + RECT 12.4725 24.2425 12.6075 24.3075 ; + RECT 12.115 25.5875 12.25 25.6525 ; + RECT 12.4725 24.2425 12.6075 24.3075 ; + RECT 12.4725 24.2425 12.6075 24.3075 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 12.5075 25.025 12.5725 25.16 ; + RECT 11.7675 24.2425 11.9025 24.3075 ; + RECT 12.1275 25.5875 12.2275 25.65 ; + RECT 12.1275 25.59 12.2275 25.6525 ; + RECT 12.455 24.385 12.5075 24.4475 ; + RECT 12.1275 25.5875 12.2275 25.65 ; + RECT 12.505 25.3875 12.575 25.5875 ; + RECT 12.505 24.9225 12.575 25.0575 ; + RECT 12.505 24.9225 12.575 25.0575 ; + RECT 11.745 24.2425 12.63 24.3075 ; + RECT 12.32 24.7575 12.495 24.8225 ; + RECT 11.8 24.9225 11.87 25.0575 ; + RECT 11.99 24.7575 12.055 25.4975 ; + RECT 12.1275 25.59 12.2275 25.6525 ; + RECT 11.75 24.385 11.8025 24.4475 ; + RECT 12.505 24.9225 12.575 25.0575 ; + RECT 11.745 25.5875 12.63 25.6525 ; + RECT 12.32 24.7575 12.385 25.3875 ; + RECT 12.505 24.9225 12.575 25.0575 ; + RECT 12.505 24.9225 12.575 25.0575 ; + RECT 11.885 24.5325 11.955 24.8225 ; + RECT 11.8 24.9225 11.87 25.0575 ; + RECT 11.745 24.3825 12.63 24.4475 ; + RECT 12.505 24.9225 12.575 25.0575 ; + RECT 12.505 25.3875 12.575 25.5875 ; + RECT 11.8 25.3875 11.87 25.5875 ; + RECT 12.425 24.5325 12.495 24.8225 ; + RECT 11.885 24.7575 12.055 24.8225 ; + RECT 12.505 25.7175 12.57 25.8525 ; + RECT 12.32 25.7175 12.385 25.8525 ; + RECT 11.805 25.7175 11.87 25.8525 ; + RECT 11.99 25.7175 12.055 25.8525 ; + RECT 12.32 26.1825 12.385 26.3175 ; + RECT 12.505 26.1825 12.57 26.3175 ; + RECT 11.99 26.1825 12.055 26.3175 ; + RECT 11.805 26.1825 11.87 26.3175 ; + RECT 12.425 26.5725 12.49 26.7075 ; + RECT 12.24 26.5725 12.305 26.7075 ; + RECT 12.07 26.5725 12.135 26.7075 ; + RECT 11.885 26.5725 11.95 26.7075 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.115 25.5875 12.25 25.6525 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 12.4725 26.9325 12.6075 26.9975 ; + RECT 12.1025 26.7925 12.2375 26.8575 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.0075 25.9025 12.1425 25.9675 ; + RECT 12.0075 25.9025 12.1425 25.9675 ; + RECT 12.2325 26.0525 12.3675 26.1175 ; + RECT 12.2325 26.0525 12.3675 26.1175 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.0575 26.5725 12.1225 26.7075 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 11.8025 26.08 11.8675 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.2525 26.5725 12.3175 26.7075 ; + RECT 12.12 26.9325 12.255 26.9975 ; + RECT 12.12 26.9325 12.255 26.9975 ; + RECT 12.4725 26.9325 12.6075 26.9975 ; + RECT 12.115 25.5875 12.25 25.6525 ; + RECT 12.4725 26.9325 12.6075 26.9975 ; + RECT 12.4725 26.9325 12.6075 26.9975 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 12.5075 26.08 12.5725 26.215 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 12.1275 25.59 12.2275 25.6525 ; + RECT 12.1275 25.5875 12.2275 25.65 ; + RECT 12.455 26.7925 12.5075 26.855 ; + RECT 12.1275 25.59 12.2275 25.6525 ; + RECT 12.505 25.6525 12.575 25.8525 ; + RECT 12.505 26.1825 12.575 26.3175 ; + RECT 12.505 26.1825 12.575 26.3175 ; + RECT 11.745 26.9325 12.63 26.9975 ; + RECT 12.32 26.4175 12.495 26.4825 ; + RECT 11.8 26.1825 11.87 26.3175 ; + RECT 11.99 25.7425 12.055 26.4825 ; + RECT 12.1275 25.5875 12.2275 25.65 ; + RECT 11.75 26.7925 11.8025 26.855 ; + RECT 12.505 26.1825 12.575 26.3175 ; + RECT 11.745 25.5875 12.63 25.6525 ; + RECT 12.32 25.8525 12.385 26.4825 ; + RECT 12.505 26.1825 12.575 26.3175 ; + RECT 12.505 26.1825 12.575 26.3175 ; + RECT 11.885 26.4175 11.955 26.7075 ; + RECT 11.8 26.1825 11.87 26.3175 ; + RECT 11.745 26.7925 12.63 26.8575 ; + RECT 12.505 26.1825 12.575 26.3175 ; + RECT 12.505 25.6525 12.575 25.8525 ; + RECT 11.8 25.6525 11.87 25.8525 ; + RECT 12.425 26.4175 12.495 26.7075 ; + RECT 11.885 26.4175 12.055 26.4825 ; + RECT 12.505 28.0775 12.57 28.2125 ; + RECT 12.32 28.0775 12.385 28.2125 ; + RECT 11.805 28.0775 11.87 28.2125 ; + RECT 11.99 28.0775 12.055 28.2125 ; + RECT 12.32 27.6125 12.385 27.7475 ; + RECT 12.505 27.6125 12.57 27.7475 ; + RECT 11.99 27.6125 12.055 27.7475 ; + RECT 11.805 27.6125 11.87 27.7475 ; + RECT 12.425 27.2225 12.49 27.3575 ; + RECT 12.24 27.2225 12.305 27.3575 ; + RECT 12.07 27.2225 12.135 27.3575 ; + RECT 11.885 27.2225 11.95 27.3575 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.115 28.2775 12.25 28.3425 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 12.4725 26.9325 12.6075 26.9975 ; + RECT 12.1025 27.0725 12.2375 27.1375 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.0075 27.9625 12.1425 28.0275 ; + RECT 12.0075 27.9625 12.1425 28.0275 ; + RECT 12.2325 27.8125 12.3675 27.8775 ; + RECT 12.2325 27.8125 12.3675 27.8775 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.0575 27.2225 12.1225 27.3575 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 11.8025 27.715 11.8675 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.2525 27.2225 12.3175 27.3575 ; + RECT 12.12 26.9325 12.255 26.9975 ; + RECT 12.12 26.9325 12.255 26.9975 ; + RECT 12.4725 26.9325 12.6075 26.9975 ; + RECT 12.115 28.2775 12.25 28.3425 ; + RECT 12.4725 26.9325 12.6075 26.9975 ; + RECT 12.4725 26.9325 12.6075 26.9975 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 12.5075 27.715 12.5725 27.85 ; + RECT 11.7675 26.9325 11.9025 26.9975 ; + RECT 12.1275 28.2775 12.2275 28.34 ; + RECT 12.1275 28.28 12.2275 28.3425 ; + RECT 12.455 27.075 12.5075 27.1375 ; + RECT 12.1275 28.2775 12.2275 28.34 ; + RECT 12.505 28.0775 12.575 28.2775 ; + RECT 12.505 27.6125 12.575 27.7475 ; + RECT 12.505 27.6125 12.575 27.7475 ; + RECT 11.745 26.9325 12.63 26.9975 ; + RECT 12.32 27.4475 12.495 27.5125 ; + RECT 11.8 27.6125 11.87 27.7475 ; + RECT 11.99 27.4475 12.055 28.1875 ; + RECT 12.1275 28.28 12.2275 28.3425 ; + RECT 11.75 27.075 11.8025 27.1375 ; + RECT 12.505 27.6125 12.575 27.7475 ; + RECT 11.745 28.2775 12.63 28.3425 ; + RECT 12.32 27.4475 12.385 28.0775 ; + RECT 12.505 27.6125 12.575 27.7475 ; + RECT 12.505 27.6125 12.575 27.7475 ; + RECT 11.885 27.2225 11.955 27.5125 ; + RECT 11.8 27.6125 11.87 27.7475 ; + RECT 11.745 27.0725 12.63 27.1375 ; + RECT 12.505 27.6125 12.575 27.7475 ; + RECT 12.505 28.0775 12.575 28.2775 ; + RECT 11.8 28.0775 11.87 28.2775 ; + RECT 12.425 27.2225 12.495 27.5125 ; + RECT 11.885 27.4475 12.055 27.5125 ; + RECT 12.505 28.4075 12.57 28.5425 ; + RECT 12.32 28.4075 12.385 28.5425 ; + RECT 11.805 28.4075 11.87 28.5425 ; + RECT 11.99 28.4075 12.055 28.5425 ; + RECT 12.32 28.8725 12.385 29.0075 ; + RECT 12.505 28.8725 12.57 29.0075 ; + RECT 11.99 28.8725 12.055 29.0075 ; + RECT 11.805 28.8725 11.87 29.0075 ; + RECT 12.425 29.2625 12.49 29.3975 ; + RECT 12.24 29.2625 12.305 29.3975 ; + RECT 12.07 29.2625 12.135 29.3975 ; + RECT 11.885 29.2625 11.95 29.3975 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.115 28.2775 12.25 28.3425 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 12.4725 29.6225 12.6075 29.6875 ; + RECT 12.1025 29.4825 12.2375 29.5475 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.0075 28.5925 12.1425 28.6575 ; + RECT 12.0075 28.5925 12.1425 28.6575 ; + RECT 12.2325 28.7425 12.3675 28.8075 ; + RECT 12.2325 28.7425 12.3675 28.8075 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.0575 29.2625 12.1225 29.3975 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 11.8025 28.77 11.8675 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.2525 29.2625 12.3175 29.3975 ; + RECT 12.12 29.6225 12.255 29.6875 ; + RECT 12.12 29.6225 12.255 29.6875 ; + RECT 12.4725 29.6225 12.6075 29.6875 ; + RECT 12.115 28.2775 12.25 28.3425 ; + RECT 12.4725 29.6225 12.6075 29.6875 ; + RECT 12.4725 29.6225 12.6075 29.6875 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 12.5075 28.77 12.5725 28.905 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 12.1275 28.28 12.2275 28.3425 ; + RECT 12.1275 28.2775 12.2275 28.34 ; + RECT 12.455 29.4825 12.5075 29.545 ; + RECT 12.1275 28.28 12.2275 28.3425 ; + RECT 12.505 28.3425 12.575 28.5425 ; + RECT 12.505 28.8725 12.575 29.0075 ; + RECT 12.505 28.8725 12.575 29.0075 ; + RECT 11.745 29.6225 12.63 29.6875 ; + RECT 12.32 29.1075 12.495 29.1725 ; + RECT 11.8 28.8725 11.87 29.0075 ; + RECT 11.99 28.4325 12.055 29.1725 ; + RECT 12.1275 28.2775 12.2275 28.34 ; + RECT 11.75 29.4825 11.8025 29.545 ; + RECT 12.505 28.8725 12.575 29.0075 ; + RECT 11.745 28.2775 12.63 28.3425 ; + RECT 12.32 28.5425 12.385 29.1725 ; + RECT 12.505 28.8725 12.575 29.0075 ; + RECT 12.505 28.8725 12.575 29.0075 ; + RECT 11.885 29.1075 11.955 29.3975 ; + RECT 11.8 28.8725 11.87 29.0075 ; + RECT 11.745 29.4825 12.63 29.5475 ; + RECT 12.505 28.8725 12.575 29.0075 ; + RECT 12.505 28.3425 12.575 28.5425 ; + RECT 11.8 28.3425 11.87 28.5425 ; + RECT 12.425 29.1075 12.495 29.3975 ; + RECT 11.885 29.1075 12.055 29.1725 ; + RECT 12.505 30.7675 12.57 30.9025 ; + RECT 12.32 30.7675 12.385 30.9025 ; + RECT 11.805 30.7675 11.87 30.9025 ; + RECT 11.99 30.7675 12.055 30.9025 ; + RECT 12.32 30.3025 12.385 30.4375 ; + RECT 12.505 30.3025 12.57 30.4375 ; + RECT 11.99 30.3025 12.055 30.4375 ; + RECT 11.805 30.3025 11.87 30.4375 ; + RECT 12.425 29.9125 12.49 30.0475 ; + RECT 12.24 29.9125 12.305 30.0475 ; + RECT 12.07 29.9125 12.135 30.0475 ; + RECT 11.885 29.9125 11.95 30.0475 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.115 30.9675 12.25 31.0325 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 12.4725 29.6225 12.6075 29.6875 ; + RECT 12.1025 29.7625 12.2375 29.8275 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.0075 30.6525 12.1425 30.7175 ; + RECT 12.0075 30.6525 12.1425 30.7175 ; + RECT 12.2325 30.5025 12.3675 30.5675 ; + RECT 12.2325 30.5025 12.3675 30.5675 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.0575 29.9125 12.1225 30.0475 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 11.8025 30.405 11.8675 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.2525 29.9125 12.3175 30.0475 ; + RECT 12.12 29.6225 12.255 29.6875 ; + RECT 12.12 29.6225 12.255 29.6875 ; + RECT 12.4725 29.6225 12.6075 29.6875 ; + RECT 12.115 30.9675 12.25 31.0325 ; + RECT 12.4725 29.6225 12.6075 29.6875 ; + RECT 12.4725 29.6225 12.6075 29.6875 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 12.5075 30.405 12.5725 30.54 ; + RECT 11.7675 29.6225 11.9025 29.6875 ; + RECT 12.1275 30.9675 12.2275 31.03 ; + RECT 12.1275 30.97 12.2275 31.0325 ; + RECT 12.455 29.765 12.5075 29.8275 ; + RECT 12.1275 30.9675 12.2275 31.03 ; + RECT 12.505 30.7675 12.575 30.9675 ; + RECT 12.505 30.3025 12.575 30.4375 ; + RECT 12.505 30.3025 12.575 30.4375 ; + RECT 11.745 29.6225 12.63 29.6875 ; + RECT 12.32 30.1375 12.495 30.2025 ; + RECT 11.8 30.3025 11.87 30.4375 ; + RECT 11.99 30.1375 12.055 30.8775 ; + RECT 12.1275 30.97 12.2275 31.0325 ; + RECT 11.75 29.765 11.8025 29.8275 ; + RECT 12.505 30.3025 12.575 30.4375 ; + RECT 11.745 30.9675 12.63 31.0325 ; + RECT 12.32 30.1375 12.385 30.7675 ; + RECT 12.505 30.3025 12.575 30.4375 ; + RECT 12.505 30.3025 12.575 30.4375 ; + RECT 11.885 29.9125 11.955 30.2025 ; + RECT 11.8 30.3025 11.87 30.4375 ; + RECT 11.745 29.7625 12.63 29.8275 ; + RECT 12.505 30.3025 12.575 30.4375 ; + RECT 12.505 30.7675 12.575 30.9675 ; + RECT 11.8 30.7675 11.87 30.9675 ; + RECT 12.425 29.9125 12.495 30.2025 ; + RECT 11.885 30.1375 12.055 30.2025 ; + RECT 12.505 31.0975 12.57 31.2325 ; + RECT 12.32 31.0975 12.385 31.2325 ; + RECT 11.805 31.0975 11.87 31.2325 ; + RECT 11.99 31.0975 12.055 31.2325 ; + RECT 12.32 31.5625 12.385 31.6975 ; + RECT 12.505 31.5625 12.57 31.6975 ; + RECT 11.99 31.5625 12.055 31.6975 ; + RECT 11.805 31.5625 11.87 31.6975 ; + RECT 12.425 31.9525 12.49 32.0875 ; + RECT 12.24 31.9525 12.305 32.0875 ; + RECT 12.07 31.9525 12.135 32.0875 ; + RECT 11.885 31.9525 11.95 32.0875 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.115 30.9675 12.25 31.0325 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 12.4725 32.3125 12.6075 32.3775 ; + RECT 12.1025 32.1725 12.2375 32.2375 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.0075 31.2825 12.1425 31.3475 ; + RECT 12.0075 31.2825 12.1425 31.3475 ; + RECT 12.2325 31.4325 12.3675 31.4975 ; + RECT 12.2325 31.4325 12.3675 31.4975 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.0575 31.9525 12.1225 32.0875 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 11.8025 31.46 11.8675 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.2525 31.9525 12.3175 32.0875 ; + RECT 12.12 32.3125 12.255 32.3775 ; + RECT 12.12 32.3125 12.255 32.3775 ; + RECT 12.4725 32.3125 12.6075 32.3775 ; + RECT 12.115 30.9675 12.25 31.0325 ; + RECT 12.4725 32.3125 12.6075 32.3775 ; + RECT 12.4725 32.3125 12.6075 32.3775 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 12.5075 31.46 12.5725 31.595 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 12.1275 30.97 12.2275 31.0325 ; + RECT 12.1275 30.9675 12.2275 31.03 ; + RECT 12.455 32.1725 12.5075 32.235 ; + RECT 12.1275 30.97 12.2275 31.0325 ; + RECT 12.505 31.0325 12.575 31.2325 ; + RECT 12.505 31.5625 12.575 31.6975 ; + RECT 12.505 31.5625 12.575 31.6975 ; + RECT 11.745 32.3125 12.63 32.3775 ; + RECT 12.32 31.7975 12.495 31.8625 ; + RECT 11.8 31.5625 11.87 31.6975 ; + RECT 11.99 31.1225 12.055 31.8625 ; + RECT 12.1275 30.9675 12.2275 31.03 ; + RECT 11.75 32.1725 11.8025 32.235 ; + RECT 12.505 31.5625 12.575 31.6975 ; + RECT 11.745 30.9675 12.63 31.0325 ; + RECT 12.32 31.2325 12.385 31.8625 ; + RECT 12.505 31.5625 12.575 31.6975 ; + RECT 12.505 31.5625 12.575 31.6975 ; + RECT 11.885 31.7975 11.955 32.0875 ; + RECT 11.8 31.5625 11.87 31.6975 ; + RECT 11.745 32.1725 12.63 32.2375 ; + RECT 12.505 31.5625 12.575 31.6975 ; + RECT 12.505 31.0325 12.575 31.2325 ; + RECT 11.8 31.0325 11.87 31.2325 ; + RECT 12.425 31.7975 12.495 32.0875 ; + RECT 11.885 31.7975 12.055 31.8625 ; + RECT 12.505 33.4575 12.57 33.5925 ; + RECT 12.32 33.4575 12.385 33.5925 ; + RECT 11.805 33.4575 11.87 33.5925 ; + RECT 11.99 33.4575 12.055 33.5925 ; + RECT 12.32 32.9925 12.385 33.1275 ; + RECT 12.505 32.9925 12.57 33.1275 ; + RECT 11.99 32.9925 12.055 33.1275 ; + RECT 11.805 32.9925 11.87 33.1275 ; + RECT 12.425 32.6025 12.49 32.7375 ; + RECT 12.24 32.6025 12.305 32.7375 ; + RECT 12.07 32.6025 12.135 32.7375 ; + RECT 11.885 32.6025 11.95 32.7375 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.115 33.6575 12.25 33.7225 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 12.4725 32.3125 12.6075 32.3775 ; + RECT 12.1025 32.4525 12.2375 32.5175 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.0075 33.3425 12.1425 33.4075 ; + RECT 12.0075 33.3425 12.1425 33.4075 ; + RECT 12.2325 33.1925 12.3675 33.2575 ; + RECT 12.2325 33.1925 12.3675 33.2575 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.0575 32.6025 12.1225 32.7375 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 11.8025 33.095 11.8675 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.2525 32.6025 12.3175 32.7375 ; + RECT 12.12 32.3125 12.255 32.3775 ; + RECT 12.12 32.3125 12.255 32.3775 ; + RECT 12.4725 32.3125 12.6075 32.3775 ; + RECT 12.115 33.6575 12.25 33.7225 ; + RECT 12.4725 32.3125 12.6075 32.3775 ; + RECT 12.4725 32.3125 12.6075 32.3775 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 12.5075 33.095 12.5725 33.23 ; + RECT 11.7675 32.3125 11.9025 32.3775 ; + RECT 12.1275 33.6575 12.2275 33.72 ; + RECT 12.1275 33.66 12.2275 33.7225 ; + RECT 12.455 32.455 12.5075 32.5175 ; + RECT 12.1275 33.6575 12.2275 33.72 ; + RECT 12.505 33.4575 12.575 33.6575 ; + RECT 12.505 32.9925 12.575 33.1275 ; + RECT 12.505 32.9925 12.575 33.1275 ; + RECT 11.745 32.3125 12.63 32.3775 ; + RECT 12.32 32.8275 12.495 32.8925 ; + RECT 11.8 32.9925 11.87 33.1275 ; + RECT 11.99 32.8275 12.055 33.5675 ; + RECT 12.1275 33.66 12.2275 33.7225 ; + RECT 11.75 32.455 11.8025 32.5175 ; + RECT 12.505 32.9925 12.575 33.1275 ; + RECT 11.745 33.6575 12.63 33.7225 ; + RECT 12.32 32.8275 12.385 33.4575 ; + RECT 12.505 32.9925 12.575 33.1275 ; + RECT 12.505 32.9925 12.575 33.1275 ; + RECT 11.885 32.6025 11.955 32.8925 ; + RECT 11.8 32.9925 11.87 33.1275 ; + RECT 11.745 32.4525 12.63 32.5175 ; + RECT 12.505 32.9925 12.575 33.1275 ; + RECT 12.505 33.4575 12.575 33.6575 ; + RECT 11.8 33.4575 11.87 33.6575 ; + RECT 12.425 32.6025 12.495 32.8925 ; + RECT 11.885 32.8275 12.055 32.8925 ; + RECT 12.505 33.7875 12.57 33.9225 ; + RECT 12.32 33.7875 12.385 33.9225 ; + RECT 11.805 33.7875 11.87 33.9225 ; + RECT 11.99 33.7875 12.055 33.9225 ; + RECT 12.32 34.2525 12.385 34.3875 ; + RECT 12.505 34.2525 12.57 34.3875 ; + RECT 11.99 34.2525 12.055 34.3875 ; + RECT 11.805 34.2525 11.87 34.3875 ; + RECT 12.425 34.6425 12.49 34.7775 ; + RECT 12.24 34.6425 12.305 34.7775 ; + RECT 12.07 34.6425 12.135 34.7775 ; + RECT 11.885 34.6425 11.95 34.7775 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.115 33.6575 12.25 33.7225 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 12.4725 35.0025 12.6075 35.0675 ; + RECT 12.1025 34.8625 12.2375 34.9275 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.0075 33.9725 12.1425 34.0375 ; + RECT 12.0075 33.9725 12.1425 34.0375 ; + RECT 12.2325 34.1225 12.3675 34.1875 ; + RECT 12.2325 34.1225 12.3675 34.1875 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.0575 34.6425 12.1225 34.7775 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 11.8025 34.15 11.8675 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.2525 34.6425 12.3175 34.7775 ; + RECT 12.12 35.0025 12.255 35.0675 ; + RECT 12.12 35.0025 12.255 35.0675 ; + RECT 12.4725 35.0025 12.6075 35.0675 ; + RECT 12.115 33.6575 12.25 33.7225 ; + RECT 12.4725 35.0025 12.6075 35.0675 ; + RECT 12.4725 35.0025 12.6075 35.0675 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 12.5075 34.15 12.5725 34.285 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 12.1275 33.66 12.2275 33.7225 ; + RECT 12.1275 33.6575 12.2275 33.72 ; + RECT 12.455 34.8625 12.5075 34.925 ; + RECT 12.1275 33.66 12.2275 33.7225 ; + RECT 12.505 33.7225 12.575 33.9225 ; + RECT 12.505 34.2525 12.575 34.3875 ; + RECT 12.505 34.2525 12.575 34.3875 ; + RECT 11.745 35.0025 12.63 35.0675 ; + RECT 12.32 34.4875 12.495 34.5525 ; + RECT 11.8 34.2525 11.87 34.3875 ; + RECT 11.99 33.8125 12.055 34.5525 ; + RECT 12.1275 33.6575 12.2275 33.72 ; + RECT 11.75 34.8625 11.8025 34.925 ; + RECT 12.505 34.2525 12.575 34.3875 ; + RECT 11.745 33.6575 12.63 33.7225 ; + RECT 12.32 33.9225 12.385 34.5525 ; + RECT 12.505 34.2525 12.575 34.3875 ; + RECT 12.505 34.2525 12.575 34.3875 ; + RECT 11.885 34.4875 11.955 34.7775 ; + RECT 11.8 34.2525 11.87 34.3875 ; + RECT 11.745 34.8625 12.63 34.9275 ; + RECT 12.505 34.2525 12.575 34.3875 ; + RECT 12.505 33.7225 12.575 33.9225 ; + RECT 11.8 33.7225 11.87 33.9225 ; + RECT 12.425 34.4875 12.495 34.7775 ; + RECT 11.885 34.4875 12.055 34.5525 ; + RECT 12.505 36.1475 12.57 36.2825 ; + RECT 12.32 36.1475 12.385 36.2825 ; + RECT 11.805 36.1475 11.87 36.2825 ; + RECT 11.99 36.1475 12.055 36.2825 ; + RECT 12.32 35.6825 12.385 35.8175 ; + RECT 12.505 35.6825 12.57 35.8175 ; + RECT 11.99 35.6825 12.055 35.8175 ; + RECT 11.805 35.6825 11.87 35.8175 ; + RECT 12.425 35.2925 12.49 35.4275 ; + RECT 12.24 35.2925 12.305 35.4275 ; + RECT 12.07 35.2925 12.135 35.4275 ; + RECT 11.885 35.2925 11.95 35.4275 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.115 36.3475 12.25 36.4125 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 12.4725 35.0025 12.6075 35.0675 ; + RECT 12.1025 35.1425 12.2375 35.2075 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.0075 36.0325 12.1425 36.0975 ; + RECT 12.0075 36.0325 12.1425 36.0975 ; + RECT 12.2325 35.8825 12.3675 35.9475 ; + RECT 12.2325 35.8825 12.3675 35.9475 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.0575 35.2925 12.1225 35.4275 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 11.8025 35.785 11.8675 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.2525 35.2925 12.3175 35.4275 ; + RECT 12.12 35.0025 12.255 35.0675 ; + RECT 12.12 35.0025 12.255 35.0675 ; + RECT 12.4725 35.0025 12.6075 35.0675 ; + RECT 12.115 36.3475 12.25 36.4125 ; + RECT 12.4725 35.0025 12.6075 35.0675 ; + RECT 12.4725 35.0025 12.6075 35.0675 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 12.5075 35.785 12.5725 35.92 ; + RECT 11.7675 35.0025 11.9025 35.0675 ; + RECT 12.1275 36.3475 12.2275 36.41 ; + RECT 12.1275 36.35 12.2275 36.4125 ; + RECT 12.455 35.145 12.5075 35.2075 ; + RECT 12.1275 36.3475 12.2275 36.41 ; + RECT 12.505 36.1475 12.575 36.3475 ; + RECT 12.505 35.6825 12.575 35.8175 ; + RECT 12.505 35.6825 12.575 35.8175 ; + RECT 11.745 35.0025 12.63 35.0675 ; + RECT 12.32 35.5175 12.495 35.5825 ; + RECT 11.8 35.6825 11.87 35.8175 ; + RECT 11.99 35.5175 12.055 36.2575 ; + RECT 12.1275 36.35 12.2275 36.4125 ; + RECT 11.75 35.145 11.8025 35.2075 ; + RECT 12.505 35.6825 12.575 35.8175 ; + RECT 11.745 36.3475 12.63 36.4125 ; + RECT 12.32 35.5175 12.385 36.1475 ; + RECT 12.505 35.6825 12.575 35.8175 ; + RECT 12.505 35.6825 12.575 35.8175 ; + RECT 11.885 35.2925 11.955 35.5825 ; + RECT 11.8 35.6825 11.87 35.8175 ; + RECT 11.745 35.1425 12.63 35.2075 ; + RECT 12.505 35.6825 12.575 35.8175 ; + RECT 12.505 36.1475 12.575 36.3475 ; + RECT 11.8 36.1475 11.87 36.3475 ; + RECT 12.425 35.2925 12.495 35.5825 ; + RECT 11.885 35.5175 12.055 35.5825 ; + RECT 12.505 36.4775 12.57 36.6125 ; + RECT 12.32 36.4775 12.385 36.6125 ; + RECT 11.805 36.4775 11.87 36.6125 ; + RECT 11.99 36.4775 12.055 36.6125 ; + RECT 12.32 36.9425 12.385 37.0775 ; + RECT 12.505 36.9425 12.57 37.0775 ; + RECT 11.99 36.9425 12.055 37.0775 ; + RECT 11.805 36.9425 11.87 37.0775 ; + RECT 12.425 37.3325 12.49 37.4675 ; + RECT 12.24 37.3325 12.305 37.4675 ; + RECT 12.07 37.3325 12.135 37.4675 ; + RECT 11.885 37.3325 11.95 37.4675 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.115 36.3475 12.25 36.4125 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 12.4725 37.6925 12.6075 37.7575 ; + RECT 12.1025 37.5525 12.2375 37.6175 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.0075 36.6625 12.1425 36.7275 ; + RECT 12.0075 36.6625 12.1425 36.7275 ; + RECT 12.2325 36.8125 12.3675 36.8775 ; + RECT 12.2325 36.8125 12.3675 36.8775 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.0575 37.3325 12.1225 37.4675 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 11.8025 36.84 11.8675 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.2525 37.3325 12.3175 37.4675 ; + RECT 12.12 37.6925 12.255 37.7575 ; + RECT 12.12 37.6925 12.255 37.7575 ; + RECT 12.4725 37.6925 12.6075 37.7575 ; + RECT 12.115 36.3475 12.25 36.4125 ; + RECT 12.4725 37.6925 12.6075 37.7575 ; + RECT 12.4725 37.6925 12.6075 37.7575 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 12.5075 36.84 12.5725 36.975 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 12.1275 36.35 12.2275 36.4125 ; + RECT 12.1275 36.3475 12.2275 36.41 ; + RECT 12.455 37.5525 12.5075 37.615 ; + RECT 12.1275 36.35 12.2275 36.4125 ; + RECT 12.505 36.4125 12.575 36.6125 ; + RECT 12.505 36.9425 12.575 37.0775 ; + RECT 12.505 36.9425 12.575 37.0775 ; + RECT 11.745 37.6925 12.63 37.7575 ; + RECT 12.32 37.1775 12.495 37.2425 ; + RECT 11.8 36.9425 11.87 37.0775 ; + RECT 11.99 36.5025 12.055 37.2425 ; + RECT 12.1275 36.3475 12.2275 36.41 ; + RECT 11.75 37.5525 11.8025 37.615 ; + RECT 12.505 36.9425 12.575 37.0775 ; + RECT 11.745 36.3475 12.63 36.4125 ; + RECT 12.32 36.6125 12.385 37.2425 ; + RECT 12.505 36.9425 12.575 37.0775 ; + RECT 12.505 36.9425 12.575 37.0775 ; + RECT 11.885 37.1775 11.955 37.4675 ; + RECT 11.8 36.9425 11.87 37.0775 ; + RECT 11.745 37.5525 12.63 37.6175 ; + RECT 12.505 36.9425 12.575 37.0775 ; + RECT 12.505 36.4125 12.575 36.6125 ; + RECT 11.8 36.4125 11.87 36.6125 ; + RECT 12.425 37.1775 12.495 37.4675 ; + RECT 11.885 37.1775 12.055 37.2425 ; + RECT 12.505 38.8375 12.57 38.9725 ; + RECT 12.32 38.8375 12.385 38.9725 ; + RECT 11.805 38.8375 11.87 38.9725 ; + RECT 11.99 38.8375 12.055 38.9725 ; + RECT 12.32 38.3725 12.385 38.5075 ; + RECT 12.505 38.3725 12.57 38.5075 ; + RECT 11.99 38.3725 12.055 38.5075 ; + RECT 11.805 38.3725 11.87 38.5075 ; + RECT 12.425 37.9825 12.49 38.1175 ; + RECT 12.24 37.9825 12.305 38.1175 ; + RECT 12.07 37.9825 12.135 38.1175 ; + RECT 11.885 37.9825 11.95 38.1175 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.115 39.0375 12.25 39.1025 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 12.4725 37.6925 12.6075 37.7575 ; + RECT 12.1025 37.8325 12.2375 37.8975 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.0075 38.7225 12.1425 38.7875 ; + RECT 12.0075 38.7225 12.1425 38.7875 ; + RECT 12.2325 38.5725 12.3675 38.6375 ; + RECT 12.2325 38.5725 12.3675 38.6375 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.0575 37.9825 12.1225 38.1175 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 11.8025 38.475 11.8675 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.2525 37.9825 12.3175 38.1175 ; + RECT 12.12 37.6925 12.255 37.7575 ; + RECT 12.12 37.6925 12.255 37.7575 ; + RECT 12.4725 37.6925 12.6075 37.7575 ; + RECT 12.115 39.0375 12.25 39.1025 ; + RECT 12.4725 37.6925 12.6075 37.7575 ; + RECT 12.4725 37.6925 12.6075 37.7575 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 12.5075 38.475 12.5725 38.61 ; + RECT 11.7675 37.6925 11.9025 37.7575 ; + RECT 12.1275 39.0375 12.2275 39.1 ; + RECT 12.1275 39.04 12.2275 39.1025 ; + RECT 12.455 37.835 12.5075 37.8975 ; + RECT 12.1275 39.0375 12.2275 39.1 ; + RECT 12.505 38.8375 12.575 39.0375 ; + RECT 12.505 38.3725 12.575 38.5075 ; + RECT 12.505 38.3725 12.575 38.5075 ; + RECT 11.745 37.6925 12.63 37.7575 ; + RECT 12.32 38.2075 12.495 38.2725 ; + RECT 11.8 38.3725 11.87 38.5075 ; + RECT 11.99 38.2075 12.055 38.9475 ; + RECT 12.1275 39.04 12.2275 39.1025 ; + RECT 11.75 37.835 11.8025 37.8975 ; + RECT 12.505 38.3725 12.575 38.5075 ; + RECT 11.745 39.0375 12.63 39.1025 ; + RECT 12.32 38.2075 12.385 38.8375 ; + RECT 12.505 38.3725 12.575 38.5075 ; + RECT 12.505 38.3725 12.575 38.5075 ; + RECT 11.885 37.9825 11.955 38.2725 ; + RECT 11.8 38.3725 11.87 38.5075 ; + RECT 11.745 37.8325 12.63 37.8975 ; + RECT 12.505 38.3725 12.575 38.5075 ; + RECT 12.505 38.8375 12.575 39.0375 ; + RECT 11.8 38.8375 11.87 39.0375 ; + RECT 12.425 37.9825 12.495 38.2725 ; + RECT 11.885 38.2075 12.055 38.2725 ; + RECT 12.505 39.1675 12.57 39.3025 ; + RECT 12.32 39.1675 12.385 39.3025 ; + RECT 11.805 39.1675 11.87 39.3025 ; + RECT 11.99 39.1675 12.055 39.3025 ; + RECT 12.32 39.6325 12.385 39.7675 ; + RECT 12.505 39.6325 12.57 39.7675 ; + RECT 11.99 39.6325 12.055 39.7675 ; + RECT 11.805 39.6325 11.87 39.7675 ; + RECT 12.425 40.0225 12.49 40.1575 ; + RECT 12.24 40.0225 12.305 40.1575 ; + RECT 12.07 40.0225 12.135 40.1575 ; + RECT 11.885 40.0225 11.95 40.1575 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.115 39.0375 12.25 39.1025 ; + RECT 11.7675 40.3825 11.9025 40.4475 ; + RECT 12.4725 40.3825 12.6075 40.4475 ; + RECT 12.1025 40.2425 12.2375 40.3075 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.0075 39.3525 12.1425 39.4175 ; + RECT 12.0075 39.3525 12.1425 39.4175 ; + RECT 12.2325 39.5025 12.3675 39.5675 ; + RECT 12.2325 39.5025 12.3675 39.5675 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.0575 40.0225 12.1225 40.1575 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 11.8025 39.53 11.8675 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.2525 40.0225 12.3175 40.1575 ; + RECT 12.12 40.3825 12.255 40.4475 ; + RECT 12.12 40.3825 12.255 40.4475 ; + RECT 12.4725 40.3825 12.6075 40.4475 ; + RECT 12.115 39.0375 12.25 39.1025 ; + RECT 12.4725 40.3825 12.6075 40.4475 ; + RECT 12.4725 40.3825 12.6075 40.4475 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 12.5075 39.53 12.5725 39.665 ; + RECT 11.7675 40.3825 11.9025 40.4475 ; + RECT 12.1275 39.04 12.2275 39.1025 ; + RECT 12.1275 39.0375 12.2275 39.1 ; + RECT 12.455 40.2425 12.5075 40.305 ; + RECT 12.1275 39.04 12.2275 39.1025 ; + RECT 12.505 39.1025 12.575 39.3025 ; + RECT 12.505 39.6325 12.575 39.7675 ; + RECT 12.505 39.6325 12.575 39.7675 ; + RECT 11.745 40.3825 12.63 40.4475 ; + RECT 12.32 39.8675 12.495 39.9325 ; + RECT 11.8 39.6325 11.87 39.7675 ; + RECT 11.99 39.1925 12.055 39.9325 ; + RECT 12.1275 39.0375 12.2275 39.1 ; + RECT 11.75 40.2425 11.8025 40.305 ; + RECT 12.505 39.6325 12.575 39.7675 ; + RECT 11.745 39.0375 12.63 39.1025 ; + RECT 12.32 39.3025 12.385 39.9325 ; + RECT 12.505 39.6325 12.575 39.7675 ; + RECT 12.505 39.6325 12.575 39.7675 ; + RECT 11.885 39.8675 11.955 40.1575 ; + RECT 11.8 39.6325 11.87 39.7675 ; + RECT 11.745 40.2425 12.63 40.3075 ; + RECT 12.505 39.6325 12.575 39.7675 ; + RECT 12.505 39.1025 12.575 39.3025 ; + RECT 11.8 39.1025 11.87 39.3025 ; + RECT 12.425 39.8675 12.495 40.1575 ; + RECT 11.885 39.8675 12.055 39.9325 ; + RECT 11.13 41.66 12.54 41.725 ; + RECT 11.13 41.1 12.54 41.165 ; + RECT 11.13 41.1 11.835 41.165 ; + RECT 11.13 41.66 11.835 41.725 ; + RECT 11.515 41.3125 11.58 41.725 ; + RECT 11.325 40.8625 11.39 40.9975 ; + RECT 11.515 40.8625 11.58 40.9975 ; + RECT 11.325 41.3125 11.39 41.4475 ; + RECT 11.515 41.3125 11.58 41.4475 ; + RECT 11.515 41.3125 11.58 41.4475 ; + RECT 11.705 41.3125 11.77 41.4475 ; + RECT 11.36 41.1 11.495 41.165 ; + RECT 11.515 41.5575 11.58 41.6925 ; + RECT 11.325 41.3125 11.39 41.4475 ; + RECT 11.705 41.3125 11.77 41.4475 ; + RECT 11.325 40.8625 11.39 40.9975 ; + RECT 11.515 40.8625 11.58 40.9975 ; + RECT 11.835 41.1 12.54 41.165 ; + RECT 11.835 41.66 12.54 41.725 ; + RECT 12.22 41.3125 12.285 41.725 ; + RECT 12.03 40.8625 12.095 40.9975 ; + RECT 12.22 40.8625 12.285 40.9975 ; + RECT 12.03 41.3125 12.095 41.4475 ; + RECT 12.22 41.3125 12.285 41.4475 ; + RECT 12.22 41.3125 12.285 41.4475 ; + RECT 12.41 41.3125 12.475 41.4475 ; + RECT 12.065 41.1 12.2 41.165 ; + RECT 12.22 41.5575 12.285 41.6925 ; + RECT 12.03 41.3125 12.095 41.4475 ; + RECT 12.41 41.3125 12.475 41.4475 ; + RECT 12.03 40.8625 12.095 40.9975 ; + RECT 12.22 40.8625 12.285 40.9975 ; + RECT 11.13 18.7 12.54 18.765 ; + RECT 11.13 14.2575 12.54 14.3225 ; + RECT 11.13 14.1275 12.54 14.1925 ; + RECT 11.6375 14.9225 11.7025 15.1975 ; + RECT 11.4525 14.9225 11.5175 15.1975 ; + RECT 11.4475 14.9225 11.5125 15.1975 ; + RECT 11.2625 14.9225 11.3275 15.1975 ; + RECT 11.5475 14.465 11.6125 14.74 ; + RECT 11.3625 14.465 11.4275 14.74 ; + RECT 11.4475 15.6675 11.5125 16.2225 ; + RECT 11.2625 15.6675 11.3275 16.2225 ; + RECT 11.6375 15.6675 11.7025 16.2225 ; + RECT 11.4525 15.6675 11.5175 16.2225 ; + RECT 11.4475 17.45 11.5125 18.145 ; + RECT 11.2625 17.45 11.3275 18.145 ; + RECT 11.6375 16.48 11.7025 17.175 ; + RECT 11.4525 16.48 11.5175 17.175 ; + RECT 11.445 18.4975 11.51 18.6325 ; + RECT 11.42 14.1275 11.555 14.1925 ; + RECT 11.8025 14.2575 11.8675 14.3925 ; + RECT 11.0975 14.2575 11.1625 14.3925 ; + RECT 11.64 17.04 11.705 17.175 ; + RECT 11.7225 14.4875 11.7875 14.6225 ; + RECT 11.2575 17.45 11.3225 17.585 ; + RECT 11.415 18.3625 11.55 18.4275 ; + RECT 11.45 18.2925 11.515 18.4275 ; + RECT 11.45 16.15 11.515 16.285 ; + RECT 11.3925 15.4625 11.4575 15.5975 ; + RECT 11.5075 15.2625 11.5725 15.3975 ; + RECT 11.4 14.2575 11.46 14.3225 ; + RECT 11.36 14.1275 11.425 14.1925 ; + RECT 11.7875 14.3225 11.8025 14.3925 ; + RECT 11.1925 18.4275 11.26 18.765 ; + RECT 11.1925 18.3625 11.55 18.4275 ; + RECT 11.695 17.32 11.76 18.5675 ; + RECT 11.1925 18.7 11.26 18.765 ; + RECT 11.5475 14.3225 11.6125 14.475 ; + RECT 11.7225 14.2575 11.7875 14.4875 ; + RECT 11.4425 18.4975 11.76 18.5675 ; + RECT 11.45 17.32 11.515 17.5175 ; + RECT 11.2625 17.32 11.76 17.385 ; + RECT 11.4525 16.35 11.5175 16.545 ; + RECT 11.4525 16.35 11.7025 16.415 ; + RECT 11.4125 14.6525 11.4825 14.98 ; + RECT 11.095 14.2575 11.87 14.3225 ; + RECT 11.6375 16.22 11.7025 16.415 ; + RECT 11.2625 16.2225 11.3275 17.32 ; + RECT 11.095 18.7 11.87 18.765 ; + RECT 11.4125 15.4975 11.7025 15.5625 ; + RECT 11.2625 15.2975 11.5525 15.3625 ; + RECT 11.2625 15.1925 11.3275 16.06 ; + RECT 11.6375 15.1975 11.7025 16.06 ; + RECT 11.095 14.1275 11.87 14.1925 ; + RECT 12.3425 14.9225 12.4075 15.1975 ; + RECT 12.1575 14.9225 12.2225 15.1975 ; + RECT 12.1525 14.9225 12.2175 15.1975 ; + RECT 11.9675 14.9225 12.0325 15.1975 ; + RECT 12.2525 14.465 12.3175 14.74 ; + RECT 12.0675 14.465 12.1325 14.74 ; + RECT 12.1525 15.6675 12.2175 16.2225 ; + RECT 11.9675 15.6675 12.0325 16.2225 ; + RECT 12.3425 15.6675 12.4075 16.2225 ; + RECT 12.1575 15.6675 12.2225 16.2225 ; + RECT 12.1525 17.45 12.2175 18.145 ; + RECT 11.9675 17.45 12.0325 18.145 ; + RECT 12.3425 16.48 12.4075 17.175 ; + RECT 12.1575 16.48 12.2225 17.175 ; + RECT 12.15 18.4975 12.215 18.6325 ; + RECT 12.125 14.1275 12.26 14.1925 ; + RECT 12.5075 14.2575 12.5725 14.3925 ; + RECT 11.8025 14.2575 11.8675 14.3925 ; + RECT 12.345 17.04 12.41 17.175 ; + RECT 12.4275 14.4875 12.4925 14.6225 ; + RECT 11.9625 17.45 12.0275 17.585 ; + RECT 12.12 18.3625 12.255 18.4275 ; + RECT 12.155 18.2925 12.22 18.4275 ; + RECT 12.155 16.15 12.22 16.285 ; + RECT 12.0975 15.4625 12.1625 15.5975 ; + RECT 12.2125 15.2625 12.2775 15.3975 ; + RECT 12.105 14.2575 12.165 14.3225 ; + RECT 12.065 14.1275 12.13 14.1925 ; + RECT 12.4925 14.3225 12.5075 14.3925 ; + RECT 11.8975 18.4275 11.965 18.765 ; + RECT 11.8975 18.3625 12.255 18.4275 ; + RECT 12.4 17.32 12.465 18.5675 ; + RECT 11.8975 18.7 11.965 18.765 ; + RECT 12.2525 14.3225 12.3175 14.475 ; + RECT 12.4275 14.2575 12.4925 14.4875 ; + RECT 12.1475 18.4975 12.465 18.5675 ; + RECT 12.155 17.32 12.22 17.5175 ; + RECT 11.9675 17.32 12.465 17.385 ; + RECT 12.1575 16.35 12.2225 16.545 ; + RECT 12.1575 16.35 12.4075 16.415 ; + RECT 12.1175 14.6525 12.1875 14.98 ; + RECT 11.8 14.2575 12.575 14.3225 ; + RECT 12.3425 16.22 12.4075 16.415 ; + RECT 11.9675 16.2225 12.0325 17.32 ; + RECT 11.8 18.7 12.575 18.765 ; + RECT 12.1175 15.4975 12.4075 15.5625 ; + RECT 11.9675 15.2975 12.2575 15.3625 ; + RECT 11.9675 15.1925 12.0325 16.06 ; + RECT 12.3425 15.1975 12.4075 16.06 ; + RECT 11.8 14.1275 12.575 14.1925 ; + RECT 11.13 10.1025 12.54 10.1675 ; + RECT 11.13 10.2325 12.54 10.2975 ; + RECT 11.13 11.035 12.54 11.1 ; + RECT 11.55 13.4975 11.615 13.6325 ; + RECT 11.175 13.4975 11.24 13.6325 ; + RECT 11.175 11.57 11.24 11.705 ; + RECT 11.55 11.57 11.615 11.705 ; + RECT 11.55 12.7025 11.615 13.1175 ; + RECT 11.175 12.7025 11.24 13.1175 ; + RECT 11.175 12.085 11.24 12.5 ; + RECT 11.55 12.085 11.615 12.5 ; + RECT 11.36 10.515 11.425 10.93 ; + RECT 11.175 10.515 11.24 10.93 ; + RECT 11.515 10.515 11.58 10.93 ; + RECT 11.7 10.515 11.765 10.93 ; + RECT 11.36 11.205 11.425 11.34 ; + RECT 11.175 11.205 11.24 11.34 ; + RECT 11.515 11.205 11.58 11.34 ; + RECT 11.7 11.205 11.765 11.34 ; + RECT 11.3375 9.97 11.4725 10.035 ; + RECT 11.4125 11.925 11.4775 12.06 ; + RECT 11.8025 11.57 11.8675 11.705 ; + RECT 11.715 11.57 11.78 11.705 ; + RECT 11.0975 12.5325 11.1625 12.6675 ; + RECT 11.8025 11.0 11.8675 11.135 ; + RECT 11.0975 11.0 11.1625 11.135 ; + RECT 11.5175 11.205 11.5825 11.34 ; + RECT 11.8025 13.4975 11.8675 13.6325 ; + RECT 11.7175 13.4975 11.7825 13.6325 ; + RECT 11.5175 10.795 11.5825 10.93 ; + RECT 11.3775 10.795 11.4425 10.93 ; + RECT 11.4 10.3825 11.535 10.4475 ; + RECT 11.2325 9.9725 11.3675 10.0375 ; + RECT 11.5725 10.1025 11.7075 10.1675 ; + RECT 11.8025 11.205 11.8675 11.34 ; + RECT 11.0975 11.205 11.1625 11.34 ; + RECT 11.235 10.2325 11.37 10.2975 ; + RECT 11.3775 13.175 11.4425 13.31 ; + RECT 11.5475 11.57 11.6125 11.705 ; + RECT 11.2375 12.485 11.3025 12.62 ; + RECT 11.3775 11.205 11.4425 11.34 ; + RECT 11.315 13.175 11.38 13.31 ; + RECT 11.0975 11.57 11.1625 11.705 ; + RECT 11.5475 12.085 11.6125 12.22 ; + RECT 11.2825 13.7075 11.3475 13.8425 ; + RECT 11.0975 13.4975 11.1625 13.6325 ; + RECT 11.7825 13.4975 11.8025 13.6325 ; + RECT 11.78 11.57 11.8025 11.705 ; + RECT 11.45 11.0375 11.5075 11.0975 ; + RECT 11.425 10.1025 11.4975 10.1675 ; + RECT 11.4275 10.2325 11.4975 10.2975 ; + RECT 11.1575 13.4975 11.17 13.6325 ; + RECT 11.1625 12.5325 11.305 12.6675 ; + RECT 11.095 11.035 11.87 11.1 ; + RECT 11.4325 10.2975 11.4975 10.4475 ; + RECT 11.76 11.205 11.8025 11.34 ; + RECT 11.7 10.2975 11.765 10.515 ; + RECT 11.175 10.2975 11.24 10.515 ; + RECT 11.095 10.2325 11.87 10.2975 ; + RECT 11.095 10.1025 11.87 10.1675 ; + RECT 11.1575 11.205 11.17 11.34 ; + RECT 11.225 12.485 11.24 12.62 ; + RECT 11.1575 11.57 11.17 11.705 ; + RECT 11.175 12.5 11.24 12.7025 ; + RECT 11.55 13.6325 11.615 13.7075 ; + RECT 11.4125 11.4125 11.575 11.4775 ; + RECT 11.165 13.4975 11.175 13.6325 ; + RECT 11.165 11.57 11.175 11.705 ; + RECT 11.51 11.2875 11.575 11.4125 ; + RECT 11.4125 11.4125 11.4775 12.01 ; + RECT 11.55 13.1175 11.615 13.4975 ; + RECT 11.28 13.7075 11.615 13.7725 ; + RECT 11.165 11.205 11.175 11.34 ; + RECT 12.255 13.4975 12.32 13.6325 ; + RECT 11.88 13.4975 11.945 13.6325 ; + RECT 11.88 11.57 11.945 11.705 ; + RECT 12.255 11.57 12.32 11.705 ; + RECT 12.255 12.7025 12.32 13.1175 ; + RECT 11.88 12.7025 11.945 13.1175 ; + RECT 11.88 12.085 11.945 12.5 ; + RECT 12.255 12.085 12.32 12.5 ; + RECT 12.065 10.515 12.13 10.93 ; + RECT 11.88 10.515 11.945 10.93 ; + RECT 12.22 10.515 12.285 10.93 ; + RECT 12.405 10.515 12.47 10.93 ; + RECT 12.065 11.205 12.13 11.34 ; + RECT 11.88 11.205 11.945 11.34 ; + RECT 12.22 11.205 12.285 11.34 ; + RECT 12.405 11.205 12.47 11.34 ; + RECT 12.0425 9.97 12.1775 10.035 ; + RECT 12.1175 11.925 12.1825 12.06 ; + RECT 12.5075 11.57 12.5725 11.705 ; + RECT 12.42 11.57 12.485 11.705 ; + RECT 11.8025 12.5325 11.8675 12.6675 ; + RECT 12.5075 11.0 12.5725 11.135 ; + RECT 11.8025 11.0 11.8675 11.135 ; + RECT 12.2225 11.205 12.2875 11.34 ; + RECT 12.5075 13.4975 12.5725 13.6325 ; + RECT 12.4225 13.4975 12.4875 13.6325 ; + RECT 12.2225 10.795 12.2875 10.93 ; + RECT 12.0825 10.795 12.1475 10.93 ; + RECT 12.105 10.3825 12.24 10.4475 ; + RECT 11.9375 9.9725 12.0725 10.0375 ; + RECT 12.2775 10.1025 12.4125 10.1675 ; + RECT 12.5075 11.205 12.5725 11.34 ; + RECT 11.8025 11.205 11.8675 11.34 ; + RECT 11.94 10.2325 12.075 10.2975 ; + RECT 12.0825 13.175 12.1475 13.31 ; + RECT 12.2525 11.57 12.3175 11.705 ; + RECT 11.9425 12.485 12.0075 12.62 ; + RECT 12.0825 11.205 12.1475 11.34 ; + RECT 12.02 13.175 12.085 13.31 ; + RECT 11.8025 11.57 11.8675 11.705 ; + RECT 12.2525 12.085 12.3175 12.22 ; + RECT 11.9875 13.7075 12.0525 13.8425 ; + RECT 11.8025 13.4975 11.8675 13.6325 ; + RECT 12.4875 13.4975 12.5075 13.6325 ; + RECT 12.485 11.57 12.5075 11.705 ; + RECT 12.155 11.0375 12.2125 11.0975 ; + RECT 12.13 10.1025 12.2025 10.1675 ; + RECT 12.1325 10.2325 12.2025 10.2975 ; + RECT 11.8625 13.4975 11.875 13.6325 ; + RECT 11.8675 12.5325 12.01 12.6675 ; + RECT 11.8 11.035 12.575 11.1 ; + RECT 12.1375 10.2975 12.2025 10.4475 ; + RECT 12.465 11.205 12.5075 11.34 ; + RECT 12.405 10.2975 12.47 10.515 ; + RECT 11.88 10.2975 11.945 10.515 ; + RECT 11.8 10.2325 12.575 10.2975 ; + RECT 11.8 10.1025 12.575 10.1675 ; + RECT 11.8625 11.205 11.875 11.34 ; + RECT 11.93 12.485 11.945 12.62 ; + RECT 11.8625 11.57 11.875 11.705 ; + RECT 11.88 12.5 11.945 12.7025 ; + RECT 12.255 13.6325 12.32 13.7075 ; + RECT 12.1175 11.4125 12.28 11.4775 ; + RECT 11.87 13.4975 11.88 13.6325 ; + RECT 11.87 11.57 11.88 11.705 ; + RECT 12.215 11.2875 12.28 11.4125 ; + RECT 12.1175 11.4125 12.1825 12.01 ; + RECT 12.255 13.1175 12.32 13.4975 ; + RECT 11.985 13.7075 12.32 13.7725 ; + RECT 11.87 11.205 11.88 11.34 ; + RECT 11.13 3.5675 12.54 3.6325 ; + RECT 11.13 9.565 12.54 9.63 ; + RECT 11.67 6.085 11.735 6.22 ; + RECT 11.485 6.085 11.55 6.22 ; + RECT 11.67 4.85 11.735 4.985 ; + RECT 11.485 4.85 11.55 4.985 ; + RECT 11.48 6.085 11.545 6.22 ; + RECT 11.295 6.085 11.36 6.22 ; + RECT 11.48 9.045 11.545 9.18 ; + RECT 11.295 9.045 11.36 9.18 ; + RECT 11.48 4.85 11.545 4.985 ; + RECT 11.295 4.85 11.36 4.985 ; + RECT 11.67 9.045 11.735 9.18 ; + RECT 11.485 9.045 11.55 9.18 ; + RECT 11.48 4.315 11.545 4.45 ; + RECT 11.295 4.315 11.36 4.45 ; + RECT 11.62 7.275 11.685 7.41 ; + RECT 11.435 7.275 11.5 7.41 ; + RECT 11.67 7.81 11.735 7.945 ; + RECT 11.485 7.81 11.55 7.945 ; + RECT 11.48 7.81 11.545 7.945 ; + RECT 11.295 7.81 11.36 7.945 ; + RECT 11.67 8.235 11.735 8.37 ; + RECT 11.485 8.235 11.55 8.37 ; + RECT 11.67 5.275 11.735 5.41 ; + RECT 11.485 5.275 11.55 5.41 ; + RECT 11.67 8.62 11.735 8.755 ; + RECT 11.485 8.62 11.55 8.755 ; + RECT 11.48 8.62 11.545 8.755 ; + RECT 11.295 8.62 11.36 8.755 ; + RECT 11.48 5.275 11.545 5.41 ; + RECT 11.295 5.275 11.36 5.41 ; + RECT 11.48 8.235 11.545 8.37 ; + RECT 11.295 8.235 11.36 8.37 ; + RECT 11.48 5.66 11.545 5.795 ; + RECT 11.295 5.66 11.36 5.795 ; + RECT 11.67 5.66 11.735 5.795 ; + RECT 11.485 5.66 11.55 5.795 ; + RECT 11.48 3.89 11.545 4.025 ; + RECT 11.295 3.89 11.36 4.025 ; + RECT 11.555 6.865 11.62 7.0 ; + RECT 11.37 6.865 11.435 7.0 ; + RECT 11.3575 3.6 11.4925 3.665 ; + RECT 11.295 9.0975 11.36 9.2325 ; + RECT 11.185 7.2825 11.25 7.4175 ; + RECT 11.295 6.3825 11.36 6.5175 ; + RECT 11.7175 6.8575 11.7825 6.9925 ; + RECT 11.295 9.4625 11.36 9.5975 ; + RECT 11.8025 3.92 11.8675 4.055 ; + RECT 11.66 8.0025 11.725 8.1375 ; + RECT 11.6625 6.395 11.7275 6.53 ; + RECT 11.6625 4.9975 11.7275 5.1325 ; + RECT 11.0975 3.78 11.1625 3.915 ; + RECT 11.6725 3.8975 11.7375 4.0325 ; + RECT 11.6725 5.5075 11.8075 5.5725 ; + RECT 11.7125 4.4225 11.7775 4.5575 ; + RECT 11.6725 8.4675 11.8075 8.5325 ; + RECT 11.62 9.355 11.685 9.49 ; + RECT 11.54 5.885 11.605 6.02 ; + RECT 11.53 8.845 11.595 8.98 ; + RECT 11.2975 7.155 11.4325 7.22 ; + RECT 11.8025 6.8575 11.8675 6.9925 ; + RECT 11.1875 4.15 11.3225 4.215 ; + RECT 11.2975 5.6625 11.3625 5.7975 ; + RECT 11.6625 9.355 11.7275 9.49 ; + RECT 11.485 8.845 11.55 8.98 ; + RECT 11.8025 8.4325 11.8675 8.5675 ; + RECT 11.8025 3.73 11.8675 3.865 ; + RECT 11.6625 6.395 11.7275 6.53 ; + RECT 11.8025 5.4725 11.8675 5.6075 ; + RECT 11.2975 5.2725 11.3625 5.4075 ; + RECT 11.485 5.2725 11.55 5.4075 ; + RECT 11.485 5.885 11.55 6.02 ; + RECT 11.295 8.2325 11.36 8.3675 ; + RECT 11.485 8.2325 11.55 8.3675 ; + RECT 11.095 3.6 11.87 3.665 ; + RECT 11.165 6.67 11.23 9.565 ; + RECT 11.67 9.18 11.735 9.49 ; + RECT 11.295 8.755 11.36 9.045 ; + RECT 11.295 7.945 11.36 8.235 ; + RECT 11.435 9.5675 11.5075 9.63 ; + RECT 11.48 9.18 11.55 9.565 ; + RECT 11.48 7.945 11.55 8.235 ; + RECT 11.48 8.465 11.55 8.6225 ; + RECT 11.67 8.755 11.735 9.045 ; + RECT 11.67 7.945 11.735 8.235 ; + RECT 11.48 8.465 11.865 8.535 ; + RECT 11.095 9.565 11.87 9.63 ; + RECT 11.095 7.575 11.87 7.64 ; + RECT 11.165 4.68 11.23 6.605 ; + RECT 11.67 6.22 11.735 6.53 ; + RECT 11.295 5.795 11.36 6.085 ; + RECT 11.37 6.96 11.435 7.41 ; + RECT 11.48 6.22 11.55 6.605 ; + RECT 11.48 5.505 11.55 5.6625 ; + RECT 11.295 6.5175 11.36 6.605 ; + RECT 11.67 5.795 11.735 6.085 ; + RECT 11.555 6.735 11.8 6.8 ; + RECT 11.7175 6.735 11.8 6.8575 ; + RECT 11.7375 6.8575 11.805 6.9925 ; + RECT 11.095 6.605 11.87 6.67 ; + RECT 11.295 4.985 11.36 5.275 ; + RECT 11.48 4.4475 11.545 4.615 ; + RECT 11.545 3.955 11.8025 4.025 ; + RECT 11.295 3.89 11.36 4.315 ; + RECT 11.48 4.985 11.55 5.275 ; + RECT 11.1875 4.145 11.36 4.22 ; + RECT 11.67 4.985 11.735 5.275 ; + RECT 11.6725 3.825 11.7375 3.8975 ; + RECT 11.48 5.505 11.865 5.575 ; + RECT 11.7125 4.535 11.7775 4.65 ; + RECT 11.095 3.76 11.87 3.825 ; + RECT 11.095 4.615 11.87 4.68 ; + RECT 11.235 3.605 11.3 3.6625 ; + RECT 11.8025 3.845 11.8675 3.9675 ; + RECT 11.62 7.4075 11.685 7.575 ; + RECT 11.555 6.8 11.6225 6.9025 ; + RECT 11.935 6.085 12.0 6.22 ; + RECT 12.12 6.085 12.185 6.22 ; + RECT 11.935 4.85 12.0 4.985 ; + RECT 12.12 4.85 12.185 4.985 ; + RECT 12.125 6.085 12.19 6.22 ; + RECT 12.31 6.085 12.375 6.22 ; + RECT 12.125 9.045 12.19 9.18 ; + RECT 12.31 9.045 12.375 9.18 ; + RECT 12.125 4.85 12.19 4.985 ; + RECT 12.31 4.85 12.375 4.985 ; + RECT 11.935 9.045 12.0 9.18 ; + RECT 12.12 9.045 12.185 9.18 ; + RECT 12.125 4.315 12.19 4.45 ; + RECT 12.31 4.315 12.375 4.45 ; + RECT 11.985 7.275 12.05 7.41 ; + RECT 12.17 7.275 12.235 7.41 ; + RECT 11.935 7.81 12.0 7.945 ; + RECT 12.12 7.81 12.185 7.945 ; + RECT 12.125 7.81 12.19 7.945 ; + RECT 12.31 7.81 12.375 7.945 ; + RECT 11.935 8.235 12.0 8.37 ; + RECT 12.12 8.235 12.185 8.37 ; + RECT 11.935 5.275 12.0 5.41 ; + RECT 12.12 5.275 12.185 5.41 ; + RECT 11.935 8.62 12.0 8.755 ; + RECT 12.12 8.62 12.185 8.755 ; + RECT 12.125 8.62 12.19 8.755 ; + RECT 12.31 8.62 12.375 8.755 ; + RECT 12.125 5.275 12.19 5.41 ; + RECT 12.31 5.275 12.375 5.41 ; + RECT 12.125 8.235 12.19 8.37 ; + RECT 12.31 8.235 12.375 8.37 ; + RECT 12.125 5.66 12.19 5.795 ; + RECT 12.31 5.66 12.375 5.795 ; + RECT 11.935 5.66 12.0 5.795 ; + RECT 12.12 5.66 12.185 5.795 ; + RECT 12.125 3.89 12.19 4.025 ; + RECT 12.31 3.89 12.375 4.025 ; + RECT 12.05 6.865 12.115 7.0 ; + RECT 12.235 6.865 12.3 7.0 ; + RECT 12.1775 3.6 12.3125 3.665 ; + RECT 12.31 9.0975 12.375 9.2325 ; + RECT 12.42 7.2825 12.485 7.4175 ; + RECT 12.31 6.3825 12.375 6.5175 ; + RECT 11.8875 6.8575 11.9525 6.9925 ; + RECT 12.31 9.4625 12.375 9.5975 ; + RECT 11.8025 3.92 11.8675 4.055 ; + RECT 11.945 8.0025 12.01 8.1375 ; + RECT 11.9425 6.395 12.0075 6.53 ; + RECT 11.9425 4.9975 12.0075 5.1325 ; + RECT 12.5075 3.78 12.5725 3.915 ; + RECT 11.9325 3.8975 11.9975 4.0325 ; + RECT 11.8625 5.5075 11.9975 5.5725 ; + RECT 11.8925 4.4225 11.9575 4.5575 ; + RECT 11.8625 8.4675 11.9975 8.5325 ; + RECT 11.985 9.355 12.05 9.49 ; + RECT 12.065 5.885 12.13 6.02 ; + RECT 12.075 8.845 12.14 8.98 ; + RECT 12.2375 7.155 12.3725 7.22 ; + RECT 11.8025 6.8575 11.8675 6.9925 ; + RECT 12.3475 4.15 12.4825 4.215 ; + RECT 12.3075 5.6625 12.3725 5.7975 ; + RECT 11.9425 9.355 12.0075 9.49 ; + RECT 12.12 8.845 12.185 8.98 ; + RECT 11.8025 8.4325 11.8675 8.5675 ; + RECT 11.8025 3.73 11.8675 3.865 ; + RECT 11.9425 6.395 12.0075 6.53 ; + RECT 11.8025 5.4725 11.8675 5.6075 ; + RECT 12.3075 5.2725 12.3725 5.4075 ; + RECT 12.12 5.2725 12.185 5.4075 ; + RECT 12.12 5.885 12.185 6.02 ; + RECT 12.31 8.2325 12.375 8.3675 ; + RECT 12.12 8.2325 12.185 8.3675 ; + RECT 11.8 3.6 12.575 3.665 ; + RECT 12.44 6.67 12.505 9.565 ; + RECT 11.935 9.18 12.0 9.49 ; + RECT 12.31 8.755 12.375 9.045 ; + RECT 12.31 7.945 12.375 8.235 ; + RECT 12.1625 9.5675 12.235 9.63 ; + RECT 12.12 9.18 12.19 9.565 ; + RECT 12.12 7.945 12.19 8.235 ; + RECT 12.12 8.465 12.19 8.6225 ; + RECT 11.935 8.755 12.0 9.045 ; + RECT 11.935 7.945 12.0 8.235 ; + RECT 11.805 8.465 12.19 8.535 ; + RECT 11.8 9.565 12.575 9.63 ; + RECT 11.8 7.575 12.575 7.64 ; + RECT 12.44 4.68 12.505 6.605 ; + RECT 11.935 6.22 12.0 6.53 ; + RECT 12.31 5.795 12.375 6.085 ; + RECT 12.235 6.96 12.3 7.41 ; + RECT 12.12 6.22 12.19 6.605 ; + RECT 12.12 5.505 12.19 5.6625 ; + RECT 12.31 6.5175 12.375 6.605 ; + RECT 11.935 5.795 12.0 6.085 ; + RECT 11.87 6.735 12.115 6.8 ; + RECT 11.87 6.735 11.9525 6.8575 ; + RECT 11.865 6.8575 11.9325 6.9925 ; + RECT 11.8 6.605 12.575 6.67 ; + RECT 12.31 4.985 12.375 5.275 ; + RECT 12.125 4.4475 12.19 4.615 ; + RECT 11.8675 3.955 12.125 4.025 ; + RECT 12.31 3.89 12.375 4.315 ; + RECT 12.12 4.985 12.19 5.275 ; + RECT 12.31 4.145 12.4825 4.22 ; + RECT 11.935 4.985 12.0 5.275 ; + RECT 11.9325 3.825 11.9975 3.8975 ; + RECT 11.805 5.505 12.19 5.575 ; + RECT 11.8925 4.535 11.9575 4.65 ; + RECT 11.8 3.76 12.575 3.825 ; + RECT 11.8 4.615 12.575 4.68 ; + RECT 12.37 3.605 12.435 3.6625 ; + RECT 11.8025 3.845 11.8675 3.9675 ; + RECT 11.985 7.4075 12.05 7.575 ; + RECT 12.0475 6.8 12.115 6.9025 ; + RECT 11.13 2.9625 12.54 3.0275 ; + RECT 11.13 1.415 12.54 1.48 ; + RECT 11.13 1.545 12.54 1.61 ; + RECT 11.485 5.44 11.55 5.575 ; + RECT 11.3 5.44 11.365 5.575 ; + RECT 11.485 5.955 11.55 6.09 ; + RECT 11.3 5.955 11.365 6.09 ; + RECT 11.6125 4.045 11.6775 4.18 ; + RECT 11.2375 4.045 11.3025 4.18 ; + RECT 11.6125 4.6725 11.6775 5.0875 ; + RECT 11.2375 4.6725 11.3025 5.0875 ; + RECT 11.6275 4.3125 11.7625 4.3775 ; + RECT 11.65 5.9625 11.715 6.0975 ; + RECT 11.415 5.18 11.55 5.245 ; + RECT 11.57 3.8925 11.705 3.9575 ; + RECT 11.8025 3.8925 11.8675 4.0275 ; + RECT 11.1875 5.31 11.3225 5.375 ; + RECT 11.2925 3.7625 11.4275 3.8275 ; + RECT 11.5475 5.82 11.6825 5.885 ; + RECT 11.8025 5.9525 11.8675 6.0875 ; + RECT 11.5475 5.78 11.6825 5.845 ; + RECT 11.57 5.18 11.705 5.245 ; + RECT 11.275 5.7625 11.34 5.8975 ; + RECT 11.4825 5.485 11.5475 5.62 ; + RECT 11.455 3.7625 11.5125 3.8275 ; + RECT 11.4075 5.31 11.47 5.375 ; + RECT 11.3 5.57 11.365 6.09 ; + RECT 11.2375 4.1575 11.3025 4.6725 ; + RECT 11.5475 4.3125 11.6825 4.3775 ; + RECT 11.13 3.7625 11.87 3.8275 ; + RECT 11.13 3.8925 11.87 3.9575 ; + RECT 11.6125 3.9575 11.6775 4.045 ; + RECT 11.505 5.9525 11.8025 6.0875 ; + RECT 11.13 5.31 11.87 5.375 ; + RECT 11.13 5.18 11.87 5.245 ; + RECT 11.6125 5.0875 11.6775 5.18 ; + RECT 11.2725 5.18 11.3375 5.245 ; + RECT 11.2375 4.045 11.3025 4.18 ; + RECT 12.12 5.44 12.185 5.575 ; + RECT 12.305 5.44 12.37 5.575 ; + RECT 12.12 5.955 12.185 6.09 ; + RECT 12.305 5.955 12.37 6.09 ; + RECT 11.9925 4.045 12.0575 4.18 ; + RECT 12.3675 4.045 12.4325 4.18 ; + RECT 11.9925 4.6725 12.0575 5.0875 ; + RECT 12.3675 4.6725 12.4325 5.0875 ; + RECT 11.9075 4.3125 12.0425 4.3775 ; + RECT 11.955 5.9625 12.02 6.0975 ; + RECT 12.12 5.18 12.255 5.245 ; + RECT 11.965 3.8925 12.1 3.9575 ; + RECT 11.8025 3.8925 11.8675 4.0275 ; + RECT 12.3475 5.31 12.4825 5.375 ; + RECT 12.2425 3.7625 12.3775 3.8275 ; + RECT 11.9875 5.82 12.1225 5.885 ; + RECT 11.8025 5.9525 11.8675 6.0875 ; + RECT 11.9875 5.78 12.1225 5.845 ; + RECT 11.965 5.18 12.1 5.245 ; + RECT 12.33 5.7625 12.395 5.8975 ; + RECT 12.1225 5.485 12.1875 5.62 ; + RECT 12.1575 3.7625 12.215 3.8275 ; + RECT 12.2 5.31 12.2625 5.375 ; + RECT 12.305 5.57 12.37 6.09 ; + RECT 12.3675 4.1575 12.4325 4.6725 ; + RECT 11.9875 4.3125 12.1225 4.3775 ; + RECT 11.8 3.7625 12.54 3.8275 ; + RECT 11.8 3.8925 12.54 3.9575 ; + RECT 11.9925 3.9575 12.0575 4.045 ; + RECT 11.8675 5.9525 12.165 6.0875 ; + RECT 11.8 5.31 12.54 5.375 ; + RECT 11.8 5.18 12.54 5.245 ; + RECT 11.9925 5.0875 12.0575 5.18 ; + RECT 12.3325 5.18 12.3975 5.245 ; + RECT 12.3675 4.045 12.4325 4.18 ; + RECT 4.97 19.4925 5.035 19.5575 ; + RECT 4.97 20.9225 5.035 20.9875 ; + RECT 4.97 22.1825 5.035 22.2475 ; + RECT 4.97 23.6125 5.035 23.6775 ; + RECT 4.97 24.8725 5.035 24.9375 ; + RECT 4.97 26.3025 5.035 26.3675 ; + RECT 4.97 27.5625 5.035 27.6275 ; + RECT 4.97 28.9925 5.035 29.0575 ; + RECT 4.97 30.2525 5.035 30.3175 ; + RECT 4.97 31.6825 5.035 31.7475 ; + RECT 4.97 32.9425 5.035 33.0075 ; + RECT 4.97 34.3725 5.035 34.4375 ; + RECT 4.97 35.6325 5.035 35.6975 ; + RECT 4.97 37.0625 5.035 37.1275 ; + RECT 4.97 38.3225 5.035 38.3875 ; + RECT 4.97 39.7525 5.035 39.8175 ; + RECT 2.82 8.73 4.22 8.795 ; + RECT 2.995 10.165 4.22 10.23 ; + RECT 3.17 11.42 4.22 11.485 ; + RECT 3.345 12.855 4.22 12.92 ; + RECT 3.52 14.11 4.22 14.175 ; + RECT 3.695 15.545 4.22 15.61 ; + RECT 3.87 16.8 4.22 16.865 ; + RECT 4.045 18.235 4.22 18.3 ; + RECT 2.82 19.805 4.22 19.87 ; + RECT 3.52 19.275 4.22 19.34 ; + RECT 2.82 20.61 4.22 20.675 ; + RECT 3.695 21.14 4.22 21.205 ; + RECT 2.82 22.495 4.22 22.56 ; + RECT 3.87 21.965 4.22 22.03 ; + RECT 2.82 23.3 4.22 23.365 ; + RECT 4.045 23.83 4.22 23.895 ; + RECT 2.995 25.185 4.22 25.25 ; + RECT 3.52 24.655 4.22 24.72 ; + RECT 2.995 25.99 4.22 26.055 ; + RECT 3.695 26.52 4.22 26.585 ; + RECT 2.995 27.875 4.22 27.94 ; + RECT 3.87 27.345 4.22 27.41 ; + RECT 2.995 28.68 4.22 28.745 ; + RECT 4.045 29.21 4.22 29.275 ; + RECT 3.17 30.565 4.22 30.63 ; + RECT 3.52 30.035 4.22 30.1 ; + RECT 3.17 31.37 4.22 31.435 ; + RECT 3.695 31.9 4.22 31.965 ; + RECT 3.17 33.255 4.22 33.32 ; + RECT 3.87 32.725 4.22 32.79 ; + RECT 3.17 34.06 4.22 34.125 ; + RECT 4.045 34.59 4.22 34.655 ; + RECT 3.345 35.945 4.22 36.01 ; + RECT 3.52 35.415 4.22 35.48 ; + RECT 3.345 36.75 4.22 36.815 ; + RECT 3.695 37.28 4.22 37.345 ; + RECT 3.345 38.635 4.22 38.7 ; + RECT 3.87 38.105 4.22 38.17 ; + RECT 3.345 39.44 4.22 39.505 ; + RECT 4.045 39.97 4.22 40.035 ; + RECT 4.715 8.7325 4.78 8.7975 ; + RECT 4.715 10.1625 4.78 10.2275 ; + RECT 4.715 11.4225 4.78 11.4875 ; + RECT 4.715 12.8525 4.78 12.9175 ; + RECT 6.62 8.73 6.685 9.3175 ; + RECT 6.16 9.2525 6.685 9.3175 ; + RECT 7.175 8.73 7.595 8.795 ; + RECT 6.51 9.4475 7.245 9.5125 ; + RECT 6.335 8.1025 7.245 8.1675 ; + RECT 6.62 9.6425 6.685 10.23 ; + RECT 5.985 9.6425 6.685 9.7075 ; + RECT 7.175 10.165 7.42 10.23 ; + RECT 6.51 9.4475 7.245 9.5125 ; + RECT 6.335 10.7925 7.245 10.8575 ; + RECT 5.53 9.045 6.23 9.11 ; + RECT 5.53 8.515 6.055 8.58 ; + RECT 5.53 9.85 5.88 9.915 ; + RECT 5.53 10.38 6.055 10.445 ; + RECT 5.53 11.735 6.23 11.8 ; + RECT 5.53 11.205 5.705 11.27 ; + RECT 5.53 12.54 5.88 12.605 ; + RECT 5.53 12.54 7.595 12.605 ; + RECT 5.53 13.07 5.705 13.135 ; + RECT 5.53 13.07 7.42 13.135 ; + RECT 5.53 8.1025 6.405 8.1675 ; + RECT 5.53 9.4475 6.58 9.5125 ; + RECT 5.53 10.7925 6.405 10.8575 ; + RECT 5.53 12.1375 6.58 12.2025 ; + RECT 5.53 13.4825 6.405 13.5475 ; + RECT 6.34 13.305 6.405 13.4825 ; + RECT 6.685 8.1025 7.245 8.1675 ; + RECT 6.685 6.7575 7.245 6.8225 ; + RECT 6.7525 6.8225 6.8175 7.0725 ; + RECT 6.7525 7.9675 6.8175 8.1025 ; + RECT 7.1125 8.0325 7.1775 8.1025 ; + RECT 7.1125 6.8225 7.1775 6.9375 ; + RECT 6.9225 6.9375 6.9875 8.0 ; + RECT 7.21 7.475 7.245 7.54 ; + RECT 6.685 7.475 6.9225 7.54 ; + RECT 7.1125 7.7625 7.1775 7.8975 ; + RECT 6.9225 7.7625 6.9875 7.8975 ; + RECT 7.1125 6.9375 7.1775 7.0725 ; + RECT 6.9225 6.9375 6.9875 7.0725 ; + RECT 6.7525 6.9375 6.8175 7.0725 ; + RECT 6.7525 7.8975 6.8175 8.0325 ; + RECT 7.075 7.475 7.21 7.54 ; + RECT 6.685 10.7925 7.245 10.8575 ; + RECT 6.685 12.1375 7.245 12.2025 ; + RECT 6.7525 11.8875 6.8175 12.1375 ; + RECT 6.7525 10.8575 6.8175 10.9925 ; + RECT 7.1125 10.8575 7.1775 10.9275 ; + RECT 7.1125 12.0225 7.1775 12.1375 ; + RECT 6.9225 10.96 6.9875 12.0225 ; + RECT 7.21 11.42 7.245 11.485 ; + RECT 6.685 11.42 6.9225 11.485 ; + RECT 7.1125 10.9925 7.1775 11.1275 ; + RECT 6.9225 10.9925 6.9875 11.1275 ; + RECT 7.1125 11.5975 7.1775 11.7325 ; + RECT 6.9225 11.5975 6.9875 11.7325 ; + RECT 6.7525 11.7525 6.8175 11.8875 ; + RECT 6.7525 10.7925 6.8175 10.9275 ; + RECT 7.075 11.345 7.21 11.41 ; + RECT 4.22 8.1025 4.78 8.1675 ; + RECT 4.22 6.7575 4.78 6.8225 ; + RECT 4.2875 6.8225 4.3525 7.0725 ; + RECT 4.2875 7.9675 4.3525 8.1025 ; + RECT 4.6475 8.0325 4.7125 8.1025 ; + RECT 4.6475 6.8225 4.7125 6.9375 ; + RECT 4.4575 6.9375 4.5225 8.0 ; + RECT 4.745 7.475 4.78 7.54 ; + RECT 4.22 7.475 4.4575 7.54 ; + RECT 4.6475 7.7625 4.7125 7.8975 ; + RECT 4.4575 7.7625 4.5225 7.8975 ; + RECT 4.6475 6.9375 4.7125 7.0725 ; + RECT 4.4575 6.9375 4.5225 7.0725 ; + RECT 4.2875 6.9375 4.3525 7.0725 ; + RECT 4.2875 7.8975 4.3525 8.0325 ; + RECT 4.61 7.475 4.745 7.54 ; + RECT 4.22 10.7925 4.78 10.8575 ; + RECT 4.22 12.1375 4.78 12.2025 ; + RECT 4.2875 11.8875 4.3525 12.1375 ; + RECT 4.2875 10.8575 4.3525 10.9925 ; + RECT 4.6475 10.8575 4.7125 10.9275 ; + RECT 4.6475 12.0225 4.7125 12.1375 ; + RECT 4.4575 10.96 4.5225 12.0225 ; + RECT 4.745 11.42 4.78 11.485 ; + RECT 4.22 11.42 4.4575 11.485 ; + RECT 4.6475 10.9925 4.7125 11.1275 ; + RECT 4.4575 10.9925 4.5225 11.1275 ; + RECT 4.6475 11.5975 4.7125 11.7325 ; + RECT 4.4575 11.5975 4.5225 11.7325 ; + RECT 4.2875 11.7525 4.3525 11.8875 ; + RECT 4.2875 10.7925 4.3525 10.9275 ; + RECT 4.61 11.345 4.745 11.41 ; + RECT 4.22 10.7925 4.78 10.8575 ; + RECT 4.22 9.4475 4.78 9.5125 ; + RECT 4.2875 9.5125 4.3525 9.7625 ; + RECT 4.2875 10.6575 4.3525 10.7925 ; + RECT 4.6475 10.7225 4.7125 10.7925 ; + RECT 4.6475 9.5125 4.7125 9.6275 ; + RECT 4.4575 9.6275 4.5225 10.69 ; + RECT 4.745 10.165 4.78 10.23 ; + RECT 4.22 10.165 4.4575 10.23 ; + RECT 4.6475 10.4525 4.7125 10.5875 ; + RECT 4.4575 10.4525 4.5225 10.5875 ; + RECT 4.6475 9.6275 4.7125 9.7625 ; + RECT 4.4575 9.6275 4.5225 9.7625 ; + RECT 4.2875 9.6275 4.3525 9.7625 ; + RECT 4.2875 10.5875 4.3525 10.7225 ; + RECT 4.61 10.165 4.745 10.23 ; + RECT 4.22 13.4825 4.78 13.5475 ; + RECT 4.22 14.8275 4.78 14.8925 ; + RECT 4.2875 14.5775 4.3525 14.8275 ; + RECT 4.2875 13.5475 4.3525 13.6825 ; + RECT 4.6475 13.5475 4.7125 13.6175 ; + RECT 4.6475 14.7125 4.7125 14.8275 ; + RECT 4.4575 13.65 4.5225 14.7125 ; + RECT 4.745 14.11 4.78 14.175 ; + RECT 4.22 14.11 4.4575 14.175 ; + RECT 4.6475 13.6825 4.7125 13.8175 ; + RECT 4.4575 13.6825 4.5225 13.8175 ; + RECT 4.6475 14.2875 4.7125 14.4225 ; + RECT 4.4575 14.2875 4.5225 14.4225 ; + RECT 4.2875 14.4425 4.3525 14.5775 ; + RECT 4.2875 13.4825 4.3525 13.6175 ; + RECT 4.61 14.035 4.745 14.1 ; + RECT 4.78 8.1025 5.53 8.1675 ; + RECT 4.78 6.7575 5.53 6.8225 ; + RECT 4.8475 6.79 4.9125 7.0725 ; + RECT 4.8475 7.955 4.9125 8.135 ; + RECT 5.3975 6.79 5.4625 7.0725 ; + RECT 5.0175 6.79 5.0825 7.0725 ; + RECT 5.3975 7.8525 5.4625 8.135 ; + RECT 5.495 7.16 5.53 7.225 ; + RECT 5.255 7.69 5.53 7.755 ; + RECT 4.78 7.4725 5.0175 7.5375 ; + RECT 5.3975 7.7175 5.4625 7.8525 ; + RECT 5.2075 7.7175 5.2725 7.8525 ; + RECT 5.2075 7.7175 5.2725 7.8525 ; + RECT 5.0175 7.7175 5.0825 7.8525 ; + RECT 5.3975 6.9375 5.4625 7.0725 ; + RECT 5.2075 6.9375 5.2725 7.0725 ; + RECT 5.2075 6.9375 5.2725 7.0725 ; + RECT 5.0175 6.9375 5.0825 7.0725 ; + RECT 4.8475 6.9375 4.9125 7.0725 ; + RECT 4.8475 7.8525 4.9125 7.9875 ; + RECT 4.985 7.175 5.05 7.24 ; + RECT 5.2075 7.175 5.2725 7.24 ; + RECT 4.985 7.2075 5.05 7.9875 ; + RECT 5.0175 7.175 5.24 7.24 ; + RECT 5.2075 7.0725 5.2725 7.2075 ; + RECT 5.36 7.16 5.495 7.225 ; + RECT 5.12 7.69 5.255 7.755 ; + RECT 4.78 10.7925 5.53 10.8575 ; + RECT 4.78 12.1375 5.53 12.2025 ; + RECT 4.8475 11.8875 4.9125 12.17 ; + RECT 4.8475 10.825 4.9125 11.005 ; + RECT 5.3975 11.8875 5.4625 12.17 ; + RECT 5.0175 11.8875 5.0825 12.17 ; + RECT 5.3975 10.825 5.4625 11.1075 ; + RECT 5.495 11.735 5.53 11.8 ; + RECT 5.255 11.205 5.53 11.27 ; + RECT 4.78 11.4225 5.0175 11.4875 ; + RECT 5.3975 11.1275 5.4625 11.2625 ; + RECT 5.2075 11.1275 5.2725 11.2625 ; + RECT 5.2075 11.1275 5.2725 11.2625 ; + RECT 5.0175 11.1275 5.0825 11.2625 ; + RECT 5.3975 11.5975 5.4625 11.7325 ; + RECT 5.2075 11.5975 5.2725 11.7325 ; + RECT 5.2075 11.5975 5.2725 11.7325 ; + RECT 5.0175 11.5975 5.0825 11.7325 ; + RECT 4.8475 11.7525 4.9125 11.8875 ; + RECT 4.8475 10.8375 4.9125 10.9725 ; + RECT 4.985 10.16 5.05 10.225 ; + RECT 5.2075 10.16 5.2725 10.225 ; + RECT 4.985 10.1925 5.05 10.9725 ; + RECT 5.0175 10.16 5.24 10.225 ; + RECT 5.2075 10.0575 5.2725 10.1925 ; + RECT 5.36 11.66 5.495 11.725 ; + RECT 5.12 11.13 5.255 11.195 ; + RECT 4.78 10.7925 5.53 10.8575 ; + RECT 4.78 9.4475 5.53 9.5125 ; + RECT 4.8475 9.48 4.9125 9.7625 ; + RECT 4.8475 10.645 4.9125 10.825 ; + RECT 5.3975 9.48 5.4625 9.7625 ; + RECT 5.0175 9.48 5.0825 9.7625 ; + RECT 5.3975 10.5425 5.4625 10.825 ; + RECT 5.495 9.85 5.53 9.915 ; + RECT 5.255 10.38 5.53 10.445 ; + RECT 4.78 10.1625 5.0175 10.2275 ; + RECT 5.3975 10.4075 5.4625 10.5425 ; + RECT 5.2075 10.4075 5.2725 10.5425 ; + RECT 5.2075 10.4075 5.2725 10.5425 ; + RECT 5.0175 10.4075 5.0825 10.5425 ; + RECT 5.3975 9.6275 5.4625 9.7625 ; + RECT 5.2075 9.6275 5.2725 9.7625 ; + RECT 5.2075 9.6275 5.2725 9.7625 ; + RECT 5.0175 9.6275 5.0825 9.7625 ; + RECT 4.8475 9.6275 4.9125 9.7625 ; + RECT 4.8475 10.5425 4.9125 10.6775 ; + RECT 4.985 9.865 5.05 9.93 ; + RECT 5.2075 9.865 5.2725 9.93 ; + RECT 4.985 9.8975 5.05 10.6775 ; + RECT 5.0175 9.865 5.24 9.93 ; + RECT 5.2075 9.7625 5.2725 9.8975 ; + RECT 5.36 9.85 5.495 9.915 ; + RECT 5.12 10.38 5.255 10.445 ; + RECT 4.78 13.4825 5.53 13.5475 ; + RECT 4.78 14.8275 5.53 14.8925 ; + RECT 4.8475 14.5775 4.9125 14.86 ; + RECT 4.8475 13.515 4.9125 13.695 ; + RECT 5.3975 14.5775 5.4625 14.86 ; + RECT 5.0175 14.5775 5.0825 14.86 ; + RECT 5.3975 13.515 5.4625 13.7975 ; + RECT 5.495 14.425 5.53 14.49 ; + RECT 5.255 13.895 5.53 13.96 ; + RECT 4.78 14.1125 5.0175 14.1775 ; + RECT 5.3975 13.8175 5.4625 13.9525 ; + RECT 5.2075 13.8175 5.2725 13.9525 ; + RECT 5.2075 13.8175 5.2725 13.9525 ; + RECT 5.0175 13.8175 5.0825 13.9525 ; + RECT 5.3975 14.2875 5.4625 14.4225 ; + RECT 5.2075 14.2875 5.2725 14.4225 ; + RECT 5.2075 14.2875 5.2725 14.4225 ; + RECT 5.0175 14.2875 5.0825 14.4225 ; + RECT 4.8475 14.4425 4.9125 14.5775 ; + RECT 4.8475 13.5275 4.9125 13.6625 ; + RECT 4.985 12.85 5.05 12.915 ; + RECT 5.2075 12.85 5.2725 12.915 ; + RECT 4.985 12.8825 5.05 13.6625 ; + RECT 5.0175 12.85 5.24 12.915 ; + RECT 5.2075 12.7475 5.2725 12.8825 ; + RECT 5.36 14.35 5.495 14.415 ; + RECT 5.12 13.82 5.255 13.885 ; + RECT 6.125 9.1825 6.26 9.2475 ; + RECT 7.49 8.66 7.625 8.725 ; + RECT 5.95 9.5725 6.085 9.6375 ; + RECT 7.315 10.095 7.45 10.16 ; + RECT 6.125 8.975 6.26 9.04 ; + RECT 5.95 8.445 6.085 8.51 ; + RECT 5.775 9.78 5.91 9.845 ; + RECT 5.95 10.31 6.085 10.375 ; + RECT 6.125 11.665 6.26 11.73 ; + RECT 5.6 11.135 5.735 11.2 ; + RECT 5.775 12.47 5.91 12.535 ; + RECT 7.49 12.47 7.625 12.535 ; + RECT 5.6 13.0 5.735 13.065 ; + RECT 7.315 13.0 7.45 13.065 ; + RECT 6.3 8.0325 6.435 8.0975 ; + RECT 6.475 9.3775 6.61 9.4425 ; + RECT 6.3 10.7225 6.435 10.7875 ; + RECT 6.475 12.0675 6.61 12.1325 ; + RECT 6.3 13.235 6.435 13.3 ; + RECT 4.715 14.1125 4.78 14.1775 ; + RECT 4.715 15.5425 4.78 15.6075 ; + RECT 4.715 16.8025 4.78 16.8675 ; + RECT 4.715 18.2325 4.78 18.2975 ; + RECT 6.62 14.11 6.685 14.6975 ; + RECT 6.16 14.6325 6.685 14.6975 ; + RECT 7.175 14.11 7.595 14.175 ; + RECT 6.51 14.8275 7.245 14.8925 ; + RECT 6.335 13.4825 7.245 13.5475 ; + RECT 6.62 15.0225 6.685 15.61 ; + RECT 5.985 15.0225 6.685 15.0875 ; + RECT 7.175 15.545 7.42 15.61 ; + RECT 6.51 14.8275 7.245 14.8925 ; + RECT 6.335 16.1725 7.245 16.2375 ; + RECT 5.53 14.425 6.23 14.49 ; + RECT 5.53 13.895 6.055 13.96 ; + RECT 5.53 15.23 5.88 15.295 ; + RECT 5.53 15.76 6.055 15.825 ; + RECT 5.53 17.115 6.23 17.18 ; + RECT 5.53 16.585 5.705 16.65 ; + RECT 5.53 17.92 5.88 17.985 ; + RECT 5.53 17.92 7.595 17.985 ; + RECT 5.53 18.45 5.705 18.515 ; + RECT 5.53 18.45 7.42 18.515 ; + RECT 5.53 13.4825 6.405 13.5475 ; + RECT 5.53 14.8275 6.58 14.8925 ; + RECT 5.53 16.1725 6.405 16.2375 ; + RECT 5.53 17.5175 6.58 17.5825 ; + RECT 5.53 18.8625 6.405 18.9275 ; + RECT 6.34 18.685 6.405 18.8625 ; + RECT 6.685 13.4825 7.245 13.5475 ; + RECT 6.685 12.1375 7.245 12.2025 ; + RECT 6.7525 12.2025 6.8175 12.4525 ; + RECT 6.7525 13.3475 6.8175 13.4825 ; + RECT 7.1125 13.4125 7.1775 13.4825 ; + RECT 7.1125 12.2025 7.1775 12.3175 ; + RECT 6.9225 12.3175 6.9875 13.38 ; + RECT 7.21 12.855 7.245 12.92 ; + RECT 6.685 12.855 6.9225 12.92 ; + RECT 7.1125 13.1425 7.1775 13.2775 ; + RECT 6.9225 13.1425 6.9875 13.2775 ; + RECT 7.1125 12.3175 7.1775 12.4525 ; + RECT 6.9225 12.3175 6.9875 12.4525 ; + RECT 6.7525 12.3175 6.8175 12.4525 ; + RECT 6.7525 13.2775 6.8175 13.4125 ; + RECT 7.075 12.855 7.21 12.92 ; + RECT 6.685 16.1725 7.245 16.2375 ; + RECT 6.685 17.5175 7.245 17.5825 ; + RECT 6.7525 17.2675 6.8175 17.5175 ; + RECT 6.7525 16.2375 6.8175 16.3725 ; + RECT 7.1125 16.2375 7.1775 16.3075 ; + RECT 7.1125 17.4025 7.1775 17.5175 ; + RECT 6.9225 16.34 6.9875 17.4025 ; + RECT 7.21 16.8 7.245 16.865 ; + RECT 6.685 16.8 6.9225 16.865 ; + RECT 7.1125 16.3725 7.1775 16.5075 ; + RECT 6.9225 16.3725 6.9875 16.5075 ; + RECT 7.1125 16.9775 7.1775 17.1125 ; + RECT 6.9225 16.9775 6.9875 17.1125 ; + RECT 6.7525 17.1325 6.8175 17.2675 ; + RECT 6.7525 16.1725 6.8175 16.3075 ; + RECT 7.075 16.725 7.21 16.79 ; + RECT 4.22 13.4825 4.78 13.5475 ; + RECT 4.22 12.1375 4.78 12.2025 ; + RECT 4.2875 12.2025 4.3525 12.4525 ; + RECT 4.2875 13.3475 4.3525 13.4825 ; + RECT 4.6475 13.4125 4.7125 13.4825 ; + RECT 4.6475 12.2025 4.7125 12.3175 ; + RECT 4.4575 12.3175 4.5225 13.38 ; + RECT 4.745 12.855 4.78 12.92 ; + RECT 4.22 12.855 4.4575 12.92 ; + RECT 4.6475 13.1425 4.7125 13.2775 ; + RECT 4.4575 13.1425 4.5225 13.2775 ; + RECT 4.6475 12.3175 4.7125 12.4525 ; + RECT 4.4575 12.3175 4.5225 12.4525 ; + RECT 4.2875 12.3175 4.3525 12.4525 ; + RECT 4.2875 13.2775 4.3525 13.4125 ; + RECT 4.61 12.855 4.745 12.92 ; + RECT 4.22 16.1725 4.78 16.2375 ; + RECT 4.22 17.5175 4.78 17.5825 ; + RECT 4.2875 17.2675 4.3525 17.5175 ; + RECT 4.2875 16.2375 4.3525 16.3725 ; + RECT 4.6475 16.2375 4.7125 16.3075 ; + RECT 4.6475 17.4025 4.7125 17.5175 ; + RECT 4.4575 16.34 4.5225 17.4025 ; + RECT 4.745 16.8 4.78 16.865 ; + RECT 4.22 16.8 4.4575 16.865 ; + RECT 4.6475 16.3725 4.7125 16.5075 ; + RECT 4.4575 16.3725 4.5225 16.5075 ; + RECT 4.6475 16.9775 4.7125 17.1125 ; + RECT 4.4575 16.9775 4.5225 17.1125 ; + RECT 4.2875 17.1325 4.3525 17.2675 ; + RECT 4.2875 16.1725 4.3525 16.3075 ; + RECT 4.61 16.725 4.745 16.79 ; + RECT 4.22 16.1725 4.78 16.2375 ; + RECT 4.22 14.8275 4.78 14.8925 ; + RECT 4.2875 14.8925 4.3525 15.1425 ; + RECT 4.2875 16.0375 4.3525 16.1725 ; + RECT 4.6475 16.1025 4.7125 16.1725 ; + RECT 4.6475 14.8925 4.7125 15.0075 ; + RECT 4.4575 15.0075 4.5225 16.07 ; + RECT 4.745 15.545 4.78 15.61 ; + RECT 4.22 15.545 4.4575 15.61 ; + RECT 4.6475 15.8325 4.7125 15.9675 ; + RECT 4.4575 15.8325 4.5225 15.9675 ; + RECT 4.6475 15.0075 4.7125 15.1425 ; + RECT 4.4575 15.0075 4.5225 15.1425 ; + RECT 4.2875 15.0075 4.3525 15.1425 ; + RECT 4.2875 15.9675 4.3525 16.1025 ; + RECT 4.61 15.545 4.745 15.61 ; + RECT 4.22 18.8625 4.78 18.9275 ; + RECT 4.22 20.2075 4.78 20.2725 ; + RECT 4.2875 19.9575 4.3525 20.2075 ; + RECT 4.2875 18.9275 4.3525 19.0625 ; + RECT 4.6475 18.9275 4.7125 18.9975 ; + RECT 4.6475 20.0925 4.7125 20.2075 ; + RECT 4.4575 19.03 4.5225 20.0925 ; + RECT 4.745 19.49 4.78 19.555 ; + RECT 4.22 19.49 4.4575 19.555 ; + RECT 4.6475 19.0625 4.7125 19.1975 ; + RECT 4.4575 19.0625 4.5225 19.1975 ; + RECT 4.6475 19.6675 4.7125 19.8025 ; + RECT 4.4575 19.6675 4.5225 19.8025 ; + RECT 4.2875 19.8225 4.3525 19.9575 ; + RECT 4.2875 18.8625 4.3525 18.9975 ; + RECT 4.61 19.415 4.745 19.48 ; + RECT 4.78 13.4825 5.53 13.5475 ; + RECT 4.78 12.1375 5.53 12.2025 ; + RECT 4.8475 12.17 4.9125 12.4525 ; + RECT 4.8475 13.335 4.9125 13.515 ; + RECT 5.3975 12.17 5.4625 12.4525 ; + RECT 5.0175 12.17 5.0825 12.4525 ; + RECT 5.3975 13.2325 5.4625 13.515 ; + RECT 5.495 12.54 5.53 12.605 ; + RECT 5.255 13.07 5.53 13.135 ; + RECT 4.78 12.8525 5.0175 12.9175 ; + RECT 5.3975 13.0975 5.4625 13.2325 ; + RECT 5.2075 13.0975 5.2725 13.2325 ; + RECT 5.2075 13.0975 5.2725 13.2325 ; + RECT 5.0175 13.0975 5.0825 13.2325 ; + RECT 5.3975 12.3175 5.4625 12.4525 ; + RECT 5.2075 12.3175 5.2725 12.4525 ; + RECT 5.2075 12.3175 5.2725 12.4525 ; + RECT 5.0175 12.3175 5.0825 12.4525 ; + RECT 4.8475 12.3175 4.9125 12.4525 ; + RECT 4.8475 13.2325 4.9125 13.3675 ; + RECT 4.985 12.555 5.05 12.62 ; + RECT 5.2075 12.555 5.2725 12.62 ; + RECT 4.985 12.5875 5.05 13.3675 ; + RECT 5.0175 12.555 5.24 12.62 ; + RECT 5.2075 12.4525 5.2725 12.5875 ; + RECT 5.36 12.54 5.495 12.605 ; + RECT 5.12 13.07 5.255 13.135 ; + RECT 4.78 16.1725 5.53 16.2375 ; + RECT 4.78 17.5175 5.53 17.5825 ; + RECT 4.8475 17.2675 4.9125 17.55 ; + RECT 4.8475 16.205 4.9125 16.385 ; + RECT 5.3975 17.2675 5.4625 17.55 ; + RECT 5.0175 17.2675 5.0825 17.55 ; + RECT 5.3975 16.205 5.4625 16.4875 ; + RECT 5.495 17.115 5.53 17.18 ; + RECT 5.255 16.585 5.53 16.65 ; + RECT 4.78 16.8025 5.0175 16.8675 ; + RECT 5.3975 16.5075 5.4625 16.6425 ; + RECT 5.2075 16.5075 5.2725 16.6425 ; + RECT 5.2075 16.5075 5.2725 16.6425 ; + RECT 5.0175 16.5075 5.0825 16.6425 ; + RECT 5.3975 16.9775 5.4625 17.1125 ; + RECT 5.2075 16.9775 5.2725 17.1125 ; + RECT 5.2075 16.9775 5.2725 17.1125 ; + RECT 5.0175 16.9775 5.0825 17.1125 ; + RECT 4.8475 17.1325 4.9125 17.2675 ; + RECT 4.8475 16.2175 4.9125 16.3525 ; + RECT 4.985 15.54 5.05 15.605 ; + RECT 5.2075 15.54 5.2725 15.605 ; + RECT 4.985 15.5725 5.05 16.3525 ; + RECT 5.0175 15.54 5.24 15.605 ; + RECT 5.2075 15.4375 5.2725 15.5725 ; + RECT 5.36 17.04 5.495 17.105 ; + RECT 5.12 16.51 5.255 16.575 ; + RECT 4.78 16.1725 5.53 16.2375 ; + RECT 4.78 14.8275 5.53 14.8925 ; + RECT 4.8475 14.86 4.9125 15.1425 ; + RECT 4.8475 16.025 4.9125 16.205 ; + RECT 5.3975 14.86 5.4625 15.1425 ; + RECT 5.0175 14.86 5.0825 15.1425 ; + RECT 5.3975 15.9225 5.4625 16.205 ; + RECT 5.495 15.23 5.53 15.295 ; + RECT 5.255 15.76 5.53 15.825 ; + RECT 4.78 15.5425 5.0175 15.6075 ; + RECT 5.3975 15.7875 5.4625 15.9225 ; + RECT 5.2075 15.7875 5.2725 15.9225 ; + RECT 5.2075 15.7875 5.2725 15.9225 ; + RECT 5.0175 15.7875 5.0825 15.9225 ; + RECT 5.3975 15.0075 5.4625 15.1425 ; + RECT 5.2075 15.0075 5.2725 15.1425 ; + RECT 5.2075 15.0075 5.2725 15.1425 ; + RECT 5.0175 15.0075 5.0825 15.1425 ; + RECT 4.8475 15.0075 4.9125 15.1425 ; + RECT 4.8475 15.9225 4.9125 16.0575 ; + RECT 4.985 15.245 5.05 15.31 ; + RECT 5.2075 15.245 5.2725 15.31 ; + RECT 4.985 15.2775 5.05 16.0575 ; + RECT 5.0175 15.245 5.24 15.31 ; + RECT 5.2075 15.1425 5.2725 15.2775 ; + RECT 5.36 15.23 5.495 15.295 ; + RECT 5.12 15.76 5.255 15.825 ; + RECT 4.78 18.8625 5.53 18.9275 ; + RECT 4.78 20.2075 5.53 20.2725 ; + RECT 4.8475 19.9575 4.9125 20.24 ; + RECT 4.8475 18.895 4.9125 19.075 ; + RECT 5.3975 19.9575 5.4625 20.24 ; + RECT 5.0175 19.9575 5.0825 20.24 ; + RECT 5.3975 18.895 5.4625 19.1775 ; + RECT 5.495 19.805 5.53 19.87 ; + RECT 5.255 19.275 5.53 19.34 ; + RECT 4.78 19.4925 5.0175 19.5575 ; + RECT 5.3975 19.1975 5.4625 19.3325 ; + RECT 5.2075 19.1975 5.2725 19.3325 ; + RECT 5.2075 19.1975 5.2725 19.3325 ; + RECT 5.0175 19.1975 5.0825 19.3325 ; + RECT 5.3975 19.6675 5.4625 19.8025 ; + RECT 5.2075 19.6675 5.2725 19.8025 ; + RECT 5.2075 19.6675 5.2725 19.8025 ; + RECT 5.0175 19.6675 5.0825 19.8025 ; + RECT 4.8475 19.8225 4.9125 19.9575 ; + RECT 4.8475 18.9075 4.9125 19.0425 ; + RECT 4.985 18.23 5.05 18.295 ; + RECT 5.2075 18.23 5.2725 18.295 ; + RECT 4.985 18.2625 5.05 19.0425 ; + RECT 5.0175 18.23 5.24 18.295 ; + RECT 5.2075 18.1275 5.2725 18.2625 ; + RECT 5.36 19.73 5.495 19.795 ; + RECT 5.12 19.2 5.255 19.265 ; + RECT 6.125 14.5625 6.26 14.6275 ; + RECT 7.49 14.04 7.625 14.105 ; + RECT 5.95 14.9525 6.085 15.0175 ; + RECT 7.315 15.475 7.45 15.54 ; + RECT 6.125 14.355 6.26 14.42 ; + RECT 5.95 13.825 6.085 13.89 ; + RECT 5.775 15.16 5.91 15.225 ; + RECT 5.95 15.69 6.085 15.755 ; + RECT 6.125 17.045 6.26 17.11 ; + RECT 5.6 16.515 5.735 16.58 ; + RECT 5.775 17.85 5.91 17.915 ; + RECT 7.49 17.85 7.625 17.915 ; + RECT 5.6 18.38 5.735 18.445 ; + RECT 7.315 18.38 7.45 18.445 ; + RECT 6.3 13.4125 6.435 13.4775 ; + RECT 6.475 14.7575 6.61 14.8225 ; + RECT 6.3 16.1025 6.435 16.1675 ; + RECT 6.475 17.4475 6.61 17.5125 ; + RECT 6.3 18.615 6.435 18.68 ; + RECT 4.22 18.8625 4.97 18.9275 ; + RECT 4.22 20.2075 4.97 20.2725 ; + RECT 4.8375 19.9575 4.9025 20.24 ; + RECT 4.8375 18.895 4.9025 19.075 ; + RECT 4.2875 19.9575 4.3525 20.24 ; + RECT 4.6675 19.9575 4.7325 20.24 ; + RECT 4.2875 18.895 4.3525 19.1775 ; + RECT 4.22 19.805 4.255 19.87 ; + RECT 4.22 19.275 4.495 19.34 ; + RECT 4.7325 19.4925 4.97 19.5575 ; + RECT 4.2875 19.1775 4.3525 19.3125 ; + RECT 4.4775 19.1775 4.5425 19.3125 ; + RECT 4.4775 19.1775 4.5425 19.3125 ; + RECT 4.6675 19.1775 4.7325 19.3125 ; + RECT 4.2875 19.9575 4.3525 20.0925 ; + RECT 4.4775 19.9575 4.5425 20.0925 ; + RECT 4.4775 19.9575 4.5425 20.0925 ; + RECT 4.6675 19.9575 4.7325 20.0925 ; + RECT 4.8375 19.9575 4.9025 20.0925 ; + RECT 4.8375 19.0425 4.9025 19.1775 ; + RECT 4.7 19.79 4.765 19.855 ; + RECT 4.4775 19.79 4.5425 19.855 ; + RECT 4.7 19.0425 4.765 19.8225 ; + RECT 4.51 19.79 4.7325 19.855 ; + RECT 4.4775 19.8225 4.5425 19.9575 ; + RECT 4.255 19.805 4.39 19.87 ; + RECT 4.495 19.275 4.63 19.34 ; + RECT 4.22 21.5525 4.97 21.6175 ; + RECT 4.22 20.2075 4.97 20.2725 ; + RECT 4.8375 20.24 4.9025 20.5225 ; + RECT 4.8375 21.405 4.9025 21.585 ; + RECT 4.2875 20.24 4.3525 20.5225 ; + RECT 4.6675 20.24 4.7325 20.5225 ; + RECT 4.2875 21.3025 4.3525 21.585 ; + RECT 4.22 20.61 4.255 20.675 ; + RECT 4.22 21.14 4.495 21.205 ; + RECT 4.7325 20.9225 4.97 20.9875 ; + RECT 4.2875 21.1475 4.3525 21.2825 ; + RECT 4.4775 21.1475 4.5425 21.2825 ; + RECT 4.4775 21.1475 4.5425 21.2825 ; + RECT 4.6675 21.1475 4.7325 21.2825 ; + RECT 4.2875 20.6775 4.3525 20.8125 ; + RECT 4.4775 20.6775 4.5425 20.8125 ; + RECT 4.4775 20.6775 4.5425 20.8125 ; + RECT 4.6675 20.6775 4.7325 20.8125 ; + RECT 4.8375 20.5225 4.9025 20.6575 ; + RECT 4.8375 21.4375 4.9025 21.5725 ; + RECT 4.7 22.185 4.765 22.25 ; + RECT 4.4775 22.185 4.5425 22.25 ; + RECT 4.7 21.4375 4.765 22.2175 ; + RECT 4.51 22.185 4.7325 22.25 ; + RECT 4.4775 22.2175 4.5425 22.3525 ; + RECT 4.255 20.685 4.39 20.75 ; + RECT 4.495 21.215 4.63 21.28 ; + RECT 4.22 21.5525 4.97 21.6175 ; + RECT 4.22 22.8975 4.97 22.9625 ; + RECT 4.8375 22.6475 4.9025 22.93 ; + RECT 4.8375 21.585 4.9025 21.765 ; + RECT 4.2875 22.6475 4.3525 22.93 ; + RECT 4.6675 22.6475 4.7325 22.93 ; + RECT 4.2875 21.585 4.3525 21.8675 ; + RECT 4.22 22.495 4.255 22.56 ; + RECT 4.22 21.965 4.495 22.03 ; + RECT 4.7325 22.1825 4.97 22.2475 ; + RECT 4.2875 21.8675 4.3525 22.0025 ; + RECT 4.4775 21.8675 4.5425 22.0025 ; + RECT 4.4775 21.8675 4.5425 22.0025 ; + RECT 4.6675 21.8675 4.7325 22.0025 ; + RECT 4.2875 22.6475 4.3525 22.7825 ; + RECT 4.4775 22.6475 4.5425 22.7825 ; + RECT 4.4775 22.6475 4.5425 22.7825 ; + RECT 4.6675 22.6475 4.7325 22.7825 ; + RECT 4.8375 22.6475 4.9025 22.7825 ; + RECT 4.8375 21.7325 4.9025 21.8675 ; + RECT 4.7 22.48 4.765 22.545 ; + RECT 4.4775 22.48 4.5425 22.545 ; + RECT 4.7 21.7325 4.765 22.5125 ; + RECT 4.51 22.48 4.7325 22.545 ; + RECT 4.4775 22.5125 4.5425 22.6475 ; + RECT 4.255 22.495 4.39 22.56 ; + RECT 4.495 21.965 4.63 22.03 ; + RECT 4.22 24.2425 4.97 24.3075 ; + RECT 4.22 22.8975 4.97 22.9625 ; + RECT 4.8375 22.93 4.9025 23.2125 ; + RECT 4.8375 24.095 4.9025 24.275 ; + RECT 4.2875 22.93 4.3525 23.2125 ; + RECT 4.6675 22.93 4.7325 23.2125 ; + RECT 4.2875 23.9925 4.3525 24.275 ; + RECT 4.22 23.3 4.255 23.365 ; + RECT 4.22 23.83 4.495 23.895 ; + RECT 4.7325 23.6125 4.97 23.6775 ; + RECT 4.2875 23.8375 4.3525 23.9725 ; + RECT 4.4775 23.8375 4.5425 23.9725 ; + RECT 4.4775 23.8375 4.5425 23.9725 ; + RECT 4.6675 23.8375 4.7325 23.9725 ; + RECT 4.2875 23.3675 4.3525 23.5025 ; + RECT 4.4775 23.3675 4.5425 23.5025 ; + RECT 4.4775 23.3675 4.5425 23.5025 ; + RECT 4.6675 23.3675 4.7325 23.5025 ; + RECT 4.8375 23.2125 4.9025 23.3475 ; + RECT 4.8375 24.1275 4.9025 24.2625 ; + RECT 4.7 24.875 4.765 24.94 ; + RECT 4.4775 24.875 4.5425 24.94 ; + RECT 4.7 24.1275 4.765 24.9075 ; + RECT 4.51 24.875 4.7325 24.94 ; + RECT 4.4775 24.9075 4.5425 25.0425 ; + RECT 4.255 23.375 4.39 23.44 ; + RECT 4.495 23.905 4.63 23.97 ; + RECT 4.22 24.2425 4.97 24.3075 ; + RECT 4.22 25.5875 4.97 25.6525 ; + RECT 4.8375 25.3375 4.9025 25.62 ; + RECT 4.8375 24.275 4.9025 24.455 ; + RECT 4.2875 25.3375 4.3525 25.62 ; + RECT 4.6675 25.3375 4.7325 25.62 ; + RECT 4.2875 24.275 4.3525 24.5575 ; + RECT 4.22 25.185 4.255 25.25 ; + RECT 4.22 24.655 4.495 24.72 ; + RECT 4.7325 24.8725 4.97 24.9375 ; + RECT 4.2875 24.5575 4.3525 24.6925 ; + RECT 4.4775 24.5575 4.5425 24.6925 ; + RECT 4.4775 24.5575 4.5425 24.6925 ; + RECT 4.6675 24.5575 4.7325 24.6925 ; + RECT 4.2875 25.3375 4.3525 25.4725 ; + RECT 4.4775 25.3375 4.5425 25.4725 ; + RECT 4.4775 25.3375 4.5425 25.4725 ; + RECT 4.6675 25.3375 4.7325 25.4725 ; + RECT 4.8375 25.3375 4.9025 25.4725 ; + RECT 4.8375 24.4225 4.9025 24.5575 ; + RECT 4.7 25.17 4.765 25.235 ; + RECT 4.4775 25.17 4.5425 25.235 ; + RECT 4.7 24.4225 4.765 25.2025 ; + RECT 4.51 25.17 4.7325 25.235 ; + RECT 4.4775 25.2025 4.5425 25.3375 ; + RECT 4.255 25.185 4.39 25.25 ; + RECT 4.495 24.655 4.63 24.72 ; + RECT 4.22 26.9325 4.97 26.9975 ; + RECT 4.22 25.5875 4.97 25.6525 ; + RECT 4.8375 25.62 4.9025 25.9025 ; + RECT 4.8375 26.785 4.9025 26.965 ; + RECT 4.2875 25.62 4.3525 25.9025 ; + RECT 4.6675 25.62 4.7325 25.9025 ; + RECT 4.2875 26.6825 4.3525 26.965 ; + RECT 4.22 25.99 4.255 26.055 ; + RECT 4.22 26.52 4.495 26.585 ; + RECT 4.7325 26.3025 4.97 26.3675 ; + RECT 4.2875 26.5275 4.3525 26.6625 ; + RECT 4.4775 26.5275 4.5425 26.6625 ; + RECT 4.4775 26.5275 4.5425 26.6625 ; + RECT 4.6675 26.5275 4.7325 26.6625 ; + RECT 4.2875 26.0575 4.3525 26.1925 ; + RECT 4.4775 26.0575 4.5425 26.1925 ; + RECT 4.4775 26.0575 4.5425 26.1925 ; + RECT 4.6675 26.0575 4.7325 26.1925 ; + RECT 4.8375 25.9025 4.9025 26.0375 ; + RECT 4.8375 26.8175 4.9025 26.9525 ; + RECT 4.7 27.565 4.765 27.63 ; + RECT 4.4775 27.565 4.5425 27.63 ; + RECT 4.7 26.8175 4.765 27.5975 ; + RECT 4.51 27.565 4.7325 27.63 ; + RECT 4.4775 27.5975 4.5425 27.7325 ; + RECT 4.255 26.065 4.39 26.13 ; + RECT 4.495 26.595 4.63 26.66 ; + RECT 4.22 26.9325 4.97 26.9975 ; + RECT 4.22 28.2775 4.97 28.3425 ; + RECT 4.8375 28.0275 4.9025 28.31 ; + RECT 4.8375 26.965 4.9025 27.145 ; + RECT 4.2875 28.0275 4.3525 28.31 ; + RECT 4.6675 28.0275 4.7325 28.31 ; + RECT 4.2875 26.965 4.3525 27.2475 ; + RECT 4.22 27.875 4.255 27.94 ; + RECT 4.22 27.345 4.495 27.41 ; + RECT 4.7325 27.5625 4.97 27.6275 ; + RECT 4.2875 27.2475 4.3525 27.3825 ; + RECT 4.4775 27.2475 4.5425 27.3825 ; + RECT 4.4775 27.2475 4.5425 27.3825 ; + RECT 4.6675 27.2475 4.7325 27.3825 ; + RECT 4.2875 28.0275 4.3525 28.1625 ; + RECT 4.4775 28.0275 4.5425 28.1625 ; + RECT 4.4775 28.0275 4.5425 28.1625 ; + RECT 4.6675 28.0275 4.7325 28.1625 ; + RECT 4.8375 28.0275 4.9025 28.1625 ; + RECT 4.8375 27.1125 4.9025 27.2475 ; + RECT 4.7 27.86 4.765 27.925 ; + RECT 4.4775 27.86 4.5425 27.925 ; + RECT 4.7 27.1125 4.765 27.8925 ; + RECT 4.51 27.86 4.7325 27.925 ; + RECT 4.4775 27.8925 4.5425 28.0275 ; + RECT 4.255 27.875 4.39 27.94 ; + RECT 4.495 27.345 4.63 27.41 ; + RECT 4.22 29.6225 4.97 29.6875 ; + RECT 4.22 28.2775 4.97 28.3425 ; + RECT 4.8375 28.31 4.9025 28.5925 ; + RECT 4.8375 29.475 4.9025 29.655 ; + RECT 4.2875 28.31 4.3525 28.5925 ; + RECT 4.6675 28.31 4.7325 28.5925 ; + RECT 4.2875 29.3725 4.3525 29.655 ; + RECT 4.22 28.68 4.255 28.745 ; + RECT 4.22 29.21 4.495 29.275 ; + RECT 4.7325 28.9925 4.97 29.0575 ; + RECT 4.2875 29.2175 4.3525 29.3525 ; + RECT 4.4775 29.2175 4.5425 29.3525 ; + RECT 4.4775 29.2175 4.5425 29.3525 ; + RECT 4.6675 29.2175 4.7325 29.3525 ; + RECT 4.2875 28.7475 4.3525 28.8825 ; + RECT 4.4775 28.7475 4.5425 28.8825 ; + RECT 4.4775 28.7475 4.5425 28.8825 ; + RECT 4.6675 28.7475 4.7325 28.8825 ; + RECT 4.8375 28.5925 4.9025 28.7275 ; + RECT 4.8375 29.5075 4.9025 29.6425 ; + RECT 4.7 30.255 4.765 30.32 ; + RECT 4.4775 30.255 4.5425 30.32 ; + RECT 4.7 29.5075 4.765 30.2875 ; + RECT 4.51 30.255 4.7325 30.32 ; + RECT 4.4775 30.2875 4.5425 30.4225 ; + RECT 4.255 28.755 4.39 28.82 ; + RECT 4.495 29.285 4.63 29.35 ; + RECT 4.22 29.6225 4.97 29.6875 ; + RECT 4.22 30.9675 4.97 31.0325 ; + RECT 4.8375 30.7175 4.9025 31.0 ; + RECT 4.8375 29.655 4.9025 29.835 ; + RECT 4.2875 30.7175 4.3525 31.0 ; + RECT 4.6675 30.7175 4.7325 31.0 ; + RECT 4.2875 29.655 4.3525 29.9375 ; + RECT 4.22 30.565 4.255 30.63 ; + RECT 4.22 30.035 4.495 30.1 ; + RECT 4.7325 30.2525 4.97 30.3175 ; + RECT 4.2875 29.9375 4.3525 30.0725 ; + RECT 4.4775 29.9375 4.5425 30.0725 ; + RECT 4.4775 29.9375 4.5425 30.0725 ; + RECT 4.6675 29.9375 4.7325 30.0725 ; + RECT 4.2875 30.7175 4.3525 30.8525 ; + RECT 4.4775 30.7175 4.5425 30.8525 ; + RECT 4.4775 30.7175 4.5425 30.8525 ; + RECT 4.6675 30.7175 4.7325 30.8525 ; + RECT 4.8375 30.7175 4.9025 30.8525 ; + RECT 4.8375 29.8025 4.9025 29.9375 ; + RECT 4.7 30.55 4.765 30.615 ; + RECT 4.4775 30.55 4.5425 30.615 ; + RECT 4.7 29.8025 4.765 30.5825 ; + RECT 4.51 30.55 4.7325 30.615 ; + RECT 4.4775 30.5825 4.5425 30.7175 ; + RECT 4.255 30.565 4.39 30.63 ; + RECT 4.495 30.035 4.63 30.1 ; + RECT 4.22 32.3125 4.97 32.3775 ; + RECT 4.22 30.9675 4.97 31.0325 ; + RECT 4.8375 31.0 4.9025 31.2825 ; + RECT 4.8375 32.165 4.9025 32.345 ; + RECT 4.2875 31.0 4.3525 31.2825 ; + RECT 4.6675 31.0 4.7325 31.2825 ; + RECT 4.2875 32.0625 4.3525 32.345 ; + RECT 4.22 31.37 4.255 31.435 ; + RECT 4.22 31.9 4.495 31.965 ; + RECT 4.7325 31.6825 4.97 31.7475 ; + RECT 4.2875 31.9075 4.3525 32.0425 ; + RECT 4.4775 31.9075 4.5425 32.0425 ; + RECT 4.4775 31.9075 4.5425 32.0425 ; + RECT 4.6675 31.9075 4.7325 32.0425 ; + RECT 4.2875 31.4375 4.3525 31.5725 ; + RECT 4.4775 31.4375 4.5425 31.5725 ; + RECT 4.4775 31.4375 4.5425 31.5725 ; + RECT 4.6675 31.4375 4.7325 31.5725 ; + RECT 4.8375 31.2825 4.9025 31.4175 ; + RECT 4.8375 32.1975 4.9025 32.3325 ; + RECT 4.7 32.945 4.765 33.01 ; + RECT 4.4775 32.945 4.5425 33.01 ; + RECT 4.7 32.1975 4.765 32.9775 ; + RECT 4.51 32.945 4.7325 33.01 ; + RECT 4.4775 32.9775 4.5425 33.1125 ; + RECT 4.255 31.445 4.39 31.51 ; + RECT 4.495 31.975 4.63 32.04 ; + RECT 4.22 32.3125 4.97 32.3775 ; + RECT 4.22 33.6575 4.97 33.7225 ; + RECT 4.8375 33.4075 4.9025 33.69 ; + RECT 4.8375 32.345 4.9025 32.525 ; + RECT 4.2875 33.4075 4.3525 33.69 ; + RECT 4.6675 33.4075 4.7325 33.69 ; + RECT 4.2875 32.345 4.3525 32.6275 ; + RECT 4.22 33.255 4.255 33.32 ; + RECT 4.22 32.725 4.495 32.79 ; + RECT 4.7325 32.9425 4.97 33.0075 ; + RECT 4.2875 32.6275 4.3525 32.7625 ; + RECT 4.4775 32.6275 4.5425 32.7625 ; + RECT 4.4775 32.6275 4.5425 32.7625 ; + RECT 4.6675 32.6275 4.7325 32.7625 ; + RECT 4.2875 33.4075 4.3525 33.5425 ; + RECT 4.4775 33.4075 4.5425 33.5425 ; + RECT 4.4775 33.4075 4.5425 33.5425 ; + RECT 4.6675 33.4075 4.7325 33.5425 ; + RECT 4.8375 33.4075 4.9025 33.5425 ; + RECT 4.8375 32.4925 4.9025 32.6275 ; + RECT 4.7 33.24 4.765 33.305 ; + RECT 4.4775 33.24 4.5425 33.305 ; + RECT 4.7 32.4925 4.765 33.2725 ; + RECT 4.51 33.24 4.7325 33.305 ; + RECT 4.4775 33.2725 4.5425 33.4075 ; + RECT 4.255 33.255 4.39 33.32 ; + RECT 4.495 32.725 4.63 32.79 ; + RECT 4.22 35.0025 4.97 35.0675 ; + RECT 4.22 33.6575 4.97 33.7225 ; + RECT 4.8375 33.69 4.9025 33.9725 ; + RECT 4.8375 34.855 4.9025 35.035 ; + RECT 4.2875 33.69 4.3525 33.9725 ; + RECT 4.6675 33.69 4.7325 33.9725 ; + RECT 4.2875 34.7525 4.3525 35.035 ; + RECT 4.22 34.06 4.255 34.125 ; + RECT 4.22 34.59 4.495 34.655 ; + RECT 4.7325 34.3725 4.97 34.4375 ; + RECT 4.2875 34.5975 4.3525 34.7325 ; + RECT 4.4775 34.5975 4.5425 34.7325 ; + RECT 4.4775 34.5975 4.5425 34.7325 ; + RECT 4.6675 34.5975 4.7325 34.7325 ; + RECT 4.2875 34.1275 4.3525 34.2625 ; + RECT 4.4775 34.1275 4.5425 34.2625 ; + RECT 4.4775 34.1275 4.5425 34.2625 ; + RECT 4.6675 34.1275 4.7325 34.2625 ; + RECT 4.8375 33.9725 4.9025 34.1075 ; + RECT 4.8375 34.8875 4.9025 35.0225 ; + RECT 4.7 35.635 4.765 35.7 ; + RECT 4.4775 35.635 4.5425 35.7 ; + RECT 4.7 34.8875 4.765 35.6675 ; + RECT 4.51 35.635 4.7325 35.7 ; + RECT 4.4775 35.6675 4.5425 35.8025 ; + RECT 4.255 34.135 4.39 34.2 ; + RECT 4.495 34.665 4.63 34.73 ; + RECT 4.22 35.0025 4.97 35.0675 ; + RECT 4.22 36.3475 4.97 36.4125 ; + RECT 4.8375 36.0975 4.9025 36.38 ; + RECT 4.8375 35.035 4.9025 35.215 ; + RECT 4.2875 36.0975 4.3525 36.38 ; + RECT 4.6675 36.0975 4.7325 36.38 ; + RECT 4.2875 35.035 4.3525 35.3175 ; + RECT 4.22 35.945 4.255 36.01 ; + RECT 4.22 35.415 4.495 35.48 ; + RECT 4.7325 35.6325 4.97 35.6975 ; + RECT 4.2875 35.3175 4.3525 35.4525 ; + RECT 4.4775 35.3175 4.5425 35.4525 ; + RECT 4.4775 35.3175 4.5425 35.4525 ; + RECT 4.6675 35.3175 4.7325 35.4525 ; + RECT 4.2875 36.0975 4.3525 36.2325 ; + RECT 4.4775 36.0975 4.5425 36.2325 ; + RECT 4.4775 36.0975 4.5425 36.2325 ; + RECT 4.6675 36.0975 4.7325 36.2325 ; + RECT 4.8375 36.0975 4.9025 36.2325 ; + RECT 4.8375 35.1825 4.9025 35.3175 ; + RECT 4.7 35.93 4.765 35.995 ; + RECT 4.4775 35.93 4.5425 35.995 ; + RECT 4.7 35.1825 4.765 35.9625 ; + RECT 4.51 35.93 4.7325 35.995 ; + RECT 4.4775 35.9625 4.5425 36.0975 ; + RECT 4.255 35.945 4.39 36.01 ; + RECT 4.495 35.415 4.63 35.48 ; + RECT 4.22 37.6925 4.97 37.7575 ; + RECT 4.22 36.3475 4.97 36.4125 ; + RECT 4.8375 36.38 4.9025 36.6625 ; + RECT 4.8375 37.545 4.9025 37.725 ; + RECT 4.2875 36.38 4.3525 36.6625 ; + RECT 4.6675 36.38 4.7325 36.6625 ; + RECT 4.2875 37.4425 4.3525 37.725 ; + RECT 4.22 36.75 4.255 36.815 ; + RECT 4.22 37.28 4.495 37.345 ; + RECT 4.7325 37.0625 4.97 37.1275 ; + RECT 4.2875 37.2875 4.3525 37.4225 ; + RECT 4.4775 37.2875 4.5425 37.4225 ; + RECT 4.4775 37.2875 4.5425 37.4225 ; + RECT 4.6675 37.2875 4.7325 37.4225 ; + RECT 4.2875 36.8175 4.3525 36.9525 ; + RECT 4.4775 36.8175 4.5425 36.9525 ; + RECT 4.4775 36.8175 4.5425 36.9525 ; + RECT 4.6675 36.8175 4.7325 36.9525 ; + RECT 4.8375 36.6625 4.9025 36.7975 ; + RECT 4.8375 37.5775 4.9025 37.7125 ; + RECT 4.7 38.325 4.765 38.39 ; + RECT 4.4775 38.325 4.5425 38.39 ; + RECT 4.7 37.5775 4.765 38.3575 ; + RECT 4.51 38.325 4.7325 38.39 ; + RECT 4.4775 38.3575 4.5425 38.4925 ; + RECT 4.255 36.825 4.39 36.89 ; + RECT 4.495 37.355 4.63 37.42 ; + RECT 4.22 37.6925 4.97 37.7575 ; + RECT 4.22 39.0375 4.97 39.1025 ; + RECT 4.8375 38.7875 4.9025 39.07 ; + RECT 4.8375 37.725 4.9025 37.905 ; + RECT 4.2875 38.7875 4.3525 39.07 ; + RECT 4.6675 38.7875 4.7325 39.07 ; + RECT 4.2875 37.725 4.3525 38.0075 ; + RECT 4.22 38.635 4.255 38.7 ; + RECT 4.22 38.105 4.495 38.17 ; + RECT 4.7325 38.3225 4.97 38.3875 ; + RECT 4.2875 38.0075 4.3525 38.1425 ; + RECT 4.4775 38.0075 4.5425 38.1425 ; + RECT 4.4775 38.0075 4.5425 38.1425 ; + RECT 4.6675 38.0075 4.7325 38.1425 ; + RECT 4.2875 38.7875 4.3525 38.9225 ; + RECT 4.4775 38.7875 4.5425 38.9225 ; + RECT 4.4775 38.7875 4.5425 38.9225 ; + RECT 4.6675 38.7875 4.7325 38.9225 ; + RECT 4.8375 38.7875 4.9025 38.9225 ; + RECT 4.8375 37.8725 4.9025 38.0075 ; + RECT 4.7 38.62 4.765 38.685 ; + RECT 4.4775 38.62 4.5425 38.685 ; + RECT 4.7 37.8725 4.765 38.6525 ; + RECT 4.51 38.62 4.7325 38.685 ; + RECT 4.4775 38.6525 4.5425 38.7875 ; + RECT 4.255 38.635 4.39 38.7 ; + RECT 4.495 38.105 4.63 38.17 ; + RECT 4.22 40.3825 4.97 40.4475 ; + RECT 4.22 39.0375 4.97 39.1025 ; + RECT 4.8375 39.07 4.9025 39.3525 ; + RECT 4.8375 40.235 4.9025 40.415 ; + RECT 4.2875 39.07 4.3525 39.3525 ; + RECT 4.6675 39.07 4.7325 39.3525 ; + RECT 4.2875 40.1325 4.3525 40.415 ; + RECT 4.22 39.44 4.255 39.505 ; + RECT 4.22 39.97 4.495 40.035 ; + RECT 4.7325 39.7525 4.97 39.8175 ; + RECT 4.2875 39.9775 4.3525 40.1125 ; + RECT 4.4775 39.9775 4.5425 40.1125 ; + RECT 4.4775 39.9775 4.5425 40.1125 ; + RECT 4.6675 39.9775 4.7325 40.1125 ; + RECT 4.2875 39.5075 4.3525 39.6425 ; + RECT 4.4775 39.5075 4.5425 39.6425 ; + RECT 4.4775 39.5075 4.5425 39.6425 ; + RECT 4.6675 39.5075 4.7325 39.6425 ; + RECT 4.8375 39.3525 4.9025 39.4875 ; + RECT 4.8375 40.2675 4.9025 40.4025 ; + RECT 4.7 41.015 4.765 41.08 ; + RECT 4.4775 41.015 4.5425 41.08 ; + RECT 4.7 40.2675 4.765 41.0475 ; + RECT 4.51 41.015 4.7325 41.08 ; + RECT 4.4775 41.0475 4.5425 41.1825 ; + RECT 4.255 39.515 4.39 39.58 ; + RECT 4.495 40.045 4.63 40.11 ; + RECT 4.97 18.8625 5.53 18.9275 ; + RECT 4.97 20.2075 5.53 20.2725 ; + RECT 5.3975 19.9575 5.4625 20.2075 ; + RECT 5.3975 18.9275 5.4625 19.0625 ; + RECT 5.0375 18.9275 5.1025 18.9975 ; + RECT 5.0375 20.0925 5.1025 20.2075 ; + RECT 5.2275 19.03 5.2925 20.0925 ; + RECT 4.97 19.49 5.005 19.555 ; + RECT 5.2925 19.49 5.53 19.555 ; + RECT 5.0375 19.1325 5.1025 19.2675 ; + RECT 5.2275 19.1325 5.2925 19.2675 ; + RECT 5.0375 19.9575 5.1025 20.0925 ; + RECT 5.2275 19.9575 5.2925 20.0925 ; + RECT 5.3975 19.9575 5.4625 20.0925 ; + RECT 5.3975 18.9975 5.4625 19.1325 ; + RECT 5.005 19.49 5.14 19.555 ; + RECT 4.97 21.5525 5.53 21.6175 ; + RECT 4.97 20.2075 5.53 20.2725 ; + RECT 5.3975 20.2725 5.4625 20.5225 ; + RECT 5.3975 21.4175 5.4625 21.5525 ; + RECT 5.0375 21.4825 5.1025 21.5525 ; + RECT 5.0375 20.2725 5.1025 20.3875 ; + RECT 5.2275 20.3875 5.2925 21.45 ; + RECT 4.97 20.925 5.005 20.99 ; + RECT 5.2925 20.925 5.53 20.99 ; + RECT 5.0375 21.2825 5.1025 21.4175 ; + RECT 5.2275 21.2825 5.2925 21.4175 ; + RECT 5.0375 20.6775 5.1025 20.8125 ; + RECT 5.2275 20.6775 5.2925 20.8125 ; + RECT 5.3975 20.5225 5.4625 20.6575 ; + RECT 5.3975 21.4825 5.4625 21.6175 ; + RECT 5.005 21.0 5.14 21.065 ; + RECT 4.97 21.5525 5.53 21.6175 ; + RECT 4.97 22.8975 5.53 22.9625 ; + RECT 5.3975 22.6475 5.4625 22.8975 ; + RECT 5.3975 21.6175 5.4625 21.7525 ; + RECT 5.0375 21.6175 5.1025 21.6875 ; + RECT 5.0375 22.7825 5.1025 22.8975 ; + RECT 5.2275 21.72 5.2925 22.7825 ; + RECT 4.97 22.18 5.005 22.245 ; + RECT 5.2925 22.18 5.53 22.245 ; + RECT 5.0375 21.8225 5.1025 21.9575 ; + RECT 5.2275 21.8225 5.2925 21.9575 ; + RECT 5.0375 22.6475 5.1025 22.7825 ; + RECT 5.2275 22.6475 5.2925 22.7825 ; + RECT 5.3975 22.6475 5.4625 22.7825 ; + RECT 5.3975 21.6875 5.4625 21.8225 ; + RECT 5.005 22.18 5.14 22.245 ; + RECT 4.97 24.2425 5.53 24.3075 ; + RECT 4.97 22.8975 5.53 22.9625 ; + RECT 5.3975 22.9625 5.4625 23.2125 ; + RECT 5.3975 24.1075 5.4625 24.2425 ; + RECT 5.0375 24.1725 5.1025 24.2425 ; + RECT 5.0375 22.9625 5.1025 23.0775 ; + RECT 5.2275 23.0775 5.2925 24.14 ; + RECT 4.97 23.615 5.005 23.68 ; + RECT 5.2925 23.615 5.53 23.68 ; + RECT 5.0375 23.9725 5.1025 24.1075 ; + RECT 5.2275 23.9725 5.2925 24.1075 ; + RECT 5.0375 23.3675 5.1025 23.5025 ; + RECT 5.2275 23.3675 5.2925 23.5025 ; + RECT 5.3975 23.2125 5.4625 23.3475 ; + RECT 5.3975 24.1725 5.4625 24.3075 ; + RECT 5.005 23.69 5.14 23.755 ; + RECT 4.97 24.2425 5.53 24.3075 ; + RECT 4.97 25.5875 5.53 25.6525 ; + RECT 5.3975 25.3375 5.4625 25.5875 ; + RECT 5.3975 24.3075 5.4625 24.4425 ; + RECT 5.0375 24.3075 5.1025 24.3775 ; + RECT 5.0375 25.4725 5.1025 25.5875 ; + RECT 5.2275 24.41 5.2925 25.4725 ; + RECT 4.97 24.87 5.005 24.935 ; + RECT 5.2925 24.87 5.53 24.935 ; + RECT 5.0375 24.5125 5.1025 24.6475 ; + RECT 5.2275 24.5125 5.2925 24.6475 ; + RECT 5.0375 25.3375 5.1025 25.4725 ; + RECT 5.2275 25.3375 5.2925 25.4725 ; + RECT 5.3975 25.3375 5.4625 25.4725 ; + RECT 5.3975 24.3775 5.4625 24.5125 ; + RECT 5.005 24.87 5.14 24.935 ; + RECT 4.97 26.9325 5.53 26.9975 ; + RECT 4.97 25.5875 5.53 25.6525 ; + RECT 5.3975 25.6525 5.4625 25.9025 ; + RECT 5.3975 26.7975 5.4625 26.9325 ; + RECT 5.0375 26.8625 5.1025 26.9325 ; + RECT 5.0375 25.6525 5.1025 25.7675 ; + RECT 5.2275 25.7675 5.2925 26.83 ; + RECT 4.97 26.305 5.005 26.37 ; + RECT 5.2925 26.305 5.53 26.37 ; + RECT 5.0375 26.6625 5.1025 26.7975 ; + RECT 5.2275 26.6625 5.2925 26.7975 ; + RECT 5.0375 26.0575 5.1025 26.1925 ; + RECT 5.2275 26.0575 5.2925 26.1925 ; + RECT 5.3975 25.9025 5.4625 26.0375 ; + RECT 5.3975 26.8625 5.4625 26.9975 ; + RECT 5.005 26.38 5.14 26.445 ; + RECT 4.97 26.9325 5.53 26.9975 ; + RECT 4.97 28.2775 5.53 28.3425 ; + RECT 5.3975 28.0275 5.4625 28.2775 ; + RECT 5.3975 26.9975 5.4625 27.1325 ; + RECT 5.0375 26.9975 5.1025 27.0675 ; + RECT 5.0375 28.1625 5.1025 28.2775 ; + RECT 5.2275 27.1 5.2925 28.1625 ; + RECT 4.97 27.56 5.005 27.625 ; + RECT 5.2925 27.56 5.53 27.625 ; + RECT 5.0375 27.2025 5.1025 27.3375 ; + RECT 5.2275 27.2025 5.2925 27.3375 ; + RECT 5.0375 28.0275 5.1025 28.1625 ; + RECT 5.2275 28.0275 5.2925 28.1625 ; + RECT 5.3975 28.0275 5.4625 28.1625 ; + RECT 5.3975 27.0675 5.4625 27.2025 ; + RECT 5.005 27.56 5.14 27.625 ; + RECT 4.97 29.6225 5.53 29.6875 ; + RECT 4.97 28.2775 5.53 28.3425 ; + RECT 5.3975 28.3425 5.4625 28.5925 ; + RECT 5.3975 29.4875 5.4625 29.6225 ; + RECT 5.0375 29.5525 5.1025 29.6225 ; + RECT 5.0375 28.3425 5.1025 28.4575 ; + RECT 5.2275 28.4575 5.2925 29.52 ; + RECT 4.97 28.995 5.005 29.06 ; + RECT 5.2925 28.995 5.53 29.06 ; + RECT 5.0375 29.3525 5.1025 29.4875 ; + RECT 5.2275 29.3525 5.2925 29.4875 ; + RECT 5.0375 28.7475 5.1025 28.8825 ; + RECT 5.2275 28.7475 5.2925 28.8825 ; + RECT 5.3975 28.5925 5.4625 28.7275 ; + RECT 5.3975 29.5525 5.4625 29.6875 ; + RECT 5.005 29.07 5.14 29.135 ; + RECT 4.97 29.6225 5.53 29.6875 ; + RECT 4.97 30.9675 5.53 31.0325 ; + RECT 5.3975 30.7175 5.4625 30.9675 ; + RECT 5.3975 29.6875 5.4625 29.8225 ; + RECT 5.0375 29.6875 5.1025 29.7575 ; + RECT 5.0375 30.8525 5.1025 30.9675 ; + RECT 5.2275 29.79 5.2925 30.8525 ; + RECT 4.97 30.25 5.005 30.315 ; + RECT 5.2925 30.25 5.53 30.315 ; + RECT 5.0375 29.8925 5.1025 30.0275 ; + RECT 5.2275 29.8925 5.2925 30.0275 ; + RECT 5.0375 30.7175 5.1025 30.8525 ; + RECT 5.2275 30.7175 5.2925 30.8525 ; + RECT 5.3975 30.7175 5.4625 30.8525 ; + RECT 5.3975 29.7575 5.4625 29.8925 ; + RECT 5.005 30.25 5.14 30.315 ; + RECT 4.97 32.3125 5.53 32.3775 ; + RECT 4.97 30.9675 5.53 31.0325 ; + RECT 5.3975 31.0325 5.4625 31.2825 ; + RECT 5.3975 32.1775 5.4625 32.3125 ; + RECT 5.0375 32.2425 5.1025 32.3125 ; + RECT 5.0375 31.0325 5.1025 31.1475 ; + RECT 5.2275 31.1475 5.2925 32.21 ; + RECT 4.97 31.685 5.005 31.75 ; + RECT 5.2925 31.685 5.53 31.75 ; + RECT 5.0375 32.0425 5.1025 32.1775 ; + RECT 5.2275 32.0425 5.2925 32.1775 ; + RECT 5.0375 31.4375 5.1025 31.5725 ; + RECT 5.2275 31.4375 5.2925 31.5725 ; + RECT 5.3975 31.2825 5.4625 31.4175 ; + RECT 5.3975 32.2425 5.4625 32.3775 ; + RECT 5.005 31.76 5.14 31.825 ; + RECT 4.97 32.3125 5.53 32.3775 ; + RECT 4.97 33.6575 5.53 33.7225 ; + RECT 5.3975 33.4075 5.4625 33.6575 ; + RECT 5.3975 32.3775 5.4625 32.5125 ; + RECT 5.0375 32.3775 5.1025 32.4475 ; + RECT 5.0375 33.5425 5.1025 33.6575 ; + RECT 5.2275 32.48 5.2925 33.5425 ; + RECT 4.97 32.94 5.005 33.005 ; + RECT 5.2925 32.94 5.53 33.005 ; + RECT 5.0375 32.5825 5.1025 32.7175 ; + RECT 5.2275 32.5825 5.2925 32.7175 ; + RECT 5.0375 33.4075 5.1025 33.5425 ; + RECT 5.2275 33.4075 5.2925 33.5425 ; + RECT 5.3975 33.4075 5.4625 33.5425 ; + RECT 5.3975 32.4475 5.4625 32.5825 ; + RECT 5.005 32.94 5.14 33.005 ; + RECT 4.97 35.0025 5.53 35.0675 ; + RECT 4.97 33.6575 5.53 33.7225 ; + RECT 5.3975 33.7225 5.4625 33.9725 ; + RECT 5.3975 34.8675 5.4625 35.0025 ; + RECT 5.0375 34.9325 5.1025 35.0025 ; + RECT 5.0375 33.7225 5.1025 33.8375 ; + RECT 5.2275 33.8375 5.2925 34.9 ; + RECT 4.97 34.375 5.005 34.44 ; + RECT 5.2925 34.375 5.53 34.44 ; + RECT 5.0375 34.7325 5.1025 34.8675 ; + RECT 5.2275 34.7325 5.2925 34.8675 ; + RECT 5.0375 34.1275 5.1025 34.2625 ; + RECT 5.2275 34.1275 5.2925 34.2625 ; + RECT 5.3975 33.9725 5.4625 34.1075 ; + RECT 5.3975 34.9325 5.4625 35.0675 ; + RECT 5.005 34.45 5.14 34.515 ; + RECT 4.97 35.0025 5.53 35.0675 ; + RECT 4.97 36.3475 5.53 36.4125 ; + RECT 5.3975 36.0975 5.4625 36.3475 ; + RECT 5.3975 35.0675 5.4625 35.2025 ; + RECT 5.0375 35.0675 5.1025 35.1375 ; + RECT 5.0375 36.2325 5.1025 36.3475 ; + RECT 5.2275 35.17 5.2925 36.2325 ; + RECT 4.97 35.63 5.005 35.695 ; + RECT 5.2925 35.63 5.53 35.695 ; + RECT 5.0375 35.2725 5.1025 35.4075 ; + RECT 5.2275 35.2725 5.2925 35.4075 ; + RECT 5.0375 36.0975 5.1025 36.2325 ; + RECT 5.2275 36.0975 5.2925 36.2325 ; + RECT 5.3975 36.0975 5.4625 36.2325 ; + RECT 5.3975 35.1375 5.4625 35.2725 ; + RECT 5.005 35.63 5.14 35.695 ; + RECT 4.97 37.6925 5.53 37.7575 ; + RECT 4.97 36.3475 5.53 36.4125 ; + RECT 5.3975 36.4125 5.4625 36.6625 ; + RECT 5.3975 37.5575 5.4625 37.6925 ; + RECT 5.0375 37.6225 5.1025 37.6925 ; + RECT 5.0375 36.4125 5.1025 36.5275 ; + RECT 5.2275 36.5275 5.2925 37.59 ; + RECT 4.97 37.065 5.005 37.13 ; + RECT 5.2925 37.065 5.53 37.13 ; + RECT 5.0375 37.4225 5.1025 37.5575 ; + RECT 5.2275 37.4225 5.2925 37.5575 ; + RECT 5.0375 36.8175 5.1025 36.9525 ; + RECT 5.2275 36.8175 5.2925 36.9525 ; + RECT 5.3975 36.6625 5.4625 36.7975 ; + RECT 5.3975 37.6225 5.4625 37.7575 ; + RECT 5.005 37.14 5.14 37.205 ; + RECT 4.97 37.6925 5.53 37.7575 ; + RECT 4.97 39.0375 5.53 39.1025 ; + RECT 5.3975 38.7875 5.4625 39.0375 ; + RECT 5.3975 37.7575 5.4625 37.8925 ; + RECT 5.0375 37.7575 5.1025 37.8275 ; + RECT 5.0375 38.9225 5.1025 39.0375 ; + RECT 5.2275 37.86 5.2925 38.9225 ; + RECT 4.97 38.32 5.005 38.385 ; + RECT 5.2925 38.32 5.53 38.385 ; + RECT 5.0375 37.9625 5.1025 38.0975 ; + RECT 5.2275 37.9625 5.2925 38.0975 ; + RECT 5.0375 38.7875 5.1025 38.9225 ; + RECT 5.2275 38.7875 5.2925 38.9225 ; + RECT 5.3975 38.7875 5.4625 38.9225 ; + RECT 5.3975 37.8275 5.4625 37.9625 ; + RECT 5.005 38.32 5.14 38.385 ; + RECT 4.97 40.3825 5.53 40.4475 ; + RECT 4.97 39.0375 5.53 39.1025 ; + RECT 5.3975 39.1025 5.4625 39.3525 ; + RECT 5.3975 40.2475 5.4625 40.3825 ; + RECT 5.0375 40.3125 5.1025 40.3825 ; + RECT 5.0375 39.1025 5.1025 39.2175 ; + RECT 5.2275 39.2175 5.2925 40.28 ; + RECT 4.97 39.755 5.005 39.82 ; + RECT 5.2925 39.755 5.53 39.82 ; + RECT 5.0375 40.1125 5.1025 40.2475 ; + RECT 5.2275 40.1125 5.2925 40.2475 ; + RECT 5.0375 39.5075 5.1025 39.6425 ; + RECT 5.2275 39.5075 5.2925 39.6425 ; + RECT 5.3975 39.3525 5.4625 39.4875 ; + RECT 5.3975 40.3125 5.4625 40.4475 ; + RECT 5.005 39.83 5.14 39.895 ; + RECT 2.79 8.73 2.925 8.795 ; + RECT 2.965 10.165 3.1 10.23 ; + RECT 3.14 11.42 3.275 11.485 ; + RECT 3.315 12.855 3.45 12.92 ; + RECT 3.49 14.11 3.625 14.175 ; + RECT 3.665 15.545 3.8 15.61 ; + RECT 3.84 16.8 3.975 16.865 ; + RECT 4.015 18.235 4.15 18.3 ; + RECT 2.79 19.805 2.925 19.87 ; + RECT 3.49 19.275 3.625 19.34 ; + RECT 2.79 20.61 2.925 20.675 ; + RECT 3.665 21.14 3.8 21.205 ; + RECT 2.79 22.495 2.925 22.56 ; + RECT 3.84 21.965 3.975 22.03 ; + RECT 2.79 23.3 2.925 23.365 ; + RECT 4.015 23.83 4.15 23.895 ; + RECT 2.965 25.185 3.1 25.25 ; + RECT 3.49 24.655 3.625 24.72 ; + RECT 2.965 25.99 3.1 26.055 ; + RECT 3.665 26.52 3.8 26.585 ; + RECT 2.965 27.875 3.1 27.94 ; + RECT 3.84 27.345 3.975 27.41 ; + RECT 2.965 28.68 3.1 28.745 ; + RECT 4.015 29.21 4.15 29.275 ; + RECT 3.14 30.565 3.275 30.63 ; + RECT 3.49 30.035 3.625 30.1 ; + RECT 3.14 31.37 3.275 31.435 ; + RECT 3.665 31.9 3.8 31.965 ; + RECT 3.14 33.255 3.275 33.32 ; + RECT 3.84 32.725 3.975 32.79 ; + RECT 3.14 34.06 3.275 34.125 ; + RECT 4.015 34.59 4.15 34.655 ; + RECT 3.315 35.945 3.45 36.01 ; + RECT 3.49 35.415 3.625 35.48 ; + RECT 3.315 36.75 3.45 36.815 ; + RECT 3.665 37.28 3.8 37.345 ; + RECT 3.315 38.635 3.45 38.7 ; + RECT 3.84 38.105 3.975 38.17 ; + RECT 3.315 39.44 3.45 39.505 ; + RECT 4.015 39.97 4.15 40.035 ; + RECT 5.725 19.025 5.79 40.805 ; + RECT 5.725 19.445 6.05 19.51 ; + RECT 6.48 19.275 6.545 19.51 ; + RECT 7.295 19.4925 7.36 19.5575 ; + RECT 7.855 19.445 7.92 19.51 ; + RECT 5.725 20.97 6.05 21.035 ; + RECT 6.48 20.97 6.545 21.205 ; + RECT 7.295 20.9225 7.36 20.9875 ; + RECT 7.79 20.97 7.855 21.035 ; + RECT 5.725 22.135 6.05 22.2 ; + RECT 6.48 21.965 6.545 22.2 ; + RECT 7.295 22.1825 7.36 22.2475 ; + RECT 7.855 22.135 7.92 22.2 ; + RECT 5.725 23.66 6.05 23.725 ; + RECT 6.48 23.66 6.545 23.895 ; + RECT 7.295 23.6125 7.36 23.6775 ; + RECT 7.79 23.66 7.855 23.725 ; + RECT 5.725 24.825 6.05 24.89 ; + RECT 6.48 24.655 6.545 24.89 ; + RECT 7.295 24.8725 7.36 24.9375 ; + RECT 7.855 24.825 7.92 24.89 ; + RECT 5.725 26.35 6.05 26.415 ; + RECT 6.48 26.35 6.545 26.585 ; + RECT 7.295 26.3025 7.36 26.3675 ; + RECT 7.79 26.35 7.855 26.415 ; + RECT 5.725 27.515 6.05 27.58 ; + RECT 6.48 27.345 6.545 27.58 ; + RECT 7.295 27.5625 7.36 27.6275 ; + RECT 7.855 27.515 7.92 27.58 ; + RECT 5.725 29.04 6.05 29.105 ; + RECT 6.48 29.04 6.545 29.275 ; + RECT 7.295 28.9925 7.36 29.0575 ; + RECT 7.79 29.04 7.855 29.105 ; + RECT 5.725 30.205 6.05 30.27 ; + RECT 6.48 30.035 6.545 30.27 ; + RECT 7.295 30.2525 7.36 30.3175 ; + RECT 7.855 30.205 7.92 30.27 ; + RECT 5.725 31.73 6.05 31.795 ; + RECT 6.48 31.73 6.545 31.965 ; + RECT 7.295 31.6825 7.36 31.7475 ; + RECT 7.79 31.73 7.855 31.795 ; + RECT 5.725 32.895 6.05 32.96 ; + RECT 6.48 32.725 6.545 32.96 ; + RECT 7.295 32.9425 7.36 33.0075 ; + RECT 7.855 32.895 7.92 32.96 ; + RECT 5.725 34.42 6.05 34.485 ; + RECT 6.48 34.42 6.545 34.655 ; + RECT 7.295 34.3725 7.36 34.4375 ; + RECT 7.79 34.42 7.855 34.485 ; + RECT 5.725 35.585 6.05 35.65 ; + RECT 6.48 35.415 6.545 35.65 ; + RECT 7.295 35.6325 7.36 35.6975 ; + RECT 7.855 35.585 7.92 35.65 ; + RECT 5.725 37.11 6.05 37.175 ; + RECT 6.48 37.11 6.545 37.345 ; + RECT 7.295 37.0625 7.36 37.1275 ; + RECT 7.79 37.11 7.855 37.175 ; + RECT 5.725 38.275 6.05 38.34 ; + RECT 6.48 38.105 6.545 38.34 ; + RECT 7.295 38.3225 7.36 38.3875 ; + RECT 7.855 38.275 7.92 38.34 ; + RECT 5.725 39.8 6.05 39.865 ; + RECT 6.48 39.8 6.545 40.035 ; + RECT 7.295 39.7525 7.36 39.8175 ; + RECT 7.79 39.8 7.855 39.865 ; + RECT 5.46 20.2075 5.595 20.2725 ; + RECT 5.915 20.2075 6.05 20.2725 ; + RECT 5.985 18.8625 6.545 18.9275 ; + RECT 5.985 20.2075 6.545 20.2725 ; + RECT 6.4125 19.9575 6.4775 20.2075 ; + RECT 6.4125 18.9275 6.4775 19.0625 ; + RECT 6.0525 18.9275 6.1175 18.9975 ; + RECT 6.0525 20.0925 6.1175 20.2075 ; + RECT 6.2425 19.03 6.3075 20.0925 ; + RECT 5.985 19.49 6.02 19.555 ; + RECT 6.3075 19.49 6.545 19.555 ; + RECT 6.0525 19.1325 6.1175 19.2675 ; + RECT 6.2425 19.1325 6.3075 19.2675 ; + RECT 6.0525 19.9575 6.1175 20.0925 ; + RECT 6.2425 19.9575 6.3075 20.0925 ; + RECT 6.4125 19.9575 6.4775 20.0925 ; + RECT 6.4125 18.9975 6.4775 19.1325 ; + RECT 6.02 19.49 6.155 19.555 ; + RECT 6.545 18.8625 7.295 18.9275 ; + RECT 6.545 20.2075 7.295 20.2725 ; + RECT 7.1625 19.9575 7.2275 20.24 ; + RECT 7.1625 18.895 7.2275 19.075 ; + RECT 6.6125 19.9575 6.6775 20.24 ; + RECT 6.9925 19.9575 7.0575 20.24 ; + RECT 6.6125 18.895 6.6775 19.1775 ; + RECT 6.545 19.805 6.58 19.87 ; + RECT 6.545 19.275 6.82 19.34 ; + RECT 7.0575 19.4925 7.295 19.5575 ; + RECT 6.6125 19.1775 6.6775 19.3125 ; + RECT 6.8025 19.1775 6.8675 19.3125 ; + RECT 6.8025 19.1775 6.8675 19.3125 ; + RECT 6.9925 19.1775 7.0575 19.3125 ; + RECT 6.6125 19.9575 6.6775 20.0925 ; + RECT 6.8025 19.9575 6.8675 20.0925 ; + RECT 6.8025 19.9575 6.8675 20.0925 ; + RECT 6.9925 19.9575 7.0575 20.0925 ; + RECT 7.1625 19.9575 7.2275 20.0925 ; + RECT 7.1625 19.0425 7.2275 19.1775 ; + RECT 7.025 19.79 7.09 19.855 ; + RECT 6.8025 19.79 6.8675 19.855 ; + RECT 7.025 19.0425 7.09 19.8225 ; + RECT 6.835 19.79 7.0575 19.855 ; + RECT 6.8025 19.8225 6.8675 19.9575 ; + RECT 6.58 19.805 6.715 19.87 ; + RECT 6.82 19.275 6.955 19.34 ; + RECT 7.295 18.8625 7.855 18.9275 ; + RECT 7.295 20.2075 7.855 20.2725 ; + RECT 7.7225 19.9575 7.7875 20.2075 ; + RECT 7.7225 18.9275 7.7875 19.0625 ; + RECT 7.3625 18.9275 7.4275 18.9975 ; + RECT 7.3625 20.0925 7.4275 20.2075 ; + RECT 7.5525 19.03 7.6175 20.0925 ; + RECT 7.295 19.49 7.33 19.555 ; + RECT 7.6175 19.49 7.855 19.555 ; + RECT 7.3625 19.1325 7.4275 19.2675 ; + RECT 7.5525 19.1325 7.6175 19.2675 ; + RECT 7.3625 19.9575 7.4275 20.0925 ; + RECT 7.5525 19.9575 7.6175 20.0925 ; + RECT 7.7225 19.9575 7.7875 20.0925 ; + RECT 7.7225 18.9975 7.7875 19.1325 ; + RECT 7.33 19.49 7.465 19.555 ; + RECT 6.545 19.8075 6.68 19.8725 ; + RECT 5.5325 19.805 5.5975 19.94 ; + RECT 5.46 21.5525 5.595 21.6175 ; + RECT 5.915 21.5525 6.05 21.6175 ; + RECT 5.985 21.5525 6.545 21.6175 ; + RECT 5.985 20.2075 6.545 20.2725 ; + RECT 6.4125 20.2725 6.4775 20.5225 ; + RECT 6.4125 21.4175 6.4775 21.5525 ; + RECT 6.0525 21.4825 6.1175 21.5525 ; + RECT 6.0525 20.2725 6.1175 20.3875 ; + RECT 6.2425 20.3875 6.3075 21.45 ; + RECT 5.985 20.925 6.02 20.99 ; + RECT 6.3075 20.925 6.545 20.99 ; + RECT 6.0525 21.2825 6.1175 21.4175 ; + RECT 6.2425 21.2825 6.3075 21.4175 ; + RECT 6.0525 20.6775 6.1175 20.8125 ; + RECT 6.2425 20.6775 6.3075 20.8125 ; + RECT 6.4125 20.5225 6.4775 20.6575 ; + RECT 6.4125 21.4825 6.4775 21.6175 ; + RECT 6.02 21.0 6.155 21.065 ; + RECT 6.545 21.5525 7.295 21.6175 ; + RECT 6.545 20.2075 7.295 20.2725 ; + RECT 7.1625 20.24 7.2275 20.5225 ; + RECT 7.1625 21.405 7.2275 21.585 ; + RECT 6.6125 20.24 6.6775 20.5225 ; + RECT 6.9925 20.24 7.0575 20.5225 ; + RECT 6.6125 21.3025 6.6775 21.585 ; + RECT 6.545 20.61 6.58 20.675 ; + RECT 6.545 21.14 6.82 21.205 ; + RECT 7.0575 20.9225 7.295 20.9875 ; + RECT 6.6125 21.1475 6.6775 21.2825 ; + RECT 6.8025 21.1475 6.8675 21.2825 ; + RECT 6.8025 21.1475 6.8675 21.2825 ; + RECT 6.9925 21.1475 7.0575 21.2825 ; + RECT 6.6125 20.6775 6.6775 20.8125 ; + RECT 6.8025 20.6775 6.8675 20.8125 ; + RECT 6.8025 20.6775 6.8675 20.8125 ; + RECT 6.9925 20.6775 7.0575 20.8125 ; + RECT 7.1625 20.5225 7.2275 20.6575 ; + RECT 7.1625 21.4375 7.2275 21.5725 ; + RECT 7.025 22.185 7.09 22.25 ; + RECT 6.8025 22.185 6.8675 22.25 ; + RECT 7.025 21.4375 7.09 22.2175 ; + RECT 6.835 22.185 7.0575 22.25 ; + RECT 6.8025 22.2175 6.8675 22.3525 ; + RECT 6.58 20.685 6.715 20.75 ; + RECT 6.82 21.215 6.955 21.28 ; + RECT 7.295 21.5525 7.855 21.6175 ; + RECT 7.295 20.2075 7.855 20.2725 ; + RECT 7.7225 20.2725 7.7875 20.5225 ; + RECT 7.7225 21.4175 7.7875 21.5525 ; + RECT 7.3625 21.4825 7.4275 21.5525 ; + RECT 7.3625 20.2725 7.4275 20.3875 ; + RECT 7.5525 20.3875 7.6175 21.45 ; + RECT 7.295 20.925 7.33 20.99 ; + RECT 7.6175 20.925 7.855 20.99 ; + RECT 7.3625 21.2825 7.4275 21.4175 ; + RECT 7.5525 21.2825 7.6175 21.4175 ; + RECT 7.3625 20.6775 7.4275 20.8125 ; + RECT 7.5525 20.6775 7.6175 20.8125 ; + RECT 7.7225 20.5225 7.7875 20.6575 ; + RECT 7.7225 21.4825 7.7875 21.6175 ; + RECT 7.33 21.0 7.465 21.065 ; + RECT 6.545 20.6075 6.68 20.6725 ; + RECT 5.5325 20.54 5.5975 20.675 ; + RECT 5.46 22.8975 5.595 22.9625 ; + RECT 5.915 22.8975 6.05 22.9625 ; + RECT 5.985 21.5525 6.545 21.6175 ; + RECT 5.985 22.8975 6.545 22.9625 ; + RECT 6.4125 22.6475 6.4775 22.8975 ; + RECT 6.4125 21.6175 6.4775 21.7525 ; + RECT 6.0525 21.6175 6.1175 21.6875 ; + RECT 6.0525 22.7825 6.1175 22.8975 ; + RECT 6.2425 21.72 6.3075 22.7825 ; + RECT 5.985 22.18 6.02 22.245 ; + RECT 6.3075 22.18 6.545 22.245 ; + RECT 6.0525 21.8225 6.1175 21.9575 ; + RECT 6.2425 21.8225 6.3075 21.9575 ; + RECT 6.0525 22.6475 6.1175 22.7825 ; + RECT 6.2425 22.6475 6.3075 22.7825 ; + RECT 6.4125 22.6475 6.4775 22.7825 ; + RECT 6.4125 21.6875 6.4775 21.8225 ; + RECT 6.02 22.18 6.155 22.245 ; + RECT 6.545 21.5525 7.295 21.6175 ; + RECT 6.545 22.8975 7.295 22.9625 ; + RECT 7.1625 22.6475 7.2275 22.93 ; + RECT 7.1625 21.585 7.2275 21.765 ; + RECT 6.6125 22.6475 6.6775 22.93 ; + RECT 6.9925 22.6475 7.0575 22.93 ; + RECT 6.6125 21.585 6.6775 21.8675 ; + RECT 6.545 22.495 6.58 22.56 ; + RECT 6.545 21.965 6.82 22.03 ; + RECT 7.0575 22.1825 7.295 22.2475 ; + RECT 6.6125 21.8675 6.6775 22.0025 ; + RECT 6.8025 21.8675 6.8675 22.0025 ; + RECT 6.8025 21.8675 6.8675 22.0025 ; + RECT 6.9925 21.8675 7.0575 22.0025 ; + RECT 6.6125 22.6475 6.6775 22.7825 ; + RECT 6.8025 22.6475 6.8675 22.7825 ; + RECT 6.8025 22.6475 6.8675 22.7825 ; + RECT 6.9925 22.6475 7.0575 22.7825 ; + RECT 7.1625 22.6475 7.2275 22.7825 ; + RECT 7.1625 21.7325 7.2275 21.8675 ; + RECT 7.025 22.48 7.09 22.545 ; + RECT 6.8025 22.48 6.8675 22.545 ; + RECT 7.025 21.7325 7.09 22.5125 ; + RECT 6.835 22.48 7.0575 22.545 ; + RECT 6.8025 22.5125 6.8675 22.6475 ; + RECT 6.58 22.495 6.715 22.56 ; + RECT 6.82 21.965 6.955 22.03 ; + RECT 7.295 21.5525 7.855 21.6175 ; + RECT 7.295 22.8975 7.855 22.9625 ; + RECT 7.7225 22.6475 7.7875 22.8975 ; + RECT 7.7225 21.6175 7.7875 21.7525 ; + RECT 7.3625 21.6175 7.4275 21.6875 ; + RECT 7.3625 22.7825 7.4275 22.8975 ; + RECT 7.5525 21.72 7.6175 22.7825 ; + RECT 7.295 22.18 7.33 22.245 ; + RECT 7.6175 22.18 7.855 22.245 ; + RECT 7.3625 21.8225 7.4275 21.9575 ; + RECT 7.5525 21.8225 7.6175 21.9575 ; + RECT 7.3625 22.6475 7.4275 22.7825 ; + RECT 7.5525 22.6475 7.6175 22.7825 ; + RECT 7.7225 22.6475 7.7875 22.7825 ; + RECT 7.7225 21.6875 7.7875 21.8225 ; + RECT 7.33 22.18 7.465 22.245 ; + RECT 6.545 22.4975 6.68 22.5625 ; + RECT 5.5325 22.495 5.5975 22.63 ; + RECT 5.46 24.2425 5.595 24.3075 ; + RECT 5.915 24.2425 6.05 24.3075 ; + RECT 5.985 24.2425 6.545 24.3075 ; + RECT 5.985 22.8975 6.545 22.9625 ; + RECT 6.4125 22.9625 6.4775 23.2125 ; + RECT 6.4125 24.1075 6.4775 24.2425 ; + RECT 6.0525 24.1725 6.1175 24.2425 ; + RECT 6.0525 22.9625 6.1175 23.0775 ; + RECT 6.2425 23.0775 6.3075 24.14 ; + RECT 5.985 23.615 6.02 23.68 ; + RECT 6.3075 23.615 6.545 23.68 ; + RECT 6.0525 23.9725 6.1175 24.1075 ; + RECT 6.2425 23.9725 6.3075 24.1075 ; + RECT 6.0525 23.3675 6.1175 23.5025 ; + RECT 6.2425 23.3675 6.3075 23.5025 ; + RECT 6.4125 23.2125 6.4775 23.3475 ; + RECT 6.4125 24.1725 6.4775 24.3075 ; + RECT 6.02 23.69 6.155 23.755 ; + RECT 6.545 24.2425 7.295 24.3075 ; + RECT 6.545 22.8975 7.295 22.9625 ; + RECT 7.1625 22.93 7.2275 23.2125 ; + RECT 7.1625 24.095 7.2275 24.275 ; + RECT 6.6125 22.93 6.6775 23.2125 ; + RECT 6.9925 22.93 7.0575 23.2125 ; + RECT 6.6125 23.9925 6.6775 24.275 ; + RECT 6.545 23.3 6.58 23.365 ; + RECT 6.545 23.83 6.82 23.895 ; + RECT 7.0575 23.6125 7.295 23.6775 ; + RECT 6.6125 23.8375 6.6775 23.9725 ; + RECT 6.8025 23.8375 6.8675 23.9725 ; + RECT 6.8025 23.8375 6.8675 23.9725 ; + RECT 6.9925 23.8375 7.0575 23.9725 ; + RECT 6.6125 23.3675 6.6775 23.5025 ; + RECT 6.8025 23.3675 6.8675 23.5025 ; + RECT 6.8025 23.3675 6.8675 23.5025 ; + RECT 6.9925 23.3675 7.0575 23.5025 ; + RECT 7.1625 23.2125 7.2275 23.3475 ; + RECT 7.1625 24.1275 7.2275 24.2625 ; + RECT 7.025 24.875 7.09 24.94 ; + RECT 6.8025 24.875 6.8675 24.94 ; + RECT 7.025 24.1275 7.09 24.9075 ; + RECT 6.835 24.875 7.0575 24.94 ; + RECT 6.8025 24.9075 6.8675 25.0425 ; + RECT 6.58 23.375 6.715 23.44 ; + RECT 6.82 23.905 6.955 23.97 ; + RECT 7.295 24.2425 7.855 24.3075 ; + RECT 7.295 22.8975 7.855 22.9625 ; + RECT 7.7225 22.9625 7.7875 23.2125 ; + RECT 7.7225 24.1075 7.7875 24.2425 ; + RECT 7.3625 24.1725 7.4275 24.2425 ; + RECT 7.3625 22.9625 7.4275 23.0775 ; + RECT 7.5525 23.0775 7.6175 24.14 ; + RECT 7.295 23.615 7.33 23.68 ; + RECT 7.6175 23.615 7.855 23.68 ; + RECT 7.3625 23.9725 7.4275 24.1075 ; + RECT 7.5525 23.9725 7.6175 24.1075 ; + RECT 7.3625 23.3675 7.4275 23.5025 ; + RECT 7.5525 23.3675 7.6175 23.5025 ; + RECT 7.7225 23.2125 7.7875 23.3475 ; + RECT 7.7225 24.1725 7.7875 24.3075 ; + RECT 7.33 23.69 7.465 23.755 ; + RECT 6.545 23.2975 6.68 23.3625 ; + RECT 5.5325 23.23 5.5975 23.365 ; + RECT 5.46 25.5875 5.595 25.6525 ; + RECT 5.915 25.5875 6.05 25.6525 ; + RECT 5.985 24.2425 6.545 24.3075 ; + RECT 5.985 25.5875 6.545 25.6525 ; + RECT 6.4125 25.3375 6.4775 25.5875 ; + RECT 6.4125 24.3075 6.4775 24.4425 ; + RECT 6.0525 24.3075 6.1175 24.3775 ; + RECT 6.0525 25.4725 6.1175 25.5875 ; + RECT 6.2425 24.41 6.3075 25.4725 ; + RECT 5.985 24.87 6.02 24.935 ; + RECT 6.3075 24.87 6.545 24.935 ; + RECT 6.0525 24.5125 6.1175 24.6475 ; + RECT 6.2425 24.5125 6.3075 24.6475 ; + RECT 6.0525 25.3375 6.1175 25.4725 ; + RECT 6.2425 25.3375 6.3075 25.4725 ; + RECT 6.4125 25.3375 6.4775 25.4725 ; + RECT 6.4125 24.3775 6.4775 24.5125 ; + RECT 6.02 24.87 6.155 24.935 ; + RECT 6.545 24.2425 7.295 24.3075 ; + RECT 6.545 25.5875 7.295 25.6525 ; + RECT 7.1625 25.3375 7.2275 25.62 ; + RECT 7.1625 24.275 7.2275 24.455 ; + RECT 6.6125 25.3375 6.6775 25.62 ; + RECT 6.9925 25.3375 7.0575 25.62 ; + RECT 6.6125 24.275 6.6775 24.5575 ; + RECT 6.545 25.185 6.58 25.25 ; + RECT 6.545 24.655 6.82 24.72 ; + RECT 7.0575 24.8725 7.295 24.9375 ; + RECT 6.6125 24.5575 6.6775 24.6925 ; + RECT 6.8025 24.5575 6.8675 24.6925 ; + RECT 6.8025 24.5575 6.8675 24.6925 ; + RECT 6.9925 24.5575 7.0575 24.6925 ; + RECT 6.6125 25.3375 6.6775 25.4725 ; + RECT 6.8025 25.3375 6.8675 25.4725 ; + RECT 6.8025 25.3375 6.8675 25.4725 ; + RECT 6.9925 25.3375 7.0575 25.4725 ; + RECT 7.1625 25.3375 7.2275 25.4725 ; + RECT 7.1625 24.4225 7.2275 24.5575 ; + RECT 7.025 25.17 7.09 25.235 ; + RECT 6.8025 25.17 6.8675 25.235 ; + RECT 7.025 24.4225 7.09 25.2025 ; + RECT 6.835 25.17 7.0575 25.235 ; + RECT 6.8025 25.2025 6.8675 25.3375 ; + RECT 6.58 25.185 6.715 25.25 ; + RECT 6.82 24.655 6.955 24.72 ; + RECT 7.295 24.2425 7.855 24.3075 ; + RECT 7.295 25.5875 7.855 25.6525 ; + RECT 7.7225 25.3375 7.7875 25.5875 ; + RECT 7.7225 24.3075 7.7875 24.4425 ; + RECT 7.3625 24.3075 7.4275 24.3775 ; + RECT 7.3625 25.4725 7.4275 25.5875 ; + RECT 7.5525 24.41 7.6175 25.4725 ; + RECT 7.295 24.87 7.33 24.935 ; + RECT 7.6175 24.87 7.855 24.935 ; + RECT 7.3625 24.5125 7.4275 24.6475 ; + RECT 7.5525 24.5125 7.6175 24.6475 ; + RECT 7.3625 25.3375 7.4275 25.4725 ; + RECT 7.5525 25.3375 7.6175 25.4725 ; + RECT 7.7225 25.3375 7.7875 25.4725 ; + RECT 7.7225 24.3775 7.7875 24.5125 ; + RECT 7.33 24.87 7.465 24.935 ; + RECT 6.545 25.1875 6.68 25.2525 ; + RECT 5.5325 25.185 5.5975 25.32 ; + RECT 5.46 26.9325 5.595 26.9975 ; + RECT 5.915 26.9325 6.05 26.9975 ; + RECT 5.985 26.9325 6.545 26.9975 ; + RECT 5.985 25.5875 6.545 25.6525 ; + RECT 6.4125 25.6525 6.4775 25.9025 ; + RECT 6.4125 26.7975 6.4775 26.9325 ; + RECT 6.0525 26.8625 6.1175 26.9325 ; + RECT 6.0525 25.6525 6.1175 25.7675 ; + RECT 6.2425 25.7675 6.3075 26.83 ; + RECT 5.985 26.305 6.02 26.37 ; + RECT 6.3075 26.305 6.545 26.37 ; + RECT 6.0525 26.6625 6.1175 26.7975 ; + RECT 6.2425 26.6625 6.3075 26.7975 ; + RECT 6.0525 26.0575 6.1175 26.1925 ; + RECT 6.2425 26.0575 6.3075 26.1925 ; + RECT 6.4125 25.9025 6.4775 26.0375 ; + RECT 6.4125 26.8625 6.4775 26.9975 ; + RECT 6.02 26.38 6.155 26.445 ; + RECT 6.545 26.9325 7.295 26.9975 ; + RECT 6.545 25.5875 7.295 25.6525 ; + RECT 7.1625 25.62 7.2275 25.9025 ; + RECT 7.1625 26.785 7.2275 26.965 ; + RECT 6.6125 25.62 6.6775 25.9025 ; + RECT 6.9925 25.62 7.0575 25.9025 ; + RECT 6.6125 26.6825 6.6775 26.965 ; + RECT 6.545 25.99 6.58 26.055 ; + RECT 6.545 26.52 6.82 26.585 ; + RECT 7.0575 26.3025 7.295 26.3675 ; + RECT 6.6125 26.5275 6.6775 26.6625 ; + RECT 6.8025 26.5275 6.8675 26.6625 ; + RECT 6.8025 26.5275 6.8675 26.6625 ; + RECT 6.9925 26.5275 7.0575 26.6625 ; + RECT 6.6125 26.0575 6.6775 26.1925 ; + RECT 6.8025 26.0575 6.8675 26.1925 ; + RECT 6.8025 26.0575 6.8675 26.1925 ; + RECT 6.9925 26.0575 7.0575 26.1925 ; + RECT 7.1625 25.9025 7.2275 26.0375 ; + RECT 7.1625 26.8175 7.2275 26.9525 ; + RECT 7.025 27.565 7.09 27.63 ; + RECT 6.8025 27.565 6.8675 27.63 ; + RECT 7.025 26.8175 7.09 27.5975 ; + RECT 6.835 27.565 7.0575 27.63 ; + RECT 6.8025 27.5975 6.8675 27.7325 ; + RECT 6.58 26.065 6.715 26.13 ; + RECT 6.82 26.595 6.955 26.66 ; + RECT 7.295 26.9325 7.855 26.9975 ; + RECT 7.295 25.5875 7.855 25.6525 ; + RECT 7.7225 25.6525 7.7875 25.9025 ; + RECT 7.7225 26.7975 7.7875 26.9325 ; + RECT 7.3625 26.8625 7.4275 26.9325 ; + RECT 7.3625 25.6525 7.4275 25.7675 ; + RECT 7.5525 25.7675 7.6175 26.83 ; + RECT 7.295 26.305 7.33 26.37 ; + RECT 7.6175 26.305 7.855 26.37 ; + RECT 7.3625 26.6625 7.4275 26.7975 ; + RECT 7.5525 26.6625 7.6175 26.7975 ; + RECT 7.3625 26.0575 7.4275 26.1925 ; + RECT 7.5525 26.0575 7.6175 26.1925 ; + RECT 7.7225 25.9025 7.7875 26.0375 ; + RECT 7.7225 26.8625 7.7875 26.9975 ; + RECT 7.33 26.38 7.465 26.445 ; + RECT 6.545 25.9875 6.68 26.0525 ; + RECT 5.5325 25.92 5.5975 26.055 ; + RECT 5.46 28.2775 5.595 28.3425 ; + RECT 5.915 28.2775 6.05 28.3425 ; + RECT 5.985 26.9325 6.545 26.9975 ; + RECT 5.985 28.2775 6.545 28.3425 ; + RECT 6.4125 28.0275 6.4775 28.2775 ; + RECT 6.4125 26.9975 6.4775 27.1325 ; + RECT 6.0525 26.9975 6.1175 27.0675 ; + RECT 6.0525 28.1625 6.1175 28.2775 ; + RECT 6.2425 27.1 6.3075 28.1625 ; + RECT 5.985 27.56 6.02 27.625 ; + RECT 6.3075 27.56 6.545 27.625 ; + RECT 6.0525 27.2025 6.1175 27.3375 ; + RECT 6.2425 27.2025 6.3075 27.3375 ; + RECT 6.0525 28.0275 6.1175 28.1625 ; + RECT 6.2425 28.0275 6.3075 28.1625 ; + RECT 6.4125 28.0275 6.4775 28.1625 ; + RECT 6.4125 27.0675 6.4775 27.2025 ; + RECT 6.02 27.56 6.155 27.625 ; + RECT 6.545 26.9325 7.295 26.9975 ; + RECT 6.545 28.2775 7.295 28.3425 ; + RECT 7.1625 28.0275 7.2275 28.31 ; + RECT 7.1625 26.965 7.2275 27.145 ; + RECT 6.6125 28.0275 6.6775 28.31 ; + RECT 6.9925 28.0275 7.0575 28.31 ; + RECT 6.6125 26.965 6.6775 27.2475 ; + RECT 6.545 27.875 6.58 27.94 ; + RECT 6.545 27.345 6.82 27.41 ; + RECT 7.0575 27.5625 7.295 27.6275 ; + RECT 6.6125 27.2475 6.6775 27.3825 ; + RECT 6.8025 27.2475 6.8675 27.3825 ; + RECT 6.8025 27.2475 6.8675 27.3825 ; + RECT 6.9925 27.2475 7.0575 27.3825 ; + RECT 6.6125 28.0275 6.6775 28.1625 ; + RECT 6.8025 28.0275 6.8675 28.1625 ; + RECT 6.8025 28.0275 6.8675 28.1625 ; + RECT 6.9925 28.0275 7.0575 28.1625 ; + RECT 7.1625 28.0275 7.2275 28.1625 ; + RECT 7.1625 27.1125 7.2275 27.2475 ; + RECT 7.025 27.86 7.09 27.925 ; + RECT 6.8025 27.86 6.8675 27.925 ; + RECT 7.025 27.1125 7.09 27.8925 ; + RECT 6.835 27.86 7.0575 27.925 ; + RECT 6.8025 27.8925 6.8675 28.0275 ; + RECT 6.58 27.875 6.715 27.94 ; + RECT 6.82 27.345 6.955 27.41 ; + RECT 7.295 26.9325 7.855 26.9975 ; + RECT 7.295 28.2775 7.855 28.3425 ; + RECT 7.7225 28.0275 7.7875 28.2775 ; + RECT 7.7225 26.9975 7.7875 27.1325 ; + RECT 7.3625 26.9975 7.4275 27.0675 ; + RECT 7.3625 28.1625 7.4275 28.2775 ; + RECT 7.5525 27.1 7.6175 28.1625 ; + RECT 7.295 27.56 7.33 27.625 ; + RECT 7.6175 27.56 7.855 27.625 ; + RECT 7.3625 27.2025 7.4275 27.3375 ; + RECT 7.5525 27.2025 7.6175 27.3375 ; + RECT 7.3625 28.0275 7.4275 28.1625 ; + RECT 7.5525 28.0275 7.6175 28.1625 ; + RECT 7.7225 28.0275 7.7875 28.1625 ; + RECT 7.7225 27.0675 7.7875 27.2025 ; + RECT 7.33 27.56 7.465 27.625 ; + RECT 6.545 27.8775 6.68 27.9425 ; + RECT 5.5325 27.875 5.5975 28.01 ; + RECT 5.46 29.6225 5.595 29.6875 ; + RECT 5.915 29.6225 6.05 29.6875 ; + RECT 5.985 29.6225 6.545 29.6875 ; + RECT 5.985 28.2775 6.545 28.3425 ; + RECT 6.4125 28.3425 6.4775 28.5925 ; + RECT 6.4125 29.4875 6.4775 29.6225 ; + RECT 6.0525 29.5525 6.1175 29.6225 ; + RECT 6.0525 28.3425 6.1175 28.4575 ; + RECT 6.2425 28.4575 6.3075 29.52 ; + RECT 5.985 28.995 6.02 29.06 ; + RECT 6.3075 28.995 6.545 29.06 ; + RECT 6.0525 29.3525 6.1175 29.4875 ; + RECT 6.2425 29.3525 6.3075 29.4875 ; + RECT 6.0525 28.7475 6.1175 28.8825 ; + RECT 6.2425 28.7475 6.3075 28.8825 ; + RECT 6.4125 28.5925 6.4775 28.7275 ; + RECT 6.4125 29.5525 6.4775 29.6875 ; + RECT 6.02 29.07 6.155 29.135 ; + RECT 6.545 29.6225 7.295 29.6875 ; + RECT 6.545 28.2775 7.295 28.3425 ; + RECT 7.1625 28.31 7.2275 28.5925 ; + RECT 7.1625 29.475 7.2275 29.655 ; + RECT 6.6125 28.31 6.6775 28.5925 ; + RECT 6.9925 28.31 7.0575 28.5925 ; + RECT 6.6125 29.3725 6.6775 29.655 ; + RECT 6.545 28.68 6.58 28.745 ; + RECT 6.545 29.21 6.82 29.275 ; + RECT 7.0575 28.9925 7.295 29.0575 ; + RECT 6.6125 29.2175 6.6775 29.3525 ; + RECT 6.8025 29.2175 6.8675 29.3525 ; + RECT 6.8025 29.2175 6.8675 29.3525 ; + RECT 6.9925 29.2175 7.0575 29.3525 ; + RECT 6.6125 28.7475 6.6775 28.8825 ; + RECT 6.8025 28.7475 6.8675 28.8825 ; + RECT 6.8025 28.7475 6.8675 28.8825 ; + RECT 6.9925 28.7475 7.0575 28.8825 ; + RECT 7.1625 28.5925 7.2275 28.7275 ; + RECT 7.1625 29.5075 7.2275 29.6425 ; + RECT 7.025 30.255 7.09 30.32 ; + RECT 6.8025 30.255 6.8675 30.32 ; + RECT 7.025 29.5075 7.09 30.2875 ; + RECT 6.835 30.255 7.0575 30.32 ; + RECT 6.8025 30.2875 6.8675 30.4225 ; + RECT 6.58 28.755 6.715 28.82 ; + RECT 6.82 29.285 6.955 29.35 ; + RECT 7.295 29.6225 7.855 29.6875 ; + RECT 7.295 28.2775 7.855 28.3425 ; + RECT 7.7225 28.3425 7.7875 28.5925 ; + RECT 7.7225 29.4875 7.7875 29.6225 ; + RECT 7.3625 29.5525 7.4275 29.6225 ; + RECT 7.3625 28.3425 7.4275 28.4575 ; + RECT 7.5525 28.4575 7.6175 29.52 ; + RECT 7.295 28.995 7.33 29.06 ; + RECT 7.6175 28.995 7.855 29.06 ; + RECT 7.3625 29.3525 7.4275 29.4875 ; + RECT 7.5525 29.3525 7.6175 29.4875 ; + RECT 7.3625 28.7475 7.4275 28.8825 ; + RECT 7.5525 28.7475 7.6175 28.8825 ; + RECT 7.7225 28.5925 7.7875 28.7275 ; + RECT 7.7225 29.5525 7.7875 29.6875 ; + RECT 7.33 29.07 7.465 29.135 ; + RECT 6.545 28.6775 6.68 28.7425 ; + RECT 5.5325 28.61 5.5975 28.745 ; + RECT 5.46 30.9675 5.595 31.0325 ; + RECT 5.915 30.9675 6.05 31.0325 ; + RECT 5.985 29.6225 6.545 29.6875 ; + RECT 5.985 30.9675 6.545 31.0325 ; + RECT 6.4125 30.7175 6.4775 30.9675 ; + RECT 6.4125 29.6875 6.4775 29.8225 ; + RECT 6.0525 29.6875 6.1175 29.7575 ; + RECT 6.0525 30.8525 6.1175 30.9675 ; + RECT 6.2425 29.79 6.3075 30.8525 ; + RECT 5.985 30.25 6.02 30.315 ; + RECT 6.3075 30.25 6.545 30.315 ; + RECT 6.0525 29.8925 6.1175 30.0275 ; + RECT 6.2425 29.8925 6.3075 30.0275 ; + RECT 6.0525 30.7175 6.1175 30.8525 ; + RECT 6.2425 30.7175 6.3075 30.8525 ; + RECT 6.4125 30.7175 6.4775 30.8525 ; + RECT 6.4125 29.7575 6.4775 29.8925 ; + RECT 6.02 30.25 6.155 30.315 ; + RECT 6.545 29.6225 7.295 29.6875 ; + RECT 6.545 30.9675 7.295 31.0325 ; + RECT 7.1625 30.7175 7.2275 31.0 ; + RECT 7.1625 29.655 7.2275 29.835 ; + RECT 6.6125 30.7175 6.6775 31.0 ; + RECT 6.9925 30.7175 7.0575 31.0 ; + RECT 6.6125 29.655 6.6775 29.9375 ; + RECT 6.545 30.565 6.58 30.63 ; + RECT 6.545 30.035 6.82 30.1 ; + RECT 7.0575 30.2525 7.295 30.3175 ; + RECT 6.6125 29.9375 6.6775 30.0725 ; + RECT 6.8025 29.9375 6.8675 30.0725 ; + RECT 6.8025 29.9375 6.8675 30.0725 ; + RECT 6.9925 29.9375 7.0575 30.0725 ; + RECT 6.6125 30.7175 6.6775 30.8525 ; + RECT 6.8025 30.7175 6.8675 30.8525 ; + RECT 6.8025 30.7175 6.8675 30.8525 ; + RECT 6.9925 30.7175 7.0575 30.8525 ; + RECT 7.1625 30.7175 7.2275 30.8525 ; + RECT 7.1625 29.8025 7.2275 29.9375 ; + RECT 7.025 30.55 7.09 30.615 ; + RECT 6.8025 30.55 6.8675 30.615 ; + RECT 7.025 29.8025 7.09 30.5825 ; + RECT 6.835 30.55 7.0575 30.615 ; + RECT 6.8025 30.5825 6.8675 30.7175 ; + RECT 6.58 30.565 6.715 30.63 ; + RECT 6.82 30.035 6.955 30.1 ; + RECT 7.295 29.6225 7.855 29.6875 ; + RECT 7.295 30.9675 7.855 31.0325 ; + RECT 7.7225 30.7175 7.7875 30.9675 ; + RECT 7.7225 29.6875 7.7875 29.8225 ; + RECT 7.3625 29.6875 7.4275 29.7575 ; + RECT 7.3625 30.8525 7.4275 30.9675 ; + RECT 7.5525 29.79 7.6175 30.8525 ; + RECT 7.295 30.25 7.33 30.315 ; + RECT 7.6175 30.25 7.855 30.315 ; + RECT 7.3625 29.8925 7.4275 30.0275 ; + RECT 7.5525 29.8925 7.6175 30.0275 ; + RECT 7.3625 30.7175 7.4275 30.8525 ; + RECT 7.5525 30.7175 7.6175 30.8525 ; + RECT 7.7225 30.7175 7.7875 30.8525 ; + RECT 7.7225 29.7575 7.7875 29.8925 ; + RECT 7.33 30.25 7.465 30.315 ; + RECT 6.545 30.5675 6.68 30.6325 ; + RECT 5.5325 30.565 5.5975 30.7 ; + RECT 5.46 32.3125 5.595 32.3775 ; + RECT 5.915 32.3125 6.05 32.3775 ; + RECT 5.985 32.3125 6.545 32.3775 ; + RECT 5.985 30.9675 6.545 31.0325 ; + RECT 6.4125 31.0325 6.4775 31.2825 ; + RECT 6.4125 32.1775 6.4775 32.3125 ; + RECT 6.0525 32.2425 6.1175 32.3125 ; + RECT 6.0525 31.0325 6.1175 31.1475 ; + RECT 6.2425 31.1475 6.3075 32.21 ; + RECT 5.985 31.685 6.02 31.75 ; + RECT 6.3075 31.685 6.545 31.75 ; + RECT 6.0525 32.0425 6.1175 32.1775 ; + RECT 6.2425 32.0425 6.3075 32.1775 ; + RECT 6.0525 31.4375 6.1175 31.5725 ; + RECT 6.2425 31.4375 6.3075 31.5725 ; + RECT 6.4125 31.2825 6.4775 31.4175 ; + RECT 6.4125 32.2425 6.4775 32.3775 ; + RECT 6.02 31.76 6.155 31.825 ; + RECT 6.545 32.3125 7.295 32.3775 ; + RECT 6.545 30.9675 7.295 31.0325 ; + RECT 7.1625 31.0 7.2275 31.2825 ; + RECT 7.1625 32.165 7.2275 32.345 ; + RECT 6.6125 31.0 6.6775 31.2825 ; + RECT 6.9925 31.0 7.0575 31.2825 ; + RECT 6.6125 32.0625 6.6775 32.345 ; + RECT 6.545 31.37 6.58 31.435 ; + RECT 6.545 31.9 6.82 31.965 ; + RECT 7.0575 31.6825 7.295 31.7475 ; + RECT 6.6125 31.9075 6.6775 32.0425 ; + RECT 6.8025 31.9075 6.8675 32.0425 ; + RECT 6.8025 31.9075 6.8675 32.0425 ; + RECT 6.9925 31.9075 7.0575 32.0425 ; + RECT 6.6125 31.4375 6.6775 31.5725 ; + RECT 6.8025 31.4375 6.8675 31.5725 ; + RECT 6.8025 31.4375 6.8675 31.5725 ; + RECT 6.9925 31.4375 7.0575 31.5725 ; + RECT 7.1625 31.2825 7.2275 31.4175 ; + RECT 7.1625 32.1975 7.2275 32.3325 ; + RECT 7.025 32.945 7.09 33.01 ; + RECT 6.8025 32.945 6.8675 33.01 ; + RECT 7.025 32.1975 7.09 32.9775 ; + RECT 6.835 32.945 7.0575 33.01 ; + RECT 6.8025 32.9775 6.8675 33.1125 ; + RECT 6.58 31.445 6.715 31.51 ; + RECT 6.82 31.975 6.955 32.04 ; + RECT 7.295 32.3125 7.855 32.3775 ; + RECT 7.295 30.9675 7.855 31.0325 ; + RECT 7.7225 31.0325 7.7875 31.2825 ; + RECT 7.7225 32.1775 7.7875 32.3125 ; + RECT 7.3625 32.2425 7.4275 32.3125 ; + RECT 7.3625 31.0325 7.4275 31.1475 ; + RECT 7.5525 31.1475 7.6175 32.21 ; + RECT 7.295 31.685 7.33 31.75 ; + RECT 7.6175 31.685 7.855 31.75 ; + RECT 7.3625 32.0425 7.4275 32.1775 ; + RECT 7.5525 32.0425 7.6175 32.1775 ; + RECT 7.3625 31.4375 7.4275 31.5725 ; + RECT 7.5525 31.4375 7.6175 31.5725 ; + RECT 7.7225 31.2825 7.7875 31.4175 ; + RECT 7.7225 32.2425 7.7875 32.3775 ; + RECT 7.33 31.76 7.465 31.825 ; + RECT 6.545 31.3675 6.68 31.4325 ; + RECT 5.5325 31.3 5.5975 31.435 ; + RECT 5.46 33.6575 5.595 33.7225 ; + RECT 5.915 33.6575 6.05 33.7225 ; + RECT 5.985 32.3125 6.545 32.3775 ; + RECT 5.985 33.6575 6.545 33.7225 ; + RECT 6.4125 33.4075 6.4775 33.6575 ; + RECT 6.4125 32.3775 6.4775 32.5125 ; + RECT 6.0525 32.3775 6.1175 32.4475 ; + RECT 6.0525 33.5425 6.1175 33.6575 ; + RECT 6.2425 32.48 6.3075 33.5425 ; + RECT 5.985 32.94 6.02 33.005 ; + RECT 6.3075 32.94 6.545 33.005 ; + RECT 6.0525 32.5825 6.1175 32.7175 ; + RECT 6.2425 32.5825 6.3075 32.7175 ; + RECT 6.0525 33.4075 6.1175 33.5425 ; + RECT 6.2425 33.4075 6.3075 33.5425 ; + RECT 6.4125 33.4075 6.4775 33.5425 ; + RECT 6.4125 32.4475 6.4775 32.5825 ; + RECT 6.02 32.94 6.155 33.005 ; + RECT 6.545 32.3125 7.295 32.3775 ; + RECT 6.545 33.6575 7.295 33.7225 ; + RECT 7.1625 33.4075 7.2275 33.69 ; + RECT 7.1625 32.345 7.2275 32.525 ; + RECT 6.6125 33.4075 6.6775 33.69 ; + RECT 6.9925 33.4075 7.0575 33.69 ; + RECT 6.6125 32.345 6.6775 32.6275 ; + RECT 6.545 33.255 6.58 33.32 ; + RECT 6.545 32.725 6.82 32.79 ; + RECT 7.0575 32.9425 7.295 33.0075 ; + RECT 6.6125 32.6275 6.6775 32.7625 ; + RECT 6.8025 32.6275 6.8675 32.7625 ; + RECT 6.8025 32.6275 6.8675 32.7625 ; + RECT 6.9925 32.6275 7.0575 32.7625 ; + RECT 6.6125 33.4075 6.6775 33.5425 ; + RECT 6.8025 33.4075 6.8675 33.5425 ; + RECT 6.8025 33.4075 6.8675 33.5425 ; + RECT 6.9925 33.4075 7.0575 33.5425 ; + RECT 7.1625 33.4075 7.2275 33.5425 ; + RECT 7.1625 32.4925 7.2275 32.6275 ; + RECT 7.025 33.24 7.09 33.305 ; + RECT 6.8025 33.24 6.8675 33.305 ; + RECT 7.025 32.4925 7.09 33.2725 ; + RECT 6.835 33.24 7.0575 33.305 ; + RECT 6.8025 33.2725 6.8675 33.4075 ; + RECT 6.58 33.255 6.715 33.32 ; + RECT 6.82 32.725 6.955 32.79 ; + RECT 7.295 32.3125 7.855 32.3775 ; + RECT 7.295 33.6575 7.855 33.7225 ; + RECT 7.7225 33.4075 7.7875 33.6575 ; + RECT 7.7225 32.3775 7.7875 32.5125 ; + RECT 7.3625 32.3775 7.4275 32.4475 ; + RECT 7.3625 33.5425 7.4275 33.6575 ; + RECT 7.5525 32.48 7.6175 33.5425 ; + RECT 7.295 32.94 7.33 33.005 ; + RECT 7.6175 32.94 7.855 33.005 ; + RECT 7.3625 32.5825 7.4275 32.7175 ; + RECT 7.5525 32.5825 7.6175 32.7175 ; + RECT 7.3625 33.4075 7.4275 33.5425 ; + RECT 7.5525 33.4075 7.6175 33.5425 ; + RECT 7.7225 33.4075 7.7875 33.5425 ; + RECT 7.7225 32.4475 7.7875 32.5825 ; + RECT 7.33 32.94 7.465 33.005 ; + RECT 6.545 33.2575 6.68 33.3225 ; + RECT 5.5325 33.255 5.5975 33.39 ; + RECT 5.46 35.0025 5.595 35.0675 ; + RECT 5.915 35.0025 6.05 35.0675 ; + RECT 5.985 35.0025 6.545 35.0675 ; + RECT 5.985 33.6575 6.545 33.7225 ; + RECT 6.4125 33.7225 6.4775 33.9725 ; + RECT 6.4125 34.8675 6.4775 35.0025 ; + RECT 6.0525 34.9325 6.1175 35.0025 ; + RECT 6.0525 33.7225 6.1175 33.8375 ; + RECT 6.2425 33.8375 6.3075 34.9 ; + RECT 5.985 34.375 6.02 34.44 ; + RECT 6.3075 34.375 6.545 34.44 ; + RECT 6.0525 34.7325 6.1175 34.8675 ; + RECT 6.2425 34.7325 6.3075 34.8675 ; + RECT 6.0525 34.1275 6.1175 34.2625 ; + RECT 6.2425 34.1275 6.3075 34.2625 ; + RECT 6.4125 33.9725 6.4775 34.1075 ; + RECT 6.4125 34.9325 6.4775 35.0675 ; + RECT 6.02 34.45 6.155 34.515 ; + RECT 6.545 35.0025 7.295 35.0675 ; + RECT 6.545 33.6575 7.295 33.7225 ; + RECT 7.1625 33.69 7.2275 33.9725 ; + RECT 7.1625 34.855 7.2275 35.035 ; + RECT 6.6125 33.69 6.6775 33.9725 ; + RECT 6.9925 33.69 7.0575 33.9725 ; + RECT 6.6125 34.7525 6.6775 35.035 ; + RECT 6.545 34.06 6.58 34.125 ; + RECT 6.545 34.59 6.82 34.655 ; + RECT 7.0575 34.3725 7.295 34.4375 ; + RECT 6.6125 34.5975 6.6775 34.7325 ; + RECT 6.8025 34.5975 6.8675 34.7325 ; + RECT 6.8025 34.5975 6.8675 34.7325 ; + RECT 6.9925 34.5975 7.0575 34.7325 ; + RECT 6.6125 34.1275 6.6775 34.2625 ; + RECT 6.8025 34.1275 6.8675 34.2625 ; + RECT 6.8025 34.1275 6.8675 34.2625 ; + RECT 6.9925 34.1275 7.0575 34.2625 ; + RECT 7.1625 33.9725 7.2275 34.1075 ; + RECT 7.1625 34.8875 7.2275 35.0225 ; + RECT 7.025 35.635 7.09 35.7 ; + RECT 6.8025 35.635 6.8675 35.7 ; + RECT 7.025 34.8875 7.09 35.6675 ; + RECT 6.835 35.635 7.0575 35.7 ; + RECT 6.8025 35.6675 6.8675 35.8025 ; + RECT 6.58 34.135 6.715 34.2 ; + RECT 6.82 34.665 6.955 34.73 ; + RECT 7.295 35.0025 7.855 35.0675 ; + RECT 7.295 33.6575 7.855 33.7225 ; + RECT 7.7225 33.7225 7.7875 33.9725 ; + RECT 7.7225 34.8675 7.7875 35.0025 ; + RECT 7.3625 34.9325 7.4275 35.0025 ; + RECT 7.3625 33.7225 7.4275 33.8375 ; + RECT 7.5525 33.8375 7.6175 34.9 ; + RECT 7.295 34.375 7.33 34.44 ; + RECT 7.6175 34.375 7.855 34.44 ; + RECT 7.3625 34.7325 7.4275 34.8675 ; + RECT 7.5525 34.7325 7.6175 34.8675 ; + RECT 7.3625 34.1275 7.4275 34.2625 ; + RECT 7.5525 34.1275 7.6175 34.2625 ; + RECT 7.7225 33.9725 7.7875 34.1075 ; + RECT 7.7225 34.9325 7.7875 35.0675 ; + RECT 7.33 34.45 7.465 34.515 ; + RECT 6.545 34.0575 6.68 34.1225 ; + RECT 5.5325 33.99 5.5975 34.125 ; + RECT 5.46 36.3475 5.595 36.4125 ; + RECT 5.915 36.3475 6.05 36.4125 ; + RECT 5.985 35.0025 6.545 35.0675 ; + RECT 5.985 36.3475 6.545 36.4125 ; + RECT 6.4125 36.0975 6.4775 36.3475 ; + RECT 6.4125 35.0675 6.4775 35.2025 ; + RECT 6.0525 35.0675 6.1175 35.1375 ; + RECT 6.0525 36.2325 6.1175 36.3475 ; + RECT 6.2425 35.17 6.3075 36.2325 ; + RECT 5.985 35.63 6.02 35.695 ; + RECT 6.3075 35.63 6.545 35.695 ; + RECT 6.0525 35.2725 6.1175 35.4075 ; + RECT 6.2425 35.2725 6.3075 35.4075 ; + RECT 6.0525 36.0975 6.1175 36.2325 ; + RECT 6.2425 36.0975 6.3075 36.2325 ; + RECT 6.4125 36.0975 6.4775 36.2325 ; + RECT 6.4125 35.1375 6.4775 35.2725 ; + RECT 6.02 35.63 6.155 35.695 ; + RECT 6.545 35.0025 7.295 35.0675 ; + RECT 6.545 36.3475 7.295 36.4125 ; + RECT 7.1625 36.0975 7.2275 36.38 ; + RECT 7.1625 35.035 7.2275 35.215 ; + RECT 6.6125 36.0975 6.6775 36.38 ; + RECT 6.9925 36.0975 7.0575 36.38 ; + RECT 6.6125 35.035 6.6775 35.3175 ; + RECT 6.545 35.945 6.58 36.01 ; + RECT 6.545 35.415 6.82 35.48 ; + RECT 7.0575 35.6325 7.295 35.6975 ; + RECT 6.6125 35.3175 6.6775 35.4525 ; + RECT 6.8025 35.3175 6.8675 35.4525 ; + RECT 6.8025 35.3175 6.8675 35.4525 ; + RECT 6.9925 35.3175 7.0575 35.4525 ; + RECT 6.6125 36.0975 6.6775 36.2325 ; + RECT 6.8025 36.0975 6.8675 36.2325 ; + RECT 6.8025 36.0975 6.8675 36.2325 ; + RECT 6.9925 36.0975 7.0575 36.2325 ; + RECT 7.1625 36.0975 7.2275 36.2325 ; + RECT 7.1625 35.1825 7.2275 35.3175 ; + RECT 7.025 35.93 7.09 35.995 ; + RECT 6.8025 35.93 6.8675 35.995 ; + RECT 7.025 35.1825 7.09 35.9625 ; + RECT 6.835 35.93 7.0575 35.995 ; + RECT 6.8025 35.9625 6.8675 36.0975 ; + RECT 6.58 35.945 6.715 36.01 ; + RECT 6.82 35.415 6.955 35.48 ; + RECT 7.295 35.0025 7.855 35.0675 ; + RECT 7.295 36.3475 7.855 36.4125 ; + RECT 7.7225 36.0975 7.7875 36.3475 ; + RECT 7.7225 35.0675 7.7875 35.2025 ; + RECT 7.3625 35.0675 7.4275 35.1375 ; + RECT 7.3625 36.2325 7.4275 36.3475 ; + RECT 7.5525 35.17 7.6175 36.2325 ; + RECT 7.295 35.63 7.33 35.695 ; + RECT 7.6175 35.63 7.855 35.695 ; + RECT 7.3625 35.2725 7.4275 35.4075 ; + RECT 7.5525 35.2725 7.6175 35.4075 ; + RECT 7.3625 36.0975 7.4275 36.2325 ; + RECT 7.5525 36.0975 7.6175 36.2325 ; + RECT 7.7225 36.0975 7.7875 36.2325 ; + RECT 7.7225 35.1375 7.7875 35.2725 ; + RECT 7.33 35.63 7.465 35.695 ; + RECT 6.545 35.9475 6.68 36.0125 ; + RECT 5.5325 35.945 5.5975 36.08 ; + RECT 5.46 37.6925 5.595 37.7575 ; + RECT 5.915 37.6925 6.05 37.7575 ; + RECT 5.985 37.6925 6.545 37.7575 ; + RECT 5.985 36.3475 6.545 36.4125 ; + RECT 6.4125 36.4125 6.4775 36.6625 ; + RECT 6.4125 37.5575 6.4775 37.6925 ; + RECT 6.0525 37.6225 6.1175 37.6925 ; + RECT 6.0525 36.4125 6.1175 36.5275 ; + RECT 6.2425 36.5275 6.3075 37.59 ; + RECT 5.985 37.065 6.02 37.13 ; + RECT 6.3075 37.065 6.545 37.13 ; + RECT 6.0525 37.4225 6.1175 37.5575 ; + RECT 6.2425 37.4225 6.3075 37.5575 ; + RECT 6.0525 36.8175 6.1175 36.9525 ; + RECT 6.2425 36.8175 6.3075 36.9525 ; + RECT 6.4125 36.6625 6.4775 36.7975 ; + RECT 6.4125 37.6225 6.4775 37.7575 ; + RECT 6.02 37.14 6.155 37.205 ; + RECT 6.545 37.6925 7.295 37.7575 ; + RECT 6.545 36.3475 7.295 36.4125 ; + RECT 7.1625 36.38 7.2275 36.6625 ; + RECT 7.1625 37.545 7.2275 37.725 ; + RECT 6.6125 36.38 6.6775 36.6625 ; + RECT 6.9925 36.38 7.0575 36.6625 ; + RECT 6.6125 37.4425 6.6775 37.725 ; + RECT 6.545 36.75 6.58 36.815 ; + RECT 6.545 37.28 6.82 37.345 ; + RECT 7.0575 37.0625 7.295 37.1275 ; + RECT 6.6125 37.2875 6.6775 37.4225 ; + RECT 6.8025 37.2875 6.8675 37.4225 ; + RECT 6.8025 37.2875 6.8675 37.4225 ; + RECT 6.9925 37.2875 7.0575 37.4225 ; + RECT 6.6125 36.8175 6.6775 36.9525 ; + RECT 6.8025 36.8175 6.8675 36.9525 ; + RECT 6.8025 36.8175 6.8675 36.9525 ; + RECT 6.9925 36.8175 7.0575 36.9525 ; + RECT 7.1625 36.6625 7.2275 36.7975 ; + RECT 7.1625 37.5775 7.2275 37.7125 ; + RECT 7.025 38.325 7.09 38.39 ; + RECT 6.8025 38.325 6.8675 38.39 ; + RECT 7.025 37.5775 7.09 38.3575 ; + RECT 6.835 38.325 7.0575 38.39 ; + RECT 6.8025 38.3575 6.8675 38.4925 ; + RECT 6.58 36.825 6.715 36.89 ; + RECT 6.82 37.355 6.955 37.42 ; + RECT 7.295 37.6925 7.855 37.7575 ; + RECT 7.295 36.3475 7.855 36.4125 ; + RECT 7.7225 36.4125 7.7875 36.6625 ; + RECT 7.7225 37.5575 7.7875 37.6925 ; + RECT 7.3625 37.6225 7.4275 37.6925 ; + RECT 7.3625 36.4125 7.4275 36.5275 ; + RECT 7.5525 36.5275 7.6175 37.59 ; + RECT 7.295 37.065 7.33 37.13 ; + RECT 7.6175 37.065 7.855 37.13 ; + RECT 7.3625 37.4225 7.4275 37.5575 ; + RECT 7.5525 37.4225 7.6175 37.5575 ; + RECT 7.3625 36.8175 7.4275 36.9525 ; + RECT 7.5525 36.8175 7.6175 36.9525 ; + RECT 7.7225 36.6625 7.7875 36.7975 ; + RECT 7.7225 37.6225 7.7875 37.7575 ; + RECT 7.33 37.14 7.465 37.205 ; + RECT 6.545 36.7475 6.68 36.8125 ; + RECT 5.5325 36.68 5.5975 36.815 ; + RECT 5.46 39.0375 5.595 39.1025 ; + RECT 5.915 39.0375 6.05 39.1025 ; + RECT 5.985 37.6925 6.545 37.7575 ; + RECT 5.985 39.0375 6.545 39.1025 ; + RECT 6.4125 38.7875 6.4775 39.0375 ; + RECT 6.4125 37.7575 6.4775 37.8925 ; + RECT 6.0525 37.7575 6.1175 37.8275 ; + RECT 6.0525 38.9225 6.1175 39.0375 ; + RECT 6.2425 37.86 6.3075 38.9225 ; + RECT 5.985 38.32 6.02 38.385 ; + RECT 6.3075 38.32 6.545 38.385 ; + RECT 6.0525 37.9625 6.1175 38.0975 ; + RECT 6.2425 37.9625 6.3075 38.0975 ; + RECT 6.0525 38.7875 6.1175 38.9225 ; + RECT 6.2425 38.7875 6.3075 38.9225 ; + RECT 6.4125 38.7875 6.4775 38.9225 ; + RECT 6.4125 37.8275 6.4775 37.9625 ; + RECT 6.02 38.32 6.155 38.385 ; + RECT 6.545 37.6925 7.295 37.7575 ; + RECT 6.545 39.0375 7.295 39.1025 ; + RECT 7.1625 38.7875 7.2275 39.07 ; + RECT 7.1625 37.725 7.2275 37.905 ; + RECT 6.6125 38.7875 6.6775 39.07 ; + RECT 6.9925 38.7875 7.0575 39.07 ; + RECT 6.6125 37.725 6.6775 38.0075 ; + RECT 6.545 38.635 6.58 38.7 ; + RECT 6.545 38.105 6.82 38.17 ; + RECT 7.0575 38.3225 7.295 38.3875 ; + RECT 6.6125 38.0075 6.6775 38.1425 ; + RECT 6.8025 38.0075 6.8675 38.1425 ; + RECT 6.8025 38.0075 6.8675 38.1425 ; + RECT 6.9925 38.0075 7.0575 38.1425 ; + RECT 6.6125 38.7875 6.6775 38.9225 ; + RECT 6.8025 38.7875 6.8675 38.9225 ; + RECT 6.8025 38.7875 6.8675 38.9225 ; + RECT 6.9925 38.7875 7.0575 38.9225 ; + RECT 7.1625 38.7875 7.2275 38.9225 ; + RECT 7.1625 37.8725 7.2275 38.0075 ; + RECT 7.025 38.62 7.09 38.685 ; + RECT 6.8025 38.62 6.8675 38.685 ; + RECT 7.025 37.8725 7.09 38.6525 ; + RECT 6.835 38.62 7.0575 38.685 ; + RECT 6.8025 38.6525 6.8675 38.7875 ; + RECT 6.58 38.635 6.715 38.7 ; + RECT 6.82 38.105 6.955 38.17 ; + RECT 7.295 37.6925 7.855 37.7575 ; + RECT 7.295 39.0375 7.855 39.1025 ; + RECT 7.7225 38.7875 7.7875 39.0375 ; + RECT 7.7225 37.7575 7.7875 37.8925 ; + RECT 7.3625 37.7575 7.4275 37.8275 ; + RECT 7.3625 38.9225 7.4275 39.0375 ; + RECT 7.5525 37.86 7.6175 38.9225 ; + RECT 7.295 38.32 7.33 38.385 ; + RECT 7.6175 38.32 7.855 38.385 ; + RECT 7.3625 37.9625 7.4275 38.0975 ; + RECT 7.5525 37.9625 7.6175 38.0975 ; + RECT 7.3625 38.7875 7.4275 38.9225 ; + RECT 7.5525 38.7875 7.6175 38.9225 ; + RECT 7.7225 38.7875 7.7875 38.9225 ; + RECT 7.7225 37.8275 7.7875 37.9625 ; + RECT 7.33 38.32 7.465 38.385 ; + RECT 6.545 38.6375 6.68 38.7025 ; + RECT 5.5325 38.635 5.5975 38.77 ; + RECT 5.46 40.3825 5.595 40.4475 ; + RECT 5.915 40.3825 6.05 40.4475 ; + RECT 5.985 40.3825 6.545 40.4475 ; + RECT 5.985 39.0375 6.545 39.1025 ; + RECT 6.4125 39.1025 6.4775 39.3525 ; + RECT 6.4125 40.2475 6.4775 40.3825 ; + RECT 6.0525 40.3125 6.1175 40.3825 ; + RECT 6.0525 39.1025 6.1175 39.2175 ; + RECT 6.2425 39.2175 6.3075 40.28 ; + RECT 5.985 39.755 6.02 39.82 ; + RECT 6.3075 39.755 6.545 39.82 ; + RECT 6.0525 40.1125 6.1175 40.2475 ; + RECT 6.2425 40.1125 6.3075 40.2475 ; + RECT 6.0525 39.5075 6.1175 39.6425 ; + RECT 6.2425 39.5075 6.3075 39.6425 ; + RECT 6.4125 39.3525 6.4775 39.4875 ; + RECT 6.4125 40.3125 6.4775 40.4475 ; + RECT 6.02 39.83 6.155 39.895 ; + RECT 6.545 40.3825 7.295 40.4475 ; + RECT 6.545 39.0375 7.295 39.1025 ; + RECT 7.1625 39.07 7.2275 39.3525 ; + RECT 7.1625 40.235 7.2275 40.415 ; + RECT 6.6125 39.07 6.6775 39.3525 ; + RECT 6.9925 39.07 7.0575 39.3525 ; + RECT 6.6125 40.1325 6.6775 40.415 ; + RECT 6.545 39.44 6.58 39.505 ; + RECT 6.545 39.97 6.82 40.035 ; + RECT 7.0575 39.7525 7.295 39.8175 ; + RECT 6.6125 39.9775 6.6775 40.1125 ; + RECT 6.8025 39.9775 6.8675 40.1125 ; + RECT 6.8025 39.9775 6.8675 40.1125 ; + RECT 6.9925 39.9775 7.0575 40.1125 ; + RECT 6.6125 39.5075 6.6775 39.6425 ; + RECT 6.8025 39.5075 6.8675 39.6425 ; + RECT 6.8025 39.5075 6.8675 39.6425 ; + RECT 6.9925 39.5075 7.0575 39.6425 ; + RECT 7.1625 39.3525 7.2275 39.4875 ; + RECT 7.1625 40.2675 7.2275 40.4025 ; + RECT 7.025 41.015 7.09 41.08 ; + RECT 6.8025 41.015 6.8675 41.08 ; + RECT 7.025 40.2675 7.09 41.0475 ; + RECT 6.835 41.015 7.0575 41.08 ; + RECT 6.8025 41.0475 6.8675 41.1825 ; + RECT 6.58 39.515 6.715 39.58 ; + RECT 6.82 40.045 6.955 40.11 ; + RECT 7.295 40.3825 7.855 40.4475 ; + RECT 7.295 39.0375 7.855 39.1025 ; + RECT 7.7225 39.1025 7.7875 39.3525 ; + RECT 7.7225 40.2475 7.7875 40.3825 ; + RECT 7.3625 40.3125 7.4275 40.3825 ; + RECT 7.3625 39.1025 7.4275 39.2175 ; + RECT 7.5525 39.2175 7.6175 40.28 ; + RECT 7.295 39.755 7.33 39.82 ; + RECT 7.6175 39.755 7.855 39.82 ; + RECT 7.3625 40.1125 7.4275 40.2475 ; + RECT 7.5525 40.1125 7.6175 40.2475 ; + RECT 7.3625 39.5075 7.4275 39.6425 ; + RECT 7.5525 39.5075 7.6175 39.6425 ; + RECT 7.7225 39.3525 7.7875 39.4875 ; + RECT 7.7225 40.3125 7.7875 40.4475 ; + RECT 7.33 39.83 7.465 39.895 ; + RECT 6.545 39.4375 6.68 39.5025 ; + RECT 5.5325 39.37 5.5975 39.505 ; + RECT 1.0825 5.02 1.1475 7.84 ; + RECT 7.08 5.02 7.145 7.84 ; + RECT 3.6 7.235 3.735 7.3 ; + RECT 3.6 7.42 3.735 7.485 ; + RECT 2.365 7.235 2.5 7.3 ; + RECT 2.365 7.42 2.5 7.485 ; + RECT 3.6 7.425 3.735 7.49 ; + RECT 3.6 7.61 3.735 7.675 ; + RECT 6.56 7.425 6.695 7.49 ; + RECT 6.56 7.61 6.695 7.675 ; + RECT 2.365 7.425 2.5 7.49 ; + RECT 2.365 7.61 2.5 7.675 ; + RECT 6.56 7.235 6.695 7.3 ; + RECT 6.56 7.42 6.695 7.485 ; + RECT 1.83 7.425 1.965 7.49 ; + RECT 1.83 7.61 1.965 7.675 ; + RECT 4.79 7.285 4.925 7.35 ; + RECT 4.79 7.47 4.925 7.535 ; + RECT 5.325 7.235 5.46 7.3 ; + RECT 5.325 7.42 5.46 7.485 ; + RECT 5.325 7.425 5.46 7.49 ; + RECT 5.325 7.61 5.46 7.675 ; + RECT 5.75 7.235 5.885 7.3 ; + RECT 5.75 7.42 5.885 7.485 ; + RECT 2.79 7.235 2.925 7.3 ; + RECT 2.79 7.42 2.925 7.485 ; + RECT 6.135 7.235 6.27 7.3 ; + RECT 6.135 7.42 6.27 7.485 ; + RECT 6.135 7.425 6.27 7.49 ; + RECT 6.135 7.61 6.27 7.675 ; + RECT 2.79 7.425 2.925 7.49 ; + RECT 2.79 7.61 2.925 7.675 ; + RECT 5.75 7.425 5.885 7.49 ; + RECT 5.75 7.61 5.885 7.675 ; + RECT 3.175 7.425 3.31 7.49 ; + RECT 3.175 7.61 3.31 7.675 ; + RECT 3.175 7.235 3.31 7.3 ; + RECT 3.175 7.42 3.31 7.485 ; + RECT 1.405 7.425 1.54 7.49 ; + RECT 1.405 7.61 1.54 7.675 ; + RECT 4.38 7.35 4.515 7.415 ; + RECT 4.38 7.535 4.515 7.6 ; + RECT 1.115 7.4775 1.18 7.6125 ; + RECT 6.6125 7.61 6.7475 7.675 ; + RECT 4.7975 7.72 4.9325 7.785 ; + RECT 3.8975 7.61 4.0325 7.675 ; + RECT 4.3725 7.1875 4.5075 7.2525 ; + RECT 6.9775 7.61 7.1125 7.675 ; + RECT 1.435 7.1025 1.57 7.1675 ; + RECT 5.5175 7.245 5.6525 7.31 ; + RECT 3.91 7.2425 4.045 7.3075 ; + RECT 2.5125 7.2425 2.6475 7.3075 ; + RECT 1.295 7.8075 1.43 7.8725 ; + RECT 1.4125 7.2325 1.5475 7.2975 ; + RECT 3.0225 7.1625 3.0875 7.2975 ; + RECT 1.9375 7.1925 2.0725 7.2575 ; + RECT 5.9825 7.1625 6.0475 7.2975 ; + RECT 6.87 7.285 7.005 7.35 ; + RECT 3.4 7.365 3.535 7.43 ; + RECT 6.36 7.375 6.495 7.44 ; + RECT 4.67 7.5375 4.735 7.6725 ; + RECT 4.3725 7.1025 4.5075 7.1675 ; + RECT 1.665 7.6475 1.73 7.7825 ; + RECT 3.1775 7.6075 3.3125 7.6725 ; + RECT 6.87 7.2425 7.005 7.3075 ; + RECT 6.36 7.42 6.495 7.485 ; + RECT 5.9475 7.1025 6.0825 7.1675 ; + RECT 1.245 7.1025 1.38 7.1675 ; + RECT 3.91 7.2425 4.045 7.3075 ; + RECT 2.9875 7.1025 3.1225 7.1675 ; + RECT 2.7875 7.6075 2.9225 7.6725 ; + RECT 2.7875 7.42 2.9225 7.485 ; + RECT 3.4 7.42 3.535 7.485 ; + RECT 5.7475 7.61 5.8825 7.675 ; + RECT 5.7475 7.42 5.8825 7.485 ; + RECT 1.115 7.1 1.18 7.875 ; + RECT 4.185 7.74 7.08 7.805 ; + RECT 6.695 7.235 7.005 7.3 ; + RECT 6.27 7.61 6.56 7.675 ; + RECT 5.46 7.61 5.75 7.675 ; + RECT 7.0825 7.4625 7.145 7.535 ; + RECT 6.695 7.42 7.08 7.49 ; + RECT 5.46 7.42 5.75 7.49 ; + RECT 5.98 7.42 6.1375 7.49 ; + RECT 6.27 7.235 6.56 7.3 ; + RECT 5.46 7.235 5.75 7.3 ; + RECT 5.98 7.105 6.05 7.49 ; + RECT 7.08 7.1 7.145 7.875 ; + RECT 5.09 7.1 5.155 7.875 ; + RECT 2.195 7.74 4.12 7.805 ; + RECT 3.735 7.235 4.045 7.3 ; + RECT 3.31 7.61 3.6 7.675 ; + RECT 4.475 7.535 4.925 7.6 ; + RECT 3.735 7.42 4.12 7.49 ; + RECT 3.02 7.42 3.1775 7.49 ; + RECT 4.0325 7.61 4.12 7.675 ; + RECT 3.31 7.235 3.6 7.3 ; + RECT 4.25 7.17 4.315 7.415 ; + RECT 4.25 7.17 4.3725 7.2525 ; + RECT 4.3725 7.165 4.5075 7.2325 ; + RECT 4.12 7.1 4.185 7.875 ; + RECT 2.5 7.61 2.79 7.675 ; + RECT 1.9625 7.425 2.13 7.49 ; + RECT 1.47 7.1675 1.54 7.425 ; + RECT 1.405 7.61 1.83 7.675 ; + RECT 2.5 7.42 2.79 7.49 ; + RECT 1.66 7.61 1.735 7.7825 ; + RECT 2.5 7.235 2.79 7.3 ; + RECT 1.34 7.2325 1.4125 7.2975 ; + RECT 3.02 7.105 3.09 7.49 ; + RECT 2.05 7.1925 2.165 7.2575 ; + RECT 1.275 7.1 1.34 7.875 ; + RECT 2.13 7.1 2.195 7.875 ; + RECT 1.12 7.67 1.1775 7.735 ; + RECT 1.36 7.1025 1.4825 7.1675 ; + RECT 4.9225 7.285 5.09 7.35 ; + RECT 4.315 7.3475 4.4175 7.415 ; + RECT 3.6 6.97 3.735 7.035 ; + RECT 3.6 6.785 3.735 6.85 ; + RECT 2.365 6.97 2.5 7.035 ; + RECT 2.365 6.785 2.5 6.85 ; + RECT 3.6 6.78 3.735 6.845 ; + RECT 3.6 6.595 3.735 6.66 ; + RECT 6.56 6.78 6.695 6.845 ; + RECT 6.56 6.595 6.695 6.66 ; + RECT 2.365 6.78 2.5 6.845 ; + RECT 2.365 6.595 2.5 6.66 ; + RECT 6.56 6.97 6.695 7.035 ; + RECT 6.56 6.785 6.695 6.85 ; + RECT 1.83 6.78 1.965 6.845 ; + RECT 1.83 6.595 1.965 6.66 ; + RECT 4.79 6.92 4.925 6.985 ; + RECT 4.79 6.735 4.925 6.8 ; + RECT 5.325 6.97 5.46 7.035 ; + RECT 5.325 6.785 5.46 6.85 ; + RECT 5.325 6.78 5.46 6.845 ; + RECT 5.325 6.595 5.46 6.66 ; + RECT 5.75 6.97 5.885 7.035 ; + RECT 5.75 6.785 5.885 6.85 ; + RECT 2.79 6.97 2.925 7.035 ; + RECT 2.79 6.785 2.925 6.85 ; + RECT 6.135 6.97 6.27 7.035 ; + RECT 6.135 6.785 6.27 6.85 ; + RECT 6.135 6.78 6.27 6.845 ; + RECT 6.135 6.595 6.27 6.66 ; + RECT 2.79 6.78 2.925 6.845 ; + RECT 2.79 6.595 2.925 6.66 ; + RECT 5.75 6.78 5.885 6.845 ; + RECT 5.75 6.595 5.885 6.66 ; + RECT 3.175 6.78 3.31 6.845 ; + RECT 3.175 6.595 3.31 6.66 ; + RECT 3.175 6.97 3.31 7.035 ; + RECT 3.175 6.785 3.31 6.85 ; + RECT 1.405 6.78 1.54 6.845 ; + RECT 1.405 6.595 1.54 6.66 ; + RECT 4.38 6.855 4.515 6.92 ; + RECT 4.38 6.67 4.515 6.735 ; + RECT 1.115 6.6575 1.18 6.7925 ; + RECT 6.6125 6.595 6.7475 6.66 ; + RECT 4.7975 6.485 4.9325 6.55 ; + RECT 3.8975 6.595 4.0325 6.66 ; + RECT 4.3725 7.0175 4.5075 7.0825 ; + RECT 6.9775 6.595 7.1125 6.66 ; + RECT 1.435 7.1025 1.57 7.1675 ; + RECT 5.5175 6.96 5.6525 7.025 ; + RECT 3.91 6.9625 4.045 7.0275 ; + RECT 2.5125 6.9625 2.6475 7.0275 ; + RECT 1.295 6.3975 1.43 6.4625 ; + RECT 1.4125 6.9725 1.5475 7.0375 ; + RECT 3.0225 6.9725 3.0875 7.1075 ; + RECT 1.9375 7.0125 2.0725 7.0775 ; + RECT 5.9825 6.9725 6.0475 7.1075 ; + RECT 6.87 6.92 7.005 6.985 ; + RECT 3.4 6.84 3.535 6.905 ; + RECT 6.36 6.83 6.495 6.895 ; + RECT 4.67 6.5975 4.735 6.7325 ; + RECT 4.3725 7.1025 4.5075 7.1675 ; + RECT 1.665 6.4875 1.73 6.6225 ; + RECT 3.1775 6.5975 3.3125 6.6625 ; + RECT 6.87 6.9625 7.005 7.0275 ; + RECT 6.36 6.785 6.495 6.85 ; + RECT 5.9475 7.1025 6.0825 7.1675 ; + RECT 1.245 7.1025 1.38 7.1675 ; + RECT 3.91 6.9625 4.045 7.0275 ; + RECT 2.9875 7.1025 3.1225 7.1675 ; + RECT 2.7875 6.5975 2.9225 6.6625 ; + RECT 2.7875 6.785 2.9225 6.85 ; + RECT 3.4 6.785 3.535 6.85 ; + RECT 5.7475 6.595 5.8825 6.66 ; + RECT 5.7475 6.785 5.8825 6.85 ; + RECT 1.115 6.395 1.18 7.17 ; + RECT 4.185 6.465 7.08 6.53 ; + RECT 6.695 6.97 7.005 7.035 ; + RECT 6.27 6.595 6.56 6.66 ; + RECT 5.46 6.595 5.75 6.66 ; + RECT 7.0825 6.735 7.145 6.8075 ; + RECT 6.695 6.78 7.08 6.85 ; + RECT 5.46 6.78 5.75 6.85 ; + RECT 5.98 6.78 6.1375 6.85 ; + RECT 6.27 6.97 6.56 7.035 ; + RECT 5.46 6.97 5.75 7.035 ; + RECT 5.98 6.78 6.05 7.165 ; + RECT 7.08 6.395 7.145 7.17 ; + RECT 5.09 6.395 5.155 7.17 ; + RECT 2.195 6.465 4.12 6.53 ; + RECT 3.735 6.97 4.045 7.035 ; + RECT 3.31 6.595 3.6 6.66 ; + RECT 4.475 6.67 4.925 6.735 ; + RECT 3.735 6.78 4.12 6.85 ; + RECT 3.02 6.78 3.1775 6.85 ; + RECT 4.0325 6.595 4.12 6.66 ; + RECT 3.31 6.97 3.6 7.035 ; + RECT 4.25 6.855 4.315 7.1 ; + RECT 4.25 7.0175 4.3725 7.1 ; + RECT 4.3725 7.0375 4.5075 7.105 ; + RECT 4.12 6.395 4.185 7.17 ; + RECT 2.5 6.595 2.79 6.66 ; + RECT 1.9625 6.78 2.13 6.845 ; + RECT 1.47 6.845 1.54 7.1025 ; + RECT 1.405 6.595 1.83 6.66 ; + RECT 2.5 6.78 2.79 6.85 ; + RECT 1.66 6.4875 1.735 6.66 ; + RECT 2.5 6.97 2.79 7.035 ; + RECT 1.34 6.9725 1.4125 7.0375 ; + RECT 3.02 6.78 3.09 7.165 ; + RECT 2.05 7.0125 2.165 7.0775 ; + RECT 1.275 6.395 1.34 7.17 ; + RECT 2.13 6.395 2.195 7.17 ; + RECT 1.12 6.535 1.1775 6.6 ; + RECT 1.36 7.1025 1.4825 7.1675 ; + RECT 4.9225 6.92 5.09 6.985 ; + RECT 4.315 6.855 4.4175 6.9225 ; + RECT 3.6 5.825 3.735 5.89 ; + RECT 3.6 6.01 3.735 6.075 ; + RECT 2.365 5.825 2.5 5.89 ; + RECT 2.365 6.01 2.5 6.075 ; + RECT 3.6 6.015 3.735 6.08 ; + RECT 3.6 6.2 3.735 6.265 ; + RECT 6.56 6.015 6.695 6.08 ; + RECT 6.56 6.2 6.695 6.265 ; + RECT 2.365 6.015 2.5 6.08 ; + RECT 2.365 6.2 2.5 6.265 ; + RECT 6.56 5.825 6.695 5.89 ; + RECT 6.56 6.01 6.695 6.075 ; + RECT 1.83 6.015 1.965 6.08 ; + RECT 1.83 6.2 1.965 6.265 ; + RECT 4.79 5.875 4.925 5.94 ; + RECT 4.79 6.06 4.925 6.125 ; + RECT 5.325 5.825 5.46 5.89 ; + RECT 5.325 6.01 5.46 6.075 ; + RECT 5.325 6.015 5.46 6.08 ; + RECT 5.325 6.2 5.46 6.265 ; + RECT 5.75 5.825 5.885 5.89 ; + RECT 5.75 6.01 5.885 6.075 ; + RECT 2.79 5.825 2.925 5.89 ; + RECT 2.79 6.01 2.925 6.075 ; + RECT 6.135 5.825 6.27 5.89 ; + RECT 6.135 6.01 6.27 6.075 ; + RECT 6.135 6.015 6.27 6.08 ; + RECT 6.135 6.2 6.27 6.265 ; + RECT 2.79 6.015 2.925 6.08 ; + RECT 2.79 6.2 2.925 6.265 ; + RECT 5.75 6.015 5.885 6.08 ; + RECT 5.75 6.2 5.885 6.265 ; + RECT 3.175 6.015 3.31 6.08 ; + RECT 3.175 6.2 3.31 6.265 ; + RECT 3.175 5.825 3.31 5.89 ; + RECT 3.175 6.01 3.31 6.075 ; + RECT 1.405 6.015 1.54 6.08 ; + RECT 1.405 6.2 1.54 6.265 ; + RECT 4.38 5.94 4.515 6.005 ; + RECT 4.38 6.125 4.515 6.19 ; + RECT 1.115 6.0675 1.18 6.2025 ; + RECT 6.6125 6.2 6.7475 6.265 ; + RECT 4.7975 6.31 4.9325 6.375 ; + RECT 3.8975 6.2 4.0325 6.265 ; + RECT 4.3725 5.7775 4.5075 5.8425 ; + RECT 6.9775 6.2 7.1125 6.265 ; + RECT 1.435 5.6925 1.57 5.7575 ; + RECT 5.5175 5.835 5.6525 5.9 ; + RECT 3.91 5.8325 4.045 5.8975 ; + RECT 2.5125 5.8325 2.6475 5.8975 ; + RECT 1.295 6.3975 1.43 6.4625 ; + RECT 1.4125 5.8225 1.5475 5.8875 ; + RECT 3.0225 5.7525 3.0875 5.8875 ; + RECT 1.9375 5.7825 2.0725 5.8475 ; + RECT 5.9825 5.7525 6.0475 5.8875 ; + RECT 6.87 5.875 7.005 5.94 ; + RECT 3.4 5.955 3.535 6.02 ; + RECT 6.36 5.965 6.495 6.03 ; + RECT 4.67 6.1275 4.735 6.2625 ; + RECT 4.3725 5.6925 4.5075 5.7575 ; + RECT 1.665 6.2375 1.73 6.3725 ; + RECT 3.1775 6.1975 3.3125 6.2625 ; + RECT 6.87 5.8325 7.005 5.8975 ; + RECT 6.36 6.01 6.495 6.075 ; + RECT 5.9475 5.6925 6.0825 5.7575 ; + RECT 1.245 5.6925 1.38 5.7575 ; + RECT 3.91 5.8325 4.045 5.8975 ; + RECT 2.9875 5.6925 3.1225 5.7575 ; + RECT 2.7875 6.1975 2.9225 6.2625 ; + RECT 2.7875 6.01 2.9225 6.075 ; + RECT 3.4 6.01 3.535 6.075 ; + RECT 5.7475 6.2 5.8825 6.265 ; + RECT 5.7475 6.01 5.8825 6.075 ; + RECT 1.115 5.69 1.18 6.465 ; + RECT 4.185 6.33 7.08 6.395 ; + RECT 6.695 5.825 7.005 5.89 ; + RECT 6.27 6.2 6.56 6.265 ; + RECT 5.46 6.2 5.75 6.265 ; + RECT 7.0825 6.0525 7.145 6.125 ; + RECT 6.695 6.01 7.08 6.08 ; + RECT 5.46 6.01 5.75 6.08 ; + RECT 5.98 6.01 6.1375 6.08 ; + RECT 6.27 5.825 6.56 5.89 ; + RECT 5.46 5.825 5.75 5.89 ; + RECT 5.98 5.695 6.05 6.08 ; + RECT 7.08 5.69 7.145 6.465 ; + RECT 5.09 5.69 5.155 6.465 ; + RECT 2.195 6.33 4.12 6.395 ; + RECT 3.735 5.825 4.045 5.89 ; + RECT 3.31 6.2 3.6 6.265 ; + RECT 4.475 6.125 4.925 6.19 ; + RECT 3.735 6.01 4.12 6.08 ; + RECT 3.02 6.01 3.1775 6.08 ; + RECT 4.0325 6.2 4.12 6.265 ; + RECT 3.31 5.825 3.6 5.89 ; + RECT 4.25 5.76 4.315 6.005 ; + RECT 4.25 5.76 4.3725 5.8425 ; + RECT 4.3725 5.755 4.5075 5.8225 ; + RECT 4.12 5.69 4.185 6.465 ; + RECT 2.5 6.2 2.79 6.265 ; + RECT 1.9625 6.015 2.13 6.08 ; + RECT 1.47 5.7575 1.54 6.015 ; + RECT 1.405 6.2 1.83 6.265 ; + RECT 2.5 6.01 2.79 6.08 ; + RECT 1.66 6.2 1.735 6.3725 ; + RECT 2.5 5.825 2.79 5.89 ; + RECT 1.34 5.8225 1.4125 5.8875 ; + RECT 3.02 5.695 3.09 6.08 ; + RECT 2.05 5.7825 2.165 5.8475 ; + RECT 1.275 5.69 1.34 6.465 ; + RECT 2.13 5.69 2.195 6.465 ; + RECT 1.12 6.26 1.1775 6.325 ; + RECT 1.36 5.6925 1.4825 5.7575 ; + RECT 4.9225 5.875 5.09 5.94 ; + RECT 4.315 5.9375 4.4175 6.005 ; + RECT 3.6 5.56 3.735 5.625 ; + RECT 3.6 5.375 3.735 5.44 ; + RECT 2.365 5.56 2.5 5.625 ; + RECT 2.365 5.375 2.5 5.44 ; + RECT 3.6 5.37 3.735 5.435 ; + RECT 3.6 5.185 3.735 5.25 ; + RECT 6.56 5.37 6.695 5.435 ; + RECT 6.56 5.185 6.695 5.25 ; + RECT 2.365 5.37 2.5 5.435 ; + RECT 2.365 5.185 2.5 5.25 ; + RECT 6.56 5.56 6.695 5.625 ; + RECT 6.56 5.375 6.695 5.44 ; + RECT 1.83 5.37 1.965 5.435 ; + RECT 1.83 5.185 1.965 5.25 ; + RECT 4.79 5.51 4.925 5.575 ; + RECT 4.79 5.325 4.925 5.39 ; + RECT 5.325 5.56 5.46 5.625 ; + RECT 5.325 5.375 5.46 5.44 ; + RECT 5.325 5.37 5.46 5.435 ; + RECT 5.325 5.185 5.46 5.25 ; + RECT 5.75 5.56 5.885 5.625 ; + RECT 5.75 5.375 5.885 5.44 ; + RECT 2.79 5.56 2.925 5.625 ; + RECT 2.79 5.375 2.925 5.44 ; + RECT 6.135 5.56 6.27 5.625 ; + RECT 6.135 5.375 6.27 5.44 ; + RECT 6.135 5.37 6.27 5.435 ; + RECT 6.135 5.185 6.27 5.25 ; + RECT 2.79 5.37 2.925 5.435 ; + RECT 2.79 5.185 2.925 5.25 ; + RECT 5.75 5.37 5.885 5.435 ; + RECT 5.75 5.185 5.885 5.25 ; + RECT 3.175 5.37 3.31 5.435 ; + RECT 3.175 5.185 3.31 5.25 ; + RECT 3.175 5.56 3.31 5.625 ; + RECT 3.175 5.375 3.31 5.44 ; + RECT 1.405 5.37 1.54 5.435 ; + RECT 1.405 5.185 1.54 5.25 ; + RECT 4.38 5.445 4.515 5.51 ; + RECT 4.38 5.26 4.515 5.325 ; + RECT 1.115 5.2475 1.18 5.3825 ; + RECT 6.6125 5.185 6.7475 5.25 ; + RECT 4.7975 5.075 4.9325 5.14 ; + RECT 3.8975 5.185 4.0325 5.25 ; + RECT 4.3725 5.6075 4.5075 5.6725 ; + RECT 6.9775 5.185 7.1125 5.25 ; + RECT 1.435 5.6925 1.57 5.7575 ; + RECT 5.5175 5.55 5.6525 5.615 ; + RECT 3.91 5.5525 4.045 5.6175 ; + RECT 2.5125 5.5525 2.6475 5.6175 ; + RECT 1.295 4.9875 1.43 5.0525 ; + RECT 1.4125 5.5625 1.5475 5.6275 ; + RECT 3.0225 5.5625 3.0875 5.6975 ; + RECT 1.9375 5.6025 2.0725 5.6675 ; + RECT 5.9825 5.5625 6.0475 5.6975 ; + RECT 6.87 5.51 7.005 5.575 ; + RECT 3.4 5.43 3.535 5.495 ; + RECT 6.36 5.42 6.495 5.485 ; + RECT 4.67 5.1875 4.735 5.3225 ; + RECT 4.3725 5.6925 4.5075 5.7575 ; + RECT 1.665 5.0775 1.73 5.2125 ; + RECT 3.1775 5.1875 3.3125 5.2525 ; + RECT 6.87 5.5525 7.005 5.6175 ; + RECT 6.36 5.375 6.495 5.44 ; + RECT 5.9475 5.6925 6.0825 5.7575 ; + RECT 1.245 5.6925 1.38 5.7575 ; + RECT 3.91 5.5525 4.045 5.6175 ; + RECT 2.9875 5.6925 3.1225 5.7575 ; + RECT 2.7875 5.1875 2.9225 5.2525 ; + RECT 2.7875 5.375 2.9225 5.44 ; + RECT 3.4 5.375 3.535 5.44 ; + RECT 5.7475 5.185 5.8825 5.25 ; + RECT 5.7475 5.375 5.8825 5.44 ; + RECT 1.115 4.985 1.18 5.76 ; + RECT 4.185 5.055 7.08 5.12 ; + RECT 6.695 5.56 7.005 5.625 ; + RECT 6.27 5.185 6.56 5.25 ; + RECT 5.46 5.185 5.75 5.25 ; + RECT 7.0825 5.325 7.145 5.3975 ; + RECT 6.695 5.37 7.08 5.44 ; + RECT 5.46 5.37 5.75 5.44 ; + RECT 5.98 5.37 6.1375 5.44 ; + RECT 6.27 5.56 6.56 5.625 ; + RECT 5.46 5.56 5.75 5.625 ; + RECT 5.98 5.37 6.05 5.755 ; + RECT 7.08 4.985 7.145 5.76 ; + RECT 5.09 4.985 5.155 5.76 ; + RECT 2.195 5.055 4.12 5.12 ; + RECT 3.735 5.56 4.045 5.625 ; + RECT 3.31 5.185 3.6 5.25 ; + RECT 4.475 5.26 4.925 5.325 ; + RECT 3.735 5.37 4.12 5.44 ; + RECT 3.02 5.37 3.1775 5.44 ; + RECT 4.0325 5.185 4.12 5.25 ; + RECT 3.31 5.56 3.6 5.625 ; + RECT 4.25 5.445 4.315 5.69 ; + RECT 4.25 5.6075 4.3725 5.69 ; + RECT 4.3725 5.6275 4.5075 5.695 ; + RECT 4.12 4.985 4.185 5.76 ; + RECT 2.5 5.185 2.79 5.25 ; + RECT 1.9625 5.37 2.13 5.435 ; + RECT 1.47 5.435 1.54 5.6925 ; + RECT 1.405 5.185 1.83 5.25 ; + RECT 2.5 5.37 2.79 5.44 ; + RECT 1.66 5.0775 1.735 5.25 ; + RECT 2.5 5.56 2.79 5.625 ; + RECT 1.34 5.5625 1.4125 5.6275 ; + RECT 3.02 5.37 3.09 5.755 ; + RECT 2.05 5.6025 2.165 5.6675 ; + RECT 1.275 4.985 1.34 5.76 ; + RECT 2.13 4.985 2.195 5.76 ; + RECT 1.12 5.125 1.1775 5.19 ; + RECT 1.36 5.6925 1.4825 5.7575 ; + RECT 4.9225 5.51 5.09 5.575 ; + RECT 4.315 5.445 4.4175 5.5125 ; + RECT 7.855 19.0025 7.92 19.0675 ; + RECT 7.8875 19.0025 11.1625 19.0675 ; + RECT 7.855 19.035 7.92 19.575 ; + RECT 7.855 21.4125 7.92 21.4775 ; + RECT 7.8875 21.4125 11.1625 21.4775 ; + RECT 7.855 20.905 7.92 21.445 ; + RECT 7.855 21.6925 7.92 21.7575 ; + RECT 7.8875 21.6925 11.1625 21.7575 ; + RECT 7.855 21.725 7.92 22.265 ; + RECT 7.855 24.1025 7.92 24.1675 ; + RECT 7.8875 24.1025 11.1625 24.1675 ; + RECT 7.855 23.595 7.92 24.135 ; + RECT 7.855 24.3825 7.92 24.4475 ; + RECT 7.8875 24.3825 11.1625 24.4475 ; + RECT 7.855 24.415 7.92 24.955 ; + RECT 7.855 26.7925 7.92 26.8575 ; + RECT 7.8875 26.7925 11.1625 26.8575 ; + RECT 7.855 26.285 7.92 26.825 ; + RECT 7.855 27.0725 7.92 27.1375 ; + RECT 7.8875 27.0725 11.1625 27.1375 ; + RECT 7.855 27.105 7.92 27.645 ; + RECT 7.855 29.4825 7.92 29.5475 ; + RECT 7.8875 29.4825 11.1625 29.5475 ; + RECT 7.855 28.975 7.92 29.515 ; + RECT 7.855 29.7625 7.92 29.8275 ; + RECT 7.8875 29.7625 11.1625 29.8275 ; + RECT 7.855 29.795 7.92 30.335 ; + RECT 7.855 32.1725 7.92 32.2375 ; + RECT 7.8875 32.1725 11.1625 32.2375 ; + RECT 7.855 31.665 7.92 32.205 ; + RECT 7.855 32.4525 7.92 32.5175 ; + RECT 7.8875 32.4525 11.1625 32.5175 ; + RECT 7.855 32.485 7.92 33.025 ; + RECT 7.855 34.8625 7.92 34.9275 ; + RECT 7.8875 34.8625 11.1625 34.9275 ; + RECT 7.855 34.355 7.92 34.895 ; + RECT 7.855 35.1425 7.92 35.2075 ; + RECT 7.8875 35.1425 11.1625 35.2075 ; + RECT 7.855 35.175 7.92 35.715 ; + RECT 7.855 37.5525 7.92 37.6175 ; + RECT 7.8875 37.5525 11.1625 37.6175 ; + RECT 7.855 37.045 7.92 37.585 ; + RECT 7.855 37.8325 7.92 37.8975 ; + RECT 7.8875 37.8325 11.1625 37.8975 ; + RECT 7.855 37.865 7.92 38.405 ; + RECT 7.855 40.2425 7.92 40.3075 ; + RECT 7.8875 40.2425 11.1625 40.3075 ; + RECT 7.855 39.735 7.92 40.275 ; + RECT 8.5425 8.73 8.6075 8.865 ; + RECT 8.3325 10.165 8.3975 10.3 ; + RECT 8.1225 14.11 8.1875 14.245 ; + RECT 7.9125 15.545 7.9775 15.68 ; + RECT 10.3625 3.6 10.4275 3.735 ; + RECT 9.9425 1.415 10.0075 1.55 ; + RECT 10.1525 2.9625 10.2175 3.0975 ; + RECT 10.3625 41.1 10.4275 41.235 ; + RECT 10.5725 10.1025 10.6375 10.2375 ; + RECT 10.7825 14.1275 10.8475 14.2625 ; + RECT 0.98 7.63 1.115 7.695 ; + RECT 5.725 41.53 5.79 41.595 ; + RECT 5.725 40.415 5.79 41.5625 ; + RECT 5.7575 41.53 9.8275 41.595 ; + RECT 9.7325 41.53 9.7975 41.665 ; + RECT 7.08 4.7925 7.145 4.8575 ; + RECT 0.0 4.7925 7.1125 4.8575 ; + RECT 7.08 4.825 7.145 5.02 ; + RECT 8.89 40.545 9.095 40.68 ; + RECT 11.77 40.5475 11.905 40.6125 ; + RECT 12.475 40.5475 12.61 40.6125 ; + RECT 10.995 40.5475 11.13 40.6125 ; + RECT 9.385 0.355 9.59 0.49 ; + RECT 11.8025 0.355 11.8675 0.49 ; + RECT 11.8025 0.355 11.8675 0.49 ; + RECT 7.755 21.555 7.89 21.62 ; + RECT 7.755 24.245 7.89 24.31 ; + RECT 7.755 26.935 7.89 27.0 ; + RECT 7.755 29.625 7.89 29.69 ; + RECT 7.755 32.315 7.89 32.38 ; + RECT 7.755 35.005 7.89 35.07 ; + RECT 7.755 37.695 7.89 37.76 ; + RECT 7.755 40.385 7.89 40.45 ; + RECT 8.89 13.4125 9.095 13.5475 ; + RECT 8.89 18.7925 9.095 18.9275 ; + RECT 7.28 7.81 7.415 7.875 ; + RECT 8.89 7.8075 9.095 7.9425 ; + RECT 7.28 6.4 7.415 6.465 ; + RECT 8.89 6.3975 9.095 6.5325 ; + RECT 7.28 6.4 7.415 6.465 ; + RECT 8.89 6.3975 9.095 6.5325 ; + RECT 7.28 4.99 7.415 5.055 ; + RECT 8.89 4.9875 9.095 5.1225 ; + RECT -2.49 17.335 -2.3225 17.4 ; + RECT -3.7575 17.335 -3.59 17.4 ; + RECT -3.9975 14.715 -3.9325 15.205 ; + RECT -4.1525 14.715 -4.0875 15.415 ; + RECT -5.5625 14.715 -5.4975 15.625 ; + RECT -4.5475 14.715 -4.4825 15.835 ; + RECT -1.325 15.345 -1.26 16.185 ; + RECT -0.795 15.975 -0.73 16.185 ; + RECT -2.12 15.975 -2.055 16.185 ; + RECT -2.6425 15.345 -2.5775 16.185 ; + RECT -2.7825 15.555 -2.7175 16.185 ; + RECT -4.025 15.975 -3.96 16.185 ; + RECT -3.5025 15.765 -3.4375 16.185 ; + RECT -3.3625 15.555 -3.2975 16.185 ; + RECT -2.49 17.895 -2.425 19.505 ; + RECT -3.655 19.015 -3.59 19.295 ; + RECT -0.965 18.455 -0.9 19.715 ; + RECT -0.3825 23.7575 -0.14 23.8225 ; + RECT -1.7275 8.415 -1.6625 20.065 ; + RECT -4.4175 17.335 -4.3525 19.925 ; + RECT -5.73 14.4425 -1.6625 14.5075 ; + RECT -0.3825 8.415 -0.3175 20.835 ; + RECT -3.0725 17.335 -3.0075 20.695 ; + RECT -5.3775 8.4775 -2.0525 8.5425 ; + RECT -5.73 8.4475 -3.615 8.5125 ; + RECT -5.73 14.445 -3.615 14.51 ; + RECT -5.19 10.965 -5.125 11.1 ; + RECT -5.375 10.965 -5.31 11.1 ; + RECT -5.19 9.73 -5.125 9.865 ; + RECT -5.375 9.73 -5.31 9.865 ; + RECT -5.38 10.965 -5.315 11.1 ; + RECT -5.565 10.965 -5.5 11.1 ; + RECT -5.38 13.925 -5.315 14.06 ; + RECT -5.565 13.925 -5.5 14.06 ; + RECT -5.38 9.73 -5.315 9.865 ; + RECT -5.565 9.73 -5.5 9.865 ; + RECT -5.19 13.925 -5.125 14.06 ; + RECT -5.375 13.925 -5.31 14.06 ; + RECT -5.38 9.195 -5.315 9.33 ; + RECT -5.565 9.195 -5.5 9.33 ; + RECT -5.24 12.155 -5.175 12.29 ; + RECT -5.425 12.155 -5.36 12.29 ; + RECT -5.19 12.69 -5.125 12.825 ; + RECT -5.375 12.69 -5.31 12.825 ; + RECT -5.38 12.69 -5.315 12.825 ; + RECT -5.565 12.69 -5.5 12.825 ; + RECT -5.19 13.115 -5.125 13.25 ; + RECT -5.375 13.115 -5.31 13.25 ; + RECT -5.19 10.155 -5.125 10.29 ; + RECT -5.375 10.155 -5.31 10.29 ; + RECT -5.19 13.5 -5.125 13.635 ; + RECT -5.375 13.5 -5.31 13.635 ; + RECT -5.38 13.5 -5.315 13.635 ; + RECT -5.565 13.5 -5.5 13.635 ; + RECT -5.38 10.155 -5.315 10.29 ; + RECT -5.565 10.155 -5.5 10.29 ; + RECT -5.38 13.115 -5.315 13.25 ; + RECT -5.565 13.115 -5.5 13.25 ; + RECT -5.38 10.54 -5.315 10.675 ; + RECT -5.565 10.54 -5.5 10.675 ; + RECT -5.19 10.54 -5.125 10.675 ; + RECT -5.375 10.54 -5.31 10.675 ; + RECT -5.38 8.77 -5.315 8.905 ; + RECT -5.565 8.77 -5.5 8.905 ; + RECT -5.305 11.745 -5.24 11.88 ; + RECT -5.49 11.745 -5.425 11.88 ; + RECT -5.5025 8.48 -5.3675 8.545 ; + RECT -5.565 13.9775 -5.5 14.1125 ; + RECT -5.675 12.1625 -5.61 12.2975 ; + RECT -5.565 11.2625 -5.5 11.3975 ; + RECT -5.1425 11.7375 -5.0775 11.8725 ; + RECT -5.565 14.3425 -5.5 14.4775 ; + RECT -5.0575 8.8 -4.9925 8.935 ; + RECT -5.2 12.8825 -5.135 13.0175 ; + RECT -5.1975 11.275 -5.1325 11.41 ; + RECT -5.1975 9.8775 -5.1325 10.0125 ; + RECT -5.7625 8.66 -5.6975 8.795 ; + RECT -5.1875 8.7775 -5.1225 8.9125 ; + RECT -5.1875 10.3875 -5.0525 10.4525 ; + RECT -5.1475 9.3025 -5.0825 9.4375 ; + RECT -5.1875 13.3475 -5.0525 13.4125 ; + RECT -5.24 14.235 -5.175 14.37 ; + RECT -5.32 10.765 -5.255 10.9 ; + RECT -5.33 13.725 -5.265 13.86 ; + RECT -5.5625 12.035 -5.4275 12.1 ; + RECT -5.0575 11.7375 -4.9925 11.8725 ; + RECT -5.6725 9.03 -5.5375 9.095 ; + RECT -5.5625 10.5425 -5.4975 10.6775 ; + RECT -5.1975 14.235 -5.1325 14.37 ; + RECT -5.375 13.725 -5.31 13.86 ; + RECT -5.0575 13.3125 -4.9925 13.4475 ; + RECT -5.0575 8.61 -4.9925 8.745 ; + RECT -5.1975 11.275 -5.1325 11.41 ; + RECT -5.0575 10.3525 -4.9925 10.4875 ; + RECT -5.5625 10.1525 -5.4975 10.2875 ; + RECT -5.375 10.1525 -5.31 10.2875 ; + RECT -5.375 10.765 -5.31 10.9 ; + RECT -5.565 13.1125 -5.5 13.2475 ; + RECT -5.375 13.1125 -5.31 13.2475 ; + RECT -5.765 8.48 -4.99 8.545 ; + RECT -5.695 11.55 -5.63 14.445 ; + RECT -5.19 14.06 -5.125 14.37 ; + RECT -5.565 13.635 -5.5 13.925 ; + RECT -5.565 12.825 -5.5 13.115 ; + RECT -5.425 14.4475 -5.3525 14.51 ; + RECT -5.38 14.06 -5.31 14.445 ; + RECT -5.38 12.825 -5.31 13.115 ; + RECT -5.38 13.345 -5.31 13.5025 ; + RECT -5.19 13.635 -5.125 13.925 ; + RECT -5.19 12.825 -5.125 13.115 ; + RECT -5.38 13.345 -4.995 13.415 ; + RECT -5.765 14.445 -4.99 14.51 ; + RECT -5.765 12.455 -4.99 12.52 ; + RECT -5.695 9.56 -5.63 11.485 ; + RECT -5.19 11.1 -5.125 11.41 ; + RECT -5.565 10.675 -5.5 10.965 ; + RECT -5.49 11.84 -5.425 12.29 ; + RECT -5.38 11.1 -5.31 11.485 ; + RECT -5.38 10.385 -5.31 10.5425 ; + RECT -5.565 11.3975 -5.5 11.485 ; + RECT -5.19 10.675 -5.125 10.965 ; + RECT -5.305 11.615 -5.06 11.68 ; + RECT -5.1425 11.615 -5.06 11.7375 ; + RECT -5.1225 11.7375 -5.055 11.8725 ; + RECT -5.765 11.485 -4.99 11.55 ; + RECT -5.565 9.865 -5.5 10.155 ; + RECT -5.38 9.3275 -5.315 9.495 ; + RECT -5.315 8.835 -5.0575 8.905 ; + RECT -5.565 8.77 -5.5 9.195 ; + RECT -5.38 9.865 -5.31 10.155 ; + RECT -5.6725 9.025 -5.5 9.1 ; + RECT -5.19 9.865 -5.125 10.155 ; + RECT -5.1875 8.705 -5.1225 8.7775 ; + RECT -5.38 10.385 -4.995 10.455 ; + RECT -5.1475 9.415 -5.0825 9.53 ; + RECT -5.765 8.64 -4.99 8.705 ; + RECT -5.765 9.495 -4.99 9.56 ; + RECT -5.625 8.485 -5.56 8.5425 ; + RECT -5.0575 8.725 -4.9925 8.8475 ; + RECT -5.24 12.2875 -5.175 12.455 ; + RECT -5.305 11.68 -5.2375 11.7825 ; + RECT -4.925 10.965 -4.86 11.1 ; + RECT -4.74 10.965 -4.675 11.1 ; + RECT -4.925 9.73 -4.86 9.865 ; + RECT -4.74 9.73 -4.675 9.865 ; + RECT -4.735 10.965 -4.67 11.1 ; + RECT -4.55 10.965 -4.485 11.1 ; + RECT -4.735 13.925 -4.67 14.06 ; + RECT -4.55 13.925 -4.485 14.06 ; + RECT -4.735 9.73 -4.67 9.865 ; + RECT -4.55 9.73 -4.485 9.865 ; + RECT -4.925 13.925 -4.86 14.06 ; + RECT -4.74 13.925 -4.675 14.06 ; + RECT -4.735 9.195 -4.67 9.33 ; + RECT -4.55 9.195 -4.485 9.33 ; + RECT -4.875 12.155 -4.81 12.29 ; + RECT -4.69 12.155 -4.625 12.29 ; + RECT -4.925 12.69 -4.86 12.825 ; + RECT -4.74 12.69 -4.675 12.825 ; + RECT -4.735 12.69 -4.67 12.825 ; + RECT -4.55 12.69 -4.485 12.825 ; + RECT -4.925 13.115 -4.86 13.25 ; + RECT -4.74 13.115 -4.675 13.25 ; + RECT -4.925 10.155 -4.86 10.29 ; + RECT -4.74 10.155 -4.675 10.29 ; + RECT -4.925 13.5 -4.86 13.635 ; + RECT -4.74 13.5 -4.675 13.635 ; + RECT -4.735 13.5 -4.67 13.635 ; + RECT -4.55 13.5 -4.485 13.635 ; + RECT -4.735 10.155 -4.67 10.29 ; + RECT -4.55 10.155 -4.485 10.29 ; + RECT -4.735 13.115 -4.67 13.25 ; + RECT -4.55 13.115 -4.485 13.25 ; + RECT -4.735 10.54 -4.67 10.675 ; + RECT -4.55 10.54 -4.485 10.675 ; + RECT -4.925 10.54 -4.86 10.675 ; + RECT -4.74 10.54 -4.675 10.675 ; + RECT -4.735 8.77 -4.67 8.905 ; + RECT -4.55 8.77 -4.485 8.905 ; + RECT -4.81 11.745 -4.745 11.88 ; + RECT -4.625 11.745 -4.56 11.88 ; + RECT -4.6825 8.48 -4.5475 8.545 ; + RECT -4.55 13.9775 -4.485 14.1125 ; + RECT -4.44 12.1625 -4.375 12.2975 ; + RECT -4.55 11.2625 -4.485 11.3975 ; + RECT -4.9725 11.7375 -4.9075 11.8725 ; + RECT -4.55 14.3425 -4.485 14.4775 ; + RECT -5.0575 8.8 -4.9925 8.935 ; + RECT -4.915 12.8825 -4.85 13.0175 ; + RECT -4.9175 11.275 -4.8525 11.41 ; + RECT -4.9175 9.8775 -4.8525 10.0125 ; + RECT -4.3525 8.66 -4.2875 8.795 ; + RECT -4.9275 8.7775 -4.8625 8.9125 ; + RECT -4.9975 10.3875 -4.8625 10.4525 ; + RECT -4.9675 9.3025 -4.9025 9.4375 ; + RECT -4.9975 13.3475 -4.8625 13.4125 ; + RECT -4.875 14.235 -4.81 14.37 ; + RECT -4.795 10.765 -4.73 10.9 ; + RECT -4.785 13.725 -4.72 13.86 ; + RECT -4.6225 12.035 -4.4875 12.1 ; + RECT -5.0575 11.7375 -4.9925 11.8725 ; + RECT -4.5125 9.03 -4.3775 9.095 ; + RECT -4.5525 10.5425 -4.4875 10.6775 ; + RECT -4.9175 14.235 -4.8525 14.37 ; + RECT -4.74 13.725 -4.675 13.86 ; + RECT -5.0575 13.3125 -4.9925 13.4475 ; + RECT -5.0575 8.61 -4.9925 8.745 ; + RECT -4.9175 11.275 -4.8525 11.41 ; + RECT -5.0575 10.3525 -4.9925 10.4875 ; + RECT -4.5525 10.1525 -4.4875 10.2875 ; + RECT -4.74 10.1525 -4.675 10.2875 ; + RECT -4.74 10.765 -4.675 10.9 ; + RECT -4.55 13.1125 -4.485 13.2475 ; + RECT -4.74 13.1125 -4.675 13.2475 ; + RECT -5.06 8.48 -4.285 8.545 ; + RECT -4.42 11.55 -4.355 14.445 ; + RECT -4.925 14.06 -4.86 14.37 ; + RECT -4.55 13.635 -4.485 13.925 ; + RECT -4.55 12.825 -4.485 13.115 ; + RECT -4.6975 14.4475 -4.625 14.51 ; + RECT -4.74 14.06 -4.67 14.445 ; + RECT -4.74 12.825 -4.67 13.115 ; + RECT -4.74 13.345 -4.67 13.5025 ; + RECT -4.925 13.635 -4.86 13.925 ; + RECT -4.925 12.825 -4.86 13.115 ; + RECT -5.055 13.345 -4.67 13.415 ; + RECT -5.06 14.445 -4.285 14.51 ; + RECT -5.06 12.455 -4.285 12.52 ; + RECT -4.42 9.56 -4.355 11.485 ; + RECT -4.925 11.1 -4.86 11.41 ; + RECT -4.55 10.675 -4.485 10.965 ; + RECT -4.625 11.84 -4.56 12.29 ; + RECT -4.74 11.1 -4.67 11.485 ; + RECT -4.74 10.385 -4.67 10.5425 ; + RECT -4.55 11.3975 -4.485 11.485 ; + RECT -4.925 10.675 -4.86 10.965 ; + RECT -4.99 11.615 -4.745 11.68 ; + RECT -4.99 11.615 -4.9075 11.7375 ; + RECT -4.995 11.7375 -4.9275 11.8725 ; + RECT -5.06 11.485 -4.285 11.55 ; + RECT -4.55 9.865 -4.485 10.155 ; + RECT -4.735 9.3275 -4.67 9.495 ; + RECT -4.9925 8.835 -4.735 8.905 ; + RECT -4.55 8.77 -4.485 9.195 ; + RECT -4.74 9.865 -4.67 10.155 ; + RECT -4.55 9.025 -4.3775 9.1 ; + RECT -4.925 9.865 -4.86 10.155 ; + RECT -4.9275 8.705 -4.8625 8.7775 ; + RECT -5.055 10.385 -4.67 10.455 ; + RECT -4.9675 9.415 -4.9025 9.53 ; + RECT -5.06 8.64 -4.285 8.705 ; + RECT -5.06 9.495 -4.285 9.56 ; + RECT -4.49 8.485 -4.425 8.5425 ; + RECT -5.0575 8.725 -4.9925 8.8475 ; + RECT -4.875 12.2875 -4.81 12.455 ; + RECT -4.8125 11.68 -4.745 11.7825 ; + RECT -3.78 10.965 -3.715 11.1 ; + RECT -3.965 10.965 -3.9 11.1 ; + RECT -3.78 9.73 -3.715 9.865 ; + RECT -3.965 9.73 -3.9 9.865 ; + RECT -3.97 10.965 -3.905 11.1 ; + RECT -4.155 10.965 -4.09 11.1 ; + RECT -3.97 13.925 -3.905 14.06 ; + RECT -4.155 13.925 -4.09 14.06 ; + RECT -3.97 9.73 -3.905 9.865 ; + RECT -4.155 9.73 -4.09 9.865 ; + RECT -3.78 13.925 -3.715 14.06 ; + RECT -3.965 13.925 -3.9 14.06 ; + RECT -3.97 9.195 -3.905 9.33 ; + RECT -4.155 9.195 -4.09 9.33 ; + RECT -3.83 12.155 -3.765 12.29 ; + RECT -4.015 12.155 -3.95 12.29 ; + RECT -3.78 12.69 -3.715 12.825 ; + RECT -3.965 12.69 -3.9 12.825 ; + RECT -3.97 12.69 -3.905 12.825 ; + RECT -4.155 12.69 -4.09 12.825 ; + RECT -3.78 13.115 -3.715 13.25 ; + RECT -3.965 13.115 -3.9 13.25 ; + RECT -3.78 10.155 -3.715 10.29 ; + RECT -3.965 10.155 -3.9 10.29 ; + RECT -3.78 13.5 -3.715 13.635 ; + RECT -3.965 13.5 -3.9 13.635 ; + RECT -3.97 13.5 -3.905 13.635 ; + RECT -4.155 13.5 -4.09 13.635 ; + RECT -3.97 10.155 -3.905 10.29 ; + RECT -4.155 10.155 -4.09 10.29 ; + RECT -3.97 13.115 -3.905 13.25 ; + RECT -4.155 13.115 -4.09 13.25 ; + RECT -3.97 10.54 -3.905 10.675 ; + RECT -4.155 10.54 -4.09 10.675 ; + RECT -3.78 10.54 -3.715 10.675 ; + RECT -3.965 10.54 -3.9 10.675 ; + RECT -3.97 8.77 -3.905 8.905 ; + RECT -4.155 8.77 -4.09 8.905 ; + RECT -3.895 11.745 -3.83 11.88 ; + RECT -4.08 11.745 -4.015 11.88 ; + RECT -4.0925 8.48 -3.9575 8.545 ; + RECT -4.155 13.9775 -4.09 14.1125 ; + RECT -4.265 12.1625 -4.2 12.2975 ; + RECT -4.155 11.2625 -4.09 11.3975 ; + RECT -3.7325 11.7375 -3.6675 11.8725 ; + RECT -4.155 14.3425 -4.09 14.4775 ; + RECT -3.6475 8.8 -3.5825 8.935 ; + RECT -3.79 12.8825 -3.725 13.0175 ; + RECT -3.7875 11.275 -3.7225 11.41 ; + RECT -3.7875 9.8775 -3.7225 10.0125 ; + RECT -4.3525 8.66 -4.2875 8.795 ; + RECT -3.7775 8.7775 -3.7125 8.9125 ; + RECT -3.7775 10.3875 -3.6425 10.4525 ; + RECT -3.7375 9.3025 -3.6725 9.4375 ; + RECT -3.7775 13.3475 -3.6425 13.4125 ; + RECT -3.83 14.235 -3.765 14.37 ; + RECT -3.91 10.765 -3.845 10.9 ; + RECT -3.92 13.725 -3.855 13.86 ; + RECT -4.1525 12.035 -4.0175 12.1 ; + RECT -3.6475 11.7375 -3.5825 11.8725 ; + RECT -4.2625 9.03 -4.1275 9.095 ; + RECT -4.1525 10.5425 -4.0875 10.6775 ; + RECT -3.7875 14.235 -3.7225 14.37 ; + RECT -3.965 13.725 -3.9 13.86 ; + RECT -3.6475 13.3125 -3.5825 13.4475 ; + RECT -3.6475 8.61 -3.5825 8.745 ; + RECT -3.7875 11.275 -3.7225 11.41 ; + RECT -3.6475 10.3525 -3.5825 10.4875 ; + RECT -4.1525 10.1525 -4.0875 10.2875 ; + RECT -3.965 10.1525 -3.9 10.2875 ; + RECT -3.965 10.765 -3.9 10.9 ; + RECT -4.155 13.1125 -4.09 13.2475 ; + RECT -3.965 13.1125 -3.9 13.2475 ; + RECT -4.355 8.48 -3.58 8.545 ; + RECT -4.285 11.55 -4.22 14.445 ; + RECT -3.78 14.06 -3.715 14.37 ; + RECT -4.155 13.635 -4.09 13.925 ; + RECT -4.155 12.825 -4.09 13.115 ; + RECT -4.015 14.4475 -3.9425 14.51 ; + RECT -3.97 14.06 -3.9 14.445 ; + RECT -3.97 12.825 -3.9 13.115 ; + RECT -3.97 13.345 -3.9 13.5025 ; + RECT -3.78 13.635 -3.715 13.925 ; + RECT -3.78 12.825 -3.715 13.115 ; + RECT -3.97 13.345 -3.585 13.415 ; + RECT -4.355 14.445 -3.58 14.51 ; + RECT -4.355 12.455 -3.58 12.52 ; + RECT -4.285 9.56 -4.22 11.485 ; + RECT -3.78 11.1 -3.715 11.41 ; + RECT -4.155 10.675 -4.09 10.965 ; + RECT -4.08 11.84 -4.015 12.29 ; + RECT -3.97 11.1 -3.9 11.485 ; + RECT -3.97 10.385 -3.9 10.5425 ; + RECT -4.155 11.3975 -4.09 11.485 ; + RECT -3.78 10.675 -3.715 10.965 ; + RECT -3.895 11.615 -3.65 11.68 ; + RECT -3.7325 11.615 -3.65 11.7375 ; + RECT -3.7125 11.7375 -3.645 11.8725 ; + RECT -4.355 11.485 -3.58 11.55 ; + RECT -4.155 9.865 -4.09 10.155 ; + RECT -3.97 9.3275 -3.905 9.495 ; + RECT -3.905 8.835 -3.6475 8.905 ; + RECT -4.155 8.77 -4.09 9.195 ; + RECT -3.97 9.865 -3.9 10.155 ; + RECT -4.2625 9.025 -4.09 9.1 ; + RECT -3.78 9.865 -3.715 10.155 ; + RECT -3.7775 8.705 -3.7125 8.7775 ; + RECT -3.97 10.385 -3.585 10.455 ; + RECT -3.7375 9.415 -3.6725 9.53 ; + RECT -4.355 8.64 -3.58 8.705 ; + RECT -4.355 9.495 -3.58 9.56 ; + RECT -4.215 8.485 -4.15 8.5425 ; + RECT -3.6475 8.725 -3.5825 8.8475 ; + RECT -3.83 12.2875 -3.765 12.455 ; + RECT -3.895 11.68 -3.8275 11.7825 ; + RECT -0.3825 13.585 -0.3175 14.715 ; + RECT -1.7275 13.585 -1.6625 14.715 ; + RECT -1.6625 14.5825 -1.2975 14.6475 ; + RECT -0.5175 14.5825 -0.3825 14.6475 ; + RECT -1.4325 13.8425 -0.485 13.9075 ; + RECT -1.4325 14.2225 -0.485 14.2875 ; + RECT -0.965 13.585 -0.9 13.62 ; + RECT -0.965 14.2875 -0.9 14.715 ; + RECT -0.7225 13.6525 -0.5875 13.7175 ; + RECT -0.7225 13.8425 -0.5875 13.9075 ; + RECT -0.7225 14.0325 -0.5875 14.0975 ; + RECT -0.7225 14.2225 -0.5875 14.2875 ; + RECT -0.7225 14.4125 -0.5875 14.4775 ; + RECT -0.855 13.6525 -0.79 13.7175 ; + RECT -0.855 14.0325 -0.79 14.0975 ; + RECT -0.855 14.0325 -0.79 14.0975 ; + RECT -0.855 14.4125 -0.79 14.4775 ; + RECT -0.7875 13.6525 -0.5875 13.7175 ; + RECT -0.8225 13.6525 -0.7875 13.7175 ; + RECT -0.855 13.685 -0.79 14.065 ; + RECT -0.8225 14.0325 -0.5875 14.0975 ; + RECT -0.7875 14.0325 -0.5875 14.0975 ; + RECT -0.8225 14.0325 -0.7875 14.0975 ; + RECT -0.855 14.065 -0.79 14.445 ; + RECT -0.8225 14.4125 -0.5875 14.4775 ; + RECT -0.5225 13.8425 -0.4575 13.9075 ; + RECT -0.5225 14.2225 -0.4575 14.2875 ; + RECT -0.5875 13.8425 -0.5225 13.9075 ; + RECT -0.5225 13.8425 -0.49 13.9075 ; + RECT -0.5225 13.875 -0.4575 14.255 ; + RECT -0.5875 14.2225 -0.49 14.2875 ; + RECT -1.5725 13.6525 -1.2975 13.7175 ; + RECT -1.5725 13.8425 -1.2975 13.9075 ; + RECT -1.5725 14.0325 -1.2975 14.0975 ; + RECT -1.5725 14.2225 -1.2975 14.2875 ; + RECT -1.5725 14.4125 -1.2975 14.4775 ; + RECT -1.705 13.6525 -1.64 13.7175 ; + RECT -1.705 14.0325 -1.64 14.0975 ; + RECT -1.705 14.0325 -1.64 14.0975 ; + RECT -1.705 14.4125 -1.64 14.4775 ; + RECT -1.6375 13.6525 -1.3675 13.7175 ; + RECT -1.6725 13.6525 -1.6375 13.7175 ; + RECT -1.705 13.685 -1.64 14.065 ; + RECT -1.6725 14.0325 -1.3675 14.0975 ; + RECT -1.6375 14.0325 -1.3675 14.0975 ; + RECT -1.6725 14.0325 -1.6375 14.0975 ; + RECT -1.705 14.065 -1.64 14.445 ; + RECT -1.6725 14.4125 -1.3675 14.4775 ; + RECT -1.2325 13.8425 -1.1675 13.9075 ; + RECT -1.2325 14.2225 -1.1675 14.2875 ; + RECT -1.3675 13.8425 -1.2325 13.9075 ; + RECT -1.2325 13.8425 -1.2 13.9075 ; + RECT -1.2325 13.875 -1.1675 14.255 ; + RECT -1.3675 14.2225 -1.2 14.2875 ; + RECT -1.5725 14.5825 -1.2975 14.6475 ; + RECT -0.5875 14.5825 -0.4525 14.6475 ; + RECT -0.965 13.62 -0.9 13.755 ; + RECT -0.3825 8.415 -0.3175 9.745 ; + RECT -1.7275 8.415 -1.6625 9.745 ; + RECT -0.8675 9.575 -0.8025 9.745 ; + RECT -0.9975 8.995 -0.9325 9.745 ; + RECT -1.695 8.4825 -1.4125 8.5475 ; + RECT -0.4525 9.0625 -0.35 9.1275 ; + RECT -1.6625 9.6125 -1.4125 9.6775 ; + RECT -1.6625 9.2325 -1.4125 9.2975 ; + RECT -0.4525 9.6125 -0.3175 9.6775 ; + RECT -0.4525 9.2325 -0.3175 9.2975 ; + RECT -0.6525 9.6125 -0.5175 9.6775 ; + RECT -0.6525 9.4225 -0.5175 9.4875 ; + RECT -0.6525 9.4225 -0.5175 9.4875 ; + RECT -0.6525 9.2325 -0.5175 9.2975 ; + RECT -1.2575 9.6125 -1.1225 9.6775 ; + RECT -1.2575 9.4225 -1.1225 9.4875 ; + RECT -1.2575 9.2325 -1.1225 9.2975 ; + RECT -1.2575 9.0325 -1.1225 9.0975 ; + RECT -1.2575 8.8425 -1.1225 8.9075 ; + RECT -1.2575 8.6525 -1.1225 8.7175 ; + RECT -1.4125 8.4825 -1.2775 8.5475 ; + RECT -0.4525 9.0625 -0.3175 9.1275 ; + RECT -0.7925 9.575 -0.7275 9.71 ; + RECT -0.9225 8.995 -0.8575 9.13 ; + RECT -0.8675 9.4225 -0.8025 9.4875 ; + RECT -0.8675 8.415 -0.8025 9.455 ; + RECT -1.2175 9.4225 -0.835 9.4875 ; + RECT -1.4125 8.8425 -1.2775 8.9075 ; + RECT -0.8675 8.84 -0.8025 8.91 ; + RECT -0.8675 8.415 -0.8025 8.875 ; + RECT -0.8675 8.8075 -0.8025 8.9425 ; + RECT -1.7625 9.4225 -1.6975 9.4875 ; + RECT -1.73 9.4225 -1.4125 9.4875 ; + RECT -1.7625 9.165 -1.6975 9.455 ; + RECT -1.7625 9.0325 -1.6975 9.0975 ; + RECT -1.73 9.0325 -1.4125 9.0975 ; + RECT -1.7625 9.065 -1.6975 9.165 ; + RECT -1.7625 8.6525 -1.6975 8.7175 ; + RECT -1.73 8.6525 -1.4125 8.7175 ; + RECT -1.7625 8.685 -1.6975 9.165 ; + RECT -0.3825 16.185 -0.3175 16.935 ; + RECT -1.7275 16.185 -1.6625 16.935 ; + RECT -1.695 16.8025 -1.4125 16.8675 ; + RECT -0.53 16.8025 -0.35 16.8675 ; + RECT -1.695 16.2525 -1.4125 16.3175 ; + RECT -1.695 16.6325 -1.4125 16.6975 ; + RECT -0.6325 16.2525 -0.35 16.3175 ; + RECT -1.325 16.185 -1.26 16.22 ; + RECT -0.795 16.185 -0.73 16.46 ; + RECT -1.0125 16.6975 -0.9475 16.935 ; + RECT -0.7675 16.2525 -0.6325 16.3175 ; + RECT -0.7675 16.4425 -0.6325 16.5075 ; + RECT -0.7675 16.4425 -0.6325 16.5075 ; + RECT -0.7675 16.6325 -0.6325 16.6975 ; + RECT -1.5475 16.2525 -1.4125 16.3175 ; + RECT -1.5475 16.4425 -1.4125 16.5075 ; + RECT -1.5475 16.4425 -1.4125 16.5075 ; + RECT -1.5475 16.6325 -1.4125 16.6975 ; + RECT -1.5475 16.8025 -1.4125 16.8675 ; + RECT -0.6325 16.8025 -0.4975 16.8675 ; + RECT -1.31 16.665 -1.245 16.73 ; + RECT -1.31 16.4425 -1.245 16.5075 ; + RECT -1.2775 16.665 -0.4975 16.73 ; + RECT -1.31 16.475 -1.245 16.6975 ; + RECT -1.4125 16.4425 -1.2775 16.5075 ; + RECT -1.325 16.22 -1.26 16.355 ; + RECT -0.795 16.46 -0.73 16.595 ; + RECT -1.9775 21.245 -1.9075 21.38 ; + RECT -2.135 21.1325 -2.07 21.1975 ; + RECT -4.1925 22.5675 -3.96 22.6325 ; + RECT -4.9025 26.2325 -0.3825 26.2975 ; + RECT -0.415 24.07 -0.35 25.19 ; + RECT -3.105 24.07 -3.04 25.19 ; + RECT -1.9075 20.6625 -1.3475 20.7275 ; + RECT -1.9075 19.3175 -1.3475 19.3825 ; + RECT -1.84 19.3825 -1.775 19.7475 ; + RECT -1.84 20.5275 -1.775 20.6625 ; + RECT -1.48 20.5925 -1.415 20.6625 ; + RECT -1.48 19.3825 -1.415 19.4725 ; + RECT -1.67 19.6125 -1.605 20.56 ; + RECT -1.3825 20.08 -1.3475 20.145 ; + RECT -1.9075 20.08 -1.67 20.145 ; + RECT -1.48 20.3225 -1.415 20.4575 ; + RECT -1.67 20.3225 -1.605 20.4575 ; + RECT -1.48 19.4725 -1.415 19.7475 ; + RECT -1.67 19.4725 -1.605 19.7475 ; + RECT -1.84 19.4725 -1.775 19.7475 ; + RECT -1.84 20.4575 -1.775 20.5925 ; + RECT -1.5175 20.08 -1.3825 20.145 ; + RECT -0.94 21.235 -0.805 21.3 ; + RECT -0.94 21.045 -0.805 21.11 ; + RECT -1.0 23.795 -0.935 23.93 ; + RECT -0.415 23.37 -0.35 23.93 ; + RECT -1.76 23.37 -1.695 23.93 ; + RECT -1.695 23.4375 -1.33 23.5025 ; + RECT -0.55 23.4375 -0.415 23.5025 ; + RECT -0.485 23.7975 -0.415 23.8625 ; + RECT -1.695 23.7975 -1.605 23.8625 ; + RECT -1.465 23.6075 -0.5175 23.6725 ; + RECT -0.9975 23.895 -0.9325 23.93 ; + RECT -0.685 23.7975 -0.55 23.8625 ; + RECT -0.685 23.6075 -0.55 23.6725 ; + RECT -1.225 23.7975 -0.95 23.8625 ; + RECT -1.225 23.6075 -0.95 23.6725 ; + RECT -1.33 23.4375 -1.055 23.5025 ; + RECT -0.485 23.4375 -0.35 23.5025 ; + RECT -0.9225 23.76 -0.8575 23.895 ; + RECT -1.0 23.235 -0.935 23.37 ; + RECT -0.415 22.81 -0.35 23.37 ; + RECT -1.76 22.81 -1.695 23.37 ; + RECT -1.695 22.8775 -1.33 22.9425 ; + RECT -0.55 22.8775 -0.415 22.9425 ; + RECT -0.485 23.2375 -0.415 23.3025 ; + RECT -1.695 23.2375 -1.605 23.3025 ; + RECT -1.465 23.0475 -0.5175 23.1125 ; + RECT -0.9975 23.335 -0.9325 23.37 ; + RECT -0.685 23.2375 -0.55 23.3025 ; + RECT -0.685 23.0475 -0.55 23.1125 ; + RECT -1.225 23.2375 -0.95 23.3025 ; + RECT -1.225 23.0475 -0.95 23.1125 ; + RECT -1.33 22.8775 -1.055 22.9425 ; + RECT -0.485 22.8775 -0.35 22.9425 ; + RECT -0.9225 23.2 -0.8575 23.335 ; + RECT -2.52 22.81 -2.455 22.945 ; + RECT -3.105 22.81 -3.04 23.37 ; + RECT -1.76 22.81 -1.695 23.37 ; + RECT -2.125 23.2375 -1.76 23.3025 ; + RECT -3.04 23.2375 -2.905 23.3025 ; + RECT -3.04 22.8775 -2.97 22.9425 ; + RECT -1.85 22.8775 -1.76 22.9425 ; + RECT -2.9375 23.0675 -1.99 23.1325 ; + RECT -2.5225 22.81 -2.4575 22.845 ; + RECT -2.905 22.8775 -2.77 22.9425 ; + RECT -2.905 23.0675 -2.77 23.1325 ; + RECT -2.505 22.8775 -2.23 22.9425 ; + RECT -2.505 23.0675 -2.23 23.1325 ; + RECT -2.4 23.2375 -2.125 23.3025 ; + RECT -3.105 23.2375 -2.97 23.3025 ; + RECT -2.5975 22.845 -2.5325 22.98 ; + RECT -2.52 23.37 -2.455 23.505 ; + RECT -3.105 23.37 -3.04 23.93 ; + RECT -1.76 23.37 -1.695 23.93 ; + RECT -2.125 23.7975 -1.76 23.8625 ; + RECT -3.04 23.7975 -2.905 23.8625 ; + RECT -3.04 23.4375 -2.97 23.5025 ; + RECT -1.85 23.4375 -1.76 23.5025 ; + RECT -2.9375 23.6275 -1.99 23.6925 ; + RECT -2.5225 23.37 -2.4575 23.405 ; + RECT -2.905 23.4375 -2.77 23.5025 ; + RECT -2.905 23.6275 -2.77 23.6925 ; + RECT -2.505 23.4375 -2.23 23.5025 ; + RECT -2.505 23.6275 -2.23 23.6925 ; + RECT -2.4 23.7975 -2.125 23.8625 ; + RECT -3.105 23.7975 -2.97 23.8625 ; + RECT -2.5975 23.405 -2.5325 23.54 ; + RECT -1.0 23.4725 -0.935 23.6075 ; + RECT -1.0 22.9125 -0.935 23.0475 ; + RECT -2.52 23.1325 -2.455 23.2675 ; + RECT -4.8475 22.9975 -4.7825 23.1325 ; + RECT -4.6625 22.9975 -4.5975 23.1325 ; + RECT -4.4925 23.0025 -4.4275 23.1375 ; + RECT -4.3075 23.0025 -4.2425 23.1375 ; + RECT -4.9275 23.3875 -4.8625 23.5225 ; + RECT -4.7425 23.3875 -4.6775 23.5225 ; + RECT -4.4125 23.3875 -4.3475 23.5225 ; + RECT -4.2275 23.3875 -4.1625 23.5225 ; + RECT -4.9275 23.8525 -4.8625 23.9875 ; + RECT -4.7425 23.8525 -4.6775 23.9875 ; + RECT -4.4125 23.8525 -4.3475 23.9875 ; + RECT -4.2275 23.8525 -4.1625 23.9875 ; + RECT -4.225 23.0025 -4.16 23.1375 ; + RECT -4.93 23.4775 -4.865 23.6125 ; + RECT -4.225 23.4775 -4.16 23.6125 ; + RECT -4.6775 22.9975 -4.6125 23.1325 ; + RECT -4.4775 23.0025 -4.4125 23.1375 ; + RECT -4.725 23.5925 -4.59 23.6575 ; + RECT -4.5 23.7425 -4.365 23.8075 ; + RECT -4.6025 22.8475 -4.4675 22.9125 ; + RECT -4.61 24.0525 -4.475 24.1175 ; + RECT -4.6075 22.7075 -4.4725 22.7725 ; + RECT -4.965 22.7075 -4.83 22.7725 ; + RECT -4.245 23.0025 -4.1925 23.1375 ; + RECT -4.26 22.7075 -4.125 22.7725 ; + RECT -4.575 24.0525 -4.51 24.1175 ; + RECT -4.2275 22.8475 -4.1575 22.9125 ; + RECT -4.7825 23.2225 -4.6775 23.2875 ; + RECT -4.7425 23.2875 -4.6775 23.3875 ; + RECT -4.8475 23.1325 -4.7825 23.2875 ; + RECT -4.4125 23.2225 -4.3075 23.2875 ; + RECT -4.4125 23.2875 -4.3475 23.3875 ; + RECT -4.3075 23.1375 -4.2425 23.2875 ; + RECT -4.9275 23.9875 -4.8625 24.0525 ; + RECT -4.2275 23.9875 -4.1625 24.0525 ; + RECT -4.7425 23.3875 -4.6775 23.8775 ; + RECT -4.4125 23.3875 -4.3475 23.8775 ; + RECT -4.9875 22.8475 -4.1025 22.9125 ; + RECT -4.9875 24.0525 -4.1025 24.1175 ; + RECT -4.9875 22.7075 -4.1025 22.7725 ; + RECT -4.9275 21.4925 -4.8625 21.6275 ; + RECT -4.7425 21.4925 -4.6775 21.6275 ; + RECT -4.2275 21.4925 -4.1625 21.6275 ; + RECT -4.4125 21.4925 -4.3475 21.6275 ; + RECT -4.7425 21.9575 -4.6775 22.0925 ; + RECT -4.9275 21.9575 -4.8625 22.0925 ; + RECT -4.4125 21.9575 -4.3475 22.0925 ; + RECT -4.2275 21.9575 -4.1625 22.0925 ; + RECT -4.8475 22.3475 -4.7825 22.4825 ; + RECT -4.6625 22.3475 -4.5975 22.4825 ; + RECT -4.4925 22.3475 -4.4275 22.4825 ; + RECT -4.3075 22.3475 -4.2425 22.4825 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.6075 21.3625 -4.4725 21.4275 ; + RECT -4.26 22.7075 -4.125 22.7725 ; + RECT -4.965 22.7075 -4.83 22.7725 ; + RECT -4.595 22.5675 -4.46 22.6325 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.5 21.6775 -4.365 21.7425 ; + RECT -4.5 21.6775 -4.365 21.7425 ; + RECT -4.725 21.8275 -4.59 21.8925 ; + RECT -4.725 21.8275 -4.59 21.8925 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.48 22.3475 -4.415 22.4825 ; + RECT -4.225 21.855 -4.16 21.99 ; + RECT -4.225 21.855 -4.16 21.99 ; + RECT -4.225 21.855 -4.16 21.99 ; + RECT -4.225 21.855 -4.16 21.99 ; + RECT -4.225 21.855 -4.16 21.99 ; + RECT -4.225 21.855 -4.16 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.675 22.3475 -4.61 22.4825 ; + RECT -4.6125 22.7075 -4.4775 22.7725 ; + RECT -4.6125 22.7075 -4.4775 22.7725 ; + RECT -4.965 22.7075 -4.83 22.7725 ; + RECT -4.6075 21.3625 -4.4725 21.4275 ; + RECT -4.965 22.7075 -4.83 22.7725 ; + RECT -4.965 22.7075 -4.83 22.7725 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.93 21.855 -4.865 21.99 ; + RECT -4.26 22.7075 -4.125 22.7725 ; + RECT -4.585 21.365 -4.485 21.4275 ; + RECT -4.585 21.3625 -4.485 21.425 ; + RECT -4.865 22.5675 -4.8125 22.63 ; + RECT -4.585 21.365 -4.485 21.4275 ; + RECT -4.9325 21.4275 -4.8625 21.6275 ; + RECT -4.9325 21.9575 -4.8625 22.0925 ; + RECT -4.9325 21.9575 -4.8625 22.0925 ; + RECT -4.9875 22.7075 -4.1025 22.7725 ; + RECT -4.8525 22.1925 -4.6775 22.2575 ; + RECT -4.2275 21.9575 -4.1575 22.0925 ; + RECT -4.4125 21.5175 -4.3475 22.2575 ; + RECT -4.585 21.3625 -4.485 21.425 ; + RECT -4.16 22.5675 -4.1075 22.63 ; + RECT -4.9325 21.9575 -4.8625 22.0925 ; + RECT -4.9875 21.3625 -4.1025 21.4275 ; + RECT -4.7425 21.6275 -4.6775 22.2575 ; + RECT -4.9325 21.9575 -4.8625 22.0925 ; + RECT -4.9325 21.9575 -4.8625 22.0925 ; + RECT -4.3125 22.1925 -4.2425 22.4825 ; + RECT -4.2275 21.9575 -4.1575 22.0925 ; + RECT -4.9875 22.5675 -4.1025 22.6325 ; + RECT -4.9325 21.9575 -4.8625 22.0925 ; + RECT -4.9325 21.4275 -4.8625 21.6275 ; + RECT -4.2275 21.4275 -4.1575 21.6275 ; + RECT -4.8525 22.1925 -4.7825 22.4825 ; + RECT -4.4125 22.1925 -4.2425 22.2575 ; + RECT -4.9275 21.1625 -4.8625 21.2975 ; + RECT -4.7425 21.1625 -4.6775 21.2975 ; + RECT -4.2275 21.1625 -4.1625 21.2975 ; + RECT -4.4125 21.1625 -4.3475 21.2975 ; + RECT -4.7425 20.6975 -4.6775 20.8325 ; + RECT -4.9275 20.6975 -4.8625 20.8325 ; + RECT -4.4125 20.6975 -4.3475 20.8325 ; + RECT -4.2275 20.6975 -4.1625 20.8325 ; + RECT -4.8475 20.3075 -4.7825 20.4425 ; + RECT -4.6625 20.3075 -4.5975 20.4425 ; + RECT -4.4925 20.3075 -4.4275 20.4425 ; + RECT -4.3075 20.3075 -4.2425 20.4425 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.6075 21.3625 -4.4725 21.4275 ; + RECT -4.26 20.0175 -4.125 20.0825 ; + RECT -4.965 20.0175 -4.83 20.0825 ; + RECT -4.595 20.1575 -4.46 20.2225 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.5 21.0475 -4.365 21.1125 ; + RECT -4.5 21.0475 -4.365 21.1125 ; + RECT -4.725 20.8975 -4.59 20.9625 ; + RECT -4.725 20.8975 -4.59 20.9625 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.48 20.3075 -4.415 20.4425 ; + RECT -4.225 20.8 -4.16 20.935 ; + RECT -4.225 20.8 -4.16 20.935 ; + RECT -4.225 20.8 -4.16 20.935 ; + RECT -4.225 20.8 -4.16 20.935 ; + RECT -4.225 20.8 -4.16 20.935 ; + RECT -4.225 20.8 -4.16 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.675 20.3075 -4.61 20.4425 ; + RECT -4.6125 20.0175 -4.4775 20.0825 ; + RECT -4.6125 20.0175 -4.4775 20.0825 ; + RECT -4.965 20.0175 -4.83 20.0825 ; + RECT -4.6075 21.3625 -4.4725 21.4275 ; + RECT -4.965 20.0175 -4.83 20.0825 ; + RECT -4.965 20.0175 -4.83 20.0825 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.93 20.8 -4.865 20.935 ; + RECT -4.26 20.0175 -4.125 20.0825 ; + RECT -4.585 21.3625 -4.485 21.425 ; + RECT -4.585 21.365 -4.485 21.4275 ; + RECT -4.865 20.16 -4.8125 20.2225 ; + RECT -4.585 21.3625 -4.485 21.425 ; + RECT -4.9325 21.1625 -4.8625 21.3625 ; + RECT -4.9325 20.6975 -4.8625 20.8325 ; + RECT -4.9325 20.6975 -4.8625 20.8325 ; + RECT -4.9875 20.0175 -4.1025 20.0825 ; + RECT -4.8525 20.5325 -4.6775 20.5975 ; + RECT -4.2275 20.6975 -4.1575 20.8325 ; + RECT -4.4125 20.5325 -4.3475 21.2725 ; + RECT -4.585 21.365 -4.485 21.4275 ; + RECT -4.16 20.16 -4.1075 20.2225 ; + RECT -4.9325 20.6975 -4.8625 20.8325 ; + RECT -4.9875 21.3625 -4.1025 21.4275 ; + RECT -4.7425 20.5325 -4.6775 21.1625 ; + RECT -4.9325 20.6975 -4.8625 20.8325 ; + RECT -4.9325 20.6975 -4.8625 20.8325 ; + RECT -4.3125 20.3075 -4.2425 20.5975 ; + RECT -4.2275 20.6975 -4.1575 20.8325 ; + RECT -4.9875 20.1575 -4.1025 20.2225 ; + RECT -4.9325 20.6975 -4.8625 20.8325 ; + RECT -4.9325 21.1625 -4.8625 21.3625 ; + RECT -4.2275 21.1625 -4.1575 21.3625 ; + RECT -4.8525 20.3075 -4.7825 20.5975 ; + RECT -4.4125 20.5325 -4.2425 20.5975 ; + RECT -1.975 21.1425 -1.91 21.2775 ; + RECT -0.9975 23.7575 -0.9325 23.8225 ; + RECT -0.965 23.7575 -0.3825 23.8225 ; + RECT -0.9975 23.65 -0.9325 23.79 ; + RECT -1.38 21.3825 -1.315 21.4525 ; + RECT -2.135 21.3825 -2.07 21.4525 ; + RECT -1.38 21.2775 -1.315 21.4175 ; + RECT -2.135 21.39 -2.07 21.4175 ; + RECT -1.415 21.385 -1.28 21.45 ; + RECT -2.17 21.385 -2.035 21.45 ; + RECT -4.4125 24.28 -4.3425 24.345 ; + RECT -4.3775 24.2825 -2.1025 24.3475 ; + RECT -4.445 24.28 -4.31 24.345 ; + RECT -0.6525 21.36 -0.5875 21.495 ; + RECT -0.65 19.52 -0.58 19.585 ; + RECT -2.5575 19.5225 -2.4875 19.5875 ; + RECT -2.5225 19.5225 -0.615 19.5875 ; + RECT -0.6825 19.52 -0.5475 19.585 ; + RECT -2.59 19.5225 -2.455 19.5875 ; + RECT -2.59 24.1675 -2.525 24.3025 ; + RECT -0.6475 21.33 -0.5825 21.465 ; + RECT -4.025 22.3975 -3.96 22.5325 ; + RECT -0.94 21.2425 -0.875 21.3075 ; + RECT -0.94 21.275 -0.875 21.6575 ; + RECT -1.3475 21.2425 -0.9075 21.3075 ; + RECT -3.7375 22.0075 -3.6675 22.0725 ; + RECT -3.7025 22.0075 -1.3475 22.0725 ; + RECT -3.77 22.0075 -3.635 22.0725 ; + RECT -0.94 21.625 -0.875 21.69 ; + RECT -0.94 21.4675 -0.875 21.6575 ; + RECT -1.3475 21.625 -0.9075 21.69 ; + RECT -1.76 24.0275 -1.695 24.0925 ; + RECT -1.76 23.93 -1.695 24.06 ; + RECT -3.7025 24.0275 -1.7275 24.0925 ; + RECT -3.7375 23.7675 -3.6675 23.8325 ; + RECT -3.7025 23.7675 -1.7275 23.8325 ; + RECT -3.77 23.7675 -3.635 23.8325 ; + RECT -0.4175 20.6625 -0.3475 20.7275 ; + RECT -0.4175 16.165 -0.3475 16.23 ; + RECT -3.1075 16.1675 -3.0375 16.2325 ; + RECT -1.3475 20.6625 -0.3825 20.7275 ; + RECT -3.0725 16.1675 -0.3825 16.2325 ; + RECT -0.45 20.6625 -0.315 20.7275 ; + RECT -0.45 16.165 -0.315 16.23 ; + RECT -3.14 16.1675 -3.005 16.2325 ; + RECT -3.77 26.2325 -3.705 26.3675 ; + RECT -3.5275 23.93 -3.4575 23.995 ; + RECT -3.5275 25.01 -3.4575 25.075 ; + RECT -3.4925 23.93 -3.04 23.995 ; + RECT -4.1925 25.0125 -3.4925 25.0775 ; + RECT -3.56 23.93 -3.425 23.995 ; + RECT -3.56 25.01 -3.425 25.075 ; + RECT -3.7375 28.345 -3.6675 28.41 ; + RECT -4.1925 28.3475 -3.7025 28.4125 ; + RECT -3.77 28.345 -3.635 28.41 ; + RECT -3.5275 23.93 -3.4575 23.995 ; + RECT -3.5275 22.6 -3.4575 22.665 ; + RECT -3.4925 23.93 -3.04 23.995 ; + RECT -4.1925 22.6025 -3.4925 22.6675 ; + RECT -3.56 23.93 -3.425 23.995 ; + RECT -3.56 22.6 -3.425 22.665 ; + RECT -3.7375 31.035 -3.6675 31.1 ; + RECT -4.545 31.0375 -3.7025 31.1025 ; + RECT -3.77 31.035 -3.635 31.1 ; + RECT -4.9325 20.6625 -4.8625 20.7275 ; + RECT -4.8975 20.6625 -0.7875 20.7275 ; + RECT -4.965 20.6625 -4.83 20.7275 ; + RECT -4.2275 20.6625 -4.1575 20.7275 ; + RECT -4.1925 20.6625 -0.7875 20.7275 ; + RECT -4.26 20.6625 -4.125 20.7275 ; + RECT -0.3825 17.335 -0.3175 17.895 ; + RECT -1.7275 17.335 -1.6625 17.895 ; + RECT -1.6625 17.4025 -1.2975 17.4675 ; + RECT -0.5175 17.4025 -0.3825 17.4675 ; + RECT -0.4525 17.7625 -0.3825 17.8275 ; + RECT -1.6625 17.7625 -1.5725 17.8275 ; + RECT -1.4325 17.5725 -0.485 17.6375 ; + RECT -0.965 17.86 -0.9 17.895 ; + RECT -0.965 17.335 -0.9 17.5725 ; + RECT -0.6525 17.7625 -0.5175 17.8275 ; + RECT -0.6525 17.5725 -0.5175 17.6375 ; + RECT -1.1925 17.7625 -0.9175 17.8275 ; + RECT -1.1925 17.5725 -0.9175 17.6375 ; + RECT -1.2975 17.4025 -1.0225 17.4675 ; + RECT -0.4525 17.4025 -0.3175 17.4675 ; + RECT -0.89 17.725 -0.825 17.86 ; + RECT -0.3825 17.895 -0.3175 18.455 ; + RECT -1.7275 17.895 -1.6625 18.455 ; + RECT -1.6625 17.9625 -1.2975 18.0275 ; + RECT -0.5175 17.9625 -0.3825 18.0275 ; + RECT -0.4525 18.3225 -0.3825 18.3875 ; + RECT -1.6625 18.3225 -1.5725 18.3875 ; + RECT -1.4325 18.1325 -0.485 18.1975 ; + RECT -0.965 18.42 -0.9 18.455 ; + RECT -0.965 17.895 -0.9 18.1325 ; + RECT -0.6525 18.3225 -0.5175 18.3875 ; + RECT -0.6525 18.1325 -0.5175 18.1975 ; + RECT -1.1925 18.3225 -0.9175 18.3875 ; + RECT -1.1925 18.1325 -0.9175 18.1975 ; + RECT -1.2975 17.9625 -1.0225 18.0275 ; + RECT -0.4525 17.9625 -0.3175 18.0275 ; + RECT -0.89 18.285 -0.825 18.42 ; + RECT -3.0725 16.185 -3.0075 17.335 ; + RECT -1.7275 16.185 -1.6625 17.335 ; + RECT -2.91 17.02 -1.825 17.085 ; + RECT -1.9775 17.1575 -1.695 17.2225 ; + RECT -3.04 17.1575 -2.77 17.2225 ; + RECT -1.9775 16.4175 -1.695 16.4825 ; + RECT -1.9775 16.7975 -1.695 16.8625 ; + RECT -3.04 16.4175 -2.7575 16.4825 ; + RECT -2.12 16.185 -2.055 16.495 ; + RECT -2.6425 16.185 -2.5775 16.35 ; + RECT -2.7825 16.185 -2.7175 16.35 ; + RECT -2.3875 17.02 -2.3225 17.335 ; + RECT -2.8125 16.4175 -2.5375 16.4825 ; + RECT -2.8125 16.6075 -2.5375 16.6725 ; + RECT -2.8125 16.6075 -2.5375 16.6725 ; + RECT -2.8125 16.7975 -2.5375 16.8625 ; + RECT -2.8125 16.7975 -2.5375 16.8625 ; + RECT -2.8125 16.9875 -2.5375 17.0525 ; + RECT -2.2675 16.4175 -2.1325 16.4825 ; + RECT -2.2675 16.6075 -2.1325 16.6725 ; + RECT -2.2675 16.6075 -2.1325 16.6725 ; + RECT -2.2675 16.7975 -2.1325 16.8625 ; + RECT -2.2675 16.7975 -2.1325 16.8625 ; + RECT -2.2675 16.9875 -2.1325 17.0525 ; + RECT -2.1125 17.1575 -1.9775 17.2225 ; + RECT -3.1925 17.1575 -2.9175 17.2225 ; + RECT -2.1125 16.6075 -1.9775 16.6725 ; + RECT -2.1125 16.9875 -1.9775 17.0525 ; + RECT -2.2 16.36 -2.135 16.495 ; + RECT -2.7125 16.665 -2.5775 16.73 ; + RECT -2.7125 16.6625 -2.5775 16.7275 ; + RECT -2.715 16.215 -2.65 16.35 ; + RECT -2.7125 16.855 -2.5775 16.92 ; + RECT -2.7125 16.8525 -2.5775 16.9175 ; + RECT -2.855 16.215 -2.79 16.35 ; + RECT -3.0725 16.185 -3.0075 17.335 ; + RECT -4.4175 16.185 -4.3525 17.335 ; + RECT -4.255 17.02 -3.17 17.085 ; + RECT -4.385 17.1575 -4.1025 17.2225 ; + RECT -3.31 17.1575 -3.04 17.2225 ; + RECT -4.385 16.4175 -4.1025 16.4825 ; + RECT -4.385 16.7975 -4.1025 16.8625 ; + RECT -3.3225 16.4175 -3.04 16.4825 ; + RECT -4.025 16.185 -3.96 16.495 ; + RECT -3.5025 16.185 -3.4375 16.35 ; + RECT -3.3625 16.185 -3.2975 16.35 ; + RECT -3.7575 17.02 -3.6925 17.335 ; + RECT -3.7125 16.4175 -3.4375 16.4825 ; + RECT -3.7125 16.6075 -3.4375 16.6725 ; + RECT -3.7125 16.6075 -3.4375 16.6725 ; + RECT -3.7125 16.7975 -3.4375 16.8625 ; + RECT -3.7125 16.7975 -3.4375 16.8625 ; + RECT -3.7125 16.9875 -3.4375 17.0525 ; + RECT -4.2375 16.4175 -4.1025 16.4825 ; + RECT -4.2375 16.6075 -4.1025 16.6725 ; + RECT -4.2375 16.6075 -4.1025 16.6725 ; + RECT -4.2375 16.7975 -4.1025 16.8625 ; + RECT -4.2375 16.7975 -4.1025 16.8625 ; + RECT -4.2375 16.9875 -4.1025 17.0525 ; + RECT -4.2375 17.1575 -4.1025 17.2225 ; + RECT -3.4375 17.1575 -3.1625 17.2225 ; + RECT -4.2375 16.6075 -4.1025 16.6725 ; + RECT -4.2375 16.9875 -4.1025 17.0525 ; + RECT -4.02 16.36 -3.955 16.495 ; + RECT -3.6375 16.665 -3.5025 16.73 ; + RECT -3.6375 16.6625 -3.5025 16.7275 ; + RECT -3.5 16.215 -3.435 16.35 ; + RECT -3.6375 16.855 -3.5025 16.92 ; + RECT -3.6375 16.8525 -3.5025 16.9175 ; + RECT -3.36 16.215 -3.295 16.35 ; + RECT -3.0725 17.335 -3.0075 17.895 ; + RECT -1.7275 17.335 -1.6625 17.895 ; + RECT -2.0925 17.7625 -1.7275 17.8275 ; + RECT -3.0075 17.7625 -2.8725 17.8275 ; + RECT -3.0075 17.4025 -2.9375 17.4675 ; + RECT -1.8175 17.4025 -1.7275 17.4675 ; + RECT -2.905 17.5925 -1.9575 17.6575 ; + RECT -2.49 17.335 -2.425 17.37 ; + RECT -2.49 17.6575 -2.425 17.895 ; + RECT -2.8725 17.4025 -2.7375 17.4675 ; + RECT -2.8725 17.5925 -2.7375 17.6575 ; + RECT -2.4725 17.4025 -2.1975 17.4675 ; + RECT -2.4725 17.5925 -2.1975 17.6575 ; + RECT -2.3675 17.7625 -2.0925 17.8275 ; + RECT -3.0725 17.7625 -2.9375 17.8275 ; + RECT -2.565 17.37 -2.5 17.505 ; + RECT -3.0725 17.335 -3.0075 17.895 ; + RECT -4.4175 17.335 -4.3525 17.895 ; + RECT -4.3525 17.7625 -3.9875 17.8275 ; + RECT -3.2075 17.7625 -3.0725 17.8275 ; + RECT -3.1425 17.4025 -3.0725 17.4675 ; + RECT -4.3525 17.4025 -4.2625 17.4675 ; + RECT -4.1225 17.5925 -3.175 17.6575 ; + RECT -3.655 17.335 -3.59 17.37 ; + RECT -3.655 17.6575 -3.59 17.895 ; + RECT -3.4125 17.4025 -3.2775 17.4675 ; + RECT -3.4125 17.5925 -3.2775 17.6575 ; + RECT -4.2625 17.4025 -3.9875 17.4675 ; + RECT -4.2625 17.5925 -3.9875 17.6575 ; + RECT -4.2625 17.7625 -3.9875 17.8275 ; + RECT -3.2775 17.7625 -3.1425 17.8275 ; + RECT -3.655 17.37 -3.59 17.505 ; + RECT -3.0725 17.895 -3.0075 18.455 ; + RECT -4.4175 17.895 -4.3525 18.455 ; + RECT -4.3525 18.3225 -3.9875 18.3875 ; + RECT -3.2075 18.3225 -3.0725 18.3875 ; + RECT -3.1425 17.9625 -3.0725 18.0275 ; + RECT -4.3525 17.9625 -4.2625 18.0275 ; + RECT -4.1225 18.1525 -3.175 18.2175 ; + RECT -3.655 17.895 -3.59 17.93 ; + RECT -3.655 18.2175 -3.59 18.455 ; + RECT -3.4125 17.9625 -3.2775 18.0275 ; + RECT -3.4125 18.1525 -3.2775 18.2175 ; + RECT -4.2625 17.9625 -3.9875 18.0275 ; + RECT -4.2625 18.1525 -3.9875 18.2175 ; + RECT -4.2625 18.3225 -3.9875 18.3875 ; + RECT -3.2775 18.3225 -3.1425 18.3875 ; + RECT -3.655 17.93 -3.59 18.065 ; + RECT -3.0725 18.455 -3.0075 19.015 ; + RECT -4.4175 18.455 -4.3525 19.015 ; + RECT -4.3525 18.8825 -3.9875 18.9475 ; + RECT -3.2075 18.8825 -3.0725 18.9475 ; + RECT -3.1425 18.5225 -3.0725 18.5875 ; + RECT -4.3525 18.5225 -4.2625 18.5875 ; + RECT -4.1225 18.7125 -3.175 18.7775 ; + RECT -3.655 18.455 -3.59 18.49 ; + RECT -3.655 18.7775 -3.59 19.015 ; + RECT -3.4125 18.5225 -3.2775 18.5875 ; + RECT -3.4125 18.7125 -3.2775 18.7775 ; + RECT -4.2625 18.5225 -3.9875 18.5875 ; + RECT -4.2625 18.7125 -3.9875 18.7775 ; + RECT -4.2625 18.8825 -3.9875 18.9475 ; + RECT -3.2775 18.8825 -3.1425 18.9475 ; + RECT -3.655 18.49 -3.59 18.625 ; + RECT -3.9975 14.615 -3.9325 14.75 ; + RECT -3.9975 15.105 -3.9325 15.24 ; + RECT -4.1525 14.615 -4.0875 14.75 ; + RECT -4.1525 15.315 -4.0875 15.45 ; + RECT -5.5625 14.615 -5.4975 14.75 ; + RECT -5.5625 15.525 -5.4975 15.66 ; + RECT -4.5475 14.615 -4.4825 14.75 ; + RECT -4.5475 15.735 -4.4825 15.87 ; + RECT -1.325 15.315 -1.26 15.45 ; + RECT -0.795 15.945 -0.73 16.08 ; + RECT -2.12 15.945 -2.055 16.08 ; + RECT -2.6425 15.315 -2.5775 15.45 ; + RECT -2.7825 15.525 -2.7175 15.66 ; + RECT -4.025 15.945 -3.96 16.08 ; + RECT -3.5025 15.735 -3.4375 15.87 ; + RECT -3.3625 15.525 -3.2975 15.66 ; + RECT -0.9975 9.85 -0.9325 9.92 ; + RECT -0.2225 9.85 -0.1575 9.92 ; + RECT -0.22 15.135 -0.155 15.205 ; + RECT -0.9975 9.745 -0.9325 9.885 ; + RECT -0.22 9.885 -0.155 15.17 ; + RECT -0.9975 9.8175 -0.9325 9.9525 ; + RECT -0.2225 9.8175 -0.1575 9.9525 ; + RECT -0.22 15.1025 -0.155 15.2375 ; + RECT -0.965 14.7525 -0.9 14.8175 ; + RECT -0.795 14.7525 -0.73 14.8175 ; + RECT -0.965 14.715 -0.9 14.785 ; + RECT -0.9325 14.7525 -0.7625 14.8175 ; + RECT -0.795 14.785 -0.73 16.185 ; + RECT -2.49 19.405 -2.425 19.54 ; + RECT -3.655 19.195 -3.59 19.33 ; + RECT -0.965 19.615 -0.9 19.75 ; + RECT -0.2075 19.435 -0.1425 19.505 ; + RECT -0.205 19.47 -0.14 23.79 ; + RECT -0.2075 19.4025 -0.1425 19.5375 ; + RECT -1.7275 19.825 -1.6625 19.96 ; + RECT -4.4175 19.825 -4.3525 19.96 ; + RECT -0.3825 14.895 -0.3175 15.03 ; + RECT -0.965 13.41 -0.9 13.48 ; + RECT -2.0875 13.41 -2.0225 13.48 ; + RECT -0.965 13.445 -0.9 13.585 ; + RECT -2.085 8.275 -2.02 13.445 ; + RECT -0.965 13.3775 -0.9 13.5125 ; + RECT -2.0875 13.3775 -2.0225 13.5125 ; + RECT -0.965 13.2025 -0.9 13.2675 ; + RECT -0.8675 13.2025 -0.8025 13.2675 ; + RECT -0.965 13.235 -0.9 13.445 ; + RECT -0.9325 13.2025 -0.835 13.2675 ; + RECT -0.8675 9.745 -0.8025 13.235 ; + RECT -2.0825 8.275 -2.0175 8.41 ; + RECT -0.865 8.415 -0.8 8.55 ; + RECT -1.0825 16.8675 -0.9475 16.9325 ; + RECT -1.035 17.3375 -0.9 17.4025 ; + RECT 0.0 19.855 0.205 19.99 ; + Layer via1 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.0975 18.8625 11.1625 18.9275 ; + RECT 11.8025 18.8625 11.8675 18.9275 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.3525 19.1875 11.4175 19.2525 ; + RECT 11.0975 19.68 11.1625 19.745 ; + RECT 11.0975 19.68 11.1625 19.745 ; + RECT 11.0975 19.68 11.1625 19.745 ; + RECT 11.0975 19.68 11.1625 19.745 ; + RECT 11.0975 19.68 11.1625 19.745 ; + RECT 11.0975 19.68 11.1625 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.5475 19.1875 11.6125 19.2525 ; + RECT 11.8025 18.8625 11.8675 18.9275 ; + RECT 11.8025 18.8625 11.8675 18.9275 ; + RECT 11.8025 18.8625 11.8675 18.9275 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.0975 18.8625 11.1625 18.9275 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.0975 21.5525 11.1625 21.6175 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.3525 21.2275 11.4175 21.2925 ; + RECT 11.0975 20.735 11.1625 20.8 ; + RECT 11.0975 20.735 11.1625 20.8 ; + RECT 11.0975 20.735 11.1625 20.8 ; + RECT 11.0975 20.735 11.1625 20.8 ; + RECT 11.0975 20.735 11.1625 20.8 ; + RECT 11.0975 20.735 11.1625 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.5475 21.2275 11.6125 21.2925 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.0975 21.5525 11.1625 21.6175 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.0975 21.5525 11.1625 21.6175 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.3525 21.8775 11.4175 21.9425 ; + RECT 11.0975 22.37 11.1625 22.435 ; + RECT 11.0975 22.37 11.1625 22.435 ; + RECT 11.0975 22.37 11.1625 22.435 ; + RECT 11.0975 22.37 11.1625 22.435 ; + RECT 11.0975 22.37 11.1625 22.435 ; + RECT 11.0975 22.37 11.1625 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.5475 21.8775 11.6125 21.9425 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.0975 21.5525 11.1625 21.6175 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.0975 24.2425 11.1625 24.3075 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.3525 23.9175 11.4175 23.9825 ; + RECT 11.0975 23.425 11.1625 23.49 ; + RECT 11.0975 23.425 11.1625 23.49 ; + RECT 11.0975 23.425 11.1625 23.49 ; + RECT 11.0975 23.425 11.1625 23.49 ; + RECT 11.0975 23.425 11.1625 23.49 ; + RECT 11.0975 23.425 11.1625 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.5475 23.9175 11.6125 23.9825 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.0975 24.2425 11.1625 24.3075 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.0975 24.2425 11.1625 24.3075 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.3525 24.5675 11.4175 24.6325 ; + RECT 11.0975 25.06 11.1625 25.125 ; + RECT 11.0975 25.06 11.1625 25.125 ; + RECT 11.0975 25.06 11.1625 25.125 ; + RECT 11.0975 25.06 11.1625 25.125 ; + RECT 11.0975 25.06 11.1625 25.125 ; + RECT 11.0975 25.06 11.1625 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.5475 24.5675 11.6125 24.6325 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.0975 24.2425 11.1625 24.3075 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.0975 26.9325 11.1625 26.9975 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.3525 26.6075 11.4175 26.6725 ; + RECT 11.0975 26.115 11.1625 26.18 ; + RECT 11.0975 26.115 11.1625 26.18 ; + RECT 11.0975 26.115 11.1625 26.18 ; + RECT 11.0975 26.115 11.1625 26.18 ; + RECT 11.0975 26.115 11.1625 26.18 ; + RECT 11.0975 26.115 11.1625 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.5475 26.6075 11.6125 26.6725 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.0975 26.9325 11.1625 26.9975 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.0975 26.9325 11.1625 26.9975 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.3525 27.2575 11.4175 27.3225 ; + RECT 11.0975 27.75 11.1625 27.815 ; + RECT 11.0975 27.75 11.1625 27.815 ; + RECT 11.0975 27.75 11.1625 27.815 ; + RECT 11.0975 27.75 11.1625 27.815 ; + RECT 11.0975 27.75 11.1625 27.815 ; + RECT 11.0975 27.75 11.1625 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.5475 27.2575 11.6125 27.3225 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.0975 26.9325 11.1625 26.9975 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.0975 29.6225 11.1625 29.6875 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.3525 29.2975 11.4175 29.3625 ; + RECT 11.0975 28.805 11.1625 28.87 ; + RECT 11.0975 28.805 11.1625 28.87 ; + RECT 11.0975 28.805 11.1625 28.87 ; + RECT 11.0975 28.805 11.1625 28.87 ; + RECT 11.0975 28.805 11.1625 28.87 ; + RECT 11.0975 28.805 11.1625 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.5475 29.2975 11.6125 29.3625 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.0975 29.6225 11.1625 29.6875 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.0975 29.6225 11.1625 29.6875 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.3525 29.9475 11.4175 30.0125 ; + RECT 11.0975 30.44 11.1625 30.505 ; + RECT 11.0975 30.44 11.1625 30.505 ; + RECT 11.0975 30.44 11.1625 30.505 ; + RECT 11.0975 30.44 11.1625 30.505 ; + RECT 11.0975 30.44 11.1625 30.505 ; + RECT 11.0975 30.44 11.1625 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.5475 29.9475 11.6125 30.0125 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.0975 29.6225 11.1625 29.6875 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.0975 32.3125 11.1625 32.3775 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.3525 31.9875 11.4175 32.0525 ; + RECT 11.0975 31.495 11.1625 31.56 ; + RECT 11.0975 31.495 11.1625 31.56 ; + RECT 11.0975 31.495 11.1625 31.56 ; + RECT 11.0975 31.495 11.1625 31.56 ; + RECT 11.0975 31.495 11.1625 31.56 ; + RECT 11.0975 31.495 11.1625 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.5475 31.9875 11.6125 32.0525 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.0975 32.3125 11.1625 32.3775 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.0975 32.3125 11.1625 32.3775 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.3525 32.6375 11.4175 32.7025 ; + RECT 11.0975 33.13 11.1625 33.195 ; + RECT 11.0975 33.13 11.1625 33.195 ; + RECT 11.0975 33.13 11.1625 33.195 ; + RECT 11.0975 33.13 11.1625 33.195 ; + RECT 11.0975 33.13 11.1625 33.195 ; + RECT 11.0975 33.13 11.1625 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.5475 32.6375 11.6125 32.7025 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.0975 32.3125 11.1625 32.3775 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.0975 35.0025 11.1625 35.0675 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.3525 34.6775 11.4175 34.7425 ; + RECT 11.0975 34.185 11.1625 34.25 ; + RECT 11.0975 34.185 11.1625 34.25 ; + RECT 11.0975 34.185 11.1625 34.25 ; + RECT 11.0975 34.185 11.1625 34.25 ; + RECT 11.0975 34.185 11.1625 34.25 ; + RECT 11.0975 34.185 11.1625 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.5475 34.6775 11.6125 34.7425 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.0975 35.0025 11.1625 35.0675 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.0975 35.0025 11.1625 35.0675 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.3525 35.3275 11.4175 35.3925 ; + RECT 11.0975 35.82 11.1625 35.885 ; + RECT 11.0975 35.82 11.1625 35.885 ; + RECT 11.0975 35.82 11.1625 35.885 ; + RECT 11.0975 35.82 11.1625 35.885 ; + RECT 11.0975 35.82 11.1625 35.885 ; + RECT 11.0975 35.82 11.1625 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.5475 35.3275 11.6125 35.3925 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.0975 35.0025 11.1625 35.0675 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.0975 37.6925 11.1625 37.7575 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.3525 37.3675 11.4175 37.4325 ; + RECT 11.0975 36.875 11.1625 36.94 ; + RECT 11.0975 36.875 11.1625 36.94 ; + RECT 11.0975 36.875 11.1625 36.94 ; + RECT 11.0975 36.875 11.1625 36.94 ; + RECT 11.0975 36.875 11.1625 36.94 ; + RECT 11.0975 36.875 11.1625 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.5475 37.3675 11.6125 37.4325 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.0975 37.6925 11.1625 37.7575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.0975 37.6925 11.1625 37.7575 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.3525 38.0175 11.4175 38.0825 ; + RECT 11.0975 38.51 11.1625 38.575 ; + RECT 11.0975 38.51 11.1625 38.575 ; + RECT 11.0975 38.51 11.1625 38.575 ; + RECT 11.0975 38.51 11.1625 38.575 ; + RECT 11.0975 38.51 11.1625 38.575 ; + RECT 11.0975 38.51 11.1625 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.5475 38.0175 11.6125 38.0825 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.0975 37.6925 11.1625 37.7575 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.0975 40.3825 11.1625 40.4475 ; + RECT 11.8025 40.3825 11.8675 40.4475 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.3525 40.0575 11.4175 40.1225 ; + RECT 11.0975 39.565 11.1625 39.63 ; + RECT 11.0975 39.565 11.1625 39.63 ; + RECT 11.0975 39.565 11.1625 39.63 ; + RECT 11.0975 39.565 11.1625 39.63 ; + RECT 11.0975 39.565 11.1625 39.63 ; + RECT 11.0975 39.565 11.1625 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.5475 40.0575 11.6125 40.1225 ; + RECT 11.8025 40.3825 11.8675 40.4475 ; + RECT 11.8025 40.3825 11.8675 40.4475 ; + RECT 11.8025 40.3825 11.8675 40.4475 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.0975 40.3825 11.1625 40.4475 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 11.8025 18.8625 11.8675 18.9275 ; + RECT 12.5075 18.8625 12.5725 18.9275 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.0575 19.1875 12.1225 19.2525 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 11.8025 19.68 11.8675 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.2525 19.1875 12.3175 19.2525 ; + RECT 12.5075 18.8625 12.5725 18.9275 ; + RECT 12.5075 18.8625 12.5725 18.9275 ; + RECT 12.5075 18.8625 12.5725 18.9275 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 12.5075 19.68 12.5725 19.745 ; + RECT 11.8025 18.8625 11.8675 18.9275 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 12.5075 21.5525 12.5725 21.6175 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.0575 21.2275 12.1225 21.2925 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 11.8025 20.735 11.8675 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.2525 21.2275 12.3175 21.2925 ; + RECT 12.5075 21.5525 12.5725 21.6175 ; + RECT 12.5075 21.5525 12.5725 21.6175 ; + RECT 12.5075 21.5525 12.5725 21.6175 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 12.5075 20.735 12.5725 20.8 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 12.5075 21.5525 12.5725 21.6175 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.0575 21.8775 12.1225 21.9425 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 11.8025 22.37 11.8675 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.2525 21.8775 12.3175 21.9425 ; + RECT 12.5075 21.5525 12.5725 21.6175 ; + RECT 12.5075 21.5525 12.5725 21.6175 ; + RECT 12.5075 21.5525 12.5725 21.6175 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 12.5075 22.37 12.5725 22.435 ; + RECT 11.8025 21.5525 11.8675 21.6175 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 12.5075 24.2425 12.5725 24.3075 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.0575 23.9175 12.1225 23.9825 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 11.8025 23.425 11.8675 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.2525 23.9175 12.3175 23.9825 ; + RECT 12.5075 24.2425 12.5725 24.3075 ; + RECT 12.5075 24.2425 12.5725 24.3075 ; + RECT 12.5075 24.2425 12.5725 24.3075 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 12.5075 23.425 12.5725 23.49 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 12.5075 24.2425 12.5725 24.3075 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.0575 24.5675 12.1225 24.6325 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 11.8025 25.06 11.8675 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.2525 24.5675 12.3175 24.6325 ; + RECT 12.5075 24.2425 12.5725 24.3075 ; + RECT 12.5075 24.2425 12.5725 24.3075 ; + RECT 12.5075 24.2425 12.5725 24.3075 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 12.5075 25.06 12.5725 25.125 ; + RECT 11.8025 24.2425 11.8675 24.3075 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 12.5075 26.9325 12.5725 26.9975 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.0575 26.6075 12.1225 26.6725 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 11.8025 26.115 11.8675 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.2525 26.6075 12.3175 26.6725 ; + RECT 12.5075 26.9325 12.5725 26.9975 ; + RECT 12.5075 26.9325 12.5725 26.9975 ; + RECT 12.5075 26.9325 12.5725 26.9975 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 12.5075 26.115 12.5725 26.18 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 12.5075 26.9325 12.5725 26.9975 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.0575 27.2575 12.1225 27.3225 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 11.8025 27.75 11.8675 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.2525 27.2575 12.3175 27.3225 ; + RECT 12.5075 26.9325 12.5725 26.9975 ; + RECT 12.5075 26.9325 12.5725 26.9975 ; + RECT 12.5075 26.9325 12.5725 26.9975 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 12.5075 27.75 12.5725 27.815 ; + RECT 11.8025 26.9325 11.8675 26.9975 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 12.5075 29.6225 12.5725 29.6875 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.0575 29.2975 12.1225 29.3625 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 11.8025 28.805 11.8675 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.2525 29.2975 12.3175 29.3625 ; + RECT 12.5075 29.6225 12.5725 29.6875 ; + RECT 12.5075 29.6225 12.5725 29.6875 ; + RECT 12.5075 29.6225 12.5725 29.6875 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 12.5075 28.805 12.5725 28.87 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 12.5075 29.6225 12.5725 29.6875 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.0575 29.9475 12.1225 30.0125 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 11.8025 30.44 11.8675 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.2525 29.9475 12.3175 30.0125 ; + RECT 12.5075 29.6225 12.5725 29.6875 ; + RECT 12.5075 29.6225 12.5725 29.6875 ; + RECT 12.5075 29.6225 12.5725 29.6875 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 12.5075 30.44 12.5725 30.505 ; + RECT 11.8025 29.6225 11.8675 29.6875 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 12.5075 32.3125 12.5725 32.3775 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.0575 31.9875 12.1225 32.0525 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 11.8025 31.495 11.8675 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.2525 31.9875 12.3175 32.0525 ; + RECT 12.5075 32.3125 12.5725 32.3775 ; + RECT 12.5075 32.3125 12.5725 32.3775 ; + RECT 12.5075 32.3125 12.5725 32.3775 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 12.5075 31.495 12.5725 31.56 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 12.5075 32.3125 12.5725 32.3775 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.0575 32.6375 12.1225 32.7025 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 11.8025 33.13 11.8675 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.2525 32.6375 12.3175 32.7025 ; + RECT 12.5075 32.3125 12.5725 32.3775 ; + RECT 12.5075 32.3125 12.5725 32.3775 ; + RECT 12.5075 32.3125 12.5725 32.3775 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 12.5075 33.13 12.5725 33.195 ; + RECT 11.8025 32.3125 11.8675 32.3775 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 12.5075 35.0025 12.5725 35.0675 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.0575 34.6775 12.1225 34.7425 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 11.8025 34.185 11.8675 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.2525 34.6775 12.3175 34.7425 ; + RECT 12.5075 35.0025 12.5725 35.0675 ; + RECT 12.5075 35.0025 12.5725 35.0675 ; + RECT 12.5075 35.0025 12.5725 35.0675 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 12.5075 34.185 12.5725 34.25 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 12.5075 35.0025 12.5725 35.0675 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.0575 35.3275 12.1225 35.3925 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 11.8025 35.82 11.8675 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.2525 35.3275 12.3175 35.3925 ; + RECT 12.5075 35.0025 12.5725 35.0675 ; + RECT 12.5075 35.0025 12.5725 35.0675 ; + RECT 12.5075 35.0025 12.5725 35.0675 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 12.5075 35.82 12.5725 35.885 ; + RECT 11.8025 35.0025 11.8675 35.0675 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 12.5075 37.6925 12.5725 37.7575 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.0575 37.3675 12.1225 37.4325 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 11.8025 36.875 11.8675 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.2525 37.3675 12.3175 37.4325 ; + RECT 12.5075 37.6925 12.5725 37.7575 ; + RECT 12.5075 37.6925 12.5725 37.7575 ; + RECT 12.5075 37.6925 12.5725 37.7575 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 12.5075 36.875 12.5725 36.94 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 12.5075 37.6925 12.5725 37.7575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.0575 38.0175 12.1225 38.0825 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 11.8025 38.51 11.8675 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.2525 38.0175 12.3175 38.0825 ; + RECT 12.5075 37.6925 12.5725 37.7575 ; + RECT 12.5075 37.6925 12.5725 37.7575 ; + RECT 12.5075 37.6925 12.5725 37.7575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 12.5075 38.51 12.5725 38.575 ; + RECT 11.8025 37.6925 11.8675 37.7575 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 11.8025 40.3825 11.8675 40.4475 ; + RECT 12.5075 40.3825 12.5725 40.4475 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.0575 40.0575 12.1225 40.1225 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 11.8025 39.565 11.8675 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.2525 40.0575 12.3175 40.1225 ; + RECT 12.5075 40.3825 12.5725 40.4475 ; + RECT 12.5075 40.3825 12.5725 40.4475 ; + RECT 12.5075 40.3825 12.5725 40.4475 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 12.5075 39.565 12.5725 39.63 ; + RECT 11.8025 40.3825 11.8675 40.4475 ; + RECT 11.325 41.3475 11.39 41.4125 ; + RECT 11.705 41.3475 11.77 41.4125 ; + RECT 11.325 40.8975 11.39 40.9625 ; + RECT 11.515 40.8975 11.58 40.9625 ; + RECT 12.03 41.3475 12.095 41.4125 ; + RECT 12.41 41.3475 12.475 41.4125 ; + RECT 12.03 40.8975 12.095 40.9625 ; + RECT 12.22 40.8975 12.285 40.9625 ; + RECT 11.445 18.5325 11.51 18.5975 ; + RECT 11.8025 14.2925 11.8675 14.3575 ; + RECT 11.0975 14.2925 11.1625 14.3575 ; + RECT 11.64 17.075 11.705 17.14 ; + RECT 11.2575 17.485 11.3225 17.55 ; + RECT 11.45 18.3275 11.515 18.3925 ; + RECT 11.45 16.185 11.515 16.25 ; + RECT 12.15 18.5325 12.215 18.5975 ; + RECT 12.5075 14.2925 12.5725 14.3575 ; + RECT 11.8025 14.2925 11.8675 14.3575 ; + RECT 12.345 17.075 12.41 17.14 ; + RECT 11.9625 17.485 12.0275 17.55 ; + RECT 12.155 18.3275 12.22 18.3925 ; + RECT 12.155 16.185 12.22 16.25 ; + RECT 11.3725 9.97 11.4375 10.035 ; + RECT 11.8025 11.605 11.8675 11.67 ; + RECT 11.8025 11.035 11.8675 11.1 ; + RECT 11.0975 11.035 11.1625 11.1 ; + RECT 11.5175 11.24 11.5825 11.305 ; + RECT 11.8025 13.5325 11.8675 13.5975 ; + RECT 11.5175 10.83 11.5825 10.895 ; + RECT 11.3775 10.83 11.4425 10.895 ; + RECT 11.8025 11.24 11.8675 11.305 ; + RECT 11.0975 11.24 11.1625 11.305 ; + RECT 11.27 10.2325 11.335 10.2975 ; + RECT 11.3775 13.21 11.4425 13.275 ; + RECT 11.5475 11.605 11.6125 11.67 ; + RECT 11.2375 12.52 11.3025 12.585 ; + RECT 11.3775 11.24 11.4425 11.305 ; + RECT 11.0975 11.605 11.1625 11.67 ; + RECT 11.5475 12.12 11.6125 12.185 ; + RECT 11.2825 13.7425 11.3475 13.8075 ; + RECT 11.0975 13.5325 11.1625 13.5975 ; + RECT 12.0775 9.97 12.1425 10.035 ; + RECT 12.5075 11.605 12.5725 11.67 ; + RECT 12.5075 11.035 12.5725 11.1 ; + RECT 11.8025 11.035 11.8675 11.1 ; + RECT 12.2225 11.24 12.2875 11.305 ; + RECT 12.5075 13.5325 12.5725 13.5975 ; + RECT 12.2225 10.83 12.2875 10.895 ; + RECT 12.0825 10.83 12.1475 10.895 ; + RECT 12.5075 11.24 12.5725 11.305 ; + RECT 11.8025 11.24 11.8675 11.305 ; + RECT 11.975 10.2325 12.04 10.2975 ; + RECT 12.0825 13.21 12.1475 13.275 ; + RECT 12.2525 11.605 12.3175 11.67 ; + RECT 11.9425 12.52 12.0075 12.585 ; + RECT 12.0825 11.24 12.1475 11.305 ; + RECT 11.8025 11.605 11.8675 11.67 ; + RECT 12.2525 12.12 12.3175 12.185 ; + RECT 11.9875 13.7425 12.0525 13.8075 ; + RECT 11.8025 13.5325 11.8675 13.5975 ; + RECT 11.295 9.1325 11.36 9.1975 ; + RECT 11.8025 3.955 11.8675 4.02 ; + RECT 11.66 8.0375 11.725 8.1025 ; + RECT 11.6625 6.43 11.7275 6.495 ; + RECT 11.6625 5.0325 11.7275 5.0975 ; + RECT 11.0975 3.815 11.1625 3.88 ; + RECT 11.8025 6.8925 11.8675 6.9575 ; + RECT 11.2975 5.6975 11.3625 5.7625 ; + RECT 11.6625 9.39 11.7275 9.455 ; + RECT 11.485 8.88 11.55 8.945 ; + RECT 11.8025 8.4675 11.8675 8.5325 ; + RECT 11.8025 3.765 11.8675 3.83 ; + RECT 11.8025 5.5075 11.8675 5.5725 ; + RECT 11.2975 5.3075 11.3625 5.3725 ; + RECT 11.485 5.3075 11.55 5.3725 ; + RECT 11.485 5.92 11.55 5.985 ; + RECT 11.295 8.2675 11.36 8.3325 ; + RECT 11.485 8.2675 11.55 8.3325 ; + RECT 12.31 9.1325 12.375 9.1975 ; + RECT 11.8025 3.955 11.8675 4.02 ; + RECT 11.945 8.0375 12.01 8.1025 ; + RECT 11.9425 6.43 12.0075 6.495 ; + RECT 11.9425 5.0325 12.0075 5.0975 ; + RECT 12.5075 3.815 12.5725 3.88 ; + RECT 11.8025 6.8925 11.8675 6.9575 ; + RECT 12.3075 5.6975 12.3725 5.7625 ; + RECT 11.9425 9.39 12.0075 9.455 ; + RECT 12.12 8.88 12.185 8.945 ; + RECT 11.8025 8.4675 11.8675 8.5325 ; + RECT 11.8025 3.765 11.8675 3.83 ; + RECT 11.8025 5.5075 11.8675 5.5725 ; + RECT 12.3075 5.3075 12.3725 5.3725 ; + RECT 12.12 5.3075 12.185 5.3725 ; + RECT 12.12 5.92 12.185 5.985 ; + RECT 12.31 8.2675 12.375 8.3325 ; + RECT 12.12 8.2675 12.185 8.3325 ; + RECT 11.45 5.18 11.515 5.245 ; + RECT 11.8025 3.9275 11.8675 3.9925 ; + RECT 11.5825 5.82 11.6475 5.885 ; + RECT 11.8025 5.9875 11.8675 6.0525 ; + RECT 11.275 5.7975 11.34 5.8625 ; + RECT 11.4825 5.52 11.5475 5.585 ; + RECT 11.5825 4.3125 11.6475 4.3775 ; + RECT 11.2375 4.08 11.3025 4.145 ; + RECT 12.155 5.18 12.22 5.245 ; + RECT 11.8025 3.9275 11.8675 3.9925 ; + RECT 12.0225 5.82 12.0875 5.885 ; + RECT 11.8025 5.9875 11.8675 6.0525 ; + RECT 12.33 5.7975 12.395 5.8625 ; + RECT 12.1225 5.52 12.1875 5.585 ; + RECT 12.0225 4.3125 12.0875 4.3775 ; + RECT 12.3675 4.08 12.4325 4.145 ; + RECT 6.16 9.1825 6.225 9.2475 ; + RECT 7.525 8.66 7.59 8.725 ; + RECT 5.985 9.5725 6.05 9.6375 ; + RECT 7.35 10.095 7.415 10.16 ; + RECT 6.16 8.975 6.225 9.04 ; + RECT 5.985 8.445 6.05 8.51 ; + RECT 5.81 9.78 5.875 9.845 ; + RECT 5.985 10.31 6.05 10.375 ; + RECT 6.16 11.665 6.225 11.73 ; + RECT 5.635 11.135 5.7 11.2 ; + RECT 5.81 12.47 5.875 12.535 ; + RECT 7.525 12.47 7.59 12.535 ; + RECT 5.635 13.0 5.7 13.065 ; + RECT 7.35 13.0 7.415 13.065 ; + RECT 6.335 8.0325 6.4 8.0975 ; + RECT 6.51 9.3775 6.575 9.4425 ; + RECT 6.335 10.7225 6.4 10.7875 ; + RECT 6.51 12.0675 6.575 12.1325 ; + RECT 6.335 13.235 6.4 13.3 ; + RECT 6.16 14.5625 6.225 14.6275 ; + RECT 7.525 14.04 7.59 14.105 ; + RECT 5.985 14.9525 6.05 15.0175 ; + RECT 7.35 15.475 7.415 15.54 ; + RECT 6.16 14.355 6.225 14.42 ; + RECT 5.985 13.825 6.05 13.89 ; + RECT 5.81 15.16 5.875 15.225 ; + RECT 5.985 15.69 6.05 15.755 ; + RECT 6.16 17.045 6.225 17.11 ; + RECT 5.635 16.515 5.7 16.58 ; + RECT 5.81 17.85 5.875 17.915 ; + RECT 7.525 17.85 7.59 17.915 ; + RECT 5.635 18.38 5.7 18.445 ; + RECT 7.35 18.38 7.415 18.445 ; + RECT 6.335 13.4125 6.4 13.4775 ; + RECT 6.51 14.7575 6.575 14.8225 ; + RECT 6.335 16.1025 6.4 16.1675 ; + RECT 6.51 17.4475 6.575 17.5125 ; + RECT 6.335 18.615 6.4 18.68 ; + RECT 2.825 8.73 2.89 8.795 ; + RECT 3.0 10.165 3.065 10.23 ; + RECT 3.175 11.42 3.24 11.485 ; + RECT 3.35 12.855 3.415 12.92 ; + RECT 3.525 14.11 3.59 14.175 ; + RECT 3.7 15.545 3.765 15.61 ; + RECT 3.875 16.8 3.94 16.865 ; + RECT 4.05 18.235 4.115 18.3 ; + RECT 2.825 19.805 2.89 19.87 ; + RECT 3.525 19.275 3.59 19.34 ; + RECT 2.825 20.61 2.89 20.675 ; + RECT 3.7 21.14 3.765 21.205 ; + RECT 2.825 22.495 2.89 22.56 ; + RECT 3.875 21.965 3.94 22.03 ; + RECT 2.825 23.3 2.89 23.365 ; + RECT 4.05 23.83 4.115 23.895 ; + RECT 3.0 25.185 3.065 25.25 ; + RECT 3.525 24.655 3.59 24.72 ; + RECT 3.0 25.99 3.065 26.055 ; + RECT 3.7 26.52 3.765 26.585 ; + RECT 3.0 27.875 3.065 27.94 ; + RECT 3.875 27.345 3.94 27.41 ; + RECT 3.0 28.68 3.065 28.745 ; + RECT 4.05 29.21 4.115 29.275 ; + RECT 3.175 30.565 3.24 30.63 ; + RECT 3.525 30.035 3.59 30.1 ; + RECT 3.175 31.37 3.24 31.435 ; + RECT 3.7 31.9 3.765 31.965 ; + RECT 3.175 33.255 3.24 33.32 ; + RECT 3.875 32.725 3.94 32.79 ; + RECT 3.175 34.06 3.24 34.125 ; + RECT 4.05 34.59 4.115 34.655 ; + RECT 3.35 35.945 3.415 36.01 ; + RECT 3.525 35.415 3.59 35.48 ; + RECT 3.35 36.75 3.415 36.815 ; + RECT 3.7 37.28 3.765 37.345 ; + RECT 3.35 38.635 3.415 38.7 ; + RECT 3.875 38.105 3.94 38.17 ; + RECT 3.35 39.44 3.415 39.505 ; + RECT 4.05 39.97 4.115 40.035 ; + RECT 5.495 20.2075 5.56 20.2725 ; + RECT 5.95 20.2075 6.015 20.2725 ; + RECT 6.58 19.8075 6.645 19.8725 ; + RECT 5.5325 19.84 5.5975 19.905 ; + RECT 5.495 21.5525 5.56 21.6175 ; + RECT 5.95 21.5525 6.015 21.6175 ; + RECT 6.58 20.6075 6.645 20.6725 ; + RECT 5.5325 20.575 5.5975 20.64 ; + RECT 5.495 22.8975 5.56 22.9625 ; + RECT 5.95 22.8975 6.015 22.9625 ; + RECT 6.58 22.4975 6.645 22.5625 ; + RECT 5.5325 22.53 5.5975 22.595 ; + RECT 5.495 24.2425 5.56 24.3075 ; + RECT 5.95 24.2425 6.015 24.3075 ; + RECT 6.58 23.2975 6.645 23.3625 ; + RECT 5.5325 23.265 5.5975 23.33 ; + RECT 5.495 25.5875 5.56 25.6525 ; + RECT 5.95 25.5875 6.015 25.6525 ; + RECT 6.58 25.1875 6.645 25.2525 ; + RECT 5.5325 25.22 5.5975 25.285 ; + RECT 5.495 26.9325 5.56 26.9975 ; + RECT 5.95 26.9325 6.015 26.9975 ; + RECT 6.58 25.9875 6.645 26.0525 ; + RECT 5.5325 25.955 5.5975 26.02 ; + RECT 5.495 28.2775 5.56 28.3425 ; + RECT 5.95 28.2775 6.015 28.3425 ; + RECT 6.58 27.8775 6.645 27.9425 ; + RECT 5.5325 27.91 5.5975 27.975 ; + RECT 5.495 29.6225 5.56 29.6875 ; + RECT 5.95 29.6225 6.015 29.6875 ; + RECT 6.58 28.6775 6.645 28.7425 ; + RECT 5.5325 28.645 5.5975 28.71 ; + RECT 5.495 30.9675 5.56 31.0325 ; + RECT 5.95 30.9675 6.015 31.0325 ; + RECT 6.58 30.5675 6.645 30.6325 ; + RECT 5.5325 30.6 5.5975 30.665 ; + RECT 5.495 32.3125 5.56 32.3775 ; + RECT 5.95 32.3125 6.015 32.3775 ; + RECT 6.58 31.3675 6.645 31.4325 ; + RECT 5.5325 31.335 5.5975 31.4 ; + RECT 5.495 33.6575 5.56 33.7225 ; + RECT 5.95 33.6575 6.015 33.7225 ; + RECT 6.58 33.2575 6.645 33.3225 ; + RECT 5.5325 33.29 5.5975 33.355 ; + RECT 5.495 35.0025 5.56 35.0675 ; + RECT 5.95 35.0025 6.015 35.0675 ; + RECT 6.58 34.0575 6.645 34.1225 ; + RECT 5.5325 34.025 5.5975 34.09 ; + RECT 5.495 36.3475 5.56 36.4125 ; + RECT 5.95 36.3475 6.015 36.4125 ; + RECT 6.58 35.9475 6.645 36.0125 ; + RECT 5.5325 35.98 5.5975 36.045 ; + RECT 5.495 37.6925 5.56 37.7575 ; + RECT 5.95 37.6925 6.015 37.7575 ; + RECT 6.58 36.7475 6.645 36.8125 ; + RECT 5.5325 36.715 5.5975 36.78 ; + RECT 5.495 39.0375 5.56 39.1025 ; + RECT 5.95 39.0375 6.015 39.1025 ; + RECT 6.58 38.6375 6.645 38.7025 ; + RECT 5.5325 38.67 5.5975 38.735 ; + RECT 5.495 40.3825 5.56 40.4475 ; + RECT 5.95 40.3825 6.015 40.4475 ; + RECT 6.58 39.4375 6.645 39.5025 ; + RECT 5.5325 39.405 5.5975 39.47 ; + RECT 6.6475 7.61 6.7125 7.675 ; + RECT 1.47 7.1025 1.535 7.1675 ; + RECT 5.5525 7.245 5.6175 7.31 ; + RECT 3.945 7.2425 4.01 7.3075 ; + RECT 2.5475 7.2425 2.6125 7.3075 ; + RECT 1.33 7.8075 1.395 7.8725 ; + RECT 4.4075 7.1025 4.4725 7.1675 ; + RECT 3.2125 7.6075 3.2775 7.6725 ; + RECT 6.905 7.2425 6.97 7.3075 ; + RECT 6.395 7.42 6.46 7.485 ; + RECT 5.9825 7.1025 6.0475 7.1675 ; + RECT 1.28 7.1025 1.345 7.1675 ; + RECT 3.0225 7.1025 3.0875 7.1675 ; + RECT 2.8225 7.6075 2.8875 7.6725 ; + RECT 2.8225 7.42 2.8875 7.485 ; + RECT 3.435 7.42 3.5 7.485 ; + RECT 5.7825 7.61 5.8475 7.675 ; + RECT 5.7825 7.42 5.8475 7.485 ; + RECT 6.6475 6.595 6.7125 6.66 ; + RECT 1.47 7.1025 1.535 7.1675 ; + RECT 5.5525 6.96 5.6175 7.025 ; + RECT 3.945 6.9625 4.01 7.0275 ; + RECT 2.5475 6.9625 2.6125 7.0275 ; + RECT 1.33 6.3975 1.395 6.4625 ; + RECT 4.4075 7.1025 4.4725 7.1675 ; + RECT 3.2125 6.5975 3.2775 6.6625 ; + RECT 6.905 6.9625 6.97 7.0275 ; + RECT 6.395 6.785 6.46 6.85 ; + RECT 5.9825 7.1025 6.0475 7.1675 ; + RECT 1.28 7.1025 1.345 7.1675 ; + RECT 3.0225 7.1025 3.0875 7.1675 ; + RECT 2.8225 6.5975 2.8875 6.6625 ; + RECT 2.8225 6.785 2.8875 6.85 ; + RECT 3.435 6.785 3.5 6.85 ; + RECT 5.7825 6.595 5.8475 6.66 ; + RECT 5.7825 6.785 5.8475 6.85 ; + RECT 6.6475 6.2 6.7125 6.265 ; + RECT 1.47 5.6925 1.535 5.7575 ; + RECT 5.5525 5.835 5.6175 5.9 ; + RECT 3.945 5.8325 4.01 5.8975 ; + RECT 2.5475 5.8325 2.6125 5.8975 ; + RECT 1.33 6.3975 1.395 6.4625 ; + RECT 4.4075 5.6925 4.4725 5.7575 ; + RECT 3.2125 6.1975 3.2775 6.2625 ; + RECT 6.905 5.8325 6.97 5.8975 ; + RECT 6.395 6.01 6.46 6.075 ; + RECT 5.9825 5.6925 6.0475 5.7575 ; + RECT 1.28 5.6925 1.345 5.7575 ; + RECT 3.0225 5.6925 3.0875 5.7575 ; + RECT 2.8225 6.1975 2.8875 6.2625 ; + RECT 2.8225 6.01 2.8875 6.075 ; + RECT 3.435 6.01 3.5 6.075 ; + RECT 5.7825 6.2 5.8475 6.265 ; + RECT 5.7825 6.01 5.8475 6.075 ; + RECT 6.6475 5.185 6.7125 5.25 ; + RECT 1.47 5.6925 1.535 5.7575 ; + RECT 5.5525 5.55 5.6175 5.615 ; + RECT 3.945 5.5525 4.01 5.6175 ; + RECT 2.5475 5.5525 2.6125 5.6175 ; + RECT 1.33 4.9875 1.395 5.0525 ; + RECT 4.4075 5.6925 4.4725 5.7575 ; + RECT 3.2125 5.1875 3.2775 5.2525 ; + RECT 6.905 5.5525 6.97 5.6175 ; + RECT 6.395 5.375 6.46 5.44 ; + RECT 5.9825 5.6925 6.0475 5.7575 ; + RECT 1.28 5.6925 1.345 5.7575 ; + RECT 3.0225 5.6925 3.0875 5.7575 ; + RECT 2.8225 5.1875 2.8875 5.2525 ; + RECT 2.8225 5.375 2.8875 5.44 ; + RECT 3.435 5.375 3.5 5.44 ; + RECT 5.7825 5.185 5.8475 5.25 ; + RECT 5.7825 5.375 5.8475 5.44 ; + RECT 8.5425 8.765 8.6075 8.83 ; + RECT 8.3325 10.2 8.3975 10.265 ; + RECT 8.1225 14.145 8.1875 14.21 ; + RECT 7.9125 15.58 7.9775 15.645 ; + RECT 10.3625 3.635 10.4275 3.7 ; + RECT 9.9425 1.45 10.0075 1.515 ; + RECT 10.1525 2.9975 10.2175 3.0625 ; + RECT 10.3625 41.135 10.4275 41.2 ; + RECT 10.5725 10.1375 10.6375 10.2025 ; + RECT 10.7825 14.1625 10.8475 14.2275 ; + RECT 1.015 7.63 1.08 7.695 ; + RECT 9.7325 41.565 9.7975 41.63 ; + RECT 8.89 40.58 8.955 40.645 ; + RECT 9.03 40.58 9.095 40.645 ; + RECT 11.805 40.5475 11.87 40.6125 ; + RECT 12.51 40.5475 12.575 40.6125 ; + RECT 11.03 40.5475 11.095 40.6125 ; + RECT 9.385 0.39 9.45 0.455 ; + RECT 9.525 0.39 9.59 0.455 ; + RECT 11.8025 0.39 11.8675 0.455 ; + RECT 11.8025 0.39 11.8675 0.455 ; + RECT 7.79 21.555 7.855 21.62 ; + RECT 7.79 24.245 7.855 24.31 ; + RECT 7.79 26.935 7.855 27.0 ; + RECT 7.79 29.625 7.855 29.69 ; + RECT 7.79 32.315 7.855 32.38 ; + RECT 7.79 35.005 7.855 35.07 ; + RECT 7.79 37.695 7.855 37.76 ; + RECT 7.79 40.385 7.855 40.45 ; + RECT 8.89 13.4475 8.955 13.5125 ; + RECT 9.03 13.4475 9.095 13.5125 ; + RECT 8.89 18.8275 8.955 18.8925 ; + RECT 9.03 18.8275 9.095 18.8925 ; + RECT 7.315 7.81 7.38 7.875 ; + RECT 8.89 7.8425 8.955 7.9075 ; + RECT 9.03 7.8425 9.095 7.9075 ; + RECT 7.315 6.4 7.38 6.465 ; + RECT 8.89 6.4325 8.955 6.4975 ; + RECT 9.03 6.4325 9.095 6.4975 ; + RECT 7.315 6.4 7.38 6.465 ; + RECT 8.89 6.4325 8.955 6.4975 ; + RECT 9.03 6.4325 9.095 6.4975 ; + RECT 7.315 4.99 7.38 5.055 ; + RECT 8.89 5.0225 8.955 5.0875 ; + RECT 9.03 5.0225 9.095 5.0875 ; + RECT -5.565 14.0125 -5.5 14.0775 ; + RECT -5.0575 8.835 -4.9925 8.9 ; + RECT -5.2 12.9175 -5.135 12.9825 ; + RECT -5.1975 11.31 -5.1325 11.375 ; + RECT -5.1975 9.9125 -5.1325 9.9775 ; + RECT -5.7625 8.695 -5.6975 8.76 ; + RECT -5.0575 11.7725 -4.9925 11.8375 ; + RECT -5.5625 10.5775 -5.4975 10.6425 ; + RECT -5.1975 14.27 -5.1325 14.335 ; + RECT -5.375 13.76 -5.31 13.825 ; + RECT -5.0575 13.3475 -4.9925 13.4125 ; + RECT -5.0575 8.645 -4.9925 8.71 ; + RECT -5.0575 10.3875 -4.9925 10.4525 ; + RECT -5.5625 10.1875 -5.4975 10.2525 ; + RECT -5.375 10.1875 -5.31 10.2525 ; + RECT -5.375 10.8 -5.31 10.865 ; + RECT -5.565 13.1475 -5.5 13.2125 ; + RECT -5.375 13.1475 -5.31 13.2125 ; + RECT -4.55 14.0125 -4.485 14.0775 ; + RECT -5.0575 8.835 -4.9925 8.9 ; + RECT -4.915 12.9175 -4.85 12.9825 ; + RECT -4.9175 11.31 -4.8525 11.375 ; + RECT -4.9175 9.9125 -4.8525 9.9775 ; + RECT -4.3525 8.695 -4.2875 8.76 ; + RECT -5.0575 11.7725 -4.9925 11.8375 ; + RECT -4.5525 10.5775 -4.4875 10.6425 ; + RECT -4.9175 14.27 -4.8525 14.335 ; + RECT -4.74 13.76 -4.675 13.825 ; + RECT -5.0575 13.3475 -4.9925 13.4125 ; + RECT -5.0575 8.645 -4.9925 8.71 ; + RECT -5.0575 10.3875 -4.9925 10.4525 ; + RECT -4.5525 10.1875 -4.4875 10.2525 ; + RECT -4.74 10.1875 -4.675 10.2525 ; + RECT -4.74 10.8 -4.675 10.865 ; + RECT -4.55 13.1475 -4.485 13.2125 ; + RECT -4.74 13.1475 -4.675 13.2125 ; + RECT -4.155 14.0125 -4.09 14.0775 ; + RECT -3.6475 8.835 -3.5825 8.9 ; + RECT -3.79 12.9175 -3.725 12.9825 ; + RECT -3.7875 11.31 -3.7225 11.375 ; + RECT -3.7875 9.9125 -3.7225 9.9775 ; + RECT -4.3525 8.695 -4.2875 8.76 ; + RECT -3.6475 11.7725 -3.5825 11.8375 ; + RECT -4.1525 10.5775 -4.0875 10.6425 ; + RECT -3.7875 14.27 -3.7225 14.335 ; + RECT -3.965 13.76 -3.9 13.825 ; + RECT -3.6475 13.3475 -3.5825 13.4125 ; + RECT -3.6475 8.645 -3.5825 8.71 ; + RECT -3.6475 10.3875 -3.5825 10.4525 ; + RECT -4.1525 10.1875 -4.0875 10.2525 ; + RECT -3.965 10.1875 -3.9 10.2525 ; + RECT -3.965 10.8 -3.9 10.865 ; + RECT -4.155 13.1475 -4.09 13.2125 ; + RECT -3.965 13.1475 -3.9 13.2125 ; + RECT -1.3775 8.8425 -1.3125 8.9075 ; + RECT -0.8675 8.8425 -0.8025 8.9075 ; + RECT -1.0 23.83 -0.935 23.895 ; + RECT -1.0 23.27 -0.935 23.335 ; + RECT -2.52 22.845 -2.455 22.91 ; + RECT -2.52 23.405 -2.455 23.47 ; + RECT -1.0 23.5075 -0.935 23.5725 ; + RECT -1.0 22.9475 -0.935 23.0125 ; + RECT -2.52 23.1675 -2.455 23.2325 ; + RECT -4.225 23.0375 -4.16 23.1025 ; + RECT -4.93 23.5125 -4.865 23.5775 ; + RECT -4.225 23.5125 -4.16 23.5775 ; + RECT -4.6775 23.0325 -4.6125 23.0975 ; + RECT -4.4775 23.0375 -4.4125 23.1025 ; + RECT -4.93 22.7075 -4.865 22.7725 ; + RECT -4.225 22.7075 -4.16 22.7725 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.225 22.7075 -4.16 22.7725 ; + RECT -4.93 22.7075 -4.865 22.7725 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.48 22.3825 -4.415 22.4475 ; + RECT -4.225 21.89 -4.16 21.955 ; + RECT -4.225 21.89 -4.16 21.955 ; + RECT -4.225 21.89 -4.16 21.955 ; + RECT -4.225 21.89 -4.16 21.955 ; + RECT -4.225 21.89 -4.16 21.955 ; + RECT -4.225 21.89 -4.16 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.675 22.3825 -4.61 22.4475 ; + RECT -4.93 22.7075 -4.865 22.7725 ; + RECT -4.93 22.7075 -4.865 22.7725 ; + RECT -4.93 22.7075 -4.865 22.7725 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.93 21.89 -4.865 21.955 ; + RECT -4.225 22.7075 -4.16 22.7725 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.225 20.0175 -4.16 20.0825 ; + RECT -4.93 20.0175 -4.865 20.0825 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.48 20.3425 -4.415 20.4075 ; + RECT -4.225 20.835 -4.16 20.9 ; + RECT -4.225 20.835 -4.16 20.9 ; + RECT -4.225 20.835 -4.16 20.9 ; + RECT -4.225 20.835 -4.16 20.9 ; + RECT -4.225 20.835 -4.16 20.9 ; + RECT -4.225 20.835 -4.16 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.675 20.3425 -4.61 20.4075 ; + RECT -4.93 20.0175 -4.865 20.0825 ; + RECT -4.93 20.0175 -4.865 20.0825 ; + RECT -4.93 20.0175 -4.865 20.0825 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.93 20.835 -4.865 20.9 ; + RECT -4.225 20.0175 -4.16 20.0825 ; + RECT -1.975 21.1775 -1.91 21.2425 ; + RECT -1.38 21.385 -1.315 21.45 ; + RECT -2.135 21.385 -2.07 21.45 ; + RECT -4.41 24.28 -4.345 24.345 ; + RECT -0.6475 19.52 -0.5825 19.585 ; + RECT -2.555 19.5225 -2.49 19.5875 ; + RECT -2.59 24.2025 -2.525 24.2675 ; + RECT -0.6475 21.365 -0.5825 21.43 ; + RECT -4.025 22.4325 -3.96 22.4975 ; + RECT -3.735 22.0075 -3.67 22.0725 ; + RECT -3.735 23.7675 -3.67 23.8325 ; + RECT -0.415 20.6625 -0.35 20.7275 ; + RECT -0.415 16.165 -0.35 16.23 ; + RECT -3.105 16.1675 -3.04 16.2325 ; + RECT -3.77 26.2675 -3.705 26.3325 ; + RECT -3.525 23.93 -3.46 23.995 ; + RECT -3.525 25.01 -3.46 25.075 ; + RECT -3.735 28.345 -3.67 28.41 ; + RECT -3.525 23.93 -3.46 23.995 ; + RECT -3.525 22.6 -3.46 22.665 ; + RECT -3.735 31.035 -3.67 31.1 ; + RECT -4.93 20.6625 -4.865 20.7275 ; + RECT -4.225 20.6625 -4.16 20.7275 ; + RECT -2.0775 16.6075 -2.0125 16.6725 ; + RECT -2.0775 16.9875 -2.0125 17.0525 ; + RECT -2.6775 16.6625 -2.6125 16.7275 ; + RECT -2.715 16.25 -2.65 16.315 ; + RECT -2.6775 16.8525 -2.6125 16.9175 ; + RECT -2.855 16.25 -2.79 16.315 ; + RECT -4.2025 16.6075 -4.1375 16.6725 ; + RECT -4.2025 16.9875 -4.1375 17.0525 ; + RECT -3.6025 16.6625 -3.5375 16.7275 ; + RECT -3.5 16.25 -3.435 16.315 ; + RECT -3.6025 16.8525 -3.5375 16.9175 ; + RECT -3.36 16.25 -3.295 16.315 ; + RECT -3.9975 14.65 -3.9325 14.715 ; + RECT -3.9975 15.14 -3.9325 15.205 ; + RECT -4.1525 14.65 -4.0875 14.715 ; + RECT -4.1525 15.35 -4.0875 15.415 ; + RECT -5.5625 14.65 -5.4975 14.715 ; + RECT -5.5625 15.56 -5.4975 15.625 ; + RECT -4.5475 14.65 -4.4825 14.715 ; + RECT -4.5475 15.77 -4.4825 15.835 ; + RECT -1.325 15.35 -1.26 15.415 ; + RECT -0.795 15.98 -0.73 16.045 ; + RECT -2.12 15.98 -2.055 16.045 ; + RECT -2.6425 15.35 -2.5775 15.415 ; + RECT -2.7825 15.56 -2.7175 15.625 ; + RECT -4.025 15.98 -3.96 16.045 ; + RECT -3.5025 15.77 -3.4375 15.835 ; + RECT -3.3625 15.56 -3.2975 15.625 ; + RECT -0.9975 9.8525 -0.9325 9.9175 ; + RECT -0.2225 9.8525 -0.1575 9.9175 ; + RECT -0.22 15.1375 -0.155 15.2025 ; + RECT -2.49 19.44 -2.425 19.505 ; + RECT -3.655 19.23 -3.59 19.295 ; + RECT -0.965 19.65 -0.9 19.715 ; + RECT -0.2075 19.4375 -0.1425 19.5025 ; + RECT -1.7275 19.86 -1.6625 19.925 ; + RECT -4.4175 19.86 -4.3525 19.925 ; + RECT -0.3825 14.93 -0.3175 14.995 ; + RECT -0.965 13.4125 -0.9 13.4775 ; + RECT -2.0875 13.4125 -2.0225 13.4775 ; + RECT -2.0825 8.31 -2.0175 8.375 ; + RECT -0.865 8.45 -0.8 8.515 ; + RECT -1.0475 16.8675 -0.9825 16.9325 ; + RECT -1.0 17.3375 -0.935 17.4025 ; + RECT 0.0 19.89 0.065 19.955 ; + RECT 0.14 19.89 0.205 19.955 ; + Layer metal2 ; + RECT -0.35 19.855 0.0 19.925 ; + RECT 8.89 0.0 9.59 41.725 ; + RECT 10.78 0.0 10.85 41.725 ; + RECT 10.57 0.0 10.64 41.725 ; + RECT 10.36 0.0 10.43 41.725 ; + RECT 10.15 0.0 10.22 41.725 ; + RECT 9.94 0.0 10.01 41.725 ; + RECT 9.73 0.0 9.8 41.725 ; + RECT 8.54 5.02 8.61 18.615 ; + RECT 8.33 5.02 8.4 18.615 ; + RECT 8.12 5.02 8.19 18.615 ; + RECT 7.91 5.02 7.98 18.615 ; + RECT 11.28 40.415 11.35 40.765 ; + RECT 11.615 40.415 11.685 40.765 ; + RECT 11.985 40.415 12.055 40.765 ; + RECT 12.32 40.415 12.39 40.765 ; + RECT 10.36 18.895 10.43 41.24 ; + RECT 11.8 40.415 11.87 40.61 ; + RECT 12.505 40.415 12.575 40.61 ; + RECT 11.06 18.895 11.13 40.61 ; + RECT 7.855 21.5525 8.89 21.6225 ; + RECT 7.855 24.2425 8.89 24.3125 ; + RECT 7.855 26.9325 8.89 27.0025 ; + RECT 7.855 29.6225 8.89 29.6925 ; + RECT 7.855 32.3125 8.89 32.3825 ; + RECT 7.855 35.0025 8.89 35.0725 ; + RECT 7.855 37.6925 8.89 37.7625 ; + RECT 7.855 40.3825 8.89 40.4525 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.0625 18.86 11.1975 18.93 ; + RECT 11.7675 18.86 11.9025 18.93 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.35 19.1525 11.42 19.2875 ; + RECT 11.095 19.645 11.165 19.78 ; + RECT 11.095 19.645 11.165 19.78 ; + RECT 11.095 19.645 11.165 19.78 ; + RECT 11.095 19.645 11.165 19.78 ; + RECT 11.095 19.645 11.165 19.78 ; + RECT 11.095 19.645 11.165 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.545 19.1525 11.615 19.2875 ; + RECT 11.7675 18.86 11.9025 18.93 ; + RECT 11.7675 18.86 11.9025 18.93 ; + RECT 11.7675 18.86 11.9025 18.93 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.0625 18.86 11.1975 18.93 ; + RECT 11.8 18.8625 11.87 20.2725 ; + RECT 11.1 18.865 11.16 18.9225 ; + RECT 11.2875 18.87 11.3425 18.9225 ; + RECT 11.6225 18.8625 11.68 18.9225 ; + RECT 11.805 18.87 11.865 18.9275 ; + RECT 11.615 18.795 11.685 20.34 ; + RECT 11.28 18.795 11.35 20.34 ; + RECT 11.095 18.795 11.165 20.34 ; + RECT 11.8 18.795 11.87 20.34 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.0625 21.55 11.1975 21.62 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.35 21.1925 11.42 21.3275 ; + RECT 11.095 20.7 11.165 20.835 ; + RECT 11.095 20.7 11.165 20.835 ; + RECT 11.095 20.7 11.165 20.835 ; + RECT 11.095 20.7 11.165 20.835 ; + RECT 11.095 20.7 11.165 20.835 ; + RECT 11.095 20.7 11.165 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.545 21.1925 11.615 21.3275 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.0625 21.55 11.1975 21.62 ; + RECT 11.8 20.2075 11.87 21.6175 ; + RECT 11.1 21.5575 11.16 21.615 ; + RECT 11.2875 21.5575 11.3425 21.61 ; + RECT 11.6225 21.5575 11.68 21.6175 ; + RECT 11.805 21.5525 11.865 21.61 ; + RECT 11.615 20.14 11.685 21.685 ; + RECT 11.28 20.14 11.35 21.685 ; + RECT 11.095 20.14 11.165 21.685 ; + RECT 11.8 20.14 11.87 21.685 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.0625 21.55 11.1975 21.62 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.35 21.8425 11.42 21.9775 ; + RECT 11.095 22.335 11.165 22.47 ; + RECT 11.095 22.335 11.165 22.47 ; + RECT 11.095 22.335 11.165 22.47 ; + RECT 11.095 22.335 11.165 22.47 ; + RECT 11.095 22.335 11.165 22.47 ; + RECT 11.095 22.335 11.165 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.545 21.8425 11.615 21.9775 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.0625 21.55 11.1975 21.62 ; + RECT 11.8 21.5525 11.87 22.9625 ; + RECT 11.1 21.555 11.16 21.6125 ; + RECT 11.2875 21.56 11.3425 21.6125 ; + RECT 11.6225 21.5525 11.68 21.6125 ; + RECT 11.805 21.56 11.865 21.6175 ; + RECT 11.615 21.485 11.685 23.03 ; + RECT 11.28 21.485 11.35 23.03 ; + RECT 11.095 21.485 11.165 23.03 ; + RECT 11.8 21.485 11.87 23.03 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.0625 24.24 11.1975 24.31 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.35 23.8825 11.42 24.0175 ; + RECT 11.095 23.39 11.165 23.525 ; + RECT 11.095 23.39 11.165 23.525 ; + RECT 11.095 23.39 11.165 23.525 ; + RECT 11.095 23.39 11.165 23.525 ; + RECT 11.095 23.39 11.165 23.525 ; + RECT 11.095 23.39 11.165 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.545 23.8825 11.615 24.0175 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.0625 24.24 11.1975 24.31 ; + RECT 11.8 22.8975 11.87 24.3075 ; + RECT 11.1 24.2475 11.16 24.305 ; + RECT 11.2875 24.2475 11.3425 24.3 ; + RECT 11.6225 24.2475 11.68 24.3075 ; + RECT 11.805 24.2425 11.865 24.3 ; + RECT 11.615 22.83 11.685 24.375 ; + RECT 11.28 22.83 11.35 24.375 ; + RECT 11.095 22.83 11.165 24.375 ; + RECT 11.8 22.83 11.87 24.375 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.0625 24.24 11.1975 24.31 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.35 24.5325 11.42 24.6675 ; + RECT 11.095 25.025 11.165 25.16 ; + RECT 11.095 25.025 11.165 25.16 ; + RECT 11.095 25.025 11.165 25.16 ; + RECT 11.095 25.025 11.165 25.16 ; + RECT 11.095 25.025 11.165 25.16 ; + RECT 11.095 25.025 11.165 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.545 24.5325 11.615 24.6675 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.0625 24.24 11.1975 24.31 ; + RECT 11.8 24.2425 11.87 25.6525 ; + RECT 11.1 24.245 11.16 24.3025 ; + RECT 11.2875 24.25 11.3425 24.3025 ; + RECT 11.6225 24.2425 11.68 24.3025 ; + RECT 11.805 24.25 11.865 24.3075 ; + RECT 11.615 24.175 11.685 25.72 ; + RECT 11.28 24.175 11.35 25.72 ; + RECT 11.095 24.175 11.165 25.72 ; + RECT 11.8 24.175 11.87 25.72 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.0625 26.93 11.1975 27.0 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.35 26.5725 11.42 26.7075 ; + RECT 11.095 26.08 11.165 26.215 ; + RECT 11.095 26.08 11.165 26.215 ; + RECT 11.095 26.08 11.165 26.215 ; + RECT 11.095 26.08 11.165 26.215 ; + RECT 11.095 26.08 11.165 26.215 ; + RECT 11.095 26.08 11.165 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.545 26.5725 11.615 26.7075 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.0625 26.93 11.1975 27.0 ; + RECT 11.8 25.5875 11.87 26.9975 ; + RECT 11.1 26.9375 11.16 26.995 ; + RECT 11.2875 26.9375 11.3425 26.99 ; + RECT 11.6225 26.9375 11.68 26.9975 ; + RECT 11.805 26.9325 11.865 26.99 ; + RECT 11.615 25.52 11.685 27.065 ; + RECT 11.28 25.52 11.35 27.065 ; + RECT 11.095 25.52 11.165 27.065 ; + RECT 11.8 25.52 11.87 27.065 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.0625 26.93 11.1975 27.0 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.35 27.2225 11.42 27.3575 ; + RECT 11.095 27.715 11.165 27.85 ; + RECT 11.095 27.715 11.165 27.85 ; + RECT 11.095 27.715 11.165 27.85 ; + RECT 11.095 27.715 11.165 27.85 ; + RECT 11.095 27.715 11.165 27.85 ; + RECT 11.095 27.715 11.165 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.545 27.2225 11.615 27.3575 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.0625 26.93 11.1975 27.0 ; + RECT 11.8 26.9325 11.87 28.3425 ; + RECT 11.1 26.935 11.16 26.9925 ; + RECT 11.2875 26.94 11.3425 26.9925 ; + RECT 11.6225 26.9325 11.68 26.9925 ; + RECT 11.805 26.94 11.865 26.9975 ; + RECT 11.615 26.865 11.685 28.41 ; + RECT 11.28 26.865 11.35 28.41 ; + RECT 11.095 26.865 11.165 28.41 ; + RECT 11.8 26.865 11.87 28.41 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.0625 29.62 11.1975 29.69 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.35 29.2625 11.42 29.3975 ; + RECT 11.095 28.77 11.165 28.905 ; + RECT 11.095 28.77 11.165 28.905 ; + RECT 11.095 28.77 11.165 28.905 ; + RECT 11.095 28.77 11.165 28.905 ; + RECT 11.095 28.77 11.165 28.905 ; + RECT 11.095 28.77 11.165 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.545 29.2625 11.615 29.3975 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.0625 29.62 11.1975 29.69 ; + RECT 11.8 28.2775 11.87 29.6875 ; + RECT 11.1 29.6275 11.16 29.685 ; + RECT 11.2875 29.6275 11.3425 29.68 ; + RECT 11.6225 29.6275 11.68 29.6875 ; + RECT 11.805 29.6225 11.865 29.68 ; + RECT 11.615 28.21 11.685 29.755 ; + RECT 11.28 28.21 11.35 29.755 ; + RECT 11.095 28.21 11.165 29.755 ; + RECT 11.8 28.21 11.87 29.755 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.0625 29.62 11.1975 29.69 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.35 29.9125 11.42 30.0475 ; + RECT 11.095 30.405 11.165 30.54 ; + RECT 11.095 30.405 11.165 30.54 ; + RECT 11.095 30.405 11.165 30.54 ; + RECT 11.095 30.405 11.165 30.54 ; + RECT 11.095 30.405 11.165 30.54 ; + RECT 11.095 30.405 11.165 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.545 29.9125 11.615 30.0475 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.0625 29.62 11.1975 29.69 ; + RECT 11.8 29.6225 11.87 31.0325 ; + RECT 11.1 29.625 11.16 29.6825 ; + RECT 11.2875 29.63 11.3425 29.6825 ; + RECT 11.6225 29.6225 11.68 29.6825 ; + RECT 11.805 29.63 11.865 29.6875 ; + RECT 11.615 29.555 11.685 31.1 ; + RECT 11.28 29.555 11.35 31.1 ; + RECT 11.095 29.555 11.165 31.1 ; + RECT 11.8 29.555 11.87 31.1 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.0625 32.31 11.1975 32.38 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.35 31.9525 11.42 32.0875 ; + RECT 11.095 31.46 11.165 31.595 ; + RECT 11.095 31.46 11.165 31.595 ; + RECT 11.095 31.46 11.165 31.595 ; + RECT 11.095 31.46 11.165 31.595 ; + RECT 11.095 31.46 11.165 31.595 ; + RECT 11.095 31.46 11.165 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.545 31.9525 11.615 32.0875 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.0625 32.31 11.1975 32.38 ; + RECT 11.8 30.9675 11.87 32.3775 ; + RECT 11.1 32.3175 11.16 32.375 ; + RECT 11.2875 32.3175 11.3425 32.37 ; + RECT 11.6225 32.3175 11.68 32.3775 ; + RECT 11.805 32.3125 11.865 32.37 ; + RECT 11.615 30.9 11.685 32.445 ; + RECT 11.28 30.9 11.35 32.445 ; + RECT 11.095 30.9 11.165 32.445 ; + RECT 11.8 30.9 11.87 32.445 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.0625 32.31 11.1975 32.38 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.35 32.6025 11.42 32.7375 ; + RECT 11.095 33.095 11.165 33.23 ; + RECT 11.095 33.095 11.165 33.23 ; + RECT 11.095 33.095 11.165 33.23 ; + RECT 11.095 33.095 11.165 33.23 ; + RECT 11.095 33.095 11.165 33.23 ; + RECT 11.095 33.095 11.165 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.545 32.6025 11.615 32.7375 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.0625 32.31 11.1975 32.38 ; + RECT 11.8 32.3125 11.87 33.7225 ; + RECT 11.1 32.315 11.16 32.3725 ; + RECT 11.2875 32.32 11.3425 32.3725 ; + RECT 11.6225 32.3125 11.68 32.3725 ; + RECT 11.805 32.32 11.865 32.3775 ; + RECT 11.615 32.245 11.685 33.79 ; + RECT 11.28 32.245 11.35 33.79 ; + RECT 11.095 32.245 11.165 33.79 ; + RECT 11.8 32.245 11.87 33.79 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.0625 35.0 11.1975 35.07 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.35 34.6425 11.42 34.7775 ; + RECT 11.095 34.15 11.165 34.285 ; + RECT 11.095 34.15 11.165 34.285 ; + RECT 11.095 34.15 11.165 34.285 ; + RECT 11.095 34.15 11.165 34.285 ; + RECT 11.095 34.15 11.165 34.285 ; + RECT 11.095 34.15 11.165 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.545 34.6425 11.615 34.7775 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.0625 35.0 11.1975 35.07 ; + RECT 11.8 33.6575 11.87 35.0675 ; + RECT 11.1 35.0075 11.16 35.065 ; + RECT 11.2875 35.0075 11.3425 35.06 ; + RECT 11.6225 35.0075 11.68 35.0675 ; + RECT 11.805 35.0025 11.865 35.06 ; + RECT 11.615 33.59 11.685 35.135 ; + RECT 11.28 33.59 11.35 35.135 ; + RECT 11.095 33.59 11.165 35.135 ; + RECT 11.8 33.59 11.87 35.135 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.0625 35.0 11.1975 35.07 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.35 35.2925 11.42 35.4275 ; + RECT 11.095 35.785 11.165 35.92 ; + RECT 11.095 35.785 11.165 35.92 ; + RECT 11.095 35.785 11.165 35.92 ; + RECT 11.095 35.785 11.165 35.92 ; + RECT 11.095 35.785 11.165 35.92 ; + RECT 11.095 35.785 11.165 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.545 35.2925 11.615 35.4275 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.0625 35.0 11.1975 35.07 ; + RECT 11.8 35.0025 11.87 36.4125 ; + RECT 11.1 35.005 11.16 35.0625 ; + RECT 11.2875 35.01 11.3425 35.0625 ; + RECT 11.6225 35.0025 11.68 35.0625 ; + RECT 11.805 35.01 11.865 35.0675 ; + RECT 11.615 34.935 11.685 36.48 ; + RECT 11.28 34.935 11.35 36.48 ; + RECT 11.095 34.935 11.165 36.48 ; + RECT 11.8 34.935 11.87 36.48 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.0625 37.69 11.1975 37.76 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.35 37.3325 11.42 37.4675 ; + RECT 11.095 36.84 11.165 36.975 ; + RECT 11.095 36.84 11.165 36.975 ; + RECT 11.095 36.84 11.165 36.975 ; + RECT 11.095 36.84 11.165 36.975 ; + RECT 11.095 36.84 11.165 36.975 ; + RECT 11.095 36.84 11.165 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.545 37.3325 11.615 37.4675 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.0625 37.69 11.1975 37.76 ; + RECT 11.8 36.3475 11.87 37.7575 ; + RECT 11.1 37.6975 11.16 37.755 ; + RECT 11.2875 37.6975 11.3425 37.75 ; + RECT 11.6225 37.6975 11.68 37.7575 ; + RECT 11.805 37.6925 11.865 37.75 ; + RECT 11.615 36.28 11.685 37.825 ; + RECT 11.28 36.28 11.35 37.825 ; + RECT 11.095 36.28 11.165 37.825 ; + RECT 11.8 36.28 11.87 37.825 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.0625 37.69 11.1975 37.76 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.35 37.9825 11.42 38.1175 ; + RECT 11.095 38.475 11.165 38.61 ; + RECT 11.095 38.475 11.165 38.61 ; + RECT 11.095 38.475 11.165 38.61 ; + RECT 11.095 38.475 11.165 38.61 ; + RECT 11.095 38.475 11.165 38.61 ; + RECT 11.095 38.475 11.165 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.545 37.9825 11.615 38.1175 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.0625 37.69 11.1975 37.76 ; + RECT 11.8 37.6925 11.87 39.1025 ; + RECT 11.1 37.695 11.16 37.7525 ; + RECT 11.2875 37.7 11.3425 37.7525 ; + RECT 11.6225 37.6925 11.68 37.7525 ; + RECT 11.805 37.7 11.865 37.7575 ; + RECT 11.615 37.625 11.685 39.17 ; + RECT 11.28 37.625 11.35 39.17 ; + RECT 11.095 37.625 11.165 39.17 ; + RECT 11.8 37.625 11.87 39.17 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.0625 40.38 11.1975 40.45 ; + RECT 11.7675 40.38 11.9025 40.45 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.35 40.0225 11.42 40.1575 ; + RECT 11.095 39.53 11.165 39.665 ; + RECT 11.095 39.53 11.165 39.665 ; + RECT 11.095 39.53 11.165 39.665 ; + RECT 11.095 39.53 11.165 39.665 ; + RECT 11.095 39.53 11.165 39.665 ; + RECT 11.095 39.53 11.165 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.545 40.0225 11.615 40.1575 ; + RECT 11.7675 40.38 11.9025 40.45 ; + RECT 11.7675 40.38 11.9025 40.45 ; + RECT 11.7675 40.38 11.9025 40.45 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.0625 40.38 11.1975 40.45 ; + RECT 11.8 39.0375 11.87 40.4475 ; + RECT 11.1 40.3875 11.16 40.445 ; + RECT 11.2875 40.3875 11.3425 40.44 ; + RECT 11.6225 40.3875 11.68 40.4475 ; + RECT 11.805 40.3825 11.865 40.44 ; + RECT 11.615 38.97 11.685 40.515 ; + RECT 11.28 38.97 11.35 40.515 ; + RECT 11.095 38.97 11.165 40.515 ; + RECT 11.8 38.97 11.87 40.515 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 11.7675 18.86 11.9025 18.93 ; + RECT 12.4725 18.86 12.6075 18.93 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.055 19.1525 12.125 19.2875 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 11.8 19.645 11.87 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.25 19.1525 12.32 19.2875 ; + RECT 12.4725 18.86 12.6075 18.93 ; + RECT 12.4725 18.86 12.6075 18.93 ; + RECT 12.4725 18.86 12.6075 18.93 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 12.505 19.645 12.575 19.78 ; + RECT 11.7675 18.86 11.9025 18.93 ; + RECT 12.505 18.8625 12.575 20.2725 ; + RECT 11.805 18.865 11.865 18.9225 ; + RECT 11.9925 18.87 12.0475 18.9225 ; + RECT 12.3275 18.8625 12.385 18.9225 ; + RECT 12.51 18.87 12.57 18.9275 ; + RECT 12.32 18.795 12.39 20.34 ; + RECT 11.985 18.795 12.055 20.34 ; + RECT 11.8 18.795 11.87 20.34 ; + RECT 12.505 18.795 12.575 20.34 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 12.4725 21.55 12.6075 21.62 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.055 21.1925 12.125 21.3275 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 11.8 20.7 11.87 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.25 21.1925 12.32 21.3275 ; + RECT 12.4725 21.55 12.6075 21.62 ; + RECT 12.4725 21.55 12.6075 21.62 ; + RECT 12.4725 21.55 12.6075 21.62 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 12.505 20.7 12.575 20.835 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 12.505 20.2075 12.575 21.6175 ; + RECT 11.805 21.5575 11.865 21.615 ; + RECT 11.9925 21.5575 12.0475 21.61 ; + RECT 12.3275 21.5575 12.385 21.6175 ; + RECT 12.51 21.5525 12.57 21.61 ; + RECT 12.32 20.14 12.39 21.685 ; + RECT 11.985 20.14 12.055 21.685 ; + RECT 11.8 20.14 11.87 21.685 ; + RECT 12.505 20.14 12.575 21.685 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 12.4725 21.55 12.6075 21.62 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.055 21.8425 12.125 21.9775 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 11.8 22.335 11.87 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.25 21.8425 12.32 21.9775 ; + RECT 12.4725 21.55 12.6075 21.62 ; + RECT 12.4725 21.55 12.6075 21.62 ; + RECT 12.4725 21.55 12.6075 21.62 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 12.505 22.335 12.575 22.47 ; + RECT 11.7675 21.55 11.9025 21.62 ; + RECT 12.505 21.5525 12.575 22.9625 ; + RECT 11.805 21.555 11.865 21.6125 ; + RECT 11.9925 21.56 12.0475 21.6125 ; + RECT 12.3275 21.5525 12.385 21.6125 ; + RECT 12.51 21.56 12.57 21.6175 ; + RECT 12.32 21.485 12.39 23.03 ; + RECT 11.985 21.485 12.055 23.03 ; + RECT 11.8 21.485 11.87 23.03 ; + RECT 12.505 21.485 12.575 23.03 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 12.4725 24.24 12.6075 24.31 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.055 23.8825 12.125 24.0175 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 11.8 23.39 11.87 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.25 23.8825 12.32 24.0175 ; + RECT 12.4725 24.24 12.6075 24.31 ; + RECT 12.4725 24.24 12.6075 24.31 ; + RECT 12.4725 24.24 12.6075 24.31 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 12.505 23.39 12.575 23.525 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 12.505 22.8975 12.575 24.3075 ; + RECT 11.805 24.2475 11.865 24.305 ; + RECT 11.9925 24.2475 12.0475 24.3 ; + RECT 12.3275 24.2475 12.385 24.3075 ; + RECT 12.51 24.2425 12.57 24.3 ; + RECT 12.32 22.83 12.39 24.375 ; + RECT 11.985 22.83 12.055 24.375 ; + RECT 11.8 22.83 11.87 24.375 ; + RECT 12.505 22.83 12.575 24.375 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 12.4725 24.24 12.6075 24.31 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.055 24.5325 12.125 24.6675 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 11.8 25.025 11.87 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.25 24.5325 12.32 24.6675 ; + RECT 12.4725 24.24 12.6075 24.31 ; + RECT 12.4725 24.24 12.6075 24.31 ; + RECT 12.4725 24.24 12.6075 24.31 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 12.505 25.025 12.575 25.16 ; + RECT 11.7675 24.24 11.9025 24.31 ; + RECT 12.505 24.2425 12.575 25.6525 ; + RECT 11.805 24.245 11.865 24.3025 ; + RECT 11.9925 24.25 12.0475 24.3025 ; + RECT 12.3275 24.2425 12.385 24.3025 ; + RECT 12.51 24.25 12.57 24.3075 ; + RECT 12.32 24.175 12.39 25.72 ; + RECT 11.985 24.175 12.055 25.72 ; + RECT 11.8 24.175 11.87 25.72 ; + RECT 12.505 24.175 12.575 25.72 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 12.4725 26.93 12.6075 27.0 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.055 26.5725 12.125 26.7075 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 11.8 26.08 11.87 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.25 26.5725 12.32 26.7075 ; + RECT 12.4725 26.93 12.6075 27.0 ; + RECT 12.4725 26.93 12.6075 27.0 ; + RECT 12.4725 26.93 12.6075 27.0 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 12.505 26.08 12.575 26.215 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 12.505 25.5875 12.575 26.9975 ; + RECT 11.805 26.9375 11.865 26.995 ; + RECT 11.9925 26.9375 12.0475 26.99 ; + RECT 12.3275 26.9375 12.385 26.9975 ; + RECT 12.51 26.9325 12.57 26.99 ; + RECT 12.32 25.52 12.39 27.065 ; + RECT 11.985 25.52 12.055 27.065 ; + RECT 11.8 25.52 11.87 27.065 ; + RECT 12.505 25.52 12.575 27.065 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 12.4725 26.93 12.6075 27.0 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.055 27.2225 12.125 27.3575 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 11.8 27.715 11.87 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.25 27.2225 12.32 27.3575 ; + RECT 12.4725 26.93 12.6075 27.0 ; + RECT 12.4725 26.93 12.6075 27.0 ; + RECT 12.4725 26.93 12.6075 27.0 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 12.505 27.715 12.575 27.85 ; + RECT 11.7675 26.93 11.9025 27.0 ; + RECT 12.505 26.9325 12.575 28.3425 ; + RECT 11.805 26.935 11.865 26.9925 ; + RECT 11.9925 26.94 12.0475 26.9925 ; + RECT 12.3275 26.9325 12.385 26.9925 ; + RECT 12.51 26.94 12.57 26.9975 ; + RECT 12.32 26.865 12.39 28.41 ; + RECT 11.985 26.865 12.055 28.41 ; + RECT 11.8 26.865 11.87 28.41 ; + RECT 12.505 26.865 12.575 28.41 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 12.4725 29.62 12.6075 29.69 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.055 29.2625 12.125 29.3975 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 11.8 28.77 11.87 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.25 29.2625 12.32 29.3975 ; + RECT 12.4725 29.62 12.6075 29.69 ; + RECT 12.4725 29.62 12.6075 29.69 ; + RECT 12.4725 29.62 12.6075 29.69 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 12.505 28.77 12.575 28.905 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 12.505 28.2775 12.575 29.6875 ; + RECT 11.805 29.6275 11.865 29.685 ; + RECT 11.9925 29.6275 12.0475 29.68 ; + RECT 12.3275 29.6275 12.385 29.6875 ; + RECT 12.51 29.6225 12.57 29.68 ; + RECT 12.32 28.21 12.39 29.755 ; + RECT 11.985 28.21 12.055 29.755 ; + RECT 11.8 28.21 11.87 29.755 ; + RECT 12.505 28.21 12.575 29.755 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 12.4725 29.62 12.6075 29.69 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.055 29.9125 12.125 30.0475 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 11.8 30.405 11.87 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.25 29.9125 12.32 30.0475 ; + RECT 12.4725 29.62 12.6075 29.69 ; + RECT 12.4725 29.62 12.6075 29.69 ; + RECT 12.4725 29.62 12.6075 29.69 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 12.505 30.405 12.575 30.54 ; + RECT 11.7675 29.62 11.9025 29.69 ; + RECT 12.505 29.6225 12.575 31.0325 ; + RECT 11.805 29.625 11.865 29.6825 ; + RECT 11.9925 29.63 12.0475 29.6825 ; + RECT 12.3275 29.6225 12.385 29.6825 ; + RECT 12.51 29.63 12.57 29.6875 ; + RECT 12.32 29.555 12.39 31.1 ; + RECT 11.985 29.555 12.055 31.1 ; + RECT 11.8 29.555 11.87 31.1 ; + RECT 12.505 29.555 12.575 31.1 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 12.4725 32.31 12.6075 32.38 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.055 31.9525 12.125 32.0875 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 11.8 31.46 11.87 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.25 31.9525 12.32 32.0875 ; + RECT 12.4725 32.31 12.6075 32.38 ; + RECT 12.4725 32.31 12.6075 32.38 ; + RECT 12.4725 32.31 12.6075 32.38 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 12.505 31.46 12.575 31.595 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 12.505 30.9675 12.575 32.3775 ; + RECT 11.805 32.3175 11.865 32.375 ; + RECT 11.9925 32.3175 12.0475 32.37 ; + RECT 12.3275 32.3175 12.385 32.3775 ; + RECT 12.51 32.3125 12.57 32.37 ; + RECT 12.32 30.9 12.39 32.445 ; + RECT 11.985 30.9 12.055 32.445 ; + RECT 11.8 30.9 11.87 32.445 ; + RECT 12.505 30.9 12.575 32.445 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 12.4725 32.31 12.6075 32.38 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.055 32.6025 12.125 32.7375 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 11.8 33.095 11.87 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.25 32.6025 12.32 32.7375 ; + RECT 12.4725 32.31 12.6075 32.38 ; + RECT 12.4725 32.31 12.6075 32.38 ; + RECT 12.4725 32.31 12.6075 32.38 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 12.505 33.095 12.575 33.23 ; + RECT 11.7675 32.31 11.9025 32.38 ; + RECT 12.505 32.3125 12.575 33.7225 ; + RECT 11.805 32.315 11.865 32.3725 ; + RECT 11.9925 32.32 12.0475 32.3725 ; + RECT 12.3275 32.3125 12.385 32.3725 ; + RECT 12.51 32.32 12.57 32.3775 ; + RECT 12.32 32.245 12.39 33.79 ; + RECT 11.985 32.245 12.055 33.79 ; + RECT 11.8 32.245 11.87 33.79 ; + RECT 12.505 32.245 12.575 33.79 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 12.4725 35.0 12.6075 35.07 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.055 34.6425 12.125 34.7775 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 11.8 34.15 11.87 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.25 34.6425 12.32 34.7775 ; + RECT 12.4725 35.0 12.6075 35.07 ; + RECT 12.4725 35.0 12.6075 35.07 ; + RECT 12.4725 35.0 12.6075 35.07 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 12.505 34.15 12.575 34.285 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 12.505 33.6575 12.575 35.0675 ; + RECT 11.805 35.0075 11.865 35.065 ; + RECT 11.9925 35.0075 12.0475 35.06 ; + RECT 12.3275 35.0075 12.385 35.0675 ; + RECT 12.51 35.0025 12.57 35.06 ; + RECT 12.32 33.59 12.39 35.135 ; + RECT 11.985 33.59 12.055 35.135 ; + RECT 11.8 33.59 11.87 35.135 ; + RECT 12.505 33.59 12.575 35.135 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 12.4725 35.0 12.6075 35.07 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.055 35.2925 12.125 35.4275 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 11.8 35.785 11.87 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.25 35.2925 12.32 35.4275 ; + RECT 12.4725 35.0 12.6075 35.07 ; + RECT 12.4725 35.0 12.6075 35.07 ; + RECT 12.4725 35.0 12.6075 35.07 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 12.505 35.785 12.575 35.92 ; + RECT 11.7675 35.0 11.9025 35.07 ; + RECT 12.505 35.0025 12.575 36.4125 ; + RECT 11.805 35.005 11.865 35.0625 ; + RECT 11.9925 35.01 12.0475 35.0625 ; + RECT 12.3275 35.0025 12.385 35.0625 ; + RECT 12.51 35.01 12.57 35.0675 ; + RECT 12.32 34.935 12.39 36.48 ; + RECT 11.985 34.935 12.055 36.48 ; + RECT 11.8 34.935 11.87 36.48 ; + RECT 12.505 34.935 12.575 36.48 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 12.4725 37.69 12.6075 37.76 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.055 37.3325 12.125 37.4675 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 11.8 36.84 11.87 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.25 37.3325 12.32 37.4675 ; + RECT 12.4725 37.69 12.6075 37.76 ; + RECT 12.4725 37.69 12.6075 37.76 ; + RECT 12.4725 37.69 12.6075 37.76 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 12.505 36.84 12.575 36.975 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 12.505 36.3475 12.575 37.7575 ; + RECT 11.805 37.6975 11.865 37.755 ; + RECT 11.9925 37.6975 12.0475 37.75 ; + RECT 12.3275 37.6975 12.385 37.7575 ; + RECT 12.51 37.6925 12.57 37.75 ; + RECT 12.32 36.28 12.39 37.825 ; + RECT 11.985 36.28 12.055 37.825 ; + RECT 11.8 36.28 11.87 37.825 ; + RECT 12.505 36.28 12.575 37.825 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 12.4725 37.69 12.6075 37.76 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.055 37.9825 12.125 38.1175 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 11.8 38.475 11.87 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.25 37.9825 12.32 38.1175 ; + RECT 12.4725 37.69 12.6075 37.76 ; + RECT 12.4725 37.69 12.6075 37.76 ; + RECT 12.4725 37.69 12.6075 37.76 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 12.505 38.475 12.575 38.61 ; + RECT 11.7675 37.69 11.9025 37.76 ; + RECT 12.505 37.6925 12.575 39.1025 ; + RECT 11.805 37.695 11.865 37.7525 ; + RECT 11.9925 37.7 12.0475 37.7525 ; + RECT 12.3275 37.6925 12.385 37.7525 ; + RECT 12.51 37.7 12.57 37.7575 ; + RECT 12.32 37.625 12.39 39.17 ; + RECT 11.985 37.625 12.055 39.17 ; + RECT 11.8 37.625 11.87 39.17 ; + RECT 12.505 37.625 12.575 39.17 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 11.7675 40.38 11.9025 40.45 ; + RECT 12.4725 40.38 12.6075 40.45 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.055 40.0225 12.125 40.1575 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 11.8 39.53 11.87 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.25 40.0225 12.32 40.1575 ; + RECT 12.4725 40.38 12.6075 40.45 ; + RECT 12.4725 40.38 12.6075 40.45 ; + RECT 12.4725 40.38 12.6075 40.45 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 12.505 39.53 12.575 39.665 ; + RECT 11.7675 40.38 11.9025 40.45 ; + RECT 12.505 39.0375 12.575 40.4475 ; + RECT 11.805 40.3875 11.865 40.445 ; + RECT 11.9925 40.3875 12.0475 40.44 ; + RECT 12.3275 40.3875 12.385 40.4475 ; + RECT 12.51 40.3825 12.57 40.44 ; + RECT 12.32 38.97 12.39 40.515 ; + RECT 11.985 38.97 12.055 40.515 ; + RECT 11.8 38.97 11.87 40.515 ; + RECT 12.505 38.97 12.575 40.515 ; + RECT 11.28 40.765 11.35 41.725 ; + RECT 11.615 40.765 11.685 41.725 ; + RECT 11.315 41.3125 11.3575 41.4475 ; + RECT 11.615 41.3125 11.7025 41.4475 ; + RECT 11.315 40.8625 11.36 40.9975 ; + RECT 11.515 40.8625 11.615 40.9975 ; + RECT 11.3225 41.3125 11.3925 41.4475 ; + RECT 11.7025 41.3125 11.7725 41.4475 ; + RECT 11.3225 40.8625 11.3925 40.9975 ; + RECT 11.5125 40.8625 11.5825 40.9975 ; + RECT 11.985 40.765 12.055 41.725 ; + RECT 12.32 40.765 12.39 41.725 ; + RECT 12.02 41.3125 12.0625 41.4475 ; + RECT 12.32 41.3125 12.4075 41.4475 ; + RECT 12.02 40.8625 12.065 40.9975 ; + RECT 12.22 40.8625 12.32 40.9975 ; + RECT 12.0275 41.3125 12.0975 41.4475 ; + RECT 12.4075 41.3125 12.4775 41.4475 ; + RECT 12.0275 40.8625 12.0975 40.9975 ; + RECT 12.2175 40.8625 12.2875 40.9975 ; + RECT 11.4425 18.755 11.5125 18.895 ; + RECT 11.4425 18.4975 11.5125 18.6325 ; + RECT 11.8 14.2575 11.87 14.3925 ; + RECT 11.095 14.2575 11.165 14.3925 ; + RECT 11.6375 17.04 11.7075 17.175 ; + RECT 11.255 17.45 11.325 17.585 ; + RECT 11.4475 18.2925 11.5175 18.4275 ; + RECT 11.4475 16.15 11.5175 16.285 ; + RECT 11.4425 18.4975 11.5125 18.895 ; + RECT 11.4425 18.84 11.5125 18.895 ; + RECT 11.28 18.835 11.35 18.895 ; + RECT 11.62 18.845 11.68 18.895 ; + RECT 11.8 14.01 11.87 18.895 ; + RECT 11.615 14.81 11.685 18.895 ; + RECT 11.4475 16.285 11.5175 18.295 ; + RECT 11.615 14.01 11.685 17.555 ; + RECT 11.28 14.01 11.35 18.895 ; + RECT 11.095 14.01 11.165 18.895 ; + RECT 12.1475 18.755 12.2175 18.895 ; + RECT 12.1475 18.4975 12.2175 18.6325 ; + RECT 12.505 14.2575 12.575 14.3925 ; + RECT 11.8 14.2575 11.87 14.3925 ; + RECT 12.3425 17.04 12.4125 17.175 ; + RECT 11.96 17.45 12.03 17.585 ; + RECT 12.1525 18.2925 12.2225 18.4275 ; + RECT 12.1525 16.15 12.2225 16.285 ; + RECT 12.1475 18.4975 12.2175 18.895 ; + RECT 12.1475 18.84 12.2175 18.895 ; + RECT 11.985 18.835 12.055 18.895 ; + RECT 12.325 18.845 12.385 18.895 ; + RECT 12.505 14.01 12.575 18.895 ; + RECT 12.32 14.81 12.39 18.895 ; + RECT 12.1525 16.285 12.2225 18.295 ; + RECT 12.32 14.01 12.39 17.555 ; + RECT 11.985 14.01 12.055 18.895 ; + RECT 11.8 14.01 11.87 18.895 ; + RECT 11.3375 9.9675 11.4725 10.0375 ; + RECT 11.4475 9.835 11.5175 9.975 ; + RECT 11.8 11.57 11.87 11.705 ; + RECT 11.8 11.0 11.87 11.135 ; + RECT 11.095 11.0 11.165 11.135 ; + RECT 11.515 11.205 11.585 11.34 ; + RECT 11.8 13.4975 11.87 13.6325 ; + RECT 11.515 10.795 11.585 10.93 ; + RECT 11.375 10.795 11.445 10.93 ; + RECT 11.8 11.205 11.87 11.34 ; + RECT 11.095 11.205 11.165 11.34 ; + RECT 11.235 10.23 11.37 10.3 ; + RECT 11.375 13.175 11.445 13.31 ; + RECT 11.545 11.57 11.615 11.705 ; + RECT 11.235 12.485 11.305 12.62 ; + RECT 11.375 11.205 11.445 11.34 ; + RECT 11.095 11.57 11.165 11.705 ; + RECT 11.545 12.085 11.615 12.22 ; + RECT 11.28 13.7075 11.35 13.8425 ; + RECT 11.095 13.4975 11.165 13.6325 ; + RECT 11.28 13.71 11.35 14.01 ; + RECT 11.28 13.945 11.35 14.01 ; + RECT 11.6175 13.95 11.6825 14.01 ; + RECT 11.45 9.84 11.515 9.9025 ; + RECT 11.8 9.835 11.87 14.01 ; + RECT 11.095 9.835 11.165 14.01 ; + RECT 11.615 11.57 11.685 14.01 ; + RECT 11.325 9.9675 11.5175 10.0375 ; + RECT 11.515 10.93 11.585 11.205 ; + RECT 11.235 10.3 11.305 12.62 ; + RECT 11.375 10.93 11.445 11.205 ; + RECT 11.375 11.205 11.445 13.3025 ; + RECT 12.0425 9.9675 12.1775 10.0375 ; + RECT 12.1525 9.835 12.2225 9.975 ; + RECT 12.505 11.57 12.575 11.705 ; + RECT 12.505 11.0 12.575 11.135 ; + RECT 11.8 11.0 11.87 11.135 ; + RECT 12.22 11.205 12.29 11.34 ; + RECT 12.505 13.4975 12.575 13.6325 ; + RECT 12.22 10.795 12.29 10.93 ; + RECT 12.08 10.795 12.15 10.93 ; + RECT 12.505 11.205 12.575 11.34 ; + RECT 11.8 11.205 11.87 11.34 ; + RECT 11.94 10.23 12.075 10.3 ; + RECT 12.08 13.175 12.15 13.31 ; + RECT 12.25 11.57 12.32 11.705 ; + RECT 11.94 12.485 12.01 12.62 ; + RECT 12.08 11.205 12.15 11.34 ; + RECT 11.8 11.57 11.87 11.705 ; + RECT 12.25 12.085 12.32 12.22 ; + RECT 11.985 13.7075 12.055 13.8425 ; + RECT 11.8 13.4975 11.87 13.6325 ; + RECT 11.985 13.71 12.055 14.01 ; + RECT 11.985 13.945 12.055 14.01 ; + RECT 12.3225 13.95 12.3875 14.01 ; + RECT 12.155 9.84 12.22 9.9025 ; + RECT 12.505 9.835 12.575 14.01 ; + RECT 11.8 9.835 11.87 14.01 ; + RECT 12.32 11.57 12.39 14.01 ; + RECT 12.03 9.9675 12.2225 10.0375 ; + RECT 12.22 10.93 12.29 11.205 ; + RECT 11.94 10.3 12.01 12.62 ; + RECT 12.08 10.93 12.15 11.205 ; + RECT 12.08 11.205 12.15 13.3025 ; + RECT 11.2925 9.0975 11.3625 9.2325 ; + RECT 11.8 3.92 11.87 4.055 ; + RECT 11.6575 8.0025 11.7275 8.1375 ; + RECT 11.66 6.395 11.73 6.53 ; + RECT 11.66 4.9975 11.73 5.1325 ; + RECT 11.095 3.78 11.165 3.915 ; + RECT 11.4475 3.395 11.5175 3.535 ; + RECT 11.8 6.8575 11.87 6.9925 ; + RECT 11.295 5.6625 11.365 5.7975 ; + RECT 11.66 9.355 11.73 9.49 ; + RECT 11.4825 8.845 11.5525 8.98 ; + RECT 11.8 8.4325 11.87 8.5675 ; + RECT 11.8 3.73 11.87 3.865 ; + RECT 11.8 5.4725 11.87 5.6075 ; + RECT 11.295 5.2725 11.365 5.4075 ; + RECT 11.4825 5.2725 11.5525 5.4075 ; + RECT 11.4825 5.885 11.5525 6.02 ; + RECT 11.2925 8.2325 11.3625 8.3675 ; + RECT 11.4825 8.2325 11.5525 8.3675 ; + RECT 11.5175 9.565 11.66 9.635 ; + RECT 11.4475 3.395 11.5175 3.54 ; + RECT 11.5125 3.47 11.66 3.54 ; + RECT 11.4475 9.565 11.5175 9.835 ; + RECT 11.0975 9.3575 11.16 9.4175 ; + RECT 11.095 3.395 11.165 9.835 ; + RECT 11.45 9.77 11.515 9.83 ; + RECT 11.66 9.355 11.73 9.635 ; + RECT 11.66 3.47 11.73 5.1325 ; + RECT 11.2925 9.1475 11.3625 9.835 ; + RECT 11.66 6.53 11.73 8.1375 ; + RECT 11.4825 8.2325 11.5525 8.95 ; + RECT 11.295 5.2725 11.365 5.7975 ; + RECT 11.2975 9.6975 11.36 9.7575 ; + RECT 11.2925 8.2325 11.3625 9.1725 ; + RECT 11.4475 3.4 11.515 3.465 ; + RECT 11.8 3.395 11.87 9.835 ; + RECT 11.4825 5.2725 11.5525 5.99 ; + RECT 12.3075 9.0975 12.3775 9.2325 ; + RECT 11.8 3.92 11.87 4.055 ; + RECT 11.9425 8.0025 12.0125 8.1375 ; + RECT 11.94 6.395 12.01 6.53 ; + RECT 11.94 4.9975 12.01 5.1325 ; + RECT 12.505 3.78 12.575 3.915 ; + RECT 12.1525 3.395 12.2225 3.535 ; + RECT 11.8 6.8575 11.87 6.9925 ; + RECT 12.305 5.6625 12.375 5.7975 ; + RECT 11.94 9.355 12.01 9.49 ; + RECT 12.1175 8.845 12.1875 8.98 ; + RECT 11.8 8.4325 11.87 8.5675 ; + RECT 11.8 3.73 11.87 3.865 ; + RECT 11.8 5.4725 11.87 5.6075 ; + RECT 12.305 5.2725 12.375 5.4075 ; + RECT 12.1175 5.2725 12.1875 5.4075 ; + RECT 12.1175 5.885 12.1875 6.02 ; + RECT 12.3075 8.2325 12.3775 8.3675 ; + RECT 12.1175 8.2325 12.1875 8.3675 ; + RECT 12.01 9.565 12.1525 9.635 ; + RECT 12.1525 3.395 12.2225 3.54 ; + RECT 12.01 3.47 12.1575 3.54 ; + RECT 12.1525 9.565 12.2225 9.835 ; + RECT 12.51 9.3575 12.5725 9.4175 ; + RECT 12.505 3.395 12.575 9.835 ; + RECT 12.155 9.77 12.22 9.83 ; + RECT 11.94 9.355 12.01 9.635 ; + RECT 11.94 3.47 12.01 5.1325 ; + RECT 12.3075 9.1475 12.3775 9.835 ; + RECT 11.94 6.53 12.01 8.1375 ; + RECT 12.1175 8.2325 12.1875 8.95 ; + RECT 12.305 5.2725 12.375 5.7975 ; + RECT 12.31 9.6975 12.3725 9.7575 ; + RECT 12.3075 8.2325 12.3775 9.1725 ; + RECT 12.155 3.4 12.2225 3.465 ; + RECT 11.8 3.395 11.87 9.835 ; + RECT 12.1175 5.2725 12.1875 5.99 ; + RECT 11.415 5.1775 11.55 5.2475 ; + RECT 11.8 3.8925 11.87 4.0275 ; + RECT 11.4475 3.5475 11.5175 3.6875 ; + RECT 11.5475 5.8175 11.6825 5.8875 ; + RECT 11.8 5.9525 11.87 6.0875 ; + RECT 11.2725 5.7625 11.3425 5.8975 ; + RECT 11.48 5.485 11.55 5.62 ; + RECT 11.235 3.675 11.4475 3.745 ; + RECT 11.4475 3.395 11.5175 3.745 ; + RECT 11.5175 6.13 11.62 6.2 ; + RECT 11.4475 6.13 11.5175 6.37 ; + RECT 11.4475 6.3125 11.5175 6.365 ; + RECT 11.4475 3.55 11.5175 3.6125 ; + RECT 11.2375 4.31 11.3075 5.8975 ; + RECT 11.62 5.8175 11.69 6.2 ; + RECT 11.48 5.2475 11.55 5.485 ; + RECT 11.8 3.395 11.87 6.37 ; + RECT 11.8 3.9675 11.87 4.025 ; + RECT 11.5475 4.31 11.6825 4.38 ; + RECT 11.3075 4.31 11.5475 4.38 ; + RECT 11.235 3.675 11.305 4.045 ; + RECT 11.235 4.045 11.305 4.18 ; + RECT 12.12 5.1775 12.255 5.2475 ; + RECT 11.8 3.8925 11.87 4.0275 ; + RECT 12.1525 3.5475 12.2225 3.6875 ; + RECT 11.9875 5.8175 12.1225 5.8875 ; + RECT 11.8 5.9525 11.87 6.0875 ; + RECT 12.3275 5.7625 12.3975 5.8975 ; + RECT 12.12 5.485 12.19 5.62 ; + RECT 12.2225 3.675 12.435 3.745 ; + RECT 12.1525 3.395 12.2225 3.745 ; + RECT 12.05 6.13 12.1525 6.2 ; + RECT 12.1525 6.13 12.2225 6.37 ; + RECT 12.1525 6.3125 12.2225 6.365 ; + RECT 12.1525 3.55 12.2225 3.6125 ; + RECT 12.3625 4.31 12.4325 5.8975 ; + RECT 11.98 5.8175 12.05 6.2 ; + RECT 12.12 5.2475 12.19 5.485 ; + RECT 11.8 3.395 11.87 6.37 ; + RECT 11.8 3.9675 11.87 4.025 ; + RECT 11.9875 4.31 12.1225 4.38 ; + RECT 12.1225 4.31 12.3625 4.38 ; + RECT 12.365 3.675 12.435 4.045 ; + RECT 12.365 4.045 12.435 4.18 ; + RECT 4.045 8.135 4.115 40.415 ; + RECT 3.87 8.135 3.94 40.415 ; + RECT 3.695 8.135 3.765 40.415 ; + RECT 3.52 8.135 3.59 40.415 ; + RECT 3.345 8.135 3.415 40.415 ; + RECT 3.17 8.135 3.24 40.415 ; + RECT 2.995 8.135 3.065 40.415 ; + RECT 2.82 8.135 2.89 40.415 ; + RECT 7.525 8.135 7.595 13.305 ; + RECT 7.35 8.135 7.42 13.305 ; + RECT 6.51 8.135 6.58 13.305 ; + RECT 6.335 8.135 6.405 13.305 ; + RECT 6.16 8.135 6.23 13.305 ; + RECT 5.985 8.135 6.055 13.305 ; + RECT 5.81 8.135 5.88 13.305 ; + RECT 5.635 8.135 5.705 13.305 ; + RECT 6.125 9.18 6.26 9.25 ; + RECT 7.49 8.6575 7.625 8.7275 ; + RECT 5.95 9.57 6.085 9.64 ; + RECT 7.315 10.0925 7.45 10.1625 ; + RECT 6.125 8.9725 6.26 9.0425 ; + RECT 5.95 8.4425 6.085 8.5125 ; + RECT 5.775 9.7775 5.91 9.8475 ; + RECT 5.95 10.3075 6.085 10.3775 ; + RECT 6.125 11.6625 6.26 11.7325 ; + RECT 5.6 11.1325 5.735 11.2025 ; + RECT 5.775 12.4675 5.91 12.5375 ; + RECT 7.49 12.4675 7.625 12.5375 ; + RECT 5.6 12.9975 5.735 13.0675 ; + RECT 7.315 12.9975 7.45 13.0675 ; + RECT 6.3 8.03 6.435 8.1 ; + RECT 6.475 9.375 6.61 9.445 ; + RECT 6.3 10.72 6.435 10.79 ; + RECT 6.475 12.065 6.61 12.135 ; + RECT 6.3 13.2325 6.435 13.3025 ; + RECT 7.525 13.515 7.595 18.685 ; + RECT 7.35 13.515 7.42 18.685 ; + RECT 6.51 13.515 6.58 18.685 ; + RECT 6.335 13.515 6.405 18.685 ; + RECT 6.16 13.515 6.23 18.685 ; + RECT 5.985 13.515 6.055 18.685 ; + RECT 5.81 13.515 5.88 18.685 ; + RECT 5.635 13.515 5.705 18.685 ; + RECT 6.125 14.56 6.26 14.63 ; + RECT 7.49 14.0375 7.625 14.1075 ; + RECT 5.95 14.95 6.085 15.02 ; + RECT 7.315 15.4725 7.45 15.5425 ; + RECT 6.125 14.3525 6.26 14.4225 ; + RECT 5.95 13.8225 6.085 13.8925 ; + RECT 5.775 15.1575 5.91 15.2275 ; + RECT 5.95 15.6875 6.085 15.7575 ; + RECT 6.125 17.0425 6.26 17.1125 ; + RECT 5.6 16.5125 5.735 16.5825 ; + RECT 5.775 17.8475 5.91 17.9175 ; + RECT 7.49 17.8475 7.625 17.9175 ; + RECT 5.6 18.3775 5.735 18.4475 ; + RECT 7.315 18.3775 7.45 18.4475 ; + RECT 6.3 13.41 6.435 13.48 ; + RECT 6.475 14.755 6.61 14.825 ; + RECT 6.3 16.1 6.435 16.17 ; + RECT 6.475 17.445 6.61 17.515 ; + RECT 6.3 18.6125 6.435 18.6825 ; + RECT 2.79 8.7275 2.925 8.7975 ; + RECT 2.965 10.1625 3.1 10.2325 ; + RECT 3.14 11.4175 3.275 11.4875 ; + RECT 3.315 12.8525 3.45 12.9225 ; + RECT 3.49 14.1075 3.625 14.1775 ; + RECT 3.665 15.5425 3.8 15.6125 ; + RECT 3.84 16.7975 3.975 16.8675 ; + RECT 4.015 18.2325 4.15 18.3025 ; + RECT 2.79 19.8025 2.925 19.8725 ; + RECT 3.49 19.2725 3.625 19.3425 ; + RECT 2.79 20.6075 2.925 20.6775 ; + RECT 3.665 21.1375 3.8 21.2075 ; + RECT 2.79 22.4925 2.925 22.5625 ; + RECT 3.84 21.9625 3.975 22.0325 ; + RECT 2.79 23.2975 2.925 23.3675 ; + RECT 4.015 23.8275 4.15 23.8975 ; + RECT 2.965 25.1825 3.1 25.2525 ; + RECT 3.49 24.6525 3.625 24.7225 ; + RECT 2.965 25.9875 3.1 26.0575 ; + RECT 3.665 26.5175 3.8 26.5875 ; + RECT 2.965 27.8725 3.1 27.9425 ; + RECT 3.84 27.3425 3.975 27.4125 ; + RECT 2.965 28.6775 3.1 28.7475 ; + RECT 4.015 29.2075 4.15 29.2775 ; + RECT 3.14 30.5625 3.275 30.6325 ; + RECT 3.49 30.0325 3.625 30.1025 ; + RECT 3.14 31.3675 3.275 31.4375 ; + RECT 3.665 31.8975 3.8 31.9675 ; + RECT 3.14 33.2525 3.275 33.3225 ; + RECT 3.84 32.7225 3.975 32.7925 ; + RECT 3.14 34.0575 3.275 34.1275 ; + RECT 4.015 34.5875 4.15 34.6575 ; + RECT 3.315 35.9425 3.45 36.0125 ; + RECT 3.49 35.4125 3.625 35.4825 ; + RECT 3.315 36.7475 3.45 36.8175 ; + RECT 3.665 37.2775 3.8 37.3475 ; + RECT 3.315 38.6325 3.45 38.7025 ; + RECT 3.84 38.1025 3.975 38.1725 ; + RECT 3.315 39.4375 3.45 39.5075 ; + RECT 4.015 39.9675 4.15 40.0375 ; + RECT 5.53 20.205 5.985 20.275 ; + RECT 5.53 19.805 6.545 19.875 ; + RECT 5.53 21.55 5.985 21.62 ; + RECT 5.53 20.605 6.545 20.675 ; + RECT 5.53 22.895 5.985 22.965 ; + RECT 5.53 22.495 6.545 22.565 ; + RECT 5.53 24.24 5.985 24.31 ; + RECT 5.53 23.295 6.545 23.365 ; + RECT 5.53 25.585 5.985 25.655 ; + RECT 5.53 25.185 6.545 25.255 ; + RECT 5.53 26.93 5.985 27.0 ; + RECT 5.53 25.985 6.545 26.055 ; + RECT 5.53 28.275 5.985 28.345 ; + RECT 5.53 27.875 6.545 27.945 ; + RECT 5.53 29.62 5.985 29.69 ; + RECT 5.53 28.675 6.545 28.745 ; + RECT 5.53 30.965 5.985 31.035 ; + RECT 5.53 30.565 6.545 30.635 ; + RECT 5.53 32.31 5.985 32.38 ; + RECT 5.53 31.365 6.545 31.435 ; + RECT 5.53 33.655 5.985 33.725 ; + RECT 5.53 33.255 6.545 33.325 ; + RECT 5.53 35.0 5.985 35.07 ; + RECT 5.53 34.055 6.545 34.125 ; + RECT 5.53 36.345 5.985 36.415 ; + RECT 5.53 35.945 6.545 36.015 ; + RECT 5.53 37.69 5.985 37.76 ; + RECT 5.53 36.745 6.545 36.815 ; + RECT 5.53 39.035 5.985 39.105 ; + RECT 5.53 38.635 6.545 38.705 ; + RECT 5.53 40.38 5.985 40.45 ; + RECT 5.53 39.435 6.545 39.505 ; + RECT 5.46 20.205 5.595 20.275 ; + RECT 5.915 20.205 6.05 20.275 ; + RECT 6.545 19.805 6.68 19.875 ; + RECT 5.53 19.805 5.6 19.94 ; + RECT 5.46 21.55 5.595 21.62 ; + RECT 5.915 21.55 6.05 21.62 ; + RECT 6.545 20.605 6.68 20.675 ; + RECT 5.53 20.54 5.6 20.675 ; + RECT 5.46 22.895 5.595 22.965 ; + RECT 5.915 22.895 6.05 22.965 ; + RECT 6.545 22.495 6.68 22.565 ; + RECT 5.53 22.495 5.6 22.63 ; + RECT 5.46 24.24 5.595 24.31 ; + RECT 5.915 24.24 6.05 24.31 ; + RECT 6.545 23.295 6.68 23.365 ; + RECT 5.53 23.23 5.6 23.365 ; + RECT 5.46 25.585 5.595 25.655 ; + RECT 5.915 25.585 6.05 25.655 ; + RECT 6.545 25.185 6.68 25.255 ; + RECT 5.53 25.185 5.6 25.32 ; + RECT 5.46 26.93 5.595 27.0 ; + RECT 5.915 26.93 6.05 27.0 ; + RECT 6.545 25.985 6.68 26.055 ; + RECT 5.53 25.92 5.6 26.055 ; + RECT 5.46 28.275 5.595 28.345 ; + RECT 5.915 28.275 6.05 28.345 ; + RECT 6.545 27.875 6.68 27.945 ; + RECT 5.53 27.875 5.6 28.01 ; + RECT 5.46 29.62 5.595 29.69 ; + RECT 5.915 29.62 6.05 29.69 ; + RECT 6.545 28.675 6.68 28.745 ; + RECT 5.53 28.61 5.6 28.745 ; + RECT 5.46 30.965 5.595 31.035 ; + RECT 5.915 30.965 6.05 31.035 ; + RECT 6.545 30.565 6.68 30.635 ; + RECT 5.53 30.565 5.6 30.7 ; + RECT 5.46 32.31 5.595 32.38 ; + RECT 5.915 32.31 6.05 32.38 ; + RECT 6.545 31.365 6.68 31.435 ; + RECT 5.53 31.3 5.6 31.435 ; + RECT 5.46 33.655 5.595 33.725 ; + RECT 5.915 33.655 6.05 33.725 ; + RECT 6.545 33.255 6.68 33.325 ; + RECT 5.53 33.255 5.6 33.39 ; + RECT 5.46 35.0 5.595 35.07 ; + RECT 5.915 35.0 6.05 35.07 ; + RECT 6.545 34.055 6.68 34.125 ; + RECT 5.53 33.99 5.6 34.125 ; + RECT 5.46 36.345 5.595 36.415 ; + RECT 5.915 36.345 6.05 36.415 ; + RECT 6.545 35.945 6.68 36.015 ; + RECT 5.53 35.945 5.6 36.08 ; + RECT 5.46 37.69 5.595 37.76 ; + RECT 5.915 37.69 6.05 37.76 ; + RECT 6.545 36.745 6.68 36.815 ; + RECT 5.53 36.68 5.6 36.815 ; + RECT 5.46 39.035 5.595 39.105 ; + RECT 5.915 39.035 6.05 39.105 ; + RECT 6.545 38.635 6.68 38.705 ; + RECT 5.53 38.635 5.6 38.77 ; + RECT 5.46 40.38 5.595 40.45 ; + RECT 5.915 40.38 6.05 40.45 ; + RECT 6.545 39.435 6.68 39.505 ; + RECT 5.53 39.37 5.6 39.505 ; + RECT 6.6125 7.6075 6.7475 7.6775 ; + RECT 1.435 7.1 1.57 7.17 ; + RECT 5.5175 7.2425 5.6525 7.3125 ; + RECT 3.91 7.24 4.045 7.31 ; + RECT 2.5125 7.24 2.6475 7.31 ; + RECT 1.295 7.805 1.43 7.875 ; + RECT 0.91 7.4525 1.05 7.5225 ; + RECT 4.3725 7.1 4.5075 7.17 ; + RECT 3.1775 7.605 3.3125 7.675 ; + RECT 6.87 7.24 7.005 7.31 ; + RECT 6.36 7.4175 6.495 7.4875 ; + RECT 5.9475 7.1 6.0825 7.17 ; + RECT 1.245 7.1 1.38 7.17 ; + RECT 2.9875 7.1 3.1225 7.17 ; + RECT 2.7875 7.605 2.9225 7.675 ; + RECT 2.7875 7.4175 2.9225 7.4875 ; + RECT 3.4 7.4175 3.535 7.4875 ; + RECT 5.7475 7.6075 5.8825 7.6775 ; + RECT 5.7475 7.4175 5.8825 7.4875 ; + RECT 7.08 7.31 7.15 7.4525 ; + RECT 0.91 7.4525 1.055 7.5225 ; + RECT 0.985 7.31 1.055 7.4575 ; + RECT 7.08 7.4525 7.35 7.5225 ; + RECT 6.8725 7.81 6.9325 7.8725 ; + RECT 0.91 7.805 7.35 7.875 ; + RECT 7.285 7.455 7.345 7.52 ; + RECT 6.87 7.24 7.15 7.31 ; + RECT 0.985 7.24 2.6475 7.31 ; + RECT 6.6625 7.6075 7.35 7.6775 ; + RECT 4.045 7.24 5.6525 7.31 ; + RECT 5.7475 7.4175 6.465 7.4875 ; + RECT 2.7875 7.605 3.3125 7.675 ; + RECT 7.2125 7.61 7.2725 7.6725 ; + RECT 5.7475 7.6075 6.6875 7.6775 ; + RECT 0.915 7.455 0.98 7.5225 ; + RECT 0.91 7.1 7.35 7.17 ; + RECT 2.7875 7.4175 3.505 7.4875 ; + RECT 6.6125 6.5925 6.7475 6.6625 ; + RECT 1.435 7.1 1.57 7.17 ; + RECT 5.5175 6.9575 5.6525 7.0275 ; + RECT 3.91 6.96 4.045 7.03 ; + RECT 2.5125 6.96 2.6475 7.03 ; + RECT 1.295 6.395 1.43 6.465 ; + RECT 0.91 6.7475 1.05 6.8175 ; + RECT 4.3725 7.1 4.5075 7.17 ; + RECT 3.1775 6.595 3.3125 6.665 ; + RECT 6.87 6.96 7.005 7.03 ; + RECT 6.36 6.7825 6.495 6.8525 ; + RECT 5.9475 7.1 6.0825 7.17 ; + RECT 1.245 7.1 1.38 7.17 ; + RECT 2.9875 7.1 3.1225 7.17 ; + RECT 2.7875 6.595 2.9225 6.665 ; + RECT 2.7875 6.7825 2.9225 6.8525 ; + RECT 3.4 6.7825 3.535 6.8525 ; + RECT 5.7475 6.5925 5.8825 6.6625 ; + RECT 5.7475 6.7825 5.8825 6.8525 ; + RECT 7.08 6.8175 7.15 6.96 ; + RECT 0.91 6.7475 1.055 6.8175 ; + RECT 0.985 6.8125 1.055 6.96 ; + RECT 7.08 6.7475 7.35 6.8175 ; + RECT 6.8725 6.3975 6.9325 6.46 ; + RECT 0.91 6.395 7.35 6.465 ; + RECT 7.285 6.75 7.345 6.815 ; + RECT 6.87 6.96 7.15 7.03 ; + RECT 0.985 6.96 2.6475 7.03 ; + RECT 6.6625 6.5925 7.35 6.6625 ; + RECT 4.045 6.96 5.6525 7.03 ; + RECT 5.7475 6.7825 6.465 6.8525 ; + RECT 2.7875 6.595 3.3125 6.665 ; + RECT 7.2125 6.5975 7.2725 6.66 ; + RECT 5.7475 6.5925 6.6875 6.6625 ; + RECT 0.915 6.7475 0.98 6.815 ; + RECT 0.91 7.1 7.35 7.17 ; + RECT 2.7875 6.7825 3.505 6.8525 ; + RECT 6.6125 6.1975 6.7475 6.2675 ; + RECT 1.435 5.69 1.57 5.76 ; + RECT 5.5175 5.8325 5.6525 5.9025 ; + RECT 3.91 5.83 4.045 5.9 ; + RECT 2.5125 5.83 2.6475 5.9 ; + RECT 1.295 6.395 1.43 6.465 ; + RECT 0.91 6.0425 1.05 6.1125 ; + RECT 4.3725 5.69 4.5075 5.76 ; + RECT 3.1775 6.195 3.3125 6.265 ; + RECT 6.87 5.83 7.005 5.9 ; + RECT 6.36 6.0075 6.495 6.0775 ; + RECT 5.9475 5.69 6.0825 5.76 ; + RECT 1.245 5.69 1.38 5.76 ; + RECT 2.9875 5.69 3.1225 5.76 ; + RECT 2.7875 6.195 2.9225 6.265 ; + RECT 2.7875 6.0075 2.9225 6.0775 ; + RECT 3.4 6.0075 3.535 6.0775 ; + RECT 5.7475 6.1975 5.8825 6.2675 ; + RECT 5.7475 6.0075 5.8825 6.0775 ; + RECT 7.08 5.9 7.15 6.0425 ; + RECT 0.91 6.0425 1.055 6.1125 ; + RECT 0.985 5.9 1.055 6.0475 ; + RECT 7.08 6.0425 7.35 6.1125 ; + RECT 6.8725 6.4 6.9325 6.4625 ; + RECT 0.91 6.395 7.35 6.465 ; + RECT 7.285 6.045 7.345 6.11 ; + RECT 6.87 5.83 7.15 5.9 ; + RECT 0.985 5.83 2.6475 5.9 ; + RECT 6.6625 6.1975 7.35 6.2675 ; + RECT 4.045 5.83 5.6525 5.9 ; + RECT 5.7475 6.0075 6.465 6.0775 ; + RECT 2.7875 6.195 3.3125 6.265 ; + RECT 7.2125 6.2 7.2725 6.2625 ; + RECT 5.7475 6.1975 6.6875 6.2675 ; + RECT 0.915 6.045 0.98 6.1125 ; + RECT 0.91 5.69 7.35 5.76 ; + RECT 2.7875 6.0075 3.505 6.0775 ; + RECT 6.6125 5.1825 6.7475 5.2525 ; + RECT 1.435 5.69 1.57 5.76 ; + RECT 5.5175 5.5475 5.6525 5.6175 ; + RECT 3.91 5.55 4.045 5.62 ; + RECT 2.5125 5.55 2.6475 5.62 ; + RECT 1.295 4.985 1.43 5.055 ; + RECT 0.91 5.3375 1.05 5.4075 ; + RECT 4.3725 5.69 4.5075 5.76 ; + RECT 3.1775 5.185 3.3125 5.255 ; + RECT 6.87 5.55 7.005 5.62 ; + RECT 6.36 5.3725 6.495 5.4425 ; + RECT 5.9475 5.69 6.0825 5.76 ; + RECT 1.245 5.69 1.38 5.76 ; + RECT 2.9875 5.69 3.1225 5.76 ; + RECT 2.7875 5.185 2.9225 5.255 ; + RECT 2.7875 5.3725 2.9225 5.4425 ; + RECT 3.4 5.3725 3.535 5.4425 ; + RECT 5.7475 5.1825 5.8825 5.2525 ; + RECT 5.7475 5.3725 5.8825 5.4425 ; + RECT 7.08 5.4075 7.15 5.55 ; + RECT 0.91 5.3375 1.055 5.4075 ; + RECT 0.985 5.4025 1.055 5.55 ; + RECT 7.08 5.3375 7.35 5.4075 ; + RECT 6.8725 4.9875 6.9325 5.05 ; + RECT 0.91 4.985 7.35 5.055 ; + RECT 7.285 5.34 7.345 5.405 ; + RECT 6.87 5.55 7.15 5.62 ; + RECT 0.985 5.55 2.6475 5.62 ; + RECT 6.6625 5.1825 7.35 5.2525 ; + RECT 4.045 5.55 5.6525 5.62 ; + RECT 5.7475 5.3725 6.465 5.4425 ; + RECT 2.7875 5.185 3.3125 5.255 ; + RECT 7.2125 5.1875 7.2725 5.25 ; + RECT 5.7475 5.1825 6.6875 5.2525 ; + RECT 0.915 5.3375 0.98 5.405 ; + RECT 0.91 5.69 7.35 5.76 ; + RECT 2.7875 5.3725 3.505 5.4425 ; + RECT 11.4475 0.2775 11.5175 0.3475 ; + RECT 11.2725 0.2775 11.4825 0.3475 ; + RECT 11.4475 0.3125 11.5175 0.54 ; + RECT 11.2375 0.2775 11.3075 0.4125 ; + RECT 12.1525 0.2775 12.2225 0.3475 ; + RECT 11.9775 0.2775 12.1875 0.3475 ; + RECT 12.1525 0.3125 12.2225 0.54 ; + RECT 11.9425 0.2775 12.0125 0.4125 ; + RECT 11.4475 0.0 11.5175 0.135 ; + RECT 12.1525 0.0 12.2225 0.135 ; + RECT 8.54 8.73 8.61 8.865 ; + RECT 8.54 7.4525 8.61 7.5875 ; + RECT 7.08 7.4525 7.215 7.5225 ; + RECT 8.33 10.165 8.4 10.3 ; + RECT 8.33 6.7475 8.4 6.8825 ; + RECT 7.08 6.7475 7.215 6.8175 ; + RECT 8.12 14.11 8.19 14.245 ; + RECT 8.12 6.0425 8.19 6.1775 ; + RECT 7.08 6.0425 7.215 6.1125 ; + RECT 7.91 15.545 7.98 15.68 ; + RECT 7.91 5.3375 7.98 5.4725 ; + RECT 7.08 5.3375 7.215 5.4075 ; + RECT 10.36 3.6 10.43 3.735 ; + RECT 9.94 1.415 10.01 1.55 ; + RECT 10.15 2.9625 10.22 3.0975 ; + RECT 10.36 41.1 10.43 41.235 ; + RECT 10.57 10.1025 10.64 10.2375 ; + RECT 10.78 14.1275 10.85 14.2625 ; + RECT 0.98 7.6275 1.115 7.6975 ; + RECT 0.98 7.6275 1.115 7.6975 ; + RECT 9.73 7.98 9.8 8.115 ; + RECT 9.73 41.53 9.8 41.665 ; + RECT 8.89 40.545 9.095 40.68 ; + RECT 11.77 40.545 11.905 40.615 ; + RECT 12.475 40.545 12.61 40.615 ; + RECT 10.995 40.545 11.13 40.615 ; + RECT 9.385 0.355 9.59 0.49 ; + RECT 11.8 0.355 11.87 0.49 ; + RECT 11.8 0.355 11.87 0.49 ; + RECT 7.755 21.5525 7.89 21.6225 ; + RECT 7.755 24.2425 7.89 24.3125 ; + RECT 7.755 26.9325 7.89 27.0025 ; + RECT 7.755 29.6225 7.89 29.6925 ; + RECT 7.755 32.3125 7.89 32.3825 ; + RECT 7.755 35.0025 7.89 35.0725 ; + RECT 7.755 37.6925 7.89 37.7625 ; + RECT 7.755 40.3825 7.89 40.4525 ; + RECT 8.89 13.4125 9.095 13.5475 ; + RECT 8.89 18.7925 9.095 18.9275 ; + RECT 7.28 7.8075 7.415 7.8775 ; + RECT 8.89 7.8075 9.095 7.9425 ; + RECT 7.28 6.3975 7.415 6.4675 ; + RECT 8.89 6.3975 9.095 6.5325 ; + RECT 7.28 6.3975 7.415 6.4675 ; + RECT 8.89 6.3975 9.095 6.5325 ; + RECT 7.28 4.9875 7.415 5.0575 ; + RECT 8.89 4.9875 9.095 5.1225 ; + RECT -5.73 14.925 -0.35 14.995 ; + RECT -5.73 15.135 -0.35 15.205 ; + RECT -5.73 15.345 -0.35 15.415 ; + RECT -5.73 15.555 -0.35 15.625 ; + RECT -5.73 15.765 -0.35 15.835 ; + RECT -5.73 15.975 -0.35 16.045 ; + RECT -5.73 19.225 -0.35 19.295 ; + RECT -5.73 19.435 -0.35 19.505 ; + RECT -5.73 19.645 -0.35 19.715 ; + RECT -5.73 19.855 -0.35 19.925 ; + RECT -5.765 14.715 -5.695 14.995 ; + RECT -4.355 14.715 -4.285 14.995 ; + RECT -4.355 14.715 -4.285 14.995 ; + RECT -0.35 15.975 -0.14 16.045 ; + RECT -0.8675 8.415 -0.14 8.485 ; + RECT -1.0125 16.865 -0.14 16.935 ; + RECT -0.35 19.225 -0.14 19.295 ; + RECT -0.965 17.335 -0.14 17.405 ; + RECT -5.5675 13.9775 -5.4975 14.1125 ; + RECT -5.06 8.8 -4.99 8.935 ; + RECT -5.2025 12.8825 -5.1325 13.0175 ; + RECT -5.2 11.275 -5.13 11.41 ; + RECT -5.2 9.8775 -5.13 10.0125 ; + RECT -5.765 8.66 -5.695 8.795 ; + RECT -5.4125 8.275 -5.3425 8.415 ; + RECT -5.06 11.7375 -4.99 11.8725 ; + RECT -5.565 10.5425 -5.495 10.6775 ; + RECT -5.2 14.235 -5.13 14.37 ; + RECT -5.3775 13.725 -5.3075 13.86 ; + RECT -5.06 13.3125 -4.99 13.4475 ; + RECT -5.06 8.61 -4.99 8.745 ; + RECT -5.06 10.3525 -4.99 10.4875 ; + RECT -5.565 10.1525 -5.495 10.2875 ; + RECT -5.3775 10.1525 -5.3075 10.2875 ; + RECT -5.3775 10.765 -5.3075 10.9 ; + RECT -5.5675 13.1125 -5.4975 13.2475 ; + RECT -5.3775 13.1125 -5.3075 13.2475 ; + RECT -5.3425 14.445 -5.2 14.515 ; + RECT -5.4125 8.275 -5.3425 8.42 ; + RECT -5.3475 8.35 -5.2 8.42 ; + RECT -5.4125 14.445 -5.3425 14.715 ; + RECT -5.7625 14.2375 -5.7 14.2975 ; + RECT -5.765 8.275 -5.695 14.715 ; + RECT -5.41 14.65 -5.345 14.71 ; + RECT -5.2 14.235 -5.13 14.515 ; + RECT -5.2 8.35 -5.13 10.0125 ; + RECT -5.5675 14.0275 -5.4975 14.715 ; + RECT -5.2 11.41 -5.13 13.0175 ; + RECT -5.3775 13.1125 -5.3075 13.83 ; + RECT -5.565 10.1525 -5.495 10.6775 ; + RECT -5.5625 14.5775 -5.5 14.6375 ; + RECT -5.5675 13.1125 -5.4975 14.0525 ; + RECT -5.4125 8.28 -5.345 8.345 ; + RECT -5.06 8.275 -4.99 14.715 ; + RECT -5.3775 10.1525 -5.3075 10.87 ; + RECT -4.5525 13.9775 -4.4825 14.1125 ; + RECT -5.06 8.8 -4.99 8.935 ; + RECT -4.9175 12.8825 -4.8475 13.0175 ; + RECT -4.92 11.275 -4.85 11.41 ; + RECT -4.92 9.8775 -4.85 10.0125 ; + RECT -4.355 8.66 -4.285 8.795 ; + RECT -4.7075 8.275 -4.6375 8.415 ; + RECT -5.06 11.7375 -4.99 11.8725 ; + RECT -4.555 10.5425 -4.485 10.6775 ; + RECT -4.92 14.235 -4.85 14.37 ; + RECT -4.7425 13.725 -4.6725 13.86 ; + RECT -5.06 13.3125 -4.99 13.4475 ; + RECT -5.06 8.61 -4.99 8.745 ; + RECT -5.06 10.3525 -4.99 10.4875 ; + RECT -4.555 10.1525 -4.485 10.2875 ; + RECT -4.7425 10.1525 -4.6725 10.2875 ; + RECT -4.7425 10.765 -4.6725 10.9 ; + RECT -4.5525 13.1125 -4.4825 13.2475 ; + RECT -4.7425 13.1125 -4.6725 13.2475 ; + RECT -4.85 14.445 -4.7075 14.515 ; + RECT -4.7075 8.275 -4.6375 8.42 ; + RECT -4.85 8.35 -4.7025 8.42 ; + RECT -4.7075 14.445 -4.6375 14.715 ; + RECT -4.35 14.2375 -4.2875 14.2975 ; + RECT -4.355 8.275 -4.285 14.715 ; + RECT -4.705 14.65 -4.64 14.71 ; + RECT -4.92 14.235 -4.85 14.515 ; + RECT -4.92 8.35 -4.85 10.0125 ; + RECT -4.5525 14.0275 -4.4825 14.715 ; + RECT -4.92 11.41 -4.85 13.0175 ; + RECT -4.7425 13.1125 -4.6725 13.83 ; + RECT -4.555 10.1525 -4.485 10.6775 ; + RECT -4.55 14.5775 -4.4875 14.6375 ; + RECT -4.5525 13.1125 -4.4825 14.0525 ; + RECT -4.705 8.28 -4.6375 8.345 ; + RECT -5.06 8.275 -4.99 14.715 ; + RECT -4.7425 10.1525 -4.6725 10.87 ; + RECT -4.1575 13.9775 -4.0875 14.1125 ; + RECT -3.65 8.8 -3.58 8.935 ; + RECT -3.7925 12.8825 -3.7225 13.0175 ; + RECT -3.79 11.275 -3.72 11.41 ; + RECT -3.79 9.8775 -3.72 10.0125 ; + RECT -4.355 8.66 -4.285 8.795 ; + RECT -4.0025 8.275 -3.9325 8.415 ; + RECT -3.65 11.7375 -3.58 11.8725 ; + RECT -4.155 10.5425 -4.085 10.6775 ; + RECT -3.79 14.235 -3.72 14.37 ; + RECT -3.9675 13.725 -3.8975 13.86 ; + RECT -3.65 13.3125 -3.58 13.4475 ; + RECT -3.65 8.61 -3.58 8.745 ; + RECT -3.65 10.3525 -3.58 10.4875 ; + RECT -4.155 10.1525 -4.085 10.2875 ; + RECT -3.9675 10.1525 -3.8975 10.2875 ; + RECT -3.9675 10.765 -3.8975 10.9 ; + RECT -4.1575 13.1125 -4.0875 13.2475 ; + RECT -3.9675 13.1125 -3.8975 13.2475 ; + RECT -3.9325 14.445 -3.79 14.515 ; + RECT -4.0025 8.275 -3.9325 8.42 ; + RECT -3.9375 8.35 -3.79 8.42 ; + RECT -4.0025 14.445 -3.9325 14.715 ; + RECT -4.3525 14.2375 -4.29 14.2975 ; + RECT -4.355 8.275 -4.285 14.715 ; + RECT -4.0 14.65 -3.935 14.71 ; + RECT -3.79 14.235 -3.72 14.515 ; + RECT -3.79 8.35 -3.72 10.0125 ; + RECT -4.1575 14.0275 -4.0875 14.715 ; + RECT -3.79 11.41 -3.72 13.0175 ; + RECT -3.9675 13.1125 -3.8975 13.83 ; + RECT -4.155 10.1525 -4.085 10.6775 ; + RECT -4.1525 14.5775 -4.09 14.6375 ; + RECT -4.1575 13.1125 -4.0875 14.0525 ; + RECT -4.0025 8.28 -3.935 8.345 ; + RECT -3.65 8.275 -3.58 14.715 ; + RECT -3.9675 10.1525 -3.8975 10.87 ; + RECT -1.4125 8.84 -1.2775 8.91 ; + RECT -0.8675 8.84 -0.8025 8.91 ; + RECT -0.835 8.84 -0.2575 8.91 ; + RECT -0.87 8.8075 -0.8 8.9425 ; + RECT -1.9775 21.245 -1.9075 21.38 ; + RECT -1.0025 23.795 -0.9325 23.93 ; + RECT -1.0025 23.235 -0.9325 23.37 ; + RECT -2.5225 22.81 -2.4525 22.945 ; + RECT -2.5225 23.37 -2.4525 23.505 ; + RECT -1.0025 23.4725 -0.9325 23.6075 ; + RECT -1.0025 23.37 -0.9325 23.6075 ; + RECT -1.0025 22.9125 -0.9325 23.0475 ; + RECT -1.0025 22.81 -0.9325 22.88 ; + RECT -2.5225 22.81 -2.4525 22.88 ; + RECT -1.0025 22.845 -0.9325 23.0475 ; + RECT -2.5225 22.81 -2.4525 22.845 ; + RECT -1.0025 22.7775 -0.9325 22.9125 ; + RECT -2.5225 22.7775 -2.4525 22.9125 ; + RECT -2.5225 23.1325 -2.4525 23.2675 ; + RECT -2.5225 23.1325 -2.4525 23.37 ; + RECT -4.2275 23.0025 -4.1575 23.1375 ; + RECT -4.9325 23.4775 -4.8625 23.6125 ; + RECT -4.2275 23.4775 -4.1575 23.6125 ; + RECT -4.68 22.9975 -4.61 23.1325 ; + RECT -4.48 23.0025 -4.41 23.1375 ; + RECT -4.965 22.705 -4.83 22.775 ; + RECT -4.26 22.705 -4.125 22.775 ; + RECT -4.7475 22.7075 -4.6775 22.7725 ; + RECT -4.4125 22.7075 -4.3425 22.7725 ; + RECT -4.2275 22.705 -4.1575 22.775 ; + RECT -4.4125 22.64 -4.3425 24.1975 ; + RECT -4.7475 22.64 -4.6775 24.205 ; + RECT -4.9325 22.64 -4.8625 24.2075 ; + RECT -4.2275 22.64 -4.1575 24.185 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.26 22.705 -4.125 22.775 ; + RECT -4.965 22.705 -4.83 22.775 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.4825 22.3475 -4.4125 22.4825 ; + RECT -4.2275 21.855 -4.1575 21.99 ; + RECT -4.2275 21.855 -4.1575 21.99 ; + RECT -4.2275 21.855 -4.1575 21.99 ; + RECT -4.2275 21.855 -4.1575 21.99 ; + RECT -4.2275 21.855 -4.1575 21.99 ; + RECT -4.2275 21.855 -4.1575 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.6775 22.3475 -4.6075 22.4825 ; + RECT -4.965 22.705 -4.83 22.775 ; + RECT -4.965 22.705 -4.83 22.775 ; + RECT -4.965 22.705 -4.83 22.775 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.9325 21.855 -4.8625 21.99 ; + RECT -4.26 22.705 -4.125 22.775 ; + RECT -4.9325 21.3625 -4.8625 22.7725 ; + RECT -4.2225 22.7125 -4.1625 22.77 ; + RECT -4.405 22.7125 -4.35 22.765 ; + RECT -4.7425 22.7125 -4.685 22.7725 ; + RECT -4.9275 22.7075 -4.8675 22.765 ; + RECT -4.7475 21.295 -4.6775 22.84 ; + RECT -4.4125 21.295 -4.3425 22.84 ; + RECT -4.2275 21.295 -4.1575 22.84 ; + RECT -4.9325 21.295 -4.8625 22.84 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.26 20.015 -4.125 20.085 ; + RECT -4.965 20.015 -4.83 20.085 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.4825 20.3075 -4.4125 20.4425 ; + RECT -4.2275 20.8 -4.1575 20.935 ; + RECT -4.2275 20.8 -4.1575 20.935 ; + RECT -4.2275 20.8 -4.1575 20.935 ; + RECT -4.2275 20.8 -4.1575 20.935 ; + RECT -4.2275 20.8 -4.1575 20.935 ; + RECT -4.2275 20.8 -4.1575 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.6775 20.3075 -4.6075 20.4425 ; + RECT -4.965 20.015 -4.83 20.085 ; + RECT -4.965 20.015 -4.83 20.085 ; + RECT -4.965 20.015 -4.83 20.085 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.9325 20.8 -4.8625 20.935 ; + RECT -4.26 20.015 -4.125 20.085 ; + RECT -4.9325 20.0175 -4.8625 21.4275 ; + RECT -4.2225 20.02 -4.1625 20.0775 ; + RECT -4.405 20.025 -4.35 20.0775 ; + RECT -4.7425 20.0175 -4.685 20.0775 ; + RECT -4.9275 20.025 -4.8675 20.0825 ; + RECT -4.7475 19.95 -4.6775 21.495 ; + RECT -4.4125 19.95 -4.3425 21.495 ; + RECT -4.2275 19.95 -4.1575 21.495 ; + RECT -4.9325 19.95 -4.8625 21.495 ; + RECT -1.9775 21.1425 -1.9075 21.2775 ; + RECT -1.38 21.3825 -1.315 21.4525 ; + RECT -2.135 21.3825 -2.07 21.4525 ; + RECT -2.1025 21.3825 -1.3475 21.4525 ; + RECT -1.415 21.3825 -1.28 21.4525 ; + RECT -2.17 21.3825 -2.035 21.4525 ; + RECT -4.4125 24.28 -4.3425 24.345 ; + RECT -4.4125 22.74 -4.3425 24.315 ; + RECT -4.445 24.2775 -4.31 24.3475 ; + RECT -0.65 19.52 -0.58 19.585 ; + RECT -2.5575 19.5225 -2.4875 19.5875 ; + RECT -0.65 19.555 -0.58 21.5325 ; + RECT -2.5575 18.8975 -2.4875 19.555 ; + RECT -0.6825 19.5175 -0.5475 19.5875 ; + RECT -2.59 19.52 -2.455 19.59 ; + RECT -2.5925 24.1675 -2.5225 24.3025 ; + RECT -0.65 21.33 -0.58 21.465 ; + RECT -0.65 22.335 -0.58 22.405 ; + RECT -4.0275 22.335 -3.9575 22.405 ; + RECT -0.65 21.5325 -0.58 22.37 ; + RECT -2.1025 22.335 -0.615 22.405 ; + RECT -3.9925 22.335 -2.1025 22.405 ; + RECT -4.0275 20.465 -3.9575 22.37 ; + RECT -4.0275 22.3975 -3.9575 22.5325 ; + RECT -3.7375 22.0075 -3.6675 22.0725 ; + RECT -3.7375 17.8475 -3.6675 22.04 ; + RECT -3.77 22.005 -3.635 22.075 ; + RECT -3.7375 23.7675 -3.6675 23.8325 ; + RECT -3.7375 21.3675 -3.6675 23.8 ; + RECT -3.77 23.765 -3.635 23.835 ; + RECT -0.4175 20.6625 -0.3475 20.7275 ; + RECT -0.4175 16.165 -0.3475 16.23 ; + RECT -3.1075 16.1675 -3.0375 16.2325 ; + RECT -0.4175 16.2 -0.3475 20.695 ; + RECT -3.1075 16.2 -3.0375 17.46 ; + RECT -0.45 20.66 -0.315 20.73 ; + RECT -0.45 16.1625 -0.315 16.2325 ; + RECT -3.14 16.165 -3.005 16.235 ; + RECT -3.7725 26.2325 -3.7025 26.3675 ; + RECT -3.5275 23.93 -3.4575 23.995 ; + RECT -3.5275 25.01 -3.4575 25.075 ; + RECT -3.5275 23.9625 -3.4575 25.045 ; + RECT -3.56 23.9275 -3.425 23.9975 ; + RECT -3.56 25.0075 -3.425 25.0775 ; + RECT -3.7375 28.345 -3.6675 28.41 ; + RECT -3.7375 26.2325 -3.6675 28.38 ; + RECT -3.77 28.3425 -3.635 28.4125 ; + RECT -3.5275 23.93 -3.4575 23.995 ; + RECT -3.5275 22.6 -3.4575 22.665 ; + RECT -3.5275 22.635 -3.4575 23.9625 ; + RECT -3.56 23.9275 -3.425 23.9975 ; + RECT -3.56 22.5975 -3.425 22.6675 ; + RECT -3.7375 31.035 -3.6675 31.1 ; + RECT -3.7375 26.2325 -3.6675 31.07 ; + RECT -3.77 31.0325 -3.635 31.1025 ; + RECT -4.9325 20.6625 -4.8625 20.7275 ; + RECT -4.9325 15.96 -4.8625 20.695 ; + RECT -4.965 20.66 -4.83 20.73 ; + RECT -4.2275 20.6625 -4.1575 20.7275 ; + RECT -4.2275 15.96 -4.1575 20.695 ; + RECT -4.26 20.66 -4.125 20.73 ; + RECT -1.9775 16.605 -1.9075 17.055 ; + RECT -2.6475 16.255 -2.5775 16.73 ; + RECT -2.7875 16.255 -2.7175 16.92 ; + RECT -2.7175 16.85 -2.5775 16.92 ; + RECT -2.1125 16.605 -1.9775 16.675 ; + RECT -2.1125 16.985 -1.9775 17.055 ; + RECT -2.7125 16.66 -2.5775 16.73 ; + RECT -2.7175 16.215 -2.6475 16.35 ; + RECT -2.7125 16.85 -2.5775 16.92 ; + RECT -2.8575 16.215 -2.7875 16.35 ; + RECT -4.1725 16.605 -4.1025 17.055 ; + RECT -3.5025 16.255 -3.4325 16.73 ; + RECT -3.3625 16.255 -3.2925 16.92 ; + RECT -3.5025 16.85 -3.3625 16.92 ; + RECT -4.2375 16.605 -4.1025 16.675 ; + RECT -4.2375 16.985 -4.1025 17.055 ; + RECT -3.6375 16.66 -3.5025 16.73 ; + RECT -3.5025 16.215 -3.4325 16.35 ; + RECT -3.6375 16.85 -3.5025 16.92 ; + RECT -3.3625 16.215 -3.2925 16.35 ; + RECT -4.0 14.615 -3.93 14.75 ; + RECT -4.0 15.105 -3.93 15.24 ; + RECT -4.155 14.615 -4.085 14.75 ; + RECT -4.155 15.315 -4.085 15.45 ; + RECT -5.565 14.615 -5.495 14.75 ; + RECT -5.565 15.525 -5.495 15.66 ; + RECT -4.55 14.615 -4.48 14.75 ; + RECT -4.55 15.735 -4.48 15.87 ; + RECT -1.3275 15.315 -1.2575 15.45 ; + RECT -0.7975 15.945 -0.7275 16.08 ; + RECT -2.1225 15.945 -2.0525 16.08 ; + RECT -2.645 15.315 -2.575 15.45 ; + RECT -2.785 15.525 -2.715 15.66 ; + RECT -4.0275 15.945 -3.9575 16.08 ; + RECT -3.505 15.735 -3.435 15.87 ; + RECT -3.365 15.525 -3.295 15.66 ; + RECT -0.9975 9.85 -0.9325 9.92 ; + RECT -0.2225 9.85 -0.1575 9.92 ; + RECT -0.22 15.135 -0.155 15.205 ; + RECT -0.965 9.85 -0.1875 9.92 ; + RECT -0.35 15.135 -0.1875 15.205 ; + RECT -1.0 9.8175 -0.93 9.9525 ; + RECT -0.225 9.8175 -0.155 9.9525 ; + RECT -0.2225 15.1025 -0.1525 15.2375 ; + RECT -2.4925 19.405 -2.4225 19.54 ; + RECT -3.6575 19.195 -3.5875 19.33 ; + RECT -0.9675 19.615 -0.8975 19.75 ; + RECT -0.2075 19.435 -0.1425 19.505 ; + RECT -0.35 19.435 -0.1725 19.505 ; + RECT -0.21 19.4025 -0.14 19.5375 ; + RECT -2.0425 19.645 -1.9075 19.715 ; + RECT -2.0425 21.245 -1.9075 21.315 ; + RECT -1.73 19.825 -1.66 19.96 ; + RECT -4.42 19.825 -4.35 19.96 ; + RECT -0.385 14.895 -0.315 15.03 ; + RECT -0.965 13.41 -0.9 13.48 ; + RECT -2.0875 13.41 -2.0225 13.48 ; + RECT -2.0525 13.41 -0.9325 13.48 ; + RECT -0.9675 13.3775 -0.8975 13.5125 ; + RECT -2.09 13.3775 -2.02 13.5125 ; + RECT -2.085 8.275 -2.015 8.41 ; + RECT -2.085 8.275 -2.015 8.41 ; + RECT -0.275 15.975 -0.14 16.045 ; + RECT -0.8675 8.415 -0.7975 8.55 ; + RECT -0.275 8.415 -0.14 8.485 ; + RECT -1.0825 16.865 -0.9475 16.935 ; + RECT -0.275 16.865 -0.14 16.935 ; + RECT -0.275 19.225 -0.14 19.295 ; + RECT -1.035 17.335 -0.9 17.405 ; + RECT -0.275 17.335 -0.14 17.405 ; + RECT 9.665 8.275 9.8 8.345 ; + RECT 10.295 15.975 10.43 16.045 ; + RECT 10.085 8.415 10.22 8.485 ; + RECT 9.875 16.865 10.01 16.935 ; + RECT 10.505 19.225 10.64 19.295 ; + RECT 10.715 17.335 10.85 17.405 ; + RECT 0.0 19.855 0.205 19.99 ; + RECT 8.89 20.695 9.095 20.83 ; + RECT -0.415 20.695 -0.28 20.765 ; + Layer via2 ; + RECT 11.4425 18.79 11.5125 18.86 ; + RECT 12.1475 18.79 12.2175 18.86 ; + RECT 11.4475 9.87 11.5175 9.94 ; + RECT 12.1525 9.87 12.2225 9.94 ; + RECT 11.4475 3.43 11.5175 3.5 ; + RECT 12.1525 3.43 12.2225 3.5 ; + RECT 11.4475 3.5825 11.5175 3.6525 ; + RECT 12.1525 3.5825 12.2225 3.6525 ; + RECT 0.945 7.4525 1.015 7.5225 ; + RECT 0.945 6.7475 1.015 6.8175 ; + RECT 0.945 6.0425 1.015 6.1125 ; + RECT 0.945 5.3375 1.015 5.4075 ; + RECT 11.24 0.3125 11.305 0.3775 ; + RECT 11.945 0.3125 12.01 0.3775 ; + RECT 11.45 0.035 11.515 0.1 ; + RECT 12.155 0.035 12.22 0.1 ; + RECT 8.5425 7.4875 8.6075 7.5525 ; + RECT 7.115 7.455 7.18 7.52 ; + RECT 8.3325 6.7825 8.3975 6.8475 ; + RECT 7.115 6.75 7.18 6.815 ; + RECT 8.1225 6.0775 8.1875 6.1425 ; + RECT 7.115 6.045 7.18 6.11 ; + RECT 7.9125 5.3725 7.9775 5.4375 ; + RECT 7.115 5.34 7.18 5.405 ; + RECT 1.015 7.63 1.08 7.695 ; + RECT 9.7325 8.015 9.7975 8.08 ; + RECT -5.4125 8.31 -5.3425 8.38 ; + RECT -4.7075 8.31 -4.6375 8.38 ; + RECT -4.0025 8.31 -3.9325 8.38 ; + RECT -1.0 22.8125 -0.935 22.8775 ; + RECT -2.52 22.8125 -2.455 22.8775 ; + RECT -2.0075 19.6475 -1.9425 19.7125 ; + RECT -2.0075 21.2475 -1.9425 21.3125 ; + RECT -2.0825 8.31 -2.0175 8.375 ; + RECT -0.24 15.9775 -0.175 16.0425 ; + RECT -0.24 8.4175 -0.175 8.4825 ; + RECT -0.24 16.8675 -0.175 16.9325 ; + RECT -0.24 19.2275 -0.175 19.2925 ; + RECT -0.24 17.3375 -0.175 17.4025 ; + RECT 9.7 8.2775 9.765 8.3425 ; + RECT 10.33 15.9775 10.395 16.0425 ; + RECT 10.12 8.4175 10.185 8.4825 ; + RECT 9.91 16.8675 9.975 16.9325 ; + RECT 10.54 19.2275 10.605 19.2925 ; + RECT 10.75 17.3375 10.815 17.4025 ; + RECT 8.89 20.73 8.955 20.795 ; + RECT 9.03 20.73 9.095 20.795 ; + RECT -0.38 20.6975 -0.315 20.7625 ; + Layer metal3 ; + RECT -2.0525 8.275 9.73 8.345 ; + RECT -0.14 15.975 10.36 16.045 ; + RECT -0.14 8.415 10.15 8.485 ; + RECT -0.14 16.865 9.94 16.935 ; + RECT -0.14 19.225 10.57 19.295 ; + RECT -0.14 17.335 10.78 17.405 ; + RECT -0.28 20.695 8.89 20.765 ; + RECT 11.4475 0.0 11.5175 3.22 ; + RECT 12.1525 0.0 12.2225 3.22 ; + RECT 7.215 7.4525 8.61 7.5225 ; + RECT 7.215 6.7475 8.4 6.8175 ; + RECT 7.215 6.0425 8.19 6.1125 ; + RECT 7.215 5.3375 7.98 5.4075 ; + RECT 0.0 7.4525 0.9825 7.5225 ; + RECT 0.0 6.7475 0.9825 6.8175 ; + RECT 0.0 6.0425 0.9825 6.1125 ; + RECT 0.0 5.3375 0.9825 5.4075 ; + RECT 11.4425 18.755 11.5125 18.895 ; + RECT 12.1475 18.755 12.2175 18.895 ; + RECT 11.4475 9.835 11.5175 9.975 ; + RECT 12.1525 9.835 12.2225 9.975 ; + RECT 11.4475 3.395 11.5175 3.535 ; + RECT 12.1525 3.395 12.2225 3.535 ; + RECT 11.4475 3.5475 11.5175 3.6875 ; + RECT 12.1525 3.5475 12.2225 3.6875 ; + RECT 0.91 7.4525 1.05 7.5225 ; + RECT 0.91 6.7475 1.05 6.8175 ; + RECT 0.91 6.0425 1.05 6.1125 ; + RECT 0.91 5.3375 1.05 5.4075 ; + RECT 11.2375 18.7325 11.3075 18.8025 ; + RECT 11.2375 0.3125 11.3075 18.7675 ; + RECT 11.2725 18.7325 11.5125 18.8025 ; + RECT 11.2375 0.2775 11.3075 0.4125 ; + RECT 11.9425 18.7325 12.0125 18.8025 ; + RECT 11.9425 0.3125 12.0125 18.7675 ; + RECT 11.9775 18.7325 12.2175 18.8025 ; + RECT 11.9425 0.2775 12.0125 0.4125 ; + RECT 11.4475 0.0 11.5175 0.135 ; + RECT 12.1525 0.0 12.2225 0.135 ; + RECT 8.54 7.4525 8.61 7.5875 ; + RECT 7.08 7.4525 7.215 7.5225 ; + RECT 8.33 6.7475 8.4 6.8825 ; + RECT 7.08 6.7475 7.215 6.8175 ; + RECT 8.12 6.0425 8.19 6.1775 ; + RECT 7.08 6.0425 7.215 6.1125 ; + RECT 7.91 5.3375 7.98 5.4725 ; + RECT 7.08 5.3375 7.215 5.4075 ; + RECT 0.98 7.6275 1.115 7.6975 ; + RECT 1.08 7.98 1.15 8.05 ; + RECT 1.08 7.6275 1.15 8.015 ; + RECT 1.115 7.98 9.7325 8.05 ; + RECT 9.73 7.98 9.8 8.115 ; + RECT -1.9775 19.645 -1.9075 21.2775 ; + RECT -5.4125 8.275 -5.3425 8.415 ; + RECT -4.7075 8.275 -4.6375 8.415 ; + RECT -4.0025 8.275 -3.9325 8.415 ; + RECT -1.0025 22.81 -0.9325 22.88 ; + RECT -2.5225 22.81 -2.4525 22.88 ; + RECT -2.4875 22.81 -0.9675 22.88 ; + RECT -1.0025 22.7775 -0.9325 22.9125 ; + RECT -2.5225 22.7775 -2.4525 22.9125 ; + RECT -2.0425 19.645 -1.9075 19.715 ; + RECT -2.0425 21.245 -1.9075 21.315 ; + RECT -2.085 8.275 -2.015 8.41 ; + RECT -0.275 15.975 -0.14 16.045 ; + RECT -0.275 8.415 -0.14 8.485 ; + RECT -0.275 16.865 -0.14 16.935 ; + RECT -0.275 19.225 -0.14 19.295 ; + RECT -0.275 17.335 -0.14 17.405 ; + RECT 9.665 8.275 9.8 8.345 ; + RECT 10.295 15.975 10.43 16.045 ; + RECT 10.085 8.415 10.22 8.485 ; + RECT 9.875 16.865 10.01 16.935 ; + RECT 10.505 19.225 10.64 19.295 ; + RECT 10.715 17.335 10.85 17.405 ; + RECT 8.89 20.695 9.095 20.83 ; + RECT -0.415 20.695 -0.28 20.765 ; + END +END sram_2_16_1_freepdk45 +END LIBRARY diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.lib b/compiler/tests/golden/sram_2_16_1_freepdk45.lib new file mode 100644 index 00000000..2a1fce2a --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.lib @@ -0,0 +1,289 @@ +library (sram_2_16_1_freepdk45_lib){ + delay_model : "table_lookup"; + time_unit : "1ns" ; + voltage_unit : "1v" ; + current_unit : "1mA" ; + resistance_unit : "1kohm" ; + capacitive_load_unit(1 ,fF) ; + leakage_power_unit : "1uW" ; + pulling_resistance_unit :"1kohm" ; + operating_conditions(TT){ + voltage : 1.0 ; + temperature : 25.000 ; + } + + input_threshold_pct_fall : 50.0 ; + output_threshold_pct_fall : 50.0 ; + input_threshold_pct_rise : 50.0 ; + output_threshold_pct_rise : 50.0 ; + slew_lower_threshold_pct_fall : 10.0 ; + slew_upper_threshold_pct_fall : 90.0 ; + slew_lower_threshold_pct_rise : 10.0 ; + slew_upper_threshold_pct_rise : 90.0 ; + + default_cell_leakage_power : 0.0 ; + default_leakage_power_density : 0.0 ; + default_input_pin_cap : 1.0 ; + default_inout_pin_cap : 1.0 ; + default_output_pin_cap : 0.0 ; + default_max_transition : 0.5 ; + default_fanout_load : 1.0 ; + default_max_fanout : 4.0 ; + default_connection_class : universal ; + + lu_table_template(CELL_UP_FOR_CLOCK){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CELL_DN_FOR_CLOCK){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CONSTRAINT_HIGH_POS){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CONSTRAINT_LOW_POS){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CLK_TRAN) { + variable_1 : constrained_pin_transition; + index_1 ("0.5"); + } + + lu_table_template(TRAN) { + variable_1 : total_output_net_capacitance; + index_1 ("0.5"); + } + + default_operating_conditions : TT; + + + type (DATA){ + base_type : array; + data_type : bit; + bit_width : 2; + bit_from : 0; + bit_to : 1; + } + + type (ADDR){ + base_type : array; + data_type : bit; + bit_width : 4; + bit_from : 0; + bit_to : 3; + } + +cell (sram_2_16_1_freepdk45){ + memory(){ + type : ram; + address_width : 4; + word_width : 2; + } + interface_timing : true; + dont_use : true; + map_only : true; + dont_touch : true; + area : 799.659625; + + bus(DATA){ + bus_type : DATA; + direction : inout; + max_capacitance : 0.62166; + pin(DATA[1:0]){ + } + three_state : "OEb & !clk"; + memory_write(){ + address : ADDR; + clocked_on : clk; + } + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + memory_read(){ + address : ADDR; + } + timing(){ + timing_sense : non_unate; + related_pin : "clk"; + timing_type : rising_edge; + cell_rise(CELL_UP_FOR_CLOCK) { + values("0.042"); + } + cell_fall(CELL_DN_FOR_CLOCK) { + values("0.241"); + } + rise_transition(TRAN) { + values("0.042"); + } + fall_transition(TRAN) { + values("0.241"); + } + } + } + + bus(ADDR){ + bus_type : ADDR; + direction : input; + capacitance : 0.2091; + max_transition : 0.5; + fanout_load : 1.000000; + pin(ADDR[3:0]){ + } + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + } + + pin(CSb){ + direction : input; + capacitance : 0.2091; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + } + + pin(OEb){ + direction : input; + capacitance : 0.2091; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + } + + pin(WEb){ + direction : input; + capacitance : 0.2091; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.015"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("0.009"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("-0.005"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.011"); + } + } + } + + pin(clk){ + clock : true; + direction : input; + capacitance : 0.2091; + min_pulse_width_high : 0.081 ; + min_pulse_width_low : 0.267 ; + timing(){ + timing_type :"min_pulse_width"; + related_pin : clk; + rise_constraint(CLK_TRAN) { + values("0"); + } + fall_constraint(CLK_TRAN) { + values("0"); + } + } + timing(){ + timing_type :"minimum_period"; + related_pin : clk; + rise_constraint(CLK_TRAN) { + values("0"); + } + fall_constraint(CLK_TRAN) { + values("0"); + } + } + } + } +} diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.sp b/compiler/tests/golden/sram_2_16_1_freepdk45.sp new file mode 100644 index 00000000..90500f05 --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.sp @@ -0,0 +1,661 @@ +* OpenRAM generated memory. +* User: mrg +.global vdd gnd +*master-slave flip-flop with both output and inverted ouput + +.SUBCKT ms_flop din dout dout_bar clk vdd gnd +xmaster din mout mout_bar clk clk_bar vdd gnd dlatch +xslave mout_bar dout_bar dout clk_bar clk_nn vdd gnd dlatch +.ENDS flop + +.SUBCKT dlatch din dout dout_bar clk clk_bar vdd gnd +*clk inverter +mPff1 clk_bar clk vdd vdd PMOS_VTG W=180.0n L=50n m=1 +mNff1 clk_bar clk gnd gnd NMOS_VTG W=90n L=50n m=1 + +*transmission gate 1 +mtmP1 din clk int1 vdd PMOS_VTG W=180.0n L=50n m=1 +mtmN1 din clk_bar int1 gnd NMOS_VTG W=90n L=50n m=1 + +*foward inverter +mPff3 dout_bar int1 vdd vdd PMOS_VTG W=180.0n L=50n m=1 +mNff3 dout_bar int1 gnd gnd NMOS_VTG W=90n L=50n m=1 + +*backward inverter +mPff4 dout dout_bar vdd vdd PMOS_VTG W=180.0n L=50n m=1 +mNf4 dout dout_bar gnd gnd NMOS_VTG W=90n L=50n m=1 + +*transmission gate 2 +mtmP2 int1 clk_bar dout vdd PMOS_VTG W=180.0n L=50n m=1 +mtmN2 int1 clk dout gnd NMOS_VTG W=90n L=50n m=1 +.ENDS dlatch + + +.SUBCKT inv_nmos11 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS inv_nmos11 + +.SUBCKT inv_pmos12 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.27u l=0.05u +.ENDS inv_pmos12 + +.SUBCKT pinv A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos11 +Xpinv_pmos Z A vdd vdd inv_pmos12 +.ENDS pinv + +.SUBCKT nand_2_nmos13 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_nmos13 + +.SUBCKT nand_2_nmos24 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_nmos24 + +.SUBCKT nand_2_pmos15 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_pmos15 + +.SUBCKT nand_2_pmos26 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_pmos26 + +.SUBCKT nand2 A B Z vdd gnd +Xnmos1 Z A net1 gnd nand_2_nmos13 +Xnmos2 net1 B gnd gnd nand_2_nmos24 +Xpmos1 vdd A Z vdd nand_2_pmos15 +Xpmos2 Z B vdd vdd nand_2_pmos26 +.ENDS nand2 + +.SUBCKT nand_3_nmos17 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.27u l=0.05u +.ENDS nand_3_nmos17 + +.SUBCKT nand_3_nmos28 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.27u l=0.05u +.ENDS nand_3_nmos28 + +.SUBCKT nand_3_nmos39 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.27u l=0.05u +.ENDS nand_3_nmos39 + +.SUBCKT nand_3_pmos110 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_3_pmos110 + +.SUBCKT nand_3_pmos211 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_3_pmos211 + +.SUBCKT nand_3_pmos312 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_3_pmos312 + +.SUBCKT NAND3 A B C Z vdd gnd +Xnmos1 net2 A gnd gnd nand_3_nmos17 +Xnmos2 net1 B net2 gnd nand_3_nmos28 +Xnmos3 Z C net1 gnd nand_3_nmos39 +Xpmos1 Z A vdd vdd nand_3_pmos110 +Xpmos2 vdd B Z vdd nand_3_pmos211 +Xpmos3 Z C vdd vdd nand_3_pmos312 +.ENDS NAND3 + +.SUBCKT inv_nmos113 D G S B +Mnmos D G S B nmos_vtg m=4 w=0.09u l=0.05u +.ENDS inv_nmos113 + +.SUBCKT inv_pmos114 D G S B +Mpmos D G S B pmos_vtg m=4 w=0.27u l=0.05u +.ENDS inv_pmos114 + +.SUBCKT pinv4 A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos113 +Xpinv_pmos Z A vdd vdd inv_pmos114 +.ENDS pinv4 + +.SUBCKT nor_2_nmos119 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS nor_2_nmos119 + +.SUBCKT nor_2_nmos220 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS nor_2_nmos220 + +.SUBCKT nor_2_pmos121 D G S B +Mpmos D G S B pmos_vtg m=2 w=0.18u l=0.05u +.ENDS nor_2_pmos121 + +.SUBCKT nor_2_pmos222 D G S B +Mpmos D G S B pmos_vtg m=2 w=0.18u l=0.05u +.ENDS nor_2_pmos222 + +.SUBCKT nor2 A B Z vdd gnd +Xnmos1 Z A gnd gnd nor_2_nmos119 +Xnmos2 Z B gnd gnd nor_2_nmos220 +Xpmos1 vdd A net1 vdd nor_2_pmos121 +Xpmos2 net1 B Z vdd nor_2_pmos222 +.ENDS nor2 + +.SUBCKT msf_control DATA[0] DATA[1] DATA[2] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] data_in[2] data_in_bar[2] clk vdd gnd +XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop +XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop +XXdff2 DATA[2] data_in[2] data_in_bar[2] clk vdd gnd ms_flop +.ENDS msf_control + +.SUBCKT replica_cell_6t bl br wl vdd gnd +MM3 bl wl gnd gnd NMOS_VTG W=135.00n L=50n +MM2 br wl net4 gnd NMOS_VTG W=135.00n L=50n +MM1 gnd net4 gnd gnd NMOS_VTG W=205.00n L=50n +MM0 net4 gnd gnd gnd NMOS_VTG W=205.00n L=50n +MM5 gnd net4 vdd vdd PMOS_VTG W=90n L=50n +MM4 net4 gnd vdd vdd PMOS_VTG W=90n L=50n +.ENDS replica_cell_6t + + +.SUBCKT cell_6t bl br wl vdd gnd +MM3 bl wl net10 gnd NMOS_VTG W=135.00n L=50n +MM2 br wl net4 gnd NMOS_VTG W=135.00n L=50n +MM1 net10 net4 gnd gnd NMOS_VTG W=205.00n L=50n +MM0 net4 net10 gnd gnd NMOS_VTG W=205.00n L=50n +MM5 net10 net4 vdd vdd PMOS_VTG W=90n L=50n +MM4 net4 net10 vdd vdd PMOS_VTG W=90n L=50n +.ENDS cell_6t + + +.SUBCKT bitline_load bl[0] br[0] wl[0] wl[1] vdd gnd +Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t +Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t +.ENDS bitline_load + +.SUBCKT inv_nmos123 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS inv_nmos123 + +.SUBCKT inv_pmos124 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.27u l=0.05u +.ENDS inv_pmos124 + +.SUBCKT delay_chain_inv A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos123 +Xpinv_pmos Z A vdd vdd inv_pmos124 +.ENDS delay_chain_inv + +.SUBCKT delay_chain clk_in clk_out vdd gnd +Xinv_chain0 clk_in s1 vdd gnd delay_chain_inv +Xinv_chain1 s1 s2 vdd gnd delay_chain_inv +Xinv_chain2 s2 s3 vdd gnd delay_chain_inv +Xinv_chain3 s3 clk_out vdd gnd delay_chain_inv +.ENDS delay_chain + +.SUBCKT inv_nmos125 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS inv_nmos125 + +.SUBCKT inv_pmos126 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.27u l=0.05u +.ENDS inv_pmos126 + +.SUBCKT RBL_inv A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos125 +Xpinv_pmos Z A vdd vdd inv_pmos126 +.ENDS RBL_inv + +.SUBCKT nor_2_nmos131 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS nor_2_nmos131 + +.SUBCKT nor_2_nmos232 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS nor_2_nmos232 + +.SUBCKT nor_2_pmos133 D G S B +Mpmos D G S B pmos_vtg m=2 w=0.18u l=0.05u +.ENDS nor_2_pmos133 + +.SUBCKT nor_2_pmos234 D G S B +Mpmos D G S B pmos_vtg m=2 w=0.18u l=0.05u +.ENDS nor_2_pmos234 + +.SUBCKT replica_bitline_nor2 A B Z vdd gnd +Xnmos1 Z A gnd gnd nor_2_nmos131 +Xnmos2 Z B gnd gnd nor_2_nmos232 +Xpmos1 vdd A net1 vdd nor_2_pmos133 +Xpmos2 net1 B Z vdd nor_2_pmos234 +.ENDS replica_bitline_nor2 + +.SUBCKT access_tx35 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.09u l=0.05u +.ENDS access_tx35 + +.SUBCKT replica_bitline en out vdd gnd +XBL_inv bl[0] out vdd gnd RBL_inv +XBL_access_tx vdd delayed_en bl[0] vdd access_tx35 +Xdelay_chain en delayed_en vdd gnd delay_chain +Xbitcell bl[0] br[0] delayed_en vdd gnd replica_cell_6t +Xload bl[0] br[0] gnd gnd vdd gnd bitline_load +.ENDS replica_bitline + +.SUBCKT control_logic CSb WEb OEb s_en w_en tri_en tri_en_bar clk_bar clk vdd gnd +Xmsf_control CSb WEb OEb CS_bar CS WE_bar WE OE_bar OE clk vdd gnd msf_control +Xclk_inverter clk clk_bar vdd gnd pinv4 +Xnor2 clk OE_bar tri_en vdd gnd nor2 +Xnand2_tri_en OE clk_bar tri_en_bar vdd gnd nand2 +Xreplica_bitline rblk pre_s_en vdd gnd replica_bitline +Xinv_s_en1 pre_s_en_bar s_en vdd gnd pinv +Xinv_s_en2 pre_s_en pre_s_en_bar vdd gnd pinv +XNAND3_rblk_bar clk_bar OE CS rblk_bar vdd gnd NAND3 +XNAND3_w_en_bar clk_bar WE CS w_en_bar vdd gnd NAND3 +Xinv_rblk rblk_bar rblk vdd gnd pinv +Xinv_w_en w_en_bar pre_w_en vdd gnd pinv +Xinv_w_en1 pre_w_en pre_w_en1 vdd gnd pinv +Xinv_w_en2 pre_w_en1 w_en vdd gnd pinv +.ENDS control_logic + +.SUBCKT bitcell_array bl[0] br[0] bl[1] br[1] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd +Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t +Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t +Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t +Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t +Xbit_r4_c0 bl[0] br[0] wl[4] vdd gnd cell_6t +Xbit_r5_c0 bl[0] br[0] wl[5] vdd gnd cell_6t +Xbit_r6_c0 bl[0] br[0] wl[6] vdd gnd cell_6t +Xbit_r7_c0 bl[0] br[0] wl[7] vdd gnd cell_6t +Xbit_r8_c0 bl[0] br[0] wl[8] vdd gnd cell_6t +Xbit_r9_c0 bl[0] br[0] wl[9] vdd gnd cell_6t +Xbit_r10_c0 bl[0] br[0] wl[10] vdd gnd cell_6t +Xbit_r11_c0 bl[0] br[0] wl[11] vdd gnd cell_6t +Xbit_r12_c0 bl[0] br[0] wl[12] vdd gnd cell_6t +Xbit_r13_c0 bl[0] br[0] wl[13] vdd gnd cell_6t +Xbit_r14_c0 bl[0] br[0] wl[14] vdd gnd cell_6t +Xbit_r15_c0 bl[0] br[0] wl[15] vdd gnd cell_6t +Xbit_r0_c1 bl[1] br[1] wl[0] vdd gnd cell_6t +Xbit_r1_c1 bl[1] br[1] wl[1] vdd gnd cell_6t +Xbit_r2_c1 bl[1] br[1] wl[2] vdd gnd cell_6t +Xbit_r3_c1 bl[1] br[1] wl[3] vdd gnd cell_6t +Xbit_r4_c1 bl[1] br[1] wl[4] vdd gnd cell_6t +Xbit_r5_c1 bl[1] br[1] wl[5] vdd gnd cell_6t +Xbit_r6_c1 bl[1] br[1] wl[6] vdd gnd cell_6t +Xbit_r7_c1 bl[1] br[1] wl[7] vdd gnd cell_6t +Xbit_r8_c1 bl[1] br[1] wl[8] vdd gnd cell_6t +Xbit_r9_c1 bl[1] br[1] wl[9] vdd gnd cell_6t +Xbit_r10_c1 bl[1] br[1] wl[10] vdd gnd cell_6t +Xbit_r11_c1 bl[1] br[1] wl[11] vdd gnd cell_6t +Xbit_r12_c1 bl[1] br[1] wl[12] vdd gnd cell_6t +Xbit_r13_c1 bl[1] br[1] wl[13] vdd gnd cell_6t +Xbit_r14_c1 bl[1] br[1] wl[14] vdd gnd cell_6t +Xbit_r15_c1 bl[1] br[1] wl[15] vdd gnd cell_6t +.ENDS bitcell_array + +.SUBCKT lower_pmos36 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.09u l=0.05u +.ENDS lower_pmos36 + +.SUBCKT upper_pmos37 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS upper_pmos37 + +.SUBCKT precharge_cell bl br clk vdd +Xlower_pmos bl clk br vdd lower_pmos36 +Xupper_pmos1 bl clk vdd vdd upper_pmos37 +Xupper_pmos2 br clk vdd vdd upper_pmos37 +.ENDS precharge_cell + +.SUBCKT precharge_array bl[0] br[0] bl[1] br[1] clk vdd +Xpre_column_0 bl[0] br[0] clk vdd precharge_cell +Xpre_column_1 bl[1] br[1] clk vdd precharge_cell +.ENDS precharge_array + +.SUBCKT sense_amp bl br dout sclk vdd gnd +M_1 dout net_1 vdd vdd pmos_vtg w=540.0n l=50.0n +M_3 net_1 dout vdd vdd pmos_vtg w=540.0n l=50.0n +M_2 dout net_1 net_2 gnd nmos_vtg w=270.0n l=50.0n +M_8 net_1 dout net_2 gnd nmos_vtg w=270.0n l=50.0n +M_5 bl sclk dout vdd pmos_vtg w=720.0n l=50.0n +M_6 br sclk net_1 vdd pmos_vtg w=720.0n l=50.0n +M_7 net_2 sclk gnd gnd nmos_vtg w=270.0n l=50.0n +.ENDS sense_amp + + +.SUBCKT sense_amp_array bl[0] br[0] bl[1] br[1] data_out[0] data_out[1] sclk vdd gnd +Xsa_d0 bl[0] br[0] data_out[0] sclk vdd gnd sense_amp +Xsa_d1 bl[1] br[1] data_out[1] sclk vdd gnd sense_amp +.ENDS sense_amp_array + +.SUBCKT write_driver din bl br wen vdd gnd +*inverters for enable and data input +minP bl_bar din vdd vdd pmos_vtg w=360.000000n l=50.000000n +minN bl_bar din gnd gnd nmos_vtg w=180.000000n l=50.000000n +moutP wen_bar wen vdd vdd pmos_vtg w=360.000000n l=50.000000n +moutN wen_bar wen gnd gnd nmos_vtg w=180.000000n l=50.000000n + +*tristate for BL +mout0P int1 bl_bar vdd vdd pmos_vtg w=360.000000n l=50.000000n +mout0P2 bl wen_bar int1 vdd pmos_vtg w=360.000000n l=50.000000n +mout0N bl wen int2 gnd nmos_vtg w=180.000000n l=50.000000n +mout0N2 int2 bl_bar gnd gnd nmos_vtg w=180.000000n l=50.000000n + +*tristate for BR +mout1P int3 din vdd vdd pmos_vtg w=360.000000n l=50.000000n +mout1P2 br wen_bar int3 vdd pmos_vtg w=360.000000n l=50.000000n +mout1N br wen int4 gnd nmos_vtg w=180.000000n l=50.000000n +mout1N2 int4 din gnd gnd nmos_vtg w=180.000000n l=50.000000n +.ENDS write_driver + + +.SUBCKT write_driver_array data_in[0] data_in[1] bl[0] br[0] bl[1] br[1] wen vdd gnd +XXwrite_driver0 data_in[0] bl[0] br[0] wen vdd gnd write_driver +XXwrite_driver1 data_in[1] bl[1] br[1] wen vdd gnd write_driver +.ENDS write_driver_array + +.SUBCKT inv_nmos139 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS inv_nmos139 + +.SUBCKT inv_pmos140 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS inv_pmos140 + +.SUBCKT INVERTER A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos139 +Xpinv_pmos Z A vdd vdd inv_pmos140 +.ENDS INVERTER + +.SUBCKT nand_2_nmos141 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_nmos141 + +.SUBCKT nand_2_nmos242 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_nmos242 + +.SUBCKT nand_2_pmos143 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_pmos143 + +.SUBCKT nand_2_pmos244 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_pmos244 + +.SUBCKT NAND2 A B Z vdd gnd +Xnmos1 Z A net1 gnd nand_2_nmos141 +Xnmos2 net1 B gnd gnd nand_2_nmos242 +Xpmos1 vdd A Z vdd nand_2_pmos143 +Xpmos2 Z B vdd vdd nand_2_pmos244 +.ENDS NAND2 + +.SUBCKT nand_2_nmos151 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_nmos151 + +.SUBCKT nand_2_nmos252 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_nmos252 + +.SUBCKT nand_2_pmos153 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_pmos153 + +.SUBCKT nand_2_pmos254 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_2_pmos254 + +.SUBCKT a_nand_2 A B Z vdd gnd +Xnmos1 Z A net1 gnd nand_2_nmos151 +Xnmos2 net1 B gnd gnd nand_2_nmos252 +Xpmos1 vdd A Z vdd nand_2_pmos153 +Xpmos2 Z B vdd vdd nand_2_pmos254 +.ENDS a_nand_2 + +.SUBCKT inv_nmos155 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS inv_nmos155 + +.SUBCKT inv_pmos156 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS inv_pmos156 + +.SUBCKT a_inv_1 A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos155 +Xpinv_pmos Z A vdd vdd inv_pmos156 +.ENDS a_inv_1 + +.SUBCKT pre2x4 A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd +XXpre2x4_inv[0] A[0] B[0] vdd gnd a_inv_1 +XXpre2x4_inv[1] A[1] B[1] vdd gnd a_inv_1 +XXpre2x4_nand_inv[0] Z[0] out[0] vdd gnd a_inv_1 +XXpre2x4_nand_inv[1] Z[1] out[1] vdd gnd a_inv_1 +XXpre2x4_nand_inv[2] Z[2] out[2] vdd gnd a_inv_1 +XXpre2x4_nand_inv[3] Z[3] out[3] vdd gnd a_inv_1 +XXpre2x4_nand[0] A[0] A[1] Z[3] vdd gnd a_nand_2 +XXpre2x4_nand[1] B[0] A[1] Z[2] vdd gnd a_nand_2 +XXpre2x4_nand[2] A[0] B[1] Z[1] vdd gnd a_nand_2 +XXpre2x4_nand[3] B[0] B[1] Z[0] vdd gnd a_nand_2 +.ENDS pre2x4 + +.SUBCKT nand_3_nmos157 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.27u l=0.05u +.ENDS nand_3_nmos157 + +.SUBCKT nand_3_nmos258 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.27u l=0.05u +.ENDS nand_3_nmos258 + +.SUBCKT nand_3_nmos359 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.27u l=0.05u +.ENDS nand_3_nmos359 + +.SUBCKT nand_3_pmos160 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_3_pmos160 + +.SUBCKT nand_3_pmos261 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_3_pmos261 + +.SUBCKT nand_3_pmos362 D G S B +Mpmos D G S B pmos_vtg m=1 w=0.18u l=0.05u +.ENDS nand_3_pmos362 + +.SUBCKT a_nand_3 A B C Z vdd gnd +Xnmos1 net2 A gnd gnd nand_3_nmos157 +Xnmos2 net1 B net2 gnd nand_3_nmos258 +Xnmos3 Z C net1 gnd nand_3_nmos359 +Xpmos1 Z A vdd vdd nand_3_pmos160 +Xpmos2 vdd B Z vdd nand_3_pmos261 +Xpmos3 Z C vdd vdd nand_3_pmos362 +.ENDS a_nand_3 + +.SUBCKT pre3x8 A[0] A[1] A[2] out[0] out[1] out[2] out[3] out[4] out[5] out[6] out[7] vdd gnd +XXpre2x4_inv[0] A[0] B[0] vdd gnd a_inv_1 +XXpre2x4_inv[1] A[1] B[1] vdd gnd a_inv_1 +XXpre2x4_inv[2] A[2] B[2] vdd gnd a_inv_1 +XXpre2x4_nand_inv[0] Z[0] out[0] vdd gnd a_inv_1 +XXpre2x4_nand_inv[1] Z[1] out[1] vdd gnd a_inv_1 +XXpre2x4_nand_inv[2] Z[2] out[2] vdd gnd a_inv_1 +XXpre2x4_nand_inv[3] Z[3] out[3] vdd gnd a_inv_1 +XXpre2x4_nand_inv[4] Z[4] out[4] vdd gnd a_inv_1 +XXpre2x4_nand_inv[5] Z[5] out[5] vdd gnd a_inv_1 +XXpre2x4_nand_inv[6] Z[6] out[6] vdd gnd a_inv_1 +XXpre2x4_nand_inv[7] Z[7] out[7] vdd gnd a_inv_1 +XXpre3x8_nand[0] A[0] A[1] A[2] Z[7] vdd gnd a_nand_3 +XXpre3x8_nand[1] A[0] A[1] B[2] Z[6] vdd gnd a_nand_3 +XXpre3x8_nand[2] A[0] B[1] A[2] Z[5] vdd gnd a_nand_3 +XXpre3x8_nand[3] A[0] B[1] B[2] Z[4] vdd gnd a_nand_3 +XXpre3x8_nand[4] B[0] A[1] A[2] Z[3] vdd gnd a_nand_3 +XXpre3x8_nand[5] B[0] A[1] B[2] Z[2] vdd gnd a_nand_3 +XXpre3x8_nand[6] B[0] B[1] A[2] Z[1] vdd gnd a_nand_3 +XXpre3x8_nand[7] B[0] B[1] B[2] Z[0] vdd gnd a_nand_3 +.ENDS pre3x8 + +.SUBCKT hierarchical_decoder A[0] A[1] A[2] A[3] decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] vdd gnd +Xpre[0] A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd pre2x4 +Xpre[1] A[2] A[3] out[4] out[5] out[6] out[7] vdd gnd pre2x4 +XNAND2_[0] out[0] out[4] Z[0] vdd gnd NAND2 +XNAND2_[1] out[0] out[5] Z[1] vdd gnd NAND2 +XNAND2_[2] out[0] out[6] Z[2] vdd gnd NAND2 +XNAND2_[3] out[0] out[7] Z[3] vdd gnd NAND2 +XNAND2_[4] out[1] out[4] Z[4] vdd gnd NAND2 +XNAND2_[5] out[1] out[5] Z[5] vdd gnd NAND2 +XNAND2_[6] out[1] out[6] Z[6] vdd gnd NAND2 +XNAND2_[7] out[1] out[7] Z[7] vdd gnd NAND2 +XNAND2_[8] out[2] out[4] Z[8] vdd gnd NAND2 +XNAND2_[9] out[2] out[5] Z[9] vdd gnd NAND2 +XNAND2_[10] out[2] out[6] Z[10] vdd gnd NAND2 +XNAND2_[11] out[2] out[7] Z[11] vdd gnd NAND2 +XNAND2_[12] out[3] out[4] Z[12] vdd gnd NAND2 +XNAND2_[13] out[3] out[5] Z[13] vdd gnd NAND2 +XNAND2_[14] out[3] out[6] Z[14] vdd gnd NAND2 +XNAND2_[15] out[3] out[7] Z[15] vdd gnd NAND2 +XINVERTER_[0] Z[0] decode_out[0] vdd gnd INVERTER +XINVERTER_[1] Z[1] decode_out[1] vdd gnd INVERTER +XINVERTER_[2] Z[2] decode_out[2] vdd gnd INVERTER +XINVERTER_[3] Z[3] decode_out[3] vdd gnd INVERTER +XINVERTER_[4] Z[4] decode_out[4] vdd gnd INVERTER +XINVERTER_[5] Z[5] decode_out[5] vdd gnd INVERTER +XINVERTER_[6] Z[6] decode_out[6] vdd gnd INVERTER +XINVERTER_[7] Z[7] decode_out[7] vdd gnd INVERTER +XINVERTER_[8] Z[8] decode_out[8] vdd gnd INVERTER +XINVERTER_[9] Z[9] decode_out[9] vdd gnd INVERTER +XINVERTER_[10] Z[10] decode_out[10] vdd gnd INVERTER +XINVERTER_[11] Z[11] decode_out[11] vdd gnd INVERTER +XINVERTER_[12] Z[12] decode_out[12] vdd gnd INVERTER +XINVERTER_[13] Z[13] decode_out[13] vdd gnd INVERTER +XINVERTER_[14] Z[14] decode_out[14] vdd gnd INVERTER +XINVERTER_[15] Z[15] decode_out[15] vdd gnd INVERTER +.ENDS hierarchical_decoder + +.SUBCKT msf_address ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] addr_clk vdd gnd +XXdff0 ADDR[0] A[0] A_bar[0] addr_clk vdd gnd ms_flop +XXdff1 ADDR[1] A[1] A_bar[1] addr_clk vdd gnd ms_flop +XXdff2 ADDR[2] A[2] A_bar[2] addr_clk vdd gnd ms_flop +XXdff3 ADDR[3] A[3] A_bar[3] addr_clk vdd gnd ms_flop +.ENDS msf_address + +.SUBCKT msf_data_in DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk vdd gnd +XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop +XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop +.ENDS msf_data_in + +.SUBCKT msf_data_out data_out[0] data_out[1] tri_in[0] tri_in_bar[0] tri_in[1] tri_in_bar[1] sclk vdd gnd +XXdff0 data_out[0] tri_in[0] tri_in_bar[0] sclk vdd gnd ms_flop +XXdff1 data_out[1] tri_in[1] tri_in_bar[1] sclk vdd gnd ms_flop +.ENDS msf_data_out + +.SUBCKT tri_gate in out en en_bar vdd gnd +M_1 net_2 in_inv gnd gnd NMOS_VTG W=180.000000n L=50.000000n +M_2 out en net_2 gnd NMOS_VTG W=180.000000n L=50.000000n +M_3 net_3 in_inv vdd vdd PMOS_VTG W=360.000000n L=50.000000n +M_4 out en_bar net_3 vdd PMOS_VTG W=360.000000n L=50.000000n +M_5 in_inv in vdd vdd PMOS_VTG W=180.000000n L=50.000000n +M_6 in_inv in gnd gnd NMOS_VTG W=90.000000n L=50.000000n +.ENDS + + +.SUBCKT tri_gate_array tri_in[0] tri_in[1] DATA[0] DATA[1] en en_bar vdd gnd +XXtri_gate0 tri_in[0] DATA[0] en en_bar vdd gnd tri_gate +XXtri_gate1 tri_in[1] DATA[1] en en_bar vdd gnd tri_gate +.ENDS tri_gate_array + +.SUBCKT wordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk vdd gnd +XWordline_driver_inv_clk0 clk clk_bar[0] vdd gnd INVERTER +XWordline_driver_nand0 decode_out[0] clk_bar[0] net[0] vdd gnd NAND2 +XWordline_driver_inv0 net[0] wl[0] vdd gnd INVERTER +XWordline_driver_inv_clk1 clk clk_bar[1] vdd gnd INVERTER +XWordline_driver_nand1 decode_out[1] clk_bar[1] net[1] vdd gnd NAND2 +XWordline_driver_inv1 net[1] wl[1] vdd gnd INVERTER +XWordline_driver_inv_clk2 clk clk_bar[2] vdd gnd INVERTER +XWordline_driver_nand2 decode_out[2] clk_bar[2] net[2] vdd gnd NAND2 +XWordline_driver_inv2 net[2] wl[2] vdd gnd INVERTER +XWordline_driver_inv_clk3 clk clk_bar[3] vdd gnd INVERTER +XWordline_driver_nand3 decode_out[3] clk_bar[3] net[3] vdd gnd NAND2 +XWordline_driver_inv3 net[3] wl[3] vdd gnd INVERTER +XWordline_driver_inv_clk4 clk clk_bar[4] vdd gnd INVERTER +XWordline_driver_nand4 decode_out[4] clk_bar[4] net[4] vdd gnd NAND2 +XWordline_driver_inv4 net[4] wl[4] vdd gnd INVERTER +XWordline_driver_inv_clk5 clk clk_bar[5] vdd gnd INVERTER +XWordline_driver_nand5 decode_out[5] clk_bar[5] net[5] vdd gnd NAND2 +XWordline_driver_inv5 net[5] wl[5] vdd gnd INVERTER +XWordline_driver_inv_clk6 clk clk_bar[6] vdd gnd INVERTER +XWordline_driver_nand6 decode_out[6] clk_bar[6] net[6] vdd gnd NAND2 +XWordline_driver_inv6 net[6] wl[6] vdd gnd INVERTER +XWordline_driver_inv_clk7 clk clk_bar[7] vdd gnd INVERTER +XWordline_driver_nand7 decode_out[7] clk_bar[7] net[7] vdd gnd NAND2 +XWordline_driver_inv7 net[7] wl[7] vdd gnd INVERTER +XWordline_driver_inv_clk8 clk clk_bar[8] vdd gnd INVERTER +XWordline_driver_nand8 decode_out[8] clk_bar[8] net[8] vdd gnd NAND2 +XWordline_driver_inv8 net[8] wl[8] vdd gnd INVERTER +XWordline_driver_inv_clk9 clk clk_bar[9] vdd gnd INVERTER +XWordline_driver_nand9 decode_out[9] clk_bar[9] net[9] vdd gnd NAND2 +XWordline_driver_inv9 net[9] wl[9] vdd gnd INVERTER +XWordline_driver_inv_clk10 clk clk_bar[10] vdd gnd INVERTER +XWordline_driver_nand10 decode_out[10] clk_bar[10] net[10] vdd gnd NAND2 +XWordline_driver_inv10 net[10] wl[10] vdd gnd INVERTER +XWordline_driver_inv_clk11 clk clk_bar[11] vdd gnd INVERTER +XWordline_driver_nand11 decode_out[11] clk_bar[11] net[11] vdd gnd NAND2 +XWordline_driver_inv11 net[11] wl[11] vdd gnd INVERTER +XWordline_driver_inv_clk12 clk clk_bar[12] vdd gnd INVERTER +XWordline_driver_nand12 decode_out[12] clk_bar[12] net[12] vdd gnd NAND2 +XWordline_driver_inv12 net[12] wl[12] vdd gnd INVERTER +XWordline_driver_inv_clk13 clk clk_bar[13] vdd gnd INVERTER +XWordline_driver_nand13 decode_out[13] clk_bar[13] net[13] vdd gnd NAND2 +XWordline_driver_inv13 net[13] wl[13] vdd gnd INVERTER +XWordline_driver_inv_clk14 clk clk_bar[14] vdd gnd INVERTER +XWordline_driver_nand14 decode_out[14] clk_bar[14] net[14] vdd gnd NAND2 +XWordline_driver_inv14 net[14] wl[14] vdd gnd INVERTER +XWordline_driver_inv_clk15 clk clk_bar[15] vdd gnd INVERTER +XWordline_driver_nand15 decode_out[15] clk_bar[15] net[15] vdd gnd NAND2 +XWordline_driver_inv15 net[15] wl[15] vdd gnd INVERTER +.ENDS wordline_driver + +.SUBCKT inv_nmos173 D G S B +Mnmos D G S B nmos_vtg m=4 w=0.09u l=0.05u +.ENDS inv_nmos173 + +.SUBCKT inv_pmos174 D G S B +Mpmos D G S B pmos_vtg m=4 w=0.27u l=0.05u +.ENDS inv_pmos174 + +.SUBCKT pinv4x A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos173 +Xpinv_pmos Z A vdd vdd inv_pmos174 +.ENDS pinv4x + +.SUBCKT nor_2_nmos183 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS nor_2_nmos183 + +.SUBCKT nor_2_nmos284 D G S B +Mnmos D G S B nmos_vtg m=1 w=0.09u l=0.05u +.ENDS nor_2_nmos284 + +.SUBCKT nor_2_pmos185 D G S B +Mpmos D G S B pmos_vtg m=2 w=0.18u l=0.05u +.ENDS nor_2_pmos185 + +.SUBCKT nor_2_pmos286 D G S B +Mpmos D G S B pmos_vtg m=2 w=0.18u l=0.05u +.ENDS nor_2_pmos286 + +.SUBCKT NOR2 A B Z vdd gnd +Xnmos1 Z A gnd gnd nor_2_nmos183 +Xnmos2 Z B gnd gnd nor_2_nmos284 +Xpmos1 vdd A net1 vdd nor_2_pmos185 +Xpmos2 net1 B Z vdd nor_2_pmos286 +.ENDS NOR2 + +.SUBCKT test_bank1 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd +Xbitcell_array bl[0] br[0] bl[1] br[1] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd bitcell_array +Xprecharge_array bl[0] br[0] bl[1] br[1] clk_bar vdd precharge_array +Xsense_amp_array bl[0] br[0] bl[1] br[1] data_out[0] data_out[1] s_en vdd gnd sense_amp_array +Xwrite_driver_array data_in[0] data_in[1] bl[0] br[0] bl[1] br[1] w_en vdd gnd write_driver_array +Xdata_in_flop_array DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk_bar vdd gnd msf_data_in +Xtrigate_data_array data_out[0] data_out[1] DATA[0] DATA[1] tri_en tri_en_bar vdd gnd tri_gate_array +Xaddress_decoder A[0] A[1] A[2] A[3] decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] vdd gnd hierarchical_decoder +Xwordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk vdd gnd wordline_driver +Xaddress_flop_array ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] clk vdd gnd msf_address +.ENDS test_bank1 + +.SUBCKT testsram DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd gnd +Xbank0 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd test_bank1 +Xcontrol CSb WEb OEb s_en w_en tri_en tri_en_bar clk_bar clk vdd gnd control_logic +.ENDS testsram diff --git a/compiler/tests/golden/sram_2_16_1_freepdk45.v b/compiler/tests/golden/sram_2_16_1_freepdk45.v new file mode 100644 index 00000000..94bd09e2 --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_freepdk45.v @@ -0,0 +1,47 @@ +// OpenRAM SRAM model +// Words: 16 +// Word size: 2 + +module sram_2_16_1_freepdk45(DATA,ADDR,CSb,WEb,OEb,clk); + + parameter DATA_WIDTH = 2 ; + parameter ADDR_WIDTH = 4 ; + parameter RAM_DEPTH = 1 << ADDR_WIDTH; + parameter DELAY = 3 ; + + inout [DATA_WIDTH-1:0] DATA; + input [ADDR_WIDTH-1:0] ADDR; + input CSb; // active low chip select + input WEb; // active low write control + input OEb; // active output enable + input clk; // clock + + reg [DATA_WIDTH-1:0] data_out ; + reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; + + // Tri-State Buffer control + // output : When WEb = 1, oeb = 0, csb = 0 + assign DATA = (!CSb && !OEb && WEb) ? data_out : 2'bz; + + // Memory Write Block + // Write Operation : When WEb = 0, CSb = 0 + always @ (posedge clk) + begin : MEM_WRITE + if ( !CSb && !WEb ) begin + mem[ADDR] = DATA; + $display($time," Writing %m ABUS=%b DATA=%b",ADDR,DATA); + end + end + + + // Memory Read Block + // Read Operation : When WEb = 1, CSb = 0 + always @ (posedge clk) + begin : MEM_READ + if (!CSb && WEb) begin + data_out <= #(DELAY) mem[ADDR]; + $display($time," Reading %m ABUS=%b DATA=%b",ADDR,mem[ADDR]); + end + end + +endmodule diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm.lef b/compiler/tests/golden/sram_2_16_1_scn3me_subm.lef new file mode 100644 index 00000000..a9f39de5 --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm.lef @@ -0,0 +1,6622 @@ +MACRO sram_2_16_1_scn3me_subm + CLASS RING ; + ORIGIN 66.9 0.0 ; + FOREIGN sram 0.0 0.0 ; + SIZE 222.3 BY 459.3 ; + SYMMETRY X Y R90 ; + PIN vdd + DIRECTION INOUT ; + USE POWER ; + SHAPE ABUTMENT ; + PORT + Layer metal1 ; + RECT 0.0 0.0 9.0 459.3 ; + RECT 147.0 0.0 156.0 459.3 ; + END + END vdd + PIN gnd + DIRECTION INOUT ; + USE GROUND ; + SHAPE ABUTMENT ; + PORT + Layer metal2 ; + RECT 95.1 0.0 104.1 459.3 ; + END + END gnd + PIN DATA[0] + DIRECTION INOUT ; + PORT + Layer metal3 ; + RECT 128.25 0.0 129.75 30.15 ; + RECT 128.25 0.0 130.05 1.8 ; + END + END DATA[0] + PIN DATA[1] + DIRECTION INOUT ; + PORT + Layer metal3 ; + RECT 138.45 0.0 139.95 30.15 ; + RECT 138.45 0.0 140.25 1.8 ; + END + END DATA[1] + PIN ADDR[0] + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT 0.0 73.95 17.1 75.45 ; + END + END ADDR[0] + PIN ADDR[1] + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT 0.0 63.75 17.1 65.25 ; + END + END ADDR[1] + PIN ADDR[2] + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT 0.0 53.55 17.1 55.05 ; + END + END ADDR[2] + PIN ADDR[3] + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT 0.0 43.35 17.1 44.85 ; + END + END ADDR[3] + PIN CSb + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT -62.1 86.4 -60.3 88.2 ; + END + END CSb + PIN OEb + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT -41.7 86.4 -39.9 88.2 ; + END + END OEb + PIN WEb + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT -51.9 86.4 -50.1 88.2 ; + END + END WEb + PIN clk + DIRECTION INPUT ; + PORT + Layer metal3 ; + RECT -27.15 85.5 105.9 87.0 ; + RECT -27.6 85.5 -25.8 87.3 ; + END + END clk + OBS + Layer metal1 ; + RECT -10.2 292.65 0.0 293.55 ; + RECT 147.0 0.0 156.0 459.3 ; + RECT 0.0 0.0 9.0 459.3 ; + RECT 51.0 206.4 51.9 209.7 ; + RECT 51.0 219.9 51.9 223.2 ; + RECT 51.0 235.8 51.9 239.1 ; + RECT 51.0 249.3 51.9 252.6 ; + RECT 51.0 265.2 51.9 268.5 ; + RECT 51.0 278.7 51.9 282.0 ; + RECT 51.0 294.6 51.9 297.9 ; + RECT 51.0 308.1 51.9 311.4 ; + RECT 51.0 324.0 51.9 327.3 ; + RECT 51.0 337.5 51.9 340.8 ; + RECT 51.0 353.4 51.9 356.7 ; + RECT 51.0 366.9 51.9 370.2 ; + RECT 51.0 382.8 51.9 386.1 ; + RECT 51.0 396.3 51.9 399.6 ; + RECT 51.0 412.2 51.9 415.5 ; + RECT 51.0 425.7 51.9 429.0 ; + RECT 88.5 214.35 123.9 215.25 ; + RECT 88.5 243.75 123.9 244.65 ; + RECT 88.5 273.15 123.9 274.05 ; + RECT 88.5 302.55 123.9 303.45 ; + RECT 88.5 331.95 123.9 332.85 ; + RECT 88.5 361.35 123.9 362.25 ; + RECT 88.5 390.75 123.9 391.65 ; + RECT 88.5 420.15 123.9 421.05 ; + RECT 74.4 88.8 91.5 89.7 ; + RECT 74.4 104.7 88.8 105.6 ; + RECT 74.4 147.6 86.1 148.5 ; + RECT 74.4 163.5 83.4 164.4 ; + RECT 114.0 33.3 129.0 34.2 ; + RECT 108.6 28.65 129.3 29.55 ; + RECT 111.3 26.25 129.3 27.15 ; + RECT 114.0 446.4 123.9 447.3 ; + RECT 116.7 97.95 123.9 98.85 ; + RECT 119.4 196.05 123.9 196.95 ; + RECT 123.9 213.75 147.0 214.65 ; + RECT 123.9 243.15 147.0 244.05 ; + RECT 123.9 272.55 147.0 273.45 ; + RECT 123.9 301.95 147.0 302.85 ; + RECT 123.9 331.35 147.0 332.25 ; + RECT 123.9 360.75 147.0 361.65 ; + RECT 123.9 390.15 147.0 391.05 ; + RECT 123.9 419.55 147.0 420.45 ; + RECT 123.9 458.4 147.0 459.3 ; + RECT 123.9 168.75 147.0 169.65 ; + RECT 123.9 100.05 147.0 100.95 ; + RECT 123.9 87.3 147.0 88.2 ; + RECT 129.3 10.35 147.0 11.25 ; + RECT 138.9 10.35 147.0 11.25 ; + RECT 0.0 214.35 29.4 215.25 ; + RECT 0.0 243.75 29.4 244.65 ; + RECT 0.0 273.15 29.4 274.05 ; + RECT 0.0 302.55 29.4 303.45 ; + RECT 0.0 331.95 29.4 332.85 ; + RECT 0.0 361.35 29.4 362.25 ; + RECT 0.0 390.75 29.4 391.65 ; + RECT 0.0 420.15 29.4 421.05 ; + RECT 0.0 126.15 29.4 127.05 ; + RECT 0.0 184.95 29.4 185.85 ; + RECT 95.1 437.1 144.3 438.0 ; + RECT 101.7 8.1 144.3 9.0 ; + RECT 29.4 140.85 95.1 141.75 ; + RECT 29.4 199.65 95.1 200.55 ; + RECT 75.3 69.15 95.1 70.05 ; + RECT 75.3 69.15 95.1 70.05 ; + RECT 75.3 48.75 95.1 49.65 ; + RECT 75.3 48.75 95.1 49.65 ; + RECT 123.3 213.6 134.7 214.8 ; + RECT 124.5 211.5 125.7 213.6 ; + RECT 127.5 211.5 128.7 212.7 ; + RECT 130.5 211.5 131.7 212.7 ; + RECT 133.5 211.5 134.7 213.6 ; + RECT 127.2 210.6 128.4 211.5 ; + RECT 124.5 204.9 125.7 210.6 ; + RECT 127.2 209.4 129.6 210.6 ; + RECT 127.2 206.1 128.4 209.4 ; + RECT 130.8 207.9 132.0 211.5 ; + RECT 130.2 206.7 132.0 207.9 ; + RECT 130.8 206.1 132.0 206.7 ; + RECT 126.9 204.9 128.1 206.1 ; + RECT 131.1 204.9 132.3 206.1 ; + RECT 133.5 204.9 134.7 210.6 ; + RECT 129.0 203.4 130.2 203.7 ; + RECT 123.3 202.2 134.7 203.4 ; + RECT 125.4 200.1 128.1 201.3 ; + RECT 129.6 200.1 132.3 201.3 ; + RECT 123.3 214.8 134.7 216.0 ; + RECT 124.5 216.0 125.7 218.1 ; + RECT 127.5 216.9 128.7 218.1 ; + RECT 130.5 216.9 131.7 218.1 ; + RECT 133.5 216.0 134.7 218.1 ; + RECT 127.2 218.1 128.4 219.0 ; + RECT 124.5 219.0 125.7 224.7 ; + RECT 127.2 219.0 129.6 220.2 ; + RECT 127.2 220.2 128.4 223.5 ; + RECT 130.8 218.1 132.0 221.7 ; + RECT 130.2 221.7 132.0 222.9 ; + RECT 130.8 222.9 132.0 223.5 ; + RECT 126.9 223.5 128.1 224.7 ; + RECT 131.1 223.5 132.3 224.7 ; + RECT 133.5 219.0 134.7 224.7 ; + RECT 129.0 225.9 130.2 226.2 ; + RECT 123.3 226.2 134.7 227.4 ; + RECT 125.4 228.3 128.1 229.5 ; + RECT 129.6 228.3 132.3 229.5 ; + RECT 123.3 243.0 134.7 244.2 ; + RECT 124.5 240.9 125.7 243.0 ; + RECT 127.5 240.9 128.7 242.1 ; + RECT 130.5 240.9 131.7 242.1 ; + RECT 133.5 240.9 134.7 243.0 ; + RECT 127.2 240.0 128.4 240.9 ; + RECT 124.5 234.3 125.7 240.0 ; + RECT 127.2 238.8 129.6 240.0 ; + RECT 127.2 235.5 128.4 238.8 ; + RECT 130.8 237.3 132.0 240.9 ; + RECT 130.2 236.1 132.0 237.3 ; + RECT 130.8 235.5 132.0 236.1 ; + RECT 126.9 234.3 128.1 235.5 ; + RECT 131.1 234.3 132.3 235.5 ; + RECT 133.5 234.3 134.7 240.0 ; + RECT 129.0 232.8 130.2 233.1 ; + RECT 123.3 231.6 134.7 232.8 ; + RECT 125.4 229.5 128.1 230.7 ; + RECT 129.6 229.5 132.3 230.7 ; + RECT 123.3 244.2 134.7 245.4 ; + RECT 124.5 245.4 125.7 247.5 ; + RECT 127.5 246.3 128.7 247.5 ; + RECT 130.5 246.3 131.7 247.5 ; + RECT 133.5 245.4 134.7 247.5 ; + RECT 127.2 247.5 128.4 248.4 ; + RECT 124.5 248.4 125.7 254.1 ; + RECT 127.2 248.4 129.6 249.6 ; + RECT 127.2 249.6 128.4 252.9 ; + RECT 130.8 247.5 132.0 251.1 ; + RECT 130.2 251.1 132.0 252.3 ; + RECT 130.8 252.3 132.0 252.9 ; + RECT 126.9 252.9 128.1 254.1 ; + RECT 131.1 252.9 132.3 254.1 ; + RECT 133.5 248.4 134.7 254.1 ; + RECT 129.0 255.3 130.2 255.6 ; + RECT 123.3 255.6 134.7 256.8 ; + RECT 125.4 257.7 128.1 258.9 ; + RECT 129.6 257.7 132.3 258.9 ; + RECT 123.3 272.4 134.7 273.6 ; + RECT 124.5 270.3 125.7 272.4 ; + RECT 127.5 270.3 128.7 271.5 ; + RECT 130.5 270.3 131.7 271.5 ; + RECT 133.5 270.3 134.7 272.4 ; + RECT 127.2 269.4 128.4 270.3 ; + RECT 124.5 263.7 125.7 269.4 ; + RECT 127.2 268.2 129.6 269.4 ; + RECT 127.2 264.9 128.4 268.2 ; + RECT 130.8 266.7 132.0 270.3 ; + RECT 130.2 265.5 132.0 266.7 ; + RECT 130.8 264.9 132.0 265.5 ; + RECT 126.9 263.7 128.1 264.9 ; + RECT 131.1 263.7 132.3 264.9 ; + RECT 133.5 263.7 134.7 269.4 ; + RECT 129.0 262.2 130.2 262.5 ; + RECT 123.3 261.0 134.7 262.2 ; + RECT 125.4 258.9 128.1 260.1 ; + RECT 129.6 258.9 132.3 260.1 ; + RECT 123.3 273.6 134.7 274.8 ; + RECT 124.5 274.8 125.7 276.9 ; + RECT 127.5 275.7 128.7 276.9 ; + RECT 130.5 275.7 131.7 276.9 ; + RECT 133.5 274.8 134.7 276.9 ; + RECT 127.2 276.9 128.4 277.8 ; + RECT 124.5 277.8 125.7 283.5 ; + RECT 127.2 277.8 129.6 279.0 ; + RECT 127.2 279.0 128.4 282.3 ; + RECT 130.8 276.9 132.0 280.5 ; + RECT 130.2 280.5 132.0 281.7 ; + RECT 130.8 281.7 132.0 282.3 ; + RECT 126.9 282.3 128.1 283.5 ; + RECT 131.1 282.3 132.3 283.5 ; + RECT 133.5 277.8 134.7 283.5 ; + RECT 129.0 284.7 130.2 285.0 ; + RECT 123.3 285.0 134.7 286.2 ; + RECT 125.4 287.1 128.1 288.3 ; + RECT 129.6 287.1 132.3 288.3 ; + RECT 123.3 301.8 134.7 303.0 ; + RECT 124.5 299.7 125.7 301.8 ; + RECT 127.5 299.7 128.7 300.9 ; + RECT 130.5 299.7 131.7 300.9 ; + RECT 133.5 299.7 134.7 301.8 ; + RECT 127.2 298.8 128.4 299.7 ; + RECT 124.5 293.1 125.7 298.8 ; + RECT 127.2 297.6 129.6 298.8 ; + RECT 127.2 294.3 128.4 297.6 ; + RECT 130.8 296.1 132.0 299.7 ; + RECT 130.2 294.9 132.0 296.1 ; + RECT 130.8 294.3 132.0 294.9 ; + RECT 126.9 293.1 128.1 294.3 ; + RECT 131.1 293.1 132.3 294.3 ; + RECT 133.5 293.1 134.7 298.8 ; + RECT 129.0 291.6 130.2 291.9 ; + RECT 123.3 290.4 134.7 291.6 ; + RECT 125.4 288.3 128.1 289.5 ; + RECT 129.6 288.3 132.3 289.5 ; + RECT 123.3 303.0 134.7 304.2 ; + RECT 124.5 304.2 125.7 306.3 ; + RECT 127.5 305.1 128.7 306.3 ; + RECT 130.5 305.1 131.7 306.3 ; + RECT 133.5 304.2 134.7 306.3 ; + RECT 127.2 306.3 128.4 307.2 ; + RECT 124.5 307.2 125.7 312.9 ; + RECT 127.2 307.2 129.6 308.4 ; + RECT 127.2 308.4 128.4 311.7 ; + RECT 130.8 306.3 132.0 309.9 ; + RECT 130.2 309.9 132.0 311.1 ; + RECT 130.8 311.1 132.0 311.7 ; + RECT 126.9 311.7 128.1 312.9 ; + RECT 131.1 311.7 132.3 312.9 ; + RECT 133.5 307.2 134.7 312.9 ; + RECT 129.0 314.1 130.2 314.4 ; + RECT 123.3 314.4 134.7 315.6 ; + RECT 125.4 316.5 128.1 317.7 ; + RECT 129.6 316.5 132.3 317.7 ; + RECT 123.3 331.2 134.7 332.4 ; + RECT 124.5 329.1 125.7 331.2 ; + RECT 127.5 329.1 128.7 330.3 ; + RECT 130.5 329.1 131.7 330.3 ; + RECT 133.5 329.1 134.7 331.2 ; + RECT 127.2 328.2 128.4 329.1 ; + RECT 124.5 322.5 125.7 328.2 ; + RECT 127.2 327.0 129.6 328.2 ; + RECT 127.2 323.7 128.4 327.0 ; + RECT 130.8 325.5 132.0 329.1 ; + RECT 130.2 324.3 132.0 325.5 ; + RECT 130.8 323.7 132.0 324.3 ; + RECT 126.9 322.5 128.1 323.7 ; + RECT 131.1 322.5 132.3 323.7 ; + RECT 133.5 322.5 134.7 328.2 ; + RECT 129.0 321.0 130.2 321.3 ; + RECT 123.3 319.8 134.7 321.0 ; + RECT 125.4 317.7 128.1 318.9 ; + RECT 129.6 317.7 132.3 318.9 ; + RECT 123.3 332.4 134.7 333.6 ; + RECT 124.5 333.6 125.7 335.7 ; + RECT 127.5 334.5 128.7 335.7 ; + RECT 130.5 334.5 131.7 335.7 ; + RECT 133.5 333.6 134.7 335.7 ; + RECT 127.2 335.7 128.4 336.6 ; + RECT 124.5 336.6 125.7 342.3 ; + RECT 127.2 336.6 129.6 337.8 ; + RECT 127.2 337.8 128.4 341.1 ; + RECT 130.8 335.7 132.0 339.3 ; + RECT 130.2 339.3 132.0 340.5 ; + RECT 130.8 340.5 132.0 341.1 ; + RECT 126.9 341.1 128.1 342.3 ; + RECT 131.1 341.1 132.3 342.3 ; + RECT 133.5 336.6 134.7 342.3 ; + RECT 129.0 343.5 130.2 343.8 ; + RECT 123.3 343.8 134.7 345.0 ; + RECT 125.4 345.9 128.1 347.1 ; + RECT 129.6 345.9 132.3 347.1 ; + RECT 123.3 360.6 134.7 361.8 ; + RECT 124.5 358.5 125.7 360.6 ; + RECT 127.5 358.5 128.7 359.7 ; + RECT 130.5 358.5 131.7 359.7 ; + RECT 133.5 358.5 134.7 360.6 ; + RECT 127.2 357.6 128.4 358.5 ; + RECT 124.5 351.9 125.7 357.6 ; + RECT 127.2 356.4 129.6 357.6 ; + RECT 127.2 353.1 128.4 356.4 ; + RECT 130.8 354.9 132.0 358.5 ; + RECT 130.2 353.7 132.0 354.9 ; + RECT 130.8 353.1 132.0 353.7 ; + RECT 126.9 351.9 128.1 353.1 ; + RECT 131.1 351.9 132.3 353.1 ; + RECT 133.5 351.9 134.7 357.6 ; + RECT 129.0 350.4 130.2 350.7 ; + RECT 123.3 349.2 134.7 350.4 ; + RECT 125.4 347.1 128.1 348.3 ; + RECT 129.6 347.1 132.3 348.3 ; + RECT 123.3 361.8 134.7 363.0 ; + RECT 124.5 363.0 125.7 365.1 ; + RECT 127.5 363.9 128.7 365.1 ; + RECT 130.5 363.9 131.7 365.1 ; + RECT 133.5 363.0 134.7 365.1 ; + RECT 127.2 365.1 128.4 366.0 ; + RECT 124.5 366.0 125.7 371.7 ; + RECT 127.2 366.0 129.6 367.2 ; + RECT 127.2 367.2 128.4 370.5 ; + RECT 130.8 365.1 132.0 368.7 ; + RECT 130.2 368.7 132.0 369.9 ; + RECT 130.8 369.9 132.0 370.5 ; + RECT 126.9 370.5 128.1 371.7 ; + RECT 131.1 370.5 132.3 371.7 ; + RECT 133.5 366.0 134.7 371.7 ; + RECT 129.0 372.9 130.2 373.2 ; + RECT 123.3 373.2 134.7 374.4 ; + RECT 125.4 375.3 128.1 376.5 ; + RECT 129.6 375.3 132.3 376.5 ; + RECT 123.3 390.0 134.7 391.2 ; + RECT 124.5 387.9 125.7 390.0 ; + RECT 127.5 387.9 128.7 389.1 ; + RECT 130.5 387.9 131.7 389.1 ; + RECT 133.5 387.9 134.7 390.0 ; + RECT 127.2 387.0 128.4 387.9 ; + RECT 124.5 381.3 125.7 387.0 ; + RECT 127.2 385.8 129.6 387.0 ; + RECT 127.2 382.5 128.4 385.8 ; + RECT 130.8 384.3 132.0 387.9 ; + RECT 130.2 383.1 132.0 384.3 ; + RECT 130.8 382.5 132.0 383.1 ; + RECT 126.9 381.3 128.1 382.5 ; + RECT 131.1 381.3 132.3 382.5 ; + RECT 133.5 381.3 134.7 387.0 ; + RECT 129.0 379.8 130.2 380.1 ; + RECT 123.3 378.6 134.7 379.8 ; + RECT 125.4 376.5 128.1 377.7 ; + RECT 129.6 376.5 132.3 377.7 ; + RECT 123.3 391.2 134.7 392.4 ; + RECT 124.5 392.4 125.7 394.5 ; + RECT 127.5 393.3 128.7 394.5 ; + RECT 130.5 393.3 131.7 394.5 ; + RECT 133.5 392.4 134.7 394.5 ; + RECT 127.2 394.5 128.4 395.4 ; + RECT 124.5 395.4 125.7 401.1 ; + RECT 127.2 395.4 129.6 396.6 ; + RECT 127.2 396.6 128.4 399.9 ; + RECT 130.8 394.5 132.0 398.1 ; + RECT 130.2 398.1 132.0 399.3 ; + RECT 130.8 399.3 132.0 399.9 ; + RECT 126.9 399.9 128.1 401.1 ; + RECT 131.1 399.9 132.3 401.1 ; + RECT 133.5 395.4 134.7 401.1 ; + RECT 129.0 402.3 130.2 402.6 ; + RECT 123.3 402.6 134.7 403.8 ; + RECT 125.4 404.7 128.1 405.9 ; + RECT 129.6 404.7 132.3 405.9 ; + RECT 123.3 419.4 134.7 420.6 ; + RECT 124.5 417.3 125.7 419.4 ; + RECT 127.5 417.3 128.7 418.5 ; + RECT 130.5 417.3 131.7 418.5 ; + RECT 133.5 417.3 134.7 419.4 ; + RECT 127.2 416.4 128.4 417.3 ; + RECT 124.5 410.7 125.7 416.4 ; + RECT 127.2 415.2 129.6 416.4 ; + RECT 127.2 411.9 128.4 415.2 ; + RECT 130.8 413.7 132.0 417.3 ; + RECT 130.2 412.5 132.0 413.7 ; + RECT 130.8 411.9 132.0 412.5 ; + RECT 126.9 410.7 128.1 411.9 ; + RECT 131.1 410.7 132.3 411.9 ; + RECT 133.5 410.7 134.7 416.4 ; + RECT 129.0 409.2 130.2 409.5 ; + RECT 123.3 408.0 134.7 409.2 ; + RECT 125.4 405.9 128.1 407.1 ; + RECT 129.6 405.9 132.3 407.1 ; + RECT 123.3 420.6 134.7 421.8 ; + RECT 124.5 421.8 125.7 423.9 ; + RECT 127.5 422.7 128.7 423.9 ; + RECT 130.5 422.7 131.7 423.9 ; + RECT 133.5 421.8 134.7 423.9 ; + RECT 127.2 423.9 128.4 424.8 ; + RECT 124.5 424.8 125.7 430.5 ; + RECT 127.2 424.8 129.6 426.0 ; + RECT 127.2 426.0 128.4 429.3 ; + RECT 130.8 423.9 132.0 427.5 ; + RECT 130.2 427.5 132.0 428.7 ; + RECT 130.8 428.7 132.0 429.3 ; + RECT 126.9 429.3 128.1 430.5 ; + RECT 131.1 429.3 132.3 430.5 ; + RECT 133.5 424.8 134.7 430.5 ; + RECT 129.0 431.7 130.2 432.0 ; + RECT 123.3 432.0 134.7 433.2 ; + RECT 125.4 434.1 128.1 435.3 ; + RECT 129.6 434.1 132.3 435.3 ; + RECT 133.5 213.6 144.9 214.8 ; + RECT 134.7 211.5 135.9 213.6 ; + RECT 137.7 211.5 138.9 212.7 ; + RECT 140.7 211.5 141.9 212.7 ; + RECT 143.7 211.5 144.9 213.6 ; + RECT 137.4 210.6 138.6 211.5 ; + RECT 134.7 204.9 135.9 210.6 ; + RECT 137.4 209.4 139.8 210.6 ; + RECT 137.4 206.1 138.6 209.4 ; + RECT 141.0 207.9 142.2 211.5 ; + RECT 140.4 206.7 142.2 207.9 ; + RECT 141.0 206.1 142.2 206.7 ; + RECT 137.1 204.9 138.3 206.1 ; + RECT 141.3 204.9 142.5 206.1 ; + RECT 143.7 204.9 144.9 210.6 ; + RECT 139.2 203.4 140.4 203.7 ; + RECT 133.5 202.2 144.9 203.4 ; + RECT 135.6 200.1 138.3 201.3 ; + RECT 139.8 200.1 142.5 201.3 ; + RECT 133.5 214.8 144.9 216.0 ; + RECT 134.7 216.0 135.9 218.1 ; + RECT 137.7 216.9 138.9 218.1 ; + RECT 140.7 216.9 141.9 218.1 ; + RECT 143.7 216.0 144.9 218.1 ; + RECT 137.4 218.1 138.6 219.0 ; + RECT 134.7 219.0 135.9 224.7 ; + RECT 137.4 219.0 139.8 220.2 ; + RECT 137.4 220.2 138.6 223.5 ; + RECT 141.0 218.1 142.2 221.7 ; + RECT 140.4 221.7 142.2 222.9 ; + RECT 141.0 222.9 142.2 223.5 ; + RECT 137.1 223.5 138.3 224.7 ; + RECT 141.3 223.5 142.5 224.7 ; + RECT 143.7 219.0 144.9 224.7 ; + RECT 139.2 225.9 140.4 226.2 ; + RECT 133.5 226.2 144.9 227.4 ; + RECT 135.6 228.3 138.3 229.5 ; + RECT 139.8 228.3 142.5 229.5 ; + RECT 133.5 243.0 144.9 244.2 ; + RECT 134.7 240.9 135.9 243.0 ; + RECT 137.7 240.9 138.9 242.1 ; + RECT 140.7 240.9 141.9 242.1 ; + RECT 143.7 240.9 144.9 243.0 ; + RECT 137.4 240.0 138.6 240.9 ; + RECT 134.7 234.3 135.9 240.0 ; + RECT 137.4 238.8 139.8 240.0 ; + RECT 137.4 235.5 138.6 238.8 ; + RECT 141.0 237.3 142.2 240.9 ; + RECT 140.4 236.1 142.2 237.3 ; + RECT 141.0 235.5 142.2 236.1 ; + RECT 137.1 234.3 138.3 235.5 ; + RECT 141.3 234.3 142.5 235.5 ; + RECT 143.7 234.3 144.9 240.0 ; + RECT 139.2 232.8 140.4 233.1 ; + RECT 133.5 231.6 144.9 232.8 ; + RECT 135.6 229.5 138.3 230.7 ; + RECT 139.8 229.5 142.5 230.7 ; + RECT 133.5 244.2 144.9 245.4 ; + RECT 134.7 245.4 135.9 247.5 ; + RECT 137.7 246.3 138.9 247.5 ; + RECT 140.7 246.3 141.9 247.5 ; + RECT 143.7 245.4 144.9 247.5 ; + RECT 137.4 247.5 138.6 248.4 ; + RECT 134.7 248.4 135.9 254.1 ; + RECT 137.4 248.4 139.8 249.6 ; + RECT 137.4 249.6 138.6 252.9 ; + RECT 141.0 247.5 142.2 251.1 ; + RECT 140.4 251.1 142.2 252.3 ; + RECT 141.0 252.3 142.2 252.9 ; + RECT 137.1 252.9 138.3 254.1 ; + RECT 141.3 252.9 142.5 254.1 ; + RECT 143.7 248.4 144.9 254.1 ; + RECT 139.2 255.3 140.4 255.6 ; + RECT 133.5 255.6 144.9 256.8 ; + RECT 135.6 257.7 138.3 258.9 ; + RECT 139.8 257.7 142.5 258.9 ; + RECT 133.5 272.4 144.9 273.6 ; + RECT 134.7 270.3 135.9 272.4 ; + RECT 137.7 270.3 138.9 271.5 ; + RECT 140.7 270.3 141.9 271.5 ; + RECT 143.7 270.3 144.9 272.4 ; + RECT 137.4 269.4 138.6 270.3 ; + RECT 134.7 263.7 135.9 269.4 ; + RECT 137.4 268.2 139.8 269.4 ; + RECT 137.4 264.9 138.6 268.2 ; + RECT 141.0 266.7 142.2 270.3 ; + RECT 140.4 265.5 142.2 266.7 ; + RECT 141.0 264.9 142.2 265.5 ; + RECT 137.1 263.7 138.3 264.9 ; + RECT 141.3 263.7 142.5 264.9 ; + RECT 143.7 263.7 144.9 269.4 ; + RECT 139.2 262.2 140.4 262.5 ; + RECT 133.5 261.0 144.9 262.2 ; + RECT 135.6 258.9 138.3 260.1 ; + RECT 139.8 258.9 142.5 260.1 ; + RECT 133.5 273.6 144.9 274.8 ; + RECT 134.7 274.8 135.9 276.9 ; + RECT 137.7 275.7 138.9 276.9 ; + RECT 140.7 275.7 141.9 276.9 ; + RECT 143.7 274.8 144.9 276.9 ; + RECT 137.4 276.9 138.6 277.8 ; + RECT 134.7 277.8 135.9 283.5 ; + RECT 137.4 277.8 139.8 279.0 ; + RECT 137.4 279.0 138.6 282.3 ; + RECT 141.0 276.9 142.2 280.5 ; + RECT 140.4 280.5 142.2 281.7 ; + RECT 141.0 281.7 142.2 282.3 ; + RECT 137.1 282.3 138.3 283.5 ; + RECT 141.3 282.3 142.5 283.5 ; + RECT 143.7 277.8 144.9 283.5 ; + RECT 139.2 284.7 140.4 285.0 ; + RECT 133.5 285.0 144.9 286.2 ; + RECT 135.6 287.1 138.3 288.3 ; + RECT 139.8 287.1 142.5 288.3 ; + RECT 133.5 301.8 144.9 303.0 ; + RECT 134.7 299.7 135.9 301.8 ; + RECT 137.7 299.7 138.9 300.9 ; + RECT 140.7 299.7 141.9 300.9 ; + RECT 143.7 299.7 144.9 301.8 ; + RECT 137.4 298.8 138.6 299.7 ; + RECT 134.7 293.1 135.9 298.8 ; + RECT 137.4 297.6 139.8 298.8 ; + RECT 137.4 294.3 138.6 297.6 ; + RECT 141.0 296.1 142.2 299.7 ; + RECT 140.4 294.9 142.2 296.1 ; + RECT 141.0 294.3 142.2 294.9 ; + RECT 137.1 293.1 138.3 294.3 ; + RECT 141.3 293.1 142.5 294.3 ; + RECT 143.7 293.1 144.9 298.8 ; + RECT 139.2 291.6 140.4 291.9 ; + RECT 133.5 290.4 144.9 291.6 ; + RECT 135.6 288.3 138.3 289.5 ; + RECT 139.8 288.3 142.5 289.5 ; + RECT 133.5 303.0 144.9 304.2 ; + RECT 134.7 304.2 135.9 306.3 ; + RECT 137.7 305.1 138.9 306.3 ; + RECT 140.7 305.1 141.9 306.3 ; + RECT 143.7 304.2 144.9 306.3 ; + RECT 137.4 306.3 138.6 307.2 ; + RECT 134.7 307.2 135.9 312.9 ; + RECT 137.4 307.2 139.8 308.4 ; + RECT 137.4 308.4 138.6 311.7 ; + RECT 141.0 306.3 142.2 309.9 ; + RECT 140.4 309.9 142.2 311.1 ; + RECT 141.0 311.1 142.2 311.7 ; + RECT 137.1 311.7 138.3 312.9 ; + RECT 141.3 311.7 142.5 312.9 ; + RECT 143.7 307.2 144.9 312.9 ; + RECT 139.2 314.1 140.4 314.4 ; + RECT 133.5 314.4 144.9 315.6 ; + RECT 135.6 316.5 138.3 317.7 ; + RECT 139.8 316.5 142.5 317.7 ; + RECT 133.5 331.2 144.9 332.4 ; + RECT 134.7 329.1 135.9 331.2 ; + RECT 137.7 329.1 138.9 330.3 ; + RECT 140.7 329.1 141.9 330.3 ; + RECT 143.7 329.1 144.9 331.2 ; + RECT 137.4 328.2 138.6 329.1 ; + RECT 134.7 322.5 135.9 328.2 ; + RECT 137.4 327.0 139.8 328.2 ; + RECT 137.4 323.7 138.6 327.0 ; + RECT 141.0 325.5 142.2 329.1 ; + RECT 140.4 324.3 142.2 325.5 ; + RECT 141.0 323.7 142.2 324.3 ; + RECT 137.1 322.5 138.3 323.7 ; + RECT 141.3 322.5 142.5 323.7 ; + RECT 143.7 322.5 144.9 328.2 ; + RECT 139.2 321.0 140.4 321.3 ; + RECT 133.5 319.8 144.9 321.0 ; + RECT 135.6 317.7 138.3 318.9 ; + RECT 139.8 317.7 142.5 318.9 ; + RECT 133.5 332.4 144.9 333.6 ; + RECT 134.7 333.6 135.9 335.7 ; + RECT 137.7 334.5 138.9 335.7 ; + RECT 140.7 334.5 141.9 335.7 ; + RECT 143.7 333.6 144.9 335.7 ; + RECT 137.4 335.7 138.6 336.6 ; + RECT 134.7 336.6 135.9 342.3 ; + RECT 137.4 336.6 139.8 337.8 ; + RECT 137.4 337.8 138.6 341.1 ; + RECT 141.0 335.7 142.2 339.3 ; + RECT 140.4 339.3 142.2 340.5 ; + RECT 141.0 340.5 142.2 341.1 ; + RECT 137.1 341.1 138.3 342.3 ; + RECT 141.3 341.1 142.5 342.3 ; + RECT 143.7 336.6 144.9 342.3 ; + RECT 139.2 343.5 140.4 343.8 ; + RECT 133.5 343.8 144.9 345.0 ; + RECT 135.6 345.9 138.3 347.1 ; + RECT 139.8 345.9 142.5 347.1 ; + RECT 133.5 360.6 144.9 361.8 ; + RECT 134.7 358.5 135.9 360.6 ; + RECT 137.7 358.5 138.9 359.7 ; + RECT 140.7 358.5 141.9 359.7 ; + RECT 143.7 358.5 144.9 360.6 ; + RECT 137.4 357.6 138.6 358.5 ; + RECT 134.7 351.9 135.9 357.6 ; + RECT 137.4 356.4 139.8 357.6 ; + RECT 137.4 353.1 138.6 356.4 ; + RECT 141.0 354.9 142.2 358.5 ; + RECT 140.4 353.7 142.2 354.9 ; + RECT 141.0 353.1 142.2 353.7 ; + RECT 137.1 351.9 138.3 353.1 ; + RECT 141.3 351.9 142.5 353.1 ; + RECT 143.7 351.9 144.9 357.6 ; + RECT 139.2 350.4 140.4 350.7 ; + RECT 133.5 349.2 144.9 350.4 ; + RECT 135.6 347.1 138.3 348.3 ; + RECT 139.8 347.1 142.5 348.3 ; + RECT 133.5 361.8 144.9 363.0 ; + RECT 134.7 363.0 135.9 365.1 ; + RECT 137.7 363.9 138.9 365.1 ; + RECT 140.7 363.9 141.9 365.1 ; + RECT 143.7 363.0 144.9 365.1 ; + RECT 137.4 365.1 138.6 366.0 ; + RECT 134.7 366.0 135.9 371.7 ; + RECT 137.4 366.0 139.8 367.2 ; + RECT 137.4 367.2 138.6 370.5 ; + RECT 141.0 365.1 142.2 368.7 ; + RECT 140.4 368.7 142.2 369.9 ; + RECT 141.0 369.9 142.2 370.5 ; + RECT 137.1 370.5 138.3 371.7 ; + RECT 141.3 370.5 142.5 371.7 ; + RECT 143.7 366.0 144.9 371.7 ; + RECT 139.2 372.9 140.4 373.2 ; + RECT 133.5 373.2 144.9 374.4 ; + RECT 135.6 375.3 138.3 376.5 ; + RECT 139.8 375.3 142.5 376.5 ; + RECT 133.5 390.0 144.9 391.2 ; + RECT 134.7 387.9 135.9 390.0 ; + RECT 137.7 387.9 138.9 389.1 ; + RECT 140.7 387.9 141.9 389.1 ; + RECT 143.7 387.9 144.9 390.0 ; + RECT 137.4 387.0 138.6 387.9 ; + RECT 134.7 381.3 135.9 387.0 ; + RECT 137.4 385.8 139.8 387.0 ; + RECT 137.4 382.5 138.6 385.8 ; + RECT 141.0 384.3 142.2 387.9 ; + RECT 140.4 383.1 142.2 384.3 ; + RECT 141.0 382.5 142.2 383.1 ; + RECT 137.1 381.3 138.3 382.5 ; + RECT 141.3 381.3 142.5 382.5 ; + RECT 143.7 381.3 144.9 387.0 ; + RECT 139.2 379.8 140.4 380.1 ; + RECT 133.5 378.6 144.9 379.8 ; + RECT 135.6 376.5 138.3 377.7 ; + RECT 139.8 376.5 142.5 377.7 ; + RECT 133.5 391.2 144.9 392.4 ; + RECT 134.7 392.4 135.9 394.5 ; + RECT 137.7 393.3 138.9 394.5 ; + RECT 140.7 393.3 141.9 394.5 ; + RECT 143.7 392.4 144.9 394.5 ; + RECT 137.4 394.5 138.6 395.4 ; + RECT 134.7 395.4 135.9 401.1 ; + RECT 137.4 395.4 139.8 396.6 ; + RECT 137.4 396.6 138.6 399.9 ; + RECT 141.0 394.5 142.2 398.1 ; + RECT 140.4 398.1 142.2 399.3 ; + RECT 141.0 399.3 142.2 399.9 ; + RECT 137.1 399.9 138.3 401.1 ; + RECT 141.3 399.9 142.5 401.1 ; + RECT 143.7 395.4 144.9 401.1 ; + RECT 139.2 402.3 140.4 402.6 ; + RECT 133.5 402.6 144.9 403.8 ; + RECT 135.6 404.7 138.3 405.9 ; + RECT 139.8 404.7 142.5 405.9 ; + RECT 133.5 419.4 144.9 420.6 ; + RECT 134.7 417.3 135.9 419.4 ; + RECT 137.7 417.3 138.9 418.5 ; + RECT 140.7 417.3 141.9 418.5 ; + RECT 143.7 417.3 144.9 419.4 ; + RECT 137.4 416.4 138.6 417.3 ; + RECT 134.7 410.7 135.9 416.4 ; + RECT 137.4 415.2 139.8 416.4 ; + RECT 137.4 411.9 138.6 415.2 ; + RECT 141.0 413.7 142.2 417.3 ; + RECT 140.4 412.5 142.2 413.7 ; + RECT 141.0 411.9 142.2 412.5 ; + RECT 137.1 410.7 138.3 411.9 ; + RECT 141.3 410.7 142.5 411.9 ; + RECT 143.7 410.7 144.9 416.4 ; + RECT 139.2 409.2 140.4 409.5 ; + RECT 133.5 408.0 144.9 409.2 ; + RECT 135.6 405.9 138.3 407.1 ; + RECT 139.8 405.9 142.5 407.1 ; + RECT 133.5 420.6 144.9 421.8 ; + RECT 134.7 421.8 135.9 423.9 ; + RECT 137.7 422.7 138.9 423.9 ; + RECT 140.7 422.7 141.9 423.9 ; + RECT 143.7 421.8 144.9 423.9 ; + RECT 137.4 423.9 138.6 424.8 ; + RECT 134.7 424.8 135.9 430.5 ; + RECT 137.4 424.8 139.8 426.0 ; + RECT 137.4 426.0 138.6 429.3 ; + RECT 141.0 423.9 142.2 427.5 ; + RECT 140.4 427.5 142.2 428.7 ; + RECT 141.0 428.7 142.2 429.3 ; + RECT 137.1 429.3 138.3 430.5 ; + RECT 141.3 429.3 142.5 430.5 ; + RECT 143.7 424.8 144.9 430.5 ; + RECT 139.2 431.7 140.4 432.0 ; + RECT 133.5 432.0 144.9 433.2 ; + RECT 135.6 434.1 138.3 435.3 ; + RECT 139.8 434.1 142.5 435.3 ; + RECT 123.9 458.4 144.3 459.3 ; + RECT 123.9 446.4 144.3 447.3 ; + RECT 123.9 446.4 134.1 447.3 ; + RECT 123.9 458.4 134.1 459.3 ; + RECT 129.3 450.9 130.2 459.3 ; + RECT 126.9 442.5 128.1 443.7 ; + RECT 129.3 442.5 130.5 443.7 ; + RECT 126.9 450.9 128.1 452.1 ; + RECT 129.3 450.9 130.5 452.1 ; + RECT 129.3 450.9 130.5 452.1 ; + RECT 131.7 450.9 132.9 452.1 ; + RECT 127.8 446.4 129.0 447.6 ; + RECT 129.3 456.3 130.5 457.5 ; + RECT 126.9 450.9 128.1 452.1 ; + RECT 131.7 450.9 132.9 452.1 ; + RECT 126.9 442.5 128.1 443.7 ; + RECT 129.3 442.5 130.5 443.7 ; + RECT 134.1 446.4 144.3 447.3 ; + RECT 134.1 458.4 144.3 459.3 ; + RECT 139.5 450.9 140.4 459.3 ; + RECT 137.1 442.5 138.3 443.7 ; + RECT 139.5 442.5 140.7 443.7 ; + RECT 137.1 450.9 138.3 452.1 ; + RECT 139.5 450.9 140.7 452.1 ; + RECT 139.5 450.9 140.7 452.1 ; + RECT 141.9 450.9 143.1 452.1 ; + RECT 138.0 446.4 139.2 447.6 ; + RECT 139.5 456.3 140.7 457.5 ; + RECT 137.1 450.9 138.3 452.1 ; + RECT 141.9 450.9 143.1 452.1 ; + RECT 137.1 442.5 138.3 443.7 ; + RECT 139.5 442.5 140.7 443.7 ; + RECT 123.9 168.75 144.3 169.65 ; + RECT 123.9 193.95 144.3 194.85 ; + RECT 123.9 196.05 144.3 196.95 ; + RECT 123.3 195.9 134.7 197.1 ; + RECT 123.3 193.8 134.7 195.0 ; + RECT 128.7 190.2 129.9 192.9 ; + RECT 131.1 190.2 132.3 193.8 ; + RECT 133.5 192.6 134.7 193.8 ; + RECT 128.7 186.3 129.6 190.2 ; + RECT 126.0 171.9 127.2 186.3 ; + RECT 128.4 183.6 129.6 186.3 ; + RECT 130.8 182.7 132.0 186.3 ; + RECT 130.8 181.5 132.9 182.7 ; + RECT 128.4 174.6 129.6 180.0 ; + RECT 130.8 174.6 132.0 181.5 ; + RECT 128.4 173.7 129.3 174.6 ; + RECT 128.4 172.8 130.2 173.7 ; + RECT 126.0 170.7 127.8 171.9 ; + RECT 129.3 171.0 130.2 172.8 ; + RECT 129.3 169.8 130.5 171.0 ; + RECT 123.3 168.6 134.7 169.8 ; + RECT 126.6 167.4 127.8 167.7 ; + RECT 125.7 166.5 127.8 167.4 ; + RECT 132.6 166.5 134.1 167.7 ; + RECT 125.7 164.4 126.6 166.5 ; + RECT 127.8 164.4 129.0 165.6 ; + RECT 125.7 158.1 126.9 164.4 ; + RECT 124.8 157.2 126.9 158.1 ; + RECT 128.1 157.2 129.3 164.4 ; + RECT 130.5 157.2 131.7 165.6 ; + RECT 133.2 164.4 134.1 166.5 ; + RECT 132.9 157.2 134.1 164.4 ; + RECT 124.8 154.5 126.0 157.2 ; + RECT 133.5 195.9 144.9 197.1 ; + RECT 133.5 193.8 144.9 195.0 ; + RECT 138.9 190.2 140.1 192.9 ; + RECT 141.3 190.2 142.5 193.8 ; + RECT 143.7 192.6 144.9 193.8 ; + RECT 138.9 186.3 139.8 190.2 ; + RECT 136.2 171.9 137.4 186.3 ; + RECT 138.6 183.6 139.8 186.3 ; + RECT 141.0 182.7 142.2 186.3 ; + RECT 141.0 181.5 143.1 182.7 ; + RECT 138.6 174.6 139.8 180.0 ; + RECT 141.0 174.6 142.2 181.5 ; + RECT 138.6 173.7 139.5 174.6 ; + RECT 138.6 172.8 140.4 173.7 ; + RECT 136.2 170.7 138.0 171.9 ; + RECT 139.5 171.0 140.4 172.8 ; + RECT 139.5 169.8 140.7 171.0 ; + RECT 133.5 168.6 144.9 169.8 ; + RECT 136.8 167.4 138.0 167.7 ; + RECT 135.9 166.5 138.0 167.4 ; + RECT 142.8 166.5 144.3 167.7 ; + RECT 135.9 164.4 136.8 166.5 ; + RECT 138.0 164.4 139.2 165.6 ; + RECT 135.9 158.1 137.1 164.4 ; + RECT 135.0 157.2 137.1 158.1 ; + RECT 138.3 157.2 139.5 164.4 ; + RECT 140.7 157.2 141.9 165.6 ; + RECT 143.4 164.4 144.3 166.5 ; + RECT 143.1 157.2 144.3 164.4 ; + RECT 135.0 154.5 136.2 157.2 ; + RECT 123.9 97.95 144.3 98.85 ; + RECT 123.9 100.05 144.3 100.95 ; + RECT 123.9 95.85 144.3 96.75 ; + RECT 125.4 147.9 126.6 149.1 ; + RECT 125.4 147.3 126.3 147.9 ; + RECT 125.1 143.7 126.3 147.3 ; + RECT 127.5 143.7 128.7 147.3 ; + RECT 129.9 143.7 131.1 148.5 ; + RECT 132.3 144.9 133.8 146.1 ; + RECT 127.8 141.3 128.7 143.7 ; + RECT 125.1 128.1 126.3 140.7 ; + RECT 127.8 140.1 132.0 141.3 ; + RECT 127.2 137.7 132.0 138.9 ; + RECT 127.5 133.8 128.7 137.7 ; + RECT 129.9 133.2 131.1 135.0 ; + RECT 132.9 133.2 133.8 144.9 ; + RECT 129.9 132.0 133.8 133.2 ; + RECT 127.5 127.2 128.7 130.2 ; + RECT 129.9 128.1 131.1 132.0 ; + RECT 123.9 126.0 134.7 127.2 ; + RECT 125.4 121.8 126.6 124.8 ; + RECT 127.8 122.7 129.0 126.0 ; + RECT 130.2 121.8 131.4 124.8 ; + RECT 125.4 120.9 131.4 121.8 ; + RECT 125.4 115.2 126.6 120.9 ; + RECT 130.2 120.6 131.4 120.9 ; + RECT 130.2 119.4 132.0 120.6 ; + RECT 127.8 115.2 129.0 117.3 ; + RECT 130.2 116.4 131.4 117.3 ; + RECT 130.2 115.2 134.1 116.4 ; + RECT 130.8 113.1 133.2 114.3 ; + RECT 124.8 111.9 126.0 113.1 ; + RECT 125.1 109.8 126.0 111.9 ; + RECT 124.8 105.9 126.0 109.8 ; + RECT 127.2 107.7 128.4 109.8 ; + RECT 129.6 107.7 130.8 111.0 ; + RECT 124.8 105.0 128.4 105.9 ; + RECT 124.8 101.1 126.0 104.1 ; + RECT 127.2 102.0 128.4 105.0 ; + RECT 129.6 101.1 130.8 104.1 ; + RECT 132.0 102.0 133.2 113.1 ; + RECT 123.9 99.9 134.7 101.1 ; + RECT 123.9 97.8 134.7 99.0 ; + RECT 123.9 95.7 134.7 96.9 ; + RECT 127.5 93.6 129.9 94.8 ; + RECT 135.6 147.9 136.8 149.1 ; + RECT 135.6 147.3 136.5 147.9 ; + RECT 135.3 143.7 136.5 147.3 ; + RECT 137.7 143.7 138.9 147.3 ; + RECT 140.1 143.7 141.3 148.5 ; + RECT 142.5 144.9 144.0 146.1 ; + RECT 138.0 141.3 138.9 143.7 ; + RECT 135.3 128.1 136.5 140.7 ; + RECT 138.0 140.1 142.2 141.3 ; + RECT 137.4 137.7 142.2 138.9 ; + RECT 137.7 133.8 138.9 137.7 ; + RECT 140.1 133.2 141.3 135.0 ; + RECT 143.1 133.2 144.0 144.9 ; + RECT 140.1 132.0 144.0 133.2 ; + RECT 137.7 127.2 138.9 130.2 ; + RECT 140.1 128.1 141.3 132.0 ; + RECT 134.1 126.0 144.9 127.2 ; + RECT 135.6 121.8 136.8 124.8 ; + RECT 138.0 122.7 139.2 126.0 ; + RECT 140.4 121.8 141.6 124.8 ; + RECT 135.6 120.9 141.6 121.8 ; + RECT 135.6 115.2 136.8 120.9 ; + RECT 140.4 120.6 141.6 120.9 ; + RECT 140.4 119.4 142.2 120.6 ; + RECT 138.0 115.2 139.2 117.3 ; + RECT 140.4 116.4 141.6 117.3 ; + RECT 140.4 115.2 144.3 116.4 ; + RECT 141.0 113.1 143.4 114.3 ; + RECT 135.0 111.9 136.2 113.1 ; + RECT 135.3 109.8 136.2 111.9 ; + RECT 135.0 105.9 136.2 109.8 ; + RECT 137.4 107.7 138.6 109.8 ; + RECT 139.8 107.7 141.0 111.0 ; + RECT 135.0 105.0 138.6 105.9 ; + RECT 135.0 101.1 136.2 104.1 ; + RECT 137.4 102.0 138.6 105.0 ; + RECT 139.8 101.1 141.0 104.1 ; + RECT 142.2 102.0 143.4 113.1 ; + RECT 134.1 99.9 144.9 101.1 ; + RECT 134.1 97.8 144.9 99.0 ; + RECT 134.1 95.7 144.9 96.9 ; + RECT 137.7 93.6 140.1 94.8 ; + RECT 123.9 32.85 144.3 33.75 ; + RECT 123.9 87.3 144.3 88.2 ; + RECT 123.3 87.3 134.7 88.2 ; + RECT 123.3 84.0 124.5 87.3 ; + RECT 125.7 85.5 132.3 86.4 ; + RECT 125.7 85.2 128.7 85.5 ; + RECT 131.1 85.2 132.3 85.5 ; + RECT 123.3 82.8 127.5 84.0 ; + RECT 131.1 82.8 134.7 84.0 ; + RECT 123.3 79.2 124.5 82.8 ; + RECT 128.7 81.9 129.9 82.8 ; + RECT 128.7 81.6 131.1 81.9 ; + RECT 125.7 80.7 132.3 81.6 ; + RECT 125.7 80.4 127.5 80.7 ; + RECT 131.1 80.4 132.3 80.7 ; + RECT 123.3 78.0 127.5 79.2 ; + RECT 123.3 64.2 124.5 78.0 ; + RECT 129.0 77.4 130.2 79.8 ; + RECT 133.8 79.2 134.7 82.8 ; + RECT 131.1 78.0 134.7 79.2 ; + RECT 133.5 76.8 134.7 78.0 ; + RECT 125.7 74.4 126.9 75.6 ; + RECT 127.8 75.3 130.2 76.5 ; + RECT 125.7 73.5 132.3 74.4 ; + RECT 125.7 73.2 127.5 73.5 ; + RECT 131.1 73.2 132.3 73.5 ; + RECT 125.7 71.1 132.3 72.0 ; + RECT 125.7 70.8 127.5 71.1 ; + RECT 129.9 70.8 132.3 71.1 ; + RECT 125.7 68.7 132.3 69.6 ; + RECT 125.7 68.4 127.5 68.7 ; + RECT 129.9 68.4 132.3 68.7 ; + RECT 128.7 66.6 129.9 67.5 ; + RECT 125.7 65.7 132.3 66.6 ; + RECT 125.7 65.4 128.7 65.7 ; + RECT 131.1 65.4 132.3 65.7 ; + RECT 123.3 63.0 127.5 64.2 ; + RECT 123.3 58.5 124.5 63.0 ; + RECT 128.4 62.1 129.6 64.5 ; + RECT 133.8 64.2 134.7 76.8 ; + RECT 131.1 63.0 134.7 64.2 ; + RECT 125.7 60.9 126.9 62.1 ; + RECT 125.7 60.0 132.3 60.9 ; + RECT 125.7 59.7 127.5 60.0 ; + RECT 131.1 59.7 132.3 60.0 ; + RECT 133.8 58.5 134.7 63.0 ; + RECT 123.3 57.3 127.5 58.5 ; + RECT 123.3 53.7 124.5 57.3 ; + RECT 129.0 56.4 130.2 57.6 ; + RECT 131.1 57.3 134.7 58.5 ; + RECT 125.7 56.1 131.1 56.4 ; + RECT 125.7 55.5 132.3 56.1 ; + RECT 125.7 54.9 127.5 55.5 ; + RECT 129.9 55.2 132.3 55.5 ; + RECT 131.1 54.9 132.3 55.2 ; + RECT 123.3 52.5 127.5 53.7 ; + RECT 123.3 37.5 124.5 52.5 ; + RECT 129.0 51.9 130.2 54.3 ; + RECT 133.8 53.7 134.7 57.3 ; + RECT 131.1 52.5 134.7 53.7 ; + RECT 133.5 51.3 134.7 52.5 ; + RECT 125.7 48.0 126.9 49.2 ; + RECT 127.8 48.9 130.2 50.1 ; + RECT 125.7 47.1 132.3 48.0 ; + RECT 125.7 46.8 127.5 47.1 ; + RECT 131.1 46.8 132.3 47.1 ; + RECT 125.7 44.7 132.3 45.6 ; + RECT 125.7 44.4 127.5 44.7 ; + RECT 129.9 44.4 132.3 44.7 ; + RECT 125.7 42.3 132.3 43.2 ; + RECT 125.7 42.0 127.5 42.3 ; + RECT 129.9 42.0 132.3 42.3 ; + RECT 128.7 40.2 129.9 41.1 ; + RECT 125.7 39.3 132.3 40.2 ; + RECT 125.7 39.0 128.7 39.3 ; + RECT 131.1 39.0 132.3 39.3 ; + RECT 133.8 37.8 134.7 51.3 ; + RECT 125.7 37.5 127.5 37.8 ; + RECT 123.3 36.6 127.5 37.5 ; + RECT 131.1 36.9 134.7 37.8 ; + RECT 131.1 36.6 132.3 36.9 ; + RECT 126.3 35.1 127.5 36.6 ; + RECT 128.4 34.2 129.6 35.1 ; + RECT 123.3 33.3 134.7 34.2 ; + RECT 133.5 87.3 144.9 88.2 ; + RECT 143.7 84.0 144.9 87.3 ; + RECT 135.9 85.5 142.5 86.4 ; + RECT 139.5 85.2 142.5 85.5 ; + RECT 135.9 85.2 137.1 85.5 ; + RECT 140.7 82.8 144.9 84.0 ; + RECT 133.5 82.8 137.1 84.0 ; + RECT 143.7 79.2 144.9 82.8 ; + RECT 138.3 81.9 139.5 82.8 ; + RECT 137.1 81.6 139.5 81.9 ; + RECT 135.9 80.7 142.5 81.6 ; + RECT 140.7 80.4 142.5 80.7 ; + RECT 135.9 80.4 137.1 80.7 ; + RECT 140.7 78.0 144.9 79.2 ; + RECT 143.7 64.2 144.9 78.0 ; + RECT 138.0 77.4 139.2 79.8 ; + RECT 133.5 79.2 134.4 82.8 ; + RECT 133.5 78.0 137.1 79.2 ; + RECT 133.5 76.8 134.7 78.0 ; + RECT 141.3 74.4 142.5 75.6 ; + RECT 138.0 75.3 140.4 76.5 ; + RECT 135.9 73.5 142.5 74.4 ; + RECT 140.7 73.2 142.5 73.5 ; + RECT 135.9 73.2 137.1 73.5 ; + RECT 135.9 71.1 142.5 72.0 ; + RECT 140.7 70.8 142.5 71.1 ; + RECT 135.9 70.8 138.3 71.1 ; + RECT 135.9 68.7 142.5 69.6 ; + RECT 140.7 68.4 142.5 68.7 ; + RECT 135.9 68.4 138.3 68.7 ; + RECT 138.3 66.6 139.5 67.5 ; + RECT 135.9 65.7 142.5 66.6 ; + RECT 139.5 65.4 142.5 65.7 ; + RECT 135.9 65.4 137.1 65.7 ; + RECT 140.7 63.0 144.9 64.2 ; + RECT 143.7 58.5 144.9 63.0 ; + RECT 138.6 62.1 139.8 64.5 ; + RECT 133.5 64.2 134.4 76.8 ; + RECT 133.5 63.0 137.1 64.2 ; + RECT 141.3 60.9 142.5 62.1 ; + RECT 135.9 60.0 142.5 60.9 ; + RECT 140.7 59.7 142.5 60.0 ; + RECT 135.9 59.7 137.1 60.0 ; + RECT 133.5 58.5 134.4 63.0 ; + RECT 140.7 57.3 144.9 58.5 ; + RECT 143.7 53.7 144.9 57.3 ; + RECT 138.0 56.4 139.2 57.6 ; + RECT 133.5 57.3 137.1 58.5 ; + RECT 137.1 56.1 142.5 56.4 ; + RECT 135.9 55.5 142.5 56.1 ; + RECT 140.7 54.9 142.5 55.5 ; + RECT 135.9 55.2 138.3 55.5 ; + RECT 135.9 54.9 137.1 55.2 ; + RECT 140.7 52.5 144.9 53.7 ; + RECT 143.7 37.5 144.9 52.5 ; + RECT 138.0 51.9 139.2 54.3 ; + RECT 133.5 53.7 134.4 57.3 ; + RECT 133.5 52.5 137.1 53.7 ; + RECT 133.5 51.3 134.7 52.5 ; + RECT 141.3 48.0 142.5 49.2 ; + RECT 138.0 48.9 140.4 50.1 ; + RECT 135.9 47.1 142.5 48.0 ; + RECT 140.7 46.8 142.5 47.1 ; + RECT 135.9 46.8 137.1 47.1 ; + RECT 135.9 44.7 142.5 45.6 ; + RECT 140.7 44.4 142.5 44.7 ; + RECT 135.9 44.4 138.3 44.7 ; + RECT 135.9 42.3 142.5 43.2 ; + RECT 140.7 42.0 142.5 42.3 ; + RECT 135.9 42.0 138.3 42.3 ; + RECT 138.3 40.2 139.5 41.1 ; + RECT 135.9 39.3 142.5 40.2 ; + RECT 139.5 39.0 142.5 39.3 ; + RECT 135.9 39.0 137.1 39.3 ; + RECT 133.5 37.8 134.4 51.3 ; + RECT 140.7 37.5 142.5 37.8 ; + RECT 140.7 36.6 144.9 37.5 ; + RECT 133.5 36.9 137.1 37.8 ; + RECT 135.9 36.6 137.1 36.9 ; + RECT 140.7 35.1 141.9 36.6 ; + RECT 138.6 34.2 139.8 35.1 ; + RECT 133.5 33.3 144.9 34.2 ; + RECT 123.9 26.25 144.3 27.15 ; + RECT 123.9 28.65 144.3 29.55 ; + RECT 123.9 10.35 144.3 11.25 ; + RECT 123.9 50.4 134.7 51.6 ; + RECT 124.8 46.8 126.3 49.2 ; + RECT 127.5 46.8 128.7 50.4 ; + RECT 129.9 46.8 131.1 49.2 ; + RECT 132.3 46.8 133.5 49.2 ; + RECT 124.8 43.5 125.7 46.8 ; + RECT 126.6 44.7 127.8 45.9 ; + RECT 124.8 42.3 129.9 43.5 ; + RECT 132.6 42.3 133.5 46.8 ; + RECT 124.8 40.2 125.7 42.3 ; + RECT 131.4 41.1 133.5 42.3 ; + RECT 132.6 40.2 133.5 41.1 ; + RECT 124.8 39.0 126.3 40.2 ; + RECT 127.5 37.8 128.7 40.2 ; + RECT 129.9 39.0 131.1 40.2 ; + RECT 132.3 39.0 133.5 40.2 ; + RECT 123.9 36.6 134.7 37.8 ; + RECT 123.9 34.5 134.7 35.7 ; + RECT 123.9 32.1 134.7 33.3 ; + RECT 133.5 50.4 144.3 51.6 ; + RECT 141.9 46.8 143.4 49.2 ; + RECT 139.5 46.8 140.7 50.4 ; + RECT 137.1 46.8 138.3 49.2 ; + RECT 134.7 46.8 135.9 49.2 ; + RECT 142.5 43.5 143.4 46.8 ; + RECT 140.4 44.7 141.6 45.9 ; + RECT 138.3 42.3 143.4 43.5 ; + RECT 134.7 42.3 135.6 46.8 ; + RECT 142.5 40.2 143.4 42.3 ; + RECT 134.7 41.1 136.8 42.3 ; + RECT 134.7 40.2 135.6 41.1 ; + RECT 141.9 39.0 143.4 40.2 ; + RECT 139.5 37.8 140.7 40.2 ; + RECT 137.1 39.0 138.3 40.2 ; + RECT 134.7 39.0 135.9 40.2 ; + RECT 133.5 36.6 144.3 37.8 ; + RECT 133.5 34.5 144.3 35.7 ; + RECT 133.5 32.1 144.3 33.3 ; + RECT 41.4 206.4 42.3 207.3 ; + RECT 41.4 222.3 42.3 223.2 ; + RECT 41.4 235.8 42.3 236.7 ; + RECT 41.4 251.7 42.3 252.6 ; + RECT 41.4 265.2 42.3 266.1 ; + RECT 41.4 281.1 42.3 282.0 ; + RECT 41.4 294.6 42.3 295.5 ; + RECT 41.4 310.5 42.3 311.4 ; + RECT 41.4 324.0 42.3 324.9 ; + RECT 41.4 339.9 42.3 340.8 ; + RECT 41.4 353.4 42.3 354.3 ; + RECT 41.4 369.3 42.3 370.2 ; + RECT 41.4 382.8 42.3 383.7 ; + RECT 41.4 398.7 42.3 399.6 ; + RECT 41.4 412.2 42.3 413.1 ; + RECT 41.4 428.1 42.3 429.0 ; + RECT 12.6 88.8 29.4 89.7 ; + RECT 14.7 104.7 29.4 105.6 ; + RECT 16.8 118.2 29.4 119.1 ; + RECT 18.9 134.1 29.4 135.0 ; + RECT 21.0 147.6 29.4 148.5 ; + RECT 23.1 163.5 29.4 164.4 ; + RECT 25.2 177.0 29.4 177.9 ; + RECT 27.3 192.9 29.4 193.8 ; + RECT 12.6 208.8 29.4 209.7 ; + RECT 21.0 205.5 29.4 206.4 ; + RECT 12.6 219.9 29.4 220.8 ; + RECT 23.1 223.2 29.4 224.1 ; + RECT 12.6 238.2 29.4 239.1 ; + RECT 25.2 234.9 29.4 235.8 ; + RECT 12.6 249.3 29.4 250.2 ; + RECT 27.3 252.6 29.4 253.5 ; + RECT 14.7 267.6 29.4 268.5 ; + RECT 21.0 264.3 29.4 265.2 ; + RECT 14.7 278.7 29.4 279.6 ; + RECT 23.1 282.0 29.4 282.9 ; + RECT 14.7 297.0 29.4 297.9 ; + RECT 25.2 293.7 29.4 294.6 ; + RECT 14.7 308.1 29.4 309.0 ; + RECT 27.3 311.4 29.4 312.3 ; + RECT 16.8 326.4 29.4 327.3 ; + RECT 21.0 323.1 29.4 324.0 ; + RECT 16.8 337.5 29.4 338.4 ; + RECT 23.1 340.8 29.4 341.7 ; + RECT 16.8 355.8 29.4 356.7 ; + RECT 25.2 352.5 29.4 353.4 ; + RECT 16.8 366.9 29.4 367.8 ; + RECT 27.3 370.2 29.4 371.1 ; + RECT 18.9 385.2 29.4 386.1 ; + RECT 21.0 381.9 29.4 382.8 ; + RECT 18.9 396.3 29.4 397.2 ; + RECT 23.1 399.6 29.4 400.5 ; + RECT 18.9 414.6 29.4 415.5 ; + RECT 25.2 411.3 29.4 412.2 ; + RECT 18.9 425.7 29.4 426.6 ; + RECT 27.3 429.0 29.4 429.9 ; + RECT 38.1 88.8 39.0 89.7 ; + RECT 38.1 104.7 39.0 105.6 ; + RECT 38.1 118.2 39.0 119.1 ; + RECT 38.1 134.1 39.0 135.0 ; + RECT 63.9 88.8 64.8 94.95 ; + RECT 58.5 94.05 64.8 94.95 ; + RECT 73.5 88.8 78.6 89.7 ; + RECT 62.7 96.75 74.4 97.65 ; + RECT 60.6 82.05 74.4 82.95 ; + RECT 63.9 99.45 64.8 105.6 ; + RECT 56.4 99.45 64.8 100.35 ; + RECT 73.5 104.7 76.5 105.6 ; + RECT 62.7 96.75 74.4 97.65 ; + RECT 60.6 111.45 74.4 112.35 ; + RECT 51.0 91.2 59.4 92.1 ; + RECT 51.0 87.9 57.3 88.8 ; + RECT 51.0 102.3 55.2 103.2 ; + RECT 51.0 105.6 57.3 106.5 ; + RECT 51.0 120.6 59.4 121.5 ; + RECT 51.0 117.3 53.1 118.2 ; + RECT 51.0 131.7 55.2 132.6 ; + RECT 51.0 131.7 78.6 132.6 ; + RECT 51.0 135.0 53.1 135.9 ; + RECT 51.0 135.0 76.5 135.9 ; + RECT 51.0 82.05 61.5 82.95 ; + RECT 51.0 96.75 63.6 97.65 ; + RECT 51.0 111.45 61.5 112.35 ; + RECT 51.0 126.15 63.6 127.05 ; + RECT 51.0 140.85 61.5 141.75 ; + RECT 60.6 138.6 61.5 140.85 ; + RECT 64.8 82.05 74.4 82.95 ; + RECT 64.8 67.35 74.4 68.25 ; + RECT 66.6 68.25 67.8 70.65 ; + RECT 66.6 80.25 67.8 82.05 ; + RECT 71.4 81.15 72.6 82.05 ; + RECT 71.4 68.25 72.6 69.45 ; + RECT 69.0 70.5 70.2 79.95 ; + RECT 72.3 75.3 74.4 76.2 ; + RECT 64.8 75.3 69.0 76.2 ; + RECT 71.4 78.75 72.6 79.95 ; + RECT 69.0 78.75 70.2 79.95 ; + RECT 71.4 69.45 72.6 70.65 ; + RECT 69.0 69.45 70.2 70.65 ; + RECT 66.6 69.45 67.8 70.65 ; + RECT 66.6 79.95 67.8 81.15 ; + RECT 71.1 75.15 72.3 76.35 ; + RECT 64.8 111.45 74.4 112.35 ; + RECT 64.8 126.15 74.4 127.05 ; + RECT 66.6 123.75 67.8 126.15 ; + RECT 66.6 112.35 67.8 114.15 ; + RECT 71.4 112.35 72.6 113.25 ; + RECT 71.4 124.95 72.6 126.15 ; + RECT 69.0 114.45 70.2 123.9 ; + RECT 72.3 118.2 74.4 119.1 ; + RECT 64.8 118.2 69.0 119.1 ; + RECT 71.4 116.85 72.6 118.05 ; + RECT 69.0 116.85 70.2 118.05 ; + RECT 71.4 117.75 72.6 118.95 ; + RECT 69.0 117.75 70.2 118.95 ; + RECT 66.6 122.55 67.8 123.75 ; + RECT 66.6 112.05 67.8 113.25 ; + RECT 71.1 116.85 72.3 118.05 ; + RECT 29.4 82.05 39.0 82.95 ; + RECT 29.4 67.35 39.0 68.25 ; + RECT 31.2 68.25 32.4 70.65 ; + RECT 31.2 80.25 32.4 82.05 ; + RECT 36.0 81.15 37.2 82.05 ; + RECT 36.0 68.25 37.2 69.45 ; + RECT 33.6 70.5 34.8 79.95 ; + RECT 36.9 75.3 39.0 76.2 ; + RECT 29.4 75.3 33.6 76.2 ; + RECT 36.0 78.75 37.2 79.95 ; + RECT 33.6 78.75 34.8 79.95 ; + RECT 36.0 69.45 37.2 70.65 ; + RECT 33.6 69.45 34.8 70.65 ; + RECT 31.2 69.45 32.4 70.65 ; + RECT 31.2 79.95 32.4 81.15 ; + RECT 35.7 75.15 36.9 76.35 ; + RECT 29.4 111.45 39.0 112.35 ; + RECT 29.4 126.15 39.0 127.05 ; + RECT 31.2 123.75 32.4 126.15 ; + RECT 31.2 112.35 32.4 114.15 ; + RECT 36.0 112.35 37.2 113.25 ; + RECT 36.0 124.95 37.2 126.15 ; + RECT 33.6 114.45 34.8 123.9 ; + RECT 36.9 118.2 39.0 119.1 ; + RECT 29.4 118.2 33.6 119.1 ; + RECT 36.0 116.85 37.2 118.05 ; + RECT 33.6 116.85 34.8 118.05 ; + RECT 36.0 117.75 37.2 118.95 ; + RECT 33.6 117.75 34.8 118.95 ; + RECT 31.2 122.55 32.4 123.75 ; + RECT 31.2 112.05 32.4 113.25 ; + RECT 35.7 116.85 36.9 118.05 ; + RECT 29.4 111.45 39.0 112.35 ; + RECT 29.4 96.75 39.0 97.65 ; + RECT 31.2 97.65 32.4 100.05 ; + RECT 31.2 109.65 32.4 111.45 ; + RECT 36.0 110.55 37.2 111.45 ; + RECT 36.0 97.65 37.2 98.85 ; + RECT 33.6 99.9 34.8 109.35 ; + RECT 36.9 104.7 39.0 105.6 ; + RECT 29.4 104.7 33.6 105.6 ; + RECT 36.0 108.15 37.2 109.35 ; + RECT 33.6 108.15 34.8 109.35 ; + RECT 36.0 98.85 37.2 100.05 ; + RECT 33.6 98.85 34.8 100.05 ; + RECT 31.2 98.85 32.4 100.05 ; + RECT 31.2 109.35 32.4 110.55 ; + RECT 35.7 104.55 36.9 105.75 ; + RECT 29.4 140.85 39.0 141.75 ; + RECT 29.4 155.55 39.0 156.45 ; + RECT 31.2 153.15 32.4 155.55 ; + RECT 31.2 141.75 32.4 143.55 ; + RECT 36.0 141.75 37.2 142.65 ; + RECT 36.0 154.35 37.2 155.55 ; + RECT 33.6 143.85 34.8 153.3 ; + RECT 36.9 147.6 39.0 148.5 ; + RECT 29.4 147.6 33.6 148.5 ; + RECT 36.0 146.25 37.2 147.45 ; + RECT 33.6 146.25 34.8 147.45 ; + RECT 36.0 147.15 37.2 148.35 ; + RECT 33.6 147.15 34.8 148.35 ; + RECT 31.2 151.95 32.4 153.15 ; + RECT 31.2 141.45 32.4 142.65 ; + RECT 35.7 146.25 36.9 147.45 ; + RECT 39.0 82.05 51.0 82.95 ; + RECT 39.0 67.35 51.0 68.25 ; + RECT 41.1 67.8 42.0 70.65 ; + RECT 41.1 80.1 42.0 82.5 ; + RECT 48.15 67.8 49.05 70.65 ; + RECT 43.35 67.8 44.25 70.65 ; + RECT 48.15 79.65 49.05 82.5 ; + RECT 48.9 72.9 51.0 73.8 ; + RECT 45.9 76.2 51.0 77.1 ; + RECT 39.0 75.3 43.35 76.2 ; + RECT 48.0 78.45 49.2 79.65 ; + RECT 45.6 78.45 46.8 79.65 ; + RECT 45.6 78.45 46.8 79.65 ; + RECT 43.2 78.45 44.4 79.65 ; + RECT 48.0 69.45 49.2 70.65 ; + RECT 45.6 69.45 46.8 70.65 ; + RECT 45.6 69.45 46.8 70.65 ; + RECT 43.2 69.45 44.4 70.65 ; + RECT 40.8 69.45 42.0 70.65 ; + RECT 40.8 79.65 42.0 80.85 ; + RECT 42.9 72.9 43.8 73.8 ; + RECT 45.9 72.9 46.8 73.8 ; + RECT 42.9 73.35 43.8 80.85 ; + RECT 43.35 72.9 46.35 73.8 ; + RECT 45.9 70.65 46.8 73.35 ; + RECT 47.7 72.9 48.9 74.1 ; + RECT 44.7 76.2 45.9 77.4 ; + RECT 39.0 111.45 51.0 112.35 ; + RECT 39.0 126.15 51.0 127.05 ; + RECT 41.1 123.75 42.0 126.6 ; + RECT 41.1 111.9 42.0 114.3 ; + RECT 48.15 123.75 49.05 126.6 ; + RECT 43.35 123.75 44.25 126.6 ; + RECT 48.15 111.9 49.05 114.75 ; + RECT 48.9 120.6 51.0 121.5 ; + RECT 45.9 117.3 51.0 118.2 ; + RECT 39.0 118.2 43.35 119.1 ; + RECT 48.0 118.35 49.2 119.55 ; + RECT 45.6 118.35 46.8 119.55 ; + RECT 45.6 118.35 46.8 119.55 ; + RECT 43.2 118.35 44.4 119.55 ; + RECT 48.0 117.75 49.2 118.95 ; + RECT 45.6 117.75 46.8 118.95 ; + RECT 45.6 117.75 46.8 118.95 ; + RECT 43.2 117.75 44.4 118.95 ; + RECT 40.8 122.55 42.0 123.75 ; + RECT 40.8 112.35 42.0 113.55 ; + RECT 42.9 105.6 43.8 106.5 ; + RECT 45.9 105.6 46.8 106.5 ; + RECT 42.9 106.05 43.8 113.55 ; + RECT 43.35 105.6 46.35 106.5 ; + RECT 45.9 103.35 46.8 106.05 ; + RECT 47.7 119.1 48.9 120.3 ; + RECT 44.7 115.8 45.9 117.0 ; + RECT 39.0 111.45 51.0 112.35 ; + RECT 39.0 96.75 51.0 97.65 ; + RECT 41.1 97.2 42.0 100.05 ; + RECT 41.1 109.5 42.0 111.9 ; + RECT 48.15 97.2 49.05 100.05 ; + RECT 43.35 97.2 44.25 100.05 ; + RECT 48.15 109.05 49.05 111.9 ; + RECT 48.9 102.3 51.0 103.2 ; + RECT 45.9 105.6 51.0 106.5 ; + RECT 39.0 104.7 43.35 105.6 ; + RECT 48.0 107.85 49.2 109.05 ; + RECT 45.6 107.85 46.8 109.05 ; + RECT 45.6 107.85 46.8 109.05 ; + RECT 43.2 107.85 44.4 109.05 ; + RECT 48.0 98.85 49.2 100.05 ; + RECT 45.6 98.85 46.8 100.05 ; + RECT 45.6 98.85 46.8 100.05 ; + RECT 43.2 98.85 44.4 100.05 ; + RECT 40.8 98.85 42.0 100.05 ; + RECT 40.8 109.05 42.0 110.25 ; + RECT 42.9 102.3 43.8 103.2 ; + RECT 45.9 102.3 46.8 103.2 ; + RECT 42.9 102.75 43.8 110.25 ; + RECT 43.35 102.3 46.35 103.2 ; + RECT 45.9 100.05 46.8 102.75 ; + RECT 47.7 102.3 48.9 103.5 ; + RECT 44.7 105.6 45.9 106.8 ; + RECT 39.0 140.85 51.0 141.75 ; + RECT 39.0 155.55 51.0 156.45 ; + RECT 41.1 153.15 42.0 156.0 ; + RECT 41.1 141.3 42.0 143.7 ; + RECT 48.15 153.15 49.05 156.0 ; + RECT 43.35 153.15 44.25 156.0 ; + RECT 48.15 141.3 49.05 144.15 ; + RECT 48.9 150.0 51.0 150.9 ; + RECT 45.9 146.7 51.0 147.6 ; + RECT 39.0 147.6 43.35 148.5 ; + RECT 48.0 147.75 49.2 148.95 ; + RECT 45.6 147.75 46.8 148.95 ; + RECT 45.6 147.75 46.8 148.95 ; + RECT 43.2 147.75 44.4 148.95 ; + RECT 48.0 147.15 49.2 148.35 ; + RECT 45.6 147.15 46.8 148.35 ; + RECT 45.6 147.15 46.8 148.35 ; + RECT 43.2 147.15 44.4 148.35 ; + RECT 40.8 151.95 42.0 153.15 ; + RECT 40.8 141.75 42.0 142.95 ; + RECT 42.9 135.0 43.8 135.9 ; + RECT 45.9 135.0 46.8 135.9 ; + RECT 42.9 135.45 43.8 142.95 ; + RECT 43.35 135.0 46.35 135.9 ; + RECT 45.9 132.75 46.8 135.45 ; + RECT 47.7 148.5 48.9 149.7 ; + RECT 44.7 145.2 45.9 146.4 ; + RECT 58.2 92.85 59.4 94.05 ; + RECT 77.4 87.6 78.6 88.8 ; + RECT 56.1 98.25 57.3 99.45 ; + RECT 75.3 103.5 76.5 104.7 ; + RECT 58.2 90.0 59.4 91.2 ; + RECT 56.1 86.7 57.3 87.9 ; + RECT 54.0 101.1 55.2 102.3 ; + RECT 56.1 104.4 57.3 105.6 ; + RECT 58.2 119.4 59.4 120.6 ; + RECT 51.9 116.1 53.1 117.3 ; + RECT 54.0 130.5 55.2 131.7 ; + RECT 77.4 130.5 78.6 131.7 ; + RECT 51.9 133.8 53.1 135.0 ; + RECT 75.3 133.8 76.5 135.0 ; + RECT 60.3 80.85 61.5 82.05 ; + RECT 62.4 95.55 63.6 96.75 ; + RECT 60.3 110.25 61.5 111.45 ; + RECT 62.4 124.95 63.6 126.15 ; + RECT 60.3 137.4 61.5 138.6 ; + RECT 38.1 147.6 39.0 148.5 ; + RECT 38.1 163.5 39.0 164.4 ; + RECT 38.1 177.0 39.0 177.9 ; + RECT 38.1 192.9 39.0 193.8 ; + RECT 63.9 147.6 64.8 153.75 ; + RECT 58.5 152.85 64.8 153.75 ; + RECT 73.5 147.6 78.6 148.5 ; + RECT 62.7 155.55 74.4 156.45 ; + RECT 60.6 140.85 74.4 141.75 ; + RECT 63.9 158.25 64.8 164.4 ; + RECT 56.4 158.25 64.8 159.15 ; + RECT 73.5 163.5 76.5 164.4 ; + RECT 62.7 155.55 74.4 156.45 ; + RECT 60.6 170.25 74.4 171.15 ; + RECT 51.0 150.0 59.4 150.9 ; + RECT 51.0 146.7 57.3 147.6 ; + RECT 51.0 161.1 55.2 162.0 ; + RECT 51.0 164.4 57.3 165.3 ; + RECT 51.0 179.4 59.4 180.3 ; + RECT 51.0 176.1 53.1 177.0 ; + RECT 51.0 190.5 55.2 191.4 ; + RECT 51.0 190.5 78.6 191.4 ; + RECT 51.0 193.8 53.1 194.7 ; + RECT 51.0 193.8 76.5 194.7 ; + RECT 51.0 140.85 61.5 141.75 ; + RECT 51.0 155.55 63.6 156.45 ; + RECT 51.0 170.25 61.5 171.15 ; + RECT 51.0 184.95 63.6 185.85 ; + RECT 51.0 199.65 61.5 200.55 ; + RECT 60.6 197.4 61.5 199.65 ; + RECT 64.8 140.85 74.4 141.75 ; + RECT 64.8 126.15 74.4 127.05 ; + RECT 66.6 127.05 67.8 129.45 ; + RECT 66.6 139.05 67.8 140.85 ; + RECT 71.4 139.95 72.6 140.85 ; + RECT 71.4 127.05 72.6 128.25 ; + RECT 69.0 129.3 70.2 138.75 ; + RECT 72.3 134.1 74.4 135.0 ; + RECT 64.8 134.1 69.0 135.0 ; + RECT 71.4 137.55 72.6 138.75 ; + RECT 69.0 137.55 70.2 138.75 ; + RECT 71.4 128.25 72.6 129.45 ; + RECT 69.0 128.25 70.2 129.45 ; + RECT 66.6 128.25 67.8 129.45 ; + RECT 66.6 138.75 67.8 139.95 ; + RECT 71.1 133.95 72.3 135.15 ; + RECT 64.8 170.25 74.4 171.15 ; + RECT 64.8 184.95 74.4 185.85 ; + RECT 66.6 182.55 67.8 184.95 ; + RECT 66.6 171.15 67.8 172.95 ; + RECT 71.4 171.15 72.6 172.05 ; + RECT 71.4 183.75 72.6 184.95 ; + RECT 69.0 173.25 70.2 182.7 ; + RECT 72.3 177.0 74.4 177.9 ; + RECT 64.8 177.0 69.0 177.9 ; + RECT 71.4 175.65 72.6 176.85 ; + RECT 69.0 175.65 70.2 176.85 ; + RECT 71.4 176.55 72.6 177.75 ; + RECT 69.0 176.55 70.2 177.75 ; + RECT 66.6 181.35 67.8 182.55 ; + RECT 66.6 170.85 67.8 172.05 ; + RECT 71.1 175.65 72.3 176.85 ; + RECT 29.4 140.85 39.0 141.75 ; + RECT 29.4 126.15 39.0 127.05 ; + RECT 31.2 127.05 32.4 129.45 ; + RECT 31.2 139.05 32.4 140.85 ; + RECT 36.0 139.95 37.2 140.85 ; + RECT 36.0 127.05 37.2 128.25 ; + RECT 33.6 129.3 34.8 138.75 ; + RECT 36.9 134.1 39.0 135.0 ; + RECT 29.4 134.1 33.6 135.0 ; + RECT 36.0 137.55 37.2 138.75 ; + RECT 33.6 137.55 34.8 138.75 ; + RECT 36.0 128.25 37.2 129.45 ; + RECT 33.6 128.25 34.8 129.45 ; + RECT 31.2 128.25 32.4 129.45 ; + RECT 31.2 138.75 32.4 139.95 ; + RECT 35.7 133.95 36.9 135.15 ; + RECT 29.4 170.25 39.0 171.15 ; + RECT 29.4 184.95 39.0 185.85 ; + RECT 31.2 182.55 32.4 184.95 ; + RECT 31.2 171.15 32.4 172.95 ; + RECT 36.0 171.15 37.2 172.05 ; + RECT 36.0 183.75 37.2 184.95 ; + RECT 33.6 173.25 34.8 182.7 ; + RECT 36.9 177.0 39.0 177.9 ; + RECT 29.4 177.0 33.6 177.9 ; + RECT 36.0 175.65 37.2 176.85 ; + RECT 33.6 175.65 34.8 176.85 ; + RECT 36.0 176.55 37.2 177.75 ; + RECT 33.6 176.55 34.8 177.75 ; + RECT 31.2 181.35 32.4 182.55 ; + RECT 31.2 170.85 32.4 172.05 ; + RECT 35.7 175.65 36.9 176.85 ; + RECT 29.4 170.25 39.0 171.15 ; + RECT 29.4 155.55 39.0 156.45 ; + RECT 31.2 156.45 32.4 158.85 ; + RECT 31.2 168.45 32.4 170.25 ; + RECT 36.0 169.35 37.2 170.25 ; + RECT 36.0 156.45 37.2 157.65 ; + RECT 33.6 158.7 34.8 168.15 ; + RECT 36.9 163.5 39.0 164.4 ; + RECT 29.4 163.5 33.6 164.4 ; + RECT 36.0 166.95 37.2 168.15 ; + RECT 33.6 166.95 34.8 168.15 ; + RECT 36.0 157.65 37.2 158.85 ; + RECT 33.6 157.65 34.8 158.85 ; + RECT 31.2 157.65 32.4 158.85 ; + RECT 31.2 168.15 32.4 169.35 ; + RECT 35.7 163.35 36.9 164.55 ; + RECT 29.4 199.65 39.0 200.55 ; + RECT 29.4 214.35 39.0 215.25 ; + RECT 31.2 211.95 32.4 214.35 ; + RECT 31.2 200.55 32.4 202.35 ; + RECT 36.0 200.55 37.2 201.45 ; + RECT 36.0 213.15 37.2 214.35 ; + RECT 33.6 202.65 34.8 212.1 ; + RECT 36.9 206.4 39.0 207.3 ; + RECT 29.4 206.4 33.6 207.3 ; + RECT 36.0 205.05 37.2 206.25 ; + RECT 33.6 205.05 34.8 206.25 ; + RECT 36.0 205.95 37.2 207.15 ; + RECT 33.6 205.95 34.8 207.15 ; + RECT 31.2 210.75 32.4 211.95 ; + RECT 31.2 200.25 32.4 201.45 ; + RECT 35.7 205.05 36.9 206.25 ; + RECT 39.0 140.85 51.0 141.75 ; + RECT 39.0 126.15 51.0 127.05 ; + RECT 41.1 126.6 42.0 129.45 ; + RECT 41.1 138.9 42.0 141.3 ; + RECT 48.15 126.6 49.05 129.45 ; + RECT 43.35 126.6 44.25 129.45 ; + RECT 48.15 138.45 49.05 141.3 ; + RECT 48.9 131.7 51.0 132.6 ; + RECT 45.9 135.0 51.0 135.9 ; + RECT 39.0 134.1 43.35 135.0 ; + RECT 48.0 137.25 49.2 138.45 ; + RECT 45.6 137.25 46.8 138.45 ; + RECT 45.6 137.25 46.8 138.45 ; + RECT 43.2 137.25 44.4 138.45 ; + RECT 48.0 128.25 49.2 129.45 ; + RECT 45.6 128.25 46.8 129.45 ; + RECT 45.6 128.25 46.8 129.45 ; + RECT 43.2 128.25 44.4 129.45 ; + RECT 40.8 128.25 42.0 129.45 ; + RECT 40.8 138.45 42.0 139.65 ; + RECT 42.9 131.7 43.8 132.6 ; + RECT 45.9 131.7 46.8 132.6 ; + RECT 42.9 132.15 43.8 139.65 ; + RECT 43.35 131.7 46.35 132.6 ; + RECT 45.9 129.45 46.8 132.15 ; + RECT 47.7 131.7 48.9 132.9 ; + RECT 44.7 135.0 45.9 136.2 ; + RECT 39.0 170.25 51.0 171.15 ; + RECT 39.0 184.95 51.0 185.85 ; + RECT 41.1 182.55 42.0 185.4 ; + RECT 41.1 170.7 42.0 173.1 ; + RECT 48.15 182.55 49.05 185.4 ; + RECT 43.35 182.55 44.25 185.4 ; + RECT 48.15 170.7 49.05 173.55 ; + RECT 48.9 179.4 51.0 180.3 ; + RECT 45.9 176.1 51.0 177.0 ; + RECT 39.0 177.0 43.35 177.9 ; + RECT 48.0 177.15 49.2 178.35 ; + RECT 45.6 177.15 46.8 178.35 ; + RECT 45.6 177.15 46.8 178.35 ; + RECT 43.2 177.15 44.4 178.35 ; + RECT 48.0 176.55 49.2 177.75 ; + RECT 45.6 176.55 46.8 177.75 ; + RECT 45.6 176.55 46.8 177.75 ; + RECT 43.2 176.55 44.4 177.75 ; + RECT 40.8 181.35 42.0 182.55 ; + RECT 40.8 171.15 42.0 172.35 ; + RECT 42.9 164.4 43.8 165.3 ; + RECT 45.9 164.4 46.8 165.3 ; + RECT 42.9 164.85 43.8 172.35 ; + RECT 43.35 164.4 46.35 165.3 ; + RECT 45.9 162.15 46.8 164.85 ; + RECT 47.7 177.9 48.9 179.1 ; + RECT 44.7 174.6 45.9 175.8 ; + RECT 39.0 170.25 51.0 171.15 ; + RECT 39.0 155.55 51.0 156.45 ; + RECT 41.1 156.0 42.0 158.85 ; + RECT 41.1 168.3 42.0 170.7 ; + RECT 48.15 156.0 49.05 158.85 ; + RECT 43.35 156.0 44.25 158.85 ; + RECT 48.15 167.85 49.05 170.7 ; + RECT 48.9 161.1 51.0 162.0 ; + RECT 45.9 164.4 51.0 165.3 ; + RECT 39.0 163.5 43.35 164.4 ; + RECT 48.0 166.65 49.2 167.85 ; + RECT 45.6 166.65 46.8 167.85 ; + RECT 45.6 166.65 46.8 167.85 ; + RECT 43.2 166.65 44.4 167.85 ; + RECT 48.0 157.65 49.2 158.85 ; + RECT 45.6 157.65 46.8 158.85 ; + RECT 45.6 157.65 46.8 158.85 ; + RECT 43.2 157.65 44.4 158.85 ; + RECT 40.8 157.65 42.0 158.85 ; + RECT 40.8 167.85 42.0 169.05 ; + RECT 42.9 161.1 43.8 162.0 ; + RECT 45.9 161.1 46.8 162.0 ; + RECT 42.9 161.55 43.8 169.05 ; + RECT 43.35 161.1 46.35 162.0 ; + RECT 45.9 158.85 46.8 161.55 ; + RECT 47.7 161.1 48.9 162.3 ; + RECT 44.7 164.4 45.9 165.6 ; + RECT 39.0 199.65 51.0 200.55 ; + RECT 39.0 214.35 51.0 215.25 ; + RECT 41.1 211.95 42.0 214.8 ; + RECT 41.1 200.1 42.0 202.5 ; + RECT 48.15 211.95 49.05 214.8 ; + RECT 43.35 211.95 44.25 214.8 ; + RECT 48.15 200.1 49.05 202.95 ; + RECT 48.9 208.8 51.0 209.7 ; + RECT 45.9 205.5 51.0 206.4 ; + RECT 39.0 206.4 43.35 207.3 ; + RECT 48.0 206.55 49.2 207.75 ; + RECT 45.6 206.55 46.8 207.75 ; + RECT 45.6 206.55 46.8 207.75 ; + RECT 43.2 206.55 44.4 207.75 ; + RECT 48.0 205.95 49.2 207.15 ; + RECT 45.6 205.95 46.8 207.15 ; + RECT 45.6 205.95 46.8 207.15 ; + RECT 43.2 205.95 44.4 207.15 ; + RECT 40.8 210.75 42.0 211.95 ; + RECT 40.8 200.55 42.0 201.75 ; + RECT 42.9 193.8 43.8 194.7 ; + RECT 45.9 193.8 46.8 194.7 ; + RECT 42.9 194.25 43.8 201.75 ; + RECT 43.35 193.8 46.35 194.7 ; + RECT 45.9 191.55 46.8 194.25 ; + RECT 47.7 207.3 48.9 208.5 ; + RECT 44.7 204.0 45.9 205.2 ; + RECT 58.2 151.65 59.4 152.85 ; + RECT 77.4 146.4 78.6 147.6 ; + RECT 56.1 157.05 57.3 158.25 ; + RECT 75.3 162.3 76.5 163.5 ; + RECT 58.2 148.8 59.4 150.0 ; + RECT 56.1 145.5 57.3 146.7 ; + RECT 54.0 159.9 55.2 161.1 ; + RECT 56.1 163.2 57.3 164.4 ; + RECT 58.2 178.2 59.4 179.4 ; + RECT 51.9 174.9 53.1 176.1 ; + RECT 54.0 189.3 55.2 190.5 ; + RECT 77.4 189.3 78.6 190.5 ; + RECT 51.9 192.6 53.1 193.8 ; + RECT 75.3 192.6 76.5 193.8 ; + RECT 60.3 139.65 61.5 140.85 ; + RECT 62.4 154.35 63.6 155.55 ; + RECT 60.3 169.05 61.5 170.25 ; + RECT 62.4 183.75 63.6 184.95 ; + RECT 60.3 196.2 61.5 197.4 ; + RECT 29.4 199.65 41.4 200.55 ; + RECT 29.4 214.35 41.4 215.25 ; + RECT 38.4 211.95 39.3 214.8 ; + RECT 38.4 200.1 39.3 202.5 ; + RECT 31.35 211.95 32.25 214.8 ; + RECT 36.15 211.95 37.05 214.8 ; + RECT 31.35 200.1 32.25 202.95 ; + RECT 29.4 208.8 31.5 209.7 ; + RECT 29.4 205.5 34.5 206.4 ; + RECT 37.05 206.4 41.4 207.3 ; + RECT 31.2 202.95 32.4 204.15 ; + RECT 33.6 202.95 34.8 204.15 ; + RECT 33.6 202.95 34.8 204.15 ; + RECT 36.0 202.95 37.2 204.15 ; + RECT 31.2 211.95 32.4 213.15 ; + RECT 33.6 211.95 34.8 213.15 ; + RECT 33.6 211.95 34.8 213.15 ; + RECT 36.0 211.95 37.2 213.15 ; + RECT 38.4 211.95 39.6 213.15 ; + RECT 38.4 201.75 39.6 202.95 ; + RECT 36.6 208.8 37.5 209.7 ; + RECT 33.6 208.8 34.5 209.7 ; + RECT 36.6 201.75 37.5 209.25 ; + RECT 34.05 208.8 37.05 209.7 ; + RECT 33.6 209.25 34.5 211.95 ; + RECT 31.5 208.5 32.7 209.7 ; + RECT 34.5 205.2 35.7 206.4 ; + RECT 29.4 229.05 41.4 229.95 ; + RECT 29.4 214.35 41.4 215.25 ; + RECT 38.4 214.8 39.3 217.65 ; + RECT 38.4 227.1 39.3 229.5 ; + RECT 31.35 214.8 32.25 217.65 ; + RECT 36.15 214.8 37.05 217.65 ; + RECT 31.35 226.65 32.25 229.5 ; + RECT 29.4 219.9 31.5 220.8 ; + RECT 29.4 223.2 34.5 224.1 ; + RECT 37.05 222.3 41.4 223.2 ; + RECT 31.2 221.85 32.4 223.05 ; + RECT 33.6 221.85 34.8 223.05 ; + RECT 33.6 221.85 34.8 223.05 ; + RECT 36.0 221.85 37.2 223.05 ; + RECT 31.2 222.45 32.4 223.65 ; + RECT 33.6 222.45 34.8 223.65 ; + RECT 33.6 222.45 34.8 223.65 ; + RECT 36.0 222.45 37.2 223.65 ; + RECT 38.4 217.65 39.6 218.85 ; + RECT 38.4 227.85 39.6 229.05 ; + RECT 36.6 234.9 37.5 235.8 ; + RECT 33.6 234.9 34.5 235.8 ; + RECT 36.6 227.85 37.5 235.35 ; + RECT 34.05 234.9 37.05 235.8 ; + RECT 33.6 235.35 34.5 238.05 ; + RECT 31.5 221.1 32.7 222.3 ; + RECT 34.5 224.4 35.7 225.6 ; + RECT 29.4 229.05 41.4 229.95 ; + RECT 29.4 243.75 41.4 244.65 ; + RECT 38.4 241.35 39.3 244.2 ; + RECT 38.4 229.5 39.3 231.9 ; + RECT 31.35 241.35 32.25 244.2 ; + RECT 36.15 241.35 37.05 244.2 ; + RECT 31.35 229.5 32.25 232.35 ; + RECT 29.4 238.2 31.5 239.1 ; + RECT 29.4 234.9 34.5 235.8 ; + RECT 37.05 235.8 41.4 236.7 ; + RECT 31.2 232.35 32.4 233.55 ; + RECT 33.6 232.35 34.8 233.55 ; + RECT 33.6 232.35 34.8 233.55 ; + RECT 36.0 232.35 37.2 233.55 ; + RECT 31.2 241.35 32.4 242.55 ; + RECT 33.6 241.35 34.8 242.55 ; + RECT 33.6 241.35 34.8 242.55 ; + RECT 36.0 241.35 37.2 242.55 ; + RECT 38.4 241.35 39.6 242.55 ; + RECT 38.4 231.15 39.6 232.35 ; + RECT 36.6 238.2 37.5 239.1 ; + RECT 33.6 238.2 34.5 239.1 ; + RECT 36.6 231.15 37.5 238.65 ; + RECT 34.05 238.2 37.05 239.1 ; + RECT 33.6 238.65 34.5 241.35 ; + RECT 31.5 237.9 32.7 239.1 ; + RECT 34.5 234.6 35.7 235.8 ; + RECT 29.4 258.45 41.4 259.35 ; + RECT 29.4 243.75 41.4 244.65 ; + RECT 38.4 244.2 39.3 247.05 ; + RECT 38.4 256.5 39.3 258.9 ; + RECT 31.35 244.2 32.25 247.05 ; + RECT 36.15 244.2 37.05 247.05 ; + RECT 31.35 256.05 32.25 258.9 ; + RECT 29.4 249.3 31.5 250.2 ; + RECT 29.4 252.6 34.5 253.5 ; + RECT 37.05 251.7 41.4 252.6 ; + RECT 31.2 251.25 32.4 252.45 ; + RECT 33.6 251.25 34.8 252.45 ; + RECT 33.6 251.25 34.8 252.45 ; + RECT 36.0 251.25 37.2 252.45 ; + RECT 31.2 251.85 32.4 253.05 ; + RECT 33.6 251.85 34.8 253.05 ; + RECT 33.6 251.85 34.8 253.05 ; + RECT 36.0 251.85 37.2 253.05 ; + RECT 38.4 247.05 39.6 248.25 ; + RECT 38.4 257.25 39.6 258.45 ; + RECT 36.6 264.3 37.5 265.2 ; + RECT 33.6 264.3 34.5 265.2 ; + RECT 36.6 257.25 37.5 264.75 ; + RECT 34.05 264.3 37.05 265.2 ; + RECT 33.6 264.75 34.5 267.45 ; + RECT 31.5 250.5 32.7 251.7 ; + RECT 34.5 253.8 35.7 255.0 ; + RECT 29.4 258.45 41.4 259.35 ; + RECT 29.4 273.15 41.4 274.05 ; + RECT 38.4 270.75 39.3 273.6 ; + RECT 38.4 258.9 39.3 261.3 ; + RECT 31.35 270.75 32.25 273.6 ; + RECT 36.15 270.75 37.05 273.6 ; + RECT 31.35 258.9 32.25 261.75 ; + RECT 29.4 267.6 31.5 268.5 ; + RECT 29.4 264.3 34.5 265.2 ; + RECT 37.05 265.2 41.4 266.1 ; + RECT 31.2 261.75 32.4 262.95 ; + RECT 33.6 261.75 34.8 262.95 ; + RECT 33.6 261.75 34.8 262.95 ; + RECT 36.0 261.75 37.2 262.95 ; + RECT 31.2 270.75 32.4 271.95 ; + RECT 33.6 270.75 34.8 271.95 ; + RECT 33.6 270.75 34.8 271.95 ; + RECT 36.0 270.75 37.2 271.95 ; + RECT 38.4 270.75 39.6 271.95 ; + RECT 38.4 260.55 39.6 261.75 ; + RECT 36.6 267.6 37.5 268.5 ; + RECT 33.6 267.6 34.5 268.5 ; + RECT 36.6 260.55 37.5 268.05 ; + RECT 34.05 267.6 37.05 268.5 ; + RECT 33.6 268.05 34.5 270.75 ; + RECT 31.5 267.3 32.7 268.5 ; + RECT 34.5 264.0 35.7 265.2 ; + RECT 29.4 287.85 41.4 288.75 ; + RECT 29.4 273.15 41.4 274.05 ; + RECT 38.4 273.6 39.3 276.45 ; + RECT 38.4 285.9 39.3 288.3 ; + RECT 31.35 273.6 32.25 276.45 ; + RECT 36.15 273.6 37.05 276.45 ; + RECT 31.35 285.45 32.25 288.3 ; + RECT 29.4 278.7 31.5 279.6 ; + RECT 29.4 282.0 34.5 282.9 ; + RECT 37.05 281.1 41.4 282.0 ; + RECT 31.2 280.65 32.4 281.85 ; + RECT 33.6 280.65 34.8 281.85 ; + RECT 33.6 280.65 34.8 281.85 ; + RECT 36.0 280.65 37.2 281.85 ; + RECT 31.2 281.25 32.4 282.45 ; + RECT 33.6 281.25 34.8 282.45 ; + RECT 33.6 281.25 34.8 282.45 ; + RECT 36.0 281.25 37.2 282.45 ; + RECT 38.4 276.45 39.6 277.65 ; + RECT 38.4 286.65 39.6 287.85 ; + RECT 36.6 293.7 37.5 294.6 ; + RECT 33.6 293.7 34.5 294.6 ; + RECT 36.6 286.65 37.5 294.15 ; + RECT 34.05 293.7 37.05 294.6 ; + RECT 33.6 294.15 34.5 296.85 ; + RECT 31.5 279.9 32.7 281.1 ; + RECT 34.5 283.2 35.7 284.4 ; + RECT 29.4 287.85 41.4 288.75 ; + RECT 29.4 302.55 41.4 303.45 ; + RECT 38.4 300.15 39.3 303.0 ; + RECT 38.4 288.3 39.3 290.7 ; + RECT 31.35 300.15 32.25 303.0 ; + RECT 36.15 300.15 37.05 303.0 ; + RECT 31.35 288.3 32.25 291.15 ; + RECT 29.4 297.0 31.5 297.9 ; + RECT 29.4 293.7 34.5 294.6 ; + RECT 37.05 294.6 41.4 295.5 ; + RECT 31.2 291.15 32.4 292.35 ; + RECT 33.6 291.15 34.8 292.35 ; + RECT 33.6 291.15 34.8 292.35 ; + RECT 36.0 291.15 37.2 292.35 ; + RECT 31.2 300.15 32.4 301.35 ; + RECT 33.6 300.15 34.8 301.35 ; + RECT 33.6 300.15 34.8 301.35 ; + RECT 36.0 300.15 37.2 301.35 ; + RECT 38.4 300.15 39.6 301.35 ; + RECT 38.4 289.95 39.6 291.15 ; + RECT 36.6 297.0 37.5 297.9 ; + RECT 33.6 297.0 34.5 297.9 ; + RECT 36.6 289.95 37.5 297.45 ; + RECT 34.05 297.0 37.05 297.9 ; + RECT 33.6 297.45 34.5 300.15 ; + RECT 31.5 296.7 32.7 297.9 ; + RECT 34.5 293.4 35.7 294.6 ; + RECT 29.4 317.25 41.4 318.15 ; + RECT 29.4 302.55 41.4 303.45 ; + RECT 38.4 303.0 39.3 305.85 ; + RECT 38.4 315.3 39.3 317.7 ; + RECT 31.35 303.0 32.25 305.85 ; + RECT 36.15 303.0 37.05 305.85 ; + RECT 31.35 314.85 32.25 317.7 ; + RECT 29.4 308.1 31.5 309.0 ; + RECT 29.4 311.4 34.5 312.3 ; + RECT 37.05 310.5 41.4 311.4 ; + RECT 31.2 310.05 32.4 311.25 ; + RECT 33.6 310.05 34.8 311.25 ; + RECT 33.6 310.05 34.8 311.25 ; + RECT 36.0 310.05 37.2 311.25 ; + RECT 31.2 310.65 32.4 311.85 ; + RECT 33.6 310.65 34.8 311.85 ; + RECT 33.6 310.65 34.8 311.85 ; + RECT 36.0 310.65 37.2 311.85 ; + RECT 38.4 305.85 39.6 307.05 ; + RECT 38.4 316.05 39.6 317.25 ; + RECT 36.6 323.1 37.5 324.0 ; + RECT 33.6 323.1 34.5 324.0 ; + RECT 36.6 316.05 37.5 323.55 ; + RECT 34.05 323.1 37.05 324.0 ; + RECT 33.6 323.55 34.5 326.25 ; + RECT 31.5 309.3 32.7 310.5 ; + RECT 34.5 312.6 35.7 313.8 ; + RECT 29.4 317.25 41.4 318.15 ; + RECT 29.4 331.95 41.4 332.85 ; + RECT 38.4 329.55 39.3 332.4 ; + RECT 38.4 317.7 39.3 320.1 ; + RECT 31.35 329.55 32.25 332.4 ; + RECT 36.15 329.55 37.05 332.4 ; + RECT 31.35 317.7 32.25 320.55 ; + RECT 29.4 326.4 31.5 327.3 ; + RECT 29.4 323.1 34.5 324.0 ; + RECT 37.05 324.0 41.4 324.9 ; + RECT 31.2 320.55 32.4 321.75 ; + RECT 33.6 320.55 34.8 321.75 ; + RECT 33.6 320.55 34.8 321.75 ; + RECT 36.0 320.55 37.2 321.75 ; + RECT 31.2 329.55 32.4 330.75 ; + RECT 33.6 329.55 34.8 330.75 ; + RECT 33.6 329.55 34.8 330.75 ; + RECT 36.0 329.55 37.2 330.75 ; + RECT 38.4 329.55 39.6 330.75 ; + RECT 38.4 319.35 39.6 320.55 ; + RECT 36.6 326.4 37.5 327.3 ; + RECT 33.6 326.4 34.5 327.3 ; + RECT 36.6 319.35 37.5 326.85 ; + RECT 34.05 326.4 37.05 327.3 ; + RECT 33.6 326.85 34.5 329.55 ; + RECT 31.5 326.1 32.7 327.3 ; + RECT 34.5 322.8 35.7 324.0 ; + RECT 29.4 346.65 41.4 347.55 ; + RECT 29.4 331.95 41.4 332.85 ; + RECT 38.4 332.4 39.3 335.25 ; + RECT 38.4 344.7 39.3 347.1 ; + RECT 31.35 332.4 32.25 335.25 ; + RECT 36.15 332.4 37.05 335.25 ; + RECT 31.35 344.25 32.25 347.1 ; + RECT 29.4 337.5 31.5 338.4 ; + RECT 29.4 340.8 34.5 341.7 ; + RECT 37.05 339.9 41.4 340.8 ; + RECT 31.2 339.45 32.4 340.65 ; + RECT 33.6 339.45 34.8 340.65 ; + RECT 33.6 339.45 34.8 340.65 ; + RECT 36.0 339.45 37.2 340.65 ; + RECT 31.2 340.05 32.4 341.25 ; + RECT 33.6 340.05 34.8 341.25 ; + RECT 33.6 340.05 34.8 341.25 ; + RECT 36.0 340.05 37.2 341.25 ; + RECT 38.4 335.25 39.6 336.45 ; + RECT 38.4 345.45 39.6 346.65 ; + RECT 36.6 352.5 37.5 353.4 ; + RECT 33.6 352.5 34.5 353.4 ; + RECT 36.6 345.45 37.5 352.95 ; + RECT 34.05 352.5 37.05 353.4 ; + RECT 33.6 352.95 34.5 355.65 ; + RECT 31.5 338.7 32.7 339.9 ; + RECT 34.5 342.0 35.7 343.2 ; + RECT 29.4 346.65 41.4 347.55 ; + RECT 29.4 361.35 41.4 362.25 ; + RECT 38.4 358.95 39.3 361.8 ; + RECT 38.4 347.1 39.3 349.5 ; + RECT 31.35 358.95 32.25 361.8 ; + RECT 36.15 358.95 37.05 361.8 ; + RECT 31.35 347.1 32.25 349.95 ; + RECT 29.4 355.8 31.5 356.7 ; + RECT 29.4 352.5 34.5 353.4 ; + RECT 37.05 353.4 41.4 354.3 ; + RECT 31.2 349.95 32.4 351.15 ; + RECT 33.6 349.95 34.8 351.15 ; + RECT 33.6 349.95 34.8 351.15 ; + RECT 36.0 349.95 37.2 351.15 ; + RECT 31.2 358.95 32.4 360.15 ; + RECT 33.6 358.95 34.8 360.15 ; + RECT 33.6 358.95 34.8 360.15 ; + RECT 36.0 358.95 37.2 360.15 ; + RECT 38.4 358.95 39.6 360.15 ; + RECT 38.4 348.75 39.6 349.95 ; + RECT 36.6 355.8 37.5 356.7 ; + RECT 33.6 355.8 34.5 356.7 ; + RECT 36.6 348.75 37.5 356.25 ; + RECT 34.05 355.8 37.05 356.7 ; + RECT 33.6 356.25 34.5 358.95 ; + RECT 31.5 355.5 32.7 356.7 ; + RECT 34.5 352.2 35.7 353.4 ; + RECT 29.4 376.05 41.4 376.95 ; + RECT 29.4 361.35 41.4 362.25 ; + RECT 38.4 361.8 39.3 364.65 ; + RECT 38.4 374.1 39.3 376.5 ; + RECT 31.35 361.8 32.25 364.65 ; + RECT 36.15 361.8 37.05 364.65 ; + RECT 31.35 373.65 32.25 376.5 ; + RECT 29.4 366.9 31.5 367.8 ; + RECT 29.4 370.2 34.5 371.1 ; + RECT 37.05 369.3 41.4 370.2 ; + RECT 31.2 368.85 32.4 370.05 ; + RECT 33.6 368.85 34.8 370.05 ; + RECT 33.6 368.85 34.8 370.05 ; + RECT 36.0 368.85 37.2 370.05 ; + RECT 31.2 369.45 32.4 370.65 ; + RECT 33.6 369.45 34.8 370.65 ; + RECT 33.6 369.45 34.8 370.65 ; + RECT 36.0 369.45 37.2 370.65 ; + RECT 38.4 364.65 39.6 365.85 ; + RECT 38.4 374.85 39.6 376.05 ; + RECT 36.6 381.9 37.5 382.8 ; + RECT 33.6 381.9 34.5 382.8 ; + RECT 36.6 374.85 37.5 382.35 ; + RECT 34.05 381.9 37.05 382.8 ; + RECT 33.6 382.35 34.5 385.05 ; + RECT 31.5 368.1 32.7 369.3 ; + RECT 34.5 371.4 35.7 372.6 ; + RECT 29.4 376.05 41.4 376.95 ; + RECT 29.4 390.75 41.4 391.65 ; + RECT 38.4 388.35 39.3 391.2 ; + RECT 38.4 376.5 39.3 378.9 ; + RECT 31.35 388.35 32.25 391.2 ; + RECT 36.15 388.35 37.05 391.2 ; + RECT 31.35 376.5 32.25 379.35 ; + RECT 29.4 385.2 31.5 386.1 ; + RECT 29.4 381.9 34.5 382.8 ; + RECT 37.05 382.8 41.4 383.7 ; + RECT 31.2 379.35 32.4 380.55 ; + RECT 33.6 379.35 34.8 380.55 ; + RECT 33.6 379.35 34.8 380.55 ; + RECT 36.0 379.35 37.2 380.55 ; + RECT 31.2 388.35 32.4 389.55 ; + RECT 33.6 388.35 34.8 389.55 ; + RECT 33.6 388.35 34.8 389.55 ; + RECT 36.0 388.35 37.2 389.55 ; + RECT 38.4 388.35 39.6 389.55 ; + RECT 38.4 378.15 39.6 379.35 ; + RECT 36.6 385.2 37.5 386.1 ; + RECT 33.6 385.2 34.5 386.1 ; + RECT 36.6 378.15 37.5 385.65 ; + RECT 34.05 385.2 37.05 386.1 ; + RECT 33.6 385.65 34.5 388.35 ; + RECT 31.5 384.9 32.7 386.1 ; + RECT 34.5 381.6 35.7 382.8 ; + RECT 29.4 405.45 41.4 406.35 ; + RECT 29.4 390.75 41.4 391.65 ; + RECT 38.4 391.2 39.3 394.05 ; + RECT 38.4 403.5 39.3 405.9 ; + RECT 31.35 391.2 32.25 394.05 ; + RECT 36.15 391.2 37.05 394.05 ; + RECT 31.35 403.05 32.25 405.9 ; + RECT 29.4 396.3 31.5 397.2 ; + RECT 29.4 399.6 34.5 400.5 ; + RECT 37.05 398.7 41.4 399.6 ; + RECT 31.2 398.25 32.4 399.45 ; + RECT 33.6 398.25 34.8 399.45 ; + RECT 33.6 398.25 34.8 399.45 ; + RECT 36.0 398.25 37.2 399.45 ; + RECT 31.2 398.85 32.4 400.05 ; + RECT 33.6 398.85 34.8 400.05 ; + RECT 33.6 398.85 34.8 400.05 ; + RECT 36.0 398.85 37.2 400.05 ; + RECT 38.4 394.05 39.6 395.25 ; + RECT 38.4 404.25 39.6 405.45 ; + RECT 36.6 411.3 37.5 412.2 ; + RECT 33.6 411.3 34.5 412.2 ; + RECT 36.6 404.25 37.5 411.75 ; + RECT 34.05 411.3 37.05 412.2 ; + RECT 33.6 411.75 34.5 414.45 ; + RECT 31.5 397.5 32.7 398.7 ; + RECT 34.5 400.8 35.7 402.0 ; + RECT 29.4 405.45 41.4 406.35 ; + RECT 29.4 420.15 41.4 421.05 ; + RECT 38.4 417.75 39.3 420.6 ; + RECT 38.4 405.9 39.3 408.3 ; + RECT 31.35 417.75 32.25 420.6 ; + RECT 36.15 417.75 37.05 420.6 ; + RECT 31.35 405.9 32.25 408.75 ; + RECT 29.4 414.6 31.5 415.5 ; + RECT 29.4 411.3 34.5 412.2 ; + RECT 37.05 412.2 41.4 413.1 ; + RECT 31.2 408.75 32.4 409.95 ; + RECT 33.6 408.75 34.8 409.95 ; + RECT 33.6 408.75 34.8 409.95 ; + RECT 36.0 408.75 37.2 409.95 ; + RECT 31.2 417.75 32.4 418.95 ; + RECT 33.6 417.75 34.8 418.95 ; + RECT 33.6 417.75 34.8 418.95 ; + RECT 36.0 417.75 37.2 418.95 ; + RECT 38.4 417.75 39.6 418.95 ; + RECT 38.4 407.55 39.6 408.75 ; + RECT 36.6 414.6 37.5 415.5 ; + RECT 33.6 414.6 34.5 415.5 ; + RECT 36.6 407.55 37.5 415.05 ; + RECT 34.05 414.6 37.05 415.5 ; + RECT 33.6 415.05 34.5 417.75 ; + RECT 31.5 414.3 32.7 415.5 ; + RECT 34.5 411.0 35.7 412.2 ; + RECT 29.4 434.85 41.4 435.75 ; + RECT 29.4 420.15 41.4 421.05 ; + RECT 38.4 420.6 39.3 423.45 ; + RECT 38.4 432.9 39.3 435.3 ; + RECT 31.35 420.6 32.25 423.45 ; + RECT 36.15 420.6 37.05 423.45 ; + RECT 31.35 432.45 32.25 435.3 ; + RECT 29.4 425.7 31.5 426.6 ; + RECT 29.4 429.0 34.5 429.9 ; + RECT 37.05 428.1 41.4 429.0 ; + RECT 31.2 427.65 32.4 428.85 ; + RECT 33.6 427.65 34.8 428.85 ; + RECT 33.6 427.65 34.8 428.85 ; + RECT 36.0 427.65 37.2 428.85 ; + RECT 31.2 428.25 32.4 429.45 ; + RECT 33.6 428.25 34.8 429.45 ; + RECT 33.6 428.25 34.8 429.45 ; + RECT 36.0 428.25 37.2 429.45 ; + RECT 38.4 423.45 39.6 424.65 ; + RECT 38.4 433.65 39.6 434.85 ; + RECT 36.6 440.7 37.5 441.6 ; + RECT 33.6 440.7 34.5 441.6 ; + RECT 36.6 433.65 37.5 441.15 ; + RECT 34.05 440.7 37.05 441.6 ; + RECT 33.6 441.15 34.5 443.85 ; + RECT 31.5 426.9 32.7 428.1 ; + RECT 34.5 430.2 35.7 431.4 ; + RECT 41.4 199.65 51.0 200.55 ; + RECT 41.4 214.35 51.0 215.25 ; + RECT 48.0 211.95 49.2 214.35 ; + RECT 48.0 200.55 49.2 202.35 ; + RECT 43.2 200.55 44.4 201.45 ; + RECT 43.2 213.15 44.4 214.35 ; + RECT 45.6 202.65 46.8 212.1 ; + RECT 41.4 206.4 43.5 207.3 ; + RECT 46.8 206.4 51.0 207.3 ; + RECT 43.2 202.65 44.4 203.85 ; + RECT 45.6 202.65 46.8 203.85 ; + RECT 43.2 211.95 44.4 213.15 ; + RECT 45.6 211.95 46.8 213.15 ; + RECT 48.0 211.95 49.2 213.15 ; + RECT 48.0 201.45 49.2 202.65 ; + RECT 43.5 206.25 44.7 207.45 ; + RECT 41.4 229.05 51.0 229.95 ; + RECT 41.4 214.35 51.0 215.25 ; + RECT 48.0 215.25 49.2 217.65 ; + RECT 48.0 227.25 49.2 229.05 ; + RECT 43.2 228.15 44.4 229.05 ; + RECT 43.2 215.25 44.4 216.45 ; + RECT 45.6 217.5 46.8 226.95 ; + RECT 41.4 222.3 43.5 223.2 ; + RECT 46.8 222.3 51.0 223.2 ; + RECT 43.2 223.35 44.4 224.55 ; + RECT 45.6 223.35 46.8 224.55 ; + RECT 43.2 222.45 44.4 223.65 ; + RECT 45.6 222.45 46.8 223.65 ; + RECT 48.0 217.65 49.2 218.85 ; + RECT 48.0 228.15 49.2 229.35 ; + RECT 43.5 223.35 44.7 224.55 ; + RECT 41.4 229.05 51.0 229.95 ; + RECT 41.4 243.75 51.0 244.65 ; + RECT 48.0 241.35 49.2 243.75 ; + RECT 48.0 229.95 49.2 231.75 ; + RECT 43.2 229.95 44.4 230.85 ; + RECT 43.2 242.55 44.4 243.75 ; + RECT 45.6 232.05 46.8 241.5 ; + RECT 41.4 235.8 43.5 236.7 ; + RECT 46.8 235.8 51.0 236.7 ; + RECT 43.2 232.05 44.4 233.25 ; + RECT 45.6 232.05 46.8 233.25 ; + RECT 43.2 241.35 44.4 242.55 ; + RECT 45.6 241.35 46.8 242.55 ; + RECT 48.0 241.35 49.2 242.55 ; + RECT 48.0 230.85 49.2 232.05 ; + RECT 43.5 235.65 44.7 236.85 ; + RECT 41.4 258.45 51.0 259.35 ; + RECT 41.4 243.75 51.0 244.65 ; + RECT 48.0 244.65 49.2 247.05 ; + RECT 48.0 256.65 49.2 258.45 ; + RECT 43.2 257.55 44.4 258.45 ; + RECT 43.2 244.65 44.4 245.85 ; + RECT 45.6 246.9 46.8 256.35 ; + RECT 41.4 251.7 43.5 252.6 ; + RECT 46.8 251.7 51.0 252.6 ; + RECT 43.2 252.75 44.4 253.95 ; + RECT 45.6 252.75 46.8 253.95 ; + RECT 43.2 251.85 44.4 253.05 ; + RECT 45.6 251.85 46.8 253.05 ; + RECT 48.0 247.05 49.2 248.25 ; + RECT 48.0 257.55 49.2 258.75 ; + RECT 43.5 252.75 44.7 253.95 ; + RECT 41.4 258.45 51.0 259.35 ; + RECT 41.4 273.15 51.0 274.05 ; + RECT 48.0 270.75 49.2 273.15 ; + RECT 48.0 259.35 49.2 261.15 ; + RECT 43.2 259.35 44.4 260.25 ; + RECT 43.2 271.95 44.4 273.15 ; + RECT 45.6 261.45 46.8 270.9 ; + RECT 41.4 265.2 43.5 266.1 ; + RECT 46.8 265.2 51.0 266.1 ; + RECT 43.2 261.45 44.4 262.65 ; + RECT 45.6 261.45 46.8 262.65 ; + RECT 43.2 270.75 44.4 271.95 ; + RECT 45.6 270.75 46.8 271.95 ; + RECT 48.0 270.75 49.2 271.95 ; + RECT 48.0 260.25 49.2 261.45 ; + RECT 43.5 265.05 44.7 266.25 ; + RECT 41.4 287.85 51.0 288.75 ; + RECT 41.4 273.15 51.0 274.05 ; + RECT 48.0 274.05 49.2 276.45 ; + RECT 48.0 286.05 49.2 287.85 ; + RECT 43.2 286.95 44.4 287.85 ; + RECT 43.2 274.05 44.4 275.25 ; + RECT 45.6 276.3 46.8 285.75 ; + RECT 41.4 281.1 43.5 282.0 ; + RECT 46.8 281.1 51.0 282.0 ; + RECT 43.2 282.15 44.4 283.35 ; + RECT 45.6 282.15 46.8 283.35 ; + RECT 43.2 281.25 44.4 282.45 ; + RECT 45.6 281.25 46.8 282.45 ; + RECT 48.0 276.45 49.2 277.65 ; + RECT 48.0 286.95 49.2 288.15 ; + RECT 43.5 282.15 44.7 283.35 ; + RECT 41.4 287.85 51.0 288.75 ; + RECT 41.4 302.55 51.0 303.45 ; + RECT 48.0 300.15 49.2 302.55 ; + RECT 48.0 288.75 49.2 290.55 ; + RECT 43.2 288.75 44.4 289.65 ; + RECT 43.2 301.35 44.4 302.55 ; + RECT 45.6 290.85 46.8 300.3 ; + RECT 41.4 294.6 43.5 295.5 ; + RECT 46.8 294.6 51.0 295.5 ; + RECT 43.2 290.85 44.4 292.05 ; + RECT 45.6 290.85 46.8 292.05 ; + RECT 43.2 300.15 44.4 301.35 ; + RECT 45.6 300.15 46.8 301.35 ; + RECT 48.0 300.15 49.2 301.35 ; + RECT 48.0 289.65 49.2 290.85 ; + RECT 43.5 294.45 44.7 295.65 ; + RECT 41.4 317.25 51.0 318.15 ; + RECT 41.4 302.55 51.0 303.45 ; + RECT 48.0 303.45 49.2 305.85 ; + RECT 48.0 315.45 49.2 317.25 ; + RECT 43.2 316.35 44.4 317.25 ; + RECT 43.2 303.45 44.4 304.65 ; + RECT 45.6 305.7 46.8 315.15 ; + RECT 41.4 310.5 43.5 311.4 ; + RECT 46.8 310.5 51.0 311.4 ; + RECT 43.2 311.55 44.4 312.75 ; + RECT 45.6 311.55 46.8 312.75 ; + RECT 43.2 310.65 44.4 311.85 ; + RECT 45.6 310.65 46.8 311.85 ; + RECT 48.0 305.85 49.2 307.05 ; + RECT 48.0 316.35 49.2 317.55 ; + RECT 43.5 311.55 44.7 312.75 ; + RECT 41.4 317.25 51.0 318.15 ; + RECT 41.4 331.95 51.0 332.85 ; + RECT 48.0 329.55 49.2 331.95 ; + RECT 48.0 318.15 49.2 319.95 ; + RECT 43.2 318.15 44.4 319.05 ; + RECT 43.2 330.75 44.4 331.95 ; + RECT 45.6 320.25 46.8 329.7 ; + RECT 41.4 324.0 43.5 324.9 ; + RECT 46.8 324.0 51.0 324.9 ; + RECT 43.2 320.25 44.4 321.45 ; + RECT 45.6 320.25 46.8 321.45 ; + RECT 43.2 329.55 44.4 330.75 ; + RECT 45.6 329.55 46.8 330.75 ; + RECT 48.0 329.55 49.2 330.75 ; + RECT 48.0 319.05 49.2 320.25 ; + RECT 43.5 323.85 44.7 325.05 ; + RECT 41.4 346.65 51.0 347.55 ; + RECT 41.4 331.95 51.0 332.85 ; + RECT 48.0 332.85 49.2 335.25 ; + RECT 48.0 344.85 49.2 346.65 ; + RECT 43.2 345.75 44.4 346.65 ; + RECT 43.2 332.85 44.4 334.05 ; + RECT 45.6 335.1 46.8 344.55 ; + RECT 41.4 339.9 43.5 340.8 ; + RECT 46.8 339.9 51.0 340.8 ; + RECT 43.2 340.95 44.4 342.15 ; + RECT 45.6 340.95 46.8 342.15 ; + RECT 43.2 340.05 44.4 341.25 ; + RECT 45.6 340.05 46.8 341.25 ; + RECT 48.0 335.25 49.2 336.45 ; + RECT 48.0 345.75 49.2 346.95 ; + RECT 43.5 340.95 44.7 342.15 ; + RECT 41.4 346.65 51.0 347.55 ; + RECT 41.4 361.35 51.0 362.25 ; + RECT 48.0 358.95 49.2 361.35 ; + RECT 48.0 347.55 49.2 349.35 ; + RECT 43.2 347.55 44.4 348.45 ; + RECT 43.2 360.15 44.4 361.35 ; + RECT 45.6 349.65 46.8 359.1 ; + RECT 41.4 353.4 43.5 354.3 ; + RECT 46.8 353.4 51.0 354.3 ; + RECT 43.2 349.65 44.4 350.85 ; + RECT 45.6 349.65 46.8 350.85 ; + RECT 43.2 358.95 44.4 360.15 ; + RECT 45.6 358.95 46.8 360.15 ; + RECT 48.0 358.95 49.2 360.15 ; + RECT 48.0 348.45 49.2 349.65 ; + RECT 43.5 353.25 44.7 354.45 ; + RECT 41.4 376.05 51.0 376.95 ; + RECT 41.4 361.35 51.0 362.25 ; + RECT 48.0 362.25 49.2 364.65 ; + RECT 48.0 374.25 49.2 376.05 ; + RECT 43.2 375.15 44.4 376.05 ; + RECT 43.2 362.25 44.4 363.45 ; + RECT 45.6 364.5 46.8 373.95 ; + RECT 41.4 369.3 43.5 370.2 ; + RECT 46.8 369.3 51.0 370.2 ; + RECT 43.2 370.35 44.4 371.55 ; + RECT 45.6 370.35 46.8 371.55 ; + RECT 43.2 369.45 44.4 370.65 ; + RECT 45.6 369.45 46.8 370.65 ; + RECT 48.0 364.65 49.2 365.85 ; + RECT 48.0 375.15 49.2 376.35 ; + RECT 43.5 370.35 44.7 371.55 ; + RECT 41.4 376.05 51.0 376.95 ; + RECT 41.4 390.75 51.0 391.65 ; + RECT 48.0 388.35 49.2 390.75 ; + RECT 48.0 376.95 49.2 378.75 ; + RECT 43.2 376.95 44.4 377.85 ; + RECT 43.2 389.55 44.4 390.75 ; + RECT 45.6 379.05 46.8 388.5 ; + RECT 41.4 382.8 43.5 383.7 ; + RECT 46.8 382.8 51.0 383.7 ; + RECT 43.2 379.05 44.4 380.25 ; + RECT 45.6 379.05 46.8 380.25 ; + RECT 43.2 388.35 44.4 389.55 ; + RECT 45.6 388.35 46.8 389.55 ; + RECT 48.0 388.35 49.2 389.55 ; + RECT 48.0 377.85 49.2 379.05 ; + RECT 43.5 382.65 44.7 383.85 ; + RECT 41.4 405.45 51.0 406.35 ; + RECT 41.4 390.75 51.0 391.65 ; + RECT 48.0 391.65 49.2 394.05 ; + RECT 48.0 403.65 49.2 405.45 ; + RECT 43.2 404.55 44.4 405.45 ; + RECT 43.2 391.65 44.4 392.85 ; + RECT 45.6 393.9 46.8 403.35 ; + RECT 41.4 398.7 43.5 399.6 ; + RECT 46.8 398.7 51.0 399.6 ; + RECT 43.2 399.75 44.4 400.95 ; + RECT 45.6 399.75 46.8 400.95 ; + RECT 43.2 398.85 44.4 400.05 ; + RECT 45.6 398.85 46.8 400.05 ; + RECT 48.0 394.05 49.2 395.25 ; + RECT 48.0 404.55 49.2 405.75 ; + RECT 43.5 399.75 44.7 400.95 ; + RECT 41.4 405.45 51.0 406.35 ; + RECT 41.4 420.15 51.0 421.05 ; + RECT 48.0 417.75 49.2 420.15 ; + RECT 48.0 406.35 49.2 408.15 ; + RECT 43.2 406.35 44.4 407.25 ; + RECT 43.2 418.95 44.4 420.15 ; + RECT 45.6 408.45 46.8 417.9 ; + RECT 41.4 412.2 43.5 413.1 ; + RECT 46.8 412.2 51.0 413.1 ; + RECT 43.2 408.45 44.4 409.65 ; + RECT 45.6 408.45 46.8 409.65 ; + RECT 43.2 417.75 44.4 418.95 ; + RECT 45.6 417.75 46.8 418.95 ; + RECT 48.0 417.75 49.2 418.95 ; + RECT 48.0 407.25 49.2 408.45 ; + RECT 43.5 412.05 44.7 413.25 ; + RECT 41.4 434.85 51.0 435.75 ; + RECT 41.4 420.15 51.0 421.05 ; + RECT 48.0 421.05 49.2 423.45 ; + RECT 48.0 433.05 49.2 434.85 ; + RECT 43.2 433.95 44.4 434.85 ; + RECT 43.2 421.05 44.4 422.25 ; + RECT 45.6 423.3 46.8 432.75 ; + RECT 41.4 428.1 43.5 429.0 ; + RECT 46.8 428.1 51.0 429.0 ; + RECT 43.2 429.15 44.4 430.35 ; + RECT 45.6 429.15 46.8 430.35 ; + RECT 43.2 428.25 44.4 429.45 ; + RECT 45.6 428.25 46.8 429.45 ; + RECT 48.0 423.45 49.2 424.65 ; + RECT 48.0 433.95 49.2 435.15 ; + RECT 43.5 429.15 44.7 430.35 ; + RECT 12.6 88.8 13.8 90.0 ; + RECT 14.7 104.7 15.9 105.9 ; + RECT 16.8 118.2 18.0 119.4 ; + RECT 18.9 134.1 20.1 135.3 ; + RECT 21.0 147.6 22.2 148.8 ; + RECT 23.1 163.5 24.3 164.7 ; + RECT 25.2 177.0 26.4 178.2 ; + RECT 27.3 192.9 28.5 194.1 ; + RECT 12.6 208.8 13.8 210.0 ; + RECT 21.0 205.5 22.2 206.7 ; + RECT 12.6 219.9 13.8 221.1 ; + RECT 23.1 223.2 24.3 224.4 ; + RECT 12.6 238.2 13.8 239.4 ; + RECT 25.2 234.9 26.4 236.1 ; + RECT 12.6 249.3 13.8 250.5 ; + RECT 27.3 252.6 28.5 253.8 ; + RECT 14.7 267.6 15.9 268.8 ; + RECT 21.0 264.3 22.2 265.5 ; + RECT 14.7 278.7 15.9 279.9 ; + RECT 23.1 282.0 24.3 283.2 ; + RECT 14.7 297.0 15.9 298.2 ; + RECT 25.2 293.7 26.4 294.9 ; + RECT 14.7 308.1 15.9 309.3 ; + RECT 27.3 311.4 28.5 312.6 ; + RECT 16.8 326.4 18.0 327.6 ; + RECT 21.0 323.1 22.2 324.3 ; + RECT 16.8 337.5 18.0 338.7 ; + RECT 23.1 340.8 24.3 342.0 ; + RECT 16.8 355.8 18.0 357.0 ; + RECT 25.2 352.5 26.4 353.7 ; + RECT 16.8 366.9 18.0 368.1 ; + RECT 27.3 370.2 28.5 371.4 ; + RECT 18.9 385.2 20.1 386.4 ; + RECT 21.0 381.9 22.2 383.1 ; + RECT 18.9 396.3 20.1 397.5 ; + RECT 23.1 399.6 24.3 400.8 ; + RECT 18.9 414.6 20.1 415.8 ; + RECT 25.2 411.3 26.4 412.5 ; + RECT 18.9 425.7 20.1 426.9 ; + RECT 27.3 429.0 28.5 430.2 ; + RECT 53.7 201.9 54.6 440.7 ; + RECT 53.7 206.4 58.2 207.3 ; + RECT 66.0 205.5 66.9 207.3 ; + RECT 78.9 206.4 79.8 207.3 ; + RECT 88.5 206.4 89.4 207.3 ; + RECT 53.7 222.3 58.2 223.2 ; + RECT 66.0 222.3 66.9 224.1 ; + RECT 78.9 222.3 79.8 223.2 ; + RECT 87.6 222.3 88.5 223.2 ; + RECT 53.7 235.8 58.2 236.7 ; + RECT 66.0 234.9 66.9 236.7 ; + RECT 78.9 235.8 79.8 236.7 ; + RECT 88.5 235.8 89.4 236.7 ; + RECT 53.7 251.7 58.2 252.6 ; + RECT 66.0 251.7 66.9 253.5 ; + RECT 78.9 251.7 79.8 252.6 ; + RECT 87.6 251.7 88.5 252.6 ; + RECT 53.7 265.2 58.2 266.1 ; + RECT 66.0 264.3 66.9 266.1 ; + RECT 78.9 265.2 79.8 266.1 ; + RECT 88.5 265.2 89.4 266.1 ; + RECT 53.7 281.1 58.2 282.0 ; + RECT 66.0 281.1 66.9 282.9 ; + RECT 78.9 281.1 79.8 282.0 ; + RECT 87.6 281.1 88.5 282.0 ; + RECT 53.7 294.6 58.2 295.5 ; + RECT 66.0 293.7 66.9 295.5 ; + RECT 78.9 294.6 79.8 295.5 ; + RECT 88.5 294.6 89.4 295.5 ; + RECT 53.7 310.5 58.2 311.4 ; + RECT 66.0 310.5 66.9 312.3 ; + RECT 78.9 310.5 79.8 311.4 ; + RECT 87.6 310.5 88.5 311.4 ; + RECT 53.7 324.0 58.2 324.9 ; + RECT 66.0 323.1 66.9 324.9 ; + RECT 78.9 324.0 79.8 324.9 ; + RECT 88.5 324.0 89.4 324.9 ; + RECT 53.7 339.9 58.2 340.8 ; + RECT 66.0 339.9 66.9 341.7 ; + RECT 78.9 339.9 79.8 340.8 ; + RECT 87.6 339.9 88.5 340.8 ; + RECT 53.7 353.4 58.2 354.3 ; + RECT 66.0 352.5 66.9 354.3 ; + RECT 78.9 353.4 79.8 354.3 ; + RECT 88.5 353.4 89.4 354.3 ; + RECT 53.7 369.3 58.2 370.2 ; + RECT 66.0 369.3 66.9 371.1 ; + RECT 78.9 369.3 79.8 370.2 ; + RECT 87.6 369.3 88.5 370.2 ; + RECT 53.7 382.8 58.2 383.7 ; + RECT 66.0 381.9 66.9 383.7 ; + RECT 78.9 382.8 79.8 383.7 ; + RECT 88.5 382.8 89.4 383.7 ; + RECT 53.7 398.7 58.2 399.6 ; + RECT 66.0 398.7 66.9 400.5 ; + RECT 78.9 398.7 79.8 399.6 ; + RECT 87.6 398.7 88.5 399.6 ; + RECT 53.7 412.2 58.2 413.1 ; + RECT 66.0 411.3 66.9 413.1 ; + RECT 78.9 412.2 79.8 413.1 ; + RECT 88.5 412.2 89.4 413.1 ; + RECT 53.7 428.1 58.2 429.0 ; + RECT 66.0 428.1 66.9 429.9 ; + RECT 78.9 428.1 79.8 429.0 ; + RECT 87.6 428.1 88.5 429.0 ; + RECT 50.7 214.35 51.9 215.55 ; + RECT 57.0 214.35 58.2 215.55 ; + RECT 57.3 199.65 66.9 200.55 ; + RECT 57.3 214.35 66.9 215.25 ; + RECT 63.9 211.95 65.1 214.35 ; + RECT 63.9 200.55 65.1 202.35 ; + RECT 59.1 200.55 60.3 201.45 ; + RECT 59.1 213.15 60.3 214.35 ; + RECT 61.5 202.65 62.7 212.1 ; + RECT 57.3 206.4 59.4 207.3 ; + RECT 62.7 206.4 66.9 207.3 ; + RECT 59.1 202.65 60.3 203.85 ; + RECT 61.5 202.65 62.7 203.85 ; + RECT 59.1 211.95 60.3 213.15 ; + RECT 61.5 211.95 62.7 213.15 ; + RECT 63.9 211.95 65.1 213.15 ; + RECT 63.9 201.45 65.1 202.65 ; + RECT 59.4 206.25 60.6 207.45 ; + RECT 66.9 199.65 78.9 200.55 ; + RECT 66.9 214.35 78.9 215.25 ; + RECT 75.9 211.95 76.8 214.8 ; + RECT 75.9 200.1 76.8 202.5 ; + RECT 68.85 211.95 69.75 214.8 ; + RECT 73.65 211.95 74.55 214.8 ; + RECT 68.85 200.1 69.75 202.95 ; + RECT 66.9 208.8 69.0 209.7 ; + RECT 66.9 205.5 72.0 206.4 ; + RECT 74.55 206.4 78.9 207.3 ; + RECT 68.7 202.95 69.9 204.15 ; + RECT 71.1 202.95 72.3 204.15 ; + RECT 71.1 202.95 72.3 204.15 ; + RECT 73.5 202.95 74.7 204.15 ; + RECT 68.7 211.95 69.9 213.15 ; + RECT 71.1 211.95 72.3 213.15 ; + RECT 71.1 211.95 72.3 213.15 ; + RECT 73.5 211.95 74.7 213.15 ; + RECT 75.9 211.95 77.1 213.15 ; + RECT 75.9 201.75 77.1 202.95 ; + RECT 74.1 208.8 75.0 209.7 ; + RECT 71.1 208.8 72.0 209.7 ; + RECT 74.1 201.75 75.0 209.25 ; + RECT 71.55 208.8 74.55 209.7 ; + RECT 71.1 209.25 72.0 211.95 ; + RECT 69.0 208.5 70.2 209.7 ; + RECT 72.0 205.2 73.2 206.4 ; + RECT 78.9 199.65 88.5 200.55 ; + RECT 78.9 214.35 88.5 215.25 ; + RECT 85.5 211.95 86.7 214.35 ; + RECT 85.5 200.55 86.7 202.35 ; + RECT 80.7 200.55 81.9 201.45 ; + RECT 80.7 213.15 81.9 214.35 ; + RECT 83.1 202.65 84.3 212.1 ; + RECT 78.9 206.4 81.0 207.3 ; + RECT 84.3 206.4 88.5 207.3 ; + RECT 80.7 202.65 81.9 203.85 ; + RECT 83.1 202.65 84.3 203.85 ; + RECT 80.7 211.95 81.9 213.15 ; + RECT 83.1 211.95 84.3 213.15 ; + RECT 85.5 211.95 86.7 213.15 ; + RECT 85.5 201.45 86.7 202.65 ; + RECT 81.0 206.25 82.2 207.45 ; + RECT 66.9 208.8 68.1 210.0 ; + RECT 51.0 208.8 52.2 210.0 ; + RECT 50.7 229.05 51.9 230.25 ; + RECT 57.0 229.05 58.2 230.25 ; + RECT 57.3 229.05 66.9 229.95 ; + RECT 57.3 214.35 66.9 215.25 ; + RECT 63.9 215.25 65.1 217.65 ; + RECT 63.9 227.25 65.1 229.05 ; + RECT 59.1 228.15 60.3 229.05 ; + RECT 59.1 215.25 60.3 216.45 ; + RECT 61.5 217.5 62.7 226.95 ; + RECT 57.3 222.3 59.4 223.2 ; + RECT 62.7 222.3 66.9 223.2 ; + RECT 59.1 223.35 60.3 224.55 ; + RECT 61.5 223.35 62.7 224.55 ; + RECT 59.1 222.45 60.3 223.65 ; + RECT 61.5 222.45 62.7 223.65 ; + RECT 63.9 217.65 65.1 218.85 ; + RECT 63.9 228.15 65.1 229.35 ; + RECT 59.4 223.35 60.6 224.55 ; + RECT 66.9 229.05 78.9 229.95 ; + RECT 66.9 214.35 78.9 215.25 ; + RECT 75.9 214.8 76.8 217.65 ; + RECT 75.9 227.1 76.8 229.5 ; + RECT 68.85 214.8 69.75 217.65 ; + RECT 73.65 214.8 74.55 217.65 ; + RECT 68.85 226.65 69.75 229.5 ; + RECT 66.9 219.9 69.0 220.8 ; + RECT 66.9 223.2 72.0 224.1 ; + RECT 74.55 222.3 78.9 223.2 ; + RECT 68.7 221.85 69.9 223.05 ; + RECT 71.1 221.85 72.3 223.05 ; + RECT 71.1 221.85 72.3 223.05 ; + RECT 73.5 221.85 74.7 223.05 ; + RECT 68.7 222.45 69.9 223.65 ; + RECT 71.1 222.45 72.3 223.65 ; + RECT 71.1 222.45 72.3 223.65 ; + RECT 73.5 222.45 74.7 223.65 ; + RECT 75.9 217.65 77.1 218.85 ; + RECT 75.9 227.85 77.1 229.05 ; + RECT 74.1 234.9 75.0 235.8 ; + RECT 71.1 234.9 72.0 235.8 ; + RECT 74.1 227.85 75.0 235.35 ; + RECT 71.55 234.9 74.55 235.8 ; + RECT 71.1 235.35 72.0 238.05 ; + RECT 69.0 221.1 70.2 222.3 ; + RECT 72.0 224.4 73.2 225.6 ; + RECT 78.9 229.05 88.5 229.95 ; + RECT 78.9 214.35 88.5 215.25 ; + RECT 85.5 215.25 86.7 217.65 ; + RECT 85.5 227.25 86.7 229.05 ; + RECT 80.7 228.15 81.9 229.05 ; + RECT 80.7 215.25 81.9 216.45 ; + RECT 83.1 217.5 84.3 226.95 ; + RECT 78.9 222.3 81.0 223.2 ; + RECT 84.3 222.3 88.5 223.2 ; + RECT 80.7 223.35 81.9 224.55 ; + RECT 83.1 223.35 84.3 224.55 ; + RECT 80.7 222.45 81.9 223.65 ; + RECT 83.1 222.45 84.3 223.65 ; + RECT 85.5 217.65 86.7 218.85 ; + RECT 85.5 228.15 86.7 229.35 ; + RECT 81.0 223.35 82.2 224.55 ; + RECT 66.9 219.6 68.1 220.8 ; + RECT 51.0 219.6 52.2 220.8 ; + RECT 50.7 243.75 51.9 244.95 ; + RECT 57.0 243.75 58.2 244.95 ; + RECT 57.3 229.05 66.9 229.95 ; + RECT 57.3 243.75 66.9 244.65 ; + RECT 63.9 241.35 65.1 243.75 ; + RECT 63.9 229.95 65.1 231.75 ; + RECT 59.1 229.95 60.3 230.85 ; + RECT 59.1 242.55 60.3 243.75 ; + RECT 61.5 232.05 62.7 241.5 ; + RECT 57.3 235.8 59.4 236.7 ; + RECT 62.7 235.8 66.9 236.7 ; + RECT 59.1 232.05 60.3 233.25 ; + RECT 61.5 232.05 62.7 233.25 ; + RECT 59.1 241.35 60.3 242.55 ; + RECT 61.5 241.35 62.7 242.55 ; + RECT 63.9 241.35 65.1 242.55 ; + RECT 63.9 230.85 65.1 232.05 ; + RECT 59.4 235.65 60.6 236.85 ; + RECT 66.9 229.05 78.9 229.95 ; + RECT 66.9 243.75 78.9 244.65 ; + RECT 75.9 241.35 76.8 244.2 ; + RECT 75.9 229.5 76.8 231.9 ; + RECT 68.85 241.35 69.75 244.2 ; + RECT 73.65 241.35 74.55 244.2 ; + RECT 68.85 229.5 69.75 232.35 ; + RECT 66.9 238.2 69.0 239.1 ; + RECT 66.9 234.9 72.0 235.8 ; + RECT 74.55 235.8 78.9 236.7 ; + RECT 68.7 232.35 69.9 233.55 ; + RECT 71.1 232.35 72.3 233.55 ; + RECT 71.1 232.35 72.3 233.55 ; + RECT 73.5 232.35 74.7 233.55 ; + RECT 68.7 241.35 69.9 242.55 ; + RECT 71.1 241.35 72.3 242.55 ; + RECT 71.1 241.35 72.3 242.55 ; + RECT 73.5 241.35 74.7 242.55 ; + RECT 75.9 241.35 77.1 242.55 ; + RECT 75.9 231.15 77.1 232.35 ; + RECT 74.1 238.2 75.0 239.1 ; + RECT 71.1 238.2 72.0 239.1 ; + RECT 74.1 231.15 75.0 238.65 ; + RECT 71.55 238.2 74.55 239.1 ; + RECT 71.1 238.65 72.0 241.35 ; + RECT 69.0 237.9 70.2 239.1 ; + RECT 72.0 234.6 73.2 235.8 ; + RECT 78.9 229.05 88.5 229.95 ; + RECT 78.9 243.75 88.5 244.65 ; + RECT 85.5 241.35 86.7 243.75 ; + RECT 85.5 229.95 86.7 231.75 ; + RECT 80.7 229.95 81.9 230.85 ; + RECT 80.7 242.55 81.9 243.75 ; + RECT 83.1 232.05 84.3 241.5 ; + RECT 78.9 235.8 81.0 236.7 ; + RECT 84.3 235.8 88.5 236.7 ; + RECT 80.7 232.05 81.9 233.25 ; + RECT 83.1 232.05 84.3 233.25 ; + RECT 80.7 241.35 81.9 242.55 ; + RECT 83.1 241.35 84.3 242.55 ; + RECT 85.5 241.35 86.7 242.55 ; + RECT 85.5 230.85 86.7 232.05 ; + RECT 81.0 235.65 82.2 236.85 ; + RECT 66.9 238.2 68.1 239.4 ; + RECT 51.0 238.2 52.2 239.4 ; + RECT 50.7 258.45 51.9 259.65 ; + RECT 57.0 258.45 58.2 259.65 ; + RECT 57.3 258.45 66.9 259.35 ; + RECT 57.3 243.75 66.9 244.65 ; + RECT 63.9 244.65 65.1 247.05 ; + RECT 63.9 256.65 65.1 258.45 ; + RECT 59.1 257.55 60.3 258.45 ; + RECT 59.1 244.65 60.3 245.85 ; + RECT 61.5 246.9 62.7 256.35 ; + RECT 57.3 251.7 59.4 252.6 ; + RECT 62.7 251.7 66.9 252.6 ; + RECT 59.1 252.75 60.3 253.95 ; + RECT 61.5 252.75 62.7 253.95 ; + RECT 59.1 251.85 60.3 253.05 ; + RECT 61.5 251.85 62.7 253.05 ; + RECT 63.9 247.05 65.1 248.25 ; + RECT 63.9 257.55 65.1 258.75 ; + RECT 59.4 252.75 60.6 253.95 ; + RECT 66.9 258.45 78.9 259.35 ; + RECT 66.9 243.75 78.9 244.65 ; + RECT 75.9 244.2 76.8 247.05 ; + RECT 75.9 256.5 76.8 258.9 ; + RECT 68.85 244.2 69.75 247.05 ; + RECT 73.65 244.2 74.55 247.05 ; + RECT 68.85 256.05 69.75 258.9 ; + RECT 66.9 249.3 69.0 250.2 ; + RECT 66.9 252.6 72.0 253.5 ; + RECT 74.55 251.7 78.9 252.6 ; + RECT 68.7 251.25 69.9 252.45 ; + RECT 71.1 251.25 72.3 252.45 ; + RECT 71.1 251.25 72.3 252.45 ; + RECT 73.5 251.25 74.7 252.45 ; + RECT 68.7 251.85 69.9 253.05 ; + RECT 71.1 251.85 72.3 253.05 ; + RECT 71.1 251.85 72.3 253.05 ; + RECT 73.5 251.85 74.7 253.05 ; + RECT 75.9 247.05 77.1 248.25 ; + RECT 75.9 257.25 77.1 258.45 ; + RECT 74.1 264.3 75.0 265.2 ; + RECT 71.1 264.3 72.0 265.2 ; + RECT 74.1 257.25 75.0 264.75 ; + RECT 71.55 264.3 74.55 265.2 ; + RECT 71.1 264.75 72.0 267.45 ; + RECT 69.0 250.5 70.2 251.7 ; + RECT 72.0 253.8 73.2 255.0 ; + RECT 78.9 258.45 88.5 259.35 ; + RECT 78.9 243.75 88.5 244.65 ; + RECT 85.5 244.65 86.7 247.05 ; + RECT 85.5 256.65 86.7 258.45 ; + RECT 80.7 257.55 81.9 258.45 ; + RECT 80.7 244.65 81.9 245.85 ; + RECT 83.1 246.9 84.3 256.35 ; + RECT 78.9 251.7 81.0 252.6 ; + RECT 84.3 251.7 88.5 252.6 ; + RECT 80.7 252.75 81.9 253.95 ; + RECT 83.1 252.75 84.3 253.95 ; + RECT 80.7 251.85 81.9 253.05 ; + RECT 83.1 251.85 84.3 253.05 ; + RECT 85.5 247.05 86.7 248.25 ; + RECT 85.5 257.55 86.7 258.75 ; + RECT 81.0 252.75 82.2 253.95 ; + RECT 66.9 249.0 68.1 250.2 ; + RECT 51.0 249.0 52.2 250.2 ; + RECT 50.7 273.15 51.9 274.35 ; + RECT 57.0 273.15 58.2 274.35 ; + RECT 57.3 258.45 66.9 259.35 ; + RECT 57.3 273.15 66.9 274.05 ; + RECT 63.9 270.75 65.1 273.15 ; + RECT 63.9 259.35 65.1 261.15 ; + RECT 59.1 259.35 60.3 260.25 ; + RECT 59.1 271.95 60.3 273.15 ; + RECT 61.5 261.45 62.7 270.9 ; + RECT 57.3 265.2 59.4 266.1 ; + RECT 62.7 265.2 66.9 266.1 ; + RECT 59.1 261.45 60.3 262.65 ; + RECT 61.5 261.45 62.7 262.65 ; + RECT 59.1 270.75 60.3 271.95 ; + RECT 61.5 270.75 62.7 271.95 ; + RECT 63.9 270.75 65.1 271.95 ; + RECT 63.9 260.25 65.1 261.45 ; + RECT 59.4 265.05 60.6 266.25 ; + RECT 66.9 258.45 78.9 259.35 ; + RECT 66.9 273.15 78.9 274.05 ; + RECT 75.9 270.75 76.8 273.6 ; + RECT 75.9 258.9 76.8 261.3 ; + RECT 68.85 270.75 69.75 273.6 ; + RECT 73.65 270.75 74.55 273.6 ; + RECT 68.85 258.9 69.75 261.75 ; + RECT 66.9 267.6 69.0 268.5 ; + RECT 66.9 264.3 72.0 265.2 ; + RECT 74.55 265.2 78.9 266.1 ; + RECT 68.7 261.75 69.9 262.95 ; + RECT 71.1 261.75 72.3 262.95 ; + RECT 71.1 261.75 72.3 262.95 ; + RECT 73.5 261.75 74.7 262.95 ; + RECT 68.7 270.75 69.9 271.95 ; + RECT 71.1 270.75 72.3 271.95 ; + RECT 71.1 270.75 72.3 271.95 ; + RECT 73.5 270.75 74.7 271.95 ; + RECT 75.9 270.75 77.1 271.95 ; + RECT 75.9 260.55 77.1 261.75 ; + RECT 74.1 267.6 75.0 268.5 ; + RECT 71.1 267.6 72.0 268.5 ; + RECT 74.1 260.55 75.0 268.05 ; + RECT 71.55 267.6 74.55 268.5 ; + RECT 71.1 268.05 72.0 270.75 ; + RECT 69.0 267.3 70.2 268.5 ; + RECT 72.0 264.0 73.2 265.2 ; + RECT 78.9 258.45 88.5 259.35 ; + RECT 78.9 273.15 88.5 274.05 ; + RECT 85.5 270.75 86.7 273.15 ; + RECT 85.5 259.35 86.7 261.15 ; + RECT 80.7 259.35 81.9 260.25 ; + RECT 80.7 271.95 81.9 273.15 ; + RECT 83.1 261.45 84.3 270.9 ; + RECT 78.9 265.2 81.0 266.1 ; + RECT 84.3 265.2 88.5 266.1 ; + RECT 80.7 261.45 81.9 262.65 ; + RECT 83.1 261.45 84.3 262.65 ; + RECT 80.7 270.75 81.9 271.95 ; + RECT 83.1 270.75 84.3 271.95 ; + RECT 85.5 270.75 86.7 271.95 ; + RECT 85.5 260.25 86.7 261.45 ; + RECT 81.0 265.05 82.2 266.25 ; + RECT 66.9 267.6 68.1 268.8 ; + RECT 51.0 267.6 52.2 268.8 ; + RECT 50.7 287.85 51.9 289.05 ; + RECT 57.0 287.85 58.2 289.05 ; + RECT 57.3 287.85 66.9 288.75 ; + RECT 57.3 273.15 66.9 274.05 ; + RECT 63.9 274.05 65.1 276.45 ; + RECT 63.9 286.05 65.1 287.85 ; + RECT 59.1 286.95 60.3 287.85 ; + RECT 59.1 274.05 60.3 275.25 ; + RECT 61.5 276.3 62.7 285.75 ; + RECT 57.3 281.1 59.4 282.0 ; + RECT 62.7 281.1 66.9 282.0 ; + RECT 59.1 282.15 60.3 283.35 ; + RECT 61.5 282.15 62.7 283.35 ; + RECT 59.1 281.25 60.3 282.45 ; + RECT 61.5 281.25 62.7 282.45 ; + RECT 63.9 276.45 65.1 277.65 ; + RECT 63.9 286.95 65.1 288.15 ; + RECT 59.4 282.15 60.6 283.35 ; + RECT 66.9 287.85 78.9 288.75 ; + RECT 66.9 273.15 78.9 274.05 ; + RECT 75.9 273.6 76.8 276.45 ; + RECT 75.9 285.9 76.8 288.3 ; + RECT 68.85 273.6 69.75 276.45 ; + RECT 73.65 273.6 74.55 276.45 ; + RECT 68.85 285.45 69.75 288.3 ; + RECT 66.9 278.7 69.0 279.6 ; + RECT 66.9 282.0 72.0 282.9 ; + RECT 74.55 281.1 78.9 282.0 ; + RECT 68.7 280.65 69.9 281.85 ; + RECT 71.1 280.65 72.3 281.85 ; + RECT 71.1 280.65 72.3 281.85 ; + RECT 73.5 280.65 74.7 281.85 ; + RECT 68.7 281.25 69.9 282.45 ; + RECT 71.1 281.25 72.3 282.45 ; + RECT 71.1 281.25 72.3 282.45 ; + RECT 73.5 281.25 74.7 282.45 ; + RECT 75.9 276.45 77.1 277.65 ; + RECT 75.9 286.65 77.1 287.85 ; + RECT 74.1 293.7 75.0 294.6 ; + RECT 71.1 293.7 72.0 294.6 ; + RECT 74.1 286.65 75.0 294.15 ; + RECT 71.55 293.7 74.55 294.6 ; + RECT 71.1 294.15 72.0 296.85 ; + RECT 69.0 279.9 70.2 281.1 ; + RECT 72.0 283.2 73.2 284.4 ; + RECT 78.9 287.85 88.5 288.75 ; + RECT 78.9 273.15 88.5 274.05 ; + RECT 85.5 274.05 86.7 276.45 ; + RECT 85.5 286.05 86.7 287.85 ; + RECT 80.7 286.95 81.9 287.85 ; + RECT 80.7 274.05 81.9 275.25 ; + RECT 83.1 276.3 84.3 285.75 ; + RECT 78.9 281.1 81.0 282.0 ; + RECT 84.3 281.1 88.5 282.0 ; + RECT 80.7 282.15 81.9 283.35 ; + RECT 83.1 282.15 84.3 283.35 ; + RECT 80.7 281.25 81.9 282.45 ; + RECT 83.1 281.25 84.3 282.45 ; + RECT 85.5 276.45 86.7 277.65 ; + RECT 85.5 286.95 86.7 288.15 ; + RECT 81.0 282.15 82.2 283.35 ; + RECT 66.9 278.4 68.1 279.6 ; + RECT 51.0 278.4 52.2 279.6 ; + RECT 50.7 302.55 51.9 303.75 ; + RECT 57.0 302.55 58.2 303.75 ; + RECT 57.3 287.85 66.9 288.75 ; + RECT 57.3 302.55 66.9 303.45 ; + RECT 63.9 300.15 65.1 302.55 ; + RECT 63.9 288.75 65.1 290.55 ; + RECT 59.1 288.75 60.3 289.65 ; + RECT 59.1 301.35 60.3 302.55 ; + RECT 61.5 290.85 62.7 300.3 ; + RECT 57.3 294.6 59.4 295.5 ; + RECT 62.7 294.6 66.9 295.5 ; + RECT 59.1 290.85 60.3 292.05 ; + RECT 61.5 290.85 62.7 292.05 ; + RECT 59.1 300.15 60.3 301.35 ; + RECT 61.5 300.15 62.7 301.35 ; + RECT 63.9 300.15 65.1 301.35 ; + RECT 63.9 289.65 65.1 290.85 ; + RECT 59.4 294.45 60.6 295.65 ; + RECT 66.9 287.85 78.9 288.75 ; + RECT 66.9 302.55 78.9 303.45 ; + RECT 75.9 300.15 76.8 303.0 ; + RECT 75.9 288.3 76.8 290.7 ; + RECT 68.85 300.15 69.75 303.0 ; + RECT 73.65 300.15 74.55 303.0 ; + RECT 68.85 288.3 69.75 291.15 ; + RECT 66.9 297.0 69.0 297.9 ; + RECT 66.9 293.7 72.0 294.6 ; + RECT 74.55 294.6 78.9 295.5 ; + RECT 68.7 291.15 69.9 292.35 ; + RECT 71.1 291.15 72.3 292.35 ; + RECT 71.1 291.15 72.3 292.35 ; + RECT 73.5 291.15 74.7 292.35 ; + RECT 68.7 300.15 69.9 301.35 ; + RECT 71.1 300.15 72.3 301.35 ; + RECT 71.1 300.15 72.3 301.35 ; + RECT 73.5 300.15 74.7 301.35 ; + RECT 75.9 300.15 77.1 301.35 ; + RECT 75.9 289.95 77.1 291.15 ; + RECT 74.1 297.0 75.0 297.9 ; + RECT 71.1 297.0 72.0 297.9 ; + RECT 74.1 289.95 75.0 297.45 ; + RECT 71.55 297.0 74.55 297.9 ; + RECT 71.1 297.45 72.0 300.15 ; + RECT 69.0 296.7 70.2 297.9 ; + RECT 72.0 293.4 73.2 294.6 ; + RECT 78.9 287.85 88.5 288.75 ; + RECT 78.9 302.55 88.5 303.45 ; + RECT 85.5 300.15 86.7 302.55 ; + RECT 85.5 288.75 86.7 290.55 ; + RECT 80.7 288.75 81.9 289.65 ; + RECT 80.7 301.35 81.9 302.55 ; + RECT 83.1 290.85 84.3 300.3 ; + RECT 78.9 294.6 81.0 295.5 ; + RECT 84.3 294.6 88.5 295.5 ; + RECT 80.7 290.85 81.9 292.05 ; + RECT 83.1 290.85 84.3 292.05 ; + RECT 80.7 300.15 81.9 301.35 ; + RECT 83.1 300.15 84.3 301.35 ; + RECT 85.5 300.15 86.7 301.35 ; + RECT 85.5 289.65 86.7 290.85 ; + RECT 81.0 294.45 82.2 295.65 ; + RECT 66.9 297.0 68.1 298.2 ; + RECT 51.0 297.0 52.2 298.2 ; + RECT 50.7 317.25 51.9 318.45 ; + RECT 57.0 317.25 58.2 318.45 ; + RECT 57.3 317.25 66.9 318.15 ; + RECT 57.3 302.55 66.9 303.45 ; + RECT 63.9 303.45 65.1 305.85 ; + RECT 63.9 315.45 65.1 317.25 ; + RECT 59.1 316.35 60.3 317.25 ; + RECT 59.1 303.45 60.3 304.65 ; + RECT 61.5 305.7 62.7 315.15 ; + RECT 57.3 310.5 59.4 311.4 ; + RECT 62.7 310.5 66.9 311.4 ; + RECT 59.1 311.55 60.3 312.75 ; + RECT 61.5 311.55 62.7 312.75 ; + RECT 59.1 310.65 60.3 311.85 ; + RECT 61.5 310.65 62.7 311.85 ; + RECT 63.9 305.85 65.1 307.05 ; + RECT 63.9 316.35 65.1 317.55 ; + RECT 59.4 311.55 60.6 312.75 ; + RECT 66.9 317.25 78.9 318.15 ; + RECT 66.9 302.55 78.9 303.45 ; + RECT 75.9 303.0 76.8 305.85 ; + RECT 75.9 315.3 76.8 317.7 ; + RECT 68.85 303.0 69.75 305.85 ; + RECT 73.65 303.0 74.55 305.85 ; + RECT 68.85 314.85 69.75 317.7 ; + RECT 66.9 308.1 69.0 309.0 ; + RECT 66.9 311.4 72.0 312.3 ; + RECT 74.55 310.5 78.9 311.4 ; + RECT 68.7 310.05 69.9 311.25 ; + RECT 71.1 310.05 72.3 311.25 ; + RECT 71.1 310.05 72.3 311.25 ; + RECT 73.5 310.05 74.7 311.25 ; + RECT 68.7 310.65 69.9 311.85 ; + RECT 71.1 310.65 72.3 311.85 ; + RECT 71.1 310.65 72.3 311.85 ; + RECT 73.5 310.65 74.7 311.85 ; + RECT 75.9 305.85 77.1 307.05 ; + RECT 75.9 316.05 77.1 317.25 ; + RECT 74.1 323.1 75.0 324.0 ; + RECT 71.1 323.1 72.0 324.0 ; + RECT 74.1 316.05 75.0 323.55 ; + RECT 71.55 323.1 74.55 324.0 ; + RECT 71.1 323.55 72.0 326.25 ; + RECT 69.0 309.3 70.2 310.5 ; + RECT 72.0 312.6 73.2 313.8 ; + RECT 78.9 317.25 88.5 318.15 ; + RECT 78.9 302.55 88.5 303.45 ; + RECT 85.5 303.45 86.7 305.85 ; + RECT 85.5 315.45 86.7 317.25 ; + RECT 80.7 316.35 81.9 317.25 ; + RECT 80.7 303.45 81.9 304.65 ; + RECT 83.1 305.7 84.3 315.15 ; + RECT 78.9 310.5 81.0 311.4 ; + RECT 84.3 310.5 88.5 311.4 ; + RECT 80.7 311.55 81.9 312.75 ; + RECT 83.1 311.55 84.3 312.75 ; + RECT 80.7 310.65 81.9 311.85 ; + RECT 83.1 310.65 84.3 311.85 ; + RECT 85.5 305.85 86.7 307.05 ; + RECT 85.5 316.35 86.7 317.55 ; + RECT 81.0 311.55 82.2 312.75 ; + RECT 66.9 307.8 68.1 309.0 ; + RECT 51.0 307.8 52.2 309.0 ; + RECT 50.7 331.95 51.9 333.15 ; + RECT 57.0 331.95 58.2 333.15 ; + RECT 57.3 317.25 66.9 318.15 ; + RECT 57.3 331.95 66.9 332.85 ; + RECT 63.9 329.55 65.1 331.95 ; + RECT 63.9 318.15 65.1 319.95 ; + RECT 59.1 318.15 60.3 319.05 ; + RECT 59.1 330.75 60.3 331.95 ; + RECT 61.5 320.25 62.7 329.7 ; + RECT 57.3 324.0 59.4 324.9 ; + RECT 62.7 324.0 66.9 324.9 ; + RECT 59.1 320.25 60.3 321.45 ; + RECT 61.5 320.25 62.7 321.45 ; + RECT 59.1 329.55 60.3 330.75 ; + RECT 61.5 329.55 62.7 330.75 ; + RECT 63.9 329.55 65.1 330.75 ; + RECT 63.9 319.05 65.1 320.25 ; + RECT 59.4 323.85 60.6 325.05 ; + RECT 66.9 317.25 78.9 318.15 ; + RECT 66.9 331.95 78.9 332.85 ; + RECT 75.9 329.55 76.8 332.4 ; + RECT 75.9 317.7 76.8 320.1 ; + RECT 68.85 329.55 69.75 332.4 ; + RECT 73.65 329.55 74.55 332.4 ; + RECT 68.85 317.7 69.75 320.55 ; + RECT 66.9 326.4 69.0 327.3 ; + RECT 66.9 323.1 72.0 324.0 ; + RECT 74.55 324.0 78.9 324.9 ; + RECT 68.7 320.55 69.9 321.75 ; + RECT 71.1 320.55 72.3 321.75 ; + RECT 71.1 320.55 72.3 321.75 ; + RECT 73.5 320.55 74.7 321.75 ; + RECT 68.7 329.55 69.9 330.75 ; + RECT 71.1 329.55 72.3 330.75 ; + RECT 71.1 329.55 72.3 330.75 ; + RECT 73.5 329.55 74.7 330.75 ; + RECT 75.9 329.55 77.1 330.75 ; + RECT 75.9 319.35 77.1 320.55 ; + RECT 74.1 326.4 75.0 327.3 ; + RECT 71.1 326.4 72.0 327.3 ; + RECT 74.1 319.35 75.0 326.85 ; + RECT 71.55 326.4 74.55 327.3 ; + RECT 71.1 326.85 72.0 329.55 ; + RECT 69.0 326.1 70.2 327.3 ; + RECT 72.0 322.8 73.2 324.0 ; + RECT 78.9 317.25 88.5 318.15 ; + RECT 78.9 331.95 88.5 332.85 ; + RECT 85.5 329.55 86.7 331.95 ; + RECT 85.5 318.15 86.7 319.95 ; + RECT 80.7 318.15 81.9 319.05 ; + RECT 80.7 330.75 81.9 331.95 ; + RECT 83.1 320.25 84.3 329.7 ; + RECT 78.9 324.0 81.0 324.9 ; + RECT 84.3 324.0 88.5 324.9 ; + RECT 80.7 320.25 81.9 321.45 ; + RECT 83.1 320.25 84.3 321.45 ; + RECT 80.7 329.55 81.9 330.75 ; + RECT 83.1 329.55 84.3 330.75 ; + RECT 85.5 329.55 86.7 330.75 ; + RECT 85.5 319.05 86.7 320.25 ; + RECT 81.0 323.85 82.2 325.05 ; + RECT 66.9 326.4 68.1 327.6 ; + RECT 51.0 326.4 52.2 327.6 ; + RECT 50.7 346.65 51.9 347.85 ; + RECT 57.0 346.65 58.2 347.85 ; + RECT 57.3 346.65 66.9 347.55 ; + RECT 57.3 331.95 66.9 332.85 ; + RECT 63.9 332.85 65.1 335.25 ; + RECT 63.9 344.85 65.1 346.65 ; + RECT 59.1 345.75 60.3 346.65 ; + RECT 59.1 332.85 60.3 334.05 ; + RECT 61.5 335.1 62.7 344.55 ; + RECT 57.3 339.9 59.4 340.8 ; + RECT 62.7 339.9 66.9 340.8 ; + RECT 59.1 340.95 60.3 342.15 ; + RECT 61.5 340.95 62.7 342.15 ; + RECT 59.1 340.05 60.3 341.25 ; + RECT 61.5 340.05 62.7 341.25 ; + RECT 63.9 335.25 65.1 336.45 ; + RECT 63.9 345.75 65.1 346.95 ; + RECT 59.4 340.95 60.6 342.15 ; + RECT 66.9 346.65 78.9 347.55 ; + RECT 66.9 331.95 78.9 332.85 ; + RECT 75.9 332.4 76.8 335.25 ; + RECT 75.9 344.7 76.8 347.1 ; + RECT 68.85 332.4 69.75 335.25 ; + RECT 73.65 332.4 74.55 335.25 ; + RECT 68.85 344.25 69.75 347.1 ; + RECT 66.9 337.5 69.0 338.4 ; + RECT 66.9 340.8 72.0 341.7 ; + RECT 74.55 339.9 78.9 340.8 ; + RECT 68.7 339.45 69.9 340.65 ; + RECT 71.1 339.45 72.3 340.65 ; + RECT 71.1 339.45 72.3 340.65 ; + RECT 73.5 339.45 74.7 340.65 ; + RECT 68.7 340.05 69.9 341.25 ; + RECT 71.1 340.05 72.3 341.25 ; + RECT 71.1 340.05 72.3 341.25 ; + RECT 73.5 340.05 74.7 341.25 ; + RECT 75.9 335.25 77.1 336.45 ; + RECT 75.9 345.45 77.1 346.65 ; + RECT 74.1 352.5 75.0 353.4 ; + RECT 71.1 352.5 72.0 353.4 ; + RECT 74.1 345.45 75.0 352.95 ; + RECT 71.55 352.5 74.55 353.4 ; + RECT 71.1 352.95 72.0 355.65 ; + RECT 69.0 338.7 70.2 339.9 ; + RECT 72.0 342.0 73.2 343.2 ; + RECT 78.9 346.65 88.5 347.55 ; + RECT 78.9 331.95 88.5 332.85 ; + RECT 85.5 332.85 86.7 335.25 ; + RECT 85.5 344.85 86.7 346.65 ; + RECT 80.7 345.75 81.9 346.65 ; + RECT 80.7 332.85 81.9 334.05 ; + RECT 83.1 335.1 84.3 344.55 ; + RECT 78.9 339.9 81.0 340.8 ; + RECT 84.3 339.9 88.5 340.8 ; + RECT 80.7 340.95 81.9 342.15 ; + RECT 83.1 340.95 84.3 342.15 ; + RECT 80.7 340.05 81.9 341.25 ; + RECT 83.1 340.05 84.3 341.25 ; + RECT 85.5 335.25 86.7 336.45 ; + RECT 85.5 345.75 86.7 346.95 ; + RECT 81.0 340.95 82.2 342.15 ; + RECT 66.9 337.2 68.1 338.4 ; + RECT 51.0 337.2 52.2 338.4 ; + RECT 50.7 361.35 51.9 362.55 ; + RECT 57.0 361.35 58.2 362.55 ; + RECT 57.3 346.65 66.9 347.55 ; + RECT 57.3 361.35 66.9 362.25 ; + RECT 63.9 358.95 65.1 361.35 ; + RECT 63.9 347.55 65.1 349.35 ; + RECT 59.1 347.55 60.3 348.45 ; + RECT 59.1 360.15 60.3 361.35 ; + RECT 61.5 349.65 62.7 359.1 ; + RECT 57.3 353.4 59.4 354.3 ; + RECT 62.7 353.4 66.9 354.3 ; + RECT 59.1 349.65 60.3 350.85 ; + RECT 61.5 349.65 62.7 350.85 ; + RECT 59.1 358.95 60.3 360.15 ; + RECT 61.5 358.95 62.7 360.15 ; + RECT 63.9 358.95 65.1 360.15 ; + RECT 63.9 348.45 65.1 349.65 ; + RECT 59.4 353.25 60.6 354.45 ; + RECT 66.9 346.65 78.9 347.55 ; + RECT 66.9 361.35 78.9 362.25 ; + RECT 75.9 358.95 76.8 361.8 ; + RECT 75.9 347.1 76.8 349.5 ; + RECT 68.85 358.95 69.75 361.8 ; + RECT 73.65 358.95 74.55 361.8 ; + RECT 68.85 347.1 69.75 349.95 ; + RECT 66.9 355.8 69.0 356.7 ; + RECT 66.9 352.5 72.0 353.4 ; + RECT 74.55 353.4 78.9 354.3 ; + RECT 68.7 349.95 69.9 351.15 ; + RECT 71.1 349.95 72.3 351.15 ; + RECT 71.1 349.95 72.3 351.15 ; + RECT 73.5 349.95 74.7 351.15 ; + RECT 68.7 358.95 69.9 360.15 ; + RECT 71.1 358.95 72.3 360.15 ; + RECT 71.1 358.95 72.3 360.15 ; + RECT 73.5 358.95 74.7 360.15 ; + RECT 75.9 358.95 77.1 360.15 ; + RECT 75.9 348.75 77.1 349.95 ; + RECT 74.1 355.8 75.0 356.7 ; + RECT 71.1 355.8 72.0 356.7 ; + RECT 74.1 348.75 75.0 356.25 ; + RECT 71.55 355.8 74.55 356.7 ; + RECT 71.1 356.25 72.0 358.95 ; + RECT 69.0 355.5 70.2 356.7 ; + RECT 72.0 352.2 73.2 353.4 ; + RECT 78.9 346.65 88.5 347.55 ; + RECT 78.9 361.35 88.5 362.25 ; + RECT 85.5 358.95 86.7 361.35 ; + RECT 85.5 347.55 86.7 349.35 ; + RECT 80.7 347.55 81.9 348.45 ; + RECT 80.7 360.15 81.9 361.35 ; + RECT 83.1 349.65 84.3 359.1 ; + RECT 78.9 353.4 81.0 354.3 ; + RECT 84.3 353.4 88.5 354.3 ; + RECT 80.7 349.65 81.9 350.85 ; + RECT 83.1 349.65 84.3 350.85 ; + RECT 80.7 358.95 81.9 360.15 ; + RECT 83.1 358.95 84.3 360.15 ; + RECT 85.5 358.95 86.7 360.15 ; + RECT 85.5 348.45 86.7 349.65 ; + RECT 81.0 353.25 82.2 354.45 ; + RECT 66.9 355.8 68.1 357.0 ; + RECT 51.0 355.8 52.2 357.0 ; + RECT 50.7 376.05 51.9 377.25 ; + RECT 57.0 376.05 58.2 377.25 ; + RECT 57.3 376.05 66.9 376.95 ; + RECT 57.3 361.35 66.9 362.25 ; + RECT 63.9 362.25 65.1 364.65 ; + RECT 63.9 374.25 65.1 376.05 ; + RECT 59.1 375.15 60.3 376.05 ; + RECT 59.1 362.25 60.3 363.45 ; + RECT 61.5 364.5 62.7 373.95 ; + RECT 57.3 369.3 59.4 370.2 ; + RECT 62.7 369.3 66.9 370.2 ; + RECT 59.1 370.35 60.3 371.55 ; + RECT 61.5 370.35 62.7 371.55 ; + RECT 59.1 369.45 60.3 370.65 ; + RECT 61.5 369.45 62.7 370.65 ; + RECT 63.9 364.65 65.1 365.85 ; + RECT 63.9 375.15 65.1 376.35 ; + RECT 59.4 370.35 60.6 371.55 ; + RECT 66.9 376.05 78.9 376.95 ; + RECT 66.9 361.35 78.9 362.25 ; + RECT 75.9 361.8 76.8 364.65 ; + RECT 75.9 374.1 76.8 376.5 ; + RECT 68.85 361.8 69.75 364.65 ; + RECT 73.65 361.8 74.55 364.65 ; + RECT 68.85 373.65 69.75 376.5 ; + RECT 66.9 366.9 69.0 367.8 ; + RECT 66.9 370.2 72.0 371.1 ; + RECT 74.55 369.3 78.9 370.2 ; + RECT 68.7 368.85 69.9 370.05 ; + RECT 71.1 368.85 72.3 370.05 ; + RECT 71.1 368.85 72.3 370.05 ; + RECT 73.5 368.85 74.7 370.05 ; + RECT 68.7 369.45 69.9 370.65 ; + RECT 71.1 369.45 72.3 370.65 ; + RECT 71.1 369.45 72.3 370.65 ; + RECT 73.5 369.45 74.7 370.65 ; + RECT 75.9 364.65 77.1 365.85 ; + RECT 75.9 374.85 77.1 376.05 ; + RECT 74.1 381.9 75.0 382.8 ; + RECT 71.1 381.9 72.0 382.8 ; + RECT 74.1 374.85 75.0 382.35 ; + RECT 71.55 381.9 74.55 382.8 ; + RECT 71.1 382.35 72.0 385.05 ; + RECT 69.0 368.1 70.2 369.3 ; + RECT 72.0 371.4 73.2 372.6 ; + RECT 78.9 376.05 88.5 376.95 ; + RECT 78.9 361.35 88.5 362.25 ; + RECT 85.5 362.25 86.7 364.65 ; + RECT 85.5 374.25 86.7 376.05 ; + RECT 80.7 375.15 81.9 376.05 ; + RECT 80.7 362.25 81.9 363.45 ; + RECT 83.1 364.5 84.3 373.95 ; + RECT 78.9 369.3 81.0 370.2 ; + RECT 84.3 369.3 88.5 370.2 ; + RECT 80.7 370.35 81.9 371.55 ; + RECT 83.1 370.35 84.3 371.55 ; + RECT 80.7 369.45 81.9 370.65 ; + RECT 83.1 369.45 84.3 370.65 ; + RECT 85.5 364.65 86.7 365.85 ; + RECT 85.5 375.15 86.7 376.35 ; + RECT 81.0 370.35 82.2 371.55 ; + RECT 66.9 366.6 68.1 367.8 ; + RECT 51.0 366.6 52.2 367.8 ; + RECT 50.7 390.75 51.9 391.95 ; + RECT 57.0 390.75 58.2 391.95 ; + RECT 57.3 376.05 66.9 376.95 ; + RECT 57.3 390.75 66.9 391.65 ; + RECT 63.9 388.35 65.1 390.75 ; + RECT 63.9 376.95 65.1 378.75 ; + RECT 59.1 376.95 60.3 377.85 ; + RECT 59.1 389.55 60.3 390.75 ; + RECT 61.5 379.05 62.7 388.5 ; + RECT 57.3 382.8 59.4 383.7 ; + RECT 62.7 382.8 66.9 383.7 ; + RECT 59.1 379.05 60.3 380.25 ; + RECT 61.5 379.05 62.7 380.25 ; + RECT 59.1 388.35 60.3 389.55 ; + RECT 61.5 388.35 62.7 389.55 ; + RECT 63.9 388.35 65.1 389.55 ; + RECT 63.9 377.85 65.1 379.05 ; + RECT 59.4 382.65 60.6 383.85 ; + RECT 66.9 376.05 78.9 376.95 ; + RECT 66.9 390.75 78.9 391.65 ; + RECT 75.9 388.35 76.8 391.2 ; + RECT 75.9 376.5 76.8 378.9 ; + RECT 68.85 388.35 69.75 391.2 ; + RECT 73.65 388.35 74.55 391.2 ; + RECT 68.85 376.5 69.75 379.35 ; + RECT 66.9 385.2 69.0 386.1 ; + RECT 66.9 381.9 72.0 382.8 ; + RECT 74.55 382.8 78.9 383.7 ; + RECT 68.7 379.35 69.9 380.55 ; + RECT 71.1 379.35 72.3 380.55 ; + RECT 71.1 379.35 72.3 380.55 ; + RECT 73.5 379.35 74.7 380.55 ; + RECT 68.7 388.35 69.9 389.55 ; + RECT 71.1 388.35 72.3 389.55 ; + RECT 71.1 388.35 72.3 389.55 ; + RECT 73.5 388.35 74.7 389.55 ; + RECT 75.9 388.35 77.1 389.55 ; + RECT 75.9 378.15 77.1 379.35 ; + RECT 74.1 385.2 75.0 386.1 ; + RECT 71.1 385.2 72.0 386.1 ; + RECT 74.1 378.15 75.0 385.65 ; + RECT 71.55 385.2 74.55 386.1 ; + RECT 71.1 385.65 72.0 388.35 ; + RECT 69.0 384.9 70.2 386.1 ; + RECT 72.0 381.6 73.2 382.8 ; + RECT 78.9 376.05 88.5 376.95 ; + RECT 78.9 390.75 88.5 391.65 ; + RECT 85.5 388.35 86.7 390.75 ; + RECT 85.5 376.95 86.7 378.75 ; + RECT 80.7 376.95 81.9 377.85 ; + RECT 80.7 389.55 81.9 390.75 ; + RECT 83.1 379.05 84.3 388.5 ; + RECT 78.9 382.8 81.0 383.7 ; + RECT 84.3 382.8 88.5 383.7 ; + RECT 80.7 379.05 81.9 380.25 ; + RECT 83.1 379.05 84.3 380.25 ; + RECT 80.7 388.35 81.9 389.55 ; + RECT 83.1 388.35 84.3 389.55 ; + RECT 85.5 388.35 86.7 389.55 ; + RECT 85.5 377.85 86.7 379.05 ; + RECT 81.0 382.65 82.2 383.85 ; + RECT 66.9 385.2 68.1 386.4 ; + RECT 51.0 385.2 52.2 386.4 ; + RECT 50.7 405.45 51.9 406.65 ; + RECT 57.0 405.45 58.2 406.65 ; + RECT 57.3 405.45 66.9 406.35 ; + RECT 57.3 390.75 66.9 391.65 ; + RECT 63.9 391.65 65.1 394.05 ; + RECT 63.9 403.65 65.1 405.45 ; + RECT 59.1 404.55 60.3 405.45 ; + RECT 59.1 391.65 60.3 392.85 ; + RECT 61.5 393.9 62.7 403.35 ; + RECT 57.3 398.7 59.4 399.6 ; + RECT 62.7 398.7 66.9 399.6 ; + RECT 59.1 399.75 60.3 400.95 ; + RECT 61.5 399.75 62.7 400.95 ; + RECT 59.1 398.85 60.3 400.05 ; + RECT 61.5 398.85 62.7 400.05 ; + RECT 63.9 394.05 65.1 395.25 ; + RECT 63.9 404.55 65.1 405.75 ; + RECT 59.4 399.75 60.6 400.95 ; + RECT 66.9 405.45 78.9 406.35 ; + RECT 66.9 390.75 78.9 391.65 ; + RECT 75.9 391.2 76.8 394.05 ; + RECT 75.9 403.5 76.8 405.9 ; + RECT 68.85 391.2 69.75 394.05 ; + RECT 73.65 391.2 74.55 394.05 ; + RECT 68.85 403.05 69.75 405.9 ; + RECT 66.9 396.3 69.0 397.2 ; + RECT 66.9 399.6 72.0 400.5 ; + RECT 74.55 398.7 78.9 399.6 ; + RECT 68.7 398.25 69.9 399.45 ; + RECT 71.1 398.25 72.3 399.45 ; + RECT 71.1 398.25 72.3 399.45 ; + RECT 73.5 398.25 74.7 399.45 ; + RECT 68.7 398.85 69.9 400.05 ; + RECT 71.1 398.85 72.3 400.05 ; + RECT 71.1 398.85 72.3 400.05 ; + RECT 73.5 398.85 74.7 400.05 ; + RECT 75.9 394.05 77.1 395.25 ; + RECT 75.9 404.25 77.1 405.45 ; + RECT 74.1 411.3 75.0 412.2 ; + RECT 71.1 411.3 72.0 412.2 ; + RECT 74.1 404.25 75.0 411.75 ; + RECT 71.55 411.3 74.55 412.2 ; + RECT 71.1 411.75 72.0 414.45 ; + RECT 69.0 397.5 70.2 398.7 ; + RECT 72.0 400.8 73.2 402.0 ; + RECT 78.9 405.45 88.5 406.35 ; + RECT 78.9 390.75 88.5 391.65 ; + RECT 85.5 391.65 86.7 394.05 ; + RECT 85.5 403.65 86.7 405.45 ; + RECT 80.7 404.55 81.9 405.45 ; + RECT 80.7 391.65 81.9 392.85 ; + RECT 83.1 393.9 84.3 403.35 ; + RECT 78.9 398.7 81.0 399.6 ; + RECT 84.3 398.7 88.5 399.6 ; + RECT 80.7 399.75 81.9 400.95 ; + RECT 83.1 399.75 84.3 400.95 ; + RECT 80.7 398.85 81.9 400.05 ; + RECT 83.1 398.85 84.3 400.05 ; + RECT 85.5 394.05 86.7 395.25 ; + RECT 85.5 404.55 86.7 405.75 ; + RECT 81.0 399.75 82.2 400.95 ; + RECT 66.9 396.0 68.1 397.2 ; + RECT 51.0 396.0 52.2 397.2 ; + RECT 50.7 420.15 51.9 421.35 ; + RECT 57.0 420.15 58.2 421.35 ; + RECT 57.3 405.45 66.9 406.35 ; + RECT 57.3 420.15 66.9 421.05 ; + RECT 63.9 417.75 65.1 420.15 ; + RECT 63.9 406.35 65.1 408.15 ; + RECT 59.1 406.35 60.3 407.25 ; + RECT 59.1 418.95 60.3 420.15 ; + RECT 61.5 408.45 62.7 417.9 ; + RECT 57.3 412.2 59.4 413.1 ; + RECT 62.7 412.2 66.9 413.1 ; + RECT 59.1 408.45 60.3 409.65 ; + RECT 61.5 408.45 62.7 409.65 ; + RECT 59.1 417.75 60.3 418.95 ; + RECT 61.5 417.75 62.7 418.95 ; + RECT 63.9 417.75 65.1 418.95 ; + RECT 63.9 407.25 65.1 408.45 ; + RECT 59.4 412.05 60.6 413.25 ; + RECT 66.9 405.45 78.9 406.35 ; + RECT 66.9 420.15 78.9 421.05 ; + RECT 75.9 417.75 76.8 420.6 ; + RECT 75.9 405.9 76.8 408.3 ; + RECT 68.85 417.75 69.75 420.6 ; + RECT 73.65 417.75 74.55 420.6 ; + RECT 68.85 405.9 69.75 408.75 ; + RECT 66.9 414.6 69.0 415.5 ; + RECT 66.9 411.3 72.0 412.2 ; + RECT 74.55 412.2 78.9 413.1 ; + RECT 68.7 408.75 69.9 409.95 ; + RECT 71.1 408.75 72.3 409.95 ; + RECT 71.1 408.75 72.3 409.95 ; + RECT 73.5 408.75 74.7 409.95 ; + RECT 68.7 417.75 69.9 418.95 ; + RECT 71.1 417.75 72.3 418.95 ; + RECT 71.1 417.75 72.3 418.95 ; + RECT 73.5 417.75 74.7 418.95 ; + RECT 75.9 417.75 77.1 418.95 ; + RECT 75.9 407.55 77.1 408.75 ; + RECT 74.1 414.6 75.0 415.5 ; + RECT 71.1 414.6 72.0 415.5 ; + RECT 74.1 407.55 75.0 415.05 ; + RECT 71.55 414.6 74.55 415.5 ; + RECT 71.1 415.05 72.0 417.75 ; + RECT 69.0 414.3 70.2 415.5 ; + RECT 72.0 411.0 73.2 412.2 ; + RECT 78.9 405.45 88.5 406.35 ; + RECT 78.9 420.15 88.5 421.05 ; + RECT 85.5 417.75 86.7 420.15 ; + RECT 85.5 406.35 86.7 408.15 ; + RECT 80.7 406.35 81.9 407.25 ; + RECT 80.7 418.95 81.9 420.15 ; + RECT 83.1 408.45 84.3 417.9 ; + RECT 78.9 412.2 81.0 413.1 ; + RECT 84.3 412.2 88.5 413.1 ; + RECT 80.7 408.45 81.9 409.65 ; + RECT 83.1 408.45 84.3 409.65 ; + RECT 80.7 417.75 81.9 418.95 ; + RECT 83.1 417.75 84.3 418.95 ; + RECT 85.5 417.75 86.7 418.95 ; + RECT 85.5 407.25 86.7 408.45 ; + RECT 81.0 412.05 82.2 413.25 ; + RECT 66.9 414.6 68.1 415.8 ; + RECT 51.0 414.6 52.2 415.8 ; + RECT 50.7 434.85 51.9 436.05 ; + RECT 57.0 434.85 58.2 436.05 ; + RECT 57.3 434.85 66.9 435.75 ; + RECT 57.3 420.15 66.9 421.05 ; + RECT 63.9 421.05 65.1 423.45 ; + RECT 63.9 433.05 65.1 434.85 ; + RECT 59.1 433.95 60.3 434.85 ; + RECT 59.1 421.05 60.3 422.25 ; + RECT 61.5 423.3 62.7 432.75 ; + RECT 57.3 428.1 59.4 429.0 ; + RECT 62.7 428.1 66.9 429.0 ; + RECT 59.1 429.15 60.3 430.35 ; + RECT 61.5 429.15 62.7 430.35 ; + RECT 59.1 428.25 60.3 429.45 ; + RECT 61.5 428.25 62.7 429.45 ; + RECT 63.9 423.45 65.1 424.65 ; + RECT 63.9 433.95 65.1 435.15 ; + RECT 59.4 429.15 60.6 430.35 ; + RECT 66.9 434.85 78.9 435.75 ; + RECT 66.9 420.15 78.9 421.05 ; + RECT 75.9 420.6 76.8 423.45 ; + RECT 75.9 432.9 76.8 435.3 ; + RECT 68.85 420.6 69.75 423.45 ; + RECT 73.65 420.6 74.55 423.45 ; + RECT 68.85 432.45 69.75 435.3 ; + RECT 66.9 425.7 69.0 426.6 ; + RECT 66.9 429.0 72.0 429.9 ; + RECT 74.55 428.1 78.9 429.0 ; + RECT 68.7 427.65 69.9 428.85 ; + RECT 71.1 427.65 72.3 428.85 ; + RECT 71.1 427.65 72.3 428.85 ; + RECT 73.5 427.65 74.7 428.85 ; + RECT 68.7 428.25 69.9 429.45 ; + RECT 71.1 428.25 72.3 429.45 ; + RECT 71.1 428.25 72.3 429.45 ; + RECT 73.5 428.25 74.7 429.45 ; + RECT 75.9 423.45 77.1 424.65 ; + RECT 75.9 433.65 77.1 434.85 ; + RECT 74.1 440.7 75.0 441.6 ; + RECT 71.1 440.7 72.0 441.6 ; + RECT 74.1 433.65 75.0 441.15 ; + RECT 71.55 440.7 74.55 441.6 ; + RECT 71.1 441.15 72.0 443.85 ; + RECT 69.0 426.9 70.2 428.1 ; + RECT 72.0 430.2 73.2 431.4 ; + RECT 78.9 434.85 88.5 435.75 ; + RECT 78.9 420.15 88.5 421.05 ; + RECT 85.5 421.05 86.7 423.45 ; + RECT 85.5 433.05 86.7 434.85 ; + RECT 80.7 433.95 81.9 434.85 ; + RECT 80.7 421.05 81.9 422.25 ; + RECT 83.1 423.3 84.3 432.75 ; + RECT 78.9 428.1 81.0 429.0 ; + RECT 84.3 428.1 88.5 429.0 ; + RECT 80.7 429.15 81.9 430.35 ; + RECT 83.1 429.15 84.3 430.35 ; + RECT 80.7 428.25 81.9 429.45 ; + RECT 83.1 428.25 84.3 429.45 ; + RECT 85.5 423.45 86.7 424.65 ; + RECT 85.5 433.95 86.7 435.15 ; + RECT 81.0 429.15 82.2 430.35 ; + RECT 66.9 425.4 68.1 426.6 ; + RECT 51.0 425.4 52.2 426.6 ; + RECT 17.25 39.0 18.15 79.8 ; + RECT 71.7 39.0 72.6 79.8 ; + RECT 71.7 69.0 72.6 80.4 ; + RECT 68.4 79.2 71.7 80.4 ; + RECT 69.9 71.4 70.8 78.0 ; + RECT 69.6 75.0 69.9 78.0 ; + RECT 69.6 71.4 69.9 72.6 ; + RECT 67.2 76.2 68.4 80.4 ; + RECT 67.2 69.0 68.4 72.6 ; + RECT 63.6 79.2 67.2 80.4 ; + RECT 66.3 73.8 67.2 75.0 ; + RECT 66.0 72.6 66.3 75.0 ; + RECT 65.1 71.4 66.0 78.0 ; + RECT 64.8 76.2 65.1 78.0 ; + RECT 64.8 71.4 65.1 72.6 ; + RECT 62.4 76.2 63.6 80.4 ; + RECT 48.6 79.2 62.4 80.4 ; + RECT 61.8 73.5 64.2 74.7 ; + RECT 63.6 69.0 67.2 69.9 ; + RECT 62.4 69.0 63.6 72.6 ; + RECT 61.2 69.0 62.4 70.2 ; + RECT 58.8 76.8 60.0 78.0 ; + RECT 59.7 73.5 60.9 75.9 ; + RECT 57.9 71.4 58.8 78.0 ; + RECT 57.6 76.2 57.9 78.0 ; + RECT 57.6 71.4 57.9 72.6 ; + RECT 55.5 71.4 56.4 78.0 ; + RECT 55.2 76.2 55.5 78.0 ; + RECT 55.2 71.4 55.5 73.8 ; + RECT 53.1 71.4 54.0 78.0 ; + RECT 52.8 76.2 53.1 78.0 ; + RECT 52.8 71.4 53.1 73.8 ; + RECT 51.0 73.8 51.9 75.0 ; + RECT 50.1 71.4 51.0 78.0 ; + RECT 49.8 75.0 50.1 78.0 ; + RECT 49.8 71.4 50.1 72.6 ; + RECT 47.4 76.2 48.6 80.4 ; + RECT 42.9 79.2 47.4 80.4 ; + RECT 46.5 74.1 48.9 75.3 ; + RECT 48.6 69.0 61.2 69.9 ; + RECT 47.4 69.0 48.6 72.6 ; + RECT 45.3 76.8 46.5 78.0 ; + RECT 44.4 71.4 45.3 78.0 ; + RECT 44.1 76.2 44.4 78.0 ; + RECT 44.1 71.4 44.4 72.6 ; + RECT 42.9 69.0 47.4 69.9 ; + RECT 41.7 76.2 42.9 80.4 ; + RECT 38.1 79.2 41.7 80.4 ; + RECT 40.8 73.5 42.0 74.7 ; + RECT 41.7 69.0 42.9 72.6 ; + RECT 40.5 72.6 40.8 78.0 ; + RECT 39.9 71.4 40.5 78.0 ; + RECT 39.3 76.2 39.9 78.0 ; + RECT 39.6 71.4 39.9 73.8 ; + RECT 39.3 71.4 39.6 72.6 ; + RECT 36.9 76.2 38.1 80.4 ; + RECT 21.9 79.2 36.9 80.4 ; + RECT 36.3 73.5 38.7 74.7 ; + RECT 38.1 69.0 41.7 69.9 ; + RECT 36.9 69.0 38.1 72.6 ; + RECT 35.7 69.0 36.9 70.2 ; + RECT 32.4 76.8 33.6 78.0 ; + RECT 33.3 73.5 34.5 75.9 ; + RECT 31.5 71.4 32.4 78.0 ; + RECT 31.2 76.2 31.5 78.0 ; + RECT 31.2 71.4 31.5 72.6 ; + RECT 29.1 71.4 30.0 78.0 ; + RECT 28.8 76.2 29.1 78.0 ; + RECT 28.8 71.4 29.1 73.8 ; + RECT 26.7 71.4 27.6 78.0 ; + RECT 26.4 76.2 26.7 78.0 ; + RECT 26.4 71.4 26.7 73.8 ; + RECT 24.6 73.8 25.5 75.0 ; + RECT 23.7 71.4 24.6 78.0 ; + RECT 23.4 75.0 23.7 78.0 ; + RECT 23.4 71.4 23.7 72.6 ; + RECT 22.2 69.0 35.7 69.9 ; + RECT 21.9 76.2 22.2 78.0 ; + RECT 21.0 76.2 21.9 80.4 ; + RECT 21.3 69.0 22.2 72.6 ; + RECT 21.0 71.4 21.3 72.6 ; + RECT 19.5 76.2 21.0 77.4 ; + RECT 18.6 74.1 19.5 75.3 ; + RECT 17.7 69.0 18.6 80.4 ; + RECT 71.7 58.8 72.6 70.2 ; + RECT 68.4 58.8 71.7 60.0 ; + RECT 69.9 61.2 70.8 67.8 ; + RECT 69.6 61.2 69.9 64.2 ; + RECT 69.6 66.6 69.9 67.8 ; + RECT 67.2 58.8 68.4 63.0 ; + RECT 67.2 66.6 68.4 70.2 ; + RECT 63.6 58.8 67.2 60.0 ; + RECT 66.3 64.2 67.2 65.4 ; + RECT 66.0 64.2 66.3 66.6 ; + RECT 65.1 61.2 66.0 67.8 ; + RECT 64.8 61.2 65.1 63.0 ; + RECT 64.8 66.6 65.1 67.8 ; + RECT 62.4 58.8 63.6 63.0 ; + RECT 48.6 58.8 62.4 60.0 ; + RECT 61.8 64.5 64.2 65.7 ; + RECT 63.6 69.3 67.2 70.2 ; + RECT 62.4 66.6 63.6 70.2 ; + RECT 61.2 69.0 62.4 70.2 ; + RECT 58.8 61.2 60.0 62.4 ; + RECT 59.7 63.3 60.9 65.7 ; + RECT 57.9 61.2 58.8 67.8 ; + RECT 57.6 61.2 57.9 63.0 ; + RECT 57.6 66.6 57.9 67.8 ; + RECT 55.5 61.2 56.4 67.8 ; + RECT 55.2 61.2 55.5 63.0 ; + RECT 55.2 65.4 55.5 67.8 ; + RECT 53.1 61.2 54.0 67.8 ; + RECT 52.8 61.2 53.1 63.0 ; + RECT 52.8 65.4 53.1 67.8 ; + RECT 51.0 64.2 51.9 65.4 ; + RECT 50.1 61.2 51.0 67.8 ; + RECT 49.8 61.2 50.1 64.2 ; + RECT 49.8 66.6 50.1 67.8 ; + RECT 47.4 58.8 48.6 63.0 ; + RECT 42.9 58.8 47.4 60.0 ; + RECT 46.5 63.9 48.9 65.1 ; + RECT 48.6 69.3 61.2 70.2 ; + RECT 47.4 66.6 48.6 70.2 ; + RECT 45.3 61.2 46.5 62.4 ; + RECT 44.4 61.2 45.3 67.8 ; + RECT 44.1 61.2 44.4 63.0 ; + RECT 44.1 66.6 44.4 67.8 ; + RECT 42.9 69.3 47.4 70.2 ; + RECT 41.7 58.8 42.9 63.0 ; + RECT 38.1 58.8 41.7 60.0 ; + RECT 40.8 64.5 42.0 65.7 ; + RECT 41.7 66.6 42.9 70.2 ; + RECT 40.5 61.2 40.8 66.6 ; + RECT 39.9 61.2 40.5 67.8 ; + RECT 39.3 61.2 39.9 63.0 ; + RECT 39.6 65.4 39.9 67.8 ; + RECT 39.3 66.6 39.6 67.8 ; + RECT 36.9 58.8 38.1 63.0 ; + RECT 21.9 58.8 36.9 60.0 ; + RECT 36.3 64.5 38.7 65.7 ; + RECT 38.1 69.3 41.7 70.2 ; + RECT 36.9 66.6 38.1 70.2 ; + RECT 35.7 69.0 36.9 70.2 ; + RECT 32.4 61.2 33.6 62.4 ; + RECT 33.3 63.3 34.5 65.7 ; + RECT 31.5 61.2 32.4 67.8 ; + RECT 31.2 61.2 31.5 63.0 ; + RECT 31.2 66.6 31.5 67.8 ; + RECT 29.1 61.2 30.0 67.8 ; + RECT 28.8 61.2 29.1 63.0 ; + RECT 28.8 65.4 29.1 67.8 ; + RECT 26.7 61.2 27.6 67.8 ; + RECT 26.4 61.2 26.7 63.0 ; + RECT 26.4 65.4 26.7 67.8 ; + RECT 24.6 64.2 25.5 65.4 ; + RECT 23.7 61.2 24.6 67.8 ; + RECT 23.4 61.2 23.7 64.2 ; + RECT 23.4 66.6 23.7 67.8 ; + RECT 22.2 69.3 35.7 70.2 ; + RECT 21.9 61.2 22.2 63.0 ; + RECT 21.0 58.8 21.9 63.0 ; + RECT 21.3 66.6 22.2 70.2 ; + RECT 21.0 66.6 21.3 67.8 ; + RECT 19.5 61.8 21.0 63.0 ; + RECT 18.6 63.9 19.5 65.1 ; + RECT 17.7 58.8 18.6 70.2 ; + RECT 71.7 48.6 72.6 60.0 ; + RECT 68.4 58.8 71.7 60.0 ; + RECT 69.9 51.0 70.8 57.6 ; + RECT 69.6 54.6 69.9 57.6 ; + RECT 69.6 51.0 69.9 52.2 ; + RECT 67.2 55.8 68.4 60.0 ; + RECT 67.2 48.6 68.4 52.2 ; + RECT 63.6 58.8 67.2 60.0 ; + RECT 66.3 53.4 67.2 54.6 ; + RECT 66.0 52.2 66.3 54.6 ; + RECT 65.1 51.0 66.0 57.6 ; + RECT 64.8 55.8 65.1 57.6 ; + RECT 64.8 51.0 65.1 52.2 ; + RECT 62.4 55.8 63.6 60.0 ; + RECT 48.6 58.8 62.4 60.0 ; + RECT 61.8 53.1 64.2 54.3 ; + RECT 63.6 48.6 67.2 49.5 ; + RECT 62.4 48.6 63.6 52.2 ; + RECT 61.2 48.6 62.4 49.8 ; + RECT 58.8 56.4 60.0 57.6 ; + RECT 59.7 53.1 60.9 55.5 ; + RECT 57.9 51.0 58.8 57.6 ; + RECT 57.6 55.8 57.9 57.6 ; + RECT 57.6 51.0 57.9 52.2 ; + RECT 55.5 51.0 56.4 57.6 ; + RECT 55.2 55.8 55.5 57.6 ; + RECT 55.2 51.0 55.5 53.4 ; + RECT 53.1 51.0 54.0 57.6 ; + RECT 52.8 55.8 53.1 57.6 ; + RECT 52.8 51.0 53.1 53.4 ; + RECT 51.0 53.4 51.9 54.6 ; + RECT 50.1 51.0 51.0 57.6 ; + RECT 49.8 54.6 50.1 57.6 ; + RECT 49.8 51.0 50.1 52.2 ; + RECT 47.4 55.8 48.6 60.0 ; + RECT 42.9 58.8 47.4 60.0 ; + RECT 46.5 53.7 48.9 54.9 ; + RECT 48.6 48.6 61.2 49.5 ; + RECT 47.4 48.6 48.6 52.2 ; + RECT 45.3 56.4 46.5 57.6 ; + RECT 44.4 51.0 45.3 57.6 ; + RECT 44.1 55.8 44.4 57.6 ; + RECT 44.1 51.0 44.4 52.2 ; + RECT 42.9 48.6 47.4 49.5 ; + RECT 41.7 55.8 42.9 60.0 ; + RECT 38.1 58.8 41.7 60.0 ; + RECT 40.8 53.1 42.0 54.3 ; + RECT 41.7 48.6 42.9 52.2 ; + RECT 40.5 52.2 40.8 57.6 ; + RECT 39.9 51.0 40.5 57.6 ; + RECT 39.3 55.8 39.9 57.6 ; + RECT 39.6 51.0 39.9 53.4 ; + RECT 39.3 51.0 39.6 52.2 ; + RECT 36.9 55.8 38.1 60.0 ; + RECT 21.9 58.8 36.9 60.0 ; + RECT 36.3 53.1 38.7 54.3 ; + RECT 38.1 48.6 41.7 49.5 ; + RECT 36.9 48.6 38.1 52.2 ; + RECT 35.7 48.6 36.9 49.8 ; + RECT 32.4 56.4 33.6 57.6 ; + RECT 33.3 53.1 34.5 55.5 ; + RECT 31.5 51.0 32.4 57.6 ; + RECT 31.2 55.8 31.5 57.6 ; + RECT 31.2 51.0 31.5 52.2 ; + RECT 29.1 51.0 30.0 57.6 ; + RECT 28.8 55.8 29.1 57.6 ; + RECT 28.8 51.0 29.1 53.4 ; + RECT 26.7 51.0 27.6 57.6 ; + RECT 26.4 55.8 26.7 57.6 ; + RECT 26.4 51.0 26.7 53.4 ; + RECT 24.6 53.4 25.5 54.6 ; + RECT 23.7 51.0 24.6 57.6 ; + RECT 23.4 54.6 23.7 57.6 ; + RECT 23.4 51.0 23.7 52.2 ; + RECT 22.2 48.6 35.7 49.5 ; + RECT 21.9 55.8 22.2 57.6 ; + RECT 21.0 55.8 21.9 60.0 ; + RECT 21.3 48.6 22.2 52.2 ; + RECT 21.0 51.0 21.3 52.2 ; + RECT 19.5 55.8 21.0 57.0 ; + RECT 18.6 53.7 19.5 54.9 ; + RECT 17.7 48.6 18.6 60.0 ; + RECT 71.7 38.4 72.6 49.8 ; + RECT 68.4 38.4 71.7 39.6 ; + RECT 69.9 40.8 70.8 47.4 ; + RECT 69.6 40.8 69.9 43.8 ; + RECT 69.6 46.2 69.9 47.4 ; + RECT 67.2 38.4 68.4 42.6 ; + RECT 67.2 46.2 68.4 49.8 ; + RECT 63.6 38.4 67.2 39.6 ; + RECT 66.3 43.8 67.2 45.0 ; + RECT 66.0 43.8 66.3 46.2 ; + RECT 65.1 40.8 66.0 47.4 ; + RECT 64.8 40.8 65.1 42.6 ; + RECT 64.8 46.2 65.1 47.4 ; + RECT 62.4 38.4 63.6 42.6 ; + RECT 48.6 38.4 62.4 39.6 ; + RECT 61.8 44.1 64.2 45.3 ; + RECT 63.6 48.9 67.2 49.8 ; + RECT 62.4 46.2 63.6 49.8 ; + RECT 61.2 48.6 62.4 49.8 ; + RECT 58.8 40.8 60.0 42.0 ; + RECT 59.7 42.9 60.9 45.3 ; + RECT 57.9 40.8 58.8 47.4 ; + RECT 57.6 40.8 57.9 42.6 ; + RECT 57.6 46.2 57.9 47.4 ; + RECT 55.5 40.8 56.4 47.4 ; + RECT 55.2 40.8 55.5 42.6 ; + RECT 55.2 45.0 55.5 47.4 ; + RECT 53.1 40.8 54.0 47.4 ; + RECT 52.8 40.8 53.1 42.6 ; + RECT 52.8 45.0 53.1 47.4 ; + RECT 51.0 43.8 51.9 45.0 ; + RECT 50.1 40.8 51.0 47.4 ; + RECT 49.8 40.8 50.1 43.8 ; + RECT 49.8 46.2 50.1 47.4 ; + RECT 47.4 38.4 48.6 42.6 ; + RECT 42.9 38.4 47.4 39.6 ; + RECT 46.5 43.5 48.9 44.7 ; + RECT 48.6 48.9 61.2 49.8 ; + RECT 47.4 46.2 48.6 49.8 ; + RECT 45.3 40.8 46.5 42.0 ; + RECT 44.4 40.8 45.3 47.4 ; + RECT 44.1 40.8 44.4 42.6 ; + RECT 44.1 46.2 44.4 47.4 ; + RECT 42.9 48.9 47.4 49.8 ; + RECT 41.7 38.4 42.9 42.6 ; + RECT 38.1 38.4 41.7 39.6 ; + RECT 40.8 44.1 42.0 45.3 ; + RECT 41.7 46.2 42.9 49.8 ; + RECT 40.5 40.8 40.8 46.2 ; + RECT 39.9 40.8 40.5 47.4 ; + RECT 39.3 40.8 39.9 42.6 ; + RECT 39.6 45.0 39.9 47.4 ; + RECT 39.3 46.2 39.6 47.4 ; + RECT 36.9 38.4 38.1 42.6 ; + RECT 21.9 38.4 36.9 39.6 ; + RECT 36.3 44.1 38.7 45.3 ; + RECT 38.1 48.9 41.7 49.8 ; + RECT 36.9 46.2 38.1 49.8 ; + RECT 35.7 48.6 36.9 49.8 ; + RECT 32.4 40.8 33.6 42.0 ; + RECT 33.3 42.9 34.5 45.3 ; + RECT 31.5 40.8 32.4 47.4 ; + RECT 31.2 40.8 31.5 42.6 ; + RECT 31.2 46.2 31.5 47.4 ; + RECT 29.1 40.8 30.0 47.4 ; + RECT 28.8 40.8 29.1 42.6 ; + RECT 28.8 45.0 29.1 47.4 ; + RECT 26.7 40.8 27.6 47.4 ; + RECT 26.4 40.8 26.7 42.6 ; + RECT 26.4 45.0 26.7 47.4 ; + RECT 24.6 43.8 25.5 45.0 ; + RECT 23.7 40.8 24.6 47.4 ; + RECT 23.4 40.8 23.7 43.8 ; + RECT 23.4 46.2 23.7 47.4 ; + RECT 22.2 48.9 35.7 49.8 ; + RECT 21.9 40.8 22.2 42.6 ; + RECT 21.0 38.4 21.9 42.6 ; + RECT 21.3 46.2 22.2 49.8 ; + RECT 21.0 46.2 21.3 47.4 ; + RECT 19.5 41.4 21.0 42.6 ; + RECT 18.6 43.5 19.5 44.7 ; + RECT 17.7 38.4 18.6 49.8 ; + RECT 88.5 202.35 89.4 203.25 ; + RECT 88.95 202.35 124.35 203.25 ; + RECT 88.5 202.8 89.4 208.2 ; + RECT 88.5 226.35 89.4 227.25 ; + RECT 88.95 226.35 124.35 227.25 ; + RECT 88.5 221.4 89.4 226.8 ; + RECT 88.5 231.75 89.4 232.65 ; + RECT 88.95 231.75 124.35 232.65 ; + RECT 88.5 232.2 89.4 237.6 ; + RECT 88.5 255.75 89.4 256.65 ; + RECT 88.95 255.75 124.35 256.65 ; + RECT 88.5 250.8 89.4 256.2 ; + RECT 88.5 261.15 89.4 262.05 ; + RECT 88.95 261.15 124.35 262.05 ; + RECT 88.5 261.6 89.4 267.0 ; + RECT 88.5 285.15 89.4 286.05 ; + RECT 88.95 285.15 124.35 286.05 ; + RECT 88.5 280.2 89.4 285.6 ; + RECT 88.5 290.55 89.4 291.45 ; + RECT 88.95 290.55 124.35 291.45 ; + RECT 88.5 291.0 89.4 296.4 ; + RECT 88.5 314.55 89.4 315.45 ; + RECT 88.95 314.55 124.35 315.45 ; + RECT 88.5 309.6 89.4 315.0 ; + RECT 88.5 319.95 89.4 320.85 ; + RECT 88.95 319.95 124.35 320.85 ; + RECT 88.5 320.4 89.4 325.8 ; + RECT 88.5 343.95 89.4 344.85 ; + RECT 88.95 343.95 124.35 344.85 ; + RECT 88.5 339.0 89.4 344.4 ; + RECT 88.5 349.35 89.4 350.25 ; + RECT 88.95 349.35 124.35 350.25 ; + RECT 88.5 349.8 89.4 355.2 ; + RECT 88.5 373.35 89.4 374.25 ; + RECT 88.95 373.35 124.35 374.25 ; + RECT 88.5 368.4 89.4 373.8 ; + RECT 88.5 378.75 89.4 379.65 ; + RECT 88.95 378.75 124.35 379.65 ; + RECT 88.5 379.2 89.4 384.6 ; + RECT 88.5 402.75 89.4 403.65 ; + RECT 88.95 402.75 124.35 403.65 ; + RECT 88.5 397.8 89.4 403.2 ; + RECT 88.5 408.15 89.4 409.05 ; + RECT 88.95 408.15 124.35 409.05 ; + RECT 88.5 408.6 89.4 414.0 ; + RECT 88.5 432.15 89.4 433.05 ; + RECT 88.95 432.15 124.35 433.05 ; + RECT 88.5 427.2 89.4 432.6 ; + RECT 90.6 88.8 91.8 90.0 ; + RECT 87.9 104.7 89.1 105.9 ; + RECT 85.2 147.6 86.4 148.8 ; + RECT 82.5 163.5 83.7 164.7 ; + RECT 114.0 33.3 115.2 34.5 ; + RECT 108.6 28.65 109.8 29.85 ; + RECT 111.3 26.25 112.5 27.45 ; + RECT 114.0 446.4 115.2 447.6 ; + RECT 116.7 97.95 117.9 99.15 ; + RECT 119.4 196.05 120.6 197.25 ; + RECT 16.5 76.5 17.7 77.7 ; + RECT 53.7 456.6 54.6 457.5 ; + RECT 53.7 435.3 54.6 457.05 ; + RECT 54.15 456.6 107.25 457.5 ; + RECT 105.9 456.6 107.1 457.8 ; + RECT 71.7 35.85 72.6 36.75 ; + RECT 0.0 35.85 72.15 36.75 ; + RECT 71.7 36.3 72.6 39.0 ; + RECT 95.1 437.1 97.5 438.3 ; + RECT 133.8 437.1 135.0 438.3 ; + RECT 144.0 437.1 145.2 438.3 ; + RECT 122.7 437.1 123.9 438.3 ; + RECT 101.7 8.1 104.1 9.3 ; + RECT 133.5 8.1 134.7 9.3 ; + RECT 133.5 8.1 134.7 9.3 ; + RECT 87.75 229.05 88.95 230.25 ; + RECT 87.75 258.45 88.95 259.65 ; + RECT 87.75 287.85 88.95 289.05 ; + RECT 87.75 317.25 88.95 318.45 ; + RECT 87.75 346.65 88.95 347.85 ; + RECT 87.75 376.05 88.95 377.25 ; + RECT 87.75 405.45 88.95 406.65 ; + RECT 87.75 434.85 88.95 436.05 ; + RECT 95.1 140.55 97.5 141.75 ; + RECT 95.1 199.35 97.5 200.55 ; + RECT 75.0 69.15 76.2 70.35 ; + RECT 95.1 69.15 97.5 70.35 ; + RECT 75.0 69.15 76.2 70.35 ; + RECT 95.1 69.15 97.5 70.35 ; + RECT 75.0 48.75 76.2 49.95 ; + RECT 95.1 48.75 97.5 49.95 ; + RECT 75.0 48.75 76.2 49.95 ; + RECT 95.1 48.75 97.5 49.95 ; + RECT -30.6 180.0 -28.95 180.9 ; + RECT -44.85 180.0 -43.2 180.9 ; + RECT -40.5 145.5 -39.6 151.8 ; + RECT -43.95 145.5 -43.05 154.5 ; + RECT -64.35 145.5 -63.45 157.2 ; + RECT -48.75 145.5 -47.85 159.9 ; + RECT -17.1 153.6 -16.2 164.4 ; + RECT -13.8 161.7 -12.9 164.4 ; + RECT -27.15 161.7 -26.25 164.4 ; + RECT -32.7 153.6 -31.8 164.4 ; + RECT -34.5 156.3 -33.6 164.4 ; + RECT -47.55 161.7 -46.65 164.4 ; + RECT -42.0 159.0 -41.1 164.4 ; + RECT -40.2 156.3 -39.3 164.4 ; + RECT -30.6 189.6 -29.7 215.1 ; + RECT -44.1 208.8 -43.2 212.4 ; + RECT -14.7 199.2 -13.8 217.8 ; + RECT -7.95 264.75 -3.0 265.65 ; + RECT -22.65 88.5 -21.75 223.5 ; + RECT -52.05 180.0 -51.15 220.5 ; + RECT -66.3 141.9 -21.75 142.8 ; + RECT -7.95 88.5 -7.05 233.4 ; + RECT -37.35 180.0 -36.45 230.4 ; + RECT -61.2 87.9 -27.15 88.8 ; + RECT -66.3 87.45 -35.7 88.35 ; + RECT -66.3 141.9 -35.7 142.8 ; + RECT -66.9 141.9 -55.5 142.8 ; + RECT -66.9 138.6 -65.7 141.9 ; + RECT -64.5 140.1 -57.9 141.0 ; + RECT -64.5 139.8 -61.5 140.1 ; + RECT -59.1 139.8 -57.9 140.1 ; + RECT -66.9 137.4 -62.7 138.6 ; + RECT -59.1 137.4 -55.5 138.6 ; + RECT -66.9 133.8 -65.7 137.4 ; + RECT -61.5 136.5 -60.3 137.4 ; + RECT -61.5 136.2 -59.1 136.5 ; + RECT -64.5 135.3 -57.9 136.2 ; + RECT -64.5 135.0 -62.7 135.3 ; + RECT -59.1 135.0 -57.9 135.3 ; + RECT -66.9 132.6 -62.7 133.8 ; + RECT -66.9 118.8 -65.7 132.6 ; + RECT -61.2 132.0 -60.0 134.4 ; + RECT -56.4 133.8 -55.5 137.4 ; + RECT -59.1 132.6 -55.5 133.8 ; + RECT -56.7 131.4 -55.5 132.6 ; + RECT -64.5 129.0 -63.3 130.2 ; + RECT -62.4 129.9 -60.0 131.1 ; + RECT -64.5 128.1 -57.9 129.0 ; + RECT -64.5 127.8 -62.7 128.1 ; + RECT -59.1 127.8 -57.9 128.1 ; + RECT -64.5 125.7 -57.9 126.6 ; + RECT -64.5 125.4 -62.7 125.7 ; + RECT -60.3 125.4 -57.9 125.7 ; + RECT -64.5 123.3 -57.9 124.2 ; + RECT -64.5 123.0 -62.7 123.3 ; + RECT -60.3 123.0 -57.9 123.3 ; + RECT -61.5 121.2 -60.3 122.1 ; + RECT -64.5 120.3 -57.9 121.2 ; + RECT -64.5 120.0 -61.5 120.3 ; + RECT -59.1 120.0 -57.9 120.3 ; + RECT -66.9 117.6 -62.7 118.8 ; + RECT -66.9 113.1 -65.7 117.6 ; + RECT -61.8 116.7 -60.6 119.1 ; + RECT -56.4 118.8 -55.5 131.4 ; + RECT -59.1 117.6 -55.5 118.8 ; + RECT -64.5 115.5 -63.3 116.7 ; + RECT -64.5 114.6 -57.9 115.5 ; + RECT -64.5 114.3 -62.7 114.6 ; + RECT -59.1 114.3 -57.9 114.6 ; + RECT -56.4 113.1 -55.5 117.6 ; + RECT -66.9 111.9 -62.7 113.1 ; + RECT -66.9 108.3 -65.7 111.9 ; + RECT -61.2 111.0 -60.0 112.2 ; + RECT -59.1 111.9 -55.5 113.1 ; + RECT -64.5 110.7 -59.1 111.0 ; + RECT -64.5 110.1 -57.9 110.7 ; + RECT -64.5 109.5 -62.7 110.1 ; + RECT -60.3 109.8 -57.9 110.1 ; + RECT -59.1 109.5 -57.9 109.8 ; + RECT -66.9 107.1 -62.7 108.3 ; + RECT -66.9 92.1 -65.7 107.1 ; + RECT -61.2 106.5 -60.0 108.9 ; + RECT -56.4 108.3 -55.5 111.9 ; + RECT -59.1 107.1 -55.5 108.3 ; + RECT -56.7 105.9 -55.5 107.1 ; + RECT -64.5 102.6 -63.3 103.8 ; + RECT -62.4 103.5 -60.0 104.7 ; + RECT -64.5 101.7 -57.9 102.6 ; + RECT -64.5 101.4 -62.7 101.7 ; + RECT -59.1 101.4 -57.9 101.7 ; + RECT -64.5 99.3 -57.9 100.2 ; + RECT -64.5 99.0 -62.7 99.3 ; + RECT -60.3 99.0 -57.9 99.3 ; + RECT -64.5 96.9 -57.9 97.8 ; + RECT -64.5 96.6 -62.7 96.9 ; + RECT -60.3 96.6 -57.9 96.9 ; + RECT -61.5 94.8 -60.3 95.7 ; + RECT -64.5 93.9 -57.9 94.8 ; + RECT -64.5 93.6 -61.5 93.9 ; + RECT -59.1 93.6 -57.9 93.9 ; + RECT -56.4 92.4 -55.5 105.9 ; + RECT -64.5 92.1 -62.7 92.4 ; + RECT -66.9 91.2 -62.7 92.1 ; + RECT -59.1 91.5 -55.5 92.4 ; + RECT -59.1 91.2 -57.9 91.5 ; + RECT -63.9 89.7 -62.7 91.2 ; + RECT -61.8 88.8 -60.6 89.7 ; + RECT -66.9 87.9 -55.5 88.8 ; + RECT -56.7 141.9 -45.3 142.8 ; + RECT -46.5 138.6 -45.3 141.9 ; + RECT -54.3 140.1 -47.7 141.0 ; + RECT -50.7 139.8 -47.7 140.1 ; + RECT -54.3 139.8 -53.1 140.1 ; + RECT -49.5 137.4 -45.3 138.6 ; + RECT -56.7 137.4 -53.1 138.6 ; + RECT -46.5 133.8 -45.3 137.4 ; + RECT -51.9 136.5 -50.7 137.4 ; + RECT -53.1 136.2 -50.7 136.5 ; + RECT -54.3 135.3 -47.7 136.2 ; + RECT -49.5 135.0 -47.7 135.3 ; + RECT -54.3 135.0 -53.1 135.3 ; + RECT -49.5 132.6 -45.3 133.8 ; + RECT -46.5 118.8 -45.3 132.6 ; + RECT -52.2 132.0 -51.0 134.4 ; + RECT -56.7 133.8 -55.8 137.4 ; + RECT -56.7 132.6 -53.1 133.8 ; + RECT -56.7 131.4 -55.5 132.6 ; + RECT -48.9 129.0 -47.7 130.2 ; + RECT -52.2 129.9 -49.8 131.1 ; + RECT -54.3 128.1 -47.7 129.0 ; + RECT -49.5 127.8 -47.7 128.1 ; + RECT -54.3 127.8 -53.1 128.1 ; + RECT -54.3 125.7 -47.7 126.6 ; + RECT -49.5 125.4 -47.7 125.7 ; + RECT -54.3 125.4 -51.9 125.7 ; + RECT -54.3 123.3 -47.7 124.2 ; + RECT -49.5 123.0 -47.7 123.3 ; + RECT -54.3 123.0 -51.9 123.3 ; + RECT -51.9 121.2 -50.7 122.1 ; + RECT -54.3 120.3 -47.7 121.2 ; + RECT -50.7 120.0 -47.7 120.3 ; + RECT -54.3 120.0 -53.1 120.3 ; + RECT -49.5 117.6 -45.3 118.8 ; + RECT -46.5 113.1 -45.3 117.6 ; + RECT -51.6 116.7 -50.4 119.1 ; + RECT -56.7 118.8 -55.8 131.4 ; + RECT -56.7 117.6 -53.1 118.8 ; + RECT -48.9 115.5 -47.7 116.7 ; + RECT -54.3 114.6 -47.7 115.5 ; + RECT -49.5 114.3 -47.7 114.6 ; + RECT -54.3 114.3 -53.1 114.6 ; + RECT -56.7 113.1 -55.8 117.6 ; + RECT -49.5 111.9 -45.3 113.1 ; + RECT -46.5 108.3 -45.3 111.9 ; + RECT -52.2 111.0 -51.0 112.2 ; + RECT -56.7 111.9 -53.1 113.1 ; + RECT -53.1 110.7 -47.7 111.0 ; + RECT -54.3 110.1 -47.7 110.7 ; + RECT -49.5 109.5 -47.7 110.1 ; + RECT -54.3 109.8 -51.9 110.1 ; + RECT -54.3 109.5 -53.1 109.8 ; + RECT -49.5 107.1 -45.3 108.3 ; + RECT -46.5 92.1 -45.3 107.1 ; + RECT -52.2 106.5 -51.0 108.9 ; + RECT -56.7 108.3 -55.8 111.9 ; + RECT -56.7 107.1 -53.1 108.3 ; + RECT -56.7 105.9 -55.5 107.1 ; + RECT -48.9 102.6 -47.7 103.8 ; + RECT -52.2 103.5 -49.8 104.7 ; + RECT -54.3 101.7 -47.7 102.6 ; + RECT -49.5 101.4 -47.7 101.7 ; + RECT -54.3 101.4 -53.1 101.7 ; + RECT -54.3 99.3 -47.7 100.2 ; + RECT -49.5 99.0 -47.7 99.3 ; + RECT -54.3 99.0 -51.9 99.3 ; + RECT -54.3 96.9 -47.7 97.8 ; + RECT -49.5 96.6 -47.7 96.9 ; + RECT -54.3 96.6 -51.9 96.9 ; + RECT -51.9 94.8 -50.7 95.7 ; + RECT -54.3 93.9 -47.7 94.8 ; + RECT -50.7 93.6 -47.7 93.9 ; + RECT -54.3 93.6 -53.1 93.9 ; + RECT -56.7 92.4 -55.8 105.9 ; + RECT -49.5 92.1 -47.7 92.4 ; + RECT -49.5 91.2 -45.3 92.1 ; + RECT -56.7 91.5 -53.1 92.4 ; + RECT -54.3 91.2 -53.1 91.5 ; + RECT -49.5 89.7 -48.3 91.2 ; + RECT -51.6 88.8 -50.4 89.7 ; + RECT -56.7 87.9 -45.3 88.8 ; + RECT -46.5 141.9 -35.1 142.8 ; + RECT -46.5 138.6 -45.3 141.9 ; + RECT -44.1 140.1 -37.5 141.0 ; + RECT -44.1 139.8 -41.1 140.1 ; + RECT -38.7 139.8 -37.5 140.1 ; + RECT -46.5 137.4 -42.3 138.6 ; + RECT -38.7 137.4 -35.1 138.6 ; + RECT -46.5 133.8 -45.3 137.4 ; + RECT -41.1 136.5 -39.9 137.4 ; + RECT -41.1 136.2 -38.7 136.5 ; + RECT -44.1 135.3 -37.5 136.2 ; + RECT -44.1 135.0 -42.3 135.3 ; + RECT -38.7 135.0 -37.5 135.3 ; + RECT -46.5 132.6 -42.3 133.8 ; + RECT -46.5 118.8 -45.3 132.6 ; + RECT -40.8 132.0 -39.6 134.4 ; + RECT -36.0 133.8 -35.1 137.4 ; + RECT -38.7 132.6 -35.1 133.8 ; + RECT -36.3 131.4 -35.1 132.6 ; + RECT -44.1 129.0 -42.9 130.2 ; + RECT -42.0 129.9 -39.6 131.1 ; + RECT -44.1 128.1 -37.5 129.0 ; + RECT -44.1 127.8 -42.3 128.1 ; + RECT -38.7 127.8 -37.5 128.1 ; + RECT -44.1 125.7 -37.5 126.6 ; + RECT -44.1 125.4 -42.3 125.7 ; + RECT -39.9 125.4 -37.5 125.7 ; + RECT -44.1 123.3 -37.5 124.2 ; + RECT -44.1 123.0 -42.3 123.3 ; + RECT -39.9 123.0 -37.5 123.3 ; + RECT -41.1 121.2 -39.9 122.1 ; + RECT -44.1 120.3 -37.5 121.2 ; + RECT -44.1 120.0 -41.1 120.3 ; + RECT -38.7 120.0 -37.5 120.3 ; + RECT -46.5 117.6 -42.3 118.8 ; + RECT -46.5 113.1 -45.3 117.6 ; + RECT -41.4 116.7 -40.2 119.1 ; + RECT -36.0 118.8 -35.1 131.4 ; + RECT -38.7 117.6 -35.1 118.8 ; + RECT -44.1 115.5 -42.9 116.7 ; + RECT -44.1 114.6 -37.5 115.5 ; + RECT -44.1 114.3 -42.3 114.6 ; + RECT -38.7 114.3 -37.5 114.6 ; + RECT -36.0 113.1 -35.1 117.6 ; + RECT -46.5 111.9 -42.3 113.1 ; + RECT -46.5 108.3 -45.3 111.9 ; + RECT -40.8 111.0 -39.6 112.2 ; + RECT -38.7 111.9 -35.1 113.1 ; + RECT -44.1 110.7 -38.7 111.0 ; + RECT -44.1 110.1 -37.5 110.7 ; + RECT -44.1 109.5 -42.3 110.1 ; + RECT -39.9 109.8 -37.5 110.1 ; + RECT -38.7 109.5 -37.5 109.8 ; + RECT -46.5 107.1 -42.3 108.3 ; + RECT -46.5 92.1 -45.3 107.1 ; + RECT -40.8 106.5 -39.6 108.9 ; + RECT -36.0 108.3 -35.1 111.9 ; + RECT -38.7 107.1 -35.1 108.3 ; + RECT -36.3 105.9 -35.1 107.1 ; + RECT -44.1 102.6 -42.9 103.8 ; + RECT -42.0 103.5 -39.6 104.7 ; + RECT -44.1 101.7 -37.5 102.6 ; + RECT -44.1 101.4 -42.3 101.7 ; + RECT -38.7 101.4 -37.5 101.7 ; + RECT -44.1 99.3 -37.5 100.2 ; + RECT -44.1 99.0 -42.3 99.3 ; + RECT -39.9 99.0 -37.5 99.3 ; + RECT -44.1 96.9 -37.5 97.8 ; + RECT -44.1 96.6 -42.3 96.9 ; + RECT -39.9 96.6 -37.5 96.9 ; + RECT -41.1 94.8 -39.9 95.7 ; + RECT -44.1 93.9 -37.5 94.8 ; + RECT -44.1 93.6 -41.1 93.9 ; + RECT -38.7 93.6 -37.5 93.9 ; + RECT -36.0 92.4 -35.1 105.9 ; + RECT -44.1 92.1 -42.3 92.4 ; + RECT -46.5 91.2 -42.3 92.1 ; + RECT -38.7 91.5 -35.1 92.4 ; + RECT -38.7 91.2 -37.5 91.5 ; + RECT -43.5 89.7 -42.3 91.2 ; + RECT -41.4 88.8 -40.2 89.7 ; + RECT -46.5 87.9 -35.1 88.8 ; + RECT -7.95 128.7 -7.05 145.5 ; + RECT -22.65 128.7 -21.75 145.5 ; + RECT -21.75 142.5 -19.35 143.7 ; + RECT -9.75 142.5 -7.95 143.7 ; + RECT -19.5 132.9 -10.05 134.1 ; + RECT -19.5 137.7 -10.05 138.9 ; + RECT -14.7 128.7 -13.8 130.8 ; + RECT -14.7 138.9 -13.8 145.5 ; + RECT -11.25 130.5 -10.05 131.7 ; + RECT -11.25 132.9 -10.05 134.1 ; + RECT -11.25 135.3 -10.05 136.5 ; + RECT -11.25 137.7 -10.05 138.9 ; + RECT -11.25 140.1 -10.05 141.3 ; + RECT -13.05 130.5 -12.15 131.4 ; + RECT -13.05 135.3 -12.15 136.2 ; + RECT -13.05 135.3 -12.15 136.2 ; + RECT -13.05 140.1 -12.15 141.0 ; + RECT -12.15 130.5 -10.05 131.4 ; + RECT -12.6 130.5 -12.15 131.4 ; + RECT -13.05 130.95 -12.15 135.75 ; + RECT -12.6 135.3 -10.05 136.2 ; + RECT -12.15 135.3 -10.05 136.2 ; + RECT -12.6 135.3 -12.15 136.2 ; + RECT -13.05 135.75 -12.15 140.55 ; + RECT -12.6 140.1 -10.05 141.0 ; + RECT -9.15 132.9 -8.25 133.8 ; + RECT -9.15 137.7 -8.25 138.6 ; + RECT -10.05 132.9 -9.15 133.8 ; + RECT -9.15 132.9 -8.7 133.8 ; + RECT -9.15 133.35 -8.25 138.15 ; + RECT -10.05 137.7 -8.7 138.6 ; + RECT -20.55 130.5 -19.35 131.7 ; + RECT -20.55 132.9 -19.35 134.1 ; + RECT -20.55 135.3 -19.35 136.5 ; + RECT -20.55 137.7 -19.35 138.9 ; + RECT -20.55 140.1 -19.35 141.3 ; + RECT -22.35 130.5 -21.45 131.4 ; + RECT -22.35 135.3 -21.45 136.2 ; + RECT -22.35 135.3 -21.45 136.2 ; + RECT -22.35 140.1 -21.45 141.0 ; + RECT -21.45 130.5 -19.35 131.4 ; + RECT -21.9 130.5 -21.45 131.4 ; + RECT -22.35 130.95 -21.45 135.75 ; + RECT -21.9 135.3 -19.35 136.2 ; + RECT -21.45 135.3 -19.35 136.2 ; + RECT -21.9 135.3 -21.45 136.2 ; + RECT -22.35 135.75 -21.45 140.55 ; + RECT -21.9 140.1 -19.35 141.0 ; + RECT -18.45 132.9 -17.55 133.8 ; + RECT -18.45 137.7 -17.55 138.6 ; + RECT -19.35 132.9 -18.45 133.8 ; + RECT -18.45 132.9 -18.0 133.8 ; + RECT -18.45 133.35 -17.55 138.15 ; + RECT -19.35 137.7 -18.0 138.6 ; + RECT -20.55 142.5 -19.35 143.7 ; + RECT -10.05 142.5 -8.85 143.7 ; + RECT -14.85 130.8 -13.65 132.0 ; + RECT -7.95 88.5 -7.05 119.7 ; + RECT -22.65 88.5 -21.75 119.7 ; + RECT -14.1 116.4 -13.2 119.7 ; + RECT -16.05 102.0 -15.15 119.7 ; + RECT -22.2 90.6 -19.65 91.5 ; + RECT -8.85 109.8 -7.5 110.7 ; + RECT -21.75 117.0 -19.65 117.9 ; + RECT -21.75 112.2 -19.65 113.1 ; + RECT -21.75 107.4 -19.65 108.3 ; + RECT -8.85 117.0 -7.05 117.9 ; + RECT -8.85 112.2 -7.05 113.1 ; + RECT -13.65 116.7 -12.45 117.9 ; + RECT -13.65 114.3 -12.45 115.5 ; + RECT -13.65 114.3 -12.45 115.5 ; + RECT -13.65 111.9 -12.45 113.1 ; + RECT -16.05 116.7 -14.85 117.9 ; + RECT -16.05 114.3 -14.85 115.5 ; + RECT -16.05 111.9 -14.85 113.1 ; + RECT -16.05 109.5 -14.85 110.7 ; + RECT -16.05 107.1 -14.85 108.3 ; + RECT -16.05 102.3 -14.85 103.5 ; + RECT -16.05 99.9 -14.85 101.1 ; + RECT -16.05 97.5 -14.85 98.7 ; + RECT -16.05 95.1 -14.85 96.3 ; + RECT -16.05 92.7 -14.85 93.9 ; + RECT -19.65 90.3 -18.45 91.5 ; + RECT -8.85 109.5 -7.65 110.7 ; + RECT -13.05 116.4 -11.85 117.6 ; + RECT -15.0 102.0 -13.8 103.2 ; + RECT -14.1 114.45 -13.2 115.35 ; + RECT -14.1 88.5 -13.2 114.9 ; + RECT -18.45 114.45 -13.65 115.35 ; + RECT -19.65 99.9 -18.45 101.1 ; + RECT -14.1 100.05 -13.2 100.95 ; + RECT -14.1 88.5 -13.2 100.5 ; + RECT -14.25 99.9 -13.05 101.1 ; + RECT -19.65 95.1 -18.45 96.3 ; + RECT -14.1 95.25 -13.2 96.15 ; + RECT -14.1 88.5 -13.2 95.7 ; + RECT -14.25 95.1 -13.05 96.3 ; + RECT -22.2 114.45 -21.3 115.35 ; + RECT -21.75 114.45 -19.65 115.35 ; + RECT -22.2 105.3 -21.3 114.9 ; + RECT -22.2 109.65 -21.3 110.55 ; + RECT -21.75 109.65 -19.65 110.55 ; + RECT -22.2 105.3 -21.3 110.1 ; + RECT -22.2 102.45 -21.3 103.35 ; + RECT -21.75 102.45 -19.65 103.35 ; + RECT -22.2 102.9 -21.3 105.3 ; + RECT -22.2 97.65 -21.3 98.55 ; + RECT -21.75 97.65 -19.65 98.55 ; + RECT -22.2 98.1 -21.3 105.3 ; + RECT -22.2 92.85 -21.3 93.75 ; + RECT -21.75 92.85 -19.65 93.75 ; + RECT -22.2 93.3 -21.3 105.3 ; + RECT -7.95 164.4 -7.05 176.4 ; + RECT -22.65 164.4 -21.75 176.4 ; + RECT -22.2 173.4 -19.35 174.3 ; + RECT -9.9 173.4 -7.5 174.3 ; + RECT -22.2 166.35 -19.35 167.25 ; + RECT -22.2 171.15 -19.35 172.05 ; + RECT -10.35 166.35 -7.5 167.25 ; + RECT -17.1 164.4 -16.2 166.5 ; + RECT -13.8 164.4 -12.9 169.5 ; + RECT -14.7 172.05 -13.8 176.4 ; + RECT -11.55 166.2 -10.35 167.4 ; + RECT -11.55 168.6 -10.35 169.8 ; + RECT -11.55 168.6 -10.35 169.8 ; + RECT -11.55 171.0 -10.35 172.2 ; + RECT -20.55 166.2 -19.35 167.4 ; + RECT -20.55 168.6 -19.35 169.8 ; + RECT -20.55 168.6 -19.35 169.8 ; + RECT -20.55 171.0 -19.35 172.2 ; + RECT -20.55 173.4 -19.35 174.6 ; + RECT -10.35 173.4 -9.15 174.6 ; + RECT -17.1 171.6 -16.2 172.5 ; + RECT -17.1 168.6 -16.2 169.5 ; + RECT -16.65 171.6 -9.15 172.5 ; + RECT -17.1 169.05 -16.2 172.05 ; + RECT -19.35 168.6 -16.65 169.5 ; + RECT -17.1 166.5 -15.9 167.7 ; + RECT -13.8 169.5 -12.6 170.7 ; + RECT -43.05 236.1 -41.85 237.3 ; + RECT -45.0 236.1 -44.1 237.0 ; + RECT -51.75 250.95 -49.2 251.85 ; + RECT -61.95 292.65 -7.95 293.55 ; + RECT -8.4 268.8 -7.5 288.0 ; + RECT -37.8 268.8 -36.9 288.0 ; + RECT -41.85 229.95 -32.25 230.85 ; + RECT -41.85 215.25 -32.25 216.15 ; + RECT -40.05 216.15 -38.85 219.9 ; + RECT -40.05 228.15 -38.85 229.95 ; + RECT -35.25 229.05 -34.05 229.95 ; + RECT -35.25 216.15 -34.05 217.2 ; + RECT -37.65 219.75 -36.45 227.85 ; + RECT -34.35 223.8 -32.25 224.7 ; + RECT -41.85 223.8 -37.65 224.7 ; + RECT -35.25 226.65 -34.05 227.85 ; + RECT -37.65 226.65 -36.45 227.85 ; + RECT -35.25 217.2 -34.05 219.9 ; + RECT -37.65 217.2 -36.45 219.9 ; + RECT -40.05 217.2 -38.85 219.9 ; + RECT -40.05 227.85 -38.85 229.05 ; + RECT -35.55 223.65 -34.35 224.85 ; + RECT -16.35 234.75 -15.15 235.95 ; + RECT -16.35 232.35 -15.15 233.55 ; + RECT -14.85 265.8 -13.65 267.0 ; + RECT -8.4 257.4 -7.5 267.0 ; + RECT -23.1 257.4 -22.2 267.0 ; + RECT -22.2 259.2 -18.45 260.4 ; + RECT -10.2 259.2 -8.4 260.4 ; + RECT -9.3 264.0 -8.4 265.2 ; + RECT -22.2 264.0 -21.15 265.2 ; + RECT -18.6 261.6 -10.5 262.8 ; + RECT -14.55 264.9 -13.65 267.0 ; + RECT -14.1 264.0 -12.9 265.2 ; + RECT -14.1 261.6 -12.9 262.8 ; + RECT -13.95 264.0 -11.25 265.2 ; + RECT -13.95 261.6 -11.25 262.8 ; + RECT -18.45 259.2 -15.75 260.4 ; + RECT -9.3 259.2 -8.1 260.4 ; + RECT -13.5 263.7 -12.3 264.9 ; + RECT -14.85 256.2 -13.65 257.4 ; + RECT -8.4 247.8 -7.5 257.4 ; + RECT -23.1 247.8 -22.2 257.4 ; + RECT -22.2 249.6 -18.45 250.8 ; + RECT -10.2 249.6 -8.4 250.8 ; + RECT -9.3 254.4 -8.4 255.6 ; + RECT -22.2 254.4 -21.15 255.6 ; + RECT -18.6 252.0 -10.5 253.2 ; + RECT -14.55 255.3 -13.65 257.4 ; + RECT -14.1 254.4 -12.9 255.6 ; + RECT -14.1 252.0 -12.9 253.2 ; + RECT -13.95 254.4 -11.25 255.6 ; + RECT -13.95 252.0 -11.25 253.2 ; + RECT -18.45 249.6 -15.75 250.8 ; + RECT -9.3 249.6 -8.1 250.8 ; + RECT -13.5 254.1 -12.3 255.3 ; + RECT -31.65 247.8 -30.45 249.0 ; + RECT -37.8 247.8 -36.9 257.4 ; + RECT -23.1 247.8 -22.2 257.4 ; + RECT -26.85 254.4 -23.1 255.6 ; + RECT -36.9 254.4 -35.1 255.6 ; + RECT -36.9 249.6 -36.0 250.8 ; + RECT -24.15 249.6 -23.1 250.8 ; + RECT -34.8 252.0 -26.7 253.2 ; + RECT -31.65 247.8 -30.75 249.9 ; + RECT -32.4 249.6 -31.2 250.8 ; + RECT -32.4 252.0 -31.2 253.2 ; + RECT -34.05 249.6 -31.35 250.8 ; + RECT -34.05 252.0 -31.35 253.2 ; + RECT -29.55 254.4 -26.85 255.6 ; + RECT -37.2 254.4 -36.0 255.6 ; + RECT -33.0 249.9 -31.8 251.1 ; + RECT -31.65 257.4 -30.45 258.6 ; + RECT -37.8 257.4 -36.9 267.0 ; + RECT -23.1 257.4 -22.2 267.0 ; + RECT -26.85 264.0 -23.1 265.2 ; + RECT -36.9 264.0 -35.1 265.2 ; + RECT -36.9 259.2 -36.0 260.4 ; + RECT -24.15 259.2 -23.1 260.4 ; + RECT -34.8 261.6 -26.7 262.8 ; + RECT -31.65 257.4 -30.75 259.5 ; + RECT -32.4 259.2 -31.2 260.4 ; + RECT -32.4 261.6 -31.2 262.8 ; + RECT -34.05 259.2 -31.35 260.4 ; + RECT -34.05 261.6 -31.35 262.8 ; + RECT -29.55 264.0 -26.85 265.2 ; + RECT -37.2 264.0 -36.0 265.2 ; + RECT -33.0 259.5 -31.8 260.7 ; + RECT -14.85 260.4 -13.65 261.6 ; + RECT -14.85 250.8 -13.65 252.0 ; + RECT -31.65 253.2 -30.45 254.4 ; + RECT -62.55 267.6 -51.75 268.8 ; + RECT -53.55 265.5 -52.35 267.6 ; + RECT -56.55 265.5 -55.35 266.7 ; + RECT -59.55 265.5 -58.35 266.7 ; + RECT -62.55 265.5 -61.35 267.6 ; + RECT -56.25 264.6 -55.05 265.5 ; + RECT -57.45 263.4 -52.35 264.6 ; + RECT -53.55 258.3 -52.35 263.4 ; + RECT -56.25 260.1 -55.05 263.4 ; + RECT -59.85 261.9 -58.65 265.5 ; + RECT -59.85 260.7 -58.05 261.9 ; + RECT -59.85 260.1 -58.65 260.7 ; + RECT -55.95 258.9 -54.75 260.1 ; + RECT -60.15 258.9 -58.95 260.1 ; + RECT -62.55 258.9 -61.35 264.6 ; + RECT -58.05 257.4 -56.85 257.7 ; + RECT -62.55 256.2 -51.75 257.4 ; + RECT -55.95 254.1 -53.25 255.3 ; + RECT -60.15 254.1 -57.45 255.3 ; + RECT -62.55 239.4 -51.15 240.6 ; + RECT -53.55 240.6 -52.35 242.7 ; + RECT -56.55 241.5 -55.35 242.7 ; + RECT -59.55 241.5 -58.35 242.7 ; + RECT -62.55 240.6 -61.35 242.7 ; + RECT -56.25 242.7 -55.05 243.6 ; + RECT -53.55 243.6 -52.35 249.3 ; + RECT -57.45 243.6 -55.05 244.8 ; + RECT -56.25 244.8 -55.05 248.1 ; + RECT -59.85 242.7 -58.65 246.3 ; + RECT -59.85 246.3 -58.05 247.5 ; + RECT -59.85 247.5 -58.65 248.1 ; + RECT -55.95 248.1 -54.75 249.3 ; + RECT -60.15 248.1 -58.95 249.3 ; + RECT -62.55 243.6 -61.35 249.3 ; + RECT -58.05 250.5 -56.85 250.8 ; + RECT -62.55 250.8 -51.15 252.0 ; + RECT -55.95 252.9 -53.25 254.1 ; + RECT -60.15 252.9 -57.45 254.1 ; + RECT -62.55 238.2 -51.15 239.4 ; + RECT -53.55 236.1 -52.35 238.2 ; + RECT -56.55 236.1 -55.35 237.3 ; + RECT -59.55 236.1 -58.35 237.3 ; + RECT -62.55 236.1 -61.35 238.2 ; + RECT -56.25 235.2 -55.05 236.1 ; + RECT -53.55 229.5 -52.35 235.2 ; + RECT -57.45 234.0 -55.05 235.2 ; + RECT -56.25 230.7 -55.05 234.0 ; + RECT -59.85 232.5 -58.65 236.1 ; + RECT -59.85 231.3 -58.05 232.5 ; + RECT -59.85 230.7 -58.65 231.3 ; + RECT -55.95 229.5 -54.75 230.7 ; + RECT -60.15 229.5 -58.95 230.7 ; + RECT -62.55 229.5 -61.35 235.2 ; + RECT -58.05 228.0 -56.85 228.3 ; + RECT -62.55 226.8 -51.15 228.0 ; + RECT -55.95 224.7 -53.25 225.9 ; + RECT -60.15 224.7 -57.45 225.9 ; + RECT -43.05 235.35 -41.85 236.55 ; + RECT -14.55 264.75 -13.65 265.65 ; + RECT -14.1 264.75 -7.95 265.65 ; + RECT -14.55 263.4 -13.65 265.2 ; + RECT -32.7 238.05 -31.8 238.95 ; + RECT -45.0 238.2 -44.1 239.1 ; + RECT -32.7 236.55 -31.8 238.65 ; + RECT -45.0 236.55 -44.1 238.65 ; + RECT -32.85 237.9 -31.65 239.1 ; + RECT -45.15 238.05 -43.95 239.25 ; + RECT -55.8 271.05 -54.9 271.95 ; + RECT -55.35 271.2 -44.55 272.1 ; + RECT -55.95 270.9 -54.75 272.1 ; + RECT -13.95 239.55 -12.75 240.75 ; + RECT -13.65 220.5 -12.75 221.4 ; + RECT -32.1 220.65 -31.2 221.55 ; + RECT -31.65 220.65 -13.2 221.55 ; + RECT -13.8 220.35 -12.6 221.55 ; + RECT -32.25 220.5 -31.05 221.7 ; + RECT -32.85 271.2 -31.65 272.4 ; + RECT -13.8 239.55 -12.6 240.75 ; + RECT -50.25 249.6 -49.05 250.8 ; + RECT -16.35 239.55 -15.45 240.45 ; + RECT -16.35 240.0 -15.45 242.55 ; + RECT -32.25 239.55 -15.9 240.45 ; + RECT -45.9 244.65 -45.0 245.55 ; + RECT -45.45 244.65 -32.25 245.55 ; + RECT -46.05 244.5 -44.85 245.7 ; + RECT -16.35 243.3 -15.45 244.2 ; + RECT -16.35 240.15 -15.45 243.75 ; + RECT -32.25 243.3 -15.9 244.2 ; + RECT -23.1 268.35 -22.2 269.25 ; + RECT -23.1 267.0 -22.2 268.8 ; + RECT -45.45 268.35 -22.65 269.25 ; + RECT -45.9 264.75 -45.0 265.65 ; + RECT -45.45 264.75 -22.65 265.65 ; + RECT -46.05 264.6 -44.85 265.8 ; + RECT -8.4 229.95 -7.5 230.85 ; + RECT -8.4 172.2 -7.5 173.1 ; + RECT -37.8 172.35 -36.9 173.25 ; + RECT -32.25 229.95 -7.95 230.85 ; + RECT -37.35 172.35 -7.95 173.25 ; + RECT -8.55 229.8 -7.35 231.0 ; + RECT -8.55 172.05 -7.35 173.25 ; + RECT -37.95 172.2 -36.75 173.4 ; + RECT -46.65 292.65 -45.45 293.85 ; + RECT -43.2 267.0 -42.3 267.9 ; + RECT -43.2 277.5 -42.3 278.4 ; + RECT -42.75 267.0 -36.9 267.9 ; + RECT -51.75 277.65 -42.75 278.55 ; + RECT -43.35 266.85 -42.15 268.05 ; + RECT -43.35 277.35 -42.15 278.55 ; + RECT -45.9 316.5 -45.0 317.4 ; + RECT -51.75 316.65 -45.45 317.55 ; + RECT -46.05 316.35 -44.85 317.55 ; + RECT -43.2 267.0 -42.3 267.9 ; + RECT -43.2 253.5 -42.3 254.4 ; + RECT -42.75 267.0 -36.9 267.9 ; + RECT -51.75 253.65 -42.75 254.55 ; + RECT -43.35 266.85 -42.15 268.05 ; + RECT -43.35 253.35 -42.15 254.55 ; + RECT -45.9 344.7 -45.0 345.6 ; + RECT -56.85 344.85 -45.45 345.75 ; + RECT -46.05 344.55 -44.85 345.75 ; + RECT -62.4 229.95 -61.5 230.85 ; + RECT -61.95 229.95 -22.65 230.85 ; + RECT -62.55 229.8 -61.35 231.0 ; + RECT -52.2 229.95 -51.3 230.85 ; + RECT -51.75 229.95 -22.65 230.85 ; + RECT -52.35 229.8 -51.15 231.0 ; + RECT -7.95 180.0 -7.05 189.6 ; + RECT -22.65 180.0 -21.75 189.6 ; + RECT -21.75 181.8 -19.35 183.0 ; + RECT -9.75 181.8 -7.95 183.0 ; + RECT -8.85 186.6 -7.95 187.8 ; + RECT -21.75 186.6 -20.55 187.8 ; + RECT -19.5 184.2 -10.05 185.4 ; + RECT -14.7 187.5 -13.8 189.6 ; + RECT -14.7 180.0 -13.8 184.2 ; + RECT -13.65 186.6 -12.45 187.8 ; + RECT -13.65 184.2 -12.45 185.4 ; + RECT -14.55 186.6 -13.35 187.8 ; + RECT -14.55 184.2 -13.35 185.4 ; + RECT -19.35 181.8 -18.15 183.0 ; + RECT -8.85 181.8 -7.65 183.0 ; + RECT -13.65 186.3 -12.45 187.5 ; + RECT -7.95 189.6 -7.05 199.2 ; + RECT -22.65 189.6 -21.75 199.2 ; + RECT -21.75 191.4 -19.35 192.6 ; + RECT -9.75 191.4 -7.95 192.6 ; + RECT -8.85 196.2 -7.95 197.4 ; + RECT -21.75 196.2 -20.55 197.4 ; + RECT -19.5 193.8 -10.05 195.0 ; + RECT -14.7 197.1 -13.8 199.2 ; + RECT -14.7 189.6 -13.8 193.8 ; + RECT -13.65 196.2 -12.45 197.4 ; + RECT -13.65 193.8 -12.45 195.0 ; + RECT -14.55 196.2 -13.35 197.4 ; + RECT -14.55 193.8 -13.35 195.0 ; + RECT -19.35 191.4 -18.15 192.6 ; + RECT -8.85 191.4 -7.65 192.6 ; + RECT -13.65 195.9 -12.45 197.1 ; + RECT -37.35 164.4 -36.45 180.0 ; + RECT -22.65 164.4 -21.75 180.0 ; + RECT -35.1 175.2 -24.0 176.1 ; + RECT -25.05 177.0 -22.2 177.9 ; + RECT -36.9 177.0 -33.3 177.9 ; + RECT -25.05 167.55 -22.2 168.45 ; + RECT -25.05 172.35 -22.2 173.25 ; + RECT -36.9 167.55 -34.05 168.45 ; + RECT -27.15 164.4 -26.25 168.6 ; + RECT -32.7 164.4 -31.8 166.5 ; + RECT -34.5 164.4 -33.6 166.5 ; + RECT -29.85 175.2 -28.95 180.0 ; + RECT -30.9 167.4 -28.2 168.6 ; + RECT -30.9 169.8 -28.2 171.0 ; + RECT -30.9 169.8 -28.2 171.0 ; + RECT -30.9 172.2 -28.2 173.4 ; + RECT -30.9 172.2 -28.2 173.4 ; + RECT -30.9 174.6 -28.2 175.8 ; + RECT -31.05 167.4 -29.85 168.6 ; + RECT -31.05 169.8 -29.85 171.0 ; + RECT -31.05 169.8 -29.85 171.0 ; + RECT -31.05 172.2 -29.85 173.4 ; + RECT -31.05 172.2 -29.85 173.4 ; + RECT -31.05 174.6 -29.85 175.8 ; + RECT -26.25 177.0 -25.05 178.2 ; + RECT -38.1 177.0 -35.4 178.2 ; + RECT -26.25 169.8 -25.05 171.0 ; + RECT -26.25 174.6 -25.05 175.8 ; + RECT -28.65 167.4 -27.45 168.6 ; + RECT -33.0 170.7 -31.8 171.9 ; + RECT -33.0 170.7 -31.8 171.9 ; + RECT -33.9 165.3 -32.7 166.5 ; + RECT -33.0 173.1 -31.8 174.3 ; + RECT -33.0 173.1 -31.8 174.3 ; + RECT -36.0 165.3 -34.8 166.5 ; + RECT -37.35 164.4 -36.45 180.0 ; + RECT -52.05 164.4 -51.15 180.0 ; + RECT -49.8 175.2 -38.7 176.1 ; + RECT -51.6 177.0 -48.75 177.9 ; + RECT -40.5 177.0 -36.9 177.9 ; + RECT -51.6 167.55 -48.75 168.45 ; + RECT -51.6 172.35 -48.75 173.25 ; + RECT -39.75 167.55 -36.9 168.45 ; + RECT -47.55 164.4 -46.65 168.6 ; + RECT -42.0 164.4 -41.1 166.5 ; + RECT -40.2 164.4 -39.3 166.5 ; + RECT -44.85 175.2 -43.95 180.0 ; + RECT -43.8 167.4 -41.1 168.6 ; + RECT -43.8 169.8 -41.1 171.0 ; + RECT -43.8 169.8 -41.1 171.0 ; + RECT -43.8 172.2 -41.1 173.4 ; + RECT -43.8 172.2 -41.1 173.4 ; + RECT -43.8 174.6 -41.1 175.8 ; + RECT -49.95 167.4 -48.75 168.6 ; + RECT -49.95 169.8 -48.75 171.0 ; + RECT -49.95 169.8 -48.75 171.0 ; + RECT -49.95 172.2 -48.75 173.4 ; + RECT -49.95 172.2 -48.75 173.4 ; + RECT -49.95 174.6 -48.75 175.8 ; + RECT -49.95 177.0 -48.75 178.2 ; + RECT -41.1 177.0 -38.4 178.2 ; + RECT -49.95 169.8 -48.75 171.0 ; + RECT -49.95 174.6 -48.75 175.8 ; + RECT -47.55 167.4 -46.35 168.6 ; + RECT -43.2 170.7 -42.0 171.9 ; + RECT -43.2 170.7 -42.0 171.9 ; + RECT -42.3 165.3 -41.1 166.5 ; + RECT -43.2 173.1 -42.0 174.3 ; + RECT -43.2 173.1 -42.0 174.3 ; + RECT -40.2 165.3 -39.0 166.5 ; + RECT -37.35 180.0 -36.45 189.6 ; + RECT -22.65 180.0 -21.75 189.6 ; + RECT -25.05 186.6 -22.65 187.8 ; + RECT -36.45 186.6 -34.65 187.8 ; + RECT -36.45 181.8 -35.55 183.0 ; + RECT -23.85 181.8 -22.65 183.0 ; + RECT -34.35 184.2 -24.9 185.4 ; + RECT -30.6 180.0 -29.7 182.1 ; + RECT -30.6 185.4 -29.7 189.6 ; + RECT -31.95 181.8 -30.75 183.0 ; + RECT -31.95 184.2 -30.75 185.4 ; + RECT -31.05 181.8 -29.85 183.0 ; + RECT -31.05 184.2 -29.85 185.4 ; + RECT -26.25 186.6 -25.05 187.8 ; + RECT -36.75 186.6 -35.55 187.8 ; + RECT -31.95 182.1 -30.75 183.3 ; + RECT -37.35 180.0 -36.45 189.6 ; + RECT -52.05 180.0 -51.15 189.6 ; + RECT -51.15 186.6 -48.75 187.8 ; + RECT -39.15 186.6 -37.35 187.8 ; + RECT -38.25 181.8 -37.35 183.0 ; + RECT -51.15 181.8 -49.95 183.0 ; + RECT -48.9 184.2 -39.45 185.4 ; + RECT -44.1 180.0 -43.2 182.1 ; + RECT -44.1 185.4 -43.2 189.6 ; + RECT -40.65 181.8 -39.45 183.0 ; + RECT -40.65 184.2 -39.45 185.4 ; + RECT -49.95 181.8 -48.75 183.0 ; + RECT -49.95 184.2 -48.75 185.4 ; + RECT -49.95 186.6 -48.75 187.8 ; + RECT -39.45 186.6 -38.25 187.8 ; + RECT -44.25 182.1 -43.05 183.3 ; + RECT -37.35 189.6 -36.45 199.2 ; + RECT -52.05 189.6 -51.15 199.2 ; + RECT -51.15 196.2 -48.75 197.4 ; + RECT -39.15 196.2 -37.35 197.4 ; + RECT -38.25 191.4 -37.35 192.6 ; + RECT -51.15 191.4 -49.95 192.6 ; + RECT -48.9 193.8 -39.45 195.0 ; + RECT -44.1 189.6 -43.2 191.7 ; + RECT -44.1 195.0 -43.2 199.2 ; + RECT -40.65 191.4 -39.45 192.6 ; + RECT -40.65 193.8 -39.45 195.0 ; + RECT -49.95 191.4 -48.75 192.6 ; + RECT -49.95 193.8 -48.75 195.0 ; + RECT -49.95 196.2 -48.75 197.4 ; + RECT -39.45 196.2 -38.25 197.4 ; + RECT -44.25 191.7 -43.05 192.9 ; + RECT -37.35 199.2 -36.45 208.8 ; + RECT -52.05 199.2 -51.15 208.8 ; + RECT -51.15 205.8 -48.75 207.0 ; + RECT -39.15 205.8 -37.35 207.0 ; + RECT -38.25 201.0 -37.35 202.2 ; + RECT -51.15 201.0 -49.95 202.2 ; + RECT -48.9 203.4 -39.45 204.6 ; + RECT -44.1 199.2 -43.2 201.3 ; + RECT -44.1 204.6 -43.2 208.8 ; + RECT -40.65 201.0 -39.45 202.2 ; + RECT -40.65 203.4 -39.45 204.6 ; + RECT -49.95 201.0 -48.75 202.2 ; + RECT -49.95 203.4 -48.75 204.6 ; + RECT -49.95 205.8 -48.75 207.0 ; + RECT -39.45 205.8 -38.25 207.0 ; + RECT -44.25 201.3 -43.05 202.5 ; + RECT -40.8 144.3 -39.6 145.5 ; + RECT -40.8 150.9 -39.6 152.1 ; + RECT -44.25 144.3 -43.05 145.5 ; + RECT -44.25 153.6 -43.05 154.8 ; + RECT -64.65 144.3 -63.45 145.5 ; + RECT -64.65 156.3 -63.45 157.5 ; + RECT -49.05 144.3 -47.85 145.5 ; + RECT -49.05 159.0 -47.85 160.2 ; + RECT -17.4 153.6 -16.2 154.8 ; + RECT -14.1 161.7 -12.9 162.9 ; + RECT -27.45 161.7 -26.25 162.9 ; + RECT -33.0 153.6 -31.8 154.8 ; + RECT -34.8 156.3 -33.6 157.5 ; + RECT -47.85 161.7 -46.65 162.9 ; + RECT -42.3 159.0 -41.1 160.2 ; + RECT -40.5 156.3 -39.3 157.5 ; + RECT -16.05 121.05 -15.15 121.95 ; + RECT -5.85 121.05 -4.95 121.95 ; + RECT -5.7 150.9 -4.8 151.8 ; + RECT -16.05 119.7 -15.15 121.5 ; + RECT -5.7 121.5 -4.8 151.35 ; + RECT -16.2 120.9 -15.0 122.1 ; + RECT -6.0 120.9 -4.8 122.1 ; + RECT -5.85 150.75 -4.65 151.95 ; + RECT -14.7 145.95 -13.8 146.85 ; + RECT -13.8 145.95 -12.9 146.85 ; + RECT -14.7 145.5 -13.8 146.4 ; + RECT -14.25 145.95 -13.35 146.85 ; + RECT -13.8 146.4 -12.9 164.4 ; + RECT -30.9 214.2 -29.7 215.4 ; + RECT -44.4 211.5 -43.2 212.7 ; + RECT -15.0 216.9 -13.8 218.1 ; + RECT -4.05 214.2 -3.15 215.1 ; + RECT -3.9 214.65 -3.0 265.2 ; + RECT -4.2 214.05 -3.0 215.25 ; + RECT -22.95 219.6 -21.75 220.8 ; + RECT -52.35 219.6 -51.15 220.8 ; + RECT -8.25 148.2 -7.05 149.4 ; + RECT -14.7 126.45 -13.8 127.35 ; + RECT -27.75 126.45 -26.85 127.35 ; + RECT -14.7 126.9 -13.8 128.7 ; + RECT -27.6 85.5 -26.7 126.9 ; + RECT -14.85 126.3 -13.65 127.5 ; + RECT -27.9 126.3 -26.7 127.5 ; + RECT -14.7 123.75 -13.8 124.65 ; + RECT -14.1 123.75 -13.2 124.65 ; + RECT -14.7 124.2 -13.8 126.9 ; + RECT -14.25 123.75 -13.65 124.65 ; + RECT -14.1 119.7 -13.2 124.2 ; + RECT -27.6 85.5 -26.4 86.7 ; + RECT -14.1 88.5 -12.9 89.7 ; + RECT -15.0 175.5 -13.8 176.7 ; + RECT -15.0 180.0 -13.8 181.2 ; + RECT 0.0 219.6 2.4 220.8 ; + Layer via1 ; + RECT 124.8 209.7 125.4 210.3 ; + RECT 133.8 209.7 134.4 210.3 ; + RECT 125.7 200.4 126.3 201.0 ; + RECT 129.9 200.4 130.5 201.0 ; + RECT 124.8 219.3 125.4 219.9 ; + RECT 133.8 219.3 134.4 219.9 ; + RECT 125.7 228.6 126.3 229.2 ; + RECT 129.9 228.6 130.5 229.2 ; + RECT 124.8 239.1 125.4 239.7 ; + RECT 133.8 239.1 134.4 239.7 ; + RECT 125.7 229.8 126.3 230.4 ; + RECT 129.9 229.8 130.5 230.4 ; + RECT 124.8 248.7 125.4 249.3 ; + RECT 133.8 248.7 134.4 249.3 ; + RECT 125.7 258.0 126.3 258.6 ; + RECT 129.9 258.0 130.5 258.6 ; + RECT 124.8 268.5 125.4 269.1 ; + RECT 133.8 268.5 134.4 269.1 ; + RECT 125.7 259.2 126.3 259.8 ; + RECT 129.9 259.2 130.5 259.8 ; + RECT 124.8 278.1 125.4 278.7 ; + RECT 133.8 278.1 134.4 278.7 ; + RECT 125.7 287.4 126.3 288.0 ; + RECT 129.9 287.4 130.5 288.0 ; + RECT 124.8 297.9 125.4 298.5 ; + RECT 133.8 297.9 134.4 298.5 ; + RECT 125.7 288.6 126.3 289.2 ; + RECT 129.9 288.6 130.5 289.2 ; + RECT 124.8 307.5 125.4 308.1 ; + RECT 133.8 307.5 134.4 308.1 ; + RECT 125.7 316.8 126.3 317.4 ; + RECT 129.9 316.8 130.5 317.4 ; + RECT 124.8 327.3 125.4 327.9 ; + RECT 133.8 327.3 134.4 327.9 ; + RECT 125.7 318.0 126.3 318.6 ; + RECT 129.9 318.0 130.5 318.6 ; + RECT 124.8 336.9 125.4 337.5 ; + RECT 133.8 336.9 134.4 337.5 ; + RECT 125.7 346.2 126.3 346.8 ; + RECT 129.9 346.2 130.5 346.8 ; + RECT 124.8 356.7 125.4 357.3 ; + RECT 133.8 356.7 134.4 357.3 ; + RECT 125.7 347.4 126.3 348.0 ; + RECT 129.9 347.4 130.5 348.0 ; + RECT 124.8 366.3 125.4 366.9 ; + RECT 133.8 366.3 134.4 366.9 ; + RECT 125.7 375.6 126.3 376.2 ; + RECT 129.9 375.6 130.5 376.2 ; + RECT 124.8 386.1 125.4 386.7 ; + RECT 133.8 386.1 134.4 386.7 ; + RECT 125.7 376.8 126.3 377.4 ; + RECT 129.9 376.8 130.5 377.4 ; + RECT 124.8 395.7 125.4 396.3 ; + RECT 133.8 395.7 134.4 396.3 ; + RECT 125.7 405.0 126.3 405.6 ; + RECT 129.9 405.0 130.5 405.6 ; + RECT 124.8 415.5 125.4 416.1 ; + RECT 133.8 415.5 134.4 416.1 ; + RECT 125.7 406.2 126.3 406.8 ; + RECT 129.9 406.2 130.5 406.8 ; + RECT 124.8 425.1 125.4 425.7 ; + RECT 133.8 425.1 134.4 425.7 ; + RECT 125.7 434.4 126.3 435.0 ; + RECT 129.9 434.4 130.5 435.0 ; + RECT 135.0 209.7 135.6 210.3 ; + RECT 144.0 209.7 144.6 210.3 ; + RECT 135.9 200.4 136.5 201.0 ; + RECT 140.1 200.4 140.7 201.0 ; + RECT 135.0 219.3 135.6 219.9 ; + RECT 144.0 219.3 144.6 219.9 ; + RECT 135.9 228.6 136.5 229.2 ; + RECT 140.1 228.6 140.7 229.2 ; + RECT 135.0 239.1 135.6 239.7 ; + RECT 144.0 239.1 144.6 239.7 ; + RECT 135.9 229.8 136.5 230.4 ; + RECT 140.1 229.8 140.7 230.4 ; + RECT 135.0 248.7 135.6 249.3 ; + RECT 144.0 248.7 144.6 249.3 ; + RECT 135.9 258.0 136.5 258.6 ; + RECT 140.1 258.0 140.7 258.6 ; + RECT 135.0 268.5 135.6 269.1 ; + RECT 144.0 268.5 144.6 269.1 ; + RECT 135.9 259.2 136.5 259.8 ; + RECT 140.1 259.2 140.7 259.8 ; + RECT 135.0 278.1 135.6 278.7 ; + RECT 144.0 278.1 144.6 278.7 ; + RECT 135.9 287.4 136.5 288.0 ; + RECT 140.1 287.4 140.7 288.0 ; + RECT 135.0 297.9 135.6 298.5 ; + RECT 144.0 297.9 144.6 298.5 ; + RECT 135.9 288.6 136.5 289.2 ; + RECT 140.1 288.6 140.7 289.2 ; + RECT 135.0 307.5 135.6 308.1 ; + RECT 144.0 307.5 144.6 308.1 ; + RECT 135.9 316.8 136.5 317.4 ; + RECT 140.1 316.8 140.7 317.4 ; + RECT 135.0 327.3 135.6 327.9 ; + RECT 144.0 327.3 144.6 327.9 ; + RECT 135.9 318.0 136.5 318.6 ; + RECT 140.1 318.0 140.7 318.6 ; + RECT 135.0 336.9 135.6 337.5 ; + RECT 144.0 336.9 144.6 337.5 ; + RECT 135.9 346.2 136.5 346.8 ; + RECT 140.1 346.2 140.7 346.8 ; + RECT 135.0 356.7 135.6 357.3 ; + RECT 144.0 356.7 144.6 357.3 ; + RECT 135.9 347.4 136.5 348.0 ; + RECT 140.1 347.4 140.7 348.0 ; + RECT 135.0 366.3 135.6 366.9 ; + RECT 144.0 366.3 144.6 366.9 ; + RECT 135.9 375.6 136.5 376.2 ; + RECT 140.1 375.6 140.7 376.2 ; + RECT 135.0 386.1 135.6 386.7 ; + RECT 144.0 386.1 144.6 386.7 ; + RECT 135.9 376.8 136.5 377.4 ; + RECT 140.1 376.8 140.7 377.4 ; + RECT 135.0 395.7 135.6 396.3 ; + RECT 144.0 395.7 144.6 396.3 ; + RECT 135.9 405.0 136.5 405.6 ; + RECT 140.1 405.0 140.7 405.6 ; + RECT 135.0 415.5 135.6 416.1 ; + RECT 144.0 415.5 144.6 416.1 ; + RECT 135.9 406.2 136.5 406.8 ; + RECT 140.1 406.2 140.7 406.8 ; + RECT 135.0 425.1 135.6 425.7 ; + RECT 144.0 425.1 144.6 425.7 ; + RECT 135.9 434.4 136.5 435.0 ; + RECT 140.1 434.4 140.7 435.0 ; + RECT 127.2 451.2 127.8 451.8 ; + RECT 132.0 451.2 132.6 451.8 ; + RECT 127.2 442.8 127.8 443.4 ; + RECT 129.6 442.8 130.2 443.4 ; + RECT 137.4 451.2 138.0 451.8 ; + RECT 142.2 451.2 142.8 451.8 ; + RECT 137.4 442.8 138.0 443.4 ; + RECT 139.8 442.8 140.4 443.4 ; + RECT 133.8 194.1 134.4 194.7 ; + RECT 128.1 164.7 128.7 165.3 ; + RECT 130.8 164.7 131.4 165.3 ; + RECT 125.1 154.8 125.7 155.4 ; + RECT 144.0 194.1 144.6 194.7 ; + RECT 138.3 164.7 138.9 165.3 ; + RECT 141.0 164.7 141.6 165.3 ; + RECT 135.3 154.8 135.9 155.4 ; + RECT 125.7 148.2 126.3 148.8 ; + RECT 130.2 147.6 130.8 148.2 ; + RECT 127.5 138.0 128.1 138.6 ; + RECT 126.6 126.3 127.2 126.9 ; + RECT 133.2 115.5 133.8 116.1 ; + RECT 129.9 110.1 130.5 110.7 ; + RECT 126.6 100.2 127.2 100.8 ; + RECT 133.8 96.0 134.4 96.6 ; + RECT 127.8 93.9 128.4 94.5 ; + RECT 135.9 148.2 136.5 148.8 ; + RECT 140.4 147.6 141.0 148.2 ; + RECT 137.7 138.0 138.3 138.6 ; + RECT 136.8 126.3 137.4 126.9 ; + RECT 143.4 115.5 144.0 116.1 ; + RECT 140.1 110.1 140.7 110.7 ; + RECT 136.8 100.2 137.4 100.8 ; + RECT 144.0 96.0 144.6 96.6 ; + RECT 138.0 93.9 138.6 94.5 ; + RECT 127.8 85.5 128.4 86.1 ; + RECT 130.2 81.0 130.8 81.6 ; + RECT 129.3 77.7 129.9 78.3 ; + RECT 133.8 77.1 134.4 77.7 ; + RECT 129.3 75.6 129.9 76.2 ; + RECT 126.0 74.7 126.6 75.3 ; + RECT 130.2 71.1 130.8 71.7 ; + RECT 130.2 68.7 130.8 69.3 ; + RECT 127.8 65.7 128.4 66.3 ; + RECT 128.7 62.4 129.3 63.0 ; + RECT 126.0 61.2 126.6 61.8 ; + RECT 130.2 55.5 130.8 56.1 ; + RECT 129.3 52.2 129.9 52.8 ; + RECT 133.8 51.6 134.4 52.2 ; + RECT 129.3 49.2 129.9 49.8 ; + RECT 126.0 48.3 126.6 48.9 ; + RECT 130.2 44.7 130.8 45.3 ; + RECT 130.2 42.3 130.8 42.9 ; + RECT 127.8 39.3 128.4 39.9 ; + RECT 139.8 85.5 140.4 86.1 ; + RECT 137.4 81.0 138.0 81.6 ; + RECT 138.3 77.7 138.9 78.3 ; + RECT 133.8 77.1 134.4 77.7 ; + RECT 138.3 75.6 138.9 76.2 ; + RECT 141.6 74.7 142.2 75.3 ; + RECT 137.4 71.1 138.0 71.7 ; + RECT 137.4 68.7 138.0 69.3 ; + RECT 139.8 65.7 140.4 66.3 ; + RECT 138.9 62.4 139.5 63.0 ; + RECT 141.6 61.2 142.2 61.8 ; + RECT 137.4 55.5 138.0 56.1 ; + RECT 138.3 52.2 138.9 52.8 ; + RECT 133.8 51.6 134.4 52.2 ; + RECT 138.3 49.2 138.9 49.8 ; + RECT 141.6 48.3 142.2 48.9 ; + RECT 137.4 44.7 138.0 45.3 ; + RECT 137.4 42.3 138.0 42.9 ; + RECT 139.8 39.3 140.4 39.9 ; + RECT 126.9 45.0 127.5 45.6 ; + RECT 131.7 41.4 132.3 42.0 ; + RECT 133.8 36.9 134.4 37.5 ; + RECT 140.7 45.0 141.3 45.6 ; + RECT 135.9 41.4 136.5 42.0 ; + RECT 133.8 36.9 134.4 37.5 ; + RECT 58.5 93.15 59.1 93.75 ; + RECT 77.7 87.9 78.3 88.5 ; + RECT 56.4 98.55 57.0 99.15 ; + RECT 75.6 103.8 76.2 104.4 ; + RECT 58.5 90.3 59.1 90.9 ; + RECT 56.4 87.0 57.0 87.6 ; + RECT 54.3 101.4 54.9 102.0 ; + RECT 56.4 104.7 57.0 105.3 ; + RECT 58.5 119.7 59.1 120.3 ; + RECT 52.2 116.4 52.8 117.0 ; + RECT 54.3 130.8 54.9 131.4 ; + RECT 77.7 130.8 78.3 131.4 ; + RECT 52.2 134.1 52.8 134.7 ; + RECT 75.6 134.1 76.2 134.7 ; + RECT 60.6 81.15 61.2 81.75 ; + RECT 62.7 95.85 63.3 96.45 ; + RECT 60.6 110.55 61.2 111.15 ; + RECT 62.7 125.25 63.3 125.85 ; + RECT 60.6 137.7 61.2 138.3 ; + RECT 58.5 151.95 59.1 152.55 ; + RECT 77.7 146.7 78.3 147.3 ; + RECT 56.4 157.35 57.0 157.95 ; + RECT 75.6 162.6 76.2 163.2 ; + RECT 58.5 149.1 59.1 149.7 ; + RECT 56.4 145.8 57.0 146.4 ; + RECT 54.3 160.2 54.9 160.8 ; + RECT 56.4 163.5 57.0 164.1 ; + RECT 58.5 178.5 59.1 179.1 ; + RECT 52.2 175.2 52.8 175.8 ; + RECT 54.3 189.6 54.9 190.2 ; + RECT 77.7 189.6 78.3 190.2 ; + RECT 52.2 192.9 52.8 193.5 ; + RECT 75.6 192.9 76.2 193.5 ; + RECT 60.6 139.95 61.2 140.55 ; + RECT 62.7 154.65 63.3 155.25 ; + RECT 60.6 169.35 61.2 169.95 ; + RECT 62.7 184.05 63.3 184.65 ; + RECT 60.6 196.5 61.2 197.1 ; + RECT 12.9 89.1 13.5 89.7 ; + RECT 15.0 105.0 15.6 105.6 ; + RECT 17.1 118.5 17.7 119.1 ; + RECT 19.2 134.4 19.8 135.0 ; + RECT 21.3 147.9 21.9 148.5 ; + RECT 23.4 163.8 24.0 164.4 ; + RECT 25.5 177.3 26.1 177.9 ; + RECT 27.6 193.2 28.2 193.8 ; + RECT 12.9 209.1 13.5 209.7 ; + RECT 21.3 205.8 21.9 206.4 ; + RECT 12.9 220.2 13.5 220.8 ; + RECT 23.4 223.5 24.0 224.1 ; + RECT 12.9 238.5 13.5 239.1 ; + RECT 25.5 235.2 26.1 235.8 ; + RECT 12.9 249.6 13.5 250.2 ; + RECT 27.6 252.9 28.2 253.5 ; + RECT 15.0 267.9 15.6 268.5 ; + RECT 21.3 264.6 21.9 265.2 ; + RECT 15.0 279.0 15.6 279.6 ; + RECT 23.4 282.3 24.0 282.9 ; + RECT 15.0 297.3 15.6 297.9 ; + RECT 25.5 294.0 26.1 294.6 ; + RECT 15.0 308.4 15.6 309.0 ; + RECT 27.6 311.7 28.2 312.3 ; + RECT 17.1 326.7 17.7 327.3 ; + RECT 21.3 323.4 21.9 324.0 ; + RECT 17.1 337.8 17.7 338.4 ; + RECT 23.4 341.1 24.0 341.7 ; + RECT 17.1 356.1 17.7 356.7 ; + RECT 25.5 352.8 26.1 353.4 ; + RECT 17.1 367.2 17.7 367.8 ; + RECT 27.6 370.5 28.2 371.1 ; + RECT 19.2 385.5 19.8 386.1 ; + RECT 21.3 382.2 21.9 382.8 ; + RECT 19.2 396.6 19.8 397.2 ; + RECT 23.4 399.9 24.0 400.5 ; + RECT 19.2 414.9 19.8 415.5 ; + RECT 25.5 411.6 26.1 412.2 ; + RECT 19.2 426.0 19.8 426.6 ; + RECT 27.6 429.3 28.2 429.9 ; + RECT 51.0 214.65 51.6 215.25 ; + RECT 57.3 214.65 57.9 215.25 ; + RECT 67.2 209.1 67.8 209.7 ; + RECT 51.3 209.1 51.9 209.7 ; + RECT 51.0 229.35 51.6 229.95 ; + RECT 57.3 229.35 57.9 229.95 ; + RECT 67.2 219.9 67.8 220.5 ; + RECT 51.3 219.9 51.9 220.5 ; + RECT 51.0 244.05 51.6 244.65 ; + RECT 57.3 244.05 57.9 244.65 ; + RECT 67.2 238.5 67.8 239.1 ; + RECT 51.3 238.5 51.9 239.1 ; + RECT 51.0 258.75 51.6 259.35 ; + RECT 57.3 258.75 57.9 259.35 ; + RECT 67.2 249.3 67.8 249.9 ; + RECT 51.3 249.3 51.9 249.9 ; + RECT 51.0 273.45 51.6 274.05 ; + RECT 57.3 273.45 57.9 274.05 ; + RECT 67.2 267.9 67.8 268.5 ; + RECT 51.3 267.9 51.9 268.5 ; + RECT 51.0 288.15 51.6 288.75 ; + RECT 57.3 288.15 57.9 288.75 ; + RECT 67.2 278.7 67.8 279.3 ; + RECT 51.3 278.7 51.9 279.3 ; + RECT 51.0 302.85 51.6 303.45 ; + RECT 57.3 302.85 57.9 303.45 ; + RECT 67.2 297.3 67.8 297.9 ; + RECT 51.3 297.3 51.9 297.9 ; + RECT 51.0 317.55 51.6 318.15 ; + RECT 57.3 317.55 57.9 318.15 ; + RECT 67.2 308.1 67.8 308.7 ; + RECT 51.3 308.1 51.9 308.7 ; + RECT 51.0 332.25 51.6 332.85 ; + RECT 57.3 332.25 57.9 332.85 ; + RECT 67.2 326.7 67.8 327.3 ; + RECT 51.3 326.7 51.9 327.3 ; + RECT 51.0 346.95 51.6 347.55 ; + RECT 57.3 346.95 57.9 347.55 ; + RECT 67.2 337.5 67.8 338.1 ; + RECT 51.3 337.5 51.9 338.1 ; + RECT 51.0 361.65 51.6 362.25 ; + RECT 57.3 361.65 57.9 362.25 ; + RECT 67.2 356.1 67.8 356.7 ; + RECT 51.3 356.1 51.9 356.7 ; + RECT 51.0 376.35 51.6 376.95 ; + RECT 57.3 376.35 57.9 376.95 ; + RECT 67.2 366.9 67.8 367.5 ; + RECT 51.3 366.9 51.9 367.5 ; + RECT 51.0 391.05 51.6 391.65 ; + RECT 57.3 391.05 57.9 391.65 ; + RECT 67.2 385.5 67.8 386.1 ; + RECT 51.3 385.5 51.9 386.1 ; + RECT 51.0 405.75 51.6 406.35 ; + RECT 57.3 405.75 57.9 406.35 ; + RECT 67.2 396.3 67.8 396.9 ; + RECT 51.3 396.3 51.9 396.9 ; + RECT 51.0 420.45 51.6 421.05 ; + RECT 57.3 420.45 57.9 421.05 ; + RECT 67.2 414.9 67.8 415.5 ; + RECT 51.3 414.9 51.9 415.5 ; + RECT 51.0 435.15 51.6 435.75 ; + RECT 57.3 435.15 57.9 435.75 ; + RECT 67.2 425.7 67.8 426.3 ; + RECT 51.3 425.7 51.9 426.3 ; + RECT 69.9 75.3 70.5 75.9 ; + RECT 65.4 72.9 66.0 73.5 ; + RECT 62.1 73.8 62.7 74.4 ; + RECT 61.5 69.3 62.1 69.9 ; + RECT 60.0 73.8 60.6 74.4 ; + RECT 59.1 77.1 59.7 77.7 ; + RECT 55.5 72.9 56.1 73.5 ; + RECT 53.1 72.9 53.7 73.5 ; + RECT 50.1 75.3 50.7 75.9 ; + RECT 46.8 74.4 47.4 75.0 ; + RECT 45.6 77.1 46.2 77.7 ; + RECT 39.9 72.9 40.5 73.5 ; + RECT 36.6 73.8 37.2 74.4 ; + RECT 36.0 69.3 36.6 69.9 ; + RECT 33.6 73.8 34.2 74.4 ; + RECT 32.7 77.1 33.3 77.7 ; + RECT 29.1 72.9 29.7 73.5 ; + RECT 26.7 72.9 27.3 73.5 ; + RECT 23.7 75.3 24.3 75.9 ; + RECT 69.9 63.3 70.5 63.9 ; + RECT 65.4 65.7 66.0 66.3 ; + RECT 62.1 64.8 62.7 65.4 ; + RECT 61.5 69.3 62.1 69.9 ; + RECT 60.0 64.8 60.6 65.4 ; + RECT 59.1 61.5 59.7 62.1 ; + RECT 55.5 65.7 56.1 66.3 ; + RECT 53.1 65.7 53.7 66.3 ; + RECT 50.1 63.3 50.7 63.9 ; + RECT 46.8 64.2 47.4 64.8 ; + RECT 45.6 61.5 46.2 62.1 ; + RECT 39.9 65.7 40.5 66.3 ; + RECT 36.6 64.8 37.2 65.4 ; + RECT 36.0 69.3 36.6 69.9 ; + RECT 33.6 64.8 34.2 65.4 ; + RECT 32.7 61.5 33.3 62.1 ; + RECT 29.1 65.7 29.7 66.3 ; + RECT 26.7 65.7 27.3 66.3 ; + RECT 23.7 63.3 24.3 63.9 ; + RECT 69.9 54.9 70.5 55.5 ; + RECT 65.4 52.5 66.0 53.1 ; + RECT 62.1 53.4 62.7 54.0 ; + RECT 61.5 48.9 62.1 49.5 ; + RECT 60.0 53.4 60.6 54.0 ; + RECT 59.1 56.7 59.7 57.3 ; + RECT 55.5 52.5 56.1 53.1 ; + RECT 53.1 52.5 53.7 53.1 ; + RECT 50.1 54.9 50.7 55.5 ; + RECT 46.8 54.0 47.4 54.6 ; + RECT 45.6 56.7 46.2 57.3 ; + RECT 39.9 52.5 40.5 53.1 ; + RECT 36.6 53.4 37.2 54.0 ; + RECT 36.0 48.9 36.6 49.5 ; + RECT 33.6 53.4 34.2 54.0 ; + RECT 32.7 56.7 33.3 57.3 ; + RECT 29.1 52.5 29.7 53.1 ; + RECT 26.7 52.5 27.3 53.1 ; + RECT 23.7 54.9 24.3 55.5 ; + RECT 69.9 42.9 70.5 43.5 ; + RECT 65.4 45.3 66.0 45.9 ; + RECT 62.1 44.4 62.7 45.0 ; + RECT 61.5 48.9 62.1 49.5 ; + RECT 60.0 44.4 60.6 45.0 ; + RECT 59.1 41.1 59.7 41.7 ; + RECT 55.5 45.3 56.1 45.9 ; + RECT 53.1 45.3 53.7 45.9 ; + RECT 50.1 42.9 50.7 43.5 ; + RECT 46.8 43.8 47.4 44.4 ; + RECT 45.6 41.1 46.2 41.7 ; + RECT 39.9 45.3 40.5 45.9 ; + RECT 36.6 44.4 37.2 45.0 ; + RECT 36.0 48.9 36.6 49.5 ; + RECT 33.6 44.4 34.2 45.0 ; + RECT 32.7 41.1 33.3 41.7 ; + RECT 29.1 45.3 29.7 45.9 ; + RECT 26.7 45.3 27.3 45.9 ; + RECT 23.7 42.9 24.3 43.5 ; + RECT 90.9 89.1 91.5 89.7 ; + RECT 88.2 105.0 88.8 105.6 ; + RECT 85.5 147.9 86.1 148.5 ; + RECT 82.8 163.8 83.4 164.4 ; + RECT 114.3 33.6 114.9 34.2 ; + RECT 108.9 28.95 109.5 29.55 ; + RECT 111.6 26.55 112.2 27.15 ; + RECT 114.3 446.7 114.9 447.3 ; + RECT 117.0 98.25 117.6 98.85 ; + RECT 119.7 196.35 120.3 196.95 ; + RECT 16.8 76.8 17.4 77.4 ; + RECT 106.2 456.9 106.8 457.5 ; + RECT 95.4 437.4 96.0 438.0 ; + RECT 96.6 437.4 97.2 438.0 ; + RECT 134.1 437.4 134.7 438.0 ; + RECT 144.3 437.4 144.9 438.0 ; + RECT 123.0 437.4 123.6 438.0 ; + RECT 102.0 8.4 102.6 9.0 ; + RECT 103.2 8.4 103.8 9.0 ; + RECT 133.8 8.4 134.4 9.0 ; + RECT 133.8 8.4 134.4 9.0 ; + RECT 88.05 229.35 88.65 229.95 ; + RECT 88.05 258.75 88.65 259.35 ; + RECT 88.05 288.15 88.65 288.75 ; + RECT 88.05 317.55 88.65 318.15 ; + RECT 88.05 346.95 88.65 347.55 ; + RECT 88.05 376.35 88.65 376.95 ; + RECT 88.05 405.75 88.65 406.35 ; + RECT 88.05 435.15 88.65 435.75 ; + RECT 95.4 140.85 96.0 141.45 ; + RECT 96.6 140.85 97.2 141.45 ; + RECT 95.4 199.65 96.0 200.25 ; + RECT 96.6 199.65 97.2 200.25 ; + RECT 75.3 69.45 75.9 70.05 ; + RECT 95.4 69.45 96.0 70.05 ; + RECT 96.6 69.45 97.2 70.05 ; + RECT 75.3 69.45 75.9 70.05 ; + RECT 95.4 69.45 96.0 70.05 ; + RECT 96.6 69.45 97.2 70.05 ; + RECT 75.3 49.05 75.9 49.65 ; + RECT 95.4 49.05 96.0 49.65 ; + RECT 96.6 49.05 97.2 49.65 ; + RECT 75.3 49.05 75.9 49.65 ; + RECT 95.4 49.05 96.0 49.65 ; + RECT 96.6 49.05 97.2 49.65 ; + RECT -62.4 140.1 -61.8 140.7 ; + RECT -60.0 135.6 -59.4 136.2 ; + RECT -60.9 132.3 -60.3 132.9 ; + RECT -56.4 131.7 -55.8 132.3 ; + RECT -60.9 130.2 -60.3 130.8 ; + RECT -64.2 129.3 -63.6 129.9 ; + RECT -60.0 125.7 -59.4 126.3 ; + RECT -60.0 123.3 -59.4 123.9 ; + RECT -62.4 120.3 -61.8 120.9 ; + RECT -61.5 117.0 -60.9 117.6 ; + RECT -64.2 115.8 -63.6 116.4 ; + RECT -60.0 110.1 -59.4 110.7 ; + RECT -60.9 106.8 -60.3 107.4 ; + RECT -56.4 106.2 -55.8 106.8 ; + RECT -60.9 103.8 -60.3 104.4 ; + RECT -64.2 102.9 -63.6 103.5 ; + RECT -60.0 99.3 -59.4 99.9 ; + RECT -60.0 96.9 -59.4 97.5 ; + RECT -62.4 93.9 -61.8 94.5 ; + RECT -50.4 140.1 -49.8 140.7 ; + RECT -52.8 135.6 -52.2 136.2 ; + RECT -51.9 132.3 -51.3 132.9 ; + RECT -56.4 131.7 -55.8 132.3 ; + RECT -51.9 130.2 -51.3 130.8 ; + RECT -48.6 129.3 -48.0 129.9 ; + RECT -52.8 125.7 -52.2 126.3 ; + RECT -52.8 123.3 -52.2 123.9 ; + RECT -50.4 120.3 -49.8 120.9 ; + RECT -51.3 117.0 -50.7 117.6 ; + RECT -48.6 115.8 -48.0 116.4 ; + RECT -52.8 110.1 -52.2 110.7 ; + RECT -51.9 106.8 -51.3 107.4 ; + RECT -56.4 106.2 -55.8 106.8 ; + RECT -51.9 103.8 -51.3 104.4 ; + RECT -48.6 102.9 -48.0 103.5 ; + RECT -52.8 99.3 -52.2 99.9 ; + RECT -52.8 96.9 -52.2 97.5 ; + RECT -50.4 93.9 -49.8 94.5 ; + RECT -42.0 140.1 -41.4 140.7 ; + RECT -39.6 135.6 -39.0 136.2 ; + RECT -40.5 132.3 -39.9 132.9 ; + RECT -36.0 131.7 -35.4 132.3 ; + RECT -40.5 130.2 -39.9 130.8 ; + RECT -43.8 129.3 -43.2 129.9 ; + RECT -39.6 125.7 -39.0 126.3 ; + RECT -39.6 123.3 -39.0 123.9 ; + RECT -42.0 120.3 -41.4 120.9 ; + RECT -41.1 117.0 -40.5 117.6 ; + RECT -43.8 115.8 -43.2 116.4 ; + RECT -39.6 110.1 -39.0 110.7 ; + RECT -40.5 106.8 -39.9 107.4 ; + RECT -36.0 106.2 -35.4 106.8 ; + RECT -40.5 103.8 -39.9 104.4 ; + RECT -43.8 102.9 -43.2 103.5 ; + RECT -39.6 99.3 -39.0 99.9 ; + RECT -39.6 96.9 -39.0 97.5 ; + RECT -42.0 93.9 -41.4 94.5 ; + RECT -19.35 100.2 -18.75 100.8 ; + RECT -13.95 100.2 -13.35 100.8 ; + RECT -19.35 95.4 -18.75 96.0 ; + RECT -13.95 95.4 -13.35 96.0 ; + RECT -14.55 266.1 -13.95 266.7 ; + RECT -14.55 256.5 -13.95 257.1 ; + RECT -31.35 248.1 -30.75 248.7 ; + RECT -31.35 257.7 -30.75 258.3 ; + RECT -14.55 260.7 -13.95 261.3 ; + RECT -14.55 251.1 -13.95 251.7 ; + RECT -31.35 253.5 -30.75 254.1 ; + RECT -53.25 263.7 -52.65 264.3 ; + RECT -62.25 263.7 -61.65 264.3 ; + RECT -54.15 254.4 -53.55 255.0 ; + RECT -58.35 254.4 -57.75 255.0 ; + RECT -53.25 243.9 -52.65 244.5 ; + RECT -62.25 243.9 -61.65 244.5 ; + RECT -54.15 253.2 -53.55 253.8 ; + RECT -58.35 253.2 -57.75 253.8 ; + RECT -53.25 234.3 -52.65 234.9 ; + RECT -62.25 234.3 -61.65 234.9 ; + RECT -54.15 225.0 -53.55 225.6 ; + RECT -58.35 225.0 -57.75 225.6 ; + RECT -42.75 235.65 -42.15 236.25 ; + RECT -32.55 238.2 -31.95 238.8 ; + RECT -44.85 238.35 -44.25 238.95 ; + RECT -55.65 271.2 -55.05 271.8 ; + RECT -13.5 220.65 -12.9 221.25 ; + RECT -31.95 220.8 -31.35 221.4 ; + RECT -32.55 271.5 -31.95 272.1 ; + RECT -13.5 239.85 -12.9 240.45 ; + RECT -49.95 249.9 -49.35 250.5 ; + RECT -45.75 244.8 -45.15 245.4 ; + RECT -45.75 264.9 -45.15 265.5 ; + RECT -8.25 230.1 -7.65 230.7 ; + RECT -8.25 172.35 -7.65 172.95 ; + RECT -37.65 172.5 -37.05 173.1 ; + RECT -46.35 292.95 -45.75 293.55 ; + RECT -43.05 267.15 -42.45 267.75 ; + RECT -43.05 277.65 -42.45 278.25 ; + RECT -45.75 316.65 -45.15 317.25 ; + RECT -43.05 267.15 -42.45 267.75 ; + RECT -43.05 253.65 -42.45 254.25 ; + RECT -45.75 344.85 -45.15 345.45 ; + RECT -62.25 230.1 -61.65 230.7 ; + RECT -52.05 230.1 -51.45 230.7 ; + RECT -25.95 170.1 -25.35 170.7 ; + RECT -25.95 174.9 -25.35 175.5 ; + RECT -32.7 171.0 -32.1 171.6 ; + RECT -33.6 165.6 -33.0 166.2 ; + RECT -32.7 173.4 -32.1 174.0 ; + RECT -35.7 165.6 -35.1 166.2 ; + RECT -49.65 170.1 -49.05 170.7 ; + RECT -49.65 174.9 -49.05 175.5 ; + RECT -42.9 171.0 -42.3 171.6 ; + RECT -42.0 165.6 -41.4 166.2 ; + RECT -42.9 173.4 -42.3 174.0 ; + RECT -39.9 165.6 -39.3 166.2 ; + RECT -40.5 144.6 -39.9 145.2 ; + RECT -40.5 151.2 -39.9 151.8 ; + RECT -43.95 144.6 -43.35 145.2 ; + RECT -43.95 153.9 -43.35 154.5 ; + RECT -64.35 144.6 -63.75 145.2 ; + RECT -64.35 156.6 -63.75 157.2 ; + RECT -48.75 144.6 -48.15 145.2 ; + RECT -48.75 159.3 -48.15 159.9 ; + RECT -17.1 153.9 -16.5 154.5 ; + RECT -13.8 162.0 -13.2 162.6 ; + RECT -27.15 162.0 -26.55 162.6 ; + RECT -32.7 153.9 -32.1 154.5 ; + RECT -34.5 156.6 -33.9 157.2 ; + RECT -47.55 162.0 -46.95 162.6 ; + RECT -42.0 159.3 -41.4 159.9 ; + RECT -40.2 156.6 -39.6 157.2 ; + RECT -15.9 121.2 -15.3 121.8 ; + RECT -5.7 121.2 -5.1 121.8 ; + RECT -5.55 151.05 -4.95 151.65 ; + RECT -30.6 214.5 -30.0 215.1 ; + RECT -44.1 211.8 -43.5 212.4 ; + RECT -14.7 217.2 -14.1 217.8 ; + RECT -3.9 214.35 -3.3 214.95 ; + RECT -22.65 219.9 -22.05 220.5 ; + RECT -52.05 219.9 -51.45 220.5 ; + RECT -7.95 148.5 -7.35 149.1 ; + RECT -14.55 126.6 -13.95 127.2 ; + RECT -27.6 126.6 -27.0 127.2 ; + RECT -27.3 85.8 -26.7 86.4 ; + RECT -13.8 88.8 -13.2 89.4 ; + RECT -14.7 175.8 -14.1 176.4 ; + RECT -14.7 180.3 -14.1 180.9 ; + RECT 0.3 219.9 0.9 220.5 ; + RECT 1.5 219.9 2.1 220.5 ; + Layer metal2 ; + RECT -7.5 219.6 0.0 220.5 ; + RECT 95.1 0.0 104.1 459.3 ; + RECT 119.4 0.0 120.3 459.3 ; + RECT 116.7 0.0 117.6 459.3 ; + RECT 114.0 0.0 114.9 459.3 ; + RECT 111.3 0.0 112.2 459.3 ; + RECT 108.6 0.0 109.5 459.3 ; + RECT 105.9 0.0 106.8 459.3 ; + RECT 90.6 39.0 91.5 196.5 ; + RECT 87.9 39.0 88.8 196.5 ; + RECT 85.2 39.0 86.1 196.5 ; + RECT 82.5 39.0 83.4 196.5 ; + RECT 127.05 435.3 127.95 439.8 ; + RECT 130.05 435.3 130.95 439.8 ; + RECT 137.25 435.3 138.15 439.8 ; + RECT 140.25 435.3 141.15 439.8 ; + RECT 114.0 200.1 114.9 448.2 ; + RECT 133.65 435.3 134.55 438.0 ; + RECT 143.85 435.3 144.75 438.0 ; + RECT 123.0 200.1 123.9 438.0 ; + RECT 88.5 229.05 95.1 229.95 ; + RECT 88.5 258.45 95.1 259.35 ; + RECT 88.5 287.85 95.1 288.75 ; + RECT 88.5 317.25 95.1 318.15 ; + RECT 88.5 346.65 95.1 347.55 ; + RECT 88.5 376.05 95.1 376.95 ; + RECT 88.5 405.45 95.1 406.35 ; + RECT 88.5 434.85 95.1 435.75 ; + RECT 123.3 204.3 125.7 214.8 ; + RECT 126.9 201.3 128.1 214.8 ; + RECT 129.9 201.3 131.1 214.8 ; + RECT 125.4 200.1 128.1 201.3 ; + RECT 129.6 200.1 131.1 201.3 ; + RECT 133.5 200.1 134.7 214.8 ; + RECT 123.3 214.8 125.7 225.3 ; + RECT 126.9 214.8 128.1 228.3 ; + RECT 129.9 214.8 131.1 228.3 ; + RECT 125.4 228.3 128.1 229.5 ; + RECT 129.6 228.3 131.1 229.5 ; + RECT 133.5 214.8 134.7 229.5 ; + RECT 123.3 233.7 125.7 244.2 ; + RECT 126.9 230.7 128.1 244.2 ; + RECT 129.9 230.7 131.1 244.2 ; + RECT 125.4 229.5 128.1 230.7 ; + RECT 129.6 229.5 131.1 230.7 ; + RECT 133.5 229.5 134.7 244.2 ; + RECT 123.3 244.2 125.7 254.7 ; + RECT 126.9 244.2 128.1 257.7 ; + RECT 129.9 244.2 131.1 257.7 ; + RECT 125.4 257.7 128.1 258.9 ; + RECT 129.6 257.7 131.1 258.9 ; + RECT 133.5 244.2 134.7 258.9 ; + RECT 123.3 263.1 125.7 273.6 ; + RECT 126.9 260.1 128.1 273.6 ; + RECT 129.9 260.1 131.1 273.6 ; + RECT 125.4 258.9 128.1 260.1 ; + RECT 129.6 258.9 131.1 260.1 ; + RECT 133.5 258.9 134.7 273.6 ; + RECT 123.3 273.6 125.7 284.1 ; + RECT 126.9 273.6 128.1 287.1 ; + RECT 129.9 273.6 131.1 287.1 ; + RECT 125.4 287.1 128.1 288.3 ; + RECT 129.6 287.1 131.1 288.3 ; + RECT 133.5 273.6 134.7 288.3 ; + RECT 123.3 292.5 125.7 303.0 ; + RECT 126.9 289.5 128.1 303.0 ; + RECT 129.9 289.5 131.1 303.0 ; + RECT 125.4 288.3 128.1 289.5 ; + RECT 129.6 288.3 131.1 289.5 ; + RECT 133.5 288.3 134.7 303.0 ; + RECT 123.3 303.0 125.7 313.5 ; + RECT 126.9 303.0 128.1 316.5 ; + RECT 129.9 303.0 131.1 316.5 ; + RECT 125.4 316.5 128.1 317.7 ; + RECT 129.6 316.5 131.1 317.7 ; + RECT 133.5 303.0 134.7 317.7 ; + RECT 123.3 321.9 125.7 332.4 ; + RECT 126.9 318.9 128.1 332.4 ; + RECT 129.9 318.9 131.1 332.4 ; + RECT 125.4 317.7 128.1 318.9 ; + RECT 129.6 317.7 131.1 318.9 ; + RECT 133.5 317.7 134.7 332.4 ; + RECT 123.3 332.4 125.7 342.9 ; + RECT 126.9 332.4 128.1 345.9 ; + RECT 129.9 332.4 131.1 345.9 ; + RECT 125.4 345.9 128.1 347.1 ; + RECT 129.6 345.9 131.1 347.1 ; + RECT 133.5 332.4 134.7 347.1 ; + RECT 123.3 351.3 125.7 361.8 ; + RECT 126.9 348.3 128.1 361.8 ; + RECT 129.9 348.3 131.1 361.8 ; + RECT 125.4 347.1 128.1 348.3 ; + RECT 129.6 347.1 131.1 348.3 ; + RECT 133.5 347.1 134.7 361.8 ; + RECT 123.3 361.8 125.7 372.3 ; + RECT 126.9 361.8 128.1 375.3 ; + RECT 129.9 361.8 131.1 375.3 ; + RECT 125.4 375.3 128.1 376.5 ; + RECT 129.6 375.3 131.1 376.5 ; + RECT 133.5 361.8 134.7 376.5 ; + RECT 123.3 380.7 125.7 391.2 ; + RECT 126.9 377.7 128.1 391.2 ; + RECT 129.9 377.7 131.1 391.2 ; + RECT 125.4 376.5 128.1 377.7 ; + RECT 129.6 376.5 131.1 377.7 ; + RECT 133.5 376.5 134.7 391.2 ; + RECT 123.3 391.2 125.7 401.7 ; + RECT 126.9 391.2 128.1 404.7 ; + RECT 129.9 391.2 131.1 404.7 ; + RECT 125.4 404.7 128.1 405.9 ; + RECT 129.6 404.7 131.1 405.9 ; + RECT 133.5 391.2 134.7 405.9 ; + RECT 123.3 410.1 125.7 420.6 ; + RECT 126.9 407.1 128.1 420.6 ; + RECT 129.9 407.1 131.1 420.6 ; + RECT 125.4 405.9 128.1 407.1 ; + RECT 129.6 405.9 131.1 407.1 ; + RECT 133.5 405.9 134.7 420.6 ; + RECT 123.3 420.6 125.7 431.1 ; + RECT 126.9 420.6 128.1 434.1 ; + RECT 129.9 420.6 131.1 434.1 ; + RECT 125.4 434.1 128.1 435.3 ; + RECT 129.6 434.1 131.1 435.3 ; + RECT 133.5 420.6 134.7 435.3 ; + RECT 133.5 204.3 135.9 214.8 ; + RECT 137.1 201.3 138.3 214.8 ; + RECT 140.1 201.3 141.3 214.8 ; + RECT 135.6 200.1 138.3 201.3 ; + RECT 139.8 200.1 141.3 201.3 ; + RECT 143.7 200.1 144.9 214.8 ; + RECT 133.5 214.8 135.9 225.3 ; + RECT 137.1 214.8 138.3 228.3 ; + RECT 140.1 214.8 141.3 228.3 ; + RECT 135.6 228.3 138.3 229.5 ; + RECT 139.8 228.3 141.3 229.5 ; + RECT 143.7 214.8 144.9 229.5 ; + RECT 133.5 233.7 135.9 244.2 ; + RECT 137.1 230.7 138.3 244.2 ; + RECT 140.1 230.7 141.3 244.2 ; + RECT 135.6 229.5 138.3 230.7 ; + RECT 139.8 229.5 141.3 230.7 ; + RECT 143.7 229.5 144.9 244.2 ; + RECT 133.5 244.2 135.9 254.7 ; + RECT 137.1 244.2 138.3 257.7 ; + RECT 140.1 244.2 141.3 257.7 ; + RECT 135.6 257.7 138.3 258.9 ; + RECT 139.8 257.7 141.3 258.9 ; + RECT 143.7 244.2 144.9 258.9 ; + RECT 133.5 263.1 135.9 273.6 ; + RECT 137.1 260.1 138.3 273.6 ; + RECT 140.1 260.1 141.3 273.6 ; + RECT 135.6 258.9 138.3 260.1 ; + RECT 139.8 258.9 141.3 260.1 ; + RECT 143.7 258.9 144.9 273.6 ; + RECT 133.5 273.6 135.9 284.1 ; + RECT 137.1 273.6 138.3 287.1 ; + RECT 140.1 273.6 141.3 287.1 ; + RECT 135.6 287.1 138.3 288.3 ; + RECT 139.8 287.1 141.3 288.3 ; + RECT 143.7 273.6 144.9 288.3 ; + RECT 133.5 292.5 135.9 303.0 ; + RECT 137.1 289.5 138.3 303.0 ; + RECT 140.1 289.5 141.3 303.0 ; + RECT 135.6 288.3 138.3 289.5 ; + RECT 139.8 288.3 141.3 289.5 ; + RECT 143.7 288.3 144.9 303.0 ; + RECT 133.5 303.0 135.9 313.5 ; + RECT 137.1 303.0 138.3 316.5 ; + RECT 140.1 303.0 141.3 316.5 ; + RECT 135.6 316.5 138.3 317.7 ; + RECT 139.8 316.5 141.3 317.7 ; + RECT 143.7 303.0 144.9 317.7 ; + RECT 133.5 321.9 135.9 332.4 ; + RECT 137.1 318.9 138.3 332.4 ; + RECT 140.1 318.9 141.3 332.4 ; + RECT 135.6 317.7 138.3 318.9 ; + RECT 139.8 317.7 141.3 318.9 ; + RECT 143.7 317.7 144.9 332.4 ; + RECT 133.5 332.4 135.9 342.9 ; + RECT 137.1 332.4 138.3 345.9 ; + RECT 140.1 332.4 141.3 345.9 ; + RECT 135.6 345.9 138.3 347.1 ; + RECT 139.8 345.9 141.3 347.1 ; + RECT 143.7 332.4 144.9 347.1 ; + RECT 133.5 351.3 135.9 361.8 ; + RECT 137.1 348.3 138.3 361.8 ; + RECT 140.1 348.3 141.3 361.8 ; + RECT 135.6 347.1 138.3 348.3 ; + RECT 139.8 347.1 141.3 348.3 ; + RECT 143.7 347.1 144.9 361.8 ; + RECT 133.5 361.8 135.9 372.3 ; + RECT 137.1 361.8 138.3 375.3 ; + RECT 140.1 361.8 141.3 375.3 ; + RECT 135.6 375.3 138.3 376.5 ; + RECT 139.8 375.3 141.3 376.5 ; + RECT 143.7 361.8 144.9 376.5 ; + RECT 133.5 380.7 135.9 391.2 ; + RECT 137.1 377.7 138.3 391.2 ; + RECT 140.1 377.7 141.3 391.2 ; + RECT 135.6 376.5 138.3 377.7 ; + RECT 139.8 376.5 141.3 377.7 ; + RECT 143.7 376.5 144.9 391.2 ; + RECT 133.5 391.2 135.9 401.7 ; + RECT 137.1 391.2 138.3 404.7 ; + RECT 140.1 391.2 141.3 404.7 ; + RECT 135.6 404.7 138.3 405.9 ; + RECT 139.8 404.7 141.3 405.9 ; + RECT 143.7 391.2 144.9 405.9 ; + RECT 133.5 410.1 135.9 420.6 ; + RECT 137.1 407.1 138.3 420.6 ; + RECT 140.1 407.1 141.3 420.6 ; + RECT 135.6 405.9 138.3 407.1 ; + RECT 139.8 405.9 141.3 407.1 ; + RECT 143.7 405.9 144.9 420.6 ; + RECT 133.5 420.6 135.9 431.1 ; + RECT 137.1 420.6 138.3 434.1 ; + RECT 140.1 420.6 141.3 434.1 ; + RECT 135.6 434.1 138.3 435.3 ; + RECT 139.8 434.1 141.3 435.3 ; + RECT 143.7 420.6 144.9 435.3 ; + RECT 127.05 439.8 127.95 459.3 ; + RECT 130.05 439.8 130.95 459.3 ; + RECT 127.35 450.9 127.5 452.1 ; + RECT 130.05 450.9 131.7 452.1 ; + RECT 127.35 442.5 127.5 443.7 ; + RECT 129.3 442.5 130.05 443.7 ; + RECT 126.9 450.9 128.1 452.1 ; + RECT 131.7 450.9 132.9 452.1 ; + RECT 126.9 442.5 128.1 443.7 ; + RECT 129.3 442.5 130.5 443.7 ; + RECT 137.25 439.8 138.15 459.3 ; + RECT 140.25 439.8 141.15 459.3 ; + RECT 137.55 450.9 137.7 452.1 ; + RECT 140.25 450.9 141.9 452.1 ; + RECT 137.55 442.5 137.7 443.7 ; + RECT 139.5 442.5 140.25 443.7 ; + RECT 137.1 450.9 138.3 452.1 ; + RECT 141.9 450.9 143.1 452.1 ; + RECT 137.1 442.5 138.3 443.7 ; + RECT 139.5 442.5 140.7 443.7 ; + RECT 126.9 165.6 128.1 200.1 ; + RECT 129.9 165.6 131.1 200.1 ; + RECT 133.5 192.6 134.7 200.1 ; + RECT 126.9 164.4 129.0 165.6 ; + RECT 129.9 164.4 131.7 165.6 ; + RECT 124.8 151.2 126.0 155.7 ; + RECT 126.9 151.2 128.1 164.4 ; + RECT 129.9 151.2 131.1 164.4 ; + RECT 137.1 165.6 138.3 200.1 ; + RECT 140.1 165.6 141.3 200.1 ; + RECT 143.7 192.6 144.9 200.1 ; + RECT 137.1 164.4 139.2 165.6 ; + RECT 140.1 164.4 141.9 165.6 ; + RECT 135.0 151.2 136.2 155.7 ; + RECT 137.1 151.2 138.3 164.4 ; + RECT 140.1 151.2 141.3 164.4 ; + RECT 126.9 149.1 128.1 151.2 ; + RECT 125.4 147.9 128.1 149.1 ; + RECT 129.9 143.7 131.1 151.2 ; + RECT 133.5 138.9 134.7 149.4 ; + RECT 127.2 137.7 134.7 138.9 ; + RECT 126.3 99.9 127.5 127.2 ; + RECT 133.5 116.4 134.7 137.7 ; + RECT 132.9 115.2 134.7 116.4 ; + RECT 133.5 112.2 134.7 115.2 ; + RECT 129.6 111.0 134.7 112.2 ; + RECT 129.6 109.8 130.8 111.0 ; + RECT 127.5 93.6 129.9 94.8 ; + RECT 128.4 90.9 129.6 93.6 ; + RECT 133.5 90.9 134.7 111.0 ; + RECT 137.1 149.1 138.3 151.2 ; + RECT 135.6 147.9 138.3 149.1 ; + RECT 140.1 143.7 141.3 151.2 ; + RECT 143.7 138.9 144.9 149.4 ; + RECT 137.4 137.7 144.9 138.9 ; + RECT 136.5 99.9 137.7 127.2 ; + RECT 143.7 116.4 144.9 137.7 ; + RECT 143.1 115.2 144.9 116.4 ; + RECT 143.7 112.2 144.9 115.2 ; + RECT 139.8 111.0 144.9 112.2 ; + RECT 139.8 109.8 141.0 111.0 ; + RECT 137.7 93.6 140.1 94.8 ; + RECT 138.6 90.9 139.8 93.6 ; + RECT 143.7 90.9 144.9 111.0 ; + RECT 125.7 86.4 126.9 90.9 ; + RECT 128.4 89.7 129.6 90.9 ; + RECT 128.4 88.5 131.1 89.7 ; + RECT 125.7 85.2 128.7 86.4 ; + RECT 125.7 75.6 126.6 85.2 ; + RECT 129.9 80.7 131.1 88.5 ; + RECT 129.0 77.4 132.0 78.6 ; + RECT 125.7 74.4 126.9 75.6 ; + RECT 129.0 75.3 130.2 76.5 ; + RECT 129.3 73.8 130.2 75.3 ; + RECT 127.8 72.9 130.2 73.8 ; + RECT 127.8 66.6 128.7 72.9 ; + RECT 131.1 72.0 132.0 77.4 ; + RECT 129.9 70.8 132.0 72.0 ; + RECT 129.9 68.4 132.0 69.6 ; + RECT 127.5 65.4 128.7 66.6 ; + RECT 125.4 60.9 126.9 62.1 ; + RECT 125.4 49.2 126.3 60.9 ; + RECT 128.4 58.8 129.6 63.3 ; + RECT 127.2 57.9 129.6 58.8 ; + RECT 127.2 51.0 128.1 57.9 ; + RECT 131.1 56.4 132.0 68.4 ; + RECT 129.9 55.2 132.0 56.4 ; + RECT 129.0 51.9 132.0 53.1 ; + RECT 127.2 50.1 128.7 51.0 ; + RECT 125.4 48.0 126.9 49.2 ; + RECT 127.8 48.9 130.2 50.1 ; + RECT 127.8 40.2 128.7 48.9 ; + RECT 131.1 45.6 132.0 51.9 ; + RECT 129.9 44.4 132.0 45.6 ; + RECT 129.9 42.0 132.0 43.2 ; + RECT 127.5 39.0 128.7 40.2 ; + RECT 131.1 33.3 132.0 42.0 ; + RECT 128.4 32.1 132.0 33.3 ; + RECT 128.4 30.9 129.6 32.1 ; + RECT 133.5 30.9 134.7 90.9 ; + RECT 141.3 86.4 142.5 90.9 ; + RECT 138.6 89.7 139.8 90.9 ; + RECT 137.1 88.5 139.8 89.7 ; + RECT 139.5 85.2 142.5 86.4 ; + RECT 141.6 75.6 142.5 85.2 ; + RECT 137.1 80.7 138.3 88.5 ; + RECT 136.2 77.4 139.2 78.6 ; + RECT 141.3 74.4 142.5 75.6 ; + RECT 138.0 75.3 139.2 76.5 ; + RECT 138.0 73.8 138.9 75.3 ; + RECT 138.0 72.9 140.4 73.8 ; + RECT 139.5 66.6 140.4 72.9 ; + RECT 136.2 72.0 137.1 77.4 ; + RECT 136.2 70.8 138.3 72.0 ; + RECT 136.2 68.4 138.3 69.6 ; + RECT 139.5 65.4 140.7 66.6 ; + RECT 141.3 60.9 142.8 62.1 ; + RECT 141.9 49.2 142.8 60.9 ; + RECT 138.6 58.8 139.8 63.3 ; + RECT 138.6 57.9 141.0 58.8 ; + RECT 140.1 51.0 141.0 57.9 ; + RECT 136.2 56.4 137.1 68.4 ; + RECT 136.2 55.2 138.3 56.4 ; + RECT 136.2 51.9 139.2 53.1 ; + RECT 139.5 50.1 141.0 51.0 ; + RECT 141.3 48.0 142.8 49.2 ; + RECT 138.0 48.9 140.4 50.1 ; + RECT 139.5 40.2 140.4 48.9 ; + RECT 136.2 45.6 137.1 51.9 ; + RECT 136.2 44.4 138.3 45.6 ; + RECT 136.2 42.0 138.3 43.2 ; + RECT 139.5 39.0 140.7 40.2 ; + RECT 136.2 33.3 137.1 42.0 ; + RECT 136.2 32.1 139.8 33.3 ; + RECT 138.6 30.9 139.8 32.1 ; + RECT 133.5 30.9 134.7 90.9 ; + RECT 128.4 45.9 129.6 52.8 ; + RECT 126.6 44.7 129.6 45.9 ; + RECT 128.4 41.1 132.6 42.3 ; + RECT 128.4 33.6 129.6 41.1 ; + RECT 128.4 32.4 129.9 33.6 ; + RECT 128.4 30.9 129.6 32.4 ; + RECT 133.5 30.9 134.7 52.8 ; + RECT 138.6 45.9 139.8 52.8 ; + RECT 138.6 44.7 141.6 45.9 ; + RECT 135.6 41.1 139.8 42.3 ; + RECT 138.6 33.6 139.8 41.1 ; + RECT 138.3 32.4 139.8 33.6 ; + RECT 138.6 30.9 139.8 32.4 ; + RECT 133.5 30.9 134.7 52.8 ; + RECT 27.3 82.5 28.2 435.3 ; + RECT 25.2 82.5 26.1 435.3 ; + RECT 23.1 82.5 24.0 435.3 ; + RECT 21.0 82.5 21.9 435.3 ; + RECT 18.9 82.5 19.8 435.3 ; + RECT 16.8 82.5 17.7 435.3 ; + RECT 14.7 82.5 15.6 435.3 ; + RECT 12.6 82.5 13.5 435.3 ; + RECT 77.7 82.5 78.6 138.6 ; + RECT 75.6 82.5 76.5 138.6 ; + RECT 62.7 82.5 63.6 138.6 ; + RECT 60.6 82.5 61.5 138.6 ; + RECT 58.5 82.5 59.4 138.6 ; + RECT 56.4 82.5 57.3 138.6 ; + RECT 54.3 82.5 55.2 138.6 ; + RECT 52.2 82.5 53.1 138.6 ; + RECT 58.2 92.85 59.4 94.05 ; + RECT 77.4 87.6 78.6 88.8 ; + RECT 56.1 98.25 57.3 99.45 ; + RECT 75.3 103.5 76.5 104.7 ; + RECT 58.2 90.0 59.4 91.2 ; + RECT 56.1 86.7 57.3 87.9 ; + RECT 54.0 101.1 55.2 102.3 ; + RECT 56.1 104.4 57.3 105.6 ; + RECT 58.2 119.4 59.4 120.6 ; + RECT 51.9 116.1 53.1 117.3 ; + RECT 54.0 130.5 55.2 131.7 ; + RECT 77.4 130.5 78.6 131.7 ; + RECT 51.9 133.8 53.1 135.0 ; + RECT 75.3 133.8 76.5 135.0 ; + RECT 60.3 80.85 61.5 82.05 ; + RECT 62.4 95.55 63.6 96.75 ; + RECT 60.3 110.25 61.5 111.45 ; + RECT 62.4 124.95 63.6 126.15 ; + RECT 60.3 137.4 61.5 138.6 ; + RECT 77.7 141.3 78.6 197.4 ; + RECT 75.6 141.3 76.5 197.4 ; + RECT 62.7 141.3 63.6 197.4 ; + RECT 60.6 141.3 61.5 197.4 ; + RECT 58.5 141.3 59.4 197.4 ; + RECT 56.4 141.3 57.3 197.4 ; + RECT 54.3 141.3 55.2 197.4 ; + RECT 52.2 141.3 53.1 197.4 ; + RECT 58.2 151.65 59.4 152.85 ; + RECT 77.4 146.4 78.6 147.6 ; + RECT 56.1 157.05 57.3 158.25 ; + RECT 75.3 162.3 76.5 163.5 ; + RECT 58.2 148.8 59.4 150.0 ; + RECT 56.1 145.5 57.3 146.7 ; + RECT 54.0 159.9 55.2 161.1 ; + RECT 56.1 163.2 57.3 164.4 ; + RECT 58.2 178.2 59.4 179.4 ; + RECT 51.9 174.9 53.1 176.1 ; + RECT 54.0 189.3 55.2 190.5 ; + RECT 77.4 189.3 78.6 190.5 ; + RECT 51.9 192.6 53.1 193.8 ; + RECT 75.3 192.6 76.5 193.8 ; + RECT 60.3 139.65 61.5 140.85 ; + RECT 62.4 154.35 63.6 155.55 ; + RECT 60.3 169.05 61.5 170.25 ; + RECT 62.4 183.75 63.6 184.95 ; + RECT 60.3 196.2 61.5 197.4 ; + RECT 12.6 88.8 13.8 90.0 ; + RECT 14.7 104.7 15.9 105.9 ; + RECT 16.8 118.2 18.0 119.4 ; + RECT 18.9 134.1 20.1 135.3 ; + RECT 21.0 147.6 22.2 148.8 ; + RECT 23.1 163.5 24.3 164.7 ; + RECT 25.2 177.0 26.4 178.2 ; + RECT 27.3 192.9 28.5 194.1 ; + RECT 12.6 208.8 13.8 210.0 ; + RECT 21.0 205.5 22.2 206.7 ; + RECT 12.6 219.9 13.8 221.1 ; + RECT 23.1 223.2 24.3 224.4 ; + RECT 12.6 238.2 13.8 239.4 ; + RECT 25.2 234.9 26.4 236.1 ; + RECT 12.6 249.3 13.8 250.5 ; + RECT 27.3 252.6 28.5 253.8 ; + RECT 14.7 267.6 15.9 268.8 ; + RECT 21.0 264.3 22.2 265.5 ; + RECT 14.7 278.7 15.9 279.9 ; + RECT 23.1 282.0 24.3 283.2 ; + RECT 14.7 297.0 15.9 298.2 ; + RECT 25.2 293.7 26.4 294.9 ; + RECT 14.7 308.1 15.9 309.3 ; + RECT 27.3 311.4 28.5 312.6 ; + RECT 16.8 326.4 18.0 327.6 ; + RECT 21.0 323.1 22.2 324.3 ; + RECT 16.8 337.5 18.0 338.7 ; + RECT 23.1 340.8 24.3 342.0 ; + RECT 16.8 355.8 18.0 357.0 ; + RECT 25.2 352.5 26.4 353.7 ; + RECT 16.8 366.9 18.0 368.1 ; + RECT 27.3 370.2 28.5 371.4 ; + RECT 18.9 385.2 20.1 386.4 ; + RECT 21.0 381.9 22.2 383.1 ; + RECT 18.9 396.3 20.1 397.5 ; + RECT 23.1 399.6 24.3 400.8 ; + RECT 18.9 414.6 20.1 415.8 ; + RECT 25.2 411.3 26.4 412.5 ; + RECT 18.9 425.7 20.1 426.9 ; + RECT 27.3 429.0 28.5 430.2 ; + RECT 51.0 214.35 57.3 215.25 ; + RECT 51.0 208.8 66.9 209.7 ; + RECT 51.0 229.05 57.3 229.95 ; + RECT 51.0 219.9 66.9 220.8 ; + RECT 51.0 243.75 57.3 244.65 ; + RECT 51.0 238.2 66.9 239.1 ; + RECT 51.0 258.45 57.3 259.35 ; + RECT 51.0 249.3 66.9 250.2 ; + RECT 51.0 273.15 57.3 274.05 ; + RECT 51.0 267.6 66.9 268.5 ; + RECT 51.0 287.85 57.3 288.75 ; + RECT 51.0 278.7 66.9 279.6 ; + RECT 51.0 302.55 57.3 303.45 ; + RECT 51.0 297.0 66.9 297.9 ; + RECT 51.0 317.25 57.3 318.15 ; + RECT 51.0 308.1 66.9 309.0 ; + RECT 51.0 331.95 57.3 332.85 ; + RECT 51.0 326.4 66.9 327.3 ; + RECT 51.0 346.65 57.3 347.55 ; + RECT 51.0 337.5 66.9 338.4 ; + RECT 51.0 361.35 57.3 362.25 ; + RECT 51.0 355.8 66.9 356.7 ; + RECT 51.0 376.05 57.3 376.95 ; + RECT 51.0 366.9 66.9 367.8 ; + RECT 51.0 390.75 57.3 391.65 ; + RECT 51.0 385.2 66.9 386.1 ; + RECT 51.0 405.45 57.3 406.35 ; + RECT 51.0 396.3 66.9 397.2 ; + RECT 51.0 420.15 57.3 421.05 ; + RECT 51.0 414.6 66.9 415.5 ; + RECT 51.0 434.85 57.3 435.75 ; + RECT 51.0 425.7 66.9 426.6 ; + RECT 50.7 214.35 51.9 215.55 ; + RECT 57.0 214.35 58.2 215.55 ; + RECT 66.9 208.8 68.1 210.0 ; + RECT 51.0 208.8 52.2 210.0 ; + RECT 50.7 229.05 51.9 230.25 ; + RECT 57.0 229.05 58.2 230.25 ; + RECT 66.9 219.6 68.1 220.8 ; + RECT 51.0 219.6 52.2 220.8 ; + RECT 50.7 243.75 51.9 244.95 ; + RECT 57.0 243.75 58.2 244.95 ; + RECT 66.9 238.2 68.1 239.4 ; + RECT 51.0 238.2 52.2 239.4 ; + RECT 50.7 258.45 51.9 259.65 ; + RECT 57.0 258.45 58.2 259.65 ; + RECT 66.9 249.0 68.1 250.2 ; + RECT 51.0 249.0 52.2 250.2 ; + RECT 50.7 273.15 51.9 274.35 ; + RECT 57.0 273.15 58.2 274.35 ; + RECT 66.9 267.6 68.1 268.8 ; + RECT 51.0 267.6 52.2 268.8 ; + RECT 50.7 287.85 51.9 289.05 ; + RECT 57.0 287.85 58.2 289.05 ; + RECT 66.9 278.4 68.1 279.6 ; + RECT 51.0 278.4 52.2 279.6 ; + RECT 50.7 302.55 51.9 303.75 ; + RECT 57.0 302.55 58.2 303.75 ; + RECT 66.9 297.0 68.1 298.2 ; + RECT 51.0 297.0 52.2 298.2 ; + RECT 50.7 317.25 51.9 318.45 ; + RECT 57.0 317.25 58.2 318.45 ; + RECT 66.9 307.8 68.1 309.0 ; + RECT 51.0 307.8 52.2 309.0 ; + RECT 50.7 331.95 51.9 333.15 ; + RECT 57.0 331.95 58.2 333.15 ; + RECT 66.9 326.4 68.1 327.6 ; + RECT 51.0 326.4 52.2 327.6 ; + RECT 50.7 346.65 51.9 347.85 ; + RECT 57.0 346.65 58.2 347.85 ; + RECT 66.9 337.2 68.1 338.4 ; + RECT 51.0 337.2 52.2 338.4 ; + RECT 50.7 361.35 51.9 362.55 ; + RECT 57.0 361.35 58.2 362.55 ; + RECT 66.9 355.8 68.1 357.0 ; + RECT 51.0 355.8 52.2 357.0 ; + RECT 50.7 376.05 51.9 377.25 ; + RECT 57.0 376.05 58.2 377.25 ; + RECT 66.9 366.6 68.1 367.8 ; + RECT 51.0 366.6 52.2 367.8 ; + RECT 50.7 390.75 51.9 391.95 ; + RECT 57.0 390.75 58.2 391.95 ; + RECT 66.9 385.2 68.1 386.4 ; + RECT 51.0 385.2 52.2 386.4 ; + RECT 50.7 405.45 51.9 406.65 ; + RECT 57.0 405.45 58.2 406.65 ; + RECT 66.9 396.0 68.1 397.2 ; + RECT 51.0 396.0 52.2 397.2 ; + RECT 50.7 420.15 51.9 421.35 ; + RECT 57.0 420.15 58.2 421.35 ; + RECT 66.9 414.6 68.1 415.8 ; + RECT 51.0 414.6 52.2 415.8 ; + RECT 50.7 434.85 51.9 436.05 ; + RECT 57.0 434.85 58.2 436.05 ; + RECT 66.9 425.4 68.1 426.6 ; + RECT 51.0 425.4 52.2 426.6 ; + RECT 70.8 76.8 75.3 78.0 ; + RECT 74.1 74.1 75.3 75.3 ; + RECT 72.9 72.6 74.1 75.3 ; + RECT 69.6 75.0 70.8 78.0 ; + RECT 60.0 77.1 69.6 78.0 ; + RECT 65.1 72.6 72.9 73.8 ; + RECT 61.8 71.7 63.0 74.7 ; + RECT 58.8 76.8 60.0 78.0 ; + RECT 59.7 73.5 60.9 74.7 ; + RECT 58.2 73.5 59.7 74.4 ; + RECT 57.3 73.5 58.2 75.9 ; + RECT 51.0 75.0 57.3 75.9 ; + RECT 56.4 71.7 61.8 72.6 ; + RECT 55.2 71.7 56.4 73.8 ; + RECT 52.8 71.7 54.0 73.8 ; + RECT 49.8 75.0 51.0 76.2 ; + RECT 45.3 76.8 46.5 78.3 ; + RECT 33.6 77.4 45.3 78.3 ; + RECT 43.2 74.1 47.7 75.3 ; + RECT 42.3 74.1 43.2 76.5 ; + RECT 35.4 75.6 42.3 76.5 ; + RECT 40.8 71.7 52.8 72.6 ; + RECT 39.6 71.7 40.8 73.8 ; + RECT 36.3 71.7 37.5 74.7 ; + RECT 34.5 75.0 35.4 76.5 ; + RECT 32.4 76.8 33.6 78.3 ; + RECT 33.3 73.5 34.5 75.9 ; + RECT 24.6 75.0 33.3 75.9 ; + RECT 30.0 71.7 36.3 72.6 ; + RECT 28.8 71.7 30.0 73.8 ; + RECT 26.4 71.7 27.6 73.8 ; + RECT 23.4 75.0 24.6 76.2 ; + RECT 17.7 71.7 26.4 72.6 ; + RECT 16.5 71.7 17.7 75.3 ; + RECT 15.3 74.1 16.5 75.3 ; + RECT 15.3 69.0 75.3 70.2 ; + RECT 70.8 61.2 75.3 62.4 ; + RECT 74.1 63.9 75.3 65.1 ; + RECT 72.9 63.9 74.1 66.6 ; + RECT 69.6 61.2 70.8 64.2 ; + RECT 60.0 61.2 69.6 62.1 ; + RECT 65.1 65.4 72.9 66.6 ; + RECT 61.8 64.5 63.0 67.5 ; + RECT 58.8 61.2 60.0 62.4 ; + RECT 59.7 64.5 60.9 65.7 ; + RECT 58.2 64.8 59.7 65.7 ; + RECT 57.3 63.3 58.2 65.7 ; + RECT 51.0 63.3 57.3 64.2 ; + RECT 56.4 66.6 61.8 67.5 ; + RECT 55.2 65.4 56.4 67.5 ; + RECT 52.8 65.4 54.0 67.5 ; + RECT 49.8 63.0 51.0 64.2 ; + RECT 45.3 60.9 46.5 62.4 ; + RECT 33.6 60.9 45.3 61.8 ; + RECT 43.2 63.9 47.7 65.1 ; + RECT 42.3 62.7 43.2 65.1 ; + RECT 35.4 62.7 42.3 63.6 ; + RECT 40.8 66.6 52.8 67.5 ; + RECT 39.6 65.4 40.8 67.5 ; + RECT 36.3 64.5 37.5 67.5 ; + RECT 34.5 62.7 35.4 64.2 ; + RECT 32.4 60.9 33.6 62.4 ; + RECT 33.3 63.3 34.5 65.7 ; + RECT 24.6 63.3 33.3 64.2 ; + RECT 30.0 66.6 36.3 67.5 ; + RECT 28.8 65.4 30.0 67.5 ; + RECT 26.4 65.4 27.6 67.5 ; + RECT 23.4 63.0 24.6 64.2 ; + RECT 17.7 66.6 26.4 67.5 ; + RECT 16.5 63.9 17.7 67.5 ; + RECT 15.3 63.9 16.5 65.1 ; + RECT 15.3 69.0 75.3 70.2 ; + RECT 70.8 56.4 75.3 57.6 ; + RECT 74.1 53.7 75.3 54.9 ; + RECT 72.9 52.2 74.1 54.9 ; + RECT 69.6 54.6 70.8 57.6 ; + RECT 60.0 56.7 69.6 57.6 ; + RECT 65.1 52.2 72.9 53.4 ; + RECT 61.8 51.3 63.0 54.3 ; + RECT 58.8 56.4 60.0 57.6 ; + RECT 59.7 53.1 60.9 54.3 ; + RECT 58.2 53.1 59.7 54.0 ; + RECT 57.3 53.1 58.2 55.5 ; + RECT 51.0 54.6 57.3 55.5 ; + RECT 56.4 51.3 61.8 52.2 ; + RECT 55.2 51.3 56.4 53.4 ; + RECT 52.8 51.3 54.0 53.4 ; + RECT 49.8 54.6 51.0 55.8 ; + RECT 45.3 56.4 46.5 57.9 ; + RECT 33.6 57.0 45.3 57.9 ; + RECT 43.2 53.7 47.7 54.9 ; + RECT 42.3 53.7 43.2 56.1 ; + RECT 35.4 55.2 42.3 56.1 ; + RECT 40.8 51.3 52.8 52.2 ; + RECT 39.6 51.3 40.8 53.4 ; + RECT 36.3 51.3 37.5 54.3 ; + RECT 34.5 54.6 35.4 56.1 ; + RECT 32.4 56.4 33.6 57.9 ; + RECT 33.3 53.1 34.5 55.5 ; + RECT 24.6 54.6 33.3 55.5 ; + RECT 30.0 51.3 36.3 52.2 ; + RECT 28.8 51.3 30.0 53.4 ; + RECT 26.4 51.3 27.6 53.4 ; + RECT 23.4 54.6 24.6 55.8 ; + RECT 17.7 51.3 26.4 52.2 ; + RECT 16.5 51.3 17.7 54.9 ; + RECT 15.3 53.7 16.5 54.9 ; + RECT 15.3 48.6 75.3 49.8 ; + RECT 70.8 40.8 75.3 42.0 ; + RECT 74.1 43.5 75.3 44.7 ; + RECT 72.9 43.5 74.1 46.2 ; + RECT 69.6 40.8 70.8 43.8 ; + RECT 60.0 40.8 69.6 41.7 ; + RECT 65.1 45.0 72.9 46.2 ; + RECT 61.8 44.1 63.0 47.1 ; + RECT 58.8 40.8 60.0 42.0 ; + RECT 59.7 44.1 60.9 45.3 ; + RECT 58.2 44.4 59.7 45.3 ; + RECT 57.3 42.9 58.2 45.3 ; + RECT 51.0 42.9 57.3 43.8 ; + RECT 56.4 46.2 61.8 47.1 ; + RECT 55.2 45.0 56.4 47.1 ; + RECT 52.8 45.0 54.0 47.1 ; + RECT 49.8 42.6 51.0 43.8 ; + RECT 45.3 40.5 46.5 42.0 ; + RECT 33.6 40.5 45.3 41.4 ; + RECT 43.2 43.5 47.7 44.7 ; + RECT 42.3 42.3 43.2 44.7 ; + RECT 35.4 42.3 42.3 43.2 ; + RECT 40.8 46.2 52.8 47.1 ; + RECT 39.6 45.0 40.8 47.1 ; + RECT 36.3 44.1 37.5 47.1 ; + RECT 34.5 42.3 35.4 43.8 ; + RECT 32.4 40.5 33.6 42.0 ; + RECT 33.3 42.9 34.5 45.3 ; + RECT 24.6 42.9 33.3 43.8 ; + RECT 30.0 46.2 36.3 47.1 ; + RECT 28.8 45.0 30.0 47.1 ; + RECT 26.4 45.0 27.6 47.1 ; + RECT 23.4 42.6 24.6 43.8 ; + RECT 17.7 46.2 26.4 47.1 ; + RECT 16.5 43.5 17.7 47.1 ; + RECT 15.3 43.5 16.5 44.7 ; + RECT 15.3 48.6 75.3 49.8 ; + RECT 128.55 6.0 129.45 6.9 ; + RECT 124.5 6.0 129.0 6.9 ; + RECT 128.55 6.45 129.45 12.45 ; + RECT 124.05 6.0 125.25 7.2 ; + RECT 138.75 6.0 139.65 6.9 ; + RECT 134.7 6.0 139.2 6.9 ; + RECT 138.75 6.45 139.65 12.45 ; + RECT 134.25 6.0 135.45 7.2 ; + RECT 128.55 0.3 129.75 1.5 ; + RECT 138.75 0.3 139.95 1.5 ; + RECT 90.6 88.8 91.8 90.0 ; + RECT 90.9 73.5 92.1 74.7 ; + RECT 72.0 73.5 73.2 74.7 ; + RECT 87.9 104.7 89.1 105.9 ; + RECT 88.2 64.8 89.4 66.0 ; + RECT 72.0 64.8 73.2 66.0 ; + RECT 85.2 147.6 86.4 148.8 ; + RECT 85.5 53.1 86.7 54.3 ; + RECT 72.0 53.1 73.2 54.3 ; + RECT 82.5 163.5 83.7 164.7 ; + RECT 82.8 44.4 84.0 45.6 ; + RECT 72.0 44.4 73.2 45.6 ; + RECT 114.0 33.3 115.2 34.5 ; + RECT 108.6 28.65 109.8 29.85 ; + RECT 111.3 26.25 112.5 27.45 ; + RECT 114.0 446.4 115.2 447.6 ; + RECT 116.7 97.95 117.9 99.15 ; + RECT 119.4 196.05 120.6 197.25 ; + RECT 16.5 76.5 17.7 77.7 ; + RECT 16.2 76.8 17.4 78.0 ; + RECT 106.2 83.1 107.4 84.3 ; + RECT 105.9 456.6 107.1 457.8 ; + RECT 95.1 437.1 97.5 438.3 ; + RECT 133.8 437.1 135.0 438.3 ; + RECT 144.0 437.1 145.2 438.3 ; + RECT 122.7 437.1 123.9 438.3 ; + RECT 101.7 8.1 104.1 9.3 ; + RECT 133.5 8.1 134.7 9.3 ; + RECT 133.5 8.1 134.7 9.3 ; + RECT 87.75 229.05 88.95 230.25 ; + RECT 87.75 258.45 88.95 259.65 ; + RECT 87.75 287.85 88.95 289.05 ; + RECT 87.75 317.25 88.95 318.45 ; + RECT 87.75 346.65 88.95 347.85 ; + RECT 87.75 376.05 88.95 377.25 ; + RECT 87.75 405.45 88.95 406.65 ; + RECT 87.75 434.85 88.95 436.05 ; + RECT 95.1 140.55 97.5 141.75 ; + RECT 95.1 199.35 97.5 200.55 ; + RECT 75.0 69.15 76.2 70.35 ; + RECT 95.1 69.15 97.5 70.35 ; + RECT 75.0 69.15 76.2 70.35 ; + RECT 95.1 69.15 97.5 70.35 ; + RECT 75.0 48.75 76.2 49.95 ; + RECT 95.1 48.75 97.5 49.95 ; + RECT 75.0 48.75 76.2 49.95 ; + RECT 95.1 48.75 97.5 49.95 ; + RECT -66.3 148.2 -7.5 149.1 ; + RECT -66.3 150.9 -7.5 151.8 ; + RECT -66.3 153.6 -7.5 154.5 ; + RECT -66.3 156.3 -7.5 157.2 ; + RECT -66.3 159.0 -7.5 159.9 ; + RECT -66.3 161.7 -7.5 162.6 ; + RECT -66.3 211.5 -7.5 212.4 ; + RECT -66.3 214.2 -7.5 215.1 ; + RECT -66.3 216.9 -7.5 217.8 ; + RECT -66.3 219.6 -7.5 220.5 ; + RECT -56.55 145.5 -55.65 149.1 ; + RECT -56.55 145.5 -55.65 149.1 ; + RECT -36.15 145.5 -35.25 149.1 ; + RECT -7.5 161.7 -3.0 162.6 ; + RECT -14.1 88.5 -3.0 89.4 ; + RECT -14.7 175.5 -3.0 176.4 ; + RECT -7.5 211.5 -3.0 212.4 ; + RECT -14.7 180.0 -3.0 180.9 ; + RECT -64.5 141.0 -63.3 145.5 ; + RECT -61.8 144.3 -60.6 145.5 ; + RECT -61.8 143.1 -59.1 144.3 ; + RECT -64.5 139.8 -61.5 141.0 ; + RECT -64.5 130.2 -63.6 139.8 ; + RECT -60.3 135.3 -59.1 143.1 ; + RECT -61.2 132.0 -58.2 133.2 ; + RECT -64.5 129.0 -63.3 130.2 ; + RECT -61.2 129.9 -60.0 131.1 ; + RECT -60.9 128.4 -60.0 129.9 ; + RECT -62.4 127.5 -60.0 128.4 ; + RECT -62.4 121.2 -61.5 127.5 ; + RECT -59.1 126.6 -58.2 132.0 ; + RECT -60.3 125.4 -58.2 126.6 ; + RECT -60.3 123.0 -58.2 124.2 ; + RECT -62.7 120.0 -61.5 121.2 ; + RECT -64.8 115.5 -63.3 116.7 ; + RECT -64.8 103.8 -63.9 115.5 ; + RECT -61.8 113.4 -60.6 117.9 ; + RECT -63.0 112.5 -60.6 113.4 ; + RECT -63.0 105.6 -62.1 112.5 ; + RECT -59.1 111.0 -58.2 123.0 ; + RECT -60.3 109.8 -58.2 111.0 ; + RECT -61.2 106.5 -58.2 107.7 ; + RECT -63.0 104.7 -61.5 105.6 ; + RECT -64.8 102.6 -63.3 103.8 ; + RECT -62.4 103.5 -60.0 104.7 ; + RECT -62.4 94.8 -61.5 103.5 ; + RECT -59.1 100.2 -58.2 106.5 ; + RECT -60.3 99.0 -58.2 100.2 ; + RECT -60.3 96.6 -58.2 97.8 ; + RECT -62.7 93.6 -61.5 94.8 ; + RECT -59.1 87.9 -58.2 96.6 ; + RECT -61.8 86.7 -58.2 87.9 ; + RECT -61.8 85.5 -60.6 86.7 ; + RECT -56.7 85.5 -55.5 145.5 ; + RECT -48.9 141.0 -47.7 145.5 ; + RECT -51.6 144.3 -50.4 145.5 ; + RECT -53.1 143.1 -50.4 144.3 ; + RECT -50.7 139.8 -47.7 141.0 ; + RECT -48.6 130.2 -47.7 139.8 ; + RECT -53.1 135.3 -51.9 143.1 ; + RECT -54.0 132.0 -51.0 133.2 ; + RECT -48.9 129.0 -47.7 130.2 ; + RECT -52.2 129.9 -51.0 131.1 ; + RECT -52.2 128.4 -51.3 129.9 ; + RECT -52.2 127.5 -49.8 128.4 ; + RECT -50.7 121.2 -49.8 127.5 ; + RECT -54.0 126.6 -53.1 132.0 ; + RECT -54.0 125.4 -51.9 126.6 ; + RECT -54.0 123.0 -51.9 124.2 ; + RECT -50.7 120.0 -49.5 121.2 ; + RECT -48.9 115.5 -47.4 116.7 ; + RECT -48.3 103.8 -47.4 115.5 ; + RECT -51.6 113.4 -50.4 117.9 ; + RECT -51.6 112.5 -49.2 113.4 ; + RECT -50.1 105.6 -49.2 112.5 ; + RECT -54.0 111.0 -53.1 123.0 ; + RECT -54.0 109.8 -51.9 111.0 ; + RECT -54.0 106.5 -51.0 107.7 ; + RECT -50.7 104.7 -49.2 105.6 ; + RECT -48.9 102.6 -47.4 103.8 ; + RECT -52.2 103.5 -49.8 104.7 ; + RECT -50.7 94.8 -49.8 103.5 ; + RECT -54.0 100.2 -53.1 106.5 ; + RECT -54.0 99.0 -51.9 100.2 ; + RECT -54.0 96.6 -51.9 97.8 ; + RECT -50.7 93.6 -49.5 94.8 ; + RECT -54.0 87.9 -53.1 96.6 ; + RECT -54.0 86.7 -50.4 87.9 ; + RECT -51.6 85.5 -50.4 86.7 ; + RECT -56.7 85.5 -55.5 145.5 ; + RECT -44.1 141.0 -42.9 145.5 ; + RECT -41.4 144.3 -40.2 145.5 ; + RECT -41.4 143.1 -38.7 144.3 ; + RECT -44.1 139.8 -41.1 141.0 ; + RECT -44.1 130.2 -43.2 139.8 ; + RECT -39.9 135.3 -38.7 143.1 ; + RECT -40.8 132.0 -37.8 133.2 ; + RECT -44.1 129.0 -42.9 130.2 ; + RECT -40.8 129.9 -39.6 131.1 ; + RECT -40.5 128.4 -39.6 129.9 ; + RECT -42.0 127.5 -39.6 128.4 ; + RECT -42.0 121.2 -41.1 127.5 ; + RECT -38.7 126.6 -37.8 132.0 ; + RECT -39.9 125.4 -37.8 126.6 ; + RECT -39.9 123.0 -37.8 124.2 ; + RECT -42.3 120.0 -41.1 121.2 ; + RECT -44.4 115.5 -42.9 116.7 ; + RECT -44.4 103.8 -43.5 115.5 ; + RECT -41.4 113.4 -40.2 117.9 ; + RECT -42.6 112.5 -40.2 113.4 ; + RECT -42.6 105.6 -41.7 112.5 ; + RECT -38.7 111.0 -37.8 123.0 ; + RECT -39.9 109.8 -37.8 111.0 ; + RECT -40.8 106.5 -37.8 107.7 ; + RECT -42.6 104.7 -41.1 105.6 ; + RECT -44.4 102.6 -42.9 103.8 ; + RECT -42.0 103.5 -39.6 104.7 ; + RECT -42.0 94.8 -41.1 103.5 ; + RECT -38.7 100.2 -37.8 106.5 ; + RECT -39.9 99.0 -37.8 100.2 ; + RECT -39.9 96.6 -37.8 97.8 ; + RECT -42.3 93.6 -41.1 94.8 ; + RECT -38.7 87.9 -37.8 96.6 ; + RECT -41.4 86.7 -37.8 87.9 ; + RECT -41.4 85.5 -40.2 86.7 ; + RECT -36.3 85.5 -35.1 145.5 ; + RECT -19.65 99.9 -18.45 101.1 ; + RECT -14.1 100.05 -13.2 100.95 ; + RECT -13.65 100.05 -7.65 100.95 ; + RECT -14.25 99.9 -13.05 101.1 ; + RECT -19.65 95.1 -18.45 96.3 ; + RECT -14.1 95.25 -13.2 96.15 ; + RECT -13.65 95.25 -7.65 96.15 ; + RECT -14.25 95.1 -13.05 96.3 ; + RECT -43.65 236.1 -41.85 237.9 ; + RECT -14.85 265.8 -13.65 267.0 ; + RECT -14.85 256.2 -13.65 257.4 ; + RECT -31.65 247.8 -30.45 249.0 ; + RECT -31.65 257.4 -30.45 258.6 ; + RECT -14.85 260.4 -13.65 261.6 ; + RECT -14.55 257.4 -13.65 261.6 ; + RECT -14.85 250.8 -13.65 252.0 ; + RECT -14.55 247.5 -13.65 249.0 ; + RECT -31.2 247.5 -30.3 249.0 ; + RECT -14.55 248.25 -13.65 252.0 ; + RECT -31.65 247.8 -30.75 248.25 ; + RECT -14.7 247.65 -13.5 248.85 ; + RECT -31.35 247.65 -30.15 248.85 ; + RECT -31.65 253.2 -30.45 254.4 ; + RECT -31.65 253.2 -30.75 257.4 ; + RECT -53.55 258.3 -51.75 268.8 ; + RECT -55.95 255.3 -54.75 268.8 ; + RECT -58.95 255.3 -57.75 268.8 ; + RECT -55.95 254.1 -53.25 255.3 ; + RECT -58.95 254.1 -57.45 255.3 ; + RECT -62.55 254.1 -61.35 268.8 ; + RECT -53.55 239.4 -51.15 249.9 ; + RECT -55.95 239.4 -54.75 252.9 ; + RECT -58.95 239.4 -57.75 252.9 ; + RECT -55.95 252.9 -53.25 254.1 ; + RECT -58.95 252.9 -57.45 254.1 ; + RECT -62.55 239.4 -61.35 254.1 ; + RECT -53.55 228.9 -51.15 239.4 ; + RECT -55.95 225.9 -54.75 239.4 ; + RECT -58.95 225.9 -57.75 239.4 ; + RECT -55.95 224.7 -53.25 225.9 ; + RECT -58.95 224.7 -57.45 225.9 ; + RECT -62.55 224.7 -61.35 239.4 ; + RECT -43.05 235.35 -41.85 236.55 ; + RECT -32.7 238.05 -31.8 238.95 ; + RECT -45.0 238.2 -44.1 239.1 ; + RECT -44.55 238.2 -32.25 239.1 ; + RECT -32.85 237.9 -31.65 239.1 ; + RECT -45.15 238.05 -43.95 239.25 ; + RECT -55.8 271.05 -54.9 271.95 ; + RECT -55.8 254.1 -54.9 271.65 ; + RECT -55.95 270.9 -54.75 272.1 ; + RECT -13.65 220.5 -12.75 221.4 ; + RECT -32.1 220.65 -31.2 221.55 ; + RECT -13.65 221.1 -12.75 241.35 ; + RECT -32.1 211.5 -31.2 221.1 ; + RECT -13.8 220.35 -12.6 221.55 ; + RECT -32.25 220.5 -31.05 221.7 ; + RECT -32.85 271.2 -31.65 272.4 ; + RECT -13.8 239.55 -12.6 240.75 ; + RECT -13.65 251.85 -12.75 252.75 ; + RECT -50.1 251.85 -49.2 252.75 ; + RECT -13.65 241.35 -12.75 252.3 ; + RECT -44.55 251.85 -13.2 252.75 ; + RECT -49.65 251.85 -44.55 252.75 ; + RECT -50.1 231.3 -49.2 252.3 ; + RECT -50.25 249.6 -49.05 250.8 ; + RECT -45.9 244.65 -45.0 245.55 ; + RECT -45.9 197.55 -45.0 245.1 ; + RECT -46.05 244.5 -44.85 245.7 ; + RECT -45.9 264.75 -45.0 265.65 ; + RECT -45.9 237.75 -45.0 265.2 ; + RECT -46.05 264.6 -44.85 265.8 ; + RECT -8.4 229.95 -7.5 230.85 ; + RECT -8.4 172.2 -7.5 173.1 ; + RECT -37.8 172.35 -36.9 173.25 ; + RECT -8.4 172.8 -7.5 230.4 ; + RECT -37.8 172.8 -36.9 193.8 ; + RECT -8.55 229.8 -7.35 231.0 ; + RECT -8.55 172.05 -7.35 173.25 ; + RECT -37.95 172.2 -36.75 173.4 ; + RECT -46.65 292.65 -45.45 293.85 ; + RECT -43.2 267.0 -42.3 267.9 ; + RECT -43.2 277.5 -42.3 278.4 ; + RECT -43.2 267.45 -42.3 278.1 ; + RECT -43.35 266.85 -42.15 268.05 ; + RECT -43.35 277.35 -42.15 278.55 ; + RECT -45.9 316.5 -45.0 317.4 ; + RECT -45.9 292.65 -45.0 317.1 ; + RECT -46.05 316.35 -44.85 317.55 ; + RECT -43.2 267.0 -42.3 267.9 ; + RECT -43.2 253.5 -42.3 254.4 ; + RECT -43.2 254.1 -42.3 267.45 ; + RECT -43.35 266.85 -42.15 268.05 ; + RECT -43.35 253.35 -42.15 254.55 ; + RECT -45.9 344.7 -45.0 345.6 ; + RECT -45.9 292.65 -45.0 345.3 ; + RECT -46.05 344.55 -44.85 345.75 ; + RECT -62.4 229.95 -61.5 230.85 ; + RECT -62.4 177.3 -61.5 230.4 ; + RECT -62.55 229.8 -61.35 231.0 ; + RECT -52.2 229.95 -51.3 230.85 ; + RECT -52.2 177.3 -51.3 230.4 ; + RECT -52.35 229.8 -51.15 231.0 ; + RECT -25.05 169.8 -23.85 175.8 ; + RECT -32.7 165.6 -31.8 171.9 ; + RECT -34.5 165.6 -33.6 174.3 ; + RECT -33.6 173.1 -31.8 174.3 ; + RECT -26.25 169.8 -25.05 171.0 ; + RECT -26.25 174.6 -25.05 175.8 ; + RECT -33.0 170.7 -31.8 171.9 ; + RECT -33.9 165.3 -32.7 166.5 ; + RECT -33.0 173.1 -31.8 174.3 ; + RECT -36.0 165.3 -34.8 166.5 ; + RECT -49.95 169.8 -48.75 175.8 ; + RECT -42.0 165.6 -41.1 171.9 ; + RECT -40.2 165.6 -39.3 174.3 ; + RECT -42.0 173.1 -40.2 174.3 ; + RECT -49.95 169.8 -48.75 171.0 ; + RECT -49.95 174.6 -48.75 175.8 ; + RECT -43.2 170.7 -42.0 171.9 ; + RECT -42.3 165.3 -41.1 166.5 ; + RECT -43.2 173.1 -42.0 174.3 ; + RECT -40.2 165.3 -39.0 166.5 ; + RECT -40.8 144.3 -39.6 145.5 ; + RECT -40.8 150.9 -39.6 152.1 ; + RECT -44.25 144.3 -43.05 145.5 ; + RECT -44.25 153.6 -43.05 154.8 ; + RECT -64.65 144.3 -63.45 145.5 ; + RECT -64.65 156.3 -63.45 157.5 ; + RECT -49.05 144.3 -47.85 145.5 ; + RECT -49.05 159.0 -47.85 160.2 ; + RECT -17.4 153.6 -16.2 154.8 ; + RECT -14.1 161.7 -12.9 162.9 ; + RECT -27.45 161.7 -26.25 162.9 ; + RECT -33.0 153.6 -31.8 154.8 ; + RECT -34.8 156.3 -33.6 157.5 ; + RECT -47.85 161.7 -46.65 162.9 ; + RECT -42.3 159.0 -41.1 160.2 ; + RECT -40.5 156.3 -39.3 157.5 ; + RECT -16.05 121.05 -15.15 121.95 ; + RECT -5.85 121.05 -4.95 121.95 ; + RECT -5.7 150.9 -4.8 151.8 ; + RECT -15.6 121.05 -5.25 121.95 ; + RECT -7.5 150.9 -5.25 151.8 ; + RECT -16.2 120.9 -15.0 122.1 ; + RECT -6.0 120.9 -4.8 122.1 ; + RECT -5.85 150.75 -4.65 151.95 ; + RECT -30.9 214.2 -29.7 215.4 ; + RECT -44.4 211.5 -43.2 212.7 ; + RECT -15.0 216.9 -13.8 218.1 ; + RECT -4.05 214.2 -3.15 215.1 ; + RECT -7.5 214.2 -3.45 215.1 ; + RECT -4.2 214.05 -3.0 215.25 ; + RECT -43.35 217.2 -42.15 218.4 ; + RECT -43.35 236.4 -42.15 237.6 ; + RECT -22.95 219.6 -21.75 220.8 ; + RECT -52.35 219.6 -51.15 220.8 ; + RECT -8.25 148.2 -7.05 149.4 ; + RECT -14.7 126.45 -13.8 127.35 ; + RECT -27.75 126.45 -26.85 127.35 ; + RECT -27.15 126.45 -14.25 127.35 ; + RECT -14.85 126.3 -13.65 127.5 ; + RECT -27.9 126.3 -26.7 127.5 ; + RECT -27.6 85.5 -26.4 86.7 ; + RECT -27.3 85.8 -26.1 87.0 ; + RECT -4.5 162.0 -3.3 163.2 ; + RECT -14.1 88.5 -12.9 89.7 ; + RECT -4.5 88.8 -3.3 90.0 ; + RECT -15.0 175.5 -13.8 176.7 ; + RECT -4.5 175.8 -3.3 177.0 ; + RECT -4.5 211.8 -3.3 213.0 ; + RECT -15.0 180.0 -13.8 181.2 ; + RECT -4.5 180.3 -3.3 181.5 ; + RECT 105.3 85.8 106.5 87.0 ; + RECT 113.4 162.0 114.6 163.2 ; + RECT 110.7 88.8 111.9 90.0 ; + RECT 108.0 175.8 109.2 177.0 ; + RECT 116.1 211.8 117.3 213.0 ; + RECT 118.8 180.3 120.0 181.5 ; + RECT 0.0 219.6 2.4 220.8 ; + RECT 95.4 230.7 98.1 231.9 ; + RECT -8.1 230.7 -6.9 231.9 ; + Layer via2 ; + RECT 125.1 152.7 125.7 153.3 ; + RECT 135.3 152.7 135.9 153.3 ; + RECT 128.7 32.4 129.3 33.0 ; + RECT 138.9 32.4 139.5 33.0 ; + RECT 128.7 32.7 129.3 33.3 ; + RECT 138.9 32.7 139.5 33.3 ; + RECT 16.8 74.4 17.4 75.0 ; + RECT 16.8 64.2 17.4 64.8 ; + RECT 16.8 54.0 17.4 54.6 ; + RECT 16.8 43.8 17.4 44.4 ; + RECT 124.35 6.3 124.95 6.9 ; + RECT 134.55 6.3 135.15 6.9 ; + RECT 128.85 0.6 129.45 1.2 ; + RECT 139.05 0.6 139.65 1.2 ; + RECT 91.2 73.8 91.8 74.4 ; + RECT 72.3 73.8 72.9 74.4 ; + RECT 88.5 65.1 89.1 65.7 ; + RECT 72.3 65.1 72.9 65.7 ; + RECT 85.8 53.4 86.4 54.0 ; + RECT 72.3 53.4 72.9 54.0 ; + RECT 83.1 44.7 83.7 45.3 ; + RECT 72.3 44.7 72.9 45.3 ; + RECT 16.5 77.1 17.1 77.7 ; + RECT 106.5 83.4 107.1 84.0 ; + RECT -61.5 87.0 -60.9 87.6 ; + RECT -51.3 87.0 -50.7 87.6 ; + RECT -41.1 87.0 -40.5 87.6 ; + RECT -14.4 247.95 -13.8 248.55 ; + RECT -31.05 247.95 -30.45 248.55 ; + RECT -43.05 217.5 -42.45 218.1 ; + RECT -43.05 236.7 -42.45 237.3 ; + RECT -27.0 86.1 -26.4 86.7 ; + RECT -4.2 162.3 -3.6 162.9 ; + RECT -4.2 89.1 -3.6 89.7 ; + RECT -4.2 176.1 -3.6 176.7 ; + RECT -4.2 212.1 -3.6 212.7 ; + RECT -4.2 180.6 -3.6 181.2 ; + RECT 105.6 86.1 106.2 86.7 ; + RECT 113.7 162.3 114.3 162.9 ; + RECT 111.0 89.1 111.6 89.7 ; + RECT 108.3 176.1 108.9 176.7 ; + RECT 116.4 212.1 117.0 212.7 ; + RECT 119.1 180.6 119.7 181.2 ; + RECT 95.7 231.0 96.3 231.6 ; + RECT 97.2 231.0 97.8 231.6 ; + RECT -7.8 231.0 -7.2 231.6 ; + Layer metal3 ; + RECT -27.15 85.5 105.9 87.0 ; + RECT -3.0 161.7 114.0 163.2 ; + RECT -3.0 88.5 111.3 90.0 ; + RECT -3.0 175.5 108.6 177.0 ; + RECT -3.0 211.5 116.7 213.0 ; + RECT -3.0 180.0 119.4 181.5 ; + RECT -6.6 230.4 95.1 231.9 ; + RECT 128.25 0.0 129.75 30.15 ; + RECT 138.45 0.0 139.95 30.15 ; + RECT 73.5 73.2 91.5 74.7 ; + RECT 73.5 64.5 88.8 66.0 ; + RECT 73.5 52.8 86.1 54.3 ; + RECT 73.5 44.1 83.4 45.6 ; + RECT 0.0 73.95 17.1 75.45 ; + RECT 0.0 63.75 17.1 65.25 ; + RECT 0.0 53.55 17.1 55.05 ; + RECT 0.0 43.35 17.1 44.85 ; + RECT 124.5 152.1 126.3 153.9 ; + RECT 134.7 152.1 136.5 153.9 ; + RECT 128.1 31.8 129.9 33.6 ; + RECT 138.3 31.8 140.1 33.6 ; + RECT 128.1 32.1 129.9 33.9 ; + RECT 138.3 32.1 140.1 33.9 ; + RECT 16.2 73.8 18.0 75.6 ; + RECT 16.2 63.6 18.0 65.4 ; + RECT 16.2 53.4 18.0 55.2 ; + RECT 16.2 43.2 18.0 45.0 ; + RECT 123.75 153.75 125.25 155.25 ; + RECT 123.75 6.45 125.25 154.5 ; + RECT 124.5 153.75 126.15 155.25 ; + RECT 123.75 5.7 125.55 7.5 ; + RECT 133.95 153.75 135.45 155.25 ; + RECT 133.95 6.45 135.45 154.5 ; + RECT 134.7 153.75 136.35 155.25 ; + RECT 133.95 5.7 135.75 7.5 ; + RECT 128.25 0.0 130.05 1.8 ; + RECT 138.45 0.0 140.25 1.8 ; + RECT 90.6 73.2 92.4 75.0 ; + RECT 71.7 73.2 73.5 75.0 ; + RECT 87.9 64.5 89.7 66.3 ; + RECT 71.7 64.5 73.5 66.3 ; + RECT 85.2 52.8 87.0 54.6 ; + RECT 71.7 52.8 73.5 54.6 ; + RECT 82.5 44.1 84.3 45.9 ; + RECT 71.7 44.1 73.5 45.9 ; + RECT 15.9 76.5 17.7 78.3 ; + RECT 16.95 82.8 18.45 84.3 ; + RECT 16.95 76.5 18.45 83.55 ; + RECT 17.7 82.8 106.2 84.3 ; + RECT 105.9 82.8 107.7 84.6 ; + RECT -43.35 216.9 -41.85 236.55 ; + RECT -62.1 86.4 -60.3 88.2 ; + RECT -51.9 86.4 -50.1 88.2 ; + RECT -41.7 86.4 -39.9 88.2 ; + RECT -14.55 247.5 -13.65 249.0 ; + RECT -31.2 247.5 -30.3 249.0 ; + RECT -31.2 247.5 -14.1 249.0 ; + RECT -15.0 247.35 -13.2 249.15 ; + RECT -31.65 247.35 -29.85 249.15 ; + RECT -43.65 216.9 -41.85 218.7 ; + RECT -43.65 236.1 -41.85 237.9 ; + RECT -27.6 85.5 -25.8 87.3 ; + RECT -4.8 161.7 -3.0 163.5 ; + RECT -4.8 88.5 -3.0 90.3 ; + RECT -4.8 175.5 -3.0 177.3 ; + RECT -4.8 211.5 -3.0 213.3 ; + RECT -4.8 180.0 -3.0 181.8 ; + RECT 105.0 85.5 106.8 87.3 ; + RECT 113.1 161.7 114.9 163.5 ; + RECT 110.4 88.5 112.2 90.3 ; + RECT 107.7 175.5 109.5 177.3 ; + RECT 115.8 211.5 117.6 213.3 ; + RECT 118.5 180.0 120.3 181.8 ; + RECT 95.1 230.4 98.4 232.2 ; + RECT -8.4 230.4 -6.6 232.2 ; + END +END sram_2_16_1_scn3me_subm +END LIBRARY diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm.lib b/compiler/tests/golden/sram_2_16_1_scn3me_subm.lib new file mode 100644 index 00000000..a6e0454f --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm.lib @@ -0,0 +1,289 @@ +library (sram_2_16_1_scn3me_subm_lib){ + delay_model : "table_lookup"; + time_unit : "1ns" ; + voltage_unit : "1v" ; + current_unit : "1mA" ; + resistance_unit : "1kohm" ; + capacitive_load_unit(1 ,fF) ; + leakage_power_unit : "1uW" ; + pulling_resistance_unit :"1kohm" ; + operating_conditions(TT){ + voltage : 5.0 ; + temperature : 25.000 ; + } + + input_threshold_pct_fall : 50.0 ; + output_threshold_pct_fall : 50.0 ; + input_threshold_pct_rise : 50.0 ; + output_threshold_pct_rise : 50.0 ; + slew_lower_threshold_pct_fall : 10.0 ; + slew_upper_threshold_pct_fall : 90.0 ; + slew_lower_threshold_pct_rise : 10.0 ; + slew_upper_threshold_pct_rise : 90.0 ; + + default_cell_leakage_power : 0.0 ; + default_leakage_power_density : 0.0 ; + default_input_pin_cap : 1.0 ; + default_inout_pin_cap : 1.0 ; + default_output_pin_cap : 0.0 ; + default_max_transition : 0.5 ; + default_fanout_load : 1.0 ; + default_max_fanout : 4.0 ; + default_connection_class : universal ; + + lu_table_template(CELL_UP_FOR_CLOCK){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CELL_DN_FOR_CLOCK){ + variable_1 : input_net_transition; + variable_2 : total_output_net_capacitance; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CONSTRAINT_HIGH_POS){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CONSTRAINT_LOW_POS){ + variable_1 : related_pin_transition; + variable_2 : constrained_pin_transition; + index_1 ("0.5"); + index_2 ("0.5"); + } + + lu_table_template(CLK_TRAN) { + variable_1 : constrained_pin_transition; + index_1 ("0.5"); + } + + lu_table_template(TRAN) { + variable_1 : total_output_net_capacitance; + index_1 ("0.5"); + } + + default_operating_conditions : TT; + + + type (DATA){ + base_type : array; + data_type : bit; + bit_width : 2; + bit_from : 0; + bit_to : 1; + } + + type (ADDR){ + base_type : array; + data_type : bit; + bit_width : 4; + bit_from : 0; + bit_to : 3; + } + +cell (sram_2_16_1_scn3me_subm){ + memory(){ + type : ram; + address_width : 4; + word_width : 2; + } + interface_timing : true; + dont_use : true; + map_only : true; + dont_touch : true; + area : 102102.39; + + bus(DATA){ + bus_type : DATA; + direction : inout; + max_capacitance : 11.3222; + pin(DATA[1:0]){ + } + three_state : "OEb & !clk"; + memory_write(){ + address : ADDR; + clocked_on : clk; + } + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + memory_read(){ + address : ADDR; + } + timing(){ + timing_sense : non_unate; + related_pin : "clk"; + timing_type : rising_edge; + cell_rise(CELL_UP_FOR_CLOCK) { + values("1.658"); + } + cell_fall(CELL_DN_FOR_CLOCK) { + values("2.364"); + } + rise_transition(TRAN) { + values("1.658"); + } + fall_transition(TRAN) { + values("2.364"); + } + } + } + + bus(ADDR){ + bus_type : ADDR; + direction : input; + capacitance : 9.8242; + max_transition : 0.5; + fanout_load : 1.000000; + pin(ADDR[3:0]){ + } + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + } + + pin(CSb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + } + + pin(OEb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + } + + pin(WEb){ + direction : input; + capacitance : 9.8242; + timing(){ + timing_type : setup_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.093"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.024"); + } + } + timing(){ + timing_type : hold_rising; + related_pin : "clk"; + rise_constraint(CONSTRAINT_HIGH_POS) { + values("0.046"); + } + fall_constraint(CONSTRAINT_LOW_POS) { + values("-0.083"); + } + } + } + + pin(clk){ + clock : true; + direction : input; + capacitance : 9.8242; + min_pulse_width_high : 1.658 ; + min_pulse_width_low : 3.428 ; + timing(){ + timing_type :"min_pulse_width"; + related_pin : clk; + rise_constraint(CLK_TRAN) { + values("0"); + } + fall_constraint(CLK_TRAN) { + values("0"); + } + } + timing(){ + timing_type :"minimum_period"; + related_pin : clk; + rise_constraint(CLK_TRAN) { + values("0"); + } + fall_constraint(CLK_TRAN) { + values("0"); + } + } + } + } +} diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm.sp b/compiler/tests/golden/sram_2_16_1_scn3me_subm.sp new file mode 100644 index 00000000..258b4464 --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm.sp @@ -0,0 +1,681 @@ +* OpenRAM generated memory. +* User: mrg +.global vdd gnd +*master-slave flip-flop with both output and inverted ouput + +.subckt ms_flop din dout dout_bar clk vdd gnd +xmaster din mout mout_bar clk clk_bar vdd gnd dlatch +xslave mout_bar dout_bar dout clk_bar clk_nn vdd gnd dlatch +.ends flop + +.subckt dlatch din dout dout_bar clk clk_bar vdd gnd +*clk inverter +mPff1 clk_bar clk vdd vdd p W=1.8u L=0.6u m=1 +mNff1 clk_bar clk gnd gnd n W=0.9u L=0.6u m=1 + +*transmission gate 1 +mtmP1 din clk int1 vdd p W=1.8u L=0.6u m=1 +mtmN1 din clk_bar int1 gnd n W=0.9u L=0.6u m=1 + +*foward inverter +mPff3 dout_bar int1 vdd vdd p W=1.8u L=0.6u m=1 +mNff3 dout_bar int1 gnd gnd n W=0.9u L=0.6u m=1 + +*backward inverter +mPff4 dout dout_bar vdd vdd p W=1.8u L=0.6u m=1 +mNf4 dout dout_bar gnd gnd n W=0.9u L=0.6u m=1 + +*transmission gate 2 +mtmP2 int1 clk_bar dout vdd p W=1.8u L=0.6u m=1 +mtmN2 int1 clk dout gnd n W=0.9u L=0.6u m=1 +.ends dlatch + + +.SUBCKT inv_nmos11 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS inv_nmos11 + +.SUBCKT inv_pmos12 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS inv_pmos12 + +.SUBCKT pinv A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos11 +Xpinv_pmos Z A vdd vdd inv_pmos12 +.ENDS pinv + +.SUBCKT nand_2_nmos13 D G S B +Mnmos D G S B n m=1 w=2.4u l=0.6u +.ENDS nand_2_nmos13 + +.SUBCKT nand_2_nmos24 D G S B +Mnmos D G S B n m=1 w=2.4u l=0.6u +.ENDS nand_2_nmos24 + +.SUBCKT nand_2_pmos15 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_2_pmos15 + +.SUBCKT nand_2_pmos26 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_2_pmos26 + +.SUBCKT nand2 A B Z vdd gnd +Xnmos1 Z A net1 gnd nand_2_nmos13 +Xnmos2 net1 B gnd gnd nand_2_nmos24 +Xpmos1 vdd A Z vdd nand_2_pmos15 +Xpmos2 Z B vdd vdd nand_2_pmos26 +.ENDS nand2 + +.SUBCKT nand_3_nmos17 D G S B +Mnmos D G S B n m=1 w=3.6u l=0.6u +.ENDS nand_3_nmos17 + +.SUBCKT nand_3_nmos28 D G S B +Mnmos D G S B n m=1 w=3.6u l=0.6u +.ENDS nand_3_nmos28 + +.SUBCKT nand_3_nmos39 D G S B +Mnmos D G S B n m=1 w=3.6u l=0.6u +.ENDS nand_3_nmos39 + +.SUBCKT nand_3_pmos110 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_3_pmos110 + +.SUBCKT nand_3_pmos211 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_3_pmos211 + +.SUBCKT nand_3_pmos312 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_3_pmos312 + +.SUBCKT NAND3 A B C Z vdd gnd +Xnmos1 net2 A gnd gnd nand_3_nmos17 +Xnmos2 net1 B net2 gnd nand_3_nmos28 +Xnmos3 Z C net1 gnd nand_3_nmos39 +Xpmos1 Z A vdd vdd nand_3_pmos110 +Xpmos2 vdd B Z vdd nand_3_pmos211 +Xpmos3 Z C vdd vdd nand_3_pmos312 +.ENDS NAND3 + +.SUBCKT inv_nmos113 D G S B +Mnmos D G S B n m=4 w=1.2u l=0.6u +.ENDS inv_nmos113 + +.SUBCKT inv_pmos114 D G S B +Mpmos D G S B p m=4 w=2.4u l=0.6u +.ENDS inv_pmos114 + +.SUBCKT pinv4 A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos113 +Xpinv_pmos Z A vdd vdd inv_pmos114 +.ENDS pinv4 + +.SUBCKT nor_2_nmos123 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS nor_2_nmos123 + +.SUBCKT nor_2_nmos224 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS nor_2_nmos224 + +.SUBCKT nor_2_pmos125 D G S B +Mpmos D G S B p m=4 w=1.2u l=0.6u +.ENDS nor_2_pmos125 + +.SUBCKT nor_2_pmos226 D G S B +Mpmos D G S B p m=4 w=1.2u l=0.6u +.ENDS nor_2_pmos226 + +.SUBCKT nor2 A B Z vdd gnd +Xnmos1 Z A gnd gnd nor_2_nmos123 +Xnmos2 Z B gnd gnd nor_2_nmos224 +Xpmos1 vdd A net1 vdd nor_2_pmos125 +Xpmos2 net1 B Z vdd nor_2_pmos226 +.ENDS nor2 + +.SUBCKT msf_control DATA[0] DATA[1] DATA[2] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] data_in[2] data_in_bar[2] clk vdd gnd +XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop +XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop +XXdff2 DATA[2] data_in[2] data_in_bar[2] clk vdd gnd ms_flop +.ENDS msf_control + +*********************** "cell_6t" ****************************** +.SUBCKT replica_cell_6t bl br wl vdd gnd +M_1 gnd net_2 vdd vdd p W='0.9u' L=1.2u +M_2 net_2 gnd vdd vdd p W='0.9u' L=1.2u +M_3 br wl net_2 gnd n W='1.2u' L=0.6u +M_4 bl wl gnd gnd n W='1.2u' L=0.6u +M_5 net_2 gnd gnd gnd n W='2.4u' L=0.6u +M_6 gnd net_2 gnd gnd n W='2.4u' L=0.6u +.ENDS $ replica_cell_6t + +*********************** "cell_6t" ****************************** +.SUBCKT cell_6t bl br wl vdd gnd +M_1 net_1 net_2 vdd vdd p W='0.9u' L=1.2u +M_2 net_2 net_1 vdd vdd p W='0.9u' L=1.2u +M_3 br wl net_2 gnd n W='1.2u' L=0.6u +M_4 bl wl net_1 gnd n W='1.2u' L=0.6u +M_5 net_2 net_1 gnd gnd n W='2.4u' L=0.6u +M_6 net_1 net_2 gnd gnd n W='2.4u' L=0.6u +.ENDS $ cell_6t + +.SUBCKT bitline_load bl[0] br[0] wl[0] wl[1] vdd gnd +Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t +Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t +.ENDS bitline_load + +.SUBCKT inv_nmos127 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS inv_nmos127 + +.SUBCKT inv_pmos128 D G S B +Mpmos D G S B p m=1 w=3.6u l=0.6u +.ENDS inv_pmos128 + +.SUBCKT delay_chain_inv A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos127 +Xpinv_pmos Z A vdd vdd inv_pmos128 +.ENDS delay_chain_inv + +.SUBCKT delay_chain clk_in clk_out vdd gnd +Xinv_chain0 clk_in s1 vdd gnd delay_chain_inv +Xinv_chain1 s1 s2 vdd gnd delay_chain_inv +Xinv_chain2 s2 s3 vdd gnd delay_chain_inv +Xinv_chain3 s3 clk_out vdd gnd delay_chain_inv +.ENDS delay_chain + +.SUBCKT inv_nmos129 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS inv_nmos129 + +.SUBCKT inv_pmos130 D G S B +Mpmos D G S B p m=1 w=3.6u l=0.6u +.ENDS inv_pmos130 + +.SUBCKT RBL_inv A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos129 +Xpinv_pmos Z A vdd vdd inv_pmos130 +.ENDS RBL_inv + +.SUBCKT nor_2_nmos139 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS nor_2_nmos139 + +.SUBCKT nor_2_nmos240 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS nor_2_nmos240 + +.SUBCKT nor_2_pmos141 D G S B +Mpmos D G S B p m=4 w=1.2u l=0.6u +.ENDS nor_2_pmos141 + +.SUBCKT nor_2_pmos242 D G S B +Mpmos D G S B p m=4 w=1.2u l=0.6u +.ENDS nor_2_pmos242 + +.SUBCKT replica_bitline_nor2 A B Z vdd gnd +Xnmos1 Z A gnd gnd nor_2_nmos139 +Xnmos2 Z B gnd gnd nor_2_nmos240 +Xpmos1 vdd A net1 vdd nor_2_pmos141 +Xpmos2 net1 B Z vdd nor_2_pmos242 +.ENDS replica_bitline_nor2 + +.SUBCKT access_tx43 D G S B +Mpmos D G S B p m=1 w=1.2u l=0.6u +.ENDS access_tx43 + +.SUBCKT replica_bitline en out vdd gnd +XBL_inv bl[0] out vdd gnd RBL_inv +XBL_access_tx vdd delayed_en bl[0] vdd access_tx43 +Xdelay_chain en delayed_en vdd gnd delay_chain +Xbitcell bl[0] br[0] delayed_en vdd gnd replica_cell_6t +Xload bl[0] br[0] gnd gnd vdd gnd bitline_load +.ENDS replica_bitline + +.SUBCKT control_logic CSb WEb OEb s_en w_en tri_en tri_en_bar clk_bar clk vdd gnd +Xmsf_control CSb WEb OEb CS_bar CS WE_bar WE OE_bar OE clk vdd gnd msf_control +Xclk_inverter clk clk_bar vdd gnd pinv4 +Xnor2 clk OE_bar tri_en vdd gnd nor2 +Xnand2_tri_en OE clk_bar tri_en_bar vdd gnd nand2 +Xreplica_bitline rblk pre_s_en vdd gnd replica_bitline +Xinv_s_en1 pre_s_en_bar s_en vdd gnd pinv +Xinv_s_en2 pre_s_en pre_s_en_bar vdd gnd pinv +XNAND3_rblk_bar clk_bar OE CS rblk_bar vdd gnd NAND3 +XNAND3_w_en_bar clk_bar WE CS w_en_bar vdd gnd NAND3 +Xinv_rblk rblk_bar rblk vdd gnd pinv +Xinv_w_en w_en_bar pre_w_en vdd gnd pinv +Xinv_w_en1 pre_w_en pre_w_en1 vdd gnd pinv +Xinv_w_en2 pre_w_en1 w_en vdd gnd pinv +.ENDS control_logic + +.SUBCKT bitcell_array bl[0] br[0] bl[1] br[1] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd +Xbit_r0_c0 bl[0] br[0] wl[0] vdd gnd cell_6t +Xbit_r1_c0 bl[0] br[0] wl[1] vdd gnd cell_6t +Xbit_r2_c0 bl[0] br[0] wl[2] vdd gnd cell_6t +Xbit_r3_c0 bl[0] br[0] wl[3] vdd gnd cell_6t +Xbit_r4_c0 bl[0] br[0] wl[4] vdd gnd cell_6t +Xbit_r5_c0 bl[0] br[0] wl[5] vdd gnd cell_6t +Xbit_r6_c0 bl[0] br[0] wl[6] vdd gnd cell_6t +Xbit_r7_c0 bl[0] br[0] wl[7] vdd gnd cell_6t +Xbit_r8_c0 bl[0] br[0] wl[8] vdd gnd cell_6t +Xbit_r9_c0 bl[0] br[0] wl[9] vdd gnd cell_6t +Xbit_r10_c0 bl[0] br[0] wl[10] vdd gnd cell_6t +Xbit_r11_c0 bl[0] br[0] wl[11] vdd gnd cell_6t +Xbit_r12_c0 bl[0] br[0] wl[12] vdd gnd cell_6t +Xbit_r13_c0 bl[0] br[0] wl[13] vdd gnd cell_6t +Xbit_r14_c0 bl[0] br[0] wl[14] vdd gnd cell_6t +Xbit_r15_c0 bl[0] br[0] wl[15] vdd gnd cell_6t +Xbit_r0_c1 bl[1] br[1] wl[0] vdd gnd cell_6t +Xbit_r1_c1 bl[1] br[1] wl[1] vdd gnd cell_6t +Xbit_r2_c1 bl[1] br[1] wl[2] vdd gnd cell_6t +Xbit_r3_c1 bl[1] br[1] wl[3] vdd gnd cell_6t +Xbit_r4_c1 bl[1] br[1] wl[4] vdd gnd cell_6t +Xbit_r5_c1 bl[1] br[1] wl[5] vdd gnd cell_6t +Xbit_r6_c1 bl[1] br[1] wl[6] vdd gnd cell_6t +Xbit_r7_c1 bl[1] br[1] wl[7] vdd gnd cell_6t +Xbit_r8_c1 bl[1] br[1] wl[8] vdd gnd cell_6t +Xbit_r9_c1 bl[1] br[1] wl[9] vdd gnd cell_6t +Xbit_r10_c1 bl[1] br[1] wl[10] vdd gnd cell_6t +Xbit_r11_c1 bl[1] br[1] wl[11] vdd gnd cell_6t +Xbit_r12_c1 bl[1] br[1] wl[12] vdd gnd cell_6t +Xbit_r13_c1 bl[1] br[1] wl[13] vdd gnd cell_6t +Xbit_r14_c1 bl[1] br[1] wl[14] vdd gnd cell_6t +Xbit_r15_c1 bl[1] br[1] wl[15] vdd gnd cell_6t +.ENDS bitcell_array + +.SUBCKT lower_pmos44 D G S B +Mpmos D G S B p m=1 w=1.2u l=0.6u +.ENDS lower_pmos44 + +.SUBCKT upper_pmos45 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS upper_pmos45 + +.SUBCKT precharge_cell bl br clk vdd +Xlower_pmos bl clk br vdd lower_pmos44 +Xupper_pmos1 bl clk vdd vdd upper_pmos45 +Xupper_pmos2 br clk vdd vdd upper_pmos45 +.ENDS precharge_cell + +.SUBCKT precharge_array bl[0] br[0] bl[1] br[1] clk vdd +Xpre_column_0 bl[0] br[0] clk vdd precharge_cell +Xpre_column_1 bl[1] br[1] clk vdd precharge_cell +.ENDS precharge_array +*********************** "sense_amp" ****************************** + +.SUBCKT sense_amp bl br dout sclk vdd gnd +M_1 dout net_1 vdd vdd p W='5.4*1u' L=0.6u +M_2 dout net_1 net_2 gnd n W='2.7*1u' L=0.6u +M_3 net_1 dout vdd vdd p W='5.4*1u' L=0.6u +M_4 net_1 dout net_2 gnd n W='2.7*1u' L=0.6u +M_5 bl sclk dout vdd p W='7.2*1u' L=0.6u +M_6 br sclk net_1 vdd p W='7.2*1u' L=0.6u +M_7 net_2 sclk gnd gnd n W='2.7*1u' L=0.6u +.ENDS sense_amp + + +.SUBCKT sense_amp_array bl[0] br[0] bl[1] br[1] data_out[0] data_out[1] sclk vdd gnd +Xsa_d0 bl[0] br[0] data_out[0] sclk vdd gnd sense_amp +Xsa_d1 bl[1] br[1] data_out[1] sclk vdd gnd sense_amp +.ENDS sense_amp_array +*********************** Write_Driver ****************************** +.SUBCKT write_driver din bl br wen vdd gnd + +**** Inverter to conver Data_in to data_in_bar ****** +M_1 net_3 din gnd gnd n W='1.2*1u' L=0.6u +M_2 net_3 din vdd vdd p W='2.1*1u' L=0.6u + +**** 2input nand gate follwed by inverter to drive BL ****** +M_3 net_2 wen net_7 gnd n W='2.1*1u' L=0.6u +M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u +M_5 net_2 wen vdd vdd p W='2.1*1u' L=0.6u +M_6 net_2 din vdd vdd p W='2.1*1u' L=0.6u + + +M_7 net_1 net_2 vdd vdd p W='2.1*1u' L=0.6u +M_8 net_1 net_2 gnd gnd n W='1.2*1u' L=0.6u + +**** 2input nand gate follwed by inverter to drive BR****** + +M_9 net_4 wen vdd vdd p W='2.1*1u' L=0.6u +M_10 net_4 wen net_8 gnd n W='2.1*1u' L=0.6u +M_11 net_8 net_3 gnd gnd n W='2.1*1u' L=0.6u +M_12 net_4 net_3 vdd vdd p W='2.1*1u' L=0.6u + +M_13 net_6 net_4 vdd vdd p W='2.1*1u' L=0.6u +M_14 net_6 net_4 gnd gnd n W='1.2*1u' L=0.6u + +************************************************ + +M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u +M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u +M_17 net_5 wen gnd gnd n W='3.6*1u' L=0.6u + + + +.ENDS $ write_driver + + +.SUBCKT write_driver_array data_in[0] data_in[1] bl[0] br[0] bl[1] br[1] wen vdd gnd +XXwrite_driver0 data_in[0] bl[0] br[0] wen vdd gnd write_driver +XXwrite_driver1 data_in[1] bl[1] br[1] wen vdd gnd write_driver +.ENDS write_driver_array + +.SUBCKT inv_nmos147 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS inv_nmos147 + +.SUBCKT inv_pmos148 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS inv_pmos148 + +.SUBCKT INVERTER A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos147 +Xpinv_pmos Z A vdd vdd inv_pmos148 +.ENDS INVERTER + +.SUBCKT nand_2_nmos149 D G S B +Mnmos D G S B n m=1 w=2.4u l=0.6u +.ENDS nand_2_nmos149 + +.SUBCKT nand_2_nmos250 D G S B +Mnmos D G S B n m=1 w=2.4u l=0.6u +.ENDS nand_2_nmos250 + +.SUBCKT nand_2_pmos151 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_2_pmos151 + +.SUBCKT nand_2_pmos252 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_2_pmos252 + +.SUBCKT NAND2 A B Z vdd gnd +Xnmos1 Z A net1 gnd nand_2_nmos149 +Xnmos2 net1 B gnd gnd nand_2_nmos250 +Xpmos1 vdd A Z vdd nand_2_pmos151 +Xpmos2 Z B vdd vdd nand_2_pmos252 +.ENDS NAND2 + +.SUBCKT nand_2_nmos159 D G S B +Mnmos D G S B n m=1 w=2.4u l=0.6u +.ENDS nand_2_nmos159 + +.SUBCKT nand_2_nmos260 D G S B +Mnmos D G S B n m=1 w=2.4u l=0.6u +.ENDS nand_2_nmos260 + +.SUBCKT nand_2_pmos161 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_2_pmos161 + +.SUBCKT nand_2_pmos262 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_2_pmos262 + +.SUBCKT a_nand_2 A B Z vdd gnd +Xnmos1 Z A net1 gnd nand_2_nmos159 +Xnmos2 net1 B gnd gnd nand_2_nmos260 +Xpmos1 vdd A Z vdd nand_2_pmos161 +Xpmos2 Z B vdd vdd nand_2_pmos262 +.ENDS a_nand_2 + +.SUBCKT inv_nmos163 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS inv_nmos163 + +.SUBCKT inv_pmos164 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS inv_pmos164 + +.SUBCKT a_inv_1 A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos163 +Xpinv_pmos Z A vdd vdd inv_pmos164 +.ENDS a_inv_1 + +.SUBCKT pre2x4 A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd +XXpre2x4_inv[0] A[0] B[0] vdd gnd a_inv_1 +XXpre2x4_inv[1] A[1] B[1] vdd gnd a_inv_1 +XXpre2x4_nand_inv[0] Z[0] out[0] vdd gnd a_inv_1 +XXpre2x4_nand_inv[1] Z[1] out[1] vdd gnd a_inv_1 +XXpre2x4_nand_inv[2] Z[2] out[2] vdd gnd a_inv_1 +XXpre2x4_nand_inv[3] Z[3] out[3] vdd gnd a_inv_1 +XXpre2x4_nand[0] A[0] A[1] Z[3] vdd gnd a_nand_2 +XXpre2x4_nand[1] B[0] A[1] Z[2] vdd gnd a_nand_2 +XXpre2x4_nand[2] A[0] B[1] Z[1] vdd gnd a_nand_2 +XXpre2x4_nand[3] B[0] B[1] Z[0] vdd gnd a_nand_2 +.ENDS pre2x4 + +.SUBCKT nand_3_nmos165 D G S B +Mnmos D G S B n m=1 w=3.6u l=0.6u +.ENDS nand_3_nmos165 + +.SUBCKT nand_3_nmos266 D G S B +Mnmos D G S B n m=1 w=3.6u l=0.6u +.ENDS nand_3_nmos266 + +.SUBCKT nand_3_nmos367 D G S B +Mnmos D G S B n m=1 w=3.6u l=0.6u +.ENDS nand_3_nmos367 + +.SUBCKT nand_3_pmos168 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_3_pmos168 + +.SUBCKT nand_3_pmos269 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_3_pmos269 + +.SUBCKT nand_3_pmos370 D G S B +Mpmos D G S B p m=1 w=2.4u l=0.6u +.ENDS nand_3_pmos370 + +.SUBCKT a_nand_3 A B C Z vdd gnd +Xnmos1 net2 A gnd gnd nand_3_nmos165 +Xnmos2 net1 B net2 gnd nand_3_nmos266 +Xnmos3 Z C net1 gnd nand_3_nmos367 +Xpmos1 Z A vdd vdd nand_3_pmos168 +Xpmos2 vdd B Z vdd nand_3_pmos269 +Xpmos3 Z C vdd vdd nand_3_pmos370 +.ENDS a_nand_3 + +.SUBCKT pre3x8 A[0] A[1] A[2] out[0] out[1] out[2] out[3] out[4] out[5] out[6] out[7] vdd gnd +XXpre2x4_inv[0] A[0] B[0] vdd gnd a_inv_1 +XXpre2x4_inv[1] A[1] B[1] vdd gnd a_inv_1 +XXpre2x4_inv[2] A[2] B[2] vdd gnd a_inv_1 +XXpre2x4_nand_inv[0] Z[0] out[0] vdd gnd a_inv_1 +XXpre2x4_nand_inv[1] Z[1] out[1] vdd gnd a_inv_1 +XXpre2x4_nand_inv[2] Z[2] out[2] vdd gnd a_inv_1 +XXpre2x4_nand_inv[3] Z[3] out[3] vdd gnd a_inv_1 +XXpre2x4_nand_inv[4] Z[4] out[4] vdd gnd a_inv_1 +XXpre2x4_nand_inv[5] Z[5] out[5] vdd gnd a_inv_1 +XXpre2x4_nand_inv[6] Z[6] out[6] vdd gnd a_inv_1 +XXpre2x4_nand_inv[7] Z[7] out[7] vdd gnd a_inv_1 +XXpre3x8_nand[0] A[0] A[1] A[2] Z[7] vdd gnd a_nand_3 +XXpre3x8_nand[1] A[0] A[1] B[2] Z[6] vdd gnd a_nand_3 +XXpre3x8_nand[2] A[0] B[1] A[2] Z[5] vdd gnd a_nand_3 +XXpre3x8_nand[3] A[0] B[1] B[2] Z[4] vdd gnd a_nand_3 +XXpre3x8_nand[4] B[0] A[1] A[2] Z[3] vdd gnd a_nand_3 +XXpre3x8_nand[5] B[0] A[1] B[2] Z[2] vdd gnd a_nand_3 +XXpre3x8_nand[6] B[0] B[1] A[2] Z[1] vdd gnd a_nand_3 +XXpre3x8_nand[7] B[0] B[1] B[2] Z[0] vdd gnd a_nand_3 +.ENDS pre3x8 + +.SUBCKT hierarchical_decoder A[0] A[1] A[2] A[3] decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] vdd gnd +Xpre[0] A[0] A[1] out[0] out[1] out[2] out[3] vdd gnd pre2x4 +Xpre[1] A[2] A[3] out[4] out[5] out[6] out[7] vdd gnd pre2x4 +XNAND2_[0] out[0] out[4] Z[0] vdd gnd NAND2 +XNAND2_[1] out[0] out[5] Z[1] vdd gnd NAND2 +XNAND2_[2] out[0] out[6] Z[2] vdd gnd NAND2 +XNAND2_[3] out[0] out[7] Z[3] vdd gnd NAND2 +XNAND2_[4] out[1] out[4] Z[4] vdd gnd NAND2 +XNAND2_[5] out[1] out[5] Z[5] vdd gnd NAND2 +XNAND2_[6] out[1] out[6] Z[6] vdd gnd NAND2 +XNAND2_[7] out[1] out[7] Z[7] vdd gnd NAND2 +XNAND2_[8] out[2] out[4] Z[8] vdd gnd NAND2 +XNAND2_[9] out[2] out[5] Z[9] vdd gnd NAND2 +XNAND2_[10] out[2] out[6] Z[10] vdd gnd NAND2 +XNAND2_[11] out[2] out[7] Z[11] vdd gnd NAND2 +XNAND2_[12] out[3] out[4] Z[12] vdd gnd NAND2 +XNAND2_[13] out[3] out[5] Z[13] vdd gnd NAND2 +XNAND2_[14] out[3] out[6] Z[14] vdd gnd NAND2 +XNAND2_[15] out[3] out[7] Z[15] vdd gnd NAND2 +XINVERTER_[0] Z[0] decode_out[0] vdd gnd INVERTER +XINVERTER_[1] Z[1] decode_out[1] vdd gnd INVERTER +XINVERTER_[2] Z[2] decode_out[2] vdd gnd INVERTER +XINVERTER_[3] Z[3] decode_out[3] vdd gnd INVERTER +XINVERTER_[4] Z[4] decode_out[4] vdd gnd INVERTER +XINVERTER_[5] Z[5] decode_out[5] vdd gnd INVERTER +XINVERTER_[6] Z[6] decode_out[6] vdd gnd INVERTER +XINVERTER_[7] Z[7] decode_out[7] vdd gnd INVERTER +XINVERTER_[8] Z[8] decode_out[8] vdd gnd INVERTER +XINVERTER_[9] Z[9] decode_out[9] vdd gnd INVERTER +XINVERTER_[10] Z[10] decode_out[10] vdd gnd INVERTER +XINVERTER_[11] Z[11] decode_out[11] vdd gnd INVERTER +XINVERTER_[12] Z[12] decode_out[12] vdd gnd INVERTER +XINVERTER_[13] Z[13] decode_out[13] vdd gnd INVERTER +XINVERTER_[14] Z[14] decode_out[14] vdd gnd INVERTER +XINVERTER_[15] Z[15] decode_out[15] vdd gnd INVERTER +.ENDS hierarchical_decoder + +.SUBCKT msf_address ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] addr_clk vdd gnd +XXdff0 ADDR[0] A[0] A_bar[0] addr_clk vdd gnd ms_flop +XXdff1 ADDR[1] A[1] A_bar[1] addr_clk vdd gnd ms_flop +XXdff2 ADDR[2] A[2] A_bar[2] addr_clk vdd gnd ms_flop +XXdff3 ADDR[3] A[3] A_bar[3] addr_clk vdd gnd ms_flop +.ENDS msf_address + +.SUBCKT msf_data_in DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk vdd gnd +XXdff0 DATA[0] data_in[0] data_in_bar[0] clk vdd gnd ms_flop +XXdff1 DATA[1] data_in[1] data_in_bar[1] clk vdd gnd ms_flop +.ENDS msf_data_in + +.SUBCKT msf_data_out data_out[0] data_out[1] tri_in[0] tri_in_bar[0] tri_in[1] tri_in_bar[1] sclk vdd gnd +XXdff0 data_out[0] tri_in[0] tri_in_bar[0] sclk vdd gnd ms_flop +XXdff1 data_out[1] tri_in[1] tri_in_bar[1] sclk vdd gnd ms_flop +.ENDS msf_data_out +*********************** tri_gate ****************************** + +.SUBCKT tri_gate in out en en_bar vdd gnd + +M_1 net_2 in_inv gnd gnd n W='1.2*1u' L=0.6u +M_2 net_3 in_inv vdd vdd p W='2.4*1u' L=0.6u +M_3 out en_bar net_3 vdd p W='2.4*1u' L=0.6u +M_4 out en net_2 gnd n W='1.2*1u' L=0.6u +M_5 in_inv in vdd vdd p W='2.4*1u' L=0.6u +M_6 in_inv in gnd gnd n W='1.2*1u' L=0.6u + + +.ENDS + +.SUBCKT tri_gate_array tri_in[0] tri_in[1] DATA[0] DATA[1] en en_bar vdd gnd +XXtri_gate0 tri_in[0] DATA[0] en en_bar vdd gnd tri_gate +XXtri_gate1 tri_in[1] DATA[1] en en_bar vdd gnd tri_gate +.ENDS tri_gate_array + +.SUBCKT wordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk vdd gnd +XWordline_driver_inv_clk0 clk clk_bar[0] vdd gnd INVERTER +XWordline_driver_nand0 decode_out[0] clk_bar[0] net[0] vdd gnd NAND2 +XWordline_driver_inv0 net[0] wl[0] vdd gnd INVERTER +XWordline_driver_inv_clk1 clk clk_bar[1] vdd gnd INVERTER +XWordline_driver_nand1 decode_out[1] clk_bar[1] net[1] vdd gnd NAND2 +XWordline_driver_inv1 net[1] wl[1] vdd gnd INVERTER +XWordline_driver_inv_clk2 clk clk_bar[2] vdd gnd INVERTER +XWordline_driver_nand2 decode_out[2] clk_bar[2] net[2] vdd gnd NAND2 +XWordline_driver_inv2 net[2] wl[2] vdd gnd INVERTER +XWordline_driver_inv_clk3 clk clk_bar[3] vdd gnd INVERTER +XWordline_driver_nand3 decode_out[3] clk_bar[3] net[3] vdd gnd NAND2 +XWordline_driver_inv3 net[3] wl[3] vdd gnd INVERTER +XWordline_driver_inv_clk4 clk clk_bar[4] vdd gnd INVERTER +XWordline_driver_nand4 decode_out[4] clk_bar[4] net[4] vdd gnd NAND2 +XWordline_driver_inv4 net[4] wl[4] vdd gnd INVERTER +XWordline_driver_inv_clk5 clk clk_bar[5] vdd gnd INVERTER +XWordline_driver_nand5 decode_out[5] clk_bar[5] net[5] vdd gnd NAND2 +XWordline_driver_inv5 net[5] wl[5] vdd gnd INVERTER +XWordline_driver_inv_clk6 clk clk_bar[6] vdd gnd INVERTER +XWordline_driver_nand6 decode_out[6] clk_bar[6] net[6] vdd gnd NAND2 +XWordline_driver_inv6 net[6] wl[6] vdd gnd INVERTER +XWordline_driver_inv_clk7 clk clk_bar[7] vdd gnd INVERTER +XWordline_driver_nand7 decode_out[7] clk_bar[7] net[7] vdd gnd NAND2 +XWordline_driver_inv7 net[7] wl[7] vdd gnd INVERTER +XWordline_driver_inv_clk8 clk clk_bar[8] vdd gnd INVERTER +XWordline_driver_nand8 decode_out[8] clk_bar[8] net[8] vdd gnd NAND2 +XWordline_driver_inv8 net[8] wl[8] vdd gnd INVERTER +XWordline_driver_inv_clk9 clk clk_bar[9] vdd gnd INVERTER +XWordline_driver_nand9 decode_out[9] clk_bar[9] net[9] vdd gnd NAND2 +XWordline_driver_inv9 net[9] wl[9] vdd gnd INVERTER +XWordline_driver_inv_clk10 clk clk_bar[10] vdd gnd INVERTER +XWordline_driver_nand10 decode_out[10] clk_bar[10] net[10] vdd gnd NAND2 +XWordline_driver_inv10 net[10] wl[10] vdd gnd INVERTER +XWordline_driver_inv_clk11 clk clk_bar[11] vdd gnd INVERTER +XWordline_driver_nand11 decode_out[11] clk_bar[11] net[11] vdd gnd NAND2 +XWordline_driver_inv11 net[11] wl[11] vdd gnd INVERTER +XWordline_driver_inv_clk12 clk clk_bar[12] vdd gnd INVERTER +XWordline_driver_nand12 decode_out[12] clk_bar[12] net[12] vdd gnd NAND2 +XWordline_driver_inv12 net[12] wl[12] vdd gnd INVERTER +XWordline_driver_inv_clk13 clk clk_bar[13] vdd gnd INVERTER +XWordline_driver_nand13 decode_out[13] clk_bar[13] net[13] vdd gnd NAND2 +XWordline_driver_inv13 net[13] wl[13] vdd gnd INVERTER +XWordline_driver_inv_clk14 clk clk_bar[14] vdd gnd INVERTER +XWordline_driver_nand14 decode_out[14] clk_bar[14] net[14] vdd gnd NAND2 +XWordline_driver_inv14 net[14] wl[14] vdd gnd INVERTER +XWordline_driver_inv_clk15 clk clk_bar[15] vdd gnd INVERTER +XWordline_driver_nand15 decode_out[15] clk_bar[15] net[15] vdd gnd NAND2 +XWordline_driver_inv15 net[15] wl[15] vdd gnd INVERTER +.ENDS wordline_driver + +.SUBCKT inv_nmos181 D G S B +Mnmos D G S B n m=4 w=1.2u l=0.6u +.ENDS inv_nmos181 + +.SUBCKT inv_pmos182 D G S B +Mpmos D G S B p m=4 w=2.4u l=0.6u +.ENDS inv_pmos182 + +.SUBCKT pinv4x A Z vdd gnd +Xpinv_nmos Z A gnd gnd inv_nmos181 +Xpinv_pmos Z A vdd vdd inv_pmos182 +.ENDS pinv4x + +.SUBCKT nor_2_nmos195 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS nor_2_nmos195 + +.SUBCKT nor_2_nmos296 D G S B +Mnmos D G S B n m=1 w=1.2u l=0.6u +.ENDS nor_2_nmos296 + +.SUBCKT nor_2_pmos197 D G S B +Mpmos D G S B p m=4 w=1.2u l=0.6u +.ENDS nor_2_pmos197 + +.SUBCKT nor_2_pmos298 D G S B +Mpmos D G S B p m=4 w=1.2u l=0.6u +.ENDS nor_2_pmos298 + +.SUBCKT NOR2 A B Z vdd gnd +Xnmos1 Z A gnd gnd nor_2_nmos195 +Xnmos2 Z B gnd gnd nor_2_nmos296 +Xpmos1 vdd A net1 vdd nor_2_pmos197 +Xpmos2 net1 B Z vdd nor_2_pmos298 +.ENDS NOR2 + +.SUBCKT test_bank1 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd +Xbitcell_array bl[0] br[0] bl[1] br[1] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] vdd gnd bitcell_array +Xprecharge_array bl[0] br[0] bl[1] br[1] clk_bar vdd precharge_array +Xsense_amp_array bl[0] br[0] bl[1] br[1] data_out[0] data_out[1] s_en vdd gnd sense_amp_array +Xwrite_driver_array data_in[0] data_in[1] bl[0] br[0] bl[1] br[1] w_en vdd gnd write_driver_array +Xdata_in_flop_array DATA[0] DATA[1] data_in[0] data_in_bar[0] data_in[1] data_in_bar[1] clk_bar vdd gnd msf_data_in +Xtrigate_data_array data_out[0] data_out[1] DATA[0] DATA[1] tri_en tri_en_bar vdd gnd tri_gate_array +Xaddress_decoder A[0] A[1] A[2] A[3] decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] vdd gnd hierarchical_decoder +Xwordline_driver decode_out[0] decode_out[1] decode_out[2] decode_out[3] decode_out[4] decode_out[5] decode_out[6] decode_out[7] decode_out[8] decode_out[9] decode_out[10] decode_out[11] decode_out[12] decode_out[13] decode_out[14] decode_out[15] wl[0] wl[1] wl[2] wl[3] wl[4] wl[5] wl[6] wl[7] wl[8] wl[9] wl[10] wl[11] wl[12] wl[13] wl[14] wl[15] clk vdd gnd wordline_driver +Xaddress_flop_array ADDR[0] ADDR[1] ADDR[2] ADDR[3] A[0] A_bar[0] A[1] A_bar[1] A[2] A_bar[2] A[3] A_bar[3] clk vdd gnd msf_address +.ENDS test_bank1 + +.SUBCKT testsram DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd gnd +Xbank0 DATA[0] DATA[1] ADDR[0] ADDR[1] ADDR[2] ADDR[3] s_en w_en tri_en_bar tri_en clk_bar clk vdd gnd test_bank1 +Xcontrol CSb WEb OEb s_en w_en tri_en tri_en_bar clk_bar clk vdd gnd control_logic +.ENDS testsram diff --git a/compiler/tests/golden/sram_2_16_1_scn3me_subm.v b/compiler/tests/golden/sram_2_16_1_scn3me_subm.v new file mode 100644 index 00000000..fc2b6cd0 --- /dev/null +++ b/compiler/tests/golden/sram_2_16_1_scn3me_subm.v @@ -0,0 +1,47 @@ +// OpenRAM SRAM model +// Words: 16 +// Word size: 2 + +module sram_2_16_1_scn3me_subm(DATA,ADDR,CSb,WEb,OEb,clk); + + parameter DATA_WIDTH = 2 ; + parameter ADDR_WIDTH = 4 ; + parameter RAM_DEPTH = 1 << ADDR_WIDTH; + parameter DELAY = 3 ; + + inout [DATA_WIDTH-1:0] DATA; + input [ADDR_WIDTH-1:0] ADDR; + input CSb; // active low chip select + input WEb; // active low write control + input OEb; // active output enable + input clk; // clock + + reg [DATA_WIDTH-1:0] data_out ; + reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; + + // Tri-State Buffer control + // output : When WEb = 1, oeb = 0, csb = 0 + assign DATA = (!CSb && !OEb && WEb) ? data_out : 2'bz; + + // Memory Write Block + // Write Operation : When WEb = 0, CSb = 0 + always @ (posedge clk) + begin : MEM_WRITE + if ( !CSb && !WEb ) begin + mem[ADDR] = DATA; + $display($time," Writing %m ABUS=%b DATA=%b",ADDR,DATA); + end + end + + + // Memory Read Block + // Read Operation : When WEb = 1, CSb = 0 + always @ (posedge clk) + begin : MEM_READ + if (!CSb && WEb) begin + data_out <= #(DELAY) mem[ADDR]; + $display($time," Reading %m ABUS=%b DATA=%b",ADDR,mem[ADDR]); + end + end + +endmodule diff --git a/compiler/tests/header.py b/compiler/tests/header.py new file mode 100644 index 00000000..194a301d --- /dev/null +++ b/compiler/tests/header.py @@ -0,0 +1,10 @@ + +def header(str, tec): + tst = "Running Test for:" + print "\n" + print " ______________________________________________________________________________ " + print "|==============================================================================|" + print "|=========" + tst.center(60) + "=========|" + print "|=========" + tec.center(60) + "=========|" + print "|=========" + str.center(60) + "=========|" + print "|==============================================================================|" diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py new file mode 100644 index 00000000..d0307116 --- /dev/null +++ b/compiler/tests/regress.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python2.7 + +import re +import unittest +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals + +(OPTS, args) = globals.parse_args() +del sys.argv[1:] + +import header +header.header(__file__, OPTS.tech_name) + +# get a list of all files in the tests directory +files = os.listdir(sys.path[0]) + +# assume any file that ends in "test.py" in it is a regression test +nametest = re.compile("test\.py$", re.IGNORECASE) +tests = filter(nametest.search, files) +tests.sort() + +# import all of the modules +filenameToModuleName = lambda f: os.path.splitext(f)[0] +moduleNames = map(filenameToModuleName, tests) +modules = map(__import__, moduleNames) +suite = unittest.TestSuite() +load = unittest.defaultTestLoader.loadTestsFromModule +suite.addTests(map(load, modules)) +unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/compiler/tests/sram_2_16_1_freepdk45.gds b/compiler/tests/sram_2_16_1_freepdk45.gds new file mode 100644 index 00000000..2be62057 Binary files /dev/null and b/compiler/tests/sram_2_16_1_freepdk45.gds differ diff --git a/compiler/tests/sram_2_16_1_freepdk45.lef b/compiler/tests/sram_2_16_1_freepdk45.lef new file mode 100644 index 00000000..e69de29b diff --git a/compiler/tests/sram_tb.v b/compiler/tests/sram_tb.v new file mode 100644 index 00000000..95d3c46b --- /dev/null +++ b/compiler/tests/sram_tb.v @@ -0,0 +1,78 @@ +module sram; + reg [3:0] addr; + reg [1:0] data; + reg clk; + reg csb; + reg web; + reg oeb; + + wire [1:0] data_in = !oeb ? 2'bzz : data; + + sram_2_16_1_freepdk45 U0 (.DATA(data_in), + .ADDR(addr), + .CSb (csb), + .WEb (web), + .OEb (oeb), + .clk (clk) + ); + + + initial + begin + + $monitor("%g addr=%b data=%b", + $time, addr, data_in,); + + + + + clk = 0; + csb = 1; + web = 1; + oeb = 1; + addr = 0; + data = 0; + + // write + #10 data=2'b10; + addr=4'h1; + web = 0; + oeb = 1; + csb = 0; + + // write another + #10 data=2'b01; + addr=4'hC; + web = 0; + oeb = 1; + csb = 0; + + // read undefined + #10 data=2'b11; + addr=4'h0; + web = 1; + oeb = 0; + csb = 0; + + // read defined + #10 data=2'b11; + addr=4'hC; + web = 1; + oeb = 0; + csb = 0; + + // read defined + #10 data=2'b11; + addr=4'h1; + web = 1; + oeb = 0; + csb = 0; + + #30 $finish; + + end + + always + #5 clk = !clk; + +endmodule diff --git a/compiler/tri_gate.py b/compiler/tri_gate.py new file mode 100644 index 00000000..cefc51fe --- /dev/null +++ b/compiler/tri_gate.py @@ -0,0 +1,22 @@ +import debug +import design +import utils +from tech import GDS,layer + +class tri_gate(design.design): + """ + This module implements the tri gate cell used in the design for + bit-line isolation. It is a hand-made cell, so the layout and + netlist should be available in the technology library. + """ + + pins = ["in", "en", "en_bar", "out", "gnd", "vdd"] + chars = utils.auto_measure_libcell(pins, "tri_gate", GDS["unit"], layer["boundary"]) + + def __init__(self, name): + design.design.__init__(self, name) + debug.info(2, "Create tri_gate object") + + + self.width = tri_gate.chars["width"] + self.height = tri_gate.chars["height"] diff --git a/compiler/tri_gate_array.py b/compiler/tri_gate_array.py new file mode 100644 index 00000000..ffc1b057 --- /dev/null +++ b/compiler/tri_gate_array.py @@ -0,0 +1,136 @@ +import debug +from tech import drc +import design +from vector import vector +from globals import OPTS + +class tri_gate_array(design.design): + """ + Dynamically generated tri gate array of all bitlines. words_per_row + """ + + def __init__(self, columns, word_size): + """Intial function of tri gate array """ + design.design.__init__(self, "tri_gate_array") + debug.info(1, "Creating {0}".format(self.name)) + + c = reload(__import__(OPTS.config.tri_gate)) + self.mod_tri_gate = getattr(c, OPTS.config.tri_gate) + self.tri_gate_chars = self.mod_tri_gate.chars + + self.columns = columns + self.word_size = word_size + self.create_layout() + self.DRC_LVS() + + def create_layout(self): + """generate layout """ + self.add_modules() + self.setup_layout_constants() + self.add_pins() + self.create_write_array() + self.add_metal_rails() + self.add_labels() + + def add_pins(self): + """create the name of pins depend on the word size""" + for i in range(self.word_size): + self.add_pin("tri_in[{0}]".format(i)) + for i in range(self.word_size): + self.add_pin("DATA[{0}]".format(i)) + for pin in ["en", "en_bar", "vdd", "gnd"]: + self.add_pin(pin) + + def setup_layout_constants(self): + """caculate the size of tri gate array""" + self.words_per_row = self.columns / self.word_size + self.width = (self.columns / self.words_per_row) * self.tri.width + self.height = self.tri.height + self.tri_gate_positions = [] + self.vdd_positions = [] + self.gnd_positions = [] + self.tri_in_positions = [] + self.DATA_positions = [] + + def add_modules(self): + """instantiation of a tri gate""" + self.tri = self.mod_tri_gate("tri_gate") + self.add_mod(self.tri) + + def create_write_array(self): + """add tri gate to the array """ + for i in range(self.word_size): + mirror = "R0" + if (i % 2 == 0): + name = "Xtri_gate{0}".format(i) + x_off = i * self.tri.width * self.words_per_row + else: + name = "Xtri_gate{0}".format(i) + if (self.words_per_row == 1): + x_off = (i + 1) * self.tri.width * self.words_per_row + mirror = "MY" + else: + x_off = i * self.tri.width * self.words_per_row + self.add_inst(name=name, + mod=self.tri, + offset=[x_off, 0], + mirror = mirror) + self.connect_inst(["tri_in[{0}]".format(i), + "DATA[{0}]".format(i), + "en", "en_bar", "vdd", "gnd"]) + + def add_metal_rails(self): + """Connect en en_bar and vdd together """ + correct = vector(0, 0.5 * drc["minwidth_metal1"]) + width = (self.tri.width * self.columns + - (self.words_per_row - 1) * self.tri.width) + self.add_rect(layer="metal1", + offset=(self.tri_gate_chars["en"] - correct).scale(0, 1), + width=width, + height=drc['minwidth_metal1']) + self.add_rect(layer="metal1", + offset=(self.tri_gate_chars["en_bar"] - correct).scale(0, 1), + width=width, + height=drc['minwidth_metal1']) + self.add_rect(layer="metal1", + offset=(self.tri_gate_chars["vdd"] - correct).scale(0, 1), + width=width, + height=drc['minwidth_metal1']) + + def add_labels(self): + """add label for pins""" + for i in range(self.word_size): + if (i % 2 == 0 or self.words_per_row > 1): + x_off = i * self.tri.width * self.words_per_row + dir_vector = vector(1,1) + else: + x_off = (i + 1) * self.tri.width * self.words_per_row + dir_vector = vector(-1,1) + + pin_offset={} + for pin in ["en", "en_bar", "vdd", "gnd", "in", "out"]: + pin_offset[pin] = vector(x_off, 0) + dir_vector.scale(self.tri_gate_chars[pin]) + + for pin in ["en", "en_bar", "vdd"]: + self.add_label(text=pin, + layer="metal1", + offset=pin_offset[pin]) + self.add_label(text="gnd", + layer="metal2", + offset=pin_offset["gnd"]) + self.add_label(text="tri_in[{0}]".format(i), + layer="metal2", + offset=pin_offset["in"]) + self.add_label(text="DATA[{0}]".format(i), + layer="metal2", + offset=pin_offset["out"]) + + self.vdd_positions.append(pin_offset["vdd"]) + self.gnd_positions.append(pin_offset["gnd"]) + self.tri_in_positions.append(pin_offset["in"]) + self.DATA_positions.append(pin_offset["out"]) + + self.add_label(text="tri gate", + layer="text", + offset=[self.width / 2.0, + self.height / 2.0]) diff --git a/compiler/utils.py b/compiler/utils.py new file mode 100644 index 00000000..4f00563a --- /dev/null +++ b/compiler/utils.py @@ -0,0 +1,43 @@ +import os +import gdsMill +import tech +import globals + +OPTS = globals.OPTS + +def snap_to_grid(offset): + """ + Changes the coodrinate to match the grid settings + """ + grid = tech.drc["grid"] + x = offset[0] + y = offset[1] + # this gets the nearest integer value + xgrid = int(round(round((x / grid), 2), 0)) + ygrid = int(round(round((y / grid), 2), 0)) + xoff = xgrid * grid + yoff = ygrid * grid + out_offset = [xoff, yoff] + return out_offset + + +def gdsPinToOffset(gdsPin): + boundary = gdsPin[2] + return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])] + + +def auto_measure_libcell(pin_list, name, units, layer): + cell_gds = OPTS.openram_tech + "gds_lib/" + str(name) + ".gds" + cell_vlsi = gdsMill.VlsiLayout(units=units) + reader = gdsMill.Gds2reader(cell_vlsi) + reader.loadFromFile(cell_gds) + + cell = {} + measure_result = cell_vlsi.readLayoutBorder(layer) + if measure_result == None: + measure_result = cell_vlsi.measureSize(name) + [cell["width"], cell["height"]] = measure_result + + for pin in pin_list: + cell[str(pin)] = gdsPinToOffset(cell_vlsi.readPin(str(pin))) + return cell diff --git a/compiler/vector.py b/compiler/vector.py new file mode 100644 index 00000000..b780f69a --- /dev/null +++ b/compiler/vector.py @@ -0,0 +1,94 @@ +import debug + + +class vector(): + """ + This is the vector class to represent the coordinate + vector. It makes the coordinate operations easy and short + so the code is concise. + It needs to override several operators to support + concise vector operations, output, and other more complex + data structures like lists. + """ + def __init__(self, x, y=None): + """ init function support two init method""" + # will take single input as a coordinate + if y==None: + self.x = x[0] + self.y = x[1] + #will take two inputs as the values of a coordinate + else: + self.x = x + self.y = y + + def __str__(self): + """ override print function output """ + return "vector:["+str(self.x)+", "+str(self.y)+"]" + + def __repr__(self): + """ override print function output """ + return "["+str(self.x)+", "+str(self.y)+"]" + + def __setitem__(self, index, value): + """ + override setitem function + can set value by vector[index]=value + """ + if index==0: + self.x=value + elif index==1: + self.y=value + else: + self.x=value[0] + self.y=value[1] + + def __getitem__(self, index): + """ + override getitem function + can get value by value=vector[index] + """ + if index==0: + return self.x + elif index==1: + return self.y + else: + return self + + def __add__(self, other): + """ + Override + function (left add) + Can add by vector(x1,y1)+vector(x2,y2) + """ + return vector(self.x + other[0], self.y + other[1]) + + def __radd__(self, other): + """ + Override + function (right add) + """ + if other == 0: + return self + else: + return self.__add__(other) + + def __sub__(self, other): + """ + Override - function (left) + """ + return vector(self.x - other[0], self.y - other[1]) + + def __rsub__(self, other): + """ + Override - function (right) + """ + return vector(other[0]- self.x, other[1] - self.y) + + def rotate(self): + """ pass a copy of rotated vector, without altering the vector! """ + return vector(self.y,self.x) + + def scale(self, x_factor, y_factor=None): + """ pass a copy of scaled vector, without altering the vector! """ + if y_factor==None: + y_factor=x_factor[1] + x_factor=x_factor[0] + return vector(self.x*x_factor,self.y*y_factor) diff --git a/compiler/verilog.py b/compiler/verilog.py new file mode 100644 index 00000000..cbbe6fa3 --- /dev/null +++ b/compiler/verilog.py @@ -0,0 +1,112 @@ +import debug + +class verilog: + """ Create a behavioral Verilog file for simulation.""" + + def __init__(self,verilog_name,sram): + self.sram_name = sram.name + self.num_words = sram.num_words + self.word_size = sram.word_size + self.addr_size = sram.addr_size + + debug.info(1,"Writing to {0}".format(verilog_name)) + self.vf = open(verilog_name, "w") + + self.create() + + self.vf.close() + + + def create(self): + self.vf.write("// OpenRAM SRAM model\n") + self.vf.write("// Words: {0}\n".format(self.num_words)) + self.vf.write("// Word size: {0}\n\n".format(self.word_size)) + + self.vf.write("module {0}(DATA,ADDR,CSb,WEb,OEb,clk);\n".format(self.sram_name)) + self.vf.write("\n") + self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size)) + self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size)) + self.vf.write(" parameter RAM_DEPTH = 1 << ADDR_WIDTH;\n") + self.vf.write(" parameter DELAY = 3 ;\n") + self.vf.write("\n") + self.vf.write(" inout [DATA_WIDTH-1:0] DATA;\n") + self.vf.write(" input [ADDR_WIDTH-1:0] ADDR;\n") + self.vf.write(" input CSb; // active low chip select\n") + self.vf.write(" input WEb; // active low write control\n") + self.vf.write(" input OEb; // active output enable\n") + self.vf.write(" input clk; // clock\n") + self.vf.write("\n") + self.vf.write(" reg [DATA_WIDTH-1:0] data_out ;\n") + self.vf.write(" reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];\n") + self.vf.write("\n") + self.vf.write(" // Tri-State Buffer control\n") + self.vf.write(" // output : When WEb = 1, oeb = 0, csb = 0\n") + self.vf.write(" assign DATA = (!CSb && !OEb && WEb) ? data_out : {0}'bz;\n".format(self.word_size)) + self.vf.write("\n") + self.vf.write(" // Memory Write Block\n") + self.vf.write(" // Write Operation : When WEb = 0, CSb = 0\n") + self.vf.write(" always @ (posedge clk)\n") + self.vf.write(" begin : MEM_WRITE\n") + self.vf.write(" if ( !CSb && !WEb ) begin\n") + self.vf.write(" mem[ADDR] = DATA;\n") + self.vf.write(" $display($time,\" Writing %m ABUS=%b DATA=%b\",ADDR,DATA);\n") + self.vf.write(" end\n") + self.vf.write(" end\n\n") + self.vf.write("\n") + self.vf.write(" // Memory Read Block\n") + self.vf.write(" // Read Operation : When WEb = 1, CSb = 0\n") + self.vf.write(" always @ (posedge clk)\n") + self.vf.write(" begin : MEM_READ\n") + self.vf.write(" if (!CSb && WEb) begin\n") + self.vf.write(" data_out <= #(DELAY) mem[ADDR];\n") + self.vf.write(" $display($time,\" Reading %m ABUS=%b DATA=%b\",ADDR,mem[ADDR]);\n") + self.vf.write(" end\n") + self.vf.write(" end\n") + self.vf.write("\n") + self.vf.write("endmodule\n") + + + +# //SRAM Model +# module sram(CSB,WRB,ABUS,DATABUS); +# input CSB; // active low chip select +# input WRB; // active low write control +# input [11:0] ABUS; // 12-bit address bus +# inout [7:0] DATABUS; // 8-bit data bus +# //** internal signals +# reg [7:0] DATABUS_driver; +# wire [7:0] DATABUS = DATABUS_driver; +# reg [7:0] ram[0:4095]; // memory cells +# integer i; + +# initial //initialize all RAM cells to 0 at startup +# begin +# DATABUS_driver = 8'bzzzzzzzz; +# for (i=0; i < 4095; i = i + 1) +# ram[i] = 0; +# end + +# always @(CSB or WRB or ABUS) +# begin +# if (CSB == 1'b0) +# begin +# if (WRB == 1'b0) //Start: latch Data on rising edge of CSB or WRB +# begin +# DATABUS_driver <= #10 8'bzzzzzzzz; +# @(posedge CSB or posedge WRB); +# $display($time," Writing %m ABUS=%b DATA=%b",ABUS,DATABUS); +# ram[ABUS] = DATABUS; +# end +# if (WRB == 1'b1) //Reading from sram (data becomes valid after 10ns) +# begin +# #10 DATABUS_driver = ram[ABUS]; +# $display($time," Reading %m ABUS=%b DATA=%b",ABUS,DATABUS_driver); +# end +# end +# else //sram unselected, stop driving bus after 10ns +# begin +# DATABUS_driver <= #10 8'bzzzzzzzz; +# end +# end +# endmodule + diff --git a/compiler/wire.py b/compiler/wire.py new file mode 100644 index 00000000..541fa50a --- /dev/null +++ b/compiler/wire.py @@ -0,0 +1,216 @@ +from tech import drc +import debug +import design +from contact import contact +from path import path + + +class wire(path): + """ + Object metal wire; given the layer type + Add a wire of minimium metal width between a set of points. + The points should be rectilinear to control the bend points. If + not, it will always go down first. + The points are the center of the wire. + The layer stack is the vertical, contact/via, and horizontal layers, respectively. + """ + unique_wire_id = 1 + + def __init__(self, layer_stack, position_list): + name = "wire_{0}".format(wire.unique_wire_id) + wire.unique_wire_id += 1 + design.design.__init__(self, name) + debug.info(2, "create wire obj {0}".format(name)) + + self.layer_stack = layer_stack + self.position_list = position_list + self.pins = [] # used for matching parm lengths + self.switch_pos_list = [] + + self.create_layout() + + def create_layout(self): + self.setup_layers() + self.create_rectilinear_route() + self.create_vias() + self.create_rectangles() + # wires and paths should not be offset to (0,0) + + def setup_layers(self): + (vert_layer, via_layer, horiz_layer) = self.layer_stack + if (via_layer != None): + self.via_layer_name = via_layer + else: + self.via_layer_name = None + + self.vert_layer_name = vert_layer + self.vert_layer_width = drc["minwidth_{0}".format(vert_layer)] + + self.horiz_layer_name = horiz_layer + self.horiz_layer_width = drc["minwidth_{0}".format(horiz_layer)] + via_connect = contact(self.layer_stack, + (1, 1)) + self.node_to_node = [drc["minwidth_" + str(self.horiz_layer_name)] \ + + via_connect.width, + drc["minwidth_" + str(self.horiz_layer_name)] \ + + via_connect.height] + + # create a 1x1 contact + def create_vias(self): + """ Add a via and corner square at every corner of the path.""" + pl = self.pairwise(self.position_list) + from itertools import izip + if self.via_layer_name == None: + c_height = 0 + c_width = 0 + c = None + else: + c = self.c = contact(self.layer_stack, (1, 1)) + c_width = c.width + c_height = c.height + orient = None # orientation toggler + offset = [0, 0] + + for (v, w), index in izip(pl, range(len(pl))): + if index != 0: + if pl[index][1] == pl[index - 1][0]: + if v[0] != w[0]: + offset = [(offset[0] + (w[0] - v[0])), + offset[1]] + else: + offset = [offset[0], + (offset[1] + w[1] - v[1])] + orient = not orient + continue + if v[0] != w[0]: + if (orient == None): + orient = True + if not orient: + orient = not orient + if w[0] - v[0] < 0: + temp_offset = [ + offset[0] + 0.5*c_height, + offset[1] - 0.5*self.horiz_layer_width] + else: + temp_offset = [offset[0] + 0.5*c_height, + offset[1] - 0.5*self.horiz_layer_width] + self.switch_pos_list.append(temp_offset) + via_offset = self.switch_pos_list[-1] + if c: + self.add_inst(name="via_{0}_{1}".format(v, w), + mod=c, + offset=via_offset, + rotate=90) + corner_offset = [via_offset[0] \ + - 0.5*(c_height + self.vert_layer_width), + via_offset[1] \ + + 0.5*(c_width - self.horiz_layer_width)] + self.draw_corner_wire(corner_offset) + offset = [(offset[0] + (w[0] - v[0])), + offset[1]] + elif v[1] != w[1]: + if (orient == None): + orient = False + if orient: + orient = not orient + if -w[1] - v[1] > 0: + temp_offset = [offset[0] + 0.5*c_height, + offset[1] - 0.5*c_width] + else: + temp_offset = [offset[0] + 0.5*c_height, + offset[1] - 0.5*c_width] + self.switch_pos_list.append(temp_offset) + via_offset = self.switch_pos_list[-1] + if c: + self.add_inst(name="via{0}_{1}".format(v, w), + mod=c, + offset=self.switch_pos_list[-1], + rotate=90) + corner_offset = [via_offset[0] \ + - 0.5*(c_height + self.vert_layer_width), + via_offset[1] \ + + 0.5*(c_width - self.horiz_layer_width)] + self.draw_corner_wire(corner_offset) + offset = [offset[0], + (offset[1] + w[1] - v[1])] + + def draw_corner_wire(self, offset): + """ This function adds the corner squares since the center + line convention only draws to the center of the corner. + It must add squares on both layers.""" + self.add_rect(layer=self.vert_layer_name, + offset=offset, + width=self.vert_layer_width, + height=self.horiz_layer_width) + self.add_rect(layer=self.horiz_layer_name, + offset=offset, + width=self.vert_layer_width, + height=self.horiz_layer_width) + + def create_rectangles(self): + """ Create the actual rectangles on teh appropriate layers + using the position list of the corners. """ + offset = [0, 0] + # FIXME: This is not a good max/min value + xval = [1000000, -1000000] + yval = [1000000, -1000000] + pl = self.position_list # position list + for index in range(len(pl) - 1): + temp_offset = offset + if temp_offset[0] < xval[0]: + xval[0] = temp_offset[0] + if temp_offset[0] > xval[1]: + xval[1] = temp_offset[0] + if temp_offset[1] < yval[0]: + yval[0] = temp_offset[1] + if temp_offset[1] > yval[1]: + yval[1] = temp_offset[1] + if pl[index][0] != pl[index + 1][0]: + line_length = pl[index + 1][0] - pl[index][0] + temp_offset = [temp_offset[0], + temp_offset[1] - 0.5*self.horiz_layer_width] + if line_length < 0: + temp_offset = [temp_offset[0] + line_length, + temp_offset[1]] + self.add_line(layer_name=self.horiz_layer_name, + length=abs(line_length), + offset=temp_offset, + orientation="horizontal") + offset = [offset[0] + line_length, offset[1]] + elif pl[index][1] != pl[index + 1][1]: + line_length = pl[index + 1][1] - pl[index][1] + temp_offset = [temp_offset[0] - 0.5 * self.vert_layer_width, + temp_offset[1]] + if line_length < 0: + temp_offset = [temp_offset[0], + temp_offset[1] + line_length] + self.add_line(layer_name=self.vert_layer_name, + length=abs(line_length), + offset=temp_offset, + orientation="vertical") + offset = [offset[0], + offset[1] + line_length] + self.width = abs(xval[0] - xval[1]) + self.height = abs(yval[0] - yval[1]) + if self.via_layer_name != None: + self.height += self.c.width + else: + self.height += self.vert_layer_width + + def assert_node(self, A, B): + """ Check if the node movements are not big enough for the + technology sizes.""" + X_diff = abs(A[0] - B[0]) + Y_diff = abs(A[1] - B[1]) + [minX, minY] = self.node_to_node + if X_diff == 0 and Y_diff == 0: + pass + else: + if X_diff == 0: + assert Y_diff >= minY, "node" + \ + str(A) + " and node" + str(B) + \ + " are too close in Y. Minmum is " + str(minX) + if Y_diff == 0: + assert X_diff >= minX, "node" + \ + str(A) + " and node" + str(B) + \ + " are too close in X. Minmum is " + str(minY) diff --git a/compiler/wordline_driver.py b/compiler/wordline_driver.py new file mode 100644 index 00000000..d8fe27a9 --- /dev/null +++ b/compiler/wordline_driver.py @@ -0,0 +1,213 @@ +from tech import drc, parameter, cell +import debug +import design +from math import log +from math import sqrt +import math +from pinv import pinv +from nand_2 import nand_2 +from vector import vector +from globals import OPTS + +class wordline_driver(design.design): + """ + Creates a Wordline Driver + Generates the wordline-driver to drive the bitcell + """ + + def __init__(self, name, rows): + design.design.__init__(self, name) + + self.rows = rows + self.add_pins() + self.design_layout() + self.DRC_LVS() + + def add_pins(self): + # inputs to wordline_driver. + for i in range(self.rows): + self.add_pin("decode_out[{0}]".format(i)) + # Outputs from wordline_driver. + for i in range(self.rows): + self.add_pin("wl[{0}]".format(i)) + self.add_pin("clk") + self.add_pin("vdd") + self.add_pin("gnd") + + def design_layout(self): + self.add_layout() + self.offsets_of_gates() + self.create_layout() + + def add_layout(self): + self.inv = pinv(name="pinverter", + nmos_width=drc["minwidth_tx"], + beta=parameter["pinv_beta"]) + self.add_mod(self.inv) + + self.NAND2 = nand_2(name="pnand2", + nmos_width=2*drc["minwidth_tx"]) + self.add_mod(self.NAND2) + + + + + def offsets_of_gates(self): + self.x_offset0 = 2 * drc["minwidth_metal1"] + 5 * drc["metal1_to_metal1"] + self.x_offset1 = self.x_offset0 + self.inv.width + self.x_offset2 = self.x_offset1 + self.NAND2.width + + self.width = self.x_offset2 + self.inv.width + self.height = self.inv.height * self.rows + + # Defining offset postions + self.decode_out_positions = [] + self.clk_positions = [] + self.WL_positions = [] + self.vdd_positions = [] + self.gnd_positions = [] + + def create_layout(self): + # Clk connection + self.add_rect(layer="metal1", + offset=[drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"], + 2 * drc["minwidth_metal1"]], + width=drc["minwidth_metal1"], + height=self.height + 4*drc["minwidth_metal1"]) + self.clk_positions.append([drc["minwidth_metal1"] + 2*drc["metal1_to_metal1"], + self.height]) + self.add_label(text="clk", + layer="metal1", + offset=self.clk_positions[0]) + + for row in range(self.rows): + name_inv1 = "Wordline_driver_inv_clk%d" % (row) + name_nand = "Wordline_driver_nand%d" % (row) + name_inv2 = "Wordline_driver_inv%d" % (row) + + # Extend vdd and gnd of Wordline_driver + yoffset = (row + 1) * self.inv.height - 0.5 * drc["minwidth_metal2"] + self.add_rect(layer="metal2", + offset=[0, yoffset], + width=self.x_offset0, + height=drc["minwidth_metal2"]) + + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[drc["minwidth_metal1"], yoffset], + mirror="R90") + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.x_offset0 + drc["minwidth_metal1"], + yoffset], + mirror="R90") + inv_nand2B_connection_height = (abs(self.inv.Z_position.y + - self.NAND2.B_position.y) + + drc["minwidth_metal1"]) + + if (row % 2): + y_offset = self.inv.height*(row + 1) + name_inv1_offset = [self.x_offset0, y_offset] + nand2_offset=[self.x_offset1, y_offset] + inv2_offset=[self.x_offset2, y_offset] + inst_mirror = "MX" + cell_dir = vector(0,-1) + m1tm2_rotate=270 + m1tm2_mirror="R0" + else: + y_offset = self.inv.height*row + name_inv1_offset = [self.x_offset0, y_offset] + nand2_offset=[self.x_offset1, y_offset] + inv2_offset=[self.x_offset2, y_offset] + inst_mirror = "R0" + cell_dir = vector(0,1) + m1tm2_rotate=90 + m1tm2_mirror="MX" + + # add inv1 based on the info above + self.add_inst(name=name_inv1, + mod=self.inv, + offset=name_inv1_offset, + mirror=inst_mirror ) + self.connect_inst(["clk", "clk_bar[{0}]".format(row), + "vdd", "gnd"]) + # add nand 2 + self.add_inst(name=name_nand, + mod=self.NAND2, + offset=nand2_offset, + mirror=inst_mirror) + self.connect_inst(["decode_out[{0}]".format(row), + "clk_bar[{0}]".format(row), + "net[{0}]".format(row), + "vdd", "gnd"]) + # add inv2 + self.add_inst(name=name_inv2, + mod=self.inv, + offset=inv2_offset, + mirror=inst_mirror) + self.connect_inst(["net[{0}]".format(row), + "wl[{0}]".format(row), + "vdd", "gnd"]) + + # clk connection + clk_offset= [drc["minwidth_metal1"] + 2 * drc["metal1_to_metal1"], + y_offset + cell_dir.y * self.inv.A_position.y] + self.add_rect(layer="metal1", + offset=clk_offset, + width=self.x_offset0 - 2*drc["metal1_to_metal1"], + height=cell_dir.y *drc["minwidth_metal1"]) + # first inv to nand2 B + inv_to_nand2B_offset = [self.x_offset1 - drc["minwidth_metal1"], + y_offset + cell_dir.y * self.NAND2.B_position.y] + self.add_rect(layer="metal1", + offset=inv_to_nand2B_offset, + width=drc["minwidth_metal1"], + height=cell_dir.y*inv_nand2B_connection_height) + # Nand2 out to 2nd inv + nand2_to_2ndinv_offset =[self.x_offset2, + y_offset + cell_dir.y * self.NAND2.Z_position.y] + self.add_rect(layer="metal1", + offset=nand2_to_2ndinv_offset, + width=drc["minwidth_metal1"], + height=cell_dir.y * drc["minwidth_metal1"]) + # nand2 A connection + self.add_rect(layer="metal2", + offset=[0, y_offset + cell_dir.y * self.NAND2.A_position.y], + width=self.x_offset1, + height=cell_dir.y*drc["minwidth_metal2"]) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[self.x_offset1, + y_offset + cell_dir.y * self.NAND2.A_position.y], + rotate=m1tm2_rotate, + mirror=m1tm2_mirror) + self.add_via(layers=("metal1", "via1", "metal2"), + offset=[0, + y_offset +cell_dir.y*self.NAND2.A_position.y], + mirror=inst_mirror) + + + base_offset = vector(self.width, y_offset) + decode_out_offset = base_offset.scale(0,1)+self.NAND2.A_position.scale(cell_dir) + wl_offset = base_offset + self.inv.Z_position.scale(cell_dir) + vdd_offset = base_offset + self.inv.vdd_position.scale(cell_dir) + gnd_offset = base_offset + self.inv.gnd_position.scale(cell_dir) + + self.add_label(text="decode_out[{0}]".format(row), + layer="metal2", + offset=decode_out_offset) + self.add_rect(layer="metal1", + offset=wl_offset, + width=drc["minwidth_metal1"]*cell_dir.y, + height=drc["minwidth_metal1"]*cell_dir.y) + self.add_label(text="wl[{0}]".format(row), + layer="metal1", + offset=wl_offset) + self.add_label(text="gnd", + layer="metal1", + offset=gnd_offset) + self.add_label(text="vdd", + layer="metal1", + offset=vdd_offset) + + self.decode_out_positions.append(decode_out_offset) + self.WL_positions.append(wl_offset) + self.vdd_positions.append(vdd_offset) + self.gnd_positions.append(gnd_offset) diff --git a/compiler/write_driver.py b/compiler/write_driver.py new file mode 100644 index 00000000..88f97cd5 --- /dev/null +++ b/compiler/write_driver.py @@ -0,0 +1,23 @@ +import debug +import design +import utils +from tech import GDS,layer + +class write_driver(design.design): + """ + Tristate write driver to be active during write operations only. + This module implements the write driver cell used in the design. It + is a hand-made cell, so the layout and netlist should be available in + the technology library. + """ + + pins = ["din", "BL", "BR", "en", "gnd", "vdd"] + chars = utils.auto_measure_libcell(pins, "write_driver", GDS["unit"], layer["boundary"]) + + def __init__(self, name): + design.design.__init__(self, name) + debug.info(2, "Create write_driver object") + + self.width = write_driver.chars["width"] + self.height = write_driver.chars["height"] + diff --git a/compiler/write_driver_array.py b/compiler/write_driver_array.py new file mode 100644 index 00000000..aa1230ca --- /dev/null +++ b/compiler/write_driver_array.py @@ -0,0 +1,146 @@ +from math import log +import design +from tech import drc +import debug +from vector import vector +from globals import OPTS + +class write_driver_array(design.design): + """ + Array of tristate drivers to write to the bitlines through the column mux. + Dynamically generated write driver array of all bitlines. + """ + + def __init__(self, columns, word_size): + design.design.__init__(self, "write_driver_array") + debug.info(1, "Creating {0}".format(self.name)) + + c = reload(__import__(OPTS.config.write_driver)) + self.mod_write_driver = getattr(c, OPTS.config.write_driver) + self.write_driver_chars = self.mod_write_driver.chars + + self.columns = columns + self.word_size = word_size + self.words_per_row = columns / word_size + + self.add_pins() + self.create_layout() + self.DRC_LVS() + + def add_pins(self): + for i in range(self.word_size): + self.add_pin("data_in[{0}]".format(i)) + if (self.words_per_row == 1): + for i in range(self.word_size): + self.add_pin("bl[{0}]".format(i)) + self.add_pin("br[{0}]".format(i)) + else: + for i in range(self.word_size): + self.add_pin("bl_out[{0}]".format(i * self.words_per_row)) + self.add_pin("br_out[{0}]".format(i * self.words_per_row)) + self.add_pin("wen") + self.add_pin("vdd") + self.add_pin("gnd") + + def create_layout(self): + self.add_write_driver_module() + self.setup_layout_constants() + self.create_write_array() + self.add_metal_rails() + self.add_labels() + self.offset_all_coordinates() + + def add_write_driver_module(self): + self.driver = self.mod_write_driver("write_driver") + self.add_mod(self.driver) + + def setup_layout_constants(self): + self.width = self.columns * self.driver.width + self.height = self.height = self.driver.height + self.gnd_positions = [] + self.vdd_positions = [] + self.wen_positions = [] + self.BL_out_positions = [] + self.BR_out_positions = [] + self.driver_positions = [] + self.Data_in_positions = [] + + def create_write_array(self): + for i in range(self.word_size): + name = "Xwrite_driver%d" % i + x_off = (i* self.driver.width * self.words_per_row) + self.driver_positions.append(vector(x_off, 0)) + self.add_inst(name=name, + mod=self.driver, + offset=[x_off, 0]) + if (self.words_per_row == 1): + self.connect_inst(["data_in[{0}]".format(i), + "bl[{0}]".format(i), + "br[{0}]".format(i), + "wen", "vdd", "gnd"]) + else: + self.connect_inst(["data_in[{0}]".format(i), + "bl_out[{0}]".format(i * self.words_per_row), + "br_out[{0}]".format(i * self.words_per_row), + "wen", "vdd", "gnd"]) + + def add_metal_rails(self): + base = vector(0, - 0.5*drc["minwidth_metal1"]) + self.add_rect(layer="metal1", + offset=base + vector(self.write_driver_chars["en"]).scale(0, 1), + width=self.width - (self.words_per_row - 1) * self.driver.width, + height=drc['minwidth_metal1']) + self.add_rect(layer="metal1", + offset=base + vector(self.write_driver_chars["vdd"]).scale(0, 1), + width=self.width, + height=drc['minwidth_metal1']) + self.add_rect(layer="metal1", + offset=base + vector(self.write_driver_chars["gnd"]).scale(0, 1), + width=self.width, + height=drc['minwidth_metal1']) + + def add_labels(self): + for i in range(self.word_size): + base = vector(i * self.driver.width * self.words_per_row, 0) + BL_offset = base + self.write_driver_chars["BL"] + BR_offset = base + self.write_driver_chars["BR"] + + self.add_label(text="data_in[{0}]".format(i), + layer="metal2", + offset=base + self.write_driver_chars["din"]) + if (self.words_per_row == 1): + self.add_label(text="bl[{0}]".format(i), + layer="metal2", + offset=BL_offset) + self.add_label(text="br[{0}]".format(i), + layer="metal2", + offset=BR_offset) + else: + self.add_label(text="bl_out[{0}]".format(i*self.words_per_row), + layer="metal2", + offset=BL_offset) + self.add_label(text="br_out[{0}]".format(i*self.words_per_row), + layer="metal2", + offset=BR_offset) + self.BL_out_positions.append(BL_offset) + self.BR_out_positions.append(BR_offset) + self.Data_in_positions.append(base + self.write_driver_chars["din"]) + + base = vector(0, - 0.5 * drc["minwidth_metal1"]) + self.add_label(text="wen", + layer="metal1", + offset=base + vector(self.write_driver_chars["en"]).scale(0,1)) + self.add_label(text="vdd", + layer="metal1", + offset=base + vector(self.write_driver_chars["vdd"]).scale(0,1)) + self.add_label(text="gnd", + layer="metal1", + offset=base + vector(self.write_driver_chars["gnd"]).scale(0,1)) + self.wen_positions.append(base + vector(self.write_driver_chars["en"]).scale(0,1)) + self.vdd_positions.append(base + vector(self.write_driver_chars["vdd"]).scale(0,1)) + self.gnd_positions.append(base + vector(self.write_driver_chars["gnd"]).scale(0,1)) + + self.add_label(text="WRITE DRIVER", + layer="text", + offset=[self.width / 2.0, + self.height / 2.0]) diff --git a/regress_daemon.py b/regress_daemon.py new file mode 100755 index 00000000..0b7474f7 --- /dev/null +++ b/regress_daemon.py @@ -0,0 +1,247 @@ +#!/usr/bin/env python2.7 + +import sys +import os +import re +import smtplib + +import sys +import os +import re +import unittest + +import getpass +import datetime + + +USER = getpass.getuser() +TO_FIELD = "openram@soe.ucsc.edu" +#TO_FIELD = "mrg@ucsc.edu" +#TO_FIELD = "bchen12@ucsc.edu" +FROM_FIELD = USER+"@ucsc.edu" + + +LOCAL = "/soe/"+USER+"/unit_test" + +sys.path.append(LOCAL+"/trunk/setup_scripts") +sys.path.append(LOCAL+"/trunk/compiler") +sys.path.append(LOCAL+"/trunk/compiler/tests") + +TECH_NAME = "NONE" + +#REPOS = "http://gforge.soe.ucsc.edu/svn/openram/trunk" +#REPOS = "http://svn.soe.ucsc.edu/svn/openram/trunk" +REPOS = "gitosis@mada0.soe.ucsc.edu:openram.git" + + +MAIL = "/usr/sbin/sendmail" + +# Add warnings/errors to email message. +# Abort if warnings or errors. +def checkout(rev): + """ + FOR SVN + """ + print "Checking out revision " + rev + + # if it doesnt exist check it out + if not os.path.isdir(LOCAL): + cmd = "svn co -r" + rev + " " + REPOS + " " + LOCAL + if os.system(cmd): + print "Cannot check out repository: " + REPOS + sys.exit(-1) + # if it does exist just update to current revision + else: + try: + os.chdir(LOCAL) + except OSError: + print "Cannot find repository at " + LOCAL + sys.exit(-2) + cmd = "svn update -r" + rev + if os.system(cmd): + print "Cannot update repository: " + REPOS + sys.exit(-1) + + print "Done." + +def git_clone(): + """ + FOR GIT + """ + if not os.path.isdir(LOCAL): + print "Cloning git repository at " + LOCAL + cmd = "git clone " + REPOS + " " + LOCAL + if os.system(cmd): + email_error("Cannot clone out repository at " + LOCAL) + + else: + print "Pulling git repository at " + LOCAL + try: + os.chdir(LOCAL) + except OSError: + email_error("Cannot find repository at " + LOCAL) + + cmd = "git pull" + if os.system(cmd): + email_error("Cannot update repository at " + LOCAL) + print "Done." + +def remove_cached_files(): + """ + removes cached .pyc files + """ + if os.path.isdir(LOCAL): + print "Removing Cached Files" + cmd = "find {0} -type f -iname \*.pyc -delete".format(LOCAL) + os.system(cmd) + print "Done." + +def regress(): + print "Running Regressions" + try: + os.chdir(LOCAL+"/trunk/compiler") + except OSError: + print "Cannot find repository at " + LOCAL + sys.exit(-2) + + # get a list of all files in the python directory + files = os.listdir("tests") + # assume any file that ends in "test.py" in it is a regression test + nametest = re.compile("test\.py$", re.IGNORECASE) + tests = filter(nametest.search, files) + tests.sort() + + # import all of the modules + filenameToModuleName = lambda f: os.path.splitext(f)[0] + moduleNames = map(filenameToModuleName, tests) + modules = map(__import__, moduleNames) + + suite = unittest.TestSuite() + load = unittest.defaultTestLoader.loadTestsFromModule + + import traceback + for m in modules: + try: + t=load(m) + suite.addTests(t) + except ImportError: + print traceback.format_exc() + #email_error(traceback.format_exc()) + + result=unittest.TextTestRunner(verbosity=2).run(suite) + + print "Done." + + return (tests,result) + +def email_start(toField,subj): + p = os.popen("%s -t" % MAIL, 'w') + + p.write("From: <"+FROM_FIELD+">\n") + p.write("Subject: {0} {1} ({2})\n".format(subj,datetime.datetime.now().replace(second=0,microsecond=0),TECH_NAME)) + p.write("To: <"+toField+">\n") # replace with openram + p.write("\n\n") + + p.write("{0} Pacific Time\n".format(datetime.datetime.now().replace(second=0,microsecond=0))) + p.write("\nTECHNOLOGY: {0}\n\n".format(TECH_NAME)) + return p; + +def email_failure(sysinfo): + type, value, tb = sysinfo + import traceback + p = email_start(TO_FIELD,"ERROR running regression") + p.write(str(value)+"\n") + traceback.print_tb(tb,None,p) + + p.write("\nLAST 5 CHECK-INS:\n\n") + try: + os.chdir(LOCAL) + except OSError: + print "Cannot find repository at " + LOCAL + sys.exit(-2) + log=os.popen("git log -5").read() + p.write(log) + + p.close() + +def email_results(tests,results): + if results.wasSuccessful(): + p = email_start(TO_FIELD,"PASSED regression result") + p.write("TESTS:\n") + for t in tests: + p.write(t + "\n") + + p.write("\nREGRESSION TEST RESULTS:\n\n") + else: + p = email_start(TO_FIELD,"FAILED regression result") + p.write("TESTS:\n") + for t in tests: + p.write(t + "\n") + + p.write("\nREGRESSION TEST RESULTS:\n\n") + + for failure in results.failures: + p.write("FAIL: "+str(failure[0])+"\n") + p.write(" "+str(failure[1])+"\n") + p.write("----------------------------\n") + + for error in results.errors: + p.write("ERROR: "+str(error[0])+"\n") + p.write(" "+str(error[1])+"\n") + p.write("----------------------------\n") + + + for skip in results.skipped: + p.write("SKIP:"+str(skip[0])+"\n") + p.write("----------------------------\n") + + p.write("\nLAST 5 CHECK-INS:\n\n") + try: + os.chdir(LOCAL) + except OSError: + print "Cannot find repository at " + LOCAL + sys.exit(-2) + log=os.popen("git log -5").read() + p.write(log) + + p.close() + +def email_error(error_msg): + p = email_start(TO_FIELD,"FAILED to run regression") + + p.write("UNABLE TO RUN REGRESSION TESTS\n") + p.write(error_msg) + + p.write("\nLAST 5 CHECK-INS:\n") + try: + os.chdir(LOCAL) + except OSError: + print "Cannot find repository at " + LOCAL + sys.exit(-2) + + log=os.popen("git log -5").read() + p.write(log) + + p.close() + + sys.exit(-3) + +if __name__ == "__main__": + # We must clone the repository before parsing any arguments + remove_cached_files() + git_clone() + + # now we can import the globals which sets up everything + import globals + globals.parse_args() + # Update our local copy to the correct tech name for the email + #globals TECH_NAME + TECH_NAME = globals.OPTS.tech_name + + try: + (tests,results) = regress() + email_results(tests,results) + except: + email_failure(sys.exc_info()) + sys.exit(0) + diff --git a/regress_daemon.sh b/regress_daemon.sh new file mode 100755 index 00000000..b324d249 --- /dev/null +++ b/regress_daemon.sh @@ -0,0 +1,14 @@ +#Check out a copy of the repository: +#cd ~ +#git clone gitosis@mada0.cse.ucsc.edu:openram.git unit_test +#crontab -l views the crontab +#crontab -e edits the crontab +#Add a command like this to the crontab to call the regression script +# m h dom mon dow user command +#0 0,12 * * * /mada/users/wubin6666/unit_test/trunk/regress_daemon.sh +!/bin/bash +source /mada/software/setup.sh +export OPENRAM_HOME="/soe/mrg/unit_test/trunk/compiler" +export OPENRAM_TECH="/soe/mrg/unit_test/trunk/technology" +python ${HOME}/unit_test/trunk/regress_daemon.py -t freepdk45 +python ${HOME}/unit_test/trunk/regress_daemon.py -t scn3me_subm diff --git a/technology/freepdk45/gds_lib/cell_6t.gds b/technology/freepdk45/gds_lib/cell_6t.gds new file mode 100644 index 00000000..077e567b Binary files /dev/null and b/technology/freepdk45/gds_lib/cell_6t.gds differ diff --git a/technology/freepdk45/gds_lib/ms_flop.gds b/technology/freepdk45/gds_lib/ms_flop.gds new file mode 100644 index 00000000..29da4bcb Binary files /dev/null and b/technology/freepdk45/gds_lib/ms_flop.gds differ diff --git a/technology/freepdk45/gds_lib/replica_cell_6t.gds b/technology/freepdk45/gds_lib/replica_cell_6t.gds new file mode 100644 index 00000000..c9ae078b Binary files /dev/null and b/technology/freepdk45/gds_lib/replica_cell_6t.gds differ diff --git a/technology/freepdk45/gds_lib/sense_amp.gds b/technology/freepdk45/gds_lib/sense_amp.gds new file mode 100644 index 00000000..6200a969 Binary files /dev/null and b/technology/freepdk45/gds_lib/sense_amp.gds differ diff --git a/technology/freepdk45/gds_lib/tri_gate.gds b/technology/freepdk45/gds_lib/tri_gate.gds new file mode 100644 index 00000000..7bd632bd Binary files /dev/null and b/technology/freepdk45/gds_lib/tri_gate.gds differ diff --git a/technology/freepdk45/gds_lib/write_driver.gds b/technology/freepdk45/gds_lib/write_driver.gds new file mode 100644 index 00000000..fee42d99 Binary files /dev/null and b/technology/freepdk45/gds_lib/write_driver.gds differ diff --git a/technology/freepdk45/layers.map b/technology/freepdk45/layers.map new file mode 100644 index 00000000..8c0c18d2 --- /dev/null +++ b/technology/freepdk45/layers.map @@ -0,0 +1,30 @@ +active drawing 1 0 +pwell drawing 2 0 +nwell drawing 3 0 +nimplant drawing 4 0 +pimplant drawing 5 0 +vtg drawing 6 0 +vth drawing 7 0 +thkox drawing 8 0 +poly drawing 9 0 +contact drawing 10 0 +metal1 drawing 11 0 +via1 drawing 12 0 +metal2 drawing 13 0 +via2 drawing 14 0 +metal3 drawing 15 0 +via3 drawing 16 0 +metal4 drawing 17 0 +via4 drawing 18 0 +metal5 drawing 19 0 +via5 drawing 20 0 +metal6 drawing 21 0 +via6 drawing 22 0 +metal7 drawing 23 0 +via7 drawing 24 0 +metal8 drawing 25 0 +via8 drawing 26 0 +metal9 drawing 27 0 +via9 drawing 28 0 +metal10 drawing 29 0 +text drawing 239 0 diff --git a/technology/freepdk45/lib/.cadence/cadence.signature.xml b/technology/freepdk45/lib/.cadence/cadence.signature.xml new file mode 100755 index 00000000..64bab7f4 --- /dev/null +++ b/technology/freepdk45/lib/.cadence/cadence.signature.xml @@ -0,0 +1,4 @@ + + + + Do not remove or change this .cadence directory signature file. diff --git a/technology/freepdk45/lib/.cadence/dfII/viva/viva.ini b/technology/freepdk45/lib/.cadence/dfII/viva/viva.ini new file mode 100755 index 00000000..dfde08be --- /dev/null +++ b/technology/freepdk45/lib/.cadence/dfII/viva/viva.ini @@ -0,0 +1,43 @@ +[browser] +orientation=horizontal +pathlist=/mada/users/cpeters/Working/simulations/latch.run1/si.raw, /mada/users/cpeters/Working/simulations/out_latch.run1/si.raw, /mada/users/cpeters/Working/simulations/ms_ff.run1/si.raw +pos=@Point(1118 92) +size=@Size(214 928) +splitter=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x2\0\0\0\0\0\0\0\xc0\x1\0\0\0\x4\x1\0\0\0\x1) + +[fullCalculator] +_bufferState=@Invalid() +_funcPanelName=undefined +_funcPanelSplitter=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x2\0\0\0\xb3\0\0\x1\xb4\0\0\0\0\x4\x1\0\0\0\x2) +_funcPanelType=FuncListType +_keyPadSplitter=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x2\0\0\x2\xf4\0\0\0\x64\x1\0\0\0\x4\x1\0\0\0\x1) +_resultsDir= +_signalSelectionHistory=@Invalid() +_splitter=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x2\0\0\0\xb3\0\0\0\0\x1\0\0\0\x4\x1\0\0\0\x2) +_testName= +clipSelectionMode=true +defaultCategory=Special Functions +displayContext=true +mathToolBar=false +plotStyle=Append +pos=@Point(582 186) +rpnMode=true +schematicAnalyses="tran,ac,dc,sweptDc,info,noise,rf" +schematicToolBar=true +showKeyPad=true +showStack=false +signalSelection=Off +size=@Size(866 834) +trigToolBar=false +userButton1=user 1, undefined +userButton10=user 1, undefined +userButton11=user 1, undefined +userButton12=user 1, undefined +userButton2=user 2, undefined +userButton3=user 3, undefined +userButton4=user 4, undefined +userButton5=user 5, undefined +userButton6=user 6, undefined +userButton7=user 7, undefined +userButton8=user 8, undefined +userButton9=user 9, undefined diff --git a/technology/freepdk45/lib/.cdsinit b/technology/freepdk45/lib/.cdsinit new file mode 100755 index 00000000..14e60079 --- /dev/null +++ b/technology/freepdk45/lib/.cdsinit @@ -0,0 +1,47 @@ + +envSetVal( "graphic" "drfPath" 'string + strcat( getShellEnvVar("PDK_DIR") "/ncsu_basekit/cdssetup/display.drf")) + + +loadi( strcat( getShellEnvVar("PDK_DIR") "/ncsu_basekit/cdssetup/common_bindkeys.il")) +if( getShellEnvVar("MGC_HOME") then + loadi( strcat( getShellEnvVar("MGC_HOME") "/shared/pkgs/icv/tools/queryskl/calibre.OA.skl")) +) ;if +procedure( prependNCSUCDKInstallPath( dir) + strcat( getShellEnvVar("PDK_DIR") "/ncsu_basekit/" dir)) +(envLoadVals +?envFile ( prependNCSUCDKInstallPath "cdssetup/cdsenv") +?tool "layout") + +printf( strcat( +"---------------------------------------------------------------------------\n" +"Welcome to the FreePDK 45nm Free, Open-Source Process Design Kit\n" +"\n" +"This initiative is brought to you by the Semiconductor Research\n" +"Corporation (SRC), the National Science Foundation (NSF), Silicon\n" +"Integration Initiative (Si2), Mentor Graphics, and Synopsys.\n" +"\n" +"This version of the kit was created by Rhett Davis, Paul Franzon,\n" +"Michael Bucher, and Sunil Basavarajaiah of North Carolina State University,\n" +"and James Stine and Ivan Castellanos of Oklahoma State University.\n" +"\n" +"Contributions and modifications to this kit are welcomed and encouraged.\n" +"\n" +"Copyright 2008 North Carolina State University (ncsu_basekit subtree)\n" +" and Oklahoma State University (osu_soc subtree)\n" +"\n" +"Licensed under the Apache License, Version 2.0 (the \"License\");\n" +"you may not use this file except in compliance with the License.\n" +"You may obtain a copy of the License at\n" +"\n" +" http://www.apache.org/licenses/LICENSE-2.0\n" +"\n" +"Unless required by applicable law or agreed to in writing, software\n" +"distributed under the License is distributed on an \"AS IS\" BASIS,\n" +"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" +"See the License for the specific language governing permissions and\n" +"limitations under the License.\n" +"---------------------------------------------------------------------------\n" +"\n" +"Done loading FreePDK customizations.\n" +)) diff --git a/technology/freepdk45/lib/.runset.calibre.drc b/technology/freepdk45/lib/.runset.calibre.drc new file mode 100755 index 00000000..35186ce6 --- /dev/null +++ b/technology/freepdk45/lib/.runset.calibre.drc @@ -0,0 +1,2 @@ +*drcRulesFile: $PDK_DIR/ncsu_basekit/techfile/calibre/calibreDRC.rul + diff --git a/technology/freepdk45/lib/.runset.calibre.lfd b/technology/freepdk45/lib/.runset.calibre.lfd new file mode 100755 index 00000000..f770b0a1 --- /dev/null +++ b/technology/freepdk45/lib/.runset.calibre.lfd @@ -0,0 +1,2 @@ +*drcRulesFile: $PDK_DIR/ncsu_basekit/techfile/calibre/calibreLFD.rul + diff --git a/technology/freepdk45/lib/.runset.calibre.lvs b/technology/freepdk45/lib/.runset.calibre.lvs new file mode 100755 index 00000000..aa47aacb --- /dev/null +++ b/technology/freepdk45/lib/.runset.calibre.lvs @@ -0,0 +1,3 @@ +*lvsRulesFile: $PDK_DIR/ncsu_basekit/techfile/calibre/calibreLVS.rul + + diff --git a/technology/freepdk45/lib/.runset.calibre.pex b/technology/freepdk45/lib/.runset.calibre.pex new file mode 100755 index 00000000..a1f8b6d9 --- /dev/null +++ b/technology/freepdk45/lib/.runset.calibre.pex @@ -0,0 +1,3 @@ +*pexRulesFile: $PDK_DIR/ncsu_basekit/techfile/calibre/calibrexRC.rul + + diff --git a/technology/freepdk45/lib/cds.lib b/technology/freepdk45/lib/cds.lib new file mode 100644 index 00000000..e3c63316 --- /dev/null +++ b/technology/freepdk45/lib/cds.lib @@ -0,0 +1,8 @@ +DEFINE analogLib $CDSHOME/tools/dfII/etc/cdslib/artist/analogLib +DEFINE US_8ths $CDSHOME/tools/dfII/etc/cdslib/sheets/US_8ths +DEFINE basic $CDSHOME/tools/dfII/etc/cdslib/basic +DEFINE cdsDefTechLib $CDSHOME/tools/dfII/etc/cdsDefTechLib +DEFINE NCSU_TechLib_FreePDK45 $PDK_DIR/ncsu_basekit/lib/NCSU_TechLib_FreePDK45 +DEFINE NCSU_Devices_FreePDK45 $PDK_DIR/ncsu_basekit/lib/NCSU_Devices_FreePDK45 +DEFINE sram $OPENRAM_HOME/lib/sram +DEFINE sub_sram $OPENRAM_HOME/lib/sub_sram diff --git a/technology/freepdk45/lib/lib.defs b/technology/freepdk45/lib/lib.defs new file mode 100644 index 00000000..c2a6982c --- /dev/null +++ b/technology/freepdk45/lib/lib.defs @@ -0,0 +1,16 @@ +DEFINE analogLib $CDSHOME/tools/dfII/etc/cdslib/artist/analogLib +ASSIGN analogLib libMode shared +DEFINE US_8ths $CDSHOME/tools/dfII/etc/cdslib/sheets/US_8ths +ASSIGN US_8ths libMode shared +DEFINE basic $CDSHOME/tools/dfII/etc/cdslib/basic +ASSIGN basic libMode shared +DEFINE cdsDefTechLib $CDSHOME/tools/dfII/etc/cdsDefTechLib +ASSIGN cdsDefTechLib libMode shared +DEFINE NCSU_TechLib_FreePDK45 $PDK_DIR/ncsu_basekit/lib/NCSU_TechLib_FreePDK45 +ASSIGN NCSU_TechLib_FreePDK45 libMode shared +DEFINE NCSU_Devices_FreePDK45 $PDK_DIR/ncsu_basekit/lib/NCSU_Devices_FreePDK45 +ASSIGN NCSU_Devices_FreePDK45 libMode shared +DEFINE sram $OPENRAM_HOME/lib/sram +ASSIGN sram libMode shared +DEFINE sub_sram $OPENRAM_HOME/lib/sub_sram +ASSIGN sub_sram libMode shared diff --git a/technology/freepdk45/lib/sram/.oalib b/technology/freepdk45/lib/sram/.oalib new file mode 100755 index 00000000..21ffef89 --- /dev/null +++ b/technology/freepdk45/lib/sram/.oalib @@ -0,0 +1,6 @@ + + + + + diff --git a/technology/freepdk45/lib/sram/addr_ff/layout/layout.oa b/technology/freepdk45/lib/sram/addr_ff/layout/layout.oa new file mode 100755 index 00000000..60a18e10 Binary files /dev/null and b/technology/freepdk45/lib/sram/addr_ff/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/addr_ff/layout/master.tag b/technology/freepdk45/lib/sram/addr_ff/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/addr_ff/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/addr_ff/schematic/data.dm b/technology/freepdk45/lib/sram/addr_ff/schematic/data.dm new file mode 100755 index 00000000..11925091 Binary files /dev/null and b/technology/freepdk45/lib/sram/addr_ff/schematic/data.dm differ diff --git a/technology/freepdk45/lib/sram/addr_ff/schematic/master.tag b/technology/freepdk45/lib/sram/addr_ff/schematic/master.tag new file mode 100755 index 00000000..26be1bef --- /dev/null +++ b/technology/freepdk45/lib/sram/addr_ff/schematic/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +sch.oa diff --git a/technology/freepdk45/lib/sram/addr_ff/schematic/sch.oa b/technology/freepdk45/lib/sram/addr_ff/schematic/sch.oa new file mode 100755 index 00000000..eabefc20 Binary files /dev/null and b/technology/freepdk45/lib/sram/addr_ff/schematic/sch.oa differ diff --git a/technology/freepdk45/lib/sram/addr_latch/layout/layout.oa b/technology/freepdk45/lib/sram/addr_latch/layout/layout.oa new file mode 100755 index 00000000..3525ac02 Binary files /dev/null and b/technology/freepdk45/lib/sram/addr_latch/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/addr_latch/layout/master.tag b/technology/freepdk45/lib/sram/addr_latch/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/addr_latch/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/addr_latch/schematic/data.dm b/technology/freepdk45/lib/sram/addr_latch/schematic/data.dm new file mode 100755 index 00000000..6a4c9f97 Binary files /dev/null and b/technology/freepdk45/lib/sram/addr_latch/schematic/data.dm differ diff --git a/technology/freepdk45/lib/sram/addr_latch/schematic/master.tag b/technology/freepdk45/lib/sram/addr_latch/schematic/master.tag new file mode 100755 index 00000000..26be1bef --- /dev/null +++ b/technology/freepdk45/lib/sram/addr_latch/schematic/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +sch.oa diff --git a/technology/freepdk45/lib/sram/addr_latch/schematic/sch.oa b/technology/freepdk45/lib/sram/addr_latch/schematic/sch.oa new file mode 100755 index 00000000..74b3999f Binary files /dev/null and b/technology/freepdk45/lib/sram/addr_latch/schematic/sch.oa differ diff --git a/technology/freepdk45/lib/sram/array/layout/layout.oa b/technology/freepdk45/lib/sram/array/layout/layout.oa new file mode 100755 index 00000000..8af1983b Binary files /dev/null and b/technology/freepdk45/lib/sram/array/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/array/layout/master.tag b/technology/freepdk45/lib/sram/array/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/array/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/cdsinfo.tag b/technology/freepdk45/lib/sram/cdsinfo.tag new file mode 100644 index 00000000..a8dcdcb4 --- /dev/null +++ b/technology/freepdk45/lib/sram/cdsinfo.tag @@ -0,0 +1,40 @@ +# +# This is a cdsinfo.tag file. +# +# See the "Cadence Application Infrastructure Reference Manual" for +# details on the format of this file, its semantics, and its use. +# +# The `#' character denotes a comment. Removing the leading `#' +# character from any of the entries below will activate them. +# +# CDSLIBRARY entry - add this entry if the directory containing +# this cdsinfo.tag file is the root of a Cadence library. +# CDSLIBRARY +# +# CDSLIBCHECK - set this entry to require that libraries have +# a cdsinfo.tag file with a CDSLIBRARY entry. Legal values are +# ON and OFF. By default (OFF), directories named in a cds.lib file +# do not have to have a cdsinfo.tag file with a CDSLIBRARY entry. +# CDSLIBCHECK ON +# +# DMTYPE - set this entry to define the DM system for Cadence's +# Generic DM facility. Values will be shifted to lower case. +# DMTYPE none +# DMTYPE crcs +# DMTYPE tdm +# DMTYPE sync +# +# NAMESPACE - set this entry to define the library namespace according +# to the type of machine on which the data is stored. Legal values are +# `LibraryNT' and +# `LibraryUnix'. +# NAMESPACE LibraryUnix +# +# Other entries may be added for use by specific applications as +# name-value pairs. Application documentation will describe the +# use and behaviour of these entries when appropriate. +# +# Current Settings: +# +CDSLIBRARY +NAMESPACE LibraryUnix diff --git a/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa b/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa new file mode 100755 index 00000000..d19dccf7 Binary files /dev/null and b/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa- b/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa- new file mode 100755 index 00000000..7f81fccb Binary files /dev/null and b/technology/freepdk45/lib/sram/cell_10t/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/cell_10t/layout/master.tag b/technology/freepdk45/lib/sram/cell_10t/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/cell_10t/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/cell_10t/schematic/data.dm b/technology/freepdk45/lib/sram/cell_10t/schematic/data.dm new file mode 100755 index 00000000..ff96d2ab Binary files /dev/null and b/technology/freepdk45/lib/sram/cell_10t/schematic/data.dm differ diff --git a/technology/freepdk45/lib/sram/cell_10t/schematic/master.tag b/technology/freepdk45/lib/sram/cell_10t/schematic/master.tag new file mode 100755 index 00000000..26be1bef --- /dev/null +++ b/technology/freepdk45/lib/sram/cell_10t/schematic/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +sch.oa diff --git a/technology/freepdk45/lib/sram/cell_10t/schematic/sch.oa b/technology/freepdk45/lib/sram/cell_10t/schematic/sch.oa new file mode 100755 index 00000000..8f2d4cd0 Binary files /dev/null and b/technology/freepdk45/lib/sram/cell_10t/schematic/sch.oa differ diff --git a/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa b/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa new file mode 100755 index 00000000..2c763698 Binary files /dev/null and b/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa- b/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa- new file mode 100755 index 00000000..43f013e8 Binary files /dev/null and b/technology/freepdk45/lib/sram/cell_6t/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/cell_6t/layout/master.tag b/technology/freepdk45/lib/sram/cell_6t/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/cell_6t/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/cell_6t/schematic/data.dm b/technology/freepdk45/lib/sram/cell_6t/schematic/data.dm new file mode 100755 index 00000000..c2d60e51 Binary files /dev/null and b/technology/freepdk45/lib/sram/cell_6t/schematic/data.dm differ diff --git a/technology/freepdk45/lib/sram/cell_6t/schematic/master.tag b/technology/freepdk45/lib/sram/cell_6t/schematic/master.tag new file mode 100755 index 00000000..26be1bef --- /dev/null +++ b/technology/freepdk45/lib/sram/cell_6t/schematic/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +sch.oa diff --git a/technology/freepdk45/lib/sram/cell_6t/schematic/sch.oa b/technology/freepdk45/lib/sram/cell_6t/schematic/sch.oa new file mode 100755 index 00000000..51940d44 Binary files /dev/null and b/technology/freepdk45/lib/sram/cell_6t/schematic/sch.oa differ diff --git a/technology/freepdk45/lib/sram/clock_nor/layout/layout.oa b/technology/freepdk45/lib/sram/clock_nor/layout/layout.oa new file mode 100755 index 00000000..7bf4d583 Binary files /dev/null and b/technology/freepdk45/lib/sram/clock_nor/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/clock_nor/layout/master.tag b/technology/freepdk45/lib/sram/clock_nor/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/clock_nor/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/data.dm b/technology/freepdk45/lib/sram/data.dm new file mode 100644 index 00000000..329ccd3c Binary files /dev/null and b/technology/freepdk45/lib/sram/data.dm differ diff --git a/technology/freepdk45/lib/sram/delay_line/layout/layout.oa b/technology/freepdk45/lib/sram/delay_line/layout/layout.oa new file mode 100755 index 00000000..2a84a075 Binary files /dev/null and b/technology/freepdk45/lib/sram/delay_line/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/delay_line/layout/master.tag b/technology/freepdk45/lib/sram/delay_line/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/delay_line/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/dinv/layout/layout.oa b/technology/freepdk45/lib/sram/dinv/layout/layout.oa new file mode 100755 index 00000000..a7735463 Binary files /dev/null and b/technology/freepdk45/lib/sram/dinv/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/dinv/layout/master.tag b/technology/freepdk45/lib/sram/dinv/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/dinv/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/dinv_mx/layout/layout.oa b/technology/freepdk45/lib/sram/dinv_mx/layout/layout.oa new file mode 100755 index 00000000..ca7c13d2 Binary files /dev/null and b/technology/freepdk45/lib/sram/dinv_mx/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/dinv_mx/layout/master.tag b/technology/freepdk45/lib/sram/dinv_mx/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/dinv_mx/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/inv/layout/layout.oa b/technology/freepdk45/lib/sram/inv/layout/layout.oa new file mode 100755 index 00000000..e98db560 Binary files /dev/null and b/technology/freepdk45/lib/sram/inv/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/inv/layout/master.tag b/technology/freepdk45/lib/sram/inv/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/inv/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/inv_clk/layout/layout.oa b/technology/freepdk45/lib/sram/inv_clk/layout/layout.oa new file mode 100755 index 00000000..20790f89 Binary files /dev/null and b/technology/freepdk45/lib/sram/inv_clk/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/inv_clk/layout/master.tag b/technology/freepdk45/lib/sram/inv_clk/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/inv_clk/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/inv_col/layout/layout.oa b/technology/freepdk45/lib/sram/inv_col/layout/layout.oa new file mode 100755 index 00000000..3335bece Binary files /dev/null and b/technology/freepdk45/lib/sram/inv_col/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/inv_col/layout/master.tag b/technology/freepdk45/lib/sram/inv_col/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/inv_col/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/inv_dec/layout/layout.oa b/technology/freepdk45/lib/sram/inv_dec/layout/layout.oa new file mode 100755 index 00000000..3ec1f587 Binary files /dev/null and b/technology/freepdk45/lib/sram/inv_dec/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/inv_dec/layout/master.tag b/technology/freepdk45/lib/sram/inv_dec/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/inv_dec/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/inv_nor/layout/layout.oa b/technology/freepdk45/lib/sram/inv_nor/layout/layout.oa new file mode 100755 index 00000000..6c23bc4e Binary files /dev/null and b/technology/freepdk45/lib/sram/inv_nor/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/inv_nor/layout/master.tag b/technology/freepdk45/lib/sram/inv_nor/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/inv_nor/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/mux_a/layout/layout.oa b/technology/freepdk45/lib/sram/mux_a/layout/layout.oa new file mode 100755 index 00000000..16e1369b Binary files /dev/null and b/technology/freepdk45/lib/sram/mux_a/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/mux_a/layout/master.tag b/technology/freepdk45/lib/sram/mux_a/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/mux_a/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/mux_abar/layout/layout.oa b/technology/freepdk45/lib/sram/mux_abar/layout/layout.oa new file mode 100755 index 00000000..126897ec Binary files /dev/null and b/technology/freepdk45/lib/sram/mux_abar/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/mux_abar/layout/master.tag b/technology/freepdk45/lib/sram/mux_abar/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/mux_abar/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/nor_1/layout/layout.oa b/technology/freepdk45/lib/sram/nor_1/layout/layout.oa new file mode 100755 index 00000000..b0d79792 Binary files /dev/null and b/technology/freepdk45/lib/sram/nor_1/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/nor_1/layout/master.tag b/technology/freepdk45/lib/sram/nor_1/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/nor_1/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/nor_1_mx/layout/layout.oa b/technology/freepdk45/lib/sram/nor_1_mx/layout/layout.oa new file mode 100755 index 00000000..82764ea0 Binary files /dev/null and b/technology/freepdk45/lib/sram/nor_1_mx/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/nor_1_mx/layout/master.tag b/technology/freepdk45/lib/sram/nor_1_mx/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/nor_1_mx/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/ntap/layout/layout.oa b/technology/freepdk45/lib/sram/ntap/layout/layout.oa new file mode 100755 index 00000000..aa2cdc19 Binary files /dev/null and b/technology/freepdk45/lib/sram/ntap/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/ntap/layout/master.tag b/technology/freepdk45/lib/sram/ntap/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/ntap/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa b/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa new file mode 100755 index 00000000..00d0b24e Binary files /dev/null and b/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa- b/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa- new file mode 100755 index 00000000..211c52e7 Binary files /dev/null and b/technology/freepdk45/lib/sram/out_inv_16/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/out_inv_16/layout/master.tag b/technology/freepdk45/lib/sram/out_inv_16/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/out_inv_16/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa b/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa new file mode 100755 index 00000000..c1480a3c Binary files /dev/null and b/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa- b/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa- new file mode 100755 index 00000000..94f550c6 Binary files /dev/null and b/technology/freepdk45/lib/sram/out_inv_2/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/out_inv_2/layout/master.tag b/technology/freepdk45/lib/sram/out_inv_2/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/out_inv_2/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/out_inv_2/schematic/data.dm b/technology/freepdk45/lib/sram/out_inv_2/schematic/data.dm new file mode 100755 index 00000000..7cfdc2da Binary files /dev/null and b/technology/freepdk45/lib/sram/out_inv_2/schematic/data.dm differ diff --git a/technology/freepdk45/lib/sram/out_inv_2/schematic/master.tag b/technology/freepdk45/lib/sram/out_inv_2/schematic/master.tag new file mode 100755 index 00000000..26be1bef --- /dev/null +++ b/technology/freepdk45/lib/sram/out_inv_2/schematic/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +sch.oa diff --git a/technology/freepdk45/lib/sram/out_inv_2/schematic/sch.oa b/technology/freepdk45/lib/sram/out_inv_2/schematic/sch.oa new file mode 100755 index 00000000..1f45ca4a Binary files /dev/null and b/technology/freepdk45/lib/sram/out_inv_2/schematic/sch.oa differ diff --git a/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa b/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa new file mode 100755 index 00000000..e0690088 Binary files /dev/null and b/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa- b/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa- new file mode 100755 index 00000000..e0690088 Binary files /dev/null and b/technology/freepdk45/lib/sram/out_inv_4/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/out_inv_4/layout/master.tag b/technology/freepdk45/lib/sram/out_inv_4/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/out_inv_4/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/output_latch/layout/layout.oa b/technology/freepdk45/lib/sram/output_latch/layout/layout.oa new file mode 100755 index 00000000..ff29df6d Binary files /dev/null and b/technology/freepdk45/lib/sram/output_latch/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/output_latch/layout/layout.oa- b/technology/freepdk45/lib/sram/output_latch/layout/layout.oa- new file mode 100755 index 00000000..6b39e820 Binary files /dev/null and b/technology/freepdk45/lib/sram/output_latch/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/output_latch/layout/master.tag b/technology/freepdk45/lib/sram/output_latch/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/output_latch/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/precharge/layout/layout.oa b/technology/freepdk45/lib/sram/precharge/layout/layout.oa new file mode 100755 index 00000000..8e1768fc Binary files /dev/null and b/technology/freepdk45/lib/sram/precharge/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/precharge/layout/master.tag b/technology/freepdk45/lib/sram/precharge/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/precharge/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/ptap/layout/layout.oa b/technology/freepdk45/lib/sram/ptap/layout/layout.oa new file mode 100755 index 00000000..f55a28cd Binary files /dev/null and b/technology/freepdk45/lib/sram/ptap/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/ptap/layout/master.tag b/technology/freepdk45/lib/sram/ptap/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/ptap/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/sense_amp/layout.oa.backup b/technology/freepdk45/lib/sram/sense_amp/layout.oa.backup new file mode 100755 index 00000000..99f09b81 Binary files /dev/null and b/technology/freepdk45/lib/sram/sense_amp/layout.oa.backup differ diff --git a/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa b/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa new file mode 100755 index 00000000..33c3ce0e Binary files /dev/null and b/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa- b/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa- new file mode 100755 index 00000000..b69654d6 Binary files /dev/null and b/technology/freepdk45/lib/sram/sense_amp/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/sense_amp/layout/master.tag b/technology/freepdk45/lib/sram/sense_amp/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/sense_amp/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/tgate/layout/layout.oa b/technology/freepdk45/lib/sram/tgate/layout/layout.oa new file mode 100755 index 00000000..df7fd9be Binary files /dev/null and b/technology/freepdk45/lib/sram/tgate/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/tgate/layout/layout.oa- b/technology/freepdk45/lib/sram/tgate/layout/layout.oa- new file mode 100755 index 00000000..df7fd9be Binary files /dev/null and b/technology/freepdk45/lib/sram/tgate/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/tgate/layout/master.tag b/technology/freepdk45/lib/sram/tgate/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/tgate/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/sram/write_driver/layout/layout.oa b/technology/freepdk45/lib/sram/write_driver/layout/layout.oa new file mode 100755 index 00000000..79c3ed15 Binary files /dev/null and b/technology/freepdk45/lib/sram/write_driver/layout/layout.oa differ diff --git a/technology/freepdk45/lib/sram/write_driver/layout/layout.oa- b/technology/freepdk45/lib/sram/write_driver/layout/layout.oa- new file mode 100755 index 00000000..e5f3cdf7 Binary files /dev/null and b/technology/freepdk45/lib/sram/write_driver/layout/layout.oa- differ diff --git a/technology/freepdk45/lib/sram/write_driver/layout/master.tag b/technology/freepdk45/lib/sram/write_driver/layout/master.tag new file mode 100755 index 00000000..431d8f04 --- /dev/null +++ b/technology/freepdk45/lib/sram/write_driver/layout/master.tag @@ -0,0 +1,2 @@ +-- Master.tag File, Rev:1.0 +layout.oa diff --git a/technology/freepdk45/lib/stream_all_gds.sh b/technology/freepdk45/lib/stream_all_gds.sh new file mode 100644 index 00000000..8a2c1048 --- /dev/null +++ b/technology/freepdk45/lib/stream_all_gds.sh @@ -0,0 +1,8 @@ + +for i in addr_ff clock_nor dinv inv_clk inv_nor nor_1 out_inv_16 output_latch sense_amp addr_latch cell_10t dinv_mx inv_col mux_a nor_1_mx out_inv_2 precharge tgate cell_6t inv inv_dec mux_abar out_inv_4 write_driver +do + echo $i + strmout -layerMap ../sram_lib/layers.map -library sram -topCell $i -view layout -strmFile ../sram_lib/$i.gds +done + + diff --git a/technology/freepdk45/sp_lib/cell_6t.sp b/technology/freepdk45/sp_lib/cell_6t.sp new file mode 100644 index 00000000..6b1be024 --- /dev/null +++ b/technology/freepdk45/sp_lib/cell_6t.sp @@ -0,0 +1,10 @@ + +.SUBCKT cell_6t bl br wl vdd gnd +MM3 bl wl net10 gnd NMOS_VTG W=135.00n L=50n +MM2 br wl net4 gnd NMOS_VTG W=135.00n L=50n +MM1 net10 net4 gnd gnd NMOS_VTG W=205.00n L=50n +MM0 net4 net10 gnd gnd NMOS_VTG W=205.00n L=50n +MM5 net10 net4 vdd vdd PMOS_VTG W=90n L=50n +MM4 net4 net10 vdd vdd PMOS_VTG W=90n L=50n +.ENDS cell_6t + diff --git a/technology/freepdk45/sp_lib/ms_flop.sp b/technology/freepdk45/sp_lib/ms_flop.sp new file mode 100644 index 00000000..e1967d84 --- /dev/null +++ b/technology/freepdk45/sp_lib/ms_flop.sp @@ -0,0 +1,29 @@ +*master-slave flip-flop with both output and inverted ouput + +.SUBCKT ms_flop din dout dout_bar clk vdd gnd +xmaster din mout mout_bar clk clk_bar vdd gnd dlatch +xslave mout_bar dout_bar dout clk_bar clk_nn vdd gnd dlatch +.ENDS flop + +.SUBCKT dlatch din dout dout_bar clk clk_bar vdd gnd +*clk inverter +mPff1 clk_bar clk vdd vdd PMOS_VTG W=180.0n L=50n m=1 +mNff1 clk_bar clk gnd gnd NMOS_VTG W=90n L=50n m=1 + +*transmission gate 1 +mtmP1 din clk int1 vdd PMOS_VTG W=180.0n L=50n m=1 +mtmN1 din clk_bar int1 gnd NMOS_VTG W=90n L=50n m=1 + +*foward inverter +mPff3 dout_bar int1 vdd vdd PMOS_VTG W=180.0n L=50n m=1 +mNff3 dout_bar int1 gnd gnd NMOS_VTG W=90n L=50n m=1 + +*backward inverter +mPff4 dout dout_bar vdd vdd PMOS_VTG W=180.0n L=50n m=1 +mNf4 dout dout_bar gnd gnd NMOS_VTG W=90n L=50n m=1 + +*transmission gate 2 +mtmP2 int1 clk_bar dout vdd PMOS_VTG W=180.0n L=50n m=1 +mtmN2 int1 clk dout gnd NMOS_VTG W=90n L=50n m=1 +.ENDS dlatch + diff --git a/technology/freepdk45/sp_lib/replica_cell_6t.sp b/technology/freepdk45/sp_lib/replica_cell_6t.sp new file mode 100644 index 00000000..3a7c40dd --- /dev/null +++ b/technology/freepdk45/sp_lib/replica_cell_6t.sp @@ -0,0 +1,10 @@ + +.SUBCKT replica_cell_6t bl br wl vdd gnd +MM3 bl wl gnd gnd NMOS_VTG W=135.00n L=50n +MM2 br wl net4 gnd NMOS_VTG W=135.00n L=50n +MM1 gnd net4 gnd gnd NMOS_VTG W=205.00n L=50n +MM0 net4 gnd gnd gnd NMOS_VTG W=205.00n L=50n +MM5 gnd net4 vdd vdd PMOS_VTG W=90n L=50n +MM4 net4 gnd vdd vdd PMOS_VTG W=90n L=50n +.ENDS replica_cell_6t + diff --git a/technology/freepdk45/sp_lib/sense_amp.sp b/technology/freepdk45/sp_lib/sense_amp.sp new file mode 100644 index 00000000..112d96f9 --- /dev/null +++ b/technology/freepdk45/sp_lib/sense_amp.sp @@ -0,0 +1,11 @@ + +.SUBCKT sense_amp bl br dout sclk vdd gnd +M_1 dout net_1 vdd vdd pmos_vtg w=540.0n l=50.0n +M_3 net_1 dout vdd vdd pmos_vtg w=540.0n l=50.0n +M_2 dout net_1 net_2 gnd nmos_vtg w=270.0n l=50.0n +M_8 net_1 dout net_2 gnd nmos_vtg w=270.0n l=50.0n +M_5 bl sclk dout vdd pmos_vtg w=720.0n l=50.0n +M_6 br sclk net_1 vdd pmos_vtg w=720.0n l=50.0n +M_7 net_2 sclk gnd gnd nmos_vtg w=270.0n l=50.0n +.ENDS sense_amp + diff --git a/technology/freepdk45/sp_lib/tri_gate.sp b/technology/freepdk45/sp_lib/tri_gate.sp new file mode 100644 index 00000000..9efe6eec --- /dev/null +++ b/technology/freepdk45/sp_lib/tri_gate.sp @@ -0,0 +1,10 @@ + +.SUBCKT tri_gate in out en en_bar vdd gnd +M_1 net_2 in_inv gnd gnd NMOS_VTG W=180.000000n L=50.000000n +M_2 out en net_2 gnd NMOS_VTG W=180.000000n L=50.000000n +M_3 net_3 in_inv vdd vdd PMOS_VTG W=360.000000n L=50.000000n +M_4 out en_bar net_3 vdd PMOS_VTG W=360.000000n L=50.000000n +M_5 in_inv in vdd vdd PMOS_VTG W=180.000000n L=50.000000n +M_6 in_inv in gnd gnd NMOS_VTG W=90.000000n L=50.000000n +.ENDS + diff --git a/technology/freepdk45/sp_lib/write_driver.sp b/technology/freepdk45/sp_lib/write_driver.sp new file mode 100644 index 00000000..8f1a551f --- /dev/null +++ b/technology/freepdk45/sp_lib/write_driver.sp @@ -0,0 +1,21 @@ + +.SUBCKT write_driver din bl br wen vdd gnd +*inverters for enable and data input +minP bl_bar din vdd vdd pmos_vtg w=360.000000n l=50.000000n +minN bl_bar din gnd gnd nmos_vtg w=180.000000n l=50.000000n +moutP wen_bar wen vdd vdd pmos_vtg w=360.000000n l=50.000000n +moutN wen_bar wen gnd gnd nmos_vtg w=180.000000n l=50.000000n + +*tristate for BL +mout0P int1 bl_bar vdd vdd pmos_vtg w=360.000000n l=50.000000n +mout0P2 bl wen_bar int1 vdd pmos_vtg w=360.000000n l=50.000000n +mout0N bl wen int2 gnd nmos_vtg w=180.000000n l=50.000000n +mout0N2 int2 bl_bar gnd gnd nmos_vtg w=180.000000n l=50.000000n + +*tristate for BR +mout1P int3 din vdd vdd pmos_vtg w=360.000000n l=50.000000n +mout1P2 br wen_bar int3 vdd pmos_vtg w=360.000000n l=50.000000n +mout1N br wen int4 gnd nmos_vtg w=180.000000n l=50.000000n +mout1N2 int4 din gnd gnd nmos_vtg w=180.000000n l=50.000000n +.ENDS write_driver + diff --git a/technology/freepdk45/tech/__init__.py b/technology/freepdk45/tech/__init__.py new file mode 100644 index 00000000..798e230d --- /dev/null +++ b/technology/freepdk45/tech/__init__.py @@ -0,0 +1,9 @@ +""" +Python GDS Mill Package + +GDS Mill is a Python package for the creation and manipulation of binary GDS2 layout files. +""" + +from tech import * +from ptx_port import * + diff --git a/technology/freepdk45/tech/ptx_port.py b/technology/freepdk45/tech/ptx_port.py new file mode 100644 index 00000000..3f0f7cfb --- /dev/null +++ b/technology/freepdk45/tech/ptx_port.py @@ -0,0 +1,23 @@ +""" +This class should be called in the ptx function to draw addtional layer as some layer may not exist in the freePDK technology +""" +import globals +import design +import tech + +#class ptx_port: +# def __init__(self,name): +# self.name=name +# self.width=0 +# self.height=0 + # def draw(self,instance_to_draw,offset,tx_type,height,width,tx=1): + + #Draw wire to connect mulit active + + #self.add_rect(tech.layer["metal1"],[contact_offset[0],-wire_to_ptx],width,tech.drc["minwidth_metal1"]) + # BINWU: Fix extra stuff + #print"Freepdk no drawing required" +# pass + +def add_custom_layer(fet): + pass diff --git a/technology/freepdk45/tech/tech.py b/technology/freepdk45/tech/tech.py new file mode 100644 index 00000000..89c29e93 --- /dev/null +++ b/technology/freepdk45/tech/tech.py @@ -0,0 +1,310 @@ +""" +File containing the process technology parameters. +""" +info = {} +info["name"] = "freepdk45" +info["body_tie_down"] = 0 +info["has_pwell"] = True +info["has_nwell"] = True + +#GDS file info +GDS = {} +GDS["unit"] = (0.0005,1e-9) + +##################################################################################################### +##GDS Layer Map###################################################################################### +##################################################################################################### + +# create the GDS layer map +# FIXME: parse the gds layer map from the cadence map? +layer = {} +layer["active"] = 1 +layer["pwell"] = 2 +layer["nwell"] = 3 +layer["nimplant"]= 4 +layer["pimplant"]= 5 +layer["vtg"] = 6 +layer["vth"] = 7 +layer["thkox"] = 8 +layer["poly"] = 9 +layer["contact"] = 10 +layer["metal1"] = 11 +layer["via1"] = 12 +layer["metal2"] = 13 +layer["via2"] = 14 +layer["metal3"] = 15 +layer["via3"] = 16 +layer["metal4"] = 17 +layer["via4"] = 18 +layer["metal5"] = 19 +layer["via5"] = 20 +layer["metal6"] = 21 +layer["via6"] = 22 +layer["metal7"] = 23 +layer["via7"] = 24 +layer["metal8"] = 25 +layer["via8"] = 26 +layer["metal9"] = 27 +layer["via9"] = 28 +layer["metal10"] = 29 +layer["text"] = 239 +layer["boundary"]= 239 + +##################################################################################################### +##END GDS Layer Map################################################################################## +##################################################################################################### + +##################################################################################################### +##GDS Library File Parameters######################################################################## +##################################################################################################### + +#lib cell size info +import os +import utils + + +cell = {} +sense_amp = {} +write_driver = {} +tri_gate = {} +ms_flop = {} +replica_cell = {} + + + +##################################################################################################### +##END GDS Library File Parameters#################################################################### +##################################################################################################### + +##################################################################################################### +##DRC/LVS Rules Setup################################################################################ +##################################################################################################### + +#technology parameter +parameter={} +parameter["min_tx_size"] = 0.09 +parameter["pinv_beta"] = 3 + +drclvs_home=os.environ.get("DRCLVS_HOME") +drc={} +#grid size +drc["grid"] = 0.0025 + +#DRC/LVS test set_up +drc["drc_rules"]=drclvs_home+"/calibreDRC.rul" +drc["lvs_rules"]=drclvs_home+"/calibreLVS.rul" +drc["xrc_rules"]=drclvs_home+"/calibrexRC.rul" +drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/layers.map" + +# minwidth_tx withcontact +drc["minwidth_tx"]=0.09 +drc["minlength_channel"] = 0.05 + +#well rules +drc["pwell_enclose_nwell"] = 0.225 +drc["minwidth_well"] = 0.2 + +#poly rules +drc["minwidth_poly"] = 0.05 +drc["minheight_poly"] = 0.0 +drc["poly_to_poly"] = 0.14 +drc["poly_extend_active"] = 0.055 +drc["active_enclosure_gate"] = 0.07 +drc["poly_to_active"] = 0.05 +drc["poly_to_field_poly"] = 0.075 +drc["minarea_poly"] = 0.0 + +#active +drc["active_extend_gate"] = 0 +drc["active_to_body_active"] = 0.08 +drc["minwidth_active"] = 0.09 +drc["minheight_active"] = 0.09 +drc["minarea_active"] = 0 +drc["well_enclosure_active"] = 0.055 +drc["well_extend_active"] = 0.055 + + +#Implant +drc["implant_to_gate"] = 0.07 +drc["implant_to_channel"] = 0.07 +drc["implant_to_contact"] = 0.025 +drc["implant_to_implant"] = 0.045 +drc["minwidth_implant"] = 0.045 + +#Contact +drc["minwidth_contact"] = 0.065 +drc["contact_to_contact"] = 0.075 +drc["active_enclosure_contact"] = 0.005 +drc["active_extend_contact"] = 0.005 +drc["poly_enclosure_contact"] = 0.005 +drc["poly_extend_contact"] = 0.005 +drc["contact_to_poly"] = 0.0375 #changed from 0.035 + +#Metal1 +drc["minwidth_metal1"] = 0.065 +drc["minheight_metal1"] = 0.0 +drc["metal1_to_metal1"] = 0.065 +drc["metal1_enclosure_contact"] = 0 +drc["metal1_extend_contact"] = 0.035 +drc["metal1_extend_via1"] = 0.035 +drc["metal1_enclosure_via1"] = 0 +drc["minarea_metal1"] = 0 + +#via1 +drc["minwidth_via1"] = 0.065 +drc["via1_to_via1"] = 0.075 + +#Metal2 +drc["minwidth_metal2"] = 0.07 +drc["minheight_metal2"] = 0.0 +drc["metal2_to_metal2"] = 0.07 +drc["metal2_extend_via1"] = 0.035 +drc["metal2_enclosure_via1"] = 0 +drc["metal2_extend_via2"] = 0.035 +drc["metal2_enclosure_via2"] = 0 +drc["minarea_metal2"] = 0 + +#Via2 +drc["minwidth_via2"] = 0.065 +drc["via2_to_via2"] = 0.075 + +#Metal3 +drc["minwidth_metal3"] = 0.07 +drc["minheight_metal3"] = 0.0 +drc["metal3_to_metal3"] = 0.07 +drc["metal3_extend_via2"] = 0.035 +drc["metal3_enclosure_via2"] = 0 +drc["metal3_extend_via3"]=0.035 +drc["metal3_enclosure_via3"] = 0 +drc["minarea_metal3"] = 0 + +#Via3 +drc["minwidth_via3"] = 0.065 +drc["via3_to_via3"] = 0.07 + +#Metal4 +drc["minwidth_metal4"] = 0.14 +drc["minheight_metal4"] = 0.0 +drc["metal4_enclosure_via3"] = 0 +drc["metal4_extend_via3"] = 0.07 +drc["metal4_to_metal4"] = 0.14 +drc["metal4_extend_via4"] = 0.07 +drc["metal4_enclosure_via4"] = 0.07 +drc["minarea_metal4"] = 0 + +#Via4 +drc["minwidth_via4"] = 0.14 +drc["via4_to_via4"] = 0.14 + +#Metal5 +drc["minwidth_metal5"] = 0.14 +drc["minheight_metal5"] = 0.0 +drc["metal5_to_metal5"] = 0.14 +drc["metal5_extend_via4"] = 0.07 +drc["metal5_enclosure_via4"] = 0.07 +drc["minarea_metal5"] = 0 + +#Via 5 +drc["minwidth_via5"] = 0.14 +drc["via5_to_via5"] = 0.14 + +#Metal6 +drc["minwidth_metal6"] = 0.14 +drc["minheight_metal6"] = 0.0 +drc["metal6_to_metal6"] = 0.14 +drc["metal6_extend_via5"] = 0 +drc["metal6_enclosure_via5"] = 0 + +#Via 6 +drc["minwidth_via6"] = 0.14 +drc["via6_to_via6"] = 0.14 + +#Metal7 +drc["minwidth_metal7"] = 0.14 +drc["minheight_metal7"] = 0.0 +drc["metal7_to_metal7"] = 0.14 +drc["metal7_extend_via6"] = 0 +drc["metal7_enclosure_via6"] = 0 + +#Via7 +drc["minwidth_via7"] = 0.14 +drc["via7_to_via7"] = 0.14 + +#Metal8 +drc["minwidth_metal8"] = 0.14 +drc["minheight_metal8"] = 0.0 +drc["metal8_to_metal8"] = 0.14 +drc["metal8_extend_via7"] = 0 +drc["metal8_enclosure_via7"] = 0 + +#Via8 +drc["minwidth_via8"] = 0.14 +drc["via8_to_via8"] = 0.14 + +#Metal9 +drc["minwidth_metal9"] = 0.14 +drc["minheight_metal9"] = 0.0 +drc["metal9_to_metal9"] = 0.14 +drc["metal9_extend_via8"] = 0 +drc["metal9_enclosure_via8"] = 0 + +#Via 9 +drc["minwidth_via9"] = 0.14 +drc["via9_to_via9"] = 0.14 + +#Metal 10 +drc["minwidth_metal10"] = 0.14 +drc["minheight_metal10"] = 0.0 +drc["metal10_to_metal10"] = 0.14 +drc["metal10_extend_via9"] = 0 +drc["metal10_enclosure_via9"] = 0 + +##################################################################################################### +##END DRC/LVS Rules################################################################################## +##################################################################################################### + +##################################################################################################### +##Spice Simulation Parameters######################################################################## +##################################################################################################### + +#spice info +spice = {} +spice["nmos"] = "nmos_vtg" +spice["pmos"] = "pmos_vtg" +spice["fet_models"] = ["/mada/software/techfiles/FreePDK45-1.4/ncsu_basekit/models/hspice/tran_models/models_nom/NMOS_VTG.inc", + "/mada/software/techfiles/FreePDK45-1.4/ncsu_basekit/models/hspice/tran_models/models_nom/PMOS_VTG.inc"] +#spice["nmos_model"] = "/import/comet1/Freepdk/ncsu_basekit/models/hspice/tran_models/models_nom/NMOS_VTG.inc" +#spice["pmos_model"] = "/import/comet1/Freepdk/ncsu_basekit/models/hspice/tran_models/models_nom/PMOS_VTG.inc" + +#spice stimulus related variables +spice["clock_period"] = 2.0 +spice["supply_voltage"] = 1.0 #vdd in [Volts] +spice["gnd_voltage"] = 0.0 #gnd in [Volts] +spice["rise_time"] = 0.001 #rise time in [Nano-seconds] +spice["fall_time"] = 0.001 #fall time in [Nano-seconds] +spice["temp"] = 25 #temperature in [Celsius] + +#parasitics of metal for bit/word lines +spice["bitline_res"] = 0.1 #bitline resistance in [Ohms/micro-meter] +spice["bitline_cap"] = 0.2 #bitline capacitance in [Femto-farad/micro-meter] +spice["wordline_res"] = 0.1 #wordline resistance in [Ohms/micro-meter] +spice["wordline_cap"] = 0.2 #wordline capacitance in [Femto-farad/micro-meter] +spice["FF_in_cap"] = 0.2091 #Input capacitance of ms_flop (Din) [Femto-farad] +spice["tri_gate_out_cap"] = 0.41256 #Output capacitance of tri_gate (tri_out) [Femto-farad] + + +#sram signal names +spice["vdd_name"] = "vdd" +spice["gnd_name"] = "gnd" +spice["control_signals"] = ["CSb", "WEb", "OEb"] +spice["data_name"] = "DATA" +spice["addr_name"] = "ADDR" +spice["pmos_name"] = spice["pmos"] +spice["nmos_name"] = spice["nmos"] +spice["minwidth_tx"] = drc["minwidth_tx"] +spice["channel"] = drc["minlength_channel"] +spice["clk"] = "clk" + +# estimated feasible period in ns +spice["feasible_period"] = 5 + diff --git a/technology/scn3me_subm/gds_lib/cell_6t.gds b/technology/scn3me_subm/gds_lib/cell_6t.gds new file mode 100644 index 00000000..8c04e9c1 Binary files /dev/null and b/technology/scn3me_subm/gds_lib/cell_6t.gds differ diff --git a/technology/scn3me_subm/gds_lib/ms_flop.gds b/technology/scn3me_subm/gds_lib/ms_flop.gds new file mode 100644 index 00000000..ce7fdd3a Binary files /dev/null and b/technology/scn3me_subm/gds_lib/ms_flop.gds differ diff --git a/technology/scn3me_subm/gds_lib/replica_cell_6t.gds b/technology/scn3me_subm/gds_lib/replica_cell_6t.gds new file mode 100644 index 00000000..3abed82f Binary files /dev/null and b/technology/scn3me_subm/gds_lib/replica_cell_6t.gds differ diff --git a/technology/scn3me_subm/gds_lib/sense_amp.gds b/technology/scn3me_subm/gds_lib/sense_amp.gds new file mode 100644 index 00000000..b36dc262 Binary files /dev/null and b/technology/scn3me_subm/gds_lib/sense_amp.gds differ diff --git a/technology/scn3me_subm/gds_lib/tri_gate.gds b/technology/scn3me_subm/gds_lib/tri_gate.gds new file mode 100644 index 00000000..36061d91 Binary files /dev/null and b/technology/scn3me_subm/gds_lib/tri_gate.gds differ diff --git a/technology/scn3me_subm/gds_lib/write_driver.gds b/technology/scn3me_subm/gds_lib/write_driver.gds new file mode 100644 index 00000000..07ac54cd Binary files /dev/null and b/technology/scn3me_subm/gds_lib/write_driver.gds differ diff --git a/technology/scn3me_subm/layers.map b/technology/scn3me_subm/layers.map new file mode 100644 index 00000000..d10d5f2d --- /dev/null +++ b/technology/scn3me_subm/layers.map @@ -0,0 +1,16 @@ +Pwell drawing 41 0 +Nwell drawing 42 0 +Active drawing 43 0 +Poly1 drawing 46 0 +Pselect drawing 45 0 +Nselect drawing 44 0 +contact drawing 25 0 +P1Con drawing 47 0 +ActX drawing 48 0 +Metal1 drawing 49 0 +Via drawing 50 0 +Metal2 drawing 51 0 +Via2 drawing 61 0 +Metal3 drawing 62 0 +Glass drawing 52 0 +text drawing 83 0 diff --git a/technology/scn3me_subm/mag_lib/cell_6t.mag b/technology/scn3me_subm/mag_lib/cell_6t.mag new file mode 100644 index 00000000..3b048720 --- /dev/null +++ b/technology/scn3me_subm/mag_lib/cell_6t.mag @@ -0,0 +1,115 @@ +magic +tech scmos +timestamp 1425318832 +<< ntransistor >> +rect 7 12 9 20 +rect 29 12 31 20 +rect 10 5 14 7 +rect 24 5 28 7 +<< ptransistor >> +rect 7 39 11 42 +rect 27 39 31 42 +<< ndiffusion >> +rect 2 20 6 23 +rect 32 20 36 23 +rect 6 16 7 20 +rect 2 12 7 16 +rect 9 16 10 20 +rect 9 12 14 16 +rect 28 16 29 20 +rect 24 12 29 16 +rect 31 16 32 20 +rect 31 12 36 16 +rect 10 7 14 12 +rect 24 7 28 12 +rect 10 4 14 5 +rect 24 4 28 5 +<< pdiffusion >> +rect 2 42 6 45 +rect 32 42 36 45 +rect 6 39 7 42 +rect 11 39 12 42 +rect 26 39 27 42 +rect 31 39 32 42 +<< ndcontact >> +rect 2 16 6 20 +rect 10 16 14 20 +rect 24 16 28 20 +rect 32 16 36 20 +rect 10 0 14 4 +rect 24 0 28 4 +<< pdcontact >> +rect 2 38 6 42 +rect 12 38 16 42 +rect 22 38 26 42 +rect 32 38 36 42 +<< psubstratepcontact >> +rect 2 23 6 27 +rect 32 23 36 27 +<< nsubstratencontact >> +rect 0 45 6 49 +rect 32 45 36 49 +<< polysilicon >> +rect 7 42 11 44 +rect 27 42 31 44 +rect 7 37 11 39 +rect 7 23 9 37 +rect 27 36 31 39 +rect 15 35 31 36 +rect 19 34 31 35 +rect 7 22 21 23 +rect 7 21 24 22 +rect 7 20 9 21 +rect 29 20 31 34 +rect 7 10 9 12 +rect 17 7 21 8 +rect 29 10 31 12 +rect 0 5 10 7 +rect 14 5 24 7 +rect 28 5 36 7 +<< polycontact >> +rect 15 31 19 35 +rect 21 22 25 26 +rect 17 8 21 12 +<< metal1 >> +rect 6 45 32 49 +rect 2 42 6 45 +rect 32 42 36 45 +rect 2 27 6 31 +rect 2 20 6 23 +rect 11 20 15 38 +rect 23 26 27 38 +rect 25 22 27 26 +rect 23 20 27 22 +rect 32 27 36 31 +rect 32 20 36 23 +rect 0 8 17 11 +rect 21 8 36 11 +rect 0 7 36 8 +rect 9 0 10 4 +rect 23 0 24 4 +<< m2contact >> +rect 2 31 6 35 +rect 32 31 36 35 +rect 5 0 9 4 +rect 19 0 23 4 +<< metal2 >> +rect 0 35 6 49 +rect 0 31 2 35 +rect 0 14 6 31 +rect 10 4 14 49 +rect 20 4 24 49 +rect 9 0 14 4 +rect 23 0 24 4 +rect 32 35 36 49 +rect 32 0 36 31 +<< m3p >> +rect 0 0 34 49 +<< labels >> +rlabel m2contact 20 4 20 4 1 BR +rlabel metal2 10 4 10 4 1 BL +rlabel metal2 32 31 32 31 7 gnd +rlabel metal1 32 45 32 45 4 vdd +rlabel metal2 2 31 2 31 3 gnd +rlabel metal1 2 8 2 8 3 WL +<< end >> diff --git a/technology/scn3me_subm/mag_lib/ms_flop.mag b/technology/scn3me_subm/mag_lib/ms_flop.mag new file mode 100644 index 00000000..8cc10c8a --- /dev/null +++ b/technology/scn3me_subm/mag_lib/ms_flop.mag @@ -0,0 +1,289 @@ +magic +tech scmos +timestamp 1424105514 +<< ntransistor >> +rect 24 178 27 180 +rect 24 162 27 164 +rect 24 138 27 140 +rect 24 130 27 132 +rect 24 112 27 114 +rect 24 93 27 95 +rect 24 77 27 79 +rect 24 50 27 52 +rect 24 42 27 44 +rect 24 24 27 26 +<< ptransistor >> +rect 6 178 12 180 +rect 6 162 12 164 +rect 6 138 12 140 +rect 6 130 12 132 +rect 6 112 12 114 +rect 6 93 12 95 +rect 6 77 12 79 +rect 6 50 12 52 +rect 6 42 12 44 +rect 6 24 12 26 +<< ndiffusion >> +rect 24 180 27 181 +rect 24 177 27 178 +rect 24 164 27 165 +rect 24 161 27 162 +rect 28 157 32 161 +rect 24 140 27 141 +rect 24 137 27 138 +rect 24 132 27 133 +rect 24 129 27 130 +rect 24 114 27 115 +rect 24 111 27 112 +rect 24 95 27 96 +rect 24 92 27 93 +rect 24 79 27 80 +rect 24 76 27 77 +rect 28 72 32 76 +rect 24 52 27 53 +rect 24 49 27 50 +rect 24 44 27 45 +rect 24 41 27 42 +rect 24 26 27 27 +rect 24 23 27 24 +<< pdiffusion >> +rect 6 180 12 181 +rect 6 177 12 178 +rect 6 164 12 165 +rect 6 161 12 162 +rect 6 140 12 141 +rect 6 137 12 138 +rect 6 132 12 133 +rect 6 129 12 130 +rect 6 114 12 115 +rect 6 111 12 112 +rect 6 95 12 96 +rect 6 92 12 93 +rect 6 79 12 80 +rect 6 76 12 77 +rect 6 52 12 53 +rect 6 49 12 50 +rect 6 44 12 45 +rect 6 41 12 42 +rect 6 26 12 27 +rect 6 23 12 24 +rect 8 18 12 19 +<< ndcontact >> +rect 24 181 28 185 +rect 24 173 28 177 +rect 24 165 28 169 +rect 24 157 28 161 +rect 24 141 28 145 +rect 24 133 28 137 +rect 24 125 28 129 +rect 24 115 28 119 +rect 24 107 28 111 +rect 24 96 28 100 +rect 24 88 28 92 +rect 24 80 28 84 +rect 24 72 28 76 +rect 24 53 28 57 +rect 24 45 28 49 +rect 24 37 28 41 +rect 24 27 28 31 +rect 24 19 28 23 +<< pdcontact >> +rect 6 181 12 185 +rect 6 173 12 177 +rect 6 165 12 169 +rect 6 157 12 161 +rect 6 141 12 145 +rect 6 133 12 137 +rect 6 125 12 129 +rect 6 115 12 119 +rect 6 107 12 111 +rect 6 96 12 100 +rect 6 88 12 92 +rect 6 80 12 84 +rect 6 72 12 76 +rect 6 53 12 57 +rect 6 45 12 49 +rect 6 37 12 41 +rect 6 27 12 31 +rect 6 19 12 23 +<< psubstratepcontact >> +rect 32 157 36 161 +rect 32 72 36 76 +<< nsubstratencontact >> +rect 8 14 12 18 +<< polysilicon >> +rect 4 178 6 180 +rect 12 178 24 180 +rect 27 178 29 180 +rect 17 173 19 178 +rect 4 162 6 164 +rect 12 163 24 164 +rect 12 162 17 163 +rect 21 162 24 163 +rect 27 162 29 164 +rect 3 148 13 150 +rect 3 140 5 148 +rect 3 138 6 140 +rect 12 138 14 140 +rect 17 138 24 140 +rect 27 138 29 140 +rect 17 132 19 138 +rect 3 130 6 132 +rect 12 130 19 132 +rect 22 130 24 132 +rect 27 130 31 132 +rect 3 114 5 130 +rect 29 122 31 130 +rect 20 120 31 122 +rect 3 112 6 114 +rect 12 112 24 114 +rect 27 112 29 114 +rect 4 93 6 95 +rect 12 93 24 95 +rect 27 93 29 95 +rect 19 89 21 93 +rect 4 77 6 79 +rect 12 78 24 79 +rect 12 77 17 78 +rect 21 77 24 78 +rect 27 77 29 79 +rect 3 60 13 62 +rect 3 52 5 60 +rect 3 50 6 52 +rect 12 50 14 52 +rect 17 50 24 52 +rect 27 50 29 52 +rect 17 44 19 50 +rect 3 42 6 44 +rect 12 42 19 44 +rect 22 42 24 44 +rect 27 42 31 44 +rect 3 26 5 42 +rect 29 34 31 42 +rect 20 32 31 34 +rect 3 24 6 26 +rect 12 24 24 26 +rect 27 24 29 26 +rect 16 14 18 24 +<< polycontact >> +rect 16 169 20 173 +rect 17 159 21 163 +rect 13 148 17 152 +rect 16 118 20 122 +rect 15 108 19 112 +rect 17 85 21 89 +rect 17 74 21 78 +rect 13 60 17 64 +rect 16 30 20 34 +rect 15 10 19 14 +<< metal1 >> +rect -2 188 36 191 +rect -2 177 2 188 +rect 16 182 24 185 +rect -2 173 6 177 +rect 28 173 36 177 +rect -2 161 2 173 +rect 12 166 20 169 +rect -2 157 6 161 +rect 33 161 36 173 +rect -2 111 2 157 +rect 28 157 32 161 +rect 12 142 24 145 +rect 12 134 20 137 +rect 12 126 20 129 +rect 20 118 24 119 +rect 16 116 24 118 +rect -2 107 6 111 +rect 33 111 36 153 +rect -2 92 2 107 +rect 28 107 36 111 +rect 12 97 24 100 +rect 33 92 36 107 +rect -2 88 6 92 +rect -2 76 2 88 +rect 28 88 36 92 +rect 6 84 20 85 +rect 12 82 20 84 +rect -2 72 6 76 +rect 33 76 36 88 +rect -2 22 2 72 +rect 28 72 32 76 +rect 12 54 24 57 +rect 12 46 20 49 +rect 12 38 20 41 +rect 20 30 24 31 +rect 16 28 24 30 +rect 33 23 36 68 +rect -2 19 6 22 +rect 28 20 36 23 +rect 8 18 12 19 +rect -2 10 15 11 +rect 19 10 36 11 +rect -2 8 36 10 +<< m2contact >> +rect 12 181 16 185 +rect 20 166 24 170 +rect 17 155 21 159 +rect 32 153 36 157 +rect 6 145 10 149 +rect 17 148 21 152 +rect 20 133 24 137 +rect 20 125 24 129 +rect 12 115 16 119 +rect 15 104 19 108 +rect 6 100 10 104 +rect 20 81 24 85 +rect 17 70 21 74 +rect 32 68 36 72 +rect 6 57 10 61 +rect 17 60 21 64 +rect 20 45 24 49 +rect 20 37 24 41 +rect 12 27 16 31 +<< metal2 >> +rect 6 185 10 200 +rect 15 196 19 200 +rect 15 192 24 196 +rect 6 181 12 185 +rect 6 149 9 181 +rect 20 170 24 192 +rect 21 155 27 159 +rect 18 143 21 148 +rect 13 140 21 143 +rect 13 119 16 140 +rect 24 133 27 155 +rect 32 157 36 200 +rect 5 100 6 104 +rect 5 61 8 100 +rect 15 93 19 104 +rect 11 90 19 93 +rect 11 67 14 90 +rect 24 81 27 129 +rect 21 70 27 74 +rect 11 64 16 67 +rect 5 57 6 61 +rect 13 60 17 64 +rect 13 31 16 60 +rect 24 45 27 70 +rect 32 72 36 153 +rect 24 8 27 41 +rect 19 4 27 8 +rect 15 0 19 4 +rect 32 0 36 68 +<< m3contact >> +rect 15 4 19 8 +<< metal3 >> +rect 14 8 20 9 +rect 14 4 15 8 +rect 19 4 20 8 +rect 14 3 20 4 +<< m3p >> +rect 0 0 34 200 +<< labels >> +rlabel metal1 0 8 0 8 2 clk +rlabel metal1 -2 191 -2 191 2 vdd +rlabel metal3 15 4 15 4 1 din +rlabel metal2 6 196 6 196 5 dout_bar +rlabel metal2 15 196 15 196 5 dout +rlabel metal2 32 0 32 0 8 gnd +<< end >> diff --git a/technology/scn3me_subm/mag_lib/replica_cell_6t.mag b/technology/scn3me_subm/mag_lib/replica_cell_6t.mag new file mode 100644 index 00000000..0211bf9e --- /dev/null +++ b/technology/scn3me_subm/mag_lib/replica_cell_6t.mag @@ -0,0 +1,118 @@ +magic +tech scmos +timestamp 1424371826 +<< ntransistor >> +rect 7 12 9 20 +rect 29 12 31 20 +rect 10 5 14 7 +rect 24 5 28 7 +<< ptransistor >> +rect 7 39 11 42 +rect 27 39 31 42 +<< ndiffusion >> +rect 2 20 6 23 +rect 32 20 36 23 +rect 6 16 7 20 +rect 2 12 7 16 +rect 9 16 10 20 +rect 9 12 14 16 +rect 28 16 29 20 +rect 24 12 29 16 +rect 31 16 32 20 +rect 31 12 36 16 +rect 10 7 14 12 +rect 24 7 28 12 +rect 10 4 14 5 +rect 24 4 28 5 +<< pdiffusion >> +rect 2 42 6 45 +rect 32 42 36 45 +rect 6 39 7 42 +rect 11 39 12 42 +rect 26 39 27 42 +rect 31 39 32 42 +<< ndcontact >> +rect 2 16 6 20 +rect 10 16 14 20 +rect 24 16 28 20 +rect 32 16 36 20 +rect 10 0 14 4 +rect 24 0 28 4 +<< pdcontact >> +rect 2 38 6 42 +rect 12 38 16 42 +rect 22 38 26 42 +rect 32 38 36 42 +<< psubstratepcontact >> +rect 2 23 6 27 +rect 32 23 36 27 +<< nsubstratencontact >> +rect 0 45 6 49 +rect 32 45 36 49 +<< polysilicon >> +rect 7 42 11 44 +rect 27 42 31 44 +rect 7 37 11 39 +rect 7 23 9 37 +rect 27 36 31 39 +rect 15 35 31 36 +rect 19 34 31 35 +rect 7 22 21 23 +rect 7 21 24 22 +rect 7 20 9 21 +rect 29 20 31 34 +rect 7 10 9 12 +rect 17 7 21 8 +rect 29 10 31 12 +rect 0 5 10 7 +rect 14 5 24 7 +rect 28 5 36 7 +<< polycontact >> +rect 15 31 19 35 +rect 21 22 25 26 +rect 17 8 21 12 +<< metal1 >> +rect 6 45 32 49 +rect 2 42 6 45 +rect 32 42 36 45 +rect 11 35 15 38 +rect 6 31 15 35 +rect 2 27 6 31 +rect 2 20 6 23 +rect 11 20 15 31 +rect 23 26 27 38 +rect 25 22 27 26 +rect 23 20 27 22 +rect 32 27 36 31 +rect 32 20 36 23 +rect 2 14 6 16 +rect 0 8 17 11 +rect 21 8 36 11 +rect 0 7 36 8 +rect 9 0 10 4 +rect 23 0 24 4 +<< m2contact >> +rect 2 31 6 35 +rect 32 31 36 35 +rect 5 0 9 4 +rect 19 0 23 4 +<< metal2 >> +rect 0 35 6 49 +rect 0 31 2 35 +rect 0 14 6 31 +rect 10 4 14 49 +rect 20 4 24 49 +rect 9 0 14 4 +rect 23 0 24 4 +rect 32 35 36 49 +rect 32 0 36 31 +<< m3p >> +rect 0 0 34 49 +<< labels >> +rlabel metal2 2 31 2 31 3 gnd +rlabel metal2 32 31 32 31 7 gnd +rlabel metal2 10 4 10 4 1 BL +rlabel m2contact 20 4 20 4 1 BR +rlabel nsubstratencontact 2 45 2 45 4 vdd +rlabel metal1 2 7 2 7 3 WL +<< end >> diff --git a/technology/scn3me_subm/mag_lib/sense_amp.mag b/technology/scn3me_subm/mag_lib/sense_amp.mag new file mode 100644 index 00000000..be7e00c5 --- /dev/null +++ b/technology/scn3me_subm/mag_lib/sense_amp.mag @@ -0,0 +1,131 @@ +magic +tech scmos +timestamp 1424193153 +<< ntransistor >> +rect 21 115 23 128 +rect 12 89 14 102 +rect 20 89 22 102 +<< ptransistor >> +rect 12 67 14 77 +rect 20 67 22 77 +rect 11 20 13 33 +rect 27 20 29 33 +<< ndiffusion >> +rect 20 115 21 128 +rect 23 115 24 128 +rect 11 89 12 102 +rect 14 89 15 102 +rect 19 89 20 102 +rect 22 89 23 102 +<< pdiffusion >> +rect 11 67 12 77 +rect 14 67 15 77 +rect 19 67 20 77 +rect 22 67 23 77 +rect 10 20 11 33 +rect 13 20 14 33 +rect 26 20 27 33 +rect 29 20 30 33 +<< ndcontact >> +rect 16 115 20 128 +rect 24 115 28 128 +rect 7 89 11 102 +rect 15 89 19 102 +rect 23 89 27 102 +<< pdcontact >> +rect 7 67 11 77 +rect 15 67 19 77 +rect 23 67 27 77 +rect 6 20 10 33 +rect 14 20 18 33 +rect 22 20 26 33 +rect 30 20 34 33 +<< psubstratepcontact >> +rect 32 127 36 131 +<< nsubstratencontact >> +rect 18 47 22 55 +<< polysilicon >> +rect 21 128 23 138 +rect 21 114 23 115 +rect 3 112 23 114 +rect 3 36 5 112 +rect 12 107 34 109 +rect 12 102 14 107 +rect 20 102 22 104 +rect 12 77 14 89 +rect 20 77 22 89 +rect 32 86 34 107 +rect 30 82 34 86 +rect 12 65 14 67 +rect 20 58 22 67 +rect 13 56 22 58 +rect 9 44 11 54 +rect 32 44 34 82 +rect 33 40 34 44 +rect 3 34 13 36 +rect 11 33 13 34 +rect 27 33 29 35 +rect 11 19 13 20 +rect 27 19 29 20 +rect 11 17 29 19 +<< polycontact >> +rect 20 138 24 142 +rect 26 82 30 86 +rect 9 54 13 58 +rect 9 40 13 44 +rect 29 40 33 44 +<< metal1 >> +rect -2 138 20 142 +rect 24 138 36 142 +rect -2 131 32 135 +rect 24 128 28 131 +rect 16 102 19 115 +rect 7 77 11 89 +rect 23 86 27 89 +rect 23 82 26 86 +rect 23 77 27 82 +rect 7 58 11 67 +rect 15 64 18 67 +rect 15 61 21 64 +rect 7 54 9 58 +rect 18 55 21 61 +rect -2 47 18 51 +rect 22 47 36 51 +rect 6 33 9 43 +rect 33 40 34 44 +rect 31 33 34 40 +rect 3 20 6 23 +rect 3 15 7 20 +<< m2contact >> +rect 32 131 36 135 +rect 13 33 17 37 +rect 22 33 26 37 +rect 3 11 7 15 +<< metal2 >> +rect 10 37 14 152 +rect 20 37 24 152 +rect 32 135 36 152 +rect 32 127 36 131 +rect 10 33 13 37 +rect 20 33 22 37 +rect 3 8 7 11 +rect 3 0 7 4 +rect 10 0 14 33 +rect 20 0 24 33 +<< m3contact >> +rect 3 4 7 8 +<< metal3 >> +rect 2 8 8 9 +rect 2 4 3 8 +rect 7 4 8 8 +rect 2 3 8 4 +<< m3p >> +rect 0 0 34 152 +<< labels >> +rlabel metal3 3 3 3 3 2 Dout +rlabel metal1 0 138 0 138 4 SCLK +rlabel metal1 0 131 0 131 5 gnd +rlabel metal1 0 47 0 47 3 vdd +rlabel metal2 20 0 20 0 1 BR +rlabel metal2 10 0 10 0 1 BL +<< end >> diff --git a/technology/scn3me_subm/mag_lib/tri_gate.mag b/technology/scn3me_subm/mag_lib/tri_gate.mag new file mode 100644 index 00000000..f4999160 --- /dev/null +++ b/technology/scn3me_subm/mag_lib/tri_gate.mag @@ -0,0 +1,107 @@ +magic +tech scmos +timestamp 1428529544 +<< ntransistor >> +rect 9 27 11 31 +rect 17 27 19 31 +rect 25 27 27 31 +<< ptransistor >> +rect 9 53 11 61 +rect 17 53 19 61 +rect 25 53 27 61 +<< ndiffusion >> +rect 8 27 9 31 +rect 11 27 12 31 +rect 16 27 17 31 +rect 19 27 20 31 +rect 24 27 25 31 +rect 27 27 28 31 +<< pdiffusion >> +rect 8 53 9 61 +rect 11 53 12 61 +rect 16 53 17 61 +rect 19 53 20 61 +rect 24 53 25 61 +rect 27 53 28 61 +<< ndcontact >> +rect 4 27 8 31 +rect 12 27 16 31 +rect 20 27 24 31 +rect 28 27 32 31 +<< pdcontact >> +rect 4 53 8 61 +rect 12 53 16 61 +rect 20 53 24 61 +rect 28 53 32 61 +<< psubstratepcontact >> +rect 12 19 16 23 +<< nsubstratencontact >> +rect 12 65 16 69 +<< polysilicon >> +rect 25 63 35 65 +rect 9 61 11 63 +rect 17 61 19 63 +rect 25 61 27 63 +rect 9 50 11 53 +rect 9 31 11 46 +rect 17 42 19 53 +rect 25 51 27 53 +rect 17 31 19 38 +rect 25 31 27 33 +rect 9 25 11 27 +rect 17 25 19 27 +rect 25 16 27 27 +rect 33 8 35 63 +rect 32 6 35 8 +<< polycontact >> +rect 9 46 13 50 +rect 16 38 20 42 +rect 25 12 29 16 +rect 28 4 32 8 +<< metal1 >> +rect 0 65 12 69 +rect 16 65 36 69 +rect 12 61 16 65 +rect 3 53 4 61 +rect 3 42 6 53 +rect 3 38 16 42 +rect 3 31 6 38 +rect 29 31 32 53 +rect 3 27 4 31 +rect 12 23 16 27 +rect 0 19 12 23 +rect 16 19 32 23 +rect 0 12 25 16 +rect 29 12 36 16 +rect 0 4 28 8 +rect 32 4 36 8 +<< m2contact >> +rect 9 46 13 50 +rect 25 34 29 38 +rect 32 19 36 23 +<< metal2 >> +rect 15 50 19 73 +rect 13 46 19 50 +rect 15 34 25 38 +rect 15 9 19 34 +rect 32 23 36 73 +rect 19 5 20 9 +rect 15 0 19 5 +rect 32 0 36 19 +<< m3contact >> +rect 15 5 19 9 +<< metal3 >> +rect 14 9 20 10 +rect 14 5 15 9 +rect 19 5 20 9 +rect 14 4 20 5 +<< m3p >> +rect 0 0 34 73 +<< labels >> +rlabel metal2 32 0 32 0 8 gnd +rlabel metal2 15 0 15 0 2 Out +rlabel metal2 15 73 15 73 5 In +rlabel metal1 0 65 0 65 4 vdd +rlabel metal1 0 12 0 12 3 en +rlabel metal1 0 4 0 4 2 en_bar +<< end >> diff --git a/technology/scn3me_subm/mag_lib/write_driver.mag b/technology/scn3me_subm/mag_lib/write_driver.mag new file mode 100644 index 00000000..8a8be7bd --- /dev/null +++ b/technology/scn3me_subm/mag_lib/write_driver.mag @@ -0,0 +1,230 @@ +magic +tech scmos +timestamp 1424105890 +<< ntransistor >> +rect 9 176 11 188 +rect 17 176 19 188 +rect 15 161 27 163 +rect 9 143 11 147 +rect 17 143 19 147 +rect 10 81 12 88 +rect 18 81 20 88 +rect 8 56 10 63 +rect 16 56 18 63 +rect 24 59 26 63 +<< ptransistor >> +rect 9 124 11 131 +rect 17 124 19 131 +rect 10 106 12 113 +rect 18 106 20 113 +rect 8 37 10 44 +rect 16 37 18 44 +rect 24 37 26 44 +<< ndiffusion >> +rect 8 176 9 188 +rect 11 176 12 188 +rect 16 176 17 188 +rect 19 176 20 188 +rect 15 163 27 164 +rect 15 160 27 161 +rect 12 156 15 159 +rect 12 155 16 156 +rect 8 143 9 147 +rect 11 143 12 147 +rect 16 143 17 147 +rect 19 143 20 147 +rect 9 81 10 88 +rect 12 81 13 88 +rect 17 81 18 88 +rect 20 81 21 88 +rect 25 81 26 85 +rect 7 56 8 63 +rect 10 56 11 63 +rect 15 56 16 63 +rect 18 56 19 63 +rect 23 59 24 63 +rect 26 59 27 63 +<< pdiffusion >> +rect 8 124 9 131 +rect 11 124 12 131 +rect 16 124 17 131 +rect 19 124 20 131 +rect 12 121 16 124 +rect 9 106 10 113 +rect 12 106 13 113 +rect 17 106 18 113 +rect 20 106 21 113 +rect 7 37 8 44 +rect 10 37 11 44 +rect 15 37 16 44 +rect 18 37 19 44 +rect 23 37 24 44 +rect 26 37 27 44 +rect 3 34 7 37 +<< ndcontact >> +rect 4 176 8 188 +rect 12 176 16 188 +rect 20 176 24 188 +rect 15 164 27 168 +rect 15 156 27 160 +rect 4 143 8 147 +rect 12 143 16 147 +rect 20 143 24 147 +rect 5 81 9 88 +rect 13 81 17 88 +rect 21 81 25 88 +rect 3 56 7 63 +rect 11 56 15 63 +rect 19 56 23 63 +rect 27 59 31 63 +<< pdcontact >> +rect 4 124 8 131 +rect 12 124 16 131 +rect 20 124 24 131 +rect 5 106 9 113 +rect 13 106 17 113 +rect 21 106 25 113 +rect 3 37 7 44 +rect 11 37 15 44 +rect 19 37 23 44 +rect 27 37 31 44 +<< psubstratepcontact >> +rect 12 151 16 155 +rect 26 81 30 85 +<< nsubstratencontact >> +rect 12 117 16 121 +rect 3 30 7 34 +<< polysilicon >> +rect 9 193 30 195 +rect 9 188 11 193 +rect 17 188 19 190 +rect 28 184 30 193 +rect 9 174 11 176 +rect 17 171 19 176 +rect 6 169 19 171 +rect 6 166 8 169 +rect 13 161 15 163 +rect 27 161 33 163 +rect 9 147 11 149 +rect 17 147 19 149 +rect 9 131 11 143 +rect 17 131 19 143 +rect 9 123 11 124 +rect 2 121 11 123 +rect 17 123 19 124 +rect 17 121 28 123 +rect 2 74 4 121 +rect 10 113 12 115 +rect 18 113 20 115 +rect 10 88 12 106 +rect 18 105 20 106 +rect 16 103 20 105 +rect 16 91 18 103 +rect 26 99 28 121 +rect 27 95 28 99 +rect 16 89 20 91 +rect 18 88 20 89 +rect 10 80 12 81 +rect 10 78 13 80 +rect 2 70 3 74 +rect 11 70 13 78 +rect 18 78 20 81 +rect 18 76 23 78 +rect 31 70 33 161 +rect 11 68 33 70 +rect 11 66 13 68 +rect 8 64 13 66 +rect 8 63 10 64 +rect 16 63 18 65 +rect 24 63 26 65 +rect 8 44 10 56 +rect 16 51 18 56 +rect 24 51 26 59 +rect 16 49 26 51 +rect 16 44 18 49 +rect 24 44 26 49 +rect 8 27 10 37 +rect 16 13 18 37 +rect 24 35 26 37 +<< polycontact >> +rect 28 180 32 184 +rect 4 162 8 166 +rect 23 95 27 99 +rect 3 70 7 74 +rect 23 74 27 78 +rect 7 23 11 27 +rect 16 9 20 13 +<< metal1 >> +rect 5 188 8 190 +rect 32 180 33 184 +rect 13 168 16 176 +rect 13 164 15 168 +rect 4 147 8 162 +rect 12 155 16 156 +rect 12 147 16 151 +rect 4 131 8 143 +rect 20 141 24 143 +rect 30 141 33 180 +rect 20 137 33 141 +rect 20 131 24 137 +rect 12 121 16 124 +rect 0 117 8 121 +rect 16 117 36 121 +rect 13 113 17 117 +rect 5 103 9 106 +rect 21 103 25 106 +rect 5 100 25 103 +rect 5 88 9 100 +rect 21 99 25 100 +rect 21 95 23 99 +rect 25 81 26 85 +rect 4 63 7 70 +rect 27 63 31 78 +rect 3 50 7 56 +rect 3 47 15 50 +rect 11 44 15 47 +rect 27 44 31 59 +rect 3 34 7 37 +rect 19 34 23 37 +rect 0 30 3 34 +rect 7 30 8 34 +rect 12 30 36 34 +rect 0 23 7 27 +rect 11 23 36 27 +rect 0 16 32 20 +<< m2contact >> +rect 5 190 9 194 +rect 20 188 24 192 +rect 11 156 15 160 +rect 8 117 12 121 +rect 30 81 34 85 +rect 19 63 23 67 +rect 8 30 12 34 +rect 32 16 36 20 +rect 12 9 16 13 +<< metal2 >> +rect 10 194 14 201 +rect 9 190 14 194 +rect 20 192 24 201 +rect 20 176 24 188 +rect 32 160 36 195 +rect 15 156 36 160 +rect 8 34 12 117 +rect 32 85 36 156 +rect 34 81 36 85 +rect 32 71 36 81 +rect 19 67 36 71 +rect 32 20 36 67 +rect 16 9 20 13 +rect 15 0 19 9 +rect 32 0 36 16 +<< m3p >> +rect 0 0 34 201 +<< labels >> +rlabel metal2 20 201 20 201 5 BR +rlabel metal2 10 201 10 201 5 BL +rlabel metal1 0 30 0 30 1 vdd +rlabel metal1 0 23 0 23 3 en +rlabel metal1 0 16 0 16 7 gnd +rlabel metal2 15 0 15 0 1 din +<< end >> diff --git a/technology/scn3me_subm/sp_lib/cell_6t.sp b/technology/scn3me_subm/sp_lib/cell_6t.sp new file mode 100644 index 00000000..0310b9fe --- /dev/null +++ b/technology/scn3me_subm/sp_lib/cell_6t.sp @@ -0,0 +1,10 @@ + +*********************** "cell_6t" ****************************** +.SUBCKT cell_6t bl br wl vdd gnd +M_1 net_1 net_2 vdd vdd p W='0.9u' L=1.2u +M_2 net_2 net_1 vdd vdd p W='0.9u' L=1.2u +M_3 br wl net_2 gnd n W='1.2u' L=0.6u +M_4 bl wl net_1 gnd n W='1.2u' L=0.6u +M_5 net_2 net_1 gnd gnd n W='2.4u' L=0.6u +M_6 net_1 net_2 gnd gnd n W='2.4u' L=0.6u +.ENDS $ cell_6t diff --git a/technology/scn3me_subm/sp_lib/ms_flop.sp b/technology/scn3me_subm/sp_lib/ms_flop.sp new file mode 100644 index 00000000..4cdf309f --- /dev/null +++ b/technology/scn3me_subm/sp_lib/ms_flop.sp @@ -0,0 +1,29 @@ +*master-slave flip-flop with both output and inverted ouput + +.subckt ms_flop din dout dout_bar clk vdd gnd +xmaster din mout mout_bar clk clk_bar vdd gnd dlatch +xslave mout_bar dout_bar dout clk_bar clk_nn vdd gnd dlatch +.ends flop + +.subckt dlatch din dout dout_bar clk clk_bar vdd gnd +*clk inverter +mPff1 clk_bar clk vdd vdd p W=1.8u L=0.6u m=1 +mNff1 clk_bar clk gnd gnd n W=0.9u L=0.6u m=1 + +*transmission gate 1 +mtmP1 din clk int1 vdd p W=1.8u L=0.6u m=1 +mtmN1 din clk_bar int1 gnd n W=0.9u L=0.6u m=1 + +*foward inverter +mPff3 dout_bar int1 vdd vdd p W=1.8u L=0.6u m=1 +mNff3 dout_bar int1 gnd gnd n W=0.9u L=0.6u m=1 + +*backward inverter +mPff4 dout dout_bar vdd vdd p W=1.8u L=0.6u m=1 +mNf4 dout dout_bar gnd gnd n W=0.9u L=0.6u m=1 + +*transmission gate 2 +mtmP2 int1 clk_bar dout vdd p W=1.8u L=0.6u m=1 +mtmN2 int1 clk dout gnd n W=0.9u L=0.6u m=1 +.ends dlatch + diff --git a/technology/scn3me_subm/sp_lib/replica_cell_6t.sp b/technology/scn3me_subm/sp_lib/replica_cell_6t.sp new file mode 100644 index 00000000..1fa75a55 --- /dev/null +++ b/technology/scn3me_subm/sp_lib/replica_cell_6t.sp @@ -0,0 +1,10 @@ + +*********************** "cell_6t" ****************************** +.SUBCKT replica_cell_6t bl br wl vdd gnd +M_1 gnd net_2 vdd vdd p W='0.9u' L=1.2u +M_2 net_2 gnd vdd vdd p W='0.9u' L=1.2u +M_3 br wl net_2 gnd n W='1.2u' L=0.6u +M_4 bl wl gnd gnd n W='1.2u' L=0.6u +M_5 net_2 gnd gnd gnd n W='2.4u' L=0.6u +M_6 gnd net_2 gnd gnd n W='2.4u' L=0.6u +.ENDS $ replica_cell_6t diff --git a/technology/scn3me_subm/sp_lib/sense_amp.sp b/technology/scn3me_subm/sp_lib/sense_amp.sp new file mode 100644 index 00000000..2d0ab02a --- /dev/null +++ b/technology/scn3me_subm/sp_lib/sense_amp.sp @@ -0,0 +1,12 @@ +*********************** "sense_amp" ****************************** + +.SUBCKT sense_amp bl br dout sclk vdd gnd +M_1 dout net_1 vdd vdd p W='5.4*1u' L=0.6u +M_2 dout net_1 net_2 gnd n W='2.7*1u' L=0.6u +M_3 net_1 dout vdd vdd p W='5.4*1u' L=0.6u +M_4 net_1 dout net_2 gnd n W='2.7*1u' L=0.6u +M_5 bl sclk dout vdd p W='7.2*1u' L=0.6u +M_6 br sclk net_1 vdd p W='7.2*1u' L=0.6u +M_7 net_2 sclk gnd gnd n W='2.7*1u' L=0.6u +.ENDS sense_amp + diff --git a/technology/scn3me_subm/sp_lib/tri_gate.sp b/technology/scn3me_subm/sp_lib/tri_gate.sp new file mode 100644 index 00000000..0d298172 --- /dev/null +++ b/technology/scn3me_subm/sp_lib/tri_gate.sp @@ -0,0 +1,13 @@ +*********************** tri_gate ****************************** + +.SUBCKT tri_gate in out en en_bar vdd gnd + +M_1 net_2 in_inv gnd gnd n W='1.2*1u' L=0.6u +M_2 net_3 in_inv vdd vdd p W='2.4*1u' L=0.6u +M_3 out en_bar net_3 vdd p W='2.4*1u' L=0.6u +M_4 out en net_2 gnd n W='1.2*1u' L=0.6u +M_5 in_inv in vdd vdd p W='2.4*1u' L=0.6u +M_6 in_inv in gnd gnd n W='1.2*1u' L=0.6u + + +.ENDS diff --git a/technology/scn3me_subm/sp_lib/write_driver.sp b/technology/scn3me_subm/sp_lib/write_driver.sp new file mode 100644 index 00000000..45fa5097 --- /dev/null +++ b/technology/scn3me_subm/sp_lib/write_driver.sp @@ -0,0 +1,37 @@ +*********************** Write_Driver ****************************** +.SUBCKT write_driver din bl br wen vdd gnd + +**** Inverter to conver Data_in to data_in_bar ****** +M_1 net_3 din gnd gnd n W='1.2*1u' L=0.6u +M_2 net_3 din vdd vdd p W='2.1*1u' L=0.6u + +**** 2input nand gate follwed by inverter to drive BL ****** +M_3 net_2 wen net_7 gnd n W='2.1*1u' L=0.6u +M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u +M_5 net_2 wen vdd vdd p W='2.1*1u' L=0.6u +M_6 net_2 din vdd vdd p W='2.1*1u' L=0.6u + + +M_7 net_1 net_2 vdd vdd p W='2.1*1u' L=0.6u +M_8 net_1 net_2 gnd gnd n W='1.2*1u' L=0.6u + +**** 2input nand gate follwed by inverter to drive BR****** + +M_9 net_4 wen vdd vdd p W='2.1*1u' L=0.6u +M_10 net_4 wen net_8 gnd n W='2.1*1u' L=0.6u +M_11 net_8 net_3 gnd gnd n W='2.1*1u' L=0.6u +M_12 net_4 net_3 vdd vdd p W='2.1*1u' L=0.6u + +M_13 net_6 net_4 vdd vdd p W='2.1*1u' L=0.6u +M_14 net_6 net_4 gnd gnd n W='1.2*1u' L=0.6u + +************************************************ + +M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u +M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u +M_17 net_5 wen gnd gnd n W='3.6*1u' L=0.6u + + + +.ENDS $ write_driver + diff --git a/technology/scn3me_subm/sue_lib/cell_6t.sue b/technology/scn3me_subm/sue_lib/cell_6t.sue new file mode 100644 index 00000000..427b1d05 --- /dev/null +++ b/technology/scn3me_subm/sue_lib/cell_6t.sue @@ -0,0 +1,46 @@ +# SUE version MMI_SUE5.0.7 + +proc SCHEMATIC_cell_6t {} { + make inout -name BL -origin {190 360} + make inout -name BR -origin {830 360} + make input -name WL -origin {240 120} + make global -orient RXY -name vdd -origin {520 160} + make global -name gnd -origin {510 600} + make pmos -orient RY -W 0.9u -L 1.2u -origin {630 230} + make pmos -orient RXY -W 0.9u -L 1.2u -origin {400 230} + make nmos -orient R90 -W 1.2 -L 0.6u -origin {740 360} + make nmos -orient R90X -W 1.2 -L 0.6u -origin {270 360} + make nmos -W 2.4u -L 0.6u -origin {630 490} + make nmos -orient RX -W 2.4u -L 0.6u -origin {400 490} + make_wire 630 550 630 530 + make_wire 400 530 400 550 + make_wire 400 190 400 170 + make_wire 630 170 630 190 + make_wire 400 360 400 270 + make_wire 310 360 400 360 + make_wire 630 360 630 450 + make_wire 630 360 700 360 + make_wire 270 300 270 120 + make_wire 270 120 740 120 + make_wire 740 120 740 300 + make_wire 230 360 190 360 + make_wire 780 360 830 360 + make_wire 510 550 400 550 + make_wire 510 550 630 550 + make_wire 510 550 510 600 + make_wire 520 170 400 170 + make_wire 520 170 630 170 + make_wire 520 160 520 170 + make_wire 240 120 270 120 + make_wire 460 290 630 290 + make_wire 460 290 460 490 + make_wire 460 290 460 230 + make_wire 630 290 630 360 + make_wire 630 290 630 270 + make_wire 570 420 400 420 + make_wire 570 420 570 490 + make_wire 570 420 570 230 + make_wire 400 420 400 360 + make_wire 400 420 400 450 +} + diff --git a/technology/scn3me_subm/sue_lib/ms_flop.sue b/technology/scn3me_subm/sue_lib/ms_flop.sue new file mode 100644 index 00000000..85cc8e03 --- /dev/null +++ b/technology/scn3me_subm/sue_lib/ms_flop.sue @@ -0,0 +1,84 @@ +# SUE version MMI_SUE5.0.7 + +proc SCHEMATIC_ms_flop {} { + make pmos -orient R90X -W 1.8u -L 0.6u -origin {40 250} + make nmos -orient R270 -W 0.9u -L 0.6u -origin {40 380} + make inverter -WP 1.8u -LP 0.6u -WN 0.9u -LN 0.6u -origin {-270 540} + make inverter -WP 1.8u -LP 0.6u -WN 0.9u -LN 0.6u -origin {310 310} + make inverter -orient RX -WP 1.8u -LP 0.6u -WN 0.9u -LN 0.6u -origin {430 730} + make pmos -orient R90X -W 1.8u -L 0.6u -origin {190 670} + make nmos -orient R270 -W 0.9u -L 0.6u -origin {190 780} + make input -name clk -origin {-380 540} + make input -name din -origin {-370 320} + make pmos -orient R90X -W 1.8u -L 0.6u -origin {720 250} + make nmos -orient R270 -W 0.9u -L 0.6u -origin {720 380} + make inverter -WP 1.8u -LP 0.6u -WN 0.9u -LN 0.6u -origin {990 310} + make pmos -orient R90X -W 1.8u -L 0.6u -origin {870 670} + make nmos -orient R270 -W 0.9u -L 0.6u -origin {870 780} + make inverter -WP 1.8u -LP 0.6u -WN 0.9u -LN 0.6u -origin {620 540} + make output -name dout -origin {1410 310} + make output -name dout_bar -origin {1430 930} + make inverter -orient RX -WP 1.8u -LP 0.6u -WN 0.9u -LN 0.6u -origin {1110 730} + make_wire -330 160 40 160 + make_wire 40 160 40 190 + make_wire -370 320 0 320 + make_wire 360 310 480 310 + make_wire 460 730 480 730 + make_wire 230 730 380 730 + make_wire 100 310 100 720 + make_wire 100 720 150 720 + make_wire 100 310 80 310 + make_wire 100 310 280 310 + make_wire 0 250 0 320 + make_wire 0 320 0 380 + make_wire 80 250 80 310 + make_wire 80 310 80 380 + make_wire 40 440 40 540 + make_wire -330 840 190 840 + make_wire 230 670 230 730 + make_wire 230 730 230 780 + make_wire 150 670 150 720 + make_wire 150 720 150 780 + make_wire 190 540 190 610 + make_wire -330 540 -330 840 + make_wire -220 540 40 540 + make_wire 40 540 190 540 + make_wire -380 540 -330 540 + make_wire -330 540 -300 540 + make_wire -330 540 -330 160 + make_wire 720 160 720 190 + make_wire 1140 730 1160 730 + make_wire 780 310 780 720 + make_wire 780 720 830 720 + make_wire 780 310 760 310 + make_wire 780 310 960 310 + make_wire 680 320 680 380 + make_wire 760 250 760 310 + make_wire 760 310 760 380 + make_wire 720 440 720 540 + make_wire 910 670 910 730 + make_wire 910 730 910 780 + make_wire 830 670 830 720 + make_wire 830 720 830 780 + make_wire 870 540 870 610 + make_wire 720 540 870 540 + make_wire 670 540 720 540 + make_wire 480 310 480 730 + make_wire 1160 310 1160 730 + make_wire 530 540 530 160 + make_wire 530 160 720 160 + make_wire 530 540 190 540 + make_wire 530 540 590 540 + make_wire 530 540 530 840 + make_wire 530 840 870 840 + make_wire 680 310 480 310 + make_wire 680 310 680 250 + make_wire 680 310 680 320 + make_wire 950 730 910 730 + make_wire 950 730 1060 730 + make_wire 1040 310 1160 310 + make_wire 1160 310 1410 310 + make_wire 950 930 1430 930 + make_wire 950 730 950 930 +} + diff --git a/technology/scn3me_subm/sue_lib/replica_cell_6t.sue b/technology/scn3me_subm/sue_lib/replica_cell_6t.sue new file mode 100644 index 00000000..56e72056 --- /dev/null +++ b/technology/scn3me_subm/sue_lib/replica_cell_6t.sue @@ -0,0 +1,49 @@ +# SUE version MMI_SUE5.0.7 + +proc SCHEMATIC_replica_cell_6t {} { + make inout -name BL -origin {190 360} + make inout -name BR -origin {830 360} + make input -name WL -origin {240 120} + make global -orient RXY -name vdd -origin {520 160} + make global -name gnd -origin {510 600} + make pmos -orient RY -W 0.9u -L 1.2u -origin {630 230} + make pmos -orient RXY -W 0.9u -L 1.2u -origin {400 230} + make nmos -orient R90 -W 1.2 -L 0.6u -origin {740 360} + make nmos -orient R90X -W 1.2 -L 0.6u -origin {270 360} + make nmos -W 2.4u -L 0.6u -origin {630 490} + make nmos -orient RX -W 2.4u -L 0.6u -origin {400 490} + make_wire 630 550 630 530 + make_wire 400 530 400 550 + make_wire 400 190 400 170 + make_wire 630 170 630 190 + make_wire 400 360 400 270 + make_wire 630 360 630 450 + make_wire 630 360 700 360 + make_wire 270 300 270 120 + make_wire 270 120 740 120 + make_wire 740 120 740 300 + make_wire 230 360 190 360 + make_wire 780 360 830 360 + make_wire 510 550 400 550 + make_wire 510 550 630 550 + make_wire 510 550 510 600 + make_wire 520 170 400 170 + make_wire 520 170 630 170 + make_wire 520 160 520 170 + make_wire 240 120 270 120 + make_wire 460 290 630 290 + make_wire 460 290 460 490 + make_wire 460 290 460 230 + make_wire 630 290 630 360 + make_wire 630 290 630 270 + make_wire 570 420 400 420 + make_wire 570 420 570 490 + make_wire 570 420 570 230 + make_wire 400 420 400 360 + make_wire 400 420 400 450 + make_wire 320 360 320 550 + make_wire 320 550 400 550 + make_wire 320 360 310 360 + make_wire 320 360 400 360 +} + diff --git a/technology/scn3me_subm/sue_lib/sense_amp.sue b/technology/scn3me_subm/sue_lib/sense_amp.sue new file mode 100644 index 00000000..4d29e11a --- /dev/null +++ b/technology/scn3me_subm/sue_lib/sense_amp.sue @@ -0,0 +1,52 @@ +# SUE version MMI_SUE5.0.7 + +proc SCHEMATIC_sense_amp {} { + make inout -name BL -origin {260 10} + make global -orient RXY -name vdd -origin {490 170} + make global -name gnd -origin {480 660} + make input -name sclk -origin {180 610} + make nmos -W 3.9u -L 0.6u -origin {600 500} + make nmos -orient RX -W 3.9u -L 0.6u -origin {370 500} + make pmos -orient RY -W 3u -L 0.6u -origin {600 240} + make pmos -orient RXY -W 3u -L 0.6u -origin {370 240} + make nmos -W 3.9u -L 0.6u -origin {480 610} + make inout -name BR -origin {710 20} + make pmos -W 3.9u -L 0.6u -origin {710 90} + make pmos -orient RX -W 3.9u -L 0.6u -origin {260 90} + make output -orient RXY -name dout -origin {110 370} + make_wire 600 560 600 540 + make_wire 370 540 370 560 + make_wire 370 200 370 180 + make_wire 600 180 600 200 + make_wire 490 180 370 180 + make_wire 490 180 600 180 + make_wire 490 170 490 180 + make_wire 430 300 600 300 + make_wire 430 300 430 500 + make_wire 430 300 430 240 + make_wire 600 300 600 280 + make_wire 540 430 370 430 + make_wire 540 430 540 500 + make_wire 540 430 540 240 + make_wire 370 430 370 460 + make_wire 480 560 600 560 + make_wire 480 560 370 560 + make_wire 480 560 480 570 + make_wire 480 650 480 660 + make_wire 420 610 180 610 + make_wire 650 90 320 90 + make_wire 600 360 710 360 + make_wire 710 360 710 130 + make_wire 600 360 600 300 + make_wire 600 360 600 460 + make_wire 370 370 260 370 + make_wire 260 370 260 130 + make_wire 370 370 370 430 + make_wire 370 370 370 280 + make_wire 260 10 260 50 + make_wire 710 20 710 50 + make_wire 320 90 180 90 + make_wire 180 90 180 610 + make_wire 110 370 260 370 +} + diff --git a/technology/scn3me_subm/sue_lib/tri_gate.sue b/technology/scn3me_subm/sue_lib/tri_gate.sue new file mode 100644 index 00000000..d296171f --- /dev/null +++ b/technology/scn3me_subm/sue_lib/tri_gate.sue @@ -0,0 +1,37 @@ +# SUE version MMI_SUE5.0.7 + +proc SCHEMATIC_tri_gate {} { + make global -orient RXY -name vdd -origin {630 150} + make global -name gnd -origin {630 570} + make input -name tri_in -origin {320 340} + make output -name tri_out -origin {690 360} + make input -name en -origin {570 410} + make input -name en_bar -origin {570 310} + make nmos -W 1.2u -L 0.6u -origin {630 490} + make nmos -W 1.2u -L 0.6u -origin {630 410} + make pmos -orient RY -W 2.4u -L 0.6u -origin {630 310} + make pmos -orient RY -W 2.4u -L 0.6u -origin {630 230} + make pmos -orient RY -W 2.4u -L 0.6u -origin {380 290} + make nmos -W 1.2u -L 0.6u -origin {380 400} + make_wire 570 490 470 490 + make_wire 470 230 570 230 + make_wire 630 550 380 550 + make_wire 380 550 380 440 + make_wire 630 550 630 570 + make_wire 630 550 630 530 + make_wire 630 170 380 170 + make_wire 380 170 380 250 + make_wire 630 170 630 190 + make_wire 630 170 630 150 + make_wire 320 340 320 400 + make_wire 320 340 320 290 + make_wire 380 340 470 340 + make_wire 380 340 380 330 + make_wire 380 340 380 360 + make_wire 470 340 470 490 + make_wire 470 340 470 230 + make_wire 630 360 630 350 + make_wire 630 360 630 370 + make_wire 630 360 690 360 +} + diff --git a/technology/scn3me_subm/sue_lib/write_driver.sue b/technology/scn3me_subm/sue_lib/write_driver.sue new file mode 100644 index 00000000..de3909a7 --- /dev/null +++ b/technology/scn3me_subm/sue_lib/write_driver.sue @@ -0,0 +1,44 @@ +# SUE version MMI_SUE5.0.7 + +proc SCHEMATIC_write_driver {} { + make inout -name BL -origin {550 260} + make inout -name BR -origin {830 250} + make inverter -WP 2.1u -LP 0.6u -WN 1.2u -LN 0.6u -origin {280 520} + make nand2 -WP 2.1u -WN 2.1u -origin {90 360} + make inverter -WP 2.1u -LP 0.6u -WN 1.2u -LN 0.6u -origin {270 360} + make nmos -W 3.6u -L 0.6u -origin {830 410} + make nmos -W 3.6u -L 0.6u -origin {710 610} + make global -name gnd -origin {710 690} + make nand2 -WP 2.1u -WN 2.1u -origin {90 520} + make nmos -W 3.6u -L 0.6u -origin {550 410} + make input -name wen -origin {-290 340} + make input -name din -origin {-290 380} + make inverter -WP 2.1u -LP 0.6u -WN 1.2u -LN 0.6u -origin {-80 540} + make_wire 160 360 240 360 + make_wire 830 250 830 370 + make_wire 550 260 550 370 + make_wire 550 450 550 560 + make_wire 550 560 710 560 + make_wire 710 560 710 570 + make_wire 710 560 830 560 + make_wire 830 560 830 450 + make_wire 710 650 710 690 + make_wire 250 520 160 520 + make_wire 770 410 770 520 + make_wire 770 520 330 520 + make_wire 320 360 490 360 + make_wire 490 360 490 410 + make_wire -180 380 -290 380 + make_wire -180 380 70 380 + make_wire -180 540 -110 540 + make_wire -180 380 -180 540 + make_wire -30 540 70 540 + make_wire 20 340 20 500 + make_wire 20 500 70 500 + make_wire 20 340 70 340 + make_wire -240 340 -240 610 + make_wire -240 610 650 610 + make_wire -240 340 20 340 + make_wire -240 340 -290 340 +} + diff --git a/technology/scn3me_subm/tech/__init__.py b/technology/scn3me_subm/tech/__init__.py new file mode 100755 index 00000000..798e230d --- /dev/null +++ b/technology/scn3me_subm/tech/__init__.py @@ -0,0 +1,9 @@ +""" +Python GDS Mill Package + +GDS Mill is a Python package for the creation and manipulation of binary GDS2 layout files. +""" + +from tech import * +from ptx_port import * + diff --git a/technology/scn3me_subm/tech/ptx_port.py b/technology/scn3me_subm/tech/ptx_port.py new file mode 100755 index 00000000..a10a18a8 --- /dev/null +++ b/technology/scn3me_subm/tech/ptx_port.py @@ -0,0 +1,29 @@ +""" +This class should be called in the ptx function to draw addtional layer as some layer may not exist in the cmrf7sf technology +""" +import globals +import design +import tech + + + +class ptx_port: + def __init__(self,name): + self.name=name + self.width=0 + self.height=0 + + + + def draw(self,instance_to_draw,offset,tx_type,height,width,tx=1): + self.height=height + self.width=width + self.offset=offset + if tx_type == "pmos": + # draw BP layer + if tx==1: + instance_to_draw.add_rect(tech.layer["BP"],[offset[0]-tech.drc["BP_enclosure_active"],offset[1]-tech.drc["BP_enclosure_gate"]],width+2*tech.drc["BP_enclosure_active"],height+2*tech.drc["BP_enclosure_gate"]) + else: + instance_to_draw.add_rect(tech.layer["BP"],[offset[0]-tech.drc["BP_enclosure_active"],offset[1]-tech.drc["BP_enclosure_active"]],width+2*tech.drc["BP_enclosure_active"],height+2*tech.drc["BP_enclosure_active"]) + + diff --git a/technology/scn3me_subm/tech/tech.py b/technology/scn3me_subm/tech/tech.py new file mode 100755 index 00000000..f5397523 --- /dev/null +++ b/technology/scn3me_subm/tech/tech.py @@ -0,0 +1,223 @@ +""" +Class containing the process technology parameters. +""" +info={} +info["name"]="scn3me_subm" +info["body_tie_down"] = 0 +info["has_pwell"] = True +info["has_nwell"] = True + +#GDS file info +GDS={} +GDS["unit"]=(0.001,1e-6) + +##################################################################################################### +##GDS Layer Map###################################################################################### +##################################################################################################### + +# create the GDS layer map +layer={} +layer["vtg"] = -1 +layer["vth"] = -1 +layer["contact"] = 25 +layer["pwell"] = 41 +layer["nwell"] = 42 +layer["active"] = 43 +layer["pimplant"] = 44 +layer["nimplant"] = 45 +layer["poly"] = 46 +layer["poly_contact"] = 47 +layer["active_contact"] = 48 +layer["metal1"] = 49 +layer["via1"] = 50 +layer["metal2"] = 51 +layer["via2"] = 61 +layer["metal3"] = 62 +layer["text"] = 83 +layer["boundary"] = 83 + +##################################################################################################### +##END GDS Layer Map################################################################################## +##################################################################################################### + +##################################################################################################### +##GDS Library File Parameters######################################################################## +##################################################################################################### + +import os + +cell = {} +sense_amp = {} +write_driver = {} +tri_gate = {} +ms_flop = {} +replica_cell = {} + + +##################################################################################################### +##END GDS Library File Parameters#################################################################### +##################################################################################################### + +##################################################################################################### +##DRC/LVS Rules Setup################################################################################ +##################################################################################################### + +#technology parameter +parameter={} +parameter["min_tx_size"] = 1.2 +parameter["pinv_beta"] = 2 #for use in pinv + +drclvs_home=os.environ.get("DRCLVS_HOME") + +drc={} +#grid size +drc["grid"]=0.15 +#DRC/LVS test set_up +drc["drc_rules"]=drclvs_home+"/calibreDRC_scn3me_subm.rul" +drc["lvs_rules"]=drclvs_home+"/calibreLVS_scn3me_subm.rul" +drc["layer_map"]=os.environ.get("OPENRAM_TECH")+"/layers.map" + + +# minwidth_tx withcontact +drc["minwidth_tx"] = 1.2 +drc["minlength_channel"] = 0.6 + +#well rules +drc["pwell_enclose_nwell"] = 0 +drc["minwidth_well"] = 3.6 + +#poly rules +drc["minwidth_poly"] = 0.6 +drc["minheight_poly"] = 0.0 +drc["poly_to_poly"] = 0.9 +drc["poly_extend_active"] = 0.6 +drc["poly_to_polycontact"] = 1.2 +drc["active_enclosure_gate"] = 0.0 +drc["poly_to_active"] = 0.3 +drc["minarea_poly"] = 0.0 + +#active +drc["active_extend_gate"] = 0 +drc["active_to_body_active"] = 1.2 # Fix me +drc["minwidth_active"] = 0.9 +drc["minheight_active"] = 0.9 +drc["minarea_active"] = 0.0 +drc["active_to_active"] = 0.9 +drc["well_enclosure_active"] = 1.8 +drc["well_extend_active"] = 1.8 + +#Implant (None in IBM) +drc["implant_to_gate"] = 0 +drc["implant_to_channel"] = 0 +drc["implant_to_contact"] = 0 +drc["implant_to_implant"] = 0 +drc["minwidth_implant"] = 0 + +#Contact +drc["minwidth_contact"] = 0.6 +drc["minwidth_active_contact"] = 0.6 +drc["minwidth_poly_contact"] = 0.6 + +drc["active_enclosure_contact"] = 0.3 +drc["active_extend_contact"] = 0.3 +drc["poly_enclosure_contact"] = 0.3 +drc["poly_extend_contact"] = 0.3 +drc["contact_to_poly"] = 0.6 + +drc["contact_to_contact"] = 0.9 +drc["active_contact_to_active_contact"] = 0.9 +drc["poly_contact_to_poly_contact"] = 0.9 + + +drc["active_extend_active_contact"] = 0.3 +drc["poly_extend_poly_contact"] = 0.3 +drc["active_enclosure_active_contact"] = 0.3 +drc["poly_enclosure_poly_contact"] = 0.3 + +#Metal1 +drc["minwidth_metal1"] = 0.9 +drc["minheight_metal1"] = 0 +drc["metal1_to_metal1"] = 0.9 +drc["metal1_to_contact"] = 0.9 +drc["metal1_enclosure_contact"] = 0.3 +drc["metal1_extend_contact"] = 0.3 +drc["metal1_extend_via1"] = 0.3 +drc["metal1_enclosure_via1"] = 0.3 +drc["minarea_metal1"] = 0 +drc["metal1_enclosure_active_contact"] = 0.3 +drc["metal1_enclosure_poly_contact"] = 0.3 +drc["metal1_extend_active_contact"] = 0.3 +drc["metal1_extend_poly_contact"] = 0.3 + +#via1 +drc["minwidth_via1"] = 0.6 +drc["via1_to_via1"] = 0.6 +drc["minselect_overlap_via1"] = 0.3 # Fix me + +#Metal2 +drc["minwidth_metal2"] = 0.9 +drc["minheight_metal2"] = 0 +drc["metal2_to_metal2"] = 0.9 +drc["metal2_extend_via1"] = 0.3 +drc["metal2_enclosure_via1"] = 0.3 +drc["metal2_extend_via2"] = 0.3 +drc["metal2_enclosure_via2"] = 0.3 +drc["minarea_metal2"] = 0 + +#Via2 +drc["minwidth_via2"] = 0.6 +drc["via2_to_via2"] = 0.9 + +#Metal3 +drc["minwidth_metal3"] = 1.5 +drc["minheight_metal3"] = 0.0 +drc["metal3_to_metal3"] = 0.9 +drc["metal3_extend_via2"] = 0.6 +drc["metal3_enclosure_via2"] = 0.6 +drc["minarea_metal3"] = 0 + +##################################################################################################### +##END DRC/LVS Rules################################################################################## +##################################################################################################### + +##################################################################################################### +##Spice Simulation Parameters######################################################################## +##################################################################################################### + +# spice model info +spice={} +spice["nmos"]="n" +spice["pmos"]="p" +spice["fet_models"] = [os.environ.get("DRCLVS_HOME")+"/on_c5n.sp"] + +#spice stimulus related variables +spice["clock_period"] = 10.0 +spice["supply_voltage"] = 5.0 #vdd in [Volts] +spice["gnd_voltage"] = 0.0 #gnd in [Volts] +spice["rise_time"] = 0.001 #rise time in [Nano-seconds] +spice["fall_time"] = 0.001 #fall time in [Nano-seconds] +spice["temp"] = 25 #temperature in [Celsius] + +#parasitics of metal for bit/word lines +spice["bitline_res"] = 0.1 #bitline resistance in [Ohms/micro-meter] +spice["bitline_cap"] = 0.2 #bitline capacitance in [Femto-farad/micro-meter] +spice["wordline_res"] = 0.1 #wordline resistance in [Ohms/micro-meter] +spice["wordline_cap"] = 0.2 #wordline capacitance in [Femto-farad/micro-meter] +spice["FF_in_cap"] = 9.8242 #Input capacitance of ms_flop (Din) [Femto-farad] +spice["tri_gate_out_cap"] = 1.4980 #Output capacitance of tri_gate (tri_out) [Femto-farad] + + +#sram signal names +spice["vdd_name"] = "vdd" +spice["gnd_name"] = "gnd" +spice["control_signals"] = ["CSb", "WEb", "OEb"] +spice["data_name"] = "DATA" +spice["addr_name"] = "ADDR" +spice["pmos_name"] = spice["pmos"] +spice["nmos_name"] = spice["nmos"] +spice["minwidth_tx"] = drc["minwidth_tx"] +spice["channel"] = drc["minlength_channel"] +spice["clk"] = "clk" + +# estimated feasible period in ns +spice["feasible_period"] = 5 diff --git a/technology/setup_scripts/README b/technology/setup_scripts/README new file mode 100644 index 00000000..628cec7e --- /dev/null +++ b/technology/setup_scripts/README @@ -0,0 +1,4 @@ +THIS DIRECTORY SHOULD ONLY CONTAIN SETUP SCRIPTS FOR TECHNOLOGIES. + +These scripts will be called automatically by the import_tech functions in +globals.py in the compiler directory. diff --git a/technology/setup_scripts/setup_openram_freepdk45.py b/technology/setup_scripts/setup_openram_freepdk45.py new file mode 100644 index 00000000..a9d36b8b --- /dev/null +++ b/technology/setup_scripts/setup_openram_freepdk45.py @@ -0,0 +1,34 @@ +#!/usr/bin/python +""" +This type of setup script should be placed in the setup_scripts directory in +the trunk +""" + +import sys +import os + +TECHNOLOGY = "freepdk45" +LOCAL = "{0}/..".format(os.path.dirname(__file__)) + +########################## +# FreePDK 45 paths +# DON'T DELETE THESE +PDK_DIR="/mada/software/techfiles/FreePDK45" +os.environ["PDK_DIR"] = PDK_DIR +os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/ncsu_basekit/cdssetup".format(PDK_DIR) +os.environ["CDS_SITE"] = PDK_DIR +os.environ["MGC_TMPDIR"] = "/tmp" + +########################### +#Openram Paths +# DON'T DELETE THESE +DRCLVS_HOME="/mada/software/techfiles/FreePDK45-1.4/ncsu_basekit/techfile/calibre" +os.environ["DRCLVS_HOME"] = DRCLVS_HOME + +########################## +#Paths required for OPENRAM to function +sys.path.append("{0}/{1}".format(LOCAL,TECHNOLOGY)) + + + + diff --git a/technology/setup_scripts/setup_openram_scn3me_subm.py b/technology/setup_scripts/setup_openram_scn3me_subm.py new file mode 100644 index 00000000..47355e73 --- /dev/null +++ b/technology/setup_scripts/setup_openram_scn3me_subm.py @@ -0,0 +1,29 @@ +#!/usr/bin/python +""" +This type of setup script should be placed in the setup_scripts directory in the trunk +""" + +import sys +import os + +TECHNOLOGY = "scn3me_subm" + + +########################## +# CDK paths? + +#os.environ["CDK_DIR"] = CDK_DIR #PDK path +#os.environ["SYSTEM_CDS_LIB_DIR"] = "{0}/cdssetup".format(CDK_DIR) #CDS library path +#os.environ["CDS_SITE"] = CDK_DIR #CDS path +os.environ["MGC_TMPDIR"] = "/tmp" #temp folder path + +########################### +#Openram Paths +# DON'T DELETE THIS LINE +DRCLVS_HOME="/mada/software/techfiles/scn3me_subm" +os.environ["DRCLVS_HOME"] = DRCLVS_HOME #DRC and LVS path + +########################## +#Paths required for OPENRAM to function +LOCAL = "{0}/..".format(os.path.dirname(__file__)) #OPENRAM Trunk path +sys.path.append("{0}/{1}".format(LOCAL,TECHNOLOGY))