<commit_before>"""
Assumption Domain
==================

:Author: <NAME> and <NAME>
"""
from typing import List

from copy import deepcopy

import math

from lyra.abstract_domains.numerical.interval_domain import IntervalState
from lyra.quality_analysis.assumption_lattice import TypeLattice, AssumptionLattice, \
    InputAssumptionLattice
from lyra.abstract_domains.stack import Stack
from lyra.abstract_domains.state import State
from lyra.abstract_domains.store import Store
from lyra.core.expressions import Expression, VariableIdentifier, ExpressionVisitor, \
    BinaryComparisonOperation, Literal, Range, UnaryArithmeticOperation
from lyra.core.types import ListLyraType, IntegerLyraType, BooleanLyraType, FloatLyraType, \
    StringLyraType
from lyra.core.utils import copy_docstring


class InputAssumptionStack(Stack):
    """ Input Assumption Stack

    Stack of elements from the Input Assumption Lattice.

    """

    def __init__(self):
        super().__init__(InputAssumptionLattice, {})

    @copy_docstring(Stack.pop)
    def pop(self):
        if len(self.stack) > 1:
            element = self.stack.pop()
            if element.infoloss:
                self.lattice.assmps.clear()
                self.lattice.infoloss = True
            elif element.is_loop:
                if len(element.assmps) > 0:
                    num_iter = self.get_num_iter_from_condition(element.condition)
                    if num_iter is None:
                        self.lattice.assmps.clear()
                        self.lattice.infoloss = True
                        return
                    if self.check_second_iteration(element):
                        self.lattice.assmps.pop(0)
                    pp = element.assmps[0].pp
                    self.lattice.add_assmps_with_iter(num_iter, element.assmps, pp)
                    self.lattice.join_as_loop = True
                else:
                    self.lattice.join_as_loop = True
            elif len(element.assmps) > 0:
                self.lattice.add_assumptions_front(element.assmps)
                self.lattice.join_as_loop = False

    def check_second_iteration(self, element):
        """Checks if the assumption in front of the current stack top is from the same program
        point than the front assumption of the element that we pop.

        :param element: The element that is currently popped
        :return: f the assumption in front of the current stack top is from the same program
        point than the parameter element.
        """
        if len(self.lattice.assmps) == 0:
            return False
        prev_element = self.lattice.assmps[0]
        if not isinstance(prev_element, InputAssumptionLattice):
            return False
        return prev_element.pp == element.assmps[0].pp

    def get_num_iter_from_condition(self, condition):
        """Extracts the number of iterations from a condition

        :param condition: condition to analyze for number of iterations
        :return: number of iterations if possible, otherwise None
        """
        if isinstance(condition, BinaryComparisonOperation):
            if condition.operator == BinaryComparisonOperation.Operator.In:
                in_element = condition.right
                if isinstance(in_element, Range):
                    start = self.get_value_for_iteration(in_element.start)
                    end = self.get_value_for_iteration(in_element.end)
                    step = self.get_value_for_iteration(in_element.step)
                    if start is not None and end is not None and step is not None:
                        return math.ceil((end - start) / step)
                    error = f"Analysis of range() is only implemented for Literal arguments."
                    raise NotImplementedError(error)
        return None

    def get_value_for_iteration(self, iter_expr):
        """Gets the value from an expression. Works for Literals or UnaryOperations of a Literal.

        :param iter_expr: expression to extract the value from
        :return: The value of the evaluated expression or None
        """
        if isinstance(iter_expr, Literal):
            return int(iter_expr.val)
        elif isinstance(iter_expr, UnaryArithmeticOperation):
            is_minus = iter_expr.operator == UnaryArithmeticOperation.Operator.Sub
            if isinstance(iter_expr.expression, Literal):
                val = int(iter_expr.expression.val)
                if is_minus:
                    val = -val
                return val
        return None

    @copy_docstring(Stack.push)
    def push(self):
        self.stack.append(InputAssumptionLattice())


class AssumptionState(Store, State):
    """Assumption analysis state. An element of the assumption abstract domain.

    Map from each program variable to the assumption tuple representing its current assumptions.

    .. document private methods
    .. automethod:: AssumptionState._assign
    .. automethod:: AssumptionState._assume
    .. automethod:: AssumptionState._output
    .. automethod:: AssumptionState._substitute

    """

    def __init__(self, variables: List[VariableIdentifier]):
        types = [BooleanLyraType(), IntegerLyraType(), FloatLyraType(), StringLyraType(), ListLyraType()]
        lattices = {typ: AssumptionLattice for typ in types}<commit_msg>using defaultdict in assumption_domain to fix usage of ListLyraType<commit_after>"""
Assumption Domain
==================

:Author: <NAME> and <NAME>
"""
from collections import defaultdict
from typing import List

from copy import deepcopy

import math

from lyra.abstract_domains.numerical.interval_domain import IntervalState
from lyra.quality_analysis.assumption_lattice import TypeLattice, AssumptionLattice, \
    InputAssumptionLattice
from lyra.abstract_domains.stack import Stack
from lyra.abstract_domains.state import State
from lyra.abstract_domains.store import Store
from lyra.core.expressions import Expression, VariableIdentifier, ExpressionVisitor, \
    BinaryComparisonOperation, Literal, Range, UnaryArithmeticOperation
from lyra.core.types import ListLyraType, IntegerLyraType, BooleanLyraType, FloatLyraType, \
    StringLyraType
from lyra.core.utils import copy_docstring


class InputAssumptionStack(Stack):
    """ Input Assumption Stack

    Stack of elements from the Input Assumption Lattice.

    """

    def __init__(self):
        super().__init__(InputAssumptionLattice, {})

    @copy_docstring(Stack.pop)
    def pop(self):
        if len(self.stack) > 1:
            element = self.stack.pop()
            if element.infoloss:
                self.lattice.assmps.clear()
                self.lattice.infoloss = True
            elif element.is_loop:
                if len(element.assmps) > 0:
                    num_iter = self.get_num_iter_from_condition(element.condition)
                    if num_iter is None:
                        self.lattice.assmps.clear()
                        self.lattice.infoloss = True
                        return
                    if self.check_second_iteration(element):
                        self.lattice.assmps.pop(0)
                    pp = element.assmps[0].pp
                    self.lattice.add_assmps_with_iter(num_iter, element.assmps, pp)
                    self.lattice.join_as_loop = True
                else:
                    self.lattice.join_as_loop = True
            elif len(element.assmps) > 0:
                self.lattice.add_assumptions_front(element.assmps)
                self.lattice.join_as_loop = False

    def check_second_iteration(self, element):
        """Checks if the assumption in front of the current stack top is from the same program
        point than the front assumption of the element that we pop.

        :param element: The element that is currently popped
        :return: f the assumption in front of the current stack top is from the same program
        point than the parameter element.
        """
        if len(self.lattice.assmps) == 0:
            return False
        prev_element = self.lattice.assmps[0]
        if not isinstance(prev_element, InputAssumptionLattice):
            return False
        return prev_element.pp == element.assmps[0].pp

    def get_num_iter_from_condition(self, condition):
        """Extracts the number of iterations from a condition

        :param condition: condition to analyze for number of iterations
        :return: number of iterations if possible, otherwise None
        """
        if isinstance(condition, BinaryComparisonOperation):
            if condition.operator == BinaryComparisonOperation.Operator.In:
                in_element = condition.right
                if isinstance(in_element, Range):
                    start = self.get_value_for_iteration(in_element.start)
                    end = self.get_value_for_iteration(in_element.end)
                    step = self.get_value_for_iteration(in_element.step)
                    if start is not None and end is not None and step is not None:
                        return math.ceil((end - start) / step)
                    error = f"Analysis of range() is only implemented for Literal arguments."
                    raise NotImplementedError(error)
        return None

    def get_value_for_iteration(self, iter_expr):
        """Gets the value from an expression. Works for Literals or UnaryOperations of a Literal.

        :param iter_expr: expression to extract the value from
        :return: The value of the evaluated expression or None
        """
        if isinstance(iter_expr, Literal):
            return int(iter_expr.val)
        elif isinstance(iter_expr, UnaryArithmeticOperation):
            is_minus = iter_expr.operator == UnaryArithmeticOperation.Operator.Sub
            if isinstance(iter_expr.expression, Literal):
                val = int(iter_expr.expression.val)
                if is_minus:
                    val = -val
                return val
        return None

    @copy_docstring(Stack.push)
    def push(self):
        self.stack.append(InputAssumptionLattice())


class AssumptionState(Store, State):
    """Assumption analysis state. An element of the assumption abstract domain.

    Map from each program variable to the assumption tuple representing its current assumptions.

    .. document private methods
    .. automethod:: AssumptionState._assign
    .. automethod:: AssumptionState._assume
    .. automethod:: AssumptionState._output
    .. automethod:: AssumptionState._substitute

    """

    def __init__(self, variables: List[VariableIdentifier]):
        # types = [BooleanLyraType(), IntegerLyraType(), FloatLyraType(), StringLyraType(), ListLyraType()]
        lattices = defaultdict(lambda: AssumptionLattice)
{'fraction_non_alphanumeric': 0.07398124941984591, 'fraction_numerical': 0.001485194467650608, 'mean_word_length': 2.3880503144654086, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, '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': '4618534', 'n_tokens_mistral': 2919, 'n_tokens_neox': 2712, 'n_words': 793}