%% This is the package dsptricks
%%
%% (c) Paolo Prandoni <paolo.prandoni _at_ epfl.ch>
%%
%% This program can be redistributed and/or modified under the terms
%% of the LaTeX Project Public License Distributed from CTAN archives
%% in directory macros/latex/base/lppl.txt.
%%
%% DESCRIPTION:
%% `dsptricks' is a LaTeX package based on PSTricks to plot discrete- and continuous-time
%% signals, pole-zero plots and DSP block diagrams. The package has been developed
%% while writing the textbook "Signal Processing for Communication" by P. Prandoni
%% and M. Vetterli, freely available at www.sp4comm.org
%%
%% v1.1, November 2023
%%
\RequirePackage{pstricks}
\RequirePackage{pstricks-add}
\RequirePackage{pst-xkey}
\RequirePackage{calc}
\RequirePackage{fp}
\RequirePackage{ifthen}
%
\ProvidesPackage{dsptricks}[2023/11/08 package for signal processing graphics]
% turn off FP messages
\FPmessagesfalse
\makeatletter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Sizes and units:
%%
%% default values for plot size
%%
\newlength{\dspWidth}\setlength{\dspWidth}{0.7\textwidth}
\newlength{\dspHeight}\setlength{\dspHeight}{0.43\dspWidth}
\def\dspAxisColor{black}
%
%% Actual size (this is the size of the chart's frame, labels are extra)
\newlength{\dspW}
\newlength{\dspH}
%% Basic Unit is a function of plot size
\newlength{\dspBU}
%% Derived units:
\newlength{\dspLineWidth}
\newlength{\dspStemWidth}
\newlength{\dspDotSize}
\newlength{\dspTickLen}
\newlength{\dspXTickGap}
\newlength{\dspYTickGap}
\newlength{\dspTickLineWidth}
\newlength{\dspFrameLineWidth}
%% psTricks units
\newlength{\dspUnitX}
\newlength{\dspUnitY}
\newlength{\dspTmpLen}
%% booleans
\newif\ifXTicks
\newif\ifDoXTicks
\newif\ifYTicks
\newif\ifDoYTicks
\newif\ifXLabel
\newif\ifYLabel
\newif\ifXAxisExp
\newif\ifYAxisExp
\newif\ifXAxisFreq
\newif\ifXInside
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Environment to clip plots to within the frame box. Do not leave
%% spaces in the environment's body otherwise graph will shift
%%
\newenvironment{dspClip}{%
\psclip{\psframe[dimen=middle,linestyle=none](\dspMinX,\dspMinY)(\dspMaxX,\dspMaxY)}}{%
\endpsclip}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Plotting discrete-time signals in the dspPlot environment:
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Keys for plotting functions (these are set together with pstricks keys
%% so they must use the [psset] prefix and be declared)
%%
%% xmin=N,
%% xmax=N range for the plotted signal
%%
\define@key[psset]{dspData}{xmin}{\def\dspXmin{#1}}
\define@key[psset]{dspData}{xmax}{\def\dspXmax{#1}}
\pst@addfams{dspData}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Plot discrete-time points starting at a given abscissa
%% Data in this case is only ordinates
%%
%% \dspTapsAt{x0}{y0 y1 y2,...}
%%
\newcommand{\dspTapsAt}[3][]{%
% use postscript to iterate over space-separated list and create indices
\listplot[plotstyle=LineToXAxis, linestyle=solid,%
showpoints=true, dotstyle=*, linewidth=\dspStemWidth,%
dotsize=\dspDotSize, #1]{%
#2
[#3] { % n []
% n a0
1 index % n a0 n
1 add
} forall
pop
}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Plot discrete-time signal from a data file
%%
%% \dspTapsFile[OPTIONS]{FILE}
%%
\newcommand{\dspTapsFile}[2][]{%
\readdata{\data}{#2}%
\listplot[plotstyle=LineToXAxis, linestyle=solid,%
showpoints=true, dotstyle=*,%
linewidth=\dspStemWidth, dotsize=\dspDotSize,%
#1]{\data}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Plot smooth function from a set of points
%%
%% \dspFuncData[OPTIONS]{x1 y1 x2 y2 ...}
%%
\newcommand{\dspFuncData}[2][]{%
\listplot[linewidth=\dspLineWidth,linejoin=1,plotstyle=line,#1]{\expandafter\m@keList#2 \relax}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Plot smooth function from a set of points starting at a given point
%% Data in this case is only ordinates
%%
%% \dspFuncDataAt[OPTIONS]{x1}{y1 y2 ...}
%%
\newcommand{\dspFuncDataAt}[3][]{%
\listplot[linewidth=\dspLineWidth,linejoin=1,plotstyle=line,#1]{%
#2
[#3] { % n []
% n a0
1 index % n a0 n
1 add
} forall
pop
}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Plot smooth function from a data file
%% file must contain a list of space-separated abscissa and ordinate pairs
%%
%% \dspFuncFile[OPTIONS]{FILE}
%%
\newcommand{\dspFuncFile}[2][]{%
\readdata{\data}{#2}%
\listplot[linewidth=\dspLineWidth,linejoin=1,plotstyle=line,#1]{\data}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%% Periodize function over the [-1, 1] interval
%
\newcommand{\dspPeriod}[1]{ #1 sub #1 2 mul div dup floor sub #1 2 mul mul #1 sub }
\newcommand{\dspPeriodize}{ \dspPeriod{1} }
\newcommand{\dspMainPeriod}[1][]{%
\psframe[linecolor=lightgray,linewidth=0.4pt,#1](-1,\dspMinY)(1,\dspMaxY)}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%% Plot text
% \dspText(x,y){text}
\newcommand{\dspText}[2]{%
\rput*[B]{0}#1{#2}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%% Pole-Zero Plots
%
% Specialized options:
%
% circle=["true" | "false"] draw unit circle
% clabel="text" set a label at circle unit
% roc
%
\define@key{dspPZ}{width}{\setlength{\dspW}{#1}\setlength{\dspH}{#1}}
\define@key{dspPZ}{height}{\setlength{\dspH}{#1}}
\define@key{dspPZ}{circle}{\def\dspCircle{#1}}
\define@key{dspPZ}{clabel}{\def\dspCircleLabel{#1}}
\define@choicekey*+{dspPZ}{xticks}[\ll\ln]{auto,none}[auto]{%
\DoXTickstrue\def\incX{-1}
\ifcase\ln\relax
\relax
\or
\DoXTicksfalse
\fi}{%
\DoXTickstrue
\def\incX{#1}}
\define@choicekey*+{dspPZ}{yticks}[\ll\ln]{auto,none}[auto]{%
\DoYTickstrue\def\incY{-1}
\ifcase\ln\relax
\relax
\or
\DoYTicksfalse
\fi}{%
\DoYTickstrue
\def\incY{#1}}
\newif\ifComplexLabels
\define@choicekey*{dspPZ}{cunits}[\ll\ln]{true,false}[true]{%
\ifcase\ln\relax
\ComplexLabelstrue
\or
\ComplexLabelsfalse
\fi}
\define@key{dspPZ}{roc}{\def\PZCROC{#1}}
\define@key{dspPZ}{antiroc}{\def\PZAROC{#1}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
%% Quick image plot, with frame but no additional space around it
\newcommand{\dspShowImage}[2][0.37\textwidth]{%
\psset{xunit=#1,yunit=#1}%
\begin{pspicture}(0,0)(0,1)%
\includegraphics[width=#1,height=#1]{#2}%
\psframe[dimen=middle,linewidth=1.1pt](-0.01,0)(-.99,.99)
\end{pspicture}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Helpers
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This is called at each plot setup with xmin, xmax, ymin, ymax and sidegap
%% and sets all lengths
\def\dspSetDims#1,#2,#3,#4,#5\relax{%
\def\sideGap{#5}%
% default sidegap: 5% of range
\FPifneg\sideGap\FPupn\sideGap{#1 #2 - 0.05 * 0.5 + 2 trunc}\fi%
\FPifgt\sideGap{1}\FPtrunc\sideGap\sideGap{0}\fi%
%%
%% active range for the independent variable
\def\dspMinActX{#1}%
\def\dspMaxActX{#2}%
%% axes range (including sidegap)
\FPupn\dspMinX{\sideGap{} #1 - clip}%
\FPupn\dspMaxX{\sideGap{} #2 + clip}%
\FPupn\dspRngX{#1 #2 - \sideGap{} 2 * + clip}%
\FPupn\dspMinY{#3 0 + clip}%
\FPupn\dspMaxY{#4 0 + clip}%
\FPupn\dspRngY{#3 #4 - clip}%
%%
% if y-range is across zero we'll need to draw the x-axis
\FPmul\u\dspMinY\dspMaxY%
\FPifneg\u\XAxisExptrue\else\XAxisExpfalse\fi%
%% same for x (but usually we never draw the y axis)
\FPmul\u\dspMinX\dspMaxX%
\FPifneg\u\YAxisExptrue\else\YAxisExpfalse\fi%
%%
%% pstricks units: we scale the units so that coordinates are "real" coords on the axes
\FPupn\tmp{\dspRngX{} {\strip@pt\dspW} / }
\setlength{\dspUnitX}{\tmp pt}%
\FPupn\tmp{\dspRngY{} {\strip@pt\dspH} / }%
\setlength{\dspUnitY}{\tmp pt}%
%%
%% basic unit based on the size of the image: ~1pt for a 10x5 cm box
\FPupn\g{2 {\strip@pt\dspW} {\strip@pt\dspH} * root 0.005 * 0 round 1 max}%
\setlength{\dspBU}{1pt*\g}%
%% derived sizes: lines and fonts
\setlength{\dspLineWidth}{1.8\dspBU}%
\setlength{\dspDotSize}{5\dspBU}%
\setlength{\dspStemWidth}{1.4\dspBU}%
\fontsize{9\dspBU}{10\dspBU}\selectfont%
% %% ticks on axes
\setlength{\dspTickLen}{4\dspBU}%
\setlength{\dspTickLineWidth}{0.4\dspBU}%
\setlength{\dspXTickGap}{6\dspBU}\addtolength{\dspXTickGap}{2ex}%
\setlength{\dspYTickGap}{2\dspBU}%
\setlength{\dspFrameLineWidth}{2.2\dspTickLineWidth}%
%%
%% height of a line of text in psticks units
\setlength{\dspTmpLen}{1em}%
\FPupn\dspTSX{{\strip@pt\dspUnitX} {\strip@pt\dspTmpLen} / }%
\FPupn\dspTSY{{\strip@pt\dspUnitY} {\strip@pt\dspTmpLen} / }%
%%
%% find the coordinates of the plot's bounding box, including labels
\ifYLabel%
\FPupn\LX{\dspTSX{} 4 * \dspMinX{} -}%
\FPupn\RX{\dspTSX{} 4 * \dspMaxX{} +}%
\else%
\FPupn\LX{\dspTSX{} 2 * \dspMinX{} -}%
\FPupn\RX{\dspTSX{} 2 * \dspMaxX{} +}%
\fi
\FPupn\TY{\dspTSY{} \dspMaxY{} +}%
\def\outTicks{%
\ifXTicks\FPupn\BY{\dspTSY{} 2.5 * \dspMinY{} -}\else\FPupn\BY{\dspTSY{} \dspMinY{} -}\fi%
\ifXLabel\FPupn\BY{\dspTSY{} 1.5 * \BY{} -}\else\relax\fi }%
\ifXAxisExp % ticks are inside
\ifXLabel\FPupn\BY{\dspTSY{} 2.5 * \dspMinY{} -}\else\FPupn\BY{\dspTSY{} \dspMinY{} -}\fi%
\else% ticks are outside
\outTicks \fi%
% did we select explicit outside ticks?
\ifXTicksOut\outTicks \fi%
%%
%% x-axis resolution for plotting functions: ~60 values/cm
\FPupn\dspPSPoints{{\strip@pt\dspW} 2 * 0 trunc}%
%%
\psset{xunit=\dspUnitX, yunit=\dspUnitY}%
\psset{linewidth=\dspLineWidth}%www
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Draw axis labels (if required) under x axis and to left and right of y axes
%%
\def\dspLabels{%
% horizontal label
\ifXTicksOut\XAxisExpfalse\fi%
\ifXLabel%
\FPupn\mid{\dspMinX{} \dspMaxX{} + 0.5 mul}%
\ifXAxisExp%
\FPupn\pos{\dspTSY{} 2 * \dspMinY{} -}%
\else%
\ifXTicks%
\FPupn\pos{\dspTSY{} 3 * \dspMinY{} -}%
\else%
\FPupn\pos{\dspTSY{} 2 * \dspMinY{} -}%
\fi%
\fi%
\rput[b]{0}(\mid,\pos){\dspXLabel}%
\fi%
%
\FPupn\mid{\dspMinY{} \dspMaxY{} + 0.5 mul}%
\ifYTicks%
\FPupn\pos{\dspTSX{} 3.4 * \dspMinX{} -}%
\else%
\FPupn\pos{\dspTSX{} 1 * \dspMinX{} -}%
\fi%
\rput[b]{90}(\pos,\mid){\dspYLabel}
%%
\FPupn\pos{\dspTSX{} 3 * \dspMaxX{} +}%
\rput[b]{-90}(\pos,\mid){\dspYLabelR}}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This sets axes ticks (spacing and number) and prepares macros
%% for plotting ticks and tick labels
\def\dspSetupAxes{%
%% tick increments; if incX/Y is zero, compute suitable value
\FPifgt\incX{0}\FPupn\incX{\incX{} 2 trunc}%
\else\FPupn\incX{\dspMinActX{} \dspMaxActX{} - 1 + 6 swap / 0 trunc 1 max}\relax\fi%
\FPifgt\incY{0}\FPupn\incY{\incY{} 2 trunc}%
\else\FPupn\incY{\dspMinY{} \dspMaxY{} - 1 + 6 swap / 0 trunc 1 max}\relax\fi%
%%
%% now find starting point and increment for ticks (and number of ticks)
%% start with y-axis:
\ifXAxisExp% is y=0 explicit or does it coincide with the lower edge?
\def\haY{0}%
% make sure ticks hit zero; add small epsilons to avoid ticks at the limits
\FPupn\stY{\incY{} \dspMinY{} / 0.001 + 0 trunc \incY{} * 2 trunc}%
\FPupn\tlimY{\stY{} \dspMaxY{} - 0.001 - \incY{} swap / abs 1 + 0 trunc}%
\else%
\def\haY{\dspMinY}%
\FPupn\tlimY{\incY{} \dspRngY{} / abs 1 + 0 trunc}%
\FPupn\stY{\dspMinY{} 2 trunc}% \incY{} 1 \tlimY{} - * \dspRngY{} - 0.5 * \dspMinY{} + 2 trunc}%
\fi%
% x-ticks outside of the box anyway?
\ifXTicksOut\def\haY{\dspMinY}\relax\fi%
% round off to integer if possible:
\FPupn\v{\stY{} 0 trunc \stY{} - abs \incY{} 0 trunc \incY{} - abs + clip}%
\FPifeq\v{0}\FPtrunc\stY\stY{0}\FPtrunc\incY\incY{0}\relax\fi%
%
%% now x-axis:
\ifYAxisExp% we almost never draw the y-axis but need to see if x-axis spans zero
% make sure ticks hit zero; add small epsilons to avoid ticks at the limits
\FPupn\stX{\incX{} \dspMinX{} / 0 trunc \incX{} * 2 trunc}%
\FPupn\tlimX{\incX{} \stX{} \dspMaxX{} - / abs 1 + 0 trunc}%
\else%
\FPupn\tlimX{\incX{} \dspRngX{} / abs 1 + 0 trunc}%
\FPupn\stX{\dspMinX{} 2 trunc}% \FPupn\stX{\incX{} 1 \tlimX{} - * \dspRngX{} - 0.5 * \dspMinX{} + 2 trunc}%
\fi%
\FPupn\v{\stX{} 0 trunc \stX{} - abs \incX{} 0 trunc \incX{} - abs +}%
\FPifzero\v\FPtrunc\stX\stX{0}\FPtrunc\incX\incX{0}\relax\fi%
%%
%% macros for drawing x-ticks
\ifXInside%
\FPupn\tickEndX{{\strip@pt\dspUnitY} {\strip@pt\dspTickLen} /}%
\FPupn\tickTxtX{{\strip@pt\dspUnitY} {\strip@pt\dspXTickGap} 0.9 * / \dspMinY{} -}% tick label pos in PS units
\FPupn\tickXB{\tickEndX{} \dspMinY{} +}%
\FPupn\tickXT{\tickEndX{} \dspMaxY{} -}%
\gdef\dspTickX##1{%
\psline[linewidth=\dspTickLineWidth,linecolor=\dspAxisColor](##1,\dspMinY)(##1,\tickXB)%
\psline[linewidth=\dspTickLineWidth,linecolor=\dspAxisColor](##1,\dspMaxY)(##1,\tickXT)}%
% \gdef\dspTickLabelX##1##2{\uput{{2\dspYTickGap}}[180]{{0}}(\dspMinX,##1){{##2}}}%
\gdef\dspTickLabelX##1##2{\rput*[B]{{0}}(##1,\tickTxtX){{##2}}}%
\else%
\FPupn\tickEndX{{\strip@pt\dspUnitY} {\strip@pt\dspTickLen} / \haY{} -}% tick len in PS units
\FPupn\tickTxtX{{\strip@pt\dspUnitY} {\strip@pt\dspXTickGap} / \haY{} -}% tick label pos in PS units
\gdef\dspTickX##1{\psline[linewidth=\dspTickLineWidth,linecolor=\dspAxisColor](##1,\haY)(##1,\tickEndX)}%
\gdef\dspTickLabelX##1##2{\rput*[B]{{0}}(##1,\tickTxtX){{##2}}}%
\fi%
%% macros for drawing y-ticks
\FPupn\tickEndY{{\strip@pt\dspUnitX} {\strip@pt\dspTickLen} /}%
\FPupn\tickXB{\tickEndY{} \dspMinX{} +}%
\FPupn\tickXT{\tickEndY{} \dspMaxX{} -}%
\gdef\dspTickY##1{%
\psline[linewidth=\dspTickLineWidth,linecolor=\dspAxisColor](\dspMinX,##1)(\tickXB,##1)%
\psline[linewidth=\dspTickLineWidth,linecolor=\dspAxisColor](\dspMaxX,##1)(\tickXT,##1)}%
\gdef\dspTickLabelY##1##2{\uput{{2\dspYTickGap}}[180]{{0}}(\dspMinX,##1){{##2}}}%
\gdef\dspTickLabelYR##1##2{\uput{{2\dspYTickGap}}[0]{{0}}(\dspMaxX,##1){{##2}}}%
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% simplify fractions of pi
\newcount\dspNum \newcount\dspDen
\def\simplifyPiFrac#1#2{%
\ifnum#1=0 $0$\else % zero is zero
\dspNum=#1 \dspDen=#2 %
\divide\dspNum by\dspDen \multiply\dspNum by\dspDen % see if frac is simply an integer
\ifnum\dspNum=#1 \divide\dspNum by\dspDen \ifnum\dspNum=1 $\pi$\else \ifnum\dspNum=-1 $-\pi$\else $\number\dspNum\pi$\fi\fi\else%
\dspNum=#1 %
\ifnum\dspNum<0 \multiply\dspNum by-1 \def\s{-}\else\def\s{}\fi % normalize sign
\divide\dspDen by\dspNum \multiply\dspDen by\dspNum % see if frac is of type 1/x
\ifnum\dspDen=#2 \divide\dspDen by\dspNum $\s\pi / \number\dspDen$\else %
\dspNum=#1\dspDen=#2 %
\removeFactor{10}\removeFactor{9}\removeFactor{8}% remove some common factors
\removeFactor{7}\removeFactor{6}\removeFactor{5}%
\removeFactor{4}\removeFactor{3}\removeFactor{2}%
$\number\dspNum \pi / \number\dspDen$\fi%
\fi %
\fi}
%% simplify a fraction (not the full Euclid, but it's ok for our purposes)
\newcount\dspTerm \newcount\dspFact
\def\removeFactor#1{\dspFact=#1 \dspTerm=\dspNum%
\divide\dspTerm by\dspFact \multiply\dspTerm by\dspFact %
\ifnum\dspTerm=\dspNum %
\dspTerm=\dspDen %
\divide\dspTerm by\dspFact \multiply\dspTerm by\dspFact %
\ifnum\dspTerm=\dspDen %
\divide\dspNum by\dspFact \divide\dspDen by\dspFact %
\fi %
\fi}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% space-separated lists must have at least two pairs of elements.
% Duplicate singletons
%
\def\m@keList#1 #2 #3\relax{%
\ifx&
#1 #2 #1 #2
\else
#1 #2 #3
\fi
}
\def\twoArgSplit#1,#2{#1, #2}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% prepend the ordinal index to each element of a space-separated list
%
\def\m@keIndexedList#1#2{%
\count0=#1%
\expandafter\m@kixl\trim #2 |}
\def\m@kixl#1 {%
\ifx#1|%
\let\next=\relax%
\else{\number\count0} \ #1\ \advance\count0 by1\let\next=\m@kixl\fi%
\next}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% perform an action on all consecutive pairs in a space-separated list
%
\def\doOnPairs#1#2{%
\def\action{#1}%
\expandafter\p@rseB\trim #2 |}
\def\p@rseB#1 #2 #3{%
\csname \action\endcsname{#1}{#2}%
\ifx#3|%
\let\next=\@gobble%
\else%
\let\next=\p@rseB\fi%
\next #3}
\def\@gobble#1{}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% PsTricks overrides
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Lollipop line style, by Denis.Girou at idris.fr %%%%%%%%%%%%%%%%%%%%%%%%
%%
\let\beginplot@LineToXAxis\beginplot@line
\def\endplot@LineToXAxis{\psLineToXAxis@ii}
\let\beginqp@LineToXAxis\beginqp@line
\let\doqp@LineToXAxis\doqp@line
\let\endqp@LineToXAxis\endqp@line
\let\testqp@LineToXAxis\testqp@line
%
\def\psLineToXAxis@ii{%
\addto@pscode{\pst@cp \psline@iii \tx@LineToXAxis}%
\end@OpenObj}
%
\def\tx@LineToXAxis{LineToXAxis }
%
% Adapted from Line
\pst@def{LineToXAxis}<{%
NArray
n 0 eq not
{ n 1 eq { 0 0 /n 2 def } if
ArrowA
/n n 2 sub def
CP 2 copy moveto pop 0 Lineto
n { 2 copy moveto pop 0 Lineto } repeat
CP
4 2 roll
ArrowB
2 copy moveto pop 0
L
pop pop } if}>