// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Collect and print statistics
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of either the GNU Lesser General Public License Version 3
// or the Perl Artistic License Version 2.0.
// SPDX-FileCopyrightText: 2005-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************

#ifndef VERILATOR_V3STATS_H_
#define VERILATOR_V3STATS_H_

#include "config_build.h"
#include "verilatedos.h"

#include "V3Error.h"

class AstNetlist;

//============================================================================

class VDouble0 final {
    // Double counter, initializes to zero for easy use
    double m_d = 0.0;  ///< Count of occurrences/ value
public:
    // METHODS
    VDouble0() = default;
    ~VDouble0() = default;

    // Implicit conversion operators:
    explicit VDouble0(const uint64_t v)
        : m_d{static_cast<double>(v)} {}
    operator double() const { return m_d; }

    // Explicit operators:
    VDouble0& operator++() {  // prefix
        ++m_d;
        return *this;
    }
    VDouble0 operator++(int) {  // postfix
        const VDouble0 old = *this;
        ++m_d;
        return old;
    }
    VDouble0& operator=(const double v) {
        m_d = v;
        return *this;
    }
    VDouble0& operator+=(const double v) {
        m_d += v;
        return *this;
    }
    VDouble0& operator-=(const double v) {
        m_d -= v;
        return *this;
    }
};

//============================================================================

class V3Statistic final {
    // A statistical entry we want published into the database
    const string m_name;  ///< Name of this statistic
    double m_value;  ///< Value of statistic (count, ratio, etc.)
    unsigned m_precision;  ///< Precision to print with (number of fractional digits)
    const string m_stage;  ///< Runtime stage
    const bool m_sumit;  ///< Do summation of similar stats
    const bool m_perf;  ///< Performance section
    bool m_printit = true;  ///< Print the results
public:
    // METHODS
    string stage() const VL_MT_SAFE { return m_stage; }
    string name() const VL_MT_SAFE { return m_name; }
    double value() const VL_MT_SAFE { return m_value; }
    unsigned precision() const VL_MT_SAFE { return m_precision; }
    bool sumit() const VL_MT_SAFE { return m_sumit; }
    bool perf() const VL_MT_SAFE { return m_perf; }
    bool printit() const VL_MT_SAFE { return m_printit; }
    virtual void dump(std::ofstream& os) const VL_MT_SAFE;
    void combineWith(V3Statistic* otherp) {
        m_value += otherp->value();
        otherp->m_printit = false;
    }
    // CONSTRUCTORS
    V3Statistic(const string& stage, const string& name, double value, unsigned precision,
                bool sumit, bool perf)
        : m_name{name}
        , m_value{value}
        , m_precision{precision}
        , m_stage{stage}
        , m_sumit{sumit}
        , m_perf{perf} {}
    virtual ~V3Statistic() = default;
};

//============================================================================

class V3Stats final {
    static V3Mutex s_mutex;  // Protects accesses

public:
    // Symbolic names for some statistics that are later read by summaryReport()
    static constexpr const char* STAT_CPP_CHARS = "Output, C++ bytes written";
    static constexpr const char* STAT_CPP_FILES = "Output, C++ files written";
    static constexpr const char* STAT_CPUTIME = "CPU time, Total (sec)";
    static constexpr const char* STAT_MODEL_SIZE = "Size prediction, Model total (bytes)";
    static constexpr const char* STAT_SOURCE_CHARS = "Input, Verilog bytes read";
    static constexpr const char* STAT_SOURCE_MODULES = "Input, Verilog modules read";
    static constexpr const char* STAT_WALLTIME = "Wall time, Total (sec)";
    static constexpr const char* STAT_WALLTIME_BUILD = "Wall time, Build (sec)";
    static constexpr const char* STAT_WALLTIME_CVT = "Wall time, Conversion (sec)";
    static constexpr const char* STAT_WALLTIME_ELAB = "Wall time, Elaboration (sec)";

    static void addStat(const V3Statistic&);
    static void addStat(const string& stage, const string& name, double value,
                        unsigned precision = 0) {
        addStat(V3Statistic{stage, name, value, precision, false, false});
    }
    static void addStat(const string& name, double value, unsigned precision = 0) {
        addStat(V3Statistic{"*", name, value, precision, false, false});
    }
    // Add summary statistic - Threadsafe _unlike most other functions here_
    static void addStatSum(const char* name, double count) VL_MT_SAFE_EXCLUDES(s_mutex) {
        // Avoid memory blow-up when called frequently with zero adds,
        // e.g. from V3Const invoked on individual expressions.
        if (count == 0.0) return;
        const V3LockGuard lock{s_mutex};
        addStat(V3Statistic{"*", name, count, 0, true, false});
    }
    static void addStatSum(const std::string& name, double count) {
        addStatSum(name.c_str(), count);
    }
    static void addStatPerf(const string& name, double value) {
        addStat(V3Statistic{"*", name, value, 6, true, true});
    }
    /// Return value of statistic, or zero if not found
    static double getStatSum(const string& name);
    static uint64_t getStatSumQ(const string& name) {
        return static_cast<uint64_t>(getStatSum(name));
    }
    /// Called each stage
    static void statsStage(const string& name);
    /// Called by the top level to collect statistics
    static void statsStageAll(AstNetlist* nodep, const string& stage, bool fastOnly = false);
    static void statsFinalAll(AstNetlist* nodep);
    /// Called by the top level to dump the statistics
    static void statsReport();
    /// Called by debug dumps
    static void infoHeader(std::ofstream& os, const string& prefix);
    /// Called for final build report
    static void summaryReport();
};

#endif  // Guard
