<commit_before>from math import log as math_log

from ..dependent import Dependent


class Feature(Dependent):
    """
    Represents a predictive feature.  This class wraps a processor function
    and some metadata about it.
    
    :Parameters:
        name : str
            The name of the feature
        process : `func`
            A function that will generate a feature value
        return_type : `type`
            A type to compare the return of this function to.
        dependencies : `list`(`hashable`)
                An ordered list of dependencies that correspond
                to the *args of `process`
    """
    def __init__(self, name, process, returns, depends_on=None):
        super().__init__(name, process, depends_on)
        self.returns = returns
        
    
    def __call__(self, *args, **kwargs):
        value = super().__call__(*args, **kwargs)
        
        if __debug__: return self.validate(value)
        else: return value
    
    def __add__(self, summand):
        return add(self, summand)
    
    def __sub__(self, subband):
        return sub(self, subband)
    
    def __truediv__(self, divisor):
        return div(self, divisor)
    
    def __mul__(self, multiplier):
        return mul(self, multiplier)
    
    def validate(self, value):
        if isinstance(value, self.returns):
            return value
        else:
            raise ValueError("Expected {0}, but got {1} instead." \
                             .format(self.returns, type(value)))


class Modifier(Feature): pass


class BinaryOperator(Modifier):
    
    CHAR = "?"
    
    def __init__(self, feature, operand, returns=None):
        if isinstance(operand, Feature):
            self.operand_value = None
            operand_name = operand.name
            operand_type = operand.returns
            depends_on = [feature, operand]
        else:
            self.operand_value = operand
            operand_name = str(operand)
            operand_type = type(operand)
            depends_on = [feature]
        
        name = "{0} {1} {2}".format(feature.name, self.CHAR, operand_name)
        if returns is None:
            returns = type(self.operate(feature.returns(), operand_type()))
        super().__init__(name, self._process, returns=returns,
                         depends_on=depends_on)
        
    def _process(self, feature_value, operand_value=None):
        
        operand_value = operand_value if operand_value is not None \
                        else self.operand_value
        
        return self.operate(feature_value, operand_value)
        
    def operate(self, left, right): raise NotImplementedError()

class add(BinaryOperator):
    
    CHAR = "+"
    
    def operate(self, left, right): return left + right

class sub(BinaryOperator):
    
    CHAR = "-"
    
    def operate(self, left, right): return left - right

class mul(BinaryOperator):
    
    CHAR = "*"
    
    def operate(self, left, right): return left * right

class div(BinaryOperator):
    
    CHAR = "/"
    def __init__(self, feature, divisor, returns=float):
        super().__init__(feature, divisor, returns)
    
    def operate(self, left, right): return left / right
        

class log(Modifier):
    
    def __init__(self, feature):
        super().__init__("log({0})".format(feature.name), self._process,
                         returns=float, depends_on=[feature])
    
    def _process(self, feature_value): return math_log(feature_value)

'''
class add(Modifier):

def __init__(self, feature, summand):
    super().__init__("{0} + {1}".format(feature.name, repr(summand)),
                     self._process,
                     returns=type(feature.returns() + summand),<commit_msg>Adds comment about hardcoded division type.<commit_after>from math import log as math_log

from ..dependent import Dependent


class Feature(Dependent):
    """
    Represents a predictive feature.  This class wraps a processor function
    and some metadata about it.
    
    :Parameters:
        name : str
            The name of the feature
        process : `func`
            A function that will generate a feature value
        return_type : `type`
            A type to compare the return of this function to.
        dependencies : `list`(`hashable`)
                An ordered list of dependencies that correspond
                to the *args of `process`
    """
    def __init__(self, name, process, returns, depends_on=None):
        super().__init__(name, process, depends_on)
        self.returns = returns
        
    
    def __call__(self, *args, **kwargs):
        value = super().__call__(*args, **kwargs)
        
        if __debug__: return self.validate(value)
        else: return value
    
    def __add__(self, summand):
        return add(self, summand)
    
    def __sub__(self, subband):
        return sub(self, subband)
    
    def __truediv__(self, divisor):
        return div(self, divisor)
    
    def __mul__(self, multiplier):
        return mul(self, multiplier)
    
    def validate(self, value):
        if isinstance(value, self.returns):
            return value
        else:
            raise ValueError("Expected {0}, but got {1} instead." \
                             .format(self.returns, type(value)))


class Modifier(Feature): pass


class BinaryOperator(Modifier):
    
    CHAR = "?"
    
    def __init__(self, feature, operand, returns=None):
        if isinstance(operand, Feature):
            self.operand_value = None
            operand_name = operand.name
            operand_type = operand.returns
            depends_on = [feature, operand]
        else:
            self.operand_value = operand
            operand_name = str(operand)
            operand_type = type(operand)
            depends_on = [feature]
        
        name = "{0} {1} {2}".format(feature.name, self.CHAR, operand_name)
        if returns is None:
            returns = type(self.operate(feature.returns(), operand_type()))
        super().__init__(name, self._process, returns=returns,
                         depends_on=depends_on)
        
    def _process(self, feature_value, operand_value=None):
        
        operand_value = operand_value if operand_value is not None \
                        else self.operand_value
        
        return self.operate(feature_value, operand_value)
        
    def operate(self, left, right): raise NotImplementedError()

class add(BinaryOperator):
    
    CHAR = "+"
    
    def operate(self, left, right): return left + right

class sub(BinaryOperator):
    
    CHAR = "-"
    
    def operate(self, left, right): return left - right

class mul(BinaryOperator):
    
    CHAR = "*"
    
    def operate(self, left, right): return left * right

class div(BinaryOperator):
    
    CHAR = "/"
    def __init__(self, feature, divisor):
        # All division returns a float, so we are hardcoding it. 
        super().__init__(feature, divisor, returns=float)
    
    def operate(self, left, right): return left / right
        

class log(Modifier):
    
    def __init__(self, feature):
        super().__init__("log({0})".format(feature.name), self._process,
                         returns=float, depends_on=[feature])
    
    def _process(self, feature_value): return math_log(feature_value)

'''
class add(Modifier):

def __init__(self, feature, summand):
    super().__init__("{0} + {1}".format(feature.name, repr(summand)),
                     self._process,
                     returns=type(feature.returns() + summand),
{'fraction_non_alphanumeric': 0.09988125082464705, 'fraction_numerical': 0.002111096450719092, 'mean_word_length': 2.1413178615830915, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4308102', 'n_tokens_mistral': 2099, 'n_tokens_neox': 2091, 'n_words': 579}