/*  timing.c   */
/*  Copyright 1989 Mountain Math Software  */
/*  All Rights Reserved                    */
#include <stream.h>
#include <complex.h>
#include <stdlib.h>
#include <unistd.h>
#include "netenm.h"
#include "cgidbg.h"
#include "network.h"
#include "netlnk.h"
#include "feedbck.h"
#include "gcd.h"
#include "dskchnhd.h"
#include "thread.h"
#include "netcnt.h"

TimingAdjustment * TimingDescription::set_thread_base = 0 ;
TimingDescription::TimingState TimingDescription::state =
	TimingDescription::not_set ;

// void TestAlloc(const char * Msg=0);


// contains the timing code to assign timing to each output channel
// within a process net.

/*
 *  The algorithm for assigning timing recursively follows all
 *  paths in the network. For a linear
 *  path the alogorithm is straightforward and is basically contained
 *  in routine TimingDescription::UpdateTiming called by
 *  DfNodeOutLink::AssignTiming. One minor complication
 *  is the need to compute the logical time of each output sample.
 *  in general this time is a function of the DSP algorithm itself
 *  and thus cannot be determined from the data flow characteristics
 *  of the node. To handle this a virtual function is used that
 *  can be tailored for each node as needed. The base function
 *  which is called in the absence of a specialized function for
 *  the node uses the algorithm for a symmetric Fir filter. This
 *  algorithm only needs the data flow parameters.
 *
 *  Complications arise from two sources:
 *
 *	1. Feebback loops
 *
 *	2. DfNodes with multiple inputs
 *
 * 			Feedback Loops
 *
 *	Here the problem is to insure that the timing is consistent. This
 *	involves two requirements:
 *
 *	1. The resampling ratios must be identical for the head and tail
 *	of the loop - if this is not the case then either infinte buffering
 *	is required or the loop deadlocks depending on weather the data
 *	within the loop grows or shrinks.
 *
 *	2. The other requirement is that the total delay of the loop is
 * 	sufficient to prevent deadlock.
 *	
 *	The first condition is checked by simply comparing the two
 *	composite resampling ratios. The second condition is more
 *	complex. A heruristic algorithm is used in which the data
 *	consumtion characteristics of the loop are simulated until
 *	the internal buffer states loop or deadlock occurs.
 *	
 *	
 *		Nodes with Multiple Inputs
 *	
 *	Here again the user needs to specify the timing alignment of	
 *	the two input streams. Again we use a virtual function. If
 *	such a function is not supplied then the streams will be
 *	aligned at the same time (or as close as is possible given
 *	the sampling rate resolution.
 *	
 *	
 */

static void EqualityTest(int32 A, int32 B,TimingCheck& Return, TimingCheck Type)
{
	if (A==B) return ;
	Return = Type ;
/*
 *	LogOut << "Equality test failure for type " << Type << ", " << A
 *		<< " != " << B << "\n" ;
 */
}

void TimingDescription::SetSamplingAdjustment(TimingDescription& Reference,
	TimingAdjustment& Adjust)
{
	// LogOut << "::SetSamplingAdjustment\n" ;
	if (DenominatorSampling != 1 || NumeratorSampling != 1) {
		if (!Reference.BackAdjustment) Reference.BackAdjustment =
			new TimingAdjustment ;
		Reference.BackAdjustment->NumeratorFactor = DenominatorSampling;
		Reference.BackAdjustment->DenominatorFactor = NumeratorSampling;
	}

	Adjust.NumeratorFactor = Reference.DenominatorSampling ;
	Adjust.DenominatorFactor = Reference.NumeratorSampling ;
	
}


void TimingAdjustment::adjust_base(TimingDescription& to_adjust)
{
/*
 *		LogOut << "TimingAdjustment::adjust_base from:\n" ;
 *		to_adjust.Display();
 *		LogOut << "By adjustment:\n" ;
 *		Display() ;
 */
        to_adjust.NumeratorSampling *= NumeratorFactor ;
        to_adjust.DenominatorSampling *= DenominatorFactor ;
        to_adjust.FirstSample += TimeShift ;
/*
 *       LogOut << "Adjust to:\n" ;
 *       to_adjust.Display();
 */
}

void TimingDescription::SetFirstSampleAdjustment(TimingDescription& Reference,
	TimingAdjustment& Adjust)
{
	// Two sampling rates are identical. We need to multiply all
	// the rates on one path by the rate on the other to make
	// the sampling rates all in comparable units. The only case
	// where both channels do not need to be changed is when one
	// of them has sampling of 1/1. In that case the other channels
	// sampling rate does not change after the multiplication.

	double RefDiff = Reference.FirstSample - FirstSample ;

	if (RefDiff >  0.0) {
		if (!Reference.BackAdjustment) Reference.BackAdjustment =
			new TimingAdjustment ;
		Reference.BackAdjustment->TimeShift = RefDiff ;
	} else Adjust.TimeShift = -RefDiff ;
}

TimingCheck TimingDescription::CheckConsistent(TimingDescription& Reference,
	TimingAdjustment& Adjust)
{
	if (!NumeratorSampling) return TimingNotDefined ;
	TimingCheck Return = TimingCorrect ;
	EqualityTest(NumeratorSampling,Reference.NumeratorSampling,
		Return, TimingSamplingDifference);
	EqualityTest(DenominatorSampling,Reference.DenominatorSampling,
		Return, TimingSamplingDifference);
	if (IsSampleRateFixed()) if (Return != TimingCorrect)
		Return = TimingError ;
	if (Return == TimingSamplingDifference) SetSamplingAdjustment(
		Reference,Adjust);

	double Diff = FirstSample - Reference.FirstSample ;
	if (fabs(Diff)> 0.0) {
		if (Diff < 0.0) {
			if (IsTimeBaseFixed()) if ((Diff + ErrorTolerance -
				DeltaTimeBase) < 0) Return = TimingError ;
		} else if (Reference.IsTimeBaseFixed())
			if ((Diff - Reference.ErrorTolerance +
				Reference.DeltaTimeBase) < 0)
				Return = TimingError ;

		if (Return != TimingError) {
			if (Return == TimingSamplingDifference)
				Return = TimingAllDifferent ;
			else Return = TimingFirstSampleDifference ;
			SetFirstSampleAdjustment(Reference,Adjust);
		}
/*
 *		LogOut << "First sample equality failure " <<
 *			FirstSample << " != " << Reference.FirstSample << "\n" ;
 */
	}
	return Return ;
}

void TimingDescription::AdjustSampleRate(TimingDescription& timing)
{
	if (!DenominatorSampling || ! timing.NumeratorSampling) return ;
	SampleRate = timing.SampleRate * NumeratorSampling *
		timing.DenominatorSampling / (DenominatorSampling *
		timing.NumeratorSampling);
/*
 *	LogOut << "TimingDescription::AdjustSampleRate - new rate = "
 *		<< SampleRate << "\n" ;
 */

	// SampleRate = timing.SampleRate ;
}

TimingDescription * DfNode::GetInitTiming(int)
{
	return 0 ;
}

double DfNode::TimeFirst(DfNodeInLink *In, DfNodeOutLink * Out)
{
	// The assumption is that the middle of the output is at the
	// same time as the middle of the input
	int32 IncrementIn = 1 ;
	int32 IncrementOut = 1 ;
	int32 Overlap = 0;
	int32 Delay = 0 ;
	if (In) {
		IncrementIn = In->GetIncrementIn();
		Overlap = In->GetOverlap();
		Delay = In->GetDelay();
	}
	if (Out) IncrementOut = Out->GetIncrementOut() ;
	double MiddleInput = (Overlap + IncrementIn - 1) * .5 ;
	double MiddleOutput = (IncrementOut - 1) * .5 ;

	double Return = (MiddleInput * IncrementOut) / IncrementIn -
		MiddleOutput - (((double) Delay) * IncrementOut) / IncrementIn ;

/*
 *	LogOut << "TimeFirst for `" << GetName() << "', IncIn = " <<
 *		IncrementIn << ", IncOut = " << IncrementOut  << "\n" ;
 *	LogOut << "Ovlp = " << Overlap << ", Delay = " << Delay <<
 *		", MidIn = " << MiddleInput << "\n" ;
 *	LogOut << "MidOut = " << MiddleOutput << ", Returning " <<
 *		Return << "\n" ;
 */

	return Return ;
}

void TimingDescription::UpdateTiming(DfNodeInLink * In, DfNodeOutLink * Out,
	DfNode * TheNode)
{
	// LogOut << "::UpdateTiming\n" ;
	int32 IncrementIn =  1;
	if (In) IncrementIn = In->GetIncrementIn() ;
	int32 IncrementOut = 1;
	if (Out) IncrementOut = Out->GetIncrementOut() ;
/*
 *	LogOut << "TimingDescription::UpdateTiming: IncIn = " << IncrementIn
 *		<< ", IncOut = " << IncrementOut << ",\n\tNum = " <<
 *		NumeratorSampling << ", Den = " << DenominatorSampling << "\n";
 */
	int Return = RemoveCommonFactor(IncrementOut,IncrementIn);
	if (Return < 0) DbgError("TimingDescription::UpdateTiming",
		"bad in/out values");
	NumeratorSampling *= IncrementOut ;
	DenominatorSampling *= IncrementIn ;
	RemoveCommonFactor(NumeratorSampling,DenominatorSampling);

	if (TheNode) FirstSample = (TheNode->TimeFirst(In,Out) *
		DenominatorSampling) / NumeratorSampling + FirstSample ;
/*
 *	LogOut << "Timing for node `" << TheNode->GetName() <<
 *		"': FirstS = " << FirstSample << "\n" ;
 *	LogOut << "NumSamp = " << NumeratorSampling << ", DenSamp = "
 *		<< DenominatorSampling << ", FirstSample = " <<
 *		FirstSample << "\n" ;
 */
}

void TimingAdjustment::Display()
{
	TheLog << "TimingAdjustment:\n" ;
	TheLog << NumeratorFactor << " / " << DenominatorFactor
	 	<< " - " << TimeShift << ", Index and flag: " <<
	 	ChangeTimingIndex << ", " <<
	 	CurrentPathFlag << "\n" ;
}

static int AdjustTiming(TimingDescription& Timing, TimingAdjustment& Adjust)
{
	// LogOut << "::AdjustTiming\n" ;
	// If timing is not set yet than we leave it alone
	if (!Timing.NumeratorSampling) return 1 ;

	// If this node has already been updated to this timing level
	// do not update it again
	if (Timing.ChangeTimingIndex >= Adjust.ChangeTimingIndex) return 1 ;

	if (Adjust.NumeratorFactor != 1 || Adjust.DenominatorFactor != 1) {
		if (Timing.IsSampleRateFixed()) return 0;
		Timing.NumeratorSampling *= Adjust.NumeratorFactor ;
		Timing.DenominatorSampling *= Adjust.DenominatorFactor ;
		RemoveCommonFactor(Timing.NumeratorSampling,
			Timing.DenominatorSampling);
	}
	if (Adjust.TimeShift != 0.0) {
		if (Timing.IsTimeBaseFixed())
			if (Adjust.TimeShift > Timing.ErrorTolerance -
				Adjust.TimeShift) return 0;
		Timing.FirstSample += Adjust.TimeShift ;
		Timing.DeltaTimeBase += Adjust.TimeShift ;
	}
	return 1 ;
}

int DfNodeOutLink::DoChangeTiming(TimingAdjustment& Adjust)
{
	// LogOut << "DfNodeOutLink::DoChangeTiming:\n" ; NameDisplay() ;
	// Timing.Display();
	// Adjust.Display();
	// This routines does the actual timing change
	int ret =  AdjustTiming(Timing, Adjust) ;
	// Timing.Display();
	return ret ;
}

int DfNodeLink::ChangeTiming(TimingAdjustment& Adjust)
{
	// LogOut << "DfNodeLink::ChangeTiming\n" ;
	int Return = 1 ;
	DfNodeOut * Out ;
	DfNodeOutIterator Next(*OutputLinks) ;
	while (Out = Next()) {
		if (Out->GetIndex()) continue ;
			// don't enter a fan in node more than once
		DfNode * TheNode = Out->GetNode();
		if (!TheNode) DbgError("DfNodeLink::ChangeTiming",
			"Null output node");
		int Results = TheNode->ChangeTiming(Adjust);
		Return = Results && Return ;
	}
	return Return ;
}


int DfNodeLink::ChangeInTiming(TimingAdjustment& Adjust)
{
	// LogOut << "DfNodeLink::ChangeInTiming\n" ;
	return GetInputNode()->ChangeTiming(Adjust);
}


int DfNodeOutLink::ChangeTiming(TimingAdjustment& Adjust)
{
	// LogOut << "DfNodeOutLink::ChangeTiming\n" ; 
	// Timing.Display();
	int ret = 0 ;
	if (DoChangeTiming(Adjust)) ret = OutLink->ChangeTiming(Adjust) ;
	// Timing.Display();
	return ret;
}

int DfNodeInLink::ChangeTiming(TimingAdjustment& Adjust) 
{
	// LogOut << "DfNodeInLink::ChangeTiming\n" ;
	return InLink->ChangeInTiming(Adjust) ;
}

int DfNodeOutLink::DoAdjust(TimingAdjustment& Adjust)
{
	// LogOut<< "DfNodeOutLink::DoAdjust\n" ;
	// Timing.Display();
	if (Adjust.NumeratorFactor == 1 && Adjust.DenominatorFactor == 1 
		&& Adjust.TimeShift == 0.0) return 1;
	Adjust.ChangeTimingIndex++;
	int ret = ChangeTiming(Adjust);
	// Timing.Display();
	return ret ;
}

void TimingDescription::Display()
{
	TheLog << "TimingDescription:\n" ;
	TheLog << NumeratorSampling << " / " <<
		DenominatorSampling
		<< " - " << FirstSample << " (" << ErrorTolerance <<
		" : " << DeltaTimeBase << "), Index and flags: " <<
		ChangeTimingIndex << ", " << (int) ChangeFlags <<
		", type " << (int) TheTimingType <<  "\n" ;
	if (BackAdjustment) {
		TheLog << "Adjustment in this structure is:\n" ;
		BackAdjustment->Display();
	}
}

int DfNodeOutLink::AssignTiming(TimingDescription& Check, DfNodeInLink *In)
{
	// LogOut << "DfNodeOutLink::AssigningTiming for:\n" ; NameDisplay();
/*
 *	LogOut << "DfNodeOutLink::AssigningTiming for:\n" ; NameDisplay();
 *	LogOut << "Timing.NumeratorSampling = " <<Timing.NumeratorSampling<<"\n";
 *	LogOut << "Current and new timings are:\n" ;
 *	Timing.Display();
 *	Check.Display();
 */
	// Timing.Display();
	Check.UpdateTiming(In,this,GetDriverNode());
	// Timing.Display();
/*
 *	LogOut << "Timing.NumeratorSampling = " <<Timing.NumeratorSampling<<"\n";
 *	LogOut<< "GetDriverNode()->GetIn() = "<< GetDriverNode()->GetIn() <<"\n";
 */
	if (GetDriverNode()->GetIn()) if (Timing.NumeratorSampling) {
		// There is one input to a multi-input link
		// It is not a feedback loop because we would
		// not have been called from DfNode if it were.
		// If a timing adjustment is needed we either need
		// to patch in one or more delay nodes to adjust timing
		// or we need to adjust the base time on one path.
		// LogOut << "Checking if we need to and can adjust.\n" ;
		TimingAdjustment Adjust(Check.ChangeTimingIndex) ;
		TimingCheck CheckResults = Timing.CheckConsistent(Check,
			Adjust);
		switch (CheckResults) {
case TimingNotDefined:
			DbgError("DfNodeOutLink::AssignTiming",
				"inconsistent data");
			return 0 ;
case TimingCorrect:
			// Timing.Display();
			return 1 ;
case TimingSamplingDifference:
case TimingFirstSampleDifference:
case TimingAllDifferent:
			{
/*
 *				LogOut <<
 *					"Timing differs, TimingDescription::set_thread_base = " <<
 *					(void *) TimingDescription::set_thread_base << "\n" ;
 *				LogOut << "Timing:\n" ;
 *				Timing.Display();
 *				LogOut << "Check:\n" ;
 *				Check.Display();
 */
				TimingAdjustment * set_base =
					TimingDescription::set_thread_base ;
				if (set_base && TimingDescription::state==TimingDescription::not_set) {
					*set_base = Adjust ;
					if (Check.BackAdjustment) {
						set_base->NumeratorFactor *= Check.BackAdjustment->
							DenominatorFactor ;
						set_base->DenominatorFactor *=
							Check.BackAdjustment->NumeratorFactor ;
						set_base->TimeShift += Check.BackAdjustment->
							TimeShift ;
					}
					TimingDescription::state = TimingDescription::restart ;

					Timing.ClearSampling();
					// Timing.Display();
					return 1 ;
				}
				int temp = DoAdjust(Adjust);
				// Do Adjust may bump ChangeTimingIndex
				Check.ChangeTimingIndex =
					Adjust.ChangeTimingIndex ;
				// LogOut << "After DoAdjust in DfNodeOutLink::AssignTiming\n" ;
				// Timing.Display();
				return temp ;
			}
case TimingError:
			// Timing.Display();
			return 0 ;
		}

		// Timing.Display();
		return 1 ;
	}
	Timing = Check ;
	// LogOut << "Assigned time\n" ; Timing.Display();
	
	// LogOut << "Calling OutLink->AssignTiming\n" ;
	int Return = OutLink->AssignTiming(Check);
	if (TimingDescription::state == TimingDescription::restart) {
		Timing.ClearSampling();
		// Timing.Display();
		return 1 ;
	}
	int Results = 1 ;
	if (Check.IsInFeedbackLoop()) {
		// LogOut << "Checking for end of feedback loop (this = 0x" <<
		// 	hex((long) this) << ").\n" ;
		Results = Check.CheckEndFeedback(this);
		Return = Return && Results ;
	}
	if(Check.BackAdjustment) {
		Timing.NumeratorSampling *=
			Check.BackAdjustment->NumeratorFactor ;
		Timing.DenominatorSampling *=
			Check.BackAdjustment->DenominatorFactor ;
		Timing.FirstSample += Check.BackAdjustment->TimeShift ;
/*
 *		LogOut << "Did BackAdjustment:\n" ;
 *		Check.Display();
 *		LogOut << "AssignTiming - BackAdjustment NumSamp = "
 *			<< Timing.NumeratorSampling << "\n" ;
 */
	}
/*
 *	LogOut << "Numerator = " << Timing.NumeratorSampling
 *		<< ", Denominator = " << Timing.DenominatorSampling << "\n" ;
 *	Timing.Display();
 */
	// TestAlloc("exit assign timing");
	// Timing.Display();
	return Return ;
}


TimingCheck TimingDescription::CheckFeedbackTiming(TimingDescription& NewTiming)
{
	if (!NumeratorSampling) return TimingNotDefined ;
	if (NewTiming.NumeratorSampling == NumeratorSampling &&
		NewTiming.DenominatorSampling == DenominatorSampling)
		return TimingCorrect ;
/*
 *	LogOut << "CheckFeedbackTiming error, current:\n" ;
 *	Display();
 *	LogOut<< "New:\n" ;
 *	NewTiming.Display();
 */
	return TimingError ;
}

TimingCheck DfNodeOutLink::CheckFeedbackTiming(TimingDescription& NewTiming)
{
	return Timing.CheckFeedbackTiming(NewTiming) ;
}

static int timing_error_reported(DfNode * node, int set_flag=0)
{
	ProcessNet * net = node->GetActiveNet() ;
	if (!net) DbgError("timing_error_reported","no net");
	NetControl * cnt = net->GetTheController();
	if (!net) DbgError("timing_error_reported","no controller");
	if (cnt->is_timing_error()) return 1 ;
	if (set_flag) cnt->set_timing_error();
	return 0 ;
}

static int AdjustFail(DfNode * Where, const char * Why)
{
	if (timing_error_reported(Where,1)) return 0 ;
/*
 *	LogOut << "Timing adjust failure at node " << Where->GetName() ;
 *	LogOut << ".\nFailure due to " << Why << ".\n" ;
 */
	*Output + OutputCppHelp << "Timing adjust failure at node " <<
		Where->GetName() ;
	*Output + OutputCppHelp << ".\nFailure due to " << Why << ".\n" ;
	return 0 ;
}


int DfNode::ChangeTiming(TimingAdjustment& Adjust, int SkipIndex)
{
/*
 *	LogOut << "DfNode::ChangeTiming for `" << GetName() << "', skip = "
 *		<< SkipIndex << ".\n" ;
 */
	if (IsTraverseFlag(TimingChangeVisit)) return 1 ;
	SetTraverseFlag(TimingChangeVisit) ;
	int Return = 1;
	if (SkipIndex == -1) {
		if (GetTimingVisitChannel()) {
			// This is source node and is done elsewhere
			if (IsTraverseFlag(TimeAdjustSourceBit)) return 1 ;

			// Can't change current path
			if (!Adjust.CurrentPathFlag) return AdjustFail(this,
				"inconsistent timing in different threads");
		} else if (Adjust.CurrentPathFlag) {
			return
			AdjustFail(this,"inconsistent timing in different threads") ;
				// can only change current path
		}
	}
	for (int i = 0 ; i < Out; i++) {
		if (i == SkipIndex) continue ;
		int Result = GetOutLink(i)->ChangeTiming(Adjust);
		if (!Result && !timing_error_reported(this)) {
			AdjustFail(this, "cannot change output channel");
			*Output + OutputCppHelp << "Failure at channel "
				<< i << ".\n" ;
		}
		Return = Return && Result ;
	}
	ClearTraverseFlag(TimingChangeVisit) ;
	return Return ;
}


int DfNode::AssignTiming(TimingDescription& Timing, int Channel)
{
	// LogOut << "DfNode::AssignTiming for `" << GetName() << "'\n" ;
	int  PreviousChannel ;
	DfNodeInLink * InLk = 0;
/*
 *	LogOut << "Channel = " << Channel << ", Assigning in node:" <<
 *		GetName() << "\n" ;
 */
	if (Channel > -1) {
		InLk = GetInLink(Channel);
		// LogOut << "InLk->GetDelay() = " << InLk->GetDelay() << "\n" ;
		// LogOut << "InLk = 0x " << (long)(InLk) << "\n" ;
	}
	// LogOut<<"TimingVisitChannel = " << GetTimingVisitChannel() << ".\n" ;
	if ((PreviousChannel = GetTimingVisitChannel()) > -1 ) {
		// Feedback loop - need to check concistency of timing
		// with current settings on the specified input channel
		int FeedbackTimingConsistent = 0;
		if (Channel < 0 || Channel >= In) DbgError(
			"DfNode::AssignTiming", "bad input for feedback");
		TimingCheck Check ;
		for (int i = 0 ; i < Out; i++) {
			TimingDescription Temp = Timing ;
			Temp.UpdateTiming(InLk,GetOutLink(i),this);
			if (!IsTraverseFlag(TimeAdjustSourceBit))
				if (TimingDescription::state == TimingDescription::restart) {
				ClearTimingVisitChannel();
				return 1 ;
			}
			Check=GetOutLink(i)->CheckFeedbackTiming(Temp);
			TimingDescription * ThisTiming = 0 ;
			switch (Check) {
case TimingNotDefined:
				continue ;
case TimingCorrect:
				// LogOut << "Creating feedback loop.\n" ;
				Timing.AddFeedbackNode(InLk,GetOutLink(i)) ;
				ThisTiming =
					GetOutLink(i)->GetTiming();
				if (!ThisTiming->IsInFeedbackLoop())
					ThisTiming->SetFeedbackLoop();
/*
 *				LogOut << "Timing for `" << GetName()
 *					<< "'.\n" ;
 *				Timing.Display();
 */
				return 1 ;
case TimingSamplingDifference: 
case TimingFirstSampleDifference:
case TimingAllDifferent:
case TimingError:
				return 0 ;
			}
		}
		DbgError("DfNode::AssignTiming", "feedback loop not found");
		return 0;
	}
	ChangeTimingIndex = Timing.ChangeTimingIndex ;
	SetTimingVisitChannel(Channel) ;
	int VerifyOk = 1;
	int SkipChannel = Channel ;
	// LogOut << "DfNode::AssignTiming Out = " << Out << "\n" ;
	for (int i = 0 ; i < Out ; i++) {
		TimingDescription Temp ;
		TimingDescription * Init ;
		if (Init = GetInitTiming(i)) {
			Temp = *Init ;
/*
 *			LogOut << "Got InitTiming:\n" ;
 *			Temp.Display();
 */
			if (TimingDescription::state == TimingDescription::adjusted) {
				TimingAdjustment * adjust = TimingDescription::set_thread_base;
				if (!adjust) DbgError("DfNode::AssignTiming",
					"null set_thread_base");
				adjust->adjust_base(Temp);
			}
		}
		else Temp = Timing ;
		Temp.Loop = 0;
		// LogOut << "Calling GetOutLink(" << i << ")->AssignTiming\n" ;
		int Verify = GetOutLink(i)->AssignTiming(Temp,InLk);
		// LogOut << "Testing for merge.\n";
		if (Temp.Loop) Timing.Merge(Temp.Loop);
		if (Verify) if (Temp.BackAdjustment) {
			SetTraverseFlag(TimeAdjustSourceBit);
			Temp.BackAdjustment->SetCurrentPathFlag();
			
			ChangeTiming(*Temp.BackAdjustment,i);
			
			ClearTraverseFlag(TimeAdjustSourceBit);
			// LogOut << "AdjustTiming calls AssignTiming\n" ;
			AdjustTiming(Timing, *Temp.BackAdjustment) ;
			delete Temp.BackAdjustment;
			Temp.BackAdjustment = 0 ;
		}
		if (!IsTraverseFlag(TimeAdjustSourceBit))
		  if (TimingDescription::state == TimingDescription::restart) {
			ClearTimingVisitChannel();
			return 1 ;
		}
		VerifyOk = VerifyOk && Verify ;
	}
	ClearTimingVisitChannel();
	// LogOut << "End Timing for `" << GetName() << "'.\n" ;
	// Timing.Display();
	return VerifyOk ;
}

void TimingDescription::CheckInitDefault()
{
	if (NumeratorSampling > 0) return ;
	NumeratorSampling = DenominatorSampling = 1;
	FirstSample = 0.0 ;
}

int DfNodeLink::AssignTiming(TimingDescription& Timing)
{
	int Return = 1;
/*
 *	LogOut << "In DfNodeLink::AssignTiming: OutSize = " <<
 *	 	OutputLinks->Size() << "\n" ; 
 */
	DfNodeOut * Out ;
	DfNodeOutIterator Next(*OutputLinks) ;
	while (Out = Next()) {
		DfNode * TheNode = Out->GetNode();
		if (!TheNode) DbgError("DfNodeLink::AssignTiming",
			"Null output node");
		int Verify = TheNode->AssignTiming(Timing,Out->GetIndex()) ;
		if (TimingDescription::state == TimingDescription::restart) return 1 ;
		Return = Verify && Return ;
	}
	return Return ;
}

double DfNode::AlignChannels(int)
{
	return 0.0 ;
}

void DfNodeOutLink::NameDisplay()
{
	if (OutLink) {
		TheLog <<
			"This output link is between the following nodes:\n" ;
		OutLink->NameDisplay() ;
	}
} 

void TimingDescription::ClearSampling()
{
	// LogOut << "SampleRate = " << SampleRate << " TIMING CLEAR\n" ;
	SampleRate = 0.0 ;
	NumeratorSampling = DenominatorSampling = 0;
		ErrorTolerance = DeltaTimeBase = FirstSample = 0.0;
		BackAdjustment = 0; Loop = 0;
		ChangeFlags = 0; BackAdjustment = 0;
}
void TimingDescription::Clear()
{
	TheTimingType = TimingTypeLinear ;
	ClearSampling();
}

void TimingDescription::SetFromDisk(const NodeOutChannelHeader& DiskState)
{
#define GET(A) A = DiskState.A
	GET(NumeratorSampling) ;
	GET(DenominatorSampling) ;
	GET(SampleRate) ;
	GET(TheTimingType) ;
	GET(FirstSample) ;
	GET(ErrorTolerance) ;
	GET(DeltaTimeBase) ;
#undef GET
	int Warn = 0 ;
	if (!NumeratorSampling) {
		Warn =1 ;
		NumeratorSampling =1 ;
	}
	if (!DenominatorSampling) {
		Warn =1 ;
		DenominatorSampling =1 ;
	}
	if (Warn) {
		HelpOut << "Disk file sampling rate is bad.\n" ;
		HelpOut << "It has been set to one.\n" ;
	}
}

