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

namespace MyNamespace
{
	/// <summary>
	///		Common routines for calculator programs
	///		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 Common
	{
		public Common()		// constructor logic
		{
			ClearBern();
			ClearEuler();
		}

		public static void RunCalc(string st)	// run on a Text stream inS
		{
			StreamReader inCF = null;			// run from inS (input screen) vice inCF input (code file)
			RunCalc(inCF, st);
		}

		// ------------------ the main command processor
		private static void RunCalc(StreamReader inCF, string st) // run on a text stream or a code file
		// byFile is true if input is from File, not by a TextBox
		{
			int i;
			int resSav;
			string ch;
			string st8;
			string fName;
			bool done = false;
			MultiID.muAbort = false;
			nested++;
			bool byFile = (inCF != null);
			if (!byFile) nested = 0;
			do
			{
				Calc.TestExpanded();
				noChange = false;
				MultiID.muErr = false;
				MultiID.muUnderflow = false;
				if (byFile)
				{
					ReadCom(inCF, out st);	// read next command from file
					if (st == null) break;	// null => EOF (end of file)
					if (st != "" && st[0] == '@') BatchOp(ref st);
					if (echoB)
					{
						MultiID.WriteTLn(Prompt() + " " + st); // echo to output textbox
						DoubleSpace();
					}
				}
				else
				{
					if (st == "")
						break;
					else
					{
						MultiID.WriteTLn(Prompt() + " " + st); // echo command as a prompt line
						DoubleSpace();
					}
				}
				GC.Collect();
				if ((echoB || !byFile) && MultiID.timeIt)
				{
					long totalMemory = GC.GetTotalMemory(false);
					DoubleSpace();
					MultiID.TimeIt("Start of execution, Memory in use = " + totalMemory);
				}
				st8 = st.Substring(0, Math.Min(8, st.Length));
				st8 = st8.ToUpper();
				if (saveTop && (st8 != "LX") && (st8 != "LT") && (st8 != "LASTTOP=")
						&& (st8 != "Z") && (st != ""))
				{
					LFindAdd("LastTop", out lastTop);
					Calc.SetToII(iTop, lastTop);
				}
				prSt = st.Trim(' ');	// needed for GoUpTo Label
				if (!byFile)
				{
					i = 0;				// delete line continuation markers in TextBox input
					do
					{
						i = st.IndexOf("\n", i + 1);
						if (i < 0) break;
						ch = st.Substring(i - 1, 1);
						if (ch == "+")
						{
							ReplaceAt(ref st, i - 1, "  "); // +\n -> 2 spaces
						}
						else if (ch == "-")
						{
							MultiID.Delete(ref st, i - 1, 2);
							i -= 2;		// -CrLf deleted and string closed up
						}
						else
						{
							ReplaceAt(ref st, i, ' '); // \n -> 1 space
						}
					}
					while (true);
				}
				int at = st.IndexOf("//");
				if (at >= 0) st = st.Remove(at, st.Length - at);
				st = st.Trim(' ');	// one final trim
				while (st != "")	// process next part on command line
				{
					char ch1 = st[0];
					string op = "";
					if (st.Length > 1) op = st.Substring(0, 2);
					bool assign = (ch1 == '=' || Calc.AssignmentOp(op));
					if ((ch1 >= '0' && ch1 <= '9' && ch1 <= MultiID.MaxDig(MultiID.baseI)[0]) ||
						"(+-=".IndexOf(ch1) >= 0 || assign)
					{
						if (!assign) st = "=" + st;
						st = nList[iTop].Nm + st;
					}
					resSav = 0;		// to prevent unassigned local variable alarm
					fName = "";		// same thing
					if (BatchOps(ref st)) {}									// a Batch command was executed
					else if (SkipLabel(ref st)) {}								// a Label was skipped
					else if (GoToLabel(ref prSt, ref st, inCF)) {}				// goTo statement was processed
					else if (Condition(ref st, iTop)) {}						// Conditional statement was processed
					else if (Calc.AProcedure(ref st, ref resSav, ref fName))	// a procedure was processed
						{Calc.ChkResSav(resSav, fName);}
					else if (Calc.AFunction(ref st, iTop)) {}					// a function was processed
					else if (Calc.Assignment(ref st, ref iTop)) {}				// assignment was made, iTop was changed
					else if (Calc.Constant(ref st, iTop)) {}					// it evaluated it to iTop
					else if (Calc.Primitive(ref st, iTop)) {}					// it evaluated it to iTop
					else
					{
						MultiID.WriteTDLn("Unknown operation, Command line discarded: |" + st);
						st = "";
					}
					if (MultiID.muErr)
					{
						ALet("muErr = 1");		// set muErr = 1 on nList
						MultiID.muErr = false;
						if (iTop >= saveI) Calc.ClearN(iTop);
					}
					MultiID.MuInterrupt();
					if (MultiID.muAbort)
					{
						exX = 0;
						st = "";
						if (iTop >= saveI) Calc.ClearN(iTop);
					}
					if (MultiID.softAbort)
					{
						ALet("softAbort = 1");		// set softAbort = 1 on nList
						MultiID.softAbort = false;	// not in DOS VPCalc program
						// softAbort on list will be 1 until a code file changes it
					}
					st = st.TrimStart(' ', ';');	// delete leading and trailing delimiters
					if (st == "" && exX > 0)
					{
						exX--;
						st = "X";
					}
					if (iTop <= cList) Calc.NormN(iTop);
					GC.Collect();	// force garbage collection of all generations
				} // end of while (st != "")
				if (echoB || !byFile)
				{
					long totalMemory = GC.GetTotalMemory(false);
					MultiID.TimeIt("End of execution, Memory in use = " + totalMemory);
				}
				if (byFile) done = (st == null || MultiID.muAbort);
				MultiID.muErr = false;
				if (autoD && (echoB || !byFile))
				{
					DoubleSpace();
					WriteNVLn(nList[iTop]); // display Top: x = xxx...
				}
			}
			while (!done && !MultiID.muAbort);
			nested--;
			// trap for error handling is in calling routine
		} // RunCalc

		public static void WriteNV(MultiN x)
		// output x name + V as text to outS, x not modified
		{
			int tot = 0;
			SingleSpace();
			Calc.WriteNV(MultiID.outS, null, x, ref tot, 0);
		}

		public static void WriteNV(MultiN x, ref int tot)
		// output x name + V as text to outS, x not modified
		{
			Calc.WriteNV(MultiID.outS, null, x, ref tot, 0);
		}

		public static void WriteNV(RichTextBox outB, StreamWriter outF, MultiN x)
		// output x name + V as text, x not modified
		{
			int tot = 0;
			SingleSpace();
			Calc.WriteNV(outB, outF, x, ref tot, 0);
		}

		public static void WriteNV(RichTextBox outB, StreamWriter outF, MultiN x, ref int tot)
		// output x name + V as text. x not modified
		{
			Calc.WriteNV(outB, outF, x, ref tot, 0);
		}

		public static void WriteNVLn(MultiN x)
		// output Self name + V and a new line to outS, x not modified
		{
			int tot = 0;
			SingleSpace();
			WriteNVLn(MultiID.outS, null, x, ref tot, 0);
		}

		public static void WriteNVLn(RichTextBox outB, StreamWriter outF, MultiN x)
		// output Self name + V and a new line, x not modified
		{
			int tot = 0;
			SingleSpace();
			WriteNVLn(outB, outF, x, ref tot, 0);
		}

		public static void WriteNVLn(RichTextBox outB, StreamWriter outF, MultiN x, ref int tot, int width)
		// output x name + V and a new line, x not modified
		{
			Calc.WriteNV(outB, outF, x, ref tot, width);
			MultiID.WriteLn(outB, outF);
			tot = 0;
		}

		public static void SingleSpace() // guarantee a single line space in outS
		{
			SingleSpace(MultiID.outS);
		}

		public static void SingleSpace(RichTextBox outB) // guarantee a single line space in output box
		{
			int iL = outB.Text.Length;
			if (iL > 0)
			{
				if (outB.Text.Substring(iL - 1) != "\n") MultiID.WriteLn(outB, null);
			}
		}

		public static void DoubleSpace(RichTextBox outB) // guarantee a double line space in output box
		{
			int iL = outB.Text.Length;
			if (iL > 0)
			{
				if (outB.Text.Substring(iL - 1) != "\n") MultiID.WriteLn(outB, null);
				if (outB.Text.Substring(outB.Text.Length - 2) != "\n\n") MultiID.WriteLn(outB, null);
			}
		}

		public static void DoubleSpace() // guarantee a double line space in outS
		{
			DoubleSpace(MultiID.outS);
		}

		private static string Prompt() // construct Command prompt
		{
			// comHL(1000000) = 1 // diag
			string st;
			DoubleSpace();
			if (MultiID.baseI != 10)
			{
				st = " (base " + MultiID.baseI.ToString() + "):";
			}
			else
			{
				st = ":";
			}
			if (nested == 0)
			{
				st = "Command" + st;
			}
			else
			{
				st = "Command" + nested.ToString() + st;
			}
			return st;
		}

		public static bool Condition(ref string st, int zI)
		// do a Conditional statement
		{
			int i;
			int j;
			string st1;
			string field;
			bool met;
			int tI;
			bool elseFound;
			string st5 = GetField(ref st);
			if (st5 != "IF") return false;
			DelNPBls(ref st, 2);
			InitNAdd(1, "Condition-tI", out tI);
			if (!Calc.Expression(ref st, tI))
			{
				ReportSin("Expression expected: (IF |", ref st);
			}
			else
			{
				met = !ZTestN(tI);
				st5 = GetField(ref st);
				if (st5 == "THEN") DelNPBls(ref st, 4);
				st1 = "";
				elseFound = false;
				while (st != "" && !elseFound)
				{
					if (DeleteComment(ref st, out field)) // comment was deleted
					{
						st1 += field;
					}
					else
					{
						field = GetField(ref st);
						if (field == "ELSE")
						{
							elseFound = true;
							DelNPBls(ref st, 4);
						}
						else
						{
							i = field.Length + 1;					// pick up delimiter also
							if (st.Length >= i && st[i-1] == '\"')	// if delimiter is a "
							{
								j = st.IndexOf("\"", i) + 1;		// pick up whole comment "asdf"...,
								if (j > 0)							// this was not in my DOS program
								{
									i = j;
								}
								else
								{
									i = st.Length;
								}
							}
							if (i > st.Length) i = st.Length;
							st1 += st.Substring(0, i);
							DelNPBls(ref st, i);
						}
					}
				}
				if (met) st = st1;
			}
			LDelete(tI);
			return true;
		} // Condition

		public static void LDelete(string st)		// delete item from list by name
		{
			int zI;
			LFind(st, out zI);
			LDelete(zI);
		}

		public static void LDelete(int zI)			// delete item from list by index
		{
			if (zI > cList || zI <= saveI) return;	// for safety, and never delete item saveI, 1 or 0
			for (int i = zI; i < cList; i++)		// move all higher items down one
			{
				nList[i] = nList[i + 1];			// just copy pointers
			}
			nList[cList] = null;					// delete highest item
			cList--;
		}

		public static void InitN(int nMaxI, string nm, int zI)
		// initialize named number z to zero with nMaxI super digits and name nm
		{
			nList[zI] = new MultiN(nMaxI, nm);
		}

		public static void InitNAdd(int nMaxI, string nm, out int zI) // init and add to list
		{
			if (cList == mList)
			{
				MultiID.MuWriteErr("InitNAdd: Named Number List Overflowed");
				zI = 0;
				return;
			}
			cList++;
			zI = cList;
			InitN(nMaxI, nm, zI);
		}

		public static bool ZTestN(int zI) // true if real part of z is Zero
		{
			if ((nList[zI].N != 1) || (nList[zI].V[0] != 0)) return false;
			nList[zI].S = false;   // side effect, prevents -0
			return true;
		}

		public static void ChSN(int zI) // change sign, n = - n (real part)
		{
			if (!ZTestN(zI)) nList[zI].S = !nList[zI].S;
		}

		public static void EnterN(ref string st, int zI)
		// set x(zI) to operator's input number
		// st is input and output, zI is input only but x(zI) changes
		{
			int i;
			Calc.ValueN(st, out i, zI); // may dispose and get new memory for value
			DelNPBls(ref st, i);
		}

		public static bool DeleteComment(ref string st, out string stCom)
		// delete comment, Rem, Pause, Echo and Learn lines
		// if nothing found, st not changed and stCom = ""
		// if found, st reduced and stCom = comment if one found
		{
			int i;
			string ch;
			string field;
			stCom = "";
			if (st[0] == '\"')
			{
				i = st.IndexOf("\"", 1) + 1;
				if (i == 0) i = st.Length;
				stCom = st.Substring(0, i);
				DelNPBls(ref st, i);
				return true;
			}
			field = GetField(ref st);
			i = field.Length + 1;
			if (st.Length < i)
			{
				ch = "=";
			}
			else
			{
				ch = st.Substring(i - 1, 1);
			}
			if (field == "ECHO" || field == "PAUSE" || field == "REM")
			{
				st = "";
				noChange = false; // not in DOS VPCalc
			}
			else if (field == "X" && ch != "=" && ch != ":" && ch != ")")
			{
				st = "";
				noChange = false;
			}
			else
			{
				return false;
			}
			return true;
		} // DeleteComment

		public static bool BatchOp(ref string st)	// process Batch operator @ECHO OFF
		{
			string field2;
			string field1 = GetField(ref st);		// @ is a delimiter so do special processing
			if (field1 != "") return false;
			int iL = st.Length;
			field1 = st.Substring(0, Math.Min(6, iL));
			field1 = field1.ToUpper();
			if (iL > 5 && inDelim(field1[5])) ReplaceAt(ref field1, 5, ' ');
			if (field1 != "@ECHO " && field1 != "@ECHO") return false;
			DelNPBls(ref st, 5);
			field2 = GetField(ref st);				// ECHO "" is comment
			if (field2 == "OFF" && st.Length == 3)
			{
				echoB = false;
			}
			else if (field2 == "ON" && st.Length == 2)
			{
				echoB = true;
			}
			st = "";
			return true;
		} // BatchOp

		public static bool BatchOps(ref string st)
		// process Batch operators ECHO, REM and PAUSE
		{
			string field1;
			string field2;
			if (BatchOp(ref st))
			{
				return true;
			}
			field1 = GetField(ref st);
			if (field1 == "ECHO")
			{
				DelNPBls(ref st, 4);
				field2 = GetField(ref st);	// ECHO "" is comment
				if (field2 == "OFF" && st.Length == 3)
				{
					echoB = false;
				}
				else if (field2 == "ON" && st.Length == 2)
				{
					echoB = true;
				}
				else if (echoB)
				{
					MultiID.WriteTLn2(st);
				}
			}
			else if (field1 == "PAUSE")
			{
				Global.objRunForm.WaitForPause();
			}
			else if (field1 != "REM")
			{
				return false;	// not ECHO, PAUSE or REM
			}
			st = "";
			return true;
		} // BatchOps

		public static bool SkipLabel(ref string st)	// skip a Label
		{
			string label1 = GetField(ref st);
			int i = label1.Length;
			if (i < st.Length && st[i] == ':')
			{
				DelNPBls(ref st, i + 1);
				MultiID.ADiag("Label skipped = " + label1 + ":");
				return true;
			}
			return false;
		}

		public static bool GoToLabel(ref string prSt, ref string st, StreamReader inCF)	// do a GoTo statement
		{
			bool upTo;
			string label1;
			bool findLabel;
			bool byFile = (inCF != null);
			string st7 = GetField(ref st);
			if (st7 != "GOTO" && st7 != "GOUPTO") return false;
			upTo = (st7 != "GOTO");
			if (upTo)
			{
				DelNPBls(ref st, 6);
			}
			else
			{
				DelNPBls(ref st, 4);
			}
			label1 = GetField(ref st);
			if (upTo) st = prSt;
			LookLabel(ref st, label1, out findLabel);
			if (findLabel) return true;
			if (!byFile) return true;
			if (upTo)
			{
				inCF.BaseStream.Seek(0, SeekOrigin.Begin);	// rewind inCF to beginning of the file
				inCF.DiscardBufferedData(); // call inCF.DiscardBufferedData() after you perform the Seek() operation
											// so the stream reader does not get stale data from its buffer.
											// This from mikeb in news group microsoft.public.dotnet.languages.csharp 5/14/2004
				if (!quiet) MultiID.WriteTLn("Rewinding code file");
			}
			do
			{
				st = inCF.ReadLine(); // read in next line
				if (st == null) { st = ""; break; } // unexpected end of file, label not found
				DelTabs(ref st);
				LookLabel(ref st, label1, out findLabel);
				if (findLabel) return true;
				MultiID.MuInterrupt();
			}
			while (!MultiID.muAbort);
			return true;
		} // GoToLabel

		public static void LookLabel(ref string st, string label1, out bool findLabel)
		{
			string label2;
			findLabel = false;
			while (st != "")
			{
				if (!DeleteComment(ref st, out label2))
				{
					noChange = false;					// new 2007-10-18
					label2 = GetField(ref st);			// comment was not deleted
					int i = label2.Length;
					if (st.Length > i && st[i] == ':')	// test Label
					{
						DelNPBls(ref st, i + 1);
						if (label1 == label2)
						{
							if (echoB)
							{
								MultiID.WriteTLn2("Went to " + label1 + ": " + st);
							}
						findLabel = true;
						return;
						}
					}
					else
					{
						DelNPBls(ref st, i + 1);
					}
				}
			}
		}

		public static bool OpenRead(out StreamReader inF, string fName, out string dir)
		// open file for Reading from CurrentDirectory or homeDir
		{
			string stEx1;
			string stEx2 = "";
			string savDir = Environment.CurrentDirectory.ToString();
			if (!quiet) DoubleSpace();
			dir = savDir;
			inF = null;
			if (OpenReadFile(out inF, fName, out stEx1))
			{
				if (!quiet)
				{
					MultiID.WriteDLn("File \"" + fName + "\" opened for reading");
					WCurDir(fName);
				}
				return true;
			}
			if (savDir != homeDir)
			{
				dir = homeDir;
				Directory.SetCurrentDirectory(homeDir);
				if (OpenReadFile(out inF, fName, out stEx2))
				{
					if (!quiet)
					{
						MultiID.WriteLn("File \"" + fName + "\" opened for reading");
						WCurDir(fName);
					}
					Directory.SetCurrentDirectory(savDir);
					return true;
				}
				Directory.SetCurrentDirectory(savDir);
			}
			dir = "";
			MultiID.WriteLn("Cannot open file \"" + fName + "\" for input");
			MultiID.WriteTLn("Not in current directory " + savDir);
			ReportError("OpenRead", stEx1);		// file not found most likely
			if (savDir != homeDir)
			{
				MultiID.WriteTLn("Not in home directory " + homeDir);
				ReportError("OpenRead", stEx2); // file not found most likely
			}
			return false;
		} // OpenRead

		public static bool OpenReadFile(out StreamReader inF, string fName, out String stEx)
		// open file for Reading from CurrentDirectory
		{
			inF = null;
			stEx = "";
			try
			{
				inF = new StreamReader(fName); // open fName for input as inF
				return true;
			}
			catch (Exception ex)
			{
				stEx = ex.Message.ToString();
				return false;
			}
		}

		public static bool OpenWrite(out StreamWriter outF, string fName) // open file for Writing
		{
			outF = null; // outF must be assigned to before control leaves the current method
			try
			{
				// open fName for output as outF
				outF = new StreamWriter(fName, false, Encoding.ASCII); // Appending = false
				if (!quiet)
				{
					MultiID.WriteDLn("File \"" + fName + "\" opened for writing");
					WCurDir(fName);
				}
				return true;
			}
			catch (Exception ex)
			{
				MultiID.WriteDLn("Cannot open file \"" + fName + "\" for output");
				WCurDir(fName);
				ReportError("OpenWrite", ex.Message);
				return false;
			}
		}

		public static bool ChkWrite(out StreamWriter outF, string fName, string st)
		// check start write of file
		{
			if (!OpenWrite(out outF, fName))
			{
				if (MakeReady() == DialogResult.OK)
				{
					if (!OpenWrite(out outF, fName))
					{
						MultiID.WriteLn("Aborting file write...");
						return false;
					}
				}
			}
			if (!quiet) MultiID.WriteTLn("Writing file \"" + fName + "\"" + st);
			return true;
		}

		public static bool OpenAppend(ref StreamWriter outF, string fName)	// open file for Writing
		{
			try
			{
				// open fName for Append as outF
				outF = new StreamWriter(fName, true, Encoding.ASCII);		//Appending = true
				return true;
			}
			catch (Exception ex)
			{
				MultiID.WriteDLn("Cannot open file \"" + fName + "\" for output");
				WCurDir(fName);
				ReportError("OpenAppend", ex.Message);
				return false;
			}
		}

		public static bool ChkAppend(ref StreamWriter outF, string fName)
		// check start Append to a file
		{
			if (!OpenAppend(ref outF, fName))
			{
				if (MakeReady() == DialogResult.OK)
				{
					if (!OpenAppend(ref outF, fName))
					{
						MultiID.WriteLn("Aborting file write...");
						return false;
					}
				}
				else
				{
					MultiID.WriteLn("Aborting file write...");
					return false;
				}
			}
			return true;
		}

		public static void ReportError(string who, string errorMessage) // report IOResult etc. error, errorMessage = caught.Message
		{
			GC.Collect();	// force garbage collection of all generations
			MultiID.muErr = true;
			if (errorMessage == "") return;
			if (MultiID.muErrRep) MultiID.WriteTLn(who + ": " + errorMessage);
		}

		public static DialogResult MakeReady() // pause for operator to make file/drive ready
		{
			DialogResult key = MessageBox.Show(Global.pName + ": Make file/drive ready and click \"OK\"\n" +
								StringOf(29, " ") + "or \"Cancel\"",
				Global.title,
				MessageBoxButtons.OKCancel,
				MessageBoxIcon.Exclamation);
			// pause ----------
			if (key != DialogResult.OK) MultiID.WriteLn("I/O operation aborted by operator!");
			return key;
		}

		public static void CalReadN(string fName, int xI)
		// read in a number from a file of given name, allow for make ready message
		{
			if (!Calc.ReadFile(fName, xI))
			{
				if (MakeReady() == DialogResult.OK)
				{
					if (!Calc.ReadFile(fName, xI))
					{
						MultiID.WriteLn("Aborting file input...");
					}
				}
			}
		}

		public static void ChangeDir(ref string st)
		// change the default disk directory and remove its name from command line
		{
			string stL;
			string savDir;
			if (st.Length == 0) st = "\""; // for safety
			stL = FileNm(ref st);
			savDir = "";
			try
			{
				if (!quiet) DoubleSpace();
				savDir = Environment.CurrentDirectory.ToString();
				if (stL == savDir) stL = "";
				if (stL != "") Directory.SetCurrentDirectory(stL);
			}
			catch
			{
				// -------- Trap for error handling
				MultiID.WriteLn("Path not found!");
			}
			finally
			{
				if (!quiet)
				{
					if (Environment.CurrentDirectory.ToString() != savDir)
					{
						MultiID.WriteTLn("Directory changed to \"" + Environment.CurrentDirectory.ToString() + '"');
					}
					else
					{
						MultiID.WriteTLn("Directory not changed \"" + Environment.CurrentDirectory.ToString() + '"');
					}
					MultiID.WriteTLn("Home directory is \"" + homeDir + '"');
				}
			}
		}

		public static string FileNm(ref string st)
		// return and remove file name from command line
		{
			int i;
			string stL;
			if (st.Length == 0)
			{
				return "";
			}
			i = st.IndexOf("\"", 1);
			if (i <= 0) i = st.Length;
			stL = st.Substring(1, i - 1);
			DelNPBls(ref st, i); // do not delete the trailing " if any
			return stL;
		}

		public static void ToggleLogg() //Toggle Logging of output screen to Log File
		{
			DateTimePicker dateTime = new DateTimePicker();
			string dateTimeString = dateTime.Value.ToString();
			if (!quiet) DoubleSpace();
			if (MultiID.logg)
			{
				string st = "Log file \"" + logFile + "\" Closed " + dateTimeString;
				if (!quiet)
				{
					MultiID.WriteLn(st);
					MultiID.outL.WriteLine();
				}
				MultiID.outL.Close();	// close the file
				logFile = "";			// allow for getting a new log file name next open
				MultiID.logg = false;
			}
			else
			{
				Calc.CalLFile(ref logFile); // get log file name
				bool check;
				check = ChkAppend(ref MultiID.outL, logFile);
				if (check) // open logFile for Append as MultiID.outL
				{
					MultiID.logg = true;
					string st = "Log file \"" + logFile + "\" Opened for Append " + dateTimeString;
					if (!quiet)
					{
						MultiID.WriteDLn(st);
						WCurDir(logFile);
						MultiID.outL.WriteLine(Global.title + ", " + Global.version);
						if (Global.system != "") MultiID.outL.WriteLine("Run on: " + Global.system);
					}
				}
				else
				{
					logFile = ""; // file did not open
					return;
				}
			}
			Global.objRunForm.SetLogCaption(); // call sub in Run form
		} // ToggleLogg

		public static void LFind(string name, out int zI)
		// find name on list, return zI = Index or 0 if not found
		{
			string iName;
			int cI = cList;			// set current Index to number on list
			name = name.ToUpper();
			zI = 0;
			while (cI != 0)
			{
				iName = nList[cI].Nm;
				iName = iName.ToUpper();
				if (iName == name)
				{
					zI = cI;
					cI = 0;
				}
				else
				{
					cI--;
				}
			}
		}

		public static void LFindAdd(string name, out int zI) // find name on list
		// and add item if not already on list
		{
			int i;
			LFind(name, out i);
			if (i == 0)
			{
				if (cList == mList)
				{
					MultiID.MuWriteErr("LFindAdd: Named Number List Overflowed");
					zI = 0;
					return;
				}
				cList++;
				i = cList;
				InitN(1, name, i);
			}
			zI = i;
		}

		public static void DelTabs(ref string st)
		// delete leading and trailing blanks, change Tabs to spaces, for code files
		// and DOS command line parameters
		// side effect, set noChange false to init processing on command
		{
			st = st.Replace("\t", " ");			// all Tab -> Space
			int i = -1;							// delete Tabs in Calc code files
			do
			{
				i = st.IndexOf('\t', i+1);		// index of a horizontal tab character
				if (i >= 0)
				{
					ReplaceAt(ref st, i, ' ');	// Tab -> Space
				}
			}
			while (i >= 0);
			st = st.Trim();						// trim all white space from start and end of command
			noChange = false;
		}

		public static void ReSizeHist(int newMax)	// change length of command history list
		{
			if (newMax == mComs || newMax < 1)
			{
				if (!quiet) MultiID.WriteDLn("Max commands in history not changed from " + mComs);
				return;
			}
			int d = cComs - newMax;					// d = number of commands to delete
			if (d < 0) d = 0;
			cComs = Math.Min(cComs, newMax);		// number of commands left
			string[] tComHL = new string[cComs+1];	// re-dimention arrays to 1 to newMax
			string[] tComPF = new string[cComs+1];
			iComs = cComs; // command to restore
			for (int j = 1; j <= cComs; j++)		// save commands in a temp array
			{
				tComHL[j] = comHL[j + d];			// delete oldest commands if d > 0
				tComPF[j] = comPF[j + d];
			}
			comHL = new string[newMax+1];			// re-dimention arrays to 1 to newMax
			comPF = new string[newMax+1];
			for (int j = 1; j <= cComs; j++)
			{
				comHL[j] = tComHL[j];				// copy cComs commands
				comPF[j] = tComPF[j];
			}
			if (!quiet) MultiID.WriteDLn("Max commands in history changed from " + mComs + " to " + newMax);
			mComs = newMax;
		}

		public static void ClearBern()		// clear Bernoulli number storage
		{
			if (nBern != 0 && !quiet)
			{
				MultiID.WriteDLn("Bernoulli number storage cleared");
				RunForm.FocusBothUs();
			}
			nBern = 0;
			bernN = new MultiSI[0];	// re-dimention arrays
			bernD = new MultiSI[0];
		}

		public static void ClearEuler()		// clear Euler number storage
		{
			if (nEuler != 0 && !quiet)
			{
				MultiID.WriteDLn("Euler number storage cleared");
				RunForm.FocusBothUs();
			}
			nEuler = 0;
			euler = new MultiSI[0];	// re-dimention arrays
		}

		public static void ClearHist()		// clear command history list
		{
			cComs = 0;
			comHL = new string[mComs+1];	// re-dimention arrays
			comPF = new string[mComs+1];
			if (!quiet)
			{
				MultiID.WriteDLn("Command history cleared");
				RunForm.FocusBothUs();
			}
		}

		public static void ClearLog()	// clear Log file
		{
			DateTimePicker dateTime = new DateTimePicker();
			string dateTimeString = dateTime.Value.ToString();
			bool logS = MultiID.logg;	// save Log status in a local
			if (!quiet) DoubleSpace();
			if (MultiID.logg)
			{
				string st = "Log file \"" + logFile + "\" Closed " + dateTimeString;
				if (!quiet)
				{
					MultiID.WriteLn(st);
					MultiID.outL.WriteLine();
				}
				MultiID.WriteLn(null, MultiID.outL);
				MultiID.outL.Close();		// close the file
				MultiID.logg = false;
			}
			logFile = "";
			Calc.CalLFile(ref logFile);	// get log file name, it may have changed
			// open logFile for output as outL, clear the file, outL gotten by open routine
			if (ChkWrite(out MultiID.outL, logFile, ""))
			{
				MultiID.logg = true;		// set to get message to the file
				string st = "Log file \"" + logFile + "\" Cleared " + dateTimeString;
				if (!quiet)
				{
					MultiID.WriteLn(st);
					WCurDir(logFile);
				}
				else
				{
					MultiID.outL.WriteLine(st);
				}
				MultiID.outL.WriteLine(Global.title + ", " + Global.version);
				if (Global.system != "") MultiID.outL.WriteLine("Run on: " + Global.system);
				if (!quiet)
				{
					MultiID.WriteLn();
				}
				else
				{
					MultiID.outL.WriteLine();
				}
				if (!logS)
				{
					MultiID.outL.Close();	// close the file
					MultiID.logg = false;
					logFile = "";			// clear name when it is closed, not if it’s open
				}
			}
		} // ClearLog

		public static void AddCom(string st, string sP)
		// add a command to the command history list
		{
			int j;
			bool found;		// true if command found in history list
			if (st == "") return;
			found = false;
			for (int i = cComs; i >= 1; i--)		// check newest first
			{
				if (st == comHL[i])					// if command already in history list
				{
					found = true;
					sP = comPF[i];					// save and use protect flag from old command
					for (j = i; j < cComs; j++)		// delete it from list
					{
						comHL[j] = comHL[j+1];		// freeing up comHL[cComs]
						comPF[j] = comPF[j+1];
					}
					break;
				}
			}
			if (!found)
			{
				cComs = cComs + 1;
				if (cComs > mComs)					// if too many commands, delete oldest command
				{
					cComs = mComs;
					for (j = 1; j < cComs; j++)
					{
						comHL[j] = comHL[j+1];		// free up comHL(cComs)
						comPF[j] = comPF[j+1];
					}
				}
			}
			comHL[cComs] = st;						// add command to top of list
			comPF[cComs] = sP;
			iComs = cComs;
		} // AddCom

		public static void RestoreHist() // read history file into command list
		{
			string st;
			string st2;
			string sP;
			int i;
			StreamReader inF = null;
			string dir = null;
			int NumRead = 0;
			if (!quiet) DoubleSpace();
			if (!OpenRead(out inF, histFile, out dir)) return;
			if (!quiet) MultiID.Write("Reading Commands from History file - Begin ... ");
			try
			{
				st = "";
				while (st != null && st.IndexOf("1: ") == -1) // test first to allow for an empty file
				{
					st = inF.ReadLine();		// get first string of first command
				}
				while (st != null && cComs <= comHL.GetUpperBound(0)) // loop until end of file or array is full
				{
					sP = st.Substring(0, 1);	// get protect flag
					st2 = "";
					i = st.IndexOf(" ", 1);
					st = st.Substring(i + 1);	// delete through first blank
					bool done = false;
					do
					{
						if (st2 != "") st2 += "\n";
						st2 += st;			// append next string to command
						st = inF.ReadLine();	// get the next string in command
						if (st != null && st.Length > 3 && st.Substring(0, 4) == "----")
							done = true;
					}
					while (st != null && !done); // until end of a command
					if (st != null)
					{
						AddCom(st2, sP);		// use this protect flag if not a duplicate command
						NumRead++;
						st = "";
						st2 = "";
						st = inF.ReadLine();	// get first string of next command
					}
				}
				if (!quiet)
				{
					MultiID.WriteLn("End");
					MultiID.Write(NumRead + " Command");
					if (NumRead != 1) MultiID.Write("s");
					MultiID.WriteLn(" read from History file, " + cComs + " total");
				}
			}
			catch (Exception ex)
			{
				ReportError("RestoreHist", ex.Message);
				MultiID.WriteLn("Aborted");
				MultiID.WriteLn("File is corrupted: \"" + histFile + '"');
			}
			finally
			{
				if (inF != null) // if the file was opened
				{
					inF.Close(); // close the file
					RunForm.FocusBothUs();
				}
			}
		} // RestoreHist

		public static void SaveHist() // save command history to file X?CalcHist.txt
		{
			int i;
			StreamWriter outF = null;
			string savDir;
			DoubleSpace();
			savDir = Environment.CurrentDirectory.ToString();
			Directory.SetCurrentDirectory(homeDir);
			DateTimePicker dateTime = new DateTimePicker();
			string dateTimeString = dateTime.Value.ToString();
			try
			{
				FileInfo FIa = new FileInfo(histFile);
				FileInfo FIb = new FileInfo(histBak);
				if (FIa.Exists)
				{
					FIb.Delete();			// delete the backup if any
					FIa.MoveTo(histBak);	// save (rename) previous History file as a backup
				}
			}
			catch (Exception ex)
			{
				ReportError("SaveHist", ex.Message);
			}
			try
			{
				outF = new StreamWriter(histFile);	// open the history file
				if (!quiet)
				{
					MultiID.WriteDLn("File " + histFile + " opened for writing");
					WCurDir(histFile);
					MultiID.Write("Writing all Commands to History file - Begin ... ");
				}
				outF.WriteLine("History of Command Input - " + dateTimeString);
				outF.WriteLine(Global.title + ", " + Global.version);
				if (Global.system != "") outF.WriteLine("Run on: " + Global.system);
				outF.WriteLine("");
				for (i = 1; i <= cComs; i++)		// write all previous commands
				{
					outF.WriteLine(comPF[i] + i + ": " + comHL[i]);
					outF.WriteLine(StringOf(40, "-"));
				}
				outF.WriteLine("End of file " + histFile);
				if (!quiet)
				{
					MultiID.WriteLn("End");
					MultiID.Write(cComs + " Command");
					if (cComs != 1) MultiID.Write("s");
					MultiID.WriteLn(" written to History file");
				}
				Directory.SetCurrentDirectory(savDir);
			}
			catch (Exception ex)
			{
				ReportError("SaveHist", ex.Message);
			}
			finally
			{
				if (outF != null)
				{
					outF.Close();					// close the file
					RunForm.FocusBothUs();
				}
			}
		} // SaveHist

		public static void FixFName(ref string fName, string ext)
		// fix file name to path + name + .extension
		{
			fName = fName.Trim(' ');					// remove leading and trailing blanks
			int i = fName.IndexOf(" ");
			if (i >= 0) fName = fName.Substring(0, i);	// get rid of all blanks
			fName = fName.TrimEnd('.');					// remove trailing '.'
			if (fName == "") return;
			int p = fName.IndexOf(@"\");				// find last "\"
			i = fName.IndexOf('.', p + 1);				// a "." after the last "\" starts extension
			if (i < 0 && ext != "")
				fName = fName + "." + ext;				// use default extension
			i = fName.IndexOf(":.");					// :. OR \. is a bad file name
			if (i < 0) i = fName.IndexOf(@"\.");
			if (i >= 0) fName = fName.Substring(0, i);	// if \.xxx, or :.xxx, .xxx deleted
		}

		public static void WCurDir(string fName)
		// write current disk directory with final \
		// using Environment.CurrentDirectory.ToString(): C:\ already has the \ but non-root directories do not
		{
			// this can garble some output, but good info
			string cD = Directory.GetCurrentDirectory();
			if (cD.Substring(cD.Length - 1) != @"\") cD += @"\";
			if (fName.Length > 1 && fName[1] == ':')
			{
				MultiID.WriteTLn("Directory name = \"" + cD + '"');
			}
			else
			{
				MultiID.WriteTLn("Full name = \"" + cD + fName + '"');
			}
		}

		public static void ALet(string st)		// do a literal assignment statement
		{
			int tI = 0;							// for example ALet('y = 1 + Sq(x)')
			st = st.Trim();						// tI changed by Assignment()
			Calc.Assignment(ref st, ref tI);	// st not returned
		}

		public static void ReadCom(StreamReader inCF, out string st)
		// read in one command line from open file inCF including continuation lines
		{
			string stL;
			string st1;
			char ch;
			bool bContinue;
			string cat = " ";
			st = ""; // for safety
			try
			{
				do
				{
					bContinue = false;
					stL = inCF.ReadLine();	// get line of command
					if (stL == null)
					{
						st = null;			// null => end of file
						return;
					}
					DelTabs(ref stL);
					if (stL.Length > 0)
					{
						ch = stL[stL.Length-1];
					}
					else
					{
						ch = ' ';
					}
					if (ch == '+' || ch == '-')
					{
						bContinue = true;
						ReplaceAt(ref stL, stL.Length - 1, ' ');
						stL = stL.TrimEnd(' ');
					}
					st1 = " ";
					if (stL == "")
					{
						st1 = "";
					}
					else if (inDelim(stL[0]))
					{
						st1 = "";
					}
					else if (st == "")
					{
						st1 = "";
					}
					else if (inDelim(st[st.Length-1]))
					{
						st1 = "";
					}
					if (st1 == " ") st1 = cat;
					if (ch == '+')
					{
						cat = " ";
					}
					else
					{
						cat = "";
					}
					if (st == "")
					{
						st = stL;
					}
					else
					{
						st = st + st1 + stL;
					}
				}
				while (bContinue);
			}
			catch
			{
				// -------- Trap for error handling
				st = null; // read error same as EOF
			}
		} // ReadCom

		public static bool inDelim(char ch)
		// is the character ch in the list of Delimiters
		{
			return (delim.IndexOf(ch) >= 0);
		}

		public static void DelNPBls(ref string st, int n)
		// delete 1st n characters plus new leading blanks, Side effect: noChange = false;
		{
			noChange = false;
			if (n >= st.Length) { st = ""; return; }
			st = st.Substring(n).TrimStart(' ');
		}

		public static void ReportSin(string message, ref string st)
		// report first syntax error, clear st
		{
			if (!MultiID.muErr)
				MultiID.WriteTDLn(message + st);
			MultiID.muErr = true;
			st = "";
		}

		public static string GetField(ref string st) // get and remove next field
		// assumes no leading blanks, no trailing blank, no tabs. Returns first string
		// in caps up to first delimiter or blank, may be null, deletes extra blanks
		// after first field in st, returned field not removed or changed.
		// noChange true until DelNPBls is called to delete the field from st
		{
			int i;
			int j;
			int iL;
			if (noChange) return oldField;
			noChange = true;
			iL = st.Length;
			if (iL == 0)
			{
				oldField = "";
				return oldField;
			}
			i = 0;
			while (i < iL && st[i] != ' ' && !inDelim(st[i])) i++;
			oldField = st.Substring(0, i);
			oldField = oldField.ToUpper();
			if (i >= iL || st[i] != ' ') return oldField;
			j = i + 1;
			while (j < iL && st[j] == ' ') j++;		// st: "" => "", "1" => "1"
			if (j < iL && !inDelim(st[j])) j--;		// "1	  2" => "1 2"
			MultiID.Delete(ref st, i, j-i);			// "1	;  2" => "1;  2"
			return oldField;
		}

		public static bool RunFile(string fName)
		{
			StreamReader r; // text file to read from
			string st;		// operator input string
			string dir;
			if (fName.Length == 0) return false; // noOp, would have input from TextBox
			if (!OpenRead(out r, fName, out dir)) return false;
			if (!quiet) MultiID.WriteLn("Running code file \"" + fName + '"');
			Global.objRunForm.AddToTitle(" " + '"' + fName + '"');
			st = "";
			RunCalc(r, st);
			r.Close();
			noChange = false;
			if (!quiet) MultiID.WriteDLn("File \"" + fName + "\" closed");
			Global.objRunForm.DelFromTitle(" " + '"' + fName + '"');
			return true;
		}

		public static void CalExLrn(ref string st) // save or execute learned line, remove it from st
		{
			string stL;
			int i = st.Length;
			ReplaceAt(ref st, 0, ' ');
			st = st.TrimStart(' ');
			stL = "Lrn: " + st; // will have one blank space after "Lrn:"
			if (lrnL == 0)
			{
				// if you do not have a learn line, make one, but don't execute it
				InitNAdd(1, stL, out lrnL);
				st = " ";
			}
			else
			{
				if (i > 1)
				{
					nList[lrnL].Nm = stL;	// if something is following the x, Change the learn line
					st = " ";
				}
				else						// x by itself so execute the learn line
				{
					st = nList[lrnL].Nm.Substring(4);	// remove "Lrn:" from "Lrn: xxx... "
					DoubleSpace();
					MultiID.WriteTLn2(nList[lrnL].Nm); // return st as new command line
					prSt = st.Trim(' ');
					AddCom(st.Substring(1), " ");
					if (st == " X" || st == " X") st = " "; // prevent loop
					// the caller will delete the leading space in st
				}
			}
		}

		public static void CalDLrn(ref string st)	// display learned line
		{
			st = " " + st.Substring(1);				// change first character to a blank
			if (lrnL != 0)
			{
				// save command string from the learned line for F3 and F4 function keys
				AddCom(nList[lrnL].Nm.Substring(5), " "); // remove "Lrn: " from "Lrn: ... "
				DoubleSpace();
				MultiID.WriteTLn(nList[lrnL].Nm);
			}
		}

		public static void ReplaceAt(ref string oldS, int i, string newS) // change oldS at i to newS
		{
			oldS = oldS.Substring(0, i) + newS + oldS.Substring(i + newS.Length);
		}

		public static void ReplaceAt(ref string oldS, int i, char ch) // change oldS at i to ch
		{
			oldS = oldS.Substring(0, i) + ch + oldS.Substring(i + 1);
		}

		public static string StringOf(int n, string stIn) // repeat stIn, n times
		{
			string stOut = "";
			for (int i = 0; i < n; i++)
			{
				stOut += stIn;
			}
			return stOut;
		}

		public static void DisplayPriority() // display priority status
		{
			string st = Thread.CurrentThread.Priority.ToString();
			MultiID.WriteDLn("Priority is " + st);
		}

		public static void SetPriority(int p) // change priority to p
		{
			if (p == 2)
				Thread.CurrentThread.Priority = ThreadPriority.Highest;
			else if (p == 1)
				Thread.CurrentThread.Priority = ThreadPriority.AboveNormal;
			else if (p == -1)
				Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
			else if (p == -2)
				Thread.CurrentThread.Priority = ThreadPriority.Lowest;
			else
			{
				p = 0;
				Thread.CurrentThread.Priority = ThreadPriority.Normal;
			}
			pri = p;
		}

		public static void ReDimPreserve(ref int[] v, int n)
		// re-dimension array but preserve old values, n is new length
		// v must be ref because its pointer is changed by v = new int[n]
		{
			if (n == v.GetLength(0)) return;
			int m = Math.Min(n, v.GetLength(0));	// copy m values, m <= n
			try
			{
				int[] tV = new int[m];				// if n is smaller than old length,
				for (int j = 0; j < m; j++)			// upper values are lost
				{
					tV[j] = v[j];					// save m values in a temp array
				}
				v = new int[n];						// re-dimention
				for (int j = 0; j < m; j++)
				{
					v[j] = tV[j];					// restore m values
				}
			}
			catch (Exception ex)
			{
				long totalMemory = GC.GetTotalMemory(false);
				string st = ex.Message + ", M = " + n + ", v set to zero, Memory used = " + totalMemory;
				v = new int[2];
			}
		}

		public static void ReDim(ref int[] v, int n)
		// re-dimension array, don't preserve old values, n is new length
		// v must be ref because its pointer is changed by v = new int[n]
		{
			if (n == v.GetLength(0)) return;
			int m = Math.Min(n, v.GetLength(0));	// copy m values, m <= n
			try
			{
				v = new int[n];						// re-dimention
			}
			catch (Exception ex)
			{
					long totalMemory = GC.GetTotalMemory(false);
					string st = ex.Message + ", M = " + m + ", z set to zero, Memory used = " + totalMemory;
				ReportError("ReDim", st);
				v = new int[2];
			}
		}

		public static void GenBernI(int nX)
		// generate even numbered Bernoulli numbers B(0) thru B(nI) = bernN[nX/2] / bernD[nX/2]
		{
			try
			{
				if (nX > maxBern)
				{
					MultiID.WriteDLn("GenBernI: Bernoulli n too large");
					return;
				}
				int i, k, m;
				if (nX < 4) nX = 4;
				m = (nX + 1) / 2;
				nX = m * 2;
				if (nBern >= m)
				{
					if (!quiet) MultiID.WriteLn("Have Bernoulli numbers upto B(" + 2 * nBern + ")");
					return;
				}
				if (!quiet) MultiID.WriteLn("Generating Bernoulli numbers upto B(" + nX + ")");
				nBern = m;
				bernN = new MultiSI[nBern+1];	// re-dimention arrays
				bernD = new MultiSI[nBern+1];
				bernN[0] = new MultiSI(1);
				bernD[0] = new MultiSI(1);
				bernN[0] &= 1;					// B(0) = 1
				bernD[0] &= 1;
				int nH = ((nX + 3) / 4) * 2;
				int nD = nH / 2 + 1;
				MultiSI[] t = new MultiSI[nD];
				for (i = 0; i < nD; i++)
				{
					t[i] = new MultiSI();
					MultiID.MuInterrupt();
					if (MultiID.muAbort)
					{
						ClearBern();
						MultiID.ADebug("MuAbort exit GenBern");
						t = new MultiSI[0];
						return;
					}
				}
				MultiSI t0 = new MultiSI();
				MultiSI t1 = new MultiSI();
				MultiSI t2 = new MultiSI();
				MultiSI b1 = new MultiSI();
				MultiSI b2 = new MultiSI();
				MultiSI q = new MultiSI();
				// 1st half
				t[0] &= 1;					// t[0] = 1;
				int n = 1;
				q &= 1;
				int kX = 0;
				do
				{
					// for odd n
					t1 &= t[0];
					for (k = 1; k <= kX; k++)
					{
						t2 &= t[k];
						t[k] &= (2*k - 1) * t1 + (2*k + 1) * t[k];
						t1 &= t2;
					}
					t[k] &= (2*k - 1) * t1;
					t0 &= t[0];
					n++;
					// for even n
					for (k = 0; k <= kX; k++)
						t[k] &= 2*k * t[k] + 2*(k+1) * t[k+1];
					t[k] &= 2*k * t[k];
					q *= 4;
					b1 &= n * t0;
					b2 &= q * (q - 1);
					ReduceSI(b1, b2);
					b1.S = ((n & 3) == 0); // if n is a multiple of 4
					bernN[n/2] = new MultiSI(b1.N);
					bernD[n/2] = new MultiSI(b2.N);
					bernN[n/2] &= b1;
					bernD[n/2] &= b2;
					if (MultiID.statReq2)
					{
						MultiID.nDone = n;
						MultiID.nToGo = nX - n;
						MultiID.WritenDone("GenBernI:");
					}
					MultiID.MuInterrupt();
					if (MultiID.muAbort)
					{
						nBern = n/2 - 1;
						MultiID.ADebug("MuAbort exit GenBern");
						if (!quiet) MultiID.WriteLn("Have Bernoulli numbers upto B(" + 2 * nBern + ")");
						return;
					}
					n++;
					kX++;
				}
				while (n <= nH);
				// 2nd half
				do
				{
					kX--;
					// for odd n
					t1 = t[0];
					for (k = 1; k <= kX; k++)
					{
						t2 = t[k];
						t[k] = (2*k-1) * t1 + (2*k+1) * t[k];
						t1 = t2;
					}
					t0 &= t[0];
					n++;
					// for even n
					for (k = 0; k <= kX; k++)
						t[k] = 2*k*t[k] + 2*(k+1) * t[k+1];
					q *= 4;
					b1 &= n * t0;
					b2 &= q * (q-1);
					ReduceSI(b1, b2);
					b1.S = ((n & 3) == 0); // if n is a multiple of 4
					bernN[n/2] = new MultiSI(b1.N);
					bernD[n/2] = new MultiSI(b2.N);
					bernN[n/2] &= b1;
					bernD[n/2] &= b2;
					if (MultiID.statReq2)
					{
						MultiID.nDone = n;
						MultiID.nToGo = nX - n;
						MultiID.WritenDone("GenBernI:");
					}
					MultiID.MuInterrupt();
					if (MultiID.muAbort)
					{
						nBern = n/2 - 1;
						MultiID.ADebug("MuAbort exit GenBern");
						if (!quiet) MultiID.WriteLn("Have Bernoulli numbers upto B(" + 2 * nBern + ")");
						return;
					}
					n++;
				}
				while (n <= nX);
				MultiID.DoneSI(q);
				MultiID.DoneSI(b2);
				MultiID.DoneSI(b1);
				MultiID.DoneSI(t2);
				MultiID.DoneSI(t1);
				MultiID.DoneSI(t0);
				t = new MultiSI[0];
			}
			catch
			{
				MultiID.WriteDLn("GenBernI: Bernoulli n too large");
				ClearBern();
			}
		} // GenBernI

		public static void GenEulerI(int nX)
		// generate even numbered Euler numbers E(0) thru E(nI) = euler[nX/2]
		{
			try
			{
				if (nX > maxEuler)
				{
					MultiID.WriteDLn("GenEulerI: Euler n too large");
					return;
				}
				int i, m;
				if (nX < 4) nX = 4;
				m = (nX + 1) / 2;
				nX = m * 2;
				if (nEuler >= m)
				{
					if (!quiet) MultiID.WriteLn("Have Euler numbers upto E(" + 2 * nEuler + ")");
					return;
				}
				if (!quiet) MultiID.WriteLn("Generating Euler numbers upto E(" + nX + ")");
				nEuler = m;
				euler = new MultiSI[nEuler+1];			// re-dimention array
				MultiSI n = new MultiSI(MultiID.mMax);
				MultiSI d = new MultiSI(MultiID.mMax);
				MultiSI t = new MultiSI(MultiID.mMax);
				MultiSI r = new MultiSI(MultiID.mMax);
				MultiSI nx = new MultiSI(MultiID.mMax);
				MultiSI dx = new MultiSI(MultiID.mMax);
				MultiSI sum = new MultiSI(MultiID.mMax);
				euler[0] = new MultiSI(1);
				euler[0] &= 1;							// E(0) = 1
				for (i = 1; i <= m; i++)
				{
					MultiID.SetTo1SI(-1, sum);			// first term is -1
					MultiID.SetTo1SI(1, n);
					MultiID.SetTo1SI(1, d);
					MultiID.SetTo1SI(i+i, nx);
					MultiID.SetTo1SI(1, dx);
					for (int j = 1; j < i; j++)
					{
						MultiID.MulSI(n, nx, n);		// update numerator
						MultiID.Add1SI(nx, -1, nx);
						MultiID.MulSI(n, nx, n);
						MultiID.Add1SI(nx, -1, nx);
						MultiID.MulSI(d, dx, d);		// update denominator
						MultiID.Add1SI(dx, 1, dx);
						MultiID.MulSI(d, dx, d);
						MultiID.Add1SI(dx, 1, dx);
						MultiID.DivQRSI(n, d, r, t);	// compute next term
						t.S = true;
						MultiID.MulSI(t, euler[j], t);
						MultiID.AddSI(sum, t, sum);		// update sum
					}
					euler[i] = new MultiSI(sum.N);
					MultiID.SetToSI(sum, euler[i]);
					MultiID.MuInterrupt();
					if (MultiID.statReq2)
					{
						MultiID.nDone = i + i;
						MultiID.nToGo = nX - MultiID.nDone;
						MultiID.WritenDone("GenEulerI:");
					}
					if (MultiID.muAbort)
					{
						nEuler = i - 1;
						MultiID.ADebug("MuAbort exit GenEuler");
						break;
					}
				}
				MultiID.DoneSI(sum);
				MultiID.DoneSI(dx);
				MultiID.DoneSI(nx);
				MultiID.DoneSI(r);
				MultiID.DoneSI(t);
				MultiID.DoneSI(d);
				MultiID.DoneSI(n);
			}
			catch
			{
				MultiID.WriteDLn("GenEulerI: Euler n too large");
				ClearEuler();
			}
		} // GenEulerI

		public static void ReduceSI(MultiSI x, MultiSI y)
		// reduce the rational number x/y to its lowest terms
		{
			MultiSI z = new MultiSI();
			MultiID.GCDSI(x, y, z);
			x &= x / z;
			y &= y / z;
			MultiID.DoneSI(z);
		}

		public static bool FMBIok(int zI, out int fMBI)
		{
			Common.LFindAdd("FMB", out fMBI);
			if (fMBI == zI)
			{
				MultiID.MuWriteErr("Cannot set FMB with a Mod FMB function, FMB cleared");
				Calc.ClearN(fMBI);
				return false;
			}
			return true;
		}

		public static int maxBern = 10000000;	// Maximum Bernoulli number index
		public static int maxEuler = 10000000;	// Maximum Euler number index
		public static int nBern;				// number of Bernoulli number in the arrays B(2*nBern) in [nBern]
		public static int nEuler;				// number of Euler number in the arrays E(2*nEuler) in [nEuler]
		public static MultiSI[] bernD;			// array of Bernoulli number denominators
		public static MultiSI[] bernN;			// array of Bernoulli number numerators
		public static MultiSI[] euler;			// array of Euler numbers

		// -------- List of MultiN in a dynamic array
		public static int cList;				// current number of items on the nList
		public static int mList;				// max length of nList, its array size
		public static int nMax;					// max N for numbers on the list, fixed at startup
		public static MultiN[] nList;			// named number list
//		public static int MultiID.muNMax 		// max number of super digits in a multi-precision
//		public static int MultiID.mMax;			// current Max number of super digits
//		public static int Calc.fMC;				// current max # of super digits in a result
//		Note: fMC <= mMax <= nMax <= muNMax But fMC = mMax for the Integer Calculator
		// -------- End of nList

		// list of command history of operator input in a dynamic array
		public static int cComs;		// current # of commands saved in history list
		public static int iComs;		// number of Command to restore to Text Input Box
		public static int mComs;		// max # of commands you can save, array size
		public static string[] comHL;	// the saved command history list (1 to mComs)
		public static string[] comPF;	// the saved command history protect flag
		// end of Command History List

		public static bool aboutUp;		// true if About form is up and running
		public static bool autoD;		// auto Display flag
		public static bool configUp;	// true if Config form is up and running
		public static bool echoB;		// true if Batch file echo flag is on
		public static bool historyUp;	// true if History form is up and running
		public static bool noChange;	// true after GetField until DelNPBls is called
		public static bool quiet;		// true if "quiet" mode is on
		public static bool running;		// true if Calc is currently processing a command
		public static bool saveTop;		// true if option "save Top value in LastTop" is on
		public static bool tExitFlag;	// command to totally exit the program
		public static bool tQuitReq;	// request to totally quit the program
		public static Button runCmdLog; // command button on Run form
		public static int comI;			// comment item, its name has the comment/File name
		public static int exX;			// number of times to execute the learned line
		public static int iTop;			// current Accumulator is nList[iTop]
		public static int lastTop;		// last value of iTop on the list
		public static int lrnL;			// learned line list item, its name is the learned line
		public static int nested;		// number of nettings of the RunCalc procedure, 0 = bad
		public static int pri;			// execution priority [-2, 2]
		public static int saveI;		// number of items on the list never to be deleted 1 or 2
		public static int selectFilter;	// file selected FilterIndex the Config Form
		public static RichTextBox inS;	// input Screen source of commands
		public static string delim;		// delimiters, ;, +, ...
		public static string histBak;	// file name of command history backup file
		public static string histFile;	// file name of command history file
		public static string homeDir;	// initial disk directory when program started
		public static string logFile;	// file name of log file or "" if not logging
		public static string oldField;	// last field gotten by GetField, if noChange true
		public static string prSt;		// previous operator input string, needed for GoUpTo Label
		public static string selectFile;// file selected on the Config Form
		public static string selectPath;// path selected on the Config Form
	}
}
