% -*- coding: utf-8; time-stamp-format: "%02d-%02m-%:y at %02H:%02M:%02S %Z" ; time-stamp-active: nil -*- %<*dtx> \def\zeckdtxtimestampaux #1<#2>{#2} \edef\zeckdtxtimestamp{\zeckdtxtimestampaux Time-stamp: <16-11-2025 at 18:36:26 CET>} % %<*drv> %% --------------------------------------------------------------- \def\zeckpackdate{2025/11/16}% \def\zeckversion {0.9d}% % %<*dtx> \catcode0 0 \catcode`\\ 12 ^^@iffalse % %<*readme>-------------------------------------------------------- | Source: zeckendorf.dtx | Version: 0.9d, 2025/11/16 | Author: Jean-François Burnol | Keywords: Zeckendorf representation; Knuth Fibonacci multiplication | License: LPPL 1.3c Aim === This package extends `\xinteval`'s syntax: - The four operations are overloaded to do algebra in `Q(phi)`, where phi is a variable standing for the golden ratio. - Functions are available in this syntax to compute: - Fibonacci numbers, - Zeckendorf representations of positive integers (as lists of indices), - Bergman phi-representations of positive elements of `Z[phi]` (as lists of exponents). - The `$` character serves as infix operator computing the Knuth Fibonacci multiplication of positive integers. The macro interface also provides conversion of integers to Zeckendorf words and Bergman phi-ary representations. The functionalities can be used in three ways: - as a LaTeX package: `\usepackage{zeckendorf}`, - as a Plain e-TeX macro file: `\input zeckendorfcore.tex`, - or interactively on the command line: `etex zeckendorf`. If available use rather `rlwrap etex zeckendorf` for a smoother experience. The package is in alpha stage and its user interface may change in backwards incompatible ways. Of course, suggestions are most welcome. Installation ============ Exert `etex zeckendorf.dtx` to extract macro files. To build the documentation run `xelatex` on the extracted `zeckendorf-doc.tex`, or directly on `zeckendorf.dtx`. The recommended TDS structure is: /tex/generic/zeckendorf/zeckendorfcore.tex /tex/generic/zeckendorf/zeckendorf.tex /tex/generic/zeckendorf/zeckendorf.sty /source/generic/zeckendorf/zeckendorf.dtx /doc/generic/zeckendorf/zeckendorf-doc.pdf /doc/generic/zeckendorf/README.md License ======= Copyright © 2025 Jean-François Burnol | This Work may be distributed and/or modified under the | conditions of the LaTeX Project Public License 1.3c. | This version of this license is in > | 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 "author-maintained". The Author and Maintainer of this Work is Jean-François Burnol. This Work consists of the main source file and its derived files zeckendorf.dtx, zeckendorfcore.tex, zeckendorf.tex, zeckendorf.sty, README.md, zeckendorf-doc.tex, zeckendorf-doc.pdf %-------------------------------------------------------- %<*!readme> %% --------------------------------------------------------------- %% The zeckendorf package: Zeckendorf representation of big integers %% Copyright (C) 2025 Jean-Francois Burnol %% % %<*changes>------------------------------------------------------- \item[0.9d (2025/11/16)] \textbf{Breaking changes:} \begin{itemize}[nosep] \item The radix separator used by default by |\PhiBasePhi| on output and expected by |\PhiXfromBasePhi| on input is now a period, not a comma (the comma was used by accident by the author due to patriotism). \item |\ZeckIndices| and |\ZeckWord| both replace a negative argument by its absolute value, rather than returning an empty output as so far. \end{itemize} \textbf{New feature:} Macros to support Zeckendorf and Bergman representations using hexadecimal digits. For lack of time, the PDF doc is not updated, please use the interactive interface or check the source code for the macro names. Thanks to \textbf{Laurent Barme} for the feature request. \textbf{Bug fix:} After the |0.9c| transition from |\xintiieval| to |\xinteval|, the |$| and |$$| infix operators were broken with operands such as |(2+3)| in place of lone integers. \textbf{Improvements:} \begin{itemize} \item The |fibseq(a,b)| function can now be used also with |a------------------------------------------------------- %<*drv>----------------------------------------------------------- %% latex zeckendorf-doc.tex (thrice) && dvipdfmx zeckendorf-doc.dvi %% to produce zeckendorf-doc.pdf %% %% Doing latexmk -pdf zeckendorf-doc.tex is also possible but %% will produce a bigger file due usage of pdflatex. %% \NeedsTeXFormat{LaTeX2e}[2020/02/02] \ProvidesFile{zeckendorf-doc.tex}% [\zeckpackdate\space v\zeckversion\space driver file for % zeckendorf documentation (JFB)]% \PassOptionsToClass{a4paper,fontsize=11pt}{scrartcl} \RequirePackage{iftex} \chardef\Withdvipdfmx \iftutex0 \else\ifpdf0 \else1 \fi\fi \chardef\NoSourceCode 0 % replace 0 by 1 for not including source code \input zeckendorf.dtx %%% Local Variables: %%% mode: latex %%% End: %----------------------------------------------------------- %<*dtx> ^^@fi ^^@catcode`\ 0 \catcode0 12 \def\hiddenifs{\iftutex0 \else\ifpdf0 \else1 \fi\fi}% \chardef\noetex 0 \ifx\numexpr\undefined \chardef\noetex 1 \fi \ifnum\noetex=1 \chardef\extractfiles 0 % extract files, then stop \else \ifx\ProvidesFile\undefined \chardef\extractfiles 0 % no LaTeX2e; etex, ... on zeckendorf.dtx \else % latex/pdflatex on zeckendorf-doc.tex or on zeckendorf.dtx \NeedsTeXFormat{LaTeX2e}[2020/02/02]% \ProvidesFile{zeckendorf.dtx}[bundle source (\zeckpackdate)]% \ifx\Withdvipdfmx\undefined % latex run is on zeckendorf.dtx, we will extract all files \chardef\extractfiles 1 % 1 = extract and typeset, 2=only typeset \chardef\NoSourceCode 0 % 0 = include source code, 1 = do not \PassOptionsToClass{a4paper,fontsize=11pt}{scrartcl}% \RequirePackage{iftex}\relax \chardef\Withdvipdfmx \hiddenifs \else % latex run is on zeckendorf-doc.tex, \chardef\extractfiles 2 % no extractions \fi \fi \fi \ifnum\extractfiles<2 % we extract files \def\MessageDeFin{\newlinechar10 \let\Msg\message \Msg{^^J}% \Msg{********************************************************************^^J}% \Msg{*^^J}% \Msg{* To finish the installation you have to move the following^^J}% \Msg{* files into a directory searched by TeX:^^J}% \Msg{*^^J}% \Msg{*\space\space\space\space zeckendorf.sty^^J}% \Msg{*\space\space\space\space zeckendorf.tex^^J}% \Msg{*\space\space\space\space zeckendorfcore.tex^^J}% \Msg{*^^J}% \Msg{* Happy TeXing!^^J}% \Msg{*^^J}% }% \ifnum\extractfiles=0 % only extraction \expandafter\def\expandafter\MessageDeFin\expandafter{% \MessageDeFin \Msg{* To produce the documentation run latex thrice on zeckendorf-doc.tex^^J}% \Msg{* then dvipdfmx on zeckendorf-doc.dvi.^^J}% \Msg{*^^J}% \Msg{********************************************************************^^J}% }% \else %% *latex run on zeckendorf.dtx \ifnum\Withdvipdfmx=1 % for dvipdfmx \expandafter\def\expandafter\MessageDeFin\expandafter{% \MessageDeFin \Msg{* Please use dvipdfmx once the LaTeX runs are done, and^^J}% \Msg{* consider renaming zeckendorf.pdf to zeckendorf-doc.pdf^^J}% \Msg{********************************************************************^^J}% }% \else % pdflatex, lualatex or xelatex \expandafter\def\expandafter\MessageDeFin\expandafter{% \MessageDeFin \Msg{* Please consider renaming zeckendorf.pdf to zeckendorf-doc.pdf^^J}% \Msg{********************************************************************^^J}% }% \fi \fi % file extraction \begingroup \input docstrip.tex \askforoverwritefalse \generate{\nopreamble\nopostamble \file{README.md}{\from{zeckendorf.dtx}{readme}} \usepreamble\defaultpreamble \usepostamble\defaultpostamble \file{zeckendorfchanges.tex}{\from{zeckendorf.dtx}{changes}} \file{zeckendorf-doc.tex}{\from{zeckendorf.dtx}{drv}} \file{zeckendorfcore.tex}{\from{zeckendorf.dtx}{core}} \file{zeckendorf.tex}{\from{zeckendorf.dtx}{interactive}} \file{zeckendorf.sty}{\from{zeckendorf.dtx}{sty}}} \endgroup \else % *latex on zeckendorf-doc.tex we skipped extraction \ifnum\Withdvipdfmx=1 \def\MessageDeFin{\newlinechar10 \let\Msg\message \Msg{^^J}% too lazy to add the multipls spaces to make a frame \Msg{********************************************************************^^J}% \Msg{* Please use dvipdfmx once the LaTeX runs are done^^J}% \Msg{********************************************************************^^J}% }% \fi \fi \ifnum\extractfiles=0 % direct tex/etex/xetex on zeckendorf.dtx, files now extracted, stop \MessageDeFin\expandafter\end \fi \ifdefined\MessageDeFin\AtEndDocument{\MessageDeFin}\fi %------------------------------------------------------------------------------- \documentclass {scrartcl} %%% START OF CUSTOM doc.sty LOADING (May 21, 2022) \usepackage{doc}[=v2] % As explained above I was formerly using scrdoc hence ltxdoc indirectly. % Let's emulate here the little I appear to need from ltxdoc.cls and % srcdoc.cls. % \AtBeginDocument{\MakeShortVerb{\|}} \DeclareFontShape{OT1}{cmtt}{bx}{n}{<-> ssub * cmtt/m/n}{} \DeclareFontFamily{OMS}{cmtt}{\skewchar\font 48} % '60 \DeclareFontShape{OMS}{cmtt}{m}{n}{<-> ssub * cmsy/m/n}{} \DeclareFontShape{OMS}{cmtt}{bx}{n}{<-> ssub * cmsy/b/n}{} \DeclareFontShape{OT1}{cmss}{m}{it}{<->ssub*cmss/m/sl}{} \ifnum\NoSourceCode=1 \OnlyDescription \fi \CodelineNumbered \DisableCrossrefs % \setcounter{StandardModuleDepth}{1} % we don't use this mechanism currently \def\cmd#1{\cs{\expandafter\cmd@to@cs\string#1}} \def\cmd@to@cs#1#2{\char\number`#2\relax} % This is hyperref compatible (contrarily to using \bslash) % I need the \detokenize due to usage in implementation part with names % containing the underscore for example \DeclareRobustCommand\cs[1]{\texttt{\textbackslash\detokenize{#1}}} \providecommand\marg[1]{% {\ttfamily\char`\{}\meta{#1}{\ttfamily\char`\}}} \providecommand\oarg[1]{% {\ttfamily[}\meta{#1}{\ttfamily]}} \providecommand\parg[1]{% {\ttfamily(}\meta{#1}{\ttfamily)}} % There is very little we seem to need from the scrdoc extras: page geometry % is set by geometry package and a4paper option from zeckendorf.tex file. So it % seems I only need the hologo loading: \usepackage{hologo} \DeclareRobustCommand*{\eTeX}{\hologo{eTeX}}% %\DeclareRobustCommand*{\LuaTeX}{\hologo{LuaTeX}}% % %%% end of ltxdoc+srcdoc emulation. \ifnum\NoSourceCode=1 \OnlyDescription\fi \usepackage{ifpdf} \ifpdf\chardef\Withdvipdfmx 0 \fi \makeatletter \ifnum\Withdvipdfmx=1 % I think those are mostly obsolete... \@for\@tempa:=hyperref,bookmark,graphicx,xcolor,pict2e\do {\PassOptionsToPackage{dvipdfmx}\@tempa} % \PassOptionsToPackage{dvipdfm}{geometry} \PassOptionsToPackage{bookmarks=true}{hyperref} \PassOptionsToPackage{dvipdfmx-outline-open}{hyperref} %\PassOptionsToPackage{dvipdfmx-outline-open}{bookmark} % \def\pgfsysdriver{pgfsys-dvipdfm.def} \else \PassOptionsToPackage{bookmarks=true}{hyperref} \fi \makeatother \pagestyle{headings} \iftutex\else % but newtxtt requires it anyhow \usepackage[T1]{fontenc} \fi \usepackage[hscale=0.66,vscale=0.75]{geometry} \usepackage{amsmath} \usepackage{amssymb}% for \blacktriangleright utilisé par titled-frame \allowdisplaybreaks % requires newtxtt 1.05 or later \usepackage[zerostyle=d,scaled=0.95,straightquotes]{newtxtt} \renewcommand\familydefault\ttdefault \usepackage[noendash]{mathastext} \renewcommand\familydefault\sfdefault % pas le temps d'expliquer pourquoi \iftutex \catcode`⊙\active\def⊙{\ensuremath{\odot}} \else \DeclareUnicodeCharacter{2299}{\ensuremath{\odot}} \fi \usepackage{graphicx} \usepackage[dvipsnames,svgnames]{xcolor} \definecolor{joli}{RGB}{225,95,0} \definecolor{JOLI}{RGB}{225,95,0} \definecolor{BLUE}{RGB}{0,0,255} %% \definecolor{niceone}{RGB}{38,128,192}% utilisé avant pour urlcolor %% \colorlet{jfverbcolor}{yellow!5} \colorlet{shortverbcolor}{magenta}% 1.6 release NavyBlue RoyalPurple \colorlet{softwrapcolor}{blue} \colorlet{digitscolor}{olive}% 1.6 {OrangeRed} % transféré de xint-manual.tex (maintenant dans xint.dtx) \DeclareFontFamily{U}{MdSymbolC}{} \DeclareFontShape {U}{MdSymbolC}{m}{n}{<-> MdSymbolC-Regular}{} \makeatletter \newbox\cdbx@SoftWrapIcon \def\cdbx@SetSoftWrapBox{% \setbox\cdbx@SoftWrapIcon\hb@xt@\z@ {\hb@xt@\fontdimen2\font {\hss{\color{softwrapcolor}\usefont{U}{MdSymbolC}{m}{n}\char"97}\hss}% \hss}% } \makeatother \usepackage[english]{babel} \usepackage{hyperref} \hypersetup{% %linktoc=all,% breaklinks=true,% colorlinks=true,% urlcolor=Cerulean,% 1.6 ProcessBlue,%SkyBlue citecolor=[RGB]{120,29,126},% ACM Purple linkcolor=PineGreen,% pdfauthor={Jean-François Burnol},% pdftitle={The Zeckendorf package},% pdfsubject={Arithmetic with TeX},% pdfkeywords={Phi-expansion, arithmetic, TeX},% pdfstartview=FitH,% pdfpagemode=UseOutlines} \usepackage{bookmark} % importé de xint 2021/05/14 \newcommand\RaisedLabel[2][6]{% \vspace*{-#1\baselineskip}% \begingroup \let\leavevmode\relax\phantomsection \label{#2}% \endgroup \vspace*{#1\baselineskip}% } %---- \verb, and verbatim like `environments'. \MicroFont et \MacroFont \makeatletter \def\MicroFont {\ttfamily\cdbx@SetSoftWrapBox\color{shortverbcolor}} %\def\MicroFont {\ttfamily \color[named]{OrangeRed}\cdbx@SetSoftWrapBox } % \MacroFont est utilisé par macrocode, mais sa définition est figée dans % \macro@font au \begin{document} \def\MacroFont {\ttfamily \baselineskip12pt\relax } %--- November 4, 2014 making quotes straight. (maintenu pour *) % % there is no hook in \macrocode after \dospecials etc. Thus I will need to % take the risk that some future evolution of doc.sty (or perhaps scrdoc) % invalidates the following. % % Actually, I should not at all rely on the doc class, I should do it all by % myself. \def\macrocode{\macro@code \frenchspacing \@vobeyspaces \makestarlowast % \makequotesstraight (obsolète) \xmacro@code } %--- lower * symbol in text \def\lowast{\raisebox{-.25\height}{*}} \catcode`* \active \def\makestarlowast {\let*\lowast\catcode`\*\active}% \catcode`* 12 \def\verb #1% {% \relax\leavevmode\null \begingroup \MicroFont \def\@makeletter##1{\catcode`##1=11\relax}% % \scantokens will have a result of inserting a space after cs's. % hence the need to have the catcodes of things like _ right. % I also need < for > for code comments % \dospecials at begin document % \do\ \do\\\do\{\do\}\do\$\do\&\do\#\do\^\do\_\do\%\do\~\do\| \odef\dospecials{\dospecials\do\:\do\<\do\>\do\-\do\+}% % naturally won't work in footnotes though. % this code is truly not satisfying, but enough for my needs here. \catcode`\\ 11 \catcode`## 11 % I don't do \catcode`\% 11 to avoid surprises in code comments % if my |...| has a linebreak \def\@jfverb ##1#1{\let\do\@makeletter \dospecials % lowering a bit the * \makestarlowast %\let\do\do@noligs \verbatim@nolig@list % not needed here \@vobeyspaces\everyeof{\noexpand}% \expandafter\@@jfverb\scantokens{##1}\relax}% \@jfverb }% \def\@@jfverb #1{\ifcat\noexpand#1\relax\endgroup\@\else \nobreak\hskip0pt plus .5pt % added 2025/10/06 for Zeckendorf % only .5pt 2025/10/15 \discretionary{\copy\cdbx@SoftWrapIcon}{}{}% #1\expandafter\@@jfverb\fi } \makeatother % everbatim environment % ===================== % October 13-14, 2014 % Verbatim with an \everypar hook, mainly to have background color, followed by % execution of the contents (not limited by a group-scope) \makeatletter \colorlet{everbatimfgcolor}{DarkBlue} \colorlet{everbatimbgcolor}{Beige} \colorlet{everbatimxfgcolor}{Maroon} \def\everbatim {\s@everbatim\@everbatim} \@namedef{everbatim*}{\s@everbatim\@everbatimx} % Note: one can not use everbatim inside itself or everbatim* inside itself \def\s@everbatim {% \everbatimtop % put there size changes \topsep \z@skip \partopsep \z@skip \itemsep \z@skip \parsep \z@skip \parskip \z@skip \lineskip \z@skip \let\do\@makeother \dospecials \let\do\do@noligs \verbatim@nolig@list \makestarlowast \everbatimhook \trivlist \@topsepadd \z@skip \item\relax \leftskip \@totalleftmargin \rightskip \z@skip \parindent \z@ \parfillskip\@flushglue \parskip \z@skip \@@par \def\par{\leavevmode\null\@@par\pagebreak[1]}% \everypar\expandafter{\the\everypar \unpenalty \everbatimeverypar \everypar \expandafter{\the\everypar\everbatimeverypar}% }% \obeylines \@vobeyspaces } % 27 mai 2022, plus de \small \def\everbatimtop {\MacroFont }% \let\everbatimhook\empty \def\everbatimeverypar{\strut {\color{everbatimbgcolor}\vrule\@width\linewidth }% \kern-\linewidth \kern\everbatimindent } \def\everbatimindent {\z@}% voir plus loin atbegindocument \begingroup \lccode`X 13 \catcode`X \active \lccode`Y `* % this is because of \makestarlowast. % I have to think whether this is useful: obviously if I were to provide % everbatim and everbatim* in a package I wouldn't do that. \catcode`Y \active \catcode`| 0 \catcode`[ 1 \catcode`] 2 \catcode`* 12 \catcode`{ 12 \catcode`} 12 |catcode`\\ 12 |lowercase[|endgroup% both freezes catcodes and converts X to active ^^M |def|@everbatim #1X#2\end{everbatim}% [#2|end[everbatim]|everbatimbottom ] |def|@everbatimx #1X#2\end{everbatimY}]% {#2\end{everbatim*}% % No group here: this allows executed code to make macro % definitions which may reused in later uses of everbatim. % refactored 2022/01/11, rather than passing \newlinechar value % as was done formerly via everbatim* (see above) and fetching it here as #1 % it is thus assumed executed contents do not terminate a scope \edef\everbatimrestorenewlinechar{\newlinechar\the\newlinechar\relax}% \newlinechar 13 % refactored 2022/01/11 to fix a \parskip issue % attention, \parskip thus set to zero for execution of contents % reason: avoid extra space if everbatim* is in an \item of a list % between verbatim and output of execution, if it starts a paragraph % a \vskip-\parskip approach (cf former \everbatimundoparskip) % would be no good in case contents create a display \edef\everbatimrestoreparskip{\parskip\the\parskip\relax}% \parskip\z@skip % execution of the contents (expected to be LaTeX code...) \everbatimxprehook \scantokens {#2}% \everbatimrestorenewlinechar \everbatimrestoreparskip \everbatimxposthook % input after \end{everbatim*} on same line in source is allowed }% % L'espace venant du endofline final mis par \scantokens sera inhibé si #2 se % termine par un % ou un \x, etc... \let\everbatimbottom\empty \def\endeverbatim{\if@newlist \leavevmode\fi\endtrivlist} \@namedef{endeverbatim*}{\endeverbatim} \def\everbatimxprehook {\colorlet{everbsavedcolor}{.}% \color{everbatimxfgcolor}}% \def\everbatimxposthook{\color{everbsavedcolor}} {\sbox0{\color{everbatimxfgcolor}\xdef\@tempa{\current@color}}} \ifpdf \ifluatex \edef\everbatimxprehook {\pdfextension colorstack\noexpand\@pdfcolorstack push{\@tempa}\relax} \def\everbatimxposthook {\pdfextension colorstack\@pdfcolorstack pop\relax} \else \edef\everbatimxprehook {\pdfcolorstack\noexpand\@pdfcolorstack push{\@tempa}\relax} \def\everbatimxposthook{\pdfcolorstack\@pdfcolorstack pop\relax} \fi \else \ifxetex \edef\everbatimxprehook{\special{color push \@tempa}} \def\everbatimxposthook{\special{color pop}} \else \ifnum\Withdvipdfmx=1 \edef\everbatimxprehook{\special{color push \@tempa}} \def\everbatimxposthook{\special{color pop}} \fi \fi \fi \makeatother % 2025/10/05 % Toujours pénible de devoir se battre avec les espaces verticaux de LaTeX. % J'ai dû réexaminer la situation pour Zeckendorf, différent de bnumexpr % où j'utilise \[..\] pour les \bneshoweval etc... \AtBeginDocument{% % \message{ICI below: \the\belowdisplayskip^^J% % belowshort: \the\belowdisplayshortskip^^J% % above: \the\abovedisplayskip^^J% % aboveshor: \the\abovedisplayshortskip^^J% % }% % below: 11.0pt plus 3.0pt minus 6.0pt % belowshort: 6.5pt plus 3.5pt minus 3.0pt % above: 11.0pt plus 3.0pt minus 6.0pt % aboveshort: 0.0pt plus 3.0pt \belowdisplayskip\belowdisplayshortskip %\abovedisplayskip\belowdisplayskip } \usepackage{xspace} \def\zeckname {\href{http://www.ctan.org/pkg/zeckendorf}{zeckendorf}\xspace }% \def\zecknameimp {\texorpdfstring {\hyperref[part:zeckendorfcode]{{\color{shortverbcolor}\ttzfamily zeckendorf}}} {zeckendorf}% \xspace }% \def\zecknameuserdoc {\texorpdfstring {\hyperref[titlepage]{{\color{PineGreen}\ttzfamily zeckendorf}}} {zeckendorf}% \xspace }% \def\bnumexprname {\href{http://www.ctan.org/pkg/bnumexpr}{bnumexpr}\xspace }% \def\xintkernelname {\href{http://www.ctan.org/pkg/xint}{xintkernel}\xspace }% \def\xintcorename {\href{http://www.ctan.org/pkg/xint}{xintcore}\xspace }% \def\xintname {\href{http://www.ctan.org/pkg/xint}{xint}\xspace }% \def\xinttoolsname {\href{http://www.ctan.org/pkg/xint}{xinttools}\xspace }% \def\xintfracname {\href{http://www.ctan.org/pkg/xint}{xintfrac}\xspace }% \def\xintexprname {\href{http://www.ctan.org/pkg/xintexpr}{xintexpr}\xspace }% \def\xintbinhexname {\href{http://www.ctan.org/pkg/xint}{xintbinhex}\xspace }% \DeclareRobustCommand\ctanpackage[1]{\href{https://ctan.org/pkg/#1}{#1}} \usepackage{zeckendorf} \usepackage[nottoc,notlot,notlof,other]{tocbibind}\tocotherhead{subsection} \usepackage{etoc} \usepackage{centeredline} \usepackage[dvipsnames]{xcolor} \usepackage{colorframed} \colorlet{TFFrameColor}{teal!20} \colorlet{TFTitleColor}{purple} \colorlet{shadecolor}{lightgray!25} \makeatletter\let\originalcheck@percent\check@percent \let\check@percent\relax\makeatother \usepackage[autolanguage,np]{numprint} \usepackage{enumitem} \newenvironment{TeXnote}{\par\footnotesize\medskip\noindent\textbf{\TeX-nical note: }}{\par\medskip} \newcommand\dgts{\textcolor{digitscolor}} \newcommand\mdgts{\mathcolor{digitscolor}} \DeclareMathOperator{\NN}{\mathbf{N}} \DeclareMathOperator{\ZZ}{\mathbf{Z}} \DeclareMathOperator{\QQ}{\mathbf{Q}} \begin{document} \thispagestyle{empty} \ttzfamily \pdfbookmark[1]{Title page}{TOP} {% \normalfont\Large\parindent0pt \parfillskip 0pt\relax \leftskip 2cm plus 1fil \rightskip 2cm plus 1fil The \zeckname package\par } \RaisedLabel{titlepage} {\centering \textsc{Jean-François Burnol}\par \small jfbu (at) free (dot) fr\par Package version: \zeckversion\ (\zeckpackdate)\par {From source file \texttt{zeckendorf.dtx} of \texttt{\zeckdtxtimestamp}}\par } \begin{titled-frame}{Warning} \small This package is still in alpha stage. Any or all of the user interface may change in backwards incompatible ways at each release. Suggestions for new features are most welcome! \end{titled-frame} \etocsetnexttocdepth{subsubsection} \etocsettagdepth{code}{section} \etocsettocstyle{}{} \etocdefaultlines \makeatletter \etocsetstyle{subsubsection} {\addpenalty\@M\etocfontthree\vspace{\etocsepthree}% \begingroup\raggedright\noindent \etocskipfirstprefix} {, }% {\etocname} {.% \baselineskip\etocbaselinespreadthree\baselineskip \par\endgroup\addpenalty{-\@highpenalty}} \makeatother \tableofcontents \section{Mathematical background} \label{sec:math} Let us recall that the Fibonacci sequence starts with $F_0=0$, $F_1=1$, and obeys the recurrence $F_n=F_{n-1} + F_{n-2}$ for $n\geq2$. So $F_2=1$, $F_3=2$, $F_4=3$ and by a simple induction $F_k = k-1$. Ahem, not at all! Here are the first few, starting at $F_2=1$: \[ \xinteval{*fibseq(2, 18)}\dots \] The ratios of consecutive Fibonacci numbers are the convergents of the golden ratio $\phi$. \[\phi = \xintDigits:=32;\frac{1+sqrt(5)}2\approx \np{\xintfloateval[-2]{(1+sqrt(5))/2}}.\] The Fibonacci recurrence can also be prolungated to negative $n$'s, and it turns out that $F_{-n}=(-1)^{n-1}F_n$. Let us a give a few equations which are constantly in use. The first one implies explicitly, in particular, that $\ZZ[\phi]$ (i.e. all polynomial expression in $\phi$ with integer coefficients) is $\ZZ + \ZZ\phi$. \begin{equation} \label{eq:phik} \forall n\in\ZZ\quad \phi^n = F_{n-1} + F_n \phi\,. \end{equation} Applying the $\phi\leftrightarrow\psi=-\phi^{-1}=1-\phi$ automorphism of the ring $\ZZ[\phi]$ and adding we obtain the \textbf{Lucas} numbers: \begin{equation} \label{eq:lucas} L_n = \phi^n + \psi^n = 2 F_{n-1} + F_n = F_{n-1} + F_{n+1}\,. \end{equation} If subtracting, we obtain the \textbf{Binet} formula: \begin{equation} \label{eq:binet} F_n = \frac{\phi^n - \psi^n}{\phi - \psi}\;. \end{equation} Of course one should always keep in mind that $-1<\psi<0$. And perhaps also that $\phi-\psi=\sqrt5$. Finally, there is an important formula using $2\times2$-matrices, closely related with equation \eqref{eq:phik} and the recurrence relation of the Fibonacci numbers: \begin{equation} \label{eq:matrix} \forall n\in\ZZ\quad \begin{pmatrix} 1&1\\1&0 \end{pmatrix}^n = \begin{pmatrix} F_{n+1}&F_n\\F_n&F_{n-1} \end{pmatrix}. \end{equation} \def\testN{100000000} \oodef\testword{\ZeckWord{\testN}} \oodef\testmaxindex{\ZeckMaxK{\testN}} \ifnum\xintNthElt{0}{\testword}=\numexpr\testmaxindex-1\relax\else\ERROR\fi \textbf{Zeckendorf}'s Theorem (\textbf{Lekkerkerker}'s \cite{LEKK} in 1952 (preprint 1951) attributes the result to Zeckendorf; Zeckendorf, who was not in academia, published \cite{ZECK} only later in 1972) says that any positive integer has a unique representation as a sum of the Fibonacci numbers $F_n$, $n\geq2$, under the conditions that no two indices differ by one, and that no index is repeated. For example: \begin{align*} \xintFor #1 in {10,100,1000,10000}\do{\xdef\tmpcsv{\ZeckIndices{#1}}% \np{#1} &= {\def\ZeckPrintOne{\ZeckTheFN}\mdgts{\ZeckPrintIndexedSum{\tmpcsv}}} = \mdgts{\ZeckPrintIndexedSum{\tmpcsv}}\\ } \xintFor #1 in {100000, 1000000, 10000000}\do{\xdef\tmpcsv{\ZeckIndices{#1}}% \np{#1} &= \def\ZeckPrintOne{\ZeckTheFN} \mdgts{\ZeckPrintIndexedSum{\tmpcsv}}\\ &= \mdgts{\ZeckPrintIndexedSum{\tmpcsv}}\\ } \np{\testN} &= \mdgts{\ZeckPrintIndexedSum{\ZeckIndices{\testN}}} \end{align*} This is called the Zeckendorf representation, and it can be given either as above, or as the list of the indices (in decreasing or increasing order), or as a binary word which in the examples above are % \begin{align*} \xintFor #1 in {10,100,1000,10000,100000, 1000000, 10000000}\do{ \np{#1} &= \mdgts{\ZeckWord{#1}_{\text{zeck}}}\\ } \np{\testN} &= \mdgts{\ZeckWord{\testN}_{\text{zeck}}}\\ \np{1000000000} &= \mdgts{\ZeckWord{1000000000}_{\text{zeck}}} \end{align*} % The least significant digit says whether the Zeckendorf representation uses $F_2$ and so on from right to left (one may prefer to put the binary digits in the reverse order, but doing as above is more reminiscent of binary, decimal, or other representations using a given radix). % In the next-to-last example the word % length is $\testmaxindex-2+1=\mdgts{\the\numexpr\testmaxindex-1\relax}$, and in % general it is $K-1$ where $K$ is the largest index such that $F_K$ is at most % equal to the given positive integer. For $\np{1000000000}$ this maximal index is % \dgts{\ZeckMaxK{1000000000}} and indeed the associated word has length % \dgts{\expanded{\noexpand\xintLength{\ZeckWord{1000000000}}}}. \ifnum\ZeckMaxK{1000000000}=\numexpr \expanded{\noexpand\xintLength{\ZeckWord{1000000000}}} +1\relax\else \ERROR\fi In a Zeckendorf binary word the sub-word \dgts{11} never occurs, and this, combined wih the fact that the leading digit is \dgts{1}, characterizes the Zeckendorf words. \textbf{Donald Knuth} (whose name may ring some bells to \TeX{} users) has defined in 1988 a \textbf{Fibonacci multiplication} (\cite{KNUTHFIBMUL}) of positive integers via the formula \begin{equation}\label{eq:kmul} a \circ b = \sum_{i,j} F_{a_i+b_j}\,, \end{equation} where $a = \sum F_{a_i}$ and $b = \sum F_{b_j}$ are the Zeckendorf representations of the positive integers $a$ and $b$. Although it is sometimes true that formula \eqref{eq:kmul} remains valid when using non-Zeckendorf expressions of $a$ and/or $b$ as sums of Fibonacci numbers, this is not a general rule. The next identity by Knuth, which applies whenever three positive integers $a$, $b$, $c$ are expressed via their Zeckendorf representations, is thus non-trivial: \begin{equation}\label{eq:kassoc} (a \circ b) \circ c = \sum_{i,j,k} F_{a_i+b_j+c_k}\,. \end{equation} From it, the associativity of the Fibonacci multiplication follows immediately, the same as commutativity followed immediately from \eqref{eq:kmul}. Knuth's proof is combinatorial in nature. \textbf{Pierre Arnoux} (\cite{ARNOUX}) obtained in 1989 a non-combinatorial proof of associativity based upon the identification of a certain subset (or subsets) of the ring $\ZZ[\phi]$, closed under multiplication, and indexed by the positive integers. The circle-product on the indices is mapped to the standard multiplication of these algebraic integers $A_n$: $A_nA_m=A_{n\circ m}$. As by-product of this, he obtained the following remarkable alternative formula for the Knuth product: \begin{equation} \label{eq:arnoux} a \circ b = ab + a\sum_j F_{b_j - 1} + b \sum_i F_{a_i - 1}\;. \end{equation} Again, here we use the Zeckendorf representations of the positive integers $a$ and $b$. Clearly formula \eqref{eq:arnoux} is advantageous numerically compared to original definition \eqref{eq:kmul}. Arnoux also re-interpreted a ``star-product'' which had been defined by \textbf{Horacio Porta} and \textbf{Kenneth Stolarsky} (\cite{PORTSTOL}). % If we extend the notion of Zeckendorf representation to all non-negative % integers by deciding that $0$ is represented by $F_0$ (chosen in preference to % the empty sum!), then both equations \eqref{eq:kmul} and \eqref{eq:arnoux} % make $0$ act as the identity. \textbf{Donald Knuth} (see \cite[7.1.3]{TAOCP4A}) has shown that any relative integer has a unique representation as a sum of the ``NegaFibonacci'' numbers $F_{-n}$, $n\geq1$, again with the condition that no index is repeated and no two indices differ by one. In the special case of zero, the representation is an empty sum. Here is the sequence of these ``NegaFibonacci'' numbers starting at $n=-1$: \[ \xinteval{*fibseq(-1,-16)}\dots \] In 1957, the twelve-year-old \textbf{George Bergman} (\cite{BERG}) introduced the notion of a ``base $\phi$'' number system. This uses $0$ and $1$ as digits but with the ambiguity rule $011\leftrightarrow100$ due to $\phi^2=\phi+1$. He proved that any positive integer can be represented this way finitely, i.e.\@ is a \emph{finite} sum of powers $\phi^k$, with decreasing relative integers as exponents (i.e.\@ each power occurring at most once and it is crucial that negative powers are allowed). For example: \[ 100 = \PhiPrintIndexedSum{\PhiExponents{100}} %\phi^9 + \phi^6 + \phi^3 + \phi + \phi^{-4} + \phi^{-7} + \phi^{-10} = \PhiBasePhi{100}_\phi\,. %1001001010.0001001001_\phi\,. \] % Such a finite ``phi-ary'' representation (it seems ``phi-representation'' is the more commonly used term in academia) is unique if one adds the condition that no two exponents differ by one. This is equivalent to requiring that the number of terms is minimal. The real numbers which can be represented by such finite sums are exactly the positive numbers in $\ZZ[\phi]$, i.e. all combinations $p + q \phi$ with $p$ and $q$ relative integers which turn out to be strictly positive. \[100 - 30\phi = \PhiPrintIndexedSum{\PhiExponents{{100}{-30}}} = \PhiBasePhi{{100}{-30}}_\phi\,. \] The naive approach to obtain the finite phi-representations, and actually prove that they do exist for all positive integers, is to show how to repeatedly add $1$ (hence also powers of $\phi$). One then only needs to explain how to subtract $1$ (hence also powers of $\phi$) to deduce that all $p+q\phi>0$ are representable. This is actually what Bergman did. If one wants, as we do, to be able to obtain the representations for integers having say more than a few decimal digits, this theoretical approach is simply not feasible as is, one needs a bit more thinking. A theoretical way, called the ``greedy'' algorithm, is based upon the fact that for any $x = p+q\phi >0$, the maximal exponent $k\in\ZZ$ in its minimal representation is characterized by $\phi^k\leq x<\phi^{k+1}$. So one only needs to get $k$ and then replace $x$ by $x-\phi^k$. Doing this using floating point number calculations will only be able to handle integers with few enough digits to be exactly representable, and may lead at some point to a negative $x$, hence fail, due to rounding errors. So here again one has to think a bit. This has been done by the author, and the resulting algorithm is implemented (expandably) here in \eTeX{}.% % \footnote{As the intrepid reader will see if looking at the code, this uses a little bit floating point logarithms witn mantissas of eight decimal digits. This is because we have arbitrary precision logarithm available from \xintexprname, with the fastest being with eight decimal digits precision, and after all we were not preparing a reference paper for \emph{Mathematics of Computation} but simply aiming at computing for fun as efficiently as we could using tools at our disposal. This shortcut induces a theoretical upper bound on the size of the starting $x$: if it is an integer it must have less than say about one million decimal digits (see \autoref{sssec:maxe} for details). As we can do computations (with \TeX Live 2025 default memory settings) only up to about |13000| decimal digits (and in reasonable time up to less than |1000| digits), this is not a problem to us. And if we were to use logarithms with about 16 decimal digits of precision, the theoretical limit would raise to say inputs of less than about $10^{14}$ decimal digits. Each of our decimal digit occupies one word of computer memory, and even if we were using a programming language manipulating binary numbers, we would need more than $37$ terabytes of computer memory to store the binary representation of (one less than) $10$ to the power $10^{14}$, so using double precision floats (which are close to having 16 decimal digits of precision) is largely enough to cover real-life cases. Nevertheless, in the |0.9d| code comments we briefly describe how we could proceed all the way using only integer arithmetic with no theoretical limit on input size. See \autoref{sssec:maxe}. Similar remarks apply to Zeckendorf representations.% } % Of course this is only elementary mathematics and it would be extremely surprising if the algorithm was not in the literature. Inputs of hundreds of digits are successfully handled. The same, implemented in C or other language with a library for big integers, would of course go way beyond and be a thousand times faster. An ``integer-only'' algorithm (i.e.\@ an algorithm which can be made to process only integers, but is in fact restricted to them; to compare, the approach described in the previous paragraph is in principle also implementable using integers only, but it applies to all $x=p+q\phi>0$ not only to integers) to obtain the Bergman minimal $\phi$-representation of a positive integer $N$ is explained by \textbf{Donald Knuth} in the solution to Problem 35 of section 1.2.8 from \cite{TAOCP1} (there is a typographical error with a missing negative sign in an exponent there, on page 495; this has been reported to the author). It starts with the position of $N$ with respect to Lucas numbers, the more subtle case being when $N$ follows an odd indexed Lucas number. One has to think a bit how to find efficiently the largest Lucas number at most equal to $N$, when $N$ has hundreds of digits. This is about the same as identifying the maximal $k$ such as $\phi^k\leq N$, as $\phi^k + (-1)^k \phi^{-k} = L_k$ is an integer. It is also very similar to finding the Zeckendorf maximal index which essentially means to locate $\sqrt{5}N$ with respect to powers of $\phi$ (as $\phi^k - (-1)^k \phi^{-k}$ for $k\geq1$ belongs to $\sqrt5 \NN$). For $x=N$ an \textbf{integer} (at least $2$) it can be proven that the smallest contribution $\phi^{-\ell}$ to the minimal Bergman representation is with $\ell=k$ if $k$ is even and $\ell=k+1$ is $k$ is odd. Otherwise stated $\ell$ is the smallest even integer at least equal to $k$. (So we can always find the location of the radix separator if we had lost it). \textbf{Christiane Frougny} and \textbf{Jacques Sakarovitch} (\cite{FROUSAKA}) showed that there exists a (non explicited) finite two-tape automaton which converts the Zeckendorf expansion of a positive integer into the Bergman representation (where the part with negative exponents is ``folded'' across the radix point to sit on top (or below) the part with positive exponents). Very recently \textbf{Jeffrey Shallit} (\cite{SHAL}) has revisited this topic and constructed explicitly a Frougny-Sakarovitch automaton. \iffalse \begin{equation} \label{eq:phipowers} \forall n\in\ZZ\quad \phi^n = F_{n-1} + F_n \phi\,, \end{equation} observe that the above expression of $100$ is equivalent to the two integer only relations: \begin{align*} \xinteval{add(fib(n-1), n = 9,6,3,1,-4,-7,-10)} &=F_8 + F_5 + F_2 + F_0 + F_{-5} + F_{-8} + F_{-11}\,, \\ \ZeckNFromIndices{9,6,3,1,-4,-7,-10} &= F_9 + F_6 + F_3 + F_1 + F_{-4} + F_{-7} + F_{-10}\,. \end{align*} Due to \textbf{Binet}'s formulas, the last relation expresses that $\phi^9 + \phi^6 + \phi^3 + \phi^1 + \phi^{-4} + \phi^{-7} + \phi^{-10}$ is invariant by the automorphism $\phi\mapsto -\phi^{-1}=1-\phi$ of $\ZZ[\phi]$. This confirms that it has to be an integer. \fi %\bibliographystyle{unsrt} %\bibliography{zeckendorf} \begin{thebibliography}{10} \bibitem{LEKK} C.~G. Lekkerkerker. \newblock Voorstelling van natuurlijke getallen door een som van getallen van {F}ibonacci. \newblock {\em Simon Stevin}, 29:190--195, 1951-1952. \bibitem{ZECK} E.~Zeckendorf. \newblock Repr\'esentation des nombres naturels par une somme de nombres de {F}ibonacci ou de nombres de {L}ucas. \newblock {\em Bull. Soc. Roy. Sci. Li\`ege}, 41:179--182, 1972. \bibitem{KNUTHFIBMUL} Donald~E. Knuth. \newblock Fibonacci multiplication. \newblock {\em Appl. Math. Lett.}, 1(1):57--60, 1988. \bibitem{ARNOUX} Pierre Arnoux. \newblock Some remarks about {F}ibonacci multiplication. \newblock {\em Appl. Math. Lett.}, 2(4):319--320, 1989. \bibitem{PORTSTOL} H.~Porta and K.~B. Stolarsky. \newblock The edge of a golden semigroup. \newblock In {\em Number theory, {V}ol.\ {I} ({B}udapest, 1987)}, volume~51 of {\em Colloq. Math. Soc. J\'anos Bolyai}, pages 465--471. North-Holland, Amsterdam, 1990. \bibitem{TAOCP4A} Donald~E. Knuth. \newblock {\em The art of computer programming. {V}ol. 4{A}. {C}ombinatorial algorithms. {P}art 1}. \newblock Addison-Wesley, Upper Saddle River, NJ, 2011. \bibitem{BERG} George Bergman. \newblock A number system with an irrational base. \newblock {\em Math. Mag.}, 31:98--110, 1957/58. \bibitem{TAOCP1} Donald~E. Knuth. \newblock {\em The art of computer programming. {V}ol. 1}. \newblock Addison-Wesley, Reading, MA, third edition, 1997. \newblock Fundamental algorithms. \bibitem{FROUSAKA} Christiane Frougny and Jacques Sakarovitch. \newblock Automatic conversion from {F}ibonacci representation to representation in base {$\phi$}, and a generalization. \newblock volume~9, pages 351--384. 1999. \newblock Dedicated to the memory of Marcel-Paul Sch\"utzenberger. \bibitem{SHAL} Jeffrey Shallit. \newblock Proving properties of {$\varphi$}-representations with the \texttt{{W}alnut} theorem-prover. \newblock {\em Commun. Math.}, 33(2):Paper No. 3, 33, 2025. \end{thebibliography} \part{User manual} \section{Use on the command line} \label{sec:interactive} Open a command line window and execute: \centeredline{|etex zeckendorf|} then follow the displayed instructions. The (\TeX{} Live) |*tex| executables are not linked with the |readline| library, and this makes interactive use quite painful. If you are on a decent system, launch the interactive session rather via \centeredline{|rlwrap etex zeckendorf|} for a smoother experience. \section{The core package features} \label{sec:macros} \subsection{Algebra in \texorpdfstring{$\QQ(\phi)$}{Q(phi)}, extensions to the \cs{xinteval} syntax} \label{ssec:xinteval} The |\xinteval| syntax is extended in the following manner: \begin{enumerate} \item Bracketed pairs |[a, b]| represent $a + b\phi$, where $\phi$ is the golden ratio, and one can operate on them with |+|, |-| (also as prefix unary operator), |*|, |/|, and |^| (or |**|) to do additions, subtractions, multiplications, divisions and powers with integer exponents. So |a| and |b| can be rational numbers and are not limited to integers for these computations. |phi| stands for |[0,1]| and its conjugate |psi = [1, -1]| is defined also. One can use on input |a + b phi|, which on output will be printed as |[a, b]|. \textit{\textbf{DO NOT USE |\phi| OR |\psi|...} except if redefined to expand to the letters |phi| and |psi| but this not recommended...}! \begin{everbatim*} \xinteval{phi^50, psi^50, phi^50 * psi^50} \end{everbatim*} \begin{everbatim*} \xinteval{(1+phi)(10-7phi)(3+phi)/(2+phi)^3} \end{everbatim*} \begin{everbatim*} \xinteval{add(phi^n, n = -4,-7,-10, 1, 3, 6, 9)} \end{everbatim*} \begin{everbatim*} \xinteval{phi^20 / phi^10} \end{everbatim*} \begin{TeXnote} When dividing, and except if both operands are scalars, the coefficients of the result are reduced to their smallest terms; but for scalar-only division, one needs to use the |reduce()| function explicitly. The |[0, 0]| acts as |0| in operations, but is not automatically replaced by it, if produced by a subtraction for example. It is not allowed as an exponent for powers. \end{TeXnote} \item The functions |phisign()|, |phiabs()|, |phinorm()|, |phiconj()| do what one expects. \begin{snugshade} \textbf{Attention:} |\xinteval| functions are always used with parentheses, not with curly braces, contrarily to macros! \end{snugshade} \begin{everbatim*} \xinteval{phisign(10000 - 6180 phi)} \end{everbatim*} \begin{everbatim*} \xinteval{phisign(10000 - 6181 phi)} \end{everbatim*} \begin{everbatim*} \xinteval{phiabs(10000 - 6181 phi)} \end{everbatim*} \begin{everbatim*} \xinteval{phinorm(10000 - 6180 phi)} \end{everbatim*} \begin{everbatim*} \xinteval{(10000 - 6180 phi) * phiconj(10000 - 6180 phi)} \end{everbatim*} \item The function |fib()| computes the Fibonacci numbers (also for negative indices), and |fibseq(a,b)| will compute a consecutive stretch of them from index |a| to index |b| (one may also have |b=a|, or |b | 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 "author-maintained". The Author and Maintainer of this Work is Jean-François Burnol. This Work consists of the main source file and its derived files zeckendorf.dtx, zeckendorfcore.tex, zeckendorf.tex, zeckendorf.sty, README.md, zeckendorf-doc.tex, zeckendorf-doc.pdf \end{verbatim} \StopEventually{\end{document}\endinput} \makeatletter\let\check@percent\originalcheck@percent\makeatother % provisoire \def\csh{\cs} % workaround % https://github.com/latex3/latex2e/issues/563 \makeatletter \AddToHook{env/macrocode/begin}{\partopsep0pt\relax} \AddToHook{env/macrocode/after}{\@nobreakfalse} \makeatother \newgeometry{hscale=0.75,vscale=0.75}% ATTENTION \newgeometry fait % un reset de vscale si on ne le % précise pas ici !!! \MakePercentIgnore \AddToHook{env/macrocode/begin}{\bfseries} % % \catcode`\<=0 \catcode`\>=11 \catcode`\*=11 \catcode`\/=11 % \let\relax % \def<*core>{\catcode`\<=12 \catcode`\>=12 \catcode`\*=12 \catcode`\/=12 } % %<*core> % \etocdepthtag.toc{code} % \part{Commented source code} % \localtableofcontents % % \label{part:zeckendorfcode} % \etocmarkbothnouc{\zecknameuserdoc \hyperref[sec:zeckendorfcode]{implementation}} % \section{Core code} % \label{sec:code:core} % \etocignoredepthtags % \etocsetnexttocdepth{subsubsection} % \localtableofcontents % % Extracts to |zeckendorfcore.tex|. % % A general remark is that expandable macros (usually) \emph{f}-expand their % arguments, and most are \emph{f}-expandable. Usually, the CamelCase macro % (with neither |@| nor |_| in their names) % expands to either |\romannumeral0| or |\expanded| followed with a lowercase % macro. Macros destined to be used in typesetting context usually omit any % such construct and may require \emph{x}-expansion. They remain fully % expandable as long as some user level customization (for example for the % radix separator) has not injected things not compatible with an |\edef|. % % For variety we use here sometimes |@| in macro names, whereas \xintname uses % only |_| (and sometimes some other a priori non-letter characters). % % \subsection{Loading \xintexprname and setting catcodes} % \begin{macrocode} \input xintexpr.sty \input xintbinhex.sty \wlog{Package: zeckendorfcore 2025/11/16 v0.9d (JFB)}% \edef\ZECKrestorecatcodes{\XINTrestorecatcodes}% \def\ZECKrestorecatcodesendinput{\ZECKrestorecatcodes\endinput}% \XINTsetcatcodes% % \end{macrocode} % Small helpers related to \cs{expanded}-based methods. But the package only % has a few macros and these helpers are used only once or twice, most macros % using own custom terminators adapted to their own optimizations. % \begin{macrocode} \def\zeck_done#1\xint:{\iffalse{\fi}}% \let\zeck_abort\zeck_done % \end{macrocode} % \subsection{Fibonacci numbers} % \subsubsection{\csh{ZeckTheFN}} % The multiplicative algorithm is as in the \bnumexprname manual (at % |1.7b|), or since about ten years in the \xintname manual (at |1.4o| or % earlier) but termination is different and simply leaves |{F_n}{F_{n-1}}| % in input stream. We do not use % |\csname...\endcsname| branching here, for variety. Also, we replaced % usage of chained expressions handled via |\xintiiexpro| with direct usage of % the \xintcorename macros, for optimized efficiency, and taking into account % that |\expanded| now helps doing this without intermediate step. % % |\Zeck@FPair| and |\Zeck@@FPair| are not public interface. The former is % allows a negative or zero argument, the latter is positive only. % \begin{macrocode} \def\Zeck@FPair#1{\expandafter\zeck@fpair\the\numexpr #1.}% \def\zeck@fpair #1{% \xint_UDzerominusfork #1-\zeck@fpair_n 0#1\zeck@fpair_n 0-\zeck@fpair_p \krof #1% }% \def\zeck@fpair_p #1.{\Zeck@@FPair{#1}}% \def\zeck@fpair_n #1.{% \ifodd#1 \expandafter\zeck@fpair_ei\else\expandafter\zeck@fpair_eii\fi \romannumeral`&&@\Zeck@@FPair{1-#1}% }% \def\zeck@fpair_ei{\expandafter\zeck@fpair_fi}% \def\zeck@fpair_eii{\expandafter\zeck@fpair_fii}% \def\zeck@fpair_fi#1#2{\expanded{{#2}{\XINT_Opp#1}}}% \def\zeck@fpair_fii#1#2{\expanded{{\XINT_Opp#2}{#1}}}% \def\Zeck@@FPair#1{% \expandafter\zeck@@fpair@start \romannumeral0\xintdectobin{\the\numexpr#1\relax};% }% % \end{macrocode} % Inlining here at start the |\zeck@@fpair@again| because we don't % want the |\expandafter|'s here, due to current |\XINTfstop| definition. % \begin{macrocode} \def\zeck@@fpair@start 1#1{% \xint_gob_til_sc#1\zeck@@fpair@done;% \xint_UDzerofork #1\zeck@@fpair@zero 0\zeck@@fpair@one \krof {1}{0}% }% % \end{macrocode} % Prior to |0.9d| we were using coding like this, as it has been easier to use % expressions (the \xintname documentation had such code for more than ten % years, precisely to illustrate chaining of expressions, and at a time when % |\expanded| was not available, and of course we simply took it over initially). %\begin{verbatim} % \romannumeral0\xintiiexpro (#1+2*#2)*#1\expandafter\relax\expandafter;% % \romannumeral0\xintiiexpro #1*#1+#2*#2\relax;% %\end{verbatim} % or %\begin{verbatim} % \romannumeral0\xintiiexpro 2*(#1+#2)*#1+#2*#2\expandafter\relax\expandafter;% % \romannumeral0\xintiiexpro (#1+2*#2)*#1\relax;% %\end{verbatim} % At |0.9d| we go directly to the \xintcorename core macros for optimal % efficiency. This required a few adjustments elsewhere and the removal from % code comments of some technical discusions about |\xintthe|. The also % dropped semi-colons as we are already using braces. % \begin{macrocode} \def\zeck@@fpair@zero #1#2#3{% \zeck@@fpair@again#3% \expanded{% {\xintiiMul{#1}{\xintiiAdd{#1}{\xintDouble{#2}}}}% {\xintiiAdd{\xintiiSqr{#1}}{\xintiiSqr{#2}}}% }% }% \def\zeck@@fpair@one #1#2#3{% \zeck@@fpair@again#3% \expanded{% {\xintiiAdd{\xintDouble{\xintiiMul{\xintiiAdd{#1}{#2}}{#1}}}% {\xintiiSqr{#2}}}% {\xintiiMul{#1}{\xintiiAdd{#1}{\xintDouble{#2}}}}% }% }% \def\zeck@@fpair@again#1{% \xint_gob_til_sc#1\zeck@@fpair@done;% \xint_UDzerofork #1{\expandafter\zeck@@fpair@zero}% 0{\expandafter\zeck@@fpair@one}% \krof }% \def\zeck@@fpair@done#1\krof{}% % \end{macrocode} % For individual Fibonacci numbers, we have non public |\Zeck@@FN| which only % works on positive input and has a braced output. We also have non-public % |\Zeck@FN| and |\Zeck@FNminusOne| which accept negative input, and whose % output is also braced. And we have public |\ZeckTheFN| which accepts % negative input and whose output is not braced. % % The reason for strange name |\ZeckTheFN| is that originally |\Zeck@FPair| % produced its output using a special \xintexprname format, which needs to be % prefixed with |\xintthe| to get resolved into only digits. We have now % modified the structure by-pass this but the name sticks. % \begin{macrocode} \def\zeck@bracedfirstoftwo #1#2{{#1}}% \def\zeck@bracedsecondoftwo #1#2{{#2}}% \def\Zeck@FN{% \expandafter\zeck@bracedfirstoftwo\romannumeral`&&@\Zeck@FPair }% \def\Zeck@FNminusOne{% \expandafter\zeck@bracedsecondoftwo\romannumeral`&&@\Zeck@FPair }% \def\ZeckTheFN{\expandafter\xint_firstoftwo\romannumeral`&&@\Zeck@FPair}% \def\Zeck@@FN{\expandafter\zeck@bracedfirstoftwo\romannumeral`&&@\Zeck@@FPair}% % \end{macrocode} % \subsubsection{\csh{ZeckTheFSeq}} % The computation of stretches of Fibonacci numbers is not needed for the % package, but is provided for user convenience. This is lifted from the % development version of the |\xintname| user manual, which refactored a bit % the code which has been there for over ten years. % % The two arguments may be negative, and since |0.9d| they do not have to be % ordered. % \begin{macrocode} \def\ZeckTheFSeq#1#2{% \expanded\bgroup\expandafter\zeckthefseq_a \the\numexpr #1\expandafter.\the\numexpr #2.% }% \def\zeckthefseq_a#1.#2.{\expandafter\zeckthefseq_b\the\numexpr#2-#1.#1.}% \def\zeckthefseq_b #1{% \xint_UDzerominusfork 0#1\zeckthefseq_n #1-\zeckthefseq_one 0-\zeckthefseq_p \krof #1% }% \def\zeckthefseq_one0.#1.{{\ZeckTheFN{#1}}\iffalse{\fi}}% % \end{macrocode} % The |#1+1| is because |\Zeck@FPair{N}| expands to |{F_{N}}{F_{N-1}}|, so % here we will have |F_{A+1};F_{A};| as starting point. We want up to % |F_B|. If |B=A+1| there will be nothing more to do. % \begin{macrocode} \def\zeckthefseq_p #1.#2.{% \expandafter\zeckthefseq_loop \the\numexpr #1-1\expandafter.% \romannumeral`&&@\expandafter\zeck@sep@with@sc \romannumeral`&&@\Zeck@FPair{#2+1}\xintiiadd }% \def\zeck@sep@with@sc #1#2{#1;#2;}% % \end{macrocode} % We will have |F_{A-1};F_{A};| as starting point. We want down to % |F_B|. If |B=A-1| there will be nothing more to do. % \begin{macrocode} \def\zeckthefseq_n -#1.#2.{% \expandafter\zeckthefseq_loop \the\numexpr #1-1\expandafter.% \romannumeral`&&@\expandafter\zeck@exch@with@sc \romannumeral`&&@\Zeck@FPair{#2}\xintiisub }% \def\zeck@exch@with@sc #1#2{#2;#1;}% % \end{macrocode} % Now leave in stream one (braced) number, and test if we have reached B and % until then apply standard Fibonacci recursion. This is all done using a % single looping macro, only termination branches to another one. % % We add a bit sub-optimality in having one single macro handling both % increasing and decreasing indices. % \begin{macrocode} \def\zeckthefseq_loop #1.#2;#3;#4{% {#3}\ifnum #1=\z@ \expandafter\zeckthefseq_end\fi \expandafter\zeckthefseq_loop\the\numexpr #1-1\expandafter.% \romannumeral0#4{#3}{#2};#2;#4% }% \def\zeckthefseq_end#1;#2;#3{{#2}\iffalse{\fi}}% % \end{macrocode} % \subsection{Zeckendorf representation} % \subsubsection{\csh{ZeckNearIndex}, \csh{ZeckMaxK}} % % If the ratio of logarithms $\log(\sqrt5 x)/\log\phi$ was the exact % mathematical value it would be certain (via rough estimates valid at least % for say $x\geq10$, and even smaller, but anyhow we can check manually it % does work) that its integer rounding gives an integer |K| such that either % |K| or |K-1| is the largest index |J| with $F_J\leq x$. But the computation % is done with only about 8 decimal digits of precision. So this assumption % fails certainly for $x$ having more than one hundred million decimal digits, % and quite probably with an input having ten million digits, as we do % not want to exceed $\phi^{10^7}/\sqrt5\approx\np{1.13e2089876}$. But with % one million decimal digits we are safe (see \autoref{sssec:maxe} for % related comments). % % As anyhow \xintname can handle multiplications only with operands of about % up to \dgts{13000} digits (with \TeX Live 2025 default memory settings), and % computation times limit reasonable inputs to less than \dgts{1000} digits, % there is no worry for us. % % \xintfracname's |\xintiRound{0}| is guaranteed to round correctly the input % it has been given. This input is some approximation to an exact theoretical % value involving ratio of logarithms (and square root of \dgts{5}). Prior to % rounding the computed numerical approximation, we are close to the exact % theoretical value, where ``close'' means we expect to have about \dgts{8} % leading digits in common (and we have already limited our scope so that we % are talking about a value quite less than \dgts{100000} at any rate). If % the computed rounding differs from the exact rounding of the exact value it % must be that argument $x$ is about mid-way (in log scale) between two % consecutive Fibonacci numbers. The conclusion is that the integer we obtain % after rounding can not be anything else than either |J| or |J+1|. % % The argument is more subtle than it looks. The conclusion is important to % us as it means we do not have to add extraneous checks involving computation % of one or more additional Fibonacci numbers. % % The formula with macros was obtained via an |\xintdeffloatfunc| and % |\xintverbosetrue| after having set |\xintDigits*| to \dgts{8}, and then we % optimized a bit manually. The advantage here is that we don't have to set % ourself |\xintDigits| and later restore it. % % We can not use (except if only caring about interactive sessions where we % control entirely the whole environment) |\XINTinFloatDiv| or % |\XINTinFloatMul| if we don't set |\xintDigits| (which is user customizable) % because they hardcode usage of |\XINTdigits|. This is e.g.\@ why we use % |\PoorManLogBaseTen_raw| and not |\PoorManLogBaseTen|. % \begin{macrocode} \def\ZeckNearIndex#1{\xintiRound{0}{% \xintFloatDiv[8]{\PoorManLogBaseTen_raw{\xintFloatMul[8]{2236068[-6]}{#1}}}% {20898764[-8]}% }% }% % \end{macrocode} % Now we compute the actual maximal index. This macro is now only for user % interface, as we dropped at |0.9c| adding a |maxk()| function to the % |\xinteval| interface. % % With |0.9d| replace negative input by its absolute value. % \begin{macrocode} \def\ZeckMaxK{\expanded\zeckmaxk}% \def\zeckmaxk#1{\expandafter\zeckmaxk_fork\romannumeral`&&@#1\xint:}% \def\zeckmaxk_fork#1{% \xint_UDzerominusfork #1-{\bgroup\zeck_abort}% 0#1\zeckmaxk_a 0-{\zeckmaxk_a#1}% \krof }% \def\zeckmaxk_a #1\xint:{% \expandafter\zeckmaxk_b \the\numexpr\ZeckNearIndex{#1}\xint:#1\xint: }% \def\zeckmaxk_b #1\xint:{% \expandafter\zeckmaxk_c \romannumeral`&&@\Zeck@@FPair{#1}#1\xint: }% \def\zeckmaxk_c #1#2#3\xint:#4\xint:{% \xintiiifGt{#1}{#4}% {{\expandafter}\the\numexpr#3-1\relax}% {{}#3}% }% % \end{macrocode} % \subsubsection{\csh{ZeckIndices}} % This starts by computing the maximal index. It then subtracts the Fibonacci % number from the input and loops. % % At |0.9d| let it (rather than returning empty output) accept a negative % argument (silently replaced by its absolute value). % \begin{macrocode} \def\ZeckIndices{\expanded\zeckindices}% \let\ZeckZeck\ZeckIndices \def\zeckindices#1{\bgroup\expandafter\zeckindices_fork\romannumeral`&&@#1\xint:}% \def\zeckindices_fork#1{% \xint_UDzerominusfork #1-\zeck_abort 0#1\zeckindices_a 0-{\zeckindices_a#1}% \krof }% \def\zeckindices_a #1\xint:{% \expandafter\zeckindices_b \the\numexpr\ZeckNearIndex{#1}\xint:#1\xint: }% \def\zeckindices_b #1\xint:{% \expandafter\zeckindices_c \romannumeral`&&@\Zeck@@FPair{#1}#1\xint: }% \def\zeckindices_c #1#2#3\xint:#4\xint:{% \xintiiifGt{#1}{#4}\zeckindices_A\zeckindices_B #1;#2;#3\xint:#4\xint: }% \def\zeckindices_A#1;#2;#3\xint:{% \the\numexpr#3-1\relax\zeckindices_loop{#2}% }% \def\zeckindices_B#1;#2;#3\xint:{% #3\zeckindices_loop{#1}% }% \def\zeckindices_loop #1#2\xint:{% \expandafter\zeckindices_loop_i \romannumeral0\xintiisub{#2}{#1}\xint: }% \def\zeckindices_loop_i#1{% \xint_UDzerofork#1\zeck_done 0{, \zeckindices_a#1}\krof }% % \end{macrocode} % \subsubsection{\csh{ZeckBList}} % This is the variant which produces the results as a sequence of braced % indices. % % Originally in \xintname, \xinttoolsname, the term ``list'' is used for % braced items. In the user manual of this package I have been using ``list'' % more colloquially for comma separated values. Here I stick with \xintname % conventions but use |BList| (short for ``list of Braced items'') and not only % ``List'' in the name. % % At |0.9d| let it (rather than returning empty output) accept a negative % argument (silently replaced by its absolute value). % \begin{macrocode} \def\ZeckBList{\expanded\zeckblist}% \def\zeckblist#1{\bgroup\expandafter\zeckblist_fork\romannumeral`&&@#1\xint:}% \def\zeckblist_fork#1{% \xint_UDzerominusfork #1-\zeck_abort 0#1\zeckblist_a 0-{\zeckblist_a#1}% \krof }% \def\zeckblist_a #1\xint:{% \expandafter\zeckblist_b \the\numexpr\ZeckNearIndex{#1}\xint:#1\xint: }% \def\zeckblist_b #1\xint:{% \expandafter\zeckblist_c \romannumeral`&&@\Zeck@@FPair{#1}#1\xint: }% \def\zeckblist_c #1#2#3\xint:#4\xint:{% \xintiiifGt{#1}{#4}\zeckblist_A\zeckblist_B #1;#2;#3\xint:#4\xint: }% \def\zeckblist_A#1;#2;#3\xint:{% {\the\numexpr#3-1\relax}\zeckblist_loop{#2}% }% \def\zeckblist_B#1;#2;#3\xint:{% {#3}\zeckblist_loop{#1}% }% \def\zeckblist_loop#1#2\xint:{% \expandafter\zeckblist_loop_i \romannumeral0\xintiisub{#2}{#1}\xint: }% \def\zeckblist_loop_i#1{\xint_UDzerofork#1\zeck_done 0{\zeckblist_a#1}\krof}% % \end{macrocode} % \subsubsection{\csh{ZeckWord}} % This is slightly more complicated than \cs{ZeckIndices} and \cs{ZeckBList} % because we have to keep track of the previous index to know how many zeros % to inject. % \begin{macrocode} \def\ZeckWord{\expanded\zeckword}% \def\zeckword#1{\bgroup\expandafter\zeckword_fork\romannumeral`&&@#1\xint:}% \def\zeckword_fork#1{% \xint_UDzerominusfork #1-\zeck_abort 0#1\zeckword_a 0-{\zeckword_a#1}% \krof }% \def\zeckword_a #1\xint:{% \expandafter\zeckword_b\the\numexpr\ZeckNearIndex{#1}\xint: #1\xint: }% \def\zeckword_b #1\xint:{% \expandafter\zeckword_c\romannumeral`&&@\Zeck@@FPair{#1}#1\xint: }% \def\zeckword_c #1#2#3\xint:#4\xint:{% \xintiiifGt{#1}{#4}\zeckword_A\zeckword_B #1;#2;#3\xint:#4\xint: }% \def\zeckword_A#1;#2;#3\xint:#4\xint:{% \expandafter\zeckword_d \romannumeral0\xintiisub{#4}{#2}\xint: \the\numexpr#3-1.% }% \def\zeckword_B#1;#2;#3\xint:#4\xint:{% \expandafter\zeckword_d \romannumeral0\xintiisub{#4}{#1}\xint: #3.% }% \def\zeckword_d #1% {\xint_UDzerofork#1\zeckword_done0{1\zeckword_e}\krof #1}% \def\zeckword_done#1\xint:#2.{1\xintReplicate{#2-2}{0}\iffalse{\fi}}% \def\zeckword_e #1\xint:{% \expandafter\zeckword_f\the\numexpr\ZeckNearIndex{#1}\xint: #1\xint: }% \def\zeckword_f #1\xint:{% \expandafter\zeckword_g\romannumeral`&&@\Zeck@@FPair{#1}#1\xint: }% \def\zeckword_g #1#2#3\xint:#4\xint:{% \xintiiifGt{#1}{#4}\zeckword_gA\zeckword_gB #1;#2;#3\xint:#4\xint: }% \def\zeckword_gA#1;#2;#3\xint:#4\xint:{% \expandafter\zeckword_h \the\numexpr#3-1\expandafter.% \romannumeral0\xintiisub{#4}{#2}% \xint: }% \def\zeckword_gB#1;#2;#3\xint:#4\xint:{% \expandafter\zeckword_h \the\numexpr#3\expandafter.% \romannumeral0\xintiisub{#4}{#1}% \xint: }% \def\zeckword_h #1.#2\xint:#3.{% \xintReplicate{#3-#1-1}{0}% \zeckword_d #2\xint:#1.% }% % \end{macrocode} % \subsubsection{\csh{ZeckHexWord}} % \begin{macrocode} \def\ZeckHexWord{\romannumeral0\zeckhexword}% \def\zeckhexword#1{% \xintbintohex{% \expanded\bgroup\expandafter\zeckword_fork\romannumeral`&&@#1\xint: }% }% % \end{macrocode} % \subsubsection{\csh{ZeckNFromIndices}} % Spaces before commas are not a problem they disappear in |\numexpr|. % % Extraneous commas are skipped, in particular a final comma is allowed. % % Each item is \emph{f}-expanded to check not empty, but perhaps we could skip % expanding, as they end up in |\numexpr|. Advantage of expansion of each % item is that at any location is that can generate multiple indices from some % macro expansion inserting commas dynamically. % \begin{macrocode} \def\ZeckNFromIndices{\romannumeral0\zecknfromindices}% \def\zecknfromindices{\zeck@applyandiisum\Zeck@FN}% \def\zeck@applyandiisum {% \expandafter\xintiisum\expanded\zeck@applytocsv }% % \end{macrocode} % Macro |#1| is assumed to output something within braces. % \begin{macrocode} \def\zeck@applytocsv #1#2{% {{\expandafter\zeck@applytocsv_a\expandafter#1% \romannumeral`&&@#2,;}}% }% \def\zeck@applytocsv_a #1#2{% \if;#2\expandafter\zeck@applytocsv_done\fi \if,#2\expandafter\zeck@applytocsv_skip\fi \zeck@applytocsv_b #1#2% }% \def\zeck@applytocsv_b #1#2,{% #1{#2}% \expandafter\zeck@applytocsv_a\expandafter#1\romannumeral`&&@% }% \def\zeck@applytocsv_done#1\zeck@applytocsv_b#2;{}% \def\zeck@applytocsv_skip #1#2,{% \expandafter\zeck@applytocsv_a\expandafter#2\romannumeral`&&@% }% % \end{macrocode} % \subsubsection{\csh{ZeckNfromWord}} % The |\xintreversedigits| will \emph{f}-expand its argument. % \begin{macrocode} \def\ZeckNfromWord{\romannumeral0\zecknfromword}% \def\zecknfromword#1{% \expandafter\zecknfromword_a\romannumeral0\xintreversedigits{#1};% }% \def\zecknfromword_a{% \expandafter\xintiisum\expanded{{\iffalse}}\fi\zecknfromword_b 2.% }% \def\zecknfromword_b#1.#2{% \if;#2\expandafter\zecknfromword_done\fi \if#21\Zeck@@FN{#1}\fi \expandafter\zecknfromword_b\the\numexpr#1+1.% }% \def\zecknfromword_done#1.{\iffalse{{\fi}}}% % \end{macrocode} % \subsubsection{\csh{ZeckNfromHexWord}} % Added at |0.9d|. % \begin{macrocode} \def\ZeckNfromHexWord{\romannumeral0\zecknfromhexword}% \def\zecknfromhexword#1{% \expandafter\zecknfromword_a\romannumeral0\xintreversedigits{\xintHexToBin{#1}};% }% % \end{macrocode} % \subsection{Bergman representation} % \subsubsection{\csh{PhiIISign_ab}} % |\PhiIISign_ab| is for use with two already expanded arguments |{a}{b}| % and which are strict integers. % % The general macro which accepts both one (unbraced) integer or two (braced) % integers is defined later for support of the |phisign()| function. % \begin{macrocode} \def\PhiIISign_ab {\romannumeral0\phiiisign_ab}% \def\phiiisign_ab #1#2{% \xintiiifsgn{#1}% {% a < 0 \xintiiifSgn{#2}% {-1}% {-1}% {% a < 0, b > 0, return 1 iff a^2+ab 0 \xintiiifSgn{#2}% {% a > 0, b < 0, return 1 iff a^2+ab>b^2 \xintiiifGt{\xintiiMul{#1}{\xintiiAdd{#1}{#2}}}{\xintiiSqr{#2}}% {1}% {-1}% }% {1}% {1}% }% }% % \end{macrocode} % \subsubsection{\cs{PhiMaxE}} % \label{sssec:maxe} % We want the greatest $k$ with $\phi^k \leq a + b \phi$, assuming that the % latter is strictly positive. We will do this using a careful computation % (i.e.\@ avoiding ``catastrophic cancellations'' if $a$ and $b$ are of % opposite signs) of % the base-ten logarithm of $a + b \phi$, with about 8 decimal digits of % precision. Rounding the quotient by $\log_{10}\phi$ to the % nearest integer we obtain a candidate |K|. We compute $\phi^K$ using % Fibonacci numbers and compare (using integer-only arithmetic). If this is % larger than $a + b \phi$ the seeked |k| is |K-1| else it is |K|. For why, % see the explanations relative to the computation of the Zeckendorf % representation and the next paragraph about theoretical limitation. % % The rounding to an integer of $\log(a+b\phi)/\log(\phi)$ obtained with 8 % decimal digits of precision will not error by $2$ units or more if the input % was less than $\phi^{10^7}$, so for an $x$ which is an integer % having less than two million decimal digits, or say one million, this is % safe. And \xintname can only do computations with operands having less than % about |13000| digits (with \TeX Live 2025 default memory settings). If % using another programming context not having such limitations, and using % rather double precision floats, which gives slightly less than |16| decimal % digits of floating point precision, the upper bound would raise to about % $\phi^{10^{15}}$, and inputs with at most $10^{14}$ decimal digits are safe, % i.e.\@ all real life inputs are safe. % % Let us nevertheless explain how we could do without logarithms and without % any upper bound on size of the input. Let $x=a+b\phi$, assumed to be % positive. First, we test if $x<1$. We can do this using integers only. If % true, we multiply $x$ by $\phi$, $\phi^2$, $\phi^4$, using algebra in % $\ZZ[\phi] = \ZZ + \ZZ \phi$, until finding the smallest power of $2$ such % that $\phi^{2^n}x = x'\geq1$. The powers of $\phi$ are computed by repeated % squarings (and they can be pre-stored up to certain reasonable maximal $n$, % but we are discussing here a ``no prior bound'' situation). The searched-for % exponent $k(x)$ is $k(x')-2^n$. So we are reduced to the $x\geq1$ case. If % $x<\phi$, then $k(x)=0$. If $x\geq\phi=\phi^{2^0}$, repeated squaring of % $\phi$ and comparisons with $x$ using $u + v \phi$ representations will give % us the smallest $n\geq0$ with $\phi^{2^{n+1}}>x$. Then divide $x$ by % $\phi^{2^n}$ (or rather multiply with $\phi^{-2^n}$), again using % $\ZZ[\phi]$ algebra, obtaining some $x'$ with $1\leq x' < \phi^{2^n}$ and % $k(x)=k(x')+2^n$ with $0\leq k(x')<2^n$. After finitely many steps we will % have reduced to $[1,\phi)$ and the algorithm ends. % % The macro helpers handling the computation of the ratio of logarithms should % not make assumptions about |\xintDigits|. Here is the original source as it % was used to create the code (not any AI would be able to do that... but % \xintexprname succeeds!). In particular note the impressive nesting of % |\xintiiexpr| inside |\xintfloatexpr|. The log output (thanks to % |\xintverbosetrue|) was then edited by hand to not use macros using tacitly % |\XINTdigits|, and to reduce to 8 digits of precision as this is enough. %\begin{verbatim} % \xintverbosetrue % \xintdeffloatvar Phi := (1 + sqrt(5))/2; % \xintdeffloatvar Psi := (1 - sqrt(5))/2; % \xintdeffloatvar logPhi := log10(Phi);% would have been precomputed anyhow % \xintdefiifunc greedyA(a,b):= \xintfloatexpr % round(log10(a+b*Phi) / logPhi)\relax; % \xintdefiifunc greedyB(a,b):= \xintfloatexpr % round(log10(\xintiiexpr (a*(a+b) -sqr(b))\relax/(a+b*Psi)) % / logPhi)\relax; %\end{verbatim} % \begin{macrocode} \def\bergman_nearindex_A#1;#2;{% \xintiRound {0}{% \xintFloatDiv[8]{% \PoorManLogBaseTen_raw {\xintFloatAdd[8]{#1}% {\xintFloatMul[8]{#2}{1618034[-6]}}}% }% {20898764[-8]}% }% }% \def\bergman_nearindex_B#1;#2;{% \xintiRound {0}{% \xintFloatDiv[8]{% \PoorManLogBaseTen_raw {\xintFloatDiv[8]% {\xintiiSub {\xintiiMul {#1}{\xintiiAdd{#1}{#2}}}% {\xintiiSqr {#2}}% }% {\xintFloatAdd[8]% {#1}{\xintFloatMul[8]{#2}{-618034[-6]}}% }% }% }% {20898764[-8]}% }% }% \def\PhiMaxE{\the\numexpr\phimaxe}% \def\phimaxe #1{% \expandafter\phimaxe_a\expanded{{#1}}% }% \def\phimaxe_a #1{\expandafter\phimaxe_b\string#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phimaxe_b #1[\if#1{\expandafter\phimaxe_X % } \else\expandafter\phimaxe_N\fi #1]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phimaxe_N #1;{\phimaxe_ab {#1}{0};}% \def\phimaxe_X #1{\expandafter\phimaxe_ab\expandafter{\iffalse}\fi}% \def\PhiMaxE_ab {\romannumeral0\phimaxe_ab}% \def\phimaxe_ab #1#2;{% \expandafter\phimaxe_i\romannumeral`&&@% \ifnum\numexpr\xintiiSgn{#1}*\xintiiSgn{#2}\relax=-1 \expandafter\bergman_nearindex_B \else \expandafter\bergman_nearindex_A \fi #1;#2;;#1;#2;% }% \def\phimaxe_i #1;{% \expandafter\phimaxe_j\romannumeral`&&@\zeck@fpair #1.#1;% }% \def\phimaxe_j #1#2#3;#4;#5;{% #3\ifnum \expandafter\PhiIISign_ab \expanded{{\xintiiSub{#2}{#4}}{\xintiiSub{#1}{#5}}}>\xint_c_ -1\fi\relax }% % \end{macrocode} % \subsubsection{\csh{PhiBList}} % % Will serve (or rather a close derivative) as support for the % |phiexponents()| function in \cs{xinteval}, and is used both by % |\PhiExponents| and |\PhiBasePhi|. % \begin{macrocode} \def\PhiBList{\expanded\phiblist}% \def\phiblist #1{% \expandafter\phiblist_a\expanded{{#1}}% }% \def\phiblist_a #1{\expandafter\phiblist_b\string#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phiblist_b #1[\if#1{\expandafter\phiblist_X % } \else\expandafter\phiblist_N\fi #1]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phiblist_N #1;{\phiblist_ab {#1}{0};}% \def\phiblist_X #1{\expandafter\phiblist_ab\expandafter{\iffalse}\fi}% \def\PhiBlist_ab {\expanded\phiblist_ab}% \def\phiblist_ab #1#2;{{% \ifcase\PhiIISign_ab{#1}{#2} % 0\expandafter\phiblist_stop \or +\expandafter\phiblist_i \else -\expandafter\phiblist_neg \fi #1;#2;% }}% \def\phiblist_stop#1;#2;{}% % \end{macrocode} % Attention that adding minus signs here would fool |\xintiiSgn| which makes % no normalization and looks only at first token. % % TODO: check if it is more efficient to do |\expanded{\noexpand\foo...}| % rather than |\expandafter\foo\expanded{...}|. % \begin{macrocode} \def\phiblist_neg#1;#2;{% \expandafter\phiblist_i\expanded{\XINT_Opp#1;\XINT_Opp#2;}% }% \def\phiblist_i#1;#2;{% \expandafter\phiblist_j\romannumeral`&&@% \ifnum\numexpr\xintiiSgn{#1}*\xintiiSgn{#2}\relax=-1 \expandafter\bergman_nearindex_B \else \expandafter\bergman_nearindex_A \fi #1;#2;;#1;#2;% }% \def\phiblist_j #1;{% \expandafter\phiblist_k\romannumeral`&&@\zeck@fpair #1.#1;% }% \def\phiblist_k #1#2#3;#4;#5;{% \if1\expandafter\PhiIISign_ab \expanded{{\xintiiSub{#2}{#4}}{\xintiiSub{#1}{#5}}}% \expandafter\phiblist_ci \else \expandafter\phiblist_cii \fi #1;#2;#3;#4;#5;% }% \def\phiblist_ci #1;#2;#3;#4;#5;{% {\the\numexpr#3-1\relax}% \expandafter\phiblist_again\expanded{% {\xintiiSub{\xintiiAdd{#2}{#4}}{#1}}% {\xintiiSub{#5}{#2}}% }% }% \def\phiblist_cii #1;#2;#3;#4;#5;{% {#3}% \expandafter\phiblist_again {\xintiiSub{#4}{#2}}% {\xintiiSub{#5}{#1}}% }% \def\phiblist_again #1#2{% \if0\PhiIISign_ab{#1}{#2}% \expandafter\phiblist_stop \else \expandafter\phiblist_i \fi #1;#2;% }% % \end{macrocode} % \subsubsection{\csh{PhiExponents}} % As this depends upon |\PhiBList| it will have to unbrace % at each step to check sign of the exponent. % \begin{macrocode} \def\PhiExponents{\expanded\phiexponents}% \def\phiexponents#1{{% \expandafter\phiexponents_a \expanded\expandafter\phiblist_a\expanded{{#1}}% ;% }}% \def\phiexponents_a #1{\if-#1.\fi\phiexponents_b}% \def\phiexponents_b #1{% \if;#1\expandafter\phiexponents_done\fi #1\phiexponents_c }% \def\phiexponents_c #1{% \if;#1\expandafter\phiexponents_done\fi \PhiExponentsSep#1\phiexponents_c }% \def\phiexponents_done#1\phiexponents_c{}% \def\PhiExponentsSep{, }% % \end{macrocode} % \subsubsection{\csh{PhiBasePhi}} % As this depends upon |\PhiBList| it will have to unbrace % at each step to check sign of the exponent. % \begin{macrocode} \def\PhiBasePhi{\expanded\phibasephi}% \def\phibasephi#1{{% \expandafter\phibasephi_a \expanded\expandafter\phiblist_a\expanded{{#1}}% ;% }}% \def\phibasephi_a #1{% \if-#1-\fi \if0#1\expandafter\xint_gob_til_sc\fi \phibasephi_b }% \def\phibasephi_b #1{\phibasephi_c #1.}% \def\phibasephi_c #1#2.{% \if-#1% 0\PhiBasePhiSep\xintReplicate{#2-1}{0}% 1\expandafter\phibasephi_n \else 1\expandafter\phibasephi_p \fi #1#2.% }% \def\phibasephi_p #1.#2{\phibasephi_pa #1.#2\xint:}% \def\phibasephi_pa #1.#2{% \if;#2\xintReplicate{#1}{0}\expandafter\xint_gob_til_xint:\fi \phibasephi_pb #1.#2% }% \def\phibasephi_pb #1.#2#3\xint:{% \if-#2% \xintReplicate{#1}{0}\PhiBasePhiSep\xintReplicate{#3-1}{0}% 1\expandafter\phibasephi_n \else \xintReplicate{#1-#2#3-1}{0}% 1\expandafter\phibasephi_p \fi #2#3.% }% \def\phibasephi_n #1.#2{\phibasephi_na #1.#2\xint:}% \def\phibasephi_na #1.#2{% \if;#2\expandafter\xint_gob_til_xint:\fi \phibasephi_nb #1.#2% }% \def\phibasephi_nb #1.#2#3\xint:{% \xintReplicate{#1+#3-1}{0}% 1\phibasephi_n #2#3.% }% \def\PhiBasePhiSep{.}% % \end{macrocode} % \subsubsection{\csh{PhiBaseHexPhi}} % Broken if |\PhiBasePhiSep| is not default. % % Attention for fractional part that we must first extend with trailing zeros % if needed to make it have 4N digits. (Expansion of integers will have an % even number of fractional digits, but of course this is not true of general % |a + b phi|. % \begin{macrocode} \def\PhiBaseHexPhi{\expanded\phibasehexphi}% \def\phibasehexphi#1{\expandafter\phibasehexphi_a\expanded{% \expandafter\phibasephi_a \expanded\expandafter\phiblist_a\expanded{{#1}}% ;}..,;% }% % \end{macrocode} % MEMO: fortunately the second |\xintBinToHex| will not trim leading zeros % which were originally zeros after the radix separator. But we also % must make sure to apply it to an input having a multiple of four number % of binary digits. % \begin{macrocode} \def\phibasehexphi_a #1.#2.#3#4;{{% \xintBinToHex{#1}% \if.#3.\xintBinToHex{\expanded{#2\expandafter\phibasehexphi_aux \romannumeral0\xintlength{#2}.}}\fi }}% % \end{macrocode} % Here |#1| is at least one. % \begin{macrocode} \def\phibasehexphi_aux #1.{\xintReplicate{4*((#1+1)/4) - #1}{0}}% % \end{macrocode} % \subsubsection{\csh{PhiXfromExponents}} % If the list starts with period, it means it represents % a negative number (or perhaps zero is there is nothing else). % \begin{macrocode} \def\PhiXfromExponents{\expanded\phixfromexponents}% \def\phixfromexponents#1{% \expandafter\phixfromexponents_a\romannumeral`&&@#1,;% }% \def\phixfromexponents_a #1{% \if.#1\expandafter\phixfromexponents_n \else\expandafter\phixfromexponents_p \fi #1% }% \def\phixfromexponents_p #1;{{% {\xintiiSum{\expanded{\zeck@applytocsv_a\Zeck@FNminusOne#1;}}}% {\xintiiSum{\expanded{\zeck@applytocsv_a\Zeck@FN#1;}}}% }}% \def\phixfromexponents_n .#1;{{% {\xintiiOpp{\xintiiSum{\expanded{\zeck@applytocsv_a\Zeck@FNminusOne#1;}}}}% {\xintiiOpp{\xintiiSum{\expanded{\zeck@applytocsv_a\Zeck@FN#1;}}}}% }}% % \end{macrocode} % \subsubsection{\csh{PhiXfromBasePhi}} % The radix separator must be an explicit period. There must be at least one digit % before the period, if the latter is there. Empty input is allowed. Input % must \emph{f}-expand completely thus input such as |\macroA.\macroB| is not % allowed. % % Coded the lazy way by first converting to comma separated list of exponents. The % |\phiexponentsfromrep|'s output has a final comma but this is ok. % \begin{macrocode} \def\PhiXfromBasePhi{\expanded\phixfrombasephi}% \def\phixfrombasephi{\expandafter\phixfromexponents\expanded\phiexponentsfromrep}% \def\phiexponentsfromrep#1{% {{\iffalse}\fi\expandafter\phiexponentsfromrep_a\romannumeral`&&@#1.;\xint:}% }% \def\phiexponentsfromrep_a #1{% \if-#1.\xint_dothis\phiexponentsfromrep_a\fi \if.#1\xint_dothis\zeck_done\fi \xint_orthat{\phiexponentsfromrep_b #1}% }% \def\phiexponentsfromrep_b #1.#2{% \expandafter\phiexponentsfromrep_c\romannumeral0\xintreversedigits{#1};% \if;#2\expandafter\zeck_done\else \expandafter\phiexponentsfromrep_i\fi #2% }% \def\phiexponentsfromrep_c{\phiexponentsfromrep_d 0.}% \def\phiexponentsfromrep_d#1.#2{% \if;#2\expandafter\xint_gob_til_dot\fi \if#21#1,\fi \expandafter\phiexponentsfromrep_d\the\numexpr#1+1.% }% \def\phiexponentsfromrep_i{\phiexponentsfromrep_j -1.}% \def\phiexponentsfromrep_j#1.#2{% \if;#2\expandafter\zeck_done\fi \if#21#1,\fi \expandafter\phiexponentsfromrep_j\the\numexpr#1-1.% }% % \end{macrocode} % \subsubsection{\csh{PhiXfromBaseHexPhi}} % Added at |0.9d|. % \begin{macrocode} \def\PhiXfromBaseHexPhi{\expanded\phixfrombasehexphi}% \def\phixfrombasehexphi{\expandafter\phixfromexponents \expanded\phiexponentsfromhexrep}% \def\phiexponentsfromhexrep#1{% {{\iffalse}\fi\expandafter\phiexponentsfromhexrep_a\romannumeral`&&@#1.;\xint:}% }% \def\phiexponentsfromhexrep_a #1{% \if-#1.\xint_dothis\phiexponentsfromhexrep_a\fi \if.#1\xint_dothis\zeck_done\fi \xint_orthat{\phiexponentsfromhexrep_b #1}% }% \def\phiexponentsfromhexrep_b #1.#2{% \expandafter\phiexponentsfromrep_c \romannumeral0\xintreversedigits{\xintHexToBin{#1}};% \if;#2\expandafter\zeck_done\else \expandafter\phiexponentsfromhexrep_i\fi #2% }% % \end{macrocode} % Attention that conversion from hexadecimal to binary must preserve leading % zeros! % \begin{macrocode} \def\phiexponentsfromhexrep_i#1;{% \expanded{\noexpand\phiexponentsfromrep_j -1.\xintCHexToBin{#1}};% }% % \end{macrocode} % \subsection{The Knuth Fibonacci multiplication} % \subsubsection{\csh{ZeckKMul}: Knuth definition} % Here a |\romannumeral0| trigger is used to match |\xintiisum|. Although % it induces defining one more macro we obide by the general coding style of % \xintname with a CamelCase then a lowercase macro, rather than having them % merged as only one. % % For the |\xinteval| we need a variant applying |\xintNum| to its arguments. % \begin{macrocode} \def\ZeckKMul{\romannumeral0\zeckkmul}% \def\zeckkmul#1#2{\expandafter\zeckkmul_a \expanded{\ZeckIndices{#1}% ,;% \ZeckIndices{#2}% ,,}% }% \def\ZeckKMulNum#1#2{\romannumeral0\zeckkmul{\xintNum{#1}}{\xintNum{#2}}}% % \end{macrocode} % The space token at start of |#2| after first one is not a problem % as it ends up in a |\numexpr| anyhow. % \begin{macrocode} \def\zeckkmul_a{\expandafter\xintiisum\expanded{{\iffalse}}\fi \zeckkmul_b}% \def\zeckkmul_b#1;#2,{% \if\relax#2\relax\expandafter\zeckkmul_done\fi \zeckkmul_c{#2}#1,\zeckkmul_b#1;% }% \def\zeckkmul_c#1#2,{% \if\relax#2\relax\expandafter\xint_gobble_iv\fi \Zeck@@FN{#1+#2}\zeckkmul_c{#1}% }% \def\zeckkmul_done#1;{\iffalse{{\fi}}}% % \end{macrocode} % \subsubsection{\csh{ZeckAMul}: Arnoux formula} % Here a |\romannumeral0| trigger is used to match |\xintiisum|. % \begin{macrocode} \def\ZeckAMul{\romannumeral0\zeckamul}% \def\ZeckAMulNum#1#2{\romannumeral0\zeckamul{\xintNum{#1}}{\xintNum{#2}}}% \def\zeckamul#1{\expandafter\zeckamul_in\romannumeral`&&@#1;}% \def\zeckamul_in#1;#2{\expandafter\zeckamul_a\romannumeral`&&@#2;#1;}% \def\zeckamul_a #1;#2;{\xintiiadd {\xintiiMul{#1}{#2}} {\xintiiAdd{\xintiiMul{#1}{\ZeckB{#2}}}{\xintiiMul{\ZeckB{#1}}{#2}}}% }% % \end{macrocode} % \subsubsection{\csh{ZeckB}: B operator} % Here a |\romannumeral0| trigger is used to match |\xintiisum|. It is a fact % of life that |\xintiiSum| needs to grab something at each item before % expanding it, rather than expanding prior to grabbing. So we use an % |\expanded| wrapper. % \begin{macrocode} \def\ZeckB{\romannumeral0\zeckb}% \def\zeckb#1{\xintiisum{\expanded{\iffalse}\fi \expandafter\zeckb_a\expanded\zeckindices{#1},,}}% % \end{macrocode} % |#1-1| is always positive. % \begin{macrocode} \def\zeckb_a#1,{% \if\relax#1\relax\expandafter\zeckb_done\fi \Zeck@@FN{#1-1}\zeckb_a }% \def\zeckb_done#1\zeckb_a{\iffalse{\fi}}% % \end{macrocode} % \subsection{Typesetting} % \subsubsection{\csh{ZeckPrintIndexedSum}} % Expandable, but needs \emph{x}-expansion. The default requires math mode, % at it uses |\sb|. We do not use |_| here due to its current catcode. It % only \emph{f}-expands its argument. No repeated or final comma is allowed. % \begin{macrocode} \def\ZeckPrintIndexedSumSep{+\allowbreak}% \def\ZeckPrintOne#1{F\sb{#1}}% \def\ZeckPrintIndexedSum#1{% \expandafter\zeckprintindexedsum\romannumeral`&&@#1,;% }% \def\zeckprintindexedsum#1{% \if,#1\expandafter\xint_gob_til_sc\fi \zeckprintindexedsum_a#1% }% \def\zeckprintindexedsum_a#1,{\ZeckPrintOne{#1}\zeckprintindexedsum_b}% \def\zeckprintindexedsum_b #1{% \if;#1\expandafter\xint_gob_til_sc\fi \ZeckPrintIndexedSumSep\zeckprintindexedsum_a#1% }% % \end{macrocode} % \subsubsection{\csh{PhiPrintIndexedSum}} % A clone of |\ZeckPrintIndexedSum| with its own namespace. % \begin{macrocode} \let\PhiPrintIndexedSumSep\ZeckPrintIndexedSumSep \def\PhiPrintOne#1{\phi\sp{#1}}% \def\PhiPrintIndexedSum#1{% \expandafter\phiprintindexedsum\romannumeral`&&@#1,;% }% \def\phiprintindexedsum#1{% \if,#1\expandafter\xint_gob_til_sc\fi \phiprintindexedsum_a#1% }% \def\phiprintindexedsum_a#1,{\PhiPrintOne{#1}\phiprintindexedsum_b}% \def\phiprintindexedsum_b #1{% \if;#1\expandafter\xint_gob_til_sc\fi \PhiPrintIndexedSumSep\phiprintindexedsum_a#1% }% % \end{macrocode} % \subsubsection{\csh{PhiTypesetX}} % \begin{macrocode} \def\PhiTypesetX #1{% \expandafter\PhiTypesetXPrint\expanded{#1}% }% \def\PhiTypesetXPrint #1#2{% \xintiiifSgn{#1}% {#1\xintiiifSgn{#2}{#2\phi}{}{+#2\phi}}% {\xintiiifSgn{#2}{#2\phi}{0}{#2\phi}}% {#1\xintiiifSgn{#2}{#2\phi}{}{+#2\phi}}% }% % \end{macrocode} % \subsection{Extensions of the \cs{xinteval} syntax} % % Initially functions and Knuth operator were added to |\xintiieval| only, but % when it was decided to overload the infix operators to handle inputs from % $\ZZ[\phi]$, it felt awkward not to include the division so finally we % support $\QQ(\phi)$ algebra, and for this had to switch to |\xinteval|. % % \subsubsection{Provisory ad hoc support for adding \xintexprname operators} % Unfortunately, contrarily to \bnumexprname, \xintexprname (at % |1.4o|) has no public interface to define an infix operator, and the macros % it defines to that end have acquired another meaning at end of loading % |xintexpr.sty|, so we have to copy quite a few lines of code. This is % provisory and will be removed when xintexpr.sty will have been udpated. We % also copy/adapt |\bnumdefinfix|. % % We test for existence of |\xintdefinfix| so as to be able to update upstream % and not have to sync it immediately. But perhaps upstream will choose some % other name than |\xintdefinfix|... % \begin{macrocode} \ifdefined\xintdefinfix \def\zeckdefinfix{\xintdefinfix {expr}}% \else \ifdefined\xint_noxpd\else\let\xint_noxpd\unexpanded\fi % support old xint \def\ZECK_defbin_c #1#2#3#4#5#6#7#8% {% \XINT_global\def #1##1% \XINT_#8_op_ {% \expanded{\xint_noxpd{#2{##1}}\expandafter}% \romannumeral`&&@\expandafter#3\romannumeral`&&@\XINT_expr_getnext }% \XINT_global\def #2##1##2##3##4% \XINT_#8_exec_ {% \expandafter##2\expandafter##3\expandafter {\romannumeral`&&@\XINT:NEhook:f:one:from:two{\romannumeral`&&@#7##1##4}}% }% \XINT_global\def #3##1% \XINT_#8_check-_ {% \xint_UDsignfork ##1{\expandafter#4\romannumeral`&&@#5}% -{#4##1}% \krof }% \XINT_global\def #4##1##2% \XINT_#8_checkp_ {% \ifnum ##1>#6% \expandafter#4% \romannumeral`&&@\csname XINT_#8_op_##2\expandafter\endcsname \else \expandafter ##1\expandafter ##2% \fi }% }% % \end{macrocode} % ATTENTION there is lacking at end here compared to the \bnumexprname version % an adjustment for updating minus operator, % if some other right precedence than 12, 14, 17 is used. % Doing this would requiring copying still more, so is not done. % \begin{macrocode} \def\ZECK_defbin_b #1#2#3#4#5% {% \expandafter\ZECK_defbin_c \csname XINT_#1_op_#2\expandafter\endcsname \csname XINT_#1_exec_#2\expandafter\endcsname \csname XINT_#1_check-_#2\expandafter\endcsname \csname XINT_#1_checkp_#2\expandafter\endcsname \csname XINT_#1_op_-\romannumeral\ifnum#4>12 #4\else12\fi\expandafter\endcsname \csname xint_c_\romannumeral#4\endcsname #5% {#1}% #8 for \ZECK_defbin_c \XINT_global \expandafter \let\csname XINT_expr_precedence_#2\expandafter\endcsname \csname xint_c_\romannumeral#3\endcsname }% % \end{macrocode} % These next two currently lifted from \bnumexprname with some adaptations, % see previous comment about precedences. % % We do not define the extra |\chardef|'s as does \bnumexprname to allow more % user choices of precedences, not only because nobody will ever use the % feature, but also because it needs extra configuration for minus unary % operator. (as mentioned above) % \begin{macrocode} \def\zeckdefinfix #1#2#3#4% {% \edef\ZECK_tmpa{#1}% \edef\ZECK_tmpa{\xint_zapspaces_o\ZECK_tmpa}% \edef\ZECK_tmpL{\the\numexpr#3\relax}% \edef\ZECK_tmpL {\ifnum\ZECK_tmpL<4 4\else\ifnum\ZECK_tmpL<23 \ZECK_tmpL\else 22\fi\fi}% \edef\ZECK_tmpR{\the\numexpr#4\relax}% \edef\ZECK_tmpR {\ifnum\ZECK_tmpR<4 4\else\ifnum\ZECK_tmpR<23 \ZECK_tmpR\else 22\fi\fi}% \ZECK_defbin_b {expr}\ZECK_tmpa\ZECK_tmpL\ZECK_tmpR #2% \expandafter\ZECK_dotheitselves\ZECK_tmpa\relax \unless\ifcsname XINT_expr_exec_-\romannumeral\ifnum\ZECK_tmpR>12 \ZECK_tmpR\else 12\fi \endcsname \xintMessage{zeckendorf}{Error}{Right precedence not among accepted values.}% \errhelp{Accepted values include 12, 14, and 17.}% \errmessage{Sorry, you can not use \ZECK_tmpR\space as right precedence.}% \fi \ifxintverbose \xintMessage{zeckendorf}{info}{infix operator \ZECK_tmpa\space \ifxintglobaldefs globally \fi does \xint_noxpd{#2}\MessageBreak with precedences \ZECK_tmpL, \ZECK_tmpR;}% \fi }% \def\ZECK_dotheitselves#1#2% {% \if#2\relax\expandafter\xint_gobble_ii \else \XINT_global \expandafter\edef\csname XINT_expr_itself_#1#2\endcsname{#1#2}% \unless\ifcsname XINT_expr_precedence_#1\endcsname \XINT_global \expandafter\edef\csname XINT_expr_precedence_#1\endcsname {\csname XINT_expr_precedence_\ZECK_tmpa\endcsname}% \XINT_global \expandafter\odef\csname XINT_expr_op_#1\endcsname {\csname XINT_expr_op_\ZECK_tmpa\endcsname}% \fi \fi \ZECK_dotheitselves{#1#2}% }% % \end{macrocode} % There is no ``undefine operator'' in \bnumexprname currently. Experimental, % I don't want to spend too much time. I sense there is a potential problem % with multi-character operators related to ``undoing the itselves'', because % of the mechanism which allows to use for example |;;| as short-cut for |;;;| % if |;;| was not pre-defined when |;;;| got defined. To undefine |;;|, I % would need to check if it really has been aliased to |;;;|, and I don't do % the effort here. % \begin{macrocode} \def\ZECK_undefbin_b #1#2% {% \XINT_global\expandafter\let \csname XINT_#1_op_#2\endcsname\xint_undefined \XINT_global\expandafter\let \csname XINT_#1_exec_#2\endcsname\xint_undefined \XINT_global\expandafter\let \csname XINT_#1_check-_#2\endcsname\xint_undefined \XINT_global\expandafter\let \csname XINT_#1_checkp_#2\endcsname\xint_undefined \XINT_global\expandafter\let \csname XINT_expr_precedence_#2\endcsname\xint_undefined \XINT_global\expandafter\let \csname XINT_expr_itself_#2\endcsname\xint_undefined }% \def\zeckundefinfix #1% {% \edef\ZECK_tmpa{#1}% \edef\ZECK_tmpa{\xint_zapspaces_o\ZECK_tmpa}% \ZECK_undefbin_b {expr}\ZECK_tmpa %% \ifxintverbose \xintMessage{zeckendorf}{Warning}{infix operator \ZECK_tmpa\space has been DELETED!}% %% \fi }% \fi \def\ZeckDeleteOperator#1{\zeckundefinfix{#1}}% % \end{macrocode} % Attention, this is like |\bnumdefinfix| and thus does not have same order % of arguments as the |\ZECK_defbin_b| above. % \begin{macrocode} \def\ZeckSetAsKnuthOp#1{\zeckdefinfix{#1}{\ZeckKMulNum}{14}{14}}% \def\ZeckSetAsArnouxOp#1{\zeckdefinfix{#1}{\ZeckAMulNum}{14}{14}}% % \end{macrocode} % \subsubsection{The \textdollar{} and \textdollar{}\textdollar{} as % infix operator for the Knuth multiplication} % \begin{macrocode} \ZeckSetAsArnouxOp{$}% $ (<-only to tame Emacs/AUCTeX highlighting) \ZeckSetAsKnuthOp{$$}% $$ % \end{macrocode} %^^A pourquoi espace disparaît après le {**}? D'où le {}. %^^A mark-up copié collé depuis xintexpr % \subsubsection{Support macros for \texorpdfstring{$\QQ(\phi)$}{Q(phi)} algebra} % \paragraph{\csh{PhiSgn}}\mbox{} % \begin{macrocode} \def\PhiSign{\romannumeral0\phisign}% \def\phisign #1{% \expandafter\phisign_a\expanded{{#1}}% }% \def\phisign_a #1{\expandafter\phisign_b\string#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phisign_b #1[\if#1{\expandafter\phisign_X % } \else\expandafter\phisign_N\fi #1]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phisign_N #1;{\XINT_sgn #1\xint:}% \def\phisign_X #1{\expandafter\phisign_ab\expandafter{\iffalse}\fi}% \def\PhiSign_ab {\romannumeral0\phisign_ab}% \def\phisign_ab #1#2{% \xintiiifsgn{#1}% {% a < 0 \xintiiifSgn{#2}% {-1}% {-1}% {% a < 0, b > 0, return 1 iff a^2+ab 0 \xintiiifSgn{#2}% {% a > 0, b < 0, return 1 iff a^2+ab>b^2 \xintifGt{\xintMul{#1}{\xintAdd{#1}{#2}}}{\xintSqr{#2}}% {1}% {-1}% }% {1}% {1}% }% }% % \end{macrocode} % \paragraph{\csh{PhiAbs}}\mbox{} % \begin{macrocode} \def\PhiAbs{\romannumeral0\phiabs}% \def\phiabs #1{% \expandafter\phiabs_a\expanded{{#1}}% }% \def\phiabs_a #1{\expandafter\phiabs_b\string#1}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phiabs_b #1[\if#1{\expandafter\phiabs_X % } \else\expandafter\phiabs_N\fi #1]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \let\phiabs_N \XINT_abs \def\phiabs_X #1{\expandafter\phiabs_x\expandafter{\iffalse}\fi}% \def\phiabs_x #1#2{\expanded{% \ifnum\PhiSign_ab {#1}{#2}<\xint_c_ \expandafter\xint_firstoftwo\else\expandafter\xint_secondoftwo\fi {{\XINT_Opp#1}{\XINT_Opp#2}}{{#1}{#2}}% }% }% % \end{macrocode} % \paragraph{\csh{PhiNorm}}\mbox{} % \begin{macrocode} \def\PhiNorm{\romannumeral0\phinorm}% \def\phinorm #1{% \expandafter\phinorm_a\expanded{{#1}}% }% \def\phinorm_a #1{\expandafter\phinorm_b\detokenize{#1;}{#1}}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phinorm_b #1#2;[\if#1{\expandafter\phinorm_X % } \else\expandafter\phinorm_N\fi]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \let\phinorm_N\xintsqr \def\phinorm_X #1{\phinorm_x #1}% \def\phinorm_x #1#2{\xintsub {\xintMul{#1}{\xintAdd{#1}{#2}}}% {\xintSqr{#2}}% }% % \end{macrocode} % \paragraph{\csh{PhiConj}}\mbox{} % \begin{macrocode} \def\PhiConj{\romannumeral0\phiconj}% \def\phiconj #1{% \expandafter\phiconj_a\expanded{{#1}}% }% \def\phiconj_a #1{\expandafter\phiconj_b\detokenize{#1;}#1}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phiconj_b #1#2;[\if#1{\expandafter\phiconj_X % } \else\expandafter\phiconj_N\fi]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \let\phiconj_N\space \def\phiconj_X #1#2{\expanded{% {\xintAdd{#1}{#2}}{\XINT_Opp #2}% }}% % \end{macrocode} % \paragraph{\csh{PhiOpp}}\mbox{} % \begin{macrocode} \def\PhiOpp{\romannumeral0\phiopp}% \def\phiopp #1{% \expandafter\phiopp_a\expanded{{#1}}% }% \def\phiopp_a #1{\expandafter\phiopp_b\string#1}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phiopp_b #1[\if#1{\expandafter\phiopp_X % } \else\expandafter\phiopp_N\fi #1]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \let\phiopp_N \XINT_opp \def\phiopp_X #1{\expandafter\phiopp_x\expandafter{\iffalse}\fi}% \def\phiopp_x #1#2{\expanded{% {\XINT_Opp #1}{\XINT_Opp #2}% }}% % \end{macrocode} % \paragraph{\csh{PhiAdd}}\mbox{} % \begin{macrocode} \def\PhiAdd{\romannumeral0\phiadd}% \def\phiadd #1#2{% \expandafter\phiadd_a\expanded{{#1}{#2}}% }% \def\phiadd_a #1{\expandafter\phiadd_b\string#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phiadd_b #1[\if#1{\expandafter\phiadd_X % } \else\expandafter\phiadd_N\fi #1]% \def\phiadd_N #1;#2[\expandafter\phiadd_n\string#2;[#1]]% \def\phiadd_n #1[\if#1{\expandafter\phiadd_nX % } \else\expandafter\phiadd_nn\fi #1]% \def\phiadd_nX #1[\expandafter\phiadd_nx\expandafter[\iffalse]\fi]% \def\phiadd_X #1[\expandafter\phiadd_x\expandafter[\iffalse]\fi]% % \end{macrocode} % |#1={a}{b}|. % \begin{macrocode} \def\phiadd_x #1;#2[\expandafter\phiadd_xa\string#2;#1]% \def\phiadd_xa#1[\if#1{\expandafter\phiadd_XX % } \else\expandafter\phiadd_xn\fi #1]% \def\phiadd_XX #1[\expandafter\phiadd_xx\expandafter[\iffalse]\fi]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phiadd_nn #1;{\xintadd{#1}}% \def\phiadd_nx #1#2;#3{\expandafter {\romannumeral0\xintadd{#1}{#3}}{#2}% }% \def\phiadd_xn #1;#2{\expandafter {\romannumeral0\xintadd{#1}{#2}}% }% \def\phiadd_xx #1#2;#3#4{\expanded{% {\xintAdd{#1}{#3}}% {\xintAdd{#2}{#4}}% }}% % \end{macrocode} % \paragraph{\csh{PhiSub}}\mbox{} % \begin{macrocode} \def\PhiSub{\romannumeral0\phisub}% \def\phisub #1#2{% \expandafter\phisub_a\expanded{{#1}{#2}}% }% \def\phisub_a #1{\expandafter\phisub_b\string#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phisub_b #1[\if#1{\expandafter\phisub_X % } \else\expandafter\phisub_N\fi #1]% \def\phisub_N #1;#2[\expandafter\phisub_n\string#2;[#1]]% \def\phisub_n #1[\if#1{\expandafter\phisub_nX % } \else\expandafter\phisub_nn\fi #1]% \def\phisub_nX #1[\expandafter\phisub_nx\expandafter[\iffalse]\fi]% \def\phisub_X #1[\expandafter\phisub_x\expandafter[\iffalse]\fi]% % \end{macrocode} % |#1={a}{b}|. % \begin{macrocode} \def\phisub_x #1;#2[\expandafter\phisub_xa\string#2;#1]% \def\phisub_xa#1[\if#1{\expandafter\phisub_XX % } \else\expandafter\phisub_xn\fi #1]% \def\phisub_XX #1[\expandafter\phisub_xx\expandafter[\iffalse]\fi]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phisub_nn #1;#2{\xintsub{#2}{#1}}% \def\phisub_nx #1#2;#3{\expanded{% {\xintSub{#3}{#1}}{\XINT_Opp#2}% }}% \def\phisub_xn #1;#2{\expandafter {\romannumeral0\xintsub{#2}{#1}}% }% \def\phisub_xx #1#2;#3#4{\expanded{% {\xintSub{#3}{#1}}% {\xintSub{#4}{#2}}% }}% % \end{macrocode} % \paragraph{\csh{PhiMul}}\mbox{} % \begin{macrocode} \def\PhiMul{\romannumeral0\phimul}% \def\phimul #1#2{% \expandafter\phimul_a\expanded{{#1}{#2}}% }% \def\phimul_a #1{\expandafter\phimul_b\string#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phimul_b #1[\if#1{\expandafter\phimul_X % } \else\expandafter\phimul_N\fi #1]% \def\phimul_N #1;#2[\expandafter\phimul_n\string#2;[#1]]% \def\phimul_n #1[\if#1{\expandafter\phimul_nX % } \else\expandafter\phimul_nn\fi #1]% \def\phimul_nX #1[\expandafter\phimul_nx\expandafter[\iffalse]\fi]% \def\phimul_X #1[\expandafter\phimul_x\expandafter[\iffalse]\fi]% % \end{macrocode} % |#1={a}{b}|. % \begin{macrocode} \def\phimul_x #1;#2[\expandafter\phimul_xa\string#2;#1]% \def\phimul_xa#1[\if#1{\expandafter\phimul_XX % } \else\expandafter\phimul_xn\fi #1]% \def\phimul_XX #1[\expandafter\phimul_xx\expandafter[\iffalse]\fi]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phimul_nn#1;{\xintmul{#1}}% \def\phimul_nx#1#2;#3{\expanded{% {\xintMul{#1}{#3}}{\xintMul{#2}{#3}}% }}% \def\phimul_xn#1;#2#3{\expanded{% {\xintMul{#1}{#2}}{\xintMul{#1}{#3}}% }}% \def\phimul_xx #1#2;#3#4{% \expandafter\phimul_xx_a\expanded{% \xintMul{#1}{#3};% ca \xintMul{#2}{#4};% db \xintMul{#1}{#4};% da \xintMul{#2}{#3};% cb }% }% \def\phimul_xx_a #1;#2;#3;#4;{\expanded{% {\xintAdd{#1}{#2}}% {\xintAdd{#3}{\xintAdd{#4}{#2}}}% }}% % \end{macrocode} % \paragraph{\csh{PhiDiv}}\mbox{} % \begin{macrocode} \def\PhiDiv{\romannumeral0\phidiv}% \def\phidiv #1#2{% \expandafter\phidiv_a\expanded{{#1}{#2}}% }% \def\phidiv_a #1{\expandafter\phidiv_b\string#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phidiv_b #1[\if#1{\expandafter\phidiv_X % } \else\expandafter\phidiv_N\fi #1]% \def\phidiv_N #1;#2[\expandafter\phidiv_n\string#2;[#1]]% \def\phidiv_n #1[\if#1{\expandafter\phidiv_nX % } \else\expandafter\phidiv_nn\fi #1]% \def\phidiv_nX #1[\expandafter\phidiv_nx\expandafter[\iffalse]\fi]% \def\phidiv_X #1[\expandafter\phidiv_x\expandafter[\iffalse]\fi]% % \end{macrocode} % |#1={a}{b}|. % \begin{macrocode} \def\phidiv_x #1;#2[\expandafter\phidiv_xa\string#2;#1]% \def\phidiv_xa#1[\if#1{\expandafter\phidiv_XX % } \else\expandafter\phidiv_xn\fi #1]% \def\phidiv_XX #1[\expandafter\phidiv_xx\expandafter[\iffalse]\fi]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phidiv_nn#1;#2{\xintdiv{#2}{#1}}% \def\phidiv_nx#1#2{% \expandafter\phidiv_nx_a\expanded{% {\xintSub{\xintMul{#1}{\xintAdd{#1}{#2}}}{\xintSqr{#2}}}% }{#1}{#2}% }% \def\phidiv_nx_a#1#2#3;#4{\expanded{% {\xintIrr{\xintDiv{\xintMul{#4}{\xintAdd{#2}{#3}}}{#1}}[0]}% {\xintIrr{\xintOpp{\xintDiv{\xintMul{#4}{#3}}{#1}}}[0]}% }}% \def\phidiv_xn #1;#2#3{\expanded{% {\xintIrr{\xintDiv{#2}{#1}}[0]}% {\xintIrr{\xintDiv{#3}{#1}}[0]}% }}% \def\phidiv_xx #1#2;#3#4{% \expandafter\phidiv_xx_a\expanded{% \expandafter\phimul_xx \expanded{{\xintAdd{#1}{#2}}{\XINT_Opp #2}};{#3}{#4}% {\xintSub{\xintMul{#1}{\xintAdd{#1}{#2}}}{\xintSqr{#2}}}% }% }% \def\phidiv_xx_a #1#2#3{% \expanded{% {\xintIrr{\xintDiv{#1}{#3}}[0]}% {\xintIrr{\xintDiv{#2}{#3}}[0]}% }% }% % \end{macrocode} % \paragraph{\csh{PhiPow}}\mbox{} % \begin{macrocode} \def\PhiPow{\romannumeral0\phipow}% \def\phipow #1#2{% \expandafter\phipow_a\expanded {{#1}{\xintNum{#2}}}% }% \def\phipow_a #1{\expandafter\phipow_b\string#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phipow_b #1[\if#1{\expandafter\phipow_X % } \else\expandafter\phipow_N\fi #1]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phipow_N #1;{\xintpow{#1}}% \def\phipow_X #1{\expandafter\phipow_x\expandafter{\iffalse}\fi}% % \end{macrocode} % Let's handle negative exponents too, now that we use |\xinteval|. % \begin{macrocode} \def\phipow_x #1;#2{\phipow_fork #2;#1}% \def\phipow_fork #1{% \xint_UDzerominusfork 0#1\phipow_neg #1-\phipow_zero 0-\phipow_pos \krof #1% }% \def\phipow_zero 0;#1#2{{1}{0}}% \def\phipow_neg -{% \expandafter\phiinv_ab\romannumeral0\phipow_pos }% \def\phiinv_ab #1#2{% \expandafter\phiinv_c\expanded{% {\xintSub{\xintMul{#1}{\xintAdd{#1}{#2}}}{\xintSqr{#2}}}% }{#1}{#2}% }% \def\phiinv_c #1#2#3{\expanded{% {\xintIrr{\xintDiv{\xintAdd{#2}{#3}}{#1}}[0]}% {\xintIrr{\xintOpp{\xintDiv{#3}{#1}}}[0]}% }}% \def\phipow_pos #1;{% \expandafter\phipow_xa \expanded{10\xintDecToBin{#1}},;% }% \def\phipow_xa #1#2#3#4;{% \if#3,\expandafter\phipow_done\fi \if#31\expandafter\phipow_xo \else\expandafter\phipow_xe\fi {#1}{#2}#4;% }% \def\phipow_done \if#1\fi #2#3;#4#5{{#2}{#3}}% \def\phipow_xo #1#2{% \expandafter\phipow_xo_a\expanded{% \xintSqr{#1};\xintMul{#1}{#2};\xintSqr{#2};% }% }% \def\phipow_xo_a #1;#2;#3;{% \expandafter\phipow_xo_b\expanded{% \xintAdd{#1}{#3};\xintAdd{#2}{\xintAdd{#2}{#3}};% }% }% \def\phipow_xo_b#1;#2;#3;#4#5{% \expandafter\phipow_xa\romannumeral0% \phimul_xx {#1}{#2};{#4}{#5}#3;{#4}{#5}% }% \def\phipow_xe #1#2{% \expandafter\phipow_xe_a\expanded{% \xintSqr{#1};\xintMul{#1}{#2};\xintSqr{#2};% }% }% \def\phipow_xe_a #1;#2;#3;{% \expandafter\phipow_xa\expanded{% {\xintAdd{#1}{#3}}{\xintAdd{#2}{\xintAdd{#2}{#3}}}% }% }% % \end{macrocode} % \subsubsection{Overloading \texttt{+}, % \texttt{\textendash}, \texorpdfstring{\texttt{\protect\lowast}}{*}, % \texttt{/}, % \texttt{\textasciicircum}, % and \texorpdfstring{\texttt{\protect\lowast\protect\lowast}}{**}} % % The |**| is pre-aliased to |^| at \xintexprname level via % |\XINT_expr_itself_**|, so nothing to do here once |^| is handled. % % The unary |-| requires extra care. % \begin{macrocode} \zeckdefinfix{+}{\PhiAdd}{12}{12}% \zeckdefinfix{-}{\PhiSub}{12}{12}% \xintFor #1 in {xii,xiv,xvii}\do{% \expandafter\def\csname XINT_expr_exec_-#1\endcsname ##1##2##3% {% \expandafter ##1\expandafter ##2\expandafter {% \romannumeral`&&@\XINT:NEhook:f:one:from:one {\romannumeral`&&@\PhiOpp##3}% }% }% }% \zeckdefinfix{*}{\PhiMul}{14}{14}% \zeckdefinfix{/}{\PhiDiv}{14}{14}% \zeckdefinfix{^}{\PhiPow}{18}{17}% % \end{macrocode} % \subsubsection{Variables and functions for \cs{xinteval}} % % The macros computing Fibonacci numbers, Zeckendorf indices, and Bergman % exponents, were done originally assuming to be used with arguments in strict % integer format. But when operations are executed in |\xinteval| the % intermediate results will use the ``raw'' format described in the % \xintexprname manual, not the ``strict integer format''. We thus need % wrappers to apply |\xintNum| for normalization, even though this adds annoying % overhead. These wrappers can assume that the argument is already expanded. % % For macros handling input being either one unbraced integer or a pair of % braced integers this is more complicated. We separated |\PhiIISign_ab| from % |\PhiSign| to this aim. The former for optimized internal usage, only using % integer algebra. The latter uses the \xintfracname macros, so there is % no problem and we do not want to truncate arguments to integers. Similarly for % |\PhiAbs| no need to do something special. % % |\PhiMaxE| is integer-only, but in the end I decided to not provide an % |\xinteval| interface and to remove the one for |\ZeckMaxK|. % % For the support for |phiexponents()|, which is also integer only we have to % use |\xintNum|, the problem is that we can't do that prior to know if used % with an integer or a nutple. So |\Phi@BList| was done to handle that. % \begin{macrocode} \xintdefvar phi:=[0,1];% \xintdefvar psi:=[1,-1];% \def\XINT_expr_func_phinorm #1#2#3% {% \expandafter #1\expandafter #2\expandafter{% \romannumeral`&&@\XINT:NEhook:f:one:from:one {\romannumeral`&&@\PhiNorm#3}}% }% \def\XINT_expr_func_phiconj #1#2#3% {% \expandafter #1\expandafter #2\expandafter{% \romannumeral`&&@\XINT:NEhook:f:one:from:one {\romannumeral`&&@\PhiConj#3}}% }% \def\XINT_expr_func_phisign #1#2#3% {% \expandafter #1\expandafter #2\expandafter{% \romannumeral`&&@\XINT:NEhook:f:one:from:one {\romannumeral`&&@\PhiSign#3}}% }% \def\XINT_expr_func_phiabs #1#2#3% {% \expandafter #1\expandafter #2\expandafter{% \romannumeral`&&@\XINT:NEhook:f:one:from:one {\romannumeral`&&@\PhiAbs#3}}% }% \def\ZeckTheFNNum#1{\ZeckTheFN{\xintNum{#1}}}% \def\XINT_expr_func_fib #1#2#3% {% \expandafter #1\expandafter #2\expandafter{% \romannumeral`&&@\XINT:NEhook:f:one:from:one {\romannumeral`&&@\ZeckTheFNNum#3}}% }% \def\ZeckTheFSeqNum#1#2{\ZeckTheFSeq{\xintNum{#1}}{\xintNum{#2}}}% \def\XINT_expr_func_fibseq #1#2#3% {% \expandafter #1\expandafter #2\expandafter{% \romannumeral`&&@\XINT:NEhook:f:one:from:two {\romannumeral`&&@\ZeckTheFSeqNum#3}}% }% \def\ZeckBListNum #1{% \expanded\bgroup\expandafter\zeckblist_fork\romannumeral0\xintnum{#1}\xint: }% \def\XINT_expr_func_zeckindices #1#2#3% {% \expandafter #1\expandafter #2\expandafter{% \romannumeral`&&@\XINT:NEhook:f:one:from:one {\romannumeral`&&@\ZeckBListNum#3}}% }% % \end{macrocode} % TODO: I have forgotten now but I vaguely remember if compatibility % with usage of the defined function in |\xintdeffunc| is hoped for % that it should first expand its argument even though in our context % if purely numerical this is unneeded (and f-expansion will % end up hitting a brace if the input is a nutple). Adding anyhow. % I have other things in mind currently, to examine later, already % quite enough hours on this package. % \begin{macrocode} \def\Phi@BList#1{\expandafter\expandafter\expandafter \phi@blist_b\expandafter\string\romannumeral`&&@#1;}% \catcode`[=1 \catcode`]=2 \catcode`\{=12 % } \def\phi@blist_b #1[\if#1{\expandafter\phi@blist_X % } \else\expandafter\phi@blist_N\fi #1]% \catcode`[=12 \catcode`]=12 \catcode`\{=1 % } \def\phi@blist_N #1;{% \expandafter\xint_gobble_i\expanded \expandafter\phiblist_ab \expanded{{\xintNum{#1}}}{0};% }% \def\phi@blist_X #1{% \expandafter\phi@blist_x\expandafter{\iffalse}\fi }% \def\phi@blist_x #1#2;{% \expandafter\xint_gobble_i\expanded \expandafter\phiblist_ab \expanded{{\xintNum{#1}}{\xintNum{#2}}};% }% \def\XINT_expr_func_phiexponents #1#2#3% {% \expandafter #1\expandafter #2\expandafter{% \romannumeral`&&@\XINT:NEhook:f:one:from:one {\romannumeral`&&@\Phi@BList#3}}% }% % \end{macrocode} % ATTENTION! we leave the modified catcodes in place! (the question mark % has regained its catcode other though). % % \catcode`\<=0 \catcode`\>=11 \catcode`\*=11 \catcode`\/=11 % \let\relax % \def<*interactive>{\catcode`\<=12 \catcode`\>=12 \catcode`\*=12 \catcode`\/=12 } % %<*interactive> % \makeatletter\global\c@CodelineNo\z@\makeatother % \section{Interactive code} % \label{sec:code:interactive} % Extracts to |zeckendorf.tex|. % \begin{macrocode} \input zeckendorfcore.tex \let\xintfirstoftwo\xint_firstoftwo \let\xintsecondoftwo\xint_secondoftw \let\zeckexprmapwithin\XINT:expr:mapwithin \def\zeckNumbraced#1{{\xintNum{#1}}} \xintexprSafeCatcodes \let\ZeckShouldISayOrShouldIGo\iftrue \def\ZeckCmdQ{\let\ZeckShouldISayOrShouldIGo\iffalse} \let\ZeckCmdX\ZeckCmdQ \let\ZeckCmdx\ZeckCmdQ \let\ZeckCmdq\ZeckCmdQ \newif\ifzeckphimode \newif\ifzeckindices \zeckindicestrue \newif\ifzeckfromN \zeckfromNtrue \newif\ifzeckmeasuretimes \newif\ifzeckevalonly \newif\ifzeckhex \def\ZeckCmdP{% \zeckphimodetrue \ifzeckindices\ZeckCmdL\else\Zeck@CmdB\fi } \let\ZeckCmdp\ZeckCmdP \def\ZeckCmdZ{% \zeckphimodefalse \ifzeckindices\ZeckCmdL\else\Zeck@CmdB\fi } \let\ZeckCmdz\ZeckCmdZ \def\PhiTypesetXPrint #1#2{a=#1, b=#2} \def\ZeckCmdL{% \zeckindicestrue \ifzeckphimode \def\ZeckFromN{\PhiExponents}% \def\ZeckToN##1{\PhiTypesetX{\PhiXfromExponents{##1}}}% \else \def\ZeckFromN{\ZeckIndices}% \def\ZeckToN{\ZeckNFromIndices}% \fi } \let\ZeckCmdl\ZeckCmdL \def\ZeckCmdB{% \zeckindicesfalse \zeckhexfalse \Zeck@CmdB } \def\Zeck@CmdB{% \ifzeckphimode \ifzeckhex \def\ZeckFromN{\PhiBaseHexPhi}% \def\ZeckToN##1{\PhiTypesetX{\PhiXfromBaseHexPhi{##1}}}% \else \def\ZeckFromN{\PhiBasePhi}% \def\ZeckToN##1{\PhiTypesetX{\PhiXfromBasePhi{##1}}}% \fi \else \ifzeckhex \def\ZeckFromN{\ZeckHexWord}% \def\ZeckToN{\ZeckNfromHexWord}% \else \def\ZeckFromN{\ZeckWord}% \def\ZeckToN{\ZeckNfromWord}% \fi \fi } \let\ZeckCmdW\ZeckCmdB \let\ZeckCmdb\ZeckCmdB \let\ZeckCmdw\ZeckCmdB \def\ZeckCmdC{% \zeckindicesfalse \zeckhextrue \Zeck@CmdB } \let\ZeckCmdc\ZeckCmdC \def\ZeckConvert{% \csname Zeck\ifzeckfromN From\else To\fi N\endcsname } \def\ZeckCmdT{\ifzeckfromN\zeckfromNfalse\else\zeckfromNtrue\fi} \let\ZeckCmdt\ZeckCmdT \expandafter\def\csname ZeckCmd@\endcsname{% \ifdefined\xinttheseconds \ifzeckmeasuretimes\zeckmeasuretimesfalse \else \zeckmeasuretimestrue \fi \else \immediate\write128{Sorry, this requires xintexpr 1.4n or later.}% \fi } \def\ZeckCmdE{\ifzeckevalonly\zeckevalonlyfalse\else\zeckevalonlytrue\fi} \let\ZeckCmde\ZeckCmdE \def\ZeckCmdH{\immediate\write128{\ZeckHelpPanel}} \let\ZeckCmdh\ZeckCmdH \ZeckCmdL \def\ZeckCommands{Enter input or command (q, z, p, l, w, b, c, t, e, @, or h for help).} \def\ZeckPrompt{% \ifzeckevalonly <<>>^^J% [IN] expression = \else \ifzeckfromN \ifzeckphimode \ifzeckindices ^^J% \else \ifzeckhex ^^J% \else ^^J% \fi \fi \ZeckCommands^^J [IN] a + b phi = \else \ifzeckindices ^^J% \else \ifzeckhex ^^J% \else ^^J% \fi \fi \ZeckCommands^^J [IN] N = \fi \else \ifzeckphimode ^^J \ZeckCommands^^J [IN] \ifzeckindices phi exponents = \else \ifzeckhex hexphi-representation = \else phi-representation = \fi \fi \else ^^J \ZeckCommands^^J [IN] \ifzeckindices indices = \else \ifzeckhex hex word = \else binary word = \fi \fi \fi \fi \fi } \newlinechar10 \immediate\write128{} \immediate\write128{Welcome to Zeckendorf 0.9d (2025/11/16, JFB).} \def\ZeckHelpPanel{Commands (lowercase also):^^J Q to quit. Also X.^^J H for this help.^^J Z to switch to Zeckendorf-mode (starting default).^^J P to switch to phi-mode.^^J L for indices or exponents.^^J W for binary words or reps. Also B.^^J C for hexadecimal words or reps.^^J T to toggle the direction of conversions.^^J E to toggle to and from \string\xinteval-only mode.^^J @ to toggle measurement of execution times.^^J ^^J - binary words, phi-representations, are parsed only by \string\edef.^^J - all other inputs are handled by \noexpand\xinteval so for example one^^J \space\space can use 2^100 or 100! or binomial(100,50). And a list of indices^^J \space\space or exponents can be for example seq(3*a+1, a=0..10).^^J ^^J \space\space The fib() function computes Fibonacci numbers.^^J \space\space The character $ serves as symbol for Knuth multiplication.^^J% **** empty input is not supported! no linebreaks in input! ****} \immediate\write128{\ZeckHelpPanel} \def\zeckpar{\par} \long\def\xintbye#1\xintbye{} \long\def\zeckgobbleii#1#2{} \long\def\zeckfirstoftwo#1#2{#1} \def\zeckonlyonehelper #1#2#3% \zeckonlyonehelper{\xintbye#2\zeckgobbleii\xintbye0} \xintFor*#1 in {0123456789}\do{% \expandafter\def\csname ZeckCmd#1\endcsname{% \immediate\write128{% ** Due to under-funding, a lone #1 is not accepted. Inputs must have^^J% ** two characters at least. Think about a donation? Try 0#1.}} }% \xintloop \message{\ZeckPrompt} \read-1to\zeckbuf \ifx\zeckbuf\zeckpar \immediate\write128{**** empty input is not supported, please try again.} \else \edef\zeckbuf{\zeckbuf} % \end{macrocode} % Space token at end of |\zeckbuf| is annoying. We could have used % |\xintLength| which does not count space tokens. % \begin{macrocode} \if 1\expandafter\zeckonlyonehelper\zeckbuf\xintbye\zeckonlyonehelper1% \ifcsname ZeckCmd\expandafter\zeckfirstoftwo\zeckbuf\relax\endcsname \csname ZeckCmd\expandafter\zeckfirstoftwo\zeckbuf\relax\endcsname \else \immediate\write128{% **** Unrecognized command letter \expandafter\zeckfirstoftwo\zeckbuf\relax. Try again.^^J} \fi \else % \end{macrocode} % Using the conditional so that this can also be used by default % with older xint. % % With |0.9b| the time needed for parsing the input was not counted, % but this meant that measuring in the evaluation-only mode always % printed |0.0s|. % % |0.9c| has refactored here entirely. % \begin{macrocode} \ifzeckmeasuretimes\xintresettimer\fi \if1\ifzeckevalonly0\fi\ifzeckfromN0\fi\ifzeckindices0\fi1% \edef\ZeckIn{{\zeckbuf}}% \else \expandafter\def\expandafter\ZeckIn\expandafter{% \romannumeral0\xintbareeval\zeckbuf\relax}% % \end{macrocode} % |0.9c| uses |\xinteval|. It adds phi-mode to the interactive interface, but % as |1/phi| or anything doing an operation will inject ``raw xintfrac % format'', we have to be careful about that, because we use |\PhiExponents| % and |\PhiBasePhi| which are assuming being used with either an integer |a| % or a pair |{a}{b}|. Using here some core level auxiliary from \xintexprname % to avoid a dozen lines like what was done for |\Phi@BList|. For this to % work we need a variant of |\xintNum| which outputs with extra braces. This % was for the author a refreshing journey to revisit forgotten deep code % written years ago for \xintexprname. But it would be more efficient to do % something akin to the |\Phi@BList| business. % % By the way we have to do this not only for phi-mode, but also for % integer-mode, because some input such as |1e40| will be internally |1[40]| % which |\ZeckIndices| does not understand as it does not apply |\xintNum|. In % fact any input doing an operation such as an addition will be in ``raw % xintfrac format'' internally. So we have to do a normalization also for % lists of exponents or indices. % \begin{macrocode} \ifzeckevalonly\else \expandafter\def\expandafter\ZeckIn \expanded \expandafter\zeckexprmapwithin \expandafter\zeckNumbraced\ZeckIn % \end{macrocode} % For lists of exponents and indices the predefined macros expect % comma separated lists. We can either "print" using (full) |\xinteval|, % or use |\xintListwithSep|, or write a little helper requiring % only |\edef| expansion. We add one level of bracing removed later. % \begin{macrocode} \ifzeckfromN\else \expandafter\def\expandafter\ZeckIn\expandafter{% \expandafter{\romannumeral0\xintlistwithsep,\ZeckIn}% }% \fi \fi \fi \immediate\write128{% [OUT] \ifzeckevalonly \expanded\expandafter\XINTexprprint\expandafter.\ZeckIn \else \expandafter\ZeckConvert\ZeckIn \fi }% \ifzeckmeasuretimes \edef\tmp{\xinttheseconds}% \immediate\write128{% \ifzeckevalonly Evaluation \else Conversion \fi took \tmp s% }% \fi \fi \fi \ZeckShouldISayOrShouldIGo \repeat \immediate\write128{Bye. Session was saved to log file (hard-wrapped too, alas).} \bye % \end{macrocode} % \catcode`\<=0 \catcode`\>=11 \catcode`\*=11 \catcode`\/=11 % \let\relax % \def<*sty>{\catcode`\<=12 \catcode`\>=12 \catcode`\*=12 \catcode`\/=12 } % %<*sty> % \makeatletter\global\c@CodelineNo\z@\makeatother % \section{\LaTeX{} code} % Extracts to |zeckendorf.sty|. % \label{sec:code:latex} % \begin{macrocode} \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{zeckendorf} [2025/11/16 v0.9d Zeckendorf and base-phi representations of big integers (JFB)]% \RequirePackage{xintexpr} \RequirePackage{xintbinhex}% superfluous if with xint 1.4n or later \input zeckendorfcore.tex \ZECKrestorecatcodesendinput% % \end{macrocode} % \catcode`\<=0 \catcode`\>=11 \catcode`\*=11 \catcode`\/=11 % \let\relax % \def<*dtx>{\catcode`\<=12 \catcode`\>=12 \catcode`\*=12 \catcode`\/=12 } % %<*dtx> % \MakePercentComment \CheckSum {3064}% \makeatletter\check@checksum\makeatother% \Finale% %% End of file zeckendorf.dtx