summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/lmake
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/lmake
Initial commit
Diffstat (limited to 'macros/latex/contrib/lmake')
-rw-r--r--macros/latex/contrib/lmake/README15
-rw-r--r--macros/latex/contrib/lmake/lmake.dtx522
-rw-r--r--macros/latex/contrib/lmake/lmake.ins55
-rw-r--r--macros/latex/contrib/lmake/lmake.pdfbin0 -> 287786 bytes
-rw-r--r--macros/latex/contrib/lmake/lmake.sty147
5 files changed, 739 insertions, 0 deletions
diff --git a/macros/latex/contrib/lmake/README b/macros/latex/contrib/lmake/README
new file mode 100644
index 0000000000..b02f3f9522
--- /dev/null
+++ b/macros/latex/contrib/lmake/README
@@ -0,0 +1,15 @@
+lmake 2012/02/29 v1.0
+Copyright (C) 2012 by Shengjun Pan panshengjun@gmail.com
+
+This package (lmake) provides two macros "lcmd" and "lmake" to simplify
+typesetting a list of phrases that fit a pattern:
+
+* \lcmd{command}{prefix}{list}
+ makes new commands in batch by adding a prefix to an existing command,
+ each with a different argument taken from a list.
+
+* \lmake[[key1=]value1,[key2=]value2,...]
+ makes a list of the form p(i1),p(i2),...,p(in), where teh p(.)'s are
+ similar phrases that fit a pattern.
+
+Plese refer to lmake.pdf for more details and examples.
diff --git a/macros/latex/contrib/lmake/lmake.dtx b/macros/latex/contrib/lmake/lmake.dtx
new file mode 100644
index 0000000000..f8298099f8
--- /dev/null
+++ b/macros/latex/contrib/lmake/lmake.dtx
@@ -0,0 +1,522 @@
+% \iffalse meta-comment
+%
+% Copyright (C) 2012 by Shengjun Pan <panshengjun@gmail.com>
+% -------------------------------------------------------
+%
+% This file may be distributed and/or modified under the
+% conditions of the LaTeX Project Public License, either version 1.2
+% of this license or (at your option) any later version.
+% The latest version of this license is in:
+%
+% http://www.latex-project.org/lppl.txt
+%
+% and version 1.2 or later is part of all distributions of LaTeX
+% version 1999/12/01 or later.
+%
+% \fi
+%
+% \iffalse
+%<*driver>
+\ProvidesFile{lmake.dtx}
+%</driver>
+%<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+%<package>\ProvidesPackage{lmake}
+%<*package>
+ [2012/02/29 v1.0 .dtx lmake file]
+%</package>
+%
+%<*driver>
+\documentclass{ltxdoc}
+\usepackage{amsmath,amssymb}
+\usepackage[width=5.5in,height=8.6in]{geometry}
+\usepackage{multirow}
+\usepackage{graphicx}
+\usepackage{makeidx}
+\usepackage[usenames]{color}
+\usepackage{lmake}[2012/02/29]
+\EnableCrossrefs
+\CodelineIndex
+\RecordChanges
+\begin{document}
+ \DocInput{lmake.dtx}
+ \PrintChanges
+ \PrintIndex
+\end{document}
+%</driver>
+% \fi
+%
+% \CheckSum{412}
+%
+% \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 \~}
+%
+%
+% \changes{v1.0}{\filedate}{Initial version}
+%
+% \GetFileInfo{lmake.dtx}
+%
+% \DoNotIndex{\newcommand,\newenvironment}
+% \DoNotIndex{\#,\$,\%,\&,\@,\\,\{,\},\^,\_,\~,\ }
+% \DoNotIndex{\def,\edef,\if,\else,\fi,\begingroup,\endgroup}
+% \DoNotIndex{\@ifpackageloaded,\advance,\cdots,\csname,\endcsname}
+% \DoNotIndex{\dotsb,\dotsc,\empty,\end,\expandafter,\global}
+% \DoNotIndex{\i,\ifcase,\ifnum,\ifx,\or,\relax,\string,\the,\let}
+% \DoNotIndex{\ldots,\newcount,\newif,\noexpand,\protected@edef}
+%
+%
+% \title{The \texttt{lmake} package\thanks{This document
+% corresponds to \texttt{lmake}~\fileversion, dated \filedate.}}
+% \author{Shengjun Pan \\ \texttt{panshengjun@gmail.com}}
+% \date{}
+% \maketitle
+% \newcommand{\bs}{\char`\\}
+% \newcommand{\us}{\char`\_}
+% \newcommand{\ur}{\char`\^}
+% \renewcommand{\arraystretch}{1.2}
+% \setlength{\fboxsep}{1ex}
+% \definecolor{lightGray}{rgb}{0.85,0.85,0.85}
+% \definecolor{lightBlue}{rgb}{0.75,0.75,1}
+% \noindent\hspace*{-0.075\textwidth}\colorbox{lightGray}{\begin{minipage}{1.15\textwidth}
+% \[
+% \lcmd{\hat}{h}{X,A,B,F,\mu,\phi,\sigma}
+% \lmake[\int_{\hX_\i=\hA_\i}^{\hB_\i},\empty]
+% \binom{N}{\lmake[\i\hphi_\i,,m]}
+% \lmake[e^{-\frac{(\hX_\i-\hmu_\i)^2}{2\hsigma_\i^2}},c=]
+% \lmake[d\hX_\i,\,]
+% \]
+% \tt
+% \bs lcmd\{\bs hat\}\{h\}\{X,A,B,F,\bs mu,\bs phi,\bs sigma\}\\
+% \bs lmake[\bs in\us\{\bs hX\us\bs i=\bs hA\us\bs i\}\ur\{\bs hB\us\bs i\},\bs empty]\\
+% \bs binom\{N\}\{\bs lmake[\bs i\bs hphi\us\bs i,,m]\}\\
+% \bs lmake[e\ur\{-\bs frac\{(\bs hX\us\bs i-\bs hmu\us\bs i)\ur2\}\{2\bs hsigma\us\bs i\ur2\}\},c=]\\
+% \bs lmake[d\bs hX\us\bs i,\bs,]
+% \end{minipage}}
+% \section{Introduction}
+% This package provides macros for \LaTeX2e to simplify typesetting a
+% list of phrases that fit a pattern.\\
+% \begin{tabular}{ll}
+% \tt \char`\\lcmd
+% & makes a list of new commands by adding a prefix to existing
+% commands.\\
+% \tt\char`\\lmake
+% & makes a list of the form $p(i_1),p(i_2),\dotsc,p(i_n)$, where
+% $p(.)$ stands for \emph{pattern}.
+% \end{tabular}
+% \section{Usage and examples}
+% \label{sec:usage}
+% \DescribeMacro{\lcmd}
+% \fcolorbox{black}{lightBlue}{\tt \char`\\lcmd\{command\}\{prefix\}\{list\}}\\[2ex]
+% makes a list of new commands from \texttt{command} by adding \texttt{prefix}
+% to each item in the comma-separated \texttt{list}. If an item in the
+% list is a macro, only its name is prefixed; the backslash is stripped away.\\[3ex]
+% \textbf{Examples}:
+% \footnote{The examples used in this document require packages {\tt amsmath,
+% amssymb} and {\tt graphicx}.}\\[1ex]
+% \lcmd{\mathcal}{c}{A,X,P}
+% \lcmd{\mathbb}{bb}{Z,R,E}
+% \lcmd{\overline}{vct}{x,\phi,psi}
+% \begin{tabular}{l@{ defines: }lll}
+% \hline
+% \verb|\lcmd{\mathcal}{c}{A,X,P}|
+% &\texttt{\char`\\ cA} $\to \cA$
+% &\texttt{\char`\\ cX} $\to \cX$
+% &\texttt{\char`\\ cP} $\to \cP$
+% \\
+% \verb|\lcmd{\mathbb}{c}{A,X,P}|
+% &\texttt{\char`\\ bbZ} $\to \bbZ$
+% &\texttt{\char`\\ bbR} $\to \bbR$
+% &\texttt{\char`\\ bbE} $\to \bbE$\\
+% \verb|\lcmd{\overline}{vct}{x,psi,\phi}|
+% &\texttt{\char`\\ vctx} $\to \vctx$
+% &\texttt{\char`\\ vctpsi} $\to \vctpsi$
+% &\texttt{\char`\\ vctphi} $\to \vctphi$\\
+% \hline
+% \end{tabular}\\[2ex]
+% In the last example the new command for \verb|\phi| is
+% \verb|\vctphi|, not \verb|\vct\phi|. Notice the difference between
+% \verb|\vctphi| and \verb|\vctpsi|.\\[4ex]
+% \DescribeMacro{\lmake}
+% \fcolorbox{black}{lightBlue}{\tt\bs lmake[[key1=]value1,[key2=]value2,...]}\\[2ex]
+% makes a list of symbols by key-value pairs. Valid keys are
+% described in the following table.\\[3ex]
+% \begin{tabular}{@{\tt }rll}
+% \hline
+% keys & default & description\\
+% \hline
+% p & \verb|\i| & Pattern. All occurrences of \verb|\i| will be replaced
+% by the corresponding index.\\
+% c & \verb|,| & Separator.\\
+% n &\verb|n| & Last index.\\
+% 1 & \verb|1| & First index.\\
+% 2 & \verb|2| & Second index.\\
+% d & \verb|\ldots|, \verb|\cdots| & Dots. If the separator is comma,
+% \verb|\ldots| is used; otherwise \verb|\cdots| is used.\\
+% & \verb|\dotsc|, \verb|\dotsb| & If \verb|amsmath| is loaded,
+% \verb|\dotsc| and \verb|\dotsb| are used respectively.\\
+% $\ell$ & \verb|{}| & List of comma-separated symbols.\\
+% \hline
+% \end{tabular}\\[3ex]
+% \textbf{Examples}:\\[1ex]
+% \begin{tabular}{ll}
+% \hline
+% what you type & what you see\\
+% \hline
+% \verb|\lmake[]| & $\lmake[]$\\
+% \verb|\lmake[2=]| & $\lmake[2=]$\\
+% \verb|\lmake[x_\i,c=]| & $\lmake[x_\i,c=]$\\
+% \verb|\lmake[x_\i,,N]| & $\lmake[x_\i,,N]$\\
+% \verb|\lmake[x_\i,\ge,k]| & $\lmake[x_\i,\ge,k]$\\
+% \verb|\lmake[\bar x_{\i},\circ,1=i,i+1]| & $\lmake[\bar x_{\i},\circ,1=i,i+1]$\\
+% \verb|\lmake[p_\i^{\mu_\i},c=,m]| & $\lmake[p_\i^{\mu_\i},c=,m]$\\
+% \verb|\lmake[N_\i!,\,,\Gamma,\alpha,\beta]| & $\lmake[N_\i!,\,,\Gamma,\alpha,\beta]$\\
+% \verb|\lmake[\left(\frac{\i}{\i+1}\right),\!]|
+% & $\lmake[\left(\frac{\i}{\i+1}\right),\!]$\\
+% \verb|\lmake[(e_\i+1),c=,k,1,2]| & $\lmake[(e_\i+1),c=,k,1,2]$\\[1ex]
+% \verb|\lmake[x_{\i},l={1,3,5,11}]|
+% & $\lmake[x_{\i},l={1,3,5,11}]$\\
+% \verb|\lmake[\rotatebox{-30}{\i},\to,l={A,B,C,D}]|
+% & $\lmake[\rotatebox{-30}{\i},\to,l={A,B,C,D}]$\\
+% \hline
+% \end{tabular}\\[2ex]
+% \emph{Remarks:}
+% \begin{itemize}
+% \item If $\tt \ell$ is empty, the resulting list has the following form:\\
+% \verb|p(1) c p(2) c d c p(n)|
+%
+% If $\tt \ell$ is not empty, the resulting list has the
+% following form, for the example $\tt \ell=\{x,y,z,u\}$:\\
+% \verb|p(x) c p(y) c p(z) c p(u)|
+% \item A \underline{non-empty item without \texttt{=}} in the argument
+% list is treated as a value. Normally the key-value pairs can appear
+% out of order in the argument list to \verb|\lmake|. For fast typing
+% the key can be omitted. The missing key is searched, starting from
+% the key that would follow the previous key in the table-order. Only
+% keys without values are searched.
+%
+% For example, in \verb|\lmake[\bar x_{\i},\circ,1=i,i+1]|, the
+% missing key for \verb|\bar x_{\i}| is \verb|p|, the missing key for
+% \verb|\circ| is \verb|c| since it follows \verb|p| in the
+% table. Similarly, the missing key for \verb|i+1| is \verb|2| since
+% it follows the previous key \verb|1| in the able.
+% \item A key not appearing or skipped in the argument list takes its
+% default value, unless it's been searched as a missing key and given
+% a value.
+%
+% For example, in \verb|\lamke[x_\i,,N]|, all keys except for \verb|p|
+% and \verb|n| take default values. Particularly, the key \verb|c| is
+% skipped and it is treated as missing.
+%
+% Note that a skipped key does not take value \emph{empty}. To force a
+% value to be empty, use one of the following workarounds:
+% {\tt \char`\\empty}, {\tt key=}, {\tt key=\char`\\empty}, or {\tt
+% key=\{\}}.
+% \end{itemize}
+%
+% \StopEventually{}
+%
+% \section{Implementation}
+% This section explains in details how verb|\lmake|, \verb|\lcmd| and
+% necessary internal macros are implemented.
+% \begin{macro}{\L@Compare}
+% \verb|\L@Compare{string1}{string2}|\\
+% compares two strings. The two arguments are fully expanded before compassion.
+% \verb|\ifL@Equal| is a Boolean variable for storing the result.
+% \begin{macrocode}
+\newif\ifL@Equal
+\def\L@Compare#1#2{%
+ \protected@edef\L@a{#1}\protected@edef\L@b{#2}%
+ \ifx\L@a\L@b\L@Equaltrue\else\L@Equalfalse\fi}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@FuzzyCompare}
+% \verb|\L@FuzzyCompare{string1}{string2}|\\
+% tests if two strings are the same, where white spaces preceding the
+% second argument are ignores. This allows flexible writing of
+% the comma-separated argument to \verb|\lmake| so that spaces may be inserted
+% between an item and the previous comma.
+% \begin{macrocode}
+\def\L@FuzzyCompare#1#2{%
+ \L@Compare{#1}{#2}\ifL@Equal\else\L@Compare{#1}{ #2}\fi}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@SoftCompare}
+% \verb|\L@SoftCompare{string1}{string2}|\\
+% similar to \verb|\L@Compare|, but does not expand the two
+% strings. This is usefully if either argument contains undefined
+% macros, for example, the value to the key \verb|p| in the argument to \verb|\lmake|.
+% \begin{macrocode}
+\def\L@SoftCompare#1#2{%
+ \def\L@a{#1}\def\L@b{#2}%
+ \ifx\L@a\L@b\L@Equaltrue\else\L@Equalfalse\fi}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@FuzzySoftCompare}
+% \verb|\L@FuzzySoftCompare{string1}{string2}|\\
+% similar to \verb|L@FuzzyCompare|, but uses \verb|\L@SoftCompare|
+% instead of \verb|\L@Compare|.
+% \begin{macrocode}
+\def\L@FuzzySoftCompare#1#2{%
+ \L@SoftCompare{#1}{#2}\ifL@Equal\else\L@SoftCompare{#1}{ #2}\fi}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@HasEqualSign}
+% \verb|\L@HasEqualSign{string}|\\
+% tests if a string has an equal sign \verb|=|. This is used to test
+% if an argument to \verb|\lmake| is a key-value pair or just a
+% value. It is defined indirectly via \verb|\L@HES|, which is a tail
+% recursion for scanning the tokens in its argument.\\[1ex]
+% \verb|\L@Ignore| is an auxiliary macro that simply ignores its
+% argument. The test result is stored in \verb|\ifL@HasEqualSign|.
+% \begin{macrocode}
+\def\L@Ignore#1\end{}
+\newif\ifL@HasEqualSign
+\def\L@HasEqualSign#1{%
+ \L@HasEqualSignfalse\L@HES#1\end}
+\def\L@HES#1{%
+ \ifx#1=\L@HasEqualSigntrue\let\L@Next=\L@Ignore%
+ \else\ifx#1\end\let\L@Next=\relax\else\let\L@Next=\L@HES\fi%
+ \fi\L@Next}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@ArName}
+% \verb|\L@ArName[index]|\\
+% returns the name of a macro via a numeric index.
+% \begin{macrocode}
+\def\L@ArName[#1]{\ifcase#1 L@Pattern\or L@Comma\or L@Last\or%
+L@First\or L@Second\or L@Dots\or L@List\else L@Other\fi}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@Set,\L@Get}
+% \verb|\L@Set[index]=value;|\\
+% \verb|\L@Get[Index]|\\
+% set and get the value of a macro by a numeric index. Numeric indices are
+% used to locate missing keys in the argument to \verb|\lmake|.
+% \begin{macrocode}
+\def\L@Set[#1]=#2;{\global\expandafter\let\csname\L@ArName[#1]\endcsname=#2}
+\def\L@Get[#1]{\csname\L@ArName[#1]\endcsname}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@LDots,\L@Cdots}
+% denotes the default macro for low dots. If \verb|amsmath| is loaded before
+% \verb|\lmake|, \verb|\L@LDots| is set to \verb|\dotsc| and
+% \verb|\L@Cdots| is set to \verb|\dotsb|. Otherwise \verb|\L@LDots|
+% is set to \verb|\ldots| and \verb|\L@Cdots| is set to \verb|\cdots|.
+% \begin{macrocode}
+\@ifpackageloaded{amsmath}
+ {\def\L@Ldots{\dotsc}\def\L@Cdots{\dotsb}}
+ {\def\L@Ldots{\ldots}\def\L@Cdots{\cdots}}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@Map}
+% \verb|\L@Map{function}{list}{new separator}|\\
+% maps a list of comma-separated items to a new list, so that each
+% item is transformed using the given function, and the commas are
+% replaced with the new separator. It is defined indirectly
+% indirectly via \verb|\L@Iterate|, which is a tail recursion for
+% scanning the comma-separated list.\\[1ex]
+% \verb|\ifL@Start| is used to indicate if the current item is the
+% first item, which is not preceded by a comma, unlike the remaining
+% items.\\[1ex]
+% \verb|\L@Map|
+% \begin{macrocode}
+\newif\ifL@Start
+\def\L@Map#1#2#3{%
+ \def\L@Sym{\empty}\def\LM@Func{#1}\def\L@Sep{#3}%
+ \L@Starttrue\expandafter\L@Iterate#2,\end}
+\def\L@Iterate#1,#2{%
+ \LM@Func{#1}%
+ \ifx#2\end\let\L@Next=\relax\def\L@Nextarg{\empty}%
+ \else\L@Sep\let\L@Next=\L@Iterate\def\L@Nextarg{#2}\fi%
+ \expandafter\L@Next\L@Nextarg}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@GetKeyValue}
+% \verb|\L@GetKeyValue{key-value pair}|\\
+% extracts the key and value from a string, and store them in
+% \verb|\L@Key| and \verb|\L@Value| respectively. If the argument has no
+% equal sign, it is used as a value and the key is set to empty. Otherwise,
+% \verb|\L@GetKV| is called to extract the key and value.
+% \begin{macrocode}
+\def\L@GetKeyValue#1{%
+ \def\L@Key{}\def\L@Value{}\L@HasEqualSign{#1}%
+ \ifL@HasEqualSign\L@GetKV#1\end%
+ \else\def\L@Key{}\def\L@Value{#1}%
+ \fi}
+\def\L@GetKV#1=#2\end{%
+ \def\L@Key{#1}\def\L@Value{#2}}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@Parse}
+% \verb|\L@Parse{list}|\\
+% parses a list of comma-separated items, extracts each item,
+% extract its key and value, looks for keys \verb|p|, \verb|c|,
+% \verb|n|, \verb|1|, \verb|2|, \verb|d| and \verb|l|, and finally assigns the
+% corresponding values to \verb|\L@Pattern|, \verb|\L@Comma|,
+% \verb|\L@Last|, \verb|\L@First|, \verb|\l@Second|, \verb|\L@Dots|
+% and \verb|\L@List| respectively.\\[1ex]
+% The counter \verb|\L@idx| is the index of the next key yet to be assigned
+% with an value.
+% \begin{macrocode}
+\newcount\L@idx
+% \end{macrocode}
+% If the list is not empty, then it calls a tail recursion \verb|\l@PRS|.
+% \begin{macrocode}
+\def\L@Parse#1{\L@idx=0%
+ \L@FuzzySoftCompare{#1}{}%
+ \ifL@Equal\else\def\L@Extra{}\L@PRS#1,\end,\fi}
+% \end{macrocode}
+% An artificial item \verb|,\end,|
+% is added to the end of the actual list, which terminates the
+% recursion when encountered.
+% \begin{macrocode}
+\def\L@PRS#1,{%
+ \L@SoftCompare{#1}{\end}\ifL@Equal%
+ \let\L@Next=\relax%
+ \else%
+% \end{macrocode}
+% If an item is empty, the missing key is searched and
+% the corresponding default value is used.
+% \begin{macrocode}
+ \L@FuzzySoftCompare{#1}{}\ifL@Equal%
+ \ifnum\L@idx<7%
+ \ifcase\the\L@idx%
+ \def\L@Default{\i}%
+ \or\def\L@Default{,}%
+ \or\def\L@Default{n}%
+ \or\def\L@Default{1}%
+ \or\def\L@Default{2}%
+ \or\def\L@Default{}%
+ \or\def\L@Default{}%
+ \fi%
+ \L@Set[\the\L@idx]=\L@Default;%
+ \advance \L@idx by 1%
+ \fi%
+% \end{macrocode}
+% If the item is not empty, the key and value are extracted. Depending
+% on what the key is, the value is assigned to the approximate macro.
+% \begin{macrocode}
+ \else%
+ \L@GetKeyValue{#1}\let\L@CV=\L@Value%
+ \L@FuzzyCompare{\L@Key}{p}\ifL@Equal\L@idx=1\L@Set[0]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{c}\ifL@Equal\L@idx=2\L@Set[1]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{n}\ifL@Equal\L@idx=3\L@Set[2]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{1}\ifL@Equal\L@idx=4\L@Set[3]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{2}\ifL@Equal\L@idx=5\L@Set[4]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{d}\ifL@Equal\L@idx=6\L@Set[5]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{l}\ifL@Equal\L@idx=7\L@Set[6]=\L@CV;%
+% \end{macrocode}
+% If an item is a value without a key, the missing key is searched and
+% given the current value.
+% \begin{macrocode}
+ \else\L@FuzzyCompare{\L@Key}{}\ifL@Equal%
+ \ifnum\L@idx<7%
+ \L@Set[\the\L@idx]=\L@CV;%
+ \fi%
+ \advance \L@idx by 1%
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi%
+ \let\L@Next=\L@PRS%
+ \fi%
+ \L@Next}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\lmake}
+% \verb|\lmake[key-value list]|\\
+% makes a list of symbols using the given key-value
+% pairs. \verb|\ifL@FoundFirst| is used to indicate if the first
+% non-empty symbol is encountered; the first symbol is not preceded
+% by a separator.
+% \begin{macrocode}
+\newif\ifL@FoundFirst
+% \end{macrocode}
+% \verb|\L@Parse| is used twice. The first time it is used to set the
+% default values. The second time it is used to set the values
+% supplied by the argument list.
+% \begin{macrocode}
+\newcommand{\lmake}[1][]{%
+\begingroup%
+ \L@Parse{p=\i,c={,},d=,1=1,2=2,n=n,l=}%
+ \L@Parse{#1}%
+% \end{macrocode}
+% The pattern is used to define the transforming function
+% \verb|\L@Func| by replacing all occurrences of \verb|\i| by the
+% actual argument.
+% \begin{macrocode}
+ \def\L@Func##1{\def\i{##1}\L@Get[0]}%
+% \end{macrocode}
+% If the key \verb|d| is not given a value, its value is automatically determined by
+% the value of the separator.
+% \begin{macrocode}
+ \L@Compare{\L@Dots}{\empty}\ifL@Equal%
+ \L@Compare{\L@Comma}{,}\ifL@Equal%
+ \def\L@Dots{\L@Ldots}\else\def\L@Dots{\L@Cdots}%
+ \fi
+ \fi
+% \end{macrocode}
+% The last step is to typeset the list of symbols. If the value to the key $\ell$ is
+% missing, the typeset list starts with two symbols followed by the dots and
+% ends with the last symbol. The symbols and dots are separated by the
+% separator.
+% \begin{macrocode}
+ \L@Compare{\L@List}{\empty}\ifL@Equal%
+ \L@FoundFirstfalse%
+ \L@Compare{\L@First}{\empty}\ifL@Equal\else%
+ \L@Func{\L@First}\L@FoundFirsttrue%
+ \fi%
+ \L@Compare{\L@Second}{\empty}\ifL@Equal\else%
+ \ifL@FoundFirst\L@Comma\fi%
+ \L@Func{\L@Second}\L@FoundFirsttrue%
+ \fi%
+ \L@Compare{\L@Dots}{\empty}\ifL@Equal\else%
+ \ifL@FoundFirst\L@Comma\fi\L@Dots%
+ \fi%
+ \L@Compare{\L@Last}{\empty}\ifL@Equal\else%
+ \L@Comma\L@Func{\L@Last}%
+ \fi%
+% \end{macrocode}
+% If a non-empty value to the key $\ell$ is given, the typeset list
+% consists of symbols from the items in the value of $\ell$,
+% transformed by the pattern function.
+% \begin{macrocode}
+ \else%
+ \L@Map{\L@Func}{\L@List}{\L@Comma}%
+ \fi%
+\endgroup}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\L@CmdName}
+% \verb|\L@CmdName{string}|\\
+% returns the string itself if it is not a macro, otherwise returns
+% the macro name with the backslash stripped away. This is an
+% auxiliary function to \verb|\lcmd|.
+% \begin{macrocode}
+\def\L@CmdName#1{%
+ \if\noexpand#1\noexpand\L@anycmd\expandafter\L@StripFirst\string#1\else#1\fi}
+\def\L@StripFirst#1#2{#2}
+% \end{macrocode}
+% \end{macro}
+% \begin{macro}{\lcmd}
+% \verb|\lcmd{command}{prefix}{list}|\\
+% makes a list of new commands. See Section~\ref{sec:usage} for more details.
+% \begin{macrocode}
+\def\lcmd#1#2#3{%
+ \def\L@MakeCmd##1{%
+ \expandafter\def\csname #2\L@CmdName##1\endcsname{#1{##1}}}%
+ \L@Map{\L@MakeCmd}{#3}{}}
+% \end{macrocode}
+% \end{macro}
+% \Finale
+\endinput
+
diff --git a/macros/latex/contrib/lmake/lmake.ins b/macros/latex/contrib/lmake/lmake.ins
new file mode 100644
index 0000000000..0bdf22092c
--- /dev/null
+++ b/macros/latex/contrib/lmake/lmake.ins
@@ -0,0 +1,55 @@
+%%
+%% Copyright (C) 2012 by Shengjun Pan <panshengjun@gmail.com>
+%%
+%% This file may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License, either version 1.2 of this license
+%% or (at your option) any later version. The latest version of this
+%% license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.2 or later is part of all distributions of LaTeX version
+%% 1999/12/01 or later.
+%%
+
+\input docstrip.tex
+\keepsilent
+
+\usedir{tex/latex/lmake}
+
+\preamble
+
+This is a generated file.
+
+Copyright (C) 2012 by Shengjun Pan <panshengjun@gmail.com>
+
+This file may be distributed and/or modified under the conditions of
+the LaTeX Project Public License, either version 1.2 of this license
+or (at your option) any later version. The latest version of this
+license is in:
+
+ http://www.latex-project.org/lppl.txt
+
+and version 1.2 or later is part of all distributions of LaTeX version
+1999/12/01 or later.
+
+\endpreamble
+
+\generate{\file{lmake.sty}{\from{lmake.dtx}{package}}}
+
+\obeyspaces
+\Msg{*************************************************************}
+\Msg{* *}
+\Msg{* To finish the installation you have to move the following *}
+\Msg{* file into a directory searched by TeX: *}
+\Msg{* *}
+\Msg{* lmake.sty *}
+\Msg{* *}
+\Msg{* To produce the documentation run the file lmake.dtx *}
+\Msg{* through LaTeX. *}
+\Msg{* *}
+\Msg{* Happy TeXing! *}
+\Msg{* *}
+\Msg{*************************************************************}
+
+\endbatchfile
diff --git a/macros/latex/contrib/lmake/lmake.pdf b/macros/latex/contrib/lmake/lmake.pdf
new file mode 100644
index 0000000000..0b588f6ce1
--- /dev/null
+++ b/macros/latex/contrib/lmake/lmake.pdf
Binary files differ
diff --git a/macros/latex/contrib/lmake/lmake.sty b/macros/latex/contrib/lmake/lmake.sty
new file mode 100644
index 0000000000..50a4c76126
--- /dev/null
+++ b/macros/latex/contrib/lmake/lmake.sty
@@ -0,0 +1,147 @@
+%%
+%% This is file `lmake.sty',
+%% generated with the docstrip utility.
+%%
+%% The original source files were:
+%%
+%% lmake.dtx (with options: `package')
+%%
+%% This is a generated file.
+%%
+%% Copyright (C) 2012 by Shengjun Pan <panshengjun@gmail.com>
+%%
+%% This file may be distributed and/or modified under the conditions of
+%% the LaTeX Project Public License, either version 1.2 of this license
+%% or (at your option) any later version. The latest version of this
+%% license is in:
+%%
+%% http://www.latex-project.org/lppl.txt
+%%
+%% and version 1.2 or later is part of all distributions of LaTeX version
+%% 1999/12/01 or later.
+%%
+\NeedsTeXFormat{LaTeX2e}[1999/12/01]
+\ProvidesPackage{lmake}
+ [2012/02/29 v1.0 .dtx lmake file]
+\newif\ifL@Equal
+\def\L@Compare#1#2{%
+ \protected@edef\L@a{#1}\protected@edef\L@b{#2}%
+ \ifx\L@a\L@b\L@Equaltrue\else\L@Equalfalse\fi}
+\def\L@FuzzyCompare#1#2{%
+ \L@Compare{#1}{#2}\ifL@Equal\else\L@Compare{#1}{ #2}\fi}
+\def\L@SoftCompare#1#2{%
+ \def\L@a{#1}\def\L@b{#2}%
+ \ifx\L@a\L@b\L@Equaltrue\else\L@Equalfalse\fi}
+\def\L@FuzzySoftCompare#1#2{%
+ \L@SoftCompare{#1}{#2}\ifL@Equal\else\L@SoftCompare{#1}{ #2}\fi}
+\def\L@Ignore#1\end{}
+\newif\ifL@HasEqualSign
+\def\L@HasEqualSign#1{%
+ \L@HasEqualSignfalse\L@HES#1\end}
+\def\L@HES#1{%
+ \ifx#1=\L@HasEqualSigntrue\let\L@Next=\L@Ignore%
+ \else\ifx#1\end\let\L@Next=\relax\else\let\L@Next=\L@HES\fi%
+ \fi\L@Next}
+\def\L@ArName[#1]{\ifcase#1 L@Pattern\or L@Comma\or L@Last\or%
+L@First\or L@Second\or L@Dots\or L@List\else L@Other\fi}
+\def\L@Set[#1]=#2;{\global\expandafter\let\csname\L@ArName[#1]\endcsname=#2}
+\def\L@Get[#1]{\csname\L@ArName[#1]\endcsname}
+\@ifpackageloaded{amsmath}
+ {\def\L@Ldots{\dotsc}\def\L@Cdots{\dotsb}}
+ {\def\L@Ldots{\ldots}\def\L@Cdots{\cdots}}
+\newif\ifL@Start
+\def\L@Map#1#2#3{%
+ \def\L@Sym{\empty}\def\LM@Func{#1}\def\L@Sep{#3}%
+ \L@Starttrue\expandafter\L@Iterate#2,\end}
+\def\L@Iterate#1,#2{%
+ \LM@Func{#1}%
+ \ifx#2\end\let\L@Next=\relax\def\L@Nextarg{\empty}%
+ \else\L@Sep\let\L@Next=\L@Iterate\def\L@Nextarg{#2}\fi%
+ \expandafter\L@Next\L@Nextarg}
+\def\L@GetKeyValue#1{%
+ \def\L@Key{}\def\L@Value{}\L@HasEqualSign{#1}%
+ \ifL@HasEqualSign\L@GetKV#1\end%
+ \else\def\L@Key{}\def\L@Value{#1}%
+ \fi}
+\def\L@GetKV#1=#2\end{%
+ \def\L@Key{#1}\def\L@Value{#2}}
+\newcount\L@idx
+\def\L@Parse#1{\L@idx=0%
+ \L@FuzzySoftCompare{#1}{}%
+ \ifL@Equal\else\def\L@Extra{}\L@PRS#1,\end,\fi}
+\def\L@PRS#1,{%
+ \L@SoftCompare{#1}{\end}\ifL@Equal%
+ \let\L@Next=\relax%
+ \else%
+ \L@FuzzySoftCompare{#1}{}\ifL@Equal%
+ \ifnum\L@idx<7%
+ \ifcase\the\L@idx%
+ \def\L@Default{\i}%
+ \or\def\L@Default{,}%
+ \or\def\L@Default{n}%
+ \or\def\L@Default{1}%
+ \or\def\L@Default{2}%
+ \or\def\L@Default{}%
+ \or\def\L@Default{}%
+ \fi%
+ \L@Set[\the\L@idx]=\L@Default;%
+ \advance \L@idx by 1%
+ \fi%
+ \else%
+ \L@GetKeyValue{#1}\let\L@CV=\L@Value%
+ \L@FuzzyCompare{\L@Key}{p}\ifL@Equal\L@idx=1\L@Set[0]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{c}\ifL@Equal\L@idx=2\L@Set[1]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{n}\ifL@Equal\L@idx=3\L@Set[2]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{1}\ifL@Equal\L@idx=4\L@Set[3]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{2}\ifL@Equal\L@idx=5\L@Set[4]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{d}\ifL@Equal\L@idx=6\L@Set[5]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{l}\ifL@Equal\L@idx=7\L@Set[6]=\L@CV;%
+ \else\L@FuzzyCompare{\L@Key}{}\ifL@Equal%
+ \ifnum\L@idx<7%
+ \L@Set[\the\L@idx]=\L@CV;%
+ \fi%
+ \advance \L@idx by 1%
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi%
+ \let\L@Next=\L@PRS%
+ \fi%
+ \L@Next}
+\newif\ifL@FoundFirst
+\newcommand{\lmake}[1][]{%
+\begingroup%
+ \L@Parse{p=\i,c={,},d=,1=1,2=2,n=n,l=}%
+ \L@Parse{#1}%
+ \def\L@Func##1{\def\i{##1}\L@Get[0]}%
+ \L@Compare{\L@Dots}{\empty}\ifL@Equal%
+ \L@Compare{\L@Comma}{,}\ifL@Equal%
+ \def\L@Dots{\L@Ldots}\else\def\L@Dots{\L@Cdots}%
+ \fi
+ \fi
+ \L@Compare{\L@List}{\empty}\ifL@Equal%
+ \L@FoundFirstfalse%
+ \L@Compare{\L@First}{\empty}\ifL@Equal\else%
+ \L@Func{\L@First}\L@FoundFirsttrue%
+ \fi%
+ \L@Compare{\L@Second}{\empty}\ifL@Equal\else%
+ \ifL@FoundFirst\L@Comma\fi%
+ \L@Func{\L@Second}\L@FoundFirsttrue%
+ \fi%
+ \L@Compare{\L@Dots}{\empty}\ifL@Equal\else%
+ \ifL@FoundFirst\L@Comma\fi\L@Dots%
+ \fi%
+ \L@Compare{\L@Last}{\empty}\ifL@Equal\else%
+ \L@Comma\L@Func{\L@Last}%
+ \fi%
+ \else%
+ \L@Map{\L@Func}{\L@List}{\L@Comma}%
+ \fi%
+\endgroup}
+\def\L@CmdName#1{%
+ \if\noexpand#1\noexpand\L@anycmd\expandafter\L@StripFirst\string#1\else#1\fi}
+\def\L@StripFirst#1#2{#2}
+\def\lcmd#1#2#3{%
+ \def\L@MakeCmd##1{%
+ \expandafter\def\csname #2\L@CmdName##1\endcsname{#1{##1}}}%
+ \L@Map{\L@MakeCmd}{#3}{}}
+\endinput
+%%
+%% End of file `lmake.sty'.