// This code implementation is the intellectual property of
// the GEANT4 collaboration.
//
// By copying, distributing or modifying the Program (or any work
// based on the Program) you indicate your acceptance of this statement,
// and all its terms.
//
// $Id: G4EnergyLossTables.icc,v 1.7.2.1 1999/12/07 20:51:21 gunter Exp $
// GEANT4 tag $Name: geant4-01-00 $
//
// $Id:
// Inline members of the G4EnergyLossTables class
//
// -------------------------------------------------------------------
// first version created by P.Urban , 06/04/1998
// modified by L.Urban , 27/05/98
// cache mechanism , L.Urban , 11/02/99
// bug fixed , L.Urban , 12/04/99
// 10/11/99: moved from RWT hash dictionary to STL map, G.Barrand, M.Maire
//
// -------------------------------------------------------------------

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4EnergyLossTablesHelper::G4EnergyLossTablesHelper(
  const G4PhysicsTable* aDEDXTable,
  const G4PhysicsTable* aRangeTable,
  const G4PhysicsTable* anInverseRangeTable,
  const G4PhysicsTable* aLabTimeTable,
  const G4PhysicsTable* aProperTimeTable,
  G4double aLowestKineticEnergy,
  G4double aHighestKineticEnergy,
  G4double aMassRatio,
  G4int aNumberOfBins)
  :
  theDEDXTable(aDEDXTable), theRangeTable(aRangeTable),
  theInverseRangeTable(anInverseRangeTable),
  theLabTimeTable(aLabTimeTable),
  theProperTimeTable(aProperTimeTable),
  theLowestKineticEnergy(aLowestKineticEnergy),
  theHighestKineticEnergy(aHighestKineticEnergy),
  theMassRatio(aMassRatio),
  theNumberOfBins(aNumberOfBins)
{
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4EnergyLossTablesHelper::G4EnergyLossTablesHelper()
{
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline const G4PhysicsTable* G4EnergyLossTables::GetDEDXTable(
  const G4ParticleDefinition* p)
{
  helper_map::iterator it;
  if((it=dict.find(p))==dict.end()) return 0;
  return (*it).second.theDEDXTable;  
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline const G4PhysicsTable* G4EnergyLossTables::GetRangeTable(
  const G4ParticleDefinition* p)
{
  helper_map::iterator it;
  if((it=dict.find(p))==dict.end()) return 0;
  return (*it).second.theRangeTable;  
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline const G4PhysicsTable* G4EnergyLossTables::GetInverseRangeTable(
  const G4ParticleDefinition* p)
{
  helper_map::iterator it;
  if((it=dict.find(p))==dict.end()) return 0;
  return (*it).second.theInverseRangeTable;  
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline const G4PhysicsTable* G4EnergyLossTables::GetLabTimeTable(
  const G4ParticleDefinition* p)
{
  helper_map::iterator it;
  if((it=dict.find(p))==dict.end()) return 0;
  return (*it).second.theLabTimeTable;  
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline const G4PhysicsTable* G4EnergyLossTables::GetProperTimeTable(
  const G4ParticleDefinition* p)
{
  helper_map::iterator it;
  if((it=dict.find(p))==dict.end()) return 0;
  return (*it).second.theProperTimeTable;  
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4EnergyLossTablesHelper G4EnergyLossTables::GetTables(
  const G4ParticleDefinition* p)
{
  helper_map::iterator it;
  if((it=dict.find(p))==dict.end()) {
    G4Exception("G4EnergyLossTables::GetTables: table not found!");
    exit(1);
  }
  return (*it).second;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4double G4EnergyLossTables::GetDEDX(
    const G4ParticleDefinition *aParticle,
    G4double KineticEnergy,
    G4Material *aMaterial)
{
  if(aParticle != lastParticle)
  {
    t= GetTables(aParticle);
    lastParticle = aParticle ;
    Chargesquare = (aParticle->GetPDGCharge())*
                   (aParticle->GetPDGCharge())/
                   QQPositron ;
  }
  const G4PhysicsTable*  dEdxTable= t.theDEDXTable;

  G4int materialIndex = aMaterial->GetIndex();
  G4double scaledKineticEnergy = KineticEnergy*t.theMassRatio;
  G4double dEdx;
  G4bool isOut;

  if (scaledKineticEnergy<t.theLowestKineticEnergy) {

     dEdx =(*dEdxTable)(materialIndex)->GetValue(
              t.theLowestKineticEnergy,isOut);

  } else if (scaledKineticEnergy>t.theHighestKineticEnergy) {

     dEdx = (*dEdxTable)(materialIndex)->GetValue(
	      t.theHighestKineticEnergy,isOut);

  } else {
    
    dEdx = (*dEdxTable)(materialIndex)->GetValue(
	       scaledKineticEnergy,isOut);

  }


  return dEdx*Chargesquare;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4double G4EnergyLossTables::GetLabTime(
    const G4ParticleDefinition *aParticle,
    G4double KineticEnergy,
    G4Material *aMaterial)
{
  if(aParticle != lastParticle)
  {
    t= GetTables(aParticle);
    lastParticle = aParticle ;
  }
  const G4PhysicsTable* labtimeTable= t.theLabTimeTable;

  const G4double parlowen=0.4 , ppar=0.5-parlowen ; 
  G4int materialIndex = aMaterial->GetIndex();
  G4double scaledKineticEnergy = KineticEnergy*t.theMassRatio;
  G4double time;
  G4bool isOut;

  if (scaledKineticEnergy<t.theLowestKineticEnergy) {

     time = exp(ppar*log(scaledKineticEnergy/t.theLowestKineticEnergy))*
            (*labtimeTable)(materialIndex)->GetValue(
              t.theLowestKineticEnergy,isOut);


  } else if (scaledKineticEnergy>t.theHighestKineticEnergy) {

     time = (*labtimeTable)(materialIndex)->GetValue(
              t.theHighestKineticEnergy,isOut);

  } else {
    
    time = (*labtimeTable)(materialIndex)->GetValue(
               scaledKineticEnergy,isOut);

  }

  return time/t.theMassRatio ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4double G4EnergyLossTables::GetDeltaLabTime(
    const G4ParticleDefinition *aParticle,
    G4double KineticEnergyStart,
    G4double KineticEnergyEnd,
    G4Material *aMaterial)
{
  if(aParticle != lastParticle)
  {
    t= GetTables(aParticle);
    lastParticle = aParticle ;
  }
  const G4PhysicsTable* labtimeTable= t.theLabTimeTable;

  const G4double parlowen=0.4 , ppar=0.5-parlowen ;
  const G4double dToverT = 0.05 , facT = 1. -dToverT ; 
  G4double timestart,timeend,deltatime,dTT;
  G4bool isOut;

  G4int materialIndex = aMaterial->GetIndex();
  G4double scaledKineticEnergy = KineticEnergyStart*t.theMassRatio;

  if (scaledKineticEnergy<t.theLowestKineticEnergy) {

     timestart = exp(ppar*log(scaledKineticEnergy/t.theLowestKineticEnergy))*
                (*labtimeTable)(materialIndex)->GetValue(
                t.theLowestKineticEnergy,isOut);


  } else if (scaledKineticEnergy>t.theHighestKineticEnergy) {

     timestart = (*labtimeTable)(materialIndex)->GetValue(
                t.theHighestKineticEnergy,isOut);

  } else {

    timestart = (*labtimeTable)(materialIndex)->GetValue(
                scaledKineticEnergy,isOut);

  }

  dTT = (KineticEnergyStart - KineticEnergyEnd)/KineticEnergyStart ;
  
  if( dTT < dToverT )
    scaledKineticEnergy = facT*KineticEnergyStart*t.theMassRatio;
  else
    scaledKineticEnergy = KineticEnergyEnd*t.theMassRatio;
 
  if (scaledKineticEnergy<t.theLowestKineticEnergy) {

     timeend = exp(ppar*log(scaledKineticEnergy/t.theLowestKineticEnergy))*
                (*labtimeTable)(materialIndex)->GetValue(
                t.theLowestKineticEnergy,isOut);


  } else if (scaledKineticEnergy>t.theHighestKineticEnergy) {

     timeend = (*labtimeTable)(materialIndex)->GetValue(
                t.theHighestKineticEnergy,isOut);

  } else {

    timeend = (*labtimeTable)(materialIndex)->GetValue(
                scaledKineticEnergy,isOut);

  }

  deltatime = timestart - timeend ;

  if( dTT < dToverT )
    deltatime *= dTT/dToverT;

  return deltatime/t.theMassRatio ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4double G4EnergyLossTables::GetProperTime(
    const G4ParticleDefinition *aParticle,
    G4double KineticEnergy,
    G4Material *aMaterial)
{
  if(aParticle != lastParticle)
  {
    t= GetTables(aParticle);
    lastParticle = aParticle ;
  }
  const G4PhysicsTable* propertimeTable= t.theProperTimeTable;

  const G4double parlowen=0.4 , ppar=0.5-parlowen ;
  G4int materialIndex = aMaterial->GetIndex();
  G4double scaledKineticEnergy = KineticEnergy*t.theMassRatio;
  G4double time;
  G4bool isOut;

  if (scaledKineticEnergy<t.theLowestKineticEnergy) {

     time = exp(ppar*log(scaledKineticEnergy/t.theLowestKineticEnergy))*
            (*propertimeTable)(materialIndex)->GetValue(
              t.theLowestKineticEnergy,isOut);


  } else if (scaledKineticEnergy>t.theHighestKineticEnergy) {

     time = (*propertimeTable)(materialIndex)->GetValue(
              t.theHighestKineticEnergy,isOut);

  } else {
   
    time = (*propertimeTable)(materialIndex)->GetValue(
               scaledKineticEnergy,isOut);

  }

  return time/t.theMassRatio ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4double G4EnergyLossTables::GetDeltaProperTime(
    const G4ParticleDefinition *aParticle,
    G4double KineticEnergyStart,
    G4double KineticEnergyEnd,
    G4Material *aMaterial)
{
  if(aParticle != lastParticle)
  {
    t= GetTables(aParticle);
    lastParticle = aParticle ;
  }
  const G4PhysicsTable* propertimeTable= t.theProperTimeTable;

  const G4double parlowen=0.4 , ppar=0.5-parlowen ;
  const G4double dToverT = 0.05 , facT = 1. -dToverT ; 
  G4double timestart,timeend,deltatime,dTT;
  G4bool isOut;

  G4int materialIndex = aMaterial->GetIndex();
  G4double scaledKineticEnergy = KineticEnergyStart*t.theMassRatio;

  if (scaledKineticEnergy<t.theLowestKineticEnergy) {

     timestart = exp(ppar*log(scaledKineticEnergy/t.theLowestKineticEnergy))*
                (*propertimeTable)(materialIndex)->GetValue(
                t.theLowestKineticEnergy,isOut);


  } else if (scaledKineticEnergy>t.theHighestKineticEnergy) {

     timestart = (*propertimeTable)(materialIndex)->GetValue(
                t.theHighestKineticEnergy,isOut);

  } else {

    timestart = (*propertimeTable)(materialIndex)->GetValue(
                scaledKineticEnergy,isOut);

  }

  dTT = (KineticEnergyStart - KineticEnergyEnd)/KineticEnergyStart ;
  
  if( dTT < dToverT )
    scaledKineticEnergy = facT*KineticEnergyStart*t.theMassRatio;
  else
    scaledKineticEnergy = KineticEnergyEnd*t.theMassRatio;
 
  if (scaledKineticEnergy<t.theLowestKineticEnergy) {

     timeend = exp(ppar*log(scaledKineticEnergy/t.theLowestKineticEnergy))*
                (*propertimeTable)(materialIndex)->GetValue(
                t.theLowestKineticEnergy,isOut);


  } else if (scaledKineticEnergy>t.theHighestKineticEnergy) {

     timeend = (*propertimeTable)(materialIndex)->GetValue(
                t.theHighestKineticEnergy,isOut);

  } else {

    timeend = (*propertimeTable)(materialIndex)->GetValue(
                scaledKineticEnergy,isOut);

  }

  deltatime = timestart - timeend ;

  if( dTT < dToverT )
    deltatime *= dTT/dToverT ;

  return deltatime/t.theMassRatio ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4double G4EnergyLossTables::GetRange(
    const G4ParticleDefinition *aParticle,
    G4double KineticEnergy,
    G4Material *aMaterial)
{
  if(aParticle != lastParticle)
  {
    t= GetTables(aParticle);
    lastParticle = aParticle ;
    Chargesquare = (aParticle->GetPDGCharge())*
                   (aParticle->GetPDGCharge())/
                    QQPositron ;
  }
  const G4PhysicsTable* rangeTable= t.theRangeTable;
  const G4PhysicsTable*  dEdxTable= t.theDEDXTable;

  G4int materialIndex = aMaterial->GetIndex();
  G4double scaledKineticEnergy = KineticEnergy*t.theMassRatio;
  G4double Range;
  G4bool isOut;

  if (scaledKineticEnergy<t.theLowestKineticEnergy) {

    Range = scaledKineticEnergy/t.theLowestKineticEnergy*
            (*rangeTable)(materialIndex)->GetValue(
              t.theLowestKineticEnergy,isOut);

  } else if (scaledKineticEnergy>t.theHighestKineticEnergy) {

    Range = (*rangeTable)(materialIndex)->GetValue(
	      t.theHighestKineticEnergy,isOut)+
            (scaledKineticEnergy-t.theHighestKineticEnergy)/
            (*dEdxTable)(materialIndex)->GetValue(
              t.theHighestKineticEnergy,isOut);

  } else {
    
    Range = (*rangeTable)(materialIndex)->GetValue(
	       scaledKineticEnergy,isOut);

  }

  return Range/(Chargesquare*t.theMassRatio);
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......

inline G4double G4EnergyLossTables::GetPreciseEnergyFromRange(
                                     const G4ParticleDefinition *aParticle,
                                           G4double range,
                                           G4Material *aMaterial)
// it returns the value of the kinetic energy for a given range
{
  if( aParticle != lastParticle)
  {
    t= GetTables(aParticle);
    lastParticle = aParticle;
    Chargesquare = (aParticle->GetPDGCharge())*
                   (aParticle->GetPDGCharge())/
                    QQPositron ;
  }
  const G4PhysicsTable* rangeTable= t.theRangeTable;
  const G4PhysicsTable*  dEdxTable= t.theDEDXTable;
  const G4PhysicsTable*  inverseRangeTable= t.theInverseRangeTable;

  G4double scaledrange,scaledKineticEnergy ;
  G4bool isOut ;

  G4int materialIndex = aMaterial->GetIndex() ;

  if(materialIndex != oldIndex)
  {
    oldIndex = materialIndex ;
    rmin = (*inverseRangeTable)(materialIndex)->
                              GetLowEdgeEnergy(0) ;
    rmax = (*inverseRangeTable)(materialIndex)->
                   GetLowEdgeEnergy(t.theNumberOfBins-2) ;
    Thigh = (*inverseRangeTable)(materialIndex)->
                              GetValue(rmax,isOut) ;
  }

  scaledrange = range*Chargesquare*t.theMassRatio ;

  if(scaledrange < rmin)
  {
    scaledKineticEnergy = t.theLowestKineticEnergy*
                                scaledrange/rmin ;
  }
  else
  {
    if(scaledrange < rmax)
    {
      scaledKineticEnergy = (*inverseRangeTable)(materialIndex)->
                              GetValue( scaledrange,isOut) ;
    }
    else
    {
      scaledKineticEnergy = Thigh +
                      (scaledrange-rmax)*
                      (*dEdxTable)(materialIndex)->
                                 GetValue(Thigh,isOut) ;
    }
  }

  return scaledKineticEnergy/t.theMassRatio ;
}

//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......







