%  __________________________________________________
% |                                                  |
% |                                                  |
% |                tabularcalc v0.2                  |
% |                                                  |
% |                  April 21 2009                   |
% |                                                  |
% |__________________________________________________|
%
% This is tabularcalc.sty
%
% The "tabularcalc" package consists of the 8 following files:
%   tabularcalc.sty (this file)
%   README
%   tabularcalc_doc_fr.tex, tabularcalc_doc_fr.pdf (manual in french)
%   tabularcalc_doc_en.tex, tabularcalc_doc_en.pdf (manual in english)
%   tabularcalc_doc_vn.tex, tabularcalc_doc_vn.tex (manual in vietnamese)
%
% Christian Tellechea 2009
% email : unbonpetit@gmail.com
% -------------------------------------------------------------------
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% 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.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
% -------------------------------------------------------------------
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Christian Tellechea
% -------------------------------------------------------------------

\ProvidesPackage{tabularcalc}[2009/04/20 v0.2 Compute formulas in tables]
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{fp}
\RequirePackage{xstring}
\RequirePackage[autolanguage]{numprint}

\newcommand\tabularcalcversion     {0.2}
\newcommand\tabularcalcdate        {2009/04/21}
\newcommand\tabularcalcfrenchdate  {21 avril 2009}
\newcommand\tabularcalcenglishdate {April $21^{\mathrm{st}}$ 2009}
\newcount\tccol
\newcount\tclin
\newif\iftc@showval
\newwrite\tc@export

\newcommand\tc@checkdefine[2]{\@ifdefinable#1{\def#1{#2}}{}}
\tc@checkdefine\tc@evalexpr{}

\def\tc@evalexpr#1#2#3{% calcul d'une expression avec fp
	\IfBeginWith{#1}-%
		{\def\tc@currentresult{0#1}}%
		{\def\tc@currentresult{#1}}%
	\FPset{#2}{#3}% variable:=#2
	\FPeval\tc@currentresult\tc@currentresult
	\FPclip\tc@currentresult\tc@currentresult}

\DeclareOption{fixFPpow}{%
	\def\FP@pow#1#2#3{% enl\`eve l'espace ind\'esirable lors du calcul de a^b
			\FP@beginmessage{POW}%
			{\def\FP@beginmessage##1{}%
			\def\FP@endmessage##1{}%
			\FPifzero{#2}%
				\FP@pow@zero{#3}%
			\else
				\FPifint{#3}% g\`ere les cas (-2)^3
					\FPifneg{#2}%
						\FPneg\FP@tmpd{#2}%
						\FPln\FP@tmpd\FP@tmpd
						\FPmul\FP@tmpd\FP@tmpd{#3}%
						\FPexp\FP@tmpd\FP@tmpd
						\FPtrunc\FP@tmp{#3}0%
						\ifodd\FP@tmp
							\FPneg\FP@tmp\FP@tmpd
						\else
							\let\FP@tmp\FP@tmpd
						\fi
					\else
						\FPln\FP@tmpd{#2}%
						\FPmul\FP@tmpd\FP@tmpd{#3}%
						\FPexp\FP@tmp\FP@tmpd
					\fi
				\else
					\FPln\FP@tmpd{#2}%
					\FPmul\FP@tmpd\FP@tmpd{#3}%
					\FPexp\FP@tmp\FP@tmpd
				\fi
			\fi
			\global\let\FP@tmp\FP@tmp}%
			\FP@endmessage{}%
			\let#1\FP@tmp}}

\ProcessOptions\relax

\newcommand\tc@addtomacro[2]{\expandafter\def\expandafter#1\expandafter{#1#2}}
\newcommand\tc@expaddtomacro[2]{%
	\expandafter\tc@addtomacro\expandafter#1\expandafter{#2}}

\tc@checkdefine\tcnoshowmark{}
\makeatother\def\tcnoshowmark{@}\makeatletter
\tc@checkdefine\tcatbeginrow{\rule[-1.2ex]{0pt}{4ex}}
\tc@checkdefine\tc@cellcode{}
\tc@checkdefine\tc@currentcellcode{}
\newcommand\tcresetcellcode{\let\tc@cellcode\@empty}
\tc@checkdefine\tclistsep{,}
\tc@checkdefine\tcprintvalue{}
\def\tcprintvalue#1{\numprint{#1}}

\tc@checkdefine\tcprintresult{}
\def\tcprintresult#1#2{\numprint{#1}}

\newcommand\edefcellcode[3]{%
	\ifx\@empty#1\@empty
		\ifx\@empty#2\@empty
			\tc@addtomacro\tc@cellcode{#3}%
		\else
			\tc@addtomacro\tc@cellcode{\ifnum\tccol=#2 #3\fi}%
		\fi
	\else
		\ifx\@empty#2\@empty
			\tc@addtomacro\tc@cellcode{\ifnum\tclin=#1 #3\fi}%
		\else
			\tc@addtomacro\tc@cellcode{%
				\ifnum\tclin=#1\relax\ifnum\tccol=#2 #3\fi\fi}%
		\fi
	\fi}

\newcommand\defcellcode[3]{\edefcellcode{#1}{#2}{\unexpanded{#3}}}

\tc@checkdefine\tc@firstrule\hline
\tc@checkdefine\tc@interrule\hline
\tc@checkdefine\tc@updwnrule\hline

% d\'efinit les 3 types de lignes
\newcommand\tcsethrule[3]{%
	\def\tc@updwnrule{#1}\def\tc@firstrule{#2}\def\tc@interrule{#3}}

\newcommand\tcresethrule{\tcsethrule\hline\hline\hline}

\tc@checkdefine\tc@lastvline{}
\tc@checkdefine\tc@firstcoltype{|c|}
\tc@checkdefine\tc@othercoltype{c|}

% d\'efinit les 3 types de colonnes
\newcommand\tcsetcoltype[3][]{%
	\def\tc@firstcoltype{#2}\def\tc@othercoltype{#3}\def\tc@lastvline{#1}}

\newcommand\tcresetcoltype{\tcsetcoltype{|c|}{c|}}

\newcommand\tc@errmess[1]{\PackageError{tabularcalc}{#1}{}}
\newcommand\tc@ifemptyerrmess[2]{\ifx\empty#1\tc@errmess{#2}\fi}

\newcommand\htablecalc{%
	\begingroup
		\@makeother\:\@makeother\;\@makeother\,
		\fullexpandarg
		\def\tc@orientation{h}%
		\tc@deftableid}

\newcommand\vtablecalc{%
	\begingroup
		\@makeother\:\@makeother\;\@makeother\,
		\fullexpandarg
		\def\tc@orientation{v}%
		\tc@deftableid}

\newcommand\tc@deftableid[3][1]{%
	\def\tc@nbformulas{#1}%
	\IfSubStr{#3};%
		{\tc@analysevalueformula{#3}}% construit la liste des valeurs
		{\StrBefore{#3}=[\tc@formulavariable]% analyse les valeurs
		 \tc@ifemptyerrmess\tc@formulavariable{Empty variable!}
		 \StrLen\tc@formulavariable[\tc@temp]%
		 \ifnum\tc@temp=\@ne\else\tc@errmess{Invalid variable}\fi
		 \StrBehind{#3}=[\tc@valuelist]%
		 \tc@ifemptyerrmess\tc@valuelist{No value!}}%
	\StrCount{\tclistsep\tc@valuelist}\tclistsep[\tc@nbval]% nombre de valeurs
	\let\tc@coltype\@empty
	\tc@expaddtomacro\tc@coltype{\tc@firstcoltype*}% construit \tc@coltype
	\if\tc@orientation h\let\tc@temp\tc@nbval\fi
	\if\tc@orientation v\let\tc@temp\tc@nbformulas\fi
	\expandafter\tc@addtomacro\expandafter\tc@coltype\expandafter
		{\expandafter{\tc@temp}}% qui contiendra par exemple |c|*{4}{c|}
	\expandafter\tc@addtomacro\expandafter\tc@coltype\expandafter
		{\expandafter{\tc@othercoltype}}%
	\tc@expaddtomacro\tc@coltype\tc@lastvline
	\let\tc@firstrow\@empty% 1ere ligne
	\tclin\z@\tccol\z@
	\edef\tc@currentcellcode{\tc@cellcode}%
	\tc@expaddtomacro\tc@firstrow\tc@currentcellcode
	\tc@addtomacro\tc@firstrow{\tcatbeginrow#2&}%
	\tc@expaddtomacro\tc@valuelist\tclistsep
	\tclin\@ne\tccol\@ne
	\@nameuse{tc@\tc@orientation readarg}}

\newcommand\tc@analysevalueformula[1]{%
	\StrBefore{#1};[\tc@valueformula]%
	\StrBehind{#1};[\tc@valueinterval]%
	\StrBefore\tc@valueinterval=[\tc@variablevalue]%
	\StrBehind\tc@valueinterval=[\tc@valueinterval]%
		\StrLen\tc@variablevalue[\tc@temp]% controles
		\ifnum\tc@temp=\@ne\else\tc@errmess{Invalid variable}\fi
		\tc@ifemptyerrmess\tc@valueinterval{Invalid syntax}%
	\StrBefore\tc@valueformula=[\tc@formulavariable]%
		\StrLen\tc@formulavariable[\tc@temp]% contr��les
		\ifnum\tc@temp=\@ne\else\tc@errmess{Invalid variable}\fi
		\ifx\tc@variablevalue\tc@formulavariable
			\tc@errmess{Variables must not be the same}%
		\fi
	\StrBehind\tc@valueformula=[\tc@valueformula]%
		\tc@ifemptyerrmess\tc@valueformula{Empty formula}%
	\StrBefore\tc@valueinterval:[\tc@startvalue]%
		\tc@ifemptyerrmess\tc@startvalue{Invalid interval}%
	\StrBehind\tc@valueinterval:[\tc@valueinterval]%
	\StrBefore{\tc@valueinterval[}[[\tc@endvalue]%
		\tc@ifemptyerrmess\tc@endvalue{Invalid interval}%
	\StrBetween\tc@valueinterval[][\tc@valuestep]%
	\ifx\@empty\tc@valuestep\def\tc@valuestep{1}\fi
	\def\tc@temp{0}%
	\ifx\tc@valuestep\tc@temp\tc@errmess{Step must not be 0}\fi
	\ifdim\tc@startvalue pt<\tc@endvalue pt\ifdim\tc@valuestep pt<0pt%
		\tc@errmess{Step should be positive}%
	\fi\fi
	\ifdim\tc@startvalue pt>\tc@endvalue pt\ifdim\tc@valuestep pt>0pt%
		\tc@errmess{Step should be negative}%
	\fi\fi
	\let\tc@valuelist\@empty
	\let\tc@currentvalue\tc@startvalue
	\edef\tc@comparesign{\ifdim\tc@valuestep pt<0pt<\else>\fi}% < ou >
	\tc@buildvaluelist}

\newcommand\tc@buildvaluelist{%
	\edef\tc@temp{%
		\noexpand\tc@evalexpr
		{\tc@valueformula}{\tc@variablevalue}{\tc@currentvalue}}%
	\tc@temp
	\tc@expaddtomacro\tc@valuelist\tc@currentresult
	\FPadd\tc@currentvalue\tc@currentvalue\tc@valuestep
	\FPclip\tc@currentresult\tc@currentresult
	\expandafter\ifdim\expandafter\tc@currentvalue
		\expandafter p\expandafter t\tc@comparesign\tc@endvalue pt
	\else
		\tc@expaddtomacro\tc@valuelist\tclistsep
		\expandafter\tc@buildvaluelist
	\fi
	\tc@temp}

\newcommand\tc@generatevaluelist[3][1]{%
	\tc@analysevalueformula{#3}%
	\edef\tc@temp{%
		\unexpanded{\tc@deftableid[#1]{#2}}%
		{\tc@formulavariable=\tc@valuelist}}%
	\tc@temp}

% lit tous les arguments {nom ligne}{formule} et les assigne dans les sc
% \tcline@i et \tc@formula@i et ainsi de suite, puis va \`a \tc@hbuildlines
\newcommand\tc@hreadarg[2]{%
	\tccol\z@
	\@namedef{tc@line@\romannumeral\tclin}{}%
	\edef\tc@currentcellcode{\tc@cellcode}%
	\tc@expaddtocurrentline\tc@currentcellcode
	\tc@addtocurrentline{\tcatbeginrow#1&}%
	\@namedef{tc@formula@\romannumeral\tclin}{#2}%
	\ifnum\tclin<\tc@nbformulas
		\advance\tclin\@ne
		\expandafter\tc@hreadarg
	\else
		\tccol\@ne
		\expandafter\tc@hbuildlines
	\fi}%

\newcommand\tc@vreadarg[2]{%
	\tclin\z@
	\edef\tc@currentcellcode{\tc@cellcode}% construit la 1\`ere ligne
	\tc@expaddtomacro\tc@firstrow{\tc@currentcellcode#1}%
	\@namedef{tc@formula@\romannumeral\tccol}{#2}%
	\ifnum\tccol<\tc@nbformulas
		\tc@addtomacro\tc@firstrow&%
		\advance\tccol\@ne
		\expandafter\tc@vreadarg
	\else
		\tclin\@ne\tccol\z@
		\loop% initialise les lignes \`a "\tc@currentcellcode\tcatbeginrow"
			\@namedef{tc@line@\romannumeral\tclin}{}%
			\edef\tc@currentcellcode{\tc@cellcode}%
			\tc@expaddtocurrentline\tc@currentcellcode
			\tc@addtocurrentline\tcatbeginrow
			\ifnum\tclin<\tc@nbval
			\advance\tclin\@ne
		\repeat
		\tclin\@ne
		\expandafter\tc@vbuildlines
	\fi}%

\newcommand\tc@findskiplist{%
	\IfBeginWith\tc@currentvalue\tcnoshowmark
		{\tc@showvalfalse% si le signe \tcnoshowmark est pr\'esent...
		 \StrGobbleLeft\tc@currentvalue1[\tc@currentvalue]}% ...efface-le
		{\tc@showvaltrue}%
	\IfSubStr\tc@currentvalue[% s'il y a une skiplist...
		{\let\tc@skiplist\tc@currentvalue
		 \StrBefore\tc@currentvalue[[\tc@currentvalue]% ce qui est avant "["
		 \StrBehind\tc@skiplist\tc@currentvalue[\tc@skiplist]}% d\'efinit-la
		{\let\tc@skiplist\@empty}}

\newcommand\tc@addtocurrentline[1]{%
	\expandafter\tc@addtomacro\csname tc@line@\romannumeral\tclin\endcsname
	{#1}}

\newcommand\tc@expaddtocurrentline[1]{%
	\expandafter\tc@addtocurrentline\expandafter{#1}}

% construit toutes les lignes du tableau par r\'ecursivit\'e principale sur
% le nombre de colonnes
\newcommand\tc@hbuildlines{%
	\tclin\z@
	\StrBefore\tc@valuelist\tclistsep[\tc@currentvalue]% prend \tc@currentvalue
	\StrBehind\tc@valuelist\tclistsep[\tc@valuelist]% purge \tc@valuelist
	\ifx\@empty\tc@currentvalue
	\else
		\tc@findskiplist
		\edef\tc@currentcellcode{\tc@cellcode}%
		\tc@expaddtomacro\tc@firstrow\tc@currentcellcode
		\iftc@showval% pas de noshowmark : ajoute affichage valeur courante
			\let\tc@currentvaluerounded\tc@currentvalue
			\tc@tcroundvalue\tc@currentvaluerounded\tc@printvalueroundprecision
			\expandafter\tc@addtomacro\expandafter\tc@firstrow
				\expandafter{\expandafter\tcprintvalue\expandafter
				{\tc@currentvaluerounded}}%
		\fi
		\tclin\@ne
		\loop
			\edef\tc@currentcellcode{\tc@cellcode}%
			\tc@expaddtocurrentline\tc@currentcellcode
			\tc@displaycurrentresult\tclin
			\ifnum\tclin<\tc@nbformulas
			\advance\tclin\@ne
		\repeat% recommence pour toutes les lignes
	\fi
	\ifnum\tccol<\tc@nbval% s'il reste des colonnes
		\tc@addtomacro\tc@firstrow&% ajoute "&" \`a la 1\`ere ligne
		\tclin\@ne
		\loop% et ajoute "&" aux autres lignes
			\tc@addtocurrentline&%
			\ifnum\tclin<\tc@nbformulas
			\advance\tclin\@ne
		\repeat
		\advance\tccol\@ne
		\let\tc@temp\tc@hbuildlines% recommence tout le processus
	\else
		\let\tc@nblines\tc@nbformulas
		\ifx\tc@filename\@empty
			\let\tc@temp\tc@printtabular% sinon, va afficher la table
		\else
			\let\tc@temp\tc@exporttabular% ou l'exporter dans un fichier
		\fi
	\fi
	\tc@temp}

% construit les lignes du tableau par r\'ecursivit\'e principale sur \tclin
\newcommand\tc@vbuildlines{%
	\StrBefore\tc@valuelist\tclistsep[\tc@currentvalue]% prend \tc@currentvalue
	\StrBehind\tc@valuelist\tclistsep[\tc@valuelist]% purge \tc@valuelist
	\ifx\@empty\tc@currentvalue
	\else
		\tc@findskiplist
		\iftc@showval% pas de noshowmark : ajoute affichage valeur courante
			\tc@tcroundvalue\tc@currentvalue\tc@printvalueroundprecision
			\expandafter\tc@expaddtocurrentline\expandafter
				{\expandafter\tcprintvalue\expandafter{\tc@currentvalue}}%
		\fi
		\tccol\@ne
		\loop
			\tc@addtocurrentline&% change de cellule dans la ligne en cours
			\edef\tc@currentcellcode{\tc@cellcode}%
			\tc@expaddtocurrentline\tc@currentcellcode
			\tc@displaycurrentresult\tccol% ajoute le r\'esultat courant ou pas
			\ifnum\tccol<\tc@nbformulas
			\advance\tccol\@ne
		\repeat% s'il reste des colonnes, recommence
	\fi
	\ifnum\tclin<\tc@nbval
		\advance\tclin\@ne
		\let\tc@temp\tc@vbuildlines% s'il reste des lignes, recommence
	\else
		\let\tc@nblines\tc@nbval
		\ifx\tc@filename\@empty
			\let\tc@temp\tc@printtabular% sinon, va afficher la table
		\else
			\let\tc@temp\tc@exporttabular% ou l'exporter dans un fichier
		\fi
	\fi
	\tc@temp}

\newcommand\tc@displaycurrentresult[1]{%
	\StrBetween\tc@skiplist[][\tc@nexttoskip]%
	\ifx\@empty\tc@skiplist% pas de skiplist -> ajoute l'affichage de resultat
		\tc@addcurrentresult#1%
	\else
		\ifnum\tc@nexttoskip=\z@% si 0, n'affiche rien
		\else
			\ifnum\tc@nexttoskip=#1% si le numero correspond
				\StrBehind\tc@skiplist][\tc@skiplist]% purge la skiplist
			\else% sinon, ajoute l'affichage de resultat
				\tc@addcurrentresult#1%
			\fi
		\fi
	\fi}

\newcommand\tc@addcurrentresult[1]{% #1 : compteur courant
	\edef\tc@temp{%
		\noexpand\tc@evalexpr{\@nameuse{tc@formula@\romannumeral#1}}%
		{\tc@formulavariable}{\tc@currentvalue}}%
	\tc@temp
	\tc@tcroundresult\tc@currentresult\tc@printresultroundprecision
	\edef\tc@temp{%
		\noexpand\tc@addtocurrentline{\noexpand\tcprintresult
		{\tc@currentresult}{\tc@currentvalue}}}%
	\tc@temp}

\newcommand\tc@printcalclines{%
	\@nameuse{tc@line@\romannumeral\tclin}% affiche la ni\`eme ligne
	\ifnum\tclin<\tc@nblines% et s'il en reste encore \`a afficher
		\\\tc@interrule% va \`a la ligne, met le filet
		\global\advance\tclin\@ne
		\expandafter\tc@printcalclines% et recommence
	\fi}%

\newcommand\tc@printtabular{%
	\global\tclin\z@
	\def\tc@currentvalue{\begin{tabular}}%
	\expandafter\tc@currentvalue\expandafter{\tc@coltype}\tc@updwnrule
		\tc@firstrow\\\tc@firstrule% 1\`ere ligne +  1er filet
		\global\tclin\@ne
		\tc@printcalclines% affichage des autres lignes
		\\\tc@updwnrule% filet du bas
	\end{tabular}% fin du tableau
	\endgroup}

% les macro d'export dans un fichier
\tc@checkdefine\tc@filename{}
\newcommand\tcwritetofile[1]{\gdef\tc@filename{#1}}

\newcommand\tc@exporttabular{%
	\immediate\openout\tc@export=\tc@filename
	\immediate\write\tc@export{%
		\noexpand\begin{tabular}{\unexpanded\expandafter{\tc@coltype}}%
		\unexpanded\expandafter{\tc@updwnrule}}%
	\immediate\write\tc@export{%
		\unexpanded\expandafter{\tc@firstrow\\}%
		\unexpanded\expandafter{\tc@firstrule}}%
	\global\tclin\@ne
	\tc@exportcalclines
	\immediate\write\tc@export{\noexpand\end{tabular}}%
	\immediate\closeout\tc@export
	\endgroup
	\gdef\tc@filename{}}

\newcommand\tc@exportcalclines{%
	\ifnum\tclin<\tc@nblines
		\let\tc@temp\tc@interrule
	\else
		\let\tc@temp\tc@updwnrule
	\fi
	\immediate\write\tc@export{%
		\unexpanded\expandafter\expandafter\expandafter
		{\csname tc@line@\romannumeral\tclin\endcsname}\noexpand\\
		\unexpanded\expandafter{\tc@temp}}% affiche la ni\`eme ligne
	\ifnum\tclin<\tc@nblines% et s'il en reste encore \`a afficher
		\global\advance\tclin\@ne
		\expandafter\tc@exportcalclines% et recommence
	\fi}

%%% Les macros d'arrondi %%%%
\newcommand\tcprintroundvalue{%
	\@ifstar
		{\let\tc@formatroundednumber\@gobbletwo\tc@printroundvalue}%
		{\let\tc@formatroundednumber\FPclip\tc@printroundvalue}}

\newcommand\tc@printroundvalue[1]{%
	\ifx\@empty#1\@empty\let\tc@tcroundvalue\@gobbletwo
	\else\let\tc@tcroundvalue\tc@round\def\tc@printvalueroundprecision{#1}%
	\fi}

\newcommand\tcprintroundresult{%
	\@ifstar
		{\let\tc@formatroundednumber\@gobbletwo\tc@printroundresult}%
		{\let\tc@formatroundednumber\FPclip\tc@printroundresult}}

\newcommand\tc@printroundresult[1]{%
	\ifx\@empty#1\@empty\let\tc@tcroundresult\@gobbletwo
	\else\let\tc@tcroundresult\tc@round\def\tc@printresultroundprecision{#1}%
	\fi}

\newcommand\tc@round[2]{% #1=sc contenant le nombre #2=rang d'arrondi
	\FPround#1#1{#2}\tc@formatroundednumber#1#1}

\tcprintroundresult{}% par d'arrondi \`a l'affichage par d\'efaut
\tcprintroundvalue{}% par d'arrondi \`a l'affichage par d\'efaut
%
% Historique
%------------------------------------------------------------------------------
% v0.1    19/03/2009
% - Premi\`ere version
%------------------------------------------------------------------------------
% v0.2    21/04/2009
% - pgfmath est abandonn\'e puisque beaucoup trop imprecis.
%   Le moteur de calcul est d\'esormais fp.
%   2 probl\`emes dans \FPpow peuvent \^etre corrig\'e par l'option "fixFPpow".
% - Les valeurs peuvent \^etre calcul\'ees au lieu d'\^etre entr\'ees une par
%   une. Cette fonctionnalit\'e tile lorsque ces valeurs suivent une loi
%   math\'ematique dans un intervalle donn\'e.
% - Il est possible avec la commande \tcwritetofile d'exporter le prochain
%   tableau vers un fichier dont on choisit le nom
% - Ajout de la traduction du manuel en vietnamien.