/*
        Create a simple Bayesian Network and use Inference method.
        Alvaro Marin - split@splitcc.net
        Abril 2005.     
*/

#include "stdio.h"
#include "pnl_dll.hpp"
#include "cvsvd.h"

PNL_USING

void printBayesianNetwork( CBNet *netbay )
{
                
        const CFactor *pCPD;
        const CNumericDenseMatrix *pMatForCPD;
        const float *dataCPD;
        int numOfEl; 
        int f; 

        // Get information from learned model (number of factors)
        int nFactors = netbay->GetNumberOfFactors();
  
        for( f = 0; f < nFactors; f++ )
        {
                std::cout<GetFactor(f);
                pMatForCPD = static_cast *>(pCPD->GetMatrix(matTable));
                pMatForCPD->GetRawData( &numOfEl, &dataCPD );
                int j;
                for( j = 0; j < numOfEl; j++ )
                {
                        std::cout<<" "<AddNodes(numOfNds);
	pGraph->AddEdge(0,1,1);
	pGraph->AddEdge(0,2,1);
	pGraph->AddEdge(1,3,1);
	pGraph->AddEdge(2,3,1);
	
	pGraph->Dump();

	CNodeType *nodeTypes = new CNodeType [4];
	nodeTypes[0].SetType(1, 2);
	nodeTypes[1].SetType(1, 2);
	nodeTypes[2].SetType(1, 2);
	nodeTypes[3].SetType(1, 2);
	
	int *nodeAssociation = new int[numOfNds];
	for ( int i = 0; i < numOfNds; i++ )
    	{
        	nodeAssociation[i] = i;
    	}

	CBNet *pBNet;
	try{
		pBNet = CBNet::Create( numOfNds, numOfNds /*NumOfNodeTypes*/, nodeTypes, nodeAssociation, pGraph );
	}
	catch (pnl::CException ex)
  	{
		printf("[PNL] EXCEPTION %d: %s\n",ex.GetCode(),ex.GetMessage());
  	}	

	float table0[] = { 0.3f, 0.7f }; 
	float table1[] = { 0.2f, 0.8f }; 
	float table2[] = { 0.05f, 0.95f }; 
	float table3[] = { 0.25f, 0.75f, 0.65f, 0.35f, 0.2f, 0.8f, 0.1f, 0.9f };
	
	float* table[] = { table0, table1, table2, table3 }; 
		
	// Factor == CPD == Celda. Vamos alojando los CPD (Conditional Probabilistic Distribution) en cada Factor
	for( int i = 0; i < numOfNds; ++i ) 
	{ 
		pBNet->AllocFactor(i); 
		CFactor* pFactor = pBNet->GetFactor(i); 
		pFactor->AllocMatrix( table[i], matTable );			
	}

	printf("\n[PNL] pBNet created.\n");
	printBayesianNetwork(pBNet);
	
	getchar();

	return pBNet;
}


void inference(CBNet *pBNetAux)
{
	
	int i=0;
		
	CEvidence* pEvidForWS;
	CNaiveInfEngine *pNaiveInf;
	
	// Make one node observed
	int nObsNds = 3;
	
	// The observed node is:
	int obsNds[] = { 0, 1, 2 };
	
	valueVector obsVals;
	obsVals.resize(3);
	obsVals[0].SetInt(1);
	obsVals[1].SetInt(0);
	obsVals[2].SetInt(1);
	
	// We create the evidence node0=1 and node1=1
	pEvidForWS = CEvidence::Create( pBNetAux, nObsNds, obsNds, obsVals );
	
	printf("[PNL] Evidence created.\n"); 
	
	// Create Naive inference for BNet
	pNaiveInf = CNaiveInfEngine::Create( pBNetAux);
	
	// Enter evidence created before
	pNaiveInf->EnterEvidence( pEvidForWS );
	
	// We want to see the result of the node 2
	int numQueryNds = 1;
	int queryNds[] = { 3 }; 

	// MarginalNodes: Calculates joint probability distribution for the nodes
	pNaiveInf->MarginalNodes( queryNds, numQueryNds );
	
	// Class CPotential implements basic operations with factors.
	// GetQueryJPD -> Returns const pointer to joint probability distribution.
	const CPotential* pMarg = pNaiveInf->GetQueryJPD();
	intVector obsNdss;
	pConstValueVector obsVls;
	pEvidForWS->GetObsNodesWithValues(&obsNdss, &obsVls);
	for( i = 0; i < obsNdss.size(); i++ )
	{
	 	std::cout<<" observed value for node "<GetInt()<GetDomain( &nnodes, &domain );
	std::cout<<" Inference results: \n";
	std::cout<<"  Probability distribution for node [ ";
	for( i = 0; i < nnodes; i++ )
	{
	  std::cout <* pMat = pMarg->GetMatrix(matTable);
	
	// graphical model hase been created using dense matrix
	// so, the marginal is also dense
	EMatrixClass type = pMat->GetMatrixClass();
	if( ! ( type == mcDense || type == mcNumericDense || type == mc2DNumericDense ) )
	{
	    assert(0);
	}
	int nEl;
	const float* data;
	static_cast*>(pMat)->GetRawData(&nEl, &data);
	for( i = 0; i < nEl; i++ )
	{
		std::cout<<"  "<