%% \CheckSum{145}
% \iffalse
% File: paramcalc.dtx
% Copyright (C) 2026 Miguel V. S. Frasson (mvsfrasson@gmail.com)
%
% This package may be distributed under the terms of the LaTeX
% Project Public License, as described in lppl.txt in the base
% LaTeX distribution, either version 1.3 or (at your option)
% any later version.
%
%<*driver>
\documentclass{ltxdoc}
\usepackage[T1]{fontenc}
\usepackage[lining]{ebgaramond}
\usepackage{ebgaramond-maths,textcomp}
\usepackage[cmintegrals,cmbraces]{newtxmath}
\let\epsilon\varepsilon
\usepackage{xcolor,microtype,amsmath,amstext,tikz,centerlastline}
\usepackage{paramcalc}
% \usepackage[margin=1in]{geometry}
\newcommand{\var}[1]{\texttt{\expandafter\string\csname #1\endcsname}}
\RecordChanges
\begin{document}
  \DocInput{paramcalc.dtx}
\end{document}
%</driver>
% \fi
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \StopEventually
%
% \changes{1.0}{2026/01/27}{Initial version}
%
% \title{\textsc{paramcalc}: compute parameters to fit condition}
% \author{Miguel V.\ S.\ Frasson\\(\texttt{mvsfrasson@gmail.com})}
% \date{2026, jan. 27}
%
% \maketitle
%
% \begin{abstract}\centerlastline\noindent
%   Sometimes one wants to get font size or letter spacing so that
%   text fits a desired length, or some unit length so that a picture
%   fits some desired size, for instance.  This package implements
%   macros to compute such parameters to fit a condition.
% \end{abstract}
%
% \section{The purpose of this package}

% Suppose that one wants to create a nice logo with two lines of same
% size, first line “AWESOME” and second line “example of use for
% \texttt{paramcalc}”.  We look for font size of first line, so that
% the lines have the same width.  This is the desired result:
%
% \newlength{\lineIwd}
% \newlength{\lineIIwd}
% \newcommand{\mytext}{\Large example of use for paramcalc}
% \settowidth{\lineIIwd}{\mytext}
% 
% \paramcalc{12}{50}{\lineIIwd}[\lineIwd]{
%   \settowidth{\lineIwd}{\fontsize{\x}{\x}\selectfont \textls*{AWESOME}}
% }
%
% \begin{center}
%   {\fontsize{\x}{\x}\selectfont \textls*{AWESOME}\par}
%   \mytext \vspace*{\bigskipamount}
% \end{center}
%
% Instead of setting font size by hand\footnote{As width is
% proportional to font size, one could also compute font size by
% proportion, measuring texts of both lines.} and visually trying to get the
% desired condition, we want to compute such font size for line 1.
%
% This can be acomplished with \texttt{paramcalc} with the following
% code (make sure you use scalable fonts\footnote{Computer Modern are
% not scalable, but Latin Modern (package \texttt{lmodern}), EB
% Garamond (package \texttt{ebgaramond}) and many others are.}):
%
% \enlargethispage{\baselineskip}
% \begin{verbatim}
%\newcommand{\mytext}{\Large example of use for paramcalc}
%\newlength{\lineIIwd}
%\settowidth{\lineIIwd}{\mytext}
%
%\newlength{\lineIwd}
%\paramcalc{12}{50}{\lineIIwd}[\lineIwd]{
%  % compute \lineIwd as function of fontsize \x
%  \settowidth{\lineIwd}{\fontsize{\x}{\x}\selectfont \textls*{AWESOME}}
%}
%
%Showing the result. 
%\begin{center}
%  {\fontsize{\x}{\x}\selectfont \textls*{AWESOME}\par}
%  \mytext
%\end{center}
%\end{verbatim}
% 
% It you use \texttt{\string\x} in the document, you get the computed
% value \x.
% 
% \section{Usage}
%
% To compute a desired parameter, the situation can be reduced to find
% a zero of a real function.  In the example above, we wanted that the
% length $y(x)$ of first line as function of its font size $x$ should
% match the length $y_0$ of second line, so that we need to find the
% zero of the function $f(x) = y(x) - y_0$.
%
% The computation is done by \texttt{\string\paramcalc} macro.\medskip
%
% \DescribeMacro{\paramcalc}^^A
% \newcommand{\varp}[1]{\textlangle\makebox{\textit{#1}}\textrangle}^^A
% \newcommand{\varm}[1]{\texttt{\{}\varp{#1}\texttt{\}}}^^A
% \newcommand{\varopt}[1]{\texttt{[}\varp{#1}\texttt{]}}
% \noindent\texttt{\string \paramcalc}\varopt{x macro}^^A
% \varm{x initial}\varm{x final}\varopt{$\epsilon$}^^A
% \varm{y desired}\varopt{y macro}\varm{body}\medskip
%
% The optional \varp{x macro} defaults to \texttt{\string\x}, the
% optional \varp{y macro} defaults to \texttt{\string\y} and both
% should be control sequences.  \varp{y macro} and \varp{y desited}
% could be lengths, converted to numbers as pt.
% 
% \varp{body} should be code that computes \varp{y macro} for an
% arbitrary value of \varp{x macro}, supposed to expand to a decimal
% value.  The result is stored in \varp{x macro} after its
% computation.  If its value is in between \varp{x initial} and
% \varp{x final}, convergence is ensured.  Often\footnote{I could not
% figure out any real life example where the function was not affine.}
% the functions involved are affine (for instance, a width is
% proportional a font size), and in this case convergence is achieved
% after just one step.
%
% The convergence is achieved when
% $\vert \text{\varp{y macro}} - \text{\varp{y desired}}\vert <
% \epsilon$.
% The value of $\epsilon$, where $\epsilon$ is given in optional arg
% \varp{epsilon} and defaults to 0.05.  If it doesn't happen after a
% maximum number of steps, an error is signaled.  Then, probably de
% desired $x$ is outside initial interval.
%
% There are situations where only integer values of $x$ are suitable,
% like in the optional parameter \textit{n} for
% \texttt{\string\textls}\varopt{n}\varm{text}.  In such cases, use
% \texttt{\string\intparamcalc}, with same arguments.  The only
% difference is that \varp{x macro} only assume integer values, but
% \varp{y macro} still are supposed to assume real values.\medskip
%
% \DescribeMacro{\intparamcalc}^^A
% \noindent\texttt{\string \intparamcalc}\varopt{x macro}^^A
% \varm{x initial}\varm{x final}\varopt{$\epsilon$}^^A
% \varm{y desired}\varopt{y macro}\varm{body}\medskip
%
% \textit{Attention}: The numerical method implemented in this package
% is not suitable when $y$ assumes (too) discrete values or isn't a
% continuous function of $x$ (it its where real-valued) like when $y$
% counts lines, for instance.
%
% \subsection{Package options}
%
% \begin{itemize}
% \item \texttt{maxsteps} = \varp{n} : sets maximum number of steps
% before giving up.  Defaults to 20.
% \item \texttt{epsilon} = \varp{epsilon} : sets the convegence
% criterium $\epsilon$ to \varp{epsilon}.  Defaults to 0.05.
% \end{itemize}
%
% \section{Examples}
%
% Creating a logo for the book “História Geral do Brasil”, in three
% lines.
% \begin{itemize}
% \item The first two lines in font size 20pt of size;
% \item For the first line we compute letter spacing to fit size of 2nd line;
% \item For the third line, we compute font size, keeping same size.
% \end{itemize}
%
%\begin{center}
%   \fontsize{20}{22}\bfseries
%  
%   \newlength{\lIIwd} \newlength{\auxwd}
%   \settowidth{\lIIwd}{\textls*{GERAL \,DO}}
% 
%   \intparamcalc[\lIls]{0}{200}{\lIIwd}[\auxwd]{
%     \settowidth{\auxwd}{\textls*[\lIls]{HISTÓRIA}}}
%   
%   \paramcalc[\lIIIfs]{20}{50}{\lIIwd}[\auxwd]{
%     \settowidth{\auxwd}{\fontsize{\lIIIfs}{\lIIIfs}\selectfont \textls*{BRASIL}}}
% 
%   \textls*[\lIls]{HISTÓRIA} \\ \textls*{GERAL \,DO} \\
%   \fontsize{\lIIIfs}{\lIIIfs}\selectfont \textls*{BRASIL} 
% \end{center}
%
% This is achieved with the code:
% \begin{verbatim}
%\begin{center}
%  \fontsize{20}{22}\bfseries
% 
%  % base length \lIIwd: line 2 with \textls* and \bfseries
%  \newlength{\lIIwd} \newlength{\auxwd}
%  \settowidth{\lIIwd}{\textls*{GERAL \,DO}}
%
%  % compute letter spacing \lIls for 1st line: body compute \auxwd 
%  \intparamcalc[\lIls]{0}{200}{\lIIwd}[\auxwd]{
%    \settowidth{\auxwd}{\textls*[\lIls]{HISTÓRIA}}}
%  
%  % compute font size \lIIIfs for 3rd line: body compute \auxwd 
%  \paramcalc[\lIIIfs]{20}{50}{\lIIwd}[\auxwd]{
%    \settowidth{\auxwd}{\fontsize{\lIIIfs}{\lIIIfs}\selectfont \textls*{BRASIL}}}
%
%  % typesetting the logo
%  \textls*[\lIls]{HISTÓRIA} \\ \textls*{GERAL \,DO} \\
%  \fontsize{\lIIIfs}{\lIIIfs}\selectfont \textls*{BRASIL} 
%\end{center}
% \end{verbatim}
% 
% \section{Numerical method implemented}
%
% To ensure convergense (with a root in $[a,b]$), we implement Illinois
% algorithm\footnote{Dowell, M., Jarratt, P.  A modified regula falsi
% method for computing the root of an equation. \textit{BIT Numerical
% mathematics} \textbf{11}, 168–174
% (1971). \url{https://doi.org/10.1007/BF01934364}}, an improvement
% over False Position method (\textit{regula falsi}):
% 
% Assuming that $f:I\to \mathbb{R}$ is a continuous function,
% end-points $a, b \in I$, $f(a)\cdot f(b) < 0$, so $f$ has a root in $[a,b]$.
% 
% Let $c$ be the root of the secant line that connects $(a,f(a))$ and
% $(b,f(b))$.
%
% \begin{center}
%   \begin{tikzpicture}
%     \draw[->] (-0.5,0) -- (2.5,0); \draw[->] (0,-1.5) -- (0,1.5);
%     \draw[thick] (0.5,-1) coordinate (a)
%     (a|-0,0) node[above] {$a$}
%     (1.8,1.5) coordinate (b)
%     (b|-0,0) node[below right] {$b$}
%     (a) -- (b)
%     (1.02,0) coordinate (x) node[below right] {$c$};
%     \draw[dashed] (a|-0,0) -- (a) -- (a-|b) -- (b);
%     \fill (a) circle[radius=1pt] (b)
%     circle[radius=1pt] (x) circle[radius=1pt];
%   \end{tikzpicture}
% \end{center}
% so, by similarity of triangles,
% \begin{equation}\label{eq:c}
%   \Delta = \frac{b-a}{f(b)-f(a)} = \frac{b-c}{f(b)} \implies
%   c = b - f(b)\Delta. 
% \end{equation}
%
% The Illinois algorithm for False Position puts the following iteraction:
% \begin{align}
%   (a,f(a)) & =
%   \begin{cases}
%     \bigl(c,f(c)\bigr), & \text{if } f(b)f(c)< 0,\\
%     \bigl(a,\frac{f(a)}{2}\bigr), & \text{otherwise}
%   \end{cases}\label{eq:iter1} \\
%   (b,f(b)) & = (c,f(c)) \label{eq:iter2}
% \end{align}
%
% \section{Code}
%
% Identidication of the package.
%    \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{paramcalc}[2026/01/27 v1.0 compute params to fit conditions]
%    \end{macrocode}
% 
% We use \LaTeX3 codebase.
%    \begin{macrocode}
\ExplSyntaxOn
%    \end{macrocode}
%
% There are two versions of the solving macros: real roots are
% admitted or only integer roots (like parameters for
% \texttt{\string\textls[\string\x]\{\textit{text}\}}).  We do a
% single implementation and the cases are regulated by a boolen.
%    \begin{macrocode}
\bool_new:N \g_PARC_int_bool
%    \end{macrocode}
%
% The iteraction will end when $|f(c)|<\epsilon$.
%
% Floating point variables for  $a$, $b$, $f(a)$, $f(b)$, $c$, $f(c)$
% and $\Delta$:
%    \begin{macrocode}
\fp_new:N \l_PARC_a_fp
\fp_new:N \l_PARC_b_fp
\fp_new:N \l_PARC_fa_fp
\fp_new:N \l_PARC_fb_fp
\fp_new:N \l_PARC_x_fp
\fp_new:N \l_PARC_fx_fp
\fp_new:N \l_PARC_delta_fp
%    \end{macrocode}
% Also, a counter for steps taken, in case of divergence.
%    \begin{macrocode}
\int_new:N \l_PARC_steps_int
%    \end{macrocode}
%
% Defining package option for maximum number of steps.
%    \begin{macrocode}
\int_new:N \l_PARC_maxsteps_int
\int_set:Nn \l_PARC_maxsteps_int { 20 }
\keys_define:nn {paramcalc} { maxsteps .int_set:N = \l_PARC_maxsteps_int }
%    \end{macrocode}
%
% Defining package option for default epsilon (stop criterium).
%    \begin{macrocode}
\fp_new:N \l_PARC_epsilon_fp
\fp_set:Nn \l_PARC_epsilon_fp { 0.05 }
\keys_define:nn {paramcalc} { epsilon .fp_set:N = \l_PARC_epsilon_fp }
%    \end{macrocode}
%
% Passing options to package.
%    \begin{macrocode}
\DeclareOption*{ \keys_set:ne { paramcalc } { \CurrentOption } }
\ProcessOptions
%    \end{macrocode}
%
% \DescribeMacro{\PARC\_paramcalc:NnnnnNn}
% The arguments of $\mathtt{\string\PARC\_paramcalc:NnnnnNn}$
% are\\
% \texttt{\#1} = macro that holds value for $x$ (default to
% \texttt{\string\x} later on)\\
% \texttt{\#2} = $a$\\
% \texttt{\#3} = $b$\\
% \texttt{\#4} = $\epsilon$\\
% \texttt{\#5} = $y_0$\\
% \texttt{\#6} = macro that stores computed $y(x)$ (default to
% \texttt{\string\y} later on)\\
% \texttt{\#7} = body of code that computes $y(x)$ and stores value in
% \texttt{\#6}.
%    \begin{macrocode}
\cs_new:Nn \PARC_paramcalc:NnnnnNn
{
%    \end{macrocode}
% Store $a$ from \texttt{\#2} in \var{l_PARC_a_fp} and set
% \texttt{\#1} as the decimal representation of $a$, taking care if
% integer values must be used.
%    \begin{macrocode}
  \fp_set:Nn \l_PARC_a_fp { #2 }
  \bool_if:NTF \g_PARC_int_bool
  { \tl_set:Ne #1 { \fp_to_int:n { #2 } } }
  { \tl_set:Ne #1 { \fp_to_decimal:n { #2 } } }
%    \end{macrocode}
% Call body to get $y(x)$ in \texttt{\#6}
%    \begin{macrocode}    
  #7
%    \end{macrocode}
% Save $f(x) = y(x) - y_0$ in \var{l_PARC_fa_fp}
%    \begin{macrocode}
  \fp_set:Nn \l_PARC_fa_fp { #6 - (#5) }
%    \end{macrocode}
%
% Do as before, now for $b$ stored in \texttt{\#3}:
%    \begin{macrocode}
  \fp_set:Nn \l_PARC_b_fp { #3 }
  \bool_if:NTF \g_PARC_int_bool
  { \tl_set:Ne #1 { \fp_to_int:n { #3 } } }
  { \tl_set:Ne #1 { \fp_to_decimal:n { #3 } } }
  #7
  \fp_set:Nn \l_PARC_fb_fp { #6 - (#5) }
%    \end{macrocode}
%
% In many situations, the function $f(x)$ is affine and the secant
% method converges after the first iteration, so it really doesn't
% matter if $f(a)f(b)<0$, so we trigger just a warning if signs of
% $f(a)$ and $f(b)$ are the same.
%    \begin{macrocode}
  \fp_compare:nNnT { (\l_PARC_fa_fp) * (\l_PARC_fb_fp) } > { 0 }
  {
    \msg_warning:nn {paramcalc}
    {
      The ~values ~of ~\string#6 ~are
      ~{\fp_to_decimal:N \l_PARC_fa_fp} ~and
      ~{\fp_to_decimal:N \l_PARC_fa_fp}, ~both
      \fp_compare:nNnTF { \l_PARC_fa_fp } > 0 { ~above } { ~below } 
      ~{\fp_to_decimal:n {#5}} ~so ~convergence ~is ~not ~ensured.\\
      Try ~to ~set ~initial ~values ~that ~surround ~the ~root.
    }
  }
%    \end{macrocode}
% \bigskip
%
% Now the loop. Last iteraction is supposed to be stored in
% \var{l_PARC_fb_fp}. Stop condition is $|f(b)|< \epsilon$, with
% $\epsilon$ stored in \texttt{\#4}.
%    \begin{macrocode}    
  \fp_while_do:nNnn { abs(\l_PARC_fb_fp) } > { #4 }
  {
%    \end{macrocode}
% Computing $\Delta$ and $c$ as in \eqref{eq:c}.
%    \begin{macrocode}
    \fp_set:Nn \l_PARC_delta_fp
      { (\l_PARC_b_fp - \l_PARC_a_fp) / (\l_PARC_fb_fp - \l_PARC_fa_fp) }
    \fp_set:Nn \l_PARC_c_fp 
      { \l_PARC_b_fp - (\l_PARC_fb_fp) * (\l_PARC_delta_fp) }
%    \end{macrocode}
% Compute $f(c)$ and store it in \var{l_PARC_fc_fp}.
%    \begin{macrocode}
    \bool_if:NTF \g_PARC_int_bool
    { \tl_set:Ne #1 { \fp_to_int:N \l_PARC_c_fp } }
    { \tl_set:Ne #1 { \fp_to_decimal:N \l_PARC_c_fp } }
    #7
    \fp_set:Nn \l_PARC_fc_fp { #6 - (#5) }
%    \end{macrocode}
% Now implement Illinois iteraction from \eqref{eq:iter1}
%    \begin{macrocode}
    \fp_compare:nNnTF { (\l_PARC_fc_fp) * (\l_PARC_fc_fp) } < { 0 }
    {
      \fp_set:Nn \l_PARC_a_fp { \l_PARC_b_fp }
      \fp_set:Nn \l_PARC_fa_fp { \l_PARC_fb_fp }
    }
    {
      \fp_set:Nn \l_PARC_fa_fp { 0.5 * (\l_PARC_fa_fp) }
    }
%    \end{macrocode}
% and \eqref{eq:iter2}.
%    \begin{macrocode}
    \fp_set:Nn \l_PARC_b_fp { \l_PARC_c_fp }
    \fp_set:Nn \l_PARC_fb_fp { \l_PARC_fc_fp }
  }
%    \end{macrocode}
%
% Handling possible divergence if $f(a)f(b)>0$. If it doesn't converge
% after \texttt{\string\l\_PARC\_maxsteps\_int} steps, an error is
% signaled.
%    \begin{macrocode}
  \int_incr:N \l_PARC_steps_int
  \int_compare:nNnT { \l_PARC_steps_int } > { \l_PARC_maxsteps_int }
  {
    \msg_error:nn {paramcalc}
    {
      Failed ~to ~converge ~after ~\int_use:N \l_PARC_steps_int ~steps.\\
      Try ~to ~narrow ~initial ~values ~around ~the ~zero.
    }
  }
}
%    \end{macrocode}
% Recall that the user access the computed value from \texttt{\#1}.
%
% Now we define user functions that set \var{g_PARC_int_bool} and
% call previous function.
%
% Define \texttt{\string\paramcalc}.
%    \begin{macrocode}
\NewDocumentCommand{\paramcalc}{ O{\x} m m O{0.05} m O{\y} m}
{
  \bool_set:Nn \g_PARC_int_bool { \c_false_bool }
  \PARC_paramcalc:NnnnnNn #1 { #2 } { #3 } { #4 } { #5 } #6 { #7 }
}
%
% Define \texttt{\string\intparamcalc}.
%    \begin{macrocode}
\NewDocumentCommand{\intparamcalc}{ O{\x} m m O{0.05} m O{\y} m}
{
  \bool_set:Nn \g_PARC_int_bool { \c_true_bool }
  \PARC_paramcalc:NnnnnNn #1 { #2 } { #3 } { #4 } { #5 } #6 { #7 }
}
%    \end{macrocode}
%
% Finish \LaTeX3 syntax.
%    \begin{macrocode}
\ExplSyntaxOff
%    \end{macrocode}
%
% \Finale
%
\endinput
%
%% \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         \~}
