// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// -*- Mode: C++ -*-
//
// Copyright (C) 2017-2025 Red Hat, Inc.
//
// Author: Dodji Seketeli

/// @file
///
/// The private data and functions of the @ref abigail::ir::comparison types.
///
/// Interfaces declared/defined in this file are to be used by parts
/// of libabigail but *NOT* by clients of libabigail.
///

#ifndef __ABG_COMPARISON_PRIV_H__
#define __ABG_COMPARISON_PRIV_H__

#include "abg-internal.h"
// <headers defining libabigail's API go under here>
#include <memory>
#include <unordered_set>
ABG_BEGIN_EXPORT_DECLARATIONS

#include "abg-hash.h"
#include "abg-suppression.h"
#include "abg-comparison.h"
#include "abg-comp-filter.h"
#include "abg-sptr-utils.h"
#include "abg-tools-utils.h"

ABG_END_EXPORT_DECLARATIONS
// </headers defining libabigail's API>

namespace abigail
{

namespace comparison
{

using std::unordered_set;
using namespace abigail::suppr;

// Inject types from outside in here.
using std::vector;
using std::dynamic_pointer_cast;
using std::static_pointer_cast;
using abigail::sptr_utils::noop_deleter;

/// Convenience typedef for a pair of decls or types.
typedef std::pair<const type_or_decl_base_sptr,
		  const type_or_decl_base_sptr> types_or_decls_type;

/// A hashing functor for @ref types_or_decls_type.
struct types_or_decls_hash
{
  size_t
  operator()(const types_or_decls_type& d) const
  {
    size_t h1 = hash_type_or_decl(d.first);
    size_t h2 = hash_type_or_decl(d.second);
    return *hashing::combine_hashes(hash_t(h1), hash_t(h2));
  }
};

/// An equality functor for @ref types_or_decls_type.
struct types_or_decls_equal
{
  bool
  operator()(const types_or_decls_type &d1, const types_or_decls_type &d2) const
  {return d1.first == d2.first && d1.second == d2.second;}
};

/// A convenience typedef for a map of @ref types_or_decls_type and
/// diff_sptr.
typedef unordered_map<types_or_decls_type, diff_sptr,
		      types_or_decls_hash, types_or_decls_equal>
  types_or_decls_diff_map_type;

/// A hashing functor for using @ref diff_sptr and @ref diff* in a
/// hash map or set.
struct diff_hash
{
  /// The function-call operator to hash a @ref diff node.
  ///
  /// @param d the @ref diff node to hash.
  ///
  /// @return the hash value of @p d.
  size_t
  operator()(const diff_sptr& d) const
  {return operator()(*d);}

  /// The function-call operator to hash a @ref diff node.
  ///
  /// @param d the @ref diff node to hash.
  ///
  /// @return the hash value of @p d.
  size_t
  operator()(const diff *d) const
  {return operator()(*d);}

  /// The function-call operator to hash a @ref diff node.
  ///
  /// @param d the @ref diff node to hash.
  ///
  /// @return the hash value of @p d.
  size_t
  operator()(const diff& d) const
  {
    diff* canonical_diff = d.get_canonical_diff();
    ABG_ASSERT(canonical_diff);
    return reinterpret_cast<size_t>(canonical_diff);
  }
}; // end struct diff_hash

/// A comparison functor for using @ref diff_sptr and @ref diff* in a
/// hash map or set.
struct diff_equal
{
  /// The function-call operator to compare two @ref diff nodes.
  ///
  /// @param d1 the first diff node involved in the comparison.
  ///
  /// @param d2 the second diff node involved in the comparison.
  ///
  /// @return true iff @p d1 equals @p d2.
  bool
  operator()(const diff* d1, const diff* d2) const
  {return operator()(*d1, *d2);}

  /// The function-call operator to compare two @ref diff nodes.
  ///
  /// @param d1 the first diff node involved in the comparison.
  ///
  /// @param d2 the second diff node involved in the comparison.
  ///
  /// @return true iff @p d1 equals @p d2.
  bool
  operator()(const diff_sptr& d1, const diff_sptr& d2) const
  {return operator()(*d1, *d2);}

  /// The function-call operator to compare two @ref diff nodes.
  ///
  /// @param d1 the first diff node involved in the comparison.
  ///
  /// @param d2 the second diff node involved in the comparison.
  ///
  /// @return true iff @p d1 equals @p d2.
  bool
  operator()(const diff& d1, const diff& d2) const
  {
    diff* canonical_diff1 = d1.get_canonical_diff();
    ABG_ASSERT(canonical_diff1);

    diff *canonical_diff2 = d2.get_canonical_diff();
    ABG_ASSERT(canonical_diff2);

    return canonical_diff1 == canonical_diff2;
  }
}; // end struct diff_equal

/// A convenience typedef for an unordered_map which key is a @ref
/// diff* and which value is a @ref artifact_sptr_set_type.
typedef unordered_map<const diff*, artifact_sptr_set_type,
		      diff_hash, diff_equal>
diff_artifact_set_map_type;

/// The private member (pimpl) for @ref diff_context.
struct diff_context::priv
{
  diff_category			allowed_category_;
  reporter_base_sptr			reporter_;
  types_or_decls_diff_map_type		types_or_decls_diff_map;
  unordered_diff_sptr_set		live_diffs_;
  vector<diff_sptr>			canonical_diffs;
  vector<filtering::filter_base_sptr>	filters_;
  // All the suppressions specifications are stored in this data
  // member.
  suppressions_type			suppressions_;
  // The negated suppressions specifications that are in
  // suppressions_ are stored here.  Each time suppressions_ is
  // modified, this data member should be cleared.
  suppressions_type			negated_suppressions_;
  // The non-negated suppressions specifications that are in
  // suppressions_ are stored here.  Each time suppressions_ is
  // modified, this data member should be cleared.
  suppressions_type			direct_suppressions_;
  pointer_map				visited_diff_nodes_;
  corpus_diff_sptr			corpus_diff_;
  ostream*				default_output_stream_;
  ostream*				error_output_stream_;
  bool					perform_change_categorization_;
  bool					leaf_changes_only_;
  bool					forbid_visiting_a_node_twice_;
  bool					reset_visited_diffs_for_each_interface_;
  bool					hex_values_;
  bool					show_offsets_sizes_in_bits_;
  bool					show_relative_offset_changes_;
  bool					show_stats_only_;
  bool					show_soname_change_;
  bool					show_architecture_change_;
  bool					show_deleted_fns_;
  bool					show_changed_fns_;
  bool					show_added_fns_;
  bool					show_deleted_vars_;
  bool					show_changed_vars_;
  bool					show_added_vars_;
  bool					show_linkage_names_;
  bool					show_locs_;
  bool					show_redundant_changes_;
  bool					show_syms_unreferenced_by_di_;
  bool					show_added_syms_unreferenced_by_di_;
  bool					show_unreachable_types_;
  bool					show_impacted_interfaces_;
  bool					dump_diff_tree_;
  bool					do_log_;

  priv()
    : allowed_category_(EVERYTHING_CATEGORY),
      reporter_(),
      default_output_stream_(),
      error_output_stream_(),
      perform_change_categorization_(true),
      leaf_changes_only_(),
      forbid_visiting_a_node_twice_(true),
      reset_visited_diffs_for_each_interface_(),
      hex_values_(),
      show_offsets_sizes_in_bits_(true),
      show_relative_offset_changes_(true),
      show_stats_only_(false),
      show_soname_change_(true),
      show_architecture_change_(true),
      show_deleted_fns_(true),
      show_changed_fns_(true),
      show_added_fns_(true),
      show_deleted_vars_(true),
      show_changed_vars_(true),
      show_added_vars_(true),
      show_linkage_names_(false),
      show_locs_(true),
      show_redundant_changes_(true),
      show_syms_unreferenced_by_di_(true),
      show_added_syms_unreferenced_by_di_(true),
      show_unreachable_types_(false),
      show_impacted_interfaces_(true),
      dump_diff_tree_(),
      do_log_()
   {}
};// end struct diff_context::priv

struct type_diff_base::priv
{
public:
  friend class type_diff_base;
}; // end class type_diff_base

/// Private data for the @ref diff type.  The details of generic view
/// of the diff node are expressed here.
struct diff::priv
{
  bool				finished_;
  bool				traversing_;
  type_or_decl_base_sptr	first_subject_;
  type_or_decl_base_sptr	second_subject_;
  vector<diff*>		children_;
  diff*			parent_;
  diff*			parent_interface_;
  diff*			canonical_diff_;
  diff_context_wptr		ctxt_;
  diff_category		local_category_;
  diff_category		category_;
  mutable bool			reported_once_;
  mutable bool			currently_reporting_;
  mutable string		pretty_representation_;

  priv();

public:

  priv(type_or_decl_base_sptr first_subject,
       type_or_decl_base_sptr second_subject,
       diff_context_sptr ctxt,
       diff_category category,
       bool reported_once,
       bool currently_reporting)
    : finished_(),
      traversing_(),
      first_subject_(first_subject),
      second_subject_(second_subject),
      parent_(),
      parent_interface_(),
      canonical_diff_(),
      ctxt_(ctxt),
      local_category_(category),
      category_(category),
      reported_once_(reported_once),
      currently_reporting_(currently_reporting)
  {}

  /// Getter of the diff context associated with this diff.
  ///
  /// @returnt a smart pointer to the diff context.
  diff_context_sptr
  get_context() const
  {return ctxt_.lock();}

  /// Check if a given categorization of a diff node should make it be
  /// filtered out.
  ///
  /// @param category the categorization to take into account.
  bool
  is_filtered_out(diff_category category)
  {
    diff_context_sptr ctxt = get_context();
    if (!ctxt)
      return false;

    if (ctxt->get_allowed_category() == EVERYTHING_CATEGORY)
      return false;

    // If this node is on the path of a node that *must* be reported,
    // then do not filter it.
    if (category & (HAS_DESCENDANT_WITH_ALLOWED_CHANGE_CATEGORY
		    | HAS_PARENT_WITH_ALLOWED_CHANGE_CATEGORY
		    | HAS_ALLOWED_CHANGE_CATEGORY))
      return false;

  /// We don't want to display nodes suppressed by a user-provided
  /// suppression specification or by a "private type" suppression
  /// specification.
    if (category & (SUPPRESSED_CATEGORY | PRIVATE_TYPE_CATEGORY))
    return true;

  // We don't want to display redundant diff nodes, when the user
  // asked to avoid seeing redundant diff nodes.
  if (!ctxt->show_redundant_changes()
      && (category & REDUNDANT_CATEGORY))
    return true;

  if (category == NO_CHANGE_CATEGORY)
    return false;

  // Ignore the REDUNDANT_CATEGORY bit when comparing allowed
  // categories and the current set of categories.
  return !((category & ~REDUNDANT_CATEGORY)
	   & (ctxt->get_allowed_category()
	      & ~REDUNDANT_CATEGORY));
  }
};// end class diff::priv

/// A functor to compare two instances of @ref diff_sptr.
struct diff_less_than_functor
{
  /// An operator that takes two instances of @ref diff_sptr returns
  /// true if its first operand compares less than its second operand.
  ///
  /// @param l the first operand to consider.
  ///
  /// @param r the second operand to consider.
  ///
  /// @return true if @p l compares less than @p r.
  bool
  operator()(const diff* l, const diff* r) const
  {
    if (!l || !r || !l->first_subject() || !r->first_subject())
      return false;

    string l_qn = get_name(l->first_subject());
    string r_qn = get_name(r->first_subject());

    return l_qn < r_qn;
  }

  /// An operator that takes two instances of @ref diff_sptr returns
  /// true if its first operand compares less than its second operand.
  ///
  /// @param l the first operand to consider.
  ///
  /// @param r the second operand to consider.
  ///
  /// @return true if @p l compares less than @p r.
  bool
  operator()(const diff_sptr& l, const diff_sptr& r) const
  {return operator()(l.get(), r.get());}
}; // end struct diff_less_than_functor

struct decl_diff_base::priv
{
public:
  friend class decl_diff_base;
};//end class priv

/// The private data structure for @ref distinct_diff.
struct distinct_diff::priv
{
  diff_sptr compatible_child_diff;
};// end struct distinct_diff

/// The internal type for the impl idiom implementation of @ref
/// var_diff.
struct var_diff::priv
{
  diff_wptr type_diff_;
};//end struct var_diff

/// The internal type for the impl idiom implementation of @ref
/// pointer_diff.
struct pointer_diff::priv
{
  diff_sptr underlying_type_diff_;

  priv(diff_sptr ud)
    : underlying_type_diff_(ud)
  {}
};//end struct pointer_diff::priv

/// The internal type for the impl idiom implementation of @ref
/// subrange_diff.
struct subrange_diff::priv
{
  diff_sptr underlying_type_diff_;

  priv(diff_sptr u)
    : underlying_type_diff_(u)
  {}
}; // end struct subrange_diff::priv

struct array_diff::priv
{
  /// The diff between the two array element types.
  diff_sptr element_type_diff_;
  vector<subrange_diff_sptr> subrange_diffs_;

  priv(diff_sptr element_type_diff,
       vector<subrange_diff_sptr>& subrange_diffs)
    : element_type_diff_(element_type_diff),
      subrange_diffs_(subrange_diffs)
  {}
};//end struct array_diff::priv

struct reference_diff::priv
{
  diff_sptr underlying_type_diff_;
  priv(diff_sptr underlying)
    : underlying_type_diff_(underlying)
  {}
};//end struct reference_diff::priv

/// The private data of the @ref ptr_to_mbr_diff type.
struct ptr_to_mbr_diff::priv
{
  diff_sptr member_type_diff_;
  diff_sptr containing_type_diff_;

  priv(const diff_sptr& member_type_diff,
       const diff_sptr& containing_type_diff)
    : member_type_diff_(member_type_diff),
      containing_type_diff_(containing_type_diff)
  {}
};//end ptr_to_mbr_diff::priv

struct qualified_type_diff::priv
{
  diff_sptr underlying_type_diff;
  mutable diff_sptr leaf_underlying_type_diff;

  priv(diff_sptr underlying)
    : underlying_type_diff(underlying)
  {}
};// end struct qualified_type_diff::priv

struct enum_diff::priv
{
  diff_sptr underlying_type_diff_;
  edit_script enumerators_changes_;
  string_enumerator_map deleted_enumerators_;
  string_enumerator_map inserted_enumerators_;
  string_changed_enumerator_map changed_enumerators_;

  priv(diff_sptr underlying)
    : underlying_type_diff_(underlying)
  {}
};//end struct enum_diff::priv

/// A functor to compare two enumerators based on their value.  This
/// implements the "less than" operator.
struct enumerator_value_comp
{
  bool
  operator()(const enum_type_decl::enumerator& f,
	     const enum_type_decl::enumerator& s) const
  {return f.get_value() < s.get_value();}
};//end struct enumerator_value_comp

/// A functor to compare two changed enumerators, based on their
/// initial value.
struct changed_enumerator_comp
{
  bool
  operator()(const changed_enumerator& f,
	     const changed_enumerator& s) const
  {return f.first.get_value() < s.first.get_value();}
};// end struct changed_enumerator_comp.

/// The type of private data of @ref class_or_union_diff.
struct class_or_union_diff::priv
{
  edit_script member_types_changes_;
  edit_script data_members_changes_;
  edit_script member_fns_changes_;
  edit_script member_fn_tmpls_changes_;
  edit_script member_class_tmpls_changes_;

  string_decl_base_sptr_map deleted_member_types_;
  string_decl_base_sptr_map inserted_member_types_;
  string_diff_sptr_map changed_member_types_;
  diff_sptrs_type sorted_changed_member_types_;
  string_decl_base_sptr_map deleted_data_members_;
  unsigned_decl_base_sptr_map deleted_dm_by_offset_;
  string_decl_base_sptr_map inserted_data_members_;
  unsigned_decl_base_sptr_map inserted_dm_by_offset_;
  // This map contains the data member which sub-type changed.
  string_var_diff_sptr_map subtype_changed_dm_;
  var_diff_sptrs_type sorted_subtype_changed_dm_;
  // This one contains the list of data members changes that can be
  // represented as a data member foo that got removed from offset N,
  // and a data member bar that got inserted at offset N; IOW, this
  // can be translated as data member foo that got changed into data
  // member bar at offset N.
  unsigned_var_diff_sptr_map changed_dm_;
  var_diff_sptrs_type sorted_changed_dm_;

  // This is a data structure to represent data members that have been
  // replaced by anonymous data members.  It's a map that associates
  // the name of the data member to the anonymous data member that
  // replaced it.
  string_decl_base_sptr_map dms_replaced_by_adms_;
  mutable changed_var_sptrs_type dms_replaced_by_adms_ordered_;
  string_member_function_sptr_map deleted_member_functions_;
  class_or_union::member_functions sorted_deleted_member_functions_;
  string_member_function_sptr_map inserted_member_functions_;
  class_or_union::member_functions sorted_inserted_member_functions_;
  string_function_decl_diff_sptr_map changed_member_functions_;
  function_decl_diff_sptrs_type sorted_changed_member_functions_;
  string_decl_base_sptr_map deleted_member_class_tmpls_;
  string_decl_base_sptr_map inserted_member_class_tmpls_;
  string_diff_sptr_map changed_member_class_tmpls_;
  diff_sptrs_type sorted_changed_member_class_tmpls_;

  type_or_decl_base_sptr
  member_type_has_changed(decl_base_sptr) const;

  decl_base_sptr
  subtype_changed_dm(decl_base_sptr) const;

  decl_base_sptr
  member_class_tmpl_has_changed(decl_base_sptr) const;

  size_t
  get_deleted_non_static_data_members_number() const;

  size_t
  get_inserted_non_static_data_members_number() const;

  size_t
  count_filtered_subtype_changed_dm(bool local_only = false);

  size_t
  count_filtered_changed_dm(bool local_only = false);

  size_t
  count_filtered_changed_mem_fns(const diff_context_sptr&);

  size_t
  count_filtered_inserted_mem_fns(const diff_context_sptr&);

  size_t
  count_filtered_deleted_mem_fns(const diff_context_sptr&);

  priv()
  {}
}; // end struct class_or_union_diff::priv

/// A comparison functor to compare two data members based on their
/// offset.
struct data_member_comp
{

  /// Compare two data members.
  ///
  /// First look at their offset and then their name.
  ///
  /// @parm first_dm the first data member to consider.
  ///
  /// @param second_dm the second data member to consider.
  bool
  compare_data_members(const var_decl_sptr& first_dm,
		       const var_decl_sptr& second_dm) const
  {
    ABG_ASSERT(first_dm);
    ABG_ASSERT(second_dm);

    size_t first_offset = get_data_member_offset(first_dm);
    size_t second_offset = get_data_member_offset(second_dm);

    // The data member at the smallest offset comes first.
    if (first_offset != second_offset)
      return first_offset < second_offset;

    string first_dm_name = first_dm->get_name();
    string second_dm_name = second_dm->get_name();

    // But in case the two data members are at the same offset, then
    // sort them lexicographically.
    return first_dm_name < second_dm_name;
  }

  /// Compare two data members.
  ///
  /// First look at their offset and then their name.
  ///
  /// @parm first_dm the first data member to consider.
  ///
  /// @param second_dm the second data member to consider.
  bool
  operator()(const decl_base_sptr& f,
	     const decl_base_sptr& s) const
  {
    var_decl_sptr first_dm = is_data_member(f);
    var_decl_sptr second_dm = is_data_member(s);

    return compare_data_members(first_dm, second_dm);
  }

  /// Compare two data members.
  ///
  /// First look at their offset and then their name.
  ///
  /// @parm first_dm the first data member to consider.
  ///
  /// @param second_dm the second data member to consider.
  bool
  operator()(const changed_var_sptr& f,
	     const changed_var_sptr& s) const
  {
    var_decl_sptr first_dm = is_data_member(is_decl(f.first));
    var_decl_sptr second_dm = is_data_member(is_decl(s.first));

    return compare_data_members(first_dm, second_dm);
  }
};//end struct data_member_comp

/// The type of the private data (pimpl sub-object) of the @ref
/// class_diff type.
struct class_diff::priv
{
  edit_script base_changes_;
  string_base_sptr_map deleted_bases_;
  class_decl::base_specs sorted_deleted_bases_;
  string_base_sptr_map inserted_bases_;
  class_decl::base_specs sorted_inserted_bases_;
  string_base_diff_sptr_map changed_bases_;
  base_diff_sptrs_type sorted_changed_bases_;
  vector<class_decl::base_spec_sptr> moved_bases_;

  class_decl::base_spec_sptr
  base_has_changed(class_decl::base_spec_sptr) const;

  size_t
  count_filtered_bases();

  priv()
  {}
};//end struct class_diff::priv

/// A functor to compare instances of @ref class_decl::base_spec.
struct base_spec_comp
{
  bool
  operator()(const class_decl::base_spec&l,
	     const class_decl::base_spec&r)
  {
    string str1 = l.get_pretty_representation();
    string str2 = r.get_pretty_representation();
    return str1 < str2;
  }
  bool
  operator()(const class_decl::base_spec_sptr&l,
	     const class_decl::base_spec_sptr&r)
  {return operator()(*l, *r);}
}; // end base_spec_comp

/// A comparison function for instances of @ref base_diff.
struct base_diff_comp
{
  bool
  operator()(const base_diff& l, const base_diff& r) const
  {
    class_decl::base_spec_sptr f = l.first_base(), s = r.first_base();
    if (f->get_offset_in_bits() >= 0
	&& s->get_offset_in_bits() >= 0)
      return f->get_offset_in_bits() < s->get_offset_in_bits();
    else
      return (f->get_base_class()->get_pretty_representation()
	      < s->get_base_class()->get_pretty_representation());
  }

  bool
  operator()(const base_diff* l, const base_diff* r) const
  {return operator()(*l, *r);}

  bool
  operator()(const base_diff_sptr l, const base_diff_sptr r) const
  {return operator()(l.get(), r.get());}
}; // end struct base_diff_comp

bool
is_less_than(const decl_diff_base& first, const decl_diff_base& second);

bool
is_less_than(const decl_diff_base_sptr& first,
	     const decl_diff_base_sptr& second);

/// A comparison functor to compare two instances of @ref var_diff
/// that represent changed data members based on the offset of the
/// initial data members, or if equal, based on their qualified name.
/// If equal again, then the offset and qualified name of the new data
/// members are considered.
struct data_member_diff_comp
{
  /// @param f the first change to data member to take into account
  ///
  /// @param s the second change to data member to take into account.
  ///
  /// @return true iff f is before s.
  bool
  operator()(const var_diff_sptr f,
	     const var_diff_sptr s) const
  {
    var_decl_sptr first_dm = f->first_var();
    var_decl_sptr second_dm = s->first_var();

    ABG_ASSERT(is_data_member(first_dm));
    ABG_ASSERT(is_data_member(second_dm));

    size_t off1 = get_data_member_offset(first_dm);
    size_t off2 = get_data_member_offset(second_dm);

    if (off1 != off2)
      return off1 < off2;

    // The two offsets of the initial data members are the same.  So
    // lets compare the qualified name of these initial data members.

    string name1 = first_dm->get_qualified_name();
    string name2 = second_dm->get_qualified_name();

    if (name1 != name2)
      return name1 < name2;

    // The offsets and the qualified names of the initial data members
    // are the same.  Let's now compare the offsets of the *new* data
    // members.

    first_dm = f->second_var();
    second_dm = s->second_var();

    ABG_ASSERT(is_data_member(first_dm));
    ABG_ASSERT(is_data_member(second_dm));

    off1 = get_data_member_offset(first_dm);
    off2 = get_data_member_offset(second_dm);

    if (off1 != off2)
      return off1 < off2;

    // The offsets of the new data members are the same, dang!  Let's
    // compare the qualified names of these new data members then.

    name1 = first_dm->get_qualified_name();
    name2 = second_dm->get_qualified_name();

    return name1 < name2;
  }
}; // end struct data_member_diff_comp

/// A comparison functor for instances of @ref function_decl_diff that
/// represent changes between two virtual member functions.
struct virtual_member_function_diff_comp
{
  bool
  operator()(const function_decl_diff& l,
	     const function_decl_diff& r) const
  {
    ABG_ASSERT(get_member_function_is_virtual(l.first_function_decl()));
    ABG_ASSERT(get_member_function_is_virtual(r.first_function_decl()));

    size_t l_offset = get_member_function_vtable_offset(l.first_function_decl());
    size_t r_offset = get_member_function_vtable_offset(r.first_function_decl());
    if (l_offset != r_offset)
      return l_offset < r_offset;

    return is_less_than(l, r);
  }

  bool
  operator()(const function_decl_diff* l,
	     const function_decl_diff* r)
  {return operator()(*l, *r);}

  bool
  operator()(const function_decl_diff_sptr l,
	     const function_decl_diff_sptr r)
  {return operator()(l.get(), r.get());}
}; // end struct virtual_member_function_diff_comp

struct base_diff::priv
{
  class_diff_sptr underlying_class_diff_;

  priv(class_diff_sptr underlying)
    : underlying_class_diff_(underlying)
  {}
}; // end struct base_diff::priv

struct scope_diff::priv
{
  // The edit script built by the function compute_diff.
  edit_script member_changes_;

  // Below are the useful lookup tables.
  //
  // If you add a new lookup table, please update member functions
  // clear_lookup_tables, lookup_tables_empty and
  // ensure_lookup_tables_built.

  // The deleted/inserted types/decls.  These basically map what is
  // inside the member_changes_ data member.  Note that for instance,
  // a given type T might be deleted from the first scope and added to
  // the second scope again; this means that the type was *changed*.
  string_decl_base_sptr_map deleted_types_;
  string_decl_base_sptr_map deleted_decls_;
  string_decl_base_sptr_map inserted_types_;
  string_decl_base_sptr_map inserted_decls_;

  // The changed types/decls lookup tables.
  //
  // These lookup tables are populated from the lookup tables above.
  //
  // Note that the value stored in each of these tables is a pair
  // containing the old decl/type and the new one.  That way it is
  // easy to run a diff between the old decl/type and the new one.
  //
  // A changed type/decl is one that has been deleted from the first
  // scope and that has been inserted into the second scope.
  string_diff_sptr_map changed_types_;
  diff_sptrs_type sorted_changed_types_;
  string_diff_sptr_map changed_decls_;
  diff_sptrs_type sorted_changed_decls_;

  // The removed types/decls lookup tables.
  //
  // A removed type/decl is one that has been deleted from the first
  // scope and that has *NOT* been inserted into it again.
  string_decl_base_sptr_map removed_types_;
  string_decl_base_sptr_map removed_decls_;

  // The added types/decls lookup tables.
  //
  // An added type/decl is one that has been inserted to the first
  // scope but that has not been deleted from it.
  string_decl_base_sptr_map added_types_;
  string_decl_base_sptr_map added_decls_;
};//end struct scope_diff::priv

/// A comparison functor for instances of @ref diff.
struct diff_comp
{
  /// Lexicographically compare two diff nodes.
  ///
  /// Compare the pretty representation of the first subjects of two
  /// diff nodes.
  ///
  /// @return true iff @p l is less than @p r.
  bool
  operator()(const diff& l, diff& r) const
  {
    return (get_pretty_representation(l.first_subject())
	    <
	    get_pretty_representation(r.first_subject()));
  }

  /// Lexicographically compare two diff nodes.
  ///
  /// Compare the pretty representation of the first subjects of two
  /// diff nodes.
  ///
  /// @return true iff @p l is less than @p r.
  bool
  operator()(const diff* l, diff* r) const
  {return operator()(*l, *r);}

  /// Lexicographically compare two diff nodes.
  ///
  /// Compare the pretty representation of the first subjects of two
  /// diff nodes.
  ///
  /// @return true iff @p l is less than @p r.
  bool
  operator()(const diff_sptr l, diff_sptr r) const
  {return operator()(l.get(), r.get());}
}; // end struct diff_comp;

struct fn_parm_diff::priv
{
  mutable diff_sptr type_diff;
}; // end struct fn_parm_diff::priv

struct function_type_diff::priv
{
  diff_sptr	return_type_diff_;
  edit_script	parm_changes_;

  // useful lookup tables.
  string_parm_map			deleted_parms_;
  vector<function_decl::parameter_sptr> sorted_deleted_parms_;
  string_parm_map			added_parms_;
  vector<function_decl::parameter_sptr> sorted_added_parms_;
  // This map contains parameters sub-type changes that don't change
  // the name of the type of the parameter.
  string_fn_parm_diff_sptr_map		subtype_changed_parms_;
  vector<fn_parm_diff_sptr>		sorted_subtype_changed_parms_;
  // This map contains parameter type changes that actually change the
  // name of the type of the parameter, but in a compatible way;
  // otherwise, the mangling of the function would have changed (in
  // c++ at least).
  unsigned_fn_parm_diff_sptr_map	changed_parms_by_id_;
  vector<fn_parm_diff_sptr>		sorted_changed_parms_by_id_;
  unsigned_parm_map			deleted_parms_by_id_;
  unsigned_parm_map			added_parms_by_id_;

  priv()
  {}
}; // end struct function_type_diff::priv

struct function_decl_diff::priv
{
  function_type_diff_sptr type_diff_;

  priv()
  {}
};// end struct function_decl_diff::priv

/// A comparison functor to compare two instances of @ref fn_parm_diff
/// based on their indexes.
struct fn_parm_diff_comp
{
  /// @param f the first diff
  ///
  /// @param s the second diff
  ///
  /// @return true if the index of @p f is less than the index of @p
  /// s.
  bool
  operator()(const fn_parm_diff& f, const fn_parm_diff& s)
  {return f.first_parameter()->get_index() < s.first_parameter()->get_index();}

  bool
  operator()(const fn_parm_diff_sptr& f, const fn_parm_diff_sptr& s)
  {return operator()(*f, *s);}
}; // end struct fn_parm_diff_comp

/// Functor that compares two function parameters for the purpose of
/// sorting them.
struct parm_comp
{
  /// Returns true iff the index of the first parameter is smaller
  /// than the of the second parameter.
  ///
  /// @param l the first parameter to compare.
  ///
  /// @param r the second parameter to compare.
  ///
  /// @return true iff the index of the first parameter is smaller
  /// than the of the second parameter.
  bool
  operator()(const function_decl::parameter& l,
	     const function_decl::parameter& r)
  {return l.get_index() < r.get_index();}

  /// Returns true iff the index of the first parameter is smaller
  /// than the of the second parameter.
  ///
  /// @param l the first parameter to compare.
  ///
  /// @param r the second parameter to compare.
  ///
  /// @return true iff the index of the first parameter is smaller
  /// than the of the second parameter.
  bool
  operator()(const function_decl::parameter_sptr& l,
	     const function_decl::parameter_sptr& r)
  {return operator()(*l, *r);}
}; // end struct parm_comp

/// A functor to compare instances of @ref var_decl base on their
/// qualified names.
struct var_comp
{
  bool
  operator() (const var_decl& l, const var_decl& r) const
  {
    string name1 = l.get_qualified_name(), name2 = r.get_qualified_name();
    return name1 < name2;
  }

  bool
  operator() (const var_decl* l, const var_decl* r) const
  {return operator()(*l, *r);}

  bool
  operator() (const var_decl_sptr& l, const var_decl_sptr& r) const
  {return operator()(l.get(), r.get());}
};// end struct var_comp

/// A functor to compare instances of @ref elf_symbol base on their
/// names.
struct elf_symbol_comp
{
  bool
  operator()(const elf_symbol& l, const elf_symbol& r)
  {
    string name1 = l.get_id_string(), name2 = r.get_id_string();
    return name1 < name2;
  }

  bool
  operator()(const elf_symbol* l, const elf_symbol* r)
  {return operator()(*l, *r);}

  bool
  operator()(const elf_symbol_sptr& l, const elf_symbol_sptr& r)
  {return operator()(l.get(), r.get());}
}; //end struct elf_symbol_comp

struct typedef_diff::priv
{
  diff_sptr underlying_type_diff_;

  priv(const diff_sptr underlying_type_diff)
    : underlying_type_diff_(underlying_type_diff)
  {}
};//end struct typedef_diff::priv

struct translation_unit_diff::priv
{
  translation_unit_sptr first_;
  translation_unit_sptr second_;

  priv(translation_unit_sptr f, translation_unit_sptr s)
    : first_(f), second_(s)
  {}
};//end struct translation_unit_diff::priv

struct corpus_diff::priv
{
  bool					finished_;
  string				pretty_representation_;
  vector<diff*>			children_;
  corpus_sptr				first_;
  corpus_sptr				second_;
  diff_context_wptr			ctxt_;
  corpus_diff::diff_stats_sptr		diff_stats_;
  bool					sonames_equal_;
  bool					architectures_equal_;
  string_function_ptr_map		deleted_fns_;
  string_function_ptr_map		suppressed_deleted_fns_;
  string_function_ptr_map		added_fns_;
  string_function_ptr_map		suppressed_added_fns_;
  string_function_decl_diff_sptr_map	changed_fns_map_;
  function_decl_diff_sptrs_type	changed_fns_;
  function_decl_diff_sptrs_type	incompatible_changed_fns_;
  string_var_ptr_map			deleted_vars_;
  string_var_ptr_map			suppressed_deleted_vars_;
  string_var_ptr_map			added_vars_;
  string_var_ptr_map			suppressed_added_vars_;
  string_var_diff_sptr_map		changed_vars_map_;
  var_diff_sptrs_type			incompatible_changed_vars_;
  var_diff_sptrs_type			sorted_changed_vars_;
  string_elf_symbol_map		added_unrefed_fn_syms_;
  string_elf_symbol_map		suppressed_added_unrefed_fn_syms_;
  string_elf_symbol_map		deleted_unrefed_fn_syms_;
  string_elf_symbol_map		suppressed_deleted_unrefed_fn_syms_;
  string_elf_symbol_map		added_unrefed_var_syms_;
  string_elf_symbol_map		suppressed_added_unrefed_var_syms_;
  string_elf_symbol_map		deleted_unrefed_var_syms_;
  string_elf_symbol_map		suppressed_deleted_unrefed_var_syms_;
  edit_script				unreachable_types_edit_script_;
  string_type_base_sptr_map		deleted_unreachable_types_;
  vector<type_base_sptr>		deleted_unreachable_types_sorted_;
  string_type_base_sptr_map		suppressed_deleted_unreachable_types_;
  string_type_base_sptr_map		added_unreachable_types_;
  vector<type_base_sptr>		added_unreachable_types_sorted_;
  string_type_base_sptr_map		suppressed_added_unreachable_types_;
  string_diff_sptr_map			changed_unreachable_types_;
  mutable vector<diff_sptr>		changed_unreachable_types_sorted_;
  diff_maps				leaf_diffs_;

  /// Default constructor of corpus_diff::priv.
  priv()
    : finished_(false),
      sonames_equal_(false),
      architectures_equal_(false)
  {}

  /// Constructor of corpus_diff::priv.
  ///
  /// @param first the first corpus of this diff.
  ///
  /// @param second the second corpus of this diff.
  ///
  /// @param ctxt the context of the diff.
  priv(corpus_sptr first,
       corpus_sptr second,
       diff_context_sptr ctxt)
    : finished_(false),
      first_(first),
      second_(second),
      ctxt_(ctxt),
      sonames_equal_(false),
      architectures_equal_(false)
  {}

  diff_context_sptr
  get_context();

  bool
  lookup_tables_empty() const;

  void
  clear_lookup_tables();

  void
  compare_fns_vars_and_ensure_lookup_tables_populated();

  void
  apply_supprs_to_added_removed_fns_vars_unreachable_types();

  bool
  deleted_function_is_suppressed(const function_decl* fn) const;

  bool
  added_function_is_suppressed(const function_decl* fn) const;

  bool
  deleted_variable_is_suppressed(const var_decl_sptr& var) const;

  bool
  added_variable_is_suppressed(const var_decl_sptr& var) const;

  bool
  added_unreachable_type_is_suppressed(const type_base *t)const ;

  bool
  deleted_unreachable_type_is_suppressed(const type_base *t)const ;

  bool
  deleted_unrefed_fn_sym_is_suppressed(const elf_symbol*) const;

  bool
  added_unrefed_fn_sym_is_suppressed(const elf_symbol*) const;

  bool
  deleted_unrefed_var_sym_is_suppressed(const elf_symbol*) const;

  bool
  added_unrefed_var_sym_is_suppressed(const elf_symbol*) const;

  void count_leaf_changes(size_t &num_changes, size_t &num_filtered);

  void count_leaf_type_changes(size_t &num_type_changes,
			       size_t &num_type_changes_filtered);

  void count_unreachable_types(size_t &num_added,
			       size_t &num_removed,
			       size_t &num_changed,
			       size_t &num_filtered_added,
			       size_t &num_filtered_removed,
			       size_t &num_filtered_changed);

  const string_diff_sptr_map&
  changed_unreachable_types() const;

  const vector<diff_sptr>&
  changed_unreachable_types_sorted() const;

  void
  apply_filters_and_compute_diff_stats(corpus_diff::diff_stats&);

  void
  emit_diff_stats(const diff_stats&	stats,
		  ostream&		out,
		  const string&	indent);

  void
  categorize_redundant_changed_sub_nodes();

  void
  clear_redundancy_categorization();

  void
  maybe_dump_diff_tree();
}; // end corpus::priv

/// "Less than" functor to compare instances of @ref function_decl.
struct function_comp
{
  /// The actual "less than" operator for instances of @ref
  /// function_decl.  It returns true if the first @ref function_decl
  /// is lest than the second one.
  ///
  /// @param f the first @ref function_decl to take in account.
  ///
  /// @param s the second @ref function_decl to take in account.
  ///
  /// @return true iff @p f is less than @p s.
  bool
  operator()(const function_decl& f, const function_decl& s)
  {return abigail::ir::function_decl_is_less_than(f, s);}

  /// The actual "less than" operator for instances of @ref
  /// function_decl.  It returns true if the first @ref function_decl
  /// is lest than the second one.
  ///
  /// @param f the first @ref function_decl to take in account.
  ///
  /// @param s the second @ref function_decl to take in account.
  ///
  /// @return true iff @p f is less than @p s.
  bool
  operator()(const function_decl* f, const function_decl* s)
  {return operator()(*f, *s);}

  /// The actual "less than" operator for instances of @ref
  /// function_decl.  It returns true if the first @ref function_decl
  /// is lest than the second one.
  ///
  /// @param f the first @ref function_decl to take in account.
  ///
  /// @param s the second @ref function_decl to take in account.
  ///
  /// @return true iff @p f is less than @p s.
  bool
  operator()(const function_decl_sptr f, const function_decl_sptr s)
  {return operator()(f.get(), s.get());}
}; // end function_comp

/// A "Less Than" functor to compare instance of @ref
/// function_decl_diff.
struct function_decl_diff_comp
{
  /// The actual less than operator.
  ///
  /// It returns true if the first @ref function_decl_diff is less
  /// than the second one.
  ///
  /// param first the first @ref function_decl_diff to consider.
  ///
  /// @param second the second @ref function_decl_diff to consider.
  ///
  /// @return true iff @p first is less than @p second.
  bool
  operator()(const function_decl_diff& first,
	     const function_decl_diff& second)
  {
    return is_less_than(first, second);
  }

  /// The actual less than operator.
  ///
  /// It returns true if the first @ref function_decl_diff_sptr is
  /// less than the second one.
  ///
  /// param first the first @ref function_decl_diff_sptr to consider.
  ///
  /// @param second the second @ref function_decl_diff_sptr to
  /// consider.
  ///
  /// @return true iff @p first is less than @p second.
  bool
  operator()(const function_decl_diff_sptr first,
	     const function_decl_diff_sptr second)
  {return operator()(*first, *second);}
}; // end struct function_decl_diff_comp

/// Functor to sort instances of @ref var_diff_sptr
struct var_diff_sptr_comp
{
  /// Return true if the first argument is less than the second one.
  ///
  /// @param f the first argument to consider.
  ///
  /// @param s the second argument to consider.
  ///
  /// @return true if @p f is less than @p s.
  bool
  operator()(const var_diff_sptr f, const var_diff_sptr s)
  {
    return is_less_than(f, s);
  }
}; // end struct var_diff_sptr_comp

/// The type of the private data of corpus_diff::diff_stats.
struct corpus_diff::diff_stats::priv
{
  friend class corpus_diff::diff_stats;

  diff_context_wptr	ctxt_;
  size_t		num_func_removed;
  size_t		num_removed_func_filtered_out;
  size_t		num_func_added;
  size_t		num_added_func_filtered_out;
  size_t		num_func_changed;
  size_t		num_changed_func_filtered_out;
  size_t		num_func_with_virt_offset_changes;
  size_t		num_func_with_local_harmful_changes;
  size_t		num_func_with_incompatible_changes;
  size_t		num_var_with_local_harmful_changes;
  size_t		num_var_with_incompatible_changes;
  size_t		num_vars_removed;
  size_t		num_removed_vars_filtered_out;
  size_t		num_vars_added;
  size_t		num_added_vars_filtered_out;
  size_t		num_vars_changed;
  size_t		num_changed_vars_filtered_out;
  size_t		num_func_syms_removed;
  size_t		num_removed_func_syms_filtered_out;
  size_t		num_func_syms_added;
  size_t		num_added_func_syms_filtered_out;
  size_t		num_var_syms_removed;
  size_t		num_removed_var_syms_filtered_out;
  size_t		num_var_syms_added;
  size_t		num_added_var_syms_filtered_out;
  size_t		num_leaf_changes;
  size_t		num_leaf_changes_filtered_out;
  size_t		num_leaf_type_changes;
  size_t		num_leaf_type_changes_filtered_out;
  size_t		num_leaf_func_changes;
  size_t		num_leaf_func_with_incompatible_changes;
  size_t		num_leaf_func_changes_filtered_out;
  size_t		num_leaf_var_changes;
  size_t		num_leaf_var_with_incompatible_changes;
  size_t		num_leaf_var_changes_filtered_out;
  size_t		num_added_unreachable_types;
  size_t		num_added_unreachable_types_filtered_out;
  size_t		num_removed_unreachable_types;
  size_t		num_removed_unreachable_types_filtered_out;
  size_t		num_changed_unreachable_types;
  size_t		num_changed_unreachable_types_filtered_out;

  priv(diff_context_sptr ctxt)
    : ctxt_(ctxt),
      num_func_removed(),
      num_removed_func_filtered_out(),
      num_func_added(),
      num_added_func_filtered_out(),
      num_func_changed(),
      num_changed_func_filtered_out(),
      num_func_with_virt_offset_changes(),
      num_func_with_local_harmful_changes(),
      num_func_with_incompatible_changes(),
      num_var_with_local_harmful_changes(),
      num_var_with_incompatible_changes(),
      num_vars_removed(),
      num_removed_vars_filtered_out(),
      num_vars_added(),
      num_added_vars_filtered_out(),
      num_vars_changed(),
      num_changed_vars_filtered_out(),
      num_func_syms_removed(),
      num_removed_func_syms_filtered_out(),
      num_func_syms_added(),
      num_added_func_syms_filtered_out(),
      num_var_syms_removed(),
      num_removed_var_syms_filtered_out(),
      num_var_syms_added(),
      num_added_var_syms_filtered_out(),
      num_leaf_changes(),
      num_leaf_changes_filtered_out(),
      num_leaf_type_changes(),
      num_leaf_type_changes_filtered_out(),
      num_leaf_func_changes(),
      num_leaf_func_with_incompatible_changes(),
      num_leaf_func_changes_filtered_out(),
      num_leaf_var_changes(),
      num_leaf_var_with_incompatible_changes(),
      num_leaf_var_changes_filtered_out(),
      num_added_unreachable_types(),
      num_added_unreachable_types_filtered_out(),
      num_removed_unreachable_types(),
      num_removed_unreachable_types_filtered_out(),
      num_changed_unreachable_types(),
      num_changed_unreachable_types_filtered_out()
  {}

  diff_context_sptr
  ctxt()
  {return ctxt_.lock();}
}; // end class corpus_diff::diff_stats::priv

void
sort_enumerators(const string_enumerator_map& enumerators_map,
		 enum_type_decl::enumerators& sorted);

void
sort_changed_enumerators(const string_changed_enumerator_map& enumerators_map,
			 changed_enumerators_type& sorted);

void
sort_data_members(const string_decl_base_sptr_map &data_members,
		  vector<decl_base_sptr>& sorted);

void
sort_changed_data_members(changed_var_sptrs_type& input);

void
sort_string_function_ptr_map(const string_function_ptr_map& map,
			     vector<const function_decl*>& sorted);

void
sort_string_member_function_sptr_map(const string_member_function_sptr_map& map,
				     class_or_union::member_functions& sorted);

void
sort_string_type_base_sptr_map(string_type_base_sptr_map& map,
			       vector<type_base_sptr>& sorted);

void
sort_string_function_decl_diff_sptr_map
(const string_function_decl_diff_sptr_map& map,
 function_decl_diff_sptrs_type& sorted);

void
sort_function_decl_diffs(function_decl_diff_sptrs_type& fn_diffs);

void
sort_string_var_diff_sptr_map(const string_var_diff_sptr_map& map,
			      var_diff_sptrs_type& sorted);

void
sort_var_diffs(var_diff_sptrs_type& var_diffs);

void
sort_string_elf_symbol_map(const string_elf_symbol_map& map,
			   vector<elf_symbol_sptr>& sorted);

void
sort_string_var_ptr_map(const string_var_ptr_map& map,
			vector<var_decl_sptr>& sorted);

void
sort_string_data_member_diff_sptr_map(const string_var_diff_sptr_map& map,
				      var_diff_sptrs_type& sorted);

void
sort_unsigned_data_member_diff_sptr_map(const unsigned_var_diff_sptr_map map,
					var_diff_sptrs_type& sorted);

void
sort_string_virtual_member_function_diff_sptr_map
(const string_function_decl_diff_sptr_map& map,
 function_decl_diff_sptrs_type& sorted);

void
sort_string_diff_sptr_map(const string_diff_sptr_map& map,
			  diff_sptrs_type& sorted);

void
sort_string_diff_ptr_map(const string_diff_ptr_map& map,
			 diff_ptrs_type& sorted);

void
sort_string_base_diff_sptr_map(const string_base_diff_sptr_map& map,
			       base_diff_sptrs_type& sorted);

void
sort_string_base_sptr_map(const string_base_sptr_map& m,
			  class_decl::base_specs& sorted);

void
sort_string_fn_parm_diff_sptr_map(const unsigned_fn_parm_diff_sptr_map& map,
				  vector<fn_parm_diff_sptr>&		sorted);
void
sort_string_fn_parm_diff_sptr_map(const string_fn_parm_diff_sptr_map&	map,
				  vector<fn_parm_diff_sptr>&		sorted);
void
sort_string_parm_map(const string_parm_map& map,
		     vector<function_decl::parameter_sptr>& sorted);

void
sort_artifacts_set(const artifact_sptr_set_type& set,
		   vector<type_or_decl_base_sptr>& sorted);

type_base_sptr
get_leaf_type(qualified_type_def_sptr t);

diff*
get_fn_decl_or_var_decl_diff_ancestor(const diff *);

bool
is_diff_of_global_decls(const diff*);

} // end namespace comparison

} // namespace abigail

#endif // __ABG_COMPARISON_PRIV_H__
