4

I found in this page in the first answer a way to write beautiful base conversions. But, I had a compile error if Y try to use the last example, the ConverToBase command. I searched, but I don't find where was the mistake. Can you help me ? The code :

\documentclass{article}
\usepackage{geometry}
\usepackage{xintexpr}
\usepackage{tikz}

% From https://tex.stackexchange.com/a/400571/4686
\newcommand{\tablenode}[2]
 {\tikz[baseline=(#1.base),remember picture]\node[inner sep=0pt,name=#1]{#2};}

\makeatletter
\newcommand\ConvertDigit[1]{\ifcase\numexpr#1\relax
0\or1\or2\or3\or4\or5\or6\or7\or8\or9%
\or A\or B\or C\or D\or E\or F\or G%
\or H\or I\or J\or K\or L\or M\or N%
\or O\or P\or Q\or R\or S\or T\or U%
\or V\or W\or X\or Y\or Z\else\TOOBIGDIGIT\fi}
%
\newcommand\ConvertToBase[2]{%
% #1 = target base
% #2 = number to be converted
  \begingroup
  \ttfamily
  \edef\my@base{#1}%   allow #1 to be a macro, must be at most 36
  \edef\my@number{#2}% allow #2 to be a macro
  \global\let\my@remainder\my@number
  % produce a comma separated list of all the powers of the base
  % at most equal to the number, with highest power first
  % of course, it is not really needed to use \xintiiexpr here,
  % but this is fun (to me) and avoids defining any helper macro...
  \edef\my@powers{%
    \xinttheiiexpr 
      [reversed(rseq(1; (@>\my@number)?{abort}{\my@base*@},n=1++))][1:]
      % [1:] is Python slicing notation to drop the first item as
      % it is actually the first base^something > number
    \relax
  }%
  \edef\my@nbofdigits{\xintCSVLength{\my@powers}}%
  \global\let\my@nodeindex\my@nbofdigits
% make fun things with colors
  \definecolorseries{foo}{rgb}{last}{red}{blue}%
  \resetcolorseries[\my@nbofdigits]{foo}%
\begin{tabular}{@{}c@{}c@{}}% used for positioning, surely a better TikZ
                            % solution exists
  % nothing here
  &
  \begin{tabular}{@{}r@{}r@{}r@{}}
    &\my@number&\\
    \cline{2-2}
% \xintFor works with comma separated lists,
% it expands once the list argument
    \xintFor ##1 in {\my@powers}\do{%
     \xdef\my@nodeindex{\the\numexpr\my@nodeindex-1}% step by -1
     \xintAssign\xintiiDivision{\my@remainder}{##1}\to\Q\R
% no LaTeX \@nameedef, only \@namedef (booooohhhh....)
     \global\expandafter\let\csname my@digits\my@nodeindex\endcsname\Q
     \xdef\my@temp{\xintiiSub{\my@remainder}{\R}}% = digit times base^power
     \global\let\my@remainder\R
     $\tablenode{A\my@nodeindex}
               {\textcolor{foo!!+}{\@nameuse{my@digits\my@nodeindex}}}
     \times{}$&$##1$&\null\space$(\my@base^{\my@nodeindex})$\\
     ${}={}$&$\my@temp$&\\
     $\to$&$\my@remainder$&\\
     \ifnum\my@nodeindex=\z@\else\cline{2-2}\fi % happy that \cline ok before \fi
     }% end of xintFor loop
  \end{tabular}\\
% CONVERTED NUMBER
  \resetcolorseries[\my@nbofdigits]{foo}% good that global effect
% \xintFor* works with braced items
% it f-expands the list argument (at each iteration after pruning an item)
% \xintSeq produces list of number, detects automatically if needs
% to go decreasing
  \xintFor* ##1 in {\xintSeq{\my@nbofdigits-1}{0}}\do{%
      \tablenode{B##1}
      {\textcolor{foo!!+}{\ConvertDigit{\@nameuse{my@digits##1}}}}%
      }% end of second For loop
  ${}_{\my@base}$%
  &
  % nothing here
\end{tabular}% end of enclosing tabular used for positioning other one
%
% workaround the fact that foo!!+ from xcolor steps twice when used
% with tikz's \draw (I guess once from the arrow, once from the arrow tip)
  \resetcolorseries[\numexpr2*\my@nbofdigits\relax]{foo}%
% DRAWING THE ARROWS
%
% YOU NEED TO COMPILE AT LEAST TWICE FOR THE START AND END
% NODE LOCATIONS TO STABILIZE
%
  \begin{tikzpicture}[remember picture, overlay, >=stealth]
    \xintFor* ##1 in {\xintSeq{\my@nbofdigits-1}{0}}\do{%
       \draw [->,very thick,{foo!!+}] 
             (A##1.west) to[out=180,in=90] (B##1.north);%
      }%
  \end{tikzpicture}%<-- no space here
 \endgroup
}
\makeatother

\begin{document}\pagestyle{empty}

\ConvertToBase{2}{10000}% layout takes lot of vertical space...

\clearpage

\ConvertToBase{16}{17155990251297}

\clearpage

\ConvertToBase{16}{4096}

\end{document}

The expected output : something like this

1 Answer 1

6

The issue is that the powers (for example 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2 for the first case) are calculated with the following line of code:

\xinttheiiexpr 
      [reversed(rseq(1; (@>\my@number)?{abort}{\my@base*@},n=1++))][1:]

This creates as output:

[8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2]

Later there is a loop over these powers, which uses \xintFor. This loop splits the input on commas.

However, since the opening [ is in the output, splitting the list makes the first item [8192 instead of 8192 what it should be. This generates the Missing number error, because [8192 is not a number.

Luckily the fix is simple: remove the square brackets from the original expression.

\edef\my@powers{%
    \xinttheiiexpr 
      reversed(rseq(1; (@>\my@number)?{abort}{\my@base*@},n=1++))[1:]
      % [1:] is Python slicing notation to drop the first item as
      % it is actually the first base^something > number
    \relax
  }%

With this change it works as in the original question.


As for the reason why the original code used to work before and does not work now: I don't know :) but probably the implementation of either \xintFor or \xinttheiiexpr or reversed has changed to not strip brackets or add them, respectively.

1
  • 1
    checking on CTAN xint the CHANGES.html page I went through the "breaking changes" and found this from the 1.4 2020/01/31 log entry: Formerly square brackets [...] were, on their own, not different from parentheses (and thus disappeared from the output), but they are now a genuine constructor of nested lists. For example \xinteval{1, [2, [3, 4]], 5} produces 1, [2, [3, 4]], 5 (recall this is free bloatware).
    – user691586
    Commented Jun 15 at 8:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .