// Copyright (C) 2026 Kiyotsugu Arai
// SPDX-License-Identifier: LGPL-3.0-or-later

// algebraic_concepts.hpp
//
// Concept definitions for algebraic structures
//
// This file defines the major concepts of abstract algebra as C++ concepts.
// This allows verifying at compile time that a type satisfies the requirements
// of a particular algebraic structure (group, ring, field, etc.).
//
// Supported structures:
// - Additive monoid, additive group
// - Multiplicative monoid, multiplicative group
// - Ring, commutative ring, division ring, field
// - Ordered field
// - Vector space
// - Structural concepts: quaternion type, rational type
// - Numeric state management

#ifndef SANGI_ALGEBRAIC_CONCEPTS_HPP
#define SANGI_ALGEBRAIC_CONCEPTS_HPP

#include <concepts>
#include <type_traits>
#include <cmath>
#include <math/core/Complex.hpp>
#include <math/core/traits.hpp> // use concepts from traits.hpp

namespace sangi::concepts {

    //-----------------------------------------------------------------------------
    // Concepts for basic mathematical operations
    //-----------------------------------------------------------------------------

    // A type for which basic mathematical operations are defined
    template<typename T>
    concept HasBasicArithmetic = requires(T a, T b) {
        { a + b } -> std::convertible_to<T>;
        { a - b } -> std::convertible_to<T>;
        { a* b } -> std::convertible_to<T>;
        { a / b } -> std::convertible_to<T>;
        { -a } -> std::convertible_to<T>;
    };

    // A type for which comparison operators are defined
    template<typename T>
    concept Comparable = requires(T a, T b) {
        { a == b } -> std::convertible_to<bool>;
        { a != b } -> std::convertible_to<bool>;
        { a < b } -> std::convertible_to<bool>;
        { a <= b } -> std::convertible_to<bool>;
        { a > b } -> std::convertible_to<bool>;
        { a >= b } -> std::convertible_to<bool>;
    };

    // A type that supports equality comparison only
    template<typename T>
    concept EqualityComparable = requires(T a, T b) {
        { a == b } -> std::convertible_to<bool>;
        { a != b } -> std::convertible_to<bool>;
    };

    // A type providing access to mathematical constants (π, e, etc.)
    template<typename T>
    concept HasMathConstants = requires {
        { T::pi() } -> std::convertible_to<T>;
        { T::e() } -> std::convertible_to<T>;
    };

    //-----------------------------------------------------------------------------
    // State management concepts - reuse definitions from traits.hpp
    //-----------------------------------------------------------------------------

    // A type capable of detecting and handling numeric states
    // Already defined in traits.hpp

    //-----------------------------------------------------------------------------
    // Concepts for algebraic structures
    //-----------------------------------------------------------------------------

    // Additive monoid
    // Requirements: associativity, identity element (zero)
    template<typename T>
    concept AdditiveMonoid = requires(T a, T b) {
        { a + b } -> std::convertible_to<T>;  // binary operation (closed)
        { T{ 0 } } -> std::convertible_to<T>;   // additive identity
        // associativity (a + b) + c == a + (b + c) cannot be verified at compile time
    };

    // Additive group
    // Requirements: additive monoid + inverse element
    template<typename T>
    concept AdditiveGroup = AdditiveMonoid<T> && requires(T a) {
        { -a } -> std::convertible_to<T>;     // additive inverse
        { a - T{ 0 } } -> std::convertible_to<T>; // subtraction
        // commutativity a + b == b + a cannot be verified at compile time
    };

    // Additive abelian group (commutative group)
    // Note: since C++ concepts cannot enforce commutativity,
    // this is defined as an additional concept that carries the meaning of having commutativity
    template<typename T>
    concept AdditiveAbelianGroup = AdditiveGroup<T>;

    // Multiplicative monoid
    // Requirements: associativity, identity element (1)
    template<typename T>
    concept MultiplicativeMonoid = requires(T a, T b) {
        { a* b } -> std::convertible_to<T>;  // binary operation (closed)
        { T{ 1 } } -> std::convertible_to<T>;   // multiplicative identity
        // associativity (a * b) * c == a * (b * c) cannot be verified at compile time
    };

    // Multiplicative group
    // Requirements: multiplicative monoid + inverse element
    template<typename T>
    concept MultiplicativeGroup = MultiplicativeMonoid<T> && requires(T a) {
        { T{ 1 } / a } -> std::convertible_to<T>; // multiplicative inverse
        // commutativity a * b == b * a cannot be verified at compile time
    };

    // Multiplicative abelian group (commutative group)
    template<typename T>
    concept MultiplicativeAbelianGroup = MultiplicativeGroup<T>;

    // Ring
    // Requirements: additive abelian group + multiplicative monoid + distributive law
    template<typename T>
    concept Ring = AdditiveAbelianGroup<T> && MultiplicativeMonoid<T>;
    // distributive law a * (b + c) == a * b + a * c cannot be verified at compile time

    // Commutative ring
    // Requirements: ring + commutativity of multiplication
    template<typename T>
    concept CommutativeRing = Ring<T>;
    // commutativity of multiplication cannot be verified at compile time, but is conceptually important

    // Integral domain
    // Requirements: commutative ring + no zero divisors (ab=0 ⇒ a=0 or b=0)
    template<typename T>
    concept IntegralDomain = CommutativeRing<T>;
    // the property of having no zero divisors cannot be verified at compile time

    // Field
    // Already defined in traits.hpp, so used directly
    // The corresponding concept is consolidated into one
    // OrderedField is likewise used directly

    // Division ring (skew field, Division Ring)
    // Requirements: ring + every nonzero element has a multiplicative inverse
    // Difference from a field (Field): does not require commutativity of multiplication
    // Example: Quaternion<T> (non-commutative); a field is a special case of a division ring
    template<typename T>
    concept DivisionRing = Ring<T> && MultiplicativeGroup<T>;

    // Scalar field - a general concept representing a type usable as a scalar in code
    template<typename T>
    concept Scalar = Field<T> || std::integral<T>;

    // Scalar with state management
    template<typename T>
    concept ScalarWithStateManagement = Scalar<T> && HasNumericState<T>;

    //-----------------------------------------------------------------------------
    // Vector spaces and their extensions - prefer those already defined in traits.hpp
    //-----------------------------------------------------------------------------

    // Inner product space
    // Requirements: vector space + inner product operation
    template<typename V, typename S = double>
    concept InnerProductSpace = VectorSpace<V, S>&& requires(V v, V w) {
        { inner_product(v, w) } -> std::convertible_to<S>; // inner product
        // properties of the inner product (positive definiteness, linearity, symmetry) cannot be verified at compile time
    };

    // Inner product space with state management
    template<typename V, typename S = double>
    concept InnerProductSpaceWithStateManagement = InnerProductSpace<V, S>&& HasNumericState<V>&& HasNumericState<S>;

    // Normed space
    // Requirements: vector space + norm operation
    template<typename V, typename S = double>
    concept NormedVectorSpace = VectorSpace<V, S>&& requires(V v) {
        { norm(v) } -> std::convertible_to<S>; // norm
        // properties of the norm (non-negativity, homogeneity, triangle inequality) cannot be verified at compile time
    };

    // Normed space with state management
    template<typename V, typename S = double>
    concept NormedVectorSpaceWithStateManagement = NormedVectorSpace<V, S>&& HasNumericState<V>&& HasNumericState<S>;

    // Banach space (complete normed space)
    // Note: since completeness cannot be verified at compile time,
    // this is used as a conceptual marker
    template<typename V, typename S = double>
    concept BanachSpace = NormedVectorSpace<V, S>;

    // Hilbert space (complete inner product space)
    // Note: since completeness cannot be verified at compile time,
    // this is used as a conceptual marker
    template<typename V, typename S = double>
    concept HilbertSpace = InnerProductSpace<V, S>;

    // Concept of a linear map
    template<typename F, typename V1, typename V2, typename S>
    concept LinearMap = VectorSpace<V1, S>&& VectorSpace<V2, S>&& requires(F f, V1 v, V1 w, S s) {
        { f(v) } -> std::convertible_to<V2>;
        { f(v + w) } -> std::convertible_to<V2>; // additivity
        { f(s * v) } -> std::convertible_to<V2>; // homogeneity
    };

    // Linear map with state management
    template<typename F, typename V1, typename V2, typename S>
    concept LinearMapWithStateManagement = LinearMap<F, V1, V2, S>&&
        HasNumericState<V1>&&
        HasNumericState<V2>&&
        HasNumericState<S>;

    // Concept of a conjugate-linear map
    template<typename F, typename V1, typename V2, typename S>
    concept ConjugateLinearMap = VectorSpace<V1, S>&& VectorSpace<V2, S>&& requires(F f, V1 v, V1 w, S s) {
        { f(v) } -> std::convertible_to<V2>;
        { f(v + w) } -> std::convertible_to<V2>; // additivity
        { f(s * v) } -> std::convertible_to<V2>; // conjugate homogeneity
        // full verification of conjugate homogeneity is not possible (f(s * v) == conj(s) * f(v) cannot be checked at compile time)
    };

    //-----------------------------------------------------------------------------
    // Concepts related to numerical computation
    //-----------------------------------------------------------------------------

    // A type that supports numerical operations
    template<typename T>
    concept Numeric = HasBasicArithmetic<T> && requires(T a) {
        { std::abs(a) } -> std::convertible_to<T>;
        { std::max(a, a) } -> std::convertible_to<T>;
        { std::min(a, a) } -> std::convertible_to<T>;
    };

    // Numeric type with state management
    template<typename T>
    concept NumericWithState = Numeric<T> && HasNumericState<T>;

    // Concept for integer types
    template<typename T>
    concept IntegerType = std::integral<T> && Ring<T>;

    // Concept for non-negative integer types
    // Used when guaranteeing non-negative values such as array indices, sizes, counters, etc.
    // unsigned types are not closed under additive inverse, so they do not qualify as a Ring or above.
    template<typename T>
    concept NonNegativeIntegerType = std::unsigned_integral<T>;

    // Integer type with state management
    template<typename T>
    concept IntegerTypeWithStateManagement = IntegerType<T> && HasNumericState<T>;

    // Concept for floating-point types
    // std::floating_point is only float/double/long double, but
    // to also include multi-precision floats such as sangi::Float, we use OrderedField && !integral
    template<typename T>
    concept FloatingPointType = OrderedField<T> && !std::integral<T>;

    // Floating-point type with state management
    template<typename T>
    concept FloatingPointTypeWithStateManagement = FloatingPointType<T> && HasNumericState<T>;

    // Concept for complex number types
    template<typename T>
    concept ComplexType = requires(T a) {
        typename T::value_type; // type of the real/imaginary parts
        requires FloatingPointType<typename T::value_type>;
        { std::real(a) } -> std::convertible_to<typename T::value_type>;
        { std::imag(a) } -> std::convertible_to<typename T::value_type>;
        { std::abs(a) } -> std::convertible_to<typename T::value_type>;
        { std::arg(a) } -> std::convertible_to<typename T::value_type>;
        { std::conj(a) } -> std::convertible_to<T>;
    };

    // Complex number type with state management
    template<typename T>
    concept ComplexTypeWithStateManagement = ComplexType<T> && HasNumericState<T>;

    // Concept for quaternion types (structural)
    // A type with 4 components (w, x, y, z), conjugate, norm, and inverse
    // Quaternion<T> satisfies DivisionRing but not Field (non-commutative)
    template<typename T>
    concept QuaternionType = requires(T q) {
        typename T::value_type;                                     // type of the components
        { q.w } -> std::convertible_to<typename T::value_type>;     // scalar part
        { q.x } -> std::convertible_to<typename T::value_type>;     // i component
        { q.y } -> std::convertible_to<typename T::value_type>;     // j component
        { q.z } -> std::convertible_to<typename T::value_type>;     // k component
        { q.conj() } -> std::convertible_to<T>;                     // conjugate
        { q.norm() } -> std::convertible_to<typename T::value_type>; // norm
        { q.inverse() } -> std::convertible_to<T>;                  // inverse
    };

    // Concept for rational number types (structural)
    // A type with access to numerator and denominator
    // Since Rational satisfies Field → OrderedField → Scalar,
    // constraining generic algorithms with Field/Scalar is sufficient.
    // This concept is used when direct access to the numerator and denominator is required.
    template<typename T>
    concept RationalType = Field<T> && requires(T r) {
        { r.numerator() };    // numerator
        { r.denominator() };  // denominator
    };

    //-----------------------------------------------------------------------------
    // Linear algebra concepts - prefer those already defined in traits.hpp
    //-----------------------------------------------------------------------------

    // Concept for square matrices
    template<typename M, typename T>
    concept SquareMatrixOf = MatrixOf<M, T>&& requires(M m) {
        requires m.rows() == m.cols();
    { m.is_square() } -> std::convertible_to<bool>;
    };

    // Concept for symmetric matrices
    template<typename M, typename T>
    concept SymmetricMatrixOf = SquareMatrixOf<M, T>;
    // Since the actual check for symmetry cannot be done at compile time, this concept
    // functions as a marker. Runtime verification is required separately.

    // Concept for orthogonal matrices
    template<typename M, typename T>
    concept OrthogonalMatrixOf = SquareMatrixOf<M, T>;
    // Since the actual check for orthogonality (m^T * m = I) cannot be done at compile time,
    // this concept functions as a marker.

    // Concept for tensors
    // A multidimensional array type with rank(), shape(i), size()
    template<typename T, typename S>
    concept TensorOf = requires(T t, size_t i, size_t j) {
        { t.rank() }    -> std::convertible_to<size_t>;  // rank
        { t.shape(i) }  -> std::convertible_to<size_t>;  // dimension of each axis
        { t.size() }    -> std::convertible_to<size_t>;  // total number of elements
        { t(i, j) }     -> std::convertible_to<S>;       // element access (rank≥2)
    };

    //-----------------------------------------------------------------------------
    // C++23 extensions (conditionally enabled)
    //-----------------------------------------------------------------------------
#if __cplusplus >= 202302L

    // Concept of a real field
    template<typename T>
    concept RealField = OrderedField<T> && requires(T a) {
        { std::sqrt(a) } -> std::convertible_to<T>;
        { std::abs(a) } -> std::convertible_to<T>;
        { std::pow(a, a) } -> std::convertible_to<T>;
        // other operations expected of a real field
    };

    // Real field with state management
    template<typename T>
    concept RealFieldWithStateManagement = RealField<T> && HasNumericState<T>;

    // Concept of a complex field
    template<typename T>
    concept ComplexField = Field<T> && requires(T a) {
        typename T::value_type; // type of the real/imaginary parts
        { std::real(a) } -> std::convertible_to<typename T::value_type>;
        { std::imag(a) } -> std::convertible_to<typename T::value_type>;
        { std::abs(a) } -> std::convertible_to<typename T::value_type>;
        { std::arg(a) } -> std::convertible_to<typename T::value_type>;
        { std::conj(a) } -> std::convertible_to<T>;
        // other operations expected of a complex field
    };

    // Complex field with state management
    template<typename T>
    concept ComplexFieldWithStateManagement = ComplexField<T> && HasNumericState<T>;

    // Real vector space
    template<typename V, typename S>
    concept RealVectorSpace = VectorSpace<V, S>&& RealField<S>;

    // Real vector space with state management
    template<typename V, typename S>
    concept RealVectorSpaceWithStateManagement = RealVectorSpace<V, S>&&
        HasNumericState<V>&&
        HasNumericState<S>;

    // Complex vector space
    template<typename V, typename S>
    concept ComplexVectorSpace = VectorSpace<V, S>&& ComplexField<S>;

    // Complex vector space with state management
    template<typename V, typename S>
    concept ComplexVectorSpaceWithStateManagement = ComplexVectorSpace<V, S>&&
        HasNumericState<V>&&
        HasNumericState<S>;

    // Concept for special matrices
    template<typename M, typename T>
    concept SparseMatrixOf = MatrixOf<M, T>&& requires(M m) {
        { m.non_zeros() } -> std::convertible_to<std::size_t>; // number of non-zero elements
    };

    // Concept for band matrices
    template<typename M, typename T>
    concept BandMatrixOf = MatrixOf<M, T>&& requires(M m) {
        { m.lower_bandwidth() } -> std::convertible_to<std::size_t>;
        { m.upper_bandwidth() } -> std::convertible_to<std::size_t>;
    };

#endif // C++23 support

    //-----------------------------------------------------------------------------
    // Additional mathematical concepts
    //-----------------------------------------------------------------------------

    // Module - generalization of a vector space
    template<typename M, typename R>
    concept Module = AdditiveAbelianGroup<M> && Ring<R> && requires(M m, M n, R r, R s) {
        { r* m } -> std::convertible_to<M>;  // scalar multiplication
        { m* r } -> std::convertible_to<M>;  // scalar multiplication (also possible from the right)
        // the module axioms cannot be verified at compile time
    };

    // Module with state management
    template<typename M, typename R>
    concept ModuleWithStateManagement = Module<M, R>&&
        HasNumericState<M>&&
        HasNumericState<R>;

    // Algebra - a vector space equipped with a multiplication
    template<typename A, typename F>
    concept Algebra = VectorSpace<A, F>&& Ring<A>&& requires(A a, A b, F f) {
        { f* (a * b) } -> std::convertible_to<A>; // part of bilinearity
        // the algebra axioms cannot be verified at compile time
    };

    // Algebra with state management
    template<typename A, typename F>
    concept AlgebraWithStateManagement = Algebra<A, F>&&
        HasNumericState<A>&&
        HasNumericState<F>;

    // Associative algebra - an algebra whose multiplication is associative
    template<typename A, typename F>
    concept AssociativeAlgebra = Algebra<A, F>;
    // Since associativity cannot be verified at compile time, this concept functions as a marker.

    // Finite-dimensional vector space
    // Note: since the actual dimension cannot be checked at compile time,
    // this is used only as a conceptual marker
    template<typename V, typename S>
    concept FiniteDimensionalVectorSpace = VectorSpace<V, S>&& requires(V v) {
        { v.size() } -> std::convertible_to<std::size_t>; // must have a size method
    };

    // Lie algebra - a non-associative algebra with anticommutativity
    template<typename L, typename F>
    concept LieAlgebra = VectorSpace<L, F>&& requires(L a, L b) {
        { a* b } -> std::convertible_to<L>; // Lie bracket
        // anticommutativity and the Jacobi identity cannot be verified at compile time
    };

    //-----------------------------------------------------------------------------
    // Helper functions and metafunctions - refer to those already defined in traits.hpp
    //-----------------------------------------------------------------------------

    // Removed because already defined in traits.hpp:
    // is_field_v, is_ordered_field_v, is_vector_space_v, is_matrix_of_v, is_vector_of_v
    // has_numeric_state_management_v, has_divergence_handling_v, has_overflow_detection_v

    // Duck typing support
    // Removed because already defined in traits.hpp:
    // has_addition_v, has_multiplication_v, has_division_v, has_negation_v
    // has_equality_v, has_comparison_v, has_abs_v, has_sqrt_v
    // has_is_normal_v, has_is_nan_v, has_is_infinite_v, has_is_divergent_v

} // namespace sangi::concepts

#endif // SANGI_ALGEBRAIC_CONCEPTS_HPP