About Docs Source
LCOV - code coverage report
Current view: top level - include/nut - dirichlet.h (source / functions) Coverage Total Hit
Test: unnamed Lines: 100.0 % 14 14
Test Date: 2025-10-22 01:14:28 Functions: 100.0 % 4 4

            Line data    Source code
       1              : #pragma once
       2              : 
       3              : /// @file
       4              : /// @author hacatu
       5              : /// @version 0.2.0
       6              : /// @section LICENSE
       7              : /// This Source Code Form is subject to the terms of the Mozilla Public
       8              : /// License, v. 2.0. If a copy of the MPL was not distributed with this
       9              : /// file, You can obtain one at http://mozilla.org/MPL/2.0/.
      10              : /// @section DESCRIPTION
      11              : /// Dirichlet Hyperbola based functions for computing sums of multiplicative functions at a single value quickly
      12              : ///
      13              : /// For a function f defined over the natural numbers, we say f is multiplicative if f(ab) = f(a)f(b) whenever a and b are coprime
      14              : /// (have gcd 1).  There are many functions in number theory that satisfy this condition, with one of the most well known being
      15              : /// euler's phi function.
      16              : ///
      17              : /// A brief list of common multiplicative functions is:
      18              : /// (see here https://en.wikipedia.org/wiki/Multiplicative_function)
      19              : /// The dirichlet convolution identity (explained later): I(n) = 1 if n == 1; 0 otherwise
      20              : /// Sometimes written epsilon(n), (very confusingly) u(n), or delta(n, 0) (since it is just a kroneker delta function of course)
      21              : /// The constant function: u(n) = 1
      22              : /// Sometimes written 1(n)
      23              : /// The identity function: N(n) = n
      24              : /// Sometimes written Id(n) or id(n)
      25              : /// The power functions: N_k(n) = n**k
      26              : /// Sometimes written Id_k(n)
      27              : /// The divisor count function: d(n) = #{k | n} (the number of natural numbers including 1 and n which divide n)
      28              : /// The generalized divisor functions: d_k(n) = # k-tuples ks of natural numbers such that ks multiplies out to n
      29              : /// The divisor power sum functions: sigma_a(n) = sum(k | n, k**a) (note that a can be any real number)
      30              : /// The mobius function: mu(n) = 0 if n is not squarefree; (-1)**Omega(n) otherwise, where Omega(n) is the number of prime factors of n with multiplicity
      31              : /// Euler's totient function: phi(n) = #{k = 1 ... n : (k coprime n)}
      32              : /// Any constant raised to the power of Omega(n) or omega(n) (the number of prime factors of n counted with and without multiplicity respectively)
      33              : /// The number of non-isomorphic abelian groups of order n: a(n)
      34              : /// The Ramanujan tau function: tau(n)
      35              : /// The unitary divisor power sums: sigma_a^*(n) = sum(d | n where (d coprime n/d), d**a)
      36              : /// All dirichlet characters, including the legendre symbol (n/p) for fixed p and gcd(n, m) for fixed m
      37              : ///
      38              : /// Some of these are fully multiplicative, meaning not only do they satisfy f(ab) = f(a)f(b) for a, b coprime, but they satisfy it for all a, b.
      39              : /// In particular, I, u, N, and N_k are fully multiplicative.
      40              : ///
      41              : /// We often want to find the sum of a multiplicative function, often denoted by a capital version, for instance phi and Phi, f and F (for a general function, etc).
      42              : /// We also often want to be able to analyze multiplicative functions in terms of simpler functions.
      43              : ///
      44              : /// Multiplicative functions are fully determined by their values at prime powers, which often leads to efficient sieve based approaches to calculating them.
      45              : /// This header provides some functions for doing this, the nut_euler_sieve family of functions, although these are mainly intended as low level
      46              : /// subroutines for the upcoming Dirichlet Hyperbola algorithm.
      47              : ///
      48              : /// However, if we only want to compute the sum of a multiplicative function up to some value n, sieve based methods will basically be O(nlogn),
      49              : /// which seems very bad considering how structured multiplicative functions are.
      50              : ///
      51              : /// Indeed, we can often do better, in particular when we want to find the sum H(n) of some multiplicative function h(n) defined as h = f <*> g
      52              : /// where <*> denotes Dirichlet convolution.  What is Dirichlet convolution?  It is an operation for combining functions, similar to ordinary
      53              : /// multiplication, division, addition, subtraction, composition, and so on.  It is defined as (f <*> g)(n) = sum(k | n, f(k)g(n/k)).
      54              : /// This is mostly useful because when f and g are multiplicative f <*> g will be as well, and it lets us build up more complicated functions in terms
      55              : /// of simpler ones.
      56              : /// (see here https://en.wikipedia.org/wiki/Dirichlet_convolution)
      57              : ///
      58              : /// Obviously, not all operations on multiplicative functions will produce multiplicative functions.  For example, if f is multiplicative, 2f is not,
      59              : /// so multiplicative functions are not closed under scalar multiplication, and thus they are not closed under addition.
      60              : /// They are closed under multiplication and dirichlet convolution though.
      61              : ///
      62              : /// The Dirichlet Hyperbola algorithm lets us rewrite H(x) = sum(n <= x, h(n)) in terms of F and G as well as f and g, but crucially it lets us evaluate them
      63              : /// at fewer values.
      64              : ///
      65              : /// In particular, sum(n <= x, (f <*> g)(n)) = sum(n <= x, ab = n, f(a)g(b)) = sum(ab <= x, f(a)g(b))
      66              : /// = sum(a <= A, b <= x/a, f(a)g(b)) + sum(b <= B, a <= x/b, f(a)g(b)) - sum(a <= A, b <= B, f(a)g(b))
      67              : /// = sum(a <= A, f(a)G(x/a)) + sum(b <= B, F(x/b)g(b)) - F(A)G(B)
      68              : /// where AB = x.
      69              : /// (see here https://gbroxey.github.io/blog/2023/04/30/mult-sum-1.html)
      70              : /// (see here https://angyansheng.github.io/blog/dirichlet-hyperbola-method)
      71              : /// (you may also find this instructive https://en.wikipedia.org/wiki/Marginal_distribution)
      72              : /// To accomplish this rearrangement, we first expanded (f <*> g) using the definition of dirichlet convolution.
      73              : /// Then, we interpreted the sum both as an a-indexed sum and as a b-indexed sum.
      74              : /// We could find the sum either way, but considering both sets us up to cut the work down quadratically.
      75              : /// These two ways of interpreting the sum are EXACTLY EQUIVALENT to the trick of transforming an integral of f(x) in terms of x into
      76              : /// an integral of f_inverse(y) in terms of y.  For integrals, it is common to just pick whichever interpretation is easier, but for our sum,
      77              : /// we benefit from realizing that if we sum f(a)g(b) for (a <= A, b <= x/a) and then separately for (b <= B, a <= x/b), then we double count the
      78              : /// rectangular region (a <= A, b <= B).
      79              : /// But more importantly, if we pick A = B = sqrt(x), then we've turned one sum over (f <*> g) for n from 1 to x, which we could accomplish in
      80              : /// about O(xlogx), into two sums over f*G and F*g for n from 1 to sqrt(x), which we can sometimes accomplish in O(sqrt(x)).
      81              : ///
      82              : /// There is a big catch though, we now need to know values of F and G as well as f and g.
      83              : /// Wonderfully though, we only need to know F(x/n) and G(x/n) for integral values of n, and floor(x/n) has at most 2sqrt(x) distinct values.
      84              : /// That, arguably, is the real magic, since it lets us store only O(sqrt(x)) values of F and G.
      85              : /// When we want to compute only H(x), this is fine, but when we want to repeatedly compute the table of values of H(x/n) for integral values of n,
      86              : /// it is often better to make A larger than B, eg A = x**(2/3) and B = x**(1/3).
      87              : 
      88              : #include <stdbool.h>
      89              : #include <assert.h>
      90              : 
      91              : #include <nut/modular_math.h>
      92              : 
      93              : /// Wrapper to hold values of some multiplicative function.
      94              : /// Stores all values f(n) for n up to (and including) y, then
      95              : /// stores values f(x/n) for n less than x/y
      96              : typedef struct{
      97              :     int64_t x;
      98              :     int64_t y, yinv;
      99              :     int64_t *buf;
     100              : } nut_Diri;
     101              : 
     102              : /// Compute the sum of the divisor count function d from 1 to max.
     103              : /// @param [in] max: inclusive upper bound of range to compute sum for
     104              : /// @param [in] m: modulus to reduce result by, or 0 to skip reducing
     105              : /// @return the sum d(1) + ... + d(max)
     106              : NUT_ATTR_CONST
     107              : uint64_t nut_dirichlet_D(uint64_t max, uint64_t m);
     108              : 
     109              : /// Compute the sum of the divisor sum function sigma from 1 to max.
     110              : /// @param [in] max: inclusive upper bound of range to compute sum for
     111              : /// @param [in] m: modulus to reduce result by, or 0 to skip reducing
     112              : /// @return the sum sigma(1) + ... + sigma(max)
     113              : NUT_ATTR_CONST
     114              : uint64_t nut_dirichlet_Sigma(uint64_t max, uint64_t m);
     115              : 
     116              : /// Given a table of values of a multiplicative function f, compute (f <*> u)(x) for all x from 1 to n
     117              : /// See { @link nut_euler_sieve_conv_u} for more info}
     118              : /// @param [in] n: inclusive upper bound of range to compute f <*> u over
     119              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     120              : /// @param [in] f_vals: table of values for f
     121              : /// @param [out] f_conv_u_vals: table to store values of f <*> u in
     122              : /// @return true on success, false on allocation failure
     123              : NUT_ATTR_NONNULL(3, 4)
     124              : NUT_ATTR_ACCESS(read_only, 3, 1)
     125              : NUT_ATTR_ACCESS(read_write, 4, 1)
     126              : bool nut_euler_sieve_conv_u(int64_t n, int64_t m, const int64_t f_vals[static n+1], int64_t f_conv_u_vals[restrict static n+1]);
     127              : 
     128              : /// Given a table of values of a multiplicative function f, compute (f <*> N)(x) for all x from 1 to n
     129              : /// See { @link nut_euler_sieve_conv_u} for more info}
     130              : /// @param [in] n: inclusive upper bound of range to compute f <*> N over
     131              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     132              : /// @param [in] f_vals: table of values for f
     133              : /// @param [out] f_conv_N_vals: table to store values of f <*> N in
     134              : /// @return true on success, false on allocation failure
     135              : NUT_ATTR_NONNULL(3, 4)
     136              : NUT_ATTR_ACCESS(read_only, 3, 1)
     137              : NUT_ATTR_ACCESS(read_write, 4, 1)
     138              : bool nut_euler_sieve_conv_N(int64_t n, int64_t m, const int64_t f_vals[static n+1], int64_t f_conv_N_vals[restrict static n+1]);
     139              : 
     140              : /// Given tables of values of multiplicative functions f and g, compute (f <*> g)(x) for all x from 1 to n
     141              : /// This uses Euler's sieve, a variant of the sieve of Eratosthenes that only marks off each multiple once.
     142              : /// This generally has worse performance than the sieve of Eratosthenes, but some preliminary tests showed
     143              : /// that Eratosthenes is only about 14% faster in release mode than Euler, so currently only Euler's sieve
     144              : /// is implemented.
     145              : /// @param [in] n: inclusive upper bound of range to compute f <*> g over
     146              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     147              : /// @param [in] f_vals: table of values for f
     148              : /// @param [in] g_vals: table of values for f
     149              : /// @param [out] f_conv_g_vals: table to store values of f <*> g in
     150              : /// @return true on success, false on allocation failure
     151              : NUT_ATTR_NONNULL(3, 4, 5)
     152              : NUT_ATTR_ACCESS(read_only, 3, 1)
     153              : NUT_ATTR_ACCESS(read_only, 4, 1)
     154              : NUT_ATTR_ACCESS(read_write, 5, 1)
     155              : bool nut_euler_sieve_conv(int64_t n, int64_t m, const int64_t f_vals[static n+1], const int64_t g_vals[static n+1], int64_t f_conv_g_vals[restrict static n+1]);
     156              : 
     157              : /// Allocate internal buffers for a diri table
     158              : /// self->buf will have f(0) through f(y) at indicies 0 through y,
     159              : /// and then f(x/1) through f(x/(yinv - 1)) at indicies y + 1 through y + yinv - 1.
     160              : /// The first two indicies, f(0) and f(1), are basically dummies though: f(0) should
     161              : /// never be relied on, and f(1) should always be 1
     162              : /// @param [in] x: inclusive upper bound of the domain of interest
     163              : /// @param [in] y: inclusive upper bound of the dense portion of the domain of interest.
     164              : /// Will be increased to sqrt(x) if needed, so 0 can be used to explicitly signal you want that behavior
     165              : /// @return true on success, false on allocation failure
     166              : NUT_ATTR_NONNULL(1)
     167              : NUT_ATTR_ACCESS(write_only, 1)
     168              : bool nut_Diri_init(nut_Diri *self, int64_t x, int64_t y);
     169              : 
     170              : /// Copy the values from one diri table to another, which must be initialized
     171              : NUT_ATTR_NONNULL(1, 2)
     172              : NUT_ATTR_ACCESS(read_write, 1)
     173              : NUT_ATTR_ACCESS(read_only, 2)
     174              : void nut_Diri_copy(nut_Diri *restrict dest, const nut_Diri *restrict src);
     175              : 
     176              : /// Deallocate internal buffers for a diri table
     177              : NUT_ATTR_NONNULL(1)
     178              : NUT_ATTR_ACCESS(read_write, 1)
     179              : void nut_Diri_destroy(nut_Diri *self);
     180              : 
     181              : NUT_ATTR_PURE
     182              : NUT_ATTR_NONNULL(1)
     183              : NUT_ATTR_ACCESS(read_only, 1)
     184     55230523 : static inline int64_t nut_Diri_get_dense(const nut_Diri *self, int64_t k){
     185     55230523 :     assert(k >= 0 && k <= self->y);
     186     55230523 :     return self->buf[k];
     187              : }
     188              : 
     189              : NUT_ATTR_NONNULL(1)
     190              : NUT_ATTR_ACCESS(read_write, 1)
     191      2320856 : static inline void nut_Diri_set_dense(nut_Diri *self, int64_t k, int64_t v){
     192      2320856 :     assert(k >= 0 && k <= self->y);
     193      2320856 :     self->buf[k] = v;
     194      2320856 : }
     195              : 
     196              : NUT_ATTR_PURE
     197              : NUT_ATTR_NONNULL(1)
     198              : NUT_ATTR_ACCESS(read_only, 1)
     199        92066 : static inline int64_t nut_Diri_get_sparse(const nut_Diri *self, int64_t k){
     200        92066 :     assert(k > 0 && k <= self->yinv);
     201        92066 :     return self->buf[self->y + k];
     202              : }
     203              : 
     204              : NUT_ATTR_NONNULL(1)
     205              : NUT_ATTR_ACCESS(read_write, 1)
     206        16961 : static inline void nut_Diri_set_sparse(nut_Diri *self, int64_t k, int64_t v){
     207        16961 :     assert(k > 0 && k <= self->yinv);
     208        16961 :     self->buf[self->y + k] = v;
     209        16961 : }
     210              : 
     211              : /// Compute the value table for the dirichlet convolution identity I(n) = \{1 if n == 0, 0 otherwise\}
     212              : /// Just memset's the dense part, then sets index 1 and y + 1 through y + yinv - 1 to 1 (remember the sparse indicies are sums)
     213              : /// @param [in, out] self: the table to store the result in, and take the bounds from.  Must be initialized
     214              : NUT_ATTR_NONNULL(1)
     215              : NUT_ATTR_ACCESS(read_write, 1)
     216              : void nut_Diri_compute_I(nut_Diri *self);
     217              : 
     218              : /// Compute the value table for the unit function u(n) = 1
     219              : /// Fills the dense part of the table with 1s, and computes the sparse entries with table[y + k] = U(x/k) = x/k
     220              : /// @param [in, out] self: the table to store the result in, and take the bounds from.  Must be initialized
     221              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     222              : NUT_ATTR_NONNULL(1)
     223              : NUT_ATTR_ACCESS(read_write, 1)
     224              : void nut_Diri_compute_u(nut_Diri *self, int64_t m);
     225              : 
     226              : /// Compute the value table for the identity function N(n) = n
     227              : /// Fills the dense part of the table with increasing numbers, and computes the sparse entries with table[y + k] = sum_N(v = x/k) = v*(v+1)/2
     228              : /// @param [in, out] self: the table to store the result in, and take the bounds from.  Must be initialized
     229              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     230              : NUT_ATTR_NONNULL(1)
     231              : NUT_ATTR_ACCESS(read_write, 1)
     232              : void nut_Diri_compute_N(nut_Diri *self, int64_t m);
     233              : 
     234              : /// Compute the value table for the mobius function mu(n) (whose sum is called the Mertens function)
     235              : /// Requires both an initialized nut_Diri from {@link nut_Diri_init} and a packed table of mobius values from {@link nut_sieve_mobius}.
     236              : /// @param [in, out] self: the table to store the result in, and take the bounds from.  Must be initialized
     237              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     238              : /// @param [in] mobius: packed table of mobius values, from {@link nut_sieve_mobius} (with upper bound self->y).
     239              : NUT_ATTR_NONNULL(1, 3)
     240              : NUT_ATTR_ACCESS(read_write, 1)
     241              : NUT_ATTR_ACCESS(read_only, 3)
     242              : void nut_Diri_compute_mertens(nut_Diri *restrict self, int64_t m, const uint8_t mobius[restrict static self->y/4 + 1]);
     243              : 
     244              : /// Compute the value table for the kth generalized divisor function dk(n)
     245              : /// dk(n) = da(n) <*> db(n) whenever a + b = k.
     246              : /// This function uses binary exponentiation to compute dk in only log(k) convolutions.
     247              : /// However, if all dk's are needed, calling { @link nut_Diri_compute_conv_u}
     248              : /// is more efficient
     249              : /// @param [in, out] self: the table to store the result in, initialized by { @link nut_Diri_init}.
     250              : /// self->y, self->x, and self->yinv must be set and consistent with the inputs
     251              : /// @param [in] k: k, ie which generalized divisor function to compute.  dk is u <*> u <*> ... <*> u with k u's
     252              : /// @param [in] m: modulus to reduce the result by, or 0 to skip reducing
     253              : /// @param [in, out] f_tbl, g_tbl: temporary tables for scratch work, initialized by { @link nut_Diri_init}, fields y, x, and yinv must be set
     254              : /// Will still have scratch data stored on return
     255              : NUT_ATTR_NONNULL(1, 4, 5)
     256              : NUT_ATTR_ACCESS(read_write, 1, 4, 5)
     257              : bool nut_Diri_compute_dk(nut_Diri *restrict self, uint64_t k, int64_t m, nut_Diri *restrict f_tbl, nut_Diri *restrict g_tbl);
     258              : 
     259              : /// Compute the value table for the kth power function N^k(n)
     260              : /// N^k(n) = n^k, so we can find the sums using https://en.wikipedia.org/wiki/Faulhaber%27s_formula
     261              : /// (we actually do this by taking a lower triangular matrix of pascal's triangle and inverting it)
     262              : /// This is one of the core components needed to compute the Dirichlet sum table for f where f(p) is a polynomial,
     263              : /// the other one being https://en.wikipedia.org/wiki/Jordan%27s_totient_function
     264              : /// @param [in, out] self: the table to store the result in, and take the bounds from.  Must be initialized
     265              : /// @param [in] k: the power of the power function to compute
     266              : /// @param [in] m: modulus to reduce the result by, or 0 to skip reducing
     267              : NUT_ATTR_NONNULL(1)
     268              : NUT_ATTR_ACCESS(read_write, 1)
     269              : bool nut_Diri_compute_Nk(nut_Diri *restrict self, uint64_t k, int64_t m);
     270              : 
     271              : /// Compute the value table for the Jacobi symbol jacobi(n/p)
     272              : /// Currently, p MUST be and odd prime
     273              : /// This is 1 if n is a quadratic residue (perfect square) mod p, -1 if it is not, or 0 if n is a multiple of p
     274              : /// @param [in, out] self: the table to store the result in, and take the bounds from.  Must be initialized
     275              : /// @param [in] p: prime modulus for jacobi symbol
     276              : NUT_ATTR_NONNULL(1)
     277              : NUT_ATTR_ACCESS(read_write, 1)
     278              : bool nut_Diri_compute_J(nut_Diri *restrict self, uint64_t p);
     279              : 
     280              : /// Compute the value table for a given Dirichlet Character Chi, whose total is 0
     281              : /// A dirichlet character is some n-periodic function Chi(x) which is
     282              : /// - completely multiplicative (Chi(ab) = Chi(a)Chi(b))
     283              : /// - 0 if x is not coprime to n
     284              : /// And here, we also require that the sum of Chi(x) over its period is 0, so it is trivial to sum
     285              : /// @param [in, out] self: the table to store the result in, and take the bounds from.  Must be initialized
     286              : /// @param [in] n: period of the dirichlet character
     287              : /// @param [in] m: modulus to reduce the result by, or 0 to skip reducing.  Note that the input values must already be reduced
     288              : /// @param [in] period_tbl: table of values of Chi(x) for 0 <= x < n.  These values should be reduced mod m and sum to 0 mod m, or if m is 0, some of these values should be 0
     289              : bool nut_Diri_compute_Chi_balanced(nut_Diri *restrict self, uint64_t n, int64_t m, const int64_t period_tbl[restrict static n]);
     290              : 
     291              : /// Compute the value table for the prime counting function, aka prime pi
     292              : /// The indicator function for primes is effectively multiplicative,
     293              : /// and the prime counting function is its sum.  But also, Lucy Hedgehog's prime counting
     294              : /// algorithm essentially uses Dirichlet tables already, hence its inclusion in this part of the library
     295              : /// @param [in, out] self: the table to store the result in, and take the bounds from.  Must be initialized
     296              : NUT_ATTR_NONNULL(1)
     297              : NUT_ATTR_ACCESS(read_write, 1)
     298              : bool nut_Diri_compute_pi(nut_Diri *restrict self);
     299              : 
     300              : /// Compute the value table for h = f <*> u, the dirichlet convolution of f and u (the unit function u(n) = 1), given the value table for f
     301              : /// See { @link nut_Diri_compute_conv } for details, this is just that function but with several specializations due to u being very simple.
     302              : /// @param [in, out] self: the table to store the result in, initialized by { @link nut_Diri_init}.
     303              : /// self->y, self->x, and self->yinv must be set and consistent with the inputs
     304              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     305              : /// @param [in] f_tbl: table for the first operand
     306              : NUT_ATTR_NONNULL(1, 3)
     307              : NUT_ATTR_ACCESS(read_write, 1)
     308              : NUT_ATTR_ACCESS(read_only, 3)
     309              : bool nut_Diri_compute_conv_u(nut_Diri *restrict self, int64_t m, const nut_Diri *restrict f_tbl);
     310              : 
     311              : /// Compute the value table for h = f <*> N, the dirichlet convolution of f and N (the identity function N(n) = n), given the value table for f
     312              : /// See { @link nut_Diri_compute_conv } for details, this is just that function but with several specializations due to N being very simple.
     313              : /// @param [in, out] self: the table to store the result in, initialized by { @link nut_Diri_init}.
     314              : /// self->y, self->x, and self->yinv must be set and consistent with the inputs
     315              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     316              : /// @param [in] f_tbl: table for the first operand
     317              : NUT_ATTR_NONNULL(1, 3)
     318              : NUT_ATTR_ACCESS(read_write, 1)
     319              : NUT_ATTR_ACCESS(read_only, 3)
     320              : bool nut_Diri_compute_conv_N(nut_Diri *restrict self, int64_t m, const nut_Diri *restrict f_tbl);
     321              : 
     322              : /// Compute the value table for h = f <*> g, the dirichlet convolution of f and g, given value tables for the operands
     323              : /// self must have been initialized using { @link nut_Diri_init}, and in particular the lengths and cutoffs for self, f_tbl, and g_tbl
     324              : /// must be set and match
     325              : /// This uses a sieve to compute h(1) through h(y), then uses dirichlet's hyperbola formula to compute H(x/1) through H(x/(yinv-1))
     326              : /// If one of the operands is a simple standard multiplicative function like the unit function u or the mobius function mu, then
     327              : /// try to use a specialized function from this library and use that instead.
     328              : /// @param [in, out] self: the table to store the result in, initialized by { @link nut_Diri_init}.
     329              : /// self->y, self->x, and self->yinv must be set and consistent with the inputs
     330              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     331              : /// @param [in] f_tbl: table for the first operand (dirichlet convolution is commutative, so order doesn't matter)
     332              : /// @param [in] g_tbl: table for the second operand
     333              : NUT_ATTR_NONNULL(1, 3, 4)
     334              : NUT_ATTR_ACCESS(read_write, 1)
     335              : NUT_ATTR_ACCESS(read_only, 3)
     336              : NUT_ATTR_ACCESS(read_only, 4)
     337              : bool nut_Diri_compute_conv(nut_Diri *restrict self, int64_t m, const nut_Diri *f_tbl, const nut_Diri *g_tbl);
     338              : 
     339              : 
     340              : /// Compute the value table for h such that f = g <*> h, aka h = f </> g where the division is in terms of dirichlet convolution
     341              : /// self must have been initialized using { @link nut_Diri_init }, and in particular the lengths and cutoffs for self, f_tbl, and g_tbl
     342              : /// must be set and match
     343              : /// This is the inverse of <*>, in the sense that if h = f </> g, then h <*> g = f.  In fact, this function is essentially
     344              : /// implemented by solving this for H(v) and applying the dirichlet hyperbola method (see the code comments for details).
     345              : /// Unlike the `compute_conv_*` family of functions, this needs to allocate space for scratch work, which can cause it to fail if
     346              : /// there is not enough memory.  About 16*(self->y + 1) bytes will be allocated.
     347              : /// Currently there are not specialized functions for dividing by simple standard functions.
     348              : /// @param [in, out] self: the table to store the result in, initialized by { @link nut_Diri_init }.
     349              : /// self->y, self->x, and self->yinv must be set ant consistent with the inputs
     350              : /// @param [in] m: modulus to reduce results by, or 0 to skip reducing
     351              : /// @param [in] f_tbl: table for the first operand (the "numerator" aka "dividend" in the "division")
     352              : /// @param [in] g_tbl: the table for the second operand (the "denominator" aka "divisor" in the "division")
     353              : NUT_ATTR_NONNULL(1, 3, 4)
     354              : NUT_ATTR_ACCESS(read_write, 1, 3, 4)
     355              : bool nut_Diri_convdiv(nut_Diri *restrict self, int64_t m, const nut_Diri *restrict f_tbl, const nut_Diri *restrict g_tbl);
     356              : 
        

Generated by: LCOV version 2.0-1