// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Hash AST trees to find duplicates
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2005-2022 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
//  Datastructure for finding duplicate AstNode trees via hashing
//
//*************************************************************************

#ifndef VERILATOR_V3DUPFINDER_H_
#define VERILATOR_V3DUPFINDER_H_
#include "config_build.h"
#include "verilatedos.h"

#include "V3Ast.h"
#include "V3Error.h"
#include "V3Hasher.h"

#include <map>
#include <memory>

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

struct V3DupFinderUserSame {
    // Functor for V3DupFinder::findDuplicate
    virtual bool isSame(AstNode*, AstNode*) = 0;
    V3DupFinderUserSame() = default;
    virtual ~V3DupFinderUserSame() = default;
};

// This really is just a multimap from 'node hash' to 'node pointer', with some minor extensions.
class V3DupFinder final : private std::multimap<V3Hash, AstNode*> {
    using Super = std::multimap<V3Hash, AstNode*>;

    // MEMBERS
    const V3Hasher* const m_hasherp = nullptr;  // Pointer to owned hasher
    const V3Hasher& m_hasher;  // Reference to hasher

public:
    // CONSTRUCTORS
    V3DupFinder()
        : m_hasherp{new V3Hasher}
        , m_hasher{*m_hasherp} {}
    explicit V3DupFinder(const V3Hasher& hasher)
        : m_hasher{hasher} {}
    ~V3DupFinder() {
        if (m_hasherp) delete m_hasherp;
    }

    // METHODS

    // Expose minimal set of superclass interface
    using Super::begin;
    using Super::cbegin;
    using Super::cend;
    using Super::clear;
    using Super::const_iterator;
    using Super::empty;
    using Super::end;
    using Super::erase;
    using Super::iterator;
    using Super::size_type;

    // Insert node into data structure
    iterator insert(AstNode* nodep) { return emplace(m_hasher(nodep), nodep); }

    // Erase node from data structure
    size_type erase(AstNode* nodep);

    // Return duplicate, if one was inserted, with optional user check for sameness
    iterator findDuplicate(AstNode* nodep, V3DupFinderUserSame* checkp = nullptr);

    // Dump for debug
    void dumpFile(const string& filename, bool tree);
    void dumpFilePrefixed(const string& nameComment, bool tree = false);
};

#endif  // Guard