Miscellaneous stuff - libzahl - big integer library
 (HTM) git clone git://git.suckless.org/libzahl
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 16a001c8fe4e5ca99d5aafdd8ed02a35f09b6caa
 (DIR) parent c98c0af42f56d0859d6f3f5e3743945906c681c8
 (HTM) Author: Mattias Andrée <maandree@kth.se>
       Date:   Fri, 13 May 2016 04:38:09 +0200
       
       Miscellaneous stuff
       
       Signed-off-by: Mattias Andrée <maandree@kth.se>
       
       Diffstat:
         M Makefile                            |       6 +++++-
         M TODO                                |       4 ++++
         M doc/arithmetic.tex                  |       5 +++--
         A doc/bit-operations.tex              |      56 +++++++++++++++++++++++++++++++
         M doc/libzahl.tex                     |       4 ++++
         A doc/not-implemented.tex             |     554 +++++++++++++++++++++++++++++++
         A doc/number-theory.tex               |      35 +++++++++++++++++++++++++++++++
         A doc/random-numbers.tex              |      28 ++++++++++++++++++++++++++++
         M src/zmodmul.c                       |       1 +
         M src/zmodpow.c                       |       2 ++
         M src/zmodpowu.c                      |       5 +++--
         M src/zpow.c                          |       2 ++
         M src/zpowu.c                         |       5 +++--
       
       13 files changed, 700 insertions(+), 7 deletions(-)
       ---
 (DIR) diff --git a/Makefile b/Makefile
       @@ -78,7 +78,11 @@ TEXSRC =\
                doc/libzahls-design.tex\
                doc/get-started.tex\
                doc/miscellaneous.tex\
       -        doc/arithmetic.tex
       +        doc/arithmetic.tex\
       +        doc/bit-operations.tex\
       +        doc/number-theory.tex\
       +        doc/random-numbers.tex\
       +        doc/not-implemented.tex
        
        HDR_PUBLIC = zahl.h $(HDR_SEMIPUBLIC)
        HDR        = $(HDR_PUBLIC) $(HDR_PRIVATE)
 (DIR) diff --git a/TODO b/TODO
       @@ -4,6 +4,10 @@ It uses optimised division algorithm that requires that d|n.
        Add zsets_radix
        Add zstr_radix
        
       +Can zmodpowu and zmodpow be improved using some other algorithm?
       +Is it worth implementing precomputed optimal
       +  addition-chain exponentiation in zpowu?
       +
        Test big endian
        Test always having .used > 0 for zero
          Test negative/non-negative instead of sign
 (DIR) diff --git a/doc/arithmetic.tex b/doc/arithmetic.tex
       @@ -186,8 +186,9 @@ can be expressed as a simple formula
        
        \vspace{-1em}
        \[ \hspace*{-0.4cm}
       -    a^b = \prod_{i = 0}^{\lceil \log_2 b \rceil}
       -    \left ( a^{2^i} \right )^{\left \lfloor {\displaystyle{b \over 2^i}} \hspace*{-1ex} \mod 2 \right \rfloor}
       +    a^b =
       +    \prod_{k \in \textbf{Z}_{+} ~:~ \left \lfloor {b \over 2^k} \hspace*{-1ex} \mod 2 \right \rfloor = 1}
       +    a^{2^k}
        \]
        
        \noindent
 (DIR) diff --git a/doc/bit-operations.tex b/doc/bit-operations.tex
       @@ -0,0 +1,56 @@
       +\chapter{Bit operations}
       +\label{chap:Bit operations}
       +
       +TODO
       +
       +\vspace{1cm}
       +\minitoc
       +
       +
       +\newpage
       +\section{Boundary}
       +\label{sec:Boundary}
       +
       +TODO % zbits zlsb
       +
       +
       +\newpage
       +\section{Logic}
       +\label{sec:Logic}
       +
       +TODO % zand zor zxor znot
       +
       +
       +\newpage
       +\section{Shift}
       +\label{sec:Shift}
       +
       +TODO % zlsh zrsh
       +
       +
       +\newpage
       +\section{Truncation}
       +\label{sec:Truncation}
       +
       +TODO % ztrunc
       +
       +
       +\newpage
       +\section{Split}
       +\label{sec:Split}
       +
       +TODO % zsplit
       +
       +
       +\newpage
       +\section{Bit manipulation}
       +\label{sec:Bit manipulation}
       +
       +TODO % zbset
       +
       +
       +\newpage
       +\section{Bit test}
       +\label{sec:Bit test}
       +
       +TODO % zbtest
 (DIR) diff --git a/doc/libzahl.tex b/doc/libzahl.tex
       @@ -82,6 +82,10 @@ all copies or substantial portions of the Document.
        \input doc/get-started.tex
        \input doc/miscellaneous.tex
        \input doc/arithmetic.tex
       +\input doc/bit-operations.tex
       +\input doc/number-theory.tex
       +\input doc/random-numbers.tex
       +\input doc/not-implemented.tex
        
        
        \appendix
 (DIR) diff --git a/doc/not-implemented.tex b/doc/not-implemented.tex
       @@ -0,0 +1,554 @@
       +\chapter{Not implemented}
       +\label{chap:Not implemented}
       +
       +In this chapter we maintain a list of
       +features we have choosen not to implement,
       +but would fit into libzahl had we not have
       +our priorities straight. Functions listed
       +herein will only be implemented if there
       +is shown that it would be overwhelmingly
       +advantageous.
       +
       +\vspace{1cm}
       +\minitoc
       +
       +
       +\newpage
       +\section{Extended greatest common divisor}
       +\label{sec:Extended greatest common divisor}
       +
       +\begin{alltt}
       +void
       +extgcd(z_t bézout_coeff_1, z_t bézout_coeff_2, z_t gcd
       +       z_t quotient_1, z_t quotient_2, z_t a, z_t b)
       +\{
       +#define old_r gcd
       +#define old_s bézout_coeff_1
       +#define old_t bézout_coeff_2
       +#define s quotient_2
       +#define t quotient_1
       +    z_t r, q, qs, qt;
       +    int odd = 0;
       +    zinit(r), zinit(q), zinit(qs), zinit(qt);
       +    zset(r, b), zset(old_r, a);
       +    zseti(s, 0), zseti(old_s, 1);
       +    zseti(t, 1), zseti(old_t, 0);
       +    while (!zzero(r)) \{
       +        odd ^= 1;
       +        zdivmod(q, old_r, old_r, r), zswap(old_r, r);
       +        zmul(qs, q, s), zsub(old_s, old_s, qs);
       +        zmul(qt, q, t), zsub(old_t, old_t, qt);
       +        zswap(old_s, s), zswap(old_t, t);
       +    \}
       +    odd ? abs(s, s) : abs(t, t);
       +    zfree(r), zfree(q), zfree(qs), zfree(qt);
       +\}
       +\end{alltt}
       +
       +
       +\newpage
       +\section{Least common multiple}
       +\label{sec:Least common multiple}
       +
       +\( \displaystyle{
       +    \mbox{lcm}(a, b) = {\lvert a \cdot b \rvert \over \mbox{gcd}(a, b)}
       +}\)
       +
       +
       +\newpage
       +\section{Modular multiplicative inverse}
       +\label{sec:Modular multiplicative inverse}
       +
       +\begin{alltt}
       +int
       +modinv(z_t inv, z_t a, z_t m)
       +\{
       +    z_t x, _1, _2, _3, gcd, mabs, apos;
       +    int invertible, aneg = zsignum(a) < 0;
       +    zinit(x), zinit(_1), zinit(_2), zinit(_3), zinit(gcd);
       +    *mabs = *m;
       +    zabs(mabs, mabs);
       +    if (aneg) \{
       +        zinit(apos);
       +        zset(apos, a);
       +        if (zcmpmag(apos, mabs))
       +            zmod(apos, apos, mabs);
       +        zadd(apos, mabs, apos);
       +    \}
       +    extgcd(inv, _1, _2, _3, gcd, apos, mabs);
       +    if ((invertible = !zcmpi(gcd, 1))) \{
       +        if (zsignum(inv) < 0)
       +            (zsignum(m) < 0 ? zsub : zadd)(x, x, m);
       +        zswap(x, inv);
       +    \}
       +    if (aneg)
       +        zfree(apos);
       +    zfree(x), zfree(_1), zfree(_2), zfree(_3), zfree(gcd);
       +    return invertible;
       +\}
       +\end{alltt}
       +
       +
       +\newpage
       +\section{Random prime number generation}
       +\label{sec:Random prime number generation}
       +
       +TODO
       +
       +
       +\newpage
       +\section{Symbols}
       +\label{sec:Symbols}
       +
       +\subsection{Legendre symbol}
       +\label{sec:Legendre symbol}
       +
       +TODO
       +
       +
       +\subsection{Jacobi symbol}
       +\label{sec:Jacobi symbol}
       +
       +TODO
       +
       +
       +\subsection{Kronecker symbol}
       +\label{sec:Kronecker symbol}
       +
       +TODO
       +
       +
       +\subsection{Power residue symbol}
       +\label{sec:Power residue symbol}
       +
       +TODO
       +
       +
       +\subsection{Pochhammer \emph{k}-symbol}
       +\label{sec:Pochhammer k-symbol}
       +
       +\( \displaystyle{
       +    (x)_{n,k} = \prod_{i = 1}^n (x + (i - 1)k)
       +}\)
       +
       +
       +\newpage
       +\section{Logarithm}
       +\label{sec:Logarithm}
       +
       +TODO
       +
       +
       +\newpage
       +\section{Roots}
       +\label{sec:Roots}
       +
       +TODO
       +
       +
       +\newpage
       +\section{Modular roots}
       +\label{sec:Modular roots}
       +
       +TODO % Square: Cipolla's algorithm, Pocklington's algorithm, Tonelli–Shanks algorithm
       +
       +
       +\newpage
       +\section{Combinatorial}
       +\label{sec:Combinatorial}
       +
       +\subsection{Factorial}
       +\label{sec:Factorial}
       +
       +\( \displaystyle{
       +    n! = \left \lbrace \begin{array}{ll}
       +        \displaystyle{\prod_{i = 0}^n i} & \textrm{if}~ n \ge 0 \\
       +        \textrm{undefined} & \textrm{otherwise}
       +    \end{array} \right .
       +}\)
       +\vspace{1em}
       +
       +This can be implemented much more efficently
       +than using the naïve method, and is a very
       +important function for many combinatorial
       +applications, therefore it may be implemented
       +in the future if the demand is high enough.
       +
       +
       +\subsection{Subfactorial}
       +\label{sec:Subfactorial}
       +
       +\( \displaystyle{
       +    !n = \left \lbrace \begin{array}{ll}
       +      n(!(n - 1)) + (-1)^n & \textrm{if}~ n > 0 \\
       +      1 & \textrm{if}~ n = 0 \\
       +      \textrm{undefined} & \textrm{otherwise}
       +    \end{array} \right . =
       +    n! \sum_{i = 0}^n {(-1)^i \over i!}
       +}\)
       +
       +
       +\subsection{Alternating factorial}
       +\label{sec:Alternating factorial}
       +
       +\( \displaystyle{
       +    \mbox{af}(n) = \sum_{i = 1}^n {(-1)^{n - i} i!}
       +}\)
       +
       +
       +\subsection{Multifactorial}
       +\label{sec:Multifactorial}
       +
       +\( \displaystyle{
       +    n!^{(k)} = \left \lbrace \begin{array}{ll}
       +      1 & \textrm{if}~ n = 0 \\
       +      n & \textrm{if}~ 0 < n \le k \\
       +      n((n - k)!^{(k)}) & \textrm{if}~ n > k \\
       +      \textrm{undefined} & \textrm{otherwise}
       +    \end{array} \right .
       +}\)
       +
       +
       +\subsection{Quadruple factorial}
       +\label{sec:Quadruple factorial}
       +
       +\( \displaystyle{
       +    (4n - 2)!^{(4)}
       +}\)
       +
       +
       +\subsection{Superfactorial}
       +\label{sec:Superfactorial}
       +
       +\( \displaystyle{
       +    \mbox{sf}(n) = \prod_{k = 1}^n k^{1 + n - k}
       +}\), undefined for $n < 0$.
       +
       +
       +\subsection{Hyperfactorial}
       +\label{sec:Hyperfactorial}
       +
       +\( \displaystyle{
       +    H(n) = \prod_{k = 1}^n k^k
       +}\), undefined for $n < 0$.
       +
       +
       +\subsection{Raising factorial}
       +\label{sec:Raising factorial}
       +
       +\( \displaystyle{
       +    x^{(n)} = {(x + n - 1)! \over (x - 1)!}
       +}\), undefined for $n < 0$.
       +
       +
       +\subsection{Failing factorial}
       +\label{sec:Failing factorial}
       +
       +\( \displaystyle{
       +    (x)_n = {x! \over (x - n)!}
       +}\), undefined for $n < 0$.
       +
       +
       +\subsection{Primorial}
       +\label{sec:Primorial}
       +
       +\( \displaystyle{
       +    n\# = \prod_{\lbrace i \in \textbf{P} ~:~ i \le n \rbrace} i
       +}\)
       +\vspace{1em}
       +
       +\noindent
       +\( \displaystyle{
       +    p_n\# = \prod_{i \in \textbf{P}_{\pi(n)}} i
       +}\)
       +
       +
       +\subsection{Gamma function}
       +\label{sec:Gamma function}
       +
       +$\Gamma(n) = (n - 1)!$, undefined for $n \le 0$.
       +
       +
       +\subsection{K-function}
       +\label{sec:K-function}
       +
       +\( \displaystyle{
       +    K(n) = \left \lbrace \begin{array}{ll}
       +      \displaystyle{\prod_{i = 1}^{n - 1} i^i}  & \textrm{if}~ n \ge 0 \\
       +      1 & \textrm{if}~ n = -1 \\
       +      0 & \textrm{otherwise (result is truncated)}
       +    \end{array} \right .
       +}\)
       +
       +
       +\subsection{Binomial coefficient}
       +\label{sec:Binomial coefficient}
       +
       +\( \displaystyle{
       +    {n \choose k} = {n! \over k!(n - k)!}
       +    = {1 \over (n - k)!} \prod_{i = k + 1}^n i
       +    = {1 \over k!} \prod_{i = n - k + 1}^n i
       +}\)
       +
       +
       +\subsection{Catalan number}
       +\label{sec:Catalan number}
       +
       +\( \displaystyle{
       +    C_n = \left . {2n \choose n} \middle / (n + 1) \right .
       +}\)
       +
       +
       +\subsection{Fuss–Catalan number}
       +\label{sec:Fuss-Catalan number} % not en dash
       +
       +\( \displaystyle{
       +    A_m(p, r) = {r \over mp + r} {mp + r \choose m}
       +}\)
       +
       +
       +\newpage
       +\section{Fibonacci numbers}
       +\label{sec:Fibonacci numbers}
       +
       +Fibonacci numbers can be computed efficiently
       +using the following algorithm:
       +
       +\begin{alltt}
       +   static void
       +   fib_ll(z_t f, z_t g, z_t n)
       +   \{
       +       z_t a, k;
       +       int odd;
       +       if (zcmpi(n, 1) <= 1) \{
       +           zseti(f, !zzero(n));
       +           zseti(f, zzero(n));
       +           return;
       +       \}
       +       zinit(a), zinit(k);
       +       zrsh(k, n, 1);
       +       if (zodd(n)) \{
       +           odd = zodd(k);
       +           fib_ll(a, g, k);
       +           zadd(f, a, a);
       +           zadd(k, f, g);
       +           zsub(f, f, g);
       +           zmul(f, f, k);
       +           zseti(k, odd ? -2 : +2);
       +           zadd(f, f, k);
       +           zadd(g, g, g);
       +           zadd(g, g, a);
       +           zmul(g, g, a);
       +       \} else \{
       +           fib_ll(g, a, k);
       +           zadd(f, a, a);
       +           zadd(f, f, g);
       +           zmul(f, f, g);
       +           zsqr(a, a);
       +           zsqr(g, g);
       +           zadd(g, a);
       +       \}
       +       zfree(k), zfree(a);
       +   \}
       +
       +   void
       +   fib(z_t f, z_t n)
       +   \{
       +       z_t tmp, k;
       +       zinit(tmp), zinit(k);
       +       zset(k, n);
       +       fib_ll(f, tmp, k);
       +       zfree(k), zfree(tmp);
       +   \}
       +\end{alltt}
       +
       +\noindent
       +This algorithm is based on the rules
       +
       +\vspace{1em}
       +\( \displaystyle{
       +    F_{2k + 1} = 4F_k^2 - F_{k - 1}^2 + 2(-1)^k = (2F_k + F_{k-1})(2F_k - F_{k-1}) + 2(-1)^k
       +}\)
       +\vspace{1em}
       +
       +\( \displaystyle{
       +    F_{2k} = F_k \cdot (F_k + 2F_{k - 1})
       +}\)
       +\vspace{1em}
       +
       +\( \displaystyle{
       +    F_{2k - 1} = F_k^2 + F_{k - 1}^2
       +}\)
       +\vspace{1em}
       +
       +\noindent
       +Each call to {\tt fib\_ll} returns $F_n$ and $F_{n - 1}$
       +for any input $n$. $F_{k}$ is only correctly returned
       +for $k \ge 0$. $F_n$ and $F_{n - 1}$ is used for
       +calculating $F_{2n}$ or $F_{2n + 1}$. The algorithm
       +can be speed up with a larger lookup table than one
       +covering just the base cases. Alternatively, a naïve
       +calculation could be used for sufficiently small input.
       +
       +
       +\newpage
       +\section{Lucas numbers}
       +\label{sec:Lucas numbers}
       +
       +Lucas numbers can be calculated by utilising
       +{\tt fib\_ll} from \secref{sec:Fibonacci numbers}:
       +
       +\begin{alltt}
       +   void
       +   lucas(z_t l, z_t n)
       +   \{
       +       z_t k;
       +       int odd;
       +       if (zcmp(n, 1) <= 0) \{
       +           zset(l, 1 + zzero(n));
       +           return;
       +       \}
       +       zinit(k);
       +       zrsh(k, n, 1);
       +       if (zeven(n)) \{
       +           lucas(l, k);
       +           zsqr(l, l);
       +           zseti(k, zodd(k) ? +2 : -2);
       +           zadd(l, k);
       +       \} else \{
       +           odd = zodd(k);
       +           fib_ll(l, k, k);
       +           zadd(l, l, l);
       +           zadd(l, l, k);
       +           zmul(l, l, k);
       +           zseti(k, 5);
       +           zmul(l, l, k);
       +           zseti(k, odd ? +4 : -4);
       +           zadd(l, l, k);
       +       \}
       +       zfree(k);
       +   \}
       +\end{alltt}
       +
       +\noindent
       +This algorithm is based on the rules
       +
       +\vspace{1em}
       +\( \displaystyle{
       +    L_{2k} = L_k^2 - 2(-1)^k
       +}\)
       +\vspace{1ex}
       +
       +\( \displaystyle{
       +    L_{2k + 1} = 5F_{k - 1} \cdot (2F_k + F_{k - 1}) - 4(-1)^k
       +}\)
       +\vspace{1em}
       +
       +\noindent
       +Alternatively, the function can be implemented
       +trivially using the rule
       +
       +\vspace{1em}
       +\( \displaystyle{
       +    L_k = F_k + 2F_{k - 1}
       +}\)
       +
       +
       +\newpage
       +\section{Bit operation}
       +\label{sec:Bit operation unimplemented}
       +
       +\subsection{Bit scanning}
       +\label{sec:Bit scanning}
       +
       +Scanning for the next set or unset bit can be
       +trivially implemented using {\tt zbtest}. A
       +more efficient, although not optimally efficient,
       +implementation would be
       +
       +\begin{alltt}
       +   size_t
       +   bscan(z_t a, size_t whence, int direction, int value)
       +   \{
       +       size_t ret;
       +       z_t t;
       +       zinit(t);
       +       value ? zset(t, a) : znot(t, a);
       +       ret = direction < 0
       +           ? (ztrunc(t, t, whence + 1), zbits(t) - 1)
       +           : (zrsh(t, t, whence), zlsb(t) + whence);
       +       zfree(t);
       +       return ret;
       +   \}
       +\end{alltt}
       +
       +
       +\subsection{Population count}
       +\label{sec:Population count}
       +
       +The following function can be used to compute
       +the population count, the number of set bits,
       +in an integer, counting the sign bit:
       +
       +\begin{alltt}
       +   size_t
       +   popcount(z_t a)
       +   \{
       +       size_t i, ret = zsignum(a) < 0;
       +       for (i = 0; i < a->used; i++) \{
       +           ret += __builtin_popcountll(a->chars[i]);
       +       \}
       +       return ret;
       +   \}
       +\end{alltt}
       +
       +\noindent
       +It requires a compiler extension, if missing,
       +there are other ways to computer the population
       +count for a word: manually bit-by-bit, or with
       +a fully unrolled
       +
       +\begin{alltt}
       +   int s;
       +   for (s = 1; s < 64; s <<= 1)
       +       w = (w >> s) + w;
       +\end{alltt}
       +
       +
       +\subsection{Hamming distance}
       +\label{sec:Hamming distance}
       +
       +A simple way to compute the Hamming distance,
       +the number of differing bits, between two number
       +is with the function
       +
       +\begin{alltt}
       +   size_t
       +   hammdist(z_t a, z_t b)
       +   \{
       +       size_t ret;
       +       z_t t;
       +       zinit(t);
       +       zxor(t, a, b);
       +       ret = popcount(t);
       +       zfree(t);
       +       return ret;
       +   \}
       +\end{alltt}
       +
       +\noindent
       +The performance of this function could
       +be improve by comparing character by
       +character manually with using {\tt zxor}.
       +
       +
       +\subsection{Character retrieval}
       +\label{sec:Character retrieval}
       +
       +\begin{alltt}
       +   uint64_t
       +   getu(z_t a)
       +   \{
       +       return zzero(a) ? 0 : a->chars[0];
       +   \}
       +\end{alltt}
 (DIR) diff --git a/doc/number-theory.tex b/doc/number-theory.tex
       @@ -0,0 +1,35 @@
       +\chapter{Number theory}
       +\label{chap:Number theory}
       +
       +TODO
       +
       +\vspace{1cm}
       +\minitoc
       +
       +
       +\newpage
       +\section{Odd or even}
       +\label{sec:Odd or even}
       +
       +TODO % zodd zeven zodd_nonzero zeven_nonzero
       +
       +
       +\newpage
       +\section{Signum}
       +\label{sec:Signum}
       +
       +TODO % zsignum zzero
       +
       +
       +\newpage
       +\section{Greatest common divisor}
       +\label{sec:Greatest common divisor}
       +
       +TODO % zgcd
       +
       +
       +\newpage
       +\section{Primality test}
       +\label{sec:Primality test}
       +
       +TODO % zptest
 (DIR) diff --git a/doc/random-numbers.tex b/doc/random-numbers.tex
       @@ -0,0 +1,28 @@
       +\chapter{Random numbers}
       +\label{chap:Random numbers}
       +
       +TODO
       +
       +\vspace{1cm}
       +\minitoc
       +
       +
       +\newpage
       +\section{Generation}
       +\label{sec:Generation}
       +
       +TODO
       +
       +
       +\newpage
       +\section{Devices}
       +\label{sec:Devices}
       +
       +TODO
       +
       +
       +\newpage
       +\section{Distributions}
       +\label{sec:Distributions}
       +
       +TODO
 (DIR) diff --git a/src/zmodmul.c b/src/zmodmul.c
       @@ -6,6 +6,7 @@ void
        zmodmul(z_t a, z_t b, z_t c, z_t d)
        {
                /* TODO Montgomery modular multiplication */
       +        /* TODO Kochanski multiplication */
                if (unlikely(a == d)) {
                        zset(libzahl_tmp_modmul, d);
                        zmul(a, b, c);
 (DIR) diff --git a/src/zmodpow.c b/src/zmodpow.c
       @@ -12,6 +12,8 @@ zmodpow(z_t a, z_t b, z_t c, z_t d)
                size_t i, j, n, bits;
                zahl_char_t x;
        
       +        /* TODO use zmodpowu when possible */
       +
                if (unlikely(zsignum(c) <= 0)) {
                        if (zzero(c)) {
                                if (check(zzero(b)))
 (DIR) diff --git a/src/zmodpowu.c b/src/zmodpowu.c
       @@ -25,10 +25,11 @@ zmodpowu(z_t a, z_t b, unsigned long long int c, z_t d)
        
                zmod(tb, b, d);
                zset(td, d);
       -        zsetu(a, 1);
        
                if (c & 1)
       -                zmodmul(a, a, tb, td);
       +                zset(a, tb);
       +        else
       +                zsetu(a, 1);
                while (c >>= 1) {
                        zmodsqr(tb, tb, td);
                        if (c & 1)
 (DIR) diff --git a/src/zpow.c b/src/zpow.c
       @@ -14,6 +14,8 @@ zpow(z_t a, z_t b, z_t c)
                 * 7↑19 = 7↑10011₂ = 7↑2⁰ ⋅ 7↑2¹ ⋅ 7↑2⁴ where a↑2↑(n + 1) = (a↑2↑n)².
                 */
        
       +        /* TODO use zpowu when possible */
       +
                size_t i, j, n, bits;
                zahl_char_t x;
                int neg;
 (DIR) diff --git a/src/zpowu.c b/src/zpowu.c
       @@ -21,10 +21,11 @@ zpowu(z_t a, z_t b, unsigned long long int c)
        
                neg = znegative(b) && (c & 1);
                zabs(tb, b);
       -        zsetu(a, 1);
        
                if (c & 1)
       -                zmul_ll(a, a, tb);
       +                zset(a, tb);
       +        else
       +                zsetu(a, 1);
                while (c >>= 1) {
                        zsqr_ll(tb, tb);
                        if (c & 1)