/* SPDX-FileCopyrightText: 2021-2022 John Scott <jscott@posteo.net>
 * SPDX-License-Identifier: GPL-2.0-or-later */
#include <assert.h>
#include <flint/fmpz.h>
#include <flint/fmpq.h>
#include <flint/arith.h>
#include <flint/fmpq_mat.h>
#include <flint/fmpq_vec.h>

#define LIMIT 10

/* The typical recurrence relation for the Bernoulli numbers
 * can be expressed as a system of linear equations, and hence
 * as a matrix. This can be derived by starting with the umbral
 * relation (B+1)^n - B^n = 0 for n = 0 and n >= 2, expanding
 * the binomial for n = 2, 3, ..., k, and moving the 1 to the
 * right-hand side.
 *
 * This yields, for k = 3, say (with the additional factoid
 * that we regard B_1 as being -1/2),
 * 2 * B_1 + 0 * B_2 + 0 * B_3 = -1
 * 3 * B_1 + 3 * B_2 + 0 * B_3 = -1
 * 4 * B_1 + 6 * B_2 + 4 * B_3 = -1
 *
 * or, expressed as a matrix equation,
 * [ 2  0  0 ] [ B_1 ]   [ -1 ]
 * [ 3  3  0 ] [ B_2 ] = [ -1 ]
 * [ 4  6  4 ] [ B_3 ]   [ -1 ]
 */
int main(void) {
	fmpq_mat_t binomcoeffs;
	fmpq_mat_init(binomcoeffs, LIMIT, LIMIT);
	fmpq_mat_zero(binomcoeffs);
	for(slong row = 0; row < LIMIT; row++) {
		for(slong col = 0; col <= row; col++) {
			fmpz *const num = fmpq_mat_entry_num(binomcoeffs, row, col);
			fmpz_bin_uiui(num, row + 2, row - col + 1);
			*fmpq_mat_entry_den(binomcoeffs, row, col) = 1;
		}
	}
	assert(fmpq_mat_is_integral(binomcoeffs));

	fmpq *const bernoullis = _fmpq_vec_init(LIMIT + 1);
	arith_bernoulli_number_vec(bernoullis, LIMIT + 1);

	fmpq *const out = _fmpq_vec_init(LIMIT);
	fmpq_mat_mul_fmpq_vec(out, binomcoeffs, bernoullis + 1 /* skip B_0 */, LIMIT);
	for(slong i = 0; i < LIMIT; i++) {
		assert(fmpq_is_pm1(out + i));
	}

	_fmpq_vec_clear(out, LIMIT);
	_fmpq_vec_clear(bernoullis, LIMIT + 1);
	fmpq_mat_clear(binomcoeffs);
}
