Print["Ignore the following warning message: Lower::shdw"];
Off[Syntax::com];
Global`$MachineID = "2433-25098-26340";
Needs["MathTensor`","/usr/local/Wolfram/Mathematica/5.0/AddOns/Applications/MathTensor/MathTensor.m"];
Remove[Global`$MachineID]

Needs["Format`","Format.m"];
Needs["Optimize`","Optimize.m"];
BeginPackage["SetTensor`",{"MathTensor`","Format`","Optimize`"}];
Print["Turning off AssignFunction::undef"];
Off[AssignFunction::undef];

AddReplaceList::usage = "AddReplaceList is a place to store a
list of replacement rules";
FortranInt::usage = "FortranInt[x1_Integer] formats an integer
for output by the fwri function.";

AddReplace::usage = "fwri[...,AddReplace->{from_String,to_String}]";

MTEval[x1_] := EvalMT[RicciToAffine[x1]]
MTEval[x1_,x2_] := EvalMT[RicciToAffine[x1],x2]

SetSqrtDetg::usage = "Define this quantity to aid evaluation where
the determinant of the metric is required.";

(* something to make SqrtDetg work right *)
prot=Unprotect[Power,Abs];
Power[Power[sDetgVal,2],1/2] := sDetgVal
Abs[sDetgVal^2] := sDetgVal^2
DetgVal := sDetgVal^2
Protect /@ prot;

SetTensor::usage =
"SetTensor[name[ua,ub],{{n11,n12,n13,...},{n12,n22,...},...}]
SetTensor[name[ua,ub],MyFunction[ua,ub]]
The first form given above for SetTensor will
assign the components of the array on the right hand side to the
tensor on the left hand side.  The second form of this function
is like SetComponents, except that it does far more than merely
summing dummy indices.  Most importantly, it does not evaluate
MyFunction until a numerical value has been assigned to the index.
Secondly, before assigning it calls EvalMT to resolve ordinary
derivatives, sums, etc. If you wish to see a message as each
component is evaluated, you can do that by typing On[EvalMT::PrintIndex]
before calling SetTensor.";

EvalMT::PrintIndex = "If this flag is set by the On[] command, then
EvalMT prints its second argument when it runs (if it was called
with two arguments.  This means that SetTensor will tell you the
indices of each component of the tensor it is assigning values to."

EvalMT::usage = "EvalMT[expression], EvalMT[name[la,lb],{la->-1,lb->-1}]
In the first form, this function lowers all covariant derivative
indices, expands all sums, and evaluates all ordinary derivatives,
and Affine connection terms.  In the second form, the values of the
indices in the expression are given in list. In order for this command
to work properly, you must first use the command InitializeMetric.";

InitializeMetricg = InitializeMetric;
InitalizeMetric::usage = "InitializeMetric[array],
InitializeMetric[array,MySimplify] This command
takes the array metdn, assigns its components to the lower indexed
Metricg, assigns Inverse[metdn] to the upper index Metricg, and then
evaluates all AffineG components.  An optional function to simplify
these various stages is provided.";

EvalOD::usage = "EvalOD[expression] Evaluates ordinary derivatives
within an expression.";

MetricgVal::usage = "This object contains the component values for the
Metricg tensor."

AffineGVal::usage = "This object contains the component values for the
AffineG tensor."

SubFun::usage = "SubFun[expression,pattern,value] operates like
the normal substitution rule ->, except that if pattern is
a function it will also substitute for derivatives of that
function.  Furthermore, if pattern is an expression to a power,
it will match the negative power, Thus SubFun[Sqrt[x]+1/Sqrt[x],Sqrt[x],y]
becomes y+1/y.";

(* The user can over-ride this function *)
Clear[MakeDeriv];
MakeDeriv[x1_,x2_,x3_] := ToExpression["d"<>x1<>x2<>x3]
MakeDeriv::usage = "MakeDeriv[derivs,function,args] a user definable
function to make the representation of derivatives more suitable for
printing.";

fwri::usage = "fwri[OuputStream,lhs,rhs] an interface to the FortranAssign
command provided through MathSource.  It is an unnecessary but user-
friendly piece of syntatic sugar. Obsolete.  See new function: FortranWrite";

$FortranOptimize::usage = "Set to True or False.  Determines whether Fortran
output will be optimized automatically."
$FortranOptimize = True;

$FortranReplace::usage = "This is an array of rules of the form one might
hand to StringReplace.  It will be used to process the output of FortranWrite.";

$FortranArrayList::usage = "The optimize routine eliminates multiple calls to
subroutines within a function by assigning the output of the function to a
variable.  Since it is hard to distinguish arrays from functions syntactically
in fortran, you can use this variable to avoid the unnecessary creation of
excess scalar variables."

FortranReplaceDefault::usage = "Calling FortranReplaceDefault[] resets the value of
$FortranReplace to its initial value."

FortranOpen::usage = "A call to OpenWrite that makes sure the file is closed first.  Aside
from this, it is the same as OpenWrite."
FortranWrite::usage = "A call to WriteString and FortranAssign, but wrapped together
and buffered. Output is only actually done when FortranFlush or FortranClose are called.
The purpose of the buffering is to allow the FortranAssign optimizer to re-use common
subexpressions in the equations.";

FortranFlush::usage = "FortranFlush[fd] : Causes the output of the last several
FortranWrite's to be done.";

FortranClose::usage = "Calls FortranFlush and then closes the file.";

Iter::usage = "Iter[Tensor Object,Code] is a type of looping mechanism
similar to multiple nested For[] loops.  The looping is over the number
of unique index values to the tensor object.";

DerivRule::usage = "A rule to implement the MakeDeriv[] function.";

AddStrs::usage = "AddStrs[Append/Prepend,arg] adds a string to the
front/end of each element of a
list composed of lists and/or strings (which may have
a minus sign) or zero's.";

AddIndex::usage = "AddIndex[Append/Prepend,arg] adds an index to a
list composed of lists and/or strings (which may have
a minus sign) or zero's.";

AddSym2::usage = "AddSym2[Append/Prepend,arg] adds two symmetric
indicies to a
list composed of lists and/or strings (which may have
a minus sign) or zero's.";

AddAsym2::usage = "AddAsym2[Append/Prepend,arg] adds two
anti-symmetric indices to a
list composed of lists and/or strings (which may have
a minus sign) or zero's.";

AddDeps::usage = "AddDeps[arg] adds the default dependency list
to the end of all strings in its arg, then runs Clear and ToExpression
on each symbol name."

DepList::usage = "DepList[leftbracket,rightbracket] provides a string
object that represents the default list.  The arguments are strings
which provide the left and right brackets you wish to use, (), [], or {}";

FortranOutputOfDepList::usage = "FortranOutputOfDepList is a variable
which is set to the string value you want your dependcy list to map
to when Fortran output is produced.  It is null by default.";
FortranOutputOfDepList = "";

Set1Component::usage = "Set1Component[tensor element,value] sets one
element of a tensor.  It takes into account the symmetries of the
tensor when performing its operation.";

DefaultDepString::usage = "Set the value of this string to the default
dependency of your functions (without the brackets).  For examples \"x,y,z\"."; 

NeedsIt::usage = "NeedsIt[func] Returns true if the function has
been in an expression handed to AddtoNeedsIt[]";

AddToNeedsIt::usage = "AddToNeedsIt[expr] finds all functions in
expr of the form f_Symbol[a__] and makes NeedsIt[f[a]] return
True.";

ClearNeedsIt::usage = "ClearNeedsIt[] causes NeedsIt[_] to return
False.";

Begin["Private`"];

EvalMT::DimensionNotSet = "This function will not evaluate
properly until you set the variable Dimension";
DimNotSet[] := If[Not[MatchQ[Dimension,_Integer]],
	Message[EvalMT::DimensionNotSet];
	True,False];

(* flag setting... *)
prot = Unprotect[On,Off];
On[EvalMT::PrintIndex] := (Flag[EvalMT::PrintIndex] = True);
Off[EvalMT::PrintIndex] := (Flag[EvalMT::PrintIndex] = False);
Protect /@ prot;
On[EvalMT::PrintIndex]

(* some helper functions *)
(* just some shorthand *)
sti[x1_] := ToString[InputForm[x1]]

(* make a string that represents a comma joined list *)
CommaJoin[{x1_}] := x1
CommaJoin[{x1_,x2_}] := x1<>","<>x2;
CommaJoin[{x1_,x2__}] := x1<>","<>CommaJoin[{x2}];

(* NoSimp is a simplification method that does nothing,
 * PostSimp is a mechanism for applying the simplification
 * method (its first arg) after the evaluation of EvalMT.
 *)
Clear[PostSimp,NoSimp];
NoSimp[x1_] := x1
PostSimp[si_,fun_] := si[fun] /; FreeQ[fun,EvalMT];
PostSimp[si_,x1_List] := PostSimp[si,#]& /@ x1;

(* This section handles the form of SetTensor that
 * takes a list as an argument.
 *)
Clear[SetTensor,FunctionArg,ArrayArg]
FunctionArg[x1_?LowerIndexQ] := "l"<>ToString[x1]
FunctionArg[x1_?UpperIndexQ] := "u"<>ToString[x1]
ArrayArg[x1_?LowerIndexQ] := "-l"<>ToString[x1]
ArrayArg[x1_?UpperIndexQ] := "u"<>ToString[x1]

SetTensor[x1_,x2_] := SetTensor[x1,x2,NoSimp];

SetTensor[x1_[x2___],x3_List,si_] := Module[{ftmp,atmp,res},
	If[DimNotSet[],Return[]];
	ftmp = "["<>CommaJoin[FunctionArg /@ {x2}]<>"]";
	atmp = "[["<>CommaJoin[ArrayArg /@ {x2}]<>"]]";
	res="Iter["<>sti[x1]<>ftmp<>",Set1Component["<>
	sti[x1]<>ftmp<>","<>sti[PostSimp[si,x3]]<>atmp<>"]]";
	ToExpression[res];
	]

(* FakeIndex will not fool MathTensor, but it can
 * be used for SetTensor, because SetTensor only
 * looks at the Upper/LowerIndexQ functions.
 *)
Clear[FakeIndex];
FakeIndex[la_?LowerIndexQ] := Module[{tmp},
	tmp = Unique[la];
	LowerIndexQ[tmp] ^:= True;
	tmp
	]
FakeIndex[la_?UpperIndexQ] := Module[{tmp},
	tmp = Unique[la];
	UpperIndexQ[tmp] ^:= True;
	tmp
	]

(* This form of SetTensor uses a function to set
 * values. *)
SetTensor[x1_[x2__],x3_?NoList,si_] := Module[{rule,v1},
	If[DimNotSet[],Return[]];
	rule = {x2} /. v1_?IndexQ -> (v1->FakeIndex[v1]);
	tmp="Private`AssignVals["<>
	ToString[InputForm[x1[x2] /. rule]]<>",Private`PostSimp["<>
	sti[si]<>",EvalMT["<>
	ToString[InputForm[x3]]<>","<>
	ToString[InputForm[rule]]<>"]]];";
	ToExpression[tmp];
]

Print["Turning off MetricgFlag"];
Off[MetricgFlag];

(* evaluate ordinary derivatives *)
EvalOD[x1_] := Module[{ODtmp},
	On[EvaluateODFlag];
	ODtmp = x1;
	Off[EvaluateODFlag];
	ODtmp];

(* rule for lowering covariant derivative indicies *)
RuleUnique[
	CDdown,
	CD[x3_,ua_],
	CD[x3,lb] Metricg[ub,ua],
	UpperIndexQ[ua]
	];

(* fix a bug that exists in some versions of Mathematica *)
(* this is a kludge *)
DiagonalQ[x1_List] := Module[{id},
	id = IdentityMatrix[Length[x1]];
	If[x1 - (x1*id) === 0*id,True,False]];
Unprotect[Inverse];
Inverse[x1_?DiagonalQ] := Module[{i1,i2,ln},
	ln = Length[x1];
	Table[If[i1 == i2,1/x1[[i1,i2]],0],
		{i1,1,ln},{i2,1,ln}]
];
Protect[Inverse];
(* End bug fix *)

(* MathTensor Evaluator *)
EvalMT[x1_] := EvalMT[x1,{}];
EvalMT[x1_,x2_] := Module[{tmp},
	If[DimNotSet[],Return[]];
	tmp=Dum[x1];
	While[Not[FreeQ[tmp,LieD]],
		tmp=LieDtoCD[tmp];
	]; (* expand LieD's *)
	tmp=ApplyRulesRepeated[tmp,{CDdown}];
		(* we can only evaluate down indicies *)
	While[Not[FreeQ[tmp,CD]],
		tmp=CDtoOD[tmp];
	]; (* expand CD's *)
	tmp = SubVals[tmp];
	tmp = MakeSum[tmp];
	If[x2 =!= {},
		(* If[FreeQ[Messages[EvalMT],
		  Literal[EvalMT::PrintIndex] :> $Off[___]], *)
		If[Flag[EvalMT::PrintIndex],
		  Print[sti[x2]] ]; (* print messages if PrintIndex is On *)
		tmp = tmp //. x2; (* substitute index values *)
	];
	tmp = SubVals[tmp]; (* needed for Epsilon *)
	tmp = EvalOD[tmp];
	tmp
	];

SetSqrtDetg[x2_,x1_] := Module[{},signDetg=x2;SqrtDetg = x1]

SubVals[x1_] := (x1 /. {
	Metricg -> MetricgVal,
	AffineG -> AffineGVal,
	Sqrt[Abs[Detg]] -> SqrtDetg
	}) /. Detg -> signDetg SqrtDetg^2;

(* Sets things up so that EvalMT can evaluate rapidly *)
NoList[_List] := False
NoList[_] := True
InitializeMetric[metdn_List] := InitializeMetric[metdn,NoSimp];
InitializeMetric[metdn_List,SimpOp_?NoList] := Module[{},
	metup = SimpOp[Inverse[metdn]];
	InitializeMetric[metdn,metup,SimpOp]];
InitializeMetric[metdn_List,metup_List] := 
	InitializeMetric[metdn,metup,NoSimp];
InitializeMetric[metdn_List,metup_List,SimpOp_] := Module[{},
	Clear[MetricgVal];
	DefineTensor[MetricgVal,"gv",{{2,1},1}];
	SetTensor[MetricgVal[la,lb],metdn,SimpOp];
	SetTensor[MetricgVal[ua,ub],metup,SimpOp];
	SetTensor[MetricgVal[ua,lb],IdentityMatrix[Dimension]];

	Print["Evaluating Affine Connections"];

	Clear[AffineGVal];
	DefineTensor[AffineGVal,"Gv",{{1,3,2},1}];
	SetTensor[AffineGVal[ua,lb,lc],AffineToMetric[
		AffineG[ua,lb,lc]
		],SimpOp];
	
	Print["Done"];
];


(* Set Zero's: this looks at the symmetries of a tensor
 * and assigns the value 0 to components which must have
 * it by symmetry, i.e. RiemannR[-1,-1,-2,-3].
 *)

(* In reality, this functionality should be provided by
 * DefineTensor, and probably will be eventually.
 *)

(* consider the example:
 * DefineTensor[foo,{{2,1},-1}];
 * consider what SetZeros[foo[la,lb]] does...
 * AllSymmetries returns {{1, 2}, 1, {2, 1}, -1}
 *)
SetZeros[foo_[x1___]] := SetZeros[foo,{x1},AllSymmetries[foo[x1]] ];
SetZeros[foo_,x1_List,sym_List] := Module[{zer,i1,i2},
	For[i1=1,i1<=Length[sym],i1 += 2,
		If[sym[[i1+1]] == -1,
			(* We collect indices not in their proper order, and
			 * stick them in zer.  If sym[[i1]] were {1,3,2}
			 * zer would become {2,3}. *)
			(* When we get here, in our example,
			 * i1==3, sym[[i1]]=={2,1} *)
			zer={};
			For[i2=1,i2<=Length[sym[[i1]]],i2++,
				If[i2 != sym[[i1,i2]],zer=AppendTo[zer,i2]];
			];
			(* We make a copy of the non-symmetric index list,
			 * in our example {la,lb}, and use the next loop
			 * to fill it in so that it becomes {la,la} *)
			list2 = x1;
			For[i2=2,i2<=Length[zer],i2++,
				list2[[ zer[[i2]] ]] = list2[[ zer[[1]] ]];
			];
			(* Now, in our example, we call AssignVals with
			 * as follows AssignVals[foo[la,la],0] *)
			AssignVals[list2 /. List->foo,0];
		];
	];
]


(* EatIndex takes a list of objects (indices) and an object to be removed
 * from the list, and returns the list with all instances of object
 * rmoved.  For example:  EatIndex[{a,b,b,c},{b}] returns {a,c}
 *)
EatIndex[x1_List,x2_] := EatIndex[x1,{},x2];
EatIndex[{x1_,x2___},{x3___},x1_] := EatIndex[{x2},{x3},x1];
EatIndex[{x1_,x2___},{x3___},x4_] := EatIndex[{x2},{x3,x1},x4];
EatIndex[{},x2_List,x3_] := x2

(* just a shorthand *)
AssignVals[x1_[xa__],x2_] := Module[{tmp,tmp2,res},
	IterNoZeros[x1[xa],Set1Component[x1[xa],x2]];
];
	
SetAttributes[AssignVals,HoldRest];

Iter[x1_,x2_] := Module[{},
	If[DimNotSet[],Return[]];
	SetZeros[x1];
	IterNoZeros[x1,x2]
]
IterNoZeros[foov_[v1___],thingv_] := Module[{IterInternal},
	Clear[IterInternal,IterSet];
	IterSet[0] := 0;
	IterInternal[{x1_?LowerIndexQ,x2___},fo_,foo_,thing_,ru_List] :=
		Module[{ix,tmp},
		For[ix=-Dimension,ix<0,ix++,
			tmp=ru;
			IterInternal[EatIndex[{x2},x1],fo,foo /. x1->ix,thing,
			AppendTo[tmp,x1->ix]]
		]
	];
	IterInternal[{x1_?UpperIndexQ,x2___},fo_,foo_,thing_,ru_List] :=
		Module[{ix,tmp},
		For[ix=1,ix<=Dimension,ix++,
			tmp=ru;
			IterInternal[EatIndex[{x2},x1],fo,foo /.
				x1->ix,thing,AppendTo[tmp,x1->ix]]
		]
	];
	IterInternal[{},foo_,IterSet[foo_[x1__]],
		thing_,ru_List] := (Evaluate[Release[thing /.
		ru]];IterSet[foo[x1]]=1);
	IterInternal[{},fo_,x1_,x2_,ru_] := Null;
	IterInternal[{v1},foov,IterSet[foov[v1]],Hold[thingv],{}];
	];
SetAttributes[Iter,HoldRest];
SetAttributes[IterNoZeros,HoldRest];

(* SubFun is part of a larger idea for a more general substitution
 * function.  This version only does functions and powers
 *)
Clear[SubFun,unList,mkdiv];
SubFun[x1_,Power[x2_,x3_],x4_] :=
	x1 /. {
		Power[x2,x3]->x4,
		Power[x2,-x3]->1/x4
		};

unList[List[x1__]] := x1;
mkdiv[val_,nums_,args_] := Module[{tmp,ii},
	tmp=Table[{args[[ii]],nums[[ii]]},{ii,1,Length[args]}];
	D[val,Evaluate[unList[tmp]] ]
	];

SubFun[x1_,fun_[args__],val_] :=
	x1 /. {
		fun[args]:>val,
		Derivative[nums__][fun][args]:>mkdiv[val,
		List[nums],List[args]]
		};

SubFun[x1_,x2_,x3_] :> x1 /. x2 -> x3
(* End of SubFun *)

(* This section is for generating values for tensors *)
Clear[IsAppPrep];
IsAppPrep[Prepend] := True;
IsAppPrep[Append] := True;
IsAppPrep[_] := False;

Clear[AddStrs]
AddStrs[Append,x1_String,x2_String] := Module[{},
	If[DimNotSet[],Return[]];x1<>x2];
AddStrs[Prepend,x1_String,x2_String] := x2<>x1;
AddStrs[pa_?IsAppPrep,x1_String,x2_List] :=
	AddStrs[pa,x1,#]& /@ x2;
AddStrs[x1_String,x2_] := AddStrs[Append,x1,x2];
AddStrs[pa_,-x1_,x2_] := -AddStrs[pa,x1,x2];
AddStrs[pa_,x1_,-x2_] := -AddStrs[pa,x1,x2];
AddStrs[pa_,-x1_,-x2_] := AddStrs[pa,x1,x2];
AddStrs[pa_,0,x2_] := 0
AddStrs[pa_,x1_,0] := 0
AddStrs[pa_,x1_List,x2_] := AddStrs[pa,#,x2]& /@ x1;
AddStrs[x1_List,x2_] := AddStrs[Append,x1,x2];

Clear[AddIndex];
AddIndex[pa_?IsAppPrep,x1_String] := Module[{},
	If[DimNotSet[],Return[]];
	AddStrs[pa,x1,#]& /@
	Table[ToString[x[i]],{i,1,Dimension}]];
AddIndex[pa_?IsAppPrep,x1_List] := AddIndex[pa,#]& /@ x1;
AddIndex[x1_] := AddIndex[Append,x1];
AddIndex[pa_?IsAppPrep,-x1_] := -AddIndex[x1]
AddIndex[pa_?IsAppPrep,0] := Module[{i},Table[0,{i,1,Dimension}]]

Clear[AddSym2,Sym2];
Sym2[x1_Integer,x2_Integer] := Module[{tmp},
	If[DimNotSet[],Return[]];
	tmp = {x1,x2};
	tmp = Abs /@ tmp;
	tmp = Sort[tmp];
	tmp = x /@ tmp;
	tmp = ToString /@ tmp;
	tmp = tmp /. List->StringJoin
	];
Sym2[] := Module[{i,j},Table[Sym2[i,j],
	{i,1,Dimension},{j,1,MathTensor`Dimension}] ];
AddSym2[pa_?IsAppPrep,x1_String] := AddStrs[pa,x1,Sym2[]];
AddSym2[pa_?IsAppPrep,x1_List] := AddSym2[pa,#]& /@ x1;
AddSym2[x1_] := AddSym2[Append,x1];
AddSym2[pa_?IsAppPrep,0] := Module[{i,j},
	Table[0,{i,1,Dimension},{j,1,MathTensor`Dimension}]
	];
AddSym2[pa_?IsAppPrep,-x1_] := -AddSym2[pa,x1];

Clear[AddAsym2,Asym2];
Asym2[x1_Integer,x2_Integer] := Module[{tmp,sgn},
	If[DimNotSet[],Return[]];
	If[x1 === x2,Return[0]];
	tmp = {x1,x2};
	tmp = Abs /@ tmp;
	tmp = Sort[tmp];
	sgn = If[tmp === {x1,x2},1,-1];
	tmp = x /@ tmp;
	tmp = ToString /@ tmp;
	tmp = tmp /. List->StringJoin;
	tmp sgn
	];
Asym2[] := Module[{i,j},Table[Asym2[i,j],
	{i,1,Dimension},{j,1,MathTensor`Dimension}] ];
AddAsym2[pa_?IsAppPrep,x1_String] := AddStrs[pa,x1,Asym2[]];
AddAsym2[pa_?IsAppPrep,x1_List] := AddAsym2[pa,#]& /@ x1;
AddAsym2[x1_] := AddAsym2[Append,x1];
AddAsym2[pa_?IsAppPrep,0] := Module[{i,j},
	Table[0,{i,1,Dimension},{j,1,MathTensor`Dimension}]
	];
AddAsym2[pa_?IsAppPrep,-x1_] := -AddAsym2[pa,x1];

ClearStrs[x1_List] := ClearStrs /@ x1;
ClearStrs[x1_String] := Clear[x1];
ClearStrs[-x1_String] := Clear[x1];

Clear[ExtendedToExpression];
ExtendedToExpression[0] := 0;
ExtendedToExpression[-x1_String] := -ToExpression[x1];
ExtendedToExpression[x1_String] := ToExpression[x1];
ExtendedToExpression[x1_List] := ExtendedToExpression /@ x1;
AddDeps[x1_] := Module[{},
	If[DimNotSet[],Return[]];
	ClearStrs[x1];
	ExtendedToExpression[ AddStrs[Prepend,DepList["[","]"],x1] ]
	];

(* Note that this does not do the same thing as x1 = x2
 * by itself.  MathTensor expressions order themselves
 * according to symmetry, and this puts that action first
 * in the order of evaluations.
 *)
Clear[Set1Component];
Set1Component[-x1_,x2_] := x1 = -x2;
Set1Component[x1_,x2_] := x1 = x2;

(* Derivative conversion *)
Clear[DerivList,DerivWithRespect2,DerivOf,DerivDeps,StringRepeat];

(* I keep thinking that there must be some
 * utility for this already, but I don't know
 * what it is.
 *)
StringRepeat[x1_,0] := "";
StringRepeat[x1_,1] := x1;
StringRepeat[x1_String,n1_Integer] := 
	x1<>StringRepeat[x1,n1-1];

DerivList[n_,args_,n1_] := StringRepeat[ToString[args[[n]]],n1];
DerivList[n_,args_,n1_,n2__] := DerivList[n,args,n1]<>
	DerivList[n+1,args,n2];
DerivWithRespect2[Derivative[n___][x2_][x3___]] :=
	DerivList[1,{x3},n];
DerivOf[Derivative[x1___][x2_][x3___]] := ToString[x2]
DerivDeps[Derivative[x1___][x2_][x3___]] := "["<>
	CommaJoin[ToString /@ {x3}]<>"]";

Clear[DerivRule];
DerivRule := Derivative[x1___][x2_][x3___] :>
	MakeDeriv[DerivWithRespect2[Derivative[x1][x2][x3]],
			    DerivOf[Derivative[x1][x2][x3]],
			  DerivDeps[Derivative[x1][x2][x3]] ];
	
Clear[DepList]
DepList[s1_String,s2_String] := Module[{i},
	If[DimNotSet[],Return[]];
	If[MatchQ[DefaultDepString,_String],Return[s1<>
		DefaultDepString<>s2]];
	s1<>CommaJoin[Table[ToString[x[i]],{i,1,Dimension}]]<>s2
	]

Clear[NeedsIt,AddToNeedsIt];
NeedsIt[_] := False
ClearNeedsIt[] := (
	Clear[NeedsIt];
	NeedsIt[_] := False;
	)
AddToNeedsIt[x1_] := Module[{tmp,x2,srule,dep},
	tmp=x1 /. DerivRule;
	dep=DepList["[","]"];
	srule = sti[x2]<>"_Symbol"<>dep<>
	" :> (NeedsIt["<>
	sti[x2]<>dep<>"] = True; "<>sti[x2]<>dep<>")";
	tmp /. ToExpression[srule];
	];

(* Here is how I make fwri, so I made a short symbol name for once.
 * Once in a while we should save on typing..
 *)
Clear[fwri,GetAddReplaceRule,IsAddReplace,FortranAssignArray,IsCall]
GetAddReplaceRule[AddReplace->{x1_}] := x1;
IsAddReplace[AddReplace->_] := True
IsAddReplace[_] := False
FortranAssignArray[{x1___}] := FortranAssign[x1];
IsCall[x1_] := Module[{tmp},
	tmp = ToString[x1];
	StringMatchQ[tmp,"call"] ||
	StringMatchQ[tmp,"Call"] ||
	StringMatchQ[tmp,"CALL"]]

Clear[FortranInt]
FortranInt[x1_?PosIntegerQ] := ToExpression["EraseMe"<>ToString[x1]]
FortranInt[x1_?NegIntegerQ] := ToExpression["MinusSign"<>ToString[-x1]]

Options[fwri] := {AddReplace->{x1_String->x2_String}};
fwri[fd_,v1_,v2_,v3___] := Module[{tmp,i,faArgs,li},
	If[MatchQ[Dimension,_Integer],
		tmp={DepList["(",")"]->FortranOutputOfDepList},
		tmp={}
	];
	tmp=AppendTo[tmp,"EraseMe"->""];
	tmp=AppendTo[tmp,"UND"->"_"];
	tmp=AppendTo[tmp,"MinusSign"->"-"];
	If[IsCall[v1],tmp=AppendTo[tmp,"="->""]];
	faArgs={}; li={v3};
	If[MatchQ[AddReplaceList,_List],
		li=Join[li,AddReplaceList]]; 
	For[i=1,i<=Length[li],i++, 
		If[IsAddReplace[li[[i]]], 
			tmp=AppendTo[tmp,GetAddReplaceRule[li[[i]]] ], 
			faArgs=AppendTo[faArgs,li[[i]] ] 
		]; 
	];
	faArgs = AppendTo[faArgs,AssignReplace->tmp];
	faArgs = PrependTo[faArgs,v2 /. DerivRule];
	faArgs = PrependTo[faArgs,v1];
	Write[fd,FortranAssignArray[faArgs]];
];

Clear[FortranOpen,FortranWrite,FortranClose,FortranSimp,
$FortranArrayList,$FortranReplace,DefaultFortranReplace,FortranFlush,FortranLHS,FortranRHS];
DefaultFortranReplace[] :=
	$FortranReplace = {
		"(r,q)"->"(i,j)",
		"UND"->"_",
		"sin(q)"->"sint(j)",
		"cos(q)"->"cost(j)",
		"ric"->"r",
		"phi"->"p",
		"tan(q)"->"tant(j)",
		"EraseMe"->"",
		"MinusSign"->"-"
	};
DefaultFortranReplace[];
$FortranArrayList = {};

FortranOpen[file_] := FortranOpen[file,NoSimp];
FortranOpen[file_,Simp_] := Module[{fd},
	Off[General::openx];
	Close[file];
	Print["Opening: ",file];
	fd = OpenWrite[file];
	FortranLHS[fd] = {};
	FortranRHS[fd] = {};
	FortranSimp[fd][x1_] := Simp[x1];
	Return[fd];
	];
FortranWrite[fd_,from_,to_,etc___] := Module[{tmpto},
	from /. xxx_[args___] :> ($FortranArrayList=Union[$FortranArrayList,{xxx}];xxx[i,j]);
	tmpto = FortranSimp[fd][to /. DerivRule];
	FortranLHS[fd] = AppendTo[FortranLHS[fd],from];
	FortranRHS[fd] = AppendTo[FortranRHS[fd],tmpto];
	];
fprep[x1_] := Flatten[x1];
fprep[{x1_}] := x1;
FortranFlush[fd_] := Module[{},
	If[Length[FortranLHS[fd]]==0,Return[]];
	If[$FortranOptimize,
	  WriteString[fd,FortranAssign[Evaluate[fprep[FortranLHS[fd]]],
	    Evaluate[fprep[FortranRHS[fd]]],
	  AssignOptimize->True,OptimizeNull->$FortranArrayList,OptimizePower->Binary,
	  AssignReplace->$FortranReplace],"\n"];
	  ,
	  WriteString[fd,FortranAssign[Evaluate[fprep[FortranLHS[fd]]],
	    Evaluate[fprep[FortranRHS[fd]]],
	  AssignReplace->$FortranReplace],"\n"];
	];
	FortranLHS[fd]={};
	FortranRHS[fd]={};
];
FortranClose[_] := Print["Bad call to FortranClose -- not opened with FortranOpen"];
FortranClose[OutputStream[s1_String, n1_Integer]] := Module[{fd},
	fd = OutputStream[s1, n1];
	FortranFlush[fd];
	Print["Closing: ",s1 ];Close[fd];
];


Protect[SetTensor,EvalOD,EvalMT,InitializeMetric,SetSqrtDetg,
	Iter,AddToNeedsIt,ClearNeedsIt,fwri,DerivRule,
	Set1Component];

End[];
EndPackage[];
