savage_core 0.2.0

A primitive computer algebra system (library)
Documentation
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2021-2022  Philipp Emanuel Weidmann <pew@worldwidemann.com>

use permutohedron::heap_recursive;
use savage_macros::function;

use crate::{expression::Expression, functions::SquareMatrix, helpers::*};

#[function(
    name = "det",
    description = "determinant of a square matrix",
    examples = r#"[
        ("det([[1, 2], [3, 4]])", "-2"),
        ("det([[a, b], [c, d]])", "a * d - b * c"),
        ("det([])", "1"),
    ]"#,
    categories = r#"[
        "linear algebra",
    ]"#
)]
fn determinant(matrix: SquareMatrix) -> Expression {
    if matrix.is_empty() {
        return int(1);
    }

    let mut indices = (0..matrix.nrows()).collect::<Vec<usize>>();

    let mut products = Vec::new();

    heap_recursive(indices.as_mut_slice(), |permutation| {
        products.push(
            (0..matrix.nrows())
                .map(|i| matrix[(i, permutation[i])].clone())
                .reduce(|a, b| a * b)
                .unwrap(),
        );
    });

    // The first permutation generated by Heap's algorithm is the identity,
    // which has positive sign...
    let mut positive = true;

    products
        .into_iter()
        .reduce(|a, b| {
            // ... and every following permutation differs from its predecessor
            // by exactly one transposition, which flips the sign.
            positive = !positive;

            if positive {
                a + b
            } else {
                a - b
            }
        })
        .unwrap()
}