//! # Diffsol
//!
//! Diffsol is a library for solving differential equations. It provides a simple interface to
//! solve ODEs with optional mass matrices, where the user can provide the equations either as
//! closures or via strings in a domain-specific language.
//!
//! ## Solving ODEs
//!
//! The simplest way to create a new problem is to use the [OdeBuilder] struct. You can set many
//! configuration options such as the initial time ([OdeBuilder::t0]), initial step size
//! ([OdeBuilder::h0]), relative tolerance ([OdeBuilder::rtol]), absolute tolerance
//! ([OdeBuilder::atol]), parameters ([OdeBuilder::p]) and equations ([OdeBuilder::rhs_implicit],
//! [OdeBuilder::init], [OdeBuilder::mass] etc.) or leave them at their default values. Then, call
//! the [OdeBuilder::build] function to create a [OdeSolverProblem].
//!
//! You will also need to choose a matrix type to use. Diffsol can use the
//! [nalgebra](https://nalgebra.org) type via [NalgebraMat], the
//! [faer](https://github.com/sarah-ek/faer-rs) type via [FaerMat],
//! or you can use any other type that implements the [Matrix] trait.
//!
//! ## Initial state
//!
//! The solver state is held in [OdeSolverState], and contains a state vector, the gradient of the
//! state vector, the time, and the step size. The [OdeSolverProblem] class has a collection of
//! methods to create and initialise a new state for each solver ([OdeSolverProblem::bdf_state],
//! [OdeSolverProblem::rk_state], [OdeSolverProblem::rk_state_and_consistent]). Or you can manually
//! intitialise a new state using [OdeSolverState::new], or create an uninitialised state using
//! [OdeSolverState::new_without_initialise] and intitialise it manually or using the
//! [StateRefMut::set_consistent] and [StateRefMut::set_step_size] methods.
//!
//! To view the state within a solver, you can use the [OdeSolverMethod::state] or
//! [OdeSolverMethod::state_mut] methods. These will return references to the state using either
//! the [StateRef] or [StateRefMut] structs
//!
//! Note that many of the solver creation methods (e.g. [OdeSolverProblem::bdf]) will create and
//! initialise a new state for you, so you might not need to manually create a state in many cases.
//! However, if you want to have more control over the initialisation of the state, or you want to
//! reuse a state across multiple solvers, then you can create and initialise the state manually as
//! described above.
//!
//! ## The solver
//!
//! To solve the problem given the initial state, you need to choose a solver. Diffsol provides the
//! following solvers:
//!    - A Backwards Difference Formulae [Bdf] solver, suitable for stiff problems
//!      and singular mass matrices.
//!    - A Singly Diagonally Implicit Runge-Kutta (SDIRK or ESDIRK) solver
//!      [Sdirk]. You can use your own butcher tableau using [Tableau] or use one of the provided
//!      ([Tableau::tr_bdf2], [Tableau::esdirk34]).
//!    - A variable order Explict Runge-Kutta (ERK) solver, suitable for non-stiff problems.
//!      One butcher tableau is provided, the 4th order [Tableau::tsit45].
//!
//! The easiest way to create a solver is to use one of the provided methods on the
//! [OdeSolverProblem] struct ([OdeSolverProblem::bdf], [OdeSolverProblem::tr_bdf2],
//! [OdeSolverProblem::esdirk34], [OdeSolverProblem::tsit45]). These will create a new solver and a new state that is
//! initialised with the initial conditions provided in the problem. If you want more control over the
//! initialisation of the state, or you want to reuse a state across multiple solvers, then you can create
//! a state manually as described above, and then create a solver using the corresponding method on the
//! [OdeSolverProblem] struct that takes a state as an argument
//! ([OdeSolverProblem::bdf_solver], [OdeSolverProblem::tr_bdf2_solver], [OdeSolverProblem::esdirk34_solver],
//! [OdeSolverProblem::tsit45]).
//!
//! See the [OdeSolverMethod] trait for a more detailed description of the available methods on
//! each solver. Possible workflows are:
//!    - Use the high-level functions [OdeSolverMethod::solve] or
//!      [OdeSolverMethod::solve_dense] that will both initialise the problem and solve the problem up
//!      to a specific time or a sequence of times.
//!    - Use [Solution::new] or [Solution::new_dense] to create a new solution and step through
//!      one or more solve segments using the [OdeSolverMethod::solve_soln] method.
//!    - Use the [OdeSolverMethod::step] method to step the solution forward in time with an internal
//!      time step chosen by the solver to meet the error tolerances.
//!    - Use the [OdeSolverMethod::interpolate] method to interpolate the solution between he last two time steps.
//!    - Use the [OdeSolverMethod::set_stop_time] method to stop the solver at a specific time
//!      (i.e. this will override the internal time step so that the solver stops at the specified time).
//!
//! ## DiffSL
//!
//! DiffSL is a domain-specific language for specifying differential equations
//! <https://github.com/martinjrobins/diffsl>. It uses the LLVM or Cranelift compiler frameworks
//! to compile the equations to efficient machine code. The LLVM JIT backend also uses the EnzymeAD
//! library to compute the jacobian and sensitivity/adjoint equations.
//!
//! You can use DiffSL with Diffsol using the [DiffSl] struct, which is created using the
//! [OdeBuilder::build_from_diffsl] method. You need to enable one of the `diffsl-llvm*` features
//! corresponding to the version of LLVM you have installed. E.g. to use your LLVM 17 installation,
//! enable the `diffsl-llvm17` feature. Or you can use the `diffsl-cranelift` feature to use the
//! Cranelift backend.
//!
//! For more information on the DiffSL language, see the [DiffSL
//! documentation](https://martinjrobins.github.io/diffsl/)
//!
//! ## Custom ODE problems
//!
//! The [OdeBuilder] struct is the easiest way to create a problem, and can be used to create an
//! ODE problem from a set of closures or the DiffSL language. However, if this is not suitable for
//! your problem or you want more control over how your equations are implemented, you can use your
//! own structs to define the problem and wrap them in an [OdeSolverEquations] struct. See the
//! [OdeSolverEquations] struct for more information.
//!
//! ## Sparsity pattern for Jacobians and Mass matrices
//!
//! Via an implementation of [OdeEquationsImplicit], the user provides the action of the jacobian
//! on a vector `J(x) v`. By default Diffsol uses this to generate a jacobian matrix for the ODE
//! solver. For sparse jacobians, Diffsol will attempt to detect the sparsity pattern of the
//! jacobian using this function and use a sparse matrix representation internally. It attempts to
//! determine the sparsity pattern of the jacobian (i.e. its non-zero values) by passing in `NaNs`
//! for the input vector `x` and checking which elements of the output vector `J(x) v` are also
//! `NaN`, using the fact that `NaN`s propagate through most operations. However, this method is
//! not foolproof and will fail if, for example, your jacobian function uses any control flow that
//! depends on the input vector. If this is the case, you can provide the jacobian matrix directly
//! by implementing the optional [NonLinearOpJacobian::jacobian_inplace] and the
//! [LinearOp::matrix_inplace] (if applicable) functions, or by providing a sparsity pattern using
//! the [NonLinearOpJacobian::jacobian_sparsity] and [LinearOp::sparsity] functions.
//!
//! ## Events / Root finding
//!
//! Diffsol provides a simple way to detect user-provided events during the integration of the
//! ODEs. You can use this by providing a closure that has a zero-crossing at the event you want to
//! detect, using the [OdeBuilder::root] method, or by providing a [NonLinearOp] that has a
//! zero-crossing at the event you want to detect. To use the root finding feature while
//! integrating with the solver, you can use the return value of [OdeSolverMethod::step] to check
//! if an event has been detected.
//!
//! ## Forward Sensitivity Analysis
//!
//! Diffsol provides a way to compute the forward sensitivity of the solution with respect to the
//! parameters. You can provide the requires equations to the builder using
//! [OdeBuilder::rhs_sens_implicit] and [OdeBuilder::init_sens], or your equations struct must
//! implement the [OdeEquationsImplicitSens] trait, Note that by default the sensitivity equations
//! are included in the error control for the solvers, you can change this by setting tolerances
//! using the [OdeBuilder::sens_atol] and [OdeBuilder::sens_rtol] methods.
//!
//! The easiest way to obtain the sensitivity solution is to use the
//! [SensitivitiesOdeSolverMethod::solve_dense_sensitivities] method, which will solve the forward
//! problem and the sensitivity equations simultaneously and return the result. If you are manually
//! stepping the solver, you can use the [OdeSolverMethod::interpolate_sens] method to obtain the
//! sensitivity solution at a given time. Otherwise the sensitivity vectors are stored in the
//! [OdeSolverState] struct.
//!
//! ## Checkpointing
//!
//! You can checkpoint the solver at the current internal time [OdeSolverMethod::checkpoint]
//! method.
//!
//! ## Interpolation
//!
//! The [HermiteInterpolator] struct provides a way to interpolate a solution between a sequence of
//! steps. If the number of steps in your solution is too large to fit in memory, you can instead
//! use checkpointing to store the solution at a reduced set of times and dynamically interpolate
//! between these checkpoints using the [Checkpointing] struct (at the cost of recomputing the
//! solution between the checkpoints).  
//!
//! ## Quadrature and Output functions
//!
//! The [OdeSolverEquations::Out] associated type can be used to define an output function. Diffsol
//! will optionally integrate this function over the solution trajectory by using the
//! [OdeBuilder::integrate_out] method. By default, the output integration is added to the error
//! control of the solver, and the tolerances can be adjusted using the [OdeBuilder::out_atol] and
//! [OdeBuilder::out_rtol] methods. It can be removed from the error control by setting the
//! tolerances to `None`.
//!
//! ## Adjoint Sensitivity Analysis
//!
//! If you require the partial gradient of the output function with respect to the parameters and
//! your parameter vector is sufficiently large, then it is more efficient to use the adjoint
//! sensitivity method. This method uses a lagrange multiplier to derive a set of adjoint ode
//! equations that are solved backwards in time, and then used to compute the sensitivities of the
//! output function. Checkpointing is typically used to store the forward solution at a set of
//! times as theses are required to solve the adjoint equations.
//!
//! To provide the builder with the required equations, you can use the
//! [OdeBuilder::rhs_adjoint_implicit], [OdeBuilder::init_adjoint], and
//! [OdeBuilder::out_adjoint_implicit] methods, or your equations struct must implement the
//! [OdeEquationsAdjoint] trait.
//!
//! To obtain the adjoint solution, it is first required to generate a checkpointing struct using
//! either [OdeSolverMethod::solve_with_checkpointing] or
//! [OdeSolverMethod::solve_dense_with_checkpointing] methods, which will solve the forwards
//! problem. Then you can create an adjoint solver using the functions on the problem struct, for
//! example [OdeSolverProblem::bdf_solver_adjoint]. Once you have created your adjoint solver, then
//! use the [AdjointOdeSolverMethod::solve_adjoint_backwards_pass] method to solve the adjoint
//! equations backwards in time and obtain your solution. The gradients of your outputs wrt the
//! parameters are stored in the [StateRef::sg] field.
//!
//! If you wish to manually do the timestepping, then the best place to start is by looking at the
//! source code for the [AdjointOdeSolverMethod::solve_adjoint_backwards_pass] method. During the
//! solution of the forwards problem you will need to use checkpointing to store the solution at a
//! set of times, and you can see how this is done in the
//! [OdeSolverMethod::solve_with_checkpointing] method.
//!
//! ## Resets
//!
//! Diffsol supports a reset operator `R(y)` which maps the current state to a new state at the
//! same point in time. You provide the reset via methods like [OdeBuilder::reset] or
//! [OdeBuilder::reset_implicit].
//!
//! ### Automatic resets
//!
//! When using [OdeSolverMethod::solve] or [OdeSolverMethod::solve_dense], and the solver stops
//! with [OdeSolverStopReason::RootFound], the reset is applied automatically if one is
//! configured. After applying the reset, the solver continues integrating to the final time.
//!
//! ### Manual resets
//!
//! When stepping manually with [OdeSolverMethod::step] or [OdeSolverMethod::solve_soln], after
//! encountering a root event you can apply the reset via [OdeSolverMethod::apply_reset] (for
//! forward problems) or [OdeSolverMethod::apply_reset_with_sens] (for forward sensitivity
//! problems). For adjoint problems, use [OdeBuilder::reset_adjoint_implicit] during the
//! backwards pass to provide the corrections to the adjoint equations.
//!
//! ## Nonlinear and linear solvers
//!
//! Diffsol provides generic nonlinear and linear solvers that are used internally by the ODE
//! solver. You can use the solvers provided by Diffsol, or implement your own following the
//! provided traits. The linear solver trait is [LinearSolver], and the nonlinear solver trait is
//! [NonLinearSolver].
//!
//! The provided linear solvers are:
//!   - [NalgebraLU]: a direct solver that uses the LU decomposition
//!     implemented in the [nalgebra](https://nalgebra.org) library.
//!   - [FaerLU]: a direct solver that uses the LU decomposition implemented in the
//!     [faer](https://github.com/sarah-ek/faer-rs) library.
//!   - [FaerSparseLU]: a sparse direct solver that uses the sparse LU decomposition implemented
//!     in the [faer](https://github.com/sarah-ek/faer-rs).
//!
//! The provided nonlinear solvers are:
//!   - [NewtonNonlinearSolver]: a nonlinear solver that uses the Newton method.
//!
//! ## Matrix and vector types
//!
//! When solving ODEs, you will need to choose a matrix and vector type to use. Diffsol uses the
//! following types:
//!   - [NalgebraVec] and [NalgebraMat] (wrappers around [nalgebra::DMatrix] and
//!     [nalgebra::DVector] from the [nalgebra](https://nalgebra.org) library).
//!   - [FaerVec], [FaerMat] and [FaerSparseMat]
//!     (wrappers around [faer::Mat], [faer::Col] and [faer::sparse::SparseColMat]
//!     from the [faer](https://github.com/sarah-ek/faer-rs) library).
//!
//! If you wish to use your own matrix and vector types, you will need to implement the following
//! traits:
//!   - For matrices: [Matrix], [MatrixView], [MatrixViewMut], [DenseMatrix], and
//!     [MatrixCommon].
//!   - For vectors: [Vector], [VectorIndex], [VectorView], [VectorViewMut], and
//!     [VectorCommon].
//!

#[cfg(feature = "diffsl")]
pub use diffsl::execution::module::{
    CodegenModule, CodegenModuleCompile, CodegenModuleEmit, CodegenModuleJit,
};
#[cfg(feature = "diffsl")]
pub use diffsl::execution::scalar::Scalar as DiffSlScalar;
#[cfg(feature = "diffsl-cranelift")]
pub use diffsl::CraneliftJitModule;
#[cfg(feature = "diffsl-llvm")]
pub use diffsl::LlvmModule;
#[cfg(feature = "diffsl")]
pub use diffsl::ObjectModule;

/// Context objects for managing device resources for vectors and matrices (e.g. device streams, threading pools, etc.).
///
/// This module provides context types that encapsulate information about where data is stored and computed
/// (CPU, GPU, etc.). Different backends like nalgebra and faer may require different context implementations.
/// The [Context] trait defines the interface that must be implemented.
pub mod context;

/// Jacobian computation and coloring algorithms for efficient Jacobian evaluation.
///
/// This module provides utilities for:
/// - Detecting the sparsity pattern of Jacobian matrices using NaN propagation
/// - Computing efficient graph colorings of the Jacobian sparsity pattern
/// - Using the coloring to compute sparse Jacobians with fewer function evaluations
///
/// The [JacobianColoring] struct is the main entry point for computing Jacobians efficiently.
pub mod jacobian;

/// Linear solver implementations and traits.
///
/// This module defines the [LinearSolver] trait for solving linear systems and provides implementations:
/// - Direct solvers: [NalgebraLU], [FaerLU], [FaerSparseLU]
/// - Optional sparse solvers: `KLU` (requires `suitesparse` feature)
/// - GPU solvers: `CudaLU` (requires `cuda` feature)
///
/// The linear solver is a critical component used internally by nonlinear solvers to solve Newton systems.
pub mod linear_solver;

/// Matrix types and operations.
///
/// This module defines the [Matrix] trait and related traits for matrix operations:
/// - [DenseMatrix] for dense column-major matrices
/// - [MatrixView] and [MatrixViewMut] for borrowed views
/// - Sparsity detection and handling
///
/// Implementations are provided for:
/// - Dense matrices: [NalgebraMat], [FaerMat]
/// - Sparse matrices: [FaerSparseMat]
/// - GPU matrices: `CudaMat` (requires `cuda` feature)
pub mod matrix;

/// Nonlinear solver implementations and traits.
///
/// This module defines the [NonLinearSolver] trait and provides the [NewtonNonlinearSolver] implementation.
/// It also includes:
/// - [LineSearch] implementations for globalization ([NoLineSearch], [BacktrackingLineSearch])
/// - Root finding algorithms via [RootFinder]
/// - Convergence testing via [Convergence]
///
/// Nonlinear solvers are used internally by ODE solvers to solve implicit equations.
pub mod nonlinear_solver;

/// ODE equations and traits.
///
/// This module defines the [OdeEquations] trait and specialized variants:
/// - [OdeEquationsImplicit] for implicit ODEs with mass matrices
/// - [OdeEquationsImplicitSens] for forward sensitivity equations
/// - [OdeEquationsAdjoint] for adjoint sensitivity equations
///
/// It also provides implementations:
/// - [DiffSl] for equations specified in the DiffSL domain-specific language
/// - [SensEquations] and [AdjointEquations] for sensitivity computations
///
/// All the test equations used in Diffsol's test suite are also provided here.
pub mod ode_equations;

/// ODE solver implementations and traits.
///
/// This module provides the complete ODE solving interface including:
/// - [OdeSolverMethod] trait with implementations: [Bdf], [Sdirk], [ExplicitRk]
/// - [SensitivitiesOdeSolverMethod] trait for solving forward sensitivity problems
/// - [AdjointOdeSolverMethod] trait for solving adjoint sensitivity problems
/// - [OdeSolverProblem] for problem setup (equations, parameters, tolerances, solver options etc.)
/// - [OdeSolverState] for managing solution state (including state vector, sensitivities, time, step size etc.)
/// - [OdeBuilder] for convenient problem construction (builds and configures [OdeSolverProblem])
/// - [Checkpointing] and [HermiteInterpolator] for solution interpolation
pub mod ode_solver;

/// Operator types and traits (e.g. non-linear, linear and constant operators; as well as their jacobians).
///
/// This module defines fundamental operator traits, for example:
/// - [NonLinearOp] and variants for Jacobians and sensitivities
/// - [LinearOp] for linear operators
/// - [ConstantOp] for constants
///
/// It also provides concrete implementations, for example:
/// - [Closure] for wrapping user-provided closures
/// - [LinearClosure] for linear operators
/// - [ConstantClosure] for constants
/// - [MatrixOp] for explicit matrix operators
pub mod op;

/// Scalar types and traits.
///
/// This module defines the [Scalar] trait that all floating-point types used in DiffSol must implement.
/// It aggregates requirements from nalgebra, faer, and num_traits to ensure compatibility with linear algebra operations.
///
/// Implementations are provided for `f32` and `f64`.
/// GPU scalar types are available via `ScalarCuda` (requires `cuda` feature).
pub mod scalar;

/// Vector types and traits.
///
/// This module defines the [Vector] trait and related traits for vector operations:
/// - [VectorView] and [VectorViewMut] for borrowed views
/// - [VectorIndex] for index collections
/// - [VectorHost] for CPU-resident vectors with direct access
///
/// Implementations are provided for:
/// - [NalgebraVec] using nalgebra vectors
/// - [FaerVec] using faer vectors
/// - `CudaVec` for GPU computation (requires `cuda` feature)
pub mod vector;

/// Error types and handling.
///
/// This module defines the [DiffsolError] enum and specialized error variants
/// for different failure modes in ODE solving, including parsing, compilation,
/// and numerical errors.
pub mod error;

pub use error::DiffsolError;

#[cfg(feature = "sundials")]
pub mod sundials_sys;

pub use linear_solver::LinearSolver;
pub use linear_solver::{faer::sparse_lu::FaerSparseLU, FaerLU, NalgebraLU};

pub use context::{faer::FaerContext, nalgebra::NalgebraContext, Context};

#[cfg(feature = "suitesparse")]
pub use linear_solver::suitesparse::klu::KLU;

#[cfg(feature = "diffsl")]
pub use ode_equations::diffsl::{DiffSl, DiffSlContext};

pub use jacobian::{
    find_adjoint_non_zeros, find_jacobian_non_zeros, find_matrix_non_zeros,
    find_sens_adjoint_non_zeros, find_sens_non_zeros, find_transpose_non_zeros, JacobianColoring,
};
use matrix::extract_block::ColMajBlock;
pub use matrix::{
    default_solver::DefaultSolver, dense_faer_serial::FaerMat, dense_nalgebra_serial::NalgebraMat,
    sparse_faer::FaerSparseMat, DenseMatrix, Matrix, MatrixCommon,
};

use matrix::{
    sparsity::Dense, sparsity::DenseRef, sparsity::MatrixSparsity, sparsity::MatrixSparsityRef,
    MatrixHost, MatrixRef, MatrixView, MatrixViewMut,
};
use nonlinear_solver::{
    convergence::Convergence, convergence::ConvergenceStatus, root::RootFinder,
};
pub use nonlinear_solver::{
    line_search::{BacktrackingLineSearch, LineSearch, NoLineSearch},
    newton::NewtonNonlinearSolver,
    NonLinearSolver,
};
pub use ode_equations::{
    adjoint_equations::AdjointContext, adjoint_equations::AdjointEquations,
    adjoint_equations::AdjointInit, adjoint_equations::AdjointRhs, sens_equations::SensEquations,
    sens_equations::SensInit, sens_equations::SensRhs, AugmentedOdeEquations,
    AugmentedOdeEquationsImplicit, NoAug, OdeEquations, OdeEquationsAdjoint, OdeEquationsImplicit,
    OdeEquationsImplicitAdjoint, OdeEquationsImplicitSens, OdeEquationsRef, OdeEquationsStoch,
    OdeSolverEquations,
};
use ode_solver::jacobian_update::JacobianUpdate;
pub use ode_solver::sde::SdeSolverMethod;
pub use ode_solver::state::{StateRef, StateRefMut};
pub use ode_solver::{
    adjoint::AdjointOdeSolverMethod,
    bdf::Bdf,
    bdf_state::BdfState,
    builder::OdeBuilder,
    checkpointing::Checkpointing,
    checkpointing::CheckpointingPath,
    checkpointing::HermiteInterpolator,
    config::{
        BdfConfig, ExplicitRkConfig, OdeSolverConfig, OdeSolverConfigMut, OdeSolverConfigRef,
        SdirkConfig,
    },
    explicit_rk::ExplicitRk,
    method::AugmentedOdeSolverMethod,
    method::OdeSolverMethod,
    method::OdeSolverStopReason,
    no_checkpointing_solver::NoCheckpointingSolver,
    problem::{InitialConditionSolverOptions, OdeSolverOptions, OdeSolverProblem},
    sdirk::Sdirk,
    sdirk_state::RkState,
    sensitivities::SensitivitiesOdeSolverMethod,
    solution::Solution,
    state::OdeSolverState,
    tableau::Tableau,
};
pub use op::constant_op::{ConstantOp, ConstantOpSens, ConstantOpSensAdjoint};
pub use op::linear_op::{LinearOp, LinearOpSens, LinearOpTranspose};
pub use op::nonlinear_op::{
    NonLinearOp, NonLinearOpAdjoint, NonLinearOpJacobian, NonLinearOpSens, NonLinearOpSensAdjoint,
    NonLinearOpTimePartial,
};
pub use op::stoch::{StochOp, StochOpKind};
pub use op::{
    closure::Closure, closure_with_adjoint::ClosureWithAdjoint, constant_closure::ConstantClosure,
    constant_closure_with_adjoint::ConstantClosureWithAdjoint, linear_closure::LinearClosure,
    matrix::MatrixOp, unit::UnitCallable, BuilderOp, Op, ParameterisedOp,
};
use op::{
    closure_no_jac::ClosureNoJac, closure_with_sens::ClosureWithSens,
    constant_closure_with_sens::ConstantClosureWithSens, init::InitOp,
};
pub use scalar::{FaerScalar, IndexType, NalgebraScalar, Scalar, Scale};
pub use vector::DefaultDenseMatrix;
pub use vector::{
    faer_serial::{FaerVec, FaerVecIndex, FaerVecMut, FaerVecRef},
    nalgebra_serial::{NalgebraVec, NalgebraVecMut, NalgebraVecRef},
    Vector, VectorCommon, VectorHost, VectorIndex, VectorRef, VectorView, VectorViewMut,
};

#[cfg(feature = "cuda")]
pub use context::cuda::CudaContext;
#[cfg(feature = "cuda")]
pub use linear_solver::cuda::lu::CudaLU;
#[cfg(feature = "cuda")]
pub use matrix::cuda::{CudaMat, CudaMatMut, CudaMatRef};
#[cfg(feature = "cuda")]
pub use scalar::cuda::{CudaType, ScalarCuda};
#[cfg(feature = "cuda")]
pub use vector::cuda::{CudaIndex, CudaVec, CudaVecMut, CudaVecRef};

pub use scalar::scale;
