import pandas as pd
import numpy as np
from itertools import combinations

class GeneralLogRatio:

    @staticmethod
    def general_logratio(X: pd.DataFrame, degree: int = 2) -> pd.DataFrame:
        X_values = X.values
        n_samples, n_features = X_values.shape

        cols_combinations = np.array(list(combinations(range(n_features), degree)))
        n_combinations = cols_combinations.shape[0]

        selected = X_values[:, cols_combinations]  # shape: (n_samples, n_combinations, degree)

        numerator = selected[:, :, 0]
        denominator = selected[:, :, 1] if degree == 2 else np.prod(selected[:, :, 1:], axis=2)

        with np.errstate(divide='ignore', invalid='ignore'):
            ratio = np.divide(numerator, denominator)
            ratio[denominator == 0] = np.nan
            ratio[numerator == 0] = np.nan
            feature_array = np.log(ratio)

        feature_names = ["__logratio__".join(X.columns[list(cols)]) for cols in cols_combinations]

        return pd.DataFrame(feature_array, columns=feature_names, index=X.index)
