<reponame>Nikolaj-K/clifford
import numba
import operator

from clifford.g3c import layout, e1, e2, e3, e4, e5
import clifford as cf
import pytest


@numba.njit
def identity(x):
    return x


class TestBasic:
    """ Test very simple construction and field access """

    def test_roundtrip_layout(self):
        layout_r = identity(layout)
        assert type(layout_r) is type(layout)
        assert layout_r is layout

    def test_roundtrip_mv(self):
        e1_r = identity(e1)
        assert type(e1_r) is type(e1_r)

        # mvs are values, and not preserved by identity
        assert e1_r.layout is e1.layout
        assert e1_r == e1

    def test_piecewise_construction(self):
        @numba.njit
        def negate(a):
            return cf.MultiVector(a.layout, -a.value)

        n_e1 = negate(e1)
        assert n_e1.layout is e1.layout
        assert n_e1 == -e1

        @numba.njit
        def add(a, b):
            return cf.MultiVector(a.layout, a.value + b.value)

        ab = add(e1, e2)
        assert ab == e1 + e2
        assert ab.layout is e1.layout

    def test_constant_multivector(self):
        @numba.njit
        def add_e1(a):
            return cf.MultiVector(a.layout, a.value + e1.value)

        assert add_e1(e2) == e1 + e2

    def test_multivector_shorthand(self):
        @numba.njit
        def double(a):
            return a.layout.MultiVector(a.value*2)

        assert double(e2) == 2 * e2


class TestOperators:
    @pytest.mark.parametrize("op", [
        pytest.param(getattr(operator, op), id=op)
        for op in ['add', 'sub', 'mul', 'xor', 'or_']
    ])
    @pytest.mark.parametrize("a,b", [(e1, e2), (1, e1), (e1, 1),
                                     (0.5, 0.5 * e1), (0.5 * e1, 0.5),
                                     (e1, 0.5), (0.5, e1),
                                     (1, 0.5*e1), (0.5*e1, 1)])
    def test_binary(self, op, a, b):
        @numba.njit
        def overload(a, b):
            return op(a, b)

        ab = op(a, b)
        ab_alt = overload(a, b)
        assert ab == ab_alt
        assert ab.layout is ab_alt.layout
        # numba disagrees with numpy about what type `int` is on windows, so we
        # can't directly compare the dtypes. We only care that the float / int
        # state is kept anyway.
        assert ab.value.dtype.kind == ab_alt.value.dtype.kind

    # `op` is not parametrized, for simplicity we only support MultiVector / scalar.
    @pytest.mark.parametrize("a,b", [(e1, 2), (2.0*e1, 2)])
    def test_truediv(self, a, b):
        op = operator.truediv

        @numba.njit
        def overload(a, b):
            return op(a, b)

        ab = op(a, b)
        ab_alt = overload(a, b)
        assert ab == ab_alt
        assert ab.layout is ab_alt.layout
        # numba disagrees with numpy about what type `int` is on windows, so we
        # can't directly compare the dtypes. We only care that the float / int
        # state is kept anyway.
        assert ab.value.dtype.kind == ab_alt.value.dtype.kind

    @pytest.mark.parametrize("op", [
        pytest.param(getattr(operator, op), id=op)
        for op in ['pos', 'neg', 'invert']
    ])
    @pytest.mark.parametrize("a", [layout.scalar, e1, 0.5*e1, e1^e2, 1 + (e1^e2)])
    def test_unary(self, op, a):
        @numba.njit
        def overload(a):
            return op(a)

        ret = op(a)
        ret_alt = overload(a)
        assert ret == ret_alt
        assert ret.layout is ret_alt.layout
        assert ret.value.dtype == ret_alt.value.dtype

    # We have a special overload for literal arguments for speed
    def literal_grade_func(a, grade):
        @numba.njit
        def grade_func(a):
            return a(grade)
        return grade_func(a)

    @numba.njit
    def grade_func(a, grade):
        return a(grade)

    # vararg versions
    def literal_grade_func_star(a, *grades):
        @numba.njit
        def grade_func(a):
            return a(*grades)
        return grade_func(a)

    @numba.njit
    def grade_func_star(a, *grades):
        return a(*grades)

    @pytest.mark.parametrize('func', [
        literal_grade_func, grade_func,
        literal_grade_func_star, grade_func_star,
    ])
    def test_grade_projection(self, func):
        a = 1 + e1 + (e1^e2) + (e1^e2^e3) + (e1^e2^e3^e4) + (e1^e2^e3^e4^e5)

        assert func(a, 0) == 1
        assert func(a, 1) == e1
        assert func(a, 2) == e1^e2
        assert func(a, 3) == e1^e2^e3
        assert func(a, 4) == e1^e2^e3^e4
        assert func(a, 5) == e1^e2^e3^e4^e5

    @pytest.mark.parametrize('func', [
        literal_grade_func_star, grade_func_star,
    ])
    def test_multiple_grade_projection(self, func):
        a = 1 + e1 + (e1^e2) + (e1^e2^e3) + (e1^e2^e3^e4) + (e1^e2^e3^e4^e5)

        assert func(a, 0, 1) == 1 + e1
        assert func(a, 0, 1, 2, 3, 4, 5) == a

import numba
import operator

from clifford.g3c import layout, e1, e2, e3, e4, e5
import clifford as cf
import pytest


@numba.njit
def identity(x):
    return x


class TestBasic:
    """ Test very simple construction and field access """

    def test_roundtrip_layout(self):
        layout_r = identity(layout)
        assert type(layout_r) is type(layout)
        assert layout_r is layout

    def test_roundtrip_mv(self):
        e1_r = identity(e1)
        assert type(e1_r) is type(e1_r)

        # mvs are values, and not preserved by identity
        assert e1_r.layout is e1.layout
        assert e1_r == e1

    def test_piecewise_construction(self):
        @numba.njit
        def negate(a):
            return cf.MultiVector(a.layout, -a.value)

        n_e1 = negate(e1)
        assert n_e1.layout is e1.layout
        assert n_e1 == -e1

        @numba.njit
        def add(a, b):
            return cf.MultiVector(a.layout, a.value + b.value)

        ab = add(e1, e2)
        assert ab == e1 + e2
        assert ab.layout is e1.layout

    def test_constant_multivector(self):
        @numba.njit
        def add_e1(a):
            return cf.MultiVector(a.layout, a.value + e1.value)

        assert add_e1(e2) == e1 + e2

    def test_multivector_shorthand(self):
        @numba.njit
        def double(a):
            return a.layout.MultiVector(a.value*2)

        assert double(e2) == 2 * e2


class TestOperators:
    @pytest.mark.parametrize("op", [
        pytest.param(getattr(operator, op), id=op)
        for op in ['add', 'sub', 'mul', 'xor', 'or_']
    ])
    @pytest.mark.parametrize("a,b", [(e1, e2), (1, e1), (e1, 1),
                                     (0.5, 0.5 * e1), (0.5 * e1, 0.5),
                                     (e1, 0.5), (0.5, e1),
                                     (1, 0.5*e1), (0.5*e1, 1)])
    def test_binary(self, op, a, b):
        @numba.njit
        def overload(a, b):
            return op(a, b)

        ab = op(a, b)
        ab_alt = overload(a, b)
        assert ab == ab_alt
        assert ab.layout is ab_alt.layout
        # numba disagrees with numpy about what type `int` is on windows, so we
        # can't directly compare the dtypes. We only care that the float / int
        # state is kept anyway.
        assert ab.value.dtype.kind == ab_alt.value.dtype.kind

    # `op` is not parametrized, for simplicity we only support MultiVector / scalar.
    @pytest.mark.parametrize("a,b", [(e1, 2), (2.0*e1, 2)])
    def test_truediv(self, a, b):
        op = operator.truediv

        @numba.njit
        def overload(a, b):
            return op(a, b)

        ab = op(a, b)
        ab_alt = overload(a, b)
        assert ab == ab_alt
        assert ab.layout is ab_alt.layout
        # numba disagrees with numpy about what type `int` is on windows, so we
        # can't directly compare the dtypes. We only care that the float / int
        # state is kept anyway.
        assert ab.value.dtype.kind == ab_alt.value.dtype.kind

    @pytest.mark.parametrize("op", [
        pytest.param(getattr(operator, op), id=op)
        for op in ['pos', 'neg', 'invert']
    ])
    @pytest.mark.parametrize("a", [layout.scalar, e1, 0.5*e1, e1^e2, 1 + (e1^e2)])
    def test_unary(self, op, a):
        @numba.njit
        def overload(a):
            return op(a)

        ret = op(a)
        ret_alt = overload(a)
        assert ret == ret_alt
        assert ret.layout is ret_alt.layout
        assert ret.value.dtype == ret_alt.value.dtype

    # We have a special overload for literal arguments for speed
    def literal_grade_func(a, grade):
        @numba.njit
        def grade_func(a):
            return a(grade)
        return grade_func(a)

    @numba.njit
    def grade_func(a, grade):
        return a(grade)

    # vararg versions
    def literal_grade_func_star(a, *grades):
        @numba.njit
        def grade_func(a):
            return a(*grades)
        return grade_func(a)

    @numba.njit
    def grade_func_star(a, *grades):
        return a(*grades)

    @pytest.mark.parametrize('func', [
        literal_grade_func, grade_func,
        literal_grade_func_star, grade_func_star,
    ])
    def test_grade_projection(self, func):
        a = 1 + e1 + (e1^e2) + (e1^e2^e3) + (e1^e2^e3^e4) + (e1^e2^e3^e4^e5)

        assert func(a, 0) == 1
        assert func(a, 1) == e1
        assert func(a, 2) == e1^e2
        assert func(a, 3) == e1^e2^e3
        assert func(a, 4) == e1^e2^e3^e4
        assert func(a, 5) == e1^e2^e3^e4^e5

    @pytest.mark.parametrize('func', [
        literal_grade_func_star, grade_func_star,
    ])
    def test_multiple_grade_projection(self, func):
        a = 1 + e1 + (e1^e2) + (e1^e2^e3) + (e1^e2^e3^e4) + (e1^e2^e3^e4^e5)

        assert func(a, 0, 1) == 1 + e1
        assert func(a, 0, 1, 2, 3, 4, 5) == a
Test very simple construction and field access # mvs are values, and not preserved by identity # numba disagrees with numpy about what type `int` is on windows, so we # can't directly compare the dtypes. We only care that the float / int # state is kept anyway. # `op` is not parametrized, for simplicity we only support MultiVector / scalar. # numba disagrees with numpy about what type `int` is on windows, so we # can't directly compare the dtypes. We only care that the float / int # state is kept anyway. # We have a special overload for literal arguments for speed # vararg versions