// ****************************************************************************
//
//          Aevol - An in silico experimental evolution platform
//
// ****************************************************************************
//
// Copyright: See the AUTHORS file provided with the package or <www.aevol.fr>
// Web: http://www.aevol.fr/
// E-mail: See <http://www.aevol.fr/contact/>
// Original Authors : Guillaume Beslon, Carole Knibbe, David Parsons
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ****************************************************************************

#ifndef AEVOL_DISCRETE_DOUBLE_FUZZY_H
#define AEVOL_DISCRETE_DOUBLE_FUZZY_H

#include <vector>

#include "macros.h"
#include "Point.h"
#include "fuzzytypes.h"
#include "AbstractFuzzy.h"

namespace aevol {

/// Triangular fuzzy sets.
///
/// This class provides management tools for "fuzzy sets" abscissa are
/// bound between X_MIN and X_MAX (defined in macros.h) A "fuzzy
/// set" should always have at least two points of abscissa X_MIN and
/// X_MAX.
///
/// A fuzzy set holds elements in a set X together with a probability
/// function X → [0,1] which tells how likely element x ∈ X beholds to
/// the fuzzy set. With these triangular fuzzy sets, the probability
/// function is a finite sum of isosceles triangles.
///
/// The current class models fuzzy sets over range X = [X_MIN; X_MAX]
/// by representing the probability function as the list of singular
/// points on its graph.
///
/// \verbatim
/// \\code{.unparsed}
///           ^
///       y2  +...              X                                            ....
///           |...             / \                                           ....
///       y6  +...            /   \               X                          ....
///           |...           /     \             / \                         ....
///       y4  +...          /       \   X       /   \                        ....
///           |...         /         \ / \     /     \                       ....
///      y3,y9+...        /           X   \   /       \             X        ....
///           |...       /                 \ /         \           / \       ....
///       y5  +...      /                   X           \         /   \      ....
///           |...     /                                 \       /     \     ....
///       0   +--X----X---------|-----|-|---|-----|-------X-----X-------X----X--->
///            X_MIN x1        x2    x3 x4 x5    x6      x7    x8  x9  x10 X_MAX
/// \\endcode
/// \endverbatim
/// fs.points_ would hold the list {(X_MIN,0),(x1,y1),...,(x10,y10)(X_MAX,0)}
///
/// \invariant{`points_.size()` ≥ 2}
/// \invariant{`points_.begin()->x == X_MIN`}
/// \invariant{`prev(points_.end())->x == X_MAX`}
/// \invariant{`is_increasing()`}
class Discrete_Double_Fuzzy : public AbstractFuzzy {
 public:
  Discrete_Double_Fuzzy(size_t vector_size);
  Discrete_Double_Fuzzy(const Discrete_Double_Fuzzy& f) = default;
  virtual ~Discrete_Double_Fuzzy() = default;

  void reset() override;
  void simplify() override;
  void add_triangle(fuzzy_x_t mean,
                    fuzzy_x_t half_width,
                    fuzzy_y_t height,
                    bool verbose = false) override;
  void add(const AbstractFuzzy& f, bool verbose = false) override;
  void sub(const AbstractFuzzy& f, bool verbose = false) override;
  void copy(const AbstractFuzzy& f, bool verbose = false) override;
  void normalize(const fuzzy_area_t& area) override;
  void add_point(fuzzy_x_t x, fuzzy_y_t y) override;

  void clip(clipping_direction direction, fuzzy_y_t bound) override;

  fuzzy_area_t get_geometric_area(bool verbose = false) const override;
  fuzzy_area_t get_geometric_area(fuzzy_x_t x_start, fuzzy_x_t x_stop) const override;
  bool is_identical_to(const AbstractFuzzy& fs, double tolerance) const override;
  void print() const override;
  void clear() override;
  size_t nb_points() const override { return points_.size(); };
  Point point(size_t i) const override;

 protected:
  mxifstream& do_extraction(mxifstream& is) override;
  mxofstream& do_insertion(mxofstream& os) const override;

  size_t nb_intervals_;
  double nb_intervals_d_;
  std::vector<fuzzy_y_t> points_;
};

}  // namespace aevol

#endif  // AEVOL_DISCRETE_DOUBLE_FUZZY_H
