/*  cmpseq.C   */
/*  Copyright 1991 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <math.h>
#include <stdlib.h>
#include <stream.h>
#include "cgidbg.h"
#include "netlnk.h"
#include "plimits.h"
#include "strmstr.h"
#include "yacintfc.h"
#include "tarparm.h"
#include "travparm.h"
#include "network.h"
#include "buffer.h"
#include "gcd.h"
#include "adjust.h"
#include "cmpseq.h"
#include "execseq.h"
#include "tarnodpar.h"
#include "bufstat.h"
extern int VarMaxSeqLength ;

/*
 *	Execution sequences can be of any of the following types:
 *
 *	1. Fixed sequence that completely uses each buffer with each
 *	call to a kernel.
 *
 *	2. Fixed sequence that completely exhausts each buffer but that
 *	may require multiple calls to kernel routines.
 *
 *	3. Fixed sequence that may wrap around buffer end without ever
 *	leaving it completely empty either because of inherent overlap
 *	in the consuming process or because one the buffers would need
 *	to be too large.
 *
 *	4. Dynamically determined sequence. Happens either because of
 *	a feedback loop or because other schemes yield buffers too large
 *	or sequences too long. (Feedback loop case can be anlyzed.)
 *
 *	Currently 4 must characterize an entire network. 1, 2 and 3
 *	may be independently assigned to each node. Eventually a network
 *	may be partitioned into parts scheduled as 1,2 and 3 andicate
 *	and parts scheduled according to 4.
 */

int CountNeeded(int32 Dividend, int32 Divisor)
{
	if (Dividend < 0 || Divisor <= 0) {
		TheLog << "Dividend = " << Dividend << ", Divisor = "
			<< Divisor << "\n" ;
		DbgError("CountNeeded","invalid parameters");
	}
	int Return = Dividend/Divisor ;
	if (Return * Divisor != Dividend) Return++;
	return Return ;
}



int BufferStatus::TryLarger()
{
	TryLargerBuffer = 0 ;
	return 0 ;
}


int32 TargetBufferParameters::ComputeCommonDivisor(int32 In, int32 Out,
	int32 Prev)
{
	// ComputeExecuiton Count
	int32 LocExec = GreatestCommonDivisor(In,Out);
	LocExec = CheckMultiply(In/LocExec, Out);
	LocExec = LocExec / In ;
	int32 Return = GreatestCommonDivisor(LocExec,Prev);
	Return = CheckMultiply(LocExec/Return,Prev);
/*
 *	LogOut << "TargetBufferParameters::ComputeCommonDivisor(" << In <<
 *		", " << Out << "," << Prev << ") = " << Return << "\n" ;
 */
	return Return ;
}

int32 TargetBufferParameters::AdjustMultiple(DfNodeOutLink * TheOutLink,
	int32 PreviousExecutionCount, BufferStatus * BufStat)
{
/*
 * 	LogOut << "TargetBufferParameters::AdjustMultiple(" <<
 *		PreviousExecutionCount << ") for " <<
 *		TheOutLink->GetDriverNodeName() << "\n" ;
 */
	int Error = 1 ;
	DfNodeLink * TheLink ;
	int TheCount ;
	if (TheOutLink) if (TheLink=TheOutLink->GetOutLink())
		if (TheCount=TheLink->GetOutSize())
			if (PreviousExecutionCount) Error = 0 ;
	if (Error) DbgError("TargetBufferParameters::AdjustMultiple",
		"bad parameters");
	int32 FirstExecutionCount ;
	int32 MultipleIn = TheOutLink->GetChunkSize() *
		TheOutLink->GetIncrementOut();
	int32 LocalExecutionCount = PreviousExecutionCount ;
				
	do for (int j = 0 ; j < TheCount; j++) {
		DfNodeInLink * ConsumerLink = TheLink->GetConsumerLink(j);
		DfNode * ConsumerNode = ConsumerLink->GetOutputNode();
		int32 ConsumerMultiple =
			ConsumerNode->GetTargetParameters()->GetNeededCount() ;
		int32 MultipleOut = ConsumerMultiple *
			ConsumerLink->GetChunkSize() *
			ConsumerLink->GetIncrementIn();
		// LogOut << "ConsumerMultiple = " << ConsumerMultiple << "\n" ;
		LocalExecutionCount = ComputeCommonDivisor(MultipleIn,
			MultipleOut,LocalExecutionCount);
		if (!LocalExecutionCount) return TraverseError ;
		if (!j) FirstExecutionCount = LocalExecutionCount ;
		if (State.IsError()) return TraverseError ;
		int32 BufSize = CheckMultiply(LocalExecutionCount,MultipleIn);
/*
 *		LogOut << "LocalExecutionCount = " << LocalExecutionCount
 *			<< ", MultipleIn = " << MultipleIn << ", BufSize = "
 *			<< BufSize << "\n" ;
 */
		SetLeastCommonMultiple(BufSize);
		if (!BufSize) {
			BufStat->SetReason(new ReasonForFailure (
			"least common multiple of execution counts overflows",
			TheOutLink->GetDriverNodeName(),-1,
			TheOutLink->GetDriverChannel(),
			ReasonForFailure::Output));
			return TraverseError ;
		} else if (BufSize > GetSize()) {
			TargetReaderParameters * TargetReader =
				ConsumerLink->GetTargetReaderParameters();
			if (!TargetReader) DbgError(
		"ExecutionSequence::AdjustMultiple","no reader parameters");
/*
 *			if (!TargetReader) {
 *				ConsumerLink->SetTargetReaderParameters(
 *				new TargetReaderParameters(*ConsumerLink,
 *					BufSize));
 *			}
 */
			ExactBufferSize SetExact(TheOutLink,j);
			// WHY DOES THIS SHRINK
			BufSize = SetExact.ComputeSize(*BufStat);
/*
 *			LogOut << "Multiple::Enlarging buffer " << GetSize()
 *				<< " to " << BufSize << "\n" ;
 */
			if (BufSize > GetSize()) AdjustSize(BufSize); 
		}
	} while (FirstExecutionCount != LocalExecutionCount) ;
/*
 *	LogOut << "LocalExecutionCount = " << LocalExecutionCount <<
 *		", MultipleIn = " << MultipleIn << "\n" ;
 */

	return LocalExecutionCount ;
}

ExactBufferSize::ExactBufferSize(DfNodeOutLink * ProviderLink,
	int BufferOutChannel)
{
	int Error = 1 ;
	DfNodeLink * BufferLink ;
	DfNodeInLink * ConsumerLink ;
	if (ProviderLink) if (BufferLink = ProviderLink->GetOutLink())
		if (ConsumerLink =
			BufferLink->GetConsumerLink(BufferOutChannel))
				Error=0;
	if (Error) DbgError("ExactBufferSize::ctor","bad link");
	TargetReaderParameters * Reader =
		ConsumerLink->GetTargetReaderParameters();
	TargetBufferParameters * Buf = BufferLink->GetTargetBuffer();
	if (!Buf || ! Reader) DbgError("ExactBufferSize::ctor",
		"null target(s)");
	ProviderIncrement = ProviderLink->GetChunkSize() *
		ProviderLink->GetIncrementOut() ;
	ConsumerIncrement = ConsumerLink->GetChunkSize() *
		ConsumerLink->GetIncrementIn() ;
	OverlapWords = ConsumerLink->GetOverlap() * ConsumerLink->GetChunkSize();
	LeftOver = Reader->GetLeftOver();
	NetworkCycleWords = Buf->GetLeastCommonMultiple() ;
	if (!NetworkCycleWords) DbgError("ExactBufferSize::ctor",
		"network cycle not set");
}

int ExactBufferSize::ComputeSequence(int32 BufferSize, int ** ReturnSequence)
// Returns the smallest execution count (if 0) then no sequence
// can be generated, if -1 then VarMaxSeqLength is exceeded
{
	int * Sequence = new int[VarMaxSeqLength] ;
	for (int i = 0 ; i < VarMaxSeqLength; i++) Sequence[i] = 0 ;
	int MinSequence = INT16_MAX ;
	int32 DataInBuffer = LeftOver ;
/*
 *	LogOut << "LeftOver = " <<  LeftOver << ", BufferSize = "
 *		<< BufferSize << ", OverlapWords = " << OverlapWords << "\n" ;
 *	LogOut << "ProviderIncrement = " << ProviderIncrement <<
 *		", ConsumerIncrement = " << ConsumerIncrement << "\n" ;
 */
	int32 CheckLoop = 0 ;
	for(int Index = 0; Index < VarMaxSeqLength; Index++) {
		int ProviderCount = (BufferSize - DataInBuffer) /
			ProviderIncrement;
		if (ProviderCount < 0) {
/*
 *			LogOut << "ExactBufferSize::ComputeSequence Provider = "
 *				<< ProviderCount << " returning 0\n" ;
 */
			delete Sequence ;
			Sequence = 0 ;
			return 0 ;
		}
		DataInBuffer += ProviderCount * ProviderIncrement ;
		int Count = (DataInBuffer - OverlapWords) / ConsumerIncrement ;
/*
 *		LogOut << Index << "::Provider = " << ProviderCount <<
 *			", Count = " << Count << ", DataInBuffer = " <<
 *			DataInBuffer << "\n" ;
 */
		if (LeftOver - OverlapWords >= ConsumerIncrement) {
			for (int count = Count-1 ; count > 0 ; count--)
				if ((DataInBuffer - ConsumerIncrement * count)
					== LeftOver) {
					Count = count ;
					break ;
				}
			// LogOut << "NewCount = " << Count << "\n" ;
		}
		if (Count < 1) {
/*
 *			LogOut << "ExactBufferSize::ComputeSequence Count = "
 *				<< Count << " returning 0\n" ;
 */
			delete Sequence ;
			Sequence = 0 ;
			return 0 ;
		}
		DataInBuffer -= Count * ConsumerIncrement ;
		if (!Index) CheckLoop = DataInBuffer ;
		else if (DataInBuffer  == CheckLoop) {
			if (ReturnSequence) {
				*ReturnSequence = new int[Index+1];
				for (int i = 0 ; i < Index;i++)
					(*ReturnSequence)[i]= Sequence[i];
				(*ReturnSequence)[Index]= -1 ;
			}
/*
 *			LogOut << "ExactBufferSize::ComputeSequence returning "
 *				<< MinSequence << "\n" ;
 */
			delete Sequence ;
			Sequence = 0 ;
/*
 *			if (ReturnSequence) for (i = 0 ; i < Index;i++)
 *				LogOut << (*ReturnSequence)[i] << "\n";
 */
			return MinSequence ;
		}
		Sequence[Index] = Count ;
		// LogOut << "Sequence[" << Index << "] = " << Count << "\n" ;
		if (Count < MinSequence) MinSequence = Count ;
	}
	// LogOut << "ExactBufferSize::ComputeSequence returning -1\n" ;
	delete Sequence ;
	Sequence = 0 ;
	return -1 ;
}

int32 ExactBufferSize::ComputeSize(BufferStatus& Constraints)
{
	int32 Desired = Constraints.GetDesiredSize();
	// enum Goal {ExistingSize,FixedSequence} TheGoal;
	if (LeftOver < OverlapWords) LeftOver = OverlapWords ;
	// DbgError("ExactBufferSize::ComputeSize", "LeftOver too small");
/*
 *	int32 CommonDivisor = GreatestCommonDivisor(ProviderIncrement,
 *		ConsumerIncrement);
 *	int32 CommonMultiple = CheckMultiply(ProviderIncrement/CommonDivisor,
 *		ConsumerIncrement);
 *	LogOut << "ExactBufferSize::ComputeSize Divisor = " <<
 *		CommonDivisor << ", Multiple = " << CommonMultiple << "\n" ;
 */
	int32 CommonMultiple = ProviderIncrement ;
 	int32 Test = Desired - LeftOver ;
/*
 *	LogOut << "Test = " << Test << ", LeftOver = " << LeftOver <<
 *		", Desired = " << Desired << "\n" ;
 */
	if (Test < 0) Test = 0 ;	
	int32 Divisor = Test / CommonMultiple ;
	int32 LowerBound = Divisor * CommonMultiple ;
	int32 UpperBound = Test ;
/*
 *	LogOut << "Divisor = " << Divisor << ", Lower = " << LowerBound <<
 *		", Upper = " << UpperBound << "\n" ;
 */
	if (LowerBound != UpperBound || !UpperBound)
		UpperBound = (Divisor+1) * CommonMultiple;
	if (!LowerBound) LowerBound = UpperBound ;
	LowerBound+= LeftOver ;
	UpperBound+= LeftOver ;

	int32 Closest = (Desired - LowerBound) > (UpperBound - Desired) ?
		UpperBound : LowerBound ;
	// LogOut << "Closest = " << Closest << "\n" ;
	
	int32 Return ;
	switch (Constraints.GetSizeGoal()) {
case BufferStatus::DesiredSizeUpperBound:
		Return = LowerBound ;
		break ;
case BufferStatus::DesiredSizeExact:
		Return = Closest ;
		break ;
case BufferStatus::DesiredSizeExactRequired:
		Return = Constraints.GetDesiredSize() ;
		break ;
case BufferStatus::DesiredSizeLowerBound:
		if (NetworkCycleWords <= Constraints.GetMaxSize()) {
			Return = NetworkCycleWords ;
			break ;
		}
		Return = UpperBound ;
		break ;
default:
		DbgError("ExactBufferSize::ComputSize","bad case");
	}
	for (;;) {
		int MinCount = ComputeSequence(Return);
		if (MinCount > 0) break ;
		if (MinCount < 0) {
			Return = -1 ;
			break ;
		}
		if (Constraints.GetSizeGoal() ==
			BufferStatus::DesiredSizeExact) {
			Return = 0 ;
			break ;
		}
		Return += ProviderIncrement ;
		if (Return > Constraints.GetMaxSize()) {
			Return = 0 ;
			break ;
		}
	}
	// LogOut<<"ExactBufferSize::ComputeSize returning "<<Return << "\n" ;
	return Return ;
}

TargetAdjustState ExecutionSequence::EnlargeSequence(int Base, int Increment)
{
	if (!Increment) return TargetAdjustOK ;
	int OldCurrentSize = CurrentSize ;
	BufStat->UpdateState(TargetAdjustChange);
	TargetAdjustState Return = SetSequence(CurrentSize+Increment);
/*
 *	for (int j = 0 ; j < CurrentSize; j++) LogOut << "Count[" << j
 *		<< "] = " << Count(j) << "\n" ; 
 */
	if (Return < TargetAdjustFail)  if (Base < OldCurrentSize)
	    for (int i=CurrentSize-1; i >= Base+Increment; i--) {
		SetValue(Count(i-Increment),i);
		SetValue(0,i-Increment);
	}

/*
 *	for (j = 0 ; j < CurrentSize; j++) LogOut << "Count[" << j
 *		<< "] = " << Count(j) << "\n" ; 
 *	LogOut << "ExecutionSequence::EnlargeSequence returning " << Return
 *		<< "\n";
 */
	return Return ;
}

TargetAdjustState ExecutionSequence::EnlargeSequence(Adjustment& TheAdjustment)
{
	int NewSize = TheAdjustment.GetNewTotal();
	int Base = TheAdjustment.GetEnlargeBase();
	int Increment = TheAdjustment.GetEnlargeSize() ;
/*
 *	LogOut << "New base = " << Base << ", size increase = "  <<
 *		Increment << ", new total = " << NewSize << "\n" ;
 *	LogOut << "CurrentSize = " << CurrentSize << "\n" ;
 */
	if (NewSize <= CurrentSize) return TargetAdjustOK ;
	if (CurrentSize + Increment != NewSize) {
/*
 *		LogOut << "Adjusting " << GetNode().GetName() <<
 *			", CurrentSize = " << CurrentSize << "\n" ;
 *		LogOut << "New base = " << Base << ", size increase = "  <<
 *			Increment << ", new total = " << NewSize << "\n" ;
 */
		Increment = NewSize - CurrentSize ;
		// DbgError("ExecutionSequence::EnlargeSequence","error");
	}
	return EnlargeSequence(Base,Increment);
}


