// SPDX-FileCopyrightText: 2017 - 2024 The Ginkgo authors
//
// SPDX-License-Identifier: BSD-3-Clause

#ifndef GKO_PUBLIC_CORE_BASE_VERSION_HPP_
#define GKO_PUBLIC_CORE_BASE_VERSION_HPP_


#include <ostream>


#include <ginkgo/config.hpp>
#include <ginkgo/core/base/types.hpp>


namespace gko {


/**
 * This structure is used to represent versions of various Ginkgo modules.
 *
 * Version structures can be compared using the usual relational operators.
 *
 * @ingroup ginkgo_version
 */
struct version {
    constexpr version(const uint64 major, const uint64 minor,
                      const uint64 patch, const char* tag)
        : major{major}, minor{minor}, patch{patch}, tag{tag}
    {}

    /**
     * The major version number.
     */
    const uint64 major;

    /**
     * The minor version number.
     */
    const uint64 minor;

    /**
     * The patch version number.
     */
    const uint64 patch;

    /**
     * Addition tag string that describes the version in more detail.
     *
     * It does not participate in comparisons.
     */
    const char* const tag;
};

inline bool operator==(const version& first, const version& second)
{
    return first.major == second.major && first.minor == second.minor &&
           first.patch == second.patch;
}

inline bool operator!=(const version& first, const version& second)
{
    return !(first == second);
}

inline bool operator<(const version& first, const version& second)
{
    if (first.major < second.major) return true;
    if (first.major == second.major && first.minor < second.minor) return true;
    if (first.major == second.major && first.minor == second.minor &&
        first.patch < second.patch)
        return true;
    return false;
}

inline bool operator<=(const version& first, const version& second)
{
    return !(second < first);
}

inline bool operator>(const version& first, const version& second)
{
    return second < first;
}

inline bool operator>=(const version& first, const version& second)
{
    return !(first < second);
}

#undef GKO_ENABLE_VERSION_COMPARISON


/**
 * Prints version information to a stream.
 *
 * @param os  output stream
 * @param ver  version structure
 *
 * @return os
 */
inline std::ostream& operator<<(std::ostream& os, const version& ver)
{
    os << ver.major << "." << ver.minor << "." << ver.patch;
    if (ver.tag) {
        os << " (" << ver.tag << ")";
    }
    return os;
}


/**
 * Ginkgo uses version numbers to label new features and to communicate backward
 * compatibility guarantees:
 *
 * 1.  Versions with different major version number have incompatible
 *     interfaces (parts of the earlier interface may not be present anymore,
 *     and new interfaces can appear).
 * 2.  Versions with the same major number X, but different minor numbers Y1 and
 *     Y2 numbers keep the same interface as version X.0.0, but additions to
 *     the interface in X.0.0 present in X.Y1.0 may not be present in X.Y2.0
 *     and vice versa.
 * 3.  Versions with the same major an minor version numbers, but different
 *     patch numbers have exactly the same interface, but the functionality may
 *     be different (something that is not implemented or has a bug in an
 *     earlier version may have this implemented or fixed in a later version).
 *
 * This structure provides versions of different parts of Ginkgo: the headers,
 * the core and the kernel modules (reference, OpenMP, CUDA, HIP, DPCPP).
 * To obtain an instance of version_info filled with information about the
 * current version of Ginkgo, call the version_info::get() static method.
 */
class version_info {
public:
    /**
     * Returns an instance of version_info.
     *
     * @return an instance of version info
     */
    static const version_info& get()
    {
        static version_info info{};
        return info;
    }

    /**
     * Contains version information of the header files.
     */
    version header_version;

    /**
     * Contains version information of the core library.
     *
     * This is the version of the static/shared library called "ginkgo".
     */
    version core_version;

    /**
     * Contains version information of the reference module.
     *
     * This is the version of the static/shared library called
     * "ginkgo_reference".
     */
    version reference_version;

    /**
     * Contains version information of the OMP module.
     *
     * This is the version of the static/shared library called "ginkgo_omp".
     */
    version omp_version;

    /**
     * Contains version information of the CUDA module.
     *
     * This is the version of the static/shared library called "ginkgo_cuda".
     */
    version cuda_version;

    /**
     * Contains version information of the HIP module.
     *
     * This is the version of the static/shared library called "ginkgo_hip".
     */
    version hip_version;

    /**
     * Contains version information of the DPC++ module.
     *
     * This is the version of the static/shared library called "ginkgo_dpcpp".
     */
    version dpcpp_version;

private:
    static constexpr version get_header_version() noexcept
    {
        return version{GKO_VERSION_MAJOR, GKO_VERSION_MINOR, GKO_VERSION_PATCH,
                       GKO_VERSION_TAG};
    }

    static version get_core_version() noexcept;

    static version get_reference_version() noexcept;

    static version get_omp_version() noexcept;

    static version get_cuda_version() noexcept;

    static version get_hip_version() noexcept;

    static version get_dpcpp_version() noexcept;

    version_info()
        : header_version{get_header_version()},
          core_version{get_core_version()},
          reference_version{get_reference_version()},
          omp_version{get_omp_version()},
          cuda_version{get_cuda_version()},
          hip_version{get_hip_version()},
          dpcpp_version{get_dpcpp_version()}
    {}
};


/**
 * Prints library version information in human-readable format to a stream.
 *
 * @param os  output stream
 * @param ver_info  version information
 *
 * @return os
 */
std::ostream& operator<<(std::ostream& os, const version_info& ver_info);


}  // namespace gko


#endif  // GKO_PUBLIC_CORE_BASE_VERSION_HPP_
