commit 16a001c8fe4e5ca99d5aafdd8ed02a35f09b6caa
Author:     Mattias Andrée <[email protected]>
AuthorDate: Fri May 13 04:38:09 2016 +0200
Commit:     Mattias Andrée <[email protected]>
CommitDate: Fri May 13 04:38:09 2016 +0200

    Miscellaneous stuff
    
    Signed-off-by: Mattias Andrée <[email protected]>

diff --git a/Makefile b/Makefile
index 8daf15d..73c1aad 100644
--- 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)
diff --git a/TODO b/TODO
index 0327bca..b748650 100644
--- 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
diff --git a/doc/arithmetic.tex b/doc/arithmetic.tex
index e90f607..52eae11 100644
--- 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
diff --git a/doc/bit-operations.tex b/doc/bit-operations.tex
new file mode 100644
index 0000000..83a08ed
--- /dev/null
+++ 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
diff --git a/doc/libzahl.tex b/doc/libzahl.tex
index 83d74c4..2bfc5c0 100644
--- 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
diff --git a/doc/not-implemented.tex b/doc/not-implemented.tex
new file mode 100644
index 0000000..7269251
--- /dev/null
+++ 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}
diff --git a/doc/number-theory.tex b/doc/number-theory.tex
new file mode 100644
index 0000000..f690277
--- /dev/null
+++ 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
diff --git a/doc/random-numbers.tex b/doc/random-numbers.tex
new file mode 100644
index 0000000..52d6b68
--- /dev/null
+++ 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
diff --git a/src/zmodmul.c b/src/zmodmul.c
index 26d1178..5dd3a6c 100644
--- 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);
diff --git a/src/zmodpow.c b/src/zmodpow.c
index 0cec96d..253b3e4 100644
--- 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)))
diff --git a/src/zmodpowu.c b/src/zmodpowu.c
index 72aa96f..beb17c2 100644
--- 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)
diff --git a/src/zpow.c b/src/zpow.c
index 29eea83..ecdadb2 100644
--- 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;
diff --git a/src/zpowu.c b/src/zpowu.c
index 35247c3..5a7676d 100644
--- 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)

Reply via email to