"""
GRNexus Numeric Processing Module

This module provides numeric processing operations using C library calls.
Translated from Ruby implementation.
"""

import ctypes
import platform
import os
from typing import List, Union

# Detect operating system and load appropriate library
# Detect operating system and load appropriate library
def detect_library():
    script_dir = os.path.dirname(os.path.abspath(__file__))
    system = platform.system()
    
    # Determine library name and platform directory
    if system == 'Windows':
        lib_name = 'numeric_proccessing.dll'
        platform_dir = 'Windows'
    elif system == 'Darwin':  # macOS
        lib_name = 'numeric_proccessing.dylib'
        platform_dir = 'Mac'
    elif system == 'Linux':
        lib_name = 'numeric_proccessing.so'
        platform_dir = 'Linux'
    else:
        raise OSError(f"Sistema operativo no soportado: {system}")
    
    # Try multiple possible locations
    possible_paths = [
        # Installed package location (site-packages/grnexus/exports/...)
        os.path.join(script_dir, '..', 'exports', platform_dir, lib_name),
        # Development location (python/lib/../../exports/...)
        os.path.join(script_dir, '..', '..', 'exports', platform_dir, lib_name),
    ]
    
    for lib_path in possible_paths:
        normalized_path = os.path.normpath(lib_path)
        if os.path.exists(normalized_path):
            return normalized_path
    
    # If not found, raise an error with helpful message
    raise FileNotFoundError(
        f"No se pudo encontrar la biblioteca nativa {lib_name} para {system}. "
        f"Buscado en: {', '.join(possible_paths)}"
    )

# Load the shared library
_lib = ctypes.CDLL(detect_library())

# Define GRNexusData structure
class GRNexusData(ctypes.Structure):
    _fields_ = [
        ('data', ctypes.POINTER(ctypes.c_double)),
        ('type', ctypes.c_int),
        ('size', ctypes.c_size_t),
        ('stride', ctypes.c_size_t),
        ('dims', ctypes.c_size_t * 3)
    ]

# Helper functions for creating and reading GRNexusData
def create_grnexus_data(array_or_scalar: Union[List[float], float]):
    """Create a GRNexusData structure from a Python list or scalar."""
    # Flatten and convert to array if needed
    if isinstance(array_or_scalar, list):
        def flatten(lst):
            result = []
            for item in lst:
                if isinstance(item, list):
                    result.extend(flatten(item))
                else:
                    result.append(item)
            return result
        values = flatten(array_or_scalar)
    else:
        values = [array_or_scalar]
    
    size = len(values)
    
    # Create buffer with data
    buffer = (ctypes.c_double * size)()
    for i, val in enumerate(values):
        buffer[i] = val
    
    # Create GRNexusData structure
    data = GRNexusData()
    data.data = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_double))
    data.type = 0 if size == 1 else 1  # SCALAR=0, ARRAY=1
    data.size = size
    data.stride = 1
    data.dims[0] = size
    data.dims[1] = 0
    data.dims[2] = 0
    
    return data, buffer

def create_output_grnexus_data(size: int):
    """Create an empty GRNexusData structure for output."""
    buffer = (ctypes.c_double * size)()
    
    data = GRNexusData()
    data.data = ctypes.cast(buffer, ctypes.POINTER(ctypes.c_double))
    data.type = 1  # ARRAY
    data.size = size
    data.stride = 1
    data.dims[0] = size
    data.dims[1] = 0
    data.dims[2] = 0
    
    return data, buffer

def read_grnexus_data(buffer, size: int) -> List[float]:
    """Read data from a GRNexusData buffer."""
    return list(buffer[:size])

# Define function signatures
FUNCTIONS = {
    # Binary operations between arrays
    'SumArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'ProductArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'SubtractArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'DivideArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'PowerArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'ModuloArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MaxArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MinArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    
    # Scalar operations
    'AddScalarToArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.c_double, ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MultiplyScalarToArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.c_double, ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    
    # Statistical operations (return scalar)
    'MeanArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'VarianceArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'StdArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MaxValueArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MinValueArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'SumAllArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'ProductAllArray': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    
    # Array operations
    'ConcatenateArrays': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MovingAverage': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_size_t]),
    'FiniteDifference': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'DiscreteIntegral': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'ZScoreNormalize': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MinMaxNormalize': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double, ctypes.c_double]),
    
    # Numeric activation functions
    'LinearNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'ReLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'SigmoidNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'TanhNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'LeakyReLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'ELUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'SoftplusNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'GELUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'SwishNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MishNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'SiLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'HardTanhNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'HardSigmoidNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'HardSwishNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'SoftsignNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'SELUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'CELUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'ISRUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'ISRLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'ReLU6Num': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'ThresholdedReLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'PReLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.POINTER(ctypes.c_double)]),
    'SquaredReLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'LiSHTNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'SnakeNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'SnakeBetaNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double, ctypes.c_double]),
    'AReluNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double, ctypes.c_double]),
    'FReLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'BReLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'HardShrinkNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'SoftShrinkNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_double]),
    'TanhShrinkNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
    'MaxoutNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_size_t]),
    'MinoutNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_size_t]),
    'GLUNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool, ctypes.c_size_t]),
    'ReLUSquaredNum': (ctypes.c_int, [ctypes.POINTER(GRNexusData), ctypes.POINTER(GRNexusData), ctypes.c_bool]),
}

# Attach functions to library
for func_name, (restype, argtypes) in FUNCTIONS.items():
    func = getattr(_lib, func_name)
    func.restype = restype
    func.argtypes = argtypes

# Base class for numeric processors
class NumericProcessor:
    def process(self, *args):
        raise NotImplementedError("Debes implementar el método de procesamiento numérico")

# Base class for operations
class BaseNumericOperation(NumericProcessor):
    def _execute_binary_operation(self, func_name: str, input1, input2, derivative: bool = False):
        input1_data, input1_buffer = create_grnexus_data(input1)
        input2_data, input2_buffer = create_grnexus_data(input2)
        
        input1_size = len(input1) if isinstance(input1, list) else 1
        input2_size = len(input2) if isinstance(input2, list) else 1
        
        # Calculate output size based on operation
        if func_name == 'ConcatenateArrays':
            output_size = input1_size + input2_size
        else:
            output_size = input1_size  # Same size as input1 for element-wise operations
        
        output_data, output_buffer = create_output_grnexus_data(output_size)
        
        args = [ctypes.byref(input1_data), ctypes.byref(input2_data), ctypes.byref(output_data), derivative]
        
        func = getattr(_lib, func_name)
        result = func(*args)
        
        if result == 0:
            return list(output_buffer[:output_size])
        else:
            raise RuntimeError(f"Función {func_name} falló con código: {result}")
    
    def _execute_unary_operation(self, func_name: str, input_values, derivative: bool = False, **kwargs):
        input_data, input_buffer = create_grnexus_data(input_values)
        input_size = len(input_values) if isinstance(input_values, list) else 1
        
        # For operations that return scalar
        scalar_output = func_name in ['MeanArray', 'VarianceArray', 'StdArray', 'MaxValueArray', 
                                       'MinValueArray', 'SumAllArray', 'ProductAllArray']
        
        # Calculate output size
        if scalar_output:
            output_size = 1
        elif func_name == 'MovingAverage':
            window_size = kwargs.get('window_size', 3)
            output_size = max(input_size - window_size + 1, 0)
        elif func_name == 'FiniteDifference':
            output_size = max(input_size - 1, 0)
        else:
            output_size = input_size
        
        output_data, output_buffer = create_output_grnexus_data(output_size)
        
        args = [ctypes.byref(input_data), ctypes.byref(output_data), derivative]
        
        # Add function-specific arguments
        if func_name == 'MovingAverage':
            args.append(kwargs.get('window_size', 3))
        elif func_name == 'MinMaxNormalize':
            args.append(kwargs.get('min_range', 0.0))
            args.append(kwargs.get('max_range', 1.0))
        elif func_name in ['LeakyReLUNum', 'ELUNum', 'CELUNum', 'ISRUNum', 'ISRLUNum', 
                           'ThresholdedReLUNum', 'FReLUNum', 'HardShrinkNum', 'SoftShrinkNum', 'SnakeNum']:
            args.append(kwargs.get('param1', 0.01))
        elif func_name in ['SnakeBetaNum', 'AReluNum']:
            args.append(kwargs.get('param1', 1.0))
            args.append(kwargs.get('param2', 1.0))
        elif func_name == 'PReLUNum':
            alpha = ctypes.c_double(kwargs.get('param1', 0.01))
            args.append(ctypes.byref(alpha))
        elif func_name in ['MaxoutNum', 'MinoutNum', 'GLUNum']:
            args.append(kwargs.get('num_pieces', 2))
        
        func = getattr(_lib, func_name)
        result = func(*args)
        
        if result == 0:
            result_array = list(output_buffer[:output_size])
            return result_array[0] if scalar_output else result_array
        else:
            raise RuntimeError(f"Función {func_name} falló con código: {result}")
    
    def _execute_scalar_operation(self, func_name: str, input_values, scalar: float, derivative: bool = False):
        input_data, input_buffer = create_grnexus_data(input_values)
        input_size = len(input_values) if isinstance(input_values, list) else 1
        output_data, output_buffer = create_output_grnexus_data(input_size)
        
        args = [ctypes.byref(input_data), scalar, ctypes.byref(output_data), derivative]
        
        func = getattr(_lib, func_name)
        result = func(*args)
        
        if result == 0:
            return list(output_buffer[:input_size])
        else:
            raise RuntimeError(f"Función {func_name} falló con código: {result}")

# ============================================================================
# BINARY OPERATIONS BETWEEN ARRAYS
# ============================================================================

class SumArray(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('SumArray', a, b, derivative=derivative)

class ProductArray(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('ProductArray', a, b, derivative=derivative)

class SubtractArray(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('SubtractArray', a, b, derivative=derivative)

class DivideArray(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('DivideArray', a, b, derivative=derivative)

class PowerArray(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('PowerArray', a, b, derivative=derivative)

class ModuloArray(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('ModuloArray', a, b, derivative=derivative)

class MaxArray(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('MaxArray', a, b, derivative=derivative)

class MinArray(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('MinArray', a, b, derivative=derivative)

# ============================================================================
# SCALAR OPERATIONS
# ============================================================================

class AddScalarToArray(BaseNumericOperation):
    def process(self, a, scalar, derivative=False):
        return self._execute_scalar_operation('AddScalarToArray', a, scalar, derivative=derivative)

class MultiplyScalarToArray(BaseNumericOperation):
    def process(self, a, scalar, derivative=False):
        return self._execute_scalar_operation('MultiplyScalarToArray', a, scalar, derivative=derivative)

# ============================================================================
# STATISTICAL OPERATIONS (RETURN SCALAR)
# ============================================================================

class MeanArray(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('MeanArray', a, derivative=derivative)

class VarianceArray(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('VarianceArray', a, derivative=derivative)

class StdArray(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('StdArray', a, derivative=derivative)

class MaxValueArray(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('MaxValueArray', a, derivative=derivative)

class MinValueArray(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('MinValueArray', a, derivative=derivative)

class SumAllArray(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SumAllArray', a, derivative=derivative)

class ProductAllArray(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ProductAllArray', a, derivative=derivative)

# ============================================================================
# ARRAY OPERATIONS
# ============================================================================

class ConcatenateArrays(BaseNumericOperation):
    def process(self, a, b, derivative=False):
        return self._execute_binary_operation('ConcatenateArrays', a, b, derivative=derivative)

class MovingAverage(BaseNumericOperation):
    def __init__(self, window_size=3):
        self.window_size = window_size
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('MovingAverage', a, derivative=derivative, window_size=self.window_size)

class FiniteDifference(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('FiniteDifference', a, derivative=derivative)

class DiscreteIntegral(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('DiscreteIntegral', a, derivative=derivative)

class ZScoreNormalize(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ZScoreNormalize', a, derivative=derivative)

class MinMaxNormalize(BaseNumericOperation):
    def __init__(self, min_range=0.0, max_range=1.0):
        self.min_range = min_range
        self.max_range = max_range
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('MinMaxNormalize', a, derivative=derivative, 
                                             min_range=self.min_range, max_range=self.max_range)

# ============================================================================
# NUMERIC ACTIVATION FUNCTIONS
# ============================================================================

class LinearNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('LinearNum', a, derivative=derivative)

class ReLUNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ReLUNum', a, derivative=derivative)

class SigmoidNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SigmoidNum', a, derivative=derivative)

class TanhNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('TanhNum', a, derivative=derivative)

class LeakyReLUNum(BaseNumericOperation):
    def __init__(self, alpha=0.01):
        self.alpha = alpha
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('LeakyReLUNum', a, derivative=derivative, param1=self.alpha)

class ELUNum(BaseNumericOperation):
    def __init__(self, alpha=1.0):
        self.alpha = alpha
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ELUNum', a, derivative=derivative, param1=self.alpha)

class SoftplusNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SoftplusNum', a, derivative=derivative)

class GELUNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('GELUNum', a, derivative=derivative)

class SwishNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SwishNum', a, derivative=derivative)

class MishNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('MishNum', a, derivative=derivative)

class SiLUNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SiLUNum', a, derivative=derivative)

class HardTanhNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('HardTanhNum', a, derivative=derivative)

class HardSigmoidNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('HardSigmoidNum', a, derivative=derivative)

class HardSwishNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('HardSwishNum', a, derivative=derivative)

class SoftsignNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SoftsignNum', a, derivative=derivative)

class SELUNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SELUNum', a, derivative=derivative)

class CELUNum(BaseNumericOperation):
    def __init__(self, alpha=1.0):
        self.alpha = alpha
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('CELUNum', a, derivative=derivative, param1=self.alpha)

class ISRUNum(BaseNumericOperation):
    def __init__(self, alpha=1.0):
        self.alpha = alpha
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ISRUNum', a, derivative=derivative, param1=self.alpha)

class ISRLUNum(BaseNumericOperation):
    def __init__(self, alpha=1.0):
        self.alpha = alpha
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ISRLUNum', a, derivative=derivative, param1=self.alpha)

class ReLU6Num(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ReLU6Num', a, derivative=derivative)

class ThresholdedReLUNum(BaseNumericOperation):
    def __init__(self, theta=1.0):
        self.theta = theta
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ThresholdedReLUNum', a, derivative=derivative, param1=self.theta)

class PReLUNum(BaseNumericOperation):
    def __init__(self, alpha=0.01):
        self.alpha = alpha
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('PReLUNum', a, derivative=derivative, param1=self.alpha)

class SquaredReLUNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SquaredReLUNum', a, derivative=derivative)

class LiSHTNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('LiSHTNum', a, derivative=derivative)

class SnakeNum(BaseNumericOperation):
    def __init__(self, alpha=1.0):
        self.alpha = alpha
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SnakeNum', a, derivative=derivative, param1=self.alpha)

class SnakeBetaNum(BaseNumericOperation):
    def __init__(self, alpha=1.0, beta=1.0):
        self.alpha = alpha
        self.beta = beta
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SnakeBetaNum', a, derivative=derivative, 
                                             param1=self.alpha, param2=self.beta)

class AReluNum(BaseNumericOperation):
    def __init__(self, alpha=0.0, beta=1.0):
        self.alpha = alpha
        self.beta = beta
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('AReluNum', a, derivative=derivative, 
                                             param1=self.alpha, param2=self.beta)

class FReLUNum(BaseNumericOperation):
    def __init__(self, alpha=1.0):
        self.alpha = alpha
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('FReLUNum', a, derivative=derivative, param1=self.alpha)

class BReLUNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('BReLUNum', a, derivative=derivative)

class HardShrinkNum(BaseNumericOperation):
    def __init__(self, lambda_=0.5):
        self.lambda_ = lambda_
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('HardShrinkNum', a, derivative=derivative, param1=self.lambda_)

class SoftShrinkNum(BaseNumericOperation):
    def __init__(self, lambda_=0.5):
        self.lambda_ = lambda_
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('SoftShrinkNum', a, derivative=derivative, param1=self.lambda_)

class TanhShrinkNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('TanhShrinkNum', a, derivative=derivative)

class MaxoutNum(BaseNumericOperation):
    def __init__(self, num_pieces=2):
        self.num_pieces = num_pieces
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('MaxoutNum', a, derivative=derivative, num_pieces=self.num_pieces)

class MinoutNum(BaseNumericOperation):
    def __init__(self, num_pieces=2):
        self.num_pieces = num_pieces
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('MinoutNum', a, derivative=derivative, num_pieces=self.num_pieces)

class GLUNum(BaseNumericOperation):
    def __init__(self, dim=1):
        self.dim = dim
    
    def process(self, a, derivative=False):
        return self._execute_unary_operation('GLUNum', a, derivative=derivative, num_pieces=self.dim)

class ReLUSquaredNum(BaseNumericOperation):
    def process(self, a, derivative=False):
        return self._execute_unary_operation('ReLUSquaredNum', a, derivative=derivative)

