/*
  maxentropy     最大エントロピーモデルのパラメータを計算
  maxentropy.cc

 Copyright (C) 2006 Masahiko Higashiyama  All rights reserved.
 This is free software with ABSOLUTELY NO WARRANTY.

 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, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 02111-1307, USA
*/

#include <vector>
#include <iostream>
#include <sstream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <string>

#define EPS 0.02
#define NEPS 0.00001
#define INF 1e+37
#define STOP_EPS EPS

//1事例のfeatureの最大数とfeatureの最大インデックス
int max_feature_num = 0;
int max_feature_index = 0;
//訓練事例の数
int example_num;
std::vector<std::vector<int> > fi2si;


inline void
calc_exp(const std::vector<double> &prob, std::vector<double> &output){
  for(int i = 0; i < max_feature_index; i++){
    for(unsigned int j = 0; j < fi2si[i].size(); j++){
      output[i] += prob[fi2si[i][j]];
    }
  }

}

inline double
calc_entropy(std::vector<double>& v){

  double result = 0.0;

  for(std::vector<double>::iterator itr = v.begin(); itr != v.end(); ++itr){
    if(*itr < EPS) continue;
    result -= *itr * log(*itr);
  }

  return result;
}

inline double newton(const std::vector <double> &a, double x, double y)  
{
  int n = (int)a.size() - 1 ;
  double fx, fp;

  do {
    fx = a[n];
    fp = n * a[n];
    for (int i = n - 1; i >= 1; i--) fx = fx * x + a[i];
    for (int i = n - 1; i >= 1; i--) fp = fp * x + i * a[i];
    fx += y;
    x -= fx/fp;
  } while (std::abs((double)fx) > NEPS);

  return x;
}

void
read(){

}

void
write(){

}

int main(int argc, char* argv[]){

  std::string filename = argv[1];
  bool read_flg = false;
  std::istream *ifs = &std::cin;
  if(strcmp("-", filename.c_str())){
    ifs = new std::ifstream(filename.c_str());
    read_flg = true;
    if(! *ifs){
      std::cout << "Can't open " << filename << std::endl;
    }
  }

  std::string line, tmp;
  std::vector<std::vector<int> > example;
  std::vector<int> feature_num;
  std::vector<double> model_prob, empirical_prob;
  double sum = 0.0;

  while(std::getline(*ifs, line)){
    std::stringstream iss(line);
    iss >> tmp;
    double freq = atof(tmp.c_str());
    if(freq < 0){
      std::cerr << "invalid freq" << std::endl;
    }

    std::vector<int> feature;
    while(!iss.eof() && (iss >> tmp)){
      int n = atoi(tmp.c_str());
      if(n > max_feature_index) max_feature_index = n;
      feature.push_back(n);
    }

    example.push_back(feature);
    //素性数
    int feature_size = feature.size();
    feature_num.push_back(feature_size);
    if(feature_size > max_feature_num) max_feature_num = feature_size;

    //頻度
    model_prob.push_back(freq);
    empirical_prob.push_back(freq);
    sum += freq;
  }

  if(read_flg){
    delete ifs;
  }
  example_num = example.size();

  for(int i = 0; i < example_num; i++){
    model_prob[i] /= sum;
    empirical_prob[i] = model_prob[i];
    for(int j = 0; j < feature_num[i]; j++) fi2si[example[i][j]].push_back(i);
  }

  std::vector<double> lambda, model_exp, empirical_exp, delta_old;
  lambda.resize(max_feature_index);  std::fill(lambda.begin(), lambda.end(), 0.0);
  model_exp.resize(max_feature_index);      std::fill(model_exp.begin(), model_exp.end(), 0.0);
  empirical_exp.resize(max_feature_index);  std::fill(empirical_exp.begin(), empirical_exp.end(), 0.0);
  delta_old.resize(max_feature_index);      std::fill(delta_old.begin(), delta_old.end(), 1.0);

  calc_exp(empirical_prob, empirical_prob);

  std::vector<double> poly_term;
  poly_term.resize(max_feature_num);

  double z;
  while(true){

    z = 0.0;
    for(int i = 0; i < example_num; i++){
      double tmp = 0.0;
      for(int j = 0; j < feature_num[i]; j++){
	tmp += lambda[example[i][j]];
      }
      model_prob[i] = exp(tmp);
      z += model_prob[i];
    }

    for(int i = 0; i < example_num; i++) model_prob[i] = model_prob[i] / z;

    double eps = -INF;
    for (int i = 0; i < max_feature_index; i++) { 
      std::fill(poly_term.begin(), poly_term.end(), 0.0);
      for (unsigned int j = 0; j < fi2si[i].size(); j++)
	poly_term[feature_num[fi2si[i][j]]] += model_prob[fi2si[i][j]];

      double delta = newton(poly_term, delta_old[i], empirical_exp[i]);
      delta_old[i] = delta;
      delta = log(delta);
      double abs_delta = std::abs(delta);
      if(eps < abs_delta) eps = abs_delta;
      lambda[i] += delta;
    }

    if(eps < STOP_EPS) break;
  }

  std::ofstream ofs(argv[2]);
  if (! ofs) {
    std::cerr << argv[0] << ": " << argv[argc-1] << ": Cannot write" << std::endl;
    exit(EXIT_FAILURE);
  }
  
  ofs.setf(std::ios::fixed,std::ios::floatfield);
  ofs.precision(16);
  ofs << max_feature_num << std::endl << max_feature_index << std::endl << z << std::endl; 
  for (int i = 0; i < max_feature_index; i++) ofs << i << " " << lambda[i] << std::endl;
  ofs.close();


}
