/*
 * Copyright (c) 2026 University of Insubria <https://www.uninsubria.it>
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or (at
 * your option) any later version. All we ask is that proper credit is given
 * for our work, which includes - but is not limited to - adding the above
 * copyright notice to the beginning of your source code files, and to any
 * copyright notice that you may distribute with programs based on this work.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation, Inc.
 * <https://fsf.org/>
 */

package qsardes.util;

import org.openscience.cdk.interfaces.IAtomContainer;

/**
 * Calculation utilities.
 *
 * @author Nicola Chirico
 */
public final class UtilsCalc
{
    /**
     * Gets the smallest value in an array.
     *
     * @param values array of values
     *
     * @return
     */
    public int get_min(int[] values)
    {
        int min = values[0];

        for (int item : values) {
            if (item < min) {
                min = item;
            }
        }

        return min;
    }

    /**
     * Gets the biggest value in an array.
     *
     * @param values array of values
     *
     * @return
     */
    public int get_max(int[] values)
    {
        int max = values[0];

        for (int item : values) {
            if (item > max) {
                max = item;
            }
        }

        return max;
    }

    /**
     * Gets the smallest value in an array.
     *
     * @param values array of values
     *
     * @return
     */
    public double get_min(double[] values)
    {
        double min = values[0];

        for (double item : values) {
            min = StrictMath.min(min, item);
        }

        return min;
    }

    /**
     * Gets the biggest value in an array.
     *
     * @param values array of values
     *
     * @return
     */
    public double get_max(double[] values)
    {
        double max = values[0];

        for (double item : values) {
            max = StrictMath.max(max, item);
        }

        return max;
    }

    /**
     * Gets the sum of values in an array.
     *
     * @param values array of values
     *
     * @return
     */
    public double calc_sum(double[] values)
    {
        double sum = 0.0;

        for (double item : values) {
            sum += item;
        }

        return sum;
    }

    /**
     * Gets the sum of absolute values in an array.
     *
     * @param values array of values
     *
     * @return
     */
    public double calc_abs_sum(double[] values)
    {
        double sum = 0.0;

        for (double item : values) {
            sum += StrictMath.abs(item);
        }

        return sum;
    }

    /**
     * Gets the average of values in an array.
     *
     * @param values array of values
     *
     * @return
     */
    public double calc_average(double[] values)
    {
        return calc_sum(values) / (double) values.length;
    }

    /**
     * Gets the average centered values in an array.
     *
     * @param values array of values
     *
     * @return
     */
    public double[] calc_centered_values(double[] values)
    {
        double av = calc_average(values);

        double[] cen = new double[values.length];

        for (int i = 0; i < values.length; i++) {
            cen[i] = values[i] - av;
        }

        return cen;
    }

    /**
     * Gets the average centered values in an array.
     *
     * @param eig_values array of eigenvalues
     *
     * @return
     */
    public double calc_ad_sum(double[] eig_values)
    {
        double eig_val_average = calc_average(eig_values);

        double sum = 0.0;

        for (double eig_val : eig_values) {
            sum += StrictMath.abs(eig_val - eig_val_average);
        }

        return sum;
    }

    /**
     * Gets the sum of a column values in a matrix.
     *
     * @param array matrix
     * @param col_idx column index
     *
     * @return
     */
    public double calc_col_sum(double[][] array, int col_idx)
    {
        double sum = 0.0;

        for (int i = 0; i < array[col_idx].length; i++) {

            sum += array[i][col_idx];
        }

        return sum;
    }

    /**
     * Gets the sum of a column absolute values in a matrix.
     *
     * @param array matrix
     * @param col_idx column index
     *
     * @return
     */
    public double calc_col_abs_sum(double[][] array, int col_idx)
    {
        double sum = 0.0;

        for (int i = 0; i < array[col_idx].length; i++) {

            sum += StrictMath.abs(array[i][col_idx]);
        }

        return sum;
    }

    /**
     * Gets the sum of the exponential part of the Log-Sum-Exp function.
     *
     * @param eig_values array of eigenvaluesreal part
     *
     * @return
     */
    public double calc_ee_lse_exp_sum(double[] eig_values)
    {
        double eig_val_max = get_max(eig_values);

        double sum = 0.0;

        for (double eig_val : eig_values) {
            sum += StrictMath.exp(eig_val - eig_val_max);
        }

        return sum;
    }

    /**
     * Gets the Randic-like eigenvector-based index.
     *
     * @param iac chemical defined as CDK atom container
     * @param eig_vec_array eigenvector matrix as returned by Jama
     *
     * @return
     */
    public double calc_vr1_sum(IAtomContainer iac,
            double[][] eig_vec_array, int eig_index)
    {
        double sum = 0.0;

        for (var bond : iac.bonds()) {
            for (int i = 0; i < bond.getAtomCount() - 1; i++) {
                double eigv_1 = eig_vec_array[iac.indexOf(bond.getAtom(i))]
                        [eig_index];

                double eigv_2 = eig_vec_array[iac.indexOf(bond.getAtom(i + 1))]
                        [eig_index];

                var eig_abs_prod = StrictMath.abs(eigv_1 * eigv_2);

                sum += StrictMath.pow(eig_abs_prod, -0.5);
            }
        }

        return sum;
    }

    /**
     * Checks if all elements in a matrix are not infinite and are number.
     *
     * @param matrix
     *
     * @return
     */
    public boolean are_all_matrix_values_valid(double[][] matrix)
    {
        for (double[] vect : matrix) {
            for (double item : vect) {
                Double value = item;

                if (value.isNaN() || value.isInfinite()) {
                    return false;
                }
            }
        }

        return true;
    }
}
