/* This file is part of the source code for the following publication:
 * Assembling Self-Supporting Structures, Deuss et al., SIGGRAPH Asia 2014
 *
 * Copyright (C) 2014 Mario Deuss <mario.deuss@epfl.ch>
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef STATISTICS_H
#define STATISTICS_H

#include "SelfAssemblyDefines.h"
#include <iostream>
#include <iomanip>

namespace SelfAssembly{

class Statistics{
public:
	Statistics():min(std::numeric_limits<double>::max()),
		max(std::numeric_limits<double>::lowest()),sum(0),n(0),nnnan(0),nnz(0),nbz(0){;}

	double avg() const {
		if(nnnan>0){ return sum / nnnan; }
		else{ assert(sum==0); return sum;}
	}

	double min,max,sum;
	unsigned int n,nnnan,nnz,nbz;

	void add( double v){
		++n;
		if(!std::isnan(v)){
			double vn=std::max(v,0.);
			max = std::max(max,vn);
			min = std::min(min,vn);
			sum += vn;
			nnnan++;
			if(vn>numericalZero()){
				nnz++;
			}
			if(v<0){
				nbz++;
			}
		}
	}

	static std::string outputDeclaration(){
		std::stringstream ss;
		ss << std::left;
		ss << std::setw(19) << "max"
		   << std::setw(19) << "min"
		   << std::setw(19) << "sum"
		   << std::setw(19) << "average"
		   << std::setw(10) << "#entries"
		   << std::setw(10) << "#non-nan"
		   << std::setw(10) << "#non-zero"
		   << std::setw(10) << "#negative";
		return ss.str();
	}

	friend std::ostream& operator<<( std::ostream& out, const Statistics& s ){
		out << std::left << std::setprecision(12)
			<< std::setw(19) << s.max
			<< std::setw(19) << s.min
			<< std::setw(19) << s.sum
			<< std::setw(19) << s.avg()
			<< std::setw(10) << s.n
			<< std::setw(10) << s.nnnan
			<< std::setw(10) << s.nnz
			<< std::setw(10) << s.nbz;
		return out;
	}
};

class StatisticsOfStatistics{
public:
	void add( const Statistics& s){
		min.add(s.min);
		max.add(s.max);
		sum.add(s.sum);
		n.add(s.n);
		nnnan.add(s.nnnan);
		nnz.add(s.nnz);
		nbz.add(s.nbz);
	}

	Statistics min,max,sum,n,nnnan,nnz,nbz;

	friend std::ostream& operator<<( std::ostream& out, const StatisticsOfStatistics& s ){
		out << "Nnz's:   " << s.nnz << std::endl;
		out << "Max's:   " << s.max << std::endl;
		out << "Min's:   " << s.min << std::endl;
		out << "N's:     " << s.n << std::endl;
		out << "Nnnan's: " << s.nnnan << std::endl;
		out << "Nbz's:   " << s.nbz << std::endl;
		return out;
	}
};

}

#endif // STATISTICS_H
