// このプログラムをfuzzy.cpp"という名前で保存して、
// gcc -g fuzzy.cpp -o fuzzy
// でコンパイルして下さい。

#include <stdio.h>
#include <stdlib.h>

// 共通関数
double max(double a){
  return a;
}

double max(double a, double b){
  if (a > b) 
    return a;
  else 
    return b;
};

double max(double a, double b, double c ){
  return max(max(a,b), c); 
};

double max(double a, double b, double c, double d ){
  return max(max(a,b,c), d); 
};

double min(double a){
  return a;
}

double min(double a, double b){
  if (b > a) 
    return a;
  else 
    return b;
};

double min(double a, double b, double c ){
  return min(min(a,b), c); 
};

double min(double a, double b, double c, double d ){
  return min(min(a,b,c), d); 
};

// ファジィ表現
typedef enum scale {LESSLESS, LESS, ZERO, MORE, MOREMORE} SCALE;

// 前件部メンバーシップ関数(山3つ)クラス
class condition_MF3
{
private:
  double center;
  double width;
  SCALE express;
  
public:
  condition_MF3(double _center, double _witdth, SCALE _express){
    center = _center;
    width = _witdth;
    express = _express;
  };
  double func(double x);
};

double condition_MF3::func(double x)
{
  // x,yは、メンバーシップ関数上の座標を示す
  double y = 0.0; // yの値は、必ず0以上1以下になる
  
  if (express == LESS){
    if (x <= center - width){
      y = 1.0;
    }
    else if (x <= center){
      y = - 1.0 / width * (x - center);
    }
    else{
      y = 0.0;
    }
  }
  else if (express == ZERO){
    if (x <= center - width){
      y = 0.0;
    }
    else if (x <= center){
      y = 1.0 / width * (x - center) + 1.0;
    }
    else if (x <= center + width){
      y = -1.0 / width * (x - center) + 1.0;
    }
    else{
      y = 0.0;
    }
  }
  else if (express == MORE){
    if (x <= center){
      y = 0.0;
    }
    else if (x <= center + width){
      y = 1.0 / width * (x - center);
    }
    else{
      y = 1.0;
    }
  }
  
  return y;
};

// 後件部メンバーシップ関数(山3つ)クラス  
class action_MF3
{
private:
  double center;
  double width;
  SCALE express;
  
  double x;
  double y;
  
public:
  action_MF3(double _center, double _witdth, SCALE _express){
    
    y = 0.0; // yの値は、必ず0以上1以下になる
    
    center = _center;
    width = _witdth;
    express = _express;
    
    if (express == LESS){
      x = center - width;
    }
    else if (express == ZERO){
      x = center;
    }
    else if (express == MORE){
      x = center + width;
    }
    else{
      printf("wrong scale expression\n");
      exit(0);
    }
  };
  
  // Y座標の値を最大値で更新する
  void func_Max(double b){
    y = max(b, y);
  };
  
  // Y座標の値をリセット(y=0)する
  void func_Reset(){
    y = 0.0;
  };
  
  // X座標を返す
  double func_X(void){
    return x;
  };
  
  // (最大値で更新された、最後の)Y座標を返す
  double func_Y(){
    return y;
  };
  
};

        
int main()
{
  condition_MF3 temp_High(20.0, 10.0, MORE);
  condition_MF3 temp_Middle(20.0, 10.0, ZERO);
  condition_MF3 temp_Low(20.0, 10.0, LESS);
  
  condition_MF3 humi_High(50.0, 20.0, MORE);
  condition_MF3 humi_Middle(50.0, 20.0, ZERO);
  condition_MF3 humi_Low(50.0, 20.0, LESS);
  
  // エアコンの温度設定を上げる("+1"にする)
  action_MF3 switch_High(0.0, 1.0, MORE); 
  
  // エアコンの温度設定に何もしない("0"にする)
  action_MF3 switch_Middle(0.0, 1.0, ZERO); 
  
  // エアコンの温度設定を下げる("-1"にする)
  action_MF3 switch_Low(0.0, 1.0, LESS); 
  
  // 入力値(温度27度、湿度57%)
  double t = 27.0;
  double h = 57.0;
  
  // (1)「もし温度が高くて湿度が高ければ、エアコンの温度設定を下げる」
  double a1 = min(temp_High.func(t),humi_High.func(h));
  printf("%f\n", a1);
  switch_Low.func_Max(a1);
  
  // (2)「もし温度が普通で湿度が高ければ、何もしない」
  double a2 = min(temp_Middle.func(t),humi_High.func(h));
  switch_Middle.func_Max(a2);
  printf("Middle"); 
  printf("%f\n", switch_Middle.func_Y());
  
  // (3)「もし温度が高くて湿度が普通なら、エアコンの温度設定を下げる」
  double a3 = min(temp_High.func(t),humi_Middle.func(h));
  switch_Low.func_Max(a3) ;
  printf("Low");
  printf("%f\n", switch_Low.func_Y());
  
  // (4)「もし温度が低くて湿度が低ければ、エアコンの温度設定を上げる」
  double a4 = min(temp_Middle.func(t),humi_Low.func(h));
  switch_High.func_Max(a4) ;
  printf("High" );
  printf("%f\n", switch_High.func_Y());
  
  // (5)(追加)もし温度が普通で、湿度が普通なら、何もしない
  double a5 = min(temp_Middle.func(t),humi_Middle.func(h));
  switch_Middle.func_Max(a5) ;
  printf( "Middle" );
  printf("%f\n", switch_Middle.func_Y() );
  
  // 推論値(重心値)を求める
  
  double reasoning =  
    (switch_High.func_X() * switch_High.func_Y()  + 
     switch_Middle.func_X() * switch_Middle.func_Y()  + 
     switch_Low.func_X() * switch_Low.func_Y()) 
    / 
    (switch_High.func_Y()  + 
     switch_Middle.func_Y()  + 
     switch_Low.func_Y());
  
  printf("\n reasoning = %f\n",reasoning);
  
  // 後件部メンバーシップ関数のリセット(ループする時は必ずリセットする)
  switch_High.func_Reset();
  switch_Middle.func_Reset();
  switch_Low.func_Reset();
  
}