00001
00002 #include <string>
00003 #include <vector>
00004 #include <iostream>
00005 #include <algorithm>
00006 #include <fstream>
00007 #include <cmath>
00008 using namespace std;
00009
00010 #include "RecoLocalCalo/HcalRecAlgos/interface/HBHEPulseShapeFlag.h"
00011 #include "RecoLocalCalo/HcalRecAlgos/interface/HcalCaloFlagLabels.h"
00012
00013 #include "FWCore/MessageLogger/interface/MessageLogger.h"
00014
00015 #include "DataFormats/HcalDigi/interface/HBHEDataFrame.h"
00016 #include "DataFormats/HcalRecHit/interface/HcalRecHitCollections.h"
00017 #include "DataFormats/HcalRecHit/interface/HBHERecHit.h"
00018
00019 #include "CalibFormats/HcalObjects/interface/HcalCalibrations.h"
00020 #include "CalibFormats/HcalObjects/interface/HcalCoderDb.h"
00021 #include "CalibCalorimetry/HcalAlgos/interface/HcalPulseShapes.h"
00022
00023 HBHEPulseShapeFlagSetter::HBHEPulseShapeFlagSetter()
00024 {
00025
00026
00027
00028
00029
00030
00031
00032
00033 mMinimumChargeThreshold = 99999999;
00034 }
00035
00036 HBHEPulseShapeFlagSetter::HBHEPulseShapeFlagSetter(double MinimumChargeThreshold,
00037 unsigned int TrianglePeakTS,
00038 vector<double> LinearThreshold,
00039 vector<double> LinearCut,
00040 vector<double> RMS8MaxThreshold,
00041 vector<double> RMS8MaxCut,
00042 vector<double> LeftSlopeThreshold,
00043 vector<double> LeftSlopeCut,
00044 vector<double> RightSlopeThreshold,
00045 vector<double> RightSlopeCut,
00046 vector<double> RightSlopeSmallThreshold,
00047 vector<double> RightSlopeSmallCut,
00048 bool UseDualFit,
00049 bool TriangleIgnoreSlow)
00050 {
00051
00052
00053
00054
00055
00056
00057
00058 mMinimumChargeThreshold = MinimumChargeThreshold;
00059 mTrianglePeakTS = TrianglePeakTS;
00060 mTriangleIgnoreSlow = TriangleIgnoreSlow;
00061
00062 for(int i = 0; i < (int)LinearThreshold.size() && i < (int)LinearCut.size(); i++)
00063 mLambdaLinearCut.push_back(pair<double, double>(LinearThreshold[i], LinearCut[i]));
00064 sort(mLambdaLinearCut.begin(), mLambdaLinearCut.end());
00065
00066 for(int i = 0; i < (int)RMS8MaxThreshold.size() && i < (int)RMS8MaxCut.size(); i++)
00067 mLambdaRMS8MaxCut.push_back(pair<double, double>(RMS8MaxThreshold[i], RMS8MaxCut[i]));
00068 sort(mLambdaRMS8MaxCut.begin(), mLambdaRMS8MaxCut.end());
00069
00070 for(int i = 0; i < (int)LeftSlopeThreshold.size() && i < (int)LeftSlopeCut.size(); i++)
00071 mLeftSlopeCut.push_back(pair<double, double>(LeftSlopeThreshold[i], LeftSlopeCut[i]));
00072 sort(mLeftSlopeCut.begin(), mLeftSlopeCut.end());
00073
00074 for(int i = 0; i < (int)RightSlopeThreshold.size() && i < (int)RightSlopeCut.size(); i++)
00075 mRightSlopeCut.push_back(pair<double, double>(RightSlopeThreshold[i], RightSlopeCut[i]));
00076 sort(mRightSlopeCut.begin(), mRightSlopeCut.end());
00077
00078 for(int i = 0; i < (int)RightSlopeSmallThreshold.size() && i < (int)RightSlopeSmallCut.size(); i++)
00079 mRightSlopeSmallCut.push_back(pair<double, double>(RightSlopeSmallThreshold[i], RightSlopeSmallCut[i]));
00080 sort(mRightSlopeSmallCut.begin(), mRightSlopeSmallCut.end());
00081
00082 mUseDualFit = UseDualFit;
00083
00084 Initialize();
00085 }
00086
00087 HBHEPulseShapeFlagSetter::~HBHEPulseShapeFlagSetter()
00088 {
00089
00090 }
00091
00092 void HBHEPulseShapeFlagSetter::Clear()
00093 {
00094
00095 }
00096
00097 void HBHEPulseShapeFlagSetter::SetPulseShapeFlags(HBHERecHit &hbhe,
00098 const HBHEDataFrame &digi,
00099 const HcalCoder &coder,
00100 const HcalCalibrations &calib)
00101 {
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111 CaloSamples Tool;
00112 coder.adc2fC(digi, Tool);
00113
00114 mCharge.clear();
00115 mCharge.resize(digi.size());
00116
00117 double TotalCharge = 0;
00118
00119 for(int i = 0; i < (int)digi.size(); ++i)
00120 {
00121 mCharge[i] = Tool[i] - calib.pedestal(digi.sample(i).capid());
00122 TotalCharge += mCharge[i];
00123 }
00124
00125
00126 if(TotalCharge < mMinimumChargeThreshold)
00127 return;
00128
00129 double NominalChi2 = 0;
00130 if (mUseDualFit == true)
00131 NominalChi2=PerformDualNominalFit(mCharge);
00132 else
00133 NominalChi2=PerformNominalFit(mCharge);
00134
00135 double LinearChi2 = PerformLinearFit(mCharge);
00136
00137 double RMS8Max = CalculateRMS8Max(mCharge);
00138 TriangleFitResult TriangleResult = PerformTriangleFit(mCharge);
00139
00140
00141 if(CheckPassFilter(TotalCharge, log(LinearChi2) - log(NominalChi2), mLambdaLinearCut, -1) == false)
00142 hbhe.setFlagField(1, HcalCaloFlagLabels::HBHEFlatNoise);
00143 if(CheckPassFilter(TotalCharge, log(RMS8Max) * 2 - log(NominalChi2), mLambdaRMS8MaxCut, -1) == false)
00144 hbhe.setFlagField(1, HcalCaloFlagLabels::HBHESpikeNoise);
00145
00146
00147 if ((int)mCharge.size() >= mTrianglePeakTS)
00148 {
00149
00150 double TS4Left = 1000;
00151 double TS4Right = 1000;
00152
00153
00154 if (TriangleResult.LeftSlope>1e-5)
00155 TS4Left = mCharge[mTrianglePeakTS] / TriangleResult.LeftSlope;
00156 if (TriangleResult.RightSlope<-1e-5)
00157 TS4Right = mCharge[mTrianglePeakTS] / -TriangleResult.RightSlope;
00158
00159 if(TS4Left > 1000 || TS4Left < -1000)
00160 TS4Left = 1000;
00161 if(TS4Right > 1000 || TS4Right < -1000)
00162 TS4Right = 1000;
00163
00164 if(mTriangleIgnoreSlow == false)
00165 {
00166 if(CheckPassFilter(mCharge[mTrianglePeakTS], TS4Left, mLeftSlopeCut, 1) == false)
00167 hbhe.setFlagField(1, HcalCaloFlagLabels::HBHETriangleNoise);
00168 else if(CheckPassFilter(mCharge[mTrianglePeakTS], TS4Right, mRightSlopeCut, 1) == false)
00169 hbhe.setFlagField(1, HcalCaloFlagLabels::HBHETriangleNoise);
00170 }
00171
00172
00173 if(CheckPassFilter(mCharge[mTrianglePeakTS], TS4Right, mRightSlopeSmallCut, -1) == false)
00174 hbhe.setFlagField(1, HcalCaloFlagLabels::HBHETriangleNoise);
00175 }
00176 }
00177
00178 void HBHEPulseShapeFlagSetter::Initialize()
00179 {
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194 vector<double> PulseShape;
00195
00196 HcalPulseShapes Shapes;
00197 HcalPulseShapes::Shape HPDShape = Shapes.hbShape();
00198
00199 PulseShape.reserve(350);
00200 for(int i = 0; i < 200; i++)
00201 PulseShape.push_back(HPDShape.at(i));
00202 PulseShape.insert(PulseShape.begin(), 150, 0);
00203
00204 CumulativeIdealPulse.reserve(350);
00205 CumulativeIdealPulse.clear();
00206 CumulativeIdealPulse.push_back(0);
00207 for(unsigned int i = 1; i < PulseShape.size(); i++)
00208 CumulativeIdealPulse.push_back(CumulativeIdealPulse[i-1] + PulseShape[i]);
00209
00210
00211 mCharge.reserve(10);
00212 }
00213
00214 TriangleFitResult HBHEPulseShapeFlagSetter::PerformTriangleFit(const vector<double> &Charge)
00215 {
00216
00217
00218
00219
00220
00221
00222 TriangleFitResult result;
00223 result.Chi2 = 0;
00224 result.LeftSlope = 0;
00225 result.RightSlope = 0;
00226
00227 int DigiSize = Charge.size();
00228
00229
00230 double MinimumRightChi2 = 1000000;
00231 double Numerator = 0;
00232 double Denominator = 0;
00233
00234 for(int iTS = mTrianglePeakTS + 2; iTS <= DigiSize; iTS++)
00235 {
00236
00237 Numerator = 0;
00238 Denominator = 0;
00239
00240 for(int i = mTrianglePeakTS + 1; i < iTS; i++)
00241 {
00242 Numerator += (i - mTrianglePeakTS) * (Charge[i] - Charge[mTrianglePeakTS]);
00243 Denominator += (i - mTrianglePeakTS) * (i - mTrianglePeakTS);
00244 }
00245
00246 double BestSlope = 0;
00247 if (Denominator!=0) BestSlope = Numerator / Denominator;
00248 if(BestSlope > 0)
00249 BestSlope = 0;
00250
00251
00252 if(iTS != DigiSize)
00253 {
00254
00255 if(BestSlope > -1 * Charge[mTrianglePeakTS] / (iTS - mTrianglePeakTS))
00256 BestSlope = -1 * Charge[mTrianglePeakTS] / (iTS - mTrianglePeakTS);
00257 if(BestSlope < -1 * Charge[mTrianglePeakTS] / (iTS - 1 - mTrianglePeakTS))
00258 BestSlope = -1 * Charge[mTrianglePeakTS] / (iTS - 1 - mTrianglePeakTS);
00259 }
00260 else
00261 {
00262
00263 if(BestSlope < -1 * Charge[mTrianglePeakTS] / (iTS - 1 - mTrianglePeakTS))
00264 BestSlope = -1 * Charge[mTrianglePeakTS] / (iTS - 1 - mTrianglePeakTS);
00265 }
00266
00267
00268
00269
00270
00271
00272 double Chi2 = 0;
00273 for(int i = mTrianglePeakTS + 1; i < iTS; i++)
00274 Chi2 += (Charge[mTrianglePeakTS] - Charge[i] + (i - mTrianglePeakTS) * BestSlope)
00275 * (Charge[mTrianglePeakTS] - Charge[i] + (i - mTrianglePeakTS) * BestSlope);
00276 for(int i = iTS; i < DigiSize; i++)
00277 Chi2 += Charge[i] * Charge[i];
00278
00279 if(Chi2 < MinimumRightChi2)
00280 {
00281 MinimumRightChi2 = Chi2;
00282 result.RightSlope = BestSlope;
00283 }
00284 }
00285
00286
00287 double MinimumLeftChi2 = 1000000;
00288
00289 for(int iTS = 0; iTS < (int)mTrianglePeakTS; iTS++)
00290 {
00291
00292 Numerator = 0;
00293 Denominator = 0;
00294 for(int i = iTS; i < (int)mTrianglePeakTS; i++)
00295 {
00296 Numerator = Numerator + (i - mTrianglePeakTS) * (Charge[i] - Charge[mTrianglePeakTS]);
00297 Denominator = Denominator + (i - mTrianglePeakTS) * (i - mTrianglePeakTS);
00298 }
00299
00300 double BestSlope = 0;
00301 if (Denominator!=0) BestSlope = Numerator / Denominator;
00302 if (BestSlope < 0)
00303 BestSlope = 0;
00304
00305
00306 if(iTS != 0)
00307 {
00308
00309 if(BestSlope > Charge[mTrianglePeakTS] / (mTrianglePeakTS - iTS))
00310 BestSlope = Charge[mTrianglePeakTS] / (mTrianglePeakTS - iTS);
00311 if(BestSlope < Charge[mTrianglePeakTS] / (mTrianglePeakTS + 1 - iTS))
00312 BestSlope = Charge[mTrianglePeakTS] / (mTrianglePeakTS + 1 - iTS);
00313 }
00314 else
00315 {
00316 if(BestSlope > Charge[mTrianglePeakTS] / (mTrianglePeakTS - iTS))
00317 BestSlope = Charge[mTrianglePeakTS] / (mTrianglePeakTS - iTS);
00318 }
00319
00320
00321 double Chi2 = 0;
00322 for(int i = 0; i < iTS; i++)
00323 Chi2 += Charge[i] * Charge[i];
00324 for(int i = iTS; i < (int)mTrianglePeakTS; i++)
00325 Chi2 += (Charge[mTrianglePeakTS] - Charge[i] + (i - mTrianglePeakTS) * BestSlope)
00326 * (Charge[mTrianglePeakTS] - Charge[i] + (i - mTrianglePeakTS) * BestSlope);
00327
00328 if(MinimumLeftChi2 > Chi2)
00329 {
00330 MinimumLeftChi2 = Chi2;
00331 result.LeftSlope = BestSlope;
00332 }
00333 }
00334
00335 result.Chi2 = MinimumLeftChi2 + MinimumRightChi2;
00336
00337 return result;
00338 }
00339
00340 double HBHEPulseShapeFlagSetter::PerformNominalFit(const vector<double> &Charge)
00341 {
00342
00343
00344
00345
00346
00347
00348
00349 int DigiSize = Charge.size();
00350
00351 double MinimumChi2 = 100000;
00352
00353 double F = 0;
00354
00355 double SumF2 = 0;
00356 double SumTF = 0;
00357 double SumT2 = 0;
00358
00359 for(int i = 0; i + 250 < (int)CumulativeIdealPulse.size(); i++)
00360 {
00361 if(CumulativeIdealPulse[i+250] - CumulativeIdealPulse[i] < 1e-5)
00362 continue;
00363
00364 SumF2 = 0;
00365 SumTF = 0;
00366 SumT2 = 0;
00367
00368 double ErrorTemp=0;
00369 for(int j = 0; j < DigiSize; j++)
00370 {
00371
00372 F = CumulativeIdealPulse[i+j*25+25] - CumulativeIdealPulse[i+j*25];
00373
00374 ErrorTemp=Charge[j];
00375 if (ErrorTemp<1)
00376 ErrorTemp=1;
00377
00378 SumF2 += F * F / ErrorTemp;
00379 SumTF += F * Charge[j] / ErrorTemp;
00380 SumT2 += fabs(Charge[j]);
00381 }
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394 double Chi2 = SumT2 - SumTF * SumTF / SumF2;
00395
00396 if(Chi2 < MinimumChi2)
00397 MinimumChi2 = Chi2;
00398 }
00399
00400
00401 if(MinimumChi2 < 1e-5)
00402 MinimumChi2 = 1e-5;
00403
00404 return MinimumChi2;
00405 }
00406
00407 double HBHEPulseShapeFlagSetter::PerformDualNominalFit(const vector<double> &Charge)
00408 {
00409
00410
00411
00412
00413
00414
00415
00416
00417 double OverallMinimumChi2 = 1000000;
00418
00419 int AvailableDistance[] = {-100, -75, -50, 50, 75, 100};
00420
00421
00422 for(int k = 0; k < 6; k++)
00423 {
00424 double SingleMinimumChi2 = 1000000;
00425 int MinOffset = 0;
00426
00427
00428 for(int i = 0; i + 250 < (int)CumulativeIdealPulse.size(); i += 10)
00429 {
00430 double Chi2 = DualNominalFitSingleTry(Charge, i, AvailableDistance[k]);
00431
00432 if(Chi2 < SingleMinimumChi2)
00433 {
00434 SingleMinimumChi2 = Chi2;
00435 MinOffset = i;
00436 }
00437 }
00438
00439
00440 for(int i = MinOffset - 15; i + 250 < (int)CumulativeIdealPulse.size() && i < MinOffset + 15; i++)
00441 {
00442 double Chi2 = DualNominalFitSingleTry(Charge, i, AvailableDistance[k]);
00443 if(Chi2 < SingleMinimumChi2)
00444 SingleMinimumChi2 = Chi2;
00445 }
00446
00447
00448 if(SingleMinimumChi2 < OverallMinimumChi2)
00449 OverallMinimumChi2 = SingleMinimumChi2;
00450 }
00451
00452 return OverallMinimumChi2;
00453 }
00454
00455 double HBHEPulseShapeFlagSetter::DualNominalFitSingleTry(const vector<double> &Charge, int Offset, int Distance)
00456 {
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467 int DigiSize = Charge.size();
00468
00469 if(Offset < 0 || Offset + 250 >= (int)CumulativeIdealPulse.size())
00470 return 1000000;
00471 if(CumulativeIdealPulse[Offset+250] - CumulativeIdealPulse[Offset] < 1e-5)
00472 return 1000000;
00473
00474 static vector<double> F1;
00475 static vector<double> F2;
00476
00477 F1.resize(DigiSize);
00478 F2.resize(DigiSize);
00479
00480 double SumF1F1 = 0;
00481 double SumF1F2 = 0;
00482 double SumF2F2 = 0;
00483 double SumTF1 = 0;
00484 double SumTF2 = 0;
00485
00486 double Error = 0;
00487
00488 for(int j = 0; j < DigiSize; j++)
00489 {
00490
00491 F1[j] = CumulativeIdealPulse[Offset+j*25+25] - CumulativeIdealPulse[Offset+j*25];
00492
00493
00494
00495
00496 int OffsetTemp = Offset + j * 25 + Distance;
00497
00498 double C1 = 0;
00499 double C2 = 0;
00500
00501 if(OffsetTemp + 25 < (int)CumulativeIdealPulse.size() && OffsetTemp + 25 >= 0)
00502 C1 = CumulativeIdealPulse[OffsetTemp+25];
00503 if(OffsetTemp + 25 >= (int)CumulativeIdealPulse.size())
00504 C1 = CumulativeIdealPulse[CumulativeIdealPulse.size()-1];
00505 if(OffsetTemp < (int)CumulativeIdealPulse.size() && OffsetTemp >= 0)
00506 C2 = CumulativeIdealPulse[OffsetTemp];
00507 if(OffsetTemp >= (int)CumulativeIdealPulse.size())
00508 C2 = CumulativeIdealPulse[CumulativeIdealPulse.size()-1];
00509 F2[j] = C1 - C2;
00510
00511 Error = Charge[j];
00512 if(Error < 1)
00513 Error = 1;
00514
00515 SumF1F1 += F1[j] * F1[j] / Error;
00516 SumF1F2 += F1[j] * F2[j] / Error;
00517 SumF2F2 += F2[j] * F2[j] / Error;
00518 SumTF1 += F1[j] * Charge[j] / Error;
00519 SumTF2 += F2[j] * Charge[j] / Error;
00520 }
00521
00522 double Height = 0;
00523 double Height2 = 0;
00524 if (fabs(SumF1F2*SumF1F2-SumF1F1*SumF2F2)>1e-5)
00525 {
00526 Height = (SumF1F2 * SumTF2 - SumF2F2 * SumTF1) / (SumF1F2 * SumF1F2 - SumF1F1 * SumF2F2);
00527 Height2 = (SumF1F2 * SumTF1 - SumF1F1 * SumTF2) / (SumF1F2 * SumF1F2 - SumF1F1 * SumF2F2);
00528 }
00529
00530 double Chi2 = 0;
00531 for(int j = 0; j < DigiSize; j++)
00532 {
00533 double Error = Charge[j];
00534 if(Error < 1)
00535 Error = 1;
00536
00537 double Residual = Height * F1[j] + Height2 * F2[j] - Charge[j];
00538 Chi2 += Residual * Residual / Error;
00539 }
00540
00541
00542 if(Chi2 < 1e-5)
00543 Chi2 = 1e-5;
00544
00545 return Chi2;
00546 }
00547
00548 double HBHEPulseShapeFlagSetter::CalculateRMS8Max(const vector<double> &Charge)
00549 {
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559 int DigiSize = Charge.size();
00560
00561 if (DigiSize<=2) return 1e-5;
00562
00563 vector<double> TempCharge = Charge;
00564
00565
00566 sort(TempCharge.begin(), TempCharge.end());
00567
00568 double Total = 0;
00569 double Total2 = 0;
00570 for(int i = 0; i < DigiSize - 2; i++)
00571 {
00572 Total = Total + TempCharge[i];
00573 Total2 = Total2 + TempCharge[i] * TempCharge[i];
00574 }
00575
00576
00577
00578
00579
00580
00581
00582 double RMS = sqrt(Total2 - Total * Total / (DigiSize - 2));
00583
00584 double RMS8Max = RMS / TempCharge[DigiSize-1];
00585 if(RMS8Max < 1e-5)
00586 RMS8Max = 1e-5;
00587
00588 return RMS / TempCharge[DigiSize-1];
00589 }
00590
00591 double HBHEPulseShapeFlagSetter::PerformLinearFit(const vector<double> &Charge)
00592 {
00593
00594
00595
00596
00597
00598
00599
00600
00601 int DigiSize = Charge.size();
00602
00603 double SumTS2OverTi = 0;
00604 double SumTSOverTi = 0;
00605 double SumOverTi = 0;
00606 double SumTiTS = 0;
00607 double SumTi = 0;
00608
00609 double Error2 = 0;
00610 for(int i = 0; i < DigiSize; i++)
00611 {
00612 Error2 = Charge[i];
00613 if(Charge[i] < 1)
00614 Error2 = 1;
00615
00616 SumTS2OverTi += 1.* i * i / Error2;
00617 SumTSOverTi += 1.* i / Error2;
00618 SumOverTi += 1. / Error2;
00619 SumTiTS += Charge[i] * i / Error2;
00620 SumTi += Charge[i] / Error2;
00621 }
00622
00623 double CM1 = SumTS2OverTi;
00624 double CM2 = SumTSOverTi;
00625 double CD1 = SumTSOverTi;
00626 double CD2 = SumOverTi;
00627 double C1 = SumTiTS;
00628 double C2 = SumTi;
00629
00630
00631 double Slope = (C1 * CD2 - C2 * CD1) / (CM1 * CD2 - CM2 * CD1);
00632 double Intercept = (C1 * CM2 - C2 * CM1) / (CD1 * CM2 - CD2 * CM1);
00633
00634
00635 double Chi2 = 0;
00636 for(int i = 0; i < DigiSize; i++)
00637 {
00638 double Deviation = Slope * i + Intercept - Charge[i];
00639 double Error2 = Charge[i];
00640 if(Charge[i] < 1)
00641 Error2 = 1;
00642 Chi2 += Deviation * Deviation / Error2;
00643 }
00644
00645
00646 if(Chi2 < 1e-5)
00647 Chi2 = 1e-5;
00648
00649 return Chi2;
00650 }
00651
00652 bool HBHEPulseShapeFlagSetter::CheckPassFilter(double Charge,
00653 double Discriminant,
00654 vector<pair<double, double> > &Cuts,
00655 int Side)
00656 {
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666 if(Cuts.size() == 0)
00667 return true;
00668
00669 if(Charge <= Cuts[0].first)
00670 return true;
00671
00672 int IndexLargerThanCharge = -1;
00673 for(int i = 1; i < (int)Cuts.size(); i++)
00674 {
00675 if(Cuts[i].first > Charge)
00676 {
00677 IndexLargerThanCharge = i;
00678 break;
00679 }
00680 }
00681
00682 double limit = 1000000;
00683
00684 if(IndexLargerThanCharge == -1)
00685 limit = Cuts[Cuts.size()-1].second;
00686 else
00687 {
00688 double C1 = Cuts[IndexLargerThanCharge].first;
00689 double C2 = Cuts[IndexLargerThanCharge-1].first;
00690 double L1 = Cuts[IndexLargerThanCharge].second;
00691 double L2 = Cuts[IndexLargerThanCharge-1].second;
00692
00693 limit = (Charge - C1) / (C2 - C1) * (L2 - L1) + L1;
00694 }
00695
00696 if(Side > 0 && Discriminant > limit)
00697 return false;
00698 if(Side < 0 && Discriminant < limit)
00699 return false;
00700
00701 return true;
00702 }
00703
00704
00705