import java.io.*; 
import java.util.*;

/**
 * Title:        WaterMixApplet
 * Description:  Mixing water with Fuzzy Logic
 * Copyright:    Copyright (c) 2001
 * @author S. Robl <stefan@qdev.de>, A. Heinze <mail@aheinze.de>
 * @version 1.0
 */

class WaterMixFuzzyLogic
{
  static final int LL=-1; // EMPTY
  static final int NB=0, NM=1, NS=2, ZE=3, PS=4, PM=5, PB=6;
  static String fnames[]={"NB", "NM", "NS", "ZE", "PS", "PM", "PB"};

  // --------------------------------------------------------

  FuzzyCollection fdtemp      = new FuzzyCollection(4);
  FuzzyCollection fdremainvol = new FuzzyCollection(3);
  FuzzyCollection fdfillvol   = new FuzzyCollection(6);
    
  //                           dT: NB,NM,NS,ZE,PS,PM,PB      dV
  private int fuzzyrules[][] =	{ {LL,LL,LL,LL,LL,LL,LL}, // NB
                                  {LL,LL,LL,LL,LL,LL,LL}, // NM
                                  {LL,LL,LL,LL,LL,LL,LL}, // NS
                                  {PM,LL,PM,LL,NM,LL,NM}, // ZE
                                  {PM,LL,PS,LL,NS,LL,NM}, // PS
                                  {LL,LL,LL,LL,LL,LL,LL}, // PM
                                  {PB,LL,PB,LL,NB,LL,NB}, // PB
                                };
                       
  private int fuzzyindex[][]=new int [7][7];

  
  public WaterMixFuzzyLogic()
  {
    int i;
    
    i=0;
    fdtemp.setup(i++, NB, -9999, 100, -20,   -5,   0);
    fdtemp.setup(i++, NS,   -20,   0,  -2,  .23,   0);
    fdtemp.setup(i++, PS,   .23,   0,   2,   20,   0);
    fdtemp.setup(i++, PB,     5,   0,  20, 9999, 100);
        
    i=0;
    fdremainvol.setup(i++, ZE,  -9999, 100,     0, 3.333,   0);
    fdremainvol.setup(i++, PS,  2.666,   0, 3.333,     4,   0);
    fdremainvol.setup(i++, PB,  3.333,   0,    10,  9999, 100);

    i=0;
    fdfillvol.setup(i++, NB, -1.05, 0,     -1,   -0.7, 0);
    fdfillvol.setup(i++, NM,  -0.8, 0,   -0.5, -0.125, 0); 
    fdfillvol.setup(i++, NS,  -0.5, 0, -0.125,      0, 0); 
    fdfillvol.setup(i++, PS,     0, 0,  0.125,    0.5, 0); 
    fdfillvol.setup(i++, PM, 0.125, 0,    0.5,    0.8, 0); 
    fdfillvol.setup(i++, PB,   0.7, 0,      1,   1.05, 0); 
  
    createFuzzyIndex(fdfillvol);
  }
  
  private void createFuzzyIndex(FuzzyCollection fc)
  {
    for(int y=0; y<7; y++)
      for(int x=0; x<7; x++)
        fuzzyindex[y][x]=fc.getIndex(fuzzyrules[y][x]);
  }
  
  public FuzzyCollection getTempFuzzyCollection()
  {
    return fdtemp;
  }

  public FuzzyCollection getRemainVolFuzzyCollection()
  {
    return fdremainvol;
  }

  public FuzzyCollection getFillVolFuzzyCollection()
  {
    return fdfillvol;
  }

  private double calcFuzzy()
  {
    int t_minindex=0, t_maxindex=0, v_minindex=0, v_maxindex=0;
    double v, w;
    int x, y;

    for(x=0; x<fdtemp.getCount(); x++)
      if (fdtemp.getFuzzySet(x).getPercentage()>fdtemp.getFuzzySet(t_maxindex).getPercentage())
        t_maxindex=x;
    
    t_minindex=t_maxindex;
    for(x=0; x<fdtemp.getCount(); x++)
    {
      v=fdtemp.getFuzzySet(x).getPercentage();
      if (v>0 && v<fdtemp.getFuzzySet(t_minindex).getPercentage())
        t_minindex=x;
    }
    
    for(x=0; x<fdremainvol.getCount(); x++)
      if (fdremainvol.getFuzzySet(x).getPercentage()>fdremainvol.getFuzzySet(v_maxindex).getPercentage())
        v_maxindex=x;
    
    v_minindex=v_maxindex;
    for(x=0; x<fdremainvol.getCount(); x++)
    {
      v=fdremainvol.getFuzzySet(x).getPercentage();
      if (v>0 && v<fdremainvol.getFuzzySet(v_minindex).getPercentage())
        v_minindex=x;
    }
    
    /*
    System.out.println(t_minindex+", "+t_maxindex+"  --  "+v_minindex+", "+v_maxindex);
    String out="tmin/max, vmin/max:  ";
    out+=fnames[fdtemp.getFuzzySet(t_minindex).getType()]+" ("+fdtemp.getFuzzySet(t_minindex).getPercentage()+"%), ";
    out+=fnames[fdtemp.getFuzzySet(t_maxindex).getType()]+" ("+fdtemp.getFuzzySet(t_maxindex).getPercentage()+"%)  / ";
    out+=fnames[fdremainvol.getFuzzySet(v_minindex).getType()]+" ("+fdremainvol.getFuzzySet(v_minindex).getPercentage()+"%), ";
    out+=fnames[fdremainvol.getFuzzySet(v_maxindex).getType()]+" ("+fdremainvol.getFuzzySet(v_maxindex).getPercentage()+"%)";
    System.out.println(out);
    */
    
    int mi=fuzzyindex[fdremainvol.getFuzzySet(v_minindex).getType()][fdtemp.getFuzzySet(t_minindex).getType()];
    int ma=fuzzyindex[fdremainvol.getFuzzySet(v_maxindex).getType()][fdtemp.getFuzzySet(t_maxindex).getType()];
    
    //System.out.println("two states: "+fnames[mi]+", "+fnames[ma]);
    
    double maxp, minp;
    
    v=fdtemp.getFuzzySet(t_minindex).getPercentage();
    w=fdremainvol.getFuzzySet(v_minindex).getPercentage();
    
    if (v<w && v>0)
      minp=v;
    else
      minp=w;
    
    v=fdtemp.getFuzzySet(t_maxindex).getPercentage();
    w=fdremainvol.getFuzzySet(v_maxindex).getPercentage();
    
    if (v<w && v>0)
      maxp=v;
    else 
      maxp=w;
      
    fdfillvol.zeroPercentages();
    fdfillvol.getFuzzySet(mi).setPercentage(minp);
    fdfillvol.getFuzzySet(ma).setPercentage(maxp);
    
    //System.out.println("value for min.-function("+mi+"): "+minp);
    //System.out.println("value for max.-function("+ma+"): "+maxp);
    //System.out.println(fdfillvol.toString());
    //System.out.println("=> output-balance point at x="+fdfillvol.calcBalancePoint());
    
    return fdfillvol.calcBalancePoint();
  }
  
  // -------------------
  
  private double container_watervolume, target_watervolume;
  private double container_watertemp, target_watertemp, coldcup_watertemp, hotcup_watertemp;
  private double coldcup_watervolume, hotcup_watervolume;


  public void setInitialValues(double start_watertemp, double start_watervolume,
                               double coldcup_watertemp, double hotcup_watertemp, 
                               double target_watertemp, double target_watervolume)
  {
    container_watertemp     = start_watertemp;
    container_watervolume   = start_watervolume;
    this.coldcup_watertemp	= coldcup_watertemp;
    this.hotcup_watertemp	= hotcup_watertemp;
    this.target_watertemp   = target_watertemp;
    this.target_watervolume = target_watervolume;
    
    coldcup_watervolume=0;
    hotcup_watervolume=0;
  }
  
  public double getContainerWaterVolume()
  {
    return container_watervolume;
  }
  
  public double getContainerWaterTemp()
  {
    return container_watertemp;
  }
  
  public double getColdCupWaterTemp()
  {
    return coldcup_watertemp;
  }
  
  public double getHotCupWaterTemp()
  {
    return hotcup_watertemp;
  }
  
  // true: cold / false: hot
  public boolean getColdOrHotCup()
  {
    return coldcup_watervolume>0;
  }
  
  public double getColdCupWaterVolume()
  {
    return coldcup_watervolume;
  }
  
  public double getHotCupWaterVolume()
  {
    return hotcup_watervolume;
  }
  
  
  
  public void singleCalculation()
  {
    coldcup_watervolume=hotcup_watervolume=0;
    
    if (!finishedCalculation())
    {
      fdtemp.calc(container_watertemp-target_watertemp);
      fdremainvol.calc(target_watervolume-container_watervolume);
    
      double v=calcFuzzy();
      
      if (v<0)
      {
        coldcup_watervolume=-v;
        container_watertemp   = ((coldcup_watervolume*coldcup_watertemp)+(container_watervolume*container_watertemp))/(coldcup_watervolume+container_watervolume);
        container_watervolume+= coldcup_watervolume;
      }
      else
      {
        hotcup_watervolume=v;
        container_watertemp   = ((hotcup_watervolume*hotcup_watertemp)+(container_watervolume*container_watertemp))/(hotcup_watervolume+container_watervolume);
        container_watervolume+= hotcup_watervolume;
      }
    }
  }
  
  public boolean finishedCalculation()
  {
    return (container_watervolume>=target_watervolume);
  }
}
