//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1997
//
//  File:       recomend.h
//
//--------------------------------------------------------------------------

//
//	recomend.h:  Recommendations computations
//

#ifndef _RECOMEND_H_
#define _RECOMEND_H_

#include "cliqset.h"

const IST istNormal = 0;	//  MSRDEVBUG!

class MBNET_RECOMMENDER;

class GPNDDDIST
{
  public:
	GPNDDDIST ( GNODEMBND * pgndd = NULL )
		:_pgndd(pgndd)
	{
	}
	GNODEMBND & Gnd()
	{
		assert( _pgndd != NULL );
		return *_pgndd;
	}
	GNODEMBND * & Pgnd()
	{
		return _pgndd;
	}
	MDVCPD & Dist ()
	{ 
		return _dd; 
	}
	DECLARE_ORDERING_OPERATORS(GPNDDDIST);

  protected:
	GNODEMBND * _pgndd;
	MDVCPD _dd;
};

inline bool GPNDDDIST :: operator < ( const GPNDDDIST & gpndist ) const
{
	return _pgndd < gpndist._pgndd;
}

//  Define VGPNDDDIST, an array of GPNDDDISTs
DEFINEV(GPNDDDIST);	

//  Define a pair of node pointer and state index
typedef pair<GNODEMBND *,IST> PNDD_IST;
//  Define VPNDD_IST
DEFINEV(PNDD_IST);


//
//	Helper class containing processed node information extracted from
//	the belief network.
//
class GNODEREFP
{
  public:
	GNODEREFP ( PROPMGR & propMgr, GNODEMBND * pgndd );

	const COST CostObserve () const			{ return _costObserve;	}
	const COST CostFix () const				{ return _costFix;		}
	COST & Util ()							{ return _costUtil;		}
	ESTDLBL ELbl () const					{ return _eLbl;			}
	GNODEMBND & Gndd ()						{ return *_pgndd;		}
	GNODEMBND * Pgndd ()					{ return _pgndd;		}
	bool BLeak () const						{ return _bLeak;		}

	bool operator == ( const GNODEREFP & gndref ) const
		{ return _pgndd == gndref._pgndd ; }
	bool operator < ( const GNODEREFP & gndref ) const
		{ return _pgndd < gndref._pgndd;  }
	bool operator != ( const GNODEREFP & gndref ) const
		{ return !(self == gndref); }

	bool operator == ( const GNODEMBND * pgndd ) const
		{ return _pgndd == pgndd ; }
	bool operator < ( const GNODEMBND * pgndd  ) const
		{ return _pgndd < pgndd;  }
	bool operator != ( const GNODEMBND * pgndd ) const
		{ return !(self == pgndd); }
	
  protected:
	GNODEMBND * _pgndd;			//  Node pointer
	ESTDLBL _eLbl;				//  Standard label
	COST _costObserve;			//  Cost to observe
	COST _costFix;				//	Cost to fix
	COST _costUtil;				//  Computed utility
	bool _bLeak;				//  Leak node from CI expansion?
};

class VPGNODEREFP : public vector<GNODEREFP *>
{
  public:
	~ VPGNODEREFP ()
	{
		clear();
	}

	int ifind ( const GNODEMBND * pgndd )
	{
		for ( int indref = 0; indref < size(); indref++ )
		{
			if ( self[indref]->Pgndd() == pgndd )
				return indref;
		}
		return -1;
	}
	void clear ()
	{
		for ( int i = 0; i < size(); i++ )
			delete self[i];

		vector<GNODEREFP *>::clear();
	}
};

//	
//	Recommendations work node structure.  (Formerly 'PROBNODE')
//
class GNODERECWORK
{
	friend class VGNODERECWORK;
  public:
    GNODERECWORK ()
		: _pgndref(NULL),
		_pbFault(0),
		_pbOverCost(0)
		{}
	GNODEREFP * operator -> ()
		{ return _pgndref; }
	GNODEREFP * operator -> () const
		{ return _pgndref; }

	COST CostObsIfFixable () const
	{
		return BFixable()
			? _pgndref->CostObserve()
			: 0.0;
	}

	GNODEREFP * Pgndref () const
		{ return _pgndref; }
	GNODEREFP & Gndref ()	const	
	{
		assert( _pgndref );
		return *_pgndref;
	}
	void SetCost ( COST cost )
	{
		assert( _pgndref );
		_pgndref->Util() = - cost;
	}
	bool BFixable () const	
	{
		ESTDLBL elbl = Pgndref()->ELbl();
		return elbl == ESTDLBL_fixunobs
			|| elbl == ESTDLBL_fixobs;
	}	
	PROB PbOverCost () const		{ return _pbOverCost; }
	PROB PbFault () const			{ return _pbFault;    }
	void SetPbFault ( PROB prob )
		{ _pbFault = prob ; }

    DECLARE_ORDERING_OPERATORS(GNODERECWORK);

  protected:
    GNODEREFP * _pgndref;
	PROB _pbFault;
	PROB _pbOverCost;	

  protected:
	void Init ( MBNET_RECOMMENDER & mbnRecom, GNODEREFP * pgndref );
	void Init ( GNODEREFP * pgndref, PROB pbFault );
};

//
//	Controlled array of recommendations node work structures (Formerly RGPROBNODE).
//
class VGNODERECWORK : public vector<GNODERECWORK>
{
  public:
	VGNODERECWORK ( MBNET_RECOMMENDER * pmbnRec = NULL )
		: _pmbnRec( pmbnRec ),
		_bSeqSet( false ),
		_iFixedK(-1)
		{}

	void InitElem ( GNODEMBND * pgndd, int index = -1 );
	void InitElem ( GNODEREFP * pgndref, int index = -1 );
	enum ESORT 
	{ 
		ESRT_ProbOverCost, 
		ESRT_SgnProb, 
		ESRT_NegCost, 
		ESRT_SgnUtil 
	};

	void Sort ( ESORT esort );
	void Rescale ();
	COST Cost ( int ielemFirst = 0, int * piMinK = NULL );
	bool BSameSequence ( const VGNODERECWORK & vgnw );
	void SetSequenceCost ();
	COST CostECRDefault () const
	{
		assert( _bSeqSet );
		return size()
			 ? self[0]->Util()
			 : CostService();
	}
	MBNET_RECOMMENDER & MbnRec ()
	{
		assert( _pmbnRec );
		return *_pmbnRec;
	}
	const MBNET_RECOMMENDER & MbnRec () const
	{
		assert( _pmbnRec );
		return *_pmbnRec;
	}
	MBNET_RECOMMENDER * & PmbnRec ()
		{ return _pmbnRec; }

	COST CostService () const;

  protected:
	MBNET_RECOMMENDER * _pmbnRec;			//  The controlling recommendations object
	bool _bSeqSet;							//	Has the sequence been set yet?
	int _iFixedK;							//  Fixed state point
};

DEFINEV(VGNODERECWORK);

///////////////////////////////////////////////////////////////////////////////////////
//
//	MBNET_RECOMMENDER: 
//	
//		The troubleshooting recommendations object.  It's a "node ranker",
//		so its results are a list of node pointers and real values stored in members
//		of the base class, MBNET_NODE_RANKER.
//
//		Since all evidence is relative to a particular inference engine, that engine
//		must be used during construction.
//	
//		To invoke, use operator().  To determine if network state is compatible with
//		troubleshooting recommendations, call BReady().  If successful, the information
//		collected is saved for the next recommendations call.  To force recollection
//		of troubleshooting information, call Unready().
//
///////////////////////////////////////////////////////////////////////////////////////
class MBNET_RECOMMENDER : public MBNET_NODE_RANKER
{
  public:
	//   Recommendations computation method
	enum ERCMETHOD 
	{ 
		ERCM_None,
		ERCM_FixPlan,
		ERCM_Cheap,
		ERCM_MostLikely,
		ERCM_Random,
		ERCM_FixPlanOnly,
		ERCM_Max
	};

	//  Construct using the appropriate inference engine
	MBNET_RECOMMENDER ( GOBJMBN_CLIQSET & inferEng, 
						ERCMETHOD ercm = ERCM_FixPlan );
	virtual ~ MBNET_RECOMMENDER ();

	INT EType () const
		{ return EBNO_RANKER_RECOMMENDATIONS; }
	
	//  The ranking function
	virtual void operator () ();

	//  Return true if the network is in a state compatible with
	//		troubleshooting recommendations or sets ErcError().  Can
	//		be called separately or will be called by ranking operator().
	bool BReady ();		
	//  Clear the "ready" condition of the object
	void Unready () 
		{ _bReady = false; }
	//  Check to see if the object is in the "ready" condition
	bool BIsReady() const
		{ return _bReady; }

	//  Enter evidence for a troubleshooting model
	void EnterEvidence ( GNODEMBND * pgndd,			//  Node to set/observe
						 const CLAMP & clamp,		//  Value to set/unset
						 bool bSet = true );		//  Set or observe?

	//  Return the cost-of-service from the model; it's stored as 
	//		the model's 'cost-to-fix'.
	COST CostServiceModel ();

	//  General accessors
	ECGM EcError () const
		{ return _err; }
	ERCMETHOD ErcMethod () const
		{ return _ercm; }
	COST CostService () const
		{ return _costService; }
	COST CostObsProbDef () const
		{ return _costObsProbDef; }
	PROPMGR & PropMgr() 
		{ return _propMgr; }
	GNODEMBND * PgnddProbDefAbnormal () const
		{ return _pgnddPDAbnormal; }
	VPGNODEMBND & VpgnddFix () 
		{ return _vpgnddFix; }
	VPGNODEREFP & Vpgndref ()
		{ return _vpgndref; }
	ESTDLBL ELbl ( GNODEMBN & gnd );

 	//  Result array of relevant fixables; if 'bUsePriorList'
	//		is true, member array is starting point.  'pgnddInfo'
	//		is optional pointer to info node used in INFOPLAN.
	void DetermineRelevantFixableNodes ( VGPNDDDIST & vgndddFixRelevant,	
										 bool bUsePriorList,
										 GNODEMBND * pgnddInfoPlan = NULL );		

	void ComputeFixSequence ( VGPNDDDIST & vgndddFixRelevant,		//  IN: Relevant fixable nodes
							  VGNODERECWORK & vgnrwFix );			//  OUT: Ordered fix/repair sequence

	//  Interface to inference engine
	void InferGetBelief ( GNODEMBND * pgndd, MDVCPD & mdvBel );
	void InferGetEvidence ( GNODEMBND * pgndd, CLAMP & clamp );
	void InferEnterEvidence ( GNODEMBND * pgndd, const CLAMP & clamp );
	bool BInferImpossible ();

  protected:
	GOBJMBN_CLIQSET & _inferEng;		//  Inference engine
	PROPMGR _propMgr;					//  Property handler
	ECGM _err;							//  Last error code
	ERCMETHOD _ercm;					//  Planning method
	GNODEMBND * _pgnddPDAbnormal;		//  Abnormal PD node
	COST _costService;					//  Service cost; cost-to-fix of network
	COST _costObsProbDef;				//  Cost to observe PD node
	VPGNODEMBND _vpgnddFix;				//  Fixable nodes
	VPGNODEREFP _vpgndref;				//  Array of references to all nodes
	bool _bReady;						//  BReady() has been successfully called
	VGPNDDDIST _vgndddFixRelevant;		//  Relevant fixable nodes with unconditional distributions

  protected:
	GOBJMBN_CLIQSET & InferEng ()  
		{ return _inferEng; }

	//  Formerly "ComputeCosts"
	void DetermineRelevantInfoNodes ( VGNODERECWORK & vgnrwFix,		// IN: relevant fixables
									  VGNODERECWORK & vgnrwInfo );	// OUT: relevant infos

	//  Add to the given array all nodes which are downstream
	void ExpandDownstream ( VPGNODEMBND & vpgndd );
	//  Return true if the current state of evidence gives a different probability
	//	distribution that the one stored 
	bool BProbsChange ( GPNDDDIST & gpndddist );

	void PrintInstantiations ();

	HIDE_UNSAFE(MBNET_RECOMMENDER);
};

#endif // _RECOMEND_H_