using System;
using System.IO;			// for StreamWriter I/O to a file
using System.Windows.Forms;	// for RichTextBox

namespace MyNamespace
{
	/// <summary>
	///		Extra precision floating-point List algorithms
	///		Developed in TURBO Pascal 5.5 and 6.0, converted to Visual Basic 6
	///		and now in Visual C# .Net
	///
	/// ---------------- *** LEGAL NOTICE: *** ------------------
	///
	/// This program is free software; you can redistribute it and/or modify
	/// it under the terms of the GNU General Public License (GPL) as published
	/// by the Free Software Foundation. cf. the file COPYING.txt.
	///
	/// ------------ *** end of legal notice *** ------------------
	/// </summary>

	public class XPCalLst
	{
		public XPCalLst()
		{
			limFac = 2000;
			InitGammaSeries();
			macheps = 1.1102230246251568e-016;	// smallest such that 1.0+DBL_EPSILON != 1.0
			machmin = Double.Epsilon;			// 4.94065645841247e-324 min positive value
			Common.saveI = 1;
		}

		public static void ExpandMore(int more) // expand the precision for guard digits
		// this may slightly exceed MultiID.muNMax
		{
			Calc.fMC += more;	// more guard "digits"
			MultiID.mMax += more;
			if (MultiID.mMax > MultiID.muNMax) MultiID.mMax = MultiID.muNMax;
		}

		public static void Expand() // expand the precision for guard digits
		// this may slightly exceed MultiID.muNMax
		{
			if (!MultiFD.expanded)
			{
				MultiFD.expanded = true;
				MultiFD.fMCB = Calc.fMC;	// save basic precision
				Calc.fMC += MultiFD.fGuard;	// n guard "digits"
				MultiFD.mMaxB = MultiID.mMax;
				MultiID.mMax += MultiFD.fGuard + 1;
				if (MultiID.mMax > MultiID.muNMax) MultiID.mMax = MultiID.muNMax;
			}
		}

		public static void RRIntN(int yI, int zI) // z = Random Integer between y and z inclusive
		{
			int yLI;	// y local
			int zLI;	// z local
			int rNI;	// random number
			int rLI;	// random number local
			Common.InitNAdd(Common.nList[yI].N, "RRIntN-yLI", out yLI);
			Common.InitNAdd(Common.nList[zI].N, "RRIntN-zLI", out zLI);
			Common.nList[yLI] &= Common.nList[yI];
			Common.nList[zLI] &= Common.nList[zI];
			RIntN(yLI);
			RIntN(zLI);
			if (Common.nList[yLI] < Common.nList[zLI])
			{
				int tmp = yLI;
				yLI = zLI;
				zLI = tmp;
			}
			Common.LFindAdd("RN", out rNI);
			Calc.RRanN(rNI);		// rN = (rNA * rN + rNC) % (10^35)
			Common.InitNAdd(Common.nList[rNI].N, "RRIntN-rLI", out rLI);
			Common.nList[rLI] &= Common.nList[rNI];
			if (rNI == zI || rNI == Common.iTop)
				MultiID.WriteLn("RInt: Warning, changing RN by RInt(X, Y), continuing...");
			RSubN(Common.nList[zLI], yLI);
			Add1N(Common.nList[yLI], 1, zI);
			RMulN(Common.nList[rLI], zI);
			RIntN(zI);
			RAddN(Common.nList[zLI], zI);	// z = Int(RN * (y - z + 1)) + z, y >= z, integers
			Common.LDelete(rLI);
			if (zLI > yLI)
			{
				Common.LDelete(zLI);
				Common.LDelete(yLI);
			}
			else
			{
				Common.LDelete(yLI);
				Common.LDelete(zLI);
			}
			Calc.NormN(zI);
		} // RRIntN

		public static void RAddN(MultiF x, int zI)	// z = z + x
		// sets muErr true if overflow
		{
			AddN(Common.nList[zI], x, zI);
		}

		public static void RSubN(MultiF x, int zI) // z = z - x
		// sets muErr true if overflow
		{
			SubN(Common.nList[zI], x, zI);
		}

		public static void AddN(MultiF x, MultiF y, int zI) // z = x + y
		// sets muErr true if overflow
		{
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.AddF(x, y, t);
			Calc.SetToN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void SubN(MultiF x, MultiF y, int zI) // z = x - y
		{
			// sets muErr true if overflow
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SubF(x, y, t);
			Calc.SetToN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void SetTo1N(int d1, int zI) // z = d1 % baseM
		// allows a literal d1, d1 an integer, |d1| < baseM, d1 not changed
		{
			MultiF t = new MultiF(2);
			MultiFD.SetTo1F(d1, t);
			Calc.SetToN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void SetToDbN(double db, int zI) // z = db % baseM^4
		// allows a literal db, db not changed, muErr set true if overflow
		{
			MultiF t = new MultiF(4);
			MultiFD.SetToDbF(db, t);
			Calc.SetToN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void RAdd1N(int d1, int zI) // z = z + d1
		// allows literal d1, d1 is not changed, sets muErr true if overflow
		{
			MultiF t = new MultiF(2); // temp
			MultiFD.SetTo1F(d1, t);
			RAddN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void Add1N(MultiF x, int d1, int zI) // z = x + d1
		// allows literal d1, d1 is not changed, sets muErr true if overflow
		{
			Calc.SetToN(x, zI);
			RAdd1N(d1, zI);
		}

		public static void NewLine(StreamWriter outF, ref int line, ref int page)
		{
			MultiID.WriteLn(null, outF);
			line++;
			if (line == 60)
			{
				if (!Common.quiet) MultiID.WriteLn("Page " + page);
				line = 0;
				MultiID.WriteLn(null, outF);
				MultiID.WriteLn(null, outF, Common.StringOf(27, " ") + "Page " + page);
				MultiID.WriteLn2(null, outF);
				MultiID.WriteLn2(null, outF);
				page++;
			}
		}

		public static void FormatNB(StreamWriter outF, string name, MultiF z)
		// output z as a formatted set of lines in base baseO
		{
			int tot = 0;
			MultiID.WriteLn(null, outF);
			MultiID.WriteTLn2(null, outF, "Base(" + MultiID.baseO + ")", ref tot, 0);
			MultiID.WriteT(null, outF, name + " =", ref tot, 0);
			MultiFD.WriteF(null, outF, z, ref tot, 0); // WriteF will call WriteFB
			MultiID.WriteLn(null, outF);
		}

		public static void FormatN(StreamWriter outF, string name, MultiF z)
		// output z as a formatted set of lines and pages
		// z is not modified
		{
			string nm;
			int n;
			int i;
			int j;
			string st;
			string st2;
			int zL;
			string ch;
			int k;
			int did;
			decimal dE;
			int page;
			int line;	// line number of last line printed, 0 - 66
			if (MultiID.baseO != 10)
			{
				FormatNB(outF, name, z);
				return;
			}
			nm = name;
			n = z.N;
			st = ((int)(MultiID.baseM + z.V[n-1])).ToString();
			j = 1;
			do
				j++;
			while (st[j-1] == '0' && j != MultiID.muDMax + 1);
			zL = j - 2;
			page = 1;
			MultiID.WriteLn(null, outF);
			MultiID.WriteLn2(null, outF);
			dE = z.C;
			dE = MultiID.muDMax * (dE + n) - zL - 1;
			if (dE >= 0)
				ch = "+";
			else
				ch = "-";
			st2 = Math.Abs(dE).ToString();
			k = st2.Length;
			if (k > MultiID.muDpG && MultiID.muDpG != 0)
			{
				int jj = k + 1;
				int iL = (k - 1) / MultiID.muDpG;
				for (i = 1; i <= iL; i++)
				{
					jj -= MultiID.muDpG;
					st2 = st2.Substring(0, jj - 1) + "," + st2.Substring(jj - 1);
				}
			}
			MultiID.WriteLn2(null, outF, nm + " = m.n E" + ch + st2 + ", m.n ="); // line may > 80 ch
			line = 6;
			if (z.S) MultiID.Write(null, outF, "-");
			MultiID.Write(null, outF, st.Substring(j - 1, 1) + ".");
			NewLine(outF, ref line, ref page);
			did = 0;
			k = MultiID.muDMax + 1;
			if (n == 1)
			{
				while (st[k-1] == '0')
					k--;
			}
			if (j >= k && n == 1)
			{
				MultiID.Write(null, outF, "0");
				did = 1; // not in DOD VPCalc
			}
			while (j < k)
			{
				j++;
				MultiID.Write(null, outF, st.Substring(j - 1, 1));
				did++;
				if (did % 5 == 0) MultiID.Write(null, outF, " ");
			}
			for (i = 2; i <= n; i++)
			{
				st = ((int)(MultiID.baseM + z.V[n-i])).ToString();
				k = MultiID.muDMax + 1;
				if (i == n)
				{
					while (st[k-1] == '0')
						k--;
				}
				for (j = 2; j <= k; j++)
				{
					MultiID.Write(null, outF, st.Substring(j - 1, 1));
					did++;
					if (did % 250 == 0)
						NewLine(outF, ref line, ref page);	// new group of 5 lines
					if (did % 50 == 0)						// new line
						NewLine(outF, ref line, ref page);
					else if (did % 5 == 0)
					{
						MultiID.Write(null, outF, " ");
					}
				}
			}
			if (did % 50 != 0)
				NewLine(outF, ref line, ref page);
			dE = z.C;
			dE = MultiID.muDMax * (dE + n) - zL - 1;
			if (dE >= 0)
				ch = "+";
			else
				ch = "-";
			did++;
			MultiID.Write(null, outF, "E" + ch + st2 + " (" + did + ")");
			NewLine(outF, ref line, ref page);
			while (line != 0)
				NewLine(outF, ref line, ref page);
			// MultiID.Write(null, outF, ((char)26).ToString()); // EOF character (1A hex) not needed
		} // FormatN

		public static void RepUnitTN(MultiF x, MultiF y, int zI) // z = x -- y
		{
			int tI;
			Common.InitNAdd(1, "RepUnitTN-tI", out tI);
			Calc.SetToN(y, tI);
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			Calc.RepUnitN(tI);
			MulN(x, Common.nList[tI], zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
			Common.LDelete(tI);
		} // RepUnitTN

		public static void CompN(MultiF x, MultiF y, string op, string op2, int zI) // z = x op y
		{
			int sign;
			bool met;
			sign = MultiFD.CompFF(x, y);
			if (op2 == "<=")
				met = (sign <= 0);
			else if (op2 == "<>" || op2 == "!=")
				met = (sign != 0);
			else if (op2 == ">=")
				met = (sign >= 0);
			else if (op2 == "==")
				met = (sign == 0);
			else if (op == "<")
				met = (sign < 0);
			else if (op == "=")
				met = (sign == 0);
			else if (op == ">")
				met = (sign > 0);
			else
				met = false;	// should not get here
			Calc.ClearN(zI);
			if (met) Common.nList[zI].V[0] = 1;	// n is initially zero
		} // CompN

		public static void AndN(MultiF x, MultiF y, int zI) // z = x & y
		{
			if (!MultiFD.ZTestF(x) && !MultiFD.ZTestF(y))
			{
				Calc.ClearN(zI);
				Common.nList[zI].V[0] = 1;
			}
			else
				Calc.ClearN(zI);
		} // AndN

		public static void OrN(MultiF x, MultiF y, int zI) // z = x | y
		{
			if (!MultiFD.ZTestF(x) || !MultiFD.ZTestF(y))
			{
				Calc.ClearN(zI);
				Common.nList[zI].V[0] = 1;
			}
			else
				Calc.ClearN(zI);
		} // OrN

		public static void RMul1N(int d1, int zI) // z = z * d1
		// Multi-precision one or two super digit multiply d1 is not changed
		// muErr set true if z overflows
		{
			MultiF t = new MultiF(2);
			MultiFD.SetTo1F(d1, t);
			RMulN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void Mul1N(MultiF x, int d1, int zI) // z = x * d1
		// Multi-precision one or two super digit multiply x and d1 are not changed
		// muErr set true if z overflows
		{
			Calc.SetToN(x, zI);
			RMul1N(d1, zI);
		}

		public static void MulN(MultiF x, MultiF y, int zI) // z = x * y
		// x or y or both may have the same address in memory as z
		// x, y are not changed unless they share memory with z
		// sets muErr true if overflow
		{
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.MulF(x, y, t);
			Calc.SetToN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void RMulN(MultiF x, int zI) // z = z * x
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow
		{
			MulN(Common.nList[zI], x, zI);
		}

		public static void SqN(MultiF x, int zI) // z = x * x
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow
		{
			MulN(x, x, zI);
		}

		public static void RSqN(int zI) // z = z * z
		// sets muErr true if overflow
		{
			MulN(Common.nList[zI], Common.nList[zI], zI);
		}

		public static void Pow10IF(int yI, MultiF p)	// y = 10^p
		{
			decimal pI;
			bool signP = p.S;
			MultiFD.GetDeF(out pI, p);
			pI = Math.Abs(pI);
			decimal k = Decimal.Floor(pI / MultiID.muDMax);
			long rem = 0;
			if (k > Int64.MaxValue)
				k = (decimal)Int64.MaxValue + 1;
			else
				rem = (long)(pI - k * MultiID.muDMax);
			Common.nList[yI].N = 1;
			if (signP)
			{
				if (rem == 0)
					rem = 8;
				else
					k++;
				if (k > Int64.MaxValue)
				{
					MultiFD.ClearF(Common.nList[yI]);
					MultiID.MuWriteErr("Floating multiply underflow, continuing...");
					MultiID.muUnderflow = true;
					return;
				}
				Common.nList[yI].C = (long)(-k);
				Common.nList[yI].V[0] = (int)Math.Pow(10.0, (double)(8 - rem));
			}
			else
			{
				if (k > Int64.MaxValue)
				{
					MultiFD.ClearF(Common.nList[yI]);
					MultiID.MuWriteErr("Floating multiply overflow, continuing...");
					return;
				}
				Common.nList[yI].C = (long)k;
				Common.nList[yI].V[0] = (int)Math.Pow(10.0, (double)rem);
			}
		} // Pow10IF

		public static void PowIF(int yI, MultiF b, MultiF p,
			bool MFlag, int fMBI)
		// y = b^p % FMB (% only if MFlag is true), p an integer
		{
			int bL;
			int pL;
			int iL;
			bool fini;
			bool signP = p.S;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			if (MultiFD.ZTestF(b)) // if b = 0
			{
				Calc.ClearN(yI);
				if (signP)
					MultiID.MuWriteErr("Cannot raise zero to a negative power");
				else if (MultiFD.ZTestF(p)) // p = 0
					SetTo1N(1, yI); // can raise zero to the zero power, 0^0 = 1
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				return;
			}
			if ((b == 10 || b == -10) && !MFlag)
			{
				int lsbP = 0;
				if (p.C	== 0)
					lsbP = p.V[0] & 1;
				bool sign = (b.S && lsbP == 1);
				Pow10IF(yI, p);
				if (MultiID.muErr)
					Calc.ClearN(yI);
				else
					Common.nList[yI].S = sign;
			}
			else
			{
				fini = false;
				Common.InitNAdd(1, "PowIF-bL", out bL);
				Calc.SetToN(b, bL);
				Common.InitNAdd(1, "PowIF-pL", out pL);
				Calc.SetToN(p, pL);
				Common.nList[pL].S = false;
				SetTo1N(1, yI);
				do
				{
					if (Common.nList[pL].C <= 0)
						iL = (int)(Common.nList[pL].V[Common.nList[pL].C]);
					else
						iL = 0;
					if ((iL & 1) != 0) // Odd(iL)
						MulN(Common.nList[bL], Common.nList[yI], yI);
					if (MFlag) RModN(Common.nList[fMBI], yI);
					RDiv1N(2, pL);
					RIntN(pL);
					if (Common.ZTestN(pL))
						fini = true;
					else
					{
						MulN(Common.nList[bL], Common.nList[bL], bL);
						if (MFlag) RModN(Common.nList[fMBI], bL);
					}
					if (MultiID.muAbort)
					{
						Calc.ClearN(yI);
						fini = true;
					}
				}
				while (!fini);
				Common.LDelete(pL);
				Common.LDelete(bL);
				if (signP) RInvN(yI);
			}
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(yI);
		} // PowIF

		public static void PowMF(int yI, MultiF b, MultiF p,
			bool MFlag, int fMBI)
		// y = b^p % FMB (% only if MFlag is true)
		{
			int tI;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			// if b is zero or (both fMB and p are integers)
			if (MultiFD.ZTestF(b) || (!MFlag || (Common.nList[fMBI].C >= 0)) && (p.C >= 0))
			{
				PowIF(yI, b, p, MFlag, fMBI);
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Calc.NormN(yI);
				return;
			}		
			if (b.S && (p.C < 0)) // b < 0 and (p not an integer)
			{
				MultiID.MuWriteErr("Cannot raise negative number to a non-integer power");
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Calc.ClearN(yI);
				return;
			}
			Common.InitNAdd(1, "PowMF-tI", out tI);
			bool signY = b.S && p.C == 0 && (p.V[0] & 1) == 1; // if negative to odd integer
			bool signB = b.S;
			b.S = false;
			LnN(b, tI);
			b.S = signB;
			RMulN(p, tI);
			ExpN(Common.nList[tI], yI);
			Common.nList[yI].S = signY;
			if (MFlag) RModN(Common.nList[fMBI], yI);
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(yI);
		} // PowMF

		public static void PowN(MultiF b, MultiF p, int zI) // z = b^p
		{
			int fMBI = 0;
			PowMF(zI, b, p, false, fMBI);	// FMB not used
		}

		public static void PowNF(MultiF b, MultiF p, MultiF z) // z = b^p
		{
			int fMBI = 0;
			int zI;
			Common.InitNAdd(1, "PowNF-zI", out zI);
			PowMF(zI, b, p, false, fMBI);	// FMB not used
			MultiFD.SetToFS(Common.nList[zI], z);
			Common.LDelete(zI);
		}

		public static void RPowN(MultiF p, int zI) // z = z^p
		{
			PowN(Common.nList[zI], p, zI);
		}

		public static void PowMN(MultiF b, MultiF p, int zI) // z = b^p % FMB
		{
			int fMBI;
			if (!Common.FMBIok(zI, out fMBI))
				return;
			if (Common.ZTestN(fMBI))
				PowMF(zI, b, p, false, fMBI);
			else
				PowMF(zI, b, p, true, fMBI);
		}

		public static void RPowMN(MultiF p, int zI) // z = z^p % FMB
		{
			PowMN(Common.nList[zI], p, zI);
		}

		public static void RDiv1N(int d1, int zI) // z = z / d1
		// Multi-precision one or two super digit divide, d1 is not changed
		// allows literal d1, muErr set true if d1 = 0
		{
			MultiF t = new MultiF(2);
			MultiFD.SetTo1F(d1, t);
			RDivN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void Div1N(MultiF u, int d1, int zI) // z = u / d1
		// Multi-precision one or two super digit divide, d1 and u not changed
		// u and z may have same address in memory, then u is changed
		// allows literal d1, muErr set true if d1 = 0
		{
			Calc.SetToN(u, zI);
			RDiv1N(d1, zI);
		}

		public static void DivN(MultiF u, MultiF d, int zI) // z = u / d
		// u or d or both may have the same address in memory as z
		// u, d are not changed unless they share memory with z
		// sets muErr true if d = 0
		{
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.DivF(u, d, t);
			Calc.SetToN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void DivRemN(MultiF u, MultiF d, int zI) // z = Floor(u / d), Re = remainder
		// u or d or both may have the same address in memory as z
		// u, d are not changed unless they share memory with z
		// sets muErr true if d = 0
		{
			int rI;
			Common.LFindAdd("Re", out rI);
			if (rI == zI || rI == Common.iTop)
			{
				MultiID.MuWriteErr(
				"Cannot set same location to both quotient and remainder, continuing...");
				return;
			}
			MultiF t = new MultiF(Calc.fMC);
			MultiF r = new MultiF(Calc.fMC);
			MultiFD.DivF(u, d, t);
			if (MultiID.muErr)
			{
				Calc.SetToN(MultiFD.fZero, rI);
				Calc.SetToN(MultiFD.fZero, zI);
				return;
			}
			MultiFD.FloorF(t, t);
			r &= u - t*d;
			Calc.SetToN(t, zI);		// z = t = integer quotient
			Calc.SetToN(r, rI);		// Re = r = remainder
		} // DivRemN

		public static void DivIntN(MultiF u, MultiF d, int zI) // z = Floor(u / d)
		// u or d or both may have the same address in memory as z
		// u, d are not changed unless they share memory with z
		// sets muErr true if d = 0
		{
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.DivF(u, d, t);
			MultiFD.FloorF(t, t);
			Calc.SetToN(t, zI);		// z = t = integer quotient
			MultiFD.DoneF(t);
		} // DivIntN

		public static void RDivN(MultiF d, int zI) // z = z / d
		// d may have the same address in memory as z
		// d is not changed unless it shares memory with z
		// sets muErr true if d = 0
		{
			DivN(Common.nList[zI], d, zI);
		}

		public static void FacNF(MultiF x, int zI, bool MFlag, int fMBI) // z = x! = 1*2*3*...*x
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			MultiF xL = new MultiF(4);
			MultiFD.IntF(x, xL);		
			if (!MFlag)
			{
				int saveFMC = Calc.fMC;
				Calc.fMC = FacFMC(xL, false);
				LimFac(Calc.fMC);
				MultiF lim = new MultiF(4);
				lim &= limFac;
				if (xL > lim)
				{
					xL += 1;					// use the gamma function for large n
					GamNF(xL, zI);
				}
				else
					FacSNF(xL, zI, MFlag, fMBI);
 				MultiFD.DoneF(lim);
				Calc.fMC = saveFMC;
			}
			else
				FacSNF(xL, zI, MFlag, fMBI);
			MultiFD.DoneF(xL);
		} // FacNF

		public static void LimFac(int fMC) // Compute limFac,
		// limit to use factorial function vice gamma function
		{
			if (fMC <= 17)
				limFac = 2000;
			else
			{
				float x1, x2, y1, y2;
				if (fMC < 29)
				{
					x1 = 17f;
					x2 = 29f;
					y1 = 2000f;
					y2 = 3000f;
				}
				else if (fMC < 54)
				{
					x1 = 29f;
					x2 = 54f;
					y1 = 3000f;
					y2 = 5000f;
				}
				else if (fMC < 104)
				{
					x1 = 54f;
					x2 = 104f;
					y1 = 5000f;
					y2 = 34000f;
				}
				else if (fMC < 204)
				{
					x1 = 104f;
					x2 = 204f;
					y1 = 34000f;
					y2 = 75000f;
				}
				else if (fMC < 404)
				{
					x1 = 204f;
					x2 = 404f;
					y1 = 75000f;
					y2 = 110000f;
				}
				else if (fMC < 804)
				{
					x1 = 404f;
					x2 = 804f;
					y1 = 110000f;
					y2 = 200000f;
				}
				else
				{
					x1 = 804f;
					x2 = 1254f;
					y1 = 200000f;
					y2 = 385000f;
				}
				double d = Math.Round(Math.Exp((fMC - x1) * (Math.Log(y2) - Math.Log(y1)) / (x2 - x1) + Math.Log(y1)));
				if (d > 4.059978300001705888E+18)
					limFac = 4059978300001705888;
				else
					limFac = (long)d;
			}
			MultiID.ADiag("LimFac: LimFac = " + limFac);
		} // LimFac

		public static int FacFMC(MultiF x, bool MFlag) // compute fMC needed for Fac(x)
		{
			int fMC;
			if (x - 14 < MultiFD.fZero)
				fMC = 2;
			else
			{
				fMC = Calc.fMC;
				int tI;
				Common.InitNAdd(5, "FacFMC-tI", out tI);
				Calc.fMC = 4;
				LogN(x + 1, tI);
				RMulN(x, tI);
				RDiv1N(MultiID.muDMax, tI);
				double db;
				MultiFD.GetDbF(out db, Common.nList[tI]);
				Calc.fMC = fMC;
				if (db < fMC || MFlag)
					fMC = (int)db + 1;
				if (fMC < 2)
					fMC = 2;
				Common.LDelete(tI);
			}
			MultiID.ADiag("FacFMC: FMC = " + fMC);
			return fMC;
		} // FacFMC

		public static int BinoFMC(MultiF x, MultiF y) // compute fMC needed for Bino(x, y)
		// y is assumed an integer and x >= y >= 0
		{
			int fMC = Calc.fMC;
			if (x.C >= 0 && x - 14 < MultiFD.fZero) // if x and y are both small integers
				fMC = 2;
			else
			{
				MultiF xL = new MultiF(x.N);
				xL &= x;
				if (xL.C < 0)
				{
					if (xL < MultiFD.fOne)
						xL += 1;
					xL.C = 0;
				}
				int tI;
				Common.InitNAdd(5, "BinoFMC-tI", out tI);
				Calc.fMC = 4;
				LogN(xL + 1, tI);
				RMulN(y, tI);
				RDiv1N(MultiID.muDMax, tI);
				double db;
				MultiFD.GetDbF(out db, Common.nList[tI]);
				Calc.fMC = fMC;
				if (db < fMC)
					fMC = (int)db + 1;
				if (fMC < 2)
					fMC = 2;
				Common.LDelete(tI);
			}			
			MultiID.ADiag("BinoFMC: FMC = " + fMC);
			return fMC;
		} // BinoFMC

		public static void FacSNF(MultiF x, int zI, bool MFlag, int fMBI) // z = x! = 1*2*3*...*x
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			MultiF xL = new MultiF(4);
			MultiF lim = new MultiF(4);
			MultiFD.IntF(x, xL);
			MultiF nL = new MultiF(4);
			nL &= 2;
			SetTo1N(1, zI);
			MultiID.nDone = 0;
			MultiID.nToGo = Int64.MaxValue;
			lim &= Int64.MaxValue;
			while (nL <= xL)
			{
				if (MFlag)
				{
					int newN = nL.N + Common.nList[zI].N + (int)nL.C + (int)Common.nList[zI].C;
					if (Calc.fMC < newN)
						Calc.fMC = newN;
				}
				MulN(nL, Common.nList[zI], zI);	// 99% of processor time of FacF is here
				if (MFlag)
				{
					ModN(Common.nList[zI], Common.nList[fMBI], zI);
					if (MultiFD.ZTestF(Common.nList[zI])) break;
				}
				if (MultiID.muAbort)
				{
					MultiID.WriteLn("Factorial function aborted by operator, " + MultiID.nToGo +
						" multiplies to go");
					Calc.ClearN(zI);
					break;
				}
				nL += 1;
				MultiID.nDone++;
				if (MultiID.statReq2)
				{
					if (xL - nL < lim)
					{
						long n;
						MultiFD.GetLgF(out n, xL - nL);
						MultiID.nToGo = n + 1;
					}
					MultiID.WritenDone("FacSNF:");
				}
			}
			MultiID.nToGo = 0;
			MultiFD.DoneF(nL);
			MultiFD.DoneF(lim);
			MultiFD.DoneF(xL);
		} // FacSNF

		public static MultiF PMr(MultiF a, MultiF b, int fMBI)
		// computes a!/b! % fMB by binary splitting (a >= b >= 0 and integers)
		{
			if (MultiID.muErr || MultiID.muAbort) return MultiFD.fZero;
			MultiF dF = new MultiF(a.N + (int)a.C);
			dF &= a - b;
			long d;
			MultiFD.GetLgF(out d, dF);
			MultiFD.DoneF(dF);
			MultiF p = new MultiF(1);
			MultiF p1 = new MultiF(1);
			MultiF p2 = new MultiF(1);
			if (d > 3)
			{
				MultiF m = new MultiF(a.N + (int)a.C + 1);
				m &= (a + b) / 2;
				MultiFD.IntF(m, m);
				if (fMBI != 0)
				{
					SetToNM(PMr(a, m, fMBI), p1);
					SetToNM(PMr(m, b, fMBI), p2);
					int newN = p1.N + p2.N + (int)p1.C + (int)p2.C;
					if (Calc.fMC < newN)
						Calc.fMC = newN;
					SetToNM(p1 * p2, p);
				}
				else
					SetToNM(PMr(a, m, fMBI) * PMr(m, b, fMBI), p);
				if (fMBI != 0) Mod(p, Common.nList[fMBI], p);
				if (MultiID.currK > (MultiID.maxK - d)) // currK += d could overflow
					MultiID.currK = MultiID.maxK;
				else
					MultiID.currK += d;
			}
			else
			{
				p &= a;
				if (fMBI != 0) Mod(p, Common.nList[fMBI], p);
				if (MultiID.currK > (MultiID.maxK - 1)) MultiID.currK = MultiID.maxK;
				else MultiID.currK += 1;
				if (d > 1)
				{
					if (fMBI != 0)
					{
						int newN = p.N + a.N + (int)p.C + (int)a.C;
						if (Calc.fMC < newN)
							Calc.fMC = newN;
					}
					p *= (a-1);
					if (fMBI != 0) Mod(p, Common.nList[fMBI], p);
					if (MultiID.currK > (MultiID.maxK - 3)) MultiID.currK = MultiID.maxK;
					else MultiID.currK += 3;
				}
				if (d > 2)
				{
					if (fMBI != 0)
					{
						int newN = p.N + a.N + (int)p.C + (int)a.C;
						if (Calc.fMC < newN)
							Calc.fMC = newN;
					}
					p *= (a - 2);
					if (fMBI != 0) Mod(p, Common.nList[fMBI], p);
					if (MultiID.currK > (MultiID.maxK - 4)) MultiID.currK = MultiID.maxK;
					else MultiID.currK += 4;
				}
			}
			MultiID.ReportPct();
			return p;
		} // PMr % fMB

		public static void Fac2N(MultiF a, MultiF b, int zI, bool MFlag)
		// z = Fac(a) / Fac(b) % fMB by binary splitting
		// a or b or both may have the same address in memory as z
		// a, b are not changed unless they share memory with z
		// sets muErr true if overflow or error
		{
			int fMBI = 0;
			if (MFlag && !Common.FMBIok(zI, out fMBI))
				return;
			if (MFlag) MFlag = !Common.ZTestN(fMBI);
			if (!MFlag) fMBI = 0;
			if (MFlag && Common.nList[fMBI].C < 0)
			{
				MultiID.MuWriteErr("Cannot compute FacM2(X, Y) if FMB is not an integer");
				Calc.ClearN(zI);
				return;
			}
			MultiID.timeAtStart = MultiID.TimerF();
			MultiF aL = new MultiF(a.N);
			MultiF bL = new MultiF(b.N);
			MultiFD.IntF(a, aL);
			MultiFD.IntF(b, bL);
			if (aL.S || bL.S)
			{
				MultiID.MuWriteErr("Cannot take factorial of number < zero");
				Calc.ClearN(zI);
				MultiFD.DoneF(bL);
				MultiFD.DoneF(aL);
				return;
			}
			if (aL == bL)
			{
				Calc.SetToN(MultiFD.fOne, zI);
				MultiFD.DoneF(bL);
				MultiFD.DoneF(aL);
				return;
			}
			if (aL < bL)
			{
				if (aL == 0 && bL == 1)
					Calc.SetToN(MultiFD.fOne, zI);
				else
					Calc.ClearN(zI);
				MultiFD.DoneF(bL);
				MultiFD.DoneF(aL);
				return;
			}
			MultiF dF = new MultiF(aL.N + (int)aL.C);
			dF &= aL - bL;
			MultiF lim = new MultiF(4);
			int j;
			MultiFD.ValueF10("4.05997,83000,01705,888E+18", out j, lim);
			if (dF > lim)
			{
				MultiID.MuWriteErr("Number too large for factorial function");
				MultiFD.DoneF(dF);
				MultiFD.DoneF(bL);
				MultiFD.DoneF(aL);
				Calc.ClearN(zI);
				return;
			}
			MultiFD.DoneF(dF);
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			MultiF nnF = new MultiF(aL.N + (int)aL.C);
			nnF &= aL - bL;
			Expand();
			if (MFlag)
			{
				int c = (int)Common.nList[fMBI].C;
				if (c < 0) c = 0;
				if (Calc.fMC <= Common.nList[fMBI].N + c)
					Calc.fMC = Common.nList[fMBI].N + c + 1;
			}
			double nn;
			MultiFD.GetDbF(out nn, nnF);
			double maxD = Math.Round(nn * (Math.Log(nn, 2) + 1));
			if (maxD > Int64.MaxValue) maxD = Int64.MaxValue;
			MultiID.maxK = (long)maxD;
			MultiID.currK = 0;
			MultiID.oldPct = 0.0;
			MultiF zF = new MultiF(Calc.fMC);
			zF &= PMr(aL, bL, fMBI);			// z = Fac(Int(a)) / Fac(Int(b)) (% fMB if fMBI != 0)
			MultiID.currK = MultiID.maxK;
			if(MultiID.oldPct < 100.0) MultiID.ReportPct();
			Calc.SetToN(zF, zI);
			MultiFD.DoneF(bL);
			MultiFD.DoneF(aL);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // Fac2N

		public static void FibNFF(MultiF x, int zI) // z = Fibonacci number of x != Int(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			// Fib(x) = (Phi^x - cos(x*Pi)/Phi^x) / Sqrt(5)
			MultiF a = new MultiF(Calc.fMC);
			MultiF b = new MultiF(Calc.fMC);
			MultiF c = new MultiF(Calc.fMC);
			int tI;
			Common.InitNAdd(1, "FibNFF-tI", out tI);// t = temp on the list
			a &= 5;									// a = 5
			MultiFD.RSqRtF(a);						// a = SqRt(5)
			b &= a;									// b = SqRt(5)
			b += 1;									// b = 1 + SqRt(5)
			MultiFD.MulF(b, MultiFD.fHalf, b);		// b = (1 + SqRt(5)) / 2
			PowNF(b, x, c);							// c = b ^ x
			bool degModeSave = Calc.degMode;
			Calc.degMode = true;
			CosN(x * 180, tI);						// t = cos(x*Pi)
			Calc.degMode = degModeSave;
			Calc.SetToN((c - Common.nList[tI] / c) / a, zI);
			Common.LDelete(tI);
			MultiFD.DoneF(c);
			MultiFD.DoneF(b);
			MultiFD.DoneF(a);
		} // FibNFF

		public static void FibNF(MultiF x, int zI) // z = Fibonacci number of Int(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			int saveFMC, i;
			saveFMC = Calc.fMC;
			MultiF a = new MultiF(Calc.fMC);
			MultiF b = new MultiF(Calc.fMC);
			MultiF n = new MultiF(4);
			int tI;
			Common.InitNAdd(1, "FibNF-tI", out tI);	// t = temp on the list
			MultiFD.IntF(x, n);						// n = Int(x)
			MultiFD.ValueF10("0.209", out i, a);	// a = 0.209 = decimal digits / n
			MultiFD.MulF(a, n, a);					// a = number of decimal digits in Fib(n)
			MultiFD.Div1F(a, MultiID.muDMax, a);	// a in super digits
			double db;
			MultiFD.GetDbInF(out db, 0, MultiID.muNMax, a);
			Calc.fMC = (int)db + 4;					// fMC in super digits, + for safety
			if (Calc.fMC > saveFMC)
				Calc.fMC = saveFMC;
			n.S = false;							// n = abs(Int(x))
			a &= 5;									// a = 5
			MultiFD.RSqRtF(a);						// a = SqRt(5)
			b &= a;									// b = SqRt(5)
			b += 1;									// b = 1 + SqRt(5)
			MultiFD.MulF(b, MultiFD.fHalf, b);		// b = (1 + SqRt(5)) / 2
			PowN(b, n, tI);							// t = b ^ n
			MultiFD.DivF(Common.nList[tI], a, b);	// b = b^n / a)
			b += MultiFD.fHalf;						// b = 0.5 + b^n / a
			MultiFD.IntF(b, a);						// a = Int(b)
			if (x.S && (n.C != 0 || (x.V[0] & 1) == 0)) // if negative even integer
			{
				a.S = true;							// fib(n) is negative
			}
			Calc.SetToN(a, zI);
			Common.LDelete(tI);
			MultiFD.DoneF(n);
			MultiFD.DoneF(b);
			MultiFD.DoneF(a);
			Calc.fMC = saveFMC;
			Calc.NormN(zI);
		} // FibNF

		public static void FacN(MultiF x, int zI, bool MFlag, int fMBI)
		// z = x! = 1*2*3*...*x % FMB
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			if (MFlag && !Common.FMBIok(zI, out fMBI))
				return;
			if (MFlag) MFlag = !Common.ZTestN(fMBI);
			if (!MFlag) fMBI = 0;
			MultiF xL = new MultiF(4);
			MultiFD.IntF(x, xL);
			if (xL.S)
			{
				MultiID.MuWriteErr("Cannot take factorial of number < zero");
				Calc.ClearN(zI);
				MultiFD.DoneF(xL);
				return;
			}
			MultiF lim = new MultiF(4);
			int j;
			MultiFD.ValueF10("4.05997,83000,01705,888E+18", out j, lim);
			if (xL > lim)
			{
				MultiID.MuWriteErr("Number too large for factorial function");
				Calc.ClearN(zI);
				MultiFD.DoneF(lim);
				MultiFD.DoneF(xL);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			if (MFlag)
			{
				int c = (int)Common.nList[fMBI].C;
				if (c < 0) c = 0;
				if (Calc.fMC <= Common.nList[fMBI].N + c)
					Calc.fMC = Common.nList[fMBI].N + c + 1;
			}			
			FacNF(x, zI, MFlag, fMBI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // FacN

		public static void FacSN(MultiF x, int zI, bool MFlag, int fMBI)
		// z = x! = 1*2*3*...*x % FMB
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			if (MFlag && !Common.FMBIok(zI, out fMBI))
				return;
			if (MFlag) MFlag = !Common.ZTestN(fMBI);
			if (!MFlag) fMBI = 0;
			MultiF xL = new MultiF(4);
			MultiFD.IntF(x, xL);
			if (xL.S)
			{
				MultiID.MuWriteErr("Cannot take factorial of number < zero");
				Calc.ClearN(zI);
				MultiFD.DoneF(xL);
				return;
			}
			MultiF lim = new MultiF(4);
			int j;
			MultiFD.ValueF10("4.05997,83000,01705,888E+18", out j, lim);
			if (xL > lim)
			{
				MultiID.MuWriteErr("Number too large for factorial function");
				Calc.ClearN(zI);
				MultiFD.DoneF(lim);
				MultiFD.DoneF(xL);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			if (MFlag)
			{
				int c = (int)Common.nList[fMBI].C;
				if (c < 0) c = 0;
				if (Calc.fMC <= Common.nList[fMBI].N + c)
					Calc.fMC = Common.nList[fMBI].N + c + 1;
			}
			FacSNF(x, zI, MFlag, fMBI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // FacSN

		public static void AFibN(MultiF x, int zI)
		// z = Arc Fibonacci number Abs(x), always an integer, accurate if x is a Fibonacci number
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			if (x > MultiFD.fMOne && x < MultiFD.fOne)
			{
				Calc.ClearN(zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF a = new MultiF(Calc.fMC);
			MultiF b = new MultiF(Calc.fMC);
			int tI, t2I;
			Common.InitNAdd(1, "AFibN-tI", out tI);	// t and t2 = temp on the list
			Common.InitNAdd(1, "AFibN-t2I", out t2I);
			a &= 5;									// a = 5
			MultiFD.RSqRtF(a);						// a = SqRt(5)
			b &= a;									// b = SqRt(5)
			b += 1;									// b = 1 + SqRt(5)
			MultiFD.MulF(b, MultiFD.fHalf, b);		// b = (1 + SqRt(5)) / 2
			a *= x;
			a.S = false;							// a = SqRt(5) * |x|
			Calc.SetToN(a, tI);
			RLnN(tI);
			Calc.SetToN(b, t2I);
			RLnN(t2I);
			RDivN(Common.nList[t2I], tI);
			RRoundN(tI);
			Calc.SetToN(Common.nList[tI], zI);
			Common.LDelete(t2I);
			Common.LDelete(tI);
			MultiFD.DoneF(b);
			MultiFD.DoneF(a);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // AFibN

		public static void FibN(MultiF x, int zI)
		// z = Fibonacci number of x
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int tI;
			Common.InitNAdd(1, "FibN-tI", out tI);	// t = temp on the list
			if (x.C >= 0)							// if x is an integer
				FibNF(x, tI);
			else
				FibNFF(x, tI);
			Calc.SetToN(Common.nList[tI], zI);
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // FibN

		public static void RIsFibN(int zI)
		// z = 1 (True) if |z| is a Fibonacci number, else 0 (False)
		// True iff z is an integer and the closed interval
		// [Phi*z - 1/z, Phi*z + 1/z] contains a positive integer
		{
			if (Common.nList[zI].C < 0)		// if z is not integer
				Calc.SetToN(MultiFD.fZero, zI);
			else if (Common.nList[zI] == MultiFD.fZero)		// if z == 0
				Calc.SetToN(MultiFD.fOne, zI);
			else
			{
				int p = Common.nList[zI].N + (int)Common.nList[zI].C;
				bool saveExp = MultiFD.expanded;
				int saveFMC = Calc.fMC;
				int saveMMax = MultiID.mMax;
				Expand();
				ExpandMore(p + 1);
				int max = 2 * p + 1 + MultiFD.fGuard;
				if (Calc.fMC > max)
					Calc.fMC = max;
				MultiF a = new MultiF(Calc.fMC);		// a = Phi = (1 + sqrt(5)) / 2 = 1.61803,39887...
				MultiFD.SqRootF(MultiFD.fFive, a);
				MultiFD.RAddF(MultiFD.fOne, a);
				MultiFD.RDivF(MultiFD.fTwo, a);
				MultiF b = new MultiF(Calc.fMC);
				MultiF c = new MultiF(Calc.fMC);
				MultiF d = new MultiF(Calc.fMC);
				MultiF t = new MultiF(1);
				bool sign = Common.nList[zI].S;
				Common.nList[zI].S = false;
				a *= Common.nList[zI];					// a = Phi * z
				b &= MultiFD.fOne / Common.nList[zI];	// b = 1 / z
				c &= a - b;
				if (c.C >= 0)	// if an integer
					MultiFD.SetToF(MultiFD.fOne, t);
				else
				{
					d &= a + b;
					MultiFD.RIntF(c);
					MultiFD.RIntF(d);
					if (c == d)
						MultiFD.SetToF(MultiFD.fZero, t);
					else
						MultiFD.SetToF(MultiFD.fOne, t);
				}
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				if (sign && t == MultiFD.fOne)
				{
					RAFibN(zI);
					if (Common.nList[zI].C == 0 && (Common.nList[zI].V[0] & 1) == 1)
						t &= MultiFD.fZero;
				}
				Calc.SetToN(t, zI);
				Calc.NormN(zI);
			}
		} // RIsFibN

		public static void RIsFib2N(int zI)
		// z = 1 (True) if |z| is a Fibonacci number, else 0 (False)
		// True iff z is an integer and 5z^2+4 or 5z^2-4 is a perfect square
		{
			bool sign = Common.nList[zI].S;
			Common.nList[zI].S = false;
			if (Common.nList[zI].C < 0)		// if z is not integer
				Calc.SetToN(MultiFD.fZero, zI);
			else if (Common.nList[zI] == MultiFD.fZero)		// if z == 0
				Calc.SetToN(MultiFD.fOne, zI);
			else if (Common.nList[zI] == MultiFD.fOne)		// if z == +/-1
				Calc.SetToN(MultiFD.fOne, zI);
			else
			{
				int p = Common.nList[zI].N + (int)Common.nList[zI].C;
				bool saveExp = MultiFD.expanded;
				int saveFMC = Calc.fMC;
				int saveMMax = MultiID.mMax;
				Expand();
				ExpandMore(p + 1);
				int max = 2 * p + 1  + MultiFD.fGuard;
				if (Calc.fMC > max)
					Calc.fMC = max;
				MultiF a = new MultiF(Calc.fMC);
				MultiF b = new MultiF(Calc.fMC);
				MultiF t = new MultiF(1);
				t &= MultiFD.fZero;
				a &= MultiFD.fFive * Common.nList[zI] * Common.nList[zI];	// a = 5*z*z
				MultiFD.SqRootF(a, b);
				MultiFD.RAddF(MultiFD.fHalf, b);	// round b to an integer
				MultiFD.FloorF(b, b);
				b &= a - b * b;
				if (MultiID.debug)
				{
					int tot = 0;
					MultiID.WriteT("IsFib2: Re = ", ref tot);
					MultiFD.WriteFLn(b, ref tot);
				}
				b.S = false;
				if (b == MultiFD.fFour)
					t &= MultiFD.fOne;
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				if (sign && t == MultiFD.fOne)
				{
					RAFibN(zI);
					if (Common.nList[zI].C == 0 && (Common.nList[zI].V[0] & 1) == 1)	// if odd
						t &= MultiFD.fZero;
				}
				Calc.SetToN(t, zI);
				Calc.NormN(zI);
			}
		} // RIsFib2N

		//	-------------------------------
		//	Lerch's transcendent Phi(x,s,a)
		//	-------------------------------
		//
		//	This program is copyright by
		//
		//	Sergej V. Aksenov (http://aksenov.freeshell.org/) and
		//	Ulrich D. Jentschura (jentschura@physik.tu-dresden.de), 2002.
		//
		//	Version 1.00 (May 1, 2002)
		//
		//	Calling sequence:
		//
		//	public int LerchPhi(double x, double s, double a, double acc,
		//				out double result, out int iter)
		//
		//	calculates Lerch's Phi transcendent Phi(x,s,a) with result to a specified
		//	accuracy acc after iter iterations. Double precision is used throughout the
		//	calculation.
		//	The program uses direct summation of the defining series for |x| <= 0.5
		//	and CNCT for 0.5 < |x| < 1.0.
		//	The integer return code has to be interpreted as follows.
		//
		//	-------------
		//	Return codes:
		//	-------------
		//
		//	0 - Normal termination.
		//	1 - Lerch Phi diverges for 1 <= |x|.
		//	2 - Lerch Phi is not defined for integer a <= 0.
		//	3 - pow() is not defined for a < 0 and s not integer.
		//	4 - Long integer overflow in Aj().
		//	5 - Underflow in remainder estimate omega in LerchPhi().
		//	6 - No convergence within the maximum number of iterations.
		//
		//	Implementation note:
		//
		//	In subroutine Aj(), defining variables ind and two2k as type double
		//	instead of long int might eliminate overflow error which occurs for
		//	high indices (error code 4).
		//
		//	#define macheps DBL_EPSILON
		//	#define machmin DBL_MIN
		//
		//	If preprocessor macro ADD_UNDERSCORE was defined, add underscore to
		//	the function name --- needed for linking to Fortran programs on a Sun.
		//
		//	#if (ADD_UNDERSCORE)
		//	#define LerchPhi LerchPhi
		//	#endif
		//
		//	Note: CNCT stands for COMBINED NONLINEAR–CONDENSATION TRANSFORMATION

		private static int Aj(double x, double s, double a, int j, double acc, out double result, string name)
		// Function that computes van Wijngaarden's A_j for a given j. // used by LerchT1 and Lerch1
		{
			double bjk, z2ind, ind;
			double sum = 0.0;
			double two2k = 0.5;
			double jp = j + 1.0;
			int flag = 0;
			for (int k = 0; true; k++)	// Sum b^j_k's over k.
			{
				two2k *= 2.0;			// Index for the term of the original series.
				ind = two2k * jp - 1.0;
				// If long integer overflow occurs, variables become zero.
				// Not relevant in v1.0 because two2k and ind are double type.				
				if (two2k == Double.PositiveInfinity || ind == Double.PositiveInfinity)
				{
					flag = 4;
					break;
				}
				z2ind = Math.Pow(x, ind);	// Increment the sum.
				bjk = two2k * z2ind / Math.Pow(a + ind, s);
				sum += bjk;
				// Stop summation if either sum is zero or |term/sum| is below requested accuracy.
				if (Math.Abs(sum) <= machmin || Math.Abs(bjk/sum) < 1.0e-2 * acc)
					break;
			}
			result = sum;
			return flag;
		} // Aj

		public static int LerchPhi(double x, double s, double a, double acc, out double result, bool T, out int iter)
		// Function that computes approximation to Lerch Phi as a	// z = result = Lerch transcendent capitol Phi(x, s, a)
		// converging sequence of CNC transforms S^n_k.
		// The LerchPhiT(x, s, a) function is Psi(x, s, a) = Sum {k = 0, 1, ...} of [x^k / (a + k)^s],
		// where |x| <= 1 and a != 0, -1, -2, ... .
		// LerchPhi(x, s, a) = sum[k=0, infinity]{x^k / |a + k|^s}, terms with a + k == 0 are ignored, |x| <= 1
		{
			const int beta = 1, n = 0, imax = 3000;
			int j, m = 0;
			int i, sign, flag, imin = 0;
			double aL, sn, eps0, eps, skn, skn0, omega = 0,
				factor, factor1, y, est = 0, iom, sum1 = 0,
				cacc, best = double.MaxValue, minEps = double.MaxValue;
			double[] num, den, StoreAj;
			aL = a;		// Local copy of a.
			// Special cases.
			// 1 < x. (Return error, Lerch Phi diverges.)
			if (1.0 < x)			// was <= but LerchPhi(1, 2, 1) = Dilog(1) = Pi^2 / 6
			{
				result = 1.0;
				iter = 0;
				return 1;
			}
			// a <= 0 is integer. (Return error, Lerch Phi is not defined.)
			if (Math.Abs(Math.Floor(a) - a) <= macheps*Math.Abs(a) && a <= 0.0)
			{
				result = 1.0;	// Lerch1 doesn’t get this far
				iter = 0;
				return 2;
			}
			// a < 0 is not integer or zero and x != 0 (x == 0 considered below) ...
			string name = "Lerch1";
			if (T) name = "LerchT1";
			if (a < 0.0 && Math.Abs(x) > machmin)
			{
				if (T)
				{
					// s is not an integer. (Return error because pow() is not defined.)
					if (Math.Abs(Math.Floor(s) - s) > macheps*Math.Abs(s))
					{
						result = 1.0;
						iter = 0;
						return 3;
					}
					else		// s is an integer. (Transform a to positive).
					{
						m = (int)-Math.Floor(a);
						aL += m;
						sum1 = 0.0;
						for (i = 0; i < m; i++)
						{
							sum1 += Math.Pow(x,i)/Math.Pow(a+i,s);
						}
					}
				}
				else
				{
					m = (int)-Math.Floor(a);			// never returns flag = 3
					aL += m;
					sum1 = 0.0;
					for (i = 0; i < m; i++)
					{
						sum1 += Math.Pow(x,i)/Math.Pow(Math.Abs(a+i),s);
					}
				}
			}
			if (Math.Abs(x) <= machmin)		// x = 0 and ...
			{
				if (T)
				{
					if (a < 0)					// ... a < 0 is not integer or zero and ...
					{
						// s is not an integer. (Return error because pow() is not defined.)
						if (Math.Abs(Math.Floor(s) - s) > macheps*Math.Abs(s))
						{
							result = 1.0;
							iter = 0;
							return 3;
						}
						else					// s is an integer. (Return first term of series.)
						{
							if (((int)s & 1) == 0) sign = 1;
							else sign = -1;
							result = sign * 1.0 / Math.Pow(Math.Abs(a), s);
						}
					}
					else						// ... a > 0. (Return first term of series.)
					{
						result = 1.0 / Math.Pow(a, s);
						iter = 1;
						return 0;
					}
				}
				else
				{
					if (Math.Abs(x) <= machmin)				// x = 0 so return first term of series
					{
						result = 1.0 / Math.Pow(Math.Abs(a), s);	// 0^0 = 1.0
						iter = 1;
						return 0;
					}
				}
			}
			//	General case.
			//
			//	Some initializations.
			//
			//	sn denotes current partial sum of defining series:
			//		x > 0.5: sn is partial sum S_n of the van
			//	Wijngaarden transformed series.
			//	x <= 0.5: sn is the partial sum of the
			//	power series defining LerchPhi.
			//	skn0 and skn denote successive partial sums S^k_n
			//	that are same as sn in case of direct summation and
			//	delta-transformed in case of CNCT.
			//	eps0 and eps denote successive differences between
			//	partial sums S^k_n.
			//	omega is next term of a partial sum (of defining power series
			//	for direct summation, of van Wijngaarden transformed series
			//	for CNCT) and also becomes a remainder estimate in the delta
			//	transformation in CNCT).
			//
			//	For x <= 0.5 van Wijngaarden transformation is not used
			//	[hence no calls to Aj()].
			eps0 = skn = skn0 = sn = 0.0;
			if (x <= 0.5)						// Direct summation and CNCT (x < -0.5) case.
				omega = 1.0 / Math.Pow(aL, s);
			else								// CNCT (x > 0.5) case.
			{
				flag = Aj(x, s, aL, 0, acc, out omega, name);
				if (flag != 0)
				{
					result = 1.0;
					iter = 0;
					return flag;
				}
			}
			num = new double[imax];						// Allocate memory for working arrays.
			den = new double[imax];		
			if (x > 0.5) StoreAj = new double[imax];	// StoreAj is used only in CNCT
			else StoreAj = new double[0];
			flag = 0;
			sign = -1;
			for (i = 0; true; i++)	// Main loop: iterations for S^k_n.
			{						// i points to current iterate.
				sign = -sign;		// Increment the sum.
				sn += omega;		// Next term: omega.
				if (x <= 0.5)		// "Direct summation".
					omega = x * Math.Pow((aL+i)/(aL+i+1), s) * omega;
				else				// CNCT (x > 0.5) case.
				{
					StoreAj[i] = sign * omega;
					if ((i & 1) == 0)
					{
						// Recurrence for even pointer i.
						omega = -sign * 0.5 * (StoreAj[i/2] - Math.Pow(x, i/2) /
							Math.Pow(aL+i/2, s));
					}
					else
					{
						flag = Aj(x, s, aL, i+1, acc, out omega, name);
						if (flag != 0) break;
						else  omega = -sign * omega;
					}
				}
				if (Math.Abs(x) <= 0.5)		// Direct summation case: store current sum and remainder estimate.
				{
					skn = sn;
					est = 2.0 * Math.Pow(Math.Abs(x), (i+1)) / Math.Pow(aL+i+1, s);
				}
				else	// CNCT case.
				{
					if (Math.Abs(omega) <= machmin)		// Make sure omega is representable machine number.
					{
						flag = 5;
						break;
					}
					else
						iom = 1.0 / omega;
					num[i] = sn * iom;		// Last terms in sums of numerator and denominator of i-th partial sum.
					den[i] = iom;
					if (i > 0)				// Recurrence computation of numerator and denominator of a S_k^n.
					{
						num[i-1] = num[i] - num[i-1];	// factor = 1.0
						den[i-1] = den[i] - den[i-1];
					}
					factor1 = (double)(beta+n+i-1) * (beta+n+i-2);
					for(j = 2; j <= i; j++)
					{
						factor = factor1 / (beta+n+i+j-2) / (beta+n+i+j-3);
						num[i-j] = num[i-j+1] - factor * num[i-j];
						den[i-j] = den[i-j+1] - factor * den[i-j];
					}
					if (den[0] == Double.PositiveInfinity ||	// Current approximation of the sum S_k^n.
						den[0] == Double.NegativeInfinity)
					{
						flag = 7;
						break;
					}
					skn = num[0] / den[0];
				} // else CNCT case.
				eps = Math.Abs(skn - skn0);
				// Check the three termination criteria.
				if (i > 0 && eps < eps0)	// |est/skn| is less than the requested accuracy (est is a remainder estimate).
				{
					if (Math.Abs(x) > 0.5)
					{
						y = eps/eps0;
						est = 2.0/y/(1.0-y)*eps;	// left to right
					}
					cacc = Math.Abs(est/skn);
					if (cacc < acc) break;
				}
				if (eps <= 0.0) break;		// Successive iterates skn are the same.
				if (eps < minEps)					// save best answer so far
				{
					imin = i + 1;
					minEps = eps;
					best = skn;
				}
				if (i > imax-2 || i >= imin + 99)	// Maximum number of iterations is exceeded.
				{
					flag = 6;
					MultiID.WriteLn(name + ": No convergence within the maximum number of iterations = " + imax);
					MultiID.WriteLn(name + ": Results at iteration " + imin + " had a relative error approximately =");
					MultiID.WriteLn((minEps / best).ToString());
					skn = best;					// result = best;
					break;
				}
				skn0 = skn;					// Go on to the next iteration.
				eps0 = eps;
			} // for
			if (a < 0)						// Store the resulting sum.
				result = sum1 + skn * Math.Pow(x,m);
			else
				result = skn;
			iter = i + 1;					// Store the number of iterations.
			return flag;
		} // LerchPhi

		public static void Lerch1(MultiF x, MultiF s, MultiF a, bool T, int zI) // z = Lerch transcendent capitol Phi(x, s, a)
		// x, s, a may have the same address in memory as z				// to 16 decimal digits and traditional
		// x, s, a are not changed unless it shares memory with x
		// sets muErr true if overflow or error
		//
		// The LerchT1(x, s, a) function is Psi(x, s, a) = Sum {k = 0, 1, ...} of x^k / (a + k)^s,
		// where |x| <= 1 and a != 0, -1, -2, ... .
		// Lerch1(x, s, a) = sum[k=0, infinity]{x^k / |a + k|^s}, terms with a + k == 0 are ignored, |x| <= 1
		{
			double xd, sd, ad, acc, result, n = 0.0;
			int iter, flag, flagL = 0;
			MultiFD.GetDbA(out xd, x);
			MultiFD.GetDbA(out sd, s);
			MultiFD.GetDbA(out ad, a);
			acc = 0.0000000000000001;
			bool reduced = false;
			string name = "LerchT1";
			if (!T)
			{
				name = "Lerch1";
				if (Math.Abs(Math.Floor(ad) - ad) <= macheps*Math.Abs(ad) && ad <= 0.0)
				{
					flagL = 2;
					n = -ad;
					ad = 1.0;
				}
			}
			do
			{
				flag = LerchPhi(xd, sd, ad, acc, out result, T, out iter);
				if (flag != 7) break;
				acc *= 10;
				reduced = true;
			}
			while (acc < 0.015);
			if (!T && flag == 0 && flagL == 2)
			{
				MultiID.WriteLn(name + ": Integer a <= 0, one infinite term ignored");
				result *= Math.Pow(xd, n+1);
				for (double k = 0; k < n; k++)
				{
					result += Math.Pow(xd, k) / Math.Pow(n - k, sd);
				}
			}
			MultiF zF = new MultiF(4);
			int i;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiFD.ValueF10(result.ToString("E15"), out i, zF);
			Calc.SetToN(zF, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
			if (flag != 0)
			{
				if (flag == 1) MultiID.MuWriteErr(name + ": Cannot compute LerchPhi(x, s, a) for x > 1");
				if (flag == 2) MultiID.MuWriteErr(name + ": LerchPhi is not defined for integer a <= 0");
				if (flag == 3) MultiID.MuWriteErr(name + ": Pow() is not defined for a < 0 and s not integer");
				if (flag == 4) MultiID.MuWriteErr(name + ": Overflow in Aj()");
				if (flag == 5) MultiID.MuWriteErr(name + ": Underflow in remainder estimate omega in LerchPhi()");
				if (flag == 7) MultiID.MuWriteErr(name + ": Denominator reached infinity");
			}
			MultiID.ADiag(name + ": Iteration = " + iter);
			if (reduced) MultiID.WriteLn(name + ": Accuracy reduced to " + acc);
		} // Lerch1

		// Function that computes van Wijngaarden's A_j for a given j. // used by LerchT and Lerch
		private static int Aj(MultiF x, MultiF s, MultiF a, int j, MultiF result, string name)
		{
			MultiF bjk = new MultiF(Calc.fMC);
			MultiF ind = new MultiF(Calc.fMC);
			MultiF jFp = new MultiF(Calc.fMC);
			MultiF p = new MultiF(1);				// p is a power
			MultiF sum = new MultiF(Calc.fMC);
			MultiF sumP = new MultiF(Calc.fMC);
			MultiF t = new MultiF(Calc.fMC);		// t is temp
			MultiF two2k = new MultiF(Calc.fMC);
			MultiF z2ind = new MultiF(Calc.fMC);
			two2k &= MultiFD.fHalf;
			jFp &= j + 1;
			int flag = 0;
			for (int k = 0; true; k++)	// Sum b^j_k's over k.
			{
				two2k *= 2;	// Index for the term of the original series.
				ind &= two2k * jFp - 1;	// ind = two2k * (j + 1) - 1
				// If long integer overflow occurs, variables become zero.
				// Not relevant in v1.0 because two2k and ind are double type.	
				if (MultiID.muErr)
				{
					flag = 4;
					break;
				}
				// Increment the sum.
				PowNF(x, ind, z2ind);					// z2ind = Pow(x, ind)
				MultiFD.AddF(a, ind, t);				// t = a + ind
				PowNF(t, s, p);							// t = Math.Pow(a + ind, s)
				bjk &= two2k * z2ind / p;				// bjk = two2k * z2ind / Math.Pow(a + ind, s)
				sumP &= sum;
				sum += bjk;
				// Stop summation if either sum is zero or sum == sumP
				if (MultiFD.ZTestF(sum) || (sum == sumP)) break;
				MultiID.MuInterrupt();
				if (MultiID.statReq3)
				{
					Common.SingleSpace();
					MultiID.WriteT(name +": Aj Iteration = " + k);
					MultiID.TimeDel();
					MultiID.statReq3 = false;
				}
				if (MultiID.muAbort || MultiID.muErr)
				{
					sum &= 0;
					break;
				}
			}
			result &= sum;
			MultiFD.DoneF(z2ind);
			MultiFD.DoneF(two2k);
			MultiFD.DoneF(t);
			MultiFD.DoneF(sumP);
			MultiFD.DoneF(sum);
			MultiFD.DoneF(p);
			MultiFD.DoneF(jFp);
			MultiFD.DoneF(ind);
			MultiFD.DoneF(bjk);
			return flag;
		} // Aj

		public static void Lerch(MultiF x, MultiF s, MultiF a, bool T, int zI) // z = Lerch transcendent capitol Phi(x, s, a)
		// Function that computes approximation to Lerch Phi as a
		// converging sequence of CNC transforms S^n_k.
		// x, s, a may have the same address in memory as z				// traditional
		// x, s, a are not changed unless it shares memory with z
		// sets muErr true if overflow or error
		//
		// The LerchT(x, s, a) function is Phi(x, s, a) = Sum {k = 0, 1, ...} of x^k / (a + k)^s,
		// where |x| <= 1 and a != 0, -1, -2, ... .
		// Lerch(x, s, a) = sum[k=0, infinity]{x^k / |a + k|^s}, terms with a + k == 0 are ignored, |x| <= 1
		{
			int flag = 0, flagL = 0;
			// Special cases.
			string name = "Lerch";
			if (T)
			{
				name = "LerchT";
				if (a <= MultiFD.fZero && a.C >= 0)			// if a <= 0 and an integer
				{
					Calc.ClearN(zI);
					flag = 2;
					MultiID.MuWriteErr(name + ": LerchPhi is not defined for integer a <= 0");
					return;
				}
				if (a.S && s.C < 0)							// if a < 0 and s not an integer
				{
					Calc.ClearN(zI);
					flag = 3;
					MultiID.MuWriteErr(name + ": Pow() is not defined for a < 0 and s not integer");
					return;
				}
			}
			// 1 < x. (Return error, Lerch Phi diverges.)	// was <= but LerchPhi(1, 2, 1) = Dilog(1) = Pi^2 / 6
			if (x > MultiFD.fOne)	// if x > 1
			{
				Calc.ClearN(zI);
				flag = 1;
				MultiID.MuWriteErr(name + ": Cannot compute LerchPhi(x, s, a) for x > 1");
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF aL = new MultiF(Calc.fMC);
			MultiF n = new MultiF();
			aL &= a;									// Local copy of a.
			if (!T)
			{
				n = new MultiF(Calc.fMC);
				if (a <= MultiFD.fZero && a.C >= 0)		// if a <= 0 and an integer
				{
					flagL = 2;
					n &= -aL;
					aL &= 1;
				}
			}
			MultiF t = new MultiF(Calc.fMC);			// t = temp
			double digits = Calc.fMC * MultiID.muDMax;
			int imax = (int)(4.0 * digits);
			int i, j, sign, iter, imin = 0;				// m = 0; const int beta = 1, n = 0;
			bool sEq2 = (s == MultiFD.fTwo);			// does s equal 2?
			if (sEq2 && a == MultiFD.fOne && x.N == 1 && x.V[0] == 1) // and a equal 1?
			{											// lerch(+/-1, 2, 1)
				int pI = GetPiPtr();
				t &= Common.nList[pI] * Common.nList[pI];
				if (x.S)
					t /= 12;			// Lerch(-1, 2, 1) = Pi^2 / 12
				else
					t /= 6;				// Lerch(1, 2, 1) = Pi^2 / 6
				Calc.SetToN(t, zI);
				iter = 0;
				goto EndItEarly;
			}
			MultiF cacc = new MultiF(Calc.fMC);
			MultiF eps = new MultiF(Calc.fMC);
			MultiF eps0 = new MultiF(Calc.fMC);
			MultiF factor = new MultiF(Calc.fMC);
			MultiF factor1 = new MultiF(Calc.fMC);
			MultiF iF = new MultiF(4);				// iF is i in floating-point
			MultiF iom = new MultiF(Calc.fMC);
			MultiF mF = new MultiF(Calc.fMC);
			MultiF omega = new MultiF(Calc.fMC);
			MultiF p1 = new MultiF(1);				// p1 and p2 are powers
			MultiF p2 = new MultiF(1);
			MultiF skn = new MultiF(Calc.fMC);
			MultiF skn0 = new MultiF(Calc.fMC);
			MultiF sn = new MultiF(Calc.fMC);
			MultiF sum1 = new MultiF(Calc.fMC);
			MultiF best = new MultiF(Calc.fMC);
			MultiF minEps = new MultiF(Calc.fMC);
			minEps.V[0] = MultiID.maxDigit;			// initialize min eps arbitrary large
			minEps.C = long.MaxValue;
			MultiF[] num;
			MultiF[] den; 
			MultiF[] StoreAj;
			if (a.S && x != 0)	// a < 0 is not integer or zero and x != 0 (x == 0 considered below) ...
			{
				// s is an integer. (Transform a to positive).
				MultiFD.FloorF(a, mF);			// m = -Floor(a)
				mF.S = false;
				MultiFD.RAddF(mF, aL);			// aL += m
				MultiFD.ClearF(sum1);
				for (iF &= 0; iF < mF; iF++)
				{
					PowNF(x, iF, p1);			// p1 = x^i
					t &= a + iF;
					if (!T) t.S = false;		// p2 = |a+i|^s
					PowNF(t, s, p2);			// p2 = (a+i)^s
					sum1 += p1/p2;
				}
			}
			if (x == 0)							// x == 0 and a not integer <= 0
			{
				iter = 1;							// (Return first term of series.)
				if(T) PowNF(a, s, p1);				// p1 = Pow(a, s)	// a < 0 ok since s is an integer then
				else
				{
					t &= a;
					t.S = false;
					PowNF(t, s, p1);				// p1 = Pow(abs(a), s)
				}
				MultiFD.DivF(MultiFD.fOne, p1, t);	// t = 1 / p1
				Calc.SetToN(t, zI);					// result = 1 / p1
				goto EndIt;
			}
			// General case.
			//
			// For x <= 0.5 van Wijngaarden transformation is not used
			// [hence no calls to Aj()].
			bool zAbsLEH = MultiFD.fMHalf <= x && x <= MultiFD.fHalf;	// Abs(x) <= 0.5
			bool xValLEH = x <= MultiFD.fHalf;							// x <= 0.5
			if (xValLEH)			// if (x <= 0.5)	// Direct summation and CNCT (x < -0.5) case.
			{
				PowNF(aL, s, p1);						// p1 = Pow(aL, s)
				MultiFD.DivF(MultiFD.fOne, p1, omega);	// omega = 1 / p1
			}
			else										// CNCT (x > 0.5) case.
			{
				flag = Aj(x, s, aL, 0, omega, name);
				if (flag != 0)
				{
					MultiID.MuWriteErr(name + ": Overflow in Aj()");
					Calc.ClearN(zI);		// result = 0.0
					iter = 0;
					goto EndIt;
				}
			}
			num = new MultiF[imax];			// Allocate memory for working arrays.
			den = new MultiF[imax];
			for (i = 0; i < imax; i++)
			{
				num[i] = new MultiF(Calc.fMC);
				den[i] = new MultiF(Calc.fMC);
				MultiID.MuInterrupt();
			}
			if (!xValLEH)					// x > 0.5
			{
				StoreAj = new MultiF[imax];	// StoreAj is used only in CNCT
				for (i = 0; i < imax; i++)
				{
					StoreAj[i] = new MultiF(Calc.fMC);
					MultiID.MuInterrupt();
				}
			}
			else StoreAj = new MultiF[0];
			flag = 0;
			sign = -1;
			for (i = 0; true; i++)	// Main loop: iterations for S^k_n.
			{						// i points to current iterate.
				iF &= i;
				sign = -sign;		// Increment the sum.
				sn += omega;		// Next term: omega.
				if (xValLEH)		// if (x <= 0.5) // "Direct summation".
				{
					t &= aL + iF;
					PowNF(t / (t+1), s, p2);
					omega &= x * p2 * omega;
				}
				else				// CNCT (x > 0.5) case.
				{
					StoreAj[i] = sign * omega;
					if ((i & 1) == 0)			// !Odd
					{
						PowNF(x, iF/2, p1);		// Recurrence for odd pointer i.
						PowNF(aL + iF/2, s, p2);
						omega &= MultiFD.fHalf * (StoreAj[i/2] - p1 / p2);
						if (sign > 0) MultiFD.ChSF(omega);	// * -sign
					}
					else
					{
						flag = Aj(x, s, aL, i+1, omega, name);
						if (flag != 0)			// flag = 4
						{
							MultiID.MuWriteErr(name + ": Overflow in Aj()");
							Calc.ClearN(zI);	// result = 0.0
							iter = 0;
							goto EndIt;
						}
						else  omega &= -sign * omega;
					}
				}
				// Direct summation case: store current sum and remainder estimate.
				if (zAbsLEH)						// Abs(x) <= 0.5
				{
					skn &= sn;
				}
				else	// CNCT case.
				{			
					if (MultiFD.ZTestF(omega))		// Make sure omega is representable machine number.
					{
						flag = 5;
						if (!MultiID.muAbort && !MultiID.muErr)
							MultiID.MuWriteErr(name + ": Underflow in remainder estimate omega in LerchPhi()");
						Calc.ClearN(zI);			// result = 0.0
						iter = i+1;
						goto EndIt;
					}
					else
						MultiFD.DivF(MultiFD.fOne, omega, iom);	// iom = 1.0 / omega;
					num[i] &= sn * iom;				// Last terms in sums of numerator and denominator of i-th partial sum.
					den[i] &= iom;
					if (i > 0)						// Recurrence computation of numerator and denominator of a S_k^n.
					{
						num[i-1] &= num[i] - num[i-1];
						den[i-1] &= den[i] - den[i-1];
					}
					factor1 &= iF * (iF - 1);		// (beta+n+i-1) * (beta+n+i-2)
					for(j = 2; j <= i; j++)
					{
						t &= i + j - 1;							// beta = 1, n = 0
						MultiFD.DivF(factor1, t, factor);		// factor = factor1 / (beta+n+i+j-2)
						t &= i + j - 2;
						MultiFD.RDivF(t, factor);				// factor = factor1 / (beta+n+i+j-2) / (beta+n+i+j-3)
						num[i-j] &= num[i-j+1] - factor * num[i-j];
						den[i-j] &= den[i-j+1] - factor * den[i-j];
					}
					skn &= num[0] / den[0];			// Current approximation of the sum S_k^n.
				} // else CNCT case.
				MultiFD.SubF(skn, skn0, eps);		// eps = Abs(skn - skn0)
				eps.S = false;
				if ((skn.N + skn.C) - (eps.N + eps.C) >= Calc.fMC - 2) break;
				if (-(eps.N + eps.C) >= Calc.fMC - 3) break;
				// Check the two termination criteria,
				if (MultiFD.ZTestF(eps)) break;		// Successive iterates skn are the same.
				if (eps < minEps)					// save best answer so far
				{
					imin = i + 1;
					minEps &= eps;
					best &= skn;
				}
				if (i > imax-2 || i >= imin + 99)	// Maximum number of iterations is exceeded.
				{
					flag = 6;
					MultiID.WriteLn(name + ": No convergence within the maximum number of iterations = " + imax);
					MultiID.WriteLn(name + ": Results at iteration " + imin + " had a relative error approximately =");
					int sFMC = Calc.fMC;
					Calc.fMC = 2;
					MultiFD.WriteFLn(minEps / best);
					Calc.fMC = sFMC;
					skn &= best;					// result = best;
					break;
				}
				skn0 &= skn;			// Go on to the next iteration.
				eps0 &= eps;
				MultiID.MuInterrupt();
				if (MultiID.statReq2)
				{
					Common.SingleSpace();
					MultiID.WriteT(name + ": Iteration = " + i + " of " + (imax-2));
					MultiID.TimeDel();
					MultiID.statReq2 = false;
				}
				if (MultiID.muAbort || MultiID.muErr)
				{
					Calc.ClearN(zI);
					iter = i+1;
					goto EndIt;
				}
			} // for
			if (a.S)					// Store the resulting sum.
			{
				PowNF(x, mF, p1);		// p1 = x^m (m is a integer > 0)
				skn *= p1;				// skn = skn * p1
				skn += sum1;			// result = skn = sum1 + skn * p1
			}
			iter = i + 1;				// Store the number of iterations.
			if (!T && flag == 0 && flagL == 2)
			{
				PowNF(x, n+1, p1);
				skn *= p1;
				for (MultiFD.ClearF(iF); iF < n; iF++)
				{
					PowNF(x, iF, p1);
					PowNF(n-iF, s, p2);
					skn += p1 / p2;			// result += x^k / (n - k)^s
				}
			}
			Calc.SetToN(skn, zI);		// result = skn;
			EndIt:
				if (!T && flag == 0 && flagL == 2)
					MultiID.WriteLn(name + ": Integer a <= 0, one infinite term ignored");
			StoreAj = new MultiF[0];
			den = new MultiF[0];
			num = new MultiF[0];
			MultiFD.DoneF(minEps);
			MultiFD.DoneF(best);
			MultiFD.DoneF(sum1);
			MultiFD.DoneF(sn);
			MultiFD.DoneF(skn0);
			MultiFD.DoneF(skn);
			MultiFD.DoneF(p2);
			MultiFD.DoneF(p1);
			MultiFD.DoneF(omega);
			MultiFD.DoneF(mF);
			MultiFD.DoneF(iom);
			MultiFD.DoneF(iF);
			MultiFD.DoneF(factor1);
			MultiFD.DoneF(factor);
			MultiFD.DoneF(eps0);
			MultiFD.DoneF(eps);
			MultiFD.DoneF(cacc);
			MultiFD.DoneF(aL);
			EndItEarly:
				MultiFD.DoneF(t);
			MultiFD.DoneF(n);
			MultiFD.DoneF(aL);
			MultiID.ADiag(name + ": Iteration = " + iter);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // Lerch

		public static void Lerch2(MultiF x, MultiF s, MultiF a, bool T, int zI) // z = Lerch transcendent capitol Phi(x, s, a)
		// x, s, a may have the same address in memory as z			// by a simple sum and traditional
		// x, s, a are not changed unless it shares memory with z
		// sets muErr true if overflow or error
		//
		// The LerchT2(x, s, a) function is Phi(x, s, a) = Sum {k = 0, 1, ...} of x^k / (a + k)^s,
		// where |x| <= 1 and a != 0, -1, -2, ... .
		// Lerch2(x, s, a) = sum[k=0, infinity]{x^k / |a + k|^s}, terms with a + k == 0 are ignored, |x| <= 1
		{
			string name = "Lerch2";
			if (T)
			{
				name = "LerchT2";
				if (a <= MultiFD.fZero && a.C >= 0) // if a <= 0 and an integer
				{
					Calc.ClearN(zI);
					MultiID.MuWriteErr(name + ": LerchPhi is not defined for integer a <= 0");
					return;
				}
				if (a.S && (s.C < 0)) // if a < 0 and s not an integer
				{
					Calc.ClearN(zI);
					MultiID.MuWriteErr(name + ": Pow() is not defined for a < 0 and s not integer");
					return;
				}
			}
			if (x > MultiFD.fOne || x < MultiFD.fMOne)	// if x > 1 or x < -1
			{
				Calc.ClearN(zI);
				MultiID.MuWriteErr(name + ": Cannot compute LerchPhi(x, s, a) for |x| > 1");
				return;
			}
			bool sEq2 = (s == MultiFD.fTwo); // does s equal 2?
			if (sEq2 && a == MultiFD.fOne)
			{
				if (x.N == 1 && x.V[0] == 1) // lerch(+/-1, 2, 1)
				{
					Lerch(x, s, a, T, zI);			// will compute Pi^2 / 12 or Pi^2 / 6
					return;
				}
			}
			if (!T && a <= MultiFD.fZero && a.C >= 0) // if a <= 0 and an integer
			{
				MultiID.WriteLn(name + ": Integer a <= 0, one infinite term ignored");
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF ap = new MultiF(Calc.fMC);
			MultiF b = new MultiF(Calc.fMC);
			MultiF d = new MultiF(Calc.fMC);
			MultiF del = new MultiF(Calc.fMC);
			MultiF n = new MultiF(Calc.fMC);
			MultiF sum = new MultiF(Calc.fMC);
			n &= 1;
			ap &= a;
			sum &= 0;
			int k;
			for (k = 0; true; k++)		// z = sum{k=0,...}(x^k / (a+k)^s
			{
				if (ap != 0)			// exclude terms with a + k = 0
				{
					MultiID.MuInterrupt();
					if (MultiID.statReq3)
					{
						Common.SingleSpace();
						MultiID.WriteT(name + ": Iteration = " + k);
						MultiID.TimeDel();
						MultiID.statReq3 = false;
					}
					if (MultiID.muAbort || MultiID.muErr)
					{
						Calc.ClearN(zI);
						break; // exit for
					}
					bool signAp= ap.S;				// save sign of a + k
					if (!T) ap.S = false;			// ap = absolute value of a + k
					if (sEq2)						// if s equals 2
						d &= ap * ap;				//		d = ap^2
					else							// else
						PowNF(ap, s, d);			//		d = ap^s or |ap|^2
					ap.S = signAp;					// restore sign of a + k
					del &= n / d;
					b &= sum;						// b = previous sum
					sum += del;
					if (b == sum)
					{
						Calc.SetToN(sum, zI);
						break;
					}
				}
				n *= x;
				ap += 1;
			}
			MultiID.ADiag(name + ": Iteration = " + k);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // Lerch2

		public static void GamNF(MultiF x, int zI) // z = gamma(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			if ( (x.C >= 0 && x.S) || MultiFD.ZTestF(x) )
			{
				MultiID.MuWriteErr("Cannot take gamma of integer <= zero");
				Calc.ClearN(zI);
				return;
			}
			MultiF lim = new MultiF(4);
			if (x.C >= 0)							// if x is an integer > 0
			{
				int saveFMC = Calc.fMC;
				Calc.fMC = FacFMC(x, false);
				LimFac(Calc.fMC);
				lim &= limFac + 1;
				if (x <= lim)						// if x a small integer
				{
					MultiF xL = new MultiF(x.N);
					xL &= x - 1;					// use the factorial function for small n
					FacN(xL, zI, false, 0);
					MultiFD.DoneF(lim);
					MultiFD.DoneF(xL);
					Calc.fMC = saveFMC;
					return;
				}
				Calc.fMC = saveFMC;
			}
			MultiF limM = new MultiF(4);
			int j;
			MultiFD.ValueF10("+4.05997,83000,01705,88908,81E+18", out j, lim);
			MultiFD.ValueF10("-4.05997,83000,01705,88908,81E+18", out j, limM);
			if (x > lim || x < limM)
			{
				MultiID.MuWriteErr("Number too large for gamma function");
				Calc.ClearN(zI);
				MultiFD.DoneF(limM);
				MultiFD.DoneF(lim);
				return;
			}
			lim &= -5;
			bool reflection = (x < lim);				// if x < -5, use reflection
			MultiFD.DoneF(limM);
			MultiFD.DoneF(lim);
			MultiF xr = new MultiF(Calc.fMC);
			MultiF xx = new MultiF(Calc.fMC);
			if (reflection)
				xr &= 1 - x;							// x = 1 - x, use reflection formula
			else
				xr &= x;
			double digits = Calc.fMC * MultiID.muDMax;
			int n = (int)(Math.Pow(digits, 1.5) / 13.0) + 1;
			int iMax = (int)(0.6 * digits) + 20;
			xx &= xr;
			xx -= n;
			MultiFD.ChSF(xx);							// xx = n - xr
			if (xx.S)
				n = 0;
			else
				MultiFD.Get1F(out n, xx);
			xx &= xr + n;								// xx = xr + n
			MultiFD.DoneF(xr);
			MultiF lnG = new MultiF(Calc.fMC);			// lnG = (xx-0.5) * ln(xx) - xx + 0.5 * ln(2*pi)
			lnG &= xx;									// lnG = xx
			lnG -= MultiFD.fHalf;						// lnG = xx - 0.5
			int tI;
			Common.InitNAdd(1, "GamNF-tI", out tI);	// t = temp on the list
			Calc.SetToN(xx, tI);
			RLnN(tI);									// t = ln(xx)
			MultiFD.MulF(lnG, Common.nList[tI], lnG);	// lnG = (xx-0.5) * ln(xx)
			lnG -= xx;									// - xx
			int pI = GetPiPtr();
			MulN(MultiFD.fTwo, Common.nList[pI], tI);	// t = 2 * Pi
			RLnN(tI);									// t = ln(2*Pi)
			MulN(MultiFD.fHalf, Common.nList[tI], tI);	// t = 0.5 * ln(2*Pi)
			lnG += Common.nList[tI];
			MultiF j2 = new MultiF(1);
			j2.V[0] = 2;								// j2 = 2
			MultiF j2M = new MultiF(1);
			j2M.V[0] = 1;								// j2M = j2 - 1 = 1
			MultiF xP = new MultiF(Calc.fMC);
			xP &= xx;									// xP = xx
			MultiF xSq = new MultiF(Calc.fMC);
			MultiFD.MulF(xx, xx, xSq);					// xSq = xx^2
			int i = Math.Min(iMax, Calc.limBern);
			Common.GenBernI(i);
			MultiF div = new MultiF(Calc.fMC);
			MultiF lnGn = new MultiF(Calc.fMC);
			bool done = false;
			do
			{
				MultiID.MuInterrupt();
				Calc.Bern(j2, tI);				// t = B(j2)
				MultiFD.MulF(j2, j2M, div);		// div = j2 * (j2 - 1)
				MultiFD.MulF(xP, div, div);		// div = j2 * (j2 - 1) * xP
				MultiFD.DivF(Common.nList[tI], div, div);	// div = t / div
				MultiFD.AddF(lnG, div, lnGn);	// lnG new = lnG + new term
				MultiFD.SubF(lnGn, lnG, div);	// div = amount lnG changed
				if (MultiFD.ZTestF(div))
					done = true;
				else
				{
					MultiFD.SetToF(lnGn, lnG);
					MultiFD.MulF(xP, xSq, xP);
					MultiFD.AddF(j2, MultiFD.fTwo, j2);
					MultiFD.AddF(j2M, MultiFD.fTwo, j2M);
				}
			}
			while (!done && !MultiID.muAbort && !MultiID.muErr);
			if (MultiID.diagOn)
			{
				int j2i;
				MultiFD.Get1F(out j2i, j2);
				MultiID.WriteLn("Used Bernoulli number B(" + j2i + ")");
			}
			ExpN(lnG, tI);					// t = Exp(lnG) = gamma(xx)
			div &= MultiFD.fOne;
			for (i = 0; i < n; i++)
			{
				MultiFD.Add1F(xx, -1, xx);	// // xx = xx - 1
				MultiFD.MulF(div, xx, div);	// div = div * xx
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr)
				{
					Calc.ClearN(tI);
					div &= 1;
					break; // exit for
				}
			}
			MultiFD.DoneF(lnGn);
			DivN(Common.nList[tI], div, tI);	// t = gamma(xr) = gamma(xx) / div
			MultiFD.DoneF(div);
			MultiFD.DoneF(xSq);
			MultiFD.DoneF(xP);
			MultiFD.DoneF(j2M);
			MultiFD.DoneF(j2);
			if (reflection)
			{
				int t2I;										// x < 0
				Common.InitNAdd(1, "GamNF-t2I", out t2I);		// t2 = temp on the list
				ModN(x, MultiFD.fTwo, t2I);						// t2 = x mod 2 >= 0
				if (Common.nList[t2I] == MultiFD.f1P5)			// if x mod 2 == 1.5, sin is -1
				{
					MultiFD.ChSF(Common.nList[tI]);
				}
				else if (Common.nList[t2I] == MultiFD.fHalf)	// if x mod 2 == 0.5, sin is +1
				{
				}
				else
				{
					bool degModeSave = Calc.degMode;
					Calc.degMode = true;
					RMul1N(180, t2I);							// t2 = (x mod 2) * Pi
					RSinN(t2I);									// t2 = sin(x * Pi)
					RMulN(Common.nList[t2I], tI);				// t = gamma(xr) * sin(x * Pi)
					Calc.degMode = degModeSave;
				}
				DivN(Common.nList[pI], Common.nList[tI], tI);	// t = Pi / t = gamma(x)
				Common.LDelete(t2I);
			}
			Calc.SetToN(Common.nList[tI], zI);
			Calc.NormN(zI);
			Common.LDelete(tI);
			MultiFD.DoneF(lnG);
			MultiFD.DoneF(xx);
		} // GamNF

		public static void BetaNF(MultiF x, int zI) // z = beta(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			if (MultiFD.ZTestF(x))
			{
				Calc.SetToN(MultiFD.fHalf, zI);			// beta(0) = 1/2
				return;
			}
			if (x.C == 0 & x.S)
			{
				if ((x.V[0] & 1) == 1)
				{
					Calc.ClearN(zI);					// beta(negative odd integer) = 0
					return;
				}
			}
			int saveFMC;
			int saveMMax;
			saveFMC = Calc.fMC;
			saveMMax = MultiID.mMax;
			MultiF xx = new MultiF(Calc.fMC);
			MultiF lim = new MultiF(1);
			// lim &= -90000000; // diag
			// lim &= -313;								// for 400 m -313 is best, for 800 m -477 is best ... (for time)
			lim &= MultiFD.fHalf;
			bool reflection = (x < lim);				// if x < lim, use reflection
			// reflection = ((x.V[0] & 3) == 0);		// diag
			MultiFD.DoneF(lim);
			if (reflection)
				xx &= 1 - x;							// x = 1 - x, use reflection formula
			else
				xx &= x;
			// if (reflection) MultiID.WriteLn("Reflection"); // diag
			MultiF d = new MultiF(Calc.fMC);
			MultiF sum = new MultiF(Calc.fMC);				// beta = Sum ...
			MultiF sumN = new MultiF(Calc.fMC);
			double digits = Calc.fMC * MultiID.muDMax;
			long n = (Int64)((digits / 300.0) * digits + 5.0);
			if (n < 300) n = 300;
			MultiF nF = new MultiF(2);						// n floating
			nF &= n;
			long j = 4 * n + 1;
			MultiF iF = new MultiF(2);						// i floating
			MultiF jF = new MultiF(2);						// j floating
			jF &= j;
			sum &= 0;
			MultiID.ADiag("BetaNF: n = " + n);
			MultiFD.ChSF(xx);								// x = -x for now
			bool sign = false;
			int tI;
			Common.InitNAdd(1, "BetaNF-tI", out tI);		// t = temp on the list
			for (int i = 3; i < j; i += 2)
			{
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr) break;
				sign = !sign;
				iF &= i;
				PowN(iF, xx, tI);							// t = i ^ -x
				Common.nList[tI].S = sign;
				MultiFD.AddF(sum, Common.nList[tI], sum);	// sum = sum +/- t
				if (MultiID.statReq2)
				{
					MultiID.nDone = i - 2;
					MultiID.nToGo = j - i - 1;
					MultiID.WritenDone("BetaNF:");
				}
			}
			iF &= j;
			PowN(iF, xx, tI);								// t = i ^ -x
			MulN(Common.nList[tI], MultiFD.fHalf, tI);		// 1/2J^x
			MultiFD.AddF(sum, Common.nList[tI], sum);		// sum = sum +/- t
			MultiFD.ChSF(xx);								// x = +x now
			MultiF twoToK = new MultiF(Calc.fMC);
			MultiF xK = new MultiF(Calc.fMC);
			MultiF xKP = new MultiF(Calc.fMC);
			MultiF kF = new MultiF(Calc.fMC);
			MultiF term = new MultiF(Calc.fMC);
			twoToK &= 4;
			xK &= xx;
			xK += 1;
			PowN(iF, xK, tI);								// t = j ^ (x + 1)
			MultiFD.Mul1F(Common.nList[tI], 4, d);			// d = 4 * (j ^ (x + 1)), denominator 2*k!*(J^(k+x-1)) with k = 2
			xK &= xx;										// xK = x + k - 2
			xKP &= xK;										// xKP = x * (x+1) * (x+2) * ... * (x+k-2)
			Common.GenBernI(Calc.limBern);					// generate Bernoulli numbers
			for (int k = 2; ; k += 2)
			{
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr) break;
				kF &= k;
				Calc.Bern(kF, tI);							// t = B(k) = k-th Bernoulli number
				MultiFD.SubF(twoToK, MultiFD.fOne, term);
				MultiFD.MulF(term, twoToK, term);
				MultiFD.MulF(term, xKP, term);
				MultiFD.AddF(xK, MultiFD.fOne, xK);
				MultiFD.MulF(xK, xKP, xKP);
				if (k > 2)
				{
					MultiFD.MulF(term, xK, term);
					MultiFD.AddF(xK, MultiFD.fOne, xK);
					MultiFD.MulF(xK, xKP, xKP);
				}
				MultiFD.MulF(term, Common.nList[tI], term);
				MultiFD.DivF(term, d, term);
				MultiFD.AddF(sum, term, sumN);				// sumN = sum + t = new sum
				if (sum == sumN)
				{
					MultiID.ADiag("Used Bernoulli number B(" + k + ")");
					k++; // diag
					break;
				}
				MultiFD.SetToF(sumN, sum);
				MultiFD.MulF(twoToK, MultiFD.fFour, twoToK);	// twoToK = 2^k
				MultiFD.MulF(d, iF, d);							// d = d * j
				MultiFD.MulF(d, iF, d);							// d = d * j
				kF &= k + 1;
				MultiFD.MulF(d, kF, d);							// d = d * (k+1)
				kF &= k + 2;
				MultiFD.MulF(d, kF, d);							// d = d * (k+2)
			}
			MultiFD.DoneF(term);
			MultiFD.DoneF(kF);
			MultiFD.DoneF(xKP);
			MultiFD.DoneF(xK);
			MultiFD.DoneF(twoToK);
			MultiFD.DoneF(jF);
			MultiFD.DoneF(iF);
			MultiFD.DoneF(nF);
			sum += 1;
			if (reflection)
			{
				ModN(xx, MultiFD.fFour, tI);						// xx > 0
				if (Common.nList[tI] == MultiFD.fOne)				// if x mod 4 == 1, sin is +1
				{
				}
				else if (Common.nList[tI] == MultiFD.fThree)		// if x mod 4 == 3, sin is -1
				{
					MultiFD.ChSF(sum);
				}
				else
				{
					bool degModeSave = Calc.degMode;
					Calc.degMode = true;
					RMul1N(90, tI);									// t = Pi * x / 2
					RSinN(tI);										// t = sin(Pi * x / 2)
					MultiFD.MulF(Common.nList[tI], sum, sum);		// sum = t * sum
					Calc.degMode = degModeSave;
				}
				int pI = GetPiPtr();
				MultiFD.DivF(MultiFD.fTwo, Common.nList[pI], d);	// 2 / Pi
				PowN(d, xx, tI);									// t = (2 / Pi) ^ x
				MultiFD.MulF(Common.nList[tI], sum, sum);			// sum = t * sum
				GamNF(xx, tI);										// t = gamma(x)
				MultiFD.MulF(Common.nList[tI], sum, sum);			// sum = t * sum
			}
			Calc.SetToN(sum, zI);
			Calc.NormN(zI);
			Common.LDelete(tI);
			MultiFD.DoneF(sumN);
			MultiFD.DoneF(sum);
			MultiFD.DoneF(d);
			MultiFD.DoneF(xx);
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			if (MultiID.muAbort || MultiID.muErr) Calc.ClearN(zI);
		} // BetaNF

		public static void ErfNF(MultiF x, int zI) // z = erf(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			if (x.N + x.C > -1)
			{
				// erf(x) = 1 - erfc(x) unless |x| is very small
				ErfCNF(x, zI);
				SubN(MultiFD.fOne, Common.nList[zI], zI);
				return;
			}
			MultiF s = new MultiF(Calc.fMC);
			MultiF m = new MultiF(Calc.fMC);
			MultiF t = new MultiF(Calc.fMC);
			MultiF f = new MultiF(Calc.fMC);
			MultiF c = new MultiF(Calc.fMC);
			MultiF p = new MultiF(Calc.fMC);
			MultiF d = new MultiF(Calc.fMC);
			MultiF xs = new MultiF(Calc.fMC);
			s &= 1;			// s = sum
			m &= 1;			// m = multiplier
			t &= 1;			// t = term
			f &= 1;			// f = factorial factor
			c &= 3;			// c = constant
			xs &= x * x;	// xs = x^2
			for (int j = 1; true; j++)	// sum of (-x^2)^j / (j!*(2*j + 1))
			{
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr) break;
				m *= xs;			// m = x^2 * m / f
				m /= f;
				MultiFD.ChSF(m);	// change sign of m
				t &= m / c;			// t = m / c
				p &= s;				// p = previous sum
				s += t;				// sum = sum + t
				d &= s - p;			// d = delta in sum
				if (MultiFD.ZTestF(d))
				{
					break;
				}
				f += 1;				// f = j
				c += 2;				// c = 2*j + 1
			}
			if (MultiID.muAbort || MultiID.muErr)
			{
				Calc.ClearN(zI);
			}
			else
			{
				s *= 2;				// s = 2 * x * sum
				s *= x;
				int pI = GetPiPtr();
				MultiFD.SqRootF(Common.nList[pI], t);	// t = SqRt(Pi);
				s /= t;				// s = s / SqRt(Pi)
				Calc.SetToN(s, zI);	// final value is s
			}
			MultiFD.DoneF(c);
			MultiFD.DoneF(f);
			MultiFD.DoneF(t);
			MultiFD.DoneF(m);
			MultiFD.DoneF(s);
		} // ErfNF

		public static void ErfCNF(MultiF x, int zI) // z = erfc(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			if (MultiFD.ZTestF(x))				// if x = 0, erfc(x) = 1;
			{
				Calc.SetToN(MultiFD.fOne, zI);
				return;
			}
			MultiF xSq = new MultiF(Calc.fMC);	// if x > 0, erfc(x) = gamq(1/2, x^2)
			bool sign = x.S;					// save sign of x
			MultiFD.MulF(x, x, xSq);
			GamQN(MultiFD.fHalf, xSq, zI);		// because this can clobber x
			if (sign)							// if x was < 0, erfc = 2 - erfc(-x)
			{
				SubN(MultiFD.fTwo, Common.nList[zI], zI);
			}
			MultiFD.DoneF(xSq);
		} // ErfCNF

		public static void EtaNF(MultiF x, int zI) // z = eta(x), eta(4) = 7*Pi^4/720
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			if (MultiFD.ZTestF(x))
			{
				Calc.SetToN(MultiFD.fHalf, zI);				// eta(0) = 1/2
				return;
			}
			if (x.C >= 0 && x.S)
			{
				if ( x.C > 0 || ((x.V[0] & 1) == 0) )
				{
					Calc.ClearN(zI);						// eta(negative even integer) = 0
					return;
				}
			}
			MultiF xx = new MultiF(Calc.fMC);
			MultiF lim = new MultiF(1);
			lim &= -4;
			bool reflection = (x < lim);					// if x < -4, use reflection
			MultiFD.DoneF(lim);
			if (reflection)
				xx &= 1 - x;								// x = 1 - x, use reflection formula
			else
				xx &= x;
			double digits = Calc.fMC * MultiID.muDMax;
			int n = (int)(1.3 * digits) + 5;
			MultiF nF = new MultiF(2);						// n floating
			nF &= n;
			MultiF sum = new MultiF(Calc.fMC);				// eta = Sum ...
			MultiF kF = new MultiF(2);						// k floating = 0
			int k;
			bool sign = false;								// (-1)^(k-1)
			int tI;
			Common.InitNAdd(1, "EtaNF-tI", out tI);			// t = temp on the list
			MultiFD.ChSF(xx);								// xx = -x
			MultiF c = new MultiF(Calc.fMC);				// c = C(n, j)
			c &= 1;
			MultiF e = new MultiF(Calc.fMC);				// e = Sum C(n, j)
			e &= 1;
			MultiF nM = new MultiF(2);						// nM = n - j + 1
			nM &= n;
			MultiF jF = new MultiF(2);						// j floating
			jF &= 1;
			MultiF d = new MultiF(Calc.fMC);				// d = delta for sum
			for (k = n+n; k > n; k--)
			{
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr) break;
				if (MultiID.statReq2)
				{
					MultiID.nDone = n+n-k;
					MultiID.nToGo = k;
					MultiID.WritenDone("EtaNF:");
				}
				sign = !sign;
				MultiFD.SetTo1F(k, kF);
				PowN(kF, xx, tI);							// t = k ^ -x
				Common.nList[tI].S = sign;					// * (-1) ^ (k-1)
				MultiFD.MulF(e, Common.nList[tI], d);		// d = e * t
				MultiFD.AddF(sum, d, sum);					// sum = sum + d
				MultiFD.MulF(c, nM, c);						// c = c * (n - j + 1)
				MultiFD.DivF(c, jF, c);						// c = c / j
				MultiFD.AddF(e, c, e);						// e = sum C(n, j)
				MultiFD.SubF(nM, MultiFD.fOne, nM);			// nM = nM - 1
				MultiFD.AddF(jF, MultiFD.fOne, jF);			// jF = jF + 1
			}
			MultiFD.DoneF(d);
			int tpI;
			Common.InitNAdd(1, "EtaNF-tpI", out tpI);		// tp = temp on the list
			PowN(MultiFD.fTwo, nF, tpI);					// tp = 2 ^ n
			MultiFD.DivF(sum, Common.nList[tpI], sum);		// sum = sum / (2 ^ n)
			Common.LDelete(tpI);
			for (k = n; k > 0; k--)
			{
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr) break;
				if (MultiID.statReq2)
				{
					MultiID.nDone = n+n-k;
					MultiID.nToGo = k;
					MultiID.WritenDone("EtaNF:");
				}
				sign = !sign;
				MultiFD.SetTo1F(k, kF);
				PowN(kF, xx, tI);							// t = k ^ -x
				Common.nList[tI].S = sign;					// * (-1) ^ (k-1)
				MultiFD.AddF(sum, Common.nList[tI], sum);	// sum += sign * (k ^ -x)
			}
			MultiFD.DoneF(jF);
			MultiFD.DoneF(nM);
			MultiFD.DoneF(e);
			MultiFD.DoneF(kF);
			if (reflection)
			{
				ModN(x, MultiFD.fFour, tI);					// x < 0
				if (Common.nList[tI] == MultiFD.fThree)		// if x mod 4 == 3, sin is -1
				{
					MultiFD.ChSF(sum);
				}
				else if (Common.nList[tI] == MultiFD.fOne)	// if x mod 4 == 1, sin is +1
				{
				}
				else
				{
					bool degModeSave = Calc.degMode;
					Calc.degMode = true;
					RMul1N(90, tI);								// t = Pi * x / 2
					RSinN(tI);									// t = sin(Pi * x / 2)
					MultiFD.MulF(Common.nList[tI], sum, sum);	// sum = t * sum
					Calc.degMode = degModeSave;
				}
				int pI = GetPiPtr();
				MultiF f = new MultiF(Calc.fMC);				// f = 2 * pi
				MultiFD.MulF(MultiFD.fTwo, Common.nList[pI], f);// f = 2 * Pi
				PowN(f, xx, tI);								// t = (2 * Pi) ^ (x-1)
				MultiFD.MulF(Common.nList[tI], sum, sum);		// sum = t * sum
				MultiFD.ChSF(xx);								// xx = +(1-x)
				GamNF(xx, tI); // t = Gamma(1-x)
				MultiFD.MulF(Common.nList[tI], sum, sum);		// sum = t * sum
				PowN(MultiFD.fTwo, xx, tI);						// t = 2 ^ (1-x)
				MultiFD.SubF(MultiFD.fOne, Common.nList[tI], f);// f = 1 - 2^(1-x)
				MultiFD.MulF(f, sum, sum);						// sum = f * sum
				RInvN(tI);										// t = 2 ^ (x-1)
				MultiFD.SubF(MultiFD.fHalf, Common.nList[tI], f);// f = .5 - 2^(x-1)
				MultiFD.DivF(sum, f, sum);						// sum = sum / f
				MultiFD.DoneF(f);
			}
			Calc.SetToN(sum, zI);
			Calc.NormN(zI);
			Common.LDelete(tI);
			MultiFD.DoneF(nF);
			MultiFD.DoneF(xx);
		} // EtaNF

		public static void LambdaNF(MultiF x, int zI) // z = lambda(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			if (x == MultiFD.fOne)
			{
				MultiID.MuWriteErr("Cannot take lambda(x) for x = one");
				Calc.ClearN(zI);
				return;
			}
			if (MultiFD.ZTestF(x))
			{
				Calc.ClearN(zI);						// lambda(0) = 0
				return;
			}
			if (x.C >= 0 && x.S)
			{
				if (x.C > 0 || (x.V[0] & 1) == 0)
				{
					Calc.ClearN(zI);					// lambda(negative even integer) = 0
					return;
				}
			}
			int zetaI, tI;
			Common.InitNAdd(1, "LambdaNF-zetaI", out zetaI);	// zeta = temp on the list
			ZetaNF(x, zetaI);									// lambda(x) = Zeta(x) * (1 - 2^x)
			Common.InitNAdd(1, "LambdaNF-t2I", out tI);			// t = temp on the list
			MultiFD.ChSF(x);
			PowN(MultiFD.fTwo, x, tI);							// t = 2 ^ (-x)
			MultiFD.ChSF(x);
			MultiF f = new MultiF(Calc.fMC);
			MultiFD.SubF(MultiFD.fOne, Common.nList[tI], f);	// f = 1 - 2^(-x)
			MultiFD.MulF(Common.nList[zetaI], f, f);			// f = eta * f
			Calc.SetToN(f, zI);									// Zeta(x) = f
			Calc.NormN(zI);
			MultiFD.DoneF(f);
			Common.LDelete(tI);
			Common.LDelete(zetaI);
		} // LambdaNF

		public static void ZetaNF(MultiF x, int zI) // z = Zeta(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			if (x == MultiFD.fOne)
			{
				MultiID.MuWriteErr("Cannot take Zeta(x) for x = one");
				Calc.ClearN(zI);
				return;
			}
			if (MultiFD.ZTestF(x))
			{
				Calc.SetToN(MultiFD.fMHalf, zI);		// Zeta(0) = -1/2
				return;
			}
			if (x.C >= 0 && x.S)
			{
				if (x.C > 0 || (x.V[0] & 1) == 0)
				{
					Calc.ClearN(zI);					// Zeta(negative even integer) = 0
					return;
				}
			}
			int etaI, tI;
			Common.InitNAdd(1, "ZetaNF-etaI", out etaI);	// eta = temp on the list
			EtaNF(x, etaI);									// Zeta(x) = eta(x) / (1 - 2^(1-x))
			Common.InitNAdd(1, "ZetaNF-t2I", out tI);		// t = temp on the list
			SubN(MultiFD.fOne, x, tI);						// t = 1 - x
			PowN(MultiFD.fTwo, Common.nList[tI], tI);		// t = 2 ^ (1-x)
			MultiF f = new MultiF(Calc.fMC);
			MultiFD.SubF(MultiFD.fOne, Common.nList[tI], f);	// f = 1 - 2^(1-x)
			MultiFD.DivF(Common.nList[etaI], f, f);				// f = eta / f
			Calc.SetToN(f, zI);									// Zeta(x) = f
			Calc.NormN(zI);
			MultiFD.DoneF(f);
			Common.LDelete(tI);
			Common.LDelete(etaI);
		} // ZetaNF

		public static void GamNF1(MultiF x, int zI) // z = gamma(x) (16-digit gamma function)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			double intPart;
			if ( (x.C >= 0 && x.S) || MultiFD.ZTestF(x) )
			{
				MultiID.MuWriteErr("Cannot take gamma of integer <= zero");
				Calc.ClearN(zI);
				return;
			}
			if (x.C >= 0)
			{
				Calc.SetToN(x, zI);
				RSubN(MultiFD.fOne, zI);				// z = x - 1
				FacN(Common.nList[zI], zI, false, 1);
				return;
			}
			if (x.N + x.C <= 2)
				MultiFD.GetDbF(out intPart, x);
			else
				intPart = 1 + (double)Int32.MaxValue;
			if (Math.Abs(intPart) > Int32.MaxValue)		// arbitrary limit
			{
				MultiID.MuWriteErr("Number too large for gamma function");
				Calc.ClearN(zI);
				return;
			}
			MultiF xx = new MultiF(4);
			xx &= x - (int)intPart;			// z = fractional part of x
			if (xx > MultiFD.fHalf)			// if xx > 0.5
			{
				MultiFD.RAdd1F(-1, xx);
				intPart++;
			}
			if (xx < MultiFD.fMHalf)		// if xx < -0.5
			{
				MultiFD.RAdd1F(1, xx);
				intPart--;
			}
			double fracPart;				// fractional part
			xx.C += 4;
			MultiFD.GetDbF(out fracPart, xx);
			fracPart /= Math.Pow(MultiID.baseM, 4);
			xx.C -= 4;
			double oog = GammaSeries(fracPart); // One Over gamma(fracPart)
			Common.nList[zI].V = new int[5];
			Common.nList[zI].M = 4;
			if (oog == 0.0)
			{
				oog = 1.0;
				fracPart = 1.0;
				intPart = intPart - 1;
				if (intPart > 0) MultiFD.RAdd1F(1, xx);
			}
			string st = oog.ToString();
			int ii;
			MultiFD.ValueF10(st, out ii, Common.nList[zI]);
			MultiFD.DivF(MultiFD.fOne, Common.nList[zI], Common.nList[zI]); // g
			if (intPart != 0.0)
			{
				MultiID.nToGo = (long)Math.Abs(intPart);
				MultiID.nDone = 0;
				if (intPart > 0)
				{
					for (double i = 1; i <= intPart; i++)
					{
						if (MultiID.muErr)
						{
							MultiID.MuWriteErr("Number too large for gamma function");
							Calc.ClearN(zI);
							break;
						}
						MultiFD.RMulF(xx, Common.nList[zI]);	// g *= xx;
						MultiFD.RAdd1F(1, xx);					// xx += 1;
						MultiID.MuInterrupt();
						if (MultiID.muAbort)
						{
							MultiID.WriteLn("Gamma function aborted by operator, " + (intPart - i) +
								" multiplies to go");
							Calc.ClearN(zI);
							break;
						}
						MultiID.nToGo--;
						MultiID.nDone++;
						if (MultiID.statReq2) MultiID.WritenDone("GamNF1:");
					}
				}
				else
				{
					for (double i = 1; i <= -intPart; i++)
					{
						if (MultiID.muErr)
						{
							MultiID.MuWriteErr("Number too large for gamma function");
							Calc.ClearN(zI);
							break;
						}
						if (oog != 1.0)
							MultiFD.RSub1F(1, xx);				// xx -= 1;
						else
							oog = 0.0;
						if (MultiID.ZTestSI(xx))
						{
							MultiID.MuWriteErr("Cannot take gamma of integer <= zero");
							Calc.ClearN(zI);
							return;
						}
						MultiFD.RDivF(xx, Common.nList[zI]);	// g /= xx;
						MultiID.MuInterrupt();
						if (MultiID.muAbort)
						{
							MultiID.WriteLn("Gamma function aborted by operator, " + (-intPart - i) +
								" divides to go");
							Calc.ClearN(zI);
							break;
						}
						MultiID.nToGo--;
						MultiID.nDone++;
						if (MultiID.statReq2) MultiID.WritenDone("GamNF1:");
					}
				}
			}
			if (Common.nList[zI].N > 3) Common.nList[zI].V[0] = 0;
			Calc.NormN(zI);
			if (Common.nList[zI].N == 3) // save only the first 16 digits
			{
				double d = 1 + Math.Floor(Math.Log(Common.nList[zI].V[2], 10));
				d = Math.Pow(10, d);
				Common.nList[zI].V[0] = (int)(d * Math.Floor(Common.nList[zI].V[0] / d));
			}
			MultiID.nToGo = 0;
			MultiFD.DoneF(xx);
		} // GamNF1

		public static void PsiNF(MultiF x, int zI) // z = digamma(x)
		// x may have the same address in memory as z
		// x is not changed unless it shares memory with z
		// sets muErr true if overflow or error
		// z must be large enough ... (See FacN)
		{
			if ( (x.C >= 0 && x.S) || MultiFD.ZTestF(x) )
			{
				MultiID.MuWriteErr("Cannot take digamma of integer <= zero");
				Calc.ClearN(zI);
				return;
			}
			// numbers are (almost) never too large for Psi
			MultiF lim = new MultiF(1);
			lim &= -5;
			bool reflection = (x < lim);				// if x < -5, use reflection
			MultiFD.DoneF(lim);
			MultiF xr = new MultiF(Calc.fMC);
			MultiF xx = new MultiF(Calc.fMC);
			if (reflection)
				xr &= 1 - x;							// x = 1 - x, use reflection formula
			else
				xr &= x;
			double digits = Calc.fMC * MultiID.muDMax;
			double dN = (Math.Pow(digits, 1.5) / 13.0) + 1;
			if (dN > Int32.MaxValue)
				dN = Int32.MaxValue;
			int n = (int)dN;
			int iMax = (int)(0.6 * digits) + 20;
			xx &= xr;
			xx -= n;
			MultiFD.ChSF(xx);							// xx = n - xr
			if (xx.S)
				n = 0;
			else
				MultiFD.Get1F(out n, xx);
			xx &= xr + n;								// xx = xr + n
			MultiFD.DoneF(xr);
			MultiF psi = new MultiF(Calc.fMC);			// psi = ln(xx) - 1 / (2 * xx)
			MultiFD.DivF(MultiFD.fMHalf, xx, psi);		// -0.5 / xx
			int tI;
			Common.InitNAdd(1, "PsiNF-tI", out tI);		// t = temp on the list
			LnN(xx, tI);								// t = ln(xx)
			MultiFD.AddF(psi, Common.nList[tI], psi);	// psi = ln(xx) - 1 / (2 * xx)
			MultiF j2 = new MultiF(1);
			j2.V[0] = 2;								// j2 = 2
			MultiF xP = new MultiF(Calc.fMC);
			MultiF xSq = new MultiF(Calc.fMC);
			MultiFD.MulF(xx, xx, xSq);					// xSq = xx^2
			xP &= xSq;									// xP = xSq
			int i = Math.Min(iMax, Calc.limBern);
			Common.GenBernI(i);
			MultiF div = new MultiF(Calc.fMC);
			MultiF psiN = new MultiF(Calc.fMC);
			bool done = false;
			int k = 0;
			do
			{
				k++;
				MultiID.MuInterrupt();
				if (MultiID.statReq2)
				{
					MultiID.WriteT("PsiNF: k = " + k);
					MultiID.TimeDel();
					MultiID.statReq2 = false;
				}
				Calc.Bern(j2, tI);							// t = B(j2)
				MultiFD.MulF(xP, j2, div);					// div = j2 * xP
				MultiFD.DivF(Common.nList[tI], div, div);	// div = t / div
				MultiFD.SubF(psi, div, psiN);				// psi new = psi + new term
				MultiFD.SubF(psiN, psi, div);				// div = amount psi changed
				if (MultiFD.ZTestF(div))
					done = true;
				else
				{
					MultiFD.SetToF(psiN, psi);
					MultiFD.MulF(xP, xSq, xP);
					MultiFD.AddF(j2, MultiFD.fTwo, j2);
				}
			}
			while (!done && !MultiID.muAbort && !MultiID.muErr);
			if (MultiID.diagOn)
			{
				int j2i;
				MultiFD.Get1F(out j2i, j2);
				MultiID.WriteLn("Used Bernoulli number B(" + j2i + ")");
			}
			Calc.SetToN(psi, tI);							// t = psi
			div &= MultiFD.fOne;
			for (i = 0; i < n; i++)
			{
				MultiFD.Add1F(xx, -1, xx);					// xx = xx - 1
				MultiFD.DivF(MultiFD.fMOne, xx, div);		// div = -1 / xx
				MultiFD.AddF(psi, div, psi);
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr)
				{
					Calc.ClearN(tI);
					div &= 1;
					break; // exit for
				}
			}
			MultiFD.DoneF(psiN);
			MultiFD.DoneF(div);
			MultiFD.DoneF(xSq);
			MultiFD.DoneF(xP);
			MultiFD.DoneF(j2);
			if (reflection && !MultiID.muAbort && !MultiID.muErr)
			{
				int pI = GetPiPtr();
				int t2I;
				Common.InitNAdd(1, "PsiNF-t2I", out t2I);			// t2 = temp on the list
				x.S = false;										// x = |x|
				ModN(x, MultiFD.fOne, t2I);
				x.S = true;											// restore sign of x
				if (Common.nList[t2I] == MultiFD.fHalf)				// if -x mod 1 == 0.5, Pi/tan is zero
				{
					Calc.ClearN(tI);
				}
				else if (Common.nList[t2I] == MultiFD.fP25)			// if -x mod 1 == 0.25, Pi/tan is Pi
				{
					Calc.SetToN(Common.nList[pI], tI);
				}
				else if (Common.nList[t2I] == MultiFD.fP75)			// if -x mod 1 == 0.75, Pi/tan is -Pi
				{
					Calc.SetToN(Common.nList[pI], tI);
					Common.nList[tI].S = true;
				}
				else
				{
					bool degModeSave = Calc.degMode;
					Calc.degMode = true;
					RMul1N(180, t2I);								// t2 = x * Pi
					RTanN(t2I);										// t2 = tan(x * Pi)
					DivN(Common.nList[pI], Common.nList[t2I], tI);	// t = Pi / tan(x * Pi)
					Calc.degMode = degModeSave;
				}
				MultiFD.AddF(psi, Common.nList[tI], psi);			// psi = psi + Pi / tan(x * Pi)
				Common.LDelete(t2I);
			}
			Calc.SetToN(psi, zI);
			if (MultiID.muAbort || MultiID.muErr) Calc.ClearN(zI);
			Calc.NormN(zI);
			Common.LDelete(tI);
			MultiFD.DoneF(psi);
			MultiFD.DoneF(xx);
		} // PsiNF

		public static void GamN(MultiF x, int zI)
		// z = gamma(x)
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			GamNF(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // GamN

		public static void BetaN(MultiF x, int zI)
		// z = beta(x)
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ExpandMore(MultiFD.fGuard);
			BetaNF(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // BetaN

		public static void ErfN(MultiF x, int zI)
		// z = error function erf(x)
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ErfNF(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ErfN

		public static void ErfCN(MultiF x, int zI)
		// z = complementary error function erfC(x)
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ErfCNF(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ErfCN

		public static void EtaN(MultiF x, int zI)
		// z = eta(x)
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			EtaNF(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // EtaN

		public static void LambdaN(MultiF x, int zI)
		// z = lambda(x)
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			LambdaNF(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // LambdaN

		public static void ZetaN(MultiF x, int zI)
		// z = Zeta(x)
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ZetaNF(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ZetaN

		public static void GamN1(MultiF x, int zI) // 16-digit gamma function
		// z = gamma(x)
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			GamNF1(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // GamN1

		public static double GammaSeries(double x)
		// Compute 1/gamma(x) using series expansion
		{
			return (((((((((((((((((((((((((c[26]*x + c[25])*x + c[24])*x + c[23])*x + c[22])*x + c[21])*x
				+ c[20])*x + c[19])*x + c[18])*x + c[17])*x + c[16])*x + c[15])*x + c[14])*x + c[13])*x + c[12])*x + c[11])*x
				+ c[10])*x + c[9])*x + c[8])*x + c[7])*x + c[6])*x + c[5])*x + c[4])*x + c[3])*x + c[2])*x + c[1])*x;
		}

		public static void InitGammaSeries()
		// Initialize the gamma series
		{
			// Series expansion for 1/gamma(z) = Sum k=1,16 ck*z^k
			// from Handbook of Math Functions, USDofC
			// c1 thru c 26 =
			c = new double[27];
			c[00] = 0.0;
			c[01] = +1.0000000000000000;
			c[02] = +0.5772156649015329;
			c[03] = -0.6558780715202538;
			c[04] = -0.0420026350340952;
			c[05] = +0.1665386113822915;
			c[06] = -0.0421977345555443;
			c[07] = -0.0096219715278770;
			c[08] = +0.0072189432466630;
			c[09] = -0.0011651675918591;
			c[10] = -0.0002152416741149;
			c[11] = +0.0001280502823882;
			c[12] = -0.0000201348547807;
			c[13] = -0.0000012504934821;
			c[14] = +0.0000011330272320;
			c[15] = -0.0000002056338417;
			c[16] = +0.0000000061160950;
			c[17] = +0.0000000050020075;
			c[18] = -0.0000000011812746;
			c[19] = +0.0000000001043427;
			c[20] = +0.0000000000077823;
			c[21] = -0.0000000000036968;
			c[22] = +0.0000000000005100;
			c[23] = -0.0000000000000206;
			c[24] = -0.0000000000000054;
			c[25] = +0.0000000000000014;
			c[26] = +0.0000000000000001;
		}

		public static void PsiN(MultiF x, int zI)
		// z = psi(x) = gamma'(x) / gamma(x) = digamma function
		// x may have the same address in memory as zI
		// x is not changed unless it shares memory with zI
		// sets muErr true if overflow or error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			PsiNF(x, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // PsiN

		public static void RFacN(int zI) // z = z! = 1*2*3*...*x
		// sets muErr true if overflow or error
		{
			int fMBI = 1;
			FacN(Common.nList[zI], zI, false, fMBI);
		}

		public static void RFacSN(int zI) // z = z! = 1*2*3*...*x
		// sets muErr true if overflow or error
		{
			int fMBI = 1;
			FacSN(Common.nList[zI], zI, false, fMBI);
		}

		public static void RAFibN(int zI) // z = Arc Fibonacci number Abs(z)
		// sets muErr true if overflow or error
		{
			AFibN(Common.nList[zI], zI);
		}

		public static void RFibN(int zI) // z = Fibonacci number z
		// sets muErr true if overflow or error
		{
			FibN(Common.nList[zI], zI);
		}

		public static void RFacMBN(int zI) // z = z! = 1*2*3*...*x % FMB
		// sets muErr true if overflow or error
		{
			int fMBI;
			if (Common.FMBIok(zI, out fMBI))
				FacN(Common.nList[zI], zI, true, fMBI);
		}

		public static void RFacMBSN(int zI) // z = z! = 1*2*3*...*x % FMB
		// sets muErr true if overflow or error
		{
			int fMBI;
			if (Common.FMBIok(zI, out fMBI))
				FacSN(Common.nList[zI], zI, true, fMBI);
		}

		public static void RBetaN(int zI) // z = beta(z)
		// sets muErr true if overflow or error
		{
			BetaN(Common.nList[zI], zI);
		}

		public static void RErfN(int zI) // z = erf(z)
		// sets muErr true if overflow or error
		{
			ErfN(Common.nList[zI], zI);
		}

		public static void RErfCN(int zI) // z = erfc(z)
		// sets muErr true if overflow or error
		{
			ErfCN(Common.nList[zI], zI);
		}

		public static void REtaN(int zI) // z = eta(z)
		// sets muErr true if overflow or error
		{
			EtaN(Common.nList[zI], zI);
		}

		public static void RLambdaN(int zI) // z = lambda(z)
		// sets muErr true if overflow or error
		{
			LambdaN(Common.nList[zI], zI);
		}

		public static void RZetaN(int zI) // z = Zeta(z)
		// sets muErr true if overflow or error
		{
			ZetaN(Common.nList[zI], zI);
		}

		public static void RGamN(int zI) // z = gamma(z)
		// sets muErr true if overflow or error
		{
			GamN(Common.nList[zI], zI);
		}

		public static void RGamN1(int zI) // z = gamma(z), 16-digit gamma function
		// sets muErr true if overflow or error
		{
			GamN1(Common.nList[zI], zI);
		}

		public static void RPsiN(int zI) // z = psi(z)
		// sets muErr true if overflow or error
		{
			PsiN(Common.nList[zI], zI);
		}

		public static void CuRootN(MultiF x, int zI) // z = CuRt(x)
		// x is not changed, it is ok for x and z to have the same location in memory
		// but then x will be changed. Sets muErr true if error
		{
			if (x == MultiFD.fZero)
			{
				Calc.SetToN(MultiFD.fZero, zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			bool sign = x.S;
			x.S = false;
			LnN(x, zI);
			RDivN(MultiFD.fThree, zI);
			RExpN(zI);
			x.S = sign;
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void SqRootN(MultiF x, int zI) // z = SqRt(x)
		// x is not changed, it is ok for x and z to have the same location in memory
		// but then x will be changed. Sets muErr true if error
		{
			MultiF t = new MultiF(Calc.fMC); // expand done by SqRoorF
			MultiFD.SqRootF(x, t);
			Calc.SetToN(t, zI);
			MultiFD.DoneF(t);
		}

		public static void RCuRtN(int zI) // z = SqRt(z)
		// sets muErr true if error
		{
			CuRootN(Common.nList[zI], zI);
		}

		public static void RSqRtN(int zI) // z = SqRt(z)
		// sets muErr true if error
		{
			SqRootN(Common.nList[zI], zI);
		}

		public static void RSqRtRemN(int zI) // z = Floor(SquareRoot(z)) and set Re to remainder
		// sets muErr true if error
		{
			int rI;
			Common.LFindAdd("Re", out rI);
			if (rI == zI || rI == Common.iTop)
			{
				MultiID.MuWriteErr(
				"Cannot set same location to both SqRt and remainder, continuing...");
				return;
			}
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SqRootF(Common.nList[zI], t);
			if (MultiID.muErr)
			{
				Calc.SetToN(MultiFD.fZero, rI);
				Calc.SetToN(MultiFD.fZero, zI);
				return;
			}
			MultiFD.FloorF(t, t);
			MultiF r = new MultiF(Calc.fMC);
			r &= Common.nList[zI] - t * t;
			Calc.SetToN(t, zI);     // z = t = integer square root
			Calc.SetToN(r, rI);     // Re = r = remainder
		} // RSqRtRemN

		public static void InvN(MultiF x, int zI) // z = 1 / x
		{
			MultiF t = new MultiF(1);
			MultiFD.SetTo1F(1, t);
			DivN(t, x, zI);		// z will be resized if not large enough
			MultiFD.DoneF(t);
		}

		public static void RInvN(int zI) // z = 1 / z
		{
			InvN(Common.nList[zI], zI);
		}

		public static int GetPiPtr() // returns a pointer to pi on list
		// puts it on the list if not there
		{
			int pI;
			Common.LFind("Pi", out pI);
			if (pI != 0)
			{
				if (Common.nList[pI].N >= Calc.fMC)
				{
					if (Common.nList[pI].N > Calc.fMC)
						Calc.NormN(pI); // reduce precision
					return pI;
				}
			}
			if (pI == 0)
				Common.InitNAdd(1, "Pi", out pI);
			if (!Calc.ReadFile("Pi.XPN", pI))
			{
				MultiPi.PiAb(Common.nList[pI]);	// Compute x = Pi by Borwein algorithm b
			}
			if (Common.nList[pI].N != Calc.fMC)	// assumes pi does not have long strings of zeros
				MultiID.WriteLn("Error in Pi, N = " +
					(Common.nList[pI].N * MultiID.muDMax - 3) + ", M = " + (Calc.fMC * MultiID.muDMax - 3));
			return pI;
		} // GetPiPtr

		public static void SetToPiN(int zI) // compute z = pi
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int xI = GetPiPtr();
			Calc.SetToII(xI, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
		}

		public static int GetLn10Ptr() // returns a pointer to Ln10 on list
		// puts it on the list if not there
		{
			int ln10;
			Common.LFind("Ln10", out ln10);
			if (ln10 != 0)
			{
				if (Common.nList[ln10].N >= Calc.fMC)
				{
					if (Common.nList[ln10].N > Calc.fMC)
						Calc.NormN(ln10);		// reduce precision
					return ln10;
				}
			}
			if (ln10 == 0)
				Common.InitNAdd(1, "Ln10", out ln10);
			if (!Calc.ReadFile("Ln10.XPN", ln10))
			{
				MultiF ten = new MultiF(1);
				MultiFD.SetTo1F(1, ten);
				MultiFD.RDiv1F(10, ten);
				MLnLN(ten, false, ln10);	// take Ln of 0.1
				Common.ChSN(ln10);
				MultiFD.DoneF(ten);
			}
			if (Common.nList[ln10].N != Calc.fMC)
				MultiID.WriteLn("Error in Ln10, fMC = " + Calc.fMC);
			return ln10;
		} // GetLn10Ptr

		public static void SetToLn10N(int zI) // compute z = Ln10
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int xI = GetLn10Ptr();
			Calc.SetToII(xI, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
		}

		public static void SetFMBN(int zI) // set Modulo base FMB on Common.nList = value of zI
		{
			int fMBI;
			Common.LFindAdd("FMB", out fMBI);
			if (fMBI == zI) return;
			Calc.SetToII(zI, fMBI);
		}

		public static void ModFMBN(MultiF x, int zI) // z = x % FMB
		{
			int fMBI;
			if (Common.FMBIok(zI, out fMBI) && !Common.ZTestN(fMBI))
				RModN(Common.nList[fMBI], zI);
		}

		public static void RModFMBN(int zI) // z = z % FMB
		{
			ModFMBN(Common.nList[zI], zI);
		}

		public static void SSinN(MultiF x, int zI)	// z = Sin(x), small x
		// Sin(x) = x - x^3 / 3! + x^5 / 5! ...
		{
			bool fini;
			int j;
			bool sign;
			int saveFMC;
			int saveMMax;
			int currFMC;
			if (x.C < (-Int32.MaxValue / 3 - Int16.MaxValue))
			{
				Calc.SetToN(x, zI);
				if (Calc.degMode) RToRadN(zI);
				return;
			}
			saveFMC = Calc.fMC;
			saveMMax = MultiID.mMax;
			currFMC = Calc.fMC;
			MultiF nn = new MultiF(2);
			MultiF term = new MultiF(Calc.fMC);
			MultiF g = new MultiF(Calc.fMC);
			MultiF sum = new MultiF(Calc.fMC);
			sign = x.S;
			MultiFD.SetToF(x, term);	// term	= |x|
			term.S = false;
			MultiFD.SetTo1F(1, nn);		// nn	= 1
			MultiFD.SqF(term, g);		// g	= -Sq(term)
			MultiFD.ChSF(g);
			MultiFD.SetToF(term, sum);	// sum	= term
			fini = false;
			MultiID.nDone = 1;
			while (!fini)
			{
				MultiFD.RMulF(g, term);		// term = term * g / (nn + 1) / (nn + 2)
				MultiFD.RAdd1F(1, nn);		// nn++
				MultiFD.RDivF(nn, term);
				MultiFD.RAdd1F(1, nn);		// nn++
				MultiFD.RDivF(nn, term);
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				long iL = Calc.fMC - sum.N - sum.C + term.N + term.C;
				if (iL > Int32.MaxValue) iL = Int32.MaxValue;
				if (iL < Int32.MinValue) iL = Int32.MinValue;
				MultiID.nToGo = (int)iL;
				MultiID.nDone++;
				if (MultiID.statReq2) MultiID.WritenDone("SSinN: series");
				if (MultiID.nToGo < 0 || MultiFD.ZTestF(term) || MultiID.muErr || MultiID.muAbort)
					fini = true;
				else
				{
					MultiFD.RAddF(term, sum); // sum = sum + term
					Calc.fMC = (int)MultiID.nToGo + 1;
					if (Calc.fMC < currFMC)
					{
						currFMC = Calc.fMC;
						j = term.N - Calc.fMC;
						if (j > 0) MultiFD.ShiftR(j, term);
						j = g.N - Calc.fMC;
						if (j > 0) MultiFD.ShiftR(j, g);
					}
					else
					{
						Calc.fMC = currFMC;
					}
				}
			}
			MultiID.nToGo = 0;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.SetToN(sum, zI);
			MultiFD.DoneF(sum);
			MultiFD.DoneF(g);
			MultiFD.DoneF(term);
			MultiFD.DoneF(nn);
			if (Common.nList[zI].N > -Common.nList[zI].C && Common.nList[zI].N > 1)
				SetTo1N(1, zI);			// if |Sin(x)| > 1 then set to 1
			Common.nList[zI].S = sign;
		} // SSinN

		public static void ReportBest(int j) // compute + report best number of iterations
		// j = number of iterations used.
		// ti0 = Base time, ti1 = Delta time of iterations, ti2 = delta time to evaluate series
		{
			Calc.ti1 = MultiID.TimerF() - Calc.ti0 + Calc.ti1;
			if (Calc.ti1 > Calc.tic && Calc.ti2 > Calc.tic && !MultiID.muErr && !MultiID.muAbort)
			{
				Calc.ti0 = j * Math.Sqrt((Calc.ti2 - Calc.tic) / (Calc.ti1 + Calc.tic));	// Min
				Calc.ti2 = j * Math.Sqrt((Calc.ti2 + Calc.tic) / (Calc.ti1 - Calc.tic));	// Max
				Calc.ti1 = (Calc.ti0 + Calc.ti2) / 2;										// Ave
				Calc.ti2 = Calc.ti1 - Calc.ti0;												// Del
				MultiID.WriteLn("Best NN = " + Calc.ti1.ToString("0.000") + " +/- " + Calc.ti2.ToString("0.000"));
			}
		}

		public static void MSinN(MultiF x, int zI)	// z = Sin(x), medium small x
		// uses Sin(x) = Sin(x/3) * (3 - 4 * Sq(Sin(x/3))) nn times
		{
			int tI;
			int nn;
			double d;
			bool sign;
			int i;
			int j;
			int k;
			sign = x.S;
			MultiF e = new MultiF(2);
			Common.InitNAdd(1, "MSinN-tI", out tI);
			//			d = 2.7242 + Math.Sqrt(Calc.fMC * MultiID.muDMax) * 0.30700581817;	// PolyFit
			d = 2.7242 + Math.Sqrt(Calc.fMC * MultiID.muDMax) * 0.5;			// PolyFit
			Common.LFind("MSinNN", out nn);
			if (nn != 0) MultiFD.GetDbF(out d, Common.nList[nn]);
			j = (int)Math.Round(d);
			MultiID.ADebug("MSin: NN = " + d.ToString("0.000"));
			Calc.SetToN(x, tI);
			if (Calc.degMode) RToRadN(tI);
			Common.nList[tI].S = false;
			MultiFD.SetTo1F(2, e);
			for (i = 1; i <= j; i++)	// e = 2 / 3^nn
				MultiFD.RDivF(MultiFD.fThree, e);
			MultiID.nDone = 0;
			k = j;
			MultiID.nToGo = k;
			j = 0;
			Calc.ti0 = MultiID.TimerF();
			while (Common.nList[tI] > e)			// t = x / 3^nn, t > e
			{
				RDivN(MultiFD.fThree, tI);
				j++;
				if (MultiID.muErr || MultiID.muAbort) SetTo1N(0, tI);
				MultiID.nDone++;
				MultiID.nToGo = k - MultiID.nDone;
				if (MultiID.statReq2) MultiID.WritenDone("MSinN: reduce");
			}
			Calc.ti1 = MultiID.TimerF() - Calc.ti0;
			MultiID.ADebug("MSin: NN = " + j);
			Calc.ti0 = MultiID.TimerF();
			SSinN(Common.nList[tI], zI); // S0 = Sin(t)
			d = MultiID.TimerF();
			Calc.ti2 = d - Calc.ti0;
			Calc.ti0 = d;
			for (i = 1; i <= j; i++)	// Si = Si-1 * (3 - Sq(2 * Si-1)) i = 1, ..., nn
			{
				AddN(Common.nList[zI], Common.nList[zI], tI);
				RSqN(tI);
				Common.ChSN(tI);			// HJS 1992-11-28
				RAddN(MultiFD.fThree, tI);
				RMulN(Common.nList[tI], zI);
				MultiID.nToGo = j - i;
				MultiID.nDone = i;
				if (MultiID.statReq2) MultiID.WritenDone("MSinN: expand");
			}
			if (MultiID.diagOn) ReportBest(j);
			Common.LDelete(tI); // Sin(x) = Snn
			MultiFD.DoneF(e);
			if (Common.nList[zI].N > -Common.nList[zI].C && Common.nList[zI].N > 1)
				SetTo1N(1, zI); // if |Sin(x)| > 1 then set to 1
			Common.nList[zI].S = sign;
		} // MSinN

		public static void SinQuadN(MultiF x, out int qu, int zI)
		// z = Sin(x), qu = Quadrant (1..4), returned
		{
			int qM4;
			int qu1;
			int pI;
			int piO2;
			bool sign;
			int i;
			long d;
			int saveFR;
			bool onYAxis;
			qu = 0; // default value
			// Common.ALet("Inv(3) + 2"); Common.ALet("Sin = Inv(6)"); // for test only
			d = x.N;
			d += x.C;
			if (!MultiFD.expanded) MultiFD.fMCB = Calc.fMC;
			if (d > MultiFD.fMCB)
			{
				MultiID.MuWriteErr("Sin: Angle too large");
				Calc.ClearN(zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF t = new MultiF(Calc.fMC);
			Common.InitNAdd(1, "SinQuadN-piO2", out piO2);
			MultiF q = new MultiF(Calc.fMC);
			Common.InitNAdd(1, "SinQuadN-qM4", out qM4);
			sign = x.S;
			MultiFD.SetToF(x, t);
			t.S = false;
			saveFR = MultiFD.fRound;
			MultiFD.fRound = 0;
			if (Calc.degMode)
			{
				SetTo1N(90, piO2);
				MultiFD.IntF(t, q);
				MultiFD.FracF(t, t);
				long iL = q.N + q.C;
				if (iL > Int32.MaxValue) iL = Int32.MaxValue;
				if (iL < Int32.MinValue) iL = Int32.MinValue;
				i = (int)iL;
				MultiF t1 = new MultiF(i);
				MultiF t2 = new MultiF(i);
				MultiFD.SetToF(q, t1);
				MultiFD.ShiftL(t1.C, t1);
				MultiID.RDivQRSI(Common.nList[piO2], t2, t1);
				SetToNM(t1, q);
				MultiFD.RAddF(t2, t);
				MultiFD.DoneF(t2);
				MultiFD.DoneF(t1);
			}
			else
			{
				pI = GetPiPtr();
				Div1N(Common.nList[pI], 2, piO2);
				MultiFD.DivF(t, Common.nList[piO2], q);
				MultiFD.RIntF(q);
				MulN(q, Common.nList[piO2], qM4);
				MultiFD.RSubF(Common.nList[qM4], t);
			}
			SetTo1N(4, qM4);
			ModN(q, Common.nList[qM4], qM4);
			MultiFD.Get1F(out qu1, Common.nList[qM4]);
			qu = (int)qu1;
			qu++;
			onYAxis = false;
			if (qu == 2 || qu == 4)
			{
				if (MultiFD.ZTestF(t))
					onYAxis = true;
				else
				{
					MultiFD.SubF(Common.nList[piO2], t, t);
				}
			}
			if (qu == 3 || qu == 4) sign = !sign;
			Common.LDelete(qM4);
			MultiFD.DoneF(q);
			Common.LDelete(piO2);
			MultiFD.fRound = saveFR;
			if (MultiFD.ZTestF(t))
			{
				Calc.ClearN(zI);
				if (onYAxis)
				{
					Common.nList[zI].V[0] = 1;
				}
			}
			else
			{
				MSinN(t, zI);
			}
			if (!Common.ZTestN(zI)) Common.nList[zI].S = sign;
			MultiFD.DoneF(t);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // SinQuadN

		public static void SinN(MultiF x, int zI)	// z = Sin(x)
		{
			int qu;
			SinQuadN(x, out qu, zI);
		}

		public static void RSinN(int zI)	// z = Sin(z)
		{
			SinN(Common.nList[zI], zI);
		}

		public static void SignN(MultiF x, int zI)	// z = Sin(x)
		{
			int sign = MultiFD.SignF(x);
			MultiFD.SetTo1F(sign, Common.nList[zI]);
			Calc.NormN(zI);
		} // SignN

		public static void RSignN(int zI)	// z = Sin(z)
		{
			SignN(Common.nList[zI], zI);
		}

		public static void CosN(MultiF x, int zI)	// z = Cos(x)
		// Cos(x) = Sin(x + pi/2)
		{
			int pI;
			int piO2;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			if (Calc.degMode)
				Add1N(x, 90, zI);
			else
			{
				pI = GetPiPtr();
				Common.InitNAdd(1, "CosN-piO2", out piO2);
				Div1N(Common.nList[pI], 2, piO2);
				AddN(x, Common.nList[piO2], zI);
				Common.LDelete(piO2);
			}
			RSinN(zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // CosN

		public static void RCosN(int zI) // z = Cos(z)
		{
			CosN(Common.nList[zI], zI);
		}

		public static void TanN(MultiF x, int zI)	// z = Tan(x)
		// Tan(x) = Sin(x) / SqRt(1 - Sq(Sin(x))
		// and change sign of Tan(x) if in 2nd or 3rd quadrant
		{
			bool forCot = false;
			TanN(x, zI, ref forCot);
		}

		public static void TanN(MultiF x, int zI, ref bool forCot)	// z = Tan(x)
		// Tan(x) = Sin(x) / SqRt(1 - Sq(Sin(x))
		// and change sign of Tan(x) if in 2nd or 3rd quadrant
		{
			int qu;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			if (x.C < (-Int32.MaxValue / 3 - Int16.MaxValue))
			{
				Calc.SetToN(x, zI);
				if (Calc.degMode) RToRadN(zI);
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Calc.NormN(zI);
				return;
			}
			SinQuadN(x, out qu, zI);
			if (Common.ZTestN(zI))
			{
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				return;
			}
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SqF(Common.nList[zI], t);
			t.S = true;
			MultiFD.RAdd1F(1, t);	// 1 - Sin(x)^2
			if (MultiFD.ZTestF(t))
			{
				if (forCot)
					forCot = false;
				else
					MultiID.MuWriteErr("Tan(x) error: x equivalent to +/- 90 Deg.");
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Calc.ClearN(zI);
			}
			else
			{
				MultiFD.RSqRtF(t);
				RDivN(t, zI);
				if (qu == 2 || qu == 3) MultiFD.ChSF(Common.nList[zI]);
			}
			MultiFD.DoneF(t);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // TanN

		public static void RTanN(int zI) // z = Tan(z)
		{
			TanN(Common.nList[zI], zI);
		}

		public static void SATanN(MultiF x, int zI)	// z = ATan(x), small x
		// ATan(x) = x - x^3 / 3 + x^5 / 5 ...
		{
			bool fini;
			bool sign;
			int j;
			int saveFMC;
			int saveMMax;
			int currFMC;
			if (x.C < (-Int32.MaxValue / 3 - Int16.MaxValue))
			{
				Calc.SetToN(x, zI);
				if (Calc.degMode) RToDegN(zI);
				return;
			}
			saveFMC = Calc.fMC;
			saveMMax = MultiID.mMax;
			currFMC = Calc.fMC;
			MultiF nn = new MultiF(2);
			MultiF term = new MultiF(Calc.fMC);
			MultiF bTerm = new MultiF(Calc.fMC);
			MultiF g = new MultiF(Calc.fMC);
			MultiF sum = new MultiF(Calc.fMC);
			sign = x.S;
			MultiFD.SetToF(x, bTerm);				// bTerm = |x|
			bTerm.S = false;
			MultiFD.SetTo1F(1, nn);					// nn	= 1
			MultiFD.SqF(bTerm, g);					// g = -Sq(bTerm)
			MultiFD.ChSF(g);
			MultiFD.SetToF(bTerm, sum);				// sum = bTerm
			fini = false;
			MultiID.nDone = 1;
			while (!fini)
			{
				MultiFD.RMulF(g, bTerm);			// bTerm = bTerm * g
				MultiFD.RAdd1F(2, nn);				// nn	= nn + 2
				MultiFD.DivF(bTerm, nn, term);		// term = bTerm / nn
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				long iL = Calc.fMC - sum.N - sum.C + term.N + term.C;
				if (iL > Int32.MaxValue) iL = Int32.MaxValue;
				if (iL < Int32.MinValue) iL = Int32.MinValue;
				MultiID.nToGo = (int)iL;
				MultiID.nDone++;
				if (MultiID.nToGo < 0 || MultiFD.ZTestF(term) || MultiID.muErr || MultiID.muAbort)
					fini = true;
				else
				{
					MultiFD.RAddF(term, sum);		// sum = sum + term
					Calc.fMC = (int)MultiID.nToGo + 1;
					if (Calc.fMC < currFMC)
					{
						currFMC = Calc.fMC;
						j = term.N - Calc.fMC;
						if (j > 0) MultiFD.ShiftR(j, term);
						j = g.N - Calc.fMC;
						if (j > 0) MultiFD.ShiftR(j, g);
					}
					else
					{
						Calc.fMC = currFMC;
					}
					if (MultiID.statReq2) MultiID.WritenDone("SATanN: series");
				}
			}
			MultiID.nToGo = 0;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.SetToN(sum, zI);
			MultiFD.DoneF(sum);
			MultiFD.DoneF(g);
			MultiFD.DoneF(bTerm);
			MultiFD.DoneF(term);
			MultiFD.DoneF(nn);
			Common.nList[zI].S = sign;
		} // SATanN

		public static void MATanN(MultiF x, int zI)	// z = ATan(x), medium small x
		// uses Tan(x/2) = Tan(x) / (1 + SqRt(1 + Sq(Tan(x)), nn times
		{
			int nn;
			double d;
			bool sign;
			int i;
			int j;
			int k;
			sign = x.S;
			//			d = 1.5832 + Math.Sqrt(Calc.fMC * MultiID.muDMax) * 0.39892668084;	// PolyFit
			d = 1.5832 + Math.Sqrt(Calc.fMC * MultiID.muDMax) * 0.22;			// PolyFit
			Common.LFind("MATanNN", out nn);
			if (nn != 0) MultiFD.GetDbF(out d, Common.nList[nn]);
			j = (int)Math.Round(d);
			MultiID.ADebug("MATan: NN = " + d.ToString("0.000"));
			MultiF e = new MultiF(2);
			MultiF f = new MultiF(2);
			MultiFD.SetTo1F(15, e);			// e = 1.5
			MultiFD.RDiv1F(10, e);
			for (i = 1; i <= j; i++)		// e = e / (1 + SqRt(1 + Sq(e))), nn times
			{
				MultiFD.SqF(e, f);
				MultiFD.RAdd1F(1, f);
				MultiFD.RSqRtF(f);
				MultiFD.RAdd1F(1, f);
				MultiFD.RDivF(f, e);
			}
			MultiFD.DoneF(f);
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SetToF(x, t);
			t.S = false;
			MultiID.nDone = 0;
			k = j;
			MultiID.nToGo = k;
			j = 0;
			Calc.ti0 = MultiID.TimerF();
			while (t > e)					// t = t / (1 + SqRt(1 + Sq(t))), while t > e
			{
				SqN(t, zI);
				RAdd1N(1, zI);
				RSqRtN(zI);
				RAdd1N(1, zI);
				MultiFD.RDivF(Common.nList[zI], t);
				j++;
				if (MultiID.muErr || MultiID.muAbort) MultiFD.SetTo1F(0, t);
				MultiID.nDone++;
				MultiID.nToGo = k - MultiID.nDone;
				if (MultiID.statReq2) MultiID.WritenDone("MATanN: reduce");
			}
			Calc.ti1 = MultiID.TimerF() - Calc.ti0;
			MultiID.ADebug("MATan: NN = " + j);
			Calc.ti0 = MultiID.TimerF();
			SATanN(t, zI);					// A0 = ATan(t)
			d = MultiID.TimerF();
			Calc.ti2 = d - Calc.ti0;
			Calc.ti0 = d;
			MultiFD.DoneF(t);
			MultiFD.DoneF(e);
			for (i = 1; i <= j; i++)		// Ai = 2 * Ai-1 i = 1, ..., nn
			{
				RMul1N(2, zI);
				MultiID.nToGo = j - i;
				MultiID.nDone = i;
				if (MultiID.statReq2) MultiID.WritenDone("MATanN: expand");
			}
			if (MultiID.diagOn) ReportBest(j);
			Common.nList[zI].S = sign;
			if (Calc.degMode) RToDegN(zI);
		} // MATanN

		public static void ATanN(MultiF x, int zI)	// z = ATan(x)
		{
			int pI;
			int piO2;
			bool sign;
			bool XGT1;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF t = new MultiF(Calc.fMC);
			sign = x.S;
			MultiFD.SetToF(x, t);
			t.S = false;
			XGT1 = false;
			if (t > MultiFD.fOne)
			{
				MultiFD.DivF(MultiFD.fOne, t, t);
				XGT1 = true;
			}
			MATanN(t, zI);
			MultiFD.DoneF(t);
			if (XGT1)
			{
				if (Calc.degMode)
				{
					Common.ChSN(zI);
					RAdd1N(90, zI);
				}
				else
				{
					pI = GetPiPtr();
					Common.InitNAdd(1, "ATanN-piO2", out piO2);
					Div1N(Common.nList[pI], 2, piO2);
					SubN(Common.nList[piO2], Common.nList[zI], zI);
					Common.LDelete(piO2);
				}
			}
			Common.nList[zI].S = sign;
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ATanN

		public static void RATanN(int zI) // z = ATan(z)
		{
			ATanN(Common.nList[zI], zI);
		}

		public static void ATan2N(MultiF y, MultiF x, int zI)	// z = ATan(y / x)
		// Compute Arctan(y / x) from -pi to pi
		{
			int pI;
			bool xSign;
			bool ySign;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ySign = y.S;					// save sign of y because z and y may be the same memory
			if (MultiFD.ZTestF(x))
			{
				if (MultiFD.ZTestF(y))
				{
					Calc.ClearN(zI);
					MultiFD.expanded = saveExp;
					Calc.fMC = saveFMC;
					MultiID.mMax = saveMMax;
					return;
				}
				if (Calc.degMode)
					SetTo1N(90, zI);	// z = 90
				else
				{
					pI = GetPiPtr();
					Calc.SetToII(pI, zI);
					RDiv1N(2, zI);		// z = pi/2
				}
				Common.nList[zI].S = ySign;
			}
			else
			{
				xSign = x.S;
				DivN(y, x, zI);
				RATanN(zI);
				if (xSign)
				{
					if (Calc.degMode)
					{
						if (ySign)
							RAdd1N(-180, zI);
						else
						{
							RAdd1N(180, zI);
						}
					}
					else
					{
						pI = GetPiPtr();
						if (ySign)
							RSubN(Common.nList[pI], zI);
						else
						{
							RAddN(Common.nList[pI], zI);
						}
					}
				}
			}
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ATan2N

		public static void MagN(MultiF x, MultiF y, int zI)	// z = SqRt(Sq(x) + Sq(y))
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SqF(x, t);
			SqN(y, zI);
			RAddN(t, zI);
			MultiFD.DoneF(t);
			RSqRtN(zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void MeanCN(MultiF x, MultiF y, int zI)	// z = common mean of x and y
		{
			if (MultiFD.ZTestF(x) || MultiFD.ZTestF(y))
			{
				Calc.ClearN(zI); // common mean of zero and a is zero
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF a = new MultiF(Calc.fMC);
			MultiF g = new MultiF(Calc.fMC);
			MultiF s = new MultiF(Calc.fMC);
			MultiF ar = new MultiF(Calc.fMC - 2);
			MultiF gr = new MultiF(Calc.fMC - 2);
			a &= x;
			g &= y;
			a.S = false;
			g.S = false;
			for (int n = 1; true; n++)
			{
				MultiID.MuInterrupt();
				if (MultiID.statReq2)
				{
					Common.SingleSpace();
					MultiID.WriteT("MeanCN: Iteration = " + n);
					MultiID.TimeDel();
					MultiID.statReq2 = false;
				}
				if (MultiID.muAbort || MultiID.muErr)
				{
					Calc.ClearN(zI);
					break;					// exit for
				}
				s &= a + g;					// s = sum of a & g
				g &= a * g;					// g = product of a & g
				MultiFD.RSqRtF(g);			// g = geometric mean of a & g
				a &= s / 2;					// a = arithmetic mean of a & g
				MultiFD.SetToF(g, gr);		// gr = g reduced in precision
				MultiFD.SetToF(a, ar);		// ar = a reduced in precision
				if (ar == gr)				// if a = g
				{
					Calc.SetToN(a, zI);
					break;					// exit for
				}
			}
			MultiFD.DoneF(s);
			MultiFD.DoneF(g);
			MultiFD.DoneF(a);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} //MeanCN

		public static void MaxN(MultiF x, MultiF y, int zI)	// z = max of x and y
		{
			if (x >= y)
				Calc.SetToN(x, zI);
			else
				Calc.SetToN(y, zI);
		} //MaxN

		public static void MinN(MultiF x, MultiF y, int zI)	// z = min of x and y
		{
			if (x >= y)
				Calc.SetToN(y, zI);
			else
				Calc.SetToN(x, zI);
		} //MinN

		public static void Mod(MultiF x, MultiF y, MultiF z)	// z = Mod(x, y))
		// same as FORTRAN definition: z = Mod(x, y) = x - (Floor(x/y) * y)
		// sign(z) = sign(y) or z = 0
		{
			MultiF ySave = new MultiF(y.N);
			ySave &= y;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int tI;
			Common.InitNAdd(1, "ModN-tI", out tI);
			FloorN(x/y, tI);
			RMulN(-y, tI);
			RAddN(x, tI);
			SetToNM(Common.nList[tI], z); // z could over store y
			if (z.S != ySave.S)
				MultiFD.ClearF(z);
			else
			{
				ySave.S = false;
				Common.nList[tI].S = false;
				if (Common.nList[tI] >= ySave)
					MultiFD.ClearF(z);
			}
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			NormNM(z);
		} // Mod

		public static void ModN(MultiF x, MultiF y, int zI)	// z = Mod(x, y))
		// same as FORTRAN definition: z = Mod(x, y) = x - (Floor(x/y) * y)
		// sign(z) = sign(y) or z = 0
		{
			MultiF zz = new MultiF();
			Mod(x, y, zz);
			Calc.SetToN(zz, zI);
		}
		// ModN

		public static void RModN(MultiF x, int zI) // z = Mod(z, x))
		{
			ModN(Common.nList[zI], x, zI);
		}

		public static void GamPN(MultiF a, MultiF x, int zI) // z = GamP(a, x)
		// GamP(a, x) is the lower regularized incomplete gamma function with parameter a and argument x
		{
			if (MultiFD.ZTestF(a))	// a = 0
			{
				if (MultiFD.ZTestF(x))
				{
					Calc.ClearN(zI);	// GamP(0, 0) = 0
					return;
				}
				Calc.SetToN(MultiFD.fOne, zI);	// GamP(0, x!=0) = 1;
				return;
			}
			if (a.C >= 0 && a.S)	// a = -int
			{
				if (MultiFD.ZTestF(x))
				{
					MultiID.MuWriteErr("Cannot take GamP(a, 0) if a is an integer < zero");
					Calc.ClearN(zI);	// GamP(-int, 0) not defined
					return;
				}
				Calc.SetToN(MultiFD.fOne, zI);	// GamP(-int, x!=0) = 1;
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int tI, t2I;
			Common.InitNAdd(1, "GamPN-tI", out tI);
			Common.InitNAdd(1, "GamPN-t2I", out t2I);
			GamLN(a, x, tI);
			GamN(a, t2I);
			MultiF b = new MultiF(Calc.fMC);
			MultiFD.DivF(Common.nList[tI], Common.nList[t2I], b);	// GamP(a, x) = GamL(a, x) / Gam(a)
			NormNM(b);
			Calc.SetToN(b, zI);
			MultiFD.DoneF(b);
			Common.LDelete(t2I);
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // GamPN

		public static void GamQN(MultiF a, MultiF x, int zI) // z = GamQ(a, x)
		// GamQ(a, x) is the upper regularized incomplete gamma function with parameter a and argument x
		{
			if (MultiFD.ZTestF(a))	// a = 0
			{
				if (MultiFD.ZTestF(x))
				{
					Calc.SetToN(MultiFD.fOne, zI);	// GamQ(0, 0) = 1
					return;
				}
				Calc.ClearN(zI);	// GamQ(0, x!=0) = 0;
				return;
			}
			if (a.C >= 0 && a.S)	// a = -int
			{
				if (MultiFD.ZTestF(x))
				{
					MultiID.MuWriteErr("Cannot take GamQ(a, 0) if a is an integer < zero");
					Calc.ClearN(zI);	// GamQ(-int, 0) not defined
					return;
				}
				Calc.ClearN(zI);	// GamP(-int, x!=0) = 0;
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int tI, t2I;
			Common.InitNAdd(1, "GamQN-tI", out tI);
			Common.InitNAdd(1, "GamQN-t2I", out t2I);
			GamUN(a, x, tI);
			GamN(a, t2I);
			MultiF b = new MultiF(Calc.fMC);
			MultiFD.DivF(Common.nList[tI], Common.nList[t2I], b);	// GamQ(a, x) = GamU(a, x) / Gam(a)
			NormNM(b);
			Calc.SetToN(b, zI);
			MultiFD.DoneF(b);
			Common.LDelete(t2I);
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // GamQN

		public static void GamLN(MultiF a, MultiF x, int zI) // z = GamL(a, x)
		// GamL(a, x) is the lower incomplete gamma function with parameter a and argument x
		{
			if (MultiFD.ZTestF(a) && MultiFD.ZTestF(x))
			{
				Calc.ClearN(zI);	// GamL(0, 0) = 0
				return;
			}
			if ((a.C >= 0 && a.S) || MultiFD.ZTestF(a))
			{
				MultiID.MuWriteErr("Cannot take GamL(a, x) if a is an integer <= zero, except GamL(0, 0) = 0");
				Calc.ClearN(zI);
				return;
			}
			if (MultiFD.ZTestF(x))
			{
				Calc.ClearN(zI);	// GamL(a, 0) = 0 if a is not a neg integer
				return;
			}
			if (x.S) // x < 0.0)
			{
				MultiID.MuWriteErr("Cannot take GamL(a, x) if x < 0.0");
				Calc.ClearN(zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF b = new MultiF(Calc.fMC);
			b &= a + 1;
			MultiF lim = new MultiF(1);
			lim &= 100;
			if (x < b || x < lim)			// if x < a + 1 or x < 100
			{
				GamLS(a, x, b);				// b = GamL(a, x) by series
			}
			else
			{
				int tI;
				Common.InitNAdd(1, "GamLN-tI", out tI);
				GamUC(a, x, b);				// b = GamU(a, x) by continued fraction
				GamNF(a, tI);				// t = Gamma(a)
				b &= Common.nList[tI] - b;	// b = t - b = Gamma(a) - GamU(a, x)
				Common.LDelete(tI);
			}
			NormNM(b);
			Calc.SetToN(b, zI);
			MultiFD.DoneF(b);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // GamLN

		public static void GamUN(MultiF a, MultiF x, int zI) // z = GamU(a, x)
		// GamU(a, x) is the upper incomplete gamma function with parameter a and argument x
		{
			if (MultiFD.ZTestF(a)) // GamU(0, x != 0) = E1(x)
			{
				if (MultiFD.ZTestF(x))
				{
					MultiID.MuWriteErr("Cannot take GamU(0, 0)");
					Calc.ClearN(zI);
				}
				else
					E1N(x, zI);
				return;
			}
			if (a.C >= 0 && a.S)
			{
				MultiID.MuWriteErr("Cannot take GamU(a, x) if a is an integer < zero");
				Calc.ClearN(zI);
				return;
			}
			if (MultiFD.ZTestF(x))
			{
				GamN(a, zI);	// GamU(a, 0) = Gam(a)
				return;
			}
			if (x.S)			// x < 0.0)
			{
				MultiID.MuWriteErr("Cannot take GamU(a, x) if x < 0.0 and a != 0");
				Calc.ClearN(zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF b = new MultiF(Calc.fMC);
			b &= a + 1;
			MultiF lim = new MultiF(1);
			lim &= 100;
			if (x < b || x < lim)				// if x < a + 1 or x < 100
			{
				int tI;
				Common.InitNAdd(1, "GamLN-tI", out tI);
				GamLS(a, x, b);					// b = GamL(a, x) by series
				GamNF(a, tI);					// t = Gamma(a)
				b &= Common.nList[tI] - b;		// b = t - b = Gamma(a) - GamU(a, x)
				Common.LDelete(tI);
			}
			else
			{
				GamUC(a, x, b);					// b = GamU(a, x) by continued fraction
			}
			NormNM(b);
			Calc.SetToN(b, zI);
			MultiFD.DoneF(b);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // GamUN

		public static void GamUC(MultiF a, MultiF x, MultiF z) // z = GamU(a, x)
		// evaluated by its continued fraction representation
		{
			MultiF g = new MultiF(Calc.fMC);
			MultiF gr = new MultiF(Calc.fMC - 2);
			MultiF gOld = new MultiF(Calc.fMC - 2);
			MultiF a0 = new MultiF(Calc.fMC);
			MultiF a1 = new MultiF(Calc.fMC);
			MultiF b0 = new MultiF(Calc.fMC);
			MultiF b1 = new MultiF(Calc.fMC);
			MultiF fac = new MultiF(Calc.fMC);
			MultiF anf = new MultiF(Calc.fMC);
			MultiF an = new MultiF(Calc.fMC);
			MultiF ana = new MultiF(Calc.fMC);
			MultiF t = new MultiF(Calc.fMC);
			gOld &= 0;
			a0 &= 1;
			a1 &= x;
			b0 &= 0;
			b1 &= 1;
			fac &= 1;
			an &= 0;
			for (int n = 1; true; n++)
			{
				MultiID.MuInterrupt();
				if (MultiID.statReq2)
				{
					Common.SingleSpace();
					MultiID.WriteT("GamUC: n = " + n);
					MultiID.TimeDel();
					MultiID.statReq2 = false;
				}
				if (MultiID.muAbort || MultiID.muErr)
				{
					z &= 0;
					break;								// exit for
				}
				MultiFD.AddF(an, MultiFD.fOne, an);		// an = n
				MultiFD.SubF(an, a, ana);				// ana = an - a;
				MultiFD.MulF(a0, ana, a0);				// a0 = (a1 + a0 * ana) * fac
				MultiFD.AddF(a0, a1, a0);
				MultiFD.MulF(a0, fac, a0);
				MultiFD.MulF(b0, ana, b0);				// b0 = (b1 + b0 * ana) * fac
				MultiFD.AddF(b0, b1, b0);
				MultiFD.MulF(b0, fac, b0);
				MultiFD.MulF(an, fac, anf);				// anf = an * fac
				MultiFD.MulF(x, a0, t);					// a1 = x * a0 + anf * a1
				MultiFD.MulF(anf, a1, a1);
				MultiFD.AddF(t, a1, a1);
				MultiFD.MulF(x, b0, t);					// b1 | x * b0 + anf * b1
				MultiFD.MulF(anf, b1, b1);
				MultiFD.AddF(t, b1, b1);
				if (!MultiFD.ZTestF(a1))
				{
					MultiFD.DivF(MultiFD.fOne, a1, fac);	// fac = 1 / a1
					MultiFD.MulF(b1, fac, g);				// g = b1 * fac
					MultiFD.SetToF(g, gr);					// gr = g reduced in precision
					if (gr == gOld)							// if gr == gOld
					{
						int tI;
						Common.InitNAdd(1, "GamUC-tI", out tI);
						LnN(x, tI);									// t = ln(x)
						an &= a * Common.nList[tI] - x;				// an = a * ln(x) - x
						ExpN(an, tI);								// t = exp(a * ln(x) - x)
						MultiFD.MulF(g, Common.nList[tI], z);		// z = g * t
						Common.LDelete(tI);
						break;
					}
					gOld &= gr;										// save old gr
				}
			}
			MultiFD.DoneF(t);
			MultiFD.DoneF(ana);
			MultiFD.DoneF(an);
			MultiFD.DoneF(anf);
			MultiFD.DoneF(fac);
			MultiFD.DoneF(b1);
			MultiFD.DoneF(b0);
			MultiFD.DoneF(a1);
			MultiFD.DoneF(a0);
			MultiFD.DoneF(gOld);
			MultiFD.DoneF(gr);
			MultiFD.DoneF(g);
		} // GamUC

		public static void GamLS(MultiF a, MultiF x, MultiF z) // z = GamL(a, x)
		// evaluated by its series representation
		{
			int n;
			MultiF sum = new MultiF(Calc.fMC);
			MultiF del = new MultiF(Calc.fMC);
			MultiF aP = new MultiF(Calc.fMC);
			MultiF b = new MultiF(Calc.fMC);
			if (MultiFD.ZTestF(x))
			{
				z &= 0;
				return;
			}
			aP &= a;
			MultiFD.DivF(MultiFD.fOne, a, sum);		// sum = 1 / a
			del &= sum;								// del = sum
			for (n = 1; ; n++)
			{
				MultiID.MuInterrupt();
				if (MultiID.statReq2)
				{
					Common.SingleSpace();
					MultiID.Write("GamLS: n = " + n);
					MultiID.TimeDel();
					MultiID.statReq2 = false;
				}
				if (MultiID.muAbort || MultiID.muErr)
				{
					z &= 0;
					break;						// exit for
				}
				aP += 1;
				MultiFD.DivF(x, aP, b);			// b = x / aP
				MultiFD.MulF(del, b, del);		// del = del * x / aP
				b &= sum;						// b = previous sum
				sum += del;
				del &= sum - b;
				if (MultiFD.ZTestF(del))
				{
					int tI;
					Common.InitNAdd(1, "GamLS-tI", out tI);
					LnN(x, tI);									// t = ln(x)
					aP &= a * Common.nList[tI] - x;				// aP = a * ln(x) - x
					ExpN(aP, tI);								// t = exp(a * ln(x) - x)
					MultiFD.MulF(sum, Common.nList[tI], z);		// z = sum * t
					Common.LDelete(tI);
					break;
				}
			}
			// MultiID.MuWriteErr("GamLS: a too large");
			MultiFD.DoneF(b);
			MultiFD.DoneF(aP);
			MultiFD.DoneF(del);
			MultiFD.DoneF(sum);
		} // GamLS

		public static void BinoN(MultiF x, MultiF y, int zI)
		// z = Binomial coefficient (x, y), uses the integer part of y
		{
			MultiF yL = new MultiF((int)Math.Min(y.N, y.N + y.C));
			MultiFD.IntF(y, yL);						// 1: yL = integer part of y
			if (yL.S)									// 2: (x, y) = 0 for all x if y < 0
			{
				MultiFD.DoneF(yL);
				Calc.ClearN(zI);
				return;
			}
			bool sign = false;							// set sign +
			bool xInt = false;
			MultiF xL = new MultiF(Math.Max(x.N, y.N) + 1);
			xL &= x;
			if (xL.C >= 0)								// 3: if x is an integer
			{
				xInt = true;
				if (xL.S)								// 3.1: if x < 0
				{
					xL &= yL - xL - 1;					// (x, y) = (-1)^y * (y-x-1, y)
					sign = yL.C == 0 && (yL.V[0] & 1) == 1; // set sign true if y is odd
				}
				if (yL > xL)							// 3.2: if y > x
				{
					MultiFD.DoneF(xL);
					MultiFD.DoneF(yL);
					Calc.ClearN(zI);					// (x, y) = 0
					return;
				}
				if (yL + yL > xL)						// 3.3: y > x / 2
					yL &= xL - yL;						// y = x - y
			}
			if (MultiFD.ZTestF(yL))						// 4: (x, 0) = 1 for all x
			{
				Calc.SetToN(MultiFD.fOne, zI);
				Common.nList[zI].S = sign;
				MultiFD.DoneF(xL);
				MultiFD.DoneF(yL);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF max = new MultiF(4);
			max &= Int32.MaxValue;
			if (yL > max)								// 5: y > 2^31 -1
				{
				Calc.ClearN(zI);						// (x, y) = 0 with error message
				MultiID.MuWriteErr("BinoN: numbers too large for binomial coefficient");
				MultiFD.DoneF(max);
			}
			else
			{
				MultiF v = new MultiF(BinoFMC(xL, yL));
				MultiF nf = new MultiF(xL.N + Math.Abs((int)xL.C));	// 6: (x, y) = x * (x-1) * (x-2) * ... * (x-y+1)
				MultiF df = new MultiF(yL.N + (int)yL.C);			//			/ 1 * 2 * ... * n
				nf &= xL;
				df &= 1;
				v &= nf;
				double yd;
				MultiFD.GetDbF(out yd, yL);
				int yi = (int)yd;
				for (int i = 1; i < yi; i++)
				{
					MultiID.MuInterrupt();
					if (MultiID.muAbort)
					{
						MultiFD.ClearF(v);
						sign = false;
						break;
					}
					MultiFD.AddF(nf, MultiFD.fMOne, nf);	// nf = nf - 1
					MultiFD.AddF(df, MultiFD.fOne, df);		// df = df + 1
					MultiFD.MulF(v, nf, v);					// v = (v * nf) / df
					MultiFD.DivF(v, df, v);
					if (MultiID.statReq3)
					{
						MultiID.nDone = i;
						MultiID.nToGo = yi - i - 1;
						MultiID.WritenDone("BinoN:");
						MultiID.statReq3 = false;
					}
				}
				Calc.SetToN(v, zI);
				if (xInt) Common.nList[zI].S = sign;
				MultiFD.DoneF(df);
				MultiFD.DoneF(nf);
				MultiFD.DoneF(v);
			}
			MultiFD.DoneF(xL);
			MultiFD.DoneF(yL);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // BinoN

		public static void BetaCN(MultiF x, MultiF y, int zI)
		// z = the complete beta function B(x, y) = gamma(x) * gamma(y) / gamma(x+y)
		// also equal to 1 / (x * Bino(x+y-1, y-1)
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF xL = new MultiF(Calc.fMC);
			xL &= x + y;
			bool gxInf = (x.C >= 0 && x.S) || MultiFD.ZTestF(x);		// gamma(x) is infinite
			bool gyInf = (y.C >= 0 && y.S) || MultiFD.ZTestF(y);		// gamma(y) is infinite
			bool gxPyInf = (xL.C >= 0 && xL.S) || MultiFD.ZTestF(xL);	// gamma(x+y) is infinite
			if (!gxInf && !gyInf && gxPyInf)
			{
				Calc.ClearN(zI);					// finite / infinity = 0
			}
			else
			{
				if ( ((gxInf || gyInf) && !gxPyInf) || (gxInf && gyInf) )	// infinity / finite = infinity
				{															// infinity * infinity / v = infinity
					MultiID.MuWriteErr("BetaC(x, y) error: BetaC is infinite");
					Calc.ClearN(zI);
				}
				else
				{
					if (gxPyInf)											// finite * infinity / infinity = ?
					{
						if (gxInf)
						{
							MultiF t = x; // interchange pointers to x and y so gamma(y) is infinity
							x = y;
							y = t;
						}
						bool sign = (y.C == 0 && ((y.V[0] & 1) == 1)) ^		// set sign true if y is odd
							(xL.C == 0 && ((xL.V[0] & 1) == 1));			// exclusive-or x+y is odd
						xL &= 1 - xL;										// xL = 1 - (x+y)
						MultiF yL2 = new MultiF(Calc.fMC);
						yL2 &= 1 - y;										// yL2 = 1 - y
						int t2I;
						Common.InitNAdd(1, "BetaCN-t2I", out t2I);			// t2 = temp on the list
						GamN(xL, t2I);
						xL &= Common.nList[t2I];							// xL = gamma(1 - (x+y))
						GamN(x, t2I);
						xL *= Common.nList[t2I];							// xL = gamma(x) * gamma(1 - (x+y))
						GamN(yL2, t2I);
						xL.S = sign;
						DivN(xL, Common.nList[t2I], zI);					// B = xL / gamma(1 - y)
						Common.LDelete(t2I);
						MultiFD.DoneF(yL2);
					}
					else
					{
						xL -= 1;
						MultiF yL = new MultiF(Calc.fMC);
						yL &= y - 1;
						int tI;
						Common.InitNAdd(1, "BetaCN-tI", out tI);			// t = temp on the list
						BinoGN(xL, yL, tI);
						xL &= x * Common.nList[tI];
						InvN(xL, zI);
						Common.LDelete(tI);
						MultiFD.DoneF(yL);
					}
				}
			}
			MultiFD.DoneF(xL);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // BetaCN

		public static void BinoGN(MultiF x, MultiF y, int zI)
		// z = Binomial coefficient (x, y) using the gamma function
		{
			if (MultiFD.ZTestF(y) || x == y)
			{
				Calc.SetToN(MultiFD.fOne, zI);	// (x, 0) = (x, x) = 1 for all x
				return;
			}
			if (y == MultiFD.fOne)
			{
				Calc.SetToN(x, zI);				// (x, 1) = x for all x
				return;
			}
			bool sign = false;
			MultiF xL = new MultiF(Calc.fMC);
			MultiF yL = new MultiF(Calc.fMC);
			xL &= x;
			yL &= y;
			if (yL.C >= 0)						// if y is an integer
			{
				if (yL.S)						// if y is a negative integer, (x, y) = 0
				{
					MultiFD.DoneF(yL);
					MultiFD.DoneF(xL);
					Calc.ClearN(zI);
					return;
				}
				if (xL.S)										// 3.1: if x < 0
				{
					xL &= yL - xL - 1;							// (x, y) = (-1)^y * (y-x-1, y)
					sign = (yL.C == 0 && (yL.V[0] & 1) == 1);	// set sign true if y is odd
					if (xL == yL)
					{
						MultiFD.DoneF(yL);
						MultiFD.DoneF(xL);
						Calc.SetToN(MultiFD.fOne, zI);			// (a, a) = 1 for all a
						Common.nList[zI].S = sign;
						return;
					}
				}
				if (xL.C >= 0)									// if x and y are both integers
				{
					if (yL > xL)								// 3.2: if y > x
					{
						MultiFD.DoneF(yL);
						MultiFD.DoneF(xL);
						Calc.ClearN(zI);						// (x, y) = 0
						return;
					}
					if (yL + yL > xL)
						yL &= xL - yL;							// make y smaller for Factorial function
				}
				int fMC = FacFMC(yL, false);
				LimFac(fMC);
				MultiF lim = new MultiF(4);
				lim &= limFac;
				if (yL <= lim)									// if y is a small integers
				{
					MultiFD.DoneF(lim);
					BinoN(xL, yL, zI);
					MultiFD.DoneF(yL);
					MultiFD.DoneF(xL);
					Common.nList[zI].S = Common.nList[zI].S ^ sign; // ^ is xor
					return;
				}
				MultiFD.DoneF(lim);
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF yT = new MultiF(Calc.fMC);
			yT &= xL - yL;				// yT = x - y
			if (yT.C >= 0 && yT.S)		// if gamma of x - y + 1 would be infinite
			{
				Calc.ClearN(zI);		// divide by infinity gives zero
			}
			else
			{
				if (yT == MultiFD.fOne)
				{
					Calc.SetToN(xL, zI);	// (a, a-1) = a for all a
				}
				else
				{
					yT &= yT + 1;
					xL &= xL + 1;
					int tI;
					Common.InitNAdd(1, "BinoGN-tI", out tI); // t = temp on the list
					GamNF(xL, tI);
					if (MultiID.muErr || MultiID.muAbort)
					{
						Calc.ClearN(zI);
					}
					else
					{
						int t2I;
						Common.InitNAdd(1, "BinoGN-t2I", out t2I); // t2 = temp on the list
						GamNF(yT, t2I);
						MultiF v = new MultiF(Calc.fMC);
						v &= Common.nList[tI] / Common.nList[t2I];
						yT &= yL + 1;
						GamNF(yT, t2I);
						v /= Common.nList[t2I];
						Calc.SetToN(v, zI);
						Common.nList[zI].S = Common.nList[zI].S ^ sign; // ^ is xor
						MultiFD.DoneF(v);
						Common.LDelete(t2I);
					}
					Common.LDelete(tI);
				}
			}
			MultiFD.DoneF(yT);
			MultiFD.DoneF(yL);
			MultiFD.DoneF(xL);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // BinoGN

		public static void GCD(MultiF a, MultiF b, MultiF z) // z = GCD(a, b))
		{
			if (MultiFD.ZTestF(a))
				SetToNM(b, z);
			else if (MultiFD.ZTestF(b))
				SetToNM(a, z);
			else
			{
				bool fini = false;
				int tot = 0;
				int nn = 0;
				MultiF aL = new MultiF();
				MultiF bL = new MultiF();
				SetToNM(a, aL);
				SetToNM(b, bL);
				aL.S = false;
				bL.S = false;
				if (MultiID.debug) Common.SingleSpace();
				while (!fini)
				{
					nn++;
					if ((nn & 1) != 0)			// Odd(nn)
					{
						Mod(aL, bL, aL);
						if (MultiID.debug)
						{
							MultiID.WriteT(nn + " ", ref tot);
							MultiFD.WriteF(aL, ref tot);
							MultiID.WriteLn();
							tot = 0;
						}
						if (MultiFD.ZTestF(aL))
						{
							SetToNM(bL, z);
							fini = true;
						}
					}
					else
					{
						Mod(bL, aL, bL);
						if (MultiID.debug)
						{
							MultiID.WriteT(nn + " ", ref tot);
							MultiFD.WriteF(bL, ref tot);
							MultiID.WriteLn();
							tot = 0;
						}
						if (MultiFD.ZTestF(bL))
						{
							SetToNM(aL, z);
							fini = true;
						}
					}
				}
			}
		} // GCD

		public static void GCDN(MultiF a, MultiF b, int zI) // z = GCD(a, b))
		{
			MultiF zz = new MultiF();
			GCD(a, b, zz);
			Calc.SetToN(zz, zI);		}
		// GCDN

		public static void LCM(MultiF a, MultiF b, MultiF z) // z = LCM(a, b))
		// LCM(a, b) = a * b / GCD(a, b)
		{
			MultiF zz = new MultiF();
			GCD(a, b, zz);
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			SetToNM((a * b) / zz, z);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			NormNM(z);
		} // LCM

		public static void LCMN(MultiF a, MultiF b, int zI) // z = LCM(a, b))
		{
			MultiF zz = new MultiF();
			LCM(a, b, zz);
			Calc.SetToN(zz, zI);
		} // LCMN

		public static void ASinN(MultiF x, int zI)	// z = ASin(x)
		// ASin(S) = ATan2(S, SqRt(1 - Sq(S)), but if Sq(S) > 1 then Error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			if (x.C < (-Int32.MaxValue / 3 - Int16.MaxValue))
			{
				Calc.SetToN(x, zI);
				if (Calc.degMode) RToDegN(zI);
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Calc.NormN(zI);
				return;
			}
			bool saveRep = MultiID.muErrRep;
			MultiID.muErrRep = false;
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SqF(x, t);
			t.S = true;
			MultiFD.RAdd1F(1, t); // 1 - S^2
			MultiID.muErrRep = saveRep;
			if (MultiID.muErr || t.S)
			{
				MultiID.MuWriteErr("ASin(x) error: |x| > 1.0");
				Calc.ClearN(zI);
			}
			else
			{
				MultiFD.RSqRtF(t);
				ATan2N(x, t, zI);
			}
			MultiFD.DoneF(t);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ASinN

		public static void RASinN(int zI) // z = ASin(z)
		{
			ASinN(Common.nList[zI], zI);
		}

		public static void ACosN(MultiF x, int zI)	// z = ACos(x)
		// ACos(C) = ATan2(SqRt(1 - Sq(C), C), but if Sq(C) > 1 then Error
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			bool saveRep = MultiID.muErrRep;
			MultiID.muErrRep = false;
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SqF(x, t);
			t.S = true;
			MultiFD.RAdd1F(1, t); // 1 - C^2
			MultiID.muErrRep = saveRep;
			if (MultiID.muErr || t.S)
			{
				MultiID.MuWriteErr("ACos(x) error: |x| > 1.0");
				Calc.ClearN(zI);
			}
			else
			{
				MultiFD.RSqRtF(t);
				ATan2N(t, x, zI);
			}
			MultiFD.DoneF(t);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ACosN

		public static void RACosN(int zI) // z = ACos(z)
		{
			ACosN(Common.nList[zI], zI);
		}

		public static void SExpLN(MultiF x, bool bL1, int zI)
		// z= (e^x) - 1, small x, (- 1 only if bL1 is true)
		// Exp(x) - 1 = x + x^2/2! + x^3/3! + ...
		{
			int saveFMC;
			int saveMMax;
			int currFMC;
			bool fini;
			int j;
			if ( bL1 && (x.C < (-Int32.MaxValue / 2 - Int16.MaxValue)) )
			{
				Calc.SetToN(x, zI);
				return;
			}
			saveFMC = Calc.fMC;
			saveMMax = MultiID.mMax;
			currFMC = Calc.fMC;
			MultiF nn = new MultiF(2);
			MultiF term = new MultiF(Calc.fMC);
			MultiF g = new MultiF(Calc.fMC);
			MultiF sum = new MultiF(Calc.fMC);
			MultiFD.SetTo1F(1, nn);				// nn	= 1
			MultiFD.SetToF(x, term);			// term	= x
			MultiFD.SetToF(term, g);			// g	= term
			MultiFD.SetToF(term, sum);			// sum	= term
			if (!bL1) MultiFD.RAdd1F(1, sum);
			fini = false;
			MultiID.nDone = 1;
			while (!fini)
			{
				MultiFD.RAdd1F(1, nn);			// nn++
				MultiFD.RMulF(g, term);			// term = (term * g) / nn
				MultiFD.RDivF(nn, term);
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				long iL = Calc.fMC - sum.N - sum.C + term.N + term.C;
				if (iL > Int32.MaxValue) iL = Int32.MaxValue;
				if (iL < Int32.MinValue) iL = Int32.MinValue;
				MultiID.nToGo = (int)iL;
				MultiID.nDone++;
				if (MultiID.nToGo < 0 || MultiFD.ZTestF(term) || MultiID.muErr || MultiID.muAbort)
					fini = true;
				else
				{
					MultiFD.RAddF(term, sum);	// sum = sum + term
					Calc.fMC = (int)MultiID.nToGo + 1;
					if (Calc.fMC < currFMC)
					{
						currFMC = Calc.fMC;
						j = term.N - Calc.fMC;
						if (j > 0)
							MultiFD.ShiftR(j, term);
						j = g.N - Calc.fMC;
						if (j > 0)
						{
							MultiFD.ShiftR(j, g);
						}
					}
					else
					{
						Calc.fMC = currFMC;
					}
				}
				if (MultiID.statReq2) MultiID.WritenDone("SExpLN: series");
			}
			MultiID.nToGo = 0;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.SetToN(sum, zI);
			MultiFD.DoneF(sum);
			MultiFD.DoneF(g);
			MultiFD.DoneF(term);
			MultiFD.DoneF(nn);
		} // SExpLN

		public static void MExpLN(MultiF x, bool bL1, int zI)
		// z = (e^x) - 1, medium small x, (- 1 only if bL1 is true)
		// Exp(x) = Exp(x / 2^N) Squared N times
		// since (1+a)^2 = 1 + 2*a + a*a = 1 + a * (2 + a)
		// a0 = (e^x) - 1
		// a1 = a0 * (2 + a0)
		// a2 = a0 * (2 + a1) etc.
		{
			int nn;
			double d;
			int i;
			int j;
			int k;
			//			d = 3.418 + Math.Sqrt(Calc.fMC * MultiID.muDMax) * 0.90631455143;	// PolyFit
			d = 3.418 + Math.Sqrt(Calc.fMC * MultiID.muDMax) * 0.9;				// PolyFit
			Common.LFind("MExpLNN", out nn);
			if (nn != 0) MultiFD.GetDbF(out d, Common.nList[nn]);
			j = (int)Math.Round(d);
			MultiID.ADebug("MExpL: NN = " + d.ToString("0.000"));
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SetToF(x, t);
			MultiF e = new MultiF(2);
			MultiFD.SetTo1F(3, e);
			for (i = 1; i <= j; i++)			// e = e / 2, nn times
				MultiFD.RDiv1F(2, e);
			MultiID.nDone = 0;
			k = j;
			MultiID.nToGo = k;
			j = 0;
			Calc.ti0 = MultiID.TimerF();
			while (t > e)						// t = t / 2, while t > e
			{
				MultiFD.RDiv1F(2, t);
				j++;
				if (MultiID.muErr || MultiID.muAbort) MultiFD.SetTo1F(0, t);
				MultiID.nDone++;
				MultiID.nToGo = k - MultiID.nDone;
				if (MultiID.statReq2) MultiID.WritenDone("MExpLN: reduce");
			}
			Calc.ti1 = MultiID.TimerF() - Calc.ti0;
			MultiID.ADebug("MExpL: NN = " + j);
			MultiFD.DoneF(e);
			Calc.ti0 = MultiID.TimerF();
			SExpLN(t, true, zI);				// A0 = ExpL(t)
			d = MultiID.TimerF();
			Calc.ti2 = d - Calc.ti0;
			Calc.ti0 = d;
			for (i = 1; i <= j; i++)			// A1 = A0 * (2 + A0), nn times
			{
				MultiFD.Add1F(Common.nList[zI], 2, t);
				RMulN(t, zI);
				MultiID.nToGo = j - i;
				MultiID.nDone = i;
				if (MultiID.statReq2) MultiID.WritenDone("MExpLN: expand");
			}
			if (MultiID.diagOn) ReportBest(j);
			if (!bL1) RAdd1N(1, zI);
			MultiFD.DoneF(t);
		} // MExpLN

		public static void ExpLN(MultiF x, bool bL1, int zI)
		// z = (e^x) - 1, accurate for small x, (- 1 only if bL1 is true)
		// n = Math.Floor(x / Ln(10))
		// g = Frac(x / Ln(10)) * Ln(10)
		// answer = Exp(g) * 10^n
		{
			int ln10 = 0;
			int i;
			int j;
			int d1;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF t = new MultiF(Calc.fMC);
			int ii = 1 + (25 / MultiID.muDMax);
			MultiF nn = new MultiF(ii);
			if (x.N + x.C > 0)							// if |x| >= 1
			{
				if (x.N + x.C > 1 || x.V[-x.C] > 1)	// if |x| >= 2
				{
					ln10 = GetLn10Ptr();
					MultiFD.DivF(x, Common.nList[ln10], t);
					MultiFD.IntF(t, nn);
				}
			}
			if (MultiFD.ZTestF(nn))
				MExpLN(x, bL1, zI);
			else
			{
				if (ln10 == 0) ln10 = GetLn10Ptr();
				FracN(t, zI);
				RMulN(Common.nList[ln10], zI);
				MExpLN(Common.nList[zI], false, zI);
				decimal nDe = nn.N;
				nDe += nn.C;
				if (nDe > ii)
					nDe = 8 * (decimal)Int64.MaxValue;
				else
					MultiFD.GetDeF(out nDe, nn);
				if (nn.S && (nDe > 0))
					nDe = -nDe;
				decimal nID = decimal.Floor(nDe / MultiID.muDMax);
				j = (int)(nDe - nID * MultiID.muDMax);
				if (j < 0)
				{
					j += MultiID.muDMax;
					nID--;
				}
				if (j > 0 && j < MultiID.muDMax)
				{
					d1 = 10;
					for (i = 2; i <= j; i++)
						d1 *= 10;
					RMul1N(d1, zI);
				}
				nDe = Common.nList[zI].C;
				nDe += nID;
				if (nDe > Int64.MaxValue)
				{
					Calc.ClearN(zI);
					MultiID.MuWriteErr("Exp overflow, continuing...");
				}
				else if (nDe < -Int64.MaxValue)
				{
					Calc.ClearN(zI);
					if (bL1)
						Common.nList[zI].V[0] = 1;
					else
					{
						MultiID.MuWriteErr("Exp underflow, continuing...");
						MultiID.muUnderflow = true;
					}
					Common.ChSN(zI);
				}
				else
				{
					Common.nList[zI].C = (long)(Common.nList[zI].C + nID);
					if (bL1)
					{
						RAdd1N(-1, zI);
					}
				}
			}
			MultiFD.DoneF(nn);
			MultiFD.DoneF(t);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ExpLN

		public static void RExpLN(int zI) // z = (e^z) - 1, accurate for small x
		{
			ExpLN(Common.nList[zI], true, zI);
		}

		public static void ExpN(MultiF x, int zI)	// z = e^x
		{
			ExpLN(x, false, zI);
		}

		public static void RExpN(int zI) // z = e^z
		{
			ExpN(Common.nList[zI], zI);
		}

		public static void SLnLN(MultiF y, bool bL1, int zI)
		// Common.nList[zI] = Ln(y + 1), small y
		// bL1 is true if y is input, false if x = 1 + y is input
		// Ln(x) = Ln(1+y) = Ln((1+z)/(1-z)) = 2 * (z + z^3/3 + z^5/5)
		// x = 1 + y = (1 + z) / (1 - z)
		// y = x - 1 = 2 * z / (1 - z)
		// z = (x - 1) / (x + 1) = y / (2 + z)
		{
			int saveFMC;
			int saveMMax;
			int currFMC;
			bool fini;
			int j;
			if ( bL1 && (y.C < (-Int32.MaxValue / 2 - Int16.MaxValue)) )
			{
				Calc.SetToN(y, zI);
				return;
			}
			saveFMC = Calc.fMC;
			saveMMax = MultiID.mMax;
			currFMC = Calc.fMC;
			MultiF nn = new MultiF(2);
			MultiF term = new MultiF(Calc.fMC);
			MultiF bTerm = new MultiF(Calc.fMC);
			MultiF g = new MultiF(Calc.fMC);
			MultiF sum = new MultiF(Calc.fMC);
			if (!bL1)
			{
				MultiFD.Add1F(y, -1, g);			// x input So g is y
				MultiFD.Add1F(g, 2, term);			// term = z = y / (y + 2)
				MultiFD.DivF(g, term, term);
			}
			else
			{
				MultiFD.Add1F(y, 2, term);			// term = z = y / (y + 2)
				MultiFD.DivF(y, term, term);
			}
			MultiFD.AddF(term, term, bTerm);		// bTerm = 2 * z
			MultiFD.SetTo1F(1, nn);					// nn	= 1
			MultiFD.SqF(term, g);					// g	= Sq(z)
			MultiFD.SetToF(bTerm, sum);				// sum	= bTerm
			fini = false;
			MultiID.nDone = 1;
			while (!fini)
			{
				MultiFD.RMulF(g, bTerm);			// bTerm = bTerm * g
				MultiFD.RAdd1F(2, nn);				// nn	= nn + 2
				MultiFD.DivF(bTerm, nn, term);		// term = bTerm / nn
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				long iL = Calc.fMC - sum.N - sum.C + term.N + term.C;
				if (iL > Int32.MaxValue) iL = Int32.MaxValue;
				if (iL < Int32.MinValue) iL = Int32.MinValue;
				MultiID.nToGo = (int)iL;
				MultiID.nDone++;
				if (MultiID.nToGo < 0 || MultiFD.ZTestF(term) || MultiID.muErr || MultiID.muAbort)
					fini = true;
				else
				{
					MultiFD.RAddF(term, sum);		// sum = sum + term
					Calc.fMC = (int)MultiID.nToGo + 1;
					if (Calc.fMC < currFMC)
					{
						currFMC = Calc.fMC;
						j = term.N - Calc.fMC;
						if (j > 0)
							MultiFD.ShiftR(j, term);
						j = g.N - Calc.fMC;
						if (j > 0)
						{
							MultiFD.ShiftR(j, g);
						}
					}
					else
					{
						Calc.fMC = currFMC;
					}
				}
				if (MultiID.statReq2) MultiID.WritenDone("SLnLN: series");
			}
			MultiID.nToGo = 0;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.SetToN(sum, zI);
			MultiFD.DoneF(sum);
			MultiFD.DoneF(g);
			MultiFD.DoneF(bTerm);
			MultiFD.DoneF(term);
			MultiFD.DoneF(nn);
		} // SLnLN

		public static void MLnLN(MultiF y, bool bL1, int zI)
		// z = Ln(y + 1), medium small y
		// Ln(x) = Ln(y + 1) = Ln(SqRt(x) n times) * 2^n
		// SqRt(y + 1) - 1 = y / (1 + SqRt(1 + y)
		{
			int nn;
			double d;
			bool sign;
			int i;
			int j;
			int k;
			//			d = 0.6795 + Math.Sqrt(Calc.fMC * MultiID.muDMax) * 0.56415806582;	// PolyFit
			d = 0.6795 + Math.Sqrt(Calc.fMC * MultiID.muDMax) * 0.24;			// PolyFit
			Common.LFind("MLnLNN", out nn);
			if (nn != 0) MultiFD.GetDbF(out d, Common.nList[nn]);
			j = (int)Math.Round(d);
			MultiID.ADebug("MLnL: NN = " + d.ToString("0.000"));
			MultiF e = new MultiF(2);
			MultiF f = new MultiF(2);
			MultiFD.SetTo1F(9, e);
			for (i = 1; i <= j; i++)				// e = e / (1 + SqRt(1 + e)), nn times
			{
				MultiFD.Add1F(e, 1, f);
				MultiFD.RSqRtF(f);
				MultiFD.RAdd1F(1, f);
				MultiFD.RDivF(f, e);
			}
			MultiFD.DoneF(f);
			MultiF t = new MultiF(Calc.fMC);
			MultiFD.SetToF(y, t);
			if (!bL1)
				MultiFD.Add1F(y, -1, t);			// x input So t is x - 1
			sign = t.S;
			t.S = false;
			MultiID.nDone = 0;
			k = j;
			MultiID.nToGo = k;
			j = 0;
			Calc.ti0 = MultiID.TimerF();
			while (t > e)							// t = t / (1 + SqRt(1 + t)), while t > e
			{
				t.S = sign;
				Add1N(t, 1, zI);
				RSqRtN(zI);
				RAdd1N(1, zI);
				MultiFD.RDivF(Common.nList[zI], t);
				j++;
				if (MultiID.muErr || MultiID.muAbort) MultiFD.SetTo1F(0, t);
				t.S = false;
				MultiID.nDone++;
				MultiID.nToGo = k - MultiID.nDone;
				if (MultiID.statReq2) MultiID.WritenDone("MLnLN: reduce");
			}
			Calc.ti1 = MultiID.TimerF() - Calc.ti0;
			if (!MultiFD.ZTestF(t)) t.S = sign;
			MultiID.ADebug("MLnL: NN = " + j);
			Calc.ti0 = MultiID.TimerF();
			SLnLN(t, true, zI);						// A0 = LnL(t)
			d = MultiID.TimerF();
			Calc.ti2 = d - Calc.ti0;
			Calc.ti0 = d;
			for (i = 1; i <= j; i++)				// A1 = A0 * 2, nn times
			{
				RMul1N(2, zI);
				MultiID.nToGo = j - i;
				MultiID.nDone = i;
				if (MultiID.statReq2) MultiID.WritenDone("MLnLN: expand");
			}
			if (MultiID.diagOn) ReportBest(j);
			MultiFD.DoneF(t);
			MultiFD.DoneF(e);
		} // MLnLN

		public static void LnLN(MultiF y, bool bL1, int zI)
		// z = Ln(y + 1), accurate for small y
		// x = y + 1 = Integer * MultiID.baseM^C
		// Multiply by 10 until MSD of i >= MultiID.baseM / 10, save count
		// change C of x to -N so x < 1, save original C+N
		// answer = Ln(x) + Ln(10) * NT, NT = MultiID.muDMax * C+N - count
		{
			int tI;
			decimal d;
			decimal nTen;
			int ln10;
			if (bL1)
			{
				if (-y.C > y.N ||
					(-y.C == y.N && y.V[y.N-1] < MultiID.baseM / 10))
				{
					MLnLN(y, true, zI);		// y < 0.1
					return;
				}
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			Common.InitNAdd(1, "LnLN-tI", out tI);
			Calc.SetToN(y, tI);
			if (bL1) RAdd1N(1, tI);
			if (Common.ZTestN(tI))
			{
				MultiID.MuWriteErr("LnL: Cannot take Ln of zero, continuing...");
				Common.LDelete(tI);
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Calc.ClearN(zI);
				return;
			}
			if (Common.nList[tI].S)
			{
				MultiID.MuWriteErr("LnL: Cannot take Ln of X < 0, continuing...");
				Common.LDelete(tI);
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Calc.ClearN(zI);
				return;
			}
			if (Common.nList[tI].V[Common.nList[tI].N-1] == 1)
			{
				nTen = Common.nList[tI].C;
				nTen += Common.nList[tI].N - 1;		// HJS 2004-07-04
				nTen *= MultiID.muDMax;
				Common.nList[tI].C = 1 - Common.nList[tI].N;
			}
			else
			{
				nTen = 0;
				while (Common.nList[tI].V[Common.nList[tI].N-1] < MultiID.baseM / 10 && !MultiID.muErr && !MultiID.muAbort)
				{
					RMul1N(10, tI);
					nTen--;
				}
				d = Common.nList[tI].C;
				nTen = nTen + (d + Common.nList[tI].N) * MultiID.muDMax;
				Common.nList[tI].C = -Common.nList[tI].N;
			}
			MLnLN(Common.nList[tI], false, zI);
			if (nTen != 0)
			{
				ln10 = GetLn10Ptr();
				MultiF fTen = new MultiF(5);
				MultiFD.SetToDeF(nTen, fTen);
				MulN(Common.nList[ln10], fTen, tI);
				MultiFD.DoneF(fTen);
				RAddN(Common.nList[tI], zI);
			}
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // LnLN

		public static void RLnLN(int zI) // z = Ln(z + 1), accurate for small z
		{
			LnLN(Common.nList[zI], true, zI);
		}

		public static void LnN(MultiF x, int zI)	// z = Ln(x)
		{
			LnLN(x, false, zI);
		}

		public static void RLnN(int zI) // z = Ln(z)
		{
			LnN(Common.nList[zI], zI);
		}

		public static void LogN(MultiF x, int zI)	// z = Log(x)
		// Log(x) = Ln(x) / Ln(10), Log base 10 of x
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			LnN(x, zI);
			if (!MultiID.muErr)
			{
				int ln10 = GetLn10Ptr();
				RDivN(Common.nList[ln10], zI);
			}
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RLogN(int zI) // z = Log(z)
		{
			LogN(Common.nList[zI], zI);
		}

		public static void SinHN(MultiF x, int zI)	// z = SinH(x)
		// for (x >= 0.1 SinH(x) = (y - 1/y) / 2, where y = Exp(x)
		// for (x <  0.1 SinH(x) = y / (2 * SqRt(y + 1)), where y = Exp(2 * x) - 1
		// SinH(-x) = -SinH(x)
		{
			int tI;
			bool sign;
			Calc.SetToN(x, zI);
			if ( Common.ZTestN(zI) || (Common.nList[zI].C < (-Int32.MaxValue / 3 - Int16.MaxValue)) )
				return;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			Common.InitNAdd(1, "SinHN-tI", out tI);
			sign = Common.nList[zI].S;
			Common.nList[zI].S = false;
			if ( -Common.nList[zI].C > Common.nList[zI].N ||
				(-Common.nList[zI].C == Common.nList[zI].N &&
				Common.nList[zI].V[Common.nList[zI].N-1] < MultiID.baseM / 10) )
			{
				RMul1N(2, zI);
				RExpLN(zI);
				Add1N(Common.nList[zI], 1, tI);
				RSqRtN(tI);
				RMul1N(2, tI);
				RDivN(Common.nList[tI], zI);
			}
			else
			{
				RExpN(zI);
				if (!Common.ZTestN(zI)) InvN(Common.nList[zI], tI);
				RSubN(Common.nList[tI], zI);
				RDiv1N(2, zI);
			}
			Common.LDelete(tI);
			Common.nList[zI].S = sign;
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // SinHN

		public static void RSinHN(int zI) // z = SinH(z)
		{
			SinHN(Common.nList[zI], zI);
		}

		public static void RiN(MultiF x, int zI)	// z = Riemann prime counting function of x
		// Ri(x) = 1 + sum{n = 1, ...} ( Ln(x)^n / (n*n!*Zeta(n+1)) )
		{
			if (x.S || MultiFD.ZTestF(x))			// if x <= 0
			{
				MultiID.MuWriteErr("Ri(x) error: x <= 0");
				Calc.ClearN(zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int sI;
			Common.InitNAdd(1, "RiN-sI", out sI);	// s = sum = 0
			Common.nList[sI].V[0] = 0;
			int oI;
			Common.InitNAdd(1, "RiN-oI", out oI);	// s = old sum = 0
			Common.nList[oI].V[0] = 0;
			int gI;
			Common.InitNAdd(1, "RiN-gI", out gI);	// g = Ln(x)
			LnN(x, gI);
			int fI;
			Common.InitNAdd(1, "RiN-fI", out fI);	// f = L^n / n! = 1
			Common.nList[fI].V[0] = 1;
			int nI;
			Common.InitNAdd(1, "RiN-nI", out nI);	// n = 1, ...
			Common.nList[nI].V[0] = 1;
			int pI;
			Common.InitNAdd(1, "RiN-pI", out pI);	// p = n + 1 = 2, ...
			Common.nList[pI].V[0] = 2;
			int tI;
			Common.InitNAdd(1, "RiN-tI", out tI);	// t = term
			for (int n = 1; ; n++)
			{
				RMulN(Common.nList[gI], fI);						// f = f * g / n
				RDivN(Common.nList[nI], fI);
				ZetaN(Common.nList[pI], tI);						// temp = Zeta(n + 1)
				MulN(Common.nList[nI], Common.nList[tI], tI);		// temp = n * Zeta(n + 1)
				DivN(Common.nList[fI], Common.nList[tI], tI);		// term = f / (n * Zeta(n + 1))
				AddN(Common.nList[oI], Common.nList[tI], sI);		// sum = old sum + term
				if (Common.nList[sI] == Common.nList[oI]) break;	// if sum = old sum, break
				Calc.SetToN(Common.nList[sI], oI);					// old sum = sum
				RAddN(MultiFD.fOne, nI);							// n = n + 1
				RAddN(MultiFD.fOne, pI);							// p = p + 1
			}
			RAddN(MultiFD.fOne, sI);								// sum = sum + 1
			Calc.SetToN(Common.nList[sI], zI);						// z = sum
			Common.LDelete(tI);
			Common.LDelete(pI);
			Common.LDelete(nI);
			Common.LDelete(fI);
			Common.LDelete(gI);
			Common.LDelete(oI);
			Common.LDelete(sI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // RiN

		public static void LiN(MultiF x, int zI)	// z = Logarithmic integral of x
		// Li(x) = Ei(Ln(x))
		{
			if (MultiFD.ZTestF(x))					// if x = 0
			{
				Calc.ClearN(zI);					// Li(0) = 0
				return;
			}
			if (x.S)								// if x < 0, error
			{
				MultiID.MuWriteErr("Li(x) undefined for x < 0");
				Calc.ClearN(zI);
				return;
			}
			if (x == MultiFD.fOne)					// if x = 1, error -
			{
				MultiID.MuWriteErr("Li(1) = -infinity");
				Calc.ClearN(zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int tI;
			Common.InitNAdd(1, "LiN-tI", out tI);
			LnN(x, tI);				// tI = ln(x)
			EiN(Common.nList[tI], zI);			// z = Ei(ln(x))
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // LiN

		public static void EiN(MultiF x, int zI)	// z = entire exponential integral of x
		// Ei(x) = -Ei(-x)
		{
			if (MultiFD.ZTestF(x))			// if x = 0
			{
				MultiID.MuWriteErr("Ei(x) error: x = 0");
				Calc.ClearN(zI);
				return;
			}
			MultiF mx = new MultiF(x.N);	// mx = -x
			mx &= x;
			MultiFD.ChSF(mx);
			E1N(mx, zI);
			MultiFD.ChSF(Common.nList[zI]);	// E1 = -Ei(-x)
		} // EiN

		public static void EiAN(MultiF x, int zI)	// z = entire exponential integral of x
		// asymptotic expansion, only use this for x << -1 (large negative x)
		{
			MultiF s = new MultiF(Calc.fMC);	// x < 0
			MultiF k = new MultiF(Calc.fMC);
			MultiF f = new MultiF(Calc.fMC);
			MultiF d = new MultiF(Calc.fMC);
			MultiF t = new MultiF(Calc.fMC);
			MultiF p = new MultiF(Calc.fMC);
			k &= 0;							// k = index
			f &= 1;							// f = k!
			d &= x;							// d = x ^ (k+1)
			s &= 0;							// s = sum
			for (int ki = 0; true; ki++)	// sum of (x)^k / (k*k!)
			{
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr) break;
				t &= f / d;				// term = k! / d
				p &= s;					// p = previous sum
				s += t;					// sum = sum + t
				p &= s - p;				// p = delta in sum
				if (MultiFD.ZTestF(p))
				{
					break;
				}
				k += 1;					// update index
				f *= k;					// f = k!
				d *= x;					// d = x ^ (k+1)
			}
			MultiFD.DoneF(p);
			MultiFD.DoneF(t);
			MultiFD.DoneF(d);
			MultiFD.DoneF(f);
			MultiFD.DoneF(k);
			if (MultiID.muAbort || MultiID.muErr)
			{
				Calc.ClearN(zI);
			}
			else
			{
				int tI;
				Common.InitNAdd(1, "EiAN-tI", out tI);
				ExpN(x, tI);						// tI = exp(x)
				s *= Common.nList[tI];				// times sum
				Calc.SetToN(s, zI);					// final value is s
				Common.LDelete(tI);
			}
			MultiFD.DoneF(s);
		} // EiAN

		public static void E1N(MultiF x, int zI)	// z = exponential integral 1 of x
		// E1(x) = integral{x to inf}(e^-t / t)dt = -Ei(-x)
		{
			if (MultiFD.ZTestF(x))
			{
				MultiID.MuWriteErr("E1(x) error: x = 0");
				Calc.ClearN(zI);
				return;
			}
			int more = 0;
			long lost = 0;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF mx = new MultiF(x.N);			// mx = -x
			mx &= x;
			MultiFD.ChSF(mx);
			if (mx.S && x > MultiFD.fOne)			// if x > 1, subject to large relative error
			{
				MultiF lim = new MultiF(2);			// limit for using Ei(x)
				lim &= (int)(10 + Calc.fMC * Math.Log(MultiID.baseM));
				if (x >= lim)						// if x >= limit
				{
					EiAN(mx, zI);
					Common.nList[zI].S = false;		// E1 = -Ei(-x) (E1 > 0 for x > 0)
					MultiFD.DoneF(lim);
					MultiFD.DoneF(mx);
					MultiFD.expanded = saveExp;
					Calc.fMC = saveFMC;
					MultiID.mMax = saveMMax;
					Calc.NormN(zI);
					return;
				}
				double db;
				MultiFD.GetDbF(out db, x);
				more = 1 + (int)(db / 9.22);		// Need more precision
				Calc.fMC += more;
				CheckNMax();
				MultiID.ADiag("E1N: added " + more * MultiID.muDMax + " more digits");
			}
			MultiF s = new MultiF(Calc.fMC);
			MultiF k = new MultiF(Calc.fMC);
			MultiF n = new MultiF(Calc.fMC);
			MultiF f = new MultiF(Calc.fMC);
			MultiF d = new MultiF(Calc.fMC);
			MultiF t = new MultiF(Calc.fMC);
			MultiF p = new MultiF(Calc.fMC);
			k &= 1;							// k = index
			n &= mx;						// n = numerator
			f &= 1;							// f = factorial factor
			s &= mx;						// s = sum
			for (int ki = 2; true; ki++)	// sum of (mx)^k / (k*k!)
			{
				MultiID.MuInterrupt();
				if (MultiID.muAbort || MultiID.muErr) break;
				if (MultiID.statReq2)
				{
					MultiID.WriteT("E1N: k = " + ki);
					MultiID.TimeDel();
					MultiID.statReq2 = false;
				}
				k += 1;					// update index
				n *= mx;				// x^k
				f *= k;					// f = k!
				d &= f * k;				// d = k * k!
				t &= n / d;				// t = n / d
				p &= s;					// p = previous sum
				s += t;					// sum = sum + t
				if (s.N + s.C > lost) lost = s.N + s.C;
				d &= s - p;				// d = delta in sum
				if (MultiFD.ZTestF(d))
				{
					break;
				}
			}
			MultiFD.DoneF(p);
			MultiFD.DoneF(t);
			MultiFD.DoneF(d);
			MultiFD.DoneF(f);
			MultiFD.DoneF(n);
			MultiFD.DoneF(k);
			if (MultiID.muAbort || MultiID.muErr)
			{
				Calc.ClearN(zI);
			}
			else
			{
				int tI;
				Common.InitNAdd(1, "E1N-tI", out tI);
				mx.S = false;						// x = |x|
				LnN(mx, tI);						// tI = ln(|x|)
				lost += s.N;
				s += Common.nList[tI];				// and add to sum
				PsiN(MultiFD.fOne, tI);				// tI = -Euler's constant = -0.5772...
				s -= Common.nList[tI];				// and subtract from sum
				lost -= s.N;
				MultiID.ADiag("E1N: lost " + lost * MultiID.muDMax + " digits");
				MultiFD.ChSF(s);					// E1 = -Ei(-x) (prevent -0)
				Calc.SetToN(s, zI);					// final value is s
				Common.LDelete(tI);
			}
			MultiFD.DoneF(s);
			MultiFD.DoneF(mx);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // E1N

		public static void DilogN(MultiF x, int zI)	// z = Dilog(x)
		// Dilog(x) = Polylog(2, x) = x * Lerch(x, 2, 1)
		{
			PolylogN(MultiFD.fTwo, x, zI);
		} // DilogN

		public static void PolylogN(MultiF s, MultiF x, int zI)	// z = Polylog(s, x)
		// Polylog(s, x) = x * Lerch(x, s, 1)
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			if (s == MultiFD.fTwo && x < MultiFD.fMOne)		// if Dilog of x < -1
			{
				MultiF y = new MultiF(Calc.fMC);
				y &= MultiFD.fOne / (1 - x);				// y = 1 / (1 - x)
				int tI;
				Common.InitNAdd(1, "PolylogN-tI", out tI);
				SubN(MultiFD.fOne, y, zI);					// z = 1 - y
				DilogN(Common.nList[zI], zI);				// z = Dilog(1-y)
				LnN(y, tI);									// t = ln(y)
				RSqN(tI);									// t = (ln(y))^2
				RMulN(MultiFD.fHalf, tI);					// t = (ln(y))^2 / 2
				RAddN(Common.nList[tI], zI);				// z = Dilog(1-y) + (ln(y))^2 / 2
				Common.nList[zI].S = true;					// z = -Dilog(1-y) - (ln(y))^2 / 2
				Common.LDelete(tI);
				MultiFD.DoneF(y);
			}
			else
			{
				MultiF saveX = new MultiF(x.N);
				saveX &= x;
				Lerch(x, s, MultiFD.fOne, false, zI);
				MulN(saveX, Common.nList[zI], zI);
				MultiFD.DoneF(saveX);
			}
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // PolylogN

		public static void ZetaHN(MultiF s, MultiF a, int zI)	// z = ZetaH(s, a) Hurwitz Zeta Function
		// ZetaH(s, a) = Lerch(1, s, a)
		{
			Lerch(MultiFD.fOne, s, a, false, zI);
		} // ZetaHN

		public static void CosHN(MultiF x, int zI)	// z = CosH(x)
		// CosH(x) = (y + 1/y) / 2, where y = Exp(|x|)
		{
			int tI;
			Calc.SetToN(x, zI);
			if (Common.ZTestN(zI))
			{
				SetTo1N(1, zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			Common.InitNAdd(1, "CosHN-tI", out tI);
			Common.nList[zI].S = false;
			ExpN(x, zI);
			if (!MultiID.muErr)
			{
				InvN(Common.nList[zI], tI);
				RAddN(Common.nList[tI], zI);
				RDiv1N(2, zI);
			}
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // CosHN

		public static void RCosHN(int zI) // z = CosH(z)
		{
			CosHN(Common.nList[zI], zI);
		}

		public static void TanHN(MultiF x, int zI)	// z = TanH(x)
		// TanH(x) = y / (y + 2), where y = Exp(2 * x) - 1
		// TanH(-x) = -TanH(x)
		{
			int tI;
			bool sign;
			bool saveRep;
			Calc.SetToN(x, zI);
			if (Common.ZTestN(zI) || (Common.nList[zI].C < (-Int32.MaxValue / 3 - Int16.MaxValue)) || MultiID.muErr) return;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			Common.InitNAdd(1, "TanHN-tI", out tI);
			sign = Common.nList[zI].S;
			Common.nList[zI].S = false;
			saveRep = MultiID.muErrRep;
			MultiID.muErrRep = false;
			RMul1N(2, zI);
			RExpLN(zI);
			MultiID.muErrRep = saveRep;
			if (MultiID.muErr)		// TanH(+/- Large) = +/- 1
			{
				MultiID.muErr = false;
				Calc.ClearN(zI);
				Common.nList[zI].V[0] = 1;
			}
			else
			{
				Add1N(Common.nList[zI], 2, tI);
				RDivN(Common.nList[tI], zI);
			}
			Common.LDelete(tI);
			Common.nList[zI].S = sign;
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // TanHN

		public static void RTanHN(int zI) // z = TanH(z)
		{
			TanHN(Common.nList[zI], zI);
		}

		public static void ASinHN(MultiF x, int zI)	// z = ASinH(x)
		// for (x >= 0.1 ASinH(x) = Ln(x + SqRt(1 + Sq(x)))
		// for (x <  0.1 ASinH(x) = LnL(x + Sq(x) / SqRt(1 + Sq(x)))
		// ASinH(-x) = -ASinH(x)
		{
			int tI;
			bool sign;
			bool saveRep;
			Calc.SetToN(x, zI);
			if (Common.ZTestN(zI) || (Common.nList[zI].C < (-Int32.MaxValue / 3 - Int16.MaxValue)) || MultiID.muErr) return;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			Common.InitNAdd(1, "ASinHN-tI", out tI);
			sign = Common.nList[zI].S;
			Common.nList[zI].S = false;
			if ( -Common.nList[zI].C > Common.nList[zI].N ||
				(-Common.nList[zI].C == Common.nList[zI].N &&
				Common.nList[zI].V[Common.nList[zI].N-1] < MultiID.baseM / 10) )
			{
				SqN(Common.nList[zI], tI);
				RAdd1N(1, tI);
				RSqRtN(tI);
				RAdd1N(1, tI);
				DivN(Common.nList[zI], Common.nList[tI], tI);
				RMulN(Common.nList[zI], tI);
				RAddN(Common.nList[tI], zI);
				Common.LDelete(tI);
				RLnLN(zI);
			}
			else
			{
				saveRep = MultiID.muErrRep;
				MultiID.muErrRep = false;
				SqN(Common.nList[zI], tI);
				MultiID.muErrRep = saveRep;
				if (MultiID.muErr)			// too big to square, so too big to add 1
				{
					MultiID.muErr = false;
					Calc.SetToII(zI, tI);
				}
				else
				{
					RAdd1N(1, tI);
					RSqRtN(tI);
				}
				RAddN(Common.nList[tI], zI);
				Common.LDelete(tI);
				RLnN(zI);
			}
			Common.nList[zI].S = sign;
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ASinHN

		public static void RASinHN(int zI) // z = ASinH(z)
		{
			ASinHN(Common.nList[zI], zI);
		}

		public static void ACosHN(MultiF x, int zI)	// z = ACosH(x)
		// ACosH(x) = Ln(x + SqRt(Sq(x) - 1))
		// error if x < 1
		{
			int tI;
			if (MultiFD.ZTestF(x) || x.S || (-x.C >= x.N)) // if x < 1
			{
				MultiID.MuWriteErr("ACosh(x) error: x < 1.0");
				Calc.ClearN(zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			Calc.SetToN(x, zI);
			Common.InitNAdd(1, "ACosHN-tI", out tI);
			SqN(x, tI);
			if (!MultiID.muErr) // if no overflow
			{
				RAdd1N(-1, tI);
				RSqRtN(tI);
				RAddN(Common.nList[tI], zI);
				RLnN(zI);
			}
			else
				Calc.ClearN(zI);
			Common.LDelete(tI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ACosHN

		public static void RACosHN(int zI) // z = ACosH(z)
		{
			ACosHN(Common.nList[zI], zI);
		}

		public static void RACotN(int zI) // z = ACot(z)
		// ACot(z) = ATan(1/z), error if z = 0
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			if (Common.nList[zI] == 0)
			{
				if (Calc.degMode)
					SetTo1N(90, zI);
				else
				{
					int pI = GetPiPtr();
					DivN(Common.nList[pI], MultiFD.fTwo, zI);	// Pi / 2
				}		
			}
			else
			{
				ATanN(MultiFD.fOne/Common.nList[zI], zI);
			}
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RACotHN(int zI) // z = ACotH(z)
		// ACotH(z) = ATanH(1/z), error if |z| <= 1
		{
			if (Common.nList[zI] == 0)
			{
				MultiID.MuWriteErr("Cannot divide by zero, continuing...");
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ATanHN(MultiFD.fOne/Common.nList[zI], zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RACscN(int zI) // z = ACsc(z)
		// ACsc(z) = ASin(1/z), error if z = 0
		{
			if (Common.nList[zI] == 0)
			{
				MultiID.MuWriteErr("Cannot divide by zero, continuing...");
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ASinN(MultiFD.fOne/Common.nList[zI], zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RACscHN(int zI) // z = ACscH(z)
		// ACscH(z) = ASinH(1/z), error if z = 0
		{
			if (Common.nList[zI] == 0)
			{
				MultiID.MuWriteErr("Cannot divide by zero, continuing...");
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ASinHN(MultiFD.fOne/Common.nList[zI], zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RASecN(int zI) // z = ASec(z)
		// ASec(z) = ACos(1/z), error if z = 0
		{
			if (Common.nList[zI] == 0)
			{
				MultiID.MuWriteErr("Cannot divide by zero, continuing...");
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ACosN(MultiFD.fOne/Common.nList[zI], zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RASecHN(int zI) // z = ASecH(z)
		// ASecH(z) = ACosH(1/z), error if z <= 0 or z > 1
		{
			if (Common.nList[zI] == 0)
			{
				MultiID.MuWriteErr("Cannot divide by zero, continuing...");
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			ACosHN(MultiFD.fOne/Common.nList[zI], zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RCotN(int zI) // z = Cot(z)
		// Cot(z) = 1/Tan(z), error if Tan(z) = 0
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			bool forCot = true;
			TanN(Common.nList[zI], zI, ref forCot);
			if (forCot == true)
				RInvN(zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RCotHN(int zI) // z = CotH(z)
		// CotH(z) = 1/TanH(z), error if TanH(z) = 0
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			TanHN(Common.nList[zI], zI);
			RInvN(zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RCscN(int zI) // z = Csc(z)
		// Csc(z) = 1/Sin(z), error if Sin(z) = 0
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			SinN(Common.nList[zI], zI);
			RInvN(zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RCscHN(int zI) // z = CscH(z)
		// CscH(z) = 1/SinH(z), error if SinH(z) = 0
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			SinHN(Common.nList[zI], zI);
			RInvN(zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RSecN(int zI) // z = Sec(z)
		// Sec(z) = 1/Cos(z), error if Cos(z) = 0
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			CosN(Common.nList[zI], zI);
			RInvN(zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RSecHN(int zI) // z = SecH(z)
		// SecH(z) = 1/CosH(z)
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			CosHN(Common.nList[zI], zI);
			RInvN(zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void ATanHN(MultiF x, int zI)	// z = ATanH(x)
		// ATanH(x) = LnL(2 * x / (1 - x))
		// ATanH(-x) = -ATanH(x)
		// error if |x| >= 1
		{
			int tI;
			bool sign;
			Calc.SetToN(x, zI);
			if (Common.ZTestN(zI) || (Common.nList[zI].C < (-Int32.MaxValue / 3 - Int16.MaxValue))) return;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			Common.InitNAdd(1, "ATanHN-tI", out tI);
			sign = Common.nList[zI].S;
			Common.nList[zI].S = false;
			Add1N(Common.nList[zI], -1, tI);
			Common.ChSN(tI);
			if (Common.ZTestN(tI) || Common.nList[tI].S)
			{
				MultiID.MuWriteErr("ATanH(x) error: |x| >= 1.0");
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Common.LDelete(tI);
				Calc.ClearN(zI);
				return;
			}
			RDivN(Common.nList[tI], zI);
			Common.LDelete(tI);
			RMul1N(2, zI);
			RLnLN(zI);
			RDiv1N(2, zI);
			Common.nList[zI].S = sign;
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		} // ATanHN

		public static void RATanHN(int zI) // z = ATanH(z)
		{
			ATanHN(Common.nList[zI], zI);
		}

		public static void CeilingN(MultiF x, int zI)	// z = smallest integer >= z
		{
			Calc.SetToN(x, zI);
			if (x.C < 0)				// if not an integer already
			{
				bool sign = x.S;
				MultiFD.RIntF(Common.nList[zI]);	// Ceiling(-2.9) = -2.0
				if (!sign) RAddN(MultiFD.fOne, zI);	// Ceiling(2.1) = 3.0
				Calc.NormN(zI);
			}
		}

		public static void RCeilingN(int zI) // z = smallest integer >= z
		{
			CeilingN(Common.nList[zI], zI);
		}

		public static void FloorN(MultiF x, int zI)	// z = largest integer <= z
		{
			Calc.SetToN(x, zI);
			if (x.C < 0)				// if not an integer already
			{
				bool sign = x.S;
				MultiFD.RIntF(Common.nList[zI]);	// Floor(2.9) = 2.0
				if (sign) RSubN(MultiFD.fOne, zI);	// Floor(-2.1) = -3.0
				Calc.NormN(zI);
			}
		}

		public static void RFloorN(int zI) // z = largest integer <= z
		{
			FloorN(Common.nList[zI], zI);
		}

		public static void RoundN(MultiF x, int zI)	// z = integer nearest to x
		{
			bool sign = x.S;
			Calc.SetToN(x, zI);
			Common.nList[zI].S = false;
			RAddN(MultiFD.fHalf, zI);	// Round(2.5) = 3.0
			MultiFD.RIntF(Common.nList[zI]);	// Round(-2.5) = -3.0
			Common.nList[zI].S = sign;
			Calc.NormN(zI);
		}

		public static void RRoundN(int zI) // z = integer nearest to z
		{
			RoundN(Common.nList[zI], zI);
		}

		public static void IntN(MultiF x, int zI)	// z = Integer part of x
		{
			Calc.SetToN(x, zI);
			MultiFD.RIntF(Common.nList[zI]);
			Calc.NormN(zI);
		}

		public static void RIntN(int zI) // z = Integer part of z
		{
			IntN(Common.nList[zI], zI);
		}

		public static void FracN(MultiF x, int zI)	// z = Fractional part of x
		{
			Calc.SetToN(x, zI);
			MultiFD.RFracF(Common.nList[zI]);
			Calc.NormN(zI);
		}

		public static void RFracN(int zI) // z = Fractional part of z
		{
			FracN(Common.nList[zI], zI);
		}

		public static void LopN(MultiF x, int zI) // z = x with one less super digit
		{
			MultiFD.LopF(x, Common.nList[zI]);
			Calc.NormN(zI);
		}

		public static void RLopN(int zI) // z = z with one less least significant "digit"
		{
			MultiFD.RLopF(Common.nList[zI]);
			Calc.NormN(zI);
		}

		public static void ToDegN(MultiF x, int zI) // z = x converted Rad to Degrees
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			int pI = GetPiPtr();
			Mul1N(x, 180, zI);
			RDivN(Common.nList[pI], zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RToDegN(int zI) // z = z converted Radians to Degrees
		{
			ToDegN(Common.nList[zI], zI);
		}

		public static void ToRadN(MultiF x, int zI)	// z = x converted Deg to Radians
		{
			int pI;
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			pI = GetPiPtr();
			MulN(x, Common.nList[pI], zI);
			RDiv1N(180, zI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(zI);
		}

		public static void RToRadN(int zI) // z = z converted Deg to Radians
		{
			ToRadN(Common.nList[zI], zI);
		}

		public static void CalSFTn(int zI) // set number of digits in truncate in display
		{
			double db;
			MultiFD.GetDbInF(out db, 0, Int32.MaxValue, Common.nList[zI]);
			db = 1 + Math.Floor((db - 1) / MultiID.muDMax); // set for at least N decimal digits
			MultiFD.fTn = (int)db;
			CheckNMax();
		}

		public static void NormC(MultiF x, MultiF m, MultiF s, int zI)
		// z = Cumulative distribution function (cdf) of normal distribution
		// m = mean, s = standard deviation
		// x, m, s may have the same address in memory as z
		// x, m, s are not changed unless it shares memory with z
		// sets muErr true if overflow or error
		{
			// NormC(x, m, s) = 0.5 * ErfC((m - x)/(s*SqRt(2))
			if (s == MultiFD.fZero)
			{
				if (x < m)
					Calc.SetToN(MultiFD.fZero, zI);
				else if (x > m)
					Calc.SetToN(MultiFD.fOne, zI);
				else
					Calc.SetToN(MultiFD.fHalf, zI);
			}
			else
			{
				bool saveExp = MultiFD.expanded;
				int saveFMC = Calc.fMC;
				int saveMMax = MultiID.mMax;
				Expand();
				MultiF t = new MultiF(Calc.fMC);
				MultiFD.SqRootF(MultiFD.fTwo, t);
				t &= (m - x)/(s*t);
				ErfCN(t, zI);
				RDiv1N(2, zI);
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				Calc.NormN(zI);
			}
		} // NormC

		public static void NormS(int zI)
		// z = Cumulative distribution function (cdf) of standard normal distribution
		// mean = 0, standard deviation = 1
		// sets muErr true if overflow or error
		{
			NormC(Common.nList[zI], MultiFD.fZero, MultiFD.fOne, zI);
		} // NormS

		public static void QuadRoot(MultiF a, MultiF b, MultiF c, int zI)
		// z = a root of quadratic equation 0 = a*x^2 + b*x + c, roots also stored in X1 and X2
		// a, b, c may have the same address in memory as z
		// a, b, c are not changed unless it shares memory with z
		// sets muErr true if overflow or error
		{
			if (a == 0 && b == 0)
			{
				if (c == 0)
					MultiID.MuWriteErr("QuadR: No unique solutions");
				else
					MultiID.MuWriteErr("QuadR: No solutions");
				Calc.SetToN(MultiFD.fZero, zI);
				return;
			}
			int x1I;	// "X1"
			int x2I;	// "X2"
			Common.LFindAdd("X1", out x1I);
			Common.LFindAdd("X2", out x2I);
			if (x1I == zI || x2I == zI || x1I == Common.iTop || x2I == Common.iTop)
			{
				MultiID.MuWriteErr("QuadR: Cannot set X1 or X2");
				return;
			}
			if (a == 0)
			{
				DivN(c, b, x1I);
				MultiFD.ChSF(Common.nList[x1I]);
				Calc.SetToII(x1I, x2I);
				Calc.SetToII(x2I, zI);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			MultiF d = new MultiF(Calc.fMC);
			d &= b*b - 4*a*c;
			if (d.S)
			{
				MultiID.MuWriteErr("QuadR: No real solutions");
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				return;
			}
			MultiFD.RSqRtF(d);
			if (b.S)
				d = b - d;
			else
				d += b;
			if (d == 0)
			{
				Calc.SetToN(MultiFD.fZero, x1I);
				Calc.SetToN(MultiFD.fZero, x2I);
			}
			else
			{
				MultiF cL = new MultiF(c.N);
				cL &= c;				// c could be the same as x1
				d *= MultiFD.fMHalf;
				DivN(d, a, x1I);
				DivN(cL, d, x2I);
			}
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			Calc.NormN(x1I);
			Calc.NormN(x2I);
			Calc.SetToII(x2I, zI);
		} // QuadRoot

		public static void NotN(MultiF x, int zI)	// z = !x
		{
			bool zip;
			zip = MultiFD.ZTestF(x);
			Calc.ClearN(zI);
			if (zip) Common.nList[zI].V[0] = 1;
		} // NotN

		public static void RNotN(int zI) // z = ACos(z)
		{
			NotN(Common.nList[zI], zI);
		}

		public static void WriteFixedB(RichTextBox outB, StreamWriter outF, MultiF z, int tot, int width)
		{
			string ch;
			int d;
			int did;		// number of fractional digits output
			int dToD;		// digits to display
			int i;
			int k;
			int nOutF;		// number of significant digits in fraction output
			int nOutI;		// number of significant digits in integer output
			string st;
			string stD;		// formatted NOut
			MultiF t2 = new MultiF();
			k = 'A' - 10;
			nOutI = 0;
			nOutF = 0;
			did = 0;
			if (outB == null) MultiID.WriteTLn2(outB, outF, "", ref tot, width);
			MultiF t1 = new MultiF(z.N + 1);
			MultiFD.SetToF(z, t1); // t1 = z with one extra digit
			if (z.S) MultiID.WriteT(outB, outF, "-", ref tot, width);
			t1.S = false;
			if (z.N + z.C > 0)
			{
				MultiFD.ReSizeF((int)(z.N + z.C), t2);
				MultiFD.IntF(t1, t2);
				MultiFD.RFracF(t1);
				if (MultiID.EQ1SI(1, t2))
				{
					MultiID.WriteT(outB, outF, "1", ref tot, width);
					nOutI = 1;
				}
				else if(MultiID.EQ1SI(0, t2))
				{
					MultiID.WriteT(outB, outF, "0", ref tot, width);
					nOutI = 1;
				}
				else
				{
					MultiID.WriteSI(outB, outF, t2, ref tot, width, false);
					nOutI = MultiID.nOutSI;
				}
				MultiFD.DoneF(t2);
			}
			else
			{
				MultiID.WriteT(outB, outF, "0", ref tot, width);
			}
			MultiID.WriteT(outB, outF, ".", ref tot, width);
			MultiFD.ReSizeF(1, t2);
			if (outB == null)
				dToD = 1 + (int)Math.Round((Calc.fMC * MultiID.muDMax) / Math.Log10(MultiID.baseO));
			else
				dToD = 1 + (int)Math.Round(((Calc.fMC - MultiFD.fTn) * MultiID.muDMax) / Math.Log10(MultiID.baseO));
			dToD = Math.Min(dToD, MultiID.maxDis);
			dToD = Math.Max(dToD, 2); // an extra digit is output, but not rounded
			do
			{
				MultiFD.RMul1F(MultiID.baseO, t1);
				MultiFD.IntF(t1, t2);
				MultiFD.RFracF(t1);
				MultiFD.Get1F(out d, t2);
				if (d < 10)
					ch = d.ToString();
				else
					ch = ((char)(d + k)).ToString();
				if (MultiID.muDpG > 0)
				{
					if (did > 0 && did % MultiID.muDpG == 0)
						MultiID.WriteT(outB, outF, ",", ref tot, width);
				}
				MultiID.WriteT(outB, outF, ch, ref tot, width);
				did++;
				if (ch != "0" || nOutI + nOutF > 0) nOutF = nOutF + 1;
			}
			while (!MultiFD.ZTestF(t1) && !(nOutI + nOutF >= dToD));
			did = nOutI + nOutF;
			if (outF != null)
			{
				MultiID.Write(outB, outF, "\n");
				tot = 0;
				ch = "";
			}
			else
			{
				ch = " ";
			}
			stD = "";
			if (did > MultiID.muLenD)
				stD = nOutI.ToString() + "." + nOutF.ToString() + " Digits ";
			MultiID.WriteT(outB, outF, ch + "(" + stD + "Base " + MultiID.baseO + ")", ref tot, width);
			if (outF == null)
			{
				i = z.V[z.N-1].ToString().Length;
				st = (MultiID.baseM + z.V[0]).ToString();
				while (st[st.Length - 1] == '0') st = st.Remove(st.Length - 1, 1);
				i += st.Length - 1 - MultiID.muDMax;
				st = (i + (z.N - 1) * MultiID.muDMax).ToString();
				MultiID.WriteT(outB, outF, " [" + st + "]", ref tot, width);
			}
			MultiFD.DoneF(t2);
			MultiFD.DoneF(t1);
			return;
		} // WriteFixedB

		public static void WriteFB(RichTextBox outB, StreamWriter outF, MultiF z, ref int tot, int width)
		// output z as a line of text, tot = characters on line so far, z is not modified
		{
			int bI;				// natural log of baseO
			string ch;
			int d;
			decimal dE;			// exponent
			double db;
			int did;			// digits output
			int dToD;			// digits to display
			bool bFixed;
			int i;
			int k;
			int nI;
			string st;			// formatted number
			string stE;			// formatted exponent "E+xxx,xxx...x"
			MultiF t1 = new MultiF();	// temp
			MultiF t2 = new MultiF(1);
			int trunc;
			int min = 2;
			bFixed = (!MultiFD.scieN) && (z.N + z.C <= min) && (z.C >= -min);
			if (bFixed && outF == null)
			{
				WriteFixedB(outB, outF, z, tot, 0); // format <= 99999999999999.0
				return;
			}
			if (outF != null) // always do full precision to file
				dToD = (int)Math.Round((Calc.fMC * MultiID.muDMax) / Math.Log10(MultiID.baseO)); // convert to baseO digits
			else
			{
				dToD = (int)Math.Round(((Calc.fMC - MultiFD.fTn) * MultiID.muDMax) / Math.Log10(MultiID.baseO));
				dToD = Math.Min(dToD, MultiID.maxDis);
				dToD = Math.Max(dToD, 2);	// needed or rounding factor will be wrong
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			db = MultiID.maxDis * Math.Log10(MultiID.baseO); // convert maxDis to Decimal digits
			trunc = (int)Math.Round(Math.Max(Calc.fMC - (db + 12) / 7, MultiFD.fTn));
			trunc = Math.Min(trunc, Calc.fMC - 2);
			if (trunc > 0 && outF == null)
				MultiFD.ReSizeF(Calc.fMC - trunc, t1);
			else
				MultiFD.ReSizeF(Calc.fMC, t1);
			MultiFD.SetToF(z, t1);	// rounds to desired digits
			t1.S = false;
			MultiF bF = new MultiF(Calc.fMC);	// baseO as a MultiF
			MultiFD.SetTo1F(MultiID.baseO, bF);
			Common.InitNAdd(6, "WriteFB-nI", out nI);
			Common.InitNAdd(1, "WriteFB-bI", out bI);
			i = t1.V[t1.N-1].ToString().Length;
			dE = Decimal.Floor((( (decimal)t1.C + t1.N - 1) * MultiID.muDMax + i) / (decimal)Math.Log10(MultiID.baseO));
			MultiFD.SetToDeF(dE, Common.nList[nI]);
			MultiFD.SetTo1F(MultiID.baseO, Common.nList[bI]);
			if (dE < 0)
			{
				Common.nList[nI].S = false;
				RPowN(Common.nList[nI], bI);
				if (MultiID.muErr) goto WriteFBEnd;
				MultiFD.MulF(t1, Common.nList[bI], t1);
			}
			else if (dE > 0)
			{
				RPowN(Common.nList[nI], bI);
				if (MultiID.muErr) goto WriteFBEnd;
				MultiFD.DivF(t1, Common.nList[bI], t1);
			}
			if (t1 == MultiFD.fZero)
				dE = 0;
			else
			{
				while (t1 < MultiFD.fOne)				// while t1 < 1
				{
					MultiFD.RMul1F(MultiID.baseO, t1);
					dE--;
				}
			}
			while (t1 >= bF)						// t1 >= baseO
			{
				MultiFD.RDiv1F(MultiID.baseO, t1);
				dE++;
			}
			MultiFD.ReSizeF(1, t2);
			if (MultiFD.fRound > 0)
			{
				SetTo1N(MultiID.baseO, bI);
				MultiFD.SetTo1F(-dToD + 1, t2);		// compute rounding factor (+1)
				RPowN(t2, bI);
				RDiv1N(2, bI);
				MultiFD.RAddF(Common.nList[bI], t1);
			}
			MultiFD.IntF(t1, t2);					// t2 = Integer part of t1
			MultiFD.Get1F(out d, t2);
			if (d == MultiID.baseO)
			{
				MultiFD.RDiv1F(MultiID.baseO, t1);
				dE++;
				MultiFD.IntF(t1, t2);				// t2 = Integer part of t1
				MultiFD.Get1F(out d, t2);
			}
			FormatStE(out stE, dE);
			if (outF != null)
				MultiID.WriteLn2(null, outF, " m.n " + stE + ", m.n ="); // line may > 80 ch
			MultiFD.RFracF(t1);						// t1 = Fractional part
			did = 0;
			k = 'A' - 10;
			st = "";
			string st2 = "";
			Save1(ref st, ref st2, ref did, d, k);			// first digit
			do
			{
				MultiFD.RMul1F(MultiID.baseO, t1);	// one digit at a time
				MultiFD.IntF(t1, t2);
				MultiFD.RFracF(t1);
				MultiFD.Get1F(out d, t2);
				Save1(ref st, ref st2, ref did, d, k);
				MultiID.MuInterrupt();
				if (MultiID.statReq)
					MultiID.WriteStatus("WriteFB: " + (st.Length + st2.Length) + " characters generated");
				if (MultiID.muAbort || MultiID.muErr)
					goto WriteFBEnd;
			}
			while (did < dToD && !MultiFD.ZTestF(t1));
			st += st2;
			i = st.Length - 1;
			while (i > 1)				// built for speed, don't call WriteT too often
			{
				ch = st.Substring(i, 1);
				if (ch == "0")			// delete trailing zeros and don't count them
				{
					st = st.Substring(0, i);
					did--;
				}
				else if (ch == ",")		// delete trailing commas
					st = st.Substring(0, i);
				else
					break;				// exit while
				i--;
			}
			if (z.S) MultiID.WriteT(outB, outF, "-", ref tot, width);
			if (st[0] > '9') MultiID.WriteT(outB, outF, "0", ref tot, width);
			MultiID.WriteT(outB, outF, st.Substring(0, 1) + ".", ref tot, width);
			if (outF != null) MultiID.WriteTLn(null, outF, "", ref tot);
			MultiID.WriteT(outB, outF, st.Substring(1), ref tot, width);
			if (outF != null) MultiID.WriteTLn(null, outF, "", ref tot);
			st = "";
			if (did > MultiID.muLenD)
				st = did.ToString() + " Digits ";
			MultiID.WriteT(outB, outF, stE + " (" + st + "Base " + MultiID.baseO + ")", ref tot, width);
			if (outF == null)
			{
				i = z.V[z.N-1].ToString().Length;
				st = (MultiID.baseM + z.V[0]).ToString();
				while (st[st.Length - 1] == '0') st = st.Remove(st.Length - 1, 1);
				i += st.Length - 1 - MultiID.muDMax;
				st = (i + (z.N - 1) * MultiID.muDMax).ToString();
				MultiID.WriteT(outB, outF, " [" + st + "]", ref tot, width);
			}
			WriteFBEnd:
			MultiFD.DoneF(t2);
			MultiFD.DoneF(bF);
			MultiFD.DoneF(t1);
			Common.LDelete(bI);
			Common.LDelete(nI);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			return;
		} // WriteFB

		private static void FormatStE(out string stE, decimal dE)
		// part of WriteFB
		{
			if (MultiID.baseO > 10) { stE = "`"; } 
			else { stE = "E"; }
			if (dE >= 0) { stE = stE + "+"; } 
			else { stE = stE + "-"; }
			string st = (Math.Abs(dE)).ToString();
			int k = st.Length;
			if (k > MultiID.muDpG && MultiID.muDpG != 0)
			{
				int j = k + 1;
				int iL = (k - 1) / MultiID.muDpG;
				for (int i = 1; i <= iL; i++)
				{
					j -= MultiID.muDpG;
					st = st.Substring(0, j - 1) + "," + st.Substring(j - 1);
				}
			}
			stE += st;
			st = "";
			return;
		}

		private static void Save1(ref string st, ref string st2, ref int did, int d, int k)
		// part of WriteFB
		{
			string ch;
			if (MultiID.muDpG > 0)
			{
				if ((did - 1) % MultiID.muDpG == 0)
				{
					if (did > 1) st2 = st2 + ",";
				}
			}
			if (d < 10)
				ch = d.ToString();
			else
				ch = ((char)(d + k)).ToString();
			st2 += ch;
			if (st2.Length >= 120)
			{
				st += st2;
				st2 = "";
			}
			did++;
			return;
		}

		public static void BernDL(MultiF nF, MultiF z)
		// z = denominator of B(n), the n'th Bernoulli number
		{
			if (nF.S) { SetToNM(MultiFD.fOne, z); return; }	// n negative
			MultiSI nSI = new MultiSI(nF.N);
			SetToFSI(nF, nSI);
			if (nSI == 0) { SetToNM(MultiFD.fOne, z); return; }
			if (nSI == 1) { SetToNM(MultiFD.fTwo, z); return; }
			if ((nSI.V[0] & 1) == 1) { SetToNM(MultiFD.fOne, z); return; }	// n odd > 1
			int i;					// index
			MultiF bD = new MultiF(Calc.fMC);
			bD &= 2;
			int[] count;		// count of the factors
			bool done;
			MultiF zIn = new MultiF(Calc.fMC);
			MultiF zz = new MultiF(Calc.fMC);
			MultiSI pT = new MultiSI(MultiID.sdInDbl);	// a temp power
			PrimeFA.ECM(nSI);
			int pN = 1;				// number of primes found for diag
			if (PrimeFA.complete)
			{
				count = new int[PrimeFA.nF + 1];		//ReDim count(1 To nF)
				for (i = 2; i <= PrimeFA.nF; i++)
					count[i] = 0;
				count[1] = 1;
				done = false;
				do
				{
					MultiID.SetTo1SI(1, zIn);
					for (i = 1; i <= PrimeFA.nF; i++)
					{
						MultiID.SetToDbSI(count[i], pT);	// pT = C
						MultiID.PowSI(PrimeFA.primes.a[i], pT, zz);	// zz = p^C
						MultiID.RMulSI(zz, zIn);			// zIn= zIn * zz
					}
					PrimeFA.AddToCounts(count, 1, ref done);
					MultiFD.RAddF(MultiFD.fOne, zIn);		// factor += 1
					if (PrimeFA.Prime(zIn) != 0)
					{
						MultiFD.MulF(zIn, bD, bD);
						pN++;
						if (MultiID.diagOn)
						{
							if (pN == 2)
								MultiID.WriteLn("2.0 is prime number 1");
							int tot = 0;
							MultiFD.WriteF(zIn, ref tot);
							MultiID.WriteLn(" is prime number " + pN);
						}
					}
				}
				while (!done && !MultiID.muAbort);
			}
			SetToNM(bD, z);
		} // BernDL

		public static void BernNL(MultiF nF, MultiF z)
		// z = numerator of B(n), the n'th Bernoulli number
		// "BD" = denominator of B(n) is put on the list of named numbers
		{
			int tI, t2I;
			Common.LFindAdd("BD", out tI);
			Calc.SetToN(MultiFD.fOne, tI);
			MultiSI nSI = new MultiSI(nF.N);
			SetToFSI(nF, nSI);
			if (nSI.S) { SetToNM(MultiFD.fZero, z); return; }	// n negative
			if (nSI == 0) { SetToNM(MultiFD.fOne, z); return; }
			if (nSI == 1)
			{
				SetToNM(MultiFD.fMOne, z); // B(1) = -1/2
				Calc.SetToN(MultiFD.fTwo, tI);
				return;
			}
			if ((nSI.V[0] & 1) == 1) { SetToNM(MultiFD.fZero, z); return; }	// n odd > 1
			MultiF bD = new MultiF(Calc.fMC);	// bD = denominator of B(n)
			MultiF bN = new MultiF(nSI.N);		// bN = floating n for B(n)
			MultiFD.SetToSIF(nSI, bN);
			BernDL(bN, bD);
			Calc.SetToN(bD, tI);
			MultiF kF = new MultiF(Calc.fMC);	// kF = 2*n! / (2*Pi)^n
			Common.InitNAdd(1, "BerNL-tI", out tI);
			Common.InitNAdd(1, "BerNL-t2I", out t2I);
			FacN(bN, tI, false, 0);							// t = n!
			MulN(MultiFD.fTwo, Common.nList[tI], tI);		// t = 2 * n!
			int pI = GetPiPtr();
			MulN(MultiFD.fTwo, Common.nList[pI], t2I);		// t2 = 2 * Pi
			PowN(Common.nList[t2I], bN, t2I);				// t2 = (2 * Pi) ^ n
			MultiFD.DivF(Common.nList[tI], Common.nList[t2I], kF);	// kF = t/t2
			MultiFD.MulF(bD, kF, kF);								// kF = bD * kF
			// nn = ceiling((K * d)^(1/(n-1)))
			AddN(bN, MultiFD.fMOne, tI);					// t = n - 1
			DivN(MultiFD.fOne, Common.nList[tI], tI);		// t = 1/(n-1)
			PowN(kF, Common.nList[tI], t2I);				// t2 = kF ^ t
			IntN(Common.nList[t2I], tI);					// t = Int(t2)
			if (Common.nList[tI].C != Common.nList[t2I].C)			// if t2 < t
				AddN(Common.nList[tI], MultiFD.fOne, tI);	// t = t + 1
			double maxN = Math.Pow(2, 53) - 1;
			double nn;
			MultiFD.GetDbInF(out nn, 0, maxN + 1, Common.nList[tI]);
			if (nn > maxN)
			{
				Common.ReportError("BernNL", "nn > " + maxN + ", too large for Bernoulli number");
				SetToNM(MultiFD.fZero, z);					// nn too large
				Common.LDelete(t2I);
				Common.LDelete(tI);
				return;
			}
			double p;				// current prime
			int i;					// index for table of primes
			int mC = 105000;		// number of primes each call of function NPrime
			int iQSize = 5500000;	// size of IQ and JQ arrays, must be large enough
			// to hold all primes <= Sqrt(Maximum integer testable)
			// Pi(Sqrt(2^53) = 5484598. 2^53 = 9,007,199,254,740,992 (16 Digits)
			double sqrt2N = Math.Sqrt(2 * nn);
			iQSize = 3 + (int)(1.2 * sqrt2N / Math.Log(sqrt2N));	// number of primes < Sqrt(2*nn)
			long mCL = 3 + (long)(1.2 * nn / Math.Log(nn));			// number of primes < nn
			if (iQSize > 5500000)
				iQSize = 5500000;
			if (mCL > 105000)
				mCL = 105000;
			mC = (int)mCL;
			new NPrimes();				// initialize class NPrimes
			new NPrimes(mC, iQSize);	// initialize NPrimes package
			MultiF pF = new MultiF(2);
			MultiF pPri = new MultiF(Calc.fMC);
			MultiF pPow = new MultiF(Calc.fMC);
			pPri &= 1;
			pPow &= 1;
			p = 2;
			for (; ; )
			{
				NPrimes.NPrime(mC, false);		// generate mC more primes in iP[1] thru iP[mC]
				for (i = 1; i <= mC; i++)
				{
					p = NPrimes.iP[i];
					if (p > nn)
					{
						new NPrimes(0, 0);		// release memory
						break;
					}
					pF &= p;
					PowN(pF, bN, tI);								// t = p^n
					SubN(Common.nList[tI], MultiFD.fOne, t2I);		// t2 = p^n - 1
					DivN(Common.nList[tI], Common.nList[t2I], tI);	// t = p^n / p^n - 1
					MultiFD.MulF(Common.nList[tI], pPow, pPow);		// pPow = Prod[p^n / p^n - 1]
					if (MultiID.muAbort || MultiID.muErr)
						break;
					if (MultiID.diagOn || MultiID.statReq2)
					{
						MultiID.WriteLn("BernNL: Using prime " + p + " <= " + nn + ", " +
							Math.Floor(100.0 * p / nn).ToString() + "% done");
						MultiID.statReq2 = false;
					}
				}
				MultiID.MuInterrupt();	// allow for abort
				if (MultiID.statReq3)
				{
					long totalMemory = GC.GetTotalMemory(false);
					string st = "BernNL: Prime = " + p + ", NN = " + nn + ", ";
					st += Math.Floor(100.0 * p / nn).ToString() + "% done, Memory in use = " + totalMemory;
					MultiID.WriteLn(st);
					st = "";
				}
				if (MultiID.muAbort || MultiID.muErr)
				{
					new NPrimes(0, 0);			// release memory
					SetToNM(MultiFD.fZero, z);
					MultiID.statReq3 = false;
					Common.LDelete(t2I);
					Common.LDelete(tI);
					return;
				}
				if (p > nn)
					break;
				if (MultiID.diagOn || MultiID.statReq3)
				{
					MultiID.WriteLn("BernNL: Getting more primes");
					MultiID.statReq3 = false;
				}
			}
			Common.LDelete(t2I);
			MulN(kF, pPow, tI);						// t = kF * t
			if (Common.nList[tI].C == 0)						// round t to nearest odd integer
				Common.nList[tI].V[0] |= 1;
			else if (Common.nList[tI].C < 0)
			{
				RIntN(tI);
				if (Common.nList[tI].C == 0)
					Common.nList[tI].V[0] |= 1;
				else if (Common.nList[tI].S)
					AddN(Common.nList[tI], MultiFD.fMOne, tI);
				else
					AddN(Common.nList[tI], MultiFD.fOne, tI);
			}
			new NPrimes(0, 0);				// release memory
			SetToNM(Common.nList[tI], z);
			if ((nSI.V[0] & 3) == 0)
				z.S = true;
			Common.LDelete(tI);
		} // BernNL

		public static void BetaL(MultiF x, MultiF z)
		// z = Beta(x) by prime numbers for large |x|
		{
			if (MultiFD.ZTestF(x))
			{
				SetToNM(MultiFD.fHalf, z);			// beta(0) = 1/2
				return;
			}
			if (x.C == 0 & x.S)
			{
				if ((x.V[0] & 1) == 1)
				{
					MultiFD.ClearF(z);					// beta(negative odd integer) = 0
					return;
				}
			}
			int tI, t2I;
			MultiF xL = new MultiF(x.N);
			MultiFD.SetToF(x, xL);
			if (x == 0) { SetToNM(MultiFD.fHalf, z); return; } // Beta(0) = 0.5
			MultiF lim = new MultiF(1);
			lim &= 100;
			Common.InitNAdd(1, "BetaL-tI", out tI);
			if (x <= lim && x >= -lim)
			{
				MultiID.WriteLn("BetaL: |X| <= 100, using Beta(X)");
				BetaN(x, tI);
				SetToNM(Common.nList[tI], z);
				Common.LDelete(tI);
				NormNM(z);
				return;
			}
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			bool reflection = (x < MultiFD.fHalf);
			if (reflection)								// if x < 1/2, use reflection
			{
				MultiF xx = new MultiF(Calc.fMC);
				MultiF be = new MultiF(Calc.fMC);
				MultiF zL = new MultiF(Calc.fMC);
				xx &= 1 - x;							// xx = 1 - x, use reflection formula
				BetaL(xx, be);							// be can have a reduced .M
				MultiFD.SetToF(be, zL);
				MultiFD.DoneF(be);
				if (!MultiID.muAbort && !MultiID.muErr)
				{
					ModN(xx, MultiFD.fFour, tI);						// xx > 0
					if (Common.nList[tI] == MultiFD.fOne)				// if x mod 4 == 1, sin is +1
					{
					}
					else if (Common.nList[tI] == MultiFD.fThree)		// if x mod 4 == 3, sin is -1
					{
						MultiFD.ChSF(zL);
					}
					else
					{
						bool degModeSave = Calc.degMode;
						Calc.degMode = true;
						RMul1N(90, tI);									// t = Pi * x / 2
						RSinN(tI);										// t = sin(Pi * x / 2)
						MultiFD.MulF(Common.nList[tI], zL, zL);			// z = t * z
						Calc.degMode = degModeSave;
					}
					int pI = GetPiPtr();
					MultiF d = new MultiF(Calc.fMC);
					MultiFD.DivF(MultiFD.fTwo, Common.nList[pI], d);	// 2 / Pi
					PowN(d, xx, tI);									// t = (2 / Pi) ^ x
					MultiFD.MulF(Common.nList[tI], zL, zL);				// z = t * zm
					GamNF(xx, tI);										// t = gamma(x)
					MultiFD.MulF(Common.nList[tI], zL, zL);				// z = t * z
				}
				Common.LDelete(tI);
				SetToNM(zL, z);
				MultiFD.expanded = saveExp;
				Calc.fMC = saveFMC;
				MultiID.mMax = saveMMax;
				NormNM(z);
				return;
			}
			Common.InitNAdd(1, "BetaL-t2I", out t2I);
			double p;				// current prime
			int i;					// index for table of primes
			int mC = 105000;		// number of primes each call of function NPrime
			int iQSize = 5500000;	// size of IQ and JQ arrays, must be large enough
			new NPrimes();				// initialize class NPrimes
			new NPrimes(mC, iQSize);	// initialize NPrimes package
			MultiF pF = new MultiF(2);
			MultiF pPri = new MultiF(Calc.fMC);
			MultiF pPow = new MultiF(Calc.fMC);
			pPri &= 1;
			pPow &= 1;
			p = 2.0;
			bool done = false;
			for (; ; )
			{
				NPrimes.NPrime(mC, false);		// generate mC more primes in iP[1] thru iP[mC]
				for (i = 1; i <= mC; i++)
				{
					p = NPrimes.iP[i];
					if (p == 2.0)
						continue;
					pF &= p;
					PowN(pF, xL, tI);							// t = p^n
					long rem;
					Math.DivRem((long)p, 4, out rem);
					if (rem == 1)
						SubN(Common.nList[tI], MultiFD.fOne, t2I);	// t2 = p^n - 1
					else
						AddN(Common.nList[tI], MultiFD.fOne, t2I);	// t2 = p^n + 1
					if (MultiFD.CompFF(Common.nList[tI], Common.nList[t2I]) == 0) // if t == t2
					{
						done = true;
						break;
					}
					DivN(Common.nList[tI], Common.nList[t2I], tI);	// t = p^n / p^n +/- 1
					MultiFD.MulF(Common.nList[tI], pPow, pPow);			// pPow = Prod[p^n / p^n +/- 1]
					if (MultiID.muAbort || MultiID.muErr)
						break;
					if (MultiID.diagOn || MultiID.statReq2)
					{
						MultiID.WriteLn("BetaL: Using prime " + p);
						//MultiID.WriteLn("BetaL: Using prime " + p + " <= " + nn + ", " +
						//	Math.Floor(100.0 * p / nn).ToString() + "% done");
						MultiID.statReq2 = false;
					}
				}
				MultiID.MuInterrupt();	// allow for abort
				if (MultiID.statReq3)
				{
					long totalMemory = GC.GetTotalMemory(false);
					//string st = "BetaL: Prime = " + p + ", NN = " + nn + ", ";
					//st += Math.Floor(100.0 * p / nn).ToString() + "% done, Memory in use = " + totalMemory;
					string st = "BetaL: Prime = " + p + ", Memory in use = " + totalMemory;
					MultiID.WriteLn(st);
					st = "";
				}
				if (MultiID.muAbort || MultiID.muErr)
				{
					new NPrimes(0, 0);			// release memory
					SetToNM(MultiFD.fZero, z);
					MultiID.statReq3 = false;
					Common.LDelete(t2I);
					Common.LDelete(tI);
					MultiFD.expanded = saveExp;
					Calc.fMC = saveFMC;
					MultiID.mMax = saveMMax;
					return;
				}
				if (done)
					break;
				if (MultiID.diagOn || MultiID.statReq3)
				{
					MultiID.WriteLn("BetaL: Getting more primes");
					MultiID.statReq3 = false;
				}
			}
			Common.LDelete(t2I);
			Common.LDelete(tI);
			new NPrimes(0, 0);				// release memory
			SetToNM(pPow, z);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			NormNM(z);
		} // BetaL

		public static void SetToFSI(MultiF x, MultiSI z) // z = Int(x)
		{
			MultiF n = new MultiF((int)(x.N + x.C));
			MultiFD.IntF(x, n);
			if (n.C > 0)
			{
				XICommon.ShiftL(n.C, n);
				n.C = 0;
			}
			SetToNM(n, z);
		} // SetToFSI

		public static void RIntSIN(int zI) // z = Int(z) as a SI
		{
			IntN(Common.nList[zI], zI);
			if (Common.nList[zI].C > 0)
			{
				MultiF n = new MultiF((int)(Common.nList[zI].N + Common.nList[zI].C));
				n &= Common.nList[zI];
				XICommon.ShiftL(Common.nList[zI].C, n);
				n.C = 0;
				Common.nList[zI].C = 0;
				SetToNM(n, (MultiSI)Common.nList[zI]);
			}
		} // RIntSIN

		public static void SetToNM(MultiSI x, MultiSI z) // z = x, resized
		// copy everything but .Nm name
		{
			int m = x.N;
			if (m != z.M)
			{
				z.V = new int[m + 1]; // resize V
				z.M = m;
			}
			MultiID.SetToSI(x, z);
		}

		public static void SetToNM(MultiF x, MultiF z) // z = x, resized
		{
			int nn;
			// how do you tell if two variables are the same? answer: Object.Equals(x, z)
			// or Object.ReferenceEquals(x, z) or x == z, but ReferenceEquals preferred here
			if (Object.ReferenceEquals(x, z))
			{
				NormNM(x);				// release any unused memory
				return;
			}
			nn = Math.Min(x.N, Calc.fMC);	// nn is the desired size
			if (nn != z.M)				// if z not correct size
			{
				MultiFD.ReSizeF(nn, z);	// resize it
			}
			if (nn == x.M)				// if x already that size
			{
				MultiID.SetToSIS(x, z);	// copy everything (but name)
				z.C = x.C;				// z = x; but you can't do it that way
				return;					// all done
			}
			MultiFD.SetToF(x, z);
			if (z.M > z.N)				// rounding occurred
				NormNM(z);				// release any unused memory
		} // SetToNM FF

		public static void NormNM(MultiF x) // normalize x with M = N, release unused memory
		{
			MultiFD.NormF(x);
			if (x.N <= 0) MultiFD.ClearF(x);
			int m = x.M;
			int n = x.N;
			if (m == n) return;
			Common.ReDimPreserve(ref x.V, n + 1);
			x.V[n] = 0; // Clear unused word
			x.M = n;
		}

		public static void CheckNMax() // fMC + 4 <= mMax <= nMax <= muNMax
		// fMC = mMax // for the Integer Calculator
		{
			int saveFMC = Calc.fMC;
			MultiID.mMax = Math.Max(MultiID.mMax, 6);
			MultiID.mMax = Math.Min(MultiID.mMax, Common.nMax);
			Calc.fMC = Math.Max(Calc.fMC, 2);
			Calc.fMC = Math.Min(Calc.fMC, MultiID.mMax - 4);
			MultiFD.fTn = Math.Max(MultiFD.fTn, 0);
			MultiFD.fTn = Math.Min(MultiFD.fTn, Calc.fMC - 2);
			if (Calc.fMC < saveFMC) Calc.ListSetM();
			double digits = Calc.fMC * MultiID.muDMax;
			double limD = 0.41 * digits + 150;
			// limD = 212; // diag test only
			if (limD > Common.maxBern)
			{
				Calc.limBern = Common.maxBern;
				MultiID.ADiag("CheckNMax: Max Bernoulli number too large");
			}
			else
			{
				Calc.limBern = 2 * (int)(limD / 2);
			}
			MultiID.ADiag("Max Bernoulli number to store = B(" + Calc.limBern + ")");
			limD = 0.55 * digits + 150;
			// limD = 212; // diag test only
			if (limD > Common.maxEuler)
			{
				Calc.limEuler = Common.maxEuler;
				MultiID.ADiag("CheckNMax: Max Euler number too large");
			}
			else
			{
				Calc.limEuler = 2 * (int)(limD / 2);
			}
			MultiID.ADiag("Max Euler number to store = E(" + Calc.limEuler + ")");
		} // CheckNMax

		public static void YankCon() // remove constant identifiers from the list
		// remove Cat, Ee, EulerC, Ln10, Phi, PhiP, Pi, and Ubiq
		{
			int zI;
			Common.LFind("Cat", out zI);	// search the list, if there, point to it
			if (zI != 0)
				Calc.CalDel(zI);
			Common.LFind("Ee", out zI);		// search the list, if there, point to it
			if (zI != 0)
				Calc.CalDel(zI);
			Common.LFind("EulerC", out zI);	// search the list, if there, point to it
			if (zI != 0)
				Calc.CalDel(zI);
			Common.LFind("Ln10", out zI);	// search the list, if there, point to it
			if (zI != 0)
				Calc.CalDel(zI);
			Common.LFind("Phi", out zI);	// search the list, if there, point to it
			if (zI != 0)
				Calc.CalDel(zI);
			Common.LFind("PhiP", out zI);	// search the list, if there, point to it
			if (zI != 0)
				Calc.CalDel(zI);
			Common.LFind("Pi", out zI);		// search the list, if there, point to it
			if (zI != 0)
				Calc.CalDel(zI);
			Common.LFind("Ubiq", out zI);	// search the list, if there, point to it
			if (zI != 0)
				Calc.CalDel(zI);
		} // YankCon

		public static void Ran() // Top = random number from random seed
		{
			int rNI;		// random number
			int i;
			if (Calc.fMC < (34 / MultiID.muDMax) + 1)
			{
				i = 0;
				Calc.RRanN(i);		// call RRanN for error message
				return;
			}
			Common.LFindAdd("RN", out rNI);
			DateTimePicker dateTime = new DateTimePicker();
			string timeSt = dateTime.Value.ToString();
			string st = "";
			double t = MultiID.TimerF();
			timeSt += t.ToString();
			for (i = 0; i < timeSt.Length; i++)
			{
				if ('0' <= timeSt[i] && timeSt[i] <= '9')
					st += timeSt[i];
			}
			Calc.ValueN10("1." + st + st, out i, rNI);
			RSqN(rNI);
			RSqN(rNI);
			Calc.RRanN(rNI);				// rN = (rNA * rN + rNC) % (base^5)
			Calc.SetToII(rNI, Common.iTop);
		} // Ran

		public static void CalFileNm(string stNm, ref string st, ref int zI)
		// get File name from command line in st and put it on the list in zI
		{
			string stF = Common.FileNm(ref st);
			string stL = stNm + ": " + stF;
			if (zI == 0)
				Common.InitNAdd(1, stL, out zI);
			else
				Common.nList[zI].Nm = stL;
			if (!Common.quiet)
			{
				MultiID.WriteTDLn(Common.nList[zI].Nm);
			}
		}

		public static bool DivFast(MultiF u, MultiF d, MultiF zOut) // zOut = u / d
		// u or d or both may have the same address in memory as this
		// u, d are not changed unless they share memory with this
		// Sets MuErr True if d = 0
		{
			int k, mL;
			k = Math.Min(zOut.M, Calc.fMC);
			if (!MultiFD.expanded) k += MultiFD.fGuard;	// n guard "digits", no sticky bit
			if (k > MultiID.muNMax) k = MultiID.muNMax;
			if (k < 13 || d.N < k / 3)
				return false;
			mL = zOut.M;
			if (!Object.ReferenceEquals(zOut, u) && !Object.ReferenceEquals(zOut, d))
				MultiFD.ReSizeF(1, zOut);
			MultiF z = new MultiF(k);
			MultiF zz = new MultiF(k);
			int pl = 5;
			int saveFMC = Calc.fMC;
			Calc.fMC = pl + 1;
			MultiF dL = new MultiF(pl + 1);
			MultiFD.SetToF(d, dL);			// dL is d at a reduced precision
			MultiFD.DivF(MultiFD.fOne, dL, zz);	// zz ~= 1 / d
			Calc.fMC = saveFMC;
			while (pl <= k)
			{
				pl += pl;
				if (pl > k) pl = k + 1;
				int p;
				for (p = k + 1; p > pl; p = (p + 1) / 2) ;
				pl = p;
				Calc.fMC = pl + 1;
				MultiFD.ReSizeF(pl + 1, dL);
				MultiFD.SetToF(d, dL);		// dL is d at a reduced precision
				MultiFD.MulF(zz, dL, z);	// zz = zz - zz * (zz * d - 1)
				MultiFD.RSubF(MultiFD.fOne, z);
				MultiFD.MulF(zz, z, z);
				if (MultiID.muUnderflow) { MultiFD.ClearF(zz); break; } // if underflow
				MultiFD.RSubF(z, zz);
			}
			Common.ReDimPreserve(ref z.V, 1);
			z.M = 0;
			dL.V = null;
			if (u != MultiFD.fOne) MultiFD.RMulF(u, zz);
			if (zOut.V != u.V && zOut.V != d.V)
				Common.ReDimPreserve(ref zOut.V, mL + 1);
			zOut.M = mL;
			MultiFD.SetToF(zz, zOut);
			Calc.fMC = saveFMC;
			return true;
		} // DivFast

		public static void EulerN(MultiF nF, MultiF z)
		// z = E(n), the n'th "small" Euler number
		{
			if (nF.S) { z &= 0; return; }	// n negative
			double db;
			MultiFD.GetDbInF(out db, 0, Common.maxEuler + 1, nF);
			int n = (int)db;
			if (n > Common.maxEuler)
			{
				Common.ReportError("EulerN", "n > " + Common.maxEuler + ", too large for Euler number");
				z &= 0;							// n too large
				return;
			}
			if (n == 0) { z &= 1; return; }
			if ((n & 1) == 1) { z &= 0; return; }			// n odd > 0
			if (n / 2 > Common.nEuler) Common.GenEulerI(n);
			MultiF bN = new MultiF(Calc.fMC);
			if (MultiID.muErr || MultiID.muAbort)
				bN &= 0;
			else
				MultiFD.SetToSIF(Common.euler[n / 2], bN);
			SetToNM(bN, z);
		} // EulerN

		public static void EulerL(MultiF x, MultiF z)
		// z = E(x), the x'th "large" Euler number
		{
			bool saveExp = MultiFD.expanded;
			int saveFMC = Calc.fMC;
			int saveMMax = MultiID.mMax;
			Expand();
			BetaL(-x, z);					// z can have a reduced .M
			MultiF zL = new MultiF(Calc.fMC);
			MultiFD.MulF(MultiFD.fTwo, z, zL);		// 2 * Beta(-x)
			SetToNM(zL, z);
			MultiFD.expanded = saveExp;
			Calc.fMC = saveFMC;
			MultiID.mMax = saveMMax;
			NormNM(z);
		} // EulerL

		public static void BernD(MultiF nF, MultiF z)
		// z = denominator of B(n), the n'th Bernoulli number
		{
			if (nF.S) { z &= 1; return; }	// n negative
			double db;
			MultiFD.GetDbInF(out db, 0, Common.maxBern + 1, nF);
			int n = (int)db;
			if (n > Common.maxBern)
			{
				Common.ReportError("BernD", "n > " + Common.maxBern + ", too large for Bernoulli number");
				z &= 1;						// n too large
				return;
			}
			if (n == 0) { z &= 1; return; }
			if (n == 1) { z &= 2; return; }
			if ((n & 1) == 1) { z &= 1; return; }	// n odd > 1
			if (n / 2 > Common.nBern) Common.GenBernI(n);
			MultiF bD = new MultiF(Calc.fMC);
			if (MultiID.muErr || MultiID.muAbort)
				bD &= 1;
			else
				MultiFD.SetToSIF(Common.bernD[n / 2], bD);
			SetToNM(bD, z);
		} // BernD

		public static void BernN(MultiF nF, MultiF z)
		// z = numerator of B(n), the n'th Bernoulli number
		{
			if (nF.S) { z &= 0; return; }	// n negative
			double db;
			MultiFD.GetDbInF(out db, 0, Common.maxBern + 1, nF);
			int n = (int)db;
			if (n > Common.maxBern)
			{
				Common.ReportError("BernN", "n > " + Common.maxBern + ", too large for Bernoulli number");
				z &= 0;						// n too large
				return;
			}
			if (n == 0) { z &= 1; return; }
			if (n == 1) { z &= -1; return; }
			if ((n & 1) == 1) { z &= 0; return; }		// n odd > 1
			if (n / 2 > Common.nBern) Common.GenBernI(n);
			MultiF bN = new MultiF(Calc.fMC);
			if (MultiID.muErr || MultiID.muAbort)
				bN &= 0;
			else
				MultiFD.SetToSIF(Common.bernN[n / 2], bN);
			SetToNM(bN, z);
		} // BernN

		private static double macheps;	// smallest such that 1.0+DBL_EPSILON != 1.0
		private static double machmin;	// min positive value

		public static double[] c;	// 26 items at 1 through 26, constants for series for 1/gamma(x)
		public static long limFac;	// limit to use factorial function vice gamma function
	}
}
