/****************************************************************************
 *   This file is part of the aGrUM/pyAgrum library.                        *
 *                                                                          *
 *   Copyright (c) 2005-2026 by                                             *
 *       - Pierre-Henri WUILLEMIN(_at_LIP6)                                 *
 *       - Christophe GONZALES(_at_AMU)                                     *
 *                                                                          *
 *   The aGrUM/pyAgrum library is free software; you can redistribute it    *
 *   and/or modify it under the terms of either :                           *
 *                                                                          *
 *    - the GNU Lesser General Public License as published by               *
 *      the Free Software Foundation, either version 3 of the License,      *
 *      or (at your option) any later version,                              *
 *    - the MIT license (MIT),                                              *
 *    - or both in dual license, as here.                                   *
 *                                                                          *
 *   (see https://agrum.gitlab.io/articles/dual-licenses-lgplv3mit.html)    *
 *                                                                          *
 *   This aGrUM/pyAgrum library is distributed in the hope that it will be  *
 *   useful, but WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,          *
 *   INCLUDING BUT NOT LIMITED TO THE WARRANTIES MERCHANTABILITY or FITNESS *
 *   FOR A PARTICULAR PURPOSE  AND NONINFRINGEMENT. IN NO EVENT SHALL THE   *
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,        *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  *
 *   OTHER DEALINGS IN THE SOFTWARE.                                        *
 *                                                                          *
 *   See LICENCES for more details.                                         *
 *                                                                          *
 *   SPDX-FileCopyrightText: Copyright 2005-2026                            *
 *       - Pierre-Henri WUILLEMIN(_at_LIP6)                                 *
 *       - Christophe GONZALES(_at_AMU)                                     *
 *   SPDX-License-Identifier: LGPL-3.0-or-later OR MIT                      *
 *                                                                          *
 *   Contact  : info_at_agrum_dot_org                                       *
 *   homepage : http://agrum.gitlab.io                                      *
 *   gitlab   : https://gitlab.com/agrumery/agrum                           *
 *                                                                          *
 ****************************************************************************/


/**
 * @file
 * @brief Abstract base class for all multi dimensionnal Causal Independency
 * models
 *
 * Independance of Causal Influence (ICI) is a method of defining a discrete
 * distribution that can dramatically reduce the number of prior probabilities
 * necessary to define a distribution.  (see "The Noisy-Average Model for Local
 * Probability Distributions", Zagorecki, 2003) (see also "Canonical
 * Probabilistic Models for Knowledge Engineering", Diez, Druzdzel, 2007)
 *
 * @author Pierre-Henri WUILLEMIN(_at_LIP6) & Christophe GONZALES(_at_AMU)
 */
#ifndef GUM_MULTI_DIM_ICI_MODEL_H
#define GUM_MULTI_DIM_ICI_MODEL_H

#include <agrum/base/core/bijection.h>
#include <agrum/base/multidim/implementations/multiDimReadOnly.h>

namespace gum {

  // ===========================================================================
  // ===                       GUM_MULTI_DIM_AGGREGATOR                      ===
  // ===========================================================================
  // clang-format off
  /**
   * @class MultiDimICIModel
   * @headerfile multiDimICIModel.h <agrum/base/multidim/ICIModels/multiDimICIModel.h>
   * @ingroup multidim_group
   *
   * @brief abstract class for Conditional Indepency Models
   *
   * @warning The first variable is assumed to be the children. The latter are
   * the causes.
   */
  // clang-format on

  template < typename GUM_ELEMENT >
  class MultiDimICIModel: public MultiDimReadOnly< GUM_ELEMENT > {
    public:
    // ============================================================================
    /// @name Constructors / Destructors
    // ============================================================================
    /// @{

    /**
     * Default constructor.
     */
    MultiDimICIModel(GUM_ELEMENT external_weight, GUM_ELEMENT default_weight = (GUM_ELEMENT)1.0);

    /**
     * Default constructor.
     */
    MultiDimICIModel(const MultiDimICIModel< GUM_ELEMENT >& from);

    /**
     * @brief Copy constructor using a bijection to swap variables from source.
     *
     * @param bij First variables are new variables, seconds are in from.
     * @param from the copied instance
     */
    MultiDimICIModel(const Bijection< const DiscreteVariable*, const DiscreteVariable* >& bij,
                     const MultiDimICIModel< GUM_ELEMENT >&                               from);

    /**
     * Destructor.
     */
    ~MultiDimICIModel() override;

    /// @}
    // ============================================================================
    /// @name Accessors / Modifiers
    // ============================================================================
    /// @{

    public:
    std::string toString() const override;

    void changeNotification(const gum::Instantiation&,
                            const gum::DiscreteVariable* const,
                            gum::Idx,
                            gum::Idx) override;

    void setFirstNotification(const gum::Instantiation&) override;

    void setLastNotification(const gum::Instantiation&) override;

    void setIncNotification(const gum::Instantiation&) override;

    void setDecNotification(const gum::Instantiation&) override;

    void setChangeNotification(const gum::Instantiation&) override;

    std::string toString(const gum::Instantiation* i) const override;

    /**
     * @brief Copy of a multiDimICIModel.
     *
     * This method is virtual because it should be optimized in certain
     * MultiDimContainer.
     *
     * @throw OperationNotAllowed Raised if src does not have the same domain
     * size than this MultiDimContainer.
     **/
    void copyFrom(const MultiDimContainer< GUM_ELEMENT >& src) const override;

    /**
     * @return the real number of parameters used for this table. This function
     * is used for compute @see compressionRatio()
     */
    Size realSize() const override;

    GUM_ELEMENT causalWeight(const DiscreteVariable& v) const;

    void causalWeight(const DiscreteVariable& v, GUM_ELEMENT w) const;

    GUM_ELEMENT externalWeight() const;

    void externalWeight(GUM_ELEMENT w) const;

    /**
     * @brief returns the real name of the multiDimArray
     *
     * In aGrUM, all the types of multi-dimensional arrays/functionals have a
     * name that describes what they are in reality. For instance, a table
     * stored in extension is a "MultiDimArray", one that stores only non zero
     * elements is a "MultiDimSparseArray", and so on. These names are unique
     * for each type of implementation and is used by the system to determine
     * which is the best functions to use, say, when we wish to use operators
     * such as operator+ on two MultiDimImplementations.
     */
    const std::string& name() const override;

    /// @}

    protected:
    /// \f$ p_0 \f$ in Henrion (89).
    mutable GUM_ELEMENT _external_weight_;

    // ============================================================================
    /// @name causal weights
    // ============================================================================
    /// @{

    /// \f$ P(e | c_i) \f$ in Henrion (89) in a hashtable with a default_value.
    mutable GUM_ELEMENT _default_weight_;

    mutable HashTable< const DiscreteVariable*, GUM_ELEMENT > _causal_weights_;
    /// @}

    void replace_(const DiscreteVariable* x, const DiscreteVariable* y) override;
  };


#ifndef GUM_NO_EXTERN_TEMPLATE_CLASS
  extern template class MultiDimICIModel< double >;
#endif

} /* namespace gum */

#include <agrum/base/multidim/ICIModels/multiDimICIModel_tpl.h>

#endif /* GUM_MULTI_DIM_ICI_MODEL_H */
