public static string[] SplitValuesDelimitedByTabOrMultipleSpaces(string sVal) {
			string[] sarrReturn=null;
			ArrayList alNow=null;
			if (sVal!=null) {
				alNow=new ArrayList();
				bool bFoundMoreThanOneValueLeftInDestructableString_Now=true;//MUST start true so first run of loop happens
				int[] iarrBreak=new int[sarrTabSeparatedSeparationsStartWith.Length];
				int iBreakAt=-1;//the current STRING index in the Haystack string
				int iBreakType=0;//the current separator string ARRAY index in the for loop
				int iBreakTypeFirst=-1;//the string ARRAY index first separator (in the separator array) that occurs in the Haystack string
				while (bFoundMoreThanOneValueLeftInDestructableString_Now) {
					bFoundMoreThanOneValueLeftInDestructableString_Now=false;
					for (int iFlag=0; iFlag<sarrTabSeparatedLineHasMoreThanOneChunkIfContains.Length; iFlag++) {
						if ( RString.Contains(sVal,sarrTabSeparatedLineHasMoreThanOneChunkIfContains[iFlag]) ) {
							//This is a valid way of knowing if has inline column names, since removed whitespace from ends of destructable string already after each substring operation above
							bFoundMoreThanOneValueLeftInDestructableString_Now=true;
							//Do NOT break here, as code below must run.
						}
					}
					if (bFoundMoreThanOneValueLeftInDestructableString_Now) {
						for (iBreakType=0; iBreakType<iarrBreak.Length; iBreakType++) {
							iarrBreak[iBreakType]=sVal.IndexOf(sarrTabSeparatedSeparationsStartWith[iBreakType]);
						}
						iBreakAt=-1;
						iBreakTypeFirst=-1;
						for (iBreakType=0; iBreakType<iarrBreak.Length; iBreakType++) {
							if ( (iarrBreak[iBreakType]>=0) && (iBreakAt<0||iarrBreak[iBreakType]<iBreakAt) ) {
								iBreakAt=iarrBreak[iBreakType];
								iBreakTypeFirst=iBreakType;
							}
						}
						if (iBreakAt>=0) {
							
							string sPart0=RString.SafeSubstring(sVal,0,iBreakAt);
							sVal=RString.SafeSubstring(sVal,iBreakAt+sarrTabSeparatedSeparationsStartWith[iBreakTypeFirst].Length);
							RString.RemoveEndsWhiteSpace(ref sPart0);
							RString.RemoveEndsWhiteSpace(ref sVal);
							alNow.Add(sPart0);
						}
					}
					else {//else only one left
						alNow.Add(sVal);//get leftover chunk that does not contain the tabs nor multiple spaces
						break;
					}
				}//end while
				if ( alNow!=null && alNow.Count>0 ) {//is an inline table
					sarrReturn=new string[alNow.Count];
					for (int iChunk=0; iChunk<alNow.Count; iChunk++) {
						sarrReturn[iChunk]=(string)alNow[iChunk];
					}
				}
			}
			//else got a null string so leave return as null array
			return sarrReturn;
		}//end SplitValuesDelimitedByTabOrMultipleSpaces
		public string TitlesToCSVLine(bool bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty, bool bExtendedTitleColumns, int ColumnStart, int ColumnCount) {
			string sReturn="";
			RecheckIntegrity();
			if (this.teTitles!=null) {
				if (bExtendedTitleColumns) {
					string sField="";
					int ColAbs=ColumnStart;
					for (int ColRel=0; ColRel<ColumnCount; ColRel++) {
						sField="";
						if (bExtendedTitleColumns) {
							sField=RString.SafeString(GetForcedType(ColAbs),false);
							if (sField!="") sField+=" ";
						}//end if bExtendedTitleColumns
						string FieldDataNow=teTitles.Field(ColAbs);
						if (FieldDataNow==null) {
							RReporting.ShowErr("Can't access field","generating csv line","TitlesToCSVLine {NewLineInField:"+(bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty?"TAB":"BR with marker")+"; Row:title; Column:"+ColAbs+"}");
						}
						sField+=RString.SafeString(FieldDataNow,false);
						if (bExtendedTitleColumns) {
							string sMeta=GetForcedMeta(ColAbs);
							if (sMeta!=null) {
								sMeta=RString.SafeString(sMeta,false);
								sMeta=RString.RemoveEndsWhiteSpace(sMeta);
								if (!sMeta.StartsWith("{")) {
									sMeta=RString.Replace(sMeta,"{","");
									sMeta="{"+sMeta;
								}
								if (!sMeta.EndsWith("}")) {
									sMeta=RString.Replace(sMeta,"}","");
									sMeta+="}";
								}
								sField+=sMeta;
							}//end if metadata
						}//end if bExtendedTitleColumns
						sReturn+=((ColRel!=0)?",":"")+RTable.LiteralFieldToCSVField(sField,this.cFieldDelimiter,this.cTextDelimiter,bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty);
						ColAbs++;
					}//end for field (column header)
				}
				else sReturn=teTitles.ToCSVLine(cFieldDelimiter, cTextDelimiter, bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty,ColumnStart,ColumnCount);
			}
			else RReporting.ShowErr("Cannot read nonexistant title row.","Converting table titles to text line","TitlesToCSVLine");
			return sReturn;
		}
		}//end Save
		public bool SaveChunk(string sFile, bool bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty, bool bExtendedTitleColumns, int RowStart, int RowCount, int ColStart, int ColCount) {
			StreamWriter fsDest=null;
			//sLastFile=sFile; do NOT set, since only saving a chunk and therefore says nothing about the source
			//if (sLastFile==null) sLastFile="";
			bool bGood=false;
			//int iLines=0;
			RecheckIntegrity();
			try {
				fsDest=new StreamWriter(sFile);
				if (TitleRowIsNonBlank) {
					//fsDest.WriteLine(teTitles.ToCSVLine(cFieldDelimiter,cTextDelimiter,bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty));
					string sTitles="";
					string sField="";
					int ColAbs=ColStart;
					for (int ColRel=0; ColRel<ColCount; ColRel++) {
						sField="";
						if (bExtendedTitleColumns) {
							sField=RString.SafeString(GetForcedType(ColAbs),false);
							if (sField!="") sField+=" ";
						}//end if bExtendedTitleColumns
						string FieldDataNow=teTitles.Field(ColAbs);
						if (FieldDataNow==null) {
							RReporting.ShowErr("Can't access field","saving csv file","Save("+RReporting.StringMessage(sFile,true)+","+(bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty?"TAB as newline mode":"BR with marker as newline mode")+"){Row:title; Column:"+ColAbs+"}");
						}
						sField+=RString.SafeString(FieldDataNow,false);
						if (bExtendedTitleColumns) {
							string sMeta=GetForcedMeta(ColAbs);
							if (sMeta!=null) {
								sMeta=RString.SafeString(sMeta,false);
								sMeta=RString.RemoveEndsWhiteSpace(sMeta);
								if (!sMeta.StartsWith("{")) {
									sMeta=RString.Replace(sMeta,"{","");
									sMeta="{"+sMeta;
								}
								if (!sMeta.EndsWith("}")) {
									sMeta=RString.Replace(sMeta,"}","");
									sMeta+="}";
								}
								sField+=sMeta;
							}//end if metadata
						}//end if bExtendedTitleColumns
						sTitles+=((ColRel!=0)?",":"")+RTable.LiteralFieldToCSVField(sField,this.cFieldDelimiter,this.cTextDelimiter,bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty);
						ColAbs++;
					}//end for field (column header)
					fsDest.WriteLine(sTitles);
				}//end if Title Row is not blank
				Console.Error.Write("Writing row range "+RowStart+" to "+(RowStart+RowCount-1)+" of "+iRows+" (total "+0+" to "+(iRows-1)+")...");
				int RowAbs=RowStart;
				for (int RowRel=0; RowRel<RowCount&&RowAbs<iRows; RowRel++) {
					fsDest.WriteLine(RowToCSVLine(RowAbs,bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty,ColStart,ColCount));
					RowAbs++;
				}
				fsDest.Close();
				Console.Error.WriteLine("done.");
				bGood=true;
			}
			catch (Exception exn) {
				Console.Error.WriteLine("Could not save table to \""+sFile+"\":");
				Console.Error.WriteLine(exn.ToString());
				bGood=false;
				try { fsDest.Close(); }
				catch (Exception exn2) {
					RReporting.ShowExn(exn2,"closing file after exception","rtable Load");
				}
			}
			return bGood;
		}//end SaveChunk
		}//end Delete_AllWithMarker
		public bool Save(string sFile, bool bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty, bool Set_bSaveMetaDataInTitleRow) {
			if (sLastFile==sFileUnknown) {
				sLastFile=sFile;
				if (sLastFile==null) sLastFile="";
			}
			bSaveMetaDataInTitleRow=Set_bSaveMetaDataInTitleRow;
			StreamWriter fsDest=null;
			bool bGood=false;
			//int iLines=0;
			Delete_AllWithMarker();
			RecheckIntegrity();
			try {
				fsDest=new StreamWriter(sFile);
				if (TitleRowIsNonBlank) {
					//fsDest.WriteLine(teTitles.ToCSVLine(cFieldDelimiter,cTextDelimiter,bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty));
					string sTitles="";//cumulative
					string sField="";//cumulative
					for (int iCol=0; iCol<teTitles.Columns; iCol++) {
						sField="";//sField=RString.SafeString(GetForcedType(iCol),false);//old way
						if (bSaveMetaDataInTitleRow) sField=RString.SafeString(GetForcedType(iCol),false);
						if (sField!="") sField+=" ";
						string FieldDataNow=teTitles.Field(iCol);
						if (FieldDataNow==null) {
							RReporting.ShowErr("Can't access field","saving csv file","Save("+RReporting.StringMessage(sFile,true)+","+(bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty?"TAB as newline mode":"BR with marker as newline mode")+"){Row:title; Column:"+iCol+"}");
						}
						sField+=RString.SafeString(FieldDataNow,false);
						if (bSaveMetaDataInTitleRow) {
						string sMeta=GetForcedMeta(iCol);
							if (sMeta!=null) {
								sMeta=RString.SafeString(sMeta,false);
								sMeta=RString.RemoveEndsWhiteSpace(sMeta);
								if (!sMeta.StartsWith("{")) {
									sMeta=RString.Replace(sMeta,"{","");
									sMeta="{"+sMeta;
								}
								if (!sMeta.EndsWith("}")) {
									sMeta=RString.Replace(sMeta,"}","");
									sMeta+="}";
								}
								sField+=sMeta;
							}//end if metadata
						}//end if bSaveMetaDataInTitleRow
						sTitles+=((iCol!=0)?",":"")+RTable.LiteralFieldToCSVField(sField,this.cFieldDelimiter,this.cTextDelimiter,bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty);
					}//end for column title iCol
					fsDest.WriteLine(sTitles);
				}
				Console.Error.Write("Writing "+iRows+" rows...");
				for (int iNow=0; iNow<iRows; iNow++) {
					fsDest.WriteLine(RowToCSVLine(iNow,bReplaceNewLineWithTabInsteadOfHTMLBrWithMarkerProperty));
				}
				fsDest.Close();
				Console.Error.WriteLine("done.");
				bGood=true;
			}
			catch (Exception exn) {
				Console.Error.WriteLine("Could not save table to \""+sFile+"\":");
				Console.Error.WriteLine(exn.ToString());
				bGood=false;
				try { fsDest.Close(); }
				catch (Exception exn2) {
					RReporting.ShowExn(exn2,"closing file after exception","rtable Load");
				}
			}
			return bGood;
		}//end Save