00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include <iomanip>
00020 #include <climits>
00021 #include <cstdlib>
00022
00023 #include "ncl/nxsdistancesblock.h"
00024
00025 #include "ncl/nxsreader.h"
00026 using namespace std;
00027
00028
00029 void NxsDistancesBlock::WriteFormatCommand(std::ostream &out) const
00030 {
00031 out << " FORMAT Missing = " << missing << " Triangle = Lower Diagonal;\n";
00032 }
00033
00034 void NxsDistancesBlock::WriteMatrixCommand(std::ostream &out) const
00035 {
00036 if (taxa == NULL)
00037 return;
00038 unsigned width = taxa->GetMaxTaxonLabelLength();
00039 const unsigned ntaxTotal = taxa->GetNTax();
00040 out << "MATRIX";
00041 int prec = out.precision(10);
00042 for (unsigned i = 0; i < ntaxTotal; i++)
00043 {
00044 const std::string currTaxonLabel = NxsString::GetEscaped(taxa->GetTaxonLabel(i));
00045 out << '\n' << currTaxonLabel;
00046 unsigned currTaxonLabelLen = (unsigned)currTaxonLabel.size();
00047 unsigned diff = width - currTaxonLabelLen;
00048 for (unsigned k = 0; k < diff+5; k++)
00049 out << ' ';
00050 for (unsigned j = 0; j< i; j++)
00051 {
00052 if (IsMissing(i,j))
00053 out << ' ' << missing << " ";
00054 else
00055 out << ' '<< GetDistance(i, j);
00056 }
00057 out << " 0.0";
00058 }
00059 out << ";\n";
00060 out.precision(prec);
00061 }
00062 void NxsDistancesBlock::WriteAsNexus(std::ostream &out) const
00063 {
00064 out << "BEGIN DISTANCES;\n";
00065 WriteBasicBlockCommands(out);
00066 if (nchar > 0)
00067 out << " DIMENSIONS NChar = " << nchar << ";\n";
00068 WriteFormatCommand(out);
00069 WriteMatrixCommand(out);
00070 WriteSkippedCommands(out);
00071 out << "END;\n";
00072 }
00073
00074
00078 NxsDistancesBlock::NxsDistancesBlock(
00079 NxsTaxaBlockAPI *t)
00080 : NxsBlock(),
00081 NxsTaxaBlockSurrogate(t, NULL)
00082 {
00083 id = "DISTANCES";
00084 Reset();
00085 }
00086
00090 NxsDistancesBlock::~NxsDistancesBlock()
00091 {
00092 Reset();
00093 }
00094
00099 void NxsDistancesBlock::HandleDimensionsCommand(
00100 NxsToken &token)
00101 {
00102 nchar = 0;
00103 unsigned ntaxRead = 0;
00104 for (;;)
00105 {
00106 token.GetNextToken();
00107 if (token.Equals("NEWTAXA"))
00108 newtaxa = true;
00109 else if (token.Equals("NTAX"))
00110 {
00111 DemandEquals(token, "after NTAX in DIMENSIONS command");
00112 ntaxRead = DemandPositiveInt(token, "NTAX");
00113 }
00114 else if (token.Equals("NCHAR"))
00115 {
00116 DemandEquals(token, "in DIMENSIONS command");
00117 nchar = DemandPositiveInt(token, "NCHAR");
00118 }
00119 else if (token.Equals(";"))
00120 break;
00121 }
00122 if (newtaxa)
00123 {
00124 if (ntaxRead == 0)
00125 {
00126 errormsg = "DIMENSIONS command must have an NTAX subcommand when the NEWTAXA option is in effect.";
00127 throw NxsException(errormsg, token);
00128 }
00129 expectedNtax = ntaxRead;
00130 AssureTaxaBlock(createImpliedBlock, token, "Dimensions");
00131 if (!createImpliedBlock)
00132 {
00133 taxa->Reset();
00134 if (nexusReader)
00135 nexusReader->RemoveBlockFromUsedBlockList(taxa);
00136 }
00137 taxa->SetNtax(expectedNtax);
00138 }
00139 else
00140 {
00141 AssureTaxaBlock(false, token, "Dimensions");
00142 const unsigned ntaxinblock = taxa->GetNumTaxonLabels();
00143 if (ntaxinblock == 0)
00144 {
00145 errormsg = "A TAXA block must be read before character data, or the DIMENSIONS command must use the NEWTAXA.";
00146 throw NxsException(errormsg, token);
00147 }
00148 if (ntaxinblock < ntaxRead)
00149 {
00150 errormsg = "NTAX in ";
00151 errormsg << id << " block must be less than or equal to NTAX in TAXA block\nNote: one circumstance that can cause this error is \nforgetting to specify NTAX in DIMENSIONS command when \na TAXA block has not been provided";
00152 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00153 }
00154 expectedNtax = (ntaxRead == 0 ? ntaxinblock : ntaxRead);;
00155 }
00156 }
00157
00162 void NxsDistancesBlock::HandleFormatCommand(
00163 NxsToken &token)
00164 {
00165 for (;;)
00166 {
00167 token.GetNextToken();
00168 if (token.Equals(";"))
00169 break;
00170 if (token.Equals("TRIANGLE"))
00171 {
00172 DemandEquals(token, "after TRIANGLE");
00173 token.GetNextToken();
00174 if (token.Equals("LOWER"))
00175 triangle = NxsDistancesBlockEnum(lower);
00176 else if (token.Equals("UPPER"))
00177 triangle = NxsDistancesBlockEnum(upper);
00178 else if (token.Equals("BOTH"))
00179 triangle = NxsDistancesBlockEnum(both);
00180 else
00181 {
00182 errormsg = "Expecting UPPER, LOWER, or BOTH but found ";
00183 errormsg += token.GetToken();
00184 errormsg += " instead";
00185 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00186 }
00187 }
00188 else if (token.Equals("DIAGONAL"))
00189 diagonal = true;
00190 else if (token.Equals("NODIAGONAL"))
00191 diagonal = false;
00192 else if (token.Equals("LABELS"))
00193 labels = true;
00194 else if (token.Equals("NOLABELS"))
00195 labels = false;
00196 else if (token.Equals("INTERLEAVE"))
00197 interleave = true;
00198 else if (token.Equals("NOINTERLEAVE"))
00199 interleave = false;
00200 else if (token.Equals("MISSING"))
00201 {
00202 DemandEquals(token, "after MISSING");
00203 token.GetNextToken();
00204 if (token.GetTokenLength() != 1 || isdigit(token.GetTokenReference()[0]))
00205 {
00206 errormsg = "Missing data symbol specified (";
00207 errormsg += token.GetToken();
00208 errormsg += ") is invalid (must be a single character)";
00209 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00210 }
00211 missing = token.GetTokenReference()[0];
00212 }
00213 else
00214 {
00215 errormsg = "Token specified (";
00216 errormsg += token.GetToken();
00217 errormsg += ") is an invalid subcommand for the FORMAT command";
00218 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00219 }
00220 }
00221 }
00222
00228 bool NxsDistancesBlock::HandleNextPass(
00229 NxsToken &token,
00230 unsigned &offset,
00231 vector<unsigned> & fileMatrixCmdOrderToTaxInd,
00232 set<unsigned> & taxIndsRead)
00233 {
00234 unsigned jmax = 0;
00235 bool done = false;
00236
00237 unsigned i_first = 0;
00238 if (triangle == NxsDistancesBlockEnum(lower))
00239 i_first = offset;
00240 unsigned i_last = expectedNtax;
00241 errormsg.clear();
00242 for (unsigned i = i_first; i < i_last; i++)
00243 {
00244
00245
00246
00247
00248
00249
00250
00251 if (labels && (!newtaxa || offset > 0))
00252 {
00253
00254
00255 do
00256 {
00257 token.SetLabileFlagBit(NxsToken::newlineIsToken);
00258 token.GetNextToken();
00259 }
00260 while(token.AtEOL());
00261
00262 try
00263 {
00264
00265
00266 unsigned k = taxa->FindTaxon(token.GetToken());
00267 if (k != i && triangle != NxsDistancesBlockEnum(lower))
00268 {
00269 errormsg << "Taxon " << token.GetToken() << " was not expected in the DISTANCES matrix.\nTaxa should be in the same order as in the Taxon block";
00270 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00271 }
00272
00273
00274
00275 if (fileMatrixCmdOrderToTaxInd[i] == UINT_MAX)
00276 {
00277 fileMatrixCmdOrderToTaxInd[i] = k;
00278 if (taxIndsRead.count(k) > 0)
00279 {
00280 errormsg << "Taxon " << token.GetToken() << " was encountered more than one time in the Distances Matrix.";
00281 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00282 }
00283 taxIndsRead.insert(k);
00284 }
00285 else if (fileMatrixCmdOrderToTaxInd[i] != k)
00286 {
00287 errormsg << "Taxon labeled " << token.GetToken() << " is out of order compared to previous interleave pages";
00288 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00289 }
00290 }
00291 catch (NxsTaxaBlock::NxsX_NoSuchTaxon)
00292 {
00293 errormsg = "Could not find ";
00294 errormsg += token.GetToken();
00295 errormsg += " among taxa previously defined";
00296 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00297 }
00298 }
00299
00300 else if (labels && newtaxa)
00301 {
00302
00303
00304 do
00305 {
00306 token.SetLabileFlagBit(NxsToken::newlineIsToken);
00307 token.GetNextToken();
00308 }
00309 while(token.AtEOL());
00310 const NxsString t(token.GetToken().c_str());
00311 taxa->AddTaxonLabel(t);
00312 fileMatrixCmdOrderToTaxInd[i] = i;
00313 taxIndsRead.insert(i);
00314 }
00315
00316
00317
00318 unsigned true_j = 0;
00319 for (unsigned j = 0; j < expectedNtax; j++)
00320 {
00321 if (i == expectedNtax - 1)
00322 {
00323 if (j == expectedNtax - 1)
00324 done = true;
00325 if (true_j == expectedNtax - 1 || (!diagonal && triangle == NxsDistancesBlockEnum(upper)))
00326 {
00327 done = true;
00328 break;
00329 }
00330 }
00331 if (!diagonal && triangle == NxsDistancesBlockEnum(lower) && j == expectedNtax - offset - 1)
00332 {
00333 done = true;
00334 break;
00335 }
00336
00337 token.SetLabileFlagBit(NxsToken::newlineIsToken);
00338 token.GetNextToken();
00339
00340 if (token.AtEOL())
00341 {
00342 if (j > jmax)
00343 {
00344 jmax = j;
00345 if (!diagonal && triangle == NxsDistancesBlockEnum(upper) && i >= offset)
00346 jmax++;
00347 if (interleave && triangle == NxsDistancesBlockEnum(upper))
00348 i_last = jmax + offset;
00349 }
00350 break;
00351 }
00352
00353 true_j = j + offset;
00354 if (triangle == NxsDistancesBlockEnum(upper) && i > offset)
00355 true_j += (i - offset);
00356 if (!diagonal && triangle == NxsDistancesBlockEnum(upper) && i >= offset)
00357 true_j++;
00358
00359 if (true_j == expectedNtax)
00360 {
00361 errormsg = "Too many distances specified in row just read in";
00362 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00363 }
00364
00365 string t = token.GetToken();
00366 unsigned corrected_i = fileMatrixCmdOrderToTaxInd.at(i);
00367 unsigned corrected_j = true_j;
00368 if (triangle == NxsDistancesBlockEnum(lower))
00369 corrected_j = fileMatrixCmdOrderToTaxInd.at(true_j);
00370 if (corrected_i == UINT_MAX || corrected_j == UINT_MAX)
00371 {
00372 errormsg = "Illegal internal row number for taxon in Distance Matrix.";
00373 throw NxsNCLAPIException(errormsg, token);
00374 }
00375 if (token.GetTokenLength() == 1 && t[0] == missing)
00376 SetMissing(corrected_i, corrected_j);
00377 else
00378 SetDistance(corrected_i, corrected_j, atof(t.c_str()));
00379 }
00380 }
00381 offset += jmax;
00382 return done;
00383 }
00384
00385 void NxsDistancesBlock::CopyDistancesContents(const NxsDistancesBlock &other)
00386 {
00387 expectedNtax = other.expectedNtax;
00388 nchar = other.nchar;
00389 diagonal = other.diagonal;
00390 interleave = other.interleave;
00391 labels = other.labels;
00392 triangle = other.triangle;
00393 missing = other.missing;
00394 matrix = other.matrix;
00395 }
00396
00401 void NxsDistancesBlock::HandleMatrixCommand(
00402 NxsToken &token)
00403 {
00404 errormsg.clear();
00405 if (expectedNtax == 0 || taxa == NULL)
00406 {
00407 AssureTaxaBlock(false, token, "Matrix");
00408 expectedNtax = taxa->GetNumTaxonLabels();
00409 }
00410 if (expectedNtax == 0)
00411 {
00412 errormsg = "MATRIX command cannot be read if NTAX is zero";
00413 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00414 }
00415
00416 if (triangle == NxsDistancesBlockEnum(both) && !diagonal)
00417 {
00418 errormsg = "Cannot specify NODIAGONAL and TRIANGLE=BOTH at the same time";
00419 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00420 }
00421 if (newtaxa)
00422 taxa->Reset();
00423
00424 vector<unsigned> fileMatrixCmdOrderToTaxInd(expectedNtax, UINT_MAX);
00425 set<unsigned> taxIndsRead;
00426 unsigned nTaxInTaxBlock = taxa->GetNumTaxonLabels();
00427 if (nTaxInTaxBlock < expectedNtax)
00428 {
00429 errormsg << "NTAX in " << id << " block must be less than or equal to NTAX in TAXA block\nNote: one circumstance that can cause this error is \nforgetting to specify NTAX in DIMENSIONS command when \na TAXA block has not been provided";
00430 throw NxsException(errormsg, token.GetFilePosition(), token.GetFileLine(), token.GetFileColumn());
00431 }
00432 NxsDistanceDatumRow row(nTaxInTaxBlock);
00433 matrix.assign(nTaxInTaxBlock, row);
00434 unsigned offset = 0;
00435 for (;;)
00436 {
00437 if (HandleNextPass(token, offset, fileMatrixCmdOrderToTaxInd, taxIndsRead))
00438 break;
00439 }
00440 DemandEndSemicolon(token, "MATRIX");
00441 }
00442
00448 void NxsDistancesBlock::Read(
00449 NxsToken &token)
00450 {
00451 isEmpty = false;
00452
00453 DemandEndSemicolon(token, "BEGIN DISTANCES");
00454
00455 for (;;)
00456 {
00457 token.GetNextToken();
00458 NxsBlock::NxsCommandResult res = HandleBasicBlockCommands(token);
00459 if (res == NxsBlock::NxsCommandResult(STOP_PARSING_BLOCK))
00460 return;
00461 if (res != NxsBlock::NxsCommandResult(HANDLED_COMMAND))
00462 {
00463 if (token.Equals("DIMENSIONS"))
00464 HandleDimensionsCommand(token);
00465 else if (token.Equals("FORMAT"))
00466 HandleFormatCommand(token);
00467 else if (token.Equals("TAXLABELS"))
00468 HandleTaxLabels(token);
00469 else if (token.Equals("MATRIX"))
00470 HandleMatrixCommand(token);
00471 else
00472 SkipCommand(token);
00473 }
00474 }
00475 }
00476
00481 void NxsDistancesBlock::Report(
00482 std::ostream &out) NCL_COULD_BE_CONST
00483 {
00484 const unsigned ntaxTotal = taxa->GetNumTaxonLabels();
00485
00486 out << endl;
00487 out << id << " block contains ";
00488 if (ntaxTotal == 0)
00489 {
00490 out << "no taxa" << endl;
00491 }
00492 else if (ntaxTotal == 1)
00493 out << "one taxon" << endl;
00494 else
00495 out << ntaxTotal << " taxa" << endl;
00496
00497 if (IsLowerTriangular())
00498 out << " Matrix is lower-triangular" << endl;
00499 else if (IsUpperTriangular())
00500 out << " Matrix is upper-triangular" << endl;
00501 else
00502 out << " Matrix is rectangular" << endl;
00503
00504 if (IsInterleave())
00505 out << " Matrix is interleaved" << endl;
00506 else
00507 out << " Matrix is non-interleaved" << endl;
00508
00509 if (IsLabels())
00510 out << " Taxon labels provided" << endl;
00511 else
00512 out << " No taxon labels provided" << endl;
00513
00514 if (IsDiagonal())
00515 out << " Diagonal elements specified" << endl;
00516 else
00517 out << " Diagonal elements not specified" << endl;
00518
00519 out << " Missing data symbol is " << missing << endl;
00520
00521 if (expectedNtax == 0)
00522 return;
00523
00524 out.setf(ios::fixed, ios::floatfield);
00525 out.setf(ios::showpoint);
00526 for (unsigned i = 0; i < ntaxTotal; i++)
00527 {
00528 if (labels)
00529 out << setw(20) << taxa->GetTaxonLabel(i);
00530 else
00531 out << " ";
00532
00533 for (unsigned j = 0; j < ntaxTotal; j++)
00534 {
00535 if (triangle == NxsDistancesBlockEnum(upper) && j < i)
00536 out << setw(12) << " ";
00537 else if (triangle == NxsDistancesBlockEnum(lower) && j > i)
00538 continue;
00539 else if (!diagonal && i == j)
00540 {
00541 out << setw(12) << " ";
00542 }
00543 else if (IsMissing(i, j))
00544 out << setw(12) << missing;
00545 else
00546 out << setw(12) << GetDistance(i, j);
00547 }
00548
00549 out << endl;
00550 }
00551 }
00552
00562 void NxsDistancesBlock::Reset()
00563 {
00564 NxsBlock::Reset();
00565 ResetSurrogate();
00566 matrix.clear();
00567 expectedNtax = 0;
00568 nchar = 0;
00569 diagonal = true;
00570 labels = true;
00571 interleave = false;
00572 missing = '?';
00573 triangle = NxsDistancesBlockEnum(lower);
00574 }
00575
00579 unsigned NxsDistancesBlock::GetNchar() NCL_COULD_BE_CONST
00580 {
00581 return nchar;
00582 }
00583
00588 double NxsDistancesBlock::GetDistance(
00589 unsigned i,
00590 unsigned j) const
00591 {
00592 return GetCell(i,j).value;
00593 }
00594
00598 char NxsDistancesBlock::GetMissingSymbol() NCL_COULD_BE_CONST
00599 {
00600 return missing;
00601 }
00602
00606 unsigned NxsDistancesBlock::GetTriangle() NCL_COULD_BE_CONST
00607 {
00608 return triangle;
00609 }
00610
00614 bool NxsDistancesBlock::IsRectangular() NCL_COULD_BE_CONST
00615 {
00616 return (triangle == NxsDistancesBlockEnum(both) ? true : false);
00617 }
00618
00622 bool NxsDistancesBlock::IsUpperTriangular() NCL_COULD_BE_CONST
00623 {
00624 return (triangle == NxsDistancesBlockEnum(upper) ? true : false);
00625 }
00626
00630 bool NxsDistancesBlock::IsLowerTriangular() NCL_COULD_BE_CONST
00631 {
00632 return (triangle == NxsDistancesBlockEnum(lower) ? true : false);
00633 }
00634
00638 bool NxsDistancesBlock::IsDiagonal() NCL_COULD_BE_CONST
00639 {
00640 return diagonal;
00641 }
00642
00646 bool NxsDistancesBlock::IsInterleave() NCL_COULD_BE_CONST
00647 {
00648 return interleave;
00649 }
00650
00654 bool NxsDistancesBlock::IsLabels() NCL_COULD_BE_CONST
00655 {
00656 return labels;
00657 }
00658
00663 bool NxsDistancesBlock::IsMissing(
00664 unsigned i,
00665 unsigned j) const
00666 {
00667 return (bool)(GetCell(i,j).missing);
00668 }
00669
00674 void NxsDistancesBlock::SetDistance(
00675 unsigned i,
00676 unsigned j,
00677 double d)
00678 {
00679 NxsDistanceDatum & c = GetCell(i, j);
00680 c.value = d;
00681 c.missing = false;
00682 }
00683
00688 void NxsDistancesBlock::SetMissing(
00689 unsigned i,
00690 unsigned j)
00691 {
00692 NxsDistanceDatum & c = GetCell(i, j);
00693 c.missing = 1;
00694 c.value = 0.0;
00695 }
00696
00700 void NxsDistancesBlock::SetNchar(
00701 unsigned n)
00702 {
00703 nchar = n;
00704 }
00705
00706 NxsDistancesBlock *NxsDistancesBlockFactory::GetBlockReaderForID(const std::string & idneeded, NxsReader *reader, NxsToken *)
00707 {
00708 if (reader == NULL || idneeded != "DISTANCES")
00709 return NULL;
00710 NxsDistancesBlock * nb = new NxsDistancesBlock(NULL);
00711 nb->SetCreateImpliedBlock(true);
00712 nb->SetImplementsLinkAPI(true);
00713 return nb;
00714 }