Repository: sympy/sympy
File: sympy/assumptions/refine.py (Python)

Code before:
        if (i - j).could_extract_minus_sign():
            return expr
        return MatrixElement(matrix, j, i)


def refine_sin_cos(expr, assumptions):
    from sympy.functions.elementary.trigonometric import sin, cos
    arg = expr.args[0]
    k_terms = []
    rem_terms = []
    terms = arg.args if arg.is_Add else (arg,)
    for term in terms:
        pi_coeff = term.coeff(S.Pi)
        if pi_coeff:
            coeff = 2 * pi_coeff
            if ask(Q.integer(coeff), assumptions):
                k_terms.append(coeff)
            else:
                rem_terms.append(term)
        else:
            rem_terms.append(term)
    k = Add(*k_terms, evaluate=False)
    rem = Add(*rem_terms, evaluate=False)
    if isinstance(expr, sin):
        k = Add(k, -1, evaluate=False)
    if ask(Q.odd(k), assumptions):
        return ((-1)**((k + 1) / 2)) * sin(rem)
    elif ask(Q.even(k), assumptions):
        return ((-1)**(k / 2)) * cos(rem)
    return expr

handlers_dict: dict[str, Callable[[Basic, Boolean | bool], Expr]] = {
    'Abs': refine_abs,
    'Pow': refine_Pow,
    'atan2': refine_atan2,
    're': refine_re,
    'im': refine_im,
    'arg': refine_arg,
    'sign': refine_sign,
    'MatrixElement': refine_matrixelement,
    'cos': refine_sin_cos,
    'sin': refine_sin_cos,
}


Review comment (suggestion):
> I don't think that this is the right way to do this. This makes refine dependent on the old assumptions and evaluation when the goal should be to separate those things.

@oscarbenjamin Do you think it's fine to create expressions with  `evaluate=False` to avoid being dependent on the old assumptions and evaluation? I don't know how reliable `evaluate=False` is. 

Code after:
        if (i - j).could_extract_minus_sign():
            return expr
        return MatrixElement(matrix, j, i)


def refine_sin_cos(expr, assumptions):
    """
    Handler for sin and cos functions.

    Examples
    ========

    >>> from sympy.assumptions.refine import refine_sin_cos
    >>> from sympy import Symbol, Q, sin, cos, pi
    >>> from sympy.abc import x, y
    >>> n = Symbol('n')
    >>> refine_sin_cos(cos(n*pi), Q.even(n))
    (-1)**n
    >>> refine_sin_cos(sin(n*pi/2), Q.odd(n) & Q.odd((n-1)/2))
    (-1)**(n/2 - 1/2)
    >>> refine_sin_cos(sin(x + n*pi/2), Q.odd(n))
    (-1)**(n/2 - 1/2)*cos(x)
    >>> refine_sin_cos(cos(x + n*pi/2), Q.even(n))
    (-1)**(n/2)*cos(x)
    >>> refine_sin_cos(cos(x + y + 2*n*pi), Q.integer(n))
    (-1)**(2*n)*cos(x + y)
    """
    from sympy.functions.elementary.trigonometric import sin, cos
    arg = expr.args[0]

    integer_coeffs_of_pi_half = []
    remaining_terms = []

    terms = arg.args if arg.is_Add else (arg,)
    for term in terms:
        coeff_of_pi = term.coeff(S.Pi)
        if coeff_of_pi and ask(Q.integer(2 * coeff_of_pi), assumptions):
            coeff_of_pi_half = 2 * coeff_of_pi
            integer_coeffs_of_pi_half.append(coeff_of_pi_half)
        else:
            remaining_terms.append(term)

    if not integer_coeffs_of_pi_half:
        return expr

    sum_of_parity_known_coeffs = 0
    sum_of_parity_unknown_coeffs = 0
    sum_of_parity_known_coeffs_is_even = True
    for coeff in integer_coeffs_of_pi_half:
        coeff_is_even = ask(Q.even(coeff), assumptions)
        if coeff_is_even is None: