/* SPDX-FileCopyrightText: 2021 John Scott <jscott@posteo.net>
 * SPDX-License-Identifier: GPL-2.0-or-later */
#include <assert.h>
#include <flint/arith.h>
#include <flint/fmpq_mat.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_mat_t bernoullis;
	fmpq_mat_init(bernoullis, LIMIT, LIMIT);
	int s = fmpq_mat_inv(bernoullis, binomcoeffs);
	assert(s);
	fmpq_mat_clear(binomcoeffs);

	fmpq_mat_t negones;
	fmpq_mat_init(negones, LIMIT, 1);
	for(slong i = 0; i < LIMIT; i++) {
		*fmpq_mat_entry_num(negones, i, 0) = WORD(-1);
		*fmpq_mat_entry_den(negones, i, 0) = WORD(1);
	}
	assert(fmpq_mat_is_integral(negones));

	fmpq_mat_mul(bernoullis, bernoullis, negones);
	fmpq_mat_clear(negones);

	fmpq exactbern;
	for(slong i = 0; i < LIMIT; i++) {
		arith_bernoulli_number(&exactbern, i + 1);
		assert(fmpq_equal(&exactbern, fmpq_mat_entry(bernoullis, i, 0)));
	}
	fmpq_mat_clear(bernoullis);
}
